455 lines
9.7 KiB
Go
455 lines
9.7 KiB
Go
// Package z80 implements a Z80 CPU emulator with support for all documented
|
|
// and undocumented opcodes, flags, and registers.
|
|
package z80
|
|
|
|
// ExecuteCBOpcode executes a CB-prefixed opcode and returns the number of T-states used
|
|
func (cpu *CPU) ExecuteCBOpcode(opcode byte) int {
|
|
// Handle rotate and shift instructions (0x00-0x3F)
|
|
if opcode <= 0x3F {
|
|
// Determine operation type from opcode bits 3-5
|
|
opType := (opcode >> 3) & 0x07
|
|
// Determine register from opcode bits 0-2
|
|
reg := opcode & 0x07
|
|
|
|
// Handle (HL) special case
|
|
if reg == 6 {
|
|
addr := cpu.GetHL()
|
|
value := cpu.Memory.ReadByte(addr)
|
|
|
|
switch opType {
|
|
case 0: // RLC
|
|
result := cpu.rlc(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
case 1: // RRC
|
|
result := cpu.rrc(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
case 2: // RL
|
|
result := cpu.rl(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
case 3: // RR
|
|
result := cpu.rr(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
case 4: // SLA
|
|
result := cpu.sla(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
case 5: // SRA
|
|
result := cpu.sra(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
case 6: // SLL (Undocumented)
|
|
result := cpu.sll(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
case 7: // SRL
|
|
result := cpu.srl(value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
}
|
|
} else {
|
|
// Handle regular registers
|
|
switch opType {
|
|
case 0: // RLC
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.rlc(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.rlc(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.rlc(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.rlc(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.rlc(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.rlc(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.rlc(cpu.A)
|
|
}
|
|
return 8
|
|
case 1: // RRC
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.rrc(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.rrc(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.rrc(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.rrc(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.rrc(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.rrc(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.rrc(cpu.A)
|
|
}
|
|
return 8
|
|
case 2: // RL
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.rl(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.rl(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.rl(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.rl(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.rl(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.rl(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.rl(cpu.A)
|
|
}
|
|
return 8
|
|
case 3: // RR
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.rr(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.rr(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.rr(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.rr(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.rr(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.rr(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.rr(cpu.A)
|
|
}
|
|
return 8
|
|
case 4: // SLA
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.sla(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.sla(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.sla(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.sla(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.sla(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.sla(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.sla(cpu.A)
|
|
}
|
|
return 8
|
|
case 5: // SRA
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.sra(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.sra(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.sra(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.sra(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.sra(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.sra(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.sra(cpu.A)
|
|
}
|
|
return 8
|
|
case 6: // SLL (Undocumented)
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.sll(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.sll(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.sll(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.sll(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.sll(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.sll(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.sll(cpu.A)
|
|
}
|
|
return 8
|
|
case 7: // SRL
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.srl(cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.srl(cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.srl(cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.srl(cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.srl(cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.srl(cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.srl(cpu.A)
|
|
}
|
|
return 8
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle bit test instructions (0x40-0x7F)
|
|
if opcode >= 0x40 && opcode <= 0x7F {
|
|
bitNum := uint((opcode >> 3) & 0x07)
|
|
reg := opcode & 0x07
|
|
|
|
// Handle (HL) special case
|
|
if reg == 6 {
|
|
value := cpu.Memory.ReadByte(cpu.GetHL())
|
|
cpu.bitMem(bitNum, value, byte(cpu.MEMPTR>>8))
|
|
return 12
|
|
} else {
|
|
// Handle regular registers
|
|
var regValue byte
|
|
switch reg {
|
|
case 0:
|
|
regValue = cpu.B
|
|
case 1:
|
|
regValue = cpu.C
|
|
case 2:
|
|
regValue = cpu.D
|
|
case 3:
|
|
regValue = cpu.E
|
|
case 4:
|
|
regValue = cpu.H
|
|
case 5:
|
|
regValue = cpu.L
|
|
case 7:
|
|
regValue = cpu.A
|
|
}
|
|
cpu.bit(bitNum, regValue)
|
|
return 8
|
|
}
|
|
}
|
|
|
|
// Handle reset bit instructions (0x80-0xBF)
|
|
if opcode >= 0x80 && opcode <= 0xBF {
|
|
bitNum := uint((opcode >> 3) & 0x07)
|
|
reg := opcode & 0x07
|
|
|
|
// Handle (HL) special case
|
|
if reg == 6 {
|
|
addr := cpu.GetHL()
|
|
value := cpu.Memory.ReadByte(addr)
|
|
result := cpu.res(bitNum, value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
} else {
|
|
// Handle regular registers
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.res(bitNum, cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.res(bitNum, cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.res(bitNum, cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.res(bitNum, cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.res(bitNum, cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.res(bitNum, cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.res(bitNum, cpu.A)
|
|
}
|
|
return 8
|
|
}
|
|
}
|
|
|
|
// Handle set bit instructions (0xC0-0xFF)
|
|
if opcode >= 0xC0 {
|
|
bitNum := uint((opcode >> 3) & 0x07)
|
|
reg := opcode & 0x07
|
|
|
|
// Handle (HL) special case
|
|
if reg == 6 {
|
|
addr := cpu.GetHL()
|
|
value := cpu.Memory.ReadByte(addr)
|
|
result := cpu.set(bitNum, value)
|
|
cpu.Memory.WriteByte(addr, result)
|
|
return 15
|
|
} else {
|
|
// Handle regular registers
|
|
switch reg {
|
|
case 0:
|
|
cpu.B = cpu.set(bitNum, cpu.B)
|
|
case 1:
|
|
cpu.C = cpu.set(bitNum, cpu.C)
|
|
case 2:
|
|
cpu.D = cpu.set(bitNum, cpu.D)
|
|
case 3:
|
|
cpu.E = cpu.set(bitNum, cpu.E)
|
|
case 4:
|
|
cpu.H = cpu.set(bitNum, cpu.H)
|
|
case 5:
|
|
cpu.L = cpu.set(bitNum, cpu.L)
|
|
case 7:
|
|
cpu.A = cpu.set(bitNum, cpu.A)
|
|
}
|
|
return 8
|
|
}
|
|
}
|
|
|
|
// Unimplemented opcode
|
|
return 8
|
|
}
|
|
|
|
// rlc rotates a byte left circular
|
|
func (cpu *CPU) rlc(value byte) byte {
|
|
result := (value << 1) | (value >> 7)
|
|
cpu.SetFlagState(FLAG_C, (value&0x80) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// rrc rotates a byte right circular
|
|
func (cpu *CPU) rrc(value byte) byte {
|
|
result := (value >> 1) | (value << 7)
|
|
cpu.SetFlagState(FLAG_C, (value&0x01) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// rl rotates a byte left through carry
|
|
func (cpu *CPU) rl(value byte) byte {
|
|
oldCarry := cpu.GetFlag(FLAG_C)
|
|
result := (value << 1)
|
|
if oldCarry {
|
|
result |= 0x01
|
|
}
|
|
cpu.SetFlagState(FLAG_C, (value&0x80) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// rr rotates a byte right through carry
|
|
func (cpu *CPU) rr(value byte) byte {
|
|
oldCarry := cpu.GetFlag(FLAG_C)
|
|
result := (value >> 1)
|
|
if oldCarry {
|
|
result |= 0x80
|
|
}
|
|
cpu.SetFlagState(FLAG_C, (value&0x01) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// sla shifts a byte left arithmetic
|
|
func (cpu *CPU) sla(value byte) byte {
|
|
result := value << 1
|
|
cpu.SetFlagState(FLAG_C, (value&0x80) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// sra shifts a byte right arithmetic
|
|
func (cpu *CPU) sra(value byte) byte {
|
|
result := (value >> 1) | (value & 0x80)
|
|
cpu.SetFlagState(FLAG_C, (value&0x01) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// sll shifts a byte left logical (Undocumented)
|
|
func (cpu *CPU) sll(value byte) byte {
|
|
result := (value << 1) | 0x01
|
|
cpu.SetFlagState(FLAG_C, (value&0x80) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// srl shifts a byte right logical
|
|
func (cpu *CPU) srl(value byte) byte {
|
|
result := value >> 1
|
|
cpu.SetFlagState(FLAG_C, (value&0x01) != 0)
|
|
cpu.ClearFlag(FLAG_H)
|
|
cpu.ClearFlag(FLAG_N)
|
|
cpu.UpdateSZXYPVFlags(result)
|
|
return result
|
|
}
|
|
|
|
// bit tests a bit in a byte
|
|
func (cpu *CPU) bit(bitNum uint, value byte) {
|
|
mask := byte(1 << bitNum)
|
|
result := value & mask
|
|
cpu.SetFlagState(FLAG_Z, result == 0)
|
|
cpu.SetFlagState(FLAG_Y, (value&(1<<5)) != 0)
|
|
cpu.SetFlagState(FLAG_X, (value&(1<<3)) != 0)
|
|
cpu.SetFlag(FLAG_H, true)
|
|
cpu.ClearFlag(FLAG_N)
|
|
if result == 0 {
|
|
cpu.SetFlag(FLAG_PV, true)
|
|
cpu.ClearFlag(FLAG_S)
|
|
} else {
|
|
cpu.ClearFlag(FLAG_PV)
|
|
// For BIT 7, S flag is set to the value of bit 7
|
|
if bitNum == 7 {
|
|
cpu.SetFlagState(FLAG_S, (value&0x80) != 0)
|
|
} else {
|
|
cpu.ClearFlag(FLAG_S)
|
|
}
|
|
}
|
|
}
|
|
|
|
// res resets a bit in a byte
|
|
func (cpu *CPU) res(bitNum uint, value byte) byte {
|
|
mask := byte(^(1 << bitNum))
|
|
return value & mask
|
|
}
|
|
|
|
// bitMem tests a bit in a byte for memory references
|
|
func (cpu *CPU) bitMem(bitNum uint, value byte, addrHi byte) {
|
|
mask := byte(1 << bitNum)
|
|
result := value & mask
|
|
cpu.SetFlagState(FLAG_Z, result == 0)
|
|
cpu.SetFlagState(FLAG_Y, (addrHi&(1<<5)) != 0)
|
|
cpu.SetFlagState(FLAG_X, (addrHi&(1<<3)) != 0)
|
|
cpu.SetFlag(FLAG_H, true)
|
|
cpu.ClearFlag(FLAG_N)
|
|
if result == 0 {
|
|
cpu.SetFlag(FLAG_PV, true)
|
|
cpu.ClearFlag(FLAG_S)
|
|
} else {
|
|
cpu.ClearFlag(FLAG_PV)
|
|
// For BIT 7, S flag is set to the value of bit 7
|
|
if bitNum == 7 {
|
|
cpu.SetFlagState(FLAG_S, (value&0x80) != 0)
|
|
} else {
|
|
cpu.ClearFlag(FLAG_S)
|
|
}
|
|
}
|
|
}
|
|
|
|
// set sets a bit in a byte
|
|
func (cpu *CPU) set(bitNum uint, value byte) byte {
|
|
mask := byte(1 << bitNum)
|
|
return value | mask
|
|
}
|