i8051_disassembler/render/
sdas.rs1use crate::address::AddressValue;
2use crate::db::DataType;
3use crate::render::Line;
4use crate::render::data::{DataChunk, DataHeuristics};
5
6pub struct SdasWriter {
7 code: String,
8 used_registers: [bool; 256],
9}
10
11impl Default for SdasWriter {
12 fn default() -> Self {
13 Self {
14 code: String::new(),
15 used_registers: [false; 256],
16 }
17 }
18}
19
20impl SdasWriter {
21 pub fn write(&mut self, string: &str) {
22 self.code.push_str(string);
23 }
24
25 pub fn write_line(&mut self, line: &Line) {
26 line_to_sdas(self, line);
27 }
28
29 pub fn use_named_register(&mut self, register: u8) {
30 self.used_registers[register as usize] = true;
31 }
32
33 pub fn into_string(self) -> String {
34 let mut prefix = String::new();
35 let mut register_count = 0;
36 for i in 0..32 {
37 if self.used_registers[i] {
38 if register_count == 0 {
39 prefix.push_str("; Registers:\n");
40 }
41 register_count += 1;
42 prefix.push_str(&format!("R{}_BANK{} = {}\n", i % 8, i / 8, i));
43 }
44 }
45 if register_count > 0 {
46 prefix.push_str("\n");
47 }
48
49 format!("{prefix}{}", self.code)
50 }
51}
52
53fn line_to_sdas(writer: &mut SdasWriter, line: &Line) {
54 let s = match line {
55 Line::Org { addr } => format!(".org 0x{addr:X}\n"),
56 Line::Blank => "\n".to_string(),
57 Line::Comment { text, .. } => format!("; {text}\n"),
58 Line::Label { name, .. } => format!("{name}:\n"),
59 Line::Instruction { text, direct, .. } => {
60 if let Some(direct) = direct {
61 writer.use_named_register(*direct);
62 }
63 format!("{text}\n")
64 }
65 Line::Data {
66 addr,
67 bytes,
68 data_type,
69 ..
70 } => match data_type {
71 DataType::Word => emit_words(bytes),
72 DataType::Byte => emit_bytes(*addr, bytes),
73 _ => emit_bytes(*addr, bytes),
74 },
75 Line::Raw { addr, bytes } => {
76 let body = emit_unknown_bytes(*addr, bytes);
77 if body.is_empty() {
78 return;
79 }
80 format!("; Unknown bytes at 0x{addr:04X}\n{body}")
81 }
82 Line::Function {
83 name,
84 signature,
85 length,
86 noreturn,
87 ..
88 } => {
89 let sig = signature.as_deref().unwrap_or("()");
90 let mut line = format!("{name}: ; fn {name}{sig}");
91 if *noreturn {
92 line.push_str("; noreturn");
93 } else {
94 line.push_str(&format!("; len = 0x{length:X}"));
95 }
96 format!("{line}\n")
97 }
98 };
99 writer.write(&s);
100}
101
102fn emit_bytes(address: AddressValue, bytes: &[u8]) -> String {
103 let heuristics = DataHeuristics::default();
104 emit_chunks(
105 &heuristics,
106 address,
107 heuristics.iterate(address, None, bytes),
108 )
109}
110
111fn emit_unknown_bytes(base_addr: AddressValue, bytes: &[u8]) -> String {
112 let heuristics = DataHeuristics::default();
113 emit_chunks(
114 &heuristics,
115 base_addr,
116 heuristics.iterate(base_addr, None, bytes),
117 )
118}
119
120fn emit_db_line(row: &[u8]) -> String {
121 let db = row
122 .iter()
123 .map(|b| format!("0x{b:02X}"))
124 .collect::<Vec<_>>()
125 .join(", ");
126 format!(" .db {db}\n")
127}
128
129fn emit_chunks<'a>(
130 heuristics: &DataHeuristics,
131 mut addr: AddressValue,
132 chunks: impl IntoIterator<Item = DataChunk<'a, u8>>,
133) -> String {
134 let mut out = String::new();
135 for chunk in chunks {
136 match chunk {
137 DataChunk::Literal(row) => {
138 for sub in heuristics.literal_rows(addr, row) {
139 out.push_str(&emit_db_line(sub));
140 }
141 addr += row.len() as AddressValue;
142 }
143 DataChunk::Run(value, len) => {
144 match value {
145 0 => out.push_str(&format!(" .ds {len}\n")),
146 value => out.push_str(&format!(
147 " .rept {len}\n .db 0x{value:02X}\n .endm\n"
148 )),
149 }
150 addr += len as AddressValue;
151 }
152 DataChunk::BlockRun(row, count) => {
153 let db = row
154 .iter()
155 .map(|b| format!("0x{b:02X}"))
156 .collect::<Vec<_>>()
157 .join(", ");
158 out.push_str(&format!(" .rept {count}\n .db {db}\n .endm\n"));
159 addr += (row.len() * count) as AddressValue;
160 }
161 }
162 }
163 out
164}
165
166fn emit_words(bytes: &[u8]) -> String {
167 let (pairs, tail) = bytes.split_at(bytes.len() - bytes.len() % 2);
168 let words: Vec<String> = pairs
169 .chunks_exact(2)
170 .map(|pair| format!("0x{:04X}", u16::from(pair[0]) | (u16::from(pair[1]) << 8)))
171 .collect();
172 let mut out = String::new();
173 if !words.is_empty() {
174 out.push_str(&format!(" .dw {}\n", words.join(", ")));
175 }
176 if !tail.is_empty() {
177 out.push_str("; WARNING: Leftover bytes\n");
180 }
181 out
182}
183
184pub fn prefix() -> String {
185 let mut s = String::new();
186 for i in 0..32 {
187 s.push_str(&format!("R{}BANK{} = {}\n", i % 8, i / 8, i));
188 }
189 s
190}