Page 2 of 3
Re: How to create Arguments like CallFunctionFast
Posted: Fri Dec 16, 2011 8:32 pm
by Danilo
Code: Select all
Procedure.i CallFast(hFunc,Array Args.l(1))
Protected size.l=ArraySize(Args()),res.i
If hFunc
!mov ecx,dword[p.v_size]
!mov eax,dword[p.v_hFunc]
!or ecx,ecx
!jz .CallFast_NoArgs
!mov edi,dword[p.a_Args]
!mov edi,dword[edi]
!@@:push dword[edi+ecx*4]
!dec ecx
!jnl @r
!.CallFast_NoArgs:
!call eax
!mov dword[p.v_res],eax
EndIf
ProcedureReturn res
EndProcedure
Dim a.l(0)
If OpenLibrary(0,"User32.dll")
f = GetFunction(0,"GetForegroundWindow")
CallFast(f,a())
EndIf
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 7:36 am
by Danilo
Code: Select all
Procedure.i CallFast(hFunc,Array Args.l(1))
Protected size.l=ArraySize(Args()),res.i
If hFunc
!mov ebx,dword[p.v_size]
!mov eax,dword[p.v_hFunc]
!or ebx,ebx
!jz .CallFast_NoArgs
!mov edi,dword[p.a_Args]
!mov edi,dword[edi]
!@@:push dword[edi+ebx*4]
!dec ebx
!jnz @r
!.CallFast_NoArgs:
!call eax
!mov dword[p.v_res],eax
EndIf
ProcedureReturn res
EndProcedure
Procedure.i CallCFast(hFunc,Array Args.l(1))
Protected size.l=ArraySize(Args()),res.i
If hFunc
!push dword ecx
!mov ecx,dword[p.v_size+4]
!mov eax,dword[p.v_hFunc+4]
!mov dword ebx,ecx
!or ecx,ecx
!jz .CallCFast_NoArgs
!mov edi,dword[p.a_Args+4]
!shl dword ebx,2
!mov edi,dword[edi]
!@@:push dword[edi+ecx*4]
!dec ecx
!jnz @r
!.CallCFast_NoArgs:
!call eax
!add esp,ebx
!pop dword ecx
!mov dword[p.v_res],eax
EndIf
ProcedureReturn res
EndProcedure
ProcedureC Cfunc1()
ProcedureReturn 123
EndProcedure
ProcedureC Cfunc2(a,b,c)
Debug a
Debug b
Debug c
ProcedureReturn 456
EndProcedure
Procedure func3()
ProcedureReturn 789
EndProcedure
Procedure func4(a,b,c)
Debug a
Debug b
Debug c
ProcedureReturn 121212
EndProcedure
Dim a.l(0)
f = @Cfunc1()
x = CallCFast(f,a()) ; cdecl func
Debug x
Debug "---"
Dim a.l(3)
a(1)=12
a(2)=34
a(3)=56
f = @Cfunc2()
x = CallCFast(f,a()) ; cdecl func
Debug x
Debug "---"
Dim a.l(0)
f = @func3()
x = CallFast(f,a()) ; stdcall func
Debug x
Debug "---"
Dim a.l(3)
a(1)=7
a(2)=8
a(3)=9
f = @func4()
x = CallFast(f,a()) ; stdcall func
Debug x
Dim a(0) means 0 arguments.
Dim a(3) means 3 arguments and the arguments need to be at a(1), a(2) and a(3)! a(0) is not used here. Don't forget!

Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 7:49 am
by wilbert
If you enclose the asm routine inside something like this
Code: Select all
!push ebp
!mov ebp, esp
; rest of asm function that makes the call
!mov esp, ebp
!pop ebp
to save ans restore the stack pointer, it will work for both C and non C functions.
That way you don't have to worry what kind of function you are calling.
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 8:02 am
by Danilo
Thanks, so it would be:
Code: Select all
Procedure.i CallFast(hFunc,Array Args.l(1))
Protected size.l=ArraySize(Args()),res.i
If hFunc
!push ebp
!mov ebx,dword[p.v_size+4]
!mov ebp, esp
!mov eax,dword[p.v_hFunc+4]
!or ebx,ebx
!jz .CallFast_NoArgs
!mov edi,dword[p.a_Args+4]
!mov edi,dword[edi]
!@@:push dword[edi+ebx*4]
!dec ebx
!jnz @r
!.CallFast_NoArgs:
!call eax
!mov esp, ebp
!pop ebp
!mov dword[p.v_res],eax
EndIf
ProcedureReturn res
EndProcedure
ProcedureC Cfunc1()
ProcedureReturn 123
EndProcedure
ProcedureC Cfunc2(a,b,c)
Debug a
Debug b
Debug c
ProcedureReturn 456
EndProcedure
Procedure func3()
ProcedureReturn 789
EndProcedure
Procedure func4(a,b,c)
Debug a
Debug b
Debug c
ProcedureReturn 121212
EndProcedure
Dim a.l(0)
f = @Cfunc1()
x = CallFast(f,a()) ; cdecl func
Debug x
Debug "---"
Dim a.l(3)
a(1)=12
a(2)=34
a(3)=56
f = @Cfunc2()
x = CallFast(f,a()) ; cdecl func
Debug x
Debug "---"
Dim a.l(0)
f = @func3()
x = CallFast(f,a()) ; stdcall func
Debug x
Debug "---"
Dim a.l(3)
a(1)=7
a(2)=8
a(3)=9
f = @func4()
x = CallFast(f,a()) ; stdcall func
Debug x
Now test with printf().

Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 8:24 am
by Danilo
Danilo wrote:Now test with printf().

