Skip to content

Commit

Permalink
Vastly improve assembler and removed lots of Boxes
Browse files Browse the repository at this point in the history
Signed-off-by: Miquel Sabaté Solà <[email protected]>
  • Loading branch information
mssola committed Oct 9, 2024
1 parent 47ea0f3 commit c90a0d4
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 278 deletions.
117 changes: 51 additions & 66 deletions lib/xixanta/src/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::mapping::Segment;
use crate::opcodes::INSTRUCTIONS;
use crate::parser::Parser;
use crate::parser::{NodeType, PNode};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::io::Read;

Expand Down Expand Up @@ -33,12 +34,12 @@ pub struct Assembler {

impl Assembler {
pub fn new(segments: Vec<Segment>) -> Self {
assert!(segments.len() > 0);
assert!(!segments.is_empty());

// TODO
let mut offsets = HashMap::new();
for segment in &segments {
offsets.insert(segment.name.clone(), 0);
for segment in segments {
offsets.insert(segment.name, 0);
}

// TODO
Expand All @@ -53,7 +54,7 @@ impl Assembler {
// First of all, parse the input so we get a list of nodes we can work
// with.
self.stage = Stage::Parsing;
let mut parser = Parser::new();
let mut parser = Parser::default();
if let Err(errors) = parser.parse(reader) {
return Err(errors.iter().map(|e| Error::Parse(e.clone())).collect());
}
Expand All @@ -72,21 +73,21 @@ impl Assembler {
self.bundle(&parser.nodes)
}

pub fn eval_context(&mut self, nodes: &Vec<Box<PNode>>) -> Result<(), Vec<Error>> {
pub fn eval_context(&mut self, nodes: &Vec<PNode>) -> Result<(), Vec<Error>> {
let mut errors = Vec::new();

for node in nodes {
match node.node_type {
NodeType::Assignment => match self.evaluate_node(&node.left.clone().unwrap()) {
NodeType::Assignment => match self.evaluate_node(node.left.as_ref().unwrap()) {
Ok(value) => {
if let Err(err) = self.context.set_variable(&node.value, &value.unwrap()) {
if let Err(err) = self.context.set_variable(&node.value, &value) {
errors.push(Error::Context(err));
}
}
Err(e) => errors.push(Error::Eval(e)),
},
NodeType::Control => {
if let Err(err) = self.context.change_context(&node) {
if let Err(err) = self.context.change_context(node) {
errors.push(Error::Context(err));
}
}
Expand All @@ -101,22 +102,18 @@ impl Assembler {
}
}

pub fn bundle(&mut self, nodes: &Vec<Box<PNode>>) -> Result<Vec<Bundle>, Vec<Error>> {
pub fn bundle(&mut self, nodes: &Vec<PNode>) -> Result<Vec<Bundle>, Vec<Error>> {
let mut bundles = Vec::new();
let mut errors = Vec::new();

for node in nodes {
match node.node_type {
NodeType::Instruction => match self.evaluate_node(&node) {
Ok(optional_bundle) => {
if let Some(bundle) = optional_bundle {
bundles.push(bundle);
}
}
NodeType::Instruction => match self.evaluate_node(node) {
Ok(bundle) => bundles.push(bundle),
Err(e) => errors.push(Error::Eval(e)),
},
NodeType::Control => {
if let Err(err) = self.context.change_context(&node) {
if let Err(err) = self.context.change_context(node) {
errors.push(Error::Context(err));
}
}
Expand All @@ -131,21 +128,20 @@ impl Assembler {
}
}

// TODO: maybe drop the Option here?
fn evaluate_node(&mut self, node: &Box<PNode>) -> Result<Option<Bundle>, EvalError> {
fn evaluate_node(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
match node.node_type {
NodeType::Instruction => Ok(Some(self.evaluate_instruction(node)?)),
NodeType::Literal => Ok(Some(self.evaluate_literal(node)?)),
NodeType::Instruction => Ok(self.evaluate_instruction(node)?),
NodeType::Literal => Ok(self.evaluate_literal(node)?),
NodeType::Value => match self.literal_mode {
Some(LiteralMode::Hexadecimal) => Ok(Some(self.evaluate_hexadecimal(node)?)),
Some(LiteralMode::Binary) => Ok(Some(self.evaluate_binary(node)?)),
Some(LiteralMode::Plain) => Ok(Some(self.evaluate_decimal(node)?)),
Some(LiteralMode::Hexadecimal) => Ok(self.evaluate_hexadecimal(node)?),
Some(LiteralMode::Binary) => Ok(self.evaluate_binary(node)?),
Some(LiteralMode::Plain) => Ok(self.evaluate_decimal(node)?),
None => {
// If we are just evaluating the context (e.g. parsing a
// variable), we'll assume that non-prefixed literals are
// just decimal values.
if self.stage == Stage::Context {
Ok(Some(self.evaluate_decimal(node)?))
Ok(self.evaluate_decimal(node)?)
} else {
Err(EvalError {
message: "no prefix was given to operand".to_string(),
Expand All @@ -161,7 +157,7 @@ impl Assembler {
}
}

fn evaluate_hexadecimal(&mut self, node: &Box<PNode>) -> Result<Bundle, EvalError> {
fn evaluate_hexadecimal(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
let mut chars = node.value.value.chars();
let mut bytes = [0, 0, 0];
let size: u8;
Expand Down Expand Up @@ -206,7 +202,7 @@ impl Assembler {
})
}

fn evaluate_binary(&mut self, node: &Box<PNode>) -> Result<Bundle, EvalError> {
fn evaluate_binary(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
let string = node.value.value.as_str();
let mut value = 0;
let mut shift = 0;
Expand All @@ -225,28 +221,26 @@ impl Assembler {
shift += 1;
}

if shift < 8 {
Err(EvalError {
match shift.cmp(&8) {
Ordering::Less => Err(EvalError {
message: "missing binary digits to get a full byte".to_string(),
line: node.value.line,
})
} else if shift > 8 {
Err(EvalError {
}),
Ordering::Greater => Err(EvalError {
message: "too many binary digits for a single byte".to_string(),
line: node.value.line,
})
} else {
Ok(Bundle {
}),
Ordering::Equal => Ok(Bundle {
bytes: [value as u8, 0, 0],
size: 1,
address: 0,
cycles: 0,
affected_on_page: false,
})
}),
}
}

fn evaluate_decimal(&mut self, node: &Box<PNode>) -> Result<Bundle, EvalError> {
fn evaluate_decimal(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
let string = node.value.value.as_str();
if string.is_empty() {
return Err(EvalError {
Expand Down Expand Up @@ -308,7 +302,7 @@ impl Assembler {
})
}

fn evaluate_literal(&mut self, node: &Box<PNode>) -> Result<Bundle, EvalError> {
fn evaluate_literal(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
// The value of the literal is guaranteed to not be empty by the parser.
// If that's not the case, then it's a bug.
let val = node.value.value.as_str();
Expand Down Expand Up @@ -341,13 +335,13 @@ impl Assembler {

// And evaluate the left node.
self.literal_mode = lm.clone();
let expr = self.evaluate_node(&left)?;
let expr = self.evaluate_node(left)?;
self.literal_mode = lm;

Ok(expr.unwrap())
Ok(expr)
}

fn char_to_hex(&mut self, oc: Option<char>, source: &Box<PNode>) -> Result<u8, EvalError> {
fn char_to_hex(&mut self, oc: Option<char>, source: &PNode) -> Result<u8, EvalError> {
match oc {
Some(c) => match c.to_digit(16) {
Some(c) => Ok(c as u8),
Expand All @@ -363,10 +357,10 @@ impl Assembler {
}
}

fn evaluate_instruction(&mut self, node: &Box<PNode>) -> Result<Bundle, EvalError> {
fn evaluate_instruction(&mut self, node: &PNode) -> Result<Bundle, EvalError> {
let (mode, mut bundle) = match &node.left {
Some(_) => self.get_addressing_mode_and_bytes(node)?,
None => (AddressingMode::Implied, Bundle::new()),
None => (AddressingMode::Implied, Bundle::default()),
};

let mnemonic = node.value.value.to_lowercase();
Expand Down Expand Up @@ -402,7 +396,7 @@ impl Assembler {

fn get_addressing_mode_and_bytes(
&mut self,
node: &Box<PNode>,
node: &PNode,
) -> Result<(AddressingMode, Bundle), EvalError> {
let left = &node.left;

Expand All @@ -415,10 +409,7 @@ impl Assembler {
}
}

fn get_from_indirect(
&mut self,
node: &Box<PNode>,
) -> Result<(AddressingMode, Bundle), EvalError> {
fn get_from_indirect(&mut self, node: &PNode) -> Result<(AddressingMode, Bundle), EvalError> {
let left = node.left.as_ref().unwrap();

match node.right.as_ref() {
Expand All @@ -433,7 +424,7 @@ impl Assembler {
});
}

let val = self.evaluate_node(&left.left.as_ref().unwrap())?.unwrap();
let val = self.evaluate_node(left.left.as_ref().unwrap())?;
if val.size != 1 {
return Err(EvalError {
message: "address can only be one byte long on indirect Y addressing"
Expand All @@ -443,15 +434,15 @@ impl Assembler {
}
return Ok((AddressingMode::IndirectY, val));
}
return Err(EvalError {
Err(EvalError {
message: "only the Y index is allowed on indirect Y addressing".to_string(),
line: node.value.line,
});
})
}
None => match left.right.as_ref() {
Some(right) => {
if right.value.value.trim().to_lowercase() == "x" {
let val = self.evaluate_node(&left.left.as_ref().unwrap())?.unwrap();
let val = self.evaluate_node(left.left.as_ref().unwrap())?;
if val.size != 1 {
return Err(EvalError {
message:
Expand All @@ -462,32 +453,29 @@ impl Assembler {
}
return Ok((AddressingMode::IndirectX, val));
}
return Err(EvalError {
Err(EvalError {
message: "only the X index is allowed on indirect X addressing".to_string(),
line: node.value.line,
});
})
}
None => {
let val = self.evaluate_node(&left.left.as_ref().unwrap())?.unwrap();
let val = self.evaluate_node(left.left.as_ref().unwrap())?;
if val.size != 2 {
return Err(EvalError {
message: "expecting a full 16-bit address".to_string(),
line: node.value.line,
});
}
return Ok((AddressingMode::Indirect, val));
Ok((AddressingMode::Indirect, val))
}
},
}
}

fn get_from_indexed(
&mut self,
node: &Box<PNode>,
) -> Result<(AddressingMode, Bundle), EvalError> {
fn get_from_indexed(&mut self, node: &PNode) -> Result<(AddressingMode, Bundle), EvalError> {
// Evaluate the left arm of the instruction.
let left = node.left.as_ref().unwrap();
let val = self.evaluate_node(&left)?.unwrap();
let val = self.evaluate_node(left)?;

// Ensure that the literal mode for the left arm ensures an address
// instead of some bogus number.
Expand Down Expand Up @@ -524,15 +512,12 @@ impl Assembler {
}
}

fn get_from_left(
&mut self,
left_arm: &Box<PNode>,
) -> Result<(AddressingMode, Bundle), EvalError> {
fn get_from_left(&mut self, left_arm: &PNode) -> Result<(AddressingMode, Bundle), EvalError> {
if left_arm.value.value.to_lowercase().trim() == "a" {
return Ok((AddressingMode::Implied, Bundle::new()));
return Ok((AddressingMode::Implied, Bundle::default()));
}

let val = self.evaluate_node(&left_arm)?.unwrap();
let val = self.evaluate_node(left_arm)?;
match self.literal_mode {
Some(LiteralMode::Hexadecimal) => {
if val.size == 1 {
Expand Down
6 changes: 3 additions & 3 deletions lib/xixanta/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Context {
}

/// Change the current context given a `node`.
pub fn change_context(&mut self, node: &Box<PNode>) -> Result<(), ContextError> {
pub fn change_context(&mut self, node: &PNode) -> Result<(), ContextError> {
// The parser already guarantees that the control node is
// from a function that we already know, so calling `unwrap`
// is not dangerous.
Expand All @@ -77,7 +77,7 @@ impl Context {

// Pushes a new context given a `node`, which holds the identifier of the
// new scope.
fn context_push(&mut self, id: &Box<PNode>) {
fn context_push(&mut self, id: &PNode) {
let name = match self.stack.last() {
Some(n) => format!("{}::{}", n, id.value.value),
None => id.value.value.clone(),
Expand Down Expand Up @@ -114,7 +114,7 @@ impl Context {
fn to_human(&self) -> String {
match self.stack.last() {
Some(n) => format!("'{}'", n),
None => format!("the global scope"),
None => "the global scope".to_string(),
}
}
}
Loading

0 comments on commit c90a0d4

Please sign in to comment.