aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/nasm/Cargo.toml1
-rw-r--r--crates/nasm/src/main.rs31
-rw-r--r--lib/xixanta/src/assembler.rs26
4 files changed, 58 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 05d7010..ca14786 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,6 +10,7 @@ version = "0.1.0"
name = "nasm"
version = "0.1.0"
dependencies = [
+ "header",
"xixanta",
]
diff --git a/crates/nasm/Cargo.toml b/crates/nasm/Cargo.toml
index f0f11db..f1294f7 100644
--- a/crates/nasm/Cargo.toml
+++ b/crates/nasm/Cargo.toml
@@ -6,3 +6,4 @@ authors = ["Miquel Sabaté Solà <mikisabate@gmail.com>"]
[dependencies]
xixanta.workspace = true
+header.workspace = true
diff --git a/crates/nasm/src/main.rs b/crates/nasm/src/main.rs
index 3864587..6ca6d27 100644
--- a/crates/nasm/src/main.rs
+++ b/crates/nasm/src/main.rs
@@ -1,3 +1,4 @@
+use header::Header;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
@@ -205,8 +206,36 @@ fn main() {
error_count += 1;
}
- // If everything was right, just deliver the bundles.
+ // Deliver the bundles unless the header is borked.
if error_count == 0 {
+ // Fetch the header first.
+ let mut temptative_header = vec![];
+ for b in &res.bundles {
+ for i in 0..b.size {
+ temptative_header.push(b.bytes[i as usize]);
+ }
+ if temptative_header.len() >= 0x10 {
+ break;
+ }
+ }
+
+ // Validate the resulting header.
+ match Header::try_from(temptative_header.as_slice()) {
+ Ok(header) => {
+ if res.accessing_working_ram && !header.has_persistent_memory {
+ die(
+ "requires Working RAM but the ROM header does not advertise it".to_string(),
+ );
+ }
+ }
+ Err(e) => {
+ die(format!(
+ "output would produce a malformed NES/Famicom ROM: {e}"
+ ));
+ }
+ };
+
+ // And now deliver the bundles.
for b in res.bundles {
for i in 0..b.size {
if let Err(e) = output.write_all(&[b.bytes[i as usize]]) {
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index 23d4219..5ab0e3d 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -83,6 +83,10 @@ struct Assembler<'a> {
// Sources that have been evaluated for the current session. This is
// directly tied to `Parser::sources`.
sources: Vec<SourceInfo>,
+
+ // Whether the assembler has detected accesses to Working RAM or not
+ // (0x6000-0x7FFF).
+ accessing_working_ram: bool,
}
/// All the information that a caller needs after calling either
@@ -104,6 +108,10 @@ pub struct AssemblerResult {
/// The resulting mappings after assembling a source. You can count on
/// fields like `offset` if `errors` is empty.
pub mappings: Vec<Mapping>,
+
+ /// Whether the resulting ROM needs Working RAM to be available in order to
+ /// work. Ignore this field if `errors` is not empty.
+ pub accessing_working_ram: bool,
}
/// Read the contents from the `reader` as a source file and produce a list of
@@ -131,6 +139,7 @@ pub fn assemble(
}],
warnings: vec![],
mappings: vec![],
+ accessing_working_ram: false,
};
}
};
@@ -161,6 +170,7 @@ pub fn assemble_with_mapping(
errors,
warnings: asm.warnings,
mappings: asm.mappings,
+ accessing_working_ram: asm.accessing_working_ram,
};
}
@@ -176,6 +186,7 @@ pub fn assemble_with_mapping(
errors: e.into(),
warnings: vec![],
mappings: asm.mappings,
+ accessing_working_ram: asm.accessing_working_ram,
};
}
}
@@ -188,6 +199,7 @@ pub fn assemble_with_mapping(
errors,
warnings: asm.warnings,
mappings: asm.mappings,
+ accessing_working_ram: asm.accessing_working_ram,
};
}
@@ -201,6 +213,7 @@ pub fn assemble_with_mapping(
errors,
warnings: asm.warnings,
mappings: asm.mappings,
+ accessing_working_ram: asm.accessing_working_ram,
};
}
@@ -212,6 +225,7 @@ pub fn assemble_with_mapping(
errors,
warnings: asm.warnings,
mappings: asm.mappings,
+ accessing_working_ram: asm.accessing_working_ram,
};
}
@@ -222,12 +236,14 @@ pub fn assemble_with_mapping(
errors: vec![],
warnings: asm.warnings,
mappings: asm.mappings,
+ accessing_working_ram: asm.accessing_working_ram,
},
Err(errors) => AssemblerResult {
bundles: vec![],
errors,
warnings: asm.warnings,
mappings: asm.mappings,
+ accessing_working_ram: asm.accessing_working_ram,
},
}
}
@@ -249,6 +265,7 @@ impl<'a> Assembler<'a> {
repeats_seen: 0,
warnings: vec![],
sources: vec![],
+ accessing_working_ram: false,
}
}
@@ -2003,6 +2020,15 @@ impl<'a> Assembler<'a> {
None => (AddressingMode::Implied, Bundle::new(true)),
};
+ // If the instruction is accessing memory and the bundle is resolved, we
+ // can start gathering info on the memory being used.
+ if bundle.resolved && !matches!(mode, AddressingMode::Implied | AddressingMode::Immediate) {
+ let value = bundle.value() as usize;
+ if (0x6000..0x8000).contains(&value) {
+ self.accessing_working_ram = true;
+ }
+ }
+
let mnemonic = node.value.value.to_lowercase();
match INSTRUCTIONS.get(&mnemonic) {
Some(entries) => match entries.get(&mode) {