flow.rsannotatedflow.rssource676 lines · 20.3 KB · raw
1#[allow(clippy::wildcard_imports)]
2use super::*;

JMP: Jump

5pub(crate) fn jmp_absolute<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
6    match cpu.state.cycle {
7        1 => {
8            cpu.state.t0 = fetch_operand(cpu, bus);
9        }
10        2 => {
11            final_cycle(cpu, bus);
12
13            let pc_msb = fetch_operand(cpu, bus);
14            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, pc_msb]);
15        }
16        _ => invalid_cycle!(cpu),
17    }
18}
20pub(crate) fn jmp_absolute_long<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
21    match cpu.state.cycle {
22        1 => {
23            cpu.state.t0 = fetch_operand(cpu, bus);
24        }
25        2 => {
26            cpu.state.t1 = fetch_operand(cpu, bus);
27        }
28        3 => {
29            final_cycle(cpu, bus);
30
31            cpu.registers.pbr = fetch_operand(cpu, bus);
32
33            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
34        }
35        _ => invalid_cycle!(cpu),
36    }
37}
38
39pub(crate) fn jmp_indirect<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
40    match cpu.state.cycle {
41        1 => {
42            cpu.state.t0 = fetch_operand(cpu, bus);
43        }
44        2 => {
45            cpu.state.t1 = fetch_operand(cpu, bus);
46        }
47        3 => {
48            let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
49            cpu.state.t2 = bus.read(address.into());
50        }
51        4 => {
52            final_cycle(cpu, bus);
53
54            let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
55            let pc_msb = bus.read(address.wrapping_add(1).into());
56            cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, pc_msb]);
57        }
58        _ => invalid_cycle!(cpu),
59    }
60}
61
62pub(crate) fn jmp_indexed_indirect<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
63    match cpu.state.cycle {
64        1 => {
65            cpu.state.t0 = fetch_operand(cpu, bus);
66        }
67        2 => {
68            cpu.state.t1 = fetch_operand(cpu, bus);
69        }
70        3 => {
71            // Idle cycle for indexing
72            bus.idle();
73        }
74        4 => {
75            let bank_addr =
76                u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(cpu.registers.x);
77            let address = u24_address(cpu.registers.pbr, bank_addr);
78            cpu.state.t2 = bus.read(address);
79        }
80        5 => {
81            final_cycle(cpu, bus);
82
83            let bank_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1])
84                .wrapping_add(cpu.registers.x)
85                .wrapping_add(1);
86            let address = u24_address(cpu.registers.pbr, bank_addr);
87            let pc_msb = bus.read(address);
88            cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, pc_msb]);
89        }
90        _ => invalid_cycle!(cpu),
91    }
92}
93
94pub(crate) fn jmp_indirect_long<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
95    match cpu.state.cycle {
96        1 => {
97            cpu.state.t0 = fetch_operand(cpu, bus);
98        }
99        2 => {
100            cpu.state.t1 = fetch_operand(cpu, bus);
101        }
102        3 => {
103            let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
104            cpu.state.t2 = bus.read(address.into());
105        }
106        4 => {
107            let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
108            cpu.state.t3 = bus.read(address.wrapping_add(1).into());
109        }
110        5 => {
111            final_cycle(cpu, bus);
112
113            let address = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
114            cpu.registers.pbr = bus.read(address.wrapping_add(2).into());
115
116            cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, cpu.state.t3]);
117        }
118        _ => invalid_cycle!(cpu),
119    }
120}

JSR: Jump to subroutine

