lib/parse: make parseDecimal "tail" HL

HL, instead of being preserved, is set to the character following
the last read character.
This commit is contained in:
Virgil Dupras 2019-12-30 10:13:55 -05:00
parent dcb96aefe9
commit 289037a3dd
5 changed files with 78 additions and 126 deletions

View File

@ -43,10 +43,8 @@ basLDBAS:
; Ok, line ready ; Ok, line ready
push hl ; --> lvl 1. current file position push hl ; --> lvl 1. current file position
ld hl, SCRATCHPAD ld hl, SCRATCHPAD
call parseDecimal call parseDecimalC
jr nz, .notANumber jr nz, .notANumber
push ix \ pop de
call toSepOrEnd
call rdSep call rdSep
call bufAdd call bufAdd
pop hl ; <-- lvl 1 pop hl ; <-- lvl 1

View File

@ -38,7 +38,7 @@ basLoop:
call printstr call printstr
call stdioReadLine call stdioReadLine
call printcrlf call printcrlf
call parseDecimal call parseDecimalC
jr z, .number jr z, .number
ld de, basCmds1 ld de, basCmds1
call basCallCmds call basCallCmds
@ -47,8 +47,6 @@ basLoop:
call basERR call basERR
jr basLoop jr basLoop
.number: .number:
push ix \ pop de
call toSepOrEnd
call rdSep call rdSep
call bufAdd call bufAdd
jp nz, basERR jp nz, basERR

View File

@ -91,14 +91,14 @@ cmdParse:
jr z, .eof jr z, .eof
; inline parseDecimalDigit ; inline parseDecimalDigit
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff add a, 0xff-'9'
sub 0xff-9 ; maps to 0-9 and carries if not a digit sub 0xff-9
jr c, .notHandled jr c, .notHandled
; straight number ; straight number
ld a, ABSOLUTE ld a, ABSOLUTE
ld (ix), a ld (ix), a
call .parseDecimalM call parseDecimal
ret nz ret nz
dec de ; from 1-based to 0-base dec de ; from 1-based to 0-base
jr .end jr .end
@ -127,11 +127,11 @@ cmdParse:
ld de, 1 ; if .pmNoSuffix ld de, 1 ; if .pmNoSuffix
; inline parseDecimalDigit ; inline parseDecimalDigit
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff add a, 0xff-'9'
sub 0xff-9 ; maps to 0-9 and carries if not a digit sub 0xff-9
jr c, .pmNoSuffix jr c, .pmNoSuffix
call .parseDecimalM ; --> DE call parseDecimal ; --> DE
.pmNoSuffix: .pmNoSuffix:
pop af ; bring back that +/- pop af ; bring back that +/-
cp '-' cp '-'
@ -148,39 +148,3 @@ cmdParse:
ld (ix+2), d ld (ix+2), d
cp a ; ensure Z cp a ; ensure Z
ret 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

View File

@ -22,87 +22,83 @@ parseHex:
ret ret
; Parse the decimal char at A and extract it's 0-9 numerical value. Put the ; Parse string at (HL) as a decimal value and return value in DE.
; result in A. ; Reads as many digits as it can and stop when:
; ; 1 - A non-digit character is read
; On success, the carry flag is reset. On error, it is set. ; 2 - The number overflows from 16-bit
; Also, zero flag set if '0' ; HL is advanced to the character following the last successfully read char.
; parseDecimalDigit has been replaced with the following code inline: ; Error conditions are:
; add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff ; 1 - There wasn't at least one character that could be read.
; sub 0xff-9 ; maps to 0-9 and carries if not a digit ; 2 - Overflow.
; Parse string at (HL) as a decimal value and return value in DE under the
; 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
; 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: parseDecimal:
push hl ; --> lvl 1 ; First char is special: it has to succeed.
ld a, (hl) 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 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
jr c, .error ; not a digit on first char? error ret c ; Error. If it's C, it's also going to be NZ
exx ; preserve bc, hl, de ; 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 h, 0
ld l, a ; load first digit in without multiplying 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: .loop:
exx exx ; HL as a string pointer
inc hl inc hl
ld a, (hl) ld a, (hl)
exx exx ; HL as a numerical result
; inline parseDecimalDigit ; same as other above
add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff add a, 0xff-'9'
sub 0xff-9 ; maps to 0-9 and carries if not a digit sub 0xff-9
jr c, .end jr c, .end
add hl, hl ; x2 add hl, hl ; x2
; We do this to detect overflow at each step
rl b
ld d, h ld d, h
ld e, l ; de is x2 ld e, l ; de is x2
add hl, hl ; x4 add hl, hl ; x4
rl b
add hl, hl ; x8 add hl, hl ; x8
rl b
add hl, de ; x10 add hl, de ; x10
rl b
ld d, 0 ld d, 0
ld e, a ld e, a
add hl, de add hl, de
jr c, .end ; if hl was 0x1999, it may carry here rl b
djnz .loop ; 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: .end:
; Because of the add and sub in parseDecimalDigit, null is mapped push hl ; --> lvl 1, result
; to 0x00+(0xff-'9')-(0xff-9)=-0x30=0xd0 exx ; HL as a string pointer, restore BC
sub 0xd0 ; if a is null, set Z pop de ; <-- lvl 1, result
; a is checked for null before any errors cp a ; ensure Z
push hl ; --> lvl 2, result ret
exx ; restore original bc
pop de ; <-- lvl 2, result ; Call parseDecimal and then check that HL points to a whitespace or a null.
pop hl ; <-- lvl 1, orig parseDecimalC:
ret z call parseDecimal
; A is not 0? Ok, but if it's a space, we're happy too. ret nz
ld a, (hl)
or a
ret z ; null? we're happy
jp isWS jp isWS
.error:
pop hl ; <-- lvl 1, orig
jp unsetZ
; Parse string at (HL) as a hexadecimal value without the "0x" prefix and ; Parse string at (HL) as a hexadecimal value without the "0x" prefix and
; return value in DE. ; return value in DE.
@ -188,7 +184,10 @@ parseLiteral:
jr z, .char jr z, .char
cp '0' cp '0'
jr z, .hexOrBin 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 ; 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 E. D is always zero.

