aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/xixanta/src/assembler.rs105
-rw-r--r--lib/xixanta/src/node.rs14
-rw-r--r--lib/xixanta/src/parser.rs2
3 files changed, 114 insertions, 7 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index 4b496dc..7d09ec2 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -253,7 +253,16 @@ impl Assembler {
if self.can_bundle {
self.literal_mode = None;
match self.evaluate_node(node) {
- Ok(bundle) => {
+ Ok(mut bundle) => {
+ if node.is_branch() {
+ // TODO: it's a bit of a pity...
+ let current = &mut self.segments[self.current_segment];
+ bundle.address = current.offset;
+
+ if let Err(e) = self.to_relative_address(node, &mut bundle) {
+ errors.push(Error::Eval(e));
+ }
+ }
if let Err(e) = self.push_bundle(bundle, node) {
errors.push(Error::Eval(e));
}
@@ -292,9 +301,18 @@ impl Assembler {
self.context.force_context_switch(&pn.context);
match self.evaluate_node(&pn.node) {
- Ok(bundle) => {
+ Ok(mut bundle) => {
+ // TODO: oh boy
+ bundle.address = self.segments[pn.segment].bundles[pn.bundle_index].address;
+
+ if pn.node.is_branch() {
+ if let Err(e) = self.to_relative_address(&pn.node, &mut bundle) {
+ errors.push(Error::Eval(e));
+ }
+ }
+
let current = &mut self.segments[pn.segment];
- current.bundles[pn.bundle_index] = bundle;
+ current.bundles[pn.bundle_index].bytes = bundle.bytes;
}
Err(e) => errors.push(Error::Eval(e)),
}
@@ -372,8 +390,9 @@ impl Assembler {
}
// TODO: move
- fn push_bundle(&mut self, bundle: Bundle, node: &PNode) -> Result<(), EvalError> {
+ fn push_bundle(&mut self, mut bundle: Bundle, node: &PNode) -> Result<(), EvalError> {
let current = &mut self.segments[self.current_segment];
+ bundle.address = current.offset;
current.offset += bundle.size as usize;
if current.offset > current.size {
@@ -415,6 +434,8 @@ impl Assembler {
// just decimal values.
Ok(self.evaluate_decimal(node)?)
} else if node.value.is_valid_identifier(true).is_err() {
+ // TODO: relative labels
+ println!("NODE: {:#?}", node);
// If this is not a valid identifier, just error out.
Err(EvalError {
message: "no prefix was given to operand".to_string(),
@@ -978,7 +999,7 @@ impl Assembler {
let val = self.evaluate_node(left_arm)?;
match self.literal_mode {
Some(LiteralMode::Hexadecimal) => {
- if val.size == 1 {
+ if base.is_branch() || val.size == 1 {
Ok((AddressingMode::RelativeOrZeropage, val))
} else {
Ok((AddressingMode::Absolute, val))
@@ -1004,6 +1025,44 @@ impl Assembler {
}),
}
}
+
+ fn to_relative_address(&self, node: &PNode, bundle: &mut Bundle) -> Result<(), EvalError> {
+ if !bundle.resolved {
+ return Ok(());
+ }
+
+ let next = (bundle.address + 2) as u16;
+ let target = u16::from_le_bytes([bundle.bytes[1], bundle.bytes[2]]);
+
+ // println!(
+ // "NEXT: {:#?} -- TARGET: {:#?} -- BUNDLE: {:#?}",
+ // next, target, bundle
+ // );
+
+ let byte = if target < next {
+ let diff = target as i16 - next as i16;
+ if diff < -128 {
+ return Err(EvalError {
+ line: node.value.line,
+ message: "you cannot branch to this location: it's too far away".to_string(),
+ });
+ }
+ diff.to_le_bytes()[0]
+ } else {
+ let diff = target - next;
+ if diff > 127 {
+ return Err(EvalError {
+ line: node.value.line,
+ message: "you cannot branch to this location: it's too far away".to_string(),
+ });
+ }
+ diff.to_le_bytes()[0]
+ };
+
+ bundle.bytes[1] = byte;
+
+ Ok(())
+ }
}
#[cfg(test)]
@@ -1538,8 +1597,40 @@ nop
assert_eq!(res[2].bytes[2], 0x00);
}
- // TODO: anonymous jumps
- // TODO: labels & anonymous beq/blt/etc.
+ // TODO: anonymous jumps & branches
+
+ #[test]
+ fn conditional_branch_to_labels() {
+ let mut asm = Assembler::new(EMPTY.to_vec());
+ let res = asm
+ .assemble(
+ r#"
+nop
+@hello:
+ beq @hello
+ beq @end
+@end:
+ nop
+"#
+ .as_bytes(),
+ )
+ .unwrap();
+
+ assert_eq!(res.len(), 4);
+
+ // beq @hello
+ assert_eq!(res[1].size, 2);
+ assert_eq!(res[1].bytes[0], 0xF0);
+ assert_eq!(res[1].bytes[1], 0xFE);
+
+ // beq @end
+ assert_eq!(res[2].size, 2);
+ assert_eq!(res[2].bytes[0], 0xF0);
+ assert_eq!(res[2].bytes[1], 0x00);
+ }
+
+ // TODO: function calls
+ // TODO: labels and jumps inside of proc's, macros, etc.
// Control statements
diff --git a/lib/xixanta/src/node.rs b/lib/xixanta/src/node.rs
index f55ca11..a28d625 100644
--- a/lib/xixanta/src/node.rs
+++ b/lib/xixanta/src/node.rs
@@ -164,3 +164,17 @@ pub struct PNode {
/// optional arguments.
pub args: Option<Vec<PNode>>,
}
+
+impl PNode {
+ pub fn is_branch(&self) -> bool {
+ if self.node_type != NodeType::Instruction {
+ return false;
+ }
+
+ let opcode = self.value.value.to_lowercase();
+ matches!(
+ opcode.as_str(),
+ "bcc" | "bcs" | "beq" | "bmi" | "bne" | "bpl" | "bvc" | "bvs"
+ )
+ }
+}
diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs
index ec15edd..2c20369 100644
--- a/lib/xixanta/src/parser.rs
+++ b/lib/xixanta/src/parser.rs
@@ -4,6 +4,8 @@ use crate::opcodes::{CONTROL_FUNCTIONS, INSTRUCTIONS};
use std::cmp::Ordering;
use std::io::{self, BufRead, Read};
+// TODO: allow for labels a la ".Lwhatever:"
+
/// The Parser struct holds basic data for the current parsing session.
#[derive(Default)]
pub struct Parser {