NERD automates a CDL-guided, bit-exact disassembly pipeline for NES ROMs: split the ROM → build a da65 info file from a Mesen 2 Code/Data Log → disassemble with da65 → reassemble with ca65/ld65 → verify the rebuilt ROM is byte-for-byte identical to the original. It wraps that workflow in a Python library, a CLI, and a desktop GUI.
Documentation: this README covers the essentials. For depth, see docs/USAGE.md (full CLI/GUI reference, getting a CDL from Mesen 2, troubleshooting), docs/ARCHITECTURE.md (how it works and why), docs/BUILDING.md (building standalone executables), and docs/CONTRIBUTING.md (contribution guide).
NERD is a reverse-engineering and educational tool for homebrew developers, students, and retro-computing enthusiasts who want to study or modify NES software they legally own or otherwise have the right to use: ROMs dumped from cartridges you own, homebrew, or public-domain releases.
- NERD does not download, host, crack, or distribute copyrighted ROM files. It only ever operates on a ROM already sitting on your own disk.
- You're responsible for using it only with ROMs you're legally entitled to use, under whatever copyright law applies to you.
- This project isn't affiliated with, and doesn't condone, piracy. If you plan on sharing a modification, distribute it as an IPS/BPS patch against the original ROM (the standard, copyright-respecting convention in the ROM-hacking community) rather than a full modified ROM. The bundled
.gitignoredeliberately excludes ROMs and ROM-derived build artifacts so you don't accidentally commit/push them.
NERD orchestrates da65, ca65, and ld65 from the cc65 suite, it doesn't bundle them, so this is a separate install regardless of how you get NERD itself in step 2.
- Debian / Ubuntu / WSL:
sudo apt update sudo apt install cc65
- macOS, via Homebrew:
brew install cc65
- Windows: there's no package-manager one-liner. Download the latest Windows build from cc65's GitHub releases, extract it somewhere permanent (e.g.
C:\cc65), then add itsbinfolder to yourPATH: Settings → search "environment variables" → Edit the system environment variables → Environment Variables → edit thePathvariable → addC:\cc65\bin. Restart any open terminal windows afterward, sincePATHchanges don't apply to ones already running. - Other Linux distributions: check your package manager first (
dnf search cc65,pacman -Ss cc65, etc.); if it isn't packaged, see the NESdev wiki's cc65 install guide for building from source.
Confirm it's on PATH before moving on:
da65 --versionPick one:
Option A, prebuilt executable (fastest, no Python needed): download nerd-windows-x64.exe, nerd-macos-arm64, or nerd-linux-x64 from the latest release. On Windows/Linux it's a console-mode executable, so running it from a terminal shows normal output, and double-clicking it (no arguments) launches the GUI with a console window alongside it. See docs/BUILDING.md for how these are made.
Option B, from source (needed if you want to edit NERD itself, or there's no prebuilt for your platform):
- Install Python 3.10 or later, if you don't already have it: python.org for Windows/macOS, or your distro's package manager for Linux (e.g.
sudo apt install python3). On Windows, tick "Add python.exe to PATH" during setup, or the commands below won't be found by your terminal. Check what you have:python3 --version
- If you want the GUI, make sure Tkinter is available. It's bundled with the standard python.org/Microsoft Store installers on Windows/macOS; on some Linux distributions it's a separate package:
sudo apt install python3-tk. - Clone and install:
git clone https://github.com/Hexadecinull/NERD.git cd NERD pip install -e .
nerd checkThis confirms da65/ca65/ld65 are found. If it reports anything missing, revisit step 1. Once it's clean, you're ready. See Usage below, or docs/USAGE.md for the full walkthrough including getting a CDL log from Mesen 2.
- Split: reads the iNES/NES 2.0 header and splits the ROM into
header.bin,prg.bin, andchr.bin(sizes are read from the header, not hard-coded). - CDL → RANGE: providing a
.cdlfile exported from Mesen 2's Code/Data Logger is highly recommended, not just a nice-to-have. NERD converts its per-byte code/data flags intoda65RANGEdirectives, soda65knows what's genuine 6502 code versus a data table. It recognizes Mesen 2'sCDLv2format directly and cross-checks the CRC32 embedded in it against yourprg.bin, so a CDL log from the wrong ROM gets caught instead of silently mis-aligning. - Disassemble: generates a
game.infofile (standard PPU/APU/vector labels + the CDL ranges) and runsda65to produceprg.s. - Reassemble & verify: assembles
main.swithca65(including a trainer segment when the ROM has one), links it withld65against a generatednes.cfg, and SHA-256-compares the result against your original ROM.
CDL data is technically optional, but treat that as "the pipeline won't refuse to run without it," not as "it works fine without it." Without a CDL file, da65 falls back to its own default heuristics, which (verified while building this tool, not just assumed) can misidentify data tables as code badly enough that ca65 fails to reassemble the result at all (typically a relative-branch "Range error"), rather than just being less readable. Real NES games mix code and data too densely for reliable static disassembly without a runtime trace; a CDL log is what actually makes the bit-exact rebuild work in practice, so generate one in Mesen 2 first unless you're disassembling something trivial.
What's supported: any ROM whose PRG-ROM is ≤32KB, which is what actually determines whether it fits in a single linearly-disassembled $8000–$FFFF block. CHR data is never disassembled, it's copied through as a raw binary include, so its size (including CHR-RAM, i.e. no CHR-ROM at all) doesn't matter. A 512-byte trainer, if present, is split out and reassembled too. Mapper number by itself isn't a hard gate: this pipeline was validated end-to-end (bit-exact SHA-256 match) against a commercial mapper 1 (MMC1) game with 32KB PRG, not just mapper 0/NROM. What NERD doesn't yet support is PRG-ROM larger than 32KB, i.e. carts that bank-switch more PRG than fits in one window. That needs per-bank splitting and a bank-switching-aware linker config, which is a good first contribution if you're interested.
See docs/ARCHITECTURE.md for the full reasoning behind these constraints, the CDLv2 format spec, and other design decisions.
nerd(or python -m nerd) launches the desktop app with no arguments. Pick your ROM and (optionally) your CDL file, choose an output folder, and click Run Full Pipeline. Progress streams into the log pane in real time; the window stays responsive since the pipeline runs on a background thread.
# Full pipeline: split, disassemble, reassemble, and verify
nerd run game.nes --cdl game.cdl -o build/
# Disassembly only (no rebuild/verify)
nerd disasm game.nes --cdl game.cdl -o build/
# Rebuild and verify an existing project directory against the original ROM
nerd verify build/ game.nes
# Check that da65/ca65/ld65 are installed and on PATH
nerd check
# Print the installed NERD version
nerd --versionnerd run exits 0 on a verified bit-exact match, 1 on a pipeline error, and 2 if the rebuild succeeded but didn't match, useful for CI. See docs/USAGE.md for the full CLI/GUI reference, every flag, the output-file reference, and troubleshooting.
.github/
├── workflows/
│ ├── ci.yml # ruff + mypy + pytest, on every push/PR
│ ├── codeql.yml # CodeQL security scanning (Python)
│ └── build.yml # PyInstaller executables for Win/macOS/Linux, on tags
├── ISSUE_TEMPLATE/ # bug report / feature request / mapper support forms
├── PULL_REQUEST_TEMPLATE.md
└── dependabot.yml # weekly, grouped updates for pip (dev deps) + github-actions
docs/
├── USAGE.md # full CLI/GUI reference, Mesen 2 CDL walkthrough, troubleshooting
├── ARCHITECTURE.md # how it works, and why, module by module
├── BUILDING.md # building standalone executables
└── CONTRIBUTING.md # contribution guide, incl. how to add mapper support
scripts/
└── pyinstaller_entry.py # absolute-import shim used only for PyInstaller builds
nerd.spec # PyInstaller build spec (see docs/BUILDING.md)
nerd/
├── core/
│ ├── rom.py # iNES/NES 2.0 header parsing + ROM splitting
│ ├── cdl.py # CDL parsing (incl. CDLv2 + CRC check) → da65 RANGE directives
│ ├── info_gen.py # game.info generation
│ ├── build.py # ca65/ld65 invocation + SHA-256 verification
│ ├── pipeline.py # orchestrates the full workflow
│ └── tools.py # da65/ca65/ld65 discovery
├── resources/
│ └── labels.py # standard PPU/APU/vector LABEL definitions
├── gui/
│ └── app.py # Tkinter desktop interface
└── cli.py # command-line interface
tests/ # pytest, unit tests plus a real-toolchain integration test
.gitattributes # LF normalization + binary handling for ROM/CDL formats
git clone https://github.com/Hexadecinull/NERD.git
cd NERD
pip install -e ".[dev]" # adds pytest, ruff, mypy
ruff check . # lint
ruff format . # format
mypy nerd # type-check
pytest tests/ -v # unit tests + a real-toolchain integration test (needs cc65; auto-skips without it)Four things run automatically:
- CI (
ci.yml): lints withruff, type-checks withmypy, and runs the test suite (installingcc65first so the integration test actually exercisesda65/ca65/ld65, not just skips). - CodeQL (
codeql.yml): GitHub's static security analysis, on every push/PR plus a weekly scheduled scan. - Build Executables (
build.yml): builds standalonenerdbinaries for Windows/macOS/Linux via the committednerd.specwhenever av*tag is pushed (or manually via workflow dispatch), and attaches them to a GitHub release. See docs/BUILDING.md for the packaging gotcha (pathex/editable installs) this works around. - Dependabot (
dependabot.yml): weekly, grouped update PRs for Python dev dependencies and GitHub Actions versions, with a short cooldown so a freshly-published version has a few days to prove itself first.
Issues and PRs are welcome. Bank-switched mapper support (for PRG >32KB: MMC1, MMC3, UxROM, CNROM, ...) is the biggest gap and a great place to start. See docs/CONTRIBUTING.md for the full guide: dev setup, coding conventions, the copyrighted-ROM-fixtures rule, and a concrete walkthrough of where mapper support would need to change. Issue templates are set up for bug reports, feature requests, and mapper support requests specifically.
MIT. See LICENSE.
Built on top of the cc65 toolchain and Mesen 2's Code/Data Logger.