mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-02 20:20:55 +11:00
Compare commits
No commits in common. "15628da7de8703e9e6d9873e25289c50ab65bb14" and "7410891ad1bc1189874404e694fae2ea10f373ab" have entirely different histories.
15628da7de
...
7410891ad1
@ -98,9 +98,12 @@ parseLiteralOrVar:
|
||||
push hl ; --> lvl 1
|
||||
ld hl, VAR_TBL
|
||||
call addHL
|
||||
push de ; --> lvl 2
|
||||
ld e, (hl)
|
||||
inc hl
|
||||
ld d, (hl)
|
||||
push de \ pop ix
|
||||
pop de ; <-- lvl 2
|
||||
pop hl ; <-- lvl 1
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
|
@ -1,141 +1,177 @@
|
||||
; *** Requirements ***
|
||||
; ari
|
||||
; 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 DE. Z for success.
|
||||
; String in (HL), returns its parsed value to IX. Z for success.
|
||||
;
|
||||
; *** 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.
|
||||
; **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.
|
||||
parseExpr:
|
||||
push iy
|
||||
push ix
|
||||
push hl
|
||||
call _parseAddSubst
|
||||
call _parseExpr
|
||||
push ix \ pop de
|
||||
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
|
||||
_parseExpr:
|
||||
ld de, exprTbl
|
||||
.loop:
|
||||
; do we have an operator?
|
||||
ld a, (de)
|
||||
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 '+'
|
||||
jp z, EXPR_PARSE ; no operator, just parse the literal
|
||||
push de ; --> lvl 1. save operator row
|
||||
call _findAndSplit
|
||||
jr z, .found
|
||||
ld ix, .minus
|
||||
cp '-'
|
||||
ret nz ; unknown char, error
|
||||
.found:
|
||||
ld iy, _parseMultDiv
|
||||
call _parseApply
|
||||
ret nz
|
||||
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:
|
||||
ld a, (hl)
|
||||
or a
|
||||
jr z, .noleft
|
||||
; Parse left operand in (HL)
|
||||
push de ; --> lvl 1
|
||||
call parseExpr
|
||||
pop hl ; <-- lvl 1, orig DE
|
||||
ret nz ; return immediately if error
|
||||
.parseright:
|
||||
; Now we have parsed everything to the left and we have its result in
|
||||
; DE. What we need to do now is the same thing on (DE) and then apply
|
||||
; the + operator. Let's save DE somewhere and parse this.
|
||||
push de ; --> lvl 1
|
||||
; right expr in (HL)
|
||||
call parseExpr ; DE is set
|
||||
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:
|
||||
add hl, de
|
||||
ex de, hl
|
||||
ret
|
||||
|
||||
.minus:
|
||||
or a ; clear carry
|
||||
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
|
||||
push bc
|
||||
call divide
|
||||
ld e, c
|
||||
ld d, b
|
||||
pop bc ; <-- lvl 1
|
||||
pop bc
|
||||
ret
|
||||
|
||||
.mod:
|
||||
@ -143,39 +179,6 @@ _parseMultDiv:
|
||||
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
|
||||
@ -206,126 +209,26 @@ _parseBitShift:
|
||||
ld a, e
|
||||
and 0xf
|
||||
ret z
|
||||
push bc ; --> lvl 1
|
||||
push bc
|
||||
ld b, a
|
||||
.rshiftLoop:
|
||||
srl h
|
||||
rr l
|
||||
djnz .rshiftLoop
|
||||
ex de, hl
|
||||
pop bc ; <-- lvl 1
|
||||
pop bc
|
||||
ret
|
||||
|
||||
.lshift:
|
||||
ld a, e
|
||||
and 0xf
|
||||
ret z
|
||||
push bc ; --> lvl 1
|
||||
push bc
|
||||
ld b, a
|
||||
.lshiftLoop:
|
||||
sla l
|
||||
rl h
|
||||
djnz .lshiftLoop
|
||||
ex de, hl
|
||||
pop bc ; <-- lvl 1
|
||||
pop bc
|
||||
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
|
||||
; 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 DE under the
|
||||
; Parse string at (HL) as a decimal value and return value in IX under the
|
||||
; same conditions as parseLiteral.
|
||||
; Sets Z on success, unset on error.
|
||||
; To parse successfully, all characters following HL must be digits and those
|
||||
@ -76,7 +76,7 @@ parseHexPair:
|
||||
; digit in the string.
|
||||
|
||||
parseDecimal:
|
||||
push hl ; --> lvl 1
|
||||
push hl
|
||||
|
||||
ld a, (hl)
|
||||
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
|
||||
@ -129,23 +129,23 @@ parseDecimal:
|
||||
; to 0x00+(0xff-'9')-(0xff-9)=-0x30=0xd0
|
||||
sub 0xd0 ; if a is null, set Z
|
||||
; a is checked for null before any errors
|
||||
push hl ; --> lvl 2, result
|
||||
exx ; restore original bc
|
||||
pop de ; <-- lvl 2, result
|
||||
pop hl ; <-- lvl 1, orig
|
||||
push hl \ pop ix
|
||||
exx ; restore original de and bc
|
||||
pop hl
|
||||
ret z
|
||||
; A is not 0? Ok, but if it's a space, we're happy too.
|
||||
jp isWS
|
||||
.error:
|
||||
pop hl ; <-- lvl 1, orig
|
||||
pop hl
|
||||
jp unsetZ
|
||||
|
||||
; Parse string at (HL) as a hexadecimal value and return value in DE under the
|
||||
; Parse string at (HL) as a hexadecimal value and return value in IX under the
|
||||
; same conditions as parseLiteral.
|
||||
parseHexadecimal:
|
||||
call hasHexPrefix
|
||||
ret nz
|
||||
push hl
|
||||
push de
|
||||
ld d, 0
|
||||
inc hl ; get rid of "0x"
|
||||
inc hl
|
||||
@ -179,6 +179,8 @@ parseHexadecimal:
|
||||
.error:
|
||||
call unsetZ
|
||||
.end:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
@ -194,14 +196,15 @@ hasHexPrefix:
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Parse string at (HL) as a binary value (0b010101) and return value in E.
|
||||
; D is always zero.
|
||||
; Parse string at (HL) as a binary value (0b010101) and return value in IX.
|
||||
; High IX byte is always clear.
|
||||
; Sets Z on success.
|
||||
parseBinaryLiteral:
|
||||
call hasBinPrefix
|
||||
ret nz
|
||||
push bc
|
||||
push hl
|
||||
push de
|
||||
ld d, 0
|
||||
inc hl ; get rid of "0b"
|
||||
inc hl
|
||||
@ -233,6 +236,8 @@ parseBinaryLiteral:
|
||||
.error:
|
||||
call unsetZ
|
||||
.end:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
pop bc
|
||||
ret
|
||||
@ -250,7 +255,7 @@ hasBinPrefix:
|
||||
ret
|
||||
|
||||
; Parse string at (HL) and, if it is a char literal, sets Z and return
|
||||
; corresponding value in E. D is always zero.
|
||||
; corresponding value in IX. High IX byte is always clear.
|
||||
;
|
||||
; 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
|
||||
@ -261,6 +266,7 @@ parseCharLiteral:
|
||||
ret nz
|
||||
|
||||
push hl
|
||||
push de
|
||||
inc hl
|
||||
inc hl
|
||||
cp (hl)
|
||||
@ -277,10 +283,12 @@ parseCharLiteral:
|
||||
ld e, a
|
||||
cp a ; ensure Z
|
||||
.end:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Parses the string at (HL) and returns the 16-bit value in DE. The string
|
||||
; Parses the string at (HL) and returns the 16-bit value in IX. The string
|
||||
; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char
|
||||
; literal ('X').
|
||||
;
|
||||
|
@ -657,6 +657,7 @@ _readDouble:
|
||||
_readk7:
|
||||
push hl
|
||||
push de
|
||||
push ix
|
||||
call parseExpr
|
||||
jr nz, .end
|
||||
; If we're in first pass, stop now. The value of HL doesn't matter and
|
||||
@ -685,6 +686,7 @@ _readk7:
|
||||
ld a, l
|
||||
cp a ; ensure Z
|
||||
.end:
|
||||
pop ix
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
@ -705,6 +707,7 @@ _readR4:
|
||||
; Set Z for success.
|
||||
_readR5:
|
||||
push de
|
||||
push ix
|
||||
ld a, (hl)
|
||||
call upcase
|
||||
cp 'R'
|
||||
@ -712,9 +715,11 @@ _readR5:
|
||||
inc hl
|
||||
call parseDecimal
|
||||
jr nz, .end
|
||||
push ix \ pop de
|
||||
ld a, 31
|
||||
call _DE2A
|
||||
.end:
|
||||
pop ix
|
||||
pop de
|
||||
ret
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
; Parse string in (HL) and return its numerical value whether its a number
|
||||
; literal or a symbol. Returns value in DE.
|
||||
; literal or a symbol. Returns value in IX.
|
||||
; Sets Z if number or symbol is valid, unset otherwise.
|
||||
parseNumberOrSymbol:
|
||||
call parseLiteral
|
||||
@ -14,25 +14,31 @@ parseNumberOrSymbol:
|
||||
cp '@'
|
||||
jr nz, .symbol
|
||||
; last val
|
||||
ld de, (DIREC_LASTVAL)
|
||||
ld ix, (DIREC_LASTVAL)
|
||||
ret
|
||||
.symbol:
|
||||
push de ; --> lvl 1
|
||||
call symFindVal ; --> DE
|
||||
jr nz, .notfound
|
||||
; value in DE. We need it in IX
|
||||
push de \ pop ix
|
||||
pop de ; <-- lvl 1
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
.notfound:
|
||||
pop de ; <-- lvl 1
|
||||
; 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.
|
||||
; Otherwise return error. Z is already unset, so in fact, this is the
|
||||
; same as jumping to zasmIsFirstPass
|
||||
; however, before we do, load DE with zero. Returning dummy non-zero
|
||||
; however, before we do, load IX with zero. Returning dummy non-zero
|
||||
; values can have weird consequence (such as false overflow errors).
|
||||
ld de, 0
|
||||
ld ix, 0
|
||||
jp zasmIsFirstPass
|
||||
|
||||
.returnPC:
|
||||
push hl
|
||||
call zasmGetPC
|
||||
ex de, hl ; result in DE
|
||||
push hl \ pop ix
|
||||
pop hl
|
||||
ret
|
||||
|
@ -6,12 +6,6 @@
|
||||
; lib/fmt
|
||||
|
||||
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:
|
||||
out (0), a
|
||||
@ -57,38 +51,6 @@ assertEQW:
|
||||
.msg:
|
||||
.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:
|
||||
ld a, (testNum)
|
||||
inc a
|
||||
|
@ -34,6 +34,12 @@ zasmIsFirstPass:
|
||||
zasmGetPC:
|
||||
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
|
||||
sBAR: .db "BAR", 0
|
||||
@ -41,7 +47,25 @@ sBAR: .db "BAR", 0
|
||||
test:
|
||||
ld sp, 0xffff
|
||||
|
||||
; before testing begins, let's set up FOO and BAR symbols
|
||||
; New-style tests
|
||||
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
|
||||
ld hl, sFOO
|
||||
ld de, 0x4000
|
||||
@ -52,26 +76,68 @@ test:
|
||||
call symRegisterGlobal
|
||||
jp nz, fail
|
||||
|
||||
call testParseExpr
|
||||
call testSPOnFail
|
||||
ld hl, s3
|
||||
call parseExpr
|
||||
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
|
||||
xor a
|
||||
halt
|
||||
|
||||
testParseExpr:
|
||||
ld hl, .alltests
|
||||
ld ix, .test
|
||||
jp testList
|
||||
ld iy, .t1
|
||||
call .testEQ
|
||||
ld iy, .t2
|
||||
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
|
||||
|
||||
.test:
|
||||
push hl \ pop iy
|
||||
.testEQ:
|
||||
push iy \ pop hl
|
||||
inc hl \ inc hl
|
||||
call parseExpr
|
||||
call assertZ
|
||||
ld l, (iy)
|
||||
ld h, (iy+1)
|
||||
jp assertEQW
|
||||
call assertEQW
|
||||
jp nexttest
|
||||
|
||||
.t1:
|
||||
.dw 7
|
||||
@ -100,46 +166,3 @@ testParseExpr:
|
||||
.t9:
|
||||
.dw 10
|
||||
.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
|
||||
else
|
||||
echo actual
|
||||
echo "$ACTUAL"
|
||||
echo $ACTUAL
|
||||
echo expected
|
||||
echo "$EXPECTED"
|
||||
echo $EXPECTED
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user