Fuzebox Hello tiles!
Introduction

In this lesson we will expand on the Hello World example and add some color while we learn about tiles.

Tiling tiles

Graphic displays such as televisions, computer monitors and LCDs all display graphics made up of pixels (picture-elements). Early VGA monitors displayed 640x480 pixels. Nowadays, hi-definition (HD) displays these days can be 1920×1080 or even higher! The Fuzebox isn't powerful enough to drive HD but it can do pretty well for normal TV, at 240x224 pixels. Even though that doesn't sound like much, most original 8-bit video game consoles had the same resolution & the games were pretty cool.

Since the display changes during the course of the game, that would imply that we should store the video data in RAM. If each pixel contained 8-bits (one byte) of data, that would mean we need:

240 pixels wide * 224 pixels high * 1 byte per pixel = 53760 bytes = 52 Kilobytes

to store the video map in memory. Even though that doesn't sound like a lot, it is for a small microcontroller: the total RAM available is only 4K! Since there's no way to cram the video data into RAM we perform a trick called tiling. basically, we say "OK our simple games will have a background or images that don't change much, or repeat. So instead of having each pixel be unique, we'll use a range of predefined tiles". For example, lets look at this screenshot of Super Mario Brothers, the best known 8-bit game

Notice how the bricks at the bottom are just copies of one 'tile'? Also the clouds are cloned-looking and the bricks also look identical. Even tho this image has a resolution of 240x224, there's really only a grid of 15x14 tiles, each one 16x16 pixels big (verify this by counting how many bricks run along the bottom of the screen).

The Fuzebox uses much smaller tiles, because it actually has more processing power than the original NES. It uses 6x8 pixel tiles, for a grid of 40x28 tiles. Assuming that we reuse most of the tiles (and, in general, you'll see that many are) we can cut down the amount of memory needed to store the video graphics. In addition, because the tiles don't change (they just get swapped around), we can store them in the flash memory (which is 64K large) instead of the RAM. Basically, our video memory problems are solved!

Lets say we have 6x8 px tiles and a range of maybe 512 tiles maximum. that means we need

6 *8 * 512 = 24 KB

of flash storage to keep all the tile (which is totally reasonable since we have 64K of flash available), and then in the video RAM we will need:

40 * 28 * 2 byte address = 2240 = 2KB

of RAM memory to store the current map, about half of the 4K we have available. Now we can actually do that!

The first tile

OK go to the hello2 project and open up the hello2.c source file.

#include <stdbool.h>
#include <avr/io.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include "kernel/uzebox.h"
#include "data/fonts.pic.inc"

const char strHello[] PROGMEM ="HELLO TILES FROM THE FUZEBOX!";

//Tile Width=6
//Tile Height=8
const char tiles[] PROGMEM ={
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF		 //tile #0 
};

int main(){
  ClearVram();

  SetTileTable(tiles);
  Fill(0, 0, 40, 28, 0); // fill everything with tile #0

  SetFontTable(fonts);
  Print(7,12,strHello);
  
  while(1);
} 

This file looks a lot like the original hello world, with some additions. The most important part is that we have added a tile!

//Tile Width=6
//Tile Height=8
const char tiles[] PROGMEM ={
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF		 //tile #0 
};

Tiles are stored in program memory, like strings, because they are not modified and there's more room in the flash, hence the const and PROGMEM flags. Each tile is made up of bytes, 6 wide and 8 tall. Each byte of a tile contains one pixel's worth of data, and each pixel/byte contains 8 bits of color data. The color data is made up of red, green and blue components. When mixed, a range of 256 colors are possible. Unfortunately, the three primary colors don't divide equally into 8 so blue gets only 2 bits of color data instead of 3.

7
6
5
4
3
2
1
0
               

So if you wanted to create white light, simply combine the maximum of each color: 0xFF
For black (absence of color), have no color data: 0x00
For the brightest blue, just the top two bits: 0xC0
For the brightest red, just the bottom three bits: 0x07
For turquoise, a mix of blue and green, the top 5 bits: 0xF8
etc.

So, now that you know how the color data is stored, what color is the single tile in the Hello World2 code?

Verify your guess by compiling and uploading the hello world code to the Fuzebox

Laying out tiles

Lets examine the rest of the code

int main(){
  ClearVram();

  SetTileTable(tiles);
  Fill(0, 0, 40, 28, 0); // fill everything with tile #0

  SetFontTable(fonts);
  Print(7,12,strHello);
  
  while(1);
} 
    

Much like before, we have a main procedure that ends in a busy-wait loop. The first line of code clears out th video ram, we've seen that before. The next line is the procedure that sets the Tile Table.

  SetTileTable(tiles);

This is the collection of tiles we will reference for drawing the game background. For example, here is a amateur Mario tile map (graphically displayed)

Look at how nearly every tile in the super mario game is represented here. There are surprisingly few tiles needed to display the game background! We will eventually use a tool that helps create and manage tile tables and maps, but for now we'll just do it by hand to get the experience

Next is a call to a kernel procedure called Fill. Fill (x, y, w, h, tile#) does exactly what it sounds like. It creates a rectangle of tiles, all pointing to one tile image, in this case tile #0, the first (and only) tile in the tile table

  Fill(0, 0, 40, 28, 0); // fill everything with tile #0
Hack the tile table

OK now it is your turn. Edit the tile to change the color and design:

Tile #2

OK, once you have become comfortable with your tile, lets get really wacky and add a second tile!

Replace hello2.c's code with the following:

#include <stdbool.h>
#include <avr/io.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include "kernel/uzebox.h"
#include "data/fonts.pic.inc"

const char strHello[] PROGMEM ="HELLO TILES FROM THE FUZEBOX!";

//Tile Width=6
//Tile Height=8
const char tiles[] PROGMEM ={
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,		 //tile #0 

 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 
 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 
 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 
 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 
 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 
 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 
 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0		 //tile #1
};

int main(){
  ClearVram();

  SetTileTable(tiles);
  Fill(0, 0, 40, 28, 0); // fill everything with tile #0

  SetFontTable(fonts);
  Print(7,12,strHello);
  
  while(1);
} 
    

Note that the second tile goes right after the first one, its just one big chunk of data. What color is the second tile?

Now modify the code so that the background is filled with the second tile (confusingly refered to as tile index #1)

Now add a line right after the Fill(...) procedure call that creates another rectange that will border the text with white (tile #0), like this:

May 17, 2011 20:07