cartreader/Cart_Reader/MSX.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

1662 lines
46 KiB
C++

//******************************************
// MSX COMPUTER MODULE
//******************************************
#ifdef enable_MSX
// MSX
// Cartridge Pinout
// 50P 2.54mm pitch connector
//
// FRONT BACK
// SIDE SIDE
// +-------+
// /CS2 -| 2 1 |- /CS1
// /SLTSL -| 4 3 |- /CS12
// /RFSH -| 6 5 |- RSV(NC)
// /INT -| 8 7 |- /WAIT
// /BUSDIR -| 10 9 |- /M1
// /MERQ -| 12 11 |- /IORQ
// /RD -| 14 13 |- /WR
// RSV(NC) -| 16 15 |- /RESET
// A15 -| 18 17 |- A9
// A10 -| 20 19 |- A11
// A6 -| 22 21 |- A7
// A8 -| 24 23 |- A12
// A13-| 26 25 |- A14
// A0 -| 28 27 |- A1
// A2 -| 30 29 |- A3
// A4 -| 32 31 |- A5
// D0 -| 34 33 |- D1
// D2 -| 36 35 |- D3
// D4 -| 38 37 |- D5
// D6 -| 40 39 |- D7
// CLOCK -| 42 41 |- GND
// SW1 -| 44 43 |- GND
// SW2 -| 46 45 |- +5V
// +12V -| 48 47 |- +5V
// -12V -| 50 49 |- SOUNDIN
// +-------+
//
// BACK
// +----------------------------------------------------------------------------+
// | 49 47 45 43 41 39 37 35 33 31 29 27 25 23 21 19 17 15 13 11 9 7 5 3 1 |
// LEFT | | RIGHT
// | 50 48 46 44 42 40 38 36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2 |
// +----------------------------------------------------------------------------+
// FRONT
//
// CONTROL PINS:
// /RESET(PH0) - SNES RESET
// CLOCK(PH1) - SNES CPUCLK
// /SLTSL(PH3) - SNES /CS
// /MERQ(PH4) - SNES /IRQ
// /WR(PH5) - SNES /WR
// /RD(PH6) - SNES /RD
// /CS1(PL0) - SNES A16
// /CS2(PL1) - SNES A17
// /CS12(PL2) - SNES A18
//******************************************
// Defines
//******************************************
#define CS1_DISABLE PORTL |= (1 << 0) // ROM SELECT 4000-7FFF
#define CS1_ENABLE PORTL &= ~(1 << 0)
#define CS2_DISABLE PORTL |= (1 << 1) // ROM SELECT 8000-BFFF
#define CS2_ENABLE PORTL &= ~(1 << 1)
#define CS12_DISABLE PORTL |= (1 << 2) // ROM SELECT 4000-BFFF
#define CS12_ENABLE PORTL &= ~(1 << 2)
#define MERQ_DISABLE PORTH |= (1 << 4)
#define MERQ_ENABLE PORTH &= ~(1 << 4)
//******************************************
// Supported Mappers
//******************************************
// Supported Mapper Array
// Format = {msxmapper,msxlo,msxhi,msxramlo,msxramhi}
static const byte PROGMEM msxmapsize[] = {
0, 1, 4, 0, 0, // No Mapper
1, 4, 7, 0, 2, // ASCII8 [sram r/w] 2K (Dires), 8K (Xanadu/Wizardry)
2, 4, 8, 0, 2, // ASCII16 [sram r/w] 2K (Daisenryaku/Hydlide 2), 8K (A-Train)
3, 4, 4, 0, 0, // Cross Blaim (db-Soft) 64K
4, 5, 5, 2, 2, // Game Master 2 128K [sram r/w] 8K
5, 8, 8, 3, 3, // Halnote (HAL) 1024K [sram r/w] 16K
6, 4, 4, 0, 0, // Harry Fox (Micro Cabin) 64K
7, 6, 8, 2, 4, // Koei [sram r/w] 8K (Nobunaga no Yabou - Zenkoku Ban), 32K (Sangokushi II)
8, 4, 8, 0, 0, // Konami without SCC "Konami4"
9, 5, 8, 0, 0, // Konami with SCC "Konami5"
10, 4, 4, 0, 0, // MSX-DOS2 64K
11, 0, 4, 2, 2, // PAC 0K/FM-PAC 64K [sram r/w] 8K
12, 7, 7, 0, 0, // R-Type 384K
13, 5, 5, 0, 0, // Super Lode Runner (Irem) 128K
};
// MSX1 = 8,16,32,128,256
// MSX2 = 32,64,128,256,512,1024
int MSX[] = { 0, 8, 16, 32, 64, 128, 256, 512, 1024 };
byte msxlo = 0; // Lowest Entry
byte msxhi = 8; // Highest Entry
byte MSXRAM[] = { 0, 2, 8, 16, 32 };
byte msxramlo = 0; // Lowest Entry
byte msxramhi = 4; // Highest Entry
byte msxmapcount = 14; // (sizeof(mapsize)/sizeof(mapsize[0])) / 5;
boolean msxmapfound = false;
byte msxmapselect;
int msxindex;
byte msxmapper;
byte newmsxmapper;
byte msxsize;
byte newmsxsize;
byte msxramsize;
byte newmsxramsize;
uint8_t msxbanks;
byte chipselect;
boolean srambit5 = false;
boolean srambit6 = false;
boolean srambit7 = false;
// EEPROM MAPPING
// 07 MAPPER
// 08 ROM SIZE
// 10 RAM SIZE
//******************************************
// MENU
//******************************************
// Base Menu
static const char msxMenuItem1[] PROGMEM = "Select Cart";
static const char msxMenuItem2[] PROGMEM = "Read Cart";
static const char msxMenuItem3[] PROGMEM = "Set Mapper + Size";
static const char msxMenuItem4[] PROGMEM = "Write SRAM";
static const char* const menuOptionsMSX[] PROGMEM = { msxMenuItem1, msxMenuItem2, msxMenuItem3, msxMenuItem4, string_reset2 };
void msxMenu() {
convertPgm(menuOptionsMSX, 5);
uint8_t mainMenu = question_box(F("MSX MENU"), menuOptions, 5, 0);
switch (mainMenu) {
case 0:
// Select Cart
setCart_MSX();
wait();
setup_MSX();
break;
case 1:
// Read ROM + Read RAM
sd.chdir("/");
readROM_MSX();
readRAM_MSX();
sd.chdir("/");
break;
case 2:
// Set Mapper + Size
setMapper_MSX();
checkMapperSize_MSX();
setROMSize_MSX();
setRAMSize_MSX();
break;
case 3:
// Write RAM
writeRAM_MSX();
println_Msg(F(""));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
break;
case 4:
// reset
resetArduino();
break;
}
}
//******************************************
// SETUP
//******************************************
void setup_MSX() {
// Request 5V
setVoltage(VOLTS_SET_5V);
// Set Address Pins to Output
// MSX uses A0-A15
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//A16-A23 - Use A16-A18 for /CS1, /CS2, /CS12
DDRL = 0xFF;
// Set Control Pins to Output
// /RST(PH0) CLOCK(PH1) /SLTSL(PH3) /MERQ(PH4) /WR(PH5) /RD(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-D7) to Input
DDRC = 0x00;
// Setting Control Pins to HIGH
// /RST(PH0) CLOCK(PH1) /SLTSL(PH3) /MERQ(PH4) /WR(PH5) /RD(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set /SLTSL(PH3) to LOW
PORTH &= ~(1 << 3);
// Set /CS1, /CS2, /CS12 to HIGH
PORTL = 0xFF; // A16-A23 (A16 = /CS1, A17 = /CS2, A18 = /CS12)
// Set Unused Data Pins (PA0-PA7) to Output
DDRA = 0xFF;
// Set Unused Pins HIGH
PORTA = 0xFF;
PORTJ |= (1 << 0); // TIME(PJ0)
checkStatus_MSX();
strcpy(romName, "MSX");
mode = mode_MSX;
}
//******************************************
// READ DATA
//******************************************
uint8_t readData_MSX(uint16_t addr) {
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A15
NOP;
NOP;
// Set /SLTSL(PH3) to LOW
// PORTH &= ~(1 << 3);
// Set /RD to LOW
PORTH &= ~(1 << 6); // /RD LOW (ENABLE)
NOP;
NOP;
NOP;
uint8_t ret = PINC;
// Pull /RD to HIGH
PORTH |= (1 << 6); // /RD HIGH (DISABLE)
// Set /SLTSL(PH3) to HIGH
// PORTH |= (1 << 3);
// NOP; NOP;
return ret;
}
void readSegment_MSX(uint32_t startaddr, uint32_t endaddr) {
for (uint32_t addr = startaddr; addr < endaddr; addr += 512) {
for (int w = 0; w < 512; w++) {
uint8_t temp = readData_MSX(addr + w);
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
}
}
//******************************************
// WRITE DATA
//******************************************
void writeData_MSX(uint16_t addr, uint8_t data) {
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A15
NOP;
NOP;
DDRC = 0xFF; // Set to Output
PORTC = data;
NOP;
NOP;
NOP;
// Set /WR(PH5) to LOW
PORTH &= ~(1 << 5);
NOP;
NOP;
NOP;
// Set /WR(PH5) to HIGH
PORTH |= (1 << 5);
NOP;
NOP;
DDRC = 0x00; // Reset to Input
}
//******************************************
// POWER
//******************************************
#ifndef enable_NES
int int_pow(int base, int exp) // Power for int
{
int result = 1;
while (exp) {
if (exp & 1)
result *= base;
exp /= 2;
base *= base;
}
return result;
}
#endif
//******************************************
// CS CODE
//******************************************
void setCS() // Set CS Line
{
chipselect = 0;
for (int x = 0; x < 4; x++) {
uint8_t check0 = readData_MSX(0x4000);
uint8_t check1 = readData_MSX(0x4001);
if ((check0 == 0x41) && (check1 == 0x42))
break;
chipselect++;
enableCS();
}
}
void checkCS() // Check for 2nd Chip
{
if (chipselect == 1) {
uint8_t check0 = readData_MSX(0x8000);
uint8_t check1 = readData_MSX(0x8001);
if ((check0 == 0x41) && (check1 == 0x42)) {
disableCS();
chipselect = 2;
enableCS();
}
}
}
void enableCS() {
if (chipselect == 1)
CS1_ENABLE;
else if (chipselect == 2)
CS2_ENABLE;
else if (chipselect == 3)
CS12_ENABLE;
}
void disableCS() {
CS1_DISABLE;
CS2_DISABLE;
CS12_DISABLE;
}
//******************************************
// READ ROM
//******************************************
void readROM_MSX() {
if (msxsize == 0) {
display_Clear();
println_Msg(F("ROM SIZE 0K"));
display_Update();
} else {
strcpy(fileName, romName);
strcat(fileName, ".bin");
// create a new folder for storing rom file
EEPROM_readAnything(0, foldern);
sprintf(folder, "MSX/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_STR(saving_to_STR, 0);
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT))
print_FatalError(sd_error_STR);
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
switch (msxmapper) {
case 0: // No Mapper
disableCS();
if (msxsize == 4) // 64K
readSegment_MSX(0x0000, 0x4000); // +16K
setCS();
readSegment_MSX(0x4000, 0x6000); // 8K
if (msxsize > 1)
readSegment_MSX(0x6000, 0x8000); // +8K = 16K
if (msxsize > 2) {
checkCS(); // Check for 2nd Chip
readSegment_MSX(0x8000, 0xC000); // +16K = 32K
}
disableCS();
if (msxsize == 4) // 64K
readSegment_MSX(0xC000, 0x10000); // +16K
break;
case 1: // ASCII8 (64K/128K/256K/512K)
case 7: // Koei (256K/512K/1024K)
msxbanks = int_pow(2, msxsize - 1);
for (int x = 0; x < msxbanks; x += 4) {
writeData_MSX(0x6000, x);
readSegment_MSX(0x4000, 0x6000); // 8K Init Bank 0
writeData_MSX(0x6800, x + 1);
readSegment_MSX(0x6000, 0x8000); // 8K Init Bank 0
writeData_MSX(0x7000, x + 2);
readSegment_MSX(0x8000, 0xA000); // 8K Init Bank 0
writeData_MSX(0x7800, x + 3);
readSegment_MSX(0xA000, 0xC000); // 8K Init Bank 0
}
break;
case 2: // ASCII16 (64K/128K/256K/512K)
msxbanks = int_pow(2, msxsize - 1) / 2;
for (int x = 0; x < msxbanks; x += 2) {
writeData_MSX(0x6000, x);
readSegment_MSX(0x4000, 0x8000); // 16K Init Bank 0
writeData_MSX(0x7000, x + 1);
readSegment_MSX(0x8000, 0xC000); // 16K Init Bank 0
}
break;
case 3: // Cross Blaim (64K)
CS1_ENABLE;
readSegment_MSX(0x4000, 0x8000); // 16K Fixed Bank 0
CS1_DISABLE;
CS2_ENABLE;
for (int x = 1; x < 4; x++) {
writeData_MSX(0x4045, x);
readSegment_MSX(0x8000, 0xC000); // 16K Init Bank 1
}
CS2_DISABLE;
break;
case 4: // Game Master 2 (128K)
readSegment_MSX(0x4000, 0x6000); // 8K Fixed Bank 0
writeData_MSX(0x6000, 1); // Set Bank 1 for subsequent reads
readSegment_MSX(0x6000, 0x8000); // 8K Init Bank 1
for (int x = 2; x < 16; x += 2) {
writeData_MSX(0x8000, x);
readSegment_MSX(0x8000, 0xA000); // 8K
writeData_MSX(0xA000, x + 1);
readSegment_MSX(0xA000, 0xC000); // 8K
}
break;
case 5: // HAL Note (1024K)
MERQ_ENABLE;
// Dummy Read - Needed to prevent random bytes
for (int y = 0; y < 32; y++) {
writeData_MSX(0x4FFF, y);
for (uint32_t addr = 0x4000; addr < 0x6000; addr++)
readData_MSX(addr); // Dummy Read
}
// READ
for (int x = 0; x < 128; x++) {
writeData_MSX(0x4FFF, x);
readSegment_MSX(0x4000, 0x6000); // 8K Init Bank 0
}
MERQ_DISABLE;
break;
case 6: // Harry Fox (64K)
CS1_ENABLE;
writeData_MSX(0x6000, 0);
readSegment_MSX(0x4000, 0x8000); // 16K Init Bank 0
CS1_DISABLE;
CS2_ENABLE;
writeData_MSX(0x7000, 0);
readSegment_MSX(0x8000, 0xC000); // 16K Init Bank 1
CS2_DISABLE;
CS1_ENABLE;
writeData_MSX(0x6000, 1);
readSegment_MSX(0x4000, 0x8000); // 16K
CS1_DISABLE;
CS2_ENABLE;
writeData_MSX(0x7000, 1);
readSegment_MSX(0x8000, 0xC000); // 16K
CS2_DISABLE;
break;
case 8: // Konami MegaROM without SCC
readSegment_MSX(0x4000, 0x6000); // 8K Fixed Bank 0
readSegment_MSX(0x6000, 0x8000); // 8K Init Bank 1
msxbanks = int_pow(2, msxsize - 1);
for (int x = 2; x < msxbanks; x += 2) {
writeData_MSX(0x8000, x);
readSegment_MSX(0x8000, 0xA000); // 8K
writeData_MSX(0xA000, x + 1);
readSegment_MSX(0xA000, 0xC000); // 8K
}
break;
case 9: // Konami MegaROM with SCC
msxbanks = int_pow(2, msxsize - 1);
for (int x = 0; x < msxbanks; x += 4) {
writeData_MSX(0x5000, x);
readSegment_MSX(0x4000, 0x6000); // 8K
writeData_MSX(0x7000, x + 1);
readSegment_MSX(0x6000, 0x8000); // 8K
writeData_MSX(0x9000, x + 2);
readSegment_MSX(0x8000, 0xA000); // 8K
writeData_MSX(0xB000, x + 3);
readSegment_MSX(0xA000, 0xC000); // 8K
}
break;
case 10: // MSX-DOS2 (64K)
MERQ_ENABLE;
CS1_ENABLE;
for (int x = 0; x < 4; x++) {
writeData_MSX(0x7FFE, x); // Official v2.20
readSegment_MSX(0x4000, 0x8000); // 16K Init Bank 0
}
CS1_DISABLE;
MERQ_DISABLE;
break;
case 11: // FM-PAC (64K)
CS1_ENABLE;
for (int x = 0; x < 4; x++) {
writeData_MSX(0x7FF7, x);
readSegment_MSX(0x4000, 0x8000); // 16K
}
CS1_DISABLE;
break;
case 12: // R-TYPE (384K)
for (int x = 0; x < 23; x++) {
writeData_MSX(0x7000, x);
readSegment_MSX(0x8000, 0xC000); // 16K Init Bank 0
}
readSegment_MSX(0x4000, 0x8000); // 16K Init Bank 0F
break;
case 13: // Super Lode Runner (128K)
MERQ_ENABLE;
CS2_ENABLE;
for (int x = 0; x < 8; x++) {
writeData_MSX(0x0000, x);
readSegment_MSX(0x8000, 0xC000); // 16K Init Bank 0
}
CS2_DISABLE;
MERQ_DISABLE;
break;
}
myFile.close();
unsigned long crcsize = MSX[msxsize] * 0x400;
calcCRC(fileName, crcsize, NULL, 0);
println_Msg(F(""));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
}
}
//******************************************
// TEST/CHECK RAM
//******************************************
boolean testRAM(byte enable1, byte enable2) {
boolean testbit = false;
for (int x = 0; x < 0x10; x++) { // Test 16 Bytes
writeData_MSX(0x7000, enable1);
byte test1 = readData_MSX(0x8000 + x);
writeData_MSX(0x7000, enable2);
byte test2 = readData_MSX(0x8000 + x);
if (test1 == test2)
testbit = true;
else {
testbit = false;
break;
}
}
return testbit;
}
void checkRAM() {
// Test carts to identify SRAM Enable Bits (Bit 5/6/7)
// Bit 5 Test
srambit5 = testRAM(0x20, 0xB0);
if (!srambit5) {
// Bit 6 Test
srambit6 = testRAM(0x40, 0xC0);
if (!srambit6) {
// Bit 7 Test
srambit7 = testRAM(0x80, 0xF0);
if (!srambit7) {
display_Clear();
print_Error(F("SRAM FAILED - CHECK BATTERY"));
}
}
}
}
//******************************************
// READ RAM
//******************************************
void readRAM_MSX() {
if (msxramsize == 0) {
display_Clear();
println_Msg(F("RAM SIZE 0K"));
display_Update();
} else {
strcpy(fileName, romName);
strcat(fileName, ".srm");
if (msxsize == 0) {
// create a new folder for storing ram file
EEPROM_readAnything(0, foldern);
sprintf(folder, "MSX/RAM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
}
display_Clear();
print_STR(saving_to_STR, 0);
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT))
print_FatalError(sd_error_STR);
if (msxsize == 0) {
// write new folder number back to EEPROM
foldern++;
EEPROM_writeAnything(0, foldern);
}
switch (msxmapper) {
case 1: // ASCII8 (2K/8K)
// ASCII8 carts use different SRAM Enable Bits
// Bit 4 (0x10) - Dires (2K)
// Bit 5 (0x20) - Xanadu
// Bit 7 (0x80) - Wizardry
if (msxramsize == 1) { // Dires (2K)
writeData_MSX(0x7000, 0x10); // Bit 4
readSegment_MSX(0x8000, 0x8800); // 2K
} else {
// Combine SRAM Enable Bit 5 + Bit 7 = 0xA0
writeData_MSX(0x7000, 0xA0); // Bit 5 + Bit 7
readSegment_MSX(0x8000, 0xA000); // 8K
}
writeData_MSX(0x7000, 0); // SRAM Disable
break;
case 2: // ASCII16 (2K/8K)
writeData_MSX(0x7000, 0x10); // Bit 4 Enable
readSegment_MSX(0x8000, 0x8800); // 2K - Hydlide 2 & Daisenryaku (2K)
if (msxramsize == 2) // A-Train (8K)
readSegment_MSX(0x8800, 0xA000); // +6K = 8K
writeData_MSX(0x7000, 0); // SRAM Disable
break;
case 4: // Game Master 2 (8K)
writeData_MSX(0xA000, 0x10); // Bit 4 Enable, Bit 5 SRAM Segment 0
readSegment_MSX(0xB000, 0xC000); // 4K
writeData_MSX(0xA000, 0x30); // Bit 4 Enable, Bit 5 SRAM Segment 1
readSegment_MSX(0xB000, 0xC000); // 4K
writeData_MSX(0xA000, 0); // SRAM Disable
break;
case 5: // HAL Note (16K)
MERQ_ENABLE;
writeData_MSX(0x4FFF, 0x80); // Bit 7 Enable
readSegment_MSX(0x0000, 0x4000); // 16K
writeData_MSX(0x4FFF, 0); // SRAM Disable
MERQ_DISABLE;
break;
case 7: // Koei (8K/32K) Nobunaga no Yabou - Zenkoku Ban (8K) & Sangokushi II (32K)
// Koei carts use different SRAM Enable Bits
// Bit 5 (0x20) - Nobunaga no Yabou - Zenkoku Ban MSX
// Bit 6 (0x40) - Nobunaga no Yabou - Zenkoku Ban MSX2
// Bit 7 (0x80) - Sangokushi II
// Use Combined Bits: 0xA0 (Bit 5 + Bit 7) and 0xC0 (Bit 6 + Bit 7)
// Note: Combined 0xE0 (Bit 5 + Bit 6 + Bit 7) does not work
checkRAM();
if (srambit6)
writeData_MSX(0x7000, 0xC0); // Bit 6 + Bit 7 Enable
else
writeData_MSX(0x7000, 0xA0); // Bit 5 + Bit 7 Enable
readSegment_MSX(0x8000, 0xA000); // 8K
if (msxramsize > 2) {
for (int x = 1; x < 4; x++) {
if (srambit6)
writeData_MSX(0x7000, 0xC0 + x); // Bit 6 + Bit 7 Enable
else
writeData_MSX(0x7000, 0xA0 + x); // Bit 5 + Bit 7 Enable
readSegment_MSX(0x8000, 0xA000); // 8K
}
}
writeData_MSX(0x7000, 0); // SRAM Disable
break;
case 11: // PAC/FM-PAC (8K)
writeData_MSX(0x5FFE, 0x4D); // SRAM Enable Step 1
writeData_MSX(0x5FFF, 0x69); // SRAM Enable Step 2
readSegment_MSX(0x4000, 0x6000); // 8K
writeData_MSX(0x5FFE, 0); // SRAM Disable
break;
}
myFile.close();
unsigned long crcsize = MSX[msxramsize] * 0x400;
calcCRC(fileName, crcsize, NULL, 0);
println_Msg(F(""));
// Prints string out of the common strings array either with or without newline
print_STR(press_button_STR, 1);
display_Update();
wait();
}
}
//******************************************
// WRITE RAM
//******************************************
void writeRAM_MSX() {
display_Clear();
if (msxramsize == 0) {
print_Error(F("RAM SIZE 0K"));
} else {
fileBrowser(F("Select RAM File"));
sd.chdir();
sprintf(filePath, "%s/%s", filePath, fileName);
display_Clear();
println_Msg(F("Writing File: "));
println_Msg(filePath);
println_Msg(fileName);
display_Update();
//open file on sd card
if (myFile.open(filePath, O_READ)) {
switch (msxmapper) {
case 1: // ASCII8 (2K/8K)
for (word address = 0x0; address < (0x800 * msxramsize * msxramsize); address += 512) { // 2K/8K
if (msxramsize == 1)
writeData_MSX(0x7000, 0x10); // Bit 4
else
writeData_MSX(0x7000, 0xA0); // Bit 5 + Bit 7
myFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
writeData_MSX(0x8000 + address + x, sdBuffer[x]);
}
}
writeData_MSX(0x7000, 0); // SRAM Disable
break;
case 2: // ASCII16 (2K/8K)
writeData_MSX(0x7000, 0x10); // Bit 4 Enable
for (word address = 0x0; address < (0x800 * msxramsize * msxramsize); address += 512) { // 2K/8K
myFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
writeData_MSX(0x8000 + address + x, sdBuffer[x]);
}
}
writeData_MSX(0x7000, 0); // SRAM Disable
break;
case 4: // Game Master 2 (8K)
for (int y = 0; y < 2; y++) {
writeData_MSX(0xA000, 0x10 + (y * 0x20)); // Bit 4 Enable, Bit 5 SRAM Segment 0/1
for (word address = 0x0; address < 0x1000; address += 512) { // 4K
myFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
writeData_MSX(0xB000 + address + x, sdBuffer[x]);
}
}
}
writeData_MSX(0xA000, 0); // SRAM Disable
break;
case 5: // HAL Note (16K)
MERQ_ENABLE;
writeData_MSX(0x4FFF, 0x80); // Bit 7 Enable
for (word address = 0; address < 0x4000; address += 512) { // 16K
myFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
writeData_MSX(address + x, sdBuffer[x]);
}
}
writeData_MSX(0x4FFF, 0); // SRAM Disable
MERQ_DISABLE;
break;
case 7: // Koei (8K/32K)
checkRAM();
if (srambit6)
writeData_MSX(0x7000, 0xC0); // Bit 6 + Bit 7 Enable
else
writeData_MSX(0x7000, 0xA0); // Bit 5 + Bit 7 Enable
for (word address = 0x0; address < 0x2000; address += 512) { // 8K
myFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
writeData_MSX(0x8000 + address + x, sdBuffer[x]);
}
}
if (msxramsize > 2) {
for (int y = 1; y < 4; y++) {
if (srambit6)
writeData_MSX(0x7000, 0xC0 + y); // Bit 6 + Bit 7 Enable
else
writeData_MSX(0x7000, 0xA0 + y); // Bit 5 + Bit 7 Enable
for (word address = 0x0; address < 0x2000; address += 512) { // 8K
myFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
writeData_MSX(0x8000 + address + x, sdBuffer[x]);
}
}
}
}
writeData_MSX(0x7000, 0); // SRAM Disable
break;
case 11: // PAC/FM-PAC (8K)
writeData_MSX(0x5FFE, 0x4D); // SRAM Enable Step 1
writeData_MSX(0x5FFF, 0x69); // SRAM Enable Step 2
for (word address = 0x0; address < 0x2000; address += 512) { // 8K
myFile.read(sdBuffer, 512);
for (int x = 0; x < 512; x++) {
writeData_MSX(0x4000 + address + x, sdBuffer[x]);
}
}
writeData_MSX(0x5FFE, 0); // SRAM Disable
break;
}
myFile.close();
println_Msg(F(""));
println_Msg(F("RAM FILE WRITTEN!"));
display_Update();
} else {
print_FatalError(sd_error_STR);
}
}
sd.chdir(); // root
filePath[0] = '\0'; // Reset filePath
}
//******************************************
// MAPPER CODE
//******************************************
void setMapper_MSX() {
#if (defined(enable_OLED) || defined(enable_LCD))
int b = 0;
int i = 0;
// Check Button Status
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
boolean buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == LOW) { // Button Pressed
while (1) { // Scroll Mapper List
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) { // Button Released
// Correct Overshoot
if (i == 0)
i = msxmapcount - 1;
else
i--;
break;
}
display_Clear();
print_Msg(F("Mapper: "));
msxindex = i * 5;
msxmapselect = pgm_read_byte(msxmapsize + msxindex);
println_Msg(msxmapselect);
printMapper(msxmapselect);
display_Update();
if (i == (msxmapcount - 1))
i = 0;
else
i++;
delay(250);
}
}
display_Clear();
print_Msg(F("Mapper: "));
msxindex = i * 5;
msxmapselect = pgm_read_byte(msxmapsize + msxindex);
println_Msg(msxmapselect);
printMapper(msxmapselect);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
while (1) {
b = checkButton();
if (b == 2) { // Previous Mapper (doubleclick)
if (i == 0)
i = msxmapcount - 1;
else
i--;
// Only update display after input because of slow LCD library
display_Clear();
print_Msg(F("Mapper: "));
msxindex = i * 5;
msxmapselect = pgm_read_byte(msxmapsize + msxindex);
println_Msg(msxmapselect);
printMapper(msxmapselect);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
}
if (b == 1) { // Next Mapper (press)
if (i == (msxmapcount - 1))
i = 0;
else
i++;
// Only update display after input because of slow LCD library
display_Clear();
print_Msg(F("Mapper: "));
msxindex = i * 5;
msxmapselect = pgm_read_byte(msxmapsize + msxindex);
println_Msg(msxmapselect);
printMapper(msxmapselect);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
}
if (b == 3) { // Long Press - Execute (hold)
newmsxmapper = msxmapselect;
break;
}
}
display.setCursor(0, 56);
print_Msg(F("MAPPER "));
print_Msg(newmsxmapper);
println_Msg(F(" SELECTED"));
display_Update();
delay(1000);
#else
setmapper:
String newmap;
msxmapfound = false;
printMapper(0);
Serial.print(F("Enter Mapper [0-13]: "));
while (Serial.available() == 0) {}
newmap = Serial.readStringUntil('\n');
Serial.println(newmap);
newmsxmapper = newmap.toInt();
for (int i = 0; i < msxmapcount; i++) {
msxindex = i * 5;
msxmapselect = pgm_read_byte(msxmapsize + msxindex);
if (newmsxmapper == msxmapselect)
msxmapfound = true;
}
if (msxmapfound == false) {
Serial.println(F("MAPPER NOT SUPPORTED!"));
Serial.println(F(""));
newmsxmapper = 0;
goto setmapper;
}
#endif
EEPROM_writeAnything(7, newmsxmapper);
msxmapper = newmsxmapper;
}
void checkMapperSize_MSX() {
for (int i = 0; i < msxmapcount; i++) {
msxindex = i * 5;
byte mapcheck = pgm_read_byte(msxmapsize + msxindex);
if (mapcheck == msxmapper) {
msxlo = pgm_read_byte(msxmapsize + msxindex + 1);
msxhi = pgm_read_byte(msxmapsize + msxindex + 2);
msxramlo = pgm_read_byte(msxmapsize + msxindex + 3);
msxramhi = pgm_read_byte(msxmapsize + msxindex + 4);
break;
}
}
}
//******************************************
// SET ROM SIZE
//******************************************
void setROMSize_MSX() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
if (msxlo == msxhi)
newmsxsize = msxlo;
else {
int b = 0;
int i = msxlo;
display_Clear();
print_Msg(F("ROM Size: "));
println_Msg(MSX[i]);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
while (1) {
b = checkButton();
if (b == 2) { // Previous (doubleclick)
if (msxmapper == 11) { // PAC/FM-PAC 0K/64K
if (i == msxlo)
i = msxhi; // 64K
else
i = msxlo; // 0K
} else {
if (i == msxlo)
i = msxhi;
else
i--;
// Only update display after input because of slow LCD library
display_Clear();
print_Msg(F("ROM Size: "));
println_Msg(MSX[i]);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
}
}
if (b == 1) { // Next (press)
if (msxmapper == 11) { // PAC/FM-PAC 0K/64K
if (i == msxlo)
i = msxhi; // 64K
else
i = msxlo; // 0K
} else {
if (i == msxhi)
i = msxlo;
else
i++;
// Only update display after input because of slow LCD library
display_Clear();
print_Msg(F("ROM Size: "));
println_Msg(MSX[i]);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
}
}
if (b == 3) { // Long Press - Execute (hold)
newmsxsize = i;
break;
}
}
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(F("ROM SIZE "));
if (msxmapper == 12) // R-Type
print_Msg(F("384"));
else
print_Msg(MSX[newmsxsize]);
println_Msg(F("K"));
display_Update();
delay(1000);
#else
if (msxlo == msxhi)
newmsxsize = msxlo;
else {
setrom:
String sizeROM;
for (int i = 0; i < (msxhi - msxlo + 1); i++) {
Serial.print(F("Select ROM Size: "));
Serial.print(i);
Serial.print(F(" = "));
if (msxmapper == 12) // R-Type
Serial.print(F("384"));
else
Serial.print(MSX[i + msxlo]);
Serial.println(F("K"));
}
Serial.print(F("Enter ROM Size: "));
while (Serial.available() == 0) {}
sizeROM = Serial.readStringUntil('\n');
Serial.println(sizeROM);
newmsxsize = sizeROM.toInt() + msxlo;
if (msxmapper == 11) { // PAC/FM-PAC 0K/64K
if ((newmsxromsize > 0) && (newmsxromsize < 4)) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(F(""));
goto setrom;
}
}
if (newmsxsize > msxhi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(F(""));
goto setrom;
}
}
Serial.print(F("ROM Size = "));
if (msxmapper == 12) // R-Type
Serial.print(F("384"));
else
Serial.print(MSX[newmsxsize]);
Serial.println(F("K"));
#endif
EEPROM_writeAnything(8, newmsxsize);
msxsize = newmsxsize;
}
//******************************************
// SET RAM SIZE
//******************************************
void setRAMSize_MSX() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
if (msxramlo == msxramhi)
newmsxramsize = msxramlo;
else {
int b = 0;
int i = msxramlo;
display_Clear();
print_Msg(F("RAM Size: "));
println_Msg(MSXRAM[i]);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
while (1) {
b = checkButton();
if (b == 2) { // Previous (doubleclick)
if (msxmapper == 7) { // Koei 8K/32K
if (i == msxramlo)
i = msxramhi; // 32K
else
i = msxramlo; // 8K
} else {
if (i == msxramlo)
i = msxramhi;
else
i--;
// Only update display after input because of slow LCD library
display_Clear();
print_Msg(F("RAM Size: "));
println_Msg(MSXRAM[i]);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
}
}
if (b == 1) { // Next (press)
if (msxmapper == 7) { // Koei 8K/32K
if (i == msxramlo)
i = msxramhi; // 32K
else
i = msxramlo; // 8K
} else {
if (i == msxramhi)
i = msxramlo;
else
i++;
// Only update display after input because of slow LCD library
display_Clear();
print_Msg(F("RAM Size: "));
println_Msg(MSXRAM[i]);
println_Msg(F(""));
#if defined(enable_OLED)
print_STR(press_to_change_STR, 1);
print_STR(right_to_select_STR, 1);
#elif defined(enable_LCD)
print_STR(rotate_to_change_STR, 1);
print_STR(press_to_select_STR, 1);
#endif
display_Update();
}
}
if (b == 3) { // Long Press - Execute (hold)
newmsxramsize = i;
break;
}
}
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(F("RAM SIZE "));
print_Msg(MSXRAM[newmsxramsize]);
println_Msg(F("K"));
display_Update();
delay(1000);
#else
if (msxramlo == msxramhi)
newmsxramsize = msxramlo;
else {
setram:
String sizeRAM;
for (int i = 0; i < (msxramhi - msxramlo + 1); i++) {
Serial.print(F("Select RAM Size: "));
Serial.print(i);
Serial.print(F(" = "));
Serial.print(MSXRAM[i + msxramlo]);
Serial.println(F("K"));
}
Serial.print(F("Enter RAM Size: "));
while (Serial.available() == 0) {}
sizeRAM = Serial.readStringUntil('\n');
Serial.println(sizeRAM);
newmsxramsize = sizeRAM.toInt() + msxramlo;
if (msxmapper == 7) { // Koei 8K/32K
if (newmsxramsize == 3) { // 16K
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(F(""));
goto setram;
}
}
if (newmsxramsize > msxramhi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(F(""));
goto setram;
}
}
Serial.print(F("RAM Size = "));
Serial.print(MSXRAM[newmsxramsize]);
Serial.println(F("K"));
#endif
EEPROM_writeAnything(10, newmsxramsize);
msxramsize = newmsxramsize;
}
//******************************************
// CHECK STATUS
//******************************************
void checkStatus_MSX() {
EEPROM_readAnything(7, msxmapper);
EEPROM_readAnything(8, msxsize);
EEPROM_readAnything(10, msxramsize);
if (msxmapper > 13) {
msxmapper = 0;
EEPROM_writeAnything(7, msxmapper);
}
if (msxsize > 8) {
msxsize = 0;
EEPROM_writeAnything(8, msxsize);
}
if (msxramsize > 4) {
msxramsize = 0;
EEPROM_writeAnything(10, msxramsize);
}
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("MSX READER"));
println_Msg(F("CURRENT SETTINGS"));
println_Msg(F(""));
print_Msg(F("MAPPER: "));
println_Msg(msxmapper);
printMapper(msxmapper);
print_Msg(F("ROM SIZE: "));
if (msxmapper == 12) // R-Type
print_Msg(F("384"));
else
print_Msg(MSX[msxsize]);
println_Msg(F("K"));
print_Msg(F("RAM SIZE: "));
print_Msg(MSXRAM[msxramsize]);
println_Msg(F("K"));
display_Update();
wait();
#else
Serial.print(F("CURRENT MAPPER: "));
Serial.println(msxmapper);
Serial.print(F("CURRENT ROM SIZE: "));
if (msxmapper == 12) // R-Type
Serial.print(F("384"));
else
Serial.print(MSX[msxsize]);
Serial.println(F("K"));
Serial.print(F("CURRENT RAM SIZE: "));
Serial.print(MSXRAM[msxramsize]);
Serial.println(F("K"));
Serial.println(F(""));
#endif
}
void printMapper(byte msxmaplabel) {
#if (defined(enable_OLED) || defined(enable_LCD))
switch (msxmaplabel) {
case 0:
println_Msg(F("NONE"));
break;
case 1:
println_Msg(F("ASCII8"));
break;
case 2:
println_Msg(F("ASCII16"));
break;
case 3:
println_Msg(F("CROSS BLAIM"));
break;
case 4:
println_Msg(F("GAME MASTER 2"));
break;
case 5:
println_Msg(F("HAL NOTE"));
break;
case 6:
println_Msg(F("HARRY FOX YUKI"));
break;
case 7:
println_Msg(F("KOEI"));
break;
case 8:
println_Msg(F("KONAMI"));
break;
case 9:
println_Msg(F("KONAMI SCC"));
break;
case 10:
println_Msg(F("MSX-DOS2"));
break;
case 11:
println_Msg(F("PAC/FM-PAC"));
break;
case 12:
println_Msg(F("R-TYPE"));
break;
case 13:
println_Msg(F("SUPER LODE RUNNER"));
break;
}
#else
Serial.println(F("0 = NONE"));
Serial.println(F("1 = ASCII8"));
Serial.println(F("2 = ASCII16"));
Serial.println(F("3 = CROSS BLAIM"));
Serial.println(F("4 = GAME MASTER 2"));
Serial.println(F("5 = HAL NOTE"));
Serial.println(F("6 = HARRY FOX YUKI"));
Serial.println(F("7 = KOEI"));
Serial.println(F("8 = KONAMI"));
Serial.println(F("9 = KONAMI SCC"));
Serial.println(F("10 = MSX-DOS2"));
Serial.println(F("11 = PAC/FM-PAC"));
Serial.println(F("12 = R-TYPE"));
Serial.println(F("13 = SUPER LODE RUNNER"));
#endif
}
//******************************************
// CART SELECT CODE
//******************************************
FsFile msxcsvFile;
char msxgame[31]; // title
char msxmm[3]; // mapper
char msxrr[3]; // romsize
char msxss[3]; // ramsize
char msxll[4]; // linelength (previous line)
unsigned long msxcsvpos; // CSV File Position
char msxcartCSV[] = "msxcart.txt"; // CSV List
char msxcsvEND[] = "EOF"; // CSV End Marker for scrolling
bool readLine_MSX(FsFile& f, char* line, size_t maxLen) {
for (size_t n = 0; n < maxLen; n++) {
int c = f.read();
if (c < 0 && n == 0) return false; // EOF
if (c < 0 || c == '\n') {
line[n] = 0;
return true;
}
line[n] = c;
}
return false; // line too long
}
bool readVals_MSX(char* msxgame, char* msxmm, char* msxrr, char* msxss, char* msxll) {
char line[41];
msxcsvpos = msxcsvFile.position();
if (!readLine_MSX(msxcsvFile, line, sizeof(line))) {
return false; // EOF or too long
}
char* comma = strtok(line, ",");
int x = 0;
while (comma != NULL) {
if (x == 0)
strcpy(msxgame, comma);
else if (x == 1)
strcpy(msxmm, comma);
else if (x == 2)
strcpy(msxrr, comma);
else if (x == 3)
strcpy(msxss, comma);
else if (x == 4)
strcpy(msxll, comma);
comma = strtok(NULL, ",");
x += 1;
}
return true;
}
bool getCartListInfo_MSX() {
bool buttonreleased = 0;
bool cartselected = 0;
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F(" HOLD TO FAST CYCLE"));
display_Update();
#else
Serial.println(F("HOLD BUTTON TO FAST CYCLE"));
#endif
delay(2000);
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
boolean buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == LOW) { // Button Held - Fast Cycle
while (1) { // Scroll Game List
while (readVals_MSX(msxgame, msxmm, msxrr, msxss, msxll)) {
if (strcmp(msxcsvEND, msxgame) == 0) {
msxcsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(msxgame);
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(msxgame);
#endif
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) { // Button Released
buttonreleased = 1;
break;
}
if (buttonreleased) {
buttonreleased = 0; // Reset Flag
break;
}
}
}
#if defined(enable_OLED)
buttonVal1 = (PIND & (1 << 7)); // PD7
#elif defined(enable_LCD)
buttonVal1 = (PING & (1 << 2)); //PG2
#endif
if (buttonVal1 == HIGH) // Button Released
break;
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("FAST CYCLE OFF"));
display_Update();
#else
Serial.println(F(""));
Serial.println(F("FAST CYCLE OFF"));
Serial.println(F("PRESS BUTTON TO STEP FORWARD"));
Serial.println(F("DOUBLE CLICK TO STEP BACK"));
Serial.println(F("HOLD TO SELECT"));
Serial.println(F(""));
#endif
while (readVals_MSX(msxgame, msxmm, msxrr, msxss, msxll)) {
if (strcmp(msxcsvEND, msxgame) == 0) {
msxcsvFile.seek(0); // Restart
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART TITLE:"));
println_Msg(F(""));
println_Msg(msxgame);
display.setCursor(0, 48);
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
#else
Serial.print(F("CART TITLE:"));
Serial.println(msxgame);
#endif
while (1) { // Single Step
int b = checkButton();
if (b == 1) { // Continue (press)
break;
}
if (b == 2) { // Reset to Start of List (doubleclick)
byte prevline = strtol(msxll, NULL, 10);
msxcsvpos -= prevline;
msxcsvFile.seek(msxcsvpos);
break;
}
if (b == 3) { // Long Press - Select Cart (hold)
newmsxmapper = strtol(msxmm, NULL, 10);
newmsxsize = strtol(msxrr, NULL, 10);
newmsxramsize = strtol(msxss, NULL, 10);
EEPROM_writeAnything(7, newmsxmapper);
EEPROM_writeAnything(8, newmsxsize);
EEPROM_writeAnything(10, newmsxramsize);
cartselected = 1; // SELECTION MADE
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F("SELECTION MADE"));
display_Update();
#else
Serial.println(F("SELECTION MADE"));
#endif
break;
}
}
if (cartselected) {
cartselected = 0; // Reset Flag
return true;
}
}
}
#if (defined(enable_OLED) || defined(enable_LCD))
println_Msg(F(""));
println_Msg(F("END OF FILE"));
display_Update();
#else
Serial.println(F("END OF FILE"));
#endif
return false;
}
void checkCSV_MSX() {
if (getCartListInfo_MSX()) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CART SELECTED"));
println_Msg(F(""));
println_Msg(msxgame);
display_Update();
// Display Settings
display.setCursor(0, 56);
print_Msg(F("CODE: M"));
print_Msg(newmsxmapper);
print_Msg(F("/R"));
print_Msg(newmsxsize);
print_Msg(F("/S"));
println_Msg(newmsxramsize);
display_Update();
#else
Serial.println(F(""));
Serial.println(F("CART SELECTED"));
Serial.println(msxgame);
// Display Settings
Serial.print(F("CODE: M"));
Serial.print(newmsxmapper);
Serial.print(F("/R"));
Serial.print(newmsxsize);
Serial.print(F("/S"));
Serial.println(newmsxramsize);
Serial.println(F(""));
#endif
} else {
#if (defined(enable_OLED) || defined(enable_LCD))
display.setCursor(0, 56);
println_Msg(F("NO SELECTION"));
display_Update();
#else
Serial.println(F("NO SELECTION"));
#endif
}
}
void setCart_MSX() {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(msxcartCSV);
display_Update();
#endif
sd.chdir();
sprintf(folder, "MSX/CSV");
sd.chdir(folder); // Switch Folder
msxcsvFile = sd.open(msxcartCSV, O_READ);
if (!msxcsvFile) {
#if (defined(enable_OLED) || defined(enable_LCD))
display_Clear();
println_Msg(F("CSV FILE NOT FOUND!"));
display_Update();
#else
Serial.println(F("CSV FILE NOT FOUND!"));
#endif
while (1) {
if (checkButton() != 0)
setup_MSX();
}
}
checkCSV_MSX();
msxcsvFile.close();
}
#endif