|
|
@@ -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) |
|
|
|