mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-14 17:05:08 +01:00
a27ee157e8
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
457 lines
12 KiB
C++
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
|
|
//******************************************
|