How to create Arguments like CallFunctionFast

Just starting out? Need help? Post your questions and find answers here.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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! ;)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: How to create Arguments like CallFunctionFast

Post 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.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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(). :)
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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()
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: How to create Arguments like CallFunctionFast

Post 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
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: How to create Arguments like CallFunctionFast

Post 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
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: How to create Arguments like CallFunctionFast

Post 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 ?
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Re: How to create Arguments like CallFunctionFast

Post 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
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: How to create Arguments like CallFunctionFast

Post 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.
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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)
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Re: How to create Arguments like CallFunctionFast

Post 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...
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Re: How to create Arguments like CallFunctionFast

Post 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
Post Reply