News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Going wild with the stack

Started by Petroizki, June 04, 2005, 05:38:42 AM

Previous topic - Next topic

Petroizki

I read this stuff from the Game Programming Gems 2 -book, and it seemed kinda interesting.
It was called "stack winding", and the idea is to make a sort of a recursion in to the code, thus simplifying things (or not). The good thing is, that you have the initialization, and the release code almost in the same place.

Here is an example code. See how the "handler" pops up the return address from the stack and calls it. This "handler" makes both, the file opening and closing, but you only need to make one call from the main code to this handler.

MyProc proc C
call @FileHandler; <- Only one call needed in the main code

... ; rest of the proc, the file is now open

; <- Without the file handler, you would normally make a call here to close the file
ret ; <- this actually returns to the file handler, and closes the file

@FileHandler:
CreateFile ; <- Open the file, make a file mapping etc..

pop edx
call edx ; <- make a return to the main code

CloseHandle ; <- Close the file, close the file mapping etc..
ret ; <- makes a return from the proc itself
MyProc endp


In the book, this method is used in DirectX initialization, and it seems an ideal way on everything that has to also release the handles.

What do you think, is this a practical way, or not so good. The recursion is obviously kinda bad, this method should not be overdone, you don't want a thousand of those file handler returns in the stack! How about popping the return address from the stack and then immediatily calling it, i have a faint memory of this being a slow thing in P4?

You can't of course use stdcall calling convention, or locals, as the stack frame is ruined by the handler.

Checking for error return values, does complicate things however..

MichaelW

Hi Petroizki,

Interesting method.

Could you post an example of how it is used for DirectX initialization?

With a small change it seems to work OK with stdcall and locals.

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc

    _write_disk_file PROTO :DWORD,:DWORD,:DWORD

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
        darryl db "my other brother darryl.txt",0
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    invoke _write_disk_file,ADDR darryl,ADDR darryl,SIZEOF darryl-1

    print ustr$(eax),13,10 

    mov   eax, input(13,10,"Press enter to exit...")
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

_write_disk_file proc lpName:DWORD,lpData:DWORD,fl:DWORD

comment * -------------------------------------------
        return value is the number of bytes written
        if the procedure succeeds or zero if it fails
        ------------------------------------------- *

    LOCAL hOutput:DWORD
    LOCAL bw     :DWORD

    call  fileHandler
   
    invoke WriteFile,hOutput,lpData,fl,ADDR bw,NULL
    invoke FlushFileBuffers,hOutput

    nop   ;
    nop   ; makes this easy to find in dissassembly
    nop   ;
    retn  ; return to fileHandler, no LEAVE, no stack adjust

  fileHandler:

    invoke CreateFile,lpName,GENERIC_WRITE,NULL,NULL,
                      CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
    cmp   eax, INVALID_HANDLE_VALUE
    jne   @F
    xor   eax, eax
    ret
  @@:
    mov   hOutput, eax

    pop   edx
    call  edx

    invoke CloseHandle,hOutput
    mov   eax, bw               ; return written byte count
    ret

_write_disk_file endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start


eschew obfuscation

Petroizki

Another example, here you have two memory handlers. You don't need to worry about releasing the memory, because it is done in the end of the function. You can consentrate on more important things..  :wink

main proc
mov ecx, 16384
call @TempMemoryHandler ; allocate 16384 bytes of memory
; eax = memory pointer

...

mov ecx, 16384
call @TempMemoryHandler ; allocate another 16384 bytes of memory
; eax = memory pointer

...

ret

@TempMemoryHandler:
invoke HeapAlloc, hDefaultHeap, HEAP_NO_SERIALIZE, ecx

pop edx ; pop the return address from stack
push eax ; push the memory pointer to stack
call edx ; call the return address

push HEAP_NO_SERIALIZE
push hDefaultHeap
call HeapFree
ret
main endp

Petroizki

Hi MichaelW,

Yes that works fine, if you have multiple handlers then you just have to pay attention on which handler should release the stack frame. :8)

This is an example of how to use it in DirectX. Also notice that the returns happen in reversed order, just like DirectX releases should be done.
main proc
call WindowHandler
call CooperativeLevelHandler
call DisplayModeHandler
call SurfaceHandler

... ; the main code

ret ; this releases the closing handlers

WindowHandler:
; open the window
pop edx
call edx
; close the window
ret
CooperativeLevelHandler:
; set cooperative level
pop edx
call edx
; restore cooperative level
ret
DisplayModeHandler:
; set resolution
pop edx
call
; restore resolution
ret
SurfaceHandler:
; create surfaces
pop edx
call edx
; release surfaces
ret
main endp

Kecol

Really interesting topic. I like the TempMemoryHandler idea, but all of them are excellent. Very well men :clap:. This topic remembered to me a simple expression parser I wrote some time ago (because the recursion).