2019-05-10 11:21:08 +10:00
|
|
|
; Manages both constants and labels within a same namespace and registry.
|
2019-05-14 06:53:52 +10:00
|
|
|
;
|
2019-05-16 10:07:21 +10:00
|
|
|
; Local Labels
|
|
|
|
;
|
|
|
|
; Local labels during the "official" first pass are ignored. To register them
|
|
|
|
; in the global registry during that pass would be wasteful in terms of memory.
|
|
|
|
;
|
|
|
|
; What we don instead is set up a separate register for them and have a "second
|
|
|
|
; first pass" whenever we encounter a new context. That is, we wipe the local
|
|
|
|
; registry, parse the code until the next global symbol (or EOF), then rewind
|
|
|
|
; and continue second pass as usual.
|
2019-05-10 11:21:08 +10:00
|
|
|
|
|
|
|
; *** Constants ***
|
2019-07-23 12:23:57 +10:00
|
|
|
; Maximum number of symbols we can have in the global and consts registry
|
2019-07-24 05:21:42 +10:00
|
|
|
.equ SYM_MAXCOUNT 0xff
|
2019-05-19 23:54:42 +10:00
|
|
|
; Maximum number of symbols we can have in the local registry
|
|
|
|
.equ SYM_LOC_MAXCOUNT 0x40
|
2019-07-24 05:21:42 +10:00
|
|
|
; Size of each record in registry
|
|
|
|
.equ SYM_RECSIZE 3
|
2019-05-10 11:21:08 +10:00
|
|
|
|
|
|
|
; 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.
|
2019-07-23 12:23:57 +10:00
|
|
|
; Global labels and consts have the same buf size
|
|
|
|
.equ SYM_BUFSIZE 0x1000
|
2019-07-24 05:21:42 +10:00
|
|
|
.equ SYM_REGSIZE SYM_BUFSIZE+1+SYM_MAXCOUNT*SYM_RECSIZE
|
2019-05-16 10:07:21 +10:00
|
|
|
|
|
|
|
; Size of the names buffer for the local context registry
|
|
|
|
.equ SYM_LOC_BUFSIZE 0x200
|
2019-07-24 05:21:42 +10:00
|
|
|
.equ SYM_LOC_REGSIZE SYM_LOC_BUFSIZE+1+SYM_LOC_MAXCOUNT*SYM_RECSIZE
|
2019-05-10 11:21:08 +10:00
|
|
|
|
|
|
|
; *** Variables ***
|
2019-07-24 05:21:42 +10:00
|
|
|
; 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
|
2019-05-14 10:23:10 +10:00
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
; Global labels registry
|
|
|
|
.equ SYM_GLOB_REG SYM_RAMSTART
|
|
|
|
.equ SYM_LOC_REG SYM_GLOB_REG+SYM_REGSIZE
|
|
|
|
.equ SYM_CONST_REG SYM_LOC_REG+SYM_LOC_REGSIZE
|
|
|
|
.equ SYM_RAMEND SYM_CONST_REG+SYM_REGSIZE
|
2019-05-10 11:21:08 +10:00
|
|
|
|
2019-07-23 03:37:57 +10:00
|
|
|
; *** Registries ***
|
2019-07-24 05:21:42 +10:00
|
|
|
; A symbol registry is a 5 bytes record with points to the name pool then the
|
|
|
|
; records list of the register and then the max record count.
|
2019-07-23 03:37:57 +10:00
|
|
|
|
|
|
|
SYM_GLOBAL_REGISTRY:
|
2019-07-24 05:21:42 +10:00
|
|
|
.dw SYM_GLOB_REG, SYM_GLOB_REG+SYM_BUFSIZE
|
|
|
|
.db SYM_MAXCOUNT
|
2019-07-23 03:37:57 +10:00
|
|
|
|
|
|
|
SYM_LOCAL_REGISTRY:
|
2019-07-24 05:21:42 +10:00
|
|
|
.dw SYM_LOC_REG, SYM_LOC_REG+SYM_LOC_BUFSIZE
|
|
|
|
.db SYM_LOC_MAXCOUNT
|
2019-07-23 12:23:57 +10:00
|
|
|
|
|
|
|
SYM_CONST_REGISTRY:
|
2019-07-24 05:21:42 +10:00
|
|
|
.dw SYM_CONST_REG, SYM_CONST_REG+SYM_BUFSIZE
|
|
|
|
.db SYM_MAXCOUNT
|
2019-07-23 12:23:57 +10:00
|
|
|
|
2019-05-10 11:21:08 +10:00
|
|
|
; *** Code ***
|
|
|
|
|
2019-05-14 10:23:10 +10:00
|
|
|
symInit:
|
2019-07-24 05:21:42 +10:00
|
|
|
ld ix, SYM_GLOBAL_REGISTRY
|
|
|
|
call symClear
|
|
|
|
ld ix, SYM_LOCAL_REGISTRY
|
|
|
|
call symClear
|
|
|
|
ld ix, SYM_CONST_REGISTRY
|
|
|
|
jp symClear
|
2019-05-16 10:07:21 +10:00
|
|
|
|
2019-05-14 10:23:10 +10:00
|
|
|
; Sets Z according to whether label in (HL) is local (starts with a dot)
|
|
|
|
symIsLabelLocal:
|
|
|
|
ld a, '.'
|
|
|
|
cp (hl)
|
|
|
|
ret
|
|
|
|
|
2019-07-23 05:59:47 +10:00
|
|
|
symRegisterGlobal:
|
|
|
|
push ix
|
|
|
|
ld ix, SYM_GLOBAL_REGISTRY
|
|
|
|
call symRegister
|
|
|
|
pop ix
|
|
|
|
ret
|
|
|
|
|
|
|
|
symRegisterLocal:
|
|
|
|
push ix
|
|
|
|
ld ix, SYM_LOCAL_REGISTRY
|
|
|
|
call symRegister
|
|
|
|
pop ix
|
|
|
|
ret
|
|
|
|
|
2019-07-23 12:23:57 +10:00
|
|
|
symRegisterConst:
|
|
|
|
push ix
|
|
|
|
ld ix, SYM_CONST_REGISTRY
|
|
|
|
call symRegister
|
|
|
|
pop ix
|
|
|
|
ret
|
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
; Register label in (HL) (minus the ending ":") into the symbol registry in IX
|
|
|
|
; and set its value in that registry to the value specified in DE.
|
|
|
|
; If successful, Z is set. Otherwise, Z is unset and A is an error code (ERR_*).
|
2019-05-10 11:21:08 +10:00
|
|
|
symRegister:
|
2019-07-23 05:59:47 +10:00
|
|
|
push hl ; --> lvl 1. it's the symbol to add
|
2019-07-23 06:34:40 +10:00
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
call _symIsFull
|
|
|
|
jr z, .outOfMemory
|
2019-05-20 03:22:14 +10:00
|
|
|
|
2019-05-10 11:21:08 +10:00
|
|
|
; First, let's get our strlen
|
|
|
|
call strlen
|
|
|
|
ld c, a ; save that strlen for later
|
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
call _symFind
|
|
|
|
jr z, .duplicateError
|
2019-05-19 23:06:24 +10:00
|
|
|
|
2019-05-10 11:21:08 +10:00
|
|
|
; Is our new name going to make us go out of bounds?
|
2019-07-24 05:21:42 +10:00
|
|
|
push hl ; --> lvl 2
|
|
|
|
push de ; --> lvl 3
|
|
|
|
ld e, (ix+2) ; DE --> pointer to record list, which is also
|
|
|
|
ld d, (ix+3) ; the end of names pool
|
2019-07-23 03:37:57 +10:00
|
|
|
; DE --> names end
|
|
|
|
ld a, c
|
|
|
|
call addHL
|
|
|
|
call cpHLDE
|
2019-07-24 05:21:42 +10:00
|
|
|
pop de ; <-- lvl 3
|
|
|
|
pop hl ; <-- lvl 2
|
2019-05-28 07:45:05 +10:00
|
|
|
jr nc, .outOfMemory ; HL >= DE
|
2019-05-10 11:21:08 +10:00
|
|
|
|
2019-05-19 23:06:24 +10:00
|
|
|
; Success. At this point, we have:
|
|
|
|
; HL -> where we want to add the string
|
2019-07-24 05:21:42 +10:00
|
|
|
; IY -> target record where the value goes
|
|
|
|
; DE -> value to register
|
|
|
|
; SP -> string to register
|
2019-05-19 23:06:24 +10:00
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
; Let's start with the record
|
|
|
|
ld (iy), c ; strlen
|
|
|
|
ld (iy+1), e
|
|
|
|
ld (iy+2), d
|
2019-05-19 23:06:24 +10:00
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
; Good! now, the string. Destination is in HL, source is in SP
|
|
|
|
ex de, hl ; dest is in DE
|
2019-07-23 05:59:47 +10:00
|
|
|
pop hl ; <-- lvl 1. string to register
|
2019-05-10 11:21:08 +10:00
|
|
|
; Copy HL into DE until we reach null char
|
2019-05-19 23:06:24 +10:00
|
|
|
call strcpyM
|
2019-05-10 11:21:08 +10:00
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
; Last thing: increase record count
|
|
|
|
ld l, (ix+2)
|
|
|
|
ld h, (ix+3)
|
|
|
|
inc (hl)
|
|
|
|
xor a ; sets Z
|
2019-05-19 23:06:24 +10:00
|
|
|
ret
|
2019-05-10 11:21:08 +10:00
|
|
|
|
2019-05-28 07:45:05 +10:00
|
|
|
.outOfMemory:
|
2019-07-23 05:59:47 +10:00
|
|
|
pop hl ; <-- lvl 1
|
2019-07-24 05:21:42 +10:00
|
|
|
ld a, ERR_OOM
|
|
|
|
jp unsetZ
|
2019-05-10 11:21:08 +10:00
|
|
|
|
2019-05-28 07:45:05 +10:00
|
|
|
.duplicateError:
|
2019-07-23 06:34:40 +10:00
|
|
|
pop hl ; <-- lvl 1
|
2019-05-28 07:45:05 +10:00
|
|
|
ld a, ERR_DUPSYM
|
|
|
|
jp unsetZ ; return
|
2019-05-20 03:22:14 +10:00
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
; Assuming that IX points to a registry, find name HL in its names and make IY
|
|
|
|
; 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.
|
2019-05-10 11:21:08 +10:00
|
|
|
; If we find something, Z is set, otherwise unset.
|
2019-07-23 03:37:57 +10:00
|
|
|
_symFind:
|
2019-07-24 05:21:42 +10:00
|
|
|
push de
|
|
|
|
push bc
|
|
|
|
|
|
|
|
call strlen
|
|
|
|
ld c, a ; save strlen
|
|
|
|
|
|
|
|
ex de, hl ; easier if needle is in DE
|
2019-05-10 11:21:08 +10:00
|
|
|
|
2019-07-24 05:21:42 +10:00
|
|
|
; IY --> records
|
|
|
|
ld l, (ix+2)
|
|
|
|
ld h, (ix+3)
|
|
|
|
; first byte is count
|
|
|
|
ld b, (hl)
|
|
|
|
inc hl ; first record
|
2019-07-23 03:37:57 +10:00
|
|
|
push hl \ pop iy
|
|
|
|
; HL --> names
|
|
|
|
ld l, (ix)
|
|
|
|
ld h, (ix+1)
|
2019-07-24 05:21:42 +10:00
|
|
|
; do we have an empty reclist?
|
|
|
|
xor a
|
|
|
|
cp b
|
|
|
|
jr z, .nothing ; zero count? nothing
|
2019-05-10 11:21:08 +10:00
|
|
|
.loop:
|
2019-07-24 05:21:42 +10:00
|
|
|
ld a, (iy) ; name len
|
|
|
|
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:
|
2019-05-10 11:21:08 +10:00
|
|
|
; ok, next!
|
2019-07-24 05:21:42 +10:00
|
|
|
ld a, (iy) ; name len again
|
|
|
|
call addHL ; advance HL by A chars
|
|
|
|
inc iy \ inc iy \ inc iy
|
|
|
|
djnz .loop
|
|
|
|
; end of the chain, nothing found
|
|
|
|
.nothing:
|
2019-05-17 23:50:11 +10:00
|
|
|
call unsetZ
|
2019-05-10 11:21:08 +10:00
|
|
|
.end:
|
2019-07-24 05:21:42 +10:00
|
|
|
pop bc
|
|
|
|
pop de
|
2019-05-10 11:21:08 +10:00
|
|
|
ret
|
|
|
|
|
2019-07-21 08:07:52 +10:00
|
|
|
; For a given symbol name in (HL), find it in the appropriate symbol register
|
|
|
|
; and return its value in DE. If (HL) is a local label, the local register is
|
|
|
|
; searched. Otherwise, the global one. It is assumed that this routine is
|
|
|
|
; always called when the global registry is selected. Therefore, we always
|
|
|
|
; reselect it afterwards.
|
|
|
|
symFindVal:
|
2019-07-23 03:37:57 +10:00
|
|
|
push ix
|
2019-07-21 08:07:52 +10:00
|
|
|
call symIsLabelLocal
|
2019-07-23 12:23:57 +10:00
|
|
|
jr z, .local
|
|
|
|
; global. Let's try labels first, then consts
|
2019-07-24 05:21:42 +10:00
|
|
|
push hl ; --> lvl 1. we'll need it again if not found.
|
2019-07-23 12:23:57 +10:00
|
|
|
ld ix, SYM_GLOBAL_REGISTRY
|
|
|
|
call _symFind
|
2019-07-24 05:21:42 +10:00
|
|
|
pop hl ; <-- lvl 1
|
2019-07-23 12:23:57 +10:00
|
|
|
jr z, .found
|
|
|
|
ld ix, SYM_CONST_REGISTRY
|
2019-07-23 03:37:57 +10:00
|
|
|
call _symFind
|
2019-07-21 08:07:52 +10:00
|
|
|
jr nz, .end
|
2019-07-23 12:23:57 +10:00
|
|
|
.found:
|
2019-07-21 08:07:52 +10:00
|
|
|
; Found! let's fetch value
|
2019-07-24 05:21:42 +10:00
|
|
|
ld e, (iy+1)
|
|
|
|
ld d, (iy+2)
|
2019-07-23 12:23:57 +10:00
|
|
|
jr .end
|
|
|
|
.local:
|
|
|
|
ld ix, SYM_LOCAL_REGISTRY
|
|
|
|
call _symFind
|
|
|
|
jr z, .found
|
|
|
|
; continue to end
|
2019-07-21 08:07:52 +10:00
|
|
|
.end:
|
2019-07-23 03:37:57 +10:00
|
|
|
pop ix
|
|
|
|
ret
|
2019-07-23 12:49:43 +10:00
|
|
|
|
|
|
|
; Clear registry at IX
|
|
|
|
symClear:
|
|
|
|
push af
|
|
|
|
push hl
|
2019-07-24 05:21:42 +10:00
|
|
|
ld l, (ix+2)
|
|
|
|
ld h, (ix+3)
|
|
|
|
; HL --> reclist count
|
2019-07-23 12:49:43 +10:00
|
|
|
xor a
|
|
|
|
ld (hl), a
|
|
|
|
pop hl
|
|
|
|
pop af
|
|
|
|
ret
|
2019-07-24 05:21:42 +10:00
|
|
|
|
|
|
|
; 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
|
|
|
|
|