//****************************************** // SNES Satellaview 8M Memory pack code by tamanegi_taro // Revision 1.0.0 October 22nd 2018 //****************************************** /****************************************** Satellaview 8M Memory Pack ******************************************/ /****************************************** Prototype Declarations *****************************************/ /* Hoping that sanni will use this progressbar function */ extern void draw_progressbar(uint32_t processedsize, uint32_t totalsize); //void svMenu(); void readROM_SV(); //void setup_SV(); void writeROM_SV (void); void eraseCheck_SV(void); void supplyCheck_SV(void); void writeCheck_SV(void); void detectCheck_SV(void); void eraseAll_SV(void); /****************************************** Variables *****************************************/ //No global variables /****************************************** Menu *****************************************/ // SV flash menu items static const char svFlashMenuItem1[] PROGMEM = "Read Memory Pack"; static const char svFlashMenuItem2[] PROGMEM = "Write Memory Pack"; static const char svFlashMenuItem3[] PROGMEM = "Back"; static const char* const menuOptionsSVFlash[] PROGMEM = {svFlashMenuItem1, svFlashMenuItem2, svFlashMenuItem3}; void svMenu() { // create menu with title and 3 options to choose from unsigned char mainMenu; // Copy menuOptions out of progmem convertPgm(menuOptionsSVFlash, 3); mainMenu = question_box(F("Satellaview 8M Memory"), menuOptions, 3, 0); // wait for user choice to come back from the question box menu switch (mainMenu) { // Read memory pack case 0: readROM_SV(); break; // Write memory pack case 1: writeROM_SV(); break; // Reset case 2: resetArduino(); break; } } // Read memory pack to SD card void readROM_SV() { // Set control dataIn(); controlIn_SNES(); // Get name, add extension and convert to char array for sd lib strcpy(fileName, "MEMPACK.sfc"); // create a new folder for the save file EEPROM_readAnything(10, foldern); sprintf(folder, "SNES/ROM/%s/%d", "MEMPACK", foldern); sd.mkdir(folder, true); sd.chdir(folder); //clear the screen display_Clear(); print_Msg(F("Saving to ")); print_Msg(folder); println_Msg(F("/...")); display_Update(); // write new folder number back to eeprom foldern = foldern + 1; EEPROM_writeAnything(10, foldern); //open file on sd card if (!myFile.open(fileName, O_RDWR | O_CREAT)) { print_Error(F("Can't create file on SD"), true); } // Read Banks for (int currBank = 0x40; currBank < 0x50; currBank++) { // Dump the bytes to SD 512B at a time for (long currByte = 0; currByte < 65536; currByte += 512) { draw_progressbar((currBank - 0x40) * 0x10000 + currByte, 0x100000); for (int c = 0; c < 512; c++) { sdBuffer[c] = readBank_SNES(currBank, currByte + c); } myFile.write(sdBuffer, 512); } } draw_progressbar(0x100000, 0x100000); //Finish drawing progress bar // Close the file: myFile.close(); println_Msg(F("Read pack completed")); display_Update(); wait(); } /****************************************** Setup *****************************************/ void setup_SV() { // Set cicrstPin(PG1) to Output DDRG |= (1 << 1); // Output a high signal until we're ready to start PORTG |= (1 << 1); // Set cichstPin(PG0) to Input DDRG &= ~(1 << 0); // Adafruit Clock Generator clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLA); clockgen.set_pll(SI5351_PLL_FIXED, SI5351_PLLB); clockgen.set_freq(2147727200ULL, SI5351_CLK0); clockgen.set_freq(307200000ULL, SI5351_CLK2); clockgen.output_enable(SI5351_CLK0, 1); clockgen.output_enable(SI5351_CLK1, 0); clockgen.output_enable(SI5351_CLK2, 1); // Set Address Pins to Output //A0-A7 DDRF = 0xFF; //A8-A15 DDRK = 0xFF; //BA0-BA7 DDRL = 0xFF; //PA0-PA7 DDRA = 0xFF; // Set Control Pins to Output RST(PH0) CS(PH3) WR(PH5) RD(PH6) DDRH |= (1 << 0) | (1 << 3) | (1 << 5) | (1 << 6); // Switch RST(PH0) and WR(PH5) to HIGH PORTH |= (1 << 0) | (1 << 5); // Switch CS(PH3) and RD(PH6) to LOW PORTH &= ~((1 << 3) | (1 << 6)); // Set Refresh(PE5) to Output DDRE |= (1 << 5); // Switch Refresh(PE5) to LOW (needed for SA-1) PORTE &= ~(1 << 5); // Set CPU Clock(PH1) to Output DDRH |= (1 << 1); //PORTH &= ~(1 << 1); // Set IRQ(PH4) to Input DDRH &= ~(1 << 4); // Activate Internal Pullup Resistors //PORTH |= (1 << 4); // Set expand(PG5) to output DDRG |= (1 << 5); // Output High PORTG |= (1 << 5); // Set Data Pins (D0-D7) to Input DDRC = 0x00; // Enable Internal Pullups //PORTC = 0xFF; // Unused pins // Set wram(PE4) to Output DDRE |= (1 << 4); //PORTE &= ~(1 << 4); // Set pawr(PJ1) to Output DDRJ |= (1 << 1); //PORTJ &= ~(1 << 1); // Set pard(PJ0) to Output DDRJ |= (1 << 0); //PORTJ &= ~(1 << 0); // Start CIC by outputting a low signal to cicrstPin(PG1) PORTG &= ~(1 << 1); // Wait for CIC reset delay(1000); } void writeROM_SV (void) { // Get Checksum as string to make sure that BS-X cart is inserted dataIn(); controlIn_SNES(); sprintf(checksumStr, "%02X%02X", readBank_SNES(0, 65503), readBank_SNES(0, 65502)); //if CRC is not 8B86, BS-X cart is not inserted. Display error and reset if (strcmp("8B86", checksumStr) != 0) { display_Clear(); print_Error(F("Error: Must use BS-X cart"), true); } //Display file Browser and wait user to select a file. Size must be 1MB. filePath[0] = '\0'; sd.chdir("/"); fileBrowser(F("Select sfc file")); // Create filepath sprintf(filePath, "%s/%s", filePath, fileName); display_Clear(); //open file on sd card if (myFile.open(filePath, O_READ)) { fileSize = myFile.fileSize(); if (fileSize != 0x100000) { println_Msg(F("File must be 1MB")); display_Update(); myFile.close(); wait(); return; } //Disable 8M memory pack write protection dataOut(); controlOut_SNES(); writeBank_SNES(0x0C, 0x5000, 0x80); //Modify write enable register writeBank_SNES(0x0E, 0x5000, 0x80); //Commit register modification //Erase memory pack println_Msg(F("Erasing pack...")); display_Update(); eraseAll_SV(); //Blank check //Set pins to input dataIn(); controlIn_SNES(); println_Msg(F("Blank check...")); display_Update(); for (int currBank = 0xC0; currBank < 0xD0; currBank++) { draw_progressbar(((currBank - 0xC0) * 0x10000), 0x100000); for (long currByte = 0; currByte < 65536; currByte++) { if (0xFF != readBank_SNES(currBank, currByte)) { println_Msg(F("")); println_Msg(F("Erase failed")); display_Update(); myFile.close(); wait(); return; } } } draw_progressbar(0x100000, 0x100000); //Write memory pack dataOut(); controlOut_SNES(); println_Msg(F("Writing pack...")); display_Update(); for (int currBank = 0xC0; currBank < 0xD0; currBank++) { draw_progressbar(((currBank - 0xC0) * 0x10000), 0x100000); for (long currByte = 0; currByte < 65536; currByte++) { writeBank_SNES(0xC0, 0x0000, 0x10); //Program Byte writeBank_SNES(currBank, currByte, myFile.read()); writeBank_SNES(0xC0, 0x0000, 0x70); //Status Mode writeCheck_SV(); } } writeBank_SNES(0xC0, 0x0000, 0x70); //Status Mode writeCheck_SV(); writeBank_SNES(0xC0, 0x0000, 0xFF); //Terminate write draw_progressbar(0x100000, 0x100000); //Verify dataIn(); //Set pins to input controlIn_SNES(); myFile.seekSet(0); // Go back to file beginning println_Msg(F("Verifying...")); display_Update(); for (int currBank = 0xC0; currBank < 0xD0; currBank++) { draw_progressbar(((currBank - 0xC0) * 0x10000), 0x100000); for (long currByte = 0; currByte < 65536; currByte++) { if (myFile.read() != readBank_SNES(currBank, currByte)) { println_Msg(F("")); println_Msg(F("Verify failed")); display_Update(); myFile.close(); wait(); return; } } } // Close the file: myFile.close(); draw_progressbar(0x100000, 0x100000); println_Msg(F("Finished successfully")); display_Update(); wait(); } else { print_Error(F("File doesn't exist"), false); } } void eraseCheck_SV(void) { byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SNES(0xC0, 0x0004); // CE or OE must be toggled with each subsequent status read or the // completion of a program or erase operation will not be evident. while ((ret & 0x80) == 0x00) { //Wait until X.bit7 = 1 controlOut_SNES(); // Switch CS(PH3) High PORTH |= (1 << 3); // Leave CE high for at least 60ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); controlIn_SNES(); // Leave CE low for at least 50ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); // Read register ret = readBank_SNES(0xC0, 0x0004); } // Switch to write dataOut(); controlOut_SNES(); } void supplyCheck_SV(void) { byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SNES(0xC0, 0x0004); // CE or OE must be toggled with each subsequent status read or the // completion of a program or erase operation will not be evident. while ((ret & 0x08) == 0x08) { //Wait until X.bit3 = 0 controlOut_SNES(); // Switch CS(PH3) High PORTH |= (1 << 3); // Leave CE high for at least 60ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); controlIn_SNES(); // Leave CE low for at least 50ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); // Read register ret = readBank_SNES(0xC0, 0x0004); } // Switch to write dataOut(); controlOut_SNES(); } void writeCheck_SV(void) { byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SNES(0xC0, 0x0000); // CE or OE must be toggled with each subsequent status read or the // completion of a program or erase operation will not be evident. while ((ret & 0x80) == 0x00) { controlOut_SNES(); // Switch CS(PH3) High PORTH |= (1 << 3); // Leave CE high for at least 60ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); controlIn_SNES(); // Leave CE low for at least 50ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); // Read register ret = readBank_SNES(0xC0, 0x0000); } // Switch to write dataOut(); controlOut_SNES(); } void detectCheck_SV(void) { int i = 0; byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SNES(0xC0, 0x0002); // CE or OE must be toggled with each subsequent status read or the // completion of a program or erase operation will not be evident. while ((ret & 0x80) == 0x00) { i++; if ( i > 10000) { //timeout break; } controlOut_SNES(); // Switch CS(PH3) High PORTH |= (1 << 3); // Leave CE high for at least 60ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); controlIn_SNES(); // Leave CE low for at least 50ns __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"); // Read register ret = readBank_SNES(0xC0, 0x0002); } // Switch to write dataOut(); controlOut_SNES(); } void eraseAll_SV(void) { dataOut(); controlOut_SNES(); writeBank_SNES(0xC0, 0x0000, 0x50); //Clear Status Registers writeBank_SNES(0xC0, 0x0000, 0x71); //Status Mode supplyCheck_SV(); writeBank_SNES(0xC0, 0x0000, 0xA7); //Chip Erase writeBank_SNES(0xC0, 0x0000, 0xD0); //Confirm writeBank_SNES(0xC0, 0x0000, 0x71); //Status Mode eraseCheck_SV(); writeBank_SNES(0xC0, 0x0000, 0xFF); //Teriminate } //****************************************** // End of File //******************************************