Hi,

I have written couple of tiny win32 assembly programs using mingw and the following is one of them. I have seen some examples in MASM and FASM which call ExitProcess at the end. This particular program is working fine even without ExitProcess. Any thoughts???



.intel_syntax noprefix

.section .data
   window_title:
       .ascii "Note\0"

   window_message:
       .ascii "Goodbye, cruel world!\0"
   
.section .text
   .globl _start
   _start:
       # function prologue
       push    ebp
       mov     ebp, esp
       
       push    0
       push    offset window_title
       push    offset window_message
       push    0
       call    _MessageBoxA@16
       mov     eax, 0
       
       # function epilogue    
       mov     esp, ebp
       pop     ebp
       ret



But the second one, a simple window program, displays window properly and gets closed when clicked on X button, but still appears in the task manager process list. Here is the second program, a bit lengthy one....I tried calling ExitProcess after the message loop, but still is not working.


# after calling win32 api functions, no need to reset the stack.
# function will adjust the stack automatically

.intel_syntax noprefix
       
.globl window_class  
.section .data
   window_title:
       .ascii "Window Title\0"
   window_class:
       .ascii "myWindowClass\0"
   error_window:
       .ascii "Error\0"
   wnd_creation_errmsg:
       .ascii "Window creation failed\0"
   class_reg_errmsg:
       .ascii "Class registration failed\0"

.section .bss
   .lcomm    hwnd, 4
   .lcomm    hIcon, 4
   .lcomm    hCursor, 4
   .lcomm    msg, 4
   .lcomm    strct_wndclassex, 48
   .lcomm    err_code, 4
   .lcomm    hInstance, 4
       
.section .text
   .globl _start
   .globl _wnd_proc  
   
   _start:
       push    ebp
       mov     ebp, esp
     
       load_icon:
           push    32512
           push    0
           call    _LoadIconA@8
           mov     dword ptr hIcon, eax
       
       load_cursor:
           push    32512
           push    0
           call    _LoadCursorA@8
           mov     dword ptr hCursor, eax
           
       get_hinstance:
           push    0
           call    _GetModuleHandleA@4
           mov     dword ptr hInstance, eax
     
       fill_struct_windclassex:
           mov     edi, offset strct_wndclassex
           mov     dword ptr , 48
           mov     dword ptr , 0
           mov     dword ptr , offset _wnd_proc
           mov     dword ptr , 0
           mov     dword ptr , 0
           mov     eax, hInstance
           mov     dword ptr , eax
           mov     eax, hIcon
           mov     dword ptr , eax
           mov     eax, hCursor
           mov     dword ptr , eax
           mov     dword ptr , 6
           mov     dword ptr , 0
           mov     dword ptr , offset window_class
           mov     eax, hIcon
           mov     dword ptr , eax
       
       regsiter_window:
           push    offset strct_wndclassex
           call    _RegisterClassExA@4
           cmp     eax, 0
           jnz     create_window
       
       class_reg_failed:
           push    0x30
           push    offset error_window
           push    offset class_reg_errmsg
           push    0
           call    _MessageBoxA@16
           mov        eax, 0
           jmp     return_WinMain
       
       create_window:
           push    0
           #push    dword ptr
           push    hInstance
           push    0
           push    0
           push    120
           push    240
           push    -2147483648
           push    -2147483648
           push    13565952
           push    offset window_title
           push    offset window_class
           push    512
           call    _CreateWindowExA@48
           cmp     eax, 0
           jnz     show_window
           
       window_creation_failed:
           push    0x30
           push    offset error_window
           push    offset wnd_creation_errmsg
           push    0
           call    _MessageBoxA@16
           mov        eax, 0
           jmp     return_WinMain
       
       show_window:
           # save the return value in hwnd
           mov     dword ptr hwnd, eax
           push    1
           push    eax
           call    _ShowWindow@8
             
       update_window:
           push    dword ptr hwnd
           call    _UpdateWindow@4
             
       msg_loop:
           push    0
           push    0
           push    0
           push    offset msg
           call    _GetMessageA@16
           cmp     eax, 0
           jg      translate_msg
           
           translate_msg:
               push    offset msg
               call    _TranslateMessage@4
                     
           dispatch_msg:
               push    offset msg
               call    _DispatchMessageA@4
               jmp     msg_loop
   
       return_WinMain:
           # function epilogue    
           mov    esp, ebp
           pop    ebp
           ret
               
   # callback function to handle messages for the window
   _wnd_proc:
       push    ebp
       mov     ebp, esp
       
       cmp     dword ptr , 16
       jz      close
       cmp     dword ptr , 2
       jz      destroy
       jmp     default
       
       close:
           push    dword ptr
           call    _DestroyWindow@4
           jmp     return_WndProc
       
       destroy:
           push    0
           call    _PostQuitMessage@4
           jmp     return_WndProc
           
       default:
           push    dword ptr
           push    dword ptr
           push    dword ptr
           push    dword ptr
           call    _DefWindowProcA@16
           
       return_WndProc:
           mov     esp, ebp
           pop     ebp
           ret
 
