| 1 | use std::fmt::Write; |
| 2 | |
| 3 | use super::AsmOutput; |
| 4 | |
| 5 | const BYTES_PER_ROW: usize = 4; |
| 6 | |
| 7 | /// Keil A51-style listing: location, object bytes, line number, source, |
| 8 | /// with errors inline and a symbol table at the end. |
| 9 | pub fn render(out: &AsmOutput, source: &str) -> String { |
| 10 | let mut s = String::new(); |
| 11 | let _ = writeln!(s, "LOC OBJ LINE SOURCE"); |
| 12 | let _ = writeln!(s); |
| 13 | let mut diags = out.diagnostics.iter().peekable(); |
| 14 | for (i, text) in source.lines().enumerate() { |
| 15 | let line = out.lines.get(i).cloned().unwrap_or_default(); |
| 16 | let loc = line.addr.map_or(" ".to_string(), |a| format!("{a:04X}")); |
| 17 | let mut chunks = line.bytes.chunks(BYTES_PER_ROW); |
| 18 | let obj = chunks.next().map(hex_bytes).unwrap_or_default(); |
| 19 | let _ = writeln!(s, "{loc} {obj:<12} {:>5} {}", i + 1, text.trim_end()); |
| 20 | let mut addr = line.addr.unwrap_or(0) as usize; |
| 21 | for c in chunks { |
| 22 | addr += BYTES_PER_ROW; |
| 23 | let _ = writeln!(s, "{:04X} {}", addr & 0xFFFF, hex_bytes(c)); |
| 24 | } |
| 25 | while let Some(d) = diags.next_if(|d| d.line == i) { |
| 26 | let _ = writeln!(s, "*** ERROR at column {}: {}", d.col + 1, d.msg); |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | let _ = writeln!(s); |
| 31 | let _ = writeln!(s, "SYMBOL TABLE"); |
| 32 | let _ = writeln!(s, "NAME TYPE VALUE"); |
| 33 | for (name, sym) in &out.symbols { |
| 34 | let _ = writeln!( |
| 35 | s, |
| 36 | "{name:<19} {:<7} {:04X}h", |
| 37 | sym.kind.name(), |
| 38 | sym.value & 0xFFFF |
| 39 | ); |
| 40 | } |
| 41 | let _ = writeln!(s); |
| 42 | let _ = writeln!( |
| 43 | s, |
| 44 | "ASSEMBLY COMPLETE: {} ERROR(S), {} BYTE(S) OF CODE", |
| 45 | out.diagnostics.len(), |
| 46 | out.code_size() |
| 47 | ); |
| 48 | s |
| 49 | } |
| 50 | |
| 51 | fn hex_bytes(b: &[u8]) -> String { |
| 52 | b.iter().map(|x| format!("{x:02X}")).collect() |
| 53 | } |