mirror of
https://github.com/sanni/cartreader.git
synced 2024-11-30 08:24:14 +01:00
1072 lines
27 KiB
C++
1072 lines
27 KiB
C++
//******************************************
|
|
// 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
|