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.

Coding > Awkward OAM problem

#91233 - Edelnutte - Wed Jul 05, 2006 1:38 pm

hi^^

I want to display and move a sprite using own written funtions, but when I want to Update the OAM it Overwrites every Attribute different and messes up my sprite.
Here are the Funtions for Moving and Updating:

Code:
void keyinput(){
     u8 x;
     u8 y;
     if(!(REG_KEYINPUT & KEY_LEFT)) {x=x-1;}
     if(!(REG_KEYINPUT & KEY_RIGHT)) {x=x+1;}
     if(!(REG_KEYINPUT & KEY_A)) {y=y+5;}
     OAMcopy[0].attr1=OBJ_X(x);
     OAMcopy[0].attr0=OBJ_Y(y);
      }


Code:

//UPDATE OAM
void updateOAM(){
     for(i=0;i<128;i++){
                        OAM[i]=OAMcopy[i];}}



Code:

//THE ORIGINAL SPRITE ATTRIBUTES
for(i=0;i<128;i++){
OBJ_BASE_ADR[i]=tiles0_Data[i];}

     
     OAMcopy[0].attr1=(OBJ_256_COLOR);           
     OAMcopy[0].attr0=(OBJ_SIZE(Sprite_16x16));
     OAMcopy[0].attr2=(OBJ_CHAR(0)|OBJ_SQUARE);




I appreciate any help. I'm pretty new and desperate with it.

#91241 - Cearn - Wed Jul 05, 2006 4:18 pm

Edelnutte wrote:
I want to display and move a sprite using own written funtions, but when I want to Update the OAM it Overwrites every Attribute different and messes up my sprite.
Here are the Functions for Moving and Updating:

Code:
void keyinput()
{
     u8 x;
     u8 y;
     if(!(REG_KEYINPUT & KEY_LEFT)) {x=x-1;}
     if(!(REG_KEYINPUT & KEY_RIGHT)) {x=x+1;}
     if(!(REG_KEYINPUT & KEY_A)) {y=y+5;}
     OAMcopy[0].attr1=OBJ_X(x);
     OAMcopy[0].attr0=OBJ_Y(y);
}



The coordinates are only a part of attr0 and attr1, so simply assigning them to the attributes will remove all the other settings. Mask out the bits you want to set and then add (masked!) x/y in there. See here for an example.
Also, don't use bytes as a variable for x as that will only cover half the range of the valid x-coordinate. And you shouldn't use bytes (or halfwords) as variables anyway, because they produce more and slower code than word variables. Use ints, please.

Edelnutte wrote:
Code:

//UPDATE OAM
void updateOAM()
{
    for(i=0;i<128;i++)
        OAM[i]=OAMcopy[i];
}

Unfortunately, struct copies don't work as well under devkitPro r19 as before, some of the alignment rules have changed. If the struct has no 32bit members and you want to do struct-copies, you have to add alignment attributes. See here for details.

Edelnutte wrote:
Code:
//THE ORIGINAL SPRITE ATTRIBUTES
for(i=0;i<128;i++)
    OBJ_BASE_ADR[i]=tiles0_Data[i];


OBJ_BASE_ADR is a void pointer, I'm confident that tiles0_Data isn't. Cast or use memcpy/dma/CpuFastSet for copies.


Last edited by Cearn on Wed Jul 05, 2006 5:42 pm; edited 2 times in total

#91248 - Edelnutte - Wed Jul 05, 2006 5:34 pm

Thank you very much, couldn't try it yet but especially the part under the Keyinput Qoute sounds logic to me

EDIT:
I appended the ALIGN(4) to the struct OBJATTR and it compiles fine But i Still dont get the OAMcopy properly copied to OAM!

BITMAP_OBJ_BASE_ADR has a u16 pointer and In VBA the Tiles are stored correctly.


heres the whole Source, for the first, Keyinput() isnt used I only tried updateOAM but even that gets me the problem


Code:

#include "include/gba.h"
#include "data/frame0.h"

OBJATTR OAMcopy[127] ;
int i;



//CLEAR OAM
void clearOAM(){
for(i=1;i<128;i++){ OAMcopy[i].attr0=OBJ_DISABLE; } }

//UPDATE OAM
void updateOAM(){
     for(i=0;i<128;i++){
                        OAM[i]=OAMcopy[i];}}

//VSYNC FUNC
void WaitForVSync() {
   while (*(vu16*)0x4000006 != 160);}
   


//KEYINPUT FUNC
void keyinput(){
     u8 x;
     u8 y;
     if(!(REG_KEYINPUT & KEY_LEFT)) {x=x-1;}
     if(!(REG_KEYINPUT & KEY_RIGHT)) {x=x+1;}
     if(!(REG_KEYINPUT & KEY_A)) {y=y+5;}
     OAMcopy[0].attr1=OBJ_X(x);
     OAMcopy[0].attr0=OBJ_Y(y);
      }