Works. Compile as console application.
Code: Select all
Procedure.i CallFast(hFunc,Array Args.l(1))
Protected size.l=ArraySize(Args()),res.i
If hFunc
!push ebp
!mov ebx,dword[p.v_size+4]
!mov ebp, esp
!mov eax,dword[p.v_hFunc+4]
!or ebx,ebx
!jz .CallFast_NoArgs
!mov edi,dword[p.a_Args+4]
!mov edi,dword[edi]
!@@:push dword[edi+ebx*4]
!dec ebx
!jnz @r
!.CallFast_NoArgs:
!call eax
!mov esp, ebp
!pop ebp
!mov dword[p.v_res],eax
EndIf
ProcedureReturn res
EndProcedure
printf.l
CompilerIf #PB_Compiler_Unicode
!extrn _wprintf
!push dword _wprintf
CompilerElse
!extrn _printf
!push dword _printf
CompilerEndIf
!pop dword [v_printf]
OpenConsole()
Dim a.l(4)
a(1) = @"hello world! arg1 = %d, arg2 = %d, arg3 = %s"
a(2) = 123
a(3) = 456
a(4) = @"PureBasic"
callfast(printf,a())
Input()
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 9:16 am
by wilbert
Danilo wrote:Thanks, so it would be
It seems to work fine that way.
Just to be sure you might want to use the volatile registers ecx and edx instead of ebx and edi since those two officially should be preserved if I'm correct.
The check for hFunc can be done inside the asm routine. That way the variable only needs to be loaded once.
Code: Select all
Procedure.l CallFast(hFunc,Array Args.l(1))
Protected size.l=ArraySize(Args())
!mov eax,dword[p.v_hFunc]
!or eax,eax
!jz .CallFast_NoFunc
!mov ecx,dword[p.v_size]
!mov edx,dword[p.a_Args]
!push ebp
!mov ebp,esp
!or ecx,ecx
!jz .CallFast_NoArgs
!mov edx,dword[edx]
!@@:push dword[edx+ecx*4]
!dec ecx
!jnz @r
!.CallFast_NoArgs:
!call eax
!mov esp,ebp
!pop ebp
!.CallFast_NoFunc:
ProcedureReturn
EndProcedure
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 9:26 am
by Danilo
wilbert wrote:Just to be sure you might want to use the volatile registers ecx and edx instead of ebx and edi since those two officially should be preserved if I'm correct.
That's correct, ESI, EDI, EBX, and EBP registers need to be preserved, so i assumed PB saves and restores
this registers in its prolog and epilog code. Of course i did not check it.

