mirror of
https://github.com/hsoft/collapseos.git
synced 2025-02-19 04:46: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 ***
|
; *** Requirements ***
|
||||||
|
; blockdev
|
||||||
; JUMP_STRNCMP
|
; JUMP_STRNCMP
|
||||||
; JUMP_ADDDE
|
; JUMP_ADDDE
|
||||||
; JUMP_UPCASE
|
; JUMP_UPCASE
|
||||||
@ -6,23 +7,23 @@
|
|||||||
; JUMP_INTODE
|
; JUMP_INTODE
|
||||||
|
|
||||||
; *** Code ***
|
; *** Code ***
|
||||||
; Parse asm file in (HL) and outputs its upcodes in (DE). Returns the number
|
; Read file through GetC routine pointer at HL and outputs its upcodes through
|
||||||
; of bytes written in C.
|
; the PutC routine pointer at DE.
|
||||||
main:
|
main:
|
||||||
ld bc, 0 ; C is our written bytes counter
|
ld (ioGetCPtr), hl
|
||||||
|
ld (ioPutCPtr), de
|
||||||
.loop:
|
.loop:
|
||||||
|
call ioReadLine
|
||||||
|
or a ; is A 0?
|
||||||
|
jr z, .stop ; We have EOF
|
||||||
call parseLine
|
call parseLine
|
||||||
jr nz, .stop
|
jr nz, .stop
|
||||||
ld a, c
|
|
||||||
add a, ixl
|
|
||||||
ld c, a
|
|
||||||
call gotoNextLine
|
|
||||||
jr nz, .stop ; error? stop
|
|
||||||
jr .loop
|
jr .loop
|
||||||
.stop:
|
.stop:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
#include "util.asm"
|
#include "util.asm"
|
||||||
|
#include "io.asm"
|
||||||
#include "parse.asm"
|
#include "parse.asm"
|
||||||
#include "literal.asm"
|
#include "literal.asm"
|
||||||
#include "instr.asm"
|
#include "instr.asm"
|
||||||
@ -36,38 +37,38 @@ main:
|
|||||||
parseLine:
|
parseLine:
|
||||||
push bc
|
push bc
|
||||||
|
|
||||||
call gotoNextNotBlankLine
|
|
||||||
jr nz, .error
|
|
||||||
call tokenize
|
call tokenize
|
||||||
ld a, b ; TOK_*
|
ld a, b ; TOK_*
|
||||||
cp TOK_INSTR
|
cp TOK_INSTR
|
||||||
jr z, .instr
|
jr z, .instr
|
||||||
cp TOK_DIRECTIVE
|
cp TOK_DIRECTIVE
|
||||||
jr z, .direc
|
jr z, .direc
|
||||||
|
cp TOK_EMPTY
|
||||||
|
jr z, .success ; empty line? do nothing but don't error out.
|
||||||
jr .error ; token not supported
|
jr .error ; token not supported
|
||||||
.instr:
|
.instr:
|
||||||
ld a, c ; I_*
|
ld a, c ; I_*
|
||||||
call parseInstruction
|
call parseInstruction
|
||||||
or a ; is zero?
|
or a ; is zero?
|
||||||
jr z, .error
|
jr z, .error
|
||||||
ld b, 0
|
ld b, a
|
||||||
ld c, a ; written bytes
|
|
||||||
push hl
|
|
||||||
ld hl, instrUpcode
|
ld hl, instrUpcode
|
||||||
call copy
|
.loopInstr:
|
||||||
pop hl
|
ld a, (hl)
|
||||||
call JUMP_ADDDE
|
call ioPutC
|
||||||
|
inc hl
|
||||||
|
djnz .loopInstr
|
||||||
jr .success
|
jr .success
|
||||||
.direc:
|
.direc:
|
||||||
ld a, c ; D_*
|
ld a, c ; D_*
|
||||||
call parseDirective
|
call parseDirective
|
||||||
ld b, 0
|
ld b, a
|
||||||
ld c, a ; written bytes
|
|
||||||
push hl
|
|
||||||
ld hl, direcData
|
ld hl, direcData
|
||||||
call copy
|
.loopDirec:
|
||||||
pop hl
|
ld a, (hl)
|
||||||
call JUMP_ADDDE
|
call ioPutC
|
||||||
|
inc hl
|
||||||
|
djnz .loopDirec
|
||||||
jr .success
|
jr .success
|
||||||
.success:
|
.success:
|
||||||
ld ixl, a
|
ld ixl, a
|
||||||
|
@ -6,19 +6,11 @@ scratchpad:
|
|||||||
|
|
||||||
; *** Code ***
|
; *** Code ***
|
||||||
|
|
||||||
; Sets Z is A is ';', CR, LF, or null.
|
; Sets Z is A is ';' or null.
|
||||||
isLineEndOrComment:
|
isLineEndOrComment:
|
||||||
cp ';'
|
cp ';'
|
||||||
ret z
|
ret z
|
||||||
; Continues onto isLineEnd...
|
or a ; cp 0
|
||||||
|
|
||||||
; 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
|
ret
|
||||||
|
|
||||||
; Sets Z is A is ' ' '\t' or ','
|
; Sets Z is A is ' ' '\t' or ','
|
||||||
@ -81,48 +73,3 @@ toWord:
|
|||||||
.success:
|
.success:
|
||||||
xor a ; ensure Z
|
xor a ; ensure Z
|
||||||
ret
|
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 ***
|
; *** Consts ***
|
||||||
TOK_INSTR .equ 0x01
|
TOK_INSTR .equ 0x01
|
||||||
TOK_DIRECTIVE .equ 0x02
|
TOK_DIRECTIVE .equ 0x02
|
||||||
|
TOK_EMPTY .equ 0xfe ; not a bad token, just an empty line
|
||||||
TOK_BAD .equ 0xff
|
TOK_BAD .equ 0xff
|
||||||
|
|
||||||
; *** Code ***
|
; *** Code ***
|
||||||
@ -14,6 +15,7 @@ TOK_BAD .equ 0xff
|
|||||||
; If no token matches, TOK_BAD is written to B
|
; If no token matches, TOK_BAD is written to B
|
||||||
tokenize:
|
tokenize:
|
||||||
call toWord
|
call toWord
|
||||||
|
jr nz, .emptyline
|
||||||
call readWord
|
call readWord
|
||||||
push hl ; Save advanced HL for later
|
push hl ; Save advanced HL for later
|
||||||
ld hl, scratchpad
|
ld hl, scratchpad
|
||||||
@ -33,3 +35,7 @@ tokenize:
|
|||||||
ld c, a
|
ld c, a
|
||||||
pop hl
|
pop hl
|
||||||
ret
|
ret
|
||||||
|
.emptyline:
|
||||||
|
ld b, TOK_EMPTY
|
||||||
|
; no HL to pop, we jumped before the push
|
||||||
|
ret
|
||||||
|
@ -8,27 +8,6 @@ rlaX:
|
|||||||
djnz .loop
|
djnz .loop
|
||||||
ret
|
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:
|
callHL:
|
||||||
jp (hl)
|
jp (hl)
|
||||||
ret
|
ret
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
TARGETS = shell zasm
|
TARGETS = shell zasm
|
||||||
KERNEL_HEADERS = shell-kernel.h zasm-kernel.h
|
KERNEL_HEADERS = shell-kernel.h zasm-kernel.h
|
||||||
|
USER_HEADERS = zasm-user.h
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
@ -23,4 +24,4 @@ libz80/libz80.o: libz80/z80.c
|
|||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGETS) $(KERNEL_HEADERS)
|
rm -f $(TARGETS) $(KERNEL_HEADERS) $(USER_HEADERS)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
; Glue code for the emulated environment
|
; Glue code for the emulated environment
|
||||||
.equ USER_CODE 0x4000
|
.equ USER_CODE 0x4000
|
||||||
.equ RAMEND 0xffff
|
.equ RAMEND 0xffff
|
||||||
.equ ZASM_INPUT 0xa000
|
|
||||||
.equ ZASM_OUTPUT 0xd000
|
|
||||||
.equ STDIO_PORT 0x00
|
.equ STDIO_PORT 0x00
|
||||||
|
|
||||||
jr init ; 2 bytes
|
jr init ; 2 bytes
|
||||||
@ -17,35 +15,24 @@ init:
|
|||||||
di
|
di
|
||||||
ld hl, RAMEND
|
ld hl, RAMEND
|
||||||
ld sp, hl
|
ld sp, hl
|
||||||
ld hl, ZASM_INPUT
|
ld hl, emulGetC
|
||||||
; yes, this means that input can't have null bytes
|
ld de, emulPutC
|
||||||
.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
|
|
||||||
call USER_CODE
|
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
|
; signal the emulator we're done
|
||||||
halt
|
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"
|
#include "core.asm"
|
||||||
|
Loading…
Reference in New Issue
Block a user