Tiles and Extended Rotation Background on one screen
From DSWiki
OK, I write this because it took me some time doing some freelance writing job and chatting with LunarC (Thanks very much again!) to figure this one out.
I was working on the sampling keyboard program for the DS and I found it a convenient way to draw the keyboard using tiles. Tiles are nice for various things. A good source of info on tiles is http://www.dspassme.com/programmers_guide/tutorial/using_tiles.html . The reason for using them was that I wanted to give visual feedback for key presses, so I decided to make the keys switch to a different color while pressed. This can be easily done with tiles by changing the palettes for the tiles in question. But this is not about the details of tiles anyway, so I might just get to the point.
Do you know how the VRAM of the DS is structured? And do you know about modes? You better should, so read it up here: http://www.drunkencoders.com/documents/DS/ndslib.htm#_VRAM Of course you should know the structure of a DS program since I will only provide code snippets here.
And another thing you should have looked into is Extended rotation backgrounds, which are introduced by doublec here: http://www.double.co.nz/nintendo_ds/nds_develop10.html
Let's start out in our main routine by defining the right mode and setting the VRAM banks:
videoSetMode(MODE_5_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG2_ACTIVE); vramSetMainBanks(VRAM_A_MAIN_BG_0x6000000, VRAM_B_MAIN_BG_0x6020000, VRAM_C_SUB_BG_0x6200000 , VRAM_D_LCD);
For the main screen, we chose mode 5. This mode provides text backgrounds (which is just another name for tile backgrounds) and Extended rotation backgrounds. We enable BG0 and BG2, where 0 is a tile background and 2 is an ERB (I'll abbreviate extended rotation backgrounds from now on.)
OK, not we set up the properties of these two backgrounds by writing to their control registers BG0_CR and BG2_CR respectively. Info on control registers is here: http://www.dspassme.com/programmers_guide/tutorial/registers.html#bg0_cr
BG0_CR = BG_COLOR_16 | BG_32x32 | BG_TILE_BASE(0) | BG_MAP_BASE(8);
Whaaa, what's that! Well, we say that we want
- 16 color palette mode
- 32x32 tiles (which fills the screen and is vertically a bit bigger than we need but who cares?)
- our map at map block 8
- our tile data at tile block 0
Blocks?! Yes, blocks. VRAM is divided into them. Different kinds of data that comes into VRAM has different block sizes:
- map blocks are 2k
- tile blocks are 16k
- bitmap blocks are 16k
By setting the tile block to 0 and the map block to 8, we store map and tiles in the beginning of our VRAM bank. But beware! Your tile data may not exceed 16k since map data starts at 8*2k=16k. If it does, you have to move the blocks after the map further forward to not overwrite them. But remember that the maximum map block is 31.
This is a good hint in general: Don't try to use the same RAM areas twice for different purposes. You can't. This was my error at first, too! Of course you wouldn't be so stupid to do that on purpose, but it happens accidentially since the true adresses are (luckily) hidden behind their defined names.
OK, now we know about blocks and we have our map- and tile data positions set. Now for BG2:
BG2_CR = BG_BMP16_256x256 | BG_BMP_BASE(2);
Now this can't shock us any more, can it? We just set the ERB to be 256x256 16 bit color pixels and to start at the second bitmap block, i.e. at vram_a + 32k.
Then we do the usual setup for ERBs:
BG2_CR = BG_BMP16_256x256 | BG_BMP_BASE(2); BG2_XDX = 1 << 8; BG2_XDY = 0; BG2_YDX = 0; BG2_YDY = 1 << 8; BG2_CY = 0; BG2_CX = 0;
All this stuff is explained nicely in doublec's tutorial. Well, that's about it! Now we just have to copy our tile data to the correct memory locations by doing this:
dmaCopy((uint16*)keyboard_Palette, (uint16*)BG_PALETTE, 32); dmaCopy((uint16*)keyboard_Tiles, (uint16*)CHAR_BASE_BLOCK(0), 736);
keyboard_Palette and keyboard_Tiles point to the palette and tile data of my keyboard. Now the set-up is complete. You can draw to the ERB by:
((uint16*)BG_BMP_RAM(2))[SCREEN_WIDTH*y+x] = RGB15(r,g,b) | BIT(15);
And you can draw tiles with:
((uint16*)SCREEN_BASE_BLOCK(8))[32*y+x] = keyboard_Map[map_width*my+mx];
Where keyboard_Map points to my map data. Sorry, I was too lazy to replace keyboard_Map by something more general like "your_custom_cool_map".
OK, now you hopefully know more. For questions, corrections and general rants you can contact me on #dsdev in EFnet when I'm there (I'm _0 558 xtob) or by mail: me@<thisdomain>
Bye, Tob

