News:

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

CALL vs INVOKE

Started by Robert Collins, December 31, 2004, 02:29:56 AM

Previous topic - Next topic

Robert Collins

I see the 'invoke' statement in all assembly source code but very seldom see the 'call' statement. What is the difference? Can I assume that the 'invoke' statement can always be used instead of the 'call' statement?

raymond

Quote from: Robert Collins on December 31, 2004, 02:29:56 AM
Can I assume that the 'invoke' statement can always be used instead of the 'call' statement?

The 'call' statement could ALWAYS be used instead of the 'invoke' statement. The 'invoke' statement could not be used unless:
- the code being called is written as a formal procedure (proc - endp)
- a PROTO is included at the beginning of your source code defining the number and types of parameters required by the procedure.

The main advantage of INVOKE: The assembler uses the PROTO data to check the number and type of parameters used when "invoking" the procedure and issues an error message if they don't match, thus preventing many potential bugs in a program.
A secondary advantage is that it makes the source code easier to read by having the parameters on the same line as the invoked procedure, instead of having to push each parameter individually (and in the proper order).

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

donkey

The PROTO directive is only used in MASM,there are no other assemblers that use it as far as I know, in MASM it is only necessary for forward referenced procedures or external ones. Invoke is a macro that pushes the parameters onto the stack (in STDCALL or reverse order) and executes a call. It is simply a convenient shorthand so instead of doing this...

push Param2
push Param1
call function

You would just use

invoke function, Param1, Param2

Both encode exactly the same, I almost exclusively use invoke, I find the code is clearer and easier to read.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Robert Collins

#3
Quote from: donkey on December 31, 2004, 03:10:11 AM
The PROTO directive is only used in MASM,there are no other assemblers that use it as far as I know, in MASM it is only necessary for forward referenced procedures or external ones. Invoke is a macro that pushes the parameters onto the stack (in STDCALL or reverse order) and executes a call. It is simply a convenient shorthand so instead of doing this...

push Param2
push Param1
call function

You would just use

invoke function, Param1, Param2

Both encode exactly the same, I almost exclusively use invoke, I find the code is clearer and easier to read.

OK, so if I use 'call' then it goes something like this.......


   .
   .
   .
   push   param2
   push   param1
   call   MyProc
   '
   '
   '
MyProc Proc
   pop   param1
   pop   param2
   '
   '
   ret


Now, if I use 'invoke', it goes something like this.....


   .
AnotherProc  PROTO :DWORD,:DWORD
   .
   .
   .
   .
   invoke AnotherProc, Param2, Param1
   '
   '
   '
AnotherProc Proc Param1, Param2
   '
   '
AnotherProc EndP


So, where in the proc that is 'invoked' are the passed parameters? I have not seen any proc that is 'invoked' pop the parameters off the stack. 
     

hutch--

Robert,

The lowest level you have is mnemonic coding where you use push/call syntax to call other procedures. Thi is fine with simple stuff but it gets progressively more complicate to keep track of as the parameter count gets higher. Where you have high level similations like "invoke" in automates the call interface and has rudimentarytype checking to catch paraeter count and size errors. When you use an API like CreateFont() or CreateWindowEx() which have large parameter counts, you do more work to get them right if you do all of the paraeter pushes manually. "invoke" also uses the "ADDR" operator which handles both .DATA section variables as well as local STACK based variables which are coded differently so as long as you know how they work, you code faster and more reliably using the type checked invoke form.

With MASM the stack corrections are done at the end but are coered by the PROC ENDP format. MASM usually uses RET BYTECOUNT then LEAVE. It is no big deal to manually code a procedure and if you ned the extra register EBP, it allows you to write some very fast code where you need it but you can have the best of both by using the standard options in MASM to turn off stack frames.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

donkey

The parametersin a proc are addressed by their relative position to EBP/ESP, there is never a need to POP them. For example

invoke someproc,Param1,Param2

someproc PROC wParam, lParam

mov eax, [wParam]
ret
someproc ENDP

