//******************************************
// LEAP MODULE
//******************************************
#ifdef ENABLE_LEAP
// Leapster
// Cartridge pinout
// 60P 1.27mm pitch connector
//
//     FRONT               BACK
//      SIDE               SIDE
//            +---------+
//       VCC -| 1B   1A |- VCC
//        nc -| 2B   2A |- GND
//       D11 -| 3B   3A |- D4
//        D3 -| 4B   4A |- D12
//       D10 -| 5B   5A |- D5
//       GND -| 6B   6A |- D2
//       D13 -| 7B   7A |- D9
//        nc -| 8B   8A |- nc 
//        D6 -| 9B   9A |- D1
//       D14 -| 10B 10A |- D8
//            |---------|
//        D7 -| 11B 11A |- GND
//        D0 -| 12B 12A |- D15
//      BYTE -| 13B 13A |- OE
//        nc -| 14B 14A |- A22
//    FLA_CE -| 15B 15A |- A23
//        CE -| 16B 16A |- A16
//        A0 -| 17B 17A |- A15
//        A1 -| 18B 18A |- A14
//        A2 -| 19B 19A |- A13
//       GND -| 20B 20A |- A3
//       A12 -| 21B 21A |- A4
//       A11 -| 22B 22A |- A5
//       A10 -| 23B 23A |- A6
//        A9 -| 24B 24A |- A7
//        A8 -| 25B 25A |- A17
//       A19 -| 26B 26A |- A18
//       A20 -| 27B 27A |- A21
//        WE -| 28B 28A |- GND
//   EEP_SDA -| 29B 29A |- EEP_WP
//        nc -| 30B 30A |- EEP_SCL
//            +---------+
//
// CONTROL PINS:
// EEPROM WP (PH0) - SNES /RESET
// EEPROM SCL (PH1) - SNES CPUCLK
// EEPROM SDA (PH4) - SNES /IRQ
// OE (PH3) - SNES /CART
// WE (PH5) - SNES /WR
// CE (PH6) - SNES /RD
// BYTE (PE5) - SNES REFRESH
// FLASH CE (PJ0) - SNES /PARD

//******************************************
// DEFINES
//******************************************
// CONTROL PINS - OE/WE/CE
#define OE_HIGH PORTH |= (1<<3)
#define OE_LOW PORTH &= ~(1<<3) 
#define WE_HIGH PORTH |= (1<<5)
#define WE_LOW PORTH &= ~(1<<5)
#define CE_HIGH PORTH |= (1<<6)
#define CE_LOW PORTH &= ~(1<<6)

// BYTE PIN IS A0 IN BYTE MODE - SHIFT ADDRESS >> 1
#define BYTE_HIGH PORTE |= (1<<5)
#define BYTE_LOW PORTE &= ~(1<<5)

// SERIAL I2C EEPROM PINS 24LC02B 2K
// PIN 5 - SDA
// PIN 6 - SCL
// PIN 7 - WP
// CART B30 = SDA
// CART A31 = SCL
// CART A30 = WP
#define WP_HIGH PORTH |= (1<<0)
#define WP_LOW PORTH &= ~(1<<0)
#define SCL_HIGH PORTH |= (1<<1)
#define SCL_LOW PORTH &= ~(1<<1)
#define SDA_HIGH PORTH |= (1<<4)
#define SDA_LOW PORTH &= ~(1<<4)

// FLASH 39VF040 512K
// CART B16 = CE#
#define FL_CE_HIGH PORTJ |= (1<<0)
#define FL_CE_LOW PORTJ &= ~(1<<0)

#define DATA_C_READ { DDRC = 0; PORTC = 0xFF; } // [INPUT PULLUP]
#define DATA_A_READ { DDRA = 0; PORTA = 0xFF; } // [INPUT PULLUP]

//******************************************
// VARIABLES
//******************************************
// ROM File = LEAP.bin
// RAM File = SAVE.eep
// FLASH File = SAVE.fla

byte LEAPSTER[] = {4,8,16};
byte leaplo = 0; // Lowest Entry
byte leaphi = 2; // Highest Entry
byte leapsize;
byte newleapsize;

byte tempbyte;
word tempword;
word ptrword;
word tempcheck;

// EEPROM MAPPING
// 08 ROM SIZE

