HL, instead of being preserved, is set to the character following the last read character.pull/85/head
@@ -43,10 +43,8 @@ basLDBAS: | |||
; Ok, line ready | |||
push hl ; --> lvl 1. current file position | |||
ld hl, SCRATCHPAD | |||
call parseDecimal | |||
call parseDecimalC | |||
jr nz, .notANumber | |||
push ix \ pop de | |||
call toSepOrEnd | |||
call rdSep | |||
call bufAdd | |||
pop hl ; <-- lvl 1 | |||
@@ -38,7 +38,7 @@ basLoop: | |||
call printstr | |||
call stdioReadLine | |||
call printcrlf | |||
call parseDecimal | |||
call parseDecimalC | |||
jr z, .number | |||
ld de, basCmds1 | |||
call basCallCmds | |||
@@ -47,8 +47,6 @@ basLoop: | |||
call basERR | |||
jr basLoop | |||
.number: | |||
push ix \ pop de | |||
call toSepOrEnd | |||
call rdSep | |||
call bufAdd | |||
jp nz, basERR | |||
@@ -91,14 +91,14 @@ cmdParse: | |||
jr z, .eof | |||
; inline parseDecimalDigit | |||
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff | |||
sub 0xff-9 ; maps to 0-9 and carries if not a digit | |||
add a, 0xff-'9' | |||
sub 0xff-9 | |||
jr c, .notHandled | |||
; straight number | |||
ld a, ABSOLUTE | |||
ld (ix), a | |||
call .parseDecimalM | |||
call parseDecimal | |||
ret nz | |||
dec de ; from 1-based to 0-base | |||
jr .end | |||
@@ -127,11 +127,11 @@ cmdParse: | |||
ld de, 1 ; if .pmNoSuffix | |||
; inline parseDecimalDigit | |||
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff | |||
sub 0xff-9 ; maps to 0-9 and carries if not a digit | |||
add a, 0xff-'9' | |||
sub 0xff-9 | |||
jr c, .pmNoSuffix | |||
call .parseDecimalM ; --> DE | |||
call parseDecimal ; --> DE | |||
.pmNoSuffix: | |||
pop af ; bring back that +/- | |||
cp '-' | |||
@@ -148,39 +148,3 @@ cmdParse: | |||
ld (ix+2), d | |||
cp a ; ensure Z | |||
ret | |||
; call parseDecimal and set HL to the character following the last digit | |||
.parseDecimalM: | |||
push bc | |||
push ix | |||
push hl | |||
.loop: | |||
inc hl | |||
ld a, (hl) | |||
; inline parseDecimalDigit | |||
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff | |||
sub 0xff-9 ; maps to 0-9 and carries if not a digit | |||
jr nc, .loop | |||
; We're at the first non-digit char. Let's save it because we're going | |||
; to temporarily replace it with a null. | |||
ld b, (hl) ; refetch (HL), A has been mucked with in | |||
; parseDecimalDigit | |||
xor a | |||
ld (hl), a | |||
; Now, let's go back to the beginning of the string and parse it. | |||
; but before we do this, let's save the end of string in DE | |||
ex de, hl | |||
pop hl | |||
call parseDecimal | |||
; Z is set properly at this point. nothing touches Z below. | |||
ld a, b | |||
ld (de), a | |||
ex de, hl ; put end of string back from DE to HL | |||
; Put addr in its final register, DE | |||
push ix \ pop de | |||
pop ix | |||
pop bc | |||
ret | |||
@@ -22,87 +22,83 @@ parseHex: | |||
ret | |||
; Parse the decimal char at A and extract it's 0-9 numerical value. Put the | |||
; result in A. | |||
; | |||
; On success, the carry flag is reset. On error, it is set. | |||
; Also, zero flag set if '0' | |||
; parseDecimalDigit has been replaced with the following code inline: | |||
; 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 | |||
; same conditions as parseLiteral. | |||
; Parse string at (HL) as a decimal value and return value in DE. | |||
; Reads as many digits as it can and stop when: | |||
; 1 - A non-digit character is read | |||
; 2 - The number overflows from 16-bit | |||
; HL is advanced to the character following the last successfully read char. | |||
; Error conditions are: | |||
; 1 - There wasn't at least one character that could be read. | |||
; 2 - Overflow. | |||
; Sets Z on success, unset on error. | |||
; To parse successfully, all characters following HL must be digits and those | |||
; digits must form a number that fits in 16 bits. To end the number, both \0 | |||
; and whitespaces (0x20 and 0x09) are accepted. There must be at least one | |||
; digit in the string. | |||
parseDecimal: | |||
push hl ; --> lvl 1 | |||
; First char is special: it has to succeed. | |||
ld a, (hl) | |||
; Parse the decimal char at A and extract it's 0-9 numerical value. Put the | |||
; result in A. | |||
; On success, the carry flag is reset. On error, it is set. | |||
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff | |||
sub 0xff-9 ; maps to 0-9 and carries if not a digit | |||
jr c, .error ; not a digit on first char? error | |||
exx ; preserve bc, hl, de | |||
ret c ; Error. If it's C, it's also going to be NZ | |||
; During this routine, we switch between HL and its shadow. On one side, | |||
; we have HL the string pointer, and on the other side, we have HL the | |||
; numerical result. We also use EXX to preserve BC, saving us a push. | |||
exx ; HL as a result | |||
ld h, 0 | |||
ld l, a ; load first digit in without multiplying | |||
ld b, 3 ; Carries can only occur for decimals >=5 in length | |||
ld b, 0 ; We use B to detect overflow | |||
.loop: | |||
exx | |||
exx ; HL as a string pointer | |||
inc hl | |||
ld a, (hl) | |||
exx | |||
exx ; HL as a numerical result | |||
; inline parseDecimalDigit | |||
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff | |||
sub 0xff-9 ; maps to 0-9 and carries if not a digit | |||
; same as other above | |||
add a, 0xff-'9' | |||
sub 0xff-9 | |||
jr c, .end | |||
add hl, hl ; x2 | |||
; We do this to detect overflow at each step | |||
rl b | |||
ld d, h | |||
ld e, l ; de is x2 | |||
add hl, hl ; x4 | |||
rl b | |||
add hl, hl ; x8 | |||
rl b | |||
add hl, de ; x10 | |||
rl b | |||
ld d, 0 | |||
ld e, a | |||
add hl, de | |||
jr c, .end ; if hl was 0x1999, it may carry here | |||
djnz .loop | |||
rl b | |||
; Did we oveflow? | |||
xor a | |||
or b | |||
jr z, .loop ; No? continue | |||
; error, NZ already set | |||
exx ; HL is now string pointer, restore BC | |||
; HL points to the char following the last success. | |||
ret | |||
inc b ; so loop only executes once more | |||
; only numbers >0x1999 can carry when multiplied by 10. | |||
ld de, 0xE666 | |||
ex de, hl | |||
add hl, de | |||
ex de, hl | |||
jr nc, .loop ; if it doesn't carry, it's small enough | |||
exx | |||
inc hl | |||
ld a, (hl) | |||
exx | |||
add a, 0xd0 ; the next line expects a null to be mapped to 0xd0 | |||
.end: | |||
; Because of the add and sub in parseDecimalDigit, null is mapped | |||
; 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 | |||
ret z | |||
; A is not 0? Ok, but if it's a space, we're happy too. | |||
push hl ; --> lvl 1, result | |||
exx ; HL as a string pointer, restore BC | |||
pop de ; <-- lvl 1, result | |||
cp a ; ensure Z | |||
ret | |||
; Call parseDecimal and then check that HL points to a whitespace or a null. | |||
parseDecimalC: | |||
call parseDecimal | |||
ret nz | |||
ld a, (hl) | |||
or a | |||
ret z ; null? we're happy | |||
jp isWS | |||
.error: | |||
pop hl ; <-- lvl 1, orig | |||
jp unsetZ | |||
; Parse string at (HL) as a hexadecimal value without the "0x" prefix and | |||
; return value in DE. | |||
@@ -188,7 +184,10 @@ parseLiteral: | |||
jr z, .char | |||
cp '0' | |||
jr z, .hexOrBin | |||
jp parseDecimal | |||
push hl | |||
call parseDecimalC | |||
pop hl | |||
ret | |||
; Parse string at (HL) and, if it is a char literal, sets Z and return | |||
; corresponding value in E. D is always zero. | |||
@@ -1,7 +1,3 @@ | |||
.equ RAMSTART 0x4000 | |||
; declare DIREC_LASTVAL manually so that we don't have to include directive.asm | |||
.equ DIREC_LASTVAL RAMSTART | |||
jp test | |||
.inc "core.asm" | |||
@@ -9,7 +5,6 @@ jp test | |||
.inc "lib/util.asm" | |||
.inc "zasm/util.asm" | |||
.inc "lib/parse.asm" | |||
.inc "zasm/parse.asm" | |||
; mocks. aren't used in tests | |||
zasmGetPC: | |||
@@ -28,8 +23,7 @@ s0b01010101: .db "0b01010101", 0 | |||
sFoo: .db "Foo", 0 | |||
test: | |||
ld hl, 0xffff | |||
ld sp, hl | |||
ld sp, 0xffff | |||
call testLiteral | |||
call testDecimal | |||
@@ -42,11 +36,10 @@ testLiteral: | |||
ld hl, s99 | |||
call parseLiteral | |||
jp nz, fail | |||
push ix \ pop hl | |||
ld a, h | |||
ld a, d | |||
or a | |||
jp nz, fail | |||
ld a, l | |||
ld a, e | |||
cp 99 | |||
jp nz, fail | |||
call nexttest | |||
@@ -54,11 +47,10 @@ testLiteral: | |||
ld hl, s0x100 | |||
call parseLiteral | |||
jp nz, fail | |||
push ix \ pop hl | |||
ld a, h | |||
ld a, d | |||
cp 1 | |||
jp nz, fail | |||
ld a, l | |||
ld a, e | |||
or a | |||
jp nz, fail | |||
call nexttest | |||
@@ -71,11 +63,10 @@ testLiteral: | |||
ld hl, s0b0101 | |||
call parseLiteral | |||
jp nz, fail | |||
push ix \ pop hl | |||
ld a, h | |||
ld a, d | |||
or a | |||
jp nz, fail | |||
ld a, l | |||
ld a, e | |||
cp 0b0101 | |||
jp nz, fail | |||
call nexttest | |||
@@ -83,11 +74,10 @@ testLiteral: | |||
ld hl, s0b01010101 | |||
call parseLiteral | |||
jp nz, fail | |||
push ix \ pop hl | |||
ld a, h | |||
ld a, d | |||
or a | |||
jp nz, fail | |||
ld a, l | |||
ld a, e | |||
cp 0b01010101 | |||
jp nz, fail | |||
call nexttest | |||
@@ -108,14 +98,15 @@ testDecimal: | |||
.loop1: | |||
push hl ; --> lvl 1 | |||
; put expected number in DE | |||
; put expected number in IX | |||
ld e, (hl) | |||
inc hl | |||
ld d, (hl) | |||
inc hl | |||
call parseDecimal | |||
push de \ pop ix | |||
call parseDecimalC ; --> DE | |||
jp nz, fail | |||
push ix \ pop hl | |||
push ix \ pop hl ; push expected number in HL | |||
ld a, h | |||
cp d | |||
jp nz, fail | |||
@@ -133,7 +124,9 @@ testDecimal: | |||
ld hl, .invalid | |||
.loop2: | |||
call parseDecimal | |||
push hl | |||
call parseDecimalC | |||
pop hl | |||
jp z, fail | |||
ld de, 7 ; row size | |||
add hl, de | |||