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.

Graphics > Teething background problem

#118365 - neilio - Mon Feb 12, 2007 8:53 pm

Yet another query :)

I'm trying to get a first tiled background program working but I can't seem to get the tilemap to work - the character graphics and palette (apparently) loads fine but all I get is a screen full of the 1st character graphic...

I'm hazarding a guess that whatever is causing it is around the //copy in the tile map data area or with the pointer to the screen base block.

Apologies for including long program code in a post but I'm not familiar with any file hosting sites at the moment! As always I'm using PERN headers.

Code:

#include"gba.h"
#include "bob.h"   
#include "bob.c"  //graphics, palette and map data

// Globals to hold the key state
u16 key_curr=0, key_prev=0;

//palette for background tiles
#define BG_PALETTE_MEM (0x05000000)

///main entry point
int main(void)
{
   int i;
   u16 ii;

   //creat pointers to vram
   u16* vramMap = (u16*)SCREEN_BASE_BLOCK(31);
   u16* vramTiles = (u16*)MAP_BASE_BLOCK(0);
   u16* vramBGPalette = (u16*)BG_PALETTE_MEM;   
      
   //copy the tile graphics in character graphics mem, 4bitsperpixel
      for(i = 0; i < 32; i++)
            vramTiles[i] = bobTiles[i]; 

   //copy in the palette, 16 colour palette
   for(i = 0; i < 16; i++)
      vramBGPalette[i] = bobPal[i];

   //copy in the tile map data
      for(i = 0; i < 16*16; i++)
            vramMap[i] = bobMapTiles[i];

   // set up BG0 for a 4bpp 32x32t map, using
   //   using charblock 0 and screenblock 31
   REG_BG0CNT = MAP_BASE_BLOCK(0) | SCREEN_BASE_BLOCK(31) | BG_COLOR_16 | TEXTBG_SIZE_256x256;

   SetMode(MODE_0 | BG0_ENABLE);   

   //main loop
   while(1)
   {
      if(key_released(KEY_A))
      {
         ii=10;
         REG_BG0HOFS=ii;
         REG_BG0VOFS=ii;
      }
   }   
}


bob.c ->

Code:

const unsigned short bobPal[16]=
{
   0x0000,0xF0F0,0x0200,0x0210,0x4000,0x4010,0x4200,0x6318,
   0x4210,0x001F,0x03E0,0x03FF,0x7C00,0x7C1F,0x7FE0,0x7FFF,
};

const unsigned short bobTiles[32]=
{
   0x1000,0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,0x0000,

   0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,
   0x1111,0x1111,0x1111,0x1111,
};

const unsigned short bobMapTiles[16*16]=
{
   0,1,1,1,1,1,1,1,
   1,0,1,1,1,1,1,1,
   1,1,0,1,1,1,1,1,
   1,1,1,0,1,1,1,1,
   1,1,1,1,0,1,1,1,
   1,1,1,1,1,0,1,1,
   1,1,1,1,1,1,0,1,
   1,1,1,1,1,1,1,0,

   0,1,1,1,1,1,1,1,
   1,0,1,1,1,1,1,1,
   1,1,0,1,1,1,1,1,
   1,1,1,0,1,1,1,1,
   1,1,1,1,0,1,1,1,
   1,1,1,1,1,0,1,1,
   1,1,1,1,1,1,0,1,
   1,1,1,1,1,1,1,0,

   0,1,1,1,1,1,1,1,
   1,0,1,1,1,1,1,1,
   1,1,0,1,1,1,1,1,
   1,1,1,0,1,1,1,1,
   1,1,1,1,0,1,1,1,
   1,1,1,1,1,0,1,1,
   1,1,1,1,1,1,0,1,
   1,1,1,1,1,1,1,0,

   0,1,1,1,1,1,1,1,
   1,0,1,1,1,1,1,1,
   1,1,0,1,1,1,1,1,
   1,1,1,0,1,1,1,1,
   1,1,1,1,0,1,1,1,
   1,1,1,1,1,0,1,1,
   1,1,1,1,1,1,0,1,
   1,1,1,1,1,1,1,0,
};


