1use crate::instructions::Condition; 2use jgenesis_common::num::GetBit; 3 4struct DecodeTableEntry { 5 mask: u32, 6 target: u32, 7 decode_fn: fn(u32) -> String, 8} 9 10impl DecodeTableEntry { 11 const fn new(mask: u32, target: u32, decode_fn: fn(u32) -> String) -> Self { 12 Self { mask, target, decode_fn } 13 } 14} 15 16const ARM_DECODE_TABLE: &[DecodeTableEntry] = &[ 17 DecodeTableEntry::new(0x0FFFFFF0, 0x012FFF10, arm_bx), 18 DecodeTableEntry::new(0x0E000000, 0x0A000000, arm_b), 19 DecodeTableEntry::new(0x0FC000F0, 0x00000090, arm_mul), 20 DecodeTableEntry::new(0x0F8000F0, 0x00800090, arm_mull), 21 DecodeTableEntry::new(0x0FB00FF0, 0x01000090, arm_swap), 22 DecodeTableEntry::new(0x0E000090, 0x00000090, arm_ldrh), 23 DecodeTableEntry::new(0x0FBF0FFF, 0x010F0000, arm_mrs), 24 DecodeTableEntry::new(0x0DB6F000, 0x0120F000, arm_msr), 25 DecodeTableEntry::new(0x0C000000, 0x00000000, arm_alu), 26 DecodeTableEntry::new(0x0E000010, 0x06000010, |_| "Undefined".into()), 27 DecodeTableEntry::new(0x0C000000, 0x04000000, arm_ldr), 28 DecodeTableEntry::new(0x0E000000, 0x08000000, arm_ldm_stm), 29 DecodeTableEntry::new(0x0F000000, 0x0F000000, arm_swi), 30]; 31 32pub fn arm(opcode: u32) -> String { 33 for &DecodeTableEntry { mask, target, decode_fn } in ARM_DECODE_TABLE { 34 if opcode & mask == target { 35 return decode_fn(opcode); 36 } 37 } 38 39 "(unknown)".into() 40} 41 42fn arm_bx(opcode: u32) -> String { 43 let cond = Condition::from_arm_opcode(opcode).suffix(); 44 let rn = opcode & 0xF; 45 format!("BX{cond} R{rn}") 46} 47 48fn arm_b(opcode: u32) -> String { 49 let cond = Condition::from_arm_opcode(opcode).suffix(); 50 let link = if opcode.bit(24) { "L" } else { "" }; 51 let offset = (((opcode & 0xFFFFFF) as i32) << 8) >> 6; 52 format!("B{link}{cond} {offset}") 53} 54 55fn arm_alu(opcode: u32) -> String { 56 use std::fmt::Write; 57 58 let operation = match (opcode >> 21) & 0xF { 59 0 => "AND", 60 1 => "EOR", 61 2 => "SUB", 62 3 => "RSB", 63 4 => "ADD", 64 5 => "ADC", 65 6 => "SBC", 66 7 => "RSC", 67 8 => "TST", 68 9 => "TEQ", 69 10 => "CMP", 70 11 => "CMN", 71 12 => "ORR", 72 13 => "MOV", 73 14 => "BIC", 74 15 => "MVN", 75 _ => unreachable!(), 76 }; 77 78 let set_conditions = if opcode.bit(20) { "S" } else { "" }; 79 80 let rn = (opcode >> 16) & 0xF; 81 let rd = (opcode >> 12) & 0xF; 82 83 let condition = Condition::from_arm_opcode(opcode).suffix(); 84 85 let operand2 = if opcode.bit(25) { 86 let imm = opcode & 0xFF; 87 let rotation = ((opcode >> 8) & 0xF) << 1; 88 format!("#0x{:X}", imm.rotate_right(rotation)) 89 } else { 90 let rm = opcode & 0xF; 91 let mut s = format!("R{rm}"); 92 93 let shift = (opcode >> 4) & 0xFF; 94 if shift != 0 { 95 let shift_type = shift_type_str(shift >> 1); 96 97 if shift.bit(0) { 98 let rs = shift >> 4; 99 let _ = write!(s, ", {shift_type} R{rs}"); 100 } else { 101 let mut amount = shift >> 3; 102 if amount == 0 { 103 amount = 32; 104 } 105 106 let _ = write!(s, ", {shift_type} #{amount}"); 107 } 108 } 109 110 s 111 }; 112 113 if operation == "MOV" || operation == "MVN" { 114 format!("{operation}{condition}{set_conditions} R{rd}, {operand2}") 115 } else if matches!(operation, "CMP" | "CMN" | "TEQ" | "TST") { 116 format!("{operation}{condition} R{rn}, {operand2}") 117 } else { 118 format!("{operation}{condition}{set_conditions} R{rd}, R{rn}, {operand2}") 119 } 120} 121 122fn shift_type_str(shift_type: u32) -> &'static str { 123 match shift_type & 3 { 124 0 => "LSL", 125 1 => "LSR", 126 2 => "ASR", 127 3 => "ROR", 128 _ => unreachable!(), 129 } 130} 131 132fn arm_mrs(opcode: u32) -> String { 133 let cond = Condition::from_arm_opcode(opcode).suffix(); 134 let psr = if opcode.bit(22) { "SPSR" } else { "CPSR" }; 135 let rd = (opcode >> 12) & 0xF; 136 137 format!("MRS{cond} R{rd}, {psr}") 138} 139 140fn arm_msr(opcode: u32) -> String { 141 let cond = Condition::from_arm_opcode(opcode).suffix(); 142 let psr = if opcode.bit(22) { "SPSR" } else { "CPSR" }; 143 let control = if opcode.bit(16) { "c" } else { "" }; 144 let flags = if opcode.bit(19) { "f" } else { "" }; 145 146 let expression = if !opcode.bit(16) && opcode.bit(25) { 147 let immediate = opcode & 0xFF; 148 let rotation = ((opcode >> 8) & 0xF) << 1; 149 let value = immediate.rotate_right(rotation); 150 format!("#0x{value:X}") 151 } else { 152 let rm = opcode & 0xF; 153 format!("R{rm}") 154 }; 155 156 format!("MSR{cond} {psr}_{control}{flags}, {expression}") 157} 158 159fn arm_mul(opcode: u32) -> String { 160 let cond = Condition::from_arm_opcode(opcode).suffix(); 161 let rm = opcode & 0xF; 162 let rs = (opcode >> 8) & 0xF; 163 let rn = (opcode >> 12) & 0xF; 164 let rd = (opcode >> 16) & 0xF; 165 let s = if opcode.bit(20) { "S" } else { "" }; 166 let accumulate = opcode.bit(21); 167 168 if accumulate { 169 format!("MLA{cond}{s} R{rd}, R{rm}, R{rs}, R{rn}") 170 } else { 171 format!("MUL{cond}{s} R{rd}, R{rm}, R{rs}") 172 } 173} 174 175fn arm_mull(opcode: u32) -> String { 176 let cond = Condition::from_arm_opcode(opcode).suffix(); 177 let rm = opcode & 0xF; 178 let rs = (opcode >> 8) & 0xF; 179 let rdlo = (opcode >> 12) & 0xF; 180 let rdhi = (opcode >> 16) & 0xF; 181 let s = if opcode.bit(20) { "S" } else { "" }; 182 let accumulate = opcode.bit(21); 183 let signed = if opcode.bit(22) { "S" } else { "U" }; 184 185 let op = if accumulate { "MLAL" } else { "MULL" }; 186 format!("{signed}{op}{cond}{s} R{rdlo}, R{rdhi}, R{rm}, R{rs}") 187} 188 189fn arm_ldr(opcode: u32) -> String { 190 let cond = Condition::from_arm_opcode(opcode).suffix(); 191 192 let rn = (opcode >> 16) & 0xF; 193 let rd = (opcode >> 12) & 0xF; 194 let load = opcode.bit(20); 195 let write_back = opcode.bit(21); 196 let byte = opcode.bit(22); 197 let add = opcode.bit(23); 198 let pre_indexed = opcode.bit(24); 199 let immediate = !opcode.bit(25); 200 201 let add_str = if add { "" } else { "-" }; 202 203 let offset = if immediate { 204 let offset = opcode & 0xFFF; 205 format!("#{add_str}0x{offset:X}") 206 } else { 207 let rm = opcode & 0xF; 208 let shift = (opcode >> 4) & 0xFF; 209 let shift_type = shift_type_str(shift >> 1); 210 let mut shift_amount = shift >> 3; 211 if shift_type != "LSL" && shift_amount == 0 { 212 shift_amount = 32; 213 } 214 215 if shift == 0 { 216 format!("{add_str}R{rm}") 217 } else { 218 format!("{add_str}R{rm}, {shift_type} {shift_amount}") 219 } 220 }; 221 222 let address = if pre_indexed { 223 let write_back_str = if write_back { "!" } else { "" }; 224 format!("[R{rn}, {offset}]{write_back_str}") 225 } else { 226 format!("[R{rn}], {offset}") 227 }; 228 229 let operation = if load { "LDR" } else { "STR" }; 230 let byte_suffix = if byte { "B" } else { "" }; 231 232 format!("{operation}{cond}{byte_suffix} R{rd}, {address}") 233} 234 235fn arm_ldrh(opcode: u32) -> String { 236 let cond = Condition::from_arm_opcode(opcode).suffix(); 237 238 let data_type = match (opcode >> 5) & 3 { 239 0 => "SWP", 240 1 => "H", 241 2 => "SB", 242 3 => "SH", 243 _ => unreachable!(), 244 }; 245 246 let operation = if opcode.bit(20) { "LDR" } else { "STR" }; 247 248 let rd = (opcode >> 12) & 0xF; 249 let rn = (opcode >> 16) & 0xF; 250 251 let offset = if opcode.bit(22) { 252 format!("#0x{:X}", (opcode >> 4) & 0xF0 | (opcode & 0xF)) 253 } else { 254 format!("R{}", opcode & 0xF) 255 }; 256 257 let sign = if opcode.bit(23) { "+" } else { "-" }; 258 let write_back = if opcode.bit(21) { "!" } else { "" }; 259 260 let address = if opcode.bit(24) { 261 format!("[R{rn}, {sign}{offset}]{write_back}") 262 } else { 263 format!("[R{rn}], {sign}{offset}{write_back}") 264 }; 265 266 format!("{operation}{cond}{data_type} R{rd}, {address}") 267} 268 269fn arm_ldm_stm(opcode: u32) -> String { 270 let cond = Condition::from_arm_opcode(opcode).suffix(); 271 272 let rlist = 273 (0..16).filter(|&i| opcode.bit(i)).map(|i| format!("R{i}")).collect::<Vec<_>>().join(", "); 274 275 let rn = (opcode >> 16) & 0xF; 276 let load = opcode.bit(20); 277 let write_back = opcode.bit(21); 278 let s_bit = opcode.bit(22); 279 let up = opcode.bit(23); 280 let pre = opcode.bit(24); 281 282 let write_back_str = if write_back { "!" } else { "" }; 283 let s_bit_str = if s_bit { "^" } else { "" }; 284 let up_str = if up { "I" } else { "D" }; 285 let pre_str = if pre { "B" } else { "A" }; 286 287 let op = if load { "LDM" } else { "STM" }; 288 289 format!("{op}{cond}{up_str}{pre_str} R{rn}{write_back_str} {{{rlist}}}{s_bit_str}") 290} 291 292fn arm_swap(opcode: u32) -> String { 293 let cond = Condition::from_arm_opcode(opcode).suffix(); 294 295 let rm = opcode & 0xF; 296 let rd = (opcode >> 12) & 0xF; 297 let rn = (opcode >> 16) & 0xF; 298 let byte = if opcode.bit(22) { "B" } else { "" }; 299 300 format!("SWP{cond}{byte} R{rd}, R{rm}, [R{rn}]") 301} 302 303fn arm_swi(opcode: u32) -> String { 304 let cond = Condition::from_arm_opcode(opcode).suffix(); 305 let expression = opcode & 0xFFFFFF; 306 307 format!("SWI{cond} 0x{expression:X}") 308} 309 310struct ThumbDecodeEntry { 311 mask: u16, 312 target: u16, 313 decode_fn: fn(u16) -> String, 314} 315 316impl ThumbDecodeEntry { 317 const fn new(mask: u16, target: u16, decode_fn: fn(u16) -> String) -> Self { 318 Self { mask, target, decode_fn } 319 } 320} 321 322const THUMB_DECODE_TABLE: &[ThumbDecodeEntry] = &[ 323 ThumbDecodeEntry::new(0xF800, 0x1800, thumb_2), 324 ThumbDecodeEntry::new(0xE000, 0x0000, thumb_1), 325 ThumbDecodeEntry::new(0xE000, 0x2000, thumb_3), 326 ThumbDecodeEntry::new(0xFC00, 0x4000, thumb_4), 327 ThumbDecodeEntry::new(0xFC00, 0x4400, thumb_5), 328 ThumbDecodeEntry::new(0xF800, 0x4800, thumb_6), 329 ThumbDecodeEntry::new(0xF200, 0x5000, thumb_7), 330 ThumbDecodeEntry::new(0xF200, 0x5200, thumb_8), 331 ThumbDecodeEntry::new(0xE000, 0x6000, thumb_9), 332 ThumbDecodeEntry::new(0xF000, 0x8000, thumb_10), 333 ThumbDecodeEntry::new(0xF000, 0x9000, thumb_11), 334 ThumbDecodeEntry::new(0xF000, 0xA000, thumb_12), 335 ThumbDecodeEntry::new(0xFF00, 0xB000, thumb_13), 336 ThumbDecodeEntry::new(0xF600, 0xB400, thumb_14), 337 ThumbDecodeEntry::new(0xF000, 0xC000, thumb_15), 338 ThumbDecodeEntry::new(0xFF00, 0xDF00, thumb_17), 339 ThumbDecodeEntry::new(0xF000, 0xD000, thumb_16), 340 ThumbDecodeEntry::new(0xF800, 0xE000, thumb_18), 341 ThumbDecodeEntry::new(0xF000, 0xF000, thumb_19), 342]; 343 344pub fn thumb(opcode: u16) -> String { 345 for &ThumbDecodeEntry { mask, target, decode_fn } in THUMB_DECODE_TABLE { 346 if opcode & mask == target { 347 return decode_fn(opcode); 348 } 349 } 350 351 "(unknown)".into() 352} 353 354// Move shifted register 355fn thumb_1(opcode: u16) -> String { 356 let rd = opcode & 7; 357 let rs = (opcode >> 3) & 7; 358 let offset = (opcode >> 6) & 0x1F; 359 360 let op = match (opcode >> 11) & 3 { 361 0 => "LSL", 362 1 => "LSR", 363 2 => "ASR", 364 _ => panic!("invalid Thumb format 1 opcode: {opcode:04X}"), 365 }; 366 367 format!("{op} R{rd}, R{rs} #{offset}") 368} 369 370// Add/subtract 371fn thumb_2(opcode: u16) -> String { 372 let rd = opcode & 7; 373 let rs = (opcode >> 3) & 7; 374 let op = if opcode.bit(9) { "SUB" } else { "ADD" }; 375 376 let immediate = opcode.bit(10); 377 if immediate { 378 let operand = (opcode >> 6) & 7; 379 format!("{op} R{rd}, R{rs}, #{operand}") 380 } else { 381 let rn = (opcode >> 6) & 7; 382 format!("{op} R{rd}, R{rs}, R{rn}") 383 } 384} 385 386// Move/compare/add/subtract immediate 387fn thumb_3(opcode: u16) -> String { 388 let immediate = opcode & 0xFF; 389 let rd = (opcode >> 8) & 7; 390 let op = match (opcode >> 11) & 3 { 391 0 => "MOV", 392 1 => "CMP", 393 2 => "ADD", 394 3 => "SUB", 395 _ => unreachable!(), 396 }; 397 398 format!("{op} R{rd}, #0x{immediate:X}") 399} 400 401// ALU operations 402fn thumb_4(opcode: u16) -> String { 403 let rd = opcode & 7; 404 let rs = (opcode >> 3) & 7; 405 let op = match (opcode >> 6) & 0xF { 406 0x0 => "AND", 407 0x1 => "EOR", 408 0x2 => "LSL", 409 0x3 => "LSR", 410 0x4 => "ASR", 411 0x5 => "ADC", 412 0x6 => "SBC", 413 0x7 => "ROR", 414 0x8 => "TST", 415 0x9 => "NEG", 416 0xA => "CMP", 417 0xB => "CMN", 418 0xC => "ORR", 419 0xD => "MUL", 420 0xE => "BIC", 421 0xF => "MVN", 422 _ => unreachable!(), 423 }; 424 425 format!("{op} R{rd}, R{rs}") 426} 427 428// Hi register operations / branch exchange 429fn thumb_5(opcode: u16) -> String { 430 let mut rd = opcode & 7; 431 let mut rs = (opcode >> 3) & 7; 432 433 let h1 = opcode.bit(7); 434 let h2 = opcode.bit(6); 435 436 if h1 { 437 rd += 8; 438 } 439 440 if h2 { 441 rs += 8; 442 } 443 444 match (opcode >> 8) & 3 { 445 0 => format!("ADD R{rd}, R{rs}"), 446 1 => format!("CMP R{rd}, R{rs}"), 447 2 => format!("MOV R{rd}, R{rs}"), 448 3 => format!("BX R{rs}"), 449 _ => unreachable!(), 450 } 451} 452 453// PC-relative load 454fn thumb_6(opcode: u16) -> String { 455 let offset = (opcode & 0xFF) << 2; 456 let rd = (opcode >> 8) & 7; 457 458 format!("LDR R{rd}, [PC, #0x{offset:X}]") 459} 460 461// Load/store with register offset 462fn thumb_7(opcode: u16) -> String { 463 let rd = opcode & 7; 464 let rb = (opcode >> 3) & 7; 465 let ro = (opcode >> 6) & 7; 466 467 let byte_suffix = if opcode.bit(10) { "B" } else { "" }; 468 let op = if opcode.bit(11) { "LDR" } else { "STR" }; 469 470 format!("{op}{byte_suffix} R{rd}, [R{rb}, R{ro}]") 471} 472 473// Load/store sign-extended byte/halfword 474fn thumb_8(opcode: u16) -> String { 475 let rd = opcode & 7; 476 let rb = (opcode >> 3) & 7; 477 let ro = (opcode >> 6) & 7; 478 479 let sh_bits = (opcode >> 10) & 3; 480 let op = match sh_bits { 481 0 => "STRH", 482 1 => "LDSB", 483 2 => "LDRH", 484 3 => "LDSH", 485 _ => unreachable!(), 486 }; 487 488 format!("{op} R{rd}, [R{rb}, R{ro}]") 489} 490 491// Load/store with immediate offset 492fn thumb_9(opcode: u16) -> String { 493 let rd = opcode & 7; 494 let rb = (opcode >> 3) & 7; 495 496 let mut offset = (opcode >> 6) & 0x1F; 497 498 let byte = opcode.bit(12); 499 if !byte { 500 offset <<= 2; 501 } 502 503 let byte_suffix = if byte { "B" } else { "" }; 504 let op = if opcode.bit(11) { "LDR" } else { "STR" }; 505 506 format!("{op}{byte_suffix} R{rd}, [R{rb}, #{offset}]") 507} 508 509// Load/store halfword 510fn thumb_10(opcode: u16) -> String { 511 let rd = opcode & 7; 512 let rb = (opcode >> 3) & 7; 513 let offset = ((opcode >> 6) & 0x1F) << 1; 514 let load = opcode.bit(11); 515 516 let op = if load { "LDRH" } else { "STRH" }; 517 format!("{op} R{rd}, [R{rb}, #0x{offset:X}]") 518} 519 520// SP-relative load/store 521fn thumb_11(opcode: u16) -> String { 522 let offset = (opcode & 0xFF) << 2; 523 let rd = (opcode >> 8) & 7; 524 let load = opcode.bit(11); 525 526 let op = if load { "LDR" } else { "STR" }; 527 format!("{op} R{rd}, [SP, #0x{offset:X}]") 528} 529 530// Load address 531fn thumb_12(opcode: u16) -> String { 532 let immediate = (opcode & 0xFF) << 2; 533 let rd = (opcode >> 8) & 7; 534 let source = if opcode.bit(11) { "SP" } else { "PC" }; 535 536 format!("ADD R{rd}, {source}, #0x{immediate:X}") 537} 538 539// Add offset to stack pointer 540fn thumb_13(opcode: u16) -> String { 541 let offset = (opcode & 0x7F) << 2; 542 let sign = opcode.bit(7); 543 544 let sign_str = if sign { "-" } else { "" }; 545 546 format!("ADD SP, #{sign_str}0x{offset:X}") 547} 548 549// Push/pop registers 550fn thumb_14(opcode: u16) -> String { 551 let lr_pc_bit = opcode.bit(8); 552 let load = opcode.bit(11); 553 554 let op = if load { "POP" } else { "PUSH" }; 555 556 let mut rlist = thumb_rlist_vec(opcode); 557 if lr_pc_bit { 558 rlist.push(if load { "PC".into() } else { "LR".into() }); 559 } 560 561 format!("{op} {{{}}}", rlist.join(", ")) 562} 563 564// Multiple load/store 565fn thumb_15(opcode: u16) -> String { 566 let op = if opcode.bit(11) { "LDMIA" } else { "STMIA" }; 567 let rb = (opcode >> 8) & 7; 568 569 let rlist = thumb_rlist_vec(opcode).join(","); 570 571 format!("{op} R{rb}! {{{rlist}}}") 572} 573 574fn thumb_rlist_vec(opcode: u16) -> Vec<String> { 575 (0..8).filter(|&i| opcode.bit(i)).map(|i| format!("R{i}")).collect() 576} 577 578// Conditional branch 579fn thumb_16(opcode: u16) -> String { 580 let cond = Condition::from_bits((opcode >> 8).into()).suffix(); 581 let offset = i16::from(opcode as i8) << 1; 582 583 format!("B{cond} {offset}") 584} 585 586// Software interrupt 587fn thumb_17(opcode: u16) -> String { 588 let comment = opcode & 0xFF; 589 format!("SWI 0x{comment:X}") 590} 591 592// Unconditional branch 593fn thumb_18(opcode: u16) -> String { 594 let offset = (((opcode & 0x3FF) as i16) << 5) >> 4; 595 format!("B {offset}") 596} 597 598// Long branch with link 599fn thumb_19(opcode: u16) -> String { 600 let which = if opcode.bit(11) { "(low)" } else { "(high)" }; 601 let offset = opcode & 0x7FF; 602 603 format!("BL {which} #0x{offset:X}") 604}