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 .
2023-11-21 09:54:54 +01:00
Date : 2023 - 11 - 21
Version : 13.1
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
# include "SdFat.h"
SdFs sd ;
FsFile myFile ;
2022-06-12 12:30:52 +02:00
# ifdef global_log
FsFile myLog ;
2022-10-04 20:30:53 +02:00
boolean 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
# ifdef enable_LCD
# 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
# ifdef enable_rotary
# 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
# ifdef enable_neopixel
// 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
# 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
# ifdef RTC_installed
2022-07-08 11:36:03 +02:00
# define _RTC_H
# include "RTClib.h"
# endif
// Clockgen Calibration
# ifdef clockgen_calibration
# 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
static const char string_press_button0 [ ] PROGMEM = " Press Button... " ;
static const char string_sd_error1 [ ] PROGMEM = " SD Error " ;
static const char string_reset2 [ ] PROGMEM = " Reset " ;
static const char string_did_not_verify3 [ ] PROGMEM = " did not verify " ;
static const char string_bytes4 [ ] PROGMEM = " bytes " ;
static const char string_error5 [ ] PROGMEM = " Error: " ;
static const char string_create_file6 [ ] PROGMEM = " Can't create file " ;
static const char string_open_file7 [ ] PROGMEM = " Can't open file " ;
static const char string_file_too_big8 [ ] PROGMEM = " File too big " ;
static const char string_done9 [ ] PROGMEM = " Done " ;
static const char string_saving_to10 [ ] PROGMEM = " Saving to " ;
static const char string_verifying11 [ ] PROGMEM = " Verifying... " ;
static const char string_flashing_file12 [ ] PROGMEM = " Flashing file " ;
static const char string_press_to_change13 [ ] PROGMEM = " Press left to Change " ;
static const char string_right_to_select14 [ ] PROGMEM = " and right to Select " ;
static const char string_rotate_to_change15 [ ] PROGMEM = " Rotate to Change " ;
static const char string_press_to_select16 [ ] PROGMEM = " Press to Select " ;
static const char * const string_table [ ] PROGMEM = { string_press_button0 , string_sd_error1 , string_reset2 , 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 } ;
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Mode menu
# define mode_N64_Cart 0
# define mode_N64_Controller 1
# define mode_SNES 2
# define mode_SFM 3
# define mode_SFM_Flash 4
# define mode_SFM_Game 5
# define mode_GB 6
# define mode_FLASH8 7
# define mode_FLASH16 8
# define mode_GBA 9
# define mode_GBM 10
2019-09-01 14:36:53 +02:00
# define mode_MD_Cart 11
2018-10-05 18:33:09 +02:00
# define mode_EPROM 12
# define mode_PCE 13
2018-10-22 20:29:49 +02:00
# define mode_SV 14
2019-09-01 14:36:53 +02:00
# define mode_NES 15
# define mode_SMS 16
# define mode_SEGA_CD 17
2019-09-26 07:38:03 +02:00
# define mode_GB_GBSmart 18
# define mode_GB_GBSmart_Flash 19
# define mode_GB_GBSmart_Game 20
2019-10-11 15:15:59 +02:00
# define mode_WS 21
2020-05-12 13:52:03 +02:00
# define mode_NGP 22
2022-07-23 11:04:17 +02:00
# define mode_INTV 23
# define mode_COL 24
# define mode_VBOY 25
2022-08-21 12:28:47 +02:00
# define mode_WSV 26
2022-09-25 10:36:28 +02:00
# define mode_PCW 27
2023-12-26 23:13:51 +01:00
# define mode_2600 28
2023-01-03 19:33:34 +01:00
# define mode_ODY2 29
# define mode_ARC 30
# define mode_FAIRCHILD 31
2023-02-23 06:48:11 +01:00
# define mode_SUPRACAN 32
2023-06-25 14:09:17 +02:00
# define mode_MSX 33
# define mode_POKE 34
2023-07-03 01:08:09 +02:00
# define mode_LOOPY 35
2023-09-23 00:11:08 +02:00
# define mode_C64 36
# define mode_5200 37
# define mode_7800 38
# define mode_VECTREX 39
2023-11-25 00:34:13 +01:00
# define mode_ST 40
2024-02-09 22:06:44 +01:00
# define mode_GPC 41
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-10-24 00:41:18 +02:00
# ifdef enable_rotary
// 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
2019-09-05 00:48:39 +02:00
# ifdef enable_OLED
// 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
# 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
2023-03-25 08:51:02 +01:00
byte mode = 0xFF ;
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
static const 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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2023-12-26 23:13:51 +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
2022-07-07 23:20:45 +02:00
# ifdef enable_NES
2022-08-03 12:14:32 +02:00
if ( ( mode = = mode_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
}
2022-10-28 15:02:51 +02: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
# ifdef enable_NES
2022-08-03 12:14:32 +02:00
if ( ( mode = = mode_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 ( ) ;
}
2022-10-28 15:02:51 +02: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
2022-08-06 14:50:40 +02:00
byte starting_letter ( ) {
2022-10-04 20:30:53 +02:00
# ifdef global_log
// Disable log to prevent unnecessary logging
dont_log = true ;
# endif
2022-08-06 14:50:40 +02:00
# if (defined(enable_LCD) || defined(enable_OLED))
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] " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " [G] [H] [ I ] [J] [K] [L] [M] " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " [N] [O] [P] [Q] [R] [S] [T] " ) ) ;
println_Msg ( F ( " " ) ) ;
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 ) {
int 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 - - ;
}
display . setDrawColor ( 0 ) ;
2022-10-07 10:15:18 +02:00
display . drawLine ( 0 , 10 + 0 * 16 , 128 , 10 + 0 * 16 ) ;
display . drawLine ( 0 , 10 + 1 * 16 , 128 , 10 + 1 * 16 ) ;
display . drawLine ( 0 , 10 + 2 * 16 , 128 , 10 + 2 * 16 ) ;
display . drawLine ( 0 , 10 + 3 * 16 , 128 , 10 + 3 * 16 ) ;
2022-08-06 14:50:40 +02:00
display . setDrawColor ( 1 ) ;
display . drawLine ( 4 + selection * 16 , 10 + line * 16 , 9 + selection * 16 , 10 + line * 16 ) ;
display_Update ( ) ;
}
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 + + ;
}
display . setDrawColor ( 0 ) ;
2022-10-07 10:15:18 +02:00
display . drawLine ( 0 , 10 + 0 * 16 , 128 , 10 + 0 * 16 ) ;
display . drawLine ( 0 , 10 + 1 * 16 , 128 , 10 + 1 * 16 ) ;
display . drawLine ( 0 , 10 + 2 * 16 , 128 , 10 + 2 * 16 ) ;
display . drawLine ( 0 , 10 + 3 * 16 , 128 , 10 + 3 * 16 ) ;
2022-08-06 14:50:40 +02:00
display . setDrawColor ( 1 ) ;
display . drawLine ( 4 + selection * 16 , 10 + line * 16 , 9 + selection * 16 , 10 + line * 16 ) ;
display_Update ( ) ;
}
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
# ifdef global_log
// 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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2023-07-19 15:13:06 +02:00
# ifdef enable_GBX
2022-06-09 00:35:11 +02:00
static const char modeItem1 [ ] PROGMEM = " Game Boy " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_NES
2022-06-09 00:35:11 +02:00
static const char modeItem2 [ ] PROGMEM = " NES/Famicom " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_SNES
2022-10-08 23:07:15 +02:00
static const char modeItem3 [ ] PROGMEM = " Super Nintendo/SFC " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_N64
2022-08-21 07:54:13 +02:00
static const char modeItem4 [ ] PROGMEM = " Nintendo 64 (3V) " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_MD
2022-10-09 10:49:20 +02:00
static const char modeItem5 [ ] PROGMEM = " Mega Drive/Genesis " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_SMS
2022-06-09 00:35:11 +02:00
static const char modeItem6 [ ] PROGMEM = " SMS/GG/MIII/SG-1000 " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_PCE
2021-10-27 20:59:57 +02:00
static const char modeItem7 [ ] PROGMEM = " PC Engine/TG16 " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_WS
2023-03-15 10:10:07 +01:00
static const char modeItem8 [ ] PROGMEM = " WonderSwan (3V) " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_NGP
2023-03-19 00:18:44 +01:00
static const char modeItem9 [ ] PROGMEM = " NeoGeo Pocket (3V) " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_INTV
2023-03-07 01:18:02 +01:00
static const char modeItem10 [ ] PROGMEM = " Intellivision " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_COLV
2022-07-23 11:04:17 +02:00
static const char modeItem11 [ ] PROGMEM = " Colecovision " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_VBOY
2022-07-23 11:04:17 +02:00
static const char modeItem12 [ ] PROGMEM = " Virtual Boy " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_WSV
2023-06-26 12:04:00 +02:00
static const char modeItem13 [ ] PROGMEM = " Watara Supervision (3V) " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_PCW
2022-09-25 10:36:28 +02:00
static const char modeItem14 [ ] PROGMEM = " Pocket Challenge W " ;
2023-07-19 15:13:06 +02:00
# endif
2023-12-26 23:13:51 +01:00
# ifdef enable_2600
2023-01-03 19:33:34 +01:00
static const char modeItem15 [ ] PROGMEM = " Atari 2600 " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_ODY2
2023-01-03 19:33:34 +01:00
static const char modeItem16 [ ] PROGMEM = " Magnavox Odyssey 2 " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_ARC
2023-01-03 19:33:34 +01:00
static const char modeItem17 [ ] PROGMEM = " Arcadia 2001 " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_FAIRCHILD
2023-01-03 19:33:34 +01:00
static const char modeItem18 [ ] PROGMEM = " Fairchild Channel F " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_SUPRACAN
2023-02-23 12:04:33 +01:00
static const char modeItem19 [ ] PROGMEM = " Super A'can " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_MSX
2023-06-25 14:09:17 +02:00
static const char modeItem20 [ ] PROGMEM = " MSX " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_POKE
2023-06-25 14:09:17 +02:00
static const char modeItem21 [ ] PROGMEM = " Pokemon Mini (3V) " ;
2023-07-19 15:13:06 +02:00
# endif
# ifdef enable_LOOPY
2023-07-03 01:08:09 +02:00
static const char modeItem22 [ ] PROGMEM = " Casio Loopy " ;
2023-07-19 15:13:06 +02:00
# endif
2023-09-23 00:11:08 +02:00
# ifdef enable_C64
static const char modeItem23 [ ] PROGMEM = " Commodore 64 " ;
# endif
# ifdef enable_5200
static const char modeItem24 [ ] PROGMEM = " Atari 5200 " ;
# endif
# ifdef enable_7800
static const char modeItem25 [ ] PROGMEM = " Atari 7800 " ;
# endif
# ifdef enable_VECTREX
static const char modeItem26 [ ] PROGMEM = " Vectrex " ;
# endif
2023-07-19 15:13:06 +02:00
# ifdef enable_FLASH
2023-09-23 00:11:08 +02:00
static const char modeItem27 [ ] PROGMEM = " Flashrom Programmer " ;
2023-07-19 15:13:06 +02:00
# endif
2023-09-25 16:12:04 +02:00
# ifdef enable_selftest
2023-09-23 00:11:08 +02:00
static const char modeItem28 [ ] PROGMEM = " Self Test (3V) " ;
2023-09-25 16:12:04 +02:00
# endif
2023-09-23 00:11:08 +02:00
static const char modeItem29 [ ] PROGMEM = " About " ;
//static const char modeItem30[] PROGMEM = "Reset"; (stored in common strings array)
2023-07-19 15:13:06 +02:00
static const char * const modeOptions [ ] PROGMEM = {
# ifdef enable_GBX
modeItem1 ,
# endif
# ifdef enable_NES
modeItem2 ,
# endif
# ifdef enable_SNES
modeItem3 ,
# endif
# ifdef enable_N64
modeItem4 ,
# endif
# ifdef enable_MD
modeItem5 ,
# endif
# ifdef enable_SMS
modeItem6 ,
# endif
# ifdef enable_PCE
modeItem7 ,
# endif
# ifdef enable_WS
modeItem8 ,
# endif
# ifdef enable_NGP
modeItem9 ,
# endif
# ifdef enable_INTV
modeItem10 ,
# endif
# ifdef enable_COLV
modeItem11 ,
# endif
# ifdef enable_VBOY
modeItem12 ,
# endif
# ifdef enable_WSV
modeItem13 ,
# endif
# ifdef enable_PCW
modeItem14 ,
# endif
2023-12-26 23:13:51 +01:00
# ifdef enable_2600
2023-07-19 15:13:06 +02:00
modeItem15 ,
# endif
# ifdef enable_ODY2
modeItem16 ,
# endif
# ifdef enable_ARC
modeItem17 ,
# endif
# ifdef enable_FAIRCHILD
modeItem18 ,
# endif
# ifdef enable_SUPRACAN
modeItem19 ,
# endif
# ifdef enable_MSX
modeItem20 ,
# endif
# ifdef enable_POKE
modeItem21 ,
# endif
# ifdef enable_LOOPY
modeItem22 ,
# endif
2023-09-23 00:11:08 +02:00
# ifdef enable_C64
2023-07-19 15:13:06 +02:00
modeItem23 ,
# endif
2023-09-23 00:11:08 +02:00
# ifdef enable_5200
modeItem24 ,
# endif
# ifdef enable_7800
modeItem25 ,
# endif
# ifdef enable_VECTREX
modeItem26 ,
# endif
# ifdef enable_FLASH
modeItem27 ,
# endif
2023-09-25 16:12:04 +02:00
# ifdef enable_selftest
2023-09-25 16:23:43 +02:00
modeItem28 ,
2023-09-25 16:12:04 +02:00
# endif
modeItem29 , string_reset2
2023-07-19 15:13:06 +02:00
} ;
// Count menu entries
byte countMenuEntries ( ) {
2023-09-25 16:12:04 +02:00
byte count = 2 ;
2023-07-19 15:13:06 +02:00
# ifdef enable_GBX
count + + ;
# endif
# ifdef enable_NES
count + + ;
# endif
# ifdef enable_SNES
count + + ;
# endif
# ifdef enable_N64
count + + ;
# endif
# ifdef enable_MD
count + + ;
# endif
# ifdef enable_SMS
count + + ;
# endif
# ifdef enable_PCE
count + + ;
# endif
# ifdef enable_WS
count + + ;
# endif
# ifdef enable_NGP
count + + ;
# endif
# ifdef enable_INTV
count + + ;
# endif
# ifdef enable_COLV
count + + ;
# endif
# ifdef enable_VBOY
count + + ;
# endif
# ifdef enable_WSV
count + + ;
# endif
# ifdef enable_PCW
count + + ;
# endif
2023-12-26 23:13:51 +01:00
# ifdef enable_2600
2023-07-19 15:13:06 +02:00
count + + ;
# endif
# ifdef enable_ODY2
count + + ;
# endif
# ifdef enable_ARC
count + + ;
# endif
# ifdef enable_FAIRCHILD
count + + ;
# endif
# ifdef enable_SUPRACAN
count + + ;
# endif
# ifdef enable_MSX
count + + ;
# endif
# ifdef enable_POKE
count + + ;
# endif
# ifdef enable_LOOPY
count + + ;
# endif
2023-09-23 00:11:08 +02:00
# ifdef enable_C64
count + + ;
# endif
# ifdef enable_5200
count + + ;
# endif
# ifdef enable_7800
count + + ;
# endif
# ifdef enable_VECTREX
count + + ;
# endif
2023-07-19 15:13:06 +02:00
# ifdef enable_FLASH
count + + ;
2023-09-25 16:12:04 +02:00
# endif
# ifdef enable_selftest
count + + ;
2023-07-19 15:13:06 +02:00
# endif
return count ;
}
// Account for disabled menue entries
unsigned char fixMenuOrder ( unsigned char modeMenu ) {
byte translationMatrix [ 26 ] ;
byte currentEntry = 0 ;
# if defined(enable_GBX)
translationMatrix [ currentEntry ] = 0 ;
currentEntry + + ;
# endif
# if defined(enable_NES)
translationMatrix [ currentEntry ] = 1 ;
currentEntry + + ;
# endif
# if defined(enable_SNES)
translationMatrix [ currentEntry ] = 2 ;
currentEntry + + ;
# endif
# if defined(enable_N64)
translationMatrix [ currentEntry ] = 3 ;
currentEntry + + ;
# endif
# if defined(enable_MD)
translationMatrix [ currentEntry ] = 4 ;
currentEntry + + ;
# endif
# if defined(enable_SMS)
translationMatrix [ currentEntry ] = 5 ;
currentEntry + + ;
# endif
# if defined(enable_PCE)
translationMatrix [ currentEntry ] = 6 ;
currentEntry + + ;
# endif
# if defined(enable_WS)
translationMatrix [ currentEntry ] = 7 ;
currentEntry + + ;
# endif
# if defined(enable_NGP)
translationMatrix [ currentEntry ] = 8 ;
currentEntry + + ;
# endif
# if defined(enable_INTV)
translationMatrix [ currentEntry ] = 9 ;
currentEntry + + ;
# endif
# if defined(enable_COLV)
translationMatrix [ currentEntry ] = 10 ;
currentEntry + + ;
# endif
# if defined(enable_VBOY)
translationMatrix [ currentEntry ] = 11 ;
currentEntry + + ;
# endif
# if defined(enable_WSV)
translationMatrix [ currentEntry ] = 12 ;
currentEntry + + ;
# endif
# if defined(enable_PCW)
translationMatrix [ currentEntry ] = 13 ;
currentEntry + + ;
# endif
2023-12-26 23:13:51 +01:00
# if defined(enable_2600)
2023-07-19 15:13:06 +02:00
translationMatrix [ currentEntry ] = 14 ;
currentEntry + + ;
# endif
# if defined(enable_ODY2)
translationMatrix [ currentEntry ] = 15 ;
currentEntry + + ;
# endif
# if defined(enable_ARC)
translationMatrix [ currentEntry ] = 16 ;
currentEntry + + ;
# endif
# if defined(enable_FAIRCHILD)
translationMatrix [ currentEntry ] = 17 ;
currentEntry + + ;
# endif
# if defined(enable_SUPRACAN)
translationMatrix [ currentEntry ] = 18 ;
currentEntry + + ;
# endif
# if defined(enable_MSX)
translationMatrix [ currentEntry ] = 19 ;
currentEntry + + ;
# endif
# if defined(enable_POKE)
translationMatrix [ currentEntry ] = 20 ;
currentEntry + + ;
# endif
# if defined(enable_LOOPY)
translationMatrix [ currentEntry ] = 21 ;
currentEntry + + ;
# endif
2023-09-23 00:11:08 +02:00
# if defined(enable_C64)
2023-07-19 15:13:06 +02:00
translationMatrix [ currentEntry ] = 22 ;
currentEntry + + ;
# endif
2023-09-23 00:11:08 +02:00
# if defined(enable_5200)
2023-07-19 15:13:06 +02:00
translationMatrix [ currentEntry ] = 23 ;
currentEntry + + ;
2023-09-23 00:11:08 +02:00
# endif
2023-07-19 15:13:06 +02:00
2023-09-23 00:11:08 +02:00
# if defined(enable_7800)
2023-07-19 15:13:06 +02:00
translationMatrix [ currentEntry ] = 24 ;
currentEntry + + ;
2023-09-23 00:11:08 +02:00
# endif
2023-07-19 15:13:06 +02:00
2023-09-23 00:11:08 +02:00
# if defined(enable_VECTREX)
2023-07-19 15:13:06 +02:00
translationMatrix [ currentEntry ] = 25 ;
currentEntry + + ;
2023-09-23 00:11:08 +02:00
# endif
# if defined(enable_FLASH)
translationMatrix [ currentEntry ] = 26 ;
currentEntry + + ;
# endif
2023-09-25 16:12:04 +02:00
# if defined(enable_selftest)
2023-09-23 00:11:08 +02:00
translationMatrix [ currentEntry ] = 27 ;
currentEntry + + ;
2023-09-25 16:12:04 +02:00
# endif
2023-09-23 00:11:08 +02:00
// About
translationMatrix [ currentEntry ] = 28 ;
currentEntry + + ;
// Reset
translationMatrix [ currentEntry ] = 29 ;
currentEntry + + ;
2023-07-19 15:13:06 +02:00
return translationMatrix [ modeMenu ] ;
}
2021-10-27 20:59:57 +02:00
// All included slots
void mainMenu ( ) {
2023-01-03 19:33:34 +01:00
// create menu with title and 20 options to choose from
2021-10-27 20:59:57 +02:00
unsigned char modeMenu ;
2022-10-30 02:31:09 +02:00
byte num_answers ;
byte option_offset ;
2021-10-27 20:59:57 +02:00
2023-07-19 15:13:06 +02:00
// Count menu entries
byte menuCount = countMenuEntries ( ) ;
2023-01-03 19:33:34 +01:00
// Main menu spans across three pages
2021-10-27 20:59:57 +02:00
currPage = 1 ;
lastPage = 1 ;
2023-07-19 15:13:06 +02:00
if ( ( menuCount % 7 ) = = 0 )
numPages = menuCount / 7 ;
else
numPages = ( byte ) ( menuCount / 7 ) + 1 ;
2021-10-27 20:59:57 +02:00
while ( 1 ) {
if ( currPage = = 1 ) {
2022-10-30 02:31:09 +02:00
option_offset = 0 ;
2023-07-19 15:13:06 +02:00
if ( menuCount < 7 )
num_answers = menuCount ;
else
num_answers = 7 ;
2022-11-05 02:01:58 +01:00
} else if ( currPage = = 2 ) {
2022-10-30 02:31:09 +02:00
option_offset = 7 ;
2023-07-19 15:13:06 +02:00
if ( menuCount < 14 )
num_answers = menuCount - 7 ;
else
num_answers = 7 ;
2023-02-23 12:04:33 +01:00
} else if ( currPage = = 3 ) {
2022-10-30 02:31:09 +02:00
option_offset = 14 ;
2023-07-19 15:13:06 +02:00
if ( menuCount < 21 )
num_answers = menuCount - 14 ;
else
num_answers = 7 ;
2023-09-23 00:11:08 +02:00
} else if ( currPage = = 4 ) {
2023-02-23 12:04:33 +01:00
option_offset = 21 ;
2023-09-23 00:11:08 +02:00
if ( menuCount < 28 )
num_answers = menuCount - 21 ;
else
num_answers = 7 ;
} else { // currPage == 5
option_offset = 28 ;
num_answers = menuCount - 28 ;
2022-08-21 12:28:47 +02:00
}
2022-10-30 02:31:09 +02:00
// Copy menuOptions out of progmem
convertPgm ( modeOptions + option_offset , num_answers ) ;
modeMenu = question_box ( F ( " OPEN SOURCE CART READER " ) , menuOptions , num_answers , 0 ) ;
2021-10-27 20:59:57 +02:00
if ( numPages = = 0 ) {
// Execute choice
2022-10-30 02:31:09 +02:00
modeMenu + = option_offset ;
2021-10-27 20:59:57 +02:00
break ;
}
}
2022-03-21 01:01:35 +01:00
// Reset page number
currPage = 1 ;
2023-07-19 15:13:06 +02:00
modeMenu = fixMenuOrder ( modeMenu ) ;
2021-10-27 20:59:57 +02:00
// wait for user choice to come back from the question box menu
2022-10-13 09:49:03 +02:00
switch ( modeMenu ) {
2023-07-19 15:13:06 +02:00
2022-06-09 00:35:11 +02:00
# ifdef enable_GBX
2021-10-27 20:59:57 +02:00
case 0 :
2022-06-09 00:35:11 +02:00
gbxMenu ( ) ;
2021-10-27 20:59:57 +02:00
break ;
# endif
2022-06-09 00:35:11 +02:00
# ifdef enable_NES
2021-10-27 20:59:57 +02:00
case 1 :
2022-07-07 23:20:45 +02:00
mode = mode_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 ( ) ;
2022-06-09 00:35:11 +02:00
nesMenu ( ) ;
2021-10-27 20:59:57 +02:00
break ;
# endif
2022-06-09 00:35:11 +02:00
# ifdef enable_SNES
2021-10-27 20:59:57 +02:00
case 2 :
2022-06-09 00:35:11 +02:00
snsMenu ( ) ;
2021-10-27 20:59:57 +02:00
break ;
# endif
2022-06-09 00:35:11 +02:00
# ifdef enable_N64
2021-10-27 20:59:57 +02:00
case 3 :
2022-06-09 00:35:11 +02:00
n64Menu ( ) ;
2021-10-27 20:59:57 +02:00
break ;
# endif
2022-06-09 00:35:11 +02:00
# ifdef enable_MD
2021-10-27 20:59:57 +02:00
case 4 :
2022-06-09 00:35:11 +02:00
mdMenu ( ) ;
2021-10-27 20:59:57 +02:00
break ;
# endif
2022-06-09 00:35:11 +02:00
# ifdef enable_SMS
2021-10-27 20:59:57 +02:00
case 5 :
2022-06-09 00:35:11 +02:00
smsMenu ( ) ;
2021-10-27 20:59:57 +02:00
break ;
# endif
# ifdef enable_PCE
case 6 :
pcsMenu ( ) ;
break ;
# endif
# ifdef enable_WS
2022-06-09 00:35:11 +02:00
case 7 :
2021-10-27 20:59:57 +02:00
display_Clear ( ) ;
display_Update ( ) ;
setup_WS ( ) ;
mode = mode_WS ;
break ;
# endif
# ifdef enable_NGP
2022-06-09 00:35:11 +02:00
case 8 :
2021-10-27 20:59:57 +02:00
display_Clear ( ) ;
display_Update ( ) ;
setup_NGP ( ) ;
mode = mode_NGP ;
break ;
# endif
2022-07-23 11:04:17 +02:00
# ifdef enable_INTV
2022-06-09 00:35:11 +02:00
case 9 :
2022-07-23 11:04:17 +02:00
setup_INTV ( ) ;
intvMenu ( ) ;
break ;
# endif
# ifdef enable_COLV
case 10 :
setup_COL ( ) ;
colMenu ( ) ;
break ;
# endif
# ifdef enable_VBOY
case 11 :
setup_VBOY ( ) ;
vboyMenu ( ) ;
break ;
# endif
2022-08-21 12:28:47 +02:00
# ifdef enable_WSV
2022-07-23 11:04:17 +02:00
case 12 :
2022-08-21 12:28:47 +02:00
setup_WSV ( ) ;
wsvMenu ( ) ;
break ;
# endif
2022-09-25 10:36:28 +02:00
# ifdef enable_PCW
2022-08-21 12:28:47 +02:00
case 13 :
2022-09-25 10:36:28 +02:00
setup_PCW ( ) ;
pcwMenu ( ) ;
break ;
# endif
2023-12-26 23:13:51 +01:00
# ifdef enable_2600
2022-09-25 10:36:28 +02:00
case 14 :
2023-12-26 23:13:51 +01:00
setup_2600 ( ) ;
a2600Menu ( ) ;
2023-01-03 19:33:34 +01:00
break ;
# endif
# ifdef enable_ODY2
case 15 :
setup_ODY2 ( ) ;
ody2Menu ( ) ;
break ;
# endif
# ifdef enable_ARC
case 16 :
setup_ARC ( ) ;
arcMenu ( ) ;
break ;
# endif
# ifdef enable_FAIRCHILD
case 17 :
setup_FAIRCHILD ( ) ;
fairchildMenu ( ) ;
break ;
# endif
2023-02-23 12:04:33 +01:00
# ifdef enable_SUPRACAN
2023-01-03 19:33:34 +01:00
case 18 :
2023-02-23 12:04:33 +01:00
setup_SuprAcan ( ) ;
break ;
# endif
2023-06-25 14:09:17 +02:00
# ifdef enable_MSX
2023-02-23 12:04:33 +01:00
case 19 :
2023-06-25 14:09:17 +02:00
setup_MSX ( ) ;
msxMenu ( ) ;
break ;
# endif
# ifdef enable_POKE
case 20 :
setup_POKE ( ) ;
pokeMenu ( ) ;
break ;
# endif
2023-07-03 01:08:09 +02:00
# ifdef enable_LOOPY
2023-06-25 14:09:17 +02:00
case 21 :
2023-07-05 10:29:46 +02:00
setup_LOOPY ( ) ;
2023-07-03 01:08:09 +02:00
loopyMenu ( ) ;
break ;
# endif
2023-09-23 00:11:08 +02:00
# ifdef enable_C64
2023-07-03 01:08:09 +02:00
case 22 :
2023-10-17 18:41:14 +02:00
setup_C64 ( ) ;
2023-09-23 00:11:08 +02:00
c64Menu ( ) ;
break ;
# endif
# ifdef enable_5200
case 23 :
2023-10-17 18:41:14 +02:00
setup_5200 ( ) ;
2023-09-23 00:11:08 +02:00
a5200Menu ( ) ;
break ;
# endif
# ifdef enable_7800
case 24 :
2023-10-17 18:41:14 +02:00
setup_7800 ( ) ;
2023-09-23 00:11:08 +02:00
a7800Menu ( ) ;
break ;
# endif
# ifdef enable_VECTREX
case 25 :
2023-10-17 18:41:14 +02:00
setup_VECTREX ( ) ;
2023-09-23 00:11:08 +02:00
vectrexMenu ( ) ;
break ;
# endif
# ifdef enable_FLASH
case 26 :
2023-06-26 12:04:00 +02:00
# ifdef ENABLE_VSELECT
setup_FlashVoltage ( ) ;
# endif
2022-06-09 00:35:11 +02:00
flashMenu ( ) ;
break ;
# endif
2023-03-08 18:26:19 +01:00
# ifdef enable_selftest
2023-09-23 00:11:08 +02:00
case 27 :
2023-03-08 18:26:19 +01:00
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
2023-09-23 00:11:08 +02:00
case 28 :
2023-03-08 18:26:19 +01:00
aboutScreen ( ) ;
break ;
2023-09-23 00:11:08 +02:00
case 29 :
2023-02-23 12:04:33 +01:00
resetArduino ( ) ;
break ;
2022-07-17 14:50:59 +02:00
default :
2022-10-31 15:41:29 +01:00
print_MissingModule ( ) ; // does not return
2021-10-27 20:59:57 +02:00
}
}
2023-03-08 18:26:19 +01:00
/******************************************
Self Test
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef enable_selftest
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 " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " Remove all Cartridges " ) ) ;
println_Msg ( F ( " before continuing!!! " ) ) ;
println_Msg ( F ( " " ) ) ;
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 " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " Turn the EEP switch on. " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " " ) ) ;
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 " ) ) ;
println_Msg ( F ( " " ) ) ;
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 ) ;
}
println_Msg ( F ( " " ) ) ;
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 ) ;
}
println_Msg ( F ( " " ) ) ;
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 " ) ) ;
println_Msg ( F ( " " ) ) ;
print_STR ( press_button_STR , 1 ) ;
display_Update ( ) ;
wait ( ) ;
resetArduino ( ) ;
}
println_Msg ( F ( " " ) ) ;
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 " ) ) ;
2023-01-16 12:35:48 +01:00
print_Msg ( F ( " 2023 Version " ) ) ;
2021-10-27 20:59:57 +02:00
println_Msg ( ver ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " " ) ) ;
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 ) {
2021-11-17 21:31:18 +01:00
# if (defined(enable_LCD) || defined(enable_OLED))
2021-10-27 20:59:57 +02:00
// get input button
int b = checkButton ( ) ;
// 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 ( ) ;
}
# elif defined(enable_serial)
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef RTC_installed
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef clockgen_calibration
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 ) ;
2023-10-17 18:41:14 +02:00
println_Msg ( F ( " " ) ) ;
2022-07-07 00:15:13 +02:00
# ifdef enable_Button2
println_Msg ( F ( " (Hold button to save) " ) ) ;
println_Msg ( F ( " " ) ) ;
println_Msg ( F ( " Decrease Increase " ) ) ;
# else
# 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 ( ) ;
}
# ifdef enable_Button2
// get input button
int a = checkButton1 ( ) ;
int b = checkButton2 ( ) ;
// 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.
int a = checkButton ( ) ;
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
print_Msg ( F ( " " ) ) ;
if ( abs_number = = 0 )
abs_number = 1 ;
2022-10-13 09:49:03 +02:00
while ( abs_number < 100000000ULL ) {
2022-07-07 00:15:13 +02:00
print_Msg ( F ( " " ) ) ;
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
2023-10-17 18:41:14 +02:00
# if defined(clockgen_calibration) || defined(use_clockgen_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 ( ) {
2023-10-17 18:41:14 +02:00
# ifdef use_clockgen_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 ( ) {
2023-03-30 03:05:01 +02:00
# if !defined(enable_serial) && defined(ENABLE_UPDATER)
ClockedSerial . begin ( UPD_BAUD ) ;
# endif
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 ) ;
2023-03-30 03:05:01 +02:00
# if defined(HW5) && !defined(ENABLE_VSELECT)
2022-06-09 00:35:11 +02:00
// HW5 has status LED connected to PD7
// Set LED Pin PD7 to Output
DDRD | = ( 1 < < 7 ) ;
PORTD | = ( 1 < < 7 ) ;
2023-03-30 03:05:01 +02:00
# elif defined(ENABLE_VSELECT)
2022-11-12 20:13:22 +01:00
DDRD | = ( 1 < < 7 ) ;
2022-06-09 00:35:11 +02:00
# else
// HW1/2/3 have button connected to PD7
// Set Button Pin PD7 to Input
DDRD & = ~ ( 1 < < 7 ) ;
# endif
2018-10-05 18:33:09 +02:00
// Activate Internal Pullup Resistors
2020-05-13 13:05:10 +02:00
//PORTG |= (1 << 2);
2022-06-09 00:35:11 +02:00
//PORTD |= (1 << 7);
2018-10-05 18:33:09 +02:00
// Read current folder number out of 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
2021-10-24 00:41:18 +02:00
# ifdef enable_LCD
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 ) ;
# endif
# ifdef enable_neopixel
2023-04-20 11:38:46 +02:00
# if defined(ENABLE_3V3FIX)
// Set power high for neopixel
setVoltage ( VOLTS_SET_5V ) ;
delay ( 10 ) ;
# endif
2021-10-24 00:41:18 +02:00
pixels . begin ( ) ;
pixels . clear ( ) ;
2022-06-09 00:35:11 +02:00
pixels . setPixelColor ( 0 , pixels . Color ( background_color ) ) ;
2021-10-26 22:19:10 +02:00
pixels . setPixelColor ( 1 , pixels . Color ( 0 , 0 , 100 ) ) ;
pixels . setPixelColor ( 2 , pixels . Color ( 0 , 0 , 100 ) ) ;
2021-10-24 00:41:18 +02:00
pixels . show ( ) ;
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
2023-04-20 11:38:46 +02:00
# if !(defined(enable_serial) || defined(HW5))
DDRE | = ( 1 < < 1 ) ;
# endif
2022-08-27 23:55:30 +02:00
# else
2023-04-20 11:38:46 +02:00
# ifndef enable_LCD
# ifdef CA_LED
// Turn LED off
digitalWrite ( 12 , 1 ) ;
digitalWrite ( 11 , 1 ) ;
digitalWrite ( 10 , 1 ) ;
# endif
// 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)
# endif
# endif
2023-06-26 15:46:47 +02:00
# ifdef ENABLE_VSELECT
2023-04-20 11:38:46 +02:00
// Set power to low to protect carts
setVoltage ( VOLTS_SET_3V3 ) ;
2023-06-26 15:46:47 +02:00
# endif
2021-10-24 00:41:18 +02:00
2019-09-05 00:48:39 +02:00
# ifdef enable_OLED
2022-10-08 16:23:33 +02:00
display . begin ( ) ;
//isplay.setContrast(40);
display . setFont ( u8g2_font_haxrcorp4089_tr ) ;
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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
// Serial Begin
Serial . begin ( 9600 ) ;
2021-10-26 11:27:07 +02:00
Serial . println ( " " ) ;
2019-09-05 00:48:39 +02:00
Serial . println ( F ( " Cartridge Reader " ) ) ;
2023-01-16 12:35:48 +01:00
Serial . println ( F ( " 2023 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 ) ;
2019-09-05 00:48:39 +02:00
# endif
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 ( ) ;
2022-10-30 03:21:01 +01:00
print_FatalError ( sd_error_STR ) ;
2018-10-05 18:33:09 +02:00
}
2023-03-30 03:05:01 +02:00
# if !defined(enable_serial) && defined(ENABLE_UPDATER)
printVersionToSerial ( ) ;
ClockedSerial . flush ( ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
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
}
2022-06-14 15:25:53 +02:00
println_Msg ( F ( " " ) ) ;
# if defined(HW1)
print_Msg ( F ( " OSCR HW1 " ) ) ;
# elif defined(HW2)
print_Msg ( F ( " OSCR HW2 " ) ) ;
# elif defined(HW3)
print_Msg ( F ( " OSCR HW3 " ) ) ;
# elif defined(HW4)
print_Msg ( F ( " OSCR HW4 " ) ) ;
# elif defined(HW5)
print_Msg ( F ( " OSCR HW5 " ) ) ;
# elif defined(SERIAL_MONITOR)
print_Msg ( F ( " OSCR Serial " ) ) ;
# endif
print_Msg ( F ( " V " ) ) ;
println_Msg ( ver ) ;
2022-06-12 12:30:52 +02:00
# endif
2021-10-02 07:29:37 +02:00
# ifdef RTC_installed
// Start RTC
RTCStart ( ) ;
// Set Date/Time Callback Funtion
SdFile : : dateTimeCallback ( dateTime ) ;
# endif
2022-08-03 12:14:32 +02:00
// status LED ON
statusLED ( true ) ;
// 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 ) {
2022-08-28 08:59:00 +02:00
# if defined(enable_neopixel)
2023-04-20 11:38:46 +02:00
# if defined(ENABLE_3V3FIX)
if ( clock = = CS_8MHZ ) return ;
# 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 ;
2021-10-24 00:41:18 +02:00
pixels . clear ( ) ;
2022-06-09 00:35:11 +02:00
pixels . setPixelColor ( 0 , pixels . Color ( background_color ) ) ;
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 ( ) ;
2022-08-28 08:59:00 +02:00
# elif defined(CA_LED)
// 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 ) {
println_Msg ( F ( " " ) ) ;
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 ) ;
2021-10-25 18:03:16 +02:00
# if defined(enable_LCD)
2021-10-26 11:27:07 +02:00
wait_btn ( ) ;
2022-10-13 09:49:03 +02:00
# elif defined(enable_OLED)
2019-09-05 00:48:39 +02:00
wait_btn ( ) ;
2022-10-13 09:49:03 +02:00
# elif defined(enable_serial)
2019-09-05 00:48:39 +02:00
wait_serial ( ) ;
# endif
2018-10-05 18:33:09 +02:00
}
2022-06-16 15:15:43 +02:00
# ifdef global_log
// 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
2022-10-08 18:19:31 +02:00
# ifdef 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . print ( string ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 [ ] ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2021-10-26 17:13:42 +02:00
Serial . print ( myString ) ;
2019-09-05 00:48:39 +02:00
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . print ( message ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . print ( message , outputFormat ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02:00
# if (defined(enable_LCD) || defined(enable_OLED))
2022-08-19 11:48:43 +02:00
display . print ( message , outputFormat ) ;
# endif
# ifdef enable_serial
Serial . print ( message , outputFormat ) ;
# endif
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) myLog . print ( message , outputFormat ) ;
2022-08-19 11:48:43 +02:00
# endif
}
void print_Msg ( int message , int outputFormat ) {
2022-10-08 16:23:33 +02:00
# if (defined(enable_LCD) || defined(enable_OLED))
2022-08-19 11:48:43 +02:00
display . print ( message , outputFormat ) ;
# endif
# ifdef enable_serial
Serial . print ( message , outputFormat ) ;
# endif
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) myLog . print ( message , outputFormat ) ;
2022-08-19 11:48:43 +02:00
# endif
}
void print_Msg ( long unsigned int message , int outputFormat ) {
2022-10-08 16:23:33 +02:00
# if (defined(enable_LCD) || defined(enable_OLED))
2022-08-19 11:48:43 +02:00
display . print ( message , outputFormat ) ;
# endif
# ifdef enable_serial
Serial . print ( message , outputFormat ) ;
# endif
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . print ( string ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . println ( string ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . println ( message , outputFormat ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 [ ] ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2022-06-12 12:30:52 +02:00
Serial . println ( myString ) ;
# endif
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . println ( string ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef 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 ) ) {
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) 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 ) {
2022-10-08 16:23:33 +02: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
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
Serial . println ( message ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) myLog . println ( message ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void display_Update ( ) {
2022-10-08 16:23:33 +02:00
# if (defined(enable_LCD) || defined(enable_OLED))
2021-10-24 00:41:18 +02:00
display . updateDisplay ( ) ;
# endif
# ifdef enable_serial
2019-09-05 00:48:39 +02:00
delay ( 100 ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) myLog . flush ( ) ;
2022-06-12 12:30:52 +02:00
# endif
2018-10-05 18:33:09 +02:00
}
void display_Clear ( ) {
2022-10-08 16:23:33 +02:00
# if (defined(enable_LCD) || defined(enable_OLED))
2021-10-24 00:41:18 +02:00
display . clearDisplay ( ) ;
display . setCursor ( 0 , 8 ) ;
# endif
2022-06-12 12:30:52 +02:00
# ifdef global_log
2022-10-13 09:49:03 +02:00
if ( ! dont_log ) myLog . println ( " " ) ;
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 ( ) {
# if (defined(enable_LCD) || defined(enable_OLED))
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 ) ;
# elif defined(enable_OLED)
PORTB ^ = ( 1 < < 4 ) ;
# elif defined(enable_LCD)
PORTE ^ = ( 1 < < 1 ) ;
# elif defined(enable_serial)
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-10-13 09:49:03 +02:00
unsigned char question_box ( const __FlashStringHelper * question , char answers [ 7 ] [ 20 ] , int num_answers , int default_choice ) {
2022-10-08 16:23:33 +02:00
# if (defined(enable_LCD) || defined(enable_OLED))
return questionBox_Display ( question , answers , num_answers , default_choice ) ;
# endif
# ifdef enable_serial
return questionBox_Serial ( question , answers , num_answers , default_choice ) ;
# endif
2018-10-05 18:33:09 +02:00
}
2022-10-08 16:23:33 +02:00
# if defined(enable_serial)
// Serial Monitor
2022-10-13 09:49:03 +02:00
byte questionBox_Serial ( const __FlashStringHelper * question , char answers [ 7 ] [ 20 ] , int num_answers , int default_choice ) {
2018-10-05 18:33:09 +02:00
// Print menu to serial monitor
2022-02-08 14:12:40 +01:00
Serial . println ( " " ) ;
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
Serial . println ( " " ) ;
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): _ " ) ) ;
2022-02-08 14:12:40 +01:00
Serial . println ( " " ) ;
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);
//Serial.println("");
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
# 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]
2022-10-13 09:49:03 +02:00
unsigned char questionBox_Display ( const __FlashStringHelper * question , char answers [ 7 ] [ 20 ] , int num_answers , int 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 */
int b = checkButton ( ) ;
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
# ifdef global_log
println_Msg ( " " ) ;
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
2023-03-30 03:05:01 +02:00
# if !defined(enable_serial) && defined(ENABLE_UPDATER)
void checkUpdater ( ) {
if ( ClockedSerial . available ( ) > 0 ) {
String cmd = ClockedSerial . readStringUntil ( ' \n ' ) ;
cmd . trim ( ) ;
if ( cmd = = " VERCHK " ) {
delay ( 500 ) ;
printVersionToSerial ( ) ;
} else if ( cmd = = " GETCLOCK " ) {
# if defined(ENABLE_3V3FIX)
ClockedSerial . print ( F ( " Clock is running at " ) ) ;
ClockedSerial . print ( ( clock = = CS_16MHZ ) ? 16UL : 8UL ) ;
ClockedSerial . println ( F ( " MHz " ) ) ;
# else
ClockedSerial . println ( F ( " Dynamic clock speed (3V3FIX) is not enabled. " ) ) ;
2023-04-20 11:38:46 +02:00
# endif
2023-03-30 03:05:01 +02:00
} else if ( cmd = = " GETVOLTS " ) {
# if defined(ENABLE_VSELECT)
ClockedSerial . print ( F ( " Voltage is set to " ) ) ;
ClockedSerial . print ( ( voltage = = VOLTS_SET_5V ) ? 5 : 3.3 ) ;
ClockedSerial . println ( F ( " V " ) ) ;
# else
ClockedSerial . println ( F ( " Automatic voltage selection (VSELECT) is not enabled. " ) ) ;
2023-07-16 07:01:58 +02:00
# endif
} else if ( cmd = = " GETTIME " ) {
# if defined(RTC_installed)
ClockedSerial . print ( F ( " Current Time: " ) ) ;
ClockedSerial . println ( RTCStamp ( ) ) ;
# else
ClockedSerial . println ( F ( " RTC not installed " ) ) ;
# endif
2023-07-19 15:13:06 +02:00
} else if ( cmd . substring ( 0 , 7 ) = = " SETTIME " ) {
2023-07-16 07:01:58 +02:00
# if defined(RTC_installed)
ClockedSerial . println ( F ( " Setting Time... " ) ) ;
rtc . adjust ( DateTime ( cmd . substring ( 8 ) . toInt ( ) ) ) ;
ClockedSerial . print ( F ( " Current Time: " ) ) ;
ClockedSerial . println ( RTCStamp ( ) ) ;
# else
ClockedSerial . println ( F ( " RTC not installed " ) ) ;
2023-03-30 03:05:01 +02:00
# endif
} else {
ClockedSerial . println ( F ( " OSCR: Unknown Command " ) ) ;
}
}
}
# 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
# if defined(enable_serial)
int checkButton ( ) {
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)
# if defined(enable_OLED)
2018-10-05 18:33:09 +02:00
// Read button state
int 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
int checkButton1 ( ) {
int 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
int checkButton2 ( ) {
int 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
int b = checkButton ( ) ;
// 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)
# if (defined(enable_LCD) && defined(enable_rotary))
// Read encoder state
int checkButton ( ) {
// 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
int 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 ( ) {
2020-07-04 14:02:34 +02:00
# ifdef enable_N64
2018-10-05 18:33:09 +02:00
if ( mode = = mode_N64_Controller ) {
n64ControllerMenu ( ) ;
2022-10-13 09:49:03 +02:00
} else if ( mode = = mode_N64_Cart ) {
2018-10-05 18:33:09 +02:00
n64CartMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# else
2022-10-13 09:49:03 +02:00
if ( 1 = = 0 ) {
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_SNES
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_SNES ) {
snesMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_FLASH
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_FLASH8 ) {
flashromMenu8 ( ) ;
}
2022-07-17 14:50:59 +02:00
# ifdef enable_FLASH16
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_FLASH16 ) {
flashromMenu16 ( ) ;
2022-10-13 09:49:03 +02:00
} else if ( mode = = mode_EPROM ) {
2018-10-05 18:33:09 +02:00
epromMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
2022-07-17 14:50:59 +02:00
# endif
2022-07-23 11:04:17 +02:00
# ifdef enable_SFM
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_SFM ) {
sfmMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_GBX
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_GB ) {
gbMenu ( ) ;
2022-10-13 09:49:03 +02:00
} else if ( mode = = mode_GBA ) {
2018-10-05 18:33:09 +02:00
gbaMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
2022-07-23 11:04:17 +02:00
# ifdef enable_SFM
2021-11-18 14:55:50 +01:00
# ifdef enable_FLASH
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_SFM_Flash ) {
sfmFlashMenu ( ) ;
}
2021-11-18 14:55:50 +01:00
# endif
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_SFM_Game ) {
sfmGameOptions ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_GBX
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_GBM ) {
gbmMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
2020-07-11 13:39:12 +02:00
# ifdef enable_MD
2019-09-01 14:36:53 +02:00
else if ( mode = = mode_MD_Cart ) {
mdCartMenu ( ) ;
2018-10-05 18:33:09 +02:00
}
2020-07-11 13:39:12 +02:00
# endif
2020-07-04 14:02:34 +02:00
# ifdef enable_PCE
2018-10-05 18:33:09 +02:00
else if ( mode = = mode_PCE ) {
pceMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_SV
2018-10-22 20:29:49 +02:00
else if ( mode = = mode_SV ) {
svMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
2023-11-25 00:34:13 +01:00
# ifdef enable_ST
else if ( mode = = mode_ST ) {
stMenu ( ) ;
}
# endif
2024-02-09 22:06:44 +01:00
# ifdef enable_GPC
else if ( mode = = mode_GPC ) {
gpcMenu ( ) ;
}
# endif
2020-07-04 14:02:34 +02:00
# ifdef enable_NES
2019-09-01 14:36:53 +02:00
else if ( mode = = mode_NES ) {
nesMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_SMS
2019-09-01 14:36:53 +02:00
else if ( mode = = mode_SMS ) {
smsMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_MD
2019-09-01 14:36:53 +02:00
else if ( mode = = mode_SEGA_CD ) {
segaCDMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_GBX
2019-09-26 07:38:03 +02:00
else if ( mode = = mode_GB_GBSmart ) {
gbSmartMenu ( ) ;
2022-10-13 09:49:03 +02:00
} else if ( mode = = mode_GB_GBSmart_Flash ) {
2019-09-26 07:38:03 +02:00
gbSmartFlashMenu ( ) ;
2022-10-13 09:49:03 +02:00
} else if ( mode = = mode_GB_GBSmart_Game ) {
2019-09-26 07:38:03 +02:00
gbSmartGameOptions ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_WS
2019-10-11 15:15:59 +02:00
else if ( mode = = mode_WS ) {
wsMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
# ifdef enable_NGP
2020-05-12 13:52:03 +02:00
else if ( mode = = mode_NGP ) {
ngpMenu ( ) ;
}
2022-07-23 11:04:17 +02:00
# endif
# ifdef enable_INTV
else if ( mode = = mode_INTV ) {
intvMenu ( ) ;
}
# endif
# ifdef enable_COLV
else if ( mode = = mode_COL ) {
colMenu ( ) ;
}
# endif
# ifdef enable_VBOY
else if ( mode = = mode_VBOY ) {
vboyMenu ( ) ;
}
2022-08-21 12:28:47 +02:00
# endif
# ifdef enable_WSV
else if ( mode = = mode_WSV ) {
wsvMenu ( ) ;
}
2022-09-25 10:36:28 +02:00
# endif
# ifdef enable_PCW
else if ( mode = = mode_PCW ) {
pcwMenu ( ) ;
}
2023-01-03 19:33:34 +01:00
# endif
# ifdef enable_ODY2
else if ( mode = = mode_ODY2 ) {
ody2Menu ( ) ;
}
# endif
# ifdef enable_ARC
else if ( mode = = mode_ARC ) {
arcMenu ( ) ;
}
# endif
# ifdef enable_FAIRCHILD
else if ( mode = = mode_FAIRCHILD ) {
fairchildMenu ( ) ;
}
2023-01-24 09:34:54 +01:00
# endif
# ifdef enable_SUPRACAN
else if ( mode = = mode_SUPRACAN ) {
suprAcanMenu ( ) ;
}
2023-06-25 14:09:17 +02:00
# endif
# ifdef enable_MSX
else if ( mode = = mode_MSX ) {
msxMenu ( ) ;
}
# endif
# ifdef enable_POKE
else if ( mode = = mode_POKE ) {
pokeMenu ( ) ;
}
2020-07-04 14:02:34 +02:00
# endif
2023-07-06 22:55:45 +02:00
# ifdef enable_LOOPY
else if ( mode = = mode_LOOPY ) {
loopyMenu ( ) ;
}
# endif
2023-09-23 00:11:08 +02:00
# ifdef enable_C64
else if ( mode = = mode_C64 ) {
c64Menu ( ) ;
}
# endif
2023-12-26 23:13:51 +01:00
# ifdef enable_2600
else if ( mode = = mode_2600 ) {
a2600Menu ( ) ;
}
# endif
2023-09-23 00:11:08 +02:00
# ifdef enable_5200
else if ( mode = = mode_5200 ) {
a5200Menu ( ) ;
}
# endif
# ifdef enable_7800
else if ( mode = = mode_7800 ) {
a7800Menu ( ) ;
}
# endif
# ifdef enable_VECTREX
else if ( mode = = mode_VECTREX ) {
vectrexMenu ( ) ;
}
# endif
2018-10-05 18:33:09 +02:00
else {
display_Clear ( ) ;
println_Msg ( F ( " Menu Error " ) ) ;
println_Msg ( " " ) ;
println_Msg ( " " ) ;
print_Msg ( F ( " Mode = " ) ) ;
print_Msg ( mode ) ;
println_Msg ( F ( " " ) ) ;
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 ) ;
2018-10-05 18:33:09 +02:00
display_Update ( ) ;
wait ( ) ;
2019-08-27 21:43:48 +02:00
resetArduino ( ) ;
2018-10-05 18:33:09 +02:00
}
}
//******************************************
// End of File
2023-03-07 01:18:02 +01:00
//******************************************