mirror of
https://github.com/hsoft/collapseos.git
synced 2024-12-24 14:28:06 +11:00
zasm: Read from and write to streams instead of memory
This commit is contained in:
parent
7f27d63c19
commit
d34aff67bb
69
apps/zasm/io.asm
Normal file
69
apps/zasm/io.asm
Normal file
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user