ordered key-value store WIP

This commit is contained in:
4am 2018-03-24 12:04:09 -04:00
parent d87ece2fb9
commit 727886cb58
4 changed files with 261 additions and 5 deletions

View File

@ -9,3 +9,5 @@
kProDOSFileBuffer = $1C00
kSystemAddress = $2000
WGInit = $4000
kGlobalPrefsStore = $8000

224
src/okvs.a Normal file
View File

@ -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
}

View File

@ -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"

View File

@ -47,6 +47,31 @@ LoadGlobalPreferences
lda #kDefaultGame
sta gCurrentGame
lda #<kGlobalPrefsStore
ldy #>kGlobalPrefsStore
jsr okvs_init
lda #<kGlobalPrefsStore
ldy #>kGlobalPrefsStore
jsr okvs_append
!word .force40
!word .debug0
!byte 0
lda #<kGlobalPrefsStore
ldy #>kGlobalPrefsStore
jsr okvs_append
!word .forceupper
!word .debug0
!byte 0
lda #<kGlobalPrefsStore
ldy #>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"
}