• Create Account

## Angelscript on Raspberry Pi

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

131 replies to this topic

### #21Andreas Jonsson  Moderators

4454
Like
0Likes
Like

Posted 14 November 2012 - 08:48 AM

The if( sysFunc->takesObjByVal ) condition is an optimization. On iOS and Windows Phone, the argument list doesn't need any modifications, unless one or more of the arguments are of an object type. In your case, just as for ANDROID, the argument list needs to be prepared properly in almost all cases, so you shouldn't check this condition.

No, you'll want to add the verifications for floats within the loop that starts on line 113 in as_callfunc_arm.cpp, i.e.

   for( asUINT n = 0; n < descr->parameterTypes.GetLength(); n++ )
{
if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].IsObjectHandle() && !descr->parameterTypes[n].IsReference() )
{
// leave this as is. Just make sure the code that is there for ANDROID is also included for Raspberry Pi
}
else if( descr->parameterTypes[n].IsFloatType() && (countFloats < 15 || freeSingleFloat != -1) )
{
// add the treatment for floats here
}
else if( descr->parameterTypes[n].IsDoubleType() && countFloats < 14 )
{
// add the treatment for doubles here
}
else
{
// leave this as is. This is where the integers are treated, and the existing code should work. Just make sure the code that is there for ANDROID is also included for Raspberry Pi
}
}


In my pseudo code I included the genericArgs array by mistake. The paramBuffer that is already existing in the as_callfunc_arm.cpp is the one that will hold the integer arguments. You don't need to change it.

Once you have the code to separate the float and double args working, the floatArgs array needs to be passed to the assembler routines where the assembler routines should load the "d" registers with contents of the floatArgs.

Regards,
Andreas
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

### #22Tzarls  Members

1341
Like
0Likes
Like

Posted 14 November 2012 - 09:13 AM

Ok, it´s clear now. So just for ease of development, I could define AS_ANDROID at the beginning, right?

### #23Andreas Jonsson  Moderators

4454
Like
0Likes
Like

Posted 14 November 2012 - 11:35 AM

Yes, I believe you can take that approach. Or, you can simply remove the checks for AS_ANDROID.

When I merge the final solution I'll need to keep conditions specific for Raspberry Pi, but as I said before you don't need to concern yourself with this at the moment. Let's just make it work for RPi first, then I'll figure out how to unify the code for all platforms.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

### #24Tzarls  Members

1341
Like
0Likes
Like

Posted 14 November 2012 - 07:09 PM

The float argument handling part is kind of done. I think it is not reordering the args the way the disassemblies showed, but for now I´m just trying to pass 1 float arg so the lack of reordering doesn´t matter. I´m stuck with this: which opcode should I use to pass the values from the array to the "s" registers? (Assuming that the array is being assigned to r5 at the beginning of the asm code.)

### #25TheChubu  Members

8955
Like
0Likes
Like

Posted 14 November 2012 - 10:21 PM

Just for the record, ARM CPUs do have different registers for integer operations and floating point calculations. I think float registers are also used for the NEON sse-like instruction set.

You can find that kind of information in ARM's site (with nice colors n' all ) I did a presentation about those a few months ago.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

### #26Tzarls  Members

1341
Like
1Likes
Like

Posted 14 November 2012 - 10:40 PM

Yes, we found that info the hard way...disassembly after disassembly after disassembly,,,
Right now I have an asm function that is passed 4 args, the fourth being the pointer to the float array args of the actual system function. I need to know how to store (in asm) the values of the array in the different float registers ("s" registers).

The problem is... I know about 0.000001e-19 % of ASM, so I´m kind of doing this "using the force"...

### #27Andreas Jonsson  Moderators

4454
Like
1Likes
Like

Posted 15 November 2012 - 08:22 AM

This is another use for disassembly. See how the compiler does it, and then copy that.

For example:

void TargetFunc(double d0, double d1, double d2, double d3, double d4, double d5, double d6, double, d7) { printf("",d0,d1,d2,d3,d4,d5,d6,d7); }

void DisassembleMe(asDWORD *floatArgs)
{
TargetFunc(*(double*)&floatArgs[0],
*(double*)&floatArgs[2],
*(double*)&floatArgs[4],
*(double*)&floatArgs[6],
*(double*)&floatArgs[8],
*(double*)&floatArgs[10],
*(double*)&floatArgs[12],
*(double*)&floatArgs[14]);
}


