Krolyxon
Branch master

Branches

Tags

Refs →

bench51cratesbench51-coresrcasmhex.rs

117 lines · 3.5 KB · Rust

1//! Intel HEX reading and writing.
2
3use std::collections::BTreeMap;
4use std::fmt::Write;
5
6const RECORD_LEN: usize = 16;
7
8/// Writes data records for contiguous runs of bytes, then the EOF record.
9pub fn write(bytes: impl Iterator<Item = (u16, u8)>) -> String {
10 let image: BTreeMap<u16, u8> = bytes.collect();
11 let mut out = String::new();
12 let mut run: Vec<u8> = Vec::new();
13 let mut run_start = 0u16;
14 let mut flush = |start: u16, data: &mut Vec<u8>| {
15 for (i, chunk) in data.chunks(RECORD_LEN).enumerate() {
16 record(
17 &mut out,
18 start.wrapping_add((i * RECORD_LEN) as u16),
19 0x00,
20 chunk,
21 );
22 }
23 data.clear();
24 };
25 for (&addr, &b) in &image {
26 if !run.is_empty() && run_start.wrapping_add(run.len() as u16) != addr {
27 flush(run_start, &mut run);
28 }
29 if run.is_empty() {
30 run_start = addr;
31 }
32 run.push(b);
33 }
34 flush(run_start, &mut run);
35 record(&mut out, 0, 0x01, &[]);
36 out
37}
38
39fn record(out: &mut String, addr: u16, kind: u8, data: &[u8]) {
40 let mut sum = data.len() as u8;
41 sum = sum
42 .wrapping_add((addr >> 8) as u8)
43 .wrapping_add(addr as u8)
44 .wrapping_add(kind);
45 let _ = write!(out, ":{:02X}{addr:04X}{kind:02X}", data.len());
46 for &b in data {
47 sum = sum.wrapping_add(b);
48 let _ = write!(out, "{b:02X}");
49 }
50 let _ = writeln!(out, "{:02X}", sum.wrapping_neg());
51}
52
53/// Parses Intel HEX into (address, byte) pairs.
54pub fn read(text: &str) -> Result<Vec<(u16, u8)>, String> {
55 let mut out = Vec::new();
56 for (n, line) in text.lines().enumerate() {
57 let line = line.trim();
58 if line.is_empty() {
59 continue;
60 }
61 let fail = |m: &str| format!("line {}: {m}", n + 1);
62 let hex = line
63 .strip_prefix(':')
64 .ok_or_else(|| fail("record must start with ':'"))?;
65 if hex.len() % 2 != 0 || hex.len() < 10 {
66 return Err(fail("malformed record"));
67 }
68 let raw = (0..hex.len())
69 .step_by(2)
70 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16))
71 .collect::<Result<Vec<u8>, _>>()
72 .map_err(|_| fail("invalid hex digit"))?;
73 let len = raw[0] as usize;
74 if raw.len() != len + 5 {
75 return Err(fail("record length mismatch"));
76 }
77 if raw.iter().fold(0u8, |a, &b| a.wrapping_add(b)) != 0 {
78 return Err(fail("checksum mismatch"));
79 }
80 let addr = u16::from_be_bytes([raw[1], raw[2]]);
81 match raw[3] {
82 0x00 => out.extend(
83 raw[4..4 + len]
84 .iter()
85 .enumerate()
86 .map(|(i, &b)| (addr.wrapping_add(i as u16), b)),
87 ),
88 0x01 => break,
89 // Extended address / start address records don't apply to a 64K 8051.
90 _ => {}
91 }
92 }
93 Ok(out)
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn round_trip() {
102 let bytes: Vec<(u16, u8)> = (0..20u16)
103 .map(|i| (i, i as u8))
104 .chain([(0x100, 0xAA)])
105 .collect();
106 let text = write(bytes.iter().copied());
107 assert!(text.ends_with(":00000001FF\n"));
108 assert_eq!(read(&text).unwrap(), bytes);
109 }
110
111 #[test]
112 fn known_record() {
113 // MOV A,#55h ; SJMP $
114 let text = write([(0, 0x74), (1, 0x55), (2, 0x80), (3, 0xFE)].into_iter());
115 assert_eq!(text.lines().next().unwrap(), ":04000000745580FEB5");
116 }
117}