diff --git a/src/memory.a b/src/memory.a index 08d4c7c..5c06750 100644 --- a/src/memory.a +++ b/src/memory.a @@ -9,3 +9,5 @@ kProDOSFileBuffer = $1C00 kSystemAddress = $2000 WGInit = $4000 +kGlobalPrefsStore = $8000 + diff --git a/src/okvs.a b/src/okvs.a new file mode 100644 index 0000000..d6effe0 --- /dev/null +++ b/src/okvs.a @@ -0,0 +1,224 @@ +;license:MIT +;(c) 2018 by 4am +; +; Ordered key/value store +; +; Public functions +; - okvs_init(address) +; - okvs_len(address) +; - okvs_append(address, key, value, max_len) +; - okvs_update(address, key, value) +; - okvs_get(address, key) +; - okvs_iter(address, callback) +; +; Used for global preferences, per-game options, and per-game version lists +; +; Call init() once. Call it again to reset the store to 0 keys. +; +; Keys are maintained in a singly linked list, so most functions are O(n). +; len() is O(1) though. +; +; Key count is stored as a byte, so a store can hold a maximum of 255 keys. +; +; Keys and values are length-prefixed strings (Pascal style), so max length +; of any single key or value is 255 bytes. +; +; Keys are case-sensitive. +; +; append() has a max_len argument to reserve more space for the value, in case +; you want to update it later. max_len is the total space to reserve, not the +; additional space. One exception: max_len can be 0, and it will be treated as +; length(value) at append time. update() always modifies the value in place. +; There is no range checking because this is assembly. + +; All functions take the starting address of the store's data buffer in +; memory, so there can be multiple independent stores at one time. append() +; will happily extend the store's data buffer without limit. There is no +; overflow protection because this is assembly. +; +; There is no sort() function. +; +; There is no delete() function. +; +; Keys can be duplicated, but get() will always return the one that was +; append()ed first. +; +; Structures: +; +; Store +; +0 length (byte) +; +1 Record +; ...Record... +; ...Record... +; +; Record +; +0 next-record pointer (word) +; +2 key length +; +3 key +; +K+2 value length (actual length, not max length) +; +K+3 value +; ... filler bytes up to value max length (set at append() time) + +!zone { +PARAM = $00 +PTR = $02 +SRC = $04 + +;------------------------------------------------------------------------------ +; okvs_init +; +; in: A = store start address [lo] +; Y = store start address [hi] +; out: $02/$03 clobbered +;------------------------------------------------------------------------------ +okvs_init + sta PTR + sty PTR+1 + lda #0 + sta (PTR) ; set number of keys + rts + +;------------------------------------------------------------------------------ +; okvs_len +; +; in: A = store start address [lo] +; Y = store start address [hi] +; out: A contains number of keys in this store +; $02/$03 clobbered +;------------------------------------------------------------------------------ +okvs_len + sta PTR + sty PTR+1 + lda (PTR) + rts + +;------------------------------------------------------------------------------ +; okvs_append +; +; in: A = store start address [lo] +; Y = store start address [hi] +; stack contains 5 bytes of parameters: +; +1 [word] address of key +; +3 [word] address of value +; +5 [byte] maximum length of value (or 0 to fit) +; out: all registers clobbered +; $00/$01 clobbered +; $02/$03 clobbered +;------------------------------------------------------------------------------ +okvs_append + sta PTR + sty PTR+1 + + pla ; get parameters address and update stack + sta PARAM + plx + stx PARAM+1 + lda #$05 + clc + adc PARAM + bcc + + inx ++ phx + pha + + lda (PTR) ; increment number of keys + inc + sta (PTR) + tax + jsr incptr ; PTR -> first record +- dex + beq + + jsr followptr ; PTR -> next record + bra - ++ ; PTR -> start of new record + lda PTR ; save PTR on stack + pha + lda PTR+1 + pha + jsr incptr + jsr incptr ; PTR -> start of new key + ldy #1 + lda (PARAM),y ; get source address of new key to copy + sta SRC + iny + lda (PARAM),y + sta SRC+1 + lda (SRC) + inc + sta .keylen + tay +- dey ; copy key + lda (SRC),y + sta (PTR),y + cpy #$FF + bne - + + lda PTR ; update PTR to byte after copied key + clc +.keylen=*+1 + adc #$FD ; set at runtime + sta PTR + bcc + + inc PTR+1 ++ ; PTR -> start of new value + ldy #3 + lda (PARAM),y ; get source address of new value + sta SRC + iny + lda (PARAM),y + sta SRC+1 + iny + lda (PARAM),y ; get max length of value + bne + + lda (SRC) ; no max, use actual length instead + inc ++ sta .valuelen + tay +- dey + lda (SRC),y + sta (PTR),y + cpy #$FF + bne - + + lda PTR + clc +.valuelen=*+1 + adc #$FD ; set at runtime + sta SRC + bcc + + inc PTR+1 ++ lda PTR+1 + sta SRC+1 ; SRC -> byte after this record + pla + sta PTR+1 + pla + sta PTR ; PTR -> start of this record + lda SRC ; update next-record pointer + sta (PTR) + ldy #1 + lda SRC+1 + sta (PTR),y + rts + +incptr +; preserves all registers + phx + ldx PTR + inx + stx PTR + bne + + inc PTR+1 ++ plx + rts + +followptr +; preserves X + lda (PTR) + pha + ldy #1 + lda (PTR),y + sta PTR+1 + pla + sta PTR + rts +} diff --git a/src/pitchdark.a b/src/pitchdark.a index a9f4455..71345ca 100644 --- a/src/pitchdark.a +++ b/src/pitchdark.a @@ -13,6 +13,7 @@ !source "src/WeeGUI_MLI.s" !source "src/memory.a" + !source "src/okvs.a" !source "src/prodos.a" !source "src/ramdisk.a" !source "src/path.a" diff --git a/src/prefs.a b/src/prefs.a index 4e8c3d0..7b406dd 100644 --- a/src/prefs.a +++ b/src/prefs.a @@ -47,6 +47,31 @@ LoadGlobalPreferences lda #kDefaultGame sta gCurrentGame + lda #kGlobalPrefsStore + jsr okvs_init + + lda #kGlobalPrefsStore + jsr okvs_append + !word .force40 + !word .debug0 + !byte 0 + + lda #kGlobalPrefsStore + jsr okvs_append + !word .forceupper + !word .debug0 + !byte 0 + + lda #kGlobalPrefsStore + jsr okvs_append + !word .sort + !word .debugsortvalue + !byte 0 + bra .exit ; TODO jsr LoadFile ; load prefs file at $2000 @@ -66,11 +91,6 @@ SaveGlobalPreferences !byte 15 !raw "PITCH.DARK.CONF" -.globalPrefsKeys - !word .force40 - !word .forceupper - !word .sort - !word .lastplayed .force40 !byte 14 !raw "FORCE40COLUMNS" @@ -83,4 +103,13 @@ SaveGlobalPreferences .lastplayed !byte 10 !raw "LASTPLAYED" + +.debug0 + !byte 1 + !raw "0" + +.debugsortvalue + !byte 4 + !raw "NAME" + }