Files
emuz80go/dd_opcodes.go

382 lines
8.9 KiB
Go

// Package z80 implements a Z80 CPU emulator with support for all documented
// and undocumented opcodes, flags, and registers.
package z80
// ExecuteDDOpcode executes a DD-prefixed opcode and returns the number of T-states used
func (cpu *CPU) ExecuteDDOpcode(opcode byte) int {
switch opcode {
// Load instructions
case 0x09: // ADD IX, BC
oldIX := cpu.IX
result := cpu.add16IX(cpu.IX, cpu.GetBC())
cpu.MEMPTR = oldIX + 1
cpu.IX = result
return 15
case 0x19: // ADD IX, DE
oldIX := cpu.IX
result := cpu.add16IX(cpu.IX, cpu.GetDE())
cpu.MEMPTR = oldIX + 1
cpu.IX = result
return 15
case 0x21: // LD IX, nn
cpu.IX = cpu.ReadImmediateWord()
return 14
case 0x22: // LD (nn), IX
addr := cpu.ReadImmediateWord()
cpu.Memory.WriteWord(addr, cpu.IX)
cpu.MEMPTR = addr + 1
return 20
case 0x23: // INC IX
cpu.IX++
return 10
case 0x24: // INC IXH
cpu.SetIXH(cpu.inc8(cpu.GetIXH()))
return 8
case 0x25: // DEC IXH
cpu.SetIXH(cpu.dec8(cpu.GetIXH()))
return 8
case 0x26: // LD IXH, n
cpu.SetIXH(cpu.ReadImmediateByte())
return 11
case 0x29: // ADD IX, IX
oldIX := cpu.IX
result := cpu.add16IX(cpu.IX, cpu.IX)
cpu.MEMPTR = oldIX + 1
cpu.IX = result
return 15
case 0x2A: // LD IX, (nn)
addr := cpu.ReadImmediateWord()
cpu.IX = cpu.Memory.ReadWord(addr)
cpu.MEMPTR = addr + 1
return 20
case 0x2B: // DEC IX
cpu.IX--
return 10
case 0x2C: // INC IXL
cpu.SetIXL(cpu.inc8(cpu.GetIXL()))
return 8
case 0x2D: // DEC IXL
cpu.SetIXL(cpu.dec8(cpu.GetIXL()))
return 8
case 0x2E: // LD IXL, n
cpu.SetIXL(cpu.ReadImmediateByte())
return 11
case 0x34: // INC (IX+d)
return cpu.executeIncDecIndexed(true)
case 0x35: // DEC (IX+d)
return cpu.executeIncDecIndexed(false)
case 0x36: // LD (IX+d), n
displacement := cpu.ReadDisplacement()
value := cpu.ReadImmediateByte()
addr := uint16(int32(cpu.IX) + int32(displacement))
cpu.Memory.WriteByte(addr, value)
cpu.MEMPTR = addr
return 19
case 0x39: // ADD IX, SP
oldIX := cpu.IX
result := cpu.add16IX(cpu.IX, cpu.SP)
cpu.MEMPTR = oldIX + 1
cpu.IX = result
return 15
case 0x40: // LD B,B
return 8
// Load register from IX register
case 0x44: // LD B, IXH
cpu.B = cpu.GetIXH()
return 8
case 0x45: // LD B, IXL
cpu.B = cpu.GetIXL()
return 8
case 0x46: // LD B, (IX+d)
return cpu.executeLoadFromIndexed(0)
case 0x4C: // LD C, IXH
cpu.C = cpu.GetIXH()
return 8
case 0x4D: // LD C, IXL
cpu.C = cpu.GetIXL()
return 8
case 0x4E: // LD C, (IX+d)
return cpu.executeLoadFromIndexed(1)
case 0x54: // LD D, IXH
cpu.D = cpu.GetIXH()
return 8
case 0x55: // LD D, IXL
cpu.D = cpu.GetIXL()
return 8
case 0x56: // LD D, (IX+d)
return cpu.executeLoadFromIndexed(2)
case 0x5C: // LD E, IXH
cpu.E = cpu.GetIXH()
return 8
case 0x5D: // LD E, IXL
cpu.E = cpu.GetIXL()
return 8
case 0x5E: // LD E, (IX+d)
return cpu.executeLoadFromIndexed(3)
case 0x60: // LD IXH, B
cpu.SetIXH(cpu.B)
return 8
case 0x61: // LD IXH, C
cpu.SetIXH(cpu.C)
return 8
case 0x62: // LD IXH, D
cpu.SetIXH(cpu.D)
return 8
case 0x63: // LD IXH, E
cpu.SetIXH(cpu.E)
return 8
case 0x64: // LD IXH, IXH
// No operation needed
return 8
case 0x65: // LD IXH, IXL
cpu.SetIXH(cpu.GetIXL())
return 8
case 0x66: // LD H, (IX+d)
return cpu.executeLoadFromIndexed(4)
case 0x67: // LD IXH, A
cpu.SetIXH(cpu.A)
return 8
case 0x68: // LD IXL, B
cpu.SetIXL(cpu.B)
return 8
case 0x69: // LD IXL, C
cpu.SetIXL(cpu.C)
return 8
case 0x6A: // LD IXL, D
cpu.SetIXL(cpu.D)
return 8
case 0x6B: // LD IXL, E
cpu.SetIXL(cpu.E)
return 8
case 0x6C: // LD IXL, IXH
cpu.SetIXL(cpu.GetIXH())
return 8
case 0x6D: // LD IXL, IXL
// No operation needed
return 8
case 0x6E: // LD L, (IX+d)
return cpu.executeLoadFromIndexed(5)
case 0x6F: // LD IXL, A
cpu.SetIXL(cpu.A)
return 8
case 0x70: // LD (IX+d), B
return cpu.executeStoreToIndexed(cpu.B)
case 0x71: // LD (IX+d), C
return cpu.executeStoreToIndexed(cpu.C)
case 0x72: // LD (IX+d), D
return cpu.executeStoreToIndexed(cpu.D)
case 0x73: // LD (IX+d), E
return cpu.executeStoreToIndexed(cpu.E)
case 0x74: // LD (IX+d), H
return cpu.executeStoreToIndexed(cpu.H)
case 0x75: // LD (IX+d), L
return cpu.executeStoreToIndexed(cpu.L)
case 0x77: // LD (IX+d), A
return cpu.executeStoreToIndexed(cpu.A)
case 0x7C: // LD A, IXH
cpu.A = cpu.GetIXH()
return 8
case 0x7D: // LD A, IXL
cpu.A = cpu.GetIXL()
return 8
case 0x7E: // LD A, (IX+d)
return cpu.executeLoadFromIndexed(7)
// Arithmetic and logic instructions
case 0x84: // ADD A, IXH
cpu.add8(cpu.GetIXH())
return 8
case 0x85: // ADD A, IXL
cpu.add8(cpu.GetIXL())
return 8
case 0x86: // ADD A, (IX+d)
return cpu.executeALUIndexed(0)
case 0x8C: // ADC A, IXH
cpu.adc8(cpu.GetIXH())
return 8
case 0x8D: // ADC A, IXL
cpu.adc8(cpu.GetIXL())
return 8
case 0x8E: // ADC A, (IX+d)
return cpu.executeALUIndexed(1)
case 0x94: // SUB IXH
cpu.sub8(cpu.GetIXH())
return 8
case 0x95: // SUB IXL
cpu.sub8(cpu.GetIXL())
return 8
case 0x96: // SUB (IX+d)
return cpu.executeALUIndexed(2)
case 0x9C: // SBC A, IXH
cpu.sbc8(cpu.GetIXH())
return 8
case 0x9D: // SBC A, IXL
cpu.sbc8(cpu.GetIXL())
return 8
case 0x9E: // SBC A, (IX+d)
return cpu.executeALUIndexed(3)
case 0xA4: // AND IXH
cpu.and8(cpu.GetIXH())
return 8
case 0xA5: // AND IXL
cpu.and8(cpu.GetIXL())
return 8
case 0xA6: // AND (IX+d)
return cpu.executeALUIndexed(4)
case 0xAC: // XOR IXH
cpu.xor8(cpu.GetIXH())
return 8
case 0xAD: // XOR IXL
cpu.xor8(cpu.GetIXL())
return 8
case 0xAE: // XOR (IX+d)
return cpu.executeALUIndexed(5)
case 0xB4: // OR IXH
cpu.or8(cpu.GetIXH())
return 8
case 0xB5: // OR IXL
cpu.or8(cpu.GetIXL())
return 8
case 0xB6: // OR (IX+d)
return cpu.executeALUIndexed(6)
case 0xBC: // CP IXH
cpu.cp8(cpu.GetIXH())
return 8
case 0xBD: // CP IXL
cpu.cp8(cpu.GetIXL())
return 8
case 0xBE: // CP (IX+d)
return cpu.executeALUIndexed(7)
// POP and PUSH instructions
case 0xE1: // POP IX
cpu.IX = cpu.Pop()
return 14
case 0xE3: // EX (SP), IX
temp := cpu.Memory.ReadWord(cpu.SP)
cpu.Memory.WriteWord(cpu.SP, cpu.IX)
cpu.IX = temp
cpu.MEMPTR = temp
return 23
case 0xE5: // PUSH IX
cpu.Push(cpu.IX)
return 15
case 0xE9: // JP (IX)
cpu.PC = cpu.IX
return 8
case 0xF9: // LD SP, IX
cpu.SP = cpu.IX
return 10
// Handle DD CB prefix (IX with displacement and CB operations)
case 0xCB: // DD CB prefix
return cpu.executeDDCBOpcode()
case 0xfd:
return 8
case 0x00: // Extended NOP (undocumented)
// DD 00 is an undocumented instruction that acts as an extended NOP
// It consumes the DD prefix and the 00 opcode but executes as a NOP
// Takes 8 cycles total (4 for DD prefix fetch + 4 for 00 opcode fetch)
return 8
case 0xdd:
return 8
default:
return cpu.ExecuteOpcode(opcode)
//panic(fmt.Sprintf("DD unexpected code %x", opcode))
}
}
// executeIncDecIndexed handles INC/DEC (IX+d) instructions
func (cpu *CPU) executeIncDecIndexed(isInc bool) int {
displacement := cpu.ReadDisplacement()
addr := uint16(int32(cpu.IX) + int32(displacement))
value := cpu.Memory.ReadByte(addr)
var result byte
if isInc {
result = cpu.inc8(value)
} else {
result = cpu.dec8(value)
}
cpu.Memory.WriteByte(addr, result)
cpu.MEMPTR = addr
return 23
}
// executeLoadFromIndexed handles LD r, (IX+d) instructions
func (cpu *CPU) executeLoadFromIndexed(reg byte) int {
displacement := cpu.ReadDisplacement()
addr := uint16(int32(cpu.IX) + int32(displacement))
value := cpu.Memory.ReadByte(addr)
switch reg {
case 0:
cpu.B = value
case 1:
cpu.C = value
case 2:
cpu.D = value
case 3:
cpu.E = value
case 4:
cpu.H = value
case 5:
cpu.L = value
case 7:
cpu.A = value
}
cpu.MEMPTR = addr
return 19
}
// executeStoreToIndexed handles LD (IX+d), r instructions
func (cpu *CPU) executeStoreToIndexed(value byte) int {
displacement := cpu.ReadDisplacement()
addr := uint16(int32(cpu.IX) + int32(displacement))
cpu.Memory.WriteByte(addr, value)
cpu.MEMPTR = addr
return 19
}
// executeALUIndexed handles ALU operations with (IX+d) operand
func (cpu *CPU) executeALUIndexed(opType byte) int {
displacement := cpu.ReadDisplacement()
addr := uint16(int32(cpu.IX) + int32(displacement))
value := cpu.Memory.ReadByte(addr)
switch opType {
case 0: // ADD
cpu.add8(value)
case 1: // ADC
cpu.adc8(value)
case 2: // SUB
cpu.sub8(value)
case 3: // SBC
cpu.sbc8(value)
case 4: // AND
cpu.and8(value)
case 5: // XOR
cpu.xor8(value)
case 6: // OR
cpu.or8(value)
case 7: // CP
cpu.cp8(value)
}
cpu.MEMPTR = addr
return 19
}
// add16IX adds two 16-bit values for IX register and updates flags
func (cpu *CPU) add16IX(a, b uint16) uint16 {
result := a + b
cpu.SetFlagState(FLAG_C, result < a)
cpu.SetFlagState(FLAG_H, (a&0x0FFF)+(b&0x0FFF) > 0x0FFF)
cpu.ClearFlag(FLAG_N)
// For IX operations, we update X and Y flags from high byte of result
cpu.UpdateFlags3and5FromAddress(result)
return result
}