Long Hours in the Smithery 
I recently came across "Odin: A Programming Language Made For Me." The author of the post has also written a book about the Odin language, and it's a really nice technical overview of aspects of Odin that he recognizes from programming practices he used at the game company "Our Machinery."
I'd never heard of "Our Machinery." On my first read, I wondered: "What's their story?"
We were creating a whole game engine in plain C.
Oh. The phrase "plain C" screams out to me. In pain. The proximity to "game engine" is garnish.
To explain why, I'm going to take the long route. Long enough that you'll probably forget the "Our Machinery" framing by the time we swing back around, but I have a story to tell and I'm gonna tell it. If you have engineering experience, this will read like a tragedy, but this story is old enough that it's become farce.
I had a very standard route into software development for an 80s kid. I liked playing games, I wanted to make games, and I vaguely understood that in order to do that I needed to learn how to program. I went to University to study Computer Science with this goal in mind, and after countless hours doing coursework that had nothing to do with games, I got my chance to make whatever I wanted for my senior project.
My school's senior project was a team-oriented affair where we had to collaborate with ~7-8 other students to build something significant over the course of two semesters. There was no issue finding a group of people who wanted to make a game. The course was designed to teach about the software development lifecycle and to give students experience doing "software engineering" as opposed to just coding. Teach us those things it did, though not via the intended mechanism.
Our group decided to make a GBA game, because we played on hard mode.
The GBA homebrew scene wasn't that big, but it was an exciting time to build for the platform. The DS wouldn't be released until after we finished our project, so the handheld itself was still a "current gen" device and culturally relevant. Detailed information on the hardware was available thanks to the GBAtek specs, extracted from the no$gba
emulator.
There were resources that detailed how to set up a cross compiling GCC and build environment to produce bootable ROMs. There were free emulators that you could use to quickly test your ROMs, but flash cartridges were also available, so you could run on the physical device.
Homebrew is the fig leaf providing the cover of legitimacy for flash cart producers, whose products are overwhelmingly used for piracy. We joked that were probably the only people on campus using them for their "official" purpose, but it was an incredible feeling to see your own code and graphics running on the device.
For the actual game, we decided to make a horizontally scrolling shmup, in the style of games like Gradius, R-Type, and Einhander. In a nod to Einhander, we named our game Estoc Advance. With a name in hand, one of our biggest challenges was already conquered.
Since our build environment used GCC, we only had a few choices for implementation language: ASM (ARM + THUMB), C, and C++. Our team's subgroup of C++ haters had quorum, so we went for plain C. Hard mode!
This decision was almost certainly a mistake, but we'd make plenty more. Our project was performance art, a speedrun for committing as many sins against Fred Brooks possible. C was simpler, and simpler had to be better, right? I went to school in New Jersey after all.
It's not like our team lacked C experience; several people only knew C, which contributed to the decision. But our experience working with a large team and assembling software out of independently developed components was, charitably, theoretical. The "bloat" that we disliked in C++ would have given us some guardrails and bridged some technical gaps for component integration, but we stubbornly believed that all of that faff was just a crutch for lazy programmers with skill issues.
A crutch isn't a bad idea if you haven't learned to walk.
I was in the "can write code" camp, but I was also unknowingly a card carrying member of the "doesn't know how to build large projects." Nonetheless, I wrote a ton of code for this project. The dirty secret in Computer Science education, especially at that time, was that most students were not able to program at all, so being able to write working, useful software put you so far ahead of the curve that it inspired overconfidence.
The first issue I had was that nearly all of the homebrew dev materials and associated tools were restricted to Windows, despite the devkit being a distro of GCC. If you want some idea of the materials I'm talking about, check out devkitadv or the Linux gbadev page.
Rather than run Windows for a few semesters, I decided it would be better if I set up my devkit in Linux, and spend a few weeks porting the tools that we needed. If you've done software estimation, you know how many months a week can take.
The first challenge we faced in the project was getting the build environment working. The equivalent of "Hello, world." for the GBA was a building a rom that make the screen flash different colors. The graphics hardware made this fairly straightforward to do.
If you wanted display the text "Hello, world.", that was different. The program we're all familiar with in plain C typically looks something like this:
#include <stdio.h>
int main() {
printf("Hello, world\n");
return 0;
}
Unfortunately, printf
is part of libc, and libc wasn't available on the GBA. If you were thinking you could use write(2)
, that's part of the POSIX standard, and there's no OS on the GBA, POSIX or otherwise. There wasn't a text mode; other than the code you wrote, you had some memory mapped hardware registers, some BIOS calls, and interrupts.
Since hardware was the beginning and the end, it's worth exploring a little more. To display anything, let alone text, you had to interface directly with the GBA's graphics hardware.
The GBA had 6 different graphics modes: 3 bitmapped and 3 tile based. Like everything else on the GBA it was memory mapped, so you selected the hardware mode by writing a specific byte into a memory location, and then you could change what got rendered by writing to specific areas of video memory.
Sadly, there was no reprise of the SNES' legendary mode 7, but the tile modes had similar tile scaling and rotation, and you could pull per-scanline shenanigans by modifying the framebuffer during HBLANK.
HBLANK? The GBA refreshed its 240x160 screen at ~60hz. After drawing 160 pixels for a scanline during a phase called HDRAW, it would "draw" an extra 68 pixels in the HBLANK phase. This gave you ~270 cycles between each line update where the screen was not being modified, which was only 16 usec. For the vertical lines, there was a similar 240 line VDRAW phase followed by a 68 line VBLANK. There were interrupts for each phase you could catch to run during that phase.
Nearly all games used one of the tiled video modes, which were numbered 0-2, because the limitations of the 3 bitmapped video modes were crippling. Mode 3's 16-bit framebuffer left no video memory for a second page, which meant you couldn't use the hardware's page flipping to avoid shearing and draw safely during HDRAW. Mode 4's 8-bit framebuffer was awkward to use, because you could read 1 byte at a time from VRAM, but writes had to be in 16-bit or 32-bit increments, so it was not possible to update less than 2 pixels at a time. Mode 5 had 16-bit framebuffers and page flipping at the cost of a lower resolution.
Using the bitmap modes meant that you couldn't take advantage of most of the GBA's graphics hardware. You could scale and rotate the entire frame, but you would have to implement your own compositing in software. There wasn't much budget to spend on doing that, since you needed to save some computation time for your actual game. You only had ~80000 cycles during VBLANK to blit the 38,400 pixels to the framebuffer, anyway.
Tiled modes were more capable, but more complicated to use. Instead of VRAM being a framebuffer, it was split into screen and tile data. The screen data was had multiple layers of framebuffer pages that would be composited together to form the final displayed buffer. In tiled mode, the framebuffer pages had references to 8x8 tiles in the tile data rather than pixel data. The tile data did not contain pixel data, either, but offsets into the background palette, which had 256 16-bit colors. The palette had no alpha channel; instead, blending could be done between layers in hardware.
No OS meant no filesystem. To load graphics for use in tile mode, you would need to "render" your image to C header file data with a palette section and a data section. If you've used something like Go's embed package, it was similar, but worse.
Compiling image data to headers ensured that your assets would be part of the ROM image. While you could modify the palette dynamically (even during HBLANK! I told you there were shenanigans), it made asset creation and management complex. We reserved a small amount of palette space for HUDs and chrome and the like, and then used the rest of it to build out the stage that the player was in.
This required a lot of discipline when developing assets, but it also meant that you needed to export your asset data using a consistent palette definition. You want a reference to 0xff00ff
to point to the same position in the palette for every image, so you had to reconcile the colors in your image file with an established palette, which creates a bootstrapping challenge early on in the asset design process.
Getting back to displaying text, once you've successfully loaded your font tile sheet and made sure that basics like orientation and bit-order and palette references were correct, you could work on positioning and mapping ASCII bytes to their associated tile offsets.
I had to look up some of the finer details here for the purpose of writing this, but I remember a lot of this because I wrote a graphics exporter (for BMP to header files), palette manager, text rendering library with arbitrarily positioned text boxes, and the intro game-select screen.
Much of what would make a playable game is conspicuously absent from this list of achievements, and that's not because it was the domain of other team members. We didn't really know how to do that in the first place, and frankly a lot of us were slackers just trying to finish up senior year.
The truth of the matter is that I found learning about the hardware and working on tool smithing more fun than building the game itself, and I spent long hours in the smithery. This was, to quote F. J. Corbato, an inadvertent research project. Despite learning about the GBA itself in depth, I barely scratched the surface of topics like level design, collision detection, or any of the basics for an actual game.
Because our whole asset processing chain ended up going through my tools, only a couple developers on the project who used Linux could run them without an awkward cygwin setup. Our computer labs weren't even any help, they ran NetBSD and my jank C code didn't build with BSD libc.
In the end, one of the other team members did manage to cobble together some simple collision detection and a few stages, so we at least had something to show on demo day. The project was to create a GBA game, not an asset pipeline.
The project part of the software project was an unholy mess. There were far too many unknowns, and we left them too late to be able to realistically complete when things didn't go our way. We made some bad technical decisions that ended up making collaboration harder. We spent too much time building a platform in an attempt to make the game easier to iterate on, rather than letting the game drive the platform requirements.
I learned a lot from that experience, though these lessons absorbed slowly over time as I started to recognize that it was actually a failure rather than an 11th hour victory. How to be a good programmer and a bad team member at the same time. How to spend time building good things that still end up being a distraction to the actual goal.
The siren's song of the smithery still calls out to me. My open source software is all tools and libraries. This post itself is more evidence of that; it's 75% GBA esoterica by volume. People who've never managed to escape this mindset can make you a beautiful table, but they can't build a house.
Back to "Our Machinery" and plain C.
When I saw those words, I worried that the people making this decision are making some of the mistakes that I made. That the adherence to C's parsimony is more of an aesthetic choice than a technical one. The people I've known who take this path are always intensely focused on quality, artistry, and craftsmanship; all good qualities; but they are employed in service to the code and not the product.
The idea that the implementation language is relevant when describing the project is a kind of disclosure itself. This engine would be distributed as object code, which could theoretically have all sorts of language bindings. It's not like mentioning a target platform like the JVM or .NET.
I have no way of knowing whether my concerns about "Our Machinery" and their game engine The Machinery are in anyway a reflection of reality, or if they are projection. They are a different group of people at different stages of their career.
Or at least, they were. They eventually released their engine, but the company failed not long afterward, and in the process they deployed a new EULA that allowed them to rug-pull the engine from pre-existing projects.
That's not my story to tell, and I'm not claiming that language choice was a contributing factor, but the mindset that the language choice matters combined with that particular choice sets off some alarms that the focus isn't where it needs to be.