A little background information, that hopefully helps to understand and work around the problems.
When a simple function like MyFunction(A, B, C) is called, it has to know what variables are passed.
Envision each variable as a little paper that is placed on a large stack of papers. They are placed in reverse order. When the function is called, the cpu adds a special paper on top with the return address so it knows where to return when the function exits with a return statement. So in this case, the stack contains
- ReturnAddress - A - B - C - ...
Is makes perfect sense that these little papers with the variables have to come off that stack again.
This is where calling conventions get important. When a C function is called, it expects the caller to get those papers out of the stack. When a PureBasic function is called, it expects the function that is called to get those papers out of the stack. That's why there is Import / ImportC, Procedure / ProcedureC so PureBasic knows who should take care of the cleaning up.
Now, when a structure is passed, usually a reference is passed so the function knows where to find the structure. In the case of some OS X functions, not the reference is passed but the structure itself by value. So if the structure contains two floats like a CGPoint structure and it is passed by value, two of those little papers are placed on the stack while there would only be one if it would be passed by reference.
So to work around this, you can simply pass the structure as if it were multiple parameters and treat the ImportC declaration likewise.
Then there's the part of the structure return. Again, usually it is returned by reference and that is no problem for PureBasic. In this case the function returns one value and that is a reference to the structure.
But there are OS X functions that return the structure itself; described like this on Wikipedia
Some compilers return simple data structures with the length of 2 registers or less in EAX:EDX, and larger structures and class objects requiring special treatment by the exception handler (e.g., a defined constructor, destructor, or assignment) are returned in memory. To pass "in memory", the caller allocates memory and passes a pointer to it as a hidden first parameter; the callee populates the memory and returns the pointer, popping the hidden pointer when returning.
To work around the issue, you can rewrite the ImportC statement - if the return structure is larger than 8 bytes - as if it has an extra initial parameter with a return structure reference but correct the stack pointer so the program won't crash.
For return structures that are not larger than 8 bytes, you need to use AEX:EDX (cpu registers).
So to make OS X functions like ...
CGAffineTransform CGAffineTransformTranslate ( CGAffineTransform t, CGFloat tx, CGFloat ty );
CGPoint CGPointApplyAffineTransform ( CGPoint point, CGAffineTransform t );
work in PureBasic, you have to do this ...
Code: Select all
EnableExplicit
ImportC ""
CGAffineTransformTranslate (*returnStructure, t_a.f, t_b.f, t_c.f, t_d.f, t_tx.f, t_ty.f, tx.f, ty.f)
CGPointApplyAffineTransform (p_x.f, p_y.f, t_a.f, t_b.f, t_c.f, t_d.f, t_tx.f, t_ty.f)
EndImport
Define Tr.CGAffineTransform
Define TrResult.CGAffineTransform
Define P.CGPoint
Define PResult.CGPoint
Tr\a = 1
Tr\b = 0
Tr\c = 0
Tr\d = 1
Tr\tx = 20
Tr\ty = 40
P\x = 5
P\y = 20
; translate Tr by (20, 50) and correct stack pointer (return structure > 8 bytes)
CGAffineTransformTranslate(@TrResult, Tr\a, Tr\b, Tr\c, Tr\d, Tr\tx, Tr\ty, 20, 50)
!sub esp,4
Debug TrResult\ty
; apply transform Tr to point and get result from EAX:EDX (return structure <= 8 bytes)
CGPointApplyAffineTransform(P\x, P\y, Tr\a, Tr\b, Tr\c, Tr\d, Tr\tx, Tr\ty)
!mov [v_PResult],eax
!mov [v_PResult + 4],edx
Debug PResult\x