From 289037a3dd939120d65a04f05132d12fe3366ed7 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Mon, 30 Dec 2019 10:13:55 -0500 Subject: [PATCH] lib/parse: make parseDecimal "tail" HL HL, instead of being preserved, is set to the character following the last read character. --- apps/basic/fs.asm | 4 +- apps/basic/main.asm | 4 +- apps/ed/cmd.asm | 48 ++----------- apps/lib/parse.asm | 109 +++++++++++++++--------------- tools/tests/unit/test_parse_z.asm | 39 +++++------ 5 files changed, 78 insertions(+), 126 deletions(-) diff --git a/apps/basic/fs.asm b/apps/basic/fs.asm index 7cce471..1272f60 100644 --- a/apps/basic/fs.asm +++ b/apps/basic/fs.asm @@ -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 diff --git a/apps/basic/main.asm b/apps/basic/main.asm index bd444bb..34fea33 100644 --- a/apps/basic/main.asm +++ b/apps/basic/main.asm @@ -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 diff --git a/apps/ed/cmd.asm b/apps/ed/cmd.asm index fd424d3..46e58c4 100644 --- a/apps/ed/cmd.asm +++ b/apps/ed/cmd.asm @@ -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 - diff --git a/apps/lib/parse.asm b/apps/lib/parse.asm index 9536379..8f18222 100644 --- a/apps/lib/parse.asm +++ b/apps/lib/parse.asm @@ -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. diff --git a/tools/tests/unit/test_parse_z.asm b/tools/tests/unit/test_parse_z.asm index 285ba5f..f933808 100644 --- a/tools/tests/unit/test_parse_z.asm +++ b/tools/tests/unit/test_parse_z.asm @@ -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