377 lines
8.9 KiB
Go
377 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
|
|
|
|
// ExecuteFDOpcode executes a FD-prefixed opcode and returns the number of T-states used
|
|
func (cpu *CPU) ExecuteFDOpcode(opcode byte) int {
|
|
switch opcode {
|
|
// Load instructions
|
|
case 0x09: // ADD IY, BC
|
|
oldIY := cpu.IY
|
|
result := cpu.add16IY(cpu.IY, cpu.GetBC())
|
|
cpu.MEMPTR = oldIY + 1
|
|
cpu.IY = result
|
|
return 15
|
|
case 0x19: // ADD IY, DE
|
|
oldIY := cpu.IY
|
|
result := cpu.add16IY(cpu.IY, cpu.GetDE())
|
|
cpu.MEMPTR = oldIY + 1
|
|
cpu.IY = result
|
|
return 15
|
|
case 0x21: // LD IY, nn
|
|
cpu.IY = cpu.ReadImmediateWord()
|
|
return 14
|
|
case 0x22: // LD (nn), IY
|
|
addr := cpu.ReadImmediateWord()
|
|
cpu.Memory.WriteWord(addr, cpu.IY)
|
|
cpu.MEMPTR = addr + 1
|
|
return 20
|
|
case 0x23: // INC IY
|
|
cpu.IY++
|
|
return 10
|
|
case 0x24: // INC IYH
|
|
cpu.SetIYH(cpu.inc8(cpu.GetIYH()))
|
|
return 8
|
|
case 0x25: // DEC IYH
|
|
cpu.SetIYH(cpu.dec8(cpu.GetIYH()))
|
|
return 8
|
|
case 0x26: // LD IYH, n
|
|
cpu.SetIYH(cpu.ReadImmediateByte())
|
|
return 11
|
|
case 0x29: // ADD IY, IY
|
|
oldIY := cpu.IY
|
|
result := cpu.add16IY(cpu.IY, cpu.IY)
|
|
cpu.MEMPTR = oldIY + 1
|
|
cpu.IY = result
|
|
return 15
|
|
case 0x2A: // LD IY, (nn)
|
|
addr := cpu.ReadImmediateWord()
|
|
cpu.IY = cpu.Memory.ReadWord(addr)
|
|
cpu.MEMPTR = addr + 1
|
|
return 20
|
|
case 0x2B: // DEC IY
|
|
cpu.IY--
|
|
return 10
|
|
case 0x2C: // INC IYL
|
|
cpu.SetIYL(cpu.inc8(cpu.GetIYL()))
|
|
return 8
|
|
case 0x2D: // DEC IYL
|
|
cpu.SetIYL(cpu.dec8(cpu.GetIYL()))
|
|
return 8
|
|
case 0x2E: // LD IYL, n
|
|
cpu.SetIYL(cpu.ReadImmediateByte())
|
|
return 11
|
|
case 0x34: // INC (IY+d)
|
|
return cpu.executeIncDecIndexedIY(true)
|
|
case 0x35: // DEC (IY+d)
|
|
return cpu.executeIncDecIndexedIY(false)
|
|
case 0x36: // LD (IY+d), n
|
|
displacement := cpu.ReadDisplacement()
|
|
value := cpu.ReadImmediateByte()
|
|
addr := uint16(int32(cpu.IY) + int32(displacement))
|
|
cpu.Memory.WriteByte(addr, value)
|
|
cpu.MEMPTR = addr
|
|
return 19
|
|
case 0x39: // ADD IY, SP
|
|
oldIY := cpu.IY
|
|
result := cpu.add16IY(cpu.IY, cpu.SP)
|
|
cpu.MEMPTR = oldIY + 1
|
|
cpu.IY = result
|
|
return 15
|
|
|
|
// Load register from IY register
|
|
case 0x44: // LD B, IYH
|
|
cpu.B = cpu.GetIYH()
|
|
return 8
|
|
case 0x45: // LD B, IYL
|
|
cpu.B = cpu.GetIYL()
|
|
return 8
|
|
case 0x46: // LD B, (IY+d)
|
|
return cpu.executeLoadFromIndexedIY(0)
|
|
case 0x4C: // LD C, IYH
|
|
cpu.C = cpu.GetIYH()
|
|
return 8
|
|
case 0x4D: // LD C, IYL
|
|
cpu.C = cpu.GetIYL()
|
|
return 8
|
|
case 0x4E: // LD C, (IY+d)
|
|
return cpu.executeLoadFromIndexedIY(1)
|
|
case 0x54: // LD D, IYH
|
|
cpu.D = cpu.GetIYH()
|
|
return 8
|
|
case 0x55: // LD D, IYL
|
|
cpu.D = cpu.GetIYL()
|
|
return 8
|
|
case 0x56: // LD D, (IY+d)
|
|
return cpu.executeLoadFromIndexedIY(2)
|
|
case 0x5C: // LD E, IYH
|
|
cpu.E = cpu.GetIYH()
|
|
return 8
|
|
case 0x5D: // LD E, IYL
|
|
cpu.E = cpu.GetIYL()
|
|
return 8
|
|
case 0x5E: // LD E, (IY+d)
|
|
return cpu.executeLoadFromIndexedIY(3)
|
|
case 0x60: // LD IYH, B
|
|
cpu.SetIYH(cpu.B)
|
|
return 8
|
|
case 0x61: // LD IYH, C
|
|
cpu.SetIYH(cpu.C)
|
|
return 8
|
|
case 0x62: // LD IYH, D
|
|
cpu.SetIYH(cpu.D)
|
|
return 8
|
|
case 0x63: // LD IYH, E
|
|
cpu.SetIYH(cpu.E)
|
|
return 8
|
|
case 0x64: // LD IYH, IYH
|
|
// No operation needed
|
|
return 8
|
|
case 0x65: // LD IYH, IYL
|
|
cpu.SetIYH(cpu.GetIYL())
|
|
return 8
|
|
case 0x66: // LD H, (IY+d)
|
|
return cpu.executeLoadFromIndexedIY(4)
|
|
case 0x67: // LD IYH, A
|
|
cpu.SetIYH(cpu.A)
|
|
return 8
|
|
case 0x68: // LD IYL, B
|
|
cpu.SetIYL(cpu.B)
|
|
return 8
|
|
case 0x69: // LD IYL, C
|
|
cpu.SetIYL(cpu.C)
|
|
return 8
|
|
case 0x6A: // LD IYL, D
|
|
cpu.SetIYL(cpu.D)
|
|
return 8
|
|
case 0x6B: // LD IYL, E
|
|
cpu.SetIYL(cpu.E)
|
|
return 8
|
|
case 0x6C: // LD IYL, IYH
|
|
cpu.SetIYL(cpu.GetIYH())
|
|
return 8
|
|
case 0x6D: // LD IYL, IYL
|
|
// No operation needed
|
|
return 8
|
|
case 0x6E: // LD L, (IY+d)
|
|
return cpu.executeLoadFromIndexedIY(5)
|
|
case 0x6F: // LD IYL, A
|
|
cpu.SetIYL(cpu.A)
|
|
return 8
|
|
case 0x70: // LD (IY+d), B
|
|
return cpu.executeStoreToIndexedIY(cpu.B)
|
|
case 0x71: // LD (IY+d), C
|
|
return cpu.executeStoreToIndexedIY(cpu.C)
|
|
case 0x72: // LD (IY+d), D
|
|
return cpu.executeStoreToIndexedIY(cpu.D)
|
|
case 0x73: // LD (IY+d), E
|
|
return cpu.executeStoreToIndexedIY(cpu.E)
|
|
case 0x74: // LD (IY+d), H
|
|
return cpu.executeStoreToIndexedIY(cpu.H)
|
|
case 0x75: // LD (IY+d), L
|
|
return cpu.executeStoreToIndexedIY(cpu.L)
|
|
case 0x77: // LD (IY+d), A
|
|
return cpu.executeStoreToIndexedIY(cpu.A)
|
|
case 0x7C: // LD A, IYH
|
|
cpu.A = cpu.GetIYH()
|
|
return 8
|
|
case 0x7D: // LD A, IYL
|
|
cpu.A = cpu.GetIYL()
|
|
return 8
|
|
case 0x7E: // LD A, (IY+d)
|
|
return cpu.executeLoadFromIndexedIY(7)
|
|
|
|
// Arithmetic and logic instructions
|
|
case 0x84: // ADD A, IYH
|
|
cpu.add8(cpu.GetIYH())
|
|
return 8
|
|
case 0x85: // ADD A, IYL
|
|
cpu.add8(cpu.GetIYL())
|
|
return 8
|
|
case 0x86: // ADD A, (IY+d)
|
|
return cpu.executeALUIndexedIY(0)
|
|
case 0x8C: // ADC A, IYH
|
|
cpu.adc8(cpu.GetIYH())
|
|
return 8
|
|
case 0x8D: // ADC A, IYL
|
|
cpu.adc8(cpu.GetIYL())
|
|
return 8
|
|
case 0x8E: // ADC A, (IY+d)
|
|
return cpu.executeALUIndexedIY(1)
|
|
case 0x94: // SUB IYH
|
|
cpu.sub8(cpu.GetIYH())
|
|
return 8
|
|
case 0x95: // SUB IYL
|
|
cpu.sub8(cpu.GetIYL())
|
|
return 8
|
|
case 0x96: // SUB (IY+d)
|
|
return cpu.executeALUIndexedIY(2)
|
|
case 0x9C: // SBC A, IYH
|
|
cpu.sbc8(cpu.GetIYH())
|
|
return 8
|
|
case 0x9D: // SBC A, IYL
|
|
cpu.sbc8(cpu.GetIYL())
|
|
return 8
|
|
case 0x9E: // SBC A, (IY+d)
|
|
return cpu.executeALUIndexedIY(3)
|
|
case 0xA4: // AND IYH
|
|
cpu.and8(cpu.GetIYH())
|
|
return 8
|
|
case 0xA5: // AND IYL
|
|
cpu.and8(cpu.GetIYL())
|
|
return 8
|
|
case 0xA6: // AND (IY+d)
|
|
return cpu.executeALUIndexedIY(4)
|
|
case 0xAC: // XOR IYH
|
|
cpu.xor8(cpu.GetIYH())
|
|
return 8
|
|
case 0xAD: // XOR IYL
|
|
cpu.xor8(cpu.GetIYL())
|
|
return 8
|
|
case 0xAE: // XOR (IY+d)
|
|
return cpu.executeALUIndexedIY(5)
|
|
case 0xB4: // OR IYH
|
|
cpu.or8(cpu.GetIYH())
|
|
return 8
|
|
case 0xB5: // OR IYL
|
|
cpu.or8(cpu.GetIYL())
|
|
return 8
|
|
case 0xB6: // OR (IY+d)
|
|
return cpu.executeALUIndexedIY(6)
|
|
case 0xBC: // CP IYH
|
|
cpu.cp8(cpu.GetIYH())
|
|
return 8
|
|
case 0xBD: // CP IYL
|
|
cpu.cp8(cpu.GetIYL())
|
|
return 8
|
|
case 0xBE: // CP (IY+d)
|
|
return cpu.executeALUIndexedIY(7)
|
|
|
|
// POP and PUSH instructions
|
|
case 0xE1: // POP IY
|
|
cpu.IY = cpu.Pop()
|
|
return 14
|
|
case 0xE3: // EX (SP), IY
|
|
temp := cpu.Memory.ReadWord(cpu.SP)
|
|
cpu.Memory.WriteWord(cpu.SP, cpu.IY)
|
|
cpu.IY = temp
|
|
cpu.MEMPTR = cpu.IY
|
|
return 23
|
|
case 0xE5: // PUSH IY
|
|
cpu.Push(cpu.IY)
|
|
return 15
|
|
case 0xE9: // JP (IY)
|
|
cpu.PC = cpu.IY
|
|
return 8
|
|
case 0xF9: // LD SP, IY
|
|
cpu.SP = cpu.IY
|
|
return 10
|
|
|
|
// Handle FD CB prefix (IY with displacement and CB operations)
|
|
case 0xCB: // FD CB prefix
|
|
return cpu.ExecuteFDCBOpcode()
|
|
|
|
case 0x00: // Extended NOP (undocumented)
|
|
// FD 00 is an undocumented instruction that acts as an extended NOP
|
|
// It consumes the FD prefix and the 00 opcode but executes as a NOP
|
|
// Takes 8 cycles total (4 for FD prefix fetch + 4 for 00 opcode fetch)
|
|
return 8
|
|
default:
|
|
// Unimplemented opcode - treat as regular opcode
|
|
// This handles cases where FD is followed by a normal opcode
|
|
return cpu.ExecuteOpcode(opcode)
|
|
}
|
|
}
|
|
|
|
// executeIncDecIndexedIY handles INC/DEC (IY+d) instructions
|
|
func (cpu *CPU) executeIncDecIndexedIY(isInc bool) int {
|
|
displacement := cpu.ReadDisplacement()
|
|
addr := uint16(int32(cpu.IY) + 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
|
|
}
|
|
|
|
// executeLoadFromIndexedIY handles LD r, (IY+d) instructions
|
|
func (cpu *CPU) executeLoadFromIndexedIY(reg byte) int {
|
|
displacement := cpu.ReadDisplacement()
|
|
addr := uint16(int32(cpu.IY) + 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
|
|
}
|
|
|
|
// executeStoreToIndexedIY handles LD (IY+d), r instructions
|
|
func (cpu *CPU) executeStoreToIndexedIY(value byte) int {
|
|
displacement := cpu.ReadDisplacement()
|
|
addr := uint16(int32(cpu.IY) + int32(displacement))
|
|
cpu.Memory.WriteByte(addr, value)
|
|
cpu.MEMPTR = addr
|
|
return 19
|
|
}
|
|
|
|
// executeALUIndexedIY handles ALU operations with (IY+d) operand
|
|
func (cpu *CPU) executeALUIndexedIY(opType byte) int {
|
|
displacement := cpu.ReadDisplacement()
|
|
addr := uint16(int32(cpu.IY) + 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
|
|
}
|
|
|
|
// add16IY adds two 16-bit values for IY register and updates flags
|
|
func (cpu *CPU) add16IY(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 IY operations, we update X and Y flags from high byte of result
|
|
cpu.UpdateFlags3and5FromAddress(result)
|
|
return result
|
|
}
|