From a37dd59b4f9e3985f56d895576fe2d4362275b8c Mon Sep 17 00:00:00 2001 From: 4am Date: Wed, 3 Jul 2019 18:31:50 -0400 Subject: [PATCH] Browse Mode --- src/4cade.a | 3 + src/4cade.init.a | 1 + src/glue.launch.a | 2 + src/ui.browse.mode.a | 217 +++++++++++++++++++++++++++++++++++++++++++ src/ui.common.a | 83 +++++++++++++++++ src/ui.search.mode.a | 158 ++++++++++--------------------- 6 files changed, 355 insertions(+), 109 deletions(-) create mode 100644 src/ui.browse.mode.a diff --git a/src/4cade.a b/src/4cade.a index 20d85f6a3..62f8faecc 100644 --- a/src/4cade.a +++ b/src/4cade.a @@ -65,6 +65,7 @@ RestoreStackNextTime ; these routines will only be called after relocating to language card !source "src/ui.search.mode.a" + !source "src/ui.browse.mode.a" !source "src/ui.attract.mode.a" !source "src/ui.attract.hgr.a" !source "src/ui.attract.dhgr.a" @@ -81,6 +82,8 @@ RestoreStackNextTime !source "src/ui.common.a" MachineStatus !byte 0 +GameCount + !byte 0 gAttractModeStore gFXStore gDFXStore diff --git a/src/4cade.init.a b/src/4cade.init.a index cb8aaabe9..e8b35f220 100644 --- a/src/4cade.init.a +++ b/src/4cade.init.a @@ -117,6 +117,7 @@ OneTimeSetup jsr okvs_len !word gGamesListStore + sta GameCount sta SAVE ; calculate and update visible game count (3-digit decimal number as ASCII string) ldy #0 diff --git a/src/glue.launch.a b/src/glue.launch.a index 74c825530..852081c5f 100644 --- a/src/glue.launch.a +++ b/src/glue.launch.a @@ -5,6 +5,7 @@ ; ; Public functions ; - PlayGameFromSearch +; - PlayGameFromBrowse ; - PlayGameFromAttract ; - Prelaunch ; - Launch @@ -17,6 +18,7 @@ !zone { PlayGameFromSearch +PlayGameFromBrowse stx @gameIndex jsr okvs_nth !word gGamesListStore diff --git a/src/ui.browse.mode.a b/src/ui.browse.mode.a new file mode 100644 index 000000000..03ca52aa2 --- /dev/null +++ b/src/ui.browse.mode.a @@ -0,0 +1,217 @@ +;license:MIT +;(c) 2018-9 by 4am +; +; Browse Mode - main UI +; +; Public functions +; - BrowseMode +; + +!zone { + +;------------------------------------------------------------------------------ +; BrowseMode +; main entry point for Browse Mode, which allows the user to browse the game +; catalog in alphabetical order and launch games +; +; in: none +; out: never returns to caller (may JMP to other major modes) +;------------------------------------------------------------------------------ +BrowseMode + +READ_RAM1_WRITE_RAM1 + ldx #$FF + txs + ldx SelectedIndex + stx BrowseSelectedIndex + +.BrowseModeInputLoop + lda KBD + bmi @gotKey + + inc RNDSEED+1 ; these are only ever incremented, never + bne + ; reset (may be used as a pseudorandom + inc RNDSEED ; seed) ++ + dec Timeout ; these are a 3-byte timeout counter + bne .BrowseModeInputLoop ; that counts down from a number set + dec Timeout+1 ; in .ResetInputTimeout and reset + bne .BrowseModeInputLoop ; on every keypress (whether or not + dec Timeout+2 ; the key leads to an action) + bne .BrowseModeInputLoop +; jsr .CoverFade ; no input for ~30 seconds, switch to + jmp MegaAttractMode ; mega-attract mode + +@gotKey + bit CLEARKBD + jsr ResetInputTimeout + + cmp #$88 ; left arrow = previous + bne + +- ldx #kBrowsePrevious + bne @BrowseDispatch ; always branches ++ cmp #$8B ; up arrow is same as left arrow + beq - + + cmp #$95 ; right arrow = next + bne + +- ldx #kBrowseNext + bne @BrowseDispatch ; always branches ++ cmp #$8A ; down arrow is same as right arrow + beq - + + cmp #$9B ; Esc switches to search mode + bne + + ldx #kBrowseExitMode + bne @BrowseDispatch ; always branches ++ + cmp #$8D ; ENTER launches the current game + bne + + ldx #kBrowseLaunch + bne @BrowseDispatch ; always branches ++ + cmp #$89 ; TAB switches to mini attract mode + bne + ; temporarily + ldx #kBrowseTab + bne @BrowseDispatch ; always branches ++ + 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 .BrowseModeInputLoop ; and start over + +@goodkey + ldx #kBrowseSearch + ; execution falls through here +@BrowseDispatch + pha ; save key pressed + txa + asl + tax + lda .BrowseDispatchTable,x + sta @j+1 + lda .BrowseDispatchTable+1,x + sta @j+2 + pla ; restore key pressed +@j jsr $FDFD ; SMC + bcs + + jmp .BrowseModeInputLoop ; if carry is clear, we're done + ; TODO ++ jmp BrowseMode ; if carry is set, force full redraw + +.OnSearch + sta InputBuffer + lda #$01 + sta InputLength + jmp SearchMode + +.OnPrevious + dec BrowseSelectedIndex + jmp + +.OnNext + inc BrowseSelectedIndex ++ + ldx BrowseSelectedIndex + cpx #$FF + bne @notTooSmall + ldx GameCount + dex + bne @done ; always branches +@notTooSmall + cpx GameCount + bcc @done + ldx #0 +@done stx BrowseSelectedIndex + jmp .OnBrowseChanged + +.OnTab + ldx BrowseSelectedIndex + jsr MiniAttractMode + cmp #$8D + beq .OnLaunch + ; TODO redraw + rts + +.OnLaunch + ldx BrowseSelectedIndex + jsr PlayGameFromBrowse + jmp BrowseMode + +.OnBrowseChanged + stx @index + jsr okvs_nth ; get the name of the new game + !word gGamesListStore +@index !byte $FD + +STAY @key + jsr GetOffscreenAddress ; load new title screenshot offscreen + sta + ; new title screenshot (offscreen) + +LDADDR kHGRTitleDirectory + jsr SetPath + +LDAY @key + jsr AddToPath + jsr LoadFileAt + !byte $00 ++ !byte $FD ; SMC + + 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 + lda #" " + +HIDE_NEXT_2_BYTES +@printTitleChar + lda (SRC),y ; copy game title to UI line 2 + sta UILine2,y + cpy #MaxInputLength+1 + bcc - + ldx #40 + lda #0 +- sta UILine1-1,x ; reset UI line 1 + dex + bne - + jsr DrawSearchBarOffscreen ; actually draw the search UI (offscreen) + jmp ShowOtherPage ; now show everything at once + +;------------------------------------------------------------------------------ + +; indices into BrowseDispatchTable +kBrowseSearch = 0 +kBrowsePrevious = 1 +kBrowseNext = 2 +kBrowseExitMode = 3 +kBrowseTab = 4 +kBrowseLaunch = 5 + +.BrowseDispatchTable + !word .OnSearch + !word .OnPrevious + !word .OnNext + !word SearchMode + !word .OnTab + !word .OnLaunch + +BrowseSelectedIndex + !byte $FF + +} diff --git a/src/ui.common.a b/src/ui.common.a index afa834c95..dcf593412 100644 --- a/src/ui.common.a +++ b/src/ui.common.a @@ -4,13 +4,96 @@ ; common UI functions ; ; Public functions +; - GetOffscreenAddress +; - DrawSearchBarOffscreen +; - ShowOtherPage +; - ToggleOffscreenPage +; - ResetInputTimeout ; - SoftBell ; - Home ; - BlankHGR ; - BlankDHGR + +; Public variables +; - OffscreenPage +; - UILine1 +; - UILine2 +; - VisibleGameCount (set during init) ; !zone { +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) + +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 "] " +VisibleGameCount + !text "000" + !text " games" + +GetOffscreenAddress +; in: none +; out: A = high byte of offscreen HGR page (#$20 or #$40) +; preserves X/Y + lda OffscreenPage + beq + + lda #$40 + rts ++ lda #$20 + 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 +; in: none +; out: A = new value of OffscreenPage +; preserves X/Y + jsr ToggleOffscreenPage + bne + + bit PAGE2 ; show page 2 + rts ++ bit PAGE1 ; show page 1 + rts + +ToggleOffscreenPage +; in: none +; out: A = new value of OffscreenPage +; preserves X/Y + lda OffscreenPage + eor #$01 + sta OffscreenPage + rts + +ResetInputTimeout +; clobbers X, preserves A/Y + ldx #$16 + stx Timeout + stx Timeout+1 + stx Timeout+2 + rts ;------------------------------------------------------------------------------ ; SoftBell diff --git a/src/ui.search.mode.a b/src/ui.search.mode.a index 5a7c3e3a7..44b93ba40 100644 --- a/src/ui.search.mode.a +++ b/src/ui.search.mode.a @@ -6,8 +6,6 @@ ; Public functions ; - SearchMode ; -; Public addresses -; - VisibleGameCount (set during init) !zone { @@ -25,12 +23,12 @@ SearchMode txs stx SelectedIndex ; no game selected inx - stx .OffscreenPage + stx OffscreenPage jsr Home ; clear screen jsr .OnInputChanged ; draw UI on HGR page 1 +HGR_MODE ; show HGR bit CLEARKBD - jsr .ResetInputTimeout + jsr ResetInputTimeout .SearchModeInputLoop lda KBD @@ -42,7 +40,7 @@ SearchMode + 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 + 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 @@ -50,8 +48,19 @@ SearchMode jmp MegaAttractMode ; mega-attract mode @gotKey + cmp #$88 ; left arrow switches to browse mode + beq @arrow + cmp #$8B ; also right arrow + 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 + jsr ResetInputTimeout cmp #$9B ; Esc clears the input buffer (if any) bne + ; or switches to mega attract mode @@ -65,12 +74,9 @@ SearchMode + cmp #$FF ; delete key bne + -- ldx #kInputBack + ldx #kInputBack bne @InputDispatch ; always branches - -+ cmp #$88 ; left arrow = delete - beq - - ++ cmp #$89 ; TAB switches to mini attract mode bne + ; if there is a game selected ldx #kInputTab @@ -171,19 +177,19 @@ SearchMode sta SelectedIndex ; no game selected ldx #40 ; reset visible line lda #0 -- sta .UILine1-1,x +- sta UILine1-1,x dex bne - ldy #MaxInputLength ; clear visible search bar lda #" " -- sta .UILine2+1,y +- sta UILine2+1,y dey bne - lda #$7F - sta .UILine2+1 + sta UILine2+1 jsr .LoadTitleOffscreen - jsr .DrawSearchBarOffscreen - jsr .ShowOtherPage + jsr DrawSearchBarOffscreen + jsr ShowOtherPage clc rts @findMatchingTitle @@ -211,12 +217,12 @@ SearchMode +STAY @key plp bne + - lda .OffscreenPage ; since we're not loading a new screenshot - eor #$01 ; we fake switching the 'offscreen' page - sta .OffscreenPage ; in order to draw on the visible page + 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 + jsr GetOffscreenAddress ; we have a new best match, so load the sta + ; new title screenshot (offscreen) +LDADDR kHGRTitleDirectory jsr SetPath @@ -246,12 +252,12 @@ SearchMode +HIDE_NEXT_2_BYTES @printTitleChar lda (SRC),y ; copy game title to search UI - sta .UILine2,y + sta UILine2,y cpy #MaxInputLength+1 bcc - ldx #40 lda #0 -- sta .UILine1-1,x ; reset search bar +- sta UILine1-1,x ; reset search bar dex bne - tay @@ -260,7 +266,7 @@ SearchMode cmp InputBuffer,x bne + lda #$0B ; add dots to highlight matched characters - sta .UILine1,y + sta UILine1,y inx cpx InputLength beq @doneHighlight @@ -269,28 +275,24 @@ SearchMode cpy #40 bne - @doneHighlight - jsr .DrawSearchBarOffscreen; actually draw the search UI (offscreen) - jmp .ShowOtherPage ; now show everything at once + jsr DrawSearchBarOffscreen; actually draw the search UI (offscreen) + jmp ShowOtherPage ; now show everything at once ;------------------------------------------------------------------------------ -.GetOffscreenAddress -; in: none -; out: A = high byte of offscreen HGR page (#$20 or #$40) -; preserves X/Y - lda .OffscreenPage - beq + - lda #$40 - rts -+ lda #$20 - rts - .LoadTitleOffscreen -; clobbers all - jsr .GetOffscreenAddress - sta + +; [private] clobbers all +LDADDR @TitleFile + bne + ; Always branches, because Y is loaded + ; last with the high byte of the address, + ; which is never 0. I miss my 65c02. +.LoadCoverOffscreen +; [private] clobbers all + +LDADDR @CoverFile ++ jsr SetPath + jsr GetOffscreenAddress + sta + jsr LoadFileAt !byte $00 + !byte $FD ; SMC @@ -298,61 +300,18 @@ SearchMode @TitleFile !byte 5 !text "TITLE" - -.LoadCoverOffscreen -; clobbers all - jsr .GetOffscreenAddress - sta + - +LDADDR @CoverFile - jsr SetPath - jsr LoadFileAt - !byte $00 -+ !byte $FD ; SMC - rts @CoverFile !byte 5 !text "COVER" -.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 PAGE2 ; show page 2 - rts -+ bit PAGE1 ; show page 1 - rts - -.ResetInputTimeout -; clobbers X, preserves A/Y - ldx #$16 - stx Timeout - stx Timeout+1 - stx Timeout+2 - rts - .CoverFade -; clobbers all +; [private] clobbers all jsr .LoadCoverOffscreen - jsr .ShowOtherPage - lda .OffscreenPage + jsr ShowOtherPage + lda OffscreenPage bne + jsr .LoadCoverOffscreen - jsr .ShowOtherPage + jsr ShowOtherPage + ; load transition effect code at $6000 +LDADDR kFXDirectory @@ -365,38 +324,19 @@ SearchMode !byte 9 !text "COVERFADE" -.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 "] " -VisibleGameCount - !text "000" - !text " 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 -kInputTab = 3 -kInputLaunch = 4 +kInputBrowse = 3 +kInputTab = 4 +kInputLaunch = 5 .InputDispatchTable !word .OnSearch !word .OnClear !word .OnBack + !word BrowseMode !word .OnTab !word .OnLaunch