aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-22 21:46:03 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-22 21:56:45 +0100
commit4a7e8db884fdaa80f6a026ec4c0c8409ea975d81 (patch)
treed7d23256b75e9cfa20390c888212841fbd4e32c5 /lib/xixanta/src
parent179a68792e980a1d12bd67d57e593aab9f27ca30 (diff)
downloadtools.nes-4a7e8db884fdaa80f6a026ec4c0c8409ea975d81.tar.gz
tools.nes-4a7e8db884fdaa80f6a026ec4c0c8409ea975d81.zip
nasm: Implement the -D flag
This allows users to define variables directly from the command line, which is useful for testing purposes. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'lib/xixanta/src')
-rw-r--r--lib/xixanta/src/assembler.rs68
1 files changed, 64 insertions, 4 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index dfaff19..4ee769b 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -1,5 +1,5 @@
use crate::mapping::{get_mapping_configuration, Mapping};
-use crate::node::{ControlType, NodeType, OperationType, PNode};
+use crate::node::{ControlType, NodeType, OperationType, PNode, PString};
use crate::object::{Bundle, Context, Object, ObjectType};
use crate::opcodes::{AddressingMode, INSTRUCTIONS};
use crate::parser::Parser;
@@ -96,7 +96,12 @@ pub struct AssemblerResult {
/// statements like ".import" or ".incbin" wouldn't know how to resolve relative
/// paths. You can specify the mapper to be used as an identifier in `mapping`,
/// which will be handled via `get_mapping_configuration`.
-pub fn assemble(reader: impl Read, mapping: &str, source: SourceInfo) -> AssemblerResult {
+pub fn assemble(
+ reader: impl Read,
+ mapping: &str,
+ defines: &[(String, u8)],
+ source: SourceInfo,
+) -> AssemblerResult {
let config = match get_mapping_configuration(mapping) {
Ok(config) => config,
Err(e) => {
@@ -113,7 +118,7 @@ pub fn assemble(reader: impl Read, mapping: &str, source: SourceInfo) -> Assembl
}
};
- assemble_with_mapping(reader, config, source)
+ assemble_with_mapping(reader, config, defines, source)
}
/// Read the contents from the `reader` as a source file and produce a list of
@@ -125,6 +130,7 @@ pub fn assemble(reader: impl Read, mapping: &str, source: SourceInfo) -> Assembl
pub fn assemble_with_mapping(
reader: impl Read,
mapping: Vec<Mapping>,
+ defines: &[(String, u8)],
source: SourceInfo,
) -> AssemblerResult {
let mut asm = Assembler::new(mapping);
@@ -143,6 +149,18 @@ pub fn assemble_with_mapping(
let nodes = parser.nodes();
asm.sources = parser.sources;
+ // Before building a context, add variables if they were defined by the
+ // caller.
+ for (name, value) in defines {
+ if let Err(e) = asm.define_variable_value(name, *value) {
+ return AssemblerResult {
+ bundles: vec![],
+ errors: e.into(),
+ warnings: vec![],
+ };
+ }
+ }
+
// Build the context by iterating over the parsed nodes and checking
// where scopes start/end, evaluating values for variables, labels, etc.
if let Err(errors) = asm.eval_context(&nodes) {
@@ -210,6 +228,42 @@ impl<'a> Assembler<'a> {
}
}
+ /// Define a new variable by using the given `name` and `value`.
+ pub fn define_variable_value(&mut self, name: &String, value: u8) -> Result<(), Error> {
+ if name.is_empty() {
+ return Err(Error {
+ global: true,
+ line: 0,
+ source: self.sources.first().unwrap().clone(),
+ message: "empty variable name".to_string(),
+ });
+ }
+
+ let var_name = PString {
+ value: name.to_string(),
+ line: 0,
+ start: 0,
+ end: name.len(),
+ };
+ let var_value = Object {
+ bundle: Bundle::fill(value),
+ mapping: self.current_mapping,
+ segment: self.current_segment,
+ object_type: ObjectType::Value,
+ };
+
+ if let Err(err) = self.context.set_variable(&var_name, &var_value, false) {
+ Err(Error {
+ global: true,
+ line: 0,
+ source: self.sources.first().unwrap().clone(),
+ message: err,
+ })
+ } else {
+ Ok(())
+ }
+ }
+
// Define a new variable by taking the given `id`. This variable will only
// be created if `id` is not empty. The function will error out if the given
// name is already taken.
@@ -2201,7 +2255,7 @@ mod tests {
// the assembler will freak out.
let real_line = minimal_header().to_string() + line;
- assemble_with_mapping(real_line.as_bytes(), empty(), SourceInfo::default())
+ assemble_with_mapping(real_line.as_bytes(), empty(), &[], SourceInfo::default())
}
// Like `just_assemble` but it only returns bundles passed the header.
@@ -3965,6 +4019,7 @@ lda #Variable
"#
.as_bytes(),
one_two().to_vec(),
+ &[],
SourceInfo::default(),
);
@@ -4006,6 +4061,7 @@ lda #Variable
"#
.as_bytes(),
one_two().to_vec(),
+ &[],
SourceInfo::default(),
);
@@ -4079,6 +4135,7 @@ lda #Variable
"#
.as_bytes(),
one_two().to_vec(),
+ &[],
SourceInfo::default(),
);
@@ -4126,6 +4183,7 @@ lda #Variable
"#
.as_bytes(),
one_two().to_vec(),
+ &[],
SourceInfo::default(),
);
@@ -4180,6 +4238,7 @@ lda #Variable
"#
.as_bytes(),
one_two().to_vec(),
+ &[],
SourceInfo::default(),
);
@@ -4206,6 +4265,7 @@ lda #Variable
"#
.as_bytes(),
one_two().to_vec(),
+ &[],
SourceInfo::default(),
);