The MASM Forum Archive 2004 to 2012

General Forums => The Laboratory => Topic started by: ThoughtCriminal on January 30, 2006, 09:43:01 AM

Title: Working WndProc with no stack frame.
Post by: ThoughtCriminal on January 30, 2006, 09:43:01 AM
I always heard a callback always needs a stack frame.  I have a working window where the WndProc has no stack frame.  Perhaps anther set of eyes will fine something.

Note: I using an unusual style of programming.  No stack frames anywhere.  ebp+fuction offset is used to call functions.   Because the initial system call to the WndProc seem to trash all the regiters, I need to restore ebp to call the APIs in WndProc.

My WndProc:

WndProc proc
mov ebp,offset FUNC0  
.if esp==2   ;Need to fix-- uMsg = esp+12 but '.if esp-12==2' is not allowed

push 0
call dword ptr[ebp]+PostQuitMessage
.else

push [esp+16]
push [esp+16]
push [esp+16]
push [esp+16]

call dword ptr[ebp]+DefWindowProc

ret 10h
.endif
xor eax,eax
ret 10h

Iczelion tut 3:

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_DESTROY
invoke PostQuitMessage, NULL
.else
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.endif
xor eax,eax
ret
WndProc endp



Mostly looking for opnions on the safety om my code.

Thanks.
Title: Re: Working WndProc with no stack frame.
Post by: zooba on January 30, 2006, 10:32:46 AM
Frankly, if you're looking for safety, I don't think you'll find it without a stack frame. Any stack corruption has the potential to crash your program in completely random places (ie, miss one pop, suddenly you've ret'd to the middle of nowhere).

By the way, you can use:

.if DWORD PTR [esp-12] == 2
or
.if esp == (2+12)
depending on exactly what you're trying to test (ie. the value of esp or the value stored on the stack) :U
Title: Re: Working WndProc with no stack frame.
Post by: Tedd on January 30, 2006, 11:17:54 AM
The stack-frame is still created for you by MASM when you use PROC - whether you use it or not :bdg

Before the function, add the following lines to remove the stack-frame
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE


And after it, to turn it back on again (for other functions, if you want to?)
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef



On other news, I don't see why a callback should/shouldn't require a stack-frame. It's simply a function - called from elsewhere, some number of times, but still simply a function. It doesn't get paused halfway through, there's nothing special about it, it can't preserve any local variables, so the use of a stack-frame is irrelevant.
Any 'state' you want to maintain must be done with global variables in any case - with or without a stackframe :P
Title: Re: Working WndProc with no stack frame.
Post by: lingo on January 30, 2006, 02:46:37 PM
We can use the simpler form too... :lol
MsgProc:
       
    mov eax, [esp+2*4]

    .if eax == WM_CLOSE
jmp     OnClose

    .elseif eax == WM_KEYDOWN
call OnKeydown

    .elseif eax == WM_DESTROY
           jmp ExitProcess
       
    .elseif eax == WM_PAINT
            call OnPaint

    .elseif eax == WM_SIZE
call     OnSize

    .endif
   
jmp DefWindowProc


Regards,
Lingo
Title: Re: Working WndProc with no stack frame.
Post by: ThoughtCriminal on January 30, 2006, 04:24:48 PM
I turned the stack frames off Tedd.  However it is always good to show people how to do it.

zooba-

I will most likely put it in, just did it to see if I could.  Thanks for the example, I dont usually use the high level directives.

lingo-

Thanks for the code.
Title: Re: Working WndProc with no stack frame.
Post by: Ratch on January 30, 2006, 06:15:25 PM
ThoughtCriminal,

Quote
I always heard a callback always needs a stack frame.

     Someone told you wrong.

Quote
Note: I using an unusual style of programming.  No stack frames anywhere.

     I never use stackframes or PROC's.  For stack frames, you have to sacrifice the EBP register in a already register starved CPU.  If you are usinging it to reference variables, you cannot use it for computations without going through a save/restore cycle each time you have need for an extra register.

     As for PROC's, they are an extra software layer that gets between the programmer and the problem.  Do a search on this thread and not how many problems occur, and bandwidth is expended about this directive.   I use the STRUCT directive to keep track of what is on the stack during function calls.  This allows me complete flexibility when calling functions.  For instance, I can PUSH variables many instructions before the function is called.  One would do that if the parameter value is available at the time, and would get lost by the time when the function is called.  This eliminates having to save the parameter value somewhere.  Of course I have to compensate for each PUSH, but after while it becomes second nature to do so.

     As for the WinProc, I search a table of message numbers and correlate it with a jump table to the message handler.  This routine also saves the "sacred four" Windows registers for restoration later, so that these registers are immediately available for the message handlers to use.

     I have published examples of the above methods in  past posts.  If you cannot find them, and are really interested, let me know.  Otherwise, forget what I said.  Ratch
