Go Down

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

werewolfslayr925

[Tengai Makyou ZERO] should dump with the config switches setup the same as for SA-1: https://github.com/sanni/cartreader/wiki/Dumping-SA1-cartridges
Okay, great! I managed to dump the game. However, it consistently gives me a bad checksum and, when comparing the ROM file with a clean dump of the ROM, there are two huge chunks of 00s where game data should be from 00100000 to 002FFFFF and from 00400000 to 004FFFFF. I've tried cycling the cart using the "Cycle Cart" option in the SNES Cart Reader menu, but it doesn't seem to make a difference as subsequent dumps are identical to the first dump. What am I doing wrong?

tamanegi_taro

Wow that is pretty exciting. What connector did you use to read the hucards? I've tried in the past to find a connector that is compatible but have never had much luck.
I used PCI socket 120 pin connector and cut it into 38pin.

Hi skaman,

Would you provide pin assignment between SNES socket and hucard?
I noticed that WR is pulled up in retrode version and 18th and 19th bit are swapped(I think there were reason for this swap). Also, High speed pin and cart detect pins are not connected. So, I assigned those pins to cic data0/1 but just noticed that cic data0/1 are not connected to arduino. So I need to change pin assignment.

Do you know header structure of hucard? I have no idea about how to find rom size/title from data in hucard. So I had to select dump size manually.

To dump street fighter II', read 0x0 to 0x07ffff. Than, I think you need to write something(any value) on 0x1ff0. Than read banked rom 0x080000 to 0x0fffff. Than, write something on 0x1ff1 and read 0x080000 to 0x0fffff again. Keep going until 0x1ff3.

hernan43

Skaman/tamanegi_taro thanks for your hard work on this. I've been looking into it for a while and am excited to see so much progress being made on it!

tamanegi_taro

Hi hernan43,

Welcome!

Hi werewolfslayr925,

You need special method to dump Tengai makyo Zero. Otherwise, you always get corrupted data like that.
I also had problem with dumping Tengai makyo Zero too. Sadly, I don't have the game nor dump anymore and I dumped it using my original old cart reader,  sanni's low level drivers, and port of skaman's Tengaimakyo Zero code. Let me share my code anyway. Hope it helps!

Quote
if(((romChips == 249) || (romChips == 245))&&(romType == 1))
    {
      //Just for Tengai makyo zero. need to fix for other games
      //For Data Size is more than 2MB
      if(DataSize < 2097152)
      {
        DataSizeEx2 = 0;
        DataSizeEx = 0;
      }
      else if(DataSize < 4194304)
      {
        DataSizeEx2 = 0;
        DataSizeEx = DataSize - 2097152;
        DataSize = 2097152;
      }
      else
      {
        DataSizeEx2 = DataSize - 4194304;
        DataSizeEx = 2097152;
        DataSize = 2097152;       
      }

      BankStartEx = 0xE0;
      BankStartEx2 = 0xF0;
     
      for(long BankCurr = BankStart; (BankCurr <= BankEnd) && (DataSize > 0); BankCurr++)
      {
        for(long AddressCurr = AddressLow; (AddressCurr < AddressHigh) && (DataSize > 0); AddressCurr++, DataSize--)
        {
          Serial.write(readBank_SNES(BankCurr, AddressCurr));
        }
      }

      dataOut();
      controlOut_SNES();

      writeBank_SNES(0, 0x4834, 0xFF);

      dataIn();
      controlIn_SNES();

      for(long BankCurr = BankStartEx; (BankCurr <= BankEnd) && (DataSizeEx > 0); BankCurr++)
      {
        for(long AddressCurr = AddressLow; (AddressCurr < AddressHigh) && (DataSizeEx > 0); AddressCurr++, DataSizeEx--)
        {
          Serial.write(readBank_SNES(BankCurr, AddressCurr));
        }
      } 

      dataOut();
      controlOut_SNES();

      writeBank_SNES(0, 0x4833, 0x3);

      dataIn();
      controlIn_SNES();

      for(long BankCurr = BankStartEx2; (BankCurr <= BankEnd) && (DataSizeEx2 > 0); BankCurr++)
      {
        for(long AddressCurr = AddressLow; (AddressCurr < AddressHigh) && (DataSizeEx2 > 0); AddressCurr++, DataSizeEx2--)
        {
          Serial.write(readBank_SNES(BankCurr, AddressCurr));
        }
      } 
      dataOut();
      controlOut_SNES();

      writeBank_SNES(0, 0x4833, 0x2);
      writeBank_SNES(0, 0x4834, 0x0);
     
    }
    else
    {
      //For normal roms
      for(long BankCurr = BankStart; (BankCurr <= BankEnd) && (DataSize > 0); BankCurr++)
      {
        for(long AddressCurr = AddressLow; (AddressCurr < AddressHigh) && (DataSize > 0); AddressCurr++, DataSize--)
        {
          Serial.write(readBank_SNES(BankCurr, AddressCurr));
        }
      } 
    }


