1//! SH-2 instruction implementations 2 3mod alu; 4mod bits; 5mod branch; 6mod load; 7 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}