Cherry-picking firmware changes only for Lynx support

Updating header comments

Better blocksize detection, cleanups
This commit is contained in:
Roger Braunstein 2024-07-06 17:39:20 -07:00
parent 9cbc8caad3
commit bd641b6476
5 changed files with 1442 additions and 11 deletions

View File

@ -183,7 +183,7 @@ template<class T> int EEPROM_readAnything(int ee, T& value) {
#define rotate_to_change_STR 15
#define press_to_select_STR 16
// This arrays holds the most often uses strings
// This array holds the most often used strings
constexpr char string_press_button0[] PROGMEM = "Press Button...";
constexpr char string_sd_error1[] PROGMEM = "SD Error";
constexpr char string_did_not_verify3[] PROGMEM = "did not verify";
@ -288,7 +288,7 @@ boolean root = 0;
boolean filebrowse = 0;
// Common
// 21 chars for SNES ROM name, one char for termination
// 21 chars for ROM name, one char for termination
char romName[22];
unsigned long sramSize = 0;
int romType = 0;
@ -456,7 +456,7 @@ uint32_t calculateCRC(char* fileName, char* folder, unsigned long offset) {
/******************************************
CRC Functions for Atari, Fairchild, Ody2, Arc, etc. modules
*****************************************/
#if (defined(ENABLE_ODY2) || defined(ENABLE_ARC) || defined(ENABLE_FAIRCHILD) || defined(ENABLE_MSX) || defined(ENABLE_POKE) || defined(ENABLE_2600) || defined(ENABLE_5200) || defined(ENABLE_7800) || defined(ENABLE_C64) || defined(ENABLE_VECTREX) || defined(ENABLE_NES))
#if (defined(ENABLE_ODY2) || defined(ENABLE_ARC) || defined(ENABLE_FAIRCHILD) || defined(ENABLE_MSX) || defined(ENABLE_POKE) || defined(ENABLE_2600) || defined(ENABLE_5200) || defined(ENABLE_7800) || defined(ENABLE_C64) || defined(ENABLE_VECTREX) || defined(ENABLE_NES) || defined(ENABLE_LYNX))
void printCRC(char* checkFile, uint32_t* crcCopy, unsigned long offset) {
uint32_t crc = calculateCRC(checkFile, folder, offset);
@ -1091,10 +1091,11 @@ constexpr char modeItem22[] PROGMEM = "Casio Loopy";
constexpr char modeItem23[] PROGMEM = "Commodore 64";
constexpr char modeItem24[] PROGMEM = "Atari 5200";
constexpr char modeItem25[] PROGMEM = "Atari 7800";
constexpr char modeItem26[] PROGMEM = "Vectrex";
constexpr char modeItem27[] PROGMEM = "Flashrom Programmer";
constexpr char modeItem28[] PROGMEM = "Self Test (3V)";
constexpr char modeItem29[] PROGMEM = "About";
constexpr char modeItem26[] PROGMEM = "Atari Lynx";
constexpr char modeItem27[] PROGMEM = "Vectrex";
constexpr char modeItem28[] PROGMEM = "Flashrom Programmer";
constexpr char modeItem29[] PROGMEM = "Self Test (3V)";
constexpr char modeItem30[] PROGMEM = "About";
static const char* const modeOptions[] PROGMEM = {
#ifdef ENABLE_GBX
@ -1172,16 +1173,20 @@ static const char* const modeOptions[] PROGMEM = {
#ifdef ENABLE_7800
modeItem25,
#endif
#ifdef ENABLE_VECTREX
#ifdef ENABLE_LYNX
modeItem26,
#endif
#ifdef ENABLE_FLASH
#ifdef ENABLE_VECTREX
modeItem27,
#endif
#ifdef ENABLE_SELFTEST
#ifdef ENABLE_FLASH
modeItem28,
#endif
modeItem29, FSTRING_RESET
#ifdef ENABLE_SELFTEST
modeItem29,
#endif
modeItem30,
FSTRING_RESET
};
uint8_t pageMenu(const __FlashStringHelper* question, const char* const* menuStrings, uint8_t entryCount, uint8_t default_choice = 0) {
@ -1385,6 +1390,13 @@ void mainMenu() {
break;
#endif
#ifdef ENABLE_LYNX
case SYSTEM_MENU_LYNX:
setup_LYNX();
return lynxMenu();
break;
#endif
#ifdef ENABLE_VECTREX
case SYSTEM_MENU_VECTREX:
setup_VECTREX();
@ -3583,6 +3595,9 @@ void loop() {
#ifdef ENABLE_7800
case CORE_7800: return a7800Menu();
#endif
#ifdef ENABLE_LYNX
case CORE_LYNX: return lynxMenu();
#endif
#ifdef ENABLE_VECTREX
case CORE_VECTREX: return vectrexMenu();
#endif

View File

@ -89,6 +89,13 @@
/****/
/* [ Atari LYNX --------------------------------------------------- ]
*/
//#define ENABLE_LYNX
/****/
/* [ Benesse Pocket Challenge W ----------------------------------- ]
*/

263
Cart_Reader/LYNX.ino Normal file
View File

@ -0,0 +1,263 @@
//******************************************
// ATARI LYNX MODULE
//******************************************
//
// For use with SNES-Lynx adapter
// +----+
// | 1 |- GND
// | 2 |- D3
// | 3 |- D2
// | 4 |- D4
// | 5 |- D1
// | 6 |- D5
// | 7 |- D0
// | 8 |- D6
// | 9 |- D7
// | 10 |- /OE
// | 11 |- A1
// | 12 |- A2
// | 13 |- A3
// | 14 |- A6
// | 15 |- A4
// | 16 |- A5
// | 17 |- A0
// | 18 |- A7
// | 19 |- A16
// | 20 |- A17
// | 21 |- A18
// | 22 |- A19
// | 23 |- A15
// | 24 |- A14
// | 25 |- A13
// | 26 |- A12
// | 27 |- /WE
// | 28 |- A8
// | 29 |- A9
// | 30 |- A10
// | 31 |- VCC
// | 32 |- AUDIN
// | 33 |- VCC
// | 34 |- SWVCC
// +----+
//
// By @partlyhuman
// This implementation would not be possible without the invaluable
// documentation on
// https://atarilynxvault.com/
// by Igor (@theatarigamer) of K-Retro Gaming / Atari Lynx Vault
// and the reference implementation of the Lynx Cart Programmer Pi-Hat
// https://bitbucket.org/atarilynx/lynx/src/master/
// by Karri Kaksonen (whitelynx.fi) and Igor as well as countless contributions
// by the Atari Lynx community
//
// Version 1.0
// Future enhancements
// 1. EEPROM read/write
// 2. Homebrew flash cart programming
//
#ifdef ENABLE_LYNX
#pragma region DEFS
#define LYNX_HEADER_SIZE 64
#define LYNX_WE 8
#define LYNX_OE 9
#define LYNX_AUDIN 46
#define LYNX_BLOCKADDR 2048UL
#define LYNX_BLOCKCOUNT 256UL
// Includes \0
static const char LYNX[5] = "LYNX";
// Cart information
static bool lynxUseAudin;
static uint16_t lynxBlockSize;
#pragma region LOWLEVEL
void setup_LYNX() {
setVoltage(VOLTS_SET_5V);
// Address pins output
// A0-7, A8-A16 (A11 doesn't exist)
DDRF = 0xff;
DDRK = 0xff;
DDRL = 0xff;
// Data pins input
DDRC = 0x00;
// Control pins output
// CE is tied low, not accessible
pinMode(LYNX_WE, OUTPUT);
pinMode(LYNX_OE, OUTPUT);
pinMode(LYNX_AUDIN, OUTPUT);
digitalWrite(LYNX_WE, HIGH);
digitalWrite(LYNX_OE, HIGH);
digitalWrite(LYNX_AUDIN, HIGH);
strcpy(romName, LYNX);
mode = CORE_LYNX;
}
static void dataDir_LYNX(byte direction) {
DDRC = (direction == OUTPUT) ? 0xff : 0x00;
}
static uint8_t readByte_LYNX(uint32_t addr, uint8_t audin = 0) {
digitalWrite(LYNX_OE, HIGH);
PORTF = addr & 0xff;
PORTK = (addr >> 8) & 0xff;
PORTL = ((addr >> 16) & 0b111) | (audin << 3);
digitalWrite(LYNX_OE, LOW);
delayMicroseconds(20);
uint8_t data = PINC;
digitalWrite(LYNX_OE, HIGH);
return data;
}
#pragma region HIGHLEVEL
static bool detectBlockSize_LYNX() {
lynxUseAudin = false;
lynxBlockSize = 0;
int i;
uint8_t block[LYNX_BLOCKADDR];
for (i = 0; i < LYNX_BLOCKADDR; i++) {
block[i] = readByte_LYNX(i, 0);
}
for (i = 0; i < LYNX_BLOCKADDR; i++) {
// If any differences are detected when AUDIN=1,
// AUDIN is used to bankswitch
// meaning we also use the maximum block size
// (1024kb cart / 256 blocks = 4kb block bank switched between two
// lower/upper 2kb blocks)
if (block[i] != readByte_LYNX(i, 1)) {
lynxUseAudin = true;
lynxBlockSize = 2048;
return true;
}
}
// Use the already-dumped 2KB to detect mirroring in a small sample
// Valid cart sizes of 128kb, 256kb, 512kb / 256 blocks
// = block sizes of 512b, 1024b, 2048b
const size_t DETECT_BYTES = 128;
for (i = 0; i < DETECT_BYTES; i++) {
if (block[i] != block[i + 256]) {
lynxBlockSize = max(lynxBlockSize, 512);
}
if (block[i] != block[i + 512]) {
lynxBlockSize = max(lynxBlockSize, 1024);
}
if (block[i] != block[i + 1024]) {
lynxBlockSize = max(lynxBlockSize, 2048);
}
}
return (lynxBlockSize > 0);
}
static bool detectCart_LYNX() {
// Could omit logging to save a few bytes
display_Clear();
println_Msg(F("Identifying..."));
if (!detectBlockSize_LYNX()) {
print_STR(error_STR, false);
display_Update();
wait();
resetArduino();
}
print_Msg(F("AUDIN="));
print_Msg(lynxUseAudin);
print_Msg(F(" BLOCK="));
println_Msg(lynxBlockSize);
display_Update();
}
static void writeHeader_LYNX() {
char header[LYNX_HEADER_SIZE] = {};
// Magic number
strcpy(header, LYNX);
// Cart name (dummy)
strcpy(header + 10, LYNX);
// Manufacturer (dummy)
strcpy(header + 42, LYNX);
// Version
header[8] = 1;
// Bank 0 page size
header[4] = lynxBlockSize & 0xff;
// Bank 1 page size
header[5] = (lynxBlockSize >> 8) & 0xff;
// AUDIN used
header[59] = lynxUseAudin;
// TODO detect EEPROM?
// header[60] = lynxUseEeprom;
myFile.write(header, LYNX_HEADER_SIZE);
}
static void readROM_LYNX() {
uint8_t block[lynxBlockSize];
uint32_t i;
dataDir_LYNX(INPUT);
// The upper part of the address is used as a block address
// There are always 256 blocks, but the size of the block can vary
// So outer loop always steps through block addresses
const uint32_t upto = LYNX_BLOCKCOUNT * LYNX_BLOCKADDR;
for (uint32_t blockAddr = 0; blockAddr < upto; blockAddr += LYNX_BLOCKADDR) {
draw_progressbar(blockAddr, upto);
blinkLED();
if (lynxUseAudin) {
// AUDIN bank switching uses a 4kb block split to 2 banks
for (i = 0; i < lynxBlockSize / 2; i++) {
block[i] = readByte_LYNX(blockAddr + i, 0);
}
for (; i < lynxBlockSize; i++) {
block[i] = readByte_LYNX(blockAddr + i - (lynxBlockSize / 2), 1);
}
} else {
for (i = 0; i < lynxBlockSize; i++) {
block[i] = readByte_LYNX(i + blockAddr);
}
}
myFile.write(block, lynxBlockSize);
}
draw_progressbar(upto, upto);
}
#pragma region MENU
static const char* const menuOptionsLYNX[] PROGMEM = {FSTRING_READ_ROM,
FSTRING_RESET};
void lynxMenu() {
size_t menuCount = sizeof(menuOptionsLYNX) / sizeof(menuOptionsLYNX[0]);
convertPgm(menuOptionsLYNX, menuCount);
uint8_t mainMenu = question_box(F("LYNX MENU"), menuOptions, menuCount, 0);
display_Clear();
display_Update();
switch (mainMenu) {
case 0:
sd.chdir("/");
createFolderAndOpenFile(LYNX, "ROM", romName, "lnx");
detectCart_LYNX();
writeHeader_LYNX();
readROM_LYNX();
myFile.close();
sd.chdir("/");
compareCRC("lynx.txt", 0, true, LYNX_HEADER_SIZE);
print_STR(done_STR, true);
display_Update();
wait();
break;
}
}
#endif

View File

@ -214,6 +214,9 @@ enum CORES: uint8_t {
# ifdef ENABLE_7800
CORE_7800,
# endif
# ifdef ENABLE_LYNX
CORE_LYNX,
# endif
# ifdef ENABLE_VECTREX
CORE_VECTREX,
# endif
@ -302,6 +305,9 @@ enum SYSTEM_MENU: uint8_t {
# if defined(ENABLE_7800)
SYSTEM_MENU_7800,
# endif
# if defined(ENABLE_LYNX)
SYSTEM_MENU_LYNX,
# endif
# if defined(ENABLE_VECTREX)
SYSTEM_MENU_VECTREX,
# endif

1140
sd/lynx.txt Normal file

File diff suppressed because it is too large Load Diff