Posted on 2009-12-17 04:51:23 by uglyhunK
If your stack is correctly balanced, your program will probably terminate just fine by doing a "ret" - but this is an undocumented Windows implementation detail, and not something you should depend on - ExitProcess is the correct way to exit.

What's with the use of labels insetad of comments, and magic numbers instead of constants? Ugly ugly.

Anyway, younever seem to exit your msssageloop... you should exit on GetMessage returning 0.
Posted on 2009-12-17 05:16:20 by f0dder
I feel, by using labels we can separate blocks of code, helps in indentation and at the same time serves the dual purpose of what that block does. Constants, yes it should be the right way. And by the way, thanks for the inputs, changing "jg    translate_msg" to "jng  return_WinMain" did the job. Damn.....been looking at it for almost 2hrs and did not even notice.....


msg_loop:
            push    0
            push    0
            push    0
            push    offset msg
            call    _GetMessageA@16
            cmp    eax, 0
            jng    return_WinMain
Posted on 2009-12-17 06:40:29 by uglyhunK
I'd like to point out that jg/jng consider SIGNED numbers, so they check zero flag AND sign/overflow flag (as opposed to ja/jb).
GetMessage() returns a BOOL, which should be interpreted as an UNSIGNED number.
You should just use jz/jnz to check for zero/nonzero values. Only on zero you should exit the loop.

So don't forget... for signed stuff, you use greater/less, for unsigned stuff you use above/below.
Here's a nice overview of the different conditional jumps:
http://unixwiz.net/techtips/x86-jumps.html
Posted on 2009-12-17 07:40:12 by Scali
I feel, by using labels we can separate blocks of code, helps in indentation and at the same time serves the dual purpose of what that block does.
If I see a label, I expect it to be used as a branch target - so it makes the program structure less clear to use labels for commenting purposes, when a comment is really what you're making.
Posted on 2009-12-17 08:57:43 by f0dder

I'd like to point out that jg/jng consider SIGNED numbers, so they check zero flag AND sign/overflow flag (as opposed to ja/jb).
GetMessage() returns a BOOL, which should be interpreted as an UNSIGNED number.
You should just use jz/jnz to check for zero/nonzero values. Only on zero you should exit the loop.


According to the API, GetMessage() returns 0 on WM_QUIT and values > 0 for other messages. But it also returns -1 on error. Ideally I should handle that exception, in which case, I should check for signed integers. Isn't it???
Posted on 2009-12-17 09:48:44 by uglyhunK

According to the API, GetMessage() returns 0 on WM_QUIT and values > 0 for other messages. But it also returns -1 on error. Ideally I should handle that exception, in which case, I should check for signed integers. Isn't it???
Yep - shame on Microsoft for using BOOL as return type when the possible values are clearly not just TRUE/FALSE.
Posted on 2009-12-17 09:52:38 by f0dder

I feel, by using labels we can separate blocks of code, helps in indentation and at the same time serves the dual purpose of what that block does.
If I see a label, I expect it to be used as a branch target - so it makes the program structure less clear to use labels for commenting purposes, when a comment is really what you're making.

Yes, I totally agree with you. But, I did not mean that labels are a replacement for comments. It certainly increases the general readability of the assembly source. Otherwise it is just strings of instructions.....may be I feel this way because my journey is from c->java->assembly.
Posted on 2009-12-17 10:03:22 by uglyhunK
MSDN says you should do this:
BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}


So special cases for 0 and -1.
Posted on 2009-12-17 10:22:31 by Scali

