The following routines provide an assembly-language library for converting
64-bit integers to and from ASCII, such as would be required when preparing
user-supplied data for qword arithmetic or FPU instructions. The library
consists of the routines ParseRadixSigned, ParseRadixUnsigned,
PrintRadixSigned, and PrintRadixUnsigned, and the macro Divide64. Wrappers for
calling the routines from C code have also been provided.
ParseRadix
ParseRadix is a pair of routines for converting an ASCII string to a signed or
unsigned 64-bit integer, using a given radix as a base. The routines take a
pointer to a string and an integer radix as input, and return a 64-bit number.
;-------------------------------------------------------------------------
ParseRadixUnsigned PROC
; Input: Pointer to zero-terminated string in ESI, radix in EDI
; Output: Parsed number in EDX::EAX
; Uses: EAX, EBX, ECX, EDX, ESI, EDI
xor ebx, ebx
; result in EDX::EAX
xor eax, eax
xor edx, edx
mov al, [esi]
inc esi
test eax, eax
jz @@endOfParsing
sub eax, 30h
.IF eax > 9
sub eax, 7
.ENDIF
mov bl, [esi]
@@smallParseLoop:
; ASCII to number conversion
sub ebx, 30h
inc esi
mul edi
.IF ebx > 9
sub ebx, 7
.ENDIF
add eax, ebx
mov bl, [esi]
jc @@carry
test ebx, ebx
jnz @@smallParseLoop
ret
@@carry:
inc edx
test ebx, ebx
jz @@endOfParsing
@@bigParseLoop:
; ASCII to number conversion
mov ecx, eax
mov eax, edx
sub ebx, 30h
inc esi
mul edi
xchg eax, ecx
mul edi
.IF ebx > 9
sub ebx, 7
.ENDIF
add eax, ebx
mov bl, [esi]
adc edx, ecx
test ebx, ebx
jnz @@bigParseLoop
@@endOfParsing:
ret
ParseRadixUnsigned ENDP
ParseRadixSigned PROC
; Input: Pointer to zero-terminated string in ESI, radix in EDI
; Output: Parsed number in EDX::EAX
; Uses: EAX, EBX, ECX, EDX, ESI, EDI
.code
; If string does not start with a '-', consider it positive
cmp byte ptr [esi], '-'
jne ParseRadixUnsigned
; Number is negative, first parse the absolute value
inc esi
call ParseRadixUnsigned
; Now negate the absolute value to get the negative result
neg edx
neg eax
sbb edx, 0
ret
ParseRadixSigned ENDP
;-------------------------------------------------------------------------
The following is a wrapper used for calling the ParseRadix routines from C.
The wrapper provides the following C functions:
extern unsigned __int64 __stdcall
ParseRadixUnsignedC(char *lpBuffer, unsigned int radix);
extern signed __int64 __stdcall
ParseRadixSignedC(char *lpBuffer, unsigned int radix);
;-------------------------------------------------------------------------
.386
.Model Flat, StdCall
.code
include ParseRadix.asm
ParseRadixUnsignedC PROC lpBuffer:PTR BYTE, radix:DWORD
push esi
mov esi, [lpBuffer]
push edi
mov edi, [radix]
push ebx
call ParseRadixUnsigned
pop ebx
pop edi
pop esi
ret
ParseRadixUnsignedC ENDP
ParseRadixSignedC PROC lpBuffer:PTR BYTE, radix:DWORD
push esi
mov esi, [lpBuffer]
push edi
mov edi, [radix]
push ebx
call ParseRadixSigned
pop ebx
pop edi
pop esi
ret
ParseRadixSignedC ENDP
END
;-------------------------------------------------------------------------
Divide64
Divide64 is a macro for doing 64-bit division using 32-bit integer instructions.
Note that this is a 'long division' algorithm. It can easily be expanded to
be able to divide any number by 32 bits. I only use it for 64 bits here to
keep the CPU from getting an exception on overflow when the input is larger
than ((2^32)-1)*divisor, so that printing any 64 bit number with any radix
is possible.
;-------------------------------------------------------------------------
Divide64 MACRO
; Input: 64 bit dividend in EBX::ECX, 32 bit divisor in ESI
; Output: 64 bit result in EBX::EAX, 32 bit remainder in EDX
; Uses: EAX, EBX, ECX, EDX, ESI
; Divide high dword by divisor.
mov eax, ebx
xor edx, edx
div esi
; Put remainder as high dword of the original dividend.
mov ebx, eax
mov eax, ecx
div esi
ENDM
;-------------------------------------------------------------------------
PrintRadix
PrintRadix is a pair of routines for converting signed and unsigned 64-bit
numbers to an ASCII, string, using a given radix as base. These routines take a
64-bit number and an integer radix as inpit, and return the pointer to a
character buffer.
;-------------------------------------------------------------------------
PrintRadixUnsigned PROC
; Input: 64 bit unsigned number in EBX::ECX, radix in ESI, pointer to output
; buffer in EDI
; Output: Zero-terminated ASCII string in output buffer, length of string in
; EAX
; Uses: EAX, EBX, ECX, EDX, ESI, EDI, EBP
xor ebp, ebp ; StringLength counter
; If the high dword of the number is larger than the divisor, we
; have to do a 'long division' to prevent overflow.
cmp ebx, esi
jb smallDiv
- longDiv
-
Divide64
; Convert the remainder to an ASCII char.
add edx, 30h
dec esp
.IF edx > 39h
add edx, 7
.ENDIF
; Store char on stack.
inc ebp
; While result is not 0, we loop.
test eax, eax
mov ecx, eax
mov [esp], dl
jz lowDWORDIsZero
cmp ebx, esi
jae longDiv
- smallDiv
-
; Set EBX::ECX to EDX::EAX for a normal 64->32 division.
mov edx, ebx
mov eax, ecx
- radixLoopSmall
-
div esi
; Convert the remainder to an ASCII char.
add edx, 30h
dec esp
.IF edx > 39h
add edx, 7
.ENDIF
; Store char on stack.
inc ebp
mov [esp], dl
; Clean out high dword for next division.
xor edx, edx
; While result is not 0, we loop.
test eax, eax
jnz radixLoopSmall
- toBuffer
-
mov eax, ebp ; Return stringlength (not including 0-terminator)
- toBufferLoop
-
; Copy the string from stack to the destination buffer.
inc edi
mov dl, [esp]
inc esp
dec ebp
mov [edi-1], dl
jnz toBufferLoop
; Zero terminate the string.
mov byte ptr [edi], 0
ret
- lowDWORDIsZero
-
test ebx, ebx
jnz longDiv
; We have the final string, time to copy it to the destination buffer.
jmp toBuffer
PrintRadixUnsigned ENDP
PrintRadixSigned PROC
; Input: 64 bit signed number in EBX::ECX, radix in ESI, pointer to output
; buffer in EDI
; Output: Zero-terminated ASCII string in output buffer, length of string in
; EAX
; Uses: EAX, EBX, ECX, EDX, ESI, EDI, EBP
; If number is non-negative, use the normal PrintRadix
test ebx, ebx
jns PrintRadixUnsigned
; Prefix the number with a - sign
mov byte ptr [edi], '-'
inc edi
; Negate the 64 bit number
neg ebx
neg ecx
sbb ebx, 0
; Do a normal PrintRadix
call PrintRadixUnsigned
inc eax
ret
PrintRadixSigned ENDP
;-------------------------------------------------------------------------
The following is a wrapper used for calling the PrintRadix routines from C.
The wrapper provides the following C functions:
extern unsigned int __stdcall
PrintRadixUnsignedC(char *lpBuffer, unsigned __int64 number,
unsigned int radix);
extern unsigned int __stdcall
PrintRadixSignedC(char *lpBuffer, signed __int64 number,
unsigned int radix);
;-------------------------------------------------------------------------
.386
.Model Flat, StdCall
.code
include PrintRadix.asm
PrintRadixUnsignedC PROC lpBuffer:PTR BYTE, number:QWORD, radix:DWORD
push ebp
mov ecx, dword ptr [number]
push ebx
mov ebx, dword ptr [number+sizeof DWORD]
push esi
mov esi, [radix]
push edi
mov edi, [lpBuffer]
call PrintRadixUnsigned
pop edi
pop esi
pop ebx
pop ebp
ret
PrintRadixUnsignedC ENDP
PrintRadixSignedC PROC lpBuffer:PTR BYTE, number:QWORD, radix:DWORD
push ebp
mov ecx, dword ptr [number]
push ebx
mov ebx, dword ptr [number+sizeof DWORD]
push esi
mov esi, [radix]
push edi
mov edi, [lpBuffer]
call PrintRadixSigned
pop edi
pop esi
pop ebx
pop ebp
ret
PrintRadixSignedC ENDP
END
;-------------------------------------------------------------------------
|