1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 08:55:38 +00:00

CLI: --memory-map flag

Dumps a human-readable table of variable allocations sorted by
address, separated into zero-page and main RAM sections with a
final byte-usage summary. Struct fields show up as individual
entries under their synthetic \`var.field\` names.

Example output for examples/structs_enums_for.ne:

    === NEScript Memory Map ===
    Zero Page (\$00-\$FF):
      \$00-\$0F  [SYSTEM]  reserved (frame flag, input, state, params, scratch)
      \$0010    [USER]    enemy_y (u8)
      \$0011    [USER]    i (u8)

    RAM (\$0200-\$07FF):
      \$0200-\$02FF  [SYSTEM]  OAM shadow buffer
      \$0300        [USER]    player.x (u8)
      \$0301        [USER]    player.y (u8)
      \$0302        [USER]    player.vx (u8)
      ...

    Zero Page: 2/128 bytes used
    Main RAM:  11/1280 bytes used

https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
Claude 2026-04-12 17:43:39 +00:00
parent 496925344d
commit db234a6ca7
No known key found for this signature in database
2 changed files with 80 additions and 7 deletions

View file

@ -938,11 +938,12 @@ nescript build game.ne --dump-ir
```
| Flag | Description |
|---------------|----------------------------------------------------------------|
|-----------------|----------------------------------------------------------------|
| `--output` | Set output ROM file path (default: input.nes) |
| `--debug` | Enable debug mode with runtime checks |
| `--asm-dump` | Dump generated 6502 assembly to stdout |
| `--dump-ir` | Dump the lowered IR program (after optimization) to stdout |
| `--memory-map` | Dump a memory map of variable allocations to stdout |
| `--use-ast` | Use the legacy AST-based codegen (default is the IR codegen) |
### Check

View file

@ -33,6 +33,11 @@ enum Cli {
#[arg(long)]
dump_ir: bool,
/// Dump a human-readable memory map of variable allocations
/// to stdout.
#[arg(long)]
memory_map: bool,
/// Use the legacy AST-based codegen. The default is the IR-based
/// codegen, which runs the optimizer passes before emitting 6502.
#[arg(long)]
@ -55,6 +60,7 @@ fn main() {
debug,
asm_dump,
dump_ir,
memory_map,
use_ast,
} => {
let output = output.unwrap_or_else(|| input.with_extension("nes"));
@ -64,6 +70,7 @@ fn main() {
debug,
asm_dump,
dump_ir,
memory_map,
use_ast,
},
) {
@ -89,6 +96,65 @@ fn main() {
}
}
/// Print a human-readable memory map of variable allocations.
/// Entries are sorted by address and labelled with their scope
/// (zero-page vs RAM).
fn print_memory_map(analysis: &nescript::analyzer::AnalysisResult) {
let mut allocs: Vec<_> = analysis.var_allocations.iter().collect();
allocs.sort_by_key(|a| a.address);
println!("=== NEScript Memory Map ===");
println!("Zero Page ($00-$FF):");
println!(" $00-$0F [SYSTEM] reserved (frame flag, input, state, params, scratch)");
for a in allocs.iter().filter(|a| a.address < 0x100) {
if a.size == 1 {
println!(" ${:04X} [USER] {} (u8)", a.address, a.name);
} else {
println!(
" ${:04X}-${:04X} [USER] {} ({} bytes)",
a.address,
a.address + a.size - 1,
a.name,
a.size
);
}
}
let ram_allocs: Vec<_> = allocs.iter().filter(|a| a.address >= 0x100).collect();
if !ram_allocs.is_empty() {
println!("\nRAM ($0200-$07FF):");
println!(" $0200-$02FF [SYSTEM] OAM shadow buffer");
for a in &ram_allocs {
if a.size == 1 {
println!(" ${:04X} [USER] {} (u8)", a.address, a.name);
} else {
println!(
" ${:04X}-${:04X} [USER] {} ({} bytes)",
a.address,
a.address + a.size - 1,
a.name,
a.size
);
}
}
}
// Summary line.
let zp_used: u16 = allocs
.iter()
.filter(|a| a.address < 0x80)
.map(|a| a.size)
.sum();
let ram_used: u16 = allocs
.iter()
.filter(|a| a.address >= 0x300)
.map(|a| a.size)
.sum();
println!();
println!("Zero Page: {zp_used}/128 bytes used");
println!("Main RAM: {ram_used}/1280 bytes used");
}
fn dump_asm(instructions: &[nescript::asm::Instruction]) {
use nescript::asm::{AddressingMode, Opcode};
for inst in instructions {
@ -112,6 +178,7 @@ struct CompileOptions {
debug: bool,
asm_dump: bool,
dump_ir: bool,
memory_map: bool,
use_ast: bool,
}
@ -119,6 +186,7 @@ fn compile(input: &PathBuf, opts: &CompileOptions) -> Result<Vec<u8>, ()> {
let debug = opts.debug;
let asm_dump = opts.asm_dump;
let dump_ir = opts.dump_ir;
let memory_map = opts.memory_map;
let use_ast = opts.use_ast;
let raw_source = std::fs::read_to_string(input).map_err(|e| {
eprintln!("error: failed to read {}: {e}", input.display());
@ -165,6 +233,10 @@ fn compile(input: &PathBuf, opts: &CompileOptions) -> Result<Vec<u8>, ()> {
print!("{}", ir_program.pretty());
}
if memory_map {
print_memory_map(&analysis);
}
// Resolve sprite assets (CHR data + tile indices) relative to the
// source file's directory, so `@binary` / `@chr` paths work naturally.
let source_dir = input.parent().unwrap_or_else(|| Path::new("."));