# 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 `prg.s` in this project folder. The rebuild is verified **byte-identical** to the original `rom.nes`. ## Status **Pass 3 data-table work complete.** `prg.s` is a hand-annotated, byte-identical disassembly of the whole 16 KB PRG: every routine, every inline data block and every data table carries a symbolic name, and no `Label_Cxxxx` placeholder remains. The Pass 3 data-table inventory is the section *"Pass 3 data-table inventory"* below — it lists all 120 named tables and all 93 inline data blocks, with the structure of each and the instruction that reads it. Two categories of block are still **uninterpreted**, and the inventory says so rather than guessing: * The rendering / animation array at `$ED00-$F94C` is read as a whole by the renderer; the individual 256-byte pages are named but the record format inside a page is not yet established. * The music / audio data at `$FE40-$FFE1` and the audio lookup data at `$FA00-$FA5F` are named by region; the exact record boundaries inside them are not yet established. --- ## 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.nes`, byte-identical to `rom.nes` | ### 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` ($C8E8), `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–$D125. - 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` ($C8A2). - 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` ($D610), `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, $D18B) - `RunPitcherWindupSequence` animates the pitcher windup (frames selected via `SetWindupFrame`, $D1A2). - `SelectPitchAction` ($D1D0): reads controller input to choose the pitch action. - `SelectPitchType` ($D1EA): searches `PitchTypeThresholdTable` / `PitchDirectionThresholdTable` ($D15D/$D14B) to determine pitch type and direction. - `AnimatePitchStart` ($D212), `UpdatePitchMovement` ($D265): apply pitch trajectory and controller-based horizontal/vertical input. - Pitch tables: `PitchVelocityBiasTable` ($D2A4), `PitchDelayTable` ($D2A7), `PitchRenderFlagTable` ($D2AA). ### Batting (SelectBatterAction, $D2AD) - `WaitForBatterAction` ($D2B2): wait for batter input. - `UseBatterControllerInput` ($D2D4): map controller to batter action slot. - `SelectActionFromObjectState` ($D32A) with `ObjectActionLookupTable` ($D339). - `DispatchActionType` ($D33C): route to the appropriate action handler. ### Object motion and collision - `MoveControlledObject` ($D382): apply controlled horizontal/vertical offsets (`ControlledHorizontalOffsetTable` / `ControlledVerticalOffsetTable` at $D3F0/$D3F3). - `TestObjectCollision` ($CBEC): object-vs-object collision testing. - `TestCurrentObjectInput` ($DB18): resolve controller input for the current object. - Geometry helpers: `CalculateCurrentObjectGeometry` ($DF45), `CalculateGeometryDifference` ($E4DD), `BuildGeometryVector` ($E513), extensive trigonometry/vector math routines ($E4xx–$E8xx). --- ## 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 $FC60): 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 $FE40–$FFE1 (`MusicSequenceData` … `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` ($EB0A) / `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` ($E99A), `ReadInlineWord` ($E9B4), `CopyInlineBytesToZeroPage` ($E9C2). - `WritePpuControlFromCarry` ($E9D2), `SetPpuAddressFromInlineRecord` ($E9DB), `WritePendingPpuByte` ($E9FC), `WriteInlinePpuBlock` ($EA02), `PPUWriteString`. - `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 | --- ## Pass 3 data-table inventory Every data block in the PRG, what its bytes are, and the instruction in this dump that reads it. The PRG is a single 16 KB bank at `$C000-$FFFF`, so every address below is both the CPU address and the PRG offset plus `$C000`. Two kinds of block exist in this project: * **Named tables** — a labelled run of `.byte` rows in `prg.s`. All 120 are listed below. * **Inline data blocks** — bytes that sit *inside* a routine and are consumed by the `jsr` immediately above them (the queued-event system pulls its arguments straight out of the return address). All 93 are listed in the second table. ### Named tables | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$C000-$C007` | 8 | `TitleCopyrightPpuStream` | PPU command stream for the title-screen copyright line: three-byte records of *PPU address high, PPU address low, data* (`$23,$45,$05`, `$AF,$01,$09,$08,$04`, `$23,$65,$18`). Written into the PPU by `ProcessInlinePpuStream` | `ProcessInlinePpuStream` at `$C008` (the stream is handed to the inline-stream walker; no absolute operand names it) | | `$C024-$C029` | 6 | `TeamInitialTileTable` | One tile index per selectable team initial, in the order A, C, D, P, R, Y: `$0A,$0C,$0D,$19,$1B,$22` | `lda $C024,y` at `$CF97` and `$CF9F`, indexed by the player's team selection | | `$C02A-$C02F` | 6 | `TeamSelectionSpriteTileTable` | Sprite tile used for the selection marker of each of the six teams | `lda $C02A,y` at `$C7F9` and `$C80D` | | `$C030-$C035` | 6 | `TeamSelectionSpriteXTable` | Screen X of each team's selection marker sprite: `$30,$21,$10,$30,$21,$10` | `lda $C030,y` at `$C7FF` and `$C813` | | `$C331-$C338` | 8 | `OAMTemplate_C331` | 8-byte OAM template (two sprites' Y, tile, attribute, X) copied into the shadow OAM when the player-selection screen is drawn | `lda $C331,x` at `$C2FB` | | `$C6E1-$C6F6` | 22 | `RoundEventSlotTable` | 22 event-slot indices, ascending by two (`$04,$06,$08,$0A,$0C,$0E,$10,$12`, …); the loop at `$C66A` walks it in reverse and dispatches each slot | `ldy RoundEventSlotTable,x` at `$C66A` | | `$CB35-$CB38` | 4 | `TrajectoryResultTable` | 4 signed result bytes, indexed by the two-bit value computed at `$CAFC-$CAFF` | `lda TrajectoryResultTable,x` at `$CB00` | | `$CD22-$CD24` | 3 | `StateHorizontalLowTable` | Low byte of the horizontal displacement applied to the animated object, one entry per state (`$00,$80,$00`). The code's base is `$CD21`, one byte below the table, so entry 0 as read is the operand byte `$2F` of the `bne` at `$CD20` | `lda $CD21,x` at `$CD54` (stored to `$050B`) | | `$CD25-$CD27` | 3 | `StateHorizontalHighTable` | High byte of the same displacement (`$09,$FF,$F7`); code base `$CD24` | `lda $CD24,x` at `$CD5A` (stored to `$050C`) | | `$CD28-$CD2A` | 3 | `StateVerticalLowTable` | Low byte of the vertical displacement (`$00,$00,$00`); code base `$CD27` | `lda $CD27,x` at `$CD60` (stored to `$053C`) | | `$CD2B-$CD2D` | 3 | `StateVerticalHighTable` | High byte of the vertical displacement (`$02,$0A,$02`); code base `$CD2A` | `lda $CD2A,x` at `$CD66` (stored to `$053D`) | | `$CE1A-$CE21` | 8 | `CelebrationPaletteSequence` | 8 palette bytes appended one at a time during the home-run celebration | `lda CelebrationPaletteSequence,y` at `$CE0F` | | `$CEC9-$CECE` | 6 | `AnnouncementTextBlank` | Entry `$00` of the 12-entry announcement table: six encoded characters, all `$25` (a blank) | `lda AnnouncementTextTable,y` at `$CEBC`, with A = the entry offset chosen by `DisplayAnnouncementPair` at `$CEA5` | | `$CECF-$CED4` | 6 | `AnnouncementTextFoul` | Entry `$06` of the announcement table (`$25,$0F,$18,$1E,$15,$25`) | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CED5-$CEDA` | 6 | `AnnouncementTextStrike` | Entry `$0C` (`$1C,$1D,$1B,$12,$14,$0E`) | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CEDB-$CEE0` | 6 | `AnnouncementTextOut` | Entry `$12` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CEE1-$CEE6` | 6 | `AnnouncementTextBall` | Entry `$18` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CEE7-$CEEC` | 6 | `AnnouncementTextWalk` | Entry `$1E` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CEED-$CEF2` | 6 | `AnnouncementTextSingle` | Entry `$24` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CEF3-$CEF8` | 6 | `AnnouncementTextTwoBase` | Entry `$2A` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CEF9-$CFE6` | 6 | `AnnouncementTextThreeBase` | Entry `$30` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CEFF-$CF04` | 6 | `AnnouncementTextDeadBall` | Entry `$36` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CF05-$CF0A` | 6 | `AnnouncementTextChange` | Entry `$3C` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$CF0B-$CF10` | 6 | `AnnouncementTextSafe` | Entry `$42` | `lda AnnouncementTextTable,y` at `$CEBC` | | `$D0A0-$D0A6` | 7 | `ObjectInit_D0A0` | Record 0 of the object-initialisation table: object-array offset `$06`, sprite index `$7A` (`$039B`), attribute `$7A` (`$03B0`), X = `$FECD`, Y = `$F84C` | `ldx ObjectInitializationTable,y` at `$D06C`; the fields are read by `lda ObjectInitFrameField,y` at `$D08C`, `lda ObjectInitAttributeField,y` at `$D092`, `lda ObjectInitXLowField,y` at `$D071`, `lda ObjectInitXHighField,y` at `$D077`, `lda ObjectInitYLowField,y` at `$D07D`, `lda ObjectInitYHighField,y` at `$D083` | | `$D0A7-$D0AD` | 7 | `ObjectInit_D0A7` | Record 1 (object-array offset `$08`) | as above | | `$D0AE-$D0B4` | 7 | `ObjectInit_D0AE` | Record 2 | as above | | `$D0B5-$D0BB` | 7 | `ObjectInit_D0B5` | Record 3 | as above | | `$D0BC-$D0C2` | 7 | `ObjectInit_D0BC` | Record 4 | as above | | `$D0C3-$D0C9` | 7 | `ObjectInit_D0C3` | Record 5 | as above | | `$D0CA-$D0D0` | 7 | `ObjectInit_D0CA` | Record 6 | as above | | `$D0D1-$D0D7` | 7 | `ObjectInit_D0D1` | Record 7 | as above | | `$D0D8-$D0DE` | 7 | `ObjectInit_D0D8` | Record 8 | as above | | `$D0DF-$D0E5` | 7 | `ObjectInit_D0DF` | Record 9 | as above | | `$D0E6-$D0EC` | 7 | `ObjectInit_D0E6` | Record 10 | as above | | `$D0ED-$D0F3` | 7 | `ObjectInit_D0ED` | Record 11 | as above | | `$D0F4-$D0FA` | 7 | `ObjectInit_D0F4` | Record 12 | as above | | `$D0FB-$D101` | 7 | `ObjectInit_D0FB` | Record 13 | as above | | `$D102-$D108` | 7 | `ObjectInit_D102` | Record 14 | as above | | `$D109-$D10F` | 7 | `ObjectInit_D109` | Record 15 | as above | | `$D110-$D116` | 7 | `ObjectInit_D110` | Record 16 | as above | | `$D117-$D11D` | 7 | `ObjectInit_D117` | Record 17 | as above | | `$D11E-$D124` | 7 | `ObjectInit_D11E` | Record 18 | as above | | `$D125` | 1 | `ObjectInitTable_End` | Zero byte that terminates the object-initialisation table | `ldx ObjectInitializationTable,y` at `$D06C` → `beq ObjectInitializationReturn` at `$D06F` | | `$D14B-$D153` | 9 | `PitchDirectionThresholdTable` | 9 ascending thresholds compared against the player's horizontal input to choose the pitch direction | `cmp PitchDirectionThresholdTable,x` at `$D1FE` | | `$D154-$D15C` | 9 | `PitchDirectionResultTable` | The 9 direction values returned when a threshold matches | `lda PitchDirectionResultTable,x` at `$D203` | | `$D15D-$D161` | 5 | `PitchTypeThresholdTable` | 5 thresholds compared against the player's vertical input to choose the pitch type | `cmp PitchTypeThresholdTable,x` at `$D1EE` | | `$D162-$D166` | 5 | `PitchTypeResultTable` | The 5 pitch types returned when a threshold matches | `lda PitchTypeResultTable,x` at `$D1F3` | | `$D2A4-$D2A6` | 3 | `PitchVelocityBiasTable` | 3-byte velocity bias, one entry per pitch type | `sbc PitchVelocityBiasTable,x` at `$D245` | | `$D2A7-$D2A9` | 3 | `PitchDelayTable` | 3-byte delay, one entry per pitch type | `lda PitchDelayTable,x` at `$D249` | | `$D2AA-$D2AC` | 3 | `PitchRenderFlagTable` | 3-byte render flag, one entry per pitch type | `lda PitchRenderFlagTable,x` at `$D23B` | | `$D339-$D33B` | 3 | `ObjectActionLookupTable` | Action/control byte for the selected batter action; the code's base is `$D338`, one byte below the label | `lda $D338,x` at `$D334` | | `$D3F0-$D3F2` | 3 | `ControlledHorizontalOffsetTable` | 3 signed horizontal offsets applied to a controlled object | `lda ControlledHorizontalOffsetTable,y` at `$D393` | | `$D3F3-$D3F7` | 5 | `ControlledVerticalOffsetTable` | 5 signed vertical offsets applied to a controlled object | `lda ControlledVerticalOffsetTable,y` at `$D3C5` | | `$D5B9-$D5BD` | 5 | `RandomMotionBaseTable` | 5 randomised base values (`$C0,$E0,$00,$40,$60`); the code's base is `$D5B8`, one byte below the label | `adc $D5B8,x` at `$D517` | | `$D5BE-$D5C2` | 5 | `RandomMotionThresholdTable` | 5 thresholds (`$00,$4B,$85,$BC,$F0`); the code's base is `$D5BD`, one byte below the label | `cmp $D5BD,x` at `$D504` | | `$D5C3-$D5CC` | 10 | `RandomMotionAngleTable` | 10 offset/result values added to the randomised motion angle | `adc $D5C3,x` at `$D532` | | `$D5CD` | 1 | `RandomMotionAnglePadding` | One standalone byte between the angle table and the angle thresholds; its role is not established | **nothing in the dump reads it** | | `$D5CE-$D5D7` | 10 | `RandomMotionAngleThresholdTable` | 10 thresholds; the search starts at X = `$0A` and decrements before comparing, so the first read is `$D5D7` | `cmp $D5CE,x` at `$D528` | | `$D837-$D842` | 12 | `AnimationFrameBaseTable` | 12 replacement / lower animation frames, one per animation range | `cmp AnimationFrameBaseTable,y` at `$D826`; `lda AnimationFrameBaseTable,y` at `$D82B` | | `$D843-$D84E` | 12 | `AnimationFrameLimitTable` | 12 upper animation-frame thresholds, one per range | `cmp AnimationFrameLimitTable,y` at `$D821` | | `$D84F-$D85A` | 12 | `AnimationFrameDelayTable` | 12 delays, one per range (most are six ticks; range four waits nine) | `lda AnimationFrameDelayTable,y` at `$D831` | | `$DA9C-$DA9F` | 4 | `ObjectAnimationAttributeTable` | 4 animation attribute bytes; the code's base is `$DA9B`, one byte below the label, so Y = `$01-$04` | `lda $DA9B,y` at `$DA8F` | | `$DAA0-$DAA3` | 4 | `ObjectAnimationFrameTable` | 4 animation frame bytes; the code's base is `$DA9F`, one byte below the label | `lda $DA9F,y` at `$DA95` | | `$DBB0-$DBB7` | 8 | `InputMaskTable` | 8 controller-input masks, one per object selector; adjacent entries repeat in pairs because neighbouring selectors accept the same buttons | `and InputMaskTable,y` at `$DB25` | | `$DBF6-$DBFD` | 8 | `SecondaryAnimationBaseTable` | 8 base / reset animation frames for the secondary (fielding) objects | `cmp SecondaryAnimationBaseTable,y` at `$DBE8`; `lda SecondaryAnimationBaseTable,y` at `$DBED` | | `$DBFE-$DC05` | 8 | `SecondaryAnimationLimitTable` | 8 upper animation-frame limits for the secondary objects | `cmp SecondaryAnimationLimitTable,y` at `$DBE3` | | `$DC29` | 1 | `VerticalAdjustmentTable` | Single signed vertical adjustment byte; the tables that follow overlap it | `adc VerticalAdjustmentTable,y` at `$DC20` | | `$DC2A-$DC30` | 7 | `HorizontalAdjustmentTable` | 7 signed horizontal adjustment bytes, overlapping the vertical table | `adc HorizontalAdjustmentTable,y` at `$DC0F` | | `$DC88-$DC90` | 9 | `ObjectEventSlotTable` | 9 event-slot indices, each an even object-array offset; halving each gives the compact object-state index | `ldy ObjectEventSlotTable,x` at `$DC62`; `ldx ObjectEventSlotTable,y` at `$DC74` and `$DCA6` | | `$DF12-$DF14` | 3 | `SetupCurrentObjectGeometry` | Three raw bytes that copy the saved base coordinates into the geometry argument structure; the original ROM uses absolute addressing for zero-page `$53-$56`, and raw bytes keep CA65 from shortening the instruction | read as part of the surrounding routine; **nothing in the dump names it directly** | | `$DFC4-$DFD3` | 16 | `SignedCoordinateOffsetTable` | 8 little-endian signed 16-bit offsets (`$F800`, `$0800`, `$F800`, `$0000`, `$00F9`, `$0007`, `$00F9`, `$FE00`); later routines index the region through nearby or overlapping bases | read through the overlapping bases in the geometry routines; **no single absolute operand names it** | | `$DFEB-$DFEE` | 4 | `ObjectDisplayAttributeTable` | 4 display attribute bytes; the load at `$DFDA` intentionally uses base `$DFEA`, so Y = `$01-$04` | `lda $DFEA,y` at `$DFDA` | | `$E01D-$E020` | 4 | `ObjectStateLookupData` | 4 bytes of lookup data for the object-state routines | `lda $E01D,x` at `$E111` | | `$E021-$E026` | 6 | `ObjectStateThresholdTable` | 6 thresholds compared against the object's state value | `cmp $E021,y` at `$DB6A` and `$E0E0` | | `$E027-$E036` | 16 | `ObjectStateResultTable` | 16 result bytes selected by the state lookup | `eor $E027,y` at `$E0D8`; `lda $E027,y` at `$E0ED` | | `$E295-$E29A` | 6 | `CandidateObjectOffsetTable` | 6 even object-array offsets searched by `FindNearestCandidateObject`; halving each gives the compact index | `ldy CandidateObjectOffsetTable,x` at `$E266` and `$E291` | | `$E319-$E320` | 8 | `CurrentObjectAnimationBaseTable` | 8 base / reset frames for the current object's animation ranges | `cmp CurrentObjectAnimationBaseTable,y` at `$E301`; `lda CurrentObjectAnimationBaseTable,y` at `$E306` | | `$E321-$E328` | 8 | `CurrentObjectAnimationLimitTable` | 8 exclusive upper frame limits | `cmp CurrentObjectAnimationLimitTable,y` at `$E2FC` | | `$E329-$E330` | 8 | `CurrentObjectAnimationDelayTable` | 8 delays, one per range | `lda CurrentObjectAnimationDelayTable,y` at `$E30C` | | `$E413-$E432` | 32 | `GeometryParameterData` | First 32 bytes of the geometry parameter block; later routines index it with calculated and overlapping offsets | `lda GeometryParameterData,y` at `$E4C2` | | `$E433-$E452` | 32 | `GeometryParameterData_Block2` | Second 32 bytes of the same structure | `lda $E433,y` at `$E4C7` | | `$E453-$E46A` | 24 | `GeometryParameterData_Block3` | Third (24-byte) block of the same structure | `lda $E453,y` at `$E4C8` | | `$E761-$E76D` | 13 | `RenderingStateTable_Default` | First 13-byte rendering state record | `lda $E761,y` at `$E75A`; the code's base is `$E761` | | `$E76E-$E77A` | 13 | `RenderingStateTable_Alternate` | Second 13-byte rendering state record | `lda $E76E,y` at `$E75A` | | `$EBBC-$EBDB` | 32 | `RenderingData_EBBC` | Compact rendering / animation records (repeating `$41` fields and control bytes); the block starts with `$D6 $E1`, which is not a plausible entry point | `lda $EBBC,y` at `$C39A` and `$C3A2` | | `$EBDC-$EBFB` | 32 | `RenderingData_EBDC` | The same record pattern continued; the exact record boundaries are not yet established | `lda $EBDC,y` at `$C3A4` | | `$EBFC-$EC1B` | 32 | `RenderingData_EBFC` | The same record pattern continued | `lda $EBFC,y` at `$C3A6` | | `$ED00-$EDFF` | 256 | `RenderingAnimationData_ED00` | One 256-byte page of rendering / animation data. The pages `$ED00`-`$F8FF` are read as a single array by the renderer, so each 256-byte page is one page of sprite/attribute records | the renderer indexes the whole `$ED00`-`$F8FF` array; no single absolute operand names this page | | `$EE00-$EEFF` | 256 | `RenderingAnimationData_EE00` | Second page of the same array | as above | | `$EF00-$EFFF` | 256 | `RenderingAnimationData_EF00` | Third page | as above | | `$F000-$F0FF` | 256 | `RenderingAnimationData_F000` | Fourth page | as above | | `$F100-$F1FF` | 256 | `RenderingAnimationData_F100` | Fifth page | as above | | `$F200-$F2FF` | 256 | `RenderingAnimationData_F200` | Sixth page | as above | | `$F300-$F3FF` | 256 | `RenderingAnimationData_F300` | Seventh page | as above | | `$F400-$F4FF` | 256 | `RenderingAnimationData_F400` | Eighth page | as above | | `$F500-$F5FF` | 256 | `RenderingAnimationData_F500` | Ninth page | as above | | `$F600-$F6FF` | 256 | `RenderingAnimationData_F600` | Tenth page | as above | | `$F700-$F7FF` | 256 | `RenderingAnimationData_F700` | Eleventh page | as above | | `$F800-$F8FF` | 256 | `RenderingAnimationData_F800` | Twelfth page | as above | | `$F900-$F94C` | 77 | `RenderingAnimationData_F900` | Last partial page of the array | as above | | `$F9A5-$F9CF` | 43 | `RenderingAnimationData_F9A5` | Trailing animation data after the `$FF` padding at `$F94D-$F9F4` | **nothing in the dump names it** | | `$F9F4-$F9FF` | 12 | `AudioPadding_F9F4` | `$FF` padding between the rendering data and the audio data | **nothing in the dump reads it** | | `$FA00-$FA5F` | 96 | `AudioLookupData_FA00` | Adjacent audio tables: 20 little-endian APU timer periods (`$F007`, `$6900`, `$0000`, `$D400`, …) up to `$FA41`, then the note-duration table at `$FA4A` (`$00,$20,$05,$0A,$14,$28,$0D,$21`) and a rhythm table at `$FA50` | `lda $FA00,y` at `$FA9A` (timer high bytes); the low-byte base `$FA01` and the duration base `$FA4A` are referenced directly by the audio routines | | `$FD0C-$FD13` | 8 | `NoiseEnvelopeSequence` | 8 envelope values; the routine at `$FD14` derives a three-bit index from it | `lda NoiseEnvelopeSequence,y` at `$FD1F` | | `$FE40-$FE5F` | 32 | `MusicSequenceData` | First 32 bytes of the music sequence / note / duration / envelope data consumed by the sequence engine at `$FD14-$FE3F` | `lda $FE40,y` at `$FD49` and `$FD51` | | `$FE60-$FE7F` | 32 | `MusicSequenceData_FE60` | Continuation of the music data | `lda $FE60,y` at `$FD53` | | `$FE80-$FE9F` | 32 | `MusicSequenceData_FE80` | Continuation of the music data | `lda $FE80,y` at `$FD55` | | `$FEA0-$FEBF` | 32 | `MusicSequenceData_FEA0` | Continuation of the music data | `lda $FEA0,y` at `$FD57` | | `$FEC0-$FEDF` | 32 | `MusicSequenceData_FEC0` | Continuation of the music data | `lda $FEC0,y` at `$FD59` | | `$FEE0-$FEFF` | 32 | `MusicSequenceData_FEE0` | Continuation of the music data | `lda $FEE0,y` at `$FD5B` | | `$FF00-$FF1F` | 32 | `MusicSequenceData_FF00` | Continuation of the music data | `lda $FF00,y` at `$FD5D` | | `$FF20-$FF3F` | 32 | `MusicSequenceData_FF20` | Continuation of the music data | `lda $FF20,y` at `$FD5F` | | `$FF40-$FF5F` | 32 | `MusicSequenceData_FF40` | Continuation of the music data | `lda $FF40,y` at `$FD61` | | `$FF60-$FF7F` | 32 | `MusicSequenceData_FF60` | Continuation of the music data | `lda $FF60,y` at `$FD63` | | `$FF80-$FF9F` | 32 | `MusicSequenceData_FF80` | Continuation of the music data | `lda $FF80,y` at `$FD65` | | `$FFA0-$FFBF` | 32 | `MusicSequenceData_FFA0` | Continuation of the music data | `lda $FFA0,y` at `$FD67` | | `$FFC0-$FFCF` | 16 | `MusicSequenceData_FFC0` | Continuation of the music data | `lda $FFC0,y` at `$FD69` | | `$FFD0-$FFE1` | 18 | `MusicSequenceData_FFD0` | Final music / audio sequence data | `lda $FFD0,y` at `$FD6B` | | `$FFE2-$FFF9` | 24 | `UnusedRomPadding` | `$52,$00` finish the audio data above; the rest through `$FFF9` is `$FF` padding | **nothing in the dump reads it** | ### Inline data blocks These are the bytes that sit inside a routine and are pulled out of the return address by the helper that the `jsr` above them names. They are listed here so that the inventory covers every byte of the PRG that is not an instruction. | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$C01B` | 27 | inline after `TitleCopyrightPpuStream` | 4 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$C023` | 1 | inline after `TitleCopyrightPpuStream` | 1 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$C0F3` | 3 | inline after `NMI_CheckState` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C0F0` | | `$C12D` | 3 | inline after `UpdateFrameObjectsAndTimers` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C12A` | | `$C246` | 3 | inline after `SaveSignedPositionDelta` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C243` | | `$C434` | 3 | inline after `Reset_ClearPageLoop` | 1 `.byte` row(s) of inline data that follow the call | `ExpandRecordPointerList` at `$C431` | | `$C456` | 7 | inline after `RunAttractMessageLoop` | 3 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C44F` | | `$C475` | 7 | inline after `RunAttractMessageLoop` | 3 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C46E` | | `$C4B7` | 2 | inline after `InitializeSessionDisplay` | 1 `.byte` row(s) of inline data that follow the call | `ProcessInlinePpuStream` at `$C4B4` | | `$C4F2` | 3 | inline after `DeriveSelectedSideFromMode` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C4EF` | | `$C4F8` | 3 | inline after `StartPlayerSelection` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C4F5` | | `$C509` | 3 | inline after `WaitForPlayerSelection` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C506` | | `$C51B` | 3 | inline after `CompleteMatchSetup` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C518` | | `$C55D` | 9 | inline after `ResetRoundCounters` | 3 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C555` | | `$C610` | 3 | inline after `CheckRoundTransitionState` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C60D` | | `$C616` | 3 | inline after `CheckRoundTransitionState` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C613` | | `$C654` | 3 | inline after `RunPrimaryRoundHandlers` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C651` | | `$C65A` | 3 | inline after `RunPrimaryRoundHandlers` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C657` | | `$C660` | 3 | inline after `RunPrimaryRoundHandlers` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C65D` | | `$C67C` | 2 | inline after `ProcessRoundEventSlotTable` | 1 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C679` | | `$C706` | 2 | inline after `CompleteMatchState` | 1 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C703` | | `$C71F` | 11 | inline after `InitializeResultsPosition` | 3 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C716` | | `$C730` | 3 | inline after `InitializeResultsPosition` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C72D` | | `$C762` | 2 | inline after `InitializeTitleScreen` | 1 `.byte` row(s) of inline data that follow the call | `ProcessInlinePpuStream` at `$C75F` | | `$C767` | 2 | inline after `InitializeTitleScreen` | 1 `.byte` row(s) of inline data that follow the call | `ProcessInlinePpuStream` at `$C764` | | `$C7AB` | 3 | inline after `WaitForTitleInputRelease` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$C7A8` | | `$C7D8` | 9 | inline after `ClearEightCharacterField` | 3 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C7D0` | | `$C7EC` | 8 | inline after `QueuePlayerSelectionDisplay` | 3 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$C7E4` | | `$C910` | 3 | inline after `QueueRandomStateRecord` | 1 `.byte` row(s) of inline data that follow the call | `ExpandRecordPointerList` at `$C90D` | | `$C917` | 3 | inline after `QueueAlternateRandomStateRecord` | 1 `.byte` row(s) of inline data that follow the call | `ExpandRecordPointerList` at `$C914` | | `$C936` | 3 | inline after `SaveDirectionSelection` | 1 `.byte` row(s) of inline data that follow the call | `ExpandRecordPointerList` at `$C933` | | `$C93F` | 3 | inline after `QueueAlternateDirectionRecord` | 1 `.byte` row(s) of inline data that follow the call | `ExpandRecordPointerList` at `$C93C` | | `$CA9B` | 3 | inline after `UpdateTrajectoryFrame` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CA98` | | `$CAA4` | 3 | inline after `QueueNegativeTrajectoryCommand` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CAA1` | | `$CAAD` | 3 | inline after `QueueLaterTrajectoryCommand` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CAAA` | | `$CB2A` | 3 | inline after `ApplyNegativeTrajectoryResult` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CB27` | | `$CB31` | 3 | inline after `QueueNegativeResultCommand` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CB2E` | | `$CC16` | 3 | inline after `AdvancePrimaryStateSequence` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CC13` | | `$CC38` | 3 | inline after `QueuePrimaryStateEvent` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CC35` | | `$CCB5` | 3 | inline after `Routine_CCAA_OneTimeCommand` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CCB2` | | `$CD14` | 3 | inline after `Routine_CD09_OneTimeCommand` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CD11` | | `$CD3E` | 3 | inline after `QueueSelectedStateMarker` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CD3B` | | `$CD84` | 5 | inline after `WaitForStateAnimation` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$CD80` | | `$CDAF` | 3 | inline after `RunHomeRunCelebration` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$CDAC` | | `$CDE8` | 9 | inline after `HomeRunAnimationLoop` | 3 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$CDE0` | | `$CF63` | 4 | inline after `QueueThreeBlanks` | 2 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$CF5F` | | `$D033` | 6 | inline after `DisplaySharedCounter` | 2 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$D02F` | | `$D061` | 6 | inline after `ConvertSharedCounterToDigits` | 2 `.byte` row(s) of inline data that follow the call | `CopyInlineDisplayRecord` at `$D05D` | | `$D138` | 5 | inline after `InitializeConditionalObject` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D134` | | `$D16E` | 3 | inline after `InitializePrimaryGameplayActor` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D16B` | | `$D184` | 6 | inline after `ClearPrimaryActorState` | 3 `.byte` row(s) of inline data that follow the call | `LoadBasePositionFieldsInline` at `$D17D` | | `$D1CB` | 3 | inline after `FinishWindupSequence` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D1C8` | | `$D21D` | 3 | inline after `AnimatePitchStart` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D21A` | | `$D262` | 3 | inline after `PitchStartAnimationLoop` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D25F` | | `$D29E` | 3 | inline after `CheckPitchVerticalLimit` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D29B` | | `$D31A` | 3 | inline after `SaveBatterAction` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D317` | | `$D35D` | 3 | inline after `Routine_D355_State1` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D35A` | | `$D36F` | 3 | inline after `Routine_D367_State2` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D36C` | | `$D37B` | 3 | inline after `Routine_D373_State3` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D378` | | `$D480` | 5 | inline after `RunFielderAnimationSequence` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D47C` | | `$D572` | 3 | inline after `SelectRandomMotionOffset` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D56F` | | `$D578` | 3 | inline after `SelectRandomMotionOffset` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D575` | | `$D5A6` | 3 | inline after `FinishRandomMotionSequence` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D5A3` | | `$D5AC` | 3 | inline after `FinishRandomMotionSequence` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D5A9` | | `$D628` | 5 | inline after `InitializeSecondaryActors` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D624` | | `$D630` | 5 | inline after `InitializeSecondaryActors` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D62C` | | `$D638` | 5 | inline after `InitializeSecondaryActors` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D634` | | `$D65A` | 5 | inline after `InitializeState6Actor` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D656` | | `$D68D` | 5 | inline after `InitializeState4Actor` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D689` | | `$D6BB` | 5 | inline after `InitializeState2Actor` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D6B7` | | `$D79F` | 3 | inline after `WaitForVerticalPositionF6` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D79C` | | `$D7A5` | 3 | inline after `WaitForVerticalPositionF6` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D7A2` | | `$D804` | 3 | inline after `WaitForVerticalPosition02` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D801` | | `$D80A` | 3 | inline after `WaitForVerticalPosition02` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D807` | | `$D863` | 3 | inline after `ProcessSecondaryObjectStates` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D860` | | `$D871` | 3 | inline after `CheckObjectState059A` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D86E` | | `$D87F` | 3 | inline after `CheckObjectState059B` | 1 `.byte` row(s) of inline data that follow the call | `QueueInlineObjectEvent` at `$D87C` | | `$D9F2` | 5 | inline after `SecondaryObject_TransitionHandler` | 2 `.byte` row(s) of inline data that follow the call | `LoadObjectPositionInline` at `$D9EE` | | `$DC7A` | 3 | inline after `InitializeObjectEventSequence_DispatchLoop` | 1 `.byte` row(s) of inline data that follow the call | `DispatchInlineObjectCommand` at `$DC77` | | `$DCAC` | 1 | inline after `DispatchSixObjectEvents_Loop` | 1 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$DF17` | 3 | inline after `SetupCurrentObjectGeometry` | 1 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$DF1C` | 3 | inline after `SetupCurrentObjectGeometry` | 1 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$DF21` | 3 | inline after `SetupCurrentObjectGeometry` | 1 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$E17B` | 3 | inline after `ObjectStateDispatch_DifferentObject` | 1 `.byte` row(s) of inline data that follow the call | `DispatchInlineObjectCommand` at `$E178` | | `$E989` | 2 | inline after `FillPpuMemoryAfterValueSaved` | 1 `.byte` row(s) of inline data that follow the call | `SetPpuAddressFromInlineRecord` at `$E986` | | `$EAC0` | 2 | inline after `RunDisplayTransition` | 1 `.byte` row(s) of inline data that follow the call | `SetPpuAddressFromInlineRecord` at `$EABD` | | `$EACA` | 2 | inline after `RunDisplayTransition` | 1 `.byte` row(s) of inline data that follow the call | `SetPpuAddressFromInlineRecord` at `$EAC7` | | `$EAD4` | 2 | inline after `RunDisplayTransition` | 1 `.byte` row(s) of inline data that follow the call | `SetPpuAddressFromInlineRecord` at `$EAD1` | | `$EADC` | 2 | inline after `RunDisplayTransition` | 1 `.byte` row(s) of inline data that follow the call | `SetPpuAddressFromInlineRecord` at `$EAD9` | | `$EAF8` | 1 | inline after `WriteFixedPpuSequence` | 1 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$EAFC` | 6 | inline after `WriteFixedPpuSequence` | 2 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$ECFC` | 228 | inline after `RenderingAndAnimationData_EC1C` | 15 `.byte` row(s) of inline data that follow the call | the routine it follows | | `$F99D` | 88 | inline after `FrameOamTemplate_F94D` | 6 `.byte` row(s) of inline data that follow the call | the routine it follows | ## 16. Notes on the Disassembly - The source is **21,626 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 this folder produces `reconstructed.nes`, compared whole-ROM against `rom.nes`. ## Power-on flow analysis Derived from the disassembly by `_tools/derive_poweron.py`; the call graph is walked from the reset entry. --- ## Entry-point naming Every `jsr`/`jmp` target in this project now carries a semantic name; **none** is left in the address-derived `Sub_CXXX` form. The last one, `Sub_C036`, was a second label at `$C036` sitting on top of the project's own `CalculateRandomizedPitchMotion` label; the duplicate definition is gone and the one call site (`jsr` at `$D256`) now names the routine directly. The routine computes a randomized signed pitch-motion value from the random state at `$0539`.