From 2f0dd5d668ea927d2a9c12ee6165b6726c6f7ec4 Mon Sep 17 00:00:00 2001 From: Virgil Dupras 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