@@ -8,5 +8,5 @@ libz80/libz80.o: libz80/z80.c | |||
kernel.h: glue.asm | |||
scas -o - -I ../../../parts/z80 $< | ./bin2c.sh KERNEL | tee $@ > /dev/null | |||
zasm.h: ../main.asm ../instr.asm ../tok.asm | |||
zasm.h: $(addprefix ../, main.asm instr.asm tok.asm util.asm) | |||
scas -o - -I.. $< | ./bin2c.sh ZASM | tee $@ > /dev/null |
@@ -6,73 +6,6 @@ INSTR_TBL_CNT .equ 135 | |||
; size in bytes of each row in the primary instructions table | |||
INSTR_TBL_ROWSIZE .equ 9 | |||
; run RLA the number of times specified in B | |||
rlaX: | |||
; first, see if B == 0 to see if we need to bail out | |||
inc b | |||
dec b | |||
ret z ; Z flag means we had B = 0 | |||
.loop: rla | |||
djnz .loop | |||
ret | |||
; Copy BC bytes from (HL) to (DE). | |||
copy: | |||
; first, let's see if BC is zero. if it is, we have nothing to do. | |||
; remember: 16-bit inc/dec don't modify flags. that's why we check B | |||
; and C separately. | |||
inc b | |||
dec b | |||
jr nz, .proceed | |||
inc c | |||
dec c | |||
ret z ; zero? nothing to do | |||
.proceed: | |||
push bc | |||
push de | |||
push hl | |||
ldir | |||
pop hl | |||
pop de | |||
pop bc | |||
ret | |||
callHL: | |||
jp (hl) | |||
ret | |||
; If string at (HL) starts with ( and ends with ), "enter" into the parens | |||
; (advance HL and put a null char at the end of the string) and set Z. | |||
; Otherwise, do nothing and reset Z. | |||
enterParens: | |||
ld a, (hl) | |||
cp '(' | |||
ret nz ; nothing to do | |||
push hl | |||
ld a, 0 ; look for null char | |||
; advance until we get null | |||
.loop: | |||
cpi | |||
jp z, .found | |||
jr .loop | |||
.found: | |||
dec hl ; cpi over-advances. go back to null-char | |||
dec hl ; looking at the last char before null | |||
ld a, (hl) | |||
cp ')' | |||
jr nz, .doNotEnter | |||
; We have parens. While we're here, let's put a null | |||
xor a | |||
ld (hl), a | |||
pop hl ; back at the beginning. Let's advance. | |||
inc hl | |||
cp a ; ensure Z | |||
ret ; we're good! | |||
.doNotEnter: | |||
pop hl | |||
call JUMP_UNSETZ | |||
ret | |||
; Checks whether A is 'N' or 'M' | |||
checkNOrM: | |||
cp 'N' | |||
@@ -772,13 +705,11 @@ processArg: | |||
call JUMP_UNSETZ | |||
ret | |||
; Parse line at (HL) and write resulting opcode(s) in curUpcode. Returns the | |||
; number of bytes written in A. | |||
parseLine: | |||
; Parse tokens in (tokInstr), (tokArg1) and (tokArg2) and write resulting | |||
; opcode(s) in (curUpcode). Returns the number of bytes written in A. | |||
parseTokens: | |||
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 | |||
@@ -0,0 +1,48 @@ | |||
#include "user.inc" | |||
; *** Code *** | |||
.org USER_CODE | |||
; Parse asm file in (HL) and outputs its upcodes in (DE). Returns the number | |||
; of bytes written in C. | |||
main: | |||
ld bc, 0 ; C is our written bytes counter | |||
.loop: | |||
call parseLine | |||
or a ; is zero? stop | |||
jr z, .stop | |||
add a, c | |||
ld c, a | |||
call gotoNextLine | |||
jr nz, .stop ; error? stop | |||
jr .loop | |||
.stop: | |||
ret | |||
; Parse line in (HL), write the resulting opcode(s) in (DE) and returns the | |||
; number of written bytes in A. Advances HL where tokenization stopped and DE | |||
; to where we should write the next upcode. | |||
parseLine: | |||
push bc | |||
call tokenize | |||
jr nz, .error | |||
call parseTokens | |||
or a ; is zero? | |||
jr z, .error | |||
ld b, 0 | |||
ld c, a ; written bytes | |||
push hl | |||
ld hl, curUpcode | |||
call copy | |||
pop hl | |||
call JUMP_ADDDE | |||
jr .end | |||
.error: | |||
xor a | |||
.end: | |||
pop bc | |||
ret | |||
#include "util.asm" | |||
#include "tok.asm" | |||
#include "instr.asm" |
@@ -5,13 +5,11 @@ set -e | |||
TMPFILE=$(mktemp) | |||
SCAS=scas | |||
ZASM=../emul/zasm | |||
ASMFILE=../zasm.asm | |||
ASMFILE=../instr.asm | |||
./geninstrs.py $ASMFILE | \ | |||
while read line; do | |||
echo $line | tee "${TMPFILE}" | |||
EXPECTED=$($SCAS -o - "${TMPFILE}" | xxd) | |||
ACTUAL=$(echo $line | $ZASM | xxd) | |||
cmpas() { | |||
EXPECTED=$($SCAS -o - "$1" | xxd) | |||
ACTUAL=$(cat $1 | $ZASM | xxd) | |||
if [ "$ACTUAL" == "$EXPECTED" ]; then | |||
echo ok | |||
else | |||
@@ -21,4 +19,15 @@ while read line; do | |||
echo $EXPECTED | |||
exit 1 | |||
fi | |||
} | |||
./geninstrs.py $ASMFILE | \ | |||
while read line; do | |||
echo $line | tee "${TMPFILE}" | |||
cmpas ${TMPFILE} | |||
done | |||
for fn in *.asm; do | |||
echo "Comparing ${fn}" | |||
cmpas $fn | |||
done |
@@ -30,25 +30,31 @@ tokenize: | |||
ld de, tokArg2 | |||
call readWord | |||
.end: | |||
cp a ; ensure Z | |||
xor a ; ensure Z | |||
pop de | |||
ret | |||
; Sets Z is A is ';', CR, LF, or null. | |||
isLineEnd: | |||
isLineEndOrComment: | |||
cp ';' | |||
ret z | |||
cp 0 | |||
; Continues onto isLineEnd... | |||
; Sets Z is A is CR, LF, or null. | |||
isLineEnd: | |||
or a ; same as cp 0 | |||
ret z | |||
cp 0x0d | |||
ret z | |||
cp 0x0a | |||
ret | |||
; Sets Z is A is ' ' or ',' | |||
; Sets Z is A is ' ' '\t' or ',' | |||
isSep: | |||
cp ' ' | |||
ret z | |||
cp 0x09 | |||
ret z | |||
cp ',' | |||
ret | |||
@@ -56,7 +62,7 @@ isSep: | |||
isSepOrLineEnd: | |||
call isSep | |||
ret z | |||
call isLineEnd | |||
call isLineEndOrComment | |||
ret | |||
; read word in (HL) and put it in (DE), null terminated, for a maximum of A | |||
@@ -92,7 +98,7 @@ readWord: | |||
toWord: | |||
.loop: | |||
ld a, (hl) | |||
call isLineEnd | |||
call isLineEndOrComment | |||
jr z, .error | |||
call isSep | |||
jr nz, .success | |||
@@ -109,6 +115,38 @@ toWord: | |||
cp a | |||
ret | |||
; Advance HL to the beginning of the next line, that is, right after the next | |||
; 0x10 or 0x13 or both. If we reach null, we stop and error out. | |||
; Sets Z on success, unsets it on error. | |||
gotoNextLine: | |||
dec hl ; a bit weird, but makes the looping easier | |||
.loop: | |||
inc hl | |||
ld a, (hl) | |||
call isLineEnd | |||
jr nz, .loop | |||
; (HL) is 0x10, 0x13 or 0 | |||
or a ; is 0? | |||
jr z, .error | |||
; we might have 0x13 followed by 0x10, let's account for this. | |||
; Yes, 0x10 followed by 0x10 will make us skip two lines, but this is of | |||
; no real consequence in our context. | |||
inc hl | |||
ld a, (hl) | |||
call isLineEnd | |||
jr nz, .success | |||
or a ; is 0? | |||
jr z, .error | |||
; There was another line sep. Skip this char | |||
inc hl | |||
; Continue on to .success | |||
.success: | |||
xor a ; ensure Z | |||
ret | |||
.error: | |||
call JUMP_UNSETZ | |||
ret | |||
; *** Variables *** | |||
tokInstr: | |||
@@ -0,0 +1,68 @@ | |||
; run RLA the number of times specified in B | |||
rlaX: | |||
; first, see if B == 0 to see if we need to bail out | |||
inc b | |||
dec b | |||
ret z ; Z flag means we had B = 0 | |||
.loop: rla | |||
djnz .loop | |||
ret | |||
; Copy BC bytes from (HL) to (DE). | |||
copy: | |||
; first, let's see if BC is zero. if it is, we have nothing to do. | |||
; remember: 16-bit inc/dec don't modify flags. that's why we check B | |||
; and C separately. | |||
inc b | |||
dec b | |||
jr nz, .proceed | |||
inc c | |||
dec c | |||
ret z ; zero? nothing to do | |||
.proceed: | |||
push bc | |||
push de | |||
push hl | |||
ldir | |||
pop hl | |||
pop de | |||
pop bc | |||
ret | |||
callHL: | |||
jp (hl) | |||
ret | |||
; If string at (HL) starts with ( and ends with ), "enter" into the parens | |||
; (advance HL and put a null char at the end of the string) and set Z. | |||
; Otherwise, do nothing and reset Z. | |||
enterParens: | |||
ld a, (hl) | |||
cp '(' | |||
ret nz ; nothing to do | |||
push hl | |||
ld a, 0 ; look for null char | |||
; advance until we get null | |||
.loop: | |||
cpi | |||
jp z, .found | |||
jr .loop | |||
.found: | |||
dec hl ; cpi over-advances. go back to null-char | |||
dec hl ; looking at the last char before null | |||
ld a, (hl) | |||
cp ')' | |||
jr nz, .doNotEnter | |||
; We have parens. While we're here, let's put a null | |||
xor a | |||
ld (hl), a | |||
pop hl ; back at the beginning. Let's advance. | |||
inc hl | |||
cp a ; ensure Z | |||
ret ; we're good! | |||
.doNotEnter: | |||
pop hl | |||
call JUMP_UNSETZ | |||
ret | |||
@@ -15,11 +15,13 @@ P_NULL: .db 0 | |||
; add the value of A into DE | |||
addDE: | |||
push af | |||
add a, e | |||
jr nc, .end ; no carry? skip inc | |||
inc d | |||
.end: | |||
ld e, a | |||
pop af | |||
ret | |||
; copy (DE) into DE, little endian style (addresses in z80 are always have | |||
@@ -46,11 +48,13 @@ intoHL: | |||
; add the value of A into HL | |||
addHL: | |||
push af | |||
add a, l | |||
jr nc, .end ; no carry? skip inc | |||
inc h | |||
.end: | |||
ld l, a | |||
pop af | |||
ret | |||