jevsnes.git / third-party / rust / jgenesis / cpu / mos6502-emu / src / instructions.rs
1use bincode::{Decode, Encode};
2
3use jgenesis_common::num::{GetBit, SignBit};
4
5use crate::bus::BusInterface;
6use crate::{CpuRegisters, IRQ_VECTOR, Mos6502, NMI_VECTOR, StatusFlags, StatusReadContext};
7
8#[derive(Debug, Clone, Encode, Decode)]
9pub struct InstructionState {
10    pub opcode: u8,
11    pub executing_interrupt: bool,
12    pub cycle: u8,
13    pub operand_first_byte: u8,
14    pub operand_second_byte: u8,
15    pub target_first_byte: u8,
16    pub target_second_byte: u8,
17    pub indirect_byte: u8,
18    pub interrupt_vector: u16,
19    pub pending_interrupt: bool,
20    pub instruction_complete: bool,
21}
22
23impl Default for InstructionState {
24    fn default() -> Self {
25        Self {
26            opcode: 0,
27            executing_interrupt: false,
28            cycle: 0,
29            operand_first_byte: 0,
30            operand_second_byte: 0,
31            target_first_byte: 0,
32            target_second_byte: 0,
33            indirect_byte: 0,
34            interrupt_vector: 0,
35            pending_interrupt: false,
36            instruction_complete: true,
37        }
38    }
39}
40
41#[inline]
42fn poll_interrupt_lines<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
43    cpu.state.pending_interrupt |=
44        bus.nmi() || (!cpu.registers.status.interrupt_disable && bus.irq());
45}
46
47#[inline]
48fn final_cycle<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
49    poll_interrupt_lines(cpu, bus);
50    cpu.state.instruction_complete = true;
51}
52
53#[inline]
54fn fetch_operand<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) -> u8 {
55    let operand = bus.read(cpu.registers.pc);
56    cpu.registers.pc = cpu.registers.pc.wrapping_add(1);
57    operand
58}
59
60macro_rules! invalid_cycle {
61    ($cpu:expr) => {
62        panic!("Invalid cycle: {}", $cpu.state.cycle)
63    };
64}
65
66macro_rules! impl_read_immediate {
67    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {{
68        final_cycle($cpu, $bus);
69
70        let $operand = fetch_operand($cpu, $bus);
71        let $registers_param = &mut $cpu.registers;
72        $body
73    }};
74}
75
76macro_rules! impl_read_zero_page {
77    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
78        match $cpu.state.cycle {
79            0 => {
80                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
81            }
82            1 => {
83                final_cycle($cpu, $bus);
84
85                let $operand = $bus.read($cpu.state.operand_first_byte.into());
86                let $registers_param = &mut $cpu.registers;
87                $body
88            }
89            _ => invalid_cycle!($cpu),
90        }
91    };
92}
93
94macro_rules! impl_read_zero_page_indexed {
95    (index: $index:ident, $cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
96        match $cpu.state.cycle {
97            0 => {
98                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
99            }
100            1 => {
101                $bus.read($cpu.state.operand_first_byte.into());
102            }
103            2 => {
104                final_cycle($cpu, $bus);
105
106                let index = $cpu.registers.$index;
107                let address = $cpu.state.operand_first_byte.wrapping_add(index);
108                let $operand = $bus.read(address.into());
109                let $registers_param = &mut $cpu.registers;
110                $body
111            }
112            _ => invalid_cycle!($cpu),
113        }
114    };
115}
116
117macro_rules! impl_read_absolute {
118    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
119        match $cpu.state.cycle {
120            0 => {
121                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
122            }
123            1 => {
124                $cpu.state.operand_second_byte = fetch_operand($cpu, $bus);
125            }
126            2 => {
127                final_cycle($cpu, $bus);
128
129                let address = u16::from_le_bytes([
130                    $cpu.state.operand_first_byte,
131                    $cpu.state.operand_second_byte,
132                ]);
133                let $operand = $bus.read(address);
134                let $registers_param = &mut $cpu.registers;
135                $body
136            }
137            _ => invalid_cycle!($cpu),
138        }
139    };
140}
141
142macro_rules! impl_read_absolute_indexed {
143    (index: $index:ident, $cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
144        match $cpu.state.cycle {
145            0 => {
146                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
147            }
148            1 => {
149                $cpu.state.operand_second_byte = fetch_operand($cpu, $bus);
150            }
151            2 => {
152                poll_interrupt_lines($cpu, $bus);
153
154                let (address_lsb, overflowed) =
155                    $cpu.state.operand_first_byte.overflowing_add($cpu.registers.$index);
156                let address = u16::from_le_bytes([address_lsb, $cpu.state.operand_second_byte]);
157                let $operand = $bus.read(address);
158
159                if !overflowed {
160                    let $registers_param = &mut $cpu.registers;
161                    $body
162                    $cpu.state.instruction_complete = true;
163                }
164            }
165            3 => {
166                final_cycle($cpu, $bus);
167
168                let address = u16::from_le_bytes([$cpu.state.operand_first_byte, $cpu.state.operand_second_byte])
169                    .wrapping_add($cpu.registers.$index.into());
170                let $operand = $bus.read(address);
171                let $registers_param = &mut $cpu.registers;
172                $body
173            }
174            _ => invalid_cycle!($cpu)
175        }
176    }
177}
178
179macro_rules! impl_read_indirect_x {
180    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
181        match $cpu.state.cycle {
182            0 => {
183                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
184            }
185            1 => {
186                $bus.read($cpu.state.operand_first_byte.into());
187            }
188            2 => {
189                let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x);
190                $cpu.state.target_first_byte = $bus.read(address.into());
191            }
192            3 => {
193                let address =
194                    $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).wrapping_add(1);
195                $cpu.state.target_second_byte = $bus.read(address.into());
196            }
197            4 => {
198                final_cycle($cpu, $bus);
199
200                let address = u16::from_le_bytes([
201                    $cpu.state.target_first_byte,
202                    $cpu.state.target_second_byte,
203                ]);
204                let $operand = $bus.read(address);
205                let $registers_param = &mut $cpu.registers;
206                $body
207            }
208            _ => invalid_cycle!($cpu),
209        }
210    };
211}
212
213macro_rules! impl_read_indirect_y {
214    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
215        match $cpu.state.cycle {
216            0 => {
217                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
218            }
219            1 => {
220                $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into());
221            }
222            2 => {
223                $cpu.state.target_second_byte =
224                    $bus.read($cpu.state.operand_first_byte.wrapping_add(1).into());
225            }
226            3 => {
227                poll_interrupt_lines($cpu, $bus);
228
229                let (address_lsb, overflowed) = $cpu.state.target_first_byte.overflowing_add($cpu.registers.y);
230                let address = u16::from_le_bytes([address_lsb, $cpu.state.target_second_byte]);
231                let $operand = $bus.read(address);
232
233                if !overflowed {
234                    let $registers_param = &mut $cpu.registers;
235                    $body
236                    $cpu.state.instruction_complete = true;
237                }
238            }
239            4 => {
240                final_cycle($cpu, $bus);
241
242                let address = u16::from_le_bytes([$cpu.state.target_first_byte, $cpu.state.target_second_byte])
243                    .wrapping_add($cpu.registers.y.into());
244                let $operand = $bus.read(address);
245                let $registers_param = &mut $cpu.registers;
246                $body
247            }
248            _ => invalid_cycle!($cpu)
249        }
250    }
251}
252
253macro_rules! impl_read_instruction {
254    (immediate, $($rest:tt)*) => {
255        impl_read_immediate!($($rest)*)
256    };
257    (zero_page, $($rest:tt)*) => {
258        impl_read_zero_page!($($rest)*)
259    };
260    (zero_page_x, $($rest:tt)*) => {
261        impl_read_zero_page_indexed!(index: x, $($rest)*)
262    };
263    (zero_page_y, $($rest:tt)*) => {
264        impl_read_zero_page_indexed!(index: y, $($rest)*)
265    };
266    (absolute, $($rest:tt)*) => {
267        impl_read_absolute!($($rest)*)
268    };
269    (absolute_x, $($rest:tt)*) => {
270        impl_read_absolute_indexed!(index: x, $($rest)*)
271    };
272    (absolute_y, $($rest:tt)*) => {
273        impl_read_absolute_indexed!(index: y, $($rest)*)
274    };
275    (indirect_x, $($rest:tt)*) => {
276        impl_read_indirect_x!($($rest)*)
277    };
278    (indirect_y, $($rest:tt)*) => {
279        impl_read_indirect_y!($($rest)*)
280    };
281}
282
283macro_rules! impl_read_fn {
284    ($name:ident, $addressing_mode:tt, |$operand:ident, $registers:ident| $body:block) => {
285        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
286            impl_read_instruction!($addressing_mode, cpu, bus, |$operand, $registers| $body);
287        }
288    };
289}
290
291macro_rules! impl_store_zero_page {
292    ($cpu:expr, $bus:expr, $register:expr) => {
293        match $cpu.state.cycle {
294            0 => {
295                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
296            }
297            1 => {
298                final_cycle($cpu, $bus);
299
300                let address = $cpu.state.operand_first_byte.into();
301                $bus.write(address, $register);
302            }
303            _ => invalid_cycle!($cpu),
304        }
305    };
306}
307
308macro_rules! impl_store_zero_page_indexed {
309    (index: $index:ident, $cpu:expr, $bus:expr, $register:expr) => {
310        match $cpu.state.cycle {
311            0 => {
312                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
313            }
314            1 => {
315                $bus.read($cpu.state.operand_first_byte.into());
316            }
317            2 => {
318                final_cycle($cpu, $bus);
319
320                let address =
321                    $cpu.state.operand_first_byte.wrapping_add($cpu.registers.$index).into();
322                $bus.write(address, $register);
323            }
324            _ => invalid_cycle!($cpu),
325        }
326    };
327}
328
329macro_rules! impl_store_absolute {
330    ($cpu:expr, $bus:expr, $register:expr) => {
331        match $cpu.state.cycle {
332            0 => {
333                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
334            }
335            1 => {
336                $cpu.state.operand_second_byte = fetch_operand($cpu, $bus);
337            }
338            2 => {
339                final_cycle($cpu, $bus);
340
341                let address = u16::from_le_bytes([
342                    $cpu.state.operand_first_byte,
343                    $cpu.state.operand_second_byte,
344                ]);
345                $bus.write(address, $register);
346            }
347            _ => invalid_cycle!($cpu),
348        }
349    };
350}
351
352macro_rules! impl_store_absolute_indexed {
353    (index: $index:ident, $cpu:expr, $bus:expr, $register:expr) => {
354        match $cpu.state.cycle {
355            0 => {
356                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
357            }
358            1 => {
359                $cpu.state.operand_second_byte = fetch_operand($cpu, $bus);
360            }
361            2 => {
362                let address_lsb = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.$index);
363                let address = u16::from_le_bytes([address_lsb, $cpu.state.operand_second_byte]);
364                $bus.read(address);
365            }
366            3 => {
367                final_cycle($cpu, $bus);
368
369                let address = u16::from_le_bytes([
370                    $cpu.state.operand_first_byte,
371                    $cpu.state.operand_second_byte,
372                ])
373                .wrapping_add($cpu.registers.$index.into());
374                $bus.write(address, $register);
375            }
376            _ => invalid_cycle!($cpu),
377        }
378    };
379}
380
381macro_rules! impl_store_indirect_x {
382    ($cpu:expr, $bus:expr, $register:expr) => {
383        match $cpu.state.cycle {
384            0 => {
385                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
386            }
387            1 => {
388                $bus.read($cpu.state.operand_first_byte.into());
389            }
390            2 => {
391                let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x);
392                $cpu.state.target_first_byte = $bus.read(address.into());
393            }
394            3 => {
395                let address =
396                    $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).wrapping_add(1);
397                $cpu.state.target_second_byte = $bus.read(address.into());
398            }
399            4 => {
400                final_cycle($cpu, $bus);
401
402                let address = u16::from_le_bytes([
403                    $cpu.state.target_first_byte,
404                    $cpu.state.target_second_byte,
405                ]);
406                $bus.write(address, $register);
407            }
408            _ => invalid_cycle!($cpu),
409        }
410    };
411}
412
413macro_rules! impl_store_indirect_y {
414    ($cpu:expr, $bus:expr, $register:expr) => {
415        match $cpu.state.cycle {
416            0 => {
417                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
418            }
419            1 => {
420                $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into());
421            }
422            2 => {
423                let address = $cpu.state.operand_first_byte.wrapping_add(1);
424                $cpu.state.target_second_byte = $bus.read(address.into());
425            }
426            3 => {
427                let address_lsb = $cpu.state.target_first_byte.wrapping_add($cpu.registers.y);
428                let address = u16::from_le_bytes([address_lsb, $cpu.state.target_second_byte]);
429                $bus.read(address);
430            }
431            4 => {
432                final_cycle($cpu, $bus);
433
434                let address = u16::from_le_bytes([
435                    $cpu.state.target_first_byte,
436                    $cpu.state.target_second_byte,
437                ])
438                .wrapping_add($cpu.registers.y.into());
439                $bus.write(address, $register);
440            }
441            _ => invalid_cycle!($cpu),
442        }
443    };
444}
445
446macro_rules! impl_store {
447    (zero_page, $($rest:tt)*) => {
448        impl_store_zero_page!($($rest)*)
449    };
450    (zero_page_x, $($rest:tt)*) => {
451        impl_store_zero_page_indexed!(index: x, $($rest)*)
452    };
453    (zero_page_y, $($rest:tt)*) => {
454        impl_store_zero_page_indexed!(index: y, $($rest)*)
455    };
456    (absolute, $($rest:tt)*) => {
457        impl_store_absolute!($($rest)*)
458    };
459    (absolute_x, $($rest:tt)*) => {
460        impl_store_absolute_indexed!(index: x, $($rest)*)
461    };
462    (absolute_y, $($rest:tt)*) => {
463        impl_store_absolute_indexed!(index: y, $($rest)*)
464    };
465    (indirect_x, $($rest:tt)*) => {
466        impl_store_indirect_x!($($rest)*)
467    };
468    (indirect_y, $($rest:tt)*) => {
469        impl_store_indirect_y!($($rest)*)
470    };
471}
472
473// STA, STX, STY, unofficial SAX
474macro_rules! impl_store_fn {
475    ($name:ident, ax, $addressing_mode:tt) => {
476        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
477            impl_store!($addressing_mode, cpu, bus, cpu.registers.accumulator & cpu.registers.x);
478        }
479    };
480    ($name:ident, $register:ident, $addressing_mode:tt) => {
481        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
482            impl_store!($addressing_mode, cpu, bus, cpu.registers.$register);
483        }
484    };
485}
486
487impl_store_fn!(sta_zero_page, accumulator, zero_page);
488impl_store_fn!(sta_zero_page_x, accumulator, zero_page_x);
489impl_store_fn!(sta_absolute, accumulator, absolute);
490impl_store_fn!(sta_absolute_x, accumulator, absolute_x);
491impl_store_fn!(sta_absolute_y, accumulator, absolute_y);
492impl_store_fn!(sta_indirect_x, accumulator, indirect_x);
493impl_store_fn!(sta_indirect_y, accumulator, indirect_y);
494
495impl_store_fn!(stx_zero_page, x, zero_page);
496impl_store_fn!(stx_zero_page_y, x, zero_page_y);
497impl_store_fn!(stx_absolute, x, absolute);
498
499impl_store_fn!(sty_zero_page, y, zero_page);
500impl_store_fn!(sty_zero_page_x, y, zero_page_x);
501impl_store_fn!(sty_absolute, y, absolute);
502
503impl_store_fn!(sax_zero_page, ax, zero_page);
504impl_store_fn!(sax_zero_page_y, ax, zero_page_y);
505impl_store_fn!(sax_absolute, ax, absolute);
506impl_store_fn!(sax_indirect_x, ax, indirect_x);
507
508macro_rules! impl_modify_accumulator {
509    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {{
510        final_cycle($cpu, $bus);
511
512        $bus.read($cpu.registers.pc);
513
514        let $operand = $cpu.registers.accumulator;
515        let $registers_param = &mut $cpu.registers;
516        $registers_param.accumulator = $body;
517    }};
518}
519
520macro_rules! impl_modify_zero_page {
521    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
522        match $cpu.state.cycle {
523            0 => {
524                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
525            }
526            1 => {
527                $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into());
528            }
529            2 => {
530                $bus.write($cpu.state.operand_first_byte.into(), $cpu.state.target_first_byte);
531            }
532            3 => {
533                final_cycle($cpu, $bus);
534
535                let $operand = $cpu.state.target_first_byte;
536                let $registers_param = &mut $cpu.registers;
537                let value = $body;
538                $bus.write($cpu.state.operand_first_byte.into(), value);
539            }
540            _ => invalid_cycle!($cpu),
541        }
542    };
543}
544
545macro_rules! impl_modify_zero_page_x {
546    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
547        match $cpu.state.cycle {
548            0 => {
549                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
550            }
551            1 => {
552                $bus.read($cpu.state.operand_first_byte.into());
553            }
554            2 => {
555                let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).into();
556                $cpu.state.target_first_byte = $bus.read(address);
557            }
558            3 => {
559                let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).into();
560                $bus.write(address, $cpu.state.target_first_byte);
561            }
562            4 => {
563                final_cycle($cpu, $bus);
564
565                let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).into();
566
567                let $operand = $cpu.state.target_first_byte;
568                let $registers_param = &mut $cpu.registers;
569                let value = $body;
570
571                $bus.write(address, value);
572            }
573            _ => invalid_cycle!($cpu),
574        }
575    };
576}
577
578macro_rules! impl_modify_absolute {
579    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
580        match $cpu.state.cycle {
581            0 => {
582                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
583            }
584            1 => {
585                $cpu.state.operand_second_byte = fetch_operand($cpu, $bus);
586            }
587            2 => {
588                let address = u16::from_le_bytes([
589                    $cpu.state.operand_first_byte,
590                    $cpu.state.operand_second_byte,
591                ]);
592                $cpu.state.target_first_byte = $bus.read(address);
593            }
594            3 => {
595                let address = u16::from_le_bytes([
596                    $cpu.state.operand_first_byte,
597                    $cpu.state.operand_second_byte,
598                ]);
599                $bus.write(address, $cpu.state.target_first_byte);
600            }
601            4 => {
602                final_cycle($cpu, $bus);
603
604                let $operand = $cpu.state.target_first_byte;
605                let $registers_param = &mut $cpu.registers;
606                let value = $body;
607
608                let address = u16::from_le_bytes([
609                    $cpu.state.operand_first_byte,
610                    $cpu.state.operand_second_byte,
611                ]);
612                $bus.write(address, value);
613            }
614            _ => invalid_cycle!($cpu),
615        }
616    };
617}
618
619macro_rules! impl_modify_absolute_indexed {
620    (index: $index:ident, $cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
621        match $cpu.state.cycle {
622            0 => {
623                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
624            }
625            1 => {
626                $cpu.state.operand_second_byte = fetch_operand($cpu, $bus);
627            }
628            2 => {
629                let address_lsb = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.$index);
630                let address = u16::from_le_bytes([address_lsb, $cpu.state.operand_second_byte]);
631                $bus.read(address);
632            }
633            3 => {
634                let address = u16::from_le_bytes([
635                    $cpu.state.operand_first_byte,
636                    $cpu.state.operand_second_byte,
637                ])
638                .wrapping_add($cpu.registers.$index.into());
639                $cpu.state.target_first_byte = $bus.read(address);
640            }
641            4 => {
642                let address = u16::from_le_bytes([
643                    $cpu.state.operand_first_byte,
644                    $cpu.state.operand_second_byte,
645                ])
646                .wrapping_add($cpu.registers.$index.into());
647                $bus.write(address, $cpu.state.target_first_byte);
648            }
649            5 => {
650                final_cycle($cpu, $bus);
651
652                let address = u16::from_le_bytes([
653                    $cpu.state.operand_first_byte,
654                    $cpu.state.operand_second_byte,
655                ])
656                .wrapping_add($cpu.registers.$index.into());
657
658                let $operand = $cpu.state.target_first_byte;
659                let $registers_param = &mut $cpu.registers;
660                let value = $body;
661
662                $bus.write(address, value);
663            }
664            _ => invalid_cycle!($cpu),
665        }
666    };
667}
668
669macro_rules! impl_modify_indirect_x {
670    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
671        match $cpu.state.cycle {
672            0 => {
673                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
674            }
675            1 => {
676                $bus.read($cpu.state.operand_first_byte.into());
677            }
678            2 => {
679                let address = $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x);
680                $cpu.state.target_first_byte = $bus.read(address.into());
681            }
682            3 => {
683                let address =
684                    $cpu.state.operand_first_byte.wrapping_add($cpu.registers.x).wrapping_add(1);
685                $cpu.state.target_second_byte = $bus.read(address.into());
686            }
687            4 => {
688                let address = u16::from_le_bytes([
689                    $cpu.state.target_first_byte,
690                    $cpu.state.target_second_byte,
691                ]);
692                $cpu.state.indirect_byte = $bus.read(address);
693            }
694            5 => {
695                let address = u16::from_le_bytes([
696                    $cpu.state.target_first_byte,
697                    $cpu.state.target_second_byte,
698                ]);
699                $bus.write(address, $cpu.state.indirect_byte);
700            }
701            6 => {
702                final_cycle($cpu, $bus);
703
704                let $operand = $cpu.state.indirect_byte;
705                let $registers_param = &mut $cpu.registers;
706                let value = $body;
707
708                let address = u16::from_le_bytes([
709                    $cpu.state.target_first_byte,
710                    $cpu.state.target_second_byte,
711                ]);
712                $bus.write(address, value);
713            }
714            _ => invalid_cycle!($cpu),
715        }
716    };
717}
718
719macro_rules! impl_modify_indirect_y {
720    ($cpu:expr, $bus:expr, |$operand:ident, $registers_param:ident| $body:block) => {
721        match $cpu.state.cycle {
722            0 => {
723                $cpu.state.operand_first_byte = fetch_operand($cpu, $bus);
724            }
725            1 => {
726                $cpu.state.target_first_byte = $bus.read($cpu.state.operand_first_byte.into());
727            }
728            2 => {
729                let address = $cpu.state.operand_first_byte.wrapping_add(1).into();
730                $cpu.state.target_second_byte = $bus.read(address);
731            }
732            3 => {
733                let address_lsb = $cpu.state.target_first_byte.wrapping_add($cpu.registers.y);
734                let address = u16::from_le_bytes([address_lsb, $cpu.state.target_second_byte]);
735                $bus.read(address);
736            }
737            4 => {
738                let address = u16::from_le_bytes([
739                    $cpu.state.target_first_byte,
740                    $cpu.state.target_second_byte,
741                ])
742                .wrapping_add($cpu.registers.y.into());
743                $cpu.state.indirect_byte = $bus.read(address);
744            }
745            5 => {
746                let address = u16::from_le_bytes([
747                    $cpu.state.target_first_byte,
748                    $cpu.state.target_second_byte,
749                ])
750                .wrapping_add($cpu.registers.y.into());
751                $bus.write(address, $cpu.state.indirect_byte);
752            }
753            6 => {
754                final_cycle($cpu, $bus);
755
756                let address = u16::from_le_bytes([
757                    $cpu.state.target_first_byte,
758                    $cpu.state.target_second_byte,
759                ])
760                .wrapping_add($cpu.registers.y.into());
761
762                let $operand = $cpu.state.indirect_byte;
763                let $registers_param = &mut $cpu.registers;
764                let value = $body;
765
766                $bus.write(address, value);
767            }
768            _ => invalid_cycle!($cpu),
769        }
770    };
771}
772
773macro_rules! impl_modify_instruction {
774    (accumulator, $($rest:tt)*) => {
775        impl_modify_accumulator!($($rest)*)
776    };
777    (zero_page, $($rest:tt)*) => {
778        impl_modify_zero_page!($($rest)*)
779    };
780    (zero_page_x, $($rest:tt)*) => {
781        impl_modify_zero_page_x!($($rest)*)
782    };
783    (absolute, $($rest:tt)*) => {
784        impl_modify_absolute!($($rest)*)
785    };
786    (absolute_x, $($rest:tt)*) => {
787        impl_modify_absolute_indexed!(index: x, $($rest)*)
788    };
789    (absolute_y, $($rest:tt)*) => {
790        impl_modify_absolute_indexed!(index: y, $($rest)*)
791    };
792    (indirect_x, $($rest:tt)*) => {
793        impl_modify_indirect_x!($($rest)*)
794    };
795    (indirect_y, $($rest:tt)*) => {
796        impl_modify_indirect_y!($($rest)*)
797    };
798}
799
800macro_rules! impl_modify_fn {
801    ($name:ident, $addressing_mode:tt, |$operand:ident, $registers:ident| $body:block) => {
802        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
803            impl_modify_instruction!($addressing_mode, cpu, bus, |$operand, $registers| $body);
804        }
805    };
806}
807
808macro_rules! impl_registers_only_fn {
809    ($name:ident, |$registers:ident| $body:block) => {
810        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
811            final_cycle(cpu, bus);
812
813            // Spurious operand read
814            bus.read(cpu.registers.pc);
815
816            let $registers = &mut cpu.registers;
817            $body
818        }
819    };
820}
821
822// LDA, LDX, LDY
823macro_rules! impl_load {
824    ($name:ident, $register:ident, $addressing_mode:tt) => {
825        impl_read_fn!($name, $addressing_mode, |operand, registers| {
826            registers.$register = operand;
827            registers.status.set_negative(operand.bit(7)).set_zero(operand == 0);
828        });
829    };
830}
831
832impl_load!(lda_immediate, accumulator, immediate);
833impl_load!(lda_zero_page, accumulator, zero_page);
834impl_load!(lda_zero_page_x, accumulator, zero_page_x);
835impl_load!(lda_absolute, accumulator, absolute);
836impl_load!(lda_absolute_x, accumulator, absolute_x);
837impl_load!(lda_absolute_y, accumulator, absolute_y);
838impl_load!(lda_indirect_x, accumulator, indirect_x);
839impl_load!(lda_indirect_y, accumulator, indirect_y);
840
841impl_load!(ldx_immediate, x, immediate);
842impl_load!(ldx_zero_page, x, zero_page);
843impl_load!(ldx_zero_page_y, x, zero_page_y);
844impl_load!(ldx_absolute, x, absolute);
845impl_load!(ldx_absolute_y, x, absolute_y);
846
847impl_load!(ldy_immediate, y, immediate);
848impl_load!(ldy_zero_page, y, zero_page);
849impl_load!(ldy_zero_page_x, y, zero_page_x);
850impl_load!(ldy_absolute, y, absolute);
851impl_load!(ldy_absolute_x, y, absolute_x);
852
853fn add(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 {
854    let existing_carry = flags.carry;
855
856    let (result, carry1) = accumulator.overflowing_add(value);
857    let (result, carry2) = result.overflowing_add(existing_carry.into());
858    let new_carry = carry1 || carry2;
859
860    let bit_6_carry = (accumulator & 0x7F) + (value & 0x7F) + u8::from(existing_carry) >= 0x80;
861    let overflow = new_carry ^ bit_6_carry;
862
863    flags
864        .set_negative(result.bit(7))
865        .set_overflow(overflow)
866        .set_zero(result == 0)
867        .set_carry(new_carry);
868
869    result
870}
871
872fn add_bcd(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 {
873    // Formulas from http://www.6502.org/tutorials/decimal_mode.html#A which correctly handle
874    // invalid values and undocumented behaviors
875
876    let existing_carry: u8 = flags.carry.into();
877
878    let mut al = (accumulator & 0x0F) + (value & 0x0F) + existing_carry;
879    if al >= 0x0A {
880        al = 0x10 | ((al + 0x06) & 0x0F);
881    }
882
883    let mut a = u16::from(accumulator & 0xF0) + u16::from(value & 0xF0) + u16::from(al);
884    if a >= 0xA0 {
885        a += 0x60;
886    }
887
888    let s = i16::from((accumulator & 0xF0) as i8) + i16::from((value & 0xF0) as i8) + i16::from(al);
889    let overflow = !(-128..128).contains(&s);
890
891    let result = a as u8;
892
893    // The 6502 calculates the Z flag based on the binary arithmetic result
894    flags.zero = accumulator.wrapping_add(value).wrapping_add(existing_carry) == 0;
895    flags.carry = a >= 0x0100;
896    flags.negative = s.bit(7);
897    flags.overflow = overflow;
898
899    result
900}
901
902// ADC
903macro_rules! impl_add_with_carry {
904    ($name:ident, $addressing_mode:tt) => {
905        impl_read_fn!($name, $addressing_mode, |operand, registers| {
906            registers.accumulator = if registers.in_decimal_mode() {
907                add_bcd(registers.accumulator, operand, &mut registers.status)
908            } else {
909                add(registers.accumulator, operand, &mut registers.status)
910            };
911        });
912    };
913}
914
915impl_add_with_carry!(adc_immediate, immediate);
916impl_add_with_carry!(adc_zero_page, zero_page);
917impl_add_with_carry!(adc_zero_page_x, zero_page_x);
918impl_add_with_carry!(adc_absolute, absolute);
919impl_add_with_carry!(adc_absolute_x, absolute_x);
920impl_add_with_carry!(adc_absolute_y, absolute_y);
921impl_add_with_carry!(adc_indirect_x, indirect_x);
922impl_add_with_carry!(adc_indirect_y, indirect_y);
923
924fn and(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 {
925    let result = accumulator & value;
926    flags.set_negative(result.bit(7)).set_zero(result == 0);
927    result
928}
929
930// AND
931macro_rules! impl_and {
932    ($name:ident, $addressing_mode:tt) => {
933        impl_read_fn!($name, $addressing_mode, |operand, registers| {
934            registers.accumulator = and(registers.accumulator, operand, &mut registers.status);
935        });
936    };
937}
938
939impl_and!(and_immediate, immediate);
940impl_and!(and_zero_page, zero_page);
941impl_and!(and_zero_page_x, zero_page_x);
942impl_and!(and_absolute, absolute);
943impl_and!(and_absolute_x, absolute_x);
944impl_and!(and_absolute_y, absolute_y);
945impl_and!(and_indirect_x, indirect_x);
946impl_and!(and_indirect_y, indirect_y);
947
948fn bit_test(accumulator: u8, value: u8, flags: &mut StatusFlags) {
949    flags.set_negative(value.bit(7)).set_overflow(value.bit(6)).set_zero(accumulator & value == 0);
950}
951
952// BIT
953macro_rules! impl_bit_test {
954    ($name:ident, $addressing_mode:tt) => {
955        impl_read_fn!($name, $addressing_mode, |operand, registers| {
956            bit_test(registers.accumulator, operand, &mut registers.status);
957        });
958    };
959}
960
961impl_bit_test!(bit_zero_page, zero_page);
962impl_bit_test!(bit_absolute, absolute);
963
964fn compare(register: u8, value: u8, flags: &mut StatusFlags) {
965    flags
966        .set_negative(register.wrapping_sub(value).bit(7))
967        .set_zero(register == value)
968        .set_carry(register >= value);
969}
970
971// CMP, CPX, CPY
972macro_rules! impl_compare {
973    ($name:ident, $register:ident, $addressing_mode:tt) => {
974        impl_read_fn!($name, $addressing_mode, |operand, registers| {
975            compare(registers.$register, operand, &mut registers.status);
976        });
977    };
978}
979
980impl_compare!(cmp_immediate, accumulator, immediate);
981impl_compare!(cmp_zero_page, accumulator, zero_page);
982impl_compare!(cmp_zero_page_x, accumulator, zero_page_x);
983impl_compare!(cmp_absolute, accumulator, absolute);
984impl_compare!(cmp_absolute_x, accumulator, absolute_x);
985impl_compare!(cmp_absolute_y, accumulator, absolute_y);
986impl_compare!(cmp_indirect_x, accumulator, indirect_x);
987impl_compare!(cmp_indirect_y, accumulator, indirect_y);
988
989impl_compare!(cpx_immediate, x, immediate);
990impl_compare!(cpx_zero_page, x, zero_page);
991impl_compare!(cpx_absolute, x, absolute);
992
993impl_compare!(cpy_immediate, y, immediate);
994impl_compare!(cpy_zero_page, y, zero_page);
995impl_compare!(cpy_absolute, y, absolute);
996
997fn xor(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 {
998    let result = accumulator ^ value;
999    flags.set_negative(result.bit(7)).set_zero(result == 0);
1000    result
1001}
1002
1003// EOR
1004macro_rules! impl_xor {
1005    ($name:ident, $addressing_mode:tt) => {
1006        impl_read_fn!($name, $addressing_mode, |operand, registers| {
1007            registers.accumulator = xor(registers.accumulator, operand, &mut registers.status);
1008        });
1009    };
1010}
1011
1012impl_xor!(eor_immediate, immediate);
1013impl_xor!(eor_zero_page, zero_page);
1014impl_xor!(eor_zero_page_x, zero_page_x);
1015impl_xor!(eor_absolute, absolute);
1016impl_xor!(eor_absolute_x, absolute_x);
1017impl_xor!(eor_absolute_y, absolute_y);
1018impl_xor!(eor_indirect_x, indirect_x);
1019impl_xor!(eor_indirect_y, indirect_y);
1020
1021fn or(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 {
1022    let result = accumulator | value;
1023    flags.set_negative(result.bit(7)).set_zero(result == 0);
1024    result
1025}
1026
1027// ORA
1028macro_rules! impl_or {
1029    ($name:ident, $addressing_mode:tt) => {
1030        impl_read_fn!($name, $addressing_mode, |operand, registers| {
1031            registers.accumulator = or(registers.accumulator, operand, &mut registers.status);
1032        });
1033    };
1034}
1035
1036impl_or!(ora_immediate, immediate);
1037impl_or!(ora_zero_page, zero_page);
1038impl_or!(ora_zero_page_x, zero_page_x);
1039impl_or!(ora_absolute, absolute);
1040impl_or!(ora_absolute_x, absolute_x);
1041impl_or!(ora_absolute_y, absolute_y);
1042impl_or!(ora_indirect_x, indirect_x);
1043impl_or!(ora_indirect_y, indirect_y);
1044
1045fn subtract(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 {
1046    // Carry flag is inverted in subtraction
1047    let existing_borrow = u8::from(!flags.carry);
1048
1049    let (result, borrowed1) = accumulator.overflowing_sub(value);
1050    let (result, borrowed2) = result.overflowing_sub(existing_borrow);
1051    let borrowed = borrowed1 || borrowed2;
1052
1053    let bit_6_borrowed = accumulator & 0x7F < (value & 0x7F) + existing_borrow;
1054    let overflow = borrowed ^ bit_6_borrowed;
1055
1056    flags
1057        .set_negative(result.bit(7))
1058        .set_overflow(overflow)
1059        .set_zero(result == 0)
1060        .set_carry(!borrowed);
1061
1062    result
1063}
1064
1065fn subtract_bcd(accumulator: u8, value: u8, flags: &mut StatusFlags) -> u8 {
1066    // Formulas from http://www.6502.org/tutorials/decimal_mode.html#A which correctly handle
1067    // invalid values and undocumented behaviors
1068
1069    let existing_borrow: u8 = (!flags.carry).into();
1070
1071    let mut al = u16::from(accumulator & 0x0F)
1072        .wrapping_sub(u16::from(value & 0x0F))
1073        .wrapping_sub(u16::from(existing_borrow));
1074    if al.sign_bit() {
1075        al = (al.wrapping_sub(0x06) & 0x0F).wrapping_sub(0x10);
1076    }
1077
1078    let mut a =
1079        u16::from(accumulator & 0xF0).wrapping_sub(u16::from(value & 0xF0)).wrapping_add(al);
1080    if a.sign_bit() {
1081        a = a.wrapping_sub(0x60);
1082    }
1083
1084    // Carry and overflow flags are set based on binary arithmetic
1085    let borrow = u16::from(accumulator) < u16::from(value) + u16::from(existing_borrow);
1086    let bit_6_borrow = accumulator & 0x7F < (value & 0x7F) + existing_borrow;
1087    let overflow = bit_6_borrow != borrow;
1088
1089    let result = a as u8;
1090
1091    // Z and N flags are set based on binary arithmetic
1092    let binary_result = accumulator.wrapping_sub(value).wrapping_sub(existing_borrow);
1093
1094    flags.zero = binary_result == 0;
1095    flags.negative = binary_result.sign_bit();
1096    flags.carry = !borrow;
1097    flags.overflow = overflow;
1098
1099    result
1100}
1101
1102// SBC
1103macro_rules! impl_subtract_with_carry {
1104    ($name:ident, $addressing_mode:tt) => {
1105        impl_read_fn!($name, $addressing_mode, |operand, registers| {
1106            registers.accumulator = if registers.in_decimal_mode() {
1107                subtract_bcd(registers.accumulator, operand, &mut registers.status)
1108            } else {
1109                subtract(registers.accumulator, operand, &mut registers.status)
1110            };
1111        });
1112    };
1113}
1114
1115impl_subtract_with_carry!(sbc_immediate, immediate);
1116impl_subtract_with_carry!(sbc_zero_page, zero_page);
1117impl_subtract_with_carry!(sbc_zero_page_x, zero_page_x);
1118impl_subtract_with_carry!(sbc_absolute, absolute);
1119impl_subtract_with_carry!(sbc_absolute_x, absolute_x);
1120impl_subtract_with_carry!(sbc_absolute_y, absolute_y);
1121impl_subtract_with_carry!(sbc_indirect_x, indirect_x);
1122impl_subtract_with_carry!(sbc_indirect_y, indirect_y);
1123
1124fn shift_left(value: u8, flags: &mut StatusFlags) -> u8 {
1125    let shifted = value << 1;
1126    flags.set_carry(value.bit(7)).set_negative(shifted.bit(7)).set_zero(shifted == 0);
1127    shifted
1128}
1129
1130// ASL
1131macro_rules! impl_shift_left {
1132    ($name:ident, $addressing_mode:tt) => {
1133        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1134            shift_left(operand, &mut registers.status)
1135        });
1136    };
1137}
1138
1139impl_shift_left!(asl_accumulator, accumulator);
1140impl_shift_left!(asl_zero_page, zero_page);
1141impl_shift_left!(asl_zero_page_x, zero_page_x);
1142impl_shift_left!(asl_absolute, absolute);
1143impl_shift_left!(asl_absolute_x, absolute_x);
1144
1145fn decrement(value: u8, flags: &mut StatusFlags) -> u8 {
1146    let decremented = value.wrapping_sub(1);
1147    flags.set_negative(decremented.bit(7)).set_zero(decremented == 0);
1148    decremented
1149}
1150
1151// DEC
1152macro_rules! impl_decrement {
1153    ($name:ident, $addressing_mode:tt) => {
1154        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1155            decrement(operand, &mut registers.status)
1156        });
1157    };
1158}
1159
1160impl_decrement!(dec_zero_page, zero_page);
1161impl_decrement!(dec_zero_page_x, zero_page_x);
1162impl_decrement!(dec_absolute, absolute);
1163impl_decrement!(dec_absolute_x, absolute_x);
1164
1165fn increment(value: u8, flags: &mut StatusFlags) -> u8 {
1166    let incremented = value.wrapping_add(1);
1167    flags.set_negative(incremented.bit(7)).set_zero(incremented == 0);
1168    incremented
1169}
1170
1171// INC
1172macro_rules! impl_increment {
1173    ($name:ident, $addressing_mode:tt) => {
1174        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1175            increment(operand, &mut registers.status)
1176        });
1177    };
1178}
1179
1180impl_increment!(inc_zero_page, zero_page);
1181impl_increment!(inc_zero_page_x, zero_page_x);
1182impl_increment!(inc_absolute, absolute);
1183impl_increment!(inc_absolute_x, absolute_x);
1184
1185fn logical_shift_right(value: u8, flags: &mut StatusFlags) -> u8 {
1186    let shifted = value >> 1;
1187    flags.set_carry(value.bit(0)).set_negative(false).set_zero(shifted == 0);
1188    shifted
1189}
1190
1191// LSR
1192macro_rules! impl_logical_shift_right {
1193    ($name:ident, $addressing_mode:tt) => {
1194        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1195            logical_shift_right(operand, &mut registers.status)
1196        });
1197    };
1198}
1199
1200impl_logical_shift_right!(lsr_accumulator, accumulator);
1201impl_logical_shift_right!(lsr_zero_page, zero_page);
1202impl_logical_shift_right!(lsr_zero_page_x, zero_page_x);
1203impl_logical_shift_right!(lsr_absolute, absolute);
1204impl_logical_shift_right!(lsr_absolute_x, absolute_x);
1205
1206fn rotate_left(value: u8, flags: &mut StatusFlags) -> u8 {
1207    let rotated = (value << 1) | u8::from(flags.carry);
1208    flags.set_carry(value.bit(7)).set_negative(rotated.bit(7)).set_zero(rotated == 0);
1209    rotated
1210}
1211
1212// ROL
1213macro_rules! impl_rotate_left {
1214    ($name:ident, $addressing_mode:tt) => {
1215        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1216            rotate_left(operand, &mut registers.status)
1217        });
1218    };
1219}
1220
1221impl_rotate_left!(rol_accumulator, accumulator);
1222impl_rotate_left!(rol_zero_page, zero_page);
1223impl_rotate_left!(rol_zero_page_x, zero_page_x);
1224impl_rotate_left!(rol_absolute, absolute);
1225impl_rotate_left!(rol_absolute_x, absolute_x);
1226
1227fn rotate_right(value: u8, flags: &mut StatusFlags) -> u8 {
1228    let rotated = (value >> 1) | (u8::from(flags.carry) << 7);
1229    flags.set_carry(value.bit(0)).set_negative(rotated.bit(7)).set_zero(rotated == 0);
1230    rotated
1231}
1232
1233// ROR
1234macro_rules! impl_rotate_right {
1235    ($name:ident, $addressing_mode:tt) => {
1236        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1237            rotate_right(operand, &mut registers.status)
1238        });
1239    };
1240}
1241
1242impl_rotate_right!(ror_accumulator, accumulator);
1243impl_rotate_right!(ror_zero_page, zero_page);
1244impl_rotate_right!(ror_zero_page_x, zero_page_x);
1245impl_rotate_right!(ror_absolute, absolute);
1246impl_rotate_right!(ror_absolute_x, absolute_x);
1247
1248// SLO (unofficial; combination of ASL and ORA)
1249macro_rules! impl_shift_left_or {
1250    ($name:ident, $addressing_mode:tt) => {
1251        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1252            let shifted = shift_left(operand, &mut registers.status);
1253            registers.accumulator = or(registers.accumulator, shifted, &mut registers.status);
1254            shifted
1255        });
1256    };
1257}
1258
1259impl_shift_left_or!(slo_zero_page, zero_page);
1260impl_shift_left_or!(slo_zero_page_x, zero_page_x);
1261impl_shift_left_or!(slo_absolute, absolute);
1262impl_shift_left_or!(slo_absolute_x, absolute_x);
1263impl_shift_left_or!(slo_absolute_y, absolute_y);
1264impl_shift_left_or!(slo_indirect_x, indirect_x);
1265impl_shift_left_or!(slo_indirect_y, indirect_y);
1266
1267// RLA (unofficial; combination of ROL and AND)
1268macro_rules! impl_rotate_left_and {
1269    ($name:ident, $addressing_mode:tt) => {
1270        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1271            let rotated = rotate_left(operand, &mut registers.status);
1272            registers.accumulator = and(registers.accumulator, rotated, &mut registers.status);
1273            rotated
1274        });
1275    };
1276}
1277
1278impl_rotate_left_and!(rla_zero_page, zero_page);
1279impl_rotate_left_and!(rla_zero_page_x, zero_page_x);
1280impl_rotate_left_and!(rla_absolute, absolute);
1281impl_rotate_left_and!(rla_absolute_x, absolute_x);
1282impl_rotate_left_and!(rla_absolute_y, absolute_y);
1283impl_rotate_left_and!(rla_indirect_x, indirect_x);
1284impl_rotate_left_and!(rla_indirect_y, indirect_y);
1285
1286// SRE (unofficial; combination of LSR and EOR)
1287macro_rules! impl_shift_right_xor {
1288    ($name:ident, $addressing_mode:tt) => {
1289        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1290            let shifted = logical_shift_right(operand, &mut registers.status);
1291            registers.accumulator = xor(registers.accumulator, shifted, &mut registers.status);
1292            shifted
1293        });
1294    };
1295}
1296
1297impl_shift_right_xor!(sre_zero_page, zero_page);
1298impl_shift_right_xor!(sre_zero_page_x, zero_page_x);
1299impl_shift_right_xor!(sre_absolute, absolute);
1300impl_shift_right_xor!(sre_absolute_x, absolute_x);
1301impl_shift_right_xor!(sre_absolute_y, absolute_y);
1302impl_shift_right_xor!(sre_indirect_x, indirect_x);
1303impl_shift_right_xor!(sre_indirect_y, indirect_y);
1304
1305// RRA (unofficial; combination of ROR and ADC)
1306macro_rules! impl_rotate_right_add {
1307    ($name:ident, $addressing_mode:tt) => {
1308        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1309            let rotated = rotate_right(operand, &mut registers.status);
1310            registers.accumulator = if registers.in_decimal_mode() {
1311                add_bcd(registers.accumulator, rotated, &mut registers.status)
1312            } else {
1313                add(registers.accumulator, rotated, &mut registers.status)
1314            };
1315            rotated
1316        });
1317    };
1318}
1319
1320impl_rotate_right_add!(rra_zero_page, zero_page);
1321impl_rotate_right_add!(rra_zero_page_x, zero_page_x);
1322impl_rotate_right_add!(rra_absolute, absolute);
1323impl_rotate_right_add!(rra_absolute_x, absolute_x);
1324impl_rotate_right_add!(rra_absolute_y, absolute_y);
1325impl_rotate_right_add!(rra_indirect_x, indirect_x);
1326impl_rotate_right_add!(rra_indirect_y, indirect_y);
1327
1328// DCP (unofficial; combination of DEC and CMP)
1329macro_rules! impl_decrement_compare {
1330    ($name:ident, $addressing_mode:tt) => {
1331        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1332            let decremented = decrement(operand, &mut registers.status);
1333            compare(registers.accumulator, decremented, &mut registers.status);
1334            decremented
1335        });
1336    };
1337}
1338
1339impl_decrement_compare!(dcp_zero_page, zero_page);
1340impl_decrement_compare!(dcp_zero_page_x, zero_page_x);
1341impl_decrement_compare!(dcp_absolute, absolute);
1342impl_decrement_compare!(dcp_absolute_x, absolute_x);
1343impl_decrement_compare!(dcp_absolute_y, absolute_y);
1344impl_decrement_compare!(dcp_indirect_x, indirect_x);
1345impl_decrement_compare!(dcp_indirect_y, indirect_y);
1346
1347// ISC (unofficial; combination of INC and SBC)
1348macro_rules! impl_increment_subtract {
1349    ($name:ident, $addressing_mode:tt) => {
1350        impl_modify_fn!($name, $addressing_mode, |operand, registers| {
1351            let incremented = increment(operand, &mut registers.status);
1352            registers.accumulator = if registers.in_decimal_mode() {
1353                subtract_bcd(registers.accumulator, incremented, &mut registers.status)
1354            } else {
1355                subtract(registers.accumulator, incremented, &mut registers.status)
1356            };
1357            incremented
1358        });
1359    };
1360}
1361
1362impl_increment_subtract!(isc_zero_page, zero_page);
1363impl_increment_subtract!(isc_zero_page_x, zero_page_x);
1364impl_increment_subtract!(isc_absolute, absolute);
1365impl_increment_subtract!(isc_absolute_x, absolute_x);
1366impl_increment_subtract!(isc_absolute_y, absolute_y);
1367impl_increment_subtract!(isc_indirect_x, indirect_x);
1368impl_increment_subtract!(isc_indirect_y, indirect_y);
1369
1370// CLC, CLD, CLI, CLV, SEC, SED, SEI
1371macro_rules! impl_set_status_flag {
1372    ($name:ident, $flag:ident = $value:expr) => {
1373        impl_registers_only_fn!($name, |registers| {
1374            registers.status.$flag = $value;
1375        });
1376    };
1377}
1378
1379impl_set_status_flag!(clc, carry = false);
1380impl_set_status_flag!(cld, decimal = false);
1381impl_set_status_flag!(cli, interrupt_disable = false);
1382impl_set_status_flag!(clv, overflow = false);
1383impl_set_status_flag!(sec, carry = true);
1384impl_set_status_flag!(sed, decimal = true);
1385impl_set_status_flag!(sei, interrupt_disable = true);
1386
1387// INX, INY
1388macro_rules! impl_increment_register {
1389    ($name:ident, $register:ident) => {
1390        impl_registers_only_fn!($name, |registers| {
1391            let value = registers.$register.wrapping_add(1);
1392            registers.$register = value;
1393            registers.status.set_negative(value.bit(7)).set_zero(value == 0);
1394        });
1395    };
1396}
1397
1398impl_increment_register!(inx, x);
1399impl_increment_register!(iny, y);
1400
1401// DEX, DEY
1402macro_rules! impl_decrement_register {
1403    ($name:ident, $register:ident) => {
1404        impl_registers_only_fn!($name, |registers| {
1405            let value = registers.$register.wrapping_sub(1);
1406            registers.$register = value;
1407            registers.status.set_negative(value.bit(7)).set_zero(value == 0);
1408        });
1409    };
1410}
1411
1412impl_decrement_register!(dex, x);
1413impl_decrement_register!(dey, y);
1414
1415macro_rules! set_transfer_flags {
1416    (sp, $registers:expr, $value:expr) => {};
1417    ($to:ident, $registers:expr, $value:expr) => {
1418        $registers.status.set_negative($value.bit(7)).set_zero($value == 0);
1419    };
1420}
1421
1422// TAX, TAY, TSX, TXA, TXS, TYA
1423macro_rules! impl_register_transfer {
1424    ($name:ident, $from:ident -> $to:ident) => {
1425        impl_registers_only_fn!($name, |registers| {
1426            let value = registers.$from;
1427            registers.$to = value;
1428            set_transfer_flags!($to, registers, value);
1429        });
1430    };
1431}
1432
1433impl_register_transfer!(tax, accumulator -> x);
1434impl_register_transfer!(tay, accumulator -> y);
1435impl_register_transfer!(tsx, sp -> x);
1436impl_register_transfer!(txa, x -> accumulator);
1437impl_register_transfer!(txs, x -> sp);
1438impl_register_transfer!(tya, y -> accumulator);
1439
1440// NOP
1441impl_registers_only_fn!(nop, |_registers| {});
1442
1443// BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS
1444macro_rules! impl_branch {
1445    ($name:ident, $flag:ident == $flag_value:expr) => {
1446        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1447            match cpu.state.cycle {
1448                0 => {
1449                    poll_interrupt_lines(cpu, bus);
1450
1451                    cpu.state.operand_first_byte = fetch_operand(cpu, bus);
1452
1453                    if cpu.registers.status.$flag != $flag_value {
1454                        cpu.state.instruction_complete = true;
1455                    }
1456                }
1457                1 => {
1458                    bus.read(cpu.registers.pc);
1459
1460                    let offset = cpu.state.operand_first_byte as i8;
1461                    let pc = cpu.registers.pc.wrapping_add_signed(offset.into());
1462
1463                    if cpu.registers.pc & 0xFF00 == pc & 0xFF00 {
1464                        cpu.registers.pc = pc;
1465                        cpu.state.instruction_complete = true;
1466                    }
1467                }
1468                2 => {
1469                    final_cycle(cpu, bus);
1470
1471                    let offset = cpu.state.operand_first_byte as i8;
1472                    let pc = cpu.registers.pc.wrapping_add_signed(offset.into());
1473
1474                    bus.read((cpu.registers.pc & 0xFF00) | (pc & 0x00FF));
1475
1476                    cpu.registers.pc = pc;
1477                }
1478                _ => invalid_cycle!(cpu),
1479            }
1480        }
1481    };
1482}
1483
1484impl_branch!(bcc, carry == false);
1485impl_branch!(bcs, carry == true);
1486impl_branch!(beq, zero == true);
1487impl_branch!(bmi, negative == true);
1488impl_branch!(bne, zero == false);
1489impl_branch!(bpl, negative == false);
1490impl_branch!(bvc, overflow == false);
1491impl_branch!(bvs, overflow == true);
1492
1493// JMP
1494fn jmp_absolute<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1495    match cpu.state.cycle {
1496        0 => {
1497            cpu.state.operand_first_byte = fetch_operand(cpu, bus);
1498        }
1499        1 => {
1500            final_cycle(cpu, bus);
1501
1502            let address_msb = bus.read(cpu.registers.pc);
1503            cpu.registers.pc = u16::from_le_bytes([cpu.state.operand_first_byte, address_msb]);
1504        }
1505        _ => invalid_cycle!(cpu),
1506    }
1507}
1508
1509// JMP
1510fn jmp_indirect<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1511    match cpu.state.cycle {
1512        0 => {
1513            cpu.state.operand_first_byte = fetch_operand(cpu, bus);
1514        }
1515        1 => {
1516            cpu.state.operand_second_byte = fetch_operand(cpu, bus);
1517        }
1518        2 => {
1519            let address =
1520                u16::from_le_bytes([cpu.state.operand_first_byte, cpu.state.operand_second_byte]);
1521            cpu.state.target_first_byte = bus.read(address);
1522        }
1523        3 => {
1524            final_cycle(cpu, bus);
1525
1526            let address_lsb = cpu.state.operand_first_byte.wrapping_add(1);
1527            let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]);
1528            let pc_msb = bus.read(address);
1529
1530            cpu.registers.pc = u16::from_le_bytes([cpu.state.target_first_byte, pc_msb]);
1531        }
1532        _ => invalid_cycle!(cpu),
1533    }
1534}
1535
1536macro_rules! read_register_for_push {
1537    (accumulator, $registers:expr) => {
1538        $registers.accumulator
1539    };
1540    (p, $registers:expr) => {
1541        $registers.status.to_byte(StatusReadContext::PushStack)
1542    };
1543}
1544
1545// PHA, PHP
1546macro_rules! impl_push_stack {
1547    ($name:ident, $register:tt) => {
1548        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1549            match cpu.state.cycle {
1550                0 => {
1551                    bus.read(cpu.registers.pc);
1552                }
1553                1 => {
1554                    final_cycle(cpu, bus);
1555
1556                    let address = u16::from_be_bytes([0x01, cpu.registers.sp]);
1557                    let value = read_register_for_push!($register, cpu.registers);
1558                    bus.write(address, value);
1559                    cpu.registers.sp = cpu.registers.sp.wrapping_sub(1);
1560                }
1561                _ => invalid_cycle!(cpu),
1562            }
1563        }
1564    };
1565}
1566
1567impl_push_stack!(pha, accumulator);
1568impl_push_stack!(php, p);
1569
1570macro_rules! write_register_for_pull {
1571    (accumulator, $registers:expr, $value:expr) => {{
1572        let value = $value;
1573        $registers.accumulator = value;
1574        $registers.status.set_negative(value.bit(7)).set_zero(value == 0);
1575    }};
1576    (p, $registers:expr, $value:expr) => {
1577        $registers.status = StatusFlags::from_byte($value);
1578    };
1579}
1580
1581// PLA, PLP
1582macro_rules! impl_pull_stack {
1583    ($name:ident, $register:tt) => {
1584        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1585            match cpu.state.cycle {
1586                0 => {
1587                    bus.read(cpu.registers.pc);
1588                }
1589                1 => {
1590                    bus.read(u16::from_be_bytes([0x01, cpu.registers.sp]));
1591                }
1592                2 => {
1593                    final_cycle(cpu, bus);
1594
1595                    cpu.registers.sp = cpu.registers.sp.wrapping_add(1);
1596                    let value = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp]));
1597                    write_register_for_pull!($register, cpu.registers, value);
1598                }
1599                _ => invalid_cycle!(cpu),
1600            }
1601        }
1602    };
1603}
1604
1605impl_pull_stack!(pla, accumulator);
1606impl_pull_stack!(plp, p);
1607
1608#[inline]
1609fn push_pc_msb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1610    let address = u16::from_be_bytes([0x01, cpu.registers.sp]);
1611    bus.write(address, (cpu.registers.pc >> 8) as u8);
1612    cpu.registers.sp = cpu.registers.sp.wrapping_sub(1);
1613}
1614
1615#[inline]
1616fn push_pc_lsb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1617    let address = u16::from_be_bytes([0x01, cpu.registers.sp]);
1618    bus.write(address, cpu.registers.pc as u8);
1619    cpu.registers.sp = cpu.registers.sp.wrapping_sub(1);
1620}
1621
1622#[inline]
1623fn pull_pc_lsb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1624    cpu.registers.sp = cpu.registers.sp.wrapping_add(1);
1625    cpu.registers.pc = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp])).into();
1626}
1627
1628#[inline]
1629fn pull_pc_msb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1630    cpu.registers.sp = cpu.registers.sp.wrapping_add(1);
1631    let pc_msb = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp]));
1632    cpu.registers.pc |= u16::from(pc_msb) << 8;
1633}
1634
1635// JSR (jump to subroutine)
1636fn jsr<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1637    match cpu.state.cycle {
1638        0 => {
1639            cpu.state.operand_first_byte = fetch_operand(cpu, bus);
1640        }
1641        1 => {
1642            // Spurious stack read
1643            bus.read(u16::from_be_bytes([0x01, cpu.registers.sp]));
1644        }
1645        2 => {
1646            push_pc_msb(cpu, bus);
1647        }
1648        3 => {
1649            push_pc_lsb(cpu, bus);
1650        }
1651        4 => {
1652            final_cycle(cpu, bus);
1653
1654            let address_msb = bus.read(cpu.registers.pc);
1655            cpu.registers.pc = u16::from_le_bytes([cpu.state.operand_first_byte, address_msb]);
1656        }
1657        _ => invalid_cycle!(cpu),
1658    }
1659}
1660
1661// RTS (return from subroutine)
1662fn rts<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1663    match cpu.state.cycle {
1664        0 => {
1665            // Spurious operand read
1666            bus.read(cpu.registers.pc);
1667        }
1668        1 => {
1669            // Spurious stack read
1670            bus.read(u16::from_be_bytes([0x01, cpu.registers.sp]));
1671        }
1672        2 => {
1673            pull_pc_lsb(cpu, bus);
1674        }
1675        3 => {
1676            pull_pc_msb(cpu, bus);
1677        }
1678        4 => {
1679            final_cycle(cpu, bus);
1680
1681            // Fetch operand and increment PC, ignore fetch result
1682            fetch_operand(cpu, bus);
1683        }
1684        _ => invalid_cycle!(cpu),
1685    }
1686}
1687
1688// RTI (return from interrupt)
1689fn rti<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1690    match cpu.state.cycle {
1691        0 => {
1692            // Spurious operand read
1693            bus.read(cpu.registers.pc);
1694        }
1695        1 => {
1696            // Spurious stack read
1697            bus.read(u16::from_be_bytes([0x01, cpu.registers.sp]));
1698        }
1699        2 => {
1700            cpu.registers.sp = cpu.registers.sp.wrapping_add(1);
1701            let value = bus.read(u16::from_be_bytes([0x01, cpu.registers.sp]));
1702            cpu.registers.status = StatusFlags::from_byte(value);
1703        }
1704        3 => {
1705            pull_pc_lsb(cpu, bus);
1706        }
1707        4 => {
1708            final_cycle(cpu, bus);
1709
1710            pull_pc_msb(cpu, bus);
1711        }
1712        _ => invalid_cycle!(cpu),
1713    }
1714}
1715
1716#[inline]
1717fn interrupt_push_status<B: BusInterface>(
1718    cpu: &mut Mos6502,
1719    bus: &mut B,
1720    read_ctx: StatusReadContext,
1721) {
1722    let stack_address = u16::from_be_bytes([0x01, cpu.registers.sp]);
1723    bus.write(stack_address, cpu.registers.status.to_byte(read_ctx));
1724    cpu.registers.sp = cpu.registers.sp.wrapping_sub(1);
1725
1726    cpu.state.interrupt_vector = if bus.nmi() {
1727        bus.acknowledge_nmi();
1728        NMI_VECTOR
1729    } else {
1730        IRQ_VECTOR
1731    };
1732}
1733
1734#[inline]
1735fn interrupt_pull_pc_lsb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1736    cpu.registers.pc = bus.read(cpu.state.interrupt_vector).into();
1737    cpu.registers.status.interrupt_disable = true;
1738}
1739
1740#[inline]
1741fn interrupt_pull_pc_msb<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1742    let pc_msb = bus.read(cpu.state.interrupt_vector + 1);
1743    cpu.registers.pc |= u16::from(pc_msb) << 8;
1744}
1745
1746// Hardware interrupt servicing routine + BRK (force interrupt)
1747fn interrupt_service_routine<const BRK: bool>(cpu: &mut Mos6502, bus: &mut impl BusInterface) {
1748    match cpu.state.cycle {
1749        0 => {
1750            if BRK {
1751                fetch_operand(cpu, bus);
1752            } else {
1753                // Spurious operand read
1754                bus.read(cpu.registers.pc);
1755            }
1756        }
1757        1 => {
1758            push_pc_msb(cpu, bus);
1759        }
1760        2 => {
1761            push_pc_lsb(cpu, bus);
1762        }
1763        3 => {
1764            let ctx = if BRK {
1765                StatusReadContext::Brk
1766            } else {
1767                StatusReadContext::HardwareInterruptHandler
1768            };
1769            interrupt_push_status(cpu, bus, ctx);
1770        }
1771        4 => {
1772            interrupt_pull_pc_lsb(cpu, bus);
1773        }
1774        5 => {
1775            // Interrupt service routine does not poll interrupt lines at end of "instruction"
1776            cpu.state.instruction_complete = true;
1777            cpu.state.executing_interrupt = false;
1778            interrupt_pull_pc_msb(cpu, bus);
1779        }
1780        _ => invalid_cycle!(cpu),
1781    }
1782}
1783
1784fn execute_unofficial_store<B: BusInterface>(
1785    cpu: &mut Mos6502,
1786    bus: &mut B,
1787    register_value: u8,
1788    index: u8,
1789) {
1790    // This is a buggy instruction that is only implemented because CPU test ROMs test
1791    // it.
1792    // This implementation ANDs the X/Y register with the high byte of the address plus 1
1793    // and then stores that value, but only if the indexing did not overflow.
1794
1795    let address = u16::from_le_bytes([cpu.state.operand_first_byte, cpu.state.operand_second_byte])
1796        .wrapping_add(index.into());
1797    let (_, overflowed) = cpu.state.operand_first_byte.overflowing_add(index);
1798
1799    let value = register_value & cpu.state.operand_second_byte.wrapping_add(1);
1800
1801    if !overflowed {
1802        bus.write(address, value);
1803    }
1804}
1805
1806// SHX, SHY (unofficial)
1807macro_rules! impl_unofficial_store {
1808    ($name:ident, register: $register:ident, index: $index:ident) => {
1809        fn $name<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1810            match cpu.state.cycle {
1811                0 => {
1812                    cpu.state.operand_first_byte = fetch_operand(cpu, bus);
1813                }
1814                1 => {
1815                    cpu.state.operand_second_byte = fetch_operand(cpu, bus);
1816                }
1817                2 => {
1818                    let address_lsb =
1819                        cpu.state.operand_first_byte.wrapping_add(cpu.registers.$index);
1820                    let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]);
1821                    bus.read(address);
1822                }
1823                3 => {
1824                    final_cycle(cpu, bus);
1825
1826                    let value = cpu.registers.$register;
1827                    let index = cpu.registers.$index;
1828                    execute_unofficial_store(cpu, bus, value, index);
1829                }
1830                _ => invalid_cycle!(cpu),
1831            }
1832        }
1833    };
1834}
1835
1836impl_unofficial_store!(shy, register: y, index: x);
1837impl_unofficial_store!(shx, register: x, index: y);
1838
1839// AHX, TAS (unofficial) (not actually implemented because these opcodes are very unstable)
1840fn unimplemented_unofficial_store_absolute_y<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1841    match cpu.state.cycle {
1842        0 => {
1843            cpu.state.operand_first_byte = fetch_operand(cpu, bus);
1844        }
1845        1 => {
1846            cpu.state.operand_second_byte = fetch_operand(cpu, bus);
1847        }
1848        2 => {
1849            let address_lsb = cpu.state.operand_first_byte.wrapping_add(cpu.registers.y);
1850            let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]);
1851            cpu.state.target_first_byte = bus.read(address);
1852        }
1853        3 => {
1854            final_cycle(cpu, bus);
1855
1856            let address_lsb = cpu.state.operand_first_byte.wrapping_add(cpu.registers.y);
1857            let address = u16::from_le_bytes([address_lsb, cpu.state.operand_second_byte]);
1858            bus.write(address, cpu.state.target_first_byte);
1859        }
1860        _ => invalid_cycle!(cpu),
1861    }
1862}
1863
1864// AHX, TAS (unofficial) (not actually implemented because these opcodes are very unstable)
1865fn unimplemented_unofficial_store_indirect_y<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
1866    match cpu.state.cycle {
1867        0 => {
1868            cpu.state.operand_first_byte = fetch_operand(cpu, bus);
1869        }
1870        1 => {
1871            cpu.state.target_first_byte = bus.read(cpu.state.operand_first_byte.into());
1872        }
1873        2 => {
1874            let address = cpu.state.operand_first_byte.wrapping_add(1).into();
1875            cpu.state.target_second_byte = bus.read(address);
1876        }
1877        3 => {
1878            let address_lsb = cpu.state.target_first_byte.wrapping_add(cpu.registers.y);
1879            let address = u16::from_le_bytes([address_lsb, cpu.state.target_second_byte]);
1880            cpu.state.indirect_byte = bus.read(address);
1881        }
1882        4 => {
1883            final_cycle(cpu, bus);
1884
1885            let address_lsb = cpu.state.target_first_byte.wrapping_add(cpu.registers.y);
1886            let address = u16::from_le_bytes([address_lsb, cpu.state.target_second_byte]);
1887            bus.write(address, cpu.state.indirect_byte);
1888        }
1889        _ => invalid_cycle!(cpu),
1890    }
1891}
1892
1893// ANC (unofficial; combination of AND and ASL)
1894impl_read_fn!(anc, immediate, |operand, registers| {
1895    // ANC performs an AND and then sets the C flag the way that ASL does
1896    registers.accumulator = and(registers.accumulator, operand, &mut registers.status);
1897    registers.status.carry = registers.accumulator.bit(7);
1898});
1899
1900// ALR (unofficial; combination of AND and LSR)
1901impl_read_fn!(alr, immediate, |operand, registers| {
1902    // ALR simply performs an AND followed by an LSR
1903    let and_value = and(registers.accumulator, operand, &mut registers.status);
1904    registers.accumulator = logical_shift_right(and_value, &mut registers.status);
1905});
1906
1907fn and_with_rotate_right(registers: &mut CpuRegisters, operand: u8) {
1908    // ARR is like a mix of AND, ROR, and ADC; the accumulator is set to (A & #imm) rotated,
1909    // but the flags are set differently from ROR
1910
1911    let and_value = and(registers.accumulator, operand, &mut StatusFlags::new());
1912    registers.accumulator = (and_value >> 1) | (u8::from(registers.status.carry) << 7);
1913
1914    // The overflow flag is set as if an ADC was performed between the AND and ROR, and
1915    // the carry flag is set based on what was bit 7 prior to the rotation
1916    let overflow = registers.accumulator.bit(6) ^ registers.accumulator.bit(5);
1917    registers
1918        .status
1919        .set_negative(registers.accumulator.bit(7))
1920        .set_overflow(overflow)
1921        .set_carry(registers.accumulator.bit(6))
1922        .set_zero(registers.accumulator == 0);
1923}
1924
1925// ARR (unofficial; combination of AND, ROR, and ADC)
1926impl_read_fn!(arr, immediate, |operand, registers| {
1927    and_with_rotate_right(registers, operand);
1928});
1929
1930// LAX (unofficial)
1931macro_rules! impl_load_transfer_ax {
1932    ($name:ident, $addressing_mode:tt) => {
1933        impl_read_fn!($name, $addressing_mode, |operand, registers| {
1934            // LAX simply performs LDA and LDX simultaneously
1935
1936            registers.accumulator = operand;
1937            registers.x = operand;
1938
1939            registers.status.set_negative(operand.bit(7)).set_zero(operand == 0);
1940        });
1941    };
1942}
1943
1944impl_load_transfer_ax!(lax_immediate, immediate);
1945impl_load_transfer_ax!(lax_zero_page, zero_page);
1946impl_load_transfer_ax!(lax_zero_page_y, zero_page_y);
1947impl_load_transfer_ax!(lax_absolute, absolute);
1948impl_load_transfer_ax!(lax_absolute_y, absolute_y);
1949impl_load_transfer_ax!(lax_indirect_x, indirect_x);
1950impl_load_transfer_ax!(lax_indirect_y, indirect_y);
1951
1952// XAA (unofficial; loads X & <imm> into A)
1953impl_read_fn!(xaa, immediate, |operand, registers| {
1954    registers.accumulator = registers.x & operand;
1955    registers
1956        .status
1957        .set_negative(registers.accumulator.bit(7))
1958        .set_zero(registers.accumulator == 0);
1959});
1960
1961// AXS (unofficial)
1962impl_read_fn!(axs, immediate, |operand, registers| {
1963    // AXS sets X to (A&X) - #imm, while ignoring the current carry flag. The flags
1964    // are set not from the subtraction operation but from a CMP between (A&X) and #imm
1965
1966    let ax = registers.accumulator & registers.x;
1967    let mut flags = StatusFlags {
1968        // Set carry to true because SBC inverts the carry flag for borrowing
1969        carry: true,
1970        ..StatusFlags::new()
1971    };
1972    registers.x = subtract(ax, operand, &mut flags);
1973
1974    compare(ax, operand, &mut registers.status);
1975});
1976
1977// LAS (unofficial; sets A, X, and S to S & value)
1978impl_read_fn!(las, absolute_y, |operand, registers| {
1979    let new_value = operand & registers.sp;
1980
1981    registers.accumulator = new_value;
1982    registers.x = new_value;
1983    registers.sp = new_value;
1984
1985    registers.status.set_negative(new_value.bit(7)).set_zero(new_value == 0);
1986});
1987
1988// unofficial NOPs
1989macro_rules! impl_multi_byte_noop {
1990    ($name:ident, $addressing_mode:tt) => {
1991        impl_read_fn!($name, $addressing_mode, |_operand, _registers| {});
1992    };
1993}
1994
1995impl_multi_byte_noop!(nop_immediate, immediate);
1996impl_multi_byte_noop!(nop_zero_page, zero_page);
1997impl_multi_byte_noop!(nop_zero_page_x, zero_page_x);
1998impl_multi_byte_noop!(nop_absolute, absolute);
1999impl_multi_byte_noop!(nop_absolute_x, absolute_x);
2000
2001pub fn execute_cycle<B: BusInterface>(cpu: &mut Mos6502, bus: &mut B) {
2002    if cpu.state.executing_interrupt {
2003        interrupt_service_routine::<false>(cpu, bus);
2004        cpu.state.cycle += 1;
2005        return;
2006    }
2007
2008    match cpu.state.opcode {
2009        0x00 => interrupt_service_routine::<true>(cpu, bus), // BRK
2010        0x01 => ora_indirect_x(cpu, bus),
2011        0x03 => slo_indirect_x(cpu, bus),
2012        0x04 | 0x44 | 0x64 => nop_zero_page(cpu, bus),
2013        0x05 => ora_zero_page(cpu, bus),
2014        0x06 => asl_zero_page(cpu, bus),
2015        0x07 => slo_zero_page(cpu, bus),
2016        0x08 => php(cpu, bus),
2017        0x09 => ora_immediate(cpu, bus),
2018        0x0A => asl_accumulator(cpu, bus),
2019        0x0B | 0x2B => anc(cpu, bus),
2020        0x0C => nop_absolute(cpu, bus),
2021        0x0D => ora_absolute(cpu, bus),
2022        0x0E => asl_absolute(cpu, bus),
2023        0x0F => slo_absolute(cpu, bus),
2024        0x10 => bpl(cpu, bus),
2025        0x11 => ora_indirect_y(cpu, bus),
2026        0x13 => slo_indirect_y(cpu, bus),
2027        0x14 | 0x34 | 0x54 | 0x74 | 0xD4 | 0xF4 => nop_zero_page_x(cpu, bus),
2028        0x15 => ora_zero_page_x(cpu, bus),
2029        0x16 => asl_zero_page_x(cpu, bus),
2030        0x17 => slo_zero_page_x(cpu, bus),
2031        0x18 => clc(cpu, bus),
2032        0x19 => ora_absolute_y(cpu, bus),
2033        0x1A | 0x3A | 0x5A | 0x7A | 0xDA | 0xEA | 0xFA => nop(cpu, bus),
2034        0x1B => slo_absolute_y(cpu, bus),
2035        0x1C | 0x3C | 0x5C | 0x7C | 0xDC | 0xFC => nop_absolute_x(cpu, bus),
2036        0x1D => ora_absolute_x(cpu, bus),
2037        0x1E => asl_absolute_x(cpu, bus),
2038        0x1F => slo_absolute_x(cpu, bus),
2039        0x20 => jsr(cpu, bus),
2040        0x21 => and_indirect_x(cpu, bus),
2041        0x23 => rla_indirect_x(cpu, bus),
2042        0x24 => bit_zero_page(cpu, bus),
2043        0x25 => and_zero_page(cpu, bus),
2044        0x26 => rol_zero_page(cpu, bus),
2045        0x27 => rla_zero_page(cpu, bus),
2046        0x28 => plp(cpu, bus),
2047        0x29 => and_immediate(cpu, bus),
2048        0x2A => rol_accumulator(cpu, bus),
2049        0x2C => bit_absolute(cpu, bus),
2050        0x2D => and_absolute(cpu, bus),
2051        0x2E => rol_absolute(cpu, bus),
2052        0x2F => rla_absolute(cpu, bus),
2053        0x30 => bmi(cpu, bus),
2054        0x31 => and_indirect_y(cpu, bus),
2055        0x33 => rla_indirect_y(cpu, bus),
2056        0x35 => and_zero_page_x(cpu, bus),
2057        0x36 => rol_zero_page_x(cpu, bus),
2058        0x37 => rla_zero_page_x(cpu, bus),
2059        0x38 => sec(cpu, bus),
2060        0x39 => and_absolute_y(cpu, bus),
2061        0x3B => rla_absolute_y(cpu, bus),
2062        0x3D => and_absolute_x(cpu, bus),
2063        0x3E => rol_absolute_x(cpu, bus),
2064        0x3F => rla_absolute_x(cpu, bus),
2065        0x40 => rti(cpu, bus),
2066        0x41 => eor_indirect_x(cpu, bus),
2067        0x43 => sre_indirect_x(cpu, bus),
2068        0x45 => eor_zero_page(cpu, bus),
2069        0x46 => lsr_zero_page(cpu, bus),
2070        0x47 => sre_zero_page(cpu, bus),
2071        0x48 => pha(cpu, bus),
2072        0x49 => eor_immediate(cpu, bus),
2073        0x4A => lsr_accumulator(cpu, bus),
2074        0x4B => alr(cpu, bus),
2075        0x4C => jmp_absolute(cpu, bus),
2076        0x4D => eor_absolute(cpu, bus),
2077        0x4E => lsr_absolute(cpu, bus),
2078        0x4F => sre_absolute(cpu, bus),
2079        0x50 => bvc(cpu, bus),
2080        0x51 => eor_indirect_y(cpu, bus),
2081        0x53 => sre_indirect_y(cpu, bus),
2082        0x55 => eor_zero_page_x(cpu, bus),
2083        0x56 => lsr_zero_page_x(cpu, bus),
2084        0x57 => sre_zero_page_x(cpu, bus),
2085        0x58 => cli(cpu, bus),
2086        0x59 => eor_absolute_y(cpu, bus),
2087        0x5B => sre_absolute_y(cpu, bus),
2088        0x5D => eor_absolute_x(cpu, bus),
2089        0x5E => lsr_absolute_x(cpu, bus),
2090        0x5F => sre_absolute_x(cpu, bus),
2091        0x60 => rts(cpu, bus),
2092        0x61 => adc_indirect_x(cpu, bus),
2093        0x63 => rra_indirect_x(cpu, bus),
2094        0x65 => adc_zero_page(cpu, bus),
2095        0x66 => ror_zero_page(cpu, bus),
2096        0x67 => rra_zero_page(cpu, bus),
2097        0x68 => pla(cpu, bus),
2098        0x69 => adc_immediate(cpu, bus),
2099        0x6A => ror_accumulator(cpu, bus),
2100        0x6B => arr(cpu, bus),
2101        0x6C => jmp_indirect(cpu, bus),
2102        0x6D => adc_absolute(cpu, bus),
2103        0x6E => ror_absolute(cpu, bus),
2104        0x6F => rra_absolute(cpu, bus),
2105        0x70 => bvs(cpu, bus),
2106        0x71 => adc_indirect_y(cpu, bus),
2107        0x73 => rra_indirect_y(cpu, bus),
2108        0x75 => adc_zero_page_x(cpu, bus),
2109        0x76 => ror_zero_page_x(cpu, bus),
2110        0x77 => rra_zero_page_x(cpu, bus),
2111        0x78 => sei(cpu, bus),
2112        0x79 => adc_absolute_y(cpu, bus),
2113        0x7B => rra_absolute_y(cpu, bus),
2114        0x7D => adc_absolute_x(cpu, bus),
2115        0x7E => ror_absolute_x(cpu, bus),
2116        0x7F => rra_absolute_x(cpu, bus),
2117        0x80 | 0x82 | 0x89 | 0xC2 | 0xE2 => nop_immediate(cpu, bus),
2118        0x81 => sta_indirect_x(cpu, bus),
2119        0x83 => sax_indirect_x(cpu, bus),
2120        0x84 => sty_zero_page(cpu, bus),
2121        0x85 => sta_zero_page(cpu, bus),
2122        0x86 => stx_zero_page(cpu, bus),
2123        0x87 => sax_zero_page(cpu, bus),
2124        0x88 => dey(cpu, bus),
2125        0x8A => txa(cpu, bus),
2126        0x8B => xaa(cpu, bus),
2127        0x8C => sty_absolute(cpu, bus),
2128        0x8D => sta_absolute(cpu, bus),
2129        0x8E => stx_absolute(cpu, bus),
2130        0x8F => sax_absolute(cpu, bus),
2131        0x90 => bcc(cpu, bus),
2132        0x91 => sta_indirect_y(cpu, bus),
2133        0x93 => unimplemented_unofficial_store_indirect_y(cpu, bus),
2134        0x94 => sty_zero_page_x(cpu, bus),
2135        0x95 => sta_zero_page_x(cpu, bus),
2136        0x96 => stx_zero_page_y(cpu, bus),
2137        0x97 => sax_zero_page_y(cpu, bus),
2138        0x98 => tya(cpu, bus),
2139        0x99 => sta_absolute_y(cpu, bus),
2140        0x9A => txs(cpu, bus),
2141        0x9B | 0x9F => unimplemented_unofficial_store_absolute_y(cpu, bus),
2142        0x9C => shy(cpu, bus),
2143        0x9D => sta_absolute_x(cpu, bus),
2144        0x9E => shx(cpu, bus),
2145        0xA0 => ldy_immediate(cpu, bus),
2146        0xA1 => lda_indirect_x(cpu, bus),
2147        0xA2 => ldx_immediate(cpu, bus),
2148        0xA3 => lax_indirect_x(cpu, bus),
2149        0xA4 => ldy_zero_page(cpu, bus),
2150        0xA5 => lda_zero_page(cpu, bus),
2151        0xA6 => ldx_zero_page(cpu, bus),
2152        0xA7 => lax_zero_page(cpu, bus),
2153        0xA8 => tay(cpu, bus),
2154        0xA9 => lda_immediate(cpu, bus),
2155        0xAA => tax(cpu, bus),
2156        0xAB => lax_immediate(cpu, bus),
2157        0xAC => ldy_absolute(cpu, bus),
2158        0xAD => lda_absolute(cpu, bus),
2159        0xAE => ldx_absolute(cpu, bus),
2160        0xAF => lax_absolute(cpu, bus),
2161        0xB0 => bcs(cpu, bus),
2162        0xB1 => lda_indirect_y(cpu, bus),
2163        0xB3 => lax_indirect_y(cpu, bus),
2164        0xB4 => ldy_zero_page_x(cpu, bus),
2165        0xB5 => lda_zero_page_x(cpu, bus),
2166        0xB6 => ldx_zero_page_y(cpu, bus),
2167        0xB7 => lax_zero_page_y(cpu, bus),
2168        0xB8 => clv(cpu, bus),
2169        0xB9 => lda_absolute_y(cpu, bus),
2170        0xBA => tsx(cpu, bus),
2171        0xBB => las(cpu, bus),
2172        0xBC => ldy_absolute_x(cpu, bus),
2173        0xBD => lda_absolute_x(cpu, bus),
2174        0xBE => ldx_absolute_y(cpu, bus),
2175        0xBF => lax_absolute_y(cpu, bus),
2176        0xC0 => cpy_immediate(cpu, bus),
2177        0xC1 => cmp_indirect_x(cpu, bus),
2178        0xC3 => dcp_indirect_x(cpu, bus),
2179        0xC4 => cpy_zero_page(cpu, bus),
2180        0xC5 => cmp_zero_page(cpu, bus),
2181        0xC6 => dec_zero_page(cpu, bus),
2182        0xC7 => dcp_zero_page(cpu, bus),
2183        0xC8 => iny(cpu, bus),
2184        0xC9 => cmp_immediate(cpu, bus),
2185        0xCA => dex(cpu, bus),
2186        0xCB => axs(cpu, bus),
2187        0xCC => cpy_absolute(cpu, bus),
2188        0xCD => cmp_absolute(cpu, bus),
2189        0xCE => dec_absolute(cpu, bus),
2190        0xCF => dcp_absolute(cpu, bus),
2191        0xD0 => bne(cpu, bus),
2192        0xD1 => cmp_indirect_y(cpu, bus),
2193        0xD3 => dcp_indirect_y(cpu, bus),
2194        0xD5 => cmp_zero_page_x(cpu, bus),
2195        0xD6 => dec_zero_page_x(cpu, bus),
2196        0xD7 => dcp_zero_page_x(cpu, bus),
2197        0xD8 => cld(cpu, bus),
2198        0xD9 => cmp_absolute_y(cpu, bus),
2199        0xDB => dcp_absolute_y(cpu, bus),
2200        0xDD => cmp_absolute_x(cpu, bus),
2201        0xDE => dec_absolute_x(cpu, bus),
2202        0xDF => dcp_absolute_x(cpu, bus),
2203        0xE0 => cpx_immediate(cpu, bus),
2204        0xE1 => sbc_indirect_x(cpu, bus),
2205        0xE3 => isc_indirect_x(cpu, bus),
2206        0xE4 => cpx_zero_page(cpu, bus),
2207        0xE5 => sbc_zero_page(cpu, bus),
2208        0xE6 => inc_zero_page(cpu, bus),
2209        0xE7 => isc_zero_page(cpu, bus),
2210        0xE8 => inx(cpu, bus),
2211        0xE9 | 0xEB => sbc_immediate(cpu, bus),
2212        0xEC => cpx_absolute(cpu, bus),
2213        0xED => sbc_absolute(cpu, bus),
2214        0xEE => inc_absolute(cpu, bus),
2215        0xEF => isc_absolute(cpu, bus),
2216        0xF0 => beq(cpu, bus),
2217        0xF1 => sbc_indirect_y(cpu, bus),
2218        0xF3 => isc_indirect_y(cpu, bus),
2219        0xF5 => sbc_zero_page_x(cpu, bus),
2220        0xF6 => inc_zero_page_x(cpu, bus),
2221        0xF7 => isc_zero_page_x(cpu, bus),
2222        0xF8 => sed(cpu, bus),
2223        0xF9 => sbc_absolute_y(cpu, bus),
2224        0xFB => isc_absolute_y(cpu, bus),
2225        0xFD => sbc_absolute_x(cpu, bus),
2226        0xFE => inc_absolute_x(cpu, bus),
2227        0xFF => isc_absolute_x(cpu, bus),
2228        0x02 | 0x12 | 0x22 | 0x32 | 0x42 | 0x52 | 0x62 | 0x72 | 0x92 | 0xB2 | 0xD2 | 0xF2 => {
2229            // KIL unofficial opcodes; executing any of these halts the CPU until a reset or power cycle
2230            cpu.frozen = true;
2231        }
2232    }
2233
2234    cpu.state.cycle += 1;
2235}