flow.rsannotatedflow.rssource110 lines · 3.1 KB · raw
1use crate::superfx::gsu::GraphicsSupportUnit;
2use crate::superfx::gsu::instructions::{
3    MemoryType, clear_prefix_flags, fetch_opcode, fill_cache_from_pc, fill_cache_to_pc,
4    read_register,
5};
6use jgenesis_common::num::SignBit;
7
8pub(super) fn link(opcode: u8, memory_type: MemoryType, gsu: &mut GraphicsSupportUnit) -> u8 {
9    // LINK #n: Link return address
10    let n = opcode & 0x0F;
11    gsu.r[11] = gsu.r[15].wrapping_add(n.into()).wrapping_sub(1);
12
13    clear_prefix_flags(gsu);
14    memory_type.access_cycles(gsu.clock_speed)
15}
16
17macro_rules! impl_branch {
18    ($name:ident $(, $flag:ident $(^ $other:ident)? == $value:expr)?) => {
19        pub(super) fn $name(memory_type: MemoryType, gsu: &mut GraphicsSupportUnit, rom: &[u8], ram: &[u8]) -> u8 {
20            let e = gsu.state.opcode_buffer as i8;
21            fetch_opcode(gsu, rom, ram);
22
23            $(
24                if gsu.$flag $(^ gsu.$other)? != $value {
25                    return 2 * memory_type.access_cycles(gsu.clock_speed);
26                }
27            )?
28
29            let cycles = fill_cache_from_pc(gsu, rom, ram);
30
31            gsu.r[15] = gsu.r[15].wrapping_add(e as u16).wrapping_sub(1);
32            gsu.state.just_jumped = true;
33
34            cycles + 2 * memory_type.access_cycles(gsu.clock_speed)
35        }
36    }
37}
38
39// Branch instructions
40impl_branch!(bra);
41impl_branch!(bge, sign_flag ^ overflow_flag == false);
42impl_branch!(blt, sign_flag ^ overflow_flag == true);
43impl_branch!(bne, zero_flag == false);
44impl_branch!(beq, zero_flag == true);
45impl_branch!(bpl, sign_flag == false);
46impl_branch!(bmi, sign_flag == true);
47impl_branch!(bcc, carry_flag == false);
48impl_branch!(bcs, carry_flag == true);
49impl_branch!(bvc, overflow_flag == false);
50impl_branch!(bvs, overflow_flag == true);
51
52pub(super) fn jmp(
53    opcode: u8,
54    memory_type: MemoryType,
55    gsu: &mut GraphicsSupportUnit,
56    rom: &[u8],
57    ram: &[u8],
58) -> u8 {
59    // JMP Rn: Jump
60    let cycles = fill_cache_from_pc(gsu, rom, ram);
61
62    gsu.r[15] = gsu.r[(opcode & 0x0F) as usize];
63    gsu.state.just_jumped = true;
64
65    cycles + memory_type.access_cycles(gsu.clock_speed)
66}
67
68pub(super) fn ljmp(
69    opcode: u8,
70    memory_type: MemoryType,
71    gsu: &mut GraphicsSupportUnit,
72    rom: &[u8],
73    ram: &[u8],
74) -> u8 {
75    // LJMP Rn: Long jump
76    gsu.r[15] = read_register(gsu, gsu.sreg);
77    gsu.pbr = gsu.r[(opcode & 0x0F) as usize] as u8;
78
79    let cbr = gsu.r[15] & 0xFFF0;
80    gsu.code_cache.update_cbr(cbr);
81    let cycles = fill_cache_to_pc(gsu, gsu.r[15], rom, ram);
82
83    clear_prefix_flags(gsu);
84    cycles + memory_type.access_cycles(gsu.clock_speed)
85}
86
87pub(super) fn loop_(
88    memory_type: MemoryType,
89    gsu: &mut GraphicsSupportUnit,
90    rom: &[u8],
91    ram: &[u8],
92) -> u8 {
93    // LOOP: Loop
94    gsu.r[12] = gsu.r[12].wrapping_sub(1);
95    gsu.zero_flag = gsu.r[12] == 0;
96    gsu.sign_flag = gsu.r[12].sign_bit();
97
98    let cycles = if !gsu.zero_flag {
99        let cycles = fill_cache_from_pc(gsu, rom, ram);
100        gsu.r[15] = gsu.r[13];
101        gsu.state.just_jumped = true;
102
103        cycles
104    } else {
105        0
106    };
107
108    clear_prefix_flags(gsu);
109    cycles + memory_type.access_cycles(gsu.clock_speed)
110}