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}

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}

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}

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}

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}

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}

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}

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}

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}

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}

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}

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}

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}

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}

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}

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}
574fn thumb_rlist_vec(opcode: u16) -> Vec<String> {
575    (0..8).filter(|&i| opcode.bit(i)).map(|i| format!("R{i}")).collect()
576}

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}

Software interrupt

587fn thumb_17(opcode: u16) -> String {
588    let comment = opcode & 0xFF;
589    format!("SWI 0x{comment:X}")
590}

Unconditional branch

593fn thumb_18(opcode: u16) -> String {
594    let offset = (((opcode & 0x3FF) as i16) << 5) >> 4;
595    format!("B {offset}")
596}

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}