gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Passing arguments to a non inline function...

#140001 - Leevon - Wed Sep 12, 2007 3:51 pm

First of all, sorry for my english guys, but i really can't figure this out alone.

I begin to write C GBA code just few days ago, and I think i'm missing something important, because my program acts quite weird.

I've written a structure to keep all the sprites informations. So the OAM of the sprite is in this structure, but when i try to pass the address of the oam field in order to be modified, this is totally erased from the stack, and then from mem when I call the update function, wich is "static inline" and works just well. This happens even if i just pass it to a totally empty function, but i can pass other types without problems, like u8* or u32*. What I'm missing?

#140008 - sajiimori - Wed Sep 12, 2007 6:46 pm

Hard to say. Can you give a very small code example that illustrates the problem? The example should include the definition of anything that the example uses.

#140009 - strager - Wed Sep 12, 2007 6:46 pm

Leevon wrote:
I've written a structure to keep all the sprites informations. So the OAM of the sprite is in this structure, but when i try to pass the address of the oam field in order to be modified, this is totally erased from the stack, and then from mem when I call the update function, wich is "static inline" and works just well. This happens even if i just pass it to a totally empty function, but i can pass other types without problems, like u8* or u32*. What I'm missing?


Are you doing something like:
Code:
struct { int massive[123456]; } bar;
foo(bar);

If so, you're probably corrupting the stack by shoving that whole array onto the stack. Or something. I'm just suspecting stack corruption. For inline functions, the 'bar' is inlined into the code and doesn't need to be pushed.

Try pushing a pointer to the data you're passing.

Code examples or snippets are good, too.

#140037 - Leevon - Wed Sep 12, 2007 11:03 pm

Here is the structure:

typedef struct Oam_entry
{
u16 attr0;
u16 attr1;
u16 attr2;
s16 fill;
}Oam_entry;


The problem is simple, every non inline function destroy this, even if I pass just the pointer, even if I pass a const pointer, and now i was trying using a global variable: Nothing.

The program works well if I use the Oam_entry only in the main(), the working loop is this:


int main()
{
u8 x = 1, y=1;
u32 i;
Oam_entry oam;

// set memory and oam....

while(1)
{
for(i=0; i<2000; i++) // I know, this sucks
while(REG_VCOUNT < 160);

key_check(key_buff);

movement(&x, &y); // obviously move the sprite

oam_set_pos(&oam, x,y ); // this is inline and works

oam_update ( &oam, 0 ); // this too

}

Whit this, the sprite shows up and moves just like I want. But if I try todo this:

while(1)
{
for(i=0; i<2000; i++) // I know, this sucks
while(REG_VCOUNT < 160);

key_check(key_buff);

movement(&x, &y, &oam); // positioning and update are in the function itself


}

There is no sign of the sprite, and neither the OAM, in fact using VBA I can't see the OAM in ram.
So, I tried doing NOTHING inside the function, and keeping oam_set_pos and oam_update in the main: no sprite again. Then I tried passing the whole Oam_entry, not just the pointer, in something like

oam = movement (&x, &y, oam);

Wich is definitely the worst thing i could ever think, but nothing, it seems that just putting the struct as a parameter screws everything up.

Again, sorry for my english :P Hope i'ts not too messed up :P

#140043 - sajiimori - Thu Sep 13, 2007 12:26 am

I don't see anything wrong with that code, besides the timing loop. After fixing that, post the 'movement' function.

Here's a reasonable VBlank wait:
Code:

while(REG_VCOUNT != 159);
while(REG_VCOUNT == 159);

#140044 - Cearn - Thu Sep 13, 2007 12:34 am

Leevon wrote:
Here is the structure:

Code:
typedef struct Oam_entry
{
  u16 attr0;
  u16 attr1;
  u16 attr2;
  s16 fill;
}Oam_entry;


The problem is simple, every non inline function destroy this, even if I pass just the pointer, even if I pass a const pointer, and now i was trying using a global variable: Nothing.

The program works well if I use the Oam_entry only in the main(), the working loop is this:

Code:
int main()
{
u8 x = 1, y=1;
u32 i;
Oam_entry oam;

// set memory and oam....

 while(1)
    {
      for(i=0; i<2000; i++) // I know, this sucks
       while(REG_VCOUNT < 160);
     
      key_check(key_buff);

      movement(&x, &y); // obviously move the sprite
     
      oam_set_pos(&oam, x,y ); // this is inline and works

      oam_update ( &oam, 0 );   // this too

    }
}

Whit this, the sprite shows up and moves just like I want. But if I try todo this:

Code:
while(1)
    {
      for(i=0; i<2000; i++)      // I know, this sucks
       while(REG_VCOUNT < 160);
     
      key_check(key_buff);

      movement(&x, &y, &oam); // positioning and update are in the function itself
    }

You can preserve code layout through the use of [code] tags. If you're using the 2000 loop because timing seems screwy otherwise, wait for the VBlank to be over before you wait for the next one. Example.

Leevon wrote:
There is no sign of the sprite, and neither the OAM, in fact using VBA I can't see the OAM in ram.
OAM isn't in RAM, it's in OAM :P. OAM is located at 0700:0000; RAM is at 0200:0000 and 0300:0000. If you meant VBA's memory viewer here, then nevermind this point. The variable you've called oam in your code actually has nothing to do with OAM just yet: it's simply a local variable (on the stack). Until you write its contents to OAM at some point, no sprites will show up. I'm assuming this is what oam_update() does, right?

Now you say that in the second code, you do the positioning and update in movement(). I can't see anything wrong in the snippets itself, so are you sure you didn't miss something deeper down? When memory items seem to go wonky, it's often something somewhere else that's causing the problem, so it's important that we see as much as possible. If they're not too large, I'd like to see the initialization of oam, formatting of OAM (the pointer to OAM I mean), oam_set_pos, oam_update() and movement() in both cases.

#140073 - Leevon - Thu Sep 13, 2007 7:13 am

Quote:
I don't see anything wrong with that code, besides the timing loop.


Me too :P I complied the same code (with all the needed changes) for the PC and works just well. Is the GBA that works different. That's the fun part :P

Quote:
OAM isn't in RAM, it's in OAM :P. OAM is located at 0700:0000; RAM is at 0200:0000 and 0300:0000.


Sorry i meant "the ram part used by OAMs" :P He disappear from there when i call oam_update after passing the OAM in a function. This means that the structure is destroyed i think...

Quote:
The variable you've called oam in your code actually has nothing to do with OAM just yet: it's simply a local variable (on the stack). Until you write its contents to OAM at some point, no sprites will show up. I'm assuming this is what oam_update() does, right?


Indeed... Is from the stack that disappears, then from the OAM, the update overwrite the OAM mem with nothing.

This afternoon (here in Italy now is 8:00 AM) i can post all the code in my FTP.

I will post both ROMs and two mains, if you have the time can take a look.

Tank you all guys ^___^

#140092 - Leevon - Thu Sep 13, 2007 2:25 pm

Sorry got a problem with the site. I'll put everything as soon as is solved...

#140099 - Leevon - Thu Sep 13, 2007 4:00 pm

Ok got the link:

http://leevon.altervista.org

You can download sources and ROMs from here.

The program is in the "bg_demo_engine" directory, the working one is "bg_demo.c" and obviously the non working one is "bg_demo_wrong.c".

I've included the other dirs just in case you want to compile it again.
To compile the wrong one just "make wrong" in a devkitARM envroinment.

#140108 - Cearn - Thu Sep 13, 2007 5:42 pm

Thank you for uploading everything, it made things a lot easier.

The problem is in the definition of Oam_entry. Particularly the alignment of the struct in memory. Add '__attribute__((aligned(4)))' to the definition of Oam_entry and everything works again.

<explanation technicality=100%>
As of DevkitARM r19, structs (and unions, etc) aren't automatically aligned to 32bits anymore; Instead, they follow the alignment of its longest member. In the case of Oam_entry, that's halfwords (16bit). Fast struct copies rely on word-alignment, but if word-alignment is not guaranteed GCC willl just use memcpy() internally, which works just as well ... usually. memcpy() will copy as words under certain conditions, otherwise it'll use byte-copies. In this case, because each Oam_entry is 8 bytes long, it's a byte-copy, and byte-writes don't work in the palette, VRAM or OAM.

The solution is to force word-alignment with the aligned attribute on structs that don't have 32bit members. Then it'll use proper struct copies and everything will be fine. Alternatively, copy member for member.

Now, the reason the 'right' version worked is because it wasn't a loose Oam_entry: it was embedded in a Sprite struct, which has a pointer-member (behaviour). Pointers are 32bit, so the struct has 32bit alignment; and because oam is the first member, it'll have guaranteed 32bit alignment as well, so the struct-copy is still good.
</explanation>

So basically, you got caught between some very specific compiler and hardware circumstances. GBA programming is like that sometimes :P

I should also point out that your oam_set_pos function may be problematic. The x field is 9 bits long, not 8. Also, negative x and y values do have some meaning: to have the sprite partially visible on the left or top side. That's why the standard version usually looks like this:

Code:
static inline void oam_set_pos ( Oam_entry* oam, int x, int y)
{
   oam->attr0= (oam->attr0 &~ 0x00FF) | (y & 0x00FF);
   oam->attr1= (oam->attr1 &~ 0x01FF) | (x & 0x01FF);
}

#140114 - Leevon - Thu Sep 13, 2007 6:05 pm

Thank you so much *_________*

For everything *_* from the technical things to the right spell of beahaviour!

Now everything works! The behaviour func now takes a Struct*, so i can just made an array of sprites and move them in a single-line for loop *_*

Thanks so much ^_^