1use std::ops::{Bound, RangeBounds};
2
3use i8051::{ControlFlow, Instruction, Mnemonic};
4use serde::{Deserialize, Serialize};
5
6pub type AddressValue = u32;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
9pub struct AddressRange {
10 pub start: AddressValue,
11 pub end: AddressValue,
12}
13
14impl AddressRange {
15 pub fn new(start_inclusive: AddressValue, end_exclusive: AddressValue) -> Self {
16 Self {
17 start: start_inclusive,
18 end: end_exclusive,
19 }
20 }
21}
22
23impl<T: RangeBounds<AddressValue>> From<T> for AddressRange {
24 fn from(range: T) -> Self {
25 let range_start_inclusive = match range.start_bound() {
26 Bound::Included(addr) => *addr,
27 Bound::Excluded(addr) => addr.saturating_add(1),
28 Bound::Unbounded => 0,
29 };
30 let range_end_inclusive = match range.end_bound() {
31 Bound::Included(addr) => *addr,
32 Bound::Excluded(addr) => addr.saturating_sub(1),
33 Bound::Unbounded => AddressValue::MAX,
34 };
35 Self {
36 start: range_start_inclusive,
37 end: range_end_inclusive,
38 }
39 }
40}
41
42pub const AREA_ORDER: [AddressSpace; 5] = [
44 AddressSpace::Code,
45 AddressSpace::Idata,
46 AddressSpace::Sfr,
47 AddressSpace::Bit,
48 AddressSpace::Xdata,
49];
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
52pub enum AddressSpace {
53 Code,
54 Idata,
55 Sfr,
56 Bit,
57 Xdata,
58}
59
60impl AddressSpace {
61 pub fn area_header(self) -> &'static str {
62 match self {
63 Self::Code => ".area CODE (CODE,ABS)\n",
64 Self::Idata => ".area IDATA (IDATA,ABS)\n",
65 Self::Sfr => ".area SFR (SFR,ABS)\n",
66 Self::Bit => ".area BIT (BIT,ABS)\n",
67 Self::Xdata => ".area XDATA (XDATA,ABS)\n",
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
73pub struct PhysicalAddr {
74 pub space: AddressSpace,
75 pub offset: AddressValue,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
79pub struct Xref {
80 pub from: PhysicalAddr,
81 pub to: PhysicalAddr,
82 pub xref_type: XrefType,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
86pub enum XrefType {
87 Call,
88 Jump,
89 Data,
90}
91
92pub fn branch_target_operand_index(insn: &Instruction) -> Option<usize> {
93 if branch_target(insn).is_none() {
94 return None;
95 }
96 match insn.mnemonic() {
97 Mnemonic::LJMP | Mnemonic::LCALL | Mnemonic::AJMP | Mnemonic::ACALL | Mnemonic::SJMP => {
98 Some(0)
99 }
100 _ => {
101 let decoded = insn.as_string();
102 let operand_count = decoded.split_once(' ').map_or(0, |(_, rest)| {
103 if rest.is_empty() {
104 0
105 } else {
106 rest.split(',').count()
107 }
108 });
109 if operand_count == 0 {
110 None
111 } else {
112 Some(operand_count - 1)
113 }
114 }
115 }
116}
117
118pub fn branch_target(insn: &Instruction) -> Option<u32> {
119 match insn.control_flow() {
120 ControlFlow::Jump { target } => Some(target),
121 ControlFlow::Call { target, .. } => Some(target),
122 ControlFlow::Choice { branch_target, .. } => Some(branch_target),
123 _ => None,
124 }
125}
126
127pub fn xrefs_from_instruction(instruction: &Instruction, source: PhysicalAddr) -> Vec<Xref> {
128 let mut xrefs = Vec::new();
129 let mut push = |to_offset: u32, xref_type: XrefType| {
130 xrefs.push(Xref {
131 from: source,
132 to: PhysicalAddr {
133 space: source.space,
134 offset: to_offset,
135 },
136 xref_type,
137 });
138 };
139
140 match instruction.control_flow() {
141 ControlFlow::Jump { target } => {
142 push(target, xref_type_for(instruction.mnemonic()));
143 }
144 ControlFlow::Call { target, .. } => push(target, XrefType::Call),
145 ControlFlow::Choice { branch_target, .. } => push(branch_target, XrefType::Jump),
146 _ => {}
147 }
148
149 xrefs
150}
151
152pub fn xrefs_to_target(
153 instruction: &Instruction,
154 source: PhysicalAddr,
155 target: &PhysicalAddr,
156) -> Vec<Xref> {
157 xrefs_from_instruction(instruction, source)
158 .into_iter()
159 .filter(|xref| xref.to == *target)
160 .collect()
161}
162
163fn xref_type_for(opcode: Mnemonic) -> XrefType {
164 match opcode {
165 Mnemonic::LCALL | Mnemonic::ACALL => XrefType::Call,
166 _ => XrefType::Jump,
167 }
168}