//******************************************
// ROM STRUCTURE
//******************************************
char LEAP[] = {0x4C,0x45,0x41,0x50}; // "LEAP" MARKER AT 0x00 OR 0x144 (WORD 0xA2)
char TBL[] = {0x01,0x00,0x00,0x01};  // TABLE START
char TXT[] = {0x04,0x00,0x00,0x01};  // NEXT DWORD IS TEXT BLOCK [5 SENTENCES] START
char VER[] = {0x0A,0x00,0x00,0x01};  // NEXT DWORD IS ROM VERSION LOCATION
char TTL[] = {0x0B,0x00,0x00,0x01};  // NEXT DWORD IS ROM TITLE LOCATION
char END[] = {0x10,0x00,0x00,0x01}; // LAST BLOCK - END SEARCH
word sentenceAddr = 0; // Sentence Block Start Address
word versionAddr = 0;  // Version Address
word titleAddr = 0;    // Title Address
char ROMVersion[20];   // Fosters [20] "155-11172 152-11808"
char ROMTitle[50];     // "Mr. Pencils Learn to Draw and Write." [37]
                       // "Thomas and Friends Calling All Engines" [39]
                       // "The Letter Factory.v1.0 - Initial Release JBM3" [47]

//******************************************
// DATA INTEGRITY BLOCK
//******************************************
// 5 Sentences - 172 bytes 
// Location not static between ROMs

static const unsigned char LeapCheck [] = {
0x4C,0x69,0x6C,0x20,0x64,0x75,0x63,0x6B,0x65,0x64,0x2E,0x20,0x20,0x54,0x68,0x65,
0x20,0x6A,0x65,0x74,0x20,0x7A,0x69,0x70,0x70,0x65,0x64,0x20,0x70,0x61,0x73,0x74,
0x20,0x68,0x65,0x72,0x20,0x68,0x65,0x61,0x64,0x2E,0x20,0x20,0x44,0x75,0x73,0x74,
0x20,0x66,0x6C,0x65,0x77,0x2C,0x20,0x4C,0x69,0x6C,0x20,0x73,0x6E,0x65,0x65,0x7A,
0x65,0x64,0x2C,0x20,0x61,0x6E,0x64,0x20,0x4C,0x65,0x61,0x70,0x20,0x74,0x75,0x72,
0x6E,0x65,0x64,0x20,0x72,0x65,0x64,0x2E,0x20,0x20,0x54,0x68,0x65,0x6E,0x20,0x4C,
0x69,0x6C,0x20,0x67,0x6F,0x74,0x20,0x75,0x70,0x2C,0x20,0x61,0x62,0x6F,0x75,0x74,
0x20,0x74,0x6F,0x20,0x79,0x65,0x6C,0x6C,0x2E,0x20,0x20,0x4C,0x65,0x61,0x70,0x20,
0x67,0x61,0x73,0x70,0x65,0x64,0x2C,0x20,0x22,0x4C,0x6F,0x6F,0x6B,0x2C,0x20,0x4C,
0x69,0x6C,0x21,0x20,0x20,0x59,0x6F,0x75,0x72,0x20,0x74,0x6F,0x6F,0x74,0x68,0x21,
0x20,0x20,0x49,0x74,0x20,0x66,0x65,0x6C,0x6C,0x21,0x22,0x00 
};

//******************************************
// MENU
//******************************************
// Base Menu
static const char leapmenuItem4[] PROGMEM = "Read EEPROM";
static const char leapmenuItem5[] PROGMEM = "Write EEPROM";
static const char leapmenuItem6[] PROGMEM = "Read FLASH";
static const char leapmenuItem7[] PROGMEM = "Write FLASH";
static const char* const menuOptionsLEAP[] PROGMEM = {  FSTRING_SELECT_CART, FSTRING_READ_ROM, FSTRING_SET_SIZE, leapmenuItem4, leapmenuItem5, leapmenuItem6, leapmenuItem7 };

void leapMenu()
{
  convertPgm(menuOptionsLEAP, 7);
  uint8_t mainMenu = question_box(F("LEAPSTER MENU"), menuOptions, 7, 0);

  switch (mainMenu)
  {
    // Select Cart
    case 0:
      setCart_LEAP();
      setup_LEAP();
      break;

    // Read ROM
    case 1:
      sd.chdir("/");
      readROM_LEAP();
      sd.chdir("/");
      break;

    // Set Size
    case 2:
      setROMSize_LEAP();
      break;

    // Read EEPROM
    case 3:
      sd.chdir("/");
      readEEP_LEAP();
      sd.chdir("/");
      break;

    // Write EEPROM
    case 4:
      writeEEP_LEAP();
      break;

    // Read FLASH
    case 5:
      idFLASH_LEAP();
      if (strcmp(flashid_str, "BFD7") == 0) {
        sd.chdir("/");
        readFLASH_LEAP();
        sd.chdir("/");
      }
      break;

    // Write FLASH
    case 6:
      idFLASH_LEAP();
      if (strcmp(flashid_str, "BFD7") == 0)
        writeFLASH_LEAP();
      break;
  }
}

