Files
disasm/_wip/Golf (JU)/copilot/NMI_CALLBACK_AUDIT.md
T
2026-10-18 16:47:03 -04:00

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.