2019-05-01 11:40:22 +10:00
|
|
|
; *** CONSTS ***
|
|
|
|
|
2019-05-02 04:07:01 +10:00
|
|
|
.equ D_DB 0x00
|
|
|
|
.equ D_DW 0x01
|
2019-05-12 12:11:05 +10:00
|
|
|
.equ D_EQU 0x02
|
2019-05-20 03:22:14 +10:00
|
|
|
.equ D_ORG 0x03
|
2019-05-20 22:20:00 +10:00
|
|
|
.equ D_FIL 0x04
|
2019-06-04 01:13:31 +10:00
|
|
|
.equ D_OUT 0x05
|
|
|
|
.equ D_INC 0x06
|
2019-07-22 02:58:02 +10:00
|
|
|
.equ D_BIN 0x07
|
2019-05-02 04:07:01 +10:00
|
|
|
.equ D_BAD 0xff
|
2019-05-01 11:40:22 +10:00
|
|
|
|
2019-05-12 12:11:05 +10:00
|
|
|
; *** Variables ***
|
2019-10-05 10:26:21 +10:00
|
|
|
; Result of the last .equ evaluation. Used for "@" symbol.
|
|
|
|
.equ DIREC_LASTVAL DIREC_RAMSTART
|
|
|
|
.equ DIREC_SCRATCHPAD DIREC_LASTVAL+2
|
2019-05-12 12:11:05 +10:00
|
|
|
.equ DIREC_RAMEND DIREC_SCRATCHPAD+SCRATCHPAD_SIZE
|
2019-05-01 11:40:22 +10:00
|
|
|
; *** CODE ***
|
|
|
|
|
2019-10-07 05:39:47 +11:00
|
|
|
; 3 bytes per row, fill with zero
|
2019-05-01 11:40:22 +10:00
|
|
|
directiveNames:
|
2019-10-07 05:39:47 +11:00
|
|
|
.db "DB", 0
|
|
|
|
.db "DW", 0
|
|
|
|
.db "EQU"
|
|
|
|
.db "ORG"
|
|
|
|
.db "FIL"
|
|
|
|
.db "OUT"
|
|
|
|
.db "INC"
|
|
|
|
.db "BIN"
|
2019-05-01 11:40:22 +10:00
|
|
|
|
2019-05-02 01:26:41 +10:00
|
|
|
; This is a list of handlers corresponding to indexes in directiveNames
|
|
|
|
directiveHandlers:
|
|
|
|
.dw handleDB
|
2019-05-02 04:07:01 +10:00
|
|
|
.dw handleDW
|
2019-05-12 12:11:05 +10:00
|
|
|
.dw handleEQU
|
2019-05-20 03:22:14 +10:00
|
|
|
.dw handleORG
|
2019-05-20 22:20:00 +10:00
|
|
|
.dw handleFIL
|
2019-06-04 01:13:31 +10:00
|
|
|
.dw handleOUT
|
2019-05-17 11:15:00 +10:00
|
|
|
.dw handleINC
|
2019-07-22 02:58:02 +10:00
|
|
|
.dw handleBIN
|
2019-05-02 01:26:41 +10:00
|
|
|
|
|
|
|
handleDB:
|
|
|
|
push hl
|
2019-05-18 05:39:28 +10:00
|
|
|
.loop:
|
2019-05-02 01:26:41 +10:00
|
|
|
call readWord
|
2019-05-28 03:44:53 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-02 01:26:41 +10:00
|
|
|
ld hl, scratchpad
|
2019-05-18 06:17:22 +10:00
|
|
|
call enterDoubleQuotes
|
|
|
|
jr z, .stringLiteral
|
2019-05-18 07:21:49 +10:00
|
|
|
call parseExpr
|
2019-05-28 03:44:53 +10:00
|
|
|
jr nz, .badarg
|
2019-05-19 10:37:34 +10:00
|
|
|
push ix \ pop hl
|
2019-05-28 03:44:53 +10:00
|
|
|
ld a, h
|
|
|
|
or a ; cp 0
|
|
|
|
jr nz, .overflow ; not zero? overflow
|
2019-05-19 10:37:34 +10:00
|
|
|
ld a, l
|
2019-05-18 05:14:38 +10:00
|
|
|
call ioPutC
|
2019-06-16 06:48:30 +10:00
|
|
|
jr nz, .ioError
|
2019-05-18 06:17:22 +10:00
|
|
|
.stopStrLit:
|
2019-05-18 05:39:28 +10:00
|
|
|
call readComma
|
|
|
|
jr z, .loop
|
2019-05-28 03:44:53 +10:00
|
|
|
cp a ; ensure Z
|
|
|
|
pop hl
|
|
|
|
ret
|
2019-06-16 06:48:30 +10:00
|
|
|
.ioError:
|
|
|
|
ld a, SHELL_ERR_IO_ERROR
|
|
|
|
jr .error
|
2019-05-28 03:44:53 +10:00
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badarg:
|
|
|
|
ld a, ERR_BAD_ARG
|
|
|
|
jr .error
|
|
|
|
.overflow:
|
|
|
|
ld a, ERR_OVFL
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
2019-05-02 01:26:41 +10:00
|
|
|
pop hl
|
|
|
|
ret
|
2019-05-28 03:44:53 +10:00
|
|
|
|
2019-05-18 06:17:22 +10:00
|
|
|
.stringLiteral:
|
|
|
|
ld a, (hl)
|
|
|
|
inc hl
|
|
|
|
or a ; when we encounter 0, that was what used to
|
|
|
|
jr z, .stopStrLit ; be our closing quote. Stop.
|
|
|
|
; Normal character, output
|
|
|
|
call ioPutC
|
2019-06-20 01:42:39 +10:00
|
|
|
jr nz, .ioError
|
2019-05-18 06:17:22 +10:00
|
|
|
jr .stringLiteral
|
2019-05-02 01:26:41 +10:00
|
|
|
|
2019-05-02 04:07:01 +10:00
|
|
|
handleDW:
|
|
|
|
push hl
|
2019-05-18 05:39:28 +10:00
|
|
|
.loop:
|
2019-05-02 04:07:01 +10:00
|
|
|
call readWord
|
2019-05-28 03:52:58 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-02 04:07:01 +10:00
|
|
|
ld hl, scratchpad
|
2019-05-15 05:26:29 +10:00
|
|
|
call parseExpr
|
2019-05-28 03:52:58 +10:00
|
|
|
jr nz, .badarg
|
2019-05-18 05:14:38 +10:00
|
|
|
push ix \ pop hl
|
|
|
|
ld a, l
|
|
|
|
call ioPutC
|
2019-06-20 01:42:39 +10:00
|
|
|
jr nz, .ioError
|
2019-05-18 05:14:38 +10:00
|
|
|
ld a, h
|
|
|
|
call ioPutC
|
2019-06-20 01:42:39 +10:00
|
|
|
jr nz, .ioError
|
2019-05-18 05:39:28 +10:00
|
|
|
call readComma
|
|
|
|
jr z, .loop
|
2019-05-28 03:44:53 +10:00
|
|
|
cp a ; ensure Z
|
2019-05-02 04:07:01 +10:00
|
|
|
pop hl
|
|
|
|
ret
|
2019-06-20 01:42:39 +10:00
|
|
|
.ioError:
|
|
|
|
ld a, SHELL_ERR_IO_ERROR
|
|
|
|
jr .error
|
2019-05-28 03:52:58 +10:00
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badarg:
|
|
|
|
ld a, ERR_BAD_ARG
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
|
|
|
pop hl
|
|
|
|
ret
|
2019-05-02 01:26:41 +10:00
|
|
|
|
2019-05-12 12:11:05 +10:00
|
|
|
handleEQU:
|
2019-05-28 07:45:05 +10:00
|
|
|
call zasmIsLocalPass ; Are we in local pass? Then ignore all .equ.
|
|
|
|
jr z, .skip ; they mess up duplicate symbol detection.
|
2019-07-23 12:49:43 +10:00
|
|
|
; We register constants on both first and second pass for one little
|
|
|
|
; reason: .org. Normally, we'd register constants on second pass only
|
|
|
|
; so that we have values for forward label references, but we need .org
|
|
|
|
; to be effective during the first pass and .org needs to support
|
|
|
|
; expressions. So, we double-parse .equ, clearing the const registry
|
|
|
|
; before the second pass.
|
2019-05-12 12:11:05 +10:00
|
|
|
push hl
|
|
|
|
push de
|
|
|
|
push bc
|
|
|
|
; Read our constant name
|
|
|
|
call readWord
|
2019-05-28 04:07:07 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-12 12:11:05 +10:00
|
|
|
; We can't register our symbol yet: we don't have our value!
|
|
|
|
; Let's copy it over.
|
2019-05-16 21:53:42 +10:00
|
|
|
ld de, DIREC_SCRATCHPAD
|
|
|
|
ld bc, SCRATCHPAD_SIZE
|
|
|
|
ldir
|
2019-05-12 12:11:05 +10:00
|
|
|
|
|
|
|
; Now, read the value associated to it
|
|
|
|
call readWord
|
2019-05-28 04:07:07 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-12 12:11:05 +10:00
|
|
|
ld hl, scratchpad
|
2019-05-15 05:26:29 +10:00
|
|
|
call parseExpr
|
2019-05-28 04:07:07 +10:00
|
|
|
jr nz, .badarg
|
2019-05-12 12:11:05 +10:00
|
|
|
ld hl, DIREC_SCRATCHPAD
|
2019-05-19 10:37:34 +10:00
|
|
|
push ix \ pop de
|
2019-10-05 10:26:21 +10:00
|
|
|
; Save value in "@" special variable
|
|
|
|
ld (DIREC_LASTVAL), de
|
2019-07-23 12:23:57 +10:00
|
|
|
call symRegisterConst ; A and Z set
|
2019-07-24 06:01:23 +10:00
|
|
|
jr z, .end ; success
|
|
|
|
; register ended up in error. We need to figure which error. If it's
|
|
|
|
; a duplicate error, we ignore it and return success because, as per
|
|
|
|
; ".equ" policy, it's fine to define the same const twice. The first
|
|
|
|
; value has precedence.
|
|
|
|
cp ERR_DUPSYM
|
|
|
|
; whatever the value of Z, it's the good one, return
|
2019-05-28 04:07:07 +10:00
|
|
|
jr .end
|
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badarg:
|
|
|
|
ld a, ERR_BAD_ARG
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
2019-05-12 12:11:05 +10:00
|
|
|
.end:
|
|
|
|
pop bc
|
|
|
|
pop de
|
|
|
|
pop hl
|
|
|
|
ret
|
2019-05-28 07:45:05 +10:00
|
|
|
.skip:
|
|
|
|
; consume args and return
|
|
|
|
call readWord
|
|
|
|
jp readWord
|
2019-05-12 12:11:05 +10:00
|
|
|
|
2019-05-20 03:22:14 +10:00
|
|
|
handleORG:
|
|
|
|
call readWord
|
2019-05-28 04:16:40 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-20 03:22:14 +10:00
|
|
|
call parseExpr
|
2019-05-28 04:16:40 +10:00
|
|
|
jr nz, .badarg
|
2019-05-20 03:22:14 +10:00
|
|
|
push ix \ pop hl
|
2019-05-28 03:44:53 +10:00
|
|
|
call zasmSetOrg
|
|
|
|
cp a ; ensure Z
|
|
|
|
ret
|
2019-05-28 04:16:40 +10:00
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badarg:
|
|
|
|
ld a, ERR_BAD_ARG
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
|
|
|
ret
|
2019-05-20 03:22:14 +10:00
|
|
|
|
2019-05-20 22:20:00 +10:00
|
|
|
handleFIL:
|
|
|
|
call readWord
|
2019-05-28 04:16:40 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-20 22:20:00 +10:00
|
|
|
call parseExpr
|
2019-05-28 04:16:40 +10:00
|
|
|
jr nz, .badarg
|
2019-05-20 22:20:00 +10:00
|
|
|
push bc
|
|
|
|
push ix \ pop bc
|
|
|
|
.loop:
|
2019-07-02 00:56:03 +10:00
|
|
|
ld a, b
|
|
|
|
or c
|
|
|
|
jr z, .loopend
|
|
|
|
xor a
|
2019-05-20 22:20:00 +10:00
|
|
|
call ioPutC
|
2019-06-20 01:42:39 +10:00
|
|
|
jr nz, .ioError
|
2019-07-02 00:56:03 +10:00
|
|
|
dec bc
|
|
|
|
jr .loop
|
|
|
|
.loopend:
|
2019-05-28 03:44:53 +10:00
|
|
|
cp a ; ensure Z
|
2019-05-20 22:20:00 +10:00
|
|
|
pop bc
|
2019-05-21 00:46:27 +10:00
|
|
|
ret
|
2019-06-20 01:42:39 +10:00
|
|
|
.ioError:
|
|
|
|
ld a, SHELL_ERR_IO_ERROR
|
|
|
|
jr .error
|
2019-05-28 04:16:40 +10:00
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badarg:
|
|
|
|
ld a, ERR_BAD_ARG
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
|
|
|
ret
|
2019-05-20 22:20:00 +10:00
|
|
|
|
2019-06-04 01:13:31 +10:00
|
|
|
handleOUT:
|
|
|
|
push hl
|
|
|
|
; Read our expression
|
|
|
|
call readWord
|
|
|
|
jr nz, .badfmt
|
|
|
|
call zasmIsFirstPass ; No .out during first pass
|
|
|
|
jr z, .end
|
|
|
|
ld hl, scratchpad
|
|
|
|
call parseExpr
|
|
|
|
jr nz, .badarg
|
|
|
|
push ix \ pop hl
|
|
|
|
ld a, h
|
|
|
|
out (ZASM_DEBUG_PORT), a
|
|
|
|
ld a, l
|
|
|
|
out (ZASM_DEBUG_PORT), a
|
|
|
|
jr .end
|
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badarg:
|
|
|
|
ld a, ERR_BAD_ARG
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
|
|
|
.end:
|
|
|
|
pop hl
|
|
|
|
ret
|
|
|
|
|
2019-05-17 11:15:00 +10:00
|
|
|
handleINC:
|
|
|
|
call readWord
|
2019-05-28 04:21:46 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-17 11:15:00 +10:00
|
|
|
; HL points to scratchpad
|
2019-05-18 06:17:22 +10:00
|
|
|
call enterDoubleQuotes
|
2019-05-28 04:21:46 +10:00
|
|
|
jr nz, .badfmt
|
2019-05-17 11:15:00 +10:00
|
|
|
call ioOpenInclude
|
2019-05-28 04:21:46 +10:00
|
|
|
jr nz, .badfn
|
|
|
|
cp a ; ensure Z
|
|
|
|
ret
|
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badfn:
|
|
|
|
ld a, ERR_FILENOTFOUND
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
2019-05-17 11:15:00 +10:00
|
|
|
ret
|
|
|
|
|
2019-07-22 02:58:02 +10:00
|
|
|
handleBIN:
|
|
|
|
call readWord
|
|
|
|
jr nz, .badfmt
|
|
|
|
; HL points to scratchpad
|
|
|
|
call enterDoubleQuotes
|
|
|
|
jr nz, .badfmt
|
|
|
|
call ioSpitBin
|
|
|
|
jr nz, .badfn
|
|
|
|
cp a ; ensure Z
|
|
|
|
ret
|
|
|
|
.badfmt:
|
|
|
|
ld a, ERR_BAD_FMT
|
|
|
|
jr .error
|
|
|
|
.badfn:
|
|
|
|
ld a, ERR_FILENOTFOUND
|
|
|
|
.error:
|
|
|
|
call unsetZ
|
|
|
|
ret
|
|
|
|
|
2019-05-01 11:40:22 +10:00
|
|
|
; Reads string in (HL) and returns the corresponding ID (D_*) in A. Sets Z if
|
|
|
|
; there's a match.
|
|
|
|
getDirectiveID:
|
2019-10-07 05:39:47 +11:00
|
|
|
ld a, (hl)
|
|
|
|
cp '.'
|
|
|
|
ret nz
|
|
|
|
push hl
|
2019-05-01 11:40:22 +10:00
|
|
|
push bc
|
|
|
|
push de
|
2019-10-07 05:39:47 +11:00
|
|
|
inc hl
|
2019-07-22 02:58:02 +10:00
|
|
|
ld b, D_BIN+1 ; D_BIN is last
|
2019-10-07 05:39:47 +11:00
|
|
|
ld c, 3
|
2019-05-01 11:40:22 +10:00
|
|
|
ld de, directiveNames
|
|
|
|
call findStringInList
|
|
|
|
pop de
|
|
|
|
pop bc
|
2019-10-07 05:39:47 +11:00
|
|
|
pop hl
|
2019-05-01 11:40:22 +10:00
|
|
|
ret
|
|
|
|
|
2019-05-16 21:53:42 +10:00
|
|
|
; Parse directive specified in A (D_* const) with args in I/O and act in
|
2019-05-02 01:26:41 +10:00
|
|
|
; an appropriate manner. If the directive results in writing data at its
|
2019-05-18 05:14:38 +10:00
|
|
|
; current location, that data is directly written through ioPutC.
|
2019-05-28 03:44:53 +10:00
|
|
|
; Each directive has the same return value pattern: Z on success, not-Z on
|
|
|
|
; error, A contains the error number (ERR_*).
|
2019-05-01 11:40:22 +10:00
|
|
|
parseDirective:
|
2019-05-02 01:26:41 +10:00
|
|
|
push de
|
|
|
|
; double A to have a proper offset in directiveHandlers
|
|
|
|
add a, a
|
|
|
|
ld de, directiveHandlers
|
2019-05-17 23:50:11 +10:00
|
|
|
call addDE
|
|
|
|
call intoDE
|
2019-05-19 10:37:34 +10:00
|
|
|
push de \ pop ix
|
2019-05-02 01:26:41 +10:00
|
|
|
pop de
|
|
|
|
jp (ix)
|