From ce4f7aeeeea2a5261a06c0e7c2e5de00ef116cae Mon Sep 17 00:00:00 2001 From: sanni Date: Mon, 22 Oct 2018 20:29:49 +0200 Subject: [PATCH] V2.2: Add Satellaview support Supported carts so far: -BS-X Sorewa Namaewo Nusumareta Machino Monogatari --- Cart_Reader/Cart_Reader.ino | 12 +- Cart_Reader/SNES.ino | 22 +- Cart_Reader/SV.ino | 457 ++++++++++++++++++++++++++++++++++++ 3 files changed, 480 insertions(+), 11 deletions(-) create mode 100644 Cart_Reader/SV.ino diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index e9a3119..cef8c98 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -2,8 +2,8 @@ Cartridge Reader for Arduino Mega2560 Author: sanni - Date: 20-10-2018 - Version: 2.1 + Date: 22-10-2018 + Version: 2.2 SD lib: https://github.com/greiman/SdFat LCD lib: https://github.com/adafruit/Adafruit_SSD1306 @@ -37,7 +37,7 @@ vogelfreiheit - N64 flashram fix **********************************************************************************/ -char ver[5] = "2.1"; +char ver[5] = "2.2"; /****************************************** Define Starting Point @@ -138,6 +138,7 @@ SdFile myFile; #define mode_MD 11 #define mode_EPROM 12 #define mode_PCE 13 +#define mode_SV 14 /****************************************** Variables @@ -1358,6 +1359,9 @@ void loop() { else if (mode == mode_PCE) { pceMenu(); } + else if (mode == mode_SV) { + svMenu(); + } else { display_Clear(); println_Msg(F("Menu Error")); @@ -1375,4 +1379,4 @@ void loop() { //****************************************** // End of File -//****************************************** +//****************************************** diff --git a/Cart_Reader/SNES.ino b/Cart_Reader/SNES.ino index 64288bd..1d64c2f 100644 --- a/Cart_Reader/SNES.ino +++ b/Cart_Reader/SNES.ino @@ -30,9 +30,10 @@ byte cx4Map = 0; // SNES/Nintendo Power SF Memory start menu static const char snsMenuItem1[] PROGMEM = "Super Nintendo"; static const char snsMenuItem2[] PROGMEM = "NPower SF Memory"; -static const char snsMenuItem3[] PROGMEM = "HiROM repro"; -static const char snsMenuItem4[] PROGMEM = "LoROM repro"; -static const char* const menuOptionsSNS[] PROGMEM = {snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4}; +static const char snsMenuItem3[] PROGMEM = "Satellaview"; +static const char snsMenuItem4[] PROGMEM = "HiROM repro"; +static const char snsMenuItem5[] PROGMEM = "LoROM repro"; +static const char* const menuOptionsSNS[] PROGMEM = {snsMenuItem1, snsMenuItem2, snsMenuItem3, snsMenuItem4, snsMenuItem5}; // SNES menu items static const char SnesMenuItem1[] PROGMEM = "Read Rom"; @@ -56,8 +57,8 @@ void snsMenu() { // create menu with title and 4 options to choose from unsigned char snsCart; // Copy menuOptions out of progmem - convertPgm(menuOptionsSNS, 4); - snsCart = question_box("Select Cart Type", menuOptions, 4, 0); + convertPgm(menuOptionsSNS, 5); + snsCart = question_box("Select Cart Type", menuOptions, 5, 0); // wait for user choice to come back from the question box menu switch (snsCart) @@ -77,6 +78,13 @@ void snsMenu() { break; case 2: + display_Clear(); + display_Update(); + setup_SV(); + mode = mode_SV; + break; + + case 3: display_Clear(); display_Update(); hiROM = 1; @@ -86,7 +94,7 @@ void snsMenu() { mode = mode_FLASH8; break; - case 3: + case 4: display_Clear(); display_Update(); hiROM = 0; @@ -1924,4 +1932,4 @@ boolean eraseSRAM (byte b) { //****************************************** // End of File -//****************************************** +//****************************************** diff --git a/Cart_Reader/SV.ino b/Cart_Reader/SV.ino new file mode 100644 index 0000000..423cae4 --- /dev/null +++ b/Cart_Reader/SV.ino @@ -0,0 +1,457 @@ +//****************************************** +// 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("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: + asm volatile (" jmp 0"); + 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("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 +//****************************************** +