Files
disasm/Pac-Man (USA) (Namco)/copilot/prg.s
T
tmerritt 25423d3828 Roms Completed:
Baseball
  Clu Clu Land
  Excitebike
  Pac-Man (Namco)
  Tennis
  Balloon Fight
  Bomberman (U)
  Burger Time (U)
  Defender 2 (U)
2026-10-15 09:52:48 -04:00

14333 lines
301 KiB
ArmAsm

;----------------------------------------------------
; 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