Go Down

Topic: ROM-Reader for Super Nintendo / Super Famicom Game Cartridges (Read 99396 times) previous topic - next topic

MichlK

Apr 07, 2013, 03:51 pm Last Edit: Apr 07, 2013, 05:20 pm by MichlK Reason: 1
Hi Arduino community,

would you like to read the ROM-contents of your Super Nintendo / Super Famicom game cartridges?

Using an Arduino and some 74HC595 shift-out registers, you can build a game cartridge-reader. Schematics and source code are included (please login to get access to the attachments). It even reads the Japanese game cartridge "Star Ocean" (one of the few game cartridges that utilise the "S-DD1" chip for graphics compression).

Have fun & best regards,
Michael
PS: This is my first Arduino-based project and I am an amateur in electrical engineering - it may contain severe errors and blow up your entire equipment, but at least it worked fine for me without doing any damage to my equipment... :)

sanni

I followed your instructions and build the dumper myself. It's working great.  :)
Did you ever implement sram reading?

MichlK

Nice!  :)

Sorry, I did never dump actual SRAM-data from my carts.

However, using the same sources as I did, you should be able to implement it quite easily:
1. Cartridge addressing scheme, i.e. which lines you have to set low / high to tell the cartridge that you want to read SRAM:
http://www.emulatronia.com/doctec/consolas/snes/sneskart.html#cartridgeaddressing
2. Code in Python for the Raspberry Pi by waterbury that includes SRAM-reading (just do a text-search for SRAM on this page):
https://github.com/waterbury/SNES-Pi/blob/master/MCP23017_CartReader/cart_reader.py
3. Reading SRAM-size and decoding Romtype (lorom / hirom) from the cart-header is already implemented in my code...

HTH  :) & best regards
Michael

sanni

With the help of the SdFat library and by changing "Serial.write(dumpByte(currBank, currByte));" to "myFile.write(dumpByte(currBank, currByte));" it was very easy to dump the rom to an SD card instead of sending it over serial to a PC.
It takes 12.5 minutes for a 1MB LoROM/SlowROM game compared to 18 minutes over serial.
And 100 minutes for a 8MB ExHiROM/FastROM.

You just got to love the arduino :)

MichlK

Ah, nice speed boost!

Sorry, I am curious  ;) : Is it an Arduino Pro Mini and did you use the analog pins for SPI-communication to the sd card reader?

Best regards
Michael

sanni

#5
May 08, 2014, 09:27 pm Last Edit: May 14, 2014, 08:11 pm by sanni Reason: 1
Yes it's one of those $4 Arduino Pro Mini's from ebay with the Atmega328.
I'm using pins 10 to 13 for the SD card and the analog pins for the shift register.
Had to remove the debug features though because the Arduino IDE complained about low memory now that the SdFat lib is also in use.


alkex

#6
May 14, 2014, 11:52 am Last Edit: May 14, 2014, 11:56 am by nutty Reason: 1
Hi Guys!

@MichlK: Thanks for posting this project, I have been fantasizing over a snes hardware sampler for ages!

I ordered the parts to make your reader and have already been studiying how the audio is stored. Hope to parse the rom and dump the samples onto an sd to play back afterwards via midi.

This is going to be fun and exciting! I'll definitely keep you posted

Thanks again,
Alex

sanni

I played a little bit more with your code as a base  :smiley-mr-green:







MichlK


I played a little bit more with your code as a base  :smiley-mr-green:


Wow - amazing what you have done there! 8) Thanks for sharing!!!

Best regards
Michael
PS: From Chrono Trigger to EarthBound - the dumper is in very good company... ;)

tylau0

#9
Sep 01, 2014, 02:30 am Last Edit: Sep 01, 2014, 03:40 pm by tylau0 Reason: 1
I also follow the instruction and have my dumper done successfully. Thanks for the sharing!

I am extending my code to support carts with SPC7110, with partial success -- I can read most of the ROM data correctly, but have to be done by parts.

SPC7110 is a special chip that appears in a few SNES carts in 1995-1997. Carts with this chip features a 8Mb program ROM and a variable-size data ROM. In Far East of Eden Zero (Tengai Makyo Zero), the size of the data ROM is 32Mb.

