aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-10 16:35:12 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2025-01-10 16:35:12 +0100
commit7c09adae9e2710aefccd25b2920ca684c714ef49 (patch)
tree45870865c7700e28d092469ee9d850cc70f3f292
parent343b2a41a36286a5f950116c183cd2ae54aad1bd (diff)
downloadtools.nes-7c09adae9e2710aefccd25b2920ca684c714ef49.tar.gz
tools.nes-7c09adae9e2710aefccd25b2920ca684c714ef49.zip
Introduce xa65
This is a bridge between 'nasm' and 'cl65'. That is, it runs the same command on both 'nasm' and 'cl65', compares the resulting binaries, and gives back the binary from 'cl65'. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
-rw-r--r--Cargo.lock9
-rw-r--r--README.md9
-rw-r--r--crates/xa65/Cargo.toml10
-rw-r--r--crates/xa65/src/main.rs124
4 files changed, 152 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index bbc3dbf..9d9cd47 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -453,6 +453,15 @@ dependencies = [
]
[[package]]
+name = "xa65"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "rand",
+]
+
+[[package]]
name = "xixanta"
version = "0.1.0"
dependencies = [
diff --git a/README.md b/README.md
index 8b4c08a..c39e9f5 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,15 @@ Alternatively, you can also pass a path to a configuration of your own. Check
out the [ones already bundled](./lib/xixanta/src/mappings) on this application
for reference.
+## `xa65`
+
+Since `nasm` is still under heavy development, it's a good idea to compare the
+results that it produces with a mature and stable assembler like
+[cc65](https://github.com/cc65/cc65). The purpose of `xa65` is to provide a
+bridge, and so it simply executes both `nasm` and `cc65` with the given
+arguments. If the results from both assemblers are not the same, then it will
+display a warning and produce the binary as taken from `cc65`.
+
## `readrom`
The `readrom` program reads a given ROM file and shows all the information that
diff --git a/crates/xa65/Cargo.toml b/crates/xa65/Cargo.toml
new file mode 100644
index 0000000..f268943
--- /dev/null
+++ b/crates/xa65/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "xa65"
+version = "0.1.0"
+edition = "2021"
+authors = ["Miquel Sabaté Solà <mikisabate@gmail.com>"]
+
+[dependencies]
+anyhow = "^1"
+clap = { version = "^4", features = ["derive"] }
+rand = "0.8.5"
diff --git a/crates/xa65/src/main.rs b/crates/xa65/src/main.rs
new file mode 100644
index 0000000..9cb8a70
--- /dev/null
+++ b/crates/xa65/src/main.rs
@@ -0,0 +1,124 @@
+use anyhow::{bail, Context, Result};
+use clap::Parser as ClapParser;
+use rand::distributions::{Alphanumeric, DistString};
+use std::path::PathBuf;
+use std::process::Command;
+
+/// Bridge between 'nasm' and 'ca65'.
+#[derive(ClapParser, Debug)]
+#[command(version, about, long_about = None)]
+struct Args {
+ /// Assemble the instructions given on this file.
+ file: String,
+
+ /// Linker configuration to be used. This configuration can be an identifier
+ /// for the configurations already baked in into this application, or it can
+ /// be a file path to a configuration of your choosing. See the
+ /// documentation for more information on this format. Defaults to 'nrom'.
+ #[arg(short = 'c', long)]
+ config: Option<String>,
+
+ /// Place the output into the given <OUT> file.
+ #[arg(short = 'o', long)]
+ out: String,
+}
+
+// Find the binary by `name` in "PATH". Implementation taken from:
+// https://stackoverflow.com/a/37499032.
+fn find_binary(name: &str) -> Option<PathBuf> {
+ std::env::var_os("PATH").and_then(|paths| {
+ std::env::split_paths(&paths)
+ .filter_map(|dir| {
+ let full_path = dir.join(name);
+ if full_path.is_file() {
+ Some(full_path)
+ } else {
+ None
+ }
+ })
+ .next()
+ })
+}
+
+// Returns the path for the binaries for 'nasm' and 'cl65'.
+fn get_binaries() -> Result<(PathBuf, PathBuf)> {
+ let nasm = match find_binary("nasm") {
+ Some(nasm) => nasm,
+ None => bail!("could not find 'nasm'".to_string()),
+ };
+ let cl65 = match find_binary("cl65") {
+ Some(cl65) => cl65,
+ None => bail!("could not find 'cl65'".to_string()),
+ };
+
+ Ok((nasm, cl65))
+}
+
+fn main() -> Result<()> {
+ // Make sure that the binaries are there.
+ let (nasm, cl65) = get_binaries()?;
+ let args = Args::parse();
+
+ // Generate a temporary directory in which both binary files will be placed
+ // as an intermediate step.
+ let random_string = &Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
+ let dir = std::env::temp_dir().join(random_string);
+ std::fs::create_dir(&dir)?;
+
+ // Run 'nasm' with the given arguments. Note that we don't care whether
+ // 'nasm' itself errors out.
+ let _ = Command::new(nasm)
+ .arg(&args.file)
+ .arg("-o")
+ .arg(dir.join("nasm.nes"))
+ .arg("-c")
+ .arg(args.config.clone().unwrap_or("nrom65".to_string()))
+ .status()
+ .with_context(|| "could not execute 'nasm'")?;
+
+ // Run 'cl65' with the given arguments.
+ let mut cl65_command = Command::new(cl65);
+ cl65_command
+ .arg("--target")
+ .arg("nes")
+ .arg(&args.file)
+ .arg("-o")
+ .arg(dir.join("cl65.nes"));
+ if let Some(config) = &args.config {
+ cl65_command.arg("-c").arg(config);
+ }
+
+ // Here, and in contrast with the 'nasm' execution, we do care about the
+ // exit code of 'cl65'.
+ let out = cl65_command
+ .status()
+ .with_context(|| "could not execute 'cl65'")?;
+ if !out.success() {
+ std::process::exit(1);
+ }
+
+ // Everything went fine, we should have both binaries available to be
+ // compared. For 'diff' actually capture the output so it does not pollute
+ // the shell.
+ let diff = Command::new("diff")
+ .arg(dir.join("nasm.nes"))
+ .arg(dir.join("cl65.nes"))
+ .output()
+ .with_context(|| "failed to run diff")?;
+
+ // If 'diff' failed, show it but don't error out.
+ if !diff.status.success() {
+ println!(
+ "xa65 (error): 'nasm' and 'ca65' have a mismatch. Check the results at {}",
+ dir.display()
+ );
+ }
+
+ // And just copy one of the binaries to where it was originally requested.
+ // Note that the binary is the one from 'cl65' just in case 'diff' failed
+ // (we take 'cl65' as the source of truth).
+ std::fs::copy(dir.join("cl65.nes"), args.out)
+ .with_context(|| "could not copy the resulting binary")?;
+
+ Ok(())
+}