//******************************************
// SETUP
//******************************************

void setup_LEAP()
{
  // Request 3.3V
  setVoltage(VOLTS_SET_3V3);

  // Control Pins PH0..PH1, PH3..PH6  BYTE, OE, CE, WE
  DDRH = 0x7B;  // 0b01111011 - CE, WE, SDA, OE, SCL, WP [OUTPUT] - Unused Pins [INPUT]
  PORTH = 0xFF; // 0b11111111 - CE, WE, SDA, OE, SCL, WP [HIGH] - Unused Pins [HIGH]

  // Address Pins
  DDRF = 0xFF;  // Address A0-A7 [OUTPUT]
  DDRK = 0xFF;  // Address A8-A15 [OUTPUT]
  DDRL = 0xFF;  // Address A16-A23 [OUTPUT]
  // Data Pins
  DDRC = 0x00;  // D0-D7 [INPUT]
  DDRA = 0x00;  // D8-D15 [INPUT]
  // BYTE Pin PE5
  DDRE = 0x20;  // 0b00100000 BYTE [OUTPUT] - Unused Pins [INPUT]
  PORTE = 0xFF; // 0b11111111 BYTE [HIGH] - Unused Pins [HIGH]
  // Flash Pin PJ0
  DDRJ = 0x01;  // 0b00000001 - FLA_CE [OUTPUT] - Unused Pins [INPUT]
  PORTJ = 0xFF; // 0b11111111 - FLA_CE [HIGH] - Unused Pins {HIGH]

  // Check Start for "LEAP" marker
  // Read ROM Version & Title
  checkStart_LEAP();

  // 39VF040 Flash Check
//  idFLASH();

  checkStatus_LEAP();
  strncpy(romName, ROMTitle, 16);  // Truncate ROMTitle to fit

  mode = CORE_LEAP;
}

//******************************************
// READ FUNCTIONS
//******************************************
// Max ROM Size 0x1000000 (Highest Address = 0xFFFFFF) - FF FFFF
word read_rom_word_LEAP(unsigned long address) // OLD TIMING #1
{
  PORTL = (address >> 16) & 0xFF;
  PORTK = (address >> 8) & 0xFF;
  PORTF = address & 0xFF;
  CE_LOW;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  OE_LOW;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  unsigned char data1 = PINC;
  unsigned char data2 = PINA;
  word data = (data1 << 8) | (data2);
  OE_HIGH;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  CE_HIGH;

  return data;
}

byte read_flash_byte_LEAP(unsigned long address) // CE HIGH, OE LOW, FL_CE LOW
{
  PORTL = (address >> 17) & 0x7F;
  PORTK = (address >> 9) & 0xFF;
  PORTF = (address >> 1) & 0xFF;
  if (address & 0x1) // BYTE = A0
    BYTE_HIGH;
  else
    BYTE_LOW;
  CE_HIGH;
  OE_LOW;
  FL_CE_LOW;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  unsigned char data = PINC;
  FL_CE_HIGH;
  OE_HIGH;

  return data;
}

//******************************************
// WRITE FUNCTION
//******************************************

void write_flash_byte_LEAP(unsigned long address, unsigned char data)
{
  PORTL = (address >> 17) & 0x7F;
  PORTK = (address >> 9) & 0xFF;
  PORTF = (address >> 1) & 0xFF;
  if (address & 0x1) // BYTE = A0
    BYTE_HIGH;
  else
    BYTE_LOW;
  OE_HIGH;
  FL_CE_LOW;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  PORTC = data;
  WE_LOW;
  WE_HIGH;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  FL_CE_HIGH;
  OE_LOW;
}

//******************************************
// DATA INTEGRITY FUNCTIONS
//******************************************

