diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/xixanta/src/assembler.rs | 108 | ||||
| -rw-r--r-- | lib/xixanta/src/errors.rs | 1 | ||||
| -rw-r--r-- | lib/xixanta/src/instruction.rs | 40 | ||||
| -rw-r--r-- | lib/xixanta/src/lib.rs | 1 | ||||
| -rw-r--r-- | lib/xixanta/src/mapping.rs | 66 |
5 files changed, 190 insertions, 26 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index 5d03186..783cccd 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -3,6 +3,7 @@ use crate::errors::ParseError; use crate::instruction::{ AddressingMode, Encodable, Generic, Instruction, Literal, Node, PString, Scoped, }; +use crate::mapping::{Mapping, Segment}; use crate::opcodes::{INSTRUCTIONS, OPCODES}; use std::collections::hash_map::Entry; use std::io::{self, BufRead, Read}; @@ -15,25 +16,17 @@ pub struct Assembler { column: usize, offset_address: usize, context: Context, - - // TODO: segments instead - nodes: Vec<Node>, -} - -impl Default for Assembler { - fn default() -> Self { - Assembler::new() - } + mapping: Mapping, } impl Assembler { - pub fn new() -> Self { + pub fn new(segments: Vec<Segment>) -> Self { Self { line: 0, column: 0, offset_address: 0, context: Context::new(), - nodes: vec![], + mapping: Mapping::new(segments), } } @@ -41,26 +34,56 @@ impl Assembler { self.line = 0; self.column = 0; self.offset_address = 0; - self.nodes = vec![]; self.context = Context::new(); + self.mapping.reset(); } - pub fn to_nodes(&mut self, reader: impl Read) -> Result<&Vec<Node>> { + pub fn assemble_nodes(&mut self, reader: impl Read) -> Result<()> { self.from_reader(reader)?; self.context.reset(); self.evaluate()?; - Ok(&self.nodes) + Ok(()) } pub fn assemble(&mut self, reader: impl Read) -> Result<Vec<&dyn Encodable>> { let mut instructions: Vec<&dyn Encodable> = vec![]; - for node in self.to_nodes(reader)? { - match node { - Node::Instruction(instr) => instructions.push(instr), - Node::Literal(lit) => instructions.push(lit), - _ => {} + self.assemble_nodes(reader)?; + + for segment in &self.mapping.segments { + let mut size: usize = 0; + + for node in &self.mapping.nodes[&segment.name] { + match node { + Node::Instruction(instr) => { + instructions.push(instr); + size += usize::from(instr.size()); + } + Node::Literal(lit) => { + instructions.push(lit); + size += usize::from(lit.size()); + } + _ => {} + } + } + + if size > segment.size { + return Err(ParseError { + line: 0, + message: format!( + "segment '{}' expected a size of '{}' bytes but '{}' bytes were produced instead", + segment.name, size, segment.size + ), + }); + } + if segment.fill_value.is_none() { + continue; + } + + while size < segment.size { + instructions.push(segment.fill_value.as_ref().unwrap()); + size += 1; } } @@ -71,7 +94,7 @@ impl Assembler { self.from_byte_reader(reader)?; let mut instructions: Vec<&dyn Encodable> = vec![]; - for node in &self.nodes { + for node in self.mapping.current() { match node { Node::Instruction(instr) => instructions.push(instr), Node::Literal(lit) => instructions.push(lit), @@ -106,7 +129,7 @@ impl Assembler { } pub fn evaluate(&mut self) -> Result<()> { - for node in &mut self.nodes { + for node in self.mapping.current_mut() { match node { Node::Instruction(instr) => { Self::update_instruction_with_context(instr, &self.context)? @@ -706,6 +729,7 @@ impl Assembler { match id.value.to_lowercase().as_str() { ".scope" => self.parse_scope_definition(&id, line), ".endscope" => self.parse_scope_end(&id), + ".segment" => self.parse_segment_definition(&id, line), ".byte" | ".db" => self.parse_literal_bytes(&id, line, false), ".word" | ".dw" => self.parse_literal_bytes(&id, line, true), _ => { @@ -716,6 +740,15 @@ impl Assembler { } } + fn parse_segment_definition(&mut self, id: &PString, line: &str) -> Result<()> { + self.skip_whitespace(line); + + let identifier = self.fetch_possibly_quoted_identifier(id, line)?; + self.mapping.switch(&identifier)?; + + Ok(()) + } + fn parse_scope_definition(&mut self, id: &PString, line: &str) -> Result<()> { self.skip_whitespace(line); @@ -726,7 +759,7 @@ impl Assembler { )); } self.context.push(&identifier.value); - self.nodes.push(Node::Scoped(Scoped { + self.mapping.push(Node::Scoped(Scoped { identifier, start: true, })); @@ -738,7 +771,7 @@ impl Assembler { if !self.context.pop() { return Err(id.parser_error("missmatched '.endscope': there is no scope to end")); } - self.nodes.push(Node::Scoped(Scoped { + self.mapping.push(Node::Scoped(Scoped { identifier: PString::new(), start: false, })); @@ -791,7 +824,7 @@ impl Assembler { // will evaluate each literal as needed (e.g. replacing // values from variables being used in this literal). let string = line.get(self.column..self.column + idx).unwrap_or(" "); - self.nodes.push(Node::Literal(Literal { + self.mapping.push(Node::Literal(Literal { identifier: PString { value: string.to_owned(), line: self.line, @@ -861,6 +894,29 @@ impl Assembler { } } + fn fetch_possibly_quoted_identifier(&mut self, id: &PString, line: &str) -> Result<PString> { + let mut identifier = self.fetch_identifier(id, line)?; + + if identifier.value.starts_with('\'') || identifier.value.starts_with('`') { + return Err(id.parser_error("use double quotes for the segment identifier instead")); + } else if identifier.value.starts_with('"') { + identifier.value = match identifier + .value + .get(1..(identifier.range.end - identifier.range.start - 1)) + { + Some(v) => v.to_string(), + None => return Err(id.parser_error("could not fetch quoted identifier")), + }; + if identifier.value.contains('"') { + return Err(id.parser_error("do not use double quotes inside of the identifier")); + } + identifier.range.start += 1; + identifier.range.end -= 1; + } + + Ok(identifier) + } + fn parse_statement(&mut self, id: PString, line: &str) -> Result<()> { if line.chars().nth(self.column).unwrap_or(' ') == ':' { self.parse_label(id, line) @@ -1089,7 +1145,7 @@ impl Assembler { instr.left = node.left; instr.right = node.right; - self.nodes.push(Node::Instruction(instr)); + self.mapping.push(Node::Instruction(instr)); Ok(()) } @@ -1118,7 +1174,7 @@ impl Assembler { } bs[i as usize] = buf[0]; } - self.nodes.push(Node::Instruction(Instruction { + self.mapping.push(Node::Instruction(Instruction { mnemonic: PString::from(&v.mnemonic), opcode: v.opcode, size: v.size, diff --git a/lib/xixanta/src/errors.rs b/lib/xixanta/src/errors.rs index 48f7286..d9a12cd 100644 --- a/lib/xixanta/src/errors.rs +++ b/lib/xixanta/src/errors.rs @@ -1,5 +1,6 @@ use std::fmt; +// TODO: global error #[derive(Debug, Clone, PartialEq)] pub struct ParseError { pub line: usize, diff --git a/lib/xixanta/src/instruction.rs b/lib/xixanta/src/instruction.rs index fac6d8e..227a3e1 100644 --- a/lib/xixanta/src/instruction.rs +++ b/lib/xixanta/src/instruction.rs @@ -268,10 +268,50 @@ impl Encodable for Literal { } } +#[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<String> { + 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 enum Node { Generic(Generic), Instruction(Instruction), Scoped(Scoped), Literal(Literal), + Fill(Fill), +} + +impl Node { + pub fn is_encodeable(&self) -> bool { + matches!( + self, + &Node::Instruction(_) | &Node::Literal(_) | &Node::Fill(_) + ) + } } diff --git a/lib/xixanta/src/lib.rs b/lib/xixanta/src/lib.rs index fc8ca96..fd3419c 100644 --- a/lib/xixanta/src/lib.rs +++ b/lib/xixanta/src/lib.rs @@ -7,4 +7,5 @@ pub mod assembler; mod context; mod errors; pub mod instruction; +pub mod mapping; mod opcodes; diff --git a/lib/xixanta/src/mapping.rs b/lib/xixanta/src/mapping.rs new file mode 100644 index 0000000..56acf18 --- /dev/null +++ b/lib/xixanta/src/mapping.rs @@ -0,0 +1,66 @@ +use crate::instruction::{Fill, Node, PString}; +use std::collections::HashMap; + +use crate::errors::ParseError; +type Result<T> = std::result::Result<T, ParseError>; + +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct Segment { + pub name: String, + pub start: u16, + pub size: usize, + pub fill_value: Option<Fill>, +} + +#[derive(Debug)] +pub struct Mapping { + pub segments: Vec<Segment>, + pub nodes: HashMap<String, Vec<Node>>, + current: String, +} + +impl Mapping { + pub fn new(mut segments: Vec<Segment>) -> Self { + segments.sort_by(|a, b| b.start.cmp(&a.start)); + + let mut nodes = HashMap::new(); + for segment in segments.iter() { + nodes.insert(segment.name.clone(), vec![]); + } + + let current_segment = &segments.first().unwrap().name.clone(); + + Mapping { + segments, + nodes, + current: current_segment.to_string(), + } + } + + pub fn reset(&mut self) { + // TODO + } + + pub fn switch(&mut self, id: &PString) -> Result<()> { + if !self.nodes.contains_key(&id.value) { + return Err( + id.parser_error(format!("segment '{}' has not been defined", id.value).as_str()) + ); + } + + id.value.clone_into(&mut self.current); + Ok(()) + } + + pub fn current(&self) -> &Vec<Node> { + self.nodes.get(&self.current).unwrap() + } + + pub fn current_mut(&mut self) -> &mut Vec<Node> { + self.nodes.get_mut(&self.current).unwrap() + } + + pub fn push(&mut self, node: Node) { + self.nodes.get_mut(&self.current).unwrap().push(node); + } +} |