werewolfslayr925

#454
Mar 10, 2018, 03:13 pm Last Edit: Mar 10, 2018, 08:20 pm by werewolfslayr925
Hi werewolfslayr925,

You need special method to dump Tengai makyo Zero. Otherwise, you always get corrupted data like that.
I also had problem with dumping Tengai makyo Zero too. Sadly, I don't have the game nor dump anymore and I dumped it using my original old cart reader,  sanni's low level drivers, and port of skaman's Tengaimakyo Zero code. Let me share my code anyway. Hope it helps!

Oh, wow! Thank you so much, tamanegi_taro! I plan to try this out a.s.a.p.! Unfortunately, I'm quite new to Arduino (and am a terrible programmer...). Shall I be able to just plug this code into the section that mentions TMZero in the normal code provided by sanni, or do I need to fiddle with anything else in the normal code so that there aren't any errors?

skaman

@tamanegi_taro,

There's no header for PCE/TG16.  I changed how the Retrode detects the HuCards.  Once the code detects PCE or TG16, then it checks the size by looking for mirrored sections at 128Kb, 256Kb, 384Kb, 512Kb, 768Kb.  The 384Kb size is slightly different as the first 256Kb is mirrored at the start so I simply shift the read to start at 0x40000.  For SF2, if the size is 1MB (default after size check), I then search a small section for "NEC HE" and switch the size if found.

I know how the SF2 mapper works but I haven't been able to get it fully working.  I had the first bank (using $1FF0) displaying but I couldn't get the other banks working.  I physically shifted the plugin around and somehow that broke the bank switching.  It might have been some misaligned pins that allowed the mapper to work at the time so I'm rechecking the hardware.

tamanegi_taro

Oh, wow! Thank you so much, tamanegi_taro! I plan to try this out a.s.a.p.! Unfortunately, I'm quite new to Arduino (and am a terrible programmer...). Shall I be able to just plug this code into the section that mentions TMZero in the normal code provided by sanni, or do I need to fiddle with anything else in the normal code so that there aren't any errors?
Sadly, you can't simply copy-paste the code.
As you can see in my code, there are several register writes before reading banked ROMs.
You need to put those in your code.

Hi skaman.
Thanks it works perfectly.

Code: [Select]

//Confirm the size of ROM - 128Kb, 256Kb, 384Kb, 512Kb, or 768Kb
uint32_t find_rom_size_PCE()
{
  uint32_t currByte;
  uint32_t rom_size;
  uint8_t check_loop;
  uint16_t checksize[3] = {128, 256, 512};

  //Set pins to read PC Engine cart
  pin_read_write_PCE();

  //Read first 64bytes
  for (currByte = 0; currByte < 64; currByte ++) {
      sdBuffer[currByte] = read_byte_PCE(currByte);
  }

  //Initialize rom_size as 768KB
  rom_size = 768;

  //loop to confirm if romsize is 128KB, 256KB or 512KB by finding where the mirror rom start from
  for(check_loop = 0; (check_loop < 3) && (rom_size == 768); check_loop++)
  {
    //Read first 64 bytes from each mirror candidates and compare it against previously read data
    for(currByte = 0; currByte < 64; currByte++)
    {
      if(sdBuffer[currByte] == read_byte_PCE(checksize[check_loop] * 1024UL + currByte))
      {
       
        //If every data matched, it is mirror. Set ROM size and end the loop
        if(currByte == 63)
        {
          rom_size = checksize[check_loop];
        }
      }
      else
      {
        break;
      }
    }
  }
 
  //Another confirmation for 384KB because 384KB hucard has data in 0x0--0x40000 and 0x80000--0xA0000(0x40000 is mirror of 0x00000)
  if(rom_size == 256)
  {
    for(currByte = 0; currByte < 64; currByte++)
    {
      //Check 0x80000 and if it did not match with 0x00000, cart is 384KB
      if(sdBuffer[currByte] != read_byte_PCE(currByte + 512UL * 1024UL))
      {
        rom_size = 384;
        break;
      }
    }
  }

  return rom_size;
}



