Here is an example of simulating the invoke statement, it demonstrates the power of MASM's macro engine.
.386
.model flat, stdcall
option casemap :none
include \GeneSys\include\windows.inc
include \GeneSys\include\kernel32.inc
include \GeneSys\include\msvcrt.inc
include invoke.inc
includelib \GeneSys\lib\kernel32.lib
includelib \GeneSys\lib\msvcrt.lib
.data
message db "Hello world!",0
template db "%s",0
.code
start:
cinvoke _strupr,ADDR message
cinvoke printf,ADDR template,eax
_invoke ExitProcess,0
END start
invoke.inc :
_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos
FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>
IFNB <arg>
pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)
IF pos
IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
lea eax,@SubStr(<arg>,%pos+5)
push eax
ELSE
push OFFSET @SubStr(<arg>,%pos+5)
ENDIF
ELSE
push arg
ENDIF
ENDIF
ENDM
call funcname
ENDM
cinvoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos,counter
counter=0
FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>
IFNB <arg>
counter=counter+4
pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)
IF pos
IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
lea eax,@SubStr(<arg>,%pos+5)
push eax
ELSE
push OFFSET @SubStr(<arg>,%pos+5)
ENDIF
ELSE
push arg
ENDIF
ENDIF
ENDM
call funcname
IF counter NE 0
add esp,counter
ENDIF
ENDM
[attachment deleted by admin]
Here is another one, this one allows the user to use the exact same format as invoke, we have included this in our macro set.
.386
.model flat, stdcall
option casemap :none
include \GeneSys\include\windows.inc
include \GeneSys\include\kernel32.inc
include \GeneSys\include\msvcrt.inc
include \GeneSys\macros\macros.asm
includelib \GeneSys\lib\kernel32.lib
includelib \GeneSys\lib\msvcrt.lib
.data
message db "Hello world!",0
.code
start:
CallIt printf, offset message, eax
Add esp, 8
CallIt ExitProcess, 0
END start
CallIt MACRO procedure, parameters:VARARG
Local param, reversed
reversed TEXTEQU <>
%For param, <parameters>
reversed CATSTR <param>, <!,>, reversed
EndM
%For param, <reversed>
push param
EndM
call procedure
ENDM
[attachment deleted by admin]
Now, the functionalities of both macros are united into one :
.code
start:
_invoke _strupr,ADDR message
_invoke printf,ADDR template,eax
_invoke ExitProcess,0
END start
invoke.inc :
_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos,counter
counter=0
FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>
IFNB <arg>
counter=counter+4
pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)
IF pos
IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
lea eax,@SubStr(<arg>,%pos+5)
push eax
ELSE
push OFFSET @SubStr(<arg>,%pos+5)
ENDIF
ELSE
push arg
ENDIF
ENDIF
ENDM
call funcname
IF (OPATTR(funcname)) EQ 421
IF counter NE 0
add esp,counter
ENDIF
ENDIF
ENDM
[attachment deleted by admin]
Hi Paul,
printf is a C function, so your line :
CallIt printf, offset message, eax
requires a manual stack balancing :
add esp,8
Thanks,
I hate that stack balancing requirement required by C functions. I am not a wizard at using C stuff like you are so if this is a silly question, please understand. How come these C functions do not balance the stack themselves. I cannot ever remember creating anything that did not do its own clean up when done. I have not coded in C in over 20 years so I cannot remember if this was always an issue. I know that printf, for example comes from C and was first created in the '80s.
Paul
Paul,
It is because the C calling convention handles a variable number of parameters which cannot be determined within the proc so it is designed for the caller to balance the stack rather than automatically doing it like STDCALL where the stack arg size is known.
Hutch,
Thank you for that reply, it is helpful but also leads into my next question. Do you think I could count parameters. If so, perhaps I could modify CallIt to count parameters, multipy by 4 and do a add esp,eax. How does that look to you? I know that you are a macro guru so I definitely value your input. The only bottleneck is the macro would have to know if the call is to a C function. That might not be possible. :'(
Actually, perhaps it is better for me to write my own preprocessor for any C function that I happen to use, but I would rather know if there is some way of knowing from the macro. It would be the perfect solution.
Paul
Hi Paul,
Check my latest version of invoke macro simulator above. ( invk_simul2.zip )
These two lines are keeping track of the number of parameters passed to the macro :
IFNB <arg>
counter=counter+4
.
.
The code portion to detect C functions :
IF (OPATTR(funcname)) EQ 421
IF counter NE 0
add esp,counter
ENDIF
ENDIF
Quote from: Vortex on October 30, 2006, 06:10:20 PM
The code portion to detect C functions :
IF (OPATTR(funcname)) EQ 421
IF counter NE 0
add esp,counter
ENDIF
ENDIF
The
IF (OPATTR(funcname)) EQ 421 should, I believe, use the AND operator (or a combination of AND and EQ), since OPATTR may return other details which will cause an equality check to fail. Though I haven't checked your 421 value, it looks like quite a few bits are set.
Cheers,
Zooba :U
Quote from: zooba on October 31, 2006, 11:11:11 PM
The IF (OPATTR(funcname)) EQ 421 should, I believe, use the AND operator (or a combination of AND and EQ), since OPATTR may return other details which will cause an equality check to fail. Though I haven't checked your 421 value, it looks like quite a few bits are set.
The set bits are:
0 – references a code label
2 – is immediate expression
5 – references no undefined symbols and is without error
7 – references an external label
8 – language type C
For an invoke I can't see how these bits could be set:
1 – is a memory expression or has a relocatable data label
3 – uses direct memory addressing
4 – is a register expression
6 – is SS relative memory expression
But what about bit 7 for a local C procedure (however unlikely that might be)?
The set bits are:
0 – references a code label
2 – is immediate expression
5 – references no undefined symbols and is without error
7 – references an external label
8 – language type C
MichaelW is right, these are the bits. The trick to obtain the 421 value is to play with the OPATTR statement :
.386
.model flat,stdcall
option casemap:none
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
temp TEXTEQU %(OPATTR(wsprintf))
.code
start:
% echo temp
ret
END start
You get 421 as the result.
This one is a sight modification :
IF ((OPATTR(funcname)) AND 11100000000y) EQ 00100000000y
IF counter NE 0
add esp,counter
ENDIF
ENDIF
[attachment deleted by admin]
Here is a new version. Thanks to E^cube to report the bugs in the macro.
_invoke MACRO funcname:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
LOCAL pos,counter
counter=0
FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>
IFNB <arg>
counter=counter+4
pos=@InStr(1,arg,<ADDR >) OR @InStr(1,arg,<addr >) OR @InStr(1,arg,<Addr >) OR \
@InStr(1,arg,<ADDR >) OR @InStr(1,arg,<addr >) OR @InStr(1,arg,<Addr >)
IF pos
IF ((OPATTR(@SubStr(arg,%pos+5))) EQ 98) OR ((OPATTR(@SubStr(arg,%pos+5))) EQ 34)
lea eax,@SubStr(<arg>,%pos+5)
push eax
ELSE
push OFFSET @SubStr(<arg>,%pos+5)
ENDIF
ELSE
push arg
ENDIF
ENDIF
ENDM
call funcname
IF ((OPATTR(funcname)) AND 11100000000y) EQ 00100000000y ; Handle C functions
IF counter NE 0
add esp,counter ; correct the stack
ENDIF
ENDIF
ENDM
[attachment deleted by admin]
This functionality is already provided by invoke. A more usefull implementation would allow
for immediate strings such as - invok SomeFunction, "Literal String".
[Edit]
$str MACRO _STR
local L
if @InStr(1, <_STR>, <!">)
.data
ifdif <_STR>, <"">
L db _STR
endif
db 0
.code
exitm <offset L>
else
exitm <_STR>
endif
endm
invok MACRO _FUNC, _ARGS:VARARG
$ARGS equ <>
irp $V, <_ARGS>
$ARGS catstr $ARGS, <,>, $str($V)
endm
% invoke _FUNC $ARGS
endm
Alright great!, thanks Vortex, i'll test it out :bg
Thanks Vortex, it will be in the next update of the GeneSys SDK.
Paul
This new version creates the functions prototypes :
_invoke MACRO FuncName:REQ,args:VARARG
LOCAL counter,params,StringSize
params TEXTEQU <>
counter = 0
FOR param,<args>
counter=counter+1
ENDM
; IFNDEF FuncName
IF counter
REPEAT counter
params CATSTR params,<:DWORD,>
ENDM
params SUBSTR params,1,@SizeStr(%params)-1
ENDIF
FuncName PROTO STDCALL params
; ENDIF
IF counter
invoke FuncName,args
ELSE
invoke FuncName
ENDIF
ENDM
cinvoke MACRO FuncName:REQ,args:VARARG
LOCAL counter
counter = 0
FOR param,<args>
counter=counter+1
ENDM
; IFNDEF FuncName
FuncName PROTO C :VARARG
; ENDIF
IF counter
invoke FuncName,args
ELSE
invoke FuncName
ENDIF
ENDM
[attachment deleted by admin]