cartreader/Cart_Reader/SV.ino
sanni ce4f7aeeee V2.2: Add Satellaview support
Supported carts so far:
-BS-X Sorewa Namaewo Nusumareta Machino Monogatari
2018-10-22 20:29:49 +02:00

458 lines
12 KiB
C++

//******************************************
// 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
//******************************************