123pub(crate) fn jsr_absolute<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
124    match cpu.state.cycle {
125        1 => {
126            cpu.state.t0 = fetch_operand(cpu, bus);
127        }
128        2 => {
129            cpu.state.t1 = fetch_operand(cpu, bus);
130        }
131        3 => {
132            bus.idle();
133        }
134        4 => {
135            let push_pc = cpu.registers.pc.wrapping_sub(1);
136            bus.write(cpu.registers.s.into(), push_pc.msb());
137            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
138
139            if cpu.registers.emulation_mode {
140                ensure_page_1_stack(&mut cpu.registers);
141            }
142        }
143        5 => {
144            final_cycle(cpu, bus);
145
146            let push_pc = cpu.registers.pc.wrapping_sub(1);
147            bus.write(cpu.registers.s.into(), push_pc.lsb());
148            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
149
150            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
151
152            if cpu.registers.emulation_mode {
153                ensure_page_1_stack(&mut cpu.registers);
154            }
155        }
156        _ => invalid_cycle!(cpu),
157    }
158}
160pub(crate) fn jsr_indirect_indexed<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
161    match cpu.state.cycle {
162        1 => {
163            cpu.state.t0 = fetch_operand(cpu, bus);
164        }
165        2 => {
166            bus.write(cpu.registers.s.into(), cpu.registers.pc.msb());
167            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
168
169            if cpu.registers.emulation_mode {
170                ensure_page_1_stack(&mut cpu.registers);
171            }
172        }
173        3 => {
174            bus.write(cpu.registers.s.into(), cpu.registers.pc.lsb());
175            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
176
177            if cpu.registers.emulation_mode {
178                ensure_page_1_stack(&mut cpu.registers);
179            }
180        }
181        4 => {
182            cpu.state.t1 = fetch_operand(cpu, bus);
183        }
184        5 => {
185            bus.idle();
186        }
187        6 => {
188            let bank_addr =
189                u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(cpu.registers.x);
190            let address = u24_address(cpu.registers.pbr, bank_addr);
191            cpu.state.t2 = bus.read(address);
192        }
193        7 => {
194            final_cycle(cpu, bus);
195
196            let bank_addr = u16::from_le_bytes([cpu.state.t0, cpu.state.t1])
197                .wrapping_add(cpu.registers.x)
198                .wrapping_add(1);
199            let address = u24_address(cpu.registers.pbr, bank_addr);
200            let pc_msb = bus.read(address);
201            cpu.registers.pc = u16::from_le_bytes([cpu.state.t2, pc_msb]);
202        }
203        _ => invalid_cycle!(cpu),
204    }
205}

JSL: Jump to subroutine long

208pub(crate) fn jsl<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
209    match cpu.state.cycle {
210        1 => {
211            cpu.state.t0 = fetch_operand(cpu, bus);
212        }
213        2 => {
214            cpu.state.t1 = fetch_operand(cpu, bus);
215        }
216        3 => {
217            bus.write(cpu.registers.s.into(), cpu.registers.pbr);
218            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
219        }
220        4 => {
221            bus.idle();
222        }
223        5 => {
224            cpu.registers.pbr = fetch_operand(cpu, bus);
225        }
226        6 => {
227            let push_pc = cpu.registers.pc.wrapping_sub(1);
228            bus.write(cpu.registers.s.into(), push_pc.msb());
229            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
230        }
231        7 => {
232            final_cycle(cpu, bus);
233
234            let push_pc = cpu.registers.pc.wrapping_sub(1);
235            bus.write(cpu.registers.s.into(), push_pc.lsb());
236            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
237
238            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
239
240            if cpu.registers.emulation_mode {
241                ensure_page_1_stack(&mut cpu.registers);
242            }
243        }
244        _ => invalid_cycle!(cpu),
245    }
246}
248macro_rules! impl_branch {
249    ($name:ident $(, $flag:ident == $value:expr)?) => {
250        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
251            match cpu.state.cycle {
252                1 => {
253                    poll_interrupt_lines(cpu, bus);
254
255                    cpu.state.t0 = fetch_operand(cpu, bus);
256
257                    // Check whether to take branch
258                    $(
259                        if cpu.registers.p.$flag != $value {
260                            cpu.state.cycle = 0;
261                        }
262                    )?
263                }
264                2 => {
265                    poll_interrupt_lines(cpu, bus);
266
267                    bus.idle();
268
269                    let offset = cpu.state.t0 as i8 as u16;
270                    let original_page = cpu.registers.pc & 0xFF00;
271                    cpu.registers.pc = cpu.registers.pc.wrapping_add(offset);
272
273                    // In emulation mode, crossing a page adds an extra cycle
274                    if !cpu.registers.emulation_mode || original_page == cpu.registers.pc & 0xFF00 {
275                        cpu.state.cycle = 0;
276                    }
277                }
278                3 => {
279                    final_cycle(cpu, bus);
280
281                    // Penalty cycle for page crossing in emulation mode
282                    bus.idle();
283                }
284                _ => invalid_cycle!(cpu)
285            }
286        }
287    };
288}

