Skip to main content

i8051_disassembler/
address.rs

1use i8051::{ControlFlow, Instruction, Mnemonic};
2use serde::{Deserialize, Serialize};
3
4pub type AddressValue = u32;
5
6/// Explicit emission order for sdas area headers.
7pub 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        Mnemonic::LJMP | Mnemonic::LCALL | Mnemonic::AJMP | Mnemonic::ACALL | Mnemonic::SJMP => {
62            Some(0)
63        }
64        _ => {
65            let decoded = insn.as_string();
66            let operand_count = decoded.split_once(' ').map_or(0, |(_, rest)| {
67                if rest.is_empty() {
68                    0
69                } else {
70                    rest.split(',').count()
71                }
72            });
73            if operand_count == 0 {
74                None
75            } else {
76                Some(operand_count - 1)
77            }
78        }
79    }
80}
81
82pub fn branch_target(insn: &Instruction) -> Option<u32> {
83    let next = insn.pc().wrapping_add(insn.len() as u32);
84    match insn.control_flow() {
85        ControlFlow::Continue(addr) if u32::from(addr) != next => Some(u32::from(addr)),
86        ControlFlow::Call(_, addr) => Some(u32::from(addr)),
87        ControlFlow::Choice(_, addr) => Some(u32::from(addr)),
88        _ => None,
89    }
90}
91
92pub fn xrefs_from_instruction(instruction: &Instruction, source: PhysicalAddr) -> Vec<Xref> {
93    let next = instruction.pc().wrapping_add(instruction.len() as u32);
94
95    let mut xrefs = Vec::new();
96    let mut push = |to_offset: u32, xref_type: XrefType| {
97        xrefs.push(Xref {
98            from: source,
99            to: PhysicalAddr {
100                space: source.space,
101                offset: to_offset,
102            },
103            xref_type,
104        });
105    };
106
107    match instruction.control_flow() {
108        ControlFlow::Continue(addr) if u32::from(addr) != next => {
109            push(u32::from(addr), xref_type_for(instruction.mnemonic()));
110        }
111        ControlFlow::Call(_, addr) => push(u32::from(addr), XrefType::Call),
112        ControlFlow::Choice(_, addr) => push(u32::from(addr), XrefType::Jump),
113        _ => {}
114    }
115
116    xrefs
117}
118
119pub fn xrefs_to_target(
120    instruction: &Instruction,
121    source: PhysicalAddr,
122    target: &PhysicalAddr,
123) -> Vec<Xref> {
124    xrefs_from_instruction(instruction, source)
125        .into_iter()
126        .filter(|xref| xref.to == *target)
127        .collect()
128}
129
130fn xref_type_for(opcode: Mnemonic) -> XrefType {
131    match opcode {
132        Mnemonic::LCALL | Mnemonic::ACALL => XrefType::Call,
133        _ => XrefType::Jump,
134    }
135}