commit 8683e2f94a97cec0d13b5385e1b5e4929569e4cf Author: Trevor Merritt Date: Wed Oct 7 08:03:54 2026 -0400 Initial commit diff --git a/Excitebike (JU)/copilot/Makefile b/Excitebike (JU)/copilot/Makefile new file mode 100644 index 0000000..3f9ba6f --- /dev/null +++ b/Excitebike (JU)/copilot/Makefile @@ -0,0 +1,10 @@ +all: reconstructed_prg.bin verify +prg.o: prg.s symbols.inc prg.bin + ca65 -g -o $@ prg.s +reconstructed_prg.bin: prg.o linker.cfg + ld65 -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o $@ prg.o +verify: reconstructed_prg.bin prg.bin + cmp reconstructed_prg.bin prg.bin + @echo BYTE-IDENTICAL +clean: + rm -f prg.o reconstructed_prg.bin reconstructed.map reconstructed.lbl diff --git a/Excitebike (JU)/copilot/chr.bin b/Excitebike (JU)/copilot/chr.bin new file mode 100644 index 0000000..5922266 Binary files /dev/null and b/Excitebike (JU)/copilot/chr.bin differ diff --git a/Excitebike (JU)/copilot/header.bin b/Excitebike (JU)/copilot/header.bin new file mode 100644 index 0000000..d5ba821 Binary files /dev/null and b/Excitebike (JU)/copilot/header.bin differ diff --git a/Excitebike (JU)/copilot/linker.cfg b/Excitebike (JU)/copilot/linker.cfg new file mode 100644 index 0000000..d9d421d --- /dev/null +++ b/Excitebike (JU)/copilot/linker.cfg @@ -0,0 +1,6 @@ +MEMORY { + PRG: start = $C000, size = $4000, file = %O, fill = yes; +} +SEGMENTS { + PRG: load = PRG, type = ro; +} diff --git a/Excitebike (JU)/copilot/prg.bin b/Excitebike (JU)/copilot/prg.bin new file mode 100644 index 0000000..5a6a2c3 Binary files /dev/null and b/Excitebike (JU)/copilot/prg.bin differ diff --git a/Excitebike (JU)/copilot/prg.s b/Excitebike (JU)/copilot/prg.s new file mode 100644 index 0000000..915603d --- /dev/null +++ b/Excitebike (JU)/copilot/prg.s @@ -0,0 +1,14112 @@ +; 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 + + +; Variables +PpuControlShadow = $10 +PpuMaskShadow = $11 +Controller1Held = $14 +Controller1Pressed = $15 +Controller2Held = $16 +Controller2Pressed = $17 +ControllerState = Controller1Held +NmiDispatchMode = $40 +NmiState = $41 +MenuSelectionIndex = $42 +NmiSequenceStep = $44 +NmiSyncState = $48 +InputActionLatch = $49 +RiderMotionState = $A4 +OamBuffer = $0200 +PersistentRecord0 = $0580 +PersistentRecord1 = $05A3 +ResetMagic1 = $05CE +ResetMagic2 = $05CF +PersistentBuffer = $06E0 +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 +MENU_HORIZONTAL_COUNT=$05 +MENU_VERTICAL_COUNT=$06 +MENU_ACTION_NONE=$00 +MENU_ACTION_ACCEPTED=$04 +NMI_SYNC_PENDING=$FF +NMI_SYNC_COMPLETE=$01 +RESET_MAGIC_1=$A5 +RESET_MAGIC_2=$5A +PPUCTRL_NMI_ENABLE=$80 + +WorkingBuffer = $05E0 +PERSISTENT_BUFFER_SIZE = $0100 + +StateTargetCA85 = $CA85 + +; Serial I/O helpers using JOY1 (verified structure; peripheral identity unknown) +WriteSerialTiming = $C62D +WaitSerialPulse = $C639 +TransmitPulseShort = $C64E +TransmitPulseLong = $C653 +TransmitByte = $C66E +IncrementSerialCount = $C68A +ReceiveSerialBit = $C691 +ReceiveByte = $C6A5 + +; Block protocol helpers +TransmitRuntimeData = $C6C8 +TransmitDataBlock = $C6D1 +SetupTransmitRamBlock = $C716 +SetupTransmitWorkingBuffer = $C726 +SetTransmitPreambleCounts = $C734 +ReceiveRuntimeData = $C739 +ReceiveDataBlock = $C792 +SerialBlockLength = $0D +SerialBlockPtr = $0E +SerialBitCountLo = $08 +SerialBitCountHi = $09 + +; App Specific Variables +TMP00 = $00 +TMP01 = $01 + + +; App Specific Magic Values +RIDER_MOTION_DISABLED = $01 +RIDER_MOTION_ACTIVE = $02 +RIDER_MOTION_TRANSITION = $03 +RIDER_MOTION_COMPLETE = $04 + + +.segment "PRG" + +; --------------------------------------------------------------------------- +; $C000-$C01B +; NMI state handler pointer table. +; 14 little-endian handler addresses, one for each NMI state $00-$0D. +; --------------------------------------------------------------------------- + +NmiStateHandlerTable: + .word NmiState00 ; state $00 + .word $C395 ; state $01 + .word UpdateGameplayRuntime ; state $02 + .word DispatchNmiSequenceStep ; state $03 + .word NmiState04_07 ; state $04 + .word UpdateGameplayRuntime ; state $05 + .word DispatchNmiSequenceStep ; state $06 + .word NmiState04_07 ; state $07 + .word $C3BD ; state $08 + .word UpdateRaceRuntime ; state $09 + .word UpdateGameplayRuntime ; state $0A + .word DispatchNmiSequenceStep ; state $0B + .word $C43D ; state $0C + .word $C41C ; state $0D + +; --------------------------------------------------------------------------- +; $C01C-$C029 +; NMI state -> sequence index. +; Indexed directly by NmiState. +; --------------------------------------------------------------------------- + +NmiStateToSequenceIndex: + .byte $00, $01, $02, $03 + .byte $04, $02, $03, $04 + .byte $06, $05, $02, $03 + .byte $07, $08 + +; --------------------------------------------------------------------------- +; $C02A-$C032 +; Low bytes of nine NMI sequence pointers. +; --------------------------------------------------------------------------- +NmiSequencePointerLow: + .byte $3C, $40, $46, $52, $5C + .byte $64, $70, $7A, $76 + +; --------------------------------------------------------------------------- +; $C033-$C03B +; High bytes of nine NMI sequence pointers. +; +; All nine streams currently reside in page $C0. +; --------------------------------------------------------------------------- + +NmiSequencePointerHigh: + .byte $C0, $C0, $C0, $C0, $C0 + .byte $C0, $C0, $C0, $C0 + + +; --------------------------------------------------------------------------- +; $C03C-$C07F +; NMI sequence handler streams. +; +; These are selected through NmiSequencePointerLow / +; NmiSequencePointerHigh. +; --------------------------------------------------------------------------- + +; Sequence index 0 +; $C03C-$C03F +NmiSequence0C03C: + .word $C2BD + .word $C514 + +; Sequence index 1 +; $C040-$C045 +NmiSequence1C040: + .word SetSerialUpdateSelector06 + .word $C3F3 + .word $C514 + + +; Sequence index 2 +; $C046-$C051 +NmiSequence2C046: + .word SerialUpdateSelectorCluster + .word ClearRuntimeZeroPage + .word InitializeCourseRuntime + .word SetSerialUpdateSelector17 + .word SetSerialUpdateSelector0E + .word InitializeGameRuntime + + +; Sequence index 3 +; $C052-$C05B +NmiSequence3C052: + .word SetSerialUpdateSelector06 + .word SetSerialUpdateSelector04 + .word SelectNmiUpdateForState + .word CallC92CWithZero + .word CompareAndUpdateRecord + + +; Sequence index 4 +; $C05C-$C063 +NmiSequence4C05C: + .word SetSerialUpdateSelector06 + .word SetSerialUpdateSelector09 + .word Prepare0300Structure + .word $C514 + + +; Sequence index 5 +; $C064-$C06F +NmiSequence5C064: + .word $C551 + .word ClearRuntimeZeroPage + .word InitializeCourseRuntime + .word SetSerialUpdateSelector17 + .word $C562 + .word $C5C2 + + +; Sequence index 6 +; $C070-$C075 +NmiSequence6C070: + .word SetSerialUpdateSelector06 + .word $C575 + .word $C514 + + +; Sequence index 8 +; $C076-$C079 +; +; Note that sequence index 8 points here, while index 7 points to $C07A. +NmiSequence8C076: + .word $C582 + .word $C518 + + +; Sequence index 7 +; $C07A-$C07F +NmiSequence7C07A: + .word ClearRuntimeZeroPage + .word RestoreWorkingRecordFields + .word $C518 + + +; --------------------------------------------------------------------------- +; $C080-$C082 +; Initial/menu Y positions. +; Indexed by menu selection. +; --------------------------------------------------------------------------- + +MenuInitialYTable: + .byte $80, $90, $A0 + +; --------------------------------------------------------------------------- +; $C083-$C087 +; Horizontal menu X positions. +; Five entries. +; --------------------------------------------------------------------------- + +MenuHorizontalXTableC083: + .byte $48, $60, $78, $90, $A8 + + +; --------------------------------------------------------------------------- +; $C088-$C08A +; Initial sprite X positions. +; Used by InitializeSprite0. +; --------------------------------------------------------------------------- + +Sprite0InitialXTableC088: + .byte $50, $48, $4C + +; --------------------------------------------------------------------------- +; $C08B-$C08F +; Vertical menu Y positions. +; Five entries. +; --------------------------------------------------------------------------- + +MenuVerticalYTableC08B: + .byte $30, $48, $78, $90, $A8 + +; --------------------------------------------------------------------------- +; $C090 +; Adjacent parameter preceding the persistent-record template. +; Purpose not yet established. +; --------------------------------------------------------------------------- + +DataC090: + .byte $C0 + + +; --------------------------------------------------------------------------- +; $C091-$C0AF +; Persistent-record initialization template. +; +; Copied into both persistent record buffers by InitializePersistentRam. +; 31 bytes total. +; --------------------------------------------------------------------------- + +PersistentRecordInitialDataC091: + .byte $01, $10, $00 + .byte $01, $10, $00 + .byte $01, $10, $00 + .byte $01, $0C, $00 + .byte $01, $06, $00 + .byte $00, $01, $18, $00 + .byte $01, $16, $00 + .byte $01, $0A, $00 + .byte $01, $14, $00 + .byte $01, $06, $00 + +; --------------------------------------------------------------------------- +; $C0B0-$C0D7 +; Parameter data with several overlapping indexed views. +; --------------------------------------------------------------------------- + +DataC0B0: + .byte $09, $0B, $0D, $0F + +DataC0B4: + .byte $0E, $1A, $26, $32 + +DataC0B8: + .byte $38, $48, $58, $68 + +; $C0BC +; Added to $90,X by the CE29 position helper. +SlotPositionAddTableC0BC: + .byte $18, $3F, $28, $20 + +; $C0C0 +DataC0C0: + .byte $28, $38, $0C, $00 + +; $C0C4 +DataC0C4: + .byte $3C, $1C, $C0, $7F + +; $C0C8 +; Indexed by ($B0,X >> 1), compared against $AC,X. +SlotACTargetTableC0C8: + .byte $06, $02 + +; $C0CA +; Alternate $AC,X target view. +SlotACAlternateTargetTable: + .byte $0A, $0B + +; $C0CC +; Indexed by ($B0,X >> 1), compared against $90,X. +SlotPositionThresholdTable: + .byte $01, $B0 + +; $C0CE +; Paired with $C0D1 by the CE29 boundary helper. +; Also used as the $20 BIT mask by UpdateMenuSelection. +Slot90BoundaryTable: + .byte $20, $40, $7F + +; $C0D1 +; Paired with $Slot90BoundaryTable and loaded into $94,X. +Slot94BoundaryTable: + .byte $03, $03, $01 + + +; $C0D4 +; Indexed by ($B0,X >> 1) and stored into $26,X. +SlotTimerReloadTable: + .byte $06, $04, $40, $58 + +; --------------------------------------------------------------------------- +; $C0D8-$C0D9 +; Currently unidentified adjacent data. +; --------------------------------------------------------------------------- + +DataC0D8: + .byte $48, $48 + +; --------------------------------------------------------------------------- +; $C0DA-$C0DD +; Four-entry sequence position/setup table. +; Used by StateSequenceStep2Process and SequenceSetupLoadIndexedParameters. +; --------------------------------------------------------------------------- +SequencePositionTable: + .byte $78, $70, $80, $B0 +; --------------------------------------------------------------------------- +; $C0DE-$C0E1 +; Four-entry sequence limit/parameter table. +; --------------------------------------------------------------------------- +SequenceLimitTable: + .byte $37, $3F, $3F, $47 +; --------------------------------------------------------------------------- +; $C0E2-$C0E4 +; Three-entry parameter table loaded into $02. +; --------------------------------------------------------------------------- +SequenceParameter02Table: + .byte $B7, $B9, $B9 + + +; --------------------------------------------------------------------------- +; $C0E5-$C0E7 +; Three-entry flag/configuration table loaded into $0B. +; --------------------------------------------------------------------------- + +SequenceFlagsTable: + .byte $01, $01, $41 + + +; --------------------------------------------------------------------------- +; $C0E8-$C0EB +; Four-entry structure offset table. +; --------------------------------------------------------------------------- + +SequencePointerOffsetTable: + .byte $04, $0C, $14, $1C + +; --------------------------------------------------------------------------- +; $C0EC-$C0F5 +; Indexed parameter data. +; +; $C0EC and $C0ED are both used as indexed table bases. +; Keep the physical bytes literal until their complete index ranges and +; semantics are established. +; --------------------------------------------------------------------------- + +IndexedParameterTableC0EC: + .byte $21 + +; Secondary overlapping indexed view at $C0ED. +IndexedParameterTableC0ED: + .byte $F2, $23, $43, $22, $32, $22, $8C, $24, $CF + + +; --------------------------------------------------------------------------- +; $C0F6-$C110 +; Low bytes of 27 PPU update-stream pointers. +; --------------------------------------------------------------------------- + +PpuUpdatePointerLow: + .byte $01, $70, $4B, $D4, $17, $4A, $3A, $9E + .byte $B4, $96, $C8, $DE, $EF, $05, $24, $FA + .byte $23, $14, $D6, $E9, $FC, $0F, $22, $35 + .byte $50, $31, $A1 + + +; --------------------------------------------------------------------------- +; $C111-$C12B +; High bytes of 27 PPU update-stream pointers. +; --------------------------------------------------------------------------- + +PpuUpdatePointerHigh: + .byte $03, $D4, $D6, $D6, $D7, $D8, $D6, $D5 + .byte $D5, $D7, $D5, $D5, $D5, $D6, $D5, $D7 + .byte $D6, $D6, $D3, $D3, $D3, $D4, $D4, $D4 + .byte $D4, $D6, $00 + +; --------------------------------------------------------------------------- +; $C12C-$C12F +; Low bytes of four NMI sequence-step dispatch targets. +; --------------------------------------------------------------------------- + +NmiSequenceStepPointerLow: + .byte $3B, $77, $A4, $6B + + +; --------------------------------------------------------------------------- +; $C130-$C133 +; High bytes of four NMI sequence-step dispatch targets. +; --------------------------------------------------------------------------- + +NmiSequenceStepPointerHigh: + .byte $CB, $CB, $CB, $CC + +; --------------------------------------------------------------------------- +; $C134-$C13B +; Eight-byte template copied into the $0301 structure by +; StateSequenceStep2CopyTable. +; --------------------------------------------------------------------------- + +StateSequenceStep2Template: + .byte $00, $FC, $FC, $FC + .byte $79, $04, $72, $22 + + +; --------------------------------------------------------------------------- +; $C13C-$C141 +; Six-byte template used by Prepare0300Structure. +; --------------------------------------------------------------------------- + +Prepare0300StructureTemplateC13C: + .byte $04, $21, $6F, $01, $00, $00 + + +; --------------------------------------------------------------------------- +; $C142-$C146 +; Five bytes of currently unidentified parameter data. +; Keep literal until its consumer is established. +; --------------------------------------------------------------------------- + +DataC142: + .byte $60, $9F, $00, $17, $04 + + +; --------------------------------------------------------------------------- +; $C147-$C14F +; Nine-entry table used by Initialize02C0Entries. +; X runs from 8 down through 0. +; --------------------------------------------------------------------------- + +Initialize02C0EntryTableC147: + .byte $DE, $DC, $E1 + .byte $F8, $FC, $EF + .byte $E1, $EE, $C9 + + +; --------------------------------------------------------------------------- +; $C150-$C157 +; Eight-byte template used by Copy02C0Template. +; X runs from 7 down through 0. +; --------------------------------------------------------------------------- + +Copy02C0TemplateC150: + .byte $44, $C9, $00, $84 + .byte $44, $F9, $00, $74 + + +; --------------------------------------------------------------------------- +; $C158-$C15F +; Currently unidentified data. +; --------------------------------------------------------------------------- + +DataC158: + .byte $07, $23, $78, $04 + .byte $1D, $F8, $FE, $FE + + +; --------------------------------------------------------------------------- +; $C160 +; Indexed by $43 and stored into $0315. +; Exact table extent remains to be established. +; --------------------------------------------------------------------------- + +State0315TableC160: + .byte $29, $01, $02, $00, $02, $29 + + +; --------------------------------------------------------------------------- +; $C166-$C169 +; Four-entry table indexed by (($3F >> 2) & 3). +; Stored into $0316. +; --------------------------------------------------------------------------- + +State0316TableC166: + .byte $30, $35, $25, $35 + + +; --------------------------------------------------------------------------- +; $C16A +; Indexed by $43 and stored into $0317. +; Exact table extent remains to be established. +; --------------------------------------------------------------------------- + +State0317TableC16A: + .byte $22, $26, $1C, $22, $22, $22 + +; --------------------------------------------------------------------------- +; $C170-$C173 +; Four-entry table indexed by (($3F >> 2) & 3). +; Stored into $0318. +; --------------------------------------------------------------------------- + +State0318TableC170: + .byte $26, $06, $36, $26 + + +; --------------------------------------------------------------------------- +; $C174-$C177 +; Currently unidentified adjacent data. +; --------------------------------------------------------------------------- + +DataC174: + .byte $01, $40, $7F, $09 + + +; --------------------------------------------------------------------------- +; $C178-$C17F +; Eight-byte runtime/OAM template copied to $0228-$022F. +; +; Structurally this is two four-byte OAM entries: +; +; $C7, $DB, $00, $E0 +; $C7, $DD, $00, $E8 +; --------------------------------------------------------------------------- +RuntimeOamTemplateC178: + .byte $C7, $DB, $00, $E0 + .byte $C7, $DD, $00 + +; $C17F +; The final byte belongs to the OAM template, but $C17F is also used as +; the base for the receive-header comparison with Y = 1..4. +ReceiveHeaderTableBaseC17F: + .byte $E8 + +; --------------------------------------------------------------------------- +; $C180-$C183 +; Expected four-byte header used by ReceiveRuntimeDataCode. +; --------------------------------------------------------------------------- +ExpectedReceiveHeaderC180: + .byte $17, $01, $00, $01 + +Reset: + sei + cld + lda #$00 + sta PPUCTRL + ldx #$FF + txs +@vblank1: lda PPUSTATUS + and #$80 + beq @vblank1 +@vblank2: lda PPUSTATUS + and #$80 + beq @vblank2 + ldy #$07 + lda ResetMagic1 + cmp #RESET_MAGIC_1 + bne @clear_select + lda ResetMagic2 + cmp #RESET_MAGIC_2 + bne @clear_select + ldy #$04 +@clear_select: + sty $01 + ldy #$00 + sty $00 + tya +@clear_ram: + sta ($00),y + dey + bne @clear_ram + dec $01 + bpl @clear_ram + lda ResetMagic1 + bne :+ + jsr InitializePersistentRam +: sta APUDMALOAD + lda #$06 + sta PPUMASK + sta $18 + jsr InitializeRuntimePpuState + lda #$90 + jsr SetPpuControl +@main: + jsr ShiftRuntimeState + lda NmiSyncState + bmi @main + beq @main + jsr DisableNmi + lda NmiSyncState + cmp #NMI_SYNC_COMPLETE + bne @alternate + jsr TransmitRuntimeDataCode +@finish: + lda #NMI_SYNC_PENDING + sta NmiSyncState + jsr WaitForVblankEndAndEnableNmi + jmp @main +@alternate: + jsr ReceiveRuntimeDataCode + jmp @finish + +InitializePersistentRam: + ldx #$1E +@copy: + lda PersistentRecordInitialDataC091,x + sta PersistentRecord0,x + sta PersistentRecord1,x + dex + bpl @copy + ldx #$01 + stx PersistentBuffer +@buf: + lda #$40 + sta PersistentBuffer,x + inx + cpx #$CF + bcc @buf + lda #$09 + sta PersistentBuffer,x + lda #RESET_MAGIC_1 + sta ResetMagic1 + lda #RESET_MAGIC_2 + sta ResetMagic2 + ldx #$00 + lda #$0A +@tails: + sta PersistentRecord0+$20,x + sta PersistentRecord1+$20,x + lda #$00 + inx + cpx #$03 + bcc @tails + rts + +; NMI handler $C23B-$C27E. +NMI: + jsr DisableNmi + lda $45 + bne @skip_mask + lda PpuMaskShadow + ora #$1A + sta PPUMASK + sta PpuMaskShadow +@skip_mask: + lda #$00 + sta OAMADDR + lda #$02 + sta OAMDMA + jsr NmiPpuUpdate + lda $47 + beq @skip_optional + jsr UpdateRiderLanePositionStates + jsr NmiRuntimeUpdate +@skip_optional: + lda $03B1 + ora $03B2 + bne @skip_timed + inc $3F + jsr UpdateRuntimeCounters +@skip_timed: + jsr ReadControllers + jsr UpdateAudio + jsr DispatchNmiState + lda PPUSTATUS + jsr WaitForVblankEndAndEnableNmi + rti + +NmiPpuUpdate: + ldx $45 +; $C289? +ProcessPpuUpdateByIndex: + lda PpuUpdatePointerLow,x + sta $00 + lda PpuUpdatePointerHigh,x + sta $01 + jsr ProcessPpuUpdateStream + lda #$00 + sta $0300 + sta $0301 + sta $45 + lda #$3F + sta PPUADDR + lda #$00 + sta PPUADDR + sta PPUADDR + sta PPUADDR + rts + +; NMI dispatcher $C2A9-$C317. +DispatchNmiState: + ldx NmiDispatchMode + beq @sequence_mode + lda NmiState + asl a + tay + lda NmiStateHandlerTable,y + sta $00 + iny + lda NmiStateHandlerTable,y + ; High byte intentionally remains in A; $CB36 stores it to $01. + jmp $CB36 + + ; Alternate initialization path $C2BD-$C2EC. +@alternate_init: + lda #$04 + sta $03F8 + lsr a + sta $45 + ldy #$00 + jsr $C409 + ldx $03F0 + bne @have_fb + lda #$01 + sta $FB +@have_fb: + cpx #$02 + bmi @advance_index + ldx #$FF +@advance_index: + inx + stx $03F0 + lda #$38 + sta $31 + lda #$0F + sta APUCTRL + lda MenuInitialYTable + sta OamBuffer +@sequence_abort: + rts + +@sequence_mode: + jsr DisablePpuRendering +@wait: + lda $30 + ; Busy state returns through the preceding alternate-path RTS at $C2EC. + bne @sequence_abort + ldx NmiState + lda NmiStateToSequenceIndex,x + tax + lda NmiSequencePointerLow,x + sta $02 + lda NmiSequencePointerHigh,x + sta $03 + ldy NmiSequenceStep + inc NmiSequenceStep + dey + bmi InitializeRuntimePpuState + tya + asl a + tay + lda ($02),y + sta $00 + iny + lda ($02),y + jmp $CB36 + +InitializeRuntimePpuState: + jsr FillOamShadowWithF8 + jsr ProcessTwoIndexedEntries + jsr $D1A9 + sta $12 + sta $50 + sta $13 + sta $51 + sta $FC +ResetRuntimePpuFieldsC32F: + lda #$10 + bne SetPpuControl +DisableNmi: + lda PpuControlShadow + and #$7F +SetPpuControl: + sta PPUCTRL + sta PpuControlShadow + rts +WaitForVblankEndAndEnableNmi: +@wait: lda PPUSTATUS + and #PPUCTRL_NMI_ENABLE + bne @wait + lda PpuControlShadow + ora #PPUCTRL_NMI_ENABLE + bne SetPpuControl + +; State-handler block $C346-$C454. +NmiState00: + lda $31 + bne @select + sta $43 + sta $4A + lda #$0F + sta $03F7 + lda #$02 + sta NmiState + bne @finish_select +@select: + lda #$03 + jsr UpdateMenuSelection + txa + bne @selection_done + sta $03F2 + lda MenuInitialYTable,y + jmp SetSprite0Y +@selection_done: + ldy MenuSelectionIndex + ldx #$00 + stx $03F7 + stx $43 + stx MenuSelectionIndex + inx + stx $03F3 + stx $4A + stx $03F0 + cpy #$02 + bne :+ + ldx #$08 +: stx NmiState + sty $03F2 +@finish_select: + jsr EnterNmiSequenceMode + lda $03F7 + eor #$0F + sta APUCTRL + rts + +NmiState01: + lda Controller1Pressed + and #$F3 + sta Controller1Pressed + and #PAD_HORIZONTAL + asl a + asl a + ora Controller1Pressed + sta Controller1Pressed + lda #MENU_HORIZONTAL_COUNT + jsr UpdateMenuSelection + txa + beq MoveHorizontalMarker + lda MenuSelectionIndex + sta $43 +AdvanceNmiStateAndEnterNmiSequence: + inc NmiState +EnterNmiSequenceMode: + lda #$00 + sta NmiDispatchMode + sta NmiSequenceStep + rts +MoveHorizontalMarker: + lda MenuHorizontalXTableC083,y + bne SetSprite0X + +NmiState08: + lda #MENU_VERTICAL_COUNT + jsr UpdateMenuSelection + txa + beq @move_vertical_marker + lda MenuSelectionIndex + cmp #$02 + beq @enter_sequence + bcs @higher_choice + sta $03F2 + inc NmiState +@enter_sequence: + ; Original bytes at $C3D2 are JMP $C3AF. Keep that exact target. + jmp AdvanceNmiStateAndEnterNmiSequence +@higher_choice: + cmp #MENU_HORIZONTAL_COUNT + beq @choice5 + clc + adc #$09 + sta NmiState + lda #$00 + sta MenuSelectionIndex + beq EnterNmiSequenceMode +@choice5: + lsr a + sta MenuSelectionIndex + jmp ResetRuntimeZeroPagePreserveSelection +@move_vertical_marker: + ldy MenuSelectionIndex + lda MenuVerticalYTableC08B,y +SetSprite0Y: + sta OamBuffer + rts + +InitializeSelectionMarker: + ldx #$04 + stx $FB + dex + stx $45 + ldy #$01 + jsr InitializeSprite0 + sta MenuSelectionIndex + lda #$2D + sta $34 + lda #$B0 + bne SetSprite0Y + +InitializeSprite0: + lda #$FA + sta OamBuffer+1 + lda Sprite0InitialXTableC088,y +SetSprite0X: + sta OamBuffer+3 + lda #$00 + sta OamBuffer+2 + sta $46 + rts + +NmiState0D: + lda #$02 + ldx NmiSequenceStep + beq StateHandshakeComplete + lda NmiSyncState + cmp #NMI_SYNC_PENDING + bne StateHandshakeReturn + ldx #$02 +@copy_record_tails: + lda $65,x + sta PersistentRecord0+$20,x + lda $68,x + sta PersistentRecord1+$20,x + dex + bpl @copy_record_tails + jsr CopyWorkingBufferToPersistentEC3B + jmp ContinueAsyncHandshake + +NmiState0C: + lda NmiSequenceStep + beq State0CSetComplete + lda NmiSyncState + cmp #NMI_SYNC_PENDING + bne StateHandshakeReturn +ContinueAsyncHandshake: + lda #$40 + sta $FB + jmp StateSequenceStep3 +State0CSetComplete: + lda #NMI_SYNC_COMPLETE +StateHandshakeComplete: + sta NmiSyncState + sta NmiSequenceStep +StateHandshakeReturn: + rts + +; $C455-$C464: choose an NMI update selector from state/$46. +SelectNmiUpdateForState: + ldy #$08 + lda NmiState + cmp #$08 + bcs @store + lda $46 + beq SharedReturnC464 + dey +@store: + sty $45 +SharedReturnC464: + rts + +; $C465-$C484: shared handler used by states $04/$07. +NmiState04_07: + lda $34 + beq @advance + lda #$06 + jsr $C92C + lda $3F + and #$10 + bne SharedReturnC464 + jmp StateHelperCAFA +@advance: + jsr AdvanceNmiStateAndEnterNmiSequence + lda NmiState + cmp #$08 + bcc @store_state + lda #$02 +@store_state: + sta NmiState + rts + +; --------------------------------------------------------------------------- +; $C485-$C521 +; Compare persistent/reference record values and prepare runtime state. +; Literal transcription of the original ROM topology. +; --------------------------------------------------------------------------- + +CompareAndUpdateRecord: + jsr LoadPersistentTriplet + + lda $05 + cmp $68 + bne C49A_FirstDifference + + lda $06 + cmp $69 + bne C49A_FirstDifference + + lda $07 + cmp $6A + beq C4A9_ValueAccepted + + +C49A_FirstDifference: + bcc C4AD_CheckReference + + ldx #$00 + +C49E_CopyTriplet: + lda $68,x + sta PersistentRecord0,y + inx + iny + cpx #$03 + bmi C49E_CopyTriplet + + +C4A9_ValueAccepted: + lda #$01 + bne C512_FinalizeValue + + +C4AD_CheckReference: + lda NmiState + cmp #$08 + bcs C4CC_CalculateValue + + jsr LoadReferenceTriplet + + lda $05 + cmp $68 + bne C4C6_ReferenceDifference + + lda $06 + cmp $69 + bne C4C6_ReferenceDifference + + lda $07 + cmp $6A + +C4C6_ReferenceDifference: + bcc C4CC_CalculateValue + + lda #$02 + bne C512_FinalizeValue + + +C4CC_CalculateValue: + lda $03F8 + sta $0A + asl a + sta $09 + + lda $68 + sec + sbc $05 + tay + + lda #$00 + + +C4DC_Accumulate: + dey + bmi C4E8_DifferenceReady + + clc + adc #$3C + bcc C4DC_Accumulate + + +C4E4_Overflow: + lda #$FF + bne C50B_SubtractLimit + + +C4E8_DifferenceReady: + sta $08 + + lda $69 + sec + sbc $06 + bcs C4F7_AddSaved + + clc + adc $08 + jmp C4FC_CompareLimit + + +C4F7_AddSaved: + clc + adc $08 + bcs C4E4_Overflow + + +C4FC_CompareLimit: + cmp $09 + bcs C50B_SubtractLimit + + clc + adc $09 + + +C503_ShiftLoop: + lsr $0A + beq C512_FinalizeValue + + lsr a + jmp C503_ShiftLoop + + +C50B_SubtractLimit: + sec + sbc $09 + lsr a + clc + adc #$04 + + +C512_FinalizeValue: + sta $53 + + lda #$18 + sta $45 + + lda #$01 + sta InputActionLatch + sta $40 + + lsr a + sta NmiSequenceStep + + rts + + +; --------------------------------------------------------------------------- +; $C522-$C539 +; Load three-byte reference record from ROM into $05-$07. +; --------------------------------------------------------------------------- + +LoadReferenceTriplet: + lda $43 + sta $02 + + jsr CalculateReferenceRecordOffset + tay + + lda PersistentRecordInitialDataC091,y + sta $05 + + lda $C092,y + sta $06 + + lda $C093,y + sta $07 + + rts + +; --------------------------------------------------------------------------- +; $C53A-$C550 +; Load three-byte persistent record into $05-$07. +; --------------------------------------------------------------------------- + +LoadPersistentTriplet: + lda $43 + sta $02 + + jsr CalculatePersistentRecordOffset + + lda PersistentRecord0,y + sta $05 + + lda PersistentRecord0+1,y + sta $06 + + lda PersistentRecord0+2,y + sta $07 + + rts + +; $C551-$C585: update-selector/setup entry cluster. + +SerialUpdateSelectorCluster: + lda #$12 + ldx NmiState + cpx #$08 + bcs SetSerialUpdateSelector + + clc + adc $43 + bne SetSerialUpdateSelector + +; $C55E +SetSerialUpdateSelector06: + lda #$06 + bne SetSerialUpdateSelector + + +; $C562 +SetSerialUpdateSelector05: + lda #$05 + bne SetSerialUpdateSelector + + +; $C566 +SetSerialUpdateSelector0E: + lda #$0E + bne SetSerialUpdateSelector + + +; $C56A +SetSerialUpdateSelector04: + lda #$04 + sta $33 + + +; $C56E +SetSerialUpdateSelector: + sta $45 + rts + + +; $C571 +SetSerialUpdateSelector09: + lda #$09 + bne SetSerialUpdateSelector + + +; $C575 +InitializeSerialSelectionMarker: + lda #$05 + sta $43 + + ldy #$02 + jsr InitializeSprite0 + + lda #$0F + bne SetSerialUpdateSelector + +; $C582 +SetSerialUpdateSelector11: + lda #$11 + bne SetSerialUpdateSelector + +; $C586-$C5AB: restore working record fields and shared selector entries. +RestoreWorkingRecordFields: + ldx #$03 + bne @copy_rom_field + +@copy_fields: + lda $05A0,x + sta $65,x + + lda $05C3,x + sta $68,x + +@copy_rom_field: + lda $C180,x + sta $61,x + + dex + bpl @copy_fields + + jsr CopyPersistentBufferToWorkingRam + + lda #$10 + bne SetSerialUpdateSelector + +; $C5A3 +SetSerialUpdateSelector17: + lda #$17 + bne SetSerialUpdateSelector + +; $C5A7 +CallC92CWithZero: + lda #$00 + jmp $C92C + +; $C5AC-$C5C1: initialize the six-byte $0300 structure. +Prepare0300Structure: + lda #$12 + sta $34 + + ldx #$05 + +@copy: + lda Prepare0300StructureTemplateC13C,x + sta $0300,x + dex + bpl @copy + + ldx $43 + inx + stx $0304 + rts +; $C5C2-$C620: runtime initialization. +InitializeRuntimeState: + ldx #$01 + stx $B7 + + jsr CopyPersistentBufferToWorkingRam + + stx $BD + stx $BE + stx $C4 + stx $C0 + stx InputActionLatch + stx $C2 + + inx + stx WorkingBuffer + + lda #$06 + sta $AC + + lda #$A0 + sta $8C + + lda #$10 + sta $80 + + lda #$04 + sta $04 + + ldx #$00 + stx $0B + + jsr BuildRuntimeEntry + + ldx #$07 +@copy_template: + lda RuntimeOamTemplateC178,x + sta $0228,x + dex + bpl @copy_template + + lda #$05 + sta $B1 + sta $B2 + + lda #$21 + sta $B0 + + lda #$03 + sta $B3 + + lda #$08 + sta $64 + sta $E0 + inc $E0 + + lda #$7A + sta $B4 + + inc $40 + inc $47 + + lda #$D0 + jsr $EC25 + jmp BuildOamEntryEC47 + + +; $C621-$C62C: copy the complete 256-byte persistent buffer +; into the working buffer. X wraps from $00 -> $FF and +; eventually back to $00, producing exactly 256 iterations. +CopyPersistentBufferToWorkingRam: + ldx #$00 + +@copy: + lda PersistentBuffer,x + sta WorkingBuffer,x + dex + bne @copy + + rts + +; These entry points also have absolute aliases in symbols.inc. +SerialWriteTimingCode: + lda #$05 + sta JOY1 + pha + pla + pha + pla + pha + pla + rts + +; $C639-$C64D: wait for $4016 bit 1 to assert and then deassert. +SerialWaitPulseCode: + @wait_high: + jsr SerialWriteTimingCode + lda JOY1 + and #$02 + beq @wait_high + + @wait_low: + jsr SerialWriteTimingCode + lda $4016 + and #$02 + bne @wait_low + + rts + +; $C64E: short-duration pulse entry. +SerialPulseShortCode: + pha + lda #$34 + bne SerialPulseCommon + +; $C653: long-duration pulse entry. +SerialPulseLongCode: + pha + lda #$6A + +; $C656: common pulse generator. +SerialPulseCommon: + sta $07 + sta $0C + + lda #$04 + sta JOY1 + +@high_delay: + dec $07 + bne @high_delay + + lda #$FF + sta JOY1 + +@low_delay: + dec $0C + bne @low_delay + + pla + rts +; --------------------------------------------------------------------------- +; $C66E-$C689: transmit one byte, MSB first. +; --------------------------------------------------------------------------- + +SerialTransmitByteCode: + jsr SerialPulseLongCode + ldx #$08 + +@next_bit: + sta $00 + clc + adc $00 + bcs @bit_set + + jsr SerialPulseShortCode + +@finish_bit: + dex + bne @next_bit + rts + +@bit_set: + jsr SerialPulseLongCode + jsr SerialIncrementCountCode + jmp @finish_bit + + +; --------------------------------------------------------------------------- +; $C68A-$C690: increment $09:$08. +; --------------------------------------------------------------------------- + +SerialIncrementCountCode: + inc $08 + bne @return + + inc $09 + +@return: + rts + + +; --------------------------------------------------------------------------- +; $C691-$C6A4: synchronize and sample JOY1 bit 1. +; --------------------------------------------------------------------------- + +SerialReceiveBitCode: + jsr SerialWaitPulseCode + + lda #$4E + sta $01 + +@delay: + dec $01 + bne @delay + + jsr SerialWriteTimingCode + + lda JOY1 + and #$02 + rts + + +; --------------------------------------------------------------------------- +; $C6A5-$C6C7: receive one byte. +; --------------------------------------------------------------------------- + +SerialReceiveByteCode: + lda #$08 + sta $03 + + lda #$00 + sta $04 + +@next_bit: + lda $04 + clc + adc $04 + sta $04 + + jsr SerialReceiveBitCode + bne @finish_bit + + jsr SerialIncrementCountCode + inc $04 + +@finish_bit: + dec $03 + bne @next_bit + + jsr SerialWaitPulseCode + + lda $04 + rts +; $C6C8-$C6D0: transmit the two runtime-data blocks. +; Deliberately falls through into TransmitDataBlockCode after +; configuring the second block. +TransmitRuntimeDataCode: + jsr SetupTransmitRamBlockCode + jsr TransmitDataBlockCode + jsr SetupTransmitWorkingBufferCode + +; Fall through intentionally into TransmitDataBlockCode. + +; $C6D1-$C715: transmit one serial data block. +TransmitDataBlockCode: + lda #$20 + sta $0A + + lda #$4E + sta $0B + +@initial_delay: + jsr SerialPulseShortCode + dec $0A + bne @initial_delay + + dec $0B + bne @initial_delay + +@long_preamble: + jsr SerialPulseLongCode + dec $05 + bne @long_preamble + +@short_preamble: + jsr SerialPulseShortCode + dec $06 + bne @short_preamble + + jsr SerialPulseLongCode + + lda #$00 + sta $08 + sta $09 + + ldy #$00 + +@send_payload: + lda ($0E),y + jsr SerialTransmitByteCode + iny + cpy $0D + bne @send_payload + + lda $09 + pha + + lda $08 + jsr SerialTransmitByteCode + + pla + jsr SerialTransmitByteCode + + jmp SerialPulseLongCode + +; $C716-$C725 +; Configure a $40-byte block beginning at $0060. +SetupTransmitRamBlockCode: + lda #$40 + sta $0D + + lda #$60 + sta $0E + + lda #$00 + sta $0F + + lda #$28 + bne SetTransmitPreambleCountsCode +; $C726-$C733 +; Configure the working-buffer block beginning at $05E0. +; A zero byte count represents 256 iterations when Y wraps. +SetupTransmitWorkingBufferCode: + lda #$00 + sta $0D + + lda #$E0 + sta $0E + + lda #$05 + sta $0F + + lda #$14 + + +; $C734-$C738 +; A contains the requested preamble timing/count value. +SetTransmitPreambleCountsCode: + sta $05 + sta $06 + rts +; --------------------------------------------------------------------------- +; $C739-$C791: receive the two runtime-data blocks. +; --------------------------------------------------------------------------- + +ReceiveRuntimeDataCode: + jsr DisableNmi + jsr SetupTransmitRamBlockCode + jsr ReceiveDataBlockCode + bcc ReceiveFirstBlockOk + +ReceiveError: + lda #$19 + sta $45 + +ReceiveRunUpdate: + jsr WaitForVblankEndAndEnableNmi + +ReceiveWaitUpdate: + lda $45 + bne ReceiveWaitUpdate + + beq ReceiveRuntimeDataCode + +ReceiveFirstBlockOk: + ldx #$00 + stx $A8 + + ldy #$01 + +ReceiveValidateHeader: + lda ($0E),y + sta $A3,y + cmp ReceiveHeaderTableBaseC17F,y + beq ReceiveHeaderByteOk + + inx + +ReceiveHeaderByteOk: + iny + cpy #$05 + bne ReceiveValidateHeader + + lda #$07 + sta $A0 + + lda #$22 + sta $A1 + + lda #$4F + sta $A2 + + lda #$04 + sta $A3 + + lda #$1A + sta $45 + + cpx #$00 + bne ReceiveRunUpdate + + jsr WaitForVblankEndAndEnableNmi + +ReceiveWaitSecondUpdate: + lda $45 + bne ReceiveWaitSecondUpdate + + jsr DisableNmi + jsr SetupTransmitWorkingBufferCode + jsr ReceiveDataBlockCode + bcs ReceiveError + + rts + +; --------------------------------------------------------------------------- +; $C792-$C7DD: receive one serial data block. +; +; Return: +; C clear = success +; C set = validation failure +; --------------------------------------------------------------------------- + +ReceiveDataBlockCode: +@restart: + lda $05 + sta $00 + +@wait_first_preamble: + jsr SerialReceiveBitCode + bne @restart + + dec $00 + bne @wait_first_preamble + + lda $06 + sta $00 + +@wait_second_preamble: + jsr SerialReceiveBitCode + beq @restart + + dec $00 + bne @wait_second_preamble + + lda #$00 + sta $08 + sta $09 + + jsr SerialWaitPulseCode + jsr SerialWaitPulseCode + + ldy #$00 + +@receive_payload: + jsr SerialReceiveByteCode + sta ($0E),y + + iny + cpy $0D + bne @receive_payload + + lda $08 + sta $0A + + lda $09 + sta $0B + + jsr SerialReceiveByteCode + cmp $0A + bne @failure + + jsr SerialReceiveByteCode + cmp $0B + beq @success + +@failure: + sec + rts + +@success: + clc + rts + +UpdateMenuSelection: + sta $00 + ldy MenuSelectionIndex + ldx #MENU_ACTION_NONE + lda Controller1Pressed + and #PAD_MENU_INPUT + bne @input + sta InputActionLatch +@early_return: + rts +@input: lda #$5D + sta $31 + lda InputActionLatch + bne @early_return + lda Controller1Pressed + bit Slot90BoundaryTable + bne @next + lsr a + lsr a + lsr a + bcs @next + lsr a + bcs @prev + ldx #MENU_ACTION_ACCEPTED + stx $30 + bne @store +@next: iny + cpy $00 + bcc @changed + ldy #$01 +@prev: dey + bpl @changed + ldy $00 + dey +@changed: + lda #$01 + sta $FF +@store: sty MenuSelectionIndex + inc InputActionLatch +@return:rts + +; --------------------------------------------------------------------------- +; $C820-$C868 +; Initialize PPU/course runtime memory and related state. +; --------------------------------------------------------------------------- + +InitializeCourseRuntime: + jsr InitializePairedPpuRows ; C820: 20 3F D1 + + lda #$03 ; C823: A9 03 + sta $01 ; C825: 85 01 + + lsr a ; C827: 4A + sta $45 ; C828: 85 45 + + lsr a ; C82A: 4A + sta $00 ; C82B: 85 00 + sta $4B ; C82D: 85 4B + + ldy #$EF ; C82F: A0 EF + +@clear_ram: + sta ($00),y ; C831: 91 00 + dey ; C833: 88 + cpy #$40 ; C834: C0 40 + bcs @clear_ram ; C836: B0 F9 + + ldx #$7F ; C838: A2 7F +@initialize_course_ram: + lda #$3B ; C83A: A9 3B + + sta $0400,x ; C83C: 9D 00 04 + + lda #$3D ; C83F: A9 3D + sta $0480,x ; C841: 9D 80 04 + sta $0500,x ; C844: 9D 00 05 + + dex ; C847: CA + bpl @initialize_course_ram ; C848: 10 F0 + + lda #$BF ; C84A: A9 BF + sta $0200 ; C84C: 8D 00 02 + + lda #$F7 ; C84F: A9 F7 + sta $0203 ; C851: 8D 03 02 + + lda #$FF ; C854: A9 FF + sta $0201 ; C856: 8D 01 02 + + lda #$3F ; C859: A9 3F + sta $E8 ; C85B: 85 E8 + + lsr a ; C85D: 4A + sta $E9 ; C85E: 85 E9 + + lda #$0A ; C860: A9 0A + sta $EB ; C862: 85 EB + + lda #$25 ; C864: A9 25 + sta $EA ; C866: 85 EA + + rts ; C868: 60 + + +; --------------------------------------------------------------------------- +; $C869-$C874 +; Clear zero-page/runtime RAM from $EF down through $4D. +; --------------------------------------------------------------------------- + +ClearRuntimeZeroPage: + lda #$00 + ldx #$EF + +@clear: + sta $00,x + dex + cpx #$4C + bne @clear + + rts + + + +; --------------------------------------------------------------------------- +; $C875-$C8DB +; Initialize global runtime and rider state. +; --------------------------------------------------------------------------- + +InitializeGameRuntime: + inc $47 + inc $40 + + lda $34 + bne @store_34 + + lda #$05 + +@store_34: + sta $34 + + lda #$34 + sta $E8 + + lda #$14 + sta $E9 + + lda #$A0 + sta $03F1 + + lda #$80 + sta $03B4 + sta $03B0 + + lda #$FF + sta $03BD + + ldx #$01 + stx $0370 + + inx + stx $03BE + + inx + + lda $03F2 + bne @initialize_riders + + ldx #$00 + +@initialize_riders: + lda $C0B4,x + sta $B8,x + + lda #$08 + sta $64,x + + lda $C0B0,x + sta $E0,x + + lda $C0B8,x + sta $80,x + + lda #$06 + sta $AC,x + sta $78,x + + lda #$01 + sta $84,x + sta $A8,x + + lda $19,x + lsr a + sta $74,x + + dex + bpl @initialize_riders + + lda $19,x + and #$03 + bne @select_rider + + lda #$02 + +@select_rider: + tax + lsr $78,x + +; --------------------------------------------------------------------------- +; $C8DE-$C92B +; Continue runtime initialization and prepare the initial $0300 structure. +; --------------------------------------------------------------------------- + +ContinueGameRuntimeC8DE: + lda $46 + beq @load_reference + + lda $43 + cmp #$04 + bne @load_reference + + ldx #$07 + +@copy_0300_template: + lda $C158,x + sta $0300,x + + dex + bpl @copy_0300_template + + lda $03F3 + jsr $DFA2 + sta $0307 + + txa + beq @load_reference + + sta $0306 + +@load_reference: + jsr LoadReferenceTriplet + + lda $41 + cmp #$08 + bcc @update_record + + jsr LoadPersistentTriplet + +@update_record: + lda $03F8 + asl a + clc + adc $06 + + cmp #$3C + bcc @store_06 + + sbc #$3C + inc $05 + +@store_06: + sta $06 + + lda #$00 + sta $07 + + lda #$02 + sta $03 + + jsr $C935 + + lda #$08 + + +; --------------------------------------------------------------------------- +; $C92C-$C934 +; Configure parameters for the $C935 structure builder. +; +; Entry: +; A = value stored into $03 +; $43 supplies $02 +; --------------------------------------------------------------------------- + +Setup0300EntryC92C: + ldy $43 + sty $02 + sta $03 + + jsr $C53E + + +; --------------------------------------------------------------------------- +; $C935-$C94A +; Prepare and copy seven bytes from $03D1 into the current $0300 +; output structure. +; --------------------------------------------------------------------------- + +Setup0300EntryC935: + ldx #$05 + jsr $DF7F + + ldx $0300 + ldy #$00 + +@copy_03d1: + lda $03D1,y + sta $0304,x + + inx + iny + cpy #$07 + bcc @copy_03d1 + + +; --------------------------------------------------------------------------- +; $C94B-$C970 +; Append another entry to the current $0300 structure. +; --------------------------------------------------------------------------- + +Append0300EntryC94B: + lda #$0A + jsr Advance0300IndexC971 + + ldy $03 + + lda IndexedParameterTableC0EC,y + sta $0301,x + + lda IndexedParameterTableC0ED,y + sta $0302,x + + lda #$FB + sta $0305,x + sta $0308,x + + lda #$07 + sta $0303,x + + lda #$00 + sta $030B,x + + rts + + +; --------------------------------------------------------------------------- +; $C971-$C97B +; Advance the current $0300 structure write index by A. +; +; Entry: +; A = amount to add +; +; Exit: +; X = previous $0300 index +; $0300 = previous index + A +; --------------------------------------------------------------------------- + +Advance0300IndexC971: + ldx $0300 ; C971: AE 00 03 + + clc ; C974: 18 + adc $0300 ; C975: 6D 00 03 + sta $0300 ; C978: 8D 00 03 + + rts ; C97B: 60 + + + +; --------------------------------------------------------------------------- +; $C97C-$C999 +; Runtime update chain. +; --------------------------------------------------------------------------- + +UpdateRaceRuntime: + jsr $CA1B + jsr UpdateGlobalRuntimeEB17 + jsr UpdateAccumulator + jsr UpdateGlobalPosition + jsr UpdateRiderCourseStateE70B + jsr ProcessCourseDefinition + jsr $F68D + jsr $F755 + jsr $C229 + +RuntimeUpdatePpuTailC997: + jmp UpdatePpuState + +; --------------------------------------------------------------------------- +; $C99A-$CA07 +; Main gameplay update chain. +; --------------------------------------------------------------------------- + +UpdateGameplayRuntime: + jsr $CA08 + + lda $03B1 + ora $03B2 + bne RuntimeUpdatePpuTailC997 + + jsr $D918 + jsr $DD8D + jsr UpdateAllRiderScriptStateE733 + jsr $E927 + + lda $52 + beq @normal_runtime + + jsr StateHelperCA9B + jmp @continue_runtime + +@normal_runtime: + lda $03F2 + beq @after_optional_updates + + jsr $DA9F + jsr $DBC4 + jsr $DEBB + +@after_optional_updates: + jsr UpdateGlobalCounters + jsr BuildGlobalDisplayEAC5 + jsr CompareRuntimeSlots + jsr CheckSlotState + +@continue_runtime: + jsr UpdateTriggeredSlots + jsr UpdateFourSlots + jsr UpdateFourSlotPositionsE96C + jsr UpdateDisplayState + jsr UpdateFourSlot8CE836 + jsr BuildFourSlotSpritesEA44 + jsr ProcessCourseDefinition + jsr UpdateSlotPairState + jsr UpdateGlobalFC + jsr UpdateGlobalDisplay + jsr ProcessRuntimeEntries + jsr PrepareSortedSlots + jsr UpdateRuntimeSlots + jsr UpdateRiderCourseStateE70B + jsr UpdatePpuState + jsr ProcessRiderSlots + + jmp ProcessSortedSlots + +; --------------------------------------------------------------------------- +; Preserve original ROM $CA08-$CA84. +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; $CA08-$CA1F +; Update all four rider slots, then process runtime input/state. +; --------------------------------------------------------------------------- + +UpdateFourRiderRuntime: + ldx #$03 + +@next_rider: + jsr $DCA0 + jsr $DFB2 + + dex + bpl @next_rider + + lda $03F7 + + ; CA16: D0 08 = BNE $CA20 + .byte $D0, $08 + + jsr RuntimeInputStateCA38 + + lda $14 + sta $5C + + rts + + +; --------------------------------------------------------------------------- +; $CA20-$CA37 +; Runtime transition/input handling. +; +; Several branches deliberately target the shared RTS at $CA84 or the +; existing code immediately following this block at $CA85. +; --------------------------------------------------------------------------- + +RuntimeTransitionCA20: + lda $03F7 + + ; CA23: F0 5F = BEQ $CA84 + .byte $F0, $5F + + lda $15 + + asl a + asl a + asl a + + ; CA2A: B0 59 = BCS $CA85 + .byte $B0, $59 + + asl a + + ; CA2D: 90 55 = BCC $CA84 + .byte $90, $55 + + lda #$00 + sta $42 + sta $47 + + jmp $C36A + + +; --------------------------------------------------------------------------- +; $CA38-$CA84 +; Runtime input/audio toggle and delay handling. +; --------------------------------------------------------------------------- + +RuntimeInputStateCA38: + lda $03B2 + bne RuntimeInputDecrementCA63 + + lda $15 + and #$10 + beq RuntimeInputStoreB0CA67 + + lda $03B0 + bne RuntimeInputStoreB0CA67 + + lda #$80 + sta $FB + sta $03B0 + + asl a + sta $FC + + lda $03B1 + eor #$01 + sta $03B1 + bne RuntimeInputSetDelayCA7F + + lda #$0F + sta APUCTRL + bne RuntimeInputSetDelayCA7F + + +; --------------------------------------------------------------------------- +; $CA63 +; --------------------------------------------------------------------------- + +RuntimeInputDecrementCA63: + dec $03B2 + rts + + +; --------------------------------------------------------------------------- +; $CA67 +; Store state and potentially disable audio. +; +; A is intentionally stored directly into $03B0 from the path that +; reaches this entry. +; --------------------------------------------------------------------------- + +RuntimeInputStoreB0CA67: + sta $03B0 + + lda $03B1 + beq RuntimeInputReturnCA84 + + lda #$00 + sta APUCTRL + + lda $15 + and #$0F + beq RuntimeInputReturnCA84 + + lda $03B2 + bne RuntimeInputReturnCA84 + + +; --------------------------------------------------------------------------- +; $CA7F +; --------------------------------------------------------------------------- + +RuntimeInputSetDelayCA7F: + lda #$28 + sta $03B2 + + +; --------------------------------------------------------------------------- +; $CA84 +; Shared return. +; --------------------------------------------------------------------------- + +RuntimeInputReturnCA84: + rts + + +ResetRuntimeZeroPagePreserveSelection: + lda MenuSelectionIndex + pha + ldx #$FF + lda #$00 +@clear: sta $00,x + dex + cpx #$20 + bne @clear + sta $FC + inc InputActionLatch + pla + sta MenuSelectionIndex + rts + +; --------------------------------------------------------------------------- +; $CA9B-$CB2B +; State/setup helpers and persistent/reference record offset calculators. +; --------------------------------------------------------------------------- + +StateHelperCA9B: + lda $32 + bne StateHelperCAAA + + jsr AdvanceNmiStateAndEnterNmiSequence + + lda $03F7 + .byte $D0, $DE + sta $47 + +SharedReturnCAA9: + rts + + +; $CAAA +StateHelperCAAA: + lda #$00 + sta $FC + sta $03A9 + sta $03E0 + sta $3C + + ldx $52 + dex + bne SharedReturnCAA9 + + lda $3F + lsr a + bcs StateHelperCAF3 + + lsr a + bcs StateHelperCAF3 + + and #$03 + tay + + lda #$3F + sta $0312 + + lda #$00 + sta $0313 + sta $0319 + + lda #$04 + sta $0314 + + lda $C166,y + sta $0316 + + lda $C170,y + sta $0318 + + ldy $43 + + lda $C160,y + sta $0315 + + lda $C16A,y + sta $0317 + + rts + + +; $CAF3 +StateHelperCAF3: + ldx #$11 + + lda $32 + lsr a + bcs SharedReturnCB05 + + +; $CAFA +StateHelperCAFA: + ldy #$06 + lda #$FE + +@fill: + sta $0304,x + inx + dey + bpl @fill + +SharedReturnCB05: + rts + + +; --------------------------------------------------------------------------- +; $CB06-$CB12 +; Calculate the persistent-record offset in Y. +; --------------------------------------------------------------------------- + +CalculatePersistentRecordOffset: + jsr CalculateReferenceRecordOffset + + ldy $03F2 + beq @store + + clc + adc #$23 + +@store: + tay + rts + + +; --------------------------------------------------------------------------- +; $CB13-$CB2B +; Calculate the base/reference-record offset. +; --------------------------------------------------------------------------- + +CalculateReferenceRecordOffset: + lda #$20 + + ldy $02 + cpy #$05 + beq @return + + lda #$00 + +@next_record: + dey + bmi @check_flag + + clc + adc #$03 + bne @next_record + +@check_flag: + ldy $46 + beq @return + + ora #$10 + +@return: + rts + +; $CB2C-$CB3A: indirect NMI sequence dispatcher. +DispatchNmiSequenceStep: + ldy NmiSequenceStep + lda $C12C,y + sta $00 + lda $C130,y + +; Intentional secondary entry used by DispatchNmiState. +DispatchIndirectHighByte: + sta $01 + jmp ($0000) + +; --------------------------------------------------------------------------- +; $CB3B-$CB76: NMI sequence step 0. +; --------------------------------------------------------------------------- + +StateSequenceStep0: + lda NmiState + cmp #$08 + bcs StateSequenceStep0Finish + + lda $03F2 + sta MenuSelectionIndex + + lda $53 + cmp #$04 + bcs StateSequenceStep0Finish + + lda $46 + beq StateSequenceStep0SetFlags + + lda $43 + cmp #$04 + bne StateSequenceStep0MaybeAdvance + + inc $03F3 + + lda $03F3 + lsr a + bcs StateSequenceStep0SetFlags + + lda $03F8 + lsr a + beq StateSequenceStep0SetFlags + + sta $03F8 + sec + +StateSequenceStep0MaybeAdvance: + bcs StateSequenceStep0SetFlags + + inc $43 + +StateSequenceStep0SetFlags: + lda #$01 + sta $46 + sta $4B + +StateSequenceStep0Finish: + lda #$04 + + ; Original CB75: D0 28 = BNE $CB9F. + ; Target is still inside the binary-backed next block. + .byte $D0, $28 + + +; --------------------------------------------------------------------------- +; $CB77-$CBA3: NMI sequence step 1. +; --------------------------------------------------------------------------- + +StateSequenceStep1: + ldy $33 + bne StateSequenceReturn + + lda #$04 + sta $03 + + ldx $0300 + +StateSequenceStep1Copy: + lda $03D9,y + sta $0304,x + + inx + iny + + cpy #$07 + bne StateSequenceStep1Copy + + jsr Append0300EntryC94B + + lda #$20 + + ldx $4B + bne StateSequenceSet03AD + + lda #$31 + +StateSequenceSet03AD: + sta $03AD + + clc + adc #$03 + + +; $CB9F +; Shared continuation used by sequence steps 0 and 1. +StateSequenceCommonCB9F: + sta $33 + inc NmiSequenceStep + + +; $CBA3 +StateSequenceReturn: + rts + +; --------------------------------------------------------------------------- +; $CBA4-$CC6A +; NMI sequence step 2 and associated initialization/update paths. +; --------------------------------------------------------------------------- + +StateSequenceStep2: + lda $33 + beq StateSequenceCommonCB9F + + cmp $03AD + beq StateSequenceStep2Process + + bcs StateSequenceReturn + + lda $4B + bne StateSequenceStep2CallThreshold + + lda $14 + and #$F0 + bne StateSequenceStep2RunStep3 + +StateSequenceStep2CallThreshold: + jsr StateSequenceStep2CheckThreshold + jmp SequenceSetup + + +StateSequenceStep2RunStep3: + jsr StateSequenceStep3 + + lda NmiState + bne StateSequenceReturn + + jmp AdvanceNmiStateAndEnterNmiSequence + + +; --------------------------------------------------------------------------- +; $CBC9 +; Main processing/initialization portion of sequence step 2. +; --------------------------------------------------------------------------- + +StateSequenceStep2Process: + lda $20 + bne StateSequenceReturn + + ldx #$03 + stx $2F + stx $0D + dex + + +StateSequenceStep2LoadTables: + lda SequenceLimitTable,x + sta $01 + + lda SequenceParameter02Table,x + sta $02 + + lda SequenceFlagsTable,x + sta $0B + + jsr SequenceSetupLoadIndexedParameters + + dex + bpl StateSequenceStep2LoadTables + + + lda #$01 + + ldx $53 + dex + stx $00 + + cpx #$03 + bcs StateSequenceStep2StoreFB + + stx $0D + lda #$02 + +StateSequenceStep2StoreFB: + sta $FB + + eor #$02 + sta $56 + + ldx $0D + + lda SequencePositionTable,x + sta $54 + + lda SequenceLimitTable,x + sta $55 + sta $03F1 + + lda $00 + bne StateSequenceStep2Return + + + lda #$0A + bne StateSequenceStep2Set45 + + +; --------------------------------------------------------------------------- +; $CC15 +; Check transition threshold and select update value. +; Also entered directly from the earlier StateSequenceStep2 path. +; --------------------------------------------------------------------------- + +StateSequenceStep2CheckThreshold: + lda $03AD + sec + sbc #$10 + cmp $33 + bne StateSequenceStep2BuildStructure + + lda $20 + bne StateSequenceStep2BuildStructure + + lda #$0D + + ldy $52 + dey + bne StateSequenceStep2Set45 + + lda $4B + clc + adc #$0B + + +StateSequenceStep2Set45: + sta $45 + rts + + +; --------------------------------------------------------------------------- +; $CC32 +; Build/update structure using table data. +; --------------------------------------------------------------------------- + +StateSequenceStep2BuildStructure: + lda #$07 + tay + + jsr Advance0300IndexC971 + + stx $05 + +StateSequenceStep2CopyTable: + lda StateSequenceStep2Template,y + sta $0301,x + + inx + dey + bpl StateSequenceStep2CopyTable + + + lda $3F + and #$10 + bne StateSequenceStep2Return + + ldy $05 + + lda $53 + jsr DivideBy10 + sta $0307,y + + txa + jsr DivideBy10 + sta $00 + + txa + bne StateSequenceStep2StorePrimary + + lda $00 + bne StateSequenceStep2StoreSecondary + + rts + + +StateSequenceStep2StorePrimary: + sta $0305,y + + +StateSequenceStep2StoreSecondary: + lda $00 + sta $0306,y + + +StateSequenceStep2Return: + rts + +; --------------------------------------------------------------------------- +; $CC6B-$CC7C: NMI sequence step 3. +; --------------------------------------------------------------------------- +StateSequenceStep3: + jsr AdvanceNmiStateAndEnterNmiSequence + + lda $4B + bne @return + + ldy NmiState + cpy #$09 + bcc @store_state + + lda #$08 + +@store_state: + sta NmiState + +@return: + rts + +; --------------------------------------------------------------------------- +; $CC7D-$CCDC: configure parameters and tail-call BuildIndirectStructure. +; --------------------------------------------------------------------------- +SequenceSetup: + lda $55 + sta $01 + + lda $56 + bne SequenceSetupSelectPointer + + lda $2F + beq SequenceSetupCalculate55 + + lda #$1F + sta $0380 + sta $038C + + lda #$AF + sta $0378 + + lda #$01 + sta $037C + lsr a + sta $0384 + + ldy #$BD + bne SequenceSetupStorePointer + +SequenceSetupCalculate55: + ldx #$00 + jsr UpdateSlotAccumulator + sta $55 + sta $01 + + lda $03F1 + cmp $55 + bcs SequenceSetupSelectPointer + + sta $55 + lda #$0A + sta $2F + +SequenceSetupSelectPointer: + ldy #$BB + +SequenceSetupStorePointer: + sty $02 + + ldx $53 + dex + cpx #$03 + bcc SequenceSetupIndexReady + ldx #$03 + +SequenceSetupIndexReady: + lda #$02 + sta $0B + +; $CCCA: shared parameter-loading entry. +SequenceSetupLoadIndexedParameters: + lda SequencePositionTable,x + sta $00 + + lda #$21 + sta $03 + + lda SequencePointerOffsetTable,x + sta $04 + + lda #$00 + jmp BuildIndirectStructure + +; --------------------------------------------------------------------------- +; $CCDD-$CD12: initialize nine 4-byte entries in the $02C0 area. +; --------------------------------------------------------------------------- +Initialize02C0Entries: + lda $3C + lsr a + bcc @return + + lda #$40 + sta $FB + + lda #$58 + sta $00 + + ldx #$08 + +@entry_loop: + txa + asl a + asl a + tay + + lda #$44 + sta $02C0,y + + lda $C147,x + sta $02C1,y + + lda #$00 + sta $02C2,y + + lda $00 + sta $02C3,y + + clc + adc #$0A + sta $00 + + dex + bpl @entry_loop + + lda #$F8 + sta $02D0 + +@return: + rts + + +; --------------------------------------------------------------------------- +; $CD13-$CD1E: copy eight ROM bytes from $C150-$C157 to $02C0-$02C7. +; --------------------------------------------------------------------------- +Copy02C0Template: + ldx #$07 + +@copy: + lda $C150,x + sta $02C0,x + dex + bpl @copy + + rts + + +; --------------------------------------------------------------------------- +; $CD1F-$CD58: update four runtime slots and set the global trigger flag. +; --------------------------------------------------------------------------- + +UpdateFourSlots: + lda $4F + beq @post_update + + ldx #$03 + +@slot_loop: + lda $A8,x + beq @next_slot + + lda $9C,x + cmp #$05 + beq @update_slot + + ora $98,x + ora $03E0,x + beq @update_slot + + lda #$00 + sta $5C,x + +@update_slot: + jsr CodeCD59 + +@next_slot: + dex + bpl @slot_loop + +@post_update: + lda $34 + bne @return + + lda $5C + and #$C0 + beq @return + + lda $03A9 + ora $52 + bne @return + + lda #$01 + sta $03A9 + sta $FE + +@return: + rts + +; --------------------------------------------------------------------------- +; $CD59-$CD78 +; Literal first incremental conversion. +; --------------------------------------------------------------------------- + +CodeCD59: + lda $5C,x ; CD59: B5 5C + and #$03 ; CD5B: 29 03 + sta $0A ; CD5D: 85 0A + ldy #$00 ; CD5F: A0 00 + + lda $0374,x ; CD61: BD 74 03 + ora $98,x ; CD64: 15 98 + + ; CD66: D0 52 = BNE $CDBA + .byte $D0, $52 + + lda $9C,x ; CD68: B5 9C + + ; CD6A: F0 04 = BEQ $CD70 + beq CodeCD70 + + cmp #$05 ; CD6C: C9 05 + + ; CD6E: D0 4A = BNE $CDBA + .byte $D0, $4A + +CodeCD70: + txa ; CD70: 8A + + ; CD71: D0 08 = BNE $CD7B + .byte $D0, $08 + + ldy #$02 ; CD73: A0 02 + lda $52 ; CD75: A5 52 + + ; CD77: D0 21 = BNE $CD9A + .byte $D0, $21 + + +; --------------------------------------------------------------------------- +; $CD79-$CDBC +; Second incremental conversion. +; --------------------------------------------------------------------------- + +CodeCD79: + ldy #$00 ; CD79: A0 00 + lda $B0,x ; CD7B: B5 B0 + + ; CD7D: D0 33 = BNE $CDB2 + .byte $D0, $33 + + lda $5C,x ; CD7F: B5 5C + and #$C0 ; CD81: 29 C0 + + ; CD83: F0 35 = BEQ $CDBA + .byte $F0, $35 + + stx $00 ; CD85: 86 00 + asl a ; CD87: 0A + + ; CD88: B0 02 = BCS $CD8C + bcs CodeCD8C + + inc $00 ; CD8A: E6 00 + +CodeCD8C: + jsr CodeCDEE ; CD8C: 20 EE CD + + ; CD8F: F0 04 = BEQ $CD95 + beq CodeCD95 + + lda $94,x ; CD91: B5 94 + + ; CD93: D0 25 = BNE $CDBA + .byte $D0, $25 + +CodeCD95: + ldy $00 ; CD95: A4 00 + txa ; CD97: 8A + + ; CD98: D0 12 = BNE $CDAC + bne CodeCDAC + + lda $94,x ; CD9A: B5 94 + cmp Slot94BoundaryTable,y ; CD9C: D9 D1 C0 + + ; CD9F: 90 0B = BCC $CDAC + bcc CodeCDAC + + ; CDA1: D0 16 = BNE $CDB9 + .byte $D0, $16 + + lda $90,x ; CDA3: B5 90 + cmp Slot90BoundaryTable,y + ; CDA5: D9 CE C0 + + ; CDA8: F0 13 = BEQ $CDBD + .byte $F0, $13 + + ; CDAA: B0 0D = BCS $CDB9 + .byte $B0, $0D + +CodeCDAC: + jsr $CE29 ; CDAC: 20 29 CE + + ; CDAF: 4C BD CD = JMP $CDBD + .byte $4C, $BD, $CD + +; --------------------------------------------------------------------------- +; $CDB2-$CDBC +; +; This is also a branch destination from CD7D. +; --------------------------------------------------------------------------- + +CodeCDB2: + ldy #$04 ; CDB2: A0 04 + lda $0A ; CDB4: A5 0A + + ; CDB6: F0 02 = BEQ $CDBA + beq CodeCDBA + + tay ; CDB8: A8 + iny ; CDB9: C8 + +CodeCDBA: + jsr CodeCE58 ; CDBA: 20 58 CE + + +; --------------------------------------------------------------------------- +; $CDBD-$CDED +; Continue slot update logic. +; --------------------------------------------------------------------------- + +CodeCDBD: + lda $98,x ; CDBD: B5 98 + + ; CDBF: D0 5B = BNE $CE1C + .byte $D0, $5B + + lda $B0,x ; CDC1: B5 B0 + bne CodeCDD5 ; CDC3: D0 10 + + lda $58,x ; CDC5: B5 58 + ora $52 ; CDC7: 05 52 + bne CodeCDDE ; CDC9: D0 13 + + lda $94,x ; CDCB: B5 94 + bne CodeCDD5 ; CDCD: D0 06 + + lda $90,x ; CDCF: B5 90 + cmp #$A0 ; CDD1: C9 A0 + bcc CodeCDDE ; CDD3: 90 09 + +CodeCDD5: + lda $0A ; CDD5: A5 0A + beq CodeCDDE ; CDD7: F0 05 + sta $00 ; CDD9: 85 00 + jmp UpdateSlotTimer ; CDDB: 4C 83 CE + +CodeCDDE: + lda $B0,x ; CDDE: B5 B0 + + ; CDE0: D0 3A = BNE $CE1C + .byte $D0, $3A + + lda $0368,x ; CDE2: BD 68 03 + cmp $AC,x ; CDE5: D5 AC + + ; CDE7: F0 33 = BEQ $CE1C + .byte $F0, $33 + + ldy #$05 ; CDE9: A0 05 + jmp $DCC7 ; CDEB: 4C C7 DC + + +; --------------------------------------------------------------------------- +; $CDEE-$CE28 +; Slot/state condition helpers. +; --------------------------------------------------------------------------- + +CodeCDEE: + lda $C0,x ; CDEE: B5 C0 + cmp #$E4 ; CDF0: C9 E4 + beq CodeCE0A ; CDF2: F0 16 + + lda #$03 ; CDF4: A9 03 + cmp RiderMotionState,x ; CDF6: D5 A4 + bne CodeCE00 ; CDF8: D0 06 + + lda $70,x ; CDFA: B5 70 + cmp #$03 ; CDFC: C9 03 + bcs CodeCE0A ; CDFE: B0 0A + +CodeCE00: + lda $B8,x ; CE00: B5 B8 + cmp #$38 ; CE02: C9 38 + bcs CodeCE0A ; CE04: B0 04 + cmp #$08 ; CE06: C9 08 + bcs CodeCE1D ; CE08: B0 13 + +CodeCE0A: + lda #$01 ; CE0A: A9 01 + cmp $036C,x ; CE0C: DD 6C 03 + beq CodeCE15 ; CE0F: F0 04 + asl a ; CE11: 0A + sta $036C,x ; CE12: 9D 6C 03 + +CodeCE15: + txa ; CE15: 8A + + ; CE16: D0 10 = BNE $CE28 + bne CodeCE28 + + lda #$04 ; CE18: A9 04 + sta $FD ; CE1A: 85 FD + rts ; CE1C: 60 + + +; --------------------------------------------------------------------------- +; $CE1D +; Secondary entry/path. +; --------------------------------------------------------------------------- + +CodeCE1D: + lda #$01 ; CE1D: A9 01 + cmp $036C,x ; CE1F: DD 6C 03 + beq CodeCE28 ; CE22: F0 04 + lsr a ; CE24: 4A + sta $036C,x ; CE25: 9D 6C 03 + +CodeCE28: + rts ; CE28: 60 + + +; --------------------------------------------------------------------------- +; $CE29-$CE57 +; Update slot position against boundary/table values. +; --------------------------------------------------------------------------- + +CodeCE29: + cpx $4C ; CE29: E4 4C + bne CodeCE45 ; CE2B: D0 18 + + lda $C0BC,y ; CE2D: B9 BC C0 + clc ; CE30: 18 + adc $90,x ; CE31: 75 90 + sta $90,x ; CE33: 95 90 + bcc CodeCE39 ; CE35: 90 02 + inc $94,x ; CE37: F6 94 + +CodeCE39: + txa ; CE39: 8A + bne CodeCE45 ; CE3A: D0 09 + + lda $94,x ; CE3C: B5 94 + cmp Slot94BoundaryTable,y + ; CE3E: D9 D1 C0 + beq CodeCE46 ; CE41: F0 03 + bcs CodeCE4D ; CE43: B0 08 + +CodeCE45: + rts ; CE45: 60 + +CodeCE46: + lda $90,x ; CE46: B5 90 + cmp Slot90BoundaryTable,y + ; CE48: D9 CE C0 + bcc CodeCE45 ; CE4B: 90 F8 + +CodeCE4D: + lda Slot90BoundaryTable,y + ; CE4D: B9 CE C0 + sta $90,x ; CE50: 95 90 + + lda Slot94BoundaryTable,y + ; CE52: B9 D1 C0 + sta $94,x ; CE55: 95 94 + rts ; CE57: 60 + + +; --------------------------------------------------------------------------- +; $CE58-$CE82 +; Slot position adjustment helper. +; --------------------------------------------------------------------------- + +CodeCE58: + cpx $4C ; CE58: E4 4C + bne CodeCE82 ; CE5A: D0 26 + + sty $00 ; CE5C: 84 00 + + lda $94,x ; CE5E: B5 94 + bne CodeCE6D ; CE60: D0 0B + + lda $B0,x ; CE62: B5 B0 + lsr a ; CE64: 4A + tay ; CE65: A8 + + lda $90,x ; CE66: B5 90 + cmp SlotPositionThresholdTable,y + ; CE68: D9 CC C0 + bcc CodeCE82 ; CE6B: 90 15 + +CodeCE6D: + ldy $00 ; CE6D: A4 00 + + lda $90,x ; CE6F: B5 90 + sec ; CE71: 38 + sbc $C0C1,y ; CE72: F9 C1 C0 + sta $90,x ; CE75: 95 90 + bcs CodeCE82 ; CE77: B0 09 + + lda $94,x ; CE79: B5 94 + beq CodeCE80 ; CE7B: F0 03 + + dec $94,x ; CE7D: D6 94 + rts ; CE7F: 60 + +CodeCE80: + sta $90,x ; CE80: 95 90 + +CodeCE82: + rts ; CE82: 60 + +; --------------------------------------------------------------------------- +; $CE83-$CEB1: update per-slot timer/state values. +; --------------------------------------------------------------------------- + +UpdateSlotTimer: + lda $26,x + bne @return + + lda $B0,x + lsr a + tay + + lda SlotTimerReloadTable,y + sta $26,x + + lsr $00 + bcc @alternate + + lda $AC,x + cmp SlotACTargetTableC0C8,y + beq @return + bcc @increment + + dec $AC,x + +@return: + rts + + +@alternate: + lda $0388,x + and #$02 + sta $0388,x + + lda $AC,x + cmp SlotACAlternateTargetTable,y +; Original CEAD: B0 03 = BCS $CEB2 + .byte $B0, $03 +@increment: + inc $AC,x + rts + + +RiderStateUpdateCEB2: + lda $5C,x + and #$C0 + beq @return + lda $B0,x + bne @return + inc $AC,x + lda #$0D + sta $26,x + lda $AC,x + cmp #$0D + bcc @return + lda #$01 + sta $98,x + asl a + sta $26,x +@return: + rts + + +; --------------------------------------------------------------------------- +; $CED0-$CF0B +; Process active rider slots. +; +; Starts with slot $4A and advances through slot 3. +; --------------------------------------------------------------------------- + +ProcessRiderSlots: + ldx $4A +@next_slot: + stx $0F + + lda $A8,x + beq @advance_slot + + lda $98,x + ora $9C,x + bne @advance_slot + + jsr $CFEA + + lda $B0,x + bne @copy_runtime + + jsr $CFCB + jsr $D018 + bne @advance_slot + + lda $03A6 + beq @advance_slot + + jsr SelectRiderConfiguration + + lda $52 + bne @advance_slot + + jsr CheckRiderCourseState + jmp @advance_slot + +@copy_runtime: + lda $0368,x + sta $7C,x + +@advance_slot: + ldx $0F + inx + cpx #$04 + bcc @next_slot + + rts + + +; --------------------------------------------------------------------------- +; $CF0C-$CF95 +; Rider course/lane state evaluation. +; +; X = current rider slot. +; +; This code has several secondary entry points and shared returns. +; --------------------------------------------------------------------------- + +CheckRiderCourseState: + lda $84,x ; CF0C: B5 84 + lsr a ; CF0E: 4A + + ; CF0F: 90 FA = BCC $CF0B + ; $CF0B is the RTS immediately preceding this routine. + .byte $90, $FA + + lda $70,x ; CF11: B5 70 + sta $02 ; CF13: 85 02 + + ldy #$02 ; CF15: A0 02 + jsr $CF96 ; CF17: 20 96 CF + + bcc RiderCourseAlternateCF2B ; CF1A: 90 0F + + jsr $CFDB ; CF1C: 20 DB CF + + lda $80,x ; CF1F: B5 80 + asl a ; CF21: 0A + + ; CF22: 90 E7 = BCC $CF0B + .byte $90, $E7 + + lda $18,x ; CF24: B5 18 + lsr a ; CF26: 4A + + bcs RiderCoursePathCF64 ; CF27: B0 3B + bcc RiderCourseEntryCF47 ; CF29: 90 1C + + +; --------------------------------------------------------------------------- +; $CF2B +; Alternate path. +; --------------------------------------------------------------------------- + +RiderCourseAlternateCF2B: + lda $DC,x ; CF2B: B5 DC + bne RiderCourseReturnCF87 ; CF2D: D0 58 + + lda $84,x ; CF2F: B5 84 + lsr a ; CF31: 4A + bcc RiderCourseReturnCF87 ; CF32: 90 53 + + lda $80,x ; CF34: B5 80 + cmp #$F0 ; CF36: C9 F0 + bcs RiderCourseReturnCF87 ; CF38: B0 4D + + asl a ; CF3A: 0A + bcc RiderCourseReturnCF87 ; CF3B: 90 4A + + lda $19,x ; CF3D: B5 19 + cmp #$E0 ; CF3F: C9 E0 + bcc RiderCourseReturnCF87 ; CF41: 90 44 + + cmp #$F8 ; CF43: C9 F8 + bcc RiderCoursePathCF64 ; CF45: 90 1D + + +; --------------------------------------------------------------------------- +; $CF47 +; Secondary entry point. +; Uses $0360,X to select a neighboring course/lane index. +; --------------------------------------------------------------------------- + +RiderCourseEntryCF47: + ldy $0360,x ; CF47: BC 60 03 + dey ; CF4A: 88 + cpy #$01 ; CF4B: C0 01 + bmi RiderCourseEntryCF6A ; CF4D: 30 1B + + jsr RiderCourseValueCheckCF88 ; CF4F: 20 88 CF + bcs RiderCourseReturnCF87 ; CF52: B0 33 + + ldy $70,x ; CF54: B4 70 + iny ; CF56: C8 + sty $02 ; CF57: 84 02 + + ldy #$00 ; CF59: A0 00 + jsr $CF96 ; CF5B: 20 96 CF + bcs RiderCourseReturnCF87 ; CF5E: B0 27 + + lda #$01 ; CF60: A9 01 + bne RiderCourseStoreCF85 ; CF62: D0 21 + + +; --------------------------------------------------------------------------- +; $CF64 +; Another course-state path. +; --------------------------------------------------------------------------- + +RiderCoursePathCF64: + lda $C0,x ; CF64: B5 C0 + cmp #$E6 ; CF66: C9 E6 + beq RiderCourseEntryCF47 ; CF68: F0 DD + + +; --------------------------------------------------------------------------- +; $CF6A +; Secondary entry point. +; --------------------------------------------------------------------------- + +RiderCourseEntryCF6A: + ldy $0360,x ; CF6A: BC 60 03 + iny ; CF6D: C8 + cpy #$05 ; CF6E: C0 05 + bpl RiderCourseEntryCF47 ; CF70: 10 D5 + + jsr RiderCourseValueCheckCF88 ; CF72: 20 88 CF + bcs RiderCourseReturnCF87 ; CF75: B0 10 + + ldy $70,x ; CF77: B4 70 + dey ; CF79: 88 + sty $02 ; CF7A: 84 02 + + ldy #$00 ; CF7C: A0 00 + jsr $CF96 ; CF7E: 20 96 CF + bcs RiderCourseReturnCF87 ; CF81: B0 04 + + lda #$FF ; CF83: A9 FF + +RiderCourseStoreCF85: + sta $DC,x ; CF85: 95 DC + +RiderCourseReturnCF87: + rts ; CF87: 60 + + +; --------------------------------------------------------------------------- +; $CF88-$CF95 +; Check a course/property value for the current rider. +; +; Carry: +; set = outside the accepted $3B-$3D range +; clear = accepted +; +; $CF8B is also a secondary entry point after the table load. +; --------------------------------------------------------------------------- + +RiderCourseValueCheckCF88: + jsr LoadIndexedValueE7FF ; CF88: 20 FF E7 + +RiderCourseValueCheckCF8B: + cmp #$3B ; CF8B: C9 3B + bcc RiderCourseValueInvalidCF94 ; CF8D: 90 05 + + cmp #$3E ; CF8F: C9 3E + bcs RiderCourseValueInvalidCF94 ; CF91: B0 01 + + rts ; CF93: 60 + +RiderCourseValueInvalidCF94: + sec ; CF94: 38 + rts ; CF95: 60 + + + + + +; --------------------------------------------------------------------------- +; $CF96-$CFCA +; Check whether another rider blocks a candidate lane. +; +; Entry: +; X = current rider slot +; Y = proximity/window selector +; $02 = candidate lane +; $0F = current rider slot saved by caller +; +; Exit: +; C clear = candidate lane has no nearby conflicting rider +; C set = another rider is in the candidate lane and distance window +; --------------------------------------------------------------------------- + +CheckAdjacentRiderConflict: + lda $80,x + clc + adc $C0D6,y + sta $00 + + sec + sbc $C0D7,y + sta $01 + + ldy $4A + +@check_rider: + cpy $0F + beq @next_rider + + lda $0084,y + lsr a + bcc @next_rider + + lda $0070,y + beq @next_rider + + cmp $02 + bne @next_rider + + lda $0080,y + cmp $00 + bcs @next_rider + + cmp $01 + bcs @return + +@next_rider: + iny + cpy #$04 + bcc @check_rider + + clc + +@return: + rts + +; --------------------------------------------------------------------------- +; $CFCB-$CFE9 +; Compare rider position pairs and update rider flags. +; +; X = current rider. +; +; $CFDB is also callable as a secondary entry. +; --------------------------------------------------------------------------- + +UpdateRiderPositionFlags: + ldy #$80 ; CFCB: A0 80 + + lda $94,x ; CFCD: B5 94 + cmp $78,x ; CFCF: D5 78 + + ; CFD1: 90 27 = BCC $CFFA + .byte $90, $27 + + bne UpdateRiderPositionFlagsCFDB ; CFD3: D0 06 + + lda $90,x ; CFD5: B5 90 + cmp $74,x ; CFD7: D5 74 + + ; CFD9: 90 1F = BCC $CFFA + .byte $90, $1F + +; --------------------------------------------------------------------------- +; $CFDB +; Secondary entry point. +; --------------------------------------------------------------------------- +UpdateRiderPositionFlagsCFDB: + lda $94,x ; CFDB: B5 94 + + ; CFDD: F0 0A = BEQ $CFE9 + beq RiderPositionFlagsReturnCFE9 + + lda #$0F ; CFDF: A9 0F + bne MergeRiderPositionFlagsCFE5 ; CFE1: D0 02 + +; $CFE3 is a genuine secondary entry used by the next routine. +SetRiderPositionHighNibbleMaskCFE3: + lda #$F0 ; CFE3: A9 F0 + +MergeRiderPositionFlagsCFE5: + and $5C,x ; CFE5: 35 5C + sta $5C,x ; CFE7: 95 5C + +RiderPositionFlagsReturnCFE9: + rts ; CFE9: 60 + + +; --------------------------------------------------------------------------- +; $CFEA-$CFFF +; Update packed rider state in $5C,X based on $7C,X versus $AC,X. +; +; X = current rider slot. +; --------------------------------------------------------------------------- + +UpdateRiderPackedState: + jsr SetRiderPositionHighNibbleMaskCFE3 ; CFEA: 20 E3 CF + + ldy #$01 ; CFED: A0 01 + + lda $7C,x ; CFEF: B5 7C + beq @return ; CFF1: F0 0C + + cmp $AC,x ; CFF3: D5 AC + beq @return ; CFF5: F0 08 + + bcc @merge ; CFF7: 90 01 + iny ; CFF9: C8 + +@merge: + tya ; CFFA: 98 + ora $5C,x ; CFFB: 15 5C + sta $5C,x ; CFFD: 95 5C + +@return: + rts ; CFFF: 60 + +; --------------------------------------------------------------------------- +; $D000-$D017 +; Select a rider configuration index and tail-call ConfigureSlot. +; +; X = current rider slot. +; Y = selected configuration value on exit to ConfigureSlot. +; --------------------------------------------------------------------------- + +SelectRiderConfiguration: + ldy #$00 ; D000: A0 00 + + cpx #$03 ; D002: E0 03 + beq @configure ; D004: F0 0F + + lda $80,x ; D006: B5 80 + and #$10 ; D008: 29 10 + + ; D00A: F0 F3 = BEQ $CFFF + ; Shared RTS immediately before this routine. + .byte $F0, $F3 + + lda $1A,x ; D00C: B5 1A + cmp #$C0 ; D00E: C9 C0 + + ; D010: 90 ED = BCC $CFFF + .byte $90, $ED + + bne @configure ; D012: D0 01 + + iny ; D014: C8 + +@configure: + jmp ConfigureSlot ; D015: 4C 50 DB + + + +; --------------------------------------------------------------------------- +; $D018-$D057 +; Scan four bytes belonging to rider X in the $03C0 table. +; +; X = current rider slot +; +; Each rider owns four bytes: +; offset = X * 4 +; --------------------------------------------------------------------------- + +ScanRiderStructure: + lda #$00 + sta $09 + sta $0D + + txa + asl a + asl a + sta $0A + + ldy #$03 +@scan_entry: + sty $0B + + ldy $0A + lda $03C0,y + + cmp #$C0 + + ; D02E: F0 28 = BEQ $D058 + .byte $F0, $28 + + cmp #$C1 + + ; D032: F0 24 = BEQ $D058 + .byte $F0, $24 + + cmp #$70 + bcc @check_48 + + cmp #$74 + + ; D03A: 90 1C = BCC $D058 + .byte $90, $1C + +@check_48: + cmp #$48 + bcc @next_entry + + cmp #$4B + + ; D042: 90 60 = BCC $D0A4 + .byte $90, $60 + +@next_entry: + inc $0A + + ldy $0B + dey + bpl @scan_entry + + ldy #$00 + lda $58,x + bne @use_y + + ldy $0368,x + +@use_y: + tya + + ; D055: 4C A6 D0 = JMP $D0A6 + .byte $4C, $A6, $D0 + + + +; --------------------------------------------------------------------------- +; $D058-$D0A5 +; Examine adjacent course positions for the current rider. +; +; X = current rider slot +; +; $08 = candidate course index +; $09 = $40 +; +; $D088 is a secondary entry used elsewhere. +; --------------------------------------------------------------------------- + +CheckRiderCourseAhead: + inc $0D ; D058: E6 0D + + lda $0A ; D05A: A5 0A + and #$03 ; D05C: 29 03 + tay ; D05E: A8 + iny ; D05F: C8 + tya ; D060: 98 + + clc ; D061: 18 + adc $E0,x ; D062: 75 E0 + and #$3F ; D064: 29 3F + sta $08 ; D066: 85 08 + + lda #$40 ; D068: A9 40 + sta $09 ; D06A: 85 09 + + ldy $0360,x ; D06C: BC 60 03 + dey ; D06F: 88 + cpy #$01 ; D070: C0 01 + bmi CheckRiderCourseOtherSideD088 ; D072: 30 14 + + jsr $E803 ; D074: 20 03 E8 + + ldy $07 ; D077: A4 07 + cmp #$FA ; D079: C9 FA + + ; D07B: F0 0B = BEQ $D088 + beq CheckRiderCourseOtherSideD088 + + jsr RiderCourseValueCheckCF8B ; D07D: 20 8B CF + + ; D080: B0 ED = BCS $D06F + ; + ; Return to DEY so another candidate position/state can be checked. + .byte $B0, $ED + + jsr RiderCourseEntryCF47 ; D082: 20 47 CF + + ; D085: 4C 4B D0 = JMP $D04B + ; Return to the earlier scan routine's fallback path. + .byte $4C, $4B, $D0 + + +; --------------------------------------------------------------------------- +; $D088 +; Secondary entry. +; Check the opposite/next course side. +; --------------------------------------------------------------------------- + +CheckRiderCourseOtherSideD088: + ldy $0360,x ; D088: BC 60 03 + iny ; D08B: C8 + cpy #$06 ; D08C: C0 06 + + ; D08E: 10 BB = BPL $D04B + .byte $10, $BB + + jsr $E803 ; D090: 20 03 E8 + + ldy $07 ; D093: A4 07 + cmp #$E4 ; D095: C9 E4 + + ; D097: F0 B2 = BEQ $D04B + .byte $F0, $B2 + + jsr RiderCourseValueCheckCF8B ; D099: 20 8B CF + + ; D09C: B0 ED = BCS $D08B + ; + ; Return to INY and try the next candidate. + .byte $B0, $ED + + jsr RiderCourseEntryCF6A ; D09E: 20 6A CF + + ; D0A1: 4C 4B D0 = JMP $D04B + .byte $4C, $4B, $D0 + + +; --------------------------------------------------------------------------- +; $D0A4 +; Shared continuation from the earlier $D042 branch. +; --------------------------------------------------------------------------- + +CourseScanResultD0A4: + lda #$09 ; D0A4: A9 09 + + + +; --------------------------------------------------------------------------- +; $D0A6-$D0AA +; Shared result tail for the rider course/object scan. +; +; Entry: +; A = result value +; X = current rider slot +; +; Exit: +; $7C,X = result +; A = $0D +; --------------------------------------------------------------------------- + +CourseScanStoreResultD0A6: + sta $7C,x + + lda $0D + rts + + +; --------------------------------------------------------------------------- +; $D0AB-$D0C5 +; Derive per-rider $0360 state from $B8,X. +; +; Processes rider slots 3 -> 0. +; $0360,X is a finer-grained vertical/lane-position band, not the +; game's four-lane index. +; --------------------------------------------------------------------------- + +UpdateRiderLanePositionStates: + ldx #$03 + +@next_rider: + ldy #$05 + + lda $B8,x + sec + sbc #$10 + bmi @store + +@reduce: + dey + beq @store + + sec + sbc #$08 + bpl @reduce + +@store: + tya + sta $0360,x + + dex + bpl @next_rider + + rts + +; --------------------------------------------------------------------------- +; $D0C6-$D0F3 +; Build four course/environment sample bytes for each rider. +; +; RAM layout: +; +; Rider 0 -> $03C0-$03C3 +; Rider 1 -> $03C4-$03C7 +; Rider 2 -> $03C8-$03CB +; Rider 3 -> $03CC-$03CF +; +; The first value is returned by $E7FC. Subsequent values are loaded +; from the course-data pointer in $03:$04. +; --------------------------------------------------------------------------- + +BuildRiderCourseObjectTable: + ldx #$03 + +@next_rider: + txa + asl a + asl a + sta $0C + + lda #$04 + sta $0B + + jsr $E7FC + sta $C0,x + + ; D0D6: 4C DD D0 = JMP $D0DD + ; First iteration uses the value already returned in A by $E7FC, + ; skipping the indexed load used by subsequent iterations. + .byte $4C, $DD, $D0 + +@read_next: + ldy $08 + lda ($03),y + +; $D0DD +@store_value: + pha + + iny + tya + and #$3F + sta $08 + + ldy $0C + pla + sta $03C0,y + + inc $0C + dec $0B + bne @read_next + + dex + bpl @next_rider + + rts + +; --------------------------------------------------------------------------- +; $D0F4-$D11E +; Copy/setup the $03D0 structure and initialize temporary pointers, +; then tail-call $D2A3. +; --------------------------------------------------------------------------- + +Initialize03D0Structure: + ldx #$06 + +@copy: + lda $03D9,x + sta $03D1,x + + dex + bpl @copy + + lda #$56 + sta $00 + + lda #$23 + sta $01 + + lda #$17 + sta $03D0 + + lda #$D0 + sta $02 + + lda #$03 + sta $03 + + lda #$FB + sta $03D2 + sta $03D5 + + jmp $D2A3 + + +; --------------------------------------------------------------------------- +; $D11F-$D13E +; Initialize command/update structure at $0300. +; +; Final structure: +; +; $0300 = $11 +; $0301 = $23 +; $0302 = $A0 +; $0303 = $0E +; $0304-$0311 = $FE +; $0312 = $00 +; --------------------------------------------------------------------------- + +Initialize0300Structure: + lda #$11 + sta $0300 + + lda #$23 + sta $0301 + + lda #$A0 + sta $0302 + + ldx #$0E + stx $0303 + + lda #$FE + +@fill: + sta $0303,x + + dex + bne @fill + + stx $0312 + rts + + +; --------------------------------------------------------------------------- +; $D13F-$D14D +; Generate three paired PPU update rows using the descriptors +; at $E6A1, $E6A5, and $E6A9. +; +; BuildPairedPpuRows uses X as the descriptor-table offset. +; The third invocation is a tail call, so its RTS returns directly +; to this routine's caller. +; --------------------------------------------------------------------------- + +InitializePairedPpuRows: + ldx #$00 + jsr BuildPairedPpuRows + + ldx #$04 + jsr BuildPairedPpuRows + + ldx #$08 + jmp BuildPairedPpuRows + + +; --------------------------------------------------------------------------- +; $D14E-$D15D +; NMI-time preparation. +; +; Wait for PPUSTATUS bit $40 to clear, call the $D1B3 helper, +; then derive $4C from the low two bits of $3F. +; --------------------------------------------------------------------------- + +NmiRuntimeUpdate: +@wait_ppu: + lda PPUSTATUS ; D14E: AD 02 20 + and #$40 ; D151: 29 40 + bne @wait_ppu ; D153: D0 F9 + + jsr ApplyPpuScrollState + ; D155: 20 B3 D1 + + lda $3F ; D158: A5 3F + and #$03 ; D15A: 29 03 + sta $4C ; D15C: 85 4C + + +; --------------------------------------------------------------------------- +; $D15E-$D17B +; Continue NMI-time runtime preparation. +; +; Initialize the $0300 update structure. Unless game state is $09, +; hide/clear a set of OAM entries and rebuild the per-rider course +; samples / $03D0 structure. +; +; Continues at $D17C. +; --------------------------------------------------------------------------- + + jsr Initialize0300Structure ; D15E: 20 1F D1 + + ldx #$43 ; D161: A2 43 + + lda $41 ; D163: A5 41 + cmp #$09 ; D165: C9 09 + + ; D167: F0 15 = BEQ $D17E + ; Target is beyond this incremental block. + .byte $F0, $15 + + lda #$F8 ; D169: A9 F8 + ldy #$EC ; D16B: A0 EC + +@hide_oam_entries: + sta $0200,y ; D16D: 99 00 02 + + dey ; D170: 88 + dey ; D171: 88 + dey ; D172: 88 + dey ; D173: 88 + + bne @hide_oam_entries ; D174: D0 F7 + + jsr BuildRiderCourseObjectTable ; D176: 20 C6 D0 + jsr Initialize03D0Structure ; D179: 20 F4 D0 + +; --------------------------------------------------------------------------- +; $D17C-$D19A +; Delay/synchronization loop followed by direct PPU register setup. +; +; $D17E is also an entry from the BEQ at $D167. On that path X retains +; its previous value instead of being initialized to $28. +; --------------------------------------------------------------------------- + + ldx #$28 ; D17C: A2 28 + +; $D17E - secondary entry from D167. +NmiRuntimeDelayD17E: + ldy #$12 ; D17E: A0 12 + +@delay_inner: + dey ; D180: 88 + bne @delay_inner ; D181: D0 FD + + dex ; D183: CA + bne NmiRuntimeDelayD17E ; D184: D0 F8 + + lda $10 ; D186: A5 10 + ora $4D ; D188: 05 4D + sta PPUCTRL ; D18A: 8D 00 20 + + lda PPUSTATUS ; D18D: AD 02 20 + + lda $12 ; D190: A5 12 + sta PPUSCROLL ; D192: 8D 05 20 + + lda $13 ; D195: A5 13 + sta PPUSCROLL ; D197: 8D 05 20 + + rts ; D19A: 60 + + +; --------------------------------------------------------------------------- +; $D19B-$D1B2 +; Wait for PPU status, update PPU control state, then reset PPUSCROLL. +; --------------------------------------------------------------------------- + +UpdatePpuState: +@wait_ppu: + lda PPUSTATUS ; D19B: AD 02 20 + and #$40 ; D19E: 29 40 + beq @wait_ppu ; D1A0: F0 F9 + + lda $10 ; D1A2: A5 10 + and #$F0 ; D1A4: 29 F0 + jsr $C333 ; D1A6: 20 33 C3 + + lda PPUSTATUS ; D1A9: AD 02 20 + + lda #$00 ; D1AC: A9 00 + sta PPUSCROLL ; D1AE: 8D 05 20 + + ; D1B1: F0 E4 = BEQ $D197 + ; A is known to be zero, so this is an unconditional branch. + .byte $F0, $E4 + + +; --------------------------------------------------------------------------- +; $D1B3-$D1C6 +; Apply alternate PPU control/scroll state. +; +; $10 | $4E -> PPUCTRL +; $50 -> first PPUSCROLL write +; $51 -> second PPUSCROLL write through shared code at $D197 +; --------------------------------------------------------------------------- + +ApplyPpuScrollState: + lda $10 + ora $4E + sta PPUCTRL + + ; Reset the PPUADDR/PPUSCROLL write latch. + lda PPUSTATUS + + lda $50 + sta PPUSCROLL + + lda $51 + + ; Tail-call the existing second-scroll-write path. + ; D1C4: 4C 97 D1 = JMP $D197 + .byte $4C, $97, $D1 + + +; --------------------------------------------------------------------------- +; $D1C7-$D23A +; Build repeated records through the indirect destination pointer $04:$05. +; +; Entry parameters are held in $00-$04 and $0A/$0B. +; $03 packs two counts into its high and low nibbles. +; +; Preserves X and Y. +; --------------------------------------------------------------------------- + +BuildIndirectStructure: + sta $0A + + txa + pha + + tya + pha + + lda #$02 + sta $05 + + lda #$0F + and $03 + sta $07 + + lda $03 + lsr a + lsr a + lsr a + lsr a + sta $06 + tax + + lda #$00 + clc + +@calculate_count: + adc $07 + dex + bne @calculate_count + + sta $08 + + lda $02 + ldy #$01 + +@build_entry: + txa + lsr a + bcs @store_value ; D1F0: B0 0A + + lsr $0A + bcc @store_value ; D1F4: 90 06 + + lda #$FC ; D1F6: A9 FC + sta ($04),y ; D1F8: 91 04 + + ; D1FA: D0 06 = BNE $D202 + bne @store_common + +@store_value: + lda $02 ; D1FC: A5 02 + sta ($04),y ; D1FE: 91 04 + inc $02 ; D200: E6 02 + +@store_common: + iny ; D202: C8 + lda $0B + + sta ($04),y + + iny + iny + iny + + inx + cpx $08 + bne @build_entry + + ldy #$00 + +@next_row: + ldx $06 + + lda $01 + sta $09 + +@write_row: + lda $09 + sta ($04),y + + clc + adc #$08 + sta $09 + + iny + iny + iny + + lda $00 + sta ($04),y + + iny + + dex + bne @write_row + + lda $00 + clc + adc #$08 + sta $00 + + dec $07 + bne @next_row + + pla + tay + + pla + tax + + rts + +; --------------------------------------------------------------------------- +; $D23B-$D24E +; Process two indexed cases through the $D24F and $D260 helpers. +; +; X runs as 1, then 2. +; The current X is preserved across the helper calls. +; $01 also receives the current index. +; --------------------------------------------------------------------------- + +ProcessTwoIndexedEntries: + ldx #$01 + +@next_entry: + txa + pha + + sta $01 + + jsr DisablePpuRendering + jsr InitializePpuMemory + + pla + tax + + inx + cpx #$03 + bcc @next_entry + + rts + +; --------------------------------------------------------------------------- +; $D24F-$D254 +; Disable PPU rendering. +; --------------------------------------------------------------------------- + +DisablePpuRendering: + lda #$00 ; D24F: A9 00 + sta PPUMASK ; D251: 8D 01 20 + rts ; D254: 60 + + +; --------------------------------------------------------------------------- +; $D255-$D25F +; Fill all 256 bytes of OAM shadow RAM ($0200-$02FF) with $F8. +; --------------------------------------------------------------------------- + +FillOamShadowWithF8: + lda #$F8 + ldy #$00 + +@loop: + sta OamBuffer,y + dey + bne @loop + + rts + +; --------------------------------------------------------------------------- +; $D260-$D2A2 +; Initialize/clear PPU memory regions. +; +; Uses $01 as a loop counter supplied by the caller. +; --------------------------------------------------------------------------- + +InitializePpuMemory: + lda PPUSTATUS ; D260: AD 02 20 + + lda $10 ; D263: A5 10 + and #$FB ; D265: 29 FB + jsr $C333 ; D267: 20 33 C3 + + lda #$1C ; D26A: A9 1C + clc ; D26C: 18 + +@calculate_address: + adc #$04 ; D26D: 69 04 + dec TMP01 ; D26F: C6 01 + bne @calculate_address ; D271: D0 FA + + sta $02 ; D273: 85 02 + sta PPUADDR ; D275: 8D 06 20 + + lda #$00 ; D278: A9 00 + sta PPUADDR ; D27A: 8D 06 20 + + ldx #$04 ; D27D: A2 04 + + ldy #$00 ; D27F: A0 00 + lda #$FC ; D281: A9 FC +@fill_fc: + sta PPUDATA ; D283: 8D 07 20 + dey ; D286: 88 + bne @fill_fc ; D287: D0 FA + + dex ; D289: CA + bne @fill_fc ; D28A: D0 F7 + + lda $02 ; D28C: A5 02 + adc #$03 ; D28E: 69 03 + sta PPUADDR ; D290: 8D 06 20 + + lda #$C0 ; D293: A9 C0 + sta PPUADDR ; D295: 8D 06 20 + + ldy #$40 ; D298: A0 40 + lda #$00 ; D29A: A9 00 +@clear_attribute_bytes: + sta PPUDATA ; D29C: 8D 07 20 + dey ; D29F: 88 + bne @clear_attribute_bytes ; D2A0: D0 FA + + rts ; D2A2: 60 + + + +; --------------------------------------------------------------------------- +; $D2A3-$D2FE +; Expand packed source data at $02:$03 into the structure at $0300. +; +; Preserves X and Y. +; --------------------------------------------------------------------------- + +Build0300Descriptor: + txa + pha + + tya + pha + + ldy #$00 + + lda ($02),y + and #$0F + sta $05 + + lda ($02),y + lsr a + lsr a + lsr a + lsr a + sta $04 + + ldx $0300 + +@next_group: + lda $01 + sta $0301,x + jsr Advance0300WriteIndex + + lda $00 + sta $0301,x + jsr Advance0300WriteIndex + + lda $05 + sta $06 + sta $0301,x + +@next_value: + jsr Advance0300WriteIndex + + iny + lda ($02),y + sta $0301,x + + dec $06 + bne @next_value + + jsr Advance0300WriteIndex + + stx $0300 + + clc + lda #$20 + adc TMP00 + sta TMP00 + + lda #$00 + adc TMP01 + sta TMP01 + + dec $04 + bne @next_group + + lda #$00 + sta $0301,x + + pla + tay + + pla + tax + + rts + + +; --------------------------------------------------------------------------- +; $D2FF-$D30F +; Advance X within the $0300 output structure. +; +; If X reaches $3F, terminate/reset the current structure and unwind +; the caller by removing its return address. +; --------------------------------------------------------------------------- + +Advance0300WriteIndex: + inx + + txa + cmp #$3F + bcc @return + + ldx $0300 + + lda #$00 + sta $0301,x + + ; Discard caller return address. + pla + pla + +@return: + rts + +; --------------------------------------------------------------------------- +; $D310-$D325 +; Update runtime countdown values. +; +; If $20 underflows, reload it with $0A and then process counters +; $21-$3D. Otherwise X remains $0E and processing begins at $2F. +; --------------------------------------------------------------------------- + +UpdateRuntimeCounters: + ldx #$0E ; D310: A2 0E + + dec $20 ; D312: C6 20 + bpl @process ; D314: 10 06 + + lda #$0A ; D316: A9 0A + sta $20 ; D318: 85 20 + + ldx #$1C ; D31A: A2 1C + +@process: + lda $21,x ; D31C: B5 21 + beq @next ; D31E: F0 02 + + dec $21,x ; D320: D6 21 + +@next: + dex ; D322: CA + bpl @process ; D323: 10 F7 + + rts ; D325: 60 + +; --------------------------------------------------------------------------- +; $D326-$D346 +; Shift the eight-byte state value at $18-$1F right by one bit. +; +; The bit shifted into carry is derived from bit 1 of $18 and $19. +; --------------------------------------------------------------------------- + +ShiftRuntimeState: + lda $18 + and #$02 + sta $00 + + lda $19 + and #$02 + eor $00 + + clc + beq @shift + + sec + +@shift: + ror $18 + ror $19 + ror $1A + ror $1B + ror $1C + ror $1D + ror $1E + ror $1F + + rts + + +ReadControllers: + lda #$01 + sta JOY1 + ldx #$00 + lda #$00 + sta JOY1 + jsr ReadOneController + inx + jsr ReadOneController + rts +ReadOneController: + ldy #$08 +@read: pha + lda JOY1,x + sta $00 + lsr a + ora $00 + lsr a + pla + rol a + dey + bne @read + stx TMP00 + asl TMP00 + ldx TMP00 + ldy ControllerState,x + sty TMP00 + sta ControllerState,x + sta ControllerState+1,x + and #$FF + bpl @done + bit TMP00 + bpl @done + and #$7F + sta ControllerState+1,x +@done: + rts + +; --------------------------------------------------------------------------- +; $D387-$D3D5: process PPU update command stream. +; $D3C1 is a secondary entry used by NmiPpuUpdate. +; --------------------------------------------------------------------------- + +ProcessPpuUpdateCommand: + sta PPUADDR + + iny + lda ($00),y + sta PPUADDR + + iny + lda ($00),y + asl a + pha + + lda PpuControlShadow + ora #$04 + bcs @set_ppuctrl + + and #$FB + +@set_ppuctrl: + jsr SetPpuControl + + pla + asl a + bcc @decode_count + + ora #$02 + iny + +@decode_count: + lsr a + lsr a + tax + +@next_write: + bcs @write_byte + + iny + +@write_byte: + lda ($00),y + sta PPUDATA + + dex + bne @next_write + + sec + tya + adc $00 + sta $00 + + lda #$00 + adc $01 + sta $01 + + +; --------------------------------------------------------------------------- +; $D3C1: secondary entry. +; Called directly by NmiPpuUpdate with $00:$01 pointing at the command stream. +; --------------------------------------------------------------------------- + +ProcessPpuUpdateStream: + ldx PPUSTATUS + + ldy #$00 + lda ($00),y + bne ProcessPpuUpdateCommand + + lda PPUSTATUS + + lda #$00 + sta PPUSCROLL + sta PPUSCROLL + + rts + + +; --------------------------------------------------------------------------- +; $D3D6-$D46D +; PPU / graphics update data. +; --------------------------------------------------------------------------- + +PpuGraphicsDataD3D6: + +; $D3D6 + .byte $3F, $00, $08, $29, $27, $22, $30, $29 + .byte $27, $18, $36, $3F, $10, $04, $29, $20 + +; $D3E6 + .byte $16, $0F, $00, $3F, $00, $08, $01, $1A + .byte $26, $33, $01, $11, $0C, $2C, $3F, $10 + +; $D3F6 + .byte $04, $01, $20, $16, $0F, $00, $3F, $00 + .byte $08, $02, $26, $1C, $30, $02, $29, $19 + +; $D406 + .byte $39, $3F, $10, $04, $02, $20, $16, $0F + .byte $00, $3F, $00, $08, $00, $26, $22, $30 + +; $D416 + .byte $00, $27, $18, $37, $3F, $10, $04, $00 + .byte $20, $16, $0F, $00, $3F, $00, $08, $02 + +; $D426 + .byte $19, $22, $30, $02, $00, $2D, $10, $3F + .byte $10, $04, $02, $20, $16, $0F, $00, $3F + +; $D436 + .byte $08, $08, $29, $22, $0F, $20, $29, $22 + .byte $0F, $16, $3F, $14, $0C, $29, $13, $20 + +; $D446 + .byte $0F, $29, $31, $1C, $0F, $29, $20, $19 + .byte $0F, $00, $3F, $00, $1C, $02, $0F, $30 + +; $D456 + .byte $21, $02, $15, $02, $2A, $02, $3C, $02 + .byte $30, $02, $30, $02, $27, $02, $20, $16 + +; $D466-$D46D + .byte $0F, $02, $27, $13, $3C, $02, $27, $16 + + +; --------------------------------------------------------------------------- +; $D46E-$D47F +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD46E: + .byte $30, $00 + .byte $23, $D0, $60, $55 + .byte $23, $F0, $50, $AA + .byte $27, $D0, $60, $55 + .byte $27, $F0, $50, $AA + +; --------------------------------------------------------------------------- +; $D480-$D48F +; Four-byte graphics / PPU records. +; --------------------------------------------------------------------------- + +PpuRecordDataD480: + .byte $20, $00, $60, $3F + .byte $20, $20, $60, $3E + .byte $20, $40, $60, $3F + .byte $20, $60, $60, $3E + + +PpuRecordDataD490: + .byte $20, $80, $60, $3F + .byte $24, $00, $60, $3F + .byte $24, $20, $60, $3E + .byte $24, $40, $60, $3F + +; --------------------------------------------------------------------------- +; $D4A0-$D4AF +; Four-byte graphics / PPU records. +; --------------------------------------------------------------------------- + +PpuRecordDataD4A0: + .byte $24, $60, $60, $3E + .byte $24, $80, $60, $3F + .byte $20, $A0, $60, $30 + .byte $20, $C0, $60, $FE + +; --------------------------------------------------------------------------- +; $D4B0-$D4BF +; Four-byte graphics / PPU records. +; --------------------------------------------------------------------------- + +PpuRecordDataD4B0: + .byte $20, $E0, $60, $FE + .byte $24, $A0, $60, $30 + .byte $24, $C0, $60, $FE + .byte $24, $E0, $60, $FE + +; --------------------------------------------------------------------------- +; $D4C0-$D4E0 +; PPU / graphics command data. +; --------------------------------------------------------------------------- + +PpuCommandDataD4C0: + .byte $20, $AB, $83, $31, $34, $37 + .byte $20, $B4, $83, $33, $36, $39 + .byte $20, $AC, $48, $32 + .byte $20, $EC, $48, $38 + .byte $20, $CC, $08 + .byte $17, $12, $17, $1D, $0E, $17, $0D, $18, $22 + .byte $00 + + +; --------------------------------------------------------------------------- +; $D4E1-$D4F0 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD4E1: + .byte $60, $3B, $22, $20 + .byte $60, $3C, $22, $40 + .byte $60, $3D, $22, $60 + .byte $60, $3B, $22, $80 + + +; --------------------------------------------------------------------------- +; $D4F1-$D500 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD4F1: + .byte $60, $3C, $22, $A0 + .byte $60, $3D, $26, $00 + .byte $60, $3B, $26, $20 + .byte $60, $3C, $26, $40 + +; --------------------------------------------------------------------------- +; $D501-$D510 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD501: + .byte $60, $3D, $26, $60 + .byte $60, $3B, $26, $80 + .byte $60, $3C, $26, $A0 + .byte $60, $3D, $23, $00 + + +; --------------------------------------------------------------------------- +; $D511-$D520 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD511: + .byte $7F, $FE, $23, $3F + .byte $7F, $FE, $23, $7E + .byte $7F, $FE, $23, $9D + .byte $63, $FE, $27, $00 + + +; --------------------------------------------------------------------------- +; $D521-$D530 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD521: + .byte $7F, $FE, $00, $24 + .byte $AB, $4A, $F2, $24 + .byte $AA, $01, $96, $24 + .byte $B5, $01, $97, $24 + + +; --------------------------------------------------------------------------- +; $D531-$D540 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD531: + .byte $CB, $03, $93, $94 + .byte $95, $23, $F1, $06 + .byte $AF, $AA, $AE, $AB + .byte $AA, $AF, $23, $23 + +; --------------------------------------------------------------------------- +; $D541-$D550 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD541: + .byte $07, $B7, $B7, $03 + .byte $1B, $0D, $B7, $B7 + .byte $23, $22, $83, $B5 + .byte $B0, $B2, $23, $2A + + +; --------------------------------------------------------------------------- +; $D551-$D560 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD551: + .byte $83, $B6, $B1, $B4 + .byte $23, $63, $47, $B3 + .byte $23, $36, $07, $B7 + .byte $B7, $1D, $12, $16 + + +; --------------------------------------------------------------------------- +; $D561-$D917 +; Graphics, UI, text, tile, and runtime lookup data. +; +; Executable code resumes at $D918. +; --------------------------------------------------------------------------- + +GraphicsAndUiDataD561: + +; $D561 + .byte $0E, $B7, $23, $35, $83, $B5, $B0, $B2 + .byte $23, $3D, $83, $B6, $B1, $B4, $23, $76 + +; $D571 + .byte $47, $B3, $23, $2C, $08, $4C, $5A, $1D + .byte $0E, $16, $19, $7A, $8B, $23, $4B, $0A + +; $D581 + .byte $4B, $4D, $5B, $FC, $FC, $FC, $FC, $7B + .byte $4D, $8C, $23, $6D, $06, $5C, $6A, $6A + +; $D591 + .byte $6A, $6A, $7C, $23, $8D, $06, $5D, $6B + .byte $6C, $6B, $6C, $8A, $00, $20, $89, $4E + +; $D5A1 + .byte $FC, $20, $A9, $0E, $FC, $FC, $0E, $21 + .byte $0C, $12, $1D, $0E, $0B, $12, $14, $0E + +; $D5B1 + .byte $FC, $FC, $00, $20, $89, $4E, $FC, $20 + .byte $AA, $0C, $0D, $0E, $1C, $12, $10, $17 + +; $D5C1 + .byte $FC, $1D, $1B, $0A, $0C, $14, $00, $22 + .byte $E7, $12, $12, $1D, $F9, $1C, $FC, $0A + +; $D5D1 + .byte $FC, $17, $0E, $20, $FC, $1B, $0E, $0C + .byte $18, $1B, $0D, $FA, $00, $22, $E5, $56 + +; $D5E1 + .byte $FC, $22, $EB, $09, $10, $0A, $16, $0E + .byte $FC, $18, $1F, $0E, $1B, $00, $22, $E7 + +; $D5F1 + .byte $12, $1D, $1B, $22, $FC, $1D, $11, $0E + .byte $FC, $17, $0E, $21, $1D, $FC, $1D, $1B + +; $D601 + .byte $0A, $0C, $14, $00, $22, $E5, $56, $FC + .byte $22, $EC, $07, $1D, $12, $16, $0E, $FC + +; $D611 + .byte $1E, $19, $00, $23, $E0, $50, $FF, $22 + .byte $0D, $07, $15, $18, $0A, $0D, $12, $17 + +; $D621 + .byte $10, $00, $23, $E0, $50, $FF, $22, $0D + .byte $06, $1C, $0A, $1F, $12, $17, $10, $00 + +; $D631 + .byte $22, $8E, $05, $0E, $1B, $1B, $18, $1B + .byte $00, $21, $84, $58, $27, $23, $44, $58 + +; $D641 + .byte $27, $21, $A4, $CD, $27, $21, $BB, $CD + .byte $27, $00, $23, $E3, $4B, $AA, $23, $F2 + +; $D651 + .byte $44, $FF, $20, $83, $5A, $27, $20, $A3 + .byte $5A, $27, $21, $83, $5A, $27, $21, $A3 + +; $D661 + .byte $5A, $27, $20, $C3, $C6, $27, $20, $C4 + .byte $C6, $27, $20, $DB, $C6, $27, $20, $DC + +; $D671 + .byte $C6, $27, $21, $07, $12, $98, $9A, $9C + .byte $9E, $A0, $A2, $A4, $A6, $A8, $98, $9A + +; $D681 + .byte $98, $AA, $A4, $AC, $AE, $98, $9A, $21 + .byte $27, $12, $99, $9B, $9D, $9F, $A1, $A3 + +; $D691 + .byte $A5, $A7, $A9, $99, $9B, $99, $AB, $A5 + .byte $AD, $AF, $99, $9B, $22, $0C, $0B, $1C + +; $D6A1 + .byte $0E, $15, $0E, $0C, $1D, $12, $18, $17 + .byte $FC, $0A, $22, $4C, $0B, $1C, $0E, $15 + +; $D6B1 + .byte $0E, $0C, $1D, $12, $18, $17, $FC, $0B + .byte $22, $8C, $06, $0D, $0E, $1C, $12, $10 + +; $D6C1 + .byte $17, $23, $09, $0E, $3A, $01, $09, $08 + .byte $04, $FC, $17, $12, $17, $1D, $0E, $17 + +; $D6D1 + .byte $0D, $18, $00, $23, $CA, $03, $40, $50 + .byte $50, $23, $D3, $42, $55, $23, $E2, $44 + +; $D6E1 + .byte $AA, $20, $E6, $43, $26, $20, $F7, $43 + .byte $26, $20, $EB, $09, $0C, $11, $0A, $15 + +; $D6F1 + .byte $15, $0E, $17, $10, $0E, $21, $0D, $04 + .byte $1B, $0A, $0C, $0E, $22, $0D, $06, $1D + +; $D701 + .byte $1B, $0A, $0C, $14, $79, $22, $69, $0D + .byte $01, $FC, $FC, $02, $FC, $FC, $03, $FC + +; $D711 + .byte $FC, $04, $FC, $FC, $05, $00, $23, $C9 + .byte $06, $0C, $07, $55, $55, $0D, $03, $23 + +; $D721 + .byte $DA, $44, $50, $23, $DE, $01, $10, $23 + .byte $E2, $05, $55, $55, $99, $AA, $22, $23 + +; $D731 + .byte $E9, $01, $C0, $23, $EA, $44, $F0, $23 + .byte $EE, $01, $30, $20, $87, $02, $52, $54 + +; $D741 + .byte $20, $97, $02, $52, $54, $20, $A4, $05 + .byte $26, $26, $FC, $53, $55, $20, $B7, $05 + +; $D751 + .byte $53, $55, $FC, $26, $26, $20, $8B, $09 + .byte $0C, $11, $0A, $15, $15, $0E, $17, $10 + +; $D761 + .byte $0E, $20, $AE, $04, $1B, $0A, $0C, $0E + .byte $21, $E8, $09, $0B, $0E, $1C, $1D, $FC + +; $D771 + .byte $1D, $12, $16, $0E, $22, $28, $09, $22 + .byte $18, $1E, $1B, $FC, $1D, $12, $16, $0E + +; $D781 + .byte $22, $68, $07, $1B, $0A, $17, $14, $12 + .byte $17, $10, $21, $2F, $01, $42, $21, $4E + +; $D791 + .byte $03, $43, $44, $45, $00, $23, $CA, $04 + .byte $40, $50, $50, $10, $23, $D3, $42, $AA + +; $D7A1 + .byte $23, $DB, $02, $AA, $22, $23, $E2, $43 + .byte $55, $23, $EA, $43, $55, $20, $E6, $43 + +; $D7B1 + .byte $26, $20, $F7, $43, $26, $20, $EB, $0A + .byte $0E, $21, $0C, $12, $1D, $0E, $0B, $12 + +; $D7C1 + .byte $14, $0E, $21, $8C, $07, $FC, $1D, $1B + .byte $0A, $0C, $14, $FC, $21, $6E, $03, $ED + +; $D7D1 + .byte $FC, $F0, $21, $AE, $03, $EE, $EF, $F1 + .byte $22, $2B, $09, $0B, $0E, $1C, $1D, $FC + +; $D7E1 + .byte $1D, $12, $16, $0E, $22, $6B, $83, $B5 + .byte $B0, $B2, $22, $73, $83, $B6, $B1, $B4 + +; $D7F1 + .byte $22, $6C, $47, $B7, $22, $AC, $47, $B3 + .byte $00, $23, $CB, $4B, $55, $23, $DB, $42 + +; $D801 + .byte $F0, $23, $E3, $01, $FF, $23, $EB, $01 + .byte $FF, $23, $F3, $42, $0F, $20, $CC, $0B + +; $D811 + .byte $19, $15, $0A, $22, $FC, $16, $18, $0D + .byte $0E, $FC, $0A, $21, $2C, $0B, $19, $15 + +; $D821 + .byte $0A, $22, $FC, $16, $18, $0D, $0E, $FC + .byte $0B, $21, $EC, $06, $0D, $0E, $1C, $12 + +; $D831 + .byte $10, $17, $22, $4C, $04, $1C, $0A, $1F + .byte $0E, $22, $AC, $04, $15, $18, $0A, $0D + +; $D841 + .byte $23, $0C, $05, $1B, $0E, $1C, $0E, $1D + .byte $00, $23, $23, $18, $0A, $0B, $0C, $0D + +; $D851 + .byte $0E, $0F, $10, $11, $12, $13, $14, $15 + .byte $16, $17, $18, $19, $1A, $1B, $1C, $FE + +; $D861 + .byte $B8, $FE, $24, $25, $00, $FF, $FF, $34 + .byte $34, $18, $34, $03, $02, $03, $02, $09 + +; $D871 + .byte $06, $08, $0F, $03, $02, $02, $02, $08 + .byte $05, $07, $0F, $0C, $09, $0A, $07, $0C + +; $D881 + .byte $0C, $0C, $00, $0C, $0A, $0B, $08, $0C + .byte $0C, $0C, $06, $03, $04, $02, $0B, $08 + +; $D891 + .byte $09, $58, $60, $69, $71, $79, $80, $0A + .byte $18, $20, $28, $30, $38, $40, $48, $50 + +; $D8A1 + .byte $50, $97, $90, $89, $11, $A6, $9F, $B3 + .byte $01, $00, $02, $02, $03, $00, $09, $08 + +; $D8B1 + .byte $08, $10, $10, $10, $02, $10, $10, $10 + .byte $01, $09, $09, $09, $01, $09, $03, $04 + +; $D8C1 + .byte $28, $4C, $70, $39, $01, $01, $01, $07 + .byte $00, $01, $01, $02, $00, $11, $22, $33 + +; $D8D1 + .byte $44, $66, $88, $AA, $02, $01, $01, $0E + .byte $1A, $26, $32, $00, $0C, $18, $A0, $40 + +; $D8E1 + .byte $40, $ED, $21, $21, $E2, $E8, $9E, $A6 + .byte $92, $9A, $86, $8E, $7A, $82, $E6, $AF + +; $D8F1 + .byte $E0, $E2, $CF, $D3, $D7, $CF, $3F, $0F + .byte $07, $07, $08, $20, $11, $11, $23, $33 + +; $D901 + .byte $53, $83, $D3, $F6, $D9, $D9, $D9, $D9 + .byte $D9, $D9, $BF, $C3, $C6, $08, $0A, $0F + +; $D911-$D917 + .byte $14, $18, $08, $14, $20, $2C, $38 + +; $D918-$D923 +UpdateFourObjects: + ldx #$03 + +@loop: + jsr DispatchSlotState + jsr UpdateSlotDerivedPosition + dex + bpl @loop + + rts + + +; --------------------------------------------------------------------------- +; $D924-$D932: dispatch per-slot state through split pointer tables. +; X = current slot +; --------------------------------------------------------------------------- + +DispatchSlotState: + ldy $9C,x + + lda $D8FF,y + sta $00 + + lda $D905,y + sta $01 + + jmp ($0000) + + + +; --------------------------------------------------------------------------- +; $D933-$D952: per-slot state handler. +; X = current slot. +; --------------------------------------------------------------------------- + +SlotStateHandlerD933: + lda $0370,x + beq @advance_state + + lda $3F + lsr a + bcs @return + + inc $0390,x + + ldy $03E4,x + + lda $80,x + clc + adc $D90E,y + cmp $0390,x + bcs @return + +@advance_state: + inc $9C,x + + ; Original D950: D0 2C = BNE $D97E. + bne SlotStateTailD97E + +@return: + rts + + +; --------------------------------------------------------------------------- +; $D953-$D96D: compare per-slot value and set direction/state. +; --------------------------------------------------------------------------- + +SlotStateHandlerD953: + txa + tay + beq @compare + + lda $BC,x + beq @compare + + ldy #$04 + +@compare: + lda $B8,x + cmp $D8C4,y + beq SlotStateResetD96E + + ldy #$01 + bcc @store_direction + + ldy #$FF + +@store_direction: + tya + sta $DC,x + rts + + +; --------------------------------------------------------------------------- +; $D96E-$D97B: clear fields and advance slot state. +; --------------------------------------------------------------------------- + +SlotStateResetD96E: + lda #$00 + +SlotStateAdvanceD970: + sta $0398,x + sta $DC,x + + inc $9C,x + + txa + bne SlotStateTailD97E + + lda #$02 + sta $FD + +; --------------------------------------------------------------------------- +; $D97E-$D982: shared tail. +; Nonzero X enters directly here, so $FD is only changed for slot 0. +; --------------------------------------------------------------------------- + +SlotStateTailD97E: + lda #$08 + sta $2A,x + rts + +; --------------------------------------------------------------------------- +; $D983-$D9D2: per-slot state handler. +; X = current slot. +; --------------------------------------------------------------------------- + +SlotStateHandlerD983: + lda $0370,x + beq @advance_state + + lda $2A,x + bne @return + + txa + ora $03F7 + bne @update_timing + + ldy $49 + + lda $14 + and #$C0 + beq @store_49 + + sta $49 + tya + beq @decrement_position + +@store_49: + sta $49 + +@update_timing: + lda $3F + and #$0F + bne @skip_decrement + +@decrement_position: + dec $0390,x + +@skip_decrement: + and #$07 + bne @check_position + + lda $0398,x + eor #$01 + sta $0398,x + +@check_position: + lda $0390,x + sec + sbc #$08 + cmp $80,x + bcs @return + +@advance_state: + lda #$03 + sta $36,x + + inc $9C,x + + lda #$00 + cpx #$03 + bne @return + + sta $03A8 + sta $0373 + +@return: + rts + +; --------------------------------------------------------------------------- +; $D9D3-$D9F5: per-slot state handler. +; --------------------------------------------------------------------------- + +SlotStateHandlerD9D3: + lda $36,x + bne @return + + txa + bne @nonzero_slot + + lda $03E0 +; Original D9DD: D0 31 = BNE $DA10 +; .byte $D0, $31 + bne SlotResetStateDA10 + + lda $03F7 + beq @advance_state + + jmp $CA85 + + +@nonzero_slot: + lda $22 + bne @return + + lda #$01 + sta $DC,x + + lda #$18 + sta $22 + + +@advance_state: + inc $9C,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $D9F6-$DA14: per-slot state/direction handler. +; --------------------------------------------------------------------------- + +; --------------------------------------------------------------------------- +; $D9F6-$DA14: per-slot state/direction handler. +; --------------------------------------------------------------------------- + +SlotStateHandlerD9F6: + lda $94,x + ora $DC,x + bne SlotSetDirectionDA03 + + lda $B8,x + cmp $D8C4,x + beq SlotStateReturnDA14 + + +; $DA03 +SlotSetDirectionDA03: + lda #$01 + sta $DC,x + + txa + beq SlotResetStateDA10 + + lda $B8,x + cmp #$08 + bcc SlotStateReturnDA14 + + +; $DA10 +; Shared entry from the preceding $D9D3 handler. +SlotResetStateDA10: + lda #$00 + sta $9C,x + + +; $DA14 +SlotStateReturnDA14: + rts + +; --------------------------------------------------------------------------- +; $DA15-$DA25: calculate per-slot derived position. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlotDerivedPosition: + lda $0390,x + sec + sbc $80,x + sbc #$08 + asl a + + clc + adc $B8,x + adc #$04 + + sta $6C,x + rts + +; --------------------------------------------------------------------------- +; $DA26-$DA69: update four runtime slots and dispatch optional follow-up. +; --------------------------------------------------------------------------- + +UpdateRuntimeSlots: + lda $4F + beq RuntimeSlotsReturnDA69 + + ldx #$03 + +@slot_loop: + jsr UpdateSlotPosition + jsr UpdateSlotDC1A + jsr UpdateSlotState + jsr UpdateSlot0374 + + dex + bpl @slot_loop + + jsr UpdateAccumulator + jsr UpdateSlotPositions + jsr UpdateGlobalPosition + + lda $3C + bne RuntimeSlotsReturnDA69 + + ldy $03BC + + lda $D8EF,y + sta $00 + + lda $D8F1,y + sta $01 + + jmp ($0000) + + +; --------------------------------------------------------------------------- +; $DA58: secondary entry. +; Update the position pair at $0394,X / $60,X. +; --------------------------------------------------------------------------- + +UpdateSlotPosition: + lda $94,x + sta $60,x + + lda $90,x + clc + adc $0394,x + sta $0394,x + bcc RuntimeSlotsReturnDA69 + + inc $60,x + + +; $DA69 +RuntimeSlotsReturnDA69: + rts + +; --------------------------------------------------------------------------- +; $DA6A-$DA79: accumulate $60 into $12 and toggle $4D on carry. +; --------------------------------------------------------------------------- + +UpdateAccumulator: + lda $12 + clc + adc $60 + sta $12 + bcc @return + + lda $4D + eor #$01 + sta $4D + +@return: + rts + +; --------------------------------------------------------------------------- +; $DA7A-$DA9E: update active slot positions using slot-0 delta in $60. +; --------------------------------------------------------------------------- + +UpdateSlotPositions: + ldx #$03 + +@slot_loop: + lda $A8,x + beq @next_slot + + lda $60,x + clc + adc $80,x + bcc @no_high_increment + + inc $84,x + +@no_high_increment: + sec + sbc $60 + sta $80,x + bcs @no_high_decrement + + dec $84,x + +@no_high_decrement: + lda $0390,x + sec + sbc $60 + sta $0390,x + +@next_slot: + dex + bne @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $DA9F-$DAE2 +; Search/update helper operating across slots 3..1. +; --------------------------------------------------------------------------- + +SlotSearchDA9F: + ldx #$03 + + lda $3B + bne @next_outer + + +@check_entry: + txa + eor $4C + ora $A8,x + ora $23 + ora $0373 + ora $03A8 + bne @next_outer + + lda $18,x + cmp #$A0 + bcc @next_outer + + stx $00 + + and #$03 + tay + iny + + lda $94 + and #$02 + sta $84,x + + ldx #$03 + + +@search_slots: + sty $01 + + lda $70,x + cmp $01 + bne @next_slot + + iny + cpy #$05 + bcc @next_slot + + ldy #$01 + + +@next_slot: + dex + bne @search_slots + + ldx $00 + jsr InitializeSlot + + +; $DADF +@next_outer: + dex + bne @check_entry + + rts + +; --------------------------------------------------------------------------- +; $DAE3-$DB4A: initialize/update the current slot. +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeSlot: + jsr ResetSlotState + + dey + lda $D8D8,y + sta $B8,x + + ldy $84,x + lda $D8DF,y + sta $80,x + + lda #$06 + sta $AC,x + + lda $E0 + clc + adc $D8E2,y + and #$3F + sta $E0,x + + lda $64 + sta $64,x + + tya + beq @configure_slot + + ldy #$05 + jsr LoadIndexedValueE7FF + cmp #$E4 + beq @configure_slot + + cmp #$3B + bcc @return_zero + + cmp #$93 + bcs @return_zero + + ldy #$02 + jsr LoadIndexedValueE7FF + cmp #$E6 + bne @configure_slot + + lda $B8,x + cmp #$20 + bcc @small_value + + lda #$30 + sta $BC,x + + lda #$01 + bne @store_a4 + +@small_value: + lda #$03 + +@store_a4: + sta RiderMotionState,x + +@configure_slot: + ldy $84,x + jsr ConfigureSlot + + lda $84,x + eor #$02 + asl a + sta $94,x + + lda #$20 + sta $039B,x + + lsr a + sta $23 + + lda #$01 + sta $A8,x + + rts + +@return_zero: + lda #$00 + rts + +; --------------------------------------------------------------------------- +; $DB50-$DB94: configure per-slot $74/$78 state. +; X = current slot. +; Y supplies a table index on some paths. +; --------------------------------------------------------------------------- + +ConfigureSlot: + lda $52 + beq SlotConfigCheckIndexDB58 + + lda #$03 + bne SlotConfigStore78DB5F + + +SlotConfigCheckIndexDB58: + cpx #$03 + beq SlotConfigSpecialDB6C + + +; $DB5C +; Shared internal entry used by several fallback paths. +SlotConfigFromTableDB5C: + lda $D8D5,y + + +; $DB5F +SlotConfigStore78DB5F: + sta $78,x + + lda $1B,x + and #$0F + tay + + lda $D8CD,y + sta $74,x + rts + + +; $DB6C +SlotConfigSpecialDB6C: + lda $84,x + lsr a + lsr a + bcs SlotConfigFromTableDB5C + + lda $94 + beq SlotConfigFromTableDB5C + + sta $78,x + + lda $90 + sta $74,x + + lda $84,x + beq SlotConfigIncrementCheckDB8C + + lda $80,x + cmp #$40 + bcs SlotConfigFromTableDB5C + + lda $70 + cmp $73 + beq SlotConfigReturnDB94 + + +; $DB8C +SlotConfigIncrementCheckDB8C: + lda $78,x + cmp #$04 + bcs SlotConfigReturnDB94 + + inc $78,x + + +; $DB94 +SlotConfigReturnDB94: + rts + +; --------------------------------------------------------------------------- +; $DB95-$DBC3: clear/reset per-slot runtime state. +; X = current slot. +; --------------------------------------------------------------------------- + +ResetSlotState: + lda #$00 + + sta $A8,x + sta $B8,x + sta $BC,x + sta $A0,x + sta RiderMotionState,x + sta $58,x + sta $CC,x + + sta $036C,x + + sta $90,x + sta $94,x + sta $98,x + sta $9C,x + sta $B0,x + sta $D4,x + + sta $0388,x + + sta $C0,x + + sta $039C,x + + sta $DC,x + sta $70,x + + sta $0370,x + + rts +; --------------------------------------------------------------------------- +; $DBC4-$DBFD: conditional slot reset/setup. +; --------------------------------------------------------------------------- + +SlotSetupDBC4: + lda $03A8 + + ; DBC7: F0 FA = BEQ $DBC3 + .byte $F0, $FA + + lda $AB + beq @initialize + + ldx #$03 + + lda $84,x + lsr a + + ; DBD2: B0 EF = BCS $DBC3 + .byte $B0, $EF + + lsr a + bcs @reset_slot + + lda $80,x + cmp #$EA + + ; DBDB: B0 E6 = BCS $DBC3 + .byte $B0, $E6 + +@reset_slot: + jmp ResetSlotState + +@initialize: + ldy #$00 + sty $03A8 + + iny + + ldx #$02 + stx $87 + inx + + jsr InitializeSlot + + ; DBEE: F0 D3 = BEQ $DBC3 + .byte $F0, $D3 + + lda #$00 + sta $97 + + lda #$92 + sta $8F + sta $0373 + + jmp $DC69 + +; --------------------------------------------------------------------------- +; $DBFE-$DC19: accumulate slot-0 delta into global position/state. +; --------------------------------------------------------------------------- + +UpdateGlobalPosition: + lda $60 + lsr a + ror a + ror a + ror a + + clc + adc $03BF + sta $03BF + + lda $60 + adc $50 + sta $50 + bcc @return + + lda $4E + eor #$01 + sta $4E + +@return: + rts + +; --------------------------------------------------------------------------- +; $DC1A-$DC96: per-slot update/check routine. +; --------------------------------------------------------------------------- + +UpdateSlotDC1A: + lda $B0,x + + ; DC1C: BEQ $DC19 + .byte $F0, $FB + + jsr CalculateSlotRemaining + sta $01 + + cmp $8C,x + + ; DC25: BCS $DC19 + .byte $B0, $F2 + + lda $8C,x + cmp #$A8 + + ; DC2B: BCS $DC19 + .byte $B0, $EC + + lda $0364,x + + ; DC30: BEQ $DC19 + .byte $F0, $E7 + + txa + bne @clear_fields + + lda #$02 + sta $FF + +@clear_fields: + lda #$00 + sta $B0,x + sta $0364,x + + lda $98,x + bne SlotUpdateCommonDC87 + + lda $52 + bne SlotUpdateAlternateDC8E + + ldy $0388,x + sta $0388,x + + tya + bne SlotUpdateCommonDC87 + + lda $D4,x + ldy $94,x + cpy #$02 + bpl @table_index_ready + + clc + adc #$08 + +@table_index_ready: + tay + + lda $AC,x + cmp $D86C,y + bcc @set_flag + + cmp $D87C,y + bcc @check_position + +@set_flag: + lda #$FF + sta $98,x + rts + +@check_position: + lda $0368,x + cmp $AC,x + beq SlotUpdateCommonDC87 + + lda $D4,x + ora $CC,x + bne SlotUpdateCommonDC87 + + lda #$04 + sta $0374,x + + lsr a + sta $0388,x + + lsr a + sta $B0,x + + +SlotUpdateCommonDC87: + dec $01 + lda $01 + sta $8C,x + +SlotUpdateReturnDC8D: + rts + + +SlotUpdateAlternateDC8E: + lda $D4,x + bne SlotUpdateReturnDC8D + + lda #$06 + sta $AC,x + rts + + +; --------------------------------------------------------------------------- +; $DC97-$DC9F: calculate remaining value after two slot fields. +; --------------------------------------------------------------------------- + +CalculateSlotRemaining: + lda $03F1 + + +; --------------------------------------------------------------------------- +; $DC9A: secondary entry. +; Subtract $BC,X and $B8,X from the value already in A. +; --------------------------------------------------------------------------- + +CalculateSlotRemainingFromADC9A: + sec + sbc $BC,x + sbc $B8,x + rts + + +; --------------------------------------------------------------------------- +; $DCA0-$DCC6: initialize/update per-slot $0368 value. +; --------------------------------------------------------------------------- + +UpdateSlot0368DCA0: + ldy $D4,x + + lda $52 + cmp #$01 + bne @from_table + + txa + ora $98 + ora $9C + ora $58 + bne @from_table + + lda #$0A + bne @store + +@from_table: + lda $D88B,y + +@store: + sta $0368,x + + lda RiderMotionState,x + cmp #$01 + bne @return + + lda #$06 + sta $0368,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $DCC7-$DCDD: adjust $AC,X toward the value supplied in A. +; +; Entry: +; A = target value +; X = current slot +; Y = value stored to $2A,X when $2A,X is zero +; --------------------------------------------------------------------------- + +AdjustSlotAC: + sta $00 + + lda $2A,x + bne @return + + sty $2A,x + + lda $AC,x + cmp $00 + beq @return + + bcc @increment + + dec $AC,x + dec $AC,x + +@increment: + inc $AC,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $DCDE-$DCF1: update per-slot $0374 countdown/state. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlot0374: + lda $0374,x + beq @return + + dec $0374,x + + lda $5C,x + and #$C0 + beq @return + + lda #$00 + sta $0374,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $DCF2-$DD37: update per-slot state/timing fields. +; --------------------------------------------------------------------------- + +UpdateSlotState: + lda $B0,x + beq StateDCF2ReturnDD37 + + cmp #$02 + beq StateDCF2Mode2DD11 + + +; $DCFA +StateDCF2ZeroDCFA: + lda #$00 + beq StateDCF2Store0384DD08 + +StateDCF2Increment94DCFE: + lda $98,x + bne StateDCF2ZeroDCFA + + inc $94,x + bne StateDCF2ZeroDCFA + + +; --------------------------------------------------------------------------- +; $DD06 +; Secondary entry: use current $94,X as the value stored in $0384,X, +; initialize slot state at $DD38, then join the common $DD1A path. +; --------------------------------------------------------------------------- + +UpdateSlotFrom94DD06: + lda $94,x + + +; $DD08 +StateDCF2Store0384DD08: + sta $0384,x + + jsr InitializeSlotState + jmp StateDCF2CommonDD1A +; $DD11 +StateDCF2Mode2DD11: + lda $5C,x + lsr a + bcc StateDCF2CommonDD1A + + lda $4C + beq StateDCF2ReturnDD37 + + +; $DD1A +StateDCF2CommonDD1A: + lda $5C,x + and #$03 + tay + + lda $D868,y + sta $038C,x + + lda $8C,x + sta $01 + + jsr UpdateSlotAccumulator + sta $8C,x + + lda $CC,x + bne StateDCF2ReturnDD37 + + lda #$01 + sta $0364,x + + +; $DD37 +StateDCF2ReturnDD37: + rts + + +; --------------------------------------------------------------------------- +; $DD38-$DD6E: initialize per-slot position/state fields. +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeSlotState: + lda #$02 + sta $B0,x + + lda #$0F + sta $0380,x + + lda $90,x + clc + adc #$AF + sta $0378,x + + lda $94,x + adc #$00 + sta $037C,x + + lda $0388,x + cmp #$02 + bne @check_slot + + lsr $037C,x + ror $0378,x + +@check_slot: + txa + bne @return + + lda $94,x + asl a + asl a + asl a + asl a + eor #$30 + bne @store_ff + + lda #$08 + +@store_ff: + sta $FF + +@return: + rts + +; --------------------------------------------------------------------------- +; $DD6F-$DD8C: update per-slot accumulated value and calculate result. +; X = current slot. +; $01 participates in the calculation and A contains the final result. +; --------------------------------------------------------------------------- + +UpdateSlotAccumulator: + lda $0380,x + adc $038C,x + sta $0380,x + + lda $0384,x + adc #$00 + sta $0384,x + + lda $01 + sbc $037C,x + sta $01 + + lda $01 + adc $0384,x + + rts + +; --------------------------------------------------------------------------- +; $DD8D-$DDD0: global state/setup dispatcher. +; --------------------------------------------------------------------------- + +GlobalStateUpdateDD8D: + lda $3C + ora $03E0 + + ; DD92: F0 F8 = BEQ $DD8C + .byte $F0, $F8 + + lda $3C + cmp #$08 + beq StateUpdateMode8DDBF + + bcc StateUpdateCopyTemplateDDCE + + lda $94 + ora $98 + ora $9C + bne StateUpdateInitialize02C0DDBC + + lda $58 + beq StateUpdateSet90DDAE + + lda #$C0 + sta $90 + bne StateUpdateInitialize02C0DDBC + + +StateUpdateSet90DDAE: + sta $90 + + ldx #$01 + + lda $B8 + cmp $D8C4 + bne StateUpdateStoreDCDDBA + + dex + + +StateUpdateStoreDCDDBA: + stx $DC + + +; $DDBC +StateUpdateInitialize02C0DDBC: + jmp Initialize02C0Entries + + +; $DDBF +StateUpdateMode8DDBF: + lda #$00 + sta $03E0 + + lda #$05 + sta $03B6 + sta $9C + sta $0374 + + +; $DDCE +StateUpdateCopyTemplateDDCE: + jmp Copy02C0Template +; --------------------------------------------------------------------------- +; $DDD1-$DE30: update four runtime slots. +; --------------------------------------------------------------------------- + +UpdateTriggeredSlots: + ldx #$03 + +@slot_loop: + lda $98,x + beq @next_slot + + ldy #$04 + + txa + bne @store_index + + sta $03A9 + sta $FC + + lda $4C + bne @select_index + + lda #$01 + sta $FD + +@select_index: + lda $1B + and #$03 + tay + +@store_index: + tya + sta $03E4,x + + lda $4C + lsr a + bcs @next_slot + + lda $94,x + bne @update_ac + + lda $B0,x + ora $58,x + bne @set_90 + + sta $90,x + sta $60,x + sta $98,x + + lda #$06 + sta $AC,x + + lda $80,x + clc + adc #$08 + sta $0390,x + + jsr UpdateSlotDerivedPosition + + lda #$02 + sta $0398,x + + lsr a + sta $9C,x + + bne @next_slot + +@set_90: + lda #$88 + sta $90,x + +@update_ac: + lda $AC,x + clc + adc $98,x + and #$0F + sta $AC,x + +@next_slot: + dex + bpl @slot_loop + + rts + + +; --------------------------------------------------------------------------- +; $DE31-$DEBA: update global state and build $02C0 display entries. +; --------------------------------------------------------------------------- +UpdateDisplayState: + lda $24 + cmp #$01 + beq StateDE31Mode1DEA6 + + ldy $4F + bne StateDE31ReturnDEA5 + + cmp #$60 + bcs StateDE31SelectValueDE4E + + lda $03A9 + beq StateDE31SelectValueDE4E + + lda $5C + and #$C0 + beq StateDE31SelectValueDE4E + + lda #$01 + sta $FE + + +; $DE4E +StateDE31SelectValueDE4E: + ldy #$F0 + + lda $24 + cmp #$10 + bcs StateDE31ValueReadyDE5E + + ldy #$F2 + + cmp #$08 + bcs StateDE31ValueReadyDE5E + + ldy #$F4 + + +; $DE5E +StateDE31ValueReadyDE5E: + cmp #$72 + bne StateDE31SetupLoopDE66 + + lda #$20 + sta $FB + + +; $DE66 +StateDE31SetupLoopDE66: + sty $00 + + lda $03B4 + sta $01 + + ldy #$07 + + +; $DE6F +StateDE31LoopDE6F: + tya + asl a + asl a + tax + + lda $D8E7,y + sta $02C0,x + + lda $00 + sta $02C1,x + + lda #$00 + sta $02C2,x + + lda $01 + sta $02C3,x + + tya + lsr a + bcc StateDE31SubtractDE91 + + inc $02C1,x + bne StateDE31NextDE9A + + +; $DE91 +StateDE31SubtractDE91: + lda $01 + sec + sbc #$10 + sta $01 + bcc StateDE31AfterLoopDE9D + + +; $DE9A +StateDE31NextDE9A: + dey + bpl StateDE31LoopDE6F + + +; $DE9D +StateDE31AfterLoopDE9D: + lda $34 + beq StateDE31ReturnDEA5 + + lda #$88 + sta $24 + + +; $DEA5 +StateDE31ReturnDEA5: + rts + + +; $DEA6 +StateDE31Mode1DEA6: + lda #$01 + sta $4F + + lda $03B4 + sec + sbc $60 + sta $03B4 + + bcc StateDE31ReturnDEA5 + + lda #$02 + sta $24 + + bne StateDE31SelectValueDE4E + +; --------------------------------------------------------------------------- +; $DEBB-$DEE1: update per-slot $03A9 flags. +; Processes slots X = 3, 2, 1. +; --------------------------------------------------------------------------- + +UpdateSlotFlags: + ldx #$03 + +@slot_loop: + lda $84,x + lsr a + bcc @next_slot + + lda $80,x + cmp #$30 + bcc @check_existing + + cmp #$40 + bcc @set_flag + + +@check_existing: + lda $03A9,x + beq @next_slot + + lda #$40 + sta $FF + + lda #$00 + beq @store_flag + + +@set_flag: + lda #$01 + + +@store_flag: + sta $03A9,x + + +@next_slot: + dex + bne @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $DEE2-$DF2F: calculate/update global $FC value. +; --------------------------------------------------------------------------- + +UpdateGlobalFC: + ldx #$00 + + lda $03A9 + beq @store_result + + lda $4C + lsr a + + ; DEEC: B0 B7 = BCS $DEA5 + .byte $B0, $B7 + + ldx #$02 + + lda $94 + sta $00 + + lda $90 + sta $01 + + +@shift_loop: + lsr $00 + ror $01 + + dex + bpl @shift_loop + + + lda #$02 + + ldy $B0 + beq @set_x + + lda #$04 + + +@set_x: + tax + + lda $01 + sec + sbc #$02 + bmi @minimum + + jsr DivideByX_DFA4 + + txa + bne @clamp + + +@minimum: + ldx #$01 + + +@clamp: + cpx #$2F + bcc @check_flag + + ldx #$2F + + lda $3F + and #$08 + bne @check_flag + + dex + + +@check_flag: + lda $5C + asl a + bcs @store_result + + txa + ora #$80 + tax + + +@store_result: + stx $FC + rts + +; --------------------------------------------------------------------------- +; $DF30-$DF7E: advance global time/counter fields and refresh display data. +; --------------------------------------------------------------------------- + +UpdateGlobalCounters: + lda $4F + beq @return + + lda $6B + clc + adc #$10 + sta $6B + + +@check_subunit: + lda $6B + cmp #$0A + bcs @carry_subunit + + +@refresh_display: + ldx #$68 + jsr BuildDisplayValues + + ldx #$06 + +@copy_display: + lda $03D1,x + sta $03D9,x + + dex + bpl @copy_display + + +@return: + rts + + +@carry_subunit: + lda $6B + sec + sbc #$0A + sta $6B + + inc $6A + + lda $6A + cmp #$64 + bcc @check_subunit + + lda #$00 + sta $6A + + inc $69 + + lda $69 + cmp #$3C + bcc @check_subunit + + lda #$00 + sta $69 + + inc $68 + + lda $68 + cmp #$09 + bcc @check_subunit + + lda #$02 + sta $52 + bne @refresh_display + +; --------------------------------------------------------------------------- +; $DF7F-$DFA1: convert three consecutive values selected by X and store +; their converted representations in $03D1-$03D7. +; --------------------------------------------------------------------------- + +BuildDisplayValues: + lda $00,x + pha + + lda $01,x + pha + + lda $02,x + jsr DivideBy10 + stx $03D6 + sta $03D7 + + pla + jsr DivideBy10 + stx $03D3 + sta $03D4 + + pla + jsr DivideBy10 + sta $03D1 + + rts + +; --------------------------------------------------------------------------- +; $DFA2: divide A by 10. +; --------------------------------------------------------------------------- + +DivideBy10: + ldx #$0A + + +; --------------------------------------------------------------------------- +; $DFA4: divide A by the divisor supplied in X. +; +; Return: +; X = quotient +; A = remainder +; --------------------------------------------------------------------------- + +DivideByX_DFA4: + stx $0F + + ldx #$00 + +@subtract: + inx + sec + sbc $0F + bpl @subtract + + dex + adc $0F + rts + +; --------------------------------------------------------------------------- +; $DFB2-$DFD4: derive per-slot $70 state from $B8 and table $D913. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlot70: + ldy #$00 + + lda $9C,x + bne @store + + lda $B8,x + +@search_table: + cmp $D913,y + bmi @found + + iny + cpy #$05 + bmi @search_table + + ldy #$00 + + +@found: + tya + beq @store + + lda #$01 + cmp RiderMotionState,x + bne @store + + iny + iny + + +@store: + tya + sta $70,x + + rts +; --------------------------------------------------------------------------- +; $DFD5-$E090: pairwise slot comparison/update. +; --------------------------------------------------------------------------- +CompareRuntimeSlots: + ldx #$00 + ldy #$01 + + +; $DFD9 +PairCheckX: + lda $A8,x + bne PairCheckY + + +; $DFDD +PairNextXTrampoline: + jmp PairNextX + + +; $DFE0 +PairCheckY: + lda $A8,y + beq PairNextYTrampoline + + lda $84,x + lsr a + bcc PairNextXTrampoline + + lda $84,y + lsr a + bcc PairNextYTrampoline + + lda $70,x + beq PairNextXTrampoline + + cmp $70,y + bne PairNextYTrampoline + + lda $98,x + ora $03E0,x + bne PairNextXTrampoline + + lda $98,y + bne PairNextY + + lda $B0,x + bne PairCompare8C + + lda $B0,y + beq PairCompare80 + +PairCompare8C: + lda $8C,x + cmp $8C,y + bcs PairCompare8CReverse + + jsr Add12UnlessHighE091 + cmp $8C,y + bcs PairCompare80 + +PairNextYTrampoline: + jmp PairNextY + + +PairCompare8CReverse: + lda $8C,y + jsr Add12UnlessHighE091 + cmp $8C,x + bcc PairNextY + + +PairCompare80: + lda $80,x + cmp $80,y + bcs PairCompare80Reverse + + jsr Add2UnlessHighE098 + cmp $80,y + bcs PairMarkX + + lda $80,x + jsr Add12UnlessHighE091 + cmp $80,y + bcs PairMarkXOnly + + bcc PairNextY + + +PairCompare80Reverse: + lda $80,y + jsr Add2UnlessHighE098 + cmp $80,x + bcs PairMarkX + + lda $80,y + jsr Add12UnlessHighE091 + cmp $80,x + bcs PairMarkBoth + + bcc PairNextY + + +PairMarkXOnly: + lda #$FF + sta $98,x + bne PairAfterMark + + +PairMarkX: + lda #$FF + sta $98,x + + +PairMarkBoth: + cpx #$00 + bne PairSetY + + cpy #$03 + bne PairSetY + + lda #$10 + sta $3B + + +PairSetY: + lda #$FF + sta $98,y + + +PairAfterMark: + cpx #$00 + bne PairNextY + + lda #$01 + sta $FD + + +; $E07E +PairNextY: + iny + cpy #$04 + beq PairNextX + + jmp PairCheckY + + +; $E086 +PairNextX: + inx + txa + tay + iny + + cpx #$03 + beq SharedReturnE097 + + jmp PairCheckX + + + +; --------------------------------------------------------------------------- +; $E091-$E097 +; --------------------------------------------------------------------------- + +Add12UnlessHighE091: + cmp #$E7 + bcs SharedReturnE097 + + adc #$0C + +SharedReturnE097: + rts + + +; --------------------------------------------------------------------------- +; $E098-$E09E +; --------------------------------------------------------------------------- + +Add2UnlessHighE098: + cmp #$E7 + bcs @return + + adc #$02 + +@return: + rts + +; --------------------------------------------------------------------------- +; $E09F-$E0E5: test slot-0 state/position conditions and update flags. +; --------------------------------------------------------------------------- + +CheckSlotState: + lda $9C + bne @return + + tax + + lda $87 + lsr a + bcc @return + + lda $0373 + beq @return + + lda $B8 + sec + sbc #$03 + cmp $6F + bcs @return + + adc #$07 + cmp $6F + bcc @return + + lda $80 + cmp $0393 + bcs @return + + adc #$12 + cmp $0393 + bcc @return + + jsr CalculateSlotRemaining + + sec + sbc #$08 + cmp $8C + bcs @return + + ldy #$FF + sty $98 + + iny + sty $DF + + iny + sty $9F + sty $FD + + iny + sty $039B + +@return: + rts + + +; --------------------------------------------------------------------------- +; $E0E6-$E17E +; State/setup update with structure generation. +; +; $E16C is also a secondary entry. +; --------------------------------------------------------------------------- + +UpdateStructureGenerator: + lda $3A + ora $52 + ora $24 + + ; E0EC: D0 B0 = BNE $E09E + ; Reuses the RTS at the end of the preceding helper. + .byte $D0, $B0 + + lda $03A0 + bne StateE0E6UpdateE10A + + lda $2E + + ; E0F5: D0 A7 = BNE $E09E + ; Reuses the same preceding RTS. + .byte $D0, $A7 + + lda $18 + and #$03 + tax + + lda $D8F3,x + sta $03A1 + + lda #$F8 + sta $03B9 + sta $03A0 + + +; $E10A +StateE0E6UpdateE10A: + lda #$50 + sta $01 + + lda $03B9 + sec + sbc $60 + sta $03B9 + + lda $60 + beq StateE0E6CheckPositionE124 + + lda $3F + and #$0F + bne StateE0E6CheckPositionE124 + + dec $03B9 + + +; $E124 +StateE0E6CheckPositionE124: + lda $03B9 + sta $00 + + cmp #$FC + bcc StateE0E6BuildStructureE139 + + lda $18 + and #$A0 + sta $2E + + lda #$00 + sta $03A0 + + rts + + +; --------------------------------------------------------------------------- +; $E139 +; Configure parameters and invoke the existing structure helper. +; --------------------------------------------------------------------------- + +StateE0E6BuildStructureE139: + lda $03A1 + sta $02 + + lda #$22 + sta $03 + + lda #$C0 + sta $04 + + lda #$20 + sta $0B + + jsr BuildIndirectStructure + + lda $03B9 + cmp #$60 + bcs StateE0E6ReturnE17E + + ldx $03A1 + inx + inx + inx + stx $00 + + jsr WriteStructureE16C + + ldx $03A1 + inx + stx $00 + + lda $04 + clc + adc #$08 + sta $04 + + +; --------------------------------------------------------------------------- +; $E16C +; Secondary entry. +; +; Writes pairs into the structure addressed through $04/$05. +; --------------------------------------------------------------------------- + +WriteStructureE16C: + ldy #$06 + +@write_loop: + lda #$60 + sta ($04),y + + dey + + lda $00 + sta ($04),y + + dec $00 + + dey + dey + dey + bpl @write_loop + + +; $E17E +StateE0E6ReturnE17E: + rts + +; --------------------------------------------------------------------------- +; $E17F-$E1A2: process four indexed runtime entries. +; --------------------------------------------------------------------------- + +ProcessRuntimeEntries: + jsr SortRuntimeSlots + + ldx #$03 + + +@entry_loop: + stx $0D + + lda $D8C0,x + sta $04 + + lda $88,x + tax + + lda $A8,x + beq @next_entry + + lda $D8C9,x + sta $0B + + jsr BuildRuntimeEntry + jsr BuildSlotStructure + + +@next_entry: + ldx $0D + dex + bpl @entry_loop + + rts + + +; --------------------------------------------------------------------------- +; $E1A3-$E1F8 +; Build/update an entry using the current slot state. +; +; X = current slot. +; Calls $E1F9 to derive the table index in A. +; --------------------------------------------------------------------------- + +BuildRuntimeEntry: + lda $80,x + sta $00 + + lda $8C,x + sta $01 + + jsr GetRuntimeEntryType + tay + + lda $D892,y + sta $02 + + lda #$33 + sta $03 + + cpy #$16 + bne EntryBuildDispatchE1D1 + + lda $04 + sta $039F + + clc + adc #$0C + sta $04 + + lda $01 + clc + adc #$08 + sta $01 + + lda #$23 + sta $03 + + +; $E1D1 +EntryBuildDispatchE1D1: + lda $D8A9,y + jsr BuildIndirectStructure + + lda $0E + beq EntryBuildReturnE1F8 + + lsr a + bcs EntryBuildAlternateE1F2 + + ldy #$11 + lda #$B2 + + +; $E1E2 +EntryBuildWriteLoopE1E2: + cpy #$09 + beq EntryBuildDecrementYE1EB + + sta ($04),y + + sec + sbc #$01 + + +; $E1EB +EntryBuildDecrementYE1EB: + dey + dey + dey + dey + + bpl EntryBuildWriteLoopE1E2 + + rts + +; $E1F2 +EntryBuildAlternateE1F2: + ldy #$15 + + lda #$AE + sta ($04),y + +; $E1F8 +EntryBuildReturnE1F8: + rts + + +; --------------------------------------------------------------------------- +; $E1F9-$E22E +; Derive an entry/type value from the current slot state. +; +; X = current slot +; Returns value in A on the main path. +; $0E and $039F are reset on entry. +; --------------------------------------------------------------------------- + +GetRuntimeEntryType: + lda #$00 + sta $0E + sta $039F + + lda $9C,x + beq EntryTypeAlternateE21F + + cmp #$05 + beq EntryTypeAlternateE21F + + ldy #$06 + + lda $BC,x + bne EntryTypeUseTwoE213 + + lda $0370,x + bne EntryTypeHaveBaseE215 + + +; $E213 +EntryTypeUseTwoE213: + ldy #$02 + + +; $E215 +EntryTypeHaveBaseE215: + tya + + ldy $36,x + beq EntryTypeAddFlagE21C + + dey + tya + + +; $E21C +EntryTypeAddFlagE21C: + ora #$10 + rts + + +; --------------------------------------------------------------------------- +; $E21F +; Alternate path for $9C,X == 0 or 5. +; --------------------------------------------------------------------------- + +EntryTypeAlternateE21F: + ldy $52 + dey + + ; E222: D0 0B = BNE $E22F + .byte $D0, $0B + + lda $AC,x + cmp #$0A + + ; E228: D0 05 = BNE $E22F + .byte $D0, $05 + + ldy #$02 + sty $0E + + rts +; --------------------------------------------------------------------------- +; $E22F-$E269: derive a value from current slot state. +; --------------------------------------------------------------------------- + +GetSlotValue: + lda $98,x + ora $B0,x + bne SlotValueReturnACE267 + + lda $58,x + beq SlotValueCheckPositionE243 + + cmp #$0D + bne SlotValueReturnACE267 + + lda $B8,x + cmp #$20 + bcc SlotValueReturnACE267 + + +; $E243 +SlotValueCheckPositionE243: + lda $90,x + ora $94,x + bne SlotValueCheckACE24D + + inc $0E + bne SlotValueReturnACE267 + + +; $E24D +SlotValueCheckACE24D: + lda $AC,x + cmp #$06 + bne SlotValueReturnACE267 + + ldy #$14 + + lda $DC,x + beq SlotValueCheckGlobalE25E + + bpl SlotValueReturnYE25C + + iny + + +; $E25C +SlotValueReturnYE25C: + tya + rts + + +; $E25E +SlotValueCheckGlobalE25E: + lda $3F + and #$02 + beq SlotValueReturnACE267 + + lda #$13 + rts + + +; $E267 +SlotValueReturnACE267: + lda $AC,x + rts + + +; --------------------------------------------------------------------------- +; $E26A-$E2AE: prepare structure parameters and tail-call $D1C7. +; X = current slot. +; --------------------------------------------------------------------------- + +BuildSlotStructure: + lda $039F + + ; E26D: F0 FA = BEQ $E269 + .byte $F0, $FA + + lda $84,x + cmp #$02 + + ; E273: F0 F4 = BEQ $E269 + .byte $F0, $F4 + + lda $0390,x + cmp #$F8 + + ; E27A: B0 ED = BCS $E269 + .byte $B0, $ED + + cmp $80,x + + ; E27E: 90 E9 = BCC $E269 + .byte $90, $E9 + + lda $0390,x + sta $00 + + lda #$A8 + sec + sbc $6C,x + sta $01 + + lda #$22 + sta $03 + + lda $039F + sta $04 + + lda RiderMotionState,x + beq @combine_flags + + lda #$20 + +@combine_flags: + ora $0B + sta $0B + + ldy $0398,x + + lda $D90B,y + sta $02 + + tya + beq @dispatch + + eor #$03 + +@dispatch: + jmp BuildIndirectStructure + + +; --------------------------------------------------------------------------- +; $E2AF-$E2F5 +; Update global structure position/state and optionally tail-call $D1C7. +; --------------------------------------------------------------------------- + +UpdateStructureState: + lda $03BD + sec + sbc $60 + sta $03BD + bcs @no_borrow + + dec $03BE + +@no_borrow: + ldy $52 + beq @continue + + dey + + ; E2C2: D0 A5 = BNE $E269 + .byte $D0, $A5 + + lda $20 + bne @continue + + lda $03BB + eor #$01 + sta $03BB + +@continue: + ldy $03BE + dey + bne SlotSpritesCheckBCE303 + + lda $03BD + sta $00 + + lda #$50 + sta $01 + + ldy $03BB + lda $D8E5,y + sta $02 + + lda #$32 + sta $03 + + lda #$C0 + sta $04 + + lda #$00 + sta $0B + + jmp BuildIndirectStructure + + +; --------------------------------------------------------------------------- +; $E2F6-$E358 +; Build/update a pair of OAM entries for the current slot. +; +; X = current slot +; Y = OAM/output offset +; +; $E303 is also a secondary entry from the preceding routine. +; --------------------------------------------------------------------------- + +BuildSlotSprites: + lda $036C,x + beq SlotSpritesReturnE358 + + lda $94,x + ora $90,x + beq SlotSpritesReturnE358 + + lda $BC,x + + +; $E303 +SlotSpritesCheckBCE303: + bne SlotSpritesReturnE358 + + lda $84,x + lsr a + bcc SlotSpritesReturnE358 + + lda $80,x + cmp #$10 + bcc SlotSpritesReturnE358 + + clc + adc #$F9 + sta $0297,y + + clc + adc #$F8 + sta $029B,y + + lda $8C,x + clc + adc #$08 + sta $0298,y + + clc + adc #$08 + sta $0294,y + + lda #$F6 + sta $0295,y + + lda #$F7 + sta $0299,y + + lda $3F + and #$04 + beq @tiles_ready + + lda #$F7 + sta $0295,y + + lda #$F6 + sta $0299,y + + +@tiles_ready: + lda $036C,x + and #$02 + sta $0296,y + sta $029A,y + + lda $94,x + bne SlotSpritesReturnE358 + + lda #$F8 + sta $0298,y + + +; $E358 +SlotSpritesReturnE358: + rts + +; --------------------------------------------------------------------------- +; $E359-$E3F3 +; Update global position/state and build four $02F0 OAM entries. +; --------------------------------------------------------------------------- + +UpdateGlobalDisplay: + lda $52 + bne GlobalDisplayBuildE3C6 + + lda $03E0 + beq GlobalDisplaySelectIndexE36B + + lda $3C + lsr a + bcs GlobalDisplayBuildE3C6 + + lda #$FC + bne GlobalDisplayStore01E3C8 + + +; $E36B +GlobalDisplaySelectIndexE36B: + lda $5C + and #$C0 + beq GlobalDisplayIndexReadyE37A + + ldy $4F + bne GlobalDisplayRotateE377 + + lda #$80 + + +; $E377 +GlobalDisplayRotateE377: + asl a + rol a + rol a + + +; $E37A +GlobalDisplayIndexReadyE37A: + tay + + lda $03B6 + cmp $D8FB,y + bcc GlobalDisplayIncreaseE39B + beq GlobalDisplayFinalizeE3AA + + lda $03B5 + sec + sbc #$0B + sta $03B5 + bcs GlobalDisplayFinalizeE3AA + + lda $03B6 + beq GlobalDisplayFinalizeE3AA + + dec $03B6 + jmp GlobalDisplayFinalizeE3AA + + +; $E39B +GlobalDisplayIncreaseE39B: + lda $03B5 + clc + adc $D8F7,y + sta $03B5 + bcc GlobalDisplayFinalizeE3AA + + inc $03B6 + + +; $E3AA +GlobalDisplayFinalizeE3AA: + ldx #$00 + + lda $03B6 + cmp #$20 + bcc GlobalDisplayCheck1AE3B8 + + sta $3C + sta $03E0 + + +; $E3B8 +GlobalDisplayCheck1AE3B8: + cmp #$1A + bcc GlobalDisplayCombineFCE3C1 + + dey + bne GlobalDisplayCombineFCE3C1 + + ldx #$B0 + + +; $E3C1 +GlobalDisplayCombineFCE3C1: + txa + ora $FC + sta $FC + + +; $E3C6 +GlobalDisplayBuildE3C6: + lda #$FE + + +; $E3C8 +GlobalDisplayStore01E3C8: + sta $01 + + lda $03B6 + clc + adc #$50 + sta $00 + + ldx #$0C + + +; $E3D4 +GlobalDisplayOamLoopE3D4: + lda #$CF + sta $02F0,x + + lda $01 + sta $02F1,x + + lda #$20 + sta $02F2,x + + lda $00 + sta $02F3,x + + clc + adc #$08 + sta $00 + + dex + dex + dex + dex + bpl GlobalDisplayOamLoopE3D4 + + rts + +; --------------------------------------------------------------------------- +; $E3F4-$E42A +; Initialize and sort the four slot indices in $88-$8B using values copied +; from $B8-$BB. +; --------------------------------------------------------------------------- + +SortRuntimeSlots: + ldx #$03 + + +@initialize: + txa + sta $88,x + + lda $B8,x + sta $00,x + + dex + bpl @initialize + + + lda $0373 + beq @start_sort + + lda $6F + sta $03 + + +@start_sort: + ldx #$03 + stx $0D + + +@outer_loop: + ldy #$00 + + +@inner_loop: + ldx $88,y + lda $00,x + + ldx $89,y + cmp $00,x + bcc @no_swap + + lda $0088,y + sta $0089,y + stx $88,y + + +@no_swap: + iny + cpy $0D + bne @inner_loop + + dec $0D + bne @outer_loop + + rts + +; --------------------------------------------------------------------------- +; $E42B-$E455: process four sorted runtime slots. +; --------------------------------------------------------------------------- + +ProcessSortedSlots: + lda $03A3 + beq @return + + ldx #$03 + +@slot_loop: + lda $88,x + beq @next_slot + + cmp #$03 + bne @process_slot + + lda $0373 + bne @next_slot + +@process_slot: + lda $D8C0,x + sta $07 + + ldy $03B3 + jsr WriteSlotPattern + +@next_slot: + dex + bpl @slot_loop + + lda $03B3 + eor #$02 + sta $03B3 + +@return: + rts + +; --------------------------------------------------------------------------- +; $E456-$E49A: prepare/process four sorted slot entries. +; --------------------------------------------------------------------------- + +PrepareSortedSlots: + ldy #$03 + +@slot_loop: + sty $05 + + lda $D8C0,y + sta $07 + + lda $0088,y + beq @next_slot + + tax + + lda $84,x + sta $02 + sta $03 + sta $04 + + lsr a + bcs @adjust + + lda $80,x + cmp #$70 + bcc @adjust + + cmp #$90 + bcs @adjust + + jsr ResetSlotState + + lda #$01 + sta $03A6 + bne @process + +@adjust: + lda $80,x + cmp #$F8 + bcc @check_second + + inc $03 + +@check_second: + cmp #$F0 + bcc @process + + inc $04 + +@process: + jsr ProcessSlotBits + +@next_slot: + ldy $05 + dey + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $E49B-$E4AC: process each clear low-bit field in $02-$04. +; --------------------------------------------------------------------------- + +ProcessSlotBits: + ldx #$00 + +@loop: + lda $02,x + lsr a + bcs @next + + txa + tay + jsr WriteSlotPattern + +@next: + inx + cpx #$03 + bcc @loop + + rts + +; --------------------------------------------------------------------------- +; $E4AD-$E4C7: write $F8 into three four-byte-spaced entries. +; --------------------------------------------------------------------------- + +WriteSlotPattern: + lda #$02 + sta $01 + + lda $07 + clc + adc $D8DC,y + sta $00 + + ldy #$00 + lda #$F8 + +@write_loop: + sta ($00),y + + iny + iny + iny + iny + + cpy #$0C + bcc @write_loop + + rts + +; --------------------------------------------------------------------------- +; $E4C8-$E52B +; Scan runtime slots and update $03A3 based on matching slot pairs. +; +; $E52C is a separately callable comparison helper. +; --------------------------------------------------------------------------- + +UpdateSlotPairState: + ldx #$01 + + +; $E4CA +SlotPairOuterE4CA: + stx $0C + + lda $84,x + lsr a + bcs SlotPairStartInnerE4D9 + + bne SlotPairNextOuterE50A + + lda $80,x + cmp #$F8 + bcc SlotPairNextOuterE50A + + +; $E4D9 +SlotPairStartInnerE4D9: + lda #$00 + sta $0A + + txa + tay + iny +SlotPairInnerE4E2: + sty $05 + + + lda $84,y + lsr a + bcs SlotPairCompareE4F0 + + bne SlotPairNextInnerE4FF + + lda $80,x + cmp #$F8 + bcc SlotPairNextInnerE4FF + + +; $E4F0 +SlotPairCompareE4F0: + jsr CompareSlotDistance + beq SlotPairNextInnerE4FF + + inc $0A + + lda $0B + asl a + asl a + ora $05 + sta $0B + + +; $E4FF +SlotPairNextInnerE4FF: + iny + cpy #$04 + bcc SlotPairInnerE4E2 + + lda $0A + cmp #$02 + bcs SlotPairMultipleE513 + + +; $E50A +SlotPairNextOuterE50A: + ldx $0C + dex + bpl SlotPairOuterE4CA + + lda #$00 + beq SlotPairStoreStateE528 + + +; $E513 +SlotPairMultipleE513: + bne SlotPairSetStateE526 + + lda $0B + pha + + and #$03 + tay + + pla + lsr a + lsr a + and #$03 + tax + + jsr CompareSlotDistance + beq SlotPairNextOuterE50A + + +; $E526 +SlotPairSetStateE526: + lda #$01 + + +; $E528 +SlotPairStoreStateE528: + sta $03A3 + rts + +; --------------------------------------------------------------------------- +; $E52C-$E53C +; Compare the $8C values of slots X and Y. +; Returns zero for the middle separation range. +; --------------------------------------------------------------------------- + +CompareSlotDistance: + lda $8C,x + sec + sbc $008C,y + + cmp #$EC + bcs @return + + cmp #$12 + bcc @return + + lda #$00 + +@return: + rts + + +; --------------------------------------------------------------------------- +; $E53D-$E540 +; Four position/state comparison thresholds. +; +; Referenced by UpdateFourSlotPositionsE96C. +; --------------------------------------------------------------------------- + +SlotPositionThresholdTableE53D: + .byte $0E, $1A, $26, $32 + + +; --------------------------------------------------------------------------- +; $E541-$E54D +; 13-byte parameter/position table. +; --------------------------------------------------------------------------- + +DataTableE541: + .byte $DD, $DC, $DB, $7C, $74, $6C, $90 + .byte $88, $80, $78, $70, $68, $60 + + +; --------------------------------------------------------------------------- +; $E54E-$E553 +; Low bytes of six RAM pointers. +; +; Combined with PointerHighTableE554: +; 0 -> $0400 +; 1 -> $0440 +; 2 -> $0480 +; 3 -> $04C0 +; 4 -> $0500 +; 5 -> $0540 +; --------------------------------------------------------------------------- + +PointerLowTableE54E: + .byte $00, $40, $80, $C0, $00, $40 + + +; --------------------------------------------------------------------------- +; $E554-$E559 +; High bytes of six RAM pointers. +; --------------------------------------------------------------------------- + +PointerHighTableE554: + .byte $04, $04, $04, $04, $05, $05 + + +; --------------------------------------------------------------------------- +; $E55A-$E56D +; Continuation of the split script-pointer low-byte table. +; +; The actual indexed base is $E559, one byte before this block. +; The $E559 byte remains in the preceding already-converted data. +; --------------------------------------------------------------------------- + +ScriptPointerLowContinuationE55A: + .byte $84, $99, $99, $A6 + .byte $BB, $D0, $E5, $F8 + .byte $0B, $1E, $31, $3E + .byte $49, $52, $67, $84 + .byte $86, $89, $89, $8C + + +; --------------------------------------------------------------------------- +; $E56E-$E582 +; High-byte half of the script pointer table. +; +; Indexed with the same Y that indexes the low-byte table at $E559. +; --------------------------------------------------------------------------- + +ScriptPointerHighTableE56E: + .byte $97 + .byte $E5, $E5, $E5, $E5 + .byte $E5, $E5, $E5, $E5 + .byte $E6, $E6, $E6, $E6 + .byte $E6, $E6, $E6, $E6 + .byte $E6, $E6, $E6, $E6 + + +; --------------------------------------------------------------------------- +; $E583 +; Final byte immediately preceding the first script. +; Keep binary-backed for now. +; This byte must be magical +; --------------------------------------------------------------------------- + .byte $E6 + +; --------------------------------------------------------------------------- +; $E584-$E598 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Entries are two-byte records terminated by $FF. +; --------------------------------------------------------------------------- + +ScriptE584: + .byte $04, $87 + .byte $08, $88 + .byte $08, $44 + .byte $29, $06 + .byte $29, $00 + .byte $31, $85 + .byte $35, $84 + .byte $35, $45 + .byte $51, $00 + .byte $52, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E599-$E5A5 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Six two-byte entries followed by $FF. +; Note: pointer-table indexes 2 and 3 both select this stream. +; --------------------------------------------------------------------------- + +ScriptE599: + .byte $04, $07 + .byte $04, $88 + .byte $0C, $86 + .byte $10, $83 + .byte $14, $85 + .byte $16, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5A6-$E5BA +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5A6: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $20, $06 + .byte $22, $00 + .byte $30, $08 + .byte $50, $84 + .byte $52, $83 + .byte $54, $43 + .byte $6C, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5BB-$E5CF +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5BB: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $23, $06 + .byte $2A, $00 + .byte $2F, $84 + .byte $33, $83 + .byte $34, $43 + .byte $53, $00 + .byte $55, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5D0-$E5E4 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5D0: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $17, $06 + .byte $1A, $00 + .byte $1F, $84 + .byte $23, $83 + .byte $23, $43 + .byte $35, $00 + .byte $37, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5E5-$E5F7 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5E5: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $12, $06 + .byte $12, $00 + .byte $1A, $83 + .byte $1B, $43 + .byte $24, $00 + .byte $26, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5F8-$E60A +; Script stream selected through the $E559/$E56E pointer tables. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5F8: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $22, $06 + .byte $22, $00 + .byte $2A, $82 + .byte $2B, $4A + .byte $38, $00 + .byte $3A, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E60B-$E61D +; Next script stream. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE60B: + .byte $04, $88 + .byte $0A, $8B + .byte $0B, $49 + .byte $17, $06 + .byte $17, $00 + .byte $25, $83 + .byte $26, $43 + .byte $3B, $00 + .byte $3D, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E61E-$E630 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE61E: + .byte $04, $8B + .byte $08, $49 + .byte $18, $06 + .byte $18, $00 + .byte $1F, $84 + .byte $20, $82 + .byte $25, $4A + .byte $35, $00 + .byte $37, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E631-$E63D +; Script stream selected through the $E559/$E56E pointer tables. +; +; Six two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE631: + .byte $02, $01 + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $1A, $06 + .byte $1A, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E63E-$E648 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Five two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE63E: + .byte $04, $8B + .byte $08, $49 + .byte $18, $06 + .byte $1A, $00 + .byte $1F, $0B + .byte $FF + + +; --------------------------------------------------------------------------- +; $E649-$E651 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Four two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE649: + .byte $0D, $0C + .byte $17, $0C + .byte $28, $0C + .byte $2B, $0C + .byte $FF + + +; --------------------------------------------------------------------------- +; $E652-$E666 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE652: + .byte $04, $8B + .byte $08, $49 + .byte $22, $06 + .byte $22, $00 + .byte $22, $14 + .byte $30, $0F + .byte $31, $83 + .byte $32, $82 + .byte $33, $4A + .byte $4B, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E667-$E683 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Fourteen two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE667: + .byte $00, $0B + .byte $18, $06 + .byte $18, $84 + .byte $1A, $83 + .byte $1A, $43 + .byte $3B, $00 + .byte $42, $10 + .byte $43, $85 + .byte $48, $84 + .byte $4A, $83 + .byte $4A, $43 + .byte $5A, $00 + .byte $5A, $0E + .byte $5C, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E684-$E688 +; Shared script stream. +; +; $E684 and $E686 are both valid entry points. +; Entering at $E684 falls through into the $E686 portion. +; --------------------------------------------------------------------------- + +ScriptE684: + .byte $0E, $0D + +ScriptE686: + .byte $0F, $0E + .byte $FF + + +; --------------------------------------------------------------------------- +; $E689-$E68B +; Single-entry script terminated by $FF. +; --------------------------------------------------------------------------- + +ScriptE689: + .byte $00, $12 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E68C-$E696 +; Five two-byte script entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE68C: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $1A, $13 + .byte $1A, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E697-$E699 +; Single two-byte record followed by $FF. +; +; Structurally matches the script encoding used immediately before it, +; but its consumer has not yet been definitively identified. +; --------------------------------------------------------------------------- + +DataE697: + .byte $1E, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E69A-$E6A0 +; Seven-byte rider sprite/tile lookup table. +; --------------------------------------------------------------------------- + +RiderSpriteTileTableE69A: + .byte $CB, $CD, $CC, $CE + .byte $CB, $CB, $CB + + +; --------------------------------------------------------------------------- +; $E6A1-$E6AC +; Three four-byte descriptors consumed by BuildPairedPpuRows. +; +; BuildPairedPpuRows receives X as the descriptor offset: +; +; X = $00 -> descriptor at $E6A1 +; X = $04 -> descriptor at $E6A5 +; X = $08 -> descriptor at $E6A9 +; +; Physical record format: +; +; byte 0 = first PPU address high byte +; second PPU address uses byte 0 XOR $04 +; byte 1 = alternating tile/value B +; byte 2 = alternating tile/value A +; byte 3 = PPU address low byte +; --------------------------------------------------------------------------- + +PpuRowDescriptorsE6A1: + +PpuRowDescriptor0E6A1: + .byte $22, $64, $62, $C0 + +PpuRowDescriptor1E6A5: + .byte $22, $74, $63, $E0 + +PpuRowDescriptor2E6A9: + .byte $21, $83, $FC, $C0 + + +; --------------------------------------------------------------------------- +; $E6AD-$E6B6 +; --------------------------------------------------------------------------- + +DataE6AD: + .byte $03, $01, $02, $02 + .byte $00, $05, $05, $06 + .byte $04, $04 + +; --------------------------------------------------------------------------- +; $E6B7-$E6E0 +; First five little-endian command-handler vectors. +; --------------------------------------------------------------------------- + +CommandHandlerVectorsE6B7: + .word ClearSlotStateE963 + .word SetSlotA4OneE8E3 + .word InitializeSlotMode6E85D + .word AdjustSlotBCE86A + .word InitializeSlotFieldsE845 + .word InitializeSlotMode2E854 + .word UpdateSlot0388E934 + .word UpdateSlotFlagE818 + .word UpdateGlobalStateEA8F + .word InitializeSlotMode4E879 + .word UpdateRiderMotionState + .word InitializeSlotModeE8EE + .word UpdateSlotBoundaryE8FF + .word Slot0388CheckA4E956 + .word ClearRiderMotionState + .word InitializeRiderMotionState + .word SetSlotBCE4To10E8FA + .word CommandHandlerVectorsE6B7 + .word InitializeGlobalB6E8D3 + .word UpdateSlotStateIfReadyE8E7 + .word $E8F5 + + +; --------------------------------------------------------------------------- +; $E6E1-$E6F4 +; 20-byte indexed lookup table. +; --------------------------------------------------------------------------- + +DataTableE6E1: + .byte $08, $07, $05, $01 + .byte $0B, $06, $0A, $0E + .byte $03, $04, $0C, $0D + .byte $0F, $10, $12, $13 + .byte $11, $15, $14, $00 + + +; --------------------------------------------------------------------------- +; $E6F5-$E70A +; 22-byte lookup / position table immediately preceding code at $E70B. +; --------------------------------------------------------------------------- + +DataTableE6F5: + .byte $09, $18, $20, $28 + .byte $30, $38, $40, $48 + .byte $50, $58, $60, $68 + .byte $70, $78, $80, $88 + .byte $90, $98, $A0, $A8 + .byte $B8, $CC + + +; --------------------------------------------------------------------------- +; $E70B-$E732 +; Update per-rider $64/$E0 state from $60. +; +; Processes rider slots 3 -> 0. +; $E0,X wraps modulo 64. +; --------------------------------------------------------------------------- + +UpdateRiderCourseStateE70B: + ldx #$03 ; E70B: A2 03 + +@next_rider: + lda $60,x ; E70D: B5 60 + beq @clear_state ; E70F: F0 1A + + lda $64,x ; E711: B5 64 + sec ; E713: 38 + sbc $60,x ; E714: F5 60 + beq @adjust ; E716: F0 06 + bmi @adjust ; E718: 30 04 + + sta $64,x ; E71A: 95 64 + bne @clear_state ; E71C: D0 0D + +@adjust: + clc ; E71E: 18 + adc #$08 ; E71F: 69 08 + sta $64,x ; E721: 95 64 + + inc $E0,x ; E723: F6 E0 + + lda $E0,x ; E725: B5 E0 + and #$3F ; E727: 29 3F + sta $E0,x ; E729: 95 E0 + +@clear_state: + lda #$00 ; E72B: A9 00 + sta $D8,x ; E72D: 95 D8 + + dex ; E72F: CA + bpl @next_rider ; E730: 10 DB + + rts ; E732: 60 + + + +; --------------------------------------------------------------------------- +; $E733-$E75F +; Update rider script/course state. +; +; Slots 3, 2, and 1 are called through the $E73B secondary entry. +; After X reaches zero, execution falls through into $E73B for rider 0. +; --------------------------------------------------------------------------- + +UpdateAllRiderScriptStateE733: + ldx #$03 + +@next_rider: + jsr UpdateSingleRiderScriptStateE73B + dex + bne @next_rider + + +; --------------------------------------------------------------------------- +; $E73B +; Update script/course state for rider X. +; --------------------------------------------------------------------------- + +UpdateSingleRiderScriptStateE73B: + lda $60,x + clc + adc $C8,x + sta $C8,x + + lda $58,x + + ; E744: D0 1C = BNE $E762 + .byte $D0, $1C + + lda $C0,x + sec + sbc #$40 + + ; E74B: 30 06 = BMI $E753 + bmi @return + + lsr a + lsr a + cmp #$16 + bcc @store_script_index + +@return: + rts + +@store_script_index: + sta $58,x + inc $58,x + + lda #$00 + sta $C4,x + + lda $64,x + sta $C8,x + + +; --------------------------------------------------------------------------- +; $E760-$E78F +; Load and process the current rider's script stream. +; +; X = current rider slot +; +; $58,X selects the script pointer. +; $C4,X is the byte offset within that script. +; $C8,X is compared against the first byte of each script entry. +; --------------------------------------------------------------------------- + +ProcessRiderScriptE760: + dec $C8,x ; E760: D6 C8 + +; $E762 is the target of the BNE at $E744. +ProcessRiderScriptEntryE762: + ldy $58,x ; E762: B4 58 + + lda $E559,y ; E764: B9 59 E5 + sta $0A ; E767: 85 0A + + lda $E56E,y ; E769: B9 6E E5 + sta $0B ; E76C: 85 0B + + ldy $C4,x ; E76E: B4 C4 + lda ($0A),y ; E770: B1 0A + + cmp #$FF ; E772: C9 FF + + ; E774: F0 5A = BEQ $E7D0 + .byte $F0, $5A + + sta $0F ; E776: 85 0F + + cmp $C8,x ; E778: D5 C8 + beq @load_command ; E77A: F0 02 + + ; E77C: B0 D5 = BCS $E753 + ; $E753 is the RTS in UpdateSingleRiderScriptStateE73B. + .byte $B0, $D5 + +@load_command: + iny ; E77E: C8 + lda ($0A),y ; E77F: B1 0A + + bit $E703 ; E781: 2C 03 E7 + + ; E784: D0 1D = BNE $E7A3 + .byte $D0, $1D + + bit $E6FB ; E786: 2C FB E6 + + ; E789: D0 67 = BNE $E7F2 + .byte $D0, $67 + + jsr $E794 ; E78B: 20 94 E7 + + inc $C4,x ; E78E: F6 C4 + + + +; --------------------------------------------------------------------------- +; $E790-$E793 +; Advance to the next two-byte script command and continue processing. +; --------------------------------------------------------------------------- + + inc $C4,x ; E790: F6 C4 + + ; E792: D0 CE = BNE $E762 + .byte $D0, $CE + + +; --------------------------------------------------------------------------- +; $E794-$E7A2 +; Dispatch A through the 21-entry handler vector table at $E6B7. +; +; A = handler index +; --------------------------------------------------------------------------- + +DispatchRiderCommandE794: + asl a ; E794: 0A + tay ; E795: A8 + + lda CommandHandlerVectorsE6B7,y + ; E796: B9 B7 E6 + sta $00 ; E799: 85 00 + + lda CommandHandlerVectorsE6B7+1,y + ; E79B: B9 B8 E6 + sta $01 ; E79E: 85 01 + + jmp ($0000) ; E7A0: 6C 00 00 + + +; --------------------------------------------------------------------------- +; $E7A3-$E7CF +; Update rider command/state from the low nibble of the current script +; command, then advance to the next two-byte command. +; --------------------------------------------------------------------------- + +UpdateRiderCommandStateE7A3: + lda $B0,x ; E7A3: B5 B0 + ora $98,x ; E7A5: 15 98 + bne @advance_script ; E7A7: D0 21 + + lda ($0A),y ; E7A9: B1 0A + and #$0F ; E7AB: 29 0F + sta $00 ; E7AD: 85 00 + + lda RiderMotionState,x ; E7AF: B5 A4 + cmp #$01 ; E7B1: C9 01 + beq @advance_script ; E7B3: F0 15 + + lda $00 ; E7B5: A5 00 + sta $AC,x ; E7B7: 95 AC + + lda $58,x ; E7B9: B5 58 + cmp #$03 ; E7BB: C9 03 + beq @advance_script ; E7BD: F0 0B + + lda $AC,x ; E7BF: B5 AC + sec ; E7C1: 38 + sbc #$02 ; E7C2: E9 02 + tay ; E7C4: A8 + + lda DataE6AD,y ; E7C5: B9 AD E6 + sta $D4,x ; E7C8: 95 D4 + +@advance_script: + inc $C4,x ; E7CA: F6 C4 + inc $C4,x ; E7CC: F6 C4 + + ; E7CE: D0 92 = BNE $E762 + .byte $D0, $92 + + +; --------------------------------------------------------------------------- +; $E7D0-$E7F1 +; End/reset the current rider script. +; --------------------------------------------------------------------------- + +ResetRiderScriptE7D0: + lda #$00 ; E7D0: A9 00 + sta $58,x ; E7D2: 95 58 + sta $D4,x ; E7D4: 95 D4 + + lda $A0,x ; E7D6: B5 A0 + bne @clear_state ; E7D8: D0 12 + + lda RiderMotionState,x ; E7DA: B5 A4 + cmp #$01 ; E7DC: C9 01 + beq @clear_state ; E7DE: F0 0C + + lda #$00 ; E7E0: A9 00 + sta $BC,x ; E7E2: 95 BC + + lda RiderMotionState,x ; E7E4: B5 A4 + cmp #$02 ; E7E6: C9 02 + bne @clear_state ; E7E8: D0 02 + + inc RiderMotionState,x ; E7EA: F6 A4 + +@clear_state: + lda #$00 ; E7EC: A9 00 + sta $036C,x ; E7EE: 9D 6C 03 + rts ; E7F1: 60 + + +; --------------------------------------------------------------------------- +; $E7F2-$E7FB +; Store command low nibble / associated trigger state, then rejoin the +; common command-advance path at $E7CA. +; --------------------------------------------------------------------------- + +StoreRiderCommandStateE7F2: + and #$0F ; E7F2: 29 0F + sta $CC,x ; E7F4: 95 CC + + lda $0F ; E7F6: A5 0F + sta $D0,x ; E7F8: 95 D0 + + ; E7FA: 10 CE = BPL $E7CA + .byte $10, $CE + +; --------------------------------------------------------------------------- +; $E7FC-$E7FE +; Load rider lane-position state. +; --------------------------------------------------------------------------- + +LoadRiderLanePositionE7FC: + ldy $0360,x ; E7FC: BC 60 03 + +; --------------------------------------------------------------------------- +; $E7FF-$E817 +; Load a table-selected value through an indirect pointer. +; +; X = current slot +; Y = table selector on entry +; +; $E80D is also a secondary entry that sets $03:$04. +; --------------------------------------------------------------------------- + +LoadIndexedValueE7FF: + lda $E0,x + sta $08 + + jsr LoadTablePointerE80D + + sty $07 + + ldy $08 + lda ($03),y + + rts + + +; --------------------------------------------------------------------------- +; $E80D +; Load a pointer from split low/high tables into $03:$04. +; --------------------------------------------------------------------------- + +LoadTablePointerE80D: + lda PointerLowTableE54E,y + sta $03 + + lda PointerHighTableE554,y + sta $04 + + rts + +; --------------------------------------------------------------------------- +; $E818-$E835 +; Set the per-slot $98 flag when slot state satisfies the required bounds. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlotFlagE818: + lda $B0,x + bne @return + + lda $AC,x + cmp #$07 + bpl @return + + lda $94,x + cmp #$03 + bcs @set_flag + + cmp #$02 + bne @return + + lda $90,x + asl a + bcc @return + + +@set_flag: + lda #$FF + sta $98,x + + +@return: + rts + +; --------------------------------------------------------------------------- +; $E836-$E844: update $8C for four runtime slots. +; --------------------------------------------------------------------------- + +UpdateFourSlot8CE836: + ldx #$03 + +@slot_loop: + lda $B0,x + bne @next_slot + + jsr CalculateSlotRemaining + sta $8C,x + +@next_slot: + dex + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $E845-$E853: initialize per-slot $D8/$BC/$E4 fields. +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeSlotFieldsE845: + lda #$80 + sta $D8,x + + lda #$05 + jsr InitializeAndCalculateSlotE893 + + lsr a +StoreSlotBCE4E84F: + sta $BC,x + sta $E4,x + + rts + +; --------------------------------------------------------------------------- +; $E854-$E85C +; Selector-$02 wrapper. Tail-calls the shared $E86F continuation. +; --------------------------------------------------------------------------- + +InitializeSlotMode2E854: + lda #$02 + jsr InitializeAndCalculateSlotE893 + + lsr a + jmp AdjustSlotBCFromAE86F + + +; --------------------------------------------------------------------------- +; $E85D-$E869 +; Initialize slot state using selector $06, reuse the $E84F continuation, +; then set $D8,X to $60. +; --------------------------------------------------------------------------- + +InitializeSlotMode6E85D: + lda #$06 + jsr InitializeAndCalculateSlotE893 + + jsr StoreSlotBCE4E84F + + lda #$60 + sta $D8,x + + rts + +; --------------------------------------------------------------------------- +; $E86A-$E878 +; Adjust $BC,X using a value derived from selector $01. +; +; $E86F is also a secondary entry used by the $E854 wrapper. +; --------------------------------------------------------------------------- + +AdjustSlotBCE86A: + lda #$01 + jsr InitializeAndCalculateSlotE893 + + +; $E86F +AdjustSlotBCFromAE86F: + sta $00 + + lda $E4,x + sec + sbc $00 + sta $BC,x + + rts + +; --------------------------------------------------------------------------- +; $E879-$E892 +; Initialize/update slot fields using selector $04. +; X = current slot. +; --------------------------------------------------------------------------- +InitializeSlotMode4E879: + lda #$04 + jsr InitializeAndCalculateSlotE893 + + asl a + jsr StoreSlotBCE4E84F + + lda $A0,x + beq @set_d8 + + lda $BC,x + clc + adc #$10 + jsr StoreSlotBCE4E84F + +@set_d8: + lda #$40 + sta $D8,x + + rts + +; --------------------------------------------------------------------------- +; $E893-$E89C +; Store the incoming value into $D4,X/$B4,X, then return $C8,X-$D0,X in A. +; +; Entry: +; A = value to store +; X = current slot +; +; Return: +; A = $C8,X - $D0,X +; --------------------------------------------------------------------------- + +InitializeAndCalculateSlotE893: + sta $D4,x + sta $B4,x + + lda $C8,x + sec + sbc $D0,x + + rts + +; --------------------------------------------------------------------------- +; $E89D-$E8D2 +; Per-slot state helpers. +; --------------------------------------------------------------------------- + +UpdateRiderMotionState: + lda RiderMotionState,x + beq @update + + cmp #$01 + beq SlotA4ReturnE8BE + + +@update: + lda #$03 + jsr InitializeAndCalculateSlotE893 + + asl a + jsr AdjustSlotBCFromAE86F + + lda RiderMotionState,x + cmp #$04 + bne SlotA4ReturnE8BE + + jsr UpdateSlotFrom94DD06 + + lda #$02 + sta RiderMotionState,x + sta $0364,x + + +; $E8BE +SlotA4ReturnE8BE: + rts + + +; --------------------------------------------------------------------------- +; $E8BF +; Clear two per-slot state fields. +; --------------------------------------------------------------------------- + +ClearRiderMotionState: + lda #$00 + sta RiderMotionState,x + sta $A0,x + rts + +; --------------------------------------------------------------------------- +; $E8C6 +; Initialize RiderMotionState,X according to $B8,X. +; --------------------------------------------------------------------------- + +InitializeRiderMotionState: + lda #$01 + sta RiderMotionState,x + + lda $B8,x + cmp #$20 + bcs @return + + inc RiderMotionState,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $E8D3-$E8E2 +; Set $03B6 to $08 when the current slot/global state is completely clear. +; +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeGlobalB6E8D3: + txa + ora $B0,x + ora $03E0 + ora $3C + bne @return + + lda #$08 + sta $03B6 + +@return: + rts + +; --------------------------------------------------------------------------- +; $E8E3-$E8FE +; Small slot-state entry/trampoline cluster. +; --------------------------------------------------------------------------- + +; $E8E3 +SetSlotA4OneE8E3: + lda #$01 + + ; E8E5: D0 DC = BNE $E8C3 + ; $E8C3 is inside the preceding ClearRiderMotionState helper. + .byte $D0, $DC + + +; --------------------------------------------------------------------------- +; $E8E7 +; If $B0,X is clear, enter the $DCFE state-update path. +; --------------------------------------------------------------------------- + +UpdateSlotStateIfReadyE8E7: + lda $B0,x + + ; E8E9: D0 F7 = BNE $E8E2 + ; Reuses the RTS immediately before this cluster. + .byte $D0, $F7 + + jmp StateDCF2Increment94DCFE + + +; --------------------------------------------------------------------------- +; $E8EE +; Initialize $A0,X/$D8,X and tail-call shared $E84F store entry. +; --------------------------------------------------------------------------- + +InitializeSlotModeE8EE: + lda #$02 + sta $A0,x + + lsr a + sta $D8,x + + lda #$30 + jmp StoreSlotBCE4E84F + + +; --------------------------------------------------------------------------- +; $E8FA +; Tail-call the shared $E84F entry with A=$10. +; --------------------------------------------------------------------------- + +SetSlotBCE4To10E8FA: + lda #$10 + jmp StoreSlotBCE4E84F + +; --------------------------------------------------------------------------- +; $E8FF-$E926 +; Update per-slot boundary/position state while preserving Y. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlotBoundaryE8FF: + lda $B0,x + bne @return + + lda RiderMotionState,x + beq @save_y + + lsr a + lsr a + bcc @return + + +@save_y: + sty $01 + + lda $94,x + beq @restore_y + + ldy #$04 + + lda $5C,x + asl a + asl a + bcc @store_boundary + + dey + + +@store_boundary: + lda #$01 + sta $036C,x + + iny + iny + + jsr $CE5C + + +@restore_y: + ldy $01 + + +@return: + rts + +; --------------------------------------------------------------------------- +; $E927-$E933 +; Process each slot whose $CC,X field is nonzero. +; --------------------------------------------------------------------------- + +UpdateFourSlotCCE927: + ldx #$03 + +@slot_loop: + lda $CC,x + beq @next_slot + + jsr $E794 + +@next_slot: + dex + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $E934-$E962 +; Update per-slot $0388 state and dispatch into the existing state handlers. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlot0388E934: + lda $B0,x + + ; E936: D0 FB = BNE $E933 + .byte $D0, $FB + + ldy #$00 + + lda $94,x + + ; E93C: F0 F5 = BEQ $E933 + .byte $F0, $F5 + + cmp #$02 + bcs @store_state + + lda $90,x + cmp $D8,x + bcs @store_state + + iny + + +@store_state: + tya + sta $0388,x + + lda $A0,x + cmp #$02 + beq Slot0388UpdateFrom94E960 + jmp StateDCF2ZeroDCFA + + +; $E956 +; Secondary entry/path. +Slot0388CheckA4E956: + lda RiderMotionState,x + cmp #$01 + + ; E95A: D0 D7 = BNE $E933 + .byte $D0, $D7 + + lda #$00 + sta $BC,x + + +; $E960 +Slot0388UpdateFrom94E960: + jmp UpdateSlotFrom94DD06 + +; --------------------------------------------------------------------------- +; $E963-$E96B +; Clear three per-slot state fields. +; X = current slot. +; --------------------------------------------------------------------------- + +ClearSlotStateE963: + lda #$00 + sta $CC,x + sta $D4,x + sta $B4,x + rts +; --------------------------------------------------------------------------- +; $E96C-$EA06 +; Update four slots' $B8 position/state values. +; --------------------------------------------------------------------------- + +UpdateFourSlotPositionsE96C: + ldx #$03 + + +; $E96E +SlotPositionLoopE96E: + lda $58,x + cmp #$15 + beq SlotPositionCheckTableE97F + + cmp #$14 + beq SlotPositionCheckTableE97F + + lda $B8,x + clc + adc $DC,x + sta $B8,x + + +; $E97F +SlotPositionCheckTableE97F: + ldy #$03 + +; $E981 +SlotPositionTableLoopE981: + lda $B8,x + cmp SlotPositionThresholdTableE53D,y + + ; E986: F0 79 = BEQ $EA01 + ; Keep literal because the symbolic target is outside + ; ca65's relative branch range during reconstruction. + .byte $F0, $79 + + dey + bpl SlotPositionTableLoopE981 + + + lda RiderMotionState,x + beq SlotPositionCheckBoundsE9C4 + + cmp #$01 + bne SlotPositionCheckState3E9B8 + + lda $B8,x + cmp #$20 + bcs SlotPositionCheckBoundsE9C4 + + lda #$04 + sta RiderMotionState,x + + lda $58,x + beq SlotPositionResetBCE9AD + + cmp #$12 + beq SlotPositionResetBCE9AD + + cmp #$10 + beq SlotPositionResetBCE9AD + + cmp #$0D + bne SlotPositionNextE9E3 + + +; $E9AD +SlotPositionResetBCE9AD: + lda #$00 + sta $BC,x + + jsr UpdateSlotFrom94DD06 + + dec RiderMotionState,x + bne SlotPositionNextE9E3 + + +; $E9B8 +SlotPositionCheckState3E9B8: + cmp #$03 + beq SlotPositionCheckBoundsE9C4 + + lda $B8,x + cmp #$20 + bcc SlotPositionCheckBoundsE9C4 + + dec $B8,x + + +; $E9C4 +SlotPositionCheckBoundsE9C4: + lda $B8,x + cmp #$08 + bcc SlotPositionLowE9E9 + + cmp #$3A + bcc SlotPositionNextE9E3 + + lda #$39 + sta $B8,x + + +; $E9D2 +; Shared entry from the $E9F9 path below. +SlotPositionCheckFlagsE9D2: + lda $9C,x + ora $03E0,x + bne SlotPositionNextE9E3 + + +; $E9D9 +SlotPositionFlipDirectionE9D9: + lda $DC,x + bne @invert + + lda #$FF + +@invert: + eor #$FE + sta $DC,x + + +; $E9E3 +SlotPositionNextE9E3: + dex + + ; E9E4: 30 21 = BMI $EA07 + ; $EA07 remains in the binary-backed next block. + .byte $30, $21 + + jmp SlotPositionLoopE96E + + +; $E9E9 +SlotPositionLowE9E9: + lda $9C,x + bne SlotPositionUseCurrentE9F1 + + lda #$07 + bne SlotPositionStoreE9F9 + + +; $E9F1 +SlotPositionUseCurrentE9F1: + lda $B8,x + cmp #$02 + bcs SlotPositionStoreE9F9 + + lda #$01 + + +; $E9F9 +SlotPositionStoreE9F9: + sta $B8,x + + cpx #$00 + beq SlotPositionFlipDirectionE9D9 + + bne SlotPositionCheckFlagsE9D2 + + +; $EA01 +SlotPositionTableMatchEA01: + lda #$00 + sta $DC,x + + beq SlotPositionNextE9E3 + + +; --------------------------------------------------------------------------- +; $EA07-$EA43 +; Update slot-0 direction/global trigger state from input/state conditions. +; --------------------------------------------------------------------------- + +UpdateSlot0DirectionEA07: + lda $4F + beq Slot0DirectionReturnEA43 + + lda $98 + ora $03E0 + ora $03F7 + bne Slot0DirectionReturnEA43 + + lda $B0 + beq @check_state + + lda $0388 + cmp #$02 + bne Slot0DirectionReturnEA43 + + inc $0388 + + +@check_state: + lda $9C + beq @check_input + + cmp #$05 + bne Slot0DirectionReturnEA43 + + +@check_input: + lda $14 + and #$0C + beq Slot0DirectionReturnEA43 + + and #$04 + bne @negative_direction + + lda #$01 + bne @store_direction + + +@negative_direction: + lda #$FF + + +@store_direction: + sta $DC + + lda #$04 + ora $FD + sta $FD + + +; $EA43 +Slot0DirectionReturnEA43: + rts + +; --------------------------------------------------------------------------- +; $EA44-$EA8E +; Build sprite/OAM entries for four runtime slots. +; +; X = slot index, 3..0 +; Y = slot's eight-byte OAM offset +; --------------------------------------------------------------------------- + +BuildFourSlotSpritesEA44: + ldx #$03 + + +; $EA46 +@slot_loop: + txa + asl a + asl a + asl a + tay + + lda $84,x + lsr a + bcc @alternate_state + + lda $80,x + cmp #$F4 + bcc @build_primary + + bcs @build_secondary + + +; $EA58 +@alternate_state: + lsr a + bcs @build_secondary + + lda $80,x + cmp #$F6 + bcc @build_secondary + + +; $EA61 +@build_primary: + lda $B0,x + beq @build_secondary + + lda #$B6 + jsr CalculateSlotRemainingFromADC9A + sta $0294,y + + lda $80,x + clc + adc #$0A + sta $0297,y + + sty $01 + + ldy $B4,x + lda $E69A,y + + ldy $01 + sta $0295,y + + lda #$00 + sta $0296,y + + ; Original EA86: F0 03 = BEQ $EA8B. + ; A was just loaded with zero, so this is always taken. + beq @next_slot + + +; $EA88 +@build_secondary: + jsr BuildSlotSprites + + +; $EA8B +@next_slot: + dex + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $EA8F-$EAC4 +; Slot-0/global-state helper. +; Copies seven display bytes into four-byte-spaced $02C0 entries, +; or initializes global state when $57 is zero. +; --------------------------------------------------------------------------- + +UpdateGlobalStateEA8F: + cpx #$00 + bne GlobalStateReturnEAC4 + + lda #$1D + sta $3A + + lda $57 + beq GlobalStateInitializeEAB5 + + lda $3C + bne GlobalStateClear3AEAC0 + + stx $0D + + ldx #$06 + ldy #$19 + + +; $EAA5 +GlobalStateCopyLoopEAA5: + lda $03D9,x + sta $02C0,y + + dey + dey + dey + dey + + dex + bpl GlobalStateCopyLoopEAA5 + + ldx $0D + rts + + +; $EAB5 +GlobalStateInitializeEAB5: + lda #$10 + sta $32 + + lda #$02 + sta $FD + + lsr a + sta $52 + + +; $EAC0 +GlobalStateClear3AEAC0: + lda #$00 + sta $3A + + +; $EAC4 +GlobalStateReturnEAC4: + rts + +; --------------------------------------------------------------------------- +; $EAC5-$EB16 +; Build/update display structures from global state and ROM tables. +; --------------------------------------------------------------------------- + +BuildGlobalDisplayEAC5: + lda $52 + ora $3C + + ; EAC9: D0 F5 = BNE $EAC0 + ; Reuses the preceding routine's clear-$3A path. + .byte $D0, $F5 + + lda $3A + + ; EACD: F0 F5 = BEQ $EAC4 + ; Reuses the preceding routine's RTS. + .byte $F0, $F5 + + ldx #$00 + ldy #$03 + + lda $3A + cmp #$0D + bcc @build_first + + lsr a + bcc @return + + lda #$40 + sta $FB + + +; $EAE0 +@build_first: + lda #$42 + + +; $EAE2 +@first_loop: + sta $02B4,x + inx + + lda $E540,y + sta $02B4,x + inx + + lda #$00 + sta $02B4,x + inx + + lda $E543,y + sta $02B4,x + inx + + dey + bne @build_first + + + ldx #$06 + + +; $EAFF +@second_loop: + lda #$4A + sta $02C0,y + + iny + iny + + lda #$00 + sta $02C0,y + + iny + + lda $E547,x + sta $02C0,y + + iny + + dex + bpl @second_loop + + +; $EB16 +@return: + rts + +; --------------------------------------------------------------------------- +; $EB17-$EC3A +; Global runtime/state-machine update. +; Contains several shared/secondary entries. +; --------------------------------------------------------------------------- + +UpdateGlobalRuntimeEB17: + lda $BE + bne GlobalRuntimeReturnEB7E + + lda $B4 + beq GlobalRuntimeCheckBDEB23 + + dec $B4 + bne GlobalRuntimeUpdateEB5E + + +; $EB23 +GlobalRuntimeCheckBDEB23: + lda $BD + ora $B9 + bne GlobalRuntimeUpdateEB5E + + sta $60 + sta $C0 + + lda $BF + beq GlobalRuntimeCheckC1EB35 + + cmp #$01 + bne GlobalRuntimeSecondaryEB9E + + +; $EB35 +GlobalRuntimeCheckC1EB35: + lda $C1 + cmp #$02 + beq GlobalRuntimeUpdateEB5E + + lda $C1 + beq GlobalRuntimeCheckBFEB49 + + lda #$09 + sta $B8 + + +; $EB43 +; Also targeted by the BEQ at $EB91. +GlobalRuntimeSetC1EB43: + lda #$02 + sta $C1 + + bne GlobalRuntimeSetFlagsEB93 + + +; $EB49 +GlobalRuntimeCheckBFEB49: + lda $BF + bne GlobalRuntimeUpdateEB5E + + lda $5C + asl a + bcc GlobalRuntimeShiftAgainEB7F + + lda #$00 + sta $49 + + lda $2B + bne GlobalRuntimeReturnEB9D + + lda #$05 + sta $2B + + +; $EB5E +GlobalRuntimeUpdateEB5E: + lda #$04 + sta $60 + + jsr $EC99 + + ldy $B3 + + lda #$00 + sta $BD + + jsr CheckRuntimeTableValuesECC9 + bne GlobalRuntimeIncrementBDEB7C + + iny + cpy #$40 + bne GlobalRuntimeCheckNextEB77 + + ldy #$00 + + +; $EB77 +GlobalRuntimeCheckNextEB77: + jsr CheckRuntimeTableValuesECC9 + beq GlobalRuntimeReturnEB9D + + +; $EB7C +GlobalRuntimeIncrementBDEB7C: + inc $BD + + +; $EB7E +GlobalRuntimeReturnEB7E: + rts + + +; --------------------------------------------------------------------------- +; $EB7F +; Secondary continuation from the $5C bit check above. +; --------------------------------------------------------------------------- + +GlobalRuntimeShiftAgainEB7F: + asl a + + ; EB80: 90 74 = BCC $EBF6 + .byte $90, $74 + + lda $49 + bne GlobalRuntimeReturnEB9D + + jsr InitializeRuntimeFieldsEC75 + + lda $B8 + cmp #$FF + beq GlobalRuntimeReturnEB9D + + cmp #$09 + beq GlobalRuntimeSetC1EB43 + + +; $EB93 +GlobalRuntimeSetFlagsEB93: + lda #$01 + sta $BE + sta $49 + + lda #$40 + sta $FB + + +; $EB9D +GlobalRuntimeReturnEB9D: + rts + + +; --------------------------------------------------------------------------- +; $EB9E +; Secondary entry used by the $BF state path. +; --------------------------------------------------------------------------- + +GlobalRuntimeSecondaryEB9E: + lda $49 + bne GlobalRuntimeCheck5CEBB6 + + lda $5C + asl a + asl a + bcc GlobalRuntimeUpdateTrackEBBD + + jsr EnterNmiSequenceMode + jsr CopyWorkingBufferToPersistentEC3B + + stx $47 + stx $42 + + lda #$08 + sta $41 + + +; $EBB6 +GlobalRuntimeCheck5CEBB6: + lda $5C + bne GlobalRuntimeReturnEBBC + + sta $49 + + +; $EBBC +GlobalRuntimeReturnEBBC: + rts + + +; --------------------------------------------------------------------------- +; $EBBD +; Secondary state/update entry. +; --------------------------------------------------------------------------- + +GlobalRuntimeUpdateTrackEBBD: + jsr UpdateOamEntriesEC5D + + lda $3A + + ; EBC2: D0 F8 = BNE $EBBC + .byte $D0, $F8 + + inc $3A + + lda $5C + cmp #$80 + beq GlobalRuntimeIncrement05E0EBCD + + cmp #$08 + bne GlobalRuntimeCheckMode4EBE9 + + +; $EBCD +GlobalRuntimeIncrement05E0EBCD: + inc $05E0 + + lda $05E0 + cmp #$0A + bmi GlobalRuntimeSetTriggerEBDC + + lda #$01 + sta $05E0 + + +; $EBDC +GlobalRuntimeSetTriggerEBDC: + lda #$01 + sta $FF + + lsr a + sta $49 + + jmp BuildOamEntryEC47 + + +; $EBE9 +GlobalRuntimeCheckMode4EBE9: + cmp #$04 + + ; EBEB: D0 C9 = BNE $EBB6 + .byte $D0, $C9 + + dec $05E0 + bne GlobalRuntimeSetTriggerEBDC + + lda #$09 + + ; EBF4: D0 E6 = BNE $EBDC + .byte $D0, $E6 + + +; --------------------------------------------------------------------------- +; $EBF6 +; Secondary entry reached by the branch at $EB80. +; --------------------------------------------------------------------------- + +GlobalRuntimeUpdateC2EBF6: + lda $2C + + ; EBF8: D0 40 = BNE $EC3A + .byte $D0, $40 + + lda #$08 + sta $2C + + lda $5C + lsr a + bcs GlobalRuntimeIncrementC2EC10 + + lsr a + + ; EC02: 90 34 = BCC $EC38 + .byte $90, $34 + + dec $C2 + + lda $C2 + bpl GlobalRuntimeSetFlagEC18 + + lda #$14 + bne GlobalRuntimeStoreC2EC16 + + +; $EC10 +GlobalRuntimeIncrementC2EC10: + inc $C2 + + lda $C2 + cmp #$15 + bcc GlobalRuntimeSetFlagEC18 + + lda #$00 + + +; $EC16 +GlobalRuntimeStoreC2EC16: + sta $C2 + + +; $EC18 +GlobalRuntimeSetFlagEC18: + lda #$01 + sta $FF + + lsr a + sta $49 + + lda #$D0 + sta $0230 + + lda #$CA + sta $0231 + + lda #$00 + sta $0232 + + ldx $C2 + lda $E6F6,x + sta $0233 + + +; $EC3A +GlobalRuntimeReturnEC3A: + rts + + +; --------------------------------------------------------------------------- +; $EC3B-$EC46 +; Copy the complete 256-byte working buffer from $05E0 to $06E0. +; X starts at zero, wraps through $FF, and returns to zero. +; --------------------------------------------------------------------------- + +CopyWorkingBufferToPersistentEC3B: + ldx #$00 + +@copy: + lda $05E0,x + sta $06E0,x + + dex + bne @copy + + rts + +; --------------------------------------------------------------------------- +; $EC47-$EC5C +; Build the OAM entry at $0234-$0237. +; --------------------------------------------------------------------------- + +BuildOamEntryEC47: + lda #$CF + sta $0234 + + lda $05E0 + sta $0235 + + lda #$00 + sta $0236 + + lda #$E4 + sta $0237 + + rts + +; --------------------------------------------------------------------------- +; $EC5D-$EC74 +; Update the $0228/$022C display entries according to frame/state flags. +; --------------------------------------------------------------------------- + +UpdateOamEntriesEC5D: + lda #$F8 + jsr $EC25 + + lda $3F + and #$10 + beq @use_c7 + + lda #$F8 + bne @store + + +@use_c7: + lda #$C7 + + +@store: + sta $0228 + sta $022C + + rts + +; --------------------------------------------------------------------------- +; $EC75-$EC98 +; Initialize working runtime fields from the current B0-B2 state and +; select a value from the $E6E1 table using $C2. +; --------------------------------------------------------------------------- + +InitializeRuntimeFieldsEC75: + lda #$00 + sta $CA + sta $C8 + sta $C9 + + lda $B2 + sta $BA + + lda $B0 + sta $BB + + lda $B1 + sta $BC + + ldy $C2 + lda DataTableE6E1,y + sta $B8 + + cmp #$11 + bcc @return + + and #$0F + sta $C8 + +@return: + rts + +; --------------------------------------------------------------------------- +; $EC99-$ECBE +; Accumulate $60 into $B9. Every time the accumulator reaches $08, +; reset it, set $C0, perform the $F6FB update, and advance $B2/$B3. +; --------------------------------------------------------------------------- + +UpdateRuntimeAccumulatorEC99: + lda $60 + + ; EC9B: F0 FB = BEQ $EC98 + ; Reuses the RTS from InitializeRuntimeFieldsEC75. + .byte $F0, $FB + + clc + adc $B9 + sta $B9 + + cmp #$08 + + ; ECA4: D0 F2 = BNE $EC98 + ; Reuses the same preceding RTS. + .byte $D0, $F2 + + lda #$01 + sta $C0 + + lsr a + sta $B9 + + jsr $F6FB + + lda $B2 + jsr IncrementWrap40ECBF + sta $B2 + + lda $B3 + jsr IncrementWrap40ECBF + sta $B3 + + rts + +; --------------------------------------------------------------------------- +; $ECBF-$ECC8 +; Increment A and wrap $40 back to $00. +; +; Entry: +; A = value +; +; Return: +; A = (A + 1) modulo $40 +; --------------------------------------------------------------------------- + +IncrementWrap40ECBF: + clc + adc #$01 + + cmp #$40 + bne @return + + lda #$00 + +@return: + rts + +; --------------------------------------------------------------------------- +; $ECC9-$ECE3 +; Check four indexed runtime-table values. +; +; Entry: +; Y = table index +; +; Return: +; A = $00 if all four expected values match +; A = $01 otherwise +; +; The success path reuses LDA #$00 / RTS at $ECC6-$ECC8. +; --------------------------------------------------------------------------- + +CheckRuntimeTableValuesECC9: + lda #$3D + + cmp $0540,y + bne @no_match + + cmp $0480,y + bne @no_match + + lda #$3B + + cmp $0400,y + bne @no_match + + cmp $04C0,y + + ; ECDF: F0 E5 = BEQ $ECC6 + ; Reuses LDA #$00 / RTS from IncrementWrap40ECBF. + .byte $F0, $E5 + + +@no_match: + lda #$01 + rts + +; --------------------------------------------------------------------------- +; $ECE4-$ED39 +; Build two related structures in $0400/$0423 from a four-byte ROM entry, +; then tail-call the existing $C289 handler. +; +; Entry: +; X = index into table at $E6A1 +; --------------------------------------------------------------------------- + +BuildPairedPpuRows: + ldy #$03 + + +; $ECE6 +@load_parameters: + lda PpuRowDescriptorsE6A1,x + inx + sta $0002,y + + dey + bpl @load_parameters + + + lda $05 + sta $0400 + + lda $02 + sta $0401 + + ldx #$20 + stx $0402 + + +; $ECFF +@fill_first: + lda $03 + sta $0403,x + dex + + lda $04 + sta $0403,x + dex + + bpl @fill_first + + + lda $05 + eor #$04 + sta $0423 + + lda $02 + sta $0424 + + ldx #$20 + stx $0425 + + +; $ED1E +@fill_second: + lda $03 + sta $0426,x + dex + + lda $04 + sta $0426,x + dex + + bpl @fill_second + + + lda #$00 + sta $0446 + + lda #$00 + sta $00 + + lda #$04 + jmp $C289 + +; --------------------------------------------------------------------------- +; $ED3A-$ED49 +; Runtime/course data preceding the $40-delimited record stream. +; --------------------------------------------------------------------------- + +RuntimeDataED3A: + .byte $46, $59, $C8, $D2 + .byte $A7, $E0, $ED, $EE + .byte $ED, $EE, $EF, $06 + .byte $02, $40, $10, $30 + + +; --------------------------------------------------------------------------- +; $ED4A-$EE09 +; Structured runtime/course command stream. +; --------------------------------------------------------------------------- + +RuntimeCommandDataED4A: +; $ED4A + .byte $40, $36, $0D, $31, $40, $07, $05, $40 + .byte $02, $88, $40, $02, $88, $40, $1C, $07 + +; $ED5A + .byte $40, $25, $10, $40, $0A, $07, $40, $02 + .byte $07, $40, $02, $8D, $40, $39, $07, $40 + +; $ED6A + .byte $02, $07, $40, $02, $07, $40, $0E, $0F + .byte $40, $10, $0C, $40, $10, $06, $40, $05 + +; $ED7A + .byte $0A, $40, $2B, $0E, $40, $1E, $0F, $40 + .byte $02, $0E, $40, $26, $0E, $40, $02, $08 + +; $ED8A + .byte $40, $02, $08, $40, $02, $08, $40, $02 + .byte $08, $40, $02, $88, $40, $12, $06, $40 + +; $ED9A + .byte $02, $11, $46, $0A, $17, $40, $14, $01 + .byte $40, $02, $01, $40, $02, $81, $40, $02 + +; $EDAA + .byte $81, $40, $02, $01, $40, $02, $01, $40 + .byte $11, $30, $8D, $40, $0A, $0C, $40, $0B + +; $EDBA + .byte $0D, $40, $03, $8D, $40, $09, $8C, $40 + .byte $02, $0C, $40, $0E, $31, $09, $02, $40 + +; $EDCA + .byte $0F, $30, $40, $34, $03, $40, $07, $04 + .byte $40, $0C, $03, $40, $0A, $04, $40, $0E + +; $EDDA + .byte $03, $40, $06, $83, $40, $09, $04, $40 + .byte $06, $84, $40, $0A, $31, $07, $40, $04 + +; $EDEA + .byte $07, $40, $05, $8B, $40, $0E, $06, $40 + .byte $09, $0A, $40, $07, $0F, $40, $02, $10 + +; $EDFA + .byte $40, $07, $06, $40, $02, $0B, $40, $02 + .byte $0A, $40, $0B, $84, $40, $0B, $13, $43 + + +; --------------------------------------------------------------------------- +; $EE0A-$EFFF +; Structured course-definition / object-command stream. +; --------------------------------------------------------------------------- + +CourseDefinitionDataEE0A: + +; $EE0A + .byte $37, $1B, $40, $18, $83, $40, $0A, $0E + .byte $40, $09, $07, $40, $02, $11, $46, $16 + +; $EE1A + .byte $17, $40, $0F, $0E, $40, $0B, $05, $40 + .byte $05, $87, $40, $09, $03, $40, $0E, $05 + +; $EE2A + .byte $40, $06, $0A, $40, $06, $06, $40, $05 + .byte $05, $40, $0F, $0F, $40, $0B, $30, $13 + +; $EE3A + .byte $43, $37, $1B, $40, $0D, $12, $42, $2A + .byte $19, $40, $11, $04, $40, $02, $8C, $40 + +; $EE4A + .byte $06, $0D, $40, $05, $0C, $40, $05, $03 + .byte $40, $02, $8D, $40, $15, $31, $09, $02 + +; $EE5A + .byte $40, $0F, $30, $40, $33, $31, $15, $41 + .byte $06, $21, $45, $06, $23, $40, $0A, $07 + +; $EE6A + .byte $40, $06, $87, $40, $08, $0C, $40, $0B + .byte $15, $41, $06, $21, $45, $06, $23, $40 + +; $EE7A + .byte $03, $87, $40, $0C, $0F, $40, $02, $10 + .byte $40, $0F, $07, $40, $0B, $07, $40, $26 + +; $EE8A + .byte $08, $40, $02, $0B, $40, $02, $88, $40 + .byte $24, $0E, $40, $02, $08, $40, $02, $11 + +; $EE9A + .byte $46, $0A, $17, $40, $02, $0E, $40, $02 + .byte $11, $46, $0E, $17, $40, $15, $10, $40 + +; $EEAA + .byte $06, $81, $40, $02, $81, $40, $02, $01 + .byte $40, $02, $01, $40, $02, $01, $40, $02 + +; $EEBA + .byte $01, $40, $17, $30, $13, $43, $37, $1B + .byte $40, $1F, $0C, $40, $0E, $0D, $40, $04 + +; $EECA + .byte $8D, $40, $05, $0C, $40, $1B, $31, $09 + .byte $02, $40, $10, $30, $40, $50, $31, $14 + +; $EEDA + .byte $44, $03, $1E, $44, $04, $1E, $44, $04 + .byte $1F, $1B, $40, $0F, $0D, $40, $05, $83 + +; $EEEA + .byte $40, $03, $0C, $40, $09, $14, $44, $03 + .byte $1E, $44, $04, $1E, $44, $04, $1F, $1B + +; $EEFA + .byte $40, $04, $07, $40, $02, $87, $40, $02 + .byte $87, $40, $09, $03, $40, $04, $84, $40 + +; $EF0A + .byte $05, $04, $40, $07, $83, $40, $05, $10 + .byte $40, $0C, $8A, $40, $0C, $07, $40, $02 + +; $EF1A + .byte $11, $46, $0A, $17, $40, $19, $0E, $40 + .byte $0D, $0E, $40, $04, $0B, $40, $04, $08 + +; $EF2A + .byte $40, $02, $08, $40, $02, $88, $40, $1A + .byte $03, $40, $06, $83, $40, $03, $04, $40 + +; $EF3A + .byte $08, $0E, $40, $04, $0B, $40, $02, $0B + .byte $40, $02, $0B, $40, $02, $8B, $40, $02 + +; $EF4A + .byte $8B, $40, $21, $0F, $40, $0C, $0D, $40 + .byte $02, $88, $40, $02, $13, $43, $17, $1B + +; $EF5A + .byte $40, $06, $12, $42, $17, $19, $40, $03 + .byte $03, $40, $08, $05, $40, $02, $05, $40 + +; $EF6A + .byte $02, $05, $40, $02, $05, $40, $02, $05 + .byte $40, $02, $11, $46, $0A, $30, $17, $40 + +; $EF7A + .byte $11, $10, $40, $0E, $0C, $40, $06, $8C + .byte $40, $09, $0D, $40, $02, $8D, $40, $0C + +; $EF8A + .byte $0C, $40, $05, $0D, $40, $06, $8C, $40 + .byte $02, $0C, $40, $07, $8D, $40, $05, $0C + +; $EF9A + .byte $40, $04, $0D, $40, $07, $8C, $40, $02 + .byte $31, $0E, $40, $05, $09, $02, $40, $10 + +; $EFAA + .byte $30, $40, $32, $0F, $40, $0F, $0C, $40 + .byte $08, $31, $0E, $40, $02, $11, $46, $0E + +; $EFBA + .byte $17, $40, $02, $0A, $40, $06, $15, $41 + .byte $06, $21, $45, $06, $23, $40, $02, $08 + +; $EFCA + .byte $40, $03, $08, $40, $03, $08, $40, $03 + .byte $88, $40, $03, $88, $40, $09, $83, $40 + +; $EFDA + .byte $05, $04, $40, $08, $10, $40, $11, $0D + .byte $40, $13, $0C, $40, $14, $0E, $40, $13 + +; $EFEA + .byte $05, $40, $04, $05, $40, $05, $05, $40 + .byte $04, $06, $40, $02, $11, $46, $0A, $17 + +; $EFFA-$EFFF + .byte $40, $05, $0C, $40, $03, $14 + + + +; --------------------------------------------------------------------------- +; $F000-$F062 +; Continuation of structured course-definition command data. +; --------------------------------------------------------------------------- + +CourseDefinitionDataF000: + +; $F000 + .byte $44, $03, $1E, $44, $04, $1E, $44, $04 + .byte $1F, $1B, $40, $06, $07, $40, $03, $83 + +; $F010 + .byte $40, $04, $0E, $40, $17, $8A, $40, $03 + .byte $0E, $40, $02, $11, $46, $05, $17, $40 + +; $F020 + .byte $02, $0B, $40, $06, $01, $40, $02, $01 + .byte $40, $02, $08, $40, $02, $01, $40, $02 + +; $F030 + .byte $01, $40, $02, $01, $40, $02, $01, $40 + .byte $02, $05, $40, $21, $30, $0C, $40, $04 + +; $F040 + .byte $83, $40, $04, $0D, $40, $04, $04, $40 + .byte $08, $8C, $40, $02, $03, $40, $05, $8D + +; $F050 + .byte $40, $02, $0D, $40, $06, $0C, $40, $03 + .byte $8C, $40, $05, $0D, $40, $06, $04, $40 + +; $F060-$F062 + .byte $05, $31, $09 + + +; --------------------------------------------------------------------------- +; $F063-$F086 +; Low-byte table for course-pattern pointers. +; +; Consumed by LoadCourseStructurePointer: +; +; lda CourseStructurePointerLowF063,x +; sta $05 +; lda CourseStructurePointerHighF087,x +; sta $06 +; --------------------------------------------------------------------------- + +CourseStructurePointerLowF063: + .byte $AB, $B3, $01, $01, $0A, $12 + .byte $6B, $A4, $D1, $EB, $5E, $97 + .byte $E2, $CC, $B4, $96, $A5, $56 + .byte $2D, $35, $5D, $F8, $5E, $66 + .byte $4D, $3D, $55, $45, $C0, $6E + .byte $6E, $CE, $0A, $14, $DC, $EA + + +; --------------------------------------------------------------------------- +; $F087-$F0AA +; High-byte table for course-pattern pointers. +; --------------------------------------------------------------------------- + +CourseStructurePointerHighF087: + .byte $F0, $F0, $F1, $F1, $F1, $F1 + .byte $F1, $F1, $F1, $F1, $F2, $F2 + .byte $F2, $F2, $F4, $F4, $F4, $F4 + .byte $F3, $F3, $F3, $F2, $F4, $F4 + .byte $F3, $F3, $F3, $F3, $F3, $F4 + .byte $F4, $F3, $F3, $F3, $F3, $F3 + +CoursePatternF0AB: + .byte $09, $3B, $3C, $3D + .byte $3B, $3C, $3D, $00 + +CourseStructureTemplateF0B3: + + ; Variant 0 + .byte $08 + .byte $D7, $40, $40, $40, $40, $40, $41 + + ; Variant 1 + .byte $08 + .byte $D6, $FE, $FE, $FE, $FE, $FE, $D8 + + ; Variant 2 + .byte $07 + .byte $D7, $FE, $FE, $FE, $FE, $FE, $D9, $FE + + ; Variant 3 + .byte $07 + .byte $D6, $FE, $FE, $FE, $FE, $FE, $D8, $FE + + ; Variant 4 + .byte $07 + .byte $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE + + ; Variant 5 + .byte $07 + .byte $DA, $FF, $FF, $FF, $FF, $FF, $DC, $FE + + ; Variant 6 + .byte $07 + .byte $DB, $FF, $FF, $FF, $FF, $FF, $DD, $FE + + ; Variant 7 + .byte $08 + .byte $DA, $FF, $FF, $FF, $FF, $FF, $DC + + ; Variant 8 + .byte $08 + .byte $DB, $FF, $FF, $FF, $FF, $FF, $DD + + .byte $00 + +; --------------------------------------------------------------------------- +; $F101-$F109 (09 bytes) +; --------------------------------------------------------------------------- +CourseStructureTemplateF101: + .byte $08, $E0, $48, $49, $4A, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F10A-$F111 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF10A: + .byte $09, $3B, $3C, $E1, $48, $49, $4A, $00 + +; --------------------------------------------------------------------------- +; $F112-$F16A (59 bytes) +; --------------------------------------------------------------------------- +CoursePatternF112: + .byte $08, $C5, $50, $50, $50, $50, $50, $51, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $C5, $FE, $FE, $FE, $FE, $FE, $C6, $FE, $FE, $05, $C5, $FE, $FE, $FE + .byte $FE, $FE, $C6, $FE, $FE, $FE, $04, $2F, $2E, $2B, $2D, $2C, $2B, $2A, $FE, $FE + .byte $FE, $FE, $05, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE, $FE, $FE, $06, $C7, $C8 + .byte $C9, $CA, $C8, $C9, $CB, $FE, $FE, $07, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F16B-$F1A3 (39 bytes) +; --------------------------------------------------------------------------- +CoursePatternF16B: + .byte $08, $C5, $5E, $5E, $5E, $5E, $5E, $5F, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $C5, $FE, $FE, $FE, $FE, $FE, $C6, $FE, $FE, $05, $2F, $2E, $2B, $2D + .byte $2C, $2B, $2A, $FE, $FE, $FE, $06, $D1, $D2, $FF, $FF, $FF, $FF, $D3, $D4, $FE + .byte $08, $D1, $D0, $D0, $D0, $D0, $D0, $D5, $00 + +; --------------------------------------------------------------------------- +; $F1A4-$F1D0 (2D bytes) +; --------------------------------------------------------------------------- +CoursePatternF1A4: + .byte $08, $C5, $56, $56, $56, $56, $56, $57, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $2F, $2E, $2B, $2D, $2E, $2B, $2A, $FE, $FE, $07, $C7, $C8, $C9, $CA + .byte $C8, $C9, $CB, $FE, $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F1D1-$F1EA (1A bytes) +; --------------------------------------------------------------------------- +CoursePatternF1D1: + .byte $08, $C5, $58, $58, $58, $58, $58, $59, $07, $2F, $2E, $2B, $2D, $2C, $2B, $2A + .byte $FE, $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F1EB-$F25D (73 bytes) +; --------------------------------------------------------------------------- +CoursePatternF1EB: + .byte $08, $C5, $4E, $4E, $4E, $4E, $4E, $4F, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $C5, $FE, $FE, $FE, $FE, $FE, $C6, $FE, $FE, $06, $3B, $3C, $3D, $3B + .byte $3C, $3D, $FE, $0F, $FE, $06, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $12, $FE, $06 + .byte $3B, $3C, $3D, $3B, $3C, $3D, $FE, $17, $FE, $06, $3B, $3C, $3D, $3B, $3C, $3D + .byte $FE, $12, $FE, $06, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $1C, $FE, $06, $3B, $3C + .byte $3D, $3B, $3C, $3D, $FE, $11, $FE, $06, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $FE, $07, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE, $08, $C7, $C8, $C9, $CA, $C8 + .byte $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F25E-$F296 (39 bytes) +; --------------------------------------------------------------------------- +CoursePatternF25E: + .byte $08, $CC, $60, $60, $60, $60, $60, $61, $06, $CC, $CD, $FE, $FE, $FE, $FE, $CE + .byte $CF, $FE, $05, $2F, $2E, $2B, $2D, $2C, $2B, $2A, $FE, $FE, $FE, $06, $C7, $C8 + .byte $C9, $CA, $C8, $C9, $CB, $FE, $FE, $07, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F297-$F2CB (35 bytes) +; --------------------------------------------------------------------------- +CoursePatternF297: + .byte $07, $CC, $CD, $65, $65, $65, $65, $66, $67, $05, $CC, $CD, $FE, $FE, $FE, $FE + .byte $CE, $CF, $FE, $FE, $04, $2F, $2E, $2B, $2D, $2C, $2B, $2A, $FE, $FE, $FE, $FE + .byte $05, $D1, $D2, $FF, $FF, $FF, $FF, $D3, $D4, $FE, $FE, $07, $D1, $D2, $FF, $FF + .byte $FF, $FF, $D3, $D4, $00 + +; --------------------------------------------------------------------------- +; $F2CC-$F2E1 (16 bytes) +; --------------------------------------------------------------------------- +CoursePatternF2CC: + .byte $09, $3B, $E7, $72, $3B, $E7, $72, $09, $3B, $E8, $BB, $3B, $E8, $BB, $09, $3B + .byte $E9, $BE, $3B, $E9, $BB, $00 + +; --------------------------------------------------------------------------- +; $F2E2-$F2F7 (16 bytes) +; --------------------------------------------------------------------------- +CoursePatternF2E2: + .byte $09, $70, $EA, $3D, $70, $EA, $3D, $09, $B9, $EB, $3D, $B9, $EB, $3D, $09, $BC + .byte $EC, $3D, $BC, $EC, $3D, $00 + +; --------------------------------------------------------------------------- +; $F2F8-$F309 (12 bytes) +; --------------------------------------------------------------------------- +CoursePatternF2F8: + .byte $08, $C5, $68, $68, $68, $68, $68, $69, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $00 + +; --------------------------------------------------------------------------- +; $F30A-$F313 (0A bytes) +; --------------------------------------------------------------------------- +CoursePatternF30A: + .byte $07, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $00 + +; --------------------------------------------------------------------------- +; $F314-$F32C (19 bytes) +; --------------------------------------------------------------------------- +CoursePatternF314: + .byte $05, $CC, $CD, $FE, $FE, $6D, $6D, $6E, $6F, $6D, $6D, $03, $CC, $CD, $FE, $FE + .byte $FE, $FE, $CE, $CF, $FE, $FE, $FE, $FE, $00 + +; --------------------------------------------------------------------------- +; $F32D-$F334 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF32D: + .byte $09, $3B, $3C, $3D, $C0, $C0, $C0, $00 + +; --------------------------------------------------------------------------- +; $F335-$F33C (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF335: + .byte $09, $C0, $C0, $C0, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F33D-$F344 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF33D: + .byte $09, $3B, $3C, $3D, $C3, $C3, $C3, $00 + +; --------------------------------------------------------------------------- +; $F345-$F34C (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF345: + .byte $09, $C3, $C3, $C3, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F34D-$F354 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF34D: + .byte $09, $3B, $3C, $3D, $E4, $E4, $E4, $00 + +; --------------------------------------------------------------------------- +; $F355-$F35C (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF355: + .byte $09, $E4, $E4, $E4, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F35D-$F3BF (63 bytes) +; --------------------------------------------------------------------------- +CoursePatternF35D: + .byte $07, $CC, $CD, $75, $75, $75, $75, $76, $77, $05, $CC, $CD, $FE, $FE, $FE, $FE + .byte $CE, $CF, $FE, $FE, $03, $CC, $CD, $FE, $FE, $FE, $FE, $CE, $CF, $FE, $FE, $FE + .byte $FE, $03, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $03, $3B + .byte $3C, $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $03, $3B, $3C, $3D, $E5 + .byte $D2, $FF, $D3, $D4, $FE, $FE, $FE, $FE, $03, $3B, $3C, $3D, $E6, $FC, $D1, $D2 + .byte $FF, $D3, $D4, $FE, $FE, $03, $3B, $3C, $3D, $E6, $FC, $FC, $FC, $D1, $D2, $FF + .byte $D3, $D4, $00 + +; --------------------------------------------------------------------------- +; $F3C0-$F3CD (0E bytes) +; --------------------------------------------------------------------------- +CoursePatternF3C0: + .byte $03, $3B, $3C, $3D, $E6, $FC, $FC, $FC, $FC, $FC, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F3CE-$F3DB (0E bytes) +; --------------------------------------------------------------------------- +CoursePatternF3CE: + .byte $03, $BF, $7D, $7E, $7F, $FC, $FC, $FC, $FC, $FC, $80, $81, $82, $00 + +; --------------------------------------------------------------------------- +; $F3DC-$F3E9 (0E bytes) +; --------------------------------------------------------------------------- +CoursePatternF3DC: + .byte $03, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $00 + +; --------------------------------------------------------------------------- +; $F3EA-$F455 (6C bytes) +; --------------------------------------------------------------------------- +CoursePatternF3EA: + .byte $03, $3B, $3C, $3D, $3B, $3C, $3D, $78, $78, $78, $78, $78, $78, $03, $3B, $3C + .byte $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $03, $C7, $C8, $C9, $CA, $C8 + .byte $C9, $CB, $FE, $FE, $FE, $FE, $FE, $04, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $FE, $FE, $FE, $05, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE, $FE, $FE, $06, $C7 + .byte $C8, $C9, $CA, $C8, $C9, $CB, $FE, $FE, $07, $3B, $3C, $3D, $3B, $3C, $3D, $FE + .byte $FE, $07, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $07, $C7, $C8, $C9, $CA, $C8 + .byte $C9, $CB, $FE, $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F456-$F45D (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF456: + .byte $09, $C0, $C0, $C0, $C0, $C0, $C0, $00 + +; --------------------------------------------------------------------------- +; $F45E-$F465 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF45E: + .byte $09, $E4, $E4, $E4, $E4, $E4, $E4, $00 + +; --------------------------------------------------------------------------- +; $F466-$F46D (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF466: + .byte $09, $C3, $C3, $C3, $C3, $C3, $C3, $00 + +; --------------------------------------------------------------------------- +; $F46E-$F495 (28 bytes) +; --------------------------------------------------------------------------- +CoursePatternF46E: + .byte $03, $3B, $3C, $3D, $E6, $28, $29, $73, $73, $73, $70, $71, $72, $03, $3B, $3C + .byte $3D, $E6, $FC, $FC, $FC, $FC, $FC, $B9, $BA, $BB, $03, $3B, $3C, $3D, $E6, $FC + .byte $FC, $FC, $FC, $FC, $BC, $BD, $BE, $00 + +; --------------------------------------------------------------------------- +; $F496-$F4A4 (0F bytes) +; --------------------------------------------------------------------------- +CoursePatternF496: + .byte $09, $88, $3C, $3D, $3B, $3C, $3D, $09, $89, $3C, $3D, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F4A5-$F4B3 (0F bytes) +; --------------------------------------------------------------------------- +CoursePatternF4A5: + .byte $09, $3B, $3C, $3D, $3B, $3C, $88, $09, $3B, $3C, $3D, $3B, $3C, $89, $00 + +; --------------------------------------------------------------------------- +; $F4B4-$F4FE (4B bytes) +; --------------------------------------------------------------------------- +CoursePatternF4B4: + .byte $08, $E2, $8D, $8E, $8F, $90, $91, $92, $07, $E2, $DE, $DF, $E3, $FE, $3B, $3C + .byte $3D, $00, $00, $20, $18, $1A, $1C, $22, $16, $15, $0A, $19, $FE, $FE, $12, $42 + .byte $17, $19, $00, $13, $43, $17, $1B, $00, $11, $46, $0A, $17, $00, $14, $44, $03 + .byte $1E, $44, $04, $1E, $44, $04, $1F, $1B, $00, $15, $41, $06, $21, $45, $06, $23 + .byte $00, $DC, $D2, $D7, $E1, $ED, $F4, $F4, $F4, $F4, $F4 + +; --------------------------------------------------------------------------- +; $F4FF-$F62C +; Process the current course/object definition stream. +; --------------------------------------------------------------------------- + +ProcessCourseDefinition: + ldx $43 + + lda RuntimeDataED3A,x + sta $07 + + lda $ED40,x + sta $08 + + lda $EB + cmp $E0 + beq @position_matches + + rts + + +@position_matches: + lda $EC + + ; F514: D0 7B + .byte $D0, $7B + + lda $EF + + ; F518: D0 7D + .byte $D0, $7D + + ldy $ED + bne @advance_stream + + lda ($07),y + sta $57 + + inc $03A4 + + +@advance_stream: + lda $41 + cmp #$09 + bne @increment_stream + + lda $03BC + beq @increment_stream + + lda #$00 + sta $EE + sta $ED + + lda #$7F + + ; F537: D0 53 + .byte $D0, $53 + + +@increment_stream: + inc $ED + + ldy $ED + lda ($07),y + + ; F53F: F0 CF = BEQ $F510 + .byte $F0, $CF + + bit $F3D8 + + ; F544: F0 04 = BEQ $F54A + .byte $F0, $04 + + lda $46 + + ; F548: F0 CF = BEQ $F519 + .byte $F0, $CF + + +; $F54A +ProcessCourseDefinitionCommandF54A: + lda ($07),y + bit $F0B5 + + ; F54F: F0 24 = BEQ $F575 + .byte $F0, $24 + + and #$0F + tax + + lda $F4C6,x + sta $EE + + inc $ED + iny + + lda $41 + cmp #$09 + + ; F55E: D0 0F + .byte $D0, $0F + + cpy #$02 + + ; F562: D0 0B + .byte $D0, $0B + + lda #$01 + sta $57 + + lda ($07),y + sec + sbc #$3D + + ; F56F: D0 1B = BNE $F58C + .byte $D0, $1B + + +; $F571 + lda ($07),y + + ; F573: D0 17 = BNE $F58C + .byte $D0, $17 + + +; $F575 + ldx #$01 + + and #$3F + sta $EE + + cmp #$30 + + ; F57D: 90 17 = BCC $F596 + .byte $90, $17 + + ; F57F: D0 05 = BNE $F586 + .byte $D0, $05 + + lda $03BC + + ; F584: F0 01 = BEQ $F587 + .byte $F0, $01 + + dex + + +; $F587 + stx $03A8 + + ; F58A: 10 8D = BPL $F519 + .byte $10, $8D + + +; $F58C + and #$7F + sta $EC + + lda #$00 + sta $EF + + dec $EC + + ldx $EE + jsr LoadCourseStructurePointer + + ldy $EF + lda ($05),y + + ; F5A0: D0 03 = BNE $F5A5 + .byte $D0, $03 + + jmp ContinueCourseDefinition + + +; $F5A5 + jsr InitializeCourseStructure + + lda ($05),y + tax + + jsr CopyCourseStructure + + lda $EE + + ; F5B0: F0 08 = BEQ $F5BA + .byte $F0, $08 + + cmp #$18 + + ; F5B4: F0 04 = BEQ $F5BA + .byte $F0, $04 + + cmp #$1A + + ; F5B8: D0 0F = BNE $F5C9 + .byte $D0, $0F + + +; $F5BA +ProcessCourseToggleF5BA: + lda $03A2 + eor #$01 + sta $03A2 + + ; F5C2: D0 05 = BNE $F5C9 + .byte $D0, $05 + + lda #$83 + sta $030A + + +; $F5C9 +ProcessCourseAfterToggleF5C9: + iny + sty $EF + + lda $EE + cmp #$09 + bne @build_structure + + lda $57 + cmp #$01 + beq @build_structure + + cpy #$25 + bcc @build_structure + + cpy #$58 + bcs @build_structure + + ldx $03A5 + + lda $F4CD,x + sta $0310 + + inc $03A5 + + cpx #$05 + bne @build_structure + + lda $03A4 + sta $0310 + + +@build_structure: + lda #$8E + sta $0303 + + lda $EA + sta $0301 + + lda $E9 + sta $0302 + + lda $E8 + sta $0F + + jsr BuildCourseStructure + + lda $E8 + jsr IncrementWrap40ECBF + sta $E8 + + inc $E9 + + lda $E9 + cmp #$20 + bne @advance_eb + + lda #$00 + sta $E9 + + lda $EA + eor #$04 + sta $EA + + +@advance_eb: + lda $E0 + jsr IncrementWrap40ECBF + sta $EB + + rts + + +; --------------------------------------------------------------------------- +; $F62D-$F64D +; Continue processing the current course-definition event. +; Secondary entry from ProcessCourseDefinition. +; --------------------------------------------------------------------------- + +ContinueCourseDefinition: + sta $EF ; F62D: 85 EF + + lda $EE ; F62F: A5 EE + cmp #$09 ; F631: C9 09 + beq @special_type ; F633: F0 03 + + jmp $F51A ; F635: 4C 1A F5 + + +@special_type: + inc $03A4 ; F638: EE A4 03 + + dec $57 ; F63B: C6 57 + bne @reset_stream ; F63D: D0 05 + + lda #$01 ; F63F: A9 01 + sta $03BC ; F641: 8D BC 03 + + +@reset_stream: + lda #$00 ; F644: A9 00 + sta $ED ; F646: 85 ED + sta $03A5 ; F648: 8D A5 03 + + jmp $F525 ; F64B: 4C 25 F5 + +; --------------------------------------------------------------------------- +; $F64E-$F675 +; Build/copy the current $0300 structure. +; --------------------------------------------------------------------------- + +BuildCourseStructure: + ldx #$05 ; F64E: A2 05 + + lda #$0D ; F650: A9 0D + sta $0C ; F652: 85 0C + + +@next_entry: + txa ; F654: 8A + tay ; F655: A8 + + jsr LoadTablePointerE80D ; F656: 20 0D E8 + + stx $0A ; F659: 86 0A + + +@find_slot: + ldx $0C ; F65B: A6 0C + + lda $0304,x ; F65D: BD 04 03 + cmp #$FC ; F660: C9 FC + bne @store_value ; F662: D0 06 + + dec $0C ; F664: C6 0C + + lda $0C ; F666: A5 0C + bpl @find_slot ; F668: 10 F1 + + +@store_value: + ldy $0F ; F66A: A4 0F + sta ($03),y ; F66C: 91 03 + + dec $0C ; F66E: C6 0C + + ldx $0A ; F670: A6 0A + dex ; F672: CA + bpl @next_entry ; F673: 10 DF + + rts ; F675: 60 + + +; --------------------------------------------------------------------------- +; $F676-$F680 +; Fill $0303-$030D with $FC. +; --------------------------------------------------------------------------- + +InitializeCourseStructure: + ldx #$0A ; F676: A2 0A + lda #$FC ; F678: A9 FC + +@fill: + sta $0303,x ; F67A: 9D 03 03 + dex ; F67D: CA + bne @fill ; F67E: D0 FA + + rts ; F680: 60 + +; --------------------------------------------------------------------------- +; $F681-$F68C +; Copy bytes from ($05),Y into $0303+X until X reaches $0F. +; --------------------------------------------------------------------------- + +CopyCourseStructure: +@copy: + iny ; F681: C8 + lda ($05),y ; F682: B1 05 + sta $0303,x ; F684: 9D 03 03 + + inx ; F687: E8 + cpx #$0F ; F688: E0 0F + bne @copy ; F68A: D0 F5 + + rts ; F68C: 60 + +; --------------------------------------------------------------------------- +; $F68D-$F6FA +; Update runtime/course-object state. +; +; Falls through into AdvanceRuntimePosition. +; --------------------------------------------------------------------------- + +UpdateCourseObjectRuntime: + lda $BE ; F68D: A5 BE + + ; F68F: F0 FB = BEQ $F68C + ; Reuses the RTS at the end of CopyCourseStructure. + .byte $F0, $FB + + lda $CA ; F691: A5 CA + + ; F693: D0 32 = BNE $F6C7 + .byte $D0, $32 + + lda $C3 ; F695: A5 C3 + + ; F697: D0 2E = BNE $F6C7 + .byte $D0, $2E + + ldx $C8 ; F699: A6 C8 + txa ; F69B: 8A + + ; F69C: F0 29 = BEQ $F6C7 + .byte $F0, $29 + + dex ; F69E: CA + jsr LoadCourseListPointer + + ldy $C9 + lda ($07),y + + ; F6A6: D0 03 = BNE $F6AB + .byte $D0, $03 + + jmp $F71E + +; $F6AB + bit $F0B5 + + ; F6AE: F0 12 = BEQ $F6C2 + .byte $F0, $12 + + and #$0F + tax + + lda $F4C6,x + sta $B8 + + iny + lda ($07),y + sta $CA + + iny + sty $C9 + + ; F6C0: D0 05 = BNE $F6C7 + .byte $D0, $05 + +; $F6C2 + sta $B8 + + iny + sty $C9 + +; $F6C7 +UpdateCourseObjectLoadStructureF6C7: + ldx $B8 + jsr LoadCourseStructurePointer + + ldy $C3 + lda ($05),y + + ; F6D0: F0 3C = BEQ $F70E + .byte $F0, $3C + + jsr InitializeCourseStructure + + lda ($05),y + tax + + jsr CopyCourseStructure + + iny + sty $C3 + + lda #$8E + sta $0303 + + lda $B0 + sta $0301 + + lda $B1 + sta $0302 + + lda $B2 + sta $0F + + jsr BuildCourseStructure + + lda $B2 + jsr IncrementWrap40ECBF + sta $B2 + + ; Falls through to $F6FB. + +; --------------------------------------------------------------------------- +; $F6FB-$F70D +; Advance runtime position/state. +; +; $B1 advances through a 32-step range. When it wraps at $20, +; reset it to zero and toggle bit 2 of $B0. +; --------------------------------------------------------------------------- + +AdvanceRuntimePosition: + inc $B1 + + lda $B1 + cmp #$20 + bne @return + + lda #$00 + sta $B1 + + lda $B0 + eor #$04 + sta $B0 + +@return: + rts + +; --------------------------------------------------------------------------- +; $F70E-$F73D +; Update runtime/course state. +; --------------------------------------------------------------------------- + +UpdateRuntimeCourseState: + lda $C8 ; F70E: A5 C8 + beq @check_course ; F710: F0 0C + + lda #$00 ; F712: A9 00 + sta $C3 ; F714: 85 C3 + + lda $CA ; F716: A5 CA + + ; F718: F0 1F = BEQ $F739 + beq @set_be + + dec $CA ; F71A: C6 CA + bpl @set_be ; F71C: 10 1B + + +@check_course: + ldy $B2 ; F71E: A4 B2 + + jsr $ECC9 ; F720: 20 C9 EC + bne @clear_runtime ; F723: D0 0C + + iny ; F725: C8 + cpy #$40 ; F726: C0 40 + bne @check_next ; F728: D0 02 + + ldy #$00 ; F72A: A0 00 + + +@check_next: + jsr $ECC9 ; F72C: 20 C9 EC + + ; F72F: F0 0D = BEQ $F73E + beq ResetRuntimeCourseState + + +@clear_runtime: + lda #$00 ; F731: A9 00 + sta $B8 ; F733: 85 B8 + sta $C3 ; F735: 85 C3 + sta $C8 ; F737: 85 C8 + + +@set_be: + lda #$01 ; F739: A9 01 + sta $BE ; F73B: 85 BE + + rts ; F73D: 60 + + +; --------------------------------------------------------------------------- +; $F73E-$F754 +; Reset course/runtime state and restore the saved B2/B0/B1 values +; from BA/BB/BC. +; --------------------------------------------------------------------------- + +ResetRuntimeCourseState: + lda #$00 ; F73E: A9 00 + + sta $C3 ; F740: 85 C3 + sta $BE ; F742: 85 BE + sta $BD ; F744: 85 BD + sta $C8 ; F746: 85 C8 + + lda $BA ; F748: A5 BA + sta $B2 ; F74A: 85 B2 + + lda $BB ; F74C: A5 BB + sta $B0 ; F74E: 85 B0 + + lda $BC ; F750: A5 BC + sta $B1 ; F752: 85 B1 + + rts ; F754: 60 + + +; --------------------------------------------------------------------------- +; $F755-$F798 +; Search course/object data for a matching three-part pattern. +; --------------------------------------------------------------------------- + +FindCourseObjectPattern: + lda $B4 + bne FindCourseObjectReturnF798 + + lda $C0 + beq FindCourseObjectReturnF798 + + ldx #$15 + + +; --------------------------------------------------------------------------- +; $F75F +; Re-enter search using X as the course-pattern table index. +; --------------------------------------------------------------------------- + +FindCourseObjectSearchF75F: + jsr LoadCourseStructurePointer + + ldy #$00 + lda ($05),y + sta $03 + + lda #$0F + sec + sbc $03 + tay + + stx $04 + +@next_pattern: + ldx $B3 + + lda ($05),y + cmp $0540,x + bne @next_candidate + + dey + dey + dey + + lda ($05),y + cmp $0480,x + bne @next_candidate + + dey + dey + + lda ($05),y + cmp $0400,x + bne @next_candidate + + lda $04 + sta $C7 + + ; F790: 10 07 = BPL $F799 + bpl BuildCourseObjectList + +@next_candidate: + dec $04 + + ldx $04 + + ; F796: 10 C7 = BPL $F75F + bpl FindCourseObjectSearchF75F + + +FindCourseObjectReturnF798: + rts + + +; --------------------------------------------------------------------------- +; $F799-$F808 +; Build/update the runtime course/object list at $05E0. +; --------------------------------------------------------------------------- + +BuildCourseObjectList: + lda $C4 + + ; F79B: D0 0F = BNE $F7AC + bne CourseObjectContinueF7AC + + lda $C7 + sta $C6 + + ldx $B7 + jsr ReserveCourseListEntry + + lda #$3D + sta $C5 + + inc $C4 + + +CourseObjectContinueF7AC: + lda $C7 + cmp $C6 + + ; F7B0: D0 17 = BNE $F7C9 + .byte $D0, $17 + + lda $C6 + + ; F7B4: D0 E2 = BNE $F798 + .byte $D0, $E2 + + inc $C5 + + lda $C5 + cmp #$7F + + ; F7BC: 90 3C = BCC $F7FA + bcc CourseObjectCheckLimitF7FA + + ldx $B7 + sta $05E0,x + + inx + jsr ReserveCourseListEntry + + ; F7C7: F0 31 = BEQ $F7FA + beq CourseObjectCheckLimitF7FA + + +; $F7C9 +CourseObjectCheckSavedF7C9: + lda $C6 + + ; F7CB: D0 1A = BNE $F7E7 + .byte $D0, $1A + + +; $F7CD +CourseObjectAppendCurrentF7CD: + ldx $B7 + + lda $C5 + sta $05E0,x + + inx + stx $B7 + + lda $C7 + cmp #$11 + + ; F7DB: B0 2C = BCS $F809 + .byte $B0, $2C + + lda $C7 + sta $05E0,x + + jsr AdvanceCourseList + + ; F7E5: F0 05 = BEQ $F7EC + beq CourseObjectAppendSavedF7EC + + +; $F7E7 +CourseObjectReserveNextF7E7: + ldx $B7 + jsr ReserveCourseListEntry + + +; $F7EC +CourseObjectAppendSavedF7EC: + lda $C7 + sta $C6 + + cmp #$09 + + ; F7F2: D0 06 = BNE $F7FA + bne CourseObjectCheckLimitF7FA + + lda #$02 + sta $BF + sta $49 + + +; $F7FA +CourseObjectCheckLimitF7FA: + lda $B7 + cmp #$ED + + ; F7FE: 90 08 = BCC $F808 + bcc CourseObjectReturnF808 + + lda $C1 + + ; F802: D0 04 = BNE $F808 + bne CourseObjectReturnF808 + + lda #$01 + sta $C1 + + +; $F808 +CourseObjectReturnF808: + rts + + +; --------------------------------------------------------------------------- +; $F809-$F820 +; Copy a zero-terminated indirect list into the runtime list at $05E0. +; --------------------------------------------------------------------------- + +CopyCourseObjectList: + and #$0F ; F809: 29 0F + tax ; F80B: AA + dex ; F80C: CA + + jsr LoadCourseListPointer + ; F80D: 20 39 F8 + + ldy #$00 ; F810: A0 00 + +@copy: + ldx $B7 ; F812: A6 B7 + + lda ($07),y ; F814: B1 07 + + ; F816: F0 D4 = BEQ $F7EC + .byte $F0, $D4 + + sta $05E0,x ; F818: 9D E0 05 + + inx ; F81B: E8 + stx $B7 ; F81C: 86 B7 + + iny ; F81E: C8 + bne @copy ; F81F: D0 F1 + +; --------------------------------------------------------------------------- +; $F821-$F825 +; Write a $40 marker into the runtime list. +; +; Falls through into AdvanceCourseList. +; --------------------------------------------------------------------------- + +ReserveCourseListEntry: + lda #$40 + sta $05E0,x + + +; --------------------------------------------------------------------------- +; $F826-$F82D +; Advance runtime-list index and clear the current run counter. +; --------------------------------------------------------------------------- + +AdvanceCourseList: + inx + stx $B7 + + lda #$00 + sta $C5 + + rts + + +; --------------------------------------------------------------------------- +; $F82E-$F838 +; Load split ROM pointer into $05:$06. +; --------------------------------------------------------------------------- + +LoadCourseStructurePointer: + lda $F063,x + sta $05 + + lda $F087,x + sta $06 + + rts + + +; --------------------------------------------------------------------------- +; $F839-$F843 +; Load split ROM pointer into $07:$08. +; --------------------------------------------------------------------------- + +LoadCourseListPointer: + lda $F4F5,x + sta $07 + + lda $F4FA,x + sta $08 + + rts + +; --------------------------------------------------------------------------- +; $F844-$F86A +; NMI-time audio/APU update. +; --------------------------------------------------------------------------- + +UpdateAudio: + lda #$C0 + sta APUFRAMECNT + + jsr UpdatePulse1Channel + jsr UpdatePulse2Channel + jsr UpdateNoiseChannel + jsr UpdateSoundEffect + + lda #$00 + sta $FF + sta $FE + sta $FD + sta $FB + + lda $07FF + beq @return + + sta APUDMALOAD + dec $07FF + +@return: + rts + +; --------------------------------------------------------------------------- +; $F86B-$F88C +; Configure an APU pulse channel. +; +; Entry: +; X = channel register offset ($00, $04, or $08) +; Y = value written to the channel's sweep register +; A = index into the table at $FF00/$FF01 +; +; $F886 and $F88A are secondary entries selecting register offsets +; $04 and $08 before joining the common code at $F872. +; --------------------------------------------------------------------------- + +ConfigurePulse1F86B: + stx APUPC1BASE ; F86B: 8E 00 40 + sty APUPC1SWEEP ; F86E: 8C 01 40 + + ldx #$00 ; F871: A2 00 + +ConfigureApuChannelCommonF873: + tay ; F873: A8 + + lda ApuPeriodTableFF00+1,y ; F874: B9 01 FF + beq @return ; F877: F0 0B + + sta APUPC1TLOW,x ; F879: 9D 02 40 + + lda ApuPeriodTableFF00,y ; F87C: B9 00 FF + ora #$08 ; F87F: 09 08 + sta APUPC1THIGH,x ; F881: 9D 03 40 + +@return: + rts ; F884: 60 + + +; --------------------------------------------------------------------------- +; $F885 +; Select APU register group beginning at $4004, then enter common code. +; --------------------------------------------------------------------------- + +ConfigureApuChannel1F885: + ldx #$04 ; F885: A2 04 + + ; F887: D0 EA = BNE $F873 + .byte $D0, $EA + + +; --------------------------------------------------------------------------- +; $F889 +; Select APU register group beginning at $4008, then enter common code. +; --------------------------------------------------------------------------- + +ConfigureApuChannel2F889: + ldx #$08 ; F889: A2 08 + + ; F88B: D0 E6 = BNE $F873 + .byte $D0, $E6 + + +; --------------------------------------------------------------------------- +; $F88D-$F89B +; Pulse 1 effect setup. +; Entry from UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF88D: + sty $F0 ; F88D: 84 F0 + + lda #$01 ; F88F: A9 01 + sta $F5 ; F891: 85 F5 + + ldx #$80 ; F893: A2 80 + ldy #$9C ; F895: A0 9C + lda #$04 ; F897: A9 04 + + jmp ConfigurePulse1F86B ; F899: 4C 6B F8 + + +; --------------------------------------------------------------------------- +; $F89C-$F8AA +; Pulse 1 effect setup. +; Entry from UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF89C: + sty $F0 ; F89C: 84 F0 + + lda #$10 ; F89E: A9 10 + sta $F5 ; F8A0: 85 F5 + + ldx #$85 ; F8A2: A2 85 + ldy #$85 ; F8A4: A0 85 + lda #$30 ; F8A6: A9 30 + + jsr ConfigurePulse1F86B ; F8A8: 20 6B F8 + + +; --------------------------------------------------------------------------- +; $F8AB +; Tail-call into the continuation/effect handler at $F93A. +; --------------------------------------------------------------------------- + +Pulse1EffectF8AB: + jmp $F93A ; F8AB: 4C 3A F9 + + +; --------------------------------------------------------------------------- +; $F8AE-$F8D3 +; Pulse 1 effect/state setup. +; Contains alternate entry points used by the dispatcher. +; --------------------------------------------------------------------------- + +Pulse1EffectF8AE: + sty $F0 ; F8AE: 84 F0 + + lda #$09 ; F8B0: A9 09 + sta $F5 ; F8B2: 85 F5 + + lda #$04 ; F8B4: A9 04 + jsr $F871 ; F8B6: 20 71 F8 + + lda $F5 ; F8B9: A5 F5 + cmp #$04 ; F8BB: C9 04 + bne @check_07 ; F8BD: D0 03 + + jmp $F93E ; F8BF: 4C 3E F9 + +@check_07: + ldy #$84 ; F8C2: A0 84 + + cmp #$07 ; F8C4: C9 07 + bcs @write_sweep ; F8C6: B0 02 + + ldy #$8B ; F8C8: A0 8B + +@write_sweep: + sty APUPC1SWEEP ; F8CA: 8C 01 40 + + ora #$90 ; F8CD: 09 90 + sta APUPC1BASE ; F8CF: 8D 00 40 + + ; F8D2: D0 66 = BNE $F93A + .byte $D0, $66 + + +; --------------------------------------------------------------------------- +; $F8D4-$F90C +; Pulse 1 sound/event dispatcher. +; +; $F4 = active Pulse 1 effect/state +; $FF = pending event/request bits, destructively shifted +; $F0 = secondary request/event bits +; +; The branch targets outside this decoded block are kept as literal +; branch bytes until their destination routines are converted. +; --------------------------------------------------------------------------- + +UpdatePulse1Channel: + ldy $F4 ; F8D4: A4 F4 + + ; F8D6: D0 35 = BNE $F90D + .byte $D0, $35 + + ldy $FF ; F8D8: A4 FF + lda $F0 ; F8DA: A5 F0 + + lsr $FF ; F8DC: 46 FF + + ; F8DE: B0 AD = BCS $F88D + .byte $B0, $AD + + lsr a ; F8E0: 4A + + ; F8E1: B0 57 = BCS $F93A + .byte $B0, $57 + + lsr $FF ; F8E3: 46 FF + + ; F8E5: B0 B5 = BCS $F89C + .byte $B0, $B5 + + lsr a ; F8E7: 4A + + ; F8E8: B0 C1 = BCS $F8AB + .byte $B0, $C1 + + lsr a ; F8EA: 4A + + ; F8EB: B0 CC = BCS $F8B9 + .byte $B0, $CC + + lsr $FF ; F8ED: 46 FF + + ; F8EF: B0 BD = BCS $F8AE + .byte $B0, $BD + + lsr $FF ; F8F1: 46 FF + + ; F8F3: B0 19 = BCS $F90E + .byte $B0, $19 + + lsr a ; F8F5: 4A + + ; F8F6: B0 29 = BCS $F921 + .byte $B0, $29 + + lsr $FF ; F8F8: 46 FF + + ; F8FA: B0 4C = BCS $F948 + .byte $B0, $4C + + lsr a ; F8FC: 4A + + ; F8FD: B0 5C = BCS $F95B + .byte $B0, $5C + + lsr $FF ; F8FF: 46 FF + + ; F901: B0 6A = BCS $F96D + .byte $B0, $6A + + lsr a ; F903: 4A + + ; F904: B0 6F = BCS $F975 + .byte $B0, $6F + + lsr $FF ; F906: 46 FF + + ; F908: B0 73 = BCS $F97D + .byte $B0, $73 + + lsr a ; F90A: 4A + + ; F90B: B0 7D = BCS $F98A + .byte $B0, $7D + + rts ; F90D: 60 + + +; --------------------------------------------------------------------------- +; $F90E-$F947 +; Pulse 1 effect state machine. +; +; Entry at $F90E is selected from UpdatePulse1Channel. +; Several internal addresses are also direct dispatcher targets. +; --------------------------------------------------------------------------- + +Pulse1EffectF90E: + sty $F0 ; F90E: 84 F0 + + lda #$06 ; F910: A9 06 + sta $F5 ; F912: 85 F5 + + ldx #$9C ; F914: A2 9C + lda #$3C ; F916: A9 3C + + jsr ConfigurePulse1F86B ; F918: 20 6B F8 + + lda #$03 ; F91B: A9 03 + sta $F6 ; F91D: 85 F6 + + ; F91F: D0 0A = BNE $F92B + .byte $D0, $0A + + +; --------------------------------------------------------------------------- +; $F921 +; Secondary entry selected by UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF921: + dec $F6 ; F921: C6 F6 + beq Pulse1EffectF931 ; F923: F0 0C + + lda $F6 ; F925: A5 F6 + cmp #$03 ; F927: C9 03 + + ; F929: D0 E2 = BNE $F90D + ; $F90D is the dispatcher RTS. + .byte $D0, $E2 + + +; --------------------------------------------------------------------------- +; $F92B +; Update Pulse 1 sweep/state. +; --------------------------------------------------------------------------- + +Pulse1EffectSweepF92B: + lda #$9A ; F92B: A9 9A + sta APUPC1SWEEP ; F92D: 8D 01 40 + rts ; F930: 60 + + +; --------------------------------------------------------------------------- +; $F931 +; Alternate sweep state. +; --------------------------------------------------------------------------- + +Pulse1EffectF931: + lda #$93 ; F931: A9 93 + sta APUPC1SWEEP ; F933: 8D 01 40 + + lda #$06 ; F936: A9 06 + sta $F6 ; F938: 85 F6 + + +; --------------------------------------------------------------------------- +; $F93A +; Shared active-effect continuation. +; Also reached by JMP $F93A from $F8AB. +; --------------------------------------------------------------------------- + +Pulse1ActiveF93A: + dec $F5 ; F93A: C6 F5 + bne Pulse1EffectReturnF947 ; F93C: D0 09 + + +; --------------------------------------------------------------------------- +; $F93E +; Shared effect-stop entry. +; Also reached from $F8BF. +; --------------------------------------------------------------------------- + +Pulse1StopF93E: + lda #$00 ; F93E: A9 00 + sta $F0 ; F940: 85 F0 + + lda #$90 ; F942: A9 90 + sta APUPC1BASE ; F944: 8D 00 40 + + +Pulse1EffectReturnF947: + rts ; F947: 60 + +; --------------------------------------------------------------------------- +; $F948-$F9A1 +; Pulse 1 effect state machine. +; +; Contains several secondary entries selected directly from +; UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF948: + sty $F0 ; F948: 84 F0 + + lda #$03 ; F94A: A9 03 + sta $F5 ; F94C: 85 F5 + + ldx #$9C ; F94E: A2 9C + lda #$00 ; F950: A9 00 + + jsr ConfigurePulse1F86B ; F952: 20 6B F8 + + lda #$04 ; F955: A9 04 + sta $F6 ; F957: 85 F6 + + ; F959: D0 0A = BNE $F965 + .byte $D0, $0A + + +; --------------------------------------------------------------------------- +; $F95B +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF95B: + dec $F6 ; F95B: C6 F6 + beq Pulse1EffectF969 + + lda $F6 ; F95F: A5 F6 + cmp #$04 ; F961: C9 04 + + ; F963: D0 A8 = BNE $F90D + .byte $D0, $A8 + + +; $F965 +Pulse1EffectSweep9AF965: + lda #$9A ; F965: A9 9A + + ; F967: D0 C4 = BNE $F92D + .byte $D0, $C4 + + +; $F969 +Pulse1EffectF969: + lda #$93 ; F969: A9 93 + + ; F96B: D0 C6 = BNE $F933 + .byte $D0, $C6 + + +; --------------------------------------------------------------------------- +; $F96D +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF96D: + sty $F0 ; F96D: 84 F0 + + lda #$02 ; F96F: A9 02 + sta $F5 ; F971: 85 F5 + + ; F973: D0 D9 = BNE $F94E + .byte $D0, $D9 + + +; --------------------------------------------------------------------------- +; $F975 +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF975: + dec $F6 ; F975: C6 F6 + + ; F977: D0 E6 = BNE $F95F + .byte $D0, $E6 + + lda #$94 ; F979: A9 94 + + ; F97B: D0 B6 = BNE $F933 + .byte $D0, $B6 + + +; --------------------------------------------------------------------------- +; $F97D +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF97D: + sty $F0 ; F97D: 84 F0 + + lda #$21 ; F97F: A9 21 + sta $F5 ; F981: 85 F5 + + ldy #$BC ; F983: A0 BC + lda #$30 ; F985: A9 30 + + jsr ConfigurePulse1F86B ; F987: 20 6B F8 + + +; --------------------------------------------------------------------------- +; $F98A +; Secondary dispatcher entry and active effect update. +; --------------------------------------------------------------------------- + +Pulse1EffectF98A: + lda $F5 ; F98A: A5 F5 + cmp #$15 ; F98C: C9 15 + bne @update_volume ; F98E: D0 05 + + ldy #$7F ; F990: A0 7F + sty APUPC1SWEEP ; F992: 8C 01 40 + + +@update_volume: + lsr a ; F995: 4A + tay ; F996: A8 + + lda $F9A2,y + ; F997: B9 A2 F9 + + bcs @store_volume ; F99A: B0 02 + + lda #$13 ; F99C: A9 13 + +@store_volume: + sta APUPC1BASE ; F99E: 8D 00 40 + + ; F9A1: D0 97 = BNE $F93A + .byte $D0, $97 + + +; --------------------------------------------------------------------------- +; $F9A3-$F9B2 +; Continuation of the Pulse 1 effect lookup table whose first byte is +; physically shared with the branch operand at $F9A2. +; +; Logical table begins: +; +; F9A2: $97 ; also operand of BNE at $F9A1 +; F9A3: $91 +; ... +; --------------------------------------------------------------------------- + +Pulse1EffectTableF9A3: + .byte $91, $92, $93, $94 + .byte $95, $96, $97, $98 + .byte $9A, $9C, $9F, $9C + .byte $9A, $98, $96, $94 + +; --------------------------------------------------------------------------- +; $F9B3-$FA4F +; Audio effect lookup data. +; --------------------------------------------------------------------------- + +AudioEffectDataF9B3: + +; $F9B3 + .byte $00, $02, $04, $02, $00, $FE, $94, $96 + .byte $94, $93, $92, $91, $1C, $54, $16, $52 + +; $F9C3 + .byte $12, $18, $18, $20, $28, $30, $32, $34 + .byte $36, $38, $39, $3A, $3B, $3C, $3D, $3E + +; $F9D3 + .byte $3F, $40, $41, $42, $43, $44, $45, $46 + .byte $47, $48, $49, $4A, $4B, $4C, $4D, $4E + +; $F9E3 + .byte $4F, $50, $51, $52, $53, $54, $55, $56 + .byte $57, $58, $59, $5A, $5B, $5C, $5D, $5E + +; $F9F3 + .byte $5F, $70, $05, $04, $03, $02, $02, $02 + .byte $02, $02, $02, $02, $02, $02, $02, $02 + +; $FA03 + .byte $02, $02, $02, $02, $02, $02, $02, $02 + .byte $02, $02, $02, $02, $02, $02, $02, $02 + +; $FA13 + .byte $02, $01, $01, $01, $01, $01, $01, $01 + .byte $01, $01, $01, $01, $01, $01, $01, $01 + +; $FA23 + .byte $01, $00, $94, $96, $98, $9C, $80, $80 + .byte $80, $80, $80, $9E, $9D, $9C, $9B, $9A + +; $FA33 + .byte $99, $98, $97, $96, $95, $95, $94, $30 + .byte $38, $40, $48, $50, $54, $58, $50, $4C + +; $FA43 + .byte $48, $44, $40, $3C, $38, $34, $30, $2C + .byte $2A, $28, $26, $24 + +; --------------------------------------------------------------------------- +; $FA4F-$FA73 +; Pulse 2 effect setup/update entry. +; --------------------------------------------------------------------------- + +Pulse2EffectFA4F: + ldy #$15 + sty $F1 + + bne @active + + dec $07F3 + + ; FA58: D0 35 = BNE $FA8F + .byte $D0, $35 + + dec $F1 + + ; FA5C: F0 31 = BEQ $FA8F + .byte $F0, $31 + +@active: + ldx $FA24,y + + lda #$01 + cpx #$80 + bne @store_mode + + lda #$02 + +@store_mode: + sta $07F3 + + lda $FA39,y + ldy #$7F + + jmp WritePulse2State + + +; --------------------------------------------------------------------------- +; $FA74-$FA94 +; Pulse 2 channel update/dispatch. +; +; $FE = pending Pulse 2 request bits +; $F1 = active Pulse 2 effect/state +; $FC = additional audio request/state bits +; --------------------------------------------------------------------------- + +UpdatePulse2Channel: + ldy $FE ; FA74: A4 FE + + ; FA76: D0 D7 = BNE $FA4F + .byte $D0, $D7 + + ldy $F1 ; FA78: A4 F1 + + ; FA7A: D0 D9 = BNE $FA55 + .byte $D0, $D9 + + lda $FC ; FA7C: A5 FC + and #$7F ; FA7E: 29 7F + + ; FA80: D0 13 = BNE $FA95 + .byte $D0, $13 + + lda $F3 ; FA82: A5 F3 + beq @return ; FA84: F0 0E + + lda #$00 ; FA86: A9 00 + sta $F3 ; FA88: 85 F3 + sta $F7 ; FA8A: 85 F7 + sta $07F8 ; FA8C: 8D F8 07 + + lda #$90 ; FA8F: A9 90 + sta APUPC2BASE ; FA91: 8D 04 40 + +@return: + rts ; FA94: 60 + +; --------------------------------------------------------------------------- +; $FA95-$FAF6 +; Active Pulse 2 effect update. +; +; Updates effect timing/state in $F3/$F7, selects values from the +; audio tables around $F9B3-$F9F4, and prepares Pulse 2 APU registers. +; --------------------------------------------------------------------------- + +UpdateActivePulse2: + ldy $F7 ; FA95: A4 F7 + inc $F7 ; FA97: E6 F7 + + cpy $07F8 ; FA99: CC F8 07 + + ; FA9C: D0 31 = BNE $FACF + .byte $D0, $31 + + cmp #$30 ; FA9E: C9 30 + + ; FAA0: B0 1D = BCS $FABF + .byte $B0, $1D + + sec ; FAA2: 38 + sbc $F3 ; FAA3: E5 F3 + + ; FAA5: F0 1C = BEQ $FAC3 + .byte $F0, $1C + + ; FAA7: B0 0A = BCS $FAB3 + .byte $B0, $0A + + dec $F3 ; FAA9: C6 F3 + + cmp #$F8 ; FAAB: C9 F8 + + ; FAAD: B0 0C = BCS $FABB + .byte $B0, $0C + + dec $F3 ; FAAF: C6 F3 + + ; FAB1: D0 08 = BNE $FABB + .byte $D0, $08 + + +; $FAB3 +Pulse2IncrementStateFAB3: + inc $F3 ; FAB3: E6 F3 + + cmp #$08 ; FAB5: C9 08 + + ; FAB7: 90 02 = BCC $FABB + .byte $90, $02 + + inc $F3 ; FAB9: E6 F3 + + +; $FABB +Pulse2StoreDelayFABB: + lda #$01 ; FABB: A9 01 + + ; FABD: D0 09 = BNE $FAC8 + .byte $D0, $09 + + +; $FABF +Pulse2ResetStateFABF: + lda #$30 ; FABF: A9 30 + sta $F3 ; FAC1: 85 F3 + + +; $FAC3 +Pulse2LoadDelayFAC3: + ldy $F3 ; FAC3: A4 F3 + lda $F9F4,y ; FAC5: B9 F4 F9 + +; $FAC8 +Pulse2StoreDelayFAC8: + sta $07F8 ; FAC8: 8D F8 07 + + lda #$00 ; FACB: A9 00 + sta $F7 ; FACD: 85 F7 + + +; $FACF +Pulse2UpdatePitchFACF: + lda $07FF ; FACF: AD FF 07 + clc ; FAD2: 18 + adc #$04 ; FAD3: 69 04 + + cmp #$40 ; FAD5: C9 40 + bcs @skip_store_07ff ; FAD7: B0 03 + + sta $07FF ; FAD9: 8D FF 07 + +@skip_store_07ff: + ldy $F3 ; FADC: A4 F3 + lda $F9C4,y ; FADE: B9 C4 F9 + + ldy $F7 ; FAE1: A4 F7 + clc ; FAE3: 18 + adc AudioEffectDataF9B3,y ; FAE4: 79 B3 F9 + + ldx $FC ; FAE7: A6 FC + bpl @positive_fc ; FAE9: 10 07 + + ldx $F9BF,y ; FAEB: BE BF F9 + ldy #$89 ; FAEE: A0 89 + + ; FAF0: D0 05 = BNE $FAF7 + .byte $D0, $05 + +@positive_fc: + ldx $F9B9,y ; FAF2: BE B9 F9 + ldy #$7F ; FAF5: A0 7F + + +; --------------------------------------------------------------------------- +; $FAF7-$FB16 +; Write Pulse 2 APU state and prepare a derived 16-bit value in $00:$01. +; +; Entry: +; X -> value written to APUPC2BASE +; Y -> value written to APUPC2SWEEP +; A -> value stored in $07F9 +; --------------------------------------------------------------------------- + +WritePulse2State: + stx APUPC2BASE ; FAF7: 8E 04 40 + sty APUPC2SWEEP ; FAFA: 8C 05 40 + sta $07F9 ; FAFD: 8D F9 07 + + ldy #$07 ; FB00: A0 07 + sty $00 ; FB02: 84 00 + + ldy #$FF ; FB04: A0 FF + sty $01 ; FB06: 84 01 + + lsr a ; FB08: 4A + lsr a ; FB09: 4A + lsr a ; FB0A: 4A + lsr a ; FB0B: 4A + lsr a ; FB0C: 4A + tay ; FB0D: A8 + + beq UpdatePulsePitch ; FB0E: F0 07 + +@scale: + lsr $00 ; FB10: 46 00 + ror $01 ; FB12: 66 01 + + dey ; FB14: 88 + bne @scale ; FB15: D0 F9 + + +; --------------------------------------------------------------------------- +; $FB17-$FB76 +; Update Pulse 2 pitch and related triangle-channel state. +; +; $00:$01 contains a derived 16-bit period/pitch value. +; $07F9 controls repeated scaling/adjustment. +; --------------------------------------------------------------------------- + +UpdatePulsePitch: + lda $00 ; FB17: A5 00 + lsr a ; FB19: 4A + tax ; FB1A: AA + + lda $01 ; FB1B: A5 01 + ror a ; FB1D: 6A + tay ; FB1E: A8 + + asl $07F9 ; FB1F: 0E F9 07 + asl $07F9 ; FB22: 0E F9 07 + asl $07F9 ; FB25: 0E F9 07 + +@adjust: + txa ; FB28: 8A + lsr a ; FB29: 4A + tax ; FB2A: AA + + tya ; FB2B: 98 + ror a ; FB2C: 6A + tay ; FB2D: A8 + + asl $07F9 ; FB2E: 0E F9 07 + bcc @check_more ; FB31: 90 0E + + sec ; FB33: 38 + eor #$FF ; FB34: 49 FF + adc $01 ; FB36: 65 01 + sta $01 ; FB38: 85 01 + + txa ; FB3A: 8A + eor #$FF ; FB3B: 49 FF + adc $00 ; FB3D: 65 00 + sta $00 ; FB3F: 85 00 + +@check_more: + lda $07F9 ; FB41: AD F9 07 + bne @adjust ; FB44: D0 E2 + + lda $01 ; FB46: A5 01 + sta APUPC2LOW ; FB48: 8D 06 40 + + lda $00 ; FB4B: A5 00 + sta APUPC2HIGH ; FB4D: 8D 07 40 + + lda $F1 ; FB50: A5 F1 + bne UpdateTriangleState ; FB52: D0 06 + + lda $F7 ; FB54: A5 F7 + ora $FA ; FB56: 05 FA + bne UpdatePulsePitchReturnFB76 + ; FB58: D0 1C + + +; --------------------------------------------------------------------------- +; $FB5A +; Derive and write the related triangle-channel period. +; --------------------------------------------------------------------------- + +UpdateTriangleState: + lsr $00 ; FB5A: 46 00 + ror $01 ; FB5C: 66 01 + + lda $00 ; FB5E: A5 00 + lsr a ; FB60: 4A + tay ; FB61: A8 + + lda $01 ; FB62: A5 01 + ror a ; FB64: 6A + + clc ; FB65: 18 + adc $01 ; FB66: 65 01 + sta APUTRILOW ; FB68: 8D 0A 40 + + tya ; FB6B: 98 + adc $00 ; FB6C: 65 00 + sta APUTRIHIGH ; FB6E: 8D 0B 40 + + lda #$04 ; FB71: A9 04 + sta APUTRIBASE ; FB73: 8D 08 40 + +UpdatePulsePitchReturnFB76: + rts ; FB76: 60 + + +; --------------------------------------------------------------------------- +; $FB77-$FBFB +; Noise/audio effect lookup tables. +; --------------------------------------------------------------------------- + +NoiseAudioDataFB77: + +; $FB77 + .byte $10, $11, $12, $12, $12, $13, $13, $14 + .byte $15, $16, $17, $18, $19, $1A, $1B, $1C + +; $FB87 + .byte $1D, $1E, $1F, $1C, $1F, $1F, $1F, $1F + .byte $1F, $18, $1F, $1F, $1F, $1F, $18, $1C + +; $FB97 + .byte $1F, $1F, $18, $1F, $1F, $1F, $14, $1F + .byte $1C, $1F, $1F, $1C, $18, $16, $0E, $0C + +; $FBA7 + .byte $0E, $0E, $0A, $0C, $0E, $0E, $0C, $0A + .byte $0E, $0D, $0E, $0E, $0E, $0E, $0C, $0E + +; $FBB7 + .byte $0A, $0E, $0C, $0C, $0E, $0D, $0C, $0D + .byte $0D, $0E, $0E, $0D, $0D, $0E, $0B, $0E + +; $FBC7 + .byte $0E, $0D, $0E, $0E, $0C, $0D, $0E, $0B + .byte $0D, $0E, $0E, $0D, $30, $31, $32, $32 + +; $FBD7 + .byte $33, $33, $34, $35, $36, $37, $38, $37 + .byte $38, $37, $36, $35, $34, $34, $34, $35 + +; $FBE7 + .byte $36, $37, $38, $39, $3A, $3B, $3C, $3A + .byte $38, $36, $35, $34, $10, $11, $12, $14 + +; $FBF7-$FBFE + .byte $16, $18, $1C, $1F, $1C + .byte $18, $16, $14 + +; --------------------------------------------------------------------------- +; $FBFF-$FC20 +; Initialize/update Noise-channel effect state. +; +; Entry: +; Y = noise effect/state selector +; +; Uses lookup tables at $FB76 and $FBA4 to configure the noise channel. +; --------------------------------------------------------------------------- + +SetupNoiseEffectLong: + sty $F2 ; FBFF: 84 F2 + + lda #$2E ; FC01: A9 2E + sta $07F6 ; FC03: 8D F6 07 + + tya ; FC06: 98 + beq @load_noise ; FC07: F0 05 + + and #$02 ; FC09: 29 02 + sta $07F7 ; FC0B: 8D F7 07 + + +@load_noise: + ldy $07F6 ; FC0E: AC F6 07 + + lda $FB76,y ; FC11: B9 76 FB + sta APUNOISEBASE ; FC14: 8D 0C 40 + + lda $FBA4,y ; FC17: B9 A4 FB + sta APUNOISELOOP ; FC1A: 8D 0E 40 + + ; FC1D: D0 56 = BNE $FC75 + .byte $D0, $56 + +; --------------------------------------------------------------------------- +; $FC1F-$FC3C +; Initialize/reset Noise-channel effect state. +; +; Y supplies the new noise-effect state. +; --------------------------------------------------------------------------- + +SetupNoiseEffectShort: + sty $F2 ; FC1F: 84 F2 + + lda #$00 ; FC21: A9 00 + sta $07F6 ; FC23: 8D F6 07 + sta $07F7 ; FC26: 8D F7 07 + + lda #$0E ; FC29: A9 0E + sta APUNOISELOOP ; FC2B: 8D 0E 40 + + lda $07F6 ; FC2E: AD F6 07 + lsr a ; FC31: 4A + lsr a ; FC32: 4A + lsr a ; FC33: 4A + tay ; FC34: A8 + + lda $FBD3,y ; FC35: B9 D3 FB + sta APUNOISEBASE ; FC38: 8D 0C 40 + + ; FC3B: D0 38 = BNE $FC75 + .byte $D0, $38 + +; --------------------------------------------------------------------------- +; $FC3D-$FC88 +; Noise-channel event dispatcher and active-effect update. +; +; $FD = pending noise request bits, destructively shifted +; $F2 = active noise effect/state +; --------------------------------------------------------------------------- + +UpdateNoiseChannel: + ldy $FD ; FC3D: A4 FD + lda $F2 ; FC3F: A5 F2 + + lsr $FD ; FC41: 46 FD + + ; FC43: B0 BA = BCS $FBFF + .byte $B0, $BA + + lsr a ; FC45: 4A + + ; FC46: B0 BE = BCS $FC06 + .byte $B0, $BE + + lsr $FD ; FC48: 46 FD + + ; FC4A: B0 D3 = BCS $FC1F + .byte $B0, $D3 + + lsr a ; FC4C: 4A + + ; FC4D: B0 DF = BCS $FC2E + .byte $B0, $DF + + lsr a ; FC4F: 4A + + ; FC50: B0 11 = BCS $FC63 + .byte $B0, $11 + + lsr $FD ; FC52: 46 FD + + ; FC54: B0 01 = BCS $FC57 + .byte $B0, $01 + + rts ; FC56: 60 + + +; --------------------------------------------------------------------------- +; $FC57 +; Start/configure another Noise effect. +; --------------------------------------------------------------------------- + +NoiseEffectFC57: + sty $F2 ; FC57: 84 F2 + + lda #$0C ; FC59: A9 0C + sta $07F6 ; FC5B: 8D F6 07 + + lda #$0D ; FC5E: A9 0D + sta APUNOISELOOP ; FC60: 8D 0E 40 + + +; --------------------------------------------------------------------------- +; $FC63 +; Active Noise effect update. +; --------------------------------------------------------------------------- + +NoiseEffectUpdateFC63: + ldy $07F6 ; FC63: AC F6 07 + + lda $FBF2,y ; FC66: B9 F2 FB + sta APUNOISEBASE ; FC69: 8D 0C 40 + + cpy #$08 ; FC6C: C0 08 + bne @trigger_noise ; FC6E: D0 05 + + lda #$0E ; FC70: A9 0E + sta APUNOISELOOP ; FC72: 8D 0E 40 + + +@trigger_noise: + lda #$08 ; FC75: A9 08 + sta APUNOISELENGTH ; FC77: 8D 0F 40 + + dec $07F6 ; FC7A: CE F6 07 + + ; FC7D: D0 D7 = BNE $FC56 + .byte $D0, $D7 + + ldy $07F7 ; FC7F: AC F7 07 + + ; FC82: D0 9B = BNE $FC1F + .byte $D0, $9B + + lda #$00 ; FC84: A9 00 + sta $F2 ; FC86: 85 F2 + + rts ; FC88: 60 + + +; --------------------------------------------------------------------------- +; $FC89-$FC8C +; Small audio lookup table. +; --------------------------------------------------------------------------- + +AudioControlTableFC89: + .byte $7F, $8E, $86, $8F + + +; --------------------------------------------------------------------------- +; $FC8D-$FCD8 +; Initialize/start the multi-channel sound-effect sequencer. +; +; Called directly from UpdateAudio. +; $FB = pending effect request +; $F4 = active effect state +; --------------------------------------------------------------------------- + +UpdateSoundEffect: + lda $FB ; FC8D: A5 FB + bne @start_effect ; FC8F: D0 05 + + lda $F4 ; FC91: A5 F4 + + ; FC93: D0 44 = BNE $FCD9 + .byte $D0, $44 + + rts ; FC95: 60 + +@start_effect: + sta $F4 ; FC96: 85 F4 + + ldy #$00 ; FC98: A0 00 + +@find_effect: + iny ; FC9A: C8 + lsr a ; FC9B: 4A + bcc @find_effect ; FC9C: 90 FC + + lda $FDD2,y ; FC9E: B9 D2 FD + tay ; FCA1: A8 + + lda SoundEffectDescriptorDataFDD3,y + ; FCA2: B9 D3 FD + sta $07F0 ; FCA5: 8D F0 07 + + lda SoundEffectDescriptorDataFDD3+1,y + ; FCA8: B9 D4 FD + sta $F8 ; FCAB: 85 F8 + + lda SoundEffectDescriptorDataFDD3+2,y + ; FCAD: B9 D5 FD + sta $F9 ; FCB0: 85 F9 + + lda SoundEffectDescriptorDataFDD3+3,y + ; FCB2: B9 D6 FD + sta $07E0 ; FCB5: 8D E0 07 + + lda SoundEffectDescriptorDataFDD3+4,y + ; FCB8: B9 D7 FD + sta $FA ; FCBB: 85 FA + + lda SoundEffectDescriptorDataFDD3+5,y + ; FCBD: B9 D8 FD + sta $07EC ; FCC0: 8D EC 07 + + lda #$01 ; FCC3: A9 01 + + sta $07E5 ; FCC5: 8D E5 07 + sta $07E1 ; FCC8: 8D E1 07 + sta $07E9 ; FCCB: 8D E9 07 + sta $07ED ; FCCE: 8D ED 07 + + ldy #$00 ; FCD1: A0 00 + sty $07E4 ; FCD3: 8C E4 07 + + inc $07FB ; FCD6: EE FB 07 + + +; --------------------------------------------------------------------------- +; $FCD9-$FD1B +; Update the first channel/voice of the active sound-effect sequence. +; +; Uses the stream pointer in $F8:$F9 and counters at $07E0/$07E1. +; --------------------------------------------------------------------------- + +UpdateSoundEffectVoice0FCD9: + ldy $07E0 ; FCD9: AC E0 07 + + ; FCDC: F0 3C = BEQ $FD1A + .byte $F0, $3C + + dec $07E1 ; FCDE: CE E1 07 + + ; FCE1: D0 37 = BNE $FD1A + .byte $D0, $37 + + inc $07E0 ; FCE3: EE E0 07 + + lda ($F8),y ; FCE6: B1 F8 + jsr DecodeAudioEvent ; FCE8: 20 53 FF + sta $07E1 ; FCEB: 8D E1 07 + + txa ; FCEE: 8A + and #$3E ; FCEF: 29 3E + jsr $F871 ; FCF1: 20 71 F8 + + bne @select_control ; FCF4: D0 04 + + ldx #$10 ; FCF6: A2 10 + + ; FCF8: D0 0A = BNE $FD04 + .byte $D0, $0A + +@select_control: + ldx #$4F ; FCFA: A2 4F + + lda $F4 ; FCFC: A5 F4 + and #$60 ; FCFE: 29 60 + bne @store_control ; FD00: D0 02 + + ldx #$05 ; FD02: A2 05 + +@store_control: + stx APUPC1BASE ; FD04: 8E 00 40 + + ldy #$7F ; FD07: A0 7F + + lda $F4 ; FD09: A5 F4 + lsr a ; FD0B: 4A + bcc @store_sweep ; FD0C: 90 09 + + lda $07FB ; FD0E: AD FB 07 + and #$03 ; FD11: 29 03 + tax ; FD13: AA + + ldy AudioControlTableFC89,x ; FD14: BC 89 FC + +@store_sweep: + sty APUPC1SWEEP ; FD17: 8C 01 40 + + dec $07E5 ; FD1A: CE E5 07 + +; --------------------------------------------------------------------------- +; $FD1D-$FD62 +; Update the second Pulse-channel portion of the active sound effect. +; --------------------------------------------------------------------------- + +UpdateSoundEffectVoice1FD1D: + ; FD1D: D0 44 = BNE $FD63 + .byte $D0, $44 + + ldy $07E4 ; FD1F: AC E4 07 + inc $07E4 ; FD22: EE E4 07 + + lda ($F8),y ; FD25: B1 F8 + bne @decode_event ; FD27: D0 09 + + lda #$00 ; FD29: A9 00 + sta $FA ; FD2B: 85 FA + sta $F4 ; FD2D: 85 F4 + + jmp $F942 ; FD2F: 4C 42 F9 + + +@decode_event: + jsr DecodeAudioEvent ; FD32: 20 53 FF + sta $07E5 ; FD35: 8D E5 07 + + lda $F4 ; FD38: A5 F4 + and #$60 ; FD3A: 29 60 + + ; FD3C: D0 25 = BNE $FD63 + .byte $D0, $25 + + txa ; FD3E: 8A + and #$3E ; FD3F: 29 3E + + jsr ConfigureApuChannel1F885 + ; FD41: 20 85 F8 + + bne @use_87 ; FD44: D0 04 + + ldx #$10 ; FD46: A2 10 + + ; FD48: D0 11 = BNE $FD5B + .byte $D0, $11 + + +@use_87: + ldx #$87 ; FD4A: A2 87 + + lda $07E5 ; FD4C: AD E5 07 + cmp #$10 ; FD4F: C9 10 + bcs @write_pulse2 ; FD51: B0 08 + + ldx #$84 ; FD53: A2 84 + + cmp #$08 ; FD55: C9 08 + bcs @write_pulse2 ; FD57: B0 02 + + ldx #$82 ; FD59: A2 82 + + +@write_pulse2: + stx APUPC2BASE ; FD5B: 8E 04 40 + + lda #$7F ; FD5E: A9 7F + sta APUPC2SWEEP ; FD60: 8D 05 40 + + +; --------------------------------------------------------------------------- +; $FD63-$FD98 +; Update triangle-channel portion of the active sound effect. +; --------------------------------------------------------------------------- + +UpdateSoundEffectTriangleFD63: + ldy $FA ; FD63: A4 FA + + ; FD65: F0 32 = BEQ $FD99 + .byte $F0, $32 + + dec $07E9 ; FD67: CE E9 07 + + ; FD6A: D0 2D = BNE $FD99 + .byte $D0, $2D + + inc $FA ; FD6C: E6 FA + + lda ($F8),y ; FD6E: B1 F8 + jsr DecodeAudioEvent ; FD70: 20 53 FF + sta $07E9 ; FD73: 8D E9 07 + + clc ; FD76: 18 + adc #$FF ; FD77: 69 FF + + cmp #$0C ; FD79: C9 0C + bcc @limit_ok ; FD7B: 90 02 + + lda #$0C ; FD7D: A9 0C + + +@limit_ok: + asl a ; FD7F: 0A + asl a ; FD80: 0A + + ldy $F4 ; FD81: A4 F4 + cpy #$20 ; FD83: C0 20 + bne @write_triangle ; FD85: D0 02 + + lda #$81 ; FD87: A9 81 + + +@write_triangle: + sta APUTRIBASE ; FD89: 8D 08 40 + + txa ; FD8C: 8A + and #$3E ; FD8D: 29 3E + + jsr ConfigureApuChannel2F889 + ; FD8F: 20 89 F8 + + ; FD92: D0 05 = BNE $FD99 + .byte $D0, $05 + + lda #$00 ; FD94: A9 00 + sta APUTRIBASE ; FD96: 8D 08 40 + + +; --------------------------------------------------------------------------- +; $FD99-$FDD2 +; Update Noise-channel portion of the active sound effect. +; --------------------------------------------------------------------------- + +UpdateSoundEffectNoiseFD99: + lda $F4 ; FD99: A5 F4 + and #$1B ; FD9B: 29 1B + + ; FD9D: F0 33 = BEQ $FDD2 + beq SoundEffectReturnFDD2 + + dec $07ED ; FD9F: CE ED 07 + + ; FDA2: D0 2E = BNE $FDD2 + bne SoundEffectReturnFDD2 + + ldy $07EC ; FDA4: AC EC 07 + inc $07EC ; FDA7: EE EC 07 + + lda ($F8),y ; FDAA: B1 F8 + jsr DecodeAudioEvent ; FDAC: 20 53 FF + sta $07ED ; FDAF: 8D ED 07 + + txa ; FDB2: 8A + and #$3E ; FDB3: 29 3E + + ; FDB5: F0 1B = BEQ $FDD2 + beq SoundEffectReturnFDD2 + + cmp #$20 ; FDB7: C9 20 + beq @noise_alt ; FDB9: F0 08 + + lda #$00 ; FDBB: A9 00 + ldx #$00 ; FDBD: A2 00 + ldy #$08 ; FDBF: A0 08 + + ; FDC1: D0 06 = BNE $FDC9 + bne @write_noise + + +@noise_alt: + lda #$00 ; FDC3: A9 00 + ldx #$02 ; FDC5: A2 02 + ldy #$08 ; FDC7: A0 08 + + +@write_noise: + sta APUNOISEBASE ; FDC9: 8D 0C 40 + stx APUNOISELOOP ; FDCC: 8E 0E 40 + sty APUNOISELENGTH ; FDCF: 8C 0F 40 + + +SoundEffectReturnFDD2: + rts ; FDD2: 60 + + +; --------------------------------------------------------------------------- +; $FDD3-$FDFB +; Sound-effect descriptor / sequence data used by UpdateSoundEffect. +; +; $FC8D uses an index derived from the requested effect, then reads: +; $FDD2,Y +; $FDD3,Y -> $07F0 +; $FDD4,Y -> $F8 +; $FDD5,Y -> $F9 +; $FDD6,Y -> $07E0 +; $FDD7,Y -> $FA +; $FDD8,Y -> $07EC +; +; Note that $FDD2 is the preceding RTS byte ($60), so the original ROM +; again reuses an instruction byte as part of an indexed data structure. +; --------------------------------------------------------------------------- + +SoundEffectDescriptorDataFDD3: +; $FDD3 + .byte $08, $0E, $14, $0E + .byte $0E, $19, $1E, $23 + .byte $00, $64, $FF, $2D + .byte $59, $74, $0F, $FB + +; $FDE3 + .byte $FD, $31, $56, $7B + .byte $08, $87, $FE, $27 + .byte $41, $0F, $F3, $FE + .byte $03, $00, $08, $FD + +; $FDF3-$FDFB + .byte $FE, $02, $00, $08 + .byte $F0, $FF, $02, $00 + .byte $02 + + +; --------------------------------------------------------------------------- +; $FDFC-$FE2B +; Audio/music sequence data. +; +; Sequence consists primarily of two-byte event/value pairs and ends +; with $00 at $FE2B. +; --------------------------------------------------------------------------- + +AudioSequenceFDFC: + .byte $20, $26 + .byte $02, $4A + .byte $02, $06 + + .byte $02, $20 + .byte $26, $02 + .byte $4A, $02 + .byte $06, $02 + + .byte $20, $26 + .byte $02, $2E + .byte $02, $2E + .byte $02, $2E + + .byte $46, $AE + .byte $02, $42 + .byte $6E, $6A + + .byte $22, $1C + .byte $2A, $22 + .byte $1C, $2A + .byte $6E, $22 + .byte $1C, $2E + .byte $22, $1C + .byte $2E, $1C + + .byte $60, $62 + .byte $64, $26 + .byte $86 + + .byte $00 + + +; --------------------------------------------------------------------------- +; $FE2C-$FEAD +; Audio/music sequence data. +; --------------------------------------------------------------------------- + +AudioSequenceFE2C: + +; $FE2C + .byte $82, $60, $58, $42, $58, $60, $58, $42 + .byte $58, $60, $5C, $60, $5C, $60, $5C, $60 + +; $FE3C + .byte $5C, $62, $18, $36, $22, $18, $36, $22 + .byte $66, $3A, $3A, $26, $3A, $3A, $26, $36 + +; $FE4C + .byte $7A, $58, $5C, $22, $A6, $82, $58, $66 + .byte $58, $66, $58, $66, $58, $66, $60, $68 + +; $FE5C + .byte $60, $68, $60, $68, $60, $68, $46, $2A + .byte $22, $06, $2A, $22, $06, $50, $26, $26 + +; $FE6C + .byte $10, $10, $26, $26, $22, $66, $6A, $6A + .byte $0A, $8E, $41, $A0, $A0, $A0, $A0, $A0 + +; $FE7C + .byte $A0, $A0, $60, $E0, $E0, $20, $60, $60 + .byte $60, $20, $A0, $22, $26, $22, $26, $22 + +; $FE8C + .byte $26, $26, $2A, $26, $2A, $26, $2A, $2A + .byte $2C, $2A, $2C, $2A, $2C, $46, $6E, $66 + +; $FE9C + .byte $6B, $6F, $46, $4A, $4E, $47, $6E, $46 + .byte $4A, $6D, $67, $62, $62, $62, $67, $67 + +; $FEAC-$FEAD + .byte $6B, $00 + + +; --------------------------------------------------------------------------- +; $FEAE-$FEF5 +; Audio/music sequence data. +; --------------------------------------------------------------------------- + +AudioSequenceFEAE: + +; $FEAE + .byte $6D, $47, $4B, $50, $4E, $46, $4B, $55 + .byte $50, $54, $56, $51, $4E, $50, $54, $4B + +; $FEBE + .byte $4F, $34, $18, $34, $18, $34, $18, $59 + .byte $59, $5D, $66, $6A, $6C, $66, $6A, $6C + +; $FECE + .byte $66, $6A, $6C, $66, $6A, $6C, $66, $6A + .byte $6C, $66, $6A, $6C, $66, $6A, $6C, $66 + +; $FEDE + .byte $6A, $6C, $66, $6A, $6C, $66, $6A, $6C + .byte $67, $6C, $6C, $6C, $66, $66, $66, $66 + +; $FEEE-$FEF5 + .byte $66, $66, $6A, $6A, $6A, $03, $03, $00 + + +; --------------------------------------------------------------------------- +; $FEF6-$FEFE +; Short audio sequence. +; --------------------------------------------------------------------------- + +AudioSequenceFEF6: + .byte $76, $82 + .byte $76, $82 + .byte $76, $82 + .byte $2B, $42 + .byte $00 + + +; --------------------------------------------------------------------------- +; $FEFF +; Preceding byte, not part of the aligned period table. +; --------------------------------------------------------------------------- + + .byte $44 + + +; --------------------------------------------------------------------------- +; $FF00-$FF3D +; APU timer-period lookup table. +; +; Callers normally supply an even index: +; Y = $00, $02, $04, ... $3E +; +; SetApuPeriodF873 reads: +; $FF00,Y +; $FF01,Y +; +; as adjacent bytes of the selected period record. +; --------------------------------------------------------------------------- + +ApuPeriodTableFF00: + .byte $06, $AE ; index $00 + .byte $00, $00 ; index $02 + .byte $00, $69 ; index $04 + .byte $00, $D4 ; index $06 + .byte $00, $C8 ; index $08 + .byte $00, $BD ; index $0A + .byte $00, $B2 ; index $0C + .byte $00, $A8 ; index $0E + + .byte $00, $9F ; index $10 + .byte $00, $96 ; index $12 + .byte $00, $8D ; index $14 + .byte $00, $7E ; index $16 + .byte $01, $AB ; index $18 + .byte $01, $93 ; index $1A + .byte $01, $7C ; index $1C + .byte $01, $67 ; index $1E + + .byte $01, $52 ; index $20 + .byte $01, $3F ; index $22 + .byte $01, $2D ; index $24 + .byte $01, $1C ; index $26 + .byte $01, $0C ; index $28 + .byte $00, $FD ; index $2A + .byte $00, $EE ; index $2C + .byte $00, $E1 ; index $2E + + .byte $03, $57 ; index $30 + .byte $02, $3A ; index $32 + .byte $02, $1A ; index $34 + .byte $01, $FC ; index $36 + .byte $01, $DF ; index $38 + .byte $01, $C4 ; index $3A + .byte $07, $FA ; index $3C + + +; --------------------------------------------------------------------------- +; $FF3E-$FF4D +; Audio bit/mask lookup table. +; --------------------------------------------------------------------------- + +AudioMaskTable: + .byte $04, $08, $10, $20 + .byte $05, $18, $0A, $06 + .byte $05, $0A, $14, $28 + .byte $50, $1E, $3C, $07 + +; --------------------------------------------------------------------------- +; $FF4E-$FF52 +; Additional audio lookup values immediately preceding the decoder +; entry at $FF53. +; --------------------------------------------------------------------------- + +AudioLookupTableFF4E: + .byte $0E, $1C, $38, $70, $2A + + +; --------------------------------------------------------------------------- +; $FF53-$FF63 +; Decode an audio sequence byte into a duration/value from AudioMaskTable. +; +; Entry: +; A = encoded audio event byte +; +; Exit: +; X = transformed event value +; Y = table index +; A = AudioMaskTable[Y] +; --------------------------------------------------------------------------- + +DecodeAudioEvent: + tax ; FF53: AA + + ror a ; FF54: 6A + + txa ; FF55: 8A + rol a ; FF56: 2A + rol a ; FF57: 2A + rol a ; FF58: 2A + + and #$07 ; FF59: 29 07 + + clc ; FF5B: 18 + adc $07F0 ; FF5C: 6D F0 07 + tay ; FF5F: A8 + + lda AudioMaskTable,y ; FF60: B9 3E FF + + rts ; FF63: 60 + + +; --------------------------------------------------------------------------- +; $FF64-$FFE9 +; Audio sequence / instrument / effect data. +; --------------------------------------------------------------------------- + +AudioDataFF64: + +; $FF64 + .byte $82, $60, $42, $66, $42, $46, $42, $60 + .byte $66, $42, $47, $C2, $68, $42, $6E, $42 + +; $FF74 + .byte $4E, $42, $68, $6E, $42, $4F, $C2, $0B + .byte $03, $C7, $0B, $03, $C7, $2B, $03, $E7 + +; $FF84 + .byte $A2, $23, $03, $E1, $5E, $5D, $58, $A0 + .byte $A2, $A4, $66, $C6, $00, $82, $58, $42 + +; $FF94 + .byte $60, $42, $66, $42, $58, $60, $42, $67 + .byte $C2, $60, $42, $68, $42, $6E, $42, $60 + +; $FFA4 + .byte $68, $42, $6F, $C2, $2B, $03, $EB, $2B + .byte $03, $EB, $23, $03, $E3, $9C, $1D, $03 + +; $FFB4 + .byte $D9, $78, $7B, $72, $98, $98, $9C, $62 + .byte $E0, $98, $A6, $98, $A6, $98, $A6, $98 + +; $FFC4 + .byte $A6, $A0, $AE, $A0, $AE, $A0, $AE, $A0 + .byte $AE, $9C, $EA, $9C, $E6, $E2, $60, $A6 + +; $FFD4 + .byte $AA, $AA, $4A, $E6, $80, $E0, $E0, $E0 + .byte $E0, $E0, $E0, $E0, $A0, $A0, $C0, $A0 + +; $FFE4 + .byte $E0, $50, $E0, $A0, $A0, $A0 + + +; --------------------------------------------------------------------------- +; $FFEA-$FFF9 +; Final audio/data bytes preceding the interrupt-vector table. +; --------------------------------------------------------------------------- + +AudioDataFFEA: + .byte $60, $10, $10, $10, $10, $A0, $83, $00 + .byte $04, $14, $04, $14, $04, $14, $04, $D4 + +; --------------------------------------------------------------------------- +; $FFFA-$FFFF +; 6502 interrupt vectors. +; --------------------------------------------------------------------------- + +InterruptVectorsFFFA: + .word NMI ; FFFA-FFFB: NMI + .word $C184 ; FFFC-FFFD: RESET + .word $FFF0 ; FFFE-FFFF: IRQ/BRK \ No newline at end of file diff --git a/Excitebike (JU)/copilot/symbols.inc b/Excitebike (JU)/copilot/symbols.inc new file mode 100644 index 0000000..7e118f5 --- /dev/null +++ b/Excitebike (JU)/copilot/symbols.inc @@ -0,0 +1,85 @@ +; PPU registers +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +OAMADDR = $2003 +OAMDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +OAMDMA = $4014 +JOY1 = $4016 + +; Variables +PpuControlShadow = $10 +PpuMaskShadow = $11 +Controller1Held = $14 +Controller1Pressed = $15 +Controller2Held = $16 +Controller2Pressed = $17 +ControllerState = Controller1Held +NmiDispatchMode = $40 +NmiState = $41 +MenuSelectionIndex = $42 +NmiSequenceStep = $44 +NmiSyncState = $48 +InputActionLatch = $49 +OamBuffer = $0200 +PersistentRecord0 = $0580 +PersistentRecord1 = $05A3 +ResetMagic1 = $05CE +ResetMagic2 = $05CF +PersistentBuffer = $06E0 +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 +MENU_HORIZONTAL_COUNT=$05 +MENU_VERTICAL_COUNT=$06 +MENU_ACTION_NONE=$00 +MENU_ACTION_ACCEPTED=$04 +NMI_SYNC_PENDING=$FF +NMI_SYNC_COMPLETE=$01 +RESET_MAGIC_1=$A5 +RESET_MAGIC_2=$5A +PPUCTRL_NMI_ENABLE=$80 + +WorkingBuffer = $05E0 +PERSISTENT_BUFFER_SIZE = $0100 + +StateTargetCA85 = $CA85 + +; Serial I/O helpers using $4016 (verified structure; peripheral identity unknown) +WriteSerialTiming = $C62D +WaitSerialPulse = $C639 +TransmitPulseShort = $C64E +TransmitPulseLong = $C653 +TransmitByte = $C66E +IncrementSerialCount = $C68A +ReceiveSerialBit = $C691 +ReceiveByte = $C6A5 + +; Block protocol helpers +TransmitRuntimeData = $C6C8 +TransmitDataBlock = $C6D1 +SetupTransmitRamBlock = $C716 +SetupTransmitWorkingBuffer = $C726 +SetTransmitPreambleCounts = $C734 +ReceiveRuntimeData = $C739 +ReceiveDataBlock = $C792 +SerialBlockLength = $0D +SerialBlockPtr = $0E +SerialBitCountLo = $08 +SerialBitCountHi = $09 + +; App Specific Variables +TMP00 = $00 +TMP01 = $01 diff --git a/Excitebike (JU)/rom.nes b/Excitebike (JU)/rom.nes new file mode 100644 index 0000000..fac67b7 Binary files /dev/null and b/Excitebike (JU)/rom.nes differ diff --git a/Pac-Man (USA) (Namco)/copilot/Makefile b/Pac-Man (USA) (Namco)/copilot/Makefile new file mode 100644 index 0000000..3f9ba6f --- /dev/null +++ b/Pac-Man (USA) (Namco)/copilot/Makefile @@ -0,0 +1,10 @@ +all: reconstructed_prg.bin verify +prg.o: prg.s symbols.inc prg.bin + ca65 -g -o $@ prg.s +reconstructed_prg.bin: prg.o linker.cfg + ld65 -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o $@ prg.o +verify: reconstructed_prg.bin prg.bin + cmp reconstructed_prg.bin prg.bin + @echo BYTE-IDENTICAL +clean: + rm -f prg.o reconstructed_prg.bin reconstructed.map reconstructed.lbl diff --git a/Pac-Man (USA) (Namco)/copilot/linker.cfg b/Pac-Man (USA) (Namco)/copilot/linker.cfg new file mode 100644 index 0000000..d9d421d --- /dev/null +++ b/Pac-Man (USA) (Namco)/copilot/linker.cfg @@ -0,0 +1,6 @@ +MEMORY { + PRG: start = $C000, size = $4000, file = %O, fill = yes; +} +SEGMENTS { + PRG: load = PRG, type = ro; +} diff --git a/Pac-Man (USA) (Namco)/copilot/prg.bin b/Pac-Man (USA) (Namco)/copilot/prg.bin new file mode 100644 index 0000000..c6c24d9 Binary files /dev/null and b/Pac-Man (USA) (Namco)/copilot/prg.bin differ diff --git a/Pac-Man (USA) (Namco)/copilot/prg.s b/Pac-Man (USA) (Namco)/copilot/prg.s new file mode 100644 index 0000000..e56656c --- /dev/null +++ b/Pac-Man (USA) (Namco)/copilot/prg.s @@ -0,0 +1,14325 @@ +;---------------------------------------------------- +; 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 + +.incbin "prg.bin", $0000, $0033 + +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 diff --git a/Pac-Man (USA) (Namco)/copilot/symbols.inc b/Pac-Man (USA) (Namco)/copilot/symbols.inc new file mode 100644 index 0000000..e69de29 diff --git a/Pac-Man (USA) (Namco)/rom.nes b/Pac-Man (USA) (Namco)/rom.nes new file mode 100644 index 0000000..788e164 Binary files /dev/null and b/Pac-Man (USA) (Namco)/rom.nes differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..6fc6398 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Disassembly Project 1 + +This is a set of disassembled NES roms using AI to translate. + +This is not for distribution. +This is not for you. + +## Roms Reproduced + +| Game | Rom | MD5 | Status | +| Balloon Fight | Balloon Fight (U) | c58e5f600884aa7daadc1ea498f1df24 | WIP | +| Excitebike | Excitebike (Japan, USA) | d7fe15cf2bc7b6582c07d12b3cf3bede | Complete | +| Pacman (Namco) | Pac-Man (USA) (Namco) | 3ddca58c51dd38d59f9271d24e7105f9 | Complete | diff --git a/_tools/chr2gif.py b/_tools/chr2gif.py new file mode 100644 index 0000000..36fce56 --- /dev/null +++ b/_tools/chr2gif.py @@ -0,0 +1,991 @@ +#!/usr/bin/env python3 + +import argparse +import math +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFont + + +# ============================================================================= +# NES CHR constants +# ============================================================================= + +CHR_BANK_SIZE = 0x2000 +PATTERN_TABLE_SIZE = 0x1000 + +TILE_SIZE_BYTES = 16 +TILE_WIDTH = 8 +TILE_HEIGHT = 8 + +TILES_PER_PATTERN_TABLE = 256 + + +# ============================================================================= +# NES RGB palette +# +# Common RGB approximation of the NES 64-color system palette. +# CHR-ROM itself does NOT store these colors. CHR pixels contain values 0-3, +# which are mapped through NES palette RAM into one of these 64 colors. +# ============================================================================= + +NES_RGB = [ + # $00-$0F + (84, 84, 84), + (0, 30, 116), + (8, 16, 144), + (48, 0, 136), + (68, 0, 100), + (92, 0, 48), + (84, 4, 0), + (60, 24, 0), + (32, 42, 0), + (8, 58, 0), + (0, 64, 0), + (0, 60, 0), + (0, 50, 60), + (0, 0, 0), + (0, 0, 0), + (0, 0, 0), + + # $10-$1F + (152, 150, 152), + (8, 76, 196), + (48, 50, 236), + (92, 30, 228), + (136, 20, 176), + (160, 20, 100), + (152, 34, 32), + (120, 60, 0), + (84, 90, 0), + (40, 114, 0), + (8, 124, 0), + (0, 118, 40), + (0, 102, 120), + (0, 0, 0), + (0, 0, 0), + (0, 0, 0), + + # $20-$2F + (236, 238, 236), + (76, 154, 236), + (120, 124, 236), + (176, 98, 236), + (228, 84, 236), + (236, 88, 180), + (236, 106, 100), + (212, 136, 32), + (160, 170, 0), + (116, 196, 0), + (76, 208, 32), + (56, 204, 108), + (56, 180, 204), + (60, 60, 60), + (0, 0, 0), + (0, 0, 0), + + # $30-$3F + (236, 238, 236), + (168, 204, 236), + (188, 188, 236), + (212, 178, 236), + (236, 174, 236), + (236, 174, 212), + (236, 180, 176), + (228, 196, 144), + (204, 210, 120), + (180, 222, 120), + (168, 226, 144), + (152, 226, 180), + (160, 214, 228), + (160, 162, 160), + (0, 0, 0), + (0, 0, 0), +] + + +GRAYSCALE = [ + (0, 0, 0), + (85, 85, 85), + (170, 170, 170), + (255, 255, 255), +] + + +# ============================================================================= +# CHR decoder +# ============================================================================= + +def decode_tile(chr_data, tile_index): + """ + Decode one NES 8x8 2bpp CHR tile. + + Each tile occupies 16 bytes: + + bytes 0-7 bitplane 0 + bytes 8-15 bitplane 1 + + Each resulting pixel has a value from 0 through 3. + """ + + offset = tile_index * TILE_SIZE_BYTES + tile = chr_data[offset:offset + TILE_SIZE_BYTES] + + if len(tile) != TILE_SIZE_BYTES: + raise ValueError( + f"Incomplete CHR tile ${tile_index:03X}" + ) + + pixels = [] + + for y in range(TILE_HEIGHT): + plane0 = tile[y] + plane1 = tile[y + 8] + + row = [] + + for x in range(TILE_WIDTH): + bit = 7 - x + + low = (plane0 >> bit) & 1 + high = (plane1 >> bit) & 1 + + pixel = low | (high << 1) + + row.append(pixel) + + pixels.append(row) + + return pixels + + +# ============================================================================= +# Palette handling +# ============================================================================= + +def nes_palette_to_rgb(indices): + """ + Convert four NES palette indices into RGB colors. + """ + + if len(indices) != 4: + raise ValueError( + "Palette must contain exactly four NES colors" + ) + + return [ + NES_RGB[index & 0x3F] + for index in indices + ] + + +def parse_hex_byte(value): + """ + Parse a command-line hexadecimal byte. + + Accepted examples: + + 0F + FF + 0x20 + $26 + """ + + value = value.strip() + + if value.startswith("$"): + value = value[1:] + + if value.lower().startswith("0x"): + value = value[2:] + + number = int(value, 16) + + if not 0 <= number <= 0xFF: + raise argparse.ArgumentTypeError( + f"Value out of byte range: {value}" + ) + + return number + + +# ============================================================================= +# Tile rendering +# ============================================================================= + +def make_tile_image(tile, scale, colors=None): + """ + Render one decoded 8x8 tile. + + colors[0..3] maps CHR pixel values 0..3 to RGB colors. + """ + + if colors is None: + colors = GRAYSCALE + + image = Image.new( + "RGB", + (TILE_WIDTH, TILE_HEIGHT), + colors[0] + ) + + pixels = image.load() + + for y in range(TILE_HEIGHT): + for x in range(TILE_WIDTH): + pixels[x, y] = colors[tile[y][x]] + + if scale != 1: + image = image.resize( + ( + TILE_WIDTH * scale, + TILE_HEIGHT * scale + ), + Image.Resampling.NEAREST + ) + + return image + + +# ============================================================================= +# Clean sprite sheet +# ============================================================================= + +def make_clean_sheet( + chr_data, + columns=16, + scale=4, + colors=None +): + """ + Render CHR tiles as a compact sprite sheet. + """ + + tile_count = len(chr_data) // TILE_SIZE_BYTES + rows = math.ceil(tile_count / columns) + + rendered_width = TILE_WIDTH * scale + rendered_height = TILE_HEIGHT * scale + + sheet = Image.new( + "RGB", + ( + columns * rendered_width, + rows * rendered_height + ), + (0, 0, 0) + ) + + for tile_index in range(tile_count): + tile = decode_tile( + chr_data, + tile_index + ) + + tile_image = make_tile_image( + tile, + scale, + colors + ) + + column = tile_index % columns + row = tile_index // columns + + x = column * rendered_width + y = row * rendered_height + + sheet.paste( + tile_image, + (x, y) + ) + + return sheet + + +# ============================================================================= +# Labeled sprite sheet +# ============================================================================= + +def make_labeled_sheet( + chr_data, + columns=16, + tile_scale=4, + label_height=12, + tile_base=0, + colors=None +): + """ + Render a debugging sheet with each tile labeled by hexadecimal tile ID. + """ + + tile_count = len(chr_data) // TILE_SIZE_BYTES + rows = math.ceil(tile_count / columns) + + rendered_width = TILE_WIDTH * tile_scale + rendered_height = TILE_HEIGHT * tile_scale + + cell_width = max( + rendered_width + 4, + 40 + ) + + cell_height = ( + rendered_height + + label_height + + 6 + ) + + sheet = Image.new( + "RGB", + ( + columns * cell_width, + rows * cell_height + ), + (24, 24, 24) + ) + + draw = ImageDraw.Draw(sheet) + font = ImageFont.load_default() + + for tile_index in range(tile_count): + tile = decode_tile( + chr_data, + tile_index + ) + + tile_image = make_tile_image( + tile, + tile_scale, + colors + ) + + column = tile_index % columns + row = tile_index // columns + + cell_x = column * cell_width + cell_y = row * cell_height + + tile_x = ( + cell_x + + (cell_width - rendered_width) // 2 + ) + + tile_y = cell_y + 2 + + sheet.paste( + tile_image, + (tile_x, tile_y) + ) + + display_index = tile_base + tile_index + + label = f"{display_index:03X}" + + bbox = draw.textbbox( + (0, 0), + label, + font=font + ) + + text_width = bbox[2] - bbox[0] + + text_x = ( + cell_x + + (cell_width - text_width) // 2 + ) + + text_y = ( + cell_y + + rendered_height + + 4 + ) + + draw.text( + (text_x, text_y), + label, + fill=(255, 255, 255), + font=font + ) + + return sheet + + +# ============================================================================= +# iNES extraction +# ============================================================================= + +def extract_chr_banks_from_ines(rom): + """ + Extract every 8 KiB CHR-ROM bank from an iNES ROM. + """ + + if len(rom) < 16: + raise ValueError( + "File is too small to contain an iNES header" + ) + + if rom[:4] != b"NES\x1A": + raise ValueError( + "Input does not contain a valid iNES header" + ) + + prg_banks = rom[4] + chr_banks = rom[5] + flags6 = rom[6] + + if chr_banks == 0: + raise ValueError( + "ROM contains no CHR-ROM. " + "This cartridge may use CHR-RAM." + ) + + trainer_size = ( + 512 + if flags6 & 0x04 + else 0 + ) + + prg_size = ( + prg_banks * 0x4000 + ) + + chr_offset = ( + 16 + + trainer_size + + prg_size + ) + + banks = [] + + for bank_index in range(chr_banks): + + start = ( + chr_offset + + bank_index * CHR_BANK_SIZE + ) + + end = ( + start + + CHR_BANK_SIZE + ) + + if end > len(rom): + raise ValueError( + f"ROM truncated while reading " + f"CHR bank ${bank_index:02X}" + ) + + banks.append( + rom[start:end] + ) + + return banks + + +# ============================================================================= +# Raw CHR extraction +# ============================================================================= + +def extract_raw_chr_banks(data): + """ + Split raw CHR data into 8 KiB banks. + """ + + if len(data) % CHR_BANK_SIZE != 0: + raise ValueError( + "Raw CHR data size must be a multiple " + f"of ${CHR_BANK_SIZE:04X} bytes" + ) + + banks = [] + + for offset in range( + 0, + len(data), + CHR_BANK_SIZE + ): + banks.append( + data[ + offset: + offset + CHR_BANK_SIZE + ] + ) + + return banks + + +# ============================================================================= +# GIF output +# ============================================================================= + +def save_gif(image, filename): + """ + Save a static GIF. + """ + + image.save( + filename, + format="GIF", + optimize=False + ) + + print( + f" {filename} " + f"({image.width}x{image.height})" + ) + + +# ============================================================================= +# Pattern table generation +# ============================================================================= + +def process_pattern_table( + pattern_data, + output_prefix, + bank_index, + table_index, + scale, + columns, + colors, + suffix="" +): + """ + Produce clean and labeled sheets for a 4 KiB pattern table. + """ + + tile_base = ( + table_index * 0x100 + ) + + base_name = ( + f"{output_prefix}" + f"_chr{bank_index:02X}" + f"_pt{table_index}" + f"{suffix}" + ) + + clean = make_clean_sheet( + pattern_data, + columns=columns, + scale=scale, + colors=colors + ) + + save_gif( + clean, + f"{base_name}.gif" + ) + + labeled = make_labeled_sheet( + pattern_data, + columns=columns, + tile_scale=scale, + tile_base=tile_base, + colors=colors + ) + + save_gif( + labeled, + f"{base_name}_labels.gif" + ) + + +# ============================================================================= +# Full CHR bank rendering +# ============================================================================= + +def process_chr_bank( + bank, + bank_index, + output_prefix, + scale, + columns, + colors, + suffix="" +): + """ + Generate: + full 8 KiB CHR bank + pattern table 0 + pattern table 1 + labeled pattern table 0 + labeled pattern table 1 + """ + + full_name = ( + f"{output_prefix}" + f"_chr{bank_index:02X}" + f"_full" + f"{suffix}" + f".gif" + ) + + full_sheet = make_clean_sheet( + bank, + columns=columns, + scale=scale, + colors=colors + ) + + save_gif( + full_sheet, + full_name + ) + + pattern0 = bank[ + 0x0000: + 0x1000 + ] + + pattern1 = bank[ + 0x1000: + 0x2000 + ] + + process_pattern_table( + pattern0, + output_prefix, + bank_index, + 0, + scale, + columns, + colors, + suffix + ) + + process_pattern_table( + pattern1, + output_prefix, + bank_index, + 1, + scale, + columns, + colors, + suffix + ) + + +# ============================================================================= +# Full 32-byte palette RAM mode +# ============================================================================= + +def process_palette_ram( + banks, + palette_ram, + output_prefix, + scale, + columns +): + """ + Render each CHR bank through all four background palettes and all four + sprite palettes contained in a 32-byte NES palette-RAM image. + """ + + if len(palette_ram) != 32: + raise ValueError( + "Palette RAM must contain exactly 32 bytes" + ) + + print() + print("Generating background palette sheets") + + for palette_number in range(4): + + start = palette_number * 4 + + palette_indices = ( + palette_ram[ + start: + start + 4 + ] + ) + + colors = nes_palette_to_rgb( + palette_indices + ) + + suffix = ( + f"_bg{palette_number}" + ) + + print() + print( + f" BG{palette_number}: " + + " ".join( + f"${value:02X}" + for value + in palette_indices + ) + ) + + for bank_index, bank in enumerate(banks): + process_chr_bank( + bank, + bank_index, + output_prefix, + scale, + columns, + colors, + suffix + ) + + print() + print("Generating sprite palette sheets") + + for palette_number in range(4): + + start = ( + 0x10 + + palette_number * 4 + ) + + palette_indices = ( + palette_ram[ + start: + start + 4 + ] + ) + + colors = nes_palette_to_rgb( + palette_indices + ) + + suffix = ( + f"_sp{palette_number}" + ) + + print() + print( + f" SP{palette_number}: " + + " ".join( + f"${value:02X}" + for value + in palette_indices + ) + ) + + for bank_index, bank in enumerate(banks): + process_chr_bank( + bank, + bank_index, + output_prefix, + scale, + columns, + colors, + suffix + ) + + +# ============================================================================= +# Main +# ============================================================================= + +def main(): + + parser = argparse.ArgumentParser( + description=( + "NES CHR-ROM GIF visualizer " + "for reverse engineering" + ) + ) + + parser.add_argument( + "input", + help="iNES ROM or raw CHR binary" + ) + + parser.add_argument( + "--output-prefix", + default=None, + help="Output filename prefix" + ) + + parser.add_argument( + "--raw", + action="store_true", + help="Treat input as raw CHR data" + ) + + parser.add_argument( + "--scale", + type=int, + default=4, + help="Tile scale factor (default: 4)" + ) + + parser.add_argument( + "--columns", + type=int, + default=16, + help="Tiles per row (default: 16)" + ) + + parser.add_argument( + "--palette", + nargs=4, + type=parse_hex_byte, + metavar=("C0", "C1", "C2", "C3"), + help=( + "Render using one NES four-color palette. " + "Example: --palette 0F 20 0F 06" + ) + ) + + parser.add_argument( + "--palette-ram", + nargs=32, + type=parse_hex_byte, + metavar="XX", + help=( + "Complete 32-byte NES palette RAM image. " + "Generates output for all four BG and " + "all four sprite palettes." + ) + ) + + args = parser.parse_args() + + if args.scale < 1: + parser.error( + "--scale must be at least 1" + ) + + if args.columns < 1: + parser.error( + "--columns must be at least 1" + ) + + if ( + args.palette is not None and + args.palette_ram is not None + ): + parser.error( + "--palette and --palette-ram " + "cannot be used together" + ) + + input_path = Path( + args.input + ) + + data = input_path.read_bytes() + + if args.output_prefix: + output_prefix = ( + args.output_prefix + ) + else: + output_prefix = ( + input_path.stem + ) + + # ------------------------------------------------------------------------- + # Extract CHR + # ------------------------------------------------------------------------- + + if args.raw: + + banks = extract_raw_chr_banks( + data + ) + + elif data[:4] == b"NES\x1A": + + banks = extract_chr_banks_from_ines( + data + ) + + else: + + print( + "No iNES header detected. " + "Treating input as raw CHR." + ) + + banks = extract_raw_chr_banks( + data + ) + + print( + f"Input: {input_path}" + ) + + print( + f"CHR banks: {len(banks)}" + ) + + # ------------------------------------------------------------------------- + # Full palette RAM mode + # ------------------------------------------------------------------------- + + if args.palette_ram is not None: + + process_palette_ram( + banks, + args.palette_ram, + output_prefix, + args.scale, + args.columns + ) + + return + + # ------------------------------------------------------------------------- + # Single-palette / grayscale mode + # ------------------------------------------------------------------------- + + if args.palette is not None: + + colors = nes_palette_to_rgb( + args.palette + ) + + suffix = "_color" + + print( + "Palette: " + + " ".join( + f"${value:02X}" + for value + in args.palette + ) + ) + + else: + + colors = GRAYSCALE + suffix = "" + + print( + "Palette: grayscale CHR values" + ) + + for bank_index, bank in enumerate(banks): + + print() + print( + f"CHR bank ${bank_index:02X}" + ) + + print( + f" Size: ${len(bank):04X}" + ) + + print( + f" Tiles: " + f"{len(bank) // TILE_SIZE_BYTES}" + ) + + process_chr_bank( + bank, + bank_index, + output_prefix, + args.scale, + args.columns, + colors, + suffix + ) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/_wip/Balloon Fight (JU)/balloon.nes b/_wip/Balloon Fight (JU)/balloon.nes new file mode 100644 index 0000000..21af62f Binary files /dev/null and b/_wip/Balloon Fight (JU)/balloon.nes differ diff --git a/_wip/Balloon Fight (JU)/balloon.txt b/_wip/Balloon Fight (JU)/balloon.txt new file mode 100644 index 0000000..21af62f Binary files /dev/null and b/_wip/Balloon Fight (JU)/balloon.txt differ diff --git a/_wip/Balloon Fight (JU)/copilot/Makefile b/_wip/Balloon Fight (JU)/copilot/Makefile new file mode 100644 index 0000000..3f9ba6f --- /dev/null +++ b/_wip/Balloon Fight (JU)/copilot/Makefile @@ -0,0 +1,10 @@ +all: reconstructed_prg.bin verify +prg.o: prg.s symbols.inc prg.bin + ca65 -g -o $@ prg.s +reconstructed_prg.bin: prg.o linker.cfg + ld65 -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o $@ prg.o +verify: reconstructed_prg.bin prg.bin + cmp reconstructed_prg.bin prg.bin + @echo BYTE-IDENTICAL +clean: + rm -f prg.o reconstructed_prg.bin reconstructed.map reconstructed.lbl diff --git a/_wip/Balloon Fight (JU)/copilot/linker.cfg b/_wip/Balloon Fight (JU)/copilot/linker.cfg new file mode 100644 index 0000000..d9d421d --- /dev/null +++ b/_wip/Balloon Fight (JU)/copilot/linker.cfg @@ -0,0 +1,6 @@ +MEMORY { + PRG: start = $C000, size = $4000, file = %O, fill = yes; +} +SEGMENTS { + PRG: load = PRG, type = ro; +} diff --git a/_wip/Balloon Fight (JU)/copilot/prg.s b/_wip/Balloon Fight (JU)/copilot/prg.s new file mode 100644 index 0000000..915603d --- /dev/null +++ b/_wip/Balloon Fight (JU)/copilot/prg.s @@ -0,0 +1,14112 @@ +; 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 + + +; Variables +PpuControlShadow = $10 +PpuMaskShadow = $11 +Controller1Held = $14 +Controller1Pressed = $15 +Controller2Held = $16 +Controller2Pressed = $17 +ControllerState = Controller1Held +NmiDispatchMode = $40 +NmiState = $41 +MenuSelectionIndex = $42 +NmiSequenceStep = $44 +NmiSyncState = $48 +InputActionLatch = $49 +RiderMotionState = $A4 +OamBuffer = $0200 +PersistentRecord0 = $0580 +PersistentRecord1 = $05A3 +ResetMagic1 = $05CE +ResetMagic2 = $05CF +PersistentBuffer = $06E0 +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 +MENU_HORIZONTAL_COUNT=$05 +MENU_VERTICAL_COUNT=$06 +MENU_ACTION_NONE=$00 +MENU_ACTION_ACCEPTED=$04 +NMI_SYNC_PENDING=$FF +NMI_SYNC_COMPLETE=$01 +RESET_MAGIC_1=$A5 +RESET_MAGIC_2=$5A +PPUCTRL_NMI_ENABLE=$80 + +WorkingBuffer = $05E0 +PERSISTENT_BUFFER_SIZE = $0100 + +StateTargetCA85 = $CA85 + +; Serial I/O helpers using JOY1 (verified structure; peripheral identity unknown) +WriteSerialTiming = $C62D +WaitSerialPulse = $C639 +TransmitPulseShort = $C64E +TransmitPulseLong = $C653 +TransmitByte = $C66E +IncrementSerialCount = $C68A +ReceiveSerialBit = $C691 +ReceiveByte = $C6A5 + +; Block protocol helpers +TransmitRuntimeData = $C6C8 +TransmitDataBlock = $C6D1 +SetupTransmitRamBlock = $C716 +SetupTransmitWorkingBuffer = $C726 +SetTransmitPreambleCounts = $C734 +ReceiveRuntimeData = $C739 +ReceiveDataBlock = $C792 +SerialBlockLength = $0D +SerialBlockPtr = $0E +SerialBitCountLo = $08 +SerialBitCountHi = $09 + +; App Specific Variables +TMP00 = $00 +TMP01 = $01 + + +; App Specific Magic Values +RIDER_MOTION_DISABLED = $01 +RIDER_MOTION_ACTIVE = $02 +RIDER_MOTION_TRANSITION = $03 +RIDER_MOTION_COMPLETE = $04 + + +.segment "PRG" + +; --------------------------------------------------------------------------- +; $C000-$C01B +; NMI state handler pointer table. +; 14 little-endian handler addresses, one for each NMI state $00-$0D. +; --------------------------------------------------------------------------- + +NmiStateHandlerTable: + .word NmiState00 ; state $00 + .word $C395 ; state $01 + .word UpdateGameplayRuntime ; state $02 + .word DispatchNmiSequenceStep ; state $03 + .word NmiState04_07 ; state $04 + .word UpdateGameplayRuntime ; state $05 + .word DispatchNmiSequenceStep ; state $06 + .word NmiState04_07 ; state $07 + .word $C3BD ; state $08 + .word UpdateRaceRuntime ; state $09 + .word UpdateGameplayRuntime ; state $0A + .word DispatchNmiSequenceStep ; state $0B + .word $C43D ; state $0C + .word $C41C ; state $0D + +; --------------------------------------------------------------------------- +; $C01C-$C029 +; NMI state -> sequence index. +; Indexed directly by NmiState. +; --------------------------------------------------------------------------- + +NmiStateToSequenceIndex: + .byte $00, $01, $02, $03 + .byte $04, $02, $03, $04 + .byte $06, $05, $02, $03 + .byte $07, $08 + +; --------------------------------------------------------------------------- +; $C02A-$C032 +; Low bytes of nine NMI sequence pointers. +; --------------------------------------------------------------------------- +NmiSequencePointerLow: + .byte $3C, $40, $46, $52, $5C + .byte $64, $70, $7A, $76 + +; --------------------------------------------------------------------------- +; $C033-$C03B +; High bytes of nine NMI sequence pointers. +; +; All nine streams currently reside in page $C0. +; --------------------------------------------------------------------------- + +NmiSequencePointerHigh: + .byte $C0, $C0, $C0, $C0, $C0 + .byte $C0, $C0, $C0, $C0 + + +; --------------------------------------------------------------------------- +; $C03C-$C07F +; NMI sequence handler streams. +; +; These are selected through NmiSequencePointerLow / +; NmiSequencePointerHigh. +; --------------------------------------------------------------------------- + +; Sequence index 0 +; $C03C-$C03F +NmiSequence0C03C: + .word $C2BD + .word $C514 + +; Sequence index 1 +; $C040-$C045 +NmiSequence1C040: + .word SetSerialUpdateSelector06 + .word $C3F3 + .word $C514 + + +; Sequence index 2 +; $C046-$C051 +NmiSequence2C046: + .word SerialUpdateSelectorCluster + .word ClearRuntimeZeroPage + .word InitializeCourseRuntime + .word SetSerialUpdateSelector17 + .word SetSerialUpdateSelector0E + .word InitializeGameRuntime + + +; Sequence index 3 +; $C052-$C05B +NmiSequence3C052: + .word SetSerialUpdateSelector06 + .word SetSerialUpdateSelector04 + .word SelectNmiUpdateForState + .word CallC92CWithZero + .word CompareAndUpdateRecord + + +; Sequence index 4 +; $C05C-$C063 +NmiSequence4C05C: + .word SetSerialUpdateSelector06 + .word SetSerialUpdateSelector09 + .word Prepare0300Structure + .word $C514 + + +; Sequence index 5 +; $C064-$C06F +NmiSequence5C064: + .word $C551 + .word ClearRuntimeZeroPage + .word InitializeCourseRuntime + .word SetSerialUpdateSelector17 + .word $C562 + .word $C5C2 + + +; Sequence index 6 +; $C070-$C075 +NmiSequence6C070: + .word SetSerialUpdateSelector06 + .word $C575 + .word $C514 + + +; Sequence index 8 +; $C076-$C079 +; +; Note that sequence index 8 points here, while index 7 points to $C07A. +NmiSequence8C076: + .word $C582 + .word $C518 + + +; Sequence index 7 +; $C07A-$C07F +NmiSequence7C07A: + .word ClearRuntimeZeroPage + .word RestoreWorkingRecordFields + .word $C518 + + +; --------------------------------------------------------------------------- +; $C080-$C082 +; Initial/menu Y positions. +; Indexed by menu selection. +; --------------------------------------------------------------------------- + +MenuInitialYTable: + .byte $80, $90, $A0 + +; --------------------------------------------------------------------------- +; $C083-$C087 +; Horizontal menu X positions. +; Five entries. +; --------------------------------------------------------------------------- + +MenuHorizontalXTableC083: + .byte $48, $60, $78, $90, $A8 + + +; --------------------------------------------------------------------------- +; $C088-$C08A +; Initial sprite X positions. +; Used by InitializeSprite0. +; --------------------------------------------------------------------------- + +Sprite0InitialXTableC088: + .byte $50, $48, $4C + +; --------------------------------------------------------------------------- +; $C08B-$C08F +; Vertical menu Y positions. +; Five entries. +; --------------------------------------------------------------------------- + +MenuVerticalYTableC08B: + .byte $30, $48, $78, $90, $A8 + +; --------------------------------------------------------------------------- +; $C090 +; Adjacent parameter preceding the persistent-record template. +; Purpose not yet established. +; --------------------------------------------------------------------------- + +DataC090: + .byte $C0 + + +; --------------------------------------------------------------------------- +; $C091-$C0AF +; Persistent-record initialization template. +; +; Copied into both persistent record buffers by InitializePersistentRam. +; 31 bytes total. +; --------------------------------------------------------------------------- + +PersistentRecordInitialDataC091: + .byte $01, $10, $00 + .byte $01, $10, $00 + .byte $01, $10, $00 + .byte $01, $0C, $00 + .byte $01, $06, $00 + .byte $00, $01, $18, $00 + .byte $01, $16, $00 + .byte $01, $0A, $00 + .byte $01, $14, $00 + .byte $01, $06, $00 + +; --------------------------------------------------------------------------- +; $C0B0-$C0D7 +; Parameter data with several overlapping indexed views. +; --------------------------------------------------------------------------- + +DataC0B0: + .byte $09, $0B, $0D, $0F + +DataC0B4: + .byte $0E, $1A, $26, $32 + +DataC0B8: + .byte $38, $48, $58, $68 + +; $C0BC +; Added to $90,X by the CE29 position helper. +SlotPositionAddTableC0BC: + .byte $18, $3F, $28, $20 + +; $C0C0 +DataC0C0: + .byte $28, $38, $0C, $00 + +; $C0C4 +DataC0C4: + .byte $3C, $1C, $C0, $7F + +; $C0C8 +; Indexed by ($B0,X >> 1), compared against $AC,X. +SlotACTargetTableC0C8: + .byte $06, $02 + +; $C0CA +; Alternate $AC,X target view. +SlotACAlternateTargetTable: + .byte $0A, $0B + +; $C0CC +; Indexed by ($B0,X >> 1), compared against $90,X. +SlotPositionThresholdTable: + .byte $01, $B0 + +; $C0CE +; Paired with $C0D1 by the CE29 boundary helper. +; Also used as the $20 BIT mask by UpdateMenuSelection. +Slot90BoundaryTable: + .byte $20, $40, $7F + +; $C0D1 +; Paired with $Slot90BoundaryTable and loaded into $94,X. +Slot94BoundaryTable: + .byte $03, $03, $01 + + +; $C0D4 +; Indexed by ($B0,X >> 1) and stored into $26,X. +SlotTimerReloadTable: + .byte $06, $04, $40, $58 + +; --------------------------------------------------------------------------- +; $C0D8-$C0D9 +; Currently unidentified adjacent data. +; --------------------------------------------------------------------------- + +DataC0D8: + .byte $48, $48 + +; --------------------------------------------------------------------------- +; $C0DA-$C0DD +; Four-entry sequence position/setup table. +; Used by StateSequenceStep2Process and SequenceSetupLoadIndexedParameters. +; --------------------------------------------------------------------------- +SequencePositionTable: + .byte $78, $70, $80, $B0 +; --------------------------------------------------------------------------- +; $C0DE-$C0E1 +; Four-entry sequence limit/parameter table. +; --------------------------------------------------------------------------- +SequenceLimitTable: + .byte $37, $3F, $3F, $47 +; --------------------------------------------------------------------------- +; $C0E2-$C0E4 +; Three-entry parameter table loaded into $02. +; --------------------------------------------------------------------------- +SequenceParameter02Table: + .byte $B7, $B9, $B9 + + +; --------------------------------------------------------------------------- +; $C0E5-$C0E7 +; Three-entry flag/configuration table loaded into $0B. +; --------------------------------------------------------------------------- + +SequenceFlagsTable: + .byte $01, $01, $41 + + +; --------------------------------------------------------------------------- +; $C0E8-$C0EB +; Four-entry structure offset table. +; --------------------------------------------------------------------------- + +SequencePointerOffsetTable: + .byte $04, $0C, $14, $1C + +; --------------------------------------------------------------------------- +; $C0EC-$C0F5 +; Indexed parameter data. +; +; $C0EC and $C0ED are both used as indexed table bases. +; Keep the physical bytes literal until their complete index ranges and +; semantics are established. +; --------------------------------------------------------------------------- + +IndexedParameterTableC0EC: + .byte $21 + +; Secondary overlapping indexed view at $C0ED. +IndexedParameterTableC0ED: + .byte $F2, $23, $43, $22, $32, $22, $8C, $24, $CF + + +; --------------------------------------------------------------------------- +; $C0F6-$C110 +; Low bytes of 27 PPU update-stream pointers. +; --------------------------------------------------------------------------- + +PpuUpdatePointerLow: + .byte $01, $70, $4B, $D4, $17, $4A, $3A, $9E + .byte $B4, $96, $C8, $DE, $EF, $05, $24, $FA + .byte $23, $14, $D6, $E9, $FC, $0F, $22, $35 + .byte $50, $31, $A1 + + +; --------------------------------------------------------------------------- +; $C111-$C12B +; High bytes of 27 PPU update-stream pointers. +; --------------------------------------------------------------------------- + +PpuUpdatePointerHigh: + .byte $03, $D4, $D6, $D6, $D7, $D8, $D6, $D5 + .byte $D5, $D7, $D5, $D5, $D5, $D6, $D5, $D7 + .byte $D6, $D6, $D3, $D3, $D3, $D4, $D4, $D4 + .byte $D4, $D6, $00 + +; --------------------------------------------------------------------------- +; $C12C-$C12F +; Low bytes of four NMI sequence-step dispatch targets. +; --------------------------------------------------------------------------- + +NmiSequenceStepPointerLow: + .byte $3B, $77, $A4, $6B + + +; --------------------------------------------------------------------------- +; $C130-$C133 +; High bytes of four NMI sequence-step dispatch targets. +; --------------------------------------------------------------------------- + +NmiSequenceStepPointerHigh: + .byte $CB, $CB, $CB, $CC + +; --------------------------------------------------------------------------- +; $C134-$C13B +; Eight-byte template copied into the $0301 structure by +; StateSequenceStep2CopyTable. +; --------------------------------------------------------------------------- + +StateSequenceStep2Template: + .byte $00, $FC, $FC, $FC + .byte $79, $04, $72, $22 + + +; --------------------------------------------------------------------------- +; $C13C-$C141 +; Six-byte template used by Prepare0300Structure. +; --------------------------------------------------------------------------- + +Prepare0300StructureTemplateC13C: + .byte $04, $21, $6F, $01, $00, $00 + + +; --------------------------------------------------------------------------- +; $C142-$C146 +; Five bytes of currently unidentified parameter data. +; Keep literal until its consumer is established. +; --------------------------------------------------------------------------- + +DataC142: + .byte $60, $9F, $00, $17, $04 + + +; --------------------------------------------------------------------------- +; $C147-$C14F +; Nine-entry table used by Initialize02C0Entries. +; X runs from 8 down through 0. +; --------------------------------------------------------------------------- + +Initialize02C0EntryTableC147: + .byte $DE, $DC, $E1 + .byte $F8, $FC, $EF + .byte $E1, $EE, $C9 + + +; --------------------------------------------------------------------------- +; $C150-$C157 +; Eight-byte template used by Copy02C0Template. +; X runs from 7 down through 0. +; --------------------------------------------------------------------------- + +Copy02C0TemplateC150: + .byte $44, $C9, $00, $84 + .byte $44, $F9, $00, $74 + + +; --------------------------------------------------------------------------- +; $C158-$C15F +; Currently unidentified data. +; --------------------------------------------------------------------------- + +DataC158: + .byte $07, $23, $78, $04 + .byte $1D, $F8, $FE, $FE + + +; --------------------------------------------------------------------------- +; $C160 +; Indexed by $43 and stored into $0315. +; Exact table extent remains to be established. +; --------------------------------------------------------------------------- + +State0315TableC160: + .byte $29, $01, $02, $00, $02, $29 + + +; --------------------------------------------------------------------------- +; $C166-$C169 +; Four-entry table indexed by (($3F >> 2) & 3). +; Stored into $0316. +; --------------------------------------------------------------------------- + +State0316TableC166: + .byte $30, $35, $25, $35 + + +; --------------------------------------------------------------------------- +; $C16A +; Indexed by $43 and stored into $0317. +; Exact table extent remains to be established. +; --------------------------------------------------------------------------- + +State0317TableC16A: + .byte $22, $26, $1C, $22, $22, $22 + +; --------------------------------------------------------------------------- +; $C170-$C173 +; Four-entry table indexed by (($3F >> 2) & 3). +; Stored into $0318. +; --------------------------------------------------------------------------- + +State0318TableC170: + .byte $26, $06, $36, $26 + + +; --------------------------------------------------------------------------- +; $C174-$C177 +; Currently unidentified adjacent data. +; --------------------------------------------------------------------------- + +DataC174: + .byte $01, $40, $7F, $09 + + +; --------------------------------------------------------------------------- +; $C178-$C17F +; Eight-byte runtime/OAM template copied to $0228-$022F. +; +; Structurally this is two four-byte OAM entries: +; +; $C7, $DB, $00, $E0 +; $C7, $DD, $00, $E8 +; --------------------------------------------------------------------------- +RuntimeOamTemplateC178: + .byte $C7, $DB, $00, $E0 + .byte $C7, $DD, $00 + +; $C17F +; The final byte belongs to the OAM template, but $C17F is also used as +; the base for the receive-header comparison with Y = 1..4. +ReceiveHeaderTableBaseC17F: + .byte $E8 + +; --------------------------------------------------------------------------- +; $C180-$C183 +; Expected four-byte header used by ReceiveRuntimeDataCode. +; --------------------------------------------------------------------------- +ExpectedReceiveHeaderC180: + .byte $17, $01, $00, $01 + +Reset: + sei + cld + lda #$00 + sta PPUCTRL + ldx #$FF + txs +@vblank1: lda PPUSTATUS + and #$80 + beq @vblank1 +@vblank2: lda PPUSTATUS + and #$80 + beq @vblank2 + ldy #$07 + lda ResetMagic1 + cmp #RESET_MAGIC_1 + bne @clear_select + lda ResetMagic2 + cmp #RESET_MAGIC_2 + bne @clear_select + ldy #$04 +@clear_select: + sty $01 + ldy #$00 + sty $00 + tya +@clear_ram: + sta ($00),y + dey + bne @clear_ram + dec $01 + bpl @clear_ram + lda ResetMagic1 + bne :+ + jsr InitializePersistentRam +: sta APUDMALOAD + lda #$06 + sta PPUMASK + sta $18 + jsr InitializeRuntimePpuState + lda #$90 + jsr SetPpuControl +@main: + jsr ShiftRuntimeState + lda NmiSyncState + bmi @main + beq @main + jsr DisableNmi + lda NmiSyncState + cmp #NMI_SYNC_COMPLETE + bne @alternate + jsr TransmitRuntimeDataCode +@finish: + lda #NMI_SYNC_PENDING + sta NmiSyncState + jsr WaitForVblankEndAndEnableNmi + jmp @main +@alternate: + jsr ReceiveRuntimeDataCode + jmp @finish + +InitializePersistentRam: + ldx #$1E +@copy: + lda PersistentRecordInitialDataC091,x + sta PersistentRecord0,x + sta PersistentRecord1,x + dex + bpl @copy + ldx #$01 + stx PersistentBuffer +@buf: + lda #$40 + sta PersistentBuffer,x + inx + cpx #$CF + bcc @buf + lda #$09 + sta PersistentBuffer,x + lda #RESET_MAGIC_1 + sta ResetMagic1 + lda #RESET_MAGIC_2 + sta ResetMagic2 + ldx #$00 + lda #$0A +@tails: + sta PersistentRecord0+$20,x + sta PersistentRecord1+$20,x + lda #$00 + inx + cpx #$03 + bcc @tails + rts + +; NMI handler $C23B-$C27E. +NMI: + jsr DisableNmi + lda $45 + bne @skip_mask + lda PpuMaskShadow + ora #$1A + sta PPUMASK + sta PpuMaskShadow +@skip_mask: + lda #$00 + sta OAMADDR + lda #$02 + sta OAMDMA + jsr NmiPpuUpdate + lda $47 + beq @skip_optional + jsr UpdateRiderLanePositionStates + jsr NmiRuntimeUpdate +@skip_optional: + lda $03B1 + ora $03B2 + bne @skip_timed + inc $3F + jsr UpdateRuntimeCounters +@skip_timed: + jsr ReadControllers + jsr UpdateAudio + jsr DispatchNmiState + lda PPUSTATUS + jsr WaitForVblankEndAndEnableNmi + rti + +NmiPpuUpdate: + ldx $45 +; $C289? +ProcessPpuUpdateByIndex: + lda PpuUpdatePointerLow,x + sta $00 + lda PpuUpdatePointerHigh,x + sta $01 + jsr ProcessPpuUpdateStream + lda #$00 + sta $0300 + sta $0301 + sta $45 + lda #$3F + sta PPUADDR + lda #$00 + sta PPUADDR + sta PPUADDR + sta PPUADDR + rts + +; NMI dispatcher $C2A9-$C317. +DispatchNmiState: + ldx NmiDispatchMode + beq @sequence_mode + lda NmiState + asl a + tay + lda NmiStateHandlerTable,y + sta $00 + iny + lda NmiStateHandlerTable,y + ; High byte intentionally remains in A; $CB36 stores it to $01. + jmp $CB36 + + ; Alternate initialization path $C2BD-$C2EC. +@alternate_init: + lda #$04 + sta $03F8 + lsr a + sta $45 + ldy #$00 + jsr $C409 + ldx $03F0 + bne @have_fb + lda #$01 + sta $FB +@have_fb: + cpx #$02 + bmi @advance_index + ldx #$FF +@advance_index: + inx + stx $03F0 + lda #$38 + sta $31 + lda #$0F + sta APUCTRL + lda MenuInitialYTable + sta OamBuffer +@sequence_abort: + rts + +@sequence_mode: + jsr DisablePpuRendering +@wait: + lda $30 + ; Busy state returns through the preceding alternate-path RTS at $C2EC. + bne @sequence_abort + ldx NmiState + lda NmiStateToSequenceIndex,x + tax + lda NmiSequencePointerLow,x + sta $02 + lda NmiSequencePointerHigh,x + sta $03 + ldy NmiSequenceStep + inc NmiSequenceStep + dey + bmi InitializeRuntimePpuState + tya + asl a + tay + lda ($02),y + sta $00 + iny + lda ($02),y + jmp $CB36 + +InitializeRuntimePpuState: + jsr FillOamShadowWithF8 + jsr ProcessTwoIndexedEntries + jsr $D1A9 + sta $12 + sta $50 + sta $13 + sta $51 + sta $FC +ResetRuntimePpuFieldsC32F: + lda #$10 + bne SetPpuControl +DisableNmi: + lda PpuControlShadow + and #$7F +SetPpuControl: + sta PPUCTRL + sta PpuControlShadow + rts +WaitForVblankEndAndEnableNmi: +@wait: lda PPUSTATUS + and #PPUCTRL_NMI_ENABLE + bne @wait + lda PpuControlShadow + ora #PPUCTRL_NMI_ENABLE + bne SetPpuControl + +; State-handler block $C346-$C454. +NmiState00: + lda $31 + bne @select + sta $43 + sta $4A + lda #$0F + sta $03F7 + lda #$02 + sta NmiState + bne @finish_select +@select: + lda #$03 + jsr UpdateMenuSelection + txa + bne @selection_done + sta $03F2 + lda MenuInitialYTable,y + jmp SetSprite0Y +@selection_done: + ldy MenuSelectionIndex + ldx #$00 + stx $03F7 + stx $43 + stx MenuSelectionIndex + inx + stx $03F3 + stx $4A + stx $03F0 + cpy #$02 + bne :+ + ldx #$08 +: stx NmiState + sty $03F2 +@finish_select: + jsr EnterNmiSequenceMode + lda $03F7 + eor #$0F + sta APUCTRL + rts + +NmiState01: + lda Controller1Pressed + and #$F3 + sta Controller1Pressed + and #PAD_HORIZONTAL + asl a + asl a + ora Controller1Pressed + sta Controller1Pressed + lda #MENU_HORIZONTAL_COUNT + jsr UpdateMenuSelection + txa + beq MoveHorizontalMarker + lda MenuSelectionIndex + sta $43 +AdvanceNmiStateAndEnterNmiSequence: + inc NmiState +EnterNmiSequenceMode: + lda #$00 + sta NmiDispatchMode + sta NmiSequenceStep + rts +MoveHorizontalMarker: + lda MenuHorizontalXTableC083,y + bne SetSprite0X + +NmiState08: + lda #MENU_VERTICAL_COUNT + jsr UpdateMenuSelection + txa + beq @move_vertical_marker + lda MenuSelectionIndex + cmp #$02 + beq @enter_sequence + bcs @higher_choice + sta $03F2 + inc NmiState +@enter_sequence: + ; Original bytes at $C3D2 are JMP $C3AF. Keep that exact target. + jmp AdvanceNmiStateAndEnterNmiSequence +@higher_choice: + cmp #MENU_HORIZONTAL_COUNT + beq @choice5 + clc + adc #$09 + sta NmiState + lda #$00 + sta MenuSelectionIndex + beq EnterNmiSequenceMode +@choice5: + lsr a + sta MenuSelectionIndex + jmp ResetRuntimeZeroPagePreserveSelection +@move_vertical_marker: + ldy MenuSelectionIndex + lda MenuVerticalYTableC08B,y +SetSprite0Y: + sta OamBuffer + rts + +InitializeSelectionMarker: + ldx #$04 + stx $FB + dex + stx $45 + ldy #$01 + jsr InitializeSprite0 + sta MenuSelectionIndex + lda #$2D + sta $34 + lda #$B0 + bne SetSprite0Y + +InitializeSprite0: + lda #$FA + sta OamBuffer+1 + lda Sprite0InitialXTableC088,y +SetSprite0X: + sta OamBuffer+3 + lda #$00 + sta OamBuffer+2 + sta $46 + rts + +NmiState0D: + lda #$02 + ldx NmiSequenceStep + beq StateHandshakeComplete + lda NmiSyncState + cmp #NMI_SYNC_PENDING + bne StateHandshakeReturn + ldx #$02 +@copy_record_tails: + lda $65,x + sta PersistentRecord0+$20,x + lda $68,x + sta PersistentRecord1+$20,x + dex + bpl @copy_record_tails + jsr CopyWorkingBufferToPersistentEC3B + jmp ContinueAsyncHandshake + +NmiState0C: + lda NmiSequenceStep + beq State0CSetComplete + lda NmiSyncState + cmp #NMI_SYNC_PENDING + bne StateHandshakeReturn +ContinueAsyncHandshake: + lda #$40 + sta $FB + jmp StateSequenceStep3 +State0CSetComplete: + lda #NMI_SYNC_COMPLETE +StateHandshakeComplete: + sta NmiSyncState + sta NmiSequenceStep +StateHandshakeReturn: + rts + +; $C455-$C464: choose an NMI update selector from state/$46. +SelectNmiUpdateForState: + ldy #$08 + lda NmiState + cmp #$08 + bcs @store + lda $46 + beq SharedReturnC464 + dey +@store: + sty $45 +SharedReturnC464: + rts + +; $C465-$C484: shared handler used by states $04/$07. +NmiState04_07: + lda $34 + beq @advance + lda #$06 + jsr $C92C + lda $3F + and #$10 + bne SharedReturnC464 + jmp StateHelperCAFA +@advance: + jsr AdvanceNmiStateAndEnterNmiSequence + lda NmiState + cmp #$08 + bcc @store_state + lda #$02 +@store_state: + sta NmiState + rts + +; --------------------------------------------------------------------------- +; $C485-$C521 +; Compare persistent/reference record values and prepare runtime state. +; Literal transcription of the original ROM topology. +; --------------------------------------------------------------------------- + +CompareAndUpdateRecord: + jsr LoadPersistentTriplet + + lda $05 + cmp $68 + bne C49A_FirstDifference + + lda $06 + cmp $69 + bne C49A_FirstDifference + + lda $07 + cmp $6A + beq C4A9_ValueAccepted + + +C49A_FirstDifference: + bcc C4AD_CheckReference + + ldx #$00 + +C49E_CopyTriplet: + lda $68,x + sta PersistentRecord0,y + inx + iny + cpx #$03 + bmi C49E_CopyTriplet + + +C4A9_ValueAccepted: + lda #$01 + bne C512_FinalizeValue + + +C4AD_CheckReference: + lda NmiState + cmp #$08 + bcs C4CC_CalculateValue + + jsr LoadReferenceTriplet + + lda $05 + cmp $68 + bne C4C6_ReferenceDifference + + lda $06 + cmp $69 + bne C4C6_ReferenceDifference + + lda $07 + cmp $6A + +C4C6_ReferenceDifference: + bcc C4CC_CalculateValue + + lda #$02 + bne C512_FinalizeValue + + +C4CC_CalculateValue: + lda $03F8 + sta $0A + asl a + sta $09 + + lda $68 + sec + sbc $05 + tay + + lda #$00 + + +C4DC_Accumulate: + dey + bmi C4E8_DifferenceReady + + clc + adc #$3C + bcc C4DC_Accumulate + + +C4E4_Overflow: + lda #$FF + bne C50B_SubtractLimit + + +C4E8_DifferenceReady: + sta $08 + + lda $69 + sec + sbc $06 + bcs C4F7_AddSaved + + clc + adc $08 + jmp C4FC_CompareLimit + + +C4F7_AddSaved: + clc + adc $08 + bcs C4E4_Overflow + + +C4FC_CompareLimit: + cmp $09 + bcs C50B_SubtractLimit + + clc + adc $09 + + +C503_ShiftLoop: + lsr $0A + beq C512_FinalizeValue + + lsr a + jmp C503_ShiftLoop + + +C50B_SubtractLimit: + sec + sbc $09 + lsr a + clc + adc #$04 + + +C512_FinalizeValue: + sta $53 + + lda #$18 + sta $45 + + lda #$01 + sta InputActionLatch + sta $40 + + lsr a + sta NmiSequenceStep + + rts + + +; --------------------------------------------------------------------------- +; $C522-$C539 +; Load three-byte reference record from ROM into $05-$07. +; --------------------------------------------------------------------------- + +LoadReferenceTriplet: + lda $43 + sta $02 + + jsr CalculateReferenceRecordOffset + tay + + lda PersistentRecordInitialDataC091,y + sta $05 + + lda $C092,y + sta $06 + + lda $C093,y + sta $07 + + rts + +; --------------------------------------------------------------------------- +; $C53A-$C550 +; Load three-byte persistent record into $05-$07. +; --------------------------------------------------------------------------- + +LoadPersistentTriplet: + lda $43 + sta $02 + + jsr CalculatePersistentRecordOffset + + lda PersistentRecord0,y + sta $05 + + lda PersistentRecord0+1,y + sta $06 + + lda PersistentRecord0+2,y + sta $07 + + rts + +; $C551-$C585: update-selector/setup entry cluster. + +SerialUpdateSelectorCluster: + lda #$12 + ldx NmiState + cpx #$08 + bcs SetSerialUpdateSelector + + clc + adc $43 + bne SetSerialUpdateSelector + +; $C55E +SetSerialUpdateSelector06: + lda #$06 + bne SetSerialUpdateSelector + + +; $C562 +SetSerialUpdateSelector05: + lda #$05 + bne SetSerialUpdateSelector + + +; $C566 +SetSerialUpdateSelector0E: + lda #$0E + bne SetSerialUpdateSelector + + +; $C56A +SetSerialUpdateSelector04: + lda #$04 + sta $33 + + +; $C56E +SetSerialUpdateSelector: + sta $45 + rts + + +; $C571 +SetSerialUpdateSelector09: + lda #$09 + bne SetSerialUpdateSelector + + +; $C575 +InitializeSerialSelectionMarker: + lda #$05 + sta $43 + + ldy #$02 + jsr InitializeSprite0 + + lda #$0F + bne SetSerialUpdateSelector + +; $C582 +SetSerialUpdateSelector11: + lda #$11 + bne SetSerialUpdateSelector + +; $C586-$C5AB: restore working record fields and shared selector entries. +RestoreWorkingRecordFields: + ldx #$03 + bne @copy_rom_field + +@copy_fields: + lda $05A0,x + sta $65,x + + lda $05C3,x + sta $68,x + +@copy_rom_field: + lda $C180,x + sta $61,x + + dex + bpl @copy_fields + + jsr CopyPersistentBufferToWorkingRam + + lda #$10 + bne SetSerialUpdateSelector + +; $C5A3 +SetSerialUpdateSelector17: + lda #$17 + bne SetSerialUpdateSelector + +; $C5A7 +CallC92CWithZero: + lda #$00 + jmp $C92C + +; $C5AC-$C5C1: initialize the six-byte $0300 structure. +Prepare0300Structure: + lda #$12 + sta $34 + + ldx #$05 + +@copy: + lda Prepare0300StructureTemplateC13C,x + sta $0300,x + dex + bpl @copy + + ldx $43 + inx + stx $0304 + rts +; $C5C2-$C620: runtime initialization. +InitializeRuntimeState: + ldx #$01 + stx $B7 + + jsr CopyPersistentBufferToWorkingRam + + stx $BD + stx $BE + stx $C4 + stx $C0 + stx InputActionLatch + stx $C2 + + inx + stx WorkingBuffer + + lda #$06 + sta $AC + + lda #$A0 + sta $8C + + lda #$10 + sta $80 + + lda #$04 + sta $04 + + ldx #$00 + stx $0B + + jsr BuildRuntimeEntry + + ldx #$07 +@copy_template: + lda RuntimeOamTemplateC178,x + sta $0228,x + dex + bpl @copy_template + + lda #$05 + sta $B1 + sta $B2 + + lda #$21 + sta $B0 + + lda #$03 + sta $B3 + + lda #$08 + sta $64 + sta $E0 + inc $E0 + + lda #$7A + sta $B4 + + inc $40 + inc $47 + + lda #$D0 + jsr $EC25 + jmp BuildOamEntryEC47 + + +; $C621-$C62C: copy the complete 256-byte persistent buffer +; into the working buffer. X wraps from $00 -> $FF and +; eventually back to $00, producing exactly 256 iterations. +CopyPersistentBufferToWorkingRam: + ldx #$00 + +@copy: + lda PersistentBuffer,x + sta WorkingBuffer,x + dex + bne @copy + + rts + +; These entry points also have absolute aliases in symbols.inc. +SerialWriteTimingCode: + lda #$05 + sta JOY1 + pha + pla + pha + pla + pha + pla + rts + +; $C639-$C64D: wait for $4016 bit 1 to assert and then deassert. +SerialWaitPulseCode: + @wait_high: + jsr SerialWriteTimingCode + lda JOY1 + and #$02 + beq @wait_high + + @wait_low: + jsr SerialWriteTimingCode + lda $4016 + and #$02 + bne @wait_low + + rts + +; $C64E: short-duration pulse entry. +SerialPulseShortCode: + pha + lda #$34 + bne SerialPulseCommon + +; $C653: long-duration pulse entry. +SerialPulseLongCode: + pha + lda #$6A + +; $C656: common pulse generator. +SerialPulseCommon: + sta $07 + sta $0C + + lda #$04 + sta JOY1 + +@high_delay: + dec $07 + bne @high_delay + + lda #$FF + sta JOY1 + +@low_delay: + dec $0C + bne @low_delay + + pla + rts +; --------------------------------------------------------------------------- +; $C66E-$C689: transmit one byte, MSB first. +; --------------------------------------------------------------------------- + +SerialTransmitByteCode: + jsr SerialPulseLongCode + ldx #$08 + +@next_bit: + sta $00 + clc + adc $00 + bcs @bit_set + + jsr SerialPulseShortCode + +@finish_bit: + dex + bne @next_bit + rts + +@bit_set: + jsr SerialPulseLongCode + jsr SerialIncrementCountCode + jmp @finish_bit + + +; --------------------------------------------------------------------------- +; $C68A-$C690: increment $09:$08. +; --------------------------------------------------------------------------- + +SerialIncrementCountCode: + inc $08 + bne @return + + inc $09 + +@return: + rts + + +; --------------------------------------------------------------------------- +; $C691-$C6A4: synchronize and sample JOY1 bit 1. +; --------------------------------------------------------------------------- + +SerialReceiveBitCode: + jsr SerialWaitPulseCode + + lda #$4E + sta $01 + +@delay: + dec $01 + bne @delay + + jsr SerialWriteTimingCode + + lda JOY1 + and #$02 + rts + + +; --------------------------------------------------------------------------- +; $C6A5-$C6C7: receive one byte. +; --------------------------------------------------------------------------- + +SerialReceiveByteCode: + lda #$08 + sta $03 + + lda #$00 + sta $04 + +@next_bit: + lda $04 + clc + adc $04 + sta $04 + + jsr SerialReceiveBitCode + bne @finish_bit + + jsr SerialIncrementCountCode + inc $04 + +@finish_bit: + dec $03 + bne @next_bit + + jsr SerialWaitPulseCode + + lda $04 + rts +; $C6C8-$C6D0: transmit the two runtime-data blocks. +; Deliberately falls through into TransmitDataBlockCode after +; configuring the second block. +TransmitRuntimeDataCode: + jsr SetupTransmitRamBlockCode + jsr TransmitDataBlockCode + jsr SetupTransmitWorkingBufferCode + +; Fall through intentionally into TransmitDataBlockCode. + +; $C6D1-$C715: transmit one serial data block. +TransmitDataBlockCode: + lda #$20 + sta $0A + + lda #$4E + sta $0B + +@initial_delay: + jsr SerialPulseShortCode + dec $0A + bne @initial_delay + + dec $0B + bne @initial_delay + +@long_preamble: + jsr SerialPulseLongCode + dec $05 + bne @long_preamble + +@short_preamble: + jsr SerialPulseShortCode + dec $06 + bne @short_preamble + + jsr SerialPulseLongCode + + lda #$00 + sta $08 + sta $09 + + ldy #$00 + +@send_payload: + lda ($0E),y + jsr SerialTransmitByteCode + iny + cpy $0D + bne @send_payload + + lda $09 + pha + + lda $08 + jsr SerialTransmitByteCode + + pla + jsr SerialTransmitByteCode + + jmp SerialPulseLongCode + +; $C716-$C725 +; Configure a $40-byte block beginning at $0060. +SetupTransmitRamBlockCode: + lda #$40 + sta $0D + + lda #$60 + sta $0E + + lda #$00 + sta $0F + + lda #$28 + bne SetTransmitPreambleCountsCode +; $C726-$C733 +; Configure the working-buffer block beginning at $05E0. +; A zero byte count represents 256 iterations when Y wraps. +SetupTransmitWorkingBufferCode: + lda #$00 + sta $0D + + lda #$E0 + sta $0E + + lda #$05 + sta $0F + + lda #$14 + + +; $C734-$C738 +; A contains the requested preamble timing/count value. +SetTransmitPreambleCountsCode: + sta $05 + sta $06 + rts +; --------------------------------------------------------------------------- +; $C739-$C791: receive the two runtime-data blocks. +; --------------------------------------------------------------------------- + +ReceiveRuntimeDataCode: + jsr DisableNmi + jsr SetupTransmitRamBlockCode + jsr ReceiveDataBlockCode + bcc ReceiveFirstBlockOk + +ReceiveError: + lda #$19 + sta $45 + +ReceiveRunUpdate: + jsr WaitForVblankEndAndEnableNmi + +ReceiveWaitUpdate: + lda $45 + bne ReceiveWaitUpdate + + beq ReceiveRuntimeDataCode + +ReceiveFirstBlockOk: + ldx #$00 + stx $A8 + + ldy #$01 + +ReceiveValidateHeader: + lda ($0E),y + sta $A3,y + cmp ReceiveHeaderTableBaseC17F,y + beq ReceiveHeaderByteOk + + inx + +ReceiveHeaderByteOk: + iny + cpy #$05 + bne ReceiveValidateHeader + + lda #$07 + sta $A0 + + lda #$22 + sta $A1 + + lda #$4F + sta $A2 + + lda #$04 + sta $A3 + + lda #$1A + sta $45 + + cpx #$00 + bne ReceiveRunUpdate + + jsr WaitForVblankEndAndEnableNmi + +ReceiveWaitSecondUpdate: + lda $45 + bne ReceiveWaitSecondUpdate + + jsr DisableNmi + jsr SetupTransmitWorkingBufferCode + jsr ReceiveDataBlockCode + bcs ReceiveError + + rts + +; --------------------------------------------------------------------------- +; $C792-$C7DD: receive one serial data block. +; +; Return: +; C clear = success +; C set = validation failure +; --------------------------------------------------------------------------- + +ReceiveDataBlockCode: +@restart: + lda $05 + sta $00 + +@wait_first_preamble: + jsr SerialReceiveBitCode + bne @restart + + dec $00 + bne @wait_first_preamble + + lda $06 + sta $00 + +@wait_second_preamble: + jsr SerialReceiveBitCode + beq @restart + + dec $00 + bne @wait_second_preamble + + lda #$00 + sta $08 + sta $09 + + jsr SerialWaitPulseCode + jsr SerialWaitPulseCode + + ldy #$00 + +@receive_payload: + jsr SerialReceiveByteCode + sta ($0E),y + + iny + cpy $0D + bne @receive_payload + + lda $08 + sta $0A + + lda $09 + sta $0B + + jsr SerialReceiveByteCode + cmp $0A + bne @failure + + jsr SerialReceiveByteCode + cmp $0B + beq @success + +@failure: + sec + rts + +@success: + clc + rts + +UpdateMenuSelection: + sta $00 + ldy MenuSelectionIndex + ldx #MENU_ACTION_NONE + lda Controller1Pressed + and #PAD_MENU_INPUT + bne @input + sta InputActionLatch +@early_return: + rts +@input: lda #$5D + sta $31 + lda InputActionLatch + bne @early_return + lda Controller1Pressed + bit Slot90BoundaryTable + bne @next + lsr a + lsr a + lsr a + bcs @next + lsr a + bcs @prev + ldx #MENU_ACTION_ACCEPTED + stx $30 + bne @store +@next: iny + cpy $00 + bcc @changed + ldy #$01 +@prev: dey + bpl @changed + ldy $00 + dey +@changed: + lda #$01 + sta $FF +@store: sty MenuSelectionIndex + inc InputActionLatch +@return:rts + +; --------------------------------------------------------------------------- +; $C820-$C868 +; Initialize PPU/course runtime memory and related state. +; --------------------------------------------------------------------------- + +InitializeCourseRuntime: + jsr InitializePairedPpuRows ; C820: 20 3F D1 + + lda #$03 ; C823: A9 03 + sta $01 ; C825: 85 01 + + lsr a ; C827: 4A + sta $45 ; C828: 85 45 + + lsr a ; C82A: 4A + sta $00 ; C82B: 85 00 + sta $4B ; C82D: 85 4B + + ldy #$EF ; C82F: A0 EF + +@clear_ram: + sta ($00),y ; C831: 91 00 + dey ; C833: 88 + cpy #$40 ; C834: C0 40 + bcs @clear_ram ; C836: B0 F9 + + ldx #$7F ; C838: A2 7F +@initialize_course_ram: + lda #$3B ; C83A: A9 3B + + sta $0400,x ; C83C: 9D 00 04 + + lda #$3D ; C83F: A9 3D + sta $0480,x ; C841: 9D 80 04 + sta $0500,x ; C844: 9D 00 05 + + dex ; C847: CA + bpl @initialize_course_ram ; C848: 10 F0 + + lda #$BF ; C84A: A9 BF + sta $0200 ; C84C: 8D 00 02 + + lda #$F7 ; C84F: A9 F7 + sta $0203 ; C851: 8D 03 02 + + lda #$FF ; C854: A9 FF + sta $0201 ; C856: 8D 01 02 + + lda #$3F ; C859: A9 3F + sta $E8 ; C85B: 85 E8 + + lsr a ; C85D: 4A + sta $E9 ; C85E: 85 E9 + + lda #$0A ; C860: A9 0A + sta $EB ; C862: 85 EB + + lda #$25 ; C864: A9 25 + sta $EA ; C866: 85 EA + + rts ; C868: 60 + + +; --------------------------------------------------------------------------- +; $C869-$C874 +; Clear zero-page/runtime RAM from $EF down through $4D. +; --------------------------------------------------------------------------- + +ClearRuntimeZeroPage: + lda #$00 + ldx #$EF + +@clear: + sta $00,x + dex + cpx #$4C + bne @clear + + rts + + + +; --------------------------------------------------------------------------- +; $C875-$C8DB +; Initialize global runtime and rider state. +; --------------------------------------------------------------------------- + +InitializeGameRuntime: + inc $47 + inc $40 + + lda $34 + bne @store_34 + + lda #$05 + +@store_34: + sta $34 + + lda #$34 + sta $E8 + + lda #$14 + sta $E9 + + lda #$A0 + sta $03F1 + + lda #$80 + sta $03B4 + sta $03B0 + + lda #$FF + sta $03BD + + ldx #$01 + stx $0370 + + inx + stx $03BE + + inx + + lda $03F2 + bne @initialize_riders + + ldx #$00 + +@initialize_riders: + lda $C0B4,x + sta $B8,x + + lda #$08 + sta $64,x + + lda $C0B0,x + sta $E0,x + + lda $C0B8,x + sta $80,x + + lda #$06 + sta $AC,x + sta $78,x + + lda #$01 + sta $84,x + sta $A8,x + + lda $19,x + lsr a + sta $74,x + + dex + bpl @initialize_riders + + lda $19,x + and #$03 + bne @select_rider + + lda #$02 + +@select_rider: + tax + lsr $78,x + +; --------------------------------------------------------------------------- +; $C8DE-$C92B +; Continue runtime initialization and prepare the initial $0300 structure. +; --------------------------------------------------------------------------- + +ContinueGameRuntimeC8DE: + lda $46 + beq @load_reference + + lda $43 + cmp #$04 + bne @load_reference + + ldx #$07 + +@copy_0300_template: + lda $C158,x + sta $0300,x + + dex + bpl @copy_0300_template + + lda $03F3 + jsr $DFA2 + sta $0307 + + txa + beq @load_reference + + sta $0306 + +@load_reference: + jsr LoadReferenceTriplet + + lda $41 + cmp #$08 + bcc @update_record + + jsr LoadPersistentTriplet + +@update_record: + lda $03F8 + asl a + clc + adc $06 + + cmp #$3C + bcc @store_06 + + sbc #$3C + inc $05 + +@store_06: + sta $06 + + lda #$00 + sta $07 + + lda #$02 + sta $03 + + jsr $C935 + + lda #$08 + + +; --------------------------------------------------------------------------- +; $C92C-$C934 +; Configure parameters for the $C935 structure builder. +; +; Entry: +; A = value stored into $03 +; $43 supplies $02 +; --------------------------------------------------------------------------- + +Setup0300EntryC92C: + ldy $43 + sty $02 + sta $03 + + jsr $C53E + + +; --------------------------------------------------------------------------- +; $C935-$C94A +; Prepare and copy seven bytes from $03D1 into the current $0300 +; output structure. +; --------------------------------------------------------------------------- + +Setup0300EntryC935: + ldx #$05 + jsr $DF7F + + ldx $0300 + ldy #$00 + +@copy_03d1: + lda $03D1,y + sta $0304,x + + inx + iny + cpy #$07 + bcc @copy_03d1 + + +; --------------------------------------------------------------------------- +; $C94B-$C970 +; Append another entry to the current $0300 structure. +; --------------------------------------------------------------------------- + +Append0300EntryC94B: + lda #$0A + jsr Advance0300IndexC971 + + ldy $03 + + lda IndexedParameterTableC0EC,y + sta $0301,x + + lda IndexedParameterTableC0ED,y + sta $0302,x + + lda #$FB + sta $0305,x + sta $0308,x + + lda #$07 + sta $0303,x + + lda #$00 + sta $030B,x + + rts + + +; --------------------------------------------------------------------------- +; $C971-$C97B +; Advance the current $0300 structure write index by A. +; +; Entry: +; A = amount to add +; +; Exit: +; X = previous $0300 index +; $0300 = previous index + A +; --------------------------------------------------------------------------- + +Advance0300IndexC971: + ldx $0300 ; C971: AE 00 03 + + clc ; C974: 18 + adc $0300 ; C975: 6D 00 03 + sta $0300 ; C978: 8D 00 03 + + rts ; C97B: 60 + + + +; --------------------------------------------------------------------------- +; $C97C-$C999 +; Runtime update chain. +; --------------------------------------------------------------------------- + +UpdateRaceRuntime: + jsr $CA1B + jsr UpdateGlobalRuntimeEB17 + jsr UpdateAccumulator + jsr UpdateGlobalPosition + jsr UpdateRiderCourseStateE70B + jsr ProcessCourseDefinition + jsr $F68D + jsr $F755 + jsr $C229 + +RuntimeUpdatePpuTailC997: + jmp UpdatePpuState + +; --------------------------------------------------------------------------- +; $C99A-$CA07 +; Main gameplay update chain. +; --------------------------------------------------------------------------- + +UpdateGameplayRuntime: + jsr $CA08 + + lda $03B1 + ora $03B2 + bne RuntimeUpdatePpuTailC997 + + jsr $D918 + jsr $DD8D + jsr UpdateAllRiderScriptStateE733 + jsr $E927 + + lda $52 + beq @normal_runtime + + jsr StateHelperCA9B + jmp @continue_runtime + +@normal_runtime: + lda $03F2 + beq @after_optional_updates + + jsr $DA9F + jsr $DBC4 + jsr $DEBB + +@after_optional_updates: + jsr UpdateGlobalCounters + jsr BuildGlobalDisplayEAC5 + jsr CompareRuntimeSlots + jsr CheckSlotState + +@continue_runtime: + jsr UpdateTriggeredSlots + jsr UpdateFourSlots + jsr UpdateFourSlotPositionsE96C + jsr UpdateDisplayState + jsr UpdateFourSlot8CE836 + jsr BuildFourSlotSpritesEA44 + jsr ProcessCourseDefinition + jsr UpdateSlotPairState + jsr UpdateGlobalFC + jsr UpdateGlobalDisplay + jsr ProcessRuntimeEntries + jsr PrepareSortedSlots + jsr UpdateRuntimeSlots + jsr UpdateRiderCourseStateE70B + jsr UpdatePpuState + jsr ProcessRiderSlots + + jmp ProcessSortedSlots + +; --------------------------------------------------------------------------- +; Preserve original ROM $CA08-$CA84. +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; $CA08-$CA1F +; Update all four rider slots, then process runtime input/state. +; --------------------------------------------------------------------------- + +UpdateFourRiderRuntime: + ldx #$03 + +@next_rider: + jsr $DCA0 + jsr $DFB2 + + dex + bpl @next_rider + + lda $03F7 + + ; CA16: D0 08 = BNE $CA20 + .byte $D0, $08 + + jsr RuntimeInputStateCA38 + + lda $14 + sta $5C + + rts + + +; --------------------------------------------------------------------------- +; $CA20-$CA37 +; Runtime transition/input handling. +; +; Several branches deliberately target the shared RTS at $CA84 or the +; existing code immediately following this block at $CA85. +; --------------------------------------------------------------------------- + +RuntimeTransitionCA20: + lda $03F7 + + ; CA23: F0 5F = BEQ $CA84 + .byte $F0, $5F + + lda $15 + + asl a + asl a + asl a + + ; CA2A: B0 59 = BCS $CA85 + .byte $B0, $59 + + asl a + + ; CA2D: 90 55 = BCC $CA84 + .byte $90, $55 + + lda #$00 + sta $42 + sta $47 + + jmp $C36A + + +; --------------------------------------------------------------------------- +; $CA38-$CA84 +; Runtime input/audio toggle and delay handling. +; --------------------------------------------------------------------------- + +RuntimeInputStateCA38: + lda $03B2 + bne RuntimeInputDecrementCA63 + + lda $15 + and #$10 + beq RuntimeInputStoreB0CA67 + + lda $03B0 + bne RuntimeInputStoreB0CA67 + + lda #$80 + sta $FB + sta $03B0 + + asl a + sta $FC + + lda $03B1 + eor #$01 + sta $03B1 + bne RuntimeInputSetDelayCA7F + + lda #$0F + sta APUCTRL + bne RuntimeInputSetDelayCA7F + + +; --------------------------------------------------------------------------- +; $CA63 +; --------------------------------------------------------------------------- + +RuntimeInputDecrementCA63: + dec $03B2 + rts + + +; --------------------------------------------------------------------------- +; $CA67 +; Store state and potentially disable audio. +; +; A is intentionally stored directly into $03B0 from the path that +; reaches this entry. +; --------------------------------------------------------------------------- + +RuntimeInputStoreB0CA67: + sta $03B0 + + lda $03B1 + beq RuntimeInputReturnCA84 + + lda #$00 + sta APUCTRL + + lda $15 + and #$0F + beq RuntimeInputReturnCA84 + + lda $03B2 + bne RuntimeInputReturnCA84 + + +; --------------------------------------------------------------------------- +; $CA7F +; --------------------------------------------------------------------------- + +RuntimeInputSetDelayCA7F: + lda #$28 + sta $03B2 + + +; --------------------------------------------------------------------------- +; $CA84 +; Shared return. +; --------------------------------------------------------------------------- + +RuntimeInputReturnCA84: + rts + + +ResetRuntimeZeroPagePreserveSelection: + lda MenuSelectionIndex + pha + ldx #$FF + lda #$00 +@clear: sta $00,x + dex + cpx #$20 + bne @clear + sta $FC + inc InputActionLatch + pla + sta MenuSelectionIndex + rts + +; --------------------------------------------------------------------------- +; $CA9B-$CB2B +; State/setup helpers and persistent/reference record offset calculators. +; --------------------------------------------------------------------------- + +StateHelperCA9B: + lda $32 + bne StateHelperCAAA + + jsr AdvanceNmiStateAndEnterNmiSequence + + lda $03F7 + .byte $D0, $DE + sta $47 + +SharedReturnCAA9: + rts + + +; $CAAA +StateHelperCAAA: + lda #$00 + sta $FC + sta $03A9 + sta $03E0 + sta $3C + + ldx $52 + dex + bne SharedReturnCAA9 + + lda $3F + lsr a + bcs StateHelperCAF3 + + lsr a + bcs StateHelperCAF3 + + and #$03 + tay + + lda #$3F + sta $0312 + + lda #$00 + sta $0313 + sta $0319 + + lda #$04 + sta $0314 + + lda $C166,y + sta $0316 + + lda $C170,y + sta $0318 + + ldy $43 + + lda $C160,y + sta $0315 + + lda $C16A,y + sta $0317 + + rts + + +; $CAF3 +StateHelperCAF3: + ldx #$11 + + lda $32 + lsr a + bcs SharedReturnCB05 + + +; $CAFA +StateHelperCAFA: + ldy #$06 + lda #$FE + +@fill: + sta $0304,x + inx + dey + bpl @fill + +SharedReturnCB05: + rts + + +; --------------------------------------------------------------------------- +; $CB06-$CB12 +; Calculate the persistent-record offset in Y. +; --------------------------------------------------------------------------- + +CalculatePersistentRecordOffset: + jsr CalculateReferenceRecordOffset + + ldy $03F2 + beq @store + + clc + adc #$23 + +@store: + tay + rts + + +; --------------------------------------------------------------------------- +; $CB13-$CB2B +; Calculate the base/reference-record offset. +; --------------------------------------------------------------------------- + +CalculateReferenceRecordOffset: + lda #$20 + + ldy $02 + cpy #$05 + beq @return + + lda #$00 + +@next_record: + dey + bmi @check_flag + + clc + adc #$03 + bne @next_record + +@check_flag: + ldy $46 + beq @return + + ora #$10 + +@return: + rts + +; $CB2C-$CB3A: indirect NMI sequence dispatcher. +DispatchNmiSequenceStep: + ldy NmiSequenceStep + lda $C12C,y + sta $00 + lda $C130,y + +; Intentional secondary entry used by DispatchNmiState. +DispatchIndirectHighByte: + sta $01 + jmp ($0000) + +; --------------------------------------------------------------------------- +; $CB3B-$CB76: NMI sequence step 0. +; --------------------------------------------------------------------------- + +StateSequenceStep0: + lda NmiState + cmp #$08 + bcs StateSequenceStep0Finish + + lda $03F2 + sta MenuSelectionIndex + + lda $53 + cmp #$04 + bcs StateSequenceStep0Finish + + lda $46 + beq StateSequenceStep0SetFlags + + lda $43 + cmp #$04 + bne StateSequenceStep0MaybeAdvance + + inc $03F3 + + lda $03F3 + lsr a + bcs StateSequenceStep0SetFlags + + lda $03F8 + lsr a + beq StateSequenceStep0SetFlags + + sta $03F8 + sec + +StateSequenceStep0MaybeAdvance: + bcs StateSequenceStep0SetFlags + + inc $43 + +StateSequenceStep0SetFlags: + lda #$01 + sta $46 + sta $4B + +StateSequenceStep0Finish: + lda #$04 + + ; Original CB75: D0 28 = BNE $CB9F. + ; Target is still inside the binary-backed next block. + .byte $D0, $28 + + +; --------------------------------------------------------------------------- +; $CB77-$CBA3: NMI sequence step 1. +; --------------------------------------------------------------------------- + +StateSequenceStep1: + ldy $33 + bne StateSequenceReturn + + lda #$04 + sta $03 + + ldx $0300 + +StateSequenceStep1Copy: + lda $03D9,y + sta $0304,x + + inx + iny + + cpy #$07 + bne StateSequenceStep1Copy + + jsr Append0300EntryC94B + + lda #$20 + + ldx $4B + bne StateSequenceSet03AD + + lda #$31 + +StateSequenceSet03AD: + sta $03AD + + clc + adc #$03 + + +; $CB9F +; Shared continuation used by sequence steps 0 and 1. +StateSequenceCommonCB9F: + sta $33 + inc NmiSequenceStep + + +; $CBA3 +StateSequenceReturn: + rts + +; --------------------------------------------------------------------------- +; $CBA4-$CC6A +; NMI sequence step 2 and associated initialization/update paths. +; --------------------------------------------------------------------------- + +StateSequenceStep2: + lda $33 + beq StateSequenceCommonCB9F + + cmp $03AD + beq StateSequenceStep2Process + + bcs StateSequenceReturn + + lda $4B + bne StateSequenceStep2CallThreshold + + lda $14 + and #$F0 + bne StateSequenceStep2RunStep3 + +StateSequenceStep2CallThreshold: + jsr StateSequenceStep2CheckThreshold + jmp SequenceSetup + + +StateSequenceStep2RunStep3: + jsr StateSequenceStep3 + + lda NmiState + bne StateSequenceReturn + + jmp AdvanceNmiStateAndEnterNmiSequence + + +; --------------------------------------------------------------------------- +; $CBC9 +; Main processing/initialization portion of sequence step 2. +; --------------------------------------------------------------------------- + +StateSequenceStep2Process: + lda $20 + bne StateSequenceReturn + + ldx #$03 + stx $2F + stx $0D + dex + + +StateSequenceStep2LoadTables: + lda SequenceLimitTable,x + sta $01 + + lda SequenceParameter02Table,x + sta $02 + + lda SequenceFlagsTable,x + sta $0B + + jsr SequenceSetupLoadIndexedParameters + + dex + bpl StateSequenceStep2LoadTables + + + lda #$01 + + ldx $53 + dex + stx $00 + + cpx #$03 + bcs StateSequenceStep2StoreFB + + stx $0D + lda #$02 + +StateSequenceStep2StoreFB: + sta $FB + + eor #$02 + sta $56 + + ldx $0D + + lda SequencePositionTable,x + sta $54 + + lda SequenceLimitTable,x + sta $55 + sta $03F1 + + lda $00 + bne StateSequenceStep2Return + + + lda #$0A + bne StateSequenceStep2Set45 + + +; --------------------------------------------------------------------------- +; $CC15 +; Check transition threshold and select update value. +; Also entered directly from the earlier StateSequenceStep2 path. +; --------------------------------------------------------------------------- + +StateSequenceStep2CheckThreshold: + lda $03AD + sec + sbc #$10 + cmp $33 + bne StateSequenceStep2BuildStructure + + lda $20 + bne StateSequenceStep2BuildStructure + + lda #$0D + + ldy $52 + dey + bne StateSequenceStep2Set45 + + lda $4B + clc + adc #$0B + + +StateSequenceStep2Set45: + sta $45 + rts + + +; --------------------------------------------------------------------------- +; $CC32 +; Build/update structure using table data. +; --------------------------------------------------------------------------- + +StateSequenceStep2BuildStructure: + lda #$07 + tay + + jsr Advance0300IndexC971 + + stx $05 + +StateSequenceStep2CopyTable: + lda StateSequenceStep2Template,y + sta $0301,x + + inx + dey + bpl StateSequenceStep2CopyTable + + + lda $3F + and #$10 + bne StateSequenceStep2Return + + ldy $05 + + lda $53 + jsr DivideBy10 + sta $0307,y + + txa + jsr DivideBy10 + sta $00 + + txa + bne StateSequenceStep2StorePrimary + + lda $00 + bne StateSequenceStep2StoreSecondary + + rts + + +StateSequenceStep2StorePrimary: + sta $0305,y + + +StateSequenceStep2StoreSecondary: + lda $00 + sta $0306,y + + +StateSequenceStep2Return: + rts + +; --------------------------------------------------------------------------- +; $CC6B-$CC7C: NMI sequence step 3. +; --------------------------------------------------------------------------- +StateSequenceStep3: + jsr AdvanceNmiStateAndEnterNmiSequence + + lda $4B + bne @return + + ldy NmiState + cpy #$09 + bcc @store_state + + lda #$08 + +@store_state: + sta NmiState + +@return: + rts + +; --------------------------------------------------------------------------- +; $CC7D-$CCDC: configure parameters and tail-call BuildIndirectStructure. +; --------------------------------------------------------------------------- +SequenceSetup: + lda $55 + sta $01 + + lda $56 + bne SequenceSetupSelectPointer + + lda $2F + beq SequenceSetupCalculate55 + + lda #$1F + sta $0380 + sta $038C + + lda #$AF + sta $0378 + + lda #$01 + sta $037C + lsr a + sta $0384 + + ldy #$BD + bne SequenceSetupStorePointer + +SequenceSetupCalculate55: + ldx #$00 + jsr UpdateSlotAccumulator + sta $55 + sta $01 + + lda $03F1 + cmp $55 + bcs SequenceSetupSelectPointer + + sta $55 + lda #$0A + sta $2F + +SequenceSetupSelectPointer: + ldy #$BB + +SequenceSetupStorePointer: + sty $02 + + ldx $53 + dex + cpx #$03 + bcc SequenceSetupIndexReady + ldx #$03 + +SequenceSetupIndexReady: + lda #$02 + sta $0B + +; $CCCA: shared parameter-loading entry. +SequenceSetupLoadIndexedParameters: + lda SequencePositionTable,x + sta $00 + + lda #$21 + sta $03 + + lda SequencePointerOffsetTable,x + sta $04 + + lda #$00 + jmp BuildIndirectStructure + +; --------------------------------------------------------------------------- +; $CCDD-$CD12: initialize nine 4-byte entries in the $02C0 area. +; --------------------------------------------------------------------------- +Initialize02C0Entries: + lda $3C + lsr a + bcc @return + + lda #$40 + sta $FB + + lda #$58 + sta $00 + + ldx #$08 + +@entry_loop: + txa + asl a + asl a + tay + + lda #$44 + sta $02C0,y + + lda $C147,x + sta $02C1,y + + lda #$00 + sta $02C2,y + + lda $00 + sta $02C3,y + + clc + adc #$0A + sta $00 + + dex + bpl @entry_loop + + lda #$F8 + sta $02D0 + +@return: + rts + + +; --------------------------------------------------------------------------- +; $CD13-$CD1E: copy eight ROM bytes from $C150-$C157 to $02C0-$02C7. +; --------------------------------------------------------------------------- +Copy02C0Template: + ldx #$07 + +@copy: + lda $C150,x + sta $02C0,x + dex + bpl @copy + + rts + + +; --------------------------------------------------------------------------- +; $CD1F-$CD58: update four runtime slots and set the global trigger flag. +; --------------------------------------------------------------------------- + +UpdateFourSlots: + lda $4F + beq @post_update + + ldx #$03 + +@slot_loop: + lda $A8,x + beq @next_slot + + lda $9C,x + cmp #$05 + beq @update_slot + + ora $98,x + ora $03E0,x + beq @update_slot + + lda #$00 + sta $5C,x + +@update_slot: + jsr CodeCD59 + +@next_slot: + dex + bpl @slot_loop + +@post_update: + lda $34 + bne @return + + lda $5C + and #$C0 + beq @return + + lda $03A9 + ora $52 + bne @return + + lda #$01 + sta $03A9 + sta $FE + +@return: + rts + +; --------------------------------------------------------------------------- +; $CD59-$CD78 +; Literal first incremental conversion. +; --------------------------------------------------------------------------- + +CodeCD59: + lda $5C,x ; CD59: B5 5C + and #$03 ; CD5B: 29 03 + sta $0A ; CD5D: 85 0A + ldy #$00 ; CD5F: A0 00 + + lda $0374,x ; CD61: BD 74 03 + ora $98,x ; CD64: 15 98 + + ; CD66: D0 52 = BNE $CDBA + .byte $D0, $52 + + lda $9C,x ; CD68: B5 9C + + ; CD6A: F0 04 = BEQ $CD70 + beq CodeCD70 + + cmp #$05 ; CD6C: C9 05 + + ; CD6E: D0 4A = BNE $CDBA + .byte $D0, $4A + +CodeCD70: + txa ; CD70: 8A + + ; CD71: D0 08 = BNE $CD7B + .byte $D0, $08 + + ldy #$02 ; CD73: A0 02 + lda $52 ; CD75: A5 52 + + ; CD77: D0 21 = BNE $CD9A + .byte $D0, $21 + + +; --------------------------------------------------------------------------- +; $CD79-$CDBC +; Second incremental conversion. +; --------------------------------------------------------------------------- + +CodeCD79: + ldy #$00 ; CD79: A0 00 + lda $B0,x ; CD7B: B5 B0 + + ; CD7D: D0 33 = BNE $CDB2 + .byte $D0, $33 + + lda $5C,x ; CD7F: B5 5C + and #$C0 ; CD81: 29 C0 + + ; CD83: F0 35 = BEQ $CDBA + .byte $F0, $35 + + stx $00 ; CD85: 86 00 + asl a ; CD87: 0A + + ; CD88: B0 02 = BCS $CD8C + bcs CodeCD8C + + inc $00 ; CD8A: E6 00 + +CodeCD8C: + jsr CodeCDEE ; CD8C: 20 EE CD + + ; CD8F: F0 04 = BEQ $CD95 + beq CodeCD95 + + lda $94,x ; CD91: B5 94 + + ; CD93: D0 25 = BNE $CDBA + .byte $D0, $25 + +CodeCD95: + ldy $00 ; CD95: A4 00 + txa ; CD97: 8A + + ; CD98: D0 12 = BNE $CDAC + bne CodeCDAC + + lda $94,x ; CD9A: B5 94 + cmp Slot94BoundaryTable,y ; CD9C: D9 D1 C0 + + ; CD9F: 90 0B = BCC $CDAC + bcc CodeCDAC + + ; CDA1: D0 16 = BNE $CDB9 + .byte $D0, $16 + + lda $90,x ; CDA3: B5 90 + cmp Slot90BoundaryTable,y + ; CDA5: D9 CE C0 + + ; CDA8: F0 13 = BEQ $CDBD + .byte $F0, $13 + + ; CDAA: B0 0D = BCS $CDB9 + .byte $B0, $0D + +CodeCDAC: + jsr $CE29 ; CDAC: 20 29 CE + + ; CDAF: 4C BD CD = JMP $CDBD + .byte $4C, $BD, $CD + +; --------------------------------------------------------------------------- +; $CDB2-$CDBC +; +; This is also a branch destination from CD7D. +; --------------------------------------------------------------------------- + +CodeCDB2: + ldy #$04 ; CDB2: A0 04 + lda $0A ; CDB4: A5 0A + + ; CDB6: F0 02 = BEQ $CDBA + beq CodeCDBA + + tay ; CDB8: A8 + iny ; CDB9: C8 + +CodeCDBA: + jsr CodeCE58 ; CDBA: 20 58 CE + + +; --------------------------------------------------------------------------- +; $CDBD-$CDED +; Continue slot update logic. +; --------------------------------------------------------------------------- + +CodeCDBD: + lda $98,x ; CDBD: B5 98 + + ; CDBF: D0 5B = BNE $CE1C + .byte $D0, $5B + + lda $B0,x ; CDC1: B5 B0 + bne CodeCDD5 ; CDC3: D0 10 + + lda $58,x ; CDC5: B5 58 + ora $52 ; CDC7: 05 52 + bne CodeCDDE ; CDC9: D0 13 + + lda $94,x ; CDCB: B5 94 + bne CodeCDD5 ; CDCD: D0 06 + + lda $90,x ; CDCF: B5 90 + cmp #$A0 ; CDD1: C9 A0 + bcc CodeCDDE ; CDD3: 90 09 + +CodeCDD5: + lda $0A ; CDD5: A5 0A + beq CodeCDDE ; CDD7: F0 05 + sta $00 ; CDD9: 85 00 + jmp UpdateSlotTimer ; CDDB: 4C 83 CE + +CodeCDDE: + lda $B0,x ; CDDE: B5 B0 + + ; CDE0: D0 3A = BNE $CE1C + .byte $D0, $3A + + lda $0368,x ; CDE2: BD 68 03 + cmp $AC,x ; CDE5: D5 AC + + ; CDE7: F0 33 = BEQ $CE1C + .byte $F0, $33 + + ldy #$05 ; CDE9: A0 05 + jmp $DCC7 ; CDEB: 4C C7 DC + + +; --------------------------------------------------------------------------- +; $CDEE-$CE28 +; Slot/state condition helpers. +; --------------------------------------------------------------------------- + +CodeCDEE: + lda $C0,x ; CDEE: B5 C0 + cmp #$E4 ; CDF0: C9 E4 + beq CodeCE0A ; CDF2: F0 16 + + lda #$03 ; CDF4: A9 03 + cmp RiderMotionState,x ; CDF6: D5 A4 + bne CodeCE00 ; CDF8: D0 06 + + lda $70,x ; CDFA: B5 70 + cmp #$03 ; CDFC: C9 03 + bcs CodeCE0A ; CDFE: B0 0A + +CodeCE00: + lda $B8,x ; CE00: B5 B8 + cmp #$38 ; CE02: C9 38 + bcs CodeCE0A ; CE04: B0 04 + cmp #$08 ; CE06: C9 08 + bcs CodeCE1D ; CE08: B0 13 + +CodeCE0A: + lda #$01 ; CE0A: A9 01 + cmp $036C,x ; CE0C: DD 6C 03 + beq CodeCE15 ; CE0F: F0 04 + asl a ; CE11: 0A + sta $036C,x ; CE12: 9D 6C 03 + +CodeCE15: + txa ; CE15: 8A + + ; CE16: D0 10 = BNE $CE28 + bne CodeCE28 + + lda #$04 ; CE18: A9 04 + sta $FD ; CE1A: 85 FD + rts ; CE1C: 60 + + +; --------------------------------------------------------------------------- +; $CE1D +; Secondary entry/path. +; --------------------------------------------------------------------------- + +CodeCE1D: + lda #$01 ; CE1D: A9 01 + cmp $036C,x ; CE1F: DD 6C 03 + beq CodeCE28 ; CE22: F0 04 + lsr a ; CE24: 4A + sta $036C,x ; CE25: 9D 6C 03 + +CodeCE28: + rts ; CE28: 60 + + +; --------------------------------------------------------------------------- +; $CE29-$CE57 +; Update slot position against boundary/table values. +; --------------------------------------------------------------------------- + +CodeCE29: + cpx $4C ; CE29: E4 4C + bne CodeCE45 ; CE2B: D0 18 + + lda $C0BC,y ; CE2D: B9 BC C0 + clc ; CE30: 18 + adc $90,x ; CE31: 75 90 + sta $90,x ; CE33: 95 90 + bcc CodeCE39 ; CE35: 90 02 + inc $94,x ; CE37: F6 94 + +CodeCE39: + txa ; CE39: 8A + bne CodeCE45 ; CE3A: D0 09 + + lda $94,x ; CE3C: B5 94 + cmp Slot94BoundaryTable,y + ; CE3E: D9 D1 C0 + beq CodeCE46 ; CE41: F0 03 + bcs CodeCE4D ; CE43: B0 08 + +CodeCE45: + rts ; CE45: 60 + +CodeCE46: + lda $90,x ; CE46: B5 90 + cmp Slot90BoundaryTable,y + ; CE48: D9 CE C0 + bcc CodeCE45 ; CE4B: 90 F8 + +CodeCE4D: + lda Slot90BoundaryTable,y + ; CE4D: B9 CE C0 + sta $90,x ; CE50: 95 90 + + lda Slot94BoundaryTable,y + ; CE52: B9 D1 C0 + sta $94,x ; CE55: 95 94 + rts ; CE57: 60 + + +; --------------------------------------------------------------------------- +; $CE58-$CE82 +; Slot position adjustment helper. +; --------------------------------------------------------------------------- + +CodeCE58: + cpx $4C ; CE58: E4 4C + bne CodeCE82 ; CE5A: D0 26 + + sty $00 ; CE5C: 84 00 + + lda $94,x ; CE5E: B5 94 + bne CodeCE6D ; CE60: D0 0B + + lda $B0,x ; CE62: B5 B0 + lsr a ; CE64: 4A + tay ; CE65: A8 + + lda $90,x ; CE66: B5 90 + cmp SlotPositionThresholdTable,y + ; CE68: D9 CC C0 + bcc CodeCE82 ; CE6B: 90 15 + +CodeCE6D: + ldy $00 ; CE6D: A4 00 + + lda $90,x ; CE6F: B5 90 + sec ; CE71: 38 + sbc $C0C1,y ; CE72: F9 C1 C0 + sta $90,x ; CE75: 95 90 + bcs CodeCE82 ; CE77: B0 09 + + lda $94,x ; CE79: B5 94 + beq CodeCE80 ; CE7B: F0 03 + + dec $94,x ; CE7D: D6 94 + rts ; CE7F: 60 + +CodeCE80: + sta $90,x ; CE80: 95 90 + +CodeCE82: + rts ; CE82: 60 + +; --------------------------------------------------------------------------- +; $CE83-$CEB1: update per-slot timer/state values. +; --------------------------------------------------------------------------- + +UpdateSlotTimer: + lda $26,x + bne @return + + lda $B0,x + lsr a + tay + + lda SlotTimerReloadTable,y + sta $26,x + + lsr $00 + bcc @alternate + + lda $AC,x + cmp SlotACTargetTableC0C8,y + beq @return + bcc @increment + + dec $AC,x + +@return: + rts + + +@alternate: + lda $0388,x + and #$02 + sta $0388,x + + lda $AC,x + cmp SlotACAlternateTargetTable,y +; Original CEAD: B0 03 = BCS $CEB2 + .byte $B0, $03 +@increment: + inc $AC,x + rts + + +RiderStateUpdateCEB2: + lda $5C,x + and #$C0 + beq @return + lda $B0,x + bne @return + inc $AC,x + lda #$0D + sta $26,x + lda $AC,x + cmp #$0D + bcc @return + lda #$01 + sta $98,x + asl a + sta $26,x +@return: + rts + + +; --------------------------------------------------------------------------- +; $CED0-$CF0B +; Process active rider slots. +; +; Starts with slot $4A and advances through slot 3. +; --------------------------------------------------------------------------- + +ProcessRiderSlots: + ldx $4A +@next_slot: + stx $0F + + lda $A8,x + beq @advance_slot + + lda $98,x + ora $9C,x + bne @advance_slot + + jsr $CFEA + + lda $B0,x + bne @copy_runtime + + jsr $CFCB + jsr $D018 + bne @advance_slot + + lda $03A6 + beq @advance_slot + + jsr SelectRiderConfiguration + + lda $52 + bne @advance_slot + + jsr CheckRiderCourseState + jmp @advance_slot + +@copy_runtime: + lda $0368,x + sta $7C,x + +@advance_slot: + ldx $0F + inx + cpx #$04 + bcc @next_slot + + rts + + +; --------------------------------------------------------------------------- +; $CF0C-$CF95 +; Rider course/lane state evaluation. +; +; X = current rider slot. +; +; This code has several secondary entry points and shared returns. +; --------------------------------------------------------------------------- + +CheckRiderCourseState: + lda $84,x ; CF0C: B5 84 + lsr a ; CF0E: 4A + + ; CF0F: 90 FA = BCC $CF0B + ; $CF0B is the RTS immediately preceding this routine. + .byte $90, $FA + + lda $70,x ; CF11: B5 70 + sta $02 ; CF13: 85 02 + + ldy #$02 ; CF15: A0 02 + jsr $CF96 ; CF17: 20 96 CF + + bcc RiderCourseAlternateCF2B ; CF1A: 90 0F + + jsr $CFDB ; CF1C: 20 DB CF + + lda $80,x ; CF1F: B5 80 + asl a ; CF21: 0A + + ; CF22: 90 E7 = BCC $CF0B + .byte $90, $E7 + + lda $18,x ; CF24: B5 18 + lsr a ; CF26: 4A + + bcs RiderCoursePathCF64 ; CF27: B0 3B + bcc RiderCourseEntryCF47 ; CF29: 90 1C + + +; --------------------------------------------------------------------------- +; $CF2B +; Alternate path. +; --------------------------------------------------------------------------- + +RiderCourseAlternateCF2B: + lda $DC,x ; CF2B: B5 DC + bne RiderCourseReturnCF87 ; CF2D: D0 58 + + lda $84,x ; CF2F: B5 84 + lsr a ; CF31: 4A + bcc RiderCourseReturnCF87 ; CF32: 90 53 + + lda $80,x ; CF34: B5 80 + cmp #$F0 ; CF36: C9 F0 + bcs RiderCourseReturnCF87 ; CF38: B0 4D + + asl a ; CF3A: 0A + bcc RiderCourseReturnCF87 ; CF3B: 90 4A + + lda $19,x ; CF3D: B5 19 + cmp #$E0 ; CF3F: C9 E0 + bcc RiderCourseReturnCF87 ; CF41: 90 44 + + cmp #$F8 ; CF43: C9 F8 + bcc RiderCoursePathCF64 ; CF45: 90 1D + + +; --------------------------------------------------------------------------- +; $CF47 +; Secondary entry point. +; Uses $0360,X to select a neighboring course/lane index. +; --------------------------------------------------------------------------- + +RiderCourseEntryCF47: + ldy $0360,x ; CF47: BC 60 03 + dey ; CF4A: 88 + cpy #$01 ; CF4B: C0 01 + bmi RiderCourseEntryCF6A ; CF4D: 30 1B + + jsr RiderCourseValueCheckCF88 ; CF4F: 20 88 CF + bcs RiderCourseReturnCF87 ; CF52: B0 33 + + ldy $70,x ; CF54: B4 70 + iny ; CF56: C8 + sty $02 ; CF57: 84 02 + + ldy #$00 ; CF59: A0 00 + jsr $CF96 ; CF5B: 20 96 CF + bcs RiderCourseReturnCF87 ; CF5E: B0 27 + + lda #$01 ; CF60: A9 01 + bne RiderCourseStoreCF85 ; CF62: D0 21 + + +; --------------------------------------------------------------------------- +; $CF64 +; Another course-state path. +; --------------------------------------------------------------------------- + +RiderCoursePathCF64: + lda $C0,x ; CF64: B5 C0 + cmp #$E6 ; CF66: C9 E6 + beq RiderCourseEntryCF47 ; CF68: F0 DD + + +; --------------------------------------------------------------------------- +; $CF6A +; Secondary entry point. +; --------------------------------------------------------------------------- + +RiderCourseEntryCF6A: + ldy $0360,x ; CF6A: BC 60 03 + iny ; CF6D: C8 + cpy #$05 ; CF6E: C0 05 + bpl RiderCourseEntryCF47 ; CF70: 10 D5 + + jsr RiderCourseValueCheckCF88 ; CF72: 20 88 CF + bcs RiderCourseReturnCF87 ; CF75: B0 10 + + ldy $70,x ; CF77: B4 70 + dey ; CF79: 88 + sty $02 ; CF7A: 84 02 + + ldy #$00 ; CF7C: A0 00 + jsr $CF96 ; CF7E: 20 96 CF + bcs RiderCourseReturnCF87 ; CF81: B0 04 + + lda #$FF ; CF83: A9 FF + +RiderCourseStoreCF85: + sta $DC,x ; CF85: 95 DC + +RiderCourseReturnCF87: + rts ; CF87: 60 + + +; --------------------------------------------------------------------------- +; $CF88-$CF95 +; Check a course/property value for the current rider. +; +; Carry: +; set = outside the accepted $3B-$3D range +; clear = accepted +; +; $CF8B is also a secondary entry point after the table load. +; --------------------------------------------------------------------------- + +RiderCourseValueCheckCF88: + jsr LoadIndexedValueE7FF ; CF88: 20 FF E7 + +RiderCourseValueCheckCF8B: + cmp #$3B ; CF8B: C9 3B + bcc RiderCourseValueInvalidCF94 ; CF8D: 90 05 + + cmp #$3E ; CF8F: C9 3E + bcs RiderCourseValueInvalidCF94 ; CF91: B0 01 + + rts ; CF93: 60 + +RiderCourseValueInvalidCF94: + sec ; CF94: 38 + rts ; CF95: 60 + + + + + +; --------------------------------------------------------------------------- +; $CF96-$CFCA +; Check whether another rider blocks a candidate lane. +; +; Entry: +; X = current rider slot +; Y = proximity/window selector +; $02 = candidate lane +; $0F = current rider slot saved by caller +; +; Exit: +; C clear = candidate lane has no nearby conflicting rider +; C set = another rider is in the candidate lane and distance window +; --------------------------------------------------------------------------- + +CheckAdjacentRiderConflict: + lda $80,x + clc + adc $C0D6,y + sta $00 + + sec + sbc $C0D7,y + sta $01 + + ldy $4A + +@check_rider: + cpy $0F + beq @next_rider + + lda $0084,y + lsr a + bcc @next_rider + + lda $0070,y + beq @next_rider + + cmp $02 + bne @next_rider + + lda $0080,y + cmp $00 + bcs @next_rider + + cmp $01 + bcs @return + +@next_rider: + iny + cpy #$04 + bcc @check_rider + + clc + +@return: + rts + +; --------------------------------------------------------------------------- +; $CFCB-$CFE9 +; Compare rider position pairs and update rider flags. +; +; X = current rider. +; +; $CFDB is also callable as a secondary entry. +; --------------------------------------------------------------------------- + +UpdateRiderPositionFlags: + ldy #$80 ; CFCB: A0 80 + + lda $94,x ; CFCD: B5 94 + cmp $78,x ; CFCF: D5 78 + + ; CFD1: 90 27 = BCC $CFFA + .byte $90, $27 + + bne UpdateRiderPositionFlagsCFDB ; CFD3: D0 06 + + lda $90,x ; CFD5: B5 90 + cmp $74,x ; CFD7: D5 74 + + ; CFD9: 90 1F = BCC $CFFA + .byte $90, $1F + +; --------------------------------------------------------------------------- +; $CFDB +; Secondary entry point. +; --------------------------------------------------------------------------- +UpdateRiderPositionFlagsCFDB: + lda $94,x ; CFDB: B5 94 + + ; CFDD: F0 0A = BEQ $CFE9 + beq RiderPositionFlagsReturnCFE9 + + lda #$0F ; CFDF: A9 0F + bne MergeRiderPositionFlagsCFE5 ; CFE1: D0 02 + +; $CFE3 is a genuine secondary entry used by the next routine. +SetRiderPositionHighNibbleMaskCFE3: + lda #$F0 ; CFE3: A9 F0 + +MergeRiderPositionFlagsCFE5: + and $5C,x ; CFE5: 35 5C + sta $5C,x ; CFE7: 95 5C + +RiderPositionFlagsReturnCFE9: + rts ; CFE9: 60 + + +; --------------------------------------------------------------------------- +; $CFEA-$CFFF +; Update packed rider state in $5C,X based on $7C,X versus $AC,X. +; +; X = current rider slot. +; --------------------------------------------------------------------------- + +UpdateRiderPackedState: + jsr SetRiderPositionHighNibbleMaskCFE3 ; CFEA: 20 E3 CF + + ldy #$01 ; CFED: A0 01 + + lda $7C,x ; CFEF: B5 7C + beq @return ; CFF1: F0 0C + + cmp $AC,x ; CFF3: D5 AC + beq @return ; CFF5: F0 08 + + bcc @merge ; CFF7: 90 01 + iny ; CFF9: C8 + +@merge: + tya ; CFFA: 98 + ora $5C,x ; CFFB: 15 5C + sta $5C,x ; CFFD: 95 5C + +@return: + rts ; CFFF: 60 + +; --------------------------------------------------------------------------- +; $D000-$D017 +; Select a rider configuration index and tail-call ConfigureSlot. +; +; X = current rider slot. +; Y = selected configuration value on exit to ConfigureSlot. +; --------------------------------------------------------------------------- + +SelectRiderConfiguration: + ldy #$00 ; D000: A0 00 + + cpx #$03 ; D002: E0 03 + beq @configure ; D004: F0 0F + + lda $80,x ; D006: B5 80 + and #$10 ; D008: 29 10 + + ; D00A: F0 F3 = BEQ $CFFF + ; Shared RTS immediately before this routine. + .byte $F0, $F3 + + lda $1A,x ; D00C: B5 1A + cmp #$C0 ; D00E: C9 C0 + + ; D010: 90 ED = BCC $CFFF + .byte $90, $ED + + bne @configure ; D012: D0 01 + + iny ; D014: C8 + +@configure: + jmp ConfigureSlot ; D015: 4C 50 DB + + + +; --------------------------------------------------------------------------- +; $D018-$D057 +; Scan four bytes belonging to rider X in the $03C0 table. +; +; X = current rider slot +; +; Each rider owns four bytes: +; offset = X * 4 +; --------------------------------------------------------------------------- + +ScanRiderStructure: + lda #$00 + sta $09 + sta $0D + + txa + asl a + asl a + sta $0A + + ldy #$03 +@scan_entry: + sty $0B + + ldy $0A + lda $03C0,y + + cmp #$C0 + + ; D02E: F0 28 = BEQ $D058 + .byte $F0, $28 + + cmp #$C1 + + ; D032: F0 24 = BEQ $D058 + .byte $F0, $24 + + cmp #$70 + bcc @check_48 + + cmp #$74 + + ; D03A: 90 1C = BCC $D058 + .byte $90, $1C + +@check_48: + cmp #$48 + bcc @next_entry + + cmp #$4B + + ; D042: 90 60 = BCC $D0A4 + .byte $90, $60 + +@next_entry: + inc $0A + + ldy $0B + dey + bpl @scan_entry + + ldy #$00 + lda $58,x + bne @use_y + + ldy $0368,x + +@use_y: + tya + + ; D055: 4C A6 D0 = JMP $D0A6 + .byte $4C, $A6, $D0 + + + +; --------------------------------------------------------------------------- +; $D058-$D0A5 +; Examine adjacent course positions for the current rider. +; +; X = current rider slot +; +; $08 = candidate course index +; $09 = $40 +; +; $D088 is a secondary entry used elsewhere. +; --------------------------------------------------------------------------- + +CheckRiderCourseAhead: + inc $0D ; D058: E6 0D + + lda $0A ; D05A: A5 0A + and #$03 ; D05C: 29 03 + tay ; D05E: A8 + iny ; D05F: C8 + tya ; D060: 98 + + clc ; D061: 18 + adc $E0,x ; D062: 75 E0 + and #$3F ; D064: 29 3F + sta $08 ; D066: 85 08 + + lda #$40 ; D068: A9 40 + sta $09 ; D06A: 85 09 + + ldy $0360,x ; D06C: BC 60 03 + dey ; D06F: 88 + cpy #$01 ; D070: C0 01 + bmi CheckRiderCourseOtherSideD088 ; D072: 30 14 + + jsr $E803 ; D074: 20 03 E8 + + ldy $07 ; D077: A4 07 + cmp #$FA ; D079: C9 FA + + ; D07B: F0 0B = BEQ $D088 + beq CheckRiderCourseOtherSideD088 + + jsr RiderCourseValueCheckCF8B ; D07D: 20 8B CF + + ; D080: B0 ED = BCS $D06F + ; + ; Return to DEY so another candidate position/state can be checked. + .byte $B0, $ED + + jsr RiderCourseEntryCF47 ; D082: 20 47 CF + + ; D085: 4C 4B D0 = JMP $D04B + ; Return to the earlier scan routine's fallback path. + .byte $4C, $4B, $D0 + + +; --------------------------------------------------------------------------- +; $D088 +; Secondary entry. +; Check the opposite/next course side. +; --------------------------------------------------------------------------- + +CheckRiderCourseOtherSideD088: + ldy $0360,x ; D088: BC 60 03 + iny ; D08B: C8 + cpy #$06 ; D08C: C0 06 + + ; D08E: 10 BB = BPL $D04B + .byte $10, $BB + + jsr $E803 ; D090: 20 03 E8 + + ldy $07 ; D093: A4 07 + cmp #$E4 ; D095: C9 E4 + + ; D097: F0 B2 = BEQ $D04B + .byte $F0, $B2 + + jsr RiderCourseValueCheckCF8B ; D099: 20 8B CF + + ; D09C: B0 ED = BCS $D08B + ; + ; Return to INY and try the next candidate. + .byte $B0, $ED + + jsr RiderCourseEntryCF6A ; D09E: 20 6A CF + + ; D0A1: 4C 4B D0 = JMP $D04B + .byte $4C, $4B, $D0 + + +; --------------------------------------------------------------------------- +; $D0A4 +; Shared continuation from the earlier $D042 branch. +; --------------------------------------------------------------------------- + +CourseScanResultD0A4: + lda #$09 ; D0A4: A9 09 + + + +; --------------------------------------------------------------------------- +; $D0A6-$D0AA +; Shared result tail for the rider course/object scan. +; +; Entry: +; A = result value +; X = current rider slot +; +; Exit: +; $7C,X = result +; A = $0D +; --------------------------------------------------------------------------- + +CourseScanStoreResultD0A6: + sta $7C,x + + lda $0D + rts + + +; --------------------------------------------------------------------------- +; $D0AB-$D0C5 +; Derive per-rider $0360 state from $B8,X. +; +; Processes rider slots 3 -> 0. +; $0360,X is a finer-grained vertical/lane-position band, not the +; game's four-lane index. +; --------------------------------------------------------------------------- + +UpdateRiderLanePositionStates: + ldx #$03 + +@next_rider: + ldy #$05 + + lda $B8,x + sec + sbc #$10 + bmi @store + +@reduce: + dey + beq @store + + sec + sbc #$08 + bpl @reduce + +@store: + tya + sta $0360,x + + dex + bpl @next_rider + + rts + +; --------------------------------------------------------------------------- +; $D0C6-$D0F3 +; Build four course/environment sample bytes for each rider. +; +; RAM layout: +; +; Rider 0 -> $03C0-$03C3 +; Rider 1 -> $03C4-$03C7 +; Rider 2 -> $03C8-$03CB +; Rider 3 -> $03CC-$03CF +; +; The first value is returned by $E7FC. Subsequent values are loaded +; from the course-data pointer in $03:$04. +; --------------------------------------------------------------------------- + +BuildRiderCourseObjectTable: + ldx #$03 + +@next_rider: + txa + asl a + asl a + sta $0C + + lda #$04 + sta $0B + + jsr $E7FC + sta $C0,x + + ; D0D6: 4C DD D0 = JMP $D0DD + ; First iteration uses the value already returned in A by $E7FC, + ; skipping the indexed load used by subsequent iterations. + .byte $4C, $DD, $D0 + +@read_next: + ldy $08 + lda ($03),y + +; $D0DD +@store_value: + pha + + iny + tya + and #$3F + sta $08 + + ldy $0C + pla + sta $03C0,y + + inc $0C + dec $0B + bne @read_next + + dex + bpl @next_rider + + rts + +; --------------------------------------------------------------------------- +; $D0F4-$D11E +; Copy/setup the $03D0 structure and initialize temporary pointers, +; then tail-call $D2A3. +; --------------------------------------------------------------------------- + +Initialize03D0Structure: + ldx #$06 + +@copy: + lda $03D9,x + sta $03D1,x + + dex + bpl @copy + + lda #$56 + sta $00 + + lda #$23 + sta $01 + + lda #$17 + sta $03D0 + + lda #$D0 + sta $02 + + lda #$03 + sta $03 + + lda #$FB + sta $03D2 + sta $03D5 + + jmp $D2A3 + + +; --------------------------------------------------------------------------- +; $D11F-$D13E +; Initialize command/update structure at $0300. +; +; Final structure: +; +; $0300 = $11 +; $0301 = $23 +; $0302 = $A0 +; $0303 = $0E +; $0304-$0311 = $FE +; $0312 = $00 +; --------------------------------------------------------------------------- + +Initialize0300Structure: + lda #$11 + sta $0300 + + lda #$23 + sta $0301 + + lda #$A0 + sta $0302 + + ldx #$0E + stx $0303 + + lda #$FE + +@fill: + sta $0303,x + + dex + bne @fill + + stx $0312 + rts + + +; --------------------------------------------------------------------------- +; $D13F-$D14D +; Generate three paired PPU update rows using the descriptors +; at $E6A1, $E6A5, and $E6A9. +; +; BuildPairedPpuRows uses X as the descriptor-table offset. +; The third invocation is a tail call, so its RTS returns directly +; to this routine's caller. +; --------------------------------------------------------------------------- + +InitializePairedPpuRows: + ldx #$00 + jsr BuildPairedPpuRows + + ldx #$04 + jsr BuildPairedPpuRows + + ldx #$08 + jmp BuildPairedPpuRows + + +; --------------------------------------------------------------------------- +; $D14E-$D15D +; NMI-time preparation. +; +; Wait for PPUSTATUS bit $40 to clear, call the $D1B3 helper, +; then derive $4C from the low two bits of $3F. +; --------------------------------------------------------------------------- + +NmiRuntimeUpdate: +@wait_ppu: + lda PPUSTATUS ; D14E: AD 02 20 + and #$40 ; D151: 29 40 + bne @wait_ppu ; D153: D0 F9 + + jsr ApplyPpuScrollState + ; D155: 20 B3 D1 + + lda $3F ; D158: A5 3F + and #$03 ; D15A: 29 03 + sta $4C ; D15C: 85 4C + + +; --------------------------------------------------------------------------- +; $D15E-$D17B +; Continue NMI-time runtime preparation. +; +; Initialize the $0300 update structure. Unless game state is $09, +; hide/clear a set of OAM entries and rebuild the per-rider course +; samples / $03D0 structure. +; +; Continues at $D17C. +; --------------------------------------------------------------------------- + + jsr Initialize0300Structure ; D15E: 20 1F D1 + + ldx #$43 ; D161: A2 43 + + lda $41 ; D163: A5 41 + cmp #$09 ; D165: C9 09 + + ; D167: F0 15 = BEQ $D17E + ; Target is beyond this incremental block. + .byte $F0, $15 + + lda #$F8 ; D169: A9 F8 + ldy #$EC ; D16B: A0 EC + +@hide_oam_entries: + sta $0200,y ; D16D: 99 00 02 + + dey ; D170: 88 + dey ; D171: 88 + dey ; D172: 88 + dey ; D173: 88 + + bne @hide_oam_entries ; D174: D0 F7 + + jsr BuildRiderCourseObjectTable ; D176: 20 C6 D0 + jsr Initialize03D0Structure ; D179: 20 F4 D0 + +; --------------------------------------------------------------------------- +; $D17C-$D19A +; Delay/synchronization loop followed by direct PPU register setup. +; +; $D17E is also an entry from the BEQ at $D167. On that path X retains +; its previous value instead of being initialized to $28. +; --------------------------------------------------------------------------- + + ldx #$28 ; D17C: A2 28 + +; $D17E - secondary entry from D167. +NmiRuntimeDelayD17E: + ldy #$12 ; D17E: A0 12 + +@delay_inner: + dey ; D180: 88 + bne @delay_inner ; D181: D0 FD + + dex ; D183: CA + bne NmiRuntimeDelayD17E ; D184: D0 F8 + + lda $10 ; D186: A5 10 + ora $4D ; D188: 05 4D + sta PPUCTRL ; D18A: 8D 00 20 + + lda PPUSTATUS ; D18D: AD 02 20 + + lda $12 ; D190: A5 12 + sta PPUSCROLL ; D192: 8D 05 20 + + lda $13 ; D195: A5 13 + sta PPUSCROLL ; D197: 8D 05 20 + + rts ; D19A: 60 + + +; --------------------------------------------------------------------------- +; $D19B-$D1B2 +; Wait for PPU status, update PPU control state, then reset PPUSCROLL. +; --------------------------------------------------------------------------- + +UpdatePpuState: +@wait_ppu: + lda PPUSTATUS ; D19B: AD 02 20 + and #$40 ; D19E: 29 40 + beq @wait_ppu ; D1A0: F0 F9 + + lda $10 ; D1A2: A5 10 + and #$F0 ; D1A4: 29 F0 + jsr $C333 ; D1A6: 20 33 C3 + + lda PPUSTATUS ; D1A9: AD 02 20 + + lda #$00 ; D1AC: A9 00 + sta PPUSCROLL ; D1AE: 8D 05 20 + + ; D1B1: F0 E4 = BEQ $D197 + ; A is known to be zero, so this is an unconditional branch. + .byte $F0, $E4 + + +; --------------------------------------------------------------------------- +; $D1B3-$D1C6 +; Apply alternate PPU control/scroll state. +; +; $10 | $4E -> PPUCTRL +; $50 -> first PPUSCROLL write +; $51 -> second PPUSCROLL write through shared code at $D197 +; --------------------------------------------------------------------------- + +ApplyPpuScrollState: + lda $10 + ora $4E + sta PPUCTRL + + ; Reset the PPUADDR/PPUSCROLL write latch. + lda PPUSTATUS + + lda $50 + sta PPUSCROLL + + lda $51 + + ; Tail-call the existing second-scroll-write path. + ; D1C4: 4C 97 D1 = JMP $D197 + .byte $4C, $97, $D1 + + +; --------------------------------------------------------------------------- +; $D1C7-$D23A +; Build repeated records through the indirect destination pointer $04:$05. +; +; Entry parameters are held in $00-$04 and $0A/$0B. +; $03 packs two counts into its high and low nibbles. +; +; Preserves X and Y. +; --------------------------------------------------------------------------- + +BuildIndirectStructure: + sta $0A + + txa + pha + + tya + pha + + lda #$02 + sta $05 + + lda #$0F + and $03 + sta $07 + + lda $03 + lsr a + lsr a + lsr a + lsr a + sta $06 + tax + + lda #$00 + clc + +@calculate_count: + adc $07 + dex + bne @calculate_count + + sta $08 + + lda $02 + ldy #$01 + +@build_entry: + txa + lsr a + bcs @store_value ; D1F0: B0 0A + + lsr $0A + bcc @store_value ; D1F4: 90 06 + + lda #$FC ; D1F6: A9 FC + sta ($04),y ; D1F8: 91 04 + + ; D1FA: D0 06 = BNE $D202 + bne @store_common + +@store_value: + lda $02 ; D1FC: A5 02 + sta ($04),y ; D1FE: 91 04 + inc $02 ; D200: E6 02 + +@store_common: + iny ; D202: C8 + lda $0B + + sta ($04),y + + iny + iny + iny + + inx + cpx $08 + bne @build_entry + + ldy #$00 + +@next_row: + ldx $06 + + lda $01 + sta $09 + +@write_row: + lda $09 + sta ($04),y + + clc + adc #$08 + sta $09 + + iny + iny + iny + + lda $00 + sta ($04),y + + iny + + dex + bne @write_row + + lda $00 + clc + adc #$08 + sta $00 + + dec $07 + bne @next_row + + pla + tay + + pla + tax + + rts + +; --------------------------------------------------------------------------- +; $D23B-$D24E +; Process two indexed cases through the $D24F and $D260 helpers. +; +; X runs as 1, then 2. +; The current X is preserved across the helper calls. +; $01 also receives the current index. +; --------------------------------------------------------------------------- + +ProcessTwoIndexedEntries: + ldx #$01 + +@next_entry: + txa + pha + + sta $01 + + jsr DisablePpuRendering + jsr InitializePpuMemory + + pla + tax + + inx + cpx #$03 + bcc @next_entry + + rts + +; --------------------------------------------------------------------------- +; $D24F-$D254 +; Disable PPU rendering. +; --------------------------------------------------------------------------- + +DisablePpuRendering: + lda #$00 ; D24F: A9 00 + sta PPUMASK ; D251: 8D 01 20 + rts ; D254: 60 + + +; --------------------------------------------------------------------------- +; $D255-$D25F +; Fill all 256 bytes of OAM shadow RAM ($0200-$02FF) with $F8. +; --------------------------------------------------------------------------- + +FillOamShadowWithF8: + lda #$F8 + ldy #$00 + +@loop: + sta OamBuffer,y + dey + bne @loop + + rts + +; --------------------------------------------------------------------------- +; $D260-$D2A2 +; Initialize/clear PPU memory regions. +; +; Uses $01 as a loop counter supplied by the caller. +; --------------------------------------------------------------------------- + +InitializePpuMemory: + lda PPUSTATUS ; D260: AD 02 20 + + lda $10 ; D263: A5 10 + and #$FB ; D265: 29 FB + jsr $C333 ; D267: 20 33 C3 + + lda #$1C ; D26A: A9 1C + clc ; D26C: 18 + +@calculate_address: + adc #$04 ; D26D: 69 04 + dec TMP01 ; D26F: C6 01 + bne @calculate_address ; D271: D0 FA + + sta $02 ; D273: 85 02 + sta PPUADDR ; D275: 8D 06 20 + + lda #$00 ; D278: A9 00 + sta PPUADDR ; D27A: 8D 06 20 + + ldx #$04 ; D27D: A2 04 + + ldy #$00 ; D27F: A0 00 + lda #$FC ; D281: A9 FC +@fill_fc: + sta PPUDATA ; D283: 8D 07 20 + dey ; D286: 88 + bne @fill_fc ; D287: D0 FA + + dex ; D289: CA + bne @fill_fc ; D28A: D0 F7 + + lda $02 ; D28C: A5 02 + adc #$03 ; D28E: 69 03 + sta PPUADDR ; D290: 8D 06 20 + + lda #$C0 ; D293: A9 C0 + sta PPUADDR ; D295: 8D 06 20 + + ldy #$40 ; D298: A0 40 + lda #$00 ; D29A: A9 00 +@clear_attribute_bytes: + sta PPUDATA ; D29C: 8D 07 20 + dey ; D29F: 88 + bne @clear_attribute_bytes ; D2A0: D0 FA + + rts ; D2A2: 60 + + + +; --------------------------------------------------------------------------- +; $D2A3-$D2FE +; Expand packed source data at $02:$03 into the structure at $0300. +; +; Preserves X and Y. +; --------------------------------------------------------------------------- + +Build0300Descriptor: + txa + pha + + tya + pha + + ldy #$00 + + lda ($02),y + and #$0F + sta $05 + + lda ($02),y + lsr a + lsr a + lsr a + lsr a + sta $04 + + ldx $0300 + +@next_group: + lda $01 + sta $0301,x + jsr Advance0300WriteIndex + + lda $00 + sta $0301,x + jsr Advance0300WriteIndex + + lda $05 + sta $06 + sta $0301,x + +@next_value: + jsr Advance0300WriteIndex + + iny + lda ($02),y + sta $0301,x + + dec $06 + bne @next_value + + jsr Advance0300WriteIndex + + stx $0300 + + clc + lda #$20 + adc TMP00 + sta TMP00 + + lda #$00 + adc TMP01 + sta TMP01 + + dec $04 + bne @next_group + + lda #$00 + sta $0301,x + + pla + tay + + pla + tax + + rts + + +; --------------------------------------------------------------------------- +; $D2FF-$D30F +; Advance X within the $0300 output structure. +; +; If X reaches $3F, terminate/reset the current structure and unwind +; the caller by removing its return address. +; --------------------------------------------------------------------------- + +Advance0300WriteIndex: + inx + + txa + cmp #$3F + bcc @return + + ldx $0300 + + lda #$00 + sta $0301,x + + ; Discard caller return address. + pla + pla + +@return: + rts + +; --------------------------------------------------------------------------- +; $D310-$D325 +; Update runtime countdown values. +; +; If $20 underflows, reload it with $0A and then process counters +; $21-$3D. Otherwise X remains $0E and processing begins at $2F. +; --------------------------------------------------------------------------- + +UpdateRuntimeCounters: + ldx #$0E ; D310: A2 0E + + dec $20 ; D312: C6 20 + bpl @process ; D314: 10 06 + + lda #$0A ; D316: A9 0A + sta $20 ; D318: 85 20 + + ldx #$1C ; D31A: A2 1C + +@process: + lda $21,x ; D31C: B5 21 + beq @next ; D31E: F0 02 + + dec $21,x ; D320: D6 21 + +@next: + dex ; D322: CA + bpl @process ; D323: 10 F7 + + rts ; D325: 60 + +; --------------------------------------------------------------------------- +; $D326-$D346 +; Shift the eight-byte state value at $18-$1F right by one bit. +; +; The bit shifted into carry is derived from bit 1 of $18 and $19. +; --------------------------------------------------------------------------- + +ShiftRuntimeState: + lda $18 + and #$02 + sta $00 + + lda $19 + and #$02 + eor $00 + + clc + beq @shift + + sec + +@shift: + ror $18 + ror $19 + ror $1A + ror $1B + ror $1C + ror $1D + ror $1E + ror $1F + + rts + + +ReadControllers: + lda #$01 + sta JOY1 + ldx #$00 + lda #$00 + sta JOY1 + jsr ReadOneController + inx + jsr ReadOneController + rts +ReadOneController: + ldy #$08 +@read: pha + lda JOY1,x + sta $00 + lsr a + ora $00 + lsr a + pla + rol a + dey + bne @read + stx TMP00 + asl TMP00 + ldx TMP00 + ldy ControllerState,x + sty TMP00 + sta ControllerState,x + sta ControllerState+1,x + and #$FF + bpl @done + bit TMP00 + bpl @done + and #$7F + sta ControllerState+1,x +@done: + rts + +; --------------------------------------------------------------------------- +; $D387-$D3D5: process PPU update command stream. +; $D3C1 is a secondary entry used by NmiPpuUpdate. +; --------------------------------------------------------------------------- + +ProcessPpuUpdateCommand: + sta PPUADDR + + iny + lda ($00),y + sta PPUADDR + + iny + lda ($00),y + asl a + pha + + lda PpuControlShadow + ora #$04 + bcs @set_ppuctrl + + and #$FB + +@set_ppuctrl: + jsr SetPpuControl + + pla + asl a + bcc @decode_count + + ora #$02 + iny + +@decode_count: + lsr a + lsr a + tax + +@next_write: + bcs @write_byte + + iny + +@write_byte: + lda ($00),y + sta PPUDATA + + dex + bne @next_write + + sec + tya + adc $00 + sta $00 + + lda #$00 + adc $01 + sta $01 + + +; --------------------------------------------------------------------------- +; $D3C1: secondary entry. +; Called directly by NmiPpuUpdate with $00:$01 pointing at the command stream. +; --------------------------------------------------------------------------- + +ProcessPpuUpdateStream: + ldx PPUSTATUS + + ldy #$00 + lda ($00),y + bne ProcessPpuUpdateCommand + + lda PPUSTATUS + + lda #$00 + sta PPUSCROLL + sta PPUSCROLL + + rts + + +; --------------------------------------------------------------------------- +; $D3D6-$D46D +; PPU / graphics update data. +; --------------------------------------------------------------------------- + +PpuGraphicsDataD3D6: + +; $D3D6 + .byte $3F, $00, $08, $29, $27, $22, $30, $29 + .byte $27, $18, $36, $3F, $10, $04, $29, $20 + +; $D3E6 + .byte $16, $0F, $00, $3F, $00, $08, $01, $1A + .byte $26, $33, $01, $11, $0C, $2C, $3F, $10 + +; $D3F6 + .byte $04, $01, $20, $16, $0F, $00, $3F, $00 + .byte $08, $02, $26, $1C, $30, $02, $29, $19 + +; $D406 + .byte $39, $3F, $10, $04, $02, $20, $16, $0F + .byte $00, $3F, $00, $08, $00, $26, $22, $30 + +; $D416 + .byte $00, $27, $18, $37, $3F, $10, $04, $00 + .byte $20, $16, $0F, $00, $3F, $00, $08, $02 + +; $D426 + .byte $19, $22, $30, $02, $00, $2D, $10, $3F + .byte $10, $04, $02, $20, $16, $0F, $00, $3F + +; $D436 + .byte $08, $08, $29, $22, $0F, $20, $29, $22 + .byte $0F, $16, $3F, $14, $0C, $29, $13, $20 + +; $D446 + .byte $0F, $29, $31, $1C, $0F, $29, $20, $19 + .byte $0F, $00, $3F, $00, $1C, $02, $0F, $30 + +; $D456 + .byte $21, $02, $15, $02, $2A, $02, $3C, $02 + .byte $30, $02, $30, $02, $27, $02, $20, $16 + +; $D466-$D46D + .byte $0F, $02, $27, $13, $3C, $02, $27, $16 + + +; --------------------------------------------------------------------------- +; $D46E-$D47F +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD46E: + .byte $30, $00 + .byte $23, $D0, $60, $55 + .byte $23, $F0, $50, $AA + .byte $27, $D0, $60, $55 + .byte $27, $F0, $50, $AA + +; --------------------------------------------------------------------------- +; $D480-$D48F +; Four-byte graphics / PPU records. +; --------------------------------------------------------------------------- + +PpuRecordDataD480: + .byte $20, $00, $60, $3F + .byte $20, $20, $60, $3E + .byte $20, $40, $60, $3F + .byte $20, $60, $60, $3E + + +PpuRecordDataD490: + .byte $20, $80, $60, $3F + .byte $24, $00, $60, $3F + .byte $24, $20, $60, $3E + .byte $24, $40, $60, $3F + +; --------------------------------------------------------------------------- +; $D4A0-$D4AF +; Four-byte graphics / PPU records. +; --------------------------------------------------------------------------- + +PpuRecordDataD4A0: + .byte $24, $60, $60, $3E + .byte $24, $80, $60, $3F + .byte $20, $A0, $60, $30 + .byte $20, $C0, $60, $FE + +; --------------------------------------------------------------------------- +; $D4B0-$D4BF +; Four-byte graphics / PPU records. +; --------------------------------------------------------------------------- + +PpuRecordDataD4B0: + .byte $20, $E0, $60, $FE + .byte $24, $A0, $60, $30 + .byte $24, $C0, $60, $FE + .byte $24, $E0, $60, $FE + +; --------------------------------------------------------------------------- +; $D4C0-$D4E0 +; PPU / graphics command data. +; --------------------------------------------------------------------------- + +PpuCommandDataD4C0: + .byte $20, $AB, $83, $31, $34, $37 + .byte $20, $B4, $83, $33, $36, $39 + .byte $20, $AC, $48, $32 + .byte $20, $EC, $48, $38 + .byte $20, $CC, $08 + .byte $17, $12, $17, $1D, $0E, $17, $0D, $18, $22 + .byte $00 + + +; --------------------------------------------------------------------------- +; $D4E1-$D4F0 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD4E1: + .byte $60, $3B, $22, $20 + .byte $60, $3C, $22, $40 + .byte $60, $3D, $22, $60 + .byte $60, $3B, $22, $80 + + +; --------------------------------------------------------------------------- +; $D4F1-$D500 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD4F1: + .byte $60, $3C, $22, $A0 + .byte $60, $3D, $26, $00 + .byte $60, $3B, $26, $20 + .byte $60, $3C, $26, $40 + +; --------------------------------------------------------------------------- +; $D501-$D510 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD501: + .byte $60, $3D, $26, $60 + .byte $60, $3B, $26, $80 + .byte $60, $3C, $26, $A0 + .byte $60, $3D, $23, $00 + + +; --------------------------------------------------------------------------- +; $D511-$D520 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD511: + .byte $7F, $FE, $23, $3F + .byte $7F, $FE, $23, $7E + .byte $7F, $FE, $23, $9D + .byte $63, $FE, $27, $00 + + +; --------------------------------------------------------------------------- +; $D521-$D530 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD521: + .byte $7F, $FE, $00, $24 + .byte $AB, $4A, $F2, $24 + .byte $AA, $01, $96, $24 + .byte $B5, $01, $97, $24 + + +; --------------------------------------------------------------------------- +; $D531-$D540 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD531: + .byte $CB, $03, $93, $94 + .byte $95, $23, $F1, $06 + .byte $AF, $AA, $AE, $AB + .byte $AA, $AF, $23, $23 + +; --------------------------------------------------------------------------- +; $D541-$D550 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD541: + .byte $07, $B7, $B7, $03 + .byte $1B, $0D, $B7, $B7 + .byte $23, $22, $83, $B5 + .byte $B0, $B2, $23, $2A + + +; --------------------------------------------------------------------------- +; $D551-$D560 +; Graphics / PPU record data. +; --------------------------------------------------------------------------- + +PpuRecordDataD551: + .byte $83, $B6, $B1, $B4 + .byte $23, $63, $47, $B3 + .byte $23, $36, $07, $B7 + .byte $B7, $1D, $12, $16 + + +; --------------------------------------------------------------------------- +; $D561-$D917 +; Graphics, UI, text, tile, and runtime lookup data. +; +; Executable code resumes at $D918. +; --------------------------------------------------------------------------- + +GraphicsAndUiDataD561: + +; $D561 + .byte $0E, $B7, $23, $35, $83, $B5, $B0, $B2 + .byte $23, $3D, $83, $B6, $B1, $B4, $23, $76 + +; $D571 + .byte $47, $B3, $23, $2C, $08, $4C, $5A, $1D + .byte $0E, $16, $19, $7A, $8B, $23, $4B, $0A + +; $D581 + .byte $4B, $4D, $5B, $FC, $FC, $FC, $FC, $7B + .byte $4D, $8C, $23, $6D, $06, $5C, $6A, $6A + +; $D591 + .byte $6A, $6A, $7C, $23, $8D, $06, $5D, $6B + .byte $6C, $6B, $6C, $8A, $00, $20, $89, $4E + +; $D5A1 + .byte $FC, $20, $A9, $0E, $FC, $FC, $0E, $21 + .byte $0C, $12, $1D, $0E, $0B, $12, $14, $0E + +; $D5B1 + .byte $FC, $FC, $00, $20, $89, $4E, $FC, $20 + .byte $AA, $0C, $0D, $0E, $1C, $12, $10, $17 + +; $D5C1 + .byte $FC, $1D, $1B, $0A, $0C, $14, $00, $22 + .byte $E7, $12, $12, $1D, $F9, $1C, $FC, $0A + +; $D5D1 + .byte $FC, $17, $0E, $20, $FC, $1B, $0E, $0C + .byte $18, $1B, $0D, $FA, $00, $22, $E5, $56 + +; $D5E1 + .byte $FC, $22, $EB, $09, $10, $0A, $16, $0E + .byte $FC, $18, $1F, $0E, $1B, $00, $22, $E7 + +; $D5F1 + .byte $12, $1D, $1B, $22, $FC, $1D, $11, $0E + .byte $FC, $17, $0E, $21, $1D, $FC, $1D, $1B + +; $D601 + .byte $0A, $0C, $14, $00, $22, $E5, $56, $FC + .byte $22, $EC, $07, $1D, $12, $16, $0E, $FC + +; $D611 + .byte $1E, $19, $00, $23, $E0, $50, $FF, $22 + .byte $0D, $07, $15, $18, $0A, $0D, $12, $17 + +; $D621 + .byte $10, $00, $23, $E0, $50, $FF, $22, $0D + .byte $06, $1C, $0A, $1F, $12, $17, $10, $00 + +; $D631 + .byte $22, $8E, $05, $0E, $1B, $1B, $18, $1B + .byte $00, $21, $84, $58, $27, $23, $44, $58 + +; $D641 + .byte $27, $21, $A4, $CD, $27, $21, $BB, $CD + .byte $27, $00, $23, $E3, $4B, $AA, $23, $F2 + +; $D651 + .byte $44, $FF, $20, $83, $5A, $27, $20, $A3 + .byte $5A, $27, $21, $83, $5A, $27, $21, $A3 + +; $D661 + .byte $5A, $27, $20, $C3, $C6, $27, $20, $C4 + .byte $C6, $27, $20, $DB, $C6, $27, $20, $DC + +; $D671 + .byte $C6, $27, $21, $07, $12, $98, $9A, $9C + .byte $9E, $A0, $A2, $A4, $A6, $A8, $98, $9A + +; $D681 + .byte $98, $AA, $A4, $AC, $AE, $98, $9A, $21 + .byte $27, $12, $99, $9B, $9D, $9F, $A1, $A3 + +; $D691 + .byte $A5, $A7, $A9, $99, $9B, $99, $AB, $A5 + .byte $AD, $AF, $99, $9B, $22, $0C, $0B, $1C + +; $D6A1 + .byte $0E, $15, $0E, $0C, $1D, $12, $18, $17 + .byte $FC, $0A, $22, $4C, $0B, $1C, $0E, $15 + +; $D6B1 + .byte $0E, $0C, $1D, $12, $18, $17, $FC, $0B + .byte $22, $8C, $06, $0D, $0E, $1C, $12, $10 + +; $D6C1 + .byte $17, $23, $09, $0E, $3A, $01, $09, $08 + .byte $04, $FC, $17, $12, $17, $1D, $0E, $17 + +; $D6D1 + .byte $0D, $18, $00, $23, $CA, $03, $40, $50 + .byte $50, $23, $D3, $42, $55, $23, $E2, $44 + +; $D6E1 + .byte $AA, $20, $E6, $43, $26, $20, $F7, $43 + .byte $26, $20, $EB, $09, $0C, $11, $0A, $15 + +; $D6F1 + .byte $15, $0E, $17, $10, $0E, $21, $0D, $04 + .byte $1B, $0A, $0C, $0E, $22, $0D, $06, $1D + +; $D701 + .byte $1B, $0A, $0C, $14, $79, $22, $69, $0D + .byte $01, $FC, $FC, $02, $FC, $FC, $03, $FC + +; $D711 + .byte $FC, $04, $FC, $FC, $05, $00, $23, $C9 + .byte $06, $0C, $07, $55, $55, $0D, $03, $23 + +; $D721 + .byte $DA, $44, $50, $23, $DE, $01, $10, $23 + .byte $E2, $05, $55, $55, $99, $AA, $22, $23 + +; $D731 + .byte $E9, $01, $C0, $23, $EA, $44, $F0, $23 + .byte $EE, $01, $30, $20, $87, $02, $52, $54 + +; $D741 + .byte $20, $97, $02, $52, $54, $20, $A4, $05 + .byte $26, $26, $FC, $53, $55, $20, $B7, $05 + +; $D751 + .byte $53, $55, $FC, $26, $26, $20, $8B, $09 + .byte $0C, $11, $0A, $15, $15, $0E, $17, $10 + +; $D761 + .byte $0E, $20, $AE, $04, $1B, $0A, $0C, $0E + .byte $21, $E8, $09, $0B, $0E, $1C, $1D, $FC + +; $D771 + .byte $1D, $12, $16, $0E, $22, $28, $09, $22 + .byte $18, $1E, $1B, $FC, $1D, $12, $16, $0E + +; $D781 + .byte $22, $68, $07, $1B, $0A, $17, $14, $12 + .byte $17, $10, $21, $2F, $01, $42, $21, $4E + +; $D791 + .byte $03, $43, $44, $45, $00, $23, $CA, $04 + .byte $40, $50, $50, $10, $23, $D3, $42, $AA + +; $D7A1 + .byte $23, $DB, $02, $AA, $22, $23, $E2, $43 + .byte $55, $23, $EA, $43, $55, $20, $E6, $43 + +; $D7B1 + .byte $26, $20, $F7, $43, $26, $20, $EB, $0A + .byte $0E, $21, $0C, $12, $1D, $0E, $0B, $12 + +; $D7C1 + .byte $14, $0E, $21, $8C, $07, $FC, $1D, $1B + .byte $0A, $0C, $14, $FC, $21, $6E, $03, $ED + +; $D7D1 + .byte $FC, $F0, $21, $AE, $03, $EE, $EF, $F1 + .byte $22, $2B, $09, $0B, $0E, $1C, $1D, $FC + +; $D7E1 + .byte $1D, $12, $16, $0E, $22, $6B, $83, $B5 + .byte $B0, $B2, $22, $73, $83, $B6, $B1, $B4 + +; $D7F1 + .byte $22, $6C, $47, $B7, $22, $AC, $47, $B3 + .byte $00, $23, $CB, $4B, $55, $23, $DB, $42 + +; $D801 + .byte $F0, $23, $E3, $01, $FF, $23, $EB, $01 + .byte $FF, $23, $F3, $42, $0F, $20, $CC, $0B + +; $D811 + .byte $19, $15, $0A, $22, $FC, $16, $18, $0D + .byte $0E, $FC, $0A, $21, $2C, $0B, $19, $15 + +; $D821 + .byte $0A, $22, $FC, $16, $18, $0D, $0E, $FC + .byte $0B, $21, $EC, $06, $0D, $0E, $1C, $12 + +; $D831 + .byte $10, $17, $22, $4C, $04, $1C, $0A, $1F + .byte $0E, $22, $AC, $04, $15, $18, $0A, $0D + +; $D841 + .byte $23, $0C, $05, $1B, $0E, $1C, $0E, $1D + .byte $00, $23, $23, $18, $0A, $0B, $0C, $0D + +; $D851 + .byte $0E, $0F, $10, $11, $12, $13, $14, $15 + .byte $16, $17, $18, $19, $1A, $1B, $1C, $FE + +; $D861 + .byte $B8, $FE, $24, $25, $00, $FF, $FF, $34 + .byte $34, $18, $34, $03, $02, $03, $02, $09 + +; $D871 + .byte $06, $08, $0F, $03, $02, $02, $02, $08 + .byte $05, $07, $0F, $0C, $09, $0A, $07, $0C + +; $D881 + .byte $0C, $0C, $00, $0C, $0A, $0B, $08, $0C + .byte $0C, $0C, $06, $03, $04, $02, $0B, $08 + +; $D891 + .byte $09, $58, $60, $69, $71, $79, $80, $0A + .byte $18, $20, $28, $30, $38, $40, $48, $50 + +; $D8A1 + .byte $50, $97, $90, $89, $11, $A6, $9F, $B3 + .byte $01, $00, $02, $02, $03, $00, $09, $08 + +; $D8B1 + .byte $08, $10, $10, $10, $02, $10, $10, $10 + .byte $01, $09, $09, $09, $01, $09, $03, $04 + +; $D8C1 + .byte $28, $4C, $70, $39, $01, $01, $01, $07 + .byte $00, $01, $01, $02, $00, $11, $22, $33 + +; $D8D1 + .byte $44, $66, $88, $AA, $02, $01, $01, $0E + .byte $1A, $26, $32, $00, $0C, $18, $A0, $40 + +; $D8E1 + .byte $40, $ED, $21, $21, $E2, $E8, $9E, $A6 + .byte $92, $9A, $86, $8E, $7A, $82, $E6, $AF + +; $D8F1 + .byte $E0, $E2, $CF, $D3, $D7, $CF, $3F, $0F + .byte $07, $07, $08, $20, $11, $11, $23, $33 + +; $D901 + .byte $53, $83, $D3, $F6, $D9, $D9, $D9, $D9 + .byte $D9, $D9, $BF, $C3, $C6, $08, $0A, $0F + +; $D911-$D917 + .byte $14, $18, $08, $14, $20, $2C, $38 + +; $D918-$D923 +UpdateFourObjects: + ldx #$03 + +@loop: + jsr DispatchSlotState + jsr UpdateSlotDerivedPosition + dex + bpl @loop + + rts + + +; --------------------------------------------------------------------------- +; $D924-$D932: dispatch per-slot state through split pointer tables. +; X = current slot +; --------------------------------------------------------------------------- + +DispatchSlotState: + ldy $9C,x + + lda $D8FF,y + sta $00 + + lda $D905,y + sta $01 + + jmp ($0000) + + + +; --------------------------------------------------------------------------- +; $D933-$D952: per-slot state handler. +; X = current slot. +; --------------------------------------------------------------------------- + +SlotStateHandlerD933: + lda $0370,x + beq @advance_state + + lda $3F + lsr a + bcs @return + + inc $0390,x + + ldy $03E4,x + + lda $80,x + clc + adc $D90E,y + cmp $0390,x + bcs @return + +@advance_state: + inc $9C,x + + ; Original D950: D0 2C = BNE $D97E. + bne SlotStateTailD97E + +@return: + rts + + +; --------------------------------------------------------------------------- +; $D953-$D96D: compare per-slot value and set direction/state. +; --------------------------------------------------------------------------- + +SlotStateHandlerD953: + txa + tay + beq @compare + + lda $BC,x + beq @compare + + ldy #$04 + +@compare: + lda $B8,x + cmp $D8C4,y + beq SlotStateResetD96E + + ldy #$01 + bcc @store_direction + + ldy #$FF + +@store_direction: + tya + sta $DC,x + rts + + +; --------------------------------------------------------------------------- +; $D96E-$D97B: clear fields and advance slot state. +; --------------------------------------------------------------------------- + +SlotStateResetD96E: + lda #$00 + +SlotStateAdvanceD970: + sta $0398,x + sta $DC,x + + inc $9C,x + + txa + bne SlotStateTailD97E + + lda #$02 + sta $FD + +; --------------------------------------------------------------------------- +; $D97E-$D982: shared tail. +; Nonzero X enters directly here, so $FD is only changed for slot 0. +; --------------------------------------------------------------------------- + +SlotStateTailD97E: + lda #$08 + sta $2A,x + rts + +; --------------------------------------------------------------------------- +; $D983-$D9D2: per-slot state handler. +; X = current slot. +; --------------------------------------------------------------------------- + +SlotStateHandlerD983: + lda $0370,x + beq @advance_state + + lda $2A,x + bne @return + + txa + ora $03F7 + bne @update_timing + + ldy $49 + + lda $14 + and #$C0 + beq @store_49 + + sta $49 + tya + beq @decrement_position + +@store_49: + sta $49 + +@update_timing: + lda $3F + and #$0F + bne @skip_decrement + +@decrement_position: + dec $0390,x + +@skip_decrement: + and #$07 + bne @check_position + + lda $0398,x + eor #$01 + sta $0398,x + +@check_position: + lda $0390,x + sec + sbc #$08 + cmp $80,x + bcs @return + +@advance_state: + lda #$03 + sta $36,x + + inc $9C,x + + lda #$00 + cpx #$03 + bne @return + + sta $03A8 + sta $0373 + +@return: + rts + +; --------------------------------------------------------------------------- +; $D9D3-$D9F5: per-slot state handler. +; --------------------------------------------------------------------------- + +SlotStateHandlerD9D3: + lda $36,x + bne @return + + txa + bne @nonzero_slot + + lda $03E0 +; Original D9DD: D0 31 = BNE $DA10 +; .byte $D0, $31 + bne SlotResetStateDA10 + + lda $03F7 + beq @advance_state + + jmp $CA85 + + +@nonzero_slot: + lda $22 + bne @return + + lda #$01 + sta $DC,x + + lda #$18 + sta $22 + + +@advance_state: + inc $9C,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $D9F6-$DA14: per-slot state/direction handler. +; --------------------------------------------------------------------------- + +; --------------------------------------------------------------------------- +; $D9F6-$DA14: per-slot state/direction handler. +; --------------------------------------------------------------------------- + +SlotStateHandlerD9F6: + lda $94,x + ora $DC,x + bne SlotSetDirectionDA03 + + lda $B8,x + cmp $D8C4,x + beq SlotStateReturnDA14 + + +; $DA03 +SlotSetDirectionDA03: + lda #$01 + sta $DC,x + + txa + beq SlotResetStateDA10 + + lda $B8,x + cmp #$08 + bcc SlotStateReturnDA14 + + +; $DA10 +; Shared entry from the preceding $D9D3 handler. +SlotResetStateDA10: + lda #$00 + sta $9C,x + + +; $DA14 +SlotStateReturnDA14: + rts + +; --------------------------------------------------------------------------- +; $DA15-$DA25: calculate per-slot derived position. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlotDerivedPosition: + lda $0390,x + sec + sbc $80,x + sbc #$08 + asl a + + clc + adc $B8,x + adc #$04 + + sta $6C,x + rts + +; --------------------------------------------------------------------------- +; $DA26-$DA69: update four runtime slots and dispatch optional follow-up. +; --------------------------------------------------------------------------- + +UpdateRuntimeSlots: + lda $4F + beq RuntimeSlotsReturnDA69 + + ldx #$03 + +@slot_loop: + jsr UpdateSlotPosition + jsr UpdateSlotDC1A + jsr UpdateSlotState + jsr UpdateSlot0374 + + dex + bpl @slot_loop + + jsr UpdateAccumulator + jsr UpdateSlotPositions + jsr UpdateGlobalPosition + + lda $3C + bne RuntimeSlotsReturnDA69 + + ldy $03BC + + lda $D8EF,y + sta $00 + + lda $D8F1,y + sta $01 + + jmp ($0000) + + +; --------------------------------------------------------------------------- +; $DA58: secondary entry. +; Update the position pair at $0394,X / $60,X. +; --------------------------------------------------------------------------- + +UpdateSlotPosition: + lda $94,x + sta $60,x + + lda $90,x + clc + adc $0394,x + sta $0394,x + bcc RuntimeSlotsReturnDA69 + + inc $60,x + + +; $DA69 +RuntimeSlotsReturnDA69: + rts + +; --------------------------------------------------------------------------- +; $DA6A-$DA79: accumulate $60 into $12 and toggle $4D on carry. +; --------------------------------------------------------------------------- + +UpdateAccumulator: + lda $12 + clc + adc $60 + sta $12 + bcc @return + + lda $4D + eor #$01 + sta $4D + +@return: + rts + +; --------------------------------------------------------------------------- +; $DA7A-$DA9E: update active slot positions using slot-0 delta in $60. +; --------------------------------------------------------------------------- + +UpdateSlotPositions: + ldx #$03 + +@slot_loop: + lda $A8,x + beq @next_slot + + lda $60,x + clc + adc $80,x + bcc @no_high_increment + + inc $84,x + +@no_high_increment: + sec + sbc $60 + sta $80,x + bcs @no_high_decrement + + dec $84,x + +@no_high_decrement: + lda $0390,x + sec + sbc $60 + sta $0390,x + +@next_slot: + dex + bne @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $DA9F-$DAE2 +; Search/update helper operating across slots 3..1. +; --------------------------------------------------------------------------- + +SlotSearchDA9F: + ldx #$03 + + lda $3B + bne @next_outer + + +@check_entry: + txa + eor $4C + ora $A8,x + ora $23 + ora $0373 + ora $03A8 + bne @next_outer + + lda $18,x + cmp #$A0 + bcc @next_outer + + stx $00 + + and #$03 + tay + iny + + lda $94 + and #$02 + sta $84,x + + ldx #$03 + + +@search_slots: + sty $01 + + lda $70,x + cmp $01 + bne @next_slot + + iny + cpy #$05 + bcc @next_slot + + ldy #$01 + + +@next_slot: + dex + bne @search_slots + + ldx $00 + jsr InitializeSlot + + +; $DADF +@next_outer: + dex + bne @check_entry + + rts + +; --------------------------------------------------------------------------- +; $DAE3-$DB4A: initialize/update the current slot. +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeSlot: + jsr ResetSlotState + + dey + lda $D8D8,y + sta $B8,x + + ldy $84,x + lda $D8DF,y + sta $80,x + + lda #$06 + sta $AC,x + + lda $E0 + clc + adc $D8E2,y + and #$3F + sta $E0,x + + lda $64 + sta $64,x + + tya + beq @configure_slot + + ldy #$05 + jsr LoadIndexedValueE7FF + cmp #$E4 + beq @configure_slot + + cmp #$3B + bcc @return_zero + + cmp #$93 + bcs @return_zero + + ldy #$02 + jsr LoadIndexedValueE7FF + cmp #$E6 + bne @configure_slot + + lda $B8,x + cmp #$20 + bcc @small_value + + lda #$30 + sta $BC,x + + lda #$01 + bne @store_a4 + +@small_value: + lda #$03 + +@store_a4: + sta RiderMotionState,x + +@configure_slot: + ldy $84,x + jsr ConfigureSlot + + lda $84,x + eor #$02 + asl a + sta $94,x + + lda #$20 + sta $039B,x + + lsr a + sta $23 + + lda #$01 + sta $A8,x + + rts + +@return_zero: + lda #$00 + rts + +; --------------------------------------------------------------------------- +; $DB50-$DB94: configure per-slot $74/$78 state. +; X = current slot. +; Y supplies a table index on some paths. +; --------------------------------------------------------------------------- + +ConfigureSlot: + lda $52 + beq SlotConfigCheckIndexDB58 + + lda #$03 + bne SlotConfigStore78DB5F + + +SlotConfigCheckIndexDB58: + cpx #$03 + beq SlotConfigSpecialDB6C + + +; $DB5C +; Shared internal entry used by several fallback paths. +SlotConfigFromTableDB5C: + lda $D8D5,y + + +; $DB5F +SlotConfigStore78DB5F: + sta $78,x + + lda $1B,x + and #$0F + tay + + lda $D8CD,y + sta $74,x + rts + + +; $DB6C +SlotConfigSpecialDB6C: + lda $84,x + lsr a + lsr a + bcs SlotConfigFromTableDB5C + + lda $94 + beq SlotConfigFromTableDB5C + + sta $78,x + + lda $90 + sta $74,x + + lda $84,x + beq SlotConfigIncrementCheckDB8C + + lda $80,x + cmp #$40 + bcs SlotConfigFromTableDB5C + + lda $70 + cmp $73 + beq SlotConfigReturnDB94 + + +; $DB8C +SlotConfigIncrementCheckDB8C: + lda $78,x + cmp #$04 + bcs SlotConfigReturnDB94 + + inc $78,x + + +; $DB94 +SlotConfigReturnDB94: + rts + +; --------------------------------------------------------------------------- +; $DB95-$DBC3: clear/reset per-slot runtime state. +; X = current slot. +; --------------------------------------------------------------------------- + +ResetSlotState: + lda #$00 + + sta $A8,x + sta $B8,x + sta $BC,x + sta $A0,x + sta RiderMotionState,x + sta $58,x + sta $CC,x + + sta $036C,x + + sta $90,x + sta $94,x + sta $98,x + sta $9C,x + sta $B0,x + sta $D4,x + + sta $0388,x + + sta $C0,x + + sta $039C,x + + sta $DC,x + sta $70,x + + sta $0370,x + + rts +; --------------------------------------------------------------------------- +; $DBC4-$DBFD: conditional slot reset/setup. +; --------------------------------------------------------------------------- + +SlotSetupDBC4: + lda $03A8 + + ; DBC7: F0 FA = BEQ $DBC3 + .byte $F0, $FA + + lda $AB + beq @initialize + + ldx #$03 + + lda $84,x + lsr a + + ; DBD2: B0 EF = BCS $DBC3 + .byte $B0, $EF + + lsr a + bcs @reset_slot + + lda $80,x + cmp #$EA + + ; DBDB: B0 E6 = BCS $DBC3 + .byte $B0, $E6 + +@reset_slot: + jmp ResetSlotState + +@initialize: + ldy #$00 + sty $03A8 + + iny + + ldx #$02 + stx $87 + inx + + jsr InitializeSlot + + ; DBEE: F0 D3 = BEQ $DBC3 + .byte $F0, $D3 + + lda #$00 + sta $97 + + lda #$92 + sta $8F + sta $0373 + + jmp $DC69 + +; --------------------------------------------------------------------------- +; $DBFE-$DC19: accumulate slot-0 delta into global position/state. +; --------------------------------------------------------------------------- + +UpdateGlobalPosition: + lda $60 + lsr a + ror a + ror a + ror a + + clc + adc $03BF + sta $03BF + + lda $60 + adc $50 + sta $50 + bcc @return + + lda $4E + eor #$01 + sta $4E + +@return: + rts + +; --------------------------------------------------------------------------- +; $DC1A-$DC96: per-slot update/check routine. +; --------------------------------------------------------------------------- + +UpdateSlotDC1A: + lda $B0,x + + ; DC1C: BEQ $DC19 + .byte $F0, $FB + + jsr CalculateSlotRemaining + sta $01 + + cmp $8C,x + + ; DC25: BCS $DC19 + .byte $B0, $F2 + + lda $8C,x + cmp #$A8 + + ; DC2B: BCS $DC19 + .byte $B0, $EC + + lda $0364,x + + ; DC30: BEQ $DC19 + .byte $F0, $E7 + + txa + bne @clear_fields + + lda #$02 + sta $FF + +@clear_fields: + lda #$00 + sta $B0,x + sta $0364,x + + lda $98,x + bne SlotUpdateCommonDC87 + + lda $52 + bne SlotUpdateAlternateDC8E + + ldy $0388,x + sta $0388,x + + tya + bne SlotUpdateCommonDC87 + + lda $D4,x + ldy $94,x + cpy #$02 + bpl @table_index_ready + + clc + adc #$08 + +@table_index_ready: + tay + + lda $AC,x + cmp $D86C,y + bcc @set_flag + + cmp $D87C,y + bcc @check_position + +@set_flag: + lda #$FF + sta $98,x + rts + +@check_position: + lda $0368,x + cmp $AC,x + beq SlotUpdateCommonDC87 + + lda $D4,x + ora $CC,x + bne SlotUpdateCommonDC87 + + lda #$04 + sta $0374,x + + lsr a + sta $0388,x + + lsr a + sta $B0,x + + +SlotUpdateCommonDC87: + dec $01 + lda $01 + sta $8C,x + +SlotUpdateReturnDC8D: + rts + + +SlotUpdateAlternateDC8E: + lda $D4,x + bne SlotUpdateReturnDC8D + + lda #$06 + sta $AC,x + rts + + +; --------------------------------------------------------------------------- +; $DC97-$DC9F: calculate remaining value after two slot fields. +; --------------------------------------------------------------------------- + +CalculateSlotRemaining: + lda $03F1 + + +; --------------------------------------------------------------------------- +; $DC9A: secondary entry. +; Subtract $BC,X and $B8,X from the value already in A. +; --------------------------------------------------------------------------- + +CalculateSlotRemainingFromADC9A: + sec + sbc $BC,x + sbc $B8,x + rts + + +; --------------------------------------------------------------------------- +; $DCA0-$DCC6: initialize/update per-slot $0368 value. +; --------------------------------------------------------------------------- + +UpdateSlot0368DCA0: + ldy $D4,x + + lda $52 + cmp #$01 + bne @from_table + + txa + ora $98 + ora $9C + ora $58 + bne @from_table + + lda #$0A + bne @store + +@from_table: + lda $D88B,y + +@store: + sta $0368,x + + lda RiderMotionState,x + cmp #$01 + bne @return + + lda #$06 + sta $0368,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $DCC7-$DCDD: adjust $AC,X toward the value supplied in A. +; +; Entry: +; A = target value +; X = current slot +; Y = value stored to $2A,X when $2A,X is zero +; --------------------------------------------------------------------------- + +AdjustSlotAC: + sta $00 + + lda $2A,x + bne @return + + sty $2A,x + + lda $AC,x + cmp $00 + beq @return + + bcc @increment + + dec $AC,x + dec $AC,x + +@increment: + inc $AC,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $DCDE-$DCF1: update per-slot $0374 countdown/state. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlot0374: + lda $0374,x + beq @return + + dec $0374,x + + lda $5C,x + and #$C0 + beq @return + + lda #$00 + sta $0374,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $DCF2-$DD37: update per-slot state/timing fields. +; --------------------------------------------------------------------------- + +UpdateSlotState: + lda $B0,x + beq StateDCF2ReturnDD37 + + cmp #$02 + beq StateDCF2Mode2DD11 + + +; $DCFA +StateDCF2ZeroDCFA: + lda #$00 + beq StateDCF2Store0384DD08 + +StateDCF2Increment94DCFE: + lda $98,x + bne StateDCF2ZeroDCFA + + inc $94,x + bne StateDCF2ZeroDCFA + + +; --------------------------------------------------------------------------- +; $DD06 +; Secondary entry: use current $94,X as the value stored in $0384,X, +; initialize slot state at $DD38, then join the common $DD1A path. +; --------------------------------------------------------------------------- + +UpdateSlotFrom94DD06: + lda $94,x + + +; $DD08 +StateDCF2Store0384DD08: + sta $0384,x + + jsr InitializeSlotState + jmp StateDCF2CommonDD1A +; $DD11 +StateDCF2Mode2DD11: + lda $5C,x + lsr a + bcc StateDCF2CommonDD1A + + lda $4C + beq StateDCF2ReturnDD37 + + +; $DD1A +StateDCF2CommonDD1A: + lda $5C,x + and #$03 + tay + + lda $D868,y + sta $038C,x + + lda $8C,x + sta $01 + + jsr UpdateSlotAccumulator + sta $8C,x + + lda $CC,x + bne StateDCF2ReturnDD37 + + lda #$01 + sta $0364,x + + +; $DD37 +StateDCF2ReturnDD37: + rts + + +; --------------------------------------------------------------------------- +; $DD38-$DD6E: initialize per-slot position/state fields. +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeSlotState: + lda #$02 + sta $B0,x + + lda #$0F + sta $0380,x + + lda $90,x + clc + adc #$AF + sta $0378,x + + lda $94,x + adc #$00 + sta $037C,x + + lda $0388,x + cmp #$02 + bne @check_slot + + lsr $037C,x + ror $0378,x + +@check_slot: + txa + bne @return + + lda $94,x + asl a + asl a + asl a + asl a + eor #$30 + bne @store_ff + + lda #$08 + +@store_ff: + sta $FF + +@return: + rts + +; --------------------------------------------------------------------------- +; $DD6F-$DD8C: update per-slot accumulated value and calculate result. +; X = current slot. +; $01 participates in the calculation and A contains the final result. +; --------------------------------------------------------------------------- + +UpdateSlotAccumulator: + lda $0380,x + adc $038C,x + sta $0380,x + + lda $0384,x + adc #$00 + sta $0384,x + + lda $01 + sbc $037C,x + sta $01 + + lda $01 + adc $0384,x + + rts + +; --------------------------------------------------------------------------- +; $DD8D-$DDD0: global state/setup dispatcher. +; --------------------------------------------------------------------------- + +GlobalStateUpdateDD8D: + lda $3C + ora $03E0 + + ; DD92: F0 F8 = BEQ $DD8C + .byte $F0, $F8 + + lda $3C + cmp #$08 + beq StateUpdateMode8DDBF + + bcc StateUpdateCopyTemplateDDCE + + lda $94 + ora $98 + ora $9C + bne StateUpdateInitialize02C0DDBC + + lda $58 + beq StateUpdateSet90DDAE + + lda #$C0 + sta $90 + bne StateUpdateInitialize02C0DDBC + + +StateUpdateSet90DDAE: + sta $90 + + ldx #$01 + + lda $B8 + cmp $D8C4 + bne StateUpdateStoreDCDDBA + + dex + + +StateUpdateStoreDCDDBA: + stx $DC + + +; $DDBC +StateUpdateInitialize02C0DDBC: + jmp Initialize02C0Entries + + +; $DDBF +StateUpdateMode8DDBF: + lda #$00 + sta $03E0 + + lda #$05 + sta $03B6 + sta $9C + sta $0374 + + +; $DDCE +StateUpdateCopyTemplateDDCE: + jmp Copy02C0Template +; --------------------------------------------------------------------------- +; $DDD1-$DE30: update four runtime slots. +; --------------------------------------------------------------------------- + +UpdateTriggeredSlots: + ldx #$03 + +@slot_loop: + lda $98,x + beq @next_slot + + ldy #$04 + + txa + bne @store_index + + sta $03A9 + sta $FC + + lda $4C + bne @select_index + + lda #$01 + sta $FD + +@select_index: + lda $1B + and #$03 + tay + +@store_index: + tya + sta $03E4,x + + lda $4C + lsr a + bcs @next_slot + + lda $94,x + bne @update_ac + + lda $B0,x + ora $58,x + bne @set_90 + + sta $90,x + sta $60,x + sta $98,x + + lda #$06 + sta $AC,x + + lda $80,x + clc + adc #$08 + sta $0390,x + + jsr UpdateSlotDerivedPosition + + lda #$02 + sta $0398,x + + lsr a + sta $9C,x + + bne @next_slot + +@set_90: + lda #$88 + sta $90,x + +@update_ac: + lda $AC,x + clc + adc $98,x + and #$0F + sta $AC,x + +@next_slot: + dex + bpl @slot_loop + + rts + + +; --------------------------------------------------------------------------- +; $DE31-$DEBA: update global state and build $02C0 display entries. +; --------------------------------------------------------------------------- +UpdateDisplayState: + lda $24 + cmp #$01 + beq StateDE31Mode1DEA6 + + ldy $4F + bne StateDE31ReturnDEA5 + + cmp #$60 + bcs StateDE31SelectValueDE4E + + lda $03A9 + beq StateDE31SelectValueDE4E + + lda $5C + and #$C0 + beq StateDE31SelectValueDE4E + + lda #$01 + sta $FE + + +; $DE4E +StateDE31SelectValueDE4E: + ldy #$F0 + + lda $24 + cmp #$10 + bcs StateDE31ValueReadyDE5E + + ldy #$F2 + + cmp #$08 + bcs StateDE31ValueReadyDE5E + + ldy #$F4 + + +; $DE5E +StateDE31ValueReadyDE5E: + cmp #$72 + bne StateDE31SetupLoopDE66 + + lda #$20 + sta $FB + + +; $DE66 +StateDE31SetupLoopDE66: + sty $00 + + lda $03B4 + sta $01 + + ldy #$07 + + +; $DE6F +StateDE31LoopDE6F: + tya + asl a + asl a + tax + + lda $D8E7,y + sta $02C0,x + + lda $00 + sta $02C1,x + + lda #$00 + sta $02C2,x + + lda $01 + sta $02C3,x + + tya + lsr a + bcc StateDE31SubtractDE91 + + inc $02C1,x + bne StateDE31NextDE9A + + +; $DE91 +StateDE31SubtractDE91: + lda $01 + sec + sbc #$10 + sta $01 + bcc StateDE31AfterLoopDE9D + + +; $DE9A +StateDE31NextDE9A: + dey + bpl StateDE31LoopDE6F + + +; $DE9D +StateDE31AfterLoopDE9D: + lda $34 + beq StateDE31ReturnDEA5 + + lda #$88 + sta $24 + + +; $DEA5 +StateDE31ReturnDEA5: + rts + + +; $DEA6 +StateDE31Mode1DEA6: + lda #$01 + sta $4F + + lda $03B4 + sec + sbc $60 + sta $03B4 + + bcc StateDE31ReturnDEA5 + + lda #$02 + sta $24 + + bne StateDE31SelectValueDE4E + +; --------------------------------------------------------------------------- +; $DEBB-$DEE1: update per-slot $03A9 flags. +; Processes slots X = 3, 2, 1. +; --------------------------------------------------------------------------- + +UpdateSlotFlags: + ldx #$03 + +@slot_loop: + lda $84,x + lsr a + bcc @next_slot + + lda $80,x + cmp #$30 + bcc @check_existing + + cmp #$40 + bcc @set_flag + + +@check_existing: + lda $03A9,x + beq @next_slot + + lda #$40 + sta $FF + + lda #$00 + beq @store_flag + + +@set_flag: + lda #$01 + + +@store_flag: + sta $03A9,x + + +@next_slot: + dex + bne @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $DEE2-$DF2F: calculate/update global $FC value. +; --------------------------------------------------------------------------- + +UpdateGlobalFC: + ldx #$00 + + lda $03A9 + beq @store_result + + lda $4C + lsr a + + ; DEEC: B0 B7 = BCS $DEA5 + .byte $B0, $B7 + + ldx #$02 + + lda $94 + sta $00 + + lda $90 + sta $01 + + +@shift_loop: + lsr $00 + ror $01 + + dex + bpl @shift_loop + + + lda #$02 + + ldy $B0 + beq @set_x + + lda #$04 + + +@set_x: + tax + + lda $01 + sec + sbc #$02 + bmi @minimum + + jsr DivideByX_DFA4 + + txa + bne @clamp + + +@minimum: + ldx #$01 + + +@clamp: + cpx #$2F + bcc @check_flag + + ldx #$2F + + lda $3F + and #$08 + bne @check_flag + + dex + + +@check_flag: + lda $5C + asl a + bcs @store_result + + txa + ora #$80 + tax + + +@store_result: + stx $FC + rts + +; --------------------------------------------------------------------------- +; $DF30-$DF7E: advance global time/counter fields and refresh display data. +; --------------------------------------------------------------------------- + +UpdateGlobalCounters: + lda $4F + beq @return + + lda $6B + clc + adc #$10 + sta $6B + + +@check_subunit: + lda $6B + cmp #$0A + bcs @carry_subunit + + +@refresh_display: + ldx #$68 + jsr BuildDisplayValues + + ldx #$06 + +@copy_display: + lda $03D1,x + sta $03D9,x + + dex + bpl @copy_display + + +@return: + rts + + +@carry_subunit: + lda $6B + sec + sbc #$0A + sta $6B + + inc $6A + + lda $6A + cmp #$64 + bcc @check_subunit + + lda #$00 + sta $6A + + inc $69 + + lda $69 + cmp #$3C + bcc @check_subunit + + lda #$00 + sta $69 + + inc $68 + + lda $68 + cmp #$09 + bcc @check_subunit + + lda #$02 + sta $52 + bne @refresh_display + +; --------------------------------------------------------------------------- +; $DF7F-$DFA1: convert three consecutive values selected by X and store +; their converted representations in $03D1-$03D7. +; --------------------------------------------------------------------------- + +BuildDisplayValues: + lda $00,x + pha + + lda $01,x + pha + + lda $02,x + jsr DivideBy10 + stx $03D6 + sta $03D7 + + pla + jsr DivideBy10 + stx $03D3 + sta $03D4 + + pla + jsr DivideBy10 + sta $03D1 + + rts + +; --------------------------------------------------------------------------- +; $DFA2: divide A by 10. +; --------------------------------------------------------------------------- + +DivideBy10: + ldx #$0A + + +; --------------------------------------------------------------------------- +; $DFA4: divide A by the divisor supplied in X. +; +; Return: +; X = quotient +; A = remainder +; --------------------------------------------------------------------------- + +DivideByX_DFA4: + stx $0F + + ldx #$00 + +@subtract: + inx + sec + sbc $0F + bpl @subtract + + dex + adc $0F + rts + +; --------------------------------------------------------------------------- +; $DFB2-$DFD4: derive per-slot $70 state from $B8 and table $D913. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlot70: + ldy #$00 + + lda $9C,x + bne @store + + lda $B8,x + +@search_table: + cmp $D913,y + bmi @found + + iny + cpy #$05 + bmi @search_table + + ldy #$00 + + +@found: + tya + beq @store + + lda #$01 + cmp RiderMotionState,x + bne @store + + iny + iny + + +@store: + tya + sta $70,x + + rts +; --------------------------------------------------------------------------- +; $DFD5-$E090: pairwise slot comparison/update. +; --------------------------------------------------------------------------- +CompareRuntimeSlots: + ldx #$00 + ldy #$01 + + +; $DFD9 +PairCheckX: + lda $A8,x + bne PairCheckY + + +; $DFDD +PairNextXTrampoline: + jmp PairNextX + + +; $DFE0 +PairCheckY: + lda $A8,y + beq PairNextYTrampoline + + lda $84,x + lsr a + bcc PairNextXTrampoline + + lda $84,y + lsr a + bcc PairNextYTrampoline + + lda $70,x + beq PairNextXTrampoline + + cmp $70,y + bne PairNextYTrampoline + + lda $98,x + ora $03E0,x + bne PairNextXTrampoline + + lda $98,y + bne PairNextY + + lda $B0,x + bne PairCompare8C + + lda $B0,y + beq PairCompare80 + +PairCompare8C: + lda $8C,x + cmp $8C,y + bcs PairCompare8CReverse + + jsr Add12UnlessHighE091 + cmp $8C,y + bcs PairCompare80 + +PairNextYTrampoline: + jmp PairNextY + + +PairCompare8CReverse: + lda $8C,y + jsr Add12UnlessHighE091 + cmp $8C,x + bcc PairNextY + + +PairCompare80: + lda $80,x + cmp $80,y + bcs PairCompare80Reverse + + jsr Add2UnlessHighE098 + cmp $80,y + bcs PairMarkX + + lda $80,x + jsr Add12UnlessHighE091 + cmp $80,y + bcs PairMarkXOnly + + bcc PairNextY + + +PairCompare80Reverse: + lda $80,y + jsr Add2UnlessHighE098 + cmp $80,x + bcs PairMarkX + + lda $80,y + jsr Add12UnlessHighE091 + cmp $80,x + bcs PairMarkBoth + + bcc PairNextY + + +PairMarkXOnly: + lda #$FF + sta $98,x + bne PairAfterMark + + +PairMarkX: + lda #$FF + sta $98,x + + +PairMarkBoth: + cpx #$00 + bne PairSetY + + cpy #$03 + bne PairSetY + + lda #$10 + sta $3B + + +PairSetY: + lda #$FF + sta $98,y + + +PairAfterMark: + cpx #$00 + bne PairNextY + + lda #$01 + sta $FD + + +; $E07E +PairNextY: + iny + cpy #$04 + beq PairNextX + + jmp PairCheckY + + +; $E086 +PairNextX: + inx + txa + tay + iny + + cpx #$03 + beq SharedReturnE097 + + jmp PairCheckX + + + +; --------------------------------------------------------------------------- +; $E091-$E097 +; --------------------------------------------------------------------------- + +Add12UnlessHighE091: + cmp #$E7 + bcs SharedReturnE097 + + adc #$0C + +SharedReturnE097: + rts + + +; --------------------------------------------------------------------------- +; $E098-$E09E +; --------------------------------------------------------------------------- + +Add2UnlessHighE098: + cmp #$E7 + bcs @return + + adc #$02 + +@return: + rts + +; --------------------------------------------------------------------------- +; $E09F-$E0E5: test slot-0 state/position conditions and update flags. +; --------------------------------------------------------------------------- + +CheckSlotState: + lda $9C + bne @return + + tax + + lda $87 + lsr a + bcc @return + + lda $0373 + beq @return + + lda $B8 + sec + sbc #$03 + cmp $6F + bcs @return + + adc #$07 + cmp $6F + bcc @return + + lda $80 + cmp $0393 + bcs @return + + adc #$12 + cmp $0393 + bcc @return + + jsr CalculateSlotRemaining + + sec + sbc #$08 + cmp $8C + bcs @return + + ldy #$FF + sty $98 + + iny + sty $DF + + iny + sty $9F + sty $FD + + iny + sty $039B + +@return: + rts + + +; --------------------------------------------------------------------------- +; $E0E6-$E17E +; State/setup update with structure generation. +; +; $E16C is also a secondary entry. +; --------------------------------------------------------------------------- + +UpdateStructureGenerator: + lda $3A + ora $52 + ora $24 + + ; E0EC: D0 B0 = BNE $E09E + ; Reuses the RTS at the end of the preceding helper. + .byte $D0, $B0 + + lda $03A0 + bne StateE0E6UpdateE10A + + lda $2E + + ; E0F5: D0 A7 = BNE $E09E + ; Reuses the same preceding RTS. + .byte $D0, $A7 + + lda $18 + and #$03 + tax + + lda $D8F3,x + sta $03A1 + + lda #$F8 + sta $03B9 + sta $03A0 + + +; $E10A +StateE0E6UpdateE10A: + lda #$50 + sta $01 + + lda $03B9 + sec + sbc $60 + sta $03B9 + + lda $60 + beq StateE0E6CheckPositionE124 + + lda $3F + and #$0F + bne StateE0E6CheckPositionE124 + + dec $03B9 + + +; $E124 +StateE0E6CheckPositionE124: + lda $03B9 + sta $00 + + cmp #$FC + bcc StateE0E6BuildStructureE139 + + lda $18 + and #$A0 + sta $2E + + lda #$00 + sta $03A0 + + rts + + +; --------------------------------------------------------------------------- +; $E139 +; Configure parameters and invoke the existing structure helper. +; --------------------------------------------------------------------------- + +StateE0E6BuildStructureE139: + lda $03A1 + sta $02 + + lda #$22 + sta $03 + + lda #$C0 + sta $04 + + lda #$20 + sta $0B + + jsr BuildIndirectStructure + + lda $03B9 + cmp #$60 + bcs StateE0E6ReturnE17E + + ldx $03A1 + inx + inx + inx + stx $00 + + jsr WriteStructureE16C + + ldx $03A1 + inx + stx $00 + + lda $04 + clc + adc #$08 + sta $04 + + +; --------------------------------------------------------------------------- +; $E16C +; Secondary entry. +; +; Writes pairs into the structure addressed through $04/$05. +; --------------------------------------------------------------------------- + +WriteStructureE16C: + ldy #$06 + +@write_loop: + lda #$60 + sta ($04),y + + dey + + lda $00 + sta ($04),y + + dec $00 + + dey + dey + dey + bpl @write_loop + + +; $E17E +StateE0E6ReturnE17E: + rts + +; --------------------------------------------------------------------------- +; $E17F-$E1A2: process four indexed runtime entries. +; --------------------------------------------------------------------------- + +ProcessRuntimeEntries: + jsr SortRuntimeSlots + + ldx #$03 + + +@entry_loop: + stx $0D + + lda $D8C0,x + sta $04 + + lda $88,x + tax + + lda $A8,x + beq @next_entry + + lda $D8C9,x + sta $0B + + jsr BuildRuntimeEntry + jsr BuildSlotStructure + + +@next_entry: + ldx $0D + dex + bpl @entry_loop + + rts + + +; --------------------------------------------------------------------------- +; $E1A3-$E1F8 +; Build/update an entry using the current slot state. +; +; X = current slot. +; Calls $E1F9 to derive the table index in A. +; --------------------------------------------------------------------------- + +BuildRuntimeEntry: + lda $80,x + sta $00 + + lda $8C,x + sta $01 + + jsr GetRuntimeEntryType + tay + + lda $D892,y + sta $02 + + lda #$33 + sta $03 + + cpy #$16 + bne EntryBuildDispatchE1D1 + + lda $04 + sta $039F + + clc + adc #$0C + sta $04 + + lda $01 + clc + adc #$08 + sta $01 + + lda #$23 + sta $03 + + +; $E1D1 +EntryBuildDispatchE1D1: + lda $D8A9,y + jsr BuildIndirectStructure + + lda $0E + beq EntryBuildReturnE1F8 + + lsr a + bcs EntryBuildAlternateE1F2 + + ldy #$11 + lda #$B2 + + +; $E1E2 +EntryBuildWriteLoopE1E2: + cpy #$09 + beq EntryBuildDecrementYE1EB + + sta ($04),y + + sec + sbc #$01 + + +; $E1EB +EntryBuildDecrementYE1EB: + dey + dey + dey + dey + + bpl EntryBuildWriteLoopE1E2 + + rts + +; $E1F2 +EntryBuildAlternateE1F2: + ldy #$15 + + lda #$AE + sta ($04),y + +; $E1F8 +EntryBuildReturnE1F8: + rts + + +; --------------------------------------------------------------------------- +; $E1F9-$E22E +; Derive an entry/type value from the current slot state. +; +; X = current slot +; Returns value in A on the main path. +; $0E and $039F are reset on entry. +; --------------------------------------------------------------------------- + +GetRuntimeEntryType: + lda #$00 + sta $0E + sta $039F + + lda $9C,x + beq EntryTypeAlternateE21F + + cmp #$05 + beq EntryTypeAlternateE21F + + ldy #$06 + + lda $BC,x + bne EntryTypeUseTwoE213 + + lda $0370,x + bne EntryTypeHaveBaseE215 + + +; $E213 +EntryTypeUseTwoE213: + ldy #$02 + + +; $E215 +EntryTypeHaveBaseE215: + tya + + ldy $36,x + beq EntryTypeAddFlagE21C + + dey + tya + + +; $E21C +EntryTypeAddFlagE21C: + ora #$10 + rts + + +; --------------------------------------------------------------------------- +; $E21F +; Alternate path for $9C,X == 0 or 5. +; --------------------------------------------------------------------------- + +EntryTypeAlternateE21F: + ldy $52 + dey + + ; E222: D0 0B = BNE $E22F + .byte $D0, $0B + + lda $AC,x + cmp #$0A + + ; E228: D0 05 = BNE $E22F + .byte $D0, $05 + + ldy #$02 + sty $0E + + rts +; --------------------------------------------------------------------------- +; $E22F-$E269: derive a value from current slot state. +; --------------------------------------------------------------------------- + +GetSlotValue: + lda $98,x + ora $B0,x + bne SlotValueReturnACE267 + + lda $58,x + beq SlotValueCheckPositionE243 + + cmp #$0D + bne SlotValueReturnACE267 + + lda $B8,x + cmp #$20 + bcc SlotValueReturnACE267 + + +; $E243 +SlotValueCheckPositionE243: + lda $90,x + ora $94,x + bne SlotValueCheckACE24D + + inc $0E + bne SlotValueReturnACE267 + + +; $E24D +SlotValueCheckACE24D: + lda $AC,x + cmp #$06 + bne SlotValueReturnACE267 + + ldy #$14 + + lda $DC,x + beq SlotValueCheckGlobalE25E + + bpl SlotValueReturnYE25C + + iny + + +; $E25C +SlotValueReturnYE25C: + tya + rts + + +; $E25E +SlotValueCheckGlobalE25E: + lda $3F + and #$02 + beq SlotValueReturnACE267 + + lda #$13 + rts + + +; $E267 +SlotValueReturnACE267: + lda $AC,x + rts + + +; --------------------------------------------------------------------------- +; $E26A-$E2AE: prepare structure parameters and tail-call $D1C7. +; X = current slot. +; --------------------------------------------------------------------------- + +BuildSlotStructure: + lda $039F + + ; E26D: F0 FA = BEQ $E269 + .byte $F0, $FA + + lda $84,x + cmp #$02 + + ; E273: F0 F4 = BEQ $E269 + .byte $F0, $F4 + + lda $0390,x + cmp #$F8 + + ; E27A: B0 ED = BCS $E269 + .byte $B0, $ED + + cmp $80,x + + ; E27E: 90 E9 = BCC $E269 + .byte $90, $E9 + + lda $0390,x + sta $00 + + lda #$A8 + sec + sbc $6C,x + sta $01 + + lda #$22 + sta $03 + + lda $039F + sta $04 + + lda RiderMotionState,x + beq @combine_flags + + lda #$20 + +@combine_flags: + ora $0B + sta $0B + + ldy $0398,x + + lda $D90B,y + sta $02 + + tya + beq @dispatch + + eor #$03 + +@dispatch: + jmp BuildIndirectStructure + + +; --------------------------------------------------------------------------- +; $E2AF-$E2F5 +; Update global structure position/state and optionally tail-call $D1C7. +; --------------------------------------------------------------------------- + +UpdateStructureState: + lda $03BD + sec + sbc $60 + sta $03BD + bcs @no_borrow + + dec $03BE + +@no_borrow: + ldy $52 + beq @continue + + dey + + ; E2C2: D0 A5 = BNE $E269 + .byte $D0, $A5 + + lda $20 + bne @continue + + lda $03BB + eor #$01 + sta $03BB + +@continue: + ldy $03BE + dey + bne SlotSpritesCheckBCE303 + + lda $03BD + sta $00 + + lda #$50 + sta $01 + + ldy $03BB + lda $D8E5,y + sta $02 + + lda #$32 + sta $03 + + lda #$C0 + sta $04 + + lda #$00 + sta $0B + + jmp BuildIndirectStructure + + +; --------------------------------------------------------------------------- +; $E2F6-$E358 +; Build/update a pair of OAM entries for the current slot. +; +; X = current slot +; Y = OAM/output offset +; +; $E303 is also a secondary entry from the preceding routine. +; --------------------------------------------------------------------------- + +BuildSlotSprites: + lda $036C,x + beq SlotSpritesReturnE358 + + lda $94,x + ora $90,x + beq SlotSpritesReturnE358 + + lda $BC,x + + +; $E303 +SlotSpritesCheckBCE303: + bne SlotSpritesReturnE358 + + lda $84,x + lsr a + bcc SlotSpritesReturnE358 + + lda $80,x + cmp #$10 + bcc SlotSpritesReturnE358 + + clc + adc #$F9 + sta $0297,y + + clc + adc #$F8 + sta $029B,y + + lda $8C,x + clc + adc #$08 + sta $0298,y + + clc + adc #$08 + sta $0294,y + + lda #$F6 + sta $0295,y + + lda #$F7 + sta $0299,y + + lda $3F + and #$04 + beq @tiles_ready + + lda #$F7 + sta $0295,y + + lda #$F6 + sta $0299,y + + +@tiles_ready: + lda $036C,x + and #$02 + sta $0296,y + sta $029A,y + + lda $94,x + bne SlotSpritesReturnE358 + + lda #$F8 + sta $0298,y + + +; $E358 +SlotSpritesReturnE358: + rts + +; --------------------------------------------------------------------------- +; $E359-$E3F3 +; Update global position/state and build four $02F0 OAM entries. +; --------------------------------------------------------------------------- + +UpdateGlobalDisplay: + lda $52 + bne GlobalDisplayBuildE3C6 + + lda $03E0 + beq GlobalDisplaySelectIndexE36B + + lda $3C + lsr a + bcs GlobalDisplayBuildE3C6 + + lda #$FC + bne GlobalDisplayStore01E3C8 + + +; $E36B +GlobalDisplaySelectIndexE36B: + lda $5C + and #$C0 + beq GlobalDisplayIndexReadyE37A + + ldy $4F + bne GlobalDisplayRotateE377 + + lda #$80 + + +; $E377 +GlobalDisplayRotateE377: + asl a + rol a + rol a + + +; $E37A +GlobalDisplayIndexReadyE37A: + tay + + lda $03B6 + cmp $D8FB,y + bcc GlobalDisplayIncreaseE39B + beq GlobalDisplayFinalizeE3AA + + lda $03B5 + sec + sbc #$0B + sta $03B5 + bcs GlobalDisplayFinalizeE3AA + + lda $03B6 + beq GlobalDisplayFinalizeE3AA + + dec $03B6 + jmp GlobalDisplayFinalizeE3AA + + +; $E39B +GlobalDisplayIncreaseE39B: + lda $03B5 + clc + adc $D8F7,y + sta $03B5 + bcc GlobalDisplayFinalizeE3AA + + inc $03B6 + + +; $E3AA +GlobalDisplayFinalizeE3AA: + ldx #$00 + + lda $03B6 + cmp #$20 + bcc GlobalDisplayCheck1AE3B8 + + sta $3C + sta $03E0 + + +; $E3B8 +GlobalDisplayCheck1AE3B8: + cmp #$1A + bcc GlobalDisplayCombineFCE3C1 + + dey + bne GlobalDisplayCombineFCE3C1 + + ldx #$B0 + + +; $E3C1 +GlobalDisplayCombineFCE3C1: + txa + ora $FC + sta $FC + + +; $E3C6 +GlobalDisplayBuildE3C6: + lda #$FE + + +; $E3C8 +GlobalDisplayStore01E3C8: + sta $01 + + lda $03B6 + clc + adc #$50 + sta $00 + + ldx #$0C + + +; $E3D4 +GlobalDisplayOamLoopE3D4: + lda #$CF + sta $02F0,x + + lda $01 + sta $02F1,x + + lda #$20 + sta $02F2,x + + lda $00 + sta $02F3,x + + clc + adc #$08 + sta $00 + + dex + dex + dex + dex + bpl GlobalDisplayOamLoopE3D4 + + rts + +; --------------------------------------------------------------------------- +; $E3F4-$E42A +; Initialize and sort the four slot indices in $88-$8B using values copied +; from $B8-$BB. +; --------------------------------------------------------------------------- + +SortRuntimeSlots: + ldx #$03 + + +@initialize: + txa + sta $88,x + + lda $B8,x + sta $00,x + + dex + bpl @initialize + + + lda $0373 + beq @start_sort + + lda $6F + sta $03 + + +@start_sort: + ldx #$03 + stx $0D + + +@outer_loop: + ldy #$00 + + +@inner_loop: + ldx $88,y + lda $00,x + + ldx $89,y + cmp $00,x + bcc @no_swap + + lda $0088,y + sta $0089,y + stx $88,y + + +@no_swap: + iny + cpy $0D + bne @inner_loop + + dec $0D + bne @outer_loop + + rts + +; --------------------------------------------------------------------------- +; $E42B-$E455: process four sorted runtime slots. +; --------------------------------------------------------------------------- + +ProcessSortedSlots: + lda $03A3 + beq @return + + ldx #$03 + +@slot_loop: + lda $88,x + beq @next_slot + + cmp #$03 + bne @process_slot + + lda $0373 + bne @next_slot + +@process_slot: + lda $D8C0,x + sta $07 + + ldy $03B3 + jsr WriteSlotPattern + +@next_slot: + dex + bpl @slot_loop + + lda $03B3 + eor #$02 + sta $03B3 + +@return: + rts + +; --------------------------------------------------------------------------- +; $E456-$E49A: prepare/process four sorted slot entries. +; --------------------------------------------------------------------------- + +PrepareSortedSlots: + ldy #$03 + +@slot_loop: + sty $05 + + lda $D8C0,y + sta $07 + + lda $0088,y + beq @next_slot + + tax + + lda $84,x + sta $02 + sta $03 + sta $04 + + lsr a + bcs @adjust + + lda $80,x + cmp #$70 + bcc @adjust + + cmp #$90 + bcs @adjust + + jsr ResetSlotState + + lda #$01 + sta $03A6 + bne @process + +@adjust: + lda $80,x + cmp #$F8 + bcc @check_second + + inc $03 + +@check_second: + cmp #$F0 + bcc @process + + inc $04 + +@process: + jsr ProcessSlotBits + +@next_slot: + ldy $05 + dey + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $E49B-$E4AC: process each clear low-bit field in $02-$04. +; --------------------------------------------------------------------------- + +ProcessSlotBits: + ldx #$00 + +@loop: + lda $02,x + lsr a + bcs @next + + txa + tay + jsr WriteSlotPattern + +@next: + inx + cpx #$03 + bcc @loop + + rts + +; --------------------------------------------------------------------------- +; $E4AD-$E4C7: write $F8 into three four-byte-spaced entries. +; --------------------------------------------------------------------------- + +WriteSlotPattern: + lda #$02 + sta $01 + + lda $07 + clc + adc $D8DC,y + sta $00 + + ldy #$00 + lda #$F8 + +@write_loop: + sta ($00),y + + iny + iny + iny + iny + + cpy #$0C + bcc @write_loop + + rts + +; --------------------------------------------------------------------------- +; $E4C8-$E52B +; Scan runtime slots and update $03A3 based on matching slot pairs. +; +; $E52C is a separately callable comparison helper. +; --------------------------------------------------------------------------- + +UpdateSlotPairState: + ldx #$01 + + +; $E4CA +SlotPairOuterE4CA: + stx $0C + + lda $84,x + lsr a + bcs SlotPairStartInnerE4D9 + + bne SlotPairNextOuterE50A + + lda $80,x + cmp #$F8 + bcc SlotPairNextOuterE50A + + +; $E4D9 +SlotPairStartInnerE4D9: + lda #$00 + sta $0A + + txa + tay + iny +SlotPairInnerE4E2: + sty $05 + + + lda $84,y + lsr a + bcs SlotPairCompareE4F0 + + bne SlotPairNextInnerE4FF + + lda $80,x + cmp #$F8 + bcc SlotPairNextInnerE4FF + + +; $E4F0 +SlotPairCompareE4F0: + jsr CompareSlotDistance + beq SlotPairNextInnerE4FF + + inc $0A + + lda $0B + asl a + asl a + ora $05 + sta $0B + + +; $E4FF +SlotPairNextInnerE4FF: + iny + cpy #$04 + bcc SlotPairInnerE4E2 + + lda $0A + cmp #$02 + bcs SlotPairMultipleE513 + + +; $E50A +SlotPairNextOuterE50A: + ldx $0C + dex + bpl SlotPairOuterE4CA + + lda #$00 + beq SlotPairStoreStateE528 + + +; $E513 +SlotPairMultipleE513: + bne SlotPairSetStateE526 + + lda $0B + pha + + and #$03 + tay + + pla + lsr a + lsr a + and #$03 + tax + + jsr CompareSlotDistance + beq SlotPairNextOuterE50A + + +; $E526 +SlotPairSetStateE526: + lda #$01 + + +; $E528 +SlotPairStoreStateE528: + sta $03A3 + rts + +; --------------------------------------------------------------------------- +; $E52C-$E53C +; Compare the $8C values of slots X and Y. +; Returns zero for the middle separation range. +; --------------------------------------------------------------------------- + +CompareSlotDistance: + lda $8C,x + sec + sbc $008C,y + + cmp #$EC + bcs @return + + cmp #$12 + bcc @return + + lda #$00 + +@return: + rts + + +; --------------------------------------------------------------------------- +; $E53D-$E540 +; Four position/state comparison thresholds. +; +; Referenced by UpdateFourSlotPositionsE96C. +; --------------------------------------------------------------------------- + +SlotPositionThresholdTableE53D: + .byte $0E, $1A, $26, $32 + + +; --------------------------------------------------------------------------- +; $E541-$E54D +; 13-byte parameter/position table. +; --------------------------------------------------------------------------- + +DataTableE541: + .byte $DD, $DC, $DB, $7C, $74, $6C, $90 + .byte $88, $80, $78, $70, $68, $60 + + +; --------------------------------------------------------------------------- +; $E54E-$E553 +; Low bytes of six RAM pointers. +; +; Combined with PointerHighTableE554: +; 0 -> $0400 +; 1 -> $0440 +; 2 -> $0480 +; 3 -> $04C0 +; 4 -> $0500 +; 5 -> $0540 +; --------------------------------------------------------------------------- + +PointerLowTableE54E: + .byte $00, $40, $80, $C0, $00, $40 + + +; --------------------------------------------------------------------------- +; $E554-$E559 +; High bytes of six RAM pointers. +; --------------------------------------------------------------------------- + +PointerHighTableE554: + .byte $04, $04, $04, $04, $05, $05 + + +; --------------------------------------------------------------------------- +; $E55A-$E56D +; Continuation of the split script-pointer low-byte table. +; +; The actual indexed base is $E559, one byte before this block. +; The $E559 byte remains in the preceding already-converted data. +; --------------------------------------------------------------------------- + +ScriptPointerLowContinuationE55A: + .byte $84, $99, $99, $A6 + .byte $BB, $D0, $E5, $F8 + .byte $0B, $1E, $31, $3E + .byte $49, $52, $67, $84 + .byte $86, $89, $89, $8C + + +; --------------------------------------------------------------------------- +; $E56E-$E582 +; High-byte half of the script pointer table. +; +; Indexed with the same Y that indexes the low-byte table at $E559. +; --------------------------------------------------------------------------- + +ScriptPointerHighTableE56E: + .byte $97 + .byte $E5, $E5, $E5, $E5 + .byte $E5, $E5, $E5, $E5 + .byte $E6, $E6, $E6, $E6 + .byte $E6, $E6, $E6, $E6 + .byte $E6, $E6, $E6, $E6 + + +; --------------------------------------------------------------------------- +; $E583 +; Final byte immediately preceding the first script. +; Keep binary-backed for now. +; This byte must be magical +; --------------------------------------------------------------------------- + .byte $E6 + +; --------------------------------------------------------------------------- +; $E584-$E598 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Entries are two-byte records terminated by $FF. +; --------------------------------------------------------------------------- + +ScriptE584: + .byte $04, $87 + .byte $08, $88 + .byte $08, $44 + .byte $29, $06 + .byte $29, $00 + .byte $31, $85 + .byte $35, $84 + .byte $35, $45 + .byte $51, $00 + .byte $52, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E599-$E5A5 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Six two-byte entries followed by $FF. +; Note: pointer-table indexes 2 and 3 both select this stream. +; --------------------------------------------------------------------------- + +ScriptE599: + .byte $04, $07 + .byte $04, $88 + .byte $0C, $86 + .byte $10, $83 + .byte $14, $85 + .byte $16, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5A6-$E5BA +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5A6: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $20, $06 + .byte $22, $00 + .byte $30, $08 + .byte $50, $84 + .byte $52, $83 + .byte $54, $43 + .byte $6C, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5BB-$E5CF +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5BB: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $23, $06 + .byte $2A, $00 + .byte $2F, $84 + .byte $33, $83 + .byte $34, $43 + .byte $53, $00 + .byte $55, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5D0-$E5E4 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5D0: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $17, $06 + .byte $1A, $00 + .byte $1F, $84 + .byte $23, $83 + .byte $23, $43 + .byte $35, $00 + .byte $37, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5E5-$E5F7 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5E5: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $12, $06 + .byte $12, $00 + .byte $1A, $83 + .byte $1B, $43 + .byte $24, $00 + .byte $26, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E5F8-$E60A +; Script stream selected through the $E559/$E56E pointer tables. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE5F8: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $22, $06 + .byte $22, $00 + .byte $2A, $82 + .byte $2B, $4A + .byte $38, $00 + .byte $3A, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E60B-$E61D +; Next script stream. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE60B: + .byte $04, $88 + .byte $0A, $8B + .byte $0B, $49 + .byte $17, $06 + .byte $17, $00 + .byte $25, $83 + .byte $26, $43 + .byte $3B, $00 + .byte $3D, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E61E-$E630 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Nine two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE61E: + .byte $04, $8B + .byte $08, $49 + .byte $18, $06 + .byte $18, $00 + .byte $1F, $84 + .byte $20, $82 + .byte $25, $4A + .byte $35, $00 + .byte $37, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E631-$E63D +; Script stream selected through the $E559/$E56E pointer tables. +; +; Six two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE631: + .byte $02, $01 + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $1A, $06 + .byte $1A, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E63E-$E648 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Five two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE63E: + .byte $04, $8B + .byte $08, $49 + .byte $18, $06 + .byte $1A, $00 + .byte $1F, $0B + .byte $FF + + +; --------------------------------------------------------------------------- +; $E649-$E651 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Four two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE649: + .byte $0D, $0C + .byte $17, $0C + .byte $28, $0C + .byte $2B, $0C + .byte $FF + + +; --------------------------------------------------------------------------- +; $E652-$E666 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Ten two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE652: + .byte $04, $8B + .byte $08, $49 + .byte $22, $06 + .byte $22, $00 + .byte $22, $14 + .byte $30, $0F + .byte $31, $83 + .byte $32, $82 + .byte $33, $4A + .byte $4B, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E667-$E683 +; Script stream selected through the $E559/$E56E pointer tables. +; +; Fourteen two-byte entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE667: + .byte $00, $0B + .byte $18, $06 + .byte $18, $84 + .byte $1A, $83 + .byte $1A, $43 + .byte $3B, $00 + .byte $42, $10 + .byte $43, $85 + .byte $48, $84 + .byte $4A, $83 + .byte $4A, $43 + .byte $5A, $00 + .byte $5A, $0E + .byte $5C, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E684-$E688 +; Shared script stream. +; +; $E684 and $E686 are both valid entry points. +; Entering at $E684 falls through into the $E686 portion. +; --------------------------------------------------------------------------- + +ScriptE684: + .byte $0E, $0D + +ScriptE686: + .byte $0F, $0E + .byte $FF + + +; --------------------------------------------------------------------------- +; $E689-$E68B +; Single-entry script terminated by $FF. +; --------------------------------------------------------------------------- + +ScriptE689: + .byte $00, $12 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E68C-$E696 +; Five two-byte script entries followed by $FF. +; --------------------------------------------------------------------------- + +ScriptE68C: + .byte $04, $88 + .byte $0A, $89 + .byte $0A, $42 + .byte $1A, $13 + .byte $1A, $00 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E697-$E699 +; Single two-byte record followed by $FF. +; +; Structurally matches the script encoding used immediately before it, +; but its consumer has not yet been definitively identified. +; --------------------------------------------------------------------------- + +DataE697: + .byte $1E, $86 + .byte $FF + + +; --------------------------------------------------------------------------- +; $E69A-$E6A0 +; Seven-byte rider sprite/tile lookup table. +; --------------------------------------------------------------------------- + +RiderSpriteTileTableE69A: + .byte $CB, $CD, $CC, $CE + .byte $CB, $CB, $CB + + +; --------------------------------------------------------------------------- +; $E6A1-$E6AC +; Three four-byte descriptors consumed by BuildPairedPpuRows. +; +; BuildPairedPpuRows receives X as the descriptor offset: +; +; X = $00 -> descriptor at $E6A1 +; X = $04 -> descriptor at $E6A5 +; X = $08 -> descriptor at $E6A9 +; +; Physical record format: +; +; byte 0 = first PPU address high byte +; second PPU address uses byte 0 XOR $04 +; byte 1 = alternating tile/value B +; byte 2 = alternating tile/value A +; byte 3 = PPU address low byte +; --------------------------------------------------------------------------- + +PpuRowDescriptorsE6A1: + +PpuRowDescriptor0E6A1: + .byte $22, $64, $62, $C0 + +PpuRowDescriptor1E6A5: + .byte $22, $74, $63, $E0 + +PpuRowDescriptor2E6A9: + .byte $21, $83, $FC, $C0 + + +; --------------------------------------------------------------------------- +; $E6AD-$E6B6 +; --------------------------------------------------------------------------- + +DataE6AD: + .byte $03, $01, $02, $02 + .byte $00, $05, $05, $06 + .byte $04, $04 + +; --------------------------------------------------------------------------- +; $E6B7-$E6E0 +; First five little-endian command-handler vectors. +; --------------------------------------------------------------------------- + +CommandHandlerVectorsE6B7: + .word ClearSlotStateE963 + .word SetSlotA4OneE8E3 + .word InitializeSlotMode6E85D + .word AdjustSlotBCE86A + .word InitializeSlotFieldsE845 + .word InitializeSlotMode2E854 + .word UpdateSlot0388E934 + .word UpdateSlotFlagE818 + .word UpdateGlobalStateEA8F + .word InitializeSlotMode4E879 + .word UpdateRiderMotionState + .word InitializeSlotModeE8EE + .word UpdateSlotBoundaryE8FF + .word Slot0388CheckA4E956 + .word ClearRiderMotionState + .word InitializeRiderMotionState + .word SetSlotBCE4To10E8FA + .word CommandHandlerVectorsE6B7 + .word InitializeGlobalB6E8D3 + .word UpdateSlotStateIfReadyE8E7 + .word $E8F5 + + +; --------------------------------------------------------------------------- +; $E6E1-$E6F4 +; 20-byte indexed lookup table. +; --------------------------------------------------------------------------- + +DataTableE6E1: + .byte $08, $07, $05, $01 + .byte $0B, $06, $0A, $0E + .byte $03, $04, $0C, $0D + .byte $0F, $10, $12, $13 + .byte $11, $15, $14, $00 + + +; --------------------------------------------------------------------------- +; $E6F5-$E70A +; 22-byte lookup / position table immediately preceding code at $E70B. +; --------------------------------------------------------------------------- + +DataTableE6F5: + .byte $09, $18, $20, $28 + .byte $30, $38, $40, $48 + .byte $50, $58, $60, $68 + .byte $70, $78, $80, $88 + .byte $90, $98, $A0, $A8 + .byte $B8, $CC + + +; --------------------------------------------------------------------------- +; $E70B-$E732 +; Update per-rider $64/$E0 state from $60. +; +; Processes rider slots 3 -> 0. +; $E0,X wraps modulo 64. +; --------------------------------------------------------------------------- + +UpdateRiderCourseStateE70B: + ldx #$03 ; E70B: A2 03 + +@next_rider: + lda $60,x ; E70D: B5 60 + beq @clear_state ; E70F: F0 1A + + lda $64,x ; E711: B5 64 + sec ; E713: 38 + sbc $60,x ; E714: F5 60 + beq @adjust ; E716: F0 06 + bmi @adjust ; E718: 30 04 + + sta $64,x ; E71A: 95 64 + bne @clear_state ; E71C: D0 0D + +@adjust: + clc ; E71E: 18 + adc #$08 ; E71F: 69 08 + sta $64,x ; E721: 95 64 + + inc $E0,x ; E723: F6 E0 + + lda $E0,x ; E725: B5 E0 + and #$3F ; E727: 29 3F + sta $E0,x ; E729: 95 E0 + +@clear_state: + lda #$00 ; E72B: A9 00 + sta $D8,x ; E72D: 95 D8 + + dex ; E72F: CA + bpl @next_rider ; E730: 10 DB + + rts ; E732: 60 + + + +; --------------------------------------------------------------------------- +; $E733-$E75F +; Update rider script/course state. +; +; Slots 3, 2, and 1 are called through the $E73B secondary entry. +; After X reaches zero, execution falls through into $E73B for rider 0. +; --------------------------------------------------------------------------- + +UpdateAllRiderScriptStateE733: + ldx #$03 + +@next_rider: + jsr UpdateSingleRiderScriptStateE73B + dex + bne @next_rider + + +; --------------------------------------------------------------------------- +; $E73B +; Update script/course state for rider X. +; --------------------------------------------------------------------------- + +UpdateSingleRiderScriptStateE73B: + lda $60,x + clc + adc $C8,x + sta $C8,x + + lda $58,x + + ; E744: D0 1C = BNE $E762 + .byte $D0, $1C + + lda $C0,x + sec + sbc #$40 + + ; E74B: 30 06 = BMI $E753 + bmi @return + + lsr a + lsr a + cmp #$16 + bcc @store_script_index + +@return: + rts + +@store_script_index: + sta $58,x + inc $58,x + + lda #$00 + sta $C4,x + + lda $64,x + sta $C8,x + + +; --------------------------------------------------------------------------- +; $E760-$E78F +; Load and process the current rider's script stream. +; +; X = current rider slot +; +; $58,X selects the script pointer. +; $C4,X is the byte offset within that script. +; $C8,X is compared against the first byte of each script entry. +; --------------------------------------------------------------------------- + +ProcessRiderScriptE760: + dec $C8,x ; E760: D6 C8 + +; $E762 is the target of the BNE at $E744. +ProcessRiderScriptEntryE762: + ldy $58,x ; E762: B4 58 + + lda $E559,y ; E764: B9 59 E5 + sta $0A ; E767: 85 0A + + lda $E56E,y ; E769: B9 6E E5 + sta $0B ; E76C: 85 0B + + ldy $C4,x ; E76E: B4 C4 + lda ($0A),y ; E770: B1 0A + + cmp #$FF ; E772: C9 FF + + ; E774: F0 5A = BEQ $E7D0 + .byte $F0, $5A + + sta $0F ; E776: 85 0F + + cmp $C8,x ; E778: D5 C8 + beq @load_command ; E77A: F0 02 + + ; E77C: B0 D5 = BCS $E753 + ; $E753 is the RTS in UpdateSingleRiderScriptStateE73B. + .byte $B0, $D5 + +@load_command: + iny ; E77E: C8 + lda ($0A),y ; E77F: B1 0A + + bit $E703 ; E781: 2C 03 E7 + + ; E784: D0 1D = BNE $E7A3 + .byte $D0, $1D + + bit $E6FB ; E786: 2C FB E6 + + ; E789: D0 67 = BNE $E7F2 + .byte $D0, $67 + + jsr $E794 ; E78B: 20 94 E7 + + inc $C4,x ; E78E: F6 C4 + + + +; --------------------------------------------------------------------------- +; $E790-$E793 +; Advance to the next two-byte script command and continue processing. +; --------------------------------------------------------------------------- + + inc $C4,x ; E790: F6 C4 + + ; E792: D0 CE = BNE $E762 + .byte $D0, $CE + + +; --------------------------------------------------------------------------- +; $E794-$E7A2 +; Dispatch A through the 21-entry handler vector table at $E6B7. +; +; A = handler index +; --------------------------------------------------------------------------- + +DispatchRiderCommandE794: + asl a ; E794: 0A + tay ; E795: A8 + + lda CommandHandlerVectorsE6B7,y + ; E796: B9 B7 E6 + sta $00 ; E799: 85 00 + + lda CommandHandlerVectorsE6B7+1,y + ; E79B: B9 B8 E6 + sta $01 ; E79E: 85 01 + + jmp ($0000) ; E7A0: 6C 00 00 + + +; --------------------------------------------------------------------------- +; $E7A3-$E7CF +; Update rider command/state from the low nibble of the current script +; command, then advance to the next two-byte command. +; --------------------------------------------------------------------------- + +UpdateRiderCommandStateE7A3: + lda $B0,x ; E7A3: B5 B0 + ora $98,x ; E7A5: 15 98 + bne @advance_script ; E7A7: D0 21 + + lda ($0A),y ; E7A9: B1 0A + and #$0F ; E7AB: 29 0F + sta $00 ; E7AD: 85 00 + + lda RiderMotionState,x ; E7AF: B5 A4 + cmp #$01 ; E7B1: C9 01 + beq @advance_script ; E7B3: F0 15 + + lda $00 ; E7B5: A5 00 + sta $AC,x ; E7B7: 95 AC + + lda $58,x ; E7B9: B5 58 + cmp #$03 ; E7BB: C9 03 + beq @advance_script ; E7BD: F0 0B + + lda $AC,x ; E7BF: B5 AC + sec ; E7C1: 38 + sbc #$02 ; E7C2: E9 02 + tay ; E7C4: A8 + + lda DataE6AD,y ; E7C5: B9 AD E6 + sta $D4,x ; E7C8: 95 D4 + +@advance_script: + inc $C4,x ; E7CA: F6 C4 + inc $C4,x ; E7CC: F6 C4 + + ; E7CE: D0 92 = BNE $E762 + .byte $D0, $92 + + +; --------------------------------------------------------------------------- +; $E7D0-$E7F1 +; End/reset the current rider script. +; --------------------------------------------------------------------------- + +ResetRiderScriptE7D0: + lda #$00 ; E7D0: A9 00 + sta $58,x ; E7D2: 95 58 + sta $D4,x ; E7D4: 95 D4 + + lda $A0,x ; E7D6: B5 A0 + bne @clear_state ; E7D8: D0 12 + + lda RiderMotionState,x ; E7DA: B5 A4 + cmp #$01 ; E7DC: C9 01 + beq @clear_state ; E7DE: F0 0C + + lda #$00 ; E7E0: A9 00 + sta $BC,x ; E7E2: 95 BC + + lda RiderMotionState,x ; E7E4: B5 A4 + cmp #$02 ; E7E6: C9 02 + bne @clear_state ; E7E8: D0 02 + + inc RiderMotionState,x ; E7EA: F6 A4 + +@clear_state: + lda #$00 ; E7EC: A9 00 + sta $036C,x ; E7EE: 9D 6C 03 + rts ; E7F1: 60 + + +; --------------------------------------------------------------------------- +; $E7F2-$E7FB +; Store command low nibble / associated trigger state, then rejoin the +; common command-advance path at $E7CA. +; --------------------------------------------------------------------------- + +StoreRiderCommandStateE7F2: + and #$0F ; E7F2: 29 0F + sta $CC,x ; E7F4: 95 CC + + lda $0F ; E7F6: A5 0F + sta $D0,x ; E7F8: 95 D0 + + ; E7FA: 10 CE = BPL $E7CA + .byte $10, $CE + +; --------------------------------------------------------------------------- +; $E7FC-$E7FE +; Load rider lane-position state. +; --------------------------------------------------------------------------- + +LoadRiderLanePositionE7FC: + ldy $0360,x ; E7FC: BC 60 03 + +; --------------------------------------------------------------------------- +; $E7FF-$E817 +; Load a table-selected value through an indirect pointer. +; +; X = current slot +; Y = table selector on entry +; +; $E80D is also a secondary entry that sets $03:$04. +; --------------------------------------------------------------------------- + +LoadIndexedValueE7FF: + lda $E0,x + sta $08 + + jsr LoadTablePointerE80D + + sty $07 + + ldy $08 + lda ($03),y + + rts + + +; --------------------------------------------------------------------------- +; $E80D +; Load a pointer from split low/high tables into $03:$04. +; --------------------------------------------------------------------------- + +LoadTablePointerE80D: + lda PointerLowTableE54E,y + sta $03 + + lda PointerHighTableE554,y + sta $04 + + rts + +; --------------------------------------------------------------------------- +; $E818-$E835 +; Set the per-slot $98 flag when slot state satisfies the required bounds. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlotFlagE818: + lda $B0,x + bne @return + + lda $AC,x + cmp #$07 + bpl @return + + lda $94,x + cmp #$03 + bcs @set_flag + + cmp #$02 + bne @return + + lda $90,x + asl a + bcc @return + + +@set_flag: + lda #$FF + sta $98,x + + +@return: + rts + +; --------------------------------------------------------------------------- +; $E836-$E844: update $8C for four runtime slots. +; --------------------------------------------------------------------------- + +UpdateFourSlot8CE836: + ldx #$03 + +@slot_loop: + lda $B0,x + bne @next_slot + + jsr CalculateSlotRemaining + sta $8C,x + +@next_slot: + dex + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $E845-$E853: initialize per-slot $D8/$BC/$E4 fields. +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeSlotFieldsE845: + lda #$80 + sta $D8,x + + lda #$05 + jsr InitializeAndCalculateSlotE893 + + lsr a +StoreSlotBCE4E84F: + sta $BC,x + sta $E4,x + + rts + +; --------------------------------------------------------------------------- +; $E854-$E85C +; Selector-$02 wrapper. Tail-calls the shared $E86F continuation. +; --------------------------------------------------------------------------- + +InitializeSlotMode2E854: + lda #$02 + jsr InitializeAndCalculateSlotE893 + + lsr a + jmp AdjustSlotBCFromAE86F + + +; --------------------------------------------------------------------------- +; $E85D-$E869 +; Initialize slot state using selector $06, reuse the $E84F continuation, +; then set $D8,X to $60. +; --------------------------------------------------------------------------- + +InitializeSlotMode6E85D: + lda #$06 + jsr InitializeAndCalculateSlotE893 + + jsr StoreSlotBCE4E84F + + lda #$60 + sta $D8,x + + rts + +; --------------------------------------------------------------------------- +; $E86A-$E878 +; Adjust $BC,X using a value derived from selector $01. +; +; $E86F is also a secondary entry used by the $E854 wrapper. +; --------------------------------------------------------------------------- + +AdjustSlotBCE86A: + lda #$01 + jsr InitializeAndCalculateSlotE893 + + +; $E86F +AdjustSlotBCFromAE86F: + sta $00 + + lda $E4,x + sec + sbc $00 + sta $BC,x + + rts + +; --------------------------------------------------------------------------- +; $E879-$E892 +; Initialize/update slot fields using selector $04. +; X = current slot. +; --------------------------------------------------------------------------- +InitializeSlotMode4E879: + lda #$04 + jsr InitializeAndCalculateSlotE893 + + asl a + jsr StoreSlotBCE4E84F + + lda $A0,x + beq @set_d8 + + lda $BC,x + clc + adc #$10 + jsr StoreSlotBCE4E84F + +@set_d8: + lda #$40 + sta $D8,x + + rts + +; --------------------------------------------------------------------------- +; $E893-$E89C +; Store the incoming value into $D4,X/$B4,X, then return $C8,X-$D0,X in A. +; +; Entry: +; A = value to store +; X = current slot +; +; Return: +; A = $C8,X - $D0,X +; --------------------------------------------------------------------------- + +InitializeAndCalculateSlotE893: + sta $D4,x + sta $B4,x + + lda $C8,x + sec + sbc $D0,x + + rts + +; --------------------------------------------------------------------------- +; $E89D-$E8D2 +; Per-slot state helpers. +; --------------------------------------------------------------------------- + +UpdateRiderMotionState: + lda RiderMotionState,x + beq @update + + cmp #$01 + beq SlotA4ReturnE8BE + + +@update: + lda #$03 + jsr InitializeAndCalculateSlotE893 + + asl a + jsr AdjustSlotBCFromAE86F + + lda RiderMotionState,x + cmp #$04 + bne SlotA4ReturnE8BE + + jsr UpdateSlotFrom94DD06 + + lda #$02 + sta RiderMotionState,x + sta $0364,x + + +; $E8BE +SlotA4ReturnE8BE: + rts + + +; --------------------------------------------------------------------------- +; $E8BF +; Clear two per-slot state fields. +; --------------------------------------------------------------------------- + +ClearRiderMotionState: + lda #$00 + sta RiderMotionState,x + sta $A0,x + rts + +; --------------------------------------------------------------------------- +; $E8C6 +; Initialize RiderMotionState,X according to $B8,X. +; --------------------------------------------------------------------------- + +InitializeRiderMotionState: + lda #$01 + sta RiderMotionState,x + + lda $B8,x + cmp #$20 + bcs @return + + inc RiderMotionState,x + +@return: + rts + +; --------------------------------------------------------------------------- +; $E8D3-$E8E2 +; Set $03B6 to $08 when the current slot/global state is completely clear. +; +; X = current slot. +; --------------------------------------------------------------------------- + +InitializeGlobalB6E8D3: + txa + ora $B0,x + ora $03E0 + ora $3C + bne @return + + lda #$08 + sta $03B6 + +@return: + rts + +; --------------------------------------------------------------------------- +; $E8E3-$E8FE +; Small slot-state entry/trampoline cluster. +; --------------------------------------------------------------------------- + +; $E8E3 +SetSlotA4OneE8E3: + lda #$01 + + ; E8E5: D0 DC = BNE $E8C3 + ; $E8C3 is inside the preceding ClearRiderMotionState helper. + .byte $D0, $DC + + +; --------------------------------------------------------------------------- +; $E8E7 +; If $B0,X is clear, enter the $DCFE state-update path. +; --------------------------------------------------------------------------- + +UpdateSlotStateIfReadyE8E7: + lda $B0,x + + ; E8E9: D0 F7 = BNE $E8E2 + ; Reuses the RTS immediately before this cluster. + .byte $D0, $F7 + + jmp StateDCF2Increment94DCFE + + +; --------------------------------------------------------------------------- +; $E8EE +; Initialize $A0,X/$D8,X and tail-call shared $E84F store entry. +; --------------------------------------------------------------------------- + +InitializeSlotModeE8EE: + lda #$02 + sta $A0,x + + lsr a + sta $D8,x + + lda #$30 + jmp StoreSlotBCE4E84F + + +; --------------------------------------------------------------------------- +; $E8FA +; Tail-call the shared $E84F entry with A=$10. +; --------------------------------------------------------------------------- + +SetSlotBCE4To10E8FA: + lda #$10 + jmp StoreSlotBCE4E84F + +; --------------------------------------------------------------------------- +; $E8FF-$E926 +; Update per-slot boundary/position state while preserving Y. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlotBoundaryE8FF: + lda $B0,x + bne @return + + lda RiderMotionState,x + beq @save_y + + lsr a + lsr a + bcc @return + + +@save_y: + sty $01 + + lda $94,x + beq @restore_y + + ldy #$04 + + lda $5C,x + asl a + asl a + bcc @store_boundary + + dey + + +@store_boundary: + lda #$01 + sta $036C,x + + iny + iny + + jsr $CE5C + + +@restore_y: + ldy $01 + + +@return: + rts + +; --------------------------------------------------------------------------- +; $E927-$E933 +; Process each slot whose $CC,X field is nonzero. +; --------------------------------------------------------------------------- + +UpdateFourSlotCCE927: + ldx #$03 + +@slot_loop: + lda $CC,x + beq @next_slot + + jsr $E794 + +@next_slot: + dex + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $E934-$E962 +; Update per-slot $0388 state and dispatch into the existing state handlers. +; X = current slot. +; --------------------------------------------------------------------------- + +UpdateSlot0388E934: + lda $B0,x + + ; E936: D0 FB = BNE $E933 + .byte $D0, $FB + + ldy #$00 + + lda $94,x + + ; E93C: F0 F5 = BEQ $E933 + .byte $F0, $F5 + + cmp #$02 + bcs @store_state + + lda $90,x + cmp $D8,x + bcs @store_state + + iny + + +@store_state: + tya + sta $0388,x + + lda $A0,x + cmp #$02 + beq Slot0388UpdateFrom94E960 + jmp StateDCF2ZeroDCFA + + +; $E956 +; Secondary entry/path. +Slot0388CheckA4E956: + lda RiderMotionState,x + cmp #$01 + + ; E95A: D0 D7 = BNE $E933 + .byte $D0, $D7 + + lda #$00 + sta $BC,x + + +; $E960 +Slot0388UpdateFrom94E960: + jmp UpdateSlotFrom94DD06 + +; --------------------------------------------------------------------------- +; $E963-$E96B +; Clear three per-slot state fields. +; X = current slot. +; --------------------------------------------------------------------------- + +ClearSlotStateE963: + lda #$00 + sta $CC,x + sta $D4,x + sta $B4,x + rts +; --------------------------------------------------------------------------- +; $E96C-$EA06 +; Update four slots' $B8 position/state values. +; --------------------------------------------------------------------------- + +UpdateFourSlotPositionsE96C: + ldx #$03 + + +; $E96E +SlotPositionLoopE96E: + lda $58,x + cmp #$15 + beq SlotPositionCheckTableE97F + + cmp #$14 + beq SlotPositionCheckTableE97F + + lda $B8,x + clc + adc $DC,x + sta $B8,x + + +; $E97F +SlotPositionCheckTableE97F: + ldy #$03 + +; $E981 +SlotPositionTableLoopE981: + lda $B8,x + cmp SlotPositionThresholdTableE53D,y + + ; E986: F0 79 = BEQ $EA01 + ; Keep literal because the symbolic target is outside + ; ca65's relative branch range during reconstruction. + .byte $F0, $79 + + dey + bpl SlotPositionTableLoopE981 + + + lda RiderMotionState,x + beq SlotPositionCheckBoundsE9C4 + + cmp #$01 + bne SlotPositionCheckState3E9B8 + + lda $B8,x + cmp #$20 + bcs SlotPositionCheckBoundsE9C4 + + lda #$04 + sta RiderMotionState,x + + lda $58,x + beq SlotPositionResetBCE9AD + + cmp #$12 + beq SlotPositionResetBCE9AD + + cmp #$10 + beq SlotPositionResetBCE9AD + + cmp #$0D + bne SlotPositionNextE9E3 + + +; $E9AD +SlotPositionResetBCE9AD: + lda #$00 + sta $BC,x + + jsr UpdateSlotFrom94DD06 + + dec RiderMotionState,x + bne SlotPositionNextE9E3 + + +; $E9B8 +SlotPositionCheckState3E9B8: + cmp #$03 + beq SlotPositionCheckBoundsE9C4 + + lda $B8,x + cmp #$20 + bcc SlotPositionCheckBoundsE9C4 + + dec $B8,x + + +; $E9C4 +SlotPositionCheckBoundsE9C4: + lda $B8,x + cmp #$08 + bcc SlotPositionLowE9E9 + + cmp #$3A + bcc SlotPositionNextE9E3 + + lda #$39 + sta $B8,x + + +; $E9D2 +; Shared entry from the $E9F9 path below. +SlotPositionCheckFlagsE9D2: + lda $9C,x + ora $03E0,x + bne SlotPositionNextE9E3 + + +; $E9D9 +SlotPositionFlipDirectionE9D9: + lda $DC,x + bne @invert + + lda #$FF + +@invert: + eor #$FE + sta $DC,x + + +; $E9E3 +SlotPositionNextE9E3: + dex + + ; E9E4: 30 21 = BMI $EA07 + ; $EA07 remains in the binary-backed next block. + .byte $30, $21 + + jmp SlotPositionLoopE96E + + +; $E9E9 +SlotPositionLowE9E9: + lda $9C,x + bne SlotPositionUseCurrentE9F1 + + lda #$07 + bne SlotPositionStoreE9F9 + + +; $E9F1 +SlotPositionUseCurrentE9F1: + lda $B8,x + cmp #$02 + bcs SlotPositionStoreE9F9 + + lda #$01 + + +; $E9F9 +SlotPositionStoreE9F9: + sta $B8,x + + cpx #$00 + beq SlotPositionFlipDirectionE9D9 + + bne SlotPositionCheckFlagsE9D2 + + +; $EA01 +SlotPositionTableMatchEA01: + lda #$00 + sta $DC,x + + beq SlotPositionNextE9E3 + + +; --------------------------------------------------------------------------- +; $EA07-$EA43 +; Update slot-0 direction/global trigger state from input/state conditions. +; --------------------------------------------------------------------------- + +UpdateSlot0DirectionEA07: + lda $4F + beq Slot0DirectionReturnEA43 + + lda $98 + ora $03E0 + ora $03F7 + bne Slot0DirectionReturnEA43 + + lda $B0 + beq @check_state + + lda $0388 + cmp #$02 + bne Slot0DirectionReturnEA43 + + inc $0388 + + +@check_state: + lda $9C + beq @check_input + + cmp #$05 + bne Slot0DirectionReturnEA43 + + +@check_input: + lda $14 + and #$0C + beq Slot0DirectionReturnEA43 + + and #$04 + bne @negative_direction + + lda #$01 + bne @store_direction + + +@negative_direction: + lda #$FF + + +@store_direction: + sta $DC + + lda #$04 + ora $FD + sta $FD + + +; $EA43 +Slot0DirectionReturnEA43: + rts + +; --------------------------------------------------------------------------- +; $EA44-$EA8E +; Build sprite/OAM entries for four runtime slots. +; +; X = slot index, 3..0 +; Y = slot's eight-byte OAM offset +; --------------------------------------------------------------------------- + +BuildFourSlotSpritesEA44: + ldx #$03 + + +; $EA46 +@slot_loop: + txa + asl a + asl a + asl a + tay + + lda $84,x + lsr a + bcc @alternate_state + + lda $80,x + cmp #$F4 + bcc @build_primary + + bcs @build_secondary + + +; $EA58 +@alternate_state: + lsr a + bcs @build_secondary + + lda $80,x + cmp #$F6 + bcc @build_secondary + + +; $EA61 +@build_primary: + lda $B0,x + beq @build_secondary + + lda #$B6 + jsr CalculateSlotRemainingFromADC9A + sta $0294,y + + lda $80,x + clc + adc #$0A + sta $0297,y + + sty $01 + + ldy $B4,x + lda $E69A,y + + ldy $01 + sta $0295,y + + lda #$00 + sta $0296,y + + ; Original EA86: F0 03 = BEQ $EA8B. + ; A was just loaded with zero, so this is always taken. + beq @next_slot + + +; $EA88 +@build_secondary: + jsr BuildSlotSprites + + +; $EA8B +@next_slot: + dex + bpl @slot_loop + + rts + +; --------------------------------------------------------------------------- +; $EA8F-$EAC4 +; Slot-0/global-state helper. +; Copies seven display bytes into four-byte-spaced $02C0 entries, +; or initializes global state when $57 is zero. +; --------------------------------------------------------------------------- + +UpdateGlobalStateEA8F: + cpx #$00 + bne GlobalStateReturnEAC4 + + lda #$1D + sta $3A + + lda $57 + beq GlobalStateInitializeEAB5 + + lda $3C + bne GlobalStateClear3AEAC0 + + stx $0D + + ldx #$06 + ldy #$19 + + +; $EAA5 +GlobalStateCopyLoopEAA5: + lda $03D9,x + sta $02C0,y + + dey + dey + dey + dey + + dex + bpl GlobalStateCopyLoopEAA5 + + ldx $0D + rts + + +; $EAB5 +GlobalStateInitializeEAB5: + lda #$10 + sta $32 + + lda #$02 + sta $FD + + lsr a + sta $52 + + +; $EAC0 +GlobalStateClear3AEAC0: + lda #$00 + sta $3A + + +; $EAC4 +GlobalStateReturnEAC4: + rts + +; --------------------------------------------------------------------------- +; $EAC5-$EB16 +; Build/update display structures from global state and ROM tables. +; --------------------------------------------------------------------------- + +BuildGlobalDisplayEAC5: + lda $52 + ora $3C + + ; EAC9: D0 F5 = BNE $EAC0 + ; Reuses the preceding routine's clear-$3A path. + .byte $D0, $F5 + + lda $3A + + ; EACD: F0 F5 = BEQ $EAC4 + ; Reuses the preceding routine's RTS. + .byte $F0, $F5 + + ldx #$00 + ldy #$03 + + lda $3A + cmp #$0D + bcc @build_first + + lsr a + bcc @return + + lda #$40 + sta $FB + + +; $EAE0 +@build_first: + lda #$42 + + +; $EAE2 +@first_loop: + sta $02B4,x + inx + + lda $E540,y + sta $02B4,x + inx + + lda #$00 + sta $02B4,x + inx + + lda $E543,y + sta $02B4,x + inx + + dey + bne @build_first + + + ldx #$06 + + +; $EAFF +@second_loop: + lda #$4A + sta $02C0,y + + iny + iny + + lda #$00 + sta $02C0,y + + iny + + lda $E547,x + sta $02C0,y + + iny + + dex + bpl @second_loop + + +; $EB16 +@return: + rts + +; --------------------------------------------------------------------------- +; $EB17-$EC3A +; Global runtime/state-machine update. +; Contains several shared/secondary entries. +; --------------------------------------------------------------------------- + +UpdateGlobalRuntimeEB17: + lda $BE + bne GlobalRuntimeReturnEB7E + + lda $B4 + beq GlobalRuntimeCheckBDEB23 + + dec $B4 + bne GlobalRuntimeUpdateEB5E + + +; $EB23 +GlobalRuntimeCheckBDEB23: + lda $BD + ora $B9 + bne GlobalRuntimeUpdateEB5E + + sta $60 + sta $C0 + + lda $BF + beq GlobalRuntimeCheckC1EB35 + + cmp #$01 + bne GlobalRuntimeSecondaryEB9E + + +; $EB35 +GlobalRuntimeCheckC1EB35: + lda $C1 + cmp #$02 + beq GlobalRuntimeUpdateEB5E + + lda $C1 + beq GlobalRuntimeCheckBFEB49 + + lda #$09 + sta $B8 + + +; $EB43 +; Also targeted by the BEQ at $EB91. +GlobalRuntimeSetC1EB43: + lda #$02 + sta $C1 + + bne GlobalRuntimeSetFlagsEB93 + + +; $EB49 +GlobalRuntimeCheckBFEB49: + lda $BF + bne GlobalRuntimeUpdateEB5E + + lda $5C + asl a + bcc GlobalRuntimeShiftAgainEB7F + + lda #$00 + sta $49 + + lda $2B + bne GlobalRuntimeReturnEB9D + + lda #$05 + sta $2B + + +; $EB5E +GlobalRuntimeUpdateEB5E: + lda #$04 + sta $60 + + jsr $EC99 + + ldy $B3 + + lda #$00 + sta $BD + + jsr CheckRuntimeTableValuesECC9 + bne GlobalRuntimeIncrementBDEB7C + + iny + cpy #$40 + bne GlobalRuntimeCheckNextEB77 + + ldy #$00 + + +; $EB77 +GlobalRuntimeCheckNextEB77: + jsr CheckRuntimeTableValuesECC9 + beq GlobalRuntimeReturnEB9D + + +; $EB7C +GlobalRuntimeIncrementBDEB7C: + inc $BD + + +; $EB7E +GlobalRuntimeReturnEB7E: + rts + + +; --------------------------------------------------------------------------- +; $EB7F +; Secondary continuation from the $5C bit check above. +; --------------------------------------------------------------------------- + +GlobalRuntimeShiftAgainEB7F: + asl a + + ; EB80: 90 74 = BCC $EBF6 + .byte $90, $74 + + lda $49 + bne GlobalRuntimeReturnEB9D + + jsr InitializeRuntimeFieldsEC75 + + lda $B8 + cmp #$FF + beq GlobalRuntimeReturnEB9D + + cmp #$09 + beq GlobalRuntimeSetC1EB43 + + +; $EB93 +GlobalRuntimeSetFlagsEB93: + lda #$01 + sta $BE + sta $49 + + lda #$40 + sta $FB + + +; $EB9D +GlobalRuntimeReturnEB9D: + rts + + +; --------------------------------------------------------------------------- +; $EB9E +; Secondary entry used by the $BF state path. +; --------------------------------------------------------------------------- + +GlobalRuntimeSecondaryEB9E: + lda $49 + bne GlobalRuntimeCheck5CEBB6 + + lda $5C + asl a + asl a + bcc GlobalRuntimeUpdateTrackEBBD + + jsr EnterNmiSequenceMode + jsr CopyWorkingBufferToPersistentEC3B + + stx $47 + stx $42 + + lda #$08 + sta $41 + + +; $EBB6 +GlobalRuntimeCheck5CEBB6: + lda $5C + bne GlobalRuntimeReturnEBBC + + sta $49 + + +; $EBBC +GlobalRuntimeReturnEBBC: + rts + + +; --------------------------------------------------------------------------- +; $EBBD +; Secondary state/update entry. +; --------------------------------------------------------------------------- + +GlobalRuntimeUpdateTrackEBBD: + jsr UpdateOamEntriesEC5D + + lda $3A + + ; EBC2: D0 F8 = BNE $EBBC + .byte $D0, $F8 + + inc $3A + + lda $5C + cmp #$80 + beq GlobalRuntimeIncrement05E0EBCD + + cmp #$08 + bne GlobalRuntimeCheckMode4EBE9 + + +; $EBCD +GlobalRuntimeIncrement05E0EBCD: + inc $05E0 + + lda $05E0 + cmp #$0A + bmi GlobalRuntimeSetTriggerEBDC + + lda #$01 + sta $05E0 + + +; $EBDC +GlobalRuntimeSetTriggerEBDC: + lda #$01 + sta $FF + + lsr a + sta $49 + + jmp BuildOamEntryEC47 + + +; $EBE9 +GlobalRuntimeCheckMode4EBE9: + cmp #$04 + + ; EBEB: D0 C9 = BNE $EBB6 + .byte $D0, $C9 + + dec $05E0 + bne GlobalRuntimeSetTriggerEBDC + + lda #$09 + + ; EBF4: D0 E6 = BNE $EBDC + .byte $D0, $E6 + + +; --------------------------------------------------------------------------- +; $EBF6 +; Secondary entry reached by the branch at $EB80. +; --------------------------------------------------------------------------- + +GlobalRuntimeUpdateC2EBF6: + lda $2C + + ; EBF8: D0 40 = BNE $EC3A + .byte $D0, $40 + + lda #$08 + sta $2C + + lda $5C + lsr a + bcs GlobalRuntimeIncrementC2EC10 + + lsr a + + ; EC02: 90 34 = BCC $EC38 + .byte $90, $34 + + dec $C2 + + lda $C2 + bpl GlobalRuntimeSetFlagEC18 + + lda #$14 + bne GlobalRuntimeStoreC2EC16 + + +; $EC10 +GlobalRuntimeIncrementC2EC10: + inc $C2 + + lda $C2 + cmp #$15 + bcc GlobalRuntimeSetFlagEC18 + + lda #$00 + + +; $EC16 +GlobalRuntimeStoreC2EC16: + sta $C2 + + +; $EC18 +GlobalRuntimeSetFlagEC18: + lda #$01 + sta $FF + + lsr a + sta $49 + + lda #$D0 + sta $0230 + + lda #$CA + sta $0231 + + lda #$00 + sta $0232 + + ldx $C2 + lda $E6F6,x + sta $0233 + + +; $EC3A +GlobalRuntimeReturnEC3A: + rts + + +; --------------------------------------------------------------------------- +; $EC3B-$EC46 +; Copy the complete 256-byte working buffer from $05E0 to $06E0. +; X starts at zero, wraps through $FF, and returns to zero. +; --------------------------------------------------------------------------- + +CopyWorkingBufferToPersistentEC3B: + ldx #$00 + +@copy: + lda $05E0,x + sta $06E0,x + + dex + bne @copy + + rts + +; --------------------------------------------------------------------------- +; $EC47-$EC5C +; Build the OAM entry at $0234-$0237. +; --------------------------------------------------------------------------- + +BuildOamEntryEC47: + lda #$CF + sta $0234 + + lda $05E0 + sta $0235 + + lda #$00 + sta $0236 + + lda #$E4 + sta $0237 + + rts + +; --------------------------------------------------------------------------- +; $EC5D-$EC74 +; Update the $0228/$022C display entries according to frame/state flags. +; --------------------------------------------------------------------------- + +UpdateOamEntriesEC5D: + lda #$F8 + jsr $EC25 + + lda $3F + and #$10 + beq @use_c7 + + lda #$F8 + bne @store + + +@use_c7: + lda #$C7 + + +@store: + sta $0228 + sta $022C + + rts + +; --------------------------------------------------------------------------- +; $EC75-$EC98 +; Initialize working runtime fields from the current B0-B2 state and +; select a value from the $E6E1 table using $C2. +; --------------------------------------------------------------------------- + +InitializeRuntimeFieldsEC75: + lda #$00 + sta $CA + sta $C8 + sta $C9 + + lda $B2 + sta $BA + + lda $B0 + sta $BB + + lda $B1 + sta $BC + + ldy $C2 + lda DataTableE6E1,y + sta $B8 + + cmp #$11 + bcc @return + + and #$0F + sta $C8 + +@return: + rts + +; --------------------------------------------------------------------------- +; $EC99-$ECBE +; Accumulate $60 into $B9. Every time the accumulator reaches $08, +; reset it, set $C0, perform the $F6FB update, and advance $B2/$B3. +; --------------------------------------------------------------------------- + +UpdateRuntimeAccumulatorEC99: + lda $60 + + ; EC9B: F0 FB = BEQ $EC98 + ; Reuses the RTS from InitializeRuntimeFieldsEC75. + .byte $F0, $FB + + clc + adc $B9 + sta $B9 + + cmp #$08 + + ; ECA4: D0 F2 = BNE $EC98 + ; Reuses the same preceding RTS. + .byte $D0, $F2 + + lda #$01 + sta $C0 + + lsr a + sta $B9 + + jsr $F6FB + + lda $B2 + jsr IncrementWrap40ECBF + sta $B2 + + lda $B3 + jsr IncrementWrap40ECBF + sta $B3 + + rts + +; --------------------------------------------------------------------------- +; $ECBF-$ECC8 +; Increment A and wrap $40 back to $00. +; +; Entry: +; A = value +; +; Return: +; A = (A + 1) modulo $40 +; --------------------------------------------------------------------------- + +IncrementWrap40ECBF: + clc + adc #$01 + + cmp #$40 + bne @return + + lda #$00 + +@return: + rts + +; --------------------------------------------------------------------------- +; $ECC9-$ECE3 +; Check four indexed runtime-table values. +; +; Entry: +; Y = table index +; +; Return: +; A = $00 if all four expected values match +; A = $01 otherwise +; +; The success path reuses LDA #$00 / RTS at $ECC6-$ECC8. +; --------------------------------------------------------------------------- + +CheckRuntimeTableValuesECC9: + lda #$3D + + cmp $0540,y + bne @no_match + + cmp $0480,y + bne @no_match + + lda #$3B + + cmp $0400,y + bne @no_match + + cmp $04C0,y + + ; ECDF: F0 E5 = BEQ $ECC6 + ; Reuses LDA #$00 / RTS from IncrementWrap40ECBF. + .byte $F0, $E5 + + +@no_match: + lda #$01 + rts + +; --------------------------------------------------------------------------- +; $ECE4-$ED39 +; Build two related structures in $0400/$0423 from a four-byte ROM entry, +; then tail-call the existing $C289 handler. +; +; Entry: +; X = index into table at $E6A1 +; --------------------------------------------------------------------------- + +BuildPairedPpuRows: + ldy #$03 + + +; $ECE6 +@load_parameters: + lda PpuRowDescriptorsE6A1,x + inx + sta $0002,y + + dey + bpl @load_parameters + + + lda $05 + sta $0400 + + lda $02 + sta $0401 + + ldx #$20 + stx $0402 + + +; $ECFF +@fill_first: + lda $03 + sta $0403,x + dex + + lda $04 + sta $0403,x + dex + + bpl @fill_first + + + lda $05 + eor #$04 + sta $0423 + + lda $02 + sta $0424 + + ldx #$20 + stx $0425 + + +; $ED1E +@fill_second: + lda $03 + sta $0426,x + dex + + lda $04 + sta $0426,x + dex + + bpl @fill_second + + + lda #$00 + sta $0446 + + lda #$00 + sta $00 + + lda #$04 + jmp $C289 + +; --------------------------------------------------------------------------- +; $ED3A-$ED49 +; Runtime/course data preceding the $40-delimited record stream. +; --------------------------------------------------------------------------- + +RuntimeDataED3A: + .byte $46, $59, $C8, $D2 + .byte $A7, $E0, $ED, $EE + .byte $ED, $EE, $EF, $06 + .byte $02, $40, $10, $30 + + +; --------------------------------------------------------------------------- +; $ED4A-$EE09 +; Structured runtime/course command stream. +; --------------------------------------------------------------------------- + +RuntimeCommandDataED4A: +; $ED4A + .byte $40, $36, $0D, $31, $40, $07, $05, $40 + .byte $02, $88, $40, $02, $88, $40, $1C, $07 + +; $ED5A + .byte $40, $25, $10, $40, $0A, $07, $40, $02 + .byte $07, $40, $02, $8D, $40, $39, $07, $40 + +; $ED6A + .byte $02, $07, $40, $02, $07, $40, $0E, $0F + .byte $40, $10, $0C, $40, $10, $06, $40, $05 + +; $ED7A + .byte $0A, $40, $2B, $0E, $40, $1E, $0F, $40 + .byte $02, $0E, $40, $26, $0E, $40, $02, $08 + +; $ED8A + .byte $40, $02, $08, $40, $02, $08, $40, $02 + .byte $08, $40, $02, $88, $40, $12, $06, $40 + +; $ED9A + .byte $02, $11, $46, $0A, $17, $40, $14, $01 + .byte $40, $02, $01, $40, $02, $81, $40, $02 + +; $EDAA + .byte $81, $40, $02, $01, $40, $02, $01, $40 + .byte $11, $30, $8D, $40, $0A, $0C, $40, $0B + +; $EDBA + .byte $0D, $40, $03, $8D, $40, $09, $8C, $40 + .byte $02, $0C, $40, $0E, $31, $09, $02, $40 + +; $EDCA + .byte $0F, $30, $40, $34, $03, $40, $07, $04 + .byte $40, $0C, $03, $40, $0A, $04, $40, $0E + +; $EDDA + .byte $03, $40, $06, $83, $40, $09, $04, $40 + .byte $06, $84, $40, $0A, $31, $07, $40, $04 + +; $EDEA + .byte $07, $40, $05, $8B, $40, $0E, $06, $40 + .byte $09, $0A, $40, $07, $0F, $40, $02, $10 + +; $EDFA + .byte $40, $07, $06, $40, $02, $0B, $40, $02 + .byte $0A, $40, $0B, $84, $40, $0B, $13, $43 + + +; --------------------------------------------------------------------------- +; $EE0A-$EFFF +; Structured course-definition / object-command stream. +; --------------------------------------------------------------------------- + +CourseDefinitionDataEE0A: + +; $EE0A + .byte $37, $1B, $40, $18, $83, $40, $0A, $0E + .byte $40, $09, $07, $40, $02, $11, $46, $16 + +; $EE1A + .byte $17, $40, $0F, $0E, $40, $0B, $05, $40 + .byte $05, $87, $40, $09, $03, $40, $0E, $05 + +; $EE2A + .byte $40, $06, $0A, $40, $06, $06, $40, $05 + .byte $05, $40, $0F, $0F, $40, $0B, $30, $13 + +; $EE3A + .byte $43, $37, $1B, $40, $0D, $12, $42, $2A + .byte $19, $40, $11, $04, $40, $02, $8C, $40 + +; $EE4A + .byte $06, $0D, $40, $05, $0C, $40, $05, $03 + .byte $40, $02, $8D, $40, $15, $31, $09, $02 + +; $EE5A + .byte $40, $0F, $30, $40, $33, $31, $15, $41 + .byte $06, $21, $45, $06, $23, $40, $0A, $07 + +; $EE6A + .byte $40, $06, $87, $40, $08, $0C, $40, $0B + .byte $15, $41, $06, $21, $45, $06, $23, $40 + +; $EE7A + .byte $03, $87, $40, $0C, $0F, $40, $02, $10 + .byte $40, $0F, $07, $40, $0B, $07, $40, $26 + +; $EE8A + .byte $08, $40, $02, $0B, $40, $02, $88, $40 + .byte $24, $0E, $40, $02, $08, $40, $02, $11 + +; $EE9A + .byte $46, $0A, $17, $40, $02, $0E, $40, $02 + .byte $11, $46, $0E, $17, $40, $15, $10, $40 + +; $EEAA + .byte $06, $81, $40, $02, $81, $40, $02, $01 + .byte $40, $02, $01, $40, $02, $01, $40, $02 + +; $EEBA + .byte $01, $40, $17, $30, $13, $43, $37, $1B + .byte $40, $1F, $0C, $40, $0E, $0D, $40, $04 + +; $EECA + .byte $8D, $40, $05, $0C, $40, $1B, $31, $09 + .byte $02, $40, $10, $30, $40, $50, $31, $14 + +; $EEDA + .byte $44, $03, $1E, $44, $04, $1E, $44, $04 + .byte $1F, $1B, $40, $0F, $0D, $40, $05, $83 + +; $EEEA + .byte $40, $03, $0C, $40, $09, $14, $44, $03 + .byte $1E, $44, $04, $1E, $44, $04, $1F, $1B + +; $EEFA + .byte $40, $04, $07, $40, $02, $87, $40, $02 + .byte $87, $40, $09, $03, $40, $04, $84, $40 + +; $EF0A + .byte $05, $04, $40, $07, $83, $40, $05, $10 + .byte $40, $0C, $8A, $40, $0C, $07, $40, $02 + +; $EF1A + .byte $11, $46, $0A, $17, $40, $19, $0E, $40 + .byte $0D, $0E, $40, $04, $0B, $40, $04, $08 + +; $EF2A + .byte $40, $02, $08, $40, $02, $88, $40, $1A + .byte $03, $40, $06, $83, $40, $03, $04, $40 + +; $EF3A + .byte $08, $0E, $40, $04, $0B, $40, $02, $0B + .byte $40, $02, $0B, $40, $02, $8B, $40, $02 + +; $EF4A + .byte $8B, $40, $21, $0F, $40, $0C, $0D, $40 + .byte $02, $88, $40, $02, $13, $43, $17, $1B + +; $EF5A + .byte $40, $06, $12, $42, $17, $19, $40, $03 + .byte $03, $40, $08, $05, $40, $02, $05, $40 + +; $EF6A + .byte $02, $05, $40, $02, $05, $40, $02, $05 + .byte $40, $02, $11, $46, $0A, $30, $17, $40 + +; $EF7A + .byte $11, $10, $40, $0E, $0C, $40, $06, $8C + .byte $40, $09, $0D, $40, $02, $8D, $40, $0C + +; $EF8A + .byte $0C, $40, $05, $0D, $40, $06, $8C, $40 + .byte $02, $0C, $40, $07, $8D, $40, $05, $0C + +; $EF9A + .byte $40, $04, $0D, $40, $07, $8C, $40, $02 + .byte $31, $0E, $40, $05, $09, $02, $40, $10 + +; $EFAA + .byte $30, $40, $32, $0F, $40, $0F, $0C, $40 + .byte $08, $31, $0E, $40, $02, $11, $46, $0E + +; $EFBA + .byte $17, $40, $02, $0A, $40, $06, $15, $41 + .byte $06, $21, $45, $06, $23, $40, $02, $08 + +; $EFCA + .byte $40, $03, $08, $40, $03, $08, $40, $03 + .byte $88, $40, $03, $88, $40, $09, $83, $40 + +; $EFDA + .byte $05, $04, $40, $08, $10, $40, $11, $0D + .byte $40, $13, $0C, $40, $14, $0E, $40, $13 + +; $EFEA + .byte $05, $40, $04, $05, $40, $05, $05, $40 + .byte $04, $06, $40, $02, $11, $46, $0A, $17 + +; $EFFA-$EFFF + .byte $40, $05, $0C, $40, $03, $14 + + + +; --------------------------------------------------------------------------- +; $F000-$F062 +; Continuation of structured course-definition command data. +; --------------------------------------------------------------------------- + +CourseDefinitionDataF000: + +; $F000 + .byte $44, $03, $1E, $44, $04, $1E, $44, $04 + .byte $1F, $1B, $40, $06, $07, $40, $03, $83 + +; $F010 + .byte $40, $04, $0E, $40, $17, $8A, $40, $03 + .byte $0E, $40, $02, $11, $46, $05, $17, $40 + +; $F020 + .byte $02, $0B, $40, $06, $01, $40, $02, $01 + .byte $40, $02, $08, $40, $02, $01, $40, $02 + +; $F030 + .byte $01, $40, $02, $01, $40, $02, $01, $40 + .byte $02, $05, $40, $21, $30, $0C, $40, $04 + +; $F040 + .byte $83, $40, $04, $0D, $40, $04, $04, $40 + .byte $08, $8C, $40, $02, $03, $40, $05, $8D + +; $F050 + .byte $40, $02, $0D, $40, $06, $0C, $40, $03 + .byte $8C, $40, $05, $0D, $40, $06, $04, $40 + +; $F060-$F062 + .byte $05, $31, $09 + + +; --------------------------------------------------------------------------- +; $F063-$F086 +; Low-byte table for course-pattern pointers. +; +; Consumed by LoadCourseStructurePointer: +; +; lda CourseStructurePointerLowF063,x +; sta $05 +; lda CourseStructurePointerHighF087,x +; sta $06 +; --------------------------------------------------------------------------- + +CourseStructurePointerLowF063: + .byte $AB, $B3, $01, $01, $0A, $12 + .byte $6B, $A4, $D1, $EB, $5E, $97 + .byte $E2, $CC, $B4, $96, $A5, $56 + .byte $2D, $35, $5D, $F8, $5E, $66 + .byte $4D, $3D, $55, $45, $C0, $6E + .byte $6E, $CE, $0A, $14, $DC, $EA + + +; --------------------------------------------------------------------------- +; $F087-$F0AA +; High-byte table for course-pattern pointers. +; --------------------------------------------------------------------------- + +CourseStructurePointerHighF087: + .byte $F0, $F0, $F1, $F1, $F1, $F1 + .byte $F1, $F1, $F1, $F1, $F2, $F2 + .byte $F2, $F2, $F4, $F4, $F4, $F4 + .byte $F3, $F3, $F3, $F2, $F4, $F4 + .byte $F3, $F3, $F3, $F3, $F3, $F4 + .byte $F4, $F3, $F3, $F3, $F3, $F3 + +CoursePatternF0AB: + .byte $09, $3B, $3C, $3D + .byte $3B, $3C, $3D, $00 + +CourseStructureTemplateF0B3: + + ; Variant 0 + .byte $08 + .byte $D7, $40, $40, $40, $40, $40, $41 + + ; Variant 1 + .byte $08 + .byte $D6, $FE, $FE, $FE, $FE, $FE, $D8 + + ; Variant 2 + .byte $07 + .byte $D7, $FE, $FE, $FE, $FE, $FE, $D9, $FE + + ; Variant 3 + .byte $07 + .byte $D6, $FE, $FE, $FE, $FE, $FE, $D8, $FE + + ; Variant 4 + .byte $07 + .byte $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE + + ; Variant 5 + .byte $07 + .byte $DA, $FF, $FF, $FF, $FF, $FF, $DC, $FE + + ; Variant 6 + .byte $07 + .byte $DB, $FF, $FF, $FF, $FF, $FF, $DD, $FE + + ; Variant 7 + .byte $08 + .byte $DA, $FF, $FF, $FF, $FF, $FF, $DC + + ; Variant 8 + .byte $08 + .byte $DB, $FF, $FF, $FF, $FF, $FF, $DD + + .byte $00 + +; --------------------------------------------------------------------------- +; $F101-$F109 (09 bytes) +; --------------------------------------------------------------------------- +CourseStructureTemplateF101: + .byte $08, $E0, $48, $49, $4A, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F10A-$F111 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF10A: + .byte $09, $3B, $3C, $E1, $48, $49, $4A, $00 + +; --------------------------------------------------------------------------- +; $F112-$F16A (59 bytes) +; --------------------------------------------------------------------------- +CoursePatternF112: + .byte $08, $C5, $50, $50, $50, $50, $50, $51, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $C5, $FE, $FE, $FE, $FE, $FE, $C6, $FE, $FE, $05, $C5, $FE, $FE, $FE + .byte $FE, $FE, $C6, $FE, $FE, $FE, $04, $2F, $2E, $2B, $2D, $2C, $2B, $2A, $FE, $FE + .byte $FE, $FE, $05, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE, $FE, $FE, $06, $C7, $C8 + .byte $C9, $CA, $C8, $C9, $CB, $FE, $FE, $07, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F16B-$F1A3 (39 bytes) +; --------------------------------------------------------------------------- +CoursePatternF16B: + .byte $08, $C5, $5E, $5E, $5E, $5E, $5E, $5F, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $C5, $FE, $FE, $FE, $FE, $FE, $C6, $FE, $FE, $05, $2F, $2E, $2B, $2D + .byte $2C, $2B, $2A, $FE, $FE, $FE, $06, $D1, $D2, $FF, $FF, $FF, $FF, $D3, $D4, $FE + .byte $08, $D1, $D0, $D0, $D0, $D0, $D0, $D5, $00 + +; --------------------------------------------------------------------------- +; $F1A4-$F1D0 (2D bytes) +; --------------------------------------------------------------------------- +CoursePatternF1A4: + .byte $08, $C5, $56, $56, $56, $56, $56, $57, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $2F, $2E, $2B, $2D, $2E, $2B, $2A, $FE, $FE, $07, $C7, $C8, $C9, $CA + .byte $C8, $C9, $CB, $FE, $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F1D1-$F1EA (1A bytes) +; --------------------------------------------------------------------------- +CoursePatternF1D1: + .byte $08, $C5, $58, $58, $58, $58, $58, $59, $07, $2F, $2E, $2B, $2D, $2C, $2B, $2A + .byte $FE, $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F1EB-$F25D (73 bytes) +; --------------------------------------------------------------------------- +CoursePatternF1EB: + .byte $08, $C5, $4E, $4E, $4E, $4E, $4E, $4F, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $06, $C5, $FE, $FE, $FE, $FE, $FE, $C6, $FE, $FE, $06, $3B, $3C, $3D, $3B + .byte $3C, $3D, $FE, $0F, $FE, $06, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $12, $FE, $06 + .byte $3B, $3C, $3D, $3B, $3C, $3D, $FE, $17, $FE, $06, $3B, $3C, $3D, $3B, $3C, $3D + .byte $FE, $12, $FE, $06, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $1C, $FE, $06, $3B, $3C + .byte $3D, $3B, $3C, $3D, $FE, $11, $FE, $06, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $FE, $07, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE, $08, $C7, $C8, $C9, $CA, $C8 + .byte $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F25E-$F296 (39 bytes) +; --------------------------------------------------------------------------- +CoursePatternF25E: + .byte $08, $CC, $60, $60, $60, $60, $60, $61, $06, $CC, $CD, $FE, $FE, $FE, $FE, $CE + .byte $CF, $FE, $05, $2F, $2E, $2B, $2D, $2C, $2B, $2A, $FE, $FE, $FE, $06, $C7, $C8 + .byte $C9, $CA, $C8, $C9, $CB, $FE, $FE, $07, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F297-$F2CB (35 bytes) +; --------------------------------------------------------------------------- +CoursePatternF297: + .byte $07, $CC, $CD, $65, $65, $65, $65, $66, $67, $05, $CC, $CD, $FE, $FE, $FE, $FE + .byte $CE, $CF, $FE, $FE, $04, $2F, $2E, $2B, $2D, $2C, $2B, $2A, $FE, $FE, $FE, $FE + .byte $05, $D1, $D2, $FF, $FF, $FF, $FF, $D3, $D4, $FE, $FE, $07, $D1, $D2, $FF, $FF + .byte $FF, $FF, $D3, $D4, $00 + +; --------------------------------------------------------------------------- +; $F2CC-$F2E1 (16 bytes) +; --------------------------------------------------------------------------- +CoursePatternF2CC: + .byte $09, $3B, $E7, $72, $3B, $E7, $72, $09, $3B, $E8, $BB, $3B, $E8, $BB, $09, $3B + .byte $E9, $BE, $3B, $E9, $BB, $00 + +; --------------------------------------------------------------------------- +; $F2E2-$F2F7 (16 bytes) +; --------------------------------------------------------------------------- +CoursePatternF2E2: + .byte $09, $70, $EA, $3D, $70, $EA, $3D, $09, $B9, $EB, $3D, $B9, $EB, $3D, $09, $BC + .byte $EC, $3D, $BC, $EC, $3D, $00 + +; --------------------------------------------------------------------------- +; $F2F8-$F309 (12 bytes) +; --------------------------------------------------------------------------- +CoursePatternF2F8: + .byte $08, $C5, $68, $68, $68, $68, $68, $69, $07, $C5, $FE, $FE, $FE, $FE, $FE, $C6 + .byte $FE, $00 + +; --------------------------------------------------------------------------- +; $F30A-$F313 (0A bytes) +; --------------------------------------------------------------------------- +CoursePatternF30A: + .byte $07, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $00 + +; --------------------------------------------------------------------------- +; $F314-$F32C (19 bytes) +; --------------------------------------------------------------------------- +CoursePatternF314: + .byte $05, $CC, $CD, $FE, $FE, $6D, $6D, $6E, $6F, $6D, $6D, $03, $CC, $CD, $FE, $FE + .byte $FE, $FE, $CE, $CF, $FE, $FE, $FE, $FE, $00 + +; --------------------------------------------------------------------------- +; $F32D-$F334 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF32D: + .byte $09, $3B, $3C, $3D, $C0, $C0, $C0, $00 + +; --------------------------------------------------------------------------- +; $F335-$F33C (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF335: + .byte $09, $C0, $C0, $C0, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F33D-$F344 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF33D: + .byte $09, $3B, $3C, $3D, $C3, $C3, $C3, $00 + +; --------------------------------------------------------------------------- +; $F345-$F34C (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF345: + .byte $09, $C3, $C3, $C3, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F34D-$F354 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF34D: + .byte $09, $3B, $3C, $3D, $E4, $E4, $E4, $00 + +; --------------------------------------------------------------------------- +; $F355-$F35C (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF355: + .byte $09, $E4, $E4, $E4, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F35D-$F3BF (63 bytes) +; --------------------------------------------------------------------------- +CoursePatternF35D: + .byte $07, $CC, $CD, $75, $75, $75, $75, $76, $77, $05, $CC, $CD, $FE, $FE, $FE, $FE + .byte $CE, $CF, $FE, $FE, $03, $CC, $CD, $FE, $FE, $FE, $FE, $CE, $CF, $FE, $FE, $FE + .byte $FE, $03, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $03, $3B + .byte $3C, $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $03, $3B, $3C, $3D, $E5 + .byte $D2, $FF, $D3, $D4, $FE, $FE, $FE, $FE, $03, $3B, $3C, $3D, $E6, $FC, $D1, $D2 + .byte $FF, $D3, $D4, $FE, $FE, $03, $3B, $3C, $3D, $E6, $FC, $FC, $FC, $D1, $D2, $FF + .byte $D3, $D4, $00 + +; --------------------------------------------------------------------------- +; $F3C0-$F3CD (0E bytes) +; --------------------------------------------------------------------------- +CoursePatternF3C0: + .byte $03, $3B, $3C, $3D, $E6, $FC, $FC, $FC, $FC, $FC, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F3CE-$F3DB (0E bytes) +; --------------------------------------------------------------------------- +CoursePatternF3CE: + .byte $03, $BF, $7D, $7E, $7F, $FC, $FC, $FC, $FC, $FC, $80, $81, $82, $00 + +; --------------------------------------------------------------------------- +; $F3DC-$F3E9 (0E bytes) +; --------------------------------------------------------------------------- +CoursePatternF3DC: + .byte $03, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $00 + +; --------------------------------------------------------------------------- +; $F3EA-$F455 (6C bytes) +; --------------------------------------------------------------------------- +CoursePatternF3EA: + .byte $03, $3B, $3C, $3D, $3B, $3C, $3D, $78, $78, $78, $78, $78, $78, $03, $3B, $3C + .byte $3D, $3B, $3C, $3D, $FE, $FE, $FE, $FE, $FE, $FE, $03, $C7, $C8, $C9, $CA, $C8 + .byte $C9, $CB, $FE, $FE, $FE, $FE, $FE, $04, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE + .byte $FE, $FE, $FE, $05, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $FE, $FE, $FE, $06, $C7 + .byte $C8, $C9, $CA, $C8, $C9, $CB, $FE, $FE, $07, $3B, $3C, $3D, $3B, $3C, $3D, $FE + .byte $FE, $07, $3B, $3C, $3D, $3B, $3C, $3D, $FE, $FE, $07, $C7, $C8, $C9, $CA, $C8 + .byte $C9, $CB, $FE, $08, $C7, $C8, $C9, $CA, $C8, $C9, $CB, $00 + +; --------------------------------------------------------------------------- +; $F456-$F45D (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF456: + .byte $09, $C0, $C0, $C0, $C0, $C0, $C0, $00 + +; --------------------------------------------------------------------------- +; $F45E-$F465 (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF45E: + .byte $09, $E4, $E4, $E4, $E4, $E4, $E4, $00 + +; --------------------------------------------------------------------------- +; $F466-$F46D (08 bytes) +; --------------------------------------------------------------------------- +CoursePatternF466: + .byte $09, $C3, $C3, $C3, $C3, $C3, $C3, $00 + +; --------------------------------------------------------------------------- +; $F46E-$F495 (28 bytes) +; --------------------------------------------------------------------------- +CoursePatternF46E: + .byte $03, $3B, $3C, $3D, $E6, $28, $29, $73, $73, $73, $70, $71, $72, $03, $3B, $3C + .byte $3D, $E6, $FC, $FC, $FC, $FC, $FC, $B9, $BA, $BB, $03, $3B, $3C, $3D, $E6, $FC + .byte $FC, $FC, $FC, $FC, $BC, $BD, $BE, $00 + +; --------------------------------------------------------------------------- +; $F496-$F4A4 (0F bytes) +; --------------------------------------------------------------------------- +CoursePatternF496: + .byte $09, $88, $3C, $3D, $3B, $3C, $3D, $09, $89, $3C, $3D, $3B, $3C, $3D, $00 + +; --------------------------------------------------------------------------- +; $F4A5-$F4B3 (0F bytes) +; --------------------------------------------------------------------------- +CoursePatternF4A5: + .byte $09, $3B, $3C, $3D, $3B, $3C, $88, $09, $3B, $3C, $3D, $3B, $3C, $89, $00 + +; --------------------------------------------------------------------------- +; $F4B4-$F4FE (4B bytes) +; --------------------------------------------------------------------------- +CoursePatternF4B4: + .byte $08, $E2, $8D, $8E, $8F, $90, $91, $92, $07, $E2, $DE, $DF, $E3, $FE, $3B, $3C + .byte $3D, $00, $00, $20, $18, $1A, $1C, $22, $16, $15, $0A, $19, $FE, $FE, $12, $42 + .byte $17, $19, $00, $13, $43, $17, $1B, $00, $11, $46, $0A, $17, $00, $14, $44, $03 + .byte $1E, $44, $04, $1E, $44, $04, $1F, $1B, $00, $15, $41, $06, $21, $45, $06, $23 + .byte $00, $DC, $D2, $D7, $E1, $ED, $F4, $F4, $F4, $F4, $F4 + +; --------------------------------------------------------------------------- +; $F4FF-$F62C +; Process the current course/object definition stream. +; --------------------------------------------------------------------------- + +ProcessCourseDefinition: + ldx $43 + + lda RuntimeDataED3A,x + sta $07 + + lda $ED40,x + sta $08 + + lda $EB + cmp $E0 + beq @position_matches + + rts + + +@position_matches: + lda $EC + + ; F514: D0 7B + .byte $D0, $7B + + lda $EF + + ; F518: D0 7D + .byte $D0, $7D + + ldy $ED + bne @advance_stream + + lda ($07),y + sta $57 + + inc $03A4 + + +@advance_stream: + lda $41 + cmp #$09 + bne @increment_stream + + lda $03BC + beq @increment_stream + + lda #$00 + sta $EE + sta $ED + + lda #$7F + + ; F537: D0 53 + .byte $D0, $53 + + +@increment_stream: + inc $ED + + ldy $ED + lda ($07),y + + ; F53F: F0 CF = BEQ $F510 + .byte $F0, $CF + + bit $F3D8 + + ; F544: F0 04 = BEQ $F54A + .byte $F0, $04 + + lda $46 + + ; F548: F0 CF = BEQ $F519 + .byte $F0, $CF + + +; $F54A +ProcessCourseDefinitionCommandF54A: + lda ($07),y + bit $F0B5 + + ; F54F: F0 24 = BEQ $F575 + .byte $F0, $24 + + and #$0F + tax + + lda $F4C6,x + sta $EE + + inc $ED + iny + + lda $41 + cmp #$09 + + ; F55E: D0 0F + .byte $D0, $0F + + cpy #$02 + + ; F562: D0 0B + .byte $D0, $0B + + lda #$01 + sta $57 + + lda ($07),y + sec + sbc #$3D + + ; F56F: D0 1B = BNE $F58C + .byte $D0, $1B + + +; $F571 + lda ($07),y + + ; F573: D0 17 = BNE $F58C + .byte $D0, $17 + + +; $F575 + ldx #$01 + + and #$3F + sta $EE + + cmp #$30 + + ; F57D: 90 17 = BCC $F596 + .byte $90, $17 + + ; F57F: D0 05 = BNE $F586 + .byte $D0, $05 + + lda $03BC + + ; F584: F0 01 = BEQ $F587 + .byte $F0, $01 + + dex + + +; $F587 + stx $03A8 + + ; F58A: 10 8D = BPL $F519 + .byte $10, $8D + + +; $F58C + and #$7F + sta $EC + + lda #$00 + sta $EF + + dec $EC + + ldx $EE + jsr LoadCourseStructurePointer + + ldy $EF + lda ($05),y + + ; F5A0: D0 03 = BNE $F5A5 + .byte $D0, $03 + + jmp ContinueCourseDefinition + + +; $F5A5 + jsr InitializeCourseStructure + + lda ($05),y + tax + + jsr CopyCourseStructure + + lda $EE + + ; F5B0: F0 08 = BEQ $F5BA + .byte $F0, $08 + + cmp #$18 + + ; F5B4: F0 04 = BEQ $F5BA + .byte $F0, $04 + + cmp #$1A + + ; F5B8: D0 0F = BNE $F5C9 + .byte $D0, $0F + + +; $F5BA +ProcessCourseToggleF5BA: + lda $03A2 + eor #$01 + sta $03A2 + + ; F5C2: D0 05 = BNE $F5C9 + .byte $D0, $05 + + lda #$83 + sta $030A + + +; $F5C9 +ProcessCourseAfterToggleF5C9: + iny + sty $EF + + lda $EE + cmp #$09 + bne @build_structure + + lda $57 + cmp #$01 + beq @build_structure + + cpy #$25 + bcc @build_structure + + cpy #$58 + bcs @build_structure + + ldx $03A5 + + lda $F4CD,x + sta $0310 + + inc $03A5 + + cpx #$05 + bne @build_structure + + lda $03A4 + sta $0310 + + +@build_structure: + lda #$8E + sta $0303 + + lda $EA + sta $0301 + + lda $E9 + sta $0302 + + lda $E8 + sta $0F + + jsr BuildCourseStructure + + lda $E8 + jsr IncrementWrap40ECBF + sta $E8 + + inc $E9 + + lda $E9 + cmp #$20 + bne @advance_eb + + lda #$00 + sta $E9 + + lda $EA + eor #$04 + sta $EA + + +@advance_eb: + lda $E0 + jsr IncrementWrap40ECBF + sta $EB + + rts + + +; --------------------------------------------------------------------------- +; $F62D-$F64D +; Continue processing the current course-definition event. +; Secondary entry from ProcessCourseDefinition. +; --------------------------------------------------------------------------- + +ContinueCourseDefinition: + sta $EF ; F62D: 85 EF + + lda $EE ; F62F: A5 EE + cmp #$09 ; F631: C9 09 + beq @special_type ; F633: F0 03 + + jmp $F51A ; F635: 4C 1A F5 + + +@special_type: + inc $03A4 ; F638: EE A4 03 + + dec $57 ; F63B: C6 57 + bne @reset_stream ; F63D: D0 05 + + lda #$01 ; F63F: A9 01 + sta $03BC ; F641: 8D BC 03 + + +@reset_stream: + lda #$00 ; F644: A9 00 + sta $ED ; F646: 85 ED + sta $03A5 ; F648: 8D A5 03 + + jmp $F525 ; F64B: 4C 25 F5 + +; --------------------------------------------------------------------------- +; $F64E-$F675 +; Build/copy the current $0300 structure. +; --------------------------------------------------------------------------- + +BuildCourseStructure: + ldx #$05 ; F64E: A2 05 + + lda #$0D ; F650: A9 0D + sta $0C ; F652: 85 0C + + +@next_entry: + txa ; F654: 8A + tay ; F655: A8 + + jsr LoadTablePointerE80D ; F656: 20 0D E8 + + stx $0A ; F659: 86 0A + + +@find_slot: + ldx $0C ; F65B: A6 0C + + lda $0304,x ; F65D: BD 04 03 + cmp #$FC ; F660: C9 FC + bne @store_value ; F662: D0 06 + + dec $0C ; F664: C6 0C + + lda $0C ; F666: A5 0C + bpl @find_slot ; F668: 10 F1 + + +@store_value: + ldy $0F ; F66A: A4 0F + sta ($03),y ; F66C: 91 03 + + dec $0C ; F66E: C6 0C + + ldx $0A ; F670: A6 0A + dex ; F672: CA + bpl @next_entry ; F673: 10 DF + + rts ; F675: 60 + + +; --------------------------------------------------------------------------- +; $F676-$F680 +; Fill $0303-$030D with $FC. +; --------------------------------------------------------------------------- + +InitializeCourseStructure: + ldx #$0A ; F676: A2 0A + lda #$FC ; F678: A9 FC + +@fill: + sta $0303,x ; F67A: 9D 03 03 + dex ; F67D: CA + bne @fill ; F67E: D0 FA + + rts ; F680: 60 + +; --------------------------------------------------------------------------- +; $F681-$F68C +; Copy bytes from ($05),Y into $0303+X until X reaches $0F. +; --------------------------------------------------------------------------- + +CopyCourseStructure: +@copy: + iny ; F681: C8 + lda ($05),y ; F682: B1 05 + sta $0303,x ; F684: 9D 03 03 + + inx ; F687: E8 + cpx #$0F ; F688: E0 0F + bne @copy ; F68A: D0 F5 + + rts ; F68C: 60 + +; --------------------------------------------------------------------------- +; $F68D-$F6FA +; Update runtime/course-object state. +; +; Falls through into AdvanceRuntimePosition. +; --------------------------------------------------------------------------- + +UpdateCourseObjectRuntime: + lda $BE ; F68D: A5 BE + + ; F68F: F0 FB = BEQ $F68C + ; Reuses the RTS at the end of CopyCourseStructure. + .byte $F0, $FB + + lda $CA ; F691: A5 CA + + ; F693: D0 32 = BNE $F6C7 + .byte $D0, $32 + + lda $C3 ; F695: A5 C3 + + ; F697: D0 2E = BNE $F6C7 + .byte $D0, $2E + + ldx $C8 ; F699: A6 C8 + txa ; F69B: 8A + + ; F69C: F0 29 = BEQ $F6C7 + .byte $F0, $29 + + dex ; F69E: CA + jsr LoadCourseListPointer + + ldy $C9 + lda ($07),y + + ; F6A6: D0 03 = BNE $F6AB + .byte $D0, $03 + + jmp $F71E + +; $F6AB + bit $F0B5 + + ; F6AE: F0 12 = BEQ $F6C2 + .byte $F0, $12 + + and #$0F + tax + + lda $F4C6,x + sta $B8 + + iny + lda ($07),y + sta $CA + + iny + sty $C9 + + ; F6C0: D0 05 = BNE $F6C7 + .byte $D0, $05 + +; $F6C2 + sta $B8 + + iny + sty $C9 + +; $F6C7 +UpdateCourseObjectLoadStructureF6C7: + ldx $B8 + jsr LoadCourseStructurePointer + + ldy $C3 + lda ($05),y + + ; F6D0: F0 3C = BEQ $F70E + .byte $F0, $3C + + jsr InitializeCourseStructure + + lda ($05),y + tax + + jsr CopyCourseStructure + + iny + sty $C3 + + lda #$8E + sta $0303 + + lda $B0 + sta $0301 + + lda $B1 + sta $0302 + + lda $B2 + sta $0F + + jsr BuildCourseStructure + + lda $B2 + jsr IncrementWrap40ECBF + sta $B2 + + ; Falls through to $F6FB. + +; --------------------------------------------------------------------------- +; $F6FB-$F70D +; Advance runtime position/state. +; +; $B1 advances through a 32-step range. When it wraps at $20, +; reset it to zero and toggle bit 2 of $B0. +; --------------------------------------------------------------------------- + +AdvanceRuntimePosition: + inc $B1 + + lda $B1 + cmp #$20 + bne @return + + lda #$00 + sta $B1 + + lda $B0 + eor #$04 + sta $B0 + +@return: + rts + +; --------------------------------------------------------------------------- +; $F70E-$F73D +; Update runtime/course state. +; --------------------------------------------------------------------------- + +UpdateRuntimeCourseState: + lda $C8 ; F70E: A5 C8 + beq @check_course ; F710: F0 0C + + lda #$00 ; F712: A9 00 + sta $C3 ; F714: 85 C3 + + lda $CA ; F716: A5 CA + + ; F718: F0 1F = BEQ $F739 + beq @set_be + + dec $CA ; F71A: C6 CA + bpl @set_be ; F71C: 10 1B + + +@check_course: + ldy $B2 ; F71E: A4 B2 + + jsr $ECC9 ; F720: 20 C9 EC + bne @clear_runtime ; F723: D0 0C + + iny ; F725: C8 + cpy #$40 ; F726: C0 40 + bne @check_next ; F728: D0 02 + + ldy #$00 ; F72A: A0 00 + + +@check_next: + jsr $ECC9 ; F72C: 20 C9 EC + + ; F72F: F0 0D = BEQ $F73E + beq ResetRuntimeCourseState + + +@clear_runtime: + lda #$00 ; F731: A9 00 + sta $B8 ; F733: 85 B8 + sta $C3 ; F735: 85 C3 + sta $C8 ; F737: 85 C8 + + +@set_be: + lda #$01 ; F739: A9 01 + sta $BE ; F73B: 85 BE + + rts ; F73D: 60 + + +; --------------------------------------------------------------------------- +; $F73E-$F754 +; Reset course/runtime state and restore the saved B2/B0/B1 values +; from BA/BB/BC. +; --------------------------------------------------------------------------- + +ResetRuntimeCourseState: + lda #$00 ; F73E: A9 00 + + sta $C3 ; F740: 85 C3 + sta $BE ; F742: 85 BE + sta $BD ; F744: 85 BD + sta $C8 ; F746: 85 C8 + + lda $BA ; F748: A5 BA + sta $B2 ; F74A: 85 B2 + + lda $BB ; F74C: A5 BB + sta $B0 ; F74E: 85 B0 + + lda $BC ; F750: A5 BC + sta $B1 ; F752: 85 B1 + + rts ; F754: 60 + + +; --------------------------------------------------------------------------- +; $F755-$F798 +; Search course/object data for a matching three-part pattern. +; --------------------------------------------------------------------------- + +FindCourseObjectPattern: + lda $B4 + bne FindCourseObjectReturnF798 + + lda $C0 + beq FindCourseObjectReturnF798 + + ldx #$15 + + +; --------------------------------------------------------------------------- +; $F75F +; Re-enter search using X as the course-pattern table index. +; --------------------------------------------------------------------------- + +FindCourseObjectSearchF75F: + jsr LoadCourseStructurePointer + + ldy #$00 + lda ($05),y + sta $03 + + lda #$0F + sec + sbc $03 + tay + + stx $04 + +@next_pattern: + ldx $B3 + + lda ($05),y + cmp $0540,x + bne @next_candidate + + dey + dey + dey + + lda ($05),y + cmp $0480,x + bne @next_candidate + + dey + dey + + lda ($05),y + cmp $0400,x + bne @next_candidate + + lda $04 + sta $C7 + + ; F790: 10 07 = BPL $F799 + bpl BuildCourseObjectList + +@next_candidate: + dec $04 + + ldx $04 + + ; F796: 10 C7 = BPL $F75F + bpl FindCourseObjectSearchF75F + + +FindCourseObjectReturnF798: + rts + + +; --------------------------------------------------------------------------- +; $F799-$F808 +; Build/update the runtime course/object list at $05E0. +; --------------------------------------------------------------------------- + +BuildCourseObjectList: + lda $C4 + + ; F79B: D0 0F = BNE $F7AC + bne CourseObjectContinueF7AC + + lda $C7 + sta $C6 + + ldx $B7 + jsr ReserveCourseListEntry + + lda #$3D + sta $C5 + + inc $C4 + + +CourseObjectContinueF7AC: + lda $C7 + cmp $C6 + + ; F7B0: D0 17 = BNE $F7C9 + .byte $D0, $17 + + lda $C6 + + ; F7B4: D0 E2 = BNE $F798 + .byte $D0, $E2 + + inc $C5 + + lda $C5 + cmp #$7F + + ; F7BC: 90 3C = BCC $F7FA + bcc CourseObjectCheckLimitF7FA + + ldx $B7 + sta $05E0,x + + inx + jsr ReserveCourseListEntry + + ; F7C7: F0 31 = BEQ $F7FA + beq CourseObjectCheckLimitF7FA + + +; $F7C9 +CourseObjectCheckSavedF7C9: + lda $C6 + + ; F7CB: D0 1A = BNE $F7E7 + .byte $D0, $1A + + +; $F7CD +CourseObjectAppendCurrentF7CD: + ldx $B7 + + lda $C5 + sta $05E0,x + + inx + stx $B7 + + lda $C7 + cmp #$11 + + ; F7DB: B0 2C = BCS $F809 + .byte $B0, $2C + + lda $C7 + sta $05E0,x + + jsr AdvanceCourseList + + ; F7E5: F0 05 = BEQ $F7EC + beq CourseObjectAppendSavedF7EC + + +; $F7E7 +CourseObjectReserveNextF7E7: + ldx $B7 + jsr ReserveCourseListEntry + + +; $F7EC +CourseObjectAppendSavedF7EC: + lda $C7 + sta $C6 + + cmp #$09 + + ; F7F2: D0 06 = BNE $F7FA + bne CourseObjectCheckLimitF7FA + + lda #$02 + sta $BF + sta $49 + + +; $F7FA +CourseObjectCheckLimitF7FA: + lda $B7 + cmp #$ED + + ; F7FE: 90 08 = BCC $F808 + bcc CourseObjectReturnF808 + + lda $C1 + + ; F802: D0 04 = BNE $F808 + bne CourseObjectReturnF808 + + lda #$01 + sta $C1 + + +; $F808 +CourseObjectReturnF808: + rts + + +; --------------------------------------------------------------------------- +; $F809-$F820 +; Copy a zero-terminated indirect list into the runtime list at $05E0. +; --------------------------------------------------------------------------- + +CopyCourseObjectList: + and #$0F ; F809: 29 0F + tax ; F80B: AA + dex ; F80C: CA + + jsr LoadCourseListPointer + ; F80D: 20 39 F8 + + ldy #$00 ; F810: A0 00 + +@copy: + ldx $B7 ; F812: A6 B7 + + lda ($07),y ; F814: B1 07 + + ; F816: F0 D4 = BEQ $F7EC + .byte $F0, $D4 + + sta $05E0,x ; F818: 9D E0 05 + + inx ; F81B: E8 + stx $B7 ; F81C: 86 B7 + + iny ; F81E: C8 + bne @copy ; F81F: D0 F1 + +; --------------------------------------------------------------------------- +; $F821-$F825 +; Write a $40 marker into the runtime list. +; +; Falls through into AdvanceCourseList. +; --------------------------------------------------------------------------- + +ReserveCourseListEntry: + lda #$40 + sta $05E0,x + + +; --------------------------------------------------------------------------- +; $F826-$F82D +; Advance runtime-list index and clear the current run counter. +; --------------------------------------------------------------------------- + +AdvanceCourseList: + inx + stx $B7 + + lda #$00 + sta $C5 + + rts + + +; --------------------------------------------------------------------------- +; $F82E-$F838 +; Load split ROM pointer into $05:$06. +; --------------------------------------------------------------------------- + +LoadCourseStructurePointer: + lda $F063,x + sta $05 + + lda $F087,x + sta $06 + + rts + + +; --------------------------------------------------------------------------- +; $F839-$F843 +; Load split ROM pointer into $07:$08. +; --------------------------------------------------------------------------- + +LoadCourseListPointer: + lda $F4F5,x + sta $07 + + lda $F4FA,x + sta $08 + + rts + +; --------------------------------------------------------------------------- +; $F844-$F86A +; NMI-time audio/APU update. +; --------------------------------------------------------------------------- + +UpdateAudio: + lda #$C0 + sta APUFRAMECNT + + jsr UpdatePulse1Channel + jsr UpdatePulse2Channel + jsr UpdateNoiseChannel + jsr UpdateSoundEffect + + lda #$00 + sta $FF + sta $FE + sta $FD + sta $FB + + lda $07FF + beq @return + + sta APUDMALOAD + dec $07FF + +@return: + rts + +; --------------------------------------------------------------------------- +; $F86B-$F88C +; Configure an APU pulse channel. +; +; Entry: +; X = channel register offset ($00, $04, or $08) +; Y = value written to the channel's sweep register +; A = index into the table at $FF00/$FF01 +; +; $F886 and $F88A are secondary entries selecting register offsets +; $04 and $08 before joining the common code at $F872. +; --------------------------------------------------------------------------- + +ConfigurePulse1F86B: + stx APUPC1BASE ; F86B: 8E 00 40 + sty APUPC1SWEEP ; F86E: 8C 01 40 + + ldx #$00 ; F871: A2 00 + +ConfigureApuChannelCommonF873: + tay ; F873: A8 + + lda ApuPeriodTableFF00+1,y ; F874: B9 01 FF + beq @return ; F877: F0 0B + + sta APUPC1TLOW,x ; F879: 9D 02 40 + + lda ApuPeriodTableFF00,y ; F87C: B9 00 FF + ora #$08 ; F87F: 09 08 + sta APUPC1THIGH,x ; F881: 9D 03 40 + +@return: + rts ; F884: 60 + + +; --------------------------------------------------------------------------- +; $F885 +; Select APU register group beginning at $4004, then enter common code. +; --------------------------------------------------------------------------- + +ConfigureApuChannel1F885: + ldx #$04 ; F885: A2 04 + + ; F887: D0 EA = BNE $F873 + .byte $D0, $EA + + +; --------------------------------------------------------------------------- +; $F889 +; Select APU register group beginning at $4008, then enter common code. +; --------------------------------------------------------------------------- + +ConfigureApuChannel2F889: + ldx #$08 ; F889: A2 08 + + ; F88B: D0 E6 = BNE $F873 + .byte $D0, $E6 + + +; --------------------------------------------------------------------------- +; $F88D-$F89B +; Pulse 1 effect setup. +; Entry from UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF88D: + sty $F0 ; F88D: 84 F0 + + lda #$01 ; F88F: A9 01 + sta $F5 ; F891: 85 F5 + + ldx #$80 ; F893: A2 80 + ldy #$9C ; F895: A0 9C + lda #$04 ; F897: A9 04 + + jmp ConfigurePulse1F86B ; F899: 4C 6B F8 + + +; --------------------------------------------------------------------------- +; $F89C-$F8AA +; Pulse 1 effect setup. +; Entry from UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF89C: + sty $F0 ; F89C: 84 F0 + + lda #$10 ; F89E: A9 10 + sta $F5 ; F8A0: 85 F5 + + ldx #$85 ; F8A2: A2 85 + ldy #$85 ; F8A4: A0 85 + lda #$30 ; F8A6: A9 30 + + jsr ConfigurePulse1F86B ; F8A8: 20 6B F8 + + +; --------------------------------------------------------------------------- +; $F8AB +; Tail-call into the continuation/effect handler at $F93A. +; --------------------------------------------------------------------------- + +Pulse1EffectF8AB: + jmp $F93A ; F8AB: 4C 3A F9 + + +; --------------------------------------------------------------------------- +; $F8AE-$F8D3 +; Pulse 1 effect/state setup. +; Contains alternate entry points used by the dispatcher. +; --------------------------------------------------------------------------- + +Pulse1EffectF8AE: + sty $F0 ; F8AE: 84 F0 + + lda #$09 ; F8B0: A9 09 + sta $F5 ; F8B2: 85 F5 + + lda #$04 ; F8B4: A9 04 + jsr $F871 ; F8B6: 20 71 F8 + + lda $F5 ; F8B9: A5 F5 + cmp #$04 ; F8BB: C9 04 + bne @check_07 ; F8BD: D0 03 + + jmp $F93E ; F8BF: 4C 3E F9 + +@check_07: + ldy #$84 ; F8C2: A0 84 + + cmp #$07 ; F8C4: C9 07 + bcs @write_sweep ; F8C6: B0 02 + + ldy #$8B ; F8C8: A0 8B + +@write_sweep: + sty APUPC1SWEEP ; F8CA: 8C 01 40 + + ora #$90 ; F8CD: 09 90 + sta APUPC1BASE ; F8CF: 8D 00 40 + + ; F8D2: D0 66 = BNE $F93A + .byte $D0, $66 + + +; --------------------------------------------------------------------------- +; $F8D4-$F90C +; Pulse 1 sound/event dispatcher. +; +; $F4 = active Pulse 1 effect/state +; $FF = pending event/request bits, destructively shifted +; $F0 = secondary request/event bits +; +; The branch targets outside this decoded block are kept as literal +; branch bytes until their destination routines are converted. +; --------------------------------------------------------------------------- + +UpdatePulse1Channel: + ldy $F4 ; F8D4: A4 F4 + + ; F8D6: D0 35 = BNE $F90D + .byte $D0, $35 + + ldy $FF ; F8D8: A4 FF + lda $F0 ; F8DA: A5 F0 + + lsr $FF ; F8DC: 46 FF + + ; F8DE: B0 AD = BCS $F88D + .byte $B0, $AD + + lsr a ; F8E0: 4A + + ; F8E1: B0 57 = BCS $F93A + .byte $B0, $57 + + lsr $FF ; F8E3: 46 FF + + ; F8E5: B0 B5 = BCS $F89C + .byte $B0, $B5 + + lsr a ; F8E7: 4A + + ; F8E8: B0 C1 = BCS $F8AB + .byte $B0, $C1 + + lsr a ; F8EA: 4A + + ; F8EB: B0 CC = BCS $F8B9 + .byte $B0, $CC + + lsr $FF ; F8ED: 46 FF + + ; F8EF: B0 BD = BCS $F8AE + .byte $B0, $BD + + lsr $FF ; F8F1: 46 FF + + ; F8F3: B0 19 = BCS $F90E + .byte $B0, $19 + + lsr a ; F8F5: 4A + + ; F8F6: B0 29 = BCS $F921 + .byte $B0, $29 + + lsr $FF ; F8F8: 46 FF + + ; F8FA: B0 4C = BCS $F948 + .byte $B0, $4C + + lsr a ; F8FC: 4A + + ; F8FD: B0 5C = BCS $F95B + .byte $B0, $5C + + lsr $FF ; F8FF: 46 FF + + ; F901: B0 6A = BCS $F96D + .byte $B0, $6A + + lsr a ; F903: 4A + + ; F904: B0 6F = BCS $F975 + .byte $B0, $6F + + lsr $FF ; F906: 46 FF + + ; F908: B0 73 = BCS $F97D + .byte $B0, $73 + + lsr a ; F90A: 4A + + ; F90B: B0 7D = BCS $F98A + .byte $B0, $7D + + rts ; F90D: 60 + + +; --------------------------------------------------------------------------- +; $F90E-$F947 +; Pulse 1 effect state machine. +; +; Entry at $F90E is selected from UpdatePulse1Channel. +; Several internal addresses are also direct dispatcher targets. +; --------------------------------------------------------------------------- + +Pulse1EffectF90E: + sty $F0 ; F90E: 84 F0 + + lda #$06 ; F910: A9 06 + sta $F5 ; F912: 85 F5 + + ldx #$9C ; F914: A2 9C + lda #$3C ; F916: A9 3C + + jsr ConfigurePulse1F86B ; F918: 20 6B F8 + + lda #$03 ; F91B: A9 03 + sta $F6 ; F91D: 85 F6 + + ; F91F: D0 0A = BNE $F92B + .byte $D0, $0A + + +; --------------------------------------------------------------------------- +; $F921 +; Secondary entry selected by UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF921: + dec $F6 ; F921: C6 F6 + beq Pulse1EffectF931 ; F923: F0 0C + + lda $F6 ; F925: A5 F6 + cmp #$03 ; F927: C9 03 + + ; F929: D0 E2 = BNE $F90D + ; $F90D is the dispatcher RTS. + .byte $D0, $E2 + + +; --------------------------------------------------------------------------- +; $F92B +; Update Pulse 1 sweep/state. +; --------------------------------------------------------------------------- + +Pulse1EffectSweepF92B: + lda #$9A ; F92B: A9 9A + sta APUPC1SWEEP ; F92D: 8D 01 40 + rts ; F930: 60 + + +; --------------------------------------------------------------------------- +; $F931 +; Alternate sweep state. +; --------------------------------------------------------------------------- + +Pulse1EffectF931: + lda #$93 ; F931: A9 93 + sta APUPC1SWEEP ; F933: 8D 01 40 + + lda #$06 ; F936: A9 06 + sta $F6 ; F938: 85 F6 + + +; --------------------------------------------------------------------------- +; $F93A +; Shared active-effect continuation. +; Also reached by JMP $F93A from $F8AB. +; --------------------------------------------------------------------------- + +Pulse1ActiveF93A: + dec $F5 ; F93A: C6 F5 + bne Pulse1EffectReturnF947 ; F93C: D0 09 + + +; --------------------------------------------------------------------------- +; $F93E +; Shared effect-stop entry. +; Also reached from $F8BF. +; --------------------------------------------------------------------------- + +Pulse1StopF93E: + lda #$00 ; F93E: A9 00 + sta $F0 ; F940: 85 F0 + + lda #$90 ; F942: A9 90 + sta APUPC1BASE ; F944: 8D 00 40 + + +Pulse1EffectReturnF947: + rts ; F947: 60 + +; --------------------------------------------------------------------------- +; $F948-$F9A1 +; Pulse 1 effect state machine. +; +; Contains several secondary entries selected directly from +; UpdatePulse1Channel. +; --------------------------------------------------------------------------- + +Pulse1EffectF948: + sty $F0 ; F948: 84 F0 + + lda #$03 ; F94A: A9 03 + sta $F5 ; F94C: 85 F5 + + ldx #$9C ; F94E: A2 9C + lda #$00 ; F950: A9 00 + + jsr ConfigurePulse1F86B ; F952: 20 6B F8 + + lda #$04 ; F955: A9 04 + sta $F6 ; F957: 85 F6 + + ; F959: D0 0A = BNE $F965 + .byte $D0, $0A + + +; --------------------------------------------------------------------------- +; $F95B +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF95B: + dec $F6 ; F95B: C6 F6 + beq Pulse1EffectF969 + + lda $F6 ; F95F: A5 F6 + cmp #$04 ; F961: C9 04 + + ; F963: D0 A8 = BNE $F90D + .byte $D0, $A8 + + +; $F965 +Pulse1EffectSweep9AF965: + lda #$9A ; F965: A9 9A + + ; F967: D0 C4 = BNE $F92D + .byte $D0, $C4 + + +; $F969 +Pulse1EffectF969: + lda #$93 ; F969: A9 93 + + ; F96B: D0 C6 = BNE $F933 + .byte $D0, $C6 + + +; --------------------------------------------------------------------------- +; $F96D +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF96D: + sty $F0 ; F96D: 84 F0 + + lda #$02 ; F96F: A9 02 + sta $F5 ; F971: 85 F5 + + ; F973: D0 D9 = BNE $F94E + .byte $D0, $D9 + + +; --------------------------------------------------------------------------- +; $F975 +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF975: + dec $F6 ; F975: C6 F6 + + ; F977: D0 E6 = BNE $F95F + .byte $D0, $E6 + + lda #$94 ; F979: A9 94 + + ; F97B: D0 B6 = BNE $F933 + .byte $D0, $B6 + + +; --------------------------------------------------------------------------- +; $F97D +; Secondary dispatcher entry. +; --------------------------------------------------------------------------- + +Pulse1EffectF97D: + sty $F0 ; F97D: 84 F0 + + lda #$21 ; F97F: A9 21 + sta $F5 ; F981: 85 F5 + + ldy #$BC ; F983: A0 BC + lda #$30 ; F985: A9 30 + + jsr ConfigurePulse1F86B ; F987: 20 6B F8 + + +; --------------------------------------------------------------------------- +; $F98A +; Secondary dispatcher entry and active effect update. +; --------------------------------------------------------------------------- + +Pulse1EffectF98A: + lda $F5 ; F98A: A5 F5 + cmp #$15 ; F98C: C9 15 + bne @update_volume ; F98E: D0 05 + + ldy #$7F ; F990: A0 7F + sty APUPC1SWEEP ; F992: 8C 01 40 + + +@update_volume: + lsr a ; F995: 4A + tay ; F996: A8 + + lda $F9A2,y + ; F997: B9 A2 F9 + + bcs @store_volume ; F99A: B0 02 + + lda #$13 ; F99C: A9 13 + +@store_volume: + sta APUPC1BASE ; F99E: 8D 00 40 + + ; F9A1: D0 97 = BNE $F93A + .byte $D0, $97 + + +; --------------------------------------------------------------------------- +; $F9A3-$F9B2 +; Continuation of the Pulse 1 effect lookup table whose first byte is +; physically shared with the branch operand at $F9A2. +; +; Logical table begins: +; +; F9A2: $97 ; also operand of BNE at $F9A1 +; F9A3: $91 +; ... +; --------------------------------------------------------------------------- + +Pulse1EffectTableF9A3: + .byte $91, $92, $93, $94 + .byte $95, $96, $97, $98 + .byte $9A, $9C, $9F, $9C + .byte $9A, $98, $96, $94 + +; --------------------------------------------------------------------------- +; $F9B3-$FA4F +; Audio effect lookup data. +; --------------------------------------------------------------------------- + +AudioEffectDataF9B3: + +; $F9B3 + .byte $00, $02, $04, $02, $00, $FE, $94, $96 + .byte $94, $93, $92, $91, $1C, $54, $16, $52 + +; $F9C3 + .byte $12, $18, $18, $20, $28, $30, $32, $34 + .byte $36, $38, $39, $3A, $3B, $3C, $3D, $3E + +; $F9D3 + .byte $3F, $40, $41, $42, $43, $44, $45, $46 + .byte $47, $48, $49, $4A, $4B, $4C, $4D, $4E + +; $F9E3 + .byte $4F, $50, $51, $52, $53, $54, $55, $56 + .byte $57, $58, $59, $5A, $5B, $5C, $5D, $5E + +; $F9F3 + .byte $5F, $70, $05, $04, $03, $02, $02, $02 + .byte $02, $02, $02, $02, $02, $02, $02, $02 + +; $FA03 + .byte $02, $02, $02, $02, $02, $02, $02, $02 + .byte $02, $02, $02, $02, $02, $02, $02, $02 + +; $FA13 + .byte $02, $01, $01, $01, $01, $01, $01, $01 + .byte $01, $01, $01, $01, $01, $01, $01, $01 + +; $FA23 + .byte $01, $00, $94, $96, $98, $9C, $80, $80 + .byte $80, $80, $80, $9E, $9D, $9C, $9B, $9A + +; $FA33 + .byte $99, $98, $97, $96, $95, $95, $94, $30 + .byte $38, $40, $48, $50, $54, $58, $50, $4C + +; $FA43 + .byte $48, $44, $40, $3C, $38, $34, $30, $2C + .byte $2A, $28, $26, $24 + +; --------------------------------------------------------------------------- +; $FA4F-$FA73 +; Pulse 2 effect setup/update entry. +; --------------------------------------------------------------------------- + +Pulse2EffectFA4F: + ldy #$15 + sty $F1 + + bne @active + + dec $07F3 + + ; FA58: D0 35 = BNE $FA8F + .byte $D0, $35 + + dec $F1 + + ; FA5C: F0 31 = BEQ $FA8F + .byte $F0, $31 + +@active: + ldx $FA24,y + + lda #$01 + cpx #$80 + bne @store_mode + + lda #$02 + +@store_mode: + sta $07F3 + + lda $FA39,y + ldy #$7F + + jmp WritePulse2State + + +; --------------------------------------------------------------------------- +; $FA74-$FA94 +; Pulse 2 channel update/dispatch. +; +; $FE = pending Pulse 2 request bits +; $F1 = active Pulse 2 effect/state +; $FC = additional audio request/state bits +; --------------------------------------------------------------------------- + +UpdatePulse2Channel: + ldy $FE ; FA74: A4 FE + + ; FA76: D0 D7 = BNE $FA4F + .byte $D0, $D7 + + ldy $F1 ; FA78: A4 F1 + + ; FA7A: D0 D9 = BNE $FA55 + .byte $D0, $D9 + + lda $FC ; FA7C: A5 FC + and #$7F ; FA7E: 29 7F + + ; FA80: D0 13 = BNE $FA95 + .byte $D0, $13 + + lda $F3 ; FA82: A5 F3 + beq @return ; FA84: F0 0E + + lda #$00 ; FA86: A9 00 + sta $F3 ; FA88: 85 F3 + sta $F7 ; FA8A: 85 F7 + sta $07F8 ; FA8C: 8D F8 07 + + lda #$90 ; FA8F: A9 90 + sta APUPC2BASE ; FA91: 8D 04 40 + +@return: + rts ; FA94: 60 + +; --------------------------------------------------------------------------- +; $FA95-$FAF6 +; Active Pulse 2 effect update. +; +; Updates effect timing/state in $F3/$F7, selects values from the +; audio tables around $F9B3-$F9F4, and prepares Pulse 2 APU registers. +; --------------------------------------------------------------------------- + +UpdateActivePulse2: + ldy $F7 ; FA95: A4 F7 + inc $F7 ; FA97: E6 F7 + + cpy $07F8 ; FA99: CC F8 07 + + ; FA9C: D0 31 = BNE $FACF + .byte $D0, $31 + + cmp #$30 ; FA9E: C9 30 + + ; FAA0: B0 1D = BCS $FABF + .byte $B0, $1D + + sec ; FAA2: 38 + sbc $F3 ; FAA3: E5 F3 + + ; FAA5: F0 1C = BEQ $FAC3 + .byte $F0, $1C + + ; FAA7: B0 0A = BCS $FAB3 + .byte $B0, $0A + + dec $F3 ; FAA9: C6 F3 + + cmp #$F8 ; FAAB: C9 F8 + + ; FAAD: B0 0C = BCS $FABB + .byte $B0, $0C + + dec $F3 ; FAAF: C6 F3 + + ; FAB1: D0 08 = BNE $FABB + .byte $D0, $08 + + +; $FAB3 +Pulse2IncrementStateFAB3: + inc $F3 ; FAB3: E6 F3 + + cmp #$08 ; FAB5: C9 08 + + ; FAB7: 90 02 = BCC $FABB + .byte $90, $02 + + inc $F3 ; FAB9: E6 F3 + + +; $FABB +Pulse2StoreDelayFABB: + lda #$01 ; FABB: A9 01 + + ; FABD: D0 09 = BNE $FAC8 + .byte $D0, $09 + + +; $FABF +Pulse2ResetStateFABF: + lda #$30 ; FABF: A9 30 + sta $F3 ; FAC1: 85 F3 + + +; $FAC3 +Pulse2LoadDelayFAC3: + ldy $F3 ; FAC3: A4 F3 + lda $F9F4,y ; FAC5: B9 F4 F9 + +; $FAC8 +Pulse2StoreDelayFAC8: + sta $07F8 ; FAC8: 8D F8 07 + + lda #$00 ; FACB: A9 00 + sta $F7 ; FACD: 85 F7 + + +; $FACF +Pulse2UpdatePitchFACF: + lda $07FF ; FACF: AD FF 07 + clc ; FAD2: 18 + adc #$04 ; FAD3: 69 04 + + cmp #$40 ; FAD5: C9 40 + bcs @skip_store_07ff ; FAD7: B0 03 + + sta $07FF ; FAD9: 8D FF 07 + +@skip_store_07ff: + ldy $F3 ; FADC: A4 F3 + lda $F9C4,y ; FADE: B9 C4 F9 + + ldy $F7 ; FAE1: A4 F7 + clc ; FAE3: 18 + adc AudioEffectDataF9B3,y ; FAE4: 79 B3 F9 + + ldx $FC ; FAE7: A6 FC + bpl @positive_fc ; FAE9: 10 07 + + ldx $F9BF,y ; FAEB: BE BF F9 + ldy #$89 ; FAEE: A0 89 + + ; FAF0: D0 05 = BNE $FAF7 + .byte $D0, $05 + +@positive_fc: + ldx $F9B9,y ; FAF2: BE B9 F9 + ldy #$7F ; FAF5: A0 7F + + +; --------------------------------------------------------------------------- +; $FAF7-$FB16 +; Write Pulse 2 APU state and prepare a derived 16-bit value in $00:$01. +; +; Entry: +; X -> value written to APUPC2BASE +; Y -> value written to APUPC2SWEEP +; A -> value stored in $07F9 +; --------------------------------------------------------------------------- + +WritePulse2State: + stx APUPC2BASE ; FAF7: 8E 04 40 + sty APUPC2SWEEP ; FAFA: 8C 05 40 + sta $07F9 ; FAFD: 8D F9 07 + + ldy #$07 ; FB00: A0 07 + sty $00 ; FB02: 84 00 + + ldy #$FF ; FB04: A0 FF + sty $01 ; FB06: 84 01 + + lsr a ; FB08: 4A + lsr a ; FB09: 4A + lsr a ; FB0A: 4A + lsr a ; FB0B: 4A + lsr a ; FB0C: 4A + tay ; FB0D: A8 + + beq UpdatePulsePitch ; FB0E: F0 07 + +@scale: + lsr $00 ; FB10: 46 00 + ror $01 ; FB12: 66 01 + + dey ; FB14: 88 + bne @scale ; FB15: D0 F9 + + +; --------------------------------------------------------------------------- +; $FB17-$FB76 +; Update Pulse 2 pitch and related triangle-channel state. +; +; $00:$01 contains a derived 16-bit period/pitch value. +; $07F9 controls repeated scaling/adjustment. +; --------------------------------------------------------------------------- + +UpdatePulsePitch: + lda $00 ; FB17: A5 00 + lsr a ; FB19: 4A + tax ; FB1A: AA + + lda $01 ; FB1B: A5 01 + ror a ; FB1D: 6A + tay ; FB1E: A8 + + asl $07F9 ; FB1F: 0E F9 07 + asl $07F9 ; FB22: 0E F9 07 + asl $07F9 ; FB25: 0E F9 07 + +@adjust: + txa ; FB28: 8A + lsr a ; FB29: 4A + tax ; FB2A: AA + + tya ; FB2B: 98 + ror a ; FB2C: 6A + tay ; FB2D: A8 + + asl $07F9 ; FB2E: 0E F9 07 + bcc @check_more ; FB31: 90 0E + + sec ; FB33: 38 + eor #$FF ; FB34: 49 FF + adc $01 ; FB36: 65 01 + sta $01 ; FB38: 85 01 + + txa ; FB3A: 8A + eor #$FF ; FB3B: 49 FF + adc $00 ; FB3D: 65 00 + sta $00 ; FB3F: 85 00 + +@check_more: + lda $07F9 ; FB41: AD F9 07 + bne @adjust ; FB44: D0 E2 + + lda $01 ; FB46: A5 01 + sta APUPC2LOW ; FB48: 8D 06 40 + + lda $00 ; FB4B: A5 00 + sta APUPC2HIGH ; FB4D: 8D 07 40 + + lda $F1 ; FB50: A5 F1 + bne UpdateTriangleState ; FB52: D0 06 + + lda $F7 ; FB54: A5 F7 + ora $FA ; FB56: 05 FA + bne UpdatePulsePitchReturnFB76 + ; FB58: D0 1C + + +; --------------------------------------------------------------------------- +; $FB5A +; Derive and write the related triangle-channel period. +; --------------------------------------------------------------------------- + +UpdateTriangleState: + lsr $00 ; FB5A: 46 00 + ror $01 ; FB5C: 66 01 + + lda $00 ; FB5E: A5 00 + lsr a ; FB60: 4A + tay ; FB61: A8 + + lda $01 ; FB62: A5 01 + ror a ; FB64: 6A + + clc ; FB65: 18 + adc $01 ; FB66: 65 01 + sta APUTRILOW ; FB68: 8D 0A 40 + + tya ; FB6B: 98 + adc $00 ; FB6C: 65 00 + sta APUTRIHIGH ; FB6E: 8D 0B 40 + + lda #$04 ; FB71: A9 04 + sta APUTRIBASE ; FB73: 8D 08 40 + +UpdatePulsePitchReturnFB76: + rts ; FB76: 60 + + +; --------------------------------------------------------------------------- +; $FB77-$FBFB +; Noise/audio effect lookup tables. +; --------------------------------------------------------------------------- + +NoiseAudioDataFB77: + +; $FB77 + .byte $10, $11, $12, $12, $12, $13, $13, $14 + .byte $15, $16, $17, $18, $19, $1A, $1B, $1C + +; $FB87 + .byte $1D, $1E, $1F, $1C, $1F, $1F, $1F, $1F + .byte $1F, $18, $1F, $1F, $1F, $1F, $18, $1C + +; $FB97 + .byte $1F, $1F, $18, $1F, $1F, $1F, $14, $1F + .byte $1C, $1F, $1F, $1C, $18, $16, $0E, $0C + +; $FBA7 + .byte $0E, $0E, $0A, $0C, $0E, $0E, $0C, $0A + .byte $0E, $0D, $0E, $0E, $0E, $0E, $0C, $0E + +; $FBB7 + .byte $0A, $0E, $0C, $0C, $0E, $0D, $0C, $0D + .byte $0D, $0E, $0E, $0D, $0D, $0E, $0B, $0E + +; $FBC7 + .byte $0E, $0D, $0E, $0E, $0C, $0D, $0E, $0B + .byte $0D, $0E, $0E, $0D, $30, $31, $32, $32 + +; $FBD7 + .byte $33, $33, $34, $35, $36, $37, $38, $37 + .byte $38, $37, $36, $35, $34, $34, $34, $35 + +; $FBE7 + .byte $36, $37, $38, $39, $3A, $3B, $3C, $3A + .byte $38, $36, $35, $34, $10, $11, $12, $14 + +; $FBF7-$FBFE + .byte $16, $18, $1C, $1F, $1C + .byte $18, $16, $14 + +; --------------------------------------------------------------------------- +; $FBFF-$FC20 +; Initialize/update Noise-channel effect state. +; +; Entry: +; Y = noise effect/state selector +; +; Uses lookup tables at $FB76 and $FBA4 to configure the noise channel. +; --------------------------------------------------------------------------- + +SetupNoiseEffectLong: + sty $F2 ; FBFF: 84 F2 + + lda #$2E ; FC01: A9 2E + sta $07F6 ; FC03: 8D F6 07 + + tya ; FC06: 98 + beq @load_noise ; FC07: F0 05 + + and #$02 ; FC09: 29 02 + sta $07F7 ; FC0B: 8D F7 07 + + +@load_noise: + ldy $07F6 ; FC0E: AC F6 07 + + lda $FB76,y ; FC11: B9 76 FB + sta APUNOISEBASE ; FC14: 8D 0C 40 + + lda $FBA4,y ; FC17: B9 A4 FB + sta APUNOISELOOP ; FC1A: 8D 0E 40 + + ; FC1D: D0 56 = BNE $FC75 + .byte $D0, $56 + +; --------------------------------------------------------------------------- +; $FC1F-$FC3C +; Initialize/reset Noise-channel effect state. +; +; Y supplies the new noise-effect state. +; --------------------------------------------------------------------------- + +SetupNoiseEffectShort: + sty $F2 ; FC1F: 84 F2 + + lda #$00 ; FC21: A9 00 + sta $07F6 ; FC23: 8D F6 07 + sta $07F7 ; FC26: 8D F7 07 + + lda #$0E ; FC29: A9 0E + sta APUNOISELOOP ; FC2B: 8D 0E 40 + + lda $07F6 ; FC2E: AD F6 07 + lsr a ; FC31: 4A + lsr a ; FC32: 4A + lsr a ; FC33: 4A + tay ; FC34: A8 + + lda $FBD3,y ; FC35: B9 D3 FB + sta APUNOISEBASE ; FC38: 8D 0C 40 + + ; FC3B: D0 38 = BNE $FC75 + .byte $D0, $38 + +; --------------------------------------------------------------------------- +; $FC3D-$FC88 +; Noise-channel event dispatcher and active-effect update. +; +; $FD = pending noise request bits, destructively shifted +; $F2 = active noise effect/state +; --------------------------------------------------------------------------- + +UpdateNoiseChannel: + ldy $FD ; FC3D: A4 FD + lda $F2 ; FC3F: A5 F2 + + lsr $FD ; FC41: 46 FD + + ; FC43: B0 BA = BCS $FBFF + .byte $B0, $BA + + lsr a ; FC45: 4A + + ; FC46: B0 BE = BCS $FC06 + .byte $B0, $BE + + lsr $FD ; FC48: 46 FD + + ; FC4A: B0 D3 = BCS $FC1F + .byte $B0, $D3 + + lsr a ; FC4C: 4A + + ; FC4D: B0 DF = BCS $FC2E + .byte $B0, $DF + + lsr a ; FC4F: 4A + + ; FC50: B0 11 = BCS $FC63 + .byte $B0, $11 + + lsr $FD ; FC52: 46 FD + + ; FC54: B0 01 = BCS $FC57 + .byte $B0, $01 + + rts ; FC56: 60 + + +; --------------------------------------------------------------------------- +; $FC57 +; Start/configure another Noise effect. +; --------------------------------------------------------------------------- + +NoiseEffectFC57: + sty $F2 ; FC57: 84 F2 + + lda #$0C ; FC59: A9 0C + sta $07F6 ; FC5B: 8D F6 07 + + lda #$0D ; FC5E: A9 0D + sta APUNOISELOOP ; FC60: 8D 0E 40 + + +; --------------------------------------------------------------------------- +; $FC63 +; Active Noise effect update. +; --------------------------------------------------------------------------- + +NoiseEffectUpdateFC63: + ldy $07F6 ; FC63: AC F6 07 + + lda $FBF2,y ; FC66: B9 F2 FB + sta APUNOISEBASE ; FC69: 8D 0C 40 + + cpy #$08 ; FC6C: C0 08 + bne @trigger_noise ; FC6E: D0 05 + + lda #$0E ; FC70: A9 0E + sta APUNOISELOOP ; FC72: 8D 0E 40 + + +@trigger_noise: + lda #$08 ; FC75: A9 08 + sta APUNOISELENGTH ; FC77: 8D 0F 40 + + dec $07F6 ; FC7A: CE F6 07 + + ; FC7D: D0 D7 = BNE $FC56 + .byte $D0, $D7 + + ldy $07F7 ; FC7F: AC F7 07 + + ; FC82: D0 9B = BNE $FC1F + .byte $D0, $9B + + lda #$00 ; FC84: A9 00 + sta $F2 ; FC86: 85 F2 + + rts ; FC88: 60 + + +; --------------------------------------------------------------------------- +; $FC89-$FC8C +; Small audio lookup table. +; --------------------------------------------------------------------------- + +AudioControlTableFC89: + .byte $7F, $8E, $86, $8F + + +; --------------------------------------------------------------------------- +; $FC8D-$FCD8 +; Initialize/start the multi-channel sound-effect sequencer. +; +; Called directly from UpdateAudio. +; $FB = pending effect request +; $F4 = active effect state +; --------------------------------------------------------------------------- + +UpdateSoundEffect: + lda $FB ; FC8D: A5 FB + bne @start_effect ; FC8F: D0 05 + + lda $F4 ; FC91: A5 F4 + + ; FC93: D0 44 = BNE $FCD9 + .byte $D0, $44 + + rts ; FC95: 60 + +@start_effect: + sta $F4 ; FC96: 85 F4 + + ldy #$00 ; FC98: A0 00 + +@find_effect: + iny ; FC9A: C8 + lsr a ; FC9B: 4A + bcc @find_effect ; FC9C: 90 FC + + lda $FDD2,y ; FC9E: B9 D2 FD + tay ; FCA1: A8 + + lda SoundEffectDescriptorDataFDD3,y + ; FCA2: B9 D3 FD + sta $07F0 ; FCA5: 8D F0 07 + + lda SoundEffectDescriptorDataFDD3+1,y + ; FCA8: B9 D4 FD + sta $F8 ; FCAB: 85 F8 + + lda SoundEffectDescriptorDataFDD3+2,y + ; FCAD: B9 D5 FD + sta $F9 ; FCB0: 85 F9 + + lda SoundEffectDescriptorDataFDD3+3,y + ; FCB2: B9 D6 FD + sta $07E0 ; FCB5: 8D E0 07 + + lda SoundEffectDescriptorDataFDD3+4,y + ; FCB8: B9 D7 FD + sta $FA ; FCBB: 85 FA + + lda SoundEffectDescriptorDataFDD3+5,y + ; FCBD: B9 D8 FD + sta $07EC ; FCC0: 8D EC 07 + + lda #$01 ; FCC3: A9 01 + + sta $07E5 ; FCC5: 8D E5 07 + sta $07E1 ; FCC8: 8D E1 07 + sta $07E9 ; FCCB: 8D E9 07 + sta $07ED ; FCCE: 8D ED 07 + + ldy #$00 ; FCD1: A0 00 + sty $07E4 ; FCD3: 8C E4 07 + + inc $07FB ; FCD6: EE FB 07 + + +; --------------------------------------------------------------------------- +; $FCD9-$FD1B +; Update the first channel/voice of the active sound-effect sequence. +; +; Uses the stream pointer in $F8:$F9 and counters at $07E0/$07E1. +; --------------------------------------------------------------------------- + +UpdateSoundEffectVoice0FCD9: + ldy $07E0 ; FCD9: AC E0 07 + + ; FCDC: F0 3C = BEQ $FD1A + .byte $F0, $3C + + dec $07E1 ; FCDE: CE E1 07 + + ; FCE1: D0 37 = BNE $FD1A + .byte $D0, $37 + + inc $07E0 ; FCE3: EE E0 07 + + lda ($F8),y ; FCE6: B1 F8 + jsr DecodeAudioEvent ; FCE8: 20 53 FF + sta $07E1 ; FCEB: 8D E1 07 + + txa ; FCEE: 8A + and #$3E ; FCEF: 29 3E + jsr $F871 ; FCF1: 20 71 F8 + + bne @select_control ; FCF4: D0 04 + + ldx #$10 ; FCF6: A2 10 + + ; FCF8: D0 0A = BNE $FD04 + .byte $D0, $0A + +@select_control: + ldx #$4F ; FCFA: A2 4F + + lda $F4 ; FCFC: A5 F4 + and #$60 ; FCFE: 29 60 + bne @store_control ; FD00: D0 02 + + ldx #$05 ; FD02: A2 05 + +@store_control: + stx APUPC1BASE ; FD04: 8E 00 40 + + ldy #$7F ; FD07: A0 7F + + lda $F4 ; FD09: A5 F4 + lsr a ; FD0B: 4A + bcc @store_sweep ; FD0C: 90 09 + + lda $07FB ; FD0E: AD FB 07 + and #$03 ; FD11: 29 03 + tax ; FD13: AA + + ldy AudioControlTableFC89,x ; FD14: BC 89 FC + +@store_sweep: + sty APUPC1SWEEP ; FD17: 8C 01 40 + + dec $07E5 ; FD1A: CE E5 07 + +; --------------------------------------------------------------------------- +; $FD1D-$FD62 +; Update the second Pulse-channel portion of the active sound effect. +; --------------------------------------------------------------------------- + +UpdateSoundEffectVoice1FD1D: + ; FD1D: D0 44 = BNE $FD63 + .byte $D0, $44 + + ldy $07E4 ; FD1F: AC E4 07 + inc $07E4 ; FD22: EE E4 07 + + lda ($F8),y ; FD25: B1 F8 + bne @decode_event ; FD27: D0 09 + + lda #$00 ; FD29: A9 00 + sta $FA ; FD2B: 85 FA + sta $F4 ; FD2D: 85 F4 + + jmp $F942 ; FD2F: 4C 42 F9 + + +@decode_event: + jsr DecodeAudioEvent ; FD32: 20 53 FF + sta $07E5 ; FD35: 8D E5 07 + + lda $F4 ; FD38: A5 F4 + and #$60 ; FD3A: 29 60 + + ; FD3C: D0 25 = BNE $FD63 + .byte $D0, $25 + + txa ; FD3E: 8A + and #$3E ; FD3F: 29 3E + + jsr ConfigureApuChannel1F885 + ; FD41: 20 85 F8 + + bne @use_87 ; FD44: D0 04 + + ldx #$10 ; FD46: A2 10 + + ; FD48: D0 11 = BNE $FD5B + .byte $D0, $11 + + +@use_87: + ldx #$87 ; FD4A: A2 87 + + lda $07E5 ; FD4C: AD E5 07 + cmp #$10 ; FD4F: C9 10 + bcs @write_pulse2 ; FD51: B0 08 + + ldx #$84 ; FD53: A2 84 + + cmp #$08 ; FD55: C9 08 + bcs @write_pulse2 ; FD57: B0 02 + + ldx #$82 ; FD59: A2 82 + + +@write_pulse2: + stx APUPC2BASE ; FD5B: 8E 04 40 + + lda #$7F ; FD5E: A9 7F + sta APUPC2SWEEP ; FD60: 8D 05 40 + + +; --------------------------------------------------------------------------- +; $FD63-$FD98 +; Update triangle-channel portion of the active sound effect. +; --------------------------------------------------------------------------- + +UpdateSoundEffectTriangleFD63: + ldy $FA ; FD63: A4 FA + + ; FD65: F0 32 = BEQ $FD99 + .byte $F0, $32 + + dec $07E9 ; FD67: CE E9 07 + + ; FD6A: D0 2D = BNE $FD99 + .byte $D0, $2D + + inc $FA ; FD6C: E6 FA + + lda ($F8),y ; FD6E: B1 F8 + jsr DecodeAudioEvent ; FD70: 20 53 FF + sta $07E9 ; FD73: 8D E9 07 + + clc ; FD76: 18 + adc #$FF ; FD77: 69 FF + + cmp #$0C ; FD79: C9 0C + bcc @limit_ok ; FD7B: 90 02 + + lda #$0C ; FD7D: A9 0C + + +@limit_ok: + asl a ; FD7F: 0A + asl a ; FD80: 0A + + ldy $F4 ; FD81: A4 F4 + cpy #$20 ; FD83: C0 20 + bne @write_triangle ; FD85: D0 02 + + lda #$81 ; FD87: A9 81 + + +@write_triangle: + sta APUTRIBASE ; FD89: 8D 08 40 + + txa ; FD8C: 8A + and #$3E ; FD8D: 29 3E + + jsr ConfigureApuChannel2F889 + ; FD8F: 20 89 F8 + + ; FD92: D0 05 = BNE $FD99 + .byte $D0, $05 + + lda #$00 ; FD94: A9 00 + sta APUTRIBASE ; FD96: 8D 08 40 + + +; --------------------------------------------------------------------------- +; $FD99-$FDD2 +; Update Noise-channel portion of the active sound effect. +; --------------------------------------------------------------------------- + +UpdateSoundEffectNoiseFD99: + lda $F4 ; FD99: A5 F4 + and #$1B ; FD9B: 29 1B + + ; FD9D: F0 33 = BEQ $FDD2 + beq SoundEffectReturnFDD2 + + dec $07ED ; FD9F: CE ED 07 + + ; FDA2: D0 2E = BNE $FDD2 + bne SoundEffectReturnFDD2 + + ldy $07EC ; FDA4: AC EC 07 + inc $07EC ; FDA7: EE EC 07 + + lda ($F8),y ; FDAA: B1 F8 + jsr DecodeAudioEvent ; FDAC: 20 53 FF + sta $07ED ; FDAF: 8D ED 07 + + txa ; FDB2: 8A + and #$3E ; FDB3: 29 3E + + ; FDB5: F0 1B = BEQ $FDD2 + beq SoundEffectReturnFDD2 + + cmp #$20 ; FDB7: C9 20 + beq @noise_alt ; FDB9: F0 08 + + lda #$00 ; FDBB: A9 00 + ldx #$00 ; FDBD: A2 00 + ldy #$08 ; FDBF: A0 08 + + ; FDC1: D0 06 = BNE $FDC9 + bne @write_noise + + +@noise_alt: + lda #$00 ; FDC3: A9 00 + ldx #$02 ; FDC5: A2 02 + ldy #$08 ; FDC7: A0 08 + + +@write_noise: + sta APUNOISEBASE ; FDC9: 8D 0C 40 + stx APUNOISELOOP ; FDCC: 8E 0E 40 + sty APUNOISELENGTH ; FDCF: 8C 0F 40 + + +SoundEffectReturnFDD2: + rts ; FDD2: 60 + + +; --------------------------------------------------------------------------- +; $FDD3-$FDFB +; Sound-effect descriptor / sequence data used by UpdateSoundEffect. +; +; $FC8D uses an index derived from the requested effect, then reads: +; $FDD2,Y +; $FDD3,Y -> $07F0 +; $FDD4,Y -> $F8 +; $FDD5,Y -> $F9 +; $FDD6,Y -> $07E0 +; $FDD7,Y -> $FA +; $FDD8,Y -> $07EC +; +; Note that $FDD2 is the preceding RTS byte ($60), so the original ROM +; again reuses an instruction byte as part of an indexed data structure. +; --------------------------------------------------------------------------- + +SoundEffectDescriptorDataFDD3: +; $FDD3 + .byte $08, $0E, $14, $0E + .byte $0E, $19, $1E, $23 + .byte $00, $64, $FF, $2D + .byte $59, $74, $0F, $FB + +; $FDE3 + .byte $FD, $31, $56, $7B + .byte $08, $87, $FE, $27 + .byte $41, $0F, $F3, $FE + .byte $03, $00, $08, $FD + +; $FDF3-$FDFB + .byte $FE, $02, $00, $08 + .byte $F0, $FF, $02, $00 + .byte $02 + + +; --------------------------------------------------------------------------- +; $FDFC-$FE2B +; Audio/music sequence data. +; +; Sequence consists primarily of two-byte event/value pairs and ends +; with $00 at $FE2B. +; --------------------------------------------------------------------------- + +AudioSequenceFDFC: + .byte $20, $26 + .byte $02, $4A + .byte $02, $06 + + .byte $02, $20 + .byte $26, $02 + .byte $4A, $02 + .byte $06, $02 + + .byte $20, $26 + .byte $02, $2E + .byte $02, $2E + .byte $02, $2E + + .byte $46, $AE + .byte $02, $42 + .byte $6E, $6A + + .byte $22, $1C + .byte $2A, $22 + .byte $1C, $2A + .byte $6E, $22 + .byte $1C, $2E + .byte $22, $1C + .byte $2E, $1C + + .byte $60, $62 + .byte $64, $26 + .byte $86 + + .byte $00 + + +; --------------------------------------------------------------------------- +; $FE2C-$FEAD +; Audio/music sequence data. +; --------------------------------------------------------------------------- + +AudioSequenceFE2C: + +; $FE2C + .byte $82, $60, $58, $42, $58, $60, $58, $42 + .byte $58, $60, $5C, $60, $5C, $60, $5C, $60 + +; $FE3C + .byte $5C, $62, $18, $36, $22, $18, $36, $22 + .byte $66, $3A, $3A, $26, $3A, $3A, $26, $36 + +; $FE4C + .byte $7A, $58, $5C, $22, $A6, $82, $58, $66 + .byte $58, $66, $58, $66, $58, $66, $60, $68 + +; $FE5C + .byte $60, $68, $60, $68, $60, $68, $46, $2A + .byte $22, $06, $2A, $22, $06, $50, $26, $26 + +; $FE6C + .byte $10, $10, $26, $26, $22, $66, $6A, $6A + .byte $0A, $8E, $41, $A0, $A0, $A0, $A0, $A0 + +; $FE7C + .byte $A0, $A0, $60, $E0, $E0, $20, $60, $60 + .byte $60, $20, $A0, $22, $26, $22, $26, $22 + +; $FE8C + .byte $26, $26, $2A, $26, $2A, $26, $2A, $2A + .byte $2C, $2A, $2C, $2A, $2C, $46, $6E, $66 + +; $FE9C + .byte $6B, $6F, $46, $4A, $4E, $47, $6E, $46 + .byte $4A, $6D, $67, $62, $62, $62, $67, $67 + +; $FEAC-$FEAD + .byte $6B, $00 + + +; --------------------------------------------------------------------------- +; $FEAE-$FEF5 +; Audio/music sequence data. +; --------------------------------------------------------------------------- + +AudioSequenceFEAE: + +; $FEAE + .byte $6D, $47, $4B, $50, $4E, $46, $4B, $55 + .byte $50, $54, $56, $51, $4E, $50, $54, $4B + +; $FEBE + .byte $4F, $34, $18, $34, $18, $34, $18, $59 + .byte $59, $5D, $66, $6A, $6C, $66, $6A, $6C + +; $FECE + .byte $66, $6A, $6C, $66, $6A, $6C, $66, $6A + .byte $6C, $66, $6A, $6C, $66, $6A, $6C, $66 + +; $FEDE + .byte $6A, $6C, $66, $6A, $6C, $66, $6A, $6C + .byte $67, $6C, $6C, $6C, $66, $66, $66, $66 + +; $FEEE-$FEF5 + .byte $66, $66, $6A, $6A, $6A, $03, $03, $00 + + +; --------------------------------------------------------------------------- +; $FEF6-$FEFE +; Short audio sequence. +; --------------------------------------------------------------------------- + +AudioSequenceFEF6: + .byte $76, $82 + .byte $76, $82 + .byte $76, $82 + .byte $2B, $42 + .byte $00 + + +; --------------------------------------------------------------------------- +; $FEFF +; Preceding byte, not part of the aligned period table. +; --------------------------------------------------------------------------- + + .byte $44 + + +; --------------------------------------------------------------------------- +; $FF00-$FF3D +; APU timer-period lookup table. +; +; Callers normally supply an even index: +; Y = $00, $02, $04, ... $3E +; +; SetApuPeriodF873 reads: +; $FF00,Y +; $FF01,Y +; +; as adjacent bytes of the selected period record. +; --------------------------------------------------------------------------- + +ApuPeriodTableFF00: + .byte $06, $AE ; index $00 + .byte $00, $00 ; index $02 + .byte $00, $69 ; index $04 + .byte $00, $D4 ; index $06 + .byte $00, $C8 ; index $08 + .byte $00, $BD ; index $0A + .byte $00, $B2 ; index $0C + .byte $00, $A8 ; index $0E + + .byte $00, $9F ; index $10 + .byte $00, $96 ; index $12 + .byte $00, $8D ; index $14 + .byte $00, $7E ; index $16 + .byte $01, $AB ; index $18 + .byte $01, $93 ; index $1A + .byte $01, $7C ; index $1C + .byte $01, $67 ; index $1E + + .byte $01, $52 ; index $20 + .byte $01, $3F ; index $22 + .byte $01, $2D ; index $24 + .byte $01, $1C ; index $26 + .byte $01, $0C ; index $28 + .byte $00, $FD ; index $2A + .byte $00, $EE ; index $2C + .byte $00, $E1 ; index $2E + + .byte $03, $57 ; index $30 + .byte $02, $3A ; index $32 + .byte $02, $1A ; index $34 + .byte $01, $FC ; index $36 + .byte $01, $DF ; index $38 + .byte $01, $C4 ; index $3A + .byte $07, $FA ; index $3C + + +; --------------------------------------------------------------------------- +; $FF3E-$FF4D +; Audio bit/mask lookup table. +; --------------------------------------------------------------------------- + +AudioMaskTable: + .byte $04, $08, $10, $20 + .byte $05, $18, $0A, $06 + .byte $05, $0A, $14, $28 + .byte $50, $1E, $3C, $07 + +; --------------------------------------------------------------------------- +; $FF4E-$FF52 +; Additional audio lookup values immediately preceding the decoder +; entry at $FF53. +; --------------------------------------------------------------------------- + +AudioLookupTableFF4E: + .byte $0E, $1C, $38, $70, $2A + + +; --------------------------------------------------------------------------- +; $FF53-$FF63 +; Decode an audio sequence byte into a duration/value from AudioMaskTable. +; +; Entry: +; A = encoded audio event byte +; +; Exit: +; X = transformed event value +; Y = table index +; A = AudioMaskTable[Y] +; --------------------------------------------------------------------------- + +DecodeAudioEvent: + tax ; FF53: AA + + ror a ; FF54: 6A + + txa ; FF55: 8A + rol a ; FF56: 2A + rol a ; FF57: 2A + rol a ; FF58: 2A + + and #$07 ; FF59: 29 07 + + clc ; FF5B: 18 + adc $07F0 ; FF5C: 6D F0 07 + tay ; FF5F: A8 + + lda AudioMaskTable,y ; FF60: B9 3E FF + + rts ; FF63: 60 + + +; --------------------------------------------------------------------------- +; $FF64-$FFE9 +; Audio sequence / instrument / effect data. +; --------------------------------------------------------------------------- + +AudioDataFF64: + +; $FF64 + .byte $82, $60, $42, $66, $42, $46, $42, $60 + .byte $66, $42, $47, $C2, $68, $42, $6E, $42 + +; $FF74 + .byte $4E, $42, $68, $6E, $42, $4F, $C2, $0B + .byte $03, $C7, $0B, $03, $C7, $2B, $03, $E7 + +; $FF84 + .byte $A2, $23, $03, $E1, $5E, $5D, $58, $A0 + .byte $A2, $A4, $66, $C6, $00, $82, $58, $42 + +; $FF94 + .byte $60, $42, $66, $42, $58, $60, $42, $67 + .byte $C2, $60, $42, $68, $42, $6E, $42, $60 + +; $FFA4 + .byte $68, $42, $6F, $C2, $2B, $03, $EB, $2B + .byte $03, $EB, $23, $03, $E3, $9C, $1D, $03 + +; $FFB4 + .byte $D9, $78, $7B, $72, $98, $98, $9C, $62 + .byte $E0, $98, $A6, $98, $A6, $98, $A6, $98 + +; $FFC4 + .byte $A6, $A0, $AE, $A0, $AE, $A0, $AE, $A0 + .byte $AE, $9C, $EA, $9C, $E6, $E2, $60, $A6 + +; $FFD4 + .byte $AA, $AA, $4A, $E6, $80, $E0, $E0, $E0 + .byte $E0, $E0, $E0, $E0, $A0, $A0, $C0, $A0 + +; $FFE4 + .byte $E0, $50, $E0, $A0, $A0, $A0 + + +; --------------------------------------------------------------------------- +; $FFEA-$FFF9 +; Final audio/data bytes preceding the interrupt-vector table. +; --------------------------------------------------------------------------- + +AudioDataFFEA: + .byte $60, $10, $10, $10, $10, $A0, $83, $00 + .byte $04, $14, $04, $14, $04, $14, $04, $D4 + +; --------------------------------------------------------------------------- +; $FFFA-$FFFF +; 6502 interrupt vectors. +; --------------------------------------------------------------------------- + +InterruptVectorsFFFA: + .word NMI ; FFFA-FFFB: NMI + .word $C184 ; FFFC-FFFD: RESET + .word $FFF0 ; FFFE-FFFF: IRQ/BRK \ No newline at end of file diff --git a/_wip/Balloon Fight (JU)/copilot/symbols.inc b/_wip/Balloon Fight (JU)/copilot/symbols.inc new file mode 100644 index 0000000..7e118f5 --- /dev/null +++ b/_wip/Balloon Fight (JU)/copilot/symbols.inc @@ -0,0 +1,85 @@ +; PPU registers +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +OAMADDR = $2003 +OAMDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +OAMDMA = $4014 +JOY1 = $4016 + +; Variables +PpuControlShadow = $10 +PpuMaskShadow = $11 +Controller1Held = $14 +Controller1Pressed = $15 +Controller2Held = $16 +Controller2Pressed = $17 +ControllerState = Controller1Held +NmiDispatchMode = $40 +NmiState = $41 +MenuSelectionIndex = $42 +NmiSequenceStep = $44 +NmiSyncState = $48 +InputActionLatch = $49 +OamBuffer = $0200 +PersistentRecord0 = $0580 +PersistentRecord1 = $05A3 +ResetMagic1 = $05CE +ResetMagic2 = $05CF +PersistentBuffer = $06E0 +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 +MENU_HORIZONTAL_COUNT=$05 +MENU_VERTICAL_COUNT=$06 +MENU_ACTION_NONE=$00 +MENU_ACTION_ACCEPTED=$04 +NMI_SYNC_PENDING=$FF +NMI_SYNC_COMPLETE=$01 +RESET_MAGIC_1=$A5 +RESET_MAGIC_2=$5A +PPUCTRL_NMI_ENABLE=$80 + +WorkingBuffer = $05E0 +PERSISTENT_BUFFER_SIZE = $0100 + +StateTargetCA85 = $CA85 + +; Serial I/O helpers using $4016 (verified structure; peripheral identity unknown) +WriteSerialTiming = $C62D +WaitSerialPulse = $C639 +TransmitPulseShort = $C64E +TransmitPulseLong = $C653 +TransmitByte = $C66E +IncrementSerialCount = $C68A +ReceiveSerialBit = $C691 +ReceiveByte = $C6A5 + +; Block protocol helpers +TransmitRuntimeData = $C6C8 +TransmitDataBlock = $C6D1 +SetupTransmitRamBlock = $C716 +SetupTransmitWorkingBuffer = $C726 +SetTransmitPreambleCounts = $C734 +ReceiveRuntimeData = $C739 +ReceiveDataBlock = $C792 +SerialBlockLength = $0D +SerialBlockPtr = $0E +SerialBitCountLo = $08 +SerialBitCountHi = $09 + +; App Specific Variables +TMP00 = $00 +TMP01 = $01