aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cart_Reader/Cart_Reader.ino67
-rw-r--r--Cart_Reader/VBOY.ino534
-rw-r--r--Cart_Reader/WS.ino3
-rw-r--r--Cart_Reader/WSV.ino562
4 files changed, 1144 insertions, 22 deletions
diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino
index 5e5b985..9233968 100644
--- a/Cart_Reader/Cart_Reader.ino
+++ b/Cart_Reader/Cart_Reader.ino
@@ -4,8 +4,8 @@
This project represents a community-driven effort to provide
an easy to build and easy to modify cartridge dumper.
- Date: 19.08.2022
- Version: 9.5
+ Date: 21.08.2022
+ Version: 9.6
SD lib: https://github.com/greiman/SdFat
OLED lib: https://github.com/adafruit/Adafruit_SSD1306
@@ -25,7 +25,7 @@
MichlK - ROM Reader for Super Nintendo
Jeff Saltzman - 4-Way Button
Wayne and Layne - Video Game Shield menu
- skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, Coleco- and Intellivision modules
+ skaman - Cart ROM READER SNES ENHANCED, Famicom Cart Dumper, Coleco-, Intellivision, Virtual Boy, WSV modules
Tamanegi_taro - PCE and Satellaview modules
splash5 - GBSmart, Wonderswan and NGP modules
hkz & themanbehindthecurtain - N64 flashram commands
@@ -60,7 +60,7 @@
**********************************************************************************/
-char ver[5] = "9.5";
+char ver[5] = "9.6";
//******************************************
// !!! CHOOSE HARDWARE VERSION !!!
@@ -97,6 +97,7 @@ char ver[5] = "9.5";
// #define enable_INTV
// #define enable_COLV
// #define enable_VBOY
+// #define enable_WSV
//******************************************
// HW CONFIGS
@@ -305,6 +306,7 @@ bool i2c_found;
#define mode_INTV 23
#define mode_COL 24
#define mode_VBOY 25
+#define mode_WSV 26
// optimization-safe nop delay
#define NOP __asm__ __volatile__ ("nop\n\t")
@@ -683,7 +685,8 @@ boolean compareCRC(char* database, char* crcString, boolean renamerom, int offse
}
}
else {
- println_Msg(F(" -> database file not found"));
+ println_Msg(F(" -> Error"));
+ println_Msg(F("Database missing"));
return 0;
}
#else
@@ -807,19 +810,20 @@ static const char modeItem9[] PROGMEM = "NeoGeo Pocket";
static const char modeItem10[] PROGMEM = "Intellvision";
static const char modeItem11[] PROGMEM = "Colecovision";
static const char modeItem12[] PROGMEM = "Virtual Boy";
-static const char modeItem13[] PROGMEM = "Flashrom Programmer";
-static const char modeItem14[] PROGMEM = "About";
-static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14};
+static const char modeItem13[] PROGMEM = "Watara Supervision";
+static const char modeItem14[] PROGMEM = "Flashrom Programmer";
+static const char modeItem15[] PROGMEM = "About";
+static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7, modeItem8, modeItem9, modeItem10, modeItem11, modeItem12, modeItem13, modeItem14, modeItem15};
// All included slots
void mainMenu() {
- // create menu with title and 13 options to choose from
+ // create menu with title and 15 options to choose from
unsigned char modeMenu;
// Main menu spans across two pages
currPage = 1;
lastPage = 1;
- numPages = 2;
+ numPages = 3;
while (1) {
if (currPage == 1) {
@@ -832,6 +836,11 @@ void mainMenu() {
convertPgm(modeOptions, 7, 7);
modeMenu = question_box(F("OPEN SOURCE CART READER"), menuOptions, 7, 0);
}
+ if (currPage == 3) {
+ // Copy menuOptions out of progmem
+ convertPgm(modeOptions, 14, 1);
+ modeMenu = question_box(F("OPEN SOURCE CART READER"), menuOptions, 1, 0);
+ }
if (numPages == 0) {
// Execute choice
modeMenu = (currPage - 1) * 7 + modeMenu;
@@ -941,8 +950,15 @@ void mainMenu() {
break;
#endif
-#ifdef enable_FLASH
+#ifdef enable_WSV
case 12:
+ setup_WSV();
+ wsvMenu();
+ break;
+#endif
+
+#ifdef enable_FLASH
+ case 13:
#ifdef enable_FLASH16
flashMenu();
#else
@@ -951,7 +967,7 @@ void mainMenu() {
break;
#endif
- case 13:
+ case 14:
aboutScreen();
break;
@@ -997,8 +1013,9 @@ static const char* const consolesOptions[] PROGMEM = {consolesItem1, consolesIte
static const char handheldsItem1[] PROGMEM = "Virtual Boy";
static const char handheldsItem2[] PROGMEM = "WonderSwan";
static const char handheldsItem3[] PROGMEM = "NeoGeo Pocket";
-static const char handheldsItem4[] PROGMEM = "Reset";
-static const char* const handheldsOptions[] PROGMEM = {handheldsItem1, handheldsItem2, handheldsItem3, handheldsItem4};
+static const char handheldsItem4[] PROGMEM = "Watara Supervision";
+static const char handheldsItem5[] PROGMEM = "Reset";
+static const char* const handheldsOptions[] PROGMEM = {handheldsItem1, handheldsItem2, handheldsItem3, handheldsItem4, handheldsItem5};
// All included slots
void mainMenu() {
@@ -1160,21 +1177,19 @@ void consoleMenu() {
// Everything that needs an adapter
void handheldMenu() {
- // create menu with title and 4 options to choose from
+ // create menu with title and 5 options to choose from
unsigned char handheldsMenu;
// Copy menuOptions out of progmem
- convertPgm(handheldsOptions, 4);
- handheldsMenu = question_box(F("Choose Adapter"), menuOptions, 4, 0);
+ convertPgm(handheldsOptions, 5);
+ handheldsMenu = question_box(F("Choose Adapter"), menuOptions, 5, 0);
// wait for user choice to come back from the question box menu
switch (handheldsMenu)
{
#ifdef enable_VBOY
case 0:
- mode = mode_VBOY;
- display_Clear();
- display_Update();
setup_VBOY();
+ vboyMenu();
break;
#endif
@@ -1196,7 +1211,14 @@ void handheldMenu() {
break;
#endif
+#ifdef enable_WSV
case 3:
+ setup_WSV();
+ wsvMenu();
+ break;
+#endif
+
+ case 4:
resetArduino();
break;
@@ -3373,6 +3395,11 @@ void loop() {
vboyMenu();
}
#endif
+#ifdef enable_WSV
+ else if (mode == mode_WSV) {
+ wsvMenu();
+ }
+#endif
else {
display_Clear();
println_Msg(F("Menu Error"));
diff --git a/Cart_Reader/VBOY.ino b/Cart_Reader/VBOY.ino
new file mode 100644
index 0000000..d532ef5
--- /dev/null
+++ b/Cart_Reader/VBOY.ino
@@ -0,0 +1,534 @@
+//******************************************
+// VIRTUALBOY MODULE
+//******************************************
+#ifdef enable_VBOY
+// Nintendo VirtualBoy
+// Cartridge Pinout
+// 60P 2.00mm pitch connector
+//
+// TOP SIDE BOTTOM SIDE
+// +-------+
+// GND -| 1 2 |- GND
+// /WE0 (SRAM WRITE ENABLE) -| 3 4 |- nc
+// nc -| 5 6 |- /CS1 (SRAM ENABLE)
+// CS2 (SRAM) -| 7 8 |- VCC(+5V)
+// nc -| 9 10 |- A23
+// A19 -| 11 12 |- A22
+// A18 -| 13 14 |- A21
+// A8 -| 15 16 |- A20
+// A7 -| 17 18 |- A9
+// A6 -| 19 20 |- A10
+// A5 -| 21 22 |- A11
+// A4 -| 23 24 |- A12
+// A3 -| 25 26 |- A13
+// A2 -| 27 28 |- A14
+// A1 -| 29 30 |- A15
+// /CE (ROM) -| 31 32 |- A16
+// GND -| 33 34 |- A17
+// /OE -| 35 36 |- VCC(+5V)
+// D8 -| 37 38 |- D7
+// D0 -| 39 40 |- D15
+// D9 -| 41 42 |- D6
+// D1 -| 43 44 |- D14
+// D10 -| 45 46 |- D5
+// D2 -| 47 48 |- D13
+// D11 -| 49 50 |- D4
+// D3 -| 51 52 |- D12
+// VCC(+5V) -| 53 54 |- VCC(+5V)
+// nc -| 55 56 |- nc
+// nc -| 57 58 |- nc
+// GND -| 59 60 |- GND
+// +-------+
+//
+
+// CONTROL PINS:
+// CS2(SRAM) - (PH0) - VBOY PIN 7 - SNES RST
+// /CE(ROM) - (PH3) - VBOY PIN 31 - SNES /CS
+// /CS1(SRAM ENABLE) - (PH4) - VBOY PIN 6 - SNES /IRQ
+// /WE0(SRAM WRITE ENABLE) - (PH5) - VBOY PIN 3 - SNES /WR
+// /OE - (PH6) - VBOY PIN 35 - SNES /RD
+
+// NOT CONNECTED:
+// CLK(PH1) - N/C
+
+//******************************************
+// SETUP
+//******************************************
+
+void setup_VBOY()
+{
+ // Set Address Pins to Output
+ //A0-A7
+ DDRF = 0xFF;
+ //A8-A15
+ DDRK = 0xFF;
+ //A16-A23
+ DDRL = 0xFF;
+
+ // Set Control Pins to Output
+ // CS2(PH0) ---(PH1) /CE(PH3) /CS1(PH4) /WE0(PH5) /OE(PH6)
+ DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
+
+ // Set TIME(PJ0) to Output (UNUSED)
+ DDRJ |= (1 << 0);
+
+ // Set Pins (D0-D15) to Input
+ DDRC = 0x00;
+ DDRA = 0x00;
+
+ // Setting Control Pins to HIGH
+ // ---(PH1) /CE(PH3) /CS1(PH4) /WE0(PH5) /OE(PH6)
+ PORTH |= (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
+ // Set CS2(PH0) to LOW
+ PORTH &= ~(1 << 0);
+
+ // Set Unused Pins HIGH
+ PORTJ |= (1 << 0); // TIME(PJ0)
+
+ getCartInfo_VB();
+
+ mode = mode_VBOY;
+}
+
+//******************************************
+// MENU
+//******************************************
+
+// Base Menu
+static const char vboyMenuItem1[] PROGMEM = "Read ROM";
+static const char vboyMenuItem2[] PROGMEM = "Read SRAM";
+static const char vboyMenuItem3[] PROGMEM = "Write SRAM";
+static const char vboyMenuItem4[] PROGMEM = "Reset";
+static const char* const menuOptionsVBOY[] PROGMEM = {vboyMenuItem1, vboyMenuItem2, vboyMenuItem3, vboyMenuItem4};
+
+void vboyMenu()
+{
+ convertPgm(menuOptionsVBOY, 4);
+ uint8_t mainMenu = question_box(F("VIRTUALBOY MENU"), menuOptions, 4, 0);
+
+ switch (mainMenu)
+ {
+ case 0:
+ // Read ROM
+ sd.chdir("/");
+ readROM_VB();
+ sd.chdir("/");
+ break;
+
+ case 1:
+ // Read SRAM
+ if (sramSize) {
+ sd.chdir("/");
+ display_Clear();
+ println_Msg(F("Reading SRAM..."));
+ display_Update();
+ readSRAM_VB();
+ sd.chdir("/");
+ }
+ else {
+ print_Error(F("Cart has no SRAM"), false);
+ }
+#if (defined(enable_OLED) || defined(enable_LCD))
+ // Wait for user input
+ println_Msg(F("Press Button..."));
+ display_Update();
+ wait();
+#endif
+ break;
+
+ case 2:
+ // Write SRAM
+ if (sramSize) {
+ // Change working dir to root
+ sd.chdir("/");
+ fileBrowser(F("Select SRM file"));
+ display_Clear();
+ writeSRAM_VB();
+ writeErrors = verifySRAM_VB();
+ if (writeErrors == 0) {
+ println_Msg(F("SRAM verified OK"));
+ display_Update();
+ }
+ else {
+ print_Msg(F("Error: "));
+ print_Msg(writeErrors);
+ println_Msg(F(" bytes "));
+ print_Error(F("did not verify."), false);
+ }
+ }
+ else {
+ print_Error(F("Cart has no SRAM"), false);
+ }
+#if (defined(enable_OLED) || defined(enable_LCD))
+ // Wait for user input
+ println_Msg(F("Press Button..."));
+ display_Update();
+ wait();
+#endif
+ break;
+
+ case 3:
+ // reset
+ resetArduino();
+ break;
+ }
+}
+
+//******************************************
+// LOW LEVEL FUNCTIONS
+//******************************************
+
+void writeByte_VB(unsigned long myAddress, byte myData) {
+ PORTF = myAddress & 0xFF;
+ PORTK = (myAddress >> 8) & 0xFF;
+ PORTL = (myAddress >> 16) & 0xFF;
+
+ __asm__("nop\n\t");
+
+ PORTA = myData;
+
+ // Set CS2(PH0), /CE(PH3), /OE(PH6) to HIGH
+ PORTH |= (1 << 0) | (1 << 3) | (1 << 6);
+ // Set /CS1(PH4), /WE0(PH5) to LOW
+ PORTH &= ~(1 << 4) & ~(1 << 5);
+
+ __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
+
+ // Set CS2(PH0), /CS1(PH4), /WE0(PH5) to HIGH
+ PORTH |= (1 << 0) | (1 << 4) | (1 << 5);
+
+ __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
+}
+
+word readWord_VB(unsigned long myAddress) {
+ PORTF = myAddress & 0xFF;
+ PORTK = (myAddress >> 8) & 0xFF;
+ PORTL = (myAddress >> 16) & 0xFF;
+
+ __asm__("nop\n\t");
+
+ // Set CS2(PH0), /CS1(PH4), /WE0(PH5) to HIGH
+ PORTH |= (1 << 0) | (1 << 4) | (1 << 5);
+ // Set /CE(PH3), /OE(PH6) to LOW
+ PORTH &= ~(1 << 3) & ~(1 << 6);
+
+ __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
+
+ word tempWord = ( ( PINA & 0xFF ) << 8 ) | ( PINC & 0xFF );
+
+ // Set /CE(PH3), /OE(PH6) to HIGH
+ PORTH |= (1 << 3) | (1 << 6);
+ // Setting CS2(PH0) LOW
+ PORTH &= ~(1 << 0);
+
+ __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
+
+ return tempWord;
+}
+
+byte readByte_VB(unsigned long myAddress) { // SRAM BYTE
+ PORTF = myAddress & 0xFF;
+ PORTK = (myAddress >> 8) & 0xFF;
+ PORTL = (myAddress >> 16) & 0xFF;
+
+ __asm__("nop\n\t");
+
+ // Set CS2(PH0), /CE(PH3), /WE0(PH5) to HIGH
+ PORTH |= (1 << 0) | (1 << 3) | (1 << 5);
+ // Set /CS1(PH4), /OE(PH6) to LOW
+ PORTH &= ~(1 << 4) & ~(1 << 6);
+
+ __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
+
+ byte tempByte = PINA;
+
+ __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
+
+ // Set /CS1(PH4), /OE(PH6) to HIGH
+ PORTH |= (1 << 3) | (1 << 6);
+ // Setting CS2(PH0) LOW
+ PORTH &= ~(1 << 0);
+
+ __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
+
+ return tempByte;
+}
+
+// Switch data pins to write
+void dataOut_VB() {
+ DDRC = 0xFF;
+ DDRA = 0xFF;
+}
+
+// Switch data pins to read
+void dataIn_VB() {
+ DDRC = 0x00;
+ DDRA = 0x00;
+}
+
+//******************************************
+// CART INFO
+//******************************************
+
+void getCartInfo_VB() {
+ // Set control
+ dataIn_VB();
+
+ cartSize = 0;
+ for (unsigned long address = 0x80000; address <= 0x400000; address *= 2) {
+ // Get Serial
+ word vbSerial = readWord_VB((address - 0x204) / 2); // Cart Serial
+
+ switch (vbSerial)
+ {
+ case 0x4D54: // MT = Mario's Tennis
+ case 0x4832: // H2 = Panic Bomber/Tobidase! Panibomb
+ case 0x5350: // SP = Space Invaders
+ case 0x5353: // SS = Space Squash
+ case 0x5452: // TR = V-Tetris
+ cartSize = 0x80000; // 512KB
+ break;
+
+ case 0x494D: // IM = Insmouse no Yakata
+ case 0x4A42: // JB = Jack Bros.
+ case 0x4D43: // MC = Mario Clash
+ case 0x5245: // RE = Red Alarm
+ case 0x4833: // H3 = Vertical Force
+ case 0x5642: // VB = Virtual Bowling
+ case 0x5646: // VF = Virtual Fishing
+ case 0x4A56: // JV = Virtual Lab
+ case 0x5650: // VP = Virtual League Baseball/Virtual Pro Yakyuu '95
+ cartSize = 0x100000; // 1MB
+ break;
+
+ case 0x5042: // PB = 3-D Tetris
+ case 0x4750: // GP = Galactic Pinball
+ case 0x5344: // SD = SD Gundam Dimension War
+ case 0x5442: // TB = Teleroboxer
+ cartSize = 0x100000; // 1MB
+ sramSize = 0x2000; // 8KB
+ break;
+
+ case 0x5647: // VG = Golf/T&E Virtual Golf
+ case 0x4E46: // NF = Nester's Funky Bowling
+ case 0x5745: // WE = Waterworld
+ cartSize = 0x200000; // 2MB
+ break;
+
+ case 0x5743: // WC = Virtual Boy Wario Land
+ cartSize = 0x200000; // 2MB
+ sramSize = 0x2000; // 8KB
+ break;
+
+ case 0x4644: // FD = Hyper Fighting
+ cartSize = 0x400000; // 4MB
+ sramSize = 0x2000; // 8KB
+ break;
+ }
+
+ if (cartSize)
+ break;
+ }
+
+ // Get name
+ for (byte c = 0; c < 20; c += 2) {
+ // split word
+ word myWord = readWord_VB((cartSize - 0x220 + c) / 2);
+ byte loByte = myWord & 0xFF;
+ byte hiByte = myWord >> 8;
+
+ // write to buffer
+ sdBuffer[c] = hiByte;
+ sdBuffer[c + 1] = loByte;
+ }
+ byte myLength = 0;
+ for (unsigned int i = 0; i < 20; i++) {
+ if (((char(sdBuffer[i]) >= 48 && char(sdBuffer[i]) <= 57) || (char(sdBuffer[i]) >= 65 && char(sdBuffer[i]) <= 122)) && myLength < 15) {
+ romName[myLength] = char(sdBuffer[i]);
+ myLength++;
+ }
+ }
+
+ display_Clear();
+ println_Msg(F("Cart Info"));
+ println_Msg(F(" "));
+ print_Msg(F("Name: "));
+ println_Msg(romName);
+ print_Msg(F("Size: "));
+ print_Msg(cartSize * 8 / 1024 / 1024 );
+ println_Msg(F(" MBit"));
+ print_Msg(F("Sram: "));
+ if (sramSize > 0) {
+ print_Msg(sramSize * 8 / 1024);
+ println_Msg(F(" KBit"));
+ }
+ else
+ println_Msg(F("None"));
+ println_Msg(F(" "));
+
+#if (defined(enable_OLED) || defined(enable_LCD))
+ // Wait for user input
+ println_Msg(F("Press Button..."));
+ display_Update();
+ wait();
+#endif
+}
+
+//******************************************
+// READ CODE
+//******************************************
+
+void readROM_VB() {
+ dataIn_VB();
+
+ strcpy(fileName, romName);
+ strcat(fileName, ".vb");
+
+ EEPROM_readAnything(10, foldern);
+ sprintf(folder, "VBOY/ROM/%s/%d", romName, foldern);
+ sd.mkdir(folder, true);
+ sd.chdir(folder);
+
+ display_Clear();
+ print_Msg(F("Saving to "));
+ print_Msg(folder);
+ println_Msg(F("/..."));
+ display_Update();
+
+ foldern = foldern + 1;
+ EEPROM_writeAnything(10, foldern);
+
+ if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
+ print_Error(F("SD Error"), true);
+ }
+
+ word d = 0;
+ // HYPER FIGHTING FIX
+ // VIRTUAL BOY ADDRESSING IS TOP DOWN
+ // ONLY FOR HYPER FIGHTING PLUGIN WITH ALL ADDRESS LINES CONNECTED
+ // NORMAL PLUGIN DOES NOT HAVE THE UPPER ADDRESS LINES CONNECTED
+ if (cartSize == 0x400000) {
+ unsigned long romData = 0x1000000 - (cartSize / 2);
+ for (unsigned long currBuffer = romData; currBuffer < 0x1000000; currBuffer += 256) {
+ for (int currWord = 0; currWord < 256; currWord++) {
+ word myWord = readWord_VB(currBuffer + currWord);
+ // Split word into two bytes
+ sdBuffer[d] = (( myWord >> 8 ) & 0xFF);
+ sdBuffer[d + 1] = (myWord & 0xFF);
+ d += 2;
+ }
+ myFile.write(sdBuffer, 512);
+ d = 0;
+ }
+ }
+ else {
+ for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 256) {
+ for (int currWord = 0; currWord < 256; currWord++) {
+ word myWord = readWord_VB(currBuffer + currWord);
+ // Split word into two bytes
+ sdBuffer[d] = (( myWord >> 8 ) & 0xFF);
+ sdBuffer[d + 1] = (myWord & 0xFF);
+ d += 2;
+ }
+ myFile.write(sdBuffer, 512);
+ d = 0;
+ }
+ }
+ myFile.close();
+
+ // Compare CRC32 to database and rename ROM if found
+ // Arguments: database name, precalculated crc string or 0 to calculate, rename rom or not, starting offset
+ compareCRC("vb.txt", 0, 1, 0);
+
+#if (defined(enable_OLED) || defined(enable_LCD))
+ // Wait for user input
+ println_Msg(F(""));
+ println_Msg(F("Press Button..."));
+ display_Update();
+ wait();
+#endif
+}
+
+//******************************************
+// SRAM
+//******************************************
+
+void writeSRAM_VB() {
+ dataOut_VB();
+
+ sprintf(filePath, "%s/%s", filePath, fileName);
+ println_Msg(F("Writing..."));
+ println_Msg(filePath);
+ display_Update();
+
+ if (myFile.open(filePath, O_READ)) {
+ for (unsigned long currByte = 0; currByte < sramSize; currByte++) {
+ // writeWord_VB(currByte, ((myFile.read() << 8 ) & 0xFF));
+ writeByte_VB(currByte, (myFile.read()));
+ }
+ myFile.close();
+ println_Msg(F("Done"));
+ display_Update();
+ }
+ else {
+ print_Error(F("SD Error"), true);
+ }
+ dataIn_VB();
+}
+
+void readSRAM_VB() {
+ dataIn_VB();
+
+ strcpy(fileName, romName);
+ strcat(fileName, ".srm");
+
+ EEPROM_readAnything(10, foldern);
+ sprintf(folder, "VBOY/SAVE/%s/%d", romName, foldern);
+ sd.mkdir(folder, true);
+ sd.chdir(folder);
+
+ foldern = foldern + 1;
+ EEPROM_writeAnything(10, foldern);
+
+ if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
+ print_Error(F("SD Error"), true);
+ }
+ for (unsigned long currBuffer = 0; currBuffer < sramSize; currBuffer += 512) {
+ for (int currByte = 0; currByte < 512; currByte++) {
+ byte myByte = readByte_VB(currBuffer + currByte);
+ sdBuffer[currByte] = myByte;
+ }
+ myFile.write(sdBuffer, 512);
+ }
+ myFile.close();
+ print_Msg(F("Saved to "));
+ print_Msg(folder);
+ println_Msg(F("/"));
+ display_Update();
+}
+
+unsigned long verifySRAM_VB() {
+ dataIn_VB();
+ writeErrors = 0;
+
+ if (myFile.open(filePath, O_READ)) {
+ for (unsigned long currBuffer = 0; currBuffer < sramSize; currBuffer += 512) {
+ for (int currByte = 0; currByte < 512; currByte++) {
+ byte myByte = readByte_VB(currBuffer + currByte);
+ sdBuffer[currByte] = myByte;
+ }
+ for (int i = 0; i < 512; i++) {
+ if (myFile.read() != sdBuffer[i]) {
+ writeErrors++;
+ }
+ }
+ }
+ myFile.close();
+ }
+ else {
+ print_Error(F("SD Error"), true);
+ }
+
+ return writeErrors;
+}
+#endif
diff --git a/Cart_Reader/WS.ino b/Cart_Reader/WS.ino
index 5d61e61..99e76f5 100644
--- a/Cart_Reader/WS.ino
+++ b/Cart_Reader/WS.ino
@@ -1,6 +1,7 @@
//******************************************
// WS MODULE
//******************************************
+#ifdef enable_WS
// Cartridge pinout
// 48P 1.25mm pitch connector
// C1, C48 : GND
@@ -22,8 +23,6 @@
// C46 : INT (for RTC alarm interrupt)
// C47 : CLK (384KHz on WS)
-#ifdef enable_WS
-
#ifdef ws_adapter_v2
#define WS_CLK_BIT 5 // USE PE5 as CLK
#else
diff --git a/Cart_Reader/WSV.ino b/Cart_Reader/WSV.ino
new file mode 100644
index 0000000..e39aba3
--- /dev/null
+++ b/Cart_Reader/WSV.ino
@@ -0,0 +1,562 @@
+//******************************************
+// WSV MODULE
+//******************************************
+#ifdef enable_WSV
+// Watara Supervision
+// Cartridge Pinout
+// 40P 2.5mm pitch connector
+//
+// BACK LABEL
+// SIDE SIDE
+// +-------+
+// /RD -| 1 40 |- +5V
+// A0 -| 2 39 |- nc
+// A1 -| 3 38 |- nc
+// A2 -| 4 37 |- nc
+// A3 -| 5 36 |- nc
+// A4 -| 6 35 |- /WR
+// A5 -| 7 34 |- D0
+// A6 -| 8 33 |- D1
+// A7 -| 9 32 |- D2
+// A8 -| 10 31 |- D3
+// A9 -| 11 30 |- D4
+// A10 -| 12 29 |- D5
+// A11 -| 13 28 |- D6
+// A12 -| 14 27 |- D7
+// A13 -| 15 26 |- nc
+// A14 -| 16 25 |- nc
+// A15 -| 17 24 |- L1
+// A16 -| 18 23 |- L2
+// L3 -| 19 22 |- GND
+// L0 -| 20 21 |- PWR GND
+// +-------+
+//
+// L3 - L0 are the Link Port's I/O - only the 'MAGNUM' variant
+// routed these to the cartridge slot as additional banking bits.
+//
+// CONTROL PINS:
+// /WR - (PH5)
+// /RD - (PH6)
+
+word WSV[] = {32, 64, 512};
+byte wsvlo = 0; // Lowest Entry
+byte wsvhi = 2; // Highest Entry
+
+byte wsvsize;
+byte newwsvsize;
+
+// EEPROM MAPPING
+// 08 ROM SIZE
+
+//******************************************
+// SETUP
+//******************************************
+
+void setup_WSV()
+{
+ // Set Address Pins to Output
+ //A0-A7
+ DDRF = 0xFF;
+ //A8-A15
+ DDRK = 0xFF;
+ //BA0-BA7 (BA5-BA7 UNUSED)
+ DDRL = 0xFF;
+ //PA0-PA7 (UNUSED)
+ DDRA = 0xFF;
+
+ // Set Data Pins (D0-D7) to Input
+ // D0 - D7
+ DDRC = 0x00;
+
+ // Set Control Pins to Output
+ // WR(PH5) RD(PH6)
+ // DDRH |= (1 << 5) | (1 << 6);
+ // ---(PH0) ---(PH1) ---(PH3) ---(PH4) /WR(PH5) /RD(PH6)
+ DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
+
+ // Switch WR(PH5) to HIGH
+ // PORTH |= (1 << 5);
+ // Switch RD(PH6) to LOW
+ // PORTH &= ~(1 << 6);
+ // Setting Control Pins to HIGH
+ // ---(PH0) ---(PH1) ---(PH3) ---(PH4) /WR(PH5)
+ PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5);
+ // Switch RD(PH6) to LOW
+ PORTH &= ~(1 << 6);
+
+ // Set Unused Pins HIGH
+ PORTL = 0xE0;
+ PORTA = 0xFF;
+ PORTJ |= (1 << 0); // TIME(PJ0)
+
+ checkStatus_WSV();
+ strcpy(romName, "SUPERVISION");
+
+ mode = mode_WSV;
+}
+
+//******************************************
+// MENU
+//******************************************
+
+// Base Menu
+static const char wsvMenuItem1[] PROGMEM = "Select Cart";
+static const char wsvMenuItem2[] PROGMEM = "Read ROM";
+static const char wsvMenuItem3[] PROGMEM = "Set Size";
+static const char wsvMenuItem4[] PROGMEM = "Reset";
+static const char* const menuOptionsSV[] PROGMEM = {wsvMenuItem1, wsvMenuItem2, wsvMenuItem3, wsvMenuItem4};
+
+void wsvMenu()
+{
+ convertPgm(menuOptionsSV, 4);
+ uint8_t mainMenu = question_box(F("SUPERVISION MENU"), menuOptions, 4, 0);
+
+ switch (mainMenu)
+ {
+ case 0:
+ // Select Cart
+ setCart_WSV();
+ setup_WSV();
+ break;
+
+ case 1:
+ // Read Rom
+ sd.chdir("/");
+ readROM_WSV();
+ sd.chdir("/");
+ break;
+
+ case 2:
+ setROMSize_WSV();
+ break;
+
+ case 3:
+ // reset
+ resetArduino();
+ break;
+ }
+}
+
+//******************************************
+// LOW LEVEL FUNCTIONS
+//******************************************
+
+// WRITE
+void controlOut_WSV() {
+ // Switch RD(PH6) to HIGH
+ PORTH |= (1 << 6);
+ // Switch WR(PH5) to LOW
+ PORTH &= ~(1 << 5);
+}
+
+// READ
+void controlIn_WSV() {
+ // Switch WR(PH5) to HIGH
+ PORTH |= (1 << 5);
+ // Switch RD(PH6) to LOW
+ PORTH &= ~(1 << 6);
+}
+
+void dataIn_WSV()
+{
+ DDRC = 0x00;
+}
+
+void dataOut_WSV()
+{
+ DDRC = 0xFF;
+}
+
+uint8_t readByte_WSV(uint32_t addr)
+{
+ PORTF = addr & 0xFF;
+ PORTK = (addr >> 8) & 0xFF;
+ PORTL = (addr >> 16) & 0xFF;
+
+ // Wait for data bus
+ // 6 x 62.5ns = 375ns
+ NOP; NOP; NOP; NOP; NOP; NOP;
+
+ uint8_t ret = PINC;
+ NOP;
+
+ return ret;
+}
+
+//******************************************
+// READ CODE
+//******************************************
+
+void readROM_WSV()
+{
+ strcpy(fileName, romName);
+ strcat(fileName, ".sv");
+
+ // create a new folder for storing rom file
+ EEPROM_readAnything(0, foldern);
+ //sprintf(folder, "WSV/ROM/%s/%d", romName, foldern);
+ sprintf(folder, "WSV/ROM/%d", foldern);
+ sd.mkdir(folder, true);
+ sd.chdir(folder);
+
+ display_Clear();
+ print_Msg(F("Saving to "));
+ print_Msg(folder);
+ println_Msg(F("/..."));
+ display_Update();
+
+ // open file on sdcard
+ if (!myFile.open(fileName, O_RDWR | O_CREAT))
+ print_Error(F("Can't create file on SD"), true);
+
+ // write new folder number back to EEPROM
+ foldern++;
+ EEPROM_writeAnything(0, foldern);
+
+ // start reading rom
+ dataIn_WSV();
+ controlIn_WSV();
+
+ romSize = WSV[wsvsize];
+
+ uint32_t romStart = 0;
+ if (romSize < 64)
+ romStart = 0x8000;
+ uint32_t romEnd = (uint32_t)romSize * 0x400;
+ for (uint32_t addr = 0; addr < romEnd; addr += 512)
+ {
+ for (uint16_t w = 0; w < 512; w++)
+ sdBuffer[w] = readByte_WSV(romStart + addr + w);
+ myFile.write(sdBuffer, 512);
+ }
+ myFile.close();
+
+ // Compare CRC32 to database and rename ROM if found
+ // Arguments: database name, precalculated crc string or 0 to calculate, rename rom or not, starting offset
+ compareCRC("wsv.txt", 0, 1, 0);
+
+ println_Msg(F(""));
+ println_Msg(F("Press Button..."));
+ display_Update();
+ wait();
+}
+
+//******************************************
+// ROM SIZE
+//******************************************
+
+void setROMSize_WSV()
+{
+#if (defined(enable_OLED) || defined(enable_LCD))
+ display_Clear();
+ if (wsvlo == wsvhi)
+ newwsvsize = wsvlo;
+ else {
+ int b = 0;
+ int i = wsvlo;
+
+ display_Clear();
+ print_Msg(F("ROM Size: "));
+ println_Msg(WSV[i]);
+ println_Msg(F(""));
+#if defined(enable_OLED)
+ println_Msg(F("Press left to Change"));
+ println_Msg(F("and right to Select"));
+#elif defined(enable_LCD)
+ println_Msg(F("Rotate to Change"));
+ println_Msg(F("Press to Select"));
+#endif
+ display_Update();
+
+ while (1) {
+ b = checkButton();
+ if (b == 2) { // Previous (doubleclick)
+ if (i == wsvlo)
+ i = wsvhi;
+ else
+ i--;
+
+ // Only update display after input because of slow LCD library
+ display_Clear();
+ print_Msg(F("ROM Size: "));
+ println_Msg(WSV[i]);
+ println_Msg(F(""));
+#if defined(enable_OLED)
+ println_Msg(F("Press left to Change"));
+ println_Msg(F("and right to Select"));
+#elif defined(enable_LCD)
+ println_Msg(F("Rotate to Change"));
+ println_Msg(F("Press to Select"));
+#endif
+ display_Update();
+ }
+ if (b == 1) { // Next (press)
+ if (i == wsvhi)
+ i = wsvlo;
+ else
+ i++;
+
+ // Only update display after input because of slow LCD library
+ display_Clear();
+ print_Msg(F("ROM Size: "));
+ println_Msg(WSV[i]);
+ println_Msg(F(""));
+#if defined(enable_OLED)
+ println_Msg(F("Press left to Change"));
+ println_Msg(F("and right to Select"));
+#elif defined(enable_LCD)
+ println_Msg(F("Rotate to Change"));
+ println_Msg(F("Press to Select"));
+#endif
+ display_Update();
+ }
+ if (b == 3) { // Long Press - Execute (hold)
+ newwsvsize = i;
+ break;
+ }
+ }
+ display.setCursor(0, 56); // Display selection at bottom
+ }
+ print_Msg(F("ROM SIZE "));
+ print_Msg(WSV[newwsvsize]);
+ println_Msg(F("K"));
+ display_Update();
+ delay(1000);
+#else
+ if (wsvlo == wsvhi)
+ newwsvsize = wsvlo;
+ else {
+setrom:
+ String sizeROM;
+ for (int i = 0; i < (wsvhi - wsvlo + 1); i++) {
+ Serial.print(F("Select ROM Size: "));
+ Serial.print(i);
+ Serial.print(F(" = "));
+ Serial.print(WSV[i + wsvlo]);
+ Serial.println(F("K"));
+ }
+ Serial.print(F("Enter ROM Size: "));
+ while (Serial.available() == 0) {}
+ sizeROM = Serial.readStringUntil('\n');
+ Serial.println(sizeROM);
+ newwsvsize = sizeROM.toInt() + wsvlo;
+ if (newwsvsize > wsvhi) {
+ Serial.println(F("SIZE NOT SUPPORTED"));
+ Serial.println(F(""));
+ goto setrom;
+ }
+ }
+ Serial.print(F("ROM Size = "));
+ Serial.print(WSV[newwsvsize]);
+ Serial.println(F("K"));
+#endif
+ EEPROM_writeAnything(8, newwsvsize);
+ wsvsize = newwsvsize;
+}
+
+void checkStatus_WSV()
+{
+ EEPROM_readAnything(8, wsvsize);
+ if (wsvsize > 2) {
+ wsvsize = 1; // default 64K
+ EEPROM_writeAnything(8, wsvsize);
+ }
+
+#if (defined(enable_OLED) || defined(enable_LCD))
+ display_Clear();
+ println_Msg(F("WATARA SUPERVISION"));
+ println_Msg(F("CURRENT SETTINGS"));
+ println_Msg(F(""));
+ print_Msg(F("ROM SIZE: "));
+ print_Msg(WSV[wsvsize]);
+ println_Msg(F("K"));
+ display_Update();
+ wait();
+#else
+ Serial.print(F("CURRENT ROM SIZE: "));
+ Serial.print(WSV[wsvsize]);
+ Serial.println(F("K"));
+ Serial.println(F(""));
+#endif
+}
+
+//******************************************
+// CART SELECT CODE
+//******************************************
+void setCart_WSV() {
+ char gamename[100];
+ char tempStr2[2];
+ char crc_search[9];
+
+ //go to root
+ sd.chdir();
+
+ // Select starting letter
+ byte myLetter = starting_letter();
+
+ // Open database
+ if (myFile.open("wsv.txt", O_READ)) {
+ // Skip ahead to selected starting letter
+ if ((myLetter > 0) && (myLetter <= 26)) {
+ while (myFile.available()) {
+ // Read current name
+ get_line(gamename, &myFile, 96);
+
+ // Compare selected letter with first letter of current name until match
+ while (gamename[0] != 64 + myLetter) {
+ skip_line(&myFile);
+ skip_line(&myFile);
+ get_line(gamename, &myFile, 96);
+ }
+ break;
+ }
+
+ // Rewind one line
+ for (byte count_newline = 0; count_newline < 2; count_newline++) {
+ while (1) {
+ if (myFile.curPosition() == 0) {
+ break;
+ }
+ else if (myFile.peek() == '\n') {
+ myFile.seekSet(myFile.curPosition() - 1);
+ break;
+ }
+ else {
+ myFile.seekSet(myFile.curPosition() - 1);
+ }
+ }
+ }
+ if (myFile.curPosition() != 0)
+ myFile.seekSet(myFile.curPosition() + 2);
+ }
+
+ // Display database
+ while (myFile.available()) {
+ display_Clear();
+
+ // Read game name
+#if defined(enable_OLED)
+ get_line(gamename, &myFile, 42);
+#else
+ get_line(gamename, &myFile, 96);
+#endif
+
+ // Read CRC32 checksum
+ sprintf(checksumStr, "%c", myFile.read());
+ for (byte i = 0; i < 7; i++) {
+ sprintf(tempStr2, "%c", myFile.read());
+ strcat(checksumStr, tempStr2);
+ }
+
+ // Skip over semicolon
+ myFile.seekSet(myFile.curPosition() + 1);
+
+ // Read CRC32 of first 512 bytes
+ sprintf(crc_search, "%c", myFile.read());
+ for (byte i = 0; i < 7; i++) {
+ sprintf(tempStr2, "%c", myFile.read());
+ strcat(crc_search, tempStr2);
+ }
+
+ // Skip over semicolon
+ myFile.seekSet(myFile.curPosition() + 1);
+
+ // Read rom size
+ // Read the next ascii character and subtract 48 to convert to decimal
+ cartSize = myFile.read() - 48;
+
+ // Remove leading 0 for single digit cart sizes
+ if (cartSize != 0) {
+ cartSize = cartSize * 10 + myFile.read() - 48;
+ }
+ else {
+ cartSize = myFile.read() - 48;
+ }
+
+ // Skip rest of line
+ myFile.seekSet(myFile.curPosition() + 2);
+
+ // Skip every 3rd line
+ skip_line(&myFile);
+
+ println_Msg(F("Select your cartridge"));
+ println_Msg(F(""));
+ println_Msg(gamename);
+ print_Msg(F("Size: "));
+ if (cartSize == 51)
+ print_Msg(F("512"));
+ else
+ print_Msg(cartSize);
+ println_Msg(F("KB"));
+ println_Msg(F(""));
+#if defined(enable_OLED)
+ println_Msg(F("Press left to Change"));
+ println_Msg(F("and right to Select"));
+#elif defined(enable_LCD)
+ println_Msg(F("Rotate to Change"));
+ println_Msg(F("Press to Select"));
+#elif defined(SERIAL_MONITOR)
+ println_Msg(F("U/D to Change"));
+ println_Msg(F("Space to Select"));
+#endif
+ display_Update();
+
+ int b = 0;
+ while (1) {
+ // Check button input
+ b = checkButton();
+
+ // Next
+ if (b == 1) {
+ break;
+ }
+
+ // Previous
+ else if (b == 2) {
+ for (byte count_newline = 0; count_newline < 7; count_newline++) {
+ while (1) {
+ if (myFile.curPosition() == 0) {
+ break;
+ }
+ else if (myFile.peek() == '\n') {
+ myFile.seekSet(myFile.curPosition() - 1);
+ break;
+ }
+ else {
+ myFile.seekSet(myFile.curPosition() - 1);
+ }
+ }
+ }
+ if (myFile.curPosition() != 0)
+ myFile.seekSet(myFile.curPosition() + 2);
+ break;
+ }
+
+ // Selection
+ else if (b == 3) {
+ //word WSV[] = {32,64,512};
+ switch (cartSize) {
+ case 32:
+ wsvsize = 0;
+ break;
+
+ case 64:
+ wsvsize = 1;
+ break;
+
+ case 51:
+ wsvsize = 2;
+ break;
+ }
+ EEPROM_writeAnything(8, wsvsize);
+ myFile.close();
+ break;
+ }
+ }
+ }
+ }
+ else {
+ print_Error(F("Database file not found"), true);
+ }
+}
+#endif