;license:MIT ;(c) 2018-9 by 4am ; ; Search Mode - main UI ; ; Public functions ; - SearchMode ; - IsSearchKey ; !zone { ;------------------------------------------------------------------------------ ; 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 +READ_RAM1_WRITE_RAM1 ldx #$FF txs stx SelectedIndex ; no game selected inx stx OffscreenPage jsr Home ; clear screen jsr .OnInputChanged ; draw UI on HGR page 1 +HGR_MODE ; show HGR bit CLEARKBD jsr ResetInputTimeout .SearchModeInputLoop jsr WaitForKeyFor30Seconds ; Don't clear keyboard strobe yet. If the user pressed an arrow key, ; we want to switch to Browse Mode with the keyboard still hot so ; that mode finds and dispatches the arrow key. cmp #$8B ; right arrow switches to browse mode beq @arrow cmp #$95 ; also up arrow beq @arrow cmp #$8A ; also down arrow bne @notArrow @arrow ldx #kInputBrowse bne @InputDispatch ; always branches @notArrow bit CLEARKBD jsr ResetInputTimeout cmp #$9B ; Esc clears the input buffer (if any) bne + ; or switches to mega 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 (may as well, since ; popular emulators remap this anyway) beq - cmp #$89 ; TAB switches to mini attract mode bne + ; if there is a game selected - ldx #kInputTab bne @InputDispatch ; always branches + cmp #$A0 ; SPACE = TAB because ][+ doesn't have TAB beq - cmp #$BF ; '?' display scredits bne + - ldx #kInputCredits bne @InputDispatch ; always branches + cmp #$AF ; '/' also displays credits beq - and #$7F ; strip high bit for search characters jsr IsSearchKey beq + jsr SoftBell ; Beep on invalid input and start over. bne .SearchModeInputLoop ; This always branches because SoftBell ; preserves all flags and we got here ; because the BEQ didn't branch. + 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 bcs + jmp .SearchModeInputLoop ; if carry is clear, we're done + 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 .OnTab ldx SelectedIndex cpx #$FF beq + jsr MiniAttractMode cmp #$8D ; if we exited mini attract mode beq .OnLaunch ; by pressing Enter, launch the game sec ; tell caller to redraw UI rts + clc rts .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 clc 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 #39 ; reset visible line - lda #0 sta UILine1,x lda Instructions,x sta UILine2,x dex bpl - jsr LoadTitleOffscreen 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 clc 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 ; get the name of the new best match !word gGamesListStore @index !byte $FD +STAY @key plp bne + jsr ToggleOffscreenPage ; Since we're not loading a new screenshot ; we fake switching the 'offscreen' page ; in order to draw on the visible page. bpl @skipload ; always branches + jsr GetOffscreenAddress ; we have a new best match, so load the sta + ; new title screenshot (offscreen) +LDADDR kHGRTitleDirectory jsr SetPath +LDAY @key jsr AddToPath jsr LoadFileAt !byte $00 + !byte $FD ; SMC @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 #8 - lda ReturnToPlay,x ; replace games count with 'to play' label sta UI_ToPlay,x dex bpl - ldx #40 lda #0 - sta UILine1-1,x ; reset search bar dex bne - tay @dotloop iny lda (SRC),y +LOW_ASCII_TO_LOWER cmp InputBuffer,x bne + lda #$11 ; add dots to highlight matched characters sta UILine1,y inx cpx InputLength ; if input buffer is exhausted, we're done beq @doneHighlight + inc HTAB cpy SAVE ; if game name is exhausted, we're done bne @dotloop @doneHighlight jsr DrawSearchBarOffscreen; actually draw the search UI (offscreen) jmp ShowOtherPage ; now show everything at once IsSearchKey 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 ldx #1 rts @goodkey ldx #0 rts ;------------------------------------------------------------------------------ ; indices into InputDispatchTable kInputSearch = 0 kInputClear = 1 kInputBack = 2 kInputBrowse = 3 kInputTab = 4 kInputLaunch = 5 kInputCredits = 6 .InputDispatchTable !word .OnSearch !word .OnClear !word .OnBack !word BrowseMode !word .OnTab !word .OnLaunch !word Credits }