4cade/src/ui.common.a

610 lines
19 KiB
Plaintext
Raw Normal View History

;License:MIT
2019-06-26 02:44:39 +00:00
;(c) 2018-9 by 4am
;
; common UI functions
;
; Public functions
; - WaitForKeyFor30Seconds
2019-07-03 22:31:50 +00:00
; - GetOffscreenAddress
; - LoadTitleOffscreen
; - LoadCoverOffscreen
2019-09-21 21:35:49 +00:00
; - LoadGameTitleOffscreen
2019-09-24 19:35:35 +00:00
; - DrawUIWithoutDots
; - DrawUI
2019-07-03 22:31:50 +00:00
; - ShowOtherPage
; - ToggleOffscreenPage
2019-06-26 02:44:39 +00:00
; - SoftBell
; - Home
; - BlankHGR
; - BlankDHGR
; - ClearOffscreen
2019-09-06 19:21:38 +00:00
; - IsSearchKey
2019-09-21 22:04:38 +00:00
; - ExecuteTransitionAndWait
; - CoverFade
2019-07-03 22:31:50 +00:00
; Public variables
; - OffscreenPage
; - VisibleGameCount (set during init)
2019-06-26 02:44:39 +00:00
;
2019-07-03 22:31:50 +00:00
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)
Instructions
!text "[Type to search, "
!byte $15 ; right arrow character
!text " to browse] "
2019-07-03 22:31:50 +00:00
VisibleGameCount
!text "000 games"
ReturnToPlay
!byte $0D
!text " to play"
2019-07-03 22:31:50 +00:00
2019-09-21 17:41:36 +00:00
kCheatsEnabled = 3 ; index of 'cheats enabled' string in following table
kCheatDescriptionLo
!byte <sNoCheats
!byte <sInfiniteLives
!byte <sInfiniteLivesAndWeapons
!byte <sCheatsEnabled
kCheatDescriptionHi
!byte >sNoCheats
!byte >sInfiniteLives
!byte >sInfiniteLivesAndWeapons
!byte >sCheatsEnabled
sNoCheats
!byte 8
!text "no cheat"
sInfiniteLives
!byte 18
!byte $16 ; bolt
!text " "
!text "infinite lives"
!text " "
!byte $16 ; bolt
sInfiniteLivesAndWeapons
!byte 28
!byte $16 ; bolt
!text " "
!text "infinite lives & weapons"
!text " "
!byte $16 ; bolt
sCheatsEnabled
!byte 18
!byte $16 ; bolt
!text " "
!text "cheats enabled"
!text " "
!byte $16 ; bolt
sCheatDescriptionPrefix
!byte 2
!byte $03 ; vertical line
!text " "
sCheatDescriptionSuffix
!byte 2
!text " "
!byte $03 ; vertical line
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
; GetOffscreenAddress
; get high byte of HGR page that is currently not showing
;
2019-07-03 22:31:50 +00:00
; in: none
; out: A = high byte of offscreen HGR page (#$20 or #$40)
; preserves X/Y
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
GetOffscreenAddress
2019-07-03 22:31:50 +00:00
lda OffscreenPage
beq +
lda #$40
rts
+ lda #$20
rts
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
; LoadTitleOffscreen
; load title screen in the HGR page that is currently not showing
;
; in: none
; out: all flags and registers clobbered
;------------------------------------------------------------------------------
LoadTitleOffscreen
2019-09-21 22:11:52 +00:00
+LDADDR kTitleFile
bne + ; Always branches, because Y is loaded
; last with the high byte of the address,
; which is never 0. I miss my 65c02.
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
; LoadCoverOffscreen
; load cover screen in the HGR page that is currently not showing
;
; in: none
; out: all flags and registers clobbered
;------------------------------------------------------------------------------
LoadCoverOffscreen
; clobbers all
2019-09-21 22:11:52 +00:00
+LDADDR kCoverFile
+
2019-09-10 02:38:17 +00:00
+STAY @fname
jsr GetOffscreenAddress
sta +
2019-09-10 02:38:17 +00:00
jsr LoadFile
!word kRootDirectory
@fname !word $FDFD ; SMC
!byte $00
+ !byte $FD ; SMC
rts
2019-09-21 21:35:49 +00:00
LoadGameTitleOffscreen
; in: X = game index
+LDADDR gGamesListStore
jsr okvs_nth
+STAY @fname
jsr GetOffscreenAddress
sta +
jsr LoadFile
!word kHGRTitleDirectory
@fname !word $FDFD ; SMC
!byte $00
+ !byte $FD ; SMC
rts
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
2019-09-21 21:35:49 +00:00
; DrawUIWithoutDots/DrawUI
; draw 2- or 4-line UI on the HGR page that is not currently showing, then
; show that HGR page
2019-09-10 04:19:07 +00:00
;
2019-09-21 16:59:56 +00:00
; in: gGameToLaunch = game index, or #$FF if no game is selected
2019-09-10 04:19:07 +00:00
; out: all flags and registers clobbered
;------------------------------------------------------------------------------
2019-09-21 21:35:49 +00:00
DrawUIWithoutDots
2019-09-21 17:41:36 +00:00
lda #" "
2019-09-21 21:35:49 +00:00
clc
bcc +
DrawUI
lda #$7F
sec
+ sta @printCursor+1 ; set up cursor printing based on entry point
php
2019-09-21 17:41:36 +00:00
ldy #39
- lda #$00 ; horizontal bar character
sta UILine1,y ; reset UI line 1 to solid bar
sta gPathname,y ; reset cheat UI line 1 to solid bar
lda Instructions,y
sta UILine2,y ; copy instructions to UI line 2
dey
bpl -
ldx gGameToLaunch
cpx #$FF ; if no game, nothing more to do on UI line 2
beq @doneWithLine2
+LDADDR gGamesListStore
jsr okvs_nth
2019-09-22 00:03:46 +00:00
jsr okvs_get_current
; (PTR) -> game title
2019-09-21 17:41:36 +00:00
ldy #0 ; copy game title into UI line 2
2019-09-22 00:03:46 +00:00
lda (PTR),y
2019-09-21 17:41:36 +00:00
sta SAVE ; title length
inc SAVE
- iny
cpy SAVE
bcc @printTitleChar
beq @printCursor
lda #" "
+HIDE_NEXT_2_BYTES
@printCursor
lda #$FD ; SMC
+HIDE_NEXT_2_BYTES
@printTitleChar
2019-09-22 00:03:46 +00:00
lda (PTR),y
2019-09-21 17:41:36 +00:00
sta UILine2,y
cpy #MaxInputLength+1
bcc -
ldx #8 ; replace games count with 'to play' label
- lda ReturnToPlay,x
sta UI_ToPlay,x
dex
bpl -
@doneWithLine2
bit gCheatsEnabled
bpl @maybeDrawDots ; if cheat mode is disabled, we don't need
; any curves or spaces on UI line 1
ldx gGameToLaunch
ldy gCheatsAvailable,x
cpx #$FF
bne +
ldy #kCheatsEnabled
+
lda kCheatDescriptionLo,y
sta SAVE
lda kCheatDescriptionHi,y
sta SAVE+1
; (SAVE) -> length-prefixed string
; (game-specific description or 'cheats enabled' message)
ldy #0
lda (SAVE),y ; A = string length
clc
adc #4 ; extra padding (2 on each side)
sta @len
lda #40
sec
sbc @len
lsr
tax
lda #$09 ; rounded bottom-right character
sta UILine1,x
ldy #1 ; fill the proper width with spaces
lda #$20 ; space character
- inx
sta UILine1,x
iny
@len=*+1
cpy #$FD ; SMC
bne -
lda #$0C ; rounded bottom-left character
2019-09-21 17:41:36 +00:00
sta UILine1,x
@maybeDrawDots
plp
bcc @doneHighlight ; if caller asked for no dots, then we're done building UI line 1
ldx #0
ldy #0
@dotloop
iny
2019-09-22 00:03:46 +00:00
lda (PTR),y ; (PTR) still points to game title
2019-09-21 17:41:36 +00:00
+LOW_ASCII_TO_LOWER
cmp InputBuffer,x
bne +
lda #$11 ; dot character
sta UILine1,y
inx
cpx InputLength ; if input buffer is exhausted, we're done drawing dots
beq @doneHighlight
+ inc HTAB
cpy SAVE ; if game name is exhausted, we're done drawing dots
bne @dotloop
@doneHighlight
lda #22
2019-07-03 22:31:50 +00:00
sta VTAB
lda OffscreenPage
2019-09-21 17:41:36 +00:00
ror
php
2019-07-03 22:31:50 +00:00
+LDADDR UILine1
2019-09-21 17:41:36 +00:00
jsr Draw40Chars ; draw UI line 1 on offscreen page
plp
2019-07-03 22:31:50 +00:00
+LDADDR UILine2
2019-09-21 17:41:36 +00:00
jsr Draw40Chars ; draw UI line 2 on offscreen page
2019-09-21 17:41:36 +00:00
bit gCheatsEnabled ; if cheats are disabled, then we're done drawing UI
2019-09-21 21:35:49 +00:00
bpl @uidone
2019-09-21 17:41:36 +00:00
; (SAVE) still points to length-prefixed cheat description
ldy #0
lda (SAVE),y ; A = length of cheat description
clc
adc #4 ; extra padding (2 on each side)
sta gPathname ; gPathname = length
tax
lda #$07 ; gPathname+length = top-right rounded corner character
sta gPathname,x
lda #$06 ; gPathname+1 = top-left rounded corner character
sta gPathname+1
lda #20
sta VTAB
lda OffscreenPage
ror
2019-09-21 17:41:36 +00:00
php
+LDADDR gPathname
2019-09-21 17:41:36 +00:00
jsr DrawCenteredString ; draw cheat UI line 1
2019-09-21 17:41:36 +00:00
ldx gGameToLaunch
ldy gCheatsAvailable,x
cpx #$FF
bne +
ldy #kCheatsEnabled
+ lda kCheatDescriptionLo,y
sta SAVE
lda kCheatDescriptionHi,y
sta SAVE+1
; (SAVE) -> length-prefixed cheat description
+LDADDR sCheatDescriptionPrefix
jsr SetPath
+LDAY SAVE
jsr AddToPath
+LDADDR sCheatDescriptionSuffix
jsr AddToPath
inc VTAB
2019-09-21 17:41:36 +00:00
plp
+LDADDR gPathname
2019-09-21 21:35:49 +00:00
jsr DrawCenteredString ; draw cheat UI line 2
@uidone jmp ShowOtherPage
2019-07-03 22:31:50 +00:00
;------------------------------------------------------------------------------
; ClearOffscreen
; clear $2000..$3FFF or $4000..$5FFF, depending on which HGR page is not
; visible right now
; does not change HGR mode
;
; in: none
; out: $2000..$3FFF or $4000..$5FFF cleared
; A = 0
; X = 0
; Y = 0
; Z = 1
;------------------------------------------------------------------------------
ClearOffscreen
jsr GetOffscreenAddress
+HIDE_NEXT_2_BYTES
.ClearHGR1
lda #$20
sta @a+2
ldx #$20
lda #$80
ldy #0
@a sta $2000,y
iny
bne @a
inc @a+2
dex
bne @a
rts
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
; ShowOtherPage
; switch to the HGR page that is not currently showing
;
2019-07-03 22:31:50 +00:00
; in: none
; out: A = new value of OffscreenPage
; preserves X/Y
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
ShowOtherPage
2019-07-03 22:31:50 +00:00
jsr ToggleOffscreenPage
bne +
bit PAGE2 ; show page 2
rts
+ bit PAGE1 ; show page 1
rts
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
; ToggleOffscreenPage
; switch the internal variable that tracks which HGR page is showing
; (does not affect screen)
;
2019-07-03 22:31:50 +00:00
; in: none
; out: A = new value of OffscreenPage
; preserves X/Y
2019-09-10 04:19:07 +00:00
;------------------------------------------------------------------------------
ToggleOffscreenPage
2019-07-03 22:31:50 +00:00
lda OffscreenPage
eor #$01
sta OffscreenPage
rts
2019-06-26 02:44:39 +00:00
;------------------------------------------------------------------------------
; SoftBell
; yell at the user, but, like, gently
;
; in: none
2019-09-07 18:15:53 +00:00
; out: C clear
; Y preserved
; A=0
; X=0
; all flags preserved
2019-06-26 02:44:39 +00:00
;------------------------------------------------------------------------------
SoftBell
ldx #32
- lda #2
jsr @wait
2019-06-27 14:55:07 +00:00
bit SPEAKER
2019-06-26 02:44:39 +00:00
lda #33
jsr @wait
2019-06-27 14:55:07 +00:00
bit SPEAKER
2019-06-26 02:44:39 +00:00
dex
bne -
2019-09-07 18:15:53 +00:00
clc
2019-06-26 02:44:39 +00:00
rts
@wait ; identical to $FCA8 ROM routine, but ROM is switched out when we need it
sec
-- pha
- sbc #1
bne -
pla
sbc #1
bne --
rts
;------------------------------------------------------------------------------
; Home
; clear and display text screen (HARDER THAN IT SOUNDS)
;
; in: none
; out: $0106..$011F clobbered
;------------------------------------------------------------------------------
Home
2019-09-11 04:26:00 +00:00
MachineStatus = *+1
lda #$D1 ; SMC
2019-06-26 02:44:39 +00:00
and #SUPPORTS_SHR
beq @noSHR
2019-06-27 14:55:07 +00:00
lda NEWVIDEO
2019-06-30 17:59:06 +00:00
and #$3F
sta NEWVIDEO ; get out of SHR mode and linear mode
2019-06-28 03:42:01 +00:00
lda #$F0
sta TBCOLOR ; white text on black background
lda #$00
sta CLOCKCTL ; black border
2019-06-28 03:50:25 +00:00
sta CLOCKCTL ; set twice for VidHD
2019-06-26 02:44:39 +00:00
@noSHR
ldx #(@end-@start-1)
- lda @start,x
sta $106,x
dex
bpl -
jmp $106
@start
; this will be run from main memory
+READ_ROM_NO_WRITE
2019-06-27 14:55:07 +00:00
sta CLR80VID ; get out of DHGR mode
sta DHIRESOFF ; get out of DHGR mode
jsr ROM_TEXT ; TEXT
jsr ROM_HOME ; HOME
2019-06-26 02:44:39 +00:00
+READ_RAM1_WRITE_RAM1
rts
@end
;------------------------------------------------------------------------------
; BlankDHGR
; clear and show DHGR page 1 without flickering
;
; in: none
; out: text page clobbered (but screen holes preserved)
; $2000..$3FFF/main and /aux cleared
;------------------------------------------------------------------------------
BlankDHGR
jsr Home
jsr .ClearHGR1 ; clear hi-res screen 1
2019-06-27 14:55:07 +00:00
sta WRITEAUXMEM
2019-06-26 02:44:39 +00:00
jsr .ClearHGR1 ; clear hi-res screen 1 in auxmem
2019-06-27 14:55:07 +00:00
sta WRITEMAINMEM
sta SET80VID
sta DHIRESON
bit PAGE1
jmp HGRMode
;------------------------------------------------------------------------------
2019-09-10 04:24:54 +00:00
; BlankHGR
; clear and show HGR page 1 without flickering
;
; in: none
; out: text page clobbered (but screen holes preserved)
; $2000..$3FFF cleared
;------------------------------------------------------------------------------
BlankHGR
jsr Home
jsr .ClearHGR1 ; clear hi-res screen 1
bit PAGE1 ; show hi-res screen 1 (now blank)
; execution falls through here
;------------------------------------------------------------------------------
; HGRMode
; twiddles softswitches to set HGR mode (does not set page 1 or 2)
;
; in: none
; out: all registers preserved
;------------------------------------------------------------------------------
HGRMode
bit $C057
bit $C052
bit $C050
2019-06-26 02:44:39 +00:00
rts
;------------------------------------------------------------------------------
; ExecuteTransitionAndWait
; call transition effect code (address passed in) and wait a period of time
; or until the user presses a key
;
; in: A/Y = address of transition effect code
; out: all flags and registers clobbered
;------------------------------------------------------------------------------
2019-06-26 02:44:39 +00:00
ExecuteTransitionAndWait
+STAY @j+1
@j jsr $FDFD ; SMC call transition effect code
ldx #$20 ; picture is showing so now we wait
- lda #0
jsr WaitForKeyWithTimeout
bmi +
dex
bpl -
+ lda KBD
cmp #$95
bne +
bit CLEARKBD
2019-06-26 02:44:39 +00:00
+ rts
2019-09-06 19:21:38 +00:00
;------------------------------------------------------------------------------
; IsSearchKey
; test whether accumulator contains a key that might trigger a new textrank
; search
;
; in: A = key
; out: A &= 0x7F
; Y preserved
; X clobbered
; Z = 1 if this is a search key
; Z = 0 if this is not a search key
;------------------------------------------------------------------------------
IsSearchKey
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
ldx #1
rts
@goodkey
ldx #0
2019-09-10 04:22:28 +00:00
ConvenientlyPlacedRTS
2019-09-06 19:21:38 +00:00
rts
2019-09-10 04:19:07 +00:00
; /!\ keep this last in the file to ensure it doesn't cross a page boundary /!\
;------------------------------------------------------------------------------
; WaitForKeyFor30Seconds
; does what it says on the tin
;
; in: none
; out: if user presses a key before the timer runs out, exits with A = key
; otherwise exits via MegaAttractMode
; X/Y preserved
;------------------------------------------------------------------------------
WaitForKeyFor30Seconds
lda #$16 ; initialize timeout counters
sta Timeout
sta Timeout+1
sta Timeout+2
@loop
lda KBD
2019-09-10 04:22:28 +00:00
bmi ConvenientlyPlacedRTS
2019-09-10 04:19:07 +00:00
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 @loop ; that counts down from a number set
dec Timeout+1 ; in .ResetInputTimeout and reset
bne @loop ; on every keypress (whether or not
dec Timeout+2 ; the key leads to an action)
bne @loop
2019-09-21 22:04:38 +00:00
; execution falls through here
CoverFade
jsr LoadCoverOffscreen
jsr ShowOtherPage
lda OffscreenPage
bne +
jsr LoadCoverOffscreen
jsr ShowOtherPage
+ jsr LoadFile ; load transition effect code at $6000
!word kFXDirectory
!word kCoverFadeFile
!word $6000
jsr $6000 ; call transition effect
jmp MegaAttractMode ; exit via mega attract mode