Game Boy emulation in C#: ROM dumping with an Arduino
To validate a Game Boy emulator against real games you first need ROMs. In this installment of the dotnetpro series, Heiner Palmen wires an Arduino Mega 2560 to a cartridge slot and combines a small firmware, gb_reader, with a C# Dumper to read real cartridges into a .gb file.
Key points
- Cartridge access uses 16 address lines, 8 data lines and the /RD, /WR, /CS and /RESET control lines; an ATmega2560-based Mega 2560 is chosen over an Uno because 28 pins are needed at once.
- The firmware protocol frames are A5 5A SEQ CMD LEN_LO LEN_HI [Payload]; there is no extra checksum because USB already guarantees integrity in hardware.
- The SerialDevice class handles frame building, sequence numbers and time-out retries, while Dumper reads the ROM in 64-byte blocks.
- The 80-byte header is validated first: if the Nintendo logo bytes at 0x0104–0x0133 do not match, the dump aborts immediately.
- The Mapper class covers ROM-only, MBC1, MBC2 and MBC3, switching 16 KB banks through register writes in 0x2000–0x3FFF.
- A mismatch in the global checksum at 0x014E–0x014F signals that something went wrong during bankswitching.
A Game Boy cartridge is essentially an addressable byte array: 16 address lines, 8 data lines and four control signals. Heiner Palmen wires a Keyestudio Mega 2560 Plus to a cartridge slot and flashes gb_reader, a firmware that speaks a compact binary frame protocol over serial at 500,000 baud (8N1) and uses sequence numbers to discard late responses.
Five commands (PING, READ, WRITE, ARM, DISARM) are enough: the firmware only moves addresses and bytes, writes stay locked until an ARM command carries a magic value, and the .NET host decides everything else. Its Dumper class reads the 80-byte header, validates the Nintendo logo, picks a Mapper for ROM-only, MBC1, MBC2 or MBC3, and pulls each 16 KB bank in 64-byte blocks; a 32 KB dump takes one to two seconds.
Real-world use
- A developer validating a hand-written Game Boy emulator end to end with real Tetris or Zelda: Link's Awakening ROMs instead of small test programs.
- A retro hardware hobbyist archiving original cartridges before their battery-backed save RAM gives out.
- A .NET developer learning hardware interop and a sequence-numbered frame protocol by driving a microcontroller over a serial port from a C# app.
Why it matters
Getting trustworthy reference data is the hardest part of emulator work; this approach reduces the hardware to a byte server driven from C#, so you can produce verified ROMs from your own cartridges.