|
- ; Manages both constants and labels within a same namespace and registry.
- ;
- ; 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 do 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.
-
- ; *** Constants ***
- ; Size of each record in registry
- .equ SYM_RECSIZE 3
-
- .equ SYM_REGSIZE ZASM_REG_BUFSZ+1+ZASM_REG_MAXCNT*SYM_RECSIZE
-
- .equ SYM_LOC_REGSIZE ZASM_LREG_BUFSZ+1+ZASM_LREG_MAXCNT*SYM_RECSIZE
-
- ; *** 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
- .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
-
- ; *** Registries ***
- ; 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.
-
- SYM_GLOBAL_REGISTRY:
- .dw SYM_GLOB_REG, SYM_GLOB_REG+ZASM_REG_BUFSZ
- .db ZASM_REG_MAXCNT
-
- SYM_LOCAL_REGISTRY:
- .dw SYM_LOC_REG, SYM_LOC_REG+ZASM_LREG_BUFSZ
- .db ZASM_LREG_MAXCNT
-
- SYM_CONST_REGISTRY:
- .dw SYM_CONST_REG, SYM_CONST_REG+ZASM_REG_BUFSZ
- .db ZASM_REG_MAXCNT
-
- ; *** Code ***
-
- symInit:
- ld ix, SYM_GLOBAL_REGISTRY
- call symClear
- ld ix, SYM_LOCAL_REGISTRY
- call symClear
- ld ix, SYM_CONST_REGISTRY
- jp symClear
-
- ; Sets Z according to whether label in (HL) is local (starts with a dot)
- symIsLabelLocal:
- ld a, '.'
- cp (hl)
- ret
-
- 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
-
- symRegisterConst:
- push ix
- ld ix, SYM_CONST_REGISTRY
- call symRegister
- pop ix
- ret
-
- ; 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_*).
- symRegister:
- push hl ; --> lvl 1. it's the symbol to add
-
- call _symIsFull
- jr z, .outOfMemory
-
- ; First, let's get our strlen
- call strlen
- ld c, a ; save that strlen for later
-
- call _symFind
- jr z, .duplicateError
-
- ; Is our new name going to make us go out of bounds?
- push hl ; --> lvl 2
- push de ; --> lvl 3
- ld d, 0
- ld e, c
- add hl, de ; if carry set here, sbc will carry too
- ld e, (ix+2) ; DE --> pointer to record list, which is also
- ld d, (ix+3) ; the end of names pool
- ; DE --> names end
-
- sbc hl, de ; compares hl and de destructively
- pop de ; <-- lvl 3
- pop hl ; <-- lvl 2
- jr nc, .outOfMemory ; HL >= DE
-
- ; Success. At this point, we have:
- ; HL -> where we want to add the string
- ; IY -> target record where the value goes
- ; DE -> value to register
- ; SP -> string to register
-
- ; Let's start with the record
- ld (iy), c ; strlen
- ld (iy+1), e
- ld (iy+2), d
-
- ; 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
- ; Copy HL into DE until we reach null char
- call strcpyM
-
- ; Last thing: increase record count
- ld l, (ix+2)
- ld h, (ix+3)
- inc (hl)
- xor a ; sets Z
- ret
-
- .outOfMemory:
- pop hl ; <-- lvl 1
- ld a, ERR_OOM
- jp unsetZ
-
- .duplicateError:
- pop hl ; <-- lvl 1
- ld a, ERR_DUPSYM
- jp unsetZ ; return
-
- ; 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.
- ; If we find something, Z is set, otherwise unset.
- _symFind:
- push de
- push bc
-
- call strlen
- ld c, a ; save strlen
-
- ex de, hl ; easier if needle is in DE
-
- ; 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
- ; HL --> names
- ld l, (ix)
- ld h, (ix+1)
- ; do we have an empty reclist?
- xor a
- cp b
- jr z, .nothing ; zero count? nothing
- .loop:
- 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:
- ; ok, next!
-
- push de ; --> lvl 1
- ld de, 0x0003
- add iy, de ; faster and shorter than three inc's
- ld e, (iy-3) ; offset is also compulsory, so no extra bytes used
- ; (iy-3) holds the name length of the string just processed
- add hl, de ; advance HL by (iy-3) characters
- pop de ; <-- lvl 1
-
- djnz .loop
- ; end of the chain, nothing found
- .nothing:
- call unsetZ
- .end:
- pop bc
- pop de
- ret
-
- ; 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:
- push ix
- call symIsLabelLocal
- jr z, .local
- ; 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
- call _symFind
- pop hl ; <-- lvl 1
- jr z, .found
- ld ix, SYM_CONST_REGISTRY
- call _symFind
- jr nz, .end
- .found:
- ; Found! let's fetch value
- ld e, (iy+1)
- ld d, (iy+2)
- jr .end
- .local:
- ld ix, SYM_LOCAL_REGISTRY
- call _symFind
- jr z, .found
- ; continue to end
- .end:
- pop ix
- ret
-
- ; Clear registry at IX
- symClear:
- push af
- push hl
- ld l, (ix+2)
- ld h, (ix+3)
- ; HL --> reclist count
- xor a
- ld (hl), a
- pop hl
- pop af
- 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
|