cartreader/Cart_Reader/SV.ino
sanni a27ee157e8
V2.3: Remove 16bit flash adapter menu
If you're still using the old 16bit adapter(the one with the additional pins left and right on the snes cart edge) you can enable the menu again by changing one line in Cart_Reader.ino:  
// Enable 16bit flash adapter menu
#define enable_flash16
2018-10-30 21:08:59 +01:00

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