intelhex/intelhex.rs
1use std::collections::BTreeMap;
2use std::io::{BufRead, BufReader, BufWriter, Write};
3
4use super::error::{IntelHexError, MemoryError, ParseError};
5
6/// Represents the execution start address in Intel HEX format
7#[derive(Debug)]
8pub enum ExecutionStartAddress {
9 /// Segment address (CS:IP) for 20-bit addressing
10 StartSegmentAddress { cs: u16, ip: u16 },
11 /// Linear address (EIP) for 32-bit addressing
12 StartLinearAddress { eip: u32 },
13}
14
15/// Represents a continuous block of memory data
16#[derive(Debug)]
17pub struct MemoryDataBlock {
18 /// Starting address of the memory block
19 pub address: u32,
20 /// Continuous data bytes stored in this block
21 pub data: Vec<u8>,
22}
23
24/// Main structure for handling Intel HEX format data
25pub struct IntelHex {
26 /// Optional execution start address (if specified in the HEX file)
27 execution_start_address: Option<ExecutionStartAddress>,
28 /// Map of memory addresses to their corresponding data bytes
29 memory_data_cells: BTreeMap<u32, u8>,
30}
31
32enum AddressMode {
33 SegmentAddress(u32),
34 LinearAddress(u32),
35}
36
37impl IntelHex {
38 /// Creates a new empty IntelHex instance
39 ///
40 /// Initializes a new IntelHex object with:
41 /// * An empty memory cells map (no data bytes stored)
42 /// * No execution start address
43 ///
44 /// # Returns
45 ///
46 /// Returns a new empty IntelHex instance
47 ///
48 /// # Examples
49 ///
50 /// ```
51 /// use intelhex::IntelHex;
52 ///
53 /// let intel_hex = IntelHex::new();
54 ///
55 /// // Memory cells map is empty
56 /// assert!(intel_hex.memory_cells().is_empty());
57 ///
58 /// // No execution start address
59 /// assert!(intel_hex.execution_start_address().is_none());
60 /// ```
61 pub fn new() -> IntelHex {
62 Self {
63 execution_start_address: None,
64 memory_data_cells: BTreeMap::new(),
65 }
66 }
67
68 /// Returns a reference to the memory cells map
69 ///
70 /// The memory cells are stored in a BTreeMap where:
71 /// * Keys (u32) are memory addresses
72 /// * Values (u8) are the data bytes stored at those addresses
73 /// * Addresses are automatically sorted in ascending order
74 ///
75 /// # Returns
76 ///
77 /// Returns a reference to the BTreeMap containing all memory cells data
78 ///
79 /// # Examples
80 ///
81 /// ```
82 /// use intelhex::IntelHex;
83 ///
84 /// let hex_text = ":10010000214601360121470136007EFE09D2190140\n:00000001FF";
85 /// let intel_hex = IntelHex::from_intel_hex_text(hex_text).unwrap();
86 ///
87 /// // Access memory cells
88 /// let memory = intel_hex.memory_cells();
89 ///
90 /// // Check if an address contains data
91 /// if let Some(&value) = memory.get(&0x100) {
92 /// println!("Value at 0x100: {:02X}", value);
93 /// }
94 ///
95 /// // Iterate through all memory cells in address order
96 /// for (&addr, &value) in memory.iter() {
97 /// println!("Address: {:08X}, Value: {:02X}", addr, value);
98 /// }
99 /// ```
100 pub fn memory_cells(&self) -> &BTreeMap<u32, u8> {
101 &self.memory_data_cells
102 }
103
104 /// Returns a reference to the execution start address if it exists
105 ///
106 /// The execution start address can be either a segment address (CS:IP) or
107 /// a linear address (EIP), as defined in the Intel HEX format.
108 ///
109 /// # Returns
110 ///
111 /// * `Some(&ExecutionStartAddress)` - A reference to the execution start address if it was specified
112 /// * `None` - If no execution start address was specified
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// use intelhex::IntelHex;
118 /// use intelhex::ExecutionStartAddress;
119 ///
120 /// let hex_text = ":0400000300100020C9\n:00000001FF"; // Contains a start segment address
121 /// let intel_hex = IntelHex::from_intel_hex_text(hex_text).unwrap();
122 ///
123 /// if let Some(start_addr) = intel_hex.execution_start_address() {
124 /// match start_addr {
125 /// ExecutionStartAddress::StartSegmentAddress { cs, ip } => {
126 /// println!("CS:{:04X}, IP:{:04X}", cs, ip)
127 /// },
128 /// ExecutionStartAddress::StartLinearAddress { eip } => {
129 /// println!("EIP:{:08X}", eip)
130 /// },
131 /// }
132 /// }
133 /// ```
134 pub fn execution_start_address(&self) -> Option<&ExecutionStartAddress> {
135 self.execution_start_address.as_ref()
136 }
137
138 /// Creates an IntelHex instance from a reader
139 ///
140 /// # Arguments
141 ///
142 /// * `reader` - Any type that implements the `Read` trait
143 ///
144 /// # Returns
145 ///
146 /// * `Ok(IntelHex)` - A new IntelHex instance containing the parsed data
147 /// * `Err(IntelHexError)` - If there was an error reading or parsing the data
148 ///
149 /// # Examples
150 ///
151 /// ```
152 /// use std::fs::File;
153 /// use intelhex::IntelHex;
154 ///
155 /// let file = File::open("tests/fixtures/firmware.hex").unwrap();
156 /// let intel_hex = IntelHex::from_reader(file).unwrap();
157 /// ```
158 pub fn from_reader<R>(reader: R) -> Result<IntelHex, IntelHexError>
159 where
160 R: std::io::Read,
161 {
162 let mut intelhex = IntelHex::new();
163 let reader = BufReader::new(reader);
164
165 let mut base_address = AddressMode::LinearAddress(0);
166 for line in reader.lines() {
167 let line = line?;
168 if line.is_empty() {
169 continue;
170 }
171
172 match intelhex.decode_record(&line, &mut base_address) {
173 Ok(true) => break,
174 Err(error) => return Err(error),
175 _ => (),
176 }
177 }
178
179 Ok(intelhex)
180 }
181
182 /// Creates an IntelHex instance from Intel HEX format text
183 ///
184 /// # Arguments
185 ///
186 /// * `hex_text` - A string slice containing Intel HEX formatted data
187 ///
188 /// # Returns
189 ///
190 /// * `Ok(IntelHex)` - A new IntelHex instance containing the parsed data
191 /// * `Err(IntelHexError)` - If the input string is not a valid Intel HEX format
192 ///
193 /// # Examples
194 ///
195 /// ```
196 /// use intelhex::IntelHex;
197 ///
198 /// let hex_text = ":10010000214601360121470136007EFE09D2190140\n:00000001FF";
199 /// let result = IntelHex::from_intel_hex_text(hex_text).unwrap();
200 /// ```
201 pub fn from_intel_hex_text(hex_text: &str) -> Result<IntelHex, IntelHexError> {
202 Self::from_reader(hex_text.as_bytes())
203 }
204
205 /// Decodes a single Intel HEX record line
206 ///
207 /// # Arguments
208 ///
209 /// * `line` - A string slice containing a single Intel HEX record
210 /// * `base_address` - A mutable reference to the current base address
211 ///
212 /// # Returns
213 ///
214 /// * `Ok(bool)` - Returns `true` if this is an End of File record, `false` otherwise
215 /// * `Err(IntelHexError)` - If there was an error decoding the record
216 ///
217 /// # Record Types Supported
218 ///
219 /// * 00 - Data Record
220 /// * 01 - End of File Record
221 /// * 02 - Extended Segment Address Record
222 /// * 03 - Start Segment Address Record
223 /// * 04 - Extended Linear Address Record
224 /// * 05 - Start Linear Address Record
225 fn decode_record(
226 &mut self,
227 line: &str,
228 base_address: &mut AddressMode,
229 ) -> Result<bool, IntelHexError> {
230 let mut line_chars_iter = line.chars();
231
232 // Check for start code
233 if let Some(':') = line_chars_iter.next() {
234 } else {
235 return Err(ParseError::MissingStartCode.into());
236 }
237
238 // Validate hex characters and length
239 let mut hexdigit_length = 0;
240 while let Some(c) = line_chars_iter.next() {
241 hexdigit_length += 1;
242 if !c.is_ascii_hexdigit() {
243 return Err(ParseError::InvalidCharacters.into());
244 }
245 }
246
247 // Validate record length
248 if hexdigit_length < ((1 + 2 + 1 + 1) * 2) {
249 return Err(ParseError::RecordTooShort.into());
250 } else if hexdigit_length > ((1 + 2 + 1 + 255 + 1) * 2) {
251 return Err(ParseError::RecordTooLong.into());
252 } else if hexdigit_length % 2 != 0 {
253 return Err(ParseError::RecordNotEvenLength.into());
254 }
255
256 // Convert hex characters to bytes
257 let mut record_bytes = Vec::new();
258 for i in (1..hexdigit_length).step_by(2) {
259 record_bytes.push(u8::from_str_radix(&line[i..i + 2], 16).unwrap());
260 }
261
262 // Verify checksum
263 let expected_chksum = record_bytes.pop().unwrap();
264 let chksum = (0 as u8).wrapping_sub(
265 record_bytes
266 .iter()
267 .fold(0, |acc: u8, x| acc.wrapping_add(*x)),
268 );
269 if chksum != expected_chksum {
270 return Err(ParseError::ChecksumMismatch(chksum, expected_chksum).into());
271 }
272
273 // Parse record fields
274 let record_length = record_bytes[0] as usize;
275 let record_offset = ((record_bytes[1] as u16) << 8) | (record_bytes[2] as u16);
276 let record_type = record_bytes[3];
277 let record_payload = &record_bytes[4..];
278
279 // Validate payload length
280 if record_payload.len() != record_length {
281 return Err(ParseError::PayloadLengthMismatch.into());
282 }
283
284 // Process record based on type
285 match record_type {
286 0 => {
287 // Data Record
288 for i in 0..record_length {
289 let address = match base_address {
290 // Segment addressing mode:
291 // Physical Address = SBA + ([DRLO + DRI] MOD 65536)
292 // where:
293 // - SBA (Segment Base Address) = base
294 // - DRLO (Data Record Load Offset) = record_offset
295 // - DRI (Data Record Index) = i
296 // - MOD 65536 is achieved through u16 wrapping addition
297 AddressMode::SegmentAddress(base) => base
298 .wrapping_add(((record_offset as u16).wrapping_add(i as u16)) as u32),
299 // Linear addressing mode:
300 // Physical Address = Base Address + DRLO + DRI
301 // where:
302 // - Base Address = base (upper 16 bits)
303 // - DRLO (Data Record Load Offset) = record_offset
304 // - DRI (Data Record Index) = i
305 AddressMode::LinearAddress(base) => base
306 .wrapping_add(record_offset as u32)
307 .wrapping_add(i as u32),
308 };
309 if let None = self.memory_data_cells.get(&address) {
310 self.memory_data_cells.insert(address, record_payload[i]);
311 } else {
312 return Err(MemoryError::AddressOverlap.into());
313 }
314 }
315 }
316
317 1 => {
318 // End of File Record
319 if record_length != 0 {
320 return Err(ParseError::InvalidLengthForType.into());
321 } else if record_offset != 0 {
322 return Err(ParseError::InvalidLoadOffset.into());
323 }
324 return Ok(true);
325 }
326
327 2 => {
328 // Extended Segment Address Record
329 if record_length != 2 {
330 return Err(ParseError::InvalidLengthForType.into());
331 } else if record_offset != 0 {
332 return Err(ParseError::InvalidLoadOffset.into());
333 } else {
334 *base_address = AddressMode::SegmentAddress(
335 (((record_payload[0] as u32) << 8) | (record_payload[1] as u32)) << 4,
336 );
337 }
338 }
339
340 3 => {
341 // Start Segment Address Record
342 if record_length != 4 {
343 return Err(ParseError::InvalidLengthForType.into());
344 } else if record_offset != 0 {
345 return Err(ParseError::InvalidLoadOffset.into());
346 } else {
347 let cs: u16 = ((record_payload[0] as u16) << 8) | record_payload[1] as u16;
348 let ip: u16 = ((record_payload[2] as u16) << 8) | record_payload[3] as u16;
349 if let None = self.execution_start_address {
350 self.execution_start_address =
351 Some(ExecutionStartAddress::StartSegmentAddress { cs, ip });
352 } else {
353 return Err(ParseError::MultipleStartAddressRecord.into());
354 }
355 }
356 }
357
358 4 => {
359 // Extended Linear Address Record
360 if record_length != 2 {
361 return Err(ParseError::InvalidLengthForType.into());
362 } else if record_offset != 0 {
363 return Err(ParseError::InvalidLoadOffset.into());
364 } else {
365 *base_address = AddressMode::LinearAddress(
366 (((record_payload[0] as u32) << 8) | (record_payload[1] as u32)) << 16,
367 );
368 }
369 }
370
371 5 => {
372 // Start Linear Address Record
373 if record_length != 4 {
374 return Err(ParseError::InvalidLengthForType.into());
375 } else if record_offset != 0 {
376 return Err(ParseError::InvalidLoadOffset.into());
377 } else {
378 let eip: u32 = ((record_payload[0] as u32) << 24)
379 | ((record_payload[1] as u32) << 16)
380 | ((record_payload[2] as u32) << 8)
381 | record_payload[3] as u32;
382 if let None = self.execution_start_address {
383 self.execution_start_address =
384 Some(ExecutionStartAddress::StartLinearAddress { eip });
385 } else {
386 return Err(ParseError::MultipleStartAddressRecord.into());
387 }
388 }
389 }
390
391 _ => return Err(ParseError::UnsupportedRecordType(record_type).into()),
392 }
393
394 Ok(false)
395 }
396
397 /// Writes Intel HEX format data to a writer
398 ///
399 /// # Arguments
400 ///
401 /// * `writer` - Any type that implements the `Write` trait
402 /// * `data_byte_count` - Number of data bytes per Data Record (1-255).
403 /// A value of 16 is recommended for general use.
404 ///
405 /// # Returns
406 ///
407 /// * `Ok(())` - If the data was successfully written
408 /// * `Err(std::io::Error)` - If there was an error writing the data
409 ///
410 /// # Examples
411 ///
412 /// ```no_run
413 /// use intelhex::IntelHex;
414 /// use std::fs::File;
415 ///
416 /// let intel_hex = IntelHex::new();
417 /// let mut file = File::create("output.hex").unwrap();
418 /// intel_hex.to_writer(&mut file, 16).unwrap(); // 16 data bytes per data record
419 /// ```
420 pub fn to_writer<W>(&self, writer: W, data_byte_count: u8) -> Result<(), std::io::Error>
421 where
422 W: std::io::Write,
423 {
424 // Since data_byte_count is u8, it's automatically limited to 0-255
425 // We only need to check the lower bound (0)
426 if data_byte_count == 0 {
427 return Err(std::io::Error::new(
428 std::io::ErrorKind::InvalidInput,
429 "data_byte_count must be between 1 and 255",
430 ));
431 }
432
433 let mut writer = BufWriter::new(writer);
434 let mut is_first = true;
435 let mut base_address: u32 = 0;
436 let mut continuous_bytes: Vec<u8> = Vec::with_capacity(data_byte_count as usize);
437 let mut load_offset = 0;
438 let mut last_addr = 0;
439
440 // Write data records
441 for (addr, val) in &self.memory_data_cells {
442 let mut need_addr_record = false;
443 let mut is_addr_continuous = false;
444
445 if is_first {
446 base_address = *addr & 0xFFFF0000;
447 need_addr_record = true;
448 last_addr = *addr;
449 is_first = false;
450 }
451
452 if *addr - base_address > 0xFFFF {
453 need_addr_record = true;
454 }
455
456 if *addr == (last_addr + 1) {
457 is_addr_continuous = true;
458 }
459
460 if (!is_addr_continuous)
461 || (continuous_bytes.len() >= data_byte_count as usize)
462 || need_addr_record
463 {
464 if !continuous_bytes.is_empty() {
465 // Data Record
466 let bytes_length = continuous_bytes.len();
467 let mut chksum: u8 = (bytes_length as u8)
468 .wrapping_add((load_offset >> 8) as u8)
469 .wrapping_add(load_offset as u8);
470
471 write!(writer, ":{:02X}{:04X}00", bytes_length, load_offset)?;
472
473 for v in &continuous_bytes {
474 chksum = chksum.wrapping_add(*v);
475 write!(writer, "{:02X}", *v)?;
476 }
477 chksum = (0 as u8).wrapping_sub(chksum);
478
479 #[cfg(windows)]
480 write!(writer, "{:02X}\r\n", chksum)?;
481 #[cfg(not(windows))]
482 write!(writer, "{:02X}\n", chksum)?;
483
484 continuous_bytes.clear();
485 load_offset = *addr - base_address;
486 }
487 }
488
489 if need_addr_record {
490 base_address = *addr & 0xFFFF0000;
491 load_offset = *addr - base_address;
492
493 // Extended Linear Address Record
494 let mut chksum: u8 = 2 + 4;
495 chksum = chksum.wrapping_add((base_address >> 24) as u8);
496 chksum = chksum.wrapping_add((base_address >> 16) as u8);
497 chksum = (0 as u8).wrapping_sub(chksum);
498
499 #[cfg(windows)]
500 write!(
501 writer,
502 ":02000004{:04X}{:02X}\r\n",
503 base_address >> 16,
504 chksum
505 )?;
506 #[cfg(not(windows))]
507 write!(
508 writer,
509 ":02000004{:04X}{:02X}\n",
510 base_address >> 16,
511 chksum
512 )?;
513 }
514
515 continuous_bytes.push(*val);
516 last_addr = *addr;
517 }
518
519 if !continuous_bytes.is_empty() {
520 // Data Record
521 let bytes_length = continuous_bytes.len();
522 let mut chksum: u8 = (bytes_length as u8)
523 .wrapping_add((load_offset >> 8) as u8)
524 .wrapping_add(load_offset as u8);
525
526 write!(writer, ":{:02X}{:04X}00", bytes_length, load_offset)?;
527
528 for v in &continuous_bytes {
529 chksum = chksum.wrapping_add(*v);
530 write!(writer, "{:02X}", *v)?;
531 }
532 chksum = (0 as u8).wrapping_sub(chksum);
533
534 #[cfg(windows)]
535 write!(writer, "{:02X}\r\n", chksum)?;
536 #[cfg(not(windows))]
537 write!(writer, "{:02X}\n", chksum)?;
538 }
539
540 // Write execution start address if present
541 if let Some(execution_start_address) = &self.execution_start_address {
542 match *execution_start_address {
543 ExecutionStartAddress::StartSegmentAddress { cs, ip } => {
544 // Start Segment Address Record
545 let mut chksum: u8 = 4 + 3;
546 chksum = chksum.wrapping_add((cs >> 8) as u8);
547 chksum = chksum.wrapping_add(cs as u8);
548 chksum = chksum.wrapping_add((ip >> 8) as u8);
549 chksum = chksum.wrapping_add(ip as u8);
550 chksum = (0 as u8).wrapping_sub(chksum);
551
552 #[cfg(windows)]
553 write!(writer, ":04000003{:04X}{:04X}{:02X}\r\n", cs, ip, chksum)?;
554 #[cfg(not(windows))]
555 write!(writer, ":04000003{:04X}{:04X}{:02X}\n", cs, ip, chksum)?;
556 }
557
558 ExecutionStartAddress::StartLinearAddress { eip } => {
559 // Start Linear Address Record
560 let mut chksum: u8 = 4 + 5;
561 chksum = chksum.wrapping_add((eip >> 24) as u8);
562 chksum = chksum.wrapping_add((eip >> 16) as u8);
563 chksum = chksum.wrapping_add((eip >> 8) as u8);
564 chksum = chksum.wrapping_add(eip as u8);
565 chksum = (0 as u8).wrapping_sub(chksum);
566
567 #[cfg(windows)]
568 write!(writer, ":04000005{:08X}{:02X}\r\n", eip, chksum)?;
569 #[cfg(not(windows))]
570 write!(writer, ":04000005{:08X}{:02X}\n", eip, chksum)?;
571 }
572 }
573 }
574
575 // End of File Record
576 #[cfg(windows)]
577 write!(writer, ":00000001FF\r\n")?;
578 #[cfg(not(windows))]
579 write!(writer, ":00000001FF\n")?;
580 writer.flush()?;
581
582 Ok(())
583 }
584
585 /// Converts the IntelHex object to Intel HEX format text
586 ///
587 /// # Returns
588 ///
589 /// * `Ok(String)` - A string containing the Intel HEX formatted data
590 /// * `Err(std::io::Error)` - If there was an error writing the data
591 pub fn to_intel_hex_text(&self) -> Result<String, std::io::Error> {
592 let mut buf: Vec<u8> = Vec::new();
593 self.to_writer(&mut buf, 16)?;
594 Ok(String::from_utf8(buf).unwrap())
595 }
596
597 /// Converts memory data into a collection of continuous data blocks
598 ///
599 /// # Returns
600 ///
601 /// Returns a vector of MemoryDataBlock, each containing:
602 /// * address: Starting address of the data block
603 /// * data: Vector of continuous data bytes
604 pub fn memory_blocks(&self) -> Vec<MemoryDataBlock> {
605 let mut blocks = Vec::new();
606 if self.memory_data_cells.is_empty() {
607 return blocks;
608 }
609
610 let mut current_block = MemoryDataBlock {
611 address: 0,
612 data: Vec::new(),
613 };
614 let mut last_address: Option<u32> = None;
615
616 for (&address, &value) in &self.memory_data_cells {
617 match last_address {
618 None => {
619 // First data point
620 current_block.address = address;
621 current_block.data.push(value);
622 }
623 Some(last_addr) => {
624 if address == last_addr + 1 {
625 // Continuous address
626 current_block.data.push(value);
627 } else {
628 // Discontinuous address, create new block
629 blocks.push(std::mem::replace(
630 &mut current_block,
631 MemoryDataBlock {
632 address,
633 data: vec![value],
634 },
635 ));
636 }
637 }
638 }
639 last_address = Some(address);
640 }
641
642 // Add the last block
643 if !current_block.data.is_empty() {
644 blocks.push(current_block);
645 }
646
647 blocks
648 }
649
650 /// Adds data from a MemoryDataBlock to the memory cells
651 ///
652 /// # Arguments
653 ///
654 /// * `block` - The MemoryDataBlock containing address and data to add
655 ///
656 /// # Returns
657 ///
658 /// * `Ok(())` - If the data was successfully added
659 /// * `Err(IntelHexError)` - If there was an error adding the data
660 ///
661 /// # Examples
662 ///
663 /// ```
664 /// use intelhex::{IntelHex, MemoryDataBlock};
665 ///
666 /// let mut intel_hex = IntelHex::new();
667 /// let block = MemoryDataBlock {
668 /// address: 0x1000,
669 /// data: vec![0x12, 0x34, 0x56],
670 /// };
671 ///
672 /// // Add the memory block
673 /// intel_hex.add_memory_block(&block).unwrap();
674 ///
675 /// // Verify the data was added
676 /// let memory = intel_hex.memory_cells();
677 /// assert_eq!(memory.get(&0x1000), Some(&0x12));
678 /// assert_eq!(memory.get(&0x1001), Some(&0x34));
679 /// assert_eq!(memory.get(&0x1002), Some(&0x56));
680 /// ```
681 pub fn add_memory_block(&mut self, block: &MemoryDataBlock) -> Result<(), IntelHexError> {
682 // Check for address overlap before adding any data
683 for i in 0..block.data.len() {
684 let address = block.address.wrapping_add(i as u32);
685 if self.memory_data_cells.contains_key(&address) {
686 return Err(MemoryError::AddressOverlap.into());
687 }
688 }
689
690 // Add all data bytes to memory cells
691 for (i, &value) in block.data.iter().enumerate() {
692 let address = block.address.wrapping_add(i as u32);
693 self.memory_data_cells.insert(address, value);
694 }
695
696 Ok(())
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703
704 // Test helper function to create a simple IntelHex instance with test data
705 fn create_test_hex() -> IntelHex {
706 let hex_text = "\
707 :10010000214601360121470136007EFE09D2190140\n\
708 :100110002146017EB7C20001FF5F16002148011988\n\
709 :00000001FF";
710 IntelHex::from_intel_hex_text(hex_text).unwrap()
711 }
712
713 #[test]
714 fn test_new() {
715 // Test creating a new empty IntelHex instance
716 let hex = IntelHex::new();
717 assert!(hex.memory_cells().is_empty());
718 assert!(hex.execution_start_address().is_none());
719 }
720
721 #[test]
722 fn test_basic_parsing() {
723 let hex = create_test_hex();
724 let memory = hex.memory_cells();
725
726 // Verify first data record was parsed correctly
727 assert_eq!(memory.get(&0x0100), Some(&0x21));
728 assert_eq!(memory.get(&0x0101), Some(&0x46));
729
730 // Verify second data record was parsed correctly
731 assert_eq!(memory.get(&0x0110), Some(&0x21));
732 assert_eq!(memory.get(&0x0111), Some(&0x46));
733 }
734
735 #[test]
736 fn test_start_segment_address() {
737 // Test parsing of Start Segment Address Record
738 let hex_text = ":0400000300100020C9\n:00000001FF";
739 let hex = IntelHex::from_intel_hex_text(hex_text).unwrap();
740
741 match hex.execution_start_address().unwrap() {
742 ExecutionStartAddress::StartSegmentAddress { cs, ip } => {
743 assert_eq!(*cs, 0x0010);
744 assert_eq!(*ip, 0x0020);
745 }
746 _ => panic!("Wrong execution start address type"),
747 }
748 }
749
750 #[test]
751 fn test_start_linear_address() {
752 // Test parsing of Start Linear Address Record
753 let hex_text = ":0400000512345678E3\n:00000001FF";
754 let hex = IntelHex::from_intel_hex_text(hex_text).unwrap();
755
756 match hex.execution_start_address().unwrap() {
757 ExecutionStartAddress::StartLinearAddress { eip } => {
758 assert_eq!(*eip, 0x12345678);
759 }
760 _ => panic!("Wrong execution start address type"),
761 }
762 }
763
764 #[test]
765 fn test_extended_segment_address() {
766 // Test parsing with Extended Segment Address Record
767 let hex_text = "\
768 :020000021000EC\n\
769 :10010000214601360121470136007EFE09D2190140\n\
770 :00000001FF";
771 let hex = IntelHex::from_intel_hex_text(hex_text).unwrap();
772 let memory = hex.memory_cells();
773
774 // Base address should be 0x10000 + offset
775 assert_eq!(memory.get(&0x10100), Some(&0x21));
776 assert_eq!(memory.get(&0x10101), Some(&0x46));
777 }
778
779 #[test]
780 fn test_extended_linear_address() {
781 // Test parsing with Extended Linear Address Record
782 let hex_text = "\
783 :02000004FFFFFC\n\
784 :10010000214601360121470136007EFE09D2190140\n\
785 :00000001FF";
786 let hex = IntelHex::from_intel_hex_text(hex_text).unwrap();
787 let memory = hex.memory_cells();
788
789 // Base address should be 0xFFFF0000 + offset
790 assert_eq!(memory.get(&0xFFFF0100), Some(&0x21));
791 assert_eq!(memory.get(&0xFFFF0101), Some(&0x46));
792 }
793
794 #[test]
795 fn test_memory_blocks() {
796 let mut hex = IntelHex::new();
797
798 // Add discontinuous memory blocks
799 let block1 = MemoryDataBlock {
800 address: 0x1000,
801 data: vec![0x11, 0x22, 0x33],
802 };
803 let block2 = MemoryDataBlock {
804 address: 0x2000,
805 data: vec![0x44, 0x55],
806 };
807
808 hex.add_memory_block(&block1).unwrap();
809 hex.add_memory_block(&block2).unwrap();
810
811 let blocks = hex.memory_blocks();
812 assert_eq!(blocks.len(), 2);
813
814 // Verify first block
815 assert_eq!(blocks[0].address, 0x1000);
816 assert_eq!(blocks[0].data, vec![0x11, 0x22, 0x33]);
817
818 // Verify second block
819 assert_eq!(blocks[1].address, 0x2000);
820 assert_eq!(blocks[1].data, vec![0x44, 0x55]);
821 }
822
823 #[test]
824 fn test_error_handling() {
825 // Test invalid start code
826 let result = IntelHex::from_intel_hex_text("10010000214601360121470136007EFE09D2190140");
827 assert!(matches!(
828 result,
829 Err(IntelHexError::Parse(ParseError::MissingStartCode))
830 ));
831
832 // Test invalid checksum
833 let result = IntelHex::from_intel_hex_text(":10010000214601360121470136007EFE09D2190141");
834 assert!(matches!(
835 result,
836 Err(IntelHexError::Parse(ParseError::ChecksumMismatch(_, _)))
837 ));
838
839 // Test address overlap
840 let mut hex = IntelHex::new();
841 let block1 = MemoryDataBlock {
842 address: 0x1000,
843 data: vec![0x11, 0x22],
844 };
845 let block2 = MemoryDataBlock {
846 address: 0x1001,
847 data: vec![0x33, 0x44],
848 };
849
850 hex.add_memory_block(&block1).unwrap();
851 assert!(matches!(
852 hex.add_memory_block(&block2),
853 Err(IntelHexError::Memory(MemoryError::AddressOverlap))
854 ));
855 }
856}