Decompile NES ROM files (.nes) into C# projects that can be rebuilt with dotnes. Use this skill whenever the user wants to decompile a ROM, reverse-engineer a .nes file, convert a ROM to C#, extract code from a NES ROM, create a project from an existing ROM, or do a round-trip test (transpile → decompile → retranspile). Also use when the user says things like "decompile this ROM", "turn this .nes into C#", "extract the code", "what does this ROM do", "reverse engineer", or "round-trip". This is the high-level decompilation tool — for low-level 6502 disassembly, use nes-rom-debug instead.
Decompile .nes ROM files into complete C# projects that can be rebuilt through dotnes.
The decompiler lives at src/dotnes.decompiler/ and is a .NET CLI tool.
dotnet run --project src/dotnes.decompiler -- <input.nes> [output-directory]
<input.nes> — Path to the NES ROM file (required)[output-directory] — Where to write the C# project (optional; defaults to the ROM filename without extension)Examples:
# Decompile a sample ROM (build it first if needed)
cd samples/hello && dotnet build && cd ../..
dotnet run --project src/dotnes.decompiler -- samples/hello/bin/Debug/net10.0/hello.nes hello-decompiled
# Decompile any .nes ROM
dotnet run --project src/dotnes.decompiler -- path/to/game.nes game-decompiled
# Decompile with default output directory (uses ROM filename)
dotnet run --project src/dotnes.decompiler -- myrom.nes
The decompiler generates a complete, buildable C# project:
| File | Description |
|---|---|
Program.cs | Decompiled C# source with recovered NESLib API calls |
{name}.csproj | MSBuild project with NES configuration from ROM metadata |
chr_generic.s | CHR ROM data as ca65 assembly (only if ROM has CHR banks) |
The decompiler recognizes these NESLib API calls:
| Category | Functions |
|---|---|
| Palette | pal_col(palette, color) |
| VRAM | vram_adr(NTADR_A(x, y)), vram_write("string") |
| PPU control | ppu_on_all(), ppu_on_bg(), ppu_on_spr(), ppu_off() |
| Sprites/OAM | oam_clear(), oam_size(), oam_hide_rest() |
| CHR banking | bank_spr(), bank_bg() |
| Input | pad_poll(0) |
| Timing | delay(frames), waitvsync() |
| Random | rand8(), set_rand() |
String literals in vram_write() are recovered from ROM data when the bytes are printable ASCII.
VRAM addresses passed to vram_adr() are decompiled back to NTADR_A(x, y) macro form when they match the nametable pattern 0x2000 + y*32 + x.
Build the sample, then decompile its ROM output:
# Build the sample
cd samples/hello && dotnet build && cd ../..
# Decompile the ROM
dotnet run --project src/dotnes.decompiler -- samples/hello/bin/Debug/net10.0/hello.nes hello-decompiled
# Inspect the output
cat hello-decompiled/Program.cs
Verify the decompiler produces code that builds back to an equivalent ROM:
# 1. Build original sample
cd samples/hello && dotnet build && cd ../..
# 2. Decompile the ROM
dotnet run --project src/dotnes.decompiler -- samples/hello/bin/Debug/net10.0/hello.nes hello-roundtrip
# 3. Build the decompiled project
cd hello-roundtrip && dotnet build && cd ..
# 4. Compare the two ROMs
python scripts/compare_rom.py samples/hello/bin/Debug/net10.0/hello.nes hello-roundtrip/bin/Debug/net10.0/hello.nes
If the round-trip produces an identical ROM, the decompiler recovered all observable behavior.
Any iNES-format ROM can be parsed, but the decompiler only recognizes dotnes/neslib patterns:
dotnet run --project src/dotnes.decompiler -- external-game.nes game-output
cat game-output/Program.cs
For ROMs not built with dotnes, the output will contain fewer recognized API calls and more comments for unrecognized subroutines.
Use the decompiler's console output to quickly check ROM properties:
dotnet run --project src/dotnes.decompiler -- myrom.nes 2>&1 | Select-Object -First 6
This shows PRG/CHR bank counts, mapper number, mirroring mode, and interrupt vectors.
The decompiler uses a three-phase algorithm:
main() by scanning the initlib block for the first JSR to an address past the built-insJMP to self)LDA #imm / JSR pusha → 8-bit argument pushLDA #lo / LDX #hi / JSR pushax → 16-bit pointer pushLDX #hi / LDA #lo / JSR <sub> → call with 16-bit register argumentLDA #imm / JSR <sub> → call with immediate argumentThe decompiler reads ROM metadata and sets appropriate MSBuild properties:
| Property | Condition | Example |
|---|---|---|
NESMapper | Mapper ≠ 0 | <NESMapper>1</NESMapper> |
NESPrgBanks | PRG banks ≠ 2 | <NESPrgBanks>4</NESPrgBanks> |
NESChrBanks | CHR banks ≠ 1 | <NESChrBanks>2</NESChrBanks> |
NESVerticalMirroring | Mirroring is vertical | <NESVerticalMirroring>true</NESVerticalMirroring> |
PyTorch深度学习模式与最佳实践,用于构建稳健、高效且可复现的训练流程、模型架构和数据加载。