# 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`.