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 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 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 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 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 (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 (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 (0xa000, 0x0000, "bra 0x4"),
554 (0xa001, 0x0000, "bra 0x6"),
555 (0xa800, 0x0000, "bra 0xfffff004"),
556 (0xa801, 0x0000, "bra 0xfffff006"),
557 (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, 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, 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}