mirror of
https://github.com/hsoft/collapseos.git
synced 2024-09-07 08:58:45 +10:00
* addHL and subHL affect flags, and are smaller Most importantly, addHL and subHL now affect the flags as you would expect from a 16 bit addition/subtraction. This seems like it'd be preferred behaviour, however I realise any code relying on it not affecting flags would break. One byte saved in addHL, and two bytes saved in subHL. Due to the branching nature of the original code, it's difficult to compare speeds, subHL is either 1 or 6 cycles faster depending on branching, and addHL is between -1 and 3 cycles faster. If the chance of a carry is 50%, addHL is expected to be a cycle faster, but for a chance of carry below 25% (so a < 0x40) this will be up to a cycle slower. * Update core.asm * Reworked one use of addHL By essentially inlining both addHL and cpHLDE, 100 cycles are saved, but due to the registers not needing preserving, a byte is saved too. * Corrected spelling error in comment * Reworked second use of addHL 43 cycles saved, and no more addHL in critical loops. No bytes saved or used. * Fixed tabs and spacing, and made a comment clearer. * Clearer comments * Adopted push/pop notation
270 lines
6.2 KiB
NASM
270 lines
6.2 KiB
NASM
; 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
|
|
|