diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-03-06 20:33:27 +1300 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-03-11 16:59:26 +1300 |
commit | 1ecee352f5844b0809d7ae66df52e34f42b44c8e (patch) | |
tree | 472b6fd57ff7f64ac3f8cd676cbe7a113ba01f05 /src/formats | |
parent | f2ed89083f5326a7a6f0a1720033d3388aa431fb (diff) | |
download | torque-asm-1ecee352f5844b0809d7ae66df52e34f42b44c8e.zip |
Rewrite entire assembler
The language is now more general, the code is better structured, error
reporting is more detailed, and many new language features have
been implemented:
- conditional blocks
- first-class strings
- more expression operators
- binary literals
- negative values
- invocations in constant expressions
Diffstat (limited to 'src/formats')
-rw-r--r-- | src/formats/debug.rs | 18 | ||||
-rw-r--r-- | src/formats/inhx.rs | 28 | ||||
-rw-r--r-- | src/formats/inhx32.rs | 39 | ||||
-rw-r--r-- | src/formats/mod.rs | 87 | ||||
-rw-r--r-- | src/formats/raw.rs | 29 |
5 files changed, 178 insertions, 23 deletions
diff --git a/src/formats/debug.rs b/src/formats/debug.rs new file mode 100644 index 0000000..23fd34f --- /dev/null +++ b/src/formats/debug.rs @@ -0,0 +1,18 @@ +use crate::*; + + +pub fn format_debug(segments: &[Segment]) -> Result<Vec<u8>, FormatError> { + let mut output = String::new(); + for segment in segments { + // Find maximum width of all words in the segment. + let width = segment.words.iter().map(|w| w.to_string().chars().count()).max().unwrap_or(0); + let address = &segment.address; + output.push_str(&format!("SEGMENT: 0x{address:>04x}\n")); + for word in &segment.words { + let string = word.to_string(); + let w = width as usize; + output.push_str(&format!(" {string:>w$}\n")); + } + } + return Ok(output.as_bytes().to_vec()); +} diff --git a/src/formats/inhx.rs b/src/formats/inhx.rs index e83e870..fc4791b 100644 --- a/src/formats/inhx.rs +++ b/src/formats/inhx.rs @@ -1,10 +1,15 @@ use crate::*; -pub fn format_inhx(words: &[Word]) -> String { +pub fn format_inhx(segments: &[Segment]) -> Result<Vec<u8>, FormatError> { let mut records = Vec::new(); - for (i, chunk) in words.chunks(16).enumerate() { - records.push(data_record(chunk, (i * 16) as u16)); + 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; + } } records.push(terminating_record()); @@ -12,21 +17,24 @@ pub fn format_inhx(words: &[Word]) -> String { for record in records { output.push_str(&record.to_string()); } - return output; + return Ok(output.as_bytes().to_vec()); } -fn data_record(words: &[Word], address: u16) -> InhxRecord { +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((words.len()) as u8); record.be_double(address); record.byte(0x00); for word in words { - match word.bits <= 8 { - true => record.byte(word.value as u8), - false => panic!("Word '{word}' has more than 8 bits."), - }; + if word.value.width > 8 { + return Err(FormatError::WordTooWide(8, word.width, word.source.clone())); + } + record.byte(word.value.value as u8); } - return record; + return Ok(record); } fn terminating_record() -> InhxRecord { diff --git a/src/formats/inhx32.rs b/src/formats/inhx32.rs index fd7fd7b..8febeae 100644 --- a/src/formats/inhx32.rs +++ b/src/formats/inhx32.rs @@ -1,11 +1,19 @@ use crate::*; -pub fn format_inhx32(words: &[Word]) -> String { +pub fn format_inhx32(segments: &[Segment]) -> Result<Vec<u8>, FormatError> { let mut records = Vec::new(); - records.push(extended_linear_address(0x0000)); - for (i, chunk) in words.chunks(8).enumerate() { - records.push(data_record(chunk, (i * 8) as u16)); + let mut address = 0; + records.push(extended_linear_address(0)); + for segment in segments { + if (segment.address >> 16) != (address >> 16) { + records.push(extended_linear_address(segment.address)); + } + address = segment.address; + for chunk in segment.words.chunks(8) { + records.push(data_record(chunk, address)?); + address += 8; + } } records.push(terminating_record()); @@ -13,24 +21,29 @@ pub fn format_inhx32(words: &[Word]) -> String { for record in records { output.push_str(&record.to_string()); } - return output; + return Ok(output.as_bytes().to_vec()); } -fn data_record(words: &[Word], address: u16) -> InhxRecord { +fn data_record(words: &[Tracked<Word>], address: usize) -> Result<InhxRecord, FormatError> { + let Ok(address) = u32::try_from(address * 2) else { + return Err(FormatError::AddressTooLarge(u32::MAX as usize / 2, address)); + }; + let address = address as u16; let mut record = InhxRecord::new(); record.byte((words.len() * 2) as u8); - record.be_double(address * 2); + record.be_double(address); record.byte(0x00); for word in words { - match word.bits <= 16 { - true => record.le_double(word.value as u16), - false => panic!("Word '{word}' has more than 16 bits."), - }; + if word.value.width > 16 { + return Err(FormatError::WordTooWide(16, word.width, word.source.clone())); + } + record.le_double(word.value.value as u16); } - return record; + return Ok(record); } -fn extended_linear_address(address: u16) -> InhxRecord { +fn extended_linear_address(address: usize) -> InhxRecord { + let address = (address >> 16) as u16; let mut record = InhxRecord::new(); record.byte(0x02); record.be_double(0x0000); diff --git a/src/formats/mod.rs b/src/formats/mod.rs index 82f19f1..132001a 100644 --- a/src/formats/mod.rs +++ b/src/formats/mod.rs @@ -1,8 +1,78 @@ mod inhx; mod inhx32; +mod raw; +mod debug; pub use inhx::*; pub use inhx32::*; +pub use raw::*; +pub use debug::*; + +use crate::*; + +use log::*; + + +#[derive(Clone, Copy, PartialEq)] +pub enum Format { + Debug, + Inhx, + Inhx32, + Raw, + Source, +} + +impl Format { + pub fn from_str(string: &str) -> Self { + match string { + "debug" => Self::Debug, + "inhx" => Self::Inhx, + "inhx32" => Self::Inhx32, + "raw" => Self::Raw, + "source" => Self::Source, + _ => fatal!("Unknown format '{string}', expected 'debug', 'inhx', 'inhx32', 'raw', or 'source'. "), + } + } +} + +impl std::fmt::Display for Format { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let string = match self { + Self::Debug => "debug", + Self::Inhx => "inhx", + Self::Inhx32 => "inhx32", + Self::Raw => "raw", + Self::Source => "source", + }; + write!(f, "{string}") + } +} + + +pub enum FormatError { + /// (expected, received) + AddressTooLarge(usize, usize), + /// (expected, received) + WordTooWide(u32, u32, SourceSpan), + /// + ExpectedFixedWidth, +} + +pub fn report_format_error(error: &FormatError, format: Format, source_code: &str) { + match error { + FormatError::AddressTooLarge(expected, received) => + error!("The {format} format requires that addresses do not exceed {expected}, but the address {received} was reached"), + FormatError::WordTooWide(expected, received, source) => { + let message = format!("The {format} format requires that words are no wider than {expected} bits, but a {received} bit word was found"); + let context = Context { source_code, source }; + report_source_issue(LogLevel::Error, &context, &message); + } + FormatError::ExpectedFixedWidth => + error!("The {format} format requires all words to be the same width"), + } + std::process::exit(1); +} + pub struct InhxRecord { @@ -43,3 +113,20 @@ impl InhxRecord { format!(":{output}{checksum:0>2X}\n") } } + + +pub fn calculate_fixed_width(segments: &[Segment]) -> Option<u32> { + let mut width = None; + for segment in segments { + for word in &segment.words { + let word_width = word.value.width; + match width { + Some(width) => if word_width != width { + return None; + } + None => width = Some(word_width), + } + } + } + return width.or(Some(0)); +} diff --git a/src/formats/raw.rs b/src/formats/raw.rs new file mode 100644 index 0000000..ecc6473 --- /dev/null +++ b/src/formats/raw.rs @@ -0,0 +1,29 @@ +use crate::*; + + +pub fn format_raw(segments: &[Segment], width: Option<u32>) -> Result<Vec<u8>, FormatError> { + let Some(width) = width.or_else(|| calculate_fixed_width(&segments)) else { + return Err(FormatError::ExpectedFixedWidth); + }; + + let mut address = 0; + let bytes_per_word = ((width + 7) / 8) as usize; + let mut bytes = Vec::new(); + + for segment in segments { + // Pad to the segment start address. + let padding = segment.address.saturating_sub(address); + bytes.resize(bytes.len() + (padding * bytes_per_word), 0); + for word in &segment.words { + // Decompose word value into bytes. + let value = word.value.value; + for i in (0..bytes_per_word).rev() { + let byte = (value >> (i*8) & 0xff) as u8; + bytes.push(byte); + } + address += 1; + } + } + + return Ok(bytes); +} |