• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Tzarls

Angelscript on Raspberry Pi

131 posts in this topic

Yes, you're right. My mistake. Sorry about that.

It should be a double, and it should be d0. The function that will be called is the AddDoubleString() and not the AddFloatString() as I said before.
0

Share this post


Link to post
Share on other sites
I found something. In the as_callfunc_arm_gcc.S file, armFuncObjLast function begins and ends like this:

[CODE]
armFuncObjLast:
stmfd sp!, {r4-r8, r10, lr}

// Magic begins....
.......


blx r4
fstmiad r10, {d0-d7} // Copy contents of registers d0-d10 to the address stored in r10
add sp, sp, r8 // sp += r8 sets sp to its original value

str sp, [r10, #68] // store final sp value - for checks only, should be taken out in finalcode

ldmfd sp!, {r4-r8, r10, pc}

[/CODE]


This doesn´t work for the TestScriptString test. But if I change:

[CODE]
armFuncObjLast:
stmfd sp!, {r4-r8, r10, lr}
[/CODE]

To this:

[CODE]
armFuncObjLast:
stmfd sp!, {r4-r8, r10, r11, lr}
[/CODE]

(With the corresponding change to the epilog) then the test passes! Is this some kind of alignment problem? Edited by Tzarls
0

Share this post


Link to post
Share on other sites
It could be. I see you had originally added r10, to the stmfd instruction. By now adding r11 as well, the instruction would maintain the 8 byte alignment.

Does this change break anything else? Otherwise I'd suggest you leave it like this (just put a comment so we can remember why r11 is being backed up too).
0

Share this post


Link to post
Share on other sites
I guess this makes it clear:

"The ARM-THUMB Procedure Call Standard

The new ATPCS requires that sp always points to 8-byte boundaries. Your assembly language code must preserve 8-byte alignment of the stack at all external interfaces."
0

Share this post


Link to post
Share on other sites
It does.

How many tests are still failing now?

I still haven't had the chance to log in to your environment to test it on my own.
0

Share this post


Link to post
Share on other sites
Well, the string test that was failing now passes, but when performing a general pass I´ve found that I´ve managed to break a test that previously passed (not related to the r11 thing) so I´m trying to fix that before continuing fixing the other failing tests.

EDIT: False alarm, everything that was working before is working now - I must have messed something when testing.
The test that are still failing are:

TestRegisterType
TestVector3
TestStdString Edited by Tzarls
0

Share this post


Link to post
Share on other sites
It's progressing well then. I would think it's just a few minor adjustments left to make.

Where in the tests are the failures happening?
0

Share this post


Link to post
Share on other sites
Well, the test_registertype is failing in the TestIrrTypes() function. The dim2f constructor is called, receives the values with no problem and creates an object that when inspected seems to be ok. But then when the script calls operator+ for the total_res = video_res + video_res; part, the "other" object is ok, but our own members (this->Width and this->Height) have wrong values.

The other tests I haven´t checked yet. I´m facing one bug at a time.
0

Share this post


Link to post
Share on other sites
The operator+ method should most likely be called like this:

this pointer in r0
other pointer in r1
return value will be placed in s0, s1

It might be a good idea to dissembly the operator+ method to confirm that this is actually what the method does.
0

Share this post


Link to post
Share on other sites
Well, operator+ uses the armFuncR0R1 function, and that´s one of the functions I haven´t changed, so that might be the problem.
0

Share this post


Link to post
Share on other sites
That would mean that AngelScript thinks the dim2f is supposed to be returned in memory. I'm not certain this is true.

If it was I think it would have worked as the function is not taking any floats.

I think it is necessary to disassemble the operator. So we can be certain what the function actually looks like.
0

Share this post


Link to post
Share on other sites
Long thread so this might have been mentioned already, but I wanted to make it explicit that passing floating point values in floating point registers (aka hard float) isn't a Raspberry Pi specific thing, and you can in fact get a soft-float (floating point values in int registers) Raspberry Pi OS from http://www.raspberrypi.org/downloads

If you make sure to never pass any floats between your program and pre compiled lib you could also (in theory[sup]tm[/sup]) use the hard float call convention in your programs on Android/iPhone/WinCE by providing the appropriate float abi flags to the compilers.
0

Share this post


Link to post
Share on other sites
Yeah, the thread started out as a Raspberry Pi specific thing, but we soon saw that it was a generic Linux/ARM system.

It's interesting to see that there are multiple variants though. Do you have any idea if there is any macro defined that will allow me to differentiate between the use of soft-floats and hard-floats in as_config.h?
0

Share this post


Link to post
Share on other sites
Sorry for the delay. Here are the disassemblies for operator+:
Code:
[CODE]#include <iostream>
#include <string>
typedef unsigned long asDWORD;
template <class T>
class dimension2d
{
public:
dimension2d() : Width(0), Height(0) {}
dimension2d(const T& width, const T& height) :
Width(width), Height(height) {}
template <class U>
explicit dimension2d(const dimension2d<U>& other) :
Width((T)other.Width), Height((T)other.Height) { }
template <class U>
dimension2d<T>& operator=(const dimension2d<U>& other)
{
Width = (T) other.Width;
Height = (T) other.Height;
return *this;
}
dimension2d<T> operator+(const dimension2d<T>& other) const
{
return dimension2d<T>(Width+other.Width, Height+other.Height);
}
T Width;
T Height;
};
int main()
{
dimension2d<float> video_res(800,600);
dimension2d<float> total_res;
total_res = video_res + video_res;
std::cout << "X: " << total_res.Width << ".\n";
std::cout << "Y: " << total_res.Height << ".\n";
return 0;
}[/CODE]
main:
[CODE]0x8620 push {r11, lr}
0x8624 add r11, sp, #4
0x8628 sub sp, sp, #32
0x862c ldr r3, [pc, #212] ; 0x8708 <main()+232>
0x8630 str r3, [r11, #-12]
0x8634 ldr r3, [pc, #208] ; 0x870c <main()+236>
0x8638 str r3, [r11, #-8]
0x863c sub r1, r11, #20
0x8640 sub r2, r11, #12
0x8644 sub r3, r11, #8
0x8648 mov r0, r1
0x864c mov r1, r2
0x8650 mov r2, r3
0x8654 bl 0x87a0 <dimension2d<float>::dimension2d(float const&, float const&)>
0x8658 sub r3, r11, #28
0x865c mov r0, r3
0x8660 bl 0x87ec <dimension2d<float>::dimension2d()>
0x8664 sub r2, r11, #20
0x8668 sub r3, r11, #20
0x866c mov r0, r2
0x8670 mov r1, r3
0x8674 bl 0x882c <dimension2d<float>::operator+(dimension2d<float> const&) const>
0x8678 vmov.f32 s14, s0
0x867c vmov.f32 s15, s1
0x8680 vstr s14, [r11, #-36] ; 0xffffffdc
0x8684 vstr s15, [r11, #-32] ; 0xffffffe0
0x8688 sub r3, r11, #28
0x868c sub r2, r11, #36 ; 0x24
0x8690 ldm r2, {r0, r1}
0x8694 stm r3, {r0, r1}
0x8698 ldr r0, [pc, #112] ; 0x8710 <main()+240>
0x869c ldr r1, [pc, #112] ; 0x8714 <main()+244>
0x86a0 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x86a4 mov r3, r0
0x86a8 vldr s15, [r11, #-28] ; 0xffffffe4
0x86ac mov r0, r3
0x86b0 vmov.f32 s0, s15
0x86b4 bl 0x8568 <std::ostream::operator<<(float)>
0x86b8 mov r3, r0
0x86bc mov r0, r3
0x86c0 ldr r1, [pc, #80] ; 0x8718 <main()+248>
0x86c4 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x86c8 ldr r0, [pc, #64] ; 0x8710 <main()+240>
0x86cc ldr r1, [pc, #72] ; 0x871c <main()+252>
0x86d0 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x86d4 mov r3, r0
0x86d8 vldr s15, [r11, #-24] ; 0xffffffe8
0x86dc mov r0, r3
0x86e0 vmov.f32 s0, s15
0x86e4 bl 0x8568 <std::ostream::operator<<(float)>
0x86e8 mov r3, r0
0x86ec mov r0, r3
0x86f0 ldr r1, [pc, #32] ; 0x8718 <main()+248>
0x86f4 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x86f8 mov r3, #0
0x86fc mov r0, r3
0x8700 sub sp, r11, #4
0x8704 pop {r11, pc}
0x8708 strbmi r0, [r8], #-0
0x870c ldrmi r0, [r6], #-0
0x8710 ; <UNDEFINED> instruction: 0x00010ab0
0x8714 andeq r8, r0, r12, lsr #18
0x8718 andeq r8, r0, r0, lsr r9
0x871c andeq r8, r0, r4, lsr r9[/CODE]
operator+:
[CODE]
0x882c push {r11, lr}
0x8830 add r11, sp, #4
0x8834 sub sp, sp, #40 ; 0x28
0x8838 str r0, [r11, #-32]
0x883c str r1, [r11, #-36] ; 0x24
0x8840 ldr r3, [r11, #-32]
0x8844 vldr s14, [r3]
0x8848 ldr r3, [r11, #-36] ; 0x24
0x884c vldr s15, [r3]
0x8850 vadd.f32 s15, s14, s15
0x8854 vstr s15, [r11, #-12]
0x8858 ldr r3, [r11, #-32]
0x885c vldr s14, [r3, #4]
0x8860 ldr r3, [r11, #-36] ; 0x24
0x8864 vldr s15, [r3, #4]
0x8868 vadd.f32 s15, s14, s15
0x886c vstr s15, [r11, #-8]
0x8870 sub r1, r11, #20
0x8874 sub r2, r11, #12
0x8878 sub r3, r11, #8
0x887c mov r0, r1
0x8880 mov r1, r2
0x8884 mov r2, r3
0x8888 bl 0x87a0 <dimension2d<float>::dimension2d(float const&, float const&)>
0x888c sub r3, r11, #28
0x8890 sub r2, r11, #20
0x8894 ldm r2, {r0, r1}
0x8898 stm r3, {r0, r1}
0x889c ldr r2, [r11, #-28]
0x88a0 ldr r3, [r11, #-24]
0x88a4 vmov s14, r2
0x88a8 vmov s15, r3
0x88ac vmov.f32 s0, s14
0x88b0 vmov.f32 s1, s15
0x88b4 sub sp, r11, #4
0x88b8 pop {r11, pc}[/CODE]
Can you see where the problem might be?
0

Share this post


Link to post
Share on other sites
Well, the function should truly be called the way I suspected. Now we need to understand why AS decides to use armFuncR0R1 when it should be using armFuncR0.
0

Share this post


Link to post
Share on other sites
Well, there´s something strange going on here... When this line (1276) is executed:

[CODE]r = ExecuteString(engine, "dim2f video_res(800,600);\n"
"dim2f total_res;\n"
"total_res = video_res + video_res;\n"
"assert( total_res.x == 1600 && total_res.y == 1200 );\n"
"ByValue(dim2f(1,2)); \n");[/CODE]

The constructor for video_res and total_res is called (armFuncObjLast), then operator+ is called (armFuncR0R1) but the function receives wrong values (which is to be expected since it isn´t being called by the appropriate asm function) but then opAssign (operator=) is scheduled for being called using armFuncR0, and in fact the asm function is called but for some reason the program never reaches the actual code for operator+.The "blx r4" line is executed, there´s a jump to some other place in the code, then the program returns to the line after "blx r4" and continues to finally display the assert failure.
0

Share this post


Link to post
Share on other sites
I'm out of ideas. I think I'll finally have to log in to your machine and do some debugging on my own to figure out what's going on.
0

Share this post


Link to post
Share on other sites
Where does AS decides the calling convention to be used? Doesn´t it respect the one used when you make the binding?

Anyway, you have the details for logging into my RPi so feel free to check if you can find the problem. Meanwhile I´ll try to fix the other failing tests.
0

Share this post


Link to post
Share on other sites
Part of it happens when the function is registered, in DetectCallingConvention(). The other part happens as a step before compiling the scripts when the full application interface is registered, in PrepareSystemFunction(). Both of these are located in the as_callfunc.cpp file, and the configurations in as_config.h control how they work on each platform.

Regards,
Andreas
0

Share this post


Link to post
Share on other sites
I logged and reviewed the code changes you had made and I see the problem with the dim2f.

The logic you put in the end to treat returned objects that have the flag asOBJ_APP_CLASS_ALLFLOATS. Also needs to be added in the beginning of the function where the condition if( sysFunc->hostReturnInMemory ) is made.

If this condition is true, but the returned object in memory is a non-complex object with asOBJ_APP_CLASS_ALLFLOATS and it is smaller than or equal to 8 bytes, then callConv should not be incremented.

It is this increment that is making the code use armFuncR0R1 instead of just armFuncR0 that it should.

---

EDIT. I didn't make any changes to the code as I didn't have much time to spend on this at the moment. Edited by Andreas Jonsson
1

Share this post


Link to post
Share on other sites
I changed that to:

[CODE]if( sysFunc->hostReturnInMemory )
{
int vv=1;
if ( !( descr->returnType.GetObjectType()->flags & COMPLEX_RETURN_MASK ) &&
( descr->returnType.GetObjectType()->flags & asOBJ_APP_CLASS_ALLFLOATS ) &&
descr->returnType.GetSizeInMemoryBytes() <= 8 )
vv--;

// The return is made in memory
callConv += vv;
}[/CODE]

And the call to operator+ is competed succesfully, using the armFuncR0 function. Then operator= is called, but not the one defined in the class´ definition - nevertheless the assertion passes. Now I´m getting an error in the ByValue(...) function. I´ll investigate a little to see what´s going on here. BTW, ByValue(...) is being called with the armFunc function. Edited by Tzarls
0

Share this post


Link to post
Share on other sites
Without looking at the code I can imagine that ByValue() has a similar need.

By default the configuration in as_config.h will make dim2f be identified as a large class due to having a size larger than 4 bytes and thus AngelScript will pass it as a reference. However, due to being a structure of 2 floats, (asOBJ_APP_CLASS_ALLFLOATS) it should be treated as a simple class and passed in the S registers.

At least, this is how I think this needs to be handled. A disassembly of the ByValue function can confirm that.
0

Share this post


Link to post
Share on other sites
Disassemblies. For this code:

code:

[CODE]
#include <iostream>
#include <string>
typedef unsigned long asDWORD;
template <class T>
class dimension2d
{
public:
dimension2d() : Width(0), Height(0) {}
dimension2d(const T& width, const T& height) :
Width(width), Height(height) {}
template <class U>
explicit dimension2d(const dimension2d<U>& other) :
Width((T)other.Width), Height((T)other.Height) { }
template <class U>
dimension2d<T>& operator=(const dimension2d<U>& other)
{
Width = (T) other.Width;
Height = (T) other.Height;
return *this;
}

dimension2d<T> operator+(const dimension2d<T>& other) const
{
return dimension2d<T>(Width+other.Width, Height+other.Height);
}
T Width;
T Height;
};
void ByValue(dimension2d<float> val)
{
std::cout << "X: " << val.Width << "\n";
std::cout << "Y: " << val.Height << "\n";
}

int main()
{
ByValue(dimension2d<float>(1,2));
return 0;
}[/CODE]
main:
[CODE]0x86b4 push {r11, lr}
0x86b8 add r11, sp, #4
0x86bc sub sp, sp, #16
0x86c0 ldr r3, [pc, #72] ; 0x8710 <main()+92>
0x86c4 str r3, [r11, #-12]
0x86c8 ldr r3, [pc, #68] ; 0x8714 <main()+96>
0x86cc str r3, [r11, #-8]
0x86d0 sub r1, r11, #20
0x86d4 sub r2, r11, #12
0x86d8 sub r3, r11, #8
0x86dc mov r0, r1
0x86e0 mov r1, r2
0x86e4 mov r2, r3
0x86e8 bl 0x8798 <dimension2d<float>::dimension2d(float const&, float const&)>
0x86ec vldr s14, [r11, #-20] ; 0xffffffec
0x86f0 vldr s15, [r11, #-16]
0x86f4 vmov.f32 s0, s14
0x86f8 vmov.f32 s1, s15
0x86fc bl 0x8620 <ByValue(dimension2d<float>)>
0x8700 mov r3, #0
0x8704 mov r0, r3
0x8708 sub sp, r11, #4
0x870c pop {r11, pc}
0x8710 svccc 0x00800000
0x8714 andmi r0, r0, r0[/CODE]
ByValue:
[CODE]0x8620 push {r11, lr}
0x8624 add r11, sp, #4
0x8628 sub sp, sp, #8
0x862c vmov.f32 s14, s0
0x8630 vmov.f32 s15, s1
0x8634 vstr s14, [r11, #-12]
0x8638 vstr s15, [r11, #-8]
0x863c ldr r0, [pc, #96] ; 0x86a4 <ByValue(dimension2d<float>)+132>
0x8640 ldr r1, [pc, #96] ; 0x86a8 <ByValue(dimension2d<float>)+136>
0x8644 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x8648 mov r3, r0
0x864c vldr s15, [r11, #-12]
0x8650 mov r0, r3
0x8654 vmov.f32 s0, s15
0x8658 bl 0x8568 <std::ostream::operator<<(float)>
0x865c mov r3, r0
0x8660 mov r0, r3
0x8664 ldr r1, [pc, #64] ; 0x86ac <ByValue(dimension2d<float>)+140>
0x8668 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x866c ldr r0, [pc, #48] ; 0x86a4 <ByValue(dimension2d<float>)+132>
0x8670 ldr r1, [pc, #56] ; 0x86b0 <ByValue(dimension2d<float>)+144>
0x8674 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x8678 mov r3, r0
0x867c vldr s15, [r11, #-8]
0x8680 mov r0, r3
0x8684 vmov.f32 s0, s15
0x8688 bl 0x8568 <std::ostream::operator<<(float)>
0x868c mov r3, r0
0x8690 mov r0, r3
0x8694 ldr r1, [pc, #16] ; 0x86ac <ByValue(dimension2d<float>)+140>
0x8698 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x869c sub sp, r11, #4
0x86a0 pop {r11, pc}
0x86a4 strdeq r0, [r1], -r0 ; <UNPREDICTABLE>
0x86a8 andeq r8, r0, r4, asr r8
0x86ac andeq r8, r0, r8, asr r8
0x86b0 andeq r8, r0, r12, asr r8[/CODE]

So yes, it is being passed in the "s" registers. So I guess I have to add some logic to the part where the object memory is copied to the buffer to detect if it is an asOBJ_APP_CLASS_ALLFLOATS and if so then I copy the object to the s registers, right? (Taking care, of course, to not overwrite anything that has already been stored in s or d registers - oh god...). What happens if I need more space than registers are available? Use the stack? I guess a disassembly of this case should show me how to proceed in such case.

In this case there´s no need to mess with the "if( sysFunc->hostReturnInMemory )" part, right?
0

Share this post


Link to post
Share on other sites
Isn't it fun to figure out how ABIs work? :)

My guess (hope) is that if there is not enough space to fit the entire object in the available registers, the entire object will be pushed on the stack. It would also be necessary to determine if the two floats needs to be aligned on even registers, i.e. similar to a double.

I believe we have already confirmed that if the type is larger than 8 bytes it will not be passed in the registers, even if it is asOBJ_APP_CLASS_ALLFLOATS, but it may be a good idea to do a disassembly of that case too.






Once everything is working I'll do some refactoring on the code you're implementing now. This special treatment for classes with asOBJ_APP_CLASS_ALLFLOATS should be identified in as_config.h, so these checks whether the type is passed in registers or by reference is done at compile time in PrepareSystemFunction, rather than at runtime. But you don't need to worry about that, I'll make this myself later, and I'll just ask you to test it or perhaps I'll do the testing myself if you allow me to use your RPi remotely for this.
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0