Recreating classic games with a bare MCU
Some time ago, I hatched a plan to introduce my kids to a handful of classic computer games. This wasn’t a matter of misplaced nostalgia: I’m not overly sentimental and I’d rather play Horizon Zero Dawn than Pong. Instead, I wanted to show how simple the design of a computer game can be — perhaps to encourage them to tinker on their own.
With their assistance, I ended up building three handheld games. It proved to be an enjoyable pursuit and a source of more joy than I anticipated — so I wanted to share some practical notes, technical details, and gameplay videos for posterity.
Game #1: TurboSnake 2000 and 25000
In this classic genre, the player controls a constantly-moving snake. The only possible input is to turn the snake 90 degrees to the left or to the right. The objective is to eat “apples” appearing on the screen, and to do so without colliding with any obstacles or hitting your own tail. Because the snake grows with every consumed apple, survival requires careful planning and a good amount of dexterity.
The history of this sort of games is muddied, but their core mechanics trace back at least to Nibbler, a 1982 arcade game developed by Rock-Ola. Much later, the reintroduction of the game by Nokia turned it into a staple of mobile gaming in the pre-smartphone years.
I picked “snake” as the initial project because of the simplicity of the gameplay and its compatibility with low-resolution displays. At first, I thought of using a 16x16 LED array as the gameplay arena; but after some experimentation with an 8x8 LED dot matrix module (LD788BS-S22 from Jameco), I was surprised to find this low-res variant still quite playable. The added benefit was the ease of driving this display directly from an MCU without any additional registers or line decoders.
So, I built the first prototype with an 8x8 LED display controlled directly by an ATmega328P in a “scanline” mode. The display has a grid of internal connections that allow the MCU to select a specific row to activate by applying a voltage to one of eight vertically-running lines, and then to turn on chosen LEDs in the selected row by via eight horizontal pins. Although this only illuminates one row at a time, persistence of vision does the rest.
The game employed four momentary switches for controlling snake direction. LED matrix aside, the only other output peripheral is a small speaker, wired through a transistor to an MCU output pin.
Here’s my wife demonstrating the gameplay:
My kids enjoyed the game quite a bit; that said, the lack of a high score system made it less competitive, so some time later, I ended rebuilding the game with a larger gameplay arena and three Lite-On LSHD-A103 7-segment displays for keeping score:
This variant required a bit more work: with 21 LED segments, 32 dot matrix pins, four buttons, and a speaker to talk to, you run out of pins on most simple MCUs. To solve this problem, the circuit uses a 4-to-16 decoder (74HC154) and output multiplexing. The cool arcade-style buttons are NKK JF15CP2D; the manufacturer carries a number of other colors and shapes. The powerhouse is an ATmega644.
The software side may seem simple, but holds surprises. For example, naïve collision detection tends to malfunction when the head of the snake is following its tail with no empty cells in between. Balancing the gameplay takes some effort, too; the Nokia implementation afforded a bit more time to make a turn whenever the snake was about to hit an obstacle — a tweak that made the game a lot more playable.
Game #2: Dino in Space
In “scroller” games, a player-controlled vehicle or a creature traverses a never-ending linear landscape; the objective is to dodge or destroy obstacles along the way. The first game instantly recognizable as a “scroller” was probably Scramble (1977), but a more iconic contemporary example is the Dinosaur Game embedded on the “no internet” error page of Google Chrome.
For the second project, I wanted to use a more sophisticated text-based LCD display, and a scroller seemed like a good fit. The initial plan was to replicate the Chrome game, but we soon decided that it would be more fun to be able to shoot things. So, I put the dinosaur into a spaceship, making it navigate an asteroid field and collect ammo needed to clear rocks.
In this game, ATmega1284 is pulling the strings, although the use of this beefier 8-bit MCU ultimately proved to be unnecessary. The initial version employed an old-school backlit Hitachi HD44780 LCD (Newhaven NHD-0420H1Z-FL-GBW-33V3), but after one of the kids inadvertently crushed the module, I replaced it with a drop-in OLED substitute powered by WiseChip US2066 (Newhaven NHD-0420CW-AY3). This upgrade improved screen legibility and reduced power consumption — a welcome feature on long car trips.
The rocks and the spaceship are drawn with custom 5x8 characters; the rest is standard text. The game also features a 4-slot high-score system in the style of old arcades. The kids loved it, played it for weeks, and racked up high scores I can’t beat. At some point, my eldest one also figured out how to modify the EEPROM to cheat, but that’s a story for another time…
Here’s my wife demonstrating the gameplay:
The software side is fairly straightforward, with most of the work going into balancing the gameplay. About the only surprise is the ages-old problem that real randomness doesn’t look random: it tends to generate long runs of zeros or ones that look suspect to players. To counter this problem, the code uses per-row back-off logic for generating new rocks at a reasonably uniform rate.
The source code of Dino in Space can be found here.
Game #3: Blockbuster 7000
The final game I built was meant to be a loose reinterpretation of Breakout (1976) / Arkanoid (1986), a pair of physics-based games where the player controlled a paddle and the objective was to bounce a ball to gradually destroy a brick wall on the opposite side of the game arena. Failing to catch the ball would cost a “life”.
I built the game with an ATmega1284 connected to a 128x64 monochrome OLED display powered by the SSD1309 controller (Crystalfontz CFAL12864J-Y). This necessitated not only writing a custom driver, but also designing a suitable 6x6 font. I would have done it by hand either way, but I was surprised that there was no open-source implementation to rely on.
With the low-level tasks out of the way, I experimented with ways to fine-tune the gameplay. To make the experience more dynamic, I borrowed an idea from Tetris and programmed the ball release button to momentarily speed up the game when held. This allowed sprinting through the “safe” segments when the ball is traveling away from the paddle, then slowing down when it’s coming back.
In contrast to earlier experiments, I also added discrete levels: there are 20 in total, each with a different arrangement of bricks — including an increasing number of “reinforced” ones that take two or three strikes to kill. To add some variety, the game also awards combo bonuses whenever the ball strikes multiple bricks.
Blockbuster 7000 proved to be a huge hit, perhaps owing to its dynamic graphics, varied level design, and a 6-slot high score system. Once again, here’s my spouse demonstrating the gameplay (the MCU is nested under the display):
The source code for Blockbuster 7000 can be found here.
There’s no real moral to this story, but I found this to be a fun exercise: an opportunity to use some obsolete parts from my project bin, to learn the interface of modern OLED displays, to connect with kids, and to have a bit of harmless fun.