Title: Re: Working WndProc with no stack frame.
Post by: OS on January 31, 2006, 02:11:40 AM
Right I don't used Stack frames either but for those of you who want to try this remember ....
(GENERAL INFO slightly off the WNDPROC topic but important for people who just want to use calls without frames)
Since when you call a function this happens
CALL XXXX =PUSH $ ->JMP XXXX
so @ [ESP] is the return address and any pushs you made are now at [ESP+4]
When the frame is created you (PUSH EBP) (store the register value)
AND then clone the stack pointer (MOV EBP,ESP) NOW When you need to preserve
registers without a stackframe , you have to push ,and that will move your data .

_simplefunc: ;(vara,varb)
PUSH EDX,ECX
MOV EDX,[ESP+12] ;ESP+4(+2 pushs)VARA
MOV ECX,[ESP+16] ;ESP+8(+2 pushs)VARB
POP ECX,EDX
....YOU can either RET XX (bytes pushed to return the stack frame)
....ADJUST the stack (ADD ESP,8)and jump to the return address with a register or global varible
....jmp [ESP] and adjust stack as a C convention type call


I don't allways create functions in the above way but in my library there are no frames and all registers
used get restored ,it's just nice to make a call with a varible in ECX and still have it be there when your call
returns.
Title: Re: Working WndProc with no stack frame.
Post by: Ratch on January 31, 2006, 04:36:16 AM
OS,
     I have stackless frames down to a system, because I use the STRUC directive to make a map of what the stack will look like after the function is called, registers saved, and local storage obtained.  Then I reference everything with respect to ESP and compensate for any PUSH'es and POP's within the subroutine.  When it comes time to return, I backtrack using the  STRUCT to restore the stack to what it was before the subroutine call.  No red tape with PROC's for me.   Ratch
Title: Re: Working WndProc with no stack frame.
Post by: hutch-- on January 31, 2006, 06:50:56 AM
I find some humour in these discussions.


55                     push    ebp
89E5                   mov     ebp,esp


C9                     leave
C21000                 ret     10h


You can barely notice a stack frame in a tiny intensive procedure. With something as slow as a WndProc superman could not measure the difference.
Title: Re: Working WndProc with no stack frame.
Post by: ThoughtCriminal on January 31, 2006, 08:34:20 AM
Ratch-- I'm interested how you use STRUCTS to mange the stack.

hutch--

Just did it to see if it could be done.
Title: Re: Working WndProc with no stack frame.
Post by: Ratch on January 31, 2006, 04:43:41 PM
ThoughtCriminal,
     OK, look at the zipped file below.  It is not important to know what the subroutine is doing.  Observe how the STRUCT is defined according to the subroutine.  At the beginning, note how the registers are saved and local storage obtained.  Notice also how the local variables and the PUSH'ed variables are refenced from the stack, and how the stack references needs to be compensated.  At the end, see how the stack is cleaned up.  In summation the STRUCT helps you to reference important data with reference to the ESP register instead of the EBP register.  I always set the EBP register to the useful constant value of zero unless I need it for something else.  No wrestling with PROC's with this method.   Any questions?  Ratch

[attachment deleted by admin]
Title: Re: Working WndProc with no stack frame.
Post by: Ratch on January 31, 2006, 05:25:59 PM
hutch--,

Quote
You can barely notice a stack frame in a tiny intensive procedure. With something as slow as a WndProc superman could not measure the difference.

     We both know of a member who, not that long ago, argued that it makes hardly any difference as far as human preception is concerned, between a program coded with a high level language or assembler.  Especially if the HLL is done with a good optimizing compilier.  I guess it all boils down to the goals of reaching the fastest speed, shortest code and being a complete control freak.  Ratch
Title: Re: Working WndProc with no stack frame.
Post by: Petroizki on January 31, 2006, 05:53:13 PM
I haven't used stack frames in a long time. I created my own function macros (pmacros (http://www.masmforum.com/simple/index.php?topic=1063.0)), which easily give me the ability to make procedures, which free up the ebp-register for more important things. I see no reason to use ebp-register to refer parameters or locals, if i can in most cases, just as easily do it through esp.

This is a procedure with pmacros, which uses esp-register to refer parameters:
func WndProc, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg == WM_DESTROY
fncall PostQuitMessage, NULL
.ELSE
jmp DefWindowProc
.ENDIF

