///|
pub struct IntelHexDocument {
record_values : Array[IntelHexRecord]
chunk_values : Array[FirmwareChunk]
entry_point_value : UInt64?
} derive(Eq, Debug)
///|
fn metadata_value(bytes : Bytes) -> UInt64 {
let mut value = 0UL
for byte in bytes {
value = value * 256UL + byte.to_uint64()
}
value
}
///|
fn intel_document_error(
code : FirmwareErrorCode,
message : String,
line_index : Int,
) -> FirmwareError {
FirmwareError::new(code, message, SourcePosition::line(line_index))
}
///|
/// Parse a complete Intel HEX document and resolve data addresses.
pub fn parse_intel_hex_document(
text : String,
) -> Result[IntelHexDocument, FirmwareError] {
let lines = split_firmware_lines(text)
let records : Array[IntelHexRecord] = []
let chunks : Array[FirmwareChunk] = []
let mut base_address = 0UL
let mut entry_point : UInt64? = None
let mut terminated = false
for line_index, line in lines {
if terminated {
return Err(
intel_document_error(
DocumentRecordAfterTerminator,
"Intel HEX record appears after the EOF record",
line_index,
),
)
}
let record = match parse_intel_hex_record(line, line_index~) {
Ok(value) => value
Err(error) => return Err(error)
}
records.push(record)
match record.record_type() {
IntelData => {
let absolute = base_address + record.address().to_uint64()
let length = record.byte_count().to_uint64()
if length > 0UL && absolute + length - 1UL > 0xFFFFFFFFUL {
return Err(
intel_document_error(
AddressOutOfRange,
"Intel HEX data range exceeds the 32-bit address space",
line_index,
),
)
}
chunks.push(FirmwareChunk::new(absolute, record.data(), line_index))
}
IntelEndOfFile => terminated = true
IntelExtendedSegmentAddress =>
base_address = metadata_value(record.data()) << 4
IntelExtendedLinearAddress =>
base_address = metadata_value(record.data()) << 16
IntelStartSegmentAddress => {
if entry_point is Some(_) {
return Err(
intel_document_error(
DocumentDuplicateMetadata,
"Intel HEX document contains multiple start addresses",
line_index,
),
)
}
let data = record.data()
let code_segment = data[0].to_uint64() * 256UL + data[1].to_uint64()
let instruction = data[2].to_uint64() * 256UL + data[3].to_uint64()
entry_point = Some((code_segment << 4) + instruction)
}
IntelStartLinearAddress => {
if entry_point is Some(_) {
return Err(
intel_document_error(
DocumentDuplicateMetadata,
"Intel HEX document contains multiple start addresses",
line_index,
),
)
}
entry_point = Some(metadata_value(record.data()))
}
}
}
if !terminated {
return Err(
intel_document_error(
DocumentMissingTerminator,
"Intel HEX document is missing its EOF record",
lines.length(),
),
)
}
Ok({
record_values: records,
chunk_values: chunks,
entry_point_value: entry_point,
})
}
///|
pub fn IntelHexDocument::records(
self : IntelHexDocument,
) -> Array[IntelHexRecord] {
self.record_values.copy()
}
///|
pub fn IntelHexDocument::data_chunks(
self : IntelHexDocument,
) -> Array[FirmwareChunk] {
self.chunk_values.copy()
}
///|
pub fn IntelHexDocument::entry_point(self : IntelHexDocument) -> UInt64? {
self.entry_point_value
}