#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CpuError { UnsupportedOpcode { opcode: u8, pc: u16 }, } pub trait CpuBus { fn read(&mut self, addr: u16) -> u8; fn write(&mut self, addr: u16, value: u8); fn poll_nmi(&mut self) -> bool { false } fn poll_irq(&mut self) -> bool { false } } const FLAG_CARRY: u8 = 0b0000_0001; const FLAG_ZERO: u8 = 0b0000_0010; const FLAG_IRQ_DISABLE: u8 = 0b0000_0100; const FLAG_DECIMAL: u8 = 0b0000_1000; const FLAG_BREAK: u8 = 0b0001_0000; const FLAG_UNUSED: u8 = 0b0010_0000; const FLAG_OVERFLOW: u8 = 0b0100_0000; const FLAG_NEGATIVE: u8 = 0b1000_0000; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Cpu6502 { pub a: u8, pub x: u8, pub y: u8, pub sp: u8, pub pc: u16, pub p: u8, pub halted: bool, pub irq_delay: bool, pub pending_nmi: bool, pub pending_irq: bool, } impl Default for Cpu6502 { fn default() -> Self { Self { a: 0, x: 0, y: 0, sp: 0xFD, pc: 0, p: FLAG_IRQ_DISABLE | FLAG_UNUSED, halted: false, irq_delay: false, pending_nmi: false, pending_irq: false, } } } impl Cpu6502 { pub fn reset(&mut self, bus: &mut B) { let lo = bus.read(0xFFFC) as u16; let hi = bus.read(0xFFFD) as u16; self.pc = (hi << 8) | lo; self.sp = self.sp.wrapping_sub(3); self.p = (self.p | FLAG_IRQ_DISABLE | FLAG_UNUSED) & !FLAG_BREAK; self.halted = false; self.irq_delay = false; self.pending_nmi = false; self.pending_irq = false; } pub fn step(&mut self, bus: &mut B) -> Result { if self.halted { return Ok(2); } if self.pending_nmi { self.pending_nmi = false; self.pending_irq = false; self.service_interrupt(bus, 0xFFFA, false); self.p |= FLAG_UNUSED; return Ok(7); } let irq_delayed = self.irq_delay; self.irq_delay = false; if self.pending_irq && !irq_delayed && (self.p & FLAG_IRQ_DISABLE) == 0 { self.pending_irq = false; self.service_interrupt(bus, 0xFFFE, false); self.p |= FLAG_UNUSED; return Ok(7); } let pc_before = self.pc; let opcode = self.fetch(bus); let cycles = self.execute_opcode(bus, opcode, pc_before)?; // Real 6502 polls IRQ/NMI near the end of the current instruction and // services it before the next opcode fetch. if bus.poll_nmi() { self.pending_nmi = true; self.pending_irq = false; } else if !irq_delayed && (self.p & FLAG_IRQ_DISABLE) == 0 && bus.poll_irq() { self.pending_irq = true; } Ok(cycles) } } // Addressing modes, ALU helpers, stack and interrupt internals. mod helpers; // Opcode decoder/executor table split out from cpu.rs for readability. mod opcodes; #[cfg(test)] mod tests;