void checkStart_LEAP()
{
  delay(500);
  OE_LOW;
  CE_LOW;
  BYTE_HIGH;
  tempword = read_rom_word_LEAP(0x0);
  if ((LEAP[0] == ((tempword >> 0x8) & 0xFF)) && (LEAP[1] == (tempword & 0xFF))) {
    tempword = read_rom_word_LEAP(0x1);
    findTable_LEAP(0x4, 0xA2);
  }
  else {
    delay(500);
    tempword = read_rom_word_LEAP(0xA2);
    if ((LEAP[0] == ((tempword >> 0x8) & 0xFF)) && (LEAP[1] == (tempword & 0xFF))) {
      tempword = read_rom_word_LEAP(0xA3);
      findTable_LEAP(0xA4, 0x146);
    }
  }
  CE_HIGH;
  OE_HIGH;
}

void findTable_LEAP(unsigned long startAddr, unsigned long endAddr)
{
  delay(500);
  CE_LOW;
  for (unsigned long addr = startAddr; addr < endAddr; addr +=2) {
    tempword = read_rom_word_LEAP(addr);
    if ((TBL[0] == ((tempword >> 0x8) & 0xFF))&&(TBL[1] == (tempword & 0xFF))) {
      tempword = read_rom_word_LEAP(addr + 1);
      if ((TBL[2] == ((tempword >> 0x8) & 0xFF))&&(TBL[3] == (tempword & 0xFF))) {
        readTable_LEAP(startAddr, endAddr);
        break;
      }
    }
  }
  CE_HIGH;
}

void readTable_LEAP(unsigned long startAddr, unsigned long endAddr)
{
  delay(500);
  CE_LOW;
  for (unsigned long addr = startAddr; addr < endAddr; addr++) {
    tempword = read_rom_word_LEAP(addr);
    if ((TXT[0] == ((tempword >> 0x8) & 0xFF))&&(TXT[1] == (tempword & 0xFF))) {
      tempword = read_rom_word_LEAP(addr + 1);
      if ((TXT[2] == ((tempword >> 0x8) & 0xFF))&&(TXT[3] == (tempword & 0xFF))) { // Text Block Marker Found
        ptrword = read_rom_word_LEAP(addr + 2);
        sentenceAddr = (((ptrword >> 8) & 0xFF) | ((ptrword & 0xFF) << 8)); // Swap Byte Order
      }
    }
    else if ((VER[0] == ((tempword >> 0x8) & 0xFF))&&(VER[1] == (tempword & 0xFF))) {
      tempword = read_rom_word_LEAP(addr + 1);
      if ((VER[2] == ((tempword >> 0x8) & 0xFF))&&(VER[3] == (tempword & 0xFF))) { // Version Marker Found
        ptrword = read_rom_word_LEAP(addr + 2);
        versionAddr = (((ptrword >> 8) & 0xFF) | ((ptrword & 0xFF) << 8)); // Swap Byte Order
      }
    }
    else if ((TTL[0] == ((tempword >> 0x8) & 0xFF))&&(TTL[1] == (tempword & 0xFF))) {
      tempword = read_rom_word_LEAP(addr + 1);      
      if ((TTL[2] == ((tempword >> 0x8) & 0xFF))&&(TTL[3] == (tempword & 0xFF))) { // Title Marker Found
        ptrword = read_rom_word_LEAP(addr + 2);
        titleAddr = (((ptrword >> 8) & 0xFF) | ((ptrword & 0xFF) << 8)); // Swap Byte Order
      }
    }
    else if ((END[0] == ((tempword >> 0x8) & 0xFF))&&(END[1] == (tempword & 0xFF))) {
      tempword = read_rom_word_LEAP(addr + 1);      
      if ((END[2] == ((tempword >> 0x8) & 0xFF))&&(END[3] == (tempword & 0xFF))) { // END OF TABLE
        break;
      }
    }
  }
  CE_HIGH;
//  print_Msg(F("Text Addr: "));
//  println_Msg(sentenceAddr, HEX);
//  print_Msg(F("Version Addr: "));
//  println_Msg(versionAddr, HEX);
//  print_Msg(F("Title Addr: "));
//  println_Msg(titleAddr, HEX);
//  display_Update();

  delay(500);
  CE_LOW;
  for (int x = 0; x < 10; x++) {
    tempword = read_rom_word_LEAP((versionAddr / 2) + x);
    ROMVersion[x * 2] = (tempword >> 0x8) & 0xFF;
    ROMVersion[(x * 2) + 1] = tempword & 0xFF;
  }

  delay(500);
  CE_LOW;
  for (int x = 0; x < 25; x++) {
    tempword = read_rom_word_LEAP((titleAddr / 2) + x);
    ROMTitle[x * 2] = (tempword >> 0x8) & 0xFF;
    ROMTitle[(x * 2) + 1] = tempword & 0xFF;
  }
}

