From 2f0dd5d668ea927d2a9c12ee6165b6726c6f7ec4 Mon Sep 17 00:00:00 2001 From: Virgil Dupras <hsoft@hardcoded.net> Date: Thu, 16 May 2019 21:15:00 -0400 Subject: [PATCH] zasm: iiiiiiiincluuuuuuudes!!1! --- apps/zasm/directive.asm | 31 ++++++++++- apps/zasm/io.asm | 124 +++++++++++++++++++++++++++++++++++++++++-- apps/zasm/main.asm | 20 +++---- parts/z80/fs.asm | 7 ++- tools/emul/Makefile | 9 ++-- tools/emul/zasm.c | 5 +- tools/emul/zasm_glue.asm | 13 +++-- tools/emul/zasm_user.asm | 6 +++ tools/tests/zasm/runtests.sh | 6 +-- tools/tests/zasm/test6.asm | 3 ++ 10 files changed, 192 insertions(+), 32 deletions(-) create mode 100644 tools/tests/zasm/test6.asm diff --git a/apps/zasm/directive.asm b/apps/zasm/directive.asm index 5082ed0..1fae84a 100644 --- a/apps/zasm/directive.asm +++ b/apps/zasm/directive.asm @@ -3,6 +3,7 @@ .equ D_DB 0x00 .equ D_DW 0x01 .equ D_EQU 0x02 +.equ D_INC 0x03 .equ D_BAD 0xff ; *** Variables *** @@ -15,12 +16,14 @@ directiveNames: .db ".DB", 0 .db ".DW", 0 .db ".EQU" + .db "#inc" ; This is a list of handlers corresponding to indexes in directiveNames directiveHandlers: .dw handleDB .dw handleDW .dw handleEQU + .dw handleINC handleDB: push hl @@ -84,12 +87,38 @@ handleEQU: pop hl ret +handleINC: + call readWord + jr nz, .end + ; HL points to scratchpad + ; First, let's verify that our string is enquoted + ld a, (hl) + cp '"' + jr nz, .end + ; We have opening quote + inc hl + xor a + call JUMP_FINDCHAR ; go to end of string + dec hl + ld a, (hl) + cp '"' + jr nz, .end + ; we have ending quote, let's replace with null char + xor a + ld (hl), a + ; Good, let's go back + ld hl, scratchpad+1 ; +1 because of the opening quote + call ioOpenInclude +.end: + xor a ; zero bytes written + ret + ; Reads string in (HL) and returns the corresponding ID (D_*) in A. Sets Z if ; there's a match. getDirectiveID: push bc push de - ld b, D_EQU+1 ; D_EQU is last + ld b, D_INC+1 ; D_INC is last ld c, 4 ld de, directiveNames call findStringInList diff --git a/apps/zasm/io.asm b/apps/zasm/io.asm index eceb719..5a3c68c 100644 --- a/apps/zasm/io.asm +++ b/apps/zasm/io.asm @@ -1,3 +1,36 @@ +; I/Os in zasm +; +; As a general rule, I/O in zasm is pretty straightfoward. We receive, as a +; parameter, two blockdevs: One that we can read and seek and one that we can +; write to (we never seek into it). +; +; zasm doesn't buffers its reads during tokenization, which simplifies its +; process. However, it also means that it needs, in certain cases, a "putback" +; mechanism, that is, a way to say "you see that character I've just read? that +; was out of my bounds. Could you make it as if I had never read it?". That +; buffer is one character big and is made with the expectation that ioPutBack +; is always called right after a ioGetC (when it's called). +; +; ioPutBack will mess up seek and tell offsets, so thath "put back" should be +; consumed before having to seek and tell. +; +; That's for the general rules. +; +; Now, let's enter includes. To simplify processing, we make include mostly +; transparent to all other units. They always read from ioGetC and a include +; directive should have the exact same effect as copy/pasting the contents of +; the included file in the caller. +; +; By the way: we don't support multiple level of inclusion. Only top level files +; can include. +; +; When we include, all we do here is open the file with fsOpen and set a flag +; indicating that we're inside an include. When that flag is on, GetC, Seek and +; Tell are transparently redirected to their fs* counterpart. +; +; When we reach EOF in an included file, we transparently unset the "in include" +; flag and continue on the general IN stream. + ; *** Variables *** .equ IO_IN_GETC IO_RAMSTART .equ IO_IN_PUTC IO_IN_GETC+2 @@ -7,21 +40,50 @@ .equ IO_OUT_PUTC IO_OUT_GETC+2 .equ IO_OUT_SEEK IO_OUT_PUTC+2 .equ IO_OUT_TELL IO_OUT_SEEK+2 +; Save pos for ioSavePos and ioRecallPos +.equ IO_SAVED_POS IO_OUT_TELL+2 +; File handle for included source +.equ IO_INCLUDE_HDL IO_SAVED_POS+2 ; see ioPutBack below -.equ IO_PUTBACK_BUF IO_OUT_TELL+2 -.equ IO_RAMEND IO_PUTBACK_BUF+1 +.equ IO_PUTBACK_BUF IO_INCLUDE_HDL+FS_HANDLE_SIZE +.equ IO_IN_INCLUDE IO_PUTBACK_BUF+1 +.equ IO_RAMEND IO_IN_INCLUDE+1 ; *** Code *** ioInit: xor a ld (IO_PUTBACK_BUF), a + ld (IO_IN_INCLUDE), a ret ioGetC: ld a, (IO_PUTBACK_BUF) or a ; cp 0 jr nz, .getback + call ioInInclude + jr z, .normalmode + ; We're in "include mode", read from FS + push de + ld de, IO_INCLUDE_HDL + call JUMP_FSGETC + pop de + or a ; cp 0 + ret nz ; not zero, all good + ; We reached EOF. What we do depends on whether we're in Local Pass + ; mode. Yes, I know, a bit hackish. Normally, we *should* be + ; transparently getting of include mode and avoid meddling with global + ; states, but here, we need to tell main.asm that the local scope if + ; over *before* we get off include mode, otherwise, our IO_SAVED_POS + ; will be wrong (an include IO_SAVED_POS used in global IN stream). + call zasmIsLocalPass + ld a, 0 ; doesn't affect Z flag + ret z ; local pass? return EOF + ; regular pass (first or second)? transparently get off include mode. + ld (IO_IN_INCLUDE), a ; A already 0 + ; continue on to "normal" reading. We don't want to return our zero +.normalmode: + ; normal mode, read from IN stream ld ix, (IO_IN_GETC) jp (ix) .getback: @@ -42,10 +104,64 @@ ioPutC: ld ix, (IO_OUT_PUTC) jp (ix) -ioSeek: +ioSavePos: + call _ioTell + ld (IO_SAVED_POS), hl + ret + +ioRecallPos: + ld hl, (IO_SAVED_POS) + jr _ioSeek + +ioRewind: + ld hl, 0 + jr _ioSeek + +; always in absolute mode (A = 0) +_ioSeek: + call ioInInclude + ld a, 0 ; don't alter flags + jr nz, .include + ; normal mode, seek in IN stream ld ix, (IO_IN_SEEK) jp (ix) +.include: + ; We're in "include mode", seek in FS + push de + ld de, IO_INCLUDE_HDL + call JUMP_FSSEEK + pop de + ret -ioTell: +_ioTell: + call ioInInclude + jp nz, .include + ; normal mode, seek in IN stream ld ix, (IO_IN_TELL) jp (ix) +.include: + ; We're in "include mode", tell from FS + push de + ld de, IO_INCLUDE_HDL + call JUMP_FSTELL + pop de + ret + +; Sets Z according to whether we're inside an include +ioInInclude: + ld a, (IO_IN_INCLUDE) + or a ; cp 0 + ret + +; Open include file name specified in (HL). +; Sets Z on success, unset on error. +ioOpenInclude: + call JUMP_FSFINDFN + ret nz + ld hl, IO_INCLUDE_HDL + call JUMP_FSOPEN + ld a, 1 + ld (IO_IN_INCLUDE), a + cp a ; ensure Z + ret + diff --git a/apps/zasm/main.asm b/apps/zasm/main.asm index 5172f1c..16cd4ed 100644 --- a/apps/zasm/main.asm +++ b/apps/zasm/main.asm @@ -28,7 +28,13 @@ ; JUMP_INTOHL ; JUMP_FINDCHAR ; JUMP_BLKSEL +; JUMP_FSFINDFN +; JUMP_FSOPEN +; JUMP_FSGETC +; JUMP_FSSEEK +; JUMP_FSTELL ; RAMSTART (where we put our variables in RAM) +; FS_HANDLE_SIZE ; *** Variables *** @@ -43,11 +49,8 @@ ; this special pass, ZASM_FIRST_PASS will also be set so that the rest of the ; code behaves as is we were in the first pass. .equ ZASM_LOCAL_PASS ZASM_PC+2 -; I/O position (in terms of ioSeek/ioTell) of the current context. Used to -; rewind to it after having parsed local labels. -.equ ZASM_CTX_POS ZASM_LOCAL_PASS+1 ; What ZASM_PC was when we started our context -.equ ZASM_CTX_PC ZASM_CTX_POS+2 +.equ ZASM_CTX_PC ZASM_LOCAL_PASS+1 .equ ZASM_RAMEND ZASM_CTX_PC+2 ; *** Code *** @@ -88,8 +91,7 @@ zasmMain: call zasmParseFile ret nz ; Second pass - ld hl, 0 - call ioSeek + call ioRewind xor a ld (ZASM_FIRST_PASS), a call zasmParseFile @@ -244,8 +246,7 @@ _parseLabel: _beginLocalPass: ; remember were I/O was - call ioTell - ld (ZASM_CTX_POS), hl + call ioSavePos ; Remember where PC was ld hl, (ZASM_PC) ld (ZASM_CTX_PC), hl @@ -264,8 +265,7 @@ _beginLocalPass: _endLocalPass: call symSelectGlobalRegistry ; recall I/O pos - ld hl, (ZASM_CTX_POS) - call ioSeek + call ioRecallPos ; recall PC ld hl, (ZASM_CTX_PC) ld (ZASM_PC), hl diff --git a/parts/z80/fs.asm b/parts/z80/fs.asm index 1147550..93a7cad 100644 --- a/parts/z80/fs.asm +++ b/parts/z80/fs.asm @@ -401,8 +401,8 @@ fsOpen: fsPlaceH: push af push hl - ld ixh, d - ld ixl, e + push de + pop ix push ix ld l, (ix) ld h, (ix+1) @@ -417,8 +417,7 @@ fsPlaceH: fsAdvanceH: push af inc (ix) - ld a, (ix) - jr nc, .end + jr nz, .end inc (ix+1) .end: pop af diff --git a/tools/emul/Makefile b/tools/emul/Makefile index d3ba997..d24d287 100644 --- a/tools/emul/Makefile +++ b/tools/emul/Makefile @@ -14,12 +14,15 @@ $(KERNEL_HEADERS): zasm-user.h: zasm_user.asm scas -o - -I ../../apps/zasm $< | ./bin2c.sh USERSPACE | tee $@ > /dev/null -zasm-includes.h: ../../parts/z80 $(CFSPACK) +zasm-includes.cfs: ../../parts/z80 $(CFSPACK) cp -rf $< zasm-includes rm zasm-includes/README.md - $(CFSPACK) zasm-includes | ./bin2c.sh FSDEV | tee $@ > /dev/null + $(CFSPACK) zasm-includes > $@ rm -rf zasm-includes +zasm-includes.h: zasm-includes.cfs + ./bin2c.sh FSDEV < $< | tee $@ > /dev/null + shell: shell.c libz80/libz80.o shell-kernel.h $(CFSPACK) zasm: zasm.c libz80/libz80.o zasm-kernel.h zasm-user.h zasm-includes.h $(TARGETS): @@ -34,4 +37,4 @@ $(CFSPACK): .PHONY: clean clean: - rm -f $(TARGETS) $(KERNEL_HEADERS) $(USER_HEADERS) + rm -f $(TARGETS) $(KERNEL_HEADERS) $(USER_HEADERS) zasm-includes.* diff --git a/tools/emul/zasm.c b/tools/emul/zasm.c index b651e2f..24be458 100644 --- a/tools/emul/zasm.c +++ b/tools/emul/zasm.c @@ -83,7 +83,7 @@ static uint8_t io_read(int unused, uint16_t addr) return fsdev_ptr & 0xff; } else { #ifdef DEBUG - fprintf(stderr, "tell %d\n", fsdev_ptr); + fprintf(stderr, "FS tell %d\n", fsdev_ptr); #endif fsdev_middle_of_seek_tell = 1; return fsdev_ptr >> 8; @@ -122,7 +122,7 @@ static void io_write(int unused, uint16_t addr, uint8_t val) fsdev_ptr |= val; fsdev_middle_of_seek_tell = 0; #ifdef DEBUG - fprintf(stderr, "seek %d\n", fsdev_ptr); + fprintf(stderr, "FS seek %d\n", fsdev_ptr); #endif } else { fsdev_ptr = (val << 8) & 0xff00; @@ -155,6 +155,7 @@ int main() for (int i=0; i<sizeof(FSDEV); i++) { fsdev[i] = FSDEV[i]; } + fsdev_size = sizeof(FSDEV); // read stdin in buffer inpt_size = 0; inpt_ptr = 0; diff --git a/tools/emul/zasm_glue.asm b/tools/emul/zasm_glue.asm index aaac49e..851fd9e 100644 --- a/tools/emul/zasm_glue.asm +++ b/tools/emul/zasm_glue.asm @@ -18,6 +18,11 @@ jp intoHL jp findchar jp parseHexPair jp blkSel +jp fsFindFN +jp fsOpen +jp fsGetC +jp fsSeek +jp fsTell #include "core.asm" .equ BLOCKDEV_RAMSTART RAMSTART @@ -89,19 +94,19 @@ fsdevPutC: fsdevSeek: push af - ld a, l - out (FS_SEEK_PORT), a ld a, h out (FS_SEEK_PORT), a + ld a, l + out (FS_SEEK_PORT), a pop af ret fsdevTell: push af in a, (FS_SEEK_PORT) - ld l, a - in a, (FS_SEEK_PORT) ld h, a + in a, (FS_SEEK_PORT) + ld l, a pop af ret diff --git a/tools/emul/zasm_user.asm b/tools/emul/zasm_user.asm index 1bd53be..157fbaf 100644 --- a/tools/emul/zasm_user.asm +++ b/tools/emul/zasm_user.asm @@ -9,7 +9,13 @@ JUMP_INTOHL .equ 0x15 JUMP_FINDCHAR .equ 0x18 JUMP_PARSEHEXPAIR .equ 0x1b JUMP_BLKSEL .equ 0x1e +JUMP_FSFINDFN .equ 0x21 +JUMP_FSOPEN .equ 0x24 +JUMP_FSGETC .equ 0x27 +JUMP_FSSEEK .equ 0x2a +JUMP_FSTELL .equ 0x2d +.equ FS_HANDLE_SIZE 8 .equ USER_CODE 0x4800 .equ RAMSTART 0x5800 .org USER_CODE diff --git a/tools/tests/zasm/runtests.sh b/tools/tests/zasm/runtests.sh index 0d54775..dd0b11e 100755 --- a/tools/tests/zasm/runtests.sh +++ b/tools/tests/zasm/runtests.sh @@ -4,11 +4,12 @@ set -e TMPFILE=$(mktemp) SCAS=scas +PARTS=../../../parts/z80 ZASM=../../emul/zasm ASMFILE=../../../apps/zasm/instr.asm cmpas() { - EXPECTED=$($SCAS -o - "$1" | xxd) + EXPECTED=$($SCAS -I ${PARTS} -o - "$1" | xxd) ACTUAL=$(cat $1 | $ZASM | xxd) if [ "$ACTUAL" == "$EXPECTED" ]; then echo ok @@ -21,9 +22,6 @@ cmpas() { fi } -echo "Comparing core.asm" -cmpas ../../../parts/z80/core.asm - for fn in *.asm; do echo "Comparing ${fn}" cmpas $fn diff --git a/tools/tests/zasm/test6.asm b/tools/tests/zasm/test6.asm new file mode 100644 index 0000000..648a642 --- /dev/null +++ b/tools/tests/zasm/test6.asm @@ -0,0 +1,3 @@ +add a, b +#include "core.asm" +inc b