;license:MIT ;(c) 2018-9 by 4am ; ; Functions to launch games and self-running demos ; ; Public functions ; - GetGameToLaunch ; - FindGame ; - FindGameInActionSlideshow ; - GetGameDisplayName ; - PlayGame ; - Launch ; ; Public variables: ; - gGameToLaunch - 0-based index into gGamesListStore (word) ; gGameToLaunch !word $FFFF ;------------------------------------------------------------------------------ ; AnyGameSelected ; get index of game that is currently selected in the UI (if any) ; ; in: none ; out: A/Y = gGameToLaunch (word) ; C clear if gGameToLaunch is not #$FFFF ; C set if gGameToLaunch is #$FFFF ; X preserved ;------------------------------------------------------------------------------ AnyGameSelected +LD16 gGameToLaunch +CMP16_NE $FFFF, ForceGoodResult rts ; CMP sets carry when equal ;------------------------------------------------------------------------------ ; GetGameToLaunch ; get filename of game that is currently selected in the UI (if any) ; ; in: gGameToLaunch = index into gGamesListStore (word) or #$FFFF if no game selected ; out: C clear if a game is selected, and ; A/Y points to game filename ; C set if no game is selected ;------------------------------------------------------------------------------ GetGameToLaunch jsr AnyGameSelected bcs _gameToLaunchExit +ST16 WINDEX +LDADDR gGamesListStore jsr okvs_nth ForceGoodResult clc _gameToLaunchExit rts ;------------------------------------------------------------------------------ ; FindGame ; check if an arbitrary game exists, for some definition of 'exists' ; ; in: A/Y points to game filename ; out: C clear if game exists in gGamesListStore, and ; $WINDEX = game index, or #$FFFF if the game doesn't really ; exist but we want to return a successful result anyway ; C set if game does not exist (this can happen because slideshows ; list games that require a joystick, but the games list parser ; filters them out if the machine doesn't have a joystick) ; all registers clobbered ;------------------------------------------------------------------------------ FindGame +ST16 @key jsr okvs_find !word gGamesListStore @key !word $FDFD ; SMC bcc GlueLaunchRTS ; Hack to allow self-running demos that don't correspond to a game ; filename. If the name ends in a '.', accept it unconditionally. ldx #$FF stx WINDEX stx WINDEX+1 +LD16 @key +ST16 PARAM ldy #0 lda (PARAM),y tay lda (PARAM),y cmp #"." beq ForceGoodResult sec GlueLaunchRTS rts ;------------------------------------------------------------------------------ ; FindGameInActionSlideshow ; check if an arbitrary game exists, for some definition of 'exists', while ; in the middle of a slideshow ; ; /!\ This function assumes that gSlideshowStore exists and is populated, ; which is generally only true during the callback function of a slideshow ; ; in: A/Y points to game filename ; out: C clear if game exists, and ; $WINDEX = game index, and ; A/Y points to game display name + game info bitfield ; C set if game can't be found by any means ;------------------------------------------------------------------------------ FindGameInActionSlideshow +ST16 @sskey jsr FindGame bcc GetGameDisplayName ; if the game was not found, try getting the value of the current record from ; gSlideshowStore (some games have multiple action screenshots, in which case ; the key is only the screenshot filename, and the value is the actual game ; filename) jsr okvs_get !word gSlideshowStore @sskey !word $FDFD ; SMC jsr FindGame bcs GlueLaunchRTS ; /!\ execution falls through here to GetGameDisplayName ;------------------------------------------------------------------------------ ; GetGameDisplayName ; lookup or construct the display name for a specific game ; ; in: A/Y contains address of a key in gGamesListStore ; out: A/Y contains address of game display name + game info bitfield ; (this might be just the corresponding value in gGamesListStore, ; or it might be a temporary buffer in main memory that we constructed ; out of thin air) ; gValLen possibly clobbered (up to gValLen+MaxInputLength) ; X preserved ; C clear ;------------------------------------------------------------------------------ GetGameDisplayName +ST16 SAVE jsr okvs_get_current ; get value for this key ; (PTR) -> truncated game display name + info bitfield ; Y = 0 lda (PTR), y ; A = length of truncated game display name + info bitfield lsr ;;cmp #1 ; 1 means there's no title, just info bitfield (1 byte) beq + +LD16 PTR clc rts + ; game display name is truncated, we must expand it iny ; Y = 1 lda (PTR), y ; A = game info bitfield pha ; save on stack dey lda (SAVE), y ; A = length of key tay iny sty @len sty gValLen lda #$24 ; BIT opcode sta @or ; first character remains capitalized ldy #1 - lda (SAVE), y cmp #$49 ; 'I' bne + cmp gValLen-1,y beq ++ ; preserve 'II' casing + cmp #$2E bne @or lda #$20 ; convert '.' to ' ' @or ora #$20 ; SMC (opcode) ++ sta gValLen, y cmp #$20 bne + lda #$24 ; first character after ' ' remains capitalized +HIDE_NEXT_2_BYTES + lda #$09 ; OR opcode sta @or @next iny @len=*+1 cpy #$FD ; SMC bne - pla sta gValLen, y +LDADDR gValLen clc rts ;------------------------------------------------------------------------------ ; PlayGame ; ; in: gGameToLaunch != #$FFFF ; out: exits via the game, but also backs up the stack, which may get ; restored in |Reenter|... what I'm saying is that we might actually ; 'return' from this after the user is done playing the game, which is ; a neat trick ;------------------------------------------------------------------------------ PlayGame jsr GetGameToLaunch ; A/Y = address of game filename +ST16 SAVE +ST16 @pfile jsr ClearScreens ; avoid seeing code load into the HGR page ; (clobbers $106, must do now before loading prelaunch code) jsr LoadFile ; load prelaunch index file into $6000 !word kRootDirectory !word kPrelaunchIndexFile - !word $6000 jsr okvs_find !word - @pfile !word $FDFD ; SMC +ST16 @indexRecordPtr jsr LoadIndexedFile !word kPrelaunchFulFile !word $0106 @indexRecordPtr !word $FDFD ; SMC ; we start by placing the subdirectory name at gPathname+kGameDirectoryLen ; to leave room for the GameDirectory name as the parent lda #kGameDirectoryLen sta gPathname +LD16 SAVE jsr AddToPath ; attach the separator inc gPathname lda #'/' sta gPathname+1,x ; then we save the length of the resulting string without the count byte dex txa pha ; then attach the game name +LD16 SAVE jsr AddToPath ; don't look while I do this ; we "place" a string at gPathname+kGameDirectoryLen ; whose length is the subdirectory name and game name ; then we load it ; the load sets the path to the GameDirectory ; and then finds the subdirectory name and game name right after it ; and attaches it to the path by overwriting the count byte ;;sec ; carry set by AddToPath lda gPathname sbc #kGameDirectoryLen sta gPathname+kGameDirectoryLen jsr LoadFile ; load the game startup file !word kGameDirectory !word gPathname+kGameDirectoryLen !word 0 ; use file's default address pla sta ProDOS_prefix ; set 'root' directory to the path part ; of the game startup file we just loaded ; so games can load other files without ; knowing which directory they're in ; execution falls through here Launch jsr SwitchToBank2 lda #$BD sta launchpatch jsr saveslot ; set proper slot information lda #$9D sta launchpatch jmp LaunchInternal Joystick jsr LoadStandardPrelaunch jsr LoadFile !word kRootDirectory !word kJoystickFile !word 0 jmp Launch LoadStandardPrelaunch jsr LoadIndexedFile ; load standard prelaunch code at $0106 !word kPrelaunchFulFile !word $0106 !word + rts + !byte 0 !be24 0 !le16 68