Checkpoint #1: Draw the graphics stored in a cartridge
Last updated:
You know what pattern tables are, where they live in the ROM, and how a tile's bytes turn into pixels. Now let's put a whole pattern table on screen.
The easiest way to do that is rendering it into an image file.
PPM, the easiest format
We need to write an actual image file, and the least painful one to produce by hand is PPM (portable pixmap, part of the Netpbm family alongside PGM for grayscale and PBM for black-and-white).
There's no library to pull in: a PPM is a tiny text header followed by pixel
values. The ASCII variant (P3) looks like this:
P3
4 2
255
255 0 0 0 255 0 0 0 255 255 255 255
0 0 0 0 0 0 0 0 0 0 0 0
P3— the magic, "ASCII color pixmap".4 2— width and height in pixels.255— the largest value a channel can take.- then one
R G Btriple per pixel, left to right, top to bottom.
That's a 4×2 image: a red, green, blue and white pixel on top, a black row
below. There's also P6, the same thing with the pixel data as raw bytes
instead of ASCII — smaller files, write that once the ASCII version works.
Notice how this format is just plaintext (not binary), and how 1 or more whitespaces are used as separators between the colors. That's why it's so friendly.
We're still in grayscale (color comes later), so map the four pixel values to four gray triples.
If RGB is new to you: each pixel is three numbers — red, green, blue — each from 0 to 255. When all three are equal you get a shade of gray, from
0 0 0(black) up to255 255 255(white).
So pick four evenly-spaced grays:
0→255 255 255(white)1→170 170 1702→85 85 853→0 0 0(black)
The goal, and the obstacle
We want the table laid out as a 16×16 grid of tiles, like the images in the previous chapter: 256 tiles, 8 pixels each, so a 128×128 image.
The obstacle is interleaving. PPM is written one full pixel row at a time, left to right, top to bottom. But tiles are stored as a strip, one complete tile after another. So the image's first pixel row isn't the first tile — it's row 0 of tile 0, then row 0 of tile 1, … up to row 0 of tile 15, then the image's second row is row 1 of those same 16 tiles, and so on. To emit the file in order you'd have to hop between 16 tiles per scanline. That bookkeeping is where it gets error-prone.
Build up to it
Don't fight the interleaving head-on. Add it last, one step at a time, so a bug has only one new thing it could be:
One tile. Render a single 8×8 tile to an 8×8 PPM. No layout problem at all — you're just checking your bitplane decode produces the right pixels.
A single column. Stack tiles vertically: an 8-wide,
8·N-tall image. This is the case worth noticing — when the image is one tile wide, a pixel row is a tile row, so writing the file in order is the same as walking the tiles in order. No interleaving yet, and you've confirmed you can place many tiles.More columns. Now go to the real grid. This is the step that introduces interleaving, and the clean way to handle it is to stop writing pixels straight to the file. Decode into an in-memory framebuffer — a flat
width × heightarray — and blit each tile into its(x, y)slot, then dump the whole buffer at the end. With random-access writes the strip-vs-grid mismatch disappears; serializing to PPM becomes a dumb loop that doesn't care how the pixels got there.
Doing it in this order means by the time you hit the grid, the only thing left to get wrong is the tile placement arithmetic — everything before it is already proven. It's slower to write but it makes the inevitable trial-and-error quick to pin down.
Blitting
Blit (short for bit block transfer, also written blitting) is the move in that last step: copy a small rectangle of pixels into a bigger buffer at some position. One tile is one blit.
Drawing the whole image is just that, in a loop — stamp each tile into its slot until the framebuffer is full:

Nothing special happens between tiles. Each blit writes its own 8×8 block and the next one writes the block beside it. Here's a single blit on its own, with the destination slot it lands in outlined:

The source can be any rectangle and you choose where it goes — copying one image into another at a position is a basic move you'll reach for constantly, not just here.