1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-12-24 14:28:06 +11:00

zasm: read input in two passes

This commit is contained in:
Virgil Dupras 2019-05-10 20:32:05 -04:00
parent 6a804a9c64
commit c239ec7dea
4 changed files with 83 additions and 19 deletions

View File

@ -22,6 +22,11 @@ ioPutC:
ld ix, (IO_OUT_PUTC)
jp (ix)
ioRewind:
ld hl, 0
ld ix, (IO_IN_SEEK)
jp (ix)
; Sets Z is A is CR, LF, or null.
isLineEnd:
or a ; same as cp 0

View File

@ -1,3 +1,22 @@
; zasm
;
; Reads input from specified blkdev ID, assemble the binary in two passes and
; spit the result in another specified blkdev ID.
;
; We don't buffer the whole source in memory, so we need our input blkdev to
; support Seek so we can read the file a second time. So, for input, we need
; GetC and Seek.
;
; For output, we only need PutC. Output doesn't start until the second pass.
;
; The goal of the second pass is to assign values to all symbols so that we
; can have forward references (instructions referencing a label that happens
; later).
;
; Labels and constants are both treated the same way, that is, they can be
; forward-referenced in instructions. ".equ" directives, however, are evaluated
; during the first pass so forward references are not allowed.
;
; *** Requirements ***
; blockdev
; JUMP_STRNCMP
@ -10,9 +29,20 @@
; JUMP_BLKSEL
; RAMSTART (where we put our variables in RAM)
jp main
; *** Variables ***
; A bool flag indicating that we're on first pass. When we are, we don't care
; about actual output, but only about the length of each upcode. This means
; that when we parse instructions and directive that error out because of a
; missing symbol, we don't error out and just write down a dummy value.
.equ ZASM_FIRST_PASS RAMSTART
.equ ZASM_RAMEND ZASM_FIRST_PASS+1
; *** Code ***
jp zasmMain
#include "util.asm"
.equ IO_RAMSTART RAMSTART
.equ IO_RAMSTART ZASM_RAMEND
#include "io.asm"
#include "parse.asm"
#include "literal.asm"
@ -21,26 +51,30 @@ jp main
.equ SYM_RAMSTART IO_RAMEND
#include "symbol.asm"
; *** Code ***
; Read file through blockdev ID in H and outputs its upcodes through blockdev
; ID in L.
main:
zasmMain:
ld a, h
ld de, IO_IN_GETC
call JUMP_BLKSEL
ld a, l
ld de, IO_OUT_GETC
call JUMP_BLKSEL
ld hl, 0
ld (curOutputOffset), hl
.loop:
call ioReadLine
or a ; is A 0?
jr z, .stop ; We have EOF
call parseLine
jr nz, .stop
jr .loop
.stop:
; First pass
ld a, 1
ld (ZASM_FIRST_PASS), a
call zasmParseFile
; Second pass
call ioRewind
xor a
ld (ZASM_FIRST_PASS), a
call zasmParseFile
ret
; Sets Z according to whether we're in first pass.
zasmIsFirstPass:
ld a, (ZASM_FIRST_PASS)
cp 1
ret
; Increase (curOutputOffset) by A
@ -52,6 +86,17 @@ incOutputOffset:
pop de
ret
zasmParseFile:
ld hl, 0
ld (curOutputOffset), hl
.loop:
call ioReadLine
or a ; is A 0?
ret z ; We have EOF
call parseLine
ret nz
jr .loop
; Parse line in (HL), write the resulting opcode(s) in (DE) and increases
; (curOutputOffset) by the number of bytes written. Advances HL where
; tokenization stopped and DE to where we should write the next upcode.
@ -75,8 +120,10 @@ parseLine:
call parseInstruction
or a ; is zero?
jr z, .error
ld b, a ; save output byte count
call incOutputOffset
ld b, a
call zasmIsFirstPass
jr z, .success ; first pass, nothing to write
ld hl, instrUpcode
.loopInstr:
ld a, (hl)
@ -87,8 +134,10 @@ parseLine:
.direc:
ld a, c ; D_*
call parseDirective
ld b, a ; save output byte count
call incOutputOffset
ld b, a
call zasmIsFirstPass
jr z, .success ; first pass, nothing to write
ld hl, direcData
.loopDirec:
ld a, (hl)
@ -97,6 +146,8 @@ parseLine:
djnz .loopDirec
jr .success
.label:
call zasmIsFirstPass
jr nz, .success ; not in first pass? nothing to do
; The string in (scratchpad) is a label with its trailing ':' removed.
ld hl, scratchpad
ld de, (curOutputOffset)

View File

@ -4,9 +4,12 @@
#include "zasm-kernel.h"
#include "zasm-user.h"
/* zasm is a "pure memory" application. It starts up being told memory location
* to read and memory location to write.
/* zasm reads from a specified blkdev, assemble the file and writes the result
* in another specified blkdev. In our emulator layer, we use stdin and stdout
* as those specified blkdevs.
*
* Because the input blkdev needs support for Seek, we buffer it in the emulator
* layer.
*
* Memory layout:
*

View File

@ -2,6 +2,7 @@
.equ RAMSTART 0x4000
.equ USER_CODE 0x4800
.equ STDIO_PORT 0x00
.equ STDIN_REWIND 0x01
jr init ; 2 bytes
; *** JUMP TABLE ***
@ -40,10 +41,14 @@ emulPutC:
out (STDIO_PORT), a
ret
emulSeek:
out (STDIN_REWIND), a
ret
#include "core.asm"
.equ BLOCKDEV_RAMSTART RAMSTART
.equ BLOCKDEV_COUNT 2
#include "blockdev.asm"
; List of devices
.dw emulGetC, 0, 0, 0
.dw emulGetC, 0, emulSeek, 0
.dw 0, emulPutC, 0, 0