bob.h ->

Code:

#ifndef __BOB__
#define __BOB__

#define bobPalLen 32
extern const unsigned short bobbyPal[16];

#define bobTilesLen 64
extern const unsigned short bobbyTiles[32];

#endif // __BOB__

_________________
I'd like to think this signature is under development, but it isn't.

#118390 - Cearn - Tue Feb 13, 2007 12:52 am

neilio wrote:
Yet another query :)

I'm trying to get a first tiled background program working but I can't seem to get the tilemap to work - the character graphics and palette (apparently) loads fine but all I get is a screen full of the 1st character graphic...

I'm hazarding a guess that whatever is causing it is around the //copy in the tile map data area or with the pointer to the screen base block.


It's this line:
neilio wrote:
Code:
   // set up BG0 for a 4bpp 32x32t map, using
   //   using charblock 0 and screenblock 31
   REG_BG0CNT = MAP_BASE_BLOCK(0) | SCREEN_BASE_BLOCK(31) | BG_COLOR_16 | TEXTBG_SIZE_256x256;


MAP_BASE_BLOCK and SCREEN_BASE_BLOCK are used to get the addresses in VRAM for the tiles and the map, respectively. They return values in the 0x06000000 range. What you put in REGxCNT are just indices, which libpern doesn't have macros for. You could try these from libgba:

Code:
#define BG_TILE_BASE(m)    ((m) << 2)
#define BG_MAP_BASE(m)     ((m) << 8)

use BG_TILE_BASE for the charblock index and BG_MAP_BASE for the screenblock index. Aside from that the code runs fine.

Note that the terms SCREEN and MAP are both used to refer to the place maps are stored, not the (graphics) tiles. Using the name 'MAP_BASE_BLOCK' for tiles instead of the map is misleading to say the least. Also, if you add casting operators to the macros you're using, you wouldn't have to cast them manually when using them later in the code and the infinite loop needs a VBlank synchronizer and key_poll to work properly.

And of course, don't use u16 for local variables, or #include data or code. :P

#118393 - sgeos - Tue Feb 13, 2007 1:07 am

Was beaten in the middle of a response. Here are a few tips to coding style and error reduction.

You shouldn't include *.c files. Compile using something like this:
Code:
gcc -Wall -o target target.c data.c


Hard coding sizes can lead to errors (unless it's automated and the automation works). I generally declare arrays like this:
Code:
type_t myArray[] =
{
   // data here
};
// gives number of bytes
int myArraySize = sizeof(myArray);
// OR gives number of members, depending on what you want
int myArraySize = sizeof(myArray) / sizeof(type_t);

// loop
for (i = 0; i < myArraySize; i++)
   // do something


You could so something like this instead:
Code:
#define MY_ARRAY_SIZE 1234
type_t myArray[MY_ARRAY_SIZE] =
{
   // data here
};

When my code does something I don't expect, first I use VBA's memory viewing features (tile, map, memory, etc) to make sure that things are loading properly. I also use the I/O viewer to make sure that registers are being set properly. I'll pull out gbatek for reference if need be. (Usually the case.)

-Brendan

#118419 - HyperHacker - Tue Feb 13, 2007 8:29 am

What is the advantage to passing each .c file to the compiler separately instead of #including them?
_________________
I'm a PSP hacker now, but I still <3 DS.

#118424 - Diddl - Tue Feb 13, 2007 11:35 am

HyperHacker wrote:
What is the advantage to passing each .c file to the compiler separately instead of #including them?


including .c files is same than packing all together in a big .c file.


if a project grows bigger and bigger, it will be more and more difficult to understand this or to find any bugs and so on.

so it is better to split a code in many small parts. each part (.c file) does a simple task and could tested stand alone. and of course each part maybe can used in another project again (without testing and without splitting/changing).

and of course, if you use any .c file in nearly every project, you can make a precompiled library (.a file) from it, like this libfat and this libnds for example ... ;)

#118430 - sgeos - Tue Feb 13, 2007 1:00 pm

Without modular code, it is hard to make large projects. I'd hate to see 50,000 or 500,000 lines of code in a single file.

