jevsnes.git / third-party / rust / jgenesis / cpu / sh2-emu / src / disassemble.rs
1use crate::Sh2;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum MemoryAccessSize {
5    Byte,
6    Word,
7    Longword,
8}
9
10#[derive(Debug, Clone, Copy)]
11pub enum Displacement {
12    Immediate(u32),
13    Register(u16),
14}
15
16impl Displacement {
17    #[must_use]
18    pub fn resolve(self, size: MemoryAccessSize, cpu: &Sh2) -> u32 {
19        match self {
20            Self::Immediate(displacement) => match size {
21                MemoryAccessSize::Byte => displacement,
22                MemoryAccessSize::Word => displacement << 1,
23                MemoryAccessSize::Longword => displacement << 2,
24            },
25            Self::Register(r) => cpu.registers.gpr[r as usize],
26        }
27    }
28}
29
30impl Displacement {
31    fn none() -> Self {
32        Self::Immediate(0)
33    }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum ReadType {
38    Load,
39    EffectiveAddress, // MOVA, JMP, JSR, BRAF, BSRF
40}
41
42#[derive(Debug, Clone, Copy)]
43pub enum MemoryAccess {
44    IndirectR { register: u16, displacement: Displacement },
45    IndirectRPredecrement { register: u16 },
46    IndirectGbr { displacement: Displacement },
47    PcRelative { pc: u32, displacement: Displacement },
48}
49
50impl MemoryAccess {
51    #[must_use]
52    pub fn resolve_address(self, size: MemoryAccessSize, cpu: &Sh2) -> u32 {
53        match self {
54            Self::IndirectR { register, displacement } => {
55                cpu.registers().gpr[register as usize].wrapping_add(displacement.resolve(size, cpu))
56            }
57            Self::IndirectRPredecrement { register } => {
58                let address = cpu.registers.gpr[register as usize];
59                match size {
60                    MemoryAccessSize::Byte => address.wrapping_sub(1),
61                    MemoryAccessSize::Word => address.wrapping_sub(2),
62                    MemoryAccessSize::Longword => address.wrapping_sub(4),
63                }
64            }
65            Self::IndirectGbr { displacement } => {
66                cpu.registers().gbr.wrapping_add(displacement.resolve(size, cpu))
67            }
68            Self::PcRelative { mut pc, displacement } => {
69                pc = pc.wrapping_add(4);
70                if size == MemoryAccessSize::Longword {
71                    pc &= !3;
72                }
73                pc.wrapping_add(displacement.resolve(size, cpu))
74            }
75        }
76    }
77}
78
79pub struct DisassembledInstruction {
80    pub pc: u32,
81    pub text: String,
82    pub opcode: u16,
83    pub memory_read: Option<(MemoryAccess, MemoryAccessSize)>,
84    pub memory_read_type: ReadType,
85    pub memory_write: Option<(MemoryAccess, MemoryAccessSize)>,
86}
87
88impl Default for DisassembledInstruction {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl DisassembledInstruction {
95    #[must_use]
96    pub fn new() -> Self {
97        Self {
98            pc: 0,
99            text: String::new(),
100            opcode: 0,
101            memory_read: None,
102            memory_read_type: ReadType::Load,
103            memory_write: None,
104        }
105    }
106
107    pub fn clear(&mut self) {
108        self.text.clear();
109        self.memory_read = None;
110        self.memory_read_type = ReadType::Load;
111        self.memory_write = None;
112    }
113}
114
115pub fn disassemble_into(pc: u32, opcode: u16, out: &mut DisassembledInstruction) {
116    out.clear();
117
118    out.text = match opcode {
119        0b0000_0000_0001_1001 => "div0u".into(),
120        0b0000_0000_0000_1011 => "rts".into(),
121        0b0000_0000_0000_1000 => "clrt".into(),
122        0b0000_0000_0010_1000 => "clrmac".into(),
123        0b0000_0000_0000_1001 => "nop".into(),
124        0b0000_0000_0010_1011 => "rte".into(),
125        0b0000_0000_0001_1000 => "sett".into(),
126        0b0000_0000_0001_1011 => "sleep".into(),
127        _ => decode_xnnx(pc, opcode, out),
128    };
129}
130
131#[inline]
132fn decode_xnnx(pc: u32, opcode: u16, out: &mut DisassembledInstruction) -> String {
133    match opcode & 0b1111_0000_0000_1111 {
134        0b0110_0000_0000_0011 => {
135            format!("mov r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
136        }
137        0b0010_0000_0000_0000 => {
138            let dest = parse_register_high(opcode);
139            out.memory_write = Some((
140                MemoryAccess::IndirectR { register: dest, displacement: Displacement::none() },
141                MemoryAccessSize::Byte,
142            ));
143            format!("mov.b r{}, @r{}", parse_register_low(opcode), dest)
144        }
145        0b0010_0000_0000_0001 => {
146            let dest = parse_register_high(opcode);
147            out.memory_write = Some((
148                MemoryAccess::IndirectR { register: dest, displacement: Displacement::none() },
149                MemoryAccessSize::Word,
150            ));
151            format!("mov.w r{}, @r{}", parse_register_low(opcode), dest)
152        }
153        0b0010_0000_0000_0010 => {
154            let dest = parse_register_high(opcode);
155            out.memory_write = Some((
156                MemoryAccess::IndirectR { register: dest, displacement: Displacement::none() },
157                MemoryAccessSize::Longword,
158            ));
159            format!("mov.l r{}, @r{}", parse_register_low(opcode), dest)
160        }
161        0b0110_0000_0000_0000 => {
162            let source = parse_register_low(opcode);
163            out.memory_read = Some((
164                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
165                MemoryAccessSize::Byte,
166            ));
167            format!("mov.b @r{}, r{}", source, parse_register_high(opcode))
168        }
169        0b0110_0000_0000_0001 => {
170            let source = parse_register_low(opcode);
171            out.memory_read = Some((
172                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
173                MemoryAccessSize::Word,
174            ));
175            format!("mov.w @r{}, r{}", source, parse_register_high(opcode))
176        }
177        0b0110_0000_0000_0010 => {
178            let source = parse_register_low(opcode);
179            out.memory_read = Some((
180                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
181                MemoryAccessSize::Longword,
182            ));
183            format!("mov.l @r{}, r{}", source, parse_register_high(opcode))
184        }
185        0b0010_0000_0000_0100 => {
186            let dest = parse_register_high(opcode);
187            out.memory_write = Some((
188                MemoryAccess::IndirectRPredecrement { register: dest },
189                MemoryAccessSize::Byte,
190            ));
191            format!("mov.b r{}, @-r{}", parse_register_low(opcode), dest)
192        }
193        0b0010_0000_0000_0101 => {
194            let dest = parse_register_high(opcode);
195            out.memory_write = Some((
196                MemoryAccess::IndirectRPredecrement { register: dest },
197                MemoryAccessSize::Word,
198            ));
199            format!("mov.w r{}, @-r{}", parse_register_low(opcode), dest)
200        }
201        0b0010_0000_0000_0110 => {
202            let dest = parse_register_high(opcode);
203            out.memory_write = Some((
204                MemoryAccess::IndirectRPredecrement { register: dest },
205                MemoryAccessSize::Longword,
206            ));
207            format!("mov.l r{}, @-r{}", parse_register_low(opcode), dest)
208        }
209        0b0110_0000_0000_0100 => {
210            let source = parse_register_low(opcode);
211            out.memory_read = Some((
212                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
213                MemoryAccessSize::Byte,
214            ));
215            format!("mov.b @r{}+, r{}", source, parse_register_high(opcode))
216        }
217        0b0110_0000_0000_0101 => {
218            let source = parse_register_low(opcode);
219            out.memory_read = Some((
220                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
221                MemoryAccessSize::Word,
222            ));
223            format!("mov.w @r{}+, r{}", source, parse_register_high(opcode))
224        }
225        0b0110_0000_0000_0110 => {
226            let source = parse_register_low(opcode);
227            out.memory_read = Some((
228                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
229                MemoryAccessSize::Longword,
230            ));
231            format!("mov.l @r{}+, r{}", source, parse_register_high(opcode))
232        }
233        0b0000_0000_0000_0100 => {
234            let dest = parse_register_high(opcode);
235            out.memory_write = Some((
236                MemoryAccess::IndirectR { register: dest, displacement: Displacement::Register(0) },
237                MemoryAccessSize::Byte,
238            ));
239            format!("mov.b r{}, @(r0,r{})", parse_register_low(opcode), dest)
240        }
241        0b0000_0000_0000_0101 => {
242            let dest = parse_register_high(opcode);
243            out.memory_write = Some((
244                MemoryAccess::IndirectR { register: dest, displacement: Displacement::Register(0) },
245                MemoryAccessSize::Word,
246            ));
247            format!("mov.w r{}, @(r0,r{})", parse_register_low(opcode), dest)
248        }
249        0b0000_0000_0000_0110 => {
250            let dest = parse_register_high(opcode);
251            out.memory_write = Some((
252                MemoryAccess::IndirectR { register: dest, displacement: Displacement::Register(0) },
253                MemoryAccessSize::Longword,
254            ));
255            format!("mov.l r{}, @(r0,r{})", parse_register_low(opcode), dest)
256        }
257        0b0000_0000_0000_1100 => {
258            let source = parse_register_low(opcode);
259            out.memory_read = Some((
260                MemoryAccess::IndirectR {
261                    register: source,
262                    displacement: Displacement::Register(0),
263                },
264                MemoryAccessSize::Byte,
265            ));
266            format!("mov.b @(r0,r{}), r{}", source, parse_register_high(opcode))
267        }
268        0b0000_0000_0000_1101 => {
269            let source = parse_register_low(opcode);
270            out.memory_read = Some((
271                MemoryAccess::IndirectR {
272                    register: source,
273                    displacement: Displacement::Register(0),
274                },
275                MemoryAccessSize::Word,
276            ));
277            format!("mov.w @(r0,r{}), r{}", source, parse_register_high(opcode))
278        }
279        0b0000_0000_0000_1110 => {
280            let source = parse_register_low(opcode);
281            out.memory_read = Some((
282                MemoryAccess::IndirectR {
283                    register: source,
284                    displacement: Displacement::Register(0),
285                },
286                MemoryAccessSize::Longword,
287            ));
288            format!("mov.l @(r0,r{}), r{}", source, parse_register_high(opcode))
289        }
290        0b0110_0000_0000_1000 => {
291            format!("swap.b r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
292        }
293        0b0110_0000_0000_1001 => {
294            format!("swap.w r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
295        }
296        0b0010_0000_0000_1101 => {
297            format!("xtrct r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
298        }
299        0b0011_0000_0000_1100 => {
300            format!("add r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
301        }
302        0b0011_0000_0000_1110 => {
303            format!("addc r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
304        }
305        0b0011_0000_0000_1111 => {
306            format!("addv r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
307        }
308        0b0011_0000_0000_0000 => {
309            format!("cmp/eq r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
310        }
311        0b0011_0000_0000_0010 => {
312            format!("cmp/hs r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
313        }
314        0b0011_0000_0000_0011 => {
315            format!("cmp/ge r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
316        }
317        0b0011_0000_0000_0110 => {
318            format!("cmp/hi r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
319        }
320        0b0011_0000_0000_0111 => {
321            format!("cmp/gt r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
322        }
323        0b0010_0000_0000_1100 => {
324            format!("cmp/str r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
325        }
326        0b0011_0000_0000_0100 => {
327            format!("div1 r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
328        }
329        0b0010_0000_0000_0111 => {
330            format!("div0s r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
331        }
332        0b0011_0000_0000_1101 => {
333            format!("dmuls.l r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
334        }
335        0b0011_0000_0000_0101 => {
336            format!("dmulu.l r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
337        }
338        0b0110_0000_0000_1110 => {
339            format!("exts.b r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
340        }
341        0b0110_0000_0000_1111 => {
342            format!("exts.w r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
343        }
344        0b0110_0000_0000_1100 => {
345            format!("extu.b r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
346        }
347        0b0110_0000_0000_1101 => {
348            format!("extu.w r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
349        }
350        0b0000_0000_0000_1111 => {
351            let source = parse_register_low(opcode);
352            let dest = parse_register_high(opcode);
353            out.memory_read = Some((
354                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
355                MemoryAccessSize::Longword,
356            ));
357            out.memory_write = Some((
358                MemoryAccess::IndirectR { register: dest, displacement: Displacement::none() },
359                MemoryAccessSize::Longword,
360            ));
361            format!("mac.l @r{source}+, @r{dest}+")
362        }
363        0b0100_0000_0000_1111 => {
364            let source = parse_register_low(opcode);
365            let dest = parse_register_high(opcode);
366            out.memory_read = Some((
367                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
368                MemoryAccessSize::Word,
369            ));
370            out.memory_write = Some((
371                MemoryAccess::IndirectR { register: dest, displacement: Displacement::none() },
372                MemoryAccessSize::Word,
373            ));
374            format!("mac.w @r{source}+, @r{dest}+")
375        }
376        0b0000_0000_0000_0111 => {
377            format!("mul.l r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
378        }
379        0b0010_0000_0000_1111 => {
380            format!("muls.w r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
381        }
382        0b0010_0000_0000_1110 => {
383            format!("mulu.w r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
384        }
385        0b0110_0000_0000_1011 => {
386            format!("neg r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
387        }
388        0b0110_0000_0000_1010 => {
389            format!("negc r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
390        }
391        0b0011_0000_0000_1000 => {
392            format!("sub r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
393        }
394        0b0011_0000_0000_1010 => {
395            format!("subc r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
396        }
397        0b0011_0000_0000_1011 => {
398            format!("subv r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
399        }
400        0b0010_0000_0000_1001 => {
401            format!("and r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
402        }
403        0b0110_0000_0000_0111 => {
404            format!("not r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
405        }
406        0b0010_0000_0000_1011 => {
407            format!("or r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
408        }
409        0b0010_0000_0000_1000 => {
410            format!("tst r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
411        }
412        0b0010_0000_0000_1010 => {
413            format!("xor r{}, r{}", parse_register_low(opcode), parse_register_high(opcode))
414        }
415        _ => decode_xxnn(pc, opcode, out),
416    }
417}
418
419#[inline]
420fn decode_xxnn(pc: u32, opcode: u16, out: &mut DisassembledInstruction) -> String {
421    match opcode & 0b1111_1111_0000_0000 {
422        0b1000_0000_0000_0000 => {
423            let displacement: u32 = parse_4bit_displacement(opcode).into();
424            let dest = parse_register_low(opcode);
425            out.memory_write = Some((
426                MemoryAccess::IndirectR {
427                    register: dest,
428                    displacement: Displacement::Immediate(displacement),
429                },
430                MemoryAccessSize::Byte,
431            ));
432            format!("mov.b r0, @({displacement},r{dest})")
433        }
434        0b1000_0001_0000_0000 => {
435            let displacement: u32 = parse_4bit_displacement(opcode).into();
436            let dest = parse_register_low(opcode);
437            out.memory_write = Some((
438                MemoryAccess::IndirectR {
439                    register: dest,
440                    displacement: Displacement::Immediate(displacement),
441                },
442                MemoryAccessSize::Word,
443            ));
444            format!("mov.w r0, @({displacement},r{dest})")
445        }
446        0b1000_0100_0000_0000 => {
447            let displacement: u32 = parse_4bit_displacement(opcode).into();
448            let source = parse_register_low(opcode);
449            out.memory_read = Some((
450                MemoryAccess::IndirectR {
451                    register: source,
452                    displacement: Displacement::Immediate(displacement),
453                },
454                MemoryAccessSize::Byte,
455            ));
456            format!("mov.b @({displacement},r{source}), r0")
457        }
458        0b1000_0101_0000_0000 => {
459            let displacement: u32 = parse_4bit_displacement(opcode).into();
460            let source = parse_register_low(opcode);
461            out.memory_read = Some((
462                MemoryAccess::IndirectR {
463                    register: source,
464                    displacement: Displacement::Immediate(displacement),
465                },
466                MemoryAccessSize::Word,
467            ));
468            format!("mov.w @({displacement},r{source}), r0")
469        }
470        0b1100_0000_0000_0000 => {
471            let displacement: u32 = parse_8bit_displacement(opcode).into();
472            out.memory_write = Some((
473                MemoryAccess::IndirectGbr { displacement: Displacement::Immediate(displacement) },
474                MemoryAccessSize::Byte,
475            ));
476            format!("mov.b r0, @({displacement},gbr)")
477        }
478        0b1100_0001_0000_0000 => {
479            let displacement: u32 = parse_8bit_displacement(opcode).into();
480            out.memory_write = Some((
481                MemoryAccess::IndirectGbr { displacement: Displacement::Immediate(displacement) },
482                MemoryAccessSize::Word,
483            ));
484            format!("mov.w r0, @({displacement},gbr)")
485        }
486        0b1100_0010_0000_0000 => {
487            let displacement: u32 = parse_8bit_displacement(opcode).into();
488            out.memory_write = Some((
489                MemoryAccess::IndirectGbr { displacement: Displacement::Immediate(displacement) },
490                MemoryAccessSize::Longword,
491            ));
492            format!("mov.l r0, @({displacement},gbr)")
493        }
494        0b1100_0100_0000_0000 => {
495            let displacement: u32 = parse_8bit_displacement(opcode).into();
496            out.memory_read = Some((
497                MemoryAccess::IndirectGbr { displacement: Displacement::Immediate(displacement) },
498                MemoryAccessSize::Byte,
499            ));
500            format!("mov.b @({displacement},gbr), r0")
501        }
502        0b1100_0101_0000_0000 => {
503            let displacement: u32 = parse_8bit_displacement(opcode).into();
504            out.memory_read = Some((
505                MemoryAccess::IndirectGbr { displacement: Displacement::Immediate(displacement) },
506                MemoryAccessSize::Word,
507            ));
508            format!("mov.w @({displacement},gbr), r0")
509        }
510        0b1100_0110_0000_0000 => {
511            let displacement: u32 = parse_8bit_displacement(opcode).into();
512            out.memory_read = Some((
513                MemoryAccess::IndirectGbr { displacement: Displacement::Immediate(displacement) },
514                MemoryAccessSize::Longword,
515            ));
516            format!("mov.l @({displacement},gbr), r0")
517        }
518        0b1100_0111_0000_0000 => {
519            let displacement: u32 = parse_8bit_displacement(opcode).into();
520            out.memory_read = Some((
521                MemoryAccess::PcRelative {
522                    pc,
523                    displacement: Displacement::Immediate(displacement),
524                },
525                MemoryAccessSize::Longword,
526            ));
527            out.memory_read_type = ReadType::EffectiveAddress;
528            format!("mova @({displacement},pc), r0")
529        }
530        0b1000_1000_0000_0000 => format!("cmp/eq #{}, r0", parse_signed_immediate(opcode)),
531        0b1100_1001_0000_0000 => format!("and #{}, r0", parse_unsigned_immediate(opcode)),
532        0b1100_1101_0000_0000 => {
533            out.memory_read = Some((
534                MemoryAccess::IndirectGbr { displacement: Displacement::Register(0) },
535                MemoryAccessSize::Byte,
536            ));
537            out.memory_write = out.memory_read;
538            format!("and.b #{}, @(r0,gbr)", parse_unsigned_immediate(opcode))
539        }
540        0b1100_1011_0000_0000 => format!("or #{}, r0", parse_unsigned_immediate(opcode)),
541        0b1100_1111_0000_0000 => {
542            out.memory_read = Some((
543                MemoryAccess::IndirectGbr { displacement: Displacement::Register(0) },
544                MemoryAccessSize::Byte,
545            ));
546            out.memory_write = out.memory_read;
547            format!("or.b #{}, @(r0,gbr)", parse_unsigned_immediate(opcode))
548        }
549        0b1100_1000_0000_0000 => format!("tst #{}, r0", parse_unsigned_immediate(opcode)),
550        0b1100_1100_0000_0000 => {
551            out.memory_read = Some((
552                MemoryAccess::IndirectGbr { displacement: Displacement::Register(0) },
553                MemoryAccessSize::Byte,
554            ));
555            out.memory_write = out.memory_read;
556            format!("tst.b #{}, @(r0,gbr)", parse_unsigned_immediate(opcode))
557        }
558        0b1100_1010_0000_0000 => format!("xor #{}, r0", parse_unsigned_immediate(opcode)),
559        0b1100_1110_0000_0000 => {
560            out.memory_read = Some((
561                MemoryAccess::IndirectGbr { displacement: Displacement::Register(0) },
562                MemoryAccessSize::Byte,
563            ));
564            out.memory_write = out.memory_read;
565            format!("xor.b #{}, @(r0,gbr)", parse_unsigned_immediate(opcode))
566        }
567        0b1000_1011_0000_0000 => {
568            format!("bf {}", branch_destination(pc, parse_signed_immediate(opcode) as u32,))
569        }
570        0b1000_1111_0000_0000 => {
571            format!("bf/s {}", branch_destination(pc, parse_signed_immediate(opcode) as u32,))
572        }
573        0b1000_1001_0000_0000 => {
574            format!("bt {}", branch_destination(pc, parse_signed_immediate(opcode) as u32,))
575        }
576        0b1000_1101_0000_0000 => {
577            format!("bt/s {}", branch_destination(pc, parse_signed_immediate(opcode) as u32,))
578        }
579        0b1100_0011_0000_0000 => format!("trapa #{}", opcode & 0xFF),
580        _ => decode_xnxx(pc, opcode, out),
581    }
582}
583
584#[inline]
585fn decode_xnxx(pc: u32, opcode: u16, out: &mut DisassembledInstruction) -> String {
586    match opcode & 0b1111_0000_1111_1111 {
587        0b0000_0000_0010_1001 => format!("movt r{}", parse_register_high(opcode)),
588        0b0100_0000_0001_0001 => format!("cmp/pz r{}", parse_register_high(opcode)),
589        0b0100_0000_0001_0101 => format!("cmp/pl r{}", parse_register_high(opcode)),
590        0b0100_0000_0001_0000 => format!("dt r{}", parse_register_high(opcode)),
591        0b0100_0000_0001_1011 => {
592            let register = parse_register_high(opcode);
593            out.memory_read = Some((
594                MemoryAccess::IndirectR { register, displacement: Displacement::none() },
595                MemoryAccessSize::Byte,
596            ));
597            out.memory_write = out.memory_read;
598            format!("tas.b @r{register}")
599        }
600        0b0100_0000_0000_0100 => format!("rotl r{}", parse_register_high(opcode)),
601        0b0100_0000_0000_0101 => format!("rotr r{}", parse_register_high(opcode)),
602        0b0100_0000_0010_0100 => format!("rotcl r{}", parse_register_high(opcode)),
603        0b0100_0000_0010_0101 => format!("rotcr r{}", parse_register_high(opcode)),
604        0b0100_0000_0010_0000 => format!("shal r{}", parse_register_high(opcode)),
605        0b0100_0000_0010_0001 => format!("shar r{}", parse_register_high(opcode)),
606        0b0100_0000_0000_0000 => format!("shll r{}", parse_register_high(opcode)),
607        0b0100_0000_0000_0001 => format!("shlr r{}", parse_register_high(opcode)),
608        0b0100_0000_0000_1000 => format!("shll2 r{}", parse_register_high(opcode)),
609        0b0100_0000_0000_1001 => format!("shlr2 r{}", parse_register_high(opcode)),
610        0b0100_0000_0001_1000 => format!("shll8 r{}", parse_register_high(opcode)),
611        0b0100_0000_0001_1001 => format!("shlr8 r{}", parse_register_high(opcode)),
612        0b0100_0000_0010_1000 => format!("shll16 r{}", parse_register_high(opcode)),
613        0b0100_0000_0010_1001 => format!("shlr16 r{}", parse_register_high(opcode)),
614        0b0000_0000_0010_0011 => {
615            let register = parse_register_high(opcode);
616            out.memory_read = Some((
617                MemoryAccess::PcRelative { pc, displacement: Displacement::Register(register) },
618                MemoryAccessSize::Word,
619            ));
620            out.memory_read_type = ReadType::EffectiveAddress;
621            format!("braf r{register}")
622        }
623        0b0000_0000_0000_0011 => {
624            let register = parse_register_high(opcode);
625            out.memory_read = Some((
626                MemoryAccess::PcRelative { pc, displacement: Displacement::Register(register) },
627                MemoryAccessSize::Word,
628            ));
629            out.memory_read_type = ReadType::EffectiveAddress;
630            format!("bsrf r{register}")
631        }
632        0b0100_0000_0010_1011 => {
633            let register = parse_register_high(opcode);
634            out.memory_read = Some((
635                MemoryAccess::IndirectR { register, displacement: Displacement::none() },
636                MemoryAccessSize::Longword,
637            ));
638            out.memory_read_type = ReadType::EffectiveAddress;
639            format!("jmp @r{register}")
640        }
641        0b0100_0000_0000_1011 => {
642            let register = parse_register_high(opcode);
643            out.memory_read = Some((
644                MemoryAccess::IndirectR { register, displacement: Displacement::none() },
645                MemoryAccessSize::Longword,
646            ));
647            out.memory_read_type = ReadType::EffectiveAddress;
648            format!("jsr @r{register}")
649        }
650        0b0100_0000_0000_1110 => format!("ldc r{}, sr", parse_register_high(opcode)),
651        0b0100_0000_0001_1110 => format!("ldc r{}, gbr", parse_register_high(opcode)),
652        0b0100_0000_0010_1110 => format!("ldc r{}, vbr", parse_register_high(opcode)),
653        0b0100_0000_0000_0111 => {
654            let source = parse_register_high(opcode);
655            out.memory_read = Some((
656                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
657                MemoryAccessSize::Longword,
658            ));
659            format!("ldc.l @r{source}+, sr")
660        }
661        0b0100_0000_0001_0111 => {
662            let source = parse_register_high(opcode);
663            out.memory_read = Some((
664                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
665                MemoryAccessSize::Longword,
666            ));
667            format!("ldc.l @r{source}+, gbr")
668        }
669        0b0100_0000_0010_0111 => {
670            let source = parse_register_high(opcode);
671            out.memory_read = Some((
672                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
673                MemoryAccessSize::Longword,
674            ));
675            format!("ldc.l @r{source}+, vbr")
676        }
677        0b0100_0000_0000_1010 => format!("lds r{}, mach", parse_register_high(opcode)),
678        0b0100_0000_0001_1010 => format!("lds r{}, macl", parse_register_high(opcode)),
679        0b0100_0000_0010_1010 => format!("lds r{}, pr", parse_register_high(opcode)),
680        0b0100_0000_0000_0110 => {
681            let source = parse_register_high(opcode);
682            out.memory_read = Some((
683                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
684                MemoryAccessSize::Longword,
685            ));
686            format!("lds.l @r{source}+, mach")
687        }
688        0b0100_0000_0001_0110 => {
689            let source = parse_register_high(opcode);
690            out.memory_read = Some((
691                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
692                MemoryAccessSize::Longword,
693            ));
694            format!("lds.l @r{source}+, macl")
695        }
696        0b0100_0000_0010_0110 => {
697            let source = parse_register_high(opcode);
698            out.memory_read = Some((
699                MemoryAccess::IndirectR { register: source, displacement: Displacement::none() },
700                MemoryAccessSize::Longword,
701            ));
702            format!("lds.l @r{source}+, pr")
703        }
704        0b0000_0000_0000_0010 => format!("stc sr, r{}", parse_register_high(opcode)),
705        0b0000_0000_0001_0010 => format!("stc gbr, r{}", parse_register_high(opcode)),
706        0b0000_0000_0010_0010 => format!("stc vbr, r{}", parse_register_high(opcode)),
707        0b0100_0000_0000_0011 => {
708            let dest = parse_register_high(opcode);
709            out.memory_write = Some((
710                MemoryAccess::IndirectRPredecrement { register: dest },
711                MemoryAccessSize::Longword,
712            ));
713            format!("stc.l sr, @-r{dest}")
714        }
715        0b0100_0000_0001_0011 => {
716            let dest = parse_register_high(opcode);
717            out.memory_write = Some((
718                MemoryAccess::IndirectRPredecrement { register: dest },
719                MemoryAccessSize::Longword,
720            ));
721            format!("stc.l gbr, @-r{dest}")
722        }
723        0b0100_0000_0010_0011 => {
724            let dest = parse_register_high(opcode);
725            out.memory_write = Some((
726                MemoryAccess::IndirectRPredecrement { register: dest },
727                MemoryAccessSize::Longword,
728            ));
729            format!("stc.l vbr, @-r{dest}")
730        }
731        0b0000_0000_0000_1010 => format!("sts mach, r{}", parse_register_high(opcode)),
732        0b0000_0000_0001_1010 => format!("sts macl, r{}", parse_register_high(opcode)),
733        0b0000_0000_0010_1010 => format!("sts pr, r{}", parse_register_high(opcode)),
734        0b0100_0000_0000_0010 => {
735            let dest = parse_register_high(opcode);
736            out.memory_write = Some((
737                MemoryAccess::IndirectRPredecrement { register: dest },
738                MemoryAccessSize::Longword,
739            ));
740            format!("sts.l mach, @-r{dest}")
741        }
742        0b0100_0000_0001_0010 => {
743            let dest = parse_register_high(opcode);
744            out.memory_write = Some((
745                MemoryAccess::IndirectRPredecrement { register: dest },
746                MemoryAccessSize::Longword,
747            ));
748            format!("sts.l macl, @-r{dest}")
749        }
750        0b0100_0000_0010_0010 => {
751            let dest = parse_register_high(opcode);
752            out.memory_write = Some((
753                MemoryAccess::IndirectRPredecrement { register: dest },
754                MemoryAccessSize::Longword,
755            ));
756            format!("sts.l pr, @-r{dest}")
757        }
758        _ => decode_xnnn(pc, opcode, out),
759    }
760}
761
762#[inline]
763fn decode_xnnn(pc: u32, opcode: u16, out: &mut DisassembledInstruction) -> String {
764    match opcode & 0b1111_0000_0000_0000 {
765        0b1110_0000_0000_0000 => {
766            format!("mov.b #{}, r{}", parse_signed_immediate(opcode), parse_register_high(opcode))
767        }
768        0b1001_0000_0000_0000 => {
769            let displacement: u32 = parse_8bit_displacement(opcode).into();
770            out.memory_read = Some((
771                MemoryAccess::PcRelative {
772                    pc,
773                    displacement: Displacement::Immediate(displacement),
774                },
775                MemoryAccessSize::Word,
776            ));
777            format!("mov.w @({},pc), r{}", displacement, parse_register_high(opcode))
778        }
779        0b1101_0000_0000_0000 => {
780            let displacement: u32 = parse_8bit_displacement(opcode).into();
781            out.memory_read = Some((
782                MemoryAccess::PcRelative {
783                    pc,
784                    displacement: Displacement::Immediate(displacement),
785                },
786                MemoryAccessSize::Longword,
787            ));
788            format!(
789                "mov.l @({},pc), r{}",
790                parse_8bit_displacement(opcode),
791                parse_register_high(opcode)
792            )
793        }
794        0b0001_0000_0000_0000 => {
795            let displacement: u32 = parse_4bit_displacement(opcode).into();
796            let dest = parse_register_high(opcode);
797            out.memory_write = Some((
798                MemoryAccess::IndirectR {
799                    register: dest,
800                    displacement: Displacement::Immediate(displacement),
801                },
802                MemoryAccessSize::Longword,
803            ));
804            format!("mov.l r{}, @({},r{})", parse_register_low(opcode), displacement, dest)
805        }
806        0b0101_0000_0000_0000 => {
807            let displacement: u32 = parse_4bit_displacement(opcode).into();
808            let source = parse_register_high(opcode);
809            out.memory_read = Some((
810                MemoryAccess::IndirectR {
811                    register: source,
812                    displacement: Displacement::Immediate(displacement),
813                },
814                MemoryAccessSize::Longword,
815            ));
816            format!("mov.l @({},r{}), r{}", displacement, source, parse_register_high(opcode))
817        }
818        0b0111_0000_0000_0000 => {
819            format!("add #{}, r{}", parse_signed_immediate(opcode), parse_register_high(opcode))
820        }
821        0b1010_0000_0000_0000 => {
822            format!("bra {}", branch_destination(pc, parse_12bit_displacement(opcode) as u32))
823        }
824        0b1011_0000_0000_0000 => {
825            format!("bsr {}", branch_destination(pc, parse_12bit_displacement(opcode) as u32))
826        }
827        _ => "illegal".into(),
828    }
829}
830
831#[inline]
832fn parse_4bit_displacement(opcode: u16) -> u16 {
833    opcode & 0xF
834}
835
836#[inline]
837fn parse_8bit_displacement(opcode: u16) -> u16 {
838    opcode & 0xFF
839}
840
841#[inline]
842fn parse_12bit_displacement(opcode: u16) -> i32 {
843    (((opcode as i16) << 4) >> 4).into()
844}
845
846#[inline]
847fn parse_signed_immediate(opcode: u16) -> i8 {
848    opcode as i8
849}
850
851#[inline]
852fn parse_unsigned_immediate(opcode: u16) -> u8 {
853    opcode as u8
854}
855
856#[inline]
857fn parse_register_low(opcode: u16) -> u16 {
858    (opcode >> 4) & 0xF
859}
860
861#[inline]
862fn parse_register_high(opcode: u16) -> u16 {
863    (opcode >> 8) & 0xF
864}
865
866#[inline]
867fn branch_destination(pc: u32, displacement: u32) -> String {
868    let address = pc.wrapping_add(4).wrapping_add(displacement << 1);
869    format!("${address:08X}")
870}