diff options
| author | Miquel Sabaté Solà <mssola@mssola.com> | 2026-04-28 13:33:23 +0200 |
|---|---|---|
| committer | Miquel Sabaté Solà <mssola@mssola.com> | 2026-04-28 13:33:23 +0200 |
| commit | 83670aee223a5b7e8b1eb196e2f6b8635b99ed3f (patch) | |
| tree | dd8a0ae44b958c96ac6ca69c0e12d01b8ebc0033 /crates/nasm | |
| parent | 98c8f0561d65f81b269400a35a99b64800207486 (diff) | |
| download | tools.nes-83670aee223a5b7e8b1eb196e2f6b8635b99ed3f.tar.gz tools.nes-83670aee223a5b7e8b1eb196e2f6b8635b99ed3f.zip | |
nasm: add the --split-segments option
This allows the programmer to tell nasm to leave each segment into its
own .out file. This can be useful when writing some specific segments
into different chips.
The caveat is that filling is not applied, and hence it's up to the
programmer to account for that if two or more segments happened to be on
the same mapping and needed some alignment in between.
Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
Diffstat (limited to 'crates/nasm')
| -rw-r--r-- | crates/nasm/src/main.rs | 103 |
1 files changed, 71 insertions, 32 deletions
diff --git a/crates/nasm/src/main.rs b/crates/nasm/src/main.rs index 63d7e9d..3def44b 100644 --- a/crates/nasm/src/main.rs +++ b/crates/nasm/src/main.rs @@ -1,6 +1,6 @@ use header::Header; use std::fs::{create_dir, File}; -use std::io::{self, Write}; +use std::io::{self, BufWriter, Write}; use std::path::{Path, PathBuf}; use xixanta::assembler::{assemble, MemoryResult}; use xixanta::mapping::Mapping; @@ -17,6 +17,7 @@ struct Args { werror: bool, stdout: bool, stats: bool, + split: bool, defines: Vec<(String, u8)>, asan: bool, info: bool, @@ -36,6 +37,7 @@ fn print_help() { println!(" -o, --out <FILE>\tFile path where the output should be located after execution."); println!(" --prelude\t\tPrint the 'prelude' file; a file that can be used for defining default implementations for nasm-only features."); println!(" -s, --stats\t\tPrint the statistics on the final layout of segments and memory."); + println!(" --split-segments\tSave each segment into a different file named {{SEGMENT}}.out"); println!(" --stdout\t\tPrint the output binary to the standard output."); println!(" -v, --version\t\tPrint the version of this program."); println!( @@ -137,6 +139,7 @@ fn parse_arguments() -> Args { }, "-s" | "--stats" => res.stats = true, "-w" | "--write-info" => res.info = true, + "--split-segments" => res.split = true, "--stdout" => match res.out { Some(_) => die("you cannot mix '-o/--out' and '--stdout'".to_string()), None => { @@ -307,6 +310,8 @@ fn main() { // Select the output stream. let (mut output, output_name): (Box<dyn Write>, &str) = if args.stdout { (Box::new(io::stdout()), "<stdout>") + } else if args.split { + (Box::new(io::stdout()), "<segments>") } else { let name = args.out.unwrap_or(String::from("out.nes")); match File::create(&name) { @@ -345,42 +350,76 @@ fn main() { let mut has_working_ram = false; - // 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]); + // And now deliver the bundles. This can be done with 'split', in which + // case we have to deliver the segments into different files, or with + // the regular output in which everything will be dumped into 'output'. + if args.split { + for mapping in &res.mappings { + for segment in &mapping.segments { + let name = &segment.name; + let path = format!("{name}.out"); + + // Open the file and truncate it if it already exists. + let file = match File::create(&path) { + Ok(f) => f, + Err(e) => { + eprintln!("error: could not create '{path}': {e}"); + std::process::exit(1); + } + }; + + // And write the contents for this segment/file with + // buffering. + let mut writer = BufWriter::new(file); + for b in &segment.bundles { + if let Err(e) = writer.write_all(&b.bytes[..b.size as usize]) { + eprintln!("error: could not write to '{path}': {e}"); + std::process::exit(1); + } + } + if let Err(e) = writer.flush() { + eprintln!("error: could not write to '{path}': {e}"); + std::process::exit(1); + } + } } - if temptative_header.len() >= 0x10 { - break; + } else { + // 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(), - ); + // Validate the header before delivering the final ROM.. + 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(), + ); + } + has_working_ram = header.has_persistent_memory; } - has_working_ram = header.has_persistent_memory; - } - 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]]) { - eprintln!("error: could not write result in '{output_name}': {e}"); - std::process::exit(1); + Err(e) => { + die(format!( + "output would produce a malformed NES/Famicom ROM: {e}" + )); + } + }; + + for b in res.bundles { + for i in 0..b.size { + if let Err(e) = output.write_all(&[b.bytes[i as usize]]) { + eprintln!("error: could not write result in '{output_name}': {e}"); + std::process::exit(1); + } } } } |
