aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src/assembler.rs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xixanta/src/assembler.rs')
-rw-r--r--lib/xixanta/src/assembler.rs108
1 files changed, 82 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,