Now, I need to modify hardware and send design to elecrow again.

skaman

If you ever dump Cyber Dodge or Hisou Kihei, I'd love to find out your results.  I'm having a discussion on No-Intro on the dumps for these two HuCards.

Part of my HuCard detection is to scan the 6502 code for the entry point and initial MPR register settings.  All of the ROMs in the No-Intro set work except for the current versions of Cyber Dodge and Hisou Kihei.  The original dumps for these two cards are detected properly, however, a few years ago, the decision was made to deinterleave these two ROMs and the "fixed" ROMs are now listed in the database.  After deinterleaving the ROMs, the 6502 code is scrambled and not recognizable.

Because the current ROMs fail the check, I decided to redump both HuCards.  My dumps match the original database entries.  I'd love to have someone independently corroborate my findings.

More discussion on this topic here:  http://forums.no-intro.org/viewtopic.php?f=7&t=3040

Oatburner

Hi Skaman,

We spoke over PM a while ago about my SNES cartridge reader I made, and some issues regarding SA-1 BW-RAM writes.

After messing around with it some, I've finally gotten it to work in a way that agrees with what the SNES development book 2 says.

What I've found is that you need to use the BW-RAM mapping and write protection registers from the SNES CPU's point of view, not the SA-1's, so that instead of using 0x2225 to map the BW-RAM block and 0x2227 to disable write protection, you use 0x2224 and 0x2226 respectively. The writes are coming from the Arduino, which is effectively the SNES CPU here, so I think this makes sense.

You mentioned that BW-RAM writes would only work if you first wrote dummy data from 0x0000-0x5FFF before writing the actual data to 0x6000-0x7FFF. After making that change, I can write to 0x6000-0x7FFF and have it work perfectly without first writing dummy data. I suspect that by writing the dummy data from 0x0000-0x5FFF, the SA-1 registers starting at 0x2200 get overwritten in a way that happens to allow it to work right, but I'm not sure.

I also found that the write protection register only protects the area specified in register 0x2228. So, if SNES CPU write protection is on, and the protected area register 0x2228 is set to 0x0 (2KBits), only the first 2KBits will be protected. If you turn off SNES CPU write protection, the entire BW-RAM can be written to. I've attached a picture showing this on a logic analyzer. Only reason I mention it is because the nocash FullSNES page indicates that it isn't certain whether or not the protected area can be set to zero if write protection is disabled.

So, not sure if you're still working on the SNES reader at all, but I thought I would share what I found so that anyone who runs into the same issue as me can figure it out.  :D

skaman

@Oatburner,

Nice job!  I'm glad you figured it out.  I only got the code to the point where it was working and didn't look deeper into it.

I'll modify my Enhanced sketch to implement your findings.  It will be nice to work on the SNES reader stuff after what feels like ages on other projects.

Thanks for the help!

EDIT:  OOPS!  Did you look at the code in the Enhanced sketch?  I think the fix was already done...  I didn't use $2226 but bypassed the protected area by shifting up to the next mirrored block.  I'll add the $2226 register change. Sorry, my memory is so foggy sometimes.  LOL

EDIT2:  Nevermind.  You were referring to the SA-1 Write code in sanni's sketch.  That was the original version that simply worked in spite of the inaccuracies.  The corrected version from the Enhanced sketch was never merged into sanni's code.  :/

tamanegi_taro

If you ever dump Cyber Dodge or Hisou Kihei, I'd love to find out your results.  I'm having a discussion on No-Intro on the dumps for these two HuCards.

Part of my HuCard detection is to scan the 6502 code for the entry point and initial MPR register settings.  All of the ROMs in the No-Intro set work except for the current versions of Cyber Dodge and Hisou Kihei.  The original dumps for these two cards are detected properly, however, a few years ago, the decision was made to deinterleave these two ROMs and the "fixed" ROMs are now listed in the database.  After deinterleaving the ROMs, the 6502 code is scrambled and not recognizable.

