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