1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-07-23 09:10:22 +10:00
collapseos/apps/zasm/main.asm
Virgil Dupras ae028e3a86 blockdev: make implementors "random access"
This huge refactoring remove the Seek and Tell routine from blockdev
implementation requirements and change GetC and PutC's API so that they
take an address to read and write (through HL/DE) at each call.

The "PTR" approach in blockdev implementation was very redundant from
device to device and it made more sense to generalize. It's possible
that future device aren't "random access", but we'll be able to add more
device types later.

Another important change in this commit is that the "blockdev handle" is
now opaque. Previously, consumers of the API would happily call routines
directly from one of the 4 offsets. We can't do that any more. This
makes the API more solid for future improvements.

This change forced me to change a lot of things in fs, but overall,
things are now simpler. No more `FS_PTR`: the "device handle" now holds
the active pointer.

Lots, lots of changes, but it also feels a lot cleaner and solid.
2019-06-04 15:36:20 -04:00

222 lines
5.2 KiB
NASM

; *** 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 ZASM_RAMSTART
; whether we're in "local pass", that is, in local label scanning mode. During
; this special pass, ZASM_FIRST_PASS will also be set so that the rest of the
; code behaves as is we were in the first pass.
.equ ZASM_LOCAL_PASS ZASM_FIRST_PASS+1
; What IO_PC was when we started our context
.equ ZASM_CTX_PC ZASM_LOCAL_PASS+1
; current ".org" offset, that is, what we must offset all our label by.
.equ ZASM_ORG ZASM_CTX_PC+2
.equ ZASM_RAMEND ZASM_ORG+2
; Takes 2 byte arguments, blkdev in and blkdev out, expressed as IDs.
; Read file through blkdev in and outputs its upcodes through blkdev out.
; HL is set to the last lineno to be read.
; Sets Z on success, unset on error. On error, A contains an error code (ERR_*)
zasmMain:
; Parse args. HL points to string already
; We don't allocate memory just to hold this. Because this happens
; before initialization, we don't really care where those args are
; parsed.
ld de, .argspecs
ld ix, ZASM_RAMSTART
call parseArgs
jr z, .goodargs
; bad args
ld hl, 0
ld de, 0
ld a, SHELL_ERR_BAD_ARGS
ret
.goodargs:
; HL now points to parsed args
; Init I/O
ld a, (ZASM_RAMSTART) ; blkdev in ID
ld de, IO_IN_BLK
call blkSel
ld a, (ZASM_RAMSTART+1) ; blkdev out ID
ld de, IO_OUT_BLK
call blkSel
; Init modules
xor a
ld (ZASM_LOCAL_PASS), a
ld (ZASM_ORG), a
ld (ZASM_ORG+1), a
call ioInit
call symInit
; First pass
ld a, 1
ld (ZASM_FIRST_PASS), a
call zasmParseFile
jr nz, .end
; Second pass
xor a
ld (ZASM_FIRST_PASS), a
call zasmParseFile
.end:
jp ioLineNo ; --> HL, --> DE, returns
.argspecs:
.db 0b001, 0b001, 0
; Sets Z according to whether we're in first pass.
zasmIsFirstPass:
ld a, (ZASM_FIRST_PASS)
cp 1
ret
; Sets Z according to whether we're in local pass.
zasmIsLocalPass:
ld a, (ZASM_LOCAL_PASS)
cp 1
ret
; Set ZASM_ORG to specified number in HL
zasmSetOrg:
ld (ZASM_ORG), hl
ret
; Return current PC (properly .org offsetted) in HL
zasmGetPC:
push de
ld hl, (ZASM_ORG)
ld de, (IO_PC)
add hl, de
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).
zasmParseFile:
call ioRewind
.loop:
call parseLine
ret nz ; error
ld a, b ; TOK_*
cp TOK_EOF
jr z, .eof
jr .loop
.eof:
call zasmIsLocalPass
jr nz, .end ; EOF and not local pass
; we're in local pass and EOF. Unwind this
call _endLocalPass
jr .loop
.end:
cp a ; ensure Z
ret
; Parse next token and accompanying args (when relevant) in I/O, write the
; resulting opcode(s) through ioPutC and increases (IO_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. EOF is not an
; error. If there is an error, A is set to the corresponding error code (ERR_*).
parseLine:
call tokenize
ld a, b ; TOK_*
cp TOK_INSTR
jp z, _parseInstr
cp TOK_DIRECTIVE
jp z, _parseDirec
cp TOK_LABEL
jr z, _parseLabel
cp TOK_EOF
ret z ; We're finished, no error.
; Bad token
ld a, ERR_UNKNOWN
jp unsetZ ; return with Z unset
_parseInstr:
ld a, c ; I_*
jp parseInstruction
_parseDirec:
ld a, c ; D_*
jp parseDirective
_parseLabel:
; The string in (scratchpad) is a label with its trailing ':' removed.
ld hl, scratchpad
call zasmIsLocalPass
jr z, .processLocalPass
; Is this a local label? If yes, we don't process it in the context of
; parseLine, whether it's first or second pass. Local labels are only
; parsed during the Local Pass
call symIsLabelLocal
jr z, .success ; local? don't do anything.
call zasmIsFirstPass
jr z, .registerLabel ; When we encounter a label in the first
; pass, we register it in the symbol
; list
; At this point, we're in second pass, we've encountered a global label
; and we'll soon continue processing our file. However, before we do
; that, we should process our local labels.
call _beginLocalPass
jr .success
.processLocalPass:
call symIsLabelLocal
jr z, .registerLabel ; local label? all good, register it
; normally
; not a local label? Then we need to end local pass
call _endLocalPass
jr .success
.registerLabel:
push hl
call zasmGetPC
ex de, hl
pop hl
call symRegister
jr nz, .error
; continue to .success
.success:
xor a ; ensure Z
ret
.error:
call unsetZ
ret
_beginLocalPass:
; remember were I/O was
call ioSavePos
; Remember where PC was
ld hl, (IO_PC)
ld (ZASM_CTX_PC), hl
; Fake first pass
ld a, 1
ld (ZASM_FIRST_PASS), a
; Set local pass
ld (ZASM_LOCAL_PASS), a
; Empty local label registry
xor a
ld (SYM_LOC_NAMES), a
call symSelectLocalRegistry
ret
_endLocalPass:
call symSelectGlobalRegistry
; recall I/O pos
call ioRecallPos
; recall PC
ld hl, (ZASM_CTX_PC)
ld (IO_PC), hl
; unfake first pass
xor a
ld (ZASM_FIRST_PASS), a
; Unset local pass
ld (ZASM_LOCAL_PASS), a
cp a ; ensure Z
ret