A few things that we need to know in order to read the data from the ROMs.

  • If an unlock sequence is not written to registered addressed 0x4834, only the first 8Mb of the data ROM can be read. Attempts to read the other parts just give you the same first 8Mb of the data ROM. The unlock sequence is as follows: 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00.

  • By default, the program ROM is mapped to addresses 0xc00000 to 0xcfffff while the first 24Mb of the data ROM is mapped to 0xd00000 to 0xffffff.

  • Register at 0x4833 can be used to decide which 8Mb of data ROM data be mapped to addresses 0xf00000 to 0xffffff. If it has 3 as its value, the last 8Mb of the 40Mb data ROM is mapped to that range of addresses. To write to that register, it appears that we need to put 0xff to register 0x4834, as shown in the ucon64 swc.c code.



For some unknown reason, my reader fails to write the registers most of the time. I need to take the following alternative approach.


1)    Upload and run the unlock0 script which just makes sure we have correct initial register values. Once you start running the script, open the Serial Monitor. If the program says 0x4844-0x4841 are not of values 0, 2, 1, 0, close the serial monitor, disconnect, reconnect the Arduino USB/power and open the serial monitor again.
2)    Without disconnecting the Arduino power, upload and run the read0 script. This time, use a terminal console to obtain the data stream from Arduino. This gives you the program ROM and the 1st 8Mb of the data ROM data. Note you may need to manually edit the two hex starting at 00100000 to 01.
3)    Upload and run the unlock1 script which just unlocks the cart, without changing the register at 0x4833. Once you start running the script, open the Serial Monitor. If the program says 0x4844 is not of value 7, close the serial monitor, disconnect, reconnect the Arduino USB/power and open the serial monitor again. Only when the program says the value is 7, we are sure the unlock sequence has been successfully written and the data ROM unlock.
4)    Without disconnecting the Arduino power, upload and run the read1 script. This time, use a terminal console to obtain the data stream from Arduino. This gives you the 2nd and 3rd 8Mb of the data ROM data. Note that you may need to manually edit the two hex at the beginning to 20.
5)    Upload and run the unlock2 script which unlocks the cart and changes the register at 0x4833 to 3. Once you start running the script, open the Serial Monitor. If the program says 0x4844 is not of value 7, close the serial monitor, disconnect, reconnect the Arduino USB/power and open the serial monitor again. Only when the program says the value is 7, we are sure that the data ROM has been unlocked. The program should also say the value of the 0x4833 register is 3.
6)    Without disconnecting the Arduino power, upload and run the read2 script. This time, use a terminal console to obtain the data stream from Arduino. This gives you the program ROM and the last 8Mb of the data ROM data. Note that you may need to manually edit the two hex at the beginning to BF.
7)    Use some program to join the file obtained in the above steps. Now you should have got the complete ROM.

MichlK



I am extending my code to support carts with SPC7110, with partial success -- I can read most of the ROM data correctly, but have to be done by parts.
...
For some unknown reason, my reader fails to write the registers most of the time. I need to take the following alternative approach.
...


Very cool - SPC7110 support - thanks for sharing!  :)

I do not own a cart with the SPC7110 (let alone the legendary Far East of Eden Zero ;)) but as far as I can think of, there may be two reasons for the problem with writing the registers:
1. I often had the problem that the connection between the socket for the cart and the jumper wires was not good enough, so I put a folded sheet of paper between the two rows of the jumper wires to improve connectivity...
2. I do not know, which timing is expected when sending the unlock codes (needs to be clocked???). Maybe a small delay between sending each byte could help? Switching between 0x00 and 0x80 would not necessarily mean that a specific timing is necessary, but as the sequence includes two consecutive 0x80, I wonder if there is a specific timing and/or if you e.g. can read the registers in between the sequence and the value changes shortly after you write to it, so you know that you can send the next byte...

Just my 2 cents...

Best regards
Michael

starkes

Hey guys

Made one of these, works pretty good. Can't seem to dump some common games like mario kart, mario world, pilotwings, and a few others but works pretty good for the rest of them. I haven't really looked into it yet, if anybody knows whats up with that let me know.

Anyways. I managed to get it to go quite a bit faster.

I cut the code down a bit, took out the delays (the snes would read from the cart way faster than the arduino, seems to work) and I used the fastDigitalWrite and fastDigitalRead functions I found here which are closer to bare port manipulation than the stock digitalRead and digitalWrite. A bunch of things are changed from variables to defines so that the pins have fixed values at compile time. To make the fastDigital functions work right.

So far it looks like it takes 177s (2m 57s) to dump a 1mb cart, where before it was taking 1113s (18m 33s). About 6x faster.