Large projects have many modules. Let's say 200, just so that we have number. If you were to include all of the other modules in main.c, you would have something that looks like this:
Code:
#include <gba.h>
#include "everything_else_i_need.h"
#include "my_module_0.c"
#include "my_module_1.c"
// ... much later ...
#include "my_module_199.c"

int main(void)
{
  // do something
}

Including 200 files is goofy. I guess it would work, although it is odd and nobody programs like that. Keep in mind that you'll have to rebuild everything- every module, every time you make a single change, because they are one big bundle. With large projects, this can take time.

If you do modularize, but include *.c files in different modules, there is a good chance you'll end up with two copys of the data. #include "something.file" basically means, open "something.file", select all, copy, and then paste it all into this file.

-Brendan

#118433 - keldon - Tue Feb 13, 2007 1:23 pm

Let's just begin by saying it is the wrong way.

#118438 - sgeos - Tue Feb 13, 2007 1:43 pm

keldon wrote:
Let's just begin by saying it is the wrong way.

I hate it when people don't give reasons. Why?

-Brendan

#118466 - tepples - Tue Feb 13, 2007 7:23 pm

sgeos wrote:
Without modular code, it is hard to make large projects.

sgeos wrote:
I hate it when people don't give reasons. Why?

Because you already did.

Also, there comes a time when understanding the reasons behind best practices is useful to a beginner, but this comes at least weeks after the time when the beginner needs to learn best practices. Beginners may have trouble understanding the best practices themselves, let alone the reasons. For instance, when you learned silent "e" in elementary school, did the teacher tell you exactly why it was silent? (Latin feminine ending a becomes French feminine ending e which was once pronounced, but languages change over generations, and final -e became dropped from pronunciation due to the inherent laziness in language acquisition. Use with English long vowels came as an analogy to use with Norman French long vowels. The reasons behind this, in turn, involve a history of the Norman Conquest.)
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#118483 - Miked0801 - Tue Feb 13, 2007 9:42 pm

I doubt my teacher even knew that. I love it when I find stuff like this :)

#118491 - HyperHacker - Tue Feb 13, 2007 11:15 pm

Diddl wrote:
HyperHacker wrote:
What is the advantage to passing each .c file to the compiler separately instead of #including them?


including .c files is same than packing all together in a big .c file.


if a project grows bigger and bigger, it will be more and more difficult to understand this or to find any bugs and so on.

so it is better to split a code in many small parts. each part (.c file) does a simple task and could tested stand alone. and of course each part maybe can used in another project again (without testing and without splitting/changing).

and of course, if you use any .c file in nearly every project, you can make a precompiled library (.a file) from it, like this libfat and this libnds for example ... ;)

Yeah, I know what #include does, but the code is still in separate files. GCC still tells me which file an error occurrs in, I can share the same file between multiple projects, etc... I fail to see how it's at all different. Do some compilers not like it? Is it slower?
_________________
I'm a PSP hacker now, but I still <3 DS.

#118500 - keldon - Tue Feb 13, 2007 11:52 pm

Like someone else said, include is telling the compiler to copy and paste the code from that file into this file however it is written. If you include the file twice then the code is copy and pasted twice. If you edit any one of the files then the entire project has to be recompiled. So yes it is slower; much much much slower.

Also you are likely to encounter many problems working in this way! It eventually becomes impractical to include .c files.

#118506 - HyperHacker - Wed Feb 14, 2007 12:23 am

What kind of problems? And how could I avoid recompiling everything else after one file changes? It's also not exactly difficult to avoid including a file twice.
_________________
I'm a PSP hacker now, but I still <3 DS.

#118512 - tepples - Wed Feb 14, 2007 1:01 am

One kind of problem is the "multiple definition" error.

HyperHacker wrote:
And how could I avoid recompiling everything else after one file changes?

A .h file contains only macros, type definitions, enumerated constant definitions, extern variable declarations, function prototypes, and static inline functions. A .c file #includes only .h files. If you use arm-eabi-gcc -c to recompile one .c file into a .o file, then you can link all the .o files together into a .exe without having to recompile all the .c files.

