Super Game Boy Borders Without the Pain: GB-SGB-Tooling
We kept rebuilding the same Super Game Boy border kit for every game, so we fixed its bugs, added real tests and gave it away. Here's what's in GB-SGB-Tooling.
Slot a Game Boy cartridge into a Super Game Boy and something wonderful can happen. The tiny green-grey screen lands in the middle of your television, and around it appears a frame: artwork the game brought with it, painted in colours the Game Boy itself could never show.
It's one of the classiest tricks a Game Boy game can pull. It's also, as we grumbled in our look at the tools still missing for Game Boy development, a craft project every developer ends up doing from scratch.
So we stopped grumbling and packaged ours. GB-SGB-Tooling is a free kit for GBDK-2020 games that takes a border from a blank page to a tested ROM: make the art, convert it, send it to the Super Game Boy, and prove it arrived.
Seven Games, Seven Copies
Every game on our Play page has a Super Game Boy border. Every one of them also carried its own copy of the code that sends it, plus its own script to draw the art, each a little different from the last.
That's how bugs survive. Our newest game, GB Slingshot, got a fix the other six never received: hand it a border of exactly 128 tiles and the old code transfers nothing at all. None of our borders happened to be that size, which is exactly the kind of luck you shouldn't build on.
GB-SGB-Tooling is the one copy that gets fixed once, for everyone.
The Example Code Had Bugs
GBDK-2020 ships an example that sends a border to the Super Game Boy, and most homebrew borders start there. It works for small borders. Push it further and it trips over itself.
The border's tile graphics go across in up to two blocks of 128 tiles. Border tiles are twice the size of the Game Boy's own, so a full block fills 256 of the Game Boy's tile slots, and the function that loads them counts in a single byte, which tops out at 255. A full block has to go out as 255 plus one. The stock example also had a path for larger borders that asked to load zero tiles, which sends nothing and reports no error. And at exactly 128 tiles it sent an empty second block.
Our sgb_border.c fixes all three. On a plain Game Boy it simply does nothing, so the same ROM runs everywhere.
The Rules Nobody Tells You
The code is the easy half. The art is where borders quietly go wrong, because the Super Game Boy has rules that a normal image converter knows nothing about.
The Game Boy's own screen sits in a 160×144 window in the middle of the border and has to stay transparent, or you paint over your own game. Colour 0 of every border palette is invisible, because the Super Game Boy uses it as the backdrop. The usual converter, png2asset, only keeps that slot free in palettes that happen to contain a transparent pixel, so a second palette often loses its brightest colour to the backdrop. Your lovely highlight just vanishes.
Then there are the limits: three palettes of 15 colours, because the Super Game Boy keeps its fourth for its own menus, and no more than 255 unique tiles.
mkborder.py handles all of it. Give it a title and it draws a simple border for you: a banded gradient, a frame and your game's name in a chunky pixel font. Give it your own 256×224 art and it clears the screen window, keeps colour 0 free in every palette, snaps colours to the console's 15-bit range, and refuses with a clear message if the art breaks a limit, instead of letting you find out on a TV.
Proof It Arrived
This is the part we were proudest of. A Super Game Boy border is the definition of "works on my machine": the transfer can fail silently, and a plain Game Boy emulator will never show you either way.
PyBoy, the emulator our test scripts run on, learned to emulate the Super Game Boy in version 2.8.0. sgb_border_test.py boots your actual ROM with that switched on, then compares every byte the Super Game Boy received with the border data you built. Wrong size, wrong tile count, missing palette: it fails, and says why.
Bytes aren't pictures, though, so there's a second opinion. gearboy_check.py runs the ROM headless in Gearboy, a completely separate emulator, and compares the border it draws with your art, pixel for pixel. Two emulators agreeing is a lot more convincing than one.
Gearboy also caught a real problem. Without the Super Game Boy's boot ROM it starts palette 0 as solid black, and our demo's game screen disappeared. A real Super Game Boy has a sensible default, but relying on that is asking for trouble, so the demo now sets its own palette after the border. We'd recommend you do the same.
The fast checks and the PyBoy test run on GitHub Actions on every push, once with a drawn border and once with generated art big enough to need two palettes and both transfer blocks.
The Small Print
A few habits will save you an evening. Load your own graphics after the border, not before, because the transfer borrows the Game Boy's video memory to do its work. Wait a few frames after switching the display on, because a PAL Super Game Boy needs a moment before it answers the console's "are you there?" check. And build with GBDK's -Wm-ys flag, or the cartridge never announces itself and the Super Game Boy ignores the lot.
We'll also be honest about the limits. Two emulators agreeing is not real hardware. Whether a real Super Game Boy detects your cart, and how the colours look on an actual television, is still worth checking once on the real thing.
Take It
GB-SGB-Tooling is GPLv2 with a linking exception, the same terms as GBDK-2020 itself. That means your game doesn't have to be open source to use it, and shipped ROMs need no credit (though we'd love to see what you make with it).
Clone it, run make TITLE="MY GAME", and you'll have a working, tested border before your tea goes cold. Then go and see what a border adds: every game on our Play page has one, and the browser player shows the Super Game Boy frame by default.