I made a little python program for it too that makes it a little easier to work with and shows progress as it dumps. It doesn't start dumping automatically now, it waits for "R" to be sent to it. So the python program can tell it to dump or do other things, like "I" to just give you cart info or read/write SRAM (which is what I plan to do next, has anyone done this with theirs yet?). You can swap cartridges without unplugging because it'll always be idle without the computer telling it to do something.

If anyone wants the code let me know. It's a bit of a mess.

sanni

Here is my current version of MichlK's code, it's a shield for an Arduino Mega.
The rom gets saved to an SD card. You can also read/write save files to the SRAM, display information about the cartridge on the LCD or calculate the checksum of your rom dump.




You control it using the push button on the left. One click moves the selection down, a double click moves it up and a long press executes the current menu option.

I used this Prototype PCB from ebay item # 201205316404 (you need to cut a trace on the top of the pcb leading from the switch to the reset pin)
That SD module from ebay item # 290901349431
And a white 0.96" IIC I2C 128X64 OLED LCD from ebay item # 291216700457

I also incorporated the DigitalIOPerformance lib now and dumping an 1MByte (8Mbit) LoRom game takes 62 seconds while dumping a 4Mbyte (32Mbit) HiRom game takes 4 minutes and 10 seconds.

skaman

Success!

I finally finished building a duplicate of sanni's cart reader.  The hardest part was finding the cart connector (aside from waiting on parts from China). 

If anyone needs a SNES cart connector, I located a Centronic 46P connector.  I contacted a distributor and found out that they sell on eBay.  The seller is wonderco_buy and they added the part listing "Industrial Card Edge Slot Socket Connector 23x2P 46P 2.54mm 0.1" 3A".  Shipping to the US from Taiwan is fast (less than a week).

I want to thank MichlK and sanni for all of their help with this project!

skaman

If anyone wants to dump SDD-1 carts (Star Ocean or Street Fighter Alpha/Zero 2), then you must enable verifySORead.  Set verifySORead=1 in the code.  Otherwise, the dump will have random corrupt bytes.  The dump will take longer but it will produce a proper dump.

To dump Street Fighter Alpha/Zero 2, changes need to be made to the dump code.  It requires the same dumping setup as Star Ocean.  Street Fighter Alpha/Zero 2 is romChips=67.

Code: [Select]
if ((romChips != 67)&&(romChips != 69))
...
if(romChips==69)
{
    romSize = 48;
      numBanks = 96;  //HiROM banks
  }
  // Street Fighter Alpha/Zero 2
  else if(romChips==67)
{
    romSize = 32;
      numBanks = 64;  //HiROM banks
  }


I also found that sanni's calc_checksum needed an addition to handle Star Ocean (and Tales of Phantasia):

Code: [Select]
    // Star Ocean/Tales of Phantasia Fix 6MB (48Mbit)
    else if (calcFilesize == 48) {
      // Add the 4MB (32Mbit) start
      for (unsigned long j = 0; j < 4194304; j++) {
        calcChecksum += myFile.read();
      }
      // Add the 2MB (16Mbit) end
      for (unsigned long j = 0; j < 2097152; j++) {
        calcChecksumChunk += myFile.read();
      }
      calcChecksum +=  2 * calcChecksumChunk;
    }


One last change was to sanni's code that write/verify SRAM.  There needs to be a delay in the dumpByte() code or the VERIFY.SRM will always be wrong.  The problem is that any delay added to dumpByte() ripples through all routines that call it which greatly slows everything down. 

My solution was a small modification to the readSRAM() code to fix the problem.  The SRAM dumps and verifies quickly and now it does it properly.

Code: [Select]
  // Dump LoRom
  if (romType == 0) {
    dumpByte(112, 0, false); // Preconfigure to fix the corrupt 1st byte
    // Sram size
    long lastByte = (long(sramSize) * 128);
    for (long currByte = 0; currByte < lastByte; currByte++) { //startAddr = 0x0000
      myFile.write(dumpByte(112, currByte, false)); //startBank = 0x70; CS low
    }
  }

  // Dump HiRom
  else  if (romType == 1) {
     dumpByte(48, 24576, true); // Preconfigure to fix the corrupt 1st byte
    // Sram size
    long lastByte = (long(sramSize) * 128) + 24576;
    for (long currByte = 24576; currByte < lastByte; currByte++) { //startAddr = 0x6000
      myFile.write(dumpByte(48, currByte, true)); //startBank = 0x30; CS high
    }
  }

Go Up