//****************************************** // SNES Satellaview 8M Memory pack code by tamanegi_taro // Revision 1.0.0 October 22nd 2018 // Added BSX Sram, copied from skamans enhanced sketch //sanni //****************************************** #ifdef enable_SV /****************************************** 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 = "Read BS-X Sram"; static const char svFlashMenuItem4[] PROGMEM = "Write BS-X Sram"; static const char svFlashMenuItem5[] PROGMEM = "Back"; static const char* const menuOptionsSVFlash[] PROGMEM = { svFlashMenuItem1, svFlashMenuItem2, svFlashMenuItem3, svFlashMenuItem4, svFlashMenuItem5 }; void svMenu() { // create menu with title and 3 options to choose from unsigned char mainMenu; // Copy menuOptions out of progmem convertPgm(menuOptionsSVFlash, 5); mainMenu = question_box(F("Satellaview 8M Memory"), menuOptions, 5, 0); // wait for user choice to come back from the question box menu switch (mainMenu) { // Read memory pack case 0: // Change working dir to root sd.chdir("/"); readROM_SV(); break; // Write memory pack case 1: // Change working dir to root sd.chdir("/"); writeROM_SV(); break; // Read BS-X Sram case 2: // Change working dir to root sd.chdir("/"); readSRAM_SV(); break; // Write BS-X Sram case 3: // Change working dir to root sd.chdir("/"); writeSRAM_SV(); unsigned long wrErrors; wrErrors = verifySRAM_SV(); if (wrErrors == 0) { println_Msg(F("Verified OK")); display_Update(); } else { print_STR(error_STR, 0); print_Msg(wrErrors); print_STR(_bytes_STR, 1); print_Error(did_not_verify_STR); } wait(); break; // Reset case 4: resetArduino(); break; } } /****************************************** 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 i2c_found = clockgen.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); if (i2c_found) { 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); } #ifdef clockgen_installed else { display_Clear(); print_FatalError(F("Clock Generator not found")); } #endif // 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); } /****************************************** Low level functions *****************************************/ // Write one byte of data to a location specified by bank and address, 00:0000 void writeBank_SV(byte myBank, word myAddress, byte myData) { PORTL = myBank; PORTF = myAddress & 0xFF; PORTK = (myAddress >> 8) & 0xFF; PORTC = myData; // Arduino running at 16Mhz -> one nop = 62.5ns // Wait till output is stable __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"); // Switch WR(PH5) to LOW PORTH &= ~(1 << 5); // Leave WR low for at least 60ns __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"); // Switch WR(PH5) to HIGH PORTH |= (1 << 5); // Leave WR high for at least 50ns __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"); } // Read one byte of data from a location specified by bank and address, 00:0000 byte readBank_SV(byte myBank, word myAddress) { PORTL = myBank; PORTF = myAddress & 0xFF; PORTK = (myAddress >> 8) & 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"); // Read byte tempByte = PINC; return tempByte; } /****************************************** SatellaView BS-X Sram functions *****************************************/ void readSRAM_SV() { // set control controlIn_SNES(); // Get name, add extension and convert to char array for sd lib strcpy(fileName, "BSX.srm"); // create a new folder for the save file EEPROM_readAnything(0, foldern); sprintf(folder, "SNES/SAVE/BSX/%d", foldern); sd.mkdir(folder, true); sd.chdir(folder); // write new folder number back to eeprom foldern = foldern + 1; EEPROM_writeAnything(0, foldern); //open file on sd card if (!myFile.open(fileName, O_RDWR | O_CREAT)) { print_FatalError(sd_error_STR); } readBank_SV(0x10, 0); // Preconfigure to fix corrupt 1st byte //startBank = 0x10; endBank = 0x17; CS low for (byte BSBank = 0x10; BSBank < 0x18; BSBank++) { //startAddr = 0x5000 for (long currByte = 0x5000; currByte < 0x6000; currByte += 512) { for (unsigned long c = 0; c < 512; c++) { sdBuffer[c] = readBank_SV(BSBank, currByte + c); } myFile.write(sdBuffer, 512); } } // Close the file: myFile.close(); // Signal end of process display_Clear(); print_Msg(F("Saved to ")); print_Msg(folder); println_Msg(F("/...")); display_Update(); wait(); } void writeSRAM_SV() { filePath[0] = '\0'; sd.chdir("/"); fileBrowser(F("Select srm file")); // Create filepath sprintf(filePath, "%s/%s", filePath, fileName); //clear the screen display_Clear(); //open file on sd card if (myFile.open(filePath, O_READ)) { // Set pins to output dataOut(); // Set RST RD WR to High and CS to Low controlOut_SNES(); println_Msg(F("Writing sram...")); display_Update(); // Write to sram bank for (byte currBank = 0x10; currBank < 0x18; currBank++) { //startAddr = 0x5000 for (long currByte = 0x5000; currByte < 0x6000; currByte += 512) { myFile.read(sdBuffer, 512); for (unsigned long c = 0; c < 512; c++) { //startBank = 0x10; CS low writeBank_SV(currBank, currByte + c, sdBuffer[c]); } } draw_progressbar(((currBank - 0x10) * 0x1000), 32768); } // Finish progressbar draw_progressbar(32768, 32768); delay(100); // Set pins to input dataIn(); // Close the file: myFile.close(); println_Msg(""); println_Msg(F("SRAM writing finished")); display_Update(); } else { print_Error(F("File doesnt exist")); } } // Check if the SRAM was written without any error unsigned long verifySRAM_SV() { //open file on sd card if (myFile.open(filePath, O_READ)) { // Variable for errors writeErrors = 0; // Set control controlIn_SNES(); //startBank = 0x10; endBank = 0x17; CS low for (byte BSBank = 0x10; BSBank < 0x18; BSBank++) { //startAddr = 0x5000 for (long currByte = 0x5000; currByte < 0x6000; currByte += 512) { //fill sdBuffer myFile.read(sdBuffer, 512); for (unsigned long c = 0; c < 512; c++) { if ((readBank_SV(BSBank, currByte + c)) != sdBuffer[c]) { writeErrors++; } } } } // Close the file: myFile.close(); return writeErrors; } else { print_Error(F("Can't open file")); return 1; } } /****************************************** SatellaView 8M Memory Pack functions *****************************************/ // 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.bs"); // create a new folder for the save file EEPROM_readAnything(0, foldern); sprintf(folder, "SNES/ROM/%s/%d", "MEMPACK", foldern); sd.mkdir(folder, true); sd.chdir(folder); //clear the screen display_Clear(); print_STR(saving_to_STR, 0); print_Msg(folder); println_Msg(F("/...")); display_Update(); // write new folder number back to eeprom foldern = foldern + 1; EEPROM_writeAnything(0, foldern); //open file on sd card if (!myFile.open(fileName, O_RDWR | O_CREAT)) { print_FatalError(create_file_STR); } // 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_SV(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(); } 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_SV(0, 65503), readBank_SV(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_FatalError(F("Error: Must use BS-X cart")); } //Display file Browser and wait user to select a file. Size must be 1MB. filePath[0] = '\0'; sd.chdir("/"); fileBrowser(F("Select BS 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_SV(0x0C, 0x5000, 0x80); //Modify write enable register writeBank_SV(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_SV(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_SV(0xC0, 0x0000, 0x10); //Program Byte writeBank_SV(currBank, currByte, myFile.read()); writeBank_SV(0xC0, 0x0000, 0x70); //Status Mode writeCheck_SV(); } } writeBank_SV(0xC0, 0x0000, 0x70); //Status Mode writeCheck_SV(); writeBank_SV(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 print_STR(verifying_STR, 1); 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_SV(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")); } } void eraseCheck_SV(void) { byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SV(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_SV(0xC0, 0x0004); } // Switch to write dataOut(); controlOut_SNES(); } void supplyCheck_SV(void) { byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SV(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_SV(0xC0, 0x0004); } // Switch to write dataOut(); controlOut_SNES(); } void writeCheck_SV(void) { byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SV(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_SV(0xC0, 0x0000); } // Switch to write dataOut(); controlOut_SNES(); } void detectCheck_SV(void) { int i = 0; byte ret; dataIn(); controlIn_SNES(); // Read register ret = readBank_SV(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_SV(0xC0, 0x0002); } // Switch to write dataOut(); controlOut_SNES(); } void eraseAll_SV(void) { dataOut(); controlOut_SNES(); writeBank_SV(0xC0, 0x0000, 0x50); //Clear Status Registers writeBank_SV(0xC0, 0x0000, 0x71); //Status Mode supplyCheck_SV(); writeBank_SV(0xC0, 0x0000, 0xA7); //Chip Erase writeBank_SV(0xC0, 0x0000, 0xD0); //Confirm writeBank_SV(0xC0, 0x0000, 0x71); //Status Mode eraseCheck_SV(); writeBank_SV(0xC0, 0x0000, 0xFF); //Teriminate } #endif //****************************************** // End of File //******************************************