mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-23 23:48:05 +11:00
zasm: make symbol registry a bit more straightforward
Instead of strings of variable length driving the iteration of the registry, we do so through records that keep track of lengths and counts.
This commit is contained in:
parent
02c7eb0161
commit
1dec33e02a
@ -12,79 +12,67 @@
|
|||||||
|
|
||||||
; *** Constants ***
|
; *** Constants ***
|
||||||
; Maximum number of symbols we can have in the global and consts registry
|
; Maximum number of symbols we can have in the global and consts registry
|
||||||
.equ SYM_MAXCOUNT 0x100
|
.equ SYM_MAXCOUNT 0xff
|
||||||
; Maximum number of symbols we can have in the local registry
|
; Maximum number of symbols we can have in the local registry
|
||||||
.equ SYM_LOC_MAXCOUNT 0x40
|
.equ SYM_LOC_MAXCOUNT 0x40
|
||||||
|
; Size of each record in registry
|
||||||
|
.equ SYM_RECSIZE 3
|
||||||
|
|
||||||
; Size of the symbol name buffer size. This is a pool. There is no maximum name
|
; Size of the symbol name buffer size. This is a pool. There is no maximum name
|
||||||
; length for a single symbol, just a maximum size for the whole pool.
|
; length for a single symbol, just a maximum size for the whole pool.
|
||||||
; Global labels and consts have the same buf size
|
; Global labels and consts have the same buf size
|
||||||
.equ SYM_BUFSIZE 0x1000
|
.equ SYM_BUFSIZE 0x1000
|
||||||
|
.equ SYM_REGSIZE SYM_BUFSIZE+1+SYM_MAXCOUNT*SYM_RECSIZE
|
||||||
|
|
||||||
; Size of the names buffer for the local context registry
|
; Size of the names buffer for the local context registry
|
||||||
.equ SYM_LOC_BUFSIZE 0x200
|
.equ SYM_LOC_BUFSIZE 0x200
|
||||||
|
.equ SYM_LOC_REGSIZE SYM_LOC_BUFSIZE+1+SYM_LOC_MAXCOUNT*SYM_RECSIZE
|
||||||
|
|
||||||
; *** Variables ***
|
; *** Variables ***
|
||||||
|
; A registry has three parts: record count (byte) record list and names pool.
|
||||||
|
; A record is a 3 bytes structure:
|
||||||
|
; 1b - name length
|
||||||
|
; 2b - value associated to symbol
|
||||||
|
;
|
||||||
|
; We know we're at the end of the record list when we hit a 0-length one.
|
||||||
|
;
|
||||||
|
; The names pool is a list of strings, not null-terminated, associated with
|
||||||
|
; the value.
|
||||||
|
;
|
||||||
|
; It is assumed that the registry is aligned in memory in that order:
|
||||||
|
; names pool, rec count, reclist
|
||||||
|
|
||||||
; Global labels registry
|
; Global labels registry
|
||||||
|
.equ SYM_GLOB_REG SYM_RAMSTART
|
||||||
; Each symbol is mapped to a word value saved here.
|
.equ SYM_LOC_REG SYM_GLOB_REG+SYM_REGSIZE
|
||||||
.equ SYM_GLOB_VALUES SYM_RAMSTART
|
.equ SYM_CONST_REG SYM_LOC_REG+SYM_LOC_REGSIZE
|
||||||
|
.equ SYM_RAMEND SYM_CONST_REG+SYM_REGSIZE
|
||||||
; A list of symbol names separated by null characters. When we encounter a
|
|
||||||
; symbol name and want to get its value, we search the name here, retrieve the
|
|
||||||
; index of the name, then go get the value at that index in SYM_GLOB_VALUES.
|
|
||||||
.equ SYM_GLOB_NAMES SYM_GLOB_VALUES+SYM_MAXCOUNT*2
|
|
||||||
|
|
||||||
; Registry for local labels. Wiped out after each context change.
|
|
||||||
.equ SYM_LOC_VALUES SYM_GLOB_NAMES+SYM_BUFSIZE
|
|
||||||
.equ SYM_LOC_NAMES SYM_LOC_VALUES+SYM_LOC_MAXCOUNT*2
|
|
||||||
|
|
||||||
; Registry for constants
|
|
||||||
.equ SYM_CONST_VALUES SYM_LOC_NAMES+SYM_LOC_BUFSIZE
|
|
||||||
.equ SYM_CONST_NAMES SYM_CONST_VALUES+SYM_MAXCOUNT*2
|
|
||||||
.equ SYM_RAMEND SYM_CONST_NAMES+SYM_BUFSIZE
|
|
||||||
|
|
||||||
; *** Registries ***
|
; *** Registries ***
|
||||||
; A symbol registry is a 6 bytes record with points to names and values of
|
; A symbol registry is a 5 bytes record with points to the name pool then the
|
||||||
; one of the register.
|
; records list of the register and then the max record count.
|
||||||
; It's 3 pointers: names, names end, values
|
|
||||||
|
|
||||||
SYM_GLOBAL_REGISTRY:
|
SYM_GLOBAL_REGISTRY:
|
||||||
.dw SYM_GLOB_NAMES, SYM_GLOB_NAMES+SYM_BUFSIZE, SYM_GLOB_VALUES
|
.dw SYM_GLOB_REG, SYM_GLOB_REG+SYM_BUFSIZE
|
||||||
|
.db SYM_MAXCOUNT
|
||||||
|
|
||||||
SYM_LOCAL_REGISTRY:
|
SYM_LOCAL_REGISTRY:
|
||||||
.dw SYM_LOC_NAMES, SYM_LOC_NAMES+SYM_LOC_BUFSIZE, SYM_LOC_VALUES
|
.dw SYM_LOC_REG, SYM_LOC_REG+SYM_LOC_BUFSIZE
|
||||||
|
.db SYM_LOC_MAXCOUNT
|
||||||
|
|
||||||
SYM_CONST_REGISTRY:
|
SYM_CONST_REGISTRY:
|
||||||
.dw SYM_CONST_NAMES, SYM_CONST_NAMES+SYM_BUFSIZE, SYM_CONST_VALUES
|
.dw SYM_CONST_REG, SYM_CONST_REG+SYM_BUFSIZE
|
||||||
|
.db SYM_MAXCOUNT
|
||||||
|
|
||||||
; *** Code ***
|
; *** Code ***
|
||||||
|
|
||||||
; Assuming that HL points in to a symbol name list, advance HL to the beginning
|
|
||||||
; of the next symbol name except if (HL) is already zero, meaning we're at the
|
|
||||||
; end of the chain. In this case, do nothing.
|
|
||||||
; Sets Z if it succeeded, unset it if there is no next.
|
|
||||||
_symNext:
|
|
||||||
xor a
|
|
||||||
cp (hl)
|
|
||||||
jr nz, .do ; (HL) is not zero? we can advance.
|
|
||||||
; (HL) is zero? we're at the end of the chain.
|
|
||||||
call unsetZ
|
|
||||||
ret
|
|
||||||
.do:
|
|
||||||
; A is already 0
|
|
||||||
call findchar ; find next null char
|
|
||||||
; go to the char after it.
|
|
||||||
inc hl
|
|
||||||
cp a ; ensure Z
|
|
||||||
ret
|
|
||||||
|
|
||||||
symInit:
|
symInit:
|
||||||
xor a
|
ld ix, SYM_GLOBAL_REGISTRY
|
||||||
ld (SYM_GLOB_NAMES), a
|
call symClear
|
||||||
ld (SYM_LOC_NAMES), a
|
ld ix, SYM_LOCAL_REGISTRY
|
||||||
ld (SYM_CONST_NAMES), a
|
call symClear
|
||||||
; Continue to symSelectGlobalRegistry
|
ld ix, SYM_CONST_REGISTRY
|
||||||
|
jp symClear
|
||||||
|
|
||||||
; Sets Z according to whether label in (HL) is local (starts with a dot)
|
; Sets Z according to whether label in (HL) is local (starts with a dot)
|
||||||
symIsLabelLocal:
|
symIsLabelLocal:
|
||||||
@ -92,53 +80,6 @@ symIsLabelLocal:
|
|||||||
cp (hl)
|
cp (hl)
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Given a registry in (IX), place HL at the end of its names that is, at the
|
|
||||||
; point where we have two consecutive null chars and DE at the corresponding
|
|
||||||
; position in its values.
|
|
||||||
; If we're within bounds, Z is set, otherwise unset.
|
|
||||||
_symNamesEnd:
|
|
||||||
push iy
|
|
||||||
push bc
|
|
||||||
|
|
||||||
; IY --> values
|
|
||||||
ld l, (ix+4)
|
|
||||||
ld h, (ix+5)
|
|
||||||
push hl \ pop iy
|
|
||||||
; HL --> names
|
|
||||||
ld l, (ix)
|
|
||||||
ld h, (ix+1)
|
|
||||||
; DE --> names end
|
|
||||||
ld e, (ix+2)
|
|
||||||
ld d, (ix+3)
|
|
||||||
.loop:
|
|
||||||
call _symNext
|
|
||||||
jr nz, .success ; We've reached the end of the chain.
|
|
||||||
inc iy
|
|
||||||
inc iy
|
|
||||||
; Are we out of bounds name-wise?
|
|
||||||
call cpHLDE
|
|
||||||
jr nc, .outOfBounds ; HL >= DE
|
|
||||||
; are we out of bounds value-wise? check if IY == (IX)'s names
|
|
||||||
; Is is assumed that values are placed right before names
|
|
||||||
push hl
|
|
||||||
push iy \ pop bc
|
|
||||||
ld l, (ix)
|
|
||||||
ld h, (ix+1)
|
|
||||||
sbc hl, bc
|
|
||||||
pop hl
|
|
||||||
jr z, .outOfBounds ; IY == (IX)'s names
|
|
||||||
jr .loop
|
|
||||||
.outOfBounds:
|
|
||||||
call unsetZ
|
|
||||||
jr .end
|
|
||||||
.success:
|
|
||||||
push iy \ pop de ; our values pos goes in DE
|
|
||||||
cp a ; ensure Z
|
|
||||||
.end:
|
|
||||||
pop bc
|
|
||||||
pop iy
|
|
||||||
ret
|
|
||||||
|
|
||||||
symRegisterGlobal:
|
symRegisterGlobal:
|
||||||
push ix
|
push ix
|
||||||
ld ix, SYM_GLOBAL_REGISTRY
|
ld ix, SYM_GLOBAL_REGISTRY
|
||||||
@ -160,110 +101,115 @@ symRegisterConst:
|
|||||||
pop ix
|
pop ix
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Register label in (HL) (minus the ending ":") into the symbol registry and
|
; Register label in (HL) (minus the ending ":") into the symbol registry in IX
|
||||||
; set its value in that registry to DE.
|
; and set its value in that registry to the value specified in DE.
|
||||||
; If successful, Z is set and A is the symbol index. Otherwise, Z is unset and
|
; If successful, Z is set. Otherwise, Z is unset and A is an error code (ERR_*).
|
||||||
; A is an error code (ERR_*).
|
|
||||||
symRegister:
|
symRegister:
|
||||||
push hl ; --> lvl 1. it's the symbol to add
|
push hl ; --> lvl 1. it's the symbol to add
|
||||||
push de ; --> lvl 2. it's our value.
|
|
||||||
|
|
||||||
call _symFind
|
|
||||||
jr z, .duplicateError
|
|
||||||
|
|
||||||
|
call _symIsFull
|
||||||
|
jr z, .outOfMemory
|
||||||
|
|
||||||
; First, let's get our strlen
|
; First, let's get our strlen
|
||||||
call strlen
|
call strlen
|
||||||
ld c, a ; save that strlen for later
|
ld c, a ; save that strlen for later
|
||||||
|
|
||||||
call _symNamesEnd
|
call _symFind
|
||||||
jr nz, .outOfMemory
|
jr z, .duplicateError
|
||||||
|
|
||||||
; Is our new name going to make us go out of bounds?
|
; Is our new name going to make us go out of bounds?
|
||||||
push hl ; --> lvl 3
|
push hl ; --> lvl 2
|
||||||
push de ; --> lvl 4
|
push de ; --> lvl 3
|
||||||
ld e, (ix+2)
|
ld e, (ix+2) ; DE --> pointer to record list, which is also
|
||||||
ld d, (ix+3)
|
ld d, (ix+3) ; the end of names pool
|
||||||
; DE --> names end
|
; DE --> names end
|
||||||
ld a, c
|
ld a, c
|
||||||
call addHL
|
call addHL
|
||||||
call cpHLDE
|
call cpHLDE
|
||||||
pop de ; <-- lvl 4
|
pop de ; <-- lvl 3
|
||||||
pop hl ; <-- lvl 3
|
pop hl ; <-- lvl 2
|
||||||
jr nc, .outOfMemory ; HL >= DE
|
jr nc, .outOfMemory ; HL >= DE
|
||||||
|
|
||||||
; Success. At this point, we have:
|
; Success. At this point, we have:
|
||||||
; HL -> where we want to add the string
|
; HL -> where we want to add the string
|
||||||
; DE -> where the value goes
|
; IY -> target record where the value goes
|
||||||
; SP -> value to register
|
; DE -> value to register
|
||||||
; SP+2 -> string to register
|
; SP -> string to register
|
||||||
|
|
||||||
; Let's start with the value.
|
; Let's start with the record
|
||||||
push hl \ pop ix ; save HL for later
|
ld (iy), c ; strlen
|
||||||
pop hl ; <-- lvl 2. value to register
|
ld (iy+1), e
|
||||||
call writeHLinDE ; write value where it goes.
|
ld (iy+2), d
|
||||||
|
|
||||||
; Good! now, the string.
|
; Good! now, the string. Destination is in HL, source is in SP
|
||||||
|
ex de, hl ; dest is in DE
|
||||||
pop hl ; <-- lvl 1. string to register
|
pop hl ; <-- lvl 1. string to register
|
||||||
push ix \ pop de ; string destination
|
|
||||||
; Copy HL into DE until we reach null char
|
; Copy HL into DE until we reach null char
|
||||||
call strcpyM
|
call strcpyM
|
||||||
|
|
||||||
; We need to add a second null char to indicate the end of the name
|
; Last thing: increase record count
|
||||||
; list. DE is already correctly placed, A is already zero
|
ld l, (ix+2)
|
||||||
ld (de), a
|
ld h, (ix+3)
|
||||||
|
inc (hl)
|
||||||
cp a ; ensure Z
|
xor a ; sets Z
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.outOfMemory:
|
.outOfMemory:
|
||||||
ld a, ERR_OOM
|
|
||||||
call unsetZ
|
|
||||||
pop de ; <-- lvl 2
|
|
||||||
pop hl ; <-- lvl 1
|
pop hl ; <-- lvl 1
|
||||||
ret
|
ld a, ERR_OOM
|
||||||
|
jp unsetZ
|
||||||
|
|
||||||
.duplicateError:
|
.duplicateError:
|
||||||
pop de ; <-- lvl 2
|
|
||||||
pop hl ; <-- lvl 1
|
pop hl ; <-- lvl 1
|
||||||
ld a, ERR_DUPSYM
|
ld a, ERR_DUPSYM
|
||||||
jp unsetZ ; return
|
jp unsetZ ; return
|
||||||
|
|
||||||
; Assuming that IX points to a registry, find name HL in its names and make DE
|
; Assuming that IX points to a registry, find name HL in its names and make IY
|
||||||
; point to the corresponding entry in its values.
|
; point to the corresponding record. If it doesn't find anything, IY will
|
||||||
|
; conveniently point to the next record after the last, and HL to the next
|
||||||
|
; name insertion point.
|
||||||
; If we find something, Z is set, otherwise unset.
|
; If we find something, Z is set, otherwise unset.
|
||||||
_symFind:
|
_symFind:
|
||||||
push iy
|
push de
|
||||||
push hl
|
push bc
|
||||||
|
|
||||||
ex de, hl ; it's easier if HL is haystack and DE is
|
call strlen
|
||||||
; needle.
|
ld c, a ; save strlen
|
||||||
; IY --> values
|
|
||||||
ld l, (ix+4)
|
ex de, hl ; easier if needle is in DE
|
||||||
ld h, (ix+5)
|
|
||||||
|
; IY --> records
|
||||||
|
ld l, (ix+2)
|
||||||
|
ld h, (ix+3)
|
||||||
|
; first byte is count
|
||||||
|
ld b, (hl)
|
||||||
|
inc hl ; first record
|
||||||
push hl \ pop iy
|
push hl \ pop iy
|
||||||
; HL --> names
|
; HL --> names
|
||||||
ld l, (ix)
|
ld l, (ix)
|
||||||
ld h, (ix+1)
|
ld h, (ix+1)
|
||||||
|
; do we have an empty reclist?
|
||||||
|
xor a
|
||||||
|
cp b
|
||||||
|
jr z, .nothing ; zero count? nothing
|
||||||
.loop:
|
.loop:
|
||||||
call strcmp
|
ld a, (iy) ; name len
|
||||||
jr z, .match
|
cp c
|
||||||
|
jr nz, .skip ; different strlen, can't possibly match. skip
|
||||||
|
call strncmp
|
||||||
|
jr z, .end ; match! Z already set, IY and HL placed.
|
||||||
|
.skip:
|
||||||
; ok, next!
|
; ok, next!
|
||||||
call _symNext
|
ld a, (iy) ; name len again
|
||||||
jr nz, .nomatch ; end of the chain, nothing found
|
call addHL ; advance HL by A chars
|
||||||
inc iy
|
inc iy \ inc iy \ inc iy
|
||||||
inc iy
|
djnz .loop
|
||||||
jr .loop
|
; end of the chain, nothing found
|
||||||
.nomatch:
|
.nothing:
|
||||||
call unsetZ
|
call unsetZ
|
||||||
jr .end
|
|
||||||
.match:
|
|
||||||
push iy \ pop de
|
|
||||||
; DE has our result
|
|
||||||
cp a ; ensure Z
|
|
||||||
.end:
|
.end:
|
||||||
pop hl
|
pop bc
|
||||||
pop iy
|
pop de
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; For a given symbol name in (HL), find it in the appropriate symbol register
|
; For a given symbol name in (HL), find it in the appropriate symbol register
|
||||||
@ -276,16 +222,18 @@ symFindVal:
|
|||||||
call symIsLabelLocal
|
call symIsLabelLocal
|
||||||
jr z, .local
|
jr z, .local
|
||||||
; global. Let's try labels first, then consts
|
; global. Let's try labels first, then consts
|
||||||
|
push hl ; --> lvl 1. we'll need it again if not found.
|
||||||
ld ix, SYM_GLOBAL_REGISTRY
|
ld ix, SYM_GLOBAL_REGISTRY
|
||||||
call _symFind
|
call _symFind
|
||||||
|
pop hl ; <-- lvl 1
|
||||||
jr z, .found
|
jr z, .found
|
||||||
ld ix, SYM_CONST_REGISTRY
|
ld ix, SYM_CONST_REGISTRY
|
||||||
call _symFind
|
call _symFind
|
||||||
jr nz, .end
|
jr nz, .end
|
||||||
.found:
|
.found:
|
||||||
; Found! let's fetch value
|
; Found! let's fetch value
|
||||||
; DE is pointing to our result
|
ld e, (iy+1)
|
||||||
call intoDE
|
ld d, (iy+2)
|
||||||
jr .end
|
jr .end
|
||||||
.local:
|
.local:
|
||||||
ld ix, SYM_LOCAL_REGISTRY
|
ld ix, SYM_LOCAL_REGISTRY
|
||||||
@ -300,10 +248,24 @@ symFindVal:
|
|||||||
symClear:
|
symClear:
|
||||||
push af
|
push af
|
||||||
push hl
|
push hl
|
||||||
ld l, (ix)
|
ld l, (ix+2)
|
||||||
ld h, (ix+1)
|
ld h, (ix+3)
|
||||||
|
; HL --> reclist count
|
||||||
xor a
|
xor a
|
||||||
ld (hl), a
|
ld (hl), a
|
||||||
pop hl
|
pop hl
|
||||||
pop af
|
pop af
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
; Returns whether register in IX has reached its capacity.
|
||||||
|
; Sets Z if full, unset if not.
|
||||||
|
_symIsFull:
|
||||||
|
push hl
|
||||||
|
ld l, (ix+2)
|
||||||
|
ld h, (ix+3)
|
||||||
|
ld l, (hl) ; record count
|
||||||
|
ld a, (ix+4) ; max record count
|
||||||
|
cp l
|
||||||
|
pop hl
|
||||||
|
ret
|
||||||
|
|
||||||
|
@ -24,6 +24,12 @@ subDEFromHL:
|
|||||||
pop af
|
pop af
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
; make Z the opposite of what it is now
|
||||||
|
toggleZ:
|
||||||
|
jp z, unsetZ
|
||||||
|
cp a
|
||||||
|
ret
|
||||||
|
|
||||||
; Returns length of string at (HL) in A.
|
; Returns length of string at (HL) in A.
|
||||||
; Doesn't include null termination.
|
; Doesn't include null termination.
|
||||||
strlen:
|
strlen:
|
||||||
|
Binary file not shown.
@ -8,18 +8,14 @@ jp test
|
|||||||
.equ SYM_RAMSTART RAMSTART
|
.equ SYM_RAMSTART RAMSTART
|
||||||
#include "zasm/symbol.asm"
|
#include "zasm/symbol.asm"
|
||||||
|
|
||||||
; Pretend that we aren't in first pass
|
|
||||||
zasmIsFirstPass:
|
|
||||||
jp unsetZ
|
|
||||||
|
|
||||||
testNum: .db 1
|
testNum: .db 1
|
||||||
|
|
||||||
sFOO: .db "FOO", 0
|
sFOO: .db "FOO", 0
|
||||||
sFOOBAR: .db "FOOBAR", 0
|
sFOOBAR: .db "FOOBAR", 0
|
||||||
|
sOther: .db "Other", 0
|
||||||
|
|
||||||
test:
|
test:
|
||||||
ld hl, 0xffff
|
ld sp, 0xffff
|
||||||
ld sp, hl
|
|
||||||
|
|
||||||
; Check that we compare whole strings (a prefix will not match a longer
|
; Check that we compare whole strings (a prefix will not match a longer
|
||||||
; string).
|
; string).
|
||||||
@ -44,14 +40,18 @@ test:
|
|||||||
jp nz, fail
|
jp nz, fail
|
||||||
call nexttest
|
call nexttest
|
||||||
|
|
||||||
|
ld hl, sOther
|
||||||
|
call symFindVal
|
||||||
|
jp z, fail
|
||||||
|
call nexttest
|
||||||
|
|
||||||
; success
|
; success
|
||||||
xor a
|
xor a
|
||||||
halt
|
halt
|
||||||
|
|
||||||
nexttest:
|
nexttest:
|
||||||
ld a, (testNum)
|
ld hl, testNum
|
||||||
inc a
|
inc (hl)
|
||||||
ld (testNum), a
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
Loading…
Reference in New Issue
Block a user