jevsnes.git / third-party / rust / jgenesis / cpu / sh2-emu / src / instructions.rs

SH-2 instruction implementations

3mod alu;
4mod bits;
5mod branch;
6mod load;
8use crate::Sh2;
9use crate::bus::BusInterface;
10use std::array;
11
12pub type OpcodeFn<Bus> = fn(&mut Sh2, u16, &mut Bus);
13
14pub struct OpcodeTable<Bus: BusInterface>(Box<[OpcodeFn<Bus>; 4096]>);
15
16impl<Bus: BusInterface> Default for OpcodeTable<Bus> {
17    fn default() -> Self {
18        // Use bits 15-12 and 7-0 for the opcode lookup instead of all 16 bits to keep the lookup table
19        // size down (32KB vs. 512KB for 64-bit), making it more likely that most of it will fit in cache.
20        //
21        // For opcodes where bits 11-8 are needed to determine which instruction function to call, the
22        // returned OpcodeFn will match on those bits when called. This is only needed for opcodes where
23        // the highest 4 bits are either 0b1000 or 0b1100.
24        Self(Box::new(array::from_fn(|i| {
25            let opcode = (i & 0xFF) | ((i & 0xF00) << 4);
26            decode_inner(opcode as u16)
27        })))
28    }
29}
30
31impl<Bus: BusInterface> OpcodeTable<Bus> {
32    #[must_use]
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    pub(crate) fn decode(&self, opcode: u16) -> OpcodeFn<Bus> {
38        self.0[((opcode & 0xFF) | ((opcode & 0xF000) >> 4)) as usize]
39    }
40}
41
42impl<Bus: BusInterface> Clone for OpcodeTable<Bus> {
43    fn clone(&self) -> Self {
44        Self(self.0.clone())
45    }
46}
47
48macro_rules! cpu {
49    ($($name:tt)*) => {
50        |cpu, _, _| Sh2::$($name)*(cpu)
51    }
52}
53
54macro_rules! cpu_op {
55    ($($name:tt)*) => {
56        |cpu, opcode, _| Sh2::$($name)*(cpu, opcode)
57    }
58}
59
60fn nop<Bus: BusInterface>(_cpu: &mut Sh2, _opcode: u16, _bus: &mut Bus) {}
61
62fn sleep<Bus: BusInterface>(cpu: &mut Sh2, _opcode: u16, _bus: &mut Bus) {
63    // In actual hardware, SLEEP causes the CPU to enter a low-power state.
64    // Since nothing uses SLEEP except for a handful of demos, use a simpler implementation that
65    // is inefficient but doesn't require checking whether the CPU is sleeping after every instruction
66    cpu.registers.next_pc = cpu.registers.pc;
67    cpu.registers.pc = cpu.registers.pc.wrapping_sub(2);
68}
69
70fn illegal_opcode<Bus: BusInterface>(cpu: &mut Sh2, opcode: u16, bus: &mut Bus) {
71    const ILLEGAL_OPCODE_VECTOR_NUMBER: u32 = 4;
72
73    // Roll back PC to point to the illegal opcode
74    cpu.registers.pc = cpu.registers.pc.wrapping_sub(2);
75
76    log::error!(
77        "[{}] SH-2 executed illegal opcode {opcode:04X}, PC={:08X}",
78        cpu.name,
79        cpu.registers.pc
80    );
81
82    cpu.handle_exception(None, ILLEGAL_OPCODE_VECTOR_NUMBER, bus);
83}
84
85fn decode_inner<Bus: BusInterface>(opcode: u16) -> OpcodeFn<Bus> {
86    match opcode & 0b1111_0000_0000_1111 {
87        0b0110_0000_0000_0011 => cpu_op!(mov_rm_rn),
88        0b0010_0000_0000_0000 => Sh2::mov_b_rm_indirect,
89        0b0010_0000_0000_0001 => Sh2::mov_w_rm_indirect,
90        0b0010_0000_0000_0010 => Sh2::mov_l_rm_indirect,
91        0b0110_0000_0000_0000 => Sh2::mov_b_indirect_rn,
92        0b0110_0000_0000_0001 => Sh2::mov_w_indirect_rn,
93        0b0110_0000_0000_0010 => Sh2::mov_l_indirect_rn,
94        0b0010_0000_0000_0100 => Sh2::mov_b_rm_predec,
95        0b0010_0000_0000_0101 => Sh2::mov_w_rm_predec,
96        0b0010_0000_0000_0110 => Sh2::mov_l_rm_predec,
97        0b0110_0000_0000_0100 => Sh2::mov_b_postinc_rn,
98        0b0110_0000_0000_0101 => Sh2::mov_w_postinc_rn,
99        0b0110_0000_0000_0110 => Sh2::mov_l_postinc_rn,
100        0b0000_0000_0000_0100 => Sh2::mov_b_rm_indirect_indexed,
101        0b0000_0000_0000_0101 => Sh2::mov_w_rm_indirect_indexed,
102        0b0000_0000_0000_0110 => Sh2::mov_l_rm_indirect_indexed,
103        0b0000_0000_0000_1100 => Sh2::mov_b_indirect_indexed_rn,
104        0b0000_0000_0000_1101 => Sh2::mov_w_indirect_indexed_rn,
105        0b0000_0000_0000_1110 => Sh2::mov_l_indirect_indexed_rn,
106        0b0110_0000_0000_1000 => cpu_op!(swap_b),
107        0b0110_0000_0000_1001 => cpu_op!(swap_w),
108        0b0010_0000_0000_1101 => cpu_op!(xtrct),
109        0b0011_0000_0000_1100 => cpu_op!(add_rm_rn),
110        0b0011_0000_0000_1110 => cpu_op!(addc),
111        0b0011_0000_0000_1111 => cpu_op!(addv),
112        0b0011_0000_0000_0000 => cpu_op!(cmp_eq_rm_rn),
113        0b0011_0000_0000_0010 => cpu_op!(cmp_hs),
114        0b0011_0000_0000_0011 => cpu_op!(cmp_ge),
115        0b0011_0000_0000_0110 => cpu_op!(cmp_hi),
116        0b0011_0000_0000_0111 => cpu_op!(cmp_gt),
117        0b0010_0000_0000_1100 => cpu_op!(cmp_str),
118        0b0011_0000_0000_0100 => cpu_op!(div1),
119        0b0010_0000_0000_0111 => cpu_op!(div0s),
120        0b0011_0000_0000_1101 => cpu_op!(dmuls),
121        0b0011_0000_0000_0101 => cpu_op!(dmulu),
122        0b0110_0000_0000_1110 => cpu_op!(exts_b),
123        0b0110_0000_0000_1111 => cpu_op!(exts_w),
124        0b0110_0000_0000_1100 => cpu_op!(extu_b),
125        0b0110_0000_0000_1101 => cpu_op!(extu_w),
126        0b0000_0000_0000_1111 => Sh2::mac_l,
127        0b0100_0000_0000_1111 => Sh2::mac_w,
128        0b0000_0000_0000_0111 => cpu_op!(mul),
129        0b0010_0000_0000_1111 => cpu_op!(muls),
130        0b0010_0000_0000_1110 => cpu_op!(mulu),
131        0b0110_0000_0000_1011 => cpu_op!(neg),
132        0b0110_0000_0000_1010 => cpu_op!(negc),
133        0b0011_0000_0000_1000 => cpu_op!(sub_rm_rn),
134        0b0011_0000_0000_1010 => cpu_op!(subc),
135        0b0011_0000_0000_1011 => cpu_op!(subv),
136        0b0010_0000_0000_1001 => cpu_op!(and_rm_rn),
137        0b0110_0000_0000_0111 => cpu_op!(not),
138        0b0010_0000_0000_1011 => cpu_op!(or_rm_rn),
139        0b0010_0000_0000_1000 => cpu_op!(tst_rm_rn),
140        0b0010_0000_0000_1010 => cpu_op!(xor_rm_rn),
141        _ => match opcode & 0b1111_0000_1111_1111 {
142            0b0000_0000_0010_1001 => cpu_op!(movt),
143            0b0100_0000_0001_0001 => cpu_op!(cmp_pz),
144            0b0100_0000_0001_0101 => cpu_op!(cmp_pl),
145            0b0100_0000_0001_0000 => cpu_op!(dt),
146            0b0100_0000_0001_1011 => Sh2::tas,
147            0b0100_0000_0000_0100 => cpu_op!(rotl),
148            0b0100_0000_0000_0101 => cpu_op!(rotr),
149            0b0100_0000_0010_0100 => cpu_op!(rotcl),
150            0b0100_0000_0010_0101 => cpu_op!(rotcr),
151            // SHAL and SHLL behave identically; use SHLL implementation for both
152            0b0100_0000_0010_0000 | 0b0100_0000_0000_0000 => cpu_op!(shll),
153            0b0100_0000_0010_0001 => cpu_op!(shar),
154            0b0100_0000_0000_0001 => cpu_op!(shlr),
155            0b0100_0000_0000_1000 => cpu_op!(shlln::<2>),
156            0b0100_0000_0000_1001 => cpu_op!(shlrn::<2>),
157            0b0100_0000_0001_1000 => cpu_op!(shlln::<8>),
158            0b0100_0000_0001_1001 => cpu_op!(shlrn::<8>),
159            0b0100_0000_0010_1000 => cpu_op!(shlln::<16>),
160            0b0100_0000_0010_1001 => cpu_op!(shlrn::<16>),
161            0b0000_0000_0010_0011 => Sh2::braf,
162            0b0000_0000_0000_0011 => Sh2::bsrf,
163            0b0100_0000_0010_1011 => Sh2::jmp,
164            0b0100_0000_0000_1011 => Sh2::jsr,
165            0b0100_0000_0000_1110 => cpu_op!(ldc_rm_sr),
166            0b0100_0000_0001_1110 => cpu_op!(ldc_rm_gbr),
167            0b0100_0000_0010_1110 => cpu_op!(ldc_rm_vbr),
168            0b0100_0000_0000_0111 => Sh2::ldc_postinc_sr,
169            0b0100_0000_0001_0111 => Sh2::ldc_postinc_gbr,
170            0b0100_0000_0010_0111 => Sh2::ldc_postinc_vbr,
171            0b0100_0000_0000_1010 => cpu_op!(lds_rm_mach),
172            0b0100_0000_0001_1010 => cpu_op!(lds_rm_macl),
173            0b0100_0000_0010_1010 => cpu_op!(lds_rm_pr),
174            0b0100_0000_0000_0110 => Sh2::lds_postinc_mach,
175            0b0100_0000_0001_0110 => Sh2::lds_postinc_macl,
176            0b0100_0000_0010_0110 => Sh2::lds_postinc_pr,
177            0b0000_0000_0000_0010 => cpu_op!(stc_sr_rn),
178            0b0000_0000_0001_0010 => cpu_op!(stc_gbr_rn),
179            0b0000_0000_0010_0010 => cpu_op!(stc_vbr_rn),
180            0b0100_0000_0000_0011 => Sh2::stc_sr_rn_predec,
181            0b0100_0000_0001_0011 => Sh2::stc_gbr_rn_predec,
182            0b0100_0000_0010_0011 => Sh2::stc_vbr_rn_predec,
183            0b0000_0000_0000_1010 => cpu_op!(sts_mach_rn),
184            0b0000_0000_0001_1010 => cpu_op!(sts_macl_rn),
185            0b0000_0000_0010_1010 => cpu_op!(sts_pr_rn),
186            0b0100_0000_0000_0010 => Sh2::sts_mach_rn_predec,
187            0b0100_0000_0001_0010 => Sh2::sts_macl_rn_predec,
188            0b0100_0000_0010_0010 => Sh2::sts_pr_rn_predec,
189            0b0000_0000_0001_1001 => cpu!(div0u),
190            0b0000_0000_0000_1011 => |cpu, _, bus| cpu.rts(bus),
191            0b0000_0000_0000_1000 => cpu!(clrt),
192            0b0000_0000_0010_1000 => cpu!(clrmac),
193            0b0000_0000_0000_1001 => nop,
194            0b0000_0000_0010_1011 => |cpu, _, bus| cpu.rte(bus),
195            0b0000_0000_0001_1000 => cpu!(sett),
196            0b0000_0000_0001_1011 => sleep,
197            _ => match opcode & 0b1111_0000_0000_0000 {
198                0b1110_0000_0000_0000 => cpu_op!(mov_b_immediate_rn),
199                0b1001_0000_0000_0000 => Sh2::mov_w_immediate_rn,
200                0b1101_0000_0000_0000 => Sh2::mov_l_immediate_rn,
201                0b0001_0000_0000_0000 => Sh2::mov_l_rm_rn_displacement,
202                0b0101_0000_0000_0000 => Sh2::mov_l_rm_displacement_rn,
203                0b0111_0000_0000_0000 => cpu_op!(add_imm_rn),
204                0b1010_0000_0000_0000 => Sh2::bra,
205                0b1011_0000_0000_0000 => Sh2::bsr,
206                // Opcode begins with 0b1000; need to match on bits 11-8
207                0b1000_0000_0000_0000 => |cpu, opcode, bus| match (opcode >> 8) & 0b1111 {
208                    0b0000 => cpu.mov_b_r0_rn_displacement(opcode, bus),
209                    0b0001 => cpu.mov_w_r0_rn_displacement(opcode, bus),
210                    0b0100 => cpu.mov_b_rm_displacement_r0(opcode, bus),
211                    0b0101 => cpu.mov_w_rm_displacement_r0(opcode, bus),
212                    0b1000 => cpu.cmp_eq_imm_r0(opcode),
213                    0b1001 => cpu.bt(opcode, bus),
214                    0b1011 => cpu.bf(opcode, bus),
215                    0b1101 => cpu.bt_s(opcode, bus),
216                    0b1111 => cpu.bf_s(opcode, bus),
217                    _ => illegal_opcode(cpu, opcode, bus),
218                },
219                // Opcode begins with 0b1100; need to match on bits 11-8
220                0b1100_0000_0000_0000 => |cpu, opcode, bus| match (opcode >> 8) & 0b1111 {
221                    0b0000 => cpu.mov_b_r0_disp_gbr(opcode, bus),
222                    0b0001 => cpu.mov_w_r0_disp_gbr(opcode, bus),
223                    0b0010 => cpu.mov_l_r0_disp_gbr(opcode, bus),
224                    0b0011 => cpu.trapa(opcode, bus),
225                    0b0100 => cpu.mov_b_disp_gbr_r0(opcode, bus),
226                    0b0101 => cpu.mov_w_disp_gbr_r0(opcode, bus),
227                    0b0110 => cpu.mov_l_disp_gbr_r0(opcode, bus),
228                    0b0111 => cpu.mova(opcode),
229                    0b1000 => cpu.tst_imm_r0(opcode),
230                    0b1001 => cpu.and_imm_r0(opcode),
231                    0b1010 => cpu.xor_imm_r0(opcode),
232                    0b1011 => cpu.or_imm_r0(opcode),
233                    0b1100 => cpu.tst_imm_gbr_indexed(opcode, bus),
234                    0b1101 => cpu.and_imm_gbr_indexed(opcode, bus),
235                    0b1110 => cpu.xor_imm_gbr_indexed(opcode, bus),
236                    0b1111 => cpu.or_imm_gbr_indexed(opcode, bus),
237                    _ => unreachable!("value & 0b1111 will always be one of the above values"),
238                },
239                _ => illegal_opcode,
240            },
241        },
242    }
243}
244
245#[inline(always)]
246fn rn(opcode: u16) -> usize {
247    ((opcode >> 8) & 0xF) as usize
248}
249
250#[inline(always)]
251fn rm(opcode: u16) -> usize {
252    ((opcode >> 4) & 0xF) as usize
253}
254
255#[inline(always)]
256fn extend_i8(value: u8) -> u32 {
257    value as i8 as u32
258}
259
260#[inline(always)]
261fn extend_i16(value: u16) -> u32 {
262    value as i16 as u32
263}