Because the current ROMs fail the check, I decided to redump both HuCards.  My dumps match the original database entries.  I'd love to have someone independently corroborate my findings.

More discussion on this topic here:  http://forums.no-intro.org/viewtopic.php?f=7&t=3040
OK. Just bought those two games from amazon japan. Also bought SF2' too. Pretty interesting to investigate special hucard structures. I'll try and let you know the result once those games arrived.

skaman

The other interesting HuCard is Populous.  When I dump it, it overdumps with what I assume is the RAM data.  The RAM data is garbage since the card doesn't have a save battery.

skaman

Ok.  I merged the SA-1 BW-RAM changes from the Enhanced sketch with Oatburner's $2226 register change and managed to get it working.  The code still isn't perfect as the initial SRAM byte needs to be rewritten at the end of the write.  It works and is more accurate than the original version that was in sanni's sketch.

Here's the fixed SA-1 BW-RAM Write Code for sanni's current V30I sketch:
Code: [Select]

void writeSRAM (boolean browseFile) {
...
    // SA1
    else if (romType == SA) {
      long lastByte = (long(sramSize) * 128);
      // Enable CPU Clock
      clockgen.set_freq(357954500ULL, SI5351_CLK1);
      clockgen.output_enable(SI5351_CLK1, 1);

      // Direct writes to BW-RAM (SRAM) in banks 0x40-0x43 don't work
      // Break BW-RAM (SRAM) into 0x2000 blocks
      byte lastBlock = 0;
      lastBlock = lastByte / 0x2000;

      // Writing SRAM on SA1 needs CS(PH3) to be high
//      PORTH |=  (1 << 3);

      // Setup BW-RAM
      // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF
      writeBank_SNES(0, 0x2224, 0);
      // Set 0x2226 (SNES SBWE) to 0x80 Write Enable
      writeBank_SNES(0, 0x2226, 0x80);
      // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area
      writeBank_SNES(0, 0x2228, 0);
      delay(1000);

      // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF
      // Use $2226 (SNES) to write enable the BW-RAM
      byte firstByte = 0;
      for (byte currBlock = 0; currBlock < lastBlock; currBlock++) {
        // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF
        writeBank_SNES(0, 0x2224, currBlock);
        // Set 0x2226 (SNES SBWE) to 0x80 Write Enable
        writeBank_SNES(0, 0x2226, 0x80);
        for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) {
          myFile.read(sdBuffer, 512);
          if ((currBlock == 0) && (currByte == 0x6000)) {
            firstByte = sdBuffer[0];
          }
          for (int c = 0; c < 512; c++) {
              writeBank_SNES(0, currByte + c, sdBuffer[c]);
          }
        }
      }
      // Rewrite First Byte
      writeBank_SNES(0, 0x2224, 0);
      writeBank_SNES(0, 0x2226, 0x80);
      writeBank_SNES(0, 0x6000, firstByte);
      // Disable CPU clock
      clockgen.output_enable(SI5351_CLK1, 0);
...
  }
...
}


Here's the fixed SA-1 BW-RAM Erase Code:
Code: [Select]

