1

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).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Hanan
  • 1,169
  • 3
  • 23
  • 40
  • 4
    Think about how many digits will be needed to store 2^38 and how many bits have you result register. – JohnTortugo Oct 01 '12 at 20:12
  • 4
    Note that 2^38 is too big for a (32 bit) int - your printf format string will need to be %lld, not %d, and the corresponding argument will need to be 64 bits. – Paul R Oct 01 '12 at 20:12

2 Answers2

8

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:
nrz
  • 10,435
  • 4
  • 39
  • 71
  • isn't the `x1` loop should start at the `dec ecx` line ? – Hanan Oct 01 '12 at 22:23
  • 1
    No. `loop` command decrements `ecx` and if `ecx` does not become zero, it jumps to the target address, in this case `x1`. `loop x1` is practically equivalent to `dec ecx; jnz x1`. A conditional jump to `dec ecx` line would shift bits only 19 times instead of the needed 37 times. – nrz Oct 01 '12 at 22:32
2

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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • How am i suppose to call the printf with two separate registers as input ? should i put it in allocated memory first ? – Hanan Oct 01 '12 at 21:31
  • 1
    @HananN.: If the printf you're using has a format that will accept a 64-bit integer, you can use that. Otherwise, you'll probably have to do the conversion into digits yourself, then print out the string. – Jerry Coffin Oct 01 '12 at 21:50