cartreader/Cart_Reader/TI99.ino

1072 lines
27 KiB
Arduino
Raw Normal View History

2024-07-08 19:25:29 +02:00
//******************************************
// TEXAS INSTRUMENTS TI-99 MODULE
//******************************************
#ifdef ENABLE_TI99
// Texas Instruments TI-99
// Cartridge Pinout
// 36P 2.54mm pitch connector
//
// RIGHT
// +-------+
// Vss -| 36 35 |- GND
// ROMS* -| 34 33 |- Vss
// WE* -| 32 31 |- GR
// A4 -| 30 29 |- -5V B
// A5 -| 28 27 |- GRC O
// T A6 -| 26 25 |- DBIN T
// O A3 -| 24 23 |- A14 T
// P A7 -| 22 21 |- GS* O
// A8 -| 20 19 |- +5V M
// S A9 -| 18 17 |- D0
// I A10 -| 16 15 |- D1 S
// D A11 -| 14 13 |- D2 I
// E A12 -| 12 11 |- D3 D
// A13 -| 10 9 |- D4 E
// A15/OUT -| 8 7 |- D5
// CRUIN -| 6 5 |- D6
// CRUCLK* -| 4 3 |- D7
// GND -| 2 1 |- RESET
// +-------+
// LEFT
//
// TOP SIDE
//
// CRU CRU A15/
// GND CLK* IN OUT A13 A12 A11 A10 A9 A8 A7 A3 A6 A5 A4 WE* ROMS* VSS
// +--------------------------------------------------------------------------+
// | 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 |
// LEFT | | RIGHT
// | 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 |
// +--------------------------------------------------------------------------+
// RST D7 D6 D5 D4 D3 D2 D1 D0 +5V GS* A14 DBIN GRC -5V GR VSS GND
//
// BOTTOM SIDE
// CONTROL PINS:
// RESET(PH0) - SNES RESET
// GRC(PH1) - SNES CPUCLK [GROM CLOCK]
// /GS(PH3) - SNES /CS [GROM SELECT]
// DBIN(PH4) - SNES /IRQ [READ MEMORY/M(GROM DIRECTION)]
// /WE(PH5) - SNES /WR
// /ROMS(PH6) - SNES /RD [ROM SELECT]
// GR(PL0) - SNES A16 [GROM READY]
// /GS = LOW FOR ADDR $9800-$9FFF
// /ROMS = LOW FOR ADDR $6000-$7FFF
// NOTE: TI-99 ADDRESS AND DATA BUS ARE BIG-ENDIAN
// LEAST SIGNIFICANT IS BIT 7 AND MOST SIGNIFICANT IS BIT 0
// PCB ADAPTER WIRED FOR DIFFERENCE
// FOR GROM-ONLY BOARDS, TOP PINS DO NOT EXIST
// DATA PINS ARE MULTIPLEXED TO HANDLE BOTH ADDRESS AND DATA
// D7-D0 = AD7-AD0
//******************************************
// DEFINES
//******************************************
#define DISABLE_GROM PORTH |= (1 << 3) // GROM SELECT 9800-9FFF
#define ENABLE_GROM PORTH &= ~(1 << 3)
#define DISABLE_ROM PORTH |= (1 << 6) // ROM SELECT 6000-7FFF
#define ENABLE_ROM PORTH &= ~(1 << 6)
#define GROM_WRITE PORTH &= ~(1 << 4) // DBIN/M LOW
#define GROM_READ PORTH |= (1 << 4) // DBIN/M HIGH
#define GROM_DATA PORTF &= ~(1 << 1) // A14/MO LOW
#define GROM_ADDR PORTF |= (1 << 1); // A14/MO HIGH
#define GRC_HI PORTH |= (1 << 1)
#define GRC_LOW PORTH &= ~(1 << 1)
//******************************************
// VARIABLES
//******************************************
// Cart Configurations
// Format = {mapper,gromlo,gromhi,romlo,romhi}
static const byte PROGMEM ti99mapsize [] = {
0,0,5,0,4, // Normal Carts (GROM 0K/6K/12K/18K/24K/30K + ROM 0K/4K/8K/12K/16K)
1,1,3,4,4, // MBX (GROM 6K/12K/18K + ROM 16K)
2,1,1,4,4, // TI-CALC (GROM 6K + ROM 16K)
};
byte ti99mapcount = 3; // (sizeof(mapsize)/sizeof(mapsize[0])) / 5;
boolean ti99mapfound = false;
byte ti99mapselect;
int ti99index;
//int GROM[] = {0,8,16,24,32,40}; // padded sizes
int GROM[] = {0,6,12,18,24,30};
byte gromlo = 0; // Lowest Entry
byte gromhi = 5; // Highest Entry
int CROM[] = {0,4,8,12,16};
byte cromlo = 0; // Lowest Entry
byte cromhi = 4; // Highest Entry
byte ti99mapper;
byte newti99mapper;
byte gromsize;
byte newgromsize;
byte cromsize;
byte newcromsize;
byte grommap;
byte newgrommap;
//boolean MBX = 0; // Normal/MBX
// EEPROM MAPPING
// 07 MAPPER
// 08 GROM SIZE
// 09 CROM SIZE
// 13 GROM MAP
//******************************************
// MENU
//******************************************
// Base Menu
static const char ti99MenuItem2[] PROGMEM = "Read Complete Cart";
static const char ti99MenuItem3[] PROGMEM = "Read GROM";
static const char ti99MenuItem6[] PROGMEM = "Set GROM Map";
static const char* const menuOptionsTI99[] PROGMEM = { FSTRING_SELECT_CART, ti99MenuItem2, ti99MenuItem3, FSTRING_READ_ROM, FSTRING_SET_SIZE, ti99MenuItem6, FSTRING_RESET };
void ti99Menu()
{
convertPgm(menuOptionsTI99, 7);
uint8_t mainMenu = question_box(F("TI-99 MENU"), menuOptions, 7, 0);
switch (mainMenu)
{
case 0:
// Select Cart
setCart_TI99();
setup_TI99();
break;
case 1:
// Read Complete Cart
CreateROMFolder_TI99();
readGROM_TI99();
readCROM_TI99();
FinishROMFolder_TI99();
break;
case 2:
// Read GROM
CreateROMFolder_TI99();
readGROM_TI99();
FinishROMFolder_TI99();
break;
case 3:
// Read ROM
CreateROMFolder_TI99();
readCROM_TI99();
FinishROMFolder_TI99();
break;
case 4:
// Set Mapper + Sizes
setMapper_TI99();
checkMapperSize_TI99();
setGROMSize_TI99();
setCROMSize_TI99();
break;
case 5:
// Set GROM Map
setGROMMap_TI99();
break;
case 6:
// reset
resetArduino();
break;
}
}
//******************************************
// SETUP
//******************************************
void setup_TI99()
{
// Request 5V
setVoltage(VOLTS_SET_5V);
// Set Address Pins to Output
// TI-99 uses A0-A15
// SNES A0-A7 [TI-99 A15-A8]
DDRF = 0xFF;
// SNES A8-A15 [TI-99 A7-A0]
DDRK = 0xFF;
// SNES A16-A23 - Use A16 for GR
DDRL = 0xFE; // A16 to Input
// Set Control Pins to Output
// RST(PH0) GRC(PH1) /GS(PH3) DBIN(PH4) /WE(PH5) /ROMS(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) GRC(PH1) /GS(PH3) DBIN(PH4) /WE(PH5) /ROMS(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set Unused Data Pins (PA0-PA7) to Output
DDRA = 0xFF;
// Set Unused PORTL Pins HIGH except GR(PL0)
PORTL |= (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)| (1 << 7);
// Set Unused Pins HIGH
PORTA = 0xFF;
PORTJ |= (1 << 0); // TIME(PJ0)
// Set Reset LOW
PORTH &= ~(1 << 0);
checkStatus_TI99();
strcpy(romName, "TI99");
mode = CORE_TI99;
}
//******************************************
// READ FUNCTIONS
//******************************************
uint8_t readROM_TI99(uint16_t addr) // Add Input Pullup
{
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A15
NOP;
NOP;
NOP;
NOP;
NOP;
// DDRC = 0x00; // Set to Input
PORTC = 0xFF; // Input Pullup
NOP;
NOP;
NOP;
NOP;
NOP;
uint8_t ret = PINC;
NOP;
NOP;
NOP;
NOP;
NOP;
return ret;
}
void readSegment_TI99(uint16_t startaddr, uint16_t endaddr)
{
for (uint16_t addr = startaddr; addr < endaddr; addr += 512) {
for (int w = 0; w < 512; w++) {
uint8_t temp = readROM_TI99(addr + w);
sdBuffer[w] = temp;
}
myFile.write(sdBuffer, 512);
}
}
void setupGROM()
{
// Setup GROM
DISABLE_GROM;
GROM_READ; // DBIN(M) HIGH = READ
GROM_ADDR; // A14(MO) HIGH = ADDR
GROM_DATA; // A14(MO) LOW = DATA
}
void pulseGRC(int times)
{
for (int i = 0; i < (times * 2); i++) {
PORTH ^= (1 << 1);
// NOP (62.5ns) x 20 = 1250ns = 1.25us
// NOP; NOP; NOP; NOP; NOP; // 20 NOPs = 20 x 62.5ns = 1250ns x 2 = 2.5us = 400 kHz
// NOP; NOP; NOP; NOP; NOP;
// NOP; NOP; NOP; NOP; NOP;
// NOP; NOP; NOP; NOP; NOP;
// Switch to 100 kHz due to some GROMs not reading out properly at faster speeds
delayMicroseconds(5); // 5us x 2 = 10us = 100 kHz
}
}
void checkGRC()
{
byte statusGR = PINL & 0x1;
pulseGRC(1);
while (!statusGR) {
statusGR = PINL & 0x1;
pulseGRC(1);
}
}
void pulseGROM(uint16_t addr) // Controlled Pulse Code
{
// GRC starts here
GRC_LOW; // START LOW
pulseGRC(16);
ENABLE_GROM;
pulseGRC(8);
DISABLE_GROM;
// Write Base
GROM_WRITE; // DBIN(M) LOW = WRITE
GROM_ADDR; // A14(MO) HIGH = ADDR
DDRC = 0xFF; // Output
PORTC = (addr >> 8) & 0xFF;
pulseGRC(16);
ENABLE_GROM;
pulseGRC(4); // this works
// checkGRC(); // Disable checkGRC() otherwise Q-bert hangs (GROM 7 at 0xE000 with NO GROMs at 0x6000/0x8000/0xA000/0xC000)
pulseGRC(2); // Replace checkGRC() with 2 pulse cycles
pulseGRC(10);
DISABLE_GROM;
pulseGRC(16);
PORTC = addr & 0xFF;
pulseGRC(16);
ENABLE_GROM;
pulseGRC(4); // this works
// checkGRC(); // Disable checkGRC() otherwise Q-bert hangs (GROM 7 at 0xE000 with NO GROMs at 0x6000/0x8000/0xA000/0xC000)
pulseGRC(2); // Replace checkGRC() with 2 pulse cycles
pulseGRC(10);
DISABLE_GROM;
pulseGRC(16);
GROM_READ; // DBIN(M) HIGH = READ
GROM_DATA; // A14(MO) LOW = DATA
DDRC = 0x00;
pulseGRC(16);
}
void readSegmentGROM_TI99(uint32_t startaddr, uint32_t endaddr)
{
for (uint32_t addr = startaddr; addr < endaddr; addr += 512) {
pulseGROM(addr); //
for (int y = 0; y < 512; y++) {
ENABLE_GROM;
pulseGRC(2);
sdBuffer[y] = PINC;
pulseGRC(6);
DISABLE_GROM;
pulseGRC(16);
}
myFile.write(sdBuffer, 512);
}
}
//******************************************
// WRITE FUNCTION
//******************************************
void writeData_TI99(uint16_t addr, uint8_t data)
{
PORTF = addr & 0xFF; // A0-A7
PORTK = (addr >> 8) & 0xFF; // A8-A15
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0xFF; // Set to Output
PORTC = data;
NOP;
NOP;
NOP;
NOP;
NOP;
// Set /WE(PH5) to LOW
PORTH &= ~(1 << 5); // /WE LOW
NOP;
NOP;
NOP;
NOP;
NOP;
// Set /WE(PH5) to HIGH
PORTH |= (1 << 5);
NOP;
NOP;
NOP;
NOP;
NOP;
DDRC = 0x00; // Reset to Input
}
//******************************************
// ROM FOLDER
//******************************************
void CreateROMFolder_TI99()
{
sd.chdir();
EEPROM_readAnything(0, foldern);
sprintf(folder, "TI99/ROM/%d", foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
}
void FinishROMFolder_TI99()
{
foldern += 1;
EEPROM_writeAnything(0, foldern); // FOLDER #
sd.chdir();
}
//******************************************
// READ ROM
//******************************************
// CARTRIDGE GROMS 3-7 (GROMS 0-2 ARE IN THE CONSOLE)
// GROM 3 = $6000-$77FF
// GROM 4 = $8000-$97FF
// GROM 5 = $A000-$B7FF
// GROM 6 = $C000-$D7FF
// GROM 7 = $E000-$F7FF
// GROM ACCESS DETAILS
// INTERNAL POINTER AUTO INCREMENTED EACH TIME CHIP ACCESSED
// SET VALUE OF POINTER BY PASSING TWO BYTES TO THE CHIP
void readGROM_TI99()
{
display_Clear();
if (gromsize == 0) {
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
println_Msg(F("GROM SIZE 0K"));
display_Update();
#else
Serial.println(F("GROM SIZE 0K"));
#endif
}
else {
// println_Msg(F("USE GOOD POWER SUPPLY"));
// println_Msg(FS(FSTRING_EMPTY));
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// Split into Individual GROM Files
for (int x = 3; x < 8; x++) {
if (((grommap >> x) & 0x1) == 1) {
snprintf(fileName, sizeof(fileName), "%s.g%d.bin", romName, x);
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT))
print_Error(F("Can't create file on SD"));
display_Clear();
print_Msg(F("Reading GROM "));
println_Msg(x);
println_Msg(FS(FSTRING_EMPTY));
display_Update();
// Normal GROM 6KB/12KB/18KB/24KB/30K
// MBX GROM 6KB/12KB/18KB
DISABLE_ROM;
ENABLE_GROM;
setupGROM();
if (x == 3)
readSegmentGROM_TI99(0x6000,0x7800); // 6K
else if (x == 4)
readSegmentGROM_TI99(0x8000,0x9800); // +6K = 12K
else if (x == 5)
readSegmentGROM_TI99(0xA000,0xB800); // +6K = 18K
else if (x == 6)
readSegmentGROM_TI99(0xC000,0xD800); // +6K = 24K
else if (x == 7)
readSegmentGROM_TI99(0xE000,0xF800); // +6K = 30K
DISABLE_GROM;
myFile.close();
printCRC(fileName, NULL, 0);
}
}
}
println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
display_Update();
wait();
}
// CARTRIDGE ROM
// EACH CART UP TO 8K MAPPED AT $6000-$7FFF
void readCROM_TI99()
{
display_Clear();
if (cromsize == 0) {
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
println_Msg(F("ROM SIZE 0K"));
display_Update();
#else
Serial.println(F("ROM SIZE 0K"));
#endif
}
else {
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
snprintf(fileName, sizeof(fileName), "%s.c.bin", romName);
// open file on sdcard
if (!myFile.open(fileName, O_RDWR | O_CREAT))
print_Error(F("Can't create file on SD"));
if (ti99mapper == 1) { // MBX
// MBX Cart ROM 16K
// Fixed Bank 0 at 0x6000 (RAM at 0x6C00)
// Bankswitch 0x7000-0x7FFF using write 1/2/3 to 0x6FFE
DISABLE_GROM;
ENABLE_ROM;
readSegment_TI99(0x6000, 0x6C00); // 3K
for (int w = 0; w < 1024; w += 512) { // 1K RAM at 0x6C00
for (int x = 0; x < 512; x++) {
sdBuffer[x] = 0xFF; // Replace Random RAM Contents with 0xFF
}
myFile.write(sdBuffer, 512);
}
for (int y = 1; y < 4; y++) {
writeData_TI99(0x6FFE, y); // Set Bank 1/2/3
readSegment_TI99(0x7000, 0x8000); // 4K x 3 = +12K = 16K
}
}
else if (ti99mapper == 2) { // TI-CALC [UNTESTED]
// TI-CALC ROM 16K
// Fixed Bank 0 at 0x6000-0x6FFF
// Bankswitch 0x7000-0x7FFF using write to 0x7000/0x7002/0x7004/0x7006
DISABLE_GROM;
ENABLE_ROM;
writeData_TI99(0x7000, 0x00); // Set Bank 0
readSegment_TI99(0x7000,0x8000); // 4K
writeData_TI99(0x7002, 0x00); // Set Bank 1
readSegment_TI99(0x7000,0x8000); // +4K = 8K
writeData_TI99(0x7004, 0x00); // Set Bank 2
readSegment_TI99(0x7000, 0x8000); // +4K = 12K
writeData_TI99(0x7006, 0x00); // Set Bank 3
readSegment_TI99(0x7000, 0x8000); // +4K = 16K
}
else {
// Normal Cart ROM 4K/8K/12K/16K
// ROM Space 0x6000-0x7FFF
// Bankswitch 0x7000-0x7FFF using write to 0x6000/0x6002
DISABLE_GROM;
ENABLE_ROM;
writeData_TI99(0x6000, 0x00); // Set Bank 0
readSegment_TI99(0x6000,0x7000); // 4K
if (cromsize > 1) {
readSegment_TI99(0x7000,0x8000); // +4K = 8K
if (cromsize > 2) {
writeData_TI99(0x6002, 0x00); // Set Bank 1
if (cromsize > 3) // 16K
readSegment_TI99(0x6000, 0x7000); // +4K = 16K
readSegment_TI99(0x7000, 0x8000); // +4K = 12K
}
}
}
DISABLE_ROM;
myFile.close();
printCRC(fileName, NULL, 0);
}
println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
display_Update();
wait();
}
//******************************************
// CHECK STATUS
//******************************************
void checkStatus_TI99()
{
EEPROM_readAnything(7, ti99mapper);
EEPROM_readAnything(8, gromsize);
EEPROM_readAnything(9, cromsize);
EEPROM_readAnything(13, grommap);
if (ti99mapper > (ti99mapcount - 1)) {
ti99mapper = 0;
EEPROM_writeAnything(7, ti99mapper);
}
if (gromsize > gromhi) {
gromsize = 1; // default 6K
EEPROM_writeAnything(8, gromsize);
}
if (cromsize > cromhi) {
cromsize = 2; // default 8K
EEPROM_writeAnything(9, cromsize);
}
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
println_Msg(F("TI-99 READER"));
println_Msg(FS(FSTRING_CURRENT_SETTINGS));
println_Msg(FS(FSTRING_EMPTY));
print_Msg(F("MAPPER: "));
// println_Msg(ti99mapper);
printMapper_TI99(ti99mapper);
print_Msg(F("GROM SIZE: "));
print_Msg(GROM[gromsize]);
println_Msg(F("KB"));
print_Msg(F("ROM SIZE: "));
print_Msg(CROM[cromsize]);
println_Msg(F("KB"));
print_Msg(F("GROM MAP: "));
display_Update();
readGROMMap();
wait();
#else
Serial.print(F("CURRENT MAPPER: "));
Serial.println(ti99mapper);
Serial.print(F("GROM SIZE: "));
Serial.print(GROM[gromsize]);
Serial.println(F("KB"));
Serial.print(F("ROM SIZE: "));
Serial.print(CROM[cromsize]);
Serial.println(F("KB"));
Serial.print(F("GROM MAP: "));
readGROMMap();
Serial.println(F(""));
#endif
}
void printMapper_TI99(byte ti99maplabel)
{
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
switch (ti99maplabel)
{
case 0:
println_Msg(F("NORMAL"));
break;
case 1:
println_Msg(F("MBX"));
break;
case 2:
println_Msg(F("TI-CALC"));
break;
}
#else
Serial.println(F("0 = NORMAL"));
Serial.println(F("1 = MBX"));
Serial.println(F("2 = TI-CALC"));
#endif
}
//******************************************
// MAPPER CODE
//******************************************
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
void printMapperSelection_TI99(int index)
{
display_Clear();
print_Msg(FS(FSTRING_MAPPER));
ti99index = index * 5;
ti99mapselect = pgm_read_byte(ti99mapsize + ti99index);
println_Msg(ti99mapselect);
}
#endif
void setMapper_TI99()
{
byte newti99mapper;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
navigateMenu(0, ti99mapcount - 1, &printMapperSelection_TI99);
newti99mapper = ti99mapselect;
display.setCursor(0, 56);
print_Msg(F("MAPPER "));
print_Msg(newti99mapper);
println_Msg(F(" SELECTED"));
display_Update();
delay(1000);
#else
setmapper:
String newmap;
ti99mapfound = false;
printMapper_TI99(0);
Serial.print(F("Enter Mapper [0-2]: "));
while (Serial.available() == 0) {}
newmap = Serial.readStringUntil('\n');
Serial.println(newmap);
newti99mapper = newmap.toInt();
for (int i = 0; i < ti99mapcount; i++) {
ti99index = i * 5;
ti99mapselect = pgm_read_byte(ti99mapsize + ti99index);
if (newti99mapper == ti99mapselect)
ti99mapfound = true;
}
if (ti99mapfound == false) {
Serial.println(F("MAPPER NOT SUPPORTED!"));
Serial.println(F(""));
newti99mapper = 0;
goto setmapper;
}
#endif
EEPROM_writeAnything(7, newti99mapper);
ti99mapper = newti99mapper;
}
void checkMapperSize_TI99()
{
for (int i = 0; i < ti99mapcount; i++) {
ti99index = i * 5;
byte mapcheck = pgm_read_byte(ti99mapsize + ti99index);
if (mapcheck == ti99mapper) {
gromlo = pgm_read_byte(ti99mapsize + ti99index + 1);
gromhi = pgm_read_byte(ti99mapsize + ti99index + 2);
cromlo = pgm_read_byte(ti99mapsize + ti99index + 3);
cromhi = pgm_read_byte(ti99mapsize + ti99index + 4);
break;
}
}
}
//******************************************
// GROM SIZE
//******************************************
void setGROMSize_TI99()
{
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
if (gromlo == gromhi)
newgromsize = gromlo;
else {
int b = 0;
int i = gromlo;
while (1) {
display_Clear();
print_Msg(F("GROM Size: "));
println_Msg(GROM[i]);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("Press to Change"));
println_Msg(F("Hold to Select"));
display_Update();
b = checkButton();
if (b == 2) { // Previous
if (i == gromlo)
i = gromhi;
else
i--;
}
if (b == 1) { // Next
if (i == gromhi)
i = gromlo;
else
i++;
}
if (b == 3) { // Long Press - Execute
newgromsize = i;
break;
}
}
display.setCursor(0, 48); // Display selection at bottom
}
print_Msg(F("GROM SIZE "));
print_Msg(GROM[newgromsize]);
println_Msg(F("KB"));
display_Update();
EEPROM_writeAnything(8, newgromsize);
gromsize = newgromsize;
// Default GROM Map to sequential GROMs
newgrommap = 0;
for (int x = 0; x < gromsize; x++){
newgrommap = (newgrommap | (1 << (x + 3)));
}
EEPROM_writeAnything(13, newgrommap);
grommap = newgrommap;
print_Msg(F("GROM MAP "));
display_Update();
readGROMMap();
wait();
#else
if (gromlo == gromhi)
newgromsize = gromlo;
else {
setgrom:
String sizeGROM;
for (int i = 0; i < (gromhi - gromlo + 1); i++) {
Serial.print(F("Select GROM Size: "));
Serial.print(i);
Serial.print(F(" = "));
Serial.print(GROM[i + gromlo]);
Serial.println(F("KB"));
}
Serial.print(F("Enter GROM Size: "));
while (Serial.available() == 0) {}
sizeGROM = Serial.readStringUntil('\n');
Serial.println(sizeGROM);
newgromsize = sizeGROM.toInt() + gromlo;
if (newgromsize > gromhi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(FS(FSTRING_EMPTY));
goto setgrom;
}
}
Serial.print(F("GROM SIZE "));
Serial.print(GROM[newgromsize]);
Serial.println(F("KB"));
EEPROM_writeAnything(8, newgromsize);
gromsize = newgromsize;
// Default GROM Map to sequential GROMs
newgrommap = 0;
for (int x = 0; x < gromsize; x++){
newgrommap = (newgrommap | (1 << (x + 3)));
}
EEPROM_writeAnything(13, newgrommap);
grommap = newgrommap;
Serial.print(F("GROM MAP "));
readGROMMap();
#endif
}
void readGROMMap()
{
newgromsize = 0;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
if (grommap == 0)
print_Msg(FS(FSTRING_EMPTY));
else {
for (int x = 3; x < 8; x++) {
if (((grommap >> x) & 0x1) == 1) {
print_Msg(x);
newgromsize++;
}
}
}
println_Msg(FS(FSTRING_EMPTY));
display_Update();
#else
if (grommap == 0)
Serial.print(0);
else (
for (int x = 3; x < 8; x++) {
if (((grommap >> x) & 0x1) == 1) {
Serial.print(x);
newgromsize++;
}
}
}
Serial.println(FS(FSTRING_EMPTY));
#endif
if (gromsize != newgromsize) {
EEPROM_writeAnything(8, newgromsize);
gromsize = newgromsize;
}
}
void setGROMMap_TI99()
{
newgrommap = 0;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
int b = 0;
for(int i = 3; i < 8; i++) {
display_Clear();
print_Msg(F("Enable GROM "));
println_Msg(i);
println_Msg(FS(FSTRING_EMPTY));
println_Msg(F("Press to Skip GROM"));
println_Msg(F("Hold to Enable GROM"));
display_Update();
while (1) {
b = checkButton();
if (b == 2) { // Previous
break;
}
if (b == 1) { // Next
break;
}
if (b == 3) { // Long Press - Execute
newgrommap = (newgrommap | (1 << i));
println_Msg(FS(FSTRING_EMPTY));
print_Msg(F("GROM "));
print_Msg(i);
println_Msg(F(" ENABLED"));
display_Update();
break;
}
}
b = 0;
delay(1000);
}
EEPROM_writeAnything(13, newgrommap);
grommap = newgrommap;
display.setCursor(0, 56); // Display selection at bottom
print_Msg(F("GROM MAP: "));
display_Update();
readGROMMap();
delay(1000);
#else
String mapGROM;
for (int i = 3; i < 8; i++) {
Serial.print(F("Enable GROM "));
Serial.println(i);
Serial.println(F("0 = NO"));
Serial.println(F("1 = YES"));
while (Serial.available() == 0) {}
mapCROM = Serial.readStringUntil('\n');
Serial.println(mapGROM);
if (mapGROM.toInt() == 1)
newgrommap = (newgrommap | (1 << i));
}
EEPROM_writeAnything(13, newgrommap);
grommap = newgrommap;
Serial.print(F("GROM MAP: "));
readGROMMap();
#endif
}
//******************************************
// ROM SIZE
//******************************************
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
void printRomSize_TI99(int index)
{
display_Clear();
print_Msg(FS(FSTRING_ROM_SIZE));
println_Msg(CROM[index]);
}
#endif
void setCROMSize_TI99()
{
byte newcromsize;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
if (cromlo == cromhi)
newcromsize = cromlo;
else {
newcromsize = navigateMenu(cromlo, cromhi, &printRomSize_TI99);
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(FS(FSTRING_ROM_SIZE));
print_Msg(CROM[newcromsize]);
println_Msg(F("KB"));
display_Update();
delay(1000);
#else
if (cromlo == cromhi)
newcromsize = cromlo;
else {
setrom:
String sizeROM;
for (int i = 0; i < (cromhi - cromlo + 1); i++) {
Serial.print(F("Select ROM Size: "));
Serial.print(i);
Serial.print(F(" = "));
Serial.print(CROM[i + cromlo]);
Serial.println(F("KB"));
}
Serial.print(F("Enter ROM Size: "));
while (Serial.available() == 0) {}
sizeROM = Serial.readStringUntil('\n');
Serial.println(sizeROM);
newcromsize = sizeROM.toInt() + cromlo;
if (newcromsize > cromhi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(FS(FSTRING_EMPTY));
goto setrom;
}
}
Serial.print(F("ROM Size = "));
Serial.print(CROM[newcromsize]);
Serial.println(F("KB"));
#endif
EEPROM_writeAnything(8, newcromsize);
cromsize = newcromsize;
}
//******************************************
// CART SELECT CODE
//******************************************
struct database_entry_TI99 {
byte gameMapper;
byte gromSize;
byte gromMap;
byte romSize;
};
void readDataLine_TI99(FsFile& database, void* entry)
{
struct database_entry_TI99* castEntry = (database_entry_TI99*)entry;
// Read Maooer
castEntry->gameMapper = database.read() - 48;
// Skip over semicolon
database.seekCur(1);
// Read grom size
castEntry->gromSize = database.read() - 48;
// Skip over semicolon
database.seekCur(1);
// Read grom map
// Read the next ascii character and subtract 48 to convert to decimal
castEntry->gromMap = ((database.read() - 48) * 100) + ((database.read() - 48) * 10) + (database.read() - 48);
// Skip over semicolon
database.seekCur(1);
// Read grom size
castEntry->romSize = database.read() - 48;
// Skip rest of line
database.seekCur(2);
}
void printDataLine_TI99(void* entry)
{
struct database_entry_TI99* castEntry = (database_entry_TI99*)entry;
print_Msg(F("CODE: M"));
print_Msg(castEntry->gameMapper);
print_Msg(F("/G"));
print_Msg(castEntry->gromSize);
print_Msg(F("/C"));
print_Msg(castEntry->romSize);
print_Msg(F("/X"));
println_Msg(castEntry->gromMap);
}
void setCart_TI99()
{
sd.chdir();
struct database_entry_TI99 entry;
// Select starting letter
byte myLetter = starting_letter();
if (myFile.open("ti99cart.txt", O_READ)) {
seek_first_letter_in_database(myFile, myLetter);
if(checkCartSelection(myFile, &readDataLine_TI99, &entry, &printDataLine_TI99)) {
EEPROM_writeAnything(7, entry.gameMapper);
EEPROM_writeAnything(8, entry.gromSize);
EEPROM_writeAnything(9, entry.romSize);
EEPROM_writeAnything(13, entry.gromMap);
}
} else {
print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND));
}
}
#endif