GNU Make allows you to write "makefiles", or definitions of what files are created from other files and how to rebuild them, and automatically calls the programs you specify to rebuild each derived file (e.g. *.o, *.elf, *.gba, *.gbfs) when a file it depends on (e.g. *.c, *.h, *.chr) changes.

HyperHacker wrote:
It's also not exactly difficult to avoid including a file twice.

Unless you have files that include other files, which in turn include other files.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#118520 - sgeos - Wed Feb 14, 2007 1:31 am

tepples wrote:
Also, there comes a time when understanding the reasons behind best practices is useful to a beginner, but this comes at least weeks after the time when the beginner needs to learn best practices.

I know. I operate on the theory that it is better to give an incomprehensible answer, than something like "because it's wrong". The short answer seems to imply that "you wouldn't get it, so I won't tell you". Sometimes this is case, sometimes it isn't. I think that making assumptions like this is wrong because the person you are talking might actually be receptive to the information; they may never have had a chance to hear it before.

If you don't want tell someone the right way to do things because you don't have enough time, fine. In that case I think that it would be best to say "There are a bunch of good reasons not to do things this way, but I don't have time to explain them now. ... is the correct way of doing things."

HyperHacker wrote:
What kind of problems?

As tepples said, multiple definition. If you only include *.c files in main, I don't think that you will run into this problem. Once projects start growing, and modules start to depend on other modules, you'll need to pass information between them. All of that information should be in the module itself. "I need to be able to send messages, call the random number generator and spawn items."

If you include that information in the modules, and include *.c files in main, you'll bump into multiple definition errors. If you use the order of definition in main to imply dependencies, you'll have to use the same ordering in future project. This is a bit of a hassle for you, but there is much larger problem with this- it is a great hastle for everyone else.

If you expect me to be able to read your mind, I'm not going to hire you; nobody else will either. In large projects, you deal with multiple people. Other people need to be able to understand what you write. If you get hit by a car a month before the game masters, somebody needs to finish what you are doing.

Quote:
And how could I avoid recompiling everything else after one file changes?

By compiling *.c separately files and then linking them. Ie, by not including *.c files. If you include everything, you copy and paste everything into one big file, compile it. You don't need to link because you are compiling every all at once. Every time. No matter what.

Quote:
It's also not exactly difficult to avoid including a file twice.

It's not hard to keep small projects organized. As they grow, things get more difficult. Even if you can sort out dependencies and avoid including the same file twice, the project structure will be alien to others.

And it will require complete rebuild after every change. And you'll need to include everything in main. The number of files included in main.c will just keep growing to the point where it is silly.

-Brendan

#118612 - neilio - Wed Feb 14, 2007 5:49 pm

Cearn->

Thanks for pointing out the most vital problem, pretty stupid trying to put a memory address pointer instead of an indice into a register! I'll be sure not to do that again.

I've changed things so I'm using SCREEN/TILE, not MAP/TILE, should be easier to follow.

Which macros were the ones you said about casting? Casting's something I need to learn about :P

Vblank synchronizer/key poll put back in, deleted them for no apparent reason!

Sgeos->

I'm now using sizeof(), makes things easier to manage!


Thanks for pointing me in the right direction of good practices too :) background works great now!
_________________
I'd like to think this signature is under development, but it isn't.

#118616 - gauauu - Wed Feb 14, 2007 6:12 pm

neilio wrote:
...pretty stupid trying to put a memory address pointer instead of an indice into a register!


Not to pick nits, but just for your sake in the future, the singular of indices is index, not indice. Pretty understandable mistake, though :)

#118617 - Cearn - Wed Feb 14, 2007 6:17 pm

neilio wrote:

Which macros were the ones you said about casting? Casting's something I need to learn about :P

These:
Code:

#define CHAR_BASE_BLOCK(n)         (((n)*0x4000)+0x6000000)
#define SCREEN_BASE_BLOCK(n)       (((n)*0x800)+0x6000000)
#define BG_PALETTE_MEM              (0x05000000)

still require casts when using them (u16*)BG_PALETTE_MEM and such instead of just BG_PALETTE_MEM.

