rustynes_cpu/
cpu.rs

1//! 6502 CPU core implementation.
2//!
3//! This module contains the main CPU structure with all registers,
4//! the instruction execution loop, interrupt handling, and stack operations.
5
6use crate::addressing::AddressingMode;
7use crate::bus::{Bus, CpuBus};
8use crate::opcodes::OPCODE_TABLE;
9use crate::state::{CpuState, InstructionType};
10use crate::status::StatusFlags;
11
12/// NES 6502 CPU
13///
14/// Cycle-accurate implementation of the MOS 6502 as used in the NES.
15/// All timing follows the NESdev Wiki specifications.
16#[derive(Debug)]
17#[allow(clippy::struct_excessive_bools)] // Bools are appropriate for CPU flags
18pub struct Cpu {
19    /// Accumulator register
20    pub a: u8,
21    /// X index register
22    pub x: u8,
23    /// Y index register
24    pub y: u8,
25    /// Program counter
26    pub pc: u16,
27    /// Stack pointer (points to $0100-$01FF)
28    pub sp: u8,
29    /// Status flags
30    pub status: StatusFlags,
31    /// Total cycles executed
32    pub cycles: u64,
33    /// Stall cycles (for DMA)
34    pub stall: u8,
35    /// NMI pending flag
36    pub(crate) nmi_pending: bool,
37    /// IRQ line state
38    pub(crate) irq_pending: bool,
39    /// I flag value sampled at start of instruction (for interrupt polling)
40    /// IRQ check uses this instead of current I flag to implement proper timing
41    pub(crate) prev_irq_inhibit: bool,
42    /// Suppress NMI check for one instruction (set after BRK completes)
43    /// This ensures the first instruction of the interrupt handler executes
44    /// before checking for another NMI (required for nmi_and_brk test)
45    pub(crate) suppress_nmi_next: bool,
46    /// CPU jammed (halt opcodes)
47    pub jammed: bool,
48
49    // ===== Cycle-by-cycle state machine fields =====
50    /// Current execution state in the state machine
51    state: CpuState,
52    /// Current opcode being executed
53    current_opcode: u8,
54    /// Current instruction type (for dispatch)
55    instr_type: InstructionType,
56    /// Current addressing mode
57    current_addr_mode: AddressingMode,
58    /// Low byte of operand (fetched during FetchOperandLo)
59    operand_lo: u8,
60    /// High byte of operand (fetched during FetchOperandHi)
61    operand_hi: u8,
62    /// Calculated effective address
63    effective_addr: u16,
64    /// Base address before indexing (for page cross detection)
65    base_addr: u16,
66    /// Temporary value for RMW operations
67    temp_value: u8,
68    /// Branch offset (signed, for branch instructions)
69    branch_offset: i8,
70    /// Indicates if current instruction crosses a page boundary
71    page_crossed: bool,
72}
73
74impl Cpu {
75    /// Create a new CPU in power-on state.
76    ///
77    /// # Power-on State
78    /// - A, X, Y: undefined (set to 0)
79    /// - SP: $FD (after RESET pulls 3 bytes)
80    /// - P: $34 (IRQ disabled)
81    /// - PC: Read from RESET vector $FFFC-$FFFD
82    pub fn new() -> Self {
83        Self {
84            a: 0,
85            x: 0,
86            y: 0,
87            pc: 0,
88            sp: 0xFD,
89            status: StatusFlags::from_bits_truncate(0x24), // I flag set, U flag set
90            cycles: 0,
91            stall: 0,
92            nmi_pending: false,
93            irq_pending: false,
94            prev_irq_inhibit: true,
95            suppress_nmi_next: false,
96            jammed: false,
97            // Cycle-by-cycle state machine fields
98            state: CpuState::default(),
99            current_opcode: 0,
100            instr_type: InstructionType::default(),
101            current_addr_mode: AddressingMode::Implied,
102            operand_lo: 0,
103            operand_hi: 0,
104            effective_addr: 0,
105            base_addr: 0,
106            temp_value: 0,
107            branch_offset: 0,
108            page_crossed: false,
109        }
110    }
111
112    /// Reset the CPU.
113    ///
114    /// Simulates the RESET interrupt sequence:
115    /// - SP decremented by 3 (no writes)
116    /// - I flag set
117    /// - PC loaded from RESET vector ($FFFC-$FFFD)
118    /// - Takes 7 cycles
119    pub fn reset(&mut self, bus: &mut impl Bus) {
120        self.sp = self.sp.wrapping_sub(3);
121        self.status.insert(StatusFlags::INTERRUPT_DISABLE);
122        self.pc = bus.read_u16(0xFFFC);
123        self.cycles += 7;
124        self.nmi_pending = false;
125        self.irq_pending = false;
126        self.prev_irq_inhibit = true;
127        self.jammed = false;
128        // Reset state machine to ready for next instruction
129        self.state = CpuState::FetchOpcode;
130        self.current_opcode = 0;
131        self.instr_type = InstructionType::default();
132        self.current_addr_mode = AddressingMode::Implied;
133        self.operand_lo = 0;
134        self.operand_hi = 0;
135        self.effective_addr = 0;
136        self.base_addr = 0;
137        self.temp_value = 0;
138        self.branch_offset = 0;
139        self.page_crossed = false;
140    }
141
142    /// Execute one instruction and return cycles taken.
143    ///
144    /// Handles interrupt polling and instruction execution.
145    /// Returns the number of CPU cycles consumed.
146    #[inline]
147    pub fn step(&mut self, bus: &mut impl Bus) -> u8 {
148        // Handle DMA stalls
149        if self.stall > 0 {
150            self.stall -= 1;
151            self.cycles += 1;
152            return 1;
153        }
154
155        // Check if CPU is jammed
156        if self.jammed {
157            self.cycles += 1;
158            return 1;
159        }
160
161        // Sample I flag at start of this instruction (for next instruction's IRQ check)
162        let current_irq_inhibit = self.status.contains(StatusFlags::INTERRUPT_DISABLE);
163
164        // Check for NMI (Non-Maskable Interrupt) - Edge triggered
165        // NMI can be suppressed for one instruction after BRK completes
166        if self.nmi_pending && !self.suppress_nmi_next {
167            self.nmi_pending = false;
168            // NMI sets I flag, so we must treat previous as inhibited to prevent immediate IRQ
169            self.prev_irq_inhibit = true;
170            return self.handle_nmi(bus);
171        }
172
173        // Clear NMI suppression flag (applies for one instruction only)
174        if self.suppress_nmi_next {
175            self.suppress_nmi_next = false;
176        }
177
178        // Check for IRQ (Maskable Interrupt) - Level triggered
179        // IRQ is ignored if I flag is set (Interrupt Disable).
180        // The check uses `prev_irq_inhibit` to model the 1-instruction latency
181        // of instructions that change the I flag (CLI, SEI, PLP, RTI).
182        if self.irq_pending && !self.prev_irq_inhibit {
183            // Entering ISR sets I flag, so we must treat previous as inhibited
184            self.prev_irq_inhibit = true;
185            return self.handle_irq(bus);
186        }
187
188        // Update prev_irq_inhibit for next instruction
189        self.prev_irq_inhibit = current_irq_inhibit;
190
191        // Fetch opcode
192        let opcode = bus.read(self.pc);
193        self.pc = self.pc.wrapping_add(1);
194
195        // Look up opcode info
196        let info = &OPCODE_TABLE[opcode as usize];
197
198        // Execute instruction
199        let extra_cycles = self.execute_opcode(opcode, info.addr_mode, bus);
200
201        // Calculate total cycles
202        let total_cycles = info.cycles + extra_cycles;
203        self.cycles += u64::from(total_cycles);
204
205        total_cycles
206    }
207
208    /// Trigger NMI (Non-Maskable Interrupt).
209    ///
210    /// NMI is edge-triggered - call this when NMI line transitions from high to low.
211    pub fn trigger_nmi(&mut self) {
212        self.nmi_pending = true;
213    }
214
215    /// Set IRQ line state.
216    ///
217    /// IRQ is level-triggered - will fire every instruction while line is low and I=0.
218    pub fn set_irq(&mut self, active: bool) {
219        self.irq_pending = active;
220    }
221
222    /// Check if IRQ is pending.
223    #[must_use]
224    pub fn irq_pending(&self) -> bool {
225        self.irq_pending
226    }
227
228    /// Get total cycles executed.
229    pub fn get_cycles(&self) -> u64 {
230        self.cycles
231    }
232
233    /// Check if CPU is jammed (halted).
234    pub fn is_jammed(&self) -> bool {
235        self.jammed
236    }
237
238    /// Get current CPU state (for debugging/testing).
239    pub fn get_state(&self) -> CpuState {
240        self.state
241    }
242
243    // =========================================================================
244    // CYCLE-ACCURATE EXECUTION
245    // =========================================================================
246
247    /// Execute exactly one CPU cycle with cycle-accurate bus synchronization.
248    ///
249    /// This is the core of cycle-accurate emulation. Each call advances the CPU
250    /// by exactly one cycle, calling `on_cpu_cycle()` before each memory access
251    /// to keep PPU and APU perfectly synchronized.
252    ///
253    /// Returns `true` when an instruction boundary is reached (ready for next instruction).
254    ///
255    /// # Cycle-Accurate Timing
256    ///
257    /// Each memory access calls `on_cpu_cycle()` BEFORE reading/writing, ensuring
258    /// that PPU has advanced 3 dots and APU has advanced 1 cycle before the CPU
259    /// observes the memory state. This is critical for accurate $2002 VBlank
260    /// flag timing.
261    pub fn tick(&mut self, bus: &mut impl CpuBus) -> bool {
262        // Handle DMA stalls (OAM DMA, DMC DMA)
263        if self.stall > 0 {
264            self.stall -= 1;
265            self.cycles += 1;
266            return false;
267        }
268
269        // Handle jammed CPU
270        if self.jammed {
271            self.cycles += 1;
272            return false;
273        }
274
275        self.cycles += 1;
276
277        // Dispatch based on current state
278        match self.state {
279            CpuState::FetchOpcode => self.tick_fetch_opcode(bus),
280            CpuState::FetchOperandLo => self.tick_fetch_operand_lo(bus),
281            CpuState::FetchOperandHi => self.tick_fetch_operand_hi(bus),
282            CpuState::ResolveAddress => self.tick_resolve_address(bus),
283            CpuState::ReadData => self.tick_read_data(bus),
284            CpuState::WriteData => self.tick_write_data(bus),
285            CpuState::RmwRead => self.tick_rmw_read(bus),
286            CpuState::RmwDummyWrite => self.tick_rmw_dummy_write(bus),
287            CpuState::RmwWrite => self.tick_rmw_write(bus),
288            CpuState::Execute => self.tick_execute(bus),
289            CpuState::FetchIndirectLo => self.tick_fetch_indirect_lo(bus),
290            CpuState::FetchIndirectHi => self.tick_fetch_indirect_hi(bus),
291            CpuState::AddIndex => self.tick_add_index(bus),
292            CpuState::PushHi => self.tick_push_hi(bus),
293            CpuState::PushLo => self.tick_push_lo(bus),
294            CpuState::PushStatus => self.tick_push_status(bus),
295            CpuState::PopLo => self.tick_pop_lo(bus),
296            CpuState::PopHi => self.tick_pop_hi(bus),
297            CpuState::PopStatus => self.tick_pop_status(bus),
298            CpuState::InternalCycle => self.tick_internal_cycle(bus),
299            CpuState::BranchTaken => self.tick_branch_taken(bus),
300            CpuState::BranchPageCross => self.tick_branch_page_cross(bus),
301            CpuState::InterruptPushPcHi => self.tick_interrupt_push_pc_hi(bus),
302            CpuState::InterruptPushPcLo => self.tick_interrupt_push_pc_lo(bus),
303            CpuState::InterruptPushStatus => self.tick_interrupt_push_status(bus),
304            CpuState::InterruptFetchVectorLo => self.tick_interrupt_fetch_vector_lo(bus),
305            CpuState::InterruptFetchVectorHi => self.tick_interrupt_fetch_vector_hi(bus),
306        }
307    }
308
309    /// Fetch opcode cycle (cycle 1 of every instruction).
310    fn tick_fetch_opcode(&mut self, bus: &mut impl CpuBus) -> bool {
311        // Sample I flag at start of this instruction (will be used for NEXT instruction's IRQ check)
312        let current_irq_inhibit = self.status.contains(StatusFlags::INTERRUPT_DISABLE);
313
314        // Check for pending interrupts (polled on last cycle of previous instruction)
315        // NMI is not affected by I flag, but can be suppressed for one instruction after BRK
316        if self.nmi_pending && !self.suppress_nmi_next {
317            self.nmi_pending = false;
318            self.prev_irq_inhibit = current_irq_inhibit;
319            // Start interrupt sequence - dummy read of current PC
320            self.dummy_cycle(bus, self.pc);
321            self.state = CpuState::InterruptPushPcHi;
322            // Store NMI vector address for later
323            self.effective_addr = 0xFFFA;
324            return false;
325        }
326
327        // Clear NMI suppression flag (applies for one instruction only)
328        if self.suppress_nmi_next {
329            self.suppress_nmi_next = false;
330        }
331
332        // IRQ uses the I flag from the PREVIOUS instruction (prev_irq_inhibit)
333        // This implements the one-instruction delay after CLI/PLP/RTI
334        if self.irq_pending && !self.prev_irq_inhibit {
335            self.prev_irq_inhibit = current_irq_inhibit;
336            // Start interrupt sequence - dummy read of current PC
337            self.dummy_cycle(bus, self.pc);
338            self.state = CpuState::InterruptPushPcHi;
339            // Store IRQ vector address for later
340            self.effective_addr = 0xFFFE;
341            return false;
342        }
343
344        // Update prev_irq_inhibit for next instruction
345        self.prev_irq_inhibit = current_irq_inhibit;
346
347        // Fetch opcode from PC
348        self.current_opcode = self.read_cycle(bus, self.pc);
349        self.pc = self.pc.wrapping_add(1);
350
351        // Look up opcode info
352        let info = &OPCODE_TABLE[self.current_opcode as usize];
353        self.current_addr_mode = info.addr_mode;
354        self.instr_type = InstructionType::from_opcode(self.current_opcode);
355
356        // Reset state for new instruction
357        self.operand_lo = 0;
358        self.operand_hi = 0;
359        self.effective_addr = 0;
360        self.base_addr = 0;
361        self.temp_value = 0;
362        self.branch_offset = 0;
363        self.page_crossed = false;
364
365        // Determine next state based on addressing mode and instruction type
366        self.state = self.next_state_after_fetch();
367
368        // Check if this is a 2-cycle implied/accumulator instruction
369        matches!(
370            self.instr_type,
371            InstructionType::Implied | InstructionType::Accumulator
372        ) && self.state == CpuState::Execute
373    }
374
375    /// Determine next state after opcode fetch based on addressing mode.
376    fn next_state_after_fetch(&self) -> CpuState {
377        match self.current_addr_mode {
378            // Implied and Accumulator: just need Execute cycle
379            AddressingMode::Implied | AddressingMode::Accumulator => CpuState::Execute,
380
381            // Immediate: fetch single byte operand
382            AddressingMode::Immediate => CpuState::FetchOperandLo,
383
384            // Zero Page: fetch single byte address
385            AddressingMode::ZeroPage | AddressingMode::ZeroPageX | AddressingMode::ZeroPageY => {
386                CpuState::FetchOperandLo
387            }
388
389            // Absolute: fetch two byte address
390            AddressingMode::Absolute | AddressingMode::AbsoluteX | AddressingMode::AbsoluteY => {
391                CpuState::FetchOperandLo
392            }
393
394            // Indirect (JMP only): fetch two byte pointer
395            AddressingMode::Indirect => CpuState::FetchOperandLo,
396
397            // Indexed Indirect (X): fetch zero page base
398            AddressingMode::IndexedIndirectX => CpuState::FetchOperandLo,
399
400            // Indirect Indexed (Y): fetch zero page pointer
401            AddressingMode::IndirectIndexedY => CpuState::FetchOperandLo,
402
403            // Relative (branches): fetch offset
404            AddressingMode::Relative => CpuState::FetchOperandLo,
405        }
406    }
407
408    // =========================================================================
409    // STATE HANDLERS - These will be implemented progressively
410    // =========================================================================
411
412    fn tick_fetch_operand_lo(&mut self, bus: &mut impl CpuBus) -> bool {
413        self.operand_lo = self.read_cycle(bus, self.pc);
414        self.pc = self.pc.wrapping_add(1);
415
416        match self.current_addr_mode {
417            // Immediate mode: operand is the value itself
418            AddressingMode::Immediate => {
419                self.effective_addr = self.pc.wrapping_sub(1);
420                self.temp_value = self.operand_lo;
421                // For Read instructions with Immediate mode, execute now and complete (2 cycles total)
422                // The operand byte IS the value, no additional read needed
423                if matches!(self.instr_type, InstructionType::Read) {
424                    self.execute_read_instruction();
425                    self.state = CpuState::FetchOpcode;
426                    return true;
427                }
428                self.state = self.next_state_for_instruction_type();
429            }
430
431            // Zero Page modes
432            AddressingMode::ZeroPage => {
433                self.effective_addr = u16::from(self.operand_lo);
434                self.state = self.next_state_for_instruction_type();
435            }
436            AddressingMode::ZeroPageX => {
437                self.base_addr = u16::from(self.operand_lo);
438                self.state = CpuState::AddIndex;
439            }
440            AddressingMode::ZeroPageY => {
441                self.base_addr = u16::from(self.operand_lo);
442                self.state = CpuState::AddIndex;
443            }
444
445            // Absolute modes: need high byte
446            AddressingMode::Absolute
447            | AddressingMode::AbsoluteX
448            | AddressingMode::AbsoluteY
449            | AddressingMode::Indirect => {
450                self.state = CpuState::FetchOperandHi;
451            }
452
453            // Indexed Indirect (X): fetch from zero page
454            AddressingMode::IndexedIndirectX => {
455                self.base_addr = u16::from(self.operand_lo);
456                self.state = CpuState::AddIndex;
457            }
458
459            // Indirect Indexed (Y): fetch low byte of pointer
460            AddressingMode::IndirectIndexedY => {
461                self.base_addr = u16::from(self.operand_lo);
462                self.state = CpuState::FetchIndirectLo;
463            }
464
465            // Relative (branches): operand is signed offset
466            AddressingMode::Relative => {
467                self.branch_offset = self.operand_lo as i8;
468                // Check branch condition
469                if self.check_branch_condition() {
470                    self.state = CpuState::BranchTaken;
471                } else {
472                    // Branch not taken - instruction complete
473                    self.state = CpuState::FetchOpcode;
474                    return true;
475                }
476            }
477
478            _ => {
479                self.state = CpuState::FetchOpcode;
480            }
481        }
482        false
483    }
484
485    fn tick_fetch_operand_hi(&mut self, bus: &mut impl CpuBus) -> bool {
486        self.operand_hi = self.read_cycle(bus, self.pc);
487        self.pc = self.pc.wrapping_add(1);
488
489        let addr = u16::from_le_bytes([self.operand_lo, self.operand_hi]);
490
491        match self.current_addr_mode {
492            AddressingMode::Absolute => {
493                self.effective_addr = addr;
494                match self.instr_type {
495                    InstructionType::JumpAbsolute => {
496                        // JMP absolute: set PC and done
497                        self.pc = self.effective_addr;
498                        self.state = CpuState::FetchOpcode;
499                        return true;
500                    }
501                    InstructionType::JumpSubroutine => {
502                        // JSR: internal cycle, then push return address
503                        self.state = CpuState::InternalCycle;
504                    }
505                    _ => {
506                        self.state = self.next_state_for_instruction_type();
507                    }
508                }
509            }
510            AddressingMode::AbsoluteX => {
511                self.base_addr = addr;
512                let indexed = addr.wrapping_add(u16::from(self.x));
513                self.effective_addr = indexed;
514                self.page_crossed = (addr & 0xFF00) != (indexed & 0xFF00);
515
516                // For writes and RMW: always do dummy read (ResolveAddress)
517                // For reads: only if page crossed
518                match self.instr_type {
519                    InstructionType::Write | InstructionType::ReadModifyWrite => {
520                        self.state = CpuState::ResolveAddress;
521                    }
522                    _ => {
523                        if self.page_crossed {
524                            self.state = CpuState::ResolveAddress;
525                        } else {
526                            self.state = self.next_state_for_instruction_type();
527                        }
528                    }
529                }
530            }
531            AddressingMode::AbsoluteY => {
532                self.base_addr = addr;
533                let indexed = addr.wrapping_add(u16::from(self.y));
534                self.effective_addr = indexed;
535                self.page_crossed = (addr & 0xFF00) != (indexed & 0xFF00);
536
537                match self.instr_type {
538                    InstructionType::Write | InstructionType::ReadModifyWrite => {
539                        self.state = CpuState::ResolveAddress;
540                    }
541                    _ => {
542                        if self.page_crossed {
543                            self.state = CpuState::ResolveAddress;
544                        } else {
545                            self.state = self.next_state_for_instruction_type();
546                        }
547                    }
548                }
549            }
550            AddressingMode::Indirect => {
551                // JMP indirect: fetch low byte of target address
552                self.base_addr = addr;
553                self.state = CpuState::FetchIndirectLo;
554            }
555            _ => {
556                self.state = CpuState::FetchOpcode;
557            }
558        }
559        false
560    }
561
562    fn tick_resolve_address(&mut self, bus: &mut impl CpuBus) -> bool {
563        // Dummy read from incorrect address (before page fix)
564        // This is the hardware behavior for indexed addressing
565        let incorrect_addr = (self.base_addr & 0xFF00) | (self.effective_addr & 0x00FF);
566        self.dummy_cycle(bus, incorrect_addr);
567
568        self.state = self.next_state_for_instruction_type();
569        false
570    }
571
572    fn tick_read_data(&mut self, bus: &mut impl CpuBus) -> bool {
573        // Read the data from memory - this is the final cycle for Read instructions
574        self.temp_value = self.read_cycle(bus, self.effective_addr);
575        // Execute the read instruction immediately (same cycle as data read)
576        // This matches 6502 hardware where read and execute happen together
577        self.execute_read_instruction();
578        self.state = CpuState::FetchOpcode;
579        true // Instruction complete
580    }
581
582    fn tick_write_data(&mut self, bus: &mut impl CpuBus) -> bool {
583        // Execute the write instruction
584        let value = self.execute_write_instruction();
585        self.write_cycle(bus, self.effective_addr, value);
586        self.state = CpuState::FetchOpcode;
587        true
588    }
589
590    fn tick_rmw_read(&mut self, bus: &mut impl CpuBus) -> bool {
591        self.temp_value = self.read_cycle(bus, self.effective_addr);
592        self.state = CpuState::RmwDummyWrite;
593        false
594    }
595
596    fn tick_rmw_dummy_write(&mut self, bus: &mut impl CpuBus) -> bool {
597        // Write back the original value (hardware behavior)
598        self.write_cycle(bus, self.effective_addr, self.temp_value);
599        self.state = CpuState::RmwWrite;
600        false
601    }
602
603    fn tick_rmw_write(&mut self, bus: &mut impl CpuBus) -> bool {
604        // Execute the RMW operation and write result
605        let result = self.execute_rmw_instruction();
606        self.write_cycle(bus, self.effective_addr, result);
607        self.state = CpuState::FetchOpcode;
608        true
609    }
610
611    fn tick_execute(&mut self, bus: &mut impl CpuBus) -> bool {
612        // Execute the instruction logic (for implied/accumulator or after read)
613        match self.instr_type {
614            InstructionType::Implied => {
615                // Dummy read of next byte
616                self.dummy_cycle(bus, self.pc);
617                self.execute_implied_instruction();
618            }
619            InstructionType::Accumulator => {
620                self.dummy_cycle(bus, self.pc);
621                self.execute_accumulator_instruction();
622            }
623            InstructionType::Read => {
624                self.execute_read_instruction();
625            }
626            _ => {}
627        }
628        self.state = CpuState::FetchOpcode;
629        true
630    }
631
632    fn tick_fetch_indirect_lo(&mut self, bus: &mut impl CpuBus) -> bool {
633        match self.current_addr_mode {
634            AddressingMode::IndirectIndexedY => {
635                // Read low byte of pointer from zero page
636                self.operand_lo = self.read_cycle(bus, self.base_addr);
637                self.state = CpuState::FetchIndirectHi;
638            }
639            AddressingMode::Indirect => {
640                // JMP indirect: read low byte of target
641                self.operand_lo = self.read_cycle(bus, self.base_addr);
642                self.state = CpuState::FetchIndirectHi;
643            }
644            AddressingMode::IndexedIndirectX => {
645                // Read low byte from (base + X) in zero page
646                let ptr = self.effective_addr as u8;
647                self.operand_lo = self.read_cycle(bus, u16::from(ptr));
648                self.state = CpuState::FetchIndirectHi;
649            }
650            _ => {
651                self.state = CpuState::FetchOpcode;
652            }
653        }
654        false
655    }
656
657    fn tick_fetch_indirect_hi(&mut self, bus: &mut impl CpuBus) -> bool {
658        match self.current_addr_mode {
659            AddressingMode::IndirectIndexedY => {
660                // Read high byte from (base + 1) with zero page wrap
661                let ptr_hi = self.base_addr.wrapping_add(1) as u8;
662                self.operand_hi = self.read_cycle(bus, u16::from(ptr_hi));
663
664                let ptr_addr = u16::from_le_bytes([self.operand_lo, self.operand_hi]);
665                let indexed = ptr_addr.wrapping_add(u16::from(self.y));
666                self.base_addr = ptr_addr;
667                self.effective_addr = indexed;
668                self.page_crossed = (ptr_addr & 0xFF00) != (indexed & 0xFF00);
669
670                match self.instr_type {
671                    InstructionType::Write | InstructionType::ReadModifyWrite => {
672                        self.state = CpuState::ResolveAddress;
673                    }
674                    _ => {
675                        if self.page_crossed {
676                            self.state = CpuState::ResolveAddress;
677                        } else {
678                            self.state = self.next_state_for_instruction_type();
679                        }
680                    }
681                }
682            }
683            AddressingMode::Indirect => {
684                // JMP indirect: read high byte with page wrap bug
685                let ptr_lo = self.base_addr as u8;
686                let ptr_hi_addr = (self.base_addr & 0xFF00) | u16::from(ptr_lo.wrapping_add(1));
687                self.operand_hi = self.read_cycle(bus, ptr_hi_addr);
688
689                self.effective_addr = u16::from_le_bytes([self.operand_lo, self.operand_hi]);
690                self.pc = self.effective_addr;
691                self.state = CpuState::FetchOpcode;
692                return true;
693            }
694            AddressingMode::IndexedIndirectX => {
695                // Read high byte from (base + X + 1) with zero page wrap
696                let ptr = (self.effective_addr as u8).wrapping_add(1);
697                self.operand_hi = self.read_cycle(bus, u16::from(ptr));
698                self.effective_addr = u16::from_le_bytes([self.operand_lo, self.operand_hi]);
699                self.state = self.next_state_for_instruction_type();
700            }
701            _ => {
702                self.state = CpuState::FetchOpcode;
703            }
704        }
705        false
706    }
707
708    fn tick_add_index(&mut self, bus: &mut impl CpuBus) -> bool {
709        // Dummy read from base address
710        self.dummy_cycle(bus, self.base_addr);
711
712        match self.current_addr_mode {
713            AddressingMode::ZeroPageX => {
714                self.effective_addr = u16::from((self.base_addr as u8).wrapping_add(self.x));
715                self.state = self.next_state_for_instruction_type();
716            }
717            AddressingMode::ZeroPageY => {
718                self.effective_addr = u16::from((self.base_addr as u8).wrapping_add(self.y));
719                self.state = self.next_state_for_instruction_type();
720            }
721            AddressingMode::IndexedIndirectX => {
722                // Calculate pointer address with wrap
723                self.effective_addr = u16::from((self.base_addr as u8).wrapping_add(self.x));
724                self.state = CpuState::FetchIndirectLo;
725            }
726            _ => {
727                self.state = CpuState::FetchOpcode;
728            }
729        }
730        false
731    }
732
733    fn tick_push_hi(&mut self, bus: &mut impl CpuBus) -> bool {
734        let value = (self.pc >> 8) as u8;
735        self.write_cycle(bus, 0x0100 | u16::from(self.sp), value);
736        self.sp = self.sp.wrapping_sub(1);
737        self.state = CpuState::PushLo;
738        false
739    }
740
741    fn tick_push_lo(&mut self, bus: &mut impl CpuBus) -> bool {
742        let value = (self.pc & 0xFF) as u8;
743        self.write_cycle(bus, 0x0100 | u16::from(self.sp), value);
744        self.sp = self.sp.wrapping_sub(1);
745
746        match self.instr_type {
747            InstructionType::JumpSubroutine => {
748                // JSR: set PC to target address
749                self.pc = self.effective_addr;
750                self.state = CpuState::FetchOpcode;
751                return true;
752            }
753            InstructionType::Break => {
754                self.state = CpuState::PushStatus;
755            }
756            _ => {
757                self.state = CpuState::FetchOpcode;
758            }
759        }
760        false
761    }
762
763    fn tick_push_status(&mut self, bus: &mut impl CpuBus) -> bool {
764        match self.instr_type {
765            InstructionType::Push => {
766                // PHP: push status with B flag set
767                let value = self.status.to_stack_byte(true);
768                self.write_cycle(bus, 0x0100 | u16::from(self.sp), value);
769                self.sp = self.sp.wrapping_sub(1);
770                self.state = CpuState::FetchOpcode;
771                return true;
772            }
773            InstructionType::Break => {
774                // BRK: check for NMI hijacking
775                // If NMI is pending, it hijacks BRK by using the NMI vector instead of IRQ/BRK vector
776                // IMPORTANT: B flag is ALWAYS set to 1 when pushed from BRK, even when NMI hijacks!
777                // This allows software to detect NMI hijacking by checking B=1 in the NMI handler.
778                // Reference: Mesen2 NesCpu.cpp BRK(), NESdev wiki "6502 BRK and B bit"
779                let nmi_hijack = self.nmi_pending;
780                if nmi_hijack {
781                    self.nmi_pending = false;
782                }
783
784                // Push status with B flag ALWAYS set (even when NMI hijacks)
785                let value = self.status.to_stack_byte(true);
786                self.write_cycle(bus, 0x0100 | u16::from(self.sp), value);
787                self.sp = self.sp.wrapping_sub(1);
788                self.status.insert(StatusFlags::INTERRUPT_DISABLE);
789
790                // Suppress NMI check for one instruction after BRK completes
791                // This ensures the first instruction of the handler executes before checking for NMI
792                // Reference: Mesen2 NesCpu.cpp BRK() "_prevNeedNmi = false"
793                self.suppress_nmi_next = true;
794
795                // Use NMI vector if hijacked, IRQ/BRK vector otherwise
796                self.effective_addr = if nmi_hijack { 0xFFFA } else { 0xFFFE };
797                self.state = CpuState::InterruptFetchVectorLo;
798            }
799            _ => {
800                self.state = CpuState::FetchOpcode;
801            }
802        }
803        false
804    }
805
806    fn tick_pop_lo(&mut self, bus: &mut impl CpuBus) -> bool {
807        // Internal cycle: increment SP
808        self.sp = self.sp.wrapping_add(1);
809        self.dummy_cycle(bus, 0x0100 | u16::from(self.sp));
810
811        match self.instr_type {
812            InstructionType::Pull => {
813                self.state = CpuState::Execute;
814            }
815            InstructionType::ReturnSubroutine => {
816                self.operand_lo = self.read_cycle(bus, 0x0100 | u16::from(self.sp));
817                self.state = CpuState::PopHi;
818            }
819            InstructionType::ReturnInterrupt => {
820                // First pop is status
821                self.state = CpuState::PopStatus;
822            }
823            _ => {
824                self.state = CpuState::FetchOpcode;
825            }
826        }
827        false
828    }
829
830    fn tick_pop_hi(&mut self, bus: &mut impl CpuBus) -> bool {
831        self.sp = self.sp.wrapping_add(1);
832        self.operand_hi = self.read_cycle(bus, 0x0100 | u16::from(self.sp));
833
834        match self.instr_type {
835            InstructionType::ReturnSubroutine => {
836                self.pc = u16::from_le_bytes([self.operand_lo, self.operand_hi]);
837                self.state = CpuState::InternalCycle;
838            }
839            InstructionType::ReturnInterrupt => {
840                self.pc = u16::from_le_bytes([self.operand_lo, self.operand_hi]);
841                self.state = CpuState::FetchOpcode;
842                return true;
843            }
844            _ => {
845                self.state = CpuState::FetchOpcode;
846            }
847        }
848        false
849    }
850
851    fn tick_pop_status(&mut self, bus: &mut impl CpuBus) -> bool {
852        let value = self.read_cycle(bus, 0x0100 | u16::from(self.sp));
853        self.status = StatusFlags::from_stack_byte(value);
854
855        // Match RTI behavior from instructions.rs:
856        // If RTI restores I=1 (Disabled), interrupts must be blocked immediately for the NEXT instruction.
857        if self.status.contains(StatusFlags::INTERRUPT_DISABLE) {
858            self.prev_irq_inhibit = true;
859        }
860
861        self.sp = self.sp.wrapping_add(1);
862        self.operand_lo = self.read_cycle(bus, 0x0100 | u16::from(self.sp));
863        self.state = CpuState::PopHi;
864        false
865    }
866
867    fn tick_internal_cycle(&mut self, bus: &mut impl CpuBus) -> bool {
868        // Dummy read
869        self.dummy_cycle(bus, 0x0100 | u16::from(self.sp));
870
871        match self.instr_type {
872            InstructionType::JumpSubroutine => {
873                // After internal cycle, push return address
874                self.state = CpuState::PushHi;
875            }
876            InstructionType::ReturnSubroutine => {
877                // Increment PC (RTS returns to addr+1)
878                self.pc = self.pc.wrapping_add(1);
879                self.state = CpuState::FetchOpcode;
880                return true;
881            }
882            InstructionType::Push => {
883                // PHA: after reading, push value
884                match self.current_opcode {
885                    0x48 => {
886                        // PHA
887                        self.write_cycle(bus, 0x0100 | u16::from(self.sp), self.a);
888                        self.sp = self.sp.wrapping_sub(1);
889                    }
890                    0x08 => {
891                        // PHP is handled in PushStatus
892                        self.state = CpuState::PushStatus;
893                        return false;
894                    }
895                    _ => {}
896                }
897                self.state = CpuState::FetchOpcode;
898                return true;
899            }
900            InstructionType::Pull => {
901                // After internal cycle, read from stack
902                self.sp = self.sp.wrapping_add(1);
903                self.temp_value = self.read_cycle(bus, 0x0100 | u16::from(self.sp));
904                match self.current_opcode {
905                    0x68 => {
906                        // PLA
907                        self.a = self.temp_value;
908                        self.set_zn(self.a);
909                    }
910                    0x28 => {
911                        // PLP
912                        self.status = StatusFlags::from_stack_byte(self.temp_value);
913                    }
914                    _ => {}
915                }
916                self.state = CpuState::FetchOpcode;
917                return true;
918            }
919            _ => {
920                self.state = CpuState::FetchOpcode;
921            }
922        }
923        false
924    }
925
926    fn tick_branch_taken(&mut self, bus: &mut impl CpuBus) -> bool {
927        // Dummy read during branch taken
928        self.dummy_cycle(bus, self.pc);
929
930        let old_pc = self.pc;
931        self.pc = self.pc.wrapping_add(self.branch_offset as u16);
932
933        // Check for page crossing
934        if (old_pc & 0xFF00) == (self.pc & 0xFF00) {
935            self.state = CpuState::FetchOpcode;
936            true
937        } else {
938            self.state = CpuState::BranchPageCross;
939            false
940        }
941    }
942
943    fn tick_branch_page_cross(&mut self, bus: &mut impl CpuBus) -> bool {
944        // Dummy read during page crossing fix
945        self.dummy_cycle(
946            bus,
947            (self.pc & 0x00FF) | ((self.pc.wrapping_sub(self.branch_offset as u16)) & 0xFF00),
948        );
949        self.state = CpuState::FetchOpcode;
950        true
951    }
952
953    fn tick_interrupt_push_pc_hi(&mut self, bus: &mut impl CpuBus) -> bool {
954        let value = (self.pc >> 8) as u8;
955        self.write_cycle(bus, 0x0100 | u16::from(self.sp), value);
956        self.sp = self.sp.wrapping_sub(1);
957        self.state = CpuState::InterruptPushPcLo;
958        false
959    }
960
961    fn tick_interrupt_push_pc_lo(&mut self, bus: &mut impl CpuBus) -> bool {
962        let value = (self.pc & 0xFF) as u8;
963        self.write_cycle(bus, 0x0100 | u16::from(self.sp), value);
964        self.sp = self.sp.wrapping_sub(1);
965        self.state = CpuState::InterruptPushStatus;
966        false
967    }
968
969    fn tick_interrupt_push_status(&mut self, bus: &mut impl CpuBus) -> bool {
970        // Interrupts push status with B=0
971        let value = self.status.to_stack_byte(false);
972        self.write_cycle(bus, 0x0100 | u16::from(self.sp), value);
973        self.sp = self.sp.wrapping_sub(1);
974        self.status.insert(StatusFlags::INTERRUPT_DISABLE);
975        self.state = CpuState::InterruptFetchVectorLo;
976        false
977    }
978
979    fn tick_interrupt_fetch_vector_lo(&mut self, bus: &mut impl CpuBus) -> bool {
980        self.operand_lo = self.read_cycle(bus, self.effective_addr);
981        self.state = CpuState::InterruptFetchVectorHi;
982        false
983    }
984
985    fn tick_interrupt_fetch_vector_hi(&mut self, bus: &mut impl CpuBus) -> bool {
986        self.operand_hi = self.read_cycle(bus, self.effective_addr.wrapping_add(1));
987        self.pc = u16::from_le_bytes([self.operand_lo, self.operand_hi]);
988        self.state = CpuState::FetchOpcode;
989        true
990    }
991
992    // =========================================================================
993    // CYCLE-ACCURATE MEMORY ACCESS METHODS
994    // =========================================================================
995    //
996    // These methods implement the sub-cycle accurate memory access pattern
997    // where on_cpu_cycle() is called BEFORE each memory access. This ensures
998    // PPU and APU are stepped to the correct state before the CPU observes
999    // memory (critical for accurate $2002 VBlank flag timing).
1000
1001    /// Read a byte from memory with cycle callback.
1002    ///
1003    /// Calls `on_cpu_cycle()` BEFORE the read, then performs the actual read.
1004    /// This is the cycle-accurate version of memory read.
1005    ///
1006    /// # Arguments
1007    ///
1008    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1009    /// * `addr` - 16-bit memory address to read from
1010    ///
1011    /// # Returns
1012    ///
1013    /// The 8-bit value at the specified address
1014    ///
1015    /// # Timing
1016    ///
1017    /// ```text
1018    /// CPU Cycle:  |-------- read --------|
1019    /// PPU Cycles: |--1--|--2--|--3--|
1020    ///              ^ on_cpu_cycle() called here
1021    /// ```
1022    #[inline]
1023    pub fn read_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u8 {
1024        bus.on_cpu_cycle();
1025        bus.read(addr)
1026    }
1027
1028    /// Write a byte to memory with cycle callback.
1029    ///
1030    /// Calls `on_cpu_cycle()` BEFORE the write, then performs the actual write.
1031    /// This is the cycle-accurate version of memory write.
1032    ///
1033    /// # Arguments
1034    ///
1035    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1036    /// * `addr` - 16-bit memory address to write to
1037    /// * `value` - 8-bit value to write
1038    ///
1039    /// # Timing
1040    ///
1041    /// ```text
1042    /// CPU Cycle:  |------- write --------|
1043    /// PPU Cycles: |--1--|--2--|--3--|
1044    ///              ^ on_cpu_cycle() called here
1045    /// ```
1046    #[inline]
1047    pub fn write_cycle(&mut self, bus: &mut impl CpuBus, addr: u16, value: u8) {
1048        bus.on_cpu_cycle();
1049        bus.write(addr, value);
1050    }
1051
1052    /// Perform a dummy read cycle (for timing purposes).
1053    ///
1054    /// Some instructions require timing cycles where a read is performed
1055    /// but the value is discarded. This method handles those cases while
1056    /// still calling `on_cpu_cycle()` for proper PPU/APU synchronization.
1057    ///
1058    /// # Use Cases
1059    ///
1060    /// - Page boundary crossing in indexed addressing
1061    /// - Implied/Accumulator mode dummy reads
1062    /// - Branch taken cycles
1063    /// - RMW dummy write-back cycles
1064    ///
1065    /// # Arguments
1066    ///
1067    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1068    /// * `addr` - Address to read from (value is discarded)
1069    #[inline]
1070    pub fn dummy_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) {
1071        bus.on_cpu_cycle();
1072        let _ = bus.read(addr);
1073    }
1074
1075    /// Push a byte to the stack with cycle callback.
1076    ///
1077    /// Calls `on_cpu_cycle()` BEFORE the write, then pushes to stack.
1078    ///
1079    /// # Arguments
1080    ///
1081    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1082    /// * `value` - 8-bit value to push
1083    #[inline]
1084    pub fn push_cycle(&mut self, bus: &mut impl CpuBus, value: u8) {
1085        bus.on_cpu_cycle();
1086        bus.write(0x0100 | u16::from(self.sp), value);
1087        self.sp = self.sp.wrapping_sub(1);
1088    }
1089
1090    /// Pop a byte from the stack with cycle callback.
1091    ///
1092    /// Calls `on_cpu_cycle()` BEFORE the read, then pops from stack.
1093    ///
1094    /// # Arguments
1095    ///
1096    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1097    ///
1098    /// # Returns
1099    ///
1100    /// The 8-bit value popped from the stack
1101    #[inline]
1102    pub fn pop_cycle(&mut self, bus: &mut impl CpuBus) -> u8 {
1103        self.sp = self.sp.wrapping_add(1);
1104        bus.on_cpu_cycle();
1105        bus.read(0x0100 | u16::from(self.sp))
1106    }
1107
1108    /// Read a 16-bit value from memory with cycle callbacks.
1109    ///
1110    /// Performs two sequential reads with proper cycle callbacks.
1111    /// Each read triggers `on_cpu_cycle()`.
1112    ///
1113    /// # Arguments
1114    ///
1115    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1116    /// * `addr` - Address of the low byte
1117    ///
1118    /// # Returns
1119    ///
1120    /// 16-bit value: `(high << 8) | low`
1121    #[inline]
1122    pub fn read_u16_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u16 {
1123        let lo = self.read_cycle(bus, addr) as u16;
1124        let hi = self.read_cycle(bus, addr.wrapping_add(1)) as u16;
1125        (hi << 8) | lo
1126    }
1127
1128    /// Read a 16-bit value with page wrap and cycle callbacks.
1129    ///
1130    /// Implements the JMP indirect bug where the high byte wraps within
1131    /// the same page if the low byte is at $xxFF.
1132    ///
1133    /// # Arguments
1134    ///
1135    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1136    /// * `addr` - Address of the low byte
1137    ///
1138    /// # Returns
1139    ///
1140    /// 16-bit value with page-wrap bug behavior
1141    #[inline]
1142    pub fn read_u16_wrap_cycle(&mut self, bus: &mut impl CpuBus, addr: u16) -> u16 {
1143        let lo = self.read_cycle(bus, addr) as u16;
1144
1145        // If low byte is at $xxFF, high byte wraps to $xx00 (6502 bug)
1146        let hi_addr = if addr & 0xFF == 0xFF {
1147            addr & 0xFF00
1148        } else {
1149            addr.wrapping_add(1)
1150        };
1151
1152        let hi = self.read_cycle(bus, hi_addr) as u16;
1153        (hi << 8) | lo
1154    }
1155
1156    /// Push a 16-bit value to the stack with cycle callbacks.
1157    ///
1158    /// Pushes high byte first, then low byte (6502 convention).
1159    /// Each push triggers `on_cpu_cycle()`.
1160    ///
1161    /// # Arguments
1162    ///
1163    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1164    /// * `value` - 16-bit value to push
1165    #[inline]
1166    pub fn push_u16_cycle(&mut self, bus: &mut impl CpuBus, value: u16) {
1167        self.push_cycle(bus, (value >> 8) as u8);
1168        self.push_cycle(bus, (value & 0xFF) as u8);
1169    }
1170
1171    /// Pop a 16-bit value from the stack with cycle callbacks.
1172    ///
1173    /// Pops low byte first, then high byte (6502 convention).
1174    /// Each pop triggers `on_cpu_cycle()`.
1175    ///
1176    /// # Arguments
1177    ///
1178    /// * `bus` - The cycle-aware bus implementing `CpuBus`
1179    ///
1180    /// # Returns
1181    ///
1182    /// 16-bit value popped from the stack
1183    #[inline]
1184    pub fn pop_u16_cycle(&mut self, bus: &mut impl CpuBus) -> u16 {
1185        let lo = self.pop_cycle(bus);
1186        let hi = self.pop_cycle(bus);
1187        u16::from_le_bytes([lo, hi])
1188    }
1189
1190    // =========================================================================
1191    // HELPER METHODS
1192    // =========================================================================
1193
1194    /// Determine next state based on instruction type.
1195    fn next_state_for_instruction_type(&self) -> CpuState {
1196        match self.instr_type {
1197            InstructionType::Read => CpuState::ReadData,
1198            InstructionType::Write => CpuState::WriteData,
1199            InstructionType::ReadModifyWrite => CpuState::RmwRead,
1200            InstructionType::Implied | InstructionType::Accumulator => CpuState::Execute,
1201            InstructionType::Push => CpuState::InternalCycle,
1202            InstructionType::Pull => CpuState::InternalCycle,
1203            _ => CpuState::Execute,
1204        }
1205    }
1206
1207    /// Check if branch condition is met for current opcode.
1208    fn check_branch_condition(&self) -> bool {
1209        match self.current_opcode {
1210            0x10 => !self.status.contains(StatusFlags::NEGATIVE), // BPL
1211            0x30 => self.status.contains(StatusFlags::NEGATIVE),  // BMI
1212            0x50 => !self.status.contains(StatusFlags::OVERFLOW), // BVC
1213            0x70 => self.status.contains(StatusFlags::OVERFLOW),  // BVS
1214            0x90 => !self.status.contains(StatusFlags::CARRY),    // BCC
1215            0xB0 => self.status.contains(StatusFlags::CARRY),     // BCS
1216            0xD0 => !self.status.contains(StatusFlags::ZERO),     // BNE
1217            0xF0 => self.status.contains(StatusFlags::ZERO),      // BEQ
1218            _ => false,
1219        }
1220    }
1221
1222    /// Execute an implied instruction (register-only operations).
1223    fn execute_implied_instruction(&mut self) {
1224        match self.current_opcode {
1225            // Transfers
1226            0xAA => {
1227                self.x = self.a;
1228                self.set_zn(self.x);
1229            } // TAX
1230            0xA8 => {
1231                self.y = self.a;
1232                self.set_zn(self.y);
1233            } // TAY
1234            0x8A => {
1235                self.a = self.x;
1236                self.set_zn(self.a);
1237            } // TXA
1238            0x98 => {
1239                self.a = self.y;
1240                self.set_zn(self.a);
1241            } // TYA
1242            0xBA => {
1243                self.a = self.sp;
1244                self.set_zn(self.a);
1245            } // TSX
1246            0x9A => {
1247                self.sp = self.x;
1248            } // TXS
1249
1250            // Increment/Decrement
1251            0xE8 => {
1252                self.x = self.x.wrapping_add(1);
1253                self.set_zn(self.x);
1254            } // INX
1255            0xC8 => {
1256                self.y = self.y.wrapping_add(1);
1257                self.set_zn(self.y);
1258            } // INY
1259            0xCA => {
1260                self.x = self.x.wrapping_sub(1);
1261                self.set_zn(self.x);
1262            } // DEX
1263            0x88 => {
1264                self.y = self.y.wrapping_sub(1);
1265                self.set_zn(self.y);
1266            } // DEY
1267
1268            // Flags
1269            0x18 => {
1270                self.status.remove(StatusFlags::CARRY);
1271            } // CLC
1272            0x38 => {
1273                self.status.insert(StatusFlags::CARRY);
1274            } // SEC
1275            0x58 => {
1276                self.status.remove(StatusFlags::INTERRUPT_DISABLE);
1277            } // CLI
1278            0x78 => {
1279                self.status.insert(StatusFlags::INTERRUPT_DISABLE);
1280            } // SEI
1281            0xB8 => {
1282                self.status.remove(StatusFlags::OVERFLOW);
1283            } // CLV
1284            0xD8 => {
1285                self.status.remove(StatusFlags::DECIMAL);
1286            } // CLD
1287            0xF8 => {
1288                self.status.insert(StatusFlags::DECIMAL);
1289            } // SED
1290
1291            // NOP (official and unofficial)
1292            0xEA | 0x1A | 0x3A | 0x5A | 0x7A | 0xDA | 0xFA => {}
1293
1294            _ => {}
1295        }
1296    }
1297
1298    /// Execute an accumulator instruction (ASL A, LSR A, ROL A, ROR A).
1299    fn execute_accumulator_instruction(&mut self) {
1300        match self.current_opcode {
1301            0x0A => {
1302                // ASL A
1303                let carry = (self.a & 0x80) != 0;
1304                self.a <<= 1;
1305                self.status.set(StatusFlags::CARRY, carry);
1306                self.set_zn(self.a);
1307            }
1308            0x4A => {
1309                // LSR A
1310                let carry = (self.a & 0x01) != 0;
1311                self.a >>= 1;
1312                self.status.set(StatusFlags::CARRY, carry);
1313                self.set_zn(self.a);
1314            }
1315            0x2A => {
1316                // ROL A
1317                let old_carry = self.status.contains(StatusFlags::CARRY);
1318                let new_carry = (self.a & 0x80) != 0;
1319                self.a = (self.a << 1) | u8::from(old_carry);
1320                self.status.set(StatusFlags::CARRY, new_carry);
1321                self.set_zn(self.a);
1322            }
1323            0x6A => {
1324                // ROR A
1325                let old_carry = self.status.contains(StatusFlags::CARRY);
1326                let new_carry = (self.a & 0x01) != 0;
1327                self.a = (self.a >> 1) | (u8::from(old_carry) << 7);
1328                self.status.set(StatusFlags::CARRY, new_carry);
1329                self.set_zn(self.a);
1330            }
1331            _ => {}
1332        }
1333    }
1334
1335    /// Execute a read instruction using self.temp_value.
1336    #[allow(clippy::too_many_lines)]
1337    fn execute_read_instruction(&mut self) {
1338        let value = self.temp_value;
1339        match self.current_opcode {
1340            // LDA
1341            0xA9 | 0xA5 | 0xB5 | 0xAD | 0xBD | 0xB9 | 0xA1 | 0xB1 => {
1342                self.a = value;
1343                self.set_zn(self.a);
1344            }
1345            // LDX
1346            0xA2 | 0xA6 | 0xB6 | 0xAE | 0xBE => {
1347                self.x = value;
1348                self.set_zn(self.x);
1349            }
1350            // LDY
1351            0xA0 | 0xA4 | 0xB4 | 0xAC | 0xBC => {
1352                self.y = value;
1353                self.set_zn(self.y);
1354            }
1355            // ADC
1356            0x69 | 0x65 | 0x75 | 0x6D | 0x7D | 0x79 | 0x61 | 0x71 => {
1357                self.do_adc(value);
1358            }
1359            // SBC (including unofficial 0xEB)
1360            0xE9 | 0xE5 | 0xF5 | 0xED | 0xFD | 0xF9 | 0xE1 | 0xF1 | 0xEB => {
1361                self.do_sbc(value);
1362            }
1363            // AND
1364            0x29 | 0x25 | 0x35 | 0x2D | 0x3D | 0x39 | 0x21 | 0x31 => {
1365                self.a &= value;
1366                self.set_zn(self.a);
1367            }
1368            // ORA
1369            0x09 | 0x05 | 0x15 | 0x0D | 0x1D | 0x19 | 0x01 | 0x11 => {
1370                self.a |= value;
1371                self.set_zn(self.a);
1372            }
1373            // EOR
1374            0x49 | 0x45 | 0x55 | 0x4D | 0x5D | 0x59 | 0x41 | 0x51 => {
1375                self.a ^= value;
1376                self.set_zn(self.a);
1377            }
1378            // CMP
1379            0xC9 | 0xC5 | 0xD5 | 0xCD | 0xDD | 0xD9 | 0xC1 | 0xD1 => {
1380                self.do_compare(self.a, value);
1381            }
1382            // CPX
1383            0xE0 | 0xE4 | 0xEC => {
1384                self.do_compare(self.x, value);
1385            }
1386            // CPY
1387            0xC0 | 0xC4 | 0xCC => {
1388                self.do_compare(self.y, value);
1389            }
1390            // BIT
1391            0x24 | 0x2C => {
1392                self.status.set(StatusFlags::ZERO, (self.a & value) == 0);
1393                self.status.set(StatusFlags::OVERFLOW, (value & 0x40) != 0);
1394                self.status.set(StatusFlags::NEGATIVE, (value & 0x80) != 0);
1395            }
1396            // LAX (unofficial)
1397            0xA7 | 0xB7 | 0xAF | 0xBF | 0xA3 | 0xB3 => {
1398                self.a = value;
1399                self.x = value;
1400                self.set_zn(self.a);
1401            }
1402            // LAS (unofficial)
1403            0xBB => {
1404                let result = value & self.sp;
1405                self.a = result;
1406                self.x = result;
1407                self.sp = result;
1408                self.set_zn(result);
1409            }
1410            // ANC (unofficial)
1411            0x0B | 0x2B => {
1412                self.a &= value;
1413                self.set_zn(self.a);
1414                self.status.set(StatusFlags::CARRY, (self.a & 0x80) != 0);
1415            }
1416            // ALR (unofficial)
1417            0x4B => {
1418                self.a &= value;
1419                let carry = (self.a & 0x01) != 0;
1420                self.a >>= 1;
1421                self.status.set(StatusFlags::CARRY, carry);
1422                self.set_zn(self.a);
1423            }
1424            // ARR (unofficial)
1425            0x6B => {
1426                self.a &= value;
1427                let old_carry = self.status.contains(StatusFlags::CARRY);
1428                self.a = (self.a >> 1) | (u8::from(old_carry) << 7);
1429                self.set_zn(self.a);
1430                self.status.set(StatusFlags::CARRY, (self.a & 0x40) != 0);
1431                self.status.set(
1432                    StatusFlags::OVERFLOW,
1433                    ((self.a & 0x40) ^ ((self.a & 0x20) << 1)) != 0,
1434                );
1435            }
1436            // XAA (unofficial, unstable)
1437            0x8B => {
1438                self.a = (self.a | 0xEE) & self.x & value;
1439                self.set_zn(self.a);
1440            }
1441            // LXA (unofficial)
1442            0xAB => {
1443                self.a = (self.a | 0xEE) & value;
1444                self.x = self.a;
1445                self.set_zn(self.a);
1446            }
1447            // AXS (unofficial)
1448            0xCB => {
1449                let temp = (self.a & self.x).wrapping_sub(value);
1450                self.status
1451                    .set(StatusFlags::CARRY, (self.a & self.x) >= value);
1452                self.x = temp;
1453                self.set_zn(self.x);
1454            }
1455            // NOPs with read (unofficial)
1456            0x80 | 0x82 | 0x89 | 0xC2 | 0xE2 | 0x04 | 0x44 | 0x64 | 0x14 | 0x34 | 0x54 | 0x74
1457            | 0xD4 | 0xF4 | 0x0C | 0x1C | 0x3C | 0x5C | 0x7C | 0xDC | 0xFC => {
1458                // Do nothing - just read
1459            }
1460            _ => {}
1461        }
1462    }
1463
1464    /// Execute a write instruction, returning value to write.
1465    fn execute_write_instruction(&self) -> u8 {
1466        match self.current_opcode {
1467            // STA
1468            0x85 | 0x95 | 0x8D | 0x9D | 0x99 | 0x81 | 0x91 => self.a,
1469            // STX
1470            0x86 | 0x96 | 0x8E => self.x,
1471            // STY
1472            0x84 | 0x94 | 0x8C => self.y,
1473            // SAX (unofficial)
1474            0x87 | 0x97 | 0x8F | 0x83 => self.a & self.x,
1475            // SHA (unofficial) - highly unstable
1476            0x93 | 0x9F => self.a & self.x & ((self.effective_addr >> 8) as u8).wrapping_add(1),
1477            // SHX (unofficial)
1478            0x9E => self.x & ((self.effective_addr >> 8) as u8).wrapping_add(1),
1479            // SHY (unofficial)
1480            0x9C => self.y & ((self.effective_addr >> 8) as u8).wrapping_add(1),
1481            // TAS (unofficial)
1482            0x9B => {
1483                // This also affects SP, but we handle value here
1484                self.a & self.x & ((self.effective_addr >> 8) as u8).wrapping_add(1)
1485            }
1486            _ => 0,
1487        }
1488    }
1489
1490    /// Execute an RMW instruction, returning the new value.
1491    fn execute_rmw_instruction(&mut self) -> u8 {
1492        let value = self.temp_value;
1493        match self.current_opcode {
1494            // ASL
1495            0x06 | 0x16 | 0x0E | 0x1E => {
1496                let carry = (value & 0x80) != 0;
1497                let result = value << 1;
1498                self.status.set(StatusFlags::CARRY, carry);
1499                self.set_zn(result);
1500                result
1501            }
1502            // LSR
1503            0x46 | 0x56 | 0x4E | 0x5E => {
1504                let carry = (value & 0x01) != 0;
1505                let result = value >> 1;
1506                self.status.set(StatusFlags::CARRY, carry);
1507                self.set_zn(result);
1508                result
1509            }
1510            // ROL
1511            0x26 | 0x36 | 0x2E | 0x3E => {
1512                let old_carry = self.status.contains(StatusFlags::CARRY);
1513                let new_carry = (value & 0x80) != 0;
1514                let result = (value << 1) | u8::from(old_carry);
1515                self.status.set(StatusFlags::CARRY, new_carry);
1516                self.set_zn(result);
1517                result
1518            }
1519            // ROR
1520            0x66 | 0x76 | 0x6E | 0x7E => {
1521                let old_carry = self.status.contains(StatusFlags::CARRY);
1522                let new_carry = (value & 0x01) != 0;
1523                let result = (value >> 1) | (u8::from(old_carry) << 7);
1524                self.status.set(StatusFlags::CARRY, new_carry);
1525                self.set_zn(result);
1526                result
1527            }
1528            // INC
1529            0xE6 | 0xF6 | 0xEE | 0xFE => {
1530                let result = value.wrapping_add(1);
1531                self.set_zn(result);
1532                result
1533            }
1534            // DEC
1535            0xC6 | 0xD6 | 0xCE | 0xDE => {
1536                let result = value.wrapping_sub(1);
1537                self.set_zn(result);
1538                result
1539            }
1540            // SLO (unofficial: ASL + ORA)
1541            0x07 | 0x17 | 0x0F | 0x1F | 0x1B | 0x03 | 0x13 => {
1542                let carry = (value & 0x80) != 0;
1543                let result = value << 1;
1544                self.status.set(StatusFlags::CARRY, carry);
1545                self.a |= result;
1546                self.set_zn(self.a);
1547                result
1548            }
1549            // RLA (unofficial: ROL + AND)
1550            0x27 | 0x37 | 0x2F | 0x3F | 0x3B | 0x23 | 0x33 => {
1551                let old_carry = self.status.contains(StatusFlags::CARRY);
1552                let new_carry = (value & 0x80) != 0;
1553                let result = (value << 1) | u8::from(old_carry);
1554                self.status.set(StatusFlags::CARRY, new_carry);
1555                self.a &= result;
1556                self.set_zn(self.a);
1557                result
1558            }
1559            // SRE (unofficial: LSR + EOR)
1560            0x47 | 0x57 | 0x4F | 0x5F | 0x5B | 0x43 | 0x53 => {
1561                let carry = (value & 0x01) != 0;
1562                let result = value >> 1;
1563                self.status.set(StatusFlags::CARRY, carry);
1564                self.a ^= result;
1565                self.set_zn(self.a);
1566                result
1567            }
1568            // RRA (unofficial: ROR + ADC)
1569            0x67 | 0x77 | 0x6F | 0x7F | 0x7B | 0x63 | 0x73 => {
1570                let old_carry = self.status.contains(StatusFlags::CARRY);
1571                let new_carry = (value & 0x01) != 0;
1572                let result = (value >> 1) | (u8::from(old_carry) << 7);
1573                self.status.set(StatusFlags::CARRY, new_carry);
1574                self.do_adc(result);
1575                result
1576            }
1577            // DCP (unofficial: DEC + CMP)
1578            0xC7 | 0xD7 | 0xCF | 0xDF | 0xDB | 0xC3 | 0xD3 => {
1579                let result = value.wrapping_sub(1);
1580                self.do_compare(self.a, result);
1581                result
1582            }
1583            // ISC (unofficial: INC + SBC)
1584            0xE7 | 0xF7 | 0xEF | 0xFF | 0xFB | 0xE3 | 0xF3 => {
1585                let result = value.wrapping_add(1);
1586                self.do_sbc(result);
1587                result
1588            }
1589            _ => value,
1590        }
1591    }
1592
1593    /// Perform ADC operation.
1594    fn do_adc(&mut self, value: u8) {
1595        let carry = u16::from(self.status.contains(StatusFlags::CARRY));
1596        let sum = u16::from(self.a) + u16::from(value) + carry;
1597        let result = sum as u8;
1598
1599        self.status.set(StatusFlags::CARRY, sum > 0xFF);
1600        self.status.set(
1601            StatusFlags::OVERFLOW,
1602            (!(self.a ^ value) & (self.a ^ result) & 0x80) != 0,
1603        );
1604        self.a = result;
1605        self.set_zn(self.a);
1606    }
1607
1608    /// Perform SBC operation.
1609    fn do_sbc(&mut self, value: u8) {
1610        // SBC is equivalent to ADC with the value inverted
1611        self.do_adc(!value);
1612    }
1613
1614    /// Perform compare operation.
1615    fn do_compare(&mut self, register: u8, value: u8) {
1616        let result = register.wrapping_sub(value);
1617        self.status.set(StatusFlags::CARRY, register >= value);
1618        self.set_zn(result);
1619    }
1620
1621    /// Handle NMI interrupt (7 cycles).
1622    #[inline]
1623    fn handle_nmi(&mut self, bus: &mut impl Bus) -> u8 {
1624        self.push_u16(bus, self.pc);
1625        self.push(bus, self.status.to_stack_byte(false)); // B=0 for interrupts
1626        self.status.insert(StatusFlags::INTERRUPT_DISABLE);
1627        self.pc = bus.read_u16(0xFFFA); // NMI vector
1628        7
1629    }
1630
1631    /// Handle IRQ interrupt (7 cycles).
1632    #[inline]
1633    fn handle_irq(&mut self, bus: &mut impl Bus) -> u8 {
1634        self.push_u16(bus, self.pc);
1635        self.push(bus, self.status.to_stack_byte(false)); // B=0 for interrupts
1636        self.status.insert(StatusFlags::INTERRUPT_DISABLE);
1637        self.pc = bus.read_u16(0xFFFE); // IRQ vector
1638        7
1639    }
1640
1641    /// Execute a single opcode.
1642    ///
1643    /// Returns extra cycles taken (for page crossing, branches, etc.).
1644    #[inline]
1645    fn execute_opcode(&mut self, opcode: u8, addr_mode: AddressingMode, bus: &mut impl Bus) -> u8 {
1646        match opcode {
1647            // Load/Store
1648            0xA9 => self.lda(bus, addr_mode),
1649            0xA5 | 0xB5 | 0xAD | 0xBD | 0xB9 | 0xA1 | 0xB1 => self.lda(bus, addr_mode),
1650            0xA2 => self.ldx(bus, addr_mode),
1651            0xA6 | 0xB6 | 0xAE | 0xBE => self.ldx(bus, addr_mode),
1652            0xA0 => self.ldy(bus, addr_mode),
1653            0xA4 | 0xB4 | 0xAC | 0xBC => self.ldy(bus, addr_mode),
1654            0x85 | 0x95 | 0x8D | 0x9D | 0x99 | 0x81 | 0x91 => self.sta(bus, addr_mode),
1655            0x86 | 0x96 | 0x8E => self.stx(bus, addr_mode),
1656            0x84 | 0x94 | 0x8C => self.sty(bus, addr_mode),
1657
1658            // Transfer
1659            0xAA => self.tax(bus),
1660            0xA8 => self.tay(bus),
1661            0x8A => self.txa(bus),
1662            0x98 => self.tya(bus),
1663            0xBA => self.tsx(bus),
1664            0x9A => self.txs(bus),
1665
1666            // Stack
1667            0x48 => self.pha(bus),
1668            0x08 => self.php(bus),
1669            0x68 => self.pla(bus),
1670            0x28 => self.plp(bus),
1671
1672            // Arithmetic
1673            0x69 | 0x65 | 0x75 | 0x6D | 0x7D | 0x79 | 0x61 | 0x71 => self.adc(bus, addr_mode),
1674            0xE9 | 0xE5 | 0xF5 | 0xED | 0xFD | 0xF9 | 0xE1 | 0xF1 | 0xEB => {
1675                self.sbc(bus, addr_mode)
1676            }
1677
1678            // Increment/Decrement
1679            0xE6 | 0xF6 | 0xEE | 0xFE => self.inc(bus, addr_mode),
1680            0xC6 | 0xD6 | 0xCE | 0xDE => self.dec(bus, addr_mode),
1681            0xE8 => self.inx(bus),
1682            0xC8 => self.iny(bus),
1683            0xCA => self.dex(bus),
1684            0x88 => self.dey(bus),
1685
1686            // Logic
1687            0x29 | 0x25 | 0x35 | 0x2D | 0x3D | 0x39 | 0x21 | 0x31 => self.and(bus, addr_mode),
1688            0x09 | 0x05 | 0x15 | 0x0D | 0x1D | 0x19 | 0x01 | 0x11 => self.ora(bus, addr_mode),
1689            0x49 | 0x45 | 0x55 | 0x4D | 0x5D | 0x59 | 0x41 | 0x51 => self.eor(bus, addr_mode),
1690            0x24 | 0x2C => self.bit(bus, addr_mode),
1691
1692            // Shift/Rotate
1693            0x0A => self.asl_acc(bus),
1694            0x06 | 0x16 | 0x0E | 0x1E => self.asl(bus, addr_mode),
1695            0x4A => self.lsr_acc(bus),
1696            0x46 | 0x56 | 0x4E | 0x5E => self.lsr(bus, addr_mode),
1697            0x2A => self.rol_acc(bus),
1698            0x26 | 0x36 | 0x2E | 0x3E => self.rol(bus, addr_mode),
1699            0x6A => self.ror_acc(bus),
1700            0x66 | 0x76 | 0x6E | 0x7E => self.ror(bus, addr_mode),
1701
1702            // Compare
1703            0xC9 | 0xC5 | 0xD5 | 0xCD | 0xDD | 0xD9 | 0xC1 | 0xD1 => self.cmp(bus, addr_mode),
1704            0xE0 | 0xE4 | 0xEC => self.cpx(bus, addr_mode),
1705            0xC0 | 0xC4 | 0xCC => self.cpy(bus, addr_mode),
1706
1707            // Branch
1708            0x10 => self.bpl(bus),
1709            0x30 => self.bmi(bus),
1710            0x50 => self.bvc(bus),
1711            0x70 => self.bvs(bus),
1712            0x90 => self.bcc(bus),
1713            0xB0 => self.bcs(bus),
1714            0xD0 => self.bne(bus),
1715            0xF0 => self.beq(bus),
1716
1717            // Jump/Subroutine
1718            0x4C => self.jmp_abs(bus),
1719            0x6C => self.jmp_ind(bus),
1720            0x20 => self.jsr(bus),
1721            0x60 => self.rts(bus),
1722            0x40 => self.rti(bus),
1723            0x00 => self.brk(bus),
1724
1725            // Flags
1726            0x18 => self.clc(bus),
1727            0x38 => self.sec(bus),
1728            0x58 => self.cli(bus),
1729            0x78 => self.sei(bus),
1730            0xB8 => self.clv(bus),
1731            0xD8 => self.cld(bus),
1732            0xF8 => self.sed(bus),
1733            0xEA => self.nop(bus),
1734
1735            // Unofficial opcodes
1736            0xA7 | 0xB7 | 0xAF | 0xBF | 0xA3 | 0xB3 => self.lax(bus, addr_mode),
1737            0x87 | 0x97 | 0x8F | 0x83 => self.sax(bus, addr_mode),
1738            0xC7 | 0xD7 | 0xCF | 0xDF | 0xDB | 0xC3 | 0xD3 => self.dcp(bus, addr_mode),
1739            0xE7 | 0xF7 | 0xEF | 0xFF | 0xFB | 0xE3 | 0xF3 => self.isc(bus, addr_mode),
1740            0x07 | 0x17 | 0x0F | 0x1F | 0x1B | 0x03 | 0x13 => self.slo(bus, addr_mode),
1741            0x27 | 0x37 | 0x2F | 0x3F | 0x3B | 0x23 | 0x33 => self.rla(bus, addr_mode),
1742            0x47 | 0x57 | 0x4F | 0x5F | 0x5B | 0x43 | 0x53 => self.sre(bus, addr_mode),
1743            0x67 | 0x77 | 0x6F | 0x7F | 0x7B | 0x63 | 0x73 => self.rra(bus, addr_mode),
1744            0x0B | 0x2B => self.anc(bus),
1745            0x4B => self.alr(bus),
1746            0x6B => self.arr(bus),
1747            0x8B => self.xaa(bus),
1748            0xAB => self.lxa(bus),
1749            0xCB => self.axs(bus),
1750            0x93 | 0x9F => self.sha(bus, addr_mode),
1751            0x9C => self.shy(bus),
1752            0x9E => self.shx(bus),
1753            0x9B => self.tas(bus),
1754            0xBB => self.las(bus, addr_mode),
1755
1756            // Unofficial NOPs
1757            0x1A | 0x3A | 0x5A | 0x7A | 0xDA | 0xFA => self.nop(bus),
1758            0x80 | 0x82 | 0x89 | 0xC2 | 0xE2 => self.nop_read(bus, addr_mode),
1759            0x04 | 0x44 | 0x64 | 0x14 | 0x34 | 0x54 | 0x74 | 0xD4 | 0xF4 => {
1760                self.nop_read(bus, addr_mode)
1761            }
1762            0x0C | 0x1C | 0x3C | 0x5C | 0x7C | 0xDC | 0xFC => self.nop_read(bus, addr_mode),
1763
1764            // JAM/KIL opcodes - halt CPU
1765            0x02 | 0x12 | 0x22 | 0x32 | 0x42 | 0x52 | 0x62 | 0x72 | 0x92 | 0xB2 | 0xD2 | 0xF2 => {
1766                self.jam()
1767            }
1768        }
1769    }
1770
1771    /// Push byte to stack.
1772    pub(crate) fn push(&mut self, bus: &mut impl Bus, value: u8) {
1773        bus.write(0x0100 | u16::from(self.sp), value);
1774        self.sp = self.sp.wrapping_sub(1);
1775    }
1776
1777    /// Pop byte from stack.
1778    pub(crate) fn pop(&mut self, bus: &mut impl Bus) -> u8 {
1779        self.sp = self.sp.wrapping_add(1);
1780        bus.read(0x0100 | u16::from(self.sp))
1781    }
1782
1783    /// Push 16-bit value to stack (high byte first).
1784    pub(crate) fn push_u16(&mut self, bus: &mut impl Bus, value: u16) {
1785        self.push(bus, (value >> 8) as u8);
1786        self.push(bus, (value & 0xFF) as u8);
1787    }
1788
1789    /// Pop 16-bit value from stack (low byte first).
1790    pub(crate) fn pop_u16(&mut self, bus: &mut impl Bus) -> u16 {
1791        let lo = self.pop(bus);
1792        let hi = self.pop(bus);
1793        u16::from_le_bytes([lo, hi])
1794    }
1795
1796    /// Read operand based on addressing mode.
1797    pub(crate) fn read_operand(&mut self, bus: &mut impl Bus, mode: AddressingMode) -> (u8, bool) {
1798        let result = mode.resolve(self.pc, self.x, self.y, bus);
1799        self.pc = self.pc.wrapping_add(u16::from(mode.operand_bytes()));
1800
1801        // Perform dummy read for indexed addressing modes with page crossing
1802        // This matches hardware behavior where CPU reads from incorrect address
1803        // before applying the page boundary correction
1804        if result.page_crossed {
1805            match mode {
1806                AddressingMode::AbsoluteX
1807                | AddressingMode::AbsoluteY
1808                | AddressingMode::IndirectIndexedY => {
1809                    // Calculate the incorrect address (before page fix)
1810                    // Take high byte from base, low byte from final address
1811                    let incorrect_addr = (result.base_addr & 0xFF00) | (result.addr & 0x00FF);
1812                    let _ = bus.read(incorrect_addr);
1813                }
1814                _ => {}
1815            }
1816        }
1817
1818        let value = match mode {
1819            AddressingMode::Accumulator => self.a,
1820            _ => bus.read(result.addr),
1821        };
1822
1823        (value, result.page_crossed)
1824    }
1825
1826    /// Write to address from addressing mode.
1827    pub(crate) fn write_operand(&mut self, bus: &mut impl Bus, mode: AddressingMode, value: u8) {
1828        let result = mode.resolve(self.pc, self.x, self.y, bus);
1829        self.pc = self.pc.wrapping_add(u16::from(mode.operand_bytes()));
1830
1831        // Perform dummy write for indexed addressing modes
1832        // Write operations ALWAYS perform a dummy write (unconditional, not just on page crossing)
1833        match mode {
1834            AddressingMode::AbsoluteX
1835            | AddressingMode::AbsoluteY
1836            | AddressingMode::IndirectIndexedY => {
1837                // Calculate the incorrect address (before page fix)
1838                let incorrect_addr = (result.base_addr & 0xFF00) | (result.addr & 0x00FF);
1839                // Dummy write to incorrect address (this is what hardware does)
1840                bus.write(incorrect_addr, value);
1841            }
1842            _ => {}
1843        }
1844
1845        match mode {
1846            AddressingMode::Accumulator => self.a = value,
1847            _ => bus.write(result.addr, value),
1848        }
1849    }
1850
1851    /// Set Zero and Negative flags based on value.
1852    #[inline]
1853    pub(crate) fn set_zn(&mut self, value: u8) {
1854        self.status.set_zn(value);
1855    }
1856}
1857
1858impl Default for Cpu {
1859    fn default() -> Self {
1860        Self::new()
1861    }
1862}
1863
1864#[cfg(test)]
1865mod tests {
1866    use super::*;
1867
1868    struct TestBus {
1869        memory: [u8; 0x10000],
1870    }
1871
1872    impl TestBus {
1873        fn new() -> Self {
1874            Self {
1875                memory: [0; 0x10000],
1876            }
1877        }
1878    }
1879
1880    impl Bus for TestBus {
1881        fn read(&mut self, addr: u16) -> u8 {
1882            self.memory[addr as usize]
1883        }
1884
1885        fn write(&mut self, addr: u16, value: u8) {
1886            self.memory[addr as usize] = value;
1887        }
1888    }
1889
1890    #[test]
1891    fn test_cpu_new() {
1892        let cpu = Cpu::new();
1893        assert_eq!(cpu.a, 0);
1894        assert_eq!(cpu.x, 0);
1895        assert_eq!(cpu.y, 0);
1896        assert_eq!(cpu.sp, 0xFD);
1897        assert!(cpu.status.contains(StatusFlags::INTERRUPT_DISABLE));
1898    }
1899
1900    #[test]
1901    fn test_cpu_reset() {
1902        let mut cpu = Cpu::new();
1903        let mut bus = TestBus::new();
1904
1905        // Set RESET vector
1906        bus.write(0xFFFC, 0x00);
1907        bus.write(0xFFFD, 0x80);
1908
1909        cpu.reset(&mut bus);
1910
1911        assert_eq!(cpu.pc, 0x8000);
1912        assert!(cpu.status.contains(StatusFlags::INTERRUPT_DISABLE));
1913        assert_eq!(cpu.cycles, 7);
1914    }
1915
1916    #[test]
1917    fn test_stack_operations() {
1918        let mut cpu = Cpu::new();
1919        let mut bus = TestBus::new();
1920
1921        cpu.sp = 0xFF;
1922
1923        // Push byte
1924        cpu.push(&mut bus, 0x42);
1925        assert_eq!(cpu.sp, 0xFE);
1926        assert_eq!(bus.read(0x01FF), 0x42);
1927
1928        // Pop byte
1929        let value = cpu.pop(&mut bus);
1930        assert_eq!(value, 0x42);
1931        assert_eq!(cpu.sp, 0xFF);
1932
1933        // Push/pop u16
1934        cpu.push_u16(&mut bus, 0x1234);
1935        assert_eq!(cpu.sp, 0xFD);
1936        let value = cpu.pop_u16(&mut bus);
1937        assert_eq!(value, 0x1234);
1938        assert_eq!(cpu.sp, 0xFF);
1939    }
1940}