Reverse-engineering and byte-identical reconstruction of seven official NES Pac-Man revisions from one shared ca65 source, with Japan V1.0 retained as the default preservation baseline.
The project is an annotated, native ca65 reconstruction of the game's single 6502 PRG bank, plus tooling that rebuilds the ROM, compares it byte for byte against the original, and replays a longplay movie in an instrumented FCEUX to catch behavioural regressions frame by frame.
Base bank_FF.asm reference: cyneprepou4uk/NES-Games-Disassembly - Pac-Man
Source Reconstruction 2.1 is the current release contract. It retains the 1.0
preservation contract and the 2.0 authoring, seven-revision, and regional-runtime
scope while adding stricter evidence, symbolic relocation, and a normalized
repository interface. See
docs/source_reconstruction_2_1.md for the
release contract. Its tag name is source-reconstruction-2.1; the preserved
predecessor tags are source-reconstruction-1.0 and
source-reconstruction-2.0.
Source Reconstruction 2.2 is the tag-ready compatible-minor candidate. Its project
manifest preserves the published 2.1 tag, records the modernization delta,
enforces newline-format-independent text and FM2 runtime inputs, records the
reviewed task-oriented documentation corpus, authenticates every selected
assembler and linker before use, and exposes
make source-2-2-functional-check for the accepted predecessor plus the current
delta. The corrected functional aggregate passed before the final metadata
transition. The complete pre-tag gate is make source-2-2-check; it must pass
on the exact candidate commit, and publication and tag creation remain owner
actions.
The annotated source covers every major subsystem, milestone 23 has resolved
every registered unknown, and the complete validation matrix is available
through make source-2-1-check. Future uncertainty remains governed by
the evidence rules in the unknowns registry. See
docs/source_reconstruction_1_0.md for the
original preservation contract and evidence summary.
The optional NROM-256 content-authoring workflow is complete. Four focused local applications edit sound and music, the maze, CHR and actor mappings, palettes, screens, English game text, HUD data, and intermission visuals. They share validated ignored-local assets and one deterministic expanded-ROM build pipeline. A future unified Qt application is tracked as a low-priority convenience milestone; it is not required to use or maintain the current tools.
Seven official cartridge profiles rebuild byte-identically from the shared
source. make verify-revisions checks every locally available reference, while
make smoke-revisions directly boots every profile in FCEUX and validates its
title/OAM behavior. ROM images remain local and ignored.
The source is split into address-ordered subsystem modules containing real 6502 instructions and ca65 data directives. Further reverse engineering can deepen provisional annotations without weakening the preservation target.
Modules are divided at natural procedure and data boundaries and remain below
600 lines. docs/source_layout.md is the detailed
address-to-file map.
Repeated domain operations use a small set of byte-preserving ca65 macros under
src/macros/; the project deliberately avoids hiding ordinary 6502 instructions
behind generic syntax aliases. See docs/macros.md.
Symbols are named by program role rather than ROM address. The sub_,
handler_, loc_, and bra_ prefixes distinguish callable subroutines from
dispatch handlers and internal control flow; see docs/naming.md.
The complete import-to-current label history is maintained separately in
config/reconstruction/label_renames.json.
make symbols generates native ld65 source mappings for Mesen, FCEUX ROM/RAM
labels, breakpoint groups, and a standard watch list. See
docs/debugger_workflow.md.
Focused natural and controlled FCEUX traces cover scoring, lifecycle, ghost
release/mode changes, all intermissions, pause, player handoff, and sound byte
classes. See docs/runtime_trace_scenarios.md.
The gate for every change is make verify: rebuild the ROM from
src/main.asm and assert byte-identity with the original. Annotation must never
alter the assembled bytes, so any diff means the edit was wrong.
The repository previously also contained a C reimplementation. It was removed —
see docs/postmortem.md for what was tried and why it did
not work.
See CONTRIBUTING.md for local-input policy, assembly naming
and formatting rules, evidence expectations, and the validation gates required
for source, data, or revision changes.
git clone <repo>
cd pacman_src
# Place the original ROM in the project root:
# Pac-Man (J) (V1.0) [!].nes
make split
make verifyA successful verification reports
[OK] Byte-identical ROM reproduced from native ca65 source.
Run make split once to validate the reference ROM and extract the ignored CHR,
maze, and audio assets described by assets/manifest.json. This command is
explicit because it overwrites those local asset files with the original data.
make build assembles src/main.asm and its modules directly with ca65/ld65.
It only checks that all required assets exist and never extracts or overwrites
them, so locally edited assets can be used for ROM hacks.
make verify performs the same native build and fails unless the result is
byte-identical to the reference ROM.
pacman_src/
|-- .editorconfig # Cross-editor text and ASM indentation rules
|-- CONTRIBUTING.md # Change policy and validation workflow
|-- assets/
| |-- manifest.json # Tracked extraction ranges and checksums
| `-- generated/ # Ignored CHR, maze, and audio payloads
|-- bin/ # ca65 / ld65
|-- build/ # Generated ROM and linker artifacts
|-- config/ # Emulator/reference/linker configuration
| `-- linker/ # ld65 layouts
|-- content/
| `-- workspace/ # Ignored private editor documents
|-- docs/ # Architecture and RE notes
|-- mk/ # Authoring, runtime, and validation recipes
|-- movies/ # FM2 inputs for automated capture
|-- scenarios/ # Runtime, scoring, and revision smoke cases
|-- scripts/
| |-- authoring/ # Codecs, editor models, and Studios
| |-- build/ # Builds, profiles, assets, and cleanup
| |-- runtime/ # Emulator capture and evidence validation
| |-- validation/ # Lint, symbols, release, and policy checks
| |-- workflow/ # Analysis and reporting tools
| `-- run.py # Stable direct-tool dispatcher
|-- src/
| |-- main.asm # Address-ordered ca65 entrypoint
| |-- expanded/nrom256.asm # JSON-backed NROM-256 entrypoint
| |-- variants/stage5.asm # Isolated behavior-changing entrypoint
| |-- macros/ # Byte-preserving ca65 abstractions
| |-- system/
| |-- game/
| |-- rendering/
| |-- audio/
| |-- data/
| |-- memory/
| `-- revisions/
|-- tests/ # Mirrors the script responsibility groups
|-- Makefile
`-- Pac-Man (J) (V1.0) [!].nes # Original ROM (not distributed)
assets/generated/ and build/ are generated artifacts and are not tracked.
Private editor documents live under ignored content/workspace/; legacy local
artifact directories remain ignored but are not touched by make clean. All
source tools under scripts/ are tracked and grouped by responsibility, and
the test tree mirrors those groups. Tool responsibilities, public commands,
and test owners are pinned by config/tooling_layout.json; see
docs/tooling_layout.md. See
docs/assets.md for the source-versus-asset policy.
make help is generated from the checked public-target registry. Use
make scaffold-check for the ROM-less disposable-checkout smoke and
make ui-smoke for real-window interaction coverage of all four Studios.
make # Same as `make build`
make build # Build the native ca65 ROM
make verify # Build and require byte-identity
make build-revision REVISION=europe # Build one official revision
make verify-revision REVISION=europe # Verify one official revision
make verify-revisions # Verify every available official revision
make smoke-revisions # Build and boot every official revision in FCEUX
make build-hack # Build the isolated default ROM-hack variant
make verify-hack # Require only its documented byte difference
make validate-hack # Prove its stage-5 behavior in FCEUX
make run-hack # Build and run the default hack
make init-expanded-assets # Initialize all editable local JSON once
make build-expanded # Build the JSON-backed NROM-256 variant
make verify-expanded # Verify assets, layout, and fixed-bank operands
make validate-expanded # Prove expanded assets are consumed in FCEUX
make run-expanded # Build and run the NROM-256 variant
make sound-studio # Open the local slot editor and piano roll
make maze-studio # Open the local 27x22 CHR-backed maze editor
make graphics-studio # Open the local CHR and metasprite editor
make screen-studio # Open the title, text, HUD, and intermission editor
make describe-sound SOUND_SLOT=4 # Inspect decoded musical notes
make preview-sound SOUND_SLOT=4 # Render an ignored WAV preview
make import-midi MIDI_FILE=x.mid # Import monophonic MIDI to ignored JSON
make symbols # Generate Mesen/FCEUX debugger artifacts
make test-debug-symbols # Test symbol parsing and conversion
make validate-symbols # Prove live symbol lookup and named breakpoint
make format # Normalize ca65 assembly source style
make lint # Check assembly, naming, docs, and Python syntax
make test # Run all focused Python workflow tests
make roundtrip-formats # Decode/encode six binary format families
make reconstruction-audit # Run the complete Source Reconstruction 1.0 gate
make reconstruction-audit-2 # Run the strict Source Reconstruction 2.0 gate
make source-2-1-audit # Check the Source 2.1 repository contract
make source-2-1-check # Run the tag-ready Source 2.1 gate
make source-2-1-post-tag-audit # Verify the annotated Source 2.1 tag
make trace-scoring # Capture semantic scoring events
make validate-scoring-trace # Revalidate an existing scoring trace
make trace-runtime # Capture and validate focused gameplay traces
make validate-runtime-traces # Revalidate existing gameplay traces
make trace-evidence # Recapture resolved research evidence
make validate-evidence # Revalidate existing evidence
make test-relocation # Shift modules and run FCEUX regression gates
make run # Build and run the ROM in FCEUX
make split # Extract CHR, maze, and audio from the original ROM
make build-dev # Check tools and clone/build FCEUX if needed
make reference # Capture the reference set from the original ROM
make analyze COUNT=32 # Run RTS reverse-engineering analysis
make chunk START=260 LINES=60 # Prepare a rename/analysis chunk
make clean # Remove local build and analysis artifacts
make help # Show the public targetsmake reference is intentionally explicit because a full reference capture is
expensive. Run it before make analyze. make clean also removes generated
reference, diff, report, and workflow directories, but never removes the FCEUX
checkout or the original ROM.
- Annotate a subsystem module under
src/(or select a logical flattened chunk withmake chunk). - Run
make verify— it must stay byte-identical. Annotation never changes assembled bytes, so any diff is a mistake in the edit. - Use
make referenceandmake analyzewhen runtime evidence is needed.
- Python 3
ca65/ld65(bundled inbin/and pinned byconfig/toolchain.json)fceux_automation— a fork of FCEUX with headless capture, reference comparison and state dumping, cloned and built at the pinned source commit bymake build-dev- MSBuild / Visual Studio 2022 (to build
fceux_automationon Windows)
- The unknowns registry preserves resolved research evidence and remains the canonical place for any future open findings. Neutral names are required whenever evidence is incomplete instead of guessing a semantic role.
make verifypermanently represents the byte-identical Japan V1.0 baseline. Behavior-changing work is already supported through the isolated fixed-size workflow indocs/rom_hack_variants.mdand the NROM-256 JSON asset pipeline indocs/expanded_rom_assets.md.
bank_FF.asmbase reference: cyneprepou4uk/NES-Games-Disassembly - Pac-Man
No original ROM data is distributed with this repository. The repository does
not currently grant an open-source license for project-authored files; see
docs/licensing.md for the distribution boundary and
third-party provenance. Original game rights remain with their respective owners.