I don't know much about assembly either. I wouldn't dream about attempting to write an assembly function from scratch. All the assembler routines I've written so far is from piecing together sequences I've gathered from disassemblies.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

### #28Tzarls  Members

1341
Like
0Likes
Like

Posted 15 November 2012 - 02:41 PM

Nice! I can´t believe I didn´t think of using the disassemblies... Now the correct float values are reaching the system function correctly, but I´m getting a seg fault afterwards. Should be simple to fix thou since I know exactly where to look.

### #29Tzarls  Members

1341
Like
0Likes
Like

Posted 16 November 2012 - 08:07 AM

I´ve got the code for passing floats/doubles to their corresponding registers in place in working (only for CDECL, but once I´ve got this working the other calling conventions should be easier to implement). Now there seems to be a problem with the way args that don´t fit in the basic registers (s0-s15, d0-d7, r0-r3) are pushed into the stack. I have some questions about your code:

You have avariable named paramSize, which is created like this:

int paramSize = sysFunc->paramSize;

But just before the first big loop (and without having used it) you set paramSize = 0; Is that on purpose?

Also, what´s the reason for this:

// Keep a free location at the beginning
args = &paramBuffer[2];

(Just after the first big loop).

EDIT: Now I see where the stack related problem is. When a float (or double) value can´t find a free "s" or "d" register, it gets pushed into the stack. But if it is in the first three "places" of the stack, the ASM routine tries to push it´s value into one of the "r" registers. Time for more disassembling to find out how the compiler handles this cases!

Edited by Tzarls, 16 November 2012 - 08:21 AM.

### #30Andreas Jonsson  Moderators

4454
Like
1Likes
Like

Posted 16 November 2012 - 12:18 PM

The paramSize gives the number of dwords present in the paramBuffer. As such, the float args that are placed in the registers shouldn't increase the paramSize.

args = &paramBuffer[2], is because in some situations a pair of hidden arguments are passed before the explicit args, e.g. when an object is returned by value, then a pointer to the memory where that object should be initialized is passed before all other args, and when calling an object method, then the object pointer is also passed as a hidden argument before the rest.

Sounds like you're making good progress. I look forward to adding Raspberry Pi as yet another supported platform
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

### #31Tzarls  Members

1341
Like
0Likes
Like

Posted 20 November 2012 - 07:40 AM

CDECL at 90%. I need to make some adjustments for when objects are passed by value, but passing all other args work fine, be it using "r", "s" or "d" registers or the stack. I´ve had to make some important changes to the existing code, so instead or trying to make it fit with the original file, I´d suggest giving it its own... in fact, I´m really curious about how the current implementation is working with Android (or iOS). Has anyone reported native calling conventions really working for those cases?

Edited by Tzarls, 20 November 2012 - 07:42 AM.

### #32Andreas Jonsson  Moderators

4454
Like
0Likes
Like

Posted 20 November 2012 - 09:03 AM

Unfortunately I do not have any development environment to certify that the code is actually working 100% on Android and iOS, but there are plenty of developers that use AngelScript on these platforms and so far I haven't heard of any problems.

If the code for Raspberry Pi becomes sufficiently different I'll probably do as you suggested and keep it separate from the rest.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

### #33Tzarls  Members

1341
Like
0Likes
Like

Posted 20 November 2012 - 03:26 PM

Andreas:

In the code section that copies the passed object to the paramBuffer, you have this:

// Copy the object's memory to the buffer
memcpy(&paramBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());

Since you aren´t performing any kind of bounds checking, this could cause problems if the object to be copied exceeds the capacity of the array... or maybe I´m missing something? In this kind of situations maybe the lib could raise an internal exception when something like this happens.

### #34Tzarls  Members

1341
Like
0Likes
Like

Posted 20 November 2012 - 04:51 PM

More questions:

How do I "trigger" the usage of the different calling conventions? I mean:

switch( callConv )
{
case ICC_CDECL_RETURNINMEM:	 // fall through
case ICC_STDCALL_RETURNINMEM:
std::cout << "armFuncR0.\n";
retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)retPointer);
break;
case ICC_CDECL:	 // fall through
case ICC_STDCALL:
std::cout << "armFunc.\n";
retQW = armFunc(args, paramSize<<2, func);
break;
case ICC_THISCALL:  // fall through
case ICC_CDECL_OBJFIRST:
std::cout << "armFuncR0.\n";
retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)obj);
break;
case ICC_THISCALL_RETURNINMEM:
std::cout << "armFuncR0R1.\n";
....