In the case above the 2 parameters are pushed onto the stack by invoke, the stack frame is built with 2 parameters (wParam & lParam),they will resolve to [EBP+8] and [EBP+12]. So the mov encodes as

mov eax, [EBP+8]

[EBP+8] contains the value that was pushed for Param1.

I should note that the stack is perhaps a bit advanced to play with in this way, you are probably better off as a beginner to allow the assembler to handle parameters and such. Above all, since the return address is pushed onto the stack when CALL is executed,do not pop anything off the stack unless you have pushed it from within the procedure. At least not until you gain a greater understanding of the stack and how it works.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Robert Collins

OK, Thanks for the info. I was just curious, I always use invoke anyway.

Vortex

GoAsm appears to be the most flexible to treat invoke, this assembler doesn't require any function prototype to be predefined.

Grincheux

The standard INVOKE statement can be replaced by a new INVOKE2. This one push the parameters in the same maner than INVOKE but does not use the CALL instruction. The CALL is replaced by a PUSH RETADR and is followed by a JMP. The syntax is

INVOKE2  OFFSET REDADDR,DoSomeThingFunc,Param1,Param2

This is very useful is you want to optimize the next instructions :

INVOKE DoSomeThing,Param1,Param2
JMP       AnyWhere

That could be replaced by

INVOKE2  AnyWhere,DoSomeThing,Param1,Param2.

or

INVOKE2 $ + 10,SelectObject,hDC,hBmp

or

INVOKE2 @F,SelectObject,hDC,hBmp
@@ :


A member of this forum wrote the macro for me. Here is the source code

********************* MACRO SOURCE CODE ***************************

@ArgRev macro arglist:REQ
   LOCAL txt, arg
   txt TEXTEQU <>
   
   %FOR arg, <arglist>
      txt CATSTR <arg>, <!,>, txt
   ENDM

   txt SUBSTR txt, 1, @SizeStr( %txt ) - 1
   txt CATSTR <!<>, txt, <!>>
   
   exitm txt
endm

INVOKE2 macro retaddr:REQ, function:REQ, parameters:VARARG
   LOCAL character, edxused, paddr
   
   edxused = 0

   IFNB <parameters>
      %FOR parameter, @ArgRev(<parameters>)
         IF @SizeStr(<parameter>) GT 4
            character SUBSTR <parameter>, 1, 5
               
            IFIDNI character, <ADDR >
               paddr SUBSTR <parameter>, 6, @SizeStr(<parameter>) - 5
               
               IF (OPATTR paddr) AND 00001001b ;; use OFFSET if direct memory address
                  push OFFSET paddr
               ELSE
                  lea edx, paddr
                  push edx
                  edxused = 1
               ENDIF
            ELSE
               push parameter
            ENDIF
         ELSE
            IF edxused NE 0
               .ERRIDNI <parameter>, <edx>, <"edx was used with ADDR, and is now messed up!">
            ENDIF
             
            push parameter
         ENDIF
      ENDM
   ENDIF
   
   IF (OPATTR retaddr) AND 11011011b      ; if is mem address etc..
      push retaddr
   ELSEIF (OPATTR retaddr) AND 00000100b      ; if is constant
      IF retaddr EQ 0
         ; do nothing..
      ELSE
         push retaddr
      ENDIF
   ELSE
      push retaddr
   ENDIF
   
   jmp function
endm
******************* END OF MACRO SOURCE CODE *************************

Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

petezl

Robert,

This thread shows just how flexible the individual code writing styles can be.
It's really worth taking a look at the "Test Department" link for a good insight to using "call", most informative and well commented, even if you decide you don't like the style.
I found that some calls, particularly calling Dll's require a "call" instead of "invoke" which falls into line with Raymonds post.
Other than that, "invoke" makes for fluent code writing (and reading), less mistakes and probably most important in my case is that I can return to it several months later and still understand the program flow.

Peter.
Cats and women do as they please
Dogs and men should realise it.

Petroizki

Quote from: IDCat on December 31, 2004, 11:24:15 AMA member of this forum wrote the macro for me. Here is the source code

