# Kung Fu (U) (a1) ## 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: 16,925 / 32,768 bytes (51.7%). `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` | | `$8E25` | `Sub_8E25` | **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 sta OAMADDR ; CPU:$82E4 PRG:02E4 8D 03 20 ldy #$02 ; CPU:$82E7 PRG:02E7 A0 02 ## 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. This revision is the same game as `NROM/Kung Fu (U)` with different routine addresses, so the tables carry the same names and the same roles. Where the two revisions differ in what reaches a table, that is noted in the row. ### `$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 `$83C1` and `$83C7` (Y = Zp51 x 2); the pair is stored to `$00`/`$01` and entered with `jmp (VramWritePtrLo)` at `$83F2` | | `$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 `$83D8`/`$83DE` (X = Zp51 x 2) -> `$02`/`$03`; `lda ($02),y` at `$83E9` (Y = Zp4F x 2) fetches the section handler, which is entered at `$83F2` | | `$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, 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 `$83E9` | | `$8050-$8055` | 6 | `ScreenInitRoutinePointerTable` | 3 x 16-bit LE code addresses (`$8DE7`, `$8DF4`, `$8733`) | no absolute-operand read in this dump; the tracer recovered the three targets as table entries | | `$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 `$82F7` | | `$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 `$82FC` | | `$80F4-$80F7` | 4 | `PlayerSpriteYPositionTable` | four sprite Y positions | in this revision the copy routine lies in the unreachable code at `$8443`, so no instruction in this dump reaches it; in the `(U)` revision the same four bytes are read by `lda $80F4,x` at `$845B` | | `$80F8-$80FF` | 8 | `SpritePairIdTable` | 4 x 2-byte identifiers, one per sprite pair | `lda $80F8,y` at `$862B`/`$8631` (Y = 2 x sprite index) -> `Zp5A`/`Zp5B` | | `$8102-$8105` | 4 | `PlayerSpawnYTable` | four starting Y positions of the spawned fighter | `lda $8102,x` at `$8615` -> `ZpD5`-`ZpD8` | | `$8106-$8109` | 4 | `PlayerSpawnHitPointTable` | four hit-point values | `lda $8106,x` at `$861A` -> `ZpE6`-`ZpE9` -> `EnemyHealth` | | `$810A-$810D` | 4 | `PlayerSpawnXTable` | four starting X positions of the spawned fighter | `lda $810A,x` at `$861F` -> `$0450`-`$0453` -> `PlayerX` | | `$810E-$8111` | 4 | `PlayerSpriteOamTemplate` | one 4-byte OAM entry (Y, tile, attribute, X) | `lda $810E,x` at `$8597` (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` | `lda $8112,y` at `$85F0`, `lda $8114,y` at `$85F5`, `lda $8117,y` at `$85E1`, `lda $8119,y` at `$85E6`, `lda $811B,y` at `$85FF`, `lda $811D,y` at `$85EB`/`$85FA`, `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 `$8726` | | `$813E-$8151` | 20 | `GameStateHandlerPointerTable` | 10 x 16-bit LE code addresses, one per game state (index = `Zp60`) | `lda $813E,y` at `$8AF2`/`$8AF8` -> `jmp (VramWritePtrLo)` at `$8AFD` | | `$8152-$8155` | 4 | `StatusSpriteOamTemplate` | one 4-byte OAM entry (the on-screen status marker) | `lda $8152,x` at `$8985` (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 `$88A3` (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 `$8E11`, then `and #$C0` | | `$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 `$84F6` and `$8E09` -> `$0539`, decremented each frame | ### The remaining blocks | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|----------| | `$8437-$84E9` | 179 | (unnamed) | the first twelve bytes (`$8437`-`$8442`) are the same three 4-byte OAM entries the `(U)` revision keeps at `$841B`-`$8426`; the copy routine that would read them lies in the 167 bytes of unreachable code that follow (`lda #$18` / `sta $52` / `sta $053B` ...) | none in this revision - no instruction in this dump fetches the twelve bytes | | `$8CA2-$8CD8` | 55 | (unnamed) | unreferenced data at the head (five 4-byte OAM-style records, then a run of tile indices) | none | | `$8CD9-$8DE6` | 270 | (unnamed) | code the tracer could not reach: `lda $39` / `bne` / `inc $03A0` / `ldx $03A0` / `cpx #$03` / `stx $35` / `jmp $88BF` | none | | `$904A-$981F` | 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 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) | | `$9828-$A495` | 3182 | `StageScreenDataTable` | the second bank of stage screen data, mixed: a VRAM update chain at `$9851`-`$98BA` and another at `$988D`-`$98BA`, per-stage parameter bytes at `$9B64`-`$9BC3`, a table of 16-bit routine addresses at `$9C00`, and unreachable code at the end | the chains are reached through the screen stream pointer table; `bit $9B72` at `$A533`, `cmp $9BBB,y` at `$A6DA`, `lda $9BA7,y` at `$A79B`, `lda $9B82,y` at `$A7B4` and `cmp $9B85,y` at `$A7BD` read the parameter bytes | | `$B6E1-$B727` | 71 | `StageObjectSpawnTable` | seven per-stage X bases at `$B6E1`-`$B6E7`, then 16 four-byte spawn records at `$B6E8` (offset, tile, `$02`, flag) | `lda $B6E1` at `$B735` (stage 0), `lda $B6E4` at `$B750` (stage 3), `lda $B6E1,y` at `$B769` (Y = stage), `adc $B6E8,y` at `$B772` | | `$BB83-$BDD1` | 591 | `SpriteMotionPathTable` | the head of the block is seven unused `$FF` bytes, then a 16-bit routine pointer at `$BB8A`, an enable byte at `$BB8D`, per-sprite parameters at `$BB8F`, two per-state byte arrays at `$BB94`/`$BBA0`, and a frame-by-frame Y-position table at `$BBA4` | `lda $BB89,y` at `$BDE6`, `lda $BB8E,x` at `$BDD4`, `lda $BB9A,x` at `$BDDA`, `lda $BB9E,y` at `$BEAD` | | `$CDEB-$CFCA` | 480 | `SpriteAnimationParameterTable` | fifteen unused `$FF` bytes at the head, then a table of small per-sprite thresholds and step values: `$CDEB` (thresholds compared at `$D09E`), `$CDF9`, `$CE0D` (`$CFF7`), `$CE23` (`$D0A8`), `$CE6D` (`$D026`) and `$CE9D` (`$D18A`) | `cmp $CDEB,y` at `$D09E`, `lda $CDF9,y` at `$D02B`, `lda $CE0D,y` at `$CFF7`, `lda $CE23,y` at `$D0A8`, `lda $CE6D,y` at `$D026`, `lda $CE9D,y` at `$D18A` | | `$D385-$D3C4` | 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 $D385,y`; the rest of the block decodes as instructions | none outside the unreachable code | | `$DAE6-$DAE9` | 4 | (unnamed) | filler (`$FF`, `$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 `$8561` and `lda $DBDC,y` at `$8551` | | `$DBDC-$DC00` | 37 | `BaseSpritePatternStream` | [PPU address `$3F00`][count `$20`][32 sprite-pattern bytes][`$00` terminator] - the sprite pattern table for all eight fighters; byte for byte the same as the `(U)` revision | `lda $DBDC,y` at `$8551` (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. ## Entry points that are still named after their address **207 of the 258 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. The names were worked out on the `Kung Fu (U)` dump, which is the same game, and then carried across to this revision by matching the two dumps' routines against each other: each renamed routine of `Kung Fu (U)` was matched to the routine of this dump whose instruction sequence is closest, and only the unambiguous matches were taken. Every routine that was matched here does the same job as the routine it was named after in the other dump. The residue is the same as in the other dump, and it is left address-derived for the same reasons: | Address | Why the dump does not reveal a better name | |---------|------------------------------------------| | the `$8xxx` block | the game-state and level-section handlers, which branch on the level index, the game state and the section counter - none of which the ROM names in words | | the `$8Exx`–`$8Fxx` PPU and controller helpers | several are three-instruction stubs that set one PPU register, and the dump does not say which screen each belongs to | | the `$Axxx` block | the per-object and per-player state handlers, which test `PlayerIndex` and a handful of flag bytes | | the `$Bxxx` block | the enemy spawn, movement and collision steps, which read the enemy tables at `$A226` and `$A288` and the sprite tables | | the `$Cxxx` block | the object-drawing and collision code | | the `$Dxxx` block | the object-flag, controller and level-handler code | | 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. ```