News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Making a loop in assembly?

Started by dcj123, July 20, 2014, 08:29:10 AM

Previous topic - Next topic

dcj123

Hello everyone,

I am new to programming and new to assembly and would like to learn basic assembly to get an idea of how memory works before moving on to C. I believe I understand what a register is and would like to start writing code but I am hitting a road block with my code. Here is my code, What I want and am expecting is for it to loop three times adding eax (5) to 6. Whats happening is it simply adds 5 to 6, getting 11 and than closes.

.386
.model flat, stdcall
option casemap :none
.CONST

.STACK 100h

.DATA
   
.CODE
_main PROC

mov ecx, 3
myloop:
    mov eax, 5
    add eax, 6
    print str$(eax),13,10;
exit
    loop myloop   
_main ENDP

END _main


If I replace print str$(eax) with print str$(ecx), I am receiving 3 which tells me that nothing in the loop is modifying the value of ecx. However, the program still does not loop. Whats wrong?

Zen

If you read the help files that come with the MASM32 project you will find:
QuoteThe Intel processors have a built in set of instructions for loops but they are very poorly optimised and run very slowly in comparison to CMP/JMP.
LOOP, LOOPE, LOOPZ, LOOPNZ, LOOPNE
I have read in numerous MASM Forum posts that these are inferior methods for controlling your loop execution.
A reliable method for controlling your loop is to declare a DWORD variable in your data section that will store the count. Then at the end of each loop add a CMP instruction, that compares that variable with, say a LOCAL variable that you will initialize to zero, and then INC (increment) at the the end of each loop. If the compare equals zero, you want to continue out of the loop, otherwise you would jump back to myloop like this: JNZ myloop.

.386
.model flat, stdcall
option casemap :none
.CONST

.STACK 100h

.DATA
Count DWORD 0
   
.CODE
_main PROC
LOCAL Num:DWORD
mov eax,0
mov Num, eax

;    mov ecx, 3
mov eax, 3
mov Count , eax

myloop:
    mov eax, 5
    add eax, 6
    print str$(eax),13,10;
    inc Num
    mov ebx, Num
    cmp ebx, Count
    jnz  myloop
;    exit
;    loop myloop   
_main ENDP

END _main


That code won't compile as it is, of course, because there are no includes.  :biggrin:


dedndave

you have mixed a few 16-bit code lines with 32-bit code lines
mainly, .STACK is not required for 32-bit code
but also, we generally assume at least a 486 processor

the big issue with your code, however, is the preservation of certain registers across calls
the "print" macro actually calls a windows API function
in which case, EAX, ECX, EDX are volitile - EBP, EBX, ESI, and EDI are preserved

the LOOP instruction is ok, in some circumstances
it's not the fastest way to loop, but you don't always need the fastest way, either
looping 3 times, i doubt you'd notice a few nanoseconds   :P

at any rate, you want to preserve EAX and ECX across the "print" call
    push    eax
    push    ecx
    print.....
    pop     ecx
    pop     eax
    loop.....

dedndave

see if this works...

    INCLUDE \masm32\include\masm32rt.inc

.CODE
_main PROC

    mov ecx, 3
    mov eax, 5

myloop:
    add eax, 6
    push    eax
    push    ecx
    print str$(eax),13,10
    pop     ecx
    pop     eax
    loop myloop

    inkey
    exit

_main ENDP

END _main

Gunther

Hi dcj123,

and welcome to the forum.

Gunther
You have to know the facts before you can distort them.

K_F

To further extend what Zen says:

Quote
If counting up:

cmp      counter, 'limit'
jae       exit_Loop
OR
jb         loop_again

If counting down:

cmp      counter, 'limit'
jle       exit_Loop
OR
ja         loop_again
This will guarantee an exit loop, in case the single limit value is missed or 'corrupted beyond the limit
;)'
'Sire, Sire!... the peasants are Revolting !!!'
'Yes, they are.. aren't they....'

jj2007

Hi dcj,

Welcome to the Forum :icon14:

Dave's code works fine, of course, but there are several ways to code a loop. Here is one using non-volatile registers (see The "register gets trashed" trap) and the .Repeat ... .Until high level syntax:

include \masm32\include\masm32rt.inc

.code
start:
   mov esi, 3   ; counter for three iterations
   mov edi, 5   ; start value
   .Repeat
      print str$(edi), " "
      add edi, 5
      dec esi   ; decrement the counter
   .Until Zero?   ; check for zero flag
   exit
end start

dcj123

Thanks Zen, dedndave, Gunther, K_F and jj2007; I appreciate the help.

I think I understand what was happening in the loop before now, is it fair to say that the print line was corrupting the value of ecx? And that like dedndave said, push and pop preserves the value across some functions by writing that data to a stack? Also what is a better way to print out a character or register that would not corrupt some values?

dedndave

that is correct - EAX, ECX, and EDX contents are likely lost in most win32 function calls

you could write your own routine to preserve those registers
but, it's far better to become accustomed to win32 standards
just know that EAX, ECX, EDX may be destroyed - and the others are preserved

as Jochen showed you, you can use non-volitile registers, like EBX, ESI, EDI
you don't have to preserve them because you are in _main
if you were writing a subroutine, you might want to preserve them, per the standard

dedndave

as a little exercise, let's write the loop in subroutines and compare the 2 methods

LoopFunc PROC

    mov     ecx, 3
    mov     eax, 5

myloop:
    add     eax, 6
    push    eax
    push    ecx
    print   str$(eax),13,10
    pop     ecx
    pop     eax
    loop    myloop

    ret

LoopFunc ENDP


LoopFunc PROC

    push    edi
    push    esi
    mov     edi, 3
    mov     esi, 5

myloop:
    add     esi, 6
    print   str$(esi),13,10
    dec     edi
    jnz     myloop

    pop     esi
    pop     edi
    ret

LoopFunc ENDP


as you can see, both subroutines follow the windows ABI rules:
EBP, EBX, ESI, and EDI are preserved

the main difference is that, in the second example, we PUSH and POP ESI and EDI only one time
in the first example, we have to PUSH and POP EAX and ECX on each loop pass

of course, because we are not using ECX as the counter, we cannot use LOOP
that's generally an acceptable trade-off, as LOOP is a slow instruction, anyways