pub struct Cpu {
pub a: u8,
pub x: u8,
pub y: u8,
pub pc: u16,
pub sp: u8,
pub status: StatusFlags,
pub cycles: u64,
pub stall: u8,
pub jammed: bool,
/* private fields */
}Expand description
NES 6502 CPU
Cycle-accurate implementation of the MOS 6502 as used in the NES. All timing follows the NESdev Wiki specifications.
Fields§
§a: u8Accumulator register
x: u8X index register
y: u8Y index register
pc: u16Program counter
sp: u8Stack pointer (points to $0100-$01FF)
status: StatusFlagsStatus flags
cycles: u64Total cycles executed
stall: u8Stall cycles (for DMA)
jammed: boolCPU jammed (halt opcodes)
Implementations§
Source§impl Cpu
impl Cpu
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new CPU in power-on state.
§Power-on State
- A, X, Y: undefined (set to 0)
- SP: $FD (after RESET pulls 3 bytes)
- P: $34 (IRQ disabled)
- PC: Read from RESET vector $FFFC-$FFFD
Sourcepub fn reset(&mut self, bus: &mut impl Bus)
pub fn reset(&mut self, bus: &mut impl Bus)
Reset the CPU.
Simulates the RESET interrupt sequence:
- SP decremented by 3 (no writes)
- I flag set
- PC loaded from RESET vector ($FFFC-$FFFD)
- Takes 7 cycles
Sourcepub fn step(&mut self, bus: &mut impl Bus) -> u8
pub fn step(&mut self, bus: &mut impl Bus) -> u8
Execute one instruction and return cycles taken.
Handles interrupt polling and instruction execution. Returns the number of CPU cycles consumed.
Sourcepub fn trigger_nmi(&mut self)
pub fn trigger_nmi(&mut self)
Trigger NMI (Non-Maskable Interrupt).
NMI is edge-triggered - call this when NMI line transitions from high to low.
Sourcepub fn set_irq(&mut self, active: bool)
pub fn set_irq(&mut self, active: bool)
Set IRQ line state.
IRQ is level-triggered - will fire every instruction while line is low and I=0.
Sourcepub fn irq_pending(&self) -> bool
pub fn irq_pending(&self) -> bool
Check if IRQ is pending.
Sourcepub fn get_cycles(&self) -> u64
pub fn get_cycles(&self) -> u64
Get total cycles executed.
Sourcepub fn tick(&mut self, bus: &mut impl CpuBus) -> bool
pub fn tick(&mut self, bus: &mut impl CpuBus) -> bool
Execute exactly one CPU cycle with cycle-accurate bus synchronization.
This is the core of cycle-accurate emulation. Each call advances the CPU
by exactly one cycle, calling on_cpu_cycle() before each memory access
to keep PPU and APU perfectly synchronized.
Returns true when an instruction boundary is reached (ready for next instruction).
§Cycle-Accurate Timing
Each memory access calls on_cpu_cycle() BEFORE reading/writing, ensuring
that PPU has advanced 3 dots and APU has advanced 1 cycle before the CPU
observes the memory state. This is critical for accurate $2002 VBlank
flag timing.
Sourcepub fn read_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u8
pub fn read_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u8
Read a byte from memory with cycle callback.
Calls on_cpu_cycle() BEFORE the read, then performs the actual read.
This is the cycle-accurate version of memory read.
§Arguments
bus- The cycle-aware bus implementingCpuBusaddr- 16-bit memory address to read from
§Returns
The 8-bit value at the specified address
§Timing
CPU Cycle: |-------- read --------|
PPU Cycles: |--1--|--2--|--3--|
^ on_cpu_cycle() called hereSourcepub fn write_cycle(&mut self, bus: &mut impl CpuBus, addr: u16, value: u8)
pub fn write_cycle(&mut self, bus: &mut impl CpuBus, addr: u16, value: u8)
Write a byte to memory with cycle callback.
Calls on_cpu_cycle() BEFORE the write, then performs the actual write.
This is the cycle-accurate version of memory write.
§Arguments
bus- The cycle-aware bus implementingCpuBusaddr- 16-bit memory address to write tovalue- 8-bit value to write
§Timing
CPU Cycle: |------- write --------|
PPU Cycles: |--1--|--2--|--3--|
^ on_cpu_cycle() called hereSourcepub fn dummy_cycle(&mut self, bus: &mut impl CpuBus, addr: u16)
pub fn dummy_cycle(&mut self, bus: &mut impl CpuBus, addr: u16)
Perform a dummy read cycle (for timing purposes).
Some instructions require timing cycles where a read is performed
but the value is discarded. This method handles those cases while
still calling on_cpu_cycle() for proper PPU/APU synchronization.
§Use Cases
- Page boundary crossing in indexed addressing
- Implied/Accumulator mode dummy reads
- Branch taken cycles
- RMW dummy write-back cycles
§Arguments
bus- The cycle-aware bus implementingCpuBusaddr- Address to read from (value is discarded)
Sourcepub fn push_cycle(&mut self, bus: &mut impl CpuBus, value: u8)
pub fn push_cycle(&mut self, bus: &mut impl CpuBus, value: u8)
Push a byte to the stack with cycle callback.
Calls on_cpu_cycle() BEFORE the write, then pushes to stack.
§Arguments
bus- The cycle-aware bus implementingCpuBusvalue- 8-bit value to push
Sourcepub fn read_u16_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u16
pub fn read_u16_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u16
Sourcepub fn read_u16_wrap_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u16
pub fn read_u16_wrap_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u16
Read a 16-bit value with page wrap and cycle callbacks.
Implements the JMP indirect bug where the high byte wraps within the same page if the low byte is at $xxFF.
§Arguments
bus- The cycle-aware bus implementingCpuBusaddr- Address of the low byte
§Returns
16-bit value with page-wrap bug behavior
Sourcepub fn push_u16_cycle(&mut self, bus: &mut impl CpuBus, value: u16)
pub fn push_u16_cycle(&mut self, bus: &mut impl CpuBus, value: u16)
Push a 16-bit value to the stack with cycle callbacks.
Pushes high byte first, then low byte (6502 convention).
Each push triggers on_cpu_cycle().
§Arguments
bus- The cycle-aware bus implementingCpuBusvalue- 16-bit value to push