cartreader/Cart_Reader/MSX.ino
sakman55 d6dd621aa9
Update MSX.ino
Added support for the Hudson Soft Bee Pack and Bee Cards.

Requires a Hudson Soft Bee Pack inserted into the MSX Adapter.

Be sure to fully insert the Bee Card into the Bee Pack.

Select Mapper 14 "Hudson Soft Bee Pack" and set the ROM size (16K/32K).
2024-11-05 07:49:24 -10:00

1306 lines
36 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
14, 2, 3, 0, 0, // Hudson Soft Bee Pack
};
// 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 = 15; // (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
}
//******************************************
// 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 {
createFolderAndOpenFile("MSX", "ROM", romName, "bin");
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;
case 14: // Hudson Soft Bee Pack (16K/32K)
CS1_ENABLE;
readSegment_MSX(0x4000,0x8000); // 16K Bank 0
CS1_DISABLE;
if (msxsize == 3) { // 32K
CS2_ENABLE;
readSegment_MSX(0x8000, 0xC000); // +16K Bank 1
CS2_DISABLE;
}
break;
}
myFile.close();
printCRC(fileName, 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();
printCRC(fileName, 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
//******************************************
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
void printMapperSelection_MSX(int index) {
display_Clear();
print_Msg(FS(FSTRING_MAPPER));
msxindex = index * 5;
msxmapselect = pgm_read_byte(msxmapsize + msxindex);
println_Msg(msxmapselect);
printMapper(msxmapselect);
}
#endif
void setMapper_MSX() {
byte newmsxmapper;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
navigateMenu(0, msxmapcount - 1, &printMapperSelection_MSX);
newmsxmapper = msxmapselect;
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-14]: "));
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
//******************************************
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
void printRomSize_MSX(uint8_t index) {
display_Clear();
print_Msg(FS(FSTRING_ROM_SIZE));
println_Msg(MSX[index]);
}
#endif
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;
printRomSize_MSX(i);
printInstructions();
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
printRomSize_MSX(i);
printInstructions();
}
}
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
printRomSize_MSX(i);
printInstructions();
}
}
if (b == 3) { // Long Press - Execute (hold)
newmsxsize = i;
break;
}
}
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(FS(FSTRING_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
//******************************************
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
void printRamSize_MSX(uint8_t index) {
display_Clear();
print_Msg(F("RAM Size: "));
println_Msg(MSXRAM[index]);
}
#endif
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;
printRamSize_MSX(i);
printInstructions();
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
printRamSize_MSX(i);
printInstructions();
}
}
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
printRamSize_MSX(i);
printInstructions();
}
}
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 > 14) {
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(FS(FSTRING_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;
case 14:
println_Msg(F("HUDSON SOFT BEE PACK"));
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"));
Serial.println(F("14 = HUDSON SOFT BEE PACK"));
#endif
}
//******************************************
// CART SELECT CODE
//******************************************
struct database_entry_MSX {
byte gameMapper;
byte gameSize;
byte ramSize;
};
void readDataLine_MSX(FsFile& database, void* entry) {
database_entry_MSX* castEntry = (database_entry_MSX*)entry;
// Read mapper
castEntry->gameMapper = ((database.read() - 48) * 10) + (database.read() - 48);
// Skip over semicolon
database.seekCur(1);
// Read rom size
castEntry->gameSize = database.read() - 48;
// Skip over semicolon
database.seekCur(1);
// Read ram size
castEntry->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