BCC: Branch if carry clear

291impl_branch!(bcc, carry == false);

BCS: Branch if carry set

294impl_branch!(bcs, carry == true);

BEQ: Branch if equal

297impl_branch!(beq, zero == true);

BMI: Branch if minus

300impl_branch!(bmi, negative == true);

BNE: Branch if not equal

303impl_branch!(bne, zero == false);

BPL: Branch if plus

306impl_branch!(bpl, negative == false);

BRA: Branch always

309impl_branch!(bra);

BVC: Branch if overflow clear

312impl_branch!(bvc, overflow == false);

BVS: Branch if overflow set

315impl_branch!(bvs, overflow == true);

BRL: Branch long

318pub(crate) fn brl<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
319    match cpu.state.cycle {
320        1 => {
321            cpu.state.t0 = fetch_operand(cpu, bus);
322        }
323        2 => {
324            cpu.state.t1 = fetch_operand(cpu, bus);
325        }
326        3 => {
327            final_cycle(cpu, bus);
328
329            bus.idle();
330
331            let offset = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
332            cpu.registers.pc = cpu.registers.pc.wrapping_add(offset);
333        }
334        _ => invalid_cycle!(cpu),
335    }
336}

BRK + COP + hardware interrupt handler (NMI + IRQ) BRK: Breakpoint software interrupt COP: Coprocessor software interrupt

341pub(crate) fn handle_interrupt<B: BusInterface>(
342    cpu: &mut Wdc65816,
343    bus: &mut B,
344    interrupt: InterruptType,
345) {
346    match cpu.state.cycle {
347        0 => {
348            // For hardware interrupts only; read operand but don't increment PC
349            bus.read(u24_address(cpu.registers.pbr, cpu.registers.pc));
350        }
351        1 => {
352            if interrupt.is_software() {
353                fetch_operand(cpu, bus);
354            } else {
355                // Read operand but don't increment PC
356                bus.read(u24_address(cpu.registers.pbr, cpu.registers.pc));
357            }
358
359            if cpu.registers.emulation_mode {
360                // Emulation mode skips pushing PBR
361                cpu.state.cycle += 1;
362            }
363        }
364        2 => {
365            bus.write(cpu.registers.s.into(), cpu.registers.pbr);
366            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
367        }
368        3 => {
369            bus.write(cpu.registers.s.into(), cpu.registers.pc.msb());
370            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
371
372            if cpu.registers.emulation_mode {
373                ensure_page_1_stack(&mut cpu.registers);
374            }
375        }
376        4 => {
377            bus.write(cpu.registers.s.into(), cpu.registers.pc.lsb());
378            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
379
380            if cpu.registers.emulation_mode {
381                ensure_page_1_stack(&mut cpu.registers);
382            }
383        }
384        5 => {
385            let p_mask = if cpu.registers.emulation_mode && !interrupt.is_software() {
386                // Mask out the x flag, which emulates the 6502's b flag in emulation mode
387                !0x10
388            } else {
389                !0
390            };
391
392            bus.write(cpu.registers.s.into(), u8::from(cpu.registers.p) & p_mask);
393            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
394
395            cpu.registers.p.irq_disabled = true;
396            cpu.registers.p.decimal_mode = false;
397
398            if cpu.registers.emulation_mode {
399                ensure_page_1_stack(&mut cpu.registers);
400            }
401        }
402        6 => {
403            let vector = if cpu.registers.emulation_mode {
404                interrupt.emulation_vector()
405            } else {
406                interrupt.native_vector()
407            };
408            cpu.state.t0 = bus.read(vector.into());
409        }
410        7 => {
411            final_cycle(cpu, bus);
412
413            let vector = if cpu.registers.emulation_mode {
414                interrupt.emulation_vector()
415            } else {
416                interrupt.native_vector()
417            };
418            let pc_msb = bus.read(vector.wrapping_add(1).into());
419            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, pc_msb]);
420            cpu.registers.pbr = 0;
421        }
422        _ => invalid_cycle!(cpu),
423    }
424}

