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