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.

Beginners > Tiling on the gba

#118749 - AdolescentFred - Fri Feb 16, 2007 11:54 am

I've been working through tutorials and examples trying to learn gba programming and I can't get the tile mapping to work. My code (below) is copied and adapted from the PERN Project day five and doesn't seem to work properly.
Code:
#include <gba.h>

u16 tileSet[8*8*2] = {1,1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,1,
                      1,1,1,1,1,1,1,1,
                      2,2,2,2,2,2,2,2,
                      2,2,2,2,2,2,2,2,
                      2,2,2,2,2,2,2,2,
                      2,2,2,2,2,2,2,2,
                      2,2,2,2,2,2,2,2,
                      2,2,2,2,2,2,2,2,
                      2,2,2,2,2,2,2,2,
                      2,2,2,2,2,2,2,2};

u16 map[16*16] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,2,1,1,2,2,1,1,1,2,1,1,1,
                  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                  1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1};

int main() {
  u16* vramMap = (u16*)SCREEN_BASE_BLOCK(32);
  u16* vramTiles = (u16*)CHAR_BASE_BLOCK(0);
 
  int i;
  for (i = 0;i<(8*8*2);i++)
    vramTiles[i] = tileSet[i];
  for (i = 0;i<(16*16);i++)
    vramMap[i] = map[i];
  BG_PALETTE[0] = RGB16(31,0,0);
  BG_PALETTE[1] = RGB16(0,31,0);
  BG_PALETTE[2] = RGB16(0,0,31);

  SetMode(MODE_2 | BG2_ENABLE);

  REG_BG2CNT = BG_256_COLOR | ROTBG_SIZE_128x128 | (32 << 8) | (0 << 2);
}


as I understand it this should show a screen of mainly tile 1 (all green) with some of tile 2 (all blue) in the pattern of map[]. Instead I get this:
[Images not permitted - Click here to view it]

I don't think it's actually reading the map because it looks like that whatever I do to the map (change it or omit it). Anybody know what I am missing?
Thanks in advance
~Fred

#118751 - Cearn - Fri Feb 16, 2007 12:26 pm

The last screenbock is #31, not #32. Use 31 or lower to get them to work.

#118752 - AdolescentFred - Fri Feb 16, 2007 12:32 pm

Thanks, i'd forgotten I'd set that like that. It was originally 31 but I changed it at some point to see if it would make a difference (it didnt at the time) and forgot to change it back. I've reset it now and it's still not displaying properly. Its wrong in a different but similar way now, but it is definately reading the map.

#118756 - Cearn - Fri Feb 16, 2007 2:13 pm

Sorry, should have seen there were more problems earlier.

Both the tiles and the map are declared as u16 arrays. First, make them 'const' arrays. If you have constant data, make sure they go into ROM, not RAM. Second, either change the datatype to bytes, or reformat the data in the arrays so that it'd look like you'd have the current data in bytes.
Since I know that sentence sucks, here's what I mean. Say you have this array:
Code:

const u16 map[8]= { 1, 2, 3, 4, 5, 6, 7, 8 };

Each element of the array is 2 bytes long (little endian), so in memory this array will look like this:
Code:

elements |  [0]  |  [1]  |  [2]  |  [3]  |  [4]  |  [5]  |  [6]  |  [7]  |
memory   | 01 00 | 02 00 | 03 00 | 04 00 | 05 00 | 06 00 | 07 00 | 08 00 |

Note the extra zeroes. For regular background that'd be fine because they use 16bit map entries, but affine backgrounds don't. Those use byte-sized entries and the 00's here are interpreted as extra map entries, which show up as gaps in the map. The same happens for the tiles.

To fix, you could declare both the map and the tiles as 'const u8' arrays, so you 'd get one affine map entry and pixel per byte as you should. However, no there's a second problem, namely that you can't write to VRAM in bytes. To get around this, you can cast the sources to u16-pointers (or better, u32) and copy those (watch out for array-size problems), or simply use a dedicated copier like memcpy or CpuFastSet.

Code:

// First, lets use a better CHAR_BASE_BLOCK that doesn't require casting all the time
#define CHAR_BASE_BLOCK(n) ( (u16*)(0x06000000 + (n)*0x4000) )


const u8 tileSet[8*8*2]= { ... };

// --- Copy using u16 casts ---
u16 *dst= CHAR_BASE_BLOCK(0), *src= (u16*)tileSet;
for(i=0; i<sizeof(tileSet)/2; i++)
    *dst++ = *src++;

// --- or copy using u32 casts ---
u32 *dst= (u32*)CHAR_BASE_BLOCK(0), *src= (u32*)tileSet;
for(i=0; i<sizeof(tileSet)/4; i++)
    *dst++ = *src++;

// --- Or using memcpy ---
memcpy(CHAR_BASE_BLOCK(0), tileSet, sizeof(tileSet));


All do pretty much the same thing, except that the u32 version is 2x faster than the u16 copier, and memcpy about 3x faster. CpuFastSet and DMA-copies are about 7x faster.

Note that all these methods have one potential problem: memory alignment. Multi-byte types must be on start on 'natural' boundaries for those types, otherwise there will be problems. To ensure alignment, use u32 arrays or attach __attribute__((aligned(4))) to the array definition.

See also: tonc : data alignment, tonc: tilemaps (especially in a few hours ^_^ )

#118764 - AdolescentFred - Fri Feb 16, 2007 2:57 pm

Thanks it's all working now when I copy with casts or memcpy(), but not with CpuFastSet, what arguments would I need to give it to use that for the copy?
Thanks
~Fred

#118766 - Cearn - Fri Feb 16, 2007 3:22 pm

AdolescentFred wrote:
Thanks it's all working now when I copy with casts or memcpy(), but not with CpuFastSet, what arguments would I need to give it to use that for the copy?
Thanks
~Fred

memcpy's format is (destination, source, #bytes). CpuFastSet uses (source, destination, #words). Switch the first two arguments and divide the byte-count by 4. Also, Pern's version of CpuFastSet declares the source as a 'void *' type. Change to 'const void *' or start casting the source parameter again.

#118767 - AdolescentFred - Fri Feb 16, 2007 3:39 pm

Thanks once again, I had the first 2 args the wrong way round thats all.

#118787 - Miked0801 - Fri Feb 16, 2007 6:47 pm

One more gotcha with CpuFastCopy - it goes in 32 byte chunks no matter what size you pass in. Pass in 35 bytes and you get either 32 bytes copied or 64, not sure which. Both are bad :)

#118894 - AdolescentFred - Sun Feb 18, 2007 12:05 am

I tested it and it it rounds up; if I tell it to copy 9 words, it copies 16.