2018-10-05 18:33:09 +02:00
/**********************************************************************************
Cartridge Reader for Arduino Mega2560
2021-08-03 13:04:40 +02:00
This project represents a community - driven effort to provide
an easy to build and easy to modify cartridge dumper .
2024-03-02 17:26:35 +01:00
Date : 2024 - 03 - 02
Version : 13.2
2018-10-05 18:33:09 +02:00
2020-10-28 18:44:13 +01:00
SD lib : https : //github.com/greiman/SdFat
2021-10-24 00:41:18 +02:00
LCD lib : https : //github.com/olikraus/u8g2
Neopixel lib : https : //github.com/adafruit/Adafruit_NeoPixel
Rotary Enc lib : https : //github.com/mathertel/RotaryEncoder
2020-10-28 18:44:13 +01:00
SI5351 lib : https : //github.com/etherkit/Si5351Arduino
2021-10-24 00:41:18 +02:00
RTC lib : https : //github.com/adafruit/RTClib
2021-10-28 23:26:10 +02:00
Frequency lib : https : //github.com/PaulStoffregen/FreqCount
2018-10-05 18:33:09 +02:00
2023-09-23 00:11:08 +02:00
Compiled with Arduino IDE 2.2 .1
2018-10-05 18:33:09 +02:00
2021-08-03 17:29:39 +02:00
Thanks to :
2021-08-03 13:04:40 +02:00
MichlK - ROM Reader for Super Nintendo
2018-10-05 18:33:09 +02:00
Jeff Saltzman - 4 - Way Button
2021-08-03 13:04:40 +02:00
Wayne and Layne - Video Game Shield menu
2023-09-23 00:11:08 +02:00
skaman - Cart ROM READER SNES ENHANCED , Famicom Cart Dumper , Coleco - , Intellivision , Virtual Boy , WSV , PCW , ARC , Atari 2600 / 5200 / 7800 , ODY2 , Fairchild , MSX , Pokemon Mini , C64 , Vectrex modules
2021-08-03 17:27:35 +02:00
Tamanegi_taro - PCE and Satellaview modules
2023-02-23 12:04:33 +01:00
splash5 - GBSmart , Wonderswan , NGP and Super A ' can modules
2023-07-13 10:31:15 +02:00
partlyhuman - Casio Loopy module
2021-08-03 13:04:40 +02:00
hkz & themanbehindthecurtain - N64 flashram commands
Andrew Brown & Peter Den Hartog - N64 controller protocol
2022-08-19 11:48:43 +02:00
libdragon - N64 controller checksum functions
2018-10-05 18:33:09 +02:00
Angus Gratton - CRC32
Snes9x - SuperFX sram fix
insidegadgets - GBCartRead
RobinTheHood - GameboyAdvanceRomDumper
2019-09-01 14:36:53 +02:00
Gens - gs - Megadrive checksum
2022-07-17 14:50:59 +02:00
fceux - iNes header
2021-08-03 13:04:40 +02:00
2021-09-03 10:21:02 +02:00
And a special Thank You to all coders and contributors on Github and the Arduino forum :
2021-10-24 00:41:18 +02:00
jiyunomegami , splash5 , Kreeblah , ramapcsx2 , PsyK0p4T , Dakkaron , majorpbx , Pickle , sdhizumi ,
2022-08-28 08:59:00 +02:00
Uzlopak , sakman55 , Tombo89 , scrap - a , borti4938 , vogelfreiheit , CaitSith2 , Modman ,
2022-10-30 11:46:55 +01:00
philenotfound , karimhadjsalem , nsx0r , ducky92 , niklasweber , Lesserkuma , BacteriaMage ,
2023-10-17 18:41:14 +02:00
vpelletier , Ancyker , mattiacci , RWeick , joshman196 , partlyhuman , ButThouMust , hxlnt ,
2023-11-20 12:19:32 +01:00
breyell , qufb
2021-10-14 09:53:07 +02:00
2021-09-03 10:21:02 +02:00
And to nocash for figuring out the secrets of the SFC Nintendo Power cartridge .
2018-10-05 18:33:09 +02:00
2022-06-08 22:34:28 +02:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < https : //www.gnu.org/licenses/>.
2018-10-05 18:33:09 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-09-05 15:43:27 +02:00
2023-03-30 03:05:01 +02:00
# include "OSCR.h"
2022-07-07 00:15:13 +02:00
2018-10-05 18:33:09 +02:00
/******************************************
Libraries
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-10-24 00:41:18 +02:00
// SD Card
SdFs sd ;
FsFile myFile ;
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2022-06-12 12:30:52 +02:00
FsFile myLog ;
2024-03-02 17:26:35 +01:00
bool dont_log = false ;
2022-06-12 12:30:52 +02:00
# endif
2021-10-24 00:41:18 +02:00
2018-10-05 18:33:09 +02:00
// AVR Eeprom
# include <EEPROM.h>
2019-09-02 20:11:08 +02:00
// forward declarations for "T" (for non Arduino IDE)
2022-10-13 09:49:03 +02:00
template < class T > int EEPROM_writeAnything ( int ee , const T & value ) ;
template < class T > int EEPROM_readAnything ( int ee , T & value ) ;
2019-09-02 20:11:08 +02:00
2021-10-24 00:41:18 +02:00
// Graphic SPI LCD
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_LCD
2021-10-24 00:41:18 +02:00
# include <U8g2lib.h>
2022-10-13 09:49:03 +02:00
U8G2_ST7567_OS12864_F_4W_HW_SPI display ( U8G2_R2 , /* cs=*/ 12 , /* dc=*/ 11 , /* reset=*/ 10 ) ;
2021-10-24 00:41:18 +02:00
# endif
2018-10-05 18:33:09 +02:00
2021-10-24 00:41:18 +02:00
// Rotary Encoder
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ROTARY
2021-10-24 00:41:18 +02:00
# include <RotaryEncoder.h>
# define PIN_IN1 18
# define PIN_IN2 19
2022-06-09 00:35:11 +02:00
# ifdef rotate_counter_clockwise
RotaryEncoder encoder ( PIN_IN2 , PIN_IN1 , RotaryEncoder : : LatchMode : : FOUR3 ) ;
# else
2021-10-24 00:41:18 +02:00
RotaryEncoder encoder ( PIN_IN1 , PIN_IN2 , RotaryEncoder : : LatchMode : : FOUR3 ) ;
2022-06-09 00:35:11 +02:00
# endif
2021-10-24 00:41:18 +02:00
int rotaryPos = 0 ;
# endif
2018-10-05 18:33:09 +02:00
2021-10-24 00:41:18 +02:00
// Choose RGB LED type
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NEOPIXEL
2021-10-24 00:41:18 +02:00
// Neopixel
# include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel pixels ( 3 , 13 , NEO_GRB + NEO_KHZ800 ) ;
# endif
2018-10-05 18:33:09 +02:00
typedef enum COLOR_T {
blue_color ,
red_color ,
purple_color ,
green_color ,
turquoise_color ,
yellow_color ,
white_color ,
} color_t ;
2021-10-24 00:41:18 +02:00
// Graphic I2C OLED
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_OLED
2022-10-08 16:23:33 +02:00
# include <U8g2lib.h>
2022-10-13 09:49:03 +02:00
U8G2_SSD1306_128X64_NONAME_F_HW_I2C display ( U8G2_R0 , /* reset=*/ U8X8_PIN_NONE ) ;
2021-10-24 00:41:18 +02:00
# endif
// Adafruit Clock Generator
# include <si5351.h>
Si5351 clockgen ;
2021-11-18 14:55:50 +01:00
bool i2c_found ;
2021-10-24 00:41:18 +02:00
2021-10-02 07:29:37 +02:00
// RTC Library
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_RTC
2022-07-08 11:36:03 +02:00
# define _RTC_H
# include "RTClib.h"
# endif
// Clockgen Calibration
2024-03-02 17:26:35 +01:00
# ifdef OPTION_CLOCKGEN_CALIBRATION
2022-07-08 11:36:03 +02:00
# include "FreqCount.h"
2021-10-02 07:29:37 +02:00
# endif
2022-10-31 15:41:29 +01:00
void _print_FatalError ( void ) __attribute__ ( ( noreturn ) ) ;
void print_FatalError ( const __FlashStringHelper * errorMessage ) __attribute__ ( ( noreturn ) ) ;
void print_FatalError ( byte errorMessage ) __attribute__ ( ( noreturn ) ) ;
2022-10-30 02:33:47 +02:00
2022-10-31 09:03:31 +01:00
/******************************************
2023-10-17 18:41:14 +02:00
End of inclusions and forward declarations
2022-10-31 09:03:31 +01:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
template < class T > int EEPROM_writeAnything ( int ee , const T & value ) {
const byte * p = ( const byte * ) ( const void * ) & value ;
unsigned int i ;
for ( i = 0 ; i < sizeof ( value ) ; i + + )
EEPROM . write ( ee + + , * p + + ) ;
return i ;
}
template < class T > int EEPROM_readAnything ( int ee , T & value ) {
byte * p = ( byte * ) ( void * ) & value ;
unsigned int i ;
for ( i = 0 ; i < sizeof ( value ) ; i + + )
* p + + = EEPROM . read ( ee + + ) ;
return i ;
}
2022-10-21 15:59:06 +02:00
/******************************************
Common Strings
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define press_button_STR 0
# define sd_error_STR 1
# define reset_STR 2
# define did_not_verify_STR 3
# define _bytes_STR 4
# define error_STR 5
# define create_file_STR 6
# define open_file_STR 7
# define file_too_big_STR 8
# define done_STR 9
# define saving_to_STR 10
# define verifying_STR 11
# define flashing_file_STR 12
# define press_to_change_STR 13
# define right_to_select_STR 14
# define rotate_to_change_STR 15
# define press_to_select_STR 16
// This arrays holds the most often uses strings
2024-03-02 17:26:35 +01:00
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 " ;
constexpr char string_bytes4 [ ] PROGMEM = " bytes " ;
constexpr char string_error5 [ ] PROGMEM = " Error: " ;
constexpr char string_create_file6 [ ] PROGMEM = " Can't create file " ;
constexpr char string_open_file7 [ ] PROGMEM = " Can't open file " ;
constexpr char string_file_too_big8 [ ] PROGMEM = " File too big " ;
constexpr char string_done9 [ ] PROGMEM = " Done " ;
constexpr char string_saving_to10 [ ] PROGMEM = " Saving to " ;
constexpr char string_verifying11 [ ] PROGMEM = " Verifying... " ;
constexpr char string_flashing_file12 [ ] PROGMEM = " Flashing file " ;
constexpr char string_press_to_change13 [ ] PROGMEM = " Press left to Change " ;
constexpr char string_right_to_select14 [ ] PROGMEM = " and right to Select " ;
constexpr char string_rotate_to_change15 [ ] PROGMEM = " Rotate to Change " ;
constexpr char string_press_to_select16 [ ] PROGMEM = " Press to Select " ;
static const char * const string_table [ ] PROGMEM = { string_press_button0 , string_sd_error1 , FSTRING_RESET , string_did_not_verify3 , string_bytes4 , string_error5 , string_create_file6 , string_open_file7 , string_file_too_big8 , string_done9 , string_saving_to10 , string_verifying11 , string_flashing_file12 , string_press_to_change13 , string_right_to_select14 , string_rotate_to_change15 , string_press_to_select16 } ;
2022-10-21 15:59:06 +02:00
void print_STR ( byte string_number , boolean newline ) {
char string_buffer [ 22 ] ;
strcpy_P ( string_buffer , ( char * ) pgm_read_word ( & ( string_table [ string_number ] ) ) ) ;
if ( newline )
println_Msg ( string_buffer ) ;
else
print_Msg ( string_buffer ) ;
}
2018-10-05 18:33:09 +02:00
/******************************************
Defines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-12-10 05:29:46 +01:00
2019-09-05 00:48:39 +02:00
// optimization-safe nop delay
2022-10-13 09:49:03 +02:00
# define NOP __asm__ __volatile__("nop\n\t")
2019-08-28 23:48:44 +02:00
2019-09-05 00:48:39 +02:00
// Button timing
2022-10-13 09:49:03 +02:00
# define debounce 20 // ms debounce period to prevent flickering when pressing or releasing the button
# define DCgap 250 // max ms between clicks for a double click event
# define holdTime 2000 // ms hold period: how long to wait for press+hold event
# define longHoldTime 5000 // ms long hold period: how long to wait for press+hold event
2019-09-05 00:48:39 +02:00
2018-10-05 18:33:09 +02:00
/******************************************
Variables
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ROTARY
2021-10-24 00:41:18 +02:00
// Button debounce
2022-10-13 09:49:03 +02:00
boolean buttonState = HIGH ; // the current reading from the input pin
boolean lastButtonState = HIGH ; // the previous reading from the input pin
2021-10-24 00:41:18 +02:00
unsigned long lastDebounceTime = 0 ; // the last time the output pin was toggled
unsigned long debounceDelay = 50 ; // the debounce time; increase if the output flickers
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_OLED
2019-09-05 00:48:39 +02:00
// Button 1
2022-10-13 09:49:03 +02:00
boolean buttonVal1 = HIGH ; // value read from button
boolean buttonLast1 = HIGH ; // buffered value of the button's previous state
boolean DCwaiting1 = false ; // whether we're waiting for a double click (down)
boolean DConUp1 = false ; // whether to register a double click on next release, or whether to wait and click
boolean singleOK1 = true ; // whether it's OK to do a single click
long downTime1 = - 1 ; // time the button was pressed down
long upTime1 = - 1 ; // time the button was released
boolean ignoreUp1 = false ; // whether to ignore the button release because the click+hold was triggered
boolean waitForUp1 = false ; // when held, whether to wait for the up event
boolean holdEventPast1 = false ; // whether or not the hold event happened already
boolean longholdEventPast1 = false ; // whether or not the long hold event happened already
2019-09-05 00:48:39 +02:00
// Button 2
2022-10-13 09:49:03 +02:00
boolean buttonVal2 = HIGH ; // value read from button
boolean buttonLast2 = HIGH ; // buffered value of the button's previous state
boolean DCwaiting2 = false ; // whether we're waiting for a double click (down)
boolean DConUp2 = false ; // whether to register a double click on next release, or whether to wait and click
boolean singleOK2 = true ; // whether it's OK to do a single click
long downTime2 = - 1 ; // time the button was pressed down
long upTime2 = - 1 ; // time the button was released
boolean ignoreUp2 = false ; // whether to ignore the button release because the click+hold was triggered
boolean waitForUp2 = false ; // when held, whether to wait for the up event
boolean holdEventPast2 = false ; // whether or not the hold event happened already
boolean longholdEventPast2 = false ; // whether or not the long hold event happened already
2021-10-24 00:41:18 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2018-10-05 18:33:09 +02:00
// For incoming serial data
int incomingByte ;
2019-09-05 00:48:39 +02:00
# endif
2018-10-05 18:33:09 +02:00
// Variables for the menu
int choice = 0 ;
// Temporary array that holds the menu option read out of progmem
char menuOptions [ 7 ] [ 20 ] ;
boolean ignoreError = 0 ;
// File browser
2022-08-19 19:56:35 +02:00
# define FILENAME_LENGTH 100
# define FILEPATH_LENGTH 132
2020-11-03 11:19:16 +01:00
# define FILEOPTS_LENGTH 20
2019-10-07 05:34:22 +02:00
char fileName [ FILENAME_LENGTH ] ;
char filePath [ FILEPATH_LENGTH ] ;
2018-10-05 18:33:09 +02:00
byte currPage ;
byte lastPage ;
byte numPages ;
boolean root = 0 ;
boolean filebrowse = 0 ;
// Common
2022-10-06 10:20:02 +02:00
// 21 chars for SNES ROM name, one char for termination
char romName [ 22 ] ;
2018-10-05 18:33:09 +02:00
unsigned long sramSize = 0 ;
int romType = 0 ;
byte saveType ;
word romSize = 0 ;
2021-06-02 12:30:59 +02:00
word numBanks = 128 ;
2022-07-03 01:03:39 +02:00
char checksumStr [ 9 ] ;
2018-10-05 18:33:09 +02:00
bool errorLvl = 0 ;
byte romVersion = 0 ;
char cartID [ 5 ] ;
unsigned long cartSize ;
2022-10-22 02:47:50 +02:00
unsigned int flashid ;
char flashid_str [ 5 ] ;
2018-10-05 18:33:09 +02:00
char vendorID [ 5 ] ;
unsigned long fileSize ;
unsigned long sramBase ;
2021-07-21 22:46:34 +02:00
unsigned long flashBanks ;
bool flashX16Mode ;
bool flashSwitchLastBits ;
2018-10-05 18:33:09 +02:00
// Variable to count errors
unsigned long writeErrors ;
// Operation mode
2024-03-02 17:26:35 +01:00
CORES mode = CORE_MAX ;
2018-10-05 18:33:09 +02:00
2022-10-06 10:20:02 +02:00
//remember folder number to create a new folder for every game
2018-10-05 18:33:09 +02:00
int foldern ;
2022-10-06 10:20:02 +02:00
// 4 chars for console type, 4 chars for SAVE/ROM, 21 chars for ROM name, 4 chars for folder number, 3 chars for slashes, one char for termination, one char savety
char folder [ 38 ] ;
2018-10-05 18:33:09 +02:00
// Array that holds the data
byte sdBuffer [ 512 ] ;
2019-08-29 19:13:50 +02:00
// soft reset Arduino: jumps to 0
// using the watchdog timer would be more elegant but some Mega2560 bootloaders are buggy with it
2022-10-31 15:41:29 +01:00
void ( * resetArduino ) ( void ) __attribute__ ( ( noreturn ) ) = 0 ;
2019-08-29 19:13:50 +02:00
2019-09-05 00:48:39 +02:00
// Progressbar
2019-08-29 19:13:50 +02:00
void draw_progressbar ( uint32_t processedsize , uint32_t totalsize ) ;
2022-06-16 17:17:16 +02:00
// used by MD and NES modules
byte eepbit [ 8 ] ;
byte eeptemp ;
2022-07-07 23:20:45 +02:00
// Array to hold iNES header
byte iNES_HEADER [ 16 ] ;
//ID 0-3
//ROM_size 4
//VROM_size 5
//ROM_type 6
//ROM_type2 7
//ROM_type3 8
//Upper_ROM_VROM_size 9
//RAM_size 10
//VRAM_size 11
//TV_system 12
//VS_hardware 13
//reserved 14, 15
2020-07-04 14:02:34 +02:00
//******************************************
2022-06-16 17:17:16 +02:00
// CRC32
2020-07-04 14:02:34 +02:00
//******************************************
// CRC32 lookup table // 256 entries
2024-03-02 17:26:35 +01:00
constexpr uint32_t crc_32_tab [ ] PROGMEM = { /* CRC polynomial 0xedb88320 */
2023-10-17 18:41:14 +02:00
0x00000000 , 0x77073096 , 0xee0e612c , 0x990951ba , 0x076dc419 , 0x706af48f ,
0xe963a535 , 0x9e6495a3 , 0x0edb8832 , 0x79dcb8a4 , 0xe0d5e91e , 0x97d2d988 ,
0x09b64c2b , 0x7eb17cbd , 0xe7b82d07 , 0x90bf1d91 , 0x1db71064 , 0x6ab020f2 ,
0xf3b97148 , 0x84be41de , 0x1adad47d , 0x6ddde4eb , 0xf4d4b551 , 0x83d385c7 ,
0x136c9856 , 0x646ba8c0 , 0xfd62f97a , 0x8a65c9ec , 0x14015c4f , 0x63066cd9 ,
0xfa0f3d63 , 0x8d080df5 , 0x3b6e20c8 , 0x4c69105e , 0xd56041e4 , 0xa2677172 ,
0x3c03e4d1 , 0x4b04d447 , 0xd20d85fd , 0xa50ab56b , 0x35b5a8fa , 0x42b2986c ,
0xdbbbc9d6 , 0xacbcf940 , 0x32d86ce3 , 0x45df5c75 , 0xdcd60dcf , 0xabd13d59 ,
0x26d930ac , 0x51de003a , 0xc8d75180 , 0xbfd06116 , 0x21b4f4b5 , 0x56b3c423 ,
0xcfba9599 , 0xb8bda50f , 0x2802b89e , 0x5f058808 , 0xc60cd9b2 , 0xb10be924 ,
0x2f6f7c87 , 0x58684c11 , 0xc1611dab , 0xb6662d3d , 0x76dc4190 , 0x01db7106 ,
0x98d220bc , 0xefd5102a , 0x71b18589 , 0x06b6b51f , 0x9fbfe4a5 , 0xe8b8d433 ,
0x7807c9a2 , 0x0f00f934 , 0x9609a88e , 0xe10e9818 , 0x7f6a0dbb , 0x086d3d2d ,
0x91646c97 , 0xe6635c01 , 0x6b6b51f4 , 0x1c6c6162 , 0x856530d8 , 0xf262004e ,
0x6c0695ed , 0x1b01a57b , 0x8208f4c1 , 0xf50fc457 , 0x65b0d9c6 , 0x12b7e950 ,
0x8bbeb8ea , 0xfcb9887c , 0x62dd1ddf , 0x15da2d49 , 0x8cd37cf3 , 0xfbd44c65 ,
0x4db26158 , 0x3ab551ce , 0xa3bc0074 , 0xd4bb30e2 , 0x4adfa541 , 0x3dd895d7 ,
0xa4d1c46d , 0xd3d6f4fb , 0x4369e96a , 0x346ed9fc , 0xad678846 , 0xda60b8d0 ,
0x44042d73 , 0x33031de5 , 0xaa0a4c5f , 0xdd0d7cc9 , 0x5005713c , 0x270241aa ,
0xbe0b1010 , 0xc90c2086 , 0x5768b525 , 0x206f85b3 , 0xb966d409 , 0xce61e49f ,
0x5edef90e , 0x29d9c998 , 0xb0d09822 , 0xc7d7a8b4 , 0x59b33d17 , 0x2eb40d81 ,
0xb7bd5c3b , 0xc0ba6cad , 0xedb88320 , 0x9abfb3b6 , 0x03b6e20c , 0x74b1d29a ,
0xead54739 , 0x9dd277af , 0x04db2615 , 0x73dc1683 , 0xe3630b12 , 0x94643b84 ,
0x0d6d6a3e , 0x7a6a5aa8 , 0xe40ecf0b , 0x9309ff9d , 0x0a00ae27 , 0x7d079eb1 ,
0xf00f9344 , 0x8708a3d2 , 0x1e01f268 , 0x6906c2fe , 0xf762575d , 0x806567cb ,
0x196c3671 , 0x6e6b06e7 , 0xfed41b76 , 0x89d32be0 , 0x10da7a5a , 0x67dd4acc ,
0xf9b9df6f , 0x8ebeeff9 , 0x17b7be43 , 0x60b08ed5 , 0xd6d6a3e8 , 0xa1d1937e ,
0x38d8c2c4 , 0x4fdff252 , 0xd1bb67f1 , 0xa6bc5767 , 0x3fb506dd , 0x48b2364b ,
0xd80d2bda , 0xaf0a1b4c , 0x36034af6 , 0x41047a60 , 0xdf60efc3 , 0xa867df55 ,
0x316e8eef , 0x4669be79 , 0xcb61b38c , 0xbc66831a , 0x256fd2a0 , 0x5268e236 ,
0xcc0c7795 , 0xbb0b4703 , 0x220216b9 , 0x5505262f , 0xc5ba3bbe , 0xb2bd0b28 ,
0x2bb45a92 , 0x5cb36a04 , 0xc2d7ffa7 , 0xb5d0cf31 , 0x2cd99e8b , 0x5bdeae1d ,
0x9b64c2b0 , 0xec63f226 , 0x756aa39c , 0x026d930a , 0x9c0906a9 , 0xeb0e363f ,
0x72076785 , 0x05005713 , 0x95bf4a82 , 0xe2b87a14 , 0x7bb12bae , 0x0cb61b38 ,
0x92d28e9b , 0xe5d5be0d , 0x7cdcefb7 , 0x0bdbdf21 , 0x86d3d2d4 , 0xf1d4e242 ,
0x68ddb3f8 , 0x1fda836e , 0x81be16cd , 0xf6b9265b , 0x6fb077e1 , 0x18b74777 ,
0x88085ae6 , 0xff0f6a70 , 0x66063bca , 0x11010b5c , 0x8f659eff , 0xf862ae69 ,
0x616bffd3 , 0x166ccf45 , 0xa00ae278 , 0xd70dd2ee , 0x4e048354 , 0x3903b3c2 ,
0xa7672661 , 0xd06016f7 , 0x4969474d , 0x3e6e77db , 0xaed16a4a , 0xd9d65adc ,
0x40df0b66 , 0x37d83bf0 , 0xa9bcae53 , 0xdebb9ec5 , 0x47b2cf7f , 0x30b5ffe9 ,
0xbdbdf21c , 0xcabac28a , 0x53b39330 , 0x24b4a3a6 , 0xbad03605 , 0xcdd70693 ,
0x54de5729 , 0x23d967bf , 0xb3667a2e , 0xc4614ab8 , 0x5d681b02 , 0x2a6f2b94 ,
0xb40bbe37 , 0xc30c8ea1 , 0x5a05df1b , 0x2d02ef8d
2020-07-04 14:02:34 +02:00
} ;
2022-10-23 14:26:40 +02:00
// Defined as a macros, as compiler disregards inlining requests and these are
// performance-critical functions.
2022-10-28 15:02:51 +02:00
# define UPDATE_CRC(crc, ch) \
do { \
uint8_t idx = ( ( crc ) ^ ( ch ) ) & 0xff ; \
uint32_t tab_value = pgm_read_dword ( crc_32_tab + idx ) ; \
( crc ) = tab_value ^ ( ( crc ) > > 8 ) ; \
} while ( 0 )
uint32_t updateCRC ( const byte * buffer , size_t length , uint32_t crc ) {
2022-10-23 14:26:40 +02:00
for ( size_t c = 0 ; c < length ; c + + ) {
UPDATE_CRC ( crc , buffer [ c ] ) ;
}
return crc ;
}
2022-10-28 15:02:51 +02:00
uint32_t calculateCRC ( const byte * buffer , size_t length ) {
2022-10-23 14:26:40 +02:00
uint32_t crc = 0xFFFFFFFF ;
2022-10-28 12:56:38 +02:00
crc = updateCRC ( buffer , length , crc ) ;
2022-10-23 14:26:40 +02:00
return ~ crc ;
}
2022-10-28 15:02:51 +02:00
uint32_t calculateCRC ( FsFile & infile ) {
2022-10-23 14:26:40 +02:00
uint32_t byte_count ;
uint32_t crc = 0xFFFFFFFF ;
2022-10-28 15:02:51 +02:00
while ( ( byte_count = infile . read ( sdBuffer , sizeof ( sdBuffer ) ) ) ! = 0 ) {
2022-10-28 12:56:38 +02:00
crc = updateCRC ( sdBuffer , byte_count , crc ) ;
2022-10-23 14:26:40 +02:00
}
return ~ crc ;
2022-06-16 17:17:16 +02:00
}
// Calculate rom's CRC32 from SD
2022-07-07 00:15:13 +02:00
uint32_t calculateCRC ( char * fileName , char * folder , int offset ) {
2022-10-23 14:26:40 +02:00
FsFile infile ;
uint32_t result ;
2022-06-21 13:29:19 +02:00
sd . chdir ( folder ) ;
2022-10-23 14:26:40 +02:00
if ( infile . open ( fileName , O_READ ) ) {
infile . seek ( offset ) ;
result = calculateCRC ( infile ) ;
infile . close ( ) ;
return result ;
2022-10-13 09:49:03 +02:00
} else {
2022-06-21 13:29:19 +02:00
display_Clear ( ) ;
print_Msg ( F ( " File " ) ) ;
//print_Msg(folder);
//print_Msg(F("/"));
//print_Msg(fileName);
2022-10-30 03:21:01 +01:00
print_FatalError ( F ( " not found " ) ) ;
2022-10-22 10:25:37 +02:00
return 0 ;
2022-06-16 17:17:16 +02:00
}
}
2023-01-03 19:33:34 +01:00
/******************************************
2023-09-23 00:11:08 +02:00
CRC Functions for Atari , Fairchild , Ody2 , Arc , etc . modules
2023-01-03 19:33:34 +01:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-03-02 17:26:35 +01:00
# 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))
2023-01-03 19:33:34 +01:00
inline uint32_t updateCRC ( uint8_t ch , uint32_t crc ) {
uint32_t idx = ( ( crc ) ^ ( ch ) ) & 0xff ;
uint32_t tab_value = pgm_read_dword ( crc_32_tab + idx ) ;
return tab_value ^ ( ( crc ) > > 8 ) ;
}
FsFile crcFile ;
char tempCRC [ 9 ] ;
uint32_t crc32 ( FsFile & file , uint32_t & charcnt ) {
uint32_t oldcrc32 = 0xFFFFFFFF ;
charcnt = 0 ;
while ( file . available ( ) ) {
crcFile . read ( sdBuffer , 512 ) ;
for ( int x = 0 ; x < 512 ; x + + ) {
uint8_t c = sdBuffer [ x ] ;
charcnt + + ;
oldcrc32 = updateCRC ( c , oldcrc32 ) ;
}
}
return ~ oldcrc32 ;
}
void calcCRC ( char * checkFile , unsigned long filesize , uint32_t * crcCopy , unsigned long offset ) {
uint32_t crc ;
crcFile = sd . open ( checkFile ) ;
crcFile . seek ( offset ) ;
crc = crc32 ( crcFile , filesize ) ;
crcFile . close ( ) ;
sprintf ( tempCRC , " %08lX " , crc ) ;
if ( crcCopy ! = NULL ) {
* crcCopy = crc ;
}
print_Msg ( F ( " CRC: " ) ) ;
println_Msg ( tempCRC ) ;
display_Update ( ) ;
}
# endif
2022-06-16 17:17:16 +02:00
//******************************************
2022-06-17 23:57:29 +02:00
// Functions for CRC32 database
2022-06-16 17:17:16 +02:00
//******************************************
2022-06-17 23:57:29 +02:00
//Skip line
2022-10-13 09:49:03 +02:00
void skip_line ( FsFile * readfile ) {
2022-06-17 23:57:29 +02:00
int i = 0 ;
char str_buf ;
2022-10-13 09:49:03 +02:00
while ( readfile - > available ( ) ) {
2022-06-17 23:57:29 +02:00
//Read 1 byte from file
str_buf = readfile - > read ( ) ;
//if end of file or newline found, execute command
2022-10-13 09:49:03 +02:00
if ( str_buf = = ' \r ' ) {
readfile - > read ( ) ; //dispose \n because \r\n
2022-06-17 23:57:29 +02:00
break ;
}
i + + ;
2022-10-13 09:49:03 +02:00
} //End while
2022-06-17 23:57:29 +02:00
}
//Get line from file
2022-10-13 09:49:03 +02:00
void get_line ( char * str_buf , FsFile * readfile , uint8_t maxi ) {
2022-10-25 17:09:08 +02:00
int read_len ;
2022-08-03 12:14:32 +02:00
// Status LED on
statusLED ( true ) ;
2022-10-25 17:09:08 +02:00
read_len = readfile - > read ( str_buf , maxi - 1 ) ;
2022-06-17 23:57:29 +02:00
2022-10-25 17:09:08 +02:00
for ( int i = 0 ; i < read_len ; i + + ) {
2022-06-17 23:57:29 +02:00
//if end of file or newline found, execute command
2022-10-13 09:49:03 +02:00
if ( str_buf [ i ] = = ' \r ' ) {
2022-10-25 17:09:08 +02:00
str_buf [ i ] = 0 ;
2022-10-28 15:02:51 +02:00
readfile - > seekCur ( i - read_len + 2 ) ; // +2 to skip over \n because \r\n
2022-10-25 17:09:08 +02:00
return ;
}
}
str_buf [ maxi - 1 ] = 0 ;
// EOL was not found, keep looking (slower)
while ( readfile - > available ( ) ) {
if ( readfile - > read ( ) = = ' \r ' ) {
2022-10-28 15:02:51 +02:00
readfile - > read ( ) ; // read \n because \r\n
2022-06-17 23:57:29 +02:00
break ;
}
2022-10-25 17:09:08 +02:00
}
2022-06-17 23:57:29 +02:00
}
2022-10-28 15:02:51 +02:00
void rewind_line ( FsFile & readfile , byte count = 1 ) {
2022-10-23 04:54:59 +02:00
uint32_t position = readfile . curPosition ( ) ;
2022-11-05 01:31:51 +01:00
// To seek one line back, this code must step over the first newline it finds
// in order to exit the current line and enter the end of the previous one.
// Convert <count> from how-many-lines-back into how-many-newlines-to-look-for
// by incrementing it by 1.
2022-10-23 04:54:59 +02:00
count + + ;
for ( byte count_newline = 0 ; count_newline < count ; count_newline + + ) {
2022-11-05 01:31:51 +01:00
// Go to the strictly previous '\n', or file start.
2022-11-02 00:44:16 +01:00
while ( position ) {
2022-11-05 01:31:51 +01:00
// Seek back first (keeping position updated)...
2022-11-02 00:44:16 +01:00
position - - ;
2022-10-23 04:54:59 +02:00
readfile . seekCur ( - 1 ) ;
2022-11-05 01:31:51 +01:00
// ...and check current byte second.
// Note: this code assumed all files use ASCII with DOS-style newlines
// so \n is encountered first when seeking backwards.
2022-10-23 04:54:59 +02:00
if ( readfile . peek ( ) = = ' \n ' )
break ;
}
}
2022-11-05 01:31:51 +01:00
// If not at file start, the current character is the '\n' just before the
// desired line, so advance by one.
2022-11-02 01:20:35 +01:00
if ( position )
readfile . seekCur ( 1 ) ;
2022-10-23 04:54:59 +02:00
}
2022-06-17 23:57:29 +02:00
// Calculate CRC32 if needed and compare it to CRC read from database
2023-02-10 13:16:32 +01:00
boolean compareCRC ( const char * database , uint32_t crc32sum , boolean renamerom , int offset ) {
2022-06-16 17:17:16 +02:00
char crcStr [ 9 ] ;
2023-02-10 13:16:32 +01:00
print_Msg ( F ( " CRC32... " ) ) ;
display_Update ( ) ;
if ( crc32sum = = 0 ) {
2022-06-21 13:29:19 +02:00
//go to root
sd . chdir ( ) ;
2022-06-17 23:57:29 +02:00
// Calculate CRC32
2022-07-07 00:15:13 +02:00
sprintf ( crcStr , " %08lX " , calculateCRC ( fileName , folder , offset ) ) ;
2022-10-13 09:49:03 +02:00
} else {
2023-02-10 13:16:32 +01:00
// Convert precalculated crc to string
sprintf ( crcStr , " %08lX " , ~ crc32sum ) ;
2022-06-17 23:57:29 +02:00
}
2022-06-16 17:17:16 +02:00
// Print checksum
print_Msg ( crcStr ) ;
2022-06-17 23:57:29 +02:00
display_Update ( ) ;
2022-06-16 17:17:16 +02:00
//Search for CRC32 in file
2022-10-25 17:31:25 +02:00
char gamename [ 96 ] ;
2022-06-16 17:17:16 +02:00
char crc_search [ 9 ] ;
//go to root
sd . chdir ( ) ;
if ( myFile . open ( database , O_READ ) ) {
//Search for same CRC in list
while ( myFile . available ( ) ) {
//Read 2 lines (game name and CRC)
2022-10-25 17:31:25 +02:00
get_line ( gamename , & myFile , sizeof ( gamename ) ) ;
get_line ( crc_search , & myFile , sizeof ( crc_search ) ) ;
2022-10-13 09:49:03 +02:00
skip_line ( & myFile ) ; //Skip every 3rd line
2022-06-16 17:17:16 +02:00
//if checksum search successful, rename the file and end search
2022-10-13 09:49:03 +02:00
if ( strcmp ( crc_search , crcStr ) = = 0 ) {
2023-02-10 13:16:32 +01:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NES
if ( ( mode = = CORE_NES ) & & ( offset ! = 0 ) ) {
2022-07-07 23:20:45 +02:00
// Rewind to iNES Header
2022-10-23 07:01:59 +02:00
myFile . seekCur ( - 36 ) ;
2022-07-07 23:20:45 +02:00
char iNES_STR [ 33 ] ;
// Read iNES header
get_line ( iNES_STR , & myFile , 33 ) ;
// Convert "4E4553" to (0x4E, 0x45, 0x53)
2022-10-22 10:25:37 +02:00
unsigned int iNES_BUF ;
2022-07-07 23:20:45 +02:00
for ( byte j = 0 ; j < 16 ; j + + ) {
2022-10-22 10:25:37 +02:00
sscanf ( iNES_STR + j * 2 , " %2X " , & iNES_BUF ) ;
iNES_HEADER [ j ] = iNES_BUF ;
2022-07-07 23:20:45 +02:00
}
//Skip CRLF
2022-10-23 07:01:59 +02:00
myFile . seekCur ( 4 ) ;
2022-07-07 23:20:45 +02:00
}
2024-03-02 17:26:35 +01:00
# endif // ENABLE_NES
2022-07-07 23:20:45 +02:00
2022-06-16 17:17:16 +02:00
// Close the file:
myFile . close ( ) ;
2022-07-07 23:20:45 +02:00
//Write iNES header
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NES
if ( ( mode = = CORE_NES ) & & ( offset ! = 0 ) ) {
2022-07-07 23:20:45 +02:00
// Write iNES header
sd . chdir ( folder ) ;
if ( ! myFile . open ( fileName , O_RDWR ) ) {
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2022-07-07 23:20:45 +02:00
}
for ( byte z = 0 ; z < 16 ; z + + ) {
myFile . write ( iNES_HEADER [ z ] ) ;
}
myFile . close ( ) ;
}
2024-03-02 17:26:35 +01:00
# endif // ENABLE_NES
2022-06-16 17:17:16 +02:00
print_Msg ( F ( " -> " ) ) ;
2022-08-03 12:14:32 +02:00
display_Update ( ) ;
2022-06-16 17:17:16 +02:00
2022-08-03 12:14:32 +02:00
if ( renamerom ) {
println_Msg ( gamename ) ;
2023-06-26 17:05:10 +02:00
// Rename file to database name
2022-08-03 12:14:32 +02:00
sd . chdir ( folder ) ;
2022-10-09 11:38:25 +02:00
delay ( 100 ) ;
2022-08-03 12:14:32 +02:00
if ( myFile . open ( fileName , O_READ ) ) {
myFile . rename ( gamename ) ;
// Close the file:
myFile . close ( ) ;
}
2022-10-13 09:49:03 +02:00
} else {
2022-08-03 12:14:32 +02:00
println_Msg ( " OK " ) ;
2022-06-16 17:17:16 +02:00
}
2022-06-17 23:57:29 +02:00
return 1 ;
2022-06-16 17:17:16 +02:00
break ;
}
}
2022-10-13 09:49:03 +02:00
if ( strcmp ( crc_search , crcStr ) ! = 0 ) {
2023-02-10 09:40:58 +01:00
print_Error ( F ( " -> Not found " ) ) ;
2022-06-17 23:57:29 +02:00
return 0 ;
2022-06-16 17:17:16 +02:00
}
2022-10-13 09:49:03 +02:00
} else {
2022-08-21 12:28:47 +02:00
println_Msg ( F ( " -> Error " ) ) ;
2023-02-10 09:40:58 +01:00
print_Error ( F ( " Database missing " ) ) ;
2022-06-17 23:57:29 +02:00
return 0 ;
2022-06-16 17:17:16 +02:00
}
2022-10-22 10:25:37 +02:00
return 0 ;
2022-06-16 17:17:16 +02:00
}
2020-07-11 13:39:12 +02:00
2024-03-02 17:26:35 +01:00
void starting_letter__subDraw ( byte selection , byte line ) {
display . setDrawColor ( 0 ) ;
for ( uint8_t i = 0 ; i < 4 ; i + + ) display . drawLine ( 0 , 10 + i * 16 , 128 , 10 + i * 16 ) ;
display . setDrawColor ( 1 ) ;
display . drawLine ( 4 + selection * 16 , 10 + line * 16 , 9 + selection * 16 , 10 + line * 16 ) ;
display_Update ( ) ;
}
2022-08-06 14:50:40 +02:00
byte starting_letter ( ) {
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2022-10-04 20:30:53 +02:00
// Disable log to prevent unnecessary logging
dont_log = true ;
# endif
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-08-06 14:50:40 +02:00
byte selection = 0 ;
byte line = 0 ;
display_Clear ( ) ;
2022-10-08 16:23:33 +02:00
2022-08-06 14:50:40 +02:00
println_Msg ( F ( " [#] [A] [B] [C] [D] [E] [F] " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2022-08-06 14:50:40 +02:00
println_Msg ( F ( " [G] [H] [ I ] [J] [K] [L] [M] " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2022-08-06 14:50:40 +02:00
println_Msg ( F ( " [N] [O] [P] [Q] [R] [S] [T] " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2022-08-06 14:50:40 +02:00
println_Msg ( F ( " [U] [V] [W] [X] [Y] [Z] [?] " ) ) ;
// Draw selection line
display . setDrawColor ( 1 ) ;
display . drawLine ( 4 + selection * 16 , 10 + line * 16 , 9 + selection * 16 , 10 + line * 16 ) ;
display_Update ( ) ;
while ( 1 ) {
2024-03-02 17:26:35 +01:00
uint8_t b = checkButton ( ) ;
2022-10-13 09:49:03 +02:00
if ( b = = 2 ) { // Previous
2022-08-06 14:50:40 +02:00
if ( ( selection = = 0 ) & & ( line > 0 ) ) {
line - - ;
selection = 6 ;
2022-10-13 09:49:03 +02:00
} else if ( ( selection = = 0 ) & & ( line = = 0 ) ) {
2022-10-07 10:15:18 +02:00
line = 3 ;
selection = 6 ;
2022-10-13 09:49:03 +02:00
} else if ( selection > 0 ) {
2022-08-06 14:50:40 +02:00
selection - - ;
}
2024-03-02 17:26:35 +01:00
starting_letter__subDraw ( selection , line ) ;
2022-08-06 14:50:40 +02:00
}
2022-10-13 09:49:03 +02:00
else if ( b = = 1 ) { // Next
2022-08-06 14:50:40 +02:00
if ( ( selection = = 6 ) & & ( line < 3 ) ) {
line + + ;
selection = 0 ;
2022-10-13 09:49:03 +02:00
} else if ( ( selection = = 6 ) & & ( line = = 3 ) ) {
2022-10-07 10:15:18 +02:00
line = 0 ;
selection = 0 ;
2022-10-13 09:49:03 +02:00
} else if ( selection < 6 ) {
2022-08-06 14:50:40 +02:00
selection + + ;
}
2024-03-02 17:26:35 +01:00
starting_letter__subDraw ( selection , line ) ;
2022-08-06 14:50:40 +02:00
}
2022-10-13 09:49:03 +02:00
else if ( b = = 3 ) { // Long Press - Execute
2022-10-08 18:19:31 +02:00
if ( ( selection + line * 7 ) ! = 27 ) {
display_Clear ( ) ;
println_Msg ( F ( " Please wait... " ) ) ;
display_Update ( ) ;
}
2022-08-06 14:50:40 +02:00
break ;
}
}
return ( selection + line * 7 ) ;
# elif defined(SERIAL_MONITOR)
Serial . println ( F ( " Enter first letter: " ) ) ;
while ( Serial . available ( ) = = 0 ) {
}
// Read the incoming byte:
byte incomingByte = Serial . read ( ) ;
return incomingByte ;
# endif
2022-10-04 20:30:53 +02:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2022-10-04 20:30:53 +02:00
// Enable log again
dont_log = false ;
# endif
2022-08-06 14:50:40 +02:00
}
2022-10-30 02:36:59 +02:00
void print_MissingModule ( void ) {
display_Clear ( ) ;
println_Msg ( F ( " Please enable module " ) ) ;
2023-09-25 18:32:44 +02:00
print_FatalError ( F ( " in Config.h. " ) ) ;
2022-10-30 02:36:59 +02:00
}
2018-10-05 18:33:09 +02:00
/******************************************
2023-07-19 15:13:06 +02:00
Main menu
2018-10-05 18:33:09 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-03-02 17:26:35 +01:00
constexpr char modeItem1 [ ] PROGMEM = " Game Boy " ;
constexpr char modeItem2 [ ] PROGMEM = " NES/Famicom " ;
constexpr char modeItem3 [ ] PROGMEM = " Super Nintendo/SFC " ;
constexpr char modeItem4 [ ] PROGMEM = " Nintendo 64 (3V) " ;
constexpr char modeItem5 [ ] PROGMEM = " Mega Drive/Genesis " ;
constexpr char modeItem6 [ ] PROGMEM = " SMS/GG/MIII/SG-1000 " ;
constexpr char modeItem7 [ ] PROGMEM = " PC Engine/TG16 " ;
constexpr char modeItem8 [ ] PROGMEM = " WonderSwan (3V) " ;
constexpr char modeItem9 [ ] PROGMEM = " NeoGeo Pocket (3V) " ;
constexpr char modeItem10 [ ] PROGMEM = " Intellivision " ;
constexpr char modeItem11 [ ] PROGMEM = " Colecovision " ;
constexpr char modeItem12 [ ] PROGMEM = " Virtual Boy " ;
constexpr char modeItem13 [ ] PROGMEM = " Watara Supervision (3V) " ;
constexpr char modeItem14 [ ] PROGMEM = " Pocket Challenge W " ;
constexpr char modeItem15 [ ] PROGMEM = " Atari 2600 " ;
constexpr char modeItem16 [ ] PROGMEM = " Magnavox Odyssey 2 " ;
constexpr char modeItem17 [ ] PROGMEM = " Arcadia 2001 " ;
constexpr char modeItem18 [ ] PROGMEM = " Fairchild Channel F " ;
constexpr char modeItem19 [ ] PROGMEM = " Super A'can " ;
constexpr char modeItem20 [ ] PROGMEM = " MSX " ;
constexpr char modeItem21 [ ] PROGMEM = " Pokemon Mini (3V) " ;
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 " ;
2023-07-19 15:13:06 +02:00
static const char * const modeOptions [ ] PROGMEM = {
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GBX
2023-07-19 15:13:06 +02:00
modeItem1 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NES
2023-07-19 15:13:06 +02:00
modeItem2 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SNES
2023-07-19 15:13:06 +02:00
modeItem3 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_N64
2023-07-19 15:13:06 +02:00
modeItem4 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_MD
2023-07-19 15:13:06 +02:00
modeItem5 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SMS
2023-07-19 15:13:06 +02:00
modeItem6 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_PCE
2023-07-19 15:13:06 +02:00
modeItem7 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_WS
2023-07-19 15:13:06 +02:00
modeItem8 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NGP
2023-07-19 15:13:06 +02:00
modeItem9 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_INTV
2023-07-19 15:13:06 +02:00
modeItem10 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_COLV
2023-07-19 15:13:06 +02:00
modeItem11 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_VBOY
2023-07-19 15:13:06 +02:00
modeItem12 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_WSV
2023-07-19 15:13:06 +02:00
modeItem13 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_PCW
2023-07-19 15:13:06 +02:00
modeItem14 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_2600
2023-07-19 15:13:06 +02:00
modeItem15 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ODY2
2023-07-19 15:13:06 +02:00
modeItem16 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ARC
2023-07-19 15:13:06 +02:00
modeItem17 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_FAIRCHILD
2023-07-19 15:13:06 +02:00
modeItem18 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SUPRACAN
2023-07-19 15:13:06 +02:00
modeItem19 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_MSX
2023-07-19 15:13:06 +02:00
modeItem20 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_POKE
2023-07-19 15:13:06 +02:00
modeItem21 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_LOOPY
2023-07-19 15:13:06 +02:00
modeItem22 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_C64
2023-07-19 15:13:06 +02:00
modeItem23 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_5200
2023-09-23 00:11:08 +02:00
modeItem24 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_7800
2023-09-23 00:11:08 +02:00
modeItem25 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_VECTREX
2023-09-23 00:11:08 +02:00
modeItem26 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_FLASH
2023-09-23 00:11:08 +02:00
modeItem27 ,
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SELFTEST
2023-09-25 16:23:43 +02:00
modeItem28 ,
2023-09-25 16:12:04 +02:00
# endif
2024-03-02 17:26:35 +01:00
modeItem29 , FSTRING_RESET
2023-07-19 15:13:06 +02:00
} ;
2024-03-02 17:26:35 +01:00
uint8_t pageMenu ( const __FlashStringHelper * question , const char * const * menuStrings , uint8_t entryCount , uint8_t default_choice = 0 ) {
// Create menu
uint8_t modeMenu ;
uint8_t num_answers ;
uint8_t option_offset ;
2023-07-19 15:13:06 +02:00
2024-03-02 17:26:35 +01:00
// Menu spans across multiple pages
2021-10-27 20:59:57 +02:00
currPage = 1 ;
lastPage = 1 ;
2024-03-02 17:26:35 +01:00
numPages = ( entryCount / 7 ) + ( ( entryCount % 7 ) ! = 0 ) ;
do {
option_offset = ( currPage - 1 ) * 7 ;
num_answers = ( ( entryCount < ( option_offset + 7 ) ) ? entryCount - option_offset : 7 ) ;
2022-10-30 02:31:09 +02:00
// Copy menuOptions out of progmem
2024-03-02 17:26:35 +01:00
convertPgm ( menuStrings + option_offset , num_answers ) ;
modeMenu = question_box ( question , menuOptions , num_answers , default_choice ) + option_offset ;
} while ( numPages ! = 0 ) ;
2021-10-27 20:59:57 +02:00
2022-03-21 01:01:35 +01:00
// Reset page number
currPage = 1 ;
2024-03-02 17:26:35 +01:00
return modeMenu ;
}
2023-07-19 15:13:06 +02:00
2024-03-02 17:26:35 +01:00
// All included slots
void mainMenu ( ) {
2021-10-27 20:59:57 +02:00
// wait for user choice to come back from the question box menu
2024-03-02 17:26:35 +01:00
switch ( pageMenu ( F ( " OPEN SOURCE CART READER " ) , modeOptions , SYSTEM_MENU_TOTAL ) ) {
2023-07-19 15:13:06 +02:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GBX
case SYSTEM_MENU_GBX :
return gbxMenu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NES
case SYSTEM_MENU_NES :
mode = CORE_NES ;
2022-06-09 00:35:11 +02:00
display_Clear ( ) ;
display_Update ( ) ;
setup_NES ( ) ;
2022-11-02 00:32:54 +01:00
getMapping ( ) ;
2022-09-18 19:26:43 +02:00
checkStatus_NES ( ) ;
2024-03-02 17:26:35 +01:00
return nesMenu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SNES
case SYSTEM_MENU_SNES :
return snsMenu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_N64
case SYSTEM_MENU_N64 :
return n64Menu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_MD
case SYSTEM_MENU_MD :
return mdMenu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SMS
case SYSTEM_MENU_SMS :
return smsMenu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_PCE
case SYSTEM_MENU_PCE :
return pcsMenu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_WS
case SYSTEM_MENU_WS :
2021-10-27 20:59:57 +02:00
display_Clear ( ) ;
display_Update ( ) ;
setup_WS ( ) ;
2024-03-02 17:26:35 +01:00
mode = CORE_WS ;
return wsMenu ( ) ;
2021-10-27 20:59:57 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NGP
case SYSTEM_MENU_NGP :
2021-10-27 20:59:57 +02:00
display_Clear ( ) ;
display_Update ( ) ;
setup_NGP ( ) ;
2024-03-02 17:26:35 +01:00
mode = CORE_NGP ;
2021-10-27 20:59:57 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_INTV
case SYSTEM_MENU_INTV :
2022-07-23 11:04:17 +02:00
setup_INTV ( ) ;
2024-03-02 17:26:35 +01:00
return intvMenu ( ) ;
2022-07-23 11:04:17 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_COLV
case SYSTEM_MENU_COLV :
2022-07-23 11:04:17 +02:00
setup_COL ( ) ;
2024-03-02 17:26:35 +01:00
return colMenu ( ) ;
2022-07-23 11:04:17 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_VBOY
case SYSTEM_MENU_VBOY :
2022-07-23 11:04:17 +02:00
setup_VBOY ( ) ;
vboyMenu ( ) ;
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_WSV
case SYSTEM_MENU_WSV :
2022-08-21 12:28:47 +02:00
setup_WSV ( ) ;
2024-03-02 17:26:35 +01:00
return wsvMenu ( ) ;
2022-08-21 12:28:47 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_PCW
case SYSTEM_MENU_PCW :
2022-09-25 10:36:28 +02:00
setup_PCW ( ) ;
2024-03-02 17:26:35 +01:00
return pcwMenu ( ) ;
2022-09-25 10:36:28 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_2600
case SYSTEM_MENU_2600 :
2023-12-26 23:13:51 +01:00
setup_2600 ( ) ;
2024-03-02 17:26:35 +01:00
return a2600Menu ( ) ;
2023-01-03 19:33:34 +01:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ODY2
case SYSTEM_MENU_ODY2 :
2023-01-03 19:33:34 +01:00
setup_ODY2 ( ) ;
2024-03-02 17:26:35 +01:00
return ody2Menu ( ) ;
2023-01-03 19:33:34 +01:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ARC
case SYSTEM_MENU_ARC :
2023-01-03 19:33:34 +01:00
setup_ARC ( ) ;
2024-03-02 17:26:35 +01:00
return arcMenu ( ) ;
2023-01-03 19:33:34 +01:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_FAIRCHILD
case SYSTEM_MENU_FAIRCHILD :
2023-01-03 19:33:34 +01:00
setup_FAIRCHILD ( ) ;
2024-03-02 17:26:35 +01:00
return fairchildMenu ( ) ;
2023-01-03 19:33:34 +01:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SUPRACAN
case SYSTEM_MENU_SUPRACAN :
return setup_SuprAcan ( ) ;
2023-02-23 12:04:33 +01:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_MSX
case SYSTEM_MENU_MSX :
2023-06-25 14:09:17 +02:00
setup_MSX ( ) ;
2024-03-02 17:26:35 +01:00
return msxMenu ( ) ;
2023-06-25 14:09:17 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_POKE
case SYSTEM_MENU_POKE :
2023-06-25 14:09:17 +02:00
setup_POKE ( ) ;
2024-03-02 17:26:35 +01:00
return pokeMenu ( ) ;
2023-06-25 14:09:17 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_LOOPY
case SYSTEM_MENU_LOOPY :
2023-07-05 10:29:46 +02:00
setup_LOOPY ( ) ;
2024-03-02 17:26:35 +01:00
return loopyMenu ( ) ;
2023-07-03 01:08:09 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_C64
case SYSTEM_MENU_C64 :
2023-10-17 18:41:14 +02:00
setup_C64 ( ) ;
2024-03-02 17:26:35 +01:00
return c64Menu ( ) ;
2023-09-23 00:11:08 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_5200
case SYSTEM_MENU_5200 :
2023-10-17 18:41:14 +02:00
setup_5200 ( ) ;
2024-03-02 17:26:35 +01:00
return a5200Menu ( ) ;
2023-09-23 00:11:08 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_7800
case SYSTEM_MENU_7800 :
2023-10-17 18:41:14 +02:00
setup_7800 ( ) ;
2024-03-02 17:26:35 +01:00
return a7800Menu ( ) ;
2023-09-23 00:11:08 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_VECTREX
case SYSTEM_MENU_VECTREX :
2023-10-17 18:41:14 +02:00
setup_VECTREX ( ) ;
2024-03-02 17:26:35 +01:00
return vectrexMenu ( ) ;
2023-09-23 00:11:08 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_FLASH
case SYSTEM_MENU_FLASH :
# ifdef ENABLE_VSELECT
2023-06-26 12:04:00 +02:00
setup_FlashVoltage ( ) ;
2024-03-02 17:26:35 +01:00
# endif
return flashMenu ( ) ;
2022-06-09 00:35:11 +02:00
break ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SELFTEST
case SYSTEM_MENU_SELFTEST :
return selfTest ( ) ;
2023-01-24 09:34:54 +01:00
break ;
2023-03-08 18:26:19 +01:00
# endif
2023-01-24 09:34:54 +01:00
2024-03-02 17:26:35 +01:00
case SYSTEM_MENU_ABOUT :
return aboutScreen ( ) ;
2023-03-08 18:26:19 +01:00
break ;
2024-03-02 17:26:35 +01:00
case SYSTEM_MENU_RESET :
return resetArduino ( ) ;
2023-02-23 12:04:33 +01:00
break ;
2022-07-17 14:50:59 +02:00
default :
2024-03-02 17:26:35 +01:00
return print_MissingModule ( ) ; // does not return
2021-10-27 20:59:57 +02:00
}
}
2023-03-08 18:26:19 +01:00
/******************************************
Self Test
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SELFTEST
2023-03-08 18:26:19 +01:00
void selfTest ( ) {
2023-06-26 12:04:00 +02:00
# ifdef ENABLE_VSELECT
// Set Automatic Voltage Selection to 3V
setVoltage ( VOLTS_SET_3V3 ) ;
# endif
2023-03-08 18:26:19 +01:00
display_Clear ( ) ;
println_Msg ( F ( " Self Test " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-03-08 18:26:19 +01:00
println_Msg ( F ( " Remove all Cartridges " ) ) ;
println_Msg ( F ( " before continuing!!! " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-03-08 18:26:19 +01:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
display_Clear ( ) ;
2023-08-02 00:26:20 +02:00
# if defined(HW3)
println_Msg ( F ( " Self Test " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-08-02 00:26:20 +02:00
println_Msg ( F ( " Turn the EEP switch on. " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-08-02 00:26:20 +02:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
display_Clear ( ) ;
# endif
2023-03-08 18:26:19 +01:00
// Test if pin 7 is held high by 1K resistor
pinMode ( 7 , INPUT ) ;
println_Msg ( F ( " Testing 1K resistor " ) ) ;
display_Update ( ) ;
if ( ! digitalRead ( 7 ) ) {
setColor_RGB ( 255 , 0 , 0 ) ;
errorLvl = 1 ;
println_Msg ( F ( " Error " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-03-08 18:26:19 +01:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
2023-03-25 08:51:02 +01:00
//wait();
//resetArduino();
2023-03-08 18:26:19 +01:00
}
println_Msg ( F ( " Testing short to GND " ) ) ;
display_Update ( ) ;
// Set pins 2-9, 14-17, 22-37, 42-49, 54-69 to input and activate internal pull-up resistors
for ( byte pinNumber = 2 ; pinNumber < = 69 ; pinNumber + + ) {
if ( ( ( 2 < = pinNumber ) & & ( pinNumber < = 9 ) ) | | ( ( 14 < = pinNumber ) & & ( pinNumber < = 17 ) ) | | ( ( 22 < = pinNumber ) & & ( pinNumber < = 37 ) ) | | ( ( 42 < = pinNumber ) & & ( pinNumber < = 49 ) ) | | ( ( 54 < = pinNumber ) & & ( pinNumber < = 69 ) ) ) {
pinMode ( pinNumber , INPUT_PULLUP ) ;
}
}
// Tests pins 2-9, 14-17, 22-37, 42-49, 54-69 for short to GND
for ( byte pinNumber = 2 ; pinNumber < = 69 ; pinNumber + + ) {
if ( ( ( 2 < = pinNumber ) & & ( pinNumber < = 9 ) ) | | ( ( 14 < = pinNumber ) & & ( pinNumber < = 17 ) ) | | ( ( 22 < = pinNumber ) & & ( pinNumber < = 37 ) ) | | ( ( 42 < = pinNumber ) & & ( pinNumber < = 49 ) ) | | ( ( 54 < = pinNumber ) & & ( pinNumber < = 69 ) ) ) {
if ( ! digitalRead ( pinNumber ) ) {
setColor_RGB ( 255 , 0 , 0 ) ;
errorLvl = 1 ;
print_Msg ( F ( " Error: Pin " ) ) ;
if ( ( 54 < = pinNumber ) & & ( pinNumber < = 69 ) ) {
print_Msg ( F ( " A " ) ) ;
println_Msg ( pinNumber - 54 ) ;
} else {
print_Msg ( F ( " D " ) ) ;
println_Msg ( pinNumber ) ;
}
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-03-08 18:26:19 +01:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
resetArduino ( ) ;
}
}
}
println_Msg ( F ( " Testing short between pins " ) ) ;
display_Update ( ) ;
// Test for short between pins 2-9, 14-17, 22-37, 42-49, 54-69
for ( byte pinNumber = 2 ; pinNumber < = 69 ; pinNumber + + ) {
if ( ( ( 2 < = pinNumber ) & & ( pinNumber < = 9 ) ) | | ( ( 14 < = pinNumber ) & & ( pinNumber < = 17 ) ) | | ( ( 22 < = pinNumber ) & & ( pinNumber < = 37 ) ) | | ( ( 42 < = pinNumber ) & & ( pinNumber < = 49 ) ) | | ( ( 54 < = pinNumber ) & & ( pinNumber < = 69 ) ) ) {
pinMode ( pinNumber , OUTPUT ) ;
digitalWrite ( pinNumber , LOW ) ;
for ( byte pinNumber2 = 2 ; pinNumber2 < = 69 ; pinNumber2 + + ) {
2023-03-25 08:51:02 +01:00
if ( ( ( ( 2 < = pinNumber2 ) & & ( pinNumber2 < = 9 ) ) | | ( ( 14 < = pinNumber2 ) & & ( pinNumber2 < = 17 ) ) | | ( ( 22 < = pinNumber2 ) & & ( pinNumber2 < = 37 ) ) | | ( ( 42 < = pinNumber2 ) & & ( pinNumber2 < = 49 ) ) | | ( ( 54 < = pinNumber2 ) & & ( pinNumber2 < = 69 ) ) ) & & ( pinNumber ! = pinNumber2 ) ) {
2023-03-08 18:26:19 +01:00
pinMode ( pinNumber2 , INPUT_PULLUP ) ;
if ( ! digitalRead ( pinNumber2 ) ) {
setColor_RGB ( 255 , 0 , 0 ) ;
errorLvl = 1 ;
print_Msg ( F ( " Error: Pin " ) ) ;
if ( ( 54 < = pinNumber ) & & ( pinNumber < = 69 ) ) {
print_Msg ( F ( " A " ) ) ;
print_Msg ( pinNumber - 54 ) ;
} else {
print_Msg ( F ( " D " ) ) ;
print_Msg ( pinNumber ) ;
}
print_Msg ( F ( " + " ) ) ;
if ( ( 54 < = pinNumber2 ) & & ( pinNumber2 < = 69 ) ) {
print_Msg ( F ( " A " ) ) ;
println_Msg ( pinNumber2 - 54 ) ;
} else {
print_Msg ( F ( " D " ) ) ;
println_Msg ( pinNumber2 ) ;
}
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-03-08 18:26:19 +01:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
resetArduino ( ) ;
}
}
}
pinMode ( pinNumber , INPUT_PULLUP ) ;
}
}
println_Msg ( F ( " Testing Clock Generator " ) ) ;
initializeClockOffset ( ) ;
if ( ! i2c_found ) {
setColor_RGB ( 255 , 0 , 0 ) ;
errorLvl = 1 ;
println_Msg ( F ( " Error: Clock Generator " ) ) ;
println_Msg ( F ( " not found " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-03-08 18:26:19 +01:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
resetArduino ( ) ;
}
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2023-03-08 18:26:19 +01:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
resetArduino ( ) ;
}
# endif
2021-10-27 20:59:57 +02:00
/******************************************
About Screen
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Info Screen
void aboutScreen ( ) {
display_Clear ( ) ;
println_Msg ( F ( " Cartridge Reader " ) ) ;
println_Msg ( F ( " github.com/sanni " ) ) ;
2024-03-02 17:26:35 +01:00
print_Msg ( F ( " 2024 FW " ) ) ;
println_Msg ( FS ( FSTRING_VERSION ) ) ;
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2022-10-21 15:59:06 +02:00
// Prints string out of the common strings array either with or without newline
print_STR ( press_button_STR , 1 ) ;
2021-10-27 20:59:57 +02:00
display_Update ( ) ;
while ( 1 ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2021-10-27 20:59:57 +02:00
// get input button
2024-03-02 17:26:35 +01:00
uint8_t b = checkButton ( ) ;
2021-10-27 20:59:57 +02:00
// if the cart readers input button is pressed shortly
if ( b = = 1 ) {
resetArduino ( ) ;
}
// if the cart readers input button is pressed long
if ( b = = 3 ) {
resetArduino ( ) ;
}
// if the button is pressed super long
if ( b = = 4 ) {
display_Clear ( ) ;
println_Msg ( F ( " Resetting folder... " ) ) ;
display_Update ( ) ;
delay ( 2000 ) ;
foldern = 0 ;
EEPROM_writeAnything ( 0 , foldern ) ;
resetArduino ( ) ;
}
2024-03-02 17:26:35 +01:00
# elif defined(ENABLE_SERIAL)
2021-10-27 20:59:57 +02:00
wait_serial ( ) ;
resetArduino ( ) ;
# endif
}
}
2019-09-01 14:36:53 +02:00
/******************************************
Progressbar
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void draw_progressbar ( uint32_t processed , uint32_t total ) {
uint8_t current , i ;
static uint8_t previous ;
uint8_t steps = 20 ;
//Find progressbar length and draw if processed size is not 0
if ( processed = = 0 ) {
previous = 0 ;
print_Msg ( F ( " [ " ) ) ;
display_Update ( ) ;
return ;
}
// Progress bar
current = ( processed > = total ) ? steps : processed / ( total / steps ) ;
//Draw "*" if needed
if ( current > previous ) {
for ( i = previous ; i < current ; i + + ) {
// steps are 20, so 20 - 1 = 19.
if ( i = = ( 19 ) ) {
//If end of progress bar, finish progress bar by drawing "]"
2021-11-17 21:49:20 +01:00
println_Msg ( F ( " ] " ) ) ;
2022-10-13 09:49:03 +02:00
} else {
2019-09-01 14:36:53 +02:00
print_Msg ( F ( " * " ) ) ;
}
}
//update previous "*" status
previous = current ;
//Update display
display_Update ( ) ;
2018-10-05 18:33:09 +02:00
}
}
2022-07-07 00:15:13 +02:00
/******************************************
RTC Module
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_RTC
2023-07-22 15:21:02 +02:00
# if defined(DS3231)
2022-07-07 00:15:13 +02:00
RTC_DS3231 rtc ;
2023-07-22 15:21:02 +02:00
# elif defined(DS1307)
RTC_DS1307 rtc ;
# endif
2022-07-07 00:15:13 +02:00
// Start Time
void RTCStart ( ) {
// Start RTC
2022-10-13 09:49:03 +02:00
if ( ! rtc . begin ( ) ) {
2022-07-07 00:15:13 +02:00
abort ( ) ;
}
2023-10-17 18:41:14 +02:00
// RTC_DS1307 does not have lostPower()
2023-07-22 15:21:02 +02:00
# if defined(DS3231)
2022-07-07 00:15:13 +02:00
// Set RTC Date/Time of Sketch Build if it lost battery power
// After initial setup it would have lost battery power ;)
if ( rtc . lostPower ( ) ) {
rtc . adjust ( DateTime ( F ( __DATE__ ) , F ( __TIME__ ) ) ) ;
}
2023-07-22 15:21:02 +02:00
# endif
2022-07-07 00:15:13 +02:00
}
// Set Date/Time Callback Funtion
// Callback for file timestamps
void dateTime ( uint16_t * date , uint16_t * time ) {
DateTime now = rtc . now ( ) ;
// Return date using FAT_DATE macro to format fields
* date = FAT_DATE ( now . year ( ) , now . month ( ) , now . day ( ) ) ;
// Return time using FAT_TIME macro to format fields
* time = FAT_TIME ( now . hour ( ) , now . minute ( ) , now . second ( ) ) ;
}
/******************************************
RTC Time Stamp Setup
Call in any other script
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Format a Date/Time stamp
String RTCStamp ( ) {
// Set a format
char dtstamp [ ] = " DDMMMYYYY hh:mm:ssAP " ;
// Get current Date/Time
DateTime now = rtc . now ( ) ;
// Convert it to a string and caps lock it
String dts = now . toString ( dtstamp ) ;
dts . toUpperCase ( ) ;
// Print results
return dts ;
}
# endif
/******************************************
Clockgen Calibration
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-03-02 17:26:35 +01:00
# ifdef OPTION_CLOCKGEN_CALIBRATION
2022-07-07 00:15:13 +02:00
int32_t cal_factor = 0 ;
int32_t old_cal = 0 ;
int32_t cal_offset = 100 ;
2022-10-13 09:49:03 +02:00
void clkcal ( ) {
2022-07-07 00:15:13 +02:00
// Adafruit Clock Generator
// last number is the clock correction factor which is custom for each clock generator
cal_factor = readClockOffset ( ) ;
2023-10-17 18:41:14 +02:00
display_Clear ( ) ;
print_Msg ( F ( " Read correction: " ) ) ;
println_Msg ( String ( cal_factor ) ) ;
display_Update ( ) ;
2022-07-07 00:15:13 +02:00
delay ( 500 ) ;
if ( cal_factor > INT32_MIN ) {
i2c_found = clockgen . init ( SI5351_CRYSTAL_LOAD_8PF , 0 , cal_factor ) ;
} else {
i2c_found = clockgen . init ( SI5351_CRYSTAL_LOAD_8PF , 0 , 0 ) ;
cal_factor = 0 ;
}
if ( ! i2c_found ) {
display_Clear ( ) ;
2022-10-30 03:21:01 +01:00
print_FatalError ( F ( " Clock Generator not found " ) ) ;
2022-07-07 00:15:13 +02:00
}
//clockgen.set_correction(cal_factor, SI5351_PLL_INPUT_XO);
clockgen . set_pll ( SI5351_PLL_FIXED , SI5351_PLLA ) ;
clockgen . set_pll ( SI5351_PLL_FIXED , SI5351_PLLB ) ;
//clockgen.pll_reset(SI5351_PLLA);
//clockgen.pll_reset(SI5351_PLLB);
clockgen . set_freq ( 400000000ULL , SI5351_CLK0 ) ;
clockgen . set_freq ( 100000000ULL , SI5351_CLK1 ) ;
clockgen . set_freq ( 307200000ULL , SI5351_CLK2 ) ;
clockgen . output_enable ( SI5351_CLK1 , 1 ) ;
clockgen . output_enable ( SI5351_CLK2 , 1 ) ;
clockgen . output_enable ( SI5351_CLK0 , 1 ) ;
// Frequency Counter
delay ( 500 ) ;
FreqCount . begin ( 1000 ) ;
2022-10-13 09:49:03 +02:00
while ( 1 ) {
2022-07-07 00:15:13 +02:00
if ( old_cal ! = cal_factor ) {
display_Clear ( ) ;
2023-10-17 18:41:14 +02:00
println_Msg ( F ( " Adjusting... " ) ) ;
2022-07-07 00:15:13 +02:00
display_Update ( ) ;
clockgen . set_correction ( cal_factor , SI5351_PLL_INPUT_XO ) ;
clockgen . set_pll ( SI5351_PLL_FIXED , SI5351_PLLA ) ;
clockgen . set_pll ( SI5351_PLL_FIXED , SI5351_PLLB ) ;
clockgen . pll_reset ( SI5351_PLLA ) ;
clockgen . pll_reset ( SI5351_PLLB ) ;
clockgen . set_freq ( 400000000ULL , SI5351_CLK0 ) ;
clockgen . set_freq ( 100000000ULL , SI5351_CLK1 ) ;
clockgen . set_freq ( 307200000ULL , SI5351_CLK2 ) ;
old_cal = cal_factor ;
delay ( 500 ) ;
2022-10-13 09:49:03 +02:00
} else {
2022-07-07 00:15:13 +02:00
clockgen . update_status ( ) ;
while ( clockgen . dev_status . SYS_INIT = = 1 ) {
}
if ( FreqCount . available ( ) ) {
float count = FreqCount . read ( ) ;
display_Clear ( ) ;
println_Msg ( F ( " Clock Calibration " ) ) ;
print_Msg ( F ( " Freq: " ) ) ;
print_Msg ( count ) ;
println_Msg ( F ( " Hz " ) ) ;
print_Msg ( F ( " Correction: " ) ) ;
2023-10-17 18:41:14 +02:00
println_Msg ( String ( cal_factor ) ) ;
print_Msg ( F ( " Step: " ) ) ;
2022-07-07 00:15:13 +02:00
print_right ( cal_offset ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
# ifdef ENABLE_BUTTON2
2022-07-07 00:15:13 +02:00
println_Msg ( F ( " (Hold button to save) " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2022-07-07 00:15:13 +02:00
println_Msg ( F ( " Decrease Increase " ) ) ;
# else
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ROTARY
2023-10-17 18:41:14 +02:00
println_Msg ( F ( " Rotate to adjust Frequency " ) ) ;
println_Msg ( F ( " Press to change step width " ) ) ;
println_Msg ( F ( " Hold to save " ) ) ;
2022-07-07 00:15:13 +02:00
# else
println_Msg ( F ( " Click/dbl to adjust " ) ) ;
# endif
# endif
display_Update ( ) ;
}
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_BUTTON2
2022-07-07 00:15:13 +02:00
// get input button
2024-03-02 17:26:35 +01:00
uint8_t a = checkButton1 ( ) ;
uint8_t b = checkButton2 ( ) ;
2022-07-07 00:15:13 +02:00
// if the cart readers input button is pressed shortly
if ( a = = 1 ) {
old_cal = cal_factor ;
cal_factor - = cal_offset ;
}
if ( b = = 1 ) {
old_cal = cal_factor ;
cal_factor + = cal_offset ;
}
// if the cart readers input buttons is double clicked
if ( a = = 2 ) {
cal_offset / = 10ULL ;
2022-10-13 09:49:03 +02:00
if ( cal_offset < 1 ) {
2022-07-07 00:15:13 +02:00
cal_offset = 100000000ULL ;
}
}
if ( b = = 2 ) {
cal_offset * = 10ULL ;
2022-10-13 09:49:03 +02:00
if ( cal_offset > 100000000ULL ) {
2022-07-07 00:15:13 +02:00
cal_offset = 1 ;
}
}
// if the cart readers input button is pressed long
if ( a = = 3 ) {
savetofile ( ) ;
}
if ( b = = 3 ) {
savetofile ( ) ;
}
# else
//Handle inputs for either rotary encoder or single button interface.
2024-03-02 17:26:35 +01:00
uint8_t a = checkButton ( ) ;
2022-07-07 00:15:13 +02:00
2022-10-13 09:49:03 +02:00
if ( a = = 1 ) { //clockwise rotation or single click
2022-07-07 00:15:13 +02:00
old_cal = cal_factor ;
cal_factor + = cal_offset ;
}
if ( a = = 2 ) { //counterclockwise rotation or double click
old_cal = cal_factor ;
cal_factor - = cal_offset ;
}
2022-10-13 09:49:03 +02:00
if ( a = = 3 ) { //button short hold
2022-07-07 00:15:13 +02:00
cal_offset * = 10ULL ;
2022-10-13 09:49:03 +02:00
if ( cal_offset > 100000000ULL ) {
2022-07-07 00:15:13 +02:00
cal_offset = 1 ;
}
}
2022-10-13 09:49:03 +02:00
if ( a = = 4 ) { //button long hold
2022-07-07 00:15:13 +02:00
savetofile ( ) ;
}
# endif
}
}
}
2022-10-13 09:49:03 +02:00
void print_right ( int32_t number ) {
2022-07-07 00:15:13 +02:00
int32_t abs_number = number ;
if ( abs_number < 0 )
abs_number * = - 1 ;
else
2024-03-02 17:26:35 +01:00
print_Msg ( FS ( FSTRING_SPACE ) ) ;
2022-07-07 00:15:13 +02:00
if ( abs_number = = 0 )
abs_number = 1 ;
2022-10-13 09:49:03 +02:00
while ( abs_number < 100000000ULL ) {
2024-03-02 17:26:35 +01:00
print_Msg ( FS ( FSTRING_SPACE ) ) ;
2022-07-07 00:15:13 +02:00
abs_number * = 10ULL ;
}
println_Msg ( number ) ;
}
void savetofile ( ) {
display_Clear ( ) ;
println_Msg ( F ( " Saving... " ) ) ;
2023-10-17 18:41:14 +02:00
println_Msg ( String ( cal_factor ) ) ;
2022-07-07 00:15:13 +02:00
display_Update ( ) ;
delay ( 2000 ) ;
if ( ! myFile . open ( " /snes_clk.txt " , O_WRITE | O_CREAT | O_TRUNC ) ) {
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2022-07-07 00:15:13 +02:00
}
// Write calibration factor to file
myFile . print ( cal_factor ) ;
// Close the file:
myFile . close ( ) ;
2022-10-21 15:59:06 +02:00
print_STR ( done_STR , 1 ) ;
2022-07-07 00:15:13 +02:00
display_Update ( ) ;
delay ( 1000 ) ;
resetArduino ( ) ;
}
# endif
2024-03-02 17:26:35 +01:00
# if defined(OPTION_CLOCKGEN_CALIBRATION) || defined(OPTION_CLOCKGEN_USE_CALIBRATION)
2022-07-08 11:36:03 +02:00
int32_t atoi32_signed ( const char * input_string ) {
if ( input_string = = NULL ) {
return 0 ;
}
int int_sign = 1 ;
int i = 0 ;
if ( input_string [ 0 ] = = ' - ' ) {
int_sign = - 1 ;
i = 1 ;
}
int32_t return_val = 0 ;
while ( input_string [ i ] ! = ' \0 ' ) {
if ( input_string [ i ] > = ' 0 ' & & input_string [ i ] < = ' 9 ' ) {
return_val = ( return_val * 10 ) + ( input_string [ i ] - ' 0 ' ) ;
} else if ( input_string [ i ] ! = ' \0 ' ) {
return 0 ;
}
i + + ;
}
return_val = return_val * int_sign ;
return return_val ;
}
2022-07-07 00:15:13 +02:00
int32_t readClockOffset ( ) {
FsFile clock_file ;
char * clock_buf ;
int16_t i ;
int32_t clock_offset ;
if ( ! clock_file . open ( " /snes_clk.txt " , O_READ ) ) {
return INT32_MIN ;
}
clock_buf = ( char * ) malloc ( 12 * sizeof ( char ) ) ;
i = clock_file . read ( clock_buf , 11 ) ;
clock_file . close ( ) ;
if ( i = = - 1 ) {
free ( clock_buf ) ;
return INT32_MIN ;
} else if ( ( i = = 11 ) & & ( clock_buf [ 0 ] ! = ' - ' ) ) {
free ( clock_buf ) ;
return INT32_MIN ;
} else {
clock_buf [ i ] = 0 ;
}
for ( i = 0 ; i < 12 ; i + + ) {
if ( clock_buf [ i ] ! = ' - ' & & clock_buf [ i ] < ' 0 ' & & clock_buf [ i ] > ' 9 ' ) {
if ( i = = 0 ) {
free ( clock_buf ) ;
return INT32_MIN ;
} else if ( ( i = = 1 ) & & ( clock_buf [ 0 ] = = ' - ' ) ) {
free ( clock_buf ) ;
return INT32_MIN ;
} else {
clock_buf [ i ] = 0 ;
}
}
}
clock_offset = atoi32_signed ( clock_buf ) ;
free ( clock_buf ) ;
return clock_offset ;
}
# endif
int32_t initializeClockOffset ( ) {
2024-03-02 17:26:35 +01:00
# ifdef OPTION_CLOCKGEN_USE_CALIBRATION
2022-07-07 00:15:13 +02:00
FsFile clock_file ;
2022-10-13 09:49:03 +02:00
const char zero_char_arr [ ] = { ' 0 ' } ;
2022-07-07 00:15:13 +02:00
int32_t clock_offset = readClockOffset ( ) ;
if ( clock_offset > INT32_MIN ) {
i2c_found = clockgen . init ( SI5351_CRYSTAL_LOAD_8PF , 0 , clock_offset ) ;
} else {
i2c_found = clockgen . init ( SI5351_CRYSTAL_LOAD_8PF , 0 , 0 ) ;
if ( clock_file . open ( " /snes_clk.txt " , O_WRITE | O_CREAT | O_TRUNC ) ) {
clock_file . write ( zero_char_arr , 1 ) ;
clock_file . close ( ) ;
}
}
return clock_offset ;
# else
// last number is the clock correction factor which is custom for each clock generator
i2c_found = clockgen . init ( SI5351_CRYSTAL_LOAD_8PF , 0 , 0 ) ;
return 0 ;
# endif
}
2018-10-05 18:33:09 +02:00
/******************************************
Setup
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void setup ( ) {
2022-06-09 00:35:11 +02:00
// Set Button Pin PG2 to Input
2018-10-05 18:33:09 +02:00
DDRG & = ~ ( 1 < < 2 ) ;
2024-02-29 23:52:04 +01:00
# if defined(HW5) && !defined(ENABLE_VSELECT)
/**
* HW5 has status LED connected to PD7
* Set LED Pin PD7 to Output
* */
2022-06-09 00:35:11 +02:00
DDRD | = ( 1 < < 7 ) ;
PORTD | = ( 1 < < 7 ) ;
2024-02-29 23:52:04 +01:00
# elif defined(ENABLE_VSELECT)
/**
* VSELECT uses pin PD7
* Set LED Pin PD7 to Output
* */
2022-11-12 20:13:22 +01:00
DDRD | = ( 1 < < 7 ) ;
2024-02-29 23:52:04 +01:00
# else /* !defined(HW5) && !defined(HW5) */
/**
* HW1 - 3 have button connected to PD7
* Set pin PD7 to input for button
* */
2022-06-09 00:35:11 +02:00
DDRD & = ~ ( 1 < < 7 ) ;
2024-02-29 23:52:04 +01:00
# endif /* HW5 &| ENABLE_VSELECT */
// Set power to low to protect carts
setVoltage ( VOLTS_SET_3V3 ) ;
# if defined(ENABLE_3V3FIX)
// Set clock high during setup
setClockScale ( CLKSCALE_16MHZ ) ;
delay ( 10 ) ;
# endif /* ENABLE_3V3FIX */
2022-06-09 00:35:11 +02:00
2024-03-02 17:26:35 +01:00
# if !defined(ENABLE_SERIAL) && defined(ENABLE_UPDATER)
2024-02-29 23:52:04 +01:00
ClockedSerial . begin ( UPD_BAUD ) ;
printVersionToSerial ( ) ;
ClockedSerial . flush ( ) ;
# endif /* ENABLE_UPDATER */
2018-10-05 18:33:09 +02:00
2024-02-29 23:52:04 +01:00
// Read current folder number out of the EEPROM
2019-09-27 17:38:42 +02:00
EEPROM_readAnything ( 0 , foldern ) ;
2023-03-30 03:05:01 +02:00
if ( foldern < 0 ) foldern = 0 ;
2018-10-05 18:33:09 +02:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_LCD
2021-10-24 00:41:18 +02:00
display . begin ( ) ;
2021-10-27 20:59:57 +02:00
display . setContrast ( 40 ) ;
2021-10-24 00:41:18 +02:00
display . setFont ( u8g2_font_haxrcorp4089_tr ) ;
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_LCD */
2021-10-24 00:41:18 +02:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NEOPIXEL
2021-10-24 00:41:18 +02:00
pixels . begin ( ) ;
2024-02-29 23:52:04 +01:00
setColor_RGB ( 0 , 0 , 100 ) ;
2021-11-29 14:32:37 +01:00
2023-10-17 18:41:14 +02:00
// Set TX0 LED Pin(PE1) to Output for status indication during flashing for HW4
2024-03-02 17:26:35 +01:00
# if !(defined(ENABLE_SERIAL) || defined(HW5))
2023-04-20 11:38:46 +02:00
DDRE | = ( 1 < < 1 ) ;
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_SERIAL */
# else /* !ENABLE_NEOPIXEL */
# ifndef ENABLE_LCD
# ifdef ENABLE_CA_LED
2023-04-20 11:38:46 +02:00
// Turn LED off
digitalWrite ( 12 , 1 ) ;
digitalWrite ( 11 , 1 ) ;
digitalWrite ( 10 , 1 ) ;
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_CA_LED */
2023-04-20 11:38:46 +02:00
// Configure 4 Pin RGB LED pins as output
DDRB | = ( 1 < < DDB6 ) ; // Red LED (pin 12)
DDRB | = ( 1 < < DDB5 ) ; // Green LED (pin 11)
DDRB | = ( 1 < < DDB4 ) ; // Blue LED (pin 10)
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_LCD */
# endif /* ENABLE_NEOPIXEL */
2023-04-20 11:38:46 +02:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_RTC
2024-02-29 23:52:04 +01:00
// Start RTC
RTCStart ( ) ;
2021-10-24 00:41:18 +02:00
2024-02-29 23:52:04 +01:00
// Set Date/Time Callback Funtion
SdFile : : dateTimeCallback ( dateTime ) ;
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_RTC */
2024-02-29 23:52:04 +01:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_OLED
2022-10-08 16:23:33 +02:00
display . begin ( ) ;
//isplay.setContrast(40);
display . setFont ( u8g2_font_haxrcorp4089_tr ) ;
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_OLED */
2018-10-05 18:33:09 +02:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
// Serial Begin
Serial . begin ( 9600 ) ;
2024-03-02 17:30:44 +01:00
Serial . println ( FS ( FSTRING_EMPTY ) ) ;
2019-09-05 00:48:39 +02:00
Serial . println ( F ( " Cartridge Reader " ) ) ;
2024-03-02 17:26:35 +01:00
Serial . println ( F ( " 2024 github.com/sanni " ) ) ;
2019-09-05 00:48:39 +02:00
// LED Error
2021-10-24 00:41:18 +02:00
setColor_RGB ( 0 , 0 , 255 ) ;
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_SERIAL */
2018-10-05 18:33:09 +02:00
// Init SD card
2021-10-24 00:41:18 +02:00
if ( ! sd . begin ( SS ) ) {
2018-10-05 18:33:09 +02:00
display_Clear ( ) ;
2024-02-29 23:52:04 +01:00
# ifdef ENABLE_VSELECT
print_STR ( sd_error_STR , 1 ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2024-02-29 23:52:04 +01:00
println_Msg ( F ( " Press button to enable 5V for " ) ) ;
println_Msg ( F ( " updating firmware... " ) ) ;
display_Update ( ) ;
wait ( ) ;
display_Clear ( ) ;
setVoltage ( VOLTS_SET_5V ) ; // Set voltage high for flashing
println_Msg ( F ( " ======== UPDATE MODE ======== " ) ) ;
println_Msg ( F ( " Waiting for update... " ) ) ;
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2024-02-29 23:52:04 +01:00
println_Msg ( F ( " Press button to cancel/restart. " ) ) ;
display_Update ( ) ;
wait ( ) ;
resetArduino ( ) ;
# else /* !ENABLE_VSELECT */
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2024-02-29 23:52:04 +01:00
# endif /* ENABLE_VSELECT */
2018-10-05 18:33:09 +02:00
}
2024-02-29 23:52:04 +01:00
# if defined(ENABLE_CONFIG)
configInit ( ) ;
2024-03-02 17:26:35 +01:00
# if defined(ENABLE_GLOBAL_LOG)
2024-02-29 23:52:04 +01:00
loggingEnabled = ! ! configGetLong ( F ( " oscr.logging " ) , 1 ) ;
# endif /*ENABLE_CONFIG*/
2023-03-30 03:05:01 +02:00
2024-02-29 23:52:04 +01:00
// Change LCD background if config specified
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NEOPIXEL
2024-02-29 23:52:04 +01:00
setColor_RGB ( 0 , 0 , 100 ) ;
2024-03-02 17:26:35 +01:00
# endif /* ENABLE_NEOPIXEL */
2024-02-29 23:52:04 +01:00
# endif /* ENABLE_CONFIG */
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2022-06-12 12:30:52 +02:00
if ( ! myLog . open ( " OSCR_LOG.txt " , O_RDWR | O_CREAT | O_APPEND ) ) {
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2022-06-12 12:30:52 +02:00
}
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2024-02-29 23:52:04 +01:00
# if defined(HW1)
2022-06-14 15:25:53 +02:00
print_Msg ( F ( " OSCR HW1 " ) ) ;
2024-02-29 23:52:04 +01:00
# elif defined(HW2)
2022-06-14 15:25:53 +02:00
print_Msg ( F ( " OSCR HW2 " ) ) ;
2024-02-29 23:52:04 +01:00
# elif defined(HW3)
2022-06-14 15:25:53 +02:00
print_Msg ( F ( " OSCR HW3 " ) ) ;
2024-02-29 23:52:04 +01:00
# elif defined(HW4)
2022-06-14 15:25:53 +02:00
print_Msg ( F ( " OSCR HW4 " ) ) ;
2024-02-29 23:52:04 +01:00
# elif defined(HW5)
2022-06-14 15:25:53 +02:00
print_Msg ( F ( " OSCR HW5 " ) ) ;
2024-02-29 23:52:04 +01:00
# elif defined(SERIAL_MONITOR)
2022-06-14 15:25:53 +02:00
print_Msg ( F ( " OSCR Serial " ) ) ;
2024-02-29 23:52:04 +01:00
# endif /* HWn */
2024-03-02 17:26:35 +01:00
print_Msg ( FS ( FSTRING_SPACE ) ) ;
println_Msg ( FS ( FSTRING_VERSION ) ) ;
# endif /* ENABLE_GLOBAL_LOG */
2022-06-12 12:30:52 +02:00
2024-02-29 23:52:04 +01:00
// Turn status LED on
2022-08-03 12:14:32 +02:00
statusLED ( true ) ;
2024-02-29 23:52:04 +01:00
# if defined(ENABLE_3V3FIX)
setClockScale ( CLKSCALE_8MHZ ) ; // Set clock back to low after setup
# endif /* ENABLE_3V3FIX */
2022-08-03 12:14:32 +02:00
// Start menu system
2023-06-26 12:04:00 +02:00
mainMenu ( ) ;
2018-10-05 18:33:09 +02:00
}
/******************************************
Common I / O Functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Switch data pins to write
void dataOut ( ) {
DDRC = 0xFF ;
}
// Switch data pins to read
void dataIn ( ) {
// Set to Input and activate pull-up resistors
DDRC = 0x00 ;
// Pullups
PORTC = 0xFF ;
}
/******************************************
Helper Functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-10-24 00:41:18 +02:00
// Set RGB color
void setColor_RGB ( byte r , byte g , byte b ) {
2024-03-02 17:26:35 +01:00
# if defined(ENABLE_NEOPIXEL)
2024-02-29 23:52:04 +01:00
# if defined(ENABLE_3V3FIX)
2023-04-20 11:38:46 +02:00
if ( clock = = CS_8MHZ ) return ;
2024-02-29 23:52:04 +01:00
# endif
2021-10-26 22:19:10 +02:00
// Dim Neopixel LEDs
if ( r > = 100 ) r = 100 ;
if ( g > = 100 ) g = 100 ;
if ( b > = 100 ) b = 100 ;
2024-02-29 23:52:04 +01:00
2021-10-24 00:41:18 +02:00
pixels . clear ( ) ;
2024-02-29 23:52:04 +01:00
# if defined(ENABLE_CONFIG)
uint8_t lcdConfColor = configGetLong ( F ( " lcd.confColor " ) ) ;
if ( lcdConfColor > 0 ) {
uint8_t lcdRed = configGetLong ( F ( " lcd.red " ) ) ;
uint8_t lcdGreen = configGetLong ( F ( " lcd.green " ) ) ;
uint8_t lcdBlue = configGetLong ( F ( " lcd.blue " ) ) ;
pixels . setPixelColor ( 0 , pixels . Color ( lcdGreen , lcdRed , lcdBlue ) ) ;
} else {
2024-03-02 17:26:35 +01:00
pixels . setPixelColor ( 0 , pixels . Color ( OPTION_LCD_BG_COLOR ) ) ;
2024-02-29 23:52:04 +01:00
}
# else /* !ENABLE_CONFIG */
2024-03-02 17:26:35 +01:00
pixels . setPixelColor ( 0 , pixels . Color ( OPTION_LCD_BG_COLOR ) ) ;
2024-02-29 23:52:04 +01:00
# endif /* ENABLE_CONFIG */
2021-10-24 00:41:18 +02:00
pixels . setPixelColor ( 1 , pixels . Color ( g , r , b ) ) ;
pixels . setPixelColor ( 2 , pixels . Color ( g , r , b ) ) ;
pixels . show ( ) ;
2024-03-02 17:26:35 +01:00
# elif defined(ENABLE_CA_LED)
2022-08-28 08:59:00 +02:00
// Set color of analog 4 Pin common anode RGB LED
analogWrite ( 12 , 255 - r ) ;
analogWrite ( 11 , 255 - g ) ;
analogWrite ( 10 , 255 - b ) ;
2021-10-24 00:41:18 +02:00
# else
2022-08-28 08:59:00 +02:00
// Set color of analog 4 Pin common cathode RGB LED
2022-08-27 23:55:30 +02:00
analogWrite ( 12 , r ) ;
analogWrite ( 11 , g ) ;
analogWrite ( 10 , b ) ;
2021-10-24 00:41:18 +02:00
# endif
}
2022-10-23 17:49:38 +02:00
// Extract ASCII printable characters from input, collapsing underscores and spaces.
// Use when extracting titles from cartridges, to build a rom title.
2022-10-28 15:02:51 +02:00
byte buildRomName ( char * output , const byte * input , byte length ) {
2022-10-23 17:49:38 +02:00
byte input_char ;
byte output_len = 0 ;
for ( unsigned int i = 0 ; i < length ; i + + ) {
input_char = input [ i ] ;
if ( isprint ( input_char ) & & input_char ! = ' < ' & & input_char ! = ' > ' & & input_char ! = ' : ' & & input_char ! = ' " ' & & input_char ! = ' / ' & & input_char ! = ' \\ ' & & input_char ! = ' | ' & & input_char ! = ' ? ' & & input_char ! = ' * ' ) {
output [ output_len + + ] = input_char ;
} else {
if ( output_len = = 0 | | output [ output_len - 1 ] ! = ' _ ' ) {
output [ output_len + + ] = ' _ ' ;
}
}
}
while (
2022-10-28 15:02:51 +02:00
output_len & & ( output [ output_len - 1 ] = = ' _ ' | | output [ output_len - 1 ] = = ' ' ) ) {
2022-10-23 17:49:38 +02:00
output_len - - ;
}
output [ output_len ] = 0 ;
return output_len ;
}
2018-10-05 18:33:09 +02:00
// Converts a progmem array into a ram array
void convertPgm ( const char * const pgmOptions [ ] , byte numArrays ) {
for ( int i = 0 ; i < numArrays ; i + + ) {
2019-10-15 06:00:35 +02:00
strlcpy_P ( menuOptions [ i ] , ( char * ) pgm_read_word ( & ( pgmOptions [ i ] ) ) , 20 ) ;
2018-10-05 18:33:09 +02:00
}
}
2022-10-30 03:21:01 +01:00
void _print_Error ( void ) {
2018-10-05 18:33:09 +02:00
errorLvl = 1 ;
2021-10-24 00:41:18 +02:00
setColor_RGB ( 255 , 0 , 0 ) ;
2018-10-05 18:33:09 +02:00
display_Update ( ) ;
2022-10-30 03:21:01 +01:00
}
2018-10-05 18:33:09 +02:00
2022-10-30 03:21:01 +01:00
void print_Error ( const __FlashStringHelper * errorMessage ) {
println_Msg ( errorMessage ) ;
_print_Error ( ) ;
2018-10-05 18:33:09 +02:00
}
2022-10-30 03:21:01 +01:00
void print_Error ( byte errorMessage ) {
2022-10-21 15:59:06 +02:00
print_STR ( errorMessage , 1 ) ;
2022-10-30 03:21:01 +01:00
_print_Error ( ) ;
}
2022-10-21 15:59:06 +02:00
2022-10-30 03:21:01 +01:00
void _print_FatalError ( void ) {
2024-03-02 17:26:35 +01:00
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2022-10-30 03:21:01 +01:00
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
resetArduino ( ) ;
2022-10-21 15:59:06 +02:00
}
2022-10-29 03:31:43 +02:00
void print_FatalError ( const __FlashStringHelper * errorMessage ) {
2022-10-30 03:21:01 +01:00
print_Error ( errorMessage ) ;
_print_FatalError ( ) ;
2022-10-29 03:31:43 +02:00
}
2022-10-31 15:41:29 +01:00
void print_FatalError ( byte errorMessage ) {
2022-10-30 03:21:01 +01:00
print_Error ( errorMessage ) ;
_print_FatalError ( ) ;
2022-10-29 03:31:43 +02:00
}
2018-10-05 18:33:09 +02:00
void wait ( ) {
2022-08-03 12:14:32 +02:00
// Switch status LED off
statusLED ( false ) ;
2024-03-02 17:26:35 +01:00
# if defined(ENABLE_LCD)
2021-10-26 11:27:07 +02:00
wait_btn ( ) ;
2024-03-02 17:26:35 +01:00
# elif defined(ENABLE_OLED)
2019-09-05 00:48:39 +02:00
wait_btn ( ) ;
2024-03-02 17:26:35 +01:00
# elif defined(ENABLE_SERIAL)
2019-09-05 00:48:39 +02:00
wait_serial ( ) ;
# endif
2018-10-05 18:33:09 +02:00
}
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2022-06-16 15:15:43 +02:00
// Copies the last part of the current log file to the dump folder
void save_log ( ) {
// Last found position
uint64_t lastPosition = 0 ;
// Go to first line of log
myLog . rewind ( ) ;
// Find location of OSCR string to determine start of current log
char tempStr [ 5 ] ;
while ( myLog . available ( ) ) {
// Read first 4 chars of line
tempStr [ 0 ] = myLog . read ( ) ;
// Check if it's an empty line
if ( tempStr [ 0 ] = = ' \r ' ) {
// skip \n
myLog . read ( ) ;
2022-10-13 09:49:03 +02:00
} else {
2022-06-16 15:15:43 +02:00
// Read more lines
tempStr [ 1 ] = myLog . read ( ) ;
tempStr [ 2 ] = myLog . read ( ) ;
tempStr [ 3 ] = myLog . read ( ) ;
tempStr [ 4 ] = ' \0 ' ;
char str_buf ;
// Skip rest of line
while ( myLog . available ( ) ) {
str_buf = myLog . read ( ) ;
//break out of loop if CRLF is found
2022-10-13 09:49:03 +02:00
if ( str_buf = = ' \r ' ) {
myLog . read ( ) ; //dispose \n because \r\n
2022-06-16 15:15:43 +02:00
break ;
}
}
// If string is OSCR remember position in file and test if it's the lastest log entry
if ( strncmp ( tempStr , " OSCR " , 4 ) = = 0 ) {
// Check if current position is newer as old position
if ( myLog . position ( ) > lastPosition ) {
lastPosition = myLog . position ( ) ;
}
}
}
}
// Go to position of last log entry
myLog . seek ( lastPosition - 16 ) ;
// Copy log from there to dump dir
sd . chdir ( folder ) ;
strcpy ( fileName , romName ) ;
strcat ( fileName , " .txt " ) ;
if ( ! myFile . open ( fileName , O_RDWR | O_CREAT ) ) {
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2022-06-16 15:15:43 +02:00
}
while ( myLog . available ( ) ) {
if ( myLog . available ( ) > = 512 ) {
for ( word i = 0 ; i < 512 ; i + + ) {
sdBuffer [ i ] = myLog . read ( ) ;
}
myFile . write ( sdBuffer , 512 ) ;
2022-10-13 09:49:03 +02:00
} else {
2022-10-22 10:25:37 +02:00
int i = 0 ;
2022-10-22 10:22:37 +02:00
for ( ; i < myLog . available ( ) ; i + + ) {
2022-06-16 15:15:43 +02:00
sdBuffer [ i ] = myLog . read ( ) ;
}
myFile . write ( sdBuffer , i ) ;
}
}
// Close the file:
myFile . close ( ) ;
}
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2022-10-13 09:49:03 +02:00
void println_Log ( const __FlashStringHelper * string ) {
2022-10-08 18:19:31 +02:00
myLog . println ( string ) ;
}
# endif
2022-10-13 09:49:03 +02:00
void print_Msg ( const __FlashStringHelper * string ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2019-09-05 00:48:39 +02:00
display . print ( string ) ;
2021-10-24 00:41:18 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . print ( string ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( string ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
2021-10-26 17:13:42 +02:00
void print_Msg ( const char myString [ ] ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2021-10-26 17:13:42 +02:00
// test for word wrap
if ( ( display . tx + strlen ( myString ) * 6 ) > 128 ) {
2022-10-22 10:25:37 +02:00
unsigned int strPos = 0 ;
2021-10-26 17:13:42 +02:00
// Print until end of display
while ( display . tx < 122 ) {
display . print ( myString [ strPos ] ) ;
strPos + + ;
}
// Newline
display . setCursor ( 0 , display . ty + 8 ) ;
2022-06-12 12:30:52 +02:00
// Print until end of display and ignore remaining characters
while ( ( strPos < strlen ( myString ) ) & & ( display . tx < 122 ) ) {
2021-10-26 17:13:42 +02:00
display . print ( myString [ strPos ] ) ;
strPos + + ;
}
2022-10-13 09:49:03 +02:00
} else {
2021-10-26 17:13:42 +02:00
display . print ( myString ) ;
}
2021-10-24 00:41:18 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2021-10-26 17:13:42 +02:00
Serial . print ( myString ) ;
2019-09-05 00:48:39 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( myString ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void print_Msg ( long unsigned int message ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2019-09-05 00:48:39 +02:00
display . print ( message ) ;
2021-10-24 00:41:18 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . print ( message ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( message ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void print_Msg ( byte message , int outputFormat ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2019-09-05 00:48:39 +02:00
display . print ( message , outputFormat ) ;
2021-10-24 00:41:18 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . print ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( message , outputFormat ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
2022-08-19 11:48:43 +02:00
void print_Msg ( word message , int outputFormat ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-08-19 11:48:43 +02:00
display . print ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2022-08-19 11:48:43 +02:00
Serial . print ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( message , outputFormat ) ;
2022-08-19 11:48:43 +02:00
# endif
}
void print_Msg ( int message , int outputFormat ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-08-19 11:48:43 +02:00
display . print ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2022-08-19 11:48:43 +02:00
Serial . print ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( message , outputFormat ) ;
2022-08-19 11:48:43 +02:00
# endif
}
void print_Msg ( long unsigned int message , int outputFormat ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-08-19 11:48:43 +02:00
display . print ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2022-08-19 11:48:43 +02:00
Serial . print ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( message , outputFormat ) ;
2022-08-19 11:48:43 +02:00
# endif
}
2018-10-05 18:33:09 +02:00
void print_Msg ( String string ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2019-09-05 00:48:39 +02:00
display . print ( string ) ;
2021-10-24 00:41:18 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . print ( string ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . print ( string ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
2020-07-03 15:15:26 +02:00
void print_Msg_PaddedHexByte ( byte message ) {
if ( message < 16 ) print_Msg ( 0 , HEX ) ;
print_Msg ( message , HEX ) ;
}
2020-07-11 17:14:37 +02:00
void print_Msg_PaddedHex16 ( word message ) {
2022-10-13 09:49:03 +02:00
print_Msg_PaddedHexByte ( ( message > > 8 ) & 0xFF ) ;
print_Msg_PaddedHexByte ( ( message > > 0 ) & 0xFF ) ;
2020-07-11 17:14:37 +02:00
}
2020-07-03 18:25:52 +02:00
void print_Msg_PaddedHex32 ( unsigned long message ) {
print_Msg_PaddedHexByte ( ( message > > 24 ) & 0xFF ) ;
print_Msg_PaddedHexByte ( ( message > > 16 ) & 0xFF ) ;
2022-10-13 09:49:03 +02:00
print_Msg_PaddedHexByte ( ( message > > 8 ) & 0xFF ) ;
print_Msg_PaddedHexByte ( ( message > > 0 ) & 0xFF ) ;
2020-07-03 18:25:52 +02:00
}
2018-10-05 18:33:09 +02:00
void println_Msg ( String string ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-06-12 12:30:52 +02:00
display . print ( string ) ;
2021-10-24 00:41:18 +02:00
display . setCursor ( 0 , display . ty + 8 ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . println ( string ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . println ( string ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void println_Msg ( byte message , int outputFormat ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-06-12 12:30:52 +02:00
display . print ( message , outputFormat ) ;
2021-10-24 00:41:18 +02:00
display . setCursor ( 0 , display . ty + 8 ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . println ( message , outputFormat ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . println ( message , outputFormat ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
2022-06-12 12:30:52 +02:00
void println_Msg ( const char myString [ ] ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-06-12 12:30:52 +02:00
// test for word wrap
if ( ( display . tx + strlen ( myString ) * 6 ) > 128 ) {
2022-10-22 10:25:37 +02:00
unsigned int strPos = 0 ;
2022-06-12 12:30:52 +02:00
// Print until end of display
2022-08-03 21:59:11 +02:00
while ( ( display . tx < 122 ) & & ( myString [ strPos ] ! = ' \0 ' ) ) {
2022-06-12 12:30:52 +02:00
display . print ( myString [ strPos ] ) ;
strPos + + ;
}
// Newline
display . setCursor ( 0 , display . ty + 8 ) ;
// Print until end of display and ignore remaining characters
2022-08-03 21:59:11 +02:00
while ( ( strPos < strlen ( myString ) ) & & ( display . tx < 122 ) & & ( myString [ strPos ] ! = ' \0 ' ) ) {
2022-06-12 12:30:52 +02:00
display . print ( myString [ strPos ] ) ;
strPos + + ;
}
2022-10-13 09:49:03 +02:00
} else {
2022-06-12 12:30:52 +02:00
display . print ( myString ) ;
}
2021-10-24 00:41:18 +02:00
display . setCursor ( 0 , display . ty + 8 ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2022-06-12 12:30:52 +02:00
Serial . println ( myString ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . println ( myString ) ;
2019-09-05 00:48:39 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
2022-10-13 09:49:03 +02:00
void println_Msg ( const __FlashStringHelper * string ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-06-12 12:30:52 +02:00
display . print ( string ) ;
2021-10-24 00:41:18 +02:00
display . setCursor ( 0 , display . ty + 8 ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . println ( string ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2022-06-14 15:25:53 +02:00
char myBuffer [ 15 ] ;
2022-10-13 09:49:03 +02:00
strlcpy_P ( myBuffer , ( char * ) string , 15 ) ;
2022-06-14 15:25:53 +02:00
if ( ( strncmp ( myBuffer , " Press Button... " , 14 ) ! = 0 ) & & ( strncmp ( myBuffer , " Select file " , 10 ) ! = 0 ) ) {
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . println ( string ) ;
2022-06-14 15:25:53 +02:00
}
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void println_Msg ( long unsigned int message ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-06-12 12:30:52 +02:00
display . print ( message ) ;
2021-10-24 00:41:18 +02:00
display . setCursor ( 0 , display . ty + 8 ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
Serial . println ( message ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . println ( message ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void display_Update ( ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2021-10-24 00:41:18 +02:00
display . updateDisplay ( ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2019-09-05 00:48:39 +02:00
delay ( 100 ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
2024-02-29 23:52:04 +01:00
if ( ! dont_log & & loggingEnabled ) myLog . flush ( ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void display_Clear ( ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2021-10-24 00:41:18 +02:00
display . clearDisplay ( ) ;
display . setCursor ( 0 , 8 ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
if ( ! dont_log & & loggingEnabled ) myLog . println ( FSTRING_EMPTY ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
2022-10-08 23:07:15 +02:00
void display_Clear_Slow ( ) {
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-10-08 23:07:15 +02:00
display . setDrawColor ( 0 ) ;
for ( byte y = 0 ; y < 64 ; y + + ) {
display . drawLine ( 0 , y , 128 , y ) ;
}
display . setDrawColor ( 1 ) ;
display . setCursor ( 0 , 8 ) ;
# endif
}
2018-10-05 18:33:09 +02:00
/******************************************
2022-10-08 16:23:33 +02:00
RGB LED
2018-10-05 18:33:09 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-10-08 16:23:33 +02:00
void rgbLed ( byte Color ) {
switch ( Color ) {
case blue_color :
setColor_RGB ( 0 , 0 , 255 ) ;
break ;
case red_color :
setColor_RGB ( 255 , 0 , 0 ) ;
break ;
case purple_color :
setColor_RGB ( 255 , 0 , 255 ) ;
break ;
case green_color :
setColor_RGB ( 0 , 255 , 0 ) ;
break ;
case turquoise_color :
setColor_RGB ( 0 , 255 , 255 ) ;
break ;
case yellow_color :
setColor_RGB ( 255 , 255 , 0 ) ;
break ;
case white_color :
setColor_RGB ( 255 , 255 , 255 ) ;
break ;
2022-08-03 19:35:45 +02:00
}
}
2022-10-08 16:23:33 +02:00
void blinkLED ( ) {
2023-03-30 03:05:01 +02:00
# if defined(ENABLE_VSELECT)
2022-11-12 20:13:22 +01:00
// Nothing
# elif defined(HW5)
2022-10-08 16:23:33 +02:00
PORTD ^ = ( 1 < < 7 ) ;
2024-03-02 17:26:35 +01:00
# elif defined(ENABLE_OLED)
2022-10-08 16:23:33 +02:00
PORTB ^ = ( 1 < < 4 ) ;
2024-03-02 17:26:35 +01:00
# elif defined(ENABLE_LCD)
2022-10-08 16:23:33 +02:00
PORTE ^ = ( 1 < < 1 ) ;
2024-03-02 17:26:35 +01:00
# elif defined(ENABLE_SERIAL)
2022-10-08 16:23:33 +02:00
PORTB ^ = ( 1 < < 4 ) ;
PORTB ^ = ( 1 < < 7 ) ;
2022-02-23 15:08:25 +01:00
# endif
2022-10-08 16:23:33 +02:00
}
2018-10-05 18:33:09 +02:00
2023-03-30 03:05:01 +02:00
# if defined(HW5) && !defined(ENABLE_VSELECT)
2022-10-22 10:25:37 +02:00
void statusLED ( boolean on ) {
2022-10-08 16:23:33 +02:00
if ( ! on )
PORTD | = ( 1 < < 7 ) ;
else
PORTD & = ~ ( 1 < < 7 ) ;
2022-11-12 20:13:22 +01:00
}
2022-10-22 10:25:37 +02:00
# else
2022-10-28 15:02:51 +02:00
void statusLED ( boolean on __attribute__ ( ( unused ) ) ) {
2022-10-22 10:25:37 +02:00
}
# endif
2022-10-08 16:23:33 +02:00
/******************************************
Menu system
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-03-02 17:26:35 +01:00
unsigned char question_box ( const __FlashStringHelper * question , char answers [ 7 ] [ 20 ] , uint8_t num_answers , uint8_t default_choice ) {
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2022-10-08 16:23:33 +02:00
return questionBox_Display ( question , answers , num_answers , default_choice ) ;
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SERIAL
2022-10-08 16:23:33 +02:00
return questionBox_Serial ( question , answers , num_answers , default_choice ) ;
# endif
2018-10-05 18:33:09 +02:00
}
2024-03-02 17:26:35 +01:00
# if defined(ENABLE_SERIAL)
2022-10-08 16:23:33 +02:00
// Serial Monitor
2024-03-02 17:26:35 +01:00
byte questionBox_Serial ( const __FlashStringHelper * question , char answers [ 7 ] [ 20 ] , uint8_t num_answers , uint8_t default_choice ) {
2018-10-05 18:33:09 +02:00
// Print menu to serial monitor
2024-03-02 17:30:44 +01:00
Serial . println ( FS ( FSTRING_EMPTY ) ) ;
2018-10-05 18:33:09 +02:00
for ( byte i = 0 ; i < num_answers ; i + + ) {
Serial . print ( i ) ;
Serial . print ( F ( " ) " ) ) ;
Serial . println ( answers [ i ] ) ;
}
// Wait for user input
2024-03-02 17:30:44 +01:00
Serial . println ( FS ( FSTRING_EMPTY ) ) ;
2018-10-05 18:33:09 +02:00
Serial . println ( F ( " Please browse pages with 'u'(up) and 'd'(down) " ) ) ;
Serial . println ( F ( " and enter a selection by typing a number(0-6): _ " ) ) ;
2024-03-02 17:30:44 +01:00
Serial . println ( FS ( FSTRING_EMPTY ) ) ;
2018-10-05 18:33:09 +02:00
while ( Serial . available ( ) = = 0 ) {
}
// Read the incoming byte:
incomingByte = Serial . read ( ) - 48 ;
// Page up (u)
if ( incomingByte = = 69 ) {
2023-07-22 15:21:02 +02:00
if ( currPage > 1 ) {
lastPage = currPage ;
currPage - - ;
} else {
root = 1 ;
}
2018-10-05 18:33:09 +02:00
}
// Page down (d)
else if ( incomingByte = = 52 ) {
2023-07-22 15:21:02 +02:00
if ( numPages > currPage ) {
2018-10-05 18:33:09 +02:00
lastPage = currPage ;
currPage + + ;
}
}
2023-07-22 15:21:02 +02:00
// Execute choice
else if ( ( incomingByte > = 0 ) & & ( incomingByte < 7 ) ) {
2023-07-19 16:32:34 +02:00
numPages = 0 ;
}
2018-10-05 18:33:09 +02:00
// Print the received byte for validation e.g. in case of a different keyboard mapping
//Serial.println(incomingByte);
2024-03-02 17:30:44 +01:00
//Serial.println(FS(FSTRING_EMPTY));
2018-10-05 18:33:09 +02:00
return incomingByte ;
}
2019-09-05 00:48:39 +02:00
# endif
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
// OLED & LCD
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) || defined(ENABLE_OLED))
2021-10-24 00:41:18 +02:00
// Display a question box with selectable answers. Make sure default choice is in (0, num_answers]
2024-03-02 17:26:35 +01:00
unsigned char questionBox_Display ( const __FlashStringHelper * question , char answers [ 7 ] [ 20 ] , uint8_t num_answers , uint8_t default_choice ) {
2021-10-24 00:41:18 +02:00
//clear the screen
display . clearDisplay ( ) ;
display . updateDisplay ( ) ;
display . setCursor ( 0 , 8 ) ;
2021-10-26 19:26:59 +02:00
display . setDrawColor ( 1 ) ;
2021-10-24 00:41:18 +02:00
// change the rgb led to the start menu color
rgbLed ( default_choice ) ;
// print menu
display . println ( question ) ;
display . setCursor ( 0 , display . ty + 8 ) ;
for ( unsigned char i = 0 ; i < num_answers ; i + + ) {
// Add space for the selection dot
2021-10-25 22:06:08 +02:00
display . print ( " " ) ;
2021-10-24 00:41:18 +02:00
// Print menu item
display . println ( answers [ i ] ) ;
display . setCursor ( 0 , display . ty + 8 ) ;
}
display . updateDisplay ( ) ;
// start with the default choice
choice = default_choice ;
// draw selection box
2021-10-25 22:06:08 +02:00
display . drawBox ( 1 , 8 * choice + 11 , 3 , 3 ) ;
2021-10-24 00:41:18 +02:00
display . updateDisplay ( ) ;
unsigned long idleTime = millis ( ) ;
byte currentColor = 0 ;
// wait until user makes his choice
while ( 1 ) {
// Attract Mode
if ( millis ( ) - idleTime > 300000 ) {
if ( ( millis ( ) - idleTime ) % 4000 = = 0 ) {
if ( currentColor < 7 ) {
currentColor + + ;
if ( currentColor = = 1 ) {
2022-10-13 09:49:03 +02:00
currentColor = 2 ; // skip red as that signifies an error to the user
2021-10-24 00:41:18 +02:00
}
2022-10-13 09:49:03 +02:00
} else {
2021-10-24 00:41:18 +02:00
currentColor = 0 ;
}
}
rgbLed ( currentColor ) ;
}
2022-10-13 10:31:17 +02:00
/* Check Button/rotary encoder
1 click / clockwise rotation
2 doubleClick / counter clockwise rotation
3 hold / press
2021-10-24 00:41:18 +02:00
4 longHold */
2024-03-02 17:26:35 +01:00
uint8_t b = checkButton ( ) ;
2021-10-24 00:41:18 +02:00
2022-10-13 10:31:17 +02:00
// if button is pressed twice or rotary encoder turned left/counter clockwise
2021-10-24 00:41:18 +02:00
if ( b = = 2 ) {
idleTime = millis ( ) ;
// remove selection box
display . setDrawColor ( 0 ) ;
2021-10-25 22:06:08 +02:00
display . drawBox ( 1 , 8 * choice + 11 , 3 , 3 ) ;
2021-10-26 19:26:59 +02:00
display . setDrawColor ( 1 ) ;
2021-10-24 00:41:18 +02:00
display . updateDisplay ( ) ;
2022-10-13 10:31:17 +02:00
// If cursor on top list entry
2021-10-27 20:59:57 +02:00
if ( choice = = 0 ) {
2022-10-13 10:31:17 +02:00
// On 2nd, 3rd, ... page go back one page
2021-10-24 00:41:18 +02:00
if ( currPage > 1 ) {
lastPage = currPage ;
currPage - - ;
break ;
2022-10-13 10:31:17 +02:00
}
// In file browser go to root dir
else if ( ( filebrowse = = 1 ) & & ( root ! = 1 ) ) {
2021-10-24 00:41:18 +02:00
root = 1 ;
break ;
}
2022-10-13 10:31:17 +02:00
// Else go to bottom of list as a shortcut
else {
choice = num_answers - 1 ;
}
}
// If not top entry go up/back one entry
else {
2021-10-24 00:41:18 +02:00
choice - - ;
}
// draw selection box
2021-10-25 22:06:08 +02:00
display . drawBox ( 1 , 8 * choice + 11 , 3 , 3 ) ;
2021-10-24 00:41:18 +02:00
display . updateDisplay ( ) ;
// change RGB led to the color of the current menu option
rgbLed ( choice ) ;
}
2021-10-26 19:26:59 +02:00
// go one down in the menu if the Cart Readers button is clicked shortly
2021-10-24 00:41:18 +02:00
if ( b = = 1 ) {
idleTime = millis ( ) ;
// remove selection box
display . setDrawColor ( 0 ) ;
2021-10-25 22:06:08 +02:00
display . drawBox ( 1 , 8 * choice + 11 , 3 , 3 ) ;
2021-10-26 19:26:59 +02:00
display . setDrawColor ( 1 ) ;
2021-10-24 00:41:18 +02:00
display . updateDisplay ( ) ;
2022-10-13 09:49:03 +02:00
if ( ( choice = = num_answers - 1 ) & & ( numPages > currPage ) ) {
2021-10-24 00:41:18 +02:00
lastPage = currPage ;
currPage + + ;
break ;
2022-10-13 09:49:03 +02:00
} else
2021-10-24 00:41:18 +02:00
choice = ( choice + 1 ) % num_answers ;
// draw selection box
2021-10-25 22:06:08 +02:00
display . drawBox ( 1 , 8 * choice + 11 , 3 , 3 ) ;
2021-10-24 00:41:18 +02:00
display . updateDisplay ( ) ;
// change RGB led to the color of the current menu option
rgbLed ( choice ) ;
}
2022-10-13 09:49:03 +02:00
// if the Cart Readers button is hold continiously leave the menu
2021-10-24 00:41:18 +02:00
// so the currently highlighted action can be executed
if ( b = = 3 ) {
idleTime = millis ( ) ;
2021-10-27 20:59:57 +02:00
// All done
numPages = 0 ;
2021-10-24 00:41:18 +02:00
break ;
}
2023-03-30 03:05:01 +02:00
checkUpdater ( ) ;
2021-10-24 00:41:18 +02:00
}
// pass on user choice
setColor_RGB ( 0 , 0 , 0 ) ;
2022-06-14 15:25:53 +02:00
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GLOBAL_LOG
println_Msg ( FS ( FSTRING_EMPTY ) ) ;
2022-06-16 17:17:16 +02:00
print_Msg ( F ( " [+] " ) ) ;
2022-06-14 15:25:53 +02:00
println_Msg ( answers [ choice ] ) ;
# endif
2021-10-24 00:41:18 +02:00
return choice ;
}
# endif
2024-03-02 17:26:35 +01:00
# if !defined(ENABLE_SERIAL) && defined(ENABLE_UPDATER)
2023-03-30 03:05:01 +02:00
void checkUpdater ( ) {
if ( ClockedSerial . available ( ) > 0 ) {
String cmd = ClockedSerial . readStringUntil ( ' \n ' ) ;
cmd . trim ( ) ;
2024-02-29 23:52:04 +01:00
if ( cmd = = " VERCHK " )
{ // VERCHK: Gets OSCR version and features
2023-03-30 03:05:01 +02:00
delay ( 500 ) ;
printVersionToSerial ( ) ;
2024-02-29 23:52:04 +01:00
}
else if ( cmd = = " GETCLOCK " )
{ // GETCLOCK: Gets the MEGA's current clock speed.
# if defined(ENABLE_3V3FIX)
2023-03-30 03:05:01 +02:00
ClockedSerial . print ( F ( " Clock is running at " ) ) ;
ClockedSerial . print ( ( clock = = CS_16MHZ ) ? 16UL : 8UL ) ;
ClockedSerial . println ( F ( " MHz " ) ) ;
2024-02-29 23:52:04 +01:00
# else /* !ENABLE_3V3FIX */
2024-03-02 17:26:35 +01:00
ClockedSerial . println ( FS ( FSTRING_MODULE_NOT_ENABLED ) ) ;
2024-02-29 23:52:04 +01:00
# endif /* ENABLE_3V3FIX */
}
else if ( cmd . substring ( 1 , 8 ) = = " ETVOLTS " )
{ // (G/S)ETVOLTS: Get and set the voltage.
# if defined(ENABLE_VSELECT)
if ( cmd ! = " GETVOLTS " ) {
switch ( cmd . substring ( 9 , 10 ) . toInt ( ) ) {
case 3 : setVoltage ( VOLTS_SET_3V3 ) ; break ;
case 5 : setVoltage ( VOLTS_SET_5V ) ; break ;
}
}
2023-03-30 03:05:01 +02:00
ClockedSerial . print ( F ( " Voltage is set to " ) ) ;
ClockedSerial . print ( ( voltage = = VOLTS_SET_5V ) ? 5 : 3.3 ) ;
ClockedSerial . println ( F ( " V " ) ) ;
2024-02-29 23:52:04 +01:00
# else /* !ENABLE_VSELECT */
2024-03-02 17:26:35 +01:00
ClockedSerial . println ( FS ( FSTRING_MODULE_NOT_ENABLED ) ) ;
2024-02-29 23:52:04 +01:00
# endif /* ENABLE_VSELECT */
}
// RTC commands
else if ( cmd . substring ( 1 , 7 ) = = " ETTIME " )
{ // (G/S)ETTIME: Get and set the date/time.
2024-03-02 17:26:35 +01:00
# if defined(ENABLE_RTC)
2024-02-29 23:52:04 +01:00
if ( cmd ! = " GETTIME " ) {
ClockedSerial . println ( F ( " Setting Time... " ) ) ;
rtc . adjust ( DateTime ( cmd . substring ( 8 ) . toInt ( ) ) ) ;
}
2023-07-16 07:01:58 +02:00
ClockedSerial . print ( F ( " Current Time: " ) ) ;
ClockedSerial . println ( RTCStamp ( ) ) ;
2024-03-02 17:26:35 +01:00
# else /* !ENABLE_RTC */
ClockedSerial . println ( FS ( FSTRING_MODULE_NOT_ENABLED ) ) ;
# endif /* ENABLE_RTC */
2023-03-30 03:05:01 +02:00
} else {
2024-03-02 17:26:35 +01:00
ClockedSerial . print ( FS ( FSTRING_OSCR ) ) ;
ClockedSerial . println ( F ( " : Unknown Command " ) ) ;
2023-03-30 03:05:01 +02:00
}
}
}
# else
void checkUpdater ( ) { }
# endif
2018-10-05 18:33:09 +02:00
/******************************************
2022-10-08 16:23:33 +02:00
User Control
2018-10-05 18:33:09 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-10-08 16:23:33 +02:00
// Using Serial Monitor
2024-03-02 17:26:35 +01:00
# if defined(ENABLE_SERIAL)
uint8_t checkButton ( ) {
2022-10-08 16:23:33 +02:00
while ( Serial . available ( ) = = 0 ) {
}
incomingByte = Serial . read ( ) - 48 ;
//Next
if ( incomingByte = = 52 ) {
return 1 ;
}
//Previous
else if ( incomingByte = = 69 ) {
return 2 ;
}
//Selection
else if ( incomingByte = = 240 ) {
return 3 ;
}
2022-11-05 02:22:57 +01:00
return 0 ;
2022-10-08 16:23:33 +02:00
}
void wait_serial ( ) {
if ( errorLvl ) {
errorLvl = 0 ;
}
while ( Serial . available ( ) = = 0 ) {
}
incomingByte = Serial . read ( ) - 48 ;
/* if ((incomingByte == 53) && (fileName[0] != '\0')) {
// Open file on sd card
sd . chdir ( folder ) ;
if ( myFile . open ( fileName , O_READ ) ) {
// Get rom size from file
fileSize = myFile . fileSize ( ) ;
// Send filesize
char tempStr [ 16 ] ;
sprintf ( tempStr , " %d " , fileSize ) ;
Serial . write ( tempStr ) ;
// Wait for ok
while ( Serial . available ( ) = = 0 ) {
}
// Send file
for ( unsigned long currByte = 0 ; currByte < fileSize ; currByte + + ) {
// Blink led
if ( currByte % 1024 = = 0 )
blinkLED ( ) ;
Serial . write ( myFile . read ( ) ) ;
}
// Close the file:
myFile . close ( ) ;
}
else {
2022-10-30 03:21:01 +01:00
print_FatalError ( open_file_STR ) ;
2022-10-08 16:23:33 +02:00
}
} */
}
# endif
// Using one or two push buttons (HW1/HW2/HW3)
2024-03-02 17:26:35 +01:00
# if defined(ENABLE_OLED)
2018-10-05 18:33:09 +02:00
// Read button state
2024-03-02 17:26:35 +01:00
uint8_t checkButton ( ) {
# ifdef ENABLE_BUTTON2
2022-11-01 22:26:53 +01:00
byte eventButton2 = checkButton2 ( ) ;
if ( ( eventButton2 > 0 ) & & ( eventButton2 < 2 ) )
2018-10-05 18:33:09 +02:00
return 3 ;
2022-11-01 22:26:53 +01:00
else if ( eventButton2 > 2 )
return 4 ;
2018-10-05 18:33:09 +02:00
# endif
2022-11-05 06:04:14 +01:00
return ( checkButton1 ( ) ) ;
2018-10-05 18:33:09 +02:00
}
// Read button 1
2024-03-02 17:26:35 +01:00
uint8_t checkButton1 ( ) {
uint8_t event = 0 ;
2019-09-05 00:48:39 +02:00
2018-10-05 18:33:09 +02:00
// Read the state of the button (PD7)
buttonVal1 = ( PIND & ( 1 < < 7 ) ) ;
// Button pressed down
if ( buttonVal1 = = LOW & & buttonLast1 = = HIGH & & ( millis ( ) - upTime1 ) > debounce ) {
downTime1 = millis ( ) ;
ignoreUp1 = false ;
waitForUp1 = false ;
singleOK1 = true ;
holdEventPast1 = false ;
longholdEventPast1 = false ;
if ( ( millis ( ) - upTime1 ) < DCgap & & DConUp1 = = false & & DCwaiting1 = = true ) DConUp1 = true ;
else DConUp1 = false ;
DCwaiting1 = false ;
}
// Button released
else if ( buttonVal1 = = HIGH & & buttonLast1 = = LOW & & ( millis ( ) - downTime1 ) > debounce ) {
2019-09-13 18:13:37 +02:00
if ( ! ignoreUp1 ) {
2018-10-05 18:33:09 +02:00
upTime1 = millis ( ) ;
if ( DConUp1 = = false ) DCwaiting1 = true ;
else {
event = 2 ;
DConUp1 = false ;
DCwaiting1 = false ;
singleOK1 = false ;
}
}
}
// Test for normal click event: DCgap expired
2022-10-13 09:49:03 +02:00
if ( buttonVal1 = = HIGH & & ( millis ( ) - upTime1 ) > = DCgap & & DCwaiting1 = = true & & DConUp1 = = false & & singleOK1 = = true ) {
2018-10-05 18:33:09 +02:00
event = 1 ;
DCwaiting1 = false ;
}
// Test for hold
if ( buttonVal1 = = LOW & & ( millis ( ) - downTime1 ) > = holdTime ) {
// Trigger "normal" hold
2019-09-13 18:13:37 +02:00
if ( ! holdEventPast1 ) {
2018-10-05 18:33:09 +02:00
event = 3 ;
waitForUp1 = true ;
ignoreUp1 = true ;
DConUp1 = false ;
DCwaiting1 = false ;
//downTime1 = millis();
holdEventPast1 = true ;
}
// Trigger "long" hold
if ( ( millis ( ) - downTime1 ) > = longHoldTime ) {
2019-09-13 18:13:37 +02:00
if ( ! longholdEventPast1 ) {
2018-10-05 18:33:09 +02:00
event = 4 ;
longholdEventPast1 = true ;
}
}
}
buttonLast1 = buttonVal1 ;
return event ;
}
// Read button 2
2024-03-02 17:26:35 +01:00
uint8_t checkButton2 ( ) {
uint8_t event = 0 ;
2019-09-05 00:48:39 +02:00
2022-11-01 22:26:53 +01:00
// Read the state of the button (PG2)
2018-10-05 18:33:09 +02:00
buttonVal2 = ( PING & ( 1 < < 2 ) ) ;
// Button pressed down
if ( buttonVal2 = = LOW & & buttonLast2 = = HIGH & & ( millis ( ) - upTime2 ) > debounce ) {
downTime2 = millis ( ) ;
ignoreUp2 = false ;
waitForUp2 = false ;
singleOK2 = true ;
holdEventPast2 = false ;
longholdEventPast2 = false ;
if ( ( millis ( ) - upTime2 ) < DCgap & & DConUp2 = = false & & DCwaiting2 = = true ) DConUp2 = true ;
else DConUp2 = false ;
DCwaiting2 = false ;
}
// Button released
else if ( buttonVal2 = = HIGH & & buttonLast2 = = LOW & & ( millis ( ) - downTime2 ) > debounce ) {
2019-09-13 18:13:37 +02:00
if ( ! ignoreUp2 ) {
2018-10-05 18:33:09 +02:00
upTime2 = millis ( ) ;
if ( DConUp2 = = false ) DCwaiting2 = true ;
else {
event = 2 ;
DConUp2 = false ;
DCwaiting2 = false ;
singleOK2 = false ;
}
}
}
// Test for normal click event: DCgap expired
2022-10-13 09:49:03 +02:00
if ( buttonVal2 = = HIGH & & ( millis ( ) - upTime2 ) > = DCgap & & DCwaiting2 = = true & & DConUp2 = = false & & singleOK2 = = true ) {
2018-10-05 18:33:09 +02:00
event = 1 ;
DCwaiting2 = false ;
}
// Test for hold
if ( buttonVal2 = = LOW & & ( millis ( ) - downTime2 ) > = holdTime ) {
// Trigger "normal" hold
2019-09-13 18:13:37 +02:00
if ( ! holdEventPast2 ) {
2018-10-05 18:33:09 +02:00
event = 3 ;
waitForUp2 = true ;
ignoreUp2 = true ;
DConUp2 = false ;
DCwaiting2 = false ;
//downTime2 = millis();
holdEventPast2 = true ;
}
// Trigger "long" hold
if ( ( millis ( ) - downTime2 ) > = longHoldTime ) {
2019-09-13 18:13:37 +02:00
if ( ! longholdEventPast2 ) {
2018-10-05 18:33:09 +02:00
event = 4 ;
longholdEventPast2 = true ;
}
}
}
buttonLast2 = buttonVal2 ;
return event ;
}
// Wait for user to push button
void wait_btn ( ) {
// Change led to green
if ( errorLvl = = 0 )
rgbLed ( green_color ) ;
2022-10-13 09:49:03 +02:00
while ( 1 ) {
2018-10-05 18:33:09 +02:00
// get input button
2024-03-02 17:26:35 +01:00
uint8_t b = checkButton ( ) ;
2018-10-05 18:33:09 +02:00
// if the cart readers input button is pressed shortly
if ( b = = 1 ) {
errorLvl = 0 ;
break ;
}
// if the cart readers input button is pressed long
if ( b = = 3 ) {
if ( errorLvl ) {
errorLvl = 0 ;
}
break ;
}
2023-03-30 03:05:01 +02:00
checkUpdater ( ) ;
2018-10-05 18:33:09 +02:00
}
}
2022-10-08 16:23:33 +02:00
# endif
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
// Using rotary encoder (HW4/HW5)
2024-03-02 17:26:35 +01:00
# if (defined(ENABLE_LCD) && defined(ENABLE_ROTARY))
2022-10-08 16:23:33 +02:00
// Read encoder state
2024-03-02 17:26:35 +01:00
uint8_t checkButton ( ) {
2022-10-08 16:23:33 +02:00
// Read rotary encoder
encoder . tick ( ) ;
int newPos = encoder . getPosition ( ) ;
// Read button
boolean reading = ( PING & ( 1 < < PING2 ) ) > > PING2 ;
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
// Check if rotary encoder has changed
if ( rotaryPos ! = newPos ) {
int rotaryDir = ( int ) encoder . getDirection ( ) ;
2022-11-05 06:04:14 +01:00
rotaryPos = newPos ;
2022-10-08 16:23:33 +02:00
if ( rotaryDir = = 1 ) {
return 1 ;
2022-10-13 09:49:03 +02:00
} else if ( rotaryDir = = - 1 ) {
2022-10-08 16:23:33 +02:00
return 2 ;
}
2022-11-05 06:04:14 +01:00
} else if ( reading ! = buttonState ) {
2022-10-08 16:23:33 +02:00
if ( reading ! = lastButtonState ) {
lastDebounceTime = millis ( ) ;
2022-11-05 06:04:14 +01:00
lastButtonState = reading ;
} else if ( ( millis ( ) - lastDebounceTime ) > debounceDelay ) {
buttonState = reading ;
// Button was pressed down
if ( buttonState = = 0 ) {
setColor_RGB ( 0 , 0 , 0 ) ;
unsigned long pushTime = millis ( ) ;
// Wait until button was let go again
while ( ( PING & ( 1 < < PING2 ) ) > > PING2 = = 0 ) {
// Signal long press delay reached
2022-11-01 22:26:53 +01:00
if ( ( millis ( ) - pushTime ) > 2000 ) {
2022-11-05 06:04:14 +01:00
rgbLed ( green_color ) ;
2022-10-08 16:23:33 +02:00
}
2018-10-05 18:33:09 +02:00
}
2022-11-05 06:04:14 +01:00
// 2 second long press
if ( ( millis ( ) - pushTime ) > 2000 ) {
return 4 ;
}
// normal press
else {
return 3 ;
}
2018-10-05 18:33:09 +02:00
}
2022-10-08 16:23:33 +02:00
}
}
2022-11-05 06:04:14 +01:00
return 0 ;
2022-10-08 16:23:33 +02:00
}
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
// Wait for user to push button
void wait_btn ( ) {
// Change led to green
if ( errorLvl = = 0 )
rgbLed ( green_color ) ;
2018-10-05 18:33:09 +02:00
2022-10-13 09:49:03 +02:00
while ( 1 ) {
2022-10-08 16:23:33 +02:00
// get input button
2024-03-02 17:26:35 +01:00
uint8_t b = checkButton ( ) ;
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
// if the cart readers input button is pressed shortly
2018-10-05 18:33:09 +02:00
if ( b = = 1 ) {
2022-10-08 16:23:33 +02:00
errorLvl = 0 ;
break ;
}
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
// if the cart readers input button is pressed long
if ( b = = 3 ) {
if ( errorLvl ) {
errorLvl = 0 ;
2018-10-05 18:33:09 +02:00
}
2022-10-08 16:23:33 +02:00
break ;
}
2023-03-30 03:05:01 +02:00
checkUpdater ( ) ;
2022-10-08 16:23:33 +02:00
}
}
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
// Wait for user to rotate knob
void wait_encoder ( ) {
// Change led to green
if ( errorLvl = = 0 )
rgbLed ( green_color ) ;
2018-10-05 18:33:09 +02:00
2022-10-13 09:49:03 +02:00
while ( 1 ) {
2022-10-08 16:23:33 +02:00
// Get rotary encoder
encoder . tick ( ) ;
int newPos = encoder . getPosition ( ) ;
2018-10-05 18:33:09 +02:00
2022-10-08 16:23:33 +02:00
if ( rotaryPos ! = newPos ) {
rotaryPos = newPos ;
errorLvl = 0 ;
2018-10-05 18:33:09 +02:00
break ;
}
}
}
2019-09-05 00:48:39 +02:00
# endif
2018-10-05 18:33:09 +02:00
/******************************************
Filebrowser Module
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-10-13 09:49:03 +02:00
void fileBrowser ( const __FlashStringHelper * browserTitle ) {
2021-11-29 13:10:04 +01:00
char fileNames [ 7 ] [ FILENAME_LENGTH ] ;
2018-10-05 18:33:09 +02:00
int currFile ;
2022-10-22 05:01:50 +02:00
FsFile myDir ;
2022-10-22 10:18:46 +02:00
div_t page_layout ;
2022-10-22 05:01:50 +02:00
2018-10-05 18:33:09 +02:00
filebrowse = 1 ;
2021-04-26 10:14:37 +02:00
// Root
filePath [ 0 ] = ' / ' ;
filePath [ 1 ] = ' \0 ' ;
2018-10-05 18:33:09 +02:00
// Temporary char array for filename
2019-10-07 05:34:22 +02:00
char nameStr [ FILENAME_LENGTH ] ;
2018-10-05 18:33:09 +02:00
browserstart :
// Print title
println_Msg ( browserTitle ) ;
// Set currFile back to 0
currFile = 0 ;
currPage = 1 ;
lastPage = 1 ;
2021-04-26 10:14:37 +02:00
// Open filepath directory
if ( ! myDir . open ( filePath ) ) {
display_Clear ( ) ;
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2021-04-26 10:14:37 +02:00
}
2021-11-29 13:10:04 +01:00
// Count files in directory
while ( myFile . openNext ( & myDir , O_READ ) ) {
2022-10-22 10:18:46 +02:00
if ( ! myFile . isHidden ( ) & & ( myFile . isDir ( ) | | myFile . isFile ( ) ) ) {
2018-10-05 18:33:09 +02:00
currFile + + ;
}
myFile . close ( ) ;
}
2021-04-26 10:14:37 +02:00
myDir . close ( ) ;
2018-10-05 18:33:09 +02:00
2022-10-22 10:18:46 +02:00
page_layout = div ( currFile , 7 ) ;
numPages = page_layout . quot + 1 ;
2018-10-05 18:33:09 +02:00
// Fill the array "answers" with 7 options to choose from in the file browser
char answers [ 7 ] [ 20 ] ;
page :
// If there are less than 7 entries, set count to that number so no empty options appear
2022-10-22 10:18:46 +02:00
byte count = currPage = = numPages ? page_layout . rem : 7 ;
2018-10-05 18:33:09 +02:00
2021-11-29 13:10:04 +01:00
// Open filepath directory
if ( ! myDir . open ( filePath ) ) {
display_Clear ( ) ;
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2021-11-29 13:10:04 +01:00
}
int countFile = 0 ;
byte i = 0 ;
// Cycle through all files
while ( ( myFile . openNext ( & myDir , O_READ ) ) & & ( i < 8 ) ) {
// Get name of file
myFile . getName ( nameStr , FILENAME_LENGTH ) ;
// Ignore if hidden
if ( myFile . isHidden ( ) ) {
}
// Directory
else if ( myFile . isDir ( ) ) {
if ( countFile = = ( ( currPage - 1 ) * 7 + i ) ) {
snprintf ( fileNames [ i ] , FILENAME_LENGTH , " %s%s " , " / " , nameStr ) ;
i + + ;
}
countFile + + ;
}
// File
else if ( myFile . isFile ( ) ) {
if ( countFile = = ( ( currPage - 1 ) * 7 + i ) ) {
snprintf ( fileNames [ i ] , FILENAME_LENGTH , " %s " , nameStr ) ;
i + + ;
}
countFile + + ;
}
myFile . close ( ) ;
}
myDir . close ( ) ;
2022-10-13 09:49:03 +02:00
for ( byte i = 0 ; i < 8 ; i + + ) {
2018-10-05 18:33:09 +02:00
// Copy short string into fileOptions
2022-10-13 09:49:03 +02:00
snprintf ( answers [ i ] , FILEOPTS_LENGTH , " %s " , fileNames [ i ] ) ;
2018-10-05 18:33:09 +02:00
}
// Create menu with title and 1-7 options to choose from
unsigned char answer = question_box ( browserTitle , answers , count , 0 ) ;
// Check if the page has been switched
if ( currPage ! = lastPage ) {
lastPage = currPage ;
goto page ;
}
// Check if we are supposed to go back to the root dir
if ( root ) {
// Change working dir to root
2021-04-26 10:14:37 +02:00
filePath [ 0 ] = ' / ' ;
filePath [ 1 ] = ' \0 ' ;
2018-10-05 18:33:09 +02:00
sd . chdir ( " / " ) ;
// Start again
root = 0 ;
goto browserstart ;
}
// wait for user choice to come back from the question box menu
2022-10-13 09:49:03 +02:00
switch ( answer ) {
2018-10-05 18:33:09 +02:00
case 0 :
2021-11-29 13:10:04 +01:00
strncpy ( fileName , fileNames [ 0 ] , FILENAME_LENGTH - 1 ) ;
2018-10-05 18:33:09 +02:00
break ;
case 1 :
2021-11-29 13:10:04 +01:00
strncpy ( fileName , fileNames [ 1 ] , FILENAME_LENGTH - 1 ) ;
2018-10-05 18:33:09 +02:00
break ;
case 2 :
2021-11-29 13:10:04 +01:00
strncpy ( fileName , fileNames [ 2 ] , FILENAME_LENGTH - 1 ) ;
2018-10-05 18:33:09 +02:00
break ;
case 3 :
2021-11-29 13:10:04 +01:00
strncpy ( fileName , fileNames [ 3 ] , FILENAME_LENGTH - 1 ) ;
2018-10-05 18:33:09 +02:00
break ;
case 4 :
2021-11-29 13:10:04 +01:00
strncpy ( fileName , fileNames [ 4 ] , FILENAME_LENGTH - 1 ) ;
2018-10-05 18:33:09 +02:00
break ;
case 5 :
2021-11-29 13:10:04 +01:00
strncpy ( fileName , fileNames [ 5 ] , FILENAME_LENGTH - 1 ) ;
2018-10-05 18:33:09 +02:00
break ;
case 6 :
2021-11-29 13:10:04 +01:00
strncpy ( fileName , fileNames [ 6 ] , FILENAME_LENGTH - 1 ) ;
2018-10-05 18:33:09 +02:00
break ;
2021-10-26 19:26:59 +02:00
//case 7:
2018-10-05 18:33:09 +02:00
// File import
2021-10-26 19:26:59 +02:00
//break;
2018-10-05 18:33:09 +02:00
}
// Add directory to our filepath if we just entered a new directory
if ( fileName [ 0 ] = = ' / ' ) {
// add dirname to path
strcat ( filePath , fileName ) ;
// Remove / from dir name
char * dirName = fileName + 1 ;
// Change working dir
sd . chdir ( dirName ) ;
// Start browser in new directory again
goto browserstart ;
2022-10-13 09:49:03 +02:00
} else {
2018-10-05 18:33:09 +02:00
// Afer everything is done change SD working directory back to root
sd . chdir ( " / " ) ;
}
filebrowse = 0 ;
}
/******************************************
Main loop
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void loop ( ) {
2024-03-02 17:26:35 +01:00
switch ( mode ) {
# ifdef ENABLE_N64
case CORE_N64_CART : return n64CartMenu ( ) ;
case CORE_N64_CONTROLLER : return n64ControllerMenu ( ) ;
# endif
# ifdef ENABLE_SNES
case CORE_SNES : return snesMenu ( ) ;
# endif
# ifdef ENABLE_SFM
case CORE_SFM : return sfmMenu ( ) ;
# ifdef ENABLE_FLASH
case CORE_SFM_FLASH : return sfmFlashMenu ( ) ;
# endif
case CORE_SFM_GAME : return sfmGameOptions ( ) ;
# endif
# ifdef ENABLE_GBX
case CORE_GB : return gbMenu ( ) ;
case CORE_GBA : return gbaMenu ( ) ;
case CORE_GBM : return gbmMenu ( ) ;
case CORE_GB_GBSMART : return gbSmartMenu ( ) ;
case CORE_GB_GBSMART_FLASH : return gbSmartFlashMenu ( ) ;
case CORE_GB_GBSMART_GAME : return gbSmartGameOptions ( ) ;
# endif
# ifdef ENABLE_FLASH
case CORE_FLASH8 : return flashromMenu8 ( ) ;
# ifdef ENABLE_FLASH16
case CORE_FLASH16 : return flashromMenu16 ( ) ;
case CORE_EPROM : return epromMenu ( ) ;
# endif
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_MD
case CORE_MD_CART : return mdCartMenu ( ) ;
case CORE_SEGA_CD : return segaCDMenu ( ) ;
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_PCE
case CORE_PCE : return pceMenu ( ) ;
2023-11-25 00:34:13 +01:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SV
case CORE_SV : return svMenu ( ) ;
2024-02-09 22:06:44 +01:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NES
case CORE_NES : return nesMenu ( ) ;
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SMS
case CORE_SMS : return smsMenu ( ) ;
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_WS
case CORE_WS : return wsMenu ( ) ;
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_NGP
case CORE_NGP : return ngpMenu ( ) ;
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_INTV
case CORE_INTV : return intvMenu ( ) ;
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_COLV
case CORE_COL : return colMenu ( ) ;
2022-07-23 11:04:17 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_VBOY
case CORE_VBOY : return vboyMenu ( ) ;
2022-07-23 11:04:17 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_WSV
case CORE_WSV : return wsvMenu ( ) ;
2022-07-23 11:04:17 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_PCW
case CORE_PCW : return pcwMenu ( ) ;
2022-08-21 12:28:47 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ODY2
case CORE_ODY2 : return ody2Menu ( ) ;
2022-09-25 10:36:28 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ARC
case CORE_ARC : return arcMenu ( ) ;
2023-01-03 19:33:34 +01:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_FAIRCHILD
case CORE_FAIRCHILD : return fairchildMenu ( ) ;
2023-01-03 19:33:34 +01:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_SUPRACAN
case CORE_SUPRACAN : return suprAcanMenu ( ) ;
2023-01-03 19:33:34 +01:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_MSX
case CORE_MSX : return msxMenu ( ) ;
2023-01-24 09:34:54 +01:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_POKE
case CORE_POKE : return pokeMenu ( ) ;
2023-06-25 14:09:17 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_LOOPY
case CORE_LOOPY : return loopyMenu ( ) ;
2023-06-25 14:09:17 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_C64
case CORE_C64 : return c64Menu ( ) ;
2020-07-04 14:02:34 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_2600
case CORE_2600 : return a2600Menu ( ) ;
2023-07-06 22:55:45 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_5200
case CORE_5200 : return a5200Menu ( ) ;
2023-09-23 00:11:08 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_7800
case CORE_7800 : return a7800Menu ( ) ;
2023-12-26 23:13:51 +01:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_VECTREX
case CORE_VECTREX : return vectrexMenu ( ) ;
2023-09-23 00:11:08 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_ST
case CORE_ST : return stMenu ( ) ;
2023-09-23 00:11:08 +02:00
# endif
2024-03-02 17:26:35 +01:00
# ifdef ENABLE_GPC
case CORE_GPC : return gpcMenu ( ) ;
2023-09-23 00:11:08 +02:00
# endif
2024-03-02 17:26:35 +01:00
case CORE_MAX : return resetArduino ( ) ;
2018-10-05 18:33:09 +02:00
}
}
//******************************************
// End of File
2023-03-07 01:18:02 +01:00
//******************************************