Generate zoomable SVG maps of SNES ROM and WRAM from linker map files, with optional machine-readable CSV output.
Supports llvm-mos (ld.lld), vbcc65816 (vlink), cc65 (ca65/ld65), wla-dx, and asar; the input format is auto-detected. Linker maps carry exact sizes; assembler symbol files (wla-dx, asar) carry only addresses, so their sizes are inferred (see below).
The repository provides two command-line scripts:
rommap.pycreates a physical-bank ROM treemap.rammap.pycreates an address-preserving WRAM overview with low-WRAM and direct-page zooms.
Both are thin entry points to snesmap.py, the shared engine that does the map
parsing, layout, and rendering; they simply preset ROM or RAM mode. Keeping one
engine means the ~95% of logic the two maps share lives in a single place, while
you still get the two commands you'd expect. (snesmap.py is not meant to be run
directly.)
All three use only the Python 3 standard library. Their SVG output supports light and dark colour schemes, browser zooming, and per-allocation tooltips.
Run the scripts from the directory containing your linker script:
python3 /path/to/rommap.py build/game.map \
--linker-script game.ld \
-o build/rommap
python3 /path/to/rammap.py build/game.map \
-o build/rammapEach command writes an SVG by default:
build/rommap.svg
build/rammap.svg
Pass --csv to also write the machine-readable CSV (build/rommap.csv,
build/rammap.csv).
The format is auto-detected. Override detection with --format, one of lld,
vlink, ca65, wla, or asar.
The ROM map displays sixteen physical 32 KiB banks in SNES reading order. Bank headers show allocated bytes and a right-aligned percentage. Symbol rectangles are proportional within each bank; transparent rectangles are free space.
For llvm-mos/ld.lld builds, ROM region names, origins, and capacities come from
the linker script's MEMORY block. Regions below $00:8000 and WRAM banks
$7E/$7F are excluded.
The RAM script derives its fixed WRAM ranges directly and does not require a linker script.
The existing rom_bank_fixed convention is recognised: its contents are shown
in every code_bank_* physical bank. Other linker-region names have no special
meaning.
The RAM map separates static linker allocation from runtime usage:
- A true-scale overview displays the full 128 KiB WRAM.
- A magnified panel displays
$7E:0100–1FFF. - A second magnified panel displays the default direct page,
$7E:0000–00FFwithD=$0000. - Allocation order and address gaps are preserved.
RAM colours identify initialized .data, zero-filled .bss, .noinit,
hardware stack reservation, compiler direct-page registers, and other
allocations. Unallocated space is transparent by default.
When present in an ld.lld map, __rcN and __stack linker symbols are used to
account for compiler direct-page registers and the page-$01 hardware stack.
Dynamic stack and heap high-water usage cannot be inferred from a static linker
map.
Options apply independently to either script:
--checkerboard
--colour-key # alias: --color-key
--coloured-percentages # alias: --colored-percentages--checkerboarduses a Photoshop-style pattern for free space.--colour-keyadds the colour legend.--coloured-percentagesshows 75–89.9% in amber and 90%+ in red.
All three are off by default.
Use --compiler "toolchain name" to override the compiler/linker label embedded
in the SVG footer.
Use --delimiter to change the field separator used throughout the SVG — headers,
tooltips, captions, and the footer (e.g. $00 / 31,333 B / 95.6% full). It is
space-padded, and the default is a bullet (·), e.g. --delimiter "/".
- llvm-mos/ld.lld map output, with ROM regions read from a GNU-style
MEMORYblock. - vbcc65816/vlink section mappings.
- vlink LoROM, plus the vlink HiROM address projection used by
vlink-hi. - cc65 ca65/ld65: the
ld65 --mapfileSegment list for exact per-segment sizes. Optionally pass your cc65 linker config (the same.cfgyou build with,cl65 -C/ld65 -C) as--linker-scriptto name the ROM regions; without it the map falls back to physical banks. - wla-dx / asar symbol files (
wlalink.sym,asar --symbols=wla|nocash): label→address only, so LoROM physical banks are assumed and sizes are inferred from label gaps (the last label in a bank runs to the bank end). Trailing free space inside a bank is therefore invisible; the footer marks these maps(approx sizes).
See docs/linker-map-support.md for assumptions and known limitations.
Run the test suite:
python3 -m unittest discover -s tests -vThe tests invoke both scripts against small checked-in fixtures for each supported format (ld.lld, vlink, ca65/ld65, wla-dx, and asar) and validate their CSV and SVG output.
The physical-bank ROM view was inspired by Pin Eight's Homebrew ROM maps and treemaps.
MIT. See LICENSE.