423 lines
11 KiB
Go
423 lines
11 KiB
Go
// Package z80 implements a Z80 CPU emulator with support for all documented
|
|
// and undocumented opcodes, flags, and registers.
|
|
package z80
|
|
|
|
// Memory interface for memory operations
|
|
type Memory interface {
|
|
ReadByte(address uint16) byte
|
|
WriteByte(address uint16, value byte)
|
|
ReadWord(address uint16) uint16
|
|
WriteWord(address uint16, value uint16)
|
|
}
|
|
|
|
// IO interface for input/output operations
|
|
type IO interface {
|
|
ReadPort(port uint16) byte
|
|
WritePort(port uint16, value byte)
|
|
CheckInterrupt() bool
|
|
}
|
|
|
|
// FLAG_* constants represent the bit positions of the FLAGS register
|
|
const (
|
|
FLAG_C = 0x01 // Carry flag
|
|
FLAG_N = 0x02 // Add/Subtract flag
|
|
FLAG_PV = 0x04 // Parity/Overflow flag
|
|
FLAG_3 = 0x08 // Undocumented 3rd bit flag
|
|
FLAG_H = 0x10 // Half Carry flag
|
|
FLAG_5 = 0x20 // Undocumented 5th bit flag
|
|
FLAG_Z = 0x40 // Zero flag
|
|
FLAG_S = 0x80 // Sign flag
|
|
)
|
|
|
|
// FLAG_X and FLAG_Y are aliases for FLAG_3 and FLAG_5 respectively
|
|
const (
|
|
FLAG_X = FLAG_3
|
|
FLAG_Y = FLAG_5
|
|
)
|
|
|
|
// CPU represents the state of a Z80 processor
|
|
type CPU struct {
|
|
A byte // Accumulator
|
|
F byte // Flags register
|
|
B, C byte // BC register pair
|
|
D, E byte // DE register pair
|
|
H, L byte // HL register pair
|
|
A_, F_ byte // Alternate AF register pair
|
|
B_, C_ byte // Alternate BC register pair
|
|
D_, E_ byte // Alternate DE register pair
|
|
H_, L_ byte // Alternate HL register pair
|
|
IX uint16 // Index register X
|
|
IY uint16 // Index register Y
|
|
SP uint16 // Stack pointer
|
|
PC uint16 // Program counter
|
|
I byte // Interrupt vector
|
|
R byte // Memory refresh
|
|
IM byte // Interrupt mode (0, 1, or 2)
|
|
IFF1 bool // Interrupt flip-flop 1
|
|
IFF2 bool // Interrupt flip-flop 2
|
|
HALT bool // HALT state flag
|
|
MEMPTR uint16 // MEMPTR register (undocumented)
|
|
|
|
Memory Memory // Memory interface
|
|
IO IO // IO interface
|
|
}
|
|
|
|
// New creates a new Z80 CPU instance
|
|
func New(memory Memory, io IO) *CPU {
|
|
return &CPU{
|
|
Memory: memory,
|
|
IO: io,
|
|
}
|
|
}
|
|
|
|
// GetBC returns the combined value of the B and C registers
|
|
func (cpu *CPU) GetBC() uint16 {
|
|
return (uint16(cpu.B) << 8) | uint16(cpu.C)
|
|
}
|
|
|
|
// GetDE returns the combined value of the D and E registers
|
|
func (cpu *CPU) GetDE() uint16 {
|
|
return (uint16(cpu.D) << 8) | uint16(cpu.E)
|
|
}
|
|
|
|
// GetHL returns the combined value of the H and L registers
|
|
func (cpu *CPU) GetHL() uint16 {
|
|
return (uint16(cpu.H) << 8) | uint16(cpu.L)
|
|
}
|
|
|
|
// SetBC sets the B and C registers from a 16-bit value
|
|
func (cpu *CPU) SetBC(value uint16) {
|
|
cpu.B = byte(value >> 8)
|
|
cpu.C = byte(value)
|
|
}
|
|
|
|
// SetDE sets the D and E registers from a 16-bit value
|
|
func (cpu *CPU) SetDE(value uint16) {
|
|
cpu.D = byte(value >> 8)
|
|
cpu.E = byte(value)
|
|
}
|
|
|
|
// SetHL sets the H and L registers from a 16-bit value
|
|
func (cpu *CPU) SetHL(value uint16) {
|
|
cpu.H = byte(value >> 8)
|
|
cpu.L = byte(value)
|
|
}
|
|
|
|
// GetIXH returns the high byte of the IX register
|
|
func (cpu *CPU) GetIXH() byte {
|
|
return byte(cpu.IX >> 8)
|
|
}
|
|
|
|
// GetIXL returns the low byte of the IX register
|
|
func (cpu *CPU) GetIXL() byte {
|
|
return byte(cpu.IX)
|
|
}
|
|
|
|
// GetIYH returns the high byte of the IY register
|
|
func (cpu *CPU) GetIYH() byte {
|
|
return byte(cpu.IY >> 8)
|
|
}
|
|
|
|
// GetIYL returns the low byte of the IY register
|
|
func (cpu *CPU) GetIYL() byte {
|
|
return byte(cpu.IY)
|
|
}
|
|
|
|
// SetIXH sets the high byte of the IX register
|
|
func (cpu *CPU) SetIXH(value byte) {
|
|
cpu.IX = (cpu.IX & 0x00FF) | (uint16(value) << 8)
|
|
}
|
|
|
|
// SetIXL sets the low byte of the IX register
|
|
func (cpu *CPU) SetIXL(value byte) {
|
|
cpu.IX = (cpu.IX & 0xFF00) | uint16(value)
|
|
}
|
|
|
|
// SetIYH sets the high byte of the IY register
|
|
func (cpu *CPU) SetIYH(value byte) {
|
|
cpu.IY = (cpu.IY & 0x00FF) | (uint16(value) << 8)
|
|
}
|
|
|
|
// SetIYL sets the low byte of the IY register
|
|
func (cpu *CPU) SetIYL(value byte) {
|
|
cpu.IY = (cpu.IY & 0xFF00) | uint16(value)
|
|
}
|
|
|
|
// GetFlag returns the state of a specific flag
|
|
func (cpu *CPU) GetFlag(flag byte) bool {
|
|
return (cpu.F & flag) != 0
|
|
}
|
|
|
|
// SetFlag sets a flag to a specific state
|
|
func (cpu *CPU) SetFlag(flag byte, state bool) {
|
|
if state {
|
|
cpu.F |= flag
|
|
} else {
|
|
cpu.F &^= flag
|
|
}
|
|
}
|
|
|
|
// SetFlagState is a helper function to set a flag based on a boolean condition
|
|
func (cpu *CPU) SetFlagState(flag byte, condition bool) {
|
|
if condition {
|
|
cpu.F |= flag
|
|
} else {
|
|
cpu.F &^= flag
|
|
}
|
|
}
|
|
|
|
// ClearFlag clears a specific flag
|
|
func (cpu *CPU) ClearFlag(flag byte) {
|
|
cpu.F &^= flag
|
|
}
|
|
|
|
// ClearAllFlags clears all flags
|
|
func (cpu *CPU) ClearAllFlags() {
|
|
cpu.F = 0
|
|
}
|
|
|
|
// ReadImmediateByte reads the next byte from memory at PC and increments PC
|
|
func (cpu *CPU) ReadImmediateByte() byte {
|
|
value := cpu.Memory.ReadByte(cpu.PC)
|
|
cpu.PC++
|
|
return value
|
|
}
|
|
|
|
// ReadImmediateWord reads the next word from memory at PC and increments PC by 2
|
|
func (cpu *CPU) ReadImmediateWord() uint16 {
|
|
lo := cpu.Memory.ReadByte(cpu.PC)
|
|
cpu.PC++
|
|
hi := cpu.Memory.ReadByte(cpu.PC)
|
|
cpu.PC++
|
|
return (uint16(hi) << 8) | uint16(lo)
|
|
}
|
|
|
|
// ReadDisplacement reads an 8-bit signed displacement value
|
|
func (cpu *CPU) ReadDisplacement() int8 {
|
|
value := int8(cpu.Memory.ReadByte(cpu.PC))
|
|
cpu.PC++
|
|
return value
|
|
}
|
|
|
|
// ReadOpcode reads the next opcode from memory at PC and increments PC
|
|
func (cpu *CPU) ReadOpcode() byte {
|
|
opcode := cpu.Memory.ReadByte(cpu.PC)
|
|
cpu.PC++
|
|
// Increment R register (memory refresh) for each opcode fetch
|
|
// Note: R is a 7-bit register, bit 7 remains unchanged
|
|
cpu.R = (cpu.R & 0x80) | ((cpu.R + 1) & 0x7F)
|
|
return opcode
|
|
}
|
|
|
|
// Push pushes a 16-bit value onto the stack
|
|
func (cpu *CPU) Push(value uint16) {
|
|
cpu.SP -= 2
|
|
cpu.Memory.WriteWord(cpu.SP, value)
|
|
}
|
|
|
|
// Pop pops a 16-bit value from the stack
|
|
func (cpu *CPU) Pop() uint16 {
|
|
// Read low byte first, then high byte (little-endian)
|
|
lo := cpu.Memory.ReadByte(cpu.SP)
|
|
hi := cpu.Memory.ReadByte(cpu.SP + 1)
|
|
cpu.SP += 2
|
|
return (uint16(hi) << 8) | uint16(lo)
|
|
}
|
|
|
|
// UpdateSZFlags updates the S and Z flags based on an 8-bit result
|
|
func (cpu *CPU) UpdateSZFlags(result byte) {
|
|
cpu.SetFlagState(FLAG_S, (result&0x80) != 0)
|
|
cpu.SetFlagState(FLAG_Z, result == 0)
|
|
}
|
|
|
|
func (cpu *CPU) UpdatePVFlags(result byte) {
|
|
// Calculate parity (even number of 1-bits = 1, odd = 0)
|
|
parity := byte(1)
|
|
for i := 0; i < 8; i++ {
|
|
parity ^= (result >> i) & 1
|
|
}
|
|
cpu.SetFlagState(FLAG_PV, parity != 0)
|
|
}
|
|
|
|
// UpdateSZXYPVFlags updates the S, Z, X, Y, P/V flags based on an 8-bit result
|
|
func (cpu *CPU) UpdateSZXYPVFlags(result byte) {
|
|
cpu.SetFlagState(FLAG_S, (result&0x80) != 0)
|
|
cpu.SetFlagState(FLAG_Z, result == 0)
|
|
cpu.SetFlagState(FLAG_X, (result&FLAG_X) != 0)
|
|
cpu.SetFlagState(FLAG_Y, (result&FLAG_Y) != 0)
|
|
|
|
// Calculate parity (even number of 1-bits = 1, odd = 0)
|
|
parity := byte(1)
|
|
for i := 0; i < 8; i++ {
|
|
parity ^= (result >> i) & 1
|
|
}
|
|
cpu.SetFlagState(FLAG_PV, parity != 0)
|
|
}
|
|
|
|
// UpdateFlags3and5FromValue updates the X and Y flags from an 8-bit value
|
|
func (cpu *CPU) UpdateFlags3and5FromValue(value byte) {
|
|
cpu.SetFlagState(FLAG_X, (value&FLAG_X) != 0)
|
|
cpu.SetFlagState(FLAG_Y, (value&FLAG_Y) != 0)
|
|
}
|
|
|
|
// UpdateFlags3and5FromAddress updates the X and Y flags from the high byte of an address
|
|
func (cpu *CPU) UpdateFlags3and5FromAddress(address uint16) {
|
|
cpu.SetFlagState(FLAG_X, (byte(address>>8)&FLAG_X) != 0)
|
|
cpu.SetFlagState(FLAG_Y, (byte(address>>8)&FLAG_Y) != 0)
|
|
}
|
|
|
|
// UpdateSZXYFlags updates the S, Z, X, Y flags based on an 8-bit result
|
|
func (cpu *CPU) UpdateSZXYFlags(result byte) {
|
|
cpu.SetFlagState(FLAG_S, (result&0x80) != 0)
|
|
cpu.SetFlagState(FLAG_Z, result == 0)
|
|
cpu.SetFlagState(FLAG_X, (result&FLAG_X) != 0)
|
|
cpu.SetFlagState(FLAG_Y, (result&FLAG_Y) != 0)
|
|
}
|
|
|
|
// boolToByte converts a boolean to a byte (1 if true, 0 if false)
|
|
func boolToByte(b bool) byte {
|
|
if b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// ExecuteOneInstruction executes a single instruction and returns the number of T-states used
|
|
func (cpu *CPU) ExecuteOneInstruction() int {
|
|
// Handle interrupts first if enabled
|
|
if cpu.IFF1 && cpu.IO.CheckInterrupt() {
|
|
return cpu.HandleInterrupt()
|
|
}
|
|
|
|
// Handle HALT state
|
|
if cpu.HALT {
|
|
return 4 // 4 T-states for HALT
|
|
}
|
|
|
|
// Read the next opcode
|
|
opcode := cpu.ReadOpcode()
|
|
|
|
// Handle prefixed opcodes
|
|
switch opcode {
|
|
case 0xCB:
|
|
cbOpcode := cpu.ReadOpcode()
|
|
return cpu.ExecuteCBOpcode(cbOpcode)
|
|
case 0xDD:
|
|
ddOpcode := cpu.ReadOpcode()
|
|
return cpu.ExecuteDDOpcode(ddOpcode)
|
|
case 0xED:
|
|
edOpcode := cpu.ReadOpcode()
|
|
return cpu.ExecuteEDOpcode(edOpcode)
|
|
case 0xFD:
|
|
fdOpcode := cpu.ReadOpcode()
|
|
return cpu.ExecuteFDOpcode(fdOpcode)
|
|
default:
|
|
return cpu.ExecuteOpcode(opcode)
|
|
}
|
|
}
|
|
|
|
// HandleInterrupt handles interrupt processing
|
|
func (cpu *CPU) HandleInterrupt() int {
|
|
// Exit HALT state if in HALT
|
|
if cpu.HALT {
|
|
cpu.HALT = false
|
|
}
|
|
|
|
// Reset interrupt flip-flops
|
|
cpu.IFF1 = false
|
|
cpu.IFF2 = false
|
|
|
|
// Handle interrupt based on mode
|
|
switch cpu.IM {
|
|
case 0, 1:
|
|
// Mode 0/1: Restart at address 0x0038
|
|
cpu.Push(cpu.PC)
|
|
cpu.PC = 0x0038
|
|
return 13 // 13 T-states for interrupt handling
|
|
case 2:
|
|
// Mode 2: Call interrupt vector
|
|
cpu.Push(cpu.PC)
|
|
vectorAddr := (uint16(cpu.I) << 8) | 0xFF // Use 0xFF as vector for non-maskable interrupt
|
|
cpu.PC = cpu.Memory.ReadWord(vectorAddr)
|
|
return 19 // 19 T-states for interrupt handling
|
|
default:
|
|
// Should not happen, but handle gracefully
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// HandleNMI handles non-maskable interrupt
|
|
func (cpu *CPU) HandleNMI() int {
|
|
// Save current PC on stack
|
|
cpu.Push(cpu.PC)
|
|
|
|
// Jump to NMI handler
|
|
cpu.PC = 0x0066
|
|
|
|
// Disable interrupts
|
|
cpu.IFF1 = false
|
|
|
|
return 11 // 11 T-states for NMI handling
|
|
}
|
|
|
|
// GetAF returns the combined value of the A and F registers
|
|
func (cpu *CPU) GetAF() uint16 {
|
|
return (uint16(cpu.A) << 8) | uint16(cpu.F)
|
|
}
|
|
|
|
// SetAF sets the A and F registers from a 16-bit value
|
|
func (cpu *CPU) SetAF(value uint16) {
|
|
cpu.A = byte(value >> 8)
|
|
cpu.F = byte(value)
|
|
}
|
|
|
|
// GetAF_ returns the combined value of the alternate A_ and F_ registers
|
|
func (cpu *CPU) GetAF_() uint16 {
|
|
return (uint16(cpu.A_) << 8) | uint16(cpu.F_)
|
|
}
|
|
|
|
// SetAF_ sets the alternate A_ and F_ registers from a 16-bit value
|
|
func (cpu *CPU) SetAF_(value uint16) {
|
|
cpu.A_ = byte(value >> 8)
|
|
cpu.F_ = byte(value)
|
|
}
|
|
|
|
// GetBC_ returns the combined value of the alternate B_ and C_ registers
|
|
func (cpu *CPU) GetBC_() uint16 {
|
|
return (uint16(cpu.B_) << 8) | uint16(cpu.C_)
|
|
}
|
|
|
|
// GetDE_ returns the combined value of the alternate D_ and E_ registers
|
|
func (cpu *CPU) GetDE_() uint16 {
|
|
return (uint16(cpu.D_) << 8) | uint16(cpu.E_)
|
|
}
|
|
|
|
// GetHL_ returns the combined value of the alternate H_ and L_ registers
|
|
func (cpu *CPU) GetHL_() uint16 {
|
|
return (uint16(cpu.H_) << 8) | uint16(cpu.L_)
|
|
}
|
|
|
|
// SetBC_ sets the alternate B_ and C_ registers from a 16-bit value
|
|
func (cpu *CPU) SetBC_(value uint16) {
|
|
cpu.B_ = byte(value >> 8)
|
|
cpu.C_ = byte(value)
|
|
}
|
|
|
|
// SetDE_ sets the alternate D_ and E_ registers from a 16-bit value
|
|
func (cpu *CPU) SetDE_(value uint16) {
|
|
cpu.D_ = byte(value >> 8)
|
|
cpu.E_ = byte(value)
|
|
}
|
|
|
|
// SetHL_ sets the alternate H_ and L_ registers from a 16-bit value
|
|
func (cpu *CPU) SetHL_(value uint16) {
|
|
cpu.H_ = byte(value >> 8)
|
|
cpu.L_ = byte(value)
|
|
}
|
|
|
|
// UpdateXYFlags updates the undocumented X and Y flags based on an 8-bit result
|
|
func (cpu *CPU) UpdateXYFlags(result byte) {
|
|
cpu.SetFlagState(FLAG_X, (result&FLAG_X) != 0)
|
|
cpu.SetFlagState(FLAG_Y, (result&FLAG_Y) != 0)
|
|
}
|