# Ms Pac-Man (Namco) (U) ## Status **Pass 3 complete; promoted.** `prg.s` is a traced disassembly with all address-derived labels replaced, all `jsr`/`jmp` operands symbolic, hardware registers named via `symbols.inc`, and `apply_symbols.py` reporting 0 residue. Code coverage: 10,136 / 32,768 bytes (30.9%). `make` assembles and verifies byte-identical to `rom.nes`. Every one of the 27 data blocks the tracer left in `prg.s` has now been interpreted and recorded in the [Pass 3 data-table inventory](#pass-3-data-table-inventory) below: what the bytes are (structure, record size, encoding, units) and which instruction of this dump reads them. Blocks that no instruction reads are recorded as unreferenced data with their ROM provenance; blocks whose bytes decode as instructions are recorded as code the tracer could not reach rather than being given a table name. ## Power-on flow analysis Derived from the disassembly by `_tools/derive_poweron.py`; the call graph is walked from the reset entry. **RESET** is at `$FFF7` (`Reset`). The CPU comes here out of reset with the PPU and APU in their power-on state and the zero page and RAM uninitialised. The reset path calls: | Address | Routine | |---------|---------| | `$C004` | `InitializeHardware` | | `$C018` | `Sub_OnNonzero` (falls through) | | `$C01D` | `Sub_OnNonnegative` (falls through) | | `$C030` | `Sub_OnNonzero2` (falls through) | **NMI** is at `$C107` (`NmiHandler`). It is the per-frame handler; what it calls: | Address | Routine | |---------|---------| | `$C15F` | `RestoreRegistersAndReturn` | **IRQ** is at `$C198` (`IrqHandler`) - a different address from RESET. ## ROM | Property | Value | |----------|-------| | Format | iNES | | Mapper | 0 (NROM) | | PRG | 32 KB | | CHR | 8 KB | | Mirroring | Horizontal | | NMI vector | $C107 | | RESET vector | $FFF7 | | IRQ vector | $C198 | ## Power-on flow RESET enters at `$FFF7`: jmp InitializeHardware ; CPU:$FFF7 PRG:7FF7 4C 04 C0 .byte $07, $C1, $F7, $FF, $98, $C1 ; CPU:$FFFA PRG:7FFA 07 C1 F7 FF 98 C1 NMI handler at `$C107`: pha ; CPU:$C107 PRG:4107 48 txa ; CPU:$C108 PRG:4108 8A pha ; CPU:$C109 PRG:4109 48 tya ; CPU:$C10A PRG:410A 98 pha ; CPU:$C10B PRG:410B 48 lda PPUSTATUS ; CPU:$C10C PRG:410C AD 02 20 lda Zp27 ; CPU:$C10F PRG:410F A5 27 beq NmiHandler_Loop ; CPU:$C111 PRG:4111 F0 07 ## Pass 3 data-table inventory One row per data block the tracer left in `prg.s` (27 in all). "Address" is the block's range, "Name" the label now attached to it (`-` = left unnamed on purpose), "Structure" what the bytes are, and "Read by" the consuming instruction(s) with their addresses. | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$8000-$C003` | 16388 | - | The **lower half of the 32 KB PRG image**, which the tracer never reached. `$8000-$8063` is a 50-entry table of 16-bit little-endian addresses into `$8077-$84ED`; `$8064-$85F5` is a run of repeating 15-19 byte display-script records; `$85F6-$8D91` holds ASCII-art screen rows - the maze/score header `------------------------------------1UP-----HI:SCORE-----2UP` at `$8A11` and the copyright screen `1980-1993-NAMCO-LTD`, `NAMCO-HOMETEK INC`, `LICENSED-BY-NINTENDO` at `$8B4D-$8D91`; `$8D92-$AB77` is more screen data; `$AB78-$BFFF` is `$FF` fill. `$C000-$C003` are the four bytes immediately below the reset stub at `$C004`. | **not referenced by any instruction.** The only absolute operands that land here are the *writes* `sta $C000` / `lda $C001` / `sta $C001` at `$C16B`/`$C16E`/`$C171`, `stx $8000` / `sta $8001` at `$C215`/`$C21B` and `inc $9BC4` at `$C4E2`. The ROM reaches data outside the traced window through its stack-popped pointer interpreter at `$C436-$C44D` (`pla`/`sta $00`/`sta $01` then `lda ($00),y`), so this block is addressable at run time even though no absolute operand names it. | | `$C0D7-$C106` | 48 | `CreditScreenVramData` / `ProgrammerCreditText` | `$C0D7-$C0F6` (32 bytes): a display record for the credit screen - a six-byte header `00 00 01 00 00 00`, the marker `$77 $77`, then three 8-byte nametable rows written as ASCII tile codes (`--10000`, `------0`, `------0`). `$C0F7-$C106` (16 bytes): the programmer credit in ASCII, `" NAOKI HIGASHIO "` (the CHR carries letters at tiles `$41-$5A`). | `lda $C0D7,x` at `$C068` copies the 32 bytes to the RAM buffer `$0700`; `lda $C0F7,x` at `$C075` copies the credit to the zero page at `$0090`. | | `$C20D-$C212` | 6 | `LevelStartParameterTable` | 6 single-byte parameters `00 02 04 05 06 07` (indexed 0-5, one per level start). | `lda $C20D,x` at `$C203` copies the six bytes to `$07E0-$07E5`; the routine at `$C215` then copies them on to `$8000-$800B` as six (index, value) pairs. | | `$C69B-$C6E7` | 77 | - | **Code the tracer could not reach**, not data: the bytes decode as a straight-line routine (`lda $31` / `jsr $C436` / `bcs $C768` / `jsr $DE32` / `jsr $E746` / `jmp $C552`). | not referenced by any instruction. | | `$CA24-$CB31` | 270 | `OneUpTwoUpText` | The block opens with 11 bytes of display text at `$CA24-$CA2E` - the command byte `$20` followed by the ASCII `"w1UP2UP "` - and the rest of the block is unreachable code (`lda $28` / `and #$10` / `jsr $CBFF` / `jmp $CA51` from `$CA2F` on). | `lda $CA24,y` / `lda $CA25,y` / `lda $CA28,y` / `lda $CA29,y` / `lda $CA2A,y` at `$CC2B`, `$CC31`, `$CC3E`, `$CC44`, `$CC4A` copy bytes of the text into the 6-byte VRAM command records at `$0300+`. | | `$CEE6-$D01F` | 314 | `SpriteOamEntryTable` | `$CEE6-$CF33` (78 bytes): a table indexed by the sprite number - the byte at a sprite's own index is that sprite's tile index. From `$CF34` on the block is unreachable code. | `lda $CEE6,x` at `$CED5` loads the tile index of the sprite selected by `SpriteIndex` (`$88`). | | `$D03D-$D0D5` | 153 | `SpriteOamAttributeTable` | `$D03D-$D044` (8 bytes): one OAM attribute byte per sprite (`$23 $03 $04 $5C $5D $3C $3D $23`); bit 2 is added for the seventh sprite when the vertical flip flag `$3C` is set. `$D045-$D04A` holds six more bytes with no reader. From `$D04B` on the block is unreachable code. | `lda $D03D,x` at `$D020`, stored into the OAM buffer at `$0300+` by the loop at `$D031`. | | `$D1C2-$D349` | 392 | `LevelObjectIdTable` / `LevelObjectHandlerTable` | `$D1C2-$D1EB` is a table of 21 two-byte **keys** (`$0202`, `$2504`, `$3405`, `$1C07`, `$1D08`, `$060A`, ... `$1538`, `$54D0`) - the byte at the even offset is compared with `ZpEE` and the byte at the odd offset with `ZpEF`, so the two halves of a key are in separate fields rather than in one 16-bit word. `$D1EC-$D215` is the matching 21-entry table of 16-bit little-endian **handler addresses** (`$D21B`, `$D218`, `$D21F`, `$D04C`, `$D241`, `$D23A`, `$D23D`, `$CFF1`, ...). From `$D216` on the block is unreachable code. | `cmp $D1C2,y` at `$D1A5` and `cmp $D1C3,y` at `$D1AC` match a two-byte key; `lda $D1EC,y` at `$D1B5` and `lda $D1ED,y` at `$D1BA` fetch the handler, which is entered with `jmp ($0008)` at `$D1BF`. | | `$D3AE-$D40E` | 97 | `ScreenPanelPointerTable` | 8 bytes at `$D3AE-$D3B5` = four 16-bit little-endian handler addresses (`$D3B6`, `$D3BB`, `$D3D2`, `$D3D7`); from `$D3B6` on the block is unreachable code. | `lda $D3AE,x` / `lda $D3AF,x` at `$D3A1`/`$D3A6` load the address, `jmp ($0008)` at `$D3AB` jumps to it. The index is built at `$D387-$D3A0` from a counter in `$01` (plus `$40` when the vertical flip flag `$3C` is set). The role of each slot is not established. | | `$D550-$D5C1` | 114 | `ScreenOffsetTable` | 8 signed byte offsets at `$D550-$D557` (`$E0 $E0 $FF $FF $20 $20 $01 $01` = -32, -32, -1, -1, +32, +32, +1, +1) added to a screen base; from `$D558` on the block is unreachable code. | `lda $D550,y` at `$D49C` and `$D4F0`; the `bmi` at `$D49F`/`$D4F3` shows the entries are signed. | | `$D669-$D66C` | 4 | - | 4 bytes `$2D $2E $31 $33`. Nothing in this dump names them. | **not referenced by any instruction.** | | `$D67C-$D6EF` | 116 | `ActorClassPointerTable` | 10 bytes at `$D67C-$D685` = five 16-bit little-endian handler addresses (`$D6AD`, `$D6F0`, `$D73F`, `$D73F`, `$D686`); from `$D686` on the block is unreachable code. | `lda $D67C,y` / `lda $D67D,y` at `$D66F`/`$D674`, entered with `jmp ($0008)` at `$D679`. The role of each slot is not established. | | `$D7A7-$D7DC` | 54 | `ActorStatePointerTable` | 8 bytes at `$D7A7-$D7AE` = four 16-bit little-endian handler addresses (`$D7C5`, `$D7C9`, `$D7AF`, `$D7B3`); from `$D7AF` on the block is unreachable code. | `lda $D7A7,y` / `lda $D7A8,y` at `$D746`/`$D74B`, entered with `jmp ($0008)` at `$D750`. The index is `ZpC1` doubled (`asl a` at `$D744`). The role of each slot is not established. | | `$D7F6-$D85F` | 106 | `ActorModePointerTable` | 8 bytes at `$D7F6-$D7FD` = four 16-bit little-endian handler addresses (`$D7FE`, `$D812`, `$D804`, `$D80C`); from `$D7FE` on the block is unreachable code. | `lda $D7F6,y` / `lda $D7F7,y` at `$D7E9`/`$D7EE`, entered with `jmp ($0008)` at `$D7F3`. The index is `ZpC1` doubled (`asl a` at `$D7E7`). The role of each slot is not established. | | `$DA6D-$DA81` | 21 | `ObjectHandlerPointerTable` | 10 bytes at `$DA6D-$DA76` = five 16-bit little-endian handler addresses (`$DAA9`, `$DAB4`, `$DACB`, `$DA77`, `$DA9E`); from `$DA77` on the block is unreachable code. | `lda $DA6D,x` / `lda $DA6E,x` at `$D8B9`/`$D8BE`, entered with `jmp ($0008)` at `$D8C3`. The role of each slot is not established. | | `$DAE7-$DB54` | 110 | `MazeTileCoordinateTable` / `TileMatchValueTable` | `$DAE7-$DAF3`: five 16-bit little-endian **maze tile coordinates** (X byte, Y byte - e.g. `$A8 $08` = tile column 21, row 1; `$18 $08`; `$A8 $D0`; `$18 $D0`; `$58 $78`), preceded by three bytes `$18 $18 $00` whose role is not established. `$DAF4-$DB05`: 18 single-byte values (`03 02 02 03 03 00 00 03 01 02 02 01 01 00 00 01`) compared against the 4-byte table at zero page `$0C`. From `$DB06` on the block is unreachable code. | `lda $DAEA,x` / `lda $DAEB,x` at `$D8C6`/`$D8CB` load the coordinate; `lda $DAF4,y` at `$D938` is compared with `a:Zp0C,y` at `$D93D`. | | `$DD90-$DE31` | 162 | `CharacterIntroHandlerTable` / `CharacterIntroNameText` | `$DD90-$DDA5`: 11 16-bit little-endian handler addresses (`$DDA6`, `$DDAC`, `$DDB2`, `$DDB8`, `$DDBE`, `$DDA6`, `$DDC4`, `$DDCA`, `$DDD0`, `$DDD6`, `$DDDC`). `$DDA6-$DDD7` is unreachable code (the eleven handlers). `$DDE2-$DE31`: 80 bytes of ASCII - the character-intro names `" THEY MEET THE CHASE JUNIOR JUNIOR2 JUNIOR3 "`, laid out as five 16-column slots. | `lda $DD90,x` / `lda $DD91,x` at `$DD6C`/`$DD71` load a handler, entered with `jmp ($0008)` at `$DD79`; `lda $DDE2,y` at `$DE75` copies the name row into `$0451+`. | | `$DFE5-$DFE8` | 4 | `SpriteFlagTable` | 4 flag bytes `$0F $00 $10 $20`, one per actor class. | `lda $DFE5,x` at `$E054`, indexed by the actor class in `Zp4B` (`$4B`); the result is stored at `$047F`. | | `$E08D-$E096` | 10 | `ActorParameterTable` | 10 single-byte parameters `$04 $01 $00 $00 $00 $00 $00 $00 $08 $00`, copied as a block. | `lda $E08D,x` at `$DFF3` copies the ten bytes to `$00C0-$00C9` (`ZpC0`-`ZpC9`); the byte at `ZpC0` is compared with `$06` at `$D8A2`. | | `$E108-$E117` | 16 | `ActorSpriteTileTable` | 16 pattern-table-0 tile indices for the 8x16 actor sprites (`$00 $18 $1A $1C $58 $5A $5C $58 $5A $5C $98 $9A $9C $D8 $DA $DC`); the lower half of each sprite is the same index plus `$20` (pattern table 1), which is how the code builds the pair. | `lda $E108,y` at `$E0CE` and `lda $E10F,y` at `$E45B`; the tile goes to `$0301` and tile+`$20` to `$0306`, i.e. the two halves of one OAM entry. | | `$E21B-$E231` | 23 | `ScoreHeaderTextStream` | 23-byte HUD text stream: three records of the form ` ` - `$00 $76 $08 "HI:SCORE"`, `$00 $F7 $03 "1UP"`, `$01 $77 $03 "2UP"`. | `lda $E21B,y` at `$E1ED`, copied into the VRAM command buffer at `$0300+`; bytes below `$02` are the command bytes and the rest are tile data. | | `$E3E5-$E414` | 48 | `NametableAttributeData` / `AttributeRowParameterTable` | `$E3E5-$E3FC`: 24 bytes of **nametable attribute data** (the NES attribute byte, two palette bits per 16x16 cell: `$04`, `$05`, `$55`, `$00`, `$F0`, `$F1`, `$F2`, `$F3`, `$AA`, ...). `$E3FD-$E400`: 4 bytes `$20 $5E $51 $B0` - a VRAM address pair plus two coordinate bytes for one HUD row. From `$E401` on the block is unreachable code. | `lda $E3E5,y` at `$E3B7`, `$E3BE` and `$E3CF` write the attribute bytes straight to `$2007` (PPUDATA) at VRAM address `$23C0`/`$27C0`; `lda $E3FD,y` at `$E383` selects the row and copies it to the zero page. | | `$E521-$E6FF` | 479 | `ObjectOffsetTable` / `SpritePaletteTable` / `ActorCoordinateTable` / `ActorTileCodeTable` / `MenuOptionTextStream` | A group of tables: `$E521-$E530` 16 small offset bytes (`$05 $04 $03 $02 $02 $01 $01 $00 ...`); `$E531-$E550` 32 palette numbers in `$00-$0F`; `$E551-$E5D0` 32 16-bit little-endian screen positions (unused by any absolute operand); `$E5D1-$E5DC` 12 X coordinates and `$E5DD-$E5E8` 12 Y coordinates; `$E5E9-$E60C` 9 four-byte tile-code records; `$E60D-$E62C` 32 bytes with no absolute reader; `$E62D-$E644` a 12-entry pointer table to text streams; `$E645-$E69C` 22 four-byte tile records; `$E69D-$E6C0` a 15-entry pointer table; `$E6C1-$E6FF` the **hidden test-menu text stream** - `` records: `"1-PLAYER"`, `"2-PLAYERS"`, `"MENU"`, `"CHAR TEST"`, `"OBJ TEST"`. | `lda $E521,y` at `$E06E`; `lda $E531,x` at `$E03C`; `lda $E5D1,y` at `$DF75` and `lda $E5DD,y` at `$DF80`; `lda $E5E9,y` at `$E014`/`$E01B`; `lda $E609,y` at `$E02F`. | | `$EE34-$F016` | 483 | - | **Code the tracer could not reach**, not data: a long run of sound-engine routines (`ldx $0506` / `lda ($18),y` / `sta $056A` / `jmp $E8C3` and friends) that write the APU registers at `$4000-$4017`. | not referenced by any instruction. | | `$F1D1-$F296` | 198 | `SoundChannelHandlerTable` | 18 bytes at `$F1D1-$F1E2` = nine 16-bit little-endian sound-channel handler addresses (`$F1E0`, `$F203`, `$F226`, `$F25A`, `$F271`, `$F27C`, `$F286`, `$F28E`); from `$F1E3` on the block is unreachable code that pokes `$4000-$4004`. | not referenced by any absolute operand in this dump (the table is reached through the sound engine's own dispatch). | | `$F492-$FFF6` | 2917 | `MusicRoutinePointerTable` / `NotePeriodTable` / `NoteNameTable` / `NoteDurationTable` | `$F492-$F4E1` 40 16-bit little-endian addresses into the sound engine (`$F312`, `$F325`, ..., `$ED97`, ..., `$F03C`); `$F4E2-$F4FF` 30 zero bytes; `$F500-$F5CB` 102 16-bit little-endian **note periods** descending from `$04EB` to `$0E00`; `$F5CC-$F5D1` the six note-name characters `"behknq"`; `$F5D2-$F5D7` six note durations `$06`; `$F5D8-$FFF5` `$FF` fill. | `lda $F524,y` at `$E970` and `lda $F525,y` at `$E976` fetch one 16-bit period from the period table. | | `$FFFA-$FFFF` | 6 | `InterruptVectorTable` | The three 6502 interrupt vectors: NMI = `$C107` (`NmiHandler`), RESET = `$FFF7` (`jmp $C004`), IRQ = `$C198` (`IrqHandler`). | The only absolute operand the tracer associates with the block is `sta $FFFF,y` at `$C4C0`, and with `Y = $01` that address wraps to `$0000`, so it is a zero-page write and not a use of the vectors. | ### Open work * **One entry point is still address-derived.** `Sub_C4DB` (`$C4DB`) is the only routine the game reaches through a `jsr` or a `jmp` that has not been given a semantic name. It is called from the main loop at `$C0AF`, but the bytes the tracer decoded into it do not make sense as instructions for this cartridge: `inc $9BC4` and `and $63C5,x` would write into the PRG ROM at `$8000-$BFFF`, and the block ends in a `brk`. The two `jsr Sub_C436` calls inside it are the script's pointer interpreter, so the routine is most probably a mis-decode of a data record rather than a routine at all. The address-derived name is left in place because the dump does not reveal what the code is for. * Many blocks carry a tail of **code the tracer could not reach** - the block itself holds a data table at its start, and the rest is unreachable 6502: `$C69B-$C6E7` (nothing but code), and the tails of `$CA24-$CB31`, `$CEE6-$D01F`, `$D03D-$D0D5`, `$D1C2-$D349`, `$D3AE-$D40E`, `$D550-$D5C1`, `$D67C-$D6EF`, `$D7A7-$D7DC`, `$D7F6-$D85F`, `$DA6D-$DA81`, `$DAE7-$DB54`, `$DD90-$DE31`, `$E3E5-$E414`, `$EE34-$F016` (nothing but code) and `$F1D1-$F296`. Re-tracing with `--extra-seed` on the handler addresses in the pointer tables above would turn them into labelled routines; the project has no `make regen` pipeline, so the naming would have to be re-applied by hand. * The **lower PRG half** (`$8000-$BFFF`) is reached only through the stack-popped pointer interpreter at `$C436-$C44D`, so a re-trace that follows those pointers would recover the display-script data and the ASCII-art screen layouts it holds. ## Build ``` Byte-identical ROM verified. ```