In the first code i used EBX after the call to restore the stack, thats why i used EBX - because
the called function needs to preserve it too. But your way does not need that anymore.
Next question is how to make it work with floats and other types (mixed) to fully use cdecl functions like printf, sprintf etc.
Not sure what Louise wants to do exactly.
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 9:37 am
by wilbert
If you need a function that also works on OS X, this should do it
Code: Select all
Procedure.l CallFast(hFunc,Array Args.l(1))
Protected addr.l=@Args(), size.l=ArraySize(Args())
EnableASM
MOV eax, hFunc
!or eax,eax
!jz CallFast_NoFunc
MOV ecx, size
MOV edx, addr
!push ebp
!mov ebp,esp
!shl ecx,2
!sub esp, ecx
!and esp, 0xfffffff0
!add esp, ecx
!or ecx,ecx
!jz CallFast_NoArgs
!CallFast_CopyArgs:
!push dword[edx+ecx]
!sub ecx,4
!jnz CallFast_CopyArgs
!CallFast_NoArgs:
!call eax
!mov esp,ebp
!pop ebp
!CallFast_NoFunc:
DisableASM
ProcedureReturn
EndProcedure
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 10:28 am
by Danilo
Can you add CallFast to this?
Code: Select all
Enumeration 1
#VAT_string
#VAT_long
#VAT_quad
#VAT_float
EndEnumeration
Structure VARARGS
type.l
StructureUnion
a.a
b.b
w.w
l.l
i.i
q.q
f.f
*p
EndStructureUnion
EndStructure
Procedure VAstring(*p.VARARGS,*str)
*p\type = #VAT_string : *p\p = *str
EndProcedure
Procedure Valong(*p.VARARGS,lng)
*p\type = #VAT_long : *p\l = lng
EndProcedure
Procedure VAquad(*p.VARARGS,quad.q)
*p\type = #VAT_quad : *p\q = quad
EndProcedure
Procedure VAfloat(*p.VARARGS,f.f)
*p\type = #VAT_float : *p\f = f
EndProcedure
Dim a.VARARGS(5)
VAstring(@a(1),@"hello world! arg1 = %d, arg2 = %lld, arg3 = %f, arg4 = %s")
VAlong (@a(2),123)
VAquad (@a(3),456)
VAfloat (@a(4),1.234)
VAstring(@a(5),@"PureBasic")
printf.l
CompilerIf #PB_Compiler_Unicode
!extrn _wprintf
!push dword _wprintf
CompilerElse
!extrn _printf
!push dword _printf
CompilerEndIf
!pop dword [v_printf]
OpenConsole()
callfast(printf,a())
Input()
Long and String as DWORD on CPU stack.
Quad as QWORD on CPU stack.
Float as DWORD on FPU stack.
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 11:06 am
by wilbert
It should be possible to make it that flexible.
I'm not sure how types like a, b and w are passed.
Do they always occupy 4 bytes or should a word be passed as 2 bytes ?
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 11:09 am
by Psychophanta
wilbert wrote:If you enclose the asm routine inside something like this
Code: Select all
!push ebp
!mov ebp, esp
; rest of asm function that makes the call
!mov esp, ebp
!pop ebp
to save ans restore the stack pointer, it will work for both C and non C functions.
That way you don't have to worry what kind of function you are calling.
Good idea, but why not just this?:
Code: Select all
!push esp
; rest of asm function that makes the call
!pop esp
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 11:19 am
by wilbert
Psychophanta wrote:Good idea, but why not just this?:
Because you can't pop the previous stack pointer once the stack pointer has been changed.
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 11:22 am
by Danilo
wilbert wrote:It should be possible to make it that flexible.
I'm not sure how types like a, b and w are passed.
Do they always occupy 4 bytes or should a word be passed as 2 bytes ?
"All arguments are widened to 32 bits when they are passed." (MSDN: Argument Passing and Naming Conventions)
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 11:27 am
by Psychophanta
Danilo wrote:wilbert wrote:It should be possible to make it that flexible.
I'm not sure how types like a, b and w are passed.
Do they always occupy 4 bytes or should a word be passed as 2 bytes ?
"All arguments are widened to 32 bits when they are passed." (MSDN: Argument Passing and Naming Conventions)
What about the returned value?
It could be a float, or double, or quad...
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 11:31 am
by Danilo
Psychophanta wrote:What about the returned value?
It could be a float, or double, or quad...
http://msdn.microsoft.com/en-us/library/k2b2ssfy.aspx