Hi.
Could someone point me to some good info on the stack, clearing stackframe etc? I am new to masm32 and would appreciate any help on the topic.
Thanks
Any stack frame should be created and destroyed by MASM without you knowing in anyway, like so:
func proc Arg1:DWORD,Arg2:DWORD
LOCAL Local1 :DWORD
LOCAL Local2 :DWORD
; MASM32 sets up your stack frame accordingly
ret
; MASM32 releases your stack frame accordingly
func endp
However, if you want to control this yourself, good luck! Here's what I know. The stack (SS:(E)SP) loads from bottom to top, not top to bottom to keep the segment size the same, back when memory was scarse. For example, a push would decrement (E)SP while a pop increments it. So, in order to set up a stack frame, first count the number of variables you'll use and do this:
; set up stack frame, 32 bit
; if 16 bit, replace EBP with BP and ESP with SP
push ebp
mov ebp,esp
sub esp,size_of_variables_and_arguments
; add esp,-(size_of_variables_and_arguments) is slightly faster
; clear stack frame
mov esp,ebp
pop ebp
ret size_of_arguments_only
; This return doesn't return a value, but instead pops off
; X number of bytes. We want that to avoid stack overflow!
Then, to access your variables and arguments, you must use correct indirect pointers:
Argument(s)
Return address (leave it alone!)
Local variable(s)
etc...
Not exactly easy, but doable. Note to decrease (E)SP, not (E)BP. If you don't modify (E)SP, then your pushes and pops will forever screw up your local variables. =)
Grrr....sorry. I meant SS: (E)SP.
Gah, sorry. Was eating lunch and didn't notice a fairly major typo. :)
Pointers:
Arguments
Return address
Original Base Pointer
first argument
second-X arguments
mars_matrix,
If you actually need the practice of seting the stack manually, the technique that Rasco has shown you will do the job but unless its a matter of needing the practice, you are a lot better off by using the assembler to do this for you.
The general model of writing prototypes and procedures with matching parameters makes you code one HELL of a lot more reliable and there is no size or performance gain by not using it.
You also have the advantage using procedures with prototypes in that you can specify the calling convention if for example you need to pass a variable number of parameters to a procedure. Where win 32 bit is normally in STDCALL, using C calling convention allows you to make the last parameter VARARG.
MASM32 version 6 has an example called "tstyle" that show what a manually code stack looks like, its not there as an example of why you should use this style, its there as an example of why you should NOT use this style.
Regards,
hutch@pbq.com.au
I most deceidly agree with hutch here, use the masm build-in stack frame handling macros. They are 100% reliable, YOU are not.
The programmer is ALWAYS the weak link in the chain.
Just one more thing to add: end all your procedures with a 'ret' This is eazy to forget, and doesn't generate any compile errors.
SomeFunct PROC param1:DWORD, parma2:DWORD
LOCAL L1:DWORD, L2:SomeStruct
{code here}
ret ; ALWAYS use 'ret'... this expands into a
; few instructions that clear the stack.
; It is acceptable to have multiple 'ret' in
; single PROC if you have mulptple exit points
; (You deceid if you don't like that coding
; convention)
SomeFunct ENDP
Actually, after
push ebp
mov ebp,esp
sub esp, LocalVarsSize
the ebp offsets should be
start of args
Old EIP (return address)
Old EBP
start of local variables
So yes, it's easier to let the assembler do it.
Yeah, I made too many typos that day. Never try to eat lunch and type at the same time, especially when you've got only got a 30 minute lunch break. :D
Thanks alot for the help. I've been sick for a few days and havn't tried it yet but I will. I ran into these 'stack' problems when doing a timer callback function.
cheers