as a starter in ASM programming i am need to get the result of 2 to the power of 38 in Assembly , and i need your help in understanding why is my program doesn't produce the result i am need (it prints 4 decimal):
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\msvcrt.inc
includelib \masm32\lib\msvcrt.lib
.data
formatstr db "%d",0
.code
start:
mov eax , 2
mov ecx , 38
mov esi , eax
mov edx , 0
.while TRUE
mul esi
mov esi, edx
add esi, eax
mov edx, 0
mov eax, 2
dec ecx
.break .if (!ecx)
.endw
invoke crt__cprintf, addr formatstr, esi
end start
as you can see i am writing it using masm32 (if that has any matter in that case).
THX.
2^38
obviously does not fit in one 32-bit register such as eax
.
To store the value 2^38
(274877906944
) you need 39 bits. In 32-bit code you can use eg. two 32-bit registers such as edx:eax
. However, in 32-bit code mul
only accepts 32-bit factors (eg. registers, other of them is always eax
), so using 32-bit mul
in a loop won't work, because you cannot store your intermediate results in a 32-bit register to be multiplied again, even if mul
stores the 64-bit result in edx:eax
.
But you can use rcl
to compute eg. 2^38
in 32-bit code:
xor edx,edx
mov eax,2 ; now you have 2 in edx:eax
mov ecx,38 ; 2^n, in this case 2^38 (any value x, 1 <= x <= 63, is valid).
x1: dec ecx ; decrease ecx by 1
jz ready ; if it's 2^1, we are ready.
shl eax,1 ; shift eax left through carry flag (CF) (overflow makes edx:eax zero)
rcl edx,1 ; rotate edx through carry flag (CF) left
jmp x1
ready: ; edx:eax contains now 2^38.
Edit: a non-loop implementation inspired by @Jagged O'Neill's answer. This one is without jumps for exponent >= 32, one jump for exponent < 32, works also for ecx
0, for ecx
greater than 63 sets edx:eax
to 0
.
mov ecx,38 ; input (exponent) in ecx. 2^n, in this case 2^38.
; (any value x, 0 <= x <= 63, is valid).
; the code begins here.
xor eax,eax
xor edx,edx ; edx:eax is now prepared.
cmp cl,64 ; if (cl >= 64),
setb al ; then set eax to 0, else set eax to 1.
jae ready ; this is to handle cl >= 64.
; now we have 0 <= cl <= 63
sub ecx,1
setnc al ; if (count == 0) then eax = 0, else eax = 1.
lea eax,[eax+1] ; eax = eax + 1. does not modify any flags.
jna ready ; 2^0 is 1, 2^1 = 2, those are ready now.
mov ebx,ecx ; copy ecx to ebx
cmp cl,32 ; if (cl >= 32)
jb low_5_bits
mov cl,31 ; then shift first 31 bits to the left.
shld edx,eax,cl
shl eax,cl ; now shifted 31 bits to the left.
lea ecx,[ebx-31] ; cl = bl - 31
low_5_bits:
shld edx,eax,cl
shl eax,cl
ready:
When you do a multiplication on an x86, edx
will hold the top 32 bits of the result, and eax
will hold the bottom 32 bits. When you do:
mul esi
mov esi, edx
add esi, eax
The result is only going to be meaningful in the case that edx
was 0 so the mov
/add
is basically doing mov esi, eax
. If the top 32 bits are non-zero, you're going to end up with a fairly meaningless mish-mash of the upper and lower bits.