What kind of functions should i register in order to be able to test every possible case in the above switch? I know the "THISCALL" ones should be called by object methods, but what´s the difference between the "RETURNINMEM" and the other ones? I´ve finished porting the armFuncR0 and I´d like to test it before going on.

Also, is there any "standard" test that the code has to pass in order to safely say that the lib supports a certain platform?

Last night I had dreams with "r" registers....

### #35Andreas Jonsson  Moderators

4454
Like
0Likes
Like

Posted 20 November 2012 - 06:54 PM

When RETURNINMEM is used depends on the ABI. Some classes will always be returned in memory regardless of their size, others will only be returned in memory if their size is bigger than some minimum. Normally the existance of explicitly declared constructors, destructors, and or assignment operator in the class declaration will determine whether the size if of importance or not. Usually these same properties also determine how the objects are passed by value, i.e. whether they are pushed on the stack or if just a reference to the object is pushed on the stack.

In the tests/test_feature project (from the SVN) you'll find the regression test suite that I use to make sure everything works ok with each release. The tests for testing the native calling convention starts on line 325 in main.cpp. I suggest you start with those tests by commenting out all other tests before that. The tests for the native calling convention are designed to test all the aspects of the ABI and will provide you with the tests you need.

// The following tests are designed specifically to test the native calling conventions.
// These are grouped by calling convention and ordered in increasing complexity.
{
// cdecl
if( TestExecute()				 ) goto failed; else printf("-- TestExecute passed\n");
if( TestCDeclReturn::Test()	   ) goto failed; else printf("-- TestCDeclReturn passed\n");
if( TestExecute1Arg()			 ) goto failed; else printf("-- TestExecute1Arg passed\n");
if( TestExecute2Args()			) goto failed; else printf("-- TestExecute2Args passed\n");
if( TestExecute4Args()			) goto failed; else printf("-- TestExecute4Args passed\n");
if( TestExecute4Argsf()		   ) goto failed; else printf("-- TestExecute4Argsf passed\n");
if( TestExecuteMixedArgs()		) goto failed; else printf("-- TestExecuteMixedArgs passed\n");
if( TestExecute32Args()		   ) goto failed; else printf("-- TestExecute32Args passed\n");
if( TestExecute32MixedArgs()	  ) goto failed; else printf("-- TestExecute32MixedArgs passed\n");
if( TestCDecl_Class()			 ) goto failed; else printf("-- TestCDecl_Class passed\n");
if( TestCDecl_ClassA()			) goto failed; else printf("-- TestCDecl_ClassA passed\n");
if( TestCDecl_ClassC()			) goto failed; else printf("-- TestCDecl_ClassC passed\n");
if( TestCDecl_ClassD()			) goto failed; else printf("-- TestCDecl_ClassD passed\n");
if( TestCDecl_ClassK()			) goto failed; else printf("-- TestCDecl_ClassK passed\n");

// cdecl_objlast and cdecl_objfirst
if( TestCDeclObjLast::Test()	  ) goto failed; else printf("-- TestCDeclObjLast passed\n");
if( TestReturnWithCDeclObjFirst() ) goto failed; else printf("-- TestReturnWithCDeclObjFirst passed\n");

// thiscall
if( TestExecuteThis32MixedArgs()  ) goto failed; else printf("-- TestExecuteThis32MixedArgs passed\n");
if( TestThiscallClass()		   ) goto failed; else printf("-- TestThiscallClass passed\n");
if( TestNotComplexThisCall()	  ) goto failed; else printf("-- TestNotComplexThisCall passed\n");
if( TestVirtualMethod()		   ) goto failed; else printf("-- TestVirtualMethod passed\n");
if( TestMultipleInheritance()	 ) goto failed; else printf("-- TestMultipleInheritance passed\n");
if( TestVirtualInheritance()	  ) goto failed; else printf("-- TestVirtualInheritance passed\n");

// stdcall
if( TestStdcall4Args()			) goto failed; else printf("-- TestStdcall4Args passed\n");
if( TestNotComplexStdcall()	   ) goto failed; else printf("-- TestNotComplexStdcall passed\n");
}


Edited by Andreas Jonsson, 20 November 2012 - 06:55 PM.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

### #36Tzarls  Members

1341
Like
0Likes
Like

Posted 21 November 2012 - 07:54 AM

Ok, I´ll run the tests to check how I´m progressing.

Now, one more question.

retQW = armFunc(args, paramSize<<2, func);

