;license:MIT ;(c) 2018-9 by 4am ; ; Search Mode - main UI ; ; Public functions ; - SearchMode ; ;------------------------------------------------------------------------------ ; SearchMode ; main entry point for Search Mode, which allows the user to search the game ; catalog and launch games ; ; in: none ; out: never returns to caller (may JMP to other major modes) ;------------------------------------------------------------------------------ SearchMode ldx #$FF txs stx SelectedIndex ; no game selected inx stx OffscreenPage jsr Home ; clear screen jsr OnInputChanged ; draw UI on HGR page 1 bit $C052 bit $C057 bit $C050 ; show HGR page 1 bit $C010 jsr _ResetInputTimeout _SearchModeInputLoop lda $C000 bmi @gotKey inc $4F ; these are only ever incremented, never bne + ; reset (may be used as a pseudorandom inc $4E ; seed) + dec Timeout ; these are a 3-byte timeout counter bne _SearchModeInputLoop ; that counts down from a number set dec Timeout+1 ; in _ResetInputTimeout and reset bne _SearchModeInputLoop ; on every keypress (whether or not dec Timeout+2 ; the key leads to an action) bne _SearchModeInputLoop jsr _CoverFade ; no input for ~30 seconds, switch to jmp MegaAttractMode ; mega-attract mode @gotKey bit $C010 jsr _ResetInputTimeout cmp #$9B ; Esc clears the input buffer (if any) bne + ; or switches to attract mode ldx #kInputClear bne InputDispatch ; always branches + cmp #$8D ; ENTER launches the current game (if any) bne + ldx #kInputLaunch bne InputDispatch ; always branches + cmp #$FF ; delete key bne + - ldx #kInputBack bne InputDispatch ; always branches + cmp #$88 ; left arrow = delete beq - and #$7F ; strip high bit for search characters cmp #$30 ; control keys and punctuation ignored bcc @badkey cmp #$3A ; numbers are good input bcc @goodkey cmp #$41 ; more punctuation (also ignored) bcc @badkey cmp #$5B ; uppercase letters are good input bcs + ora #$20 ; convert uppercase letters to lowercase bne @goodkey ; always branches + cmp #$61 ; more punctuation (also ignored) bcc @badkey cmp #$7B ; lowercase letters are good input bcc @goodkey @badkey jsr SoftBell ; beep on invalid input jmp _SearchModeInputLoop ; and start over @goodkey ldx #kInputSearch ; execution falls through here InputDispatch pha ; save key pressed txa asl tax lda InputDispatchTable,x sta @j+1 lda InputDispatchTable+1,x sta @j+2 pla ; restore key pressed @j jsr $FDFD ; SMC bcc _SearchModeInputLoop jmp SearchMode ; if carry is set, force full redraw OnClear ldx InputLength bne + jsr _CoverFade ; Esc with no input switches to jmp MegaAttractMode ; mega-attract mode + ldx #0 ; Esc with input clears the input stx InputLength jmp OnInputChanged OnBack ldx InputLength bne + jsr SoftBell rts + dec InputLength jmp OnInputChanged OnLaunch ldx SelectedIndex cpx #$FF beq + jsr PlayGameFromSearch sec ; tell caller to redraw UI rts + clc rts OnSearch ldx InputLength cpx #MaxInputLength bne + jsr SoftBell rts + sta InputBuffer,x inc InputLength ; execution falls through here OnInputChanged lda InputLength bne @findMatchingTitle ; no input, reset params and UI lda #$FF sta SelectedIndex ; no game selected ldx #40 ; reset visible line lda #0 - sta UILine1-1,x dex bne - ldy #MaxInputLength ; clear visible search bar lda #" " - sta UILine2+1,y dey bne - lda #$7F sta UILine2+1 jsr _LoadCoverOffscreen jsr _DrawSearchBarOffscreen jsr _ShowOtherPage clc rts @findMatchingTitle jsr ResetTextRank jsr okvs_iter_values ; iterate through all game titles !word gGamesListStore ; and rank them for the best match !word TextRankCallback ; to the current input buffer lda MatchCount ; any matches at all? bne + jsr SoftBell ; no matches for this input buffer, beep dec InputLength ; and ignore the last key typed rts + lda BestMatchIndex ; check if the new best match is the same cmp SelectedIndex ; as the current best match php ; (we'll use this later to skip reloading) sta SelectedIndex sta @index jsr okvs_nth !word gGamesListStore @index !byte $FD +STAY @key plp bne + lda OffscreenPage eor #$01 sta OffscreenPage jmp @skipload + lda OffscreenPage ; we have a new best match, so load the beq + ; new title screenshot (offscreen) lda #$40 +HIDE_NEXT_2_BYTES + lda #$20 sta @titleaddress+1 +LDADDR kHGRTitleDirectory jsr SetPath +LDAY @key jsr AddToPath jsr LoadFileAt @titleaddress !word $FD00 @skipload jsr okvs_get !word gGamesListStore @key !word $FDFD +STAY SRC ; A/Y points to game title (in OKVS) ldy #0 ; copy game title into search bar buffer lda (SRC),y sta SAVE ; game title length inc SAVE - iny cpy SAVE bcc @printTitleChar beq @printCursor lda #" " +HIDE_NEXT_2_BYTES @printCursor lda #$7F +HIDE_NEXT_2_BYTES @printTitleChar lda (SRC),y ; copy game title to search UI sta UILine2,y cpy #MaxInputLength+1 bcc - ldx #40 lda #0 - sta UILine1-1,x ; reset search bar dex bne - tay - lda (SRC),y +LOW_ASCII_TO_LOWER cmp InputBuffer,x bne + lda #$0B ; add dots to highlight matched characters sta UILine1,y inx cpx InputLength beq @doneHighlight + inc HTAB iny cpy #40 bne - @doneHighlight jsr _DrawSearchBarOffscreen; actually draw the search UI (offscreen) jmp _ShowOtherPage ; now show everything at once ;------------------------------------------------------------------------------ _LoadCoverOffscreen ; clobbers all lda OffscreenPage beq + lda #$40 +HIDE_NEXT_2_BYTES + lda #$20 sta @coveraddress+1 +LDADDR Cover jsr SetPath jsr LoadFileAt ; load default background @coveraddress !word $FD00 ; SMC rts _DrawSearchBarOffscreen ; clobbers all lda #22 ; draw visible search bar sta VTAB lda OffscreenPage ror ; draw on offscreen page +LDADDR UILine1 jsr Draw40Chars lda OffscreenPage ror ; draw on offscreen page +LDADDR UILine2 jmp Draw40Chars _ShowOtherPage ; clobbers A, preserves X/Y lda OffscreenPage eor #$01 sta OffscreenPage bne + bit $C055 ; show page 2 rts + bit $C054 ; show page 1 rts _ResetInputTimeout ; clobbers X, preserves A/Y ldx #$16 stx Timeout stx Timeout+1 stx Timeout+2 rts _CoverFade ; clobbers all jsr _LoadCoverOffscreen jsr _ShowOtherPage lda OffscreenPage bne + jsr _LoadCoverOffscreen jsr _ShowOtherPage + ; load transition effect code at $6000 +LDADDR kFXDirectory jsr SetPath +LDADDR kFXCoverFade jsr AddToPath jsr LoadFile jmp $6000 kFXCoverFade !byte 9 !text "COVERFADE" Cover !byte 5 !text "COVER" UILine1 !byte 0,0,0,0,0,0,0,0,0,0 !byte 0,0,0,0,0,0,0,0,0,0 !byte 0,0,0,0,0,0,0,0,0,0 !byte 0,0,0,0,0,0,0,0,0,0 UILine2 !text "[" !byte $7F !text " " !text "] " UILine2_GameCount !text "000 games" OffscreenPage !byte 1 ; 0 = currently showing HGR page 2 ; (so offscreen is page 1 @ $2000) ; 1 = currently showing HGR page 1 ; (so offscreen is page 2 @ $4000) ; indices into InputDispatchTable kInputSearch = 0 kInputClear = 1 kInputBack = 2 kInputLaunch = 3 InputDispatchTable !word OnSearch !word OnClear !word OnBack !word OnLaunch