Parcourir la source

lib/parse: make parseDecimal "tail" HL

HL, instead of being preserved, is set to the character following
the last read character.
pull/85/head
Virgil Dupras il y a 4 ans
Parent
révision
289037a3dd
5 fichiers modifiés avec 78 ajouts et 126 suppressions
  1. +1
    -3
      apps/basic/fs.asm
  2. +1
    -3
      apps/basic/main.asm
  3. +6
    -42
      apps/ed/cmd.asm
  4. +54
    -55
      apps/lib/parse.asm
  5. +16
    -23
      tools/tests/unit/test_parse_z.asm

+ 1
- 3
apps/basic/fs.asm Voir le fichier

@@ -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


+ 1
- 3
apps/basic/main.asm Voir le fichier

@@ -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


+ 6
- 42
apps/ed/cmd.asm Voir le fichier

@@ -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


+ 54
- 55
apps/lib/parse.asm Voir le fichier

@@ -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.


+ 16
- 23
tools/tests/unit/test_parse_z.asm Voir le fichier

@@ -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


Chargement…
Annuler
Enregistrer