diff --git a/apps/ed/cmd.asm b/apps/ed/cmd.asm index e7b8f23..7d4c7da 100644 --- a/apps/ed/cmd.asm +++ b/apps/ed/cmd.asm @@ -89,7 +89,11 @@ cmdParse: jr z, .dot cp '$' jr z, .eof - call parseDecimalDigit + + ; 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 c, .notHandled ; straight number ld a, ABSOLUTE @@ -121,7 +125,11 @@ cmdParse: inc hl ; advance cmd cursor ld a, (hl) ld de, 1 ; if .pmNoSuffix - call parseDecimalDigit + + ; 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 c, .pmNoSuffix call .parseDecimalM ; --> DE .pmNoSuffix: @@ -149,7 +157,11 @@ cmdParse: .loop: inc hl ld a, (hl) - call parseDecimalDigit + + ; 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. diff --git a/apps/lib/parse.asm b/apps/lib/parse.asm index 6ab6e16..c51aad2 100644 --- a/apps/lib/parse.asm +++ b/apps/lib/parse.asm @@ -1,5 +1,5 @@ ; *** Requirements *** -; unsetZ +; None ; ; *** Code *** @@ -7,58 +7,71 @@ ; result in A. ; ; On success, the carry flag is reset. On error, it is set. -parseDecimalDigit: - ; First, let's see if we have an easy 0-9 case - cp '0' - ret c ; if < '0', we have a problem - sub '0' ; our value now is valid if it's < 10 - cp 10 ; on success, C is set, which is the opposite - ; of what we want - ccf ; invert C flag - ret +; 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 IX under the ; same conditions as parseLiteral. ; Sets Z on success, unset on error. + parseDecimal: - push hl - push de + push hl + + ld a, (hl) + add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff + sub 0xff-9 ; maps to 0-9 and carries if not a digit + exx ; preserve bc, hl, de + ld h, 0 + ld l, a ; load first digit in without multiplying + ld b, 3 ; Carries can only occur for decimals >=5 in length + jr c, .end - ld ix, 0 .loop: - ld a, (hl) - or a - jr z, .end ; success! - call parseDecimalDigit - jr c, .error - - ; Now, let's add A to IX. First, multiply by 10. - push ix \ pop de - add ix, ix ; x2 - jr c, .error - add ix, ix ; x4 - jr c, .error - add ix, ix ; x8 - jr c, .error - add ix, de ; x9 - jr c, .error - add ix, de ; x10 - jr c, .error + exx + inc hl + ld a, (hl) + exx + + ; 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 c, .end + + add hl, hl ; x2 + ld d, h + ld e, l ; de is x2 + add hl, hl ; x4 + add hl, hl ; x8 + add hl, de ; x10 ld d, 0 ld e, a - add ix, de - jr c, .error + add hl, de + jr c, .end ; if hl was 0x1999, it may carry here + djnz .loop - inc hl - jr .loop - cp a ; ensure Z - jr .end -.error: - call unsetZ + 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: - pop de + ; 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 \ pop ix + exx ; restore original de and bc pop hl ret - - diff --git a/kernel/core.asm b/kernel/core.asm index 1289351..be41e97 100644 --- a/kernel/core.asm +++ b/kernel/core.asm @@ -112,14 +112,16 @@ callIY: jp (iy) ; Ensures that Z is unset (more complicated than it sounds...) +; There are often better inline alternatives, either replacing rets with +; appropriate jmps, or if an 8 bit register is known to not be 0, an inc +; then a dec. If a is nonzero, 'or a' is optimal. unsetZ: - push bc - ld b, a - inc b - cp b - pop bc + or a ;if a nonzero, Z reset + ret nz + cp 1 ;if a is zero, Z reset ret + ; *** STRINGS *** ; Fill B bytes at (HL) with A @@ -165,15 +167,22 @@ findchar: pop bc ret + ; Format the lower nibble of A into a hex char and stores the result in A. fmtHex: - and 0xf - cp 10 - jr nc, .alpha ; if >= 10, we have alpha - add a, '0' - ret -.alpha: - add a, 'A'-10 + ; The idea here is that there's 7 characters between '9' and 'A' + ; in the ASCII table, and so we add 7 if the digit is >9. + ; daa is designed for using Binary Coded Decimal format, where each + ; nibble represents a single base 10 digit. If a nibble has a value >9, + ; it adds 6 to that nibble, carrying to the next nibble and bringing the + ; value back between 0-9. This gives us 6 of that 7 we needed to add, so + ; then we just condtionally set the carry and add that carry, along with + ; a number that maps 0 to '0'. We also need the upper nibble to be a + ; set value, and have the N, C and H flags clear. + or 0xf0 + daa ; now a =0x50 + the original value + 0x06 if >= 0xfa + add a, 0xa0 ; cause a carry for the values that were >=0x0a + adc a, 0x40 ret ; Formats value in A into a string hex pair. Stores it in the memory location @@ -190,7 +199,6 @@ fmtHexPair: dec hl pop af push af - and 0xf0 rra \ rra \ rra \ rra call fmtHex ld (hl), a @@ -198,6 +206,7 @@ fmtHexPair: pop af ret + ; Compares strings pointed to by HL and DE up to A count of characters. If ; equal, Z is set. If not equal, Z is reset. strncmp: diff --git a/kernel/parse.asm b/kernel/parse.asm index 1bd81b2..8f7397b 100644 --- a/kernel/parse.asm +++ b/kernel/parse.asm @@ -11,27 +11,19 @@ ; On success, the carry flag is reset. On error, it is set. parseHex: ; First, let's see if we have an easy 0-9 case - cp '0' - jr c, .error ; if < '0', we have a problem - cp '9'+1 - jr nc, .alpha ; if >= '9'+1, we might have alpha - ; We are in the 0-9 range - sub '0' ; C is clear + + add a, 0xc6 ; maps '0'-'9' onto 0xf6-0xff + sub 0xf6 ; maps to 0-9 and carries if not a digit + ret nc + + and 0xdf ; converts lowercase to uppercase + add a, 0xe9 ; map 0x11-x017 onto 0xFA - 0xFF + sub 0xfa ; map onto 0-6 + ret c + ; we have an A-F digit + add a, 10 ; C is clear, map back to 0xA-0xF ret -.alpha: - call upcase - cp 'A' - jr c, .error ; if < 'A', we have a problem - cp 'F'+1 - jr nc, .error ; if >= 'F', we have a problem - ; We have alpha. - sub 'A'-10 ; C is clear - ret - -.error: - scf - ret ; Parses 2 characters of the string pointed to by HL and returns the numerical ; value in A. If the second character is a "special" character (<0x21) we don't