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 > Strange problem : memory being overwritten

#18510 - Shadow - Sun Mar 28, 2004 4:14 am

I've got a tedious problem here. As I was trying out some code, at one point it looked like there was a bug because nothing was happening (I had code to load values into the sprite palette memory and nothing happened at all). After some time, I found where the problem was and I created a small program that would reproduce it. Here is the code :

Code:

static unsigned short palette[ 256 ] =
{
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111
};



int main( void )
{
   unsigned short index = 0;
   unsigned short data[ 16384 ];
   unsigned short * gba_sprite_palette = ( unsigned short * ) 0x5000200;


   for ( index = 0; index < 16384; ++ index )
   {
      data[ index ] = 0x6666;
   }


   for ( index = 0; index < 256; ++ index )
   {
      gba_sprite_palette[ index ] = palette[ index ];
   }


   while ( 1 )
   {
   }
   

   return 0;
}


(Originally, I had the data array in a structure that represented OAM Data, and I was trying to clear it).

The program initializes an array of 16384 16-bit values to 0x6666 and then loads into the sprite palette memory the palette data declared at the top, which consists of 256 times the value 0x1111. At least this is what should happen. What happens is that the values loaded into the palette are not 0x1111 but 0x6666, which makes no sense at all. Now, I found that if I only initialized the first 164 values of the data array, then everything worked correctly. From the 165th value on, palette values started to be corrupted.

I decided to look into the memory using VisualBoy Advance, and I found the 16384 items of data starting somewhere in EWRAM, and "spilling" into IWRAM, completely overwriting the palette data, which was stored there.

