4cade/src/ui.search.mode.a

317 lines
9.8 KiB
Plaintext

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