RTS: Return from subroutine

427pub(crate) fn rts<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
428    match cpu.state.cycle {
429        1 | 2 => {
430            bus.idle();
431        }
432        3 => {
433            cpu.registers.s = cpu.registers.s.wrapping_add(1);
434
435            if cpu.registers.emulation_mode {
436                ensure_page_1_stack(&mut cpu.registers);
437            }
438
439            cpu.state.t0 = bus.read(cpu.registers.s.into());
440        }
441        4 => {
442            cpu.registers.s = cpu.registers.s.wrapping_add(1);
443
444            if cpu.registers.emulation_mode {
445                ensure_page_1_stack(&mut cpu.registers);
446            }
447
448            cpu.state.t1 = bus.read(cpu.registers.s.into());
449        }
450        5 => {
451            final_cycle(cpu, bus);
452
453            bus.idle();
454
455            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(1);
456        }
457        _ => invalid_cycle!(cpu),
458    }
459}

RTL: Return from subroutine long

462pub(crate) fn rtl<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
463    match cpu.state.cycle {
464        1 | 2 => {
465            bus.idle();
466        }
467        3 => {
468            cpu.registers.s = cpu.registers.s.wrapping_add(1);
469            cpu.state.t0 = bus.read(cpu.registers.s.into());
470        }
471        4 => {
472            cpu.registers.s = cpu.registers.s.wrapping_add(1);
473            cpu.state.t1 = bus.read(cpu.registers.s.into());
474        }
475        5 => {
476            final_cycle(cpu, bus);
477
478            cpu.registers.s = cpu.registers.s.wrapping_add(1);
479            cpu.registers.pbr = bus.read(cpu.registers.s.into());
480
481            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]).wrapping_add(1);
482
483            if cpu.registers.emulation_mode {
484                ensure_page_1_stack(&mut cpu.registers);
485            }
486        }
487        _ => invalid_cycle!(cpu),
488    }
489}

RTI: Return from interrupt

492pub(crate) fn rti<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
493    match cpu.state.cycle {
494        1 | 2 => {
495            bus.idle();
496        }
497        3 => {
498            cpu.registers.s = cpu.registers.s.wrapping_add(1);
499
500            if cpu.registers.emulation_mode {
501                ensure_page_1_stack(&mut cpu.registers);
502            }
503
504            let p = bus.read(cpu.registers.s.into());
505            if cpu.registers.emulation_mode {
506                // m and x bits are forced to 1 in emulation mode
507                cpu.registers.p = (0x30 | p).into();
508            } else {
509                cpu.registers.p = p.into();
510
511                // Immediately truncate X and Y if x bit was set
512                if cpu.registers.p.index_size == SizeBits::Eight {
513                    cpu.registers.x &= 0x00FF;
514                    cpu.registers.y &= 0x00FF;
515                }
516            }
517        }
518        4 => {
519            cpu.registers.s = cpu.registers.s.wrapping_add(1);
520
521            if cpu.registers.emulation_mode {
522                ensure_page_1_stack(&mut cpu.registers);
523            }
524
525            cpu.state.t0 = bus.read(cpu.registers.s.into());
526        }
527        5 => {
528            cpu.registers.s = cpu.registers.s.wrapping_add(1);
529
530            if cpu.registers.emulation_mode {
531                // Emulation mode skips the last cycle (pull PBR)
532                final_cycle(cpu, bus);
533
534                ensure_page_1_stack(&mut cpu.registers);
535
536                let pc_msb = bus.read(cpu.registers.s.into());
537                cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, pc_msb]);
538            } else {
539                cpu.state.t1 = bus.read(cpu.registers.s.into());
540            }
541        }
542        6 => {
543            final_cycle(cpu, bus);
544
545            cpu.registers.s = cpu.registers.s.wrapping_add(1);
546            cpu.registers.pbr = bus.read(cpu.registers.s.into());
547
548            cpu.registers.pc = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
549        }
550        _ => invalid_cycle!(cpu),
551    }
552}

