@@ -0,0 +1,69 @@ | |||
; *** Consts *** | |||
.equ IO_MAX_LINELEN 0xff | |||
; *** Variables *** | |||
ioGetCPtr: | |||
.fill 2 | |||
ioPutCPtr: | |||
.fill 2 | |||
ioLineBuf: | |||
.fill IO_MAX_LINELEN+1 | |||
; *** Code *** | |||
ioGetC: | |||
ld ix, (ioGetCPtr) | |||
jp (ix) | |||
ioPutC: | |||
ld ix, (ioPutCPtr) | |||
jp (ix) | |||
; 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 | |||
; Read a single line from ioGetCPtr and place it in ioLineBuf. | |||
; Returns number of chars read in A. 0 means we're at the end of our input | |||
; stream, which happens when GetC unsets Z. Make HL point to ioLineBuf. | |||
; We ignore empty lines and pass through them like butter. | |||
; A null char is written at the end of the line. | |||
ioReadLine: | |||
push bc | |||
; consume ioGetC as long as it yields a line end char. | |||
.loop1: | |||
call ioGetC | |||
jr nz, .eof ; GetC unsets Z? We don't have a line to read, | |||
; we have EOF. | |||
call isLineEnd | |||
jr z, .loop1 | |||
; A contains the first char of our line. | |||
ld c, 1 | |||
ld (ioLineBuf), a | |||
ld hl, ioLineBuf+1 | |||
.loop2: | |||
call ioGetC | |||
call isLineEnd | |||
jr z, .success ; We have end of line | |||
ld (hl), a | |||
inc hl | |||
inc c | |||
jr .loop2 | |||
.success: | |||
; write null char at HL before we return | |||
xor a | |||
ld (hl), a | |||
ld a, c | |||
ld hl, ioLineBuf | |||
jr .end | |||
.eof: | |||
xor a | |||
.end: | |||
pop bc | |||
ret | |||
@@ -1,4 +1,5 @@ | |||
; *** Requirements *** | |||
; blockdev | |||
; JUMP_STRNCMP | |||
; JUMP_ADDDE | |||
; JUMP_UPCASE | |||
@@ -6,23 +7,23 @@ | |||
; JUMP_INTODE | |||
; *** Code *** | |||
; Parse asm file in (HL) and outputs its upcodes in (DE). Returns the number | |||
; of bytes written in C. | |||
; Read file through GetC routine pointer at HL and outputs its upcodes through | |||
; the PutC routine pointer at DE. | |||
main: | |||
ld bc, 0 ; C is our written bytes counter | |||
ld (ioGetCPtr), hl | |||
ld (ioPutCPtr), de | |||
.loop: | |||
call ioReadLine | |||
or a ; is A 0? | |||
jr z, .stop ; We have EOF | |||
call parseLine | |||
jr nz, .stop | |||
ld a, c | |||
add a, ixl | |||
ld c, a | |||
call gotoNextLine | |||
jr nz, .stop ; error? stop | |||
jr .loop | |||
.stop: | |||
ret | |||
#include "util.asm" | |||
#include "io.asm" | |||
#include "parse.asm" | |||
#include "literal.asm" | |||
#include "instr.asm" | |||
@@ -36,38 +37,38 @@ main: | |||
parseLine: | |||
push bc | |||
call gotoNextNotBlankLine | |||
jr nz, .error | |||
call tokenize | |||
ld a, b ; TOK_* | |||
cp TOK_INSTR | |||
jr z, .instr | |||
cp TOK_DIRECTIVE | |||
jr z, .direc | |||
cp TOK_EMPTY | |||
jr z, .success ; empty line? do nothing but don't error out. | |||
jr .error ; token not supported | |||
.instr: | |||
ld a, c ; I_* | |||
call parseInstruction | |||
or a ; is zero? | |||
jr z, .error | |||
ld b, 0 | |||
ld c, a ; written bytes | |||
push hl | |||
ld b, a | |||
ld hl, instrUpcode | |||
call copy | |||
pop hl | |||
call JUMP_ADDDE | |||
.loopInstr: | |||
ld a, (hl) | |||
call ioPutC | |||
inc hl | |||
djnz .loopInstr | |||
jr .success | |||
.direc: | |||
ld a, c ; D_* | |||
call parseDirective | |||
ld b, 0 | |||
ld c, a ; written bytes | |||
push hl | |||
ld b, a | |||
ld hl, direcData | |||
call copy | |||
pop hl | |||
call JUMP_ADDDE | |||
.loopDirec: | |||
ld a, (hl) | |||
call ioPutC | |||
inc hl | |||
djnz .loopDirec | |||
jr .success | |||
.success: | |||
ld ixl, a | |||
@@ -6,19 +6,11 @@ scratchpad: | |||
; *** Code *** | |||
; Sets Z is A is ';', CR, LF, or null. | |||
; Sets Z is A is ';' or null. | |||
isLineEndOrComment: | |||
cp ';' | |||
ret z | |||
; 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 | |||
or a ; cp 0 | |||
ret | |||
; Sets Z is A is ' ' '\t' or ',' | |||
@@ -81,48 +73,3 @@ toWord: | |||
.success: | |||
xor a ; ensure Z | |||
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 | |||
; Repeatedly calls gotoNextLine until the line in (HL) points to a line that | |||
; isn't blank or 100% comment. Sets Z if we reach a line, Unset Z if we reach | |||
; EOF | |||
gotoNextNotBlankLine: | |||
call toWord | |||
ret z ; Z set? we have a not-blank line | |||
; Z not set? (HL) is at the end of the line or at the beginning of | |||
; comments. | |||
call gotoNextLine | |||
ret nz | |||
jr gotoNextNotBlankLine | |||
@@ -4,6 +4,7 @@ | |||
; *** Consts *** | |||
TOK_INSTR .equ 0x01 | |||
TOK_DIRECTIVE .equ 0x02 | |||
TOK_EMPTY .equ 0xfe ; not a bad token, just an empty line | |||
TOK_BAD .equ 0xff | |||
; *** Code *** | |||
@@ -14,6 +15,7 @@ TOK_BAD .equ 0xff | |||
; If no token matches, TOK_BAD is written to B | |||
tokenize: | |||
call toWord | |||
jr nz, .emptyline | |||
call readWord | |||
push hl ; Save advanced HL for later | |||
ld hl, scratchpad | |||
@@ -33,3 +35,7 @@ tokenize: | |||
ld c, a | |||
pop hl | |||
ret | |||
.emptyline: | |||
ld b, TOK_EMPTY | |||
; no HL to pop, we jumped before the push | |||
ret |
@@ -8,27 +8,6 @@ rlaX: | |||
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 | |||
@@ -1,5 +1,6 @@ | |||
TARGETS = shell zasm | |||
KERNEL_HEADERS = shell-kernel.h zasm-kernel.h | |||
USER_HEADERS = zasm-user.h | |||
.PHONY: all | |||
all: $(TARGETS) | |||
@@ -23,4 +24,4 @@ libz80/libz80.o: libz80/z80.c | |||
.PHONY: clean | |||
clean: | |||
rm -f $(TARGETS) $(KERNEL_HEADERS) | |||
rm -f $(TARGETS) $(KERNEL_HEADERS) $(USER_HEADERS) |
@@ -1,8 +1,6 @@ | |||
; Glue code for the emulated environment | |||
.equ USER_CODE 0x4000 | |||
.equ RAMEND 0xffff | |||
.equ ZASM_INPUT 0xa000 | |||
.equ ZASM_OUTPUT 0xd000 | |||
.equ STDIO_PORT 0x00 | |||
jr init ; 2 bytes | |||
@@ -17,35 +15,24 @@ init: | |||
di | |||
ld hl, RAMEND | |||
ld sp, hl | |||
ld hl, ZASM_INPUT | |||
; yes, this means that input can't have null bytes | |||
.inloop: | |||
in a, (STDIO_PORT) | |||
ld (hl), a ; before cond jr so we write a final \0 | |||
or a | |||
jr z, .inloopend | |||
inc hl | |||
jr .inloop | |||
.inloopend: | |||
ld hl, ZASM_INPUT | |||
ld de, ZASM_OUTPUT | |||
ld hl, emulGetC | |||
ld de, emulPutC | |||
call USER_CODE | |||
; BC contains the number of written bytes | |||
xor a | |||
cp b | |||
jr nz, .spit | |||
cp c | |||
jr z, .end ; no output | |||
.spit: | |||
ld hl, ZASM_OUTPUT | |||
.outloop: | |||
ld a, (hl) | |||
out (STDIO_PORT), a | |||
cpi ; a trick to inc HL and dec BC at the same time. | |||
; P/V indicates whether BC reached 0 | |||
jp pe, .outloop ; BC is not zero, loop | |||
.end: | |||
; signal the emulator we're done | |||
halt | |||
emulGetC: | |||
in a, (STDIO_PORT) | |||
or a ; cp 0 | |||
jr z, .eof | |||
cp a ; ensure z | |||
ret | |||
.eof: | |||
call unsetZ | |||
ret | |||
emulPutC: | |||
out (STDIO_PORT), a | |||
ret | |||
#include "core.asm" |