... Operator

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: ... Operator

Post by Demivec »

Josh wrote:User_Russian passes the number of following parameters as the first parameter when calling the procedure Test() and the procedure evaluates this again. There is no miracle behind this.
Thank you. I missed that trick. The numbers had appeared random for all parameters. I thought that there was something behind the scenes that supplied the count and that it was somehow automatically placed there.

I ran into a snag when testing User_Russian's code sample as written above without any modifications. The results are incorrect. This is what is displayed:

Code: Select all

Count = 2
Parameters: 0  0
----
Count = 10
Parameters: 140717973961617  0  8192000  100  4  1234  10000  10  16  80
----
I addition I combined User_Russian's code with a different approach using prototypes to see how that worked and got some strange results as well. My prototype method worked with the debugger on but not with it off. The portion that worked as User_Russian had written it still malfunctioned.

I did change User_Russian's method here to use CallFunctionFast() instead of CallCFunctionFast(). Here is my test code I used:

Code: Select all

Structure ArrI
  i.i[0]
EndStructure

Prototype MultiArgInteger(Count,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,a10=0,a11=0,a12=0,a13=0,a14=0,a15=0,a16=0,a17=0,a18=0,a19=0)

Procedure Test(Count)
  Protected *Param.ArrI=@Count+SizeOf(Count)
  Protected i
 
  PrintN("Count = "+Count)
  Print("Parameters: ")
  For i=0 To Count-1
    Print(Str(*Param\i[i])+"  ")
  Next
 
  PrintN(#CRLF$+"----")
EndProcedure

Procedure max(Count)
  Protected *Param.ArrI = @Count + SizeOf(Count)
  Protected mx = *Param\i[0]
  Protected i
  
  For i = 0 To Count - 1
    If *Param\i[i] > mx: mx = *Param\i[i]: EndIf
  Next
  ProcedureReturn mx
EndProcedure

Define Display.MultiArgInteger = @Test(), Max_multi.MultiArgInteger = @max()

OpenConsole()
CallFunctionFast(@Test(), 2, 1, 2)
CallFunctionFast(@Test(), 10, 2, 8, 20, 100, 4, 1234, 10000, 10, 16, 80)

Display(2, 1, 2)
Display(10, 2, 8, 20, 100, 4, 1234, 10000, 10, 16, 80)
PrintN("max value:" + Max_multi(3,22,133,45))                  ; result --> 133
PrintN("max value:" + Max_multi(6,90,88,12,45,677,211))        ; result --> 677
PrintN("max value:" + Max_multi(8,33,25,233,85,11,231,34,64))  ; result --> 233
Input()
The results are as follows with the Debugger:

Code: Select all

Count = 2
Parameters: 0  0
----
Count = 10
Parameters: 5368715837  34  0  100  4  1234  10000  10  16  80
----
Count = 2
Parameters: 1  2
----
Count = 10
Parameters: 2  8  20  100  4  1234  10000  10  16  80
----
max value:133
max value:677
max value:233
And the results without the debugger:

Code: Select all

Count = 2
Parameters: 0  0
----
Count = 10
Parameters: 140717973961617  0  8323072  100  4  1234  10000  10  16  80
----
Count = 2
Parameters: 10000  10
----
Count = 10
Parameters: 10000  10  16  100  4  1234  10000  10  16  80
----
max value:5368713718
max value:140717973961617
max value:140717973961617
Any idea what is going on? I'm using PureBasic v5.72 LTS x64 Windows 10
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: ... Operator

Post by Josh »

This is related to the calling conventions. CallFunctionFast() uses StdCall and this does not support a variable argument list. CallCFunctionFast() uses cdecl, so it works with CallCFunctionFast() and not with CallFunctionFast().

x64 is different anyway. I am not sure, but I think there is no difference between CallFunctionFast() and CallCFunctionFast() anymore. Which calling conventions are used there, I can't say at the moment.
sorry for my bad english
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: ... Operator

Post by Demivec »

Josh wrote:This is related to the calling conventions. CallFunctionFast() uses StdCall and this does not support a variable argument list. CallCFunctionFast() uses cdecl, so it works with CallCFunctionFast() and not with CallFunctionFast().

x64 is different anyway. I am not sure, but I think there is no difference between CallFunctionFast() and CallCFunctionFast() anymore. Which calling conventions are used there, I can't say at the moment.
I agree that the conventions are the same for x64 and it doesn't matter which one is used. However it fails for User_Russian's code, that is it doesn't function as he intended when I compile it. As for the prototype variation it works with the Debugger but doesn't when the Debugger is off.

I'll try and explore this further to figure out what's up. It's all just tinkering to see what may be useful for any future needs I may have in the future.
User_Russian
Addict
Addict
Posts: 1520
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: ... Operator

Post by User_Russian »

For x64.

Code: Select all

Structure ArrI
  i.i[0]
EndStructure

ProcedureC Test(Count, x, y, z)
  Protected *Param.ArrI=@Count+SizeOf(Count)
  Protected i
  
  PrintN("Count = "+Count)
  Print("Parameters: ")
  For i=0 To Count-1
    Print(Str(*Param\i[i])+"  ")
  Next
  
  PrintN(#CRLF$+"----")
EndProcedure
OpenConsole()
CallCFunctionFast(@Test(), 2, 1, 2)
CallCFunctionFast(@Test(), 10, 2, 8, 20, 100, 4, 1234, 10000, 10, 16, 80)
Input()
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: ... Operator

Post by Demivec »

Thanks for posting that update User_Russian. I looked over some documentation for the cdecl callign convention and also fastcall but wasn't sure if I should just modify the start pointer for the parameters on the stack or not.

Here is my update of the similar method using prototypes or CallCFunctionFast():

Code: Select all

Structure ArrI
  i.i[0]
EndStructure

;'Prototype' can also be used if compiling for x64 and will function equally well
PrototypeC.i MultiArgInteger(Count, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0, a7 = 0, a8 = 0, a9 = 0, a10 = 0,
                           a11 = 0, a12 = 0, a13 = 0, a14 = 0, a15 = 0, a16 = 0, a17 = 0, a18 = 0, a19 = 0)

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 
  ProcedureC Test(Count)
CompilerElse
  ProcedureC Test(Count, u1, u2, u3)  ;include 3 unused named parameters ;can also be declared as 'Procedure' both are the same in x64
CompilerEndIf
;ProcedureC Test(Count)
  Protected *Param.ArrI=@Count+SizeOf(Count)
  Protected i
 
  PrintN("Count = "+Count)
  Print("Parameters: ")
  For i=0 To Count-1
    Print(Str(*Param\i[i])+"  ")
  Next
 
  PrintN(#CRLF$+"----")
EndProcedure

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 
  ProcedureC max(Count)
CompilerElse
  ProcedureC max(Count, u1, u2, u3) ;include 3 unused named parameters ;can also be declared as 'Procedure' both are the same in x64
CompilerEndIf
;ProcedureC max(Count)
  Protected *Param.ArrI = @Count + SizeOf(Count)
  Protected mx = *Param\i[0]
  Protected i, s$ = "Params(" + count + "): "
  
  For i = 0 To Count - 1
    If *Param\i[i] > mx: mx = *Param\i[i]: EndIf
    s$ + *Param\i[i] + ", " ;show parameters to demonstrate number of arguements
  Next
  PrintN(s$ + "max value: " + mx)
  ProcedureReturn mx
EndProcedure

Define Display.MultiArgInteger = @Test(), Max_multi.MultiArgInteger = @max()

OpenConsole()
CallCFunctionFast(@Test(), 2, 1, 2)
CallCFunctionFast(@Test(), 10, 2, 8, 20, 100, 4, 1234, 10000, 10, 16, 80)

Display(2, 1, 2)
Display(10, 2, 8, 20, 100, 4, 1234, 10000, 10, 16, 80)
PrintN("max value:" + Max_multi(3, 22, 133, 45))                  ; result --> 133
PrintN("max value:" + Max_multi(6, 90, 88, 12, 45, 677, 211))        ; result --> 677
PrintN("max value:" + Max_multi(19, 33, 25, 233, 85, 11, 231, 34, 64, 5, 10, 330, 22, 15,-5, 90, 550, -2393, 551, 552))  ; result --> 552
The code above includes adaptations to compile correctly for x86 or x64 as needed. Only tested for windows.

I wanted to include an example that had string parameters but ran into a strange bug dealing with the return value not actually being returned. Very strange. If I unravel it I will post a working example with string parameters. The whole purpose of using prototypes is so that other variable types could be used if desired.
Olli
Addict
Addict
Posts: 1201
Joined: Wed May 27, 2020 12:26 pm

Re: ... Operator

Post by Olli »

It is a good recall of UserRussian about C std call.

Just in this context, we must count the parameters and set this count on the first place, what it removes the simple side of this solved problem.

An other tip used by UserRussian :

Code: Select all

Structure x
a.I[0]
EndStructure
It (zero) is shorter than a big number in the place of the index. Interesting...
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: ... Operator

Post by Josh »

Olli wrote:It (zero) is shorter than a big number in the place of the index. Interesting...
If you are familiar with static arrays, it should not be a surprise that a null array is smaller than all other indexes. Zero IS zero and you can place this integer-array anywhere in a structure without changing the structure itself. So you can easily access all following integer-values in the structure and even the values listed above. This is an important standard behavior of static arrays, which makes things much easier.
sorry for my bad english
Olli
Addict
Addict
Posts: 1201
Joined: Wed May 27, 2020 12:26 pm

Re: ... Operator

Post by Olli »

I think UserRussian has found the perfect context to give the best understanding about the power of this tip !


:D


We can control easily the endianness of whatever unitary datas with this.

Code: Select all

Structure Union
   A.A[0]
   U.U[0]
   L.L[0]
   Q.Q[0]
EndStructure

Define *X.Union = AllocateMemory(8)

*X\Q[0] = 1
Debug *X\A[0]
For I = 0 To 3
   Swap *X\A[I], *X\A[7 - I]
Next
Debug Hex(*X\Q[0] )
User avatar
mk-soft
Always Here
Always Here
Posts: 6209
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: ... Operator

Post by mk-soft »

Code: Select all

; Swap Bytes
Procedure.w BSWAP16(value.w)
  !xor eax,eax
  !mov ax, word [p.v_value]
  !rol ax, 8
  ProcedureReturn
EndProcedure

Procedure BSWAP32(value.l)
  !mov eax, dword [p.v_value]
  !bswap eax
  ProcedureReturn
EndProcedure

Procedure.q BSWAP64(value.q)
  
  CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
    !mov rax, qword [p.v_value]
    !bswap rax
  CompilerElse
    !mov edx, dword [p.v_value]
    !mov eax, dword [p.v_value + 4]
    !bswap edx
    !bswap eax
  CompilerEndIf
  ProcedureReturn
  
EndProcedure

lVal.l = $12345678
Debug Hex(BSWAP32(lVal))
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: ... Operator

Post by Mijikai »

Why so focused on parameters isnt one enough?
Olli
Addict
Addict
Posts: 1201
Joined: Wed May 27, 2020 12:26 pm

Re: ... Operator

Post by Olli »

You can use

Code: Select all

! shufps xmm0, xmm0, 0x1B
Post Reply