The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: Bieb on January 12, 2005, 01:39:58 PM

Title: Procedures
Post by: Bieb on January 12, 2005, 01:39:58 PM
How exactly do I make a procedure that accepts arguments?  Do I have to have it take them off the stack by itself, or can the process be automated with MASM?  If I have to do it myself, how do I have to balance the stack frame?
Title: Re: Procedures
Post by: donkey on January 12, 2005, 01:47:26 PM
Most procedures you use are STDCALL, that means the proc balances the stack. An automated stack frame will modify the RET opcode to include the number of bytes to remove from the stack,for example if you have 2 DWORD parameters it will change it to RET 8. So no, you do not have to worry about balancing the stack in STDCALL, it is handled in the stack frame. C calling convention requires that the caller balance the stack, in that case you are responsible but specific to MASM, it will also insert the necessary ADD ESP,xxx into your code so you need not worry about it either.
Title: Re: Procedures
Post by: donkey on January 12, 2005, 01:58:49 PM
Just to explain a bit about stack frames,which seem to be a hot topic these days, this is the general way they are built...

CALL:
1. Push the parameters onto the stack
2. push the return address
3. jump to the function

PROC:
1. Save the value of EBP (stack base pointer usually PUSH it)
2. Save any registers in the USES directive
3. Move ESP into EBP (the top of the entry stack is now seen as the base)
4. SUB the number of local bytes from ESP (make room for the local data)

5. Execute the proc...

6. Move EBP into ESP (reset the top of the stack)
7. Restore the USES registers
8. Restore EBP (usually POP it)
9. Adjust RET to remove the parameters in bytes from the stack (ie RET 8)
10. POP the return address
11. Jump to the return address
Title: Re: Procedures
Post by: Bieb on January 12, 2005, 02:40:00 PM
Could I get a quick code example?
Title: Re: Procedures
Post by: donkey on January 12, 2005, 02:53:59 PM
In MASM syntax...

MyProc PROTO :DWORD,:DWORD,:DWORD

invoke MyProc, 1,2,3

MyProc PROC uses EDI ESI EBX Param1:DWORD, Param2:DWORD,Param3:DWORD


RET
MyProc ENDP
Title: Re: Procedures
Post by: Bieb on January 12, 2005, 03:16:08 PM
And then I can just use Param1-Param3 like I would any other memory location?
Title: Re: Procedures
Post by: donkey on January 12, 2005, 03:18:53 PM
Yup, they assemble as an offset from EBP but you can use them as normal memory labels, mov data in and out perform math etc... The only exception is that you cannot directly get the offset of the memory location as it is not known at compile time, you must use LEA to caclulate it like this...

lea eax, Param1
Title: Re: Procedures
Post by: Vortex on January 12, 2005, 05:56:58 PM
Hi Bied,

Here is an example demonstrating how to code procedures at low level:


    .386
    .model flat, stdcall
    option casemap :none

    include \masm32\include\windows.inc
    include \masm32\include\kernel32.inc

    public StdOut@4
    .code

StdOut@4:
    push ebp
    mov ebp,esp
    add esp,-12    ;3 dwords
;   LOCAL hOutPut  :DWORD
;   LOCAL bWritten :DWORD
;   LOCAL sl       :DWORD

    invoke GetStdHandle,STD_OUTPUT_HANDLE
    mov [ebp-4], eax ; hOutPut

    invoke lstrlen,[ebp+8] ; lpszText
    mov [ebp-12], eax

    lea eax,[ebp-8] ; bWritten
    invoke WriteFile,[ebp-4],[ebp+8],[ebp-12],eax,NULL ; [ebp-12] = sl
    mov eax,[ebp-8]
    leave
    ret 4

end


Main application:


.386
.model flat, stdcall
option casemap :none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

StdOut PROTO :DWORD

.data

Message db 'Hello my friend!',0

.code

start:

invoke StdOut,ADDR Message 
invoke ExitProcess,0
END start



[attachment deleted by admin]
Title: Re: Procedures
Post by: MichaelW on January 12, 2005, 07:32:40 PM
Hi donkey,

In your listing of the operations under PROC, for MASM the order should be:
1. Save the value of EBP (stack base pointer usually PUSH it)
2. Move ESP into EBP (the top of the entry stack is now seen as the base)
3. SUB the number of local bytes from ESP (make room for the local data)
4. Save any registers in the USES directive
...

MyProc proc uses eax ebx ecx edx param1:DWORD, param2:DWORD
    LOCAL local1:DWORD, local2:DWORD, local3:DWORD
    mov   eax, param1
    mov   eax, param2
    mov   eax, local1
    mov   eax, local2
    mov   eax, local3
    ret
MyProc endp

00401000 55                     push    ebp
00401001 8BEC                   mov     ebp,esp
00401003 83C4F4                 add     esp,0FFFFFFF4h
00401006 50                     push    eax
00401007 53                     push    ebx
00401008 51                     push    ecx
00401009 52                     push    edx
0040100A 8B4508                 mov     eax,[ebp+8]
0040100D 8B450C                 mov     eax,[ebp+0Ch]
00401010 8B45FC                 mov     eax,[ebp-4]
00401013 8B45F8                 mov     eax,[ebp-8]
00401016 8B45F4                 mov     eax,[ebp-0Ch]
00401019 5A                     pop     edx
0040101A 59                     pop     ecx
0040101B 5B                     pop     ebx
0040101C 58                     pop     eax
0040101D C9                     leave
0040101E C20800                 ret     8

Title: Re: Procedures
Post by: donkey on January 12, 2005, 10:39:30 PM
Hi MichaelW,

Depends on which assembler you use :),

GoAsm...

0040105C   . 55             PUSH EBP
0040105D   . 57             PUSH EDI
0040105E   . 56             PUSH ESI
0040105F   . 53             PUSH EBX
00401060   . 89E5           MOV EBP,ESP
00401062   . 83EC 04        SUB ESP,4
00401065   . 8B45 18        MOV EAX,DWORD PTR SS:[EBP+18]

00401068   . 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
0040106A   . 50             PUSH EAX                                 ; |Title
0040106B   . 55             PUSH EBP                                 ; |Text
0040106C   . 830424 FC      ADD DWORD PTR SS:[ESP],-4                ; |
00401070   . 6A 00          PUSH 0                                   ; |hOwner = NULL
00401072   . E8 A72F0000    CALL <JMP.&USER32.MessageBoxA>           ; \MessageBoxA
; ...
0040108E   . 89EC           MOV ESP,EBP
00401090   . 5B             POP EBX
00401091   . 5E             POP ESI
00401092   . 5F             POP EDI
00401093   . 5D             POP EBP
00401094   . C2 1000        RETN 10


Note that at offset 0040106B it pushes an offset to a local but never uses LEA, the source line was...

invoke MessageBox,NULL,offset testlocal,eax,0

Should the USES stuff be pushed as in MASM it would be more difficult to handle a "registerless" invoke, by doing them prior to the mov it makes the math exceedingly easy.