It’s been almost a year now since I set out to build my own handheld console. I learned a lot about the inner workings of the original Gameboy as well as about electronics in general. In the last installment of this series I talked about creating a display timing circuit with mostly 74xx components and I was planning on moving on to creating VRAM next. Things didn’t exactly go as planned, though.
As you may have gathered from the title picture, I replaced the countless 74xx ICs for the display system with something more compact. However, that was only after I already planned the whole VRAM with frame buffer and bought the parts to build it on multiple breadboards. It was then that I realized that this is starting to get ridiculously complicated and would never be able to fit inside a handheld case — even when replacing all the trough hole components with their surface mounted counterparts.
So I went and got myself a couple of FT81x chips. These are capable of outputting display timing signals as well as a full 24 bits of parallel RGB. That’s exactly what I needed to drive my 4 inch display. On top of that, they understand SPI which is easy enough to implement — even using Arduino.
While playing around with the chip, I decided to deviate a bit and create an open source Arduino library as well as an open source driver board that’s compatible with all Arduino development boards. There’s even a guide on how to get started with the hardware.
Microcontroller and Storage
I started development of my Gameboy emulator on the Teensy 4.0 board. In the meantime, the creator of the Teensy board family, Paul Stoffregen, released the Teensy 4.1 with an integrated SD card slot. Thanks to some great contributions to my emulator code from GitHub user granthaack, the project switched to the new board with its external storage capabilities. The emulator is now able to load ROM files from SD cards.
The Teensy 4.x with its 600 MHz clock speed turned out to be enough to run a Gameboy emulator at full speed. One of the limiting factors has been the data transfer to the display driver. Thanks to high speed SPI clocking at 24 MHz and the use of DMA to take most of the load off the CPU, it’s now enough to transfer around 30 frames per second — which is pretty much exactly what the original Gameboy had to offer.
Integrating audio into my emulator always seemed daunting to me. For one, because you read a lot that proper audio emulation is one of the hardest tasks when building an emulator, and for another, because I wasn’t sure what additional hardware I’d be needing for this. I was thinking about the Teensy Audio Adaptor first and I started writing some code for a proof of concept and it seemed to work nicely.
While digging deeper into this matter, I realized that using an audio codec IC like the one on the audio adaptor board is not only overkill, it doesn’t really help me take off the load of generating audio signals off the microcontroller, either. Because of this and because Gameboy audio is all square waves anyway, I decided to directly output each of the four audio channels of the Gameboy on a separate GPIO of the Teensy. If it wasn’t for volume control I wouldn’t even need PWM signals.
As it turns out, audio signals with at maximum 20 kHz are of such low frequency compared to the Teensy’s main clock that generating them doesn’t really take a lot of CPU time anyway.
As of now, I implemented 3 of the 4 audio channels of the original Gameboy. Due to some limitations with hardware timers I need to invest more time to figure out how to best add the fourth channel. However, I’m confident that there’s a satisfactory solution for this. Having the two square wave channels and the noise channel is enough to play Tetris with its very familiar soundtrack.
To mix the three channels together I just feed them trough a 1k resistor each into an audio amplifier circuit which, at its other end, is connected to a Gameboy replacement speaker for improved authenticity.
Hooking up the buttons for the joypad input was the easiest part of the whole build. Each of the buttons is connected to a dedicated GPIO on the Teensy board and pulled to ground when pressed. Internal pull-up resistors are used and debouncing isn’t required since Gameboy games expect raw button inputs anyway.
Internally, the signals from the buttons’ GPIOs are made available to the running Gameboy code by mapping them into the correct region of the Gameboy’s memory-mapped I/O. Piece of cake (or Bob’s your uncle for fans of the great EEVblog).
By now you’re hopefully eager to see (and hear!) this breadboard Gameboy play Tetris — as I’m writing this, that’s also the only game it runs since the emulator is still very much work in progress.
Some of the top points on my priority list currently are:
- Add support for more games
- Add automated tests
- Improve the overall form factor