jevsnes.git / third-party / rust / jgenesis / cpu / wdc65816-emu / src / core / instructions.rs
1mod alu;
2mod bits;
3mod disassemble;
4mod flags;
5mod flow;
6mod load;
7
8use crate::core::{InterruptType, Registers, SizeBits, Wdc65816};
9use crate::traits::BusInterface;
10use jgenesis_common::num::{GetBit, SignBit, U16Ext};
11
12fn u24_address(bank: u8, address: u16) -> u32 {
13    (u32::from(bank) << 16) | u32::from(address)
14}
15
16fn fetch_operand<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) -> u8 {
17    let operand = bus.read(u24_address(cpu.registers.pbr, cpu.registers.pc));
18    cpu.registers.pc = cpu.registers.pc.wrapping_add(1);
19
20    log::trace!(
21        "Fetched operand {operand:02X} from PBR {:02X} / PC {:04X}",
22        cpu.registers.pbr,
23        cpu.registers.pc.wrapping_sub(1)
24    );
25
26    operand
27}
28
29fn poll_interrupt_lines<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
30    let nmi = bus.nmi();
31    cpu.state.nmi_triggered |= !cpu.state.last_nmi && nmi;
32    cpu.state.last_nmi = nmi;
33
34    let irq = bus.irq();
35    cpu.state.irq_triggered = !cpu.registers.p.irq_disabled && irq;
36    cpu.state.last_irq = irq;
37}
38
39fn final_cycle<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
40    poll_interrupt_lines(cpu, bus);
41    cpu.state.cycle = 0;
42}
43
44fn ensure_page_1_stack(registers: &mut Registers) {
45    registers.s = 0x0100 | (registers.s & 0x00FF);
46}
47
48macro_rules! invalid_cycle {
49    ($cpu:expr) => {
50        panic!("Invalid cycle for opcode {:02X}: {}", $cpu.state.opcode, $cpu.state.cycle)
51    };
52}
53
54use invalid_cycle;
55
56macro_rules! impl_read_immediate_u8 {
57    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
58        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
59            final_cycle(cpu, bus);
60
61            let $value = fetch_operand(cpu, bus);
62            let $registers = &mut cpu.registers;
63            $body
64        }
65    };
66}
67
68use impl_read_immediate_u8;
69
70macro_rules! impl_read_immediate_u16 {
71    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
72        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
73            match cpu.state.cycle {
74                1 => {
75                    cpu.state.t0 = fetch_operand(cpu, bus);
76                }
77                2 => {
78                    final_cycle(cpu, bus);
79
80                    let msb = fetch_operand(cpu, bus);
81                    let $value = u16::from_le_bytes([cpu.state.t0, msb]);
82                    let $registers = &mut cpu.registers;
83                    $body
84                }
85                _ => invalid_cycle!(cpu),
86            }
87        }
88    };
89}
90
91use impl_read_immediate_u16;
92
93fn check_direct_page_low_byte(cpu: &mut Wdc65816) {
94    // Low byte of D being non-zero causes an extra cycle for direct page access; emulate this by
95    // skipping a cycle if it is zero
96    if cpu.registers.d & 0x00FF == 0 {
97        cpu.state.cycle += 1;
98    }
99}
100
101macro_rules! impl_read_direct_page_u8 {
102    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
103        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
104            match cpu.state.cycle {
105                1 => {
106                    cpu.state.t0 = fetch_operand(cpu, bus);
107                    check_direct_page_low_byte(cpu);
108                }
109                2 => {
110                    // Idle cycle if D LSB is non-zero
111                    bus.idle();
112                }
113                3 => {
114                    final_cycle(cpu, bus);
115
116                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
117                    let $value = bus.read(address.into());
118                    let $registers = &mut cpu.registers;
119                    $body
120                }
121                _ => invalid_cycle!(cpu),
122            }
123        }
124    };
125}
126
127use impl_read_direct_page_u8;
128
129macro_rules! impl_read_direct_page_u16 {
130    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
131        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
132            match cpu.state.cycle {
133                1 => {
134                    cpu.state.t0 = fetch_operand(cpu, bus);
135                    check_direct_page_low_byte(cpu);
136                }
137                2 => {
138                    // Idle cycle if D LSB is non-zero
139                    bus.idle();
140                }
141                3 => {
142                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
143                    cpu.state.t1 = bus.read(address.into());
144                }
145                4 => {
146                    final_cycle(cpu, bus);
147
148                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
149                    let value_msb = bus.read(address.wrapping_add(1).into());
150                    let $value = u16::from_le_bytes([cpu.state.t1, value_msb]);
151                    let $registers = &mut cpu.registers;
152                    $body
153                }
154                _ => invalid_cycle!(cpu),
155            }
156        }
157    };
158}
159
160use impl_read_direct_page_u16;
161
162fn index_direct_page(cpu: &Wdc65816, operand: u8, index: u16) -> u16 {
163    if cpu.registers.emulation_mode && cpu.registers.d & 0x00FF == 0 {
164        // If in emulation mode with D LSB equal to zero, indexing wraps at page boundary
165        (cpu.registers.d.wrapping_add(operand.into()).wrapping_add(index) & 0x00FF)
166            | (cpu.registers.d & 0xFF00)
167    } else {
168        cpu.registers.d.wrapping_add(operand.into()).wrapping_add(index)
169    }
170}
171
172macro_rules! impl_read_direct_page_indexed_u8 {
173    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
174        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
175            match cpu.state.cycle {
176                1 => {
177                    cpu.state.t0 = fetch_operand(cpu, bus);
178                    check_direct_page_low_byte(cpu);
179                }
180                2 => {
181                    // Idle cycle if D LSB is non-zero
182                    bus.idle();
183                }
184                3 => {
185                    // Idle cycle for adding index
186                    bus.idle();
187                }
188                4 => {
189                    final_cycle(cpu, bus);
190
191                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
192                    let $value = bus.read(address.into());
193                    let $registers = &mut cpu.registers;
194                    $body
195                }
196                _ => invalid_cycle!(cpu),
197            }
198        }
199    };
200}
201
202use impl_read_direct_page_indexed_u8;
203
204macro_rules! impl_read_direct_page_indexed_u16 {
205    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
206        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
207            match cpu.state.cycle {
208                1 => {
209                    cpu.state.t0 = fetch_operand(cpu, bus);
210                    check_direct_page_low_byte(cpu);
211                }
212                2 => {
213                    // Idle cycle if D LSB is non-zero
214                    bus.idle();
215                }
216                3 => {
217                    // Idle cycle for adding index
218                    bus.idle();
219                }
220                4 => {
221                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
222                    cpu.state.t1 = bus.read(address.into());
223                }
224                5 => {
225                    final_cycle(cpu, bus);
226
227                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
228                    let value_msb = bus.read(address.wrapping_add(1).into());
229                    let $value = u16::from_le_bytes([cpu.state.t1, value_msb]);
230                    let $registers = &mut cpu.registers;
231                    $body
232                }
233                _ => invalid_cycle!(cpu),
234            }
235        }
236    };
237}
238
239use impl_read_direct_page_indexed_u16;
240
241macro_rules! impl_read_direct_page_indirect_u8 {
242    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
243        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
244            match cpu.state.cycle {
245                1 => {
246                    cpu.state.t0 = fetch_operand(cpu, bus);
247                    check_direct_page_low_byte(cpu);
248                }
249                2 => {
250                    // Idle cycle if D LSB is non-zero
251                    bus.idle();
252                }
253                3 => {
254                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
255                    cpu.state.t1 = bus.read(address.into());
256                }
257                4 => {
258                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
259                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
260                }
261                5 => {
262                    final_cycle(cpu, bus);
263
264                    let address = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
265                    let $value = bus.read(u24_address(cpu.registers.dbr, address));
266                    let $registers = &mut cpu.registers;
267                    $body
268                }
269                _ => invalid_cycle!(cpu),
270            }
271        }
272    };
273}
274
275use impl_read_direct_page_indirect_u8;
276
277macro_rules! impl_read_direct_page_indirect_u16 {
278    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
279        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
280            match cpu.state.cycle {
281                1 => {
282                    cpu.state.t0 = fetch_operand(cpu, bus);
283                    check_direct_page_low_byte(cpu);
284                }
285                2 => {
286                    // Idle cycle if D LSB is non-zero
287                    bus.idle();
288                }
289                3 => {
290                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
291                    cpu.state.t1 = bus.read(address.into());
292                }
293                4 => {
294                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
295                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
296                }
297                5 => {
298                    let address = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
299                    cpu.state.t3 = bus.read(u24_address(cpu.registers.dbr, address));
300                }
301                6 => {
302                    final_cycle(cpu, bus);
303
304                    let base_addr = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
305                    let address = (u24_address(cpu.registers.dbr, base_addr) + 1) & B::ADDRESS_MASK;
306                    let value_msb = bus.read(address);
307                    let $value = u16::from_le_bytes([cpu.state.t3, value_msb]);
308                    let $registers = &mut cpu.registers;
309                    $body
310                }
311                _ => invalid_cycle!(cpu),
312            }
313        }
314    };
315}
316
317use impl_read_direct_page_indirect_u16;
318
319macro_rules! impl_read_direct_page_indexed_indirect_u8 {
320    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
321        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
322            match cpu.state.cycle {
323                1 => {
324                    cpu.state.t0 = fetch_operand(cpu, bus);
325                    check_direct_page_low_byte(cpu);
326                }
327                2 => {
328                    // Idle cycle if D LSB is non-zero
329                    bus.idle();
330                }
331                3 => {
332                    // Idle cycle to add index
333                    bus.idle();
334                }
335                4 => {
336                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x);
337                    cpu.state.t1 = bus.read(address.into());
338                }
339                5 => {
340                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x);
341                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
342                }
343                6 => {
344                    final_cycle(cpu, bus);
345
346                    let base_addr = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
347                    let address = u24_address(cpu.registers.dbr, base_addr);
348                    let $value = bus.read(address);
349                    let $registers = &mut cpu.registers;
350                    $body
351                }
352                _ => invalid_cycle!(cpu),
353            }
354        }
355    };
356}
357
358use impl_read_direct_page_indexed_indirect_u8;
359
360macro_rules! impl_read_direct_page_indexed_indirect_u16 {
361    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
362        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
363            match cpu.state.cycle {
364                1 => {
365                    cpu.state.t0 = fetch_operand(cpu, bus);
366                    check_direct_page_low_byte(cpu);
367                }
368                2 => {
369                    // Idle cycle if D LSB is non-zero
370                    bus.idle();
371                }
372                3 => {
373                    // Idle cycle to add index
374                    bus.idle();
375                }
376                4 => {
377                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x);
378                    cpu.state.t1 = bus.read(address.into());
379                }
380                5 => {
381                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.x);
382                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
383                }
384                6 => {
385                    let base_addr = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
386                    let address = u24_address(cpu.registers.dbr, base_addr);
387                    cpu.state.t3 = bus.read(address);
388                }
389                7 => {
390                    final_cycle(cpu, bus);
391
392                    let base_addr = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
393                    let address = (u24_address(cpu.registers.dbr, base_addr) + 1) & B::ADDRESS_MASK;
394                    let value_msb = bus.read(address);
395                    let $value = u16::from_le_bytes([cpu.state.t3, value_msb]);
396                    let $registers = &mut cpu.registers;
397                    $body
398                }
399                _ => invalid_cycle!(cpu),
400            }
401        }
402    };
403}
404
405use impl_read_direct_page_indexed_indirect_u16;
406
407macro_rules! impl_read_direct_page_indirect_indexed_u8 {
408    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
409        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
410            match cpu.state.cycle {
411                1 => {
412                    cpu.state.t0 = fetch_operand(cpu, bus);
413                    check_direct_page_low_byte(cpu);
414                }
415                2 => {
416                    // Idle cycle if D LSB is non-zero
417                    bus.idle();
418                }
419                3 => {
420                    let address = index_direct_page(cpu, cpu.state.t0, 0);
421                    cpu.state.t1 = bus.read(address.into());
422                }
423                4 => {
424                    let address = index_direct_page(cpu, cpu.state.t0, 0);
425                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
426                    check_absolute_index(
427                        cpu,
428                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
429                        cpu.registers.y,
430                    );
431                }
432                5 => {
433                    // Idle cycle for adding index
434                    bus.idle();
435                }
436                6 => {
437                    final_cycle(cpu, bus);
438
439                    let base_addr = u24_address(
440                        cpu.registers.dbr,
441                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
442                    );
443                    let index: u32 = cpu.registers.y.into();
444                    let address = (base_addr + index) & B::ADDRESS_MASK;
445                    let $value = bus.read(address);
446                    let $registers = &mut cpu.registers;
447                    $body
448                }
449                _ => invalid_cycle!(cpu),
450            }
451        }
452    };
453}
454
455use impl_read_direct_page_indirect_indexed_u8;
456
457macro_rules! impl_read_direct_page_indirect_indexed_u16 {
458    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
459        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
460            match cpu.state.cycle {
461                1 => {
462                    cpu.state.t0 = fetch_operand(cpu, bus);
463                    check_direct_page_low_byte(cpu);
464                }
465                2 => {
466                    // Idle cycle if D LSB is non-zero
467                    bus.idle();
468                }
469                3 => {
470                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
471                    cpu.state.t1 = bus.read(address.into());
472                }
473                4 => {
474                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into()).wrapping_add(1);
475                    cpu.state.t2 = bus.read(address.into());
476                    check_absolute_index(
477                        cpu,
478                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
479                        cpu.registers.y,
480                    );
481                }
482                5 => {
483                    // Idle cycle for adding index
484                    bus.idle();
485                }
486                6 => {
487                    let base_addr = u24_address(
488                        cpu.registers.dbr,
489                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
490                    );
491                    let index: u32 = cpu.registers.y.into();
492                    let address = (base_addr + index) & B::ADDRESS_MASK;
493                    cpu.state.t3 = bus.read(address);
494                }
495                7 => {
496                    final_cycle(cpu, bus);
497
498                    let base_addr = u24_address(
499                        cpu.registers.dbr,
500                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
501                    );
502                    let index: u32 = cpu.registers.y.into();
503                    let address = (base_addr + index + 1) & B::ADDRESS_MASK;
504                    let value_msb = bus.read(address);
505                    let $value = u16::from_le_bytes([cpu.state.t3, value_msb]);
506                    let $registers = &mut cpu.registers;
507                    $body
508                }
509                _ => invalid_cycle!(cpu),
510            }
511        }
512    };
513}
514
515use impl_read_direct_page_indirect_indexed_u16;
516
517macro_rules! impl_read_direct_page_indirect_long_u8 {
518    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
519        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
520            match cpu.state.cycle {
521                1 => {
522                    cpu.state.t0 = fetch_operand(cpu, bus);
523                    check_direct_page_low_byte(cpu);
524                }
525                2 => {
526                    // Idle cycle if D LSB is non-zero
527                    bus.idle();
528                }
529                3 => {
530                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
531                    cpu.state.t1 = bus.read(address.into());
532                }
533                4 => {
534                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
535                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
536                }
537                5 => {
538                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
539                    cpu.state.t3 = bus.read(address.wrapping_add(2).into());
540                }
541                6 => {
542                    final_cycle(cpu, bus);
543
544                    let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]);
545                    let $value = bus.read(address);
546                    let $registers = &mut cpu.registers;
547                    $body
548                }
549                _ => invalid_cycle!(cpu),
550            }
551        }
552    };
553}
554
555use impl_read_direct_page_indirect_long_u8;
556
557macro_rules! impl_read_direct_page_indirect_long_u16 {
558    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
559        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
560            match cpu.state.cycle {
561                1 => {
562                    cpu.state.t0 = fetch_operand(cpu, bus);
563                    check_direct_page_low_byte(cpu);
564                }
565                2 => {
566                    // Idle cycle if D LSB is non-zero
567                    bus.idle();
568                }
569                3 => {
570                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
571                    cpu.state.t1 = bus.read(address.into());
572                }
573                4 => {
574                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
575                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
576                }
577                5 => {
578                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
579                    cpu.state.t3 = bus.read(address.wrapping_add(2).into());
580                }
581                6 => {
582                    let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]);
583                    cpu.state.t4 = bus.read(address);
584                }
585                7 => {
586                    final_cycle(cpu, bus);
587
588                    let address = u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]);
589                    let value_msb = bus.read((address + 1) & B::ADDRESS_MASK);
590                    let $value = u16::from_le_bytes([cpu.state.t4, value_msb]);
591                    let $registers = &mut cpu.registers;
592                    $body
593                }
594                _ => invalid_cycle!(cpu),
595            }
596        }
597    };
598}
599
600use impl_read_direct_page_indirect_long_u16;
601
602macro_rules! impl_read_direct_page_indirect_long_indexed_u8 {
603    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
604        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
605            match cpu.state.cycle {
606                1 => {
607                    cpu.state.t0 = fetch_operand(cpu, bus);
608                    check_direct_page_low_byte(cpu);
609                }
610                2 => {
611                    // Idle cycle if D LSB is non-zero
612                    bus.idle();
613                }
614                3 => {
615                    let address = index_direct_page(cpu, cpu.state.t0, 0);
616                    cpu.state.t1 = bus.read(address.into());
617                }
618                4 => {
619                    let address = index_direct_page(cpu, cpu.state.t0, 1);
620                    cpu.state.t2 = bus.read(address.into());
621                }
622                5 => {
623                    let address = index_direct_page(cpu, cpu.state.t0, 2);
624                    cpu.state.t3 = bus.read(address.into());
625                }
626                6 => {
627                    final_cycle(cpu, bus);
628
629                    let base_addr =
630                        u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]);
631                    let index: u32 = cpu.registers.y.into();
632                    let address = (base_addr + index) & B::ADDRESS_MASK;
633                    let $value = bus.read(address);
634                    let $registers = &mut cpu.registers;
635                    $body
636                }
637                _ => invalid_cycle!(cpu),
638            }
639        }
640    };
641}
642
643use impl_read_direct_page_indirect_long_indexed_u8;
644
645macro_rules! impl_read_direct_page_indirect_long_indexed_u16 {
646    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
647        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
648            match cpu.state.cycle {
649                1 => {
650                    cpu.state.t0 = fetch_operand(cpu, bus);
651                    check_direct_page_low_byte(cpu);
652                }
653                2 => {
654                    // Idle cycle if D LSB is non-zero
655                    bus.idle();
656                }
657                3 => {
658                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
659                    cpu.state.t1 = bus.read(address.into());
660                }
661                4 => {
662                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
663                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
664                }
665                5 => {
666                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
667                    cpu.state.t3 = bus.read(address.wrapping_add(2).into());
668                }
669                6 => {
670                    let base_addr =
671                        u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]);
672                    let index: u32 = cpu.registers.y.into();
673                    let address = (base_addr + index) & B::ADDRESS_MASK;
674                    cpu.state.t4 = bus.read(address);
675                }
676                7 => {
677                    final_cycle(cpu, bus);
678
679                    let base_addr =
680                        u32::from_le_bytes([cpu.state.t1, cpu.state.t2, cpu.state.t3, 0]);
681                    let index: u32 = cpu.registers.y.into();
682                    let address = (base_addr + index + 1) & B::ADDRESS_MASK;
683                    let value_msb = bus.read(address);
684                    let $value = u16::from_le_bytes([cpu.state.t4, value_msb]);
685                    let $registers = &mut cpu.registers;
686                    $body
687                }
688                _ => invalid_cycle!(cpu),
689            }
690        }
691    };
692}
693
694use impl_read_direct_page_indirect_long_indexed_u16;
695
696macro_rules! impl_read_absolute_u8 {
697    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
698        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
699            match cpu.state.cycle {
700                1 => {
701                    cpu.state.t0 = fetch_operand(cpu, bus);
702                }
703                2 => {
704                    cpu.state.t1 = fetch_operand(cpu, bus);
705                }
706                3 => {
707                    final_cycle(cpu, bus);
708
709                    let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
710                    let $value = bus.read(u24_address(cpu.registers.dbr, address));
711                    let $registers = &mut cpu.registers;
712                    $body
713                }
714                _ => invalid_cycle!(cpu),
715            }
716        }
717    };
718}
719
720use impl_read_absolute_u8;
721
722macro_rules! impl_read_absolute_u16 {
723    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
724        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
725            match cpu.state.cycle {
726                1 => {
727                    cpu.state.t0 = fetch_operand(cpu, bus);
728                }
729                2 => {
730                    cpu.state.t1 = fetch_operand(cpu, bus);
731                }
732                3 => {
733                    let base_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
734                    cpu.state.t2 = bus.read(u24_address(cpu.registers.dbr, base_addr));
735                }
736                4 => {
737                    final_cycle(cpu, bus);
738
739                    let base_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
740                    let address = (u24_address(cpu.registers.dbr, base_addr) + 1) & B::ADDRESS_MASK;
741                    let value_msb = bus.read(address);
742                    let $value = u16::from_le_bytes([cpu.state.t2, value_msb]);
743                    let $registers = &mut cpu.registers;
744                    $body
745                }
746                _ => invalid_cycle!(cpu),
747            }
748        }
749    };
750}
751
752use impl_read_absolute_u16;
753
754fn check_absolute_index(cpu: &mut Wdc65816, absolute_addr: u16, index: u16) {
755    // Absolute X/Y reads skip the idle cycle if index is 8-bit and indexing does not cross a page
756    // boundary
757    if cpu.registers.p.index_size == SizeBits::Eight
758        && absolute_addr.wrapping_add(index) & 0xFF00 == absolute_addr & 0xFF00
759    {
760        cpu.state.cycle += 1;
761    }
762}
763
764macro_rules! impl_read_absolute_indexed_u8 {
765    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
766        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
767            match cpu.state.cycle {
768                1 => {
769                    cpu.state.t0 = fetch_operand(cpu, bus);
770                }
771                2 => {
772                    cpu.state.t1 = fetch_operand(cpu, bus);
773                    check_absolute_index(
774                        cpu,
775                        u16::from_le_bytes([cpu.state.t0, cpu.state.t1]),
776                        cpu.registers.$index,
777                    );
778                }
779                3 => {
780                    // Idle cycle for adding index, taken if either of the following is true:
781                    // - x flag is set (8-bit index) and indexing crosses a page boundary
782                    // - x flag is not set (16-bit index)
783                    bus.idle();
784                }
785                4 => {
786                    final_cycle(cpu, bus);
787
788                    let base_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
789                    let index: u32 = cpu.registers.$index.into();
790                    let address =
791                        (u24_address(cpu.registers.dbr, base_addr) + index) & B::ADDRESS_MASK;
792                    let $value = bus.read(address);
793                    let $registers = &mut cpu.registers;
794                    $body
795                }
796                _ => invalid_cycle!(cpu),
797            }
798        }
799    };
800}
801
802use impl_read_absolute_indexed_u8;
803
804macro_rules! impl_read_absolute_indexed_u16 {
805    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
806        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
807            match cpu.state.cycle {
808                1 => {
809                    cpu.state.t0 = fetch_operand(cpu, bus);
810                }
811                2 => {
812                    cpu.state.t1 = fetch_operand(cpu, bus);
813                    check_absolute_index(
814                        cpu,
815                        u16::from_le_bytes([cpu.state.t0, cpu.state.t1]),
816                        cpu.registers.$index,
817                    );
818                }
819                3 => {
820                    // Idle cycle for adding index
821                    bus.idle();
822                }
823                4 => {
824                    let base_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
825                    let index: u32 = cpu.registers.$index.into();
826                    let address =
827                        (u24_address(cpu.registers.dbr, base_addr) + index) & B::ADDRESS_MASK;
828                    cpu.state.t2 = bus.read(address);
829                }
830                5 => {
831                    final_cycle(cpu, bus);
832
833                    let base_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
834                    let index: u32 = cpu.registers.$index.into();
835                    let address =
836                        (u24_address(cpu.registers.dbr, base_addr) + index + 1) & B::ADDRESS_MASK;
837                    let value_msb = bus.read(address);
838                    let $value = u16::from_le_bytes([cpu.state.t2, value_msb]);
839                    let $registers = &mut cpu.registers;
840                    $body
841                }
842                _ => invalid_cycle!(cpu),
843            }
844        }
845    };
846}
847
848use impl_read_absolute_indexed_u16;
849
850macro_rules! impl_read_absolute_long_u8 {
851    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
852        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
853            match cpu.state.cycle {
854                1 => {
855                    cpu.state.t0 = fetch_operand(cpu, bus);
856                }
857                2 => {
858                    cpu.state.t1 = fetch_operand(cpu, bus);
859                }
860                3 => {
861                    cpu.state.t2 = fetch_operand(cpu, bus);
862                }
863                4 => {
864                    final_cycle(cpu, bus);
865
866                    let address = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]);
867                    let $value = bus.read(address);
868                    let $registers = &mut cpu.registers;
869                    $body
870                }
871                _ => invalid_cycle!(cpu),
872            }
873        }
874    };
875}
876
877use impl_read_absolute_long_u8;
878
879macro_rules! impl_read_absolute_long_u16 {
880    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
881        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
882            match cpu.state.cycle {
883                1 => {
884                    cpu.state.t0 = fetch_operand(cpu, bus);
885                }
886                2 => {
887                    cpu.state.t1 = fetch_operand(cpu, bus);
888                }
889                3 => {
890                    cpu.state.t2 = fetch_operand(cpu, bus);
891                }
892                4 => {
893                    let address = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]);
894                    cpu.state.t3 = bus.read(address);
895                }
896                5 => {
897                    final_cycle(cpu, bus);
898
899                    let address = u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]);
900                    let value_msb = bus.read((address + 1) & B::ADDRESS_MASK);
901                    let $value = u16::from_le_bytes([cpu.state.t3, value_msb]);
902                    let $registers = &mut cpu.registers;
903                    $body
904                }
905                _ => invalid_cycle!(cpu),
906            }
907        }
908    };
909}
910
911use impl_read_absolute_long_u16;
912
913macro_rules! impl_read_absolute_long_indexed_u8 {
914    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
915        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
916            match cpu.state.cycle {
917                1 => {
918                    cpu.state.t0 = fetch_operand(cpu, bus);
919                }
920                2 => {
921                    cpu.state.t1 = fetch_operand(cpu, bus);
922                }
923                3 => {
924                    cpu.state.t2 = fetch_operand(cpu, bus);
925                }
926                4 => {
927                    final_cycle(cpu, bus);
928
929                    let base_addr =
930                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]);
931                    let index: u32 = cpu.registers.$index.into();
932                    let address = (base_addr + index) & B::ADDRESS_MASK;
933                    let $value = bus.read(address);
934                    let $registers = &mut cpu.registers;
935                    $body
936                }
937                _ => invalid_cycle!(cpu),
938            }
939        }
940    };
941}
942
943use impl_read_absolute_long_indexed_u8;
944
945macro_rules! impl_read_absolute_long_indexed_u16 {
946    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
947        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
948            match cpu.state.cycle {
949                1 => {
950                    cpu.state.t0 = fetch_operand(cpu, bus);
951                }
952                2 => {
953                    cpu.state.t1 = fetch_operand(cpu, bus);
954                }
955                3 => {
956                    cpu.state.t2 = fetch_operand(cpu, bus);
957                }
958                4 => {
959                    let base_addr =
960                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]);
961                    let index: u32 = cpu.registers.$index.into();
962                    let address = (base_addr + index) & B::ADDRESS_MASK;
963                    cpu.state.t3 = bus.read(address);
964                }
965                5 => {
966                    final_cycle(cpu, bus);
967
968                    let base_addr =
969                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.state.t2, 0]);
970                    let index: u32 = cpu.registers.$index.into();
971                    let address = (base_addr + index + 1) & B::ADDRESS_MASK;
972                    let value_msb = bus.read(address);
973                    let $value = u16::from_le_bytes([cpu.state.t3, value_msb]);
974                    let $registers = &mut cpu.registers;
975                    $body
976                }
977                _ => invalid_cycle!(cpu),
978            }
979        }
980    };
981}
982
983use impl_read_absolute_long_indexed_u16;
984
985macro_rules! impl_read_stack_relative_u8 {
986    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
987        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
988            match cpu.state.cycle {
989                1 => {
990                    cpu.state.t0 = fetch_operand(cpu, bus);
991                }
992                2 => {
993                    // Idle cycle for indexing into stack
994                    bus.idle();
995                }
996                3 => {
997                    final_cycle(cpu, bus);
998
999                    let address = cpu.registers.s.wrapping_add(cpu.state.t0.into());
1000                    let $value = bus.read(address.into());
1001                    let $registers = &mut cpu.registers;
1002                    $body
1003                }
1004                _ => invalid_cycle!(cpu),
1005            }
1006        }
1007    };
1008}
1009
1010use impl_read_stack_relative_u8;
1011
1012macro_rules! impl_read_stack_relative_u16 {
1013    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1014        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1015            match cpu.state.cycle {
1016                1 => {
1017                    cpu.state.t0 = fetch_operand(cpu, bus);
1018                }
1019                2 => {
1020                    // Idle cycle for indexing into stack
1021                    bus.idle();
1022                }
1023                3 => {
1024                    let address = cpu.registers.s.wrapping_add(cpu.state.t0.into());
1025                    cpu.state.t1 = bus.read(address.into());
1026                }
1027                4 => {
1028                    final_cycle(cpu, bus);
1029
1030                    let address = cpu.registers.s.wrapping_add(cpu.state.t0.into());
1031                    let value_msb = bus.read(address.wrapping_add(1).into());
1032                    let $value = u16::from_le_bytes([cpu.state.t1, value_msb]);
1033                    let $registers = &mut cpu.registers;
1034                    $body
1035                }
1036                _ => invalid_cycle!(cpu),
1037            }
1038        }
1039    };
1040}
1041
1042use impl_read_stack_relative_u16;
1043
1044macro_rules! impl_read_stack_relative_indirect_indexed_u8 {
1045    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1046        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1047            match cpu.state.cycle {
1048                1 => {
1049                    cpu.state.t0 = fetch_operand(cpu, bus);
1050                }
1051                2 => {
1052                    // Idle cycle for indexing into stack
1053                    bus.idle();
1054                }
1055                3 => {
1056                    let address = cpu.registers.s.wrapping_add(cpu.state.t0.into());
1057                    cpu.state.t1 = bus.read(address.into());
1058                }
1059                4 => {
1060                    let address = cpu.registers.s.wrapping_add(cpu.state.t0.into());
1061                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
1062                }
1063                5 => {
1064                    // Idle cycle for indexing with Y
1065                    bus.idle();
1066                }
1067                6 => {
1068                    final_cycle(cpu, bus);
1069
1070                    let base_addr = u24_address(
1071                        cpu.registers.dbr,
1072                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
1073                    );
1074                    let index: u32 = cpu.registers.y.into();
1075                    let address = (base_addr + index) & B::ADDRESS_MASK;
1076                    let $value = bus.read(address);
1077                    let $registers = &mut cpu.registers;
1078                    $body
1079                }
1080                _ => invalid_cycle!(cpu),
1081            }
1082        }
1083    };
1084}
1085
1086use impl_read_stack_relative_indirect_indexed_u8;
1087
1088macro_rules! impl_read_stack_relative_indirect_indexed_u16 {
1089    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1090        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1091            match cpu.state.cycle {
1092                1 => {
1093                    cpu.state.t0 = fetch_operand(cpu, bus);
1094                }
1095                2 => {
1096                    // Idle cycle for indexing into stack
1097                    bus.idle();
1098                }
1099                3 => {
1100                    let address = cpu.registers.s.wrapping_add(cpu.state.t0.into());
1101                    cpu.state.t1 = bus.read(address.into());
1102                }
1103                4 => {
1104                    let address = cpu.registers.s.wrapping_add(cpu.state.t0.into());
1105                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
1106                }
1107                5 => {
1108                    // Idle cycle for indexing with Y
1109                    bus.idle();
1110                }
1111                6 => {
1112                    let base_addr = u24_address(
1113                        cpu.registers.dbr,
1114                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
1115                    );
1116                    let index: u32 = cpu.registers.y.into();
1117                    let address = (base_addr + index) & B::ADDRESS_MASK;
1118                    cpu.state.t3 = bus.read(address);
1119                }
1120                7 => {
1121                    final_cycle(cpu, bus);
1122
1123                    let base_addr = u24_address(
1124                        cpu.registers.dbr,
1125                        u16::from_le_bytes([cpu.state.t1, cpu.state.t2]),
1126                    );
1127                    let index: u32 = cpu.registers.y.into();
1128                    let address = (base_addr + index + 1) & B::ADDRESS_MASK;
1129                    let value_msb = bus.read(address);
1130                    let $value = u16::from_le_bytes([cpu.state.t3, value_msb]);
1131                    let $registers = &mut cpu.registers;
1132                    $body
1133                }
1134                _ => invalid_cycle!(cpu),
1135            }
1136        }
1137    };
1138}
1139
1140use impl_read_stack_relative_indirect_indexed_u16;
1141
1142macro_rules! impl_read_op_u8 {
1143    (@inner immediate, $($rest:tt)*) => {
1144        impl_read_immediate_u8!($($rest)*);
1145    };
1146    (@inner direct_page, $($rest:tt)*) => {
1147        impl_read_direct_page_u8!($($rest)*);
1148    };
1149    (@inner direct_page_x, $($rest:tt)*) => {
1150        impl_read_direct_page_indexed_u8!(x, $($rest)*);
1151    };
1152    (@inner direct_page_y, $($rest:tt)*) => {
1153        impl_read_direct_page_indexed_u8!(y, $($rest)*);
1154    };
1155    (@inner direct_page_indirect, $($rest:tt)*) => {
1156        impl_read_direct_page_indirect_u8!($($rest)*);
1157    };
1158    (@inner direct_page_indexed_indirect, $($rest:tt)*) => {
1159        impl_read_direct_page_indexed_indirect_u8!($($rest)*);
1160    };
1161    (@inner direct_page_indirect_indexed, $($rest:tt)*) => {
1162        impl_read_direct_page_indirect_indexed_u8!($($rest)*);
1163    };
1164    (@inner direct_page_indirect_long, $($rest:tt)*) => {
1165        impl_read_direct_page_indirect_long_u8!($($rest)*);
1166    };
1167    (@inner direct_page_indirect_long_indexed, $($rest:tt)*) => {
1168        impl_read_direct_page_indirect_long_indexed_u8!($($rest)*);
1169    };
1170    (@inner absolute, $($rest:tt)*) => {
1171        impl_read_absolute_u8!($($rest)*);
1172    };
1173    (@inner absolute_x, $($rest:tt)*) => {
1174        impl_read_absolute_indexed_u8!(x, $($rest)*);
1175    };
1176    (@inner absolute_y, $($rest:tt)*) => {
1177        impl_read_absolute_indexed_u8!(y, $($rest)*);
1178    };
1179    (@inner absolute_long, $($rest:tt)*) => {
1180        impl_read_absolute_long_u8!($($rest)*);
1181    };
1182    (@inner absolute_long_x, $($rest:tt)*) => {
1183        impl_read_absolute_long_indexed_u8!(x, $($rest)*);
1184    };
1185    (@inner stack_relative, $($rest:tt)*) => {
1186        impl_read_stack_relative_u8!($($rest)*);
1187    };
1188    (@inner stack_relative_indirect_indexed, $($rest:tt)*) => {
1189        impl_read_stack_relative_indirect_indexed_u8!($($rest)*);
1190    };
1191    ([$($am:tt -> $name:ident),* $(,)?] $rest:tt) => {
1192        $(
1193            impl_read_op_u8!(@inner $am, $name, $rest);
1194        )*
1195    };
1196}
1197
1198use impl_read_op_u8;
1199
1200macro_rules! impl_read_op_u16 {
1201    (@inner immediate, $($rest:tt)*) => {
1202        impl_read_immediate_u16!($($rest)*);
1203    };
1204    (@inner direct_page, $($rest:tt)*) => {
1205        impl_read_direct_page_u16!($($rest)*);
1206    };
1207    (@inner direct_page_x, $($rest:tt)*) => {
1208        impl_read_direct_page_indexed_u16!(x, $($rest)*);
1209    };
1210    (@inner direct_page_y, $($rest:tt)*) => {
1211        impl_read_direct_page_indexed_u16!(y, $($rest)*);
1212    };
1213    (@inner direct_page_indirect, $($rest:tt)*) => {
1214        impl_read_direct_page_indirect_u16!($($rest)*);
1215    };
1216    (@inner direct_page_indexed_indirect, $($rest:tt)*) => {
1217        impl_read_direct_page_indexed_indirect_u16!($($rest)*);
1218    };
1219    (@inner direct_page_indirect_indexed, $($rest:tt)*) => {
1220        impl_read_direct_page_indirect_indexed_u16!($($rest)*);
1221    };
1222    (@inner direct_page_indirect_long, $($rest:tt)*) => {
1223        impl_read_direct_page_indirect_long_u16!($($rest)*);
1224    };
1225    (@inner direct_page_indirect_long_indexed, $($rest:tt)*) => {
1226        impl_read_direct_page_indirect_long_indexed_u16!($($rest)*);
1227    };
1228    (@inner absolute, $($rest:tt)*) => {
1229        impl_read_absolute_u16!($($rest)*);
1230    };
1231    (@inner absolute_x, $($rest:tt)*) => {
1232        impl_read_absolute_indexed_u16!(x, $($rest)*);
1233    };
1234    (@inner absolute_y, $($rest:tt)*) => {
1235        impl_read_absolute_indexed_u16!(y, $($rest)*);
1236    };
1237    (@inner absolute_long, $($rest:tt)*) => {
1238        impl_read_absolute_long_u16!($($rest)*);
1239    };
1240    (@inner absolute_long_x, $($rest:tt)*) => {
1241        impl_read_absolute_long_indexed_u16!(x, $($rest)*);
1242    };
1243    (@inner stack_relative, $($rest:tt)*) => {
1244        impl_read_stack_relative_u16!($($rest)*);
1245    };
1246    (@inner stack_relative_indirect_indexed, $($rest:tt)*) => {
1247        impl_read_stack_relative_indirect_indexed_u16!($($rest)*);
1248    };
1249    ([$($am:tt -> $name:ident),* $(,)?] $rest:tt) => {
1250        $(
1251            impl_read_op_u16!(@inner $am, $name, $rest);
1252        )*
1253    }
1254}
1255
1256use impl_read_op_u16;
1257
1258macro_rules! impl_modify_accumulator_u8 {
1259    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1260        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1261            final_cycle(cpu, bus);
1262
1263            bus.idle();
1264
1265            let $value = cpu.registers.a as u8;
1266            let $registers = &mut cpu.registers;
1267            let value = $body;
1268            cpu.registers.a.set_lsb(value);
1269        }
1270    };
1271}
1272
1273use impl_modify_accumulator_u8;
1274
1275macro_rules! impl_modify_accumulator_u16 {
1276    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1277        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1278            final_cycle(cpu, bus);
1279
1280            bus.idle();
1281
1282            let $value = cpu.registers.a;
1283            let $registers = &mut cpu.registers;
1284            let value = $body;
1285            cpu.registers.a = value;
1286        }
1287    };
1288}
1289
1290use impl_modify_accumulator_u16;
1291
1292macro_rules! impl_modify_direct_page_u8 {
1293    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1294        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1295            match cpu.state.cycle {
1296                1 => {
1297                    cpu.state.t0 = fetch_operand(cpu, bus);
1298                    check_direct_page_low_byte(cpu);
1299                }
1300                2 => {
1301                    // Idle cycle if D LSB is non-zero
1302                    bus.idle();
1303                }
1304                3 => {
1305                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
1306                    cpu.state.t1 = bus.read(address.into());
1307                }
1308                4 => {
1309                    if cpu.registers.emulation_mode {
1310                        // Write value back to address
1311                        let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
1312                        bus.write(address.into(), cpu.state.t1);
1313                    } else {
1314                        // Idle cycle for computation
1315                        bus.idle();
1316                    }
1317                }
1318                5 => {
1319                    final_cycle(cpu, bus);
1320
1321                    let $value = cpu.state.t1;
1322                    let $registers = &mut cpu.registers;
1323                    let value = $body;
1324
1325                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
1326                    bus.write(address.into(), value);
1327                }
1328                _ => invalid_cycle!(cpu),
1329            }
1330        }
1331    };
1332}
1333
1334use impl_modify_direct_page_u8;
1335
1336macro_rules! impl_modify_direct_page_u16 {
1337    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1338        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1339            match cpu.state.cycle {
1340                1 => {
1341                    cpu.state.t0 = fetch_operand(cpu, bus);
1342                    check_direct_page_low_byte(cpu);
1343                }
1344                2 => {
1345                    // Idle cycle if D LSB is non-zero
1346                    bus.idle();
1347                }
1348                3 => {
1349                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
1350                    cpu.state.t1 = bus.read(address.into());
1351                }
1352                4 => {
1353                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
1354                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
1355                }
1356                5 => {
1357                    // Idle cycle for computation
1358                    bus.idle();
1359                }
1360                6 => {
1361                    let $value = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
1362                    let $registers = &mut cpu.registers;
1363                    let value = $body;
1364                    let [value_lsb, value_msb] = value.to_le_bytes();
1365
1366                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
1367                    bus.write(address.wrapping_add(1).into(), value_msb);
1368                    cpu.state.t3 = value_lsb;
1369                }
1370                7 => {
1371                    final_cycle(cpu, bus);
1372
1373                    let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
1374                    bus.write(address.into(), cpu.state.t3);
1375                }
1376                _ => invalid_cycle!(cpu),
1377            }
1378        }
1379    };
1380}
1381
1382use impl_modify_direct_page_u16;
1383
1384macro_rules! impl_modify_direct_page_indexed_u8 {
1385    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1386        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1387            match cpu.state.cycle {
1388                1 => {
1389                    cpu.state.t0 = fetch_operand(cpu, bus);
1390                    check_direct_page_low_byte(cpu);
1391                }
1392                2 => {
1393                    // Idle cycle if D LSB is non-zero
1394                    bus.idle();
1395                }
1396                3 => {
1397                    // Idle cycle for indexing
1398                    bus.idle();
1399                }
1400                4 => {
1401                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
1402                    cpu.state.t1 = bus.read(address.into());
1403                }
1404                5 => {
1405                    if cpu.registers.emulation_mode {
1406                        // Write value back to address
1407                        let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
1408                        bus.write(address.into(), cpu.state.t1);
1409                    } else {
1410                        // Idle cycle for computation
1411                        bus.idle();
1412                    }
1413                }
1414                6 => {
1415                    final_cycle(cpu, bus);
1416
1417                    let $value = cpu.state.t1;
1418                    let $registers = &mut cpu.registers;
1419                    let value = $body;
1420
1421                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
1422                    bus.write(address.into(), value);
1423                }
1424                _ => invalid_cycle!(cpu),
1425            }
1426        }
1427    };
1428}
1429
1430use impl_modify_direct_page_indexed_u8;
1431
1432macro_rules! impl_modify_direct_page_indexed_u16 {
1433    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1434        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1435            match cpu.state.cycle {
1436                1 => {
1437                    cpu.state.t0 = fetch_operand(cpu, bus);
1438                    check_direct_page_low_byte(cpu);
1439                }
1440                2 => {
1441                    // Idle cycle if D LSB is non-zero
1442                    bus.idle();
1443                }
1444                3 => {
1445                    // Idle cycle for indexing
1446                    bus.idle();
1447                }
1448                4 => {
1449                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
1450                    cpu.state.t1 = bus.read(address.into());
1451                }
1452                5 => {
1453                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
1454                    cpu.state.t2 = bus.read(address.wrapping_add(1).into());
1455                }
1456                6 => {
1457                    // Idle cycle for computation
1458                    bus.idle();
1459                }
1460                7 => {
1461                    let $value = u16::from_le_bytes([cpu.state.t1, cpu.state.t2]);
1462                    let $registers = &mut cpu.registers;
1463                    let value = $body;
1464                    let [value_lsb, value_msb] = value.to_le_bytes();
1465
1466                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
1467                    bus.write(address.wrapping_add(1).into(), value_msb);
1468                    cpu.state.t3 = value_lsb;
1469                }
1470                8 => {
1471                    final_cycle(cpu, bus);
1472
1473                    let address = index_direct_page(cpu, cpu.state.t0, cpu.registers.$index);
1474                    bus.write(address.into(), cpu.state.t3);
1475                }
1476                _ => invalid_cycle!(cpu),
1477            }
1478        }
1479    };
1480}
1481
1482use impl_modify_direct_page_indexed_u16;
1483
1484macro_rules! impl_modify_absolute_u8 {
1485    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1486        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1487            match cpu.state.cycle {
1488                1 => {
1489                    cpu.state.t0 = fetch_operand(cpu, bus);
1490                }
1491                2 => {
1492                    cpu.state.t1 = fetch_operand(cpu, bus);
1493                }
1494                3 => {
1495                    let address =
1496                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1497                    cpu.state.t2 = bus.read(address);
1498                }
1499                4 => {
1500                    if cpu.registers.emulation_mode {
1501                        // Write back to address
1502                        let address =
1503                            u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1504                        bus.write(address, cpu.state.t2);
1505                    } else {
1506                        // Idle cycle for computation
1507                        bus.idle();
1508                    }
1509                }
1510                5 => {
1511                    final_cycle(cpu, bus);
1512
1513                    let $value = cpu.state.t2;
1514                    let $registers = &mut cpu.registers;
1515                    let value = $body;
1516
1517                    let address =
1518                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1519                    bus.write(address, value);
1520                }
1521                _ => invalid_cycle!(cpu),
1522            }
1523        }
1524    };
1525}
1526
1527use impl_modify_absolute_u8;
1528
1529macro_rules! impl_modify_absolute_u16 {
1530    ($name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1531        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1532            match cpu.state.cycle {
1533                1 => {
1534                    cpu.state.t0 = fetch_operand(cpu, bus);
1535                }
1536                2 => {
1537                    cpu.state.t1 = fetch_operand(cpu, bus);
1538                }
1539                3 => {
1540                    let address =
1541                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1542                    cpu.state.t2 = bus.read(address);
1543                }
1544                4 => {
1545                    let address =
1546                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1547                    cpu.state.t3 = bus.read((address + 1) & B::ADDRESS_MASK);
1548                }
1549                5 => {
1550                    // Idle cycle for computation
1551                    bus.idle();
1552                }
1553                6 => {
1554                    let $value = u16::from_le_bytes([cpu.state.t2, cpu.state.t3]);
1555                    let $registers = &mut cpu.registers;
1556                    let value = $body;
1557                    let [value_lsb, value_msb] = value.to_le_bytes();
1558
1559                    let address =
1560                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1561                    bus.write((address + 1) & B::ADDRESS_MASK, value_msb);
1562                    cpu.state.t4 = value_lsb;
1563                }
1564                7 => {
1565                    final_cycle(cpu, bus);
1566
1567                    let address =
1568                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1569                    bus.write(address, cpu.state.t4);
1570                }
1571                _ => invalid_cycle!(cpu),
1572            }
1573        }
1574    };
1575}
1576
1577use impl_modify_absolute_u16;
1578
1579macro_rules! impl_modify_absolute_indexed_u8 {
1580    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1581        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1582            match cpu.state.cycle {
1583                1 => {
1584                    cpu.state.t0 = fetch_operand(cpu, bus);
1585                }
1586                2 => {
1587                    cpu.state.t1 = fetch_operand(cpu, bus);
1588                }
1589                3 => {
1590                    // Idle cycle for indexing
1591                    bus.idle();
1592                }
1593                4 => {
1594                    let base_addr =
1595                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1596                    let address = (base_addr + u32::from(cpu.registers.$index)) & B::ADDRESS_MASK;
1597                    cpu.state.t2 = bus.read(address);
1598                }
1599                5 => {
1600                    if cpu.registers.emulation_mode {
1601                        // Write back to address
1602                        let base_addr =
1603                            u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1604                        let address =
1605                            (base_addr + u32::from(cpu.registers.$index)) & B::ADDRESS_MASK;
1606                        bus.write(address, cpu.state.t2);
1607                    } else {
1608                        // Idle cycle for computation
1609                        bus.idle();
1610                    }
1611                }
1612                6 => {
1613                    final_cycle(cpu, bus);
1614
1615                    let $value = cpu.state.t2;
1616                    let $registers = &mut cpu.registers;
1617                    let value = $body;
1618
1619                    let base_addr =
1620                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1621                    let address = (base_addr + u32::from(cpu.registers.$index)) & B::ADDRESS_MASK;
1622                    bus.write(address, value);
1623                }
1624                _ => invalid_cycle!(cpu),
1625            }
1626        }
1627    };
1628}
1629
1630use impl_modify_absolute_indexed_u8;
1631
1632macro_rules! impl_modify_absolute_indexed_u16 {
1633    ($index:ident, $name:ident, (|$registers:ident, $value:ident| $body:block)) => {
1634        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1635            match cpu.state.cycle {
1636                1 => {
1637                    cpu.state.t0 = fetch_operand(cpu, bus);
1638                }
1639                2 => {
1640                    cpu.state.t1 = fetch_operand(cpu, bus);
1641                }
1642                3 => {
1643                    // Idle cycle for indexing
1644                    bus.idle();
1645                }
1646                4 => {
1647                    let base_addr =
1648                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1649                    let address = (base_addr + u32::from(cpu.registers.$index)) & B::ADDRESS_MASK;
1650                    cpu.state.t2 = bus.read(address);
1651                }
1652                5 => {
1653                    let base_addr =
1654                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1655                    let address =
1656                        (base_addr + u32::from(cpu.registers.$index) + 1) & B::ADDRESS_MASK;
1657                    cpu.state.t3 = bus.read(address);
1658                }
1659                6 => {
1660                    // Idle cycle for computation
1661                    bus.idle();
1662                }
1663                7 => {
1664                    let $value = u16::from_le_bytes([cpu.state.t2, cpu.state.t3]);
1665                    let $registers = &mut cpu.registers;
1666                    let value = $body;
1667                    let [value_lsb, value_msb] = value.to_le_bytes();
1668
1669                    let base_addr =
1670                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1671                    let address =
1672                        (base_addr + u32::from(cpu.registers.$index) + 1) & B::ADDRESS_MASK;
1673                    bus.write(address, value_msb);
1674                    cpu.state.t4 = value_lsb;
1675                }
1676                8 => {
1677                    final_cycle(cpu, bus);
1678
1679                    let base_addr =
1680                        u32::from_le_bytes([cpu.state.t0, cpu.state.t1, cpu.registers.dbr, 0]);
1681                    let address = (base_addr + u32::from(cpu.registers.$index)) & B::ADDRESS_MASK;
1682                    bus.write(address, cpu.state.t4);
1683                }
1684                _ => invalid_cycle!(cpu),
1685            }
1686        }
1687    };
1688}
1689
1690use impl_modify_absolute_indexed_u16;
1691
1692macro_rules! impl_modify_op_u8 {
1693    (@inner accumulator, $($rest:tt)*) => {
1694        impl_modify_accumulator_u8!($($rest)*);
1695    };
1696    (@inner direct_page, $($rest:tt)*) => {
1697        impl_modify_direct_page_u8!($($rest)*);
1698    };
1699    (@inner direct_page_x, $($rest:tt)*) => {
1700        impl_modify_direct_page_indexed_u8!(x, $($rest)*);
1701    };
1702    (@inner absolute, $($rest:tt)*) => {
1703        impl_modify_absolute_u8!($($rest)*);
1704    };
1705    (@inner absolute_x, $($rest:tt)*) => {
1706        impl_modify_absolute_indexed_u8!(x, $($rest)*);
1707    };
1708    ([$($am:tt -> $name:ident),* $(,)?] $rest:tt) => {
1709        $(
1710            impl_modify_op_u8!(@inner $am, $name, $rest);
1711        )*
1712    }
1713}
1714
1715use impl_modify_op_u8;
1716
1717macro_rules! impl_modify_op_u16 {
1718    (@inner accumulator, $($rest:tt)*) => {
1719        impl_modify_accumulator_u16!($($rest)*);
1720    };
1721    (@inner direct_page, $($rest:tt)*) => {
1722        impl_modify_direct_page_u16!($($rest)*);
1723    };
1724    (@inner direct_page_x, $($rest:tt)*) => {
1725        impl_modify_direct_page_indexed_u16!(x, $($rest)*);
1726    };
1727    (@inner absolute, $($rest:tt)*) => {
1728        impl_modify_absolute_u16!($($rest)*);
1729    };
1730    (@inner absolute_x, $($rest:tt)*) => {
1731        impl_modify_absolute_indexed_u16!(x, $($rest)*);
1732    };
1733    ([$($am:tt -> $name:ident),* $(,)?] $rest:tt) => {
1734        $(
1735            impl_modify_op_u16!(@inner $am, $name, $rest);
1736        )*
1737    }
1738}
1739
1740use impl_modify_op_u16;
1741
1742macro_rules! impl_registers_op {
1743    ($name:ident, |$registers:ident| $body:block) => {
1744        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1745            final_cycle(cpu, bus);
1746
1747            bus.idle();
1748            let $registers = &mut cpu.registers;
1749            $body
1750        }
1751    };
1752}
1753
1754use impl_registers_op;

