aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-16 16:14:40 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-16 16:14:40 +0100
commita3a630dfee207c111213d2cad85ceb983df7625a (patch)
tree314d3c55e62f437596648704fbd7a81d2c332e44 /lib/xixanta/src
parent161d7b406b7fba75a2057b8d9e780f521b9bc6bf (diff)
downloadtools.nes-a3a630dfee207c111213d2cad85ceb983df7625a.tar.gz
tools.nes-a3a630dfee207c111213d2cad85ceb983df7625a.zip
Implement .def/.defined expressions
This can be combined with .if/.elsif statements just like any other expression. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib/xixanta/src')
-rw-r--r--lib/xixanta/src/assembler.rs53
-rw-r--r--lib/xixanta/src/node.rs2
-rw-r--r--lib/xixanta/src/opcodes.rs2
3 files changed, 57 insertions, 0 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index ce34983..d0a0e6e 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -1550,6 +1550,7 @@ impl<'a> Assembler<'a> {
match node.node_type {
NodeType::Control(ControlType::Hibyte) => self.evaluate_byte(node, true),
NodeType::Control(ControlType::Lobyte) => self.evaluate_byte(node, false),
+ NodeType::Control(ControlType::Defined) => self.evaluate_defined(node),
_ => Err(Error {
line: node.value.line,
message: format!(
@@ -1585,6 +1586,24 @@ impl<'a> Assembler<'a> {
Ok(bundle)
}
+ // Returns a bundle containing a boolean value on whether the symbol
+ // referenced in the given `node` is actually defined on this context or
+ // not.
+ fn evaluate_defined(&mut self, node: &PNode) -> Result<Bundle, Error> {
+ // Guaranteed by the parser to be only one argument.
+ let name = node.args.as_ref().unwrap().first().unwrap();
+
+ if self
+ .context
+ .get_variable(&name.value, &self.mappings)
+ .is_ok()
+ {
+ Ok(Bundle::fill(0x01))
+ } else {
+ Ok(Bundle::fill(0x00))
+ }
+ }
+
fn push_evaluated_arguments(&mut self, node: &PNode, nbytes: u8) -> Result<(), Error> {
match &node.args {
Some(args) => {
@@ -3372,6 +3391,40 @@ jsr Movement::update
);
}
+ #[test]
+ fn defined() {
+ let res = just_bundles(
+ r#"Var = 0
+.if .defined(Var)
+ lda #1
+.else
+ lda #0
+.endif
+
+.if .defined(Var1)
+ lda #1
+.else
+ lda #0
+.endif
+
+.if !.defined(Var1)
+ lda #1
+.else
+ lda #0
+.endif
+"#,
+ );
+
+ assert_eq!(res.len(), 3);
+
+ let instrs: Vec<[u8; 2]> = vec![[0xA9, 0x01], [0xA9, 0x00], [0xA9, 0x01]];
+ for i in 0..instrs.len() {
+ assert_eq!(res[i].size, 2);
+ assert_eq!(res[i].bytes[0], instrs[i][0]);
+ assert_eq!(res[i].bytes[1], instrs[i][1]);
+ }
+ }
+
// Macros
#[test]
diff --git a/lib/xixanta/src/node.rs b/lib/xixanta/src/node.rs
index ae79f1e..f1eb558 100644
--- a/lib/xixanta/src/node.rs
+++ b/lib/xixanta/src/node.rs
@@ -147,6 +147,7 @@ pub enum ControlType {
Elsif,
Else,
EndIf,
+ Defined,
}
impl fmt::Display for ControlType {
@@ -174,6 +175,7 @@ impl fmt::Display for ControlType {
ControlType::Elsif => write!(f, ".elsif"),
ControlType::Else => write!(f, ".else"),
ControlType::EndIf => write!(f, ".endif"),
+ ControlType::Defined => write!(f, ".defined"),
}
}
}
diff --git a/lib/xixanta/src/opcodes.rs b/lib/xixanta/src/opcodes.rs
index 8488b77..80878ec 100644
--- a/lib/xixanta/src/opcodes.rs
+++ b/lib/xixanta/src/opcodes.rs
@@ -761,6 +761,8 @@ lazy_static! {
functions.insert(String::from(".elsif"), Control { control_type: ControlType::Elsif, has_identifier: None, required_args: Some((1, 1)), touches_context: false, only_string: false });
functions.insert(String::from(".else"), Control { control_type: ControlType::Else, has_identifier: None, required_args: Some((0, 0)), touches_context: false, only_string: false });
functions.insert(String::from(".endif"), Control { control_type: ControlType::EndIf, has_identifier: None, required_args: Some((0, 0)), touches_context: false, only_string: false });
+ functions.insert(String::from(".def"), Control { control_type: ControlType::Defined, has_identifier: None, required_args: Some((1, 1)), touches_context: false, only_string: false });
+ functions.insert(String::from(".defined"), Control { control_type: ControlType::Defined, has_identifier: None, required_args: Some((1, 1)), touches_context: false, only_string: false });
functions
};