From 6d016989b0ff5f600f3bdaced1ebf443d2f32da7 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Fri, 11 Apr 2025 16:18:31 +1200 Subject: Add support for the CP/M CMD file module format --- src/formats/cmd.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/formats/cmd.rs (limited to 'src/formats/cmd.rs') diff --git a/src/formats/cmd.rs b/src/formats/cmd.rs new file mode 100644 index 0000000..2ff2097 --- /dev/null +++ b/src/formats/cmd.rs @@ -0,0 +1,54 @@ +// CP/M CMD format, also the TRS-80 Load Module Format (LMF) +// https://en.wikipedia.org/wiki/CMD_file_(CP/M) +// https://www.tim-mann.org/trs80/doc/ldosq1-4.pdf (page 43) +use crate::*; + + +pub fn format_cmd(segments: &[Segment]) -> Result, FormatError> { + let mut records = Vec::new(); + let mut address; + for segment in segments { + address = segment.address; + for chunk in segment.words.chunks(16) { + records.push(data_record(chunk, address)?); + address += 16; + } + } + let start_address = segments.first().map(|s| s.address).unwrap_or(0); + records.push(terminating_record(start_address)?); + + let mut output = String::new(); + for record in records { + output.push_str(&record.to_string_plain()); + } + return Ok(output.into_bytes()); +} + +fn data_record(words: &[Tracked], address: usize) -> Result { + let Ok(address) = u16::try_from(address) else { + return Err(FormatError::AddressTooLarge(u16::MAX as usize, address)); + }; + let mut record = InhxRecord::new(); + record.byte(0x01); + let data_bytes = words.len() as u8; + record.byte(data_bytes.wrapping_add(2)); + record.le_double(address); + for word in words { + if word.value.width > 8 { + return Err(FormatError::WordTooWide(8, word.width, word.source.clone())); + } + record.byte(word.value.value as u8); + } + return Ok(record); +} + +fn terminating_record(address: usize) -> Result { + let Ok(address) = u16::try_from(address) else { + return Err(FormatError::AddressTooLarge(u16::MAX as usize, address)); + }; + let mut record = InhxRecord::new(); + record.byte(0x02); + record.byte(0x02); + record.le_double(address); + return Ok(record); +} -- cgit v1.2.3-70-g09d2