GBPunk
https://github.com/kaybarkbark/gbpunk
This is the GBPunk. It’s a GameBoy ROM and RAM dumper I made with the new hot chip of the day, the RP2040. It works how I always thought these things should work, just plug it in and treat your cartridge like a flash drive. Pull off your ROM and save file, write a new save file to it, just drag and drop.
Some of the first projects I ever worked on were GameBoy centric. I cut my teeth overclocking DMGs, modding in backlights before IPSs were widely available, adding awful LEDs to the inside of clear console shells, and blasting sound through the tiny speaker with an amp that was way too powerful for it. The GameBoy isn’t even my favorite console, but it’s so interesting and hackable. Capable enough to have some decent games, but simple enough for a single engineer to wrap their head around. The Z80 (ok, technically Sharp SM83) gives it street cred too.
This did not start out as a cart dumping project. First, the idea was to make a flash cart based around the RP2040, with PIO driving the parallel bus interface. I neglected to realize that there was not nearly enough SRAM in the RP2040 to hold several of the most popular ROMs in memory (if your cart can’t play Pokemon, what’s the fucking point) and pulling them from QSPI on the fly was definitely going to be too slow. I pivoted the idea after a furry con where I had taken a bunch of photos on my GameBoy camera of friends in their fursuits. I wanted to get those photos off but I had spent all my money on the con and didn’t want to dump another $60 on a dumper.
Well. I had this thing. I could just solder a connector to it, reverse the direction of the bus write some software…
Dumping Should Be Easy
I wanted this thing to be dead simple to use. It should just be plug and play. No stupid software to install to make your dumper work! So much of this aftermarket GameBoy stuff seems to be slapped together with bad software that only works on Windows 98 and below. I have to use an entire Windows XP virtual machine every time I want to put a new game on my old flash cart. Drives me nuts.
You should be able to plug this into a computer, and it should show up as a drive. Cartridges are just a storage medium, why should this be any different. Just make it pretend to be a FAT16 filesystem. Drag and drop your ROM and SAV files from it. Easy.
I found one other ROM dumper that operates this way, made by Benn Venn. That thing is a super cool piece of kit and I have a ton of respect for the team that put it together. I hope to make something as capable as that with this project.
Why is Dumping Hard? GameBoy Mappers 101
So how does this thing work? It just reads a cartridge right? That should be EZ, it’s just a ROM. Right? Just read it.
Wrong
The GameBoy has a tiny address space, 16 bits. That’s 2^16 memory locations, or about 64K. That’s for the whole system! All the ROM, cartridge memory, peripherals, video RAM, working RAM, it all has to fit in 64K. That’s a tiny amount of space. The cartridge gets a couple chunks of this memory, 32K (half) for ROM where all the game code lives and 8K for some extra RAM if the cartridge decides to include it. That’s even less space! How do you fit a game into 32 kilobytes?
Some games did fit a game into this much space. Tetris and Dr. Mario for example, are small and simple games that don’t need a whole lot of extra space. You can fit those games into 32K, and that’s what they did.
Other games like Pokemon have ROMs that are one megabyte in size. That’s way to big to fit into memory! They also have 32K (check this) of SRAM to store your save game! Pokemon save files are huge! How else are you going to fit your whole collection of pokemon, each with unique stats and names?
Cheating with Bank Switching
Think of bank switching like swapping out a GameBoy cartridge. You have two games, Tetris and Dr. Mario. These games both completely fill up all 32K of ROM. So how does your GameBoy access them both? Well, you manually pull out a cartridge and put a new one in. You have twice as much game as your GameBoy can access, so you just access one at a time.
What if your GameBoy could electronically swap out its games?
That’s kind of what bank switching is. When you plug in Pokemon, your gameboy can only see 32K of it initially. When it wants to see the other megabyte of game, it tells the cartridge to electronically unplug 32K of memory and plug another 32K in in its place. In Pokemon, it might do this every time you go to a new town. Unplug the old town, plug in a new town.
This technique is called Bank Switching and it’s used often in computers with memory constraints like this. I’ll be using more technical terms from here on out, so here’s what they are. Sections of memory are called banks. There are two banks of ROM in the GameBoy (16K each, 32K total) and one bank of RAM. When you swap banks out, that’s called mapping. This is why the mapping chips are called mappers.
Flavors of Mapper Chips
To make things complicated, there are more than one type of mapper chip used in GameBoy carts. Some are way more common than others, and some mappers are only ever used in a single game. Most of these were (presumably) designed by Nintendo, some were designed by second party developers like Hudson Soft, and some were made by bootleggers to enable multicarts.
Nintendo Mappers: MBC1-7 (not MBC4)
The Nintendo developed mappers are prefixed with MBC. There are six known mappers, MBC1 through MBC7, skipping MBC4. Why did they skip MBC4? Apparently it was supposed to link up to the N64 with a cable but this never came to fruition and no carts were produced for this mapper.
Nintendo mappers generally improve with each iteration. MBC1 had a lot of quirks, it was constrained on how many banks of SRAM or ROM you could map at a time. Some banks (0x20, 0x40, 0x60) are just completely unmappable due to how the chip works. MBC3 added support for a real time clock, so you could see the passage of time in your copies of Pokemon Gold, Silver, and Crystal.
The Japanese version of Pokemon Crystal used a bespoke version of the MBC3 called the MBC30, this just doubled the amount of ROM and RAM that could used on the cartridge.
MBC5 removed RTC support, but it is generally the easiest mapper to use and increased the amount of accessible banks. MBC3 and MBC5 are the most common mappers seen in the wild, with MBC5 being especially more common as we move into the GameBoy Color era.
MBC6 has only been seen in one game, Net de Get: Minigame @ 100 (ネットでゲット ミニゲーム@100). These carts are rare, expensive, and Japan only, so I am relying on Gekkio’s hardware database for this info. In addition to ROM and SRAM, this cart also features a flash memory chip. Flash memory is very unique in GameBoy games, with most opting to use nonvolatile SRAM for writable storage and a mask ROM for read only storage. This cart has both of those, and flash memory! If I have time I’ll look through some emulator code and see if I can figure out how this mapper was used, since there’s not a whole lot of documentation out there on this mapper.
MBC7 allows cartridges to interface with an accelerometer to keep track of the position of the GameBoy. Kirby’s Tilt and Tumble is a notable example of this, where you can tilt the GameBoy to control Kirby.
The Game Boy Camera Mapper
The Game Boy Camera is a special case. Not only does it need to handle bankswitching, but it also needs to handle the attached camera peripheral.
It’s possible to control and operate the camera entirely from the mapper, but that’s way out of scope for the GBPunk. The GBPunk could pretty easily read the save data from the Game Boy Camera, but that’s not what most people would want to use it for. What people want off of the Game Boy Camera are the photos! The photos are stored in a custom format in the save data that isn’t very well documented. I found some code online that could read out the memory and turn it into bitmaps instead. I glued this snippet into the GBPunk and made it read memory, convert all the photos into bitmaps, and just let users pull them off as pre-converted files
What’s Next?
Right now I have very good support for almost every single Game Boy cartridge. There are a couple edge cases I have not accounted for, but those edge cases account for maybe five cartridges in the entire Game Boy library (and they’re not very common or popular carts either).
The next most obvious thing to add support for is the Game Boy Advance. The cartridge footprint is the same, so I should just be able to plug the thing in right? Well, the Game Boy Advance is powered by 3.3V signals. The original Game Boy is powered by 5V signals. Everything that supports both Game Boy and Game Boy Advance cartridges has a tiny switch inside the console that can detect if you’ve inserted a Game Boy or Game Boy Advance game and it adjusts the voltage level accordingly. If you ever look at the back of a Game Boy Advance cartridge, you can see there are some notches cut out of the plastic that are not on the original Game Boy cartridges. This is what the switch settles in to.
Adding a switch like this is going to be a huge pain, so I am wondering if I can just drive both the Game Boy and Game Boy Advance cartridges at 3.3V. 3.3V technically falls within the acceptable range for 5V logic levels. Given how overbuilt the Game Boy and its cartridges seem to be, it might just work.
Lastly, I’d need to fix some of the bugs in tinyUSB. The GBPunk works great on Debian, but attempting to connect it to a Windows, Mac, or even Ubuntu machine starts to throw weird filesystem and connectivity errors. There are lots of projects out there that use a microcontroller to emulate a mass storage device (even the bootrom of the RP2040 can do it!) so I just need to read through them and follow their example.