mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-27 06:54:15 +01:00
1668 lines
47 KiB
C++
1668 lines
47 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);
|
|
#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();
|
|
#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
|