boolean eraseSRAM (byte b) {
...
  // SA1
  else if (romType == SA) {
    long lastByte = (long(sramSize) * 128);
    // Enable CPU Clock
    clockgen.set_freq(357954500ULL, SI5351_CLK1);
    clockgen.output_enable(SI5351_CLK1, 1);

    // Direct writes to BW-RAM (SRAM) in banks 0x40-0x43 don't work
    // Break BW-RAM (SRAM) into 0x2000 blocks
    // Use $2224 to map BW-RAM block to 0x6000-0x7FFF
    byte lastBlock = 0;
    lastBlock = lastByte / 0x2000;

    // Writing SRAM on SA1 needs CS(PH3) to be high
//    PORTH |=  (1 << 3);

    // Setup BW-RAM
    // Set 0x2224 (SNES BMAPS) to map SRAM Block 0 to 0x6000-0x7FFF
    writeBank_SNES(0, 0x2224, 0);
    // Set 0x2226 (SNES SBWE) to 0x80 Write Enable
    writeBank_SNES(0, 0x2226, 0x80);
    // Set 0x2228 (SNES BWPA) to 0x00 BW-RAM Write-Protected Area
    writeBank_SNES(0, 0x2228, 0);
    delay(1000);

    // Use $2224 (SNES) to map BW-RAM block to 0x6000-0x7FFF
    // Use $2226 (SNES) to write enable the BW-RAM
    for (byte currBlock = 0; currBlock < lastBlock; currBlock++) {
      // Set 0x2224 (SNES BMAPS) to map SRAM Block to 0x6000-0x7FFF
      writeBank_SNES(0, 0x2224, currBlock);
      // Set 0x2226 (SNES SBWE) to 0x80 Write Enable
      writeBank_SNES(0, 0x2226, 0x80);
      for (long currByte = 0x6000; currByte < 0x8000; currByte += 512) {
        for (int c = 0; c < 512; c++) {
            writeBank_SNES(0, currByte + c, b);
        }
      }
    }
    // Rewrite First Byte
    writeBank_SNES(0, 0x2224, 0);
    writeBank_SNES(0, 0x2226, 0x80);
    writeBank_SNES(0, 0x6000, b);
    // Disable CPU clock
    clockgen.output_enable(SI5351_CLK1, 0);
  }
...
}


tamanegi_taro

Street Fighter 2' HuCARD just arrived!
Had no problem dumping the cart.

I tried to post the whole code but forum gave me error by exceeding 9000 characters...

Code: [Select]

  //If rom size is more than or equal to 512KB, detect Street fighter II'
  if(rom_size >= 512)
  {
    //Look for "NEC HE "
    if(read_byte_PCE(0x7FFF9) == 'N' && read_byte_PCE(0x7FFFA) == 'E'  && read_byte_PCE(0x7FFFB) == 'C'
    && read_byte_PCE(0x7FFFC) == ' ' && read_byte_PCE(0x7FFFD) == 'H' && read_byte_PCE(0x7FFFE) == 'E')
    {
      rom_size = 2560;
    }
  }

************************************
  else if(rom_size == 2560)
  {
    //Dump Street fighter II' Champion Edition
    read_bank_PCE(0, 0x80000, &processed_size, rom_size * 1024UL);

    //Display first bank
    write_byte_PCE(0x1FF0, 0xFF);
    read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL);

    //Display second bank
    write_byte_PCE(0x1FF1, 0xFF);
    read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL);

    //Display third bank
    write_byte_PCE(0x1FF2, 0xFF);
    read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL);

    //Display forth bank
    write_byte_PCE(0x1FF3, 0xFF);
    read_bank_PCE(0x80000, 0x100000, &processed_size, rom_size * 1024UL);
  }
*******************************************

uint8_t write_byte_PCE(uint32_t address, uint8_t data)
{
  uint8_t ret;
  uint8_t address_byte;

  //Set address
  PORTF = address & 0xFF;
  PORTK = (address >> 8) & 0xFF;
  PORTL = (address >> 16) & 0xFF;

  // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total
  __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");

  //Swap bit order for PC Engine HuCARD
  if(pce_internal_mode == HUCARD)
  {
    data = ((data & 0x01) << 7) | ((data & 0x02) << 5) | ((data & 0x04) << 3) | ((data & 0x08) << 1) | ((data & 0x10) >> 1) | ((data & 0x20) >> 3) | ((data & 0x40) >> 5) | ((data & 0x80) >> 7);
  }

  //write byte
  PORTC = data;

  // Set Data Pins (D0-D7) to Output
  DDRC = 0xFF;

  // Switch CS(PH3) and WR(PH5) to LOW
  PORTH &= ~((1 << 3) | (1 << 5));

  // Arduino running at 16Mhz -> one nop = 62.5ns -> 1000ns total
  __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
 
  //Set CE and WR as high
  PORTH |= (1 << 3) | (1 << 5);

  // Set Data Pins (D0-D7) to Input
  DDRC = 0x00;

  // Enable Internal Pullups
  PORTC = 0xFF;
 
  //return read data
  return ret;
 
}





skaman

Nice work!

I'm still working on the Retrode version.  It should be simple but it never is with so much going on on the Retrode. :/

Take Care!

Go Up