;---------------------------------------------------- ; NES Specific ;---------------------------------------------------- ; PPU registers PPUCTRL = $2000 PPUMASK = $2001 PPUSTATUS = $2002 OAMADDR = $2003 OAMDATA = $2004 PPUSCROLL = $2005 PPUADDR = $2006 PPUDATA = $2007 OAMDMA = $4014 JOY1 = $4016 APUPC1BASE = $4000 APUPC1SWEEP = $4001 APUPC1TLOW = $4002 APUPC1THIGH = $4003 APUPC2BASE = $4004 APUPC2SWEEP = $4005 APUPC2LOW = $4006 APUPC2HIGH = $4007 APUTRIBASE = $4008 APUTRILOW = $400A APUTRIHIGH = $400B APUNOISEBASE = $400C APUNOISELOOP = $400E APUNOISELENGTH = $400F APUDMABASE = $4010 APUDMALOAD = $4011 APUDMAADDR = $4012 APUDMALEN = $4013 APUCTRL = $4015 APUSTATUS = $4015 APUFRAMECNT = $4017 PAD_RIGHT=$01 PAD_LEFT=$02 PAD_DOWN=$04 PAD_UP=$08 PAD_START=$10 PAD_SELECT=$20 PAD_B=$40 PAD_A=$80 PAD_HORIZONTAL=PAD_LEFT|PAD_RIGHT PAD_VERTICAL=PAD_UP|PAD_DOWN PAD_DPAD=PAD_HORIZONTAL|PAD_VERTICAL PAD_MENU_INPUT=PAD_SELECT|PAD_START|PAD_VERTICAL ;---------------------------------------------------- ; Pacman Specific ;---------------------------------------------------- NmiHandlerOffset = $C10E ResetOffset = $C033 IrqHandlerOffset = $C17E ;============================================================================== ; Zero Page ;============================================================================== ;------------------------------------------------------------------------------ ; General work pointers / scratch ;------------------------------------------------------------------------------ workPtr0 = $00 workPtr1 = $02 workValue0 = $04 workValue1 = $05 workValue2 = $06 workValue3 = $07 workValue4 = $08 workValue5 = $09 ;------------------------------------------------------------------------------ ; Maze pathfinding / direction selection workspace ;------------------------------------------------------------------------------ pathCompareValue = $0A pathFallbackValue = $0B legalDirections = $0C ;------------------------------------------------------------------------------ ; Indirect dispatcher vector ;------------------------------------------------------------------------------ dispatchVector = $10 ;------------------------------------------------------------------------------ ; 16-bit arithmetic workspace ;------------------------------------------------------------------------------ arithmeticValue = $12 arithmeticOperand = $14 ;------------------------------------------------------------------------------ ; Actor fixed-point/motion records ; ; Each actor record occupies four bytes. ;------------------------------------------------------------------------------ pacmanMotion = $1A ghost1Motion = $1E ghost2Motion = $22 ghost3Motion = $26 ghost4Motion = $2A ;------------------------------------------------------------------------------ ; Pac-Man maze targeting ;------------------------------------------------------------------------------ pacmanTargetRow = $2E pacmanTargetColumn = $30 ;------------------------------------------------------------------------------ ; Sprite/animation selection ;------------------------------------------------------------------------------ pacmanSpriteFrame = $32 ghostSpriteFrames = $33 ;------------------------------------------------------------------------------ ; Ghost direction/control state ;------------------------------------------------------------------------------ ghostDirectionFlags = $39 ;------------------------------------------------------------------------------ ; Main gameplay state ;------------------------------------------------------------------------------ gameState = $3F ;------------------------------------------------------------------------------ ; PPU / player-screen state ;------------------------------------------------------------------------------ ppuCtrlShadow = $43 cocktailModeFlag = $46 activePlayerFlag = $47 gameplaySuspended = $48 frameCounter = $4B stateTimer = $4C ;------------------------------------------------------------------------------ ; Input / Pac-Man movement ;------------------------------------------------------------------------------ currentInput = $4D newlyPressedInput = $4E previousInput = $4F pacmanRequestedDirection = $50 pacmanDirection = $51 ;------------------------------------------------------------------------------ ; Score / round / maze progression ;------------------------------------------------------------------------------ highScoreDigits = $61 roundNumber = $67 roundDisplayValue = $68 pelletsRemaining = $6A extraLifeAwarded = $6B actorStatusDisplay = $6C playerScoreDigits = $70 ;------------------------------------------------------------------------------ ; Audio Engine ;------------------------------------------------------------------------------ audioRequestPtr = $F0 audioChannelTablePtr = $F2 audioSequenceTablePtr = $F4 audioWorkPtr = $F6 audioWorkValue = $F7 audioChannelFlags = $F8 audioRequestIndex = $FC audioChannelOffset = $FD audioIndirectPtr = $FE .segment "PRG" .org $C000 CopyrightText: .byte $43,$4F,$50,$59,$20,$52,$49,$47 .byte $48,$54,$20,$31,$39,$38,$34,$20 .byte $31,$39,$39,$33,$20,$4E,$41,$4D .byte $43,$4F,$20,$4C,$54,$44,$2E,$20 .byte $41,$4C,$4C,$20,$52,$49,$47,$48 .byte $54,$53,$20,$52,$45,$53,$45,$52 .byte $56,$45,$44 Reset: ldx #$00 stx PPUCTRL sei cld dex txs nop nop nop ldx #$02 @WaitForPpu: lda #$06 sta PPUMASK lda PPUSTATUS @WaitVBlank: lda PPUSTATUS bpl @WaitVBlank lda #$00 sta PPUSCROLL sta PPUSCROLL dex bne @WaitForPpu lda #$00 tay @ClearLowRam: sta $0000,y iny cpy #$3E bne @ClearLowRam ldx #$08 ldy #$87 @ClearRamPages: sta ($00),y iny bne @ClearRamPages inc $01 cpx $01 bne @ClearRamPages lda #$06 sta PPUMASK lda #$00 sta PPUSCROLL sta PPUSCROLL sta $41 sta $42 sta PPUCTRL sta PPUMASK tay @CheckRamSignature: lda RamSignature,y cmp $0052,y bne @InitializeRamSignature iny cpy #$0F bne @CheckRamSignature beq @SignatureAlreadyPresent @InitializeRamSignature: lda #$00 tay @ClearZeroPage: sta $0000,y iny bne @ClearZeroPage ldy #$00 @CopyRamSignature: lda RamSignature,y sta $0052,y iny cpy #$0F bne @CopyRamSignature lda #$01 sta $64 @SignatureAlreadyPresent: ldy #$00 sty $3F lda #$07 sta $45 lda #$01 cmp $47 bne @SkipSwap cmp $46 bne @SkipSwap @SwapLoop: lda $77,y sta workPtr0 lda $67,y sta $77,y lda workPtr0 sta $67,y iny cpy #$10 bne @SwapLoop @SkipSwap: lda #$1F sta APUCTRL lda #$C0 sta APUFRAMECNT jsr InitializeAudioEngine jsr $EEB4 lda #$88 sta $43 sta PPUCTRL lda #$FF sta $48 sta $023F sta $024B jmp MainEntry RamSignature: .byte "HIROKI AOYAGI" .byte $AA, $55 ;============================================================================== ; $C10E-$C17E: NMI Handler ; ; Local: ; Handles the per-frame NMI. Saves the CPU registers, configures the PPU, ; performs sprite OAM DMA, calls several frame-processing routines, restores ; the current scroll and PPU control state, polls both controllers, selects ; the active controller input, and conditionally performs additional ; per-frame processing. ; ; Global: ; Primary VBlank/frame interrupt entry point. This routine ties together ; rendering updates, sprite DMA, controller polling, and other work that ; needs to occur once per video frame. ; ; Notes: ; $41/$42 are used as the two PPUSCROLL writes. ; $45 supplies the source page for OAM DMA. ; $4D/$4E receive controller 1/2 input bits. ; $4F receives the controller state selected by $46/$47. ; ; The purposes of $40-$48 and the called routines at FlushGameplayPpuUpdates ; ReadNearbyNametableTiles, UpdatePalettes, and InitializeAudioChannels will ; be refined during later passes. ;============================================================================== NmiHandler: pha txa pha tya pha lda #$1E sta PPUMASK lda #$00 sta OAMADDR sta $40 lda $45 sta OAMDMA jsr FlushGameplayPpuUpdates jsr ReadNearbyNametableTiles jsr UpdatePalettes ; Restore the two PPU scroll components. lda $41 sta PPUSCROLL lda $42 sta PPUSCROLL ; Construct the PPUCTRL value for this frame. lda $47 and $46 and #$01 asl ora $43 sta PPUCTRL ; Strobe the controller ports. lda #$01 sta JOY1 lda #$00 sta JOY1 ; Read eight serial bits from both controllers. ldx #$08 @ReadControllers: lda JOY1 and #$03 cmp #$01 ror $4D lda APUFRAMECNT and #$03 cmp #$01 ror $4E dex bne @ReadControllers ; Controller 1 is the default input source. ldx currentInput ; Under the condition controlled by $46/$47, ; use controller 2 instead. lda cocktailModeFlag and activePlayerFlag beq @ControllerSelected ldx newlyPressedInput @ControllerSelected: stx previousInput ; Advance a per-frame counter. inc frameCounter ; $48 gates an additional per-frame routine. lda $48 bne @NmiDone jsr InitializeAudioChannels @NmiDone: pla tay pla tax pla IrqHandler: rti UpdatePalettes: lda $0299 cmp #$0F bne @CheckSecondPalette lda PPUSTATUS lda #$3F sta PPUADDR lda #$00 sta PPUADDR ldy #$00 @CopyFirstPalette: lda $0299,y sta PPUDATA iny cpy #$10 bne @CopyFirstPalette @CheckSecondPalette: lda $02A9 cmp #$0F bne @CheckPaletteState lda PPUSTATUS lda #$3F sta PPUADDR lda #$10 sta PPUADDR ldy #$00 @CopySecondPalette: lda $02A9,y sta PPUDATA iny cpy #$10 bne @CopySecondPalette @CheckPaletteState: lda $0299 cmp #$0F beq @ResetPpuAddress lda $02A9 cmp #$0F beq @ResetPpuAddress rts @ResetPpuAddress: lda #$30 sta PPUADDR lda #$00 sta PPUADDR sta PPUADDR sta PPUADDR sta $0299 sta $02A9 rts ;============================================================================== ; $C1E7-$C266: Main Initialization and State Dispatch ; ; Local: ; Synchronizes with the NMI through $40, initializes PPU/OAM and several ; game-state variables, calls four initialization routines, initializes the ; two 16-byte palette buffers, then enters the main state-dispatch loop. ; ; The loop synchronizes with NMI again, uses $3F as an index into a table of ; five handler addresses, stores the selected address in $10/$11, and jumps ; to that handler indirectly. ; ; Global: ; This is the main program entry reached from Reset after hardware and RAM ; initialization. It transitions the game into its normal frame-driven state ; machine. NMI clears $40 each frame, allowing the foreground/main code to ; proceed through its next state-machine update. ; ; Notes: ; $40 clearly provides synchronization between foreground code and NMI. ; Writing $01 to $40 and spinning until NMI clears it forms a frame wait. ; ; $3F is used here as a state/dispatch index. Its exact semantic states will ; be named as the individual handlers are decoded. ; ; $10/$11 form a temporary indirect JMP pointer. ; ; $C267 begins the state-handler address table and is intentionally left for ; the next decoding pass. ;============================================================================== MainEntry: lda #$01 sta $40 @WaitForInitialNmi: lda $40 bne @WaitForInitialNmi lda #$08 sta PPUCTRL sta ppuCtrlShadow lda PPUSTATUS lda #$00 sta PPUMASK tax @ClearOamPage: sta $0700,x inx bne @ClearOamPage sta cocktailModeFlag jsr ClearGameplayNametables jsr InitializeNametableAttributes jsr InitializeNametableData jsr $E419 ldy #$00 @InitializePaletteBuffers: lda TitleScreenPalette,y sta $0299,y iny cpy #$20 bne @InitializePaletteBuffers lda #$2D sta actorStatusDisplay sta actorStatusDisplay+1 sta actorStatusDisplay+2 sta actorStatusDisplay+3 lda #$FF sta $023F sta gameplaySuspended lda #$00 sta activePlayerFlag sta cocktailModeFlag sta $42 sta $41 sta stateTimer sta $87 sta $88 lda gameState bne @UseAlternatePpuCtrl lda #$8A bne @SetPpuCtrl @UseAlternatePpuCtrl: lda #$88 @SetPpuCtrl: sta PPUCTRL sta ppuCtrlShadow MainDispatchLoop: lda #$01 sta $40 @WaitForNmi: lda $40 bne @WaitForNmi ldy gameState lda $C267,y sta dispatchVector lda $C268,y sta dispatchVector+1 jmp ($0010) ;============================================================================== ; $C267-$C26E: Main State Dispatch Table ; ; Local: ; Four 16-bit addresses used by the indirect state dispatcher at the end of ; MainEntry. $3F is used directly as a byte offset into this table: ; ; lda MainStateHandlerTable,y ; sta $10 ; lda MainStateHandlerTable+1,y ; sta $11 ; jmp ($0010) ; ; This implies the normal state values in $3F are even byte offsets: ; $00, $02, $04, $06 ; ; Global: ; Selects one of four major foreground/game-state handlers. This is the ; top-level state machine entered after Reset initialization and synchronized ; to the NMI-driven frame loop. ; ; Notes: ; The four targets are: ; InitialStateHandler ; PlayerSelectionState ; State04Handler ; TimedStateTransition ; ; Handler names remain provisional until each target is decoded. ;============================================================================== MainStateHandlerTable: .word InitialStateHandler .word PlayerSelectionState .word State04Handler .word TimedStateTransition ;============================================================================== ; $C26F-$C279: Timed State Transition Handler ; ; Local: ; Decrements the counter at $0F once per frame. While the counter remains ; nonzero, control returns to the common NMI synchronization/dispatch loop ; at $C250. ; ; When the counter reaches zero, changes the main state offset at $3F to ; $02 and then returns to the same dispatch loop. ; ; Global: ; Implements the handler selected when the main state offset is $06. ; This appears to be a timed transitional state. Another state places a ; duration in $0F and selects state $06; this handler counts that duration ; down before advancing to state $02. ; ; Notes: ; $0F is clearly being used as a countdown timer here, but its broader use ; should be traced before assigning it a permanent global name. ; ; $3F is the byte offset into MainStateHandlerTable: ; $00 -> InitialStateHandler ; $02 -> PlayerSelectionState ; $04 -> State04Handler ; $06 -> this handler ; ; $C250 is inside MainEntry and is the common frame synchronization point. ;============================================================================== TimedStateTransition: dec $0F bne MainDispatchLoop lda #$02 sta gameState jmp MainDispatchLoop ;============================================================================== ; $C27A-$C2A5: Initial/Input State Handler ; ; Local: ; Checks controller-1 state bits $0C. If either bit is set, selects state ; $06, initializes the countdown at $0F to $5A, and jumps back to MainEntry. ; ; Otherwise increments $42. When $42 reaches $F0, resets it to zero, ; changes the saved PPUCTRL value at $43 to $88, and selects state $06 ; with the same $5A countdown. ; ; If $42 has not yet reached $F0, the initialization of state $06 is ; skipped and execution reaches the common JMP back to MainDispatchLoop. ; ; Global: ; Handler selected by main state offset $00. It appears to manage an early ; or attract/input-wait state, transitioning into the timed state either ; because of controller input or because the scrolling/progression value ; at $42 reaches $F0. ; ; Notes: ; $4D is controller-1 input assembled by NmiHandler. ; $42 is also used as the second PPUSCROLL write during NMI. ; Exact game-state semantics will be refined in later passes. ;============================================================================== InitialStateHandler: lda currentInput and #$0C beq @AdvanceScroll lda #$06 sta gameState lda #$5A sta $0F jmp MainEntry @AdvanceScroll: inc $42 lda #$F0 cmp $42 bne @ReturnToMainLoop lda #$00 sta $42 lda #$88 sta ppuCtrlShadow lda #$06 sta gameState lda #$5A sta $0F @ReturnToMainLoop: jmp MainDispatchLoop ;============================================================================== ; $C2A6-$C30A: Initialize Nametable Data ; ; Local: ; Performs two groups of PPU writes used during initial screen setup. ; ; First phase: ; - Begins at PPU address $20E5. ; - Copies $17 bytes from the ROM data at TitleScreenTileBlock ; - Repeats this for six rows. ; - Advances the destination PPU address by $20 after each row. ; ; Second phase: ; - Processes nine variable-length PPU update records beginning at $C3B0. ; - Each record supplies a two-byte PPU address followed by data bytes. ; - $FF terminates the data portion of each record. ; ; Global: ; Called from MainEntry during initial game setup. This routine constructs ; part of the initial nametable/display before normal state dispatch begins. ; ; Notes: ; $00/$01 are used here as a 16-bit PPU destination address. ; $02 counts the six repeated rows. ; $03 counts $17 bytes within each row. ; ; TitleScreenTileBlock is a repeated row of tile data. ; $C3B0 contains nine encoded PPU update records. ; ; These ROM data blocks will be named more precisely when their contents ; and resulting screen regions are decoded in later passes. ;============================================================================== InitializeNametableData: ; Initial PPU destination = $20E5. lda #$20 sta workPtr0 lda #$E5 sta workPtr0+1 ; Write six rows. lda #$06 sta workPtr1 ldy #$00 @WriteRow: lda PPUSTATUS lda workPtr0 sta PPUADDR lda workPtr0+1 sta PPUADDR ; Each row contains $17 bytes. lda #$17 sta workPtr1+1 @WriteRowBytes: lda TitleScreenTileBlock,y sta PPUDATA iny dec workPtr1+1 bne @WriteRowBytes ; Advance the PPU destination by $20. lda workPtr0+1 clc adc #$20 sta workPtr0+1 lda #$00 adc workPtr0 sta workPtr0 dec workPtr1 bne @WriteRow ; Process nine additional encoded PPU update records. lda #$09 sta workPtr0 ldy #$00 @NextPpuRecord: lda PPUSTATUS ; First two bytes of each record are the PPU destination address. lda TitleScreenPpuRecords,y sta PPUADDR iny lda TitleScreenPpuRecords,y sta PPUADDR iny @WritePpuRecord: lda TitleScreenPpuRecords,y cmp #$FF beq @EndPpuRecord sta PPUDATA iny bne @WritePpuRecord @EndPpuRecord: iny dec workPtr0 bne @NextPpuRecord rts ;============================================================================== ; $C30B-$C325: Initialize Nametable Attributes ; ; Local: ; Writes $18 bytes from TitleScreenAttributeData to PPU address $23C8. ; ; Global: ; Initializes a portion of the nametable attribute table during startup. ;============================================================================== InitializeNametableAttributes: lda PPUSTATUS lda #$23 sta PPUADDR lda #$C8 sta PPUADDR ldy #$00 @CopyAttributes: lda TitleScreenAttributeData,y sta PPUDATA iny cpy #$18 bne @CopyAttributes rts ;============================================================================== ; $C326-$C3AF: Initial Nametable Tile Block ; ; Local: ; Contains six rows of $17 (23) tile indices. InitializeNametableData copies ; these rows sequentially to the PPU beginning at $20E5, advancing the PPU ; destination by $20 after each row. ; ; Destination layout: ; Row 0 -> $20E5-$20FB ; Row 1 -> $2105-$211B ; Row 2 -> $2125-$213B ; Row 3 -> $2145-$215B ; Row 4 -> $2165-$217B ; Row 5 -> $2185-$219B ; ; Global: ; Forms a rectangular portion of the initial nametable display constructed ; during MainEntry. The first and last rows consist primarily of border ; tiles, while the middle four rows contain the interior graphic/tile data. ; ; Notes: ; Each row is exactly $17 bytes. ; ; Tile values are left numeric during this pass. Their visual meanings can ; be determined later from the CHR-ROM and assigned symbolic tile names if ; useful. ;============================================================================== TitleScreenTileBlock: ; Row 0 -> PPU $20E5-$20FB .byte $E4,$E8,$E8,$E8,$E8,$E8,$E8,$E8 .byte $E8,$E8,$E8,$E8,$E8,$E8,$E8,$E8 .byte $E8,$E8,$E8,$E8,$E8,$E8,$E5 ; Row 1 -> PPU $2105-$211B .byte $EB,$88,$80,$81,$82,$83,$84,$85 .byte $86,$87,$88,$88,$89,$8A,$8B,$8C .byte $8D,$8E,$8F,$90,$91,$A3,$E9 ; Row 2 -> PPU $2125-$213B .byte $EB,$88,$92,$93,$94,$95,$96,$97 .byte $98,$99,$9A,$9B,$9C,$9D,$9E,$9F .byte $A0,$A1,$A2,$A3,$A4,$A3,$E9 ; Row 3 -> PPU $2145-$215B .byte $EB,$88,$92,$A5,$A6,$A7,$A8,$A9 .byte $AA,$AB,$AC,$AD,$AE,$A3,$AF,$D0 .byte $D1,$D2,$A3,$D3,$A4,$A3,$E9 ; Row 4 -> PPU $2165-$217B .byte $EB,$88,$D4,$D5,$D6,$D7,$D8,$D9 .byte $DA,$DB,$88,$88,$DC,$D7,$DD,$DE .byte $DF,$E0,$E1,$E2,$E3,$A3,$E9 ; Row 5 -> PPU $2185-$219B .byte $E7,$EA,$EA,$EA,$EA,$EA,$EA,$EA .byte $EA,$EA,$EA,$EA,$EA,$EA,$EA,$EA .byte $EA,$EA,$EA,$EA,$EA,$EA,$E6 ;============================================================================== ; $C3B0-$C43E: Initial Screen PPU Records ; ; Local: ; Nine variable-length PPU write records consumed by InitializeNametableData. ; Each record has the form: ; ; .byte PPU address high ; .byte PPU address low ; .byte tile data... ; .byte $FF ; ; InitializeNametableData processes exactly nine records. For each record it ; loads the two-byte destination into PPUADDR and copies tile values to ; PPUDATA until the $FF terminator is encountered. ; ; Global: ; Supplies additional initial-screen graphics and text after the main ; six-row tile block has been installed. These records include player-count ; selections and copyright/licensing text as represented by the ROM's ; nametable tile values. ; ; Notes: ; Many values correspond directly to ASCII character codes, but not all do. ; Preserve the original tile bytes rather than converting these records to ; assembler strings during this pass. ; ; $FF is structural and is not written to PPUDATA. ;============================================================================== TitleScreenPpuRecords: ;------------------------------------------------------------------------------ ; Record 0: $C3B0-$C3C7 ; PPU destination: $2065 ; Data length: $15 (21) tiles ;------------------------------------------------------------------------------ .byte $20,$65 .byte $B0,$B3,$B2,$20,$20,$20,$20 .byte $B4,$B5,$B6,$B7,$B8,$B9,$BA,$BB .byte $20,$20,$20,$B1,$B3,$B2 .byte $FF ;------------------------------------------------------------------------------ ; Record 1: $C3C8-$C3D2 ; PPU destination: $220C ; Data length: $08 ; ; Visible tile values correspond to: ; "1 PLAYER" ;------------------------------------------------------------------------------ .byte $22,$0C .byte $31,$20,$50,$4C,$41,$59,$45,$52 .byte $FF ;------------------------------------------------------------------------------ ; Record 2: $C3D3-$C3DE ; PPU destination: $224C ; Data length: $09 ; ; Visible tile values correspond to: ; "2 PLAYERS" ;------------------------------------------------------------------------------ .byte $22,$4C .byte $32,$20,$50,$4C,$41,$59,$45,$52,$53 .byte $FF ;------------------------------------------------------------------------------ ; Record 3: $C3DF-$C3FC ; PPU destination: $22E3 ; Data length: $1B (27) ; ; Mostly text-like tile data containing the original copyright/date/NAMCO ; display. Special tile values are retained numerically. ;------------------------------------------------------------------------------ .byte $22,$E3 .byte $54,$4D,$20,$CC,$20,$5D .byte $20,$31,$39,$38,$30 .byte $20,$31,$39,$39,$33 .byte $20,$4E,$41,$4D,$43,$4F .byte $20,$4C,$54,$44,$5B .byte $FF ;------------------------------------------------------------------------------ ; Record 4: $C3FD-$C411 ; PPU destination: $2327 ; Data length: $12 (18) ; ; Mostly text-like tile data containing: ; NAMCO HOMETEK ... INC ; ; Special tile values remain numeric. ;------------------------------------------------------------------------------ .byte $23,$27 .byte $4E,$41,$4D,$43,$4F,$20 .byte $48,$4F,$4D,$45,$54,$45,$4B .byte $CD .byte $49,$4E,$43 .byte $5B .byte $FF ;------------------------------------------------------------------------------ ; Record 5: $C412-$C428 ; PPU destination: $2366 ; Data length: $14 (20) ; ; Tile values correspond to: ; "LICENSED BY NINTENDO" ;------------------------------------------------------------------------------ .byte $23,$66 .byte $4C,$49,$43,$45,$4E,$53,$45,$44 .byte $20 .byte $42,$59 .byte $20 .byte $4E,$49,$4E,$54,$45,$4E,$44,$4F .byte $FF ;------------------------------------------------------------------------------ ; Record 6: $C429-$C42D ; PPU destination: $2293 ; Data length: $02 ;------------------------------------------------------------------------------ .byte $22,$93 .byte $CE,$CF .byte $FF ;------------------------------------------------------------------------------ ; Record 7: $C42E-$C439 ; PPU destination: $22AC ; Data length: $09 ;------------------------------------------------------------------------------ .byte $22,$AC .byte $23,$24,$25,$26,$27,$28,$29,$2A,$2B .byte $FF ;------------------------------------------------------------------------------ ; Record 8: $C43A-$C43E ; PPU destination: $20FC ; Data length: $02 ;------------------------------------------------------------------------------ .byte $20,$FC .byte $0E,$0F .byte $FF ;============================================================================== ; $C43F-$C45E: Initial Palette Data ; ; Local: ; Contains $20 bytes copied to RAM $0299-$02B8 during MainEntry. The first ; $10 bytes feed PPU palette $3F00-$3F0F and the second $10 bytes feed ; $3F10-$3F1F through UpdatePalettes. ; ; Global: ; Defines the initial background and sprite palette configuration. ; ; Notes: ; Palette values remain numeric during this pass. ;============================================================================== TitleScreenPalette: ; Background palettes .byte $0F,$20,$0F,$06 .byte $0F,$26,$20,$27 .byte $0F,$06,$0F,$26 .byte $0F,$06,$20,$26 ; Sprite palettes .byte $0F,$27,$20,$06 .byte $0F,$11,$20,$33 .byte $0F,$20,$20,$21 .byte $0F,$09,$20,$17 ;============================================================================== ; $C45F-$C476: Initial Nametable Attribute Data ; ; Local: ; Contains $18 attribute bytes copied by InitializeNametableAttributes to ; PPU $23C8-$23DF. ; ; Global: ; Defines background-palette selections for part of the initial nametable ; display. ; ; Notes: ; Attribute bytes remain raw during this first pass. ;============================================================================== TitleScreenAttributeData: .byte $80,$A0,$A0,$A0,$A0,$A0,$A0,$00 .byte $00,$66,$55,$55,$55,$55,$DD,$00 .byte $08,$0A,$0A,$0A,$0A,$0A,$0A,$00 ;============================================================================== ; $C477-$C506: Player Selection State Handler ; ; Local: ; Handles input and display updates for the player-count selection state. ; ; On first entry, identified by $87/$88 both being zero, copies a small ; display-data sequence from $C507 to RAM beginning at $024B and captures ; the current controller states for bits $04 and $08. ; ; $87/$88 form a 16-bit counter that advances once per frame. When its high ; byte reaches $02, the counter is reset and the main state changes to $04. ; ; During normal processing: ; - Detects changes to controller bit $04. ; - When that bit becomes active, toggles $47 between 0 and 1 and changes ; display data associated with the selected player count. ; - Detects changes to controller bit $08. ; - When bit $08 becomes active, clears $48, calls $EEB4, and jumps to ; GameModeEntry. ; ; Global: ; Handler selected by MainStateHandlerTable when $3F = $02. The surrounding PPU ; data and controller behavior strongly indicate that this state implements ; the "1 PLAYER" / "2 PLAYERS" selection screen. ; ; If no selection completes within the $87/$88 timeout, execution advances ; automatically to main state $04. ; ; Notes: ; $50 stores the previous masked state of controller bit $04. ; $51 stores the previous masked state of controller bit $08. ; ; $87/$88 act as a 16-bit frame counter in this state. ; ; $47 is toggled between 0 and 1 when the $04 input becomes active. This ; strongly suggests it represents the current 1-player/2-player selection, ; but the name should be confirmed through later consumers. ; ; $C507 begins an adjacent display-data table consumed during state entry. ;============================================================================== PlayerSelectionState: lda $87 ora $88 bne @AdvanceTimeout ldy #$00 @InitializeDisplayData: lda PlayerCountSelectionDisplayData,y sta $024B,y iny cmp #$FF bne @InitializeDisplayData lda currentInput and #$04 sta pacmanRequestedDirection lda currentInput and #$08 sta pacmanDirection @AdvanceTimeout: clc lda #$01 adc $87 sta $87 lda $88 adc #$00 sta $88 cmp #$02 bne @CheckSelectionInput lda #$00 sta $87 lda #$04 sta gameState jmp MainDispatchLoop @CheckSelectionInput: lda currentInput and #$04 cmp pacmanRequestedDirection beq @CheckConfirmInput sta pacmanRequestedDirection lda #$00 sta $88 lda #$01 sta $87 lda pacmanRequestedDirection beq @CheckConfirmInput ldx #$00 inc activePlayerFlag lda activePlayerFlag and #$01 sta activePlayerFlag beq @UseFirstSelectionData inx @UseFirstSelectionData: lda $C50F,x sta $024D lda $C510,x sta $0251 lda #$22 sta $024B jmp MainDispatchLoop @CheckConfirmInput: lda currentInput and #$08 cmp pacmanDirection bne @ConfirmStateChanged jmp MainDispatchLoop @ConfirmStateChanged: sta pacmanDirection lda pacmanDirection bne @ConfirmPressed jmp MainDispatchLoop @ConfirmPressed: lda #$00 sta gameplaySuspended jsr $EEB4 jmp GameModeEntry ;============================================================================== ; $C507-$C511: Player Selection Display Data ; ; Local: ; Contains display-update data used by PlayerSelectionState. ; ; The first eight bytes, $C507-$C50E, are copied to RAM beginning at $024B ; when the player-selection state is first entered. The copy terminates after ; the $FF byte has also been stored. ; ; The final three bytes, $C50F-$C511, form an overlapping pair table used ; when $47 toggles between its two selection values: ; ; X = 0 -> $5C, $20 ; X = 1 -> $20, $5C ; ; These values are written to $024D and $0251, respectively. ; ; Global: ; Provides the display-update data for the "1 PLAYER" / "2 PLAYERS" ; selection state. The overlapping three-byte table moves a $5C tile ; between two display positions while placing $20 at the other position. ; ; Notes: ; $5C appears to be the selection-marker tile and $20 appears to clear the ; alternate position. Their exact visual appearance can be confirmed later ; against CHR-ROM. ; ; $FF terminates the initialization data copied to $024B. ;============================================================================== PlayerCountSelectionDisplayData: .byte $22,$0A,$5C,$00 .byte $22,$4A,$20,$FF PlayerCountSelectionMarkerTiles: .byte $5C,$20,$5C ;============================================================================== ; $C512-$C52D: State $04 Handler / Substate Dispatcher ; ; Local: ; Handles the top-level processing for main state $04. ; ; First checks controller-1 bits $0C. If either bit is active, changes the ; main state back to $02 and jumps to MainEntry, causing the main display ; initialization sequence to run again. ; ; Otherwise uses $87 as a byte offset into a table of 16-bit handler ; addresses beginning at $C52E. The selected address is placed in $10/$11 ; and execution jumps to it indirectly. ; ; Global: ; This is the fourth major handler in MainStateHandlerTable. Unlike states $00, ; $02, and $06, state $04 contains its own secondary state machine driven ; by $87. ; ; $87 therefore has a different role depending on the current main state. ; In state $02 it participated in a 16-bit frame counter with $88. Here it ; acts as the byte offset into a secondary dispatch table. ; ; Notes: ; $87 appears to take even values because it indexes packed 16-bit addresses ; directly rather than multiplying a state number by two. ; ; The substate table begins immediately afterward at $C52E. ; We leave the individual substate handlers for following passes. ;============================================================================== State04Handler: lda currentInput and #$0C beq @DispatchSubstate lda #$02 sta gameState jmp MainEntry @DispatchSubstate: ldy $87 lda State04SubstateTable,y sta dispatchVector lda State04SubstateTable+1,y sta dispatchVector+1 jmp ($0010) ;============================================================================== ; $C52E-$C545: State $04 Substate Dispatch Table ; ; Local: ; Twelve 16-bit handler addresses selected by State04Handler using $87 as ; a direct byte offset. ; ; Valid offsets observed from the table structure are: ; $00,$02,$04,$06,$08,$0A,$0C,$0E,$10,$12,$14,$16 ; ; Global: ; Defines the secondary state machine used while the top-level game state ; is $04. Several intermediate substates deliberately share the same handler ; at $C573. ; ; Notes: ; Later code advances $87 using two INC instructions, confirming that $87 ; is a byte offset into this table rather than an ordinal state number. ;============================================================================== State04SubstateTable: .word State04Initialize ; $00 .word State04TimedSubstate ; $02 .word State04TimedSubstate ; $04 .word State04TimedSubstate ; $06 .word State04TimedSubstate ; $08 .word State04TimedSubstate ; $0A .word State04TimedSubstate ; $0C .word State04TimedSubstate ; $0E .word State04TimedSubstate ; $10 .word State04TimedSubstate ; $12 .word State04LongDelay ; $14 .word State04FinalPhase ; $16 ;============================================================================== ; $C546-$C572: State $04 Initialization Substate ; ; Local: ; Performs the one-time initialization for state $04 when substate offset ; $87 is $00. ; ; First synchronizes with NMI using $40. It then sets PPUCTRL/$43 to $08, ; disables rendering through PPUMASK, clears $88, and calls three setup ; routines: ; ; $C5D8 ; $C608 ; SetSelectionFlagAndContinue ; ; After setup completes, advances $87 by two, selecting substate $02 on the ; following frame. PPUCTRL/$43 are then changed to $88 before returning to ; MainDispatchLoop. ; ; Global: ; Entry/initialization substate for the secondary state machine used by ; main state $04. It prepares the display and state-specific data before ; handing execution to the shared $C573 substate handler. ; ; Notes: ; $40 continues to act as the foreground/NMI synchronization flag. ; ; $87 is a byte offset into State04SubstateTable. The two INC instructions ; deliberately advance it from $00 to $02. ; ; The three called routines remain structurally named by address until they ; are decoded. $C5D8 and $C608 appear to perform direct PPU initialization, ; while SetSelectionFlagAndContinue operates on state/display data. ;============================================================================== State04Initialize: lda #$01 sta $40 @WaitForNmi: lda $40 bne @WaitForNmi lda #$08 sta PPUCTRL sta ppuCtrlShadow lda #$00 sta PPUMASK sta $88 jsr $C5D8 jsr InitializeState04PpuData jsr LoadState04DisplayData inc $87 inc $87 lda #$88 sta PPUCTRL sta ppuCtrlShadow jmp MainDispatchLoop ;============================================================================== ; $C573-$C588: State $04 Shared Timed Substate Handler ; ; Local: ; Advances $88 once per frame. At $30 frames, clears $88, invokes the ; state-$04 display-data helper, and advances $87 by two to the next ; secondary state. ; ; Global: ; Shared timing handler used by state-$04 substates $02-$12. ; ; Notes: ; LoadState04DisplayData is kept as a raw address temporarily to guarantee ; byte identity while the overlapping $C5A6-$C5D8 region is being resolved. ;============================================================================== State04TimedSubstate: inc $88 lda $88 cmp #$30 bne @ReturnToMainLoop lda #$00 sta $88 jsr $C5A6 inc $87 inc $87 @ReturnToMainLoop: jmp MainDispatchLoop ;============================================================================== ; $C589-$C59F: State $04 Long Delay Substate ; ; Local: ; Increments $88 once per frame and waits until it reaches $80. ; ; While $88 is not $80, returns to MainDispatchLoop. ; ; When $88 reaches $80: ; - Advances $87 by two, selecting substate $16. ; - Clears $88. ; - Returns to MainDispatchLoop. ; ; Global: ; Implements substate $14 of the state $04 secondary state machine. It ; follows the series of $30-frame substates handled by ; State04TimedSubstate and introduces a longer delay before entering the ; final known substate at $16. ; ; Notes: ; $88 is the frame counter for this substate. ; $87 is the byte offset into State04SubstateTable. ; ; Unlike substates $02-$12, reaching the timeout here does not call ; SetSelectionFlagAndContinue. It simply advances from substate $14 to $16. ;============================================================================== State04LongDelay: inc $88 lda $88 cmp #$80 beq @AdvanceSubstate jmp MainDispatchLoop @AdvanceSubstate: inc $87 inc $87 lda #$00 sta $88 jmp MainDispatchLoop ;============================================================================== ; $C59F-$C5A5: Select Alternate Player Mode and Continue ; ; Local: ; Sets $47 to $01 and tail-jumps to the routine at GameModeEntry. ; ; Global: ; Forces the same $47 flag that PlayerSelectionState toggles between $00 ; and $01, then transfers control to GameModeEntry. This strongly links the routine ; to the player-count/player-mode selection logic. ; ; The exact global purpose of GameModeEntry has not yet been decoded, so the routine ; and $47 remain conservatively named during this pass. ; ; Notes: ; There is no RTS. Control is permanently transferred to GameModeEntry. ; ; $47 is known to be constrained to 0/1 by PlayerSelectionState and is also ; consulted by NmiHandler when selecting controller input and constructing ; PPUCTRL. We should trace GameModeEntry before assigning $47 a final semantic name. ;============================================================================== SetSelectionFlagAndContinue: lda #$01 sta activePlayerFlag jmp GameModeEntry ;============================================================================== ; $C5A6-$C5D7: State $04 Display Data Loader ; ; Local: ; Uses $87 as a byte offset into a pointer table at $C687. The selected ; $FF-terminated stream is copied through $00/$01 into RAM beginning at ; $024B, including the $FF terminator. ; ; The byte following the terminator controls an optional secondary copy: ; nonzero -> return ; zero -> copy 16 bytes using a substate-derived offset ; ; Global: ; Supplies update/display data as the state-$04 secondary state machine ; progresses. ; ; Notes: ; $00/$01 form the temporary source pointer. ;============================================================================== LoadState04DisplayData: ldy $87 lda CharacterIntroDisplayDataTable,y sta workPtr0 lda CharacterIntroDisplayDataTable+1,y sta workPtr0+1 ldy #$00 @CopyDisplayData: lda (workPtr0),y sta $024B,y iny cmp #$FF bne @CopyDisplayData lda (workPtr0),y beq @SecondaryCopy rts @SecondaryCopy: lda $87 sec sbc #$02 asl asl tay ldx #$10 @CopySecondaryData: lda CharacterIntroSpriteData,y sta $0760,y iny dex bne @CopySecondaryData rts ;============================================================================== ; $C5D8-$C607: Initialize Playfield Area ; ; Local: ; Sets the PPU destination to $20C0 and fills 24 complete 32-tile nametable ; rows. ; ; Each row consists of: ; 2 x $2D ; 28 x $20 ; 2 x $2D ; ; The inner count at $01 is reinitialized to $1C at the beginning of every ; row. $00 begins at $17 and counts rows down through zero. ; ; Global: ; Called during State04Initialize with rendering disabled. Builds the large ; rectangular background area used by state $04. ; ; Notes: ; The BPL at $C605 intentionally returns to $C5E6, not directly to the first ; tile write. This reloads $01 with $1C for every row and must be preserved ; for byte identity. ;============================================================================== InitializePlayfieldArea: lda #$20 sta PPUADDR lda #$C0 sta PPUADDR lda #$17 sta workPtr0 @NextRow: lda #$1C sta workPtr0+1 lda #$2D sta PPUDATA sta PPUDATA lda #$20 @ClearRowInterior: sta PPUDATA dec workPtr0+1 bne @ClearRowInterior lda #$2D sta PPUDATA sta PPUDATA dec workPtr0 bpl @NextRow rts ;============================================================================== ; $C608-$C666: Initialize State $04 PPU Data ; ; Local: ; Performs three groups of PPU initialization: ; ; 1. Writes $24 (36) bytes from $C76E to PPUDATA beginning at PPU $23C8. ; ; 2. Processes a sequence of variable-length PPU write records beginning ; at $C69B. Each record contains a two-byte PPU destination followed by ; data bytes terminated by $FF. ; ; 3. Writes a full $20-byte palette table from CharacterIntroPalette to PPU palette RAM ; beginning at $3F00. ; ; After the palette transfer, resets PPUADDR away from palette space and ; returns. ; ; Global: ; Called by State04Initialize while rendering is disabled. Together with ; InitializePlayfieldArea, this routine constructs the PPU state needed by ; the state-$04 display sequence. ; ; Notes: ; Unlike InitializeNametableData, the PPU-record loop at $C61D does not ; contain an explicit record count. The encoded data and Y wrap behavior ; determine when processing terminates. We should decode $C69B before ; assigning a more precise description to that stream. ; ; CharacterIntroPalette begins the 32-byte palette table. ; $C76E is the source of the 36-byte attribute-data transfer. ;============================================================================== InitializeState04PpuData: ; Set PPU destination to $23C8. lda #$23 sta PPUADDR lda #$C8 sta PPUADDR ; Copy $24 bytes of attribute data. ldy #$00 @CopyAttributeData: lda $C76E,y sta PPUDATA iny cpy #$24 bne @CopyAttributeData ; Process encoded PPU write data from $C69B. ldy #$00 @NextPpuRecord: lda CharacterNicknameHeaderData,y sta PPUADDR iny lda CharacterNicknameHeaderData,y sta PPUADDR iny @CopyPpuRecord: lda CharacterNicknameHeaderData,y cmp #$FF beq @EndPpuRecord sta PPUDATA iny bne @CopyPpuRecord @EndPpuRecord: lda PPUSTATUS ; Upload the complete 32-byte palette directly. lda #$3F sta PPUADDR lda #$00 sta PPUADDR ldy #$00 @CopyPalette: lda CharacterIntroPalette,y sta PPUDATA iny cpy #$20 bne @CopyPalette ; Reset the PPU address following palette access. lda #$3F sta PPUADDR lda #$00 sta PPUADDR sta PPUADDR sta PPUADDR rts ;============================================================================== ; $C667-$C686: State $04 Palette Data ; ; Local: ; Complete 32-byte palette uploaded directly to PPU $3F00-$3F1F by ; InitializeState04PpuData. ; ; Global: ; Defines the background and sprite colors used by the state-$04 display. ; ; Notes: ; First 16 bytes are the four background palettes. ; Second 16 bytes are the four sprite palettes. ;============================================================================== CharacterIntroPalette: ; Background palettes .byte $0F,$20,$0F,$06 .byte $0F,$06,$0F,$33 .byte $0F,$33,$0F,$27 .byte $0F,$17,$0F,$21 ; Sprite palettes .byte $0F,$27,$20,$06 .byte $0F,$11,$20,$33 .byte $0F,$20,$20,$21 .byte $0F,$09,$20,$17 ;============================================================================== ; $C687-$C69A: State $04 Display Data Pointer Table ; ; Local: ; Contains ten 16-bit pointers to display/update data streams. The helper at ; $C5A6 uses $87 directly as a byte offset into this table: ; ; ldy $87 ; lda CharacterIntroDisplayDataTable,y ; sta $00 ; lda CharacterIntroDisplayDataTable+1,y ; sta $01 ; ; The valid table offsets are therefore: ; $00,$02,$04,$06,$08,$0A,$0C,$0E,$10,$12 ; ; Global: ; Selects the display data used as the state-$04 secondary state machine ; progresses. State04Initialize uses offset $00, while the successive timed ; substates use offsets $02 through $12. ; ; Notes: ; Each target begins a $FF-terminated display/update stream copied into RAM ; at $024B by LoadState04DisplayData. ; ; Several streams contain an additional control byte following $FF. The ; helper uses that byte to decide whether to perform its secondary copy. ;============================================================================== CharacterIntroDisplayDataTable: .word CharacterNicknameHeaderData ; $87 = $00 Character; NICKNAME .word ShadowNameData ; $87 = $02 : SHADOW .word BlinkyNameData ; $87 = $04 _BLINKY_ .word SpeedyNameData ; $87 = $06 : SPEEDY .word PinkyNameData ; $87 = $08 _PINKY_ .word CharacterIntroGraphicData0 ; $87 = $0A .word CharacterIntroGraphicData1 ; $87 = $0C .word PokeyNameData ; $87 = $0E .word ClydeNameData ; $87 = $10 .word ScoringLegendData ; $87 = $12 ;============================================================================== ; $C69B-$C72D: State $04 Display Update Streams ; ; Local: ; Contains ten display/update streams selected through ; CharacterIntroDisplayDataTable according to the state-$04 substate offset $87. ; ; LoadState04DisplayData copies the selected stream into RAM beginning at ; $024B until it encounters $FF. The $FF byte itself is also copied. ; ; For several streams, the byte immediately following $FF is $00. The ; loader examines this byte and uses it to trigger its optional secondary ; data copy. ; ; Global: ; Supplies the sequence of text and display changes shown during the timed ; state-$04 sequence. The data reveals that this portion of the attract/ ; information display introduces game characters and later installs ; scoring/instruction-related display data. ; ; Notes: ; Many tile values correspond directly to ASCII-like character tiles, but ; special tile values are retained numerically. ; ; $00 bytes appearing after $FF belong to the control mechanism used by ; LoadState04DisplayData. They are not part of the $FF-terminated portion ; copied to $024B. ; ; The final stream at $C707 is structurally different: it contains multiple ; apparent PPU/update command groups before the final $FF. ;============================================================================== ;------------------------------------------------------------------------------ ; $C69B-$C6B2 ; State04 substate offset: $00 ; ; Destination prefix: $20E6 ; ; Tile data corresponds approximately to: ; "CHARACTER ; NICKNAME" ; ; No explicit $00 control byte follows this stream before the next record. ;------------------------------------------------------------------------------ CharacterNicknameHeaderData: .byte $20,$E6 .byte $43,$48,$41,$52,$41,$43,$54,$45,$52 .byte $20,$3B,$20,$20 .byte $4E,$49,$43,$4B,$4E,$41,$4D,$45 .byte $FF ;------------------------------------------------------------------------------ ; $C6B3-$C6BD ; State04 substate offset: $02 ; ; Destination prefix: $2148 ; ; Text-like tile data: ; ":SHADOW" ; ; $00 after $FF selects the loader's secondary-copy path. ;------------------------------------------------------------------------------ ShadowNameData: .byte $21,$48 .byte $3A,$53,$48,$41,$44,$4F,$57 .byte $FF .byte $00 ;------------------------------------------------------------------------------ ; $C6BE-$C6C8 ; State04 substate offset: $04 ; ; Destination prefix: $2153 ; ; Text-like tile data: ; "_BLINKY_" ; ; No trailing $00 before the next stream. ;------------------------------------------------------------------------------ BlinkyNameData: .byte $21,$53 .byte $5F,$42,$4C,$49,$4E,$4B,$59,$5F .byte $FF ;------------------------------------------------------------------------------ ; $C6C9-$C6D3 ; State04 substate offset: $06 ; ; Destination prefix: $21A8 ; ; Text-like tile data: ; ":SPEEDY" ; ; $00 after $FF selects the loader's secondary-copy path. ;------------------------------------------------------------------------------ SpeedyNameData: .byte $21,$A8 .byte $3A,$53,$50,$45,$45,$44,$59 .byte $FF .byte $00 ;------------------------------------------------------------------------------ ; $C6D4-$C6DD ; State04 substate offset: $08 ; ; Destination prefix: $21B3 ; ; Text-like tile data: ; "_PINKY_" ;------------------------------------------------------------------------------ PinkyNameData: .byte $21,$B3 .byte $5F,$50,$49,$4E,$4B,$59,$5F .byte $FF ;------------------------------------------------------------------------------ ; $C6DE-$C6E9 ; State04 substate offset: $0A ; ; Destination prefix: $2208 ; ; Contains graphical/special tile values rather than plain character codes. ; $00 after $FF selects the loader's secondary-copy path. ;------------------------------------------------------------------------------ CharacterIntroGraphicData0: .byte $22,$08 .byte $B6,$C1,$C3,$B7,$B4,$C2,$B3,$C4 .byte $FF .byte $00 ;------------------------------------------------------------------------------ ; $C6EA-$C6F2 ; State04 substate offset: $0C ; ; Destination prefix: $2213 ; ; Contains another set of special graphical tile values. ;------------------------------------------------------------------------------ CharacterIntroGraphicData1: .byte $22,$13 .byte $C8,$B5,$C5,$C0,$C6,$C8 .byte $FF ;------------------------------------------------------------------------------ ; $C6F3-$C6FC ; State04 substate offset: $0E ; ; Destination prefix: $2268 ; ; Text-like tile data: ; ":POKEY" ; ; $00 after $FF selects the loader's secondary-copy path. ;------------------------------------------------------------------------------ PokeyNameData: .byte $22,$68 .byte $3A,$50,$4F,$4B,$45,$59 .byte $FF .byte $00 ;------------------------------------------------------------------------------ ; $C6FD-$C706 ; State04 substate offset: $10 ; ; Destination prefix: $2273 ; ; Text-like tile data: ; "_CLYDE_" ;------------------------------------------------------------------------------ ClydeNameData: .byte $22,$73 .byte $5F,$43,$4C,$59,$44,$45,$5F .byte $FF ;------------------------------------------------------------------------------ ; $C707-$C72D ; State04 substate offset: $12 ; ; Final composite display stream. Unlike the earlier simple records, this ; contains several apparent update groups separated by $00 bytes before the ; final $FF. ; ; Visible character-like portions include: ; "10 PTS" ; "50 PTS" ; ; The special tile values $CE,$CF and $23-$2B are preserved numerically. ;------------------------------------------------------------------------------ ScoringLegendData: .byte $22,$AD .byte $03,$20,$31,$30,$20,$50,$54,$53 .byte $00 .byte $22,$ED .byte $01,$20,$35,$30,$20,$50,$54,$53 .byte $00 .byte $23,$34 .byte $CE,$CF .byte $00 .byte $23,$4D .byte $23,$24,$25,$26,$27,$28,$29,$2A,$2B .byte $FF ;============================================================================== ; $C72E-$C76D: State $04 Secondary Object/Sprite Data ; ; Local: ; Contains 64 bytes of secondary display data used by ; LoadState04DisplayData. ; ; When a state-$04 display stream requests the secondary-copy path, the ; loader calculates: ; ; Y = ($87 - $02) * 4 ; ; It then copies $10 (16) bytes from: ; ; CharacterIntroSpriteData,Y ; ; to: ; ; $0760,Y ; ; Because state-$04 substates advance by two, Y advances by eight bytes: ; ; $87 = $02 -> Y = $00 ; $87 = $04 -> Y = $08 ; $87 = $06 -> Y = $10 ; $87 = $08 -> Y = $18 ; ... ; ; The 16-byte transfers therefore intentionally overlap by eight bytes. ; ; Global: ; Supplies progressively updated object/sprite-related state during the ; state-$04 timed display sequence. The destination lies in RAM page $07, ; which is also used as an OAM DMA source elsewhere in the program. ; ; The repeated four-byte patterns strongly suggest sprite/OAM-style records, ; but exact field semantics should be confirmed against the code that ; consumes $0760 and the active OAM page before assigning field names. ; ; Notes: ; The table is safest to represent as 16 four-byte records during this pass. ; This preserves its natural repeated structure while allowing the loader's ; overlapping 16-byte windows to remain obvious. ;============================================================================== CharacterIntroSpriteData: ; $C72E - record $00 .byte $48,$1C,$40,$26 ; $C732 - record $01 .byte $48,$1B,$40,$2E ; $C736 - record $02 .byte $50,$1F,$40,$26 ; $C73A - record $03 .byte $50,$1D,$40,$2E ; $C73E - record $04 .byte $60,$1C,$41,$26 ; $C742 - record $05 .byte $60,$1B,$41,$2E ; $C746 - record $06 .byte $68,$1F,$41,$26 ; $C74A - record $07 .byte $68,$1D,$41,$2E ; $C74E - record $08 .byte $78,$1C,$42,$26 ; $C752 - record $09 .byte $78,$1B,$42,$2E ; $C756 - record $0A .byte $80,$1F,$42,$26 ; $C75A - record $0B .byte $80,$1D,$42,$2E ; $C75E - record $0C .byte $90,$1C,$43,$26 ; $C762 - record $0D .byte $90,$1B,$43,$2E ; $C766 - record $0E .byte $98,$1F,$43,$26 ; $C76A - record $0F .byte $98,$1D,$43,$2E ;============================================================================== ; $C76E-$C791: State $04 Nametable Attribute Data ; ; Local: ; Contains $24 (36) bytes copied directly to PPUDATA by ; InitializeState04PpuData. ; ; The destination begins at PPU $23C8, so the complete transfer covers: ; ; $23C8-$23EB ; ; Global: ; Defines background palette selection attributes used by the state-$04 ; display. The highly regular $00/$55/$AA/$FF pattern assigns repeating ; palette selections across large regions of the nametable. ; ; Notes: ; These are NES attribute-table bytes rather than tile indices. ; ; Each attribute byte contains four 2-bit palette selections. Values such ; as $00, $55, $AA and $FF therefore select the same palette throughout all ; four quadrants covered by that attribute byte: ; ; $00 -> palette 0 in all four quadrants ; $55 -> palette 1 in all four quadrants ; $AA -> palette 2 in all four quadrants ; $FF -> palette 3 in all four quadrants ; ; The final $22 mixes palette selections within its attribute regions. ;============================================================================== CharacterIntroAttributeData: .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $55,$55,$55,$55,$55,$55,$55,$55 .byte $AA,$AA,$AA,$AA,$AA,$AA,$AA,$AA .byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF .byte $AA,$AA,$AA,$22 ;============================================================================== ; $C792-$C7A0: State $04 Final-Phase Dispatcher ; ; Local: ; Uses $88 as a direct byte offset into a four-entry table of 16-bit ; addresses beginning at $C7A1. ; ; The selected address is stored in the temporary pointer $10/$11 and ; execution is transferred to it using an indirect JMP. ; ; Global: ; Implements state-$04 substate offset $16. Rather than performing the work ; directly, this substate introduces another secondary state machine. ; ; At this stage: ; ; $3F = $04 main state ; $87 = $16 state-$04 substate ; $88 final-phase dispatch offset ; ; This provides another level of progression within the final state-$04 ; phase. ; ; Notes: ; $88 is used directly as a byte offset, so normal values are expected to ; advance by two: ; ; $00, $02, $04, $06 ; ; The individual target handlers will be named as they are decoded. ;============================================================================== State04FinalPhase: ldy $88 lda State04FinalPhaseTable,y sta dispatchVector lda State04FinalPhaseTable+1,y sta dispatchVector+1 jmp ($0010) ;============================================================================== ; $C7A1-$C7A6: State $04 Final-Phase Dispatch Table ; ; Local: ; Three 16-bit handler addresses indexed by $88. ; ; Global: ; Selects the processing stage used during State04FinalPhase. ; ; Notes: ; Valid byte offsets established by the table are $00, $02 and $04. ; $C7A7 is executable code and is not part of the pointer table. ;============================================================================== State04FinalPhaseTable: .word State04FinalPhaseInitialize ; $88 = $00 .word State04FinalPhaseActive ; $88 = $02 .word State04FinalPhaseComplete ; $88 = $04 ;============================================================================== ; $C7A7-$C7F1: State $04 Final-Phase Initialization ; ; Local: ; Initializes the first stage of State04FinalPhase. ; ; Copies a $FF-terminated display/update stream from State04FinalDisplayData ; into RAM at $024B, including the terminator. ; ; Initializes several state variables and object/display values, clears ; $001E-$0031 and $0278-$028B, calls the helper at $DAF1, then advances ; $88 by two to select final-phase offset $02. ; ; Global: ; First handler selected by State04FinalPhase when $88 = $00. It prepares ; the RAM and display state required by the next final-phase handler at ; $C7F2. ; ; Notes: ; $1A is initialized to $FF. ; $1B, $89 and $8A are initialized to $00. ; ; $0274 = $F4 ; $0276 = $A8 ; $028C = $01 ; $0292 = $20 ; ; The exact semantics of these locations will be refined after tracing ; their consumers. ; ; State04FinalDisplayData is a display/update stream in the same general RAM ; command area used elsewhere through $024B. ;============================================================================== State04FinalPhaseInitialize: ldy #$00 @CopyDisplayData: lda State04FinalDisplayData,y sta $024B,y iny cmp #$FF bne @CopyDisplayData lda #$FF sta pacmanMotion lda #$00 sta $1B sta $89 sta $8A lda #$F4 sta $0274 lda #$A8 sta $0276 lda #$01 sta $028C lda #$20 sta $0292 lda #$00 tay @ClearLowRamBlock: sta $001E,y iny cpy #$14 bne @ClearLowRamBlock tay @ClearObjectRamBlock: sta $0278,y iny cpy #$14 bne @ClearObjectRamBlock jsr $DAF1 inc $88 inc $88 jmp MainDispatchLoop ;============================================================================== ; $C7F2-$C856: State $04 Final-Phase Active Handler ; ; Local: ; Calls two per-frame helpers at UpdateState04Objects and ; AnimateState04DisplayPattern, then examines $0274. ; ; When $0274 equals one of: ; ; $E0 ; $D1 ; $C2 ; $B3 ; ; the routine calls InitializeState04ObjectSlot. ; ; Processing otherwise continues until $0274 reaches $40. While it has not ; reached $40, control returns to MainDispatchLoop. ; ; When $0274 reaches $40, the routine: ; ; - Initializes four 4-byte structures beginning at $001E. ; - Sets $1A = $01 and $1B = $50. ; - Sets $0293-$0296 to $01. ; - Clears $89. ; - Writes a short display/update sequence at $024B/$024D. ; - Advances $88 by two, selecting final-phase offset $04. ; ; Global: ; Active middle stage of State04FinalPhase. The handler appears to drive ; an animated sequence by calling UpdateState04Objects/AnimateState04DisplayPattern ; each frame and reacting when $0274 reaches several specific values. ; ; The milestone values $E0,$D1,$C2,$B3 invoke InitializeState04ObjectSlot. The exact meaning of ; those milestones should be assigned after the movement/update helpers are ; decoded. ; ; Notes: ; $0274 was initialized to $F4 by State04FinalPhaseInitialize, so the ; descending milestone tests strongly suggest $0274 is modified by one of ; the per-frame helper routines. ; ; The four low-RAM structures are $001E/$0022/$0026/$002A. Each receives ; $00 in its first byte and $C0 in its second byte. The remaining two bytes ; are not changed by this loop. ;============================================================================== State04FinalPhaseActive: jsr UpdateState04Objects jsr AnimateState04DisplayPattern lda $0274 cmp #$E0 beq @RunMilestoneUpdate cmp #$D1 beq @RunMilestoneUpdate cmp #$C2 beq @RunMilestoneUpdate cmp #$B3 bne @CheckForCompletion @RunMilestoneUpdate: jsr InitializeState04ObjectSlot @CheckForCompletion: lda $0274 cmp #$40 beq @CompletePhase jmp MainDispatchLoop @CompletePhase: ldy #$00 @InitializeStructures: lda #$00 sta ghost1Motion,y lda #$C0 sta ghost1Motion+1,y iny iny iny iny cpy #$10 bne @InitializeStructures lda #$01 sta pacmanMotion lda #$50 sta pacmanMotion+1 lda #$01 sta $0293 sta $0294 sta $0295 sta $0296 lda #$00 sta $89 lda #$22 sta $024B lda #$20 sta $024D inc $88 inc $88 jmp MainDispatchLoop ;============================================================================== ; $C857-$C8A7: State $04 Final-Phase Completion Handler ; ; Local: ; Final handler for final-phase offset $88 = $04. ; ; If $8A is nonzero, calls RotateState04ObjectSlots and decrements the countdown. When the ; countdown reaches zero, scans status entries beginning at $028D until an ; entry with one or more upper-three status bits set is found. ; ; The matching entry and associated state are cleared. If $89 has reached ; $04, the routine sets $47 to $01 and transfers control to GameModeEntry. ; ; When $8A is already zero on entry, calls $C8DC and returns to the common ; main dispatch loop. ; ; Global: ; Final processing stage of the nested state-$04 sequence: ; ; $3F = $04 ; $87 = $16 ; $88 = $04 ; ; The eventual transfer to GameModeEntry appears to leave this attract/display ; sequence and enter another major portion of the program. ; ; Notes: ; $8A acts as a countdown here. ; $89 is tested against $04 for completion. ; ; The scan at $028D looks for the first entry satisfying: ; ; ($028D,Y & $E0) != 0 ; ; Exact object/state semantics remain for a later pass. ;============================================================================== State04FinalPhaseComplete: lda $8A beq @RunRegularUpdate jsr RotateState04ObjectSlots dec $8A beq @CountdownComplete jmp MainDispatchLoop @CountdownComplete: ldy #$00 @FindEntry: lda $028D,y and #$E0 bne @EntryFound iny bne @FindEntry @EntryFound: lda #$00 sta $028D,y lda #$00 sta $0293,y tya asl asl tay lda #$00 sta $0278,y sta $027A,y sta ghost1Motion,y sta ghost1Motion+1,y lda #$A8 sta $0276 lda $89 cmp #$04 beq @FinishSequence jmp MainDispatchLoop @FinishSequence: lda #$01 sta activePlayerFlag jmp GameModeEntry @RunRegularUpdate: jsr UpdateState04Objects jmp MainDispatchLoop ;============================================================================== ; $C8A8-$C8DB: Allocate/Initialize State $04 Object Slot ; ; Local: ; Searches four 4-byte structures beginning at $0278 for the first slot ; whose first byte is zero. ; ; During the search, $00 tracks the logical slot number while Y advances ; by four bytes per structure. ; ; Once a free slot is found, initializes that structure: ; ; +0 = $F4 ; +2 = $A8 ; ; It also initializes corresponding four-byte-strided zero-page state: ; ; $001E+Y = $FF ; $001F+Y = $00 ; ; The logical slot number saved in $00 is then used to index two separate ; per-slot arrays: ; ; $0293+slot = current $89 value ; $028D+slot = $0C ; ; Finally increments $89 and returns. ; ; Global: ; Called by State04FinalPhaseActive whenever $0274 reaches the milestone ; values $E0, $D1, $C2, or $B3. ; ; This strongly suggests that the routine activates one additional object ; at each milestone during the state-$04 final sequence. Four slots are ; available, matching the four milestone values. ; ; Notes: ; $0278 appears to be the first field of a four-byte structure array. ; $027A is the third field of the same structure. ; ; $001E/$001F are parallel four-byte-strided state fields. ; ; $028D and $0293 are indexed by logical slot number rather than the ; four-byte structure offset. ; ; Exact object semantics remain deliberately unnamed until UpdateState04Objects ; and RotateState04ObjectSlots reveal how these structures are updated. ;============================================================================== InitializeState04ObjectSlot: ldy #$00 sty workPtr0 @FindFreeSlot: lda $0278,y beq @InitializeSlot inc workPtr0 iny iny iny iny bne @FindFreeSlot @InitializeSlot: lda #$F4 sta $0278,y lda #$A8 sta $027A,y lda #$FF sta ghost1Motion,y lda #$00 sta $001F,y ldy workPtr0 lda $89 sta $0293,y lda #$0C sta $028D,y inc $89 rts ;============================================================================== ; $C8DC-$C8EA: State $04 Per-Frame Object Update Coordinator ; ; Local: ; Runs four update stages in sequence: ; ; UpdateTransitionObjectPositions ; RotateState04ObjectSlots ; UpdateState04ObjectStatus ; CheckState04ObjectTrigger ; ; After those return, tail-jumps to $DAF1 rather than returning directly. ; ; Global: ; Called once per frame during portions of the state-$04 final sequence. ; This routine coordinates the lower-level movement/object/display work ; associated with that sequence. ; ; It is called from both State04FinalPhaseActive and ; State04FinalPhaseComplete. ; ; Notes: ; The exact responsibilities of $EA19, $C92E, $C9FA, CheckState04ObjectTrigger ; and $DAF1 will be refined as those routines are decoded. ; ; The JMP to $DAF1 is a tail call: $DAF1 ultimately returns directly to ; the original caller of this routine. ;============================================================================== UpdateState04Objects: jsr UpdateTransitionObjectPositions jsr RotateState04ObjectSlots jsr UpdateState04ObjectStatus jsr CheckState04ObjectTrigger jmp BuildEntitySpriteBuffer ;============================================================================== ; $C8EB-$C92D: Check State $04 Object Trigger ; ; Local: ; Returns immediately when $1A is negative. ; ; Otherwise computes: ; ; threshold = $0274 + $08 ; ; and scans four 4-byte structures beginning at $0278. ; ; For each active structure, compares its first byte against the threshold. ; When a qualifying structure is found, uses $89 as an index into a ROM ; table at $C9EE, installs the resulting value into a per-slot status array, ; clears a related slot value, initializes $8A to $40, and clears $028C and ; $0276. ; ; Global: ; Part of UpdateState04Objects. Appears to detect when one of the active ; state-$04 objects crosses a position threshold relative to the primary ; value at $0274, then initiates a $40-frame/countdown event through $8A. ; ; Notes: ; $00 holds the comparison threshold ($0274 + $08). ; $01 tracks the logical object slot while Y steps through four-byte ; structures. ; ; $89 is incremented as part of the event setup. ; ; $C9EE is a small ROM lookup table that remains to be decoded. ;============================================================================== CheckState04ObjectTrigger: lda pacmanMotion bpl @Continue rts @Continue: lda $0274 clc adc #$08 sta workPtr0 ldy #$00 sty workPtr0+1 @ScanObjects: lda $0278,y beq @NextObject cmp workPtr0 bcc @TriggerEvent @NextObject: inc workPtr0+1 iny iny iny iny cpy #$10 bne @ScanObjects rts @TriggerEvent: ldy $89 inc $89 lda #$40 sta $8A lda $C9EE,y ldy workPtr0+1 sta $028D,y lda #$00 sta $0293,y lda #$00 sta $028C sta $0276 rts ;============================================================================== ; $C92E-$C9B7: Rotate State $04 Object Slots ; ; Local: ; Rotates four related object/state entries across several parallel arrays. ; ; The routine operates on: ; ; $001E/$001F four-byte-strided zero-page structures ; $0278-$0287 four 4-byte object structures ; $028D-$0290 four object/status bytes ; $0293-$0296 four object/index bytes ; ; For each array, the first entry is saved temporarily, entries 1-3 are ; shifted toward entry 0, and the saved entry 0 is moved to entry 3. ; ; Global: ; Called by UpdateState04Objects every frame, and also directly from ; State04FinalPhaseComplete while its $8A countdown is active. ; ; This appears to cycle the four state-$04 object slots through a fixed ; ordering while keeping their parallel state/status arrays synchronized. ; ; Notes: ; $00-$03 are temporary storage. ; ; The zero-page structures use a four-byte stride but only the first two ; bytes of each structure are moved here. ; ; The $0278 array moves all four bytes of each structure. ; ; $028D-$0290 and $0293-$0296 are simple four-entry byte arrays. ;============================================================================== RotateState04ObjectSlots: ; ------------------------------------------------------------------------- ; Rotate the first two bytes of the four-byte-strided zero-page structures. ; ; Entries: ; 0 -> $001E/$001F ; 1 -> $0022/$0023 ; 2 -> $0026/$0027 ; 3 -> $002A/$002B ; ------------------------------------------------------------------------- lda ghost1Motion sta workPtr0 lda ghost1Motion+1 sta workPtr0+1 ldy #$00 @ShiftLowRamStructures: lda ghost2Motion,y sta ghost1Motion,y lda ghost2Motion+1,y sta ghost1Motion+1,y iny iny iny iny cpy #$0C bne @ShiftLowRamStructures lda workPtr0 sta ghost4Motion lda workPtr0+1 sta ghost4Motion+1 ; ------------------------------------------------------------------------- ; Rotate four complete 4-byte structures at $0278-$0287. ; ------------------------------------------------------------------------- lda $0278 sta workPtr0 lda $0279 sta workPtr0+1 lda $027A sta workPtr1 lda $027B sta workPtr1+1 ldy #$00 @ShiftObjectStructures: lda $027C,y sta $0278,y iny cpy #$0C bne @ShiftObjectStructures lda workPtr0 sta $0284 lda workPtr0+1 sta $0285 lda workPtr1 sta $0286 lda workPtr1+1 sta $0287 ; ------------------------------------------------------------------------- ; Rotate the four status bytes at $028D-$0290. ; ------------------------------------------------------------------------- lda $028D sta workPtr0 ldy #$00 @ShiftStatusArray: lda $028E,y sta $028D,y iny cpy #$03 bne @ShiftStatusArray lda workPtr0 sta $0290 ; ------------------------------------------------------------------------- ; Rotate the four associated index/state bytes at $0293-$0296. ; ------------------------------------------------------------------------- lda $0293 sta workPtr0 ldy #$00 @ShiftIndexArray: lda $0294,y sta $0293,y iny cpy #$03 bne @ShiftIndexArray lda workPtr0 sta $0296 rts ;============================================================================== ; $C9B8-$C9D7: Animate State $04 Display Pattern ; ; Local: ; Uses the frame counter at $4B to periodically copy one of two four-byte ; patterns into RAM $024B-$024E. ; ; The routine runs only when the low three bits of $4B are zero: ; ; ($4B & $07) == 0 ; ; so the display data is updated once every eight frames. ; ; Bit 3 of $4B selects between two adjacent four-byte patterns beginning ; at $C9F2: ; ; bit 3 = 0 -> source $C9F2-$C9F5 ; bit 3 = 1 -> source $C9F6-$C9F9 ; ; Global: ; Called by State04FinalPhaseActive once per frame. Provides a simple ; two-frame animation/blink effect by updating the command/display buffer ; at $024B according to the NMI-driven frame counter $4B. ; ; Notes: ; $4B is incremented by NmiHandler once per frame. ; ; The source data at $C9F2 overlaps the tail of nearby ROM data. We should ; decode the surrounding $C9D8-$C9F9 region before assigning a more ; specific visual meaning to the two animation frames. ;============================================================================== AnimateState04DisplayPattern: lda frameCounter and #$07 beq @UpdatePattern rts @UpdatePattern: ldx #$00 ldy #$00 lda frameCounter and #$08 beq @CopyPattern ldy #$04 @CopyPattern: lda State04AnimationFrames,y sta $024B,x inx iny cpx #$04 bne @CopyPattern rts ;============================================================================== ; $C9D8-$C9ED: State $04 Final Display Data ;============================================================================== State04FinalDisplayData: .byte $22,$AD .byte $2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D .byte $00 .byte $22,$ED .byte $2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D .byte $FF ;------------------------------------------------------------------------------ ; $C9EE-$C9F1: Adjacent data, purpose not yet resolved. ;------------------------------------------------------------------------------ State04UnknownData: .byte $2D,$2F,$32,$34 ;============================================================================== ; $C9F2-$C9F9: State $04 Animation Frames ; ; Two four-byte animation/update frames. ; ; $4B bit 3 = 0 -> frame 0 ; $4B bit 3 = 1 -> frame 1 ;============================================================================== State04AnimationFrames: .byte $22,$C7,$20,$FF .byte $22,$C7,$01,$FF ;============================================================================== ; $C9FA-$CA3F: Update State $04 Object Status Animation ; ; Local: ; Selects an animation/status value from a ROM table at State04ObjectStatusTable and writes ; the value into active entries of $028D-$0290. ; ; The selected table range depends on $1A: ; ; $1A negative -> base offset $00 ; $1A nonnegative -> base offset $0A ; ; $B7 is incremented each call and its low three bits select an entry from ; the first lookup range. The resulting value is written to $028C. ; ; The routine then updates the four status entries $028D-$0290 only once ; every eight frames. Bit 3 of $4B selects between two animation phases. ; ; Global: ; Called by UpdateState04Objects during the active state-$04 final sequence. ; Provides periodic animation/status changes for the four object slots ; maintained in $028D-$0290. ; ; Notes: ; $B7 behaves as an additional animation counter here. ; ; $4B is the NMI-driven frame counter. ; ; $028C is updated on every call, while $028D-$0290 are changed only when ; ($4B & $07) == 0. ; ; The table beginning at State04ObjectStatusTable follows this routine immediately and can be ; decoded separately. ;============================================================================== UpdateState04ObjectStatus: ldx #$00 lda $1A bmi @UseTableBase ldx #$0A @UseTableBase: stx workPtr0 inc $B7 lda $B7 and #$07 clc adc workPtr0 tay lda State04ObjectStatusTable,y sta $028C ; Only update the four object entries once every eight frames. lda $4B and #$07 beq @UpdateObjectEntries rts @UpdateObjectEntries: lda frameCounter and #$08 beq @SelectTableValue lda #$01 @SelectTableValue: clc adc workPtr0 adc #$08 tay lda State04ObjectStatusTable,y sta workPtr0 ldy #$00 @UpdateStatusEntries: lda $028D,y beq @NextStatusEntry lda workPtr0 sta $028D,y @NextStatusEntry: iny cpy #$04 bne @UpdateStatusEntries rts ;============================================================================== ; $CA40-$CA53: State $04 Object Status/Animation Lookup ; ; Local: ; Twenty-byte lookup table used by UpdateState04ObjectStatus. ; ; The routine selects one of two 10-byte groups: ; ; $1A negative -> group at offset $00 ; $1A nonnegative -> group at offset $0A ; ; Within each group: ; ; bytes 0-7 are selected by ($B7 & $07) ; bytes 8-9 are selected by the slower frame-animation state ; ; Global: ; Supplies animation/status values for the four objects used during the ; final state-$04 sequence. ; ; Notes: ; Exact meanings of the individual values remain intentionally unnamed ; until the consumers of $028C and $028D-$0290 are better understood. ;============================================================================== State04ObjectStatusTable: ; Group 0, selected while $1A is negative. .byte $04,$04,$04,$05,$05,$04,$01,$01 .byte $0C,$0D ; Group 1, selected while $1A is nonnegative. .byte $08,$08,$08,$09,$09,$08,$01,$01 .byte $1E,$1F ;============================================================================== ; $CA54-$CAD6: Main Game/Mode Initialization and Dispatch Loop ; ; Local: ; Reconfigures the PPU, waits for VBlank, disables rendering, clears several ; state variables, and invokes three initialization helpers. ; ; Depending on $48, initializes or preserves parts of two parallel RAM ; regions beginning at $67 and $77. It then initializes additional state, ; re-enables NMI through PPUCTRL=$88, and synchronizes with NMI via $40. ; ; After initialization, enters a persistent foreground loop that: ; ; - optionally watches controller bits $0C and returns to MainEntry, ; - services the countdown at $4C, ; - uses $3F as a byte offset into an eight-entry dispatch table at $CAD7, ; - indirect-jumps through $10/$11 to the selected handler. ; ; Global: ; Multiple earlier state-machine paths converge here, making this a major ; transition point out of the initial/state-$04 sequence and into another ; top-level game-mode state machine. ; ; The routine contains both one-time initialization and the common dispatch ; loop used by the handlers selected from $CAD7. ; ; Notes: ; $3F is reused as a byte offset into this new dispatch table. ; ; $40 continues to provide foreground/NMI synchronization. ; ; $4C acts as a countdown/delay gate in the dispatch loop. ; ; The exact semantics of $48, $46, $67-$86 and the new dispatch states will ; be refined as their handlers are decoded. ;============================================================================== GameModeEntry: lda #$08 sta PPUCTRL sta ppuCtrlShadow @WaitForVBlank: lda PPUSTATUS bpl @WaitForVBlank lda #$00 sta PPUMASK sta gameState sta cocktailModeFlag sta $69 sta $4A jsr ClearGameplayNametables jsr DrawGameplayHudLabels jsr DrawGameplayScoreFields lda gameplaySuspended beq @ClearPlayerState lda #$01 sta roundNumber bne @InitializeCommonState @ClearPlayerState: lda #$00 tay @ClearPlayerStateLoop: sta roundNumber,y iny cpy #$20 bne @ClearPlayerStateLoop lda #$03 sta roundNumber sta $77 sta $0600 sta $0601 @InitializeCommonState: lda #$FF sta roundDisplayValue sta $78 sta $0245 lda #$88 sta ppuCtrlShadow sta PPUCTRL GameModeFrameSync: @WaitForNextFrame: lda #$01 sta $40 @WaitForNmi: lda $40 bne @WaitForNmi @DispatchLoop: lda gameplaySuspended beq @ProcessDelay lda currentInput and #$0C beq @ProcessDelay lda #$02 ; TODO: what is state $02? sta gameState jmp MainEntry @ProcessDelay: lda stateTimer beq @DispatchState dec stateTimer bne @WaitForNextFrame @DispatchState: ldy gameState lda GameModeStateTable,y sta dispatchVector lda GameModeStateTable+1,y sta dispatchVector+1 jmp ($0010) ;============================================================================== ; $CAD7-$CAE6: Game Mode State Dispatch Table ; ; Local: ; Eight 16-bit handler addresses selected by GameModeEntry using $3F as a ; direct byte offset. ; ; Global: ; Defines the next major foreground state machine entered through GameModeEntry. ; ; Notes: ; Since $3F indexes raw bytes rather than words, expected state offsets are: ; ; $00,$02,$04,$06,$08,$0A,$0C,$0E ; ; Handler names remain unresolved until each target is decoded. ;============================================================================== GameModeStateTable: .word GameModeState00 ; $3F = $00 .word GameModeState02 ; $3F = $02 .word GameModeState04 ; $3F = $04 .word GameModeState06 ; $3F = $06 .word GameModeState08 ; $3F = $08 .word GameModeState0A ; $3F = $0A .word GameModeState0C ; $3F = $0C .word InitializeGameplayTransitionState ; $3F = $0E ;============================================================================== ; $CAE7-$CAE8: Game-Mode Auxiliary Pointer ; ; Local: ; Two-byte little-endian ROM value containing $E7BF. ; ; Global: ; Sits immediately between GameModeStateTable and the $3F=$04 handler. ; Its consumer has not yet been established during this pass. ; ; Notes: ; Keep this as data rather than attempting to decode it as 6502 code. ;============================================================================== GameModeAuxPointer: .word $E7BF ;============================================================================== ; $CAE9-$CB5A: Game-Mode State $04 / Pause and Per-Frame Processing ; ; Local: ; First checks $060F. If nonzero, immediately returns to the common ; NMI/frame synchronization point at $CAA7. ; ; Otherwise performs edge detection on controller bit $08 using $49 as ; the stored previous state. ; ; On a new active $08 input: ; - increments $4A, ; - installs a short update command at $024B onward, ; - selects between two command variants according to $47 & $46, ; - uses bit 0 of $4A to alternate the displayed data, ; - optionally clears $060F. ; ; When no new pause/input edge is being handled, and bit 0 of $4A is clear, ; calls a sequence of gameplay/update helpers before returning to the common ; frame synchronization point. ; ; Global: ; Handler selected when GameModeStateTable offset $3F = $04. ; ; The adjacent data beginning at $CB5B contains the tile values for the word ; "PAUSE", strongly indicating that this handler manages pause-state input ; and display updates while also running the normal per-frame gameplay ; helper chain when not paused. ; ; Notes: ; $49 stores the previous masked state of controller bit $08. ; $4A is incremented on each newly detected active edge and bit 0 is used ; here as a pause/run toggle. ; ; $060F is consulted as another gate associated with this handler. ; ; Helper routines remain address-named during this pass. ;============================================================================== GameModeState04: lda $060F beq @CheckPauseInput jmp GameModeFrameSync @CheckPauseInput: lda currentInput and #$08 cmp $49 beq @ProcessCurrentMode sta $49 lda $49 beq @ProcessCurrentMode inc $4A ldx #$22 lda activePlayerFlag and cocktailModeFlag beq @SetCommandAddress ldx #$2A @SetCommandAddress: stx $024B lda #$37 sta $024C ldx #$06 ldy #$00 lda $4A and #$01 beq @CopyPauseData ldx #$00 sta $060F @CopyPauseData: lda PauseDisplayData,x sta $024D,y inx iny cpy #$06 bne @CopyPauseData jmp GameModeFrameSync @ProcessCurrentMode: lda $4A and #$01 beq @RunFrameUpdates jmp GameModeFrameSync @RunFrameUpdates: jsr UpdateGameplayTimers jsr UpdateGameplayTileState jsr UpdatePlayerMovement jsr UpdatePlayerDirectionAnimation jsr UpdateGameplayEntities jsr UpdateEntitySpriteStates jsr UpdateEntityStatusFlags jsr UpdatePeriodicFourEntryState jsr CheckGameplayProximity jsr CheckEntityCollisions jmp GameModeFrameSync ;============================================================================== ; $CB5B-$CB66: Pause Display Data ; ; Local: ; Two six-byte display strings selected by GameModeState04. ; ; Offset $00: ; "PAUSE",$FF ; ; Offset $06: ; five $2D tiles followed by $FF, used to replace/clear the pause text. ; ; Global: ; Provides the visible pause-state update controlled by $4A bit 0. ;============================================================================== PauseDisplayData: .byte $50,$41,$55,$53,$45,$FF .byte $2D,$2D,$2D,$2D,$2D,$FF ;============================================================================== ; $CB67-$CC9B: Game-Mode State $02 Handler ; ; Local: ; Implements a multi-phase initialization/progression state. ; ; When $87 is zero, constructs structured data in RAM page $07 using several ; ROM lookup tables. The construction is influenced by $48, $47, $46, ; $68 and $69. ; ; Once $87 is nonzero, it advances once per frame. At $87=$78, initializes ; a new group of RAM state and prepares display/update data in $024B-$0254. ; ; At $87=$C0, checks $0600/$0601. If both are zero, clears a large area of ; page $07 and advances the main game-mode state to $04. ; ; Global: ; Handler selected by GameModeStateTable when $3F=$02. This appears to be ; a substantial setup/transition state between game-mode phases, preparing ; object/OAM-related RAM, display command data, and other game state before ; handing control to GameModeState04. ; ; Notes: ; $87 serves as the phase timer/progression value. ; ; $00/$01 initially form a pointer to $0760 during the page-$07 build. ; The same locations are reused later as temporary display-address values. ; ; ROM tables $CC9C-$CCD8 immediately follow this routine and should be ; decoded next. ;============================================================================== GameModeState02: lda $87 beq @BuildInitialData jmp @AdvancePhase @BuildInitialData: lda #$60 sta workPtr0 lda #$07 sta workPtr0+1 lda $48 bne @AdvancePhase lda #$02 sta workValue0 ldy #$00 sty workValue1 sty workValue2 sty workValue3 @BuildOuterLoop: ldx workValue2 lda State02PositionTable,x sta workPtr1 lda State02PositionTable + 1,x sta workPtr1+1 ldx workValue3 lda State02EntryCounts,x sta workValue4 @BuildInnerLoop: lda workPtr1+1 sta ($00),y ldx workValue1 lda State02ValueTableA,x iny sta (workPtr0),y lda State02ValueTableB,x iny sta (workPtr0),y lda workPtr1 iny sta ($00),y lda workPtr1 clc adc #$08 sta workPtr1 iny inc workValue1 dec workValue4 bne @BuildInnerLoop inc workValue2 inc workValue2 inc workValue3 lda $69 bne @ContinueBuild lda roundDisplayValue bne @AdvancePhase @ContinueBuild: dec workValue0 beq @CheckPlayerMode bpl @BuildOuterLoop bmi @AdvancePhase @CheckPlayerMode: lda activePlayerFlag beq @AdvancePhase lda cocktailModeFlag beq @BuildOuterLoop inc workValue1 inc workValue1 inc workValue1 bne @BuildOuterLoop ;------------------------------------------------------------------------------ ; $CBE4 ; Frame-based progression portion of the same state. ;------------------------------------------------------------------------------ @AdvancePhase: lda $87 cmp #$C0 bne @IncrementPhase jmp $CC7C @IncrementPhase: inc $87 lda $87 cmp #$78 beq @InitializeMidPhase jmp $CC99 @InitializeMidPhase: lda #$A8 sta $1C lda #$60 sta pacmanMotion sta ghost1Motion sta ghost2Motion lda #$58 sta ghost3Motion sta $20 lda #$68 sta ghost4Motion lda #$70 sta $24 sta $28 sta $2C lda #$00 tay @ClearOamRegion: sta $0778,y iny cpy #$24 bne @ClearOamRegion lda activePlayerFlag and cocktailModeFlag beq @NoPlayerOffset lda #$08 @NoPlayerOffset: sta $00 lda $67 sec sbc #$01 asl tay lda State02DisplayAddressTable,y clc adc workPtr0 sta workPtr0 lda State02DisplayAddressTable+1,y sta workPtr0+1 lda workPtr0 sta $024B lda workPtr0+1 sta $024C lda #$2D sta $024D sta $024E lda #$00 sta $024F lda $00 sta $0250 lda $01 clc adc #$20 sta $0251 lda #$2D sta $0252 sta $0253 lda #$FF sta $0254 jsr UpdatePlayerDirectionAnimation jsr UpdateEntitySpriteStates jsr $DA40 jmp $CC99 @CheckCompletion: lda $0600 ora $0601 bne $CC99 lda $48 bne @AdvanceMainState lda #$00 sta $69 tay @ClearPage07Region: sta $0760,y iny cpy #$A0 bne @ClearPage07Region @AdvanceMainState: lda #$04 sta $3F @ReturnToFrameLoop: jmp GameModeFrameSync ;============================================================================== ; $CC9C-$CCD8: Game-Mode State $02 Lookup Tables ; ; Local: ; Collection of lookup tables consumed by GameModeState02 while constructing ; page-$07 object/display data and preparing the update command at $024B. ; ; Global: ; Supplies the tile/status/position/count/address parameters used by the ; state-$02 setup sequence. ; ; Notes: ; The tables are separated according to the exact addresses referenced by ; GameModeState02. Names remain structural during this first pass. ;============================================================================== ;------------------------------------------------------------------------------ ; $CC9C-$CCAD: State $02 tile/value table ; ; Indexed through $05: ; ; lda State02ValueTableA,x ; ; Written as the second byte of each 4-byte structure generated at $0760+. ; Length: $12 (18) bytes. ;------------------------------------------------------------------------------ State02ValueTableA: .byte $C6,$C3,$C1,$BA,$C7,$BB .byte $B0,$B1,$B2,$B3,$B4,$B5 .byte $B6,$B7,$B4,$B8,$B9,$B6 ;------------------------------------------------------------------------------ ; $CCAE-$CCBF: State $02 secondary value table ; ; Indexed through the same $05 value as State02ValueTableA. ; ; Written as the third byte of each generated 4-byte structure. ; Length: $12 (18) bytes. ;------------------------------------------------------------------------------ State02ValueTableB: .byte $00,$00,$00,$00,$00,$00 .byte $02,$02,$02,$02,$02,$02 .byte $02,$02,$02,$02,$02,$02 ;------------------------------------------------------------------------------ ; $CCC0-$CCC5: State $02 position/parameter pairs ; ; GameModeState02 accesses this table as overlapping little groups: ; ; lda State02PositionTable,x ; lda State02PositionTable+1,x ; ; $06 advances by 2 each outer pass. ; ; Therefore the natural structure is three 2-byte pairs. ;------------------------------------------------------------------------------ State02PositionTable: .byte $44,$88 .byte $44,$60 .byte $50,$70 ;------------------------------------------------------------------------------ ; $CCC6-$CCC8: State $02 entry-count table ; ; Indexed by $07. Determines how many four-byte structures are emitted by ; each inner construction loop. ;------------------------------------------------------------------------------ State02EntryCounts: .byte $06,$06,$03 ;------------------------------------------------------------------------------ ; $CCC9-$CCD0: Additional state-$02 values ; ; No direct reference has yet been established in the portion decoded so far. ; Keep this data structurally separated until its consumer is found. ;------------------------------------------------------------------------------ State02UnknownData: .byte $C0,$C1,$C2,$C3,$C4,$C5,$C3,$C6 ;------------------------------------------------------------------------------ ; $CCD1-$CCD8: Display-address table ; ; Indexed by: ; ; ($67 - 1) * 2 ; ; and read as two-byte values: ; ; lda State02DisplayAddressTable,y ; lda State02DisplayAddressTable+1,y ; ; The bytes form four PPU-style address pairs: ; ; $2317 ; $2319 ; $231B ; $2357 ; ; When $47 & $46 is nonzero, $08 is added to the low byte before the command ; is placed into $024B/$024C. ;------------------------------------------------------------------------------ State02DisplayAddressTable: .byte $23,$17 .byte $23,$19 .byte $23,$1B .byte $23,$57 ;============================================================================== ; $CCD9-$CD05: Game-Mode State $06 Handler ; ; Local: ; Increments $DA each frame. Normally falls through to a group of four ; per-frame update routines. ; ; When $DA reaches $28, resets $DA to $02, changes the main game-mode state ; to $04, finds an even-indexed entry in $B8+X containing $08, and changes ; that entry to $06 before continuing through the same update routines. ; ; Global: ; Handler selected by GameModeStateTable when $3F = $06. Implements a timed ; transition back to game-mode state $04 while continuing normal frame ; processing throughout the delay. ; ; Notes: ; $DA is used as a frame/progression counter. ; The $B8 array is searched only at even offsets. ;============================================================================== GameModeState06: inc $DA lda #$28 cmp $DA bne @RunFrameUpdates lda #$02 sta $DA lda #$04 sta $3F lda #$08 ldx #$FE @FindStateEntry: inx inx cmp $B8,x bne @FindStateEntry lda #$06 sta $B8,x @RunFrameUpdates: jsr UpdatePeriodicFourEntryState jsr UpdateEntityMovementStates jsr UpdateEntitySpriteStates jsr $DA40 jmp GameModeFrameSync ;============================================================================== ; $CD06-$CDAF: Game-Mode State $08 Handler ; ; Local: ; Multi-phase timed state using $87, $DB, and $32. ; ; During the initial phase, $DB counts down. When it expires, $87 advances, ; $DB and $0603 are initialized to $0A, and $001E-$0031 are cleared. ; ; During the active phase, $DB periodically advances $32 until $32 reaches ; $1E. The completion logic then updates the two parallel 16-byte state ; blocks at $67-$76 and $77-$86 and selects the next game-mode state. ; ; Global: ; Handler selected by GameModeStateTable when $3F = $08. ; ; Notes: ; $DB acts as a short-period timer. ; $87 distinguishes the initial and active phases. ; $67-$76 and $77-$86 are interchangeable 16-byte state structures. ; ; A few cross-flow destinations are kept as raw addresses during this pass ; to preserve the original control flow exactly. ;============================================================================== GameModeState08: lda $87 bne @ActivePhase dec $DB bne @InitialFrameUpdates inc $87 lda #$0A sta $DB sta $0603 lda #$00 tax @ClearLowRamState: sta $1E,x inx cpx #$14 bne @ClearLowRamState @InitialFrameUpdates: jsr UpdatePeriodicFourEntryState jsr UpdateEntitySpriteStates jsr $DA40 jmp GameModeFrameSync @ActivePhase: dec $DB bpl @ActiveFrameUpdates lda #$0A sta $DB lda $32 clc adc #$01 sta $32 cmp #$1E beq @PhaseComplete @ActiveFrameUpdates: jsr UpdatePeriodicFourEntryState jsr $DA40 jmp GameModeFrameSync @PhaseComplete: lda $48 bne @SinglePlayerRestart dec $67 bne @ContinuePlayerHandling lda #$00 sta $87 sta $69 lda #$0A sta $3F jmp GameModeFrameSync @ContinuePlayerHandling: lda #$00 sta $3F lda #$01 sta $69 lda $47 beq @CheckPrimaryBlock lda $77 bne @SwapBlocksAndToggle @CheckPrimaryBlock: lda $67 bne $CDAD sta $69 lda $46 beq @SinglePlayerRestart ldx #$0F @SwapBlocksForMainEntry: lda $67,x sta $00 lda $77,x sta $67,x lda $00 sta $77,x dex bpl @SwapBlocksForMainEntry @SinglePlayerRestart: lda #$00 sta $46 lda #$00 sta $3F jmp MainEntry @SwapBlocksAndToggle: ldx #$0F @SwapBlocks: lda $67,x sta $00 lda $77,x sta $67,x lda $00 sta $77,x dex bpl @SwapBlocks inc $46 lda $46 and #$01 sta $46 jmp GameModeFrameSync ;============================================================================== ; $CDB0-$CE1E: Game-Mode State $0C Handler ; ; Local: ; Uses $87 as a frame/progression counter. ; ; On first entry ($87 == 0): ; - Clears $001E-$0031. ; - Increments $87. ; - Initializes $32 to $01. ; ; On subsequent frames, processing is throttled by the low three bits of ; the NMI-driven frame counter $4B. Only once every eight frames does the ; handler: ; ; - Alternate a value written to $029E according to bit 0 of $87. ; - Set $0299 to $0F. ; - Increment $87. ; ; When $87 reaches $10, the routine clears $1A/$1C/$87 and selects a new ; main game-mode state according to the value at $68. ; ; Global: ; Handler selected by GameModeStateTable when $3F = $0C. ; ; This appears to implement a short visual/timed transition. The write of ; $0F to $0299 interacts directly with the palette-update mechanism decoded ; earlier, while $029E lies inside that first 16-byte palette/update buffer. ; ; Notes: ; $4B is the NMI-driven frame counter. ; ; $87 advances only once every eight frames after initialization. ; ; $68 controls which state is selected when the sequence completes. ;============================================================================== GameModeState0C: lda $87 bne @TimedPhase ldx #$00 @ClearLowRam: sta $1E,x inx cpx #$14 bne @ClearLowRam inc $87 lda #$01 sta $32 bne @RunFrameUpdates @TimedPhase: lda $4B and #$07 bne @RunFrameUpdates ldx #$11 lda $87 and #$01 bne @SetPaletteValue ldx #$20 @SetPaletteValue: stx $029E lda #$0F sta $0299 inc $87 lda #$10 cmp $87 bne @RunFrameUpdates lda #$00 sta $1A sta $1C sta $87 lda $68 cmp #$01 beq @SelectState0E inc $87 inc $87 cmp #$04 beq @SelectState0E inc $87 inc $87 cmp #$08 beq @SelectState0E cmp #$0C beq @SelectState0E cmp #$10 bne @SelectState00 @SelectState0E: lda #$0E sta $3F jmp @RunFrameUpdates @SelectState00: lda #$00 sta $3F @RunFrameUpdates: jsr UpdatePeriodicFourEntryState jsr $DA40 jmp GameModeFrameSync ;============================================================================== ; $CE1F-$CEBE: Game-Mode State $0A Handler ; ; Local: ; Uses $87 to distinguish between initialization and subsequent processing. ; ; On initial entry: ; - Fills $0700-$075F with $FF. ; - Initializes $00/$01 as a pointer to $0760. ; - Builds structured four-byte records in page $07 from several ROM ; lookup tables beginning at $CEBF. ; ; The builder uses: ; ; $00/$01 destination pointer ($0760) ; $02/$03 per-group values ; $04 outer pass counter, initially $03 ; $05 shared table index ; $06 index into the value-pair table ; $07 index into the entry-count table ; $08 inner record counter ; ; Player/mode variables $47, $77, and $46 influence whether passes repeat ; and whether the lookup index is advanced. ; ; After the construction phase, $87 is incremented. A later entry either ; clears $0760-$07FF and transitions to the code at $CD5E, or simply ; returns to the common frame loop. ; ; Global: ; Handler selected by GameModeStateTable when $3F = $0A. ; ; Structurally this is closely related to GameModeState02, but uses a ; different set of ROM tables and first clears $0700-$075F to $FF. Both ; handlers construct arrays of four-byte records in page $07. ; ; Notes: ; The tables at $CEBF-$CEF2 are decoded immediately after this routine. ; Semantic object names are deferred until the page-$07 consumers are ; understood more completely. ;============================================================================== GameModeState0A: lda $87 beq @Initialize jmp $CEAA @Initialize: ldy #$00 lda #$FF @FillPage07Prefix: sta $0700,y iny cpy #$60 bne @FillPage07Prefix lda #$60 sta $00 lda #$07 sta $01 lda #$03 sta $04 ldy #$00 sty $05 sty $06 sty $07 @BuildOuterLoop: ldx $06 lda State0AParameterPairs,x sta $02 lda State0AParameterPairs+1,x sta $03 ldx $07 lda State0AEntryCounts,x sta $08 @BuildInnerLoop: lda $03 sta ($00),y ldx $05 lda State0AValueTableA,x iny sta ($00),y lda State0AValueTableB,x iny sta ($00),y lda $02 iny sta ($00),y lda $02 clc adc #$08 sta $02 iny inc $05 dec $08 bne @BuildInnerLoop inc $06 inc $06 inc $07 lda $47 beq @ContinueOuterPass lda $77 bne @ContinueOuterPass lda $04 cmp #$02 beq $CEAA @ContinueOuterPass: dec $04 beq @CheckPlayerMode bpl @BuildOuterLoop bmi @FinishInitialization @CheckPlayerMode: lda $47 beq @FinishInitialization lda $46 beq @BuildOuterLoop @AdjustTableIndex: inc $05 inc $05 inc $05 bne @BuildOuterLoop @FinishInitialization: inc $87 bne @ReturnToFrameLoop @PostInitialization: lda #$00 tay @ClearPage07Region: sta $0760,y iny cpy #$A0 bne @ClearPage07Region jmp $CD5E @ReturnToFrameLoop: jmp GameModeFrameSync ;============================================================================== ; $CEBF-$CEF2: Game-Mode State $0A Lookup Tables ; ; Local: ; Lookup data used by GameModeState0A to construct four-byte records in ; page $07. ; ; Global: ; Provides the object/tile/value and layout parameters for state $0A. ;============================================================================== ;------------------------------------------------------------------------------ ; $CEBF-$CED2: Primary value table ; Length: $14 (20) bytes ;------------------------------------------------------------------------------ State0AValueTableA: .byte $BC,$B2,$BD,$B4,$B6,$BE,$B4,$B5 .byte $B0,$B1,$B2,$B3,$B4,$B5,$B6,$B7 .byte $B4,$B8,$B9,$B6 ;------------------------------------------------------------------------------ ; $CED3-$CEE6: Secondary value table ; Length: $14 (20) bytes ;------------------------------------------------------------------------------ State0AValueTableB: .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $02,$02,$02,$02,$02,$02,$02,$02 .byte $02,$02,$02,$02 ;------------------------------------------------------------------------------ ; $CEE7-$CEEE: Parameter-pair table ; ; $06 advances by two, so represent this as four 2-byte pairs. ;------------------------------------------------------------------------------ State0AParameterPairs: .byte $38,$88 .byte $60,$88 .byte $44,$60 .byte $50,$70 ;------------------------------------------------------------------------------ ; $CEEF-$CEF2: Inner-loop entry counts ;------------------------------------------------------------------------------ State0AEntryCounts: .byte $04,$04,$06,$03 ;============================================================================== ; $CEF3-$D0AA: Game-Mode State $00 Initialization ; ; Local: ; Performs the main initialization for the game-mode state machine: ; ; - Synchronizes with NMI and disables rendering. ; - Clears working RAM $87-$EF. ; - Clears page $0700-$07FF. ; - Copies a 32-byte palette table from GameplayPaletteData to $0299-$02B8. ; - Selects game-mode state $02. ; - Initializes gameplay/state variables and invokes several setup helpers. ; - Loads numerous parameters from ROM lookup tables. ; - Initializes arrays and display/update data. ; - Re-enables NMI with PPUCTRL=$88. ; - Returns to GameModeFrameSync. ; ; Global: ; Primary initialization handler for GameModeStateTable offset $00. ; Establishes the working state used by the later $02/$04/etc. handlers. ; ; Notes: ; Many ROM tables referenced from $EBxx/$ECxx are intentionally left raw ; during this pass and will be named when those regions are reached. ;============================================================================== GameModeState00: lda #$01 sta $40 @WaitForNmi: lda $40 bne @WaitForNmi lda #$08 sta PPUCTRL sta $43 lda #$00 sta PPUMASK ldx #$00 @ClearWorkingRam: sta $87,x inx cpx #$69 bne @ClearWorkingRam tax @ClearPage07: sta $0700,x inx bne @ClearPage07 ldy #$00 @LoadPalette: lda GameplayPaletteData,y sta $0299,y iny cpy #$20 bne @LoadPalette lda #$02 sta $3F lda $69 bne $CF4A sta $8B lda #$01 sta $6C sta $6D sta $6E sta $6F lda #$C0 sta $6A lda $68 cmp #$16 beq @SkipIncrement68 inc $68 @SkipIncrement68: jsr InitializeGameplayAttributes jsr DrawMazeNametable jsr DrawGameplayScoreFields jsr InitializeGameNametableRegion jsr DrawLevelProgressMarkers jsr $E5C1 jsr DrawGameplayHudLabels @ContinueSetup: ldx $68 lda #$00 clc @MultiplyBy6: dex bmi @MultiplyBy6Done adc #$06 bne @MultiplyBy6 @MultiplyBy6Done: sta $00 tax lda $EBB6,x tax lda #$00 clc @MultiplyBy22: dex bmi @MultiplyBy22Done adc #$16 bne @MultiplyBy22 @MultiplyBy22Done: tay ldx #$00 @CopyGameParameters: lda $EC40,y sta $9F,x inx iny cpx #$16 bne @CopyGameParameters ldx $00 lda $EBB6,x asl asl asl tax ldy #$00 sty $D0 ; Save the first selected table value separately. lda $ECAE,x sta $CF @CopyTableECae: lda $ECAE,x sta $0097,y inx iny cpy #$08 bne @CopyTableECae inc $00 ldx $00 lda $EBB6,x sta $8C inc $00 ldx $00 lda $EBB6,x asl tax lda $ECCE,x sta $8D inx lda $ECCE,x sta $8E inc $00 lda $69 beq @UseAlternateData ldx #$0C ldy #$00 lda $ECDC,x sta $008F,y iny inx lda #$C0 sec sbc $6A sta $01 @CopyOffsetData: lda $01 clc adc $ECDC,x sta $008F,y iny inx cpy #$04 bne @CopyOffsetData beq @InitializeArray @UseAlternateData: ldx $00 lda $EBB6,x asl asl tax ldy #$00 @CopyAlternateData: lda $ECDC,x sta $008F,y inx iny cpy #$04 bne @CopyAlternateData @InitializeArray: lda #$04 sta $B8 lda #$01 sta $B9 lda $8F sta $01 ldx #$00 @InitializeArrayLoop: dec $01 beq @InitializeArrayTail lda #$02 sta $BA,x lda #$00 sta $BB,x inx inx bne @InitializeArrayLoop @InitializeArrayTail: lda #$00 @ClearRemainingArray: cpx #$06 beq @ContinueFinalSetup sta $BA,x sta $BB,x inx inx bne @ClearRemainingArray @ContinueFinalSetup: lda $90 sta $D3 inc $00 ldx $00 lda $EBB6,x sta $93 inc $00 ldx $00 lda $EBB6,x sta $96 lda $A5 sta $B5 lda $A6 sta $B6 lda $AF sta $C4 sta $C6 sta $C8 sta $CA lda $B0 sta $C5 sta $C7 sta $C9 sta $CB lda #$01 sta $50 sta $51 ldy #$00 ldx #$00 lda $46 and $47 beq @CopyDisplayInit ldy #$09 @CopyDisplayInit: lda GameDisplayInitialData,y sta $026B,x iny inx cpx #$09 bne @CopyDisplayInit ldy #$00 @CopyStateData: lda GameStateInitialValues,y sta $0032,y iny cpy #$0C bne @CopyStateData ldy #$00 @NormalizeValues: lda $006C,y cmp #$02 bne @NextNormalizeValue lda #$01 sta $006C,y @NextNormalizeValue: iny cpy #$04 bne @NormalizeValues sty $C0 lda #$FF sta $89 lda #$88 sta PPUCTRL sta $43 jmp GameModeFrameSync ;============================================================================== ; $D0AB-$D0F2: Initialize/Clear Repeated Nametable Region ; ; Local: ; Selects an initial PPU destination based on ($46 & $47): ; ; zero -> $2256 ; nonzero -> $2A56 ; ; It then performs 10 iterations. For each iteration: ; ; - Sets PPUADDR to the current destination. ; - Writes six $20 tiles. ; - Writes two $2D tiles. ; - Advances the destination address by $20. ; ; Global: ; Called from GameModeState00 during game/round initialization while ; rendering is disabled. This prepares the same rectangular display region ; in one of two nametable locations depending on the current player/mode ; state represented by $46/$47. ; ; Notes: ; $00/$01 form the current 16-bit PPU destination address. ; $02 counts 10 rows. ; $03 counts the six $20 tile writes in each row. ; ; $20 and $2D are retained as raw tile IDs during this pass. ;============================================================================== InitializeGameNametableRegion: ldy #$22 lda $46 and $47 beq @SetDestination ldy #$2A @SetDestination: sty $00 lda #$56 sta $01 lda #$0A sta $02 @NextRow: lda PPUSTATUS lda $00 sta PPUADDR lda $01 sta PPUADDR lda #$06 sta $03 lda #$20 @WriteInterior: sta PPUDATA dec $03 bne @WriteInterior lda #$2D sta PPUDATA sta PPUDATA ; Advance the destination by one full nametable row ($20 bytes). lda $01 clc adc #$20 sta $01 lda $00 adc #$00 sta $00 dec $02 bne @NextRow rts ;============================================================================== ; $D0F3-$D130: Game-Mode State $00 Initialization Data ; ; Local: ; Contains three adjacent data blocks consumed during GameModeState00: ; ; $D0F3-$D0FE 12-byte state initialization table ; $D0FF-$D110 18-byte display/update table ; $D111-$D130 32-byte palette table ; ; Global: ; Supplies initial state, display-command, and palette data used when ; GameModeState00 prepares a new game/round state. ; ; Notes: ; The display table is intentionally kept as raw tile/command bytes during ; this pass. GameModeState00 selects either offset $00 or $09 from this ; table and copies nine bytes to $026B-$0273. ; ; The palette table is copied in full to $0299-$02B8. ;============================================================================== ;============================================================================== ; $D0F3-$D0FE: Game State Initialization Values ; ; Local: ; 12 bytes copied directly to zero-page RAM $32-$3D: ; ; ROM $D0F3-$D0FE -> RAM $0032-$003D ; ; Global: ; Establishes a compact block of initial game-state values during ; GameModeState00. ; ; Notes: ; Individual fields remain unnamed until their consumers are traced. ;============================================================================== GameStateInitialValues: .byte $04,$0C,$0A,$0A .byte $0A,$00,$00,$00 .byte $01,$02,$03,$00 ;============================================================================== ; $D0FF-$D110: Game Display Initialization Data ; ; Local: ; Contains two 9-byte records. GameModeState00 selects the first record when ; ($46 & $47) is zero and the second record when it is nonzero. ; ; The selected nine bytes are copied to RAM $026B-$0273. ; ; Global: ; Provides alternate display/update initialization according to the active ; player/mode state. ; ; Notes: ; These bytes appear to mix PPU/update-command values and tile IDs. ; Preserve them numerically until the consumer of $026B is decoded. ;============================================================================== GameDisplayInitialData: ; Record 0, selected when ($46 & $47) == 0. ; $D0FF-$D107 .byte $21,$36,$20,$F7,$B0,$B3,$B2,$20,$B6 ; Record 1, selected when ($46 & $47) != 0. ; $D108-$D110 .byte $29,$B6,$29,$77,$B1,$B3,$B2,$28,$B6 ;============================================================================== ; $D111-$D130: Initial Gameplay Palette ; ; Local: ; Complete 32-byte palette copied to RAM $0299-$02B8 by GameModeState00. ; The NMI palette-update mechanism later transfers these buffers into PPU ; palette RAM. ; ; Global: ; Defines the initial background and sprite color palettes used for normal ; gameplay/game-mode initialization. ; ; Notes: ; $D111-$D120 contains four background palettes. ; $D121-$D130 contains four sprite palettes. ; ; Palette values are retained as NES color indices. ;============================================================================== GameplayPaletteData: ; Background palette 0 .byte $0F,$20,$0F,$06 ; Background palette 1 .byte $0F,$11,$0F,$27 ; Background palette 2 .byte $0F,$16,$26,$06 ; Background palette 3 .byte $0F,$19,$17,$12 ; Sprite palette 0 .byte $0F,$27,$20,$06 ; Sprite palette 1 .byte $0F,$11,$20,$33 ; Sprite palette 2 .byte $0F,$21,$20,$21 ; Sprite palette 3 .byte $0F,$09,$20,$17 ;============================================================================== ; $D131-$D15F: Initialize Gameplay Nametable Attributes ; ; Local: ; Copies the 64-byte attribute table at $D160 to two PPU destinations: ; ; pass 1 -> PPU $23C0-$23FF ; pass 2 -> PPU $2BC0-$2BFF ; ; The first pass uses X=$01. After copying all $40 bytes, X is decremented. ; Since X becomes zero, execution falls through into the setup for the ; second destination at $2BC0. ; ; After the second copy, X decrements from $00 to $FF and the routine ; returns. ; ; Global: ; Called by GameModeState00 during game/round initialization. Initializes ; complete attribute tables for two nametables using identical palette- ; selection data. ; ; Notes: ; The branch at $D15E intentionally jumps back into the shared copy loop ; after selecting PPU address $2BC0. ; ; $D160 begins the 64-byte attribute source table and is not executable. ;============================================================================== InitializeGameplayAttributes: lda PPUSTATUS lda #$23 sta PPUADDR lda #$C0 sta PPUADDR ldx #$01 @CopyAttributeTable: ldy #$00 @CopyAttributeBytes: lda GameplayAttributeData,y sta PPUDATA iny cpy #$40 bne @CopyAttributeBytes dex beq @SetupSecondNametable rts @SetupSecondNametable: lda PPUSTATUS lda #$2B sta PPUADDR lda #$C0 sta PPUADDR bne @CopyAttributeTable ;============================================================================== ; $D160-$D19F: Gameplay Nametable Attribute Data ; ; Local: ; Complete 64-byte NES attribute table copied to both PPU $23C0-$23FF and ; $2BC0-$2BFF by InitializeGameplayAttributes. ; ; Global: ; Defines background-palette selections for the gameplay nametables. ; ; Notes: ; Common values: ; ; $00 -> palette 0 in all four attribute quadrants ; $55 -> palette 1 in all four quadrants ; ; Values such as $11, $51, $50 and $05 combine different palette selections ; within the four quadrants of an attribute byte. ;============================================================================== GameplayAttributeData: .byte $55,$55,$55,$55,$55,$11,$00,$00 .byte $55,$55,$55,$55,$55,$11,$00,$00 .byte $55,$55,$55,$55,$55,$11,$00,$00 .byte $55,$55,$55,$55,$55,$51,$50,$50 .byte $55,$55,$55,$55,$55,$11,$05,$05 .byte $55,$55,$55,$55,$55,$11,$00,$00 .byte $55,$55,$55,$55,$55,$55,$55,$55 .byte $55,$55,$55,$55,$55,$55,$55,$55 ;============================================================================== ; $D1A0-$D289: Gameplay Timers and State Progression ; ; Local: ; Updates several gameplay timers, palette-animation values, progression ; counters, and state variables. ; ; Global: ; Called once per active gameplay frame from GameModeState04. Coordinates ; several independent timing and progression systems. ; ; Notes: ; Address-based local labels are used during this pass to make the original ; branch destinations explicit and simplify byte-identical verification. ;============================================================================== UpdateGameplayTimers: lda $89 bmi @D1E1 inc $8A lda #$3C cmp $8A bne @D1C9 lda #$00 sta $8A dec $89 bpl @D1C9 sta $88 ldx #$00 @D1B7: lda $39,x and #$FC sta $00 txa ora $00 sta $39,x inx cpx #$04 bne @D1B7 @D1C9: lda $89 cmp #$02 bcs @D1E1 ldx #$11 lda $8A and #$08 bne @D1D9 ldx #$20 @D1D9: stx $02AE lda #$0F sta $02A9 @D1E1: lda $88 bne @D213 lda $CF cmp #$FF beq @D213 inc $D1 lda $D1 cmp #$3C bne @D213 lda #$00 sta $D1 dec $CF bpl @D213 inc $D0 lda $D0 and #$01 beq @D209 jsr $E08E lda #$0F bne @D20B @D209: lda $D2 @D20B: sta $87 ldx $D0 lda $97,x sta $CF @D213: lda $D3 beq @D236 clc adc $6A cmp #$C0 bne @D236 jsr ActivateNextStateEntry lda $D3 ldx #$00 @D225: cmp $8F,x bne @D231 inx lda $8F,x sta $D3 jmp @D236 @D231: inx cpx #$04 bne @D225 @D236: inc $D6 lda #$60 cmp $D6 bne @D251 lda #$00 sta $D6 inc $D5 lda $D5 cmp $96 bne @D251 lda #$00 sta $D5 jsr ActivateNextStateEntry @D251: ldx $D4 cpx #$02 beq @D26E lda $8D,x cmp $6A bne @D26E inc $D4 lda #$01 sta $D2 txa asl tax lda $AB,x sta $CA lda $AC,x sta $CB @D26E: ora $D7 ora $D8 beq @D289 dec $D8 bne @D289 lda $D7 beq @D283 dec $D7 lda #$3C sta $D8 rts @D283: sta $2E sta $30 sta $8B @D289: rts ;============================================================================== ; $D28A-$D29B: Activate First Available State Entry ; ; Local: ; Searches the even-indexed entries at $BA, $BC, and $BE for the first ; zero value. ; ; When a zero entry is found, replaces it with $02 and returns. ; ; If all three entries are already nonzero, returns without modifying them. ; ; Global: ; Called by UpdateGameplayTimers when certain progression timers expire. ; This appears to activate the next available entry in a three-element ; state array. ; ; Notes: ; Only the even addresses $BA/$BC/$BE are examined. The interleaved bytes ; $BB/$BD/$BF form a parallel field initialized elsewhere. ; ; The meaning of state value $02 will be refined when the consumers of ; $BA-$BF are decoded. ;============================================================================== ActivateNextStateEntry: ldx #$00 @FindAvailableEntry: lda $BA,x bne @NextEntry lda #$02 sta $BA,x rts @NextEntry: inx inx cpx #$06 bne @FindAvailableEntry rts ;============================================================================== ; $D29C-$D2A3: Sequential Tile/Value Table ; ; Local: ; Eight sequential values from $24 through $2B. ; ; Global: ; Consumer has not yet been decoded during this pass. Given the sequential ; values and the surrounding graphics-oriented code, this may represent ; tile IDs or animation values. ; ; Notes: ; Keep the table generically named until a direct reference establishes its ; exact purpose. ;============================================================================== SequentialValueTableD29C: .byte $24,$25,$26,$27,$28,$29,$2A,$2B ;============================================================================== ; $D2A4-$D36B: Gameplay Proximity / State Transition Handler ; ; Local: ; Returns immediately when $6A is zero. ; ; Otherwise scans even-indexed state entries beginning at $B8. Entries whose ; state value is $04 undergo two proximity tests using the four-byte ; structures beginning at $001E. ; ; $02 is shifted once for each scanned entry and acts as a candidate bitmask. ; When a qualifying entry is found, the routine selects one of several state ; transition paths based on X and ($02 & $88). ; ; Global: ; Called during normal gameplay processing from GameModeState04. This ; routine appears to detect proximity/collision conditions and initiate ; corresponding gameplay-state transitions. ; ; Notes: ; $00/$01 form a temporary pointer, initially $001E. ; $02 is a shifting bitmask. ; $03 is temporary storage for distance/index calculations. ; ; Lookup tables begin at ProximityCandidateValues and will be decoded separately. ;============================================================================== CheckGameplayProximity: lda $6A bne @Start rts @Start: lda #$1E sta $00 lda #$00 sta $01 lda #$01 sta $02 ldx #$00 @ScanEntries: lda $B8,x cmp #$04 bne @NextEntry ldy #$00 lda $1A cmp ($00),y bcs @FirstAxisReverse lda ($00),y sec sbc $1A bcs @CheckFirstAxisDistance @FirstAxisReverse: sbc ($00),y @CheckFirstAxisDistance: cmp #$0A bcs @NextEntry sta $03 ldy #$02 lda $1C cmp ($00),y bcs @SecondAxisReverse lda ($00),y sec sbc $1C bcs @CheckSecondAxisDistance @SecondAxisReverse: sbc ($00),y @CheckSecondAxisDistance: cmp #$0A bcs @NextEntry adc $03 cmp #$05 bcc @CandidateFound @NextEntry: inx inx lda $00 clc adc #$04 sta $00 asl $02 cpx #$0A bne @ScanEntries rts @CandidateFound: cpx #$08 beq @ProcessSecondaryState lda $02 and $88 beq @SpecialCandidate txa lsr sta $03 ldy $D9 lda ProximityCandidateValues,y ldy $03 sta $0033,y lda #$00 sta $32 ldy $D9 lda ProximityDDValues,y sta $DD lda ProximityDEValues,y sta $DE inc $D9 lda #$08 sta $B8,x sta $0607 lda #$06 sta $3F jmp $E0E6 ;------------------------------------------------------------------------------ ; $D337 ; Candidate path selected when ($02 & $88) == 0. ;------------------------------------------------------------------------------ @SpecialCandidate: lda #$08 sta $3F lda #$12 sta $32 lda #$80 sta $DB lda #$00 sta $87 rts ;------------------------------------------------------------------------------ ; $D348 ; Special path selected when X == $08. ;------------------------------------------------------------------------------ @ProcessSecondaryState: lda $8B beq @InitializeSecondaryState rts ;------------------------------------------------------------------------------ ; $D34D ; Initialize secondary state. ;------------------------------------------------------------------------------ @InitializeSecondaryState: sta $D7 lda #$80 sta $D8 sta $8B sta $0606 ldy $93 lda SecondaryStateDDValues,y sta $DD lda SecondaryStateDEValues,y sta $DE lda SecondaryState37Values,y sta $37 jmp $E0E6 ;============================================================================== ; $D36C-$D38F: Gameplay Proximity / Transition Lookup Data ; ; Local: ; Lookup data used by CheckGameplayProximity when a qualifying proximity ; condition triggers a gameplay-state transition. ; ; Global: ; Supplies values written to $33+, $37, $DD and $DE during the different ; transition paths handled by CheckGameplayProximity. ; ; Notes: ; $D370 is intentionally a label inside the table beginning at $D36C. ; No bytes are duplicated. ;============================================================================== ProximityCandidateValues: ; $D36C-$D36F .byte $2D,$2F,$32,$34 SecondaryState37Values: ; $D370-$D373 ; Also forms bytes 4-7 of ProximityCandidateValues. .byte $2C,$2E,$30,$31 ;------------------------------------------------------------------------------ ; $D374-$D377 ; Consumer not yet established. ;------------------------------------------------------------------------------ ProximityUnknownTableD374: .byte $33,$35,$36,$37 ;------------------------------------------------------------------------------ ; $D378-$D37B ; Indexed by $D9 and loaded into $DE. ;------------------------------------------------------------------------------ ProximityDEValues: .byte $00,$00,$00,$01 ;------------------------------------------------------------------------------ ; $D37C-$D37F ; Indexed by $D9 and loaded into $DD. ;------------------------------------------------------------------------------ ProximityDDValues: .byte $02,$04,$08,$06 ;------------------------------------------------------------------------------ ; $D380-$D387 ; Indexed by $93 and loaded into $DD. ;------------------------------------------------------------------------------ SecondaryStateDDValues: .byte $01,$03,$05,$07 .byte $00,$00,$00,$00 ;------------------------------------------------------------------------------ ; $D388-$D38F ; Indexed by $93 and loaded into $DE. ;------------------------------------------------------------------------------ SecondaryStateDEValues: .byte $00,$00,$00,$00 .byte $01,$02,$03,$05 ;============================================================================== ; $D390-...: Gameplay Direction / Movement Processing ; ; Local: ; Processes directional controller input, validates a requested direction ; against the current map/tile state, and selects movement parameters. ; ; Global: ; Called during normal gameplay processing from GameModeState04. ; ; Notes: ; Address-derived labels are retained during this pass to make branch ; targets explicit while maintaining byte identity. ;============================================================================== UpdatePlayerMovement: lda $48 beq @D397 jmp $D506 @D397: lda $4F and #$F0 beq @D3D2 ldx #$FF @D39F: inx asl bcc @D39F lda InputDirectionTable,x sta $50 clc adc #$02 and #$03 cmp $51 bne @D3C9 lda $1A ora $1C and #$07 bne @D3C2 ldy $50 lda $022B,y and #$F0 bne @D3C9 @D3C2: lda $50 sta $51 jmp @D3D6 @D3C9: ldx $50 lda $022B,x and #$F0 beq @D3D6 @D3D2: lda $51 sta $50 @D3D6: ldx #$04 lda $88 bne @D3DE ldx #$0A @D3DE: lda $022A cmp #$01 beq @D3F4 cmp #$02 beq @D3F4 dex dex cmp #$03 beq @D3F4 cmp #$09 beq @D3F4 dex dex @D3F4: lda $9F,x sta $B5 lda $A0,x sta $B6 lda $51 asl tay lda MovementArithmeticTable,y sta $10 lda MovementArithmeticTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D40E-$D415: Movement Arithmetic Dispatch Table ; ; Local: ; Four 16-bit handlers selected using the internal direction value in $51. ; ; Global: ; Dispatches into direction-specific coordinate arithmetic used by ; UpdatePlayerMovement. ; ; Notes: ; Direction semantics remain unnamed during this pass. ;============================================================================== MovementArithmeticTable: .word $D416 .word $D41A .word $D435 .word $D431 ;============================================================================== ; $D416-$D443: Direction-Specific Movement Arithmetic ; ; Local: ; Four entry points selected by MovementArithmeticTable converge into two ; shared arithmetic paths. ; ; $D416/$D41A select the coordinate component and enter subtraction. ; $D431/$D435 select the coordinate component and enter addition. ; ; Global: ; Computes the fixed-point movement result used by the second direction ; dispatch beginning at MovementApplyResult. ; ; Notes: ; Shared labels are deliberately non-local because multiple entry-point ; labels must branch into the same code. ;============================================================================== MovementArithmeticD416: ldx #$02 bne MovementSubtract MovementArithmeticD41A: ldx #$00 MovementSubtract: lda $1B,x sec sbc $B5 sta $1B,x lda #$00 bcs MovementSubtractNoBorrow lda #$01 MovementSubtractNoBorrow: clc adc $B6 sta $CC jmp MovementApplyResult MovementArithmeticD431: ldx #$00 beq MovementAdd MovementArithmeticD435: ldx #$02 MovementAdd: lda $1B,x clc adc $B5 sta $1B,x lda #$00 adc $B6 sta $CC ;============================================================================== ; $D444-$D459: Apply Movement Result and Dispatch Direction Handler ; ; Local: ; Decrements the movement accumulator $CC. If it becomes negative, returns ; immediately. ; ; Otherwise uses the current direction value at $51 as a byte offset into a ; four-entry handler table beginning at $D45A. ; ; Global: ; Second stage of UpdatePlayerMovement. After the fixed-point arithmetic ; performed by the $D416-$D443 handlers, this dispatches to the actual ; direction-specific coordinate/tile movement code. ; ; Notes: ; $51 is multiplied by two before indexing the pointer table. ;============================================================================== MovementApplyResult: dec $CC bpl @D449 rts @D449: lda $51 asl tay lda MovementDirectionTable,y sta $10 lda MovementDirectionTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D45A-$D461: Direction-Specific Movement Dispatch Table ; ; Local: ; Four handlers selected according to the internal direction value in $51. ; ; Notes: ; Handler names remain address-based until coordinate effects are fully ; mapped to semantic direction names. ;============================================================================== MovementDirectionTable: .word MovementDirectionD49A .word MovementDirectionD490 .word MovementDirectionD46C .word MovementDirectionD462 ;============================================================================== ; $D462-$D501: Direction-Specific Movement Handlers ; ; Local: ; Four entry points selected by MovementDirectionTable. ; ; $D462 and $D46C enter the increment path. ; $D490 and $D49A enter the decrement path. ; ; X selects which coordinate component at $1A/$1C is modified. $00 and $01 ; preserve working indexes used for collision/tile checks against $022B. ; ; After movement, the shared tail handles coordinate wrapping/state flags ; and, when aligned to an 8-unit boundary, commits a requested direction ; change from $50 to $51. ; ; Global: ; Final stage of UpdatePlayerMovement. Applies movement to the player's ; coordinate state and reconciles requested direction changes. ; ; Notes: ; Entry points intentionally share code. Use non-local labels for shared ; targets so ca65 scope rules do not change the branch structure. ;============================================================================== ;------------------------------------------------------------------------------ ; $D462 ; Increment coordinate component X=0. ;------------------------------------------------------------------------------ MovementDirectionD462: ldx #$00 stx $00 lda #$03 sta $01 bne MovementIncrement ;------------------------------------------------------------------------------ ; $D46C ; Increment coordinate component X=2. ;------------------------------------------------------------------------------ MovementDirectionD46C: ldx #$02 stx $00 stx $01 ;------------------------------------------------------------------------------ ; $D472: Shared increment path. ;------------------------------------------------------------------------------ MovementIncrement: lda $1A,x clc adc #$01 sta $1A,x and #$04 bne MovementSharedTail ldx $01 lda $022B,x and #$F0 beq MovementSharedTail ldx $00 lda $1A,x and #$FC sta $1A,x bne MovementSharedTail ;------------------------------------------------------------------------------ ; $D490 ; Decrement coordinate component X=0. ;------------------------------------------------------------------------------ MovementDirectionD490: ldx #$00 stx $00 lda #$01 sta $01 bne MovementDecrement ;------------------------------------------------------------------------------ ; $D49A ; Decrement coordinate component X=2. ;------------------------------------------------------------------------------ MovementDirectionD49A: ldx #$02 stx $00 lda #$00 sta $01 ;------------------------------------------------------------------------------ ; $D4A2: Shared decrement path. ;------------------------------------------------------------------------------ MovementDecrement: lda $1A,x sec sbc #$01 sta $1A,x and #$04 beq MovementSharedTail ldx $01 lda $022B,x and #$F0 beq MovementSharedTail ldx $00 lda $1A,x clc adc #$04 and #$FC sta $1A,x ;------------------------------------------------------------------------------ ; $D4C1: Shared movement/wrap tail. ;------------------------------------------------------------------------------ MovementSharedTail: lda $1A cmp #$18 bcc MovementCheckLeftWrap cmp #$A9 bcs MovementCheckLeftWrap lda $38 and #$DF bcc MovementStoreWrapState MovementCheckLeftWrap: lda #$20 ora $38 MovementStoreWrapState: sta $38 lda $1A cmp #$0B bcs MovementCheckRightWrap lda #$BF sta $1A bne MovementCheckAlignment MovementCheckRightWrap: cmp #$C0 bcc MovementCheckAlignment lda #$0B sta $1A MovementCheckAlignment: lda $1A ora $1C and #$07 bne MovementReturnToArithmetic lda $50 cmp $51 beq MovementReturnToArithmetic sta $51 inc $CC inc $CC MovementReturnToArithmetic: jmp MovementApplyResult ;============================================================================== ; $D502-$D505: Controller Direction Mapping ;============================================================================== InputDirectionTable: .byte $03,$01,$02,$00 ;============================================================================== ; $D506-$D51E: Scripted Direction/Input Update ; ; Local: ; Uses $E3 as a countdown and $E4 as an offset into the table beginning at ; $D51F. ; ; Each time $E3 reaches zero: ; - Advances $E4 by two. ; - Loads a new duration from $D51F+$E4 into $E3. ; ; It then takes the following table byte, masks it to two bits, and ; tail-jumps into UpdatePlayerMovement at $D3A6 with that value in A. ; ; Global: ; Entered from UpdatePlayerMovement when $48 is nonzero. This provides a ; scripted/autonomous direction source in place of normal controller input. ; ; Notes: ; $E4 indexes two-byte table records: ; ; byte 0 = duration/count ; byte 1 = direction/control value ; ; The second byte is masked with #$03 before entering movement processing. ;============================================================================== UpdateScriptedDirection: ldy $E4 dec $E3 bne @UseCurrentEntry inc $E4 inc $E4 ldy $E4 lda ScriptedDirectionTable,y sta $E3 @UseCurrentEntry: lda ScriptedDirectionTable+1,y and #$03 jmp $D3A6 ;============================================================================== ; $D51F-...: Scripted Direction Table ; ; Local: ; Two-byte records used by UpdateScriptedDirection: ; ; byte 0 = duration ; byte 1 = direction/control number ; ; Global: ; Drives autonomous/scripted movement when $48 selects this path instead of ; normal player controller processing. ;============================================================================== ScriptedDirectionTable: .byte $00,$01 .byte $D0,$00 .byte $28,$01 .byte $28,$02 .byte $90,$03 .byte $38,$00 .byte $20,$03 .byte $18,$01 .byte $60,$02 .byte $30,$01 .byte $10,$02 .byte $48,$01 .byte $18,$02 .byte $40,$01 .byte $20,$02 .byte $18,$00 .byte $20,$03 .byte $40,$02 .byte $38,$01 .byte $18,$02 .byte $E0,$03 .byte $30,$00 .byte $28,$01 .byte $28,$02 .byte $20,$02 .byte $80,$03 .byte $40,$00 .byte $40,$01 ;============================================================================== ; $D557-$D586: Update Four Gameplay Entities ; ; Local: ; Iterates four gameplay entries using X offsets: ; ; $00, $02, $04, $06 ; ; Initializes several temporary values before the loop: ; ; $00 = $2F ; $01 = $02 ; $02 = $1E ; $03 = $00 ; $04 = $01 ; ; For each entry, calls the dispatcher at $D587. After each call: ; ; $00 += 4 ; $02 += 4 ; $04 <<= 1 ; X += 2 ; ; Processing ends after X reaches $08. ; ; Global: ; Called every active gameplay frame from GameModeState04. Coordinates ; per-entry updates for four gameplay entities/state slots. ; ; Notes: ; $00 and $02 appear to advance through parallel four-byte-strided RAM ; structures. ; ; $04 becomes a per-entry bitmask: ; ; $01,$02,$04,$08 ; ; The exact roles of the temporary values will become clearer as the ; dispatcher at $D587 and its handlers are decoded. ;============================================================================== UpdateGameplayEntities: ldx #$00 lda #$2F sta $00 lda #$02 sta $01 lda #$1E sta $02 lda #$00 sta $03 lda #$01 sta $04 @UpdateNextEntity: jsr $D587 lda $00 clc adc #$04 sta $00 lda $02 clc adc #$04 sta $02 asl $04 inx inx cpx #$08 bne @UpdateNextEntity rts ;============================================================================== ; $D587-$D595: Gameplay Entity State Dispatcher ; ; Local: ; Reads the state value for the current entity from $B8,X and uses that ; value directly as a byte offset into EntityStateTable. ; ; The selected 16-bit handler address is copied to $10/$11 and execution ; transfers to it with an indirect JMP. ; ; Global: ; Called by UpdateGameplayEntities once for each of four gameplay entity ; slots. This provides the per-entity state machine used by those entries. ; ; Notes: ; X identifies the current entry using even offsets: ; ; $00,$02,$04,$06 ; ; $B8,X is a byte offset into the pointer table, so valid state values are ; even: ; ; $00,$02,$04,$06,$08 ;============================================================================== DispatchGameplayEntityState: ldy $B8,x lda EntityStateTable,y sta $10 lda EntityStateTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D596-$D59F: Gameplay Entity State Table ; ; Local: ; Five 16-bit handlers indexed directly by the even state value stored in ; $B8,X. ; ; Global: ; Defines the per-entity state machine used by ; DispatchGameplayEntityState. ; ; Notes: ; States $04 and $06 deliberately share the same handler at $D5A1. ; State $08 targets $D5A0, one byte before that shared handler, suggesting ; another intentional overlapping entry point. ;============================================================================== EntityStateTable: .word $D8D5 ; state $00 .word $D885 ; state $02 .word $D5A1 ; state $04 .word $D5A1 ; state $06 .word EntityState08 ; state $08 ;============================================================================== ; $D5A0: Entity State $08 Handler ; ; Local: ; Returns immediately. ; ; Global: ; The entity-state table uses this as the handler for state $08, effectively ; disabling further processing for that entity state. ;============================================================================== EntityState08: rts ;============================================================================== ; $D5A1-$D5B4: Entity State $04/$06 Secondary Dispatcher ; ; Local: ; Called for entity states $04 and $06 from EntityStateTable. ; ; First invokes the common entity helper at $D821. It then reads the ; secondary state value at $B9,X, doubles it, and uses the result as a byte ; offset into a four-entry pointer table at $D5B5. ; ; The selected address is stored in $10/$11 and execution transfers through ; an indirect JMP. ; ; Global: ; Provides a second-level state machine for gameplay entities whose primary ; state at $B8,X is $04 or $06. ; ; Notes: ; $B9,X behaves as an ordinal secondary state here. Unlike several earlier ; state tables, the value is explicitly multiplied by two before indexing ; the pointer table. ;============================================================================== DispatchEntitySecondaryState: jsr $D821 lda $B9,x asl tay lda EntitySecondaryStateTable,y sta $10 lda EntitySecondaryStateTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D5B5-$D5BC: Entity Secondary-State Dispatch Table ; ; Local: ; Four handler addresses selected using ($B9,X * 2). ; ; Global: ; Defines the second-level state machine used by primary entity states ; $04 and $06. ;============================================================================== EntitySecondaryStateTable: .word EntitySecondaryState0 ; secondary state 0 .word EntitySecondaryState1 ; secondary state 1 .word EntitySecondaryState2 ; secondary state 2 .word EntitySecondaryState3 ; secondary state 3 ;============================================================================== ; $D5BD-$D5D2: Secondary-State Coordinate Adjustment ; ; Local: ; Secondary state 2 enters at $D5BD and selects Y=$03. ; Secondary state 3 enters at $D5C1 and selects Y=$01. ; ; Both then read through the indirect pointer at $02/$03, add the ; entity-specific values at $C2,X/$C3,X, write the low-byte result back ; through the pointer, and store the high-byte result in $CC. ; ; Control then tail-jumps to $D5EB. ; ; Global: ; Shared movement/update arithmetic for two secondary entity states. ; ; Notes: ; $02/$03 were initialized by UpdateGameplayEntities and advance by four ; bytes per entity, so they identify the current entity's four-byte state ; structure. ;============================================================================== EntitySecondaryState2: ldy #$03 bne EntitySecondaryAdjust EntitySecondaryState3: ldy #$01 EntitySecondaryAdjust: lda ($02),y clc adc $C2,x sta ($02),y lda #$00 adc $C3,x sta $CC jmp $D5EB ;============================================================================== ; $D5D3-$D5EE: Entity Secondary States $00/$01 Movement Arithmetic ; ; Local: ; Secondary state 0 enters at $D5D3 and selects Y=$03. ; Secondary state 1 enters at $D5D7 and selects Y=$01. ; ; Both subtract the entity-specific movement value at $C2,X from the ; structure addressed by $02/$03. Borrow is converted into a high-byte ; adjustment using $C3,X and stored in $CC. ; ; Afterward, $CC is decremented. A negative result returns immediately; ; otherwise execution continues into another secondary dispatcher at $D5EF. ; ; Global: ; Complements the addition paths at $D5BD/$D5C1. Together, the four ; secondary states provide signed movement along two coordinate components. ; ; Notes: ; The shared labels are non-local because multiple handler entry points ; converge into them. ;============================================================================== EntitySecondaryState0: ldy #$03 bne EntitySecondarySubtract EntitySecondaryState1: ldy #$01 EntitySecondarySubtract: lda ($02),y sec sbc $C2,x sta ($02),y lda #$00 bcs EntitySecondarySubtractNoBorrow lda #$01 EntitySecondarySubtractNoBorrow: clc adc $C3,x sta $CC ;============================================================================== ; $D5EB-$D600: Apply Entity Movement and Dispatch Direction Substate ; ; Local: ; Shared continuation reached by both the addition and subtraction ; secondary-state handlers. ; ; Decrements $CC. If it becomes negative, returns. Otherwise reads the ; current entity secondary state from $B9,X, doubles it, and selects a ; handler from the table at $D601. ; ; Global: ; Final arithmetic stage for entity movement before dispatching into the ; direction/substate-specific update handlers. ;============================================================================== EntitySecondaryApplyResult: dec $CC bpl EntityDirectionDispatch rts EntityDirectionDispatch: lda $B9,x asl tay lda EntityDirectionStateTable,y sta $10 lda EntityDirectionStateTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D601-$D608: Entity Direction-State Dispatch Table ; ; Local: ; Four handlers selected from the entity's secondary state at $B9,X. ; ; Notes: ; The pointer order deliberately differs from the physical order of the ; handlers in ROM. ;============================================================================== EntityDirectionStateTable: .word $D609 .word $D61D .word $D60F .word $D617 ;============================================================================== ; $D609-$D622: Entity Direction-State Handlers ; ; Local: ; Four handlers selected by EntityDirectionStateTable. ; ; Each handler configures: ; ; $05 = +1 or -1 ; Y = $00 or $02 ; ; before entering the shared coordinate-update code at $D623. ; ; Global: ; Provides the four direction-specific movement modes for the per-entity ; state machine. ; ; Notes: ; The four states form two coordinate axes and two signed directions: ; ; $D609 -> $05=$FF, Y=$02 ; $D60F -> $05=$01, Y=$02 ; $D617 -> $05=$01, Y=$00 ; $D61D -> $05=$FF, Y=$00 ; ; Concrete direction names remain deferred until the coordinate axes are ; mapped to screen X/Y. ;============================================================================== EntityDirectionD609: lda #$FF sta $05 bne EntityDirectionAxis2 EntityDirectionD60F: lda #$01 sta $05 EntityDirectionAxis2: ldy #$02 bne EntityDirectionMove EntityDirectionD617: lda #$01 sta $05 bne EntityDirectionAxis0 EntityDirectionD61D: lda #$FF sta $05 EntityDirectionAxis0: ldy #$00 ;============================================================================== ; $D623-$D69A: Shared Entity Direction Movement ; ; Local: ; Applies the signed step in $05 to either byte $00 or $02 of the entity ; structure referenced by $02/$03. ; ; Performs coordinate wrapping/bounds handling, updates a per-entity flag ; stored in $39+, checks tile/grid alignment, and when the entity reaches a ; specific location ($60,$70) changes the entity primary state to $02. ; ; Global: ; Shared movement implementation for the four handlers above. ; ; Notes: ; This section eventually rejoins the earlier movement arithmetic at $D5EB ; when the entity is not aligned to the grid. ;============================================================================== EntityDirectionMove: lda ($02),y clc adc $05 sta ($02),y ldy #$00 lda ($02),y cmp #$0A bne @D637 lda #$BF sta ($02),y bne @D64B @D637: cmp #$C0 bne @D641 lda #$0B sta ($02),y bne @D64B @D641: cmp #$18 bcc @D64B cmp #$A9 bcc @D64F @D64B: lda #$20 bne @D651 @D64F: lda #$00 @D651: sta $05 txa lsr tay lda $39,y and #$DF ora $05 sta $39,y ldy #$00 lda ($02),y ldy #$02 ora ($02),y and #$07 beq @D66E jmp EntitySecondaryApplyResult @D66E: ldy #$00 lda ($02),y cmp #$60 bne $D69B ldy #$02 lda ($02),y cmp #$70 bne $D69B lda #$02 sta $B8,x lda #$00 clc sbc $04 and $88 sta $88 txa lsr tay sta $05 lda $39,y and #$FC ora $05 sta $39,y rts ;============================================================================== ; $D69B-$D6C6: Entity Movement Continuation / Target Selection ; ; Local: ; Continues entity processing after direction movement. ; ; Primary entity state $06 selects the fixed value $6F60 in $CD/$CE. ; Otherwise entity bitmasks in $04 are tested against $88 and $87. ; ; ($04 & $88) != 0 transfers to $D70A. ; ($04 & $87) != 0 transfers to $D6CF. ; ; If neither mask matches, the current entity index X selects a 16-bit value ; from EntityTargetTable and processing continues at $D778. ; ; Global: ; Chooses the next entity target/path-processing mode based on entity state ; and per-entity bitmasks. ; ; Notes: ; Cross-region branch targets remain raw until the destination blocks are ; decoded. ;============================================================================== EntityMovementContinuation: lda $B8,x cmp #$06 bne @CheckEntityMasks lda #$60 sta $CD lda #$6F sta $CE jmp $D778 @CheckEntityMasks: lda $04 and $88 bne $D70A lda $04 and $87 bne $D6CF txa tay lda EntityTargetTable,y sta $CD lda EntityTargetTable+1,y sta $CE jmp $D778 ;============================================================================== ; $D6C7-$D6CE: Entity Target Table ; ; Local: ; Four 16-bit values indexed directly by the even entity offset X. ; ; Global: ; Supplies the $CD/$CE working target used by EntityMovementContinuation. ; ; Notes: ; Values are retained numerically until the consumer at $D778 is decoded. ;============================================================================== EntityTargetTable: .word $08A8 .word $0818 .word $D0A8 .word $D018 ;============================================================================== ; $D6CF-$D6DD: Entity Target-Selection Dispatcher ; ; Local: ; Uses the current even entity index X directly as a byte offset into the ; four-entry pointer table at $D6DE. ; ; The selected handler address is stored in $10/$11 and execution transfers ; through an indirect JMP. ; ; Global: ; Entered from EntityMovementContinuation when the current entity's bit in ; $87 is set. Selects entity-specific target/path behavior. ; ; Notes: ; X is already constrained by the calling code to the even entity offsets: ; ; $00,$02,$04,$06 ; ; Therefore no multiply-by-two is required here. ;============================================================================== DispatchEntityTargetBehavior: txa tay lda EntityTargetBehaviorTable,y sta $10 lda EntityTargetBehaviorTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D6DE-$D6E5: Entity Target-Behavior Dispatch Table ; ; Local: ; Four 16-bit handlers indexed directly by the even entity index X. ; ; Global: ; Selects different target/path calculations for each of the four gameplay ; entities. ; ; Notes: ; The fourth entry deliberately points backward to $D6E6, immediately after ; this table. ;============================================================================== EntityTargetBehaviorTable: .word EntityTargetBehaviorD73B ; X = $00 .word EntityTargetBehaviorD745 ; X = $02 .word EntityTargetBehaviorD764 ; X = $04 .word EntityTargetBehaviorD6E6 ; X = $06 ;============================================================================== ; $D6E6-$D73A: Entity Target Behavior - Proximity/Direction Selection ; ; Local: ; Computes absolute differences between $1A/$2A and $1C/$2C. ; If either difference is at least $20, transfers to EntityTargetBehaviorD73B. ; ; Otherwise attempts up to five candidate directions. Each candidate is ; checked against the data addressed by $00/$01 and against the current ; entity direction/state at $B9,X. ; ; The selected value is stored into $B9,X before returning to the common ; entity movement result handler at $D5EB. ; ; Global: ; Fourth entry in EntityTargetBehaviorTable, selected for entity X=$06. ; ; Notes: ; Cross-block target $D73B remains raw until that handler is decoded. ;============================================================================== EntityTargetBehaviorD6E6: lda $1A sec sbc $2A bcs @D6F4 sta $05 lda #$00 sec sbc $05 @D6F4: cmp #$20 bcs $D73B lda $1C sec sbc $2C bcs @D703 sta $05 lda #$00 sec sbc $05 @D703: cmp #$20 bcs $D73B lda $4B sta $05 lda #$05 sta $06 @D712: dec $06 bne @D71A lda #$02 bne @D736 @D71A: inc $05 lda $05 and #$03 tay lda ($00),y and #$F8 bne @D712 lda $05 clc adc #$02 and #$03 cmp $B9,x beq @D712 lda $05 and #$03 @D736: sta $B9,x jmp EntitySecondaryApplyResult ;============================================================================== ; $D73B-$D777: Entity Target Calculation Handlers ; ; Local: ; Three handlers selected by EntityTargetBehaviorTable. ; ; $D73B: ; Uses the current $1A/$1C coordinate pair directly as the target. ; ; $D745: ; Uses $51 to select a signed coordinate offset from the table at $D75C, ; then adds that offset to $1A/$1C. ; ; $D764: ; Reflects/extrapolates the current coordinate pair relative to ; $1E/$20. ; ; All three produce the target coordinate in $CD/$CE and converge on the ; still-undecoded common processing at $D778. ; ; Global: ; Three of the four target strategies selected by ; EntityTargetBehaviorTable. ; ; Notes: ; Cross-block destination $D778 remains raw until the common handler is ; decoded separately. ;============================================================================== ;------------------------------------------------------------------------------ ; $D73B: direct target ;------------------------------------------------------------------------------ EntityTargetBehaviorD73B: lda $1A sta $CD lda $1C sta $CE bne $D778 ;------------------------------------------------------------------------------ ; $D745: direction-offset target ;------------------------------------------------------------------------------ EntityTargetBehaviorD745: lda $51 asl tay lda EntityDirectionTargetOffsets,y clc adc $1A sta $CD lda EntityDirectionTargetOffsets+1,y clc adc $1C sta $CE jmp $D778 ;============================================================================== ; $D75C-$D763: Direction Target Offset Table ; ; Local: ; Four signed coordinate offset pairs selected by ($51 * 2). ; ; As signed 8-bit values: ; ; 0 -> ( 0, -24 ) ; 1 -> (-24, 0 ) ; 2 -> ( 0, +24 ) ; 3 -> (+24, 0 ) ; ; Global: ; Used by EntityTargetBehaviorD745 to project a target position in the ; entity's current direction. ;============================================================================== EntityDirectionTargetOffsets: .byte $00,$E8 .byte $E8,$00 .byte $00,$18 .byte $18,$00 ;------------------------------------------------------------------------------ ; $D764: relative/reflected target ; ; Computes: ; ; $CD = $1A + ($1A - $1E) ; $CE = $1C + ($1C - $20) ; ; which algebraically corresponds to: ; ; $CD = 2*$1A - $1E ; $CE = 2*$1C - $20 ; ; using 8-bit arithmetic. ;------------------------------------------------------------------------------ EntityTargetBehaviorD764: lda $1A sec sbc $1E clc adc $1A sta $CD lda $1C sec sbc $20 clc adc $1C sta $CE ;============================================================================== ; $D778-$D79F: Common Entity Target Processing ; ; Local: ; Shared continuation used after an entity target coordinate has been ; calculated in $CD/$CE. ; ; Uses the entity primary state at $B8,X, minus $04, as a byte offset into ; a packed pointer table beginning at $D881. The selected pointer is stored ; in $0A/$0B and passed to BuildLegalDirectionList. ; ; The results returned in $0C/$0D determine the entity's next secondary ; state at $B9,X: ; ; $0C == $FF ; -> force state $02 ; ; $0C != $FF and $0D == $FF ; -> store $0C into $B9,X and return ; ; $0C != $FF and $0D != $FF ; -> continue at $D7A0 for additional selection logic ; ; Global: ; Common target-evaluation stage reached by the entity target strategies ; at $D73B, $D745 and $D764, as well as fixed-target paths from earlier ; entity processing. ; ; Notes: ; $D881 remains undecoded and is therefore referenced by address. ; ; The branch to $D7A0 is intentionally kept raw because $D7A0 belongs to ; the next block and is an explicit continuation rather than an RTS. ;============================================================================== EntityTargetCommon: lda $B8,x sec sbc #$04 tay lda $D881,y sta $0A lda $D882,y sta $0B jsr BuildLegalDirectionList lda $0C cmp #$FF bne @D795 lda #$02 bne @D79D @D795: lda $0D cmp #$FF bne $D7A0 lda $0C @D79D: sta $B9,x rts ;============================================================================== ; $D7A0-$D820: Select Entity Direction Toward Target ; ; Local: ; Entered from EntityTargetCommon when both $0C and $0D returned by ; BuildLegalDirectionList are valid. ; ; Computes absolute coordinate distances between target $CD/$CE and the ; current entity coordinates addressed through $02/$03. ; ; $05 accumulates direction-order information according to: ; - sign of the horizontal/vertical difference ; - which axis has the larger absolute distance ; ; The result is used to index a direction-priority table at $D871. ; Candidate directions are compared against the four values at $0C-$0F. ; When a candidate matches, execution branches back to the common direction ; store at $D79D. ; ; If the preferred candidates fail, the routine tests directions adjacent ; to the current secondary state $B9,X before eventually returning. ; ; Global: ; Alternate continuation of EntityTargetCommon. Selects the next movement ; direction for an entity based on its current position and target. ; ; Notes: ; $05 is a temporary direction-priority index. ; $06/$07 hold absolute coordinate differences. ; $08 is temporary storage during absolute-value calculations. ; ; $D871 is not decoded yet and remains a raw table reference. ;============================================================================== SelectEntityDirectionTowardTarget: ldy #$00 sty $05 lda $CD sec sbc ($02),y bcs @D7B9 sta $08 lda #$00 sec sbc $08 inc $05 inc $05 inc $05 inc $05 @D7B9: sta $06 lda $CE ldy #$02 sec sbc ($02),y bcs @D7D2 sta $08 lda #$00 sec sbc $08 inc $05 inc $05 @D7D2: sta $07 lda $06 cmp $07 bcs @D7DB inc $05 @D7DB: lda $05 asl sta $05 lda #$02 sta $06 @D7E3: ldy $05 lda $D871,y ldy #$00 @D7EA: cmp $000C,y beq $D79D iny cpy #$04 bne @D7EA inc $05 dec $06 bne @D7E3 lda $B9,x sec sbc #$01 and #$03 ldy #$00 @D802: cmp $000C,y beq $D79D iny cpy #$04 bne @D802 lda $B9,x clc adc #$01 and #$03 ldy #$00 @D814: cmp $000C,y beq $D79D iny cpy #$04 bne @D814 rts ;============================================================================== ; $D821-$D870: Select Entity Movement Parameters ; ; Local: ; Chooses the two-byte movement/step value stored at: ; ; $C2,X ; $C3,X ; ; for the current gameplay entity. ; ; Selection depends on: ; ; - primary entity state $B8,X ; - current entity coordinates through ($02) ; - entity bitmask $04 against $88 ; - whether X is zero or identifies another entity ; ; Parameter pairs are copied from several global values: ; ; $B3/$B4 ; $CA/$CB ; $AF/$B0 ; $B1/$B2 ; ; Primary state $06 instead installs the fixed pair: ; ; $00,$02 ; ; Global: ; Called before the $B9,X secondary-state dispatcher at $D5A1. This chooses ; the movement increment used by the entity movement arithmetic. ; ; Notes: ; The coordinate pointer $02/$03 identifies the current entity's structure. ; X is the even entity slot index. ;============================================================================== SelectEntityMovementSpeed: lda $B8,x cmp #$06 beq @D864 ldy #$02 lda ($02),y cmp #$70 bne @D843 ldy #$00 lda ($02),y cmp #$90 bcs @D83D cmp #$30 bcs @D843 @D83D: lda $B3 sta $C2,x lda $B4 sta $C3,x rts @D843: lda $04 and $88 bne @D85A txa bne @D854 lda $CA sta $C2 lda $CB sta $C3 rts @D854: lda $AF sta $C2,x lda $B0 sta $C3,x rts @D85A: lda $B1 sta $C2,x lda $B2 sta $C3,x rts @D864: lda #$00 sta $C2,x lda #$02 sta $C3,x rts ;============================================================================== ; $D871-$D880: Entity Direction Priority Table ; ; Local: ; Sixteen direction values used by SelectEntityDirectionTowardTarget. ; ; $05 is derived from: ; - the sign of each target-coordinate difference, ; - which axis has the greater absolute distance. ; ; $05 is doubled before indexing this table, and the routine may examine ; the following entry as its second preferred candidate. ; ; Global: ; Defines the preferred entity movement directions for the different ; relative target-position quadrants/axis priorities. ; ; Notes: ; Values are internal direction numbers 0-3. Concrete direction names remain ; deferred until the internal direction numbering is fully mapped. ; ; The natural layout is eight pairs of preferred directions. ;============================================================================== EntityDirectionPriorityTable: .byte $03,$02 .byte $02,$03 .byte $03,$00 .byte $00,$03 .byte $01,$02 .byte $02,$01 .byte $01,$00 .byte $00,$01 ;============================================================================== ; $D881-$D884: Entity Path/Map Pointer Table ; ; Local: ; Two 16-bit values selected according to entity primary state: ; ; state $04 -> $0000 ; state $06 -> $082C ; ; The selected value is placed in $0A/$0B and passed to the helper at ; BuildLegalDirectionList. ; ; Global: ; Supplies state-dependent path/map parameters used by EntityTargetCommon. ; ; Notes: ; Keep the entries as literal 16-bit values until BuildLegalDirectionList ; is decoded and the semantic meaning of $0A/$0B is established. ;============================================================================== EntityPathParameterTable: .word $0000 .word $082C ;============================================================================== ; $D885-$D8D4: Entity Primary State $02 Handler ;============================================================================== UpdateEntityState02: lda #$00 tay sta $05 lda #$80 sta $06 lda ($02),y cmp #$60 beq UpdateEntityState02SecondaryPhase bcc @D89C dec $05 lda #$01 bne @D89E @D89C: lda #$03 @D89E: sta $B9,x ldy #$01 lda ($02),y clc adc $06 sta ($02),y ldy #$00 lda ($02),y adc $05 sta ($02),y rts ;============================================================================== ; $D8B2-$D8D4: Entity State $02 Secondary Movement Phase ;============================================================================== UpdateEntityState02SecondaryPhase: lda #$00 sta $B9,x ldy #$03 lda ($02),y sec sbc #$80 sta ($02),y ldy #$02 lda ($02),y sbc #$00 sta ($02),y cmp #$58 beq @D8CC rts @D8CC: lda #$01 sta $B9,x lda #$04 sta $B8,x rts ;============================================================================== ; $D8D5-$D913: Entity Primary State $00 Handler ; ; Local: ; Initializes working movement values: ; ; $05 = $00 ; $06 = $80 ; ; Then uses the entity secondary state at $B9,X as a byte offset into a ; two-entry table at $D8EC. ; ; The selected subhandler adjusts bytes 3/2 of the current entity structure ; through $02/$03. When the resulting high byte crosses defined thresholds, ; $B9,X advances to secondary state $02 or resets to $00. ; ; Global: ; Handler selected by EntityStateTable for primary entity state $00. ; ; Notes: ; Unlike the $D5A1 secondary dispatcher, $B9,X is already treated as a byte ; offset here. Valid values are $00 and $02. ;============================================================================== EntityState00: lda #$00 sta $05 lda #$80 sta $06 ldy $B9,x lda EntityState00SubstateTable,y sta $10 lda EntityState00SubstateTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D8EC-$D8EF: Entity State $00 Secondary Table ;============================================================================== EntityState00SubstateTable: .word UpdateEntityState00MovingNegative .word UpdateEntityState00MovingPositive ;------------------------------------------------------------------------------ ; $D8F0: secondary state $00 ;------------------------------------------------------------------------------ UpdateEntityState00MovingNegative: dec $05 ;------------------------------------------------------------------------------ ; $D8F2: secondary state $02 ;------------------------------------------------------------------------------ UpdateEntityState00MovingPositive: ldy #$03 lda ($02),y clc adc $06 sta ($02),y ldy #$02 lda ($02),y adc $05 sta ($02),y cmp #$69 bcs @D90B lda #$02 bne @D911 @D90B: cmp #$70 bcc @D913 lda #$00 @D911: sta $B9,x @D913: rts ;============================================================================== ; $D914-$D943: Update Four Gameplay Entity States ; ; Local: ; Iterates the four gameplay entity slots using even X offsets: ; ; $00,$02,$04,$06 ; ; Initializes the same per-entity working values used by the other entity ; update coordinator: ; ; $00 = $2F ; $01 = $02 ; $02 = $1E ; $03 = $00 ; $04 = $01 ; ; Calls the entity dispatcher at $D944 for each slot. ; ; After each call: ; ; $00 += 4 ; $02 += 4 ; $04 <<= 1 ; X += 2 ; ; Global: ; Called from GameModeState06 during its per-frame update sequence. ; Processes a second entity-state machine for all four gameplay entities. ; ; Notes: ; The layout closely parallels UpdateGameplayEntities at $D557, but uses ; a different dispatcher and therefore a different behavior set. ;============================================================================== UpdateEntityMovementStates: ldx #$00 lda #$2F sta $00 lda #$02 sta $01 lda #$1E sta $02 lda #$00 sta $03 lda #$01 sta $04 @D92A: jsr $D944 lda $00 clc adc #$04 sta $00 lda $02 clc adc #$04 sta $02 asl $04 inx inx cpx #$08 bne @D92A rts ;============================================================================== ; $D944-$D952: Gameplay Entity Transition-State Dispatcher ; ; Local: ; Reads the current entity primary state from $B8,X and uses it directly as ; a byte offset into the packed pointer table at $D953. ; ; The selected 16-bit address is stored in $10/$11 and execution transfers ; to it through an indirect JMP. ; ; Global: ; Called by UpdateEntityMovementStates once for each of the four ; gameplay entity slots. ; ; Notes: ; X identifies the entity using even offsets: ; ; $00,$02,$04,$06 ; ; $B8,X is itself an even byte offset into the pointer table: ; ; $00,$02,$04,$06,$08 ;============================================================================== DispatchEntityMovementState: ldy $B8,x lda EntityMovementStateHandlerTable,y sta $10 lda EntityMovementStateHandlerTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D953-$D95C: Gameplay Entity Transition-State Table ; ; Local: ; Five 16-bit handlers indexed directly by the entity primary state stored ; in $B8,X. ; ; Global: ; Defines the per-entity behavior used by ; DispatchEntityMovementState. ; ; Notes: ; States $00, $02, $04 and $08 all use the same handler at $D95D. ; ; State $06 reuses DispatchEntitySecondaryState at $D5A1. ;============================================================================== EntityMovementStateHandlerTable: .word EntityMovementStateNoOp ; state $00 .word EntityMovementStateNoOp ; state $02 .word EntityMovementStateNoOp ; state $04 .word DispatchEntitySecondaryState ; state $06 .word EntityMovementStateNoOp ; state $08 ;============================================================================== ; $D95D: No-Op Entity Transition Handler ; ; Local: ; Returns immediately. ; ; Global: ; Used by EntityMovementStateHandlerTable for primary entity states ; $00, $02, $04 and $08. ;============================================================================== EntityMovementStateNoOp: rts ;============================================================================== ; $D95E-$D979: Check Entity Transition State ; ; Local: ; Scans the four primary entity-state entries at: ; ; $B8,$BA,$BC,$BE ; ; If any entity is in primary state $06, stores the current A value ; ($06 from the CMP) into $0608 and returns. ; ; If no entity is in state $06, tests $88. When $88 is nonzero, stores that ; value into $0609 before returning. ; ; Global: ; Called during normal gameplay processing. Exports entity/state conditions ; to RAM $0608/$0609 for use by other gameplay systems. ; ; Notes: ; $00 is initialized but not subsequently used inside this routine in the ; decoded path. Preserve the write because another overlapping entry or ; external expectation may depend on it. ;============================================================================== UpdateEntityStatusFlags: ldx #$00 stx $00 @D962: lda $B8,x cmp #$06 bne @D96C sta $0608 rts @D96C: inx inx cpx #$08 bne @D962 lda $88 beq UpdatePlayerVerticalRegionFlag sta $0609 rts ;============================================================================== ; $D97A-$D98D: Update Gameplay Range Flag ;============================================================================== UpdatePlayerVerticalRegionFlag: ldy #$00 lda $6A cmp #$88 bcs @D988 iny cmp #$42 bcs @D988 iny @D988: lda #$01 sta $060A,y rts ;============================================================================== ; $D98E-$D9C5: Update Direction-Dependent Gameplay Value ; ; Local: ; Builds a base value from the current direction $51, then selects an ; additional offset based on coordinate alignment, tile state, and $B7. ; ; The resulting value is stored in $32. ; ; Notes: ; Address-derived labels preserve the original compact control flow. ;============================================================================== UpdatePlayerDirectionAnimation: lda $51 asl clc adc #$02 sta $00 lda $1A ora $1C and #$07 bne @D9AE ldy $51 lda $022B,y and #$F0 beq @D9AE lda $B7 and #$03 jmp @D9B2 @D9AE: inc $B7 lda $B7 @D9B2: and #$07 cmp #$06 bcc @D9BC lda #$01 bne @D9C3 @D9BC: tay lda PlayerDirectionAnimationOffsets,y clc adc $00 @D9C3: sta $32 rts ;============================================================================== ; $D9C6-$D9CB: Direction-Value Offset Table ; ; Local: ; Six values indexed by $B7 & $07 when that result is less than six. ; ; Global: ; Provides the small offset added to the direction-derived base before the ; result is stored in $32. ;============================================================================== PlayerDirectionAnimationOffsets: .byte $00,$00,$00,$01,$01,$00 ;============================================================================== ; $D9CC-$D9EA: Update Four Entity Display/State Values ; ; Local: ; Initializes: ; ; X = $00 ; $00 = $00 or $01 depending on frame-counter bit 3 ; $01 = $01 ; ; Then iterates the four entity slots: ; ; X = $00,$02,$04,$06 ; ; For each entity: ; - Calls the dispatcher at $D9EB. ; - Shifts $01 left once, producing per-entity masks: ; ; $01,$02,$04,$08 ; ; - Advances X by two. ; ; Global: ; Called from several game-mode states and during normal gameplay. Provides ; per-frame updates for all four gameplay entities, with an alternating ; phase selected from frame-counter bit 3. ; ; Notes: ; $4B is the NMI-driven frame counter. ; $00 is a two-phase animation/control value. ; $01 is the current entity bitmask. ;============================================================================== UpdateEntitySpriteStates: lda #$00 tax sta $00 lda $4B and #$08 beq @D9DC lda #$01 sta $00 @D9DC: lda #$01 sta $01 @D9E0: jsr $D9EB asl $01 inx inx cpx #$08 bne @D9E0 rts ;============================================================================== ; $D9EB-$D9F9: Per-Entity Display/State Dispatcher ;============================================================================== DispatchEntitySpriteState: ldy $B8,x lda EntitySpriteStateHandlerTable,y sta $10 lda EntitySpriteStateHandlerTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $D9FA-$DA03: Entity Display/State Table ;============================================================================== EntitySpriteStateHandlerTable: .word UpdateNormalEntitySprite ; state $00 .word UpdateNormalEntitySprite ; state $02 .word UpdateNormalEntitySprite ; state $04 .word UpdateSpecialEntitySprite ; state $06 .word $D9EA ; state $08 -> shared RTS ;============================================================================== ; $DA04-$DA3F: Entity Display/State Value Handlers ; ; Local: ; Three entry points used by EntitySpriteStateHandlerTable: ; ; $DA21 primary states $00/$02/$04 ; $DA2D primary state $06 ; ; $DA04 is shared calculation code entered by those handlers. ; ; Each path computes a value from the entity secondary state $B9,X, combines ; it with a base value, and stores the final result into one of $33-$36 ; according to the current entity index X. ; ; Global: ; Produces per-entity display/state values during UpdateEntitySpriteStates. ; ; Notes: ; X is an even entity index ($00,$02,$04,$06). TXA/LSR converts this into ; the ordinal entity index 0-3 for the final store at $33,Y. ;============================================================================== ;============================================================================== ; $DA04-$DA3F: Entity Display/State Value Handlers ; ; Local: ; Shared calculations used by the per-entity display/state dispatcher. ; ; Global: ; UpdateNormalEntitySprite handles primary states $00/$02/$04. ; UpdateSpecialEntitySprite handles primary state $06. ; ; Notes: ; Non-local labels are intentional because multiple handler entry points ; branch backward into shared code. ;============================================================================== ;------------------------------------------------------------------------------ ; $DA04 ;------------------------------------------------------------------------------ CalculateEntitySpriteValue: lda $B9,x sta $03 lda #$0A ;------------------------------------------------------------------------------ ; $DA0A ;------------------------------------------------------------------------------ CalculateEntitySpriteValueLoop: dec $03 bmi AddEntitySpriteBase clc adc #$02 bne CalculateEntitySpriteValueLoop ;------------------------------------------------------------------------------ ; $DA13 ;------------------------------------------------------------------------------ AddEntitySpriteBase: clc adc $00 sta $02 ;------------------------------------------------------------------------------ ; $DA18 ;------------------------------------------------------------------------------ StoreEntitySpriteValue: txa lsr tay lda $02 sta $0033,y rts ;------------------------------------------------------------------------------ ; $DA21 ; Primary entity states $00/$02/$04. ;------------------------------------------------------------------------------ UpdateNormalEntitySprite: lda $01 and $88 beq CalculateEntitySpriteValue lda #$1E sta $02 bne AddEntitySpriteBase ;------------------------------------------------------------------------------ ; $DA2D ; Primary entity state $06. ;------------------------------------------------------------------------------ UpdateSpecialEntitySprite: lda $B9,x sta $03 lda #$20 ;------------------------------------------------------------------------------ ; $DA33 ;------------------------------------------------------------------------------ UpdateSpecialEntitySpriteLoop: dec $03 bmi UpdateSpecialEntitySpriteDone clc adc #$01 bne UpdateSpecialEntitySpriteLoop ;------------------------------------------------------------------------------ ; $DA3C ;------------------------------------------------------------------------------ UpdateSpecialEntitySpriteDone: sta $02 bmi StoreEntitySpriteValue ;============================================================================== ; $DA40-$DAF0: Gameplay Entity Collision / Slot Exchange ; ; Local: ; Copies $24 bytes from zero-page $1A-$3D to RAM $0274-$0297. ; ; Then initializes an indirect pointer to $0284 and scans entity structures ; while testing them against the primary coordinate pair at $0274/$0276. ; ; For each enabled entity, computes absolute coordinate differences. If both ; axes are within $19 and their combined distance is below $10, exchanges ; coordinate and associated state fields between the primary slot and the ; selected entity slot. ; ; If no qualifying overlap is found, advances the structure pointer backward ; by four bytes, shifts the per-entity mask, and continues. ; ; Global: ; Called in several game-mode states and during normal gameplay. Maintains ; the mirrored gameplay/object state in $0274+ and handles close-overlap ; exchanges between entity slots. ; ; Notes: ; $00/$01 form a temporary pointer, initially $0284. ; $02 counts entity slots. ; $03 is a bitmask, initially $08. ; $04-$06 are temporary coordinate/state values during a swap. ; ; $DAF1 is a separate routine and is not included in this block. ;============================================================================== CheckEntityCollisions: ldx #$23 @CopyWorkingState: lda $1A,x sta $0274,x dex bpl @CopyWorkingState lda #$84 sta $00 lda #$02 sta $01 lda #$08 sta $03 lda #$03 sta $02 @CheckNextEntity: lda $03 and $88 bne @AdvanceEntity ldy #$00 lda $0274 cmp ($00),y bcs @FirstAxisReverse lda ($00),y sec sbc $0274 bcs @CheckFirstAxis @FirstAxisReverse: sbc ($00),y @CheckFirstAxis: cmp #$19 bcs @AdvanceEntity sta $04 ldy #$02 lda $0276 cmp ($00),y bcs @SecondAxisReverse lda ($00),y sec sbc $0276 bcs @CheckSecondAxis @SecondAxisReverse: sbc ($00),y @CheckSecondAxis: cmp #$19 bcs @AdvanceEntity adc $04 cmp #$10 bcs @AdvanceEntity ; Save the primary slot coordinates/state. lda $0274 sta $03 lda $0276 sta $04 lda $028C sta $05 lda $0292 sta $06 ; Copy selected entity coordinates into the primary slot. ldy #$00 lda ($00),y sta $0274 lda $03 sta ($00),y ldy #$02 lda ($00),y sta $0276 lda $04 sta ($00),y ; Exchange the parallel status values. ldx $02 lda $028D,x sta $028C lda $05 sta $028D,x lda $0293,x sta $0292 lda $06 sta $0293,x jmp $DAEB @AdvanceEntity: lda $00 sec sbc #$04 sta $00 lsr $03 dec $02 bmi @NoCollision jmp @CheckNextEntity @NoCollision: jsr $E1DA jmp $DAF1 ;============================================================================== ; $DAF1-$DBEE: Rebuild Gameplay Entity Render Data ; ; Local: ; Builds four-byte render/object records in page $07 from mirrored gameplay ; state beginning at $0274. ; ; Working variables: ; ; $00/$01 = source pointer, initially $0274 ; $02/$03 = destination pointer, initially $0700 ; $04 = source-group index ; $05 = record/lookup index ; $06 = temporary lookup index ; ; X advances through even entity offsets $00,$02,$04,$06. ; ; Encoded state values at $028C+ determine which lookup-table path is used ; to construct bytes 1 and 2 of each four-byte output record. ; ; Global: ; Called by several gameplay paths to rebuild the page-$07 representation ; used by the rendering/object pipeline. ; ; Notes: ; Several ROM tables referenced here remain undecoded: ; ; $DBEE ; $DCEE ; $DD22 ; EntitySpriteAttributeTable0 ; EntitySpriteCoordinateOffsets ; $DE57 ; ; $DBEE is particularly interesting because it is also the RTS terminating ; this routine. The ROM intentionally uses that address as both executable ; code and the base of a later indexed lookup. ;============================================================================== BuildEntitySpriteBuffer: lda #$74 sta $00 lda #$02 sta $01 lda #$00 sta $02 lda #$07 sta $03 lda #$00 sta $04 ;------------------------------------------------------------------------------ ; $DB05 ; Start/restart one source group. ;------------------------------------------------------------------------------ BuildSpriteBufferNextGroup: lda #$00 sta $05 tax ;------------------------------------------------------------------------------ ; $DB0A ; Build one four-byte render record. ;------------------------------------------------------------------------------ BuildSpriteBufferNextSprite: ldy #$02 lda ($00),y bne AdjustSpriteY lda #$FF bne StoreSpriteY ;------------------------------------------------------------------------------ ; $DB14 ;------------------------------------------------------------------------------ AdjustSpriteY: clc adc EntitySpriteCoordinateOffsets,x ;------------------------------------------------------------------------------ ; $DB18 ;------------------------------------------------------------------------------ StoreSpriteY: ldy #$00 sta ($02),y ldy $04 lda $028C,y asl rol rol and #$03 beq BuildSpriteClass0 cmp #$02 beq BuildSpriteClass2 bcc BuildSpriteClass1 ;------------------------------------------------------------------------------ ; $DB2E ; Status-class path. ;------------------------------------------------------------------------------ lda $028C,y asl asl clc adc $05 tay sta $06 lda $DD22,y ldy #$01 sta ($02),y ldy $04 lda $0292,y ldy $06 ora EntitySpriteCoordinateOffsets,y ldy #$02 sta ($02),y jmp BuildSpriteX ;------------------------------------------------------------------------------ ; $DB51 ;------------------------------------------------------------------------------ BuildSpriteClass0: lda $028C,y asl asl clc adc $05 tay sta $06 lda $DBEE,y ldy #$01 sta ($02),y ldy $04 lda $0292,y ldy $06 ora $DD22,y ldy #$02 sta ($02),y jmp BuildSpriteX ;------------------------------------------------------------------------------ ; $DB74 ;------------------------------------------------------------------------------ BuildSpriteClass1: lda $028C,y asl asl clc adc $05 tay sta $06 lda $DCEE,y ldy #$01 sta ($02),y ldy $04 lda $0292,y ldy $06 ora $DE22,y ldy #$02 sta ($02),y jmp BuildSpriteX ;------------------------------------------------------------------------------ ; $DB97 ;------------------------------------------------------------------------------ BuildSpriteClass2: lda $028C,y asl asl clc adc $05 tay sta $06 lda $DD22,y ldy #$01 sta ($02),y ldy $04 lda $0292,y ldy $06 ora EntitySpriteCoordinateOffsets,y ldy #$02 sta ($02),y ;------------------------------------------------------------------------------ ; $DBB7 ; Construct byte 3 of the render record. ;------------------------------------------------------------------------------ BuildSpriteX: ldy #$00 lda ($00),y bne AdjustSpriteX lda #$FF bne StoreSpriteX ;------------------------------------------------------------------------------ ; $DBC0 ;------------------------------------------------------------------------------ AdjustSpriteX: clc adc EntitySpriteCoordinateOffsets+1,x ;------------------------------------------------------------------------------ ; $DBC4 ;------------------------------------------------------------------------------ StoreSpriteX: ldy #$03 sta ($02),y ; Advance destination by one 4-byte record. lda $02 clc adc #$04 sta $02 inc $05 inx inx cpx #$08 beq BuildSpriteBufferNextSource jmp BuildSpriteBufferNextSprite BuildSpriteBufferNextSource: lda $00 clc adc #$04 sta $00 inc $04 lda $04 cmp #$06 beq BuildSpriteBufferDone jmp BuildSpriteBufferNextGroup BuildSpriteBufferDone: rts ;============================================================================== ; $DBEE-$DC3F: Gameplay Entity Render Lookup Data ; ; Local: ; Lookup data used by BuildEntitySpriteBuffer. ; ; The routine constructs an index from: ; ; ($028C,Y * 4) + $05 ; ; and uses that value to index this region for render-facing object data. ; ; Global: ; Supplies tile/state values used when rebuilding the four-byte page-$07 ; entity records. ; ; Notes: ; The region contains strongly patterned groups of repeated values, but the ; complete set of consumers has not yet been decoded. Keep this as one ; contiguous table during the first pass rather than assigning speculative ; semantic subdivisions. ;============================================================================== EntitySpriteTileTable0: ; $DBEE-$DBFD .byte $4C,$4C,$4C,$4C .byte $00,$00,$00,$00 .byte $04,$04,$03,$03 .byte $08,$08,$07,$07 ; $DBFE-$DC0D .byte $02,$01,$02,$01 .byte $06,$05,$06,$05 .byte $03,$03,$04,$04 .byte $07,$07,$08,$08 ; $DC0E-$DC1D .byte $01,$02,$01,$02 .byte $05,$06,$05,$06 .byte $18,$18,$19,$19 .byte $18,$18,$1A,$1A ; $DC1E-$DC2D .byte $1B,$1C,$1D,$1F .byte $1B,$1C,$1E,$20 .byte $21,$21,$22,$22 .byte $21,$21,$23,$23 ; $DC2E-$DC3D .byte $1C,$1B,$1F,$1D .byte $1C,$1B,$20,$1E .byte $00,$00,$00,$00 .byte $09,$09,$0A,$0A ; $DC3E-$DC3F .byte $0B,$0B ;============================================================================== ; $DC40-$DCED: Gameplay Entity Render Lookup Data, Continued ; ; Local: ; Additional render/tile lookup values associated with the table beginning ; at $DBEE. ; ; The data is strongly grouped into repeated pairs and four-byte patterns, ; consistent with direction/animation variants used by the four-byte entity ; render records. ; ; Global: ; Part of the graphics-facing lookup data used when gameplay state is ; converted into page-$07 render records. ; ; Notes: ; Keep these bytes as a contiguous data block for now. $DCEE is separately ; referenced by BuildEntitySpriteBuffer and therefore marks the next ; known lookup-table boundary. ;============================================================================== EntityRenderLookupDC40: ; $DC40-$DC4F .byte $0C,$0C,$4C,$4C .byte $0D,$0D,$4C,$4C .byte $0E,$0E,$4C,$4C .byte $0F,$0F,$4C,$4C ; $DC50-$DC5F .byte $10,$10,$4C,$4C .byte $11,$11,$4C,$4C .byte $12,$12,$4C,$4C .byte $13,$13,$14,$15 ; $DC60-$DC6F .byte $16,$17,$4C,$4C .byte $4C,$4C,$24,$24 .byte $25,$25,$24,$24 .byte $26,$26,$27,$27 ; $DC70-$DC7F .byte $4C,$4C,$28,$29 .byte $2A,$2B,$2C,$2C .byte $2D,$2D,$29,$28 .byte $2B,$2A,$90,$91 ; $DC80-$DC8F .byte $92,$93,$94,$95 .byte $96,$97,$98,$99 .byte $9A,$9B,$9C,$9D .byte $9E,$9F,$A0,$A1 ; $DC90-$DC9F .byte $A2,$A3,$A4,$A5 .byte $A6,$A7,$A8,$A9 .byte $AA,$AB,$AC,$AD .byte $AE,$AF,$2E,$2F ; $DCA0-$DCAF .byte $30,$31,$32,$2F .byte $33,$31,$34,$2F .byte $35,$31,$36,$2F .byte $37,$31,$38,$2F ; $DCB0-$DCBF .byte $39,$31,$3A,$2F .byte $3B,$31,$3C,$2F .byte $3D,$31,$3E,$3F .byte $40,$41,$42,$3F ; $DCC0-$DCCF .byte $43,$41,$44,$45 .byte $46,$47,$48,$45 .byte $49,$47,$4A,$45 .byte $4B,$47,$4D,$4E ; $DCD0-$DCDF .byte $4F,$50,$4E,$4D .byte $50,$4F,$4F,$50 .byte $4D,$4E,$50,$4F .byte $4E,$4D,$4D,$4E ; $DCE0-$DCED .byte $4F,$51,$4E,$52 .byte $53,$54,$4F,$51 .byte $4D,$4E,$53,$54 .byte $4E,$52 ;============================================================================== ; $DCEE-$DD21: Gameplay Entity Render Lookup Table ; ; Local: ; Lookup values used by BuildEntitySpriteBuffer in the status-class ; path beginning at $DB74. ; ; The index is constructed from: ; ; ($028C,Y * 4) + $05 ; ; and the selected byte is written as byte 1 of the generated page-$07 ; render record. ; ; Global: ; Supplies graphics/tile values for one class of gameplay entity render ; states. ; ; Notes: ; The repeated $4C values and four-byte groupings strongly suggest ; animation/direction variants. Keep the table as raw values during this ; first pass. ; ; $DD22 is separately referenced by the render-data builder and therefore ; marks the next lookup-table boundary. ;============================================================================== EntitySpriteTileTable2: ; $DCEE-$DCFD .byte $55,$4C,$56,$4C .byte $56,$4C,$55,$4C .byte $4C,$57,$4C,$58 .byte $4C,$59,$4C,$5A ; $DCFE-$DD0D .byte $4C,$5B,$4C,$5C .byte $4C,$4C,$4C,$5D .byte $18,$18,$19,$5E .byte $60,$61,$19,$5E ; $DD0E-$DD1D .byte $1B,$1C,$1D,$62 .byte $1B,$1C,$1E,$63 .byte $64,$65,$66,$67 .byte $64,$65,$68,$69 ; $DD1E-$DD21 .byte $6A,$6B,$4C,$4C ;============================================================================== ; $DD22-$DE21: Gameplay Entity Render Attribute Lookup ; ; Local: ; 256-byte lookup table used by BuildEntitySpriteBuffer. ; ; Entries are predominantly: ; ; $00 ; $40 ; $80 ; $C0 ; ; These values occupy bits 6-7 and are ORed with the corresponding value ; from $0292+ before being written as byte 2 of a page-$07 four-byte render ; record. ; ; Global: ; Supplies upper-bit render attributes for gameplay entities across their ; various states, animation phases and directional variants. ; ; Notes: ; The table is exactly $100 bytes long: ; ; $DD22-$DE21 ; ; $DE22 is independently referenced by another render-data path and therefore ; marks the next table boundary. ;============================================================================== EntitySpriteAttributeTable0: ; $DD22-$DD31 .byte $00,$00,$00,$00 .byte $00,$40,$80,$C0 .byte $80,$C0,$80,$C0 .byte $80,$C0,$80,$C0 ; $DD32-$DD41 .byte $00,$00,$80,$80 .byte $00,$00,$80,$80 .byte $00,$40,$00,$40 .byte $00,$40,$00,$40 ; $DD42-$DD51 .byte $40,$40,$C0,$C0 .byte $40,$40,$C0,$C0 .byte $00,$40,$00,$40 .byte $00,$40,$00,$40 ; $DD52-$DD61 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$40,$00,$40 .byte $00,$40,$00,$40 ; $DD62-$DD71 .byte $40,$40,$40,$40 .byte $40,$40,$40,$40 .byte $00,$40,$80,$C0 .byte $00,$40,$00,$40 ; $DD72-$DD81 .byte $00,$40,$00,$40 .byte $00,$00,$00,$40 .byte $00,$00,$00,$40 .byte $00,$00,$00,$40 ; $DD82-$DD91 .byte $00,$00,$00,$40 .byte $00,$00,$00,$40 .byte $00,$00,$00,$40 .byte $00,$00,$00,$40 ; $DD92-$DDA1 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$40,$00,$40 .byte $00,$40,$00,$40 ; $DDA2-$DDB1 .byte $00,$40,$00,$00 .byte $00,$00,$00,$00 .byte $00,$40,$00,$40 .byte $40,$40,$40,$40 ; $DDB2-$DDC1 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 ; $DDC2-$DDD1 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 ; $DDD2-$DDE1 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 ; $DDE2-$DDF1 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 ; $DDF2-$DE01 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 ; $DE02-$DE11 .byte $00,$00,$00,$00 .byte $40,$40,$00,$40 .byte $80,$00,$80,$80 .byte $00,$C0,$C0,$C0 ; $DE12-$DE21 .byte $00,$00,$00,$00 .byte $40,$00,$00,$00 .byte $80,$80,$80,$80 .byte $80,$80,$C0,$80 ;============================================================================== ; $DE22-$DE55: Secondary Gameplay Entity Render Attribute Lookup ; ; Local: ; Lookup data used by BuildEntitySpriteBuffer in the path beginning ; at $DB74. ; ; Values from this table are ORed with the corresponding state value from ; $0292+ and written as byte 2 of a four-byte page-$07 render record. ; ; Global: ; Supplies upper-bit render attributes for one class of gameplay entity ; render states. ; ; Notes: ; As with the larger table at $DD22, the significant nonzero values occupy ; the upper bits, primarily $40 and $80. ; ; EntitySpriteCoordinateOffsets is separately referenced as another ; lookup-table base and therefore marks the next known boundary. ;============================================================================== EntitySpriteAttributeTable1: ; $DE22-$DE31 .byte $00,$00,$00,$00 .byte $80,$00,$80,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 ; $DE32-$DE41 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$40,$00,$00 .byte $00,$00,$00,$00 ; $DE42-$DE51 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 .byte $00,$00,$00,$00 ; $DE52-$DE55 .byte $00,$00,$00,$00 ;============================================================================== ; $DE56-$DE5D: Entity Render Coordinate Adjustment Table ; ; Local: ; Eight coordinate-adjustment bytes used by ; BuildEntitySpriteBuffer. ; ; X takes even values $00,$02,$04,$06, and the routine reads: ; ; EntitySpriteCoordinateOffsets,X ; EntitySpriteCoordinateOffsets+1,X ; ; giving four byte pairs. ; ; Global: ; Adjusts source coordinates when building page-$07 four-byte render ; records. ;============================================================================== EntitySpriteCoordinateOffsets: .byte $03,$F4 .byte $03,$FC .byte $0B,$F4 .byte $0B,$FC ;============================================================================== ; $DE5E-$DE7D: Periodic Four-Entry State Update ; ; Local: ; Once every 16 frames, updates the four entries at $6C-$6F: ; ; $07 -> remains $07 ; $01 -> becomes $02 ; anything else -> becomes $01 ;============================================================================== UpdatePeriodicFourEntryState: lda $4B and #$0F beq @DE65 rts @DE65: tax @DE66: lda $6C,x cmp #$07 beq @DE76 cmp #$01 bne @DE74 lda #$02 bne @DE76 @DE74: lda #$01 @DE76: sta $6C,x inx cpx #$04 bne @DE66 rts ;============================================================================== ; $DE7E-$DF6B: Gameplay PPU / Display Update Processor ; ; Local: ; Processes several gameplay display-update buffers and writes their data ; directly to the PPU. ; ; Handles: ; - Six-byte update block at $023F using destination $026B/$026C. ; - Six-byte update block at $0245 using destination $0272/$0273. ; - Three-byte periodic update at $026F-$0271. ; - Four state values at $6C-$6F written to PPU addresses in the table ; beginning at GameplayStatusPpuAddressTable. ; - Generic PPU command stream beginning at $024B. ; ; Global: ; Central gameplay display-update routine. ; ; Notes: ; UpdateAnimatedGameplayPpuFields at $DF15 is a shared entry point reached from ; several earlier control-flow paths and is therefore a global label rather ; than a ca65 cheap-local label. ;============================================================================== FlushGameplayPpuUpdates: lda $48 beq @DE85 jmp UpdateAnimatedGameplayPpuFields ;------------------------------------------------------------------------------ ; $DE85 ; Process the update block beginning at $023F. ;------------------------------------------------------------------------------ @DE85: lda $023F cmp #$FF beq @DEB5 lda PPUSTATUS lda $026B sta PPUADDR lda $026C sta PPUADDR ldy #$00 @DE9B: lda $023F,y sta PPUDATA iny cpy #$06 bne @DE9B lda #$30 sta PPUDATA lda #$FF sta $023F bne @DEE2 ;------------------------------------------------------------------------------ ; $DEB5 ; Process the update block beginning at $0245. ;------------------------------------------------------------------------------ @DEB5: lda $0245 cmp #$FF beq @DEE2 lda PPUSTATUS lda $0272 sta PPUADDR lda $0273 sta PPUADDR ldy #$00 @DECB: lda $0245,y sta PPUDATA iny cpy #$06 bne @DECB lda #$30 sta PPUDATA lda #$FF sta $0245 ;------------------------------------------------------------------------------ ; $DEE2 ; Periodic three-byte display update. ;------------------------------------------------------------------------------ @DEE2: lda $4B and #$07 bne UpdateAnimatedGameplayPpuFields lda PPUSTATUS lda $026D sta PPUADDR lda $026E sta PPUADDR ldx #$00 lda $4B and #$18 beq @DF05 @DEFB: lda $026F,x sta PPUDATA inx cpx #$03 bne @DEFB beq UpdateAnimatedGameplayPpuFields @DF05: lda #$20 @DF07: sta PPUDATA inx cpx #$03 bne @DF07 ;============================================================================== ; $DF15: Shared frame-dependent display update entry ;============================================================================== UpdateAnimatedGameplayPpuFields: lda $4B and #$01 beq @DF42 ldy #$00 ldx #$00 lda $46 and $47 beq @DF27 ldy #$08 @DF27: lda PPUSTATUS lda GameplayStatusPpuAddressTable,y sta PPUADDR lda GameplayStatusPpuAddressTable+1,y sta PPUADDR lda $6C,x sta PPUDATA iny iny inx cpx #$04 bne @DF27 ;------------------------------------------------------------------------------ ; $DF42 ; Process the $024B command stream. ;------------------------------------------------------------------------------ @DF42: lda PPUSTATUS ldy #$FF @DF46: iny lda $024B,y cmp #$FF beq @DF66 sta PPUADDR iny lda $024B,y sta PPUADDR @DF55: iny lda $024B,y beq @DF46 cmp #$FF beq @DF66 sta PPUDATA bne @DF55 @DF66: sta $024B rts ;============================================================================== ; $DF6C-$DF7B: Gameplay PPU Address Table ; ; Local: ; Eight PPU addresses stored in high-byte/low-byte order. ; ; When ($46 & $47) == 0, the first four addresses are used. ; Otherwise the second four addresses are used. ; ; Values from $6C-$6F are written to the selected addresses. ; ; Notes: ; These must remain .byte pairs. Using .word would reverse each address ; because ca65 emits words little-endian. ;============================================================================== GameplayStatusPpuAddressTable: .byte $20,$B4 .byte $20,$A2 .byte $22,$D4 .byte $22,$C2 .byte $28,$B4 .byte $28,$A2 .byte $2A,$D4 .byte $2A,$C2 ;============================================================================== ; $DF7C-$E042: Gameplay Tile / State Update ; ; Local: ; Processes selected values of $022A, updates $6C-$6F, emits a command into ; the $024B display-command buffer, updates $6A and related gameplay state, ; and may transfer control to $E0E6. ; ; Global: ; Called during the normal gameplay frame-update chain. ; ; Notes: ; $E043 and $E044 are lookup-table bases used near the end of this routine. ; Those bytes are data and begin immediately after the final JMP at $E040. ;============================================================================== UpdateGameplayTileState: ldx #$00 lda $022A cmp #$09 beq $DFBB inx inx cmp #$03 beq $DFBB cmp #$01 beq @DF94 cmp #$02 beq @DF94 rts @DF94: jsr $E063 lda $1A sta $02 lda $1C sta $03 jsr $E263 ldx #$00 lda $03 and #$07 beq @DFAD inx inx @DFAD: lda $02 and #$0F cmp #$04 beq @DFB6 inx @DFB6: lda #$07 sta $6C,x @DFBB: ldx #$04 lda #$00 sta $D5 sta $D6 lda $1A sta $02 lda $1C sta $03 jsr $E263 ldy #$FF @DFCE: iny lda $024B,y cmp #$FF bne @DFCE tya beq @DFDC lda #$00 sta $024B,y iny @DFDC: lda $03 sta $024B,y iny lda $02 sta $024B,y iny lda $E043,x sta $024B,y iny lda #$FF sta $024B,y lda #$07 sta $022A lda $E044,x sta $DC dec $6A bne @E012 lda #$0C sta $3F lda #$00 sta $87 lda #$48 sta $4C @E012: lda $6A cmp #$37 beq @E01C cmp #$86 bne $E036 @E01C: lda #$0A sta $D7 lda #$3C sta $D8 ldy $93 lda GameplayStateValueLookup,y sta $37 lda #$03 sta $3D lda #$60 sta $2E lda #$80 sta $30 lda $6A and #$01 tay lda #$01 sta $0604,y jmp $E0E6 ;============================================================================== ; $E043-$E048: Gameplay Tile-State Lookup ; ; Local: ; Used by UpdateGameplayTileState through overlapping indexed reads at ; $E043,X and $E044,X. ;============================================================================== TileEventDisplayLookup: .byte $08,$01,$07,$01,$07,$05 ;============================================================================== ; $E049-$E05A: Activate First Available State Slot ; ; Local: ; Scans the three even-indexed entries: ; ; $BA,$BC,$BE ; ; for the first zero value. If one is found, changes it to $02 and returns. ; If all three are already active/nonzero, returns without modification. ;============================================================================== ActivateNextStateSlotE049: ldx #$00 @E04B: lda $BA,x bne @E054 lda #$02 sta $BA,x rts @E054: inx inx cpx #$06 bne @E04B rts ;============================================================================== ; $E05B-$E062: Gameplay State Value Lookup ; ; Local: ; Eight sequential values indexed by $93 and copied into $37 during ; gameplay-state transitions. ;============================================================================== GameplayStateValueLookup: .byte $24,$25,$26,$27,$28,$29,$2A,$2B ;============================================================================== ; $E063-$E0E5: Initialize / Adjust Entity Direction State ; ; Local: ; Initializes several gameplay variables and scans all four entity slots. ; ; Initialization: ; ; if $8C == 0: ; $8A = $1E ; ; $89 = $00 ; $88 = $0F ; $D9 = $00 ; ; The low two bits of $39-$3C are normalized to $01, preserving the upper ; six bits. ; ; Palette/update values are installed at: ; ; $02AE = $11 ; $02A9 = $0F ; ; Then the four entity structures are examined. For eligible entities in ; primary state $04, a candidate secondary direction is calculated from ; $B9,X and accepted when coordinate alignment and path-map checks permit it. ; ; Global: ; Called from UpdateGameplayTileState before that routine derives its ; direction/tile update. ; ; Notes: ; $00/$01 and $02/$03 are parallel indirect pointers. ; X advances through entity offsets $00,$02,$04,$06. ; $04 temporarily holds a candidate direction value. ;============================================================================== InitializeEntityDirectionState: lda $8C bne @E06D lda #$1E sta $8A lda #$00 @E06D: sta $89 lda #$0F sta $88 ldx #$03 @E075: lda $39,x and #$FC ora #$01 sta $39,x dex bpl @E075 lda #$11 sta $02AE lda #$0F sta $02A9 lda #$00 sta $D9 ldx #$00 lda #$2F sta $00 lda #$02 sta $01 lda #$1E sta $02 lda #$00 sta $03 @E09E: lda #$0F cmp $88 beq @E0AA lda $D2 bne @E0D1 @E0AA: lda $B8,x cmp #$04 bne @E0D1 lda $B9,x clc adc #$02 and #$03 sta $04 ldy #$00 lda ($02),y ldy #$02 ora ($02),y and #$07 bne @E0CD ldy $04 lda ($00),y and #$F8 bne @E0D1 @E0CD: lda $04 sta $B9,x @E0D1: lda $00 clc adc #$04 sta $00 lda $02 clc adc #$04 sta $02 inx inx cpx #$08 bne @E0AA rts ;============================================================================== ; $E0E6-$E1BF: Score / State Display Update ; ; Updates the six decimal-like values at $70-$75 using increments at $DC-$E1, ; converts the result into display characters at $023F, handles progression ; state associated with $6B/$73/$67, appends display commands to $024B, then ; compares and updates the saved values at $61-$66. ;============================================================================== UpdateScoreDisplay: lda $48 beq @E0EB rts ;------------------------------------------------------------------------------ ; $E0EB ; Add $DC-$E1 into the six values at $70-$75 using decimal carry handling. ;------------------------------------------------------------------------------ @E0EB: ldy #$00 lda #$06 sta $00 clc ;------------------------------------------------------------------------------ ; $E0F2 ;------------------------------------------------------------------------------ @E0F2: lda $70,y adc $DC,y sta $70,y cmp #$0A bcc @E105 sbc #$0A sta $70,y sec ;------------------------------------------------------------------------------ ; $E105 ; Clear the consumed increment and continue through all six entries. ;------------------------------------------------------------------------------ @E105: lda #$00 sta $DC,y iny dec $00 bne @E0F2 ;------------------------------------------------------------------------------ ; $E10F ; Convert the six numeric values into display/tile values. ;------------------------------------------------------------------------------ ldx #$00 dey ;------------------------------------------------------------------------------ ; $E112 ; Suppress leading zero-style values by replacing $30 with $20. ;------------------------------------------------------------------------------ @E112: lda $70,y jsr $E1CE cmp #$30 bne @E12B lda #$20 sta $023F,x inx dey bne @E112 ;------------------------------------------------------------------------------ ; $E125 ; Convert remaining digits without leading-space suppression. ;------------------------------------------------------------------------------ @E125: lda $70,y jsr $E1CE ;------------------------------------------------------------------------------ ; $E12B ;------------------------------------------------------------------------------ @E12B: sta $023F,x inx dey bpl @E125 ;------------------------------------------------------------------------------ ; $E132 ; Progression check. ;------------------------------------------------------------------------------ lda $6B bne @E196 lda #$01 cmp $73 bne @E196 lda #$01 sta $6B sta $0602 inc $67 lda $67 sec sbc #$02 asl sta $00 ;------------------------------------------------------------------------------ ; Locate the $FF terminator of the $024B display-command stream. ;------------------------------------------------------------------------------ ldy #$FF @E14D: iny lda $024B,y cmp #$FF bne @E14D tya beq @E15D lda #$00 sta $024B,y iny ;------------------------------------------------------------------------------ ; $E15D ; Select a display offset according to $46 & $47. ;------------------------------------------------------------------------------ @E15D: lda $46 and $47 beq @E165 lda #$08 @E165: sta $01 ldx #$00 ;------------------------------------------------------------------------------ ; $E169 ; Append two five-byte command templates from $E1C0. ;------------------------------------------------------------------------------ @E169: lda ScorePpuCommandTemplates,x clc adc $01 sta $024B,y iny inx lda ScorePpuCommandTemplates,x clc adc $00 sta $024B,y lda #$03 sta $02 @E182: iny inx lda ScorePpuCommandTemplates,x sta $024B,y dec $02 bne @E182 iny inx cpx #$0A bne @E169 ;------------------------------------------------------------------------------ ; $E196 ; Compare saved values $61-$66 with current values $70-$75. ;------------------------------------------------------------------------------ @E196: ldy #$05 @E198: lda $61,y cmp $70,y beq @E1A3 bcc @E1A7 rts @E1A3: dey bpl @E198 rts ;------------------------------------------------------------------------------ ; $E1A7 ; Copy display buffer $023F-$0244 to $0245-$024A. ;------------------------------------------------------------------------------ @E1A7: ldx #$00 @E1A9: lda $023F,x sta $0245,x inx cpx #$06 bne @E1A9 ;------------------------------------------------------------------------------ ; Copy current values $70-$75 into saved values $61-$66. ;------------------------------------------------------------------------------ ldx #$00 @E1B4: lda $70,x sta $61,x inx cpx #$06 bne @E1B4 rts ;============================================================================== ; $E1C0-$E1C9: Score Display Command Table ;============================================================================== ScorePpuCommandTemplates: .byte $23,$17,$3C,$3D,$00 .byte $23,$37,$3E,$3F,$FF ;============================================================================== ; $E1CA-$E1D9: Convert High Nibble to Display Character ; ; Input: ; A = packed numeric value ; ; Output: ; A = display character code ; ; Behavior: ; Uses the original high nibble: ; ; $0-$9 -> $30-$39 ; $A-$F -> $40-$45 ; ; Global: ; Called by UpdateScoreDisplay while building the display buffer at ; $023F. ;============================================================================== ConvertNibbleToDigitTile: lsr lsr lsr lsr and #$0F cmp #$0A bcs @HexDigit adc #$30 rts @HexDigit: adc #$36 rts ;============================================================================== ; $E1DA-$E262: Rebuild Gameplay Coordinate/Map Buffer ; ; Local: ; Converts the primary coordinate pair $1A/$1C through the helper at $E263 ; and stores the result at $0200/$0201. ; ; Then iterates through gameplay coordinate structures beginning at $1A, ; advancing X by four each iteration. ; ; For each iteration: ; - Converts the current $1A+X/$1C+X pair through $E263. ; - Initializes working 16-bit values in $12-$15. ; - Calls helpers at $E2D4 and $E2C6. ; - Emits two groups of four bytes into the buffer at $0202+. ; ; The output loop terminates when Y reaches $28. ; ; Global: ; Called after gameplay collision/entity-state changes to rebuild a ; 40-byte coordinate/map-facing buffer in page $02. ; ; Notes: ; $E263 is the next known helper and is already called elsewhere. ;============================================================================== BuildNearbyTileAddressBuffer: lda $1A sta $02 lda $1C sta $03 jsr $E263 lda $03 sta $0200 lda $02 sta $0201 ldx #$00 ldy #$00 @E1F2: lda $1A,x sta $02 lda $1C,x sta $03 jsr $E263 lda #$20 sta $14 lda #$00 sta $15 lda $02 sta $12 lda $03 sta $13 jsr $E2D4 lda $13 sta $0202,y lda $12 iny sta $0202,y lda $03 iny sta $0202,y lda $02 sec sbc #$01 iny sta $0202,y lda #$20 sta $14 lda #$00 sta $15 lda $02 sta $12 lda $03 sta $13 jsr $E2C6 lda $13 iny sta $0202,y lda $12 iny sta $0202,y lda $03 iny sta $0202,y lda $02 clc adc #$01 iny sta $0202,y inx inx inx inx iny cpy #$28 bne @E1F2 rts ;============================================================================== ; $E263-$E2A1: Convert Gameplay Coordinates to Map/PPU Address ; ; Input: ; $02 = first gameplay coordinate ; $03 = second gameplay coordinate ; ; Output: ; $02/$03 = converted 16-bit map/PPU-style address ; ; Local: ; Derives a nametable-style address from the coordinate pair. ; ; The second coordinate is adjusted by -4, aligned to an 8-unit boundary, ; then multiplied/shifted while carries are accumulated in $05. ; ; The first coordinate is adjusted by -4 and divided by eight before being ; added to the low address byte. ; ; The resulting address receives a base of $20xx. ; ; When ($46 & $47) is nonzero, $08 is added to the high/result byte at $03, ; selecting the corresponding alternate nametable region. ; ; Global: ; Called from several gameplay routines whenever object coordinates need to ; be converted into map/PPU addresses. ; ; Notes: ; $04/$05 are temporary low/high address components. ;============================================================================== ConvertPixelToNametableAddress: lda #$00 sta $05 lda $03 sec sbc #$04 and #$F8 asl rol $05 asl rol $05 clc adc #$40 sta $04 lda #$00 adc $05 sta $05 lda $02 sec sbc #$04 lsr lsr lsr clc adc $04 sta $02 lda $05 clc adc #$20 sta $03 lda $46 and $47 bne @E299 rts @E299: lda $03 clc adc #$08 sta $03 rts ;============================================================================== ; $E2A2-$E2C5: Read PPU Values for Coordinate Address Buffer ; ; Reads 21 PPU addresses stored at $0200-$0229 and stores the corresponding ; PPU data bytes at $022A-$023E. ; ; PPUSTATUS is read once before processing the address list. ;============================================================================== ReadNearbyNametableTiles: ldx #$00 ldy #$00 lda PPUSTATUS @E2A9: lda $0200,x sta PPUADDR inx lda $0200,x sta PPUADDR ; Buffered PPU read. lda PPUDATA ; Actual requested value. lda PPUDATA sta $022A,y inx iny cpx #$2A bne @E2A9 rts ;============================================================================== ; $E2C6-$E2D3: Add 16-Bit Working Values ; ; Input: ; $12/$13 = low/high first operand ; $14/$15 = low/high second operand ; ; Output: ; $12/$13 = 16-bit sum ;============================================================================== Add16BitWorkingValue: clc lda $12 adc $14 sta $12 lda $13 adc $15 sta $13 rts ;============================================================================== ; $E2D4-$E2E1: Subtract 16-Bit Working Values ; ; Input: ; $12/$13 = low/high first operand ; $14/$15 = low/high second operand ; ; Output: ; $12/$13 = first operand - second operand ;============================================================================== Subtract16BitWorkingValue: sec lda $12 sbc $14 sta $12 lda $13 sbc $15 sta $13 rts ;============================================================================== ; $E2E2-$E381: Draw Gameplay Nametable ; ; Local: ; Expands compressed gameplay map data directly into VRAM. ; ; The primary destination begins at: ; ; $2040 when ($47 & $46) == 0 ; $2840 when ($47 & $46) != 0 ; ; The compressed source pointer is loaded from $FFF6/$FFF7. ; ; After the primary map is written, a small decoration pattern from ; MazeNametableDecorationTiles is written to additional PPU locations. ; ; Global: ; Called from GameModeState00 while initializing gameplay. ; ; Notes: ; $00/$01 = compressed source pointer during the first phase ; $02/$03 = PPU destination during the first phase ; $04 = number of PPU bytes remaining in current row ; $05 = repeat count extracted from the source byte ; X = row counter ; Y = compressed source index ;============================================================================== DrawMazeNametable: lda #$20 sta $02 lda #$40 sta $03 lda $47 and $46 beq @E2F4 lda #$28 sta $02 ;------------------------------------------------------------------------------ ; $E2F4 ; Load compressed-map source pointer. ;------------------------------------------------------------------------------ @E2F4: lda MazeDataPointerBytes+1 sta $01 lda MazeDataPointerBytes sta $00 ldx #$1B ldy #$00 ;------------------------------------------------------------------------------ ; $E302 ; Set PPU destination for the next row. ;------------------------------------------------------------------------------ @E302: lda PPUSTATUS lda $02 sta PPUADDR lda $03 sta PPUADDR lda #$16 sta $04 ;------------------------------------------------------------------------------ ; $E313 ; Start decoding the next compressed source byte. ;------------------------------------------------------------------------------ @E313: lda #$00 sta $05 lda ($00),y asl rol $05 asl rol $05 lda ($00),y and #$3F ;------------------------------------------------------------------------------ ; $E321 ; Repeat the decoded value according to the repeat count in $05. ;------------------------------------------------------------------------------ @E321: sta PPUDATA dec $04 dec $05 bpl @E321 iny bne @E32F inc $01 ;------------------------------------------------------------------------------ ; $E32F ; Continue until the current row contains $16 output bytes. ;------------------------------------------------------------------------------ @E32F: lda $04 bne @E313 lda $03 clc adc #$20 sta $03 lda $02 adc #$00 sta $02 dex bne @E302 ;============================================================================== ; $E345-$E381: Draw additional nametable decoration strips. ;============================================================================== ldy #$02 lda #$21 sta $00 lda #$D6 sta $01 lda $47 and $46 beq @E359 lda #$29 sta $00 ;------------------------------------------------------------------------------ ; $E359 ; Start one decoration strip. ;------------------------------------------------------------------------------ @E359: ldx #$07 lda PPUSTATUS lda $00 sta PPUADDR lda $01 sta PPUADDR lda MazeNametableDecorationTiles,y ;------------------------------------------------------------------------------ ; $E36B ; Write the selected table value eight times. ;------------------------------------------------------------------------------ @E36B: sta PPUDATA dex bpl @E36B lda $01 clc adc #$20 sta $01 lda #$00 adc $00 sta $00 dey bpl @E359 rts ;============================================================================== ; $E382-$E385: Gameplay Nametable Decoration Table ;============================================================================== MazeNametableDecorationTiles: .byte $2D,$04,$2D ;============================================================================== ; $E385-$E3FE: Initialize Gameplay Nametable Regions ; ; Fills gameplay nametable regions with tile $2D, then clears the corresponding ; attribute-table regions. ; ; Address-based global labels are used for shared entry points to avoid ca65 ; cheap-local scope issues and collisions with previously defined symbols. ;============================================================================== ClearGameplayNametables: lda PPUSTATUS lda #$20 sta PPUADDR lda #$00 sta PPUADDR lda #$01 sta $02 ;------------------------------------------------------------------------------ ; $E396 ; Shared entry used for another nametable pass. ;------------------------------------------------------------------------------ ClearGameplayNametablesSecondPass: lda #$01 sta $03 ;------------------------------------------------------------------------------ ; $E39A ; Initialize fill counters. ;------------------------------------------------------------------------------ InitializeNametableFillCounters: lda #$03 sta $00 lda #$C0 sta $01 lda #$2D ;------------------------------------------------------------------------------ ; $E3A4 ; Shared fill loop. ;------------------------------------------------------------------------------ FillNametableByte: sta PPUDATA dec $01 bne FillNametableByte dec $00 bpl FillNametableByte dec $03 bne AdvanceNametableFillPass lda #$40 sta $01 lda #$00 beq FillNametableByte ;------------------------------------------------------------------------------ ; $E3BB ; Advance to alternate nametable / next phase. ;------------------------------------------------------------------------------ AdvanceNametableFillPass: dec $02 bne ClearGameplayAttributeTables lda PPUSTATUS lda #$28 sta PPUADDR lda #$00 sta PPUADDR jmp ClearGameplayNametablesSecondPass ;------------------------------------------------------------------------------ ; $E3CF ; Begin clearing attribute-table regions. ;------------------------------------------------------------------------------ ClearGameplayAttributeTables: lda #$01 sta $00 lda PPUSTATUS lda #$23 sta PPUADDR lda #$C0 sta PPUADDR ;------------------------------------------------------------------------------ ; $E3E0 ; Shared attribute-clear setup. ;------------------------------------------------------------------------------ BeginAttributeTableClear: lda #$00 tay ;------------------------------------------------------------------------------ ; $E3E3 ; Write $20 zero bytes to PPUDATA. ;------------------------------------------------------------------------------ ClearAttributeTableLoop: sta PPUDATA iny cpy #$20 bne ClearAttributeTableLoop dec $00 bne ClearSecondAttributeTable rts ;------------------------------------------------------------------------------ ; $E3F0 ; Set destination to the second attribute table, $2BC0. ;------------------------------------------------------------------------------ ClearSecondAttributeTable: lda PPUSTATUS lda #$2B sta PPUADDR lda #$C0 sta PPUADDR bne BeginAttributeTableClear ;============================================================================== ; $E3FF-$E473: Draw Gameplay Numeric / Status Fields ; ; Local: ; Configures a PPU destination and repeatedly draws numeric fields through ; the helper at DrawSixDigitScore. ; ; Initial destination/configuration: ; ; $00/$01 = PPU destination ; $04 = destination stride ; X = index into a pointer table at ScoreFieldSourcePointerTable ; ; The destination and table index vary depending on $47 & $46. ; ; Three fields are drawn per pass. The routine may perform another pass ; according to $47 and the current field counter. ; ; Global: ; Called by GameModeState00 as part of gameplay initialization. ; ; Notes: ; DrawSixDigitScore renders one numeric field using the source pointer ; loaded from the table at ScoreFieldSourcePointerTable. ;============================================================================== DrawGameplayScoreFields: lda #$20 sta $00 lda #$B6 sta $01 lda #$80 sta $04 ldx #$00 lda $47 and $46 beq @E429 lda #$28 sta $00 bne @E429 ;------------------------------------------------------------------------------ ; Alternate setup path. ;------------------------------------------------------------------------------ lda #$20 sta $00 lda #$83 sta $01 lda #$09 sta $04 lda #$01 sta $47 ldx #$0C ;------------------------------------------------------------------------------ ; $E429 ; Adjust lookup-table index for alternate player/mode state. ;------------------------------------------------------------------------------ @E429: lda $47 and $46 beq @E436 txa clc adc #$06 tax @E436: lda #$03 sta $05 ;------------------------------------------------------------------------------ ; $E43A: render one status field. ;------------------------------------------------------------------------------ @E43A: lda PPUSTATUS lda $00 sta PPUADDR lda $01 sta PPUADDR lda ScoreFieldSourcePointerTable,x sta $02 lda ScoreFieldSourcePointerTable+1,x sta $03 jsr DrawSixDigitScore inx inx lda $01 clc adc $04 sta $01 lda #$00 adc $00 sta $00 dec $05 lda $05 cmp #$01 bne @E46D lda $47 beq @E473 @E46D: lda $05 bne @E43A @E473: rts ;============================================================================== ; $E474-$E49E: Draw Six-Digit Numeric Field ; ; Input: ; $02/$03 = pointer to packed numeric values ; ; Local: ; Processes six source values from index 5 down to 0. ; ; Leading values whose low nibble is zero are rendered as spaces ($20). ; Once a nonzero low nibble is encountered, remaining values are converted ; through the shared conversion entry at $E1CE and written to PPUDATA. ; ; The final value at Y=0 is always converted, after which a trailing $30 ; character is written. ; ; Notes: ; The conversion calls MUST target $E1CE, not $E1CA. $E1CE is the shared ; entry that begins with AND #$0F, while $E1CA additionally shifts A right ; four times. ;============================================================================== DrawSixDigitScore: ldy #$05 SkipLeadingZeroDigits: lda ($02),y and #$0F bne ConvertCurrentScoreDigit lda #$20 sta PPUDATA dey bne SkipLeadingZeroDigits beq DrawFinalScoreDigit LoadNextScoreDigit: lda ($02),y ConvertCurrentScoreDigit: jsr $E1CE sta PPUDATA dey bne LoadNextScoreDigit DrawFinalScoreDigit: lda ($02),y jsr $E1CE sta PPUDATA lda #$30 sta PPUDATA rts ;============================================================================== ; $E49F-$E4B0: Gameplay Status Field Source Pointer Table ; ; Local: ; Nine little-endian zero-page/RAM pointers used by the gameplay status-field ; drawing routine beginning at $E3FF. ; ; X indexes this table in steps of two. Each selected address is loaded into ; $02/$03 and passed to DrawSixDigitScore. ; ; Global: ; Selects the numeric/state arrays rendered into the different gameplay ; status display fields. ;============================================================================== ScoreFieldSourcePointerTable: .word $0061 .word $0070 .word $0080 .word $0061 .word $0080 .word $0070 .word $0070 .word $0061 .word $0080 ;============================================================================== ; $E4B1-$E501: Build Entity Direction Candidate List ; ; Input: ; $00/$01 = pointer to direction/path data ; $0A/$0B = comparison values prepared by the caller ; X = current entity slot index ; ; Output: ; $0C-$0F = candidate direction values, with invalid entries marked $FF ; ; Local: ; First clears all four candidate entries to $FF. ; ; Then examines four values through ($00),Y. A candidate is accepted when ; either: ; ; value == $0A ; ; or: ; ; (value & $F8) != 0 ; and value == $0B ; ; Accepted entries store their Y index into $0C-$0F. ; ; The entity's reverse/current-excluded direction is then calculated from ; $B9,X and explicitly removed from the candidate list. ; ; Finally performs up to three passes compacting $FF holes by shifting the ; following entry left. ; ; Global: ; Called by EntityTargetCommon at $D778 before entity direction selection. ; ; Notes: ; $0C-$0F therefore form a filtered list of legal direction indices. ;============================================================================== BuildLegalDirectionList: ldy #$FF sty $0C sty $0D sty $0E sty $0F iny ScanDirectionCandidate: lda ($00),y cmp $0A beq AcceptDirectionCandidate and #$F8 beq AcceptDirectionCandidate cmp $0B bne NextDirectionCandidate AcceptDirectionCandidate: tya sta $000C,y NextDirectionCandidate: iny cpy #$04 bne ScanDirectionCandidate lda $B9,x clc adc #$02 and #$03 tay lda #$FF sta $000C,y lda #$03 sta $0B CompactDirectionListPass: ldy #$00 CompactDirectionListEntry: lda $000C,y cmp #$FF bne NextDirectionListEntry lda $000D,y sta $000C,y lda #$FF sta $000D,y NextDirectionListEntry: iny cpy #$03 bne CompactDirectionListEntry dec $0B bne CompactDirectionListPass rts ;============================================================================== ; $E502-$E53B: Draw Gameplay Initialization Data ; ; Local: ; Reads compact PPU command data beginning at $E53C and writes the payloads ; directly to PPUDATA. ; ; X selects between two passes/variants. ; Y indexes the command stream. ; $00 holds the number of bytes remaining in the current payload. ; ; Global: ; Called from GameModeState00 during gameplay initialization. ;============================================================================== DrawGameplayHudLabels: ldx #$01 ;------------------------------------------------------------------------------ ; $E504 ; Reset command-stream index for a pass. ;------------------------------------------------------------------------------ InitData_E504: ldy #$00 ;------------------------------------------------------------------------------ ; $E506 ; Start one command record. ;------------------------------------------------------------------------------ InitData_E506: lda $E53C,y cpx #$00 bne InitData_E50F adc #$07 ;------------------------------------------------------------------------------ ; $E50F ; Write PPU destination address. ;------------------------------------------------------------------------------ InitData_E50F: sta PPUADDR iny lda $E53C,y sta PPUADDR iny lda $E53C,y sta $00 ;------------------------------------------------------------------------------ ; $E51F ; Write record payload. ;------------------------------------------------------------------------------ InitData_E51F: iny lda $E53C,y sta PPUDATA dec $00 bne InitData_E51F iny cpy #$11 bne InitData_E534 lda $47 bne InitData_E534 rts ;------------------------------------------------------------------------------ ; $E534 ; Continue through the first/second stream section. ;------------------------------------------------------------------------------ InitData_E534: cpy #$17 bne InitData_E506 dex beq InitData_E504 rts ;============================================================================== ; $E53C-$E552: Gameplay Initialization PPU Command Data ; ; Record format: ; byte 0 = PPU address high ; byte 1 = PPU address low ; byte 2 = payload length ; remaining bytes = payload written to PPUDATA ;============================================================================== GameplayHudInitializationData: ; $E53C: PPU $2076, 8-byte payload .byte $20,$76,$08 .byte $B4,$B5,$B6,$B7,$B8,$B9,$BA,$BB ; $E547: PPU $20F7, 3-byte payload .byte $20,$F7,$03 .byte $B0,$B3,$B2 ; $E54D: PPU $2177, 3-byte payload .byte $21,$77,$03 .byte $B1,$B3,$B2 ;============================================================================== ; $E553-$E5C0: Draw Progression / Status Markers ; ; Local: ; Returns immediately when $67 is zero. ; ; Otherwise computes: ; ; count = min($67 + 1, $07) ; ; and stores that in $02. ; ; Initializes: ; ; $03 = $04 ; $00/$01 = $2317 ; ; If ($46 & $47) != 0, the high destination byte is adjusted by $08. ; ; The routine then performs nested loops, calling the helper at $E59A with ; Y=$3C. The helper writes a 2x2-style pattern at the current PPU position. ; ; Global: ; Called during gameplay initialization to draw a progression/status display. ; ; Notes: ; $E59A is a valid internal helper entry point within this routine. ;============================================================================== DrawLevelProgressMarkers: lda $67 bne Progression_E558 rts Progression_E558: clc adc #$01 cmp #$07 bcc Progression_E55F lda #$07 Progression_E55F: sta $02 lda #$04 sta $03 lda #$23 sta $00 lda #$17 sta $01 lda $46 and $47 beq Progression_E57C lda $00 clc adc #$08 sta $00 Progression_E57C: dec $02 bne Progression_E581 rts Progression_E581: dec $03 bne Progression_E58A lda $01 clc adc #$3A sta $01 Progression_E58A: ldy #$3C jsr Draw2x2Marker lda $01 clc adc #$02 sta $01 bne Progression_E57C ;============================================================================== ; $E59A-$E5C0: Draw 2x2 Progression Marker ; ; Input: ; $00/$01 = first PPU destination ; Y = first tile value ; ; Effect: ; Writes Y and Y+1 horizontally, then Y+2 and Y+3 one row ($20 bytes) ; below the initial destination. ;============================================================================== Draw2x2Marker: lda $00 sta PPUADDR lda $01 sta PPUADDR sty PPUDATA iny sty PPUDATA lda $00 sta PPUADDR lda $01 clc adc #$20 sta PPUADDR iny sty PPUDATA iny sty PPUDATA rts ;============================================================================== ; $-$E699: Initialize Gameplay State / Markers ; ; Local: ; Builds several gameplay marker/display values using the current value at ; $68 and a collection of lookup tables beginning at LevelProgressMarkerIndexTable ; ; The routine: ; - Initializes working values at $0A-$0F. ; - Derives an index/count from $68. ; - Selects PPU coordinates according to $46/$47. ; - Draws repeated 2x2 marker blocks using Draw2x2Marker. ; - Combines several lookup-table values into $0A+ working state. ; - Writes the resulting values directly into PPU attribute/display areas. ; - Selects final values for $02B6 and $02A9. ; ; Global: ; Called from GameModeState00 during initial gameplay setup. ; ; Notes: ; Address-oriented labels are used for the larger loops to preserve the ; original compact branch structure. ;============================================================================== InitializeLevelProgressDisplay: lda #$00 sta $03 sta $0E sta $0F lda #$15 sta $0A lda #$11 sta $0D lda #$05 sta $0B sta $0C lda $68 sta $02 sec sbc #$07 bcc UseFirstLevelProgressIndex cmp #$0C bcc ClampLevelProgressIndex lda #$0C ;------------------------------------------------------------------------------ ; $E5E9 ;------------------------------------------------------------------------------ ClampLevelProgressIndex: tax lda #$07 sta $02 bne InitializeLevelProgressPosition ;------------------------------------------------------------------------------ ; $E5F0 ;------------------------------------------------------------------------------ UseFirstLevelProgressIndex: ldx #$00 ;------------------------------------------------------------------------------ ; $E5F2 ; Initial PPU destination. ;------------------------------------------------------------------------------ InitializeLevelProgressPosition: lda #$22 sta $00 lda #$56 sta $01 lda $46 and $47 beq InitializeLevelProgressRowCounter lda $00 clc adc #$08 sta $00 ;------------------------------------------------------------------------------ ; $E605 ;------------------------------------------------------------------------------ InitializeLevelProgressRowCounter: lda #$05 sta $04 ;------------------------------------------------------------------------------ ; $E609 ; Main marker loop. ;------------------------------------------------------------------------------ DrawNextLevelProgressMarker: dec $04 bne SelectLevelProgressMarker lda $01 clc adc #$38 sta $01 ;------------------------------------------------------------------------------ ; $E612 ; Select marker information. ;------------------------------------------------------------------------------ SelectLevelProgressMarker: lda LevelProgressMarkerIndexTable,x sta $05 asl asl adc #$60 tay jsr Draw2x2Marker ldy $03 lda LevelProgressAttributeIndexTable,y sta $06 lda LevelProgressAttributeIndexTable+1,y sta $07 ldy $05 lda LevelProgressAttributeBits,y ;------------------------------------------------------------------------------ ; $E633 ; Shift selected marker bits. ;------------------------------------------------------------------------------ ShiftLevelProgressAttributeBits: dec $07 bmi StoreLevelProgressAttributeBits asl asl bcc ShiftLevelProgressAttributeBits ;------------------------------------------------------------------------------ ; $E63B ; Combine marker bits into working values. ;------------------------------------------------------------------------------ StoreLevelProgressAttributeBits: ldy $06 ora $0A,y sta $0A,y lda $01 clc adc #$02 sta $01 inc $03 inc $03 inx dec $02 bpl DrawNextLevelProgressMarker ;============================================================================== ; Write generated marker-state values into PPU memory. ;============================================================================== ldx #$23 lda $47 and $46 beq SetLevelProgressAttributeAddress ldx #$2B ;------------------------------------------------------------------------------ ; $E65D ; Set first PPU destination. ;------------------------------------------------------------------------------ SetLevelProgressAttributeAddress: stx PPUADDR stx $00 lda #$E5 sta PPUADDR ldx #$00 ;------------------------------------------------------------------------------ ; $E667 ; Reset count for one three-byte PPU group. ;------------------------------------------------------------------------------ ResetAttributeWriteCount: lda #$03 sta $01 ;------------------------------------------------------------------------------ ; $E66B ; Write three values from $0A+. ;------------------------------------------------------------------------------ WriteLevelProgressAttribute: lda $0A,x sta PPUDATA inx dec $01 bne WriteLevelProgressAttribute inc $02 bne SelectLevelProgressPalette lda $00 sta PPUADDR lda #$ED sta PPUADDR bne ResetAttributeWriteCount ;------------------------------------------------------------------------------ ; $E685 ; Select final marker/display value. ;------------------------------------------------------------------------------ SelectLevelProgressPalette: lda $68 cmp #$10 bcc StoreLevelProgressPalette lda #$0F ;------------------------------------------------------------------------------ ; $E68D ;------------------------------------------------------------------------------ StoreLevelProgressPalette: tay lda LevelProgressPaletteTable,y sta $02B6 lda #$0F sta $02A9 rts ;============================================================================== ; $E69A-$E6AD: Gameplay Marker Level Index Table ;============================================================================== LevelProgressMarkerIndexTable: .byte $00,$01,$02,$02 .byte $03,$03,$04,$04 .byte $05,$05,$06,$06 .byte $07,$07,$07,$07 .byte $07,$07,$07,$07 ;============================================================================== ; $E6AE-$E6BD: Gameplay Marker Working-Value Table ; ; Used as byte pairs through $03. ;============================================================================== LevelProgressAttributeIndexTable: .byte $00,$03 .byte $01,$02 .byte $01,$03 .byte $02,$02 .byte $03,$01 .byte $04,$00 .byte $04,$01 .byte $05,$00 ;============================================================================== ; $E6BE-$E6C5: Gameplay Marker Bit/Pattern Table ;============================================================================== LevelProgressAttributeBits: .byte $02,$02,$02,$02 .byte $03,$03,$03,$03 ;============================================================================== ; $E6C6-$E6D5: Gameplay Marker Final Value Table ; ; Indexed by min($68,$0F). Selected value is stored at $02B6. ;============================================================================== LevelProgressPaletteTable: .byte $16,$16,$26,$26 .byte $06,$06,$19,$19 .byte $17,$17,$17,$17 .byte $12,$12,$12,$12 ;============================================================================== ; $E6D6-$E737: Initialize Gameplay Transition State ;============================================================================== InitializeGameplayTransitionState: lda #$08 sta $43 sta PPUCTRL TransitionInit_E6DD: lda PPUSTATUS bpl TransitionInit_E6DD lda #$01 sta $060D sta $060E lda #$00 sta PPUMASK jsr ClearPlayfieldNametable lda #$01 sta $028D lda #$01 sta $0293 lda #$F7 sta $0278 lda #$7C sta $027A lda #$FF sta $1E lda #$00 sta $1F lda #$00 sta $88 ldy #$00 TransitionInit_CopyPalette: lda GameplayTransitionPalette,y sta $02A9,y iny cpy #$10 bne TransitionInit_CopyPalette lda #$20 sta $026F sta $0270 sta $0271 lda #$10 sta $3F lda #$88 sta PPUCTRL sta $43 jmp $CAA7 ;============================================================================== ; $E738-$E7AE: Initialize Gameplay PPU Region ; ; Selects $2000/$2800 according to $47 & $46, performs a patterned fill, ; clears $40 PPU bytes, and conditionally writes tile $5E at $2230/$2A30. ; ; Several branches deliberately re-enter the counter setup at $E751. ;============================================================================== ClearPlayfieldNametable: ldx #$20 lda $47 and $46 beq SetPlayfieldPpuAddress ldx #$28 ;------------------------------------------------------------------------------ ; $E742 ; Initialize selected nametable address. ;------------------------------------------------------------------------------ SetPlayfieldPpuAddress: lda PPUSTATUS stx PPUADDR lda #$00 sta PPUADDR lda #$1C sta $00 ;------------------------------------------------------------------------------ ; $E751 ; Shared counter-setup entry. ; ; The outer $00 loop deliberately jumps back here rather than rebuilding $00. ;------------------------------------------------------------------------------ ResetPlayfieldFillCounters: lda #$02 sta $01 lda #$1C sta $02 lda #$02 sta $03 lda #$2D ;------------------------------------------------------------------------------ ; $E75F ; Shared PPUDATA write point. ;------------------------------------------------------------------------------ WritePlayfieldFillByte: sta PPUDATA ldx $01 beq AdvancePlayfieldInnerRun dec $01 bne WritePlayfieldFillByte lda #$20 bne WritePlayfieldFillByte ;------------------------------------------------------------------------------ ; $E76E ; Process the second run counter. ;------------------------------------------------------------------------------ AdvancePlayfieldInnerRun: ldx $02 beq AdvancePlayfieldOuterRun dec $02 bne WritePlayfieldFillByte lda #$2D bne WritePlayfieldFillByte ;------------------------------------------------------------------------------ ; $E77A ; Process outer fill counters. ;------------------------------------------------------------------------------ AdvancePlayfieldOuterRun: dec $03 bne WritePlayfieldFillByte dec $00 bne ResetPlayfieldFillCounters ;------------------------------------------------------------------------------ ; $E782 ; Clear $40 PPU bytes. ;------------------------------------------------------------------------------ lda #$00 tax ClearPlayfieldTail: sta PPUDATA inx cpx #$40 bne ClearPlayfieldTail lda $87 cmp #$02 beq DrawPlayfieldSpecialTile rts ;------------------------------------------------------------------------------ ; $E794 ; Optional special tile at PPU $2230 or $2A30. ;------------------------------------------------------------------------------ DrawPlayfieldSpecialTile: ldx #$22 lda $47 and $46 beq SetSpecialTilePpuAddress ldx #$2A SetSpecialTilePpuAddress: lda PPUSTATUS stx PPUADDR lda #$30 sta PPUADDR lda #$5E sta PPUDATA rts ;============================================================================== ; $E7AF-$E7BE: Gameplay Transition Palette ;============================================================================== GameplayTransitionPalette: .byte $0F,$36,$20,$06 .byte $0F,$27,$20,$06 .byte $0F,$11,$20,$33 .byte $0F,$36,$20,$11 ;============================================================================== ; $E7BF-$E7CD: Gameplay Transition Update Coordinator ; ; Runs several already-known gameplay/render helpers, then dispatches through ; the state machine at $E7CE before returning to the main game-mode dispatcher. ;============================================================================== UpdateIntermissionTransition: jsr $EA19 jsr $EA94 jsr BuildEntitySpriteBuffer jsr DispatchIntermissionState jmp $CAA7 ;============================================================================== ; $E7CE-$E7DC: Transition state dispatcher ; ; $87 is an even state value used directly as a byte offset into the table. ;============================================================================== DispatchIntermissionState: ldy $87 lda IntermissionStateHandlerTable,y sta $10 lda IntermissionStateHandlerTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $E7DD-$E7E2: Transition state pointer table ;============================================================================== IntermissionStateHandlerTable: .word DispatchIntermissionPhase .word DispatchIntermissionState2Phase .word DispatchIntermissionState4Phase ;============================================================================== ; $E7E3-$E7F1: Secondary transition-state dispatcher ; ; $88 is used directly as a byte offset into another pointer table. ;============================================================================== DispatchIntermissionPhase: ldy $88 lda IntermissionPhaseHandlerTable,y sta $10 lda IntermissionPhaseHandlerTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $E7F2-$E7F9: Transition substate pointer table ;============================================================================== IntermissionPhaseHandlerTable: .word UpdateIntermissionPhase0 .word UpdateIntermissionPhase1 .word UpdateIntermissionPhase2 .word UpdateIntermissionPhase3 ;============================================================================== ; $E7FA-$E822: Transition substate 0 ; ; Waits until $0278 reaches at least $D0, then advances $88 by two and ; initializes several mirrored object/state values. ;============================================================================== UpdateIntermissionPhase0: lda $0278 cmp #$D0 bcc TransitionSubstate_E802 rts TransitionSubstate_E802: inc $88 inc $88 lda #$0C sta $028C lda #$20 sta $0292 lda #$F7 sta $0274 lda #$7C sta $0276 lda #$FE sta $1A lda #$E0 sta $1B rts ;============================================================================== ; $E823-$E84D: Transition substate 1 ; ; Waits for $0274 to become zero, then advances $88 and initializes a ; different object/state configuration. ;============================================================================== UpdateIntermissionPhase1: lda $0274 beq TransitionSubstate_E829 rts TransitionSubstate_E829: inc $88 inc $88 lda #$40 sta $4C lda #$1E sta $028C lda #$22 sta $0292 lda #$09 sta $0274 lda #$7C sta $0276 lda #$01 sta $1A lda #$00 sta $1B rts ;============================================================================== ; $E84E-$E8B9: Transition substate 2 ; ; Waits until $0274 reaches at least $80, then advances $88 and initializes ; four related coordinate/state structures and their mirrored render values. ;============================================================================== UpdateIntermissionPhase2: lda #$01 sta $060D sta $060E lda $0274 cmp #$80 bcs TransitionSubstate_E85D rts TransitionSubstate_E85D: inc $88 inc $88 lda #$01 sta $1E sta $22 sta $26 sta $2A lda #$C0 sta $1F sta $23 sta $27 sta $2B clc lda #$38 sta $028D adc #$01 sta $028E adc #$01 sta $028F adc #$01 sta $0290 lda #$21 sta $0293 sta $0294 sta $0295 sta $0296 lda #$09 sta $0278 sta $0280 lda #$19 sta $027C sta $0284 lda #$6C sta $027A sta $027E lda #$7C sta $0282 sta $0286 rts ;============================================================================== ; $E8BA-$E8C6: Transition substate 3 ; ; Waits for $0278 to become zero, then primes delay $4C and transfers to the ; routine at $EA14. ;============================================================================== UpdateIntermissionPhase3: lda $0278 beq TransitionSubstate_E8C0 rts TransitionSubstate_E8C0: lda #$40 sta $4C jmp $EA14 ;============================================================================== ; $E8C7-$E8D5: Transition State $02 Substate Dispatcher ; ; Local: ; Uses $88 as an even byte offset into IntermissionState2PhaseTable. ;============================================================================== DispatchIntermissionState2Phase: ldy $88 lda IntermissionState2PhaseTable,y sta $10 lda IntermissionState2PhaseTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $E8D6-$E8DB: Transition phase table ;============================================================================== IntermissionState2PhaseTable: .word UpdateIntermissionState2Phase0 .word UpdateIntermissionState2Phase1 .word UpdateIntermissionState2Phase2 ;============================================================================== ; $E8DC-$E904: Transition phase 0 ; ; Waits until $0278 reaches $A0, then advances the $88 state and initializes ; the primary mirrored object/state values. ;============================================================================== UpdateIntermissionState2Phase0: lda $0278 cmp #$A0 bcc TransitionPhase_E8E4 rts TransitionPhase_E8E4: inc $88 inc $88 lda #$0C sta $028C lda #$20 sta $0292 lda #$F7 sta $0274 lda #$7C sta $0276 lda #$FF sta $1A lda #$00 sta $1B rts ;============================================================================== ; $E905-$E93E: Transition phase 1 ; ; Watches $0274. At $80 it installs one object configuration. At $78 it ; clears the position pair and advances to the next phase. ;============================================================================== UpdateIntermissionState2Phase1: lda $0274 cmp #$80 bne TransitionPhase_E92D lda #$FF sta $1A lda #$C0 sta $1B lda #$42 sta $028E lda #$00 sta $0294 lda #$80 sta $027C lda #$7C sta $027E lda #$00 sta $22 sta $23 rts TransitionPhase_E92D: cmp #$78 beq TransitionPhase_E933 rts TransitionPhase_E933: lda #$00 sta $1A sta $1B inc $88 inc $88 rts ;============================================================================== ; $E93F-$E96D: Transition phase 2 ; ; Uses bit $40 of $028C and timer/state value $89 to alternate the object ; state between values $46 and $47. Eventually transfers to $EA14. ;============================================================================== UpdateIntermissionState2Phase2: lda $028C and #$40 bne TransitionPhase_E953 lda #$46 sta $028C lda #$45 sta $028E lda #$40 sta $89 rts TransitionPhase_E953: dec $89 beq TransitionPhase_E958 rts TransitionPhase_E958: lda $028C cmp #$47 bne TransitionPhase_E963 jmp $EA14 TransitionPhase_E963: lda #$47 sta $028C lda #$40 sta $89 rts ;============================================================================== ; $E96E-$E97C: Transition State $04 Dispatcher ; ; $88 is an even byte offset into the four-entry phase table. ;============================================================================== DispatchIntermissionState4Phase: ldy $88 lda IntermissionState4PhaseTable,y sta $10 lda IntermissionState4PhaseTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $E97D-$E984: Transition State $04 Phase Table ;============================================================================== IntermissionState4PhaseTable: .word UpdateIntermissionState4Phase0 .word UpdateIntermissionState4Phase1 .word UpdateIntermissionState4Phase2 .word UpdateIntermissionState4Phase3 ;============================================================================== ; $E985-$E9AD: Phase 0 ; ; Waits until $0278 reaches $D8, advances $88, then installs the initial ; mirrored object/state configuration. ;============================================================================== UpdateIntermissionState4Phase0: lda $0278 cmp #$D8 bcc TransitionPhase_E98D rts TransitionPhase_E98D: inc $88 inc $88 lda #$4C sta $028C lda #$20 sta $0292 lda #$F7 sta $0274 lda #$7C sta $0276 lda #$FF sta $1A lda #$00 sta $1B rts ;============================================================================== ; $E9AE-$E9D8: Phase 1 ; ; Waits for $0274 to reach zero, advances $88 and installs the next object ; configuration. ;============================================================================== UpdateIntermissionState4Phase1: lda $0274 beq TransitionPhase_E9B4 rts TransitionPhase_E9B4: inc $88 inc $88 lda #$28 sta $4C lda #$4A sta $028C lda #$23 sta $0292 lda #$09 sta $0274 lda #$7C sta $0276 lda #$01 sta $1A lda #$00 sta $1B rts ;============================================================================== ; $E9D9-$EA09: Phase 2 ; ; Enables transition flags, waits until $0274 reaches $19, then advances $88 ; and sets the next mirrored-state values. ;============================================================================== UpdateIntermissionState4Phase2: lda #$01 sta $060D sta $060E lda $0274 cmp #$19 bcs TransitionPhase_E9E8 rts TransitionPhase_E9E8: inc $88 inc $88 lda #$4C sta $028D lda #$21 sta $0293 lda #$11 sta $0278 lda #$84 sta $027A lda #$01 sta $1E lda #$00 sta $1F rts ;============================================================================== ; $EA0A-$EA18: Phase 3 ; ; Waits for $0278 to reach zero, then primes the next game-mode/state ; transition. ;============================================================================== UpdateIntermissionState4Phase3: lda $0278 beq TransitionPhase_EA10 rts TransitionPhase_EA10: lda #$40 sta $4C lda #$00 sta $3F rts ;============================================================================== ; $EA19-$EA93: Update Mirrored Gameplay Object Positions ; ; Local: ; Walks five four-byte structures in parallel: ; ; $00/$01 -> source gameplay structures beginning at $001A ; $02/$03 -> mirrored structures beginning at $0274 ; ; Movement increments from the source structures are added into mirrored ; coordinates. Attribute bit $20 in $0292+ is adjusted according to the ; resulting coordinate, and coordinate reset/wrap handling is performed. ; ; Global: ; Called from gameplay transition processing before render data is rebuilt. ; ; Notes: ; Several branches deliberately re-enter shared code, so global address-style ; labels are used throughout. ;============================================================================== UpdateTransitionObjectPositions: lda #$1A sta $00 lda #$00 sta $01 lda #$74 sta $02 lda #$02 sta $03 ldx #$00 ;------------------------------------------------------------------------------ ; $EA2B ; Process one structure. ;------------------------------------------------------------------------------ UpdateNextTransitionObject: ldy #$00 lda ($02),y beq AdvanceTransitionObjectPointers iny lda ($00),y clc adc ($02),y sta ($02),y dey lda ($00),y adc ($02),y sta ($02),y cmp #$C0 bcc UpdateTransitionObjectAttribute ;------------------------------------------------------------------------------ ; $EA44 ; Set attribute bit $20. ;------------------------------------------------------------------------------ SetTransitionObjectAttributeBit: lda #$20 ora $0292,x sta $0292,x bne CheckTransitionObjectWrap ;------------------------------------------------------------------------------ ; $EA4E ; Clear/set attribute according to coordinate range. ; ; If value < $40, branch backward to $EA44 and set bit $20. ; Otherwise clear bit $20 with mask $DF. ;------------------------------------------------------------------------------ UpdateTransitionObjectAttribute: cmp #$40 bcc SetTransitionObjectAttributeBit lda #$DF and $0292,x sta $0292,x ;------------------------------------------------------------------------------ ; $EA5A ; Coordinate wrap/reset test. ;------------------------------------------------------------------------------ CheckTransitionObjectWrap: ldy #$00 lda ($02),y cmp #$FC bcc CheckTransitionObjectLowBoundary ;------------------------------------------------------------------------------ ; $EA62 ; Reset coordinate/movement state. ;------------------------------------------------------------------------------ ResetTransitionObjectPosition: lda #$00 sta ($02),y sta ($00),y iny sta ($00),y iny sta ($02),y bne AdvanceTransitionObjectPointers ;------------------------------------------------------------------------------ ; $EA70 ; Values below $04 also take the reset path. ;------------------------------------------------------------------------------ CheckTransitionObjectLowBoundary: cmp #$04 bcc ResetTransitionObjectPosition ;------------------------------------------------------------------------------ ; $EA74 ; Advance both source and mirrored pointers. ;------------------------------------------------------------------------------ AdvanceTransitionObjectPointers: lda $00 clc adc #$04 sta $00 lda $01 adc #$00 sta $01 lda $02 clc adc #$04 sta $02 lda $03 adc #$00 sta $03 inx cpx #$05 bne UpdateNextTransitionObject rts ;============================================================================== ; $EA94-$EAA2: Gameplay Animation State Dispatcher ; ; $87 is an even state value used directly as an offset into the pointer table ; at $EAA3. ;============================================================================== DispatchTransitionAnimation: ldy $87 lda TransitionAnimationHandlerTable,y sta $10 lda TransitionAnimationHandlerTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $EAA3-$EAA8: Gameplay Animation State Table ;============================================================================== TransitionAnimationHandlerTable: .word DispatchTransitionAnimationPhase .word DispatchTransitionAnimationState2 .word DispatchTransitionAnimationState4 ;============================================================================== ; $EAA9-$EAB7: Animation Substate Dispatcher ; ; $88 is an even state value used directly as an offset into this table. ;============================================================================== DispatchTransitionAnimationPhase: ldy $88 lda TransitionAnimationPhaseTable,y sta $10 lda TransitionAnimationPhaseTable+1,y sta $11 jmp ($0010) ;============================================================================== ; $EAB8-$EABF: Animation Substate Table ;============================================================================== TransitionAnimationPhaseTable: .word UpdateTransitionAnimationFrame .word UpdateTransitionAnimationType1 .word UpdateTransitionAnimationType2 .word UpdateTransitionAnimationPattern ;============================================================================== ; $EAC0-$EACD: Animation Substate 0 ; ; Advances $B7 modulo eight and uses it to select a value for $028D. ;============================================================================== UpdateTransitionAnimationFrame: inc $B7 lda $B7 and #$07 tay lda TransitionAnimationFrameTable,y sta $028D rts ;============================================================================== ; $EACE-$EAD5: Animation frame table ;============================================================================== TransitionAnimationFrameTable: .byte $04,$04,$04,$05 .byte $05,$04,$01,$01 ;============================================================================== ; $EAD6-$EAEC: Animation Substate 1 ; ; Runs only when ($4B & 7) == 0. Alternates $028C between $0C and $0D ; according to frame-counter bit 3, then falls back into the common animation ; update path beginning at $EAC0. ;============================================================================== UpdateTransitionAnimationType1: lda $4B and #$07 bne UpdateTransitionAnimationFrame lda $4B and #$08 bne AnimationSubstate_EAE7 lda #$0C bne AnimationSubstate_EAE9 AnimationSubstate_EAE7: lda #$0D AnimationSubstate_EAE9: sta $028C bne UpdateTransitionAnimationFrame ;============================================================================== ; $EAED-$EB03: Animation Substate 2 ; ; Updates only once every eight frames and alternates $028C between $1E/$1F ; according to frame-counter bit 3. ;============================================================================== UpdateTransitionAnimationType2: lda $4B and #$07 beq AnimationSubstate_EAF5 rts AnimationSubstate_EAF5: lda $4B and #$08 bne AnimationSubstate_EB00 lda #$1E bne AnimationSubstate_EB02 AnimationSubstate_EB00: lda #$1F AnimationSubstate_EB02: sta $028C rts ;============================================================================== ; $EB04-$EB28: Animation Substate 3 ; ; Advances $B7 modulo eight, maps the result through a small index table, and ; uses the selected entry to load four consecutive animation values into ; $028D-$0290. ; ; The final BNE intentionally branches backward into the common update path. ;============================================================================== UpdateTransitionAnimationPattern: inc $B7 lda $B7 and #$07 tay lda TransitionAnimationPatternIndex,y tay lda TransitionAnimationPatternTiles,y sta $028D lda TransitionAnimationPatternTiles+1,y sta $028E lda TransitionAnimationPatternTiles+2,y sta $028F lda TransitionAnimationPatternTiles+3,y sta $0290 bne UpdateTransitionAnimationType2 ;============================================================================== ; $EB29-$EB30: Animation pattern index table ;============================================================================== TransitionAnimationPatternIndex: .byte $00,$00,$00,$04 .byte $04,$00,$08,$08 ;============================================================================== ; $EB31-$EB3C: Animation pattern data ; ; Indexed using offsets 0, 4 and 8. The final logical group continues beyond ; this initial range, so preserve the next code/data boundary carefully. ;============================================================================== TransitionAnimationPatternTiles: .byte $3C,$3D,$3E,$3F .byte $3C,$40,$3E,$41 .byte $38,$39,$3A,$3B ;============================================================================== ; $EB3D-$EB4B: Gameplay Animation State $02 Dispatcher ;============================================================================== DispatchTransitionAnimationState2: ldy $88 lda TransitionAnimationState2Table,y sta $10 lda TransitionAnimationState2Table+1,y sta $11 jmp ($0010) ;============================================================================== ; $EB4C-$EB51: Gameplay Animation State $02 Pointer Table ;============================================================================== TransitionAnimationState2Table: .word UpdateTransitionAnimationFrame .word UpdateTransitionAnimationState2 .word UpdateTransitionAnimationFrame ;============================================================================== ; $EB52-$EB6C: Animation State $02 Handler ;============================================================================== UpdateTransitionAnimationState2: jsr UpdateTransitionAnimationType1 lda $0274 cmp #$7D bne AnimationState2_EB62 lda #$43 sta $028E rts AnimationState2_EB62: cmp #$7B beq AnimationState2_EB68 rts AnimationState2_EB68: lda #$44 sta $028E rts ;============================================================================== ; $EB6D-$EB7B: Gameplay Animation State $04 Dispatcher ;============================================================================== DispatchTransitionAnimationState4: ldy $88 lda TransitionAnimationState4Table,y sta $10 lda TransitionAnimationState4Table+1,y sta $11 jmp ($0010) ;============================================================================== ; $EB7C-$EB83: Gameplay Animation State $04 Pointer Table ;============================================================================== TransitionAnimationState4Table: .word UpdateTransitionAnimationFrame .word UpdateTransitionAnimationState4Type1 .word UpdateTransitionAnimationState4Type2 .word UpdateTransitionAnimationState4Type2 ;============================================================================== ; $EB84-$EB9E: Animation State $04 Handler 1 ; ; Alternates $028C between $48/$49 every eight frames, while non-update ; frames tail-jump into the common $EAC0 animation updater. ;============================================================================== UpdateTransitionAnimationState4Type1: lda $4B and #$07 beq AnimationState4_EB8E jmp UpdateTransitionAnimationFrame AnimationState4_EB8E: lda $4B and #$08 bne AnimationState4_EB97 lda #$48 bne AnimationState4_EB99 AnimationState4_EB97: lda #$49 AnimationState4_EB99: sta $028C jmp UpdateTransitionAnimationFrame ;============================================================================== ; $EB9F-$EBB5: Animation State $04 Handler 2/3 ; ; Alternates $028C between $4A and $4B every eight frames. ;============================================================================== UpdateTransitionAnimationState4Type2: lda $4B and #$07 beq AnimationState4_EBA6 rts AnimationState4_EBA6: lda $4B and #$08 bne AnimationState4_EBB0 lda #$4A bne AnimationState4_EBB2 AnimationState4_EBB0: lda #$4B AnimationState4_EBB2: sta $028C rts ;============================================================================== ; $EBB6-$EC3F: Gameplay Lookup Data ; ; Raw lookup data preserved byte-for-byte. ; Semantic subdivision is deferred until the consuming routines are decoded. ;============================================================================== MazeGameplayData_EBB6: ; $EBB6-$EBC5 .byte $00,$07,$00,$00,$00,$04,$01,$06 .byte $01,$01,$01,$04,$01,$04,$02,$02 ; $EBC6-$EBD5 .byte $02,$03,$02,$03,$02,$02,$02,$03 .byte $02,$02,$02,$02,$03,$03,$03,$05 ; $EBD6-$EBE5 .byte $03,$02,$03,$03,$03,$02,$03,$02 .byte $04,$03,$03,$02,$03,$02,$04,$03 ; $EBE6-$EBF5 .byte $03,$01,$04,$02,$05,$03,$03,$05 .byte $04,$02,$05,$03,$03,$02,$04,$02 ; $EBF6-$EC05 .byte $06,$03,$03,$01,$05,$02,$06,$03 .byte $03,$01,$05,$02,$07,$03,$03,$03 ; $EC06-$EC15 .byte $05,$02,$07,$03,$03,$01,$06,$02 .byte $07,$03,$03,$01,$06,$02,$07,$03 ; $EC16-$EC25 .byte $03,$00,$06,$02,$07,$03,$03,$01 .byte $06,$02,$07,$03,$03,$00,$06,$02 ; $EC26-$EC35 .byte $07,$03,$03,$00,$06,$02,$07,$03 .byte $03,$00,$06,$02,$07,$03,$04,$00 ; $EC36-$EC3F .byte $06,$02,$07,$03,$04,$00,$06,$02 .byte $07,$03 ;============================================================================== ; $EC40-$ECAB: Gameplay Parameter Lookup Data ; ; Raw lookup data preserved byte-for-byte. ; ; The byte pattern strongly resembles little-endian 16-bit quantities, but ; semantic grouping is intentionally deferred until the consuming routines ; have been identified. ;============================================================================== MazeGameplayData_EC40: ; $EC40-$EC4F .byte $D0,$00,$C0,$00,$A0,$00,$B0,$00 .byte $A0,$00,$80,$00,$A0,$00,$B0,$00 ; $EC50-$EC5F .byte $90,$00,$50,$00,$50,$00,$D0,$00 .byte $C0,$00,$A0,$00,$B0,$00,$A0,$00 ; $EC60-$EC6F .byte $80,$00,$B0,$00,$C0,$00,$A0,$00 .byte $50,$00,$50,$00,$F0,$00,$E0,$00 ; $EC70-$EC7F .byte $C0,$00,$E0,$00,$D0,$00,$B0,$00 .byte $E0,$00,$F0,$00,$D0,$00,$70,$00 ; $EC80-$EC8F .byte $70,$00,$00,$01,$F0,$00,$D0,$00 .byte $00,$01,$F0,$00,$D0,$00,$00,$01 ; $EC90-$EC9F .byte $10,$01,$F0,$00,$80,$00,$80,$00 .byte $00,$01,$F0,$00,$D0,$00,$00,$01 ; $ECA0-$ECAB .byte $F0,$00,$D0,$00,$20,$01,$30,$01 .byte $10,$01,$80,$00 ;============================================================================== ; $ECAC-$ECFF: Gameplay Lookup / Parameter Data ; ; Raw data preserved byte-for-byte. Semantic subdivision is intentionally ; deferred until the consuming routines are decoded. ;============================================================================== MazeGameplayData_ECAC: ; $ECAC-$ECBB .byte $80,$00,$07,$14,$07,$14,$05,$14 .byte $05,$FF,$07,$14,$07,$14,$05,$FF ; $ECBC-$ECCB .byte $00,$00,$05,$14,$05,$14,$05,$FF .byte $00,$00,$05,$14,$05,$14,$05,$FF ; $ECCC-$ECDB .byte $00,$00,$14,$0A,$1E,$0F,$28,$14 .byte $32,$19,$3C,$1E,$50,$28,$64,$32 ; $ECDC-$ECEB .byte $02,$1E,$5A,$00,$03,$32,$00,$00 .byte $04,$00,$00,$00,$01,$07,$11,$20 ; $ECEC-$ECFB .byte $2D,$1F,$D0,$D0,$10,$13,$D0,$D0 .byte $10,$1D,$2D,$11,$C3,$C3,$03,$11 ; $ECFC-$ECFF .byte $C3,$C3,$03,$11 ;============================================================================== ; $ED00-$EDFF: Gameplay Lookup / Pattern Data ; ; Raw data preserved byte-for-byte. This region contains highly structured ; repeated values and bit-pattern-like fields, but semantic subdivisions are ; deferred until the routines that consume this region are identified. ;============================================================================== MazeLayoutData_ED00: ; $ED00-$ED0F .byte $2D,$11,$03,$1F,$10,$1D,$03,$1F .byte $10,$1D,$03,$11,$03,$1F,$10,$1D ; $ED10-$ED1F .byte $03,$1F,$10,$1D,$03,$11,$2D,$11 .byte $01,$11,$20,$11,$03,$11,$20,$11 ; $ED20-$ED2F .byte $03,$11,$03,$11,$20,$11,$03,$11 .byte $20,$11,$01,$11,$2D,$11,$03,$1E ; $ED30-$ED3F .byte $10,$1C,$03,$1E,$10,$1C,$03,$1A .byte $03,$1E,$10,$1C,$03,$1E,$10,$1C ; $ED40-$ED4F .byte $03,$11,$2D,$11,$C3,$C3,$C3,$C3 .byte $83,$11,$2D,$11,$03,$1F,$10,$1D ; $ED50-$ED5F .byte $03,$1B,$03,$1F,$10,$10,$10,$1D .byte $03,$1B,$03,$1F,$10,$1D,$03,$11 ; $ED60-$ED6F .byte $2D,$11,$03,$1E,$10,$1C,$03,$11 .byte $03,$1E,$10,$13,$10,$1C,$03,$11 ; $ED70-$ED7F .byte $03,$1E,$10,$1C,$03,$11,$2D,$11 .byte $C3,$03,$11,$83,$11,$83,$11,$C3 ; $ED80-$ED8F .byte $03,$11,$2D,$1E,$90,$1D,$03,$15 .byte $10,$18,$08,$1A,$08,$19,$10,$14 ; $ED90-$ED9F .byte $03,$1F,$90,$1C,$2D,$E0,$11,$03 .byte $11,$C0,$80,$11,$03,$11,$E0,$2D ; $EDA0-$EDAF .byte $E0,$11,$03,$11,$00,$1F,$17,$2C .byte $16,$1D,$00,$11,$03,$11,$E0,$2D ; $EDB0-$EDBF .byte $22,$90,$1C,$03,$1A,$00,$11,$80 .byte $11,$00,$1A,$03,$1E,$90,$21,$04 ; $EDC0-$EDCF .byte $06,$C0,$03,$40,$11,$80,$11,$40 .byte $03,$C0,$05,$2D,$22,$90,$1D,$03 ; $EDD0-$EDDF .byte $1B,$00,$1E,$90,$1C,$00,$1B,$03 .byte $1F,$90,$21,$2D,$E0,$11,$03,$11 ; $EDE0-$EDEF .byte $C0,$80,$11,$03,$11,$E0,$2D,$E0 .byte $11,$03,$11,$00,$1F,$90,$1D,$00 ; $EDF0-$EDFF .byte $11,$03,$11,$E0,$2D,$1F,$90,$1C .byte $03,$1A,$00,$1E,$10,$13,$10,$1C ;============================================================================== ; $EE00-$EE8B: Gameplay Lookup / Pattern Data ; ; Raw data preserved byte-for-byte. ;============================================================================== MazeLayoutData_EE00: ; $EE00-$EE0F .byte $00,$1A,$03,$1E,$90,$1D,$2D,$11 .byte $C3,$C3,$03,$11,$C3,$C3,$03,$11 ; $EE10-$EE1F .byte $2D,$11,$03,$19,$10,$1D,$03,$19 .byte $10,$18,$09,$1A,$09,$19,$10,$18 ; $EE20-$EE2F .byte $03,$1F,$10,$18,$03,$11,$2D,$11 .byte $01,$43,$11,$C3,$03,$00,$C3,$03 ; $EE30-$EE3F .byte $11,$43,$01,$11,$2D,$15,$10,$1D .byte $03,$11,$03,$1B,$03,$1F,$90,$1D ; $EE40-$EE4F .byte $03,$1B,$03,$11,$03,$1F,$10,$14 .byte $2D,$15,$10,$1C,$03,$1A,$03,$11 ; $EE50-$EE5F .byte $03,$1E,$10,$13,$10,$1C,$03,$11 .byte $03,$1A,$03,$1E,$10,$14,$2D,$11 ; $EE60-$EE6F .byte $C3,$03,$11,$83,$11,$83,$11,$C3 .byte $03,$11,$2D,$11,$03,$19,$90,$12 ; $EE70-$EE7F .byte $10,$18,$03,$1A,$03,$19,$10,$12 .byte $90,$18,$03,$11,$2D,$11,$C3,$C3 ; $EE80-$EE8B .byte $C3,$C3,$83,$11,$2D,$1E,$D0,$D0 .byte $D0,$D0,$90,$1C ;============================================================================== ; $EE8C-$EECF: Initialize Runtime Work Buffers ; ; Initializes zero-page runtime pointers and hardware state, clears $0600-$060F, ; then clears sixteen locations at eight-byte intervals beginning at $0620. ;============================================================================== InitializeAudioEngine: lda #$00 sta audioRequestPtr lda #$06 sta audioRequestPtr+1 lda #$20 sta audioChannelTablePtr lda #$06 sta audioChannelTablePtr+1 lda $F100 sta audioSequenceTablePtr lda $F101 sta audioSequenceTablePtr+1 lda #$40 sta audioWorkValue lda #$1F sta APUSTATUS lda #$C0 sta APUFRAMECNT ldy #$00 lda #$00 ;------------------------------------------------------------------------------ ; $EEB8 ; Clear sixteen contiguous bytes through ($F0). ;------------------------------------------------------------------------------ RuntimeInit_Clear0600: sta (audioRequestPtr),y iny cpy #$10 bne RuntimeInit_Clear0600 ldy #$00 ldx #$10 ;------------------------------------------------------------------------------ ; $EEC3 ; Clear sixteen entries at an eight-byte stride. ; ; LDA #$00 is intentionally executed on every iteration. ;------------------------------------------------------------------------------ RuntimeInit_Clear0620Stride: lda #$00 sta (audioChannelTablePtr),y tya clc adc #$08 tay dex bne RuntimeInit_Clear0620Stride rts ;============================================================================== ; $EED0-$EF2C: Initialize and Scan Runtime Command Slots ; ; Clears runtime work values, then scans sixteen 8-byte entries addressed ; through $F2/$F3. Occupied entries update flags through $FE/$FF, while newly ; activated entries copy four bytes through the pointer selected in $F6. ; ; Execution falls through at $EF2D into the next processing phase. ;============================================================================== InitializeAudioChannels: lda #$00 sta audioChannelFlags sta $F9 sta $FA sta $FB sta audioChannelOffset sta $FF lda #$F8 sta audioIndirectPtr ;------------------------------------------------------------------------------ ; $EEE2 ; Examine one eight-byte slot. ;------------------------------------------------------------------------------ ScanAudioChannelSlot: ldy $FD lda (audioChannelTablePtr),y beq AdvanceAudioChannelSlot cmp #$05 bcc ActivateAudioChannelSlot sec sbc #$05 tay lda #$01 sta ($FE),y jmp AdvanceAudioChannelSlot ;------------------------------------------------------------------------------ ; $EEF5 ; Process slot values $01-$04. ;------------------------------------------------------------------------------ ActivateAudioChannelSlot: sec sbc #$01 tay lda ($FE),y bne AdvanceAudioChannelSlot lda #$01 sta ($FE),y tya tax adc #$04 ldy $FD sta ($F2),y txa asl asl sta $F6 ldx #$00 ;------------------------------------------------------------------------------ ; $EF12 ; Set initial four-byte copy count. ;------------------------------------------------------------------------------ BeginAudioChannelParameterCopy: lda #$04 ;------------------------------------------------------------------------------ ; $EF14 ; Push the current copy count. ; ; The loop at $EF20 deliberately returns here, so PHA is repeated on every ; iteration. ;------------------------------------------------------------------------------ CopyAudioChannelParameter: pha iny lda ($F2),y sta ($F6,x) inc $F6 pla sec sbc #$01 bne CopyAudioChannelParameter ;------------------------------------------------------------------------------ ; $EF22 ; Advance to the next eight-byte slot. ;------------------------------------------------------------------------------ AdvanceAudioChannelSlot: lda $FD clc adc #$08 sta $FD cmp #$80 bcc ScanAudioChannelSlot ; Fall through to $EF2D. ;============================================================================== ; $EF2D-$F006: Process Runtime Command Entries ; ; Continuation of the runtime command-slot scan. ; ; $FC indexes source commands through ($F0). ; $FD indexes eight-byte runtime records through ($F2). ; ; Empty runtime records are initialized from tables through $F4/$F5 and ; command bytes fetched through the helper at $F0C3. Active records update ; countdowns and command parameters before both indices advance. ;============================================================================== UpdateAudioChannels: ldy #$00 sty $FC sty $FD ;------------------------------------------------------------------------------ ; $EF33 ; Process one source entry. ;------------------------------------------------------------------------------ UpdateNextAudioSequence: ldy $FC lda ($F0),y bne CheckAudioChannelActive jmp AdvanceAudioSequence ;------------------------------------------------------------------------------ ; $EF3B ; Check the corresponding runtime record. ;------------------------------------------------------------------------------ CheckAudioChannelActive: ldy $FD lda ($F2),y bne TickAudioChannelDelay lda $FC asl tay lda ($F4),y pha lda $FD adc #$05 tay pla sta ($F2),y lda $FC asl adc #$01 tay lda ($F4),y pha lda $FD adc #$06 tay pla sta ($F2),y jsr $F0C3 ldy $FD sta ($F2),y jsr $F0C3 tax lda $FD clc adc #$01 tay txa sta ($F2),y jsr $F0C3 tax lda $FD clc adc #$02 tay txa sta ($F2),y jsr $F0C3 tax lda $FD clc adc #$04 tay txa sta ($F2),y jmp DecodeAudioCommand ;------------------------------------------------------------------------------ ; $EF94 ; Decrement the active record countdown at offset 7. ;------------------------------------------------------------------------------ TickAudioChannelDelay: lda $FD clc adc #$07 tay lda ($F2),y sec sbc #$01 sta ($F2),y bne AdvanceAudioSequence ;------------------------------------------------------------------------------ ; $EFA2 ; Fetch and decode the next runtime command byte. ;------------------------------------------------------------------------------ DecodeAudioCommand: jsr $F0C3 cmp #$F0 bcs $F00D cmp #$C0 bcs LoadAudioCommandDelay pha and #$F0 lsr lsr lsr tax lda AudioFrequencyLookupTable,x sta $FE lda AudioFrequencyLookupTable+1,x sta $FF pla and #$0F beq ApplyAudioFrequencyMask tax ;------------------------------------------------------------------------------ ; $EFC4 ; Shift the selected mask by the command low nibble. ;------------------------------------------------------------------------------ ShiftAudioFrequencyMask: lsr $FE ror $FF dex bne ShiftAudioFrequencyMask ;------------------------------------------------------------------------------ ; $EFCB ; Merge the generated mask into the runtime record. ;------------------------------------------------------------------------------ ApplyAudioFrequencyMask: lda $FD clc adc #$04 tay lda ($F2),y and #$F8 ora $FE sta ($F2),y lda $FF dey sta ($F2),y ldy $FD lda ($F2),y cmp #$05 bcc LoadAudioCommandDelay sec sbc #$04 sta ($F2),y ;------------------------------------------------------------------------------ ; $EFEB ; Load the new runtime countdown. ;------------------------------------------------------------------------------ LoadAudioCommandDelay: jsr $F0C3 pha lda $FD clc adc #$07 tay pla sta ($F2),y ;------------------------------------------------------------------------------ ; $EFF8 ; Advance to the next runtime/source entry. ;------------------------------------------------------------------------------ AdvanceAudioSequence: lda $FD clc adc #$08 sta $FD lda $FC adc #$01 sta $FC cmp #$10 ; Fall through at $F007 to the next control-flow block. ;============================================================================== ; $F007-$F00C: Runtime command-loop termination / continuation ; ; Entry is the fall-through from the CMP #$10 at $F005. ;============================================================================== FinishAudioSequenceScan: bcs AudioSequenceUpdateDone jmp UpdateNextAudioSequence AudioSequenceUpdateDone: rts ;============================================================================== ; $F00D-$F01D: Runtime Command Dispatcher ; ; Input: ; A = command byte >= $F0 ; ; Uses the low nibble to select a handler through the pointer table at $F01E. ;============================================================================== DispatchAudioCommand: and #$0F asl tax lda AudioCommandHandlerTable,x sta $FE lda AudioCommandHandlerTable+1,x sta $FF jmp ($00FE) ;============================================================================== ; $F01E-$F03D: Runtime Command Handler Table ; ; Sixteen 16-bit handlers. Unsupported command values use the no-op handler ; at $F03E. ;============================================================================== AudioCommandHandlerTable: .word StopAudioSequence ; $F0 .word AudioCommandF1 ; $F1 .word AudioCommandF2 ; $F2 .word AudioCommandF3 ; $F3 .word AudioCommandF4 ; $F4 .word AudioCommandF5 ; $F5 .word AudioCommandF6 ; $F6 .word StopAudioSequence ; $F7 .word StopAudioSequence ; $F8 .word StopAudioSequence ; $F9 .word StopAudioSequence ; $FA .word StopAudioSequence ; $FB .word StopAudioSequence ; $FC .word StopAudioSequence ; $FD .word StopAudioSequence ; $FE .word StopAudioSequence ; $FF ;============================================================================== ; $F03E-$F04A: Runtime command no-op/terminate handler ; ; Clears the current source entry and current runtime record, then advances ; through the common continuation at $EFF8. ;============================================================================== StopAudioSequence: ldy $FC lda #$00 sta ($F0),y ldy $FD sta ($F2),y jmp AdvanceAudioSequence ;============================================================================== ; $F04B-$F062: Runtime command $F1 ; ; Fetches one command byte and merges its bits into runtime-record byte 0/1. ;============================================================================== AudioCommandF1: jsr FetchAudioSequenceByte pha lda $FD clc adc #$01 tay lda ($F2),y and #$3F sta ($F2),y pla ora ($F2),y sta ($F2),y jmp DecodeAudioCommand ;============================================================================== ; $F063-$F07A: Runtime command $F2 ;============================================================================== AudioCommandF2: jsr FetchAudioSequenceByte pha lda $FD clc adc #$01 tay lda ($F2),y and #$CF sta ($F2),y pla ora ($F2),y sta ($F2),y jmp DecodeAudioCommand ;============================================================================== ; $F07B-$F092: Runtime command $F3 ;============================================================================== AudioCommandF3: jsr FetchAudioSequenceByte pha lda $FD clc adc #$01 tay lda ($F2),y and #$F0 sta ($F2),y pla ora ($F2),y sta ($F2),y jmp DecodeAudioCommand ;============================================================================== ; $F093-$F0A2: Runtime command $F4 ;============================================================================== AudioCommandF4: jsr FetchAudioSequenceByte pha lda $FD clc adc #$02 tay pla sta ($F2),y jmp DecodeAudioCommand ;============================================================================== ; $F0A3-$F0B2: Runtime command $F5 ;============================================================================== AudioCommandF5: jsr FetchAudioSequenceByte pha lda $FD clc adc #$04 tay pla sta ($F2),y jmp DecodeAudioCommand ;============================================================================== ; $F0B3-$F0C2: Runtime command $F6 ;============================================================================== AudioCommandF6: jsr FetchAudioSequenceByte pha lda $FD clc adc #$01 tay pla sta ($F2),y jmp DecodeAudioCommand ;============================================================================== ; $F0C3-$F0E7: Fetch Runtime Command Byte ; ; Uses the 16-bit source pointer stored in runtime-record bytes +5/+6. ; ; Output: ; A = byte read from the command stream ; ; Side effect: ; Advances the source pointer by one and writes it back into bytes +5/+6. ;============================================================================== FetchAudioSequenceByte: lda $FD clc adc #$05 tay lda ($F2),y sta $FE iny lda ($F2),y sta $FF ldx #$00 lda ($FE,x) pha lda $FE dey clc adc #$01 sta ($F2),y lda $FF adc #$00 iny sta ($F2),y pla rts ;============================================================================== ; $F0E8-$F14F: Runtime / Audio Lookup Data ; ; Raw data preserved byte-for-byte. ; ; The first portion is referenced by UpdateAudioChannels through ; $F0E8,X / $F0E9,X as paired mask values. Later portions appear to contain ; additional pointers and command/parameter data. ; ; Semantic subdivision is deferred until all consumers are decoded. ;============================================================================== AudioFrequencyLookupTable: ; $F0E8-$F0F7 .byte $03,$F9,$03,$C0,$03,$8A,$03,$57 .byte $03,$27,$02,$FA,$02,$CF,$02,$A7 ; $F0F8-$F107 .byte $02,$81,$02,$5D,$02,$3B,$02,$1B .byte $02,$F1,$CB,$F3,$14,$F4,$22,$F1 ; $F108-$F117 .byte $3C,$F3,$3F,$F4,$50,$F4,$37,$F1 .byte $E4,$F1,$1D,$F3,$CF,$F1,$62,$F1 ; $F118-$F127 .byte $8D,$F1,$B0,$F1,$07,$F2,$7A,$F2 .byte $61,$F4,$01,$03,$7F,$08,$A2,$0C ; $F128-$F137 .byte $A2,$0C,$A2,$0C,$A2,$0C,$A2,$0C .byte $A2,$0C,$A2,$0C,$A2,$0C,$F0,$03 ; $F138-$F147 .byte $04,$7F,$08,$33,$01,$23,$01,$03 .byte $01,$A2,$01,$92,$01,$72,$01,$52 ; $F148-$F14F .byte $01,$32,$01,$22,$01,$02,$01,$A1 ;============================================================================== ; $F150-$F24F: Runtime / Audio Command Data ; ; Raw data preserved byte-for-byte. ; ; The region contains strongly patterned pairs and command-like values used ; by the late-PRG runtime/audio interpreter. Semantic subdivision is deferred ; until its individual consumers are mapped. ;============================================================================== AudioSequenceData_F150: ; $F150-$F15F .byte $01,$91,$01,$C0,$08,$91,$01,$A1 .byte $01,$02,$01,$22,$01,$32,$01,$52 ; $F160-$F16F .byte $01,$F0,$01,$96,$7F,$28,$82,$01 .byte $92,$01,$A2,$01,$B2,$01,$03,$01 ; $F170-$F17F .byte $13,$01,$23,$01,$33,$01,$43,$01 .byte $53,$01,$33,$01,$23,$01,$13,$01 ; $F180-$F18F .byte $03,$01,$B2,$01,$A2,$01,$92,$01 .byte $82,$01,$72,$01,$F0,$01,$96,$7F ; $F190-$F19F .byte $28,$A2,$01,$B2,$01,$03,$01,$13 .byte $01,$23,$01,$33,$01,$43,$01,$53 ; $F1A0-$F1AF .byte $01,$33,$01,$23,$01,$13,$01,$03 .byte $01,$B2,$01,$A2,$01,$92,$01,$F0 ; $F1B0-$F1BF .byte $01,$96,$7F,$28,$B2,$01,$03,$01 .byte $13,$01,$23,$01,$33,$01,$43,$01 ; $F1C0-$F1CF .byte $53,$01,$33,$01,$23,$01,$13,$01 .byte $03,$01,$B2,$01,$A2,$01,$F0,$02 ; $F1D0-$F1DF .byte $9C,$7F,$28,$31,$01,$71,$01,$A1 .byte $01,$12,$01,$32,$01,$72,$01,$A2 ; $F1E0-$F1EF .byte $01,$13,$01,$F0,$02,$00,$7F,$10 .byte $20,$03,$50,$03,$80,$02,$A0,$02 ; $F1F0-$F1FF .byte $21,$02,$51,$02,$81,$01,$A1,$01 .byte $02,$01,$22,$01,$32,$01,$52,$01 ; $F200-$F20F .byte $F5,$18,$73,$01,$C0,$08,$F0,$01 .byte $03,$7F,$08,$72,$02,$82,$08,$72 ; $F210-$F21F .byte $02,$82,$08,$72,$02,$82,$08,$42 .byte $02,$52,$04,$32,$04,$82,$06,$82 ; $F220-$F22F .byte $08,$B2,$02,$03,$10,$C0,$08,$72 .byte $02,$82,$08,$72,$02,$82,$08,$72 ; $F230-$F23F .byte $02,$82,$08,$42,$02,$52,$04,$32 .byte $04,$82,$06,$82,$08,$42,$02,$52 ; $F240-$F24F .byte $10,$C0,$08,$72,$02,$82,$08,$72 .byte $02,$82,$08,$72,$02,$82,$08,$42 ;============================================================================== ; $F250-$F34F: Runtime / Audio Command Data ; ; Raw data preserved byte-for-byte. ; Semantic subdivision is deferred until the individual stream consumers are ; fully mapped. ;============================================================================== AudioSequenceData_F250: ; $F250-$F25F .byte $02,$52,$04,$32,$04,$82,$06,$82 .byte $08,$A2,$02,$B2,$08,$03,$02,$13 ; $F260-$F26F .byte $0A,$13,$02,$23,$0C,$03,$02,$13 .byte $08,$A2,$02,$B2,$08,$82,$0A,$A2 ; $F270-$F27F .byte $02,$B2,$0A,$72,$02,$82,$10,$C0 .byte $08,$F0,$02,$81,$7F,$40,$80,$08 ; $F280-$F28F .byte $C0,$06,$80,$04,$C0,$02,$80,$08 .byte $C0,$06,$80,$04,$C0,$02,$80,$08 ; $F290-$F29F .byte $C0,$06,$80,$04,$C0,$02,$01,$04 .byte $C0,$01,$11,$04,$C0,$01,$21,$04 ; $F2A0-$F2AF .byte $C0,$01,$31,$04,$C0,$01,$80,$08 .byte $C0,$06,$80,$04,$C0,$02,$80,$08 ; $F2B0-$F2BF .byte $C0,$06,$80,$04,$C0,$02,$80,$08 .byte $C0,$06,$80,$04,$C0,$02,$01,$04 ; $F2C0-$F2CF .byte $C0,$01,$11,$04,$C0,$01,$21,$04 .byte $C0,$01,$31,$04,$C0,$01,$80,$08 ; $F2D0-$F2DF .byte $C0,$06,$80,$04,$C0,$02,$80,$08 .byte $C0,$06,$80,$04,$C0,$02,$80,$08 ; $F2E0-$F2EF .byte $C0,$06,$80,$04,$C0,$02,$01,$04 .byte $C0,$01,$11,$04,$C0,$01,$21,$04 ; $F2F0-$F2FF .byte $C0,$01,$31,$04,$C0,$01,$81,$0A .byte $31,$04,$C0,$01,$21,$04,$C0,$01 ; $F300-$F30F .byte $11,$04,$C0,$01,$B0,$04,$C0,$01 .byte $80,$04,$C0,$01,$70,$04,$C0,$01 ; $F310-$F31F .byte $60,$08,$C0,$02,$70,$08,$C0,$02 .byte $80,$0C,$C0,$06,$F0,$02,$98,$7F ; $F320-$F32F .byte $28,$33,$01,$23,$01,$13,$01,$03 .byte $01,$A2,$01,$82,$01,$72,$01,$52 ; $F330-$F33F .byte $01,$32,$01,$12,$01,$A1,$01,$71 .byte $01,$31,$01,$F0,$01,$42,$7F,$38 ; $F340-$F34F .byte $23,$02,$33,$02,$43,$02,$53,$02 .byte $43,$02,$33,$02,$23,$02,$13,$02 ;============================================================================== ; $F350-$F44F: Runtime / Audio Command Data ; ; Raw command/parameter stream preserved byte-for-byte. ; Semantic subdivision is deferred until all stream consumers are mapped. ;============================================================================== AudioSequenceData_F350: ; $F350-$F35F .byte $23,$02,$33,$02,$43,$02,$33,$02 .byte $23,$02,$13,$02,$03,$02,$13,$02 ; $F360-$F36F .byte $23,$02,$33,$02,$23,$02,$13,$02 .byte $03,$02,$B2,$02,$03,$02,$13,$02 ; $F370-$F37F .byte $23,$02,$13,$02,$03,$02,$B2,$02 .byte $A2,$02,$B2,$02,$03,$02,$23,$02 ; $F380-$F38F .byte $03,$02,$B2,$02,$A2,$02,$92,$02 .byte $A2,$02,$B2,$02,$03,$02,$B2,$02 ; $F390-$F39F .byte $A2,$02,$92,$02,$82,$02,$92,$02 .byte $A2,$02,$03,$02,$A2,$02,$92,$02 ; $F3A0-$F3AF .byte $82,$02,$F2,$00,$F3,$00,$F5,$18 .byte $31,$01,$71,$01,$B1,$01,$32,$01 ; $F3B0-$F3BF .byte $72,$01,$B2,$01,$33,$01,$73,$01 .byte $C0,$02,$31,$01,$71,$01,$B1,$01 ; $F3C0-$F3CF .byte $32,$01,$72,$01,$B2,$01,$33,$01 .byte $73,$01,$F0,$01,$81,$7F,$40,$31 ; $F3D0-$F3DF .byte $08,$32,$08,$A1,$08,$71,$08,$32 .byte $07,$A1,$07,$71,$0C,$C0,$06,$41 ; $F3E0-$F3EF .byte $08,$42,$08,$B1,$08,$81,$08,$42 .byte $07,$B1,$07,$81,$0C,$C0,$06,$31 ; $F3F0-$F3FF .byte $08,$32,$08,$A1,$08,$71,$08,$32 .byte $07,$A1,$07,$71,$0C,$C0,$06,$61 ; $F400-$F40F .byte $05,$71,$05,$81,$05,$81,$05,$91 .byte $05,$A1,$05,$A1,$05,$B1,$05,$02 ; $F410-$F41F .byte $05,$32,$0C,$F0,$02,$82,$7F,$40 .byte $30,$18,$A0,$08,$30,$0E,$A0,$0C ; $F420-$F42F .byte $C0,$06,$40,$18,$B0,$08,$40,$0E .byte $B0,$0C,$C0,$06,$30,$18,$A0,$08 ; $F430-$F43F .byte $30,$0E,$A0,$0C,$C0,$06,$A0,$0F .byte $01,$0F,$21,$0F,$31,$0C,$F0,$01 ; $F440-$F44F .byte $9F,$7F,$28,$01,$01,$41,$01,$71 .byte $01,$A1,$01,$02,$01,$F5,$18,$F0 ;============================================================================== ; $F450-$F49A: Runtime / Audio Command Data ; ; Raw command data preserved byte-for-byte. ;============================================================================== AudioSequenceData_F450: ; $F450-$F45F .byte $01,$9F,$7F,$28,$02,$01,$A1,$01 .byte $71,$01,$41,$01,$F5,$18,$01,$01 ; $F460-$F46F .byte $F0,$01,$9F,$7F,$28,$52,$01,$92 .byte $01,$03,$01,$33,$01,$F5,$18,$53 ; $F470-$F47F .byte $03,$F5,$28,$53,$01,$33,$01,$03 .byte $01,$92,$01,$F5,$18,$52,$03,$92 ; $F480-$F48F .byte $01,$13,$01,$43,$01,$73,$01,$F5 .byte $18,$93,$0A,$F5,$28,$93,$01,$73 ; $F490-$F49A .byte $01,$43,$01,$13,$01,$F5,$18,$92 .byte $03,$F0,$FF ;============================================================================== ; $F49B-$F64F: PRG Padding ;============================================================================== .res $0B5B, $FF ;============================================================================== ; $FFF6-$FFF9: Final ROM Data ; ; Notes: ; $FFF6/$FFF7 are read directly by DrawMazeNametable: ; ; lda $FFF7 ; sta $01 ; lda $FFF6 ; sta $00 ; ; Therefore the first two bytes form the little-endian value $ECEC. ; ; $FFF8/$FFF9 form the little-endian value $C10E. ;============================================================================== MazeDataPointerBytes: .byte $EC,$EC .byte $0E,$C1 ;============================================================================== ; $FFFA-$FFFF: Interrupt Vectors ;============================================================================== .org $FFFA CpuInterruptVectors: .word NmiHandler ; NMI .word Reset ; Reset .word IrqHandler ; IRQ/BRK