;license:MIT ;(c) 2018 by 4am ; ; Mega Attract Mode - cycle through slideshows and self-running demos ; ; Public functions ; - AttractMode ; ; Public variables ; - gAttractIndex ; [byte] numeric index of Mega-Attract Module ; - gFXIndex ; [byte] numeric index of HGR transition effect ; - gDFXIndex ; [byte] numeric index of DHGR transition effect ;------------------------------------------------------------------------------ ; AttractMode ; main entry point for Mega Attract Mode, which cycles through modules listed ; in ATTRACT.CONF to provide pretty visual effects and snippets of self-running ; self-terminating game demos ; ; in: gGlobalPrefsStore must be initialized ; out: exits to caller (note that some attract mode modules never return but ; instead call |Reenter|, in which case this routine technically never ; returns) ; if this routine returns, everything is clobbered (zero page, main ; memory, unused portions of the stack page, all registers, all flags) ;------------------------------------------------------------------------------ AttractMode jsr LoadFile ; load attract-mode configuration file at $8000 !word kAttractModeConfFile jsr ParseKeyValueList ; parse attract-mode configuration into OKVS data structure at $6000 !word gAttractModeStore !word ldrlo2 ; (ldrlo2) points to last load address, so $8000 !byte 0 jsr okvs_get ; get next attract-mode module from prefs !word gGlobalPrefsStore !word kNextAttract bcs @noattract +STAY @attract jsr okvs_get !word gAttractModeStore @attract !word $FDFD ; SMC bcc + @noattract ldx #0 + stx gAttractIndex jsr okvs_nth ; get filename of next attract-mode module !word gAttractModeStore gAttractIndex !byte 0 +STAY @key inc gAttractIndex ; increment module index for next time jsr okvs_len !word gAttractModeStore cmp gAttractIndex bne + lda #0 sta gAttractIndex + lda gAttractIndex sta @nexti jsr okvs_nth ; get name of next attract-mode module !word gAttractModeStore @nexti !byte $FD ; SMC +STAY @nextattract jsr okvs_update ; save name of next attract-mode module in prefs store !word gGlobalPrefsStore !word kNextAttract @nextattract !word $FDFD ; SMC jsr SaveGlobalPreferences ; write prefs store to disk jsr okvs_get !word gAttractModeStore @key !word $FDFD ; SMC +STAY PTR jsr @RunNextAttractModule lda $C000 bpl AttractMode rts @RunNextAttractModule ldy #1 lda (PTR),y and #$0F bne @Slideshow ; Self-running demos are loaded into main memory and executed. ; Each binary has been patched to quit on any key and jump back ; to the |Reenter| entry point. ; All demos are strictly 48K / main memory. No demo uses the ; language card or auxiliary memory. jsr Home ; clear text screen and switch to it during loading ; to avoid seeing executable code load into the HGR page +LOAD_PATH kDemoDirectory ldy gPathname iny iny sty ProDOS_prefix - lda gPathname-2, y sta ProDOS_prefix, y dey bne - lda #'/' sta ProDOS_prefix+1 lda #'X' sta ProDOS_prefix+2 +LOAD_FILE_IMM @key jsr SaveScreenHoles ; save screen hole contents in case game changes them ldx #(End_Prelaunch-Prelaunch-1) - lda Prelaunch,x ; copy pre-launch code to main memory sta $100,x dex bpl - ldx #(end_promote-promote-1) - lda promote,x ; copy ProDOS shim to main memory sta $bf00,x dex bpl - jmp $106 ; jump to pre-launch code @Slideshow ; HGR or DHGR slideshow pha ; save module type (1=HGR title, 2=HGR action, 3=DHGR title) ; load slideshow configuration file at $4000 +LOAD_FILE kAttractModeSlideshowDirectory, @key jsr ParseKeyValueList ; parse slideshow configuration into an OKVS data structure at $0800 !word gSlideshowStore !word ldrlo2 ; (ldrlo2) points to address of last loaded file, so $4000 !byte 0 pla ; restore module type cmp #$01 beq @HGRTitleSlideshow cmp #$02 beq @HGRActionSlideshow @DHGRTitleSlideshow bit MachineStatus ; only run DHGR slideshow if we have 128K bvc @exit jsr LoadDHGRTransition ; load transition effect code at $6000 jsr BlankDHGR ; switch to DHGR mode with initial blank screen jsr okvs_iter ; cycle through all listed DHGR files !word gSlideshowStore !word DHGRTitleCallback ; address of callback (called on each file) jmp BlankHGR ; switch back to HGR mode with initial blank screen on exit @HGRTitleSlideshow jsr LoadHGRTransition ; load transition effect code at $6000 jsr okvs_iter ; cycle through all listed HGR files !word gSlideshowStore !word HGRTitleCallback ; address of callback (called on each file) @exit rts ; exit with last picture still visible @HGRActionSlideshow jsr LoadHGRTransition ; load transition effect code at $6000 jsr okvs_iter ; cycle through all listed HGR files !word gSlideshowStore !word HGRActionCallback ; address of callback (called on each file) rts ; exit with last picture still visible kAttractModeConfFile !byte @kAttractModeConfFile_e-*-1 !text "ATTRACT.CONF" @kAttractModeConfFile_e ;------------------------------------------------------------------------------ ; internal functions ;------------------------------------------------------------------------------ ; HGRTitleCallback ; callback called by okvs_iter on gSlideshowStore ; to load and display a single HGR title screenshot ; in: A/Y contains address of filename (name only, path is always /HGR/) ; X contains 0-based index of the current record in gSlideshowStore ; out: all registers and flags clobbered ; $0800..$1EFF preserved (this contains the gSlideshowStore OKVS data) ; $2000..$BFFF clobbered by graphics data and transition code ;------------------------------------------------------------------------------ HGRTitleCallback ldx $C000 bpl + rts + +STAY PTR ; load HGR screenshot at $4000 +LOAD_FILE kHGRTitleDirectory, PTR jmp ExecuteTransition ; call transition effect code to display picture ;------------------------------------------------------------------------------ ; HGRActionCallback ; callback called by okvs_iter on gSlideshowStore ; to load and display a single HGR action screenshot ; in: A/Y contains address of filename (name only, path is always /ACTION/) ; X contains 0-based index of the current record in gSlideshowStore ; gGamesListStore must be initialized ; out: all registers and flags clobbered ; $0800..$1EFF preserved (this contains the gSlideshowStore OKVS data) ; $2000..$BFFF clobbered by graphics data and transition code ;------------------------------------------------------------------------------ HGRActionCallback ldx $C000 bpl + rts + +STAY PTR +STAY @key +STAY @key2 ; load HGR screenshot at $4000 +LOAD_FILE kHGRActionDirectory, PTR ; try to get the human-readable name of this game from gGamesListStore ; and display it in the bottom-left corner jsr okvs_get !word gGamesListStore @key !word $FDFD ; SMC bcc @foundname ; if the key is not found, try getting the value of the current record ; from gSlideshowStore and using that instead ; (some games have multiple action screenshots) jsr okvs_get !word gSlideshowStore @key2 !word $FDFD ; SMC bcs @noname ; should never happen +STAY @key3 jsr okvs_get !word gGamesListStore @key3 !word $FDFD ; SMC bcs @noname ; should never happen @foundname +STAY PTR @drawname ldx #0 stx HTAB ldx #22 stx VTAB ldy #0 lda (PTR),y tax +LDADDR @topline sec jsr DrawBuffer ldx #3 +LDADDR @toprightcorner sec jsr DrawBuffer inc VTAB ldx #0 stx HTAB inx +LDADDR @rightline sec jsr DrawBuffer +LDAY PTR sec jsr DrawString ldx #2 +LDADDR @rightline sec jsr DrawBuffer @noname jmp ExecuteTransition @topline !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 !byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @toprightcorner !byte 0,0,7 @rightline !text " " !byte 3 ;------------------------------------------------------------------------------ ; DHGRTitleCallback ; callback called by okvs_iter on gSlideshowStore ; to load and display a single DHGR title screenshot ; in: A/Y contains address of filename (name only, path is always /DHGR/) ; X contains 0-based index of the current record in gSlideshowStore ; gGamesListStore must be initialized ; out: all registers and flags clobbered ; $0800..$1EFF preserved (this contains the gSlideshowStore OKVS data) ; $2000..$BFFF clobbered by graphics data and transition code ; $2000..$5FFF/aux clobbered ;------------------------------------------------------------------------------ DHGRTitleCallback ldx $C000 bpl + rts + ; load DHGR screenshot at $4000/main and $4000/aux +STAY PTR jsr ResetPath +LDADDR kDHGRTitleDirectory jsr AddToPath +LDADDR kPathSeparator jsr AddToPath +LDAY PTR jsr AddToPath jsr LoadDHRFile !word gPathname ; execution falls through here ExecuteTransition jsr $6000 ; call transition effect code to display picture ldx #$20 ; picture is already showing so just wait - lda #0 jsr WaitForKeyWithTimeout bmi + dex bpl - + rts ;------------------------------------------------------------------------------ ; LoadHGRTransition ; looks up name of next HGR transition effect in FX.CONF and loads that file ; at $6000 ; in: gFXStore has been initialized ; gGlobalPrefsStore has been initialized ; out: all registers and flags clobbered ; $6000..$BFFF contains transition effect code ;------------------------------------------------------------------------------ LoadHGRTransition jsr LoadFile ; load HGR transition effects list into $8000 !word kFXConfFile jsr ParseKeyValueList ; parse HGR transition effects list into $6000 !word gFXStore !word ldrlo2 ; (ldrlo2) points to last load address !byte 0 jsr okvs_get ; get next HGR transition effect from prefs !word gGlobalPrefsStore !word kNextFX bcs @nofx +STAY @fx jsr okvs_get !word gFXStore @fx !word $FDFD ; SMC bcc + @nofx ldx #0 + stx gFXIndex jsr okvs_nth ; get filename of transition effect code !word gFXStore gFXIndex !byte 0 +STAY @fxkey inc gFXIndex ; increment transition effect index jsr okvs_len !word gFXStore cmp gFXIndex bne + lda #0 sta gFXIndex + lda gFXIndex sta @nexti jsr okvs_nth ; get name of next HGR transition !word gFXStore @nexti !byte $FD ; SMC +STAY @nextfx jsr okvs_update ; save name of next HGR transition in prefs store !word gGlobalPrefsStore !word kNextFX @nextfx !word $FDFD ; SMC jsr SaveGlobalPreferences ; write prefs store to disk ; load transition effect code at $6000 +LOAD_FILE kFXDirectory, @fxkey rts @fxkey !word $FDFD kFXConfFile !byte @kFXConfFile_e-@kFXConfFile_b @kFXConfFile_b !text "FX.CONF" @kFXConfFile_e ;------------------------------------------------------------------------------ ; LoadDHGRTransition ; looks up name of next DHGR transition effect in DFX.CONF and loads that file ; at $6000 ; in: gDFXStore has been initialized ; gGlobalPrefsStore has been initialized ; out: all registers and flags clobbered ; $6000..$BFFF/main contains transition effect code ;------------------------------------------------------------------------------ LoadDHGRTransition jsr LoadFile ; load DHGR transition effects list into $8000 !word kDFXConfFile jsr ParseKeyValueList ; parse DHGR transition effects list into $6000 !word gDFXStore !word ldrlo2 ; (ldrlo2) points to last load address !byte 0 jsr okvs_get ; get next DHGR transition effect from prefs !word gGlobalPrefsStore !word kNextDFX bcs @nodfx +STAY @dfx jsr okvs_get !word gDFXStore @dfx !word $FDFD ; SMC bcc + @nodfx ldx #0 + stx gDFXIndex jsr okvs_nth ; get filename of DHGR transition effect code !word gDFXStore gDFXIndex !byte 0 +STAY @dfxkey inc gDFXIndex ; increment transition effect index for next time jsr okvs_len !word gDFXStore cmp gDFXIndex bne + lda #0 sta gDFXIndex + lda gDFXIndex sta @nexti jsr okvs_nth ; get name of next DHGR transition !word gDFXStore @nexti !byte $FD ; SMC +STAY @nextdfx jsr okvs_update ; save name of next DHGR transition in prefs store !word gGlobalPrefsStore !word kNextDFX @nextdfx !word $FDFD ; SMC jsr SaveGlobalPreferences ; write prefs store to disk ; load transition effect code at $6000 +LOAD_FILE kFXDirectory, @dfxkey rts @dfxkey !word $FDFD kDFXConfFile !byte @kDFXConfFile_e-@kDFXConfFile_b @kDFXConfFile_b !text "DFX.CONF" @kDFXConfFile_e ;------------------------------------------------------------------------------ ; 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 lda $c057 ; show hi-res screen 1 (now blank) lda $c054 lda $c052 lda $c050 rts ;------------------------------------------------------------------------------ ; 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 sta $C005 jsr ClearHGR1 ; clear hi-res screen 1 in auxmem sta $C004 sta $c00d sta $c057 sta $c054 sta $c052 sta $c050 sta $c05e rts ;------------------------------------------------------------------------------ ; ClearHGR1 ; clear $2000..$3FFF in current memory bank (main or auxmem) ; ; in: none ; out: $2000..$3FFF cleared ; A = 0 ; X = 0 ; Y = 0 ; Z = 1 ;------------------------------------------------------------------------------ ClearHGR1 ldx #$20 stx @a+2 lda #0 tay @a sta $2000,y iny bne @a inc @a+2 dex bne @a rts ;------------------------------------------------------------------------------ ; Home ; clear and display text screen ; ; in: none ; out: $0106..$011F clobbered ;------------------------------------------------------------------------------ Home 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 sta $C00C ; get out of DHGR mode sta $C05F ; get out of DHGR mode jsr $FB2F ; TEXT jsr $FC58 ; HOME +READ_RAM1_WRITE_RAM1 rts @end ;------------------------------------------------------------------------------ ; Prelaunch ; code to set up machine for running a self-running, self-terminating game demo ; DO NOT CALL DIRECTLY ; must be run from main memory ; contains multiple entry points ; ; in: none ; out: exits via JMP (ldrlo2), which is expected to exit via JMP |Prelaunch| ; (not here, but wherever it was copied to in main memory) or by ; manually enabling LC RAM bank 1 then JMP |Reenter| ;------------------------------------------------------------------------------ Prelaunch ; this runs from main memory ; entry point used by some self-running demos lda $C088 jmp Reenter ; entry point to launch game +READ_ROM_NO_WRITE lda ldrlo2 ; set up game entry point in stack page ldy ldrhi2 ; (last load address - 1) sec sbc #$01 bcs + dey + +STAY $1FE ldx #0 ; wipe zero page txa - sta $00,x inx bne - lda #$65 ; initialize random seed (some games like Pooyan sta $4E ; require these to be non-zero) lda #$02 sta $4F jsr $FE89 ; initialize machine like a cold boot (many jsr $FE93 ; games assume a 'clean slate' and rely on jsr $FE84 ; zero page values set by these ROM routines, sta $C000 ; e.g. Wavy Navy just prints out text via $FDED sta $C002 ; and expects it to work) sta $C004 sta $C00C sta $C00E jsr $FB2F jsr $FC58 ldx #$FD ; jump to game entry point via stack pop txs rts End_Prelaunch