aboutsummaryrefslogtreecommitdiffhomepage
path: root/Cart_Reader/VBOY.ino
diff options
context:
space:
mode:
authorsanni <[email protected]>2022-08-21 12:28:47 +0200
committersanni <[email protected]>2022-08-21 12:28:47 +0200
commit3b7d6bd4fc6338e71989eda4997d93869012ee57 (patch)
tree8ae921a96d41421e1045fdcb346de48dc8c8214a /Cart_Reader/VBOY.ino
parentdc867db7ed22067b423f9402e69b29b24c56d1d6 (diff)
downloadcartreader-3b7d6bd4fc6338e71989eda4997d93869012ee57.tar.gz
cartreader-3b7d6bd4fc6338e71989eda4997d93869012ee57.zip
Add Virtual Boy and Watara Supervision (thx to skaman)
Diffstat (limited to 'Cart_Reader/VBOY.ino')
-rw-r--r--Cart_Reader/VBOY.ino534
1 files changed, 534 insertions, 0 deletions
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