While I was looking for a flashcart for running LSDJ, I started wondering if it would be possible to make a cheaper alternative using a modern microcontroller with lots of memory and computing power.
It turned out that Dhole had this idea before me and successfully used an STM32 to emulate a cartridge.
With the proof of concept already done for me, I decided to follow through with the project and make my own Game Boy cartridge.
A Game Boy cartridge in its simplest form is just a ROM chip attached to a bus with 16 bits of address and 8 bits of data running at 1 MHz. More advanced carts have a Memory Bank Controller that switches memory banks and types (ROM/SRAM) according to the address on the bus.
The cartridges max out at 8 MB of ROM and 128 KB of RAM, with most games using much less ROM space. Those numbers look like memory sizes found in modern microcontrollers, which led me to the flashcart idea.
For the cartridge I’ve selected STM32F405 with 1 MB of flash and 192 KB of RAM. That should be enough for LSDJ, which is nominally a 1 MB ROM, but actually uses about half of that.
The bus lines are connected to microcontroller pins in logical order (A0 to PB0 etc.). This way makes routing the PCB harder, but allows to quickly read the data without any calculations during the very time critical cartridge operation loop. The bus runs at 5V, which is tolerated by STM32 chips, and as Dhole has confirmed, the Game Boy works with 3.3V signals on the bus, so no buffering or level translation is necessary.
Game Boy cartridges store saves in battery-backed SRAM. The flashcart must also have a way to preserve the state when the console is turned off. That is why I have included a CR1220 coin cell holder. Unfortunately, retaining data in SRAM of the microcontroller takes too much current (around 0.3 mA in theory) to be feasible with such a cell. Instead, the idea is to use the battery to quickly save the SRAM contents either to flash or external FRAM chip.
I used Gekkio’s design for the cartridge shape and connector footprint.
1 MHz seems easy to manage with a microcontroller running at 168 MHz but as it turns out it’s not that simple. You have less than 500ns to detect the rising edge of the clock signal, decode the address and perform the appropriate action.
For now I have it working with a simple demo ROM. I still need to work on SRAM support and retention.
UPDATE: I’ve got LSDJ up and running: https://emeryth.net/2020/01/03/lsdj-on-the-stm32-game-boy-cartridge/
Another challenge is to make it work with Game Boy Color ROMs which run at twice the speed! This might be impossible even with overclocking the STM32 to 240 MHz.