Hello!
After reading some calling convention and optimization docs I've decided to create fastcall macros to declare and to call procedures with 3 arguments passed in regs. I bumped into a big obstacle: how can I compare a macro argument to a text? Let's have a look on my fastcall macro:
;===============================================================
;
; Calling a fastcall procedure
; parameters are passed in eax,ecx,edx and the stack
;
;===============================================================
fastcall MACRO procname:REQ,arglist:VARARG
LOCAL count
LOCAL bytes
LOCAL regtext
count = 0
bytes = 0
regtext TEXTEQU <eaxecxedx>
FOR arg,<arglist>
count = count + 1
IF count EQ 1
mov eax,arg
ENDIF
IF count EQ 2
mov ecx,arg
ENDIF
IF count EQ 3
mov edx,arg
ENDIF
IF count GT 3
push arg
bytes = bytes + sizeof &arg
ENDIF
ENDM
call &procname
IF bytes NE 0
add esp,bytes
ENDIF
ENDM
This works but must be improved. If the first parameter was already eax, then it would insert the errorous "mov eax,eax" line. I need to check whether the arg was already eax (or ecx, edx).
I tried this:
IF arg NE <eax>
mov eax,arg
ENDIF
This does not work. :'(
Basicly I need a method to be able to check whether the argument passed to the macro was a register, and which register it was...
The point here is to hide the fastcall thing as much as possible, so for the declaration the best solution would be
__fastcall procedure PROC USES register,argumentlist
I am afraid to process this kind of syntax in a macro would be too complex, so I simply leave it (thus giving opportunity for coding errors :( )
Any idea about this second problem is appreciated!
Greets, Gábor
ifdif <arg>,<eax>
mov eax,arg
endif
Nice work Gabor. You would like to have a look at libraries exporting FASTCALL API functions, an example is dnsapi.lib :
LIBRARY dnsapi
EXPORTS
"@DnsGetDomainName@4"
"@DnsIsAMailboxType@4"
"@DnsIsStatusRcode@4"
"@DnsMapRcodeToStatus@4"
"@DnsStatusString@4"
"@DnsUnicodeToUtf8@16"
"@DnsUtf8ToUnicode@16"
"@Dns_ReadPacketName@28"
"@Dns_ReadPacketNameAllocate@28"
"@Dns_SkipPacketName@8"
"@Dns_WriteDottedNameToPacket@24"
POASM supports the FASTCALL calling convention.
With just a bit of argument-verification:
see last post
Note that I removed the "add esp,bytes", since I think it'll be faster to base on stdcall rather than c-call.
Hello!
Thank you very much for the posts, they are very helpful!
Vortex!
Thanks for the suggestion! How could I have a look on those lib? (Sorry for the lame question.) Will that help me in the declaration of fastcall procedures?
PoASM is really great for supporting this, I guess I have to check it out more seriously.
Ultrano!
I used stdcall because AFAIK this is the only way of calling a procedure with variable argument count. I am afraid what you posted won't work because the argument are passed on the stack but the called procedure won't recognize how many args were passed. Of course I may be wrong...
Still, my greater problem is the declaration of fastcall procedures. I want it to look like a normal PROTO or PROC declaration (so all parameters given) but processing the macro would remove the first 3 parameters (since they are passed via registers).
Thanks again!
Greets, Gábor
Fixed and added:
fastcall MACRO procname:REQ,arglist:VARARG
LOCAL count,arg,tempnum
LOCAL usedEAX,usedECX
usedEAX=0
usedECX=0
;----[ first, push excessive to stack ]--------[
count = 0
FOR arg,<arglist>
count = count + 1
endm
if count GT 3
tempnum = count
while tempnum gt 3
count = 0
for arg,<arglist>
count = count+1
if count eq tempnum
push arg
endif
endm
tempnum = tempnum-1
endm
endif
;----------------------------------------------/
;----[ set registers ]------------------------[
count = 0
FOR arg,<arglist>
count = count + 1
IF count EQ 1
ifdifi <arg>,<eax>
mov eax,arg
usedEAX=1
endif
ENDIF
IF count EQ 2
ifdifi <arg>,<ecx>
mov ecx,arg
usedECX=1
endif
ifidni <arg>,<eax>
if usedEAX
.err <Second parameter can't be EAX>
endif
endif
ENDIF
IF count EQ 3
ifdifi <arg>,<edx>
mov edx,arg
endif
ifidni <arg>,<eax>
if usedEAX
.err <Third parameter can't be EAX>
endif
endif
ifidni <arg>,<ecx>
if usedECX
.err <Third parameter can't be ECX>
endif
endif
ENDIF
endm
;---------------------------------------------/
call &procname
ENDM
FastCall macro args:VarArg
local arg,text
local count
count=0
for arg,<args>
if count eq 0
arg CATSTR <eax>
elseif count eq 1
arg CATSTR <ecx>
elseif count eq 2
arg CATSTR <edx>
elseif count eq 3
text CATSTR <arg>
else
text CATSTR text,<,>,<arg>
endif
count = count+1
endm
if count LT 4
exitm <>
else
exitm <text>
endif
endm
Example usage:
blah proc FastCall(param1,param2,param3,param4,param5,param6)
print param1
print param2
print param3
print param4
print param5
print param6
ret
blah endp
main proc
fastcall blah,17,2,3,10,5,999
ret
main endp
Just watch-out when overwriting eax,ecx and edx in "blah" :)
Hi gabor,
You are welcome. Attached file includes dnsapi.def and dnsapi.lib Notice that FASTCALL functions should have a prepended @ symbol :
@DnsGetDomainName@4
PS : I mentioned about this library because it's an example of FASTCALL API functions.
[attachment deleted by admin]
Thanks for the macros :U
Hello again!
Vortex, thanks for the lib, but I am afraid it won't help me too much since it only contains the names of the fastcall procs. What I need is a macro that hides the fastcall mechanism. Like
__fastcall procname PROTO arg1,arg2,arg3,arg4
is change via macros into
procname PROTO arg4
The first 3 args are passed in eax,ecx,edx, so they won't be passed via the stack.
Of course since I would use the fastcall macro to invoke the proc I can simply leave off the PROTO. (I could put it into a comment just to show the signature of the proc.)
Really important is this:
__fastcall procname PROC USES reglist,arg1,arg2,arg3,arg4
I understand that to parse such a text could be too much so the approach Ultrano gave would be just fine:
procname PROC USES reglist,@Fastcall(arg1,arg2,arg3,arg4)
There is 1 problem with this.
arg CATSTR <eax>
This causes that the text <arg> will be substitued later also, at unwanted places in the code. This gives errors.
An example:
MyProc1 PROC @FastCall(arg1,arg2,arg3,arg4) ; causes arg1=<eax>, arg2=<ecx>, arg3=<edx>
; later in the code
MyProc2 PROC arg1:DWORD,arg2:DWORD ; is changed to MyProc2 PROC eax:DWORD,ecx:DWORD --> gives sytax error
Is it somehow possible to make a literal (arg1,arg2) lose its value, to undefine it?
Greets, Gábor
The only way I found is to later rename Arg1 into Arg1__
FastCall macro args:VarArg
local arg,text
local count
count=0
for arg,<args>
if count eq 0
$FastCall$Z1 CATSTR <arg>,<__>
$FastCall$P1 textequ <eax>
arg textequ <$FastCall$P1>
elseif count eq 1
$FastCall$Z2 CATSTR <arg>,<__>
$FastCall$P2 textequ <ecx>
arg textequ <$FastCall$P2>
elseif count eq 2
$FastCall$Z3 CATSTR <arg>,<__>
$FastCall$P3 textequ <edx>
arg textequ <$FastCall$P3>
elseif count eq 3
text CATSTR <arg>
else
text CATSTR text,<,>,<arg>
endif
count = count+1
endm
if count EQ 0
$FastCall$P1 textequ $FastCall$Z1
$FastCall$P2 textequ $FastCall$Z2
$FastCall$P3 textequ $FastCall$Z3
endif
if count LT 4
exitm <>
else
exitm <text>
endif
endm
And you'll have to use FastCall like this:
blah proc FastCall(param1,param2,param3,param4,param5,param6)
print param1
print param2
print param3
print param4
print param5
print param6
ret
blah endp FastCall()
Btw, the "fastcall" calling method doesn't actually boost your code even with 2 cycles...