I believe i made that macro. You should be careful when using the 'ADDR' for effective adresses, there are cases where the macro cannot resolve if the edx is already used and you will end up pushing wrong data to the stack. So you should manually use the 'lea' instruction, this would also usually result in better pairing.

Anyway, i'm making new version of my function macros, and i it will have some changes to these call macros. The real power of using your own invoke macro is to add some nice things; like calling with floating points and strings, like in my fncall.

Vortex,
If GoAsm doesn't need prototypes, then it probably doesn't warn you when calling with wrong amount of parameters? Is there a way to automatically correct the stack when calling with C-calling convention?

donkey

Hi,

No, GoAsm does not do parameter checking, with C calls, like FASM, you must manually correct the stack with ADD ESP, xx. However you could always write a macro to do it using ARGCOUNT, though for myself I am comfortable with correcting the stack myself.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Ratch

Quote@ArgRev macro arglist:REQ
   LOCAL txt, arg
   txt TEXTEQU <>
   
   %FOR arg, <arglist>
      txt CATSTR <arg>, <!,>, txt
   ENDM

   txt SUBSTR txt, 1, @SizeStr( %txt ) - 1
   txt CATSTR <!<>, txt, <!>>
   
   exitm txt
endm

IDCat,
     Look at this link http://www.masmforum.com/simple/index.php?topic=99.0.  Toward the end is my macro for INVOKIT.  Notice the simplified version for @ArgRev.  Notice also the macro for RPUSHIT.  Why can't we do the following?


@        TEXTEQU <OFFSET>
RPUSHIT @ ANYWHERE,PARAM1,PARAM2,PARAM3,.....
JMP DOSOMETHING


     Sure, Invoke2 swallows the JMP, but making the JMP external helps document it.  Ratch

Grincheux

I don't really understand macro, making macro, using them is very easy. The macro INVOKIT seems to be very small and with these changes it seems to do the same thing as INVOKE2.

The macro INVOKE2 is very good for me. Now I have too solutions.

In your solution or in the original INVOKE2 (THAT I DID NOT WRITE) it could be pleasant to add the '@' statement to sepcify an OFFSET but if use it with "@F" or "@B" I am not sure it's OK.

What I would like is : INVOKXX ANYWHERE,DOMETHING,PARM1,PARM2... and I would use as :

INVOKXX @F,DOSOMETHING,eax,ADDR szString
or
INVOKXX ANYWHERE,DOSOMETHING,eax,ADDR szString

I prefer a macro that use the edx register rather than a macro which uses the eax register for creating LEA instructions.

Sometimes the jump macro statement could be calculated, an example :

INVOKXX ANYWHERE,[TabFunc + eax * 4],ebx,ADDR szString

How is it possible to have this.

To the original author of INVOKE2 (it is not the original name) : e-mail me when you have a new version.

Thanks for everyone and happy new year


Kenavo

Grincheux
_____________________________________________________
http://www.phrio.biz

Vortex

Quote from: donkey on December 31, 2004, 06:45:04 PM
Hi,

No, GoAsm does not do parameter checking, with C calls, like FASM, you must manually correct the stack with ADD ESP, xx. However you could always write a macro to do it using ARGCOUNT, though for myself I am comfortable with correcting the stack myself.

Here is an example of cinvoke macro for GoAsm:

cinvoke(funcname,%1,%2,%3,%4,%5) = \
                               #if ARGCOUNT=1 \
                               invoke funcname  \
                               #elif ARGCOUNT=2   \
                               invoke funcname,%1  \
                               #elif ARGCOUNT=3      \
                               invoke funcname,%1,%2  \
                               #elif ARGCOUNT=4        \
                               invoke funcname,%1,%2,%3 \
                               #elif ARGCOUNT=5          \
                               invoke funcname,%1,%2,%3,%4 \
                               #elif ARGCOUNT=6             \
                               invoke funcname,%1,%2,%3,%4,%5 \
                               #endif                          \
                               #if ARGCOUNT>1                   \
                               ADD ESP,ARGCOUNT-1*4               \
                               #endif