diff --git a/apps/zasm/const.asm b/apps/zasm/const.asm index 2f9db4e..dfddfd6 100644 --- a/apps/zasm/const.asm +++ b/apps/zasm/const.asm @@ -13,3 +13,9 @@ .equ ERR_OVFL 0x04 .equ ERR_FILENOTFOUND 0x05 + +; Duplicate symbol +.equ ERR_DUPSYM 0x06 + +; Out of memory +.equ ERR_OOM 0x07 diff --git a/apps/zasm/directive.asm b/apps/zasm/directive.asm index 99d7b88..181490d 100644 --- a/apps/zasm/directive.asm +++ b/apps/zasm/directive.asm @@ -104,6 +104,8 @@ handleDW: ret handleEQU: + call zasmIsLocalPass ; Are we in local pass? Then ignore all .equ. + jr z, .skip ; they mess up duplicate symbol detection. push hl push de push bc @@ -124,8 +126,7 @@ handleEQU: jr nz, .badarg ld hl, DIREC_SCRATCHPAD push ix \ pop de - call symRegister ; TODO: handle duplicate symbol error, OOM, etc. - cp a ; ensure Z + call symRegister ; A and Z set jr .end .badfmt: ld a, ERR_BAD_FMT @@ -139,6 +140,10 @@ handleEQU: pop de pop hl ret +.skip: + ; consume args and return + call readWord + jp readWord handleORG: call readWord diff --git a/apps/zasm/main.asm b/apps/zasm/main.asm index 4b3f1aa..34684be 100644 --- a/apps/zasm/main.asm +++ b/apps/zasm/main.asm @@ -120,8 +120,7 @@ _parseInstr: _parseDirec: ld a, c ; D_* - call parseDirective - ret + jp parseDirective _parseLabel: ; The string in (scratchpad) is a label with its trailing ':' removed. diff --git a/apps/zasm/symbol.asm b/apps/zasm/symbol.asm index 2b09336..69e483a 100644 --- a/apps/zasm/symbol.asm +++ b/apps/zasm/symbol.asm @@ -11,11 +11,6 @@ ; and continue second pass as usual. ; *** Constants *** -; Duplicate symbol in registry -.equ SYM_ERR_DUPLICATE 0x01 -; Symbol registry buffer is full -.equ SYM_ERR_FULLBUF 0x02 - ; Maximum number of symbols we can have in the global registry .equ SYM_MAXCOUNT 0x200 ; Maximum number of symbols we can have in the local registry @@ -153,7 +148,7 @@ symNamesEnd: ; Register label in (HL) (minus the ending ":") into the symbol registry and ; set its value in that registry to DE. ; If successful, Z is set and A is the symbol index. Otherwise, Z is unset and -; A is an error code (SYM_ERR_*). +; A is an error code (ERR_*). symRegister: call symFind jr z, .alreadyThere @@ -167,7 +162,7 @@ symRegister: ld c, a ; save that strlen for later call symNamesEnd - jr nz, .error + jr nz, .outOfMemory ; Is our new name going to make us go out of bounds? push hl @@ -178,7 +173,7 @@ symRegister: call cpHLDE pop de pop hl - jr nc, .error ; HL >= DE + jr nc, .outOfMemory ; HL >= DE ; Success. At this point, we have: ; HL -> where we want to add the string @@ -201,16 +196,37 @@ symRegister: ; list. DE is already correctly placed, A is already zero ld (de), a + cp a ; ensure Z ; Nothing to pop. We've already popped our stack in the lines above. ret -.error: +.outOfMemory: + ld a, ERR_OOM call unsetZ pop de pop hl ret .alreadyThere: + ; We are in a tricky situation with regards to our handling of the + ; duplicate symbol error. Normally, it should be straightforward: We + ; only register labels during first pass and evaluate constants during + ; the second. Easy. + ; We can *almost* do that... but we have ".org". .org affects label + ; values and supports expressions, which means that we have to evaluate + ; constants during first pass. But because we can possibly have forward + ; references in ".equ", some constants are going to have a bad value. + ; Therefore, we really can't evaluate all constants during the first + ; pass. + ; With this situation, how do you manage detection of duplicate symbols? + ; By limiting the "duplicate error" condition to the first pass. During, + ; first pass, sure, we don't have our proper values, but we have all our + ; symbol names. So, if we end up in .alreadyThere during first pass, + ; then it's an error condition. If it's not first pass, then we need + ; to update our value. + call zasmIsFirstPass + jr z, .duplicateError + ; Second pass. Don't error out, just update value push hl ld hl, (SYM_CTX_PTR) ex de, hl @@ -218,6 +234,9 @@ symRegister: pop hl cp a ; ensure Z ret +.duplicateError: + ld a, ERR_DUPSYM + jp unsetZ ; return ; Select global or local registry according to label name in (HL) symSelect: @@ -246,7 +265,6 @@ symFind: inc ix inc ix jr .loop - ; exhausted djnz? no match .nomatch: call unsetZ jr .end diff --git a/tools/tests/unit/test_expr.asm b/tools/tests/unit/test_expr.asm index eec8984..fd6b0f3 100644 --- a/tools/tests/unit/test_expr.asm +++ b/tools/tests/unit/test_expr.asm @@ -4,6 +4,7 @@ jp test #include "core.asm" #include "parse.asm" #include "zasm/util.asm" +#include "zasm/const.asm" #include "zasm/parse.asm" .equ SYM_RAMSTART RAMSTART #include "zasm/symbol.asm" diff --git a/tools/tests/unit/test_symbol.asm b/tools/tests/unit/test_symbol.asm index a2aefdb..03bd791 100644 --- a/tools/tests/unit/test_symbol.asm +++ b/tools/tests/unit/test_symbol.asm @@ -3,9 +3,14 @@ jp test #include "core.asm" #include "zasm/util.asm" +#include "zasm/const.asm" .equ SYM_RAMSTART RAMSTART #include "zasm/symbol.asm" +; Pretend that we aren't in first pass +zasmIsFirstPass: + jp unsetZ + testNum: .db 1 sFOO: .db "FOO", 0 diff --git a/tools/tests/zasm/errtests.sh b/tools/tests/zasm/errtests.sh index b48d8cf..834f8a0 100755 --- a/tools/tests/zasm/errtests.sh +++ b/tools/tests/zasm/errtests.sh @@ -16,6 +16,24 @@ chkerr() { fi } +chkoom() { + echo "Trying OOM error..." + local s="" + # 300 x 27-29 bytes > 8192 bytes. Large enough to smash the pool. + for i in {1..300}; do + s+=".equ abcdefghijklmnopqrstuvwxyz$i 42" + s+=$'\n' + done + ${ZASM} <<< "$s" > /dev/null + local res=$? + if [[ $res == 7 ]]; then + echo "Good!" + else + echo "$res != 7" + exit 1 + fi +} + chkerr "foo" 1 chkerr "ld a, foo" 2 chkerr "ld a, hl" 2 @@ -37,3 +55,5 @@ chkerr "#inc foo" 3 chkerr "ld a, 0x100" 4 chkerr ".db 0x100" 4 chkerr "#inc \"doesnotexist\"" 5 +chkerr ".equ foo 42 \\ .equ foo 42" 6 +chkoom