//******************************************
// READ ROM
//******************************************

void readROM_LEAP()
{
  createFolderAndOpenFile("LEAP", "ROM", romName, "bin");

  for (unsigned long address = 0; address < 0x200000; address += 256) { // 4MB
    for (unsigned int x = 0; x < 256; x++) {
      tempword = read_rom_word_LEAP(address + x);
      sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF;
      sdBuffer[(x * 2) + 1] = tempword & 0xFF;
    }
    myFile.write(sdBuffer, 512);
  }
  if (leapsize > 0) {
    for (unsigned long address = 0x200000; address < 0x400000; address += 256) { // +4MB = 8MB
      for (unsigned int x = 0; x < 256; x++) {
        tempword = read_rom_word_LEAP(address + x);
        sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF;
        sdBuffer[(x * 2) + 1] = tempword & 0xFF;
      }
      myFile.write(sdBuffer, 512);
    }
    if (leapsize > 1) {
      for (unsigned long address = 0x400000; address < 0x800000; address += 256) { // +8MB = 16MB
        for (unsigned int x = 0; x < 256; x++) {
          tempword = read_rom_word_LEAP(address + x);
          sdBuffer[x * 2] = (tempword >> 0x8) & 0xFF;
          sdBuffer[(x * 2) + 1] = tempword & 0xFF;
        }
        myFile.write(sdBuffer, 512);
      }
    }
  }
  myFile.close();

  printCRC(fileName, NULL, 0);

  println_Msg(FS(FSTRING_EMPTY));
  print_STR(press_button_STR, 1);
  display_Update();
  wait();
}

//******************************************
// ROM SIZE
//******************************************

#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
void printRomSize_LEAP(int index)
{
    display_Clear();
    print_Msg(FS(FSTRING_ROM_SIZE));
    println_Msg(LEAPSTER[index]);
}
#endif

void setROMSize_LEAP()
{
  byte newleapsize;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
  display_Clear();
  if (leaplo == leaphi)
    newleapsize = leaplo;
  else {
    newleapsize = navigateMenu(leaplo, leaphi, &printRomSize_LEAP);
    
    display.setCursor(0, 56);  // Display selection at bottom
  }
  print_Msg(FS(FSTRING_ROM_SIZE));
  print_Msg(LEAPSTER[newleapsize]);
  println_Msg(F("MB"));
  display_Update();
  delay(1000);
#else
  if (leaplo == leaphi)
    newleapsize = leaplo;
  else {
setrom:
    String sizeROM;
    for (int i = 0; i < (leaphi - leaplo + 1); i++) {
      Serial.print(F("Select ROM Size:  "));
      Serial.print(i);
      Serial.print(F(" = "));
      Serial.print(LEAPSTER[i + leaplo]);
      Serial.println(F("MB"));
    }
    Serial.print(F("Enter ROM Size: "));
    while (Serial.available() == 0) {}
    sizeROM = Serial.readStringUntil('\n');
    Serial.println(sizeROM);
    newleapsize = sizeROM.toInt() + leaplo;
    if (newleapsize > leaphi) {
      Serial.println(F("SIZE NOT SUPPORTED"));
      Serial.println(FS(FSTRING_EMPTY));
      goto setrom;
    }
  }
  Serial.print(F("ROM Size = "));
  Serial.print(LEAPSTER[newleapsize]);
  Serial.println(F("MB"));
#endif
  EEPROM_writeAnything(8, newleapsize);
  leapsize = newleapsize;
}

void checkStatus_LEAP()
{
  EEPROM_readAnything(8, leapsize);
  if (leapsize > leaphi) {
    leapsize = 1; // default 8M
    EEPROM_writeAnything(8, leapsize);
  }

#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
  display_Clear();
  println_Msg(F("LEAPSTER READER"));
  println_Msg(FS(FSTRING_CURRENT_SETTINGS));
  println_Msg(FS(FSTRING_EMPTY));
  print_Msg(F("TITLE: "));
  println_Msg(ROMTitle);
  print_Msg(F("VER: "));
  println_Msg(ROMVersion);
  print_Msg(FS(FSTRING_ROM_SIZE));
  print_Msg(LEAPSTER[leapsize]);
  println_Msg(F("MB"));
  display_Update();
  wait();
#else
  Serial.print(F("TITLE: "));
  Serial.println(ROMTitle);
  Serial.print(F("VER: "));
  Serial.println(ROMVersion);
  Serial.print(FS(FSTRING_ROM_SIZE));
  Serial.print(LEAPSTER[leapsize]);
  Serial.println(F("MB"));
  Serial.println(FS(FSTRING_EMPTY));
#endif
}

