Conversion of the unsigned 64-bit number held in EDX:EAX
On x86 a cascade of 2 divisions is needed to divide the 64-bit value in EDX:EAX by 10.
The 1st division divides the high dividend (extended with 0) yielding a high quotient. The 2nd division divides the low dividend (extended with the remainder from the 1st division) yielding the low quotient. It's the remainder from the 2nd division that we save on the stack.
To check if the qword in EDX:EAX is zero, I've OR-ed both halves in a scratch register.
Instead of counting the digits, requiring a register, I chose to put a sentinel on the stack. Because this sentinel gets a value (10) that no digit can ever have ([0,9]), it nicely allows to determine when the storage loop has to stop.
mov edi, Buffer ; Begin of the buffer
mov ebx, 10 ; CONST
push ebx ; Sentinel
.a: mov ecx, eax ; Temporarily store LowDividend in ECX
mov eax, edx ; First divide the HighDividend
xor edx, edx ; Setup for division EDX:EAX / EBX
div ebx ; -> EAX is HighQuotient, Remainder is re-used
xchg eax, ecx ; Temporarily move it to ECX restoring LowDividend
div ebx ; -> EAX is LowQuotient, Remainder EDX=[0,9]
push edx ; (1) Save remainder for now
mov edx, ecx ; Build true 64-bit quotient in EDX:EAX
or ecx, eax ; Is the true 64-bit quotient zero?
jnz .a ; No, use as next dividend
pop eax ; (1a) First pop (Is digit for sure)
.b: add eax, "0" ; Turn into character [0,9] -> ["0","9"]
stosb ; Store in buffer
pop eax ; (1b) All remaining pops
cmp eax, ebx ; Was it the sentinel?
jb .b ; Not yet
An alternative to the above that doesn't use the stack:
mov edi, Buffer+32 ; End of the buffer
mov ebx, 10 ; CONST
.c: mov ecx, eax ; Temporarily store LowDividend in ECX
mov eax, edx ; First divide the HighDividend
xor edx, edx ; Setup for division EDX:EAX / EBX
div ebx ; -> EAX is HighQuotient, Remainder is re-used
xchg eax, ecx ; Temporarily move it to ECX restoring LowDividend
div ebx ; -> EAX is LowQuotient, Remainder EDX=[0,9]
dec edi
add edx, "0" ; Turn into character [0,9] -> ["0","9"]
mov [edi], dl
mov edx, ecx ; Build true 64-bit quotient in EDX:EAX
or ecx, eax ; Is the true 64-bit quotient zero?
jnz .c ; No, use as next dividend
; EDI now points at the left-most digit of the 64-bit number.
Conversion of the signed 64-bit number held in EDX:EAX
The procedure is as follows:
First find out if the signed number is negative by testing the sign bit.
If it is, then negate the number and output a "-" character.
The rest of the snippet is the same as for an unsigned number.
mov edi, Buffer ; Begin of the buffer
test edx, edx ; Sign bit is bit 31 of high dword
jns .u ; It's a positive number
neg edx ; |
neg eax ; | Negate EDX:EAX
sbb edx, 0 ; |
mov byte [edi], "-"
inc edi
.u: mov ebx, 10 ; CONST
push ebx ; Sentinel
.a:
...
Again an alternative that doesn't use the stack:
test edx, edx ; Sign bit is bit 31 of high dword
pushf ; (***)
jns .u ; It's a positive number
neg edx ; |
neg eax ; | Negate EDX:EAX
sbb edx, 0 ; |
.u: mov edi, Buffer+32 ; End of the buffer
mov ebx, 10 ; CONST
.c:
...
popf ; (***)
jns .z ; It's a positive number
dec edi
mov byte [edi], "-"
.z:
; EDI now points at the left-most digit (or the sign) of the 64-bit number.