Yes, I totally agree with you. But, I did not mean that labels are a replacement for comments. It certainly increases the general readability of the assembly source. Otherwise it is just strings of instructions.....may be I feel this way because my journey is from c->java->assembly.
My language "journey" has been Pascal, asm16, asm32, C, C++, Python (pretty small tools), LUA (very little), asm64 (very little), Java, and I'll pick up C# in a few months... I definitely have a high-level bias, and I still don't think using labels instead of comments is the way to go :)

Programs are just string of instructions, but we can split them into procedures/functions - which is when you'd use labels :)
Posted on 2009-12-17 10:28:38 by f0dder

I feel, by using labels we can separate blocks of code, helps in indentation and at the same time serves the dual purpose of what that block does.




;=====<< sDrawBSpline >>===\
.data
BSplineShader dd 0
BSplineShadeWid dd 1
.code

sDrawBSpline proc PUBLIC UseAll pSpline:ptr SDSPLINE,numPoints,dwColor
local xa,xb,xc
local ya,yb,yc
local deltaT:real8,curT:real8
local curX,curY,nX,nY

mov ecx,pSpline
test ecx,ecx
jz _ret
assume ecx:ptr SDSPLINE

; C = 3.0 * (cp[1] - cp[0]);
mov eax,.p1.x
mov edx,.p1.y
sub eax,.p0.x
sub edx,.p0.y
lea eax,
lea edx,
mov xc,eax
mov yc,edx

; B = 3.0 * (cp[2] - cp[1]) - C;
mov eax,.p2.x
mov edx,.p2.y
sub eax,.p1.x
sub edx,.p1.y
lea eax,
lea edx,
sub eax,xc
sub edx,yc
mov xb,eax
mov yb,edx

; A = cp[3].x - cp[0].x - cx - bx;
mov eax,.p3.x
mov edx,.p3.y
sub eax,.p0.x
sub edx,.p0.y
sub eax,xc
sub edx,yc
sub eax,xb
sub edx,yb
mov xa,eax
mov ya,edx

;--------[ compute numPoints if necessary ]----------\
.if numPoints==0
;----[ compute average point of p1 & p2 ]--\
mov eax,.p2.x
mov edx,.p2.y
add eax,.p1.x
add edx,.p1.y
sar eax,1
sar edx,1
mov nX,eax ; store it temporarily in nX:nY
mov nY,eax
;------------------------------------------/

;----[ compute sqr of len p0:pN ]--\
fild .p0.x
fisub nX
fmul ST,ST
fild .p0.y
fisub nY
fmul ST,ST
fadd
;----------------------------------/
;----[ compute sqr of len p3:pN ]--\
fild .p3.x
fisub nX
fmul ST,ST
fild .p3.y
fisub nY
fmul ST,ST
fadd
;----------------------------------/
fadd
fsqrt
fistp numPoints
shr numPoints,3
add numPoints,4
.endif
;----------------------------------------------------/




fld1
fidiv numPoints
fst deltaT
fstp curT
m2m curX,.p0.x
m2m curY,.p0.y

@@:
; nX = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x;
fild .p0.x
fild xc
fmul curT
fadd
fild xb
fmul curT
fmul curT
fadd
fild xa
fmul curT
fmul curT
fmul curT
fadd
fistp nX

; nY = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y;
fild .p0.y
fild yc
fmul curT
fadd
fild yb
fmul curT
fmul curT
fadd
fild ya
fmul curT
fmul curT
fmul curT
fadd
fistp nY

.if !BSplineShader
invoke sDrawLine,curX,curY,nX,nY,dwColor
.else
invoke sDrawLineCustom,curX,curY,nX,nY,BSplineShader,BSplineShadeWid,dwColor
.endif



m2m curX,nX
m2m curY,nY
fld curT
fadd deltaT
fstp curT
dec numPoints
jnz @B



assume ecx:nothing
_ret: ret
sDrawBSpline endp

sDrawBSplineShade proc PUBLIC pSpline:ptr SDSPLINE,numPoints,dwColor,pShadeFunc,ShadeWid
m2m BSplineShader,pShadeFunc
m2m BSplineShadeWid,ShadeWid

invoke sDrawBSpline,pSpline,numPoints,dwColor

mov BSplineShader,0
mov BSplineShadeWid,1
ret
sDrawBSplineShade endp
;=======/


(view in Notepad++ or another editor that does color-hilite ) http://dl.dropbox.com/u/1969613/openglForum/layout.png
Posted on 2009-12-17 23:11:34 by Ultrano
Fine....may be the convention is not use labels unless it is a branch target. But, is there a reason to not use labels other than cosmetic reasons???
Posted on 2009-12-18 01:04:21 by uglyhunK

