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.rs60
1 files changed, 59 insertions, 1 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index 40d2438..2b82008 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -247,6 +247,7 @@ impl<'a> Assembler<'a> {
};
let var_value = Object {
bundle: Bundle::fill(value),
+ node: None,
mapping: self.current_mapping,
segment: self.current_segment,
object_type: ObjectType::Value,
@@ -338,6 +339,7 @@ impl<'a> Assembler<'a> {
&node.value,
&Object {
bundle: value,
+ node: None,
mapping: self.current_mapping,
segment: self.current_segment,
object_type: ObjectType::Value,
@@ -481,6 +483,7 @@ impl<'a> Assembler<'a> {
resolved: false,
negative: false,
},
+ node: None,
mapping: self.current_mapping,
segment: self.current_segment,
object_type: ObjectType::Address,
@@ -774,6 +777,7 @@ impl<'a> Assembler<'a> {
self.literal_mode = None;
let obj = Object {
bundle: self.evaluate_node(arg)?,
+ node: Some(arg.clone()),
mapping: self.current_mapping,
segment: self.current_segment,
object_type: ObjectType::Value,
@@ -1571,6 +1575,7 @@ impl<'a> Assembler<'a> {
&args.last().unwrap().value,
&Object {
bundle: Bundle::fill(i as u8),
+ node: None,
mapping: self.current_mapping,
segment: self.current_segment,
object_type: ObjectType::Value,
@@ -1908,7 +1913,31 @@ impl<'a> Assembler<'a> {
fn evaluate_variable(&mut self, node: &PNode) -> Result<Bundle, Error> {
match self.context.get_variable(&node.value, &self.mappings) {
- Ok(value) => Ok(value.bundle),
+ Ok(mut value) => {
+ // If the given variable has a zero value AND we are resolving
+ // pending nodes AND the variable has a node object in it, then
+ // chances are that this is a macro call with an argument to be
+ // resolved. Hence, the value is not this empty variable but the
+ // node to be resolved which is contained on this "variable".
+ if self.stage == Stage::Crunching && value.bundle.is_zero() && value.node.is_some()
+ {
+ // Evaluate the node, which has the proper (resolved at this
+ // stage) value.
+ let bundle = self.evaluate_node(&value.node.clone().unwrap())?;
+
+ // Overwrite the variable with this new value. This way the
+ // next time this is found we don't have to evaluate it
+ // again. If the variable could not be set, then it's not
+ // that big of a deal at this stage.
+ value.bundle = bundle.clone();
+ let _ = self.context.set_variable(&node.value, &value, true);
+
+ // And return the computed bundle.
+ Ok(bundle)
+ } else {
+ Ok(value.bundle)
+ }
+ }
Err(e) => Err(Error {
message: e,
line: node.value.line,
@@ -3284,6 +3313,35 @@ jsr Movement::update
assert_eq!(res[4].bytes[2], 0x80);
}
+ #[test]
+ fn jmp_by_using_address_in_macro() {
+ let res = just_bundles(
+ r#"
+.macro JAL ADDR
+ jmp ADDR
+.endmacro
+
+JAL procedure
+
+.proc procedure
+ rts
+.endproc
+ "#,
+ );
+
+ assert_eq!(res.len(), 2);
+
+ // JAL procedure
+ assert_eq!(res[0].size, 3);
+ assert_eq!(res[0].bytes[0], 0x4C);
+ assert_eq!(res[0].bytes[1], 0x03);
+ assert_eq!(res[0].bytes[2], 0x80);
+
+ // rts
+ assert_eq!(res[1].size, 1);
+ assert_eq!(res[1].bytes[0], 0x60);
+ }
+
// Control statements
#[test]