Dumping NES Mask ROMs
I wanted my own copy of Duck Hunt to run legally in an emulator with my Wiimote-based lightgun, so I bought a Super Mario / Duck Hunt / World Class Track Meet on ebay, as well as a 72-pin connector. However, I quickly realized that it would be a nuisance to read the data from the cartridge using the 72-pin connector, having to deal with bank mapping, and likely more connection lines than the microcontrollers I had available had. So I desoldered the mask ROMs (while feeling bad about damaging a cartridge, but hey it's meant for playing) and dumped them with one of the many cheap STM32F103C8T6 development boards I had lying around.
You can do this with any Arduino-compatible board that has the right number of pins for your ROMs.
I am quite new to this, and there are no doubt many wrinkles for more complicated ROMs, but these instructions should work for ones with a simple prg/chr layout.
Supplies
- Blue Pill STM32F103C8T6 (clone is OK) or other Arduino-compatible microcontroller with at least 24 GPIO pins (for 64kb ROMs; need 25 pins for 128kb, 26 for 256kb and 27 for 512kb), at least eight of them 5V-tolerant
- breadboards and wires
- mask ROMs from NES cartridges
- computer with Arduino IDE
- ability to run python scripts
Microcontroller Preparation
If you're using a Blue Pill STM32F1 board, use Steps 1 and 2 here to install a bootloader and set up your Arduino to work with it.
Get my sketch with its included python scripts.
Look up your cartridge in the NES ROM database so you have some idea what you will be dealing with. In particular, note the PRG and CHR sizes.
Remove Mask ROMs
Open up your cartridge. I massacred the screw areas with a rotary tool, but you can buy a "game bit" screwdriver for the weird security screws.
Find the Mask ROMs. They will typically be 28 pin or 32 pin. Layouts are here. If you're unlucky and they're covered with epoxy blobs, you'll have to do something else than what I did.
You'll either need to desolder them and stick them on a breadboard or else solder fine wires onto the pins. The latter is less destructive but more work. I went the desoldering route. It was surprisingly easy with a heat gun. I covered the chips with aluminum tape, and covered up other components on the board with aluminum tape, and thoroughly heated the pins on the underside of the PCB, and then again on the top side. The chips started moving really nicely then and came out perfectly clean and nice with an IC puller. Sorry, no photos.
There will be at least one PRG ROM, and there may be a CHR ROM as well (though it might be RAM, in which case I think you should leave it alone).
Edit Sketch
If you're dealing with a blue pill microcontroller, you can leave my Arduino sketch as is. Otherwise, you'll need to edit the pin definitions. Refer to the mask ROM layouts here and here as applicable. You need to define NES_D0 through NES_D7 for the data lines. These need to be 5V-tolerant pins. And you need to define NES_A00 through NES_A1x for the address lines. How many address lines you need depends on your mask ROM: 64k requires 16, 128k (which is what I had) requires 17, 256k requires 18, and 512k requires 19. These pins can be 3.3V.
Wiring
Use a breadboard to wire up your microcontroller to the mask ROM as per the pinout and sketch. The pinouts may list some /CE or /OE lines. Those can be connected to an unused address line pin listed in the sketch (since the sketch zeroes the unused address lines) or just grounded.
When wiring, make sure you have enough room over the mask ROM to remove it to pop in another (hopefully with similar wiring) without shifting the wiring. Repeat for all the mask ROMs, rewiring if necessary.
Dump!
Upload the sketch to your microcontroller. (With the blue pill, you may need to unplug and plug it back in once you see the "maple_loader" line.) Open Arduino's serial monitor (shift-control-M). If all went well, you will be asked for your ROM size in kb. Enter it (use the NES ROM database), and you should see a bunch of lines of data scrolling. Once done, it should show a CRC. Verify it with the NES ROM database. Copy and paste the output to a fresh file (e.g., using Notepad or your favorite editor) which you can put in the sketch directory with a .hex extension. I will assume you call them prg.hex and chr.hex.
Process
You should now have a PRG and maybe CHR text files with lots of hex numbers. Process them to binary files using the hex2bin.py included with the sketch:
python3 hex2bin.py prg.hex
python3 hex2bin.py chr.hex
You should now have prg.bin and chr.bin.
To generate a ROM file that can be played in an emulator, you need to take a 16-byte iNES header and then append prg.bin and chr.bin. You can get various pieces of information for the header, most importantly the mapper number, from the NES ROM database. You can generate a simple header using nesjoiner.py (also included in the sketch):
python3 nesjoiner.py prg.bin chr.bin outfile.nes mapperID [flags...]
Fill in the prgSize and chrSize in kb, and the mapper ID from the database. You can also add the low nybbles for bytes 6 and 7 at the end of the commandline under "flags". If your cartridge doesn't have a CHR ROM, put "-" for chr.bin.
Try it out in an emulator, e.g., jsNES online.