Backgrounds

Finally, a somewhat interesting chapter... Here we will discuss how to setup the 2 backgounds we will use.

Now, to configure the background settings, we must mess around with some hardware registers. Hardware registers are things that may be accessed by the program to control certain hardware activity. For the DS and GBA, the hardware registers (or in/out ports) may be accessed at memory region 0x4000000 -> 0x4FFFFFF. Not every address is assigned to a hardware register (there aren't that many!). The register we will modify to control the BG behavior is the BGxCNT registers. There are 4 of them, BG0CNT, BG1CNT, BG2CNT, and BG3CNT. They hold the controls for each background.

Here we see the BGxCNT registers picked apart into the different values they hold. Each value uses a certain number of bits in the register. Here is an explanation of what each setting affects.

Priority (0..3)

This is the backgrounds priority. When mixing backgrounds together without setting priority, BG0 will be displayed on top of everything, BG1 will be displayed under BG0, BG2 under BG1, and BG3 under BG2. BUT, if you change the priority value, you can modify this ordering. Lower priority values indicate higher priority. If BG0 has priority 1, and BG1 has priority 0, then BG1 will be displayed on top of BG0.

Character Base Block (0..15)

Now.. one limitation of 16-color backgrounds is that each map entry has a 10 bit number to select a tile. This gives you 1024 tiles to choose from. 1024 tiles can fit in 32KB of memory, but the large VRAM banks are 128KB of memory each! Now, although you can only use 1024 tiles at a time for a single BG, you can control WHERE in the memory the tiles are picked. This is done with the character base block. The character base block adds N * 16KB to the tile base offset. Here is a picture of increasing character base blocks for my GBA game (works just about the same on DS).

On the GBA, you only had 64KB of BG video memory, so the character base block could only be 0->3, bits 4-5 of BGxCNT were reserved. For DS, the two bits were used to extend the character base block selection so you could access much more memory.

In the above picture, we see some interesting things. In block 0, the tiles for the terrain below were stored. We see some remnants from the title screen in the unused bottom half.

In the next block (CCB 1), we have scrolled down 16KB. Now we can see another 16KB of memory. This block is mostly filled with BG map data (the corrupted looking stuff).

In the next block, we are at the 32KB mark (2 * 16KB). Here we see the bottom half of CCB 1, and we see the next region, which is filled with our text characters!

In the last block, the base is set directly to the address of the text characters, this block (block3) was used by one of the BGs in the game to draw text on the screen. I set it up like this so I could simply write an ASCII number into the tile entry, and it would select the associated character.

Mosaic (0..1)

The seventh in the control register enables mosaic effects. The GBA and DS (and snes!) have special hardware that can apply a "mosaic" effect to the image. You can see this effect in Super Mario when you enter doors, or start a level. See below different levels of mosaic (yes I just applied the effect over one of the devkitPro GBA examples).

The actual mosaic levels are set in another register.

LOL, it looks like he dropped his pants!

16/256 Colours (0..1)

This register controls the tile format of the BG. If it is 1, then the BG will display 256-color tiles. We will set this bit to 0 to use 16-color tiles.

Screen Base Block (0..31)

This is another important setting, this is like the Character Base Block, but it selects the Screen Base Block. The Screen Base Block is the offset added to the base of video memory to select where the background will read the map data. The map data for a single 32x32 BG will use 2KB of memory. The Screen Base Block is added to the base of VRAM in 2KB steps. Specifying 5 will use the memory starting at the 10KB mark.

Above we see an image of our VRAM marking each screen base block in red lines. Each block is 2KB wide, so each uses the space that can hold 64 16-color tiles.

We need to select blocks to hold the data for our two backgrounds. We can't use block 0 (our tiles are stored there!). We can use block 1 for storing the "bricks" BG, and we can use block 2 for storing the "gradient" BG. There is alot of extra video memory left that may be used for more complex games.

Display Area Overflow

This bit isn't used in text BGs. For bitmap/affine BGs this bit controls what is seen when outside of the BG area is shown. If it is zero, the area will be transparent. If it is one, the background will loop when you scroll outside its boundaries. Text BGs will always loop, so we don't need to care about this bit for our purposes.

Size (0..3)

The last attribute controls the size of the background.

SizeDimensions
032 by 32 tiles (256x256 pixels)
164 by 32 tiles (512x256 pixels)
232 by 64 tiles (256x512 pixels)
364 by 64 tiles (512x512 pixels)

This table only applies to text backgrounds. When rot/scale backgrounds are used, there are different sizes and behavior of the map data.

When sizes other than 0 are used, the text background will use more than 1 screen base block. Also, dont expect a nice linear block when using sizes other than 0! When using other sizes, the background is composed of multiple 32x32 blocks.

Setting up the Controls

What settings will we write to our BG controls? Well, the BG graphics aren't going to overlap, so we don't have to use the priority bits. The tiles for both BGs are at the very beginning of memory, so we don't have to use a character base block offset. We aren't using Mosaic. We are using 16 color tiles (0 setting). We want a normal 32x32 size bg (also 0). Display Area Overflow isn't used. The only thing we need to set (the only non-zero value) is the Screen Base Block. libnds defines this as BG_MAP_BASE(n); it shifts n to the left 8 bits so it corresponds to bits 8->12 in the BG control.

We will set BG0 to use block 1 and BG1 to use block 2.

// libnds prefixes the register names with REG_
REG_BG0CNT = BG_MAP_BASE(1);
REG_BG1CNT = BG_MAP_BASE(2);

All that explanation for 2 lines??? Well, the explanation of the other stuff may come in handy later. :P

The Tilemap

Before the BGs can display anything, we must fill up their tilemap with data!

Let's add two definitions to point to VRAM where our BG Screen Base Blocks point to.

#define bg0map    ((u16*)BG_MAP_RAM(1))
#define bg1map    ((u16*)BG_MAP_RAM(2))

BG_MAP_RAM is a libnds definition that gets a pointer to VRAM at with a specified screen base block.

Each entry in the BG map is 16 bits wide, let us analyze these bits!

Now, in the 32KB screen block we can fit 1024 16-bit tile entries, the 32x32 tilemap is stored in the block linearly from left->right, top->bottom. Here is a picture of the layout when the tilemap is empty (using our empty tile 0, our backdrop, and a light grid).

We will write data into the tile entries so we get something that looks like this:

The top 6 lines will be filled with brick data, the other tiles will be cleared to zero.

Now, you may be confused how those bricks were made with our single tile! This is when the horizontal flip bit come in handy. Below we see a picture of what the bricks will look like without flipping the tiles.

Pretty ugly huh, now see what happens when we apply the "horizontal flip" flag to every odd column.

Still not quite right, but better. Let's stagger the image by flipping odd columns for even rows, and even columns for odd rows.

That's more like it :P. Let's write the code!

Generating the Bricks' Tilemap

We will start by clearing the entire bricks' tilemap to zero.

    int n;
    for( n = 0; n < 1024; n++ )
        bg0map[n] = 0;

Now let's draw the 32x6 image of bricks.

    int x, y;
    for( x = 0; x < 32; x++ )
    {
        for( y = 0; y < 6; y++ )
        {
            // magical formula to calculate if the tile needs to be flipped.
            int hflip = (x & 1) ^ (y & 1);
            
            // set the tilemap entry
            bg0map[x + y * 32] = tile_brick | (hflip << 10) | (pal_bricks << 12);
        }
    }

See how hflip was shifted left 10 spaces, and pal_bricks 12 spaces. This matches the diagram shown before. We also use the tile index of the brick that we defined in the loading chapter.

Let's see how this looks, add DISPLAY_BG0_ACTIVE to your video mode to enable your shiny new BG.

    videoSetMode( MODE_0_2D | DISPLAY_BG0_ACTIVE );

Compile the project and open the NDS file in no$gba. You should see this image on the top screen.

Whoops, the brick platform is at the top instead of the bottom! Let's fix this by using the scrolling registers.

We have two scroll registers for each bg, they are called BGxHOFS and BGxVOFS where x is replaced by 0->3. They mean BG Horizontal Offset , and BG Vertical Offset. Technically they aren't really offsets of the BGs. They are more like offsets for the display. Look at the image below.

When we move the display to show a part that is outside of our 256x256 background region, the region will loop back to the opposite side and start drawing from there. The faded parts in the picture represent the "looped" parts of the background.

Now, we can solve our problem with the vertical offset register in two ways. We could move the display upwards 144 pixels (offset = -144), or we could move the display down 112 pixels (offset = 112). Both will look exactly the same.

Let's use the 112 option. Add this single line to your setup code.

    REG_BG0VOFS = 112;

Compile the code and run the NDS file in no$gba. You should see this now:

Ah, much better. ;)

The Gradient

We will use BG1 to display the gradient. Start by clearing the map data.

    int n;
    for( n = 0; n < 1024; n++ )
        bg1map[n] = 0;

Next, fill the upper 256x64 region (32x8 tiles) with the gradient tiles. Choose a gradient tile according to the row number, and repeat it for each column.

    int x, y;
    for( x = 0; x < 32; x++ )
    {
        for( y = 0; y < 8; y++ )
        {
            int tile = tile_gradient + y;
            bg1map[ x + y * 32 ] = tile | (pal_gradient << 12);
        }
    }

And enable the background in your videoSetMode call...

    videoSetMode( MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE );

Compile the code and run the game... You should get a ugly looking gradient pasted over your scene.

The next chapter will explain how to blend the gradient onto the backdrop.

Previous: Loading the graphicsContentsNext: Alpha blending