Search code examples
assemblyx86masm32

x86 Assembly Newbie: Basic adding and storing numbers issue


Hello I am new to assembly and I'm struggling to get a two-part program to work. I am using Visual Studio for this x86 assembly.

Part I) My first goal is to count up to 13, adding each value on the way there. Ex, 0+1+2+3...+13=91. I want to store that total in totall.

Part 2) Secondly, I want to count up by the powers of 2 from 2^0 to 2^6. Ex, 0,1,2,4,8,32,64. I think* I am doing that but I am not storing each value as I go. I want to store these in consecutive memory locations.

I have this so far,

.586
.MODEL FLAT

.STACK 4096

.DATA
num1 BYTE 13          ;Initialize number to count to
totall BYTE 0         ;Total of all counted numbers
temp BYTE 0           ;Temp for loop adding

shiftme BYTE 1        ;Start of counting 2^0 so I can reach 2^6

.CODE
main PROC
;code here

increment:            ;Increment label
inc temp              ;Increment temp by 1
mov eax, temp
add totall, eax       ;Add temp+totall and store in totall
cmp eax, num1         ;Compare for jump
jne increment         ;Jump if not equal

;this part should store each value 1,2,4,8,32.. in consecutive memory locat
shiftallthethings:    ;Shift label
shl shiftme, 1        ;Shifting bits to the left one
cmp shiftme, 64       ;Comparing for the jump
jne shiftallthethings ;Jump if not equal to

ret
main ENDP
END

Questions to help me understand.

  • How does one store values in consecutive memory locations?
  • Am I using the jump and label instructions correctly?
  • Do I need to use specific registers like eax to perform these problems? Why?

Any help is GREATLY appreciated, thanks.


Solution

  • First of all, in answer to your questions:

    How does one store values in consecutive memory locations?

    In MASM32, you can either directly do a mov like mov sum_addr, eax (as long as the data types have the same size) or you can pass around data pointers. In the example I wrote, a pointer to total is passed to the function. The function then writes a value to the memory pointed to by that pointer (i.e. contents of total). Not quite sure what you mean by consecutive memory locations. If you mean pointer arithmetic, I can show you an example of that too.

    Am I using the jump and label instructions correctly?

    Yes, that seems to be fine. An alternative which I have use is anonymous labels. This is appropriate when the label is trivial and is fairly close by. It's personal preference. If you feel a label name is more appropriate, feel free to use that too.

    Do I need to use specific registers like eax to perform these problems? Why?

    MASM32 follows the call convention of Win32 (stdcall), so it's good for you to do that too. In terms of register preservation, it means that all functions are expected to preserve registers except for eax, ecx and edx which are deemed trashable. Return values are stored in eax or eax and edx if more than 4 bytes is required.


    In terms of the code you have written, you have a few problems such as attempting to move different size data types into each other. For example, if you move a byte into a dword, you must extend it to be of the same size first.

    mov eax, temp
    

    That will not compile because temp is only 1 byte long, whereas eax is 4 bytes. You could do instead:

    movzx eax, temp
    

    This zero-extends temp before doing the move. Here is some code I've thrown together which might teach you a few things. It uses macros (not sure if you want to learn those yet), but otherwise demonstrates idiomatic MASM32 parameter passing and return values.

    include \masm32\include\masm32rt.inc
    
    .DATA
    
    total DWORD 0         ;Total of all counted numbers
    
    .CODE
    
    ; takes in address of total and writes the value there
    sum PROC sum_addr:DWORD
    
    xor eax, eax
    xor ecx, ecx
    
      @@:
    inc ecx
    add eax, ecx
    cmp ecx, 13
    jnz @b
    
    mov edx, sum_addr
    mov dword ptr ds:[edx], eax
    print ustr$(eax), 13, 10
    
    mov edx, sum_addr
    mov eax, dword ptr ds:[edx]
    ret
    
    sum ENDP
    
    start:
    
    push offset total          ; pass address of total
    call sum
    print ustr$(eax), 13, 10   ; demonstrating how to use return values
    
    print ustr$(total), 13, 10 ; testing result got stored properly
    ret
    
    end start
    END
    

    The code is not optimised but should be easily understandable. Notice how I have tried to use registers as much as possible (this is more efficient than constantly dealing with memory if we have enough registers).