//******************************************
// FLASH SAVES
//******************************************
// FLASH 39VF040
// MR. PENCIL'S LEARN TO DRAW & WRITE
// TOP SECRET - PERSONAL BEESWAX
// FLASH IS BYTE MODE 
// BYTE PIN IS A0 - SHIFT ADDRESS >> 1

void dataOut_LEAP()
{
  DDRC = 0xFF;
  DDRA = 0xFF;
}

void dataIn_LEAP()
{
  DDRC = 0x00;
  DDRA = 0x00;
}

void idFLASH_LEAP() // "BFD7" = 39VF040
{
  int ID1 = 0;
  int ID2 = 0;
  dataOut_LEAP();
  resetFLASH_LEAP();
  write_flash_byte_LEAP(0x5555, 0xAA);
  write_flash_byte_LEAP(0x2AAA, 0x55);
  write_flash_byte_LEAP(0x5555, 0x90);
  dataIn_LEAP();
  ID1 = read_flash_byte_LEAP(0x0000);
  ID2 = read_flash_byte_LEAP(0x0001);
  dataOut_LEAP();
  resetFLASH_LEAP();
  dataIn_LEAP();
  sprintf(flashid_str, "%02X%02X", ID1, ID2);
  display_Clear();
  print_Msg(F("Flash ID: "));
  println_Msg(flashid_str);
  display_Update();
}

void resetFLASH_LEAP()
{
  write_flash_byte_LEAP(0x5555, 0xF0);
}

void eraseFLASH_LEAP()
{
  write_flash_byte_LEAP(0x5555, 0xAA);
  write_flash_byte_LEAP(0x2AAA, 0x55);
  write_flash_byte_LEAP(0x5555, 0x80);
  write_flash_byte_LEAP(0x5555, 0xAA);
  write_flash_byte_LEAP(0x2AAA, 0x55);
  write_flash_byte_LEAP(0x5555, 0x10);
}

void programFLASH_LEAP()
{
  write_flash_byte_LEAP(0x5555, 0xAA);
  write_flash_byte_LEAP(0x2AAA, 0x55);
  write_flash_byte_LEAP(0x5555, 0xA0);
}

void statusFLASH_LEAP()
{
  byte flashStatus = 1;
  do {
    flashStatus = ((PORTA & 0x80) >> 7); // D7 = PORTA7
    _delay_us(4);
  }
  while (flashStatus == 1);
}

void readFLASH_LEAP()
{
  createFolderAndOpenFile("LEAP", "SAVE", romName, "fla");

  if(myFile) {
    CE_HIGH;
    OE_LOW;
    FL_CE_LOW;
    for (unsigned long address = 0x0; address < 0x80000; address += 512) { // 512K
      if ((address % 0x8000) == 0) {
        print_Msg(F("*"));
        display_Update();
      }
      for (unsigned int x = 0; x < 512; x++) {
        // CONSOLE READS ADDRESS 4X
        do {
          tempbyte = read_flash_byte_LEAP(address + x);
          tempcheck = read_flash_byte_LEAP(address + x);
        }
        while (tempbyte != tempcheck);
        sdBuffer[x] = tempbyte;
      }
      myFile.write(sdBuffer, 512);
    }
    myFile.flush();
    myFile.close();
    FL_CE_HIGH;
    OE_HIGH;
    println_Msg(FS(FSTRING_EMPTY));
    printCRC(fileName, NULL, 0); // 512K
  }
  println_Msg(FS(FSTRING_EMPTY));
  print_STR(press_button_STR, 1);
  display_Update();
  wait();
}

