diff --git a/apps/zasm/emul/Makefile b/apps/zasm/emul/Makefile index 4e82430..c1745a8 100644 --- a/apps/zasm/emul/Makefile +++ b/apps/zasm/emul/Makefile @@ -7,5 +7,5 @@ libz80/libz80.so: libz80/Makefile kernel.h: glue.asm scas -o - -I ../../../parts $< | ./bin2c.sh KERNEL | tee $@ > /dev/null -zasm.h: ../zasm.asm - scas -o - -I ./emul $< | ./bin2c.sh ZASM | tee $@ > /dev/null +zasm.h: ../zasm.asm ../tok.asm + scas -o - -I.. $< | ./bin2c.sh ZASM | tee $@ > /dev/null diff --git a/apps/zasm/tok.asm b/apps/zasm/tok.asm new file mode 100644 index 0000000..c92cfa6 --- /dev/null +++ b/apps/zasm/tok.asm @@ -0,0 +1,119 @@ +; tok +; +; Tokenizes an ASM source file into 1, 2 or 3-sized structures. +; +; *** Requirements *** +; JUMP_UPCASE + +; *** Code *** +; Parse line in (HL) and place each element in tokInstr, tokArg1, tokArg2. Those +; values are null-terminated and empty if not present. +; Sets Z on success, unsets it on error. Blank line is not an error. +; (as of now, we don't have any error condition. We always succeed) +tokenize: + push de + xor a + ld (tokInstr), a + ld (tokArg1), a + ld (tokArg2), a + ld de, tokInstr + ld a, 4 + call readWord + call toWord + jr nz, .end + ld de, tokArg1 + ld a, 8 + call readWord + call toWord + jr nz, .end + ld de, tokArg2 + call readWord +.end: + cp a ; ensure Z + pop de + ret + +; Sets Z is A is ';', CR, LF, or null. +isLineEnd: + cp ';' + ret z + cp 0 + ret z + cp 0x0d + ret z + cp 0x0a + ret + +; Sets Z is A is ' ' or ',' +isSep: + cp ' ' + ret z + cp ',' + ret + +; Sets Z is A is ' ', ',', ';', CR, LF, or null. +isSepOrLineEnd: + call isSep + ret z + call isLineEnd + ret + +; read word in (HL) and put it in (DE), null terminated, for a maximum of A +; characters. As a result, A is the read length. HL is advanced to the next +; separator char. +readWord: + push bc + ld b, a +.loop: + ld a, (hl) + call isSepOrLineEnd + jr z, .success + call JUMP_UPCASE + ld (de), a + inc hl + inc de + djnz .loop +.success: + xor a + ld (de), a + ld a, 4 + sub a, b + jr .end +.error: + xor a + ld (de), a +.end: + pop bc + ret + +; (HL) being a string, advance it to the next non-sep character. +; Set Z if we could do it before the line ended, reset Z if we couldn't. +toWord: +.loop: + ld a, (hl) + call isLineEnd + jr z, .error + call isSep + jr nz, .success + inc hl + jr .loop +.error: + ; we need the Z flag to be unset and it is set now. Let's CP with + ; something it can't be equal to, something not a line end. + cp 'a' ; Z flag unset + ret +.success: + ; We need the Z flag to be set and it is unset. Let's compare it with + ; itself to return a set Z + cp a + ret + +; *** Variables *** + +tokInstr: + .fill 5 +tokArg1: + .fill 9 +tokArg2: + .fill 9 + diff --git a/apps/zasm/zasm.asm b/apps/zasm/zasm.asm index cea8198..e180f33 100644 --- a/apps/zasm/zasm.asm +++ b/apps/zasm/zasm.asm @@ -10,6 +10,7 @@ INSTR_TBL_ROWSIZE .equ 9 ; *** Code *** .org USER_CODE + call parseLine ld b, 0 ld c, a ; written bytes @@ -17,6 +18,8 @@ ld hl, curUpcode call copy ret +#include "tok.asm" + unsetZ: push bc ld b, a @@ -165,129 +168,6 @@ parseNumber: pop hl ret -; Sets Z is A is ';', CR, LF, or null. -isLineEnd: - cp ';' - ret z - cp 0 - ret z - cp 0x0d - ret z - cp 0x0a - ret - -; Sets Z is A is ' ' or ',' -isSep: - cp ' ' - ret z - cp ',' - ret - -; Sets Z is A is ' ', ',', ';', CR, LF, or null. -isSepOrLineEnd: - call isSep - ret z - call isLineEnd - ret - -; read word in (HL) and put it in (DE), null terminated, for a maximum of A -; characters. As a result, A is the read length. HL is advanced to the next -; separator char. -readWord: - push bc - ld b, a -.loop: - ld a, (hl) - call isSepOrLineEnd - jr z, .success - call JUMP_UPCASE - ld (de), a - inc hl - inc de - djnz .loop -.success: - xor a - ld (de), a - ld a, 4 - sub a, b - jr .end -.error: - xor a - ld (de), a -.end: - pop bc - ret - -; (HL) being a string, advance it to the next non-sep character. -; Set Z if we could do it before the line ended, reset Z if we couldn't. -toWord: -.loop: - ld a, (hl) - call isLineEnd - jr z, .error - call isSep - jr nz, .success - inc hl - jr .loop -.error: - ; we need the Z flag to be unset and it is set now. Let's CP with - ; something it can't be equal to, something not a line end. - cp 'a' ; Z flag unset - ret -.success: - ; We need the Z flag to be set and it is unset. Let's compare it with - ; itself to return a set Z - cp a - ret - - -; Read arg from (HL) into argspec at (DE) -; HL is advanced to the next word. Z is set if there's a next word. -readArg: - push de - ld de, tmpBuf - ld a, 8 - call readWord - push hl - ld hl, tmpBuf - call parseArg - pop hl - pop de - ld (de), a - ; When A is a number, IX is set with the value of that number. Because - ; We don't use the space allocated to store those numbers in any other - ; occasion, we store IX there unconditonally, LSB first. - inc de - ld a, ixl - ld (de), a - inc de - ld a, ixh - ld (de), a - - call toWord - ret - -; Read line from (HL) into (curWord), (curArg1) and (curArg2) -readLine: - push de - xor a - ld (curWord), a - ld (curArg1), a - ld (curArg2), a - ld de, curWord - ld a, 4 - call readWord - call toWord - jr nz, .end - ld de, curArg1 - call readArg - jr nz, .end - ld de, curArg2 - call readArg -.end: - pop de - ret - ; Returns length of string at (HL) in A. strlen: push bc @@ -488,12 +368,12 @@ matchArg: pop hl ret -; Compare primary row at (DE) with string at curWord. Sets Z flag if there's a +; Compare primary row at (DE) with string at tokInstr. Sets Z flag if there's a ; match, reset if not. matchPrimaryRow: push hl push ix - ld hl, curWord + ld hl, tokInstr ld a, 4 call JUMP_STRNCMP jr nz, .end @@ -658,23 +538,47 @@ getUpcode: pop ix ret +; Parse tokenizes argument in (HL), parses it and place it in (DE) +; Sets Z on success, reset on error. +processArg: + call parseArg + cp 0xff + jr z, .error + ld (de), a + ; When A is a number, IX is set with the value of that number. Because + ; We don't use the space allocated to store those numbers in any other + ; occasion, we store IX there unconditonally, LSB first. + inc de + ld a, ixl + ld (de), a + inc de + ld a, ixh + ld (de), a + cp a ; ensure Z is set + ret +.error: + call unsetZ + ret + ; Parse line at (HL) and write resulting opcode(s) in curUpcode. Returns the ; number of bytes written in A. parseLine: - call readLine - ; Check whether we have errors. We don't do any parsing if we do. - ld a, (curArg1) - cp 0xff - jr z, .error - ret z - ld a, (curArg2) - cp 0xff - jr nz, .noerror -.error: - xor a - ret -.noerror: + push hl push de + call tokenize + jr nz, .error + ld a, (tokInstr) + cp 0 + jr z, .error ; for now, we treat blank lines as errors + ld hl, tokArg1 + ld de, curArg1 + call processArg + jr nz, .error + ld hl, tokArg2 + ld de, curArg2 + call processArg + jr nz, .error + ; Parsing done, no error, let's move forward to instr row matching! ld de, instrTBl ld b, INSTR_TBL_CNT .loop: @@ -691,8 +595,12 @@ parseLine: ; We have our matching instruction row. We're getting pretty near our ; goal here! call getUpcode + jr .end +.error: + xor a .end: pop de + pop hl ret @@ -860,10 +768,6 @@ instrTBl: ; *** Variables *** -; enough space for 4 chars and a null -curWord: - .db 0, 0, 0, 0, 0 - ; Args are 3 bytes: argspec, then values of numerical constants (when that's ; appropriate) curArg1: @@ -874,7 +778,3 @@ curArg2: curUpcode: .db 0, 0, 0, 0 -; space for tmp stuff -tmpBuf: - .fill 0x20 -