===========
What it’s supposed to be
===========
smcdis is a disassembler and explorer for SNES ROM images.
Since ROM images are unformatted, it uses abstract interpretation
to find all possible function entry points and tell apart text and
data sections.
===========
What it actually does
===========
Figures out the ROM format and original dumper hardware for (most)
images, and prints out a lot of headers, with more accurate meanings
than all other SNES emulators I tried (including bsnes).
Maps LoROM and HiROM images, then traces every code path reachable
from the exception vectors — following branches and calls, and
tracking the E/M/X status flags so immediate operand sizes decode
correctly — and prints a labeled disassembly.
Build with `make` (or the Xcode project). `make sanitize` builds
with ASan/UBSan.
===========
TODO
===========
- More memory maps: Sufami Turbo…
- Tell apart code and data; find data referenced by instructions
- Code that games copy or decompress into WRAM and run there (Secret
of Mana, FF5, Rudra, and parts of many others) needs a load-image
analysis: model the copy loops/MVN/DMA and trace the materialized
bytes. - Flag tracking is per-state now: every (address, M, X) combination
is traced, divergent decodes are listed as ~ alternates, subroutine
exit states propagate to call sites, and interrupt handlers are tried
both 8- and 16-bit. What’s left is flags that come from data (PLP of
a computed value, task switchers) – real dataflow territory. - Task schedulers that stash entry PCs in records and RTS into them
(Mega Man 7) need the same dataflow.
===========
Architecture debts
===========
From a model design review of everything above, ranked; pay these
down before or during the WRAM-image and SPC700-upload work:
- Recovery passes communicate by mutating one shared decode database
(including erasing instructions to mean “this is data”). Should be
typed facts with provenance that rules subscribe to. - The mapper knows one ROM backing and hands out raw pointers; WRAM
images, SRAM and I/O need region identity (“what’s here and where
did it come from”), not just a byte pointer. - 65816 semantics and per-run state are file-static globals in
disasm.cpp; an Isa + session split would have made SPC700 a plug-in
(spc700.cpp dodges this by being its own little world, which is the
cheap version of the same idea). - The coverage benchmark checks addresses, not whether the listing
picked the decode matching the runtime M/X state; traces should
carry CPU + state + edges. - Which decode is “primary” depends on traversal order; should be a
stated rendering policy. - Subroutine exit summaries are keyed only by callee; fine for M/X,
too coarse once DBR/direct-page tracking arrives. - The 2014 sketch’s known-bits idea (doc/2014-tracer-sketch.diff) is
implemented: flags carry a known bitfield and the tracer forks both
widths where an unknown one is used. Extending knownness to values
(registers, cells) is the remaining half.
===========
Documentation
===========
See doc/.
A particularly useful file that was too large to include can be found
at http://www.romhacking.net/docs/226/.