Sonic Battle (GBA) Renderer Series - Rendering on the GBA (overview)

Posted on September 25, 2018

Table of Contents

Welcome to the second article of the Sonic Battle (GBA) Renderer series.


The sacred bible of GBA development is Tonc. It’s too detailed for our purposes though, especially since we aren’t developing for the GBA but only emulating the high-level rendering functionality. This article will distill the relevant parts of the guide. Credit goes entirely to Tonc.

Video hardware

  • 240x160 pixel, 15bit color LCD screen with 59.73 Hz refresh rate (~60 FPS)
  • 3 bitmap modes and 3 tilemap modes, and sprites
  • 4 individual tilemap layers and 128 sprites (objects)
  • Affine transformations (rotate/scale/shear) supported for 2 tilemaps and 32 objects

Video modes

The GBA has 6 modes in total. Only one mode may be active at a time.

Each one has its strengths and weaknesses, and typical use-cases.

Bitmap modes

Bitmap modes are numbers 3, 4, and 5.

They allow us to treat the screen as an array of [screen_width * screen_height] size and to write the colors of individual pixels directly.

Unfortunately the bitmap modes are rather slow. Sonic Battle doesn’t use them during gameplay so I’ll skip them.

Tilemap modes

Modes 0, 1, and 2 are tilemap modes. Sonic Battle uses mode 2 specifically.

Mode 2 can display 2 tilemaps with affine transformations and up to 128 sprites (32 of which support affine transformations).


Tilemaps are large images composed of small “building blocks” called tiles which are contained in a tileset. The high-level idea is universal, so I’ll skip the GBA’s low-level specifics.

Here’s a sample tileset. A grid overlay was added for demonstration purposes. Every cell in the grid is a tile.

(Tileset by Buch)

A tilemap is a list of tile indices, representing a 2D layout of tiles which construct a full image during rendering.

Here’s a sample tilemap constructed using the tileset above. Note that it’s quite small and upscaled for demonstration purposes.

Using tilemaps saves a lot of memory because we can repeat tiles without having to copy their pixels over and over again, at the cost visible patterns. Artists compromise by making a few variations for tiles to break up the visual monotony, but it’s overall an accepted limitation of the medium.

It’s also cheap to modify or animate a tilemap by changing a specific position’s tile index.

Tilemaps are usually reserved for displaying the game environment since they’re typically between 512px by 512px and 1024px by 1024px large on the GBA, which supports 2 to 4 tilemaps depending on the mode.


Sprites on the other hand are small (up to 64px by 64px large), numerous, and support more rendering features than tilemaps. They’re used for dynamic objects. They are also composed of tiles much like tilemaps.

Let’s look for example at this screenshot from the intro cutscene of The Legend of Zelda: The Minish Cap. Princess Zelda is a dynamic entity (she’s walking along the path) so she’s rendered as a sprite. The rest of the objects on the screen are rendered as part of two tilemaps (one contains the tree which is barely visible at the lower left, and the other contains everything else).

Affine transformations

An affine transformation is any combination of (strictly 2D in our case) translation, rotation, scale, and shear, represented by an affine matrix.

They can be applied to sprites as well as tilemaps to modify the default axis-aligned rectangle shape. This capability is the core of the Sonic Battle renderer, and it will be discussed in later articles.

The four basic affine transformations

Draw order

Tilemaps and sprites can be assigned a draw order priority, in which no distinction is made between both types.

Fixed-point math

The GBA has no hardware-accelerated floating point math (for float and double types). It’s all done in software which is too slow for most purposes.

Luckily it’s possible to represent fractional values using only integers, and to operate on them using only integer arithmetic which is much faster.

Standard trigonometry functions like sin and cos can’t be used either because they deal in floats, and also because much better performance can be achieved by using a pre-computed table with interpolation.

Those are very interesting topics which I won’t cover at all unfortunately. But you can read about them in Tonc if you’re curious.

I didn’t write my implementation for the GBA so I could afford to use floats. However because it’s the only meaningful difference between my version of the renderer and the original I might convert mine to fixed-point eventually for authenticity’s sake.

The next article in the series takes a small detour to discuss the palette shifting technique and its many uses in Sonic Battle.