Skip to main content

i8051_disassembler/
address.rs

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
42/// Explicit emission order for sdas area headers.
43pub 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}