6.6 KiB
title, description, date, tags, draft
| title | description | date | tags | draft | ||||
|---|---|---|---|---|---|---|---|---|
| A game(boy jam) story | 2024-08-15 |
|
true |
I recently mentioned on Twitter that I made a gameboy game with some friends during my first year of college. Since there's some cool stuff to showcase, here's a blog about it!
Context
This is some context to explain why we decided to make a gameboy game and a bit of background, if you don't care feel free to directly skip to the first paragraph.
Introduction
Quickly after starting college, I made some friends in my year group and some who were already in the 3rd year of college. All courses in the first year of my college were in C and the second year contained lot of C, C++ and one assembly course. It was common for students to have low-level side projects. My friends in 3rd year were doing a gameboy emulator.
It looked incredibly fun, they would barge in the room with their laptop in hand like "LOOK IT WORKS" and you would see a Pikachu in the screen, saying a BAURHAU because the sound system was broken.
Naturally, with friends of our year we decided to also make an emulator: a SNES one. This project is completely unrelated to this story, but that's how we learned how assembly & retro games worked. While this was not the same CPU, we made a disassembly and implemented instructions, learned about DMA & some hacks that games abused to run on limited hardware. Btw if you're interested, checkout Retro Game Mechanics Explained on youtube, especially his SNES series which is phenomenal.
Why a gameboy game?
We had a teacher that pushed us to do as much crazy stuff as possible (that's how I ended up making a xml parser in C for a game project...) Since the game jam war organized by the school, he started teasing our friends saying stuff like "for the game jam, you're going to make a gameboy game, right?". Of course, he was not serious, but the idea was already growing in our heads. We decided to make this game with our group of 6 (4 3rd year that did the gameboy emulator and me & a friend who did the snes emulator). One of us thought this would be a huge failure and refused to work on the asm side. He instead thought it would be cool to make a real cartridge that could run roms in a real hardware.
Space shooter
Initial goal
The game jam started, we had a weekend to make our game. The theme was announced a Friday at 18h, and we needed to make a keynote/presentation of the game on the Monday morning (even if we failed). We had access to everything in the school 24/24, even the electronics stuff. The theme for this jam was Space.
Since we wanted to make the simplest game possible, it didn't take us long (approximately 10ms) to settle on the game idea: a space shooter.
On a side note, I don't think we ever decided something this quickly :3
We wrote on a whiteboard what we wanted to do and started working. At the end of the weekend, the board looked like this:
Here's the same board but in text & English:
| Must | Should | Could |
|---|---|---|
| {{< icon "check" >}} Move | {{< icon "check" >}} Game over | |
| {{< icon "check" >}} Obstacle | {{< icon "check" >}} Music | |
| {{< icon "check" >}} Shoot | Enemies can shoot | Power ups |
| {{< icon "check" >}} Moving obstacles | Lifes | |
| {{< icon "check" >}} SFX | ||
| {{< icon "check" >}} Get hit | Flash | |
| {{< icon "check" >}} Simple menues | {{< icon "check" >}} Boss | |
| {{< icon "check" >}} Intro | ||
| {{< icon "check" >}} Score |
Turns out we overestimated the difficulty of the thing, we made a playable POC before going home (at 2am).
CPU stuff
It might sound complicated, but you don't have much to understand in order to write code. We have less than 256 instructions. Once you dedup via parameters (because increment register a is a different instruction than increment register b), you end up with only 44 instruction types (and that's counting the 13 bitshift instructions & the 7 interrupt instruction [like stop that stops the CPU]).
The gameboy doesn't have an OS. This means you don't have to deal with syscalls or to share memory. Allocating memory become easier:
-
You look at
constant.asmwhich can look like (note that$means hexadecimal):; Game registers DEF FRAME_COUNTER EQU $C000 DEF HARDWARE_TYPE EQU $C001 DEF RANDOM_REGISTER EQU $C002 DEF PLAYER_STRUCT EQU $C003 -
You find an empty address (like
$C005for example, becausePLAYER_STRUCTtakes 2 bytes). -
You shoot in the room "Is
$C005taken?", if nobody answers add a line inconstant.asmlikeDEF BOSS_DEATH_COUNTER EQU $C005congratulation you just alloced memory (forever). Until you merge your code, you must inform others that your address is used.
Jokes aside, this means you can write at any arbitrary addresses so your struct or variables are just magic addresses.
This logic is applied everywhere, since everything is memory mapped.
You are probably used to see address and assume that it's a somewhere on the RAM. This is not true for the gameboy, addresses between $C000 and $DFFF are in RAM. The others are somewhere else (others addresses can also refer to the ram as you can see from the table above).
If you want to read an asset from your game cartridge, you can just use an address between $0000 and $3FFF. There is no file syscall or anything like that.
PPU stuff
There's no GPU or fancy graphic system, the gameboy (and most consoles of the time) had a PPU instead: a Picture Processing Unit. This thing can display sprite, background and even palettes/colors if you are on a gameboy color.
This works by interpreting the VRAM as tiles.



