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?
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:
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.....
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
Hi dcj123,
and welcome to the forum.
Gunther
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
;)'
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 (http://www.webalice.it/jj2006/Masm32_Tips_Tricks_and_Traps.htm)) 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
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?
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
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