aboutsummaryrefslogtreecommitdiff
path: root/crates/nasm
diff options
context:
space:
mode:
Diffstat (limited to 'crates/nasm')
-rw-r--r--crates/nasm/Cargo.toml9
-rw-r--r--crates/nasm/src/main.rs80
2 files changed, 89 insertions, 0 deletions
diff --git a/crates/nasm/Cargo.toml b/crates/nasm/Cargo.toml
new file mode 100644
index 0000000..e1f3e88
--- /dev/null
+++ b/crates/nasm/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "nasm"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.82"
+clap = { version = "4.5.4", features = ["derive"] }
+xixanta.workspace = true
diff --git a/crates/nasm/src/main.rs b/crates/nasm/src/main.rs
new file mode 100644
index 0000000..9f26417
--- /dev/null
+++ b/crates/nasm/src/main.rs
@@ -0,0 +1,80 @@
+use anyhow::Result;
+use clap::Parser as ClapParser;
+use std::fs::File;
+use std::io::{self, Read, Write};
+use xixanta::assembler::Assembler;
+
+/// Assembler for the 6502 microprocessor that targets the NES.
+#[derive(ClapParser, Debug)]
+#[command(version, about, long_about = None)]
+struct Args {
+ /// Assemble the instructions given on this file. The standard input is used
+ /// when this argument is not given.
+ file: Option<String>,
+
+ /// Disassemble instead of assembling. Disabled by default.
+ #[arg(short, long, default_value_t = false)]
+ disassemble: bool,
+
+ /// Place the output into the given <OUT> file. Ignored if the `stdout` flag
+ /// is provided. Defaults to `out.nes` when assembling, and to `out.s` when
+ /// disassembling.
+ #[arg(short = 'o', long)]
+ out: Option<String>,
+
+ /// Spit the output into the standard output instead. This ignores any given
+ /// `out` flag. Disabled by default.
+ #[arg(long, default_value_t = false)]
+ stdout: bool,
+}
+
+fn main() -> Result<()> {
+ let args = Args::parse();
+ let mut assembler = Assembler::new();
+
+ // Select the input stream.
+ let input: Box<dyn Read> = match args.file {
+ Some(file) => Box::new(File::open(file)?),
+ None => Box::new(std::io::stdin()),
+ };
+
+ // Select the output stream.
+ let mut output: Box<dyn Write> = if args.stdout {
+ Box::new(io::stdout())
+ } else {
+ match args.out {
+ Some(file) => Box::new(File::create(file)?),
+ None => Box::new(File::create(if args.disassemble {
+ "out.s"
+ } else {
+ "out.nes"
+ })?),
+ }
+ };
+
+ // After the parse operation, just print the results.
+ if args.disassemble {
+ let instructions = assembler.disassemble(input)?;
+
+ for instr in instructions {
+ output.write_all(instr.to_human().as_bytes())?;
+ output.write_all("\n".as_bytes())?;
+ }
+ } else {
+ let instructions = assembler.assemble(input)?;
+
+ for instr in instructions {
+ let bs = instr.to_bytes();
+
+ if instr.size() == 1 {
+ output.write_all(&[bs[0]])?;
+ } else if instr.size() == 2 {
+ output.write_all(&[bs[0], bs[1]])?;
+ } else {
+ output.write_all(&[bs[0], bs[1], bs[2]])?;
+ }
+ }
+ }
+
+ Ok(())
+}