diff options
Diffstat (limited to 'lib/xixanta')
| -rw-r--r-- | lib/xixanta/src/assembler.rs | 60 | ||||
| -rw-r--r-- | lib/xixanta/src/object.rs | 16 |
2 files changed, 75 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] diff --git a/lib/xixanta/src/object.rs b/lib/xixanta/src/object.rs index db7c51e..373d06c 100644 --- a/lib/xixanta/src/object.rs +++ b/lib/xixanta/src/object.rs @@ -48,6 +48,16 @@ impl Bundle { } } + /// Returns true of this bundle has a zero value on its `bytes` attribute + /// while also taking into account the size of the bundle. + pub fn is_zero(&self) -> bool { + match self.size { + 1 => self.bytes[0] == 0, + 2 => self.bytes[0] == 0 && self.bytes[1] == 0, + _ => self.bytes[0] == 0 && self.bytes[1] == 0 && self.bytes[2] == 0, + } + } + /// Create a bundle tailored for filling purposes. pub fn fill(value: u8) -> Self { Self { @@ -105,6 +115,11 @@ pub struct Object { /// Bundle representing the actual value. pub bundle: Bundle, + /// Node which marks the source of the computed `bundle` attribute. This is + /// only provided in cases like macro calls where at the crunching stage we + /// might need to fetch previous context for the current `bundle` value. + pub node: Option<PNode>, + /// The mapping index where the object was found. Note that this index /// doesn't mean much on the table, but it has to mean something by the /// caller. @@ -124,6 +139,7 @@ impl Object { pub fn new(mapping: usize, segment: usize, object_type: ObjectType) -> Self { Self { bundle: Bundle::default(), + node: None, mapping, segment, object_type, |
