Browse Mode

This commit is contained in:
4am 2019-07-03 18:31:50 -04:00
parent 045fe1cfef
commit a37dd59b4f
6 changed files with 355 additions and 109 deletions

View File

@ -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

View File

@ -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

View File

@ -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

217
src/ui.browse.mode.a Normal file
View File

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

View File

@ -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

View File

@ -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