MERKÉN: Breakdown of a Game Boy Demo
Building my first Game Boy Demo
- What is MERKÉN
- Why make this breakdown?
- Why make a Game Boy Demo?
- Tools
- How to Build
- The Entry Point
- The Logo Intro
- El Matapacos
- The Twister
- Drowning Myself
- Bitmap Animation
- 3D Cylinder
- Fin
What is MERKÉN?
MERKÉN is a Game Boy demo that was released during Revision 2020. The music was created by Francisco “Foco” Cerda.
We got 2nd place in the Oldskool Compo category.
Merkén is also a delicious smoked chili pepper condiment traditional in Mapuche cuisine in Chile. I love it!
Picture by McKay Savage - CC BY 2.0
Why make this breakdown?
Personally I love when people share details of how they make things. I feel the information they share has a lot of educational value and can help and motivate other people to do the same.
Here is a small list of my favorite demosceners, articles and repos where people share and talk about their productions.
- How a 64k intro is made.
- Flopine - Live shader coding.
- Source code for Ctrl-Alt-Test productions.
- LunaSorcery - A mix of live shader coding, demo breakdown and graphics stuff.
- Ferrisstreamstuff - A mix of emulators, demo breakdown, tools, hardware and other stuff.
Why make a Game Boy Demo?
After releasing my first PC Demo Sacrificio, during Flashparty 2019, I became obssessed with wanting to release something for the DMG Game Boy. This handheld console has been my favorite console of all times for many reasons, but the most important one is that it was a symbol that marked my childhood as a kid who grew up in the 90s.
For the last couple of years, I’ve been learning about the internals of the Game Boy and how to program the Sharp LR35902 in assembly. I was confident enough with my knowledge and skills that I could make a small production.
During March of this year Revision announced that they would be doing the whole event online and they would allow for remote entries. I saw this as an opportunity to participate in one of my favorite events of the year and decided to start programming something for the Oldskool Compo.
Tools
The tools for making this demo were:
- For assembling and linking I use RGBDS.
- For the music Foco used Carillon Tracker.
- For programming I used Sublime Text 3 with the RGBDS syntax package.
- For graphics I used Shadertoy, Photoshop and a tool I made for converting RGBA images to the gameboy special format.
- BGB emulator for running and debugging the rom.
- Everdrive GB for testing on the real hardware.
You can also find the whole source code for this demo in my Github repo.
A lot of the data like sine waves and scanline effects were generated by running a JavaScript script with node.js that created the tables and then I just copy pasted those tables into my source files.
How to Build
I use Windows as my main OS for development so I decided to use batch scripts to help me with my project building process.
The BUILD.BAT script is very simple, but it requires this fixed project structure.
ROOT_PATH/
PROJECT_NAME/
code/
src/
include/
In the src/
folder you put all code that will be assembled and on the include/
folder you put constant information, macros and assets. Then you can load them with the include
preprocessor directive like in C.
With that only requirement this batch script will assemble and link all source files and output a .GB file into a build/
folder in the root of the project.
The Entry Point
MAIN.ASM is where the entry point is located. It has a very simple structure. Here I initialize every common part of the demo, like the music; common variables, like fading; and scene control.
At the top of the file you can see a lot of boilerplate like setting sections for interrupts, even though I don’t use them, and the cartridge header. The fun part comes after that.
ONLY DMG, SRY
I originally wanted to make this demo only playable on DMG CPUs. By only DMG CPUs I mean the whole generation before Game Boy Color.
I had this piece of code that would act as a firewall if it detected a system that wasn’t what I wanted. At the end I chose to allow any system to play the demo, but I still left the code that displayed “ONLY DMG, SRY”.
On systems like the CGB (Game Boy Color) and AGB (Game Boy Advance) the A
register is initialized to $11
. What I did here was check if the register was initialized to that value and if it wasn’t the program will jump to the .is_dmg
code and start executing the demo. If not it would display this nice message:
This was disabled so it’s only visible if you go to the source code and add jp not_dmg
after the line jr nz, .is_dmg
.
Start Demo
As I mentioned before, in the MAIN.ASM
I do some initialization and handle the sequence of effects. It’s about 30 lines of assembly that puts together all the different scenes.
In the .start
code path I initialize the stack, setup the fade color to the default gradient, initialize DMA control, initialize and start music.
Next comes the sequence of effects. You might see that the order isn’t sequential, and that is only because the number is related to the order of creation. After writing them I ordered them according to what I though looked the best.
So what happens is that I call fxN
that initializes and runs an specific effect. I’ll call these effects source files “modules”. That same module is the one in charge of returning when the effect timer is up. So when that happens it jumps to the next effect. When it reaches the last scene it’ll loop forever playing the music and the last effect.
The Logo Intro
FX1.ASM contains the intro scene. Where the Nintendo logo scrolls and shows the MERKÉN logo with the bleeding eye.
Even though it might look like a very simple intro there are some well timed loading points that are crucial for this to work. The Game Boy has a screen of 160x144 px, but the internal tile map is of 256x256 px, which is split into 8x8 px tiles. That means that the background map is 32x32 tiles. The height of this scrolling scene is around 64 tiles in height, that’s 512 px. This means I need to load tiles while the screen is scrolling. You can see how that looks in VRAM.
All this loading happens frame by frame. Instead of loading everything at once in a single frame and producing an ugly hitch that is very notorious with the music playing, we load small chunks of tiles per frame.
The setup for this is at the initialization of the effect.
The code that handles the loading is near the bottom of the file.
At the top of this subroutine we have a check to see if we need to wait for the scroll to reach a specific point. This is because we need to wait for the screen to finish scrolling up to some point so we have enough time to load the tiles that are left. This code is run on every frame so on every frame there is a possibility of loading a chunk of tiles. Very similar to what happens in scrolling games like Super Mario Land.
Double Nintendo Logo?
So if you look through the code you might see that I actually load a “new” Nintendo logo over the one that’s already visible. This is because I am lazy. The reason I did this was because the Game Boy starts up with this palette: %11111100
. This is equivalent of having all indices in the palette to be dark except for the last one. If I changed the palette to have a traditional gradient (from dark to light) it would display the Nintendo logo as a light logo. So what I did was load the same logo but with a dark palette, this way I wouldn’t have to be worried of swapping the background palette in the middle of the screen. Of course this only happens on DMG systems because CGB and AGB don’t store the logo once it finishes the boot sequence.
El Matapacos
FX3.ASM and FX4.ASM contain the scene where the Matapacos appears.
This scene is split into two modules because I didn’t want to make a huge file for two effects. FX3.ASM
handles loading, scrolling and doing the initial animation. FX4.ASM
handles the scanline wave effect.
There aren’t many interesting things happening here. The only thing that might be worth mentioning is the scanline wobble which is a very simple effect. What I did here was first I had a table of 256 bytes aligned by 8 bits of precalculated sine wave values with an amplitude of 8. This allowed me to traverse this table without the need to worry of overflowing and reading outside the table’s memory. To animate the wave motion I had a variable that controls the reading starting point to the table.
The effect is just a couple of lines of assembly and it’s called every frame.
The Twister
FX2.ASM contains the twister effect. I personally like this effect since it looks more complex than it really is.
The visual effect is mostly a result of a specific image and a bunch of scanline sine waves. This is the image I used for generating the effect.
I was careful to make the image tileable so I could repeat it along the full tile map.
Here you can see how I layed the image in VRAM and you can also see the overall movement of the LCD scroll.
The code that produces this effect wasn’t very long. Similar to the wave effect found in Matapacos scene, this one also used a sine wave table and offsets to animate it. The important parts can be found here.
What this code does is use two different wave tables to apply displacement to the X and Y scrolling of the LCD on every scanline.
+
=
=
Drowning Myself
FX6.ASM has the code for this effect. I am very fond of it because it was one of my first Game Boy scanline wobbles I made. This was made a bit more than a year ago and in some sense it was the first step into making a Game Boy demo.
As you may have noticed there is a trend in these effects. Most, if not all, have some kind of scanline displacement and this one is not any different.
The code, the same as the other displacements, is very straight forward. We go through each selected scanline and apply a displacement with an offset based on a wave table. Just to be consistent with the rest of the effects here you can see the commented code that produces the underwater effect.
Bitmap Animation
FX7.ASM, FX8.ASM & FX9.ASM render this bitmap animations. These were the scenes that took the most time to produce, but not for their technical complexity, but because of all the steps needed to generate the animation.
I didn’t have a pipeline to create these animations and it was mostly a manual work which included screen recording or camera recording, Photoshop, export to frames and transform to tilemaps.
For example, for the animation of my cat Shin what I did was record a short video of him on my phone. Then I transferred it to my PC and opened it with Photoshop and reduced by hand the number of frames in the video until I had 9 frames in total. After that I exported all frames into images at a resolution of 20x18 px. You may be asking yourself, why 20x18? This is because the screen when its scroll X and Y is set at 0 the number of visible tiles are 20x18 tiles. Knowing that I can transform each pixel into a tile.
These are the exported frames from Photoshop.
The final step was transforming them from pixel data to Game Boy tile indices.
I created this gradient tileset that would be used to render the image on the Game Boy’s LCD.
This means that I needed to transform the pixels from a range of R8G8B8 to an index not bigger than 16. What I did was use the relative luminance of the image to get a value from 0 to 1 and then multiply that by the tileset tile count. Finally store that into a file I could embed into my ROM.
The code that did that on my tool was this:
This is the code for rendering and updating these animations on the system.
3D Cylinder
FX5.ASM has this final scene with the credits. This is one is my favorites. I like that I was able to mix two different effects into a single scene.
This was possible because of the simplicity of running the bitmap animation. The way I was able to have two different effects in a single screen was by changing the bit for selecting the background tilemap on the LCD control register for each effect. When I rendered the bitmap animation I was displaying tile map data from address range $9C00-$9FFF
and for the cylinder it was $9800-$9BFF
.
Here you can see how it looks in the different regions.
To display the rotating cylinder what I did was, again, have a displacement table with values that represented an arc. When these values reached the peak of the arc they had to be negated so that the effect of cylinder could be mirrored as a displacement on the LCD.
The code for displaying the cylinder effect can be found here. On summary what it does is wait for scanline 40 to be reached, swap the background display map bit on LCDC, start the scanline effect going through each scanline applying the displacement effect plus a scroll. Repeat until scanline 99 and then return to the previous background display map.
Fin
This was a very fun demo to make. It was my first time participating in Revision and the second time I’ve ever participated with a production.
I hope this breakdown can motivate other people to create their own Game Boy demos and participate in demoparties or to just get started in programming this awesome system. One thing I love about the Game Boy is that programming for it is very accessible since there are tons of documentation and a community constantly working on tools.
I highly recommend looking into the GBDEV Community. You’ll find people with a lot of knowledge about the Game Boy.
I would also like to recommend watching this awesome talk by Michael Steil called The Ultimate Game Boy Talk to get familiar with the Game Boy hardware. I also suggest reading and using the gbdev Pan Docs for an in-depth understanding of the system.