1use i8051::{ControlFlow, Instruction, Opcode};
2use serde::{Deserialize, Serialize};
3
4pub type AddressValue = u32;
5
6pub const AREA_ORDER: [AddressSpace; 5] = [
8 AddressSpace::Code,
9 AddressSpace::Idata,
10 AddressSpace::Sfr,
11 AddressSpace::Bit,
12 AddressSpace::Xdata,
13];
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
16pub enum AddressSpace {
17 Code,
18 Idata,
19 Sfr,
20 Bit,
21 Xdata,
22}
23
24impl AddressSpace {
25 pub fn area_header(self) -> &'static str {
26 match self {
27 Self::Code => ".area CODE (CODE,ABS)\n",
28 Self::Idata => ".area IDATA (IDATA,ABS)\n",
29 Self::Sfr => ".area SFR (SFR,ABS)\n",
30 Self::Bit => ".area BIT (BIT,ABS)\n",
31 Self::Xdata => ".area XDATA (XDATA,ABS)\n",
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
37pub struct PhysicalAddr {
38 pub space: AddressSpace,
39 pub offset: AddressValue,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
43pub struct Xref {
44 pub from: PhysicalAddr,
45 pub to: PhysicalAddr,
46 pub xref_type: XrefType,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
50pub enum XrefType {
51 Call,
52 Jump,
53 Data,
54}
55
56pub fn branch_target_operand_index(insn: &Instruction) -> Option<usize> {
57 if branch_target(insn).is_none() {
58 return None;
59 }
60 match insn.mnemonic() {
61 Opcode::LJMP | Opcode::LCALL | Opcode::AJMP | Opcode::ACALL | Opcode::SJMP => Some(0),
62 _ => {
63 let decoded = insn.as_string();
64 let operand_count = decoded.split_once(' ').map_or(0, |(_, rest)| {
65 if rest.is_empty() {
66 0
67 } else {
68 rest.split(',').count()
69 }
70 });
71 if operand_count == 0 {
72 None
73 } else {
74 Some(operand_count - 1)
75 }
76 }
77 }
78}
79
80pub fn branch_target(insn: &Instruction) -> Option<u32> {
81 let next = insn.pc().wrapping_add(insn.len() as u32);
82 match insn.control_flow() {
83 ControlFlow::Continue(addr) if u32::from(addr) != next => Some(u32::from(addr)),
84 ControlFlow::Call(_, addr) => Some(u32::from(addr)),
85 ControlFlow::Choice(_, addr) => Some(u32::from(addr)),
86 _ => None,
87 }
88}
89
90pub fn xrefs_from_instruction(instruction: &Instruction, source: PhysicalAddr) -> Vec<Xref> {
91 let next = instruction.pc().wrapping_add(instruction.len() as u32);
92
93 let mut xrefs = Vec::new();
94 let mut push = |to_offset: u32, xref_type: XrefType| {
95 xrefs.push(Xref {
96 from: source,
97 to: PhysicalAddr {
98 space: source.space,
99 offset: to_offset,
100 },
101 xref_type,
102 });
103 };
104
105 match instruction.control_flow() {
106 ControlFlow::Continue(addr) if u32::from(addr) != next => {
107 push(u32::from(addr), xref_type_for(instruction.mnemonic()));
108 }
109 ControlFlow::Call(_, addr) => push(u32::from(addr), XrefType::Call),
110 ControlFlow::Choice(_, addr) => push(u32::from(addr), XrefType::Jump),
111 _ => {}
112 }
113
114 xrefs
115}
116
117pub fn xrefs_to_target(
118 instruction: &Instruction,
119 source: PhysicalAddr,
120 target: &PhysicalAddr,
121) -> Vec<Xref> {
122 xrefs_from_instruction(instruction, source)
123 .into_iter()
124 .filter(|xref| xref.to == *target)
125 .collect()
126}
127
128fn xref_type_for(opcode: Opcode) -> XrefType {
129 match opcode {
130 Opcode::LCALL | Opcode::ACALL => XrefType::Call,
131 _ => XrefType::Jump,
132 }
133}