cartreader/Cart_Reader/SV.ino
Ancyker 2cf7f5dbe7 Cleanup voltage requests
The `setVoltage()` function should be called even when `ENABLE_VSELECT` is disabled because `ENABLE_3V3FIX` also uses it. There is no resource cost to do this as when both options are disabled the compiler will optimize this function out. This just "future proofs" the code so if that function ever does more it doesn't need updated everywhere. This applies to `setup_FlashVoltage()` as well.

The changes to OSCR.cpp are just for code formatting and additional comments to clarify this.
2023-06-26 15:25:54 -04:00

758 lines
20 KiB
C++

//******************************************
// 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() {
// Request 5V
setVoltage(VOLTS_SET_5V);
// 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
//******************************************