The thing is I have no idea what to do. I'm still only learning how the GBA works and how to program it, and I'm afraid I've accidentally bumped into a really difficult problem that is way beyond my level. I would imagine it has something to do with the linkscript and/or gcc. I'm using DevKitAdvance, with the default CRT0.S and lnkscript, and I've never had any problems with it before. I've tried other things such as GNUARM, but I could never get them to work (too many confusing command-line options and things I don't understand...). I don't know how the gcc linkscript commands work, but if I must I will get my hands dirty and learn them.

Any suggestions would be very appreciated. I'm completely blocked by this problem and I'm determined to find a solution.

Thank you very much for your help.

#18514 - Miked0801 - Sun Mar 28, 2004 5:59 am

Dear God, your placing 32K on the stack which lives in IWRAM which is 32K in size:

unsigned short data[ 16384 ];

I'm suprised this thing runs at all. Put the array in slow RAM or not at all...

#18531 - Shadow - Sun Mar 28, 2004 4:33 pm

I see. The thing is I have no idea where data is placed in memory; I just programmed things as I normally would on any other platform. Is code like this really so uncommon? I was only trying to keep a "secondary" storage for OAM Data, which could then be updated to the real OAM Data. Is there any other way to do this? And how do you decide where to put data in memory?

#18534 - poslundc - Sun Mar 28, 2004 4:49 pm

Shadow wrote:
I was only trying to keep a "secondary" storage for OAM Data, which could then be updated to the real OAM Data. Is there any other way to do this?


Sprite data doesn't typically require a double-buffer, since it is easy enough to copy data directly from the ROM into VRAM during VBlank for any sprites that need changing.

If you really want to double-buffer sprite data, either do it for individual sprites in IWRAM (but be frugal with it... a 64x64 16-colour sprite already consumes 2K out of your 32K available for everything else), or put it in EWRAM. The latter is most commonly used as a buffer for decompressing sprites during VDraw.

Also, a tip: "OAM Data" is really a misnomer (from the Pern Project I think) that is liable to confuse a lot of people if you keep using it. Call it sprite VRAM or sprite data. OAM refers strictly to the 128 memory entries that describe the sprite's properties, not their contents.

Quote:
And how do you decide where to put data in memory?


There are a few ways to do this. By default, any non-const variables declared by your program are put into IWRAM.

EWRAM is left empty if your game isn't multiboot, so you can do pretty much whatever you want with it. There's a way to configure DKA so that malloc() will use EWRAM, but I've never done it. It's far simpler, in my opinion, to use tepples' struct-technique in this thread.

Dan.

#18537 - Paul Shirley - Sun Mar 28, 2004 6:50 pm

removed

Last edited by Paul Shirley on Sun Mar 28, 2004 8:53 pm; edited 1 time in total

#18542 - tepples - Sun Mar 28, 2004 7:29 pm

Paul Shirley wrote:
malloc uses EWRAM by default. You don't need to configure anything to use it unless you try directly accessing EWRAM or allocating with the section directive as well.

Last time I tried malloc() in DevKit Advance R4's default link script, malloc() would return addresses that OVERLAPPED the running multiboot program, which is part of why I turned to static allocation. Has this been fixed in DevKit Advance R5 beta 3?

And if you allocate EWRAM using the section directive, you'll get a data allocation rather than a bss allocation. (This results ultimately from a workaround in DKA's link script for Binutils' inability to handle more than one bss section.) Unlike bss allocations, data allocations take up space in your ROM even if they're completely full of zeroes.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#18548 - poslundc - Sun Mar 28, 2004 8:29 pm

Tepples' technique for static allocation merits repetition. This is by far the easiest way to deal with EWRAM if you're comfortable mapping out how it's being used.

tepples wrote:
Static allocation within C looks like this:
Code:

struct EWRAM_GLOBALS
{
  struct Player foo;
  int bar;
  /* stick lots more variables here */
};

#define EW (*(struct EWRAM_GLOBALS *)0x02000000)

Then access variables in EWRAM with EW.foo, EW.bar, etc. If you're making a multiboot program, you will have to change EW so that it definitely comes after the end of your program. I tend to use 0x02030000, which gives me 192 KB for program and constant data and 64 KB for work RAM, and I use most of work RAM as a temporary area for decompressing data.


For some parts of my game where I need to use large chunks of EWRAM - such as my random-level generator, which stores the resultant level in the upper 64K of EWRAM - I'll just grab a static pointer to somewhere in EWRAM (in this case 0x02030000) and go.

Dan.

#18549 - Paul Shirley - Sun Mar 28, 2004 8:43 pm

removed - sick of watching the stupid (mis)lead the blind

Last edited by Paul Shirley on Sun Mar 28, 2004 10:16 pm; edited 2 times in total

#18551 - poslundc - Sun Mar 28, 2004 9:29 pm

Paul Shirley wrote:
removed - sick of dealing with amateurs


Charming.

Dan.

#18555 - Miked0801 - Sun Mar 28, 2004 10:10 pm

Or you can spend a day or two and write your own memAlloc/memCAlloc() which is what we did. Makes i easier to track usage as well.

#18561 - Shadow - Sun Mar 28, 2004 11:03 pm

Thank you for all your answers.

I find that tepples' static allocation technique is very interesting. This is essencially what I was trying to get to... being able to control where I put things. Still, I find it surprising that EWRAM is never used implicitly.

Quote:

Sprite data doesn't typically require a double-buffer, since it is easy enough to copy data directly from the ROM into VRAM during VBlank for any sprites that need changing.

If you really want to double-buffer sprite data, either do it for individual sprites in IWRAM (but be frugal with it... a 64x64 16-colour sprite already consumes 2K out of your 32K available for everything else), or put it in EWRAM. The latter is most commonly used as a buffer for decompressing sprites during VDraw.

Also, a tip: "OAM Data" is really a misnomer (from the Pern Project I think) that is liable to confuse a lot of people if you keep using it. Call it sprite VRAM or sprite data. OAM refers strictly to the 128 memory entries that describe the sprite's properties, not their contents.


It makes sense that sprite data doesn't require a double-buffer; I guess I was trying to create a "uniform" system, where you could update different sections of the GBA with functions such as UpdateSpriteInfo( ), UpdateSpriteData( ), etc... but I can see that it isn't a good idea considering the memory limitations of the GBA.
I also find "OAM Data" to be a strange name ("Object Attribute Memory Data"; what is that supposed to mean?). I would personally call it "ODM" (Object Data Memory), or "OBM" (Object Bitmap Memory), which would make more sense, but I prefer more user friendly names, so I chose Sprite Data (and Sprite Info for OAM). I only used "OAM Data" because I thought that was the more standard name for it, that's all.

Quote:

Or you can spend a day or two and write your own memAlloc/memCAlloc() which is what we did. Makes i easier to track usage as well.


That sounds like an excellent idea. That way, you would have a stack in IWRAM and a heap in EWRAM, and you could use the GBA a bit more like a normal computer, in terms of programming techniques. However, I'm not sure I would know how to write an effecient dynamic allocation function, considering my level, and the problem of choosing the most appropriate algorithm.

#18580 - tepples - Mon Mar 29, 2004 4:22 am

Shadow wrote:
I also find "OAM Data" to be a strange name ("Object Attribute Memory Data"; what is that supposed to mean?). I would personally call it "ODM" (Object Data Memory), or "OBM" (Object Bitmap Memory), which would make more sense, but I prefer more user friendly names, so I chose Sprite Data (and Sprite Info for OAM). I only used "OAM Data" because I thought that was the more standard name for it, that's all.

A commonly understood term around here is "sprite cel VRAM" or the like.

Quote:
However, I'm not sure I would know how to write an effecient dynamic allocation function, considering my level, and the problem of choosing the most appropriate algorithm.

You could try writing an allocator that allocates any of 255 1 KB blocks of EWRAM, the first being reserved for a map of allocated blocks.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#18587 - Miked0801 - Mon Mar 29, 2004 7:13 am

That's a good start - then extend it down to 32 byte chunks and you've got a workable allocator :)