mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-26 05:48:05 +11:00
Compare commits
4 Commits
7410891ad1
...
15628da7de
Author | SHA1 | Date | |
---|---|---|---|
|
15628da7de | ||
|
981c93bfd4 | ||
|
4760d044c0 | ||
|
213614af33 |
@ -98,12 +98,9 @@ parseLiteralOrVar:
|
|||||||
push hl ; --> lvl 1
|
push hl ; --> lvl 1
|
||||||
ld hl, VAR_TBL
|
ld hl, VAR_TBL
|
||||||
call addHL
|
call addHL
|
||||||
push de ; --> lvl 2
|
|
||||||
ld e, (hl)
|
ld e, (hl)
|
||||||
inc hl
|
inc hl
|
||||||
ld d, (hl)
|
ld d, (hl)
|
||||||
push de \ pop ix
|
|
||||||
pop de ; <-- lvl 2
|
|
||||||
pop hl ; <-- lvl 1
|
pop hl ; <-- lvl 1
|
||||||
cp a ; ensure Z
|
cp a ; ensure Z
|
||||||
ret
|
ret
|
||||||
|
@ -1,177 +1,141 @@
|
|||||||
; *** Requirements ***
|
; *** Requirements ***
|
||||||
; findchar
|
; ari
|
||||||
; multDEBC
|
|
||||||
; callIXI
|
|
||||||
;
|
;
|
||||||
; *** Defines ***
|
; *** Defines ***
|
||||||
;
|
;
|
||||||
; EXPR_PARSE: routine to call to parse literals or symbols that are part of
|
; EXPR_PARSE: routine to call to parse literals or symbols that are part of
|
||||||
; the expression. Routine's signature:
|
; the expression. Routine's signature:
|
||||||
; String in (HL), returns its parsed value to IX. Z for success.
|
; String in (HL), returns its parsed value to DE. Z for success.
|
||||||
;
|
;
|
||||||
; *** Code ***
|
; *** Code ***
|
||||||
;
|
;
|
||||||
|
|
||||||
; Parse expression in string at (HL) and returns the result in DE.
|
; Parse expression in string at (HL) and returns the result in DE.
|
||||||
; **This routine mutates (HL).**
|
; This routine needs to be able to mutate (HL), but it takes care of restoring
|
||||||
; We expect (HL) to be disposable: we mutate it to avoid having to make a copy.
|
; the string to its original value before returning.
|
||||||
; Sets Z on success, unset on error.
|
; Sets Z on success, unset on error.
|
||||||
parseExpr:
|
parseExpr:
|
||||||
|
push iy
|
||||||
push ix
|
push ix
|
||||||
push hl
|
push hl
|
||||||
call _parseExpr
|
call _parseAddSubst
|
||||||
push ix \ pop de
|
|
||||||
pop hl
|
pop hl
|
||||||
pop ix
|
pop ix
|
||||||
|
pop iy
|
||||||
ret
|
ret
|
||||||
|
|
||||||
_parseExpr:
|
; *** Op signature ***
|
||||||
ld de, exprTbl
|
; The signature of "operators routines" (.plus, .mult, etc) below is this:
|
||||||
.loop:
|
; Combine HL and DE with an operator (+, -, *, etc) and put the result in DE.
|
||||||
ld a, (de)
|
; Destroys HL and A. Never fails. Yes, that's a problem for division by zero.
|
||||||
or a
|
; Don't divide by zero. All other registers are protected.
|
||||||
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,
|
; Given a running result in DE, a rest-of-expression in (HL), a parse routine
|
||||||
; that is, the same (HL) string but with the found A char replaced by a null
|
; in IY and an apply "operator routine" in IX, (HL/DE --> DE)
|
||||||
; char. DE points to the second part of the split.
|
; With that, parse the rest of (HL) and apply the operation on it, then place
|
||||||
; Sets Z if found, unset if not found.
|
; HL at the end of the parsed string, with A containing the last char of it,
|
||||||
_findAndSplit:
|
; which can be either an operator or a null char.
|
||||||
push hl
|
; Z for success.
|
||||||
call .skipCharLiteral
|
;
|
||||||
call findchar
|
_parseApply:
|
||||||
jr nz, .end ; nothing found
|
push de ; --> lvl 1, left result
|
||||||
; Alright, we have our char and we're pointing at it. Let's replace it
|
push ix ; --> lvl 2, routine to apply
|
||||||
; with a null char.
|
inc hl ; after op char
|
||||||
xor a
|
call callIY ; --> DE
|
||||||
ld (hl), a ; + changed to \0
|
pop ix ; <-- lvl 2, routine to apply
|
||||||
inc hl
|
; Here we do some stack kung fu. We have, in HL, a string pointer we
|
||||||
ex de, hl ; DE now points to the second part of the split
|
; want to keep. We have, in (SP), our left result we want to use.
|
||||||
cp a ; ensure Z
|
ex (sp), hl ; <-> lvl 1
|
||||||
|
jr nz, .end
|
||||||
|
push af ; --> lvl 2, save ending operator
|
||||||
|
call callIX
|
||||||
|
pop af ; <-- lvl 2, restore operator.
|
||||||
.end:
|
.end:
|
||||||
pop hl ; HL is back to the start
|
pop hl ; <-- lvl 1, restore str pointer
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.skipCharLiteral:
|
; Unless there's an error, this routine completely resolves any valid expression
|
||||||
; special case: if our first char is ', skip the first 3 characters
|
; from (HL) and puts the result in DE.
|
||||||
; so that we don't mistake a literal for an iterator
|
; Destroys HL
|
||||||
push af
|
; Z for success.
|
||||||
ld a, (hl)
|
_parseAddSubst:
|
||||||
cp 0x27 ; '
|
call _parseMultDiv
|
||||||
jr nz, .skipCharLiteralEnd ; not a '
|
ret nz
|
||||||
xor a ; check for null char during skipping
|
.loop:
|
||||||
; skip 3
|
; do we have an operator?
|
||||||
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:
|
|
||||||
ld a, (hl)
|
|
||||||
or a
|
or a
|
||||||
jr z, .noleft
|
ret z ; null char, we're done
|
||||||
; Parse left operand in (HL)
|
; We have an operator. Resolve the rest of the expr then apply it.
|
||||||
push de ; --> lvl 1
|
ld ix, .plus
|
||||||
call parseExpr
|
cp '+'
|
||||||
pop hl ; <-- lvl 1, orig DE
|
jr z, .found
|
||||||
ret nz ; return immediately if error
|
ld ix, .minus
|
||||||
.parseright:
|
cp '-'
|
||||||
; Now we have parsed everything to the left and we have its result in
|
ret nz ; unknown char, error
|
||||||
; DE. What we need to do now is the same thing on (DE) and then apply
|
.found:
|
||||||
; the + operator. Let's save DE somewhere and parse this.
|
ld iy, _parseMultDiv
|
||||||
push de ; --> lvl 1
|
call _parseApply
|
||||||
; right expr in (HL)
|
ret nz
|
||||||
call parseExpr ; DE is set
|
jr .loop
|
||||||
pop hl ; <-- lvl 1. left value
|
|
||||||
ret ; Z is parseExpr's result
|
|
||||||
.noleft:
|
|
||||||
; special case: is (HL) zero? If yes, it means that our left operand
|
|
||||||
; is empty. consider it as 0
|
|
||||||
ex de, hl ; (DE) goes in (HL) for .parseright
|
|
||||||
ld de, 0
|
|
||||||
jr .parseright
|
|
||||||
|
|
||||||
; 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:
|
.plus:
|
||||||
add hl, de
|
add hl, de
|
||||||
ex de, hl
|
ex de, hl
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.minus:
|
.minus:
|
||||||
or a ; clear carry
|
or a ; clear carry
|
||||||
sbc hl, de
|
sbc hl, de
|
||||||
ex de, hl
|
ex de, hl
|
||||||
ret
|
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:
|
.mult:
|
||||||
|
push bc ; --> lvl 1
|
||||||
ld b, h
|
ld b, h
|
||||||
ld c, l
|
ld c, l
|
||||||
call multDEBC ; --> HL
|
call multDEBC ; --> HL
|
||||||
|
pop bc ; <-- lvl 1
|
||||||
ex de, hl
|
ex de, hl
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.div:
|
.div:
|
||||||
; divide takes HL/DE
|
; divide takes HL/DE
|
||||||
push bc
|
ld a, l
|
||||||
|
push bc ; --> lvl 1
|
||||||
call divide
|
call divide
|
||||||
ld e, c
|
ld e, c
|
||||||
ld d, b
|
ld d, b
|
||||||
pop bc
|
pop bc ; <-- lvl 1
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.mod:
|
.mod:
|
||||||
@ -179,6 +143,39 @@ exprTbl:
|
|||||||
ex de, hl
|
ex de, hl
|
||||||
ret
|
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:
|
.and:
|
||||||
ld a, h
|
ld a, h
|
||||||
and d
|
and d
|
||||||
@ -209,26 +206,126 @@ exprTbl:
|
|||||||
ld a, e
|
ld a, e
|
||||||
and 0xf
|
and 0xf
|
||||||
ret z
|
ret z
|
||||||
push bc
|
push bc ; --> lvl 1
|
||||||
ld b, a
|
ld b, a
|
||||||
.rshiftLoop:
|
.rshiftLoop:
|
||||||
srl h
|
srl h
|
||||||
rr l
|
rr l
|
||||||
djnz .rshiftLoop
|
djnz .rshiftLoop
|
||||||
ex de, hl
|
ex de, hl
|
||||||
pop bc
|
pop bc ; <-- lvl 1
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.lshift:
|
.lshift:
|
||||||
ld a, e
|
ld a, e
|
||||||
and 0xf
|
and 0xf
|
||||||
ret z
|
ret z
|
||||||
push bc
|
push bc ; --> lvl 1
|
||||||
ld b, a
|
ld b, a
|
||||||
.lshiftLoop:
|
.lshiftLoop:
|
||||||
sla l
|
sla l
|
||||||
rl h
|
rl h
|
||||||
djnz .lshiftLoop
|
djnz .lshiftLoop
|
||||||
ex de, hl
|
ex de, hl
|
||||||
pop bc
|
pop bc ; <-- lvl 1
|
||||||
ret
|
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
|
||||||
|
; Copy beginning of string to DE, we'll need it later
|
||||||
|
ld d, h
|
||||||
|
ld e, l
|
||||||
|
|
||||||
|
; Special case 2: we have a char literal. If we have a char literal, we
|
||||||
|
; don't want to go through the "_isOp" loop below because if that char
|
||||||
|
; is one of our operators, we're messing up our processing. So, set
|
||||||
|
; ourselves 3 chars further and continue from there. EXPR_PARSE will
|
||||||
|
; take care of validating those 3 chars.
|
||||||
|
cp 0x27 ; apostrophe (') char
|
||||||
|
jr nz, .skip2
|
||||||
|
; "'". advance HL by 3
|
||||||
|
inc hl \ inc hl \ inc hl
|
||||||
|
; End of special case 2
|
||||||
|
.skip2:
|
||||||
|
|
||||||
|
dec hl ; offset "inc-hl-before" in loop
|
||||||
|
.loop:
|
||||||
|
inc hl
|
||||||
|
ld a, (hl)
|
||||||
|
call _isOp
|
||||||
|
jr nz, .loop
|
||||||
|
; (HL) and A is an op or a null
|
||||||
|
push af ; --> lvl 1 save op
|
||||||
|
push hl ; --> lvl 2 save end of string
|
||||||
|
; temporarily put a null char instead of the op
|
||||||
|
xor a
|
||||||
|
ld (hl), a
|
||||||
|
ex de, hl ; rewind to beginning of number
|
||||||
|
call EXPR_PARSE ; --> DE
|
||||||
|
ex af, af' ; keep result flags away while we restore (HL)
|
||||||
|
pop hl ; <-- lvl 2, end of string
|
||||||
|
pop af ; <-- lvl 1, saved op
|
||||||
|
ld (hl), a
|
||||||
|
ex af, af' ; restore Z from EXPR_PARSE
|
||||||
|
ret nz
|
||||||
|
; HL is currently at the end of the number's string
|
||||||
|
; On success, have A be the operator char following the number
|
||||||
|
ex af, af'
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Sets Z if A contains a valid operator char or a null char.
|
||||||
|
_isOp:
|
||||||
|
or a
|
||||||
|
ret z
|
||||||
|
push hl ; --> lvl 1
|
||||||
|
; Set A' to zero for quick end-of-table checks
|
||||||
|
ex af, af'
|
||||||
|
xor a
|
||||||
|
ex af, af'
|
||||||
|
ld hl, .exprChars
|
||||||
|
.loop:
|
||||||
|
cp (hl)
|
||||||
|
jr z, .found
|
||||||
|
ex af, af'
|
||||||
|
cp (hl)
|
||||||
|
jr z, .notFound ; end of table
|
||||||
|
ex af, af'
|
||||||
|
inc hl ; next char
|
||||||
|
jr .loop
|
||||||
|
.notFound:
|
||||||
|
ex af, af' ; restore orig A
|
||||||
|
inc a ; unset Z
|
||||||
|
.found:
|
||||||
|
; Z already set
|
||||||
|
pop hl ; <-- lvl 1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.exprChars:
|
||||||
|
.db "+-*/%&|^{}", 0
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ parseHexPair:
|
|||||||
; add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
|
; add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
|
||||||
; sub 0xff-9 ; maps to 0-9 and carries if not a digit
|
; sub 0xff-9 ; maps to 0-9 and carries if not a digit
|
||||||
|
|
||||||
; Parse string at (HL) as a decimal value and return value in IX under the
|
; Parse string at (HL) as a decimal value and return value in DE under the
|
||||||
; same conditions as parseLiteral.
|
; same conditions as parseLiteral.
|
||||||
; Sets Z on success, unset on error.
|
; Sets Z on success, unset on error.
|
||||||
; To parse successfully, all characters following HL must be digits and those
|
; To parse successfully, all characters following HL must be digits and those
|
||||||
@ -76,7 +76,7 @@ parseHexPair:
|
|||||||
; digit in the string.
|
; digit in the string.
|
||||||
|
|
||||||
parseDecimal:
|
parseDecimal:
|
||||||
push hl
|
push hl ; --> lvl 1
|
||||||
|
|
||||||
ld a, (hl)
|
ld a, (hl)
|
||||||
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
|
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
|
||||||
@ -129,23 +129,23 @@ parseDecimal:
|
|||||||
; to 0x00+(0xff-'9')-(0xff-9)=-0x30=0xd0
|
; to 0x00+(0xff-'9')-(0xff-9)=-0x30=0xd0
|
||||||
sub 0xd0 ; if a is null, set Z
|
sub 0xd0 ; if a is null, set Z
|
||||||
; a is checked for null before any errors
|
; a is checked for null before any errors
|
||||||
push hl \ pop ix
|
push hl ; --> lvl 2, result
|
||||||
exx ; restore original de and bc
|
exx ; restore original bc
|
||||||
pop hl
|
pop de ; <-- lvl 2, result
|
||||||
|
pop hl ; <-- lvl 1, orig
|
||||||
ret z
|
ret z
|
||||||
; A is not 0? Ok, but if it's a space, we're happy too.
|
; A is not 0? Ok, but if it's a space, we're happy too.
|
||||||
jp isWS
|
jp isWS
|
||||||
.error:
|
.error:
|
||||||
pop hl
|
pop hl ; <-- lvl 1, orig
|
||||||
jp unsetZ
|
jp unsetZ
|
||||||
|
|
||||||
; Parse string at (HL) as a hexadecimal value and return value in IX under the
|
; Parse string at (HL) as a hexadecimal value and return value in DE under the
|
||||||
; same conditions as parseLiteral.
|
; same conditions as parseLiteral.
|
||||||
parseHexadecimal:
|
parseHexadecimal:
|
||||||
call hasHexPrefix
|
call hasHexPrefix
|
||||||
ret nz
|
ret nz
|
||||||
push hl
|
push hl
|
||||||
push de
|
|
||||||
ld d, 0
|
ld d, 0
|
||||||
inc hl ; get rid of "0x"
|
inc hl ; get rid of "0x"
|
||||||
inc hl
|
inc hl
|
||||||
@ -179,8 +179,6 @@ parseHexadecimal:
|
|||||||
.error:
|
.error:
|
||||||
call unsetZ
|
call unsetZ
|
||||||
.end:
|
.end:
|
||||||
push de \ pop ix
|
|
||||||
pop de
|
|
||||||
pop hl
|
pop hl
|
||||||
ret
|
ret
|
||||||
|
|
||||||
@ -196,15 +194,14 @@ hasHexPrefix:
|
|||||||
pop hl
|
pop hl
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Parse string at (HL) as a binary value (0b010101) and return value in IX.
|
; Parse string at (HL) as a binary value (0b010101) and return value in E.
|
||||||
; High IX byte is always clear.
|
; D is always zero.
|
||||||
; Sets Z on success.
|
; Sets Z on success.
|
||||||
parseBinaryLiteral:
|
parseBinaryLiteral:
|
||||||
call hasBinPrefix
|
call hasBinPrefix
|
||||||
ret nz
|
ret nz
|
||||||
push bc
|
push bc
|
||||||
push hl
|
push hl
|
||||||
push de
|
|
||||||
ld d, 0
|
ld d, 0
|
||||||
inc hl ; get rid of "0b"
|
inc hl ; get rid of "0b"
|
||||||
inc hl
|
inc hl
|
||||||
@ -236,8 +233,6 @@ parseBinaryLiteral:
|
|||||||
.error:
|
.error:
|
||||||
call unsetZ
|
call unsetZ
|
||||||
.end:
|
.end:
|
||||||
push de \ pop ix
|
|
||||||
pop de
|
|
||||||
pop hl
|
pop hl
|
||||||
pop bc
|
pop bc
|
||||||
ret
|
ret
|
||||||
@ -255,7 +250,7 @@ hasBinPrefix:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
; Parse string at (HL) and, if it is a char literal, sets Z and return
|
; Parse string at (HL) and, if it is a char literal, sets Z and return
|
||||||
; corresponding value in IX. High IX byte is always clear.
|
; corresponding value in E. D is always zero.
|
||||||
;
|
;
|
||||||
; A valid char literal starts with ', ends with ' and has one character in the
|
; A valid char literal starts with ', ends with ' and has one character in the
|
||||||
; middle. No escape sequence are accepted, but ''' will return the apostrophe
|
; middle. No escape sequence are accepted, but ''' will return the apostrophe
|
||||||
@ -266,7 +261,6 @@ parseCharLiteral:
|
|||||||
ret nz
|
ret nz
|
||||||
|
|
||||||
push hl
|
push hl
|
||||||
push de
|
|
||||||
inc hl
|
inc hl
|
||||||
inc hl
|
inc hl
|
||||||
cp (hl)
|
cp (hl)
|
||||||
@ -283,12 +277,10 @@ parseCharLiteral:
|
|||||||
ld e, a
|
ld e, a
|
||||||
cp a ; ensure Z
|
cp a ; ensure Z
|
||||||
.end:
|
.end:
|
||||||
push de \ pop ix
|
|
||||||
pop de
|
|
||||||
pop hl
|
pop hl
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Parses the string at (HL) and returns the 16-bit value in IX. The string
|
; Parses the string at (HL) and returns the 16-bit value in DE. The string
|
||||||
; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char
|
; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char
|
||||||
; literal ('X').
|
; literal ('X').
|
||||||
;
|
;
|
||||||
|
@ -657,7 +657,6 @@ _readDouble:
|
|||||||
_readk7:
|
_readk7:
|
||||||
push hl
|
push hl
|
||||||
push de
|
push de
|
||||||
push ix
|
|
||||||
call parseExpr
|
call parseExpr
|
||||||
jr nz, .end
|
jr nz, .end
|
||||||
; If we're in first pass, stop now. The value of HL doesn't matter and
|
; If we're in first pass, stop now. The value of HL doesn't matter and
|
||||||
@ -686,7 +685,6 @@ _readk7:
|
|||||||
ld a, l
|
ld a, l
|
||||||
cp a ; ensure Z
|
cp a ; ensure Z
|
||||||
.end:
|
.end:
|
||||||
pop ix
|
|
||||||
pop de
|
pop de
|
||||||
pop hl
|
pop hl
|
||||||
ret
|
ret
|
||||||
@ -707,7 +705,6 @@ _readR4:
|
|||||||
; Set Z for success.
|
; Set Z for success.
|
||||||
_readR5:
|
_readR5:
|
||||||
push de
|
push de
|
||||||
push ix
|
|
||||||
ld a, (hl)
|
ld a, (hl)
|
||||||
call upcase
|
call upcase
|
||||||
cp 'R'
|
cp 'R'
|
||||||
@ -715,11 +712,9 @@ _readR5:
|
|||||||
inc hl
|
inc hl
|
||||||
call parseDecimal
|
call parseDecimal
|
||||||
jr nz, .end
|
jr nz, .end
|
||||||
push ix \ pop de
|
|
||||||
ld a, 31
|
ld a, 31
|
||||||
call _DE2A
|
call _DE2A
|
||||||
.end:
|
.end:
|
||||||
pop ix
|
|
||||||
pop de
|
pop de
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
; Parse string in (HL) and return its numerical value whether its a number
|
; Parse string in (HL) and return its numerical value whether its a number
|
||||||
; literal or a symbol. Returns value in IX.
|
; literal or a symbol. Returns value in DE.
|
||||||
; Sets Z if number or symbol is valid, unset otherwise.
|
; Sets Z if number or symbol is valid, unset otherwise.
|
||||||
parseNumberOrSymbol:
|
parseNumberOrSymbol:
|
||||||
call parseLiteral
|
call parseLiteral
|
||||||
@ -14,31 +14,25 @@ parseNumberOrSymbol:
|
|||||||
cp '@'
|
cp '@'
|
||||||
jr nz, .symbol
|
jr nz, .symbol
|
||||||
; last val
|
; last val
|
||||||
ld ix, (DIREC_LASTVAL)
|
ld de, (DIREC_LASTVAL)
|
||||||
ret
|
ret
|
||||||
.symbol:
|
.symbol:
|
||||||
push de ; --> lvl 1
|
|
||||||
call symFindVal ; --> DE
|
call symFindVal ; --> DE
|
||||||
jr nz, .notfound
|
jr nz, .notfound
|
||||||
; value in DE. We need it in IX
|
|
||||||
push de \ pop ix
|
|
||||||
pop de ; <-- lvl 1
|
|
||||||
cp a ; ensure Z
|
|
||||||
ret
|
ret
|
||||||
.notfound:
|
.notfound:
|
||||||
pop de ; <-- lvl 1
|
|
||||||
; If not found, check if we're in first pass. If we are, it doesn't
|
; If not found, check if we're in first pass. If we are, it doesn't
|
||||||
; matter that we didn't find our symbol. Return success anyhow.
|
; matter that we didn't find our symbol. Return success anyhow.
|
||||||
; Otherwise return error. Z is already unset, so in fact, this is the
|
; Otherwise return error. Z is already unset, so in fact, this is the
|
||||||
; same as jumping to zasmIsFirstPass
|
; same as jumping to zasmIsFirstPass
|
||||||
; however, before we do, load IX with zero. Returning dummy non-zero
|
; however, before we do, load DE with zero. Returning dummy non-zero
|
||||||
; values can have weird consequence (such as false overflow errors).
|
; values can have weird consequence (such as false overflow errors).
|
||||||
ld ix, 0
|
ld de, 0
|
||||||
jp zasmIsFirstPass
|
jp zasmIsFirstPass
|
||||||
|
|
||||||
.returnPC:
|
.returnPC:
|
||||||
push hl
|
push hl
|
||||||
call zasmGetPC
|
call zasmGetPC
|
||||||
push hl \ pop ix
|
ex de, hl ; result in DE
|
||||||
pop hl
|
pop hl
|
||||||
ret
|
ret
|
||||||
|
@ -6,6 +6,12 @@
|
|||||||
; lib/fmt
|
; lib/fmt
|
||||||
|
|
||||||
testNum: .db 1
|
testNum: .db 1
|
||||||
|
; Each time we call assertSP, we verify that our stack isn't imbalanced by
|
||||||
|
; comparing SP to its saved value. Whenever your "base" SP value change,
|
||||||
|
; generally at the beginning of a test routine, run "ld (testSP), sp" to have
|
||||||
|
; proper value saved to heap.
|
||||||
|
testSP: .dw 0xffff
|
||||||
|
|
||||||
|
|
||||||
STDIO_PUTC:
|
STDIO_PUTC:
|
||||||
out (0), a
|
out (0), a
|
||||||
@ -51,6 +57,38 @@ assertEQW:
|
|||||||
.msg:
|
.msg:
|
||||||
.db "HL != DE", CR, LF, 0
|
.db "HL != DE", CR, LF, 0
|
||||||
|
|
||||||
|
; Given a list of pointer to test data structures in HL and a pointer to a test
|
||||||
|
; routine in IX, call (IX) with HL pointing to the test structure until the list
|
||||||
|
; points to a zero. See testParseExpr in test_expr for an example usage.
|
||||||
|
testList:
|
||||||
|
push hl ; --> lvl 1
|
||||||
|
call intoHL
|
||||||
|
ld a, h
|
||||||
|
or l
|
||||||
|
jr z, .end
|
||||||
|
call callIX
|
||||||
|
call nexttest
|
||||||
|
pop hl ; <-- lvl 1
|
||||||
|
inc hl \ inc hl
|
||||||
|
jr testList
|
||||||
|
.end:
|
||||||
|
pop hl ; <-- lvl 1
|
||||||
|
ret
|
||||||
|
|
||||||
|
; test that SP == testSP
|
||||||
|
assertSP:
|
||||||
|
ld hl, (testSP)
|
||||||
|
; offset the fact that we call assertSP
|
||||||
|
dec hl \ dec hl
|
||||||
|
or a ; reset carry
|
||||||
|
sbc hl, sp
|
||||||
|
ret z
|
||||||
|
ld hl, .msg
|
||||||
|
call printstr
|
||||||
|
jr fail
|
||||||
|
.msg:
|
||||||
|
.db "Wrong SP", CR, LF, 0
|
||||||
|
|
||||||
nexttest:
|
nexttest:
|
||||||
ld a, (testNum)
|
ld a, (testNum)
|
||||||
inc a
|
inc a
|
||||||
|
@ -34,12 +34,6 @@ zasmIsFirstPass:
|
|||||||
zasmGetPC:
|
zasmGetPC:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
s1: .db "2+2", 0
|
|
||||||
s2: .db "0x4001+0x22", 0
|
|
||||||
s3: .db "FOO+BAR", 0
|
|
||||||
s4: .db "BAR*3", 0
|
|
||||||
s5: .db "FOO-3", 0
|
|
||||||
s6: .db "FOO+BAR*4", 0
|
|
||||||
|
|
||||||
sFOO: .db "FOO", 0
|
sFOO: .db "FOO", 0
|
||||||
sBAR: .db "BAR", 0
|
sBAR: .db "BAR", 0
|
||||||
@ -47,25 +41,7 @@ sBAR: .db "BAR", 0
|
|||||||
test:
|
test:
|
||||||
ld sp, 0xffff
|
ld sp, 0xffff
|
||||||
|
|
||||||
; New-style tests
|
; before testing begins, let's set up FOO and BAR symbols
|
||||||
call testParseExpr
|
|
||||||
|
|
||||||
; Old-style tests, not touching them now.
|
|
||||||
ld hl, s1
|
|
||||||
call parseExpr
|
|
||||||
call assertZ
|
|
||||||
ld hl, 4
|
|
||||||
call assertEQW
|
|
||||||
call nexttest
|
|
||||||
|
|
||||||
ld hl, s2
|
|
||||||
call parseExpr
|
|
||||||
call assertZ
|
|
||||||
ld hl, 0x4023
|
|
||||||
call assertEQW
|
|
||||||
call nexttest
|
|
||||||
|
|
||||||
; before the next test, let's set up FOO and BAR symbols
|
|
||||||
call symInit
|
call symInit
|
||||||
ld hl, sFOO
|
ld hl, sFOO
|
||||||
ld de, 0x4000
|
ld de, 0x4000
|
||||||
@ -76,68 +52,26 @@ test:
|
|||||||
call symRegisterGlobal
|
call symRegisterGlobal
|
||||||
jp nz, fail
|
jp nz, fail
|
||||||
|
|
||||||
ld hl, s3
|
call testParseExpr
|
||||||
call parseExpr
|
call testSPOnFail
|
||||||
call assertZ
|
|
||||||
ld hl, 0x4020
|
|
||||||
call assertEQW
|
|
||||||
call nexttest
|
|
||||||
|
|
||||||
ld hl, s4
|
|
||||||
call parseExpr
|
|
||||||
call assertZ
|
|
||||||
ld hl, 0x60
|
|
||||||
call assertEQW
|
|
||||||
call nexttest
|
|
||||||
|
|
||||||
ld hl, s5
|
|
||||||
call parseExpr
|
|
||||||
call assertZ
|
|
||||||
ld hl, 0x3ffd
|
|
||||||
call assertEQW
|
|
||||||
call nexttest
|
|
||||||
|
|
||||||
ld hl, s6
|
|
||||||
call parseExpr
|
|
||||||
call assertZ
|
|
||||||
ld hl, 0x4080
|
|
||||||
call assertEQW
|
|
||||||
call nexttest
|
|
||||||
|
|
||||||
; success
|
; success
|
||||||
xor a
|
xor a
|
||||||
halt
|
halt
|
||||||
|
|
||||||
testParseExpr:
|
testParseExpr:
|
||||||
ld iy, .t1
|
ld hl, .alltests
|
||||||
call .testEQ
|
ld ix, .test
|
||||||
ld iy, .t2
|
jp testList
|
||||||
call .testEQ
|
|
||||||
ld iy, .t3
|
|
||||||
call .testEQ
|
|
||||||
ld iy, .t4
|
|
||||||
call .testEQ
|
|
||||||
ld iy, .t5
|
|
||||||
call .testEQ
|
|
||||||
ld iy, .t6
|
|
||||||
call .testEQ
|
|
||||||
ld iy, .t7
|
|
||||||
call .testEQ
|
|
||||||
ld iy, .t8
|
|
||||||
call .testEQ
|
|
||||||
ld iy, .t9
|
|
||||||
call .testEQ
|
|
||||||
ret
|
|
||||||
|
|
||||||
.testEQ:
|
.test:
|
||||||
push iy \ pop hl
|
push hl \ pop iy
|
||||||
inc hl \ inc hl
|
inc hl \ inc hl
|
||||||
call parseExpr
|
call parseExpr
|
||||||
call assertZ
|
call assertZ
|
||||||
ld l, (iy)
|
ld l, (iy)
|
||||||
ld h, (iy+1)
|
ld h, (iy+1)
|
||||||
call assertEQW
|
jp assertEQW
|
||||||
jp nexttest
|
|
||||||
|
|
||||||
.t1:
|
.t1:
|
||||||
.dw 7
|
.dw 7
|
||||||
@ -166,3 +100,46 @@ testParseExpr:
|
|||||||
.t9:
|
.t9:
|
||||||
.dw 10
|
.dw 10
|
||||||
.db "2*3+4", 0
|
.db "2*3+4", 0
|
||||||
|
|
||||||
|
; There was this untested regression during the replacement of find-and-subst
|
||||||
|
; parseExpr to the recursive descent one. It was time consuming to find. Here
|
||||||
|
; it goes, here it stays.
|
||||||
|
.t10:
|
||||||
|
.dw '-'+1
|
||||||
|
.db "'-'+1", 0
|
||||||
|
|
||||||
|
.t11:
|
||||||
|
.dw 0x4023
|
||||||
|
.db "0x4001+0x22", 0
|
||||||
|
|
||||||
|
.t12:
|
||||||
|
.dw 0x4020
|
||||||
|
.db "FOO+BAR", 0
|
||||||
|
|
||||||
|
.t13:
|
||||||
|
.dw 0x60
|
||||||
|
.db "BAR*3", 0
|
||||||
|
|
||||||
|
.t14:
|
||||||
|
.dw 0x3ffd
|
||||||
|
.db "FOO-3", 0
|
||||||
|
|
||||||
|
.t15:
|
||||||
|
.dw 0x4080
|
||||||
|
.db "FOO+BAR*4", 0
|
||||||
|
|
||||||
|
.alltests:
|
||||||
|
.dw .t1, .t2, .t3, .t4, .t5, .t6, .t7, .t8, .t9, .t10, .t11, .t12
|
||||||
|
.dw .t13, .t14, .t15, 0
|
||||||
|
|
||||||
|
; Ensure that stack is balanced on failure
|
||||||
|
testSPOnFail:
|
||||||
|
ld (testSP), sp
|
||||||
|
ld hl, .sFail
|
||||||
|
call parseExpr
|
||||||
|
call assertNZ
|
||||||
|
call assertSP
|
||||||
|
jp nexttest
|
||||||
|
|
||||||
|
.sFail: .db "1+abc123", 0
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ cmpas() {
|
|||||||
echo ok
|
echo ok
|
||||||
else
|
else
|
||||||
echo actual
|
echo actual
|
||||||
echo $ACTUAL
|
echo "$ACTUAL"
|
||||||
echo expected
|
echo expected
|
||||||
echo $EXPECTED
|
echo "$EXPECTED"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user