501fe96e07
It's an awkward register to use and avoiding its use allows us to strip the resulting binary significantly. parseEXPR keeps the same signature though.
224 lines
4.1 KiB
NASM
224 lines
4.1 KiB
NASM
; *** Requirements ***
|
|
; findchar
|
|
; multDEBC
|
|
; callIXI
|
|
;
|
|
; *** Defines ***
|
|
;
|
|
; EXPR_PARSE: routine to call to parse literals or symbols that are part of
|
|
; the expression. Routine's signature:
|
|
; String in (HL), returns its parsed value to IX. Z for success.
|
|
;
|
|
; *** Code ***
|
|
;
|
|
; Parse expression in string at (HL) and returns the result in IX.
|
|
; **This routine mutates (HL).**
|
|
; We expect (HL) to be disposable: we mutate it to avoid having to make a copy.
|
|
; Sets Z on success, unset on error.
|
|
; TODO: the IX output register is a bit awkward. Nearly everywhere, I need
|
|
; to push \ pop that thing. See if we could return the result in DE
|
|
; instead.
|
|
parseExpr:
|
|
push de
|
|
push hl
|
|
call _parseExpr
|
|
pop hl
|
|
pop de
|
|
ret
|
|
|
|
_parseExpr:
|
|
ld de, exprTbl
|
|
.loop:
|
|
ld a, (de)
|
|
or a
|
|
jp z, EXPR_PARSE ; no operator, just parse the literal
|
|
push de ; --> lvl 1. save operator row
|
|
call _findAndSplit
|
|
jr z, .found
|
|
pop de ; <-- lvl 1
|
|
inc de \ inc de \ inc de
|
|
jr .loop
|
|
.found:
|
|
; Operator found, string splitted. Left in (HL), right in (DE)
|
|
call _resolveLeftAndRight
|
|
; Whether _resolveLeftAndRight was a success, we pop our lvl 1 stack
|
|
; out, which contains our operator row. We pop it in IX.
|
|
; L-R numbers are parsed in HL (left) and DE (right).
|
|
pop ix ; <-- lvl 1
|
|
ret nz
|
|
; Resolving left and right succeeded, proceed!
|
|
inc ix ; point to routine pointer
|
|
call callIXI
|
|
push de \ pop ix
|
|
cp a ; ensure Z
|
|
ret
|
|
|
|
; Given a string in (HL) and a separator char in A, return a splitted string,
|
|
; that is, the same (HL) string but with the found A char replaced by a null
|
|
; char. DE points to the second part of the split.
|
|
; Sets Z if found, unset if not found.
|
|
_findAndSplit:
|
|
push hl
|
|
call .skipCharLiteral
|
|
call findchar
|
|
jr nz, .end ; nothing found
|
|
; Alright, we have our char and we're pointing at it. Let's replace it
|
|
; with a null char.
|
|
xor a
|
|
ld (hl), a ; + changed to \0
|
|
inc hl
|
|
ex de, hl ; DE now points to the second part of the split
|
|
cp a ; ensure Z
|
|
.end:
|
|
pop hl ; HL is back to the start
|
|
ret
|
|
|
|
.skipCharLiteral:
|
|
; special case: if our first char is ', skip the first 3 characters
|
|
; so that we don't mistake a literal for an iterator
|
|
push af
|
|
ld a, (hl)
|
|
cp 0x27 ; '
|
|
jr nz, .skipCharLiteralEnd ; not a '
|
|
xor a ; check for null char during skipping
|
|
; skip 3
|
|
inc hl
|
|
cp (hl)
|
|
jr z, .skipCharLiteralEnd
|
|
inc hl
|
|
cp (hl)
|
|
jr z, .skipCharLiteralEnd
|
|
inc hl
|
|
.skipCharLiteralEnd:
|
|
pop af
|
|
ret
|
|
.find:
|
|
|
|
; parse expression on the left (HL) and the right (DE) and put the results in
|
|
; HL (left) and DE (right)
|
|
_resolveLeftAndRight:
|
|
call parseExpr
|
|
ret nz ; return immediately if error
|
|
; Now we have parsed everything to the left and we have its result in
|
|
; IX. What we need to do now is the same thing on (DE) and then apply
|
|
; the + operator. Let's save IX somewhere and parse this.
|
|
ex de, hl ; right expr now in HL
|
|
push ix ; --> lvl 1
|
|
call parseExpr
|
|
pop hl ; <-- lvl 1. left
|
|
push ix \ pop de ; right
|
|
ret ; Z is parseExpr's result
|
|
|
|
; Routines in here all have the same signature: they take two numbers, DE (left)
|
|
; and IX (right), apply the operator and put the resulting number in DE.
|
|
; The table has 3 bytes per row: 1 byte for operator and 2 bytes for routine
|
|
; pointer.
|
|
exprTbl:
|
|
.db '+'
|
|
.dw .plus
|
|
.db '-'
|
|
.dw .minus
|
|
.db '*'
|
|
.dw .mult
|
|
.db '/'
|
|
.dw .div
|
|
.db '%'
|
|
.dw .mod
|
|
.db '&'
|
|
.dw .and
|
|
.db 0x7c ; '|'
|
|
.dw .or
|
|
.db '^'
|
|
.dw .xor
|
|
.db '}'
|
|
.dw .rshift
|
|
.db '{'
|
|
.dw .lshift
|
|
.db 0 ; end of table
|
|
|
|
.plus:
|
|
add hl, de
|
|
ex de, hl
|
|
ret
|
|
|
|
.minus:
|
|
or a ; clear carry
|
|
sbc hl, de
|
|
ex de, hl
|
|
ret
|
|
|
|
.mult:
|
|
ld b, h
|
|
ld c, l
|
|
call multDEBC ; --> HL
|
|
ex de, hl
|
|
ret
|
|
|
|
.div:
|
|
; divide takes HL/DE
|
|
push bc
|
|
call divide
|
|
ld e, c
|
|
ld d, b
|
|
pop bc
|
|
ret
|
|
|
|
.mod:
|
|
call .div
|
|
ex de, hl
|
|
ret
|
|
|
|
.and:
|
|
ld a, h
|
|
and d
|
|
ld d, a
|
|
ld a, l
|
|
and e
|
|
ld e, a
|
|
ret
|
|
.or:
|
|
ld a, h
|
|
or d
|
|
ld d, a
|
|
ld a, l
|
|
or e
|
|
ld e, a
|
|
ret
|
|
|
|
.xor:
|
|
ld a, h
|
|
xor d
|
|
ld d, a
|
|
ld a, l
|
|
xor e
|
|
ld e, a
|
|
ret
|
|
|
|
.rshift:
|
|
ld a, e
|
|
and 0xf
|
|
ret z
|
|
push bc
|
|
ld b, a
|
|
.rshiftLoop:
|
|
srl h
|
|
rr l
|
|
djnz .rshiftLoop
|
|
ex de, hl
|
|
pop bc
|
|
ret
|
|
|
|
.lshift:
|
|
ld a, e
|
|
and 0xf
|
|
ret z
|
|
push bc
|
|
ld b, a
|
|
.lshiftLoop:
|
|
sla l
|
|
rl h
|
|
djnz .lshiftLoop
|
|
ex de, hl
|
|
pop bc
|
|
ret
|