;license:MIT ;(c) 2018-9 by 4am ; ; generic key/value text parser ; ; Public functions: ; - ParseKeyValueList ; - IncAndGetChar ; ;------------------------------------------------------------------------------ ; ParseKeyValueList ; parse buffer with KEY=VALUE lines of a text file into an okvs ; keys and values limited to 127 characters, which should be enough for anyone ; if '=' is missing, key is kept and value is a 0-length string ; blank lines are ignored ; '#' character at beginning of line is a comment, entire line is ignored ; '[' character at beginning of line exits the parser ; ; in: stack contains 5 bytes of parameters: ; +1 [word] handle to storage space for okvs ; +3 [word] handle to buffer containing contents of text file ; +5 [byte] max length for okvs records (or 0) ; out: all registers and flags clobbered ; $1F00..$1FFF clobbered ; $00/$01 clobbered ; $02/$03 clobbered ; $04/$05 has the address of the next available byte after the okvs ; $FE/$FF clobbered ;------------------------------------------------------------------------------ ParseKeyValueList +PARAMS_ON_STACK 5 +LDPARAM 1 ; not LDPARAMPTR, SetKeyPtr requires A/Y! +ST16 @store2 jsr SetKeyPtr ldy #5 lda (PARAM),y sta @maxLength ldy #$00 ; index into ($FE) pointing to current character @newkey ldx #$00 ; X = index into current key stx gValLen ; initialize value length (in case this line has no value) beq @emptyline ; always branches @skipLine ; skip to CR jsr IncAndGetChar cmp #$0A ; CR bne @skipLine @emptyline jsr IncAndGetChar cmp #$0A ; CR in first position (blank line) -> no key beq @emptyline cmp #$23 ; '#' starts a comment -> no key, skip to CR beq @skipLine cmp #$5B ; '[' ends the parsing beq .parseKeyValueDone bne @appendToKey @gatherKey jsr IncAndGetChar cmp #$0A ; CR -> finalize key, no value beq @finalizeKey cmp #$3D ; '=' -> finalize key, start gathering value beq @finalizeKey @appendToKey sta gKey,x inx bpl @gatherKey @finalizeKey stx gKeyLen cmp #$0A beq @storeInOKVS ldx #$00 ; now X = index into the current value @gatherValue jsr IncAndGetChar cmp #$0A ; CR -> finalize value beq @finalizeValue sta gVal,x inx bpl @gatherValue @finalizeValue stx gValLen @storeInOKVS tya pha ; okvs functions clobber everything but we need Y jsr okvs_append @store2 !word $FDFD ; SMC !word gKeyLen !word gValLen @maxLength !byte $FD ; SMC pla tay clc bcc @newkey ; always branches ;------------------------------------------------------------------------------ ; SetKeyPtr ; ; in: PARAM set ; out: okvs initialised ; ($FE) -> buffer ;------------------------------------------------------------------------------ SetKeyPtr jsr okvs_init ; reset key/value store +LDPARAMPTR 3, $FE ldy #0 lda ($FE),y tax bne + iny lda ($FE),y sta $FF dec $FF + dex stx $FE rts ;------------------------------------------------------------------------------ ; IncAndGetChar ; ; in: Y = index into ($FE) ; ($FE) -> buffer ; out: A contains next byte from buffer ; Y incremented ; $FF possibly incremented ;------------------------------------------------------------------------------ IncAndGetChar iny bne + inc $FF + lda ($FE),y cmp #$0D ; CR - hide it beq IncAndGetChar .parseKeyValueDone rts ;------------------------------------------------------------------------------ ; okvs_append ; ; in: stack contains 7 bytes of parameters: ; +1 [word] handle to storage space ; +3 [word] address of key ; +5 [word] address of value ; +7 [byte] maximum length of value (or 0 to fit) ; out: (new record count is not returned because no one cares) ; all registers clobbered ; $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 jsr GetStoreAddress ; PTR -> store ; Y = 0 lda (PTR),y ; A = number of keys in store sta WINDEX iny lda (PTR), y sta WINDEX+1 inc WINDEX bne + inc WINDEX+1 + dey lda WINDEX sta (PTR),y ; increment number of keys lda WINDEX+1 iny sta (PTR),y iny lda (PTR),y ; get address of next free space tax iny lda (PTR),y sta PTR+1 sta SAVE+1 stx PTR stx SAVE ; PTR -> new record ; SAVE -> new record jsr incptr ; PTR -> space for new key +LDPARAMPTR 3, SRC ; SRC -> new key to copy ldy #0 lda (SRC),y tay tax - lda (SRC),y ; copy new key sta (PTR),y dey cpy #$FF bne - ;;sec txa adc PTR ; update PTR to byte after copied key sta PTR bcc + inc PTR+1 + ; PTR -> space for new value +LDPARAMPTR 5, SRC ; SRC -> new value to copy iny ;;ldy #7 lda (PARAM),y ; get max length of value tax bne + tay lda (SRC),y ; no max, use actual length instead tax + inx tay - lda (SRC),y sta (PTR),y dey cpy #$FF bne - txa clc adc PTR tax lda PTR+1 adc #0 pha + jsr GetStoreAddress ; PTR -> store pla ldy #3 sta (PTR),y ; update next-free-space pointer in head dey txa sta (PTR),y sec sbc SAVE ldy #0 sta (SAVE),y ; set record length rts ;------------------------------------------------------------------------------ ; okvs_init ; ; in: A/Y = handle to storage space ; out: $00/$01 clobbered ; $02/$03 clobbered ; all registers clobbered ;------------------------------------------------------------------------------ okvs_init jsr GetStoreAddressFromAY ; PTR -> store ; Y = 0 tya sta (PTR),y ; set number of keys in store to 0 (word) iny sta (PTR),y iny ; set next-free-space pointer to store + 4 ldx PTR+1 lda PTR clc adc #$04 bcc + inx + sta (PTR),y iny txa sta (PTR),y rts