collapseos/apps/lib/expr.asm

244 lines
4.5 KiB
NASM
Raw Normal View History

; *** 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 ***
;
2019-05-15 05:26:29 +10:00
; Parse expression in string at (HL) and returns the result in IX.
2019-11-22 08:06:14 +11:00
; **This routine mutates (HL).**
2019-05-15 05:26:29 +10:00
; 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.
2019-05-15 05:26:29 +10:00
parseExpr:
push de
2019-05-15 05:26:29 +10:00
push hl
2019-05-18 12:22:10 +10:00
call _parseExpr
pop hl
2019-05-18 12:22:10 +10:00
pop de
ret
; Same as parseExpr, but preserves IX and puts result in DE. This is a
; transitionary routine and will replace parseExpr when everyone has jumped
; ship.
parseExprDE:
push ix
push hl
call _parseExpr
push ix \ pop de
pop hl
pop ix
ret
2019-05-18 12:22:10 +10:00
_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
2019-05-18 12:22:10 +10:00
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
2019-05-18 12:22:10 +10:00
; 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
2019-05-18 12:22:10 +10:00
jr nz, .end ; nothing found
; Alright, we have our char and we're pointing at it. Let's replace it
; with a null char.
2019-05-15 05:26:29 +10:00
xor a
ld (hl), a ; + changed to \0
inc hl
2019-05-18 12:22:10 +10:00
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:
2019-05-18 12:22:10 +10:00
; parse expression on the left (HL) and the right (DE) and put the results in
; HL (left) and DE (right)
2019-05-18 12:22:10 +10:00
_resolveLeftAndRight:
2019-11-24 06:56:23 +11:00
; special case: is (HL) zero? If yes, it means that our left operand
; is empty. consider it as 0
ld ix, 0 ; pre-set to 0
ld a, (hl)
or a
jr z, .skip
; Parse left operand in (HL)
call parseExpr
2019-05-15 05:26:29 +10:00
ret nz ; return immediately if error
2019-11-24 06:56:23 +11:00
.skip:
2019-05-18 12:22:10 +10:00
; 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.
2019-05-20 00:45:11 +10:00
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
2019-05-18 12:22:10 +10:00
; 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
2019-11-23 09:16:51 +11:00
.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
2019-05-18 12:22:10 +10:00
ret
.minus:
or a ; clear carry
sbc hl, de
ex de, hl
2019-05-18 12:22:10 +10:00
ret
.mult:
ld b, h
ld c, l
call multDEBC ; --> HL
ex de, hl
2019-05-15 05:26:29 +10:00
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
2019-11-23 09:16:51 +11:00
ret
.and:
ld a, h
and d
ld d, a
2019-11-23 09:16:51 +11:00
ld a, l
and e
ld e, a
2019-11-23 09:16:51 +11:00
ret
.or:
ld a, h
or d
ld d, a
2019-11-23 09:16:51 +11:00
ld a, l
or e
ld e, a
2019-11-23 09:16:51 +11:00
ret
.xor:
ld a, h
xor d
ld d, a
2019-11-23 09:16:51 +11:00
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