Skip to main content

rustysnes_cpu/
exec.rs

1//! Instruction fetch / decode / execute for the 65C816, plus bus plumbing.
2//!
3//! Behavior is modelled on the bsnes / ares `wdc65816` reference cores (study-only; this is a
4//! clean-room Rust implementation). The decimal-mode `ADC`/`SBC` digit-wise correction
5//! follows bsnes `algorithms.cpp` exactly. Cycle counts use the standard 65C816 timing tables
6//! with the variable adjustments documented in `docs/cpu.md`:
7//!
8//! - `+1` if `M = 0` (16-bit memory / accumulator access),
9//! - `+1` if the low byte of `D` is non-zero (direct-page modes),
10//! - `+1` on an indexed page-cross,
11//! - `+1` branch taken, `+1` more on emulation-mode page-cross.
12
13// Truncating / sign-changing casts between integer widths are the core of a bit-accurate CPU
14// model (e.g. taking the low byte of a 16-bit register, or folding a wider ALU result back to
15// the operand width). They are deliberate and ubiquitous here; flagging each one would bury
16// real issues, so the cast-precision family is allowed for this module only.
17#![allow(
18    clippy::cast_possible_truncation,
19    clippy::cast_sign_loss,
20    clippy::cast_possible_wrap
21)]
22// `missing_const_for_fn` (nursery) fires on the many small `&mut self` opcode bodies that
23// only mutate the register file. Marking each `const` adds no real benefit (they are never
24// evaluated at compile time) and is brittle — one non-const callee revokes the whole chain.
25// We leave them non-const so the opcode bodies stay uniform and edits don't ripple.
26#![allow(clippy::missing_const_for_fn)]
27
28use crate::addr::{Effective, Mode};
29use crate::regs::Status;
30use crate::{Bus, Cpu, vectors};
31
32impl Cpu {
33    // ----------------------------------------------------------------------------------
34    // Bus plumbing. Every byte access ticks `on_cpu_cycle` and bumps the cycle counters.
35    // ----------------------------------------------------------------------------------
36
37    /// Read a byte at a 24-bit address, charging one CPU cycle. ares `CPU::read`: advance the
38    /// clock to four master clocks before the cycle end, latch the byte there, then advance the
39    /// final four — so the read lands at the hardware-exact dot.
40    pub(crate) fn bus_read8(&mut self, bus: &mut impl Bus, addr: u32) -> u8 {
41        let addr = addr & 0x00FF_FFFF;
42        let speed = bus.access_cycles(addr);
43        bus.advance(speed - 4);
44        let v = bus.read24(addr);
45        bus.advance(4);
46        self.cycles += 1;
47        self.cyc += 1;
48        v
49    }
50
51    /// Write a byte at a 24-bit address, charging one CPU cycle. ares `CPU::write`: advance the
52    /// whole cycle FIRST, then perform the write — the store lands at the END of its cycle, so a
53    /// register write becomes visible to the PPU/HDMA one cycle later than an equivalent read.
54    pub(crate) fn bus_write8(&mut self, bus: &mut impl Bus, addr: u32, val: u8) {
55        let addr = addr & 0x00FF_FFFF;
56        let speed = bus.access_cycles(addr);
57        bus.advance(speed);
58        bus.write24(addr, val);
59        self.cycles += 1;
60        self.cyc += 1;
61    }
62
63    /// Internal (no-bus) cycle, e.g. ALU/indexing dead cycles. Charges one CPU cycle (ares
64    /// `CPU::idle`: a flat six-clock step with no memory access).
65    pub(crate) fn io(&mut self, bus: &mut impl Bus) {
66        bus.advance(6);
67        self.cycles += 1;
68        self.cyc += 1;
69    }
70
71    /// Fetch the next program byte from `PBR:PC`, incrementing `PC` with 16-bit bank wrap.
72    pub(crate) fn fetch8(&mut self, bus: &mut impl Bus) -> u8 {
73        let addr = (u32::from(self.regs.pbr) << 16) | u32::from(self.regs.pc);
74        let v = self.bus_read8(bus, addr);
75        self.regs.pc = self.regs.pc.wrapping_add(1);
76        v
77    }
78
79    /// Fetch a little-endian 16-bit program operand.
80    pub(crate) fn fetch16(&mut self, bus: &mut impl Bus) -> u16 {
81        let lo = self.fetch8(bus);
82        let hi = self.fetch8(bus);
83        u16::from(lo) | (u16::from(hi) << 8)
84    }
85
86    /// Fetch a little-endian 24-bit program operand.
87    pub(crate) fn fetch24(&mut self, bus: &mut impl Bus) -> u32 {
88        let lo = self.fetch8(bus);
89        let hi = self.fetch8(bus);
90        let bank = self.fetch8(bus);
91        u32::from(lo) | (u32::from(hi) << 8) | (u32::from(bank) << 16)
92    }
93
94    /// Read a little-endian 16-bit value from `addr`/`addr+1` (low-byte address wraps within
95    /// the same bank, matching hardware for pointer fetches that are kept in-bank).
96    fn read16(&mut self, bus: &mut impl Bus, addr: u32) -> u16 {
97        let lo = self.bus_read8(bus, addr);
98        let hi = self.bus_read8(bus, addr.wrapping_add(1));
99        u16::from(lo) | (u16::from(hi) << 8)
100    }
101
102    // ----------------------------------------------------------------------------------
103    // Stack. Native mode uses the full 16-bit S; emulation mode confines S to page $01.
104    // ----------------------------------------------------------------------------------
105
106    /// Push a byte to the stack honoring emulation-mode page-1 wrapping.
107    fn push8(&mut self, bus: &mut impl Bus, val: u8) {
108        let addr = u32::from(self.regs.s);
109        self.bus_write8(bus, addr, val);
110        if self.regs.emulation {
111            self.regs.s = 0x0100 | u16::from((self.regs.s as u8).wrapping_sub(1));
112        } else {
113            self.regs.s = self.regs.s.wrapping_sub(1);
114        }
115    }
116
117    /// Pull a byte from the stack honoring emulation-mode page-1 wrapping.
118    fn pull8(&mut self, bus: &mut impl Bus) -> u8 {
119        if self.regs.emulation {
120            self.regs.s = 0x0100 | u16::from((self.regs.s as u8).wrapping_add(1));
121        } else {
122            self.regs.s = self.regs.s.wrapping_add(1);
123        }
124        let addr = u32::from(self.regs.s);
125        self.bus_read8(bus, addr)
126    }
127
128    /// Push a 16-bit value (high byte first, so it pulls back low-then-high).
129    fn push16(&mut self, bus: &mut impl Bus, val: u16) {
130        self.push8(bus, (val >> 8) as u8);
131        self.push8(bus, val as u8);
132    }
133
134    /// Pull a 16-bit value (low byte first).
135    fn pull16(&mut self, bus: &mut impl Bus) -> u16 {
136        let lo = self.pull8(bus);
137        let hi = self.pull8(bus);
138        u16::from(lo) | (u16::from(hi) << 8)
139    }
140
141    /// Push a byte with a *full 16-bit* `S` decrement (bsnes `pushN`), used by the "new" 65816
142    /// stack ops (`PEA`/`PEI`/`PER`/`PHD`). Unlike [`Self::push8`], the intermediate `S` is
143    /// **not** confined to page `$01`; emulation-mode confinement is re-applied at the
144    /// instruction boundary by [`Self::normalize_emulation`].
145    fn push_n8(&mut self, bus: &mut impl Bus, val: u8) {
146        let addr = u32::from(self.regs.s);
147        self.bus_write8(bus, addr, val);
148        self.regs.s = self.regs.s.wrapping_sub(1);
149    }
150
151    /// Pull a byte with a *full 16-bit* `S` increment (bsnes `pullN`), used by `PLD`/`PLB`.
152    fn pull_n8(&mut self, bus: &mut impl Bus) -> u8 {
153        self.regs.s = self.regs.s.wrapping_add(1);
154        let addr = u32::from(self.regs.s);
155        self.bus_read8(bus, addr)
156    }
157
158    /// Push a 16-bit value with full 16-bit `S` decrement (high byte first).
159    fn push_n16(&mut self, bus: &mut impl Bus, val: u16) {
160        self.push_n8(bus, (val >> 8) as u8);
161        self.push_n8(bus, val as u8);
162    }
163
164    /// Pull a 16-bit value with full 16-bit `S` increment (low byte first).
165    fn pull_n16(&mut self, bus: &mut impl Bus) -> u16 {
166        let lo = self.pull_n8(bus);
167        let hi = self.pull_n8(bus);
168        u16::from(lo) | (u16::from(hi) << 8)
169    }
170
171    // ----------------------------------------------------------------------------------
172    // Direct-page penalty: +1 cycle when D's low byte is non-zero.
173    // ----------------------------------------------------------------------------------
174
175    fn dp_penalty(&mut self, bus: &mut impl Bus) {
176        if self.regs.d & 0x00FF != 0 {
177            self.io(bus);
178        }
179    }
180
181    // ----------------------------------------------------------------------------------
182    // Direct-page address arithmetic (clean-room port of bsnes `memory.cpp`).
183    //
184    // `readDirect`  : EF && DL==0 → (D & 0xFF00) | (addr & 0xFF)  [page-locked low byte]
185    //                 else        → (D + addr) & 0xFFFF
186    // `readDirectN` : always (D + addr) & 0xFFFF  (long-indirect pointer fetch, no page-lock)
187    //
188    // NOTE on (dp,X): bsnes additionally models an emulation-mode `DL!=0` high-byte wrap in
189    // `readDirectX`. The SingleStepTests reference does NOT reproduce that wrap (it reads the
190    // pointer high byte linearly), so we follow the oracle and apply only the `DL==0`
191    // page-lock here — see [`Self::direct_x_addr`].
192    // ----------------------------------------------------------------------------------
193
194    /// Effective bank-`0` address of a direct-page byte at `D + addr`, applying the
195    /// emulation-mode page-lock (bsnes `readDirect`). `addr` is the running offset including
196    /// any pre-added index.
197    fn direct_addr(&self, addr: u16) -> u32 {
198        if self.regs.emulation && self.regs.d.trailing_zeros() >= 8 {
199            u32::from((self.regs.d & 0xFF00) | (addr & 0x00FF))
200        } else {
201            u32::from(self.regs.d.wrapping_add(addr))
202        }
203    }
204
205    /// Effective bank-`0` address for the `(dp,X)` pointer-byte fetch. `addr` already includes
206    /// the `+X` from the caller; `offset` is the byte index (`0`/`1`). Applies the `DL==0`
207    /// page-lock only (the oracle does not model the bsnes `DL!=0` high-byte wrap).
208    fn direct_x_addr(&self, addr: u16, offset: u16) -> u32 {
209        self.direct_addr(addr.wrapping_add(offset))
210    }
211
212    /// Read a 16-bit direct-page pointer via per-byte [`Self::direct_addr`] (page-locked).
213    fn read_dp_ptr16(&mut self, bus: &mut impl Bus, dp: u16) -> u16 {
214        let a0 = self.direct_addr(dp);
215        let a1 = self.direct_addr(dp.wrapping_add(1));
216        let lo = self.bus_read8(bus, a0);
217        let hi = self.bus_read8(bus, a1);
218        u16::from(lo) | (u16::from(hi) << 8)
219    }
220
221    /// Read a 16-bit pointer via per-byte [`Self::direct_x_addr`] for `(dp,X)`. Matches bsnes
222    /// `readDirectX(U.l + X.w, 0)` / `(…, 1)`: the index is folded into the address, and the
223    /// per-byte offset is `0`/`1`.
224    fn read_dp_x_ptr16(&mut self, bus: &mut impl Bus, dp: u16, x: u16) -> u16 {
225        let base = dp.wrapping_add(x);
226        let a0 = self.direct_x_addr(base, 0);
227        let a1 = self.direct_x_addr(base, 1);
228        let lo = self.bus_read8(bus, a0);
229        let hi = self.bus_read8(bus, a1);
230        u16::from(lo) | (u16::from(hi) << 8)
231    }
232
233    /// Read a 24-bit long pointer via [`Self::direct_n_addr`] (no page-lock).
234    fn read_dp_ptr24(&mut self, bus: &mut impl Bus, dp: u16) -> u32 {
235        let lo = self.bus_read8(bus, self.direct_n_addr(dp));
236        let mid = self.bus_read8(bus, self.direct_n_addr(dp.wrapping_add(1)));
237        let hi = self.bus_read8(bus, self.direct_n_addr(dp.wrapping_add(2)));
238        u32::from(lo) | (u32::from(mid) << 8) | (u32::from(hi) << 16)
239    }
240
241    /// Long-indirect pointer-byte address: always `(D + addr) & 0xFFFF` (bsnes `readDirectN`).
242    fn direct_n_addr(&self, addr: u16) -> u32 {
243        u32::from(self.regs.d.wrapping_add(addr))
244    }
245
246    // ----------------------------------------------------------------------------------
247    // Addressing-mode resolution. Fetches the operand bytes and returns the effective addr.
248    // ----------------------------------------------------------------------------------
249
250    /// Resolve an addressing mode to an effective 24-bit address, fetching operand bytes and
251    /// charging the direct-page penalty where applicable. Immediate modes are handled by the
252    /// caller (they have no address); calling this with an immediate mode is a logic error and
253    /// resolves to the current `PC` defensively.
254    #[allow(clippy::too_many_lines)] // one arm per addressing mode; splitting would obscure it
255    fn resolve(&mut self, bus: &mut impl Bus, mode: Mode) -> Effective {
256        match mode {
257            Mode::ImmediateM | Mode::ImmediateX => Effective {
258                addr: (u32::from(self.regs.pbr) << 16) | u32::from(self.regs.pc),
259                page_cross: false,
260                bank0_wrap: false,
261            },
262            Mode::Direct => {
263                self.dp_penalty(bus);
264                let dp = u16::from(self.fetch8(bus));
265                Effective {
266                    addr: self.direct_addr(dp),
267                    page_cross: false,
268                    bank0_wrap: true,
269                }
270            }
271            Mode::DirectX => {
272                self.dp_penalty(bus);
273                let dp = u16::from(self.fetch8(bus));
274                self.io(bus);
275                let off = dp.wrapping_add(self.regs.x_val());
276                Effective {
277                    addr: self.direct_addr(off),
278                    page_cross: false,
279                    bank0_wrap: true,
280                }
281            }
282            Mode::DirectY => {
283                self.dp_penalty(bus);
284                let dp = u16::from(self.fetch8(bus));
285                self.io(bus);
286                let off = dp.wrapping_add(self.regs.y_val());
287                Effective {
288                    addr: self.direct_addr(off),
289                    page_cross: false,
290                    bank0_wrap: true,
291                }
292            }
293            Mode::DirectIndirect => {
294                self.dp_penalty(bus);
295                let dp = u16::from(self.fetch8(bus));
296                let base = self.read_dp_ptr16(bus, dp);
297                let addr = (u32::from(self.regs.dbr) << 16) | u32::from(base);
298                Effective {
299                    addr,
300                    page_cross: false,
301                    bank0_wrap: false,
302                }
303            }
304            Mode::DirectXIndirect => {
305                self.dp_penalty(bus);
306                let dp = u16::from(self.fetch8(bus));
307                self.io(bus);
308                let base = self.read_dp_x_ptr16(bus, dp, self.regs.x_val());
309                let addr = (u32::from(self.regs.dbr) << 16) | u32::from(base);
310                Effective {
311                    addr,
312                    page_cross: false,
313                    bank0_wrap: false,
314                }
315            }
316            Mode::DirectIndirectY => {
317                self.dp_penalty(bus);
318                let dp = u16::from(self.fetch8(bus));
319                let base = self.read_dp_ptr16(bus, dp);
320                let base24 = (u32::from(self.regs.dbr) << 16) | u32::from(base);
321                let addr = base24.wrapping_add(u32::from(self.regs.y_val())) & 0x00FF_FFFF;
322                let page_cross = (base24 & 0xFF00) != (addr & 0xFF00);
323                Effective {
324                    addr,
325                    page_cross,
326                    bank0_wrap: false,
327                }
328            }
329            Mode::DirectIndirectLong => {
330                self.dp_penalty(bus);
331                let dp = u16::from(self.fetch8(bus));
332                let addr = self.read_dp_ptr24(bus, dp);
333                Effective {
334                    addr,
335                    page_cross: false,
336                    bank0_wrap: false,
337                }
338            }
339            Mode::DirectIndirectLongY => {
340                self.dp_penalty(bus);
341                let dp = u16::from(self.fetch8(bus));
342                let base = self.read_dp_ptr24(bus, dp);
343                let addr = base.wrapping_add(u32::from(self.regs.y_val())) & 0x00FF_FFFF;
344                Effective {
345                    addr,
346                    page_cross: false,
347                    bank0_wrap: false,
348                }
349            }
350            Mode::Absolute => {
351                let off = self.fetch16(bus);
352                let addr = (u32::from(self.regs.dbr) << 16) | u32::from(off);
353                Effective {
354                    addr,
355                    page_cross: false,
356                    bank0_wrap: false,
357                }
358            }
359            Mode::AbsoluteX => {
360                let off = self.fetch16(bus);
361                let base = (u32::from(self.regs.dbr) << 16) | u32::from(off);
362                let addr = base.wrapping_add(u32::from(self.regs.x_val())) & 0x00FF_FFFF;
363                let page_cross = (base & 0xFF00) != (addr & 0xFF00);
364                Effective {
365                    addr,
366                    page_cross,
367                    bank0_wrap: false,
368                }
369            }
370            Mode::AbsoluteY => {
371                let off = self.fetch16(bus);
372                let base = (u32::from(self.regs.dbr) << 16) | u32::from(off);
373                let addr = base.wrapping_add(u32::from(self.regs.y_val())) & 0x00FF_FFFF;
374                let page_cross = (base & 0xFF00) != (addr & 0xFF00);
375                Effective {
376                    addr,
377                    page_cross,
378                    bank0_wrap: false,
379                }
380            }
381            Mode::AbsoluteLong => {
382                let addr = self.fetch24(bus);
383                Effective {
384                    addr,
385                    page_cross: false,
386                    bank0_wrap: false,
387                }
388            }
389            Mode::AbsoluteLongX => {
390                let base = self.fetch24(bus);
391                let addr = base.wrapping_add(u32::from(self.regs.x_val())) & 0x00FF_FFFF;
392                Effective {
393                    addr,
394                    page_cross: false,
395                    bank0_wrap: false,
396                }
397            }
398            Mode::StackRelative => {
399                let sr = u16::from(self.fetch8(bus));
400                self.io(bus);
401                let ea = self.regs.s.wrapping_add(sr);
402                Effective {
403                    addr: u32::from(ea),
404                    page_cross: false,
405                    bank0_wrap: true,
406                }
407            }
408            Mode::StackRelativeIndirectY => {
409                let sr = u16::from(self.fetch8(bus));
410                self.io(bus);
411                let ptr = self.regs.s.wrapping_add(sr);
412                let lo = self.bus_read8(bus, u32::from(ptr));
413                let hi = self.bus_read8(bus, u32::from(ptr.wrapping_add(1)));
414                let base = u16::from(lo) | (u16::from(hi) << 8);
415                self.io(bus);
416                let base24 = (u32::from(self.regs.dbr) << 16) | u32::from(base);
417                let addr = base24.wrapping_add(u32::from(self.regs.y_val())) & 0x00FF_FFFF;
418                Effective {
419                    addr,
420                    page_cross: false,
421                    bank0_wrap: false,
422                }
423            }
424        }
425    }
426
427    /// Compute the address of operand byte `n` given a resolved effective address, honoring
428    /// bank-`0` wrap for direct-page / stack modes (the high byte wraps within `$0000..=$FFFF`
429    /// rather than carrying into the next bank).
430    fn operand_byte_addr(e: Effective, n: u32) -> u32 {
431        if e.bank0_wrap {
432            u32::from((e.addr as u16).wrapping_add(n as u16))
433        } else {
434            e.addr.wrapping_add(n) & 0x00FF_FFFF
435        }
436    }
437
438    // ----------------------------------------------------------------------------------
439    // Width-aware operand read/write at an effective address.
440    // ----------------------------------------------------------------------------------
441
442    /// Read an accumulator-width operand (8 or 16 bits per `M`), honoring bank-`0` high-byte
443    /// wrap for direct-page / stack operands.
444    fn read_m(&mut self, bus: &mut impl Bus, e: Effective) -> u16 {
445        if self.regs.m8() {
446            u16::from(self.bus_read8(bus, e.addr))
447        } else {
448            let lo = self.bus_read8(bus, Self::operand_byte_addr(e, 0));
449            let hi = self.bus_read8(bus, Self::operand_byte_addr(e, 1));
450            u16::from(lo) | (u16::from(hi) << 8)
451        }
452    }
453
454    /// Read an index-width operand (8 or 16 bits per `X`).
455    fn read_x(&mut self, bus: &mut impl Bus, e: Effective) -> u16 {
456        if self.regs.x8() {
457            u16::from(self.bus_read8(bus, e.addr))
458        } else {
459            let lo = self.bus_read8(bus, Self::operand_byte_addr(e, 0));
460            let hi = self.bus_read8(bus, Self::operand_byte_addr(e, 1));
461            u16::from(lo) | (u16::from(hi) << 8)
462        }
463    }
464
465    /// Write an accumulator-width operand (low byte first; high byte only when `M=0`).
466    fn write_m(&mut self, bus: &mut impl Bus, e: Effective, val: u16) {
467        self.bus_write8(bus, e.addr, val as u8);
468        if !self.regs.m8() {
469            self.bus_write8(bus, Self::operand_byte_addr(e, 1), (val >> 8) as u8);
470        }
471    }
472
473    /// Write an index-width operand.
474    fn write_x(&mut self, bus: &mut impl Bus, e: Effective, val: u16) {
475        self.bus_write8(bus, e.addr, val as u8);
476        if !self.regs.x8() {
477            self.bus_write8(bus, Self::operand_byte_addr(e, 1), (val >> 8) as u8);
478        }
479    }
480
481    /// Immediate accumulator-width operand fetched from the program stream.
482    fn imm_m(&mut self, bus: &mut impl Bus) -> u16 {
483        if self.regs.m8() {
484            u16::from(self.fetch8(bus))
485        } else {
486            self.fetch16(bus)
487        }
488    }
489
490    /// Immediate index-width operand fetched from the program stream.
491    fn imm_x(&mut self, bus: &mut impl Bus) -> u16 {
492        if self.regs.x8() {
493            u16::from(self.fetch8(bus))
494        } else {
495            self.fetch16(bus)
496        }
497    }
498
499    // ----------------------------------------------------------------------------------
500    // ALU primitives (clean-room ports of bsnes `algorithms.cpp` behavior).
501    // ----------------------------------------------------------------------------------
502
503    /// `ADC` honoring the `D` (decimal) flag, at the current accumulator width.
504    fn adc(&mut self, data: u16) {
505        if self.regs.m8() {
506            let a = self.regs.a & 0x00FF;
507            let d = data & 0x00FF;
508            let c = u16::from(self.regs.p.contains(Status::C));
509            let mut result: i32 = if self.regs.p.contains(Status::D) {
510                let mut r = i32::from(a & 0x0F) + i32::from(d & 0x0F) + i32::from(c);
511                if r > 0x09 {
512                    r += 0x06;
513                }
514                let carry = i32::from(r > 0x0F);
515                i32::from(a & 0xF0) + i32::from(d & 0xF0) + (carry << 4) + (r & 0x0F)
516            } else {
517                i32::from(a) + i32::from(d) + i32::from(c)
518            };
519            let overflow = (!(a ^ d) & (a ^ (result as u16)) & 0x80) != 0;
520            self.regs.set_flag(Status::V, overflow);
521            if self.regs.p.contains(Status::D) && result > 0x9F {
522                result += 0x60;
523            }
524            self.regs.set_flag(Status::C, result > 0xFF);
525            self.regs.set_flag(Status::Z, (result as u8) == 0);
526            self.regs.set_flag(Status::N, result & 0x80 != 0);
527            self.regs.a = (self.regs.a & 0xFF00) | u16::from(result as u8);
528        } else {
529            let a = self.regs.a;
530            let d = data;
531            let c = u32::from(self.regs.p.contains(Status::C));
532            let mut result: i64 = if self.regs.p.contains(Status::D) {
533                let mut r = i64::from(a & 0x000F) + i64::from(d & 0x000F) + i64::from(c);
534                if r > 0x0009 {
535                    r += 0x0006;
536                }
537                let mut carry = i64::from(r > 0x000F);
538                r = i64::from(a & 0x00F0) + i64::from(d & 0x00F0) + (carry << 4) + (r & 0x000F);
539                if r > 0x009F {
540                    r += 0x0060;
541                }
542                carry = i64::from(r > 0x00FF);
543                r = i64::from(a & 0x0F00) + i64::from(d & 0x0F00) + (carry << 8) + (r & 0x00FF);
544                if r > 0x09FF {
545                    r += 0x0600;
546                }
547                carry = i64::from(r > 0x0FFF);
548                i64::from(a & 0xF000) + i64::from(d & 0xF000) + (carry << 12) + (r & 0x0FFF)
549            } else {
550                i64::from(a) + i64::from(d) + i64::from(c)
551            };
552            let overflow = (!(a ^ d) & (a ^ (result as u16)) & 0x8000) != 0;
553            self.regs.set_flag(Status::V, overflow);
554            if self.regs.p.contains(Status::D) && result > 0x9FFF {
555                result += 0x6000;
556            }
557            self.regs.set_flag(Status::C, result > 0xFFFF);
558            self.regs.set_flag(Status::Z, (result as u16) == 0);
559            self.regs.set_flag(Status::N, result & 0x8000 != 0);
560            self.regs.a = result as u16;
561        }
562    }
563
564    /// `SBC` honoring the `D` flag, at the current accumulator width.
565    fn sbc(&mut self, data: u16) {
566        if self.regs.m8() {
567            let a = self.regs.a & 0x00FF;
568            let d = (!data) & 0x00FF;
569            let c = u16::from(self.regs.p.contains(Status::C));
570            let mut result: i32 = if self.regs.p.contains(Status::D) {
571                let mut r = i32::from(a & 0x0F) + i32::from(d & 0x0F) + i32::from(c);
572                if r <= 0x0F {
573                    r -= 0x06;
574                }
575                let carry = i32::from(r > 0x0F);
576                i32::from(a & 0xF0) + i32::from(d & 0xF0) + (carry << 4) + (r & 0x0F)
577            } else {
578                i32::from(a) + i32::from(d) + i32::from(c)
579            };
580            let overflow = (!(a ^ d) & (a ^ (result as u16)) & 0x80) != 0;
581            self.regs.set_flag(Status::V, overflow);
582            if self.regs.p.contains(Status::D) && result <= 0xFF {
583                result -= 0x60;
584            }
585            self.regs.set_flag(Status::C, result > 0xFF);
586            self.regs.set_flag(Status::Z, (result as u8) == 0);
587            self.regs.set_flag(Status::N, result & 0x80 != 0);
588            self.regs.a = (self.regs.a & 0xFF00) | u16::from(result as u8);
589        } else {
590            let a = self.regs.a;
591            let d = !data;
592            let c = u32::from(self.regs.p.contains(Status::C));
593            let mut result: i64 = if self.regs.p.contains(Status::D) {
594                let mut r = i64::from(a & 0x000F) + i64::from(d & 0x000F) + i64::from(c);
595                if r <= 0x000F {
596                    r -= 0x0006;
597                }
598                let mut carry = i64::from(r > 0x000F);
599                r = i64::from(a & 0x00F0) + i64::from(d & 0x00F0) + (carry << 4) + (r & 0x000F);
600                if r <= 0x00FF {
601                    r -= 0x0060;
602                }
603                carry = i64::from(r > 0x00FF);
604                r = i64::from(a & 0x0F00) + i64::from(d & 0x0F00) + (carry << 8) + (r & 0x00FF);
605                if r <= 0x0FFF {
606                    r -= 0x0600;
607                }
608                carry = i64::from(r > 0x0FFF);
609                i64::from(a & 0xF000) + i64::from(d & 0xF000) + (carry << 12) + (r & 0x0FFF)
610            } else {
611                i64::from(a) + i64::from(d) + i64::from(c)
612            };
613            let overflow = (!(a ^ d) & (a ^ (result as u16)) & 0x8000) != 0;
614            self.regs.set_flag(Status::V, overflow);
615            if self.regs.p.contains(Status::D) && result <= 0xFFFF {
616                result -= 0x6000;
617            }
618            self.regs.set_flag(Status::C, result > 0xFFFF);
619            self.regs.set_flag(Status::Z, (result as u16) == 0);
620            self.regs.set_flag(Status::N, result & 0x8000 != 0);
621            self.regs.a = result as u16;
622        }
623    }
624
625    /// Compare a register value against memory data at the active width, setting `N/Z/C`.
626    fn compare(&mut self, reg: u16, data: u16, width8: bool) {
627        if width8 {
628            let result = i32::from(reg & 0xFF) - i32::from(data & 0xFF);
629            self.regs.set_flag(Status::C, result >= 0);
630            self.regs.set_flag(Status::Z, (result as u8) == 0);
631            self.regs.set_flag(Status::N, result & 0x80 != 0);
632        } else {
633            let result = i32::from(reg) - i32::from(data);
634            self.regs.set_flag(Status::C, result >= 0);
635            self.regs.set_flag(Status::Z, (result as u16) == 0);
636            self.regs.set_flag(Status::N, result & 0x8000 != 0);
637        }
638    }
639
640    // ----------------------------------------------------------------------------------
641    // Top-level step + decode.
642    // ----------------------------------------------------------------------------------
643
644    /// Execute one 65C816 instruction against `bus`, returning the **CPU cycles** consumed
645    /// (see the crate docs for the unit). Polls NMI then IRQ at the instruction boundary
646    /// before fetching; IRQ is honored only when the `I` flag is clear. If the `STP` latch is
647    /// set, the CPU stays halted (one idle cycle returned) until [`Cpu::reset`].
648    pub fn step(&mut self, bus: &mut impl Bus) -> u32 {
649        self.cyc = 0;
650        // Hardware holds these invariants continuously in emulation mode, so assert them up
651        // front too — instructions that push to the stack must see `S.h = $01` before they run.
652        self.normalize_emulation();
653
654        if self.stopped {
655            self.io(bus);
656            return self.cyc;
657        }
658
659        let nmi = bus.poll_nmi();
660        let irq = bus.poll_irq();
661
662        if nmi {
663            self.waiting = false;
664            self.service_interrupt(bus, false, true);
665        } else if irq && !self.regs.p.contains(Status::I) {
666            self.waiting = false;
667            self.service_interrupt(bus, false, false);
668        } else if self.waiting {
669            // WAI exits on ANY asserted interrupt line (NMI/IRQ/ABORT/RESET) regardless of the
670            // `I` flag (WDC 65816 datasheet) — `I` only gates whether the CPU *vectors* to the
671            // handler, not whether WAI wakes. A masked IRQ (I=1) here must still clear `waiting`
672            // so execution resumes at the instruction after WAI; otherwise a program using
673            // `SEI; WAI` as a cheap interrupt-line sync primitive (no vectoring wanted) hangs
674            // forever once an IRQ source it never unmasks fires.
675            self.waiting = !irq;
676            self.io(bus);
677        } else {
678            let opcode = self.fetch8(bus);
679            self.execute(bus, opcode);
680        }
681
682        self.normalize_emulation();
683        self.cyc
684    }
685
686    /// Re-assert the emulation-mode hardware invariants that hold at every instruction
687    /// boundary when `E=1`: the stack pointer high byte is forced to `$01`, the `M`/`X` status
688    /// bits read back as set, and the index-register high bytes are zero. The 65816 maintains
689    /// these continuously in 6502-emulation mode (matching bsnes, which forces `S.h=0x01`,
690    /// `XF=MF=1`, and `X.h=Y.h=0` on every relevant op), so the per-opcode oracle expects them
691    /// regardless of which instruction ran.
692    fn normalize_emulation(&mut self) {
693        if self.regs.emulation {
694            self.regs.s = 0x0100 | (self.regs.s & 0x00FF);
695            self.regs.set_flag(Status::M, true);
696            self.regs.set_flag(Status::X, true);
697            self.regs.x &= 0x00FF;
698            self.regs.y &= 0x00FF;
699        }
700    }
701
702    /// Service a hardware/software interrupt (NMI/IRQ/BRK/COP). Pushes the return frame and
703    /// loads `PC` from the appropriate emulation/native vector, then sets `I` and clears `D`.
704    fn service_interrupt(&mut self, bus: &mut impl Bus, software: bool, nmi: bool) {
705        // The 65C816 hardware interrupt sequence opens with TWO internal (dead) cycles before it
706        // begins pushing the return frame (WDC datasheet: IRQ/NMI = 2 internal + push PBR[native]
707        // + push PCH + push PCL + push P + 2 vector fetches). This path is hardware IRQ/NMI ONLY —
708        // BRK/COP are handled by `op_brk`/`op_cop` and are cycle-validated by the single-step
709        // oracle, so widening this to the correct 2 cycles does not touch their counts. The extra
710        // cycle also matters for interrupt *latency*: it pushes an IRQ-driven register write ~1
711        // dot later, which is one input to the `hdmaen_latch_test` HDMAEN-vs-latch race.
712        self.io(bus);
713        self.io(bus);
714        if !self.regs.emulation {
715            self.push8(bus, self.regs.pbr);
716        }
717        self.push16(bus, self.regs.pc);
718        // For emulation mode the B flag (== X bit position) distinguishes BRK from IRQ.
719        let mut status = self.regs.p.bits();
720        if self.regs.emulation && !software {
721            status &= !Status::X.bits(); // B clear for hardware IRQ
722        }
723        self.push8(bus, status);
724        self.regs.set_flag(Status::I, true);
725        self.regs.set_flag(Status::D, false);
726        let vector = self.interrupt_vector(software, nmi);
727        let pc = self.read16(bus, vector);
728        self.regs.pc = pc;
729        self.regs.pbr = 0;
730    }
731
732    /// Select the interrupt vector for the current mode.
733    const fn interrupt_vector(&self, software: bool, nmi: bool) -> u32 {
734        if nmi {
735            if self.regs.emulation {
736                vectors::NMI_EMU
737            } else {
738                vectors::NMI_NATIVE
739            }
740        } else if software {
741            // BRK / IRQ share a vector in emulation mode; native splits them — handled by the
742            // caller for COP. Here `software` covers BRK and IRQ.
743            if self.regs.emulation {
744                vectors::IRQ_BRK_EMU
745            } else {
746                vectors::IRQ_NATIVE
747            }
748        } else if self.regs.emulation {
749            vectors::IRQ_BRK_EMU
750        } else {
751            vectors::IRQ_NATIVE
752        }
753    }
754
755    /// Decode and execute a single opcode. Every one of the 256 opcodes is handled.
756    #[allow(clippy::too_many_lines)]
757    fn execute(&mut self, bus: &mut impl Bus, opcode: u8) {
758        match opcode {
759            // ---- ORA ----
760            0x09 => {
761                let v = self.imm_m(bus);
762                self.op_ora(v);
763            }
764            0x05 => {
765                let e = self.resolve(bus, Mode::Direct);
766                let v = self.read_m(bus, e);
767                self.op_ora(v);
768            }
769            0x15 => {
770                let e = self.resolve(bus, Mode::DirectX);
771                let v = self.read_m(bus, e);
772                self.op_ora(v);
773            }
774            0x0D => {
775                let e = self.resolve(bus, Mode::Absolute);
776                let v = self.read_m(bus, e);
777                self.op_ora(v);
778            }
779            0x1D => {
780                let e = self.resolve(bus, Mode::AbsoluteX);
781                self.idx_penalty(bus, e);
782                let v = self.read_m(bus, e);
783                self.op_ora(v);
784            }
785            0x19 => {
786                let e = self.resolve(bus, Mode::AbsoluteY);
787                self.idx_penalty(bus, e);
788                let v = self.read_m(bus, e);
789                self.op_ora(v);
790            }
791            0x01 => {
792                let e = self.resolve(bus, Mode::DirectXIndirect);
793                let v = self.read_m(bus, e);
794                self.op_ora(v);
795            }
796            0x11 => {
797                let e = self.resolve(bus, Mode::DirectIndirectY);
798                self.idx_penalty(bus, e);
799                let v = self.read_m(bus, e);
800                self.op_ora(v);
801            }
802            0x12 => {
803                let e = self.resolve(bus, Mode::DirectIndirect);
804                let v = self.read_m(bus, e);
805                self.op_ora(v);
806            }
807            0x07 => {
808                let e = self.resolve(bus, Mode::DirectIndirectLong);
809                let v = self.read_m(bus, e);
810                self.op_ora(v);
811            }
812            0x17 => {
813                let e = self.resolve(bus, Mode::DirectIndirectLongY);
814                let v = self.read_m(bus, e);
815                self.op_ora(v);
816            }
817            0x0F => {
818                let e = self.resolve(bus, Mode::AbsoluteLong);
819                let v = self.read_m(bus, e);
820                self.op_ora(v);
821            }
822            0x1F => {
823                let e = self.resolve(bus, Mode::AbsoluteLongX);
824                let v = self.read_m(bus, e);
825                self.op_ora(v);
826            }
827            0x03 => {
828                let e = self.resolve(bus, Mode::StackRelative);
829                let v = self.read_m(bus, e);
830                self.op_ora(v);
831            }
832            0x13 => {
833                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
834                let v = self.read_m(bus, e);
835                self.op_ora(v);
836            }
837
838            // ---- AND ----
839            0x29 => {
840                let v = self.imm_m(bus);
841                self.op_and(v);
842            }
843            0x25 => {
844                let e = self.resolve(bus, Mode::Direct);
845                let v = self.read_m(bus, e);
846                self.op_and(v);
847            }
848            0x35 => {
849                let e = self.resolve(bus, Mode::DirectX);
850                let v = self.read_m(bus, e);
851                self.op_and(v);
852            }
853            0x2D => {
854                let e = self.resolve(bus, Mode::Absolute);
855                let v = self.read_m(bus, e);
856                self.op_and(v);
857            }
858            0x3D => {
859                let e = self.resolve(bus, Mode::AbsoluteX);
860                self.idx_penalty(bus, e);
861                let v = self.read_m(bus, e);
862                self.op_and(v);
863            }
864            0x39 => {
865                let e = self.resolve(bus, Mode::AbsoluteY);
866                self.idx_penalty(bus, e);
867                let v = self.read_m(bus, e);
868                self.op_and(v);
869            }
870            0x21 => {
871                let e = self.resolve(bus, Mode::DirectXIndirect);
872                let v = self.read_m(bus, e);
873                self.op_and(v);
874            }
875            0x31 => {
876                let e = self.resolve(bus, Mode::DirectIndirectY);
877                self.idx_penalty(bus, e);
878                let v = self.read_m(bus, e);
879                self.op_and(v);
880            }
881            0x32 => {
882                let e = self.resolve(bus, Mode::DirectIndirect);
883                let v = self.read_m(bus, e);
884                self.op_and(v);
885            }
886            0x27 => {
887                let e = self.resolve(bus, Mode::DirectIndirectLong);
888                let v = self.read_m(bus, e);
889                self.op_and(v);
890            }
891            0x37 => {
892                let e = self.resolve(bus, Mode::DirectIndirectLongY);
893                let v = self.read_m(bus, e);
894                self.op_and(v);
895            }
896            0x2F => {
897                let e = self.resolve(bus, Mode::AbsoluteLong);
898                let v = self.read_m(bus, e);
899                self.op_and(v);
900            }
901            0x3F => {
902                let e = self.resolve(bus, Mode::AbsoluteLongX);
903                let v = self.read_m(bus, e);
904                self.op_and(v);
905            }
906            0x23 => {
907                let e = self.resolve(bus, Mode::StackRelative);
908                let v = self.read_m(bus, e);
909                self.op_and(v);
910            }
911            0x33 => {
912                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
913                let v = self.read_m(bus, e);
914                self.op_and(v);
915            }
916
917            // ---- EOR ----
918            0x49 => {
919                let v = self.imm_m(bus);
920                self.op_eor(v);
921            }
922            0x45 => {
923                let e = self.resolve(bus, Mode::Direct);
924                let v = self.read_m(bus, e);
925                self.op_eor(v);
926            }
927            0x55 => {
928                let e = self.resolve(bus, Mode::DirectX);
929                let v = self.read_m(bus, e);
930                self.op_eor(v);
931            }
932            0x4D => {
933                let e = self.resolve(bus, Mode::Absolute);
934                let v = self.read_m(bus, e);
935                self.op_eor(v);
936            }
937            0x5D => {
938                let e = self.resolve(bus, Mode::AbsoluteX);
939                self.idx_penalty(bus, e);
940                let v = self.read_m(bus, e);
941                self.op_eor(v);
942            }
943            0x59 => {
944                let e = self.resolve(bus, Mode::AbsoluteY);
945                self.idx_penalty(bus, e);
946                let v = self.read_m(bus, e);
947                self.op_eor(v);
948            }
949            0x41 => {
950                let e = self.resolve(bus, Mode::DirectXIndirect);
951                let v = self.read_m(bus, e);
952                self.op_eor(v);
953            }
954            0x51 => {
955                let e = self.resolve(bus, Mode::DirectIndirectY);
956                self.idx_penalty(bus, e);
957                let v = self.read_m(bus, e);
958                self.op_eor(v);
959            }
960            0x52 => {
961                let e = self.resolve(bus, Mode::DirectIndirect);
962                let v = self.read_m(bus, e);
963                self.op_eor(v);
964            }
965            0x47 => {
966                let e = self.resolve(bus, Mode::DirectIndirectLong);
967                let v = self.read_m(bus, e);
968                self.op_eor(v);
969            }
970            0x57 => {
971                let e = self.resolve(bus, Mode::DirectIndirectLongY);
972                let v = self.read_m(bus, e);
973                self.op_eor(v);
974            }
975            0x4F => {
976                let e = self.resolve(bus, Mode::AbsoluteLong);
977                let v = self.read_m(bus, e);
978                self.op_eor(v);
979            }
980            0x5F => {
981                let e = self.resolve(bus, Mode::AbsoluteLongX);
982                let v = self.read_m(bus, e);
983                self.op_eor(v);
984            }
985            0x43 => {
986                let e = self.resolve(bus, Mode::StackRelative);
987                let v = self.read_m(bus, e);
988                self.op_eor(v);
989            }
990            0x53 => {
991                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
992                let v = self.read_m(bus, e);
993                self.op_eor(v);
994            }
995
996            // ---- ADC ----
997            0x69 => {
998                let v = self.imm_m(bus);
999                self.adc(v);
1000            }
1001            0x65 => {
1002                let e = self.resolve(bus, Mode::Direct);
1003                let v = self.read_m(bus, e);
1004                self.adc(v);
1005            }
1006            0x75 => {
1007                let e = self.resolve(bus, Mode::DirectX);
1008                let v = self.read_m(bus, e);
1009                self.adc(v);
1010            }
1011            0x6D => {
1012                let e = self.resolve(bus, Mode::Absolute);
1013                let v = self.read_m(bus, e);
1014                self.adc(v);
1015            }
1016            0x7D => {
1017                let e = self.resolve(bus, Mode::AbsoluteX);
1018                self.idx_penalty(bus, e);
1019                let v = self.read_m(bus, e);
1020                self.adc(v);
1021            }
1022            0x79 => {
1023                let e = self.resolve(bus, Mode::AbsoluteY);
1024                self.idx_penalty(bus, e);
1025                let v = self.read_m(bus, e);
1026                self.adc(v);
1027            }
1028            0x61 => {
1029                let e = self.resolve(bus, Mode::DirectXIndirect);
1030                let v = self.read_m(bus, e);
1031                self.adc(v);
1032            }
1033            0x71 => {
1034                let e = self.resolve(bus, Mode::DirectIndirectY);
1035                self.idx_penalty(bus, e);
1036                let v = self.read_m(bus, e);
1037                self.adc(v);
1038            }
1039            0x72 => {
1040                let e = self.resolve(bus, Mode::DirectIndirect);
1041                let v = self.read_m(bus, e);
1042                self.adc(v);
1043            }
1044            0x67 => {
1045                let e = self.resolve(bus, Mode::DirectIndirectLong);
1046                let v = self.read_m(bus, e);
1047                self.adc(v);
1048            }
1049            0x77 => {
1050                let e = self.resolve(bus, Mode::DirectIndirectLongY);
1051                let v = self.read_m(bus, e);
1052                self.adc(v);
1053            }
1054            0x6F => {
1055                let e = self.resolve(bus, Mode::AbsoluteLong);
1056                let v = self.read_m(bus, e);
1057                self.adc(v);
1058            }
1059            0x7F => {
1060                let e = self.resolve(bus, Mode::AbsoluteLongX);
1061                let v = self.read_m(bus, e);
1062                self.adc(v);
1063            }
1064            0x63 => {
1065                let e = self.resolve(bus, Mode::StackRelative);
1066                let v = self.read_m(bus, e);
1067                self.adc(v);
1068            }
1069            0x73 => {
1070                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
1071                let v = self.read_m(bus, e);
1072                self.adc(v);
1073            }
1074
1075            // ---- SBC ----
1076            0xE9 => {
1077                let v = self.imm_m(bus);
1078                self.sbc(v);
1079            }
1080            0xE5 => {
1081                let e = self.resolve(bus, Mode::Direct);
1082                let v = self.read_m(bus, e);
1083                self.sbc(v);
1084            }
1085            0xF5 => {
1086                let e = self.resolve(bus, Mode::DirectX);
1087                let v = self.read_m(bus, e);
1088                self.sbc(v);
1089            }
1090            0xED => {
1091                let e = self.resolve(bus, Mode::Absolute);
1092                let v = self.read_m(bus, e);
1093                self.sbc(v);
1094            }
1095            0xFD => {
1096                let e = self.resolve(bus, Mode::AbsoluteX);
1097                self.idx_penalty(bus, e);
1098                let v = self.read_m(bus, e);
1099                self.sbc(v);
1100            }
1101            0xF9 => {
1102                let e = self.resolve(bus, Mode::AbsoluteY);
1103                self.idx_penalty(bus, e);
1104                let v = self.read_m(bus, e);
1105                self.sbc(v);
1106            }
1107            0xE1 => {
1108                let e = self.resolve(bus, Mode::DirectXIndirect);
1109                let v = self.read_m(bus, e);
1110                self.sbc(v);
1111            }
1112            0xF1 => {
1113                let e = self.resolve(bus, Mode::DirectIndirectY);
1114                self.idx_penalty(bus, e);
1115                let v = self.read_m(bus, e);
1116                self.sbc(v);
1117            }
1118            0xF2 => {
1119                let e = self.resolve(bus, Mode::DirectIndirect);
1120                let v = self.read_m(bus, e);
1121                self.sbc(v);
1122            }
1123            0xE7 => {
1124                let e = self.resolve(bus, Mode::DirectIndirectLong);
1125                let v = self.read_m(bus, e);
1126                self.sbc(v);
1127            }
1128            0xF7 => {
1129                let e = self.resolve(bus, Mode::DirectIndirectLongY);
1130                let v = self.read_m(bus, e);
1131                self.sbc(v);
1132            }
1133            0xEF => {
1134                let e = self.resolve(bus, Mode::AbsoluteLong);
1135                let v = self.read_m(bus, e);
1136                self.sbc(v);
1137            }
1138            0xFF => {
1139                let e = self.resolve(bus, Mode::AbsoluteLongX);
1140                let v = self.read_m(bus, e);
1141                self.sbc(v);
1142            }
1143            0xE3 => {
1144                let e = self.resolve(bus, Mode::StackRelative);
1145                let v = self.read_m(bus, e);
1146                self.sbc(v);
1147            }
1148            0xF3 => {
1149                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
1150                let v = self.read_m(bus, e);
1151                self.sbc(v);
1152            }
1153
1154            // ---- CMP ----
1155            0xC9 => {
1156                let v = self.imm_m(bus);
1157                self.op_cmp(v);
1158            }
1159            0xC5 => {
1160                let e = self.resolve(bus, Mode::Direct);
1161                let v = self.read_m(bus, e);
1162                self.op_cmp(v);
1163            }
1164            0xD5 => {
1165                let e = self.resolve(bus, Mode::DirectX);
1166                let v = self.read_m(bus, e);
1167                self.op_cmp(v);
1168            }
1169            0xCD => {
1170                let e = self.resolve(bus, Mode::Absolute);
1171                let v = self.read_m(bus, e);
1172                self.op_cmp(v);
1173            }
1174            0xDD => {
1175                let e = self.resolve(bus, Mode::AbsoluteX);
1176                self.idx_penalty(bus, e);
1177                let v = self.read_m(bus, e);
1178                self.op_cmp(v);
1179            }
1180            0xD9 => {
1181                let e = self.resolve(bus, Mode::AbsoluteY);
1182                self.idx_penalty(bus, e);
1183                let v = self.read_m(bus, e);
1184                self.op_cmp(v);
1185            }
1186            0xC1 => {
1187                let e = self.resolve(bus, Mode::DirectXIndirect);
1188                let v = self.read_m(bus, e);
1189                self.op_cmp(v);
1190            }
1191            0xD1 => {
1192                let e = self.resolve(bus, Mode::DirectIndirectY);
1193                self.idx_penalty(bus, e);
1194                let v = self.read_m(bus, e);
1195                self.op_cmp(v);
1196            }
1197            0xD2 => {
1198                let e = self.resolve(bus, Mode::DirectIndirect);
1199                let v = self.read_m(bus, e);
1200                self.op_cmp(v);
1201            }
1202            0xC7 => {
1203                let e = self.resolve(bus, Mode::DirectIndirectLong);
1204                let v = self.read_m(bus, e);
1205                self.op_cmp(v);
1206            }
1207            0xD7 => {
1208                let e = self.resolve(bus, Mode::DirectIndirectLongY);
1209                let v = self.read_m(bus, e);
1210                self.op_cmp(v);
1211            }
1212            0xCF => {
1213                let e = self.resolve(bus, Mode::AbsoluteLong);
1214                let v = self.read_m(bus, e);
1215                self.op_cmp(v);
1216            }
1217            0xDF => {
1218                let e = self.resolve(bus, Mode::AbsoluteLongX);
1219                let v = self.read_m(bus, e);
1220                self.op_cmp(v);
1221            }
1222            0xC3 => {
1223                let e = self.resolve(bus, Mode::StackRelative);
1224                let v = self.read_m(bus, e);
1225                self.op_cmp(v);
1226            }
1227            0xD3 => {
1228                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
1229                let v = self.read_m(bus, e);
1230                self.op_cmp(v);
1231            }
1232
1233            // ---- CPX / CPY ----
1234            0xE0 => {
1235                let v = self.imm_x(bus);
1236                self.op_cpx(v);
1237            }
1238            0xE4 => {
1239                let e = self.resolve(bus, Mode::Direct);
1240                let v = self.read_x(bus, e);
1241                self.op_cpx(v);
1242            }
1243            0xEC => {
1244                let e = self.resolve(bus, Mode::Absolute);
1245                let v = self.read_x(bus, e);
1246                self.op_cpx(v);
1247            }
1248            0xC0 => {
1249                let v = self.imm_x(bus);
1250                self.op_cpy(v);
1251            }
1252            0xC4 => {
1253                let e = self.resolve(bus, Mode::Direct);
1254                let v = self.read_x(bus, e);
1255                self.op_cpy(v);
1256            }
1257            0xCC => {
1258                let e = self.resolve(bus, Mode::Absolute);
1259                let v = self.read_x(bus, e);
1260                self.op_cpy(v);
1261            }
1262
1263            // ---- BIT ----
1264            0x89 => {
1265                let v = self.imm_m(bus);
1266                self.op_bit_imm(v);
1267            }
1268            0x24 => {
1269                let e = self.resolve(bus, Mode::Direct);
1270                let v = self.read_m(bus, e);
1271                self.op_bit(v);
1272            }
1273            0x34 => {
1274                let e = self.resolve(bus, Mode::DirectX);
1275                let v = self.read_m(bus, e);
1276                self.op_bit(v);
1277            }
1278            0x2C => {
1279                let e = self.resolve(bus, Mode::Absolute);
1280                let v = self.read_m(bus, e);
1281                self.op_bit(v);
1282            }
1283            0x3C => {
1284                let e = self.resolve(bus, Mode::AbsoluteX);
1285                self.idx_penalty(bus, e);
1286                let v = self.read_m(bus, e);
1287                self.op_bit(v);
1288            }
1289
1290            // ---- LDA ----
1291            0xA9 => {
1292                let v = self.imm_m(bus);
1293                self.op_lda(v);
1294            }
1295            0xA5 => {
1296                let e = self.resolve(bus, Mode::Direct);
1297                let v = self.read_m(bus, e);
1298                self.op_lda(v);
1299            }
1300            0xB5 => {
1301                let e = self.resolve(bus, Mode::DirectX);
1302                let v = self.read_m(bus, e);
1303                self.op_lda(v);
1304            }
1305            0xAD => {
1306                let e = self.resolve(bus, Mode::Absolute);
1307                let v = self.read_m(bus, e);
1308                self.op_lda(v);
1309            }
1310            0xBD => {
1311                let e = self.resolve(bus, Mode::AbsoluteX);
1312                self.idx_penalty(bus, e);
1313                let v = self.read_m(bus, e);
1314                self.op_lda(v);
1315            }
1316            0xB9 => {
1317                let e = self.resolve(bus, Mode::AbsoluteY);
1318                self.idx_penalty(bus, e);
1319                let v = self.read_m(bus, e);
1320                self.op_lda(v);
1321            }
1322            0xA1 => {
1323                let e = self.resolve(bus, Mode::DirectXIndirect);
1324                let v = self.read_m(bus, e);
1325                self.op_lda(v);
1326            }
1327            0xB1 => {
1328                let e = self.resolve(bus, Mode::DirectIndirectY);
1329                self.idx_penalty(bus, e);
1330                let v = self.read_m(bus, e);
1331                self.op_lda(v);
1332            }
1333            0xB2 => {
1334                let e = self.resolve(bus, Mode::DirectIndirect);
1335                let v = self.read_m(bus, e);
1336                self.op_lda(v);
1337            }
1338            0xA7 => {
1339                let e = self.resolve(bus, Mode::DirectIndirectLong);
1340                let v = self.read_m(bus, e);
1341                self.op_lda(v);
1342            }
1343            0xB7 => {
1344                let e = self.resolve(bus, Mode::DirectIndirectLongY);
1345                let v = self.read_m(bus, e);
1346                self.op_lda(v);
1347            }
1348            0xAF => {
1349                let e = self.resolve(bus, Mode::AbsoluteLong);
1350                let v = self.read_m(bus, e);
1351                self.op_lda(v);
1352            }
1353            0xBF => {
1354                let e = self.resolve(bus, Mode::AbsoluteLongX);
1355                let v = self.read_m(bus, e);
1356                self.op_lda(v);
1357            }
1358            0xA3 => {
1359                let e = self.resolve(bus, Mode::StackRelative);
1360                let v = self.read_m(bus, e);
1361                self.op_lda(v);
1362            }
1363            0xB3 => {
1364                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
1365                let v = self.read_m(bus, e);
1366                self.op_lda(v);
1367            }
1368
1369            // ---- LDX / LDY ----
1370            0xA2 => {
1371                let v = self.imm_x(bus);
1372                self.op_ldx(v);
1373            }
1374            0xA6 => {
1375                let e = self.resolve(bus, Mode::Direct);
1376                let v = self.read_x(bus, e);
1377                self.op_ldx(v);
1378            }
1379            0xB6 => {
1380                let e = self.resolve(bus, Mode::DirectY);
1381                let v = self.read_x(bus, e);
1382                self.op_ldx(v);
1383            }
1384            0xAE => {
1385                let e = self.resolve(bus, Mode::Absolute);
1386                let v = self.read_x(bus, e);
1387                self.op_ldx(v);
1388            }
1389            0xBE => {
1390                let e = self.resolve(bus, Mode::AbsoluteY);
1391                self.idx_penalty(bus, e);
1392                let v = self.read_x(bus, e);
1393                self.op_ldx(v);
1394            }
1395            0xA0 => {
1396                let v = self.imm_x(bus);
1397                self.op_ldy(v);
1398            }
1399            0xA4 => {
1400                let e = self.resolve(bus, Mode::Direct);
1401                let v = self.read_x(bus, e);
1402                self.op_ldy(v);
1403            }
1404            0xB4 => {
1405                let e = self.resolve(bus, Mode::DirectX);
1406                let v = self.read_x(bus, e);
1407                self.op_ldy(v);
1408            }
1409            0xAC => {
1410                let e = self.resolve(bus, Mode::Absolute);
1411                let v = self.read_x(bus, e);
1412                self.op_ldy(v);
1413            }
1414            0xBC => {
1415                let e = self.resolve(bus, Mode::AbsoluteX);
1416                self.idx_penalty(bus, e);
1417                let v = self.read_x(bus, e);
1418                self.op_ldy(v);
1419            }
1420
1421            // ---- STA ----
1422            0x85 => {
1423                let e = self.resolve(bus, Mode::Direct);
1424                self.write_m(bus, e, self.regs.a_val());
1425            }
1426            0x95 => {
1427                let e = self.resolve(bus, Mode::DirectX);
1428                self.write_m(bus, e, self.regs.a_val());
1429            }
1430            0x8D => {
1431                let e = self.resolve(bus, Mode::Absolute);
1432                self.write_m(bus, e, self.regs.a_val());
1433            }
1434            0x9D => {
1435                let e = self.resolve(bus, Mode::AbsoluteX);
1436                self.io(bus);
1437                self.write_m(bus, e, self.regs.a_val());
1438            }
1439            0x99 => {
1440                let e = self.resolve(bus, Mode::AbsoluteY);
1441                self.io(bus);
1442                self.write_m(bus, e, self.regs.a_val());
1443            }
1444            0x81 => {
1445                let e = self.resolve(bus, Mode::DirectXIndirect);
1446                self.write_m(bus, e, self.regs.a_val());
1447            }
1448            0x91 => {
1449                let e = self.resolve(bus, Mode::DirectIndirectY);
1450                self.io(bus);
1451                self.write_m(bus, e, self.regs.a_val());
1452            }
1453            0x92 => {
1454                let e = self.resolve(bus, Mode::DirectIndirect);
1455                self.write_m(bus, e, self.regs.a_val());
1456            }
1457            0x87 => {
1458                let e = self.resolve(bus, Mode::DirectIndirectLong);
1459                self.write_m(bus, e, self.regs.a_val());
1460            }
1461            0x97 => {
1462                let e = self.resolve(bus, Mode::DirectIndirectLongY);
1463                self.write_m(bus, e, self.regs.a_val());
1464            }
1465            0x8F => {
1466                let e = self.resolve(bus, Mode::AbsoluteLong);
1467                self.write_m(bus, e, self.regs.a_val());
1468            }
1469            0x9F => {
1470                let e = self.resolve(bus, Mode::AbsoluteLongX);
1471                self.write_m(bus, e, self.regs.a_val());
1472            }
1473            0x83 => {
1474                let e = self.resolve(bus, Mode::StackRelative);
1475                self.write_m(bus, e, self.regs.a_val());
1476            }
1477            0x93 => {
1478                let e = self.resolve(bus, Mode::StackRelativeIndirectY);
1479                self.write_m(bus, e, self.regs.a_val());
1480            }
1481
1482            // ---- STX / STY ----
1483            0x86 => {
1484                let e = self.resolve(bus, Mode::Direct);
1485                self.write_x(bus, e, self.regs.x_val());
1486            }
1487            0x96 => {
1488                let e = self.resolve(bus, Mode::DirectY);
1489                self.write_x(bus, e, self.regs.x_val());
1490            }
1491            0x8E => {
1492                let e = self.resolve(bus, Mode::Absolute);
1493                self.write_x(bus, e, self.regs.x_val());
1494            }
1495            0x84 => {
1496                let e = self.resolve(bus, Mode::Direct);
1497                self.write_x(bus, e, self.regs.y_val());
1498            }
1499            0x94 => {
1500                let e = self.resolve(bus, Mode::DirectX);
1501                self.write_x(bus, e, self.regs.y_val());
1502            }
1503            0x8C => {
1504                let e = self.resolve(bus, Mode::Absolute);
1505                self.write_x(bus, e, self.regs.y_val());
1506            }
1507
1508            // ---- STZ ----
1509            0x64 => {
1510                let e = self.resolve(bus, Mode::Direct);
1511                self.write_m(bus, e, 0);
1512            }
1513            0x74 => {
1514                let e = self.resolve(bus, Mode::DirectX);
1515                self.write_m(bus, e, 0);
1516            }
1517            0x9C => {
1518                let e = self.resolve(bus, Mode::Absolute);
1519                self.write_m(bus, e, 0);
1520            }
1521            0x9E => {
1522                let e = self.resolve(bus, Mode::AbsoluteX);
1523                self.io(bus);
1524                self.write_m(bus, e, 0);
1525            }
1526
1527            // ---- INC / DEC memory + accumulator ----
1528            0x1A => {
1529                self.io(bus);
1530                self.op_inc_a();
1531            }
1532            0x3A => {
1533                self.io(bus);
1534                self.op_dec_a();
1535            }
1536            0xE6 => {
1537                let e = self.resolve(bus, Mode::Direct);
1538                self.rmw_inc(bus, e);
1539            }
1540            0xF6 => {
1541                let e = self.resolve(bus, Mode::DirectX);
1542                self.rmw_inc(bus, e);
1543            }
1544            0xEE => {
1545                let e = self.resolve(bus, Mode::Absolute);
1546                self.rmw_inc(bus, e);
1547            }
1548            0xFE => {
1549                let e = self.resolve(bus, Mode::AbsoluteX);
1550                self.io(bus);
1551                self.rmw_inc(bus, e);
1552            }
1553            0xC6 => {
1554                let e = self.resolve(bus, Mode::Direct);
1555                self.rmw_dec(bus, e);
1556            }
1557            0xD6 => {
1558                let e = self.resolve(bus, Mode::DirectX);
1559                self.rmw_dec(bus, e);
1560            }
1561            0xCE => {
1562                let e = self.resolve(bus, Mode::Absolute);
1563                self.rmw_dec(bus, e);
1564            }
1565            0xDE => {
1566                let e = self.resolve(bus, Mode::AbsoluteX);
1567                self.io(bus);
1568                self.rmw_dec(bus, e);
1569            }
1570
1571            // ---- ASL / LSR / ROL / ROR ----
1572            0x0A => {
1573                self.io(bus);
1574                self.op_asl_a();
1575            }
1576            0x06 => {
1577                let e = self.resolve(bus, Mode::Direct);
1578                self.rmw_asl(bus, e);
1579            }
1580            0x16 => {
1581                let e = self.resolve(bus, Mode::DirectX);
1582                self.rmw_asl(bus, e);
1583            }
1584            0x0E => {
1585                let e = self.resolve(bus, Mode::Absolute);
1586                self.rmw_asl(bus, e);
1587            }
1588            0x1E => {
1589                let e = self.resolve(bus, Mode::AbsoluteX);
1590                self.io(bus);
1591                self.rmw_asl(bus, e);
1592            }
1593            0x4A => {
1594                self.io(bus);
1595                self.op_lsr_a();
1596            }
1597            0x46 => {
1598                let e = self.resolve(bus, Mode::Direct);
1599                self.rmw_lsr(bus, e);
1600            }
1601            0x56 => {
1602                let e = self.resolve(bus, Mode::DirectX);
1603                self.rmw_lsr(bus, e);
1604            }
1605            0x4E => {
1606                let e = self.resolve(bus, Mode::Absolute);
1607                self.rmw_lsr(bus, e);
1608            }
1609            0x5E => {
1610                let e = self.resolve(bus, Mode::AbsoluteX);
1611                self.io(bus);
1612                self.rmw_lsr(bus, e);
1613            }
1614            0x2A => {
1615                self.io(bus);
1616                self.op_rol_a();
1617            }
1618            0x26 => {
1619                let e = self.resolve(bus, Mode::Direct);
1620                self.rmw_rol(bus, e);
1621            }
1622            0x36 => {
1623                let e = self.resolve(bus, Mode::DirectX);
1624                self.rmw_rol(bus, e);
1625            }
1626            0x2E => {
1627                let e = self.resolve(bus, Mode::Absolute);
1628                self.rmw_rol(bus, e);
1629            }
1630            0x3E => {
1631                let e = self.resolve(bus, Mode::AbsoluteX);
1632                self.io(bus);
1633                self.rmw_rol(bus, e);
1634            }
1635            0x6A => {
1636                self.io(bus);
1637                self.op_ror_a();
1638            }
1639            0x66 => {
1640                let e = self.resolve(bus, Mode::Direct);
1641                self.rmw_ror(bus, e);
1642            }
1643            0x76 => {
1644                let e = self.resolve(bus, Mode::DirectX);
1645                self.rmw_ror(bus, e);
1646            }
1647            0x6E => {
1648                let e = self.resolve(bus, Mode::Absolute);
1649                self.rmw_ror(bus, e);
1650            }
1651            0x7E => {
1652                let e = self.resolve(bus, Mode::AbsoluteX);
1653                self.io(bus);
1654                self.rmw_ror(bus, e);
1655            }
1656
1657            // ---- TSB / TRB ----
1658            0x04 => {
1659                let e = self.resolve(bus, Mode::Direct);
1660                self.rmw_tsb(bus, e);
1661            }
1662            0x0C => {
1663                let e = self.resolve(bus, Mode::Absolute);
1664                self.rmw_tsb(bus, e);
1665            }
1666            0x14 => {
1667                let e = self.resolve(bus, Mode::Direct);
1668                self.rmw_trb(bus, e);
1669            }
1670            0x1C => {
1671                let e = self.resolve(bus, Mode::Absolute);
1672                self.rmw_trb(bus, e);
1673            }
1674
1675            // ---- INX/INY/DEX/DEY ----
1676            0xE8 => {
1677                self.io(bus);
1678                let v = self.regs.x_val().wrapping_add(1);
1679                self.regs.set_x(v);
1680                self.regs.set_nz_x(self.regs.x_val());
1681            }
1682            0xC8 => {
1683                self.io(bus);
1684                let v = self.regs.y_val().wrapping_add(1);
1685                self.regs.set_y(v);
1686                self.regs.set_nz_x(self.regs.y_val());
1687            }
1688            0xCA => {
1689                self.io(bus);
1690                let v = self.regs.x_val().wrapping_sub(1);
1691                self.regs.set_x(v);
1692                self.regs.set_nz_x(self.regs.x_val());
1693            }
1694            0x88 => {
1695                self.io(bus);
1696                let v = self.regs.y_val().wrapping_sub(1);
1697                self.regs.set_y(v);
1698                self.regs.set_nz_x(self.regs.y_val());
1699            }
1700
1701            // ---- Transfers ----
1702            0xAA => {
1703                self.io(bus);
1704                let v = self.regs.a;
1705                self.regs.set_x(v);
1706                self.regs.set_nz_x(self.regs.x_val());
1707            }
1708            0xA8 => {
1709                self.io(bus);
1710                let v = self.regs.a;
1711                self.regs.set_y(v);
1712                self.regs.set_nz_x(self.regs.y_val());
1713            }
1714            0x8A => {
1715                self.io(bus);
1716                let v = self.regs.x_val();
1717                self.regs.set_a(v);
1718                self.regs.set_nz_m(self.regs.a_val());
1719            }
1720            0x98 => {
1721                self.io(bus);
1722                let v = self.regs.y_val();
1723                self.regs.set_a(v);
1724                self.regs.set_nz_m(self.regs.a_val());
1725            }
1726            0xBA => {
1727                self.io(bus);
1728                let v = self.regs.s;
1729                self.regs.set_x(v);
1730                self.regs.set_nz_x(self.regs.x_val());
1731            }
1732            0x9A => {
1733                self.io(bus);
1734                self.op_txs();
1735            }
1736            0x9B => {
1737                self.io(bus);
1738                let v = self.regs.x_val();
1739                self.regs.set_y(v);
1740                self.regs.set_nz_x(self.regs.y_val());
1741            }
1742            0xBB => {
1743                self.io(bus);
1744                let v = self.regs.y_val();
1745                self.regs.set_x(v);
1746                self.regs.set_nz_x(self.regs.x_val());
1747            }
1748            0x5B => {
1749                self.io(bus);
1750                self.regs.d = self.regs.a;
1751                self.regs.set_nz16(self.regs.d);
1752            }
1753            0x7B => {
1754                self.io(bus);
1755                let v = self.regs.d;
1756                self.regs.a = v;
1757                self.regs.set_nz16(v);
1758            }
1759            0x1B => {
1760                self.io(bus);
1761                self.op_tcs();
1762            }
1763            0x3B => {
1764                self.io(bus);
1765                let v = self.regs.s;
1766                self.regs.a = v;
1767                self.regs.set_nz16(v);
1768            }
1769
1770            // ---- Stack push/pull ----
1771            0x48 => {
1772                self.io(bus);
1773                self.op_pha(bus);
1774            }
1775            0x68 => {
1776                self.io(bus);
1777                self.io(bus);
1778                self.op_pla(bus);
1779            }
1780            0xDA => {
1781                self.io(bus);
1782                self.op_phx(bus);
1783            }
1784            0xFA => {
1785                self.io(bus);
1786                self.io(bus);
1787                self.op_plx(bus);
1788            }
1789            0x5A => {
1790                self.io(bus);
1791                self.op_phy(bus);
1792            }
1793            0x7A => {
1794                self.io(bus);
1795                self.io(bus);
1796                self.op_ply(bus);
1797            }
1798            0x08 => {
1799                self.io(bus);
1800                self.push8(bus, self.regs.p.bits());
1801            }
1802            0x28 => {
1803                self.io(bus);
1804                self.io(bus);
1805                self.op_plp(bus);
1806            }
1807            0x8B => {
1808                self.io(bus);
1809                self.push8(bus, self.regs.dbr);
1810            }
1811            0xAB => {
1812                self.io(bus);
1813                self.io(bus);
1814                let v = self.pull_n8(bus); // PLB uses pullN
1815                self.regs.dbr = v;
1816                self.regs.set_nz8(v);
1817            }
1818            0x0B => {
1819                self.io(bus);
1820                self.push_n16(bus, self.regs.d); // PHD uses pushN
1821            }
1822            0x2B => {
1823                self.io(bus);
1824                self.io(bus);
1825                let v = self.pull_n16(bus); // PLD uses pullN
1826                self.regs.d = v;
1827                self.regs.set_nz16(v);
1828            }
1829            0x4B => {
1830                self.io(bus);
1831                self.push8(bus, self.regs.pbr);
1832            }
1833            0xF4 => {
1834                let v = self.fetch16(bus);
1835                self.push_n16(bus, v); // PEA uses pushN
1836            } // PEA
1837            0xD4 => {
1838                self.dp_penalty(bus);
1839                let dp = u16::from(self.fetch8(bus));
1840                // PEI pointer fetch uses readDirectN (no page-lock); push uses pushN.
1841                let lo = self.bus_read8(bus, self.direct_n_addr(dp));
1842                let hi = self.bus_read8(bus, self.direct_n_addr(dp.wrapping_add(1)));
1843                let v = u16::from(lo) | (u16::from(hi) << 8);
1844                self.push_n16(bus, v);
1845            } // PEI
1846            0x62 => {
1847                let off = self.fetch16(bus);
1848                self.io(bus);
1849                let v = self.regs.pc.wrapping_add(off);
1850                self.push_n16(bus, v); // PER uses pushN
1851            } // PER
1852
1853            // ---- Flag ops ----
1854            0x18 => {
1855                self.io(bus);
1856                self.regs.set_flag(Status::C, false);
1857            }
1858            0x38 => {
1859                self.io(bus);
1860                self.regs.set_flag(Status::C, true);
1861            }
1862            0x58 => {
1863                self.io(bus);
1864                self.regs.set_flag(Status::I, false);
1865            }
1866            0x78 => {
1867                self.io(bus);
1868                self.regs.set_flag(Status::I, true);
1869            }
1870            0xD8 => {
1871                self.io(bus);
1872                self.regs.set_flag(Status::D, false);
1873            }
1874            0xF8 => {
1875                self.io(bus);
1876                self.regs.set_flag(Status::D, true);
1877            }
1878            0xB8 => {
1879                self.io(bus);
1880                self.regs.set_flag(Status::V, false);
1881            }
1882            0xC2 => {
1883                let m = self.fetch8(bus);
1884                self.io(bus);
1885                self.op_rep(m);
1886            }
1887            0xE2 => {
1888                let m = self.fetch8(bus);
1889                self.io(bus);
1890                self.op_sep(m);
1891            }
1892            0xFB => {
1893                self.io(bus);
1894                self.op_xce();
1895            }
1896
1897            // ---- Branches ----
1898            0x10 => {
1899                self.branch(bus, !self.regs.p.contains(Status::N));
1900            }
1901            0x30 => {
1902                self.branch(bus, self.regs.p.contains(Status::N));
1903            }
1904            0x50 => {
1905                self.branch(bus, !self.regs.p.contains(Status::V));
1906            }
1907            0x70 => {
1908                self.branch(bus, self.regs.p.contains(Status::V));
1909            }
1910            0x90 => {
1911                self.branch(bus, !self.regs.p.contains(Status::C));
1912            }
1913            0xB0 => {
1914                self.branch(bus, self.regs.p.contains(Status::C));
1915            }
1916            0xD0 => {
1917                self.branch(bus, !self.regs.p.contains(Status::Z));
1918            }
1919            0xF0 => {
1920                self.branch(bus, self.regs.p.contains(Status::Z));
1921            }
1922            0x80 => {
1923                self.branch(bus, true);
1924            }
1925            0x82 => {
1926                self.op_brl(bus);
1927            }
1928
1929            // ---- Jumps / calls / returns ----
1930            0x4C => {
1931                let off = self.fetch16(bus);
1932                self.regs.pc = off;
1933            }
1934            0x6C => {
1935                let ptr = self.fetch16(bus);
1936                let lo = self.bus_read8(bus, u32::from(ptr));
1937                let hi = self.bus_read8(bus, u32::from(ptr.wrapping_add(1)));
1938                self.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
1939            }
1940            0x7C => {
1941                let base = self.fetch16(bus);
1942                self.io(bus);
1943                let ptr = base.wrapping_add(self.regs.x_val());
1944                let addr = (u32::from(self.regs.pbr) << 16) | u32::from(ptr);
1945                let lo = self.bus_read8(bus, addr);
1946                let hi = self.bus_read8(
1947                    bus,
1948                    (u32::from(self.regs.pbr) << 16) | u32::from(ptr.wrapping_add(1)),
1949                );
1950                self.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
1951            }
1952            0x5C => {
1953                let addr = self.fetch24(bus);
1954                self.regs.pc = addr as u16;
1955                self.regs.pbr = (addr >> 16) as u8;
1956            }
1957            0xDC => {
1958                let ptr = self.fetch16(bus);
1959                let lo = self.bus_read8(bus, u32::from(ptr));
1960                let mid = self.bus_read8(bus, u32::from(ptr.wrapping_add(1)));
1961                let hi = self.bus_read8(bus, u32::from(ptr.wrapping_add(2)));
1962                self.regs.pc = u16::from(lo) | (u16::from(mid) << 8);
1963                self.regs.pbr = hi;
1964            }
1965            0x20 => {
1966                self.op_jsr(bus);
1967            }
1968            0xFC => {
1969                self.op_jsr_abs_x_ind(bus);
1970            }
1971            0x22 => {
1972                self.op_jsl(bus);
1973            }
1974            0x60 => {
1975                self.op_rts(bus);
1976            }
1977            0x6B => {
1978                self.op_rtl(bus);
1979            }
1980            0x40 => {
1981                self.op_rti(bus);
1982            }
1983
1984            // ---- Block move ----
1985            0x54 => {
1986                self.op_mvn(bus);
1987            }
1988            0x44 => {
1989                self.op_mvp(bus);
1990            }
1991
1992            // ---- Interrupts / misc ----
1993            0x00 => {
1994                let _ = self.fetch8(bus);
1995                self.op_brk(bus);
1996            }
1997            0x02 => {
1998                let _ = self.fetch8(bus);
1999                self.op_cop(bus);
2000            }
2001            0xDB => {
2002                // STP: opcode fetch + 3 internal cycles (oracle = 4 total), then halt.
2003                self.io(bus);
2004                self.io(bus);
2005                self.io(bus);
2006                self.stopped = true;
2007            }
2008            0xCB => {
2009                // WAI: opcode fetch + 3 internal cycles (oracle = 4 total), then wait.
2010                self.io(bus);
2011                self.io(bus);
2012                self.io(bus);
2013                self.waiting = true;
2014            }
2015            0xEA => {
2016                self.io(bus);
2017            }
2018            0x42 => {
2019                let _ = self.fetch8(bus);
2020            } // WDM: 2-byte no-op
2021
2022            // ---- XBA: exchange the high and low bytes of the 16-bit accumulator ----
2023            0xEB => {
2024                self.io(bus);
2025                self.io(bus);
2026                self.op_xba();
2027            }
2028        }
2029    }
2030
2031    // ----------------------------------------------------------------------------------
2032    // Indexed page-cross penalty (read-path indexed modes).
2033    // ----------------------------------------------------------------------------------
2034
2035    fn idx_penalty(&mut self, bus: &mut impl Bus, e: Effective) {
2036        // In 8-bit index mode a page cross always costs +1; in 16-bit index mode the extra
2037        // cycle is always taken regardless (the index high byte is always added).
2038        if e.page_cross || !self.regs.x8() {
2039            self.io(bus);
2040        }
2041    }
2042
2043    // ----------------------------------------------------------------------------------
2044    // Simple register ops.
2045    // ----------------------------------------------------------------------------------
2046
2047    const fn op_ora(&mut self, v: u16) {
2048        let r = self.regs.a_val() | v;
2049        self.regs.set_a(r);
2050        self.regs.set_nz_m(self.regs.a_val());
2051    }
2052    const fn op_and(&mut self, v: u16) {
2053        let r = self.regs.a_val() & v;
2054        self.regs.set_a(r);
2055        self.regs.set_nz_m(self.regs.a_val());
2056    }
2057    const fn op_eor(&mut self, v: u16) {
2058        let r = self.regs.a_val() ^ v;
2059        self.regs.set_a(r);
2060        self.regs.set_nz_m(self.regs.a_val());
2061    }
2062    const fn op_lda(&mut self, v: u16) {
2063        self.regs.set_a(v);
2064        self.regs.set_nz_m(self.regs.a_val());
2065    }
2066    const fn op_ldx(&mut self, v: u16) {
2067        self.regs.set_x(v);
2068        self.regs.set_nz_x(self.regs.x_val());
2069    }
2070    const fn op_ldy(&mut self, v: u16) {
2071        self.regs.set_y(v);
2072        self.regs.set_nz_x(self.regs.y_val());
2073    }
2074    fn op_cmp(&mut self, v: u16) {
2075        let a = self.regs.a_val();
2076        self.compare(a, v, self.regs.m8());
2077    }
2078    fn op_cpx(&mut self, v: u16) {
2079        let x = self.regs.x_val();
2080        self.compare(x, v, self.regs.x8());
2081    }
2082    fn op_cpy(&mut self, v: u16) {
2083        let y = self.regs.y_val();
2084        self.compare(y, v, self.regs.x8());
2085    }
2086    const fn op_bit(&mut self, v: u16) {
2087        let a = self.regs.a_val();
2088        if self.regs.m8() {
2089            let z = (v & a) & 0xFF;
2090            self.regs.set_flag(Status::Z, z == 0);
2091            self.regs.set_flag(Status::V, v & 0x40 != 0);
2092            self.regs.set_flag(Status::N, v & 0x80 != 0);
2093        } else {
2094            self.regs.set_flag(Status::Z, (v & a) == 0);
2095            self.regs.set_flag(Status::V, v & 0x4000 != 0);
2096            self.regs.set_flag(Status::N, v & 0x8000 != 0);
2097        }
2098    }
2099    const fn op_bit_imm(&mut self, v: u16) {
2100        // Immediate BIT affects only Z.
2101        let a = self.regs.a_val();
2102        let mask = if self.regs.m8() { 0x00FF } else { 0xFFFF };
2103        let z = (v & a) & mask;
2104        self.regs.set_flag(Status::Z, z == 0);
2105    }
2106    fn op_inc_a(&mut self) {
2107        let v = self.regs.a_val().wrapping_add(1);
2108        self.regs.set_a(v);
2109        self.regs.set_nz_m(self.regs.a_val());
2110    }
2111    fn op_dec_a(&mut self) {
2112        let v = self.regs.a_val().wrapping_sub(1);
2113        self.regs.set_a(v);
2114        self.regs.set_nz_m(self.regs.a_val());
2115    }
2116    fn op_asl_a(&mut self) {
2117        let v = self.regs.a_val();
2118        let (r, c) = if self.regs.m8() {
2119            ((v << 1) & 0xFF, v & 0x80 != 0)
2120        } else {
2121            (v << 1, v & 0x8000 != 0)
2122        };
2123        self.regs.set_a(r);
2124        self.regs.set_flag(Status::C, c);
2125        self.regs.set_nz_m(self.regs.a_val());
2126    }
2127    fn op_lsr_a(&mut self) {
2128        let v = self.regs.a_val();
2129        let c = v & 1 != 0;
2130        let r = v >> 1;
2131        self.regs.set_a(r);
2132        self.regs.set_flag(Status::C, c);
2133        self.regs.set_nz_m(self.regs.a_val());
2134    }
2135    fn op_rol_a(&mut self) {
2136        let v = self.regs.a_val();
2137        let carry_in = u16::from(self.regs.p.contains(Status::C));
2138        let (r, c) = if self.regs.m8() {
2139            (((v << 1) | carry_in) & 0xFF, v & 0x80 != 0)
2140        } else {
2141            ((v << 1) | carry_in, v & 0x8000 != 0)
2142        };
2143        self.regs.set_a(r);
2144        self.regs.set_flag(Status::C, c);
2145        self.regs.set_nz_m(self.regs.a_val());
2146    }
2147    fn op_ror_a(&mut self) {
2148        let v = self.regs.a_val();
2149        let carry_in = u16::from(self.regs.p.contains(Status::C));
2150        let c = v & 1 != 0;
2151        let r = if self.regs.m8() {
2152            (v >> 1) | (carry_in << 7)
2153        } else {
2154            (v >> 1) | (carry_in << 15)
2155        };
2156        self.regs.set_a(r);
2157        self.regs.set_flag(Status::C, c);
2158        self.regs.set_nz_m(self.regs.a_val());
2159    }
2160    fn op_xba(&mut self) {
2161        let lo = self.regs.a & 0x00FF;
2162        let hi = (self.regs.a >> 8) & 0x00FF;
2163        self.regs.a = (lo << 8) | hi;
2164        // NZ set from the new low byte (the old high byte), always 8-bit.
2165        self.regs.set_nz8(hi as u8);
2166    }
2167    fn op_txs(&mut self) {
2168        if self.regs.emulation {
2169            self.regs.s = 0x0100 | (self.regs.x_val() & 0x00FF);
2170        } else {
2171            self.regs.s = self.regs.x_val();
2172        }
2173    }
2174    fn op_tcs(&mut self) {
2175        if self.regs.emulation {
2176            self.regs.s = 0x0100 | (self.regs.a & 0x00FF);
2177        } else {
2178            self.regs.s = self.regs.a;
2179        }
2180    }
2181
2182    // ----------------------------------------------------------------------------------
2183    // Read-modify-write memory ops (width-aware, with the internal modify cycle).
2184    // ----------------------------------------------------------------------------------
2185
2186    fn rmw_inc(&mut self, bus: &mut impl Bus, e: Effective) {
2187        let v = self.read_m(bus, e);
2188        self.io(bus);
2189        let r = if self.regs.m8() {
2190            (v.wrapping_add(1)) & 0xFF
2191        } else {
2192            v.wrapping_add(1)
2193        };
2194        self.regs.set_nz_m(r);
2195        self.write_m(bus, e, r);
2196    }
2197    fn rmw_dec(&mut self, bus: &mut impl Bus, e: Effective) {
2198        let v = self.read_m(bus, e);
2199        self.io(bus);
2200        let r = if self.regs.m8() {
2201            (v.wrapping_sub(1)) & 0xFF
2202        } else {
2203            v.wrapping_sub(1)
2204        };
2205        self.regs.set_nz_m(r);
2206        self.write_m(bus, e, r);
2207    }
2208    fn rmw_asl(&mut self, bus: &mut impl Bus, e: Effective) {
2209        let v = self.read_m(bus, e);
2210        self.io(bus);
2211        let (r, c) = if self.regs.m8() {
2212            ((v << 1) & 0xFF, v & 0x80 != 0)
2213        } else {
2214            (v << 1, v & 0x8000 != 0)
2215        };
2216        self.regs.set_flag(Status::C, c);
2217        self.regs.set_nz_m(r);
2218        self.write_m(bus, e, r);
2219    }
2220    fn rmw_lsr(&mut self, bus: &mut impl Bus, e: Effective) {
2221        let v = self.read_m(bus, e);
2222        self.io(bus);
2223        let c = v & 1 != 0;
2224        let r = v >> 1;
2225        self.regs.set_flag(Status::C, c);
2226        self.regs.set_nz_m(r);
2227        self.write_m(bus, e, r);
2228    }
2229    fn rmw_rol(&mut self, bus: &mut impl Bus, e: Effective) {
2230        let v = self.read_m(bus, e);
2231        self.io(bus);
2232        let carry_in = u16::from(self.regs.p.contains(Status::C));
2233        let (r, c) = if self.regs.m8() {
2234            (((v << 1) | carry_in) & 0xFF, v & 0x80 != 0)
2235        } else {
2236            ((v << 1) | carry_in, v & 0x8000 != 0)
2237        };
2238        self.regs.set_flag(Status::C, c);
2239        self.regs.set_nz_m(r);
2240        self.write_m(bus, e, r);
2241    }
2242    fn rmw_ror(&mut self, bus: &mut impl Bus, e: Effective) {
2243        let v = self.read_m(bus, e);
2244        self.io(bus);
2245        let carry_in = u16::from(self.regs.p.contains(Status::C));
2246        let c = v & 1 != 0;
2247        let r = if self.regs.m8() {
2248            (v >> 1) | (carry_in << 7)
2249        } else {
2250            (v >> 1) | (carry_in << 15)
2251        };
2252        self.regs.set_flag(Status::C, c);
2253        self.regs.set_nz_m(r);
2254        self.write_m(bus, e, r);
2255    }
2256    fn rmw_tsb(&mut self, bus: &mut impl Bus, e: Effective) {
2257        let v = self.read_m(bus, e);
2258        self.io(bus);
2259        let a = self.regs.a_val();
2260        let mask = if self.regs.m8() { 0x00FF } else { 0xFFFF };
2261        self.regs.set_flag(Status::Z, (v & a) & mask == 0);
2262        let r = v | a;
2263        self.write_m(bus, e, r);
2264    }
2265    fn rmw_trb(&mut self, bus: &mut impl Bus, e: Effective) {
2266        let v = self.read_m(bus, e);
2267        self.io(bus);
2268        let a = self.regs.a_val();
2269        let mask = if self.regs.m8() { 0x00FF } else { 0xFFFF };
2270        self.regs.set_flag(Status::Z, (v & a) & mask == 0);
2271        let r = v & !a;
2272        self.write_m(bus, e, r);
2273    }
2274
2275    // ----------------------------------------------------------------------------------
2276    // Stack ops bodies.
2277    // ----------------------------------------------------------------------------------
2278
2279    fn op_pha(&mut self, bus: &mut impl Bus) {
2280        if self.regs.m8() {
2281            self.push8(bus, self.regs.a as u8);
2282        } else {
2283            self.push16(bus, self.regs.a);
2284        }
2285    }
2286    fn op_pla(&mut self, bus: &mut impl Bus) {
2287        if self.regs.m8() {
2288            let v = self.pull8(bus);
2289            self.regs.a = (self.regs.a & 0xFF00) | u16::from(v);
2290            self.regs.set_nz8(v);
2291        } else {
2292            let v = self.pull16(bus);
2293            self.regs.a = v;
2294            self.regs.set_nz16(v);
2295        }
2296    }
2297    fn op_phx(&mut self, bus: &mut impl Bus) {
2298        if self.regs.x8() {
2299            self.push8(bus, self.regs.x as u8);
2300        } else {
2301            self.push16(bus, self.regs.x);
2302        }
2303    }
2304    fn op_plx(&mut self, bus: &mut impl Bus) {
2305        if self.regs.x8() {
2306            let v = self.pull8(bus);
2307            self.regs.set_x(u16::from(v));
2308            self.regs.set_nz8(v);
2309        } else {
2310            let v = self.pull16(bus);
2311            self.regs.set_x(v);
2312            self.regs.set_nz16(v);
2313        }
2314    }
2315    fn op_phy(&mut self, bus: &mut impl Bus) {
2316        if self.regs.x8() {
2317            self.push8(bus, self.regs.y as u8);
2318        } else {
2319            self.push16(bus, self.regs.y);
2320        }
2321    }
2322    fn op_ply(&mut self, bus: &mut impl Bus) {
2323        if self.regs.x8() {
2324            let v = self.pull8(bus);
2325            self.regs.set_y(u16::from(v));
2326            self.regs.set_nz8(v);
2327        } else {
2328            let v = self.pull16(bus);
2329            self.regs.set_y(v);
2330            self.regs.set_nz16(v);
2331        }
2332    }
2333    fn op_plp(&mut self, bus: &mut impl Bus) {
2334        let v = self.pull8(bus);
2335        self.regs.p = Status::from_bits_truncate(v);
2336        if self.regs.emulation {
2337            // In emulation mode M and X are forced set.
2338            self.regs.set_flag(Status::M, true);
2339            self.regs.set_flag(Status::X, true);
2340        }
2341        if self.regs.x8() {
2342            // Re-zero index high bytes if X became 8-bit.
2343            self.regs.x &= 0x00FF;
2344            self.regs.y &= 0x00FF;
2345        }
2346    }
2347
2348    // ----------------------------------------------------------------------------------
2349    // REP / SEP / XCE.
2350    // ----------------------------------------------------------------------------------
2351
2352    fn op_rep(&mut self, mask: u8) {
2353        let new = Status::from_bits_truncate(self.regs.p.bits() & !mask);
2354        self.regs.p = new;
2355        if self.regs.emulation {
2356            self.regs.set_flag(Status::M, true);
2357            self.regs.set_flag(Status::X, true);
2358        }
2359    }
2360    fn op_sep(&mut self, mask: u8) {
2361        let new = Status::from_bits_truncate(self.regs.p.bits() | mask);
2362        self.regs.p = new;
2363        if self.regs.x8() {
2364            self.regs.x &= 0x00FF;
2365            self.regs.y &= 0x00FF;
2366        }
2367    }
2368    fn op_xce(&mut self) {
2369        let carry = self.regs.p.contains(Status::C);
2370        let e = self.regs.emulation;
2371        self.regs.set_flag(Status::C, e);
2372        self.regs.emulation = carry;
2373        if self.regs.emulation {
2374            self.regs.set_flag(Status::M, true);
2375            self.regs.set_flag(Status::X, true);
2376            self.regs.x &= 0x00FF;
2377            self.regs.y &= 0x00FF;
2378            self.regs.s = 0x0100 | (self.regs.s & 0x00FF);
2379        }
2380    }
2381
2382    // ----------------------------------------------------------------------------------
2383    // Branches.
2384    // ----------------------------------------------------------------------------------
2385
2386    fn branch(&mut self, bus: &mut impl Bus, taken: bool) {
2387        let off = self.fetch8(bus) as i8;
2388        if taken {
2389            self.io(bus);
2390            let old = self.regs.pc;
2391            let new = (old as i16).wrapping_add(i16::from(off)) as u16;
2392            if self.regs.emulation && (old & 0xFF00) != (new & 0xFF00) {
2393                self.io(bus);
2394            }
2395            self.regs.pc = new;
2396        }
2397    }
2398    fn op_brl(&mut self, bus: &mut impl Bus) {
2399        let off = self.fetch16(bus);
2400        self.io(bus);
2401        self.regs.pc = self.regs.pc.wrapping_add(off);
2402    }
2403
2404    // ----------------------------------------------------------------------------------
2405    // Calls / returns.
2406    // ----------------------------------------------------------------------------------
2407
2408    fn op_jsr(&mut self, bus: &mut impl Bus) {
2409        let target = self.fetch16(bus);
2410        self.io(bus);
2411        let ret = self.regs.pc.wrapping_sub(1);
2412        self.push16(bus, ret);
2413        self.regs.pc = target;
2414    }
2415    fn op_jsr_abs_x_ind(&mut self, bus: &mut impl Bus) {
2416        // JSR (abs,X): push return, then read pointer at PBR:(operand+X).
2417        let base = self.fetch16(bus);
2418        let ret = self.regs.pc.wrapping_sub(1);
2419        self.push16(bus, ret);
2420        self.io(bus);
2421        let ptr = base.wrapping_add(self.regs.x_val());
2422        let lo = self.bus_read8(bus, (u32::from(self.regs.pbr) << 16) | u32::from(ptr));
2423        let hi = self.bus_read8(
2424            bus,
2425            (u32::from(self.regs.pbr) << 16) | u32::from(ptr.wrapping_add(1)),
2426        );
2427        self.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
2428    }
2429    fn op_jsl(&mut self, bus: &mut impl Bus) {
2430        let lo = self.fetch8(bus);
2431        let hi = self.fetch8(bus);
2432        // ares `CallLong` pushes via `pushN` (full 16-bit S, no emulation page-1 confinement
2433        // mid-instruction); the boundary `normalize_emulation` re-confines S afterwards. Using
2434        // the page-locked `push8`/`push16` corrupts S when it crosses a page in emulation mode.
2435        self.push_n8(bus, self.regs.pbr);
2436        self.io(bus);
2437        let bank = self.fetch8(bus);
2438        let ret = self.regs.pc.wrapping_sub(1);
2439        self.push_n16(bus, ret);
2440        self.regs.pc = u16::from(lo) | (u16::from(hi) << 8);
2441        self.regs.pbr = bank;
2442    }
2443    fn op_rts(&mut self, bus: &mut impl Bus) {
2444        self.io(bus);
2445        self.io(bus);
2446        let pc = self.pull16(bus);
2447        self.io(bus);
2448        self.regs.pc = pc.wrapping_add(1);
2449    }
2450    fn op_rtl(&mut self, bus: &mut impl Bus) {
2451        self.io(bus);
2452        self.io(bus);
2453        // ares `instructionReturnLong` pulls via `pullN` (full 16-bit S, no page-1 confinement),
2454        // then forces `S.h = 0x01` at the boundary. Page-locked `pull16`/`pull8` corrupt S on a
2455        // page wrap in emulation mode. `normalize_emulation` re-applies the `S.h=0x01`.
2456        let pc = self.pull_n16(bus);
2457        let bank = self.pull_n8(bus);
2458        self.regs.pc = pc.wrapping_add(1);
2459        self.regs.pbr = bank;
2460    }
2461    fn op_rti(&mut self, bus: &mut impl Bus) {
2462        self.io(bus);
2463        self.io(bus);
2464        let p = self.pull8(bus);
2465        self.regs.p = Status::from_bits_truncate(p);
2466        if self.regs.emulation {
2467            self.regs.set_flag(Status::M, true);
2468            self.regs.set_flag(Status::X, true);
2469        }
2470        let pc = self.pull16(bus);
2471        self.regs.pc = pc;
2472        if !self.regs.emulation {
2473            let bank = self.pull8(bus);
2474            self.regs.pbr = bank;
2475        }
2476        if self.regs.x8() {
2477            self.regs.x &= 0x00FF;
2478            self.regs.y &= 0x00FF;
2479        }
2480    }
2481
2482    // ----------------------------------------------------------------------------------
2483    // Block moves. Each iteration moves one byte; PC stays on the instruction until X/Y/A
2484    // exhaust (A == 0xFFFF after the final decrement). We model the full transfer per step()
2485    // for simplicity (count is per-byte accurate; interruptibility is approximated).
2486    // ----------------------------------------------------------------------------------
2487
2488    /// `MVN` (`0x54`, adjust `+1`) — block move forward. Clean-room port of ares
2489    /// `instructionBlockMove8/16`: the source/dest addresses use the **full 16-bit** `X`/`Y`
2490    /// (`V.b<<16 | X.w`); only the *increment* respects the index width — `X.l += adjust` when
2491    /// `X=1` (8-bit index, high byte preserved), `X.w += adjust` when `X=0`. The loop tests `A.w`
2492    /// *then* post-decrements (`if(A.w--) PC.w -= 3`), so it moves `A+1` bytes.
2493    fn op_mvn(&mut self, bus: &mut impl Bus) {
2494        self.block_move(bus, 1);
2495    }
2496    /// `MVP` (`0x44`, adjust `-1`) — block move backward. See [`Self::op_mvn`].
2497    fn op_mvp(&mut self, bus: &mut impl Bus) {
2498        self.block_move(bus, -1i16 as u16);
2499    }
2500    /// Shared block-move body (ares `instructionBlockMove8/16`). `adjust` is `+1` (MVN) or `-1`
2501    /// (MVP, passed as `0xFFFF`).
2502    fn block_move(&mut self, bus: &mut impl Bus, adjust: u16) {
2503        let dst_bank = self.fetch8(bus);
2504        let src_bank = self.fetch8(bus);
2505        self.regs.dbr = dst_bank;
2506        // Address uses the full 16-bit X/Y regardless of index width (ares `X.w`).
2507        let src = (u32::from(src_bank) << 16) | u32::from(self.regs.x);
2508        let dst = (u32::from(dst_bank) << 16) | u32::from(self.regs.y);
2509        let b = self.bus_read8(bus, src);
2510        self.bus_write8(bus, dst, b);
2511        self.io(bus);
2512        self.io(bus);
2513        // Increment respects index width: 8-bit keeps the high byte (`X.l += adjust`).
2514        if self.regs.x8() {
2515            self.regs.x =
2516                (self.regs.x & 0xFF00) | u16::from((self.regs.x as u8).wrapping_add(adjust as u8));
2517            self.regs.y =
2518                (self.regs.y & 0xFF00) | u16::from((self.regs.y as u8).wrapping_add(adjust as u8));
2519        } else {
2520            self.regs.x = self.regs.x.wrapping_add(adjust);
2521            self.regs.y = self.regs.y.wrapping_add(adjust);
2522        }
2523        // `if(A.w--) PC.w -= 3`: test the pre-decrement value, then decrement.
2524        let continue_move = self.regs.a != 0;
2525        self.regs.a = self.regs.a.wrapping_sub(1);
2526        if continue_move {
2527            self.regs.pc = self.regs.pc.wrapping_sub(3);
2528        }
2529    }
2530
2531    // ----------------------------------------------------------------------------------
2532    // Software interrupts BRK / COP.
2533    // ----------------------------------------------------------------------------------
2534
2535    fn op_brk(&mut self, bus: &mut impl Bus) {
2536        // Signature byte already consumed by the caller; PC now points past it.
2537        if !self.regs.emulation {
2538            self.push8(bus, self.regs.pbr);
2539        }
2540        self.push16(bus, self.regs.pc);
2541        let mut status = self.regs.p.bits();
2542        if self.regs.emulation {
2543            status |= Status::X.bits(); // B set for BRK
2544        }
2545        self.push8(bus, status);
2546        self.regs.set_flag(Status::I, true);
2547        self.regs.set_flag(Status::D, false);
2548        let vector = if self.regs.emulation {
2549            vectors::IRQ_BRK_EMU
2550        } else {
2551            vectors::BRK_NATIVE
2552        };
2553        let pc = self.read16(bus, vector);
2554        self.regs.pc = pc;
2555        self.regs.pbr = 0;
2556    }
2557    fn op_cop(&mut self, bus: &mut impl Bus) {
2558        if !self.regs.emulation {
2559            self.push8(bus, self.regs.pbr);
2560        }
2561        self.push16(bus, self.regs.pc);
2562        self.push8(bus, self.regs.p.bits());
2563        self.regs.set_flag(Status::I, true);
2564        self.regs.set_flag(Status::D, false);
2565        let vector = if self.regs.emulation {
2566            vectors::COP_EMU
2567        } else {
2568            vectors::COP_NATIVE
2569        };
2570        let pc = self.read16(bus, vector);
2571        self.regs.pc = pc;
2572        self.regs.pbr = 0;
2573    }
2574}