Skip to main content

objdiff_core/arch/superh/
mod.rs

1use alloc::{collections::BTreeMap, format, vec, vec::Vec};
2
3use anyhow::Result;
4use object::elf;
5
6use crate::{
7    arch::{Arch, superh::disasm::sh2_disasm},
8    diff::{DiffObjConfig, display::InstructionPart},
9    obj::{InstructionRef, Relocation, RelocationFlags, ResolvedInstructionRef},
10};
11
12pub mod disasm;
13
14#[derive(Debug)]
15pub struct ArchSuperH {}
16
17impl ArchSuperH {
18    pub fn new(_file: &object::File) -> Result<Self> { Ok(Self {}) }
19}
20
21struct DataInfo {
22    address: u64,
23    size: u32,
24}
25
26impl Arch for ArchSuperH {
27    fn scan_instructions_internal(
28        &self,
29        address: u64,
30        code: &[u8],
31        _section_index: usize,
32        _relocations: &[Relocation],
33        _diff_config: &DiffObjConfig,
34    ) -> Result<Vec<InstructionRef>> {
35        let mut ops = Vec::<InstructionRef>::with_capacity(code.len() / 2);
36        let mut offset = address;
37
38        for chunk in code.as_chunks::<2>().0 {
39            let opcode = u16::from_be_bytes(*chunk);
40            let mut parts: Vec<InstructionPart> = vec![];
41            let resolved: ResolvedInstructionRef = Default::default();
42            let mut branch_dest: Option<u64> = None;
43            sh2_disasm(
44                offset.try_into().unwrap(),
45                opcode,
46                true,
47                &mut parts,
48                &resolved,
49                &mut branch_dest,
50            );
51
52            let opcode_enum: u16 = match parts.first() {
53                Some(InstructionPart::Opcode(_, val)) => *val,
54                _ => 0,
55            };
56            ops.push(InstructionRef { address: offset, size: 2, opcode: opcode_enum, branch_dest });
57            offset += 2;
58        }
59
60        Ok(ops)
61    }
62
63    fn display_instruction(
64        &self,
65        resolved: ResolvedInstructionRef,
66        _diff_config: &DiffObjConfig,
67        cb: &mut dyn FnMut(InstructionPart) -> Result<()>,
68    ) -> Result<()> {
69        let opcode = u16::from_be_bytes(resolved.code.try_into().unwrap());
70        let mut parts: Vec<InstructionPart> = vec![];
71        let mut branch_dest: Option<u64> = None;
72
73        sh2_disasm(0, opcode, true, &mut parts, &resolved, &mut branch_dest);
74
75        if let Some(symbol_data) =
76            resolved.section.data_range(resolved.symbol.address, resolved.symbol.size as usize)
77        {
78            // scan for data
79            // map of instruction offsets to data target offsets
80            let mut data_offsets = BTreeMap::<u64, DataInfo>::new();
81
82            let mut pos: u64 = 0;
83            for chunk in symbol_data.as_chunks::<2>().0 {
84                let opcode = u16::from_be_bytes(*chunk);
85                // mov.w
86                if (opcode & 0xf000) == 0x9000 {
87                    let target = (opcode as u64 & 0xff) * 2 + 4 + pos;
88                    let data_info = DataInfo { address: target, size: 2 };
89                    data_offsets.insert(pos, data_info);
90                }
91                // mov.l
92                else if (opcode & 0xf000) == 0xd000 {
93                    let target = ((opcode as u64 & 0xff) * 4 + 4 + pos) & 0xfffffffc;
94                    let data_info = DataInfo { address: target, size: 4 };
95                    data_offsets.insert(pos, data_info);
96                }
97                pos += 2;
98            }
99
100            let pos = resolved.ins_ref.address - resolved.symbol.address;
101
102            // add the data info
103            if let Some(value) = data_offsets.get(&pos) {
104                if value.size == 2 && value.address as usize + 1 < symbol_data.len() {
105                    let data = u16::from_be_bytes(
106                        symbol_data[value.address as usize..value.address as usize + 2]
107                            .try_into()
108                            .unwrap(),
109                    );
110                    parts.push(InstructionPart::basic(" /* "));
111                    parts.push(InstructionPart::basic("0x"));
112                    parts.push(InstructionPart::basic(format!("{data:04X}")));
113                    parts.push(InstructionPart::basic(" */"));
114                } else if value.size == 4 && value.address as usize + 3 < symbol_data.len() {
115                    let data = u32::from_be_bytes(
116                        symbol_data[value.address as usize..value.address as usize + 4]
117                            .try_into()
118                            .unwrap(),
119                    );
120                    parts.push(InstructionPart::basic(" /* "));
121                    parts.push(InstructionPart::basic("0x"));
122                    parts.push(InstructionPart::basic(format!("{data:08X}")));
123                    parts.push(InstructionPart::basic(" */"));
124                }
125            }
126        }
127
128        for part in parts {
129            cb(part)?;
130        }
131
132        Ok(())
133    }
134
135    fn reloc_name(&self, flags: RelocationFlags) -> Option<&'static str> {
136        match flags {
137            RelocationFlags::Elf(r_type) => match r_type {
138                elf::R_SH_NONE => Some("R_SH_NONE"),
139                elf::R_SH_DIR32 => Some("R_SH_DIR32"),
140                elf::R_SH_REL32 => Some("R_SH_REL32"),
141                elf::R_SH_DIR8WPN => Some("R_SH_DIR8WPN"),
142                elf::R_SH_IND12W => Some("R_SH_IND12W"),
143                elf::R_SH_DIR8WPL => Some("R_SH_DIR8WPL"),
144                elf::R_SH_DIR8WPZ => Some("R_SH_DIR8WPZ"),
145                elf::R_SH_DIR8BP => Some("R_SH_DIR8BP"),
146                elf::R_SH_DIR8W => Some("R_SH_DIR8W"),
147                elf::R_SH_DIR8L => Some("R_SH_DIR8L"),
148                elf::R_SH_SWITCH16 => Some("R_SH_SWITCH16"),
149                elf::R_SH_SWITCH32 => Some("R_SH_SWITCH32"),
150                elf::R_SH_USES => Some("R_SH_USES"),
151                elf::R_SH_COUNT => Some("R_SH_COUNT"),
152                elf::R_SH_ALIGN => Some("R_SH_ALIGN"),
153                elf::R_SH_CODE => Some("R_SH_CODE"),
154                elf::R_SH_DATA => Some("R_SH_DATA"),
155                elf::R_SH_LABEL => Some("R_SH_LABEL"),
156                elf::R_SH_SWITCH8 => Some("R_SH_SWITCH8"),
157                elf::R_SH_GNU_VTINHERIT => Some("R_SH_GNU_VTINHERIT"),
158                elf::R_SH_GNU_VTENTRY => Some("R_SH_GNU_VTENTRY"),
159                elf::R_SH_TLS_GD_32 => Some("R_SH_TLS_GD_32"),
160                elf::R_SH_TLS_LD_32 => Some("R_SH_TLS_LD_32"),
161                elf::R_SH_TLS_LDO_32 => Some("R_SH_TLS_LDO_32"),
162                elf::R_SH_TLS_IE_32 => Some("R_SH_TLS_IE_32"),
163                elf::R_SH_TLS_LE_32 => Some("R_SH_TLS_LE_32"),
164                elf::R_SH_TLS_DTPMOD32 => Some("R_SH_TLS_DTPMOD32"),
165                elf::R_SH_TLS_DTPOFF32 => Some("R_SH_TLS_DTPOFF32"),
166                elf::R_SH_TLS_TPOFF32 => Some("R_SH_TLS_TPOFF32"),
167                elf::R_SH_GOT32 => Some("R_SH_GOT32"),
168                elf::R_SH_PLT32 => Some("R_SH_PLT32"),
169                elf::R_SH_COPY => Some("R_SH_COPY"),
170                elf::R_SH_GLOB_DAT => Some("R_SH_GLOB_DAT"),
171                elf::R_SH_JMP_SLOT => Some("R_SH_JMP_SLOT"),
172                elf::R_SH_RELATIVE => Some("R_SH_RELATIVE"),
173                elf::R_SH_GOTOFF => Some("R_SH_GOTOFF"),
174                elf::R_SH_GOTPC => Some("R_SH_GOTPC"),
175                _ => None,
176            },
177            _ => None,
178        }
179    }
180
181    fn data_reloc_size(&self, flags: RelocationFlags) -> usize {
182        match flags {
183            RelocationFlags::Elf(elf::R_SH_DIR32) => 4,
184            RelocationFlags::Elf(_) => 1,
185            _ => 1,
186        }
187    }
188}
189
190#[cfg(test)]
191mod test {
192    use std::fmt::{self, Display};
193
194    use super::*;
195    use crate::obj::{InstructionArg, Section, SectionData, Symbol};
196
197    impl Display for InstructionPart<'_> {
198        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199            match self {
200                InstructionPart::Basic(s) => f.write_str(s),
201                InstructionPart::Opcode(s, _o) => write!(f, "{s} "),
202                InstructionPart::Arg(arg) => write!(f, "{arg}"),
203                InstructionPart::Separator => f.write_str(", "),
204            }
205        }
206    }
207
208    impl Display for InstructionArg<'_> {
209        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210            match self {
211                InstructionArg::Value(v) => write!(f, "{v}"),
212                InstructionArg::BranchDest(v) => write!(f, "{v}"),
213                InstructionArg::Reloc => f.write_str("reloc"),
214            }
215        }
216    }
217
218    #[test]
219    fn test_sh2_display_instruction_basic_ops() {
220        let arch = ArchSuperH {};
221        let ops: [(u16, &str); 8] = [
222            (0x0008, "clrt "),
223            (0x0028, "clrmac "),
224            (0x0019, "div0u "),
225            (0x0009, "nop "),
226            (0x002b, "rte "),
227            (0x000b, "rts "),
228            (0x0018, "sett "),
229            (0x001b, "sleep "),
230        ];
231
232        for (opcode, expected_str) in ops {
233            let code = opcode.to_be_bytes();
234            let mut parts = Vec::new();
235
236            arch.display_instruction(
237                ResolvedInstructionRef {
238                    ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
239                    code: &code,
240                    ..Default::default()
241                },
242                &DiffObjConfig::default(),
243                &mut |part| {
244                    parts.push(part.into_static());
245                    Ok(())
246                },
247            )
248            .unwrap();
249
250            let joined_str: String = parts.iter().map(<_>::to_string).collect();
251            assert_eq!(joined_str, expected_str.to_string());
252        }
253    }
254
255    #[test]
256    fn test_sh2_display_instruction_f0ff_ops() {
257        let arch = ArchSuperH {};
258        let ops: [(u16, &str); 49] = [
259            (0x4015, "cmp/pl r0"),
260            (0x4115, "cmp/pl r1"),
261            (0x4215, "cmp/pl r2"),
262            (0x4315, "cmp/pl r3"),
263            (0x4011, "cmp/pz r0"),
264            (0x4010, "dt r0"),
265            (0x0029, "movt r0"),
266            (0x4004, "rotl r0"),
267            (0x4005, "rotr r0"),
268            (0x4024, "rotcl r0"),
269            (0x4025, "rotcr r0"),
270            (0x4020, "shal r0"),
271            (0x4021, "shar r0"),
272            (0x4000, "shll r0"),
273            (0x4001, "shlr r0"),
274            (0x4008, "shll2 r0"),
275            (0x4009, "shlr2 r0"),
276            (0x4018, "shll8 r0"),
277            (0x4019, "shlr8 r0"),
278            (0x4028, "shll16 r0"),
279            (0x4029, "shlr16 r0"),
280            (0x0002, "stc sr, r0"),
281            (0x0012, "stc gbr, r0"),
282            (0x0022, "stc vbr, r0"),
283            (0x000a, "sts mach, r0"),
284            (0x001a, "sts macl, r0"),
285            (0x402a, "lds r0, pr"),
286            (0x401b, "tas.b r0"),
287            (0x4003, "stc.l sr, @-r0"),
288            (0x4013, "stc.l gbr, @-r0"),
289            (0x4023, "stc.l vbr, @-r0"),
290            (0x4002, "sts.l mach, @-r0"),
291            (0x4012, "sts.l macl, @-r0"),
292            (0x4022, "sts.l pr, @-r0"),
293            (0x400e, "ldc r0, sr"),
294            (0x401e, "ldc r0, gbr"),
295            (0x402e, "ldc r0, vbr"),
296            (0x400a, "lds r0, mach"),
297            (0x401a, "lds r0, macl"),
298            (0x402b, "jmp @r0"),
299            (0x400b, "jsr @r0"),
300            (0x4007, "ldc.l @r0+, sr"),
301            (0x4017, "ldc.l @r0+, gbr"),
302            (0x4027, "ldc.l @r0+, vbr"),
303            (0x4006, "lds.l @r0+, mach"),
304            (0x4016, "lds.l @r0+, macl"),
305            (0x4026, "lds.l @r0+, pr"),
306            (0x0023, "braf r0"),
307            (0x0003, "bsrf r0"),
308        ];
309
310        for (opcode, expected_str) in ops {
311            let code = opcode.to_be_bytes();
312            let mut parts = Vec::new();
313
314            arch.display_instruction(
315                ResolvedInstructionRef {
316                    ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
317                    code: &code,
318                    ..Default::default()
319                },
320                &DiffObjConfig::default(),
321                &mut |part| {
322                    parts.push(part.into_static());
323                    Ok(())
324                },
325            )
326            .unwrap();
327
328            let joined_str: String = parts.iter().map(<_>::to_string).collect();
329            assert_eq!(joined_str, expected_str.to_string());
330        }
331    }
332
333    #[test]
334    fn test_sh2_display_instructions_f00f() {
335        let arch = ArchSuperH {};
336        let ops: [(u16, &str); 54] = [
337            (0x300c, "add r0, r0"),
338            (0x300e, "addc r0, r0"),
339            (0x300f, "addv r0, r0"),
340            (0x2009, "and r0, r0"),
341            (0x3000, "cmp/eq r0, r0"),
342            (0x3002, "cmp/hs r0, r0"),
343            (0x3003, "cmp/ge r0, r0"),
344            (0x3006, "cmp/hi r0, r0"),
345            (0x3007, "cmp/gt r0, r0"),
346            (0x200c, "cmp/str r0, r0"),
347            (0x3004, "div1 r0, r0"),
348            (0x2007, "div0s r0, r0"),
349            (0x300d, "dmuls.l r0, r0"),
350            (0x3005, "dmulu.l r0, r0"),
351            (0x600e, "exts.b r0, r0"),
352            (0x600f, "exts.w r0, r0"),
353            (0x600c, "extu.b r0, r0"),
354            (0x600d, "extu.w r0, r0"),
355            (0x6003, "mov r0, r0"),
356            (0x0007, "mul.l r0, r0"),
357            (0x200f, "muls r0, r0"),
358            (0x200e, "mulu r0, r0"),
359            (0x600b, "neg r0, r0"),
360            (0x600a, "negc r0, r0"),
361            (0x6007, "not r0, r0"),
362            (0x200b, "or r0, r0"),
363            (0x3008, "sub r0, r0"),
364            (0x300a, "subc r0, r0"),
365            (0x300b, "subv r0, r0"),
366            (0x6008, "swap.b r0, r0"),
367            (0x6009, "swap.w r0, r0"),
368            (0x2008, "tst r0, r0"),
369            (0x200a, "xor r0, r0"),
370            (0x200d, "xtrct r0, r0"),
371            (0x2000, "mov.b r0, @r0"),
372            (0x2001, "mov.w r0, @r0"),
373            (0x2002, "mov.l r0, @r0"),
374            (0x6000, "mov.b @r0, r0"),
375            (0x6001, "mov.w @r0, r0"),
376            (0x6002, "mov.l @r0, r0"),
377            (0x000f, "mac.l @r0+, @r0+"),
378            (0x400f, "mac.w @r0+, @r0+"),
379            (0x6004, "mov.b @r0+, r0"),
380            (0x6005, "mov.w @r0+, r0"),
381            (0x6006, "mov.l @r0+, r0"),
382            (0x2004, "mov.b r0, @-r0"),
383            (0x2005, "mov.w r0, @-r0"),
384            (0x2006, "mov.l r0, @-r0"),
385            (0x0004, "mov.b r0, @(r0, r0)"),
386            (0x0005, "mov.w r0, @(r0, r0)"),
387            (0x0006, "mov.l r0, @(r0, r0)"),
388            (0x000c, "mov.b @(r0, r0), r0"),
389            (0x000d, "mov.w @(r0, r0), r0"),
390            (0x000e, "mov.l @(r0, r0), r0"),
391        ];
392
393        for (opcode, expected_str) in ops {
394            let code = opcode.to_be_bytes();
395            let mut parts = Vec::new();
396
397            arch.display_instruction(
398                ResolvedInstructionRef {
399                    ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
400                    code: &code,
401                    ..Default::default()
402                },
403                &DiffObjConfig::default(),
404                &mut |part| {
405                    parts.push(part.into_static());
406                    Ok(())
407                },
408            )
409            .unwrap();
410
411            let joined_str: String = parts.iter().map(<_>::to_string).collect();
412            assert_eq!(joined_str, expected_str.to_string());
413        }
414    }
415
416    #[test]
417    fn test_sh2_display_instruction_mov_immediate_offset() {
418        let arch = ArchSuperH {};
419        let ops: [(u16, &str); 8] = [
420            (0x8000, "mov.b r0, @(0x0, r0)"),
421            (0x8011, "mov.b r0, @(0x1, r1)"),
422            (0x8102, "mov.w r0, @(0x4, r0)"),
423            (0x8113, "mov.w r0, @(0x6, r1)"),
424            (0x8404, "mov.b @(0x4, r0), r0"),
425            (0x8415, "mov.b @(0x5, r1), r0"),
426            (0x8506, "mov.w @(0xc, r0), r0"),
427            (0x8517, "mov.w @(0xe, r1), r0"),
428        ];
429
430        for (opcode, expected_str) in ops {
431            let code = opcode.to_be_bytes();
432            let mut parts = Vec::new();
433
434            arch.display_instruction(
435                ResolvedInstructionRef {
436                    ins_ref: InstructionRef { address: 0x1000, size: 2, opcode, branch_dest: None },
437                    code: &code,
438                    ..Default::default()
439                },
440                &DiffObjConfig::default(),
441                &mut |part| {
442                    parts.push(part.into_static());
443                    Ok(())
444                },
445            )
446            .unwrap();
447
448            let joined_str: String = parts.iter().map(<_>::to_string).collect();
449            assert_eq!(joined_str, expected_str.to_string());
450        }
451    }
452
453    #[test]
454    fn test_sh2_display_instruction_gbr_and_branches() {
455        let arch = ArchSuperH {};
456        let ops: &[(u16, u32, &str)] = &[
457            (0xc000, 0x0000, "mov.b r0, @(0x0, gbr)"),
458            (0xc07f, 0x0000, "mov.b r0, @(0x7f, gbr)"),
459            (0xc100, 0x0000, "mov.w r0, @(0x0, gbr)"),
460            (0xc17f, 0x0000, "mov.w r0, @(0xfe, gbr)"),
461            (0xc200, 0x0000, "mov.l r0, @(0x0, gbr)"),
462            (0xc27f, 0x0000, "mov.l r0, @(0x1fc, gbr)"),
463            (0xc400, 0x0000, "mov.b @(0x0, gbr), r0"),
464            (0xc47f, 0x0000, "mov.b @(0x7f, gbr), r0"),
465            (0xc500, 0x0000, "mov.w @(0x0, gbr), r0"),
466            (0xc57f, 0x0000, "mov.w @(0xfe, gbr), r0"),
467            (0xc600, 0x0000, "mov.l @(0x0, gbr), r0"),
468            (0xc67f, 0x0000, "mov.l @(0x1fc, gbr), r0"),
469            (0x8b20, 0x1000, "bf 0x44"),
470            (0x8b80, 0x1000, "bf 0xffffff04"),
471            (0x8f10, 0x2000, "bf.s 0x24"),
472            (0x8f90, 0x2000, "bf.s 0xffffff24"),
473            (0x8904, 0x3000, "bt 0xc"),
474            (0x8980, 0x3000, "bt 0xffffff04"),
475            (0x8d04, 0x4000, "bt.s 0xc"),
476            (0x8d80, 0x4000, "bt.s 0xffffff04"),
477        ];
478
479        for &(opcode, addr, expected_str) in ops {
480            let code = opcode.to_be_bytes();
481            let mut parts = Vec::new();
482
483            arch.display_instruction(
484                ResolvedInstructionRef {
485                    ins_ref: InstructionRef {
486                        address: addr as u64,
487                        size: 2,
488                        opcode,
489                        branch_dest: None,
490                    },
491                    code: &code,
492                    ..Default::default()
493                },
494                &DiffObjConfig::default(),
495                &mut |part| {
496                    parts.push(part.into_static());
497                    Ok(())
498                },
499            )
500            .unwrap();
501
502            let joined_str: String = parts.iter().map(<_>::to_string).collect();
503            assert_eq!(joined_str, expected_str.to_string());
504        }
505    }
506
507    #[test]
508    fn test_sh2_display_instruction_mov_l() {
509        let arch = ArchSuperH {};
510        let ops: &[(u16, u32, &str)] = &[
511            // mov.l rX, @(0xXXX, rY)
512            (0x1000, 0x0000, "mov.l r0, @(0x0, r0)"),
513            (0x1001, 0x0000, "mov.l r0, @(0x4, r0)"),
514            (0x100f, 0x0000, "mov.l r0, @(0x3c, r0)"),
515            (0x101f, 0x0000, "mov.l r1, @(0x3c, r0)"),
516            // mov.l @(0xXXX, rY), rX
517            (0x5000, 0x0000, "mov.l @(0x0, r0), r0"),
518        ];
519
520        for &(opcode, addr, expected_str) in ops {
521            let code = opcode.to_be_bytes();
522            let mut parts = Vec::new();
523
524            arch.display_instruction(
525                ResolvedInstructionRef {
526                    ins_ref: InstructionRef {
527                        address: addr as u64,
528                        size: 2,
529                        opcode,
530                        branch_dest: None,
531                    },
532                    code: &code,
533                    ..Default::default()
534                },
535                &DiffObjConfig::default(),
536                &mut |part| {
537                    parts.push(part.into_static());
538                    Ok(())
539                },
540            )
541            .unwrap();
542
543            let joined_str: String = parts.iter().map(<_>::to_string).collect();
544            assert_eq!(joined_str, expected_str.to_string());
545        }
546    }
547
548    #[test]
549    fn test_sh2_display_instruction_bra_bsr() {
550        let arch: ArchSuperH = ArchSuperH {};
551        let ops: &[(u16, u32, &str)] = &[
552            // bra
553            (0xa000, 0x0000, "bra 0x4"),
554            (0xa001, 0x0000, "bra 0x6"),
555            (0xa800, 0x0000, "bra 0xfffff004"),
556            (0xa801, 0x0000, "bra 0xfffff006"),
557            // bsr
558            (0xb000, 0x0000, "bsr 0x4"),
559            (0xb001, 0x0000, "bsr 0x6"),
560            (0xb800, 0x0000, "bsr 0xfffff004"),
561            (0xb801, 0x0000, "bsr 0xfffff006"),
562        ];
563
564        for &(opcode, addr, expected_str) in ops {
565            let code = opcode.to_be_bytes();
566            let mut parts = Vec::new();
567
568            arch.display_instruction(
569                ResolvedInstructionRef {
570                    ins_ref: InstructionRef {
571                        address: addr as u64,
572                        size: 2,
573                        opcode,
574                        branch_dest: None,
575                    },
576                    code: &code,
577                    ..Default::default()
578                },
579                &DiffObjConfig::default(),
580                &mut |part| {
581                    parts.push(part.into_static());
582                    Ok(())
583                },
584            )
585            .unwrap();
586
587            let joined_str: String = parts.iter().map(<_>::to_string).collect();
588            assert_eq!(joined_str, expected_str.to_string());
589        }
590    }
591
592    #[test]
593    fn test_sh2_display_instruction_operations() {
594        let arch = ArchSuperH {};
595        let ops: &[(u16, u32, &str)] = &[
596            (0xcdff, 0x0000, "and.b #0xff, @(r0, gbr)"),
597            (0xcfff, 0x0000, "or.b #0xff, @(r0, gbr)"),
598            (0xccff, 0x0000, "tst.b #0xff, @(r0, gbr)"),
599            (0xceff, 0x0000, "xor.b #0xff, @(r0, gbr)"),
600            (0xc9ff, 0x0000, "and #0xff, r0"),
601            (0x88ff, 0x0000, "cmp/eq #0xff, r0"),
602            (0xcbff, 0x0000, "or #0xff, r0"),
603            (0xc8ff, 0x0000, "tst #0xff, r0"),
604            (0xcaff, 0x0000, "xor #0xff, r0"),
605            (0xc3ff, 0x0000, "trapa #0xff"),
606        ];
607
608        for &(opcode, addr, expected_str) in ops {
609            let code = opcode.to_be_bytes();
610            let mut parts = Vec::new();
611
612            arch.display_instruction(
613                ResolvedInstructionRef {
614                    ins_ref: InstructionRef {
615                        address: addr as u64,
616                        size: 2,
617                        opcode,
618                        branch_dest: None,
619                    },
620                    code: &code,
621                    ..Default::default()
622                },
623                &DiffObjConfig::default(),
624                &mut |part| {
625                    parts.push(part.into_static());
626                    Ok(())
627                },
628            )
629            .unwrap();
630
631            let joined_str: String = parts.iter().map(<_>::to_string).collect();
632            assert_eq!(joined_str, expected_str.to_string());
633        }
634    }
635
636    #[test]
637    fn test_sh2_add_mov_unknown_instructions() {
638        let arch = ArchSuperH {};
639        let ops: &[(u16, u32, &str)] = &[
640            (0x70FF, 0x0000, "add #0xff, r0"),
641            (0xE0FF, 0x0000, "mov #0xff, r0"),
642            (0x0000, 0x0000, ".word 0x0000 /* unknown instruction */"),
643        ];
644
645        for &(opcode, addr, expected_str) in ops {
646            let code = opcode.to_be_bytes();
647            let mut parts = Vec::new();
648
649            arch.display_instruction(
650                ResolvedInstructionRef {
651                    ins_ref: InstructionRef {
652                        address: addr as u64,
653                        size: 2,
654                        opcode,
655                        branch_dest: None,
656                    },
657                    code: &code,
658                    ..Default::default()
659                },
660                &DiffObjConfig::default(),
661                &mut |part| {
662                    parts.push(part.into_static());
663                    Ok(())
664                },
665            )
666            .unwrap();
667
668            let joined_str: String = parts.iter().map(<_>::to_string).collect();
669            assert_eq!(joined_str, expected_str.to_string());
670        }
671    }
672
673    #[test]
674    fn test_sh2_mov_instructions_with_labels() {
675        let arch = ArchSuperH {};
676        let ops: &[(u16, u32, &str)] =
677            &[(0x9000, 0x0000, "mov.w @(0x4, pc), r0"), (0xd000, 0x0000, "mov.l @(0x4, pc), r0")];
678
679        for &(opcode, addr, expected_str) in ops {
680            let code = opcode.to_be_bytes();
681            let mut parts = Vec::new();
682
683            arch.display_instruction(
684                ResolvedInstructionRef {
685                    ins_ref: InstructionRef {
686                        address: addr as u64,
687                        size: 2,
688                        opcode,
689                        branch_dest: None,
690                    },
691                    code: &code,
692                    ..Default::default()
693                },
694                &DiffObjConfig::default(),
695                &mut |part| {
696                    parts.push(part.into_static());
697                    Ok(())
698                },
699            )
700            .unwrap();
701
702            let joined_str: String = parts.iter().map(<_>::to_string).collect();
703            assert_eq!(joined_str, expected_str.to_string());
704        }
705    }
706
707    #[test]
708    fn test_func_0606_f378_mov_w_data_labeling() {
709        let arch = ArchSuperH {};
710        let ops: &[(u16, u32, &str)] = &[(0x9000, 0x0606F378, "mov.w @(0x4, pc), r0 /* 0x00B0 */")];
711
712        let mut code = Vec::new();
713        code.extend_from_slice(&0x9000_u16.to_be_bytes());
714        code.extend_from_slice(&0x0009_u16.to_be_bytes());
715        code.extend_from_slice(&0x00B0_u16.to_be_bytes());
716
717        for &(opcode, addr, expected_str) in ops {
718            let mut parts = Vec::new();
719
720            arch.display_instruction(
721                ResolvedInstructionRef {
722                    ins_ref: InstructionRef {
723                        address: addr as u64,
724                        size: 2,
725                        opcode,
726                        branch_dest: None,
727                    },
728                    code: &opcode.to_be_bytes(),
729                    symbol: &Symbol {
730                        address: 0x0606F378, // func base address
731                        size: code.len() as u64,
732                        ..Default::default()
733                    },
734                    section: &Section {
735                        address: 0x0606F378,
736                        size: code.len() as u64,
737                        data: SectionData(code.clone()),
738                        ..Default::default()
739                    },
740                    ..Default::default()
741                },
742                &DiffObjConfig::default(),
743                &mut |part| {
744                    parts.push(part.into_static());
745                    Ok(())
746                },
747            )
748            .unwrap();
749
750            let joined_str: String = parts.iter().map(<_>::to_string).collect();
751            assert_eq!(joined_str, expected_str.to_string());
752        }
753    }
754
755    #[test]
756    fn test_func_0606_f378_mov_l_data_labeling() {
757        let arch = ArchSuperH {};
758        let ops: &[(u16, u32, &str)] =
759            &[(0xd000, 0x0606F378, "mov.l @(0x4, pc), r0 /* 0x00B000B0 */")];
760
761        let mut code = Vec::new();
762        code.extend_from_slice(&0xd000_u16.to_be_bytes());
763        code.extend_from_slice(&0x0009_u16.to_be_bytes());
764        code.extend_from_slice(&0x00B0_u16.to_be_bytes());
765        code.extend_from_slice(&0x00B0_u16.to_be_bytes());
766
767        for &(opcode, addr, expected_str) in ops {
768            let mut parts = Vec::new();
769
770            arch.display_instruction(
771                ResolvedInstructionRef {
772                    ins_ref: InstructionRef {
773                        address: addr as u64,
774                        size: 2,
775                        opcode,
776                        branch_dest: None,
777                    },
778                    code: &opcode.to_be_bytes(),
779                    symbol: &Symbol {
780                        address: 0x0606F378, // func base address
781                        size: code.len() as u64,
782                        ..Default::default()
783                    },
784                    section: &Section {
785                        address: 0x0606F378,
786                        size: code.len() as u64,
787                        data: SectionData(code.clone()),
788                        ..Default::default()
789                    },
790                    ..Default::default()
791                },
792                &DiffObjConfig::default(),
793                &mut |part| {
794                    parts.push(part.into_static());
795                    Ok(())
796                },
797            )
798            .unwrap();
799
800            let joined_str: String = parts.iter().map(<_>::to_string).collect();
801            assert_eq!(joined_str, expected_str.to_string());
802        }
803    }
804}