int main(void){
    //SCREENMODE
SetMode(MODE_1|OBJ_ON|OBJ_1D_MAP);

//SPRITE DATA
for(i=0;i<128;i++){
OBJ_BASE_ADR[i]=tiles0_Data[i];}
for(i=0;i<256;i++){
OBJ_COLORS[i]=tiles0_Palette[i];}


     
     OAMcopy[0].attr1=(OBJ_256_COLOR);           
     OAMcopy[0].attr0=(OBJ_SIZE(Sprite_16x16));
     OAMcopy[0].attr2=(OBJ_CHAR(0)|OBJ_SQUARE); 
     
     clearOAM();
     
     while(1) {
               WaitForVSync();;
               updateOAM(); }
               
               
              return 0; }

#92304 - Cearn - Wed Jul 12, 2006 6:28 pm

Edelnutte wrote:
I appended the ALIGN(4) to the struct OBJATTR and it compiles fine But i Still dont get the OAMcopy properly copied to OAM!

I see what you mean :(

For some reason, devkitPro has become insanely stupid when it comes to struct copies with r19. I thought the alignment would fix it but I guess I was wrong. In the past it would use ldm/stm for small-struct copies (if you don't know what they, just know that they are good), but nowadays it always uses memcpy, which is not only very slow for small structs, but doesn't even work for PAL/VRAM/OAM because of the no-byte-write rule. The only workaround that seems to work is by working with pointers manually:
Code:
void updateOAM()
{
   int ii;
   OBJATTR *dst= OAM, *src= OAMcopy;
   ii=128;
   while(ii--)
      *dst++= *src++;
}
Even this isn't compiled optimally anymore, but at least it still works.

On a side note, please use one of the standard indent styles. Hiding the initial brace behind other code makes code difficult to read, hiding both braces makes it next to impossible.


ETA:
OK, I think I may have found the problem. Or part of it anyway.

It's not just alignment, it's the combination of alignment and the amount of stuff you want to copy. For small enough loops it'll try to unroll the loops (at least for -O2 optimization). Example for a 2 iteration loop:

Code:

   for(ii=0; ii<2; ii++)
      OAM[ii]= OAMcopy[ii];
// compiles to:
   ldr   r3, .L12
   mov   r2, #224
   lsl   r2, r2, #19
   ldmia   r3!, {r0, r1}
   stmia   r2!, {r0, r1}
   ldmia   r3!, {r0, r1}
   stmia   r2!, {r0, r1}

But the full 128 version becomes:
Code:
   mov   r5, #224
   ldr   r6, .L8
   ldr   r4, .L8+4
   lsl   r5, r5, #19
.L2:
   mov   r0, r5
   mov   r1, r4
   mov   r2, #8
   add   r5, r5, #8
   bl   memcpy
   add   r4, r4, #8
   cmp   r5, r6
   bne   .L2

128 iterations would be a little long to unroll, so that's not an option. but even then it should use ldm/stm like it does in the unrolled version (well, partially at least), and not memcpy. In the past I've seen memcpy being called for struct copies if the size was large enough (around 13 words). It could be that GCC looks at the loop-size and says "Nevermind, unrolling that", but also looks at the total copy size (128*8 bytes) and decides to use memcpy, even though that would be highly inappropriate here.

But I could be wrong.

Interestingly, my TILE struct copies still work properly, it's only with OBJATTR that I have problems with. Even defining it as two words doesn't help. I'm very close to calling this an optimization bug.

ETA2: Just for a lark I made OAMcopy const and got this:
Code:

const OBJATTR OAMcopy[128]= {{0}};
for(ii=0; ii<2; ii++)
   OAM[ii]= OAMcopy[ii];
// asm:
   mov   r3, #224
   lsl   r3, r3, #19
   mov   r2, #0
   str   r2, [r3]
   str   r2, [r3, #4]
   ldr   r3, .L12
   ldr   r2, .L12+4
   add   r3, r3, #8
   ldmia   r3!, {r0, r1}
   stmia   r2!, {r0, r1}
Whut? o_O

#93320 - Edelnutte - Tue Jul 18, 2006 4:33 pm

Thanks for the help. It tourned out that actually without Bitmasking some attributes of the OAM were overwritten

#93333 - tepples - Tue Jul 18, 2006 6:13 pm

Cearn wrote:
On a side note, please use one of the standard indent styles. Hiding the initial brace behind other code makes code difficult to read, hiding both braces makes it next to impossible.

Looks like Pico mixed with K&R.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#93345 - Edelnutte - Tue Jul 18, 2006 7:10 pm

tepples wrote:
Cearn wrote:
On a side note, please use one of the standard indent styles. Hiding the initial brace behind other code makes code difficult to read, hiding both braces makes it next to impossible.

Looks like Pico mixed with K&R.


Well I was kind of unpatient when I wrote that stuff simply because I had no problems with the Compiling before I formatted my PC. Usually I do that Pico stuff (without even knowing the name :P )