diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-04-11 16:18:31 +1200 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-04-11 16:18:31 +1200 |
commit | 6d016989b0ff5f600f3bdaced1ebf443d2f32da7 (patch) | |
tree | b8732928eec01632e0cba965e6437718598f3251 /src | |
parent | 2d29e79715c1208655316cce517713590894f5a6 (diff) | |
download | torque-asm-6d016989b0ff5f600f3bdaced1ebf443d2f32da7.zip |
Add support for the CP/M CMD file module format
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/tq.rs | 1 | ||||
-rw-r--r-- | src/formats/cmd.rs | 54 | ||||
-rw-r--r-- | src/formats/mod.rs | 13 |
3 files changed, 68 insertions, 0 deletions
diff --git a/src/bin/tq.rs b/src/bin/tq.rs index b27f702..ffbc330 100644 --- a/src/bin/tq.rs +++ b/src/bin/tq.rs @@ -172,6 +172,7 @@ Created by Ben Bridle. if !dry_run { let result = match format { + Format::Cmd => format_cmd(&segments), Format::Debug => format_debug(&segments), Format::Inhx => format_inhx(&segments), Format::Inhx32 => format_inhx32(&segments), 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<Vec<u8>, 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<Word>], address: usize) -> Result<InhxRecord, FormatError> { + 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<InhxRecord, FormatError> { + 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); +} diff --git a/src/formats/mod.rs b/src/formats/mod.rs index a77bd72..105207c 100644 --- a/src/formats/mod.rs +++ b/src/formats/mod.rs @@ -1,8 +1,10 @@ +mod cmd; mod inhx; mod inhx32; mod raw; mod debug; +pub use cmd::*; pub use inhx::*; pub use inhx32::*; pub use raw::*; @@ -15,6 +17,7 @@ use log::*; #[derive(Clone, Copy, PartialEq)] pub enum Format { + Cmd, Debug, Inhx, Inhx32, @@ -25,6 +28,7 @@ pub enum Format { impl Format { pub fn from_str(string: &str) -> Self { match string { + "cmd" => Self::Cmd, "debug" => Self::Debug, "inhx" => Self::Inhx, "inhx32" => Self::Inhx32, @@ -38,6 +42,7 @@ impl Format { impl std::fmt::Display for Format { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { let string = match self { + Self::Cmd => "cmd", Self::Debug => "debug", Self::Inhx => "inhx", Self::Inhx32 => "inhx32", @@ -112,6 +117,14 @@ impl InhxRecord { } format!(":{output}{checksum:0>2X}\n") } + + pub fn to_string_plain(self) -> String { + let mut output = String::new(); + for byte in &self.bytes { + output.push_str(&format!("{byte:0>2X}")); + } + format!("{output}\n") + } } |