Here I present you with a library routine that scans a value from a string and converts it to an integer. It is very useful, not only when you have to convert string->value but also if you are parsing and want to recognise a numeric token.
The routine will scan values in any radix from 0 to 36. Characters
for the digit values from 10-35 are naturally "A"-"Z"/"a"-"z".
With this routine there are 2 API's 'scanur' and 'scanu'. 'scanur'
is used to set the radix of the scan conversion. Once this value is
set the main routine 'scanu' can be called freely to scan values from
the string.
The scan routine is called with a string pointer which is updated
on exit to the first invalid character. It will return with the carry
flag set if the value was too big to fit into the return register EAX.
If the carry flag is clear, there is no error, however now the zero flag
indicates if a valid value was actually scanned. This return status
convention gives the most flexibility to the application programmer,
also if a valid value MUST be scanned they can detect the condition
via:-
CALL NEAR PTR scanu
JNA error ;get out if overflow/no value
The branch will be taken if CF=1 or ZF=1. Hence, if a value has to be
scanned errors may be picked up with only one test.
=========START OF CODE=====================================================
;
;(current scan radix)
;
scanuradi:
DB ?
;
;scanur- set up for scanu routine
;
;entry: AL=radix
;
; !! radix must be in range 0<=radix<=36
;
; !! radix must be set by calling this routine prior to
; !! using scanu
;
;exit: (all registers preserved)
;
scanur PROC NEAR
MOV BYTE PTR CS:[scanuradi],AL
RET
scanur ENDP
;
;scanu- scan string value returning result
;
;entry: DS:SI=address of string
; DF=0
;
; !! radix must be set previously by calling 'scanur'
;
;exit: SI=updated to offset of first invalid character
;
; CF=1
; a numeric overflow has occurred, ie: the number being scanned
; has become too big to fit into EAX
;
; CF=0
; if ZF=0 then a valid value was scanned, if ZF=1 then no
; valid digits were scanned
;
; EAX=converted value
;
scanu PROC NEAR
;
;preserve registers
;
PUSH EDX
PUSH EBX
PUSH ECX
PUSH DI
;
;initialise
; EBX=radix constant
; EAX=total
; ECX=0, bits8-24 of ECX always=0 to pad byte digit to dword
; DI=holds original offset
;
XOR EAX,EAX
XOR EBX,EBX
XOR ECX,ECX
MOV DI,SI
MOV BL,BYTE PTR CS:[scanuradi]
;
;main loop start
; EAX,ECX change roles so that we can use AL for the digit calculation
; saving code length
;
lop: XCHG EAX,ECX
LODSB
;
;if "0"-"9" map to 0-9 and skip to radix check
;
SUB AL,030h
CMP AL,0Ah
JC SHORT ko
ADD AL,030h
;
;map "A"-"Z"-/"a"-"z"- to 10-35- aborting on the one invalid value (040h)
;that won't get trapped in the next stage
;
AND AL,0DFh
SUB AL,037h
CMP AL,0Ah
JC SHORT ko2
;
;digit value checked that it is valid for the current radix
;this also weeds out previous invalid values (since they would be >35)
;jump out of loop is delayed so that EAX can be restored for exit
;
ko: CMP AL,BL
CMC
ko2: XCHG EAX,ECX
JC SHORT erriv
;
;accumalate the digit to the total. the total must be pre-multiplied.
;checks for overflow are done at both points so the routine can never
;generate false results
;
MUL EBX
JC errovr
ADD EAX,ECX
JNC lop
;
;overflow error
; adjust SI index to current char and exit, note
; that CF =1 already
;
errovr: DEC SI
JMP SHORT don
;
;invalid character
; main exit point, SI is adjusted to the current char
; the CMP ensures that CF =0, and also that ZF =1 iff
; no chars have been read
;
erriv: DEC SI
CMP SI,DI
;
;(restore registers and exit)
;
don: POP DI
POP ECX
POP EBX
POP EDX
RET
scanu ENDP
=========END OF CODE=======================================================
|