-
Notifications
You must be signed in to change notification settings - Fork 227
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cherry-picking firmware changes only for Lynx support
Updating header comments Better blocksize detection, cleanups
- Loading branch information
1 parent
9cbc8ca
commit bd641b6
Showing
5 changed files
with
1,442 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.