Skip to main content

i8051_disassembler/
address.rs

1use i8051::{ControlFlow, Instruction, Opcode};
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        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}