Fine....may be the convention is not use labels unless it is a branch target. But, is there a reason to not use labels other than cosmetic reasons???
IMHO not making the program logic less clear is a bit more than just cosmetic - but other than that, not really. Your assembler will run a slight bit slower and use a bit more memory, but it's not going to be noticeable for simple use like this.
Posted on 2009-12-18 07:21:59 by f0dder
The reason is that all those labels end up as Symbols inside the .OBJ file, which wouldn't be a problem, if the OBJ file format didn't have a limit on the number of symbols, and as long as you don't care how long it takes to build your application (commercial apps tend to get pretty big and need to be rebuilt millions of times, and its actually not hard to reach the symbol limit of your typical assembler, which was never designed for large workloads).
There are ways around this problem, but this is not the right thread to be discussing those in.
Anyway, he's right, don't do it, it's a bad habit - and generally, it won't help you debug, because the debugger doesn't know or see those symbols in the binary executable, why generate them?
I'm pretty sure this is exactly why the COMMENT directives exist, to differentiate between HUMAN and MACHINE language within a given sourcecode.
On the other hand, it CAN be beneficial to purposefully inject text strings into your binary executable for debugging purposes - ObjAsm32 does this for DEBUG builds, and can help you identify what line, in what file, caused you problems.
Posted on 2009-12-18 09:12:25 by Homer
Afaik the labels aren't going to end up in the .obj file unless you declare them as PUBLIC?
Posted on 2009-12-18 09:28:03 by f0dder
Thanks for the info. All I have been doing till now is using labels as way to increase readability of the source, not for debugging. But, the overwhelming response about the art of writing assembly is totally against liberal use of labels. Now, I'm convinced that I can't post any assembly snippets, in this forum, with labels posted all over the place and I can't stop myself coming here....so I'm changing my habit... :lol:

Posted on 2009-12-18 11:55:40 by uglyhunK
Everyone has bad or odd habits in coding. Those habits are a hindrance only in 2 related things: teamwork/sharing and speed/reliability of coding. Your code already contains enough comments (despite the unusual format), so it's already good enough for reminding or sharing;  also the chunks of code are logically separated, the label-name is not acronyms/too-brief and it's not taking long to type. Your style isn't very bad, it's just slightly odd.
The style I recommended allows me to navigate through heaps of code, as it's describing the code structure. Your style, with the appropriate/special indentation that you mentioned can already do the same. So, you only have to make labels local inside a proc, and look-out for same-named labels in that proc :) . [ p. s. ] Aside from showing whether a label is actually used.
Every company makes-up their own coding conventions afaik; and so far I've seen complete sync of styles of several coders on a given project in only two ways: 1) MSDN-sized comments on every proc;  2) horrible code with barely any hints.
So, don't sweat it ;) . Post and code in whatever way you like and find reliable across years of development.
Posted on 2009-12-18 13:16:46 by Ultrano

Thanks for the info. All I have been doing till now is using labels as way to increase readability of the source, not for debugging. But, the overwhelming response about the art of writing assembly is totally against liberal use of labels. Now, I'm convinced that I can't post any assembly snippets, in this forum, with labels posted all over the place and I can't stop myself coming here....so I'm changing my habit... :lol:
*grin*

Well, pad yourself on the back for commenting your code, even if you're doing it in a kinda unusual way right now - it's definitely better than a lot of the junk that's floating around. Just keep in mind that people will generally think "OK, there's a label, why's it there and where is it referenced?" :)
Posted on 2009-12-18 18:16:59 by f0dder

    * By default statement labels appearing in a procedure are local to that procedure.
    * By default all procedure names are public.
    * By default most other symbols are global.

Note that these rules apply to MASM 6.x only. Other assemblers and earlier versions of MASM follow different rules.


Since the labels are not inside a procedure, they are public in scope, by default ;)
Posted on 2009-12-18 19:23:50 by Homer

Since the labels are not inside a procedure, they are public in scope, by default ;)


I'm not sure if this 'public' means the same as the PUBLIC keyword... because PUBLIC makes the symbols global (ie exported symbols in the .obj file). Without the PUBLIC keyword, I don't think symbols can be seen at link-time (unlike C/C++ for example, where they are global by default, and you use the static keyword to avoid that).
Posted on 2009-12-20 06:26:46 by Scali