0

I am using MASM and currently implementing a loop displaying Capital alphabets, small alphabets and ASCII in between

.386

.model flat, stdcall
option casemap :none

include c:\masm32\include\windows.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\masm32.inc
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\masm32.lib 

.data
    _ret sdword 13
    _car sdword 10
    _num sdword 0


.code
main:
    mov edx, 'A'
    mov ecx, 58
    jmp _end

_loop:  
    push edx
    invoke StdOut, esp
    invoke StdOut, addr _ret
    invoke StdOut, addr _car
    pop edx

    inc edx
    dec ecx

_end:
    cmp ecx, 0
    jne _loop

_exit:    
    invoke ExitProcess, 0
end main

The problem here I am facing is that ECX is not decreasing during the body of the loop and the loop continue forever even after A-Z %somehere% a-z

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Mian Bilawal
  • 61
  • 10
  • 2
    With stdcall calling convention the registers EAX, ECX, and EDX are volatile (they can be changed by functions you call). I suspect `StdOut` clobbered your loop counter. Easiest solution is to use a register other than EAX, ECX and EDX for the loop. – Michael Petch Dec 07 '17 at 17:45
  • So, I should swap with a global variable? EDIT: thanks, It now works – Mian Bilawal Dec 07 '17 at 17:46
  • 1
    (a) attach debugger, look at ECX value before and after the invokes. (b) use loop instruction – Sten Petrov Dec 07 '17 at 20:12
  • 1
    In this case you even don't need a separate loop counter; use `cmp edx, 'A'+58`. You're already saving/restoring it across the function calls that could clobber it. You could be using `ebx` and `inc ebx` / `mov [esp], ebx` (with space on the stack for that to not overwrite anything), so you wouldn't need to pop it inside the loop. – Peter Cordes Dec 07 '17 at 20:50

1 Answers1

2

Your issue is one of calling convention. You've set the default convention to .model flat, stdcall which is not an issue. This means that unless otherwise specified stdcall calling convention will be used by the invoke directive. The stdcall calling convention includes these rules:

  1. The volatile registers are: EAX, ECX, EDX, and ST0 - ST7
  2. The non-volatile registers are: EBX, EBP, ESP, EDI, ESI, CS, DS, ES, FS and GS

This means that you can't assume that the values in EAX, ECX, and EDX will remain the same after a call/invoke. Your loop variable is ECX so it is likely the calls to StdOut clobbered ECX each time they were called leading to the infinite loop.

The best choice is to use one of the non-volatile registers EBX, EDI, ESI (you cause other issues if you use EBP and ESP). If you run out of registers you can:

  • Save and restore the value of ECX before and after the calls to StdOut the same way that EDX was preserved in your code using PUSH/POP.
  • Store them in a global or stack based memory location and restore them after.
  • Write the code to avoid using an extra register for the loop.
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Even better option: use `cmp edx, 'A'+58` / `jne` as the loop bound; no separate counter required. – Peter Cordes Dec 07 '17 at 20:34
  • @PeterCordes : Unlike you I won't muddy the waters with something specific like that since it really pollutes the answer. Should point out _EDX_ is a bad choice to begin with since you have to save and restore. So I wouldn't actually use your solution as I wouldn't even use _EDX_ in this code. But this isn't an optimization related question so I see no sense in making a response unreadable for the average reader. If you wish to put up what you think is a better answer, be my guest. – Michael Petch Dec 07 '17 at 20:40
  • 1
    The OP is pushing, and then passing `esp` as the pointer to the character. Yes, it would be slightly more efficient to use `ebx` or something and just store to `[esp]` instead of reading the old value back. – Peter Cordes Dec 07 '17 at 20:44
  • Anyway, I get your point, but it seems to be a common mistake to waste a 2nd register instead of using another loop variable as the exit condition. I don't think it needs a separate answer, I just wanted to add a footnote to yours. I can make it a comment on the question and we can delete this clutter of comments. – Peter Cordes Dec 07 '17 at 20:45