// 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); }