collapseos/apps/zasm/main.asm

213 lines
4.8 KiB
NASM
Raw Normal View History

2019-05-11 10:32:05 +10:00
; 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.
;
2019-05-10 04:09:40 +10:00
; *** Requirements ***
; blockdev
2019-05-10 04:09:40 +10:00
; JUMP_STRNCMP
; JUMP_ADDDE
2019-05-10 11:21:08 +10:00
; JUMP_ADDHL
2019-05-10 04:09:40 +10:00
; JUMP_UPCASE
; JUMP_UNSETZ
; JUMP_INTODE
2019-05-13 12:07:21 +10:00
; JUMP_INTOHL
2019-05-10 11:21:08 +10:00
; JUMP_FINDCHAR
; JUMP_BLKSEL
2019-05-10 11:21:08 +10:00
; RAMSTART (where we put our variables in RAM)
2019-05-01 05:51:39 +10:00
2019-05-11 10:32:05 +10:00
; *** 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
; The offset where we currently are with regards to outputting opcodes
.equ ZASM_PC ZASM_FIRST_PASS+1
.equ ZASM_RAMEND ZASM_PC+2
2019-05-11 10:32:05 +10:00
; *** Code ***
jp zasmMain
#include "util.asm"
2019-05-11 10:32:05 +10:00
.equ IO_RAMSTART ZASM_RAMEND
#include "io.asm"
2019-05-11 11:19:34 +10:00
#include "tok.asm"
#include "parse.asm"
2019-05-15 05:26:29 +10:00
#include "expr.asm"
#include "instr.asm"
2019-05-12 12:11:05 +10:00
.equ DIREC_RAMSTART IO_RAMEND
#include "directive.asm"
2019-05-12 12:11:05 +10:00
.equ SYM_RAMSTART DIREC_RAMEND
#include "symbol.asm"
; Read file through blockdev ID in H and outputs its upcodes through blockdev
; ID in L.
2019-05-11 10:32:05 +10:00
zasmMain:
2019-05-14 10:23:10 +10:00
; Init I/O
ld a, h
ld de, IO_IN_GETC
call JUMP_BLKSEL
ld a, l
ld de, IO_OUT_GETC
call JUMP_BLKSEL
2019-05-14 10:23:10 +10:00
; Init modules
call symInit
2019-05-11 10:32:05 +10:00
; First pass
ld a, 1
ld (ZASM_FIRST_PASS), a
call zasmParseFile
ret nz
2019-05-11 10:32:05 +10:00
; Second pass
ld hl, 0
call ioSeek
2019-05-11 10:32:05 +10:00
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
2019-05-01 05:51:39 +10:00
ret
; Increase (ZASM_PC) by A
2019-05-10 11:21:08 +10:00
incOutputOffset:
push de
ld de, (ZASM_PC)
2019-05-10 11:21:08 +10:00
call JUMP_ADDDE
ld (ZASM_PC), de
2019-05-10 11:21:08 +10:00
pop de
ret
; Repeatedly reads lines from IO, assemble them and spit the binary code in
; IO. Z is set on success, unset on error. DE contains the last line number to
; be read (first line is 1).
2019-05-11 10:32:05 +10:00
zasmParseFile:
ld de, 0
ld (ZASM_PC), de
2019-05-11 10:32:05 +10:00
.loop:
inc de
2019-05-11 10:32:05 +10:00
call ioReadLine
or a ; is A 0?
ret z ; We have EOF
call parseLine
ret nz ; error
2019-05-11 10:32:05 +10:00
jr .loop
; Parse line in (HL), write the resulting opcode(s) through ioPutC and increases
2019-05-16 05:16:35 +10:00
; (ZASM_PC) by the number of bytes written. BC is set to the result of the call
; to tokenize.
; Sets Z if parse was successful, unset if there was an error or EOF.
2019-05-01 05:51:39 +10:00
parseLine:
call tokenize
2019-05-01 12:27:11 +10:00
ld a, b ; TOK_*
2019-05-01 11:40:22 +10:00
cp TOK_INSTR
2019-05-16 05:16:35 +10:00
jp z, _parseInstr
2019-05-02 01:26:41 +10:00
cp TOK_DIRECTIVE
2019-05-16 05:16:35 +10:00
jp z, _parseDirec
2019-05-10 11:21:08 +10:00
cp TOK_LABEL
jr z, .label
cp TOK_EMPTY
2019-05-16 05:16:35 +10:00
ret ; Z is correct. If empty, Z is set and not an
2019-05-15 04:32:12 +10:00
; error, otherwise, it means bad token and
; errors out.
.label:
push hl
2019-05-15 04:32:12 +10:00
call _parseLabel
pop hl
2019-05-16 05:16:35 +10:00
ret nz ; error out
; We're finished here. However, because it's a label, it's possible that
; another logical line follows directly after the label. Let's parse
; this and propagate error.
call parseLine
2019-05-16 05:16:35 +10:00
; Z has proper value
2019-05-15 04:32:12 +10:00
ret
_parseInstr:
2019-05-01 12:27:11 +10:00
ld a, c ; I_*
2019-05-01 11:40:22 +10:00
call parseInstruction
2019-05-01 05:51:39 +10:00
or a ; is zero?
jr z, .error
2019-05-11 10:32:05 +10:00
ld b, a ; save output byte count
2019-05-10 11:21:08 +10:00
call incOutputOffset
2019-05-11 10:32:05 +10:00
call zasmIsFirstPass
jr z, .success ; first pass, nothing to write
2019-05-02 01:26:41 +10:00
ld hl, instrUpcode
.loopInstr:
ld a, (hl)
call ioPutC
inc hl
djnz .loopInstr
2019-05-15 04:32:12 +10:00
; continue to success
.success:
xor a ; ensure Z
ret
.error:
call JUMP_UNSETZ
ret
_parseDirec:
2019-05-02 01:26:41 +10:00
ld a, c ; D_*
call parseDirective
2019-05-12 12:11:05 +10:00
or a ; cp 0
jr z, .success ; if zero, shortcut through
2019-05-11 10:32:05 +10:00
ld b, a ; save output byte count
2019-05-10 11:21:08 +10:00
call incOutputOffset
2019-05-11 10:32:05 +10:00
call zasmIsFirstPass
jr z, .success ; first pass, nothing to write
2019-05-02 01:26:41 +10:00
ld hl, direcData
.loopDirec:
ld a, (hl)
call ioPutC
inc hl
djnz .loopDirec
2019-05-15 04:32:12 +10:00
; continue to success
.success:
xor a ; ensure Z
ret
_parseLabel:
2019-05-10 11:21:08 +10:00
; The string in (scratchpad) is a label with its trailing ':' removed.
ld hl, scratchpad
2019-05-14 10:23:10 +10:00
call zasmIsFirstPass
jr z, .registerLabel ; When we encounter a label in the first
; pass, we register it in the symbol
; list
; When we're not in the first pass, we set the context (if label is not
; local) to that label.
call symIsLabelLocal
jr z, .success ; local? don't set context
2019-05-14 10:23:10 +10:00
call symSetContext
jr z, .success
; NZ? this means that (HL) couldn't be found in symbol list. Weird
2019-05-14 10:23:10 +10:00
jr .error
.registerLabel:
ld de, (ZASM_PC)
2019-05-10 11:21:08 +10:00
call symRegister
2019-05-14 10:23:10 +10:00
jr nz, .error
; continue to .success
.success:
xor a ; ensure Z
2019-05-15 04:32:12 +10:00
ret
2019-05-01 05:51:39 +10:00
.error:
call JUMP_UNSETZ
2019-05-01 05:51:39 +10:00
ret