mirror of
https://github.com/hsoft/collapseos.git
synced 2024-09-08 08:28:46 +10:00
268 lines
5.4 KiB
NASM
268 lines
5.4 KiB
NASM
; *** Requirements ***
|
|
; ari
|
|
;
|
|
; *** 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 DE. Z for success.
|
|
; HL is advanced to the character following the last successfully
|
|
; read char.
|
|
;
|
|
; *** Code ***
|
|
;
|
|
; Parse expression in string at (HL) and returns the result in DE.
|
|
; This routine needs to be able to mutate (HL), but it takes care of restoring
|
|
; the string to its original value before returning.
|
|
; Sets Z on success, unset on error.
|
|
parseExpr:
|
|
push iy
|
|
push ix
|
|
push hl
|
|
call _parseAddSubst
|
|
pop hl
|
|
pop ix
|
|
pop iy
|
|
ret
|
|
|
|
; *** Op signature ***
|
|
; The signature of "operators routines" (.plus, .mult, etc) below is this:
|
|
; Combine HL and DE with an operator (+, -, *, etc) and put the result in DE.
|
|
; Destroys HL and A. Never fails. Yes, that's a problem for division by zero.
|
|
; Don't divide by zero. All other registers are protected.
|
|
|
|
; Given a running result in DE, a rest-of-expression in (HL), a parse routine
|
|
; in IY and an apply "operator routine" in IX, (HL/DE --> DE)
|
|
; With that, parse the rest of (HL) and apply the operation on it, then place
|
|
; HL at the end of the parsed string, with A containing the last char of it,
|
|
; which can be either an operator or a null char.
|
|
; Z for success.
|
|
;
|
|
_parseApply:
|
|
push de ; --> lvl 1, left result
|
|
push ix ; --> lvl 2, routine to apply
|
|
inc hl ; after op char
|
|
call callIY ; --> DE
|
|
pop ix ; <-- lvl 2, routine to apply
|
|
; Here we do some stack kung fu. We have, in HL, a string pointer we
|
|
; want to keep. We have, in (SP), our left result we want to use.
|
|
ex (sp), hl ; <-> lvl 1
|
|
jr nz, .end
|
|
push af ; --> lvl 2, save ending operator
|
|
call callIX
|
|
pop af ; <-- lvl 2, restore operator.
|
|
.end:
|
|
pop hl ; <-- lvl 1, restore str pointer
|
|
ret
|
|
|
|
; Unless there's an error, this routine completely resolves any valid expression
|
|
; from (HL) and puts the result in DE.
|
|
; Destroys HL
|
|
; Z for success.
|
|
_parseAddSubst:
|
|
call _parseMultDiv
|
|
ret nz
|
|
.loop:
|
|
; do we have an operator?
|
|
or a
|
|
ret z ; null char, we're done
|
|
; We have an operator. Resolve the rest of the expr then apply it.
|
|
ld ix, .plus
|
|
cp '+'
|
|
jr z, .found
|
|
ld ix, .minus
|
|
cp '-'
|
|
ret nz ; unknown char, error
|
|
.found:
|
|
ld iy, _parseMultDiv
|
|
call _parseApply
|
|
ret nz
|
|
jr .loop
|
|
.plus:
|
|
add hl, de
|
|
ex de, hl
|
|
ret
|
|
.minus:
|
|
or a ; clear carry
|
|
sbc hl, de
|
|
ex de, hl
|
|
ret
|
|
|
|
; Parse (HL) as far as it can, that is, resolving expressions at its level or
|
|
; lower (anything but + and -).
|
|
; A is set to the last op it encountered. Unless there's an error, this can only
|
|
; be +, - or null. Null if we're done parsing, + and - if there's still work to
|
|
; do.
|
|
; (HL) points to last op encountered.
|
|
; DE is set to the numerical value of everything that was parsed left of (HL).
|
|
_parseMultDiv:
|
|
call _parseBitShift
|
|
ret nz
|
|
.loop:
|
|
; do we have an operator?
|
|
or a
|
|
ret z ; null char, we're done
|
|
; We have an operator. Resolve the rest of the expr then apply it.
|
|
ld ix, .mult
|
|
cp '*'
|
|
jr z, .found
|
|
ld ix, .div
|
|
cp '/'
|
|
jr z, .found
|
|
ld ix, .mod
|
|
cp '%'
|
|
jr z, .found
|
|
; might not be an error, return success
|
|
cp a
|
|
ret
|
|
.found:
|
|
ld iy, _parseBitShift
|
|
call _parseApply
|
|
ret nz
|
|
jr .loop
|
|
|
|
.mult:
|
|
push bc ; --> lvl 1
|
|
ld b, h
|
|
ld c, l
|
|
call multDEBC ; --> HL
|
|
pop bc ; <-- lvl 1
|
|
ex de, hl
|
|
ret
|
|
|
|
.div:
|
|
; divide takes HL/DE
|
|
ld a, l
|
|
push bc ; --> lvl 1
|
|
call divide
|
|
ld e, c
|
|
ld d, b
|
|
pop bc ; <-- lvl 1
|
|
ret
|
|
|
|
.mod:
|
|
call .div
|
|
ex de, hl
|
|
ret
|
|
|
|
; Same as _parseMultDiv, but a layer lower.
|
|
_parseBitShift:
|
|
call _parseNumber
|
|
ret nz
|
|
.loop:
|
|
; do we have an operator?
|
|
or a
|
|
ret z ; null char, we're done
|
|
; We have an operator. Resolve the rest of the expr then apply it.
|
|
ld ix, .and
|
|
cp '&'
|
|
jr z, .found
|
|
ld ix, .or
|
|
cp 0x7c ; '|'
|
|
jr z, .found
|
|
ld ix, .xor
|
|
cp '^'
|
|
jr z, .found
|
|
ld ix, .rshift
|
|
cp '}'
|
|
jr z, .found
|
|
ld ix, .lshift
|
|
cp '{'
|
|
jr z, .found
|
|
; might not be an error, return success
|
|
cp a
|
|
ret
|
|
.found:
|
|
ld iy, _parseNumber
|
|
call _parseApply
|
|
ret nz
|
|
jr .loop
|
|
|
|
.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 ; --> lvl 1
|
|
ld b, a
|
|
.rshiftLoop:
|
|
srl h
|
|
rr l
|
|
djnz .rshiftLoop
|
|
ex de, hl
|
|
pop bc ; <-- lvl 1
|
|
ret
|
|
|
|
.lshift:
|
|
ld a, e
|
|
and 0xf
|
|
ret z
|
|
push bc ; --> lvl 1
|
|
ld b, a
|
|
.lshiftLoop:
|
|
sla l
|
|
rl h
|
|
djnz .lshiftLoop
|
|
ex de, hl
|
|
pop bc ; <-- lvl 1
|
|
ret
|
|
|
|
; Parse first number of expression at (HL). A valid number is anything that can
|
|
; be parsed by EXPR_PARSE and is followed either by a null char or by any of the
|
|
; operator chars. This routines takes care of replacing an operator char with
|
|
; the null char before calling EXPR_PARSE and then replace the operator back
|
|
; afterwards.
|
|
; HL is moved to the char following the number having been parsed.
|
|
; DE contains the numerical result.
|
|
; A contains the operator char following the number (or null). Only on success.
|
|
; Z for success.
|
|
_parseNumber:
|
|
; Special case 1: number starts with '-'
|
|
ld a, (hl)
|
|
cp '-'
|
|
jr nz, .skip1
|
|
; We have a negative number. Parse normally, then subst from zero
|
|
inc hl
|
|
call _parseNumber
|
|
push hl ; --> lvl 1
|
|
ex af, af' ; preserve flags
|
|
or a ; clear carry
|
|
ld hl, 0
|
|
sbc hl, de
|
|
ex de, hl
|
|
ex af, af' ; restore flags
|
|
pop hl ; <-- lvl 1
|
|
ret
|
|
.skip1:
|
|
; End of special case 1
|
|
call EXPR_PARSE ; --> DE
|
|
ret nz
|
|
; Check if (HL) points to null or op
|
|
ld a, (hl)
|
|
ret
|