PEA: Push effective address

555pub(crate) fn pea<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
556    match cpu.state.cycle {
557        1 => {
558            cpu.state.t0 = fetch_operand(cpu, bus);
559        }
560        2 => {
561            cpu.state.t1 = fetch_operand(cpu, bus);
562        }
563        3 => {
564            bus.write(cpu.registers.s.into(), cpu.state.t1);
565            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
566        }
567        4 => {
568            final_cycle(cpu, bus);
569
570            bus.write(cpu.registers.s.into(), cpu.state.t0);
571            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
572
573            if cpu.registers.emulation_mode {
574                ensure_page_1_stack(&mut cpu.registers);
575            }
576        }
577        _ => invalid_cycle!(cpu),
578    }
579}

PEI: Push effective indirect address

582pub(crate) fn pei<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
583    match cpu.state.cycle {
584        1 => {
585            cpu.state.t0 = fetch_operand(cpu, bus);
586            check_direct_page_low_byte(cpu);
587        }
588        2 => {
589            // Idle cycle if D LSB is non-zero
590            bus.idle();
591        }
592        3 => {
593            let address = cpu.registers.d.wrapping_add(cpu.state.t0.into());
594            cpu.state.t1 = bus.read(address.into());
595        }
596        4 => {
597            let address = index_direct_page(cpu, cpu.state.t0, 1);
598            cpu.state.t2 = bus.read(address.into());
599        }
600        5 => {
601            bus.write(cpu.registers.s.into(), cpu.state.t2);
602            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
603        }
604        6 => {
605            final_cycle(cpu, bus);
606
607            bus.write(cpu.registers.s.into(), cpu.state.t1);
608            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
609
610            if cpu.registers.emulation_mode {
611                ensure_page_1_stack(&mut cpu.registers);
612            }
613        }
614        _ => invalid_cycle!(cpu),
615    }
616}

PER: Push effective relative address

619pub(crate) fn per<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
620    match cpu.state.cycle {
621        1 => {
622            cpu.state.t0 = fetch_operand(cpu, bus);
623        }
624        2 => {
625            cpu.state.t1 = fetch_operand(cpu, bus);
626        }
627        3 => {
628            bus.idle();
629        }
630        4 => {
631            let offset = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
632            let address = cpu.registers.pc.wrapping_add(offset);
633            bus.write(cpu.registers.s.into(), address.msb());
634            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
635        }
636        5 => {
637            final_cycle(cpu, bus);
638
639            let offset = u16::from_le_bytes([cpu.state.t0, cpu.state.t1]);
640            let address = cpu.registers.pc.wrapping_add(offset);
641            bus.write(cpu.registers.s.into(), address.lsb());
642            cpu.registers.s = cpu.registers.s.wrapping_sub(1);
643
644            if cpu.registers.emulation_mode {
645                ensure_page_1_stack(&mut cpu.registers);
646            }
647        }
648        _ => invalid_cycle!(cpu),
649    }
650}
652macro_rules! impl_halt {
653    ($name:ident, $field:ident) => {
654        pub(crate) fn $name<B: BusInterface>(cpu: &mut Wdc65816, bus: &mut B) {
655            match cpu.state.cycle {
656                1 => {
657                    bus.idle();
658                }
659                2 => {
660                    final_cycle(cpu, bus);
661
662                    bus.idle();
663
664                    cpu.state.$field = true;
665                }
666                _ => invalid_cycle!(cpu),
667            }
668        }
669    };
670}

WAI: Wait for interrupt

673impl_halt!(wai, waiting);

STP: Stop the clock

676impl_halt!(stp, stopped);