This code
.686
.model flat,stdcall
option casemap:none

include \masm32\include\masm32rt.inc

.data
blah db 0

.code

start:
mov ecx, 0
.while ecx < 10
print chr$("Hello world",13,10)
inc ecx
.endw
invoke ExitProcess,0

end start


will print "Hello world!" only once. But changing it into:
.686
.model flat,stdcall
option casemap:none

include \masm32\include\masm32rt.inc

.data
blah db 0

.code

start:
.while blah < 10
print chr$("Hello world",13,10)
inc blah
.endw
invoke ExitProcess,0

end start


will give the correct result. So I guess the ECX isn't incremented during the loop.
I tought ECX was for counting? Or maybe I don't understand it properly?
Posted on 2010-07-31 01:08:11 by anta40
Try adding "push ecx" before "print" and "pop ecx" before "inc ecx". If that fixes the problem then print most likely to be trashing some registers (probably the same that are NOT guaranteed to be preserved in the stdcall convention).
Posted on 2010-07-31 01:27:18 by LocoDelAssembly
To expand on LocoDelAssemblys answer, you have a Calling Convention issue. When interfacing with external code, you have to follow the calling convention, which specifies the order arguments are pushed to the stack, who does stack cleanup, and register preservation rules. MASM's INVOKE macro takes care of the argument pushing, but register preservation is up to you.

The standard rules are as following: when calling external code, you should always assume that EAX, ECX and EDX are trashed by the routine. If you have data in any of these registers that you want to use after calling an external routine, you need to manually preserve that data (whether by push/pop or saving elsewhere). Even if empirical testing shows that one of these registers isn't trashed on your particular windows installation, don't make assumptions - things will break sometime in the future.

The EBX, ESI and EDI registers, on the other hand, are guaranteed to be preserved by the code you're calling... so instead of using ECX for the counter and doing push/pop, you could use one of these registers and avoid doing the push/pop jiggle.

Note that you should also follow the standard calling convention rules when writing your own routines. It's not strictly necessary for routines used only internally in your app, but you save yourself some headaches if you stick by it. And for things like callback routines (for instance the lpEnumFunc for EnumWindows()) you have to follow the rules to avoid your app crashing.
Posted on 2010-07-31 02:20:32 by f0dder