These macros do not require the user to use explicit casts:
Code:

#define CHAR_BASE_BLOCK(n)         ( (u16*)(0x06000000+(n)*0x4000))
#define SCREEN_BASE_BLOCK(n)       ( (u16*)(0x06000000+(n)*0x800))
#define BG_PALETTE_MEM              ((u16*)0x05000000)


You can also map matrices over VRAM like this:
Code:

typedef u16         SCREENBLOCK[1024];
#define se_mem      ((SCREENBLOCK*)0x06000000)

// Copy bobMapTiles to Screenblock 31:
for(ii=0; ii<sizeof(bobMapTiles); ii++)
    se_mem[31][ii]= bobMapTiles[ii];

Tonc's code uses se_mem for screenblocks and tile_mem for charblocks. The nice thing about the latter is that the tile indices will be the same as those in the screen entries and attr2 of objects, which makes it easier to see what goes where. Besides, TILE copies much faster than u16 copies, but then so it almost every copy method.

There's also a macro in libgba called MAP which is a triple indexed array: MAP[sbb][ty][tx] is screen-entry ([tx, ty) in block sbb. Things like these make locating stuff much easier.

#118702 - neilio - Thu Feb 15, 2007 9:39 pm

Thanks, that helps to make things a bit more managable with the code. Run into a couple more problems but I'll save them for another thread since they're not related to backgrounds!
_________________
I'd like to think this signature is under development, but it isn't.

#118915 - HyperHacker - Sun Feb 18, 2007 5:42 am

tepples wrote:
One kind of problem is the "multiple definition" error.

I generally avoid that by wrapping in #ifnded:

#ifndef FILE_C_INCLUDED
#define FILE_C_INCLUDED
//code goes here
#endif

No matter how many times you include this file, it's only processed once, unless you do something silly like undefine FILE_C_INCLUDED.

HyperHacker wrote:
And how could I avoid recompiling everything else after one file changes?

A .h file contains only macros, type definitions, enumerated constant definitions, extern variable declarations, function prototypes, and static inline functions. A .c file #includes only .h files. If you use arm-eabi-gcc -c to recompile one .c file into a .o file, then you can link all the .o files together into a .exe without having to recompile all the .c files.

GNU Make allows you to write "makefiles", or definitions of what files are created from other files and how to rebuild them, and automatically calls the programs you specify to rebuild each derived file (e.g. *.o, *.elf, *.gba, *.gbfs) when a file it depends on (e.g. *.c, *.h, *.chr) changes.[/quote]
So what you're saying is if each source file is compiled to its own object file, there will never be a need to recompile one source file that hasn't changed just because another has? That makes sense, and I suppose if compiling was taking a long time, I could exploit this by storing somewhere the date and time each file was last compiled, and only recompiling if the last modification date is later than this. So far, though, I don't think I've had anything take more than 10 seconds to compile, even with tens of thousands of lines. I'd end up recompiling them all anyway, because just typing "make" is easier than "make file.c".


Bit off topic?
_________________
I'm a PSP hacker now, but I still <3 DS.

#118938 - sgeos - Sun Feb 18, 2007 11:31 am

make automatically pays attention to dates for you.

-Brendan

#118990 - wintermute - Sun Feb 18, 2007 11:49 pm

Not only that but the handy devkitPro makefiles autogenerate dependencies on the fly. This means that if headers used by a particular .c/.cpp file change then that file will be rebuilt.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog

#119018 - HyperHacker - Mon Feb 19, 2007 3:40 am

Nice. Where does this date info get stored?
_________________
I'm a PSP hacker now, but I still <3 DS.

#119024 - gauauu - Mon Feb 19, 2007 4:05 am

It just compares the last modified dates (stored by the operating system) of the result file and the dependancy files. For example, in compiling a C file to a obj file, it compares the dates of the C file and the obj file. If the obj file is the most recently modified, then it knows it is up to date. If the C file was modified more recently, it can tell that it needs to regenerate the obj file.

#119060 - sgeos - Mon Feb 19, 2007 12:10 pm

The date info is stored with each file. Read the previous post for more information.

-Brendan