cartreader/Cart_Reader/MSX.ino
2024-05-01 09:35:00 +02:00

1463 lines
41 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 msxsize;
byte msxramsize;
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 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 = { FSTRING_SELECT_CART, msxMenuItem2, msxMenuItem3, msxMenuItem4, FSTRING_RESET };
void msxMenu() {
convertPgm(menuOptionsMSX, 5);
uint8_t mainMenu = question_box(F("MSX MENU"), menuOptions, 5, 0);
switch (mainMenu) {
case 0:
// Select Cart
setCart_MSX();
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(FS(FSTRING_EMPTY));
// 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 = CORE_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(FS(FSTRING_EMPTY));
// 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(FS(FSTRING_EMPTY));
// 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(FS(FSTRING_EMPTY));
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() {
byte newmsxmapper;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
uint8_t 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(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
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() {
byte newmsxsize;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
if (msxlo == msxhi)
newmsxsize = msxlo;
else {
uint8_t b = 0;
int i = msxlo;
display_Clear();
print_Msg(F("ROM Size: "));
println_Msg(MSX[i]);
println_Msg(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
goto setrom;
}
}
if (newmsxsize > msxhi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(FS(FSTRING_EMPTY));
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() {
byte newmsxramsize;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
if (msxramlo == msxramhi)
newmsxramsize = msxramlo;
else {
uint8_t b = 0;
int i = msxramlo;
display_Clear();
print_Msg(F("RAM Size: "));
println_Msg(MSXRAM[i]);
println_Msg(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
#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(FS(FSTRING_EMPTY));
goto setram;
}
}
if (newmsxramsize > msxramhi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(FS(FSTRING_EMPTY));
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(FS(FSTRING_CURRENT_SETTINGS));
println_Msg(FS(FSTRING_EMPTY));
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(FS(FSTRING_EMPTY));
#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
//******************************************
struct database_entry_MSX {
byte gameMapper;
byte gameSize;
byte ramSize;
};
void readDataLine_MSX(FsFile& database, struct database_entry_MSX* entry) {
// Read mapper
entry->gameMapper = ((database.read() - 48) * 10) + (database.read() - 48);
// Skip over semicolon
database.seekCur(1);
// Read rom size
entry->gameSize = database.read() - 48;
// Skip over semicolon
database.seekCur(1);
// Read ram size
entry->ramSize = database.read() - 48;
// Skip rest of line
database.seekCur(2);
}
void setCart_MSX() {
//go to root
sd.chdir();
struct database_entry_MSX entry;
// Select starting letter
byte myLetter = starting_letter();
// Open database
if (myFile.open("msxcart.txt", O_READ)) {
seek_first_letter_in_database(myFile, myLetter);
if(checkCartSelection(myFile, &readDataLine_MSX, &entry)) {
EEPROM_writeAnything(7, entry.gameMapper);
EEPROM_writeAnything(8, entry.gameSize);
EEPROM_writeAnything(10, entry.ramSize);
}
} else {
print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND));
}
}
#endif