Files
emuz80go/z80.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)
}