# Kung Fu (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: 17,068 / 32,768 bytes (52.1%). `make` assembles and verifies byte-identical to `rom.nes`. Every data block in the PRG is interpreted in the [Pass 3 data-table inventory](#pass-3-data-table-inventory) below: each block says what the bytes are and which instruction in this dump reads them. Blocks that no instruction reaches are left unnamed in `prg.s` and recorded in the inventory as unreferenced data or as code the tracer could not reach. ## 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 `$8258` (`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 | |---------|---------| | `$8262` | `Reset_OnCarryClear` (falls through) | | `$8268` | `Reset_OnCarryClear2` (falls through) | | `$8277` | `Reset_OnNonzero` (falls through) | | `$8287` | `Reset_OnNonzero2` (falls through) | **NMI** is at `$82D6` (`NmiHandler`). It is the per-frame handler; what it calls: | Address | Routine | |---------|---------| | `$DB3E` | `Sub_DB3E` | | `$8E0D` | `Sub_8E0D` | **IRQ** is at `$FFF0` (`$FFF0`) - a different address from RESET. ## ROM | Property | Value | |----------|-------| | Format | iNES | | Mapper | 0 (NROM) | | PRG | 32 KB | | CHR | 8 KB | | Mirroring | Vertical | | NMI vector | $82D6 | | RESET vector | $8258 | | IRQ vector | $FFF0 | ## Power-on flow RESET enters at `$8258`: sei ; CPU:$8258 PRG:0258 78 cld ; CPU:$8259 PRG:0259 D8 lda #$00 ; CPU:$825A PRG:025A A9 00 sta PPUCTRL ; CPU:$825C PRG:025C 8D 00 20 ldx #$FF ; CPU:$825F PRG:025F A2 FF txs ; CPU:$8261 PRG:0261 9A lda PPUSTATUS ; CPU:$8262 PRG:0262 AD 02 20 asl a ; CPU:$8265 PRG:0265 0A bcc Reset_OnCarryClear ; CPU:$8266 PRG:0266 90 FA lda PPUSTATUS ; CPU:$8268 PRG:0268 AD 02 20 asl a ; CPU:$826B PRG:026B 0A bcc Reset_OnCarryClear2 ; CPU:$826C PRG:026C 90 FA NMI handler at `$82D6`: lda Zp10 ; CPU:$82D6 PRG:02D6 A5 10 and #$7F ; CPU:$82D8 PRG:02D8 29 7F sta PPUCTRL ; CPU:$82DA PRG:02DA 8D 00 20 sta Zp10 ; CPU:$82DD PRG:02DD 85 10 lda #$00 ; CPU:$82DF PRG:02DF A9 00 sta PPUMASK ; CPU:$82E1 PRG:02E1 8D 01 20 lda #$00 ; CPU:$82E4 PRG:02E4 A9 00 sta OAMADDR ; CPU:$82E6 PRG:02E6 8D 03 20 ## Pass 3 data-table inventory Every data block in the PRG is listed below. "Read by" gives the address of the instruction in this dump that fetches the block; where no instruction reaches the bytes the row says so and records where the bytes come from. ### `$8000-$8257` (600 bytes) - level, screen and sprite parameter tables This block is a bundle of separate tables, so it is listed table by table. | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|----------| | `$8000-$800D` | 14 | `LevelHandlerPointerTable` | 7 x 16-bit little-endian code addresses: the entry routine of each of the seven levels, indexed by the level number held in `Zp51` | `lda $8000,y` at `$83A5` and `$83AB` (Y = Zp51 x 2); the pair is stored to `$00`/`$01` and entered with `jmp (VramWritePtrLo)` at `$83D6` | | `$800E-$801B` | 14 | `LevelSectionListOffsetTable` | 7 x 16-bit LE offsets into the section list that starts at `$801C`, one per level | `lda $800E,x` at `$83BC`/`$83C2` (X = Zp51 x 2) -> `$02`/`$03`; `lda ($02),y` at `$83CD` (Y = Zp4F x 2) fetches the section handler, which is entered at `$83D6` | | `$801C-$804F` | 52 | `LevelSectionHandlerTable` | 26 x 16-bit LE code addresses: the routine that runs each in-level section. Level 0's list starts at word 14 (`$84F0`), level 1's at word 16, level 5's at word 18 and level 6's at word 21, so the lists overlap | `lda ($02),y` at `$83CD` | | `$8050-$8055` | 6 | `ScreenInitRoutinePointerTable` | 3 x 16-bit LE code addresses (`$8DCF`, `$8DDC`, `$8718`) | no absolute-operand read in this dump; the tracer recovered the three targets as table entries, and `$8718` is the routine entered from the level list | | `$8056-$80A4` | 79 | `ScreenVramStreamPtrLoTable` | low byte of the PRG address of the VRAM update stream of each of the 79 screens (index = screen id in `Zp52`) | `lda $8056,x` at `$82F0` | | `$80A5-$80F3` | 79 | `ScreenVramStreamPtrHiTable` | high byte of the same addresses; entries 0-4 are `$0301`, the RAM buffer that holds the base sprite stream | `lda $80A5,x` at `$82F5` | | `$80F4-$80F7` | 4 | `PlayerSpriteYPositionTable` | four sprite Y positions | `lda $80F4,x` at `$845B` (X = Zp54) -> OAM `$0200` | | `$80F8-$80FF` | 8 | `SpritePairIdTable` | 4 x 2-byte identifiers, one per sprite pair | `lda $80F8,y` at `$8610`/`$8616` (Y = 2 x sprite index) -> `Zp5A`/`Zp5B`; the pair `$2AAC` is the constant compared at `$BA3A`/`$BA42` | | `$8102-$8105` | 4 | `PlayerSpawnYTable` | four starting Y positions of the spawned fighter | `lda $8102,x` at `$85FA` -> `ZpD5`-`ZpD8` -> `ZpCE` at `$AC7D` | | `$8106-$8109` | 4 | `PlayerSpawnHitPointTable` | four hit-point values | `lda $8106,x` at `$85FF` -> `ZpE6`-`ZpE9` -> `EnemyHealth` at `$AC82` | | `$810A-$810D` | 4 | `PlayerSpawnXTable` | four starting X positions of the spawned fighter | `lda $810A,x` at `$8604` -> `$0450`-`$0453` -> `PlayerX` at `$AC75` | | `$810E-$8111` | 4 | `PlayerSpriteOamTemplate` | one 4-byte OAM entry (Y, tile, attribute, X) | `lda $810E,x` at `$857C` (X = 3..0) -> `$0200`-`$0203` | | `$8112-$8123` | 18 | `SpriteSpawnParameterTable` | six parallel 4-entry arrays with stride 2, at offsets 0, 2, 5, 7, 9 and 11 from `$8112`; the fields land in `Zp74`, `ZpE5`, `ZpE4`, `ZpD3`, `$0384`, `Zp64` and `Zp65` | `lda $8112,y` at `$85D5`, `lda $8114,y` at `$85DA`, `lda $8117,y` at `$85C6`, `lda $8119,y` at `$85CB`, `lda $811B,y` at `$85E4`, `lda $811D,y` at `$85D0`, `lda $8114,x` at `$85BA` | | `$812A-$813D` | 20 | `SpritePatrolOffsetTable` | four 4-byte records giving each fighter's patrol X offset and its speed/timer bytes | `adc $812A,x` at `$870B` adds the record's offset to the patrol base | | `$813E-$8151` | 20 | `GameStateHandlerPointerTable` | 10 x 16-bit LE code addresses, one per game state (index = `Zp60`) | `lda $813E,y` at `$8ADA`/`$8AE0` (Y = Zp60 x 2) -> `jmp (VramWritePtrLo)` at `$8AE5` | | `$8152-$8155` | 4 | `StatusSpriteOamTemplate` | one 4-byte OAM entry (the on-screen status marker) | `lda $8152,x` at `$896A` (X = 3..0) -> `$0200`-`$0203` | | `$8163-$8183` | 33 | `StageScreenLayoutTemplate` | a 33-byte screen template; the bytes fall into 16 (tile, attribute) pairs followed by a `$00` terminator | `lda $8163,x` at `$8888` (X = `$20`..0) -> RAM `$0300`-`$0320` | | `$8184-$81ED` | 106 | `EnemySpawnAttributeTable` | 106 sprite attribute bytes, one per enemy slot (palette in bits 7-6, flip in bit 5) | `lda $8184,y` at `$8DF9`, then `and #$C0` at `$8DFE` | | `$81EE-$8257` | 106 | `EnemySpawnDelayTable` | 106 frame counts, one per enemy slot, in the same order as the attribute table; `$00` means "no more enemies in this slot" | `lda $81EE,y` at `$84DB` and `$8DF1` -> `$0539`, decremented at `$8E06` | ### The remaining blocks | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|----------| | `$841B-$8426` | 12 | `InitialEnemyOamTemplate` | three 4-byte OAM entries (Y, tile, attribute, X) - the fighters placed on screen when a stage opens | `lda $841B,x` at `$8450` (X = `$0B`..0) -> `$0200`-`$020B` | | `$8C8A-$8CC0` | 55 | (unnamed) | unreferenced data: five 4-byte OAM-style records at the head, then a 35-byte run of tile indices. No instruction in this dump fetches these bytes | none | | `$8CC1-$8DCE` | 110 | (unnamed) | code the tracer could not reach: `lda $39` / `bne` / `inc $03A0` / `ldx $03A0` / `cpx #$03` / `stx $35` / `jmp $88BF` | none | | `$9032-$9807` | 2006 | `StageNametableUpdateStream` | a chain of VRAM update records - [PPU address high][PPU address low][count][count bytes of tile and attribute data] - each chain closed by a `$00` first byte. The records in this block write PPU `$2000`-`$23FF`: nametable 0 and its attribute table at `$23C0` | reached through `ScreenVramStreamPtrLoTable`/`HiTable`; the writer is the routine at `$DAEA` (address) and `$DB19` (data) | | `$9810-$A481` | 3186 | `StageScreenDataTable` | the second bank of stage screen data, mixed: a VRAM update chain at `$9839`-`$98A2` and another at `$9875`-`$98A2` (both writing nametable 0 and 1), per-stage parameter bytes at `$9B50`-`$9BAF`, a table of 16-bit routine addresses at `$9C00`-`$9C37`, and 66 bytes of unreachable code at `$A440` | the chains are reached through the screen stream pointer table; `bit $9B5E` at `$A51F`, `cmp $9BA7,y` at `$A6C6`, `lda $9B93,y` at `$A787`, `lda $9B6E,y` at `$A7A0` and `cmp $9B71,y` at `$A7A9` read the parameter bytes | | `$B6CD-$B713` | 71 | `StageObjectSpawnTable` | seven per-stage X bases at `$B6CD`-`$B6D3` (`$FF`, `$0B`, `$1B`, `$1F`, `$2B`, `$3B`, `$3F`), then 16 four-byte spawn records at `$B6D4` (offset, tile, `$02`, flag) | `lda $B6CD` at `$B721` (stage 0), `lda $B6D0` at `$B73C` (stage 3), `lda $B6CD,y` at `$B755` (Y = stage), `adc $B6D4,y` at `$B75E` | | `$BB6F-$BDC3` | 597 | `SpriteMotionPathTable` | the head of the block is seven unused `$FF` bytes, then a 16-bit routine pointer (`$A890`) at `$BB76`, an enable byte at `$BB79`, per-sprite parameters at `$BB7B`, two per-state byte arrays at `$BB80`/`$BB8C`, and a frame-by-frame Y-position table at `$BB90` | `lda $BB7B,y` at `$BDD8`, `lda $BB80,x` at `$BDC6`, `lda $BB8C,x` at `$BDCC`, `lda $BB90,y` at `$BE9F` | | `$CDDD-$CFCB` | 495 | `SpriteAnimationParameterTable` | fifteen unused `$FF` bytes at the head, then a table of small per-sprite thresholds and step values: `$CDEC` (thresholds compared at `$D09F`), `$CDFA`, `$CE0E` (`$CFF8`), `$CE24` (`$D0A9`), `$CE6E` (`$D027`) and `$CE9E` (`$D18B`) | `cmp $CDEC,y` at `$D09F`, `lda $CDFA,y` at `$D02C`, `lda $CE0E,y` at `$CFF8`, `lda $CE24,y` at `$D0A9`, `lda $CE6E,y` at `$D027`, `lda $CE9E,y` at `$D18B` | | `$D386-$D3C5` | 64 | (unnamed) | code the tracer could not reach. It opens with the bytes `$00 $0C $18 $24 $AD $8C`, which the same unreachable code reads with `adc $D386,y` at `$D3B4`; the rest of the block decodes as instructions (`lda $69` / `and #$20` / `sta $00` / `lda $CEEE,y` ...) | none outside the unreachable code | | `$DAE7-$DAE9` | 3 | (unnamed) | filler (`$FF`, `$FF`, `$FF`) between two routines | none | | `$DB48-$F830` | 7401 | `StageVramUpdateStream` | the bundle of stage VRAM data: a chain of VRAM update records for the title and intro screens at `$DB48`-`$DBDB`, the base sprite-pattern stream at `$DBDC`, and 7352 bytes of stage graphic data at `$DC01` | `lda $DB48,y` at `$8546` and `lda $DBDC,y` at `$8536`; the chain at `$DB48` is reached through the screen stream pointer table (entry 5 is `$DB6C`) | | `$DBDC-$DC00` | 37 | `BaseSpritePatternStream` | [PPU address `$3F00`][count `$20`][32 sprite-pattern bytes][`$00` terminator] - the sprite pattern table for all eight fighters | `lda $DBDC,y` at `$8536` (Y = `$24`..0) copies all 37 bytes to RAM `$0301`-`$0325`; screens 0-4 use that RAM copy, because their stream pointer is `$0301` | | `$DC01-$F830` | 7352 | (unnamed) | unreferenced stage graphic data. The bytes are all in `$00`-`$FF` and decode as a long run of instructions, so the layout is not established; no instruction in this dump fetches them | none | | `$F981-$F9B8` | 56 | `EnemySpawnPositionTable` | two groups of per-stage enemy slots: a 13-entry group (`$F980`-`$F999`: an enable byte and an X position per slot) and a 10-entry group (`$F99A`-`$F9B7`) | `ldx $F98D,y` at `$F9C1`, `lda $F99A,y` at `$F9D4`, `ldx $F9A4,y` at `$F9D1`, `ldx $F9B3,y` at `$FA0A`, `lda $F9AE,y` at `$FA0D` | | `$FA36-$FA89` | 84 | `EnemyMovementParameterTable` | the movement and appearance parameters of the enemy slots: a 13-entry group at `$FA35`-`$FA4F` and a 10-entry group at `$FA4A`-`$FA79` | `lda $FA43,y` at `$FAAF`, `ldx $FA3C,y` at `$FAB5`, `lda $FA58,y` at `$FABE`, `ldx $FA51,y` at `$FAC4`, `lda $FA4A,y` at `$FAC7`, `lda $FA6D,y` at `$FACD` | | `$FCE9-$FFEF` | 775 | `SoundNoteSequenceTable` | the sound engine's note data: a table of note records (loop count, pitch, duration, envelope) read five bytes at a time, and the APU pulse channel timer periods | `lda $FCE9,y` at `$FB74` and the five following reads load the note record; `lda $FF01,y` at `$F85B` and `lda $FF00,y` at `$F863` write the pulse channel timers | | `$FF00-$FF03` | 4 | `PulseChannelTimerPeriodTable` | the APU pulse channel timer periods: high and low byte for pulse 1 and pulse 2 | `lda $FF01,y` at `$F85B` -> `APUPULSE1TIMERLO`, `lda $FF00,y` at `$F863` -> `APUPULSE1TIMERHI` | | `$FFFF` | 1 | (unnamed) | filler (`$FF`) after the IRQ vector | none | ### How the VRAM update records were derived The writer at `$DAEA`-`$DB47` reads the stream through the zero-page pointer `$00`/`$01`: the first byte goes to `PPUADDR` as the high byte of the PPU address, the second as the low byte, the third is the byte count (count = byte & `$3F`; bit 7 of the count selects the +32 VRAM increment instead of +1), and the bytes after that are the tile and attribute data written to `PPUDATA`. A `$00` first byte ends the chain. The screen stream pointer table at `$8056`/`$80A5` supplies the start address of the chain for each screen, and every pointer in it lands on a byte in `$20`-`$3F`, which is what confirms the reading: for example entry 6 gives `$9703`, whose first three bytes are `23 C2 04` - PPU `$23C2`, four bytes - and the next record starts at `$970A` with `23 C8 08`. ## Entry points that are still named after their address **209 of the 260 address-derived entry points are still named `Sub_` or `SubC`.** The other 51 now carry a name taken from what the ROM shows them doing, and their `; Global:` lines were rewritten to say so. The residue is grouped below by *why* the dump does not reveal a better name. | Address | Why the dump does not reveal a better name | |---------|------------------------------------------| | `$8392`, `$84C5`… (the `$8xxx` block) | the game-state and level-section handlers. Each one branches on the level index (`LevelIndex`), the game state (`GameState`) and the section counter (`$03CD`), and the ROM does not name the states or the sections in words | | `$8A03`, `$8A25`, `$8A2E`, `$8A4D`, `$8A6C`, `$8AD6`, `$897A` | the three screen counters and the screen-script reader that go with them. The dump shows the counters being incremented and a script pointer being read out of the table at `$813E`, but not which of the game's screens each counter belongs to | | `$8E0D`, `$8E1C`, `$8E85`, `$8E9B`, `$8EAF`, `$8F54`, `$8F63`, `$8FA0`, `$8FA6`, `$8FB1`, `$8FC5` | the PPU and controller helpers. Several of them are three-instruction stubs that only set one PPU register, and the dump does not say which of the game's screens each of them belongs to | | `$A482`…`$A936` (the `$Axxx` block) | the per-object and per-player state handlers: they test `PlayerIndex`, `$0496`, `Zp23` and `Zp67` and branch on them. The dump shows the tests but not the game's word for the states they select | | `$B0CE`, `$B1C9`, `$B1D0`, `$B1E3`, `$B208`, `$B220`, `$B231`, `$B243` | the small flag and arithmetic helpers of the enemy code: they set one bit of a state byte, add to a 16-bit value or take one off an enemy's health. The dump shows the arithmetic but not the name of the field | | `$B2BA`…`$B714`, `$B789`…`$B9F9` | the enemy spawn and movement steps. They read the enemy tables at `$A226` and `$A288` and the sprite tables at `$A24F`/`$A256`, and the dump does not say which of the game's enemies each step places | | `$BA3A`, `$BAA5`, `$BADD`, `$BB48`, `$BDC4`, `$BF15`, `$BF3E`, `$BFB4` | the enemy-collision and sprite-attribute steps | | `$C01F`…`$C7AB` | the object-drawing and collision code: the routines that move a sprite, test it against another and store its position. The dump shows the arithmetic on the object records but not the game's word for the object types | | `$CB15`, `$CB22`, `$CB2F`, `$CB3E`, `$CB50` | the two's-complement helpers of the collision code: they subtract one coordinate from another and turn the result into a sprite address | | `$CC31`…`$CE7D`, `$CF36`, `$CF97`, `$D054`…`$D2E3` | the object-flag, controller and level-handler code. The controller code is well understood (it reads `$D317`, the `ControllerActionTable`), but the object flags and the level handlers are plain numbers the ROM does not name | | `$D3C6`…`$D9BF`, `$DA0E`, `$DA51`, `$DAB8` | the enemy and player animation steps | | `$A1A4`…`$AEBF` and the `SubCA…` labels | the labels whose address does not match the four hex digits in their name: the pass-2 naming put a `C` in front of the address for the labels that sit in the second half of the code window. They are the same routines as the neighbouring `Sub_…` labels and are named the same way | ## Build ``` Byte-identical ROM verified. ```