I´m having trouble with return values. When the system function returns an int, everything is fine. The problem arises when the function returns a float or a double. From the dissasemblies I´ve found that in those cases the return values are being passed back stored at registers s0 or d0 (float / double) - as opposed to when returning an int, which is stored in r0. I was wondering how do we get that value into retQW. Is it stored there automaitcally by some kind of compiler wizardry? (as a result of the "retQW = " assignment maybe?) If so, then how should we interpret the returning value? Maybe it´s some kind of alignment problem or something..... any ideas would be appreciated.

In other words..... heeeeelp!

### #37Andreas Jonsson  Moderators

4454
Like
0Likes
Like

Posted 21 November 2012 - 01:53 PM

As I mentioned earlier. My suggestion is to do something similar to what is done in as_callfunc_x86.cpp. Simply create a separate assembler routine that copies the d0 register to r0,r1. Let's call it GetReturnedDouble(). Then call this function from CallSystemFunctionNative() after the armFunc() has returned with the following condition:

// If the return is a float value we need to get the value from the d0 register (or just s0 in case of single float)
if( sysFunc->hostReturnFloat )
retQW = GetReturnedDouble();


You can probably get the assembler instructions for doing this by disassemblying the following C function:

asQWORD GetReturnedDouble(double dbl)
{
// Return the binary representation of the double in the qword
return *(asQWORD*)&dbl;
}


Regards,
Andreas
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

### #38Tzarls  Members

1341
Like
0Likes
Like

Posted 21 November 2012 - 02:23 PM

Oh, yes, i had forgotten about your previous post regarding the x86 implementation. I have an idea which might also work - its similar but involves less conditionals and function calling. I just wanted to know if maybe there was some "hidden" compiler feature I didn´t know about... So, I guess I have all I need to get this working.... until the next problem arises! ;)

### #39Tzarls  Members

1341
Like
0Likes
Like

Posted 25 November 2012 - 01:11 PM

Andreas:

I got the returning of floats/doubles in armFunc working. Now I´ve got a problem with returning objects from armFuncR0 (it fails the TestReturnStruct () test in the test bench).

What I´ve found so far:

The system registered object is created (type point) and it is getting the correct values. The problem appears when the engine receives the created object.

CallSytemFunctionNative() reports that sysFunc->hostReturnInMemory = true and sysFunc->hostReturnSize = 1. If I just let the function exit with the usual "return retQW;", the returned struct has all its members with a value of 0. (The same happens for the rect structure).

If bejore returning I do this:

*(asDWORD*)retPointer = (asDWORD)retQW;

Then the returned structure has its first member with the correct value. If I do it this way:

*(asQWORD*)retPointer = retQW;

The the returned structure has its first and second members with the correct values.
Now, in the original asm file, you´ve got:

mov	 r0, r3  // r0 explicitly set

As I understand it, this copies r3 (which holds retPointer) to r0. But then you never use r3 again, and I guess r0 is going to be overwritten with the pointer to the return struct... shouldn´t I end the asm function copying r0 back to where r3 is pointing at? Any ideas?

### #40Andreas Jonsson  Moderators

4454
Like
0Likes
Like

Posted 25 November 2012 - 03:08 PM

First of all. Disassembly the TestPoint() and TestRect() functions so we can see how the ABI expects these structures to be returned.

For both iOS and Android these functions return the value in memory, which is why armFuncR0. However, this may not be the case for Raspberry Pi, which is why we need to see the disassembly.

From what you say that retQW appears to hold the value, then it would seem the structure is returned in the registers rather than in memory. At least with the Point type. In this case the configuration in as_config.h needs to be changed according to the ABI for RPi.

I need to know how the RPi is being identified, i.e. what defines are given by default by gcc when compiling for RPi. Can you run the command "g++ -dM -E as_config.h" and show me the result? It should print the defines that are set after going through the as_config.h macros.

Most likely you'll find the following two in the output:

CDECL_RETURN_SIMPLE_IN_MEMORY
CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2

My guess is that the second needs to be changed to at least 3 for RPi, i.e. simple structs smaller than 3 DWORDs will be returned in registers, others will be returned in memory.

As for the question on the use of R0 in armFuncR0. This is for functions that return a value in memory. In this case the caller must pass a pointer to the memory where the return value should be placed. This pointer is passed as an implicit first argument to the function, i.e. in R0. The called function will take the pointer it receives in R0, and store the return value in that memory spot. As the memory is already populated by the called function, there is nothing to do by the caller once the function returns.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.