No problem, Gerhard! Always good to have more information, and/or a different approach!
Besides Pascal, the Windows APIs use the "callee cleans up" convention...
push 0
push caption
push message
push 0
call MessageBoxA
; no need to remove parameters from stack
The reason we don't need to remove the parameters is that the code for MessageBoxA, although we don't see it, ends like this...
...
ret 16 ; 4 parameters of 4 bytes each
Although they share the name "ret", this is a completely different opcode than "ret" with no parameter. This one, besides popping the return address, "removes" 4 parameters from the stack - just moves esp up by 16 bytes, the parameters don't really go anywhere...
The reason you can "ret" from "main" (if it's a "real C-style main" - you can call anything "main" in assembly language) is that "main" is "call"ed by some code in what I call "C startup code" (the real name used to be "crt0.o" - I think they call it something else now). This code, amongst other things, rearranges the stack slightly and calls main. After main returns, this should take care of the actual "back to the shell" interrupt. This code gets linked with your gem unless you explicitly ask for "--nostartfiles" or some such. So IF you haven't butchered the stack, and IF you've preserved the non-volatile registers (ebp, ebx, esi, edi) (this may not matter these days, but it's the way I learned it), you can "ret" from main...
The Linux "_start" label is not "call"ed, so we can't "ret" from it. We have to do the int 80h/1 or whatever - the "destroy this process and return to the shell" interrupt... or I suppose we could call "exit()" to do it for us... I'm not sure whether there's an equivalent "not-called" label for Windows or not...
Best,
Frank