Documentation
¶
Overview ¶
Package cpu6502 provides a cycle-accurate emulator for the MOS Technology 6502 microprocessor and its variants.
The 6502 is an 8-bit microprocessor that was widely used in home computers and game consoles during the 1970s and 1980s, including the Apple II, Commodore 64, and Atari 2600.
This implementation supports:
- All 151 official 6502 instructions
- Multiple CPU variants (NMOS 6502, CMOS 65C02, Ricoh 2A03)
- Cycle-accurate timing including page boundary crossing
- Decimal mode (BCD) arithmetic
- Interrupt handling (IRQ, NMI, BRK)
- Many unofficial/illegal opcodes
Basic usage:
bus := &SimpleBus{}
cpu := cpu6502.NewCPU(bus)
cpu.Reset()
for cpu.RemainingCycles() > 0 {
if err := cpu.Clock(); err != nil {
log.Fatal(err)
}
}
Package cpu6502 provides type definitions for the 6502 CPU emulator.
This file contains all core type definitions, constants, and enums used throughout the emulator. Having types in a dedicated file provides:
- A single source of truth for all data structures
- Easy reference for developers
- Better documentation organization
- Reduced clutter in the main CPU implementation
Index ¶
- func FormatFlags(p Flags) string
- type AddrModeType
- type Bus
- type CPU
- func (c *CPU) ABS() uint8
- func (c *CPU) ABX() uint8
- func (c *CPU) ABY() uint8
- func (c *CPU) ADC() uint8
- func (c *CPU) AND() uint8
- func (c *CPU) ASL() uint8
- func (c *CPU) BCC() uint8
- func (c *CPU) BCS() uint8
- func (c *CPU) BEQ() uint8
- func (c *CPU) BIT() uint8
- func (c *CPU) BMI() uint8
- func (c *CPU) BNE() uint8
- func (c *CPU) BPL() uint8
- func (c *CPU) BRK() uint8
- func (c *CPU) BVC() uint8
- func (c *CPU) BVS() uint8
- func (c *CPU) CLC() uint8
- func (c *CPU) CLD() uint8
- func (c *CPU) CLI() uint8
- func (c *CPU) CLV() uint8
- func (c *CPU) CMP() uint8
- func (c *CPU) CPX() uint8
- func (c *CPU) CPY() uint8
- func (c *CPU) ClearNMI()
- func (c *CPU) Clock() error
- func (c *CPU) CurrentOpcode() uint8
- func (c *CPU) DEC() uint8
- func (c *CPU) DEX() uint8
- func (c *CPU) DEY() uint8
- func (c *CPU) DisableInstructionCache()
- func (c *CPU) Disassemble(startAddr, endAddr uint16) map[uint16]string
- func (c *CPU) EOR() uint8
- func (c *CPU) EnableInstructionCache()
- func (c *CPU) GetCurrentInstruction() *Instruction
- func (c *CPU) GetState() string
- func (c *CPU) GetStateSnapshot() State
- func (c *CPU) HasPendingInterrupt() bool
- func (c *CPU) IMM() uint8
- func (c *CPU) IMP() uint8
- func (c *CPU) INC() uint8
- func (c *CPU) IND() uint8
- func (c *CPU) INX() uint8
- func (c *CPU) INY() uint8
- func (c *CPU) IZX() uint8
- func (c *CPU) IZY() uint8
- func (c *CPU) InstructionCacheStats() (hits, misses uint64, hitRate float64)
- func (c *CPU) InterruptRequest()deprecated
- func (c *CPU) InvalidateInstructionCache()
- func (c *CPU) IsIllegalOpcode(opcode uint8) bool
- func (c *CPU) JMP() uint8
- func (c *CPU) JSR() uint8
- func (c *CPU) LDA() uint8
- func (c *CPU) LDX() uint8
- func (c *CPU) LDY() uint8
- func (c *CPU) LSR() uint8
- func (c *CPU) LastError() *CPUError
- func (c *CPU) LookupInstruction(opcode uint8) Instruction
- func (c *CPU) LookupTable() [256]Instruction
- func (c *CPU) NOP() uint8
- func (c *CPU) NonMaskableInterrupt()deprecated
- func (c *CPU) ORA() uint8
- func (c *CPU) Opcode() uint8
- func (c *CPU) PHA() uint8
- func (c *CPU) PHP() uint8
- func (c *CPU) PLA() uint8
- func (c *CPU) PLP() uint8
- func (c *CPU) REL() uint8
- func (c *CPU) ROL() uint8
- func (c *CPU) ROR() uint8
- func (c *CPU) ROR_RevA() uint8
- func (c *CPU) RTI() uint8
- func (c *CPU) RTS() uint8
- func (c *CPU) RemainingCycles() uint8
- func (c *CPU) Reset()
- func (c *CPU) SBC() uint8
- func (c *CPU) SEC() uint8
- func (c *CPU) SED() uint8
- func (c *CPU) SEI() uint8
- func (c *CPU) STA() uint8
- func (c *CPU) STX() uint8
- func (c *CPU) STY() uint8
- func (c *CPU) SetCycles(cycles uint8)
- func (c *CPU) SetIRQ(asserted bool)
- func (c *CPU) SetNMI(asserted bool)
- func (c *CPU) TAX() uint8
- func (c *CPU) TAY() uint8
- func (c *CPU) TSX() uint8
- func (c *CPU) TXA() uint8
- func (c *CPU) TXS() uint8
- func (c *CPU) TYA() uint8
- func (c *CPU) TotalCycles() uint64
- func (c *CPU) Variant() CPUVariant
- func (c *CPU) XXX() uint8
- func (c *CPU) ZP0() uint8
- func (c *CPU) ZPX() uint8
- func (c *CPU) ZPY() uint8
- type CPUBuilder
- func (b *CPUBuilder) Build() *CPU
- func (b *CPUBuilder) DisableDecimalMode() *CPUBuilder
- func (b *CPUBuilder) DisableInstructionCache() *CPUBuilder
- func (b *CPUBuilder) WithErrorHandler(handler ErrorHandler) *CPUBuilder
- func (b *CPUBuilder) WithInstructionCacheSize(size int) *CPUBuilder
- func (b *CPUBuilder) WithStrictMode() *CPUBuilder
- func (b *CPUBuilder) WithVariant(variant CPUVariant) *CPUBuilder
- type CPUConfig
- type CPUError
- type CPUVariant
- type ErrorHandler
- type ErrorType
- type Flags
- type Instruction
- type InstructionCache
- type InstructionCacheEntry
- type LoggingErrorHandler
- type State
- type StrictErrorHandler
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FormatFlags ¶
FormatFlags returns a human-readable string representation of processor flags.
The format is "NVUBDIZC" where each letter represents a flag:
- N: Negative
- V: Overflow
- U: Unused (always 1)
- B: Break
- D: Decimal
- I: Interrupt Disable
- Z: Zero
- C: Carry
Set flags are shown as their letter, cleared flags as '.'
Example: "N.U.D.Z." means N, U, D, and Z flags are set
Types ¶
type AddrModeType ¶
type AddrModeType uint8
AddrModeType represents the addressing mode of an instruction
const ( AddrModeIMP AddrModeType = iota // Implied AddrModeIMM // Immediate AddrModeZP0 // Zero Page AddrModeZPX // Zero Page, X AddrModeZPY // Zero Page, Y AddrModeREL // Relative AddrModeABS // Absolute AddrModeABX // Absolute, X AddrModeABY // Absolute, Y AddrModeIND // Indirect AddrModeIZX // Indexed Indirect AddrModeIZY // Indirect Indexed )
func (AddrModeType) String ¶
func (a AddrModeType) String() string
String returns the addressing mode name for debugging
type Bus ¶
type Bus interface {
// Read returns the byte at the specified address.
//
// The address space is 16-bit (0x0000-0xFFFF). Implementations
// must handle all possible addresses, even if they map to the
// same physical memory or return constant values.
//
// For memory-mapped I/O, reads may have side effects (e.g.,
// clearing interrupt flags, advancing hardware state).
Read(addr uint16) uint8
// Write stores a byte at the specified address.
//
// The address space is 16-bit (0x0000-0xFFFF). Implementations
// may ignore writes to read-only regions (ROM) or trigger
// hardware behavior for memory-mapped I/O addresses.
//
// For memory-mapped I/O, writes may trigger immediate hardware
// actions (e.g., sending data to a device, updating graphics).
Write(addr uint16, data uint8)
}
Bus defines the interface for memory access.
Implementations of this interface provide the CPU with access to memory and memory-mapped I/O. The interface is intentionally simple to allow for flexible implementations.
Memory Mapping Patterns ¶
The Bus interface supports various memory mapping strategies:
- Simple RAM: Direct array access for the full 64KB address space
- Banked Memory: Multiple memory banks switched via control registers
- Memory-Mapped I/O: Special addresses that trigger hardware behavior
- ROM/RAM Combinations: Read-only regions mixed with writable RAM
Implementation Guidelines ¶
Implementations should:
- Handle all 64KB addresses (0x0000-0xFFFF)
- Return consistent values for reads (no side effects unless intended)
- Complete operations quickly (the CPU expects cycle-accurate timing)
- Consider thread safety if used in concurrent contexts
Example Implementation ¶
Simple 64KB RAM:
type SimpleBus struct {
ram [65536]uint8
}
func (b *SimpleBus) Read(addr uint16) uint8 {
return b.ram[addr]
}
func (b *SimpleBus) Write(addr uint16, data uint8) {
b.ram[addr] = data
}
Memory-mapped I/O example:
type IOBus struct {
ram [65536]uint8
output io.Writer
}
func (b *IOBus) Write(addr uint16, data uint8) {
if addr == 0x6000 {
// Write to output device
b.output.Write([]byte{data})
} else {
b.ram[addr] = data
}
}
type CPU ¶
type CPU struct {
// Registers (public for direct access)
A uint8 // Accumulator
X uint8 // X Index Register
Y uint8 // Y Index Register
SP uint8 // Stack Pointer (relative to $0100)
PC uint16 // Program Counter
P Flags // Processor Status Register (Flags)
// contains filtered or unexported fields
}
CPU represents a MOS Technology 6502 microprocessor.
The CPU executes instructions fetched from memory via the Bus interface. It maintains internal registers (A, X, Y, SP, PC, P) and provides cycle-accurate emulation of the 6502 instruction set.
The CPU operates in a fetch-decode-execute cycle:
- Fetch opcode from memory at PC
- Decode opcode using lookup table
- Execute addressing mode calculation
- Execute instruction operation
- Update cycle counter
Example:
cpu := cpu6502.NewCPU(bus)
cpu.Reset()
for {
if err := cpu.Clock(); err != nil {
break
}
}
func NewCPU ¶
NewCPU creates a new 6502 CPU instance with default configuration.
The CPU is initialized with:
- All registers cleared
- Stack pointer at $FD
- Status flags: U and I set
- NMOS 6502 variant
- Logging error handler
The CPU must be reset before execution:
cpu := NewCPU(bus) cpu.Reset() // Loads PC from reset vector at $FFFC/FD
For custom configuration, use NewCPUWithConfig or the builder pattern.
Parameters:
- bus: The memory bus interface
Returns a new CPU instance ready to be reset and executed.
func NewCPUWithConfig ¶
NewCPUWithConfig creates a new CPU with full configuration.
This function provides complete control over CPU initialization. It applies the configuration and initializes the CPU's internal state.
Parameters:
- bus: The memory bus interface
- config: The configuration to apply
Returns a new CPU instance with the specified configuration.
Example:
config := cpu6502.DefaultConfig() config.Variant = cpu6502.VariantCMOS65C02 config.StrictMode = true cpu := cpu6502.NewCPUWithConfig(bus, config)
func NewCPUWithErrorHandler ¶
func NewCPUWithErrorHandler(bus Bus, handler ErrorHandler) *CPU
NewCPUWithErrorHandler creates a new CPU with custom error handler.
This is a convenience function for the common case of only needing to change the error handler from the default.
Parameters:
- bus: The memory bus interface
- handler: The error handler to use
Returns a new CPU instance with the specified error handler.
Example:
handler := &MyCustomErrorHandler{}
cpu := cpu6502.NewCPUWithErrorHandler(bus, handler)
func NewCPUWithVariant ¶
func NewCPUWithVariant(bus Bus, variant CPUVariant) *CPU
NewCPUWithVariant creates a new CPU with specified variant.
This is a convenience function for the common case of only needing to change the CPU variant from the default.
Parameters:
- bus: The memory bus interface
- variant: The CPU variant to emulate
Returns a new CPU instance with the specified variant.
Example:
cpu := cpu6502.NewCPUWithVariant(bus, cpu6502.VariantCMOS65C02)
func NewCPUWithVariantAndErrorHandler ¶
func NewCPUWithVariantAndErrorHandler(bus Bus, variant CPUVariant, handler ErrorHandler) *CPU
NewCPUWithVariantAndErrorHandler creates a new CPU with specified variant and error handler.
This is a convenience function for the common case of needing to change both the variant and error handler.
Parameters:
- bus: The memory bus interface
- variant: The CPU variant to emulate
- handler: The error handler to use
Returns a new CPU instance with the specified configuration.
Example:
handler := &MyCustomErrorHandler{}
cpu := cpu6502.NewCPUWithVariantAndErrorHandler(bus,
cpu6502.VariantCMOS65C02, handler)
func (*CPU) ABS ¶
ABS - Absolute addressing mode.
The full 16-bit address is specified in the next two bytes (low byte first, then high byte - little endian).
Example: LDA $1234
- Opcode at PC
- Low byte $34 at PC+1
- High byte $12 at PC+2
- Loads value from address $1234
Returns 0 (no page cross possible - address is explicit).
func (*CPU) ABX ¶
ABX - Absolute,X addressing mode.
The X register is added to the 16-bit base address to form the effective address. If the addition crosses a page boundary, returns 1 to indicate an extra cycle may be needed.
Example: LDA $1234,X (with X=$10)
- Base address $1234
- Effective address: $1234 + $10 = $1244
- No page cross (both in page $12)
Page cross example: LDA $12FF,X (with X=$02)
- Base address $12FF
- Effective address: $12FF + $02 = $1301
- Page cross! ($12 -> $13)
- Returns 1 for potential extra cycle
Returns 1 if page boundary crossed, 0 otherwise.
func (*CPU) ABY ¶
ABY - Absolute,Y addressing mode.
Similar to ABX, but uses the Y register instead of X.
Example: LDA $1234,Y (with Y=$10)
- Effective address: $1234 + $10 = $1244
Returns 1 if page boundary crossed, 0 otherwise.
func (*CPU) ADC ¶
ADC - Add with Carry
Performs A = A + M + C, where:
A = Accumulator M = Memory operand C = Carry flag (0 or 1)
In binary mode (D=0):
- Standard 8-bit addition with carry
- C flag set if result > 255 (unsigned overflow)
- V flag set if signed overflow occurs
- Z flag set if result is zero
- N flag set if bit 7 of result is 1
In decimal mode (D=1):
- BCD (Binary Coded Decimal) addition
- Each nibble represents 0-9 (not 0-F)
- Adjustments made when nibble exceeds 9
- N/V flags based on binary intermediate result (NMOS behavior)
- C flag set if BCD result > 99
- Z flag set if BCD result is 00
func (*CPU) AND ¶
AND - Logical AND Performs a bitwise AND between the accumulator and memory. Result is stored in the accumulator. Flags affected: Z, N
func (*CPU) ASL ¶
ASL - Arithmetic Shift Left Shifts all bits left one position. Bit 0 is set to 0. The original bit 7 is shifted into the Carry flag. Flags affected: C, Z, N
func (*CPU) BEQ ¶
BEQ - Branch if Equal (Zero Set) Branches if the Zero flag is 1. Flags affected: None
func (*CPU) BIT ¶
BIT - Bit Test Tests bits in memory with the accumulator without storing the result. The Z flag is set based on the AND result (A & M). The N flag is set to bit 7 of the memory value. The V flag is set to bit 6 of the memory value. Flags affected: Z, N, V
func (*CPU) BMI ¶
BMI - Branch if Minus (Negative Set) Branches if the Negative flag is 1. Flags affected: None
func (*CPU) BNE ¶
BNE - Branch if Not Equal (Zero Clear) Branches if the Zero flag is 0. Flags affected: None
func (*CPU) BPL ¶
BPL - Branch if Plus (Negative Clear) Branches if the Negative flag is 0. Flags affected: None
func (*CPU) BRK ¶
BRK - Break Triggers a software interrupt (IRQ). Pushes PC+2 and processor status (with B and U flags set) onto the stack, sets the I flag, and jumps to the IRQ vector at $FFFE/F.
Note: The 6502 increments PC twice after fetching the BRK opcode, so the pushed PC points two bytes after the BRK instruction. Flags affected: B (set in pushed copy only), I (set in actual register)
func (*CPU) BVC ¶
BVC - Branch if Overflow Clear Branches if the Overflow flag is 0. Flags affected: None
func (*CPU) BVS ¶
BVS - Branch if Overflow Set Branches if the Overflow flag is 1. Flags affected: None
func (*CPU) CLD ¶
CLD - Clear Decimal Mode Flag Sets the Decimal mode flag to 0, disabling BCD arithmetic. Flags affected: D
func (*CPU) CLI ¶
CLI - Clear Interrupt Disable Flag Sets the Interrupt disable flag to 0, enabling IRQ interrupts. Flags affected: I
func (*CPU) CMP ¶
CMP - Compare Accumulator Compares the accumulator with a memory value by subtracting (A - M). The result is not stored, only flags are affected. C flag is set if A >= M (no borrow needed). Z flag is set if A == M. N flag is set based on bit 7 of the result. Flags affected: C, Z, N
func (*CPU) CPX ¶
CPX - Compare X Register Compares the X register with a memory value by subtracting (X - M). The result is not stored, only flags are affected. C flag is set if X >= M (no borrow needed). Z flag is set if X == M. N flag is set based on bit 7 of the result. Flags affected: C, Z, N
func (*CPU) CPY ¶
CPY - Compare Y Register Compares the Y register with a memory value by subtracting (Y - M). The result is not stored, only flags are affected. C flag is set if Y >= M (no borrow needed). Z flag is set if Y == M. N flag is set based on bit 7 of the result. Flags affected: C, Z, N
func (*CPU) ClearNMI ¶
func (c *CPU) ClearNMI()
ClearNMI clears the pending NMI.
This is called internally after NMI is serviced. It should not normally be called by user code, as the CPU handles this automatically.
func (*CPU) Clock ¶
Clock executes one clock cycle of the CPU.
This method should be called repeatedly to execute instructions. Each instruction takes multiple cycles to complete. The CPU tracks remaining cycles internally and fetches the next instruction when the current one completes.
Returns an error if an unrecoverable error occurs (e.g., illegal opcode in strict mode). The error can be handled or ignored based on the configured error handler.
Example:
for {
if err := cpu.Clock(); err != nil {
log.Printf("CPU error: %v", err)
break
}
}
func (*CPU) CurrentOpcode ¶
CurrentOpcode returns the opcode of the currently executing instruction.
This is the opcode that was fetched at the start of the current instruction's execution. It remains valid until the next instruction is fetched.
Returns the current opcode byte (0x00-0xFF).
Example:
opcode := cpu.CurrentOpcode()
instr := cpu.LookupInstruction(opcode)
fmt.Printf("Executing: %s\n", instr.Name)
func (*CPU) DEC ¶
DEC - Decrement Memory Subtracts 1 from the value at the memory location. Flags affected: Z, N
func (*CPU) DisableInstructionCache ¶
func (c *CPU) DisableInstructionCache()
DisableInstructionCache disables the instruction cache
func (*CPU) Disassemble ¶
Disassemble disassembles instructions in the specified memory range.
This method reads memory and decodes instructions, producing a map of addresses to disassembled instruction strings. The format matches common 6502 assembler syntax.
Parameters:
- startAddr: Starting address to disassemble
- endAddr: Ending address to disassemble (inclusive)
Returns a map where keys are instruction addresses and values are disassembled instruction strings.
Format examples:
- "LDA #$42" (immediate)
- "STA $1234" (absolute)
- "BNE $8010" (relative, shows target address)
- "JMP ($FFFC)" (indirect)
The disassembler handles:
- All addressing modes correctly
- Relative branch target calculation
- Multi-byte operands (little-endian)
- Illegal opcodes (marked with *)
Example:
disasm := cpu.Disassemble(0x8000, 0x8010)
for addr := uint16(0x8000); addr <= 0x8010; {
if instr, ok := disasm[addr]; ok {
fmt.Printf("$%04X: %s\n", addr, instr)
// Advance by instruction length
addr += uint16(cpu.LookupInstruction(cpu.read(addr)).Length)
}
}
func (*CPU) EOR ¶
EOR - Exclusive OR Performs a bitwise XOR between the accumulator and memory. Result is stored in the accumulator. Flags affected: Z, N
func (*CPU) EnableInstructionCache ¶
func (c *CPU) EnableInstructionCache()
EnableInstructionCache enables the instruction cache
func (*CPU) GetCurrentInstruction ¶
func (c *CPU) GetCurrentInstruction() *Instruction
GetCurrentInstruction returns a pointer to the current instruction definition. Returns nil if no instruction has been fetched yet.
func (*CPU) GetState ¶
GetState returns a formatted string representation of the CPU state.
This provides a human-readable view of the CPU state, useful for debugging and logging. The format is similar to common 6502 debuggers.
Format: PC:XXXX A:XX X:XX Y:XX P:XX[FLAGS] SP:XX CYC:NNNN (INSTR $XX)
Where FLAGS is an 8-character string showing each flag:
- N: Negative
- V: Overflow
- U: Unused (always set)
- B: Break
- D: Decimal
- I: Interrupt Disable
- Z: Zero
- C: Carry
Returns a formatted string describing the current state.
Example output:
PC:8000 A:42 X:10 Y:20 P:24[..U.D...] SP:FD CYC:1234 (LDA $A9)
func (*CPU) GetStateSnapshot ¶
GetStateSnapshot returns a snapshot of the current CPU state.
This captures all relevant CPU state in a single struct, useful for:
- Debugging and logging
- Implementing save states
- Testing and verification
- Comparing states across executions
The snapshot includes:
- All registers (A, X, Y, SP, PC, P)
- Cycle counters
- Current opcode and instruction
- Interrupt state
Returns a State struct containing the complete CPU state.
Example:
state := cpu.GetStateSnapshot()
fmt.Printf("PC: $%04X, A: $%02X\n", state.PC, state.A)
func (*CPU) HasPendingInterrupt ¶
HasPendingInterrupt returns true if any interrupt is pending.
This checks for:
- Pending NMI (always serviceable)
- Asserted IRQ with I flag clear (maskable)
Returns true if an interrupt will be serviced at the next instruction boundary.
Example:
if cpu.HasPendingInterrupt() {
fmt.Println("Interrupt will be serviced soon")
}
func (*CPU) IMM ¶
IMM - Immediate addressing mode.
The operand is the byte immediately following the opcode. The effective address is the current PC value.
Example: LDA #$42
- Opcode at PC
- Operand $42 at PC+1
- Loads the literal value $42 into A
Returns 0 (no page cross possible).
func (*CPU) IMP ¶
IMP - Implied addressing mode.
The operand is implied by the instruction itself. No additional bytes are read from memory. Used by instructions like:
- Register transfers (TAX, TXA, etc.)
- Stack operations (PHA, PLA, etc.)
- Flag operations (CLC, SEC, etc.)
Example: TAX (Transfer A to X)
- No operand needed
- Operation is implied
Returns 0 (no page cross possible).
func (*CPU) INC ¶
INC - Increment Memory Adds 1 to the value at the memory location. Flags affected: Z, N
func (*CPU) IND ¶
IND - Indirect addressing mode.
Used only by JMP instruction. The operand is a 16-bit address that points to the actual target address (pointer to pointer).
NMOS 6502 Bug: If the low byte of the pointer is $FF, the high byte is fetched from $xx00 instead of $xx00+1, due to a hardware bug. The CMOS 65C02 fixes this bug.
Example: JMP ($1234)
- Pointer address $1234 specified in instruction
- Low byte of target read from $1234
- High byte of target read from $1235
- Jump to the constructed 16-bit address
Bug example (NMOS only): JMP ($12FF)
- Low byte read from $12FF
- High byte read from $1200 (not $1300!)
- This is the famous indirect JMP bug
Returns 0 (no page cross possible).
func (*CPU) IZX ¶
IZX - Indexed Indirect addressing mode (Indirect,X).
The X register is added to the zero page address to get a pointer address. The actual operand address is then read from this pointer. All arithmetic wraps within the zero page.
Example: LDA ($40,X) with X=$05
- Base zero page address $40
- Add X: $40 + $05 = $45
- Read pointer from $0045 (low) and $0046 (high)
- If pointer = $1234, load from $1234
Wrapping example: LDA ($FF,X) with X=$02
- Pointer address: ($FF + $02) & $FF = $01
- Read pointer from $0001 and $0002
- Note: wraps within zero page
Returns 0 (no page cross possible in pointer calculation).
func (*CPU) IZY ¶
IZY - Indirect Indexed addressing mode (Indirect),Y.
A zero page address points to a base address. The Y register is then added to this base address to get the effective address. If the addition crosses a page boundary, returns 1.
Example: LDA ($40),Y with Y=$10
- Read pointer from $0040 (low) and $0041 (high)
- If pointer = $1234, base address = $1234
- Add Y: $1234 + $10 = $1244
- Load from $1244
Page cross example: LDA ($40),Y with Y=$10
- Pointer at $40 = $12FF
- Add Y: $12FF + $10 = $130F
- Page cross! ($12 -> $13)
- Returns 1 for potential extra cycle
Returns 1 if page boundary crossed, 0 otherwise.
func (*CPU) InstructionCacheStats ¶
InstructionCacheStats returns cache performance statistics
func (*CPU) InterruptRequest
deprecated
func (c *CPU) InterruptRequest()
InterruptRequest is deprecated. Use SetIRQ(true) instead.
This method is kept for backward compatibility with existing code. It immediately asserts the IRQ line and attempts to handle the interrupt if conditions allow.
Deprecated: Use SetIRQ(true) for proper interrupt handling.
func (*CPU) InvalidateInstructionCache ¶
func (c *CPU) InvalidateInstructionCache()
InvalidateInstructionCache clears the instruction cache Call this after self-modifying code or when loading new programs
func (*CPU) IsIllegalOpcode ¶
IsIllegalOpcode returns true if the given opcode is illegal/unofficial.
Illegal opcodes are undocumented instructions that exist due to the 6502's internal logic but were not officially supported. Some programs use these for various purposes.
Parameters:
- opcode: The opcode to check (0x00-0xFF)
Returns true if the opcode is illegal, false if it's official.
Example:
if cpu.IsIllegalOpcode(0x04) {
fmt.Println("This is an illegal NOP")
}
func (*CPU) JMP ¶
JMP - Jump Sets the program counter to the specified address. This is an unconditional jump. Flags affected: None
func (*CPU) JSR ¶
JSR - Jump to Subroutine Pushes the return address (PC-1) onto the stack and jumps to the subroutine. The return address points to the last byte of the JSR instruction, so RTS will increment it to return to the next instruction. Flags affected: None
func (*CPU) LDA ¶
LDA - Load Accumulator Loads a value from memory into the accumulator. Flags affected: Z, N
func (*CPU) LDX ¶
LDX - Load X Register Loads a value from memory into the X register. Flags affected: Z, N
func (*CPU) LDY ¶
LDY - Load Y Register Loads a value from memory into the Y register. Flags affected: Z, N
func (*CPU) LSR ¶
LSR - Logical Shift Right Shifts all bits right one position. Bit 7 is set to 0. The original bit 0 is shifted into the Carry flag. Flags affected: C, Z, N
func (*CPU) LastError ¶
LastError returns the last error that occurred during execution.
This provides access to detailed error information including:
- Error type
- Opcode that caused the error
- Program counter at the time of error
- Descriptive message
Returns nil if no error has occurred, or a pointer to the last CPUError.
Example:
if err := cpu.Clock(); err != nil {
if cpuErr := cpu.LastError(); cpuErr != nil {
fmt.Printf("Error at $%04X: %s\n", cpuErr.PC, cpuErr.Message)
}
}
func (*CPU) LookupInstruction ¶
func (c *CPU) LookupInstruction(opcode uint8) Instruction
LookupInstruction returns the instruction definition for a given opcode.
This provides access to the instruction's metadata including:
- Name (mnemonic)
- Cycle count
- Addressing mode
- Whether it's an illegal opcode
Parameters:
- opcode: The opcode to look up (0x00-0xFF)
Returns the Instruction struct for the given opcode.
Example:
instr := cpu.LookupInstruction(0xA9) // LDA immediate
fmt.Printf("%s takes %d cycles\n", instr.Name, instr.Cycles)
func (*CPU) LookupTable ¶
func (c *CPU) LookupTable() [256]Instruction
LookupTable exposes the instruction lookup table.
This provides direct access to the 256-entry instruction table, primarily intended for tools like disassemblers or UI displays.
Each entry contains:
- Name: Instruction mnemonic
- Operate: Function pointer to implementation
- AddrMode: Function pointer to addressing mode
- AddrModeType: Addressing mode enum
- Cycles: Base cycle count
- Length: Instruction length in bytes
- PageCrossPenalty: Whether page cross adds a cycle
- Illegal: Whether this is an unofficial opcode
Returns a copy of the lookup table array.
Example:
table := cpu.LookupTable()
for opcode, instr := range table {
if !instr.Illegal {
fmt.Printf("$%02X: %s (%d cycles)\n",
opcode, instr.Name, instr.Cycles)
}
}
Warning: Use with caution. Modifying the returned array does not affect the CPU's internal table (it's a copy).
func (*CPU) NOP ¶
NOP - No Operation Does nothing. Takes 2 cycles. Some unofficial opcodes are also NOPs with different addressing modes and cycle counts, but they all use this same implementation. Flags affected: None
func (*CPU) NonMaskableInterrupt
deprecated
func (c *CPU) NonMaskableInterrupt()
NonMaskableInterrupt is deprecated. Use SetNMI(false) after SetNMI(true) instead.
This method is kept for backward compatibility with existing code. It creates a falling edge on the NMI line by setting it high then low, which triggers the NMI interrupt.
Deprecated: Use SetNMI(true) followed by SetNMI(false) for proper NMI handling.
func (*CPU) ORA ¶
ORA - Logical OR (Inclusive) Performs a bitwise OR between the accumulator and memory. Result is stored in the accumulator. Flags affected: Z, N
func (*CPU) Opcode ¶
Opcode returns the last fetched opcode. Useful for debugging/halt conditions. Deprecated: Use CurrentOpcode() instead
func (*CPU) PHA ¶
PHA - Push Accumulator Pushes the accumulator value onto the stack. Stack pointer is decremented after the push. Flags affected: None
func (*CPU) PHP ¶
PHP - Push Processor Status Pushes the processor status register (flags) onto the stack. The B and U flags are set in the pushed copy but not in the actual register. Stack pointer is decremented after the push. Flags affected: None
func (*CPU) PLA ¶
PLA - Pull Accumulator Pulls a value from the stack into the accumulator. Stack pointer is incremented before the pull. Flags affected: Z, N
func (*CPU) PLP ¶
PLP - Pull Processor Status Pulls a value from the stack into the processor status register (flags). The B and U flags are preserved from the current register, not the pulled value. Stack pointer is incremented before the pull. Flags affected: All (except B and U which are preserved)
func (*CPU) REL ¶
REL - Relative addressing mode.
Used exclusively by branch instructions. The operand is a signed 8-bit offset (-128 to +127) relative to the address of the next instruction (PC after reading the offset).
The effective address is calculated as: PC + offset If bit 7 of the offset is set, it's treated as negative.
Example: BEQ $10 (at address $8000)
- Opcode at $8000
- Offset $10 at $8001
- PC after reading = $8002
- Branch target = $8002 + $10 = $8012
Negative example: BEQ $FE (at address $8000)
- Offset $FE = -2 in two's complement
- PC after reading = $8002
- Branch target = $8002 + (-2) = $8000
Returns 0 (page cross is handled by branch instructions).
func (*CPU) ROL ¶
ROL - Rotate Left Shifts all bits left one position. The Carry flag is shifted into bit 0. The original bit 7 is shifted into the Carry flag. Flags affected: C, Z, N
func (*CPU) ROR ¶
ROR - Rotate Right Shifts all bits right one position. The Carry flag is shifted into bit 7. The original bit 0 is shifted into the Carry flag. Flags affected: C, Z, N
func (*CPU) ROR_RevA ¶ added in v0.0.4
ROR_RevA - Rotate Right (Rev A Hardware Quirk Version) The original Rev A NMOS 6502 didn't have proper ROR circuitry. Instead of rotating right, it behaves like ASL (Arithmetic Shift Left):
- Shifts left instead of right (like ASL)
- Shifts a zero in instead of C (like ASL)
- Doesn't update C (unlike ASL, the carry flag is not modified)
This quirk was fixed in Rev B and later revisions. Flags affected: Z, N (C is NOT affected, unlike normal ASL)
func (*CPU) RTI ¶
RTI - Return from Interrupt Pulls the processor status and program counter from the stack. This returns control from an interrupt handler. The B flag is cleared and U flag is set in the restored status. Flags affected: All (restored from stack)
func (*CPU) RTS ¶
RTS - Return from Subroutine Pulls the return address from the stack and increments it. This returns control to the instruction after the JSR. Flags affected: None
func (*CPU) RemainingCycles ¶
RemainingCycles returns the number of cycles remaining for the current instruction.
Each instruction takes multiple cycles to complete. This method returns how many cycles are left before the next instruction will be fetched.
Returns 0 if no instruction is currently executing (ready for next instruction).
Example:
for cpu.RemainingCycles() > 0 {
cpu.Clock()
}
func (*CPU) Reset ¶
func (c *CPU) Reset()
Reset initializes the CPU to its power-on state.
This method:
- Clears all registers (A, X, Y)
- Sets stack pointer to $FD
- Sets status flags to U | I
- Loads PC from reset vector at $FFFC/FD
- Takes 8 cycles to complete
The reset vector should be set in memory before calling Reset:
bus.Write(0xFFFC, 0x00) // Low byte bus.Write(0xFFFD, 0x80) // High byte -> PC = $8000 cpu.Reset()
func (*CPU) SED ¶
SED - Set Decimal Mode Flag Sets the Decimal mode flag to 1, enabling BCD arithmetic. Flags affected: D
func (*CPU) SEI ¶
SEI - Set Interrupt Disable Flag Sets the Interrupt disable flag to 1, disabling IRQ interrupts. Note: NMI interrupts cannot be disabled. Flags affected: I
func (*CPU) STA ¶
STA - Store Accumulator Stores the accumulator value to memory. Flags affected: None
func (*CPU) STX ¶
STX - Store X Register Stores the X register value to memory. Flags affected: None
func (*CPU) STY ¶
STY - Store Y Register Stores the Y register value to memory. Flags affected: None
func (*CPU) SetCycles ¶
SetCycles sets the number of cycles remaining.
This method is primarily for testing and debugging purposes. It allows manual control of the cycle counter, which can be useful for testing timing-sensitive code.
Parameters:
- cycles: Number of cycles to set
Warning: Modifying the cycle counter during normal execution can lead to incorrect timing and behavior.
func (*CPU) SetIRQ ¶
SetIRQ sets the IRQ line state.
IRQ is level-triggered: it will be serviced as long as the line is asserted and the I (Interrupt Disable) flag is clear. The interrupt is checked between instructions, not during instruction execution.
Parameters:
- asserted: true to assert IRQ, false to clear it
Example:
// Assert IRQ (e.g., from a timer) cpu.SetIRQ(true) // Later, clear IRQ after handling cpu.SetIRQ(false)
Note: The interrupt will only be serviced if:
- The I flag is clear (interrupts enabled)
- No instruction is currently executing (cycles == 0)
- Not already handling an interrupt
func (*CPU) SetNMI ¶
SetNMI sets the NMI line state.
NMI is edge-triggered: it will be serviced on a falling edge (high to low transition). Once triggered, the NMI is pending until serviced, even if the line goes high again.
Parameters:
- asserted: true to set line high, false to set line low
Example:
// Trigger NMI with falling edge cpu.SetNMI(true) // Set high cpu.SetNMI(false) // Set low - triggers NMI
Note: The interrupt will be serviced at the next instruction boundary, regardless of the I flag state.
func (*CPU) TAX ¶
TAX - Transfer Accumulator to X Copies the accumulator value to the X register. Flags affected: Z, N
func (*CPU) TAY ¶
TAY - Transfer Accumulator to Y Copies the accumulator value to the Y register. Flags affected: Z, N
func (*CPU) TSX ¶
TSX - Transfer Stack Pointer to X Copies the stack pointer value to the X register. Flags affected: Z, N
func (*CPU) TXA ¶
TXA - Transfer X to Accumulator Copies the X register value to the accumulator. Flags affected: Z, N
func (*CPU) TXS ¶
TXS - Transfer X to Stack Pointer Copies the X register value to the stack pointer. Flags affected: None
func (*CPU) TYA ¶
TYA - Transfer Y to Accumulator Copies the Y register value to the accumulator. Flags affected: Z, N
func (*CPU) TotalCycles ¶
TotalCycles returns the total number of cycles executed since CPU creation.
This counter increments with each clock cycle and is useful for:
- Performance profiling
- Timing synchronization with other components
- Debugging timing-sensitive issues
The counter wraps around at 2^64 cycles (effectively never in practice).
Example:
start := cpu.TotalCycles()
// ... execute some code ...
elapsed := cpu.TotalCycles() - start
fmt.Printf("Executed %d cycles\n", elapsed)
func (*CPU) XXX ¶
XXX - Illegal Opcode Handler Called when an illegal/unofficial opcode is encountered. Logs an error message and returns 1 to prevent infinite loops. The actual error handling behavior depends on the configured error handler. Flags affected: None
func (*CPU) ZP0 ¶
ZP0 - Zero Page addressing mode.
The operand is located in the zero page (addresses $0000-$00FF). Only one byte is needed to specify the address, making this mode faster and more compact than absolute addressing.
Example: LDA $42
- Opcode at PC
- Zero page address $42 at PC+1
- Loads value from address $0042
Returns 0 (no page cross possible - always in zero page).
func (*CPU) ZPX ¶
ZPX - Zero Page,X addressing mode.
Similar to ZP0, but the X register is added to the zero page address. The result wraps around within the zero page (no carry to high byte).
Example: LDA $42,X (with X=$10)
- Opcode at PC
- Base address $42 at PC+1
- Effective address: ($42 + $10) & $FF = $52
- Loads value from address $0052
Wrapping example: LDA $FF,X (with X=$02)
- Effective address: ($FF + $02) & $FF = $01
- Wraps to $0001, not $0101
Returns 0 (no page cross possible - wraps within zero page).
func (*CPU) ZPY ¶
ZPY - Zero Page,Y addressing mode.
Similar to ZPX, but uses the Y register instead of X. Only used by LDX and STX instructions.
Example: LDX $42,Y (with Y=$10)
- Effective address: ($42 + $10) & $FF = $52
- Loads value from address $0052 into X
Returns 0 (no page cross possible - wraps within zero page).
type CPUBuilder ¶
type CPUBuilder struct {
// contains filtered or unexported fields
}
CPUBuilder provides a fluent interface for CPU configuration.
The builder pattern allows for readable, chainable configuration:
cpu := cpu6502.NewBuilder(bus).
WithVariant(cpu6502.VariantCMOS65C02).
WithStrictMode().
DisableDecimalMode().
Build()
This is more convenient than manually creating a CPUConfig struct when you only need to change a few settings from the defaults.
func NewBuilder ¶
func NewBuilder(bus Bus) *CPUBuilder
NewBuilder creates a new CPU builder with default configuration.
Parameters:
- bus: The memory bus interface for the CPU
Returns a builder that can be configured using method chaining.
Example:
builder := cpu6502.NewBuilder(bus) cpu := builder.WithVariant(cpu6502.VariantCMOS65C02).Build()
func (*CPUBuilder) Build ¶
func (b *CPUBuilder) Build() *CPU
Build creates the configured CPU.
Returns a new CPU instance with the configured settings.
Example:
cpu := cpu6502.NewBuilder(bus).
WithVariant(cpu6502.VariantCMOS65C02).
WithStrictMode().
Build()
func (*CPUBuilder) DisableDecimalMode ¶
func (b *CPUBuilder) DisableDecimalMode() *CPUBuilder
DisableDecimalMode disables decimal mode.
This is useful for emulating systems that don't support decimal mode (like the Ricoh 2A03 in the NES) or for slight performance improvements.
Returns the builder for method chaining.
Example:
builder.DisableDecimalMode()
func (*CPUBuilder) DisableInstructionCache ¶
func (b *CPUBuilder) DisableInstructionCache() *CPUBuilder
DisableInstructionCache disables the instruction cache.
This should be used when emulating self-modifying code or when cache invalidation would be too complex to manage.
Returns the builder for method chaining.
Example:
builder.DisableInstructionCache()
func (*CPUBuilder) WithErrorHandler ¶
func (b *CPUBuilder) WithErrorHandler(handler ErrorHandler) *CPUBuilder
WithErrorHandler sets a custom error handler.
Parameters:
- handler: The error handler to use
Returns the builder for method chaining.
Example:
handler := &MyCustomErrorHandler{}
builder.WithErrorHandler(handler)
func (*CPUBuilder) WithInstructionCacheSize ¶
func (b *CPUBuilder) WithInstructionCacheSize(size int) *CPUBuilder
WithInstructionCacheSize sets the instruction cache size.
Note: Currently the cache size is fixed at 256 entries. This method is reserved for future use.
Parameters:
- size: The desired cache size
Returns the builder for method chaining.
func (*CPUBuilder) WithStrictMode ¶
func (b *CPUBuilder) WithStrictMode() *CPUBuilder
WithStrictMode enables strict mode.
In strict mode, the CPU halts execution when encountering illegal opcodes instead of continuing with default behavior.
Returns the builder for method chaining.
Example:
builder.WithStrictMode()
func (*CPUBuilder) WithVariant ¶
func (b *CPUBuilder) WithVariant(variant CPUVariant) *CPUBuilder
WithVariant sets the CPU variant.
Parameters:
- variant: The CPU variant to emulate
Returns the builder for method chaining.
Example:
builder.WithVariant(cpu6502.VariantCMOS65C02)
type CPUConfig ¶
type CPUConfig struct {
// Variant specifies the CPU variant (NMOS, CMOS, Ricoh)
Variant CPUVariant
// ErrorHandler defines how errors are handled
ErrorHandler ErrorHandler
// StrictMode halts execution on illegal opcodes
StrictMode bool
// EnableDecimalMode allows disabling decimal mode even on variants that support it
EnableDecimalMode bool
// EnableInstructionCache enables instruction caching for performance
EnableInstructionCache bool
// InstructionCacheSize sets the cache size (default: 256 entries)
InstructionCacheSize int
}
CPUConfig holds configuration options for CPU creation.
This structure provides fine-grained control over CPU behavior, including variant selection, error handling, and performance options.
Configuration Options ¶
Variant: Selects the CPU variant (NMOS 6502, CMOS 65C02, Ricoh 2A03)
- Affects instruction behavior and bug emulation
- Default: VariantNMOS6502
ErrorHandler: Defines how errors are handled during execution
- LoggingErrorHandler: Logs errors but continues execution
- StrictErrorHandler: Halts execution on any error
- Custom handlers can be implemented
StrictMode: Halts execution on illegal opcodes
- Overrides ErrorHandler for illegal opcode errors
- Useful for debugging or strict compatibility testing
EnableDecimalMode: Controls decimal mode support
- Some variants (Ricoh 2A03) don't support decimal mode
- Can be disabled for performance or compatibility
EnableInstructionCache: Enables instruction caching
- Improves performance by caching instruction lookups
- Should be disabled for self-modifying code
InstructionCacheSize: Sets cache size (currently fixed at 256)
- Reserved for future use
Example usage:
config := cpu6502.DefaultConfig() config.Variant = cpu6502.VariantCMOS65C02 config.StrictMode = true cpu := cpu6502.NewCPUWithConfig(bus, config)
func DefaultConfig ¶
func DefaultConfig() CPUConfig
DefaultConfig returns a configuration with sensible defaults.
Default configuration:
- Variant: NMOS 6502 (original)
- ErrorHandler: LoggingErrorHandler (logs to default logger)
- StrictMode: false (continues on errors)
- EnableDecimalMode: true
- EnableInstructionCache: true
- InstructionCacheSize: 256
This configuration is suitable for most emulation scenarios and provides a good balance between accuracy and performance.
type CPUVariant ¶
type CPUVariant int
CPUVariant represents different 6502 processor variants
const ( // VariantNMOS6502 is the original NMOS 6502 (1975) // Used in: Apple II, Commodore 64, Atari 2600/800, BBC Micro // Features: All documented bugs, decimal mode supported // Note: This represents Rev B and later which have working ROR VariantNMOS6502 CPUVariant = iota // VariantNMOS6502RevA is the original Rev A NMOS 6502 // This early revision had a hardware bug where ROR was not implemented // and performed a modified ROL operation instead // Features: ROR quirk, all other NMOS bugs, decimal mode supported VariantNMOS6502RevA // VariantCMOS65C02 is the CMOS 65C02 (1982) // Used in: Apple IIc, Apple IIe (enhanced), later systems // Features: Bug fixes, additional instructions, lower power VariantCMOS65C02 // VariantRicoh2A03 is the NES/Famicom CPU (1983) // Used in: Nintendo Entertainment System, Famicom // Features: No decimal mode, integrated APU, different timing VariantRicoh2A03 // VariantRicoh2A07 is the PAL NES CPU // Same as 2A03 but with PAL timing VariantRicoh2A07 )
func (CPUVariant) HasIndirectJMPBug ¶
func (v CPUVariant) HasIndirectJMPBug() bool
HasIndirectJMPBug returns true if the variant has the indirect JMP page boundary bug
func (CPUVariant) HasRORQuirk ¶ added in v0.0.4
func (v CPUVariant) HasRORQuirk() bool
HasRORQuirk returns true if the variant has the ROR hardware bug. Only the original Rev A NMOS 6502 had this quirk where ROR didn't have proper circuitry and performed a modified ROL operation instead. This was fixed in Rev B and was never present in Ricoh or CMOS variants.
func (CPUVariant) SupportsDecimalMode ¶
func (v CPUVariant) SupportsDecimalMode() bool
SupportsDecimalMode returns true if the variant supports decimal mode
type ErrorHandler ¶
ErrorHandler defines how the CPU handles errors
type Flags ¶
type Flags uint8
Flags represents the processor status register.
The 6502 has 8 status flags that indicate the result of operations:
- N (Negative): Set if result is negative (bit 7 = 1)
- V (Overflow): Set if signed overflow occurred
- U (Unused): Always set to 1
- B (Break): Set when BRK instruction executed
- D (Decimal): Enables BCD arithmetic mode
- I (Interrupt Disable): When set, IRQ interrupts are ignored
- Z (Zero): Set if result is zero
- C (Carry): Set if unsigned overflow/borrow occurred
const ( C Flags = 1 << 0 // Carry Bit Z Flags = 1 << 1 // Zero I Flags = 1 << 2 // Disable Interrupts D Flags = 1 << 3 // Decimal Mode (rarely used, often ignored in NES emu) B Flags = 1 << 4 // Break Command U Flags = 1 << 5 // Unused (always 1) V Flags = 1 << 6 // Overflow N Flags = 1 << 7 // Negative )
type Instruction ¶
type Instruction struct {
Name string // Mnemonic (e.g., "LDA")
Operate func(*CPU) uint8 // Function to execute the instruction's logic (accepts *CPU)
AddrMode func(*CPU) uint8 // Function to calculate the address and fetch data (accepts *CPU)
AddrModeType AddrModeType // Type of addressing mode
Cycles uint8 // Base cycles for this instruction/mode
Length uint8 // Length of the instruction in bytes
Illegal bool // Whether this is an official or unofficial/illegal opcode
PageCrossPenalty bool // Whether to add +1 cycle on page boundary cross
}
Instruction represents a single 6502 instruction with its associated metadata.
Each instruction consists of:
- Name: The mnemonic (e.g., "LDA", "STA")
- Operate: Function that executes the instruction's logic
- AddrMode: Function that calculates the effective address
- AddrModeType: Enum identifying the addressing mode
- Cycles: Base number of cycles the instruction takes
- Length: Size of the instruction in bytes (including opcode)
- Illegal: Whether this is an unofficial/illegal opcode
- PageCrossPenalty: Whether to add +1 cycle on page boundary cross
type InstructionCache ¶
type InstructionCache struct {
// contains filtered or unexported fields
}
InstructionCache provides fast lookup for recently executed instructions.
The cache improves performance by avoiding repeated lookups in the main instruction table. It uses a direct-mapped strategy with 256 entries, indexed by the low byte of the program counter.
Cache Strategy ¶
The cache uses PC & 0xFF as the index, which provides good locality for:
- Tight loops (instructions repeat at similar addresses)
- Sequential code (nearby instructions share cache lines)
- Subroutines (local code patterns)
Performance Characteristics ¶
Typical hit rates:
- Tight loops: 95-99% (excellent)
- Sequential code: 60-80% (good)
- Random jumps: 20-40% (poor, but rare)
Cache Invalidation ¶
The cache should be invalidated when:
- Self-modifying code is detected
- New program is loaded into memory
- Memory is modified in executable regions
Example usage:
cache := NewInstructionCache()
if instr, hit := cache.Lookup(pc, opcode); hit {
// Use cached instruction
} else {
// Look up in main table and cache result
instr := &lookupTable[opcode]
cache.Store(pc, opcode, instr)
}
func NewInstructionCache ¶
func NewInstructionCache() *InstructionCache
NewInstructionCache creates a new instruction cache.
The cache is initially empty (all entries invalid) and statistics are zeroed. The cache is ready to use immediately.
func (*InstructionCache) Invalidate ¶
func (ic *InstructionCache) Invalidate()
Invalidate clears the entire cache.
This marks all cache entries as invalid and resets statistics to zero. Call this after self-modifying code execution or when loading a new program into memory.
Example:
// After writing to executable memory cpu.InvalidateInstructionCache()
func (*InstructionCache) Lookup ¶
func (ic *InstructionCache) Lookup(pc uint16, opcode uint8) (*Instruction, bool)
Lookup attempts to find an instruction in the cache.
Returns the cached instruction and true if found, or nil and false if not found or the cache entry is invalid. Updates hit/miss statistics.
The lookup uses the low byte of PC as the cache index and verifies that the cached opcode matches the requested opcode.
Parameters:
- pc: Program counter value (low byte used as cache index)
- opcode: The opcode to look up
Returns:
- instruction: Pointer to cached instruction (nil if not found)
- hit: true if instruction was found in cache
func (*InstructionCache) Stats ¶
func (ic *InstructionCache) Stats() (hits, misses uint64, hitRate float64)
Stats returns cache performance statistics.
Returns:
- hits: Number of successful cache lookups
- misses: Number of failed cache lookups
- hitRate: Percentage of successful lookups (0.0 to 1.0)
The hit rate is calculated as hits / (hits + misses). Returns 0.0 if no lookups have been performed yet.
Example:
hits, misses, rate := cache.Stats()
fmt.Printf("Cache: %d hits, %d misses, %.1f%% hit rate\n",
hits, misses, rate*100)
func (*InstructionCache) Store ¶
func (ic *InstructionCache) Store(pc uint16, opcode uint8, instruction *Instruction)
Store adds an instruction to the cache.
Stores the instruction at the cache index determined by the low byte of the PC. Any existing entry at that index is replaced.
Parameters:
- pc: Program counter value (low byte used as cache index)
- opcode: The opcode being cached
- instruction: Pointer to the instruction definition
type InstructionCacheEntry ¶
type InstructionCacheEntry struct {
// contains filtered or unexported fields
}
InstructionCacheEntry represents a cached instruction lookup result.
The cache uses a direct-mapped strategy where the low byte of the PC is used as the cache index. Each entry stores the opcode and instruction pointer, along with a validity flag.
type LoggingErrorHandler ¶
LoggingErrorHandler logs errors but continues execution
func (*LoggingErrorHandler) HandleError ¶
func (h *LoggingErrorHandler) HandleError(err *CPUError) error
HandleError logs the error and continues execution
type State ¶
type State struct {
A uint8 // Accumulator
X uint8 // X Index Register
Y uint8 // Y Index Register
SP uint8 // Stack Pointer
PC uint16 // Program Counter
P Flags // Processor Status Register
Cycles uint8 // Cycles remaining for current instruction
TotalCycles uint64 // Total cycles executed since creation
Opcode uint8 // Current opcode being executed
Instruction string // Current instruction mnemonic
InInterrupt bool // Whether currently handling an interrupt
InterruptVector uint16 // Vector being used for current interrupt
}
State represents a snapshot of CPU state at a point in time.
This structure is useful for:
- Debugging and inspection
- Save states in emulators
- Testing and verification
- Logging execution traces
type StrictErrorHandler ¶
type StrictErrorHandler struct{}
StrictErrorHandler halts execution on any error
func (*StrictErrorHandler) HandleError ¶
func (h *StrictErrorHandler) HandleError(err *CPUError) error
HandleError returns the error, halting execution
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
Package main demonstrates basic CPU usage
|
Package main demonstrates basic CPU usage |
|
memory-mapped
command
Package main demonstrates memory-mapped I/O
|
Package main demonstrates memory-mapped I/O |