LSDJ on the STM32 Game Boy Cartridge

Posted on

I got LSDJ up and running on my STM32 flashcart and added some features that make the thing a complete flashcart.

ROM

The microcontroller has 1MiB of flash, so after reserving some space for code and 128KiB to save RAM, I could spare 768KiB for the actual ROM. LSDJ is 1MiB nominally, but actually uses about half of that, so it fits without problems.

RAM retention

The second most important part of a cartridge is the battery-backed RAM that stores saves.

During operation my cart stores the RAM contents in the microcontrollers SRAM. LSDJ needs the maximum possible (for a GB cartridge) amount of 128KiB of RAM. Even though the STM32F405 contains 192KiB, it’s actually divided into regular SRAM and 64KiB of Core-Coupled Memory. Because of this, the RAM buffer had to be divided in two, but that did not cause any problems with access.

But what happens when you turn off the console?

In my cart it would not be feasible to keep the data in the microcontroller’s RAM because the current consumption would drain the small CR1220 battery in a matter of days. The idea was to use the battery, to keep the microcontroller alive long enough to write the RAM contents to flash or external FRAM and then shut down.

Unfortunately that idea failed because of a simple design flaw: when the 5V power is removed and the battery kicks in, the LDO feeds the power back to the 5V rail causing too much of a current draw.

Fortunately I was prepared for a simpler, manual approach. The on-board button is used to dump the RAM contents to the microcontrollers flash. Of course this crashes the console because the firmware is too busy writing to flash to respond to bus requests, but you were going to turn off the console anyway.

USB

I’ve added USB mass storage support to the cart, allowing the user to upload and download ROM and RAM without any additional software. When plugged in, it appears as a small mass storage device with two files on it.

The CubeMX USB library made the implementation a breeze, all I needed to do is to implement write and read functions. I also needed to prepare a filesystem image to present to the PC. Here’s how I did it using basic Linux tools.

# Create an empty disk image
dd if=/dev/zero of=gb.bin bs=512 count=2352
# Setup the image as a loop device
losetup -f gb.bin
# Create a FAT filesystem on the device
mkfs.vfat -v /dev/loop0
# Mount the filesystem
mount /dev/loop0 /mnt/loop
# Copy dummy files of the correct size to the device
cp rom.gb /mnt/loop
cp ram.sav /mnt/loop
# Unmount and remove device
umount /mnt/loop
losetup -D

The FAT filesystem is simple enough that it stores all the file information at the beginning of the disk and the actual data next. Thanks to this, I needed to embed only the first few kilobytes of the image in my firmware and serve the file contents from flash.

I noted the memory locations of the data for the rom and ram files so I know how to interpret read and write requests from the PC. Interestingly, some file managers try to be smart and do not want to overwrite the files in place. They try to create a new file first and change its name later. I have no solution for this, you have to use plain old cp.

Conclusions

Here’s LSDJ running on my cart, playing a song by RoccoW. That is all.

Links

https://github.com/Emeryth/stm32gbcart

https://github.com/Emeryth/stm32gbcart-fw

Leave a Reply

Your email address will not be published. Required fields are marked *