From 16114b2ca358dbbef09cb5a8d3a843a0e11a3b88 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Wed, 9 Oct 2024 15:41:30 +0200 Subject: Adapt the assembler to the changes on the parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of 184c39579227 ("Re-work the parser from scratch") the parser has a proper AST, and the assembler has to traverse it accordingly. Moreover, the assembler has been stripped from a lot of unneeded memory allocations and it's overall more keen on using mere references when possible. Signed-off-by: Miquel Sabaté Solà --- lib/xixanta/src/instruction.rs | 451 ----------------------------------------- 1 file changed, 451 deletions(-) delete mode 100644 lib/xixanta/src/instruction.rs (limited to 'lib/xixanta/src/instruction.rs') diff --git a/lib/xixanta/src/instruction.rs b/lib/xixanta/src/instruction.rs deleted file mode 100644 index ced452a..0000000 --- a/lib/xixanta/src/instruction.rs +++ /dev/null @@ -1,451 +0,0 @@ -use crate::errors::ParseError; -use std::fmt; -use std::ops::Range; - -/// PString is a String with position information. -#[derive(Debug, Clone, PartialEq)] -pub struct PString { - pub value: String, - pub line: usize, - pub range: Range, -} - -impl PString { - pub fn new() -> Self { - PString { - value: String::from(""), - line: 0, - range: Range { start: 0, end: 0 }, - } - } - - pub fn from(value: &str) -> Self { - PString { - value: String::from(value), - line: 0, - range: Range { start: 0, end: 0 }, - } - } - - pub fn parser_error(&self, message: &str) -> ParseError { - // TODO: we can go further :) - ParseError { - line: self.line, - message: String::from(message), - parse: true, - } - } - - pub fn is_valid(&self) -> bool { - !(self.value.is_empty() || self.range.is_empty()) - } - - pub fn is_valid_identifier(&self) -> Result<(), String> { - if self.value.trim().is_empty() { - return Err(format!("empty identifier")); - } - - // You cannot assign into a name which is reserved. - if matches!(self.value.to_lowercase().as_str(), "x" | "y" | "a") { - return Err(format!("cannot use reserved name '{}'", self.value)); - } - - // You cannot assign into scoped names: declare them into their - // respective scopes instead. - if self.value.contains("::") { - return Err(format!( - "the name '{}' is scoped: do not declare things this way", - self.value - )); - } - - // Let's gather info from the variable name which is relevant to later - // checks. - let mut alpha_seen = false; - let mut valid_hex = match self.value.len() { - 1 | 2 | 3 | 4 => true, - _ => false, - }; - for c in self.value.to_lowercase().chars() { - if c == '_' { - valid_hex = false; - } else { - if c.is_alphabetic() { - alpha_seen = true; - if c > 'f' && c <= 'z' { - valid_hex = false; - } - } - } - } - - // We need at least one alphabetic character. Otherwise it might be - // confusing with numbers. - if !alpha_seen { - return Err(format!( - "name '{}' requires at least one alphabetic character", - self.value - )); - } - - // To avoid problems down the line, you cannot assign into names which - // are proper hexadecimal values. - if valid_hex { - return Err(format!( - "cannot use names which are valid hexadecimal values such as '{}'", - self.value - )); - } - - Ok(()) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Bundle { - pub bytes: [u8; 3], - pub size: u8, - pub address: usize, - pub cycles: u8, - pub affected_on_page: bool, -} - -impl Bundle { - pub fn new() -> Self { - Self { - bytes: [0, 0, 0], - size: 0, - address: 0, - cycles: 0, - affected_on_page: false, - } - } -} - -#[derive(Eq, Hash, PartialEq, Debug, Clone)] -pub enum AddressingMode { - Unknown, // TODO: is this really used? - Implied, - Immediate, - Absolute, - RelativeOrZeropage, - IndexedX, - IndexedY, - ZeropageIndexedX, - ZeropageIndexedY, - Indirect, - IndirectX, - IndirectY, -} - -impl fmt::Display for AddressingMode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - AddressingMode::Implied => write!(f, "implied"), - AddressingMode::Immediate => write!(f, "immediate"), - AddressingMode::Absolute => write!(f, "absolute"), - AddressingMode::RelativeOrZeropage => write!(f, "relative or zeropage"), - AddressingMode::IndexedX => write!(f, "indexed by x"), - AddressingMode::IndexedY => write!(f, "indexed by y"), - AddressingMode::ZeropageIndexedX => write!(f, "zeropage indexed by x"), - AddressingMode::ZeropageIndexedY => write!(f, "zeropage indexed by y"), - AddressingMode::Indirect => write!(f, "indirect"), - AddressingMode::IndirectX => write!(f, "indirect indexed by x"), - AddressingMode::IndirectY => write!(f, "indirect indexed by y"), - _ => write!(f, "unknown"), - } - } -} - -/// Encodable is a trait to be implemented by those structs that might need to -/// be encoded into the outside world. That is, structures that make sense to -/// output into files or other output streams. -pub trait Encodable { - /// Returns a fixed array of bytes which belong to an encodable object. Note - /// that the capacity is fixed, but the actual size must be checked with the - /// `size` trait function, otherwise elements beyond that size might contain - /// junk. - fn to_bytes(&self) -> [u8; 3]; - - /// Returns the actual size of the data returned by `to_bytes`. - fn size(&self) -> u8; - - /// Returns a vector which contains the exact byte data for the given - /// object. In contrast with `to_bytes`, the caller does not need to check - /// for `size`: the returned vector is tailored to the exact amount of - /// bytes for the object. - fn to_hex(&self) -> Vec; - - /// Returns a string representation which makes sense to a human (e.g. - /// instead of providing the byte encoded opcode for an instruction, show - /// the mnemonic). - fn to_human(&self) -> String; - - /// Returns a string representation with higher verbosity than `to_human`. - fn to_verbose(&self) -> String; -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Instruction { - pub mnemonic: PString, - pub opcode: u8, - pub bytes: [u8; 2], - pub size: u8, - pub left: Option, - pub right: Option, - pub mode: AddressingMode, - pub cycles: u8, // NOTE: relative addressing makes this runtime-dependant (if branch is taken, then +1 cycle to the base cycle here). - pub affected_on_page: bool, // TODO: needed? - pub address: u16, - pub resolved: bool, -} - -impl Instruction { - pub fn unknown() -> Instruction { - Instruction { - mnemonic: PString::new(), - opcode: 0, - bytes: [0, 0], - size: 0, - left: None, - right: None, - mode: AddressingMode::Unknown, - cycles: 0, - affected_on_page: false, - address: 0, - resolved: true, - } - } - - pub fn from(mnemonic: &str) -> Instruction { - Instruction { - mnemonic: PString::from(mnemonic), - opcode: 0, - bytes: [0, 0], - size: 0, - left: None, - right: None, - mode: AddressingMode::Unknown, - cycles: 0, - affected_on_page: false, - address: 0, - resolved: true, - } - } -} - -impl Encodable for Instruction { - fn size(&self) -> u8 { - self.size - } - - fn to_hex(&self) -> Vec { - let mut ret = vec![]; - - ret.push(format!("{:02X}", self.opcode)); - if self.size > 1 { - ret.push(format!("{:02X}", self.bytes[0])); - } - if self.size == 3 { - ret.push(format!("{:02X}", self.bytes[1])); - } - - ret - } - - fn to_bytes(&self) -> [u8; 3] { - [self.opcode.to_le_bytes()[0], self.bytes[0], self.bytes[1]] - } - - fn to_human(&self) -> String { - match self.mode { - AddressingMode::Implied => self.mnemonic.value.clone(), - AddressingMode::Immediate => format!("{} #${:02X}", self.mnemonic.value, self.bytes[0]), - AddressingMode::Absolute => format!( - "{} ${:02X}{:02X}", - self.mnemonic.value, self.bytes[1], self.bytes[0] - ), - AddressingMode::RelativeOrZeropage => { - format!("{} ${:02X}", self.mnemonic.value, self.bytes[0]) - } - AddressingMode::IndexedX => format!( - "{} ${:02X}{:02X}, x", - self.mnemonic.value, self.bytes[1], self.bytes[0] - ), - AddressingMode::IndexedY => format!( - "{} ${:02X}{:02X}, y", - self.mnemonic.value, self.bytes[1], self.bytes[0] - ), - AddressingMode::ZeropageIndexedX => { - format!("{} ${:02X}, x", self.mnemonic.value, self.bytes[0]) - } - AddressingMode::ZeropageIndexedY => { - format!("{} ${:02X}, y", self.mnemonic.value, self.bytes[0]) - } - AddressingMode::Indirect => format!( - "{} (${:02X}{:02X})", - self.mnemonic.value, self.bytes[1], self.bytes[0] - ), - AddressingMode::IndirectX => { - format!("{} (${:02X}, x)", self.mnemonic.value, self.bytes[0]) - } - AddressingMode::IndirectY => { - format!("{} (${:02X}), y", self.mnemonic.value, self.bytes[0]) - } - AddressingMode::Unknown => String::from("unknown instruction"), - } - } - - fn to_verbose(&self) -> String { - format!("{:#?}", self) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Generic { - pub identifier: PString, - pub left: Option>, - pub right: Option>, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Scoped { - pub identifier: PString, - pub start: bool, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Literal { - pub identifier: PString, - pub bytes: [u8; 2], - pub size: u8, - pub resolved: bool, -} - -impl Encodable for Literal { - fn size(&self) -> u8 { - self.size - } - - fn to_hex(&self) -> Vec { - let mut ret = vec![]; - - ret.push(format!("{:02X}", self.bytes[0])); - if self.size == 2 { - ret.push(format!("{:02X}", self.bytes[1])); - } else if self.size != 1 { - panic!("size for literal should be either 1 or 2"); - } - - ret - } - - fn to_bytes(&self) -> [u8; 3] { - [self.bytes[0], self.bytes[1], 0] - } - - fn to_human(&self) -> String { - match self.size { - 1 => format!(".byte ${:02X}", self.bytes[0]), - 2 => format!(".byte ${:02X}{:02X}", self.bytes[1], self.bytes[0]), - _ => String::from("unknown literal"), - } - } - - fn to_verbose(&self) -> String { - format!("{:#?}", self) - } -} - -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct Fill { - pub value: u8, -} - -impl Encodable for Fill { - fn size(&self) -> u8 { - 1 - } - - fn to_hex(&self) -> Vec { - let mut ret = vec![]; - ret.push(format!("{:02X}", self.value)); - - ret - } - - fn to_bytes(&self) -> [u8; 3] { - [self.value, 0, 0] - } - - fn to_human(&self) -> String { - format!("${:02X}", self.value) - } - - fn to_verbose(&self) -> String { - format!("{:#?}", self) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Label { - pub value: String, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Node { - Generic(Generic), - Instruction(Instruction), - Scoped(Scoped), - Literal(Literal), - Fill(Fill), - Label(Label), -} - -impl Node { - pub fn is_encodeable(&self) -> bool { - matches!( - self, - &Node::Instruction(_) | &Node::Literal(_) | &Node::Fill(_) - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn is_err(line: &str, message: &str) { - let pstring = PString { - value: line.to_string(), - line: 0, - range: Range::default(), - }; - let ret = pstring.is_valid_identifier(); - - assert!(ret.is_err()); - if let Err(e) = ret { - assert_eq!(e, message); - } - } - - #[test] - fn bad_variable_names() { - is_err("a", "cannot use reserved name 'a'"); - is_err("X", "cannot use reserved name 'X'"); - - is_err( - "AA", - "cannot use names which are valid hexadecimal values such as 'AA'", - ); - - is_err("11", "name '11' requires at least one alphabetic character"); - - is_err("__", "name '__' requires at least one alphabetic character"); - - is_err( - "Scope::Variable", - "the name 'Scope::Variable' is scoped: do not declare things this way", - ); - } -} -- cgit v1.2.3