V2.2: Add Satellaview support

Supported carts so far:
-BS-X Sorewa Namaewo Nusumareta Machino Monogatari
This commit is contained in:
sanni 2018-10-22 20:29:49 +02:00
parent 7127c77559
commit ce4f7aeeee
3 changed files with 480 additions and 11 deletions

View File

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

View File

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

457
Cart_Reader/SV.ino Normal file
View File

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