110 lines
3.3 KiB
Markdown
110 lines
3.3 KiB
Markdown
# Pass 1 NMI callback audit
|
|
|
|
## Dispatcher
|
|
|
|
The hardware NMI vector enters `$C0A6`. The fixed prologue saves A/X/Y, performs
|
|
OAM DMA from page `$02`, then executes `JMP ($0002)`. Every installed callback
|
|
must therefore finish through the common epilogue at `$C0B8` or deliberately
|
|
reset/restart the program.
|
|
|
|
`$C888` is the generic installer:
|
|
|
|
- Y -> `$0002` (callback low byte)
|
|
- X -> `$0003` (callback high byte)
|
|
- PPU NMI remains enabled through the shadow value at `$00`
|
|
|
|
## Confirmed callback installations
|
|
|
|
### `$C6C8` installed at `$C0EC`
|
|
|
|
Caller loads Y=`$C8`, X=`$C6`, then calls `$C888`.
|
|
|
|
Role: early/main gameplay display callback. It updates OAM entries at
|
|
`$0214-$0217` and `$0284-$0287`, advances animation counters `$8E/$8F`, and
|
|
terminates with `JMP $C0B8`.
|
|
|
|
### `$CFAA` installed at `$CF5C`
|
|
|
|
Caller loads Y=`$AA`, X=`$CF`, then calls `$C888`.
|
|
|
|
Role: gameplay sequencing callback. It decrements/increments `$32`, updates OAM
|
|
coordinates, and changes the callback vector to one of the following mid-routine
|
|
continuations as state thresholds are crossed:
|
|
|
|
- `$CFC3`
|
|
- `$D03E`
|
|
- `$D08A`
|
|
|
|
The routine ultimately reaches the gameplay display tail at `$C6C8`, which exits
|
|
through `$C0B8`.
|
|
|
|
### `$DAA2` installed at `$DA27`
|
|
|
|
Caller loads Y=`$A2`, X=`$DA`, then calls `$C888`.
|
|
|
|
Role: title/menu/attract controller callback. It polls controller port `$4016`,
|
|
updates `$09/$0A/$0D`, writes one OAM cursor entry at `$0200-$0203`, and either:
|
|
|
|
- jumps to `$C0B8` for the normal NMI epilogue,
|
|
- restarts title/game initialization through `$DA2F/$DA4C`, or
|
|
- restarts the initial state through `$C0C7`.
|
|
|
|
Important correction: `$DB0D` (`78 88 98`) is used as OAM Y-position data for
|
|
`$0200`, not as an NMI callback-low-byte table. Consequently `$BA78/$BA88/$BA98`
|
|
are not callbacks.
|
|
|
|
### `$C5A0` installed at `$FC8E`
|
|
|
|
Caller loads Y=`$A0`, X=`$C5`, then calls `$C888`.
|
|
|
|
Role: active gameplay controller/animation callback. It serially reads `$4016`,
|
|
updates input-derived state `$2E/$30`, angle/selection fields `$17/$44/$45`, OAM
|
|
entries, and then enters `$C6C8` before returning through `$C0B8`.
|
|
|
|
## Direct callback-vector transitions
|
|
|
|
The following direct writes to `$02/$03` are confirmed executable and are part
|
|
of the `$CFAA-$D121` callback state machine:
|
|
|
|
- `$CFB6/$CFBA`: install `$CFC3`
|
|
- `$CFDA/$CFDE`: install `$D03E`
|
|
- `$D006/$D00A`: install `$D03E`
|
|
- `$D02B/$D02F`: install `$D08A`
|
|
- `$D05E/$D062`: install `$D08A`
|
|
- `$D07A/$D07E`: install `$D08A`
|
|
|
|
These are mid-routine continuation entries, not independent top-level systems.
|
|
They share state in `$17/$1D/$1E/$1F/$32/$34/$35` and converge on `$C6C8`.
|
|
|
|
## Confirmed callback set
|
|
|
|
Top-level callback installations:
|
|
|
|
- `$C5A0`
|
|
- `$C6C8`
|
|
- `$CFAA`
|
|
- `$DAA2`
|
|
|
|
Dynamic continuation callbacks:
|
|
|
|
- `$CFC3`
|
|
- `$D03E`
|
|
- `$D08A`
|
|
|
|
Common normal exit:
|
|
|
|
- `$C0B8`
|
|
|
|
No other verified executable write to `$0002/$0003` has been found. Apparent
|
|
writes inside compressed hole packages are data and are excluded.
|
|
|
|
## Source conversion
|
|
|
|
The previously retained callback spans are now emitted as annotated instructions:
|
|
|
|
- `$C5A0-$C71D`: gameplay input/display callback chain
|
|
- `$CFAA-$D123`: gameplay sequence callback and dynamic continuation entries
|
|
|
|
The three bytes at `$C71E-$C720` remain data because execution at `$C71B` jumps
|
|
directly to the NMI epilogue and cannot fall through into them.
|