NOP: No-op

1757impl_registers_op!(nop, |_registers| {});

WDM: William D. Mensch, Jr. (2-byte no-op)

1760fn wdm<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1761    final_cycle(cpu, bus);
1762
1763    bus.idle();
1764
1765    cpu.registers.pc = cpu.registers.pc.wrapping_add(1);
1766}
1768pub fn execute<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1769    if cpu.state.cycle != 0 {
1770        // If mid-instruction, continue to execute that instruction
1771        execute_cycle(cpu, bus);
1772
1773        // Increment only if the instruction didn't finish
1774        if cpu.state.cycle != 0 {
1775            cpu.state.cycle += 1;
1776        }
1777
1778        return;
1779    }
1780
1781    // If stopped, the CPU is halted until reset
1782    if cpu.state.stopped {
1783        bus.idle();
1784        return;
1785    }
1786
1787    // If waiting, the CPU is halted until an NMI or IRQ triggers; check last_irq instead of irq_triggered
1788    // because IRQs will unhalt the CPU even if the I flag is set (though the IRQ won't be handled in that case)
1789    if cpu.state.waiting && !cpu.state.nmi_triggered && !cpu.state.last_irq {
1790        poll_interrupt_lines(cpu, bus);
1791        bus.idle();
1792        return;
1793    }
1794    cpu.state.waiting = false;
1795
1796    if cpu.state.nmi_triggered {
1797        log::trace!("Handling NMI");
1798
1799        cpu.state.nmi_triggered = false;
1800        bus.acknowledge_nmi();
1801
1802        flow::handle_interrupt(cpu, bus, InterruptType::Nmi);
1803        cpu.state.handling_interrupt = Some(InterruptType::Nmi);
1804        cpu.state.cycle = 1;
1805        return;
1806    }
1807
1808    if cpu.state.irq_triggered {
1809        log::trace!("Handling IRQ");
1810
1811        flow::handle_interrupt(cpu, bus, InterruptType::Irq);
1812        cpu.state.handling_interrupt = Some(InterruptType::Irq);
1813        cpu.state.cycle = 1;
1814        return;
1815    }
1816
1817    // Cycle 0 and not handling an interrupt is always opcode fetch
1818    cpu.state.opcode = fetch_operand(cpu, bus);
1819    cpu.state.cycle = 1;
1820
1821    if log::log_enabled!(log::Level::Trace) {
1822        let instr_name = disassemble::instruction_str(cpu.state.opcode);
1823        log::trace!(
1824            "Fetched opcode from PBR={:02X} / PC={:04X}: {:02X} ({instr_name}); m={}, x={}, e={}, A={:04X}, X={:04X}, Y={:04X}, D={:04X}, S={:04X}, DBR={:02X}",
1825            cpu.registers.pbr,
1826            cpu.registers.pc.wrapping_sub(1),
1827            cpu.state.opcode,
1828            u8::from(cpu.registers.p.accumulator_size == SizeBits::Eight),
1829            u8::from(cpu.registers.p.index_size == SizeBits::Eight),
1830            u8::from(cpu.registers.emulation_mode),
1831            cpu.registers.a,
1832            cpu.registers.x,
1833            cpu.registers.y,
1834            cpu.registers.d,
1835            cpu.registers.s,
1836            cpu.registers.dbr
1837        );
1838    }
1839}
1840
1841fn execute_cycle<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
1842    use SizeBits::{Eight, Sixteen};
1843
1844    if let Some(interrupt) = cpu.state.handling_interrupt {
1845        flow::handle_interrupt(cpu, bus, interrupt);
1846
1847        if cpu.state.cycle == 0 {
1848            cpu.state.handling_interrupt = None;
1849        }
1850
1851        return;
1852    }
1853
1854    match cpu.state.opcode {
1855        0x00 => flow::handle_interrupt(cpu, bus, InterruptType::Breakpoint),
1856        0x01 => match cpu.registers.p.accumulator_size {
1857            Eight => alu::ora_direct_page_indexed_indirect_u8(cpu, bus),
1858            Sixteen => alu::ora_direct_page_indexed_indirect_u16(cpu, bus),
1859        },
1860        0x02 => flow::handle_interrupt(cpu, bus, InterruptType::Coprocessor),
1861        0x03 => match cpu.registers.p.accumulator_size {
1862            Eight => alu::ora_stack_relative_u8(cpu, bus),
1863            Sixteen => alu::ora_stack_relative_u16(cpu, bus),
1864        },
1865        0x04 => match cpu.registers.p.accumulator_size {
1866            Eight => bits::tsb_direct_page_u8(cpu, bus),
1867            Sixteen => bits::tsb_direct_page_u16(cpu, bus),
1868        },
1869        0x05 => match cpu.registers.p.accumulator_size {
1870            Eight => alu::ora_direct_page_u8(cpu, bus),
1871            Sixteen => alu::ora_direct_page_u16(cpu, bus),
1872        },
1873        0x06 => match cpu.registers.p.accumulator_size {
1874            Eight => bits::asl_direct_page_u8(cpu, bus),
1875            Sixteen => bits::asl_direct_page_u16(cpu, bus),
1876        },
1877        0x07 => match cpu.registers.p.accumulator_size {
1878            Eight => alu::ora_direct_page_indirect_long_u8(cpu, bus),
1879            Sixteen => alu::ora_direct_page_indirect_long_u16(cpu, bus),
1880        },
1881        0x08 => load::php(cpu, bus),
1882        0x09 => match cpu.registers.p.accumulator_size {
1883            Eight => alu::ora_immediate_u8(cpu, bus),
1884            Sixteen => alu::ora_immediate_u16(cpu, bus),
1885        },
1886        0x0A => match cpu.registers.p.accumulator_size {
1887            Eight => bits::asl_accumulator_u8(cpu, bus),
1888            Sixteen => bits::asl_accumulator_u16(cpu, bus),
1889        },
1890        0x0B => load::phd(cpu, bus),
1891        0x0C => match cpu.registers.p.accumulator_size {
1892            Eight => bits::tsb_absolute_u8(cpu, bus),
1893            Sixteen => bits::tsb_absolute_u16(cpu, bus),
1894        },
1895        0x0D => match cpu.registers.p.accumulator_size {
1896            Eight => alu::ora_absolute_u8(cpu, bus),
1897            Sixteen => alu::ora_absolute_u16(cpu, bus),
1898        },
1899        0x0E => match cpu.registers.p.accumulator_size {
1900            Eight => bits::asl_absolute_u8(cpu, bus),
1901            Sixteen => bits::asl_absolute_u16(cpu, bus),
1902        },
1903        0x0F => match cpu.registers.p.accumulator_size {
1904            Eight => alu::ora_absolute_long_u8(cpu, bus),
1905            Sixteen => alu::ora_absolute_long_u16(cpu, bus),
1906        },
1907        0x10 => flow::bpl(cpu, bus),
1908        0x11 => match cpu.registers.p.accumulator_size {
1909            Eight => alu::ora_direct_page_indirect_indexed_u8(cpu, bus),
1910            Sixteen => alu::ora_direct_page_indirect_indexed_u16(cpu, bus),
1911        },
1912        0x12 => match cpu.registers.p.accumulator_size {
1913            Eight => alu::ora_direct_page_indirect_u8(cpu, bus),
1914            Sixteen => alu::ora_direct_page_indirect_u16(cpu, bus),
1915        },
1916        0x13 => match cpu.registers.p.accumulator_size {
1917            Eight => alu::ora_stack_relative_indirect_indexed_u8(cpu, bus),
1918            Sixteen => alu::ora_stack_relative_indirect_indexed_u16(cpu, bus),
1919        },
1920        0x14 => match cpu.registers.p.accumulator_size {
1921            Eight => bits::trb_direct_page_u8(cpu, bus),
1922            Sixteen => bits::trb_direct_page_u16(cpu, bus),
1923        },
1924        0x15 => match cpu.registers.p.accumulator_size {
1925            Eight => alu::ora_direct_page_x_u8(cpu, bus),
1926            Sixteen => alu::ora_direct_page_x_u16(cpu, bus),
1927        },
1928        0x16 => match cpu.registers.p.accumulator_size {
1929            Eight => bits::asl_direct_page_x_u8(cpu, bus),
1930            Sixteen => bits::asl_direct_page_x_u16(cpu, bus),
1931        },
1932        0x17 => match cpu.registers.p.accumulator_size {
1933            Eight => alu::ora_direct_page_indirect_long_indexed_u8(cpu, bus),
1934            Sixteen => alu::ora_direct_page_indirect_long_indexed_u16(cpu, bus),
1935        },
1936        0x18 => flags::clc(cpu, bus),
1937        0x19 => match cpu.registers.p.accumulator_size {
1938            Eight => alu::ora_absolute_y_u8(cpu, bus),
1939            Sixteen => alu::ora_absolute_y_u16(cpu, bus),
1940        },
1941        0x1A => match cpu.registers.p.accumulator_size {
1942            Eight => alu::inc_accumulator_u8(cpu, bus),
1943            Sixteen => alu::inc_accumulator_u16(cpu, bus),
1944        },
1945        0x1B => load::tcs(cpu, bus),
1946        0x1C => match cpu.registers.p.accumulator_size {
1947            Eight => bits::trb_absolute_u8(cpu, bus),
1948            Sixteen => bits::trb_absolute_u16(cpu, bus),
1949        },
1950        0x1D => match cpu.registers.p.accumulator_size {
1951            Eight => alu::ora_absolute_x_u8(cpu, bus),
1952            Sixteen => alu::ora_absolute_x_u16(cpu, bus),
1953        },
1954        0x1E => match cpu.registers.p.accumulator_size {
1955            Eight => bits::asl_absolute_x_u8(cpu, bus),
1956            Sixteen => bits::asl_absolute_x_u16(cpu, bus),
1957        },
1958        0x1F => match cpu.registers.p.accumulator_size {
1959            Eight => alu::ora_absolute_long_x_u8(cpu, bus),
1960            Sixteen => alu::ora_absolute_long_x_u16(cpu, bus),
1961        },
1962        0x20 => flow::jsr_absolute(cpu, bus),
1963        0x21 => match cpu.registers.p.accumulator_size {
1964            Eight => alu::and_direct_page_indexed_indirect_u8(cpu, bus),
1965            Sixteen => alu::and_direct_page_indexed_indirect_u16(cpu, bus),
1966        },
1967        0x22 => flow::jsl(cpu, bus),
1968        0x23 => match cpu.registers.p.accumulator_size {
1969            Eight => alu::and_stack_relative_u8(cpu, bus),
1970            Sixteen => alu::and_stack_relative_u16(cpu, bus),
1971        },
1972        0x24 => match cpu.registers.p.accumulator_size {
1973            Eight => bits::bit_direct_page_u8(cpu, bus),
1974            Sixteen => bits::bit_direct_page_u16(cpu, bus),
1975        },
1976        0x25 => match cpu.registers.p.accumulator_size {
1977            Eight => alu::and_direct_page_u8(cpu, bus),
1978            Sixteen => alu::and_direct_page_u16(cpu, bus),
1979        },
1980        0x26 => match cpu.registers.p.accumulator_size {
1981            Eight => bits::rol_direct_page_u8(cpu, bus),
1982            Sixteen => bits::rol_direct_page_u16(cpu, bus),
1983        },
1984        0x27 => match cpu.registers.p.accumulator_size {
1985            Eight => alu::and_direct_page_indirect_long_u8(cpu, bus),
1986            Sixteen => alu::and_direct_page_indirect_long_u16(cpu, bus),
1987        },
1988        0x28 => load::plp(cpu, bus),
1989        0x29 => match cpu.registers.p.accumulator_size {
1990            Eight => alu::and_immediate_u8(cpu, bus),
1991            Sixteen => alu::and_immediate_u16(cpu, bus),
1992        },
1993        0x2A => match cpu.registers.p.accumulator_size {
1994            Eight => bits::rol_accumulator_u8(cpu, bus),
1995            Sixteen => bits::rol_accumulator_u16(cpu, bus),
1996        },
1997        0x2B => load::pld(cpu, bus),
1998        0x2C => match cpu.registers.p.accumulator_size {
1999            Eight => bits::bit_absolute_u8(cpu, bus),
2000            Sixteen => bits::bit_absolute_u16(cpu, bus),
2001        },
2002        0x2D => match cpu.registers.p.accumulator_size {
2003            Eight => alu::and_absolute_u8(cpu, bus),
2004            Sixteen => alu::and_absolute_u16(cpu, bus),
2005        },
2006        0x2E => match cpu.registers.p.accumulator_size {
2007            Eight => bits::rol_absolute_u8(cpu, bus),
2008            Sixteen => bits::rol_absolute_u16(cpu, bus),
2009        },
2010        0x2F => match cpu.registers.p.accumulator_size {
2011            Eight => alu::and_absolute_long_u8(cpu, bus),
2012            Sixteen => alu::and_absolute_long_u16(cpu, bus),
2013        },
2014        0x30 => flow::bmi(cpu, bus),
2015        0x31 => match cpu.registers.p.accumulator_size {
2016            Eight => alu::and_direct_page_indirect_indexed_u8(cpu, bus),
2017            Sixteen => alu::and_direct_page_indirect_indexed_u16(cpu, bus),
2018        },
2019        0x32 => match cpu.registers.p.accumulator_size {
2020            Eight => alu::and_direct_page_indirect_u8(cpu, bus),
2021            Sixteen => alu::and_direct_page_indirect_u16(cpu, bus),
2022        },
2023        0x33 => match cpu.registers.p.accumulator_size {
2024            Eight => alu::and_stack_relative_indirect_indexed_u8(cpu, bus),
2025            Sixteen => alu::and_stack_relative_indirect_indexed_u16(cpu, bus),
2026        },
2027        0x34 => match cpu.registers.p.accumulator_size {
2028            Eight => bits::bit_direct_page_x_u8(cpu, bus),
2029            Sixteen => bits::bit_direct_page_x_u16(cpu, bus),
2030        },
2031        0x35 => match cpu.registers.p.accumulator_size {
2032            Eight => alu::and_direct_page_x_u8(cpu, bus),
2033            Sixteen => alu::and_direct_page_x_u16(cpu, bus),
2034        },
2035        0x36 => match cpu.registers.p.accumulator_size {
2036            Eight => bits::rol_direct_page_x_u8(cpu, bus),
2037            Sixteen => bits::rol_direct_page_x_u16(cpu, bus),
2038        },
2039        0x37 => match cpu.registers.p.accumulator_size {
2040            Eight => alu::and_direct_page_indirect_long_indexed_u8(cpu, bus),
2041            Sixteen => alu::and_direct_page_indirect_long_indexed_u16(cpu, bus),
2042        },
2043        0x38 => flags::sec(cpu, bus),
2044        0x39 => match cpu.registers.p.accumulator_size {
2045            Eight => alu::and_absolute_y_u8(cpu, bus),
2046            Sixteen => alu::and_absolute_y_u16(cpu, bus),
2047        },
2048        0x3A => match cpu.registers.p.accumulator_size {
2049            Eight => alu::dec_accumulator_u8(cpu, bus),
2050            Sixteen => alu::dec_accumulator_u16(cpu, bus),
2051        },
2052        0x3B => load::tsc(cpu, bus),
2053        0x3C => match cpu.registers.p.accumulator_size {
2054            Eight => bits::bit_absolute_x_u8(cpu, bus),
2055            Sixteen => bits::bit_absolute_x_u16(cpu, bus),
2056        },
2057        0x3D => match cpu.registers.p.accumulator_size {
2058            Eight => alu::and_absolute_x_u8(cpu, bus),
2059            Sixteen => alu::and_absolute_x_u16(cpu, bus),
2060        },
2061        0x3E => match cpu.registers.p.accumulator_size {
2062            Eight => bits::rol_absolute_x_u8(cpu, bus),
2063            Sixteen => bits::rol_absolute_x_u16(cpu, bus),
2064        },
2065        0x3F => match cpu.registers.p.accumulator_size {
2066            Eight => alu::and_absolute_long_x_u8(cpu, bus),
2067            Sixteen => alu::and_absolute_long_x_u16(cpu, bus),
2068        },
2069        0x40 => flow::rti(cpu, bus),
2070        0x41 => match cpu.registers.p.accumulator_size {
2071            Eight => alu::eor_direct_page_indexed_indirect_u8(cpu, bus),
2072            Sixteen => alu::eor_direct_page_indexed_indirect_u16(cpu, bus),
2073        },
2074        0x42 => wdm(cpu, bus),
2075        0x43 => match cpu.registers.p.accumulator_size {
2076            Eight => alu::eor_stack_relative_u8(cpu, bus),
2077            Sixteen => alu::eor_stack_relative_u16(cpu, bus),
2078        },
2079        0x44 => load::mvp(cpu, bus),
2080        0x45 => match cpu.registers.p.accumulator_size {
2081            Eight => alu::eor_direct_page_u8(cpu, bus),
2082            Sixteen => alu::eor_direct_page_u16(cpu, bus),
2083        },
2084        0x46 => match cpu.registers.p.accumulator_size {
2085            Eight => bits::lsr_direct_page_u8(cpu, bus),
2086            Sixteen => bits::lsr_direct_page_u16(cpu, bus),
2087        },
2088        0x47 => match cpu.registers.p.accumulator_size {
2089            Eight => alu::eor_direct_page_indirect_long_u8(cpu, bus),
2090            Sixteen => alu::eor_direct_page_indirect_long_u16(cpu, bus),
2091        },
2092        0x48 => match cpu.registers.p.accumulator_size {
2093            Eight => load::pha_u8(cpu, bus),
2094            Sixteen => load::pha_u16(cpu, bus),
2095        },
2096        0x49 => match cpu.registers.p.accumulator_size {
2097            Eight => alu::eor_immediate_u8(cpu, bus),
2098            Sixteen => alu::eor_immediate_u16(cpu, bus),
2099        },
2100        0x4A => match cpu.registers.p.accumulator_size {
2101            Eight => bits::lsr_accumulator_u8(cpu, bus),
2102            Sixteen => bits::lsr_accumulator_u16(cpu, bus),
2103        },
2104        0x4B => load::phk(cpu, bus),
2105        0x4C => flow::jmp_absolute(cpu, bus),
2106        0x4D => match cpu.registers.p.accumulator_size {
2107            Eight => alu::eor_absolute_u8(cpu, bus),
2108            Sixteen => alu::eor_absolute_u16(cpu, bus),
2109        },
2110        0x4E => match cpu.registers.p.accumulator_size {
2111            Eight => bits::lsr_absolute_u8(cpu, bus),
2112            Sixteen => bits::lsr_absolute_u16(cpu, bus),
2113        },
2114        0x4F => match cpu.registers.p.accumulator_size {
2115            Eight => alu::eor_absolute_long_u8(cpu, bus),
2116            Sixteen => alu::eor_absolute_long_u16(cpu, bus),
2117        },
2118        0x50 => flow::bvc(cpu, bus),
2119        0x51 => match cpu.registers.p.accumulator_size {
2120            Eight => alu::eor_direct_page_indirect_indexed_u8(cpu, bus),
2121            Sixteen => alu::eor_direct_page_indirect_indexed_u16(cpu, bus),
2122        },
2123        0x52 => match cpu.registers.p.accumulator_size {
2124            Eight => alu::eor_direct_page_indirect_u8(cpu, bus),
2125            Sixteen => alu::eor_direct_page_indirect_u16(cpu, bus),
2126        },
2127        0x53 => match cpu.registers.p.accumulator_size {
2128            Eight => alu::eor_stack_relative_indirect_indexed_u8(cpu, bus),
2129            Sixteen => alu::eor_stack_relative_indirect_indexed_u16(cpu, bus),
2130        },
2131        0x54 => load::mvn(cpu, bus),
2132        0x55 => match cpu.registers.p.accumulator_size {
2133            Eight => alu::eor_direct_page_x_u8(cpu, bus),
2134            Sixteen => alu::eor_direct_page_x_u16(cpu, bus),
2135        },
2136        0x56 => match cpu.registers.p.accumulator_size {
2137            Eight => bits::lsr_direct_page_x_u8(cpu, bus),
2138            Sixteen => bits::lsr_direct_page_x_u16(cpu, bus),
2139        },
2140        0x57 => match cpu.registers.p.accumulator_size {
2141            Eight => alu::eor_direct_page_indirect_long_indexed_u8(cpu, bus),
2142            Sixteen => alu::eor_direct_page_indirect_long_indexed_u16(cpu, bus),
2143        },
2144        0x58 => flags::cli(cpu, bus),
2145        0x59 => match cpu.registers.p.accumulator_size {
2146            Eight => alu::eor_absolute_y_u8(cpu, bus),
2147            Sixteen => alu::eor_absolute_y_u16(cpu, bus),
2148        },
2149        0x5A => match cpu.registers.p.index_size {
2150            Eight => load::phy_u8(cpu, bus),
2151            Sixteen => load::phy_u16(cpu, bus),
2152        },
2153        0x5B => load::tcd(cpu, bus),
2154        0x5C => flow::jmp_absolute_long(cpu, bus),
2155        0x5D => match cpu.registers.p.accumulator_size {
2156            Eight => alu::eor_absolute_x_u8(cpu, bus),
2157            Sixteen => alu::eor_absolute_x_u16(cpu, bus),
2158        },
2159        0x5E => match cpu.registers.p.accumulator_size {
2160            Eight => bits::lsr_absolute_x_u8(cpu, bus),
2161            Sixteen => bits::lsr_absolute_x_u16(cpu, bus),
2162        },
2163        0x5F => match cpu.registers.p.accumulator_size {
2164            Eight => alu::eor_absolute_long_x_u8(cpu, bus),
2165            Sixteen => alu::eor_absolute_long_x_u16(cpu, bus),
2166        },
2167        0x60 => flow::rts(cpu, bus),
2168        0x61 => match cpu.registers.p.accumulator_size {
2169            Eight => alu::adc_direct_page_indexed_indirect_u8(cpu, bus),
2170            Sixteen => alu::adc_direct_page_indexed_indirect_u16(cpu, bus),
2171        },
2172        0x62 => flow::per(cpu, bus),
2173        0x63 => match cpu.registers.p.accumulator_size {
2174            Eight => alu::adc_stack_relative_u8(cpu, bus),
2175            Sixteen => alu::adc_stack_relative_u16(cpu, bus),
2176        },
2177        0x64 => match cpu.registers.p.accumulator_size {
2178            Eight => load::stz_direct_page_u8(cpu, bus),
2179            Sixteen => load::stz_direct_page_u16(cpu, bus),
2180        },
2181        0x65 => match cpu.registers.p.accumulator_size {
2182            Eight => alu::adc_direct_page_u8(cpu, bus),
2183            Sixteen => alu::adc_direct_page_u16(cpu, bus),
2184        },
2185        0x66 => match cpu.registers.p.accumulator_size {
2186            Eight => bits::ror_direct_page_u8(cpu, bus),
2187            Sixteen => bits::ror_direct_page_u16(cpu, bus),
2188        },
2189        0x67 => match cpu.registers.p.accumulator_size {
2190            Eight => alu::adc_direct_page_indirect_long_u8(cpu, bus),
2191            Sixteen => alu::adc_direct_page_indirect_long_u16(cpu, bus),
2192        },
2193        0x68 => match cpu.registers.p.accumulator_size {
2194            Eight => load::pla_u8(cpu, bus),
2195            Sixteen => load::pla_u16(cpu, bus),
2196        },
2197        0x69 => match cpu.registers.p.accumulator_size {
2198            Eight => alu::adc_immediate_u8(cpu, bus),
2199            Sixteen => alu::adc_immediate_u16(cpu, bus),
2200        },
2201        0x6A => match cpu.registers.p.accumulator_size {
2202            Eight => bits::ror_accumulator_u8(cpu, bus),
2203            Sixteen => bits::ror_accumulator_u16(cpu, bus),
2204        },
2205        0x6B => flow::rtl(cpu, bus),
2206        0x6C => flow::jmp_indirect(cpu, bus),
2207        0x6D => match cpu.registers.p.accumulator_size {
2208            Eight => alu::adc_absolute_u8(cpu, bus),
2209            Sixteen => alu::adc_absolute_u16(cpu, bus),
2210        },
2211        0x6E => match cpu.registers.p.accumulator_size {
2212            Eight => bits::ror_absolute_u8(cpu, bus),
2213            Sixteen => bits::ror_absolute_u16(cpu, bus),
2214        },
2215        0x6F => match cpu.registers.p.accumulator_size {
2216            Eight => alu::adc_absolute_long_u8(cpu, bus),
2217            Sixteen => alu::adc_absolute_long_u16(cpu, bus),
2218        },
2219        0x70 => flow::bvs(cpu, bus),
2220        0x71 => match cpu.registers.p.accumulator_size {
2221            Eight => alu::adc_direct_page_indirect_indexed_u8(cpu, bus),
2222            Sixteen => alu::adc_direct_page_indirect_indexed_u16(cpu, bus),
2223        },
2224        0x72 => match cpu.registers.p.accumulator_size {
2225            Eight => alu::adc_direct_page_indirect_u8(cpu, bus),
2226            Sixteen => alu::adc_direct_page_indirect_u16(cpu, bus),
2227        },
2228        0x73 => match cpu.registers.p.accumulator_size {
2229            Eight => alu::adc_stack_relative_indirect_indexed_u8(cpu, bus),
2230            Sixteen => alu::adc_stack_relative_indirect_indexed_u16(cpu, bus),
2231        },
2232        0x74 => match cpu.registers.p.accumulator_size {
2233            Eight => load::stz_direct_page_x_u8(cpu, bus),
2234            Sixteen => load::stz_direct_page_x_u16(cpu, bus),
2235        },
2236        0x75 => match cpu.registers.p.accumulator_size {
2237            Eight => alu::adc_direct_page_x_u8(cpu, bus),
2238            Sixteen => alu::adc_direct_page_x_u16(cpu, bus),
2239        },
2240        0x76 => match cpu.registers.p.accumulator_size {
2241            Eight => bits::ror_direct_page_x_u8(cpu, bus),
2242            Sixteen => bits::ror_direct_page_x_u16(cpu, bus),
2243        },
2244        0x77 => match cpu.registers.p.accumulator_size {
2245            Eight => alu::adc_direct_page_indirect_long_indexed_u8(cpu, bus),
2246            Sixteen => alu::adc_direct_page_indirect_long_indexed_u16(cpu, bus),
2247        },
2248        0x78 => flags::sei(cpu, bus),
2249        0x79 => match cpu.registers.p.accumulator_size {
2250            Eight => alu::adc_absolute_y_u8(cpu, bus),
2251            Sixteen => alu::adc_absolute_y_u16(cpu, bus),
2252        },
2253        0x7A => match cpu.registers.p.index_size {
2254            Eight => load::ply_u8(cpu, bus),
2255            Sixteen => load::ply_u16(cpu, bus),
2256        },
2257        0x7B => load::tdc(cpu, bus),
2258        0x7C => flow::jmp_indexed_indirect(cpu, bus),
2259        0x7D => match cpu.registers.p.accumulator_size {
2260            Eight => alu::adc_absolute_x_u8(cpu, bus),
2261            Sixteen => alu::adc_absolute_x_u16(cpu, bus),
2262        },
2263        0x7E => match cpu.registers.p.accumulator_size {
2264            Eight => bits::ror_absolute_x_u8(cpu, bus),
2265            Sixteen => bits::ror_absolute_x_u16(cpu, bus),
2266        },
2267        0x7F => match cpu.registers.p.accumulator_size {
2268            Eight => alu::adc_absolute_long_x_u8(cpu, bus),
2269            Sixteen => alu::adc_absolute_long_x_u16(cpu, bus),
2270        },
2271        0x80 => flow::bra(cpu, bus),
2272        0x81 => match cpu.registers.p.accumulator_size {
2273            Eight => load::sta_direct_page_indexed_indirect_u8(cpu, bus),
2274            Sixteen => load::sta_direct_page_indexed_indirect_u16(cpu, bus),
2275        },
2276        0x82 => flow::brl(cpu, bus),
2277        0x83 => match cpu.registers.p.accumulator_size {
2278            Eight => load::sta_stack_relative_u8(cpu, bus),
2279            Sixteen => load::sta_stack_relative_u16(cpu, bus),
2280        },
2281        0x84 => match cpu.registers.p.index_size {
2282            Eight => load::sty_direct_page_u8(cpu, bus),
2283            Sixteen => load::sty_direct_page_u16(cpu, bus),
2284        },
2285        0x85 => match cpu.registers.p.accumulator_size {
2286            Eight => load::sta_direct_page_u8(cpu, bus),
2287            Sixteen => load::sta_direct_page_u16(cpu, bus),
2288        },
2289        0x86 => match cpu.registers.p.index_size {
2290            Eight => load::stx_direct_page_u8(cpu, bus),
2291            Sixteen => load::stx_direct_page_u16(cpu, bus),
2292        },
2293        0x87 => match cpu.registers.p.accumulator_size {
2294            Eight => load::sta_direct_page_indirect_long_u8(cpu, bus),
2295            Sixteen => load::sta_direct_page_indirect_long_u16(cpu, bus),
2296        },
2297        0x88 => match cpu.registers.p.index_size {
2298            Eight => alu::dey_u8(cpu, bus),
2299            Sixteen => alu::dey_u16(cpu, bus),
2300        },
2301        0x89 => match cpu.registers.p.accumulator_size {
2302            Eight => bits::bit_immediate_u8(cpu, bus),
2303            Sixteen => bits::bit_immediate_u16(cpu, bus),
2304        },
2305        0x8A => match cpu.registers.p.accumulator_size {
2306            Eight => load::txa_u8(cpu, bus),
2307            Sixteen => load::txa_u16(cpu, bus),
2308        },
2309        0x8B => load::phb(cpu, bus),
2310        0x8C => match cpu.registers.p.index_size {
2311            Eight => load::sty_absolute_u8(cpu, bus),
2312            Sixteen => load::sty_absolute_u16(cpu, bus),
2313        },
2314        0x8D => match cpu.registers.p.accumulator_size {
2315            Eight => load::sta_absolute_u8(cpu, bus),
2316            Sixteen => load::sta_absolute_u16(cpu, bus),
2317        },
2318        0x8E => match cpu.registers.p.index_size {
2319            Eight => load::stx_absolute_u8(cpu, bus),
2320            Sixteen => load::stx_absolute_u16(cpu, bus),
2321        },
2322        0x8F => match cpu.registers.p.accumulator_size {
2323            Eight => load::sta_absolute_long_u8(cpu, bus),
2324            Sixteen => load::sta_absolute_long_u16(cpu, bus),
2325        },
2326        0x90 => flow::bcc(cpu, bus),
2327        0x91 => match cpu.registers.p.accumulator_size {
2328            Eight => load::sta_direct_page_indirect_indexed_u8(cpu, bus),
2329            Sixteen => load::sta_direct_page_indirect_indexed_u16(cpu, bus),
2330        },
2331        0x92 => match cpu.registers.p.accumulator_size {
2332            Eight => load::sta_direct_page_indirect_u8(cpu, bus),
2333            Sixteen => load::sta_direct_page_indirect_u16(cpu, bus),
2334        },
2335        0x93 => match cpu.registers.p.accumulator_size {
2336            Eight => load::sta_stack_relative_indirect_indexed_u8(cpu, bus),
2337            Sixteen => load::sta_stack_relative_indirect_indexed_u16(cpu, bus),
2338        },
2339        0x94 => match cpu.registers.p.index_size {
2340            Eight => load::sty_direct_page_x_u8(cpu, bus),
2341            Sixteen => load::sty_direct_page_x_u16(cpu, bus),
2342        },
2343        0x95 => match cpu.registers.p.accumulator_size {
2344            Eight => load::sta_direct_page_x_u8(cpu, bus),
2345            Sixteen => load::sta_direct_page_x_u16(cpu, bus),
2346        },
2347        0x96 => match cpu.registers.p.index_size {
2348            Eight => load::stx_direct_page_y_u8(cpu, bus),
2349            Sixteen => load::stx_direct_page_y_u16(cpu, bus),
2350        },
2351        0x97 => match cpu.registers.p.accumulator_size {
2352            Eight => load::sta_direct_page_indirect_long_indexed_u8(cpu, bus),
2353            Sixteen => load::sta_direct_page_indirect_long_indexed_u16(cpu, bus),
2354        },
2355        0x98 => match cpu.registers.p.accumulator_size {
2356            Eight => load::tya_u8(cpu, bus),
2357            Sixteen => load::tya_u16(cpu, bus),
2358        },
2359        0x99 => match cpu.registers.p.accumulator_size {
2360            Eight => load::sta_absolute_y_u8(cpu, bus),
2361            Sixteen => load::sta_absolute_y_u16(cpu, bus),
2362        },
2363        0x9A => load::txs(cpu, bus),
2364        0x9B => match cpu.registers.p.index_size {
2365            Eight => load::txy_u8(cpu, bus),
2366            Sixteen => load::txy_u16(cpu, bus),
2367        },
2368        0x9C => match cpu.registers.p.accumulator_size {
2369            Eight => load::stz_absolute_u8(cpu, bus),
2370            Sixteen => load::stz_absolute_u16(cpu, bus),
2371        },
2372        0x9D => match cpu.registers.p.accumulator_size {
2373            Eight => load::sta_absolute_x_u8(cpu, bus),
2374            Sixteen => load::sta_absolute_x_u16(cpu, bus),
2375        },
2376        0x9E => match cpu.registers.p.accumulator_size {
2377            Eight => load::stz_absolute_x_u8(cpu, bus),
2378            Sixteen => load::stz_absolute_x_u16(cpu, bus),
2379        },
2380        0x9F => match cpu.registers.p.accumulator_size {
2381            Eight => load::sta_absolute_long_x_u8(cpu, bus),
2382            Sixteen => load::sta_absolute_long_x_u16(cpu, bus),
2383        },
2384        0xA0 => match cpu.registers.p.index_size {
2385            Eight => load::ldy_immediate_u8(cpu, bus),
2386            Sixteen => load::ldy_immediate_u16(cpu, bus),
2387        },
2388        0xA1 => match cpu.registers.p.accumulator_size {
2389            Eight => load::lda_direct_page_indexed_indirect_u8(cpu, bus),
2390            Sixteen => load::lda_direct_page_indexed_indirect_u16(cpu, bus),
2391        },
2392        0xA2 => match cpu.registers.p.index_size {
2393            Eight => load::ldx_immediate_u8(cpu, bus),
2394            Sixteen => load::ldx_immediate_u16(cpu, bus),
2395        },
2396        0xA3 => match cpu.registers.p.accumulator_size {
2397            Eight => load::lda_stack_relative_u8(cpu, bus),
2398            Sixteen => load::lda_stack_relative_u16(cpu, bus),
2399        },
2400        0xA4 => match cpu.registers.p.index_size {
2401            Eight => load::ldy_direct_page_u8(cpu, bus),
2402            Sixteen => load::ldy_direct_page_u16(cpu, bus),
2403        },
2404        0xA5 => match cpu.registers.p.accumulator_size {
2405            Eight => load::lda_direct_page_u8(cpu, bus),
2406            Sixteen => load::lda_direct_page_u16(cpu, bus),
2407        },
2408        0xA6 => match cpu.registers.p.index_size {
2409            Eight => load::ldx_direct_page_u8(cpu, bus),
2410            Sixteen => load::ldx_direct_page_u16(cpu, bus),
2411        },
2412        0xA7 => match cpu.registers.p.accumulator_size {
2413            Eight => load::lda_direct_page_indirect_long_u8(cpu, bus),
2414            Sixteen => load::lda_direct_page_indirect_long_u16(cpu, bus),
2415        },
2416        0xA8 => match cpu.registers.p.index_size {
2417            Eight => load::tay_u8(cpu, bus),
2418            Sixteen => load::tay_u16(cpu, bus),
2419        },
2420        0xA9 => match cpu.registers.p.accumulator_size {
2421            Eight => load::lda_immediate_u8(cpu, bus),
2422            Sixteen => load::lda_immediate_u16(cpu, bus),
2423        },
2424        0xAA => match cpu.registers.p.index_size {
2425            Eight => load::tax_u8(cpu, bus),
2426            Sixteen => load::tax_u16(cpu, bus),
2427        },
2428        0xAB => load::plb(cpu, bus),
2429        0xAC => match cpu.registers.p.index_size {
2430            Eight => load::ldy_absolute_u8(cpu, bus),
2431            Sixteen => load::ldy_absolute_u16(cpu, bus),
2432        },
2433        0xAD => match cpu.registers.p.accumulator_size {
2434            Eight => load::lda_absolute_u8(cpu, bus),
2435            Sixteen => load::lda_absolute_u16(cpu, bus),
2436        },
2437        0xAE => match cpu.registers.p.index_size {
2438            Eight => load::ldx_absolute_u8(cpu, bus),
2439            Sixteen => load::ldx_absolute_u16(cpu, bus),
2440        },
2441        0xAF => match cpu.registers.p.accumulator_size {
2442            Eight => load::lda_absolute_long_u8(cpu, bus),
2443            Sixteen => load::lda_absolute_long_u16(cpu, bus),
2444        },
2445        0xB0 => flow::bcs(cpu, bus),
2446        0xB1 => match cpu.registers.p.accumulator_size {
2447            Eight => load::lda_direct_page_indirect_indexed_u8(cpu, bus),
2448            Sixteen => load::lda_direct_page_indirect_indexed_u16(cpu, bus),
2449        },
2450        0xB2 => match cpu.registers.p.accumulator_size {
2451            Eight => load::lda_direct_page_indirect_u8(cpu, bus),
2452            Sixteen => load::lda_direct_page_indirect_u16(cpu, bus),
2453        },
2454        0xB3 => match cpu.registers.p.accumulator_size {
2455            Eight => load::lda_stack_relative_indirect_indexed_u8(cpu, bus),
2456            Sixteen => load::lda_stack_relative_indirect_indexed_u16(cpu, bus),
2457        },
2458        0xB4 => match cpu.registers.p.index_size {
2459            Eight => load::ldy_direct_page_x_u8(cpu, bus),
2460            Sixteen => load::ldy_direct_page_x_u16(cpu, bus),
2461        },
2462        0xB5 => match cpu.registers.p.accumulator_size {
2463            Eight => load::lda_direct_page_x_u8(cpu, bus),
2464            Sixteen => load::lda_direct_page_x_u16(cpu, bus),
2465        },
2466        0xB6 => match cpu.registers.p.index_size {
2467            Eight => load::ldx_direct_page_y_u8(cpu, bus),
2468            Sixteen => load::ldx_direct_page_y_u16(cpu, bus),
2469        },
2470        0xB7 => match cpu.registers.p.accumulator_size {
2471            Eight => load::lda_direct_page_indirect_long_indexed_u8(cpu, bus),
2472            Sixteen => load::lda_direct_page_indirect_long_indexed_u16(cpu, bus),
2473        },
2474        0xB8 => flags::clv(cpu, bus),
2475        0xB9 => match cpu.registers.p.accumulator_size {
2476            Eight => load::lda_absolute_y_u8(cpu, bus),
2477            Sixteen => load::lda_absolute_y_u16(cpu, bus),
2478        },
2479        0xBA => match cpu.registers.p.index_size {
2480            Eight => load::tsx_u8(cpu, bus),
2481            Sixteen => load::tsx_u16(cpu, bus),
2482        },
2483        0xBB => match cpu.registers.p.index_size {
2484            Eight => load::tyx_u8(cpu, bus),
2485            Sixteen => load::tyx_u16(cpu, bus),
2486        },
2487        0xBC => match cpu.registers.p.index_size {
2488            Eight => load::ldy_absolute_x_u8(cpu, bus),
2489            Sixteen => load::ldy_absolute_x_u16(cpu, bus),
2490        },
2491        0xBD => match cpu.registers.p.accumulator_size {
2492            Eight => load::lda_absolute_x_u8(cpu, bus),
2493            Sixteen => load::lda_absolute_x_u16(cpu, bus),
2494        },
2495        0xBE => match cpu.registers.p.index_size {
2496            Eight => load::ldx_absolute_y_u8(cpu, bus),
2497            Sixteen => load::ldx_absolute_y_u16(cpu, bus),
2498        },
2499        0xBF => match cpu.registers.p.accumulator_size {
2500            Eight => load::lda_absolute_long_x_u8(cpu, bus),
2501            Sixteen => load::lda_absolute_long_x_u16(cpu, bus),
2502        },
2503        0xC0 => match cpu.registers.p.index_size {
2504            Eight => alu::cpy_immediate_u8(cpu, bus),
2505            Sixteen => alu::cpy_immediate_u16(cpu, bus),
2506        },
2507        0xC1 => match cpu.registers.p.accumulator_size {
2508            Eight => alu::cmp_direct_page_indexed_indirect_u8(cpu, bus),
2509            Sixteen => alu::cmp_direct_page_indexed_indirect_u16(cpu, bus),
2510        },
2511        0xC2 => flags::rep(cpu, bus),
2512        0xC3 => match cpu.registers.p.accumulator_size {
2513            Eight => alu::cmp_stack_relative_u8(cpu, bus),
2514            Sixteen => alu::cmp_stack_relative_u16(cpu, bus),
2515        },
2516        0xC4 => match cpu.registers.p.index_size {
2517            Eight => alu::cpy_direct_page_u8(cpu, bus),
2518            Sixteen => alu::cpy_direct_page_u16(cpu, bus),
2519        },
2520        0xC5 => match cpu.registers.p.accumulator_size {
2521            Eight => alu::cmp_direct_page_u8(cpu, bus),
2522            Sixteen => alu::cmp_direct_page_u16(cpu, bus),
2523        },
2524        0xC6 => match cpu.registers.p.accumulator_size {
2525            Eight => alu::dec_direct_page_u8(cpu, bus),
2526            Sixteen => alu::dec_direct_page_u16(cpu, bus),
2527        },
2528        0xC7 => match cpu.registers.p.accumulator_size {
2529            Eight => alu::cmp_direct_page_indirect_long_u8(cpu, bus),
2530            Sixteen => alu::cmp_direct_page_indirect_long_u16(cpu, bus),
2531        },
2532        0xC8 => match cpu.registers.p.index_size {
2533            Eight => alu::iny_u8(cpu, bus),
2534            Sixteen => alu::iny_u16(cpu, bus),
2535        },
2536        0xC9 => match cpu.registers.p.accumulator_size {
2537            Eight => alu::cmp_immediate_u8(cpu, bus),
2538            Sixteen => alu::cmp_immediate_u16(cpu, bus),
2539        },
2540        0xCA => match cpu.registers.p.index_size {
2541            Eight => alu::dex_u8(cpu, bus),
2542            Sixteen => alu::dex_u16(cpu, bus),
2543        },
2544        0xCB => flow::wai(cpu, bus),
2545        0xCC => match cpu.registers.p.index_size {
2546            Eight => alu::cpy_absolute_u8(cpu, bus),
2547            Sixteen => alu::cpy_absolute_u16(cpu, bus),
2548        },
2549        0xCD => match cpu.registers.p.accumulator_size {
2550            Eight => alu::cmp_absolute_u8(cpu, bus),
2551            Sixteen => alu::cmp_absolute_u16(cpu, bus),
2552        },
2553        0xCE => match cpu.registers.p.accumulator_size {
2554            Eight => alu::dec_absolute_u8(cpu, bus),
2555            Sixteen => alu::dec_absolute_u16(cpu, bus),
2556        },
2557        0xCF => match cpu.registers.p.accumulator_size {
2558            Eight => alu::cmp_absolute_long_u8(cpu, bus),
2559            Sixteen => alu::cmp_absolute_long_u16(cpu, bus),
2560        },
2561        0xD0 => flow::bne(cpu, bus),
2562        0xD1 => match cpu.registers.p.accumulator_size {
2563            Eight => alu::cmp_direct_page_indirect_indexed_u8(cpu, bus),
2564            Sixteen => alu::cmp_direct_page_indirect_indexed_u16(cpu, bus),
2565        },
2566        0xD2 => match cpu.registers.p.accumulator_size {
2567            Eight => alu::cmp_direct_page_indirect_u8(cpu, bus),
2568            Sixteen => alu::cmp_direct_page_indirect_u16(cpu, bus),
2569        },
2570        0xD3 => match cpu.registers.p.accumulator_size {
2571            Eight => alu::cmp_stack_relative_indirect_indexed_u8(cpu, bus),
2572            Sixteen => alu::cmp_stack_relative_indirect_indexed_u16(cpu, bus),
2573        },
2574        0xD4 => flow::pei(cpu, bus),
2575        0xD5 => match cpu.registers.p.accumulator_size {
2576            Eight => alu::cmp_direct_page_x_u8(cpu, bus),
2577            Sixteen => alu::cmp_direct_page_x_u16(cpu, bus),
2578        },
2579        0xD6 => match cpu.registers.p.accumulator_size {
2580            Eight => alu::dec_direct_page_x_u8(cpu, bus),
2581            Sixteen => alu::dec_direct_page_x_u16(cpu, bus),
2582        },
2583        0xD7 => match cpu.registers.p.accumulator_size {
2584            Eight => alu::cmp_direct_page_indirect_long_indexed_u8(cpu, bus),
2585            Sixteen => alu::cmp_direct_page_indirect_long_indexed_u16(cpu, bus),
2586        },
2587        0xD8 => flags::cld(cpu, bus),
2588        0xD9 => match cpu.registers.p.accumulator_size {
2589            Eight => alu::cmp_absolute_y_u8(cpu, bus),
2590            Sixteen => alu::cmp_absolute_y_u16(cpu, bus),
2591        },
2592        0xDA => match cpu.registers.p.index_size {
2593            Eight => load::phx_u8(cpu, bus),
2594            Sixteen => load::phx_u16(cpu, bus),
2595        },
2596        0xDB => flow::stp(cpu, bus),
2597        0xDC => flow::jmp_indirect_long(cpu, bus),
2598        0xDD => match cpu.registers.p.accumulator_size {
2599            Eight => alu::cmp_absolute_x_u8(cpu, bus),
2600            Sixteen => alu::cmp_absolute_x_u16(cpu, bus),
2601        },
2602        0xDE => match cpu.registers.p.accumulator_size {
2603            Eight => alu::dec_absolute_x_u8(cpu, bus),
2604            Sixteen => alu::dec_absolute_x_u16(cpu, bus),
2605        },
2606        0xDF => match cpu.registers.p.accumulator_size {
2607            Eight => alu::cmp_absolute_long_x_u8(cpu, bus),
2608            Sixteen => alu::cmp_absolute_long_x_u16(cpu, bus),
2609        },
2610        0xE0 => match cpu.registers.p.index_size {
2611            Eight => alu::cpx_immediate_u8(cpu, bus),
2612            Sixteen => alu::cpx_immediate_u16(cpu, bus),
2613        },
2614        0xE1 => match cpu.registers.p.accumulator_size {
2615            Eight => alu::sbc_direct_page_indexed_indirect_u8(cpu, bus),
2616            Sixteen => alu::sbc_direct_page_indexed_indirect_u16(cpu, bus),
2617        },
2618        0xE2 => flags::sep(cpu, bus),
2619        0xE3 => match cpu.registers.p.accumulator_size {
2620            Eight => alu::sbc_stack_relative_u8(cpu, bus),
2621            Sixteen => alu::sbc_stack_relative_u16(cpu, bus),
2622        },
2623        0xE4 => match cpu.registers.p.index_size {
2624            Eight => alu::cpx_direct_page_u8(cpu, bus),
2625            Sixteen => alu::cpx_direct_page_u16(cpu, bus),
2626        },
2627        0xE5 => match cpu.registers.p.accumulator_size {
2628            Eight => alu::sbc_direct_page_u8(cpu, bus),
2629            Sixteen => alu::sbc_direct_page_u16(cpu, bus),
2630        },
2631        0xE6 => match cpu.registers.p.accumulator_size {
2632            Eight => alu::inc_direct_page_u8(cpu, bus),
2633            Sixteen => alu::inc_direct_page_u16(cpu, bus),
2634        },
2635        0xE7 => match cpu.registers.p.accumulator_size {
2636            Eight => alu::sbc_direct_page_indirect_long_u8(cpu, bus),
2637            Sixteen => alu::sbc_direct_page_indirect_long_u16(cpu, bus),
2638        },
2639        0xE8 => match cpu.registers.p.index_size {
2640            Eight => alu::inx_u8(cpu, bus),
2641            Sixteen => alu::inx_u16(cpu, bus),
2642        },
2643        0xE9 => match cpu.registers.p.accumulator_size {
2644            Eight => alu::sbc_immediate_u8(cpu, bus),
2645            Sixteen => alu::sbc_immediate_u16(cpu, bus),
2646        },
2647        0xEA => nop(cpu, bus),
2648        0xEB => load::xba(cpu, bus),
2649        0xEC => match cpu.registers.p.index_size {
2650            Eight => alu::cpx_absolute_u8(cpu, bus),
2651            Sixteen => alu::cpx_absolute_u16(cpu, bus),
2652        },
2653        0xED => match cpu.registers.p.accumulator_size {
2654            Eight => alu::sbc_absolute_u8(cpu, bus),
2655            Sixteen => alu::sbc_absolute_u16(cpu, bus),
2656        },
2657        0xEE => match cpu.registers.p.accumulator_size {
2658            Eight => alu::inc_absolute_u8(cpu, bus),
2659            Sixteen => alu::inc_absolute_u16(cpu, bus),
2660        },
2661        0xEF => match cpu.registers.p.accumulator_size {
2662            Eight => alu::sbc_absolute_long_u8(cpu, bus),
2663            Sixteen => alu::sbc_absolute_long_u16(cpu, bus),
2664        },
2665        0xF0 => flow::beq(cpu, bus),
2666        0xF1 => match cpu.registers.p.accumulator_size {
2667            Eight => alu::sbc_direct_page_indirect_indexed_u8(cpu, bus),
2668            Sixteen => alu::sbc_direct_page_indirect_indexed_u16(cpu, bus),
2669        },
2670        0xF2 => match cpu.registers.p.accumulator_size {
2671            Eight => alu::sbc_direct_page_indirect_u8(cpu, bus),
2672            Sixteen => alu::sbc_direct_page_indirect_u16(cpu, bus),
2673        },
2674        0xF3 => match cpu.registers.p.accumulator_size {
2675            Eight => alu::sbc_stack_relative_indirect_indexed_u8(cpu, bus),
2676            Sixteen => alu::sbc_stack_relative_indirect_indexed_u16(cpu, bus),
2677        },
2678        0xF4 => flow::pea(cpu, bus),
2679        0xF5 => match cpu.registers.p.accumulator_size {
2680            Eight => alu::sbc_direct_page_x_u8(cpu, bus),
2681            Sixteen => alu::sbc_direct_page_x_u16(cpu, bus),
2682        },
2683        0xF6 => match cpu.registers.p.accumulator_size {
2684            Eight => alu::inc_direct_page_x_u8(cpu, bus),
2685            Sixteen => alu::inc_direct_page_x_u16(cpu, bus),
2686        },
2687        0xF7 => match cpu.registers.p.accumulator_size {
2688            Eight => alu::sbc_direct_page_indirect_long_indexed_u8(cpu, bus),
2689            Sixteen => alu::sbc_direct_page_indirect_long_indexed_u16(cpu, bus),
2690        },
2691        0xF8 => flags::sed(cpu, bus),
2692        0xF9 => match cpu.registers.p.accumulator_size {
2693            Eight => alu::sbc_absolute_y_u8(cpu, bus),
2694            Sixteen => alu::sbc_absolute_y_u16(cpu, bus),
2695        },
2696        0xFA => match cpu.registers.p.index_size {
2697            Eight => load::plx_u8(cpu, bus),
2698            Sixteen => load::plx_u16(cpu, bus),
2699        },
2700        0xFB => flags::xce(cpu, bus),
2701        0xFC => flow::jsr_indirect_indexed(cpu, bus),
2702        0xFD => match cpu.registers.p.accumulator_size {
2703            Eight => alu::sbc_absolute_x_u8(cpu, bus),
2704            Sixteen => alu::sbc_absolute_x_u16(cpu, bus),
2705        },
2706        0xFE => match cpu.registers.p.accumulator_size {
2707            Eight => alu::inc_absolute_x_u8(cpu, bus),
2708            Sixteen => alu::inc_absolute_x_u16(cpu, bus),
2709        },
2710        0xFF => match cpu.registers.p.accumulator_size {
2711            Eight => alu::sbc_absolute_long_x_u8(cpu, bus),
2712            Sixteen => alu::sbc_absolute_long_x_u16(cpu, bus),
2713        },
2714    }
2715}