aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/xixanta/src/assembler.rs60
-rw-r--r--lib/xixanta/src/object.rs16
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,