diff --git a/src/constants.a b/src/constants.a index 438cf26..1f66f2b 100644 --- a/src/constants.a +++ b/src/constants.a @@ -37,4 +37,5 @@ MAGICRTS = $FF58 ; used to set overflow bit PTR = $02 SRC = $04 DEST = $06 +ZPTR = $06 ; used by glue.zinfo because it's unused by okvs_append SAVE = $08 diff --git a/src/glue.common.a b/src/glue.common.a new file mode 100644 index 0000000..99b9961 --- /dev/null +++ b/src/glue.common.a @@ -0,0 +1,30 @@ +;license:MIT +;(c) 2018 by 4am +; +; Common functions across glue code +; +; Public functions +; - SetStartupPathToCurrentVersionOfCurrentGame + +SetStartupPathToCurrentVersionOfCurrentGame + jsr ResetPath ; gPathname = '' + + +LDADDR kGameRootDirectory ; gPathname = game root directory + '/' + jsr AddToPath + + jsr okvs_get + !word gGlobalPrefsStore + !word kLastPlayed + +STAY + ; A/Y contains address + jsr AddToPath ; gPathname = root + '/' + game shortname + + +LDADDR kPathSeparator + jsr AddToPath ; gPathname = root + '/' + game shortname + '/' + + jsr okvs_get + !word gGlobalPrefsStore ++ !word $FDFD ; SMC + jsr AddToPath ; gPathname = root + '/' + game shortname + '/' + game filename + + +LDADDR gPathname + jmp SetStartupPath ; store gPathname at $2006 to pass it to interpreter diff --git a/src/glue.onbeyond.a b/src/glue.onbeyond.a index aa0349b..342de24 100644 --- a/src/glue.onbeyond.a +++ b/src/glue.onbeyond.a @@ -11,11 +11,12 @@ ; - LaunchInterpreterWithHints ; +!zone { kGameRootDirectory ; length-prefixed pathname of game subdirectories !byte 2 !raw "Z/" kHintsRootDirectory ; length-prefixed pathname of hint files - !byte 5 + !byte 6 !raw "PRIZM/" kOnBeyondOptionsStruct = $0300 @@ -133,26 +134,7 @@ LaunchInterpreterWithGame jsr SaveGlobalPreferences jsr LoadInterpreter bcs .launchError - - jsr ResetPath - +LDADDR kGameRootDirectory - jsr AddToPath - - jsr okvs_get - !word gGlobalPrefsStore - !word kLastPlayed - +STAY + ; A/Y contains address - jsr AddToPath - - jsr SetPrefix ; set ProDOS prefix to game directory - !word gPathname - bcs .launchError - - jsr okvs_get - !word gGlobalPrefsStore -+ !word $FDFD ; SMC - jsr SetStartupPath ; store game filename at $2006 - + jsr SetStartupPathToCurrentVersionOfCurrentGame jsr ExitWeeGUI ; shut down WeeGUI jsr SetInterpreterOptions ; set options struct at $300 jmp kSystemAddress ; exit via interpreter @@ -175,6 +157,7 @@ LaunchInterpreterWithHints bcs .launchError jsr ResetPath + +LDADDR kHintsRootDirectory jsr AddToPath @@ -199,19 +182,15 @@ LaunchInterpreterWithHints ; out: C clear if success ; C set if interpreter could not be loaded ; all other registers and flags clobbered -; gPathname clobbered ;------------------------------------------------------------------------------ LoadInterpreter - jsr ResetPath - +LDADDR .interpreterFilename - jsr AddToPath - jsr LoadFile - !word gPathname + !word kInterpreterFilename !word kSystemAddress !word kProDOSFileBuffer rts -.interpreterFilename +kInterpreterFilename !byte 15 !raw "ONBEYOND.SYSTEM" +} diff --git a/src/glue.zinfo.a b/src/glue.zinfo.a new file mode 100644 index 0000000..5ed4e99 --- /dev/null +++ b/src/glue.zinfo.a @@ -0,0 +1,155 @@ +;license:MIT +;(c) 2018 by 4am +; +; glue code to load and call ZINFO, the Z-Machine saved game parser +; +; Public functions +; - LoadSavedGameInfo +; +; Public variables +; - gSavedGamesStore +; + +; TODO move these to a ZINFO_MLI kind of separate file +zinfo_base = $2000 +zinfo_recordlen= 64 +zinfo_maxslots = 8 +name_offset = 0 ;1+37 bytes +time_offset = 42 ;1+8 bytes ("12:01 pm") +score_offset = 51 ;1+6 bytes (-12345) +moves_offset = 58 ;1+5 bytes (12345) + +!zone { +gSavedGamesStore + !word $FDFD ; set in LoadGameInfo +gSavedGamesSlotsInUse + !byte $FD ; set in LoadSavedGameInfo + +;------------------------------------------------------------------------------ +; LoadSavedGameInfo +; load ZINFO, call it with the current game, and parse the results +; +; in: current ProDOS prefix is the same as the PITCH.DARK file +; out: C set if something went wrong +; C clear otherwise +;------------------------------------------------------------------------------ +LoadSavedGameInfo + jsr okvs_init + !word gSavedGamesStore + stz gSavedGamesSlotsInUse + + jsr LoadFile ; load ZINFO.SYSTEM at $2000 + !word kZINFOFilename + !word kSystemAddress + !word kProDOSFileBuffer + bcs .error + + jsr SetStartupPathToCurrentVersionOfCurrentGame + jsr kSystemAddress + jsr zparse + clc + rts +.error + jmp SoftBell + +zparse + +LDADDR zinfo_base + +STAY ZPTR + ldx #0 +.zParseLoop + +LDAY ZPTR + pha + phy + phx + jsr ResetPath + lda (ZPTR) + beq .saveAndMoveToNextSlot; no saved game in this slot + inc gSavedGamesSlotsInUse ; update number of slots in use (separate from length of store) + +LDAY ZPTR + jsr AddToPathWithHighBit ; add location information + lda ZPTR + clc + adc #time_offset + sta ZPTR + bcc + + inc ZPTR+1 ++ ; ZPTR -> ZINFO time field (length-prefixed string, may be empty string if no time) + +LDADDR kLeftParen + jsr AddToPathWithHighBit ; add ' (' + lda (ZPTR) + bne .justTime + lda ZPTR + clc + adc #(score_offset-time_offset) + sta ZPTR + bcc + + inc ZPTR+1 ++ ; ZPTR -> ZINFO score field (length-prefixed string, guaranteed non-empty if no time) + ldy ZPTR+1 + jsr AddToPathWithHighBit ; add score information + +LDADDR kSlash + jsr AddToPathWithHighBit ; add '/' + lda ZPTR + clc + adc #(moves_offset-score_offset) + sta ZPTR + bcc + + inc ZPTR+1 ++ ; A/Y -> ZINFO moves field (length-prefixed string, guaranteed non-empty) + ; execution falls through here +.justTime + +LDAY ZPTR + jsr AddToPathWithHighBit ; add final piece of information (moves or time, depending on how we got here) + +LDADDR kRightParen + jsr AddToPathWithHighBit ; add ')' + ; execution falls through here +.saveAndMoveToNextSlot + plx + phx + lda gPathname + beq + + lda #1 ++ sta kSlotName ; set length of key (0 if value is empty, otherwise 8) + + jsr okvs_append ; add this key/value to saved games store (used by ResumeDialog) + !word gSavedGamesStore + !word kSlotName ; key = printable short label, or empty string if slot is unused + !word gPathname ; value = printable long label, or empty string if slot is unused + !byte 0 + + plx + ply + pla + clc + adc #zinfo_recordlen + bcc + + iny ++ + +STAY ZPTR ; ZPTR -> start of next ZINFO record + + inx + cpx #zinfo_maxslots + bcs + + jmp .zParseLoop ++ rts + +kZINFOFilename + !byte 12 + !raw "ZINFO.SYSTEM" + +kSlotName + !byte 1 + !text " " + +kLeftParen + !byte 2 + !text " (" + +kRightParen + !byte 1 + !text ")" + +kSlash + !byte 1 + !text "/" +} diff --git a/src/okvs.a b/src/okvs.a index 0027c3e..099d0aa 100644 --- a/src/okvs.a +++ b/src/okvs.a @@ -108,6 +108,7 @@ okvs_len ; $00/$01 clobbered ; $02/$03 clobbered ; $04/$05 has the address of the next available byte after the new record +; $08/$09 clobbered ;------------------------------------------------------------------------------ okvs_append +PARAMS_ON_STACK 7 diff --git a/src/parse.gameinfo.a b/src/parse.gameinfo.a index 5d2ca9d..af94c36 100644 --- a/src/parse.gameinfo.a +++ b/src/parse.gameinfo.a @@ -137,6 +137,10 @@ LoadGameInfo !word gOptionsStore !word addrOptions !byte 0 + + +LDAY SRC + +STAY gSavedGamesStore ; save pointer to free space for next store + ; execution falls through here CheckForSavedGames jsr okvs_get ; get shortname of current game diff --git a/src/pitchdark.a b/src/pitchdark.a index 667ab69..4d66b45 100644 --- a/src/pitchdark.a +++ b/src/pitchdark.a @@ -30,17 +30,19 @@ !source "src/prodos.mli.a" !source "src/prodos.ramdisk.a" !source "src/prodos.path.a" - !source "src/glue.onbeyond.a" !source "src/parse.common.a" !source "src/parse.gamelist.a" !source "src/parse.gameinfo.a" !source "src/parse.prefs.a" + !source "src/glue.common.a" + !source "src/glue.onbeyond.a" + !source "src/glue.zinfo.a" !source "src/ui.common.a" !source "src/ui.sound.a" !source "src/ui.main.a" !source "src/ui.main.keys.a" !source "src/ui.options.a" -; !source "src/ui.resume.a" + !source "src/ui.resume.a" !source "src/ui.versions.a" !source "src/ui.artwork.a" diff --git a/src/prodos.path.a b/src/prodos.path.a index 27564b4..3d92563 100644 --- a/src/prodos.path.a +++ b/src/prodos.path.a @@ -37,7 +37,12 @@ ResetPath ; gPathname updated with concatenated length-prefixed string ;------------------------------------------------------------------------------ !zone { +AddToPathWithHighBit + ldx #$80 + !byte $2C AddToPath + ldx #0 + stx .mask +STAY $00 ldx gPathname ; current pathname length lda ($00) ; length of this segment @@ -45,6 +50,8 @@ AddToPath sta .len ldy #$01 - lda ($00),y +.mask=*+1 + ora #$FD ; SMC sta gPathname+1,x inx iny diff --git a/src/ui.main.keys.a b/src/ui.main.keys.a index cf7b4d3..83cdb0c 100644 --- a/src/ui.main.keys.a +++ b/src/ui.main.keys.a @@ -210,7 +210,7 @@ callback_next callback_options = OptionsDialog callback_play = LaunchInterpreterWithGame -callback_resume = LaunchInterpreterWithGame +callback_resume = ResumeDialog callback_boxart = DisplayArtwork callback_hints = LaunchInterpreterWithHints callback_versions = VersionsDialog diff --git a/src/ui.resume.a b/src/ui.resume.a index 0d0084e..c192bf8 100644 --- a/src/ui.resume.a +++ b/src/ui.resume.a @@ -67,10 +67,13 @@ ResumeDialog ldx #WGResetAll ; reset WeeGUI jsr WeeGUI + jsr LoadSavedGameInfo + ; TODO call ZINFO and calculate proper height ldy #3 sty kViewResumeFrame+3 ; frame top iny + sty .resumeVTAB ; top of first radio button sty kViewResumeOK+2 ; OK top iny iny @@ -88,22 +91,21 @@ ResumeDialog jsr CreateButton !word kViewResumeCancel + jsr okvs_iter + !word gSavedGamesStore + !word CreateResumeRadioCallback + jsr CreateRadio - !word kViewResumeSlot0 - jsr CreateRadio - !word kViewResumeSlot1 - jsr CreateRadio - !word kViewResumeSlot2 - jsr CreateRadio - !word kViewResumeSlot3 - jsr CreateRadio - !word kViewResumeSlot4 - jsr CreateRadio - !word kViewResumeSlot5 - jsr CreateRadio - !word kViewResumeSlot6 - jsr CreateRadio - !word kViewResumeSlot7 + !word kViewResumeNewGame + + ; TODO set proper radio button based on ZINFO + lda #ID_RESUME_NEWGAME + ldx #WGSelectView + jsr WeeGUI + lda #1 + sta PARAM0 + ldx #WGSetState + jsr WeeGUI ldx #WGDesktop ; paint background jsr WeeGUI @@ -113,10 +115,16 @@ ResumeDialog jsr PaintTitleBar ; paint top title bar - ldx #WGSelectView + ldx #WGSelectView ; select frame (required for print routines that follow) lda #ID_RESUME_FRAME jsr WeeGUI + lda #1 ; WeeGUI radio buttons are limited to 15 characters, so we + sta .resumeVTAB ; print the longer labels separately + jsr okvs_iter_values + !word gSavedGamesStore + !word PrintResumeLabelCallback + .runLoop ldx #WGPendingViewAction jsr WeeGUI @@ -129,6 +137,48 @@ ResumeDialog ;------------------------------------------------------------------------------ ; internal functions +CreateResumeRadioCallback +; X = index (0-based) into gSavedGamesStore, which is also the slot number +; A/Y points to caption (length-prefixed, 0 if slot is unused and should be skipped) +; + +STAY SAVE + lda (SAVE) + sta gResumeViewInUse+1,x ; mark whether this view is in use (hotkeys activate based on this array) + beq .createResumeRadioDone; 0-length key means this slot is unused, so we're done + txa + asl + tax + lda kViewResumeArray,x + ldy kViewResumeArray+1,x + +STAY + + +STAY SRC + ldy #2 + lda .resumeVTAB + sta (SRC),y ; radio button top = frame top + 1 + (2 * X) + jsr CreateRadio ; create radio button for this version (will print label later) ++ !word $FDFD ; SMC + inc .resumeVTAB + inc .resumeVTAB +.createResumeRadioDone + rts + +PrintResumeLabelCallback + +STAY SAVE + lda (SAVE) + beq .printVersionLabelDone + ldx #44 + lda SAVE + jsr CreateNullTerminatedString + jsr PrintAt + !byte 13 ; htab (constant) +.resumeVTAB + !byte $FD ; SMC + !word kNullTerminatedBuffer + inc .resumeVTAB + inc .resumeVTAB +.printVersionLabelDone + rts + HandleResumeKey ldx #.endkeys-.keys - cmp .keys,x @@ -174,91 +224,94 @@ kViewResumeCancel !word callback_resume_cancel ; callback !word kStringCancel ; caption +kViewResumeArray + !word kViewResumeSlot0 + !word kViewResumeSlot1 + !word kViewResumeSlot2 + !word kViewResumeSlot3 + !word kViewResumeSlot4 + !word kViewResumeSlot5 + !word kViewResumeSlot6 + !word kViewResumeSlot7 + kViewResumeSlot0 !byte ID_RESUME_SLOT0 ; view ID !byte 8 ; left !byte 4 ; top !word kStringSlot0 ; caption -kStringSlot0 - !text " Slot " - !byte $30 ; '0' inverse - !text ":",0 - kViewResumeSlot1 !byte ID_RESUME_SLOT1 ; view ID !byte 8 ; left !byte 6 ; top !word kStringSlot1 ; caption -kStringSlot1 - !text " Slot " - !byte $31 ; '1' inverse - !text ":",0 - kViewResumeSlot2 !byte ID_RESUME_SLOT2 ; view ID !byte 8 ; left !byte 8 ; top !word kStringSlot2 ; caption -kStringSlot2 - !text " Slot " - !byte $32 ; '2' inverse - !text ":",0 - kViewResumeSlot3 !byte ID_RESUME_SLOT3 ; view ID !byte 8 ; left !byte 10 ; top !word kStringSlot3 ; caption -kStringSlot3 - !text " Slot " - !byte $33 ; '3' inverse - !text ":",0 - kViewResumeSlot4 !byte ID_RESUME_SLOT4 ; view ID !byte 8 ; left !byte 12 ; top !word kStringSlot4 ; caption -kStringSlot4 - !text " Slot " - !byte $34 ; '4' inverse - !text ":",0 - kViewResumeSlot5 !byte ID_RESUME_SLOT5 ; view ID !byte 8 ; left !byte 14 ; top !word kStringSlot5 ; caption -kStringSlot5 - !text " Slot " - !byte $35 ; '5' inverse - !text ":",0 - kViewResumeSlot6 !byte ID_RESUME_SLOT6 ; view ID !byte 8 ; left !byte 16 ; top !word kStringSlot6 ; caption -kStringSlot6 - !text " Slot " - !byte $36 ; '6' inverse - !text ":",0 - kViewResumeSlot7 !byte ID_RESUME_SLOT7 ; view ID !byte 8 ; left - !byte 18 ; top + !byte 18 ; top !word kStringSlot7 ; caption +kViewResumeNewGame + !byte ID_RESUME_NEWGAME ; view ID + !byte 8 ; left + !byte 20 ; top + !word kStringNewGame ; caption + +kStringSlot0 + !text " Slot ",$30,":",0 + +kStringSlot1 + !text " Slot ",$31,":",0 + +kStringSlot2 + !text " Slot ",$32,":",0 + +kStringSlot3 + !text " Slot ",$33,":",0 + +kStringSlot4 + !text " Slot ",$34,":",0 + +kStringSlot5 + !text " Slot ",$35,":",0 + +kStringSlot6 + !text " Slot ",$36,":",0 + kStringSlot7 - !text " Slot " - !byte $37 ; '7' inverse - !text ":",0 + !text " Slot ",$37,":",0 + +kStringNewGame + !text " Start ",110,"ew game",0 } diff --git a/src/ui.versions.a b/src/ui.versions.a index 544712b..6301c5a 100644 --- a/src/ui.versions.a +++ b/src/ui.versions.a @@ -326,87 +326,78 @@ kViewVersions1 !byte 4 ; top !word kString1 ; caption -kString1 - !text " " - !byte $31,0 ; '1' inverse - kViewVersions2 !byte ID_VERSIONS_2 ; view ID !byte 8 ; left !byte 6 ; top !word kString2 ; caption -kString2 - !text " " - !byte $32,0 ; '2' inverse - kViewVersions3 !byte ID_VERSIONS_3 ; view ID !byte 8 ; left !byte 8 ; top !word kString3 ; caption -kString3 - !text " " - !byte $33,0 ; '3' inverse - kViewVersions4 !byte ID_VERSIONS_4 ; view ID !byte 8 ; left !byte 10 ; top !word kString4 ; caption -kString4 - !text " " - !byte $34,0 ; '4' inverse - kViewVersions5 !byte ID_VERSIONS_5 ; view ID !byte 8 ; left !byte 12 ; top !word kString5 ; caption -kString5 - !text " " - !byte $35,0 ; '5' inverse - kViewVersions6 !byte ID_VERSIONS_6 ; view ID !byte 8 ; left !byte 14 ; top !word kString6 ; caption -kString6 - !text " " - !byte $36,0 ; '6' inverse - kViewVersions7 !byte ID_VERSIONS_7 ; view ID !byte 8 ; left !byte 16 ; top !word kString7 ; caption -kString7 - !text " " - !byte $37,0 ; '7' inverse - kViewVersions8 !byte ID_VERSIONS_8 ; view ID !byte 8 ; left !byte 18 ; top !word kString8 ; caption -kString8 - !text " " - !byte $38,0 ; '8' inverse - kViewVersions9 !byte ID_VERSIONS_9 ; view ID !byte 8 ; left !byte 20 ; top !word kString9 ; caption +kString1 + !text " ",$31,0 + +kString2 + !text " ",$32,0 + +kString3 + !text " ",$33,0 + +kString4 + !text " ",$34,0 + +kString5 + !text " ",$35,0 + +kString6 + !text " ",$36,0 + +kString7 + !text " ",$37,0 + +kString8 + !text " ",$38,0 + kString9 - !text " " - !byte $39,0 ; '9' inverse + !text " ",$39,0 }