xor eax, eax
ret
endf WndProc
Title: Re: Working WndProc with no stack frame.
Post by: Gustav on January 31, 2006, 09:03:18 PM
> The stack-frame is still created for you by MASM when you use PROC - whether you use it or not

No. It it created only (by MASM) if the proc has parameters or local variables defined. No need to use the prologue/epilogue macros.

Title: Re: Working WndProc with no stack frame.
Post by: EduardoS on February 01, 2006, 12:00:18 AM
using ESP as base register you need 1 byte more for each time you need a local variable or parameters, 5 of them takes more space then the stack frame,
creating a stack frame may be a good ideia when you don't need 7 registers,

Now another question, some routines save the esp in a global variable and use all 8 registers to the proc, what happen if windows interrupt the execution and esp have a random value?
Title: Re: Working WndProc with no stack frame.
Post by: Ratch on February 01, 2006, 12:56:31 AM
EduardoS,
Quote
using ESP as base register you need 1 byte more for each time you need a local variable or parameters, 5 of them takes more space then the stack frame,
creating a stack frame may be a good ideia when you don't need 7 registers,

     You are correct about ESP references needing one more byte, but no matter how you cut it, you are tying up an often needed extra register if you use it for a stackframe.

Quote
Now another question, some routines save the esp in a global variable and use all 8 registers to the proc, what happen if windows interrupt the execution and esp have a random value?

     What is the purpose of the routines doing that?  A Windows OS interrupt is supposed to restore the environment, including the previous value of ESP to the user.  Ratch
Title: Re: Working WndProc with no stack frame.
Post by: EduardoS on February 01, 2006, 09:37:06 AM
Ratch, that rotine is a SHA1 algo, and need 8 registers
windows really restore the evirenmont, but to do that it dont need the stack?
Title: Re: Working WndProc with no stack frame.
Post by: hutch-- on February 01, 2006, 09:49:16 AM
EduardoS,

If you are using a late enough processor, there is a trick where you use MOVD to preserve as many of the normal 32 bit registers as you like in the XMM registers. Sure solves the problem if you need 8 registers.
Title: Re: Working WndProc with no stack frame.
Post by: Gustav on February 01, 2006, 12:16:55 PM
> Now another question, some routines save the esp in a global variable and use all 8 registers to the proc, what
> happen if windows interrupt the execution and esp have a random value?

that should be no problem because the interrupt will also cause a switch to ring 0, which has it's own ESP, so the ring 3 ESP isn't used at all to save anything.
Title: Re: Working WndProc with no stack frame.
Post by: lingo on February 01, 2006, 03:34:10 PM
 :lol
“…there is a trick where you use MOVD to preserve as many of the normal 32 bit registers”


25112 Rev. 3.06 Software Optimization Guide for AMD64 Processors September 2005

9.6 Avoid Moving Data Directly Between General-Purpose and MMX™ Registers

Optimization
Avoid moving data directly between general-purpose registers and MMX™ registers;
this operation requires the use of the MOVD instruction. If it is absolutely necessary to move data
between these two types of registers, use separate store and load instructions to move the data
from the source register to a temporary location in memory and then from memory into the
destination register, separating the store and the load by at least 10 instructions.

Application
This optimization applies to:
• 32-bit software
• 64-bit software

Rationale
The register-to-register forms of the MOVD instruction are either VectorPath or DirectPath Double
instructions. When compared with DirectPath Single instructions, VectorPath and DirectPath Double
instructions have comparatively longer execution latencies. In addition, VectorPath instructions prevent
the processor from simultaneously decoding other insructions.

Example
Avoid code like this, which copies a value directly from an MMX register to a general-purpose register:
movd eax, mm2
If it is absolutely necessary to copy a value from an MMX register to a general-purpose register (or
vice versa), use separate store and load instructions, separating them by at least 10 instructions:

movd DWORD PTR temp, mm2 ; Store the value in memory.
...
; At least 10 other instructions appear here.
...
mov eax, DWORD PTR temp ; Load the value from memory.


Regards,
Lingo
Title: Re: Working WndProc with no stack frame.
Post by: hutch-- on February 05, 2006, 11:31:59 AM
Lingo,

This makes sense but they forgot to tell Intel who created the XMM registers.
Title: Re: Working WndProc with no stack frame.
Post by: zcoder on March 09, 2006, 05:34:08 AM
No lets tell intel to make an instruction
thats swaps ALL registers
in other word there is really 2 FULL sets
of the same registers with same names
like a mem bank you swap to the other set

Z80 had this build in to it.


Zcoder....