View File

@ -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 jp test
.inc "core.asm" .inc "core.asm"
@ -9,7 +5,6 @@ jp test
.inc "lib/util.asm" .inc "lib/util.asm"
.inc "zasm/util.asm" .inc "zasm/util.asm"
.inc "lib/parse.asm" .inc "lib/parse.asm"
.inc "zasm/parse.asm"
; mocks. aren't used in tests ; mocks. aren't used in tests
zasmGetPC: zasmGetPC:
@ -28,8 +23,7 @@ s0b01010101: .db "0b01010101", 0
sFoo: .db "Foo", 0 sFoo: .db "Foo", 0
test: test:
ld hl, 0xffff ld sp, 0xffff
ld sp, hl
call testLiteral call testLiteral
call testDecimal call testDecimal
@ -42,11 +36,10 @@ testLiteral:
ld hl, s99 ld hl, s99
call parseLiteral call parseLiteral
jp nz, fail jp nz, fail
push ix \ pop hl ld a, d
ld a, h
or a or a
jp nz, fail jp nz, fail
ld a, l ld a, e
cp 99 cp 99
jp nz, fail jp nz, fail
call nexttest call nexttest
@ -54,11 +47,10 @@ testLiteral:
ld hl, s0x100 ld hl, s0x100
call parseLiteral call parseLiteral
jp nz, fail jp nz, fail
push ix \ pop hl ld a, d
ld a, h
cp 1 cp 1
jp nz, fail jp nz, fail
ld a, l ld a, e
or a or a
jp nz, fail jp nz, fail
call nexttest call nexttest
@ -71,11 +63,10 @@ testLiteral:
ld hl, s0b0101 ld hl, s0b0101
call parseLiteral call parseLiteral
jp nz, fail jp nz, fail
push ix \ pop hl ld a, d
ld a, h
or a or a
jp nz, fail jp nz, fail
ld a, l ld a, e
cp 0b0101 cp 0b0101
jp nz, fail jp nz, fail
call nexttest call nexttest
@ -83,11 +74,10 @@ testLiteral:
ld hl, s0b01010101 ld hl, s0b01010101
call parseLiteral call parseLiteral
jp nz, fail jp nz, fail
push ix \ pop hl ld a, d
ld a, h
or a or a
jp nz, fail jp nz, fail
ld a, l ld a, e
cp 0b01010101 cp 0b01010101
jp nz, fail jp nz, fail
call nexttest call nexttest
@ -108,14 +98,15 @@ testDecimal:
.loop1: .loop1:
push hl ; --> lvl 1 push hl ; --> lvl 1
; put expected number in DE ; put expected number in IX
ld e, (hl) ld e, (hl)
inc hl inc hl
ld d, (hl) ld d, (hl)
inc hl inc hl
call parseDecimal push de \ pop ix
call parseDecimalC ; --> DE
jp nz, fail jp nz, fail
push ix \ pop hl push ix \ pop hl ; push expected number in HL
ld a, h ld a, h
cp d cp d
jp nz, fail jp nz, fail
@ -133,7 +124,9 @@ testDecimal:
ld hl, .invalid ld hl, .invalid
.loop2: .loop2:
call parseDecimal push hl
call parseDecimalC
pop hl
jp z, fail jp z, fail
ld de, 7 ; row size ld de, 7 ; row size
add hl, de add hl, de