Video Mode 13

From Uzebox Wiki
Jump to: navigation, search

Mode 13 saves RAM and program memory by storing a pair of pixels in each byte in a tile. This means RAM and flash tiles only take half the amount of space that mode 3 would.

The important thing to understand is how mode 13 extracts the two pixel colour values from a single byte in the main rendering loop. The value of the byte is actually an *index* into a lookup table which contains the two pixel colours.

For the 8 colour mode, the encoding of two pixels looks like this:


Where 'aaa' is the 3-bit value of the first pixel and 'bbb' is the 3-bit value of the second pixel. For example, the encoding for (6, 3) would be:

^^^ ^^^
b=3 a=6

So the stored byte would be 01101100 or 108 in decimal. When the rendering loop needs to get the pixel colour values for this pair, it will look up into the palette look up table at index 108 for the first pixel, and 109 for the second:

encode(6, 3) => index 108
palette[108] => colour 6 [a]
palette[109] => colour 3 [b]

So in a more generalised sense:

encode(a, b) => index
palette[index] => a
palette[index+1] => b

The 8 colour mode encoding specifically sets each index to be an even number, since odd numbers refer to the second pixel in a pair. However, if we change the method of encoding, we can use any index value we want without changing the rendering loop.

For 15 colour mode, the layout of the palette lookup table has been re-arranged to be like this:

0x0, 0x0, 0x1, 0x1, 0x2, 0x2, 0x3, 0x3, 0x4, 0x4, 0x5, 0x5, 0x6, 0x6, 0x7,
0x7, 0x8, 0x8, 0x9, 0x9, 0xA, 0xA, 0xB, 0xB, 0xC, 0xC, 0xD, 0xD, 0xE, 0xE,
0x1, 0x3, 0x5, 0x7, 0x9, 0xB, 0xD, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE, 0x2,
0x5, 0x8, 0xB, 0xE, 0x3, 0x6, 0x9, 0xC, 0x1, 0x4, 0x7, 0xA, 0xD, 0x1, 0x5,
0x9, 0xD, 0x3, 0x7, 0xB, 0x2, 0x6, 0xA, 0xE, 0x4, 0x8, 0xC, 0x2, 0x7, 0xC,
0x3, 0x8, 0xD, 0x4, 0x9, 0xE, 0x5, 0xA, 0x1, 0x6, 0xB, 0x1, 0x7, 0xD, 0x5,
0xB, 0x3, 0x9, 0x2, 0x8, 0xE, 0x6, 0xC, 0x4, 0xA, 0x2, 0x9, 0x1, 0x8, 0x3,
0xA, 0x4, 0xB, 0x5, 0xC, 0x6, 0xD, 0x7, 0xE, 0x7, 0x1, 0x9, 0x3, 0xB, 0x4,
0xC, 0x5, 0xD, 0x6, 0xE, 0x8, 0x2, 0xA, 0x3, 0xC, 0x7, 0x2, 0xB, 0x6, 0x1,
0xA, 0x5, 0xE, 0x9, 0x4, 0xD, 0x8, 0x1, 0xB, 0x7, 0x3, 0xD, 0x9, 0x5, 0x2,
0xC, 0x8, 0x4, 0xE, 0xA, 0x6, 0x2, 0xD, 0xA, 0x7, 0x4, 0x1, 0xC, 0x9, 0x6,
0x3, 0xE, 0xB, 0x8, 0x5, 0x1, 0xD, 0xB, 0x9, 0x7, 0x5, 0x3, 0x2, 0xE, 0xC,
0xA, 0x8, 0x6, 0x4, 0x2, 0x1, 0xE, 0xD, 0xC, 0xB, 0xA, 0x9, 0x8, 0x7, 0x6,
0x5, 0x4, 0x3, 0x1, 0x0, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0, 0x5, 0x0, 0x6, 0x0,
0x7, 0x0, 0x8, 0x0, 0x9, 0x0, 0xA, 0x0, 0xB, 0x0, 0xC, 0x0, 0xD, 0x0, 0xE, 0x0

The layout is special because the sequence contains every combination of 15 pairs exactly once. When a tile's data is being generated by gconvert, for each pair of pixels it encodes, it just needs to search through the table until it finds the relevant index.

So for example, having the pair (6, 3), we search the table for a 6 followed by a 3. In this case, the index where this is valid is index 164. (In the table above, last column, 11th row down). So in a similar fashion:

encode(6, 3) => index 164
palette[164] => colour 6 [a]
palette[165] => colour 3 [b]

The tricky part is when using this format with sprite blitting. It would be too expensive to search through the table each time you need to encode a pixel pair. Instead, sprites are encoded like this:


Where 'aaaa' and 'bbbb' are the 4-bit pixel values for the first and second pixel in the pair. For example, the encoding (6, 3) would look like:

^^^^ a=6
so now:
encode(6, 3) => 0x36 (54 in decimal)

Since the first pixel occupies the lower nibble, and the second pixel occupies the upper nibble, it makes it fairly straightforward to implement a blitter in this format. Now we just need a way to convert at run time between formats so that the sprite blitter can output in a format that the render loop can use.

Fortunately this is quite simple: two additional look up tables are needed:

PaletteStandardToExtendedTable - Converts from sprite format to the format needed by the video mode
PaletteExtendedToStandardTable - Converts from the video mode format to the sprite format

e.g. The blitter does it's work in the sprite format:

blitter(6, 3) => 0x36 (54 in decimal)

And converts to the video mode format using the lookup table:

PaletteStandardToExtendedTable[54] => 0xA4 (164 in decimal)

And the video loop looks up the colour values:

palette[164] => colour 6
palette[165] => colour 3