Standardizes build process and adds OVERVIEW.md for each

This commit is contained in:
tmerritt committed 2026-10-15 18:50:41 -04:00
1 parent 25423d3828
commit a29b03b00b
135 files changed
+26562 -420

No files matched your search

+433
View File
@@ -0,0 +1,433 @@
# Baseball (UE) — Game Flow from Power-On
This document describes the execution flow of the disassembled Baseball (UE) PRG bank (16 KB, mapped at CPU **$C000–$FFFF**), starting from power-on. It is based entirely on `copilot/prg.s` in the repository. The rebuild is verified **byte-identical** to the original `prg.bin`.
---
## 1. ROM and Memory Layout
| Property | Value |
|----------|-------|
| Format | iNES / NES 2.0 |
| PRG | 1 × 16 KB (mapped at CPU $C000–$FFFF) |
| CHR | 1 × 8 KB |
| Mirroring | Horizontal |
| PRG bank start | CPU $C000 (`.org $C000`) |
| Linker output | `reconstructed_prg.bin`, byte-identical to `prg.bin` |
### Interrupt vectors (CPU $FFFA–$FFFF)
| Vector | Address | Handler | Notes |
|--------|---------|---------|-------|
| NMI | $C046 | `NMI` | Runs once per frame; the primary frame driver |
| RESET | $C40B | `Reset` | Cold-start initialization |
| IRQ / BRK | $C43E | `IRQ` | Just `rti` (unused) |
---
## 2. Reset — CPU $FFFC → $C40B
**`Reset`** ($C40B)
On power-on or reset:
1. `sei` / `cld` — disable maskable interrupts, clear decimal mode.
2. **Clear CPU RAM** $0000–$07FF — one 256-byte page at a time using the indirect pointer at $00/$01, repeatedly writing $2000 to PPUCTRL during the clear.
3. `jsr InitializeSpriteSortTables` ($C7B1) — set up the sprite sort/index tables at $0377, $038C, $0399.
4. `jsr DisablePpuRendering` ($E96C) — turn off PPU output.
5. **Disable APU** — write $00 to APUSTATUS ($4015) to silence all channels, then select the five-step frame-counter mode and inhibit frame-counter IRQ via JOY2 ($4017).
6. `jsr ExpandRecordPointerList` ($C8BB) with inline data — builds record pointer structures.
7. `lda #$1A / sta PpuMaskShadow` — initial PPU mask shadow value.
8. `jmp InitializeTitleScreen` ($C74A) — RESET does not return; it transfers directly to the title screen.
---
## 3. NMI / Frame Heartbeat — `NMI` ($C046)
The NMI fires once per frame and is the central frame driver. It does **not** simply return; it performs substantial per-frame work:
1. **Save X and Y** into `NmiSavedX` ($0D) / `NmiSavedY` ($0E).
2. **Read PPUSTATUS** ($2002) to reset the PPU address latch.
3. **OAM DMA** — write $02 to OAMDMA ($4014) to copy $0200–$02FF to sprite memory.
4. **Process the PPU command queue** at $0300 — walk variable-length packets (write packets to $2007, read packets discarded), terminating on a zero-length entry, then reset the queue head (`stx $0300`).
5. **Apply scroll** — write `ScrollX` ($DF) and `ScrollY` ($E0) to $2005.
6. **Apply PPU registers** — write `PpuMaskShadow` ($DE) to $2001 and `PpuCtrlShadow` ($DD) to $2000.
7. `jsr InitializeAudioEngine` ($F9D0) — initialize/advance the audio engine.
8. **Latch controllers** — strobe JOY1 ($4016) with $01 then $00.
9. **Read both controllers** — sample JOY1/JOY2 eight times each, shifting button states into `ControllerPressed1` ($15) / `ControllerPressed2` ($16).
10. **Update controller state** — compute newly-pressed edges (`Pressed = (Pressed & ~Held) | raw`) and update `ControllerHeld1/2` ($11/$12).
11. **START-button branch**:
- If START pressed and `SessionActive` ($1E) is set → `jsr ProcessQueuedObjectEvent` with command $0B.
- If START pressed and session is **not** active → set `TransitionActive` ($1D) and `GameplaySuspended` ($EF), then `jmp InitializeNewSession` ($C49C).
12. `NMI_CheckState` — if `NmiStateFlag` ($1F) is nonzero, skip ahead and restore registers.
13. Otherwise `jsr QueueInlineObjectEvent` ($EB55) with inline command bytes `$05,$21,$C1`, then `jsr ProcessQueuedObjectEvent` ($EB2A) with command $0B.
14. `inc FrameCounter` ($E3).
15. `jsr CalculateControllerAssignments` ($C87E) — derive per-player control state.
16. Store derived input into `InputState1/2` ($13/$14) and `InputAux1/2` ($17/$18).
17. **Restore X, Y, A**, `rti`.
Because the NMI does controller reading and event dispatch, the main loop largely waits on the NMI-driven queued-event system rather than polling hardware directly.
---
## 4. Title Screen — InitializeTitleScreen ($C74A)
Shared entry point used by RESET and by post-match/transition paths:
1. `jsr InitializeQueuedEventSystem` ($EB9D) — clear the $0600–$06FF event page, the queue count ($E2), active-event slots, and set `CurrentObjectIndex` ($E1) = 1.
2. `GameplaySuspended` ($EF) = 0; `TransitionActive` ($1D) = 1.
3. `PpuCtrlShadow` = $80; disable PPU rendering; fill PPU memory (`FillPpuMemoryAfterValueSaved` at $E985 with value $25).
4. `jsr ProcessInlinePpuStream` ($EA63) twice — streams title PPU data (inline records `$8D,$F8` and `$00,$C0`).
5. `jsr EnableNmiAndWaitForFrame` ($E978) — enable NMI and synchronize to a frame.
6. Enable APU channels ($4015 = $0F), set timing byte $FD = $08.
7. `RefreshTitleScreen` ($C77A) loop:
- Update `ModeFlags` ($40).
- `jsr QueueModeIndicatorTiles` ($E5CF).
- Wait for controller input release (SELECT/$20) and for the frame counter / delay byte $1B to elapse.
8. On exit: `TransitionActive` = 0, `GameplaySuspended` = 1, clear audio ($4015), re-init the event system, queue an inline command `$03,$88,$C4`, then `jmp InitializeSessionDisplay` ($C4AA).
### Attract mode — RunAttractMessageLoop ($C43F)
Before reaching the title input loop, `Reset` can flow into `RunAttractMessageLoop`, which:
- Clears `NmiStateFlag`, selects object event bank 1.
- Draws encoded six-character messages via `CopyInlineDisplayRecord` ($EA17) with inline text bytes.
- Exchanges the object event bank ($EB4E), increments `NmiStateFlag`, and loops back to itself — this is the idle attract display until the player presses START.
---
## 5. Session and Match Initialization
### InitializeSessionDisplay ($C4AA)
PPU shadow setup, disable rendering, fill PPU memory with $26, then process an inline PPU stream (`$F2,$F4`).
### InitializeMatchState ($C4B9)
- Clears base position and acceleration fields ($C94F/$C943).
- Sets `$053B` and `PositionSignState` ($B7) to $80.
- `jsr EnableNmiAndWaitForFrame`.
- **Player/team selection**:
- `Random0To5` ($C850) → `PlayerOneSelection` ($34).
- Repeatedly `Random0To5` until a value different from player one → `PlayerTwoSelection` ($35).
- If `GameplaySuspended` is set, skip side selection; otherwise derive `ActivePlayerSide` ($4B) from mode flags or a random bit.
- Queue inline object events `$03,$19,$C8` and `$02,$19,$C8`, set `PlayerSelectionActive` ($44) = 1.
- `WaitForPlayerSelection` ($C4FF) spins until `$43` (`SelectionState`) becomes negative, then queues `$03,$3E,$C4` and falls into `CompleteMatchSetup` ($C50C).
### CompleteMatchSetup ($C50C)
- `FillSelectionBackgroundBlocks` ($C85A), `QueuePlayerSelectionDisplay` ($C7DD), `InitializeRandomBitfields` ($C899), `QueuePlayerValueDisplay` ($CF8B).
- Queue inline command `$09,$37,$CE`, then enter `InitializeRoundState` ($C51E).
---
## 6. Round / Gameplay State Machine
### InitializeRoundState ($C51E)
Round-setup entry point:
- If gameplay is not suspended and `RoundStateIndex` ($33) < $12, select round music cue; otherwise reset round counters.
- `EnableRoundAudio` ($C52A): enable APU channels ($4015 = $0F), set $FD = $08.
- `ResetRoundCounters` ($C536): clear scores ($67/$68) and `RoundStateIndex`.
- `jsr InitializeObjectsFromTable` ($D06B) — initialize objects from the table at $D0A0–$D131.
- Set `$053B` / `PositionSignState` = $80.
- Store a $3C-tick delay, draw an 8-character message, store another $3C delay.
- `SelectRoundMusicCue` ($C566): choose a music delay value ($20 or $40) based on `RoundStateIndex` parity → store in $FD.
- `PrepareRoundObjects` ($C573): queue player-selection display, queue random-state record, clear `MatchCompleteFlag` ($45) and `SecondarySequenceState` ($64).
### ResetGameplayState ($C57F)
- Clear `InningOrSharedScore` ($66), `BaseMotionActive` ($5C).
- Mark four state entries ($0598–$059B) inactive ($FF).
- `jsr QueueRoundNumberDisplay` ($CF67), re-init objects, set sign state to $80.
- Store a $B4-tick delay.
### Round setup continuation points
- `ResumeRoundSetupAtRandomDirection` ($C5A4): queue random-direction record, clear primary/transition sequence state, set `StateSequenceActive` ($46) = 1, clear state field pairs ($03,$23 via $CF51).
- `ResumeRoundSetupAtSelectionDisplay` ($C5BA): clear the 8-character field, clear $0597–$05A6, clear the 38-byte motion field range $056F–$0594.
- Based on `RoundStateIndex` and score comparison ($67 vs $68), either jump to `CompleteMatchState` ($C6F7) or finish the reset.
### FinishGameplayReset ($C5F1)
- `InitializeDefaultObjects` ($D030), clear sign state, select event bank 1 → enter `RunRoundStateLoop` ($C5FE).
### RunRoundStateLoop ($C5FE) — main per-round loop
This is the core gameplay loop:
- If `PendingStateTransition` ($60) is set → `InitializeObjectStateLookup` ($C8A3).
- If either `PendingStateTransition` or `StateSequenceActive` ($46) is nonzero → dispatch state-specific handlers via queued inline commands (`$0D,$77,$D4`, `$0F,$81,$D3`).
- `RunPrimaryRoundHandlers` ($C619):
- `InitializeSecondaryActors` ($D633), `InitializeConditionalObject` ($D126).
- Clear working state: `PendingScoreIncrement`, `BaseMotionPhase`, `BaseMotionEventCount`, `TrajectoryResult`, `PendingStateTransition`, `SpecialSequenceState`, `ObjectTransitionState`, `BaseMotionActive`, `ActionSequenceCounter`, `TrajectoryEvaluationEnabled`, and several others.
- Clear base acceleration and position fields.
- Queue three inline object events (`$25,$5A,$D8`, `$0E,$66,$D1`, `$1F,$76,$CA`).
- `jsr UpdateObjectEventBank_Exchange` with command $08.
- Walk the 22-entry `RoundEventSlotTable` ($C6E1) in reverse, dispatching each via `DispatchNextQueuedEvent` ($EB66).
- Write one inline character ($3F, value $28) to the PPU.
- `ApplyPendingScoreUpdate` ($C67E): if a score increment is pending and no state transition, `jsr UpdateCountersAndDisplay` ($CFA6), clear the increment.
- `StartPostRoundDelay` ($C68D): wait for `PendingEvent` ($6F) to clear, then count down delay counter $48.
- If match is not complete and no sequence is active → `jmp ResumeRoundSetupAtSelectionDisplay` ($C5BA) to start the next round.
- `ProcessPostRoundEvents` ($C6A5): dispatch event slots $0D and $0F, store a $0A-tick delay, decrement a counter based on `RoundStateIndex` parity.
- `CheckPostRoundContinuation` ($C6C5): if match complete → `AdvanceRoundNumber` ($C6CC); otherwise `jmp ResumeRoundSetupAtRandomDirection` ($C5A4).
- `AdvanceRoundNumber` ($C6CC): update counters/display, increment `RoundStateIndex`; if index reaches $12 → `InitializeResultsPosition` ($C708); otherwise `SelectRoundMusicCue` ($C566) to begin the next round.
---
## 7. Per-Frame Object and Motion Updates
### UpdateFrameObjectsAndTimers ($C122)
Called each frame to drive object motion:
- Reset PPU queue offset, OAM write offset, and `FrameState20` ($20).
- Queue/process inline object events ($07,$64,$C2 via bank, then command $01).
- Decrement per-object timers at $063F,X (30 slots); when a timer reaches zero, queue a decoded object event.
- Decrement four short countdowns at $05AD,X.
- If a transition is active, jump to `CommonEngineExit` ($EB6A); otherwise `IntegrateAllObjectPositions` ($C15B).
### IntegrateAllObjectPositions ($C15B)
- For 36 object slots (X stepping by 2, each logical object uses two indices):
- Sign-extend and add horizontal delta ($050B/$050C,X from $056F/$0570,X).
- Sign-extend and add vertical delta ($053C/$053D,X from $0570/$0571,X).
### UpdateBaseMotionSequence ($C18D)
- Integrate velocity/position for three base object indices ($00, $31, $62).
- Reflect base motion values when $056C is negative (negate $056B/$056C and halve $0569/$056A).
- Apply doubled horizontal/vertical velocity adjustments to $0507/$0508 and $0538/$0539.
- `jsr EvaluateTrajectoryResult` ($CAD2).
- Advance `BaseMotionPhase` ($5D); when motion settles, clear base acceleration/position fields.
- `UpdatePositionSignState` ($C228): compute a signed position delta relative to a $0A threshold, track sign changes in `PositionSignState` ($B7), and update rendering flags.
### IntegrateObjectVelocityAndPosition ($C3CA)
Signed fixed-point position integration used by the three base motion call sites: shifts the signed 16-bit velocity right twice (sign-preserving), adds to position ($0507/$0508), then adds the result to the secondary position ($0509/$050A).
---
## 8. Rendering and OAM
### BuildFrameOam ($C2DE)
- If `TransitionActive` is set: copy an 88-byte sprite/OAM template from $F94D into shadow OAM at $0200, set `OamWriteOffset` ($3F), and jump to `TerminatePpuUpdateQueue`.
- Otherwise, if `PlayerSelectionActive` ($44) is set: copy an 8-byte OAM template from $C331, position two sprites from player selection coordinates (scaled ×16 + $50), choose tiles based on `ModeFlags`.
- Otherwise: `SortObjectsByY` ($C339) — sort object indices by signed 16-bit Y coordinates ($0538/$0539) using the index table at $0377/$0378, then render each sorted object via `RenderSpriteRecordList` ($E77B).
### FinalizeOamAndPpuQueue ($C386)
- If `ObjectListFlag` ($C7) is set, skip status OAM; otherwise copy 20 bytes from $EBC2 into shadow OAM and select two extra bytes from $EBBC based on player selections.
- `TerminatePpuUpdateQueue` ($C3AA): write $00 to terminate the PPU command queue at $0300,Y.
- Hide unused sprites by writing $F0 to each remaining OAM Y coordinate (stepping by 4).
- `jsr AdvanceRandomState` ($E5F4), then `jmp CommonEngineExit` ($EB6A).
---
## 9. Batting and Pitching
### Pitching (RunPitcherWindupSequence, $C6044)
- `RunPitcherWindupSequence` animates the pitcher windup (frames selected via `SetWindupFrame`).
- `SelectPitchAction` ($C6139): reads controller input to choose the pitch action.
- `SelectPitchType` ($C6178): searches `PitchTypeThresholdTable` / `PitchDirectionThresholdTable` ($C5973/$C5980) to determine pitch type and direction.
- `AnimatePitchStart` ($C6229), `UpdatePitchMovement` ($C6321): apply pitch trajectory and controller-based horizontal/vertical input.
- Pitch tables: `PitchVelocityBiasTable` ($C6399), `PitchDelayTable` ($C6402), `PitchRenderFlagTable` ($C6405).
### Batting (SelectBatterAction, $C6414)
- `WaitForBatterAction` ($C6422): wait for batter input.
- `UseBatterControllerInput` ($C6464): map controller to batter action slot.
- `SelectActionFromObjectState` ($C6590) with `ObjectActionLookupTable` ($C6502).
- `DispatchActionType` ($C6614): route to the appropriate action handler.
### Object motion and collision
- `MoveControlledObject` ($D371): apply controlled horizontal/vertical offsets (`ControlledHorizontalOffsetTable` / `ControlledVerticalOffsetTable` at $D384/$D38C).
- `TestObjectCollision` ($D38B): object-vs-object collision testing.
- `TestCurrentObjectInput` ($D7D7): resolve controller input for the current object.
- Geometry helpers: `CalculateCurrentObjectGeometry` ($D7AE), `CalculateGeometryDifference` ($D566), `BuildGeometryVector` ($D74D), extensive trigonometry/vector math routines ($D5xx–$D8xx).
---
## 10. Audio Engine
### InitializeAudioEngine ($F9D0)
Initialize the APU: write DMC settings, configure pulse/triangle/noise channel presets, write pulse periods from tables ($FA00–), set up audio lookup data.
### UpdateAudioEngine ($FB05)
Per-frame audio update:
- Clear/finalize primary audio effects.
- Update pulse 1 and pulse 2 effects (preset effects, descending effects, state-change effects, phase effects).
- Update triangle pitch sweeps.
- Dispatch music and noise (`DispatchMusicAndNoise` at $FB65): update music sequencer, noise envelope, timed triangle effects, DMC output.
### Music
- `StartMusicSequence` ($FD34): initialize a music sequence by finding its bank and copying the descriptor.
- Channels: pulse 1 ($FD96), pulse 2 ($FDCF), triangle ($FE0B).
- Music sequence data tables at $FE60–$FFD0 (`MusicSequenceData_FE60` … `MusicSequenceData_FFD0`).
- Sound effect presets and noise/triangle envelopes throughout the $F9xx–$FE2x region.
Audio is advanced from the NMI via `jsr InitializeAudioEngine` ($F9D0) each frame; the broader `UpdateAudioEngine` ($FB05) is called from the event system.
---
## 11. Object Event System (the main-loop engine)
The game is driven by a **queued event system** rather than a simple polling loop:
- `InitializeQueuedEventSystem` ($EB9D): clear $0600–$06FF, queue count $E2, active slots, set current object index.
- `QueueInlineObjectEvent` ($EB55) / `QueueDecodedObjectEvent` ($EB2A): push an event onto the queue.
- `ProcessQueuedObjectEvent` ($EB2A): walk the queue and process active events.
- `DispatchNextQueuedEvent` ($EB66): pop the newest event, save its continuation address on the hardware stack (SP = $FF), and `rts` into it — this is how control transfers between event handlers.
- `CommonEngineExit` ($EB6A): mark the current object inactive; if the queue is empty, wait (`beq DispatchNextQueuedEvent_Wait`) polling $E2; otherwise disable NMI bit 7 and dispatch the next entry.
The main gameplay loop (`RunRoundStateLoop`) and most state handlers ultimately pass through `CommonEngineExit`, which blocks until the NMI or another producer queues the next event. This makes the NMI the real-time heartbeat and the main loop an event consumer.
---
## 12. PPU Streaming Helpers ($E9xx–$EBxx)
A family of routines drives PPU updates from inline byte streams embedded in the code:
- `DisablePpuRendering` ($E96C), `EnableNmiAndWaitForFrame` ($E978).
- `FillPpuMemoryWith26` / `FillPpuMemoryAfterValueSaved` ($E983/$E985): fill PPU memory with a byte value.
- `DecodeInlineParameters` ($EA09), `ReadInlineWord` ($EA28), `CopyInlineBytesToZeroPage` ($EA45).
- `WritePpuControlFromCarry` ($EA6E), `SetPpuAddressFromInlineRecord` ($EA97), `WritePendingPpuByte` ($EABF), `WriteInlinePpuBlock` ($EAC9), `PPUWriteString` ($EAFB).
- `CopyInlineDisplayRecord` ($EA17): draw an encoded text record (count byte followed by character data) — used heavily for titles, scores, round numbers, and announcements.
- `ProcessInlinePpuStream` ($EA63): process a stream of PPU commands.
Many routines are followed by `.byte` inline data that must **not** be disassembled as instructions; the disassembly marks these explicitly.
---
## 13. Match Completion and Results
### CompleteMatchState ($C6F7)
- Increment `RoundStateIndex`.
- If `InningOrSharedScore` ($66) is nonzero → `InitializeResultsPosition`; otherwise draw a single encoded character ($32).
- `InitializeResultsPosition` ($C708): set sign state to $80, draw a 10-character message (`$25,$10,$0A,$16,$0E,$25,$1C,$0E,$1D,$25`), set timing $FD = $4, dispatch event slot 2, queue inline `$02,$88,$C4`.
- `ResultsDelayLoop` ($C737): repeat a $3C-tick delay 10 times.
- Disable APU ($4015 = $00), clear `SessionActive` ($1E), then fall into `InitializeTitleScreen` ($C74A) to return to the title.
---
## 14. Summary of Execution Flow
```
Power On / Reset
│
▼
Reset ($C40B) ───► clear RAM $0000-$07FF, init sprite sort, disable PPU/APU
│
▼
jmp InitializeTitleScreen ($C74A)
│
├─ init event system, draw title screen
├─ EnableNmiAndWaitForFrame (NMI now runs each frame)
├─ WaitForTitleInputRelease (wait for START + input release)
▼
jmp InitializeSessionDisplay ($C4AA)
│
▼
jmp InitializeMatchState ($C4B9)
│
├─ Random0To5 → PlayerOneSelection ($34)
├─ Random0To5 (≠ P1) → PlayerTwoSelection ($35)
├─ derive ActivePlayerSide ($4B)
└─ WaitForPlayerSelection (spin until $43 < 0)
│
▼
CompleteMatchSetup ($C50C)
│
▼
InitializeRoundState ($C51E)
│
├─ enable audio, reset counters, init objects from table ($D06B)
├─ draw messages, select music cue
▼
ResetGameplayState ($C57F) → FinishGameplayReset ($C5F1)
│
▼
RunRoundStateLoop ($C5FE) ◄───────────────────┐
│ │
├─ InitializeObjectStateLookup │
├─ RunPrimaryRoundHandlers │
│ ├─ InitializeSecondaryActors │
│ ├─ InitializeConditionalObject │
│ ├─ walk RoundEventSlotTable │
│ └─ dispatch queued events │
├─ ApplyPendingScoreUpdate │
├─ StartPostRoundDelay (wait $48 ticks) │
├─ ProcessPostRoundEvents │
├─ CheckPostRoundContinuation │
│ ├─ match complete → results → title │
│ └─ AdvanceRoundNumber → next round │
└─ eventually → CommonEngineExit ($EB6A) │
wait for queued event ($E2) │
dispatch next event (rts into it)│
└────────────────────────────────┘
NMI ($C046) [every frame]
├─ PPU command queue flush ($0300)
├─ OAM DMA ($4014)
├─ scroll ($2005), PPU mask/ctrl ($2001/$2000)
├─ InitializeAudioEngine ($F9D0)
├─ controller latch + read (JOY1/JOY2)
├─ controller state (pressed/held edges)
├─ START branch → new session or command $0B
├─ QueueInlineObjectEvent + ProcessQueuedObjectEvent
├─ FrameCounter++, CalculateControllerAssignments
└─ restore registers, rti
```
---
## 15. Key Zero-Page / RAM Symbols
| Address | Symbol | Use |
|---------|--------|-----|
| $00–$01 | IndirectPtrLo/Hi | RAM-clear indirect pointer |
| $0D–$0E | NmiSavedX/Y | NMI register save |
| $0F | DerivedControl0 | Derived control input |
| $11–$12 | ControllerHeld1/2 | Currently held buttons |
| $13–$14 | InputState1/2 | Current input state |
| $15–$16 | ControllerPressed1/2 | Newly pressed buttons |
| $17–$18 | InputAux1/2 | Auxiliary input |
| $19–$1A | RandomStateLo/Hi | PRNG state |
| $1B | TransitionCounter | Transition delay |
| $1D | TransitionActive | Transition in progress |
| $1E | SessionActive | Session/game active flag |
| $1F | NmiStateFlag | NMI state flag |
| $20 | FrameState20 | Frame state |
| $21 | SelectedPitchAction | Current pitch action |
| $33 | RoundStateIndex | Current round/inning index |
| $34–$35 | PlayerOne/TwoSelection | Selected teams |
| $36–$39 | ObjectScreenX/Y (+Hi) | Object screen coordinates |
| $3E–$3F | PpuQueueOffset / OamWriteOffset | PPU/OAM write positions |
| $40 | ModeFlags | Display/mode flags |
| $42 | CurrentObjectArrayOffset | Current object slot |
| $43 | SelectionState | Selection state |
| $44 | PlayerSelectionActive | Player selection active |
| $45 | MatchCompleteFlag | Match finished |
| $46 | StateSequenceActive | State sequence running |
| $48 | DelayCounter | General delay |
| $4B | ActivePlayerSide | Active player (0/1) |
| $4D–$50 | RandomState0/1, RandomBits0/1 | Randomization state |
| $53–$56 | SavedBaseX/Y (Lo/Hi) | Saved base coordinates |
| $5C | BaseMotionActive | Base motion active |
| $5D | BaseMotionPhase | Base motion phase |
| $5E | BaseMotionEventCount | Base motion event count |
| $5F | TrajectoryResult | Trajectory result |
| $60 | PendingStateTransition | Pending state change |
| $66 | InningOrSharedScore | Inning / shared score |
| $67–$68 | PlayerOne/TwoScore | Scores |
| $69–$6A | EventWrite/ReadIndex | Event queue indices |
| $6F | PendingEvent | Pending event |
| $7E | ActionSequenceCounter | Action sequence counter |
| $83 | ObjectApproachCount | Approach counter |
| $86 | TrajectoryEvaluationEnabled | Trajectory eval enabled |
| $8E–$90 | RandomHorizontal/VerticalMotion | Random motion |
| $9D | CurrentActionState | Current action |
| $9F–$A1 | SecondaryActorState0–2 | Secondary actor states |
| $A2 | ObjectSequenceState | Object sequence state |
| $B7 | PositionSignState | Position sign tracking |
| $C7 | ObjectListFlag | Object list flag |
| $C9–$CA | SpriteSortBaseIndex / SpriteArrayEnd | Sort table bounds |
| $DD–$E0 | PpuCtrl/MaskShadow, ScrollX/Y | PPU/shadow/scroll state |
| $E1 | CurrentObjectIndex | Current object being processed |
| $E2 | ScriptDepth / queue count | Queued event count |
| $E3 | FrameCounter | Frame tick counter |
| $EF | GameplaySuspended | Gameplay suspended |
| $F6–$F7 | SoundPtrLo/Hi | Sound pointer |
| $FE | PendingAudioFlags | Pending audio flags |
| $FF | RenderStateFlags | Render state flags |
---
## 16. Notes on the Disassembly
- The source is **21,624 lines** and thoroughly annotated with symbolic names for nearly every routine, table, and data block — there are no generic `Label_Cxxxx` placeholders.
- Numerous routines are followed by inline `.byte` data blocks consumed by `QueueInlineObjectEvent`, `CopyInlineDisplayRecord`, or `ProcessInlinePpuStream`; these are intentionally left as raw bytes and marked in comments.
- The PRG is a single 16 KB bank at $C000–$FFFF; calls to $Dxxx/$Exxx/$Fxxx targets are all within the same bank (no bank switching).
- Build verification: `make` in `copilot/` produces `reconstructed_prg.bin` that is **byte-identical** to `prg.bin` and to the PRG bank of `baseball.nes`.