void writeFLASH_LEAP()
{
  fileBrowser(F("Select FLASH 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)) {
    CE_HIGH;
    dataOut_LEAP();
    eraseFLASH_LEAP();
    statusFLASH_LEAP();
    delay(100); // WORKS ~75 OK
    for (unsigned long address = 0x0; address < 0x80000; address += 512) { // 512K
      myFile.read(sdBuffer, 512);
      if ((address % 0x10000) == 0) {
        print_Msg(F("*"));
        display_Update();
      }
      for (unsigned int x = 0; x < 512; x++) {
        programFLASH_LEAP();
        write_flash_byte_LEAP(address + x, sdBuffer[x]);
      }
    }
    dataIn_LEAP();
    myFile.close();
    println_Msg(FS(FSTRING_EMPTY));
    println_Msg(F("FLASH FILE WRITTEN!"));
    display_Update();
  }
  else {
    println_Msg(F("SD ERROR"));
    display_Update();
  }
  sd.chdir(); // root
  filePath[0] = '\0'; // Reset filePath
}

//******************************************
// EEPROM SAVES
//******************************************
// EEPROM 24LC02/24LC16
// 24LC02B = 256 BYTES
// 24LC16B = 2048 BYTES
// DORA THE EXPLORER - WILDLIFE RESCUE 24LC16B
// SCHOLASTIC MATH MISSIONS 24LC16B

// MODIFIED FOR COMPATIBILITY WITH ISSI EEPROM
// PET PALS ISSI 416A-2GLI = 24C16A

// START - SDA HIGH TO LOW WHEN SCL HIGH
void eepromStart_LEAP()
{
  SCL_LOW;
  SDA_LOW;
  SCL_HIGH;
  _delay_us(2);
  SDA_HIGH;
  _delay_us(2);
  SDA_LOW;
  _delay_us(2);
  SCL_LOW;
  _delay_us(2);
}

void eepromSet0_LEAP()
{
  SCL_LOW;
  SDA_LOW;
  _delay_us(2);
  SCL_HIGH;
  _delay_us(5);
  SCL_LOW;
  _delay_us(2);
}

void eepromSet1_LEAP()
{
  SCL_LOW;
  SDA_LOW;
  _delay_us(2);
  SDA_HIGH;
  _delay_us(2);
  SCL_HIGH;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP; // ~500ns
  SCL_LOW;
  SDA_LOW;
  _delay_us(2);
}

void eepromDevice_LEAP()
{
  eepromSet1_LEAP();
  eepromSet0_LEAP();
  eepromSet1_LEAP();
  eepromSet0_LEAP();
}

void eepromSetDeviceAddress_LEAP(unsigned long addrhi)
{
  for (int i = 0; i < 3; i++) {
    if ((addrhi >> 2) & 0x1) // Bit is HIGH
      eepromSet1_LEAP();
    else // Bit is LOW
      eepromSet0_LEAP();
    addrhi <<= 1; // rotate to the next bit
  }
}

void eepromStatus_LEAP() // ACK
{
  byte eepStatus = 1;
  DDRH &= ~(1 << 4); // SDA to INPUT
  SCL_LOW;
  _delay_us(2);
  SCL_HIGH;
  _delay_us(5);
  SCL_LOW;
  do {
    eepStatus = ((PINH & 0x10) >> 4); // SDA = PORTH4
  }
  while (eepStatus == 1);
  DDRH |= (1 << 4); // SDA to OUTPUT
}

void eepromReadMode_LEAP()
{
  eepromSet1_LEAP(); // READ
  eepromStatus_LEAP(); // ACK
}

void eepromWriteMode_LEAP()
{
  eepromSet0_LEAP(); // WRITE
  eepromStatus_LEAP(); // ACK
}

void eepromReadData_LEAP()
{
  DDRH &= ~(1 << 4); // SDA to INPUT
  for (int i = 0; i < 8; i++) {
    SCL_LOW;
    SDA_LOW;
    _delay_us(2);
    SCL_HIGH;
    eepbit[i] = ((PINH & 0x10) >> 4); // SDA = PORTH4
    if (eepbit[i] == 1) {
      SCL_LOW;
      _delay_us(4);
    }
    else {
      _delay_us(4);
      SCL_LOW;
    }
    _delay_us(2);
  }
  DDRH |= (1 << 4); // SDA to OUTPUT
}

void eepromWriteData_LEAP(byte data)
{
  for (int i = 0; i < 8; i++) {
    if ((data >> 7) & 0x1) // Bit is HIGH
      eepromSet1_LEAP();
    else // Bit is LOW
      eepromSet0_LEAP();
    data <<= 1; // rotate to the next bit
  }
  eepromStatus_LEAP(); // ACK
}

// STOP - SDA LOW TO HIGH WHEN SCL HIGH
void eepromStop_LEAP()
{
  SCL_LOW;
  SDA_LOW;
  _delay_us(1);
  SCL_HIGH;
  _delay_us(2);
  SDA_HIGH;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP;
  NOP; // ~500nS
  SCL_LOW;
  SDA_LOW;
}

void eepromSetAddress_LEAP(word address)
{
  for (int i = 0; i < 8; i++) {
    if ((address >> 7) & 0x1) // Bit is HIGH
      eepromSet1_LEAP();
    else // Bit is LOW
      eepromSet0_LEAP();
    address <<= 1; // rotate to the next bit
  }
  eepromStatus_LEAP(); // ACK
}

void readEepromByte_LEAP(word address)
{
  word addrhi = address >> 8;
  word addrlo = address & 0xFF;
  eepromStart_LEAP(); // START
  eepromDevice_LEAP(); // DEVICE [1010]
  eepromSetDeviceAddress_LEAP(addrhi); // ADDR [A10..A8]
  eepromWriteMode_LEAP();
  eepromSetAddress_LEAP(addrlo);
  eepromStart_LEAP(); // START
  eepromDevice_LEAP(); // DEVICE [1010]
  eepromSetDeviceAddress_LEAP(addrhi); // ADDR [A10..A8]
  eepromReadMode_LEAP();
  eepromReadData_LEAP();
  eepromStop_LEAP(); // STOP
  // OR 8 bits into byte
  eeptemp = eepbit[0] << 7 | eepbit[1] << 6 | eepbit[2] << 5 | eepbit[3] << 4 | eepbit[4] << 3 | eepbit[5] << 2 | eepbit[6] << 1 | eepbit[7];
  sdBuffer[addrlo] = eeptemp;
}

void writeEepromByte_LEAP(word address)
{
  word addrhi = address >> 8;
  word addrlo = address & 0xFF;
  eeptemp = sdBuffer[addrlo];
  eepromStart_LEAP(); // START
  eepromDevice_LEAP(); // DEVICE [1010]
  eepromSetDeviceAddress_LEAP(addrhi); // ADDR [A10-A8]
  eepromWriteMode_LEAP(); // WRITE
  eepromSetAddress_LEAP(addrlo);
  eepromWriteData_LEAP(eeptemp);
  eepromStop_LEAP(); // STOP
}

// Read EEPROM and save to the SD card
void readEEP_LEAP()
{
  createFolderAndOpenFile("LEAP", "SAVE", romName, "eep");

  if (myFile) {
    for (word currByte = 0; currByte < 2048; currByte += 256) {
      for (int i = 0; i < 256; i++) {
        readEepromByte_LEAP(currByte + i);
      }
      myFile.write(sdBuffer, 256);
    }
// 24LC02
//    for (word currByte = 0; currByte < 256; currByte++) {
//      readEepromByte_LEAP(currByte);
//    }
//    myFile.write(sdBuffer, 256);
    myFile.close();

    printCRC(fileName, NULL, 0); // 2048
  }
  println_Msg(FS(FSTRING_EMPTY));
  print_STR(press_button_STR, 1);
  display_Update();
  wait();
}

void writeEEP_LEAP()
{
  fileBrowser(F("Select EEPROM 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)) {
    WP_LOW;
    for (word currByte = 0; currByte < 2048; currByte += 256) {
      myFile.read(sdBuffer, 256);
      for (int i = 0; i < 256; i++) {
        writeEepromByte_LEAP(currByte + i);
        delay(50);
      }
      print_Msg(F("*"));
      display_Update();
    }
    WP_HIGH;
    myFile.close();
    println_Msg(FS(FSTRING_EMPTY));
    println_Msg(F("EEPROM FILE WRITTEN!"));
    display_Update();
  }
  else {
    println_Msg(F("SD ERROR"));
    display_Update();
  }
  sd.chdir(); // root
  filePath[0] = '\0'; // Reset filePath
}

//******************************************
// CART SELECT CODE
//******************************************

void setCart_LEAP()
{
  //go to root
  sd.chdir();

  byte gameSize;

  // Select starting letter
  //byte myLetter = starting_letter();

  // Open database
  if (myFile.open("leapster.txt", O_READ)) {
    // seek_first_letter_in_database(myFile, myLetter);

    if(checkCartSelection(myFile, &readDataLineSingleDigit, &gameSize)) {
      EEPROM_writeAnything(8, gameSize);
    }
  } else {
    print_FatalError(FS(FSTRING_DATABASE_FILE_NOT_FOUND));
  }
}
#endif
