Add Virtual Boy and Watara Supervision (thx to skaman)

This commit is contained in:
sanni 2022-08-21 12:28:47 +02:00
parent dc867db7ed
commit 3b7d6bd4fc
4 changed files with 1144 additions and 22 deletions

View File

@ -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;
@ -3372,6 +3394,11 @@ void loop() {
else if (mode == mode_VBOY) {
vboyMenu();
}
#endif
#ifdef enable_WSV
else if (mode == mode_WSV) {
wsvMenu();
}
#endif
else {
display_Clear();

534
Cart_Reader/VBOY.ino Normal file
View File

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

View File

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

562
Cart_Reader/WSV.ino Normal file
View File

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