When multiplying 32-bit numbers in assembly, the result will be put in EDX:EAX combination. The upper half of the result goes into EDX and the lower half goes into EAX. If both EDX and EAX have two parts of a result, how can I print these values to the screen using the Irvine32 bit library? See the sample code and comments:
.386
.model flat, stdcall
.stack 4096
ExitProcess proto, dwExitCode:dword
include Irvine32.inc
.data
num1 dword 1000000
num2 dword 1000000
temp dword ?
full_result qword ?
.code
main proc
mov eax, num1
mul num2 ;Result will be put in EDX:EAX (Upper half of number and Lower half of number)
;EDX has the value 232 in decimal. 000000E8 in hex
;EAX has the value 3567587328 in decimal. D4A51000 in hex
;When you put these numbers togather, you get 000000E8D4A51000 in hex.
;When you convert these numbers back to its decimal representation, we get the correct value of 1000000000000
;How to display the result into the screen using Irvine32 library (not 64)
mov temp, eax
mov eax, edx ;Put the upper half of result in eax
call WriteDec ;Write the value in eax
mov eax, temp ;Put the lower half of result in eax
call WriteDec
;This will prints out 2323567587328 instead of 1000000000000
invoke ExitProcess, 0
main endp
end main
Is there a way to convert this number 2323567587328 in a different form so that I can display the upper half and lower half correctly? (packed BCD, etc...)
If it is not possible to format this number in a way so that I can have 1000000000000 in two different registers, please let me know how can I assign this value to the full_result
qword type variable.
mov eax, num1 mul num2
This mul
instruction produces an unsigned 64-bit product in EDX:EAX.
What follows is a code that converts the unsigned 64-bit number held in EDX:EAX into its decimal representation. A string that you can then output using Irvine's WriteString
function.
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.
.data
num1 dword 1000000
num2 dword 1000000
Buffer byte 32 dup(0)
.code
main proc
mov eax, num1
mul num2
push ebx
push edi
mov edi, OFFSET 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
mov BYTE PTR [edi], 0 ; Irvine32 requires zero-termination
pop edi
pop ebx
mov edx, OFFSET Buffer
call WriteString
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, OFFSET 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 PTR [edi], "-"
inc edi
.u: mov ebx, 10 ; CONST
push ebx ; Sentinel
.a:
...
The above code snippets are based on my 16-bit Q/A Displaying numbers with DOS. You could read that too for some additional explanations...
This version is shorter and faster.
.data
num1 dword 1000000
num2 dword 1000000
Buffer byte 32 dup(0)
.code
main proc
mov eax, num1
mul num2
push ebx
push edi
mov edi, OFFSET Buffer+31 ; End of the buffer
mov BYTE PTR [edi], 0 ; Irvine32 requires zero-termination
mov ebx, 10 ; CONST
.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]
dec edi
add edx, "0" ; Turn into character [0,9] -> ["0","9"]
mov [edi], dl ; Store in buffer
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
mov edx, edi ; -> EDX is address of ASCIIZ string
pop edi
pop ebx
call WriteString