├── photos ├── splashscreen.jpg ├── IMG_20200104_165849.jpg ├── IMG_20200104_170224.jpg └── IMG_20200112_093101.jpg ├── LICENSE ├── memory.md ├── Basic cheatsheet.md ├── BasicCheatsheet.md ├── host.h ├── SMART_Response_BASIC.ino ├── README_original.md ├── basic.h ├── README.md ├── host.cpp ├── logo2_rle.h └── basic.cpp /photos/splashscreen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdufnews/SMART_Response_BASIC/HEAD/photos/splashscreen.jpg -------------------------------------------------------------------------------- /photos/IMG_20200104_165849.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdufnews/SMART_Response_BASIC/HEAD/photos/IMG_20200104_165849.jpg -------------------------------------------------------------------------------- /photos/IMG_20200104_170224.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdufnews/SMART_Response_BASIC/HEAD/photos/IMG_20200104_170224.jpg -------------------------------------------------------------------------------- /photos/IMG_20200112_093101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdufnews/SMART_Response_BASIC/HEAD/photos/IMG_20200112_093101.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 fdufnews 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /memory.md: -------------------------------------------------------------------------------- 1 | # Usage of memory in BASIC 2 | 3 | As I spent some time understanding how mem[] was used, I have made this memo to keep trace of the information for later use 4 | 5 | # Memory map 6 | mem[] size is defined in basic.h 7 | ``` 8 | #define MEMORY_SIZE (10L*1024L) 9 | extern unsigned char mem[]; 10 | ``` 11 | mem is declared in SMART_Response_BASIC.ino 12 | 13 | mem is sliced in different subsections that are adressed using variables used as indexes inside mem 14 | * Program 15 | * Stack 16 | * Variables 17 | * Gosub 18 | 19 | |Direction|ID| Use | 20 | |------|-----|----| 21 | | ` V ` | 0 | | 22 | | ` V ` | | BASIC Program | 23 | | ` V ` | sysPROGEND | | 24 | | ` V ` |sysSTACKSTART | | 25 | | ` V ` | | evaluation stack | 26 | | ` V ` | sysSTACKEND | 27 | | | | Free memory | 28 | | ` ^ ` |sysVARSTART | | 29 | | ` ^ ` | | Variables | 30 | | ` ^ ` | sysVAREND | | 31 | | ` ^ ` | sysGOSUBSTART | 32 | | ` ^ ` | | Temporary storage for gosub| 33 | | ` ^ ` | sysGOSUBEND | | 34 | | ` ^ ` | MEMORY_SIZE | | 35 | 36 | After reset: 37 | * sysPROGEND, sysSTACKSTART, sysSTACKEND are set to 0 38 | * sysGOSUBEND, sysGOSUBSTART, sysVAREND, sysVARSTART are set to MEMORY_SIZE 39 | 40 | They grow up or down when necessary. 41 | 42 | It shall be noticed that using GOSUB is at some cost. When pushing (or pulling) return addresses on the GOSUB stack, the software must shift the variables stack to make (or recover) some room. The same apply to the variables so it is perhaps more efficient to declare the variables at the beginning of the program so there will be no time spent moving the stack during the execution. 43 | 44 | # Program space 45 | Program space starts at 0 in mem\[\] and goes up to sysPROGEND 46 | 47 | Each line starts with 48 | 49 | | size | function | 50 | |---|---| 51 | | int | offset to next line | 52 | | int | line number | 53 | 54 | Next are tokens as defined in basic.h 55 | ``` 56 | #define TOKEN_EOL 0 57 | #define TOKEN_IDENT 1 // special case - identifier follows 58 | #define TOKEN_INTEGER 2 // special case - integer follows (line numbers only) 59 | #define TOKEN_NUMBER 3 // special case - number follows 60 | #define TOKEN_STRING 4 // special case - string follows 61 | 62 | #define TOKEN_LBRACKET 8 63 | #define TOKEN_RBRACKET 9 64 | #define TOKEN_PLUS 10 65 | #define TOKEN_MINUS 11 66 | #define TOKEN_MULT 12 67 | 68 | up to 69 | 70 | #define TOKEN_DIR 64 71 | #define TOKEN_DELETE 65 72 | #define TOKEN_ASC 66 73 | #define TOKEN_SLEEP 67 74 | #define TOKEN_TONE 68 75 | #define TOKEN_NOTONE 69 76 | ``` 77 | Lines are terminated with TOKEN_EOL (00) 78 | There is no token indicating end of program. End of program is detected when program pointer equates sysPROGEND. 79 | 80 | The files on the SD card are a dump of the basic program space in mem so they have the same form 81 | 82 | A file in the EEPROM has a header with the length of the file, the name of the file, the length of the program, the program and a 00 00 trailer. 83 | -------------------------------------------------------------------------------- /Basic cheatsheet.md: -------------------------------------------------------------------------------- 1 | BASIC Language 2 | -------------- 3 | Variables names can be up to 8 alphanumeric characters but start with a letter e.g. a, bob32 4 | String variable names must end in $ e.g. a$, bob32$ 5 | Case is ignored (for all identifiers). BOB32 is the same as Bob32. print is the same as PRINT 6 | 7 | Array variables are independent from normal variables. So you can use both: 8 | ``` 9 | LET a = 5 10 | DIM a(10) 11 | ``` 12 | There is no ambiguity, since a on its own refers to the simple variable 'a', and a(n) referes to an element of the 'a' array. 13 | 14 | ``` 15 | Arithmetic operators: + - * / MOD 16 | Comparisons: <> (not equals) > >= < <= 17 | Logical: AND, OR, NOT 18 | ``` 19 | 20 | Expressions can be numerical e.g. 5*(3+2), or string "Hello "+"world". 21 | Only the addition operator is supported on strings (plus the functions below). 22 | 23 | Commands 24 | ``` 25 | PRINT ;... e.g. PRINT "A=";a 26 | LET variable = e.g. LET A$="Hello". 27 | variable = e.g. A=5 28 | LIST [start],[end] e.g. LIST or LIST 10,100 29 | RUN [lineNumber] 30 | GOTO lineNumber 31 | REM e.g. REM ** My Program *** 32 | STOP 33 | CONT (continue from a STOP) 34 | INPUT variable e.g. INPUT a$ or INPUT a(5,3) 35 | IF THEN cmd e.g. IF a>10 THEN a = 0: GOTO 20 36 | FOR variable = start TO end STEP step 37 | NEXT variable 38 | NEW 39 | GOSUB lineNumber 40 | RETURN 41 | DIM variable(n1,n2...) 42 | CLS 43 | SLEEP put terminal in low power mode, exit with power button 44 | PAUSE milliseconds 45 | POSITION x,y sets the cursor 46 | PIN pinNum, value (0 = low, non-zero = high) 47 | PINMODE pinNum, mode ( 0 = input, 1 = output) 48 | LOAD (from internal EEPROM) 49 | SAVE (to internal EEPROM) e.g. use SAVE + to set auto-run on boot flag 50 | LOAD "filename", SAVE "filename, DIR, DELETE "filename" if using with external EEPROM or SD card. Concerning SD card, the filename is limited to 8 characters. the software adds the ".BAS" extension. Concerning SD card, the filename is limited to 8 characters. the software adds the ".BAS" extension. 51 | MOUNT force the detection of the SD card. Necessary if it has been inserted when the terminal is running. Mount is made at reset and when restarting after a sleep 52 | UNMOUNT close all the files and prepare the card to be extracted. UNMMOUNT is made when entering sleep. 53 | TONE frequency_to_play Play a sound in the piezo buzzer 54 | NOTONE stop the tone 55 | ``` 56 | 57 | "Pseudo-identifiers" 58 | ``` 59 | INKEY$ - returns (and eats) the last key pressed buffer (non-blocking). e.g. PRINT INKEY$ 60 | RND - random number betweeen 0 and 1. e.g. LET a = RND 61 | ``` 62 | 63 | Functions 64 | ``` 65 | LEN(string) e.g. PRINT LEN("Hello") -> 5 66 | ASC(string) e.g. PRINT ASC("ABCD") -> 65 (ASCII code of A) 67 | VAL(string) e.g. PRINT VAL("1+2") 68 | INT(number) e.g. INT(1.5)-> 1 69 | STR$(number) e.g. STR$(2) -> "2" 70 | LEFT$(string,n) 71 | RIGHT$(string,n) 72 | MID$(string,start,n) 73 | PINREAD(pin) - see Arduino digitalRead() 74 | ANALOGRD(pin) - see Arduino analogRead() 75 | ``` 76 | Usually, a BASIC program can be halted with a CTRL+C. As there are no control keys on the terminal, the square root key has been chosen for that. 77 | -------------------------------------------------------------------------------- /BasicCheatsheet.md: -------------------------------------------------------------------------------- 1 | BASIC Language 2 | -------------- 3 | Variables names can be up to 8 alphanumeric characters but start with a letter e.g. a, bob32 4 | String variable names must end in $ e.g. a$, bob32$ 5 | Case is ignored (for all identifiers). BOB32 is the same as Bob32. print is the same as PRINT 6 | 7 | Array variables are independent from normal variables. So you can use both: 8 | ``` 9 | LET a = 5 10 | DIM a(10) 11 | ``` 12 | There is no ambiguity, since a on its own refers to the simple variable 'a', and a(n) referes to an element of the 'a' array. 13 | 14 | ``` 15 | Arithmetic operators: + - * / MOD 16 | Comparisons: <> (not equals) > >= < <= 17 | Logical: AND, OR, NOT 18 | ``` 19 | 20 | Expressions can be numerical e.g. 5*(3+2), or string "Hello "+"world". 21 | Only the addition operator is supported on strings (plus the functions below). 22 | 23 | Commands 24 | ``` 25 | PRINT ;... e.g. PRINT "A=";a 26 | LET variable = e.g. LET A$="Hello". 27 | variable = e.g. A=5 28 | LIST [start],[end] e.g. LIST or LIST 10,100 29 | RUN [lineNumber] 30 | GOTO lineNumber 31 | REM e.g. REM ** My Program *** 32 | STOP 33 | CONT (continue from a STOP) 34 | INPUT variable e.g. INPUT a$ or INPUT a(5,3) 35 | IF THEN cmd e.g. IF a>10 THEN a = 0: GOTO 20 36 | FOR variable = start TO end STEP step 37 | NEXT variable 38 | NEW 39 | GOSUB lineNumber 40 | RETURN 41 | DIM variable(n1,n2...) 42 | CLS 43 | SLEEP put terminal in low power mode, exit with power button 44 | PAUSE milliseconds 45 | POSITION x,y sets the cursor 46 | PIN pinNum, value (0 = low, non-zero = high) 47 | PINMODE pinNum, mode ( 0 = input, 1 = output) 48 | LOAD (from internal EEPROM) 49 | SAVE (to internal EEPROM) e.g. use SAVE + to set auto-run on boot flag 50 | LOAD "filename", SAVE "filename, DIR, DELETE "filename" if using with external EEPROM or SD card. Concerning SD card, the filename is limited to 8 characters. the software adds the ".BAS" extension. Concerning SD card, the filename is limited to 8 characters. the software adds the ".BAS" extension. 51 | MOUNT force the detection of the SD card. Necessary if it has been inserted when the terminal is running. Mount is made at reset and when restarting after a sleep 52 | UNMOUNT close all the files and prepare the card to be extracted. UNMMOUNT is made when entering sleep. 53 | TONE frequency_to_play Play a sound in the piezo buzzer 54 | NOTONE stop the tone 55 | POKE address, value write value in memory at address 56 | ``` 57 | 58 | "Pseudo-identifiers" 59 | ``` 60 | INKEY$ - returns (and eats) the last key pressed buffer (non-blocking). e.g. PRINT INKEY$ 61 | RND - random number betweeen 0 and 1. e.g. LET a = RND 62 | ``` 63 | 64 | Functions 65 | ``` 66 | LEN(string) e.g. PRINT LEN("Hello") -> 5 67 | ASC(string) e.g. PRINT ASC("ABCD") -> 65 (ASCII code of A) 68 | VAL(string) e.g. PRINT VAL("1+2") 69 | INT(number) e.g. INT(1.5)-> 1 70 | STR$(number) e.g. STR$(2) -> "2" 71 | LEFT$(string,n) 72 | RIGHT$(string,n) 73 | MID$(string,start,n) 74 | PINREAD(pin) - see Arduino digitalRead() 75 | ANALOGRD(pin) - see Arduino analogRead() 76 | PEEK(address) - display content of memory location address 77 | ``` 78 | Usually, a BASIC program can be halted with a CTRL+C. As there are no control keys on the terminal, the square root key has been chosen for that. 79 | -------------------------------------------------------------------------------- /host.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // put 0 if you don't want to display the splashscreen 6 | // very flash-mem hungry 7 | #define SHOW_SPLASHSCREEN 1 8 | 9 | // set display between two status updates 10 | #define STATUS_RATE (2000UL) 11 | 12 | // that is size of display when using the small font 13 | #define MAX_SCREEN_WIDTH 64 14 | #define MAX_SCREEN_HEIGHT 16 15 | 16 | #define KEY_DELETE 0x08 17 | #define KEY_ENTER 0x0D 18 | #define KEY_ESC 0x1B 19 | 20 | // Put 1 if you use an external EEPROM on I2C bus 21 | // NOTE 22 | // ONLY ONE OF EXTERNAL_EEPROM OR SD_CARD CAN BE ACTIVE AT THE SAME TIME ON A CONFIGURATION 23 | #define EXTERNAL_EEPROM 0 24 | #define EXTERNAL_EEPROM_ADDR 0x50 // I2C address (7 bits) 25 | #define EXTERNAL_EEPROM_SIZE 32768 // only <=32k tested (64k might work?) 26 | 27 | #define MAGIC_AUTORUN_NUMBER 0xFC 28 | 29 | // fdufnews 28/07/2020 30 | // Put 1 if using an SD card on SPI 31 | // NOTE 32 | // ONLY ONE OF EXTERNAL_EEPROM OR SD_CARD CAN BE ACTIVE AT THE SAME TIME ON A CONFIGURATION 33 | #define SD_CARD 1 34 | #define SD_CARD_CS FLASH_CS 35 | 36 | #if EXTERNAL_EEPROM && SD_CARD 37 | #error "EXTERNAL_EEPROM and SD_CARD cannot be active at the same time" 38 | #endif 39 | 40 | 41 | void host_init(int buzzer_Pin, int led_Pin); 42 | void host_sleep(long ms); 43 | void host_digitalWrite(int pin, int state); 44 | int host_digitalRead(int pin); 45 | int host_analogRead(int pin); 46 | void host_pinMode(int pin, int mode); 47 | void host_click(); 48 | void host_startupTone(); 49 | void host_notone(); 50 | void host_tone(int note); 51 | unsigned int host_BASICFreeMem(void); 52 | unsigned int host_CFreeMem(void); 53 | void host_setFont(unsigned char fontNum); 54 | void host_setBatStat(void); 55 | float host_getBatStat(void); 56 | void host_printStatus(boolean now); 57 | boolean host_setLineWrap(boolean wrap); 58 | void host_cls(); 59 | void host_showBuffer(); 60 | void host_moveCursor(int x, int y); 61 | void host_outputString(char *str); 62 | void host_outputProgMemString(const char *str); 63 | void host_outputChar(char c); 64 | void host_outputFloat(float f); 65 | char *host_floatToStr(float f, char *buf); 66 | int host_outputInt(long val); 67 | #if SHOW_SPLASHSCREEN 68 | void host_splashscreen(void); 69 | #endif 70 | void host_newLine(); 71 | void host_newLine(byte num); 72 | void host_goToSleep(); 73 | char *host_readLine(); 74 | char host_getKey(); 75 | bool host_ESCPressed(); 76 | void host_outputFreeMem(unsigned int val); 77 | void host_saveProgram(bool autoexec); 78 | void host_loadProgram(); 79 | 80 | #if EXTERNAL_EEPROM 81 | #include 82 | void writeExtEEPROM(unsigned int address, byte data); 83 | void host_directoryExtEEPROM(); 84 | bool host_saveExtEEPROM(char *fileName); 85 | bool host_loadExtEEPROM(char *fileName); 86 | bool host_removeExtEEPROM(char *fileName); 87 | #endif 88 | 89 | #if SD_CARD 90 | #include 91 | #include 92 | void host_directorySD(void); 93 | void printDirectory(File dir, int deep); 94 | boolean host_saveSD(char *filename); 95 | boolean host_loadSD(char *filename); 96 | boolean host_removeSD(char *filename); 97 | void host_mountSD(void); 98 | void host_unmountSD(void); 99 | /* 100 | void host_openSD(); 101 | void host_readSD(); 102 | void host_writeSD(); 103 | */ 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /SMART_Response_BASIC.ino: -------------------------------------------------------------------------------- 1 | 2 | /* This is SMART Response XE Basic 3 | A basic interpreter for the SMART Response XE terminal 4 | This BASIC interpreter is based on Robin Edwards work (see note in basic.cpp) 5 | 6 | fdufnews 11/2019 7 | 8 | TODO larger tokenBuf 9 | 10 | 01/2020 11 | added support for a status line at bottom of screen 12 | 13 | 07/2020 14 | added support for an SD card 15 | * */ 16 | 17 | #include 18 | 19 | #include "basic.h" 20 | #include "host.h" 21 | 22 | // Define in host.h if using an SD card 23 | // should be connected to the SPI lines 24 | // 25 | 26 | #if SD_CARD 27 | #include 28 | #include 29 | #endif 30 | 31 | // Define in host.h if using an external EEPROM e.g. 24LC256 32 | // Should be connected to the I2C pins 33 | // SDA -> Analog Pin 4, SCL -> Analog Pin 5 34 | // See e.g. http://www.hobbytronics.co.uk/arduino-external-eeprom 35 | 36 | // If using an external EEPROM, you'll also have to initialise it by 37 | // running once with the appropriate lines enabled in setup() - see below 38 | // 39 | 40 | #if EXTERNAL_EEPROM 41 | #include 42 | // Instance of class for hardware master with pullups enabled 43 | TwiMaster rtc(true); 44 | #endif 45 | 46 | 47 | // buzzer pin, 0 = disabled/not present 48 | #define BUZZER_PIN BUZZER 49 | // LED pin, 0 = disabled/not present, LED uses the default LED of the board 50 | #define LED_PIN LED 51 | 52 | // BASIC 53 | unsigned char mem[MEMORY_SIZE]; 54 | #define TOKEN_BUF_SIZE 64 55 | unsigned char tokenBuf[TOKEN_BUF_SIZE]; 56 | 57 | const char welcomeStr[] PROGMEM = "SMART Response BASIC"; 58 | char autorun = 0; 59 | 60 | void setup() { 61 | 62 | reset(); 63 | 64 | host_init(BUZZER_PIN, LED_PIN); // Initializes I/O and clear screen 65 | host_cls(); // clear screen buffer 66 | host_outputProgMemString(welcomeStr); // greeting message 67 | // show memory size 68 | host_outputFreeMem(sysVARSTART - sysPROGEND); 69 | host_showBuffer(); // display buffer on screen 70 | host_setBatStat(); 71 | // IF USING EXTERNAL EEPROM 72 | // The following line 'wipes' the external EEPROM and prepares 73 | // it for use. Uncomment it, upload the sketch, then comment it back 74 | // in again and upload again, if you use a new EEPROM. 75 | // writeExtEEPROM(0,0); writeExtEEPROM(1,0); 76 | 77 | #if EXTERNAL_EEPROM 78 | if (EEPROM.read(0) == MAGIC_AUTORUN_NUMBER) 79 | autorun = 1; 80 | else 81 | host_startupTone(); 82 | #endif 83 | } 84 | 85 | void loop() { 86 | int ret = ERROR_NONE; 87 | 88 | //host_printStatus(); 89 | if (!autorun) { 90 | // get a line from the user 91 | char *input = host_readLine(); 92 | // special editor commands 93 | if (input[0] == '?' && input[1] == 0) { 94 | host_outputFreeMem(sysVARSTART - sysPROGEND); 95 | host_showBuffer(); 96 | return; 97 | } 98 | // otherwise tokenize 99 | ret = tokenize((unsigned char*)input, tokenBuf, TOKEN_BUF_SIZE); 100 | } 101 | else { 102 | host_loadProgram(); 103 | tokenBuf[0] = TOKEN_RUN; 104 | tokenBuf[1] = 0; 105 | autorun = 0; 106 | } 107 | 108 | // execute the token buffer 109 | if (ret == ERROR_NONE) { 110 | host_newLine(); 111 | ret = processInput(tokenBuf); 112 | } 113 | if (ret != ERROR_NONE) { 114 | host_newLine(); 115 | if (lineNumber != 0) { 116 | host_outputInt(lineNumber); 117 | host_outputChar('-'); 118 | } 119 | host_outputProgMemString((char *)pgm_read_word(&(errorTable[ret]))); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /README_original.md: -------------------------------------------------------------------------------- 1 | Arduino Basic 2 | ============= 3 | Now you can turn your Arduino into an 80's home computer! 4 | 5 | A complete BASIC interpreter for the Arduino, using a PS/2 keyboard, and SPI OLED screen. The BASIC supports almost all the usual features, with float and string variables, multi-dimensional arrays, FOR-NEXT, GOSUB-RETURN, etc. Saving and Loading from EEPROM is supported, as well as auto-running a program on power-up. You can also read and write from the analog and digital pins. 6 | 7 | There's about 1k of RAM available for your BASIC programs and variables, so its roughly equivalent to my first computer (a Sinclair ZX81). The other 1k of RAM (on an UNO) is used for the keyboard and screen buffers, with a small bit of room left for the CPU stack. That works out quite well, since there's a 1k EEPROM on the arduino so if your program fits in the basic environment, it will fit when saved to EEPROM! 8 | 9 | [![Demo](http://img.youtube.com/vi/JB5RXoO1IwQ/0.jpg)](http://www.youtube.com/watch?v=JB5RXoO1IwQ) 10 | 11 | Prerequisites 12 | ------------- 13 | 1: An Arduino with at least 2k of RAM. i.e. UNO/Duemilanove/etc. It should also work on the MEGA but is untested. I think the sketch is too big for the Leonardo, since it looks like the bootloader uses more RAM on this model. 14 | 15 | 2: A PS/2 Keyboard. See http://playground.arduino.cc/Main/PS2Keyboard for wiring details. 16 | 17 | 3: An SSD1306 based OLED Screen connected using SPI. See e.g. http://www.adafruit.com/product/938. I think it should also work fine with the 128x32 version - you'll just need to change a couple of defines. 18 | 19 | 4: (Optional) A Piezoelectric buzzer for keyboard clicks and other sounds. 20 | 21 | 5: (Optional) A external EEPROM (e.g. 24LC256) lets you save more than one file. You can pick these up for about £2/$2 on ebay. 22 | 23 | Getting Started 24 | --------------- 25 | 1: Download the zip file, unpack and copy the *folder* to your arduino sketches directory. 26 | 27 | 2: Install the PS/2 keyboard library if required. I've included the version I used in the zip file. 28 | 29 | 3: Install the SSD1306ASCII library. The normal Adafruit library is too RAM hungry for this project, so I'm using a massively cut down driver instead. I've modified this library a bit to get fast SPI transfers which improved the screen updating speed by a factor of about 4. The modified version is included in the zip file. 30 | 31 | For both libraries, unzip the files and copy the *folder* to your arduino libraries directory. 32 | 33 | 4: Check your wiring corresponds to the pins in the comments/defines at the top of the Arduino_BASIC file. 34 | 35 | BASIC Language 36 | -------------- 37 | Variables names can be up to 8 alphanumeric characters but start with a letter e.g. a, bob32 38 | String variable names must end in $ e.g. a$, bob32$ 39 | Case is ignored (for all identifiers). BOB32 is the same as Bob32. print is the same as PRINT 40 | 41 | Array variables are independent from normal variables. So you can use both: 42 | ``` 43 | LET a = 5 44 | DIM a(10) 45 | ``` 46 | There is no ambiguity, since a on its own refers to the simple variable 'a', and a(n) referes to an element of the 'a' array. 47 | 48 | ``` 49 | Arithmetic operators: + - * / MOD 50 | Comparisons: <> (not equals) > >= < <= 51 | Logical: AND, OR, NOT 52 | ``` 53 | 54 | Expressions can be numerical e.g. 5*(3+2), or string "Hello "+"world". 55 | Only the addition operator is supported on strings (plus the functions below). 56 | 57 | Commands 58 | ``` 59 | PRINT ;... e.g. PRINT "A=";a 60 | LET variable = e.g. LET A$="Hello". 61 | variable = e.g. A=5 62 | LIST [start],[end] e.g. LIST or LIST 10,100 63 | RUN [lineNumber] 64 | GOTO lineNumber 65 | REM e.g. REM ** My Program *** 66 | STOP 67 | CONT (continue from a STOP) 68 | INPUT variable e.g. INPUT a$ or INPUT a(5,3) 69 | IF THEN cmd e.g. IF a>10 THEN a = 0: GOTO 20 70 | FOR variable = start TO end STEP step 71 | NEXT variable 72 | NEW 73 | GOSUB lineNumber 74 | RETURN 75 | DIM variable(n1,n2...) 76 | CLS 77 | PAUSE milliseconds 78 | POSITION x,y sets the cursor 79 | PIN pinNum, value (0 = low, non-zero = high) 80 | PINMODE pinNum, mode ( 0 = input, 1 = output) 81 | LOAD (from internal EEPROM) 82 | SAVE (to internal EEPROM) e.g. use SAVE + to set auto-run on boot flag 83 | LOAD "filename", SAVE "filename, DIR, DELETE "filename" if using with external EEPROM. 84 | ``` 85 | 86 | "Pseudo-identifiers" 87 | ``` 88 | INKEY$ - returns (and eats) the last key pressed buffer (non-blocking). e.g. PRINT INKEY$ 89 | RND - random number betweeen 0 and 1. e.g. LET a = RND 90 | ``` 91 | 92 | Functions 93 | ``` 94 | LEN(string) e.g. PRINT LEN("Hello") -> 5 95 | VAL(string) e.g. PRINT VAL("1+2") 96 | INT(number) e.g. INT(1.5)-> 1 97 | STR$(number) e.g. STR$(2) -> "2" 98 | LEFT$(string,n) 99 | RIGHT$(string,n) 100 | MID$(string,start,n) 101 | PINREAD(pin) - see Arduino digitalRead() 102 | ANALOGRD(pin) - see Arduino analogRead() 103 | ``` 104 | -------------------------------------------------------------------------------- /basic.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASIC_H 2 | #define _BASIC_H 3 | 4 | #include 5 | 6 | #define TOKEN_EOL 0 7 | #define TOKEN_IDENT 1 // special case - identifier follows 8 | #define TOKEN_INTEGER 2 // special case - integer follows (line numbers only) 9 | #define TOKEN_NUMBER 3 // special case - number follows 10 | #define TOKEN_STRING 4 // special case - string follows 11 | 12 | #define TOKEN_LBRACKET 8 13 | #define TOKEN_RBRACKET 9 14 | #define TOKEN_PLUS 10 15 | #define TOKEN_MINUS 11 16 | #define TOKEN_MULT 12 17 | #define TOKEN_DIV 13 18 | #define TOKEN_EQUALS 14 19 | #define TOKEN_GT 15 20 | #define TOKEN_LT 16 21 | #define TOKEN_NOT_EQ 17 22 | #define TOKEN_GT_EQ 18 23 | #define TOKEN_LT_EQ 19 24 | #define TOKEN_CMD_SEP 20 25 | #define TOKEN_SEMICOLON 21 26 | #define TOKEN_COMMA 22 27 | #define TOKEN_AND 23 // FIRST_IDENT_TOKEN 28 | #define TOKEN_OR 24 29 | #define TOKEN_NOT 25 30 | #define TOKEN_PRINT 26 31 | #define TOKEN_LET 27 32 | #define TOKEN_LIST 28 33 | #define TOKEN_RUN 29 34 | #define TOKEN_GOTO 30 35 | #define TOKEN_REM 31 36 | #define TOKEN_STOP 32 37 | #define TOKEN_INPUT 33 38 | #define TOKEN_CONT 34 39 | #define TOKEN_IF 35 40 | #define TOKEN_THEN 36 41 | #define TOKEN_LEN 37 42 | #define TOKEN_VAL 38 43 | #define TOKEN_RND 39 44 | #define TOKEN_INT 40 45 | #define TOKEN_STR 41 46 | #define TOKEN_FOR 42 47 | #define TOKEN_TO 43 48 | #define TOKEN_STEP 44 49 | 50 | #define TOKEN_NEXT 45 51 | #define TOKEN_MOD 46 52 | #define TOKEN_NEW 47 53 | #define TOKEN_GOSUB 48 54 | #define TOKEN_RETURN 49 55 | #define TOKEN_DIM 50 56 | #define TOKEN_LEFT 51 57 | #define TOKEN_RIGHT 52 58 | #define TOKEN_MID 53 59 | #define TOKEN_CLS 54 60 | #define TOKEN_PAUSE 55 61 | #define TOKEN_POSITION 56 62 | #define TOKEN_PIN 57 63 | #define TOKEN_PINMODE 58 64 | #define TOKEN_INKEY 59 65 | #define TOKEN_SAVE 60 66 | #define TOKEN_LOAD 61 67 | #define TOKEN_PINREAD 62 68 | #define TOKEN_ANALOGRD 63 69 | #define TOKEN_DIR 64 70 | #define TOKEN_DELETE 65 71 | // fdufnews 2019/12 72 | // added ASC(string) 73 | // added SLEEP 74 | #define TOKEN_ASC 66 75 | #define TOKEN_SLEEP 67 76 | // fdufnews 2020/07 77 | #define TOKEN_TONE 68 78 | #define TOKEN_NOTONE 69 79 | // fdufnews 2020/08/06 80 | #define TOKEN_MOUNT 70 81 | #define TOKEN_UNMOUNT 71 82 | // fdufnews 2021/04/01 83 | #define TOKEN_HELP 72 84 | 85 | // fdufnews 2021/07/22 86 | #define TOKEN_PEEK 73 87 | #define TOKEN_POKE 74 88 | 89 | 90 | #define FIRST_IDENT_TOKEN 23 91 | #define LAST_IDENT_TOKEN 74 92 | 93 | #define FIRST_NON_ALPHA_TOKEN 8 94 | #define LAST_NON_ALPHA_TOKEN 22 95 | 96 | #define ERROR_NONE 0 97 | // parse errors 98 | #define ERROR_LEXER_BAD_NUM 1 99 | #define ERROR_LEXER_TOO_LONG 2 100 | #define ERROR_LEXER_UNEXPECTED_INPUT 3 101 | #define ERROR_LEXER_UNTERMINATED_STRING 4 102 | #define ERROR_EXPR_MISSING_BRACKET 5 103 | #define ERROR_UNEXPECTED_TOKEN 6 104 | #define ERROR_EXPR_EXPECTED_NUM 7 105 | #define ERROR_EXPR_EXPECTED_STR 8 106 | #define ERROR_LINE_NUM_TOO_BIG 9 107 | // runtime errors 108 | #define ERROR_OUT_OF_MEMORY 10 109 | #define ERROR_EXPR_DIV_ZERO 11 110 | #define ERROR_VARIABLE_NOT_FOUND 12 111 | #define ERROR_UNEXPECTED_CMD 13 112 | #define ERROR_BAD_LINE_NUM 14 113 | #define ERROR_BREAK_PRESSED 15 114 | #define ERROR_NEXT_WITHOUT_FOR 16 115 | #define ERROR_STOP_STATEMENT 17 116 | #define ERROR_MISSING_THEN 18 117 | #define ERROR_RETURN_WITHOUT_GOSUB 19 118 | #define ERROR_WRONG_ARRAY_DIMENSIONS 20 119 | #define ERROR_ARRAY_SUBSCRIPT_OUT_RANGE 21 120 | #define ERROR_STR_SUBSCRIPT_OUT_RANGE 22 121 | #define ERROR_IN_VAL_INPUT 23 122 | #define ERROR_BAD_PARAMETER 24 123 | 124 | #define MAX_IDENT_LEN 8 125 | #define MAX_FILENAME_LEN 12 126 | #define MAX_NUMBER_LEN 10 127 | 128 | // size of BASIC memory space this is the size allocated to mem[] which is where program and data are located 129 | #define MEMORY_SIZE (10L*1024L) 130 | extern unsigned char mem[]; 131 | extern int sysPROGEND; // end of program (beginning of mem) 132 | extern int sysSTACKSTART; // start of evaluation stack 133 | extern int sysSTACKEND; // end of evaluation stack 134 | extern int sysVARSTART; // start of variable space 135 | extern int sysVAREND; // end of variable space 136 | extern int sysGOSUBSTART; // start of gosub stack 137 | extern int sysGOSUBEND; // end of gosub stack (end of mem) 138 | 139 | extern uint16_t lineNumber; // 0 = input buffer 140 | 141 | typedef struct { 142 | float val; 143 | float step; 144 | float end; 145 | uint16_t lineNumber; 146 | uint16_t stmtNumber; 147 | } 148 | ForNextData; 149 | 150 | typedef struct { 151 | char *token; 152 | uint8_t format; 153 | } 154 | TokenTableEntry; 155 | 156 | //extern const char *errorTable[]; 157 | extern const char* const errorTable[]; 158 | 159 | 160 | void reset(); 161 | int tokenize(unsigned char *input, unsigned char *output, int outputSize); 162 | int processInput(unsigned char *tokenBuf); 163 | 164 | #endif 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SMART Response Basic 2 | ============= 3 | 4 | The SMART Response Basic is based on the original work of [Robin Edwards](https://github.com/robinhedwards/ArduinoBASIC). 5 | 6 | Original README.md file can be [read here](README_original.md) 7 | 8 | My aim is to make a mobile programming platform that can be used on the lab or on the road to test components. 9 | 10 | The SMART Response XE terminal is a very attractive platform : 11 | * 384x136 pixels LCD screen, 12 | * a good keyboard, 13 | * a CPU with 128kB of program flash memory, 16kB of RAM, 4k of EEPROM 14 | * 128kB of external flash 15 | * it can work for hours on 4 AAA batteries. 16 | 17 | Nevertheless, there are drawbacks. The main one is how the display works. There is a memory plane inside the display that hold the graphic but this memory cannot be read back as the display interface is in serial mode. This make it difficult to make addition of graphics over existing ones. Especially as the memory is written 3 pixels at a time. If you want to toggle one pixel you can potentially overwrite two others. 18 | 19 | So, at first I will try to make BASIC work in text mode only. 20 | 21 | TODO list 22 | ---------- 23 | - [x] make BASIC work with keyboard and LCD display without any other modification 24 | - [ ] enhance display functions 25 | - [ ] use hardware scroll to speed up display (coded but not currently in use) 26 | - [x] font change and dynamic adaptation to font size 27 | - [x] use bottom line of screen to show status of terminal (battery status, font in use, line wrap, free mem...) 28 | - [x] add save/restore BASIC programs using ~~Flash memory of the terminal~~ an SD card 29 | - [ ] add SPI support 30 | - [ ] add I2C support 31 | 32 | Prerequisites 33 | ------------- 34 | 1. A SMART Response XE terminal. 35 | 36 | 2. (Optional) A Piezoelectric buzzer for keyboard clicks and other sounds [see hack on the github of the hardware](https://github.com/fdufnews/SMART-Response-XE-schematics). 37 | 38 | 3. (Optional) An LED to show some status or to help debug software [see hack on the github of the hardware](https://github.com/fdufnews/SMART-Response-XE-schematics). 39 | 40 | 4. (Optionnal) An SD card reader in order to save/restore progr to show some status or to help debug software [see hack on the github of the hardware](https://github.com/fdufnews/SMART-Response-XE-schematics). 41 | 42 | 5. (Optionnal) An SD card reader in order to save/restore program [see hack on the github of the hardware](https://github.com/fdufnews/SMART-Response-XE-schematics). 43 | 44 | 45 | 46 | Getting started 47 | ------------- 48 | 49 | 1. Download the zip file, unpack and copy the *folder* to your arduino sketches directory. 50 | 51 | 2. Install the [SMART response XE library](https://github.com/fdufnews/SMART-Response-XE-Low_level) it is [bitbank2's library](https://github.com/bitbank2/SmartResponseXE) with some modification. Unzip the file and copy the *folder* in a hardware directory as explained in the readme.md of the library. 52 | 3. Install the [MemoryFree Library](https://github.com/McNeight/MemoryFree) 53 | 4. You may want to add/suppress some features. 54 | 55 | a. in host.h, SHOW_SPLASHSCREEN tells the compiler to add or not the splashscreen when the terminal is powered on. 56 | 57 | b. in host.h, SD_CARD tells the compiler to add or not support for an sd card 58 | 59 | c. in basic.h MEMORY_SIZE can change the size of the memory allocated to basic 60 | 61 | BASIC Language 62 | -------------- 63 | See [Basic Cheatsheet](BasicCheatsheet.md) 64 | 65 | 66 | Configuration / Status 67 | ------------- 68 | In any mode, 8 lines are kept on bottom of screen to display status of the system. 69 | First the state of the battery, the font used, the state of the SD card, the memory usage. 70 | 71 | * The system gives the user the option to switch from one font to another. There are 4 fonts: 72 | * **normal**, using a 9x8 matrix and giving 17 lines of 42 characters 73 | * **small**, using a 6x8 matrix and giving 17 lines of 64 characters (really, really small difficult to use for poor old eyes)SHOW_SPLASHSCREENSHOW_SPLASHSCREEN 74 | * **medium**, using a 12x16 matrix and giving 8 lines of 32 characters 75 | * **large**, using a 15x16 matrix and giving 8 lines of 25 characters 76 | The medium and large font leave 8 lines at bottom of screen so it was decided to do the same for the normal and small one. Theese 8 lines are used to display the status. 77 | * SD card state, can be one of: 78 | * NO SD CARD 79 | * SD CARD OK 80 | * the name of the last loaded or saved file 81 | * Memory usage displays 2 numbers: 82 | * BASIC free mem, the BASIC interpreter has a reserved buffer used to store program and data. The value displayed gives how much of the buffer is free 83 | * C free mem, the second number is the size of the stack left to the C that is running the interpreter 84 | 85 | Currently, BASIC has a 10kB buffer and the remaining 6kB are for C. After setup, there are a bit less than 4kB of free RAM for C. As much of the memory is allocated during setup, 4kB seems to be very conservative. It can probably be reduced if BASIC interpreter needs more free memory. MEMORY_SIZE, defined in basic.h, defines the size of the BASIC memory. For detailed information on memory usage see [memory.md](memory.md) 86 | 87 | Not part of the BASIC language, there is support for the MENU key (SYM + space). 88 | That key is used to make a call to the host_setConf function in which user can change the font used with the display. 89 | You can see you have entered configuration mode as the cursor stops blinking. Using UP and DOWN keys the user can change the font. The name of the selected font is displayed on the status line. 90 | You leave configuration mode by pressing ENTER (DEL key) or by pressing MENU (SYM + space). 91 | The screen buffer is cleared, but the current line is kept and displayed on top with cursor at the right position, if user was currently editing a line when switching to configuration mode. 92 | 93 | Usually, a BASIC program can be halted with a CTRL+C. As there are no control keys on the terminal, the square root key has been chosen for that. 94 | -------------------------------------------------------------------------------- /host.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | fdufnews 3 | 12/2019 4 | 5 | Added support to SMART Response XE using SmartResponseXE library 6 | use terminal keyboard for input and display text on its LCD screen 7 | use ON/OFF button to put terminal in sleep mode 8 | added host_setFont to select active font 9 | added host_setLineWrap to choose between line wrap or truncated line 10 | added fontStatus structure to retrieve information relative to the text interface (size of font, nb car per line and line per screen) 11 | added setConf in order to change font (only at command line level) 12 | added support of MENU key which calls setConf to change font 13 | 14 | DONE: ADC configuration (using 1.6V internal ref) 15 | DONE: battery management, display battery state 16 | DONE 01/2020: font as an option --> dy// fdufnews 6/08/2020namic screen width and screen height 17 | 2/3 DONE: last line used for status (battery state, font used, freemem) but issue with battery state 18 | 19 | fdufnews 6/08/2020 20 | added support for an SD card 21 | added save, load, dir, mount, unmount statements to manage the SD card 22 | */ 23 | 24 | #include "host.h" 25 | #include "basic.h" 26 | 27 | #include "logo2_rle.h" 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | extern EEPROMClass EEPROM; 35 | int timer1_counter; 36 | 37 | // Allocates a buffer sized for the larger screen mode 38 | char screenBuffer[MAX_SCREEN_WIDTH * MAX_SCREEN_HEIGHT]; 39 | char lineDirty[MAX_SCREEN_HEIGHT]; 40 | int curX = 0, curY = 0; // current position in the screen buffer 41 | byte lineCount=0; // count how many lines were scrolled (for dir and list) 42 | volatile char flash = 0, redraw = 0; 43 | char inputMode = 0; 44 | char inkeyChar = 0; 45 | char buzPin = 0; // not 0 if buzzer present 46 | char ledPin = 0; // not 0 if LED wired 47 | char ledState = 0; // current state of the LED (to restore state after a sleep) 48 | 49 | #if SD_CARD 50 | #include 51 | #include 52 | 53 | boolean sdCardPresent = false; // true if an SD card has been initialized successfully 54 | const char ok_card[] PROGMEM = " SD CARD OK "; 55 | const char ko_card[] PROGMEM = " NO SD CARD "; 56 | #endif 57 | char currentFile[13] = " NO SD CARD "; 58 | 59 | char *fontName[4] = {"NORMAL", "SMALL", "MEDIUM", "LARGE"}; 60 | 61 | struct sizeOfFont { 62 | unsigned char width; 63 | unsigned char height; 64 | }; 65 | struct sizeOfFont fontSize[4] = {{9, 8}, {6, 8}, {12, 16}, {15, 16}}; 66 | 67 | struct sizeOfScreen { 68 | unsigned char nbCar; 69 | unsigned char nbLine; 70 | }; 71 | // note that the screenSize definitions here under reserve one block of 8 lines to display status in all modes 72 | struct sizeOfScreen screenSize[4] = {{42, 16}, {64, 16}, {32, 8}, {25, 8}}; 73 | 74 | // this structure holds all the information concerning the current screen configuration 75 | // it is updated every time a new font is selected. 76 | struct { 77 | unsigned char currentFont; // font currently in use 78 | unsigned char width; // width of the font 79 | unsigned char height; // height of the font 80 | unsigned char nbCar; // number of car per line 81 | unsigned char nbLine; // number of line per screen 82 | unsigned int bufSize; // size of the screen buffer 83 | boolean lineWrap; // line wrap flag (not used) 84 | } fontStatus = {FONT_NORMAL, 9, 8, 42, 16, 42 * 16, true}; 85 | 86 | const char bytesFreeStr[] PROGMEM = "bytes free"; 87 | 88 | void initTimer() { 89 | noInterrupts(); // disable all interrupts 90 | TCCR1A = 0; 91 | TCCR1B = 0; 92 | timer1_counter = 34286; // preload timer 65536-16MHz/256/2Hz 93 | TCNT1 = timer1_counter; // preload timer 94 | TCCR1B |= (1 << CS12); // 256 prescaler 95 | TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt 96 | interrupts(); // enable all interrupts 97 | } 98 | 99 | ISR(TIMER1_OVF_vect) // interrupt service routine 100 | { 101 | TCNT1 = timer1_counter; // preload timer 102 | flash = !flash; 103 | redraw = 1; 104 | } 105 | 106 | /* 107 | * host_init 108 | * Init parts specific to the host (display, dedicated I/Os) 109 | * 110 | * 111 | */ 112 | void host_init(int buzzer_Pin, int led_Pin) { 113 | char ledState = 0; 114 | 115 | // Serial.begin(115200); 116 | buzPin = buzzer_Pin; 117 | ledPin = led_Pin; 118 | SRXEInit(0xe7, 0xd6, 0xa2); // initialize and clear display // CS, D/C, RESET 119 | host_setFont(FONT_NORMAL); 120 | SRXEFill(0); // clear display 121 | // set on/off button as input 122 | pinMode(POWER_BUTTON, INPUT_PULLUP); 123 | // Blink the LED 124 | #if SHOW_SPLASHSCREEN 125 | host_splashscreen(); // displays the splashscreen 126 | #endif 127 | if (buzPin) 128 | pinMode(buzPin, OUTPUT); 129 | SRXEFill(0); // clear display 130 | initTimer(); 131 | #if SD_CARD 132 | if (sdCardPresent = SD.begin(SD_CARD_CS)) // true if an sd card is present 133 | strcpy_P(currentFile, ok_card); 134 | else 135 | strcpy_P(currentFile, ko_card); 136 | #endif 137 | host_setBatStat(); // Initialize A0 for battery measurement 138 | } 139 | 140 | void host_sleep(long ms) { 141 | delay(ms); 142 | } 143 | 144 | void host_digitalWrite(int pin, int state) { 145 | if ((ledPin != 0) && (pin == ledPin)) // If LED present and we are modifying its state 146 | ledState = state; // save its new state. 147 | digitalWrite(pin, state ? HIGH : LOW); 148 | } 149 | 150 | int host_digitalRead(int pin) { 151 | return digitalRead(pin); 152 | } 153 | 154 | int host_analogRead(int pin) { 155 | return analogRead(pin); 156 | } 157 | 158 | void host_pinMode(int pin, int mode) { 159 | pinMode(pin, mode); 160 | } 161 | 162 | /* 163 | * host_click 164 | * Generates a short click 165 | * 166 | */ 167 | void host_click() { 168 | if (!buzPin) return; 169 | digitalWrite(buzPin, HIGH); 170 | delay(1); 171 | digitalWrite(buzPin, LOW); 172 | } 173 | 174 | /* 175 | * host_startupTone 176 | * A short "melody" after a reset 177 | * 178 | */ 179 | void host_startupTone() { 180 | if (!buzPin) return; 181 | for (int i = 1; i <= 2; i++) { 182 | for (int j = 0; j < 50 * i; j++) { 183 | digitalWrite(buzPin, HIGH); 184 | delay(3 - i); 185 | digitalWrite(buzPin, LOW); 186 | delay(3 - i); 187 | } 188 | delay(100); 189 | } 190 | } 191 | 192 | // host_notone 193 | // halt tone generated on buzpin 194 | // 195 | void host_notone() { 196 | if (!buzPin) return; 197 | noTone(buzPin); 198 | } 199 | 200 | // host_tone 201 | // generates a tone on buzpin 202 | // 203 | void host_tone(int note) { 204 | if (!buzPin) return; 205 | tone(buzPin, note); 206 | } 207 | 208 | // host_BASICFreeMem 209 | // returns free BASIC memory 210 | // 211 | unsigned int host_BASICFreeMem(void) { 212 | return sysVARSTART - sysSTACKEND; 213 | } 214 | 215 | // host_CFreeMem 216 | // returns free C memory 217 | // 218 | unsigned int host_CFreeMem(void) { 219 | return freeMemory(); 220 | } 221 | 222 | // host_setFont 223 | // set the active font used to display text on screen 224 | // input fontNum shall be one of FONT_NORMAL, FONT_SMALL, FONT_MEDIUM, FONT_LARGE 225 | // output nothing 226 | // updates fontStatus structure with new parameters 227 | // 228 | void host_setFont(unsigned char fontNum) { 229 | if (fontNum < 0 || fontNum > 3) return ; 230 | fontStatus.currentFont = fontNum; 231 | fontStatus.width = fontSize[fontNum].width; 232 | fontStatus.height = fontSize[fontNum].height; 233 | fontStatus.nbCar = screenSize[fontNum].nbCar; 234 | fontStatus.nbLine = screenSize[fontNum].nbLine; 235 | fontStatus.bufSize = fontStatus.nbCar * fontStatus.nbLine; 236 | } 237 | 238 | // host_setBatStat 239 | // set ADC configuration 240 | // Select 1.6V ref voltage 241 | // Select A0 as input 242 | void host_setBatStat(void) { 243 | ADCSRA = 0; // Disable ADC 244 | ADMUX = 0xC0; // Int ref 1.6V 245 | ADCSRA = 0x87; // Enable ADC 246 | ADCSRB = 0x00; // MUX5= 0, freerun 247 | ADCSRC = 0x54; // Default value 248 | ADCSRA = 0x97; // Enable ADC 249 | DIDR0 = DIDR0 | ADC0D; // Disable logic buffer on ADC0 250 | //delay(5); 251 | ADCSRA |= (1 << ADSC); // start conversion 252 | } 253 | 254 | // host_getBatStat 255 | // returns battery's voltage in millivolts 256 | // input nothing 257 | // output battery voltage 258 | // Battery is connected through a resistor divider (825k and 300k) with gain of 0.266666 259 | // ADC ref is 1.6V with 1024 steps 260 | // 261 | float host_getBatStat(void) { 262 | uint16_t low, high; 263 | 264 | ADCSRA |= (1 << ADSC); 265 | while (ADCSRA & (1 << ADSC)); 266 | low = ADCL; 267 | high = ADCH; 268 | return (float)((high << 8) + low) * 1.6 / 1024 / 0.26666; 269 | } 270 | 271 | // host_printStatus 272 | // print system status on bottom line of display 273 | // Battery voltage, selected font, SD card state, memory usage 274 | // Memory usage is Free BASIC mem and Free C mem 275 | // input 276 | // boolean now: if true print status even if STATUS_RATE has not expired 277 | // output nothing 278 | // 279 | void host_printStatus(boolean now) { 280 | static uint32_t last_update = 0L; 281 | uint32_t currentMillis; 282 | char buffer[43]; 283 | char buf[16]; 284 | 285 | currentMillis = millis(); 286 | if (currentMillis - last_update > STATUS_RATE || now == true) { 287 | sprintf(buffer, " %4.4sV| %6.6s | %12s | %05d/%04d ", host_floatToStr(host_getBatStat(), buf), fontName[fontStatus.currentFont], currentFile, host_BASICFreeMem(), host_CFreeMem()); 288 | SRXEWriteString(0, 135 - 8 + 1, buffer, FONT_NORMAL, 0, 3); 289 | last_update = currentMillis; 290 | } 291 | } 292 | 293 | // host_setLineWrap 294 | // set Line Wrap status used with print 295 | // input wrap true if line wrap active, false if line truncated active 296 | // NOTE: not currently used 297 | // 298 | boolean host_setLineWrap(boolean wrap) { 299 | fontStatus.lineWrap = wrap; 300 | return wrap; 301 | } 302 | 303 | /* 304 | * host_cls 305 | * Clear screen and put cursor at home position 306 | * 307 | */ 308 | void host_cls() { 309 | memset(screenBuffer, 32, fontStatus.bufSize); 310 | memset(lineDirty, 1, fontStatus.nbLine); 311 | curX = 0; 312 | curY = 0; 313 | } 314 | 315 | void host_moveCursor(int x, int y) { 316 | if (x < 0) x = 0; 317 | if (x >= fontStatus.nbCar) x = fontStatus.nbCar - 1; 318 | if (y < 0) y = 0; 319 | if (y >= fontStatus.nbLine) y = fontStatus.nbLine - 1; 320 | curX = x; 321 | curY = y; 322 | } 323 | 324 | 325 | /* 326 | * host_showBuffer 327 | * Updates display 328 | * To speed things, only the lines tagged as dirty are redrawn. 329 | * 330 | */ 331 | void host_showBuffer() { 332 | for (int y = 0; y < fontStatus.nbLine; y++) { 333 | if (lineDirty[y] || (inputMode && y == curY)) { // if line is tagged as changed or is the input line 334 | for (int x = 0; x < fontStatus.nbCar; x++) { // scan the cars of this line 335 | char c = screenBuffer[y * fontStatus.nbCar + x]; 336 | if (c < 32) c = ' '; // ignore control cars 337 | if (x == curX && y == curY && inputMode && flash) c = 127; // cursor management 338 | char buf[2] = {0, 0}; 339 | buf[0] = c; 340 | if (c != 127) { 341 | SRXEWriteString(x * fontStatus.width, y * fontStatus.height, buf, fontStatus.currentFont, 3, 0); // printable car 342 | } else { 343 | buf[0] = 0x20; 344 | SRXEWriteString(x * fontStatus.width, y * fontStatus.height, buf, fontStatus.currentFont, 0, 3); // cursor 345 | } 346 | } 347 | lineDirty[y] = 0; // line marked as updated 348 | } 349 | } 350 | } 351 | 352 | 353 | /* 354 | * scrollBuffer 355 | * scrolls the display one line up 356 | * count how many line were scrolled (used in newLine for list and dir, and maybe others) 357 | */ 358 | void scrollBuffer() { 359 | memcpy(screenBuffer, screenBuffer + fontStatus.nbCar, fontStatus.nbCar * (fontStatus.nbLine - 1)); // move content of screenbuffer one line up 360 | memset(screenBuffer + fontStatus.nbCar * (fontStatus.nbLine - 1), 32, fontStatus.nbCar); // clear bottom line in the buffer 361 | memset(lineDirty, 1, fontStatus.nbLine); // tag last line as dirty 362 | lineCount++; // count the total of scrolled lines 363 | curY--; // update cursor position 364 | } 365 | 366 | 367 | /* 368 | * host_outputString 369 | * Puts a string in the screen buffer starting at the cursor position 370 | * 371 | */ 372 | void host_outputString(char *str) { 373 | int pos = curY * fontStatus.nbCar + curX; 374 | while (*str) { 375 | lineDirty[pos / fontStatus.nbCar] = 1; 376 | screenBuffer[pos++] = *str++; 377 | if (pos >= fontStatus.bufSize) { 378 | scrollBuffer(); 379 | pos -= fontStatus.nbCar; 380 | } 381 | } 382 | curX = pos % fontStatus.nbCar; 383 | curY = pos / fontStatus.nbCar; 384 | } 385 | 386 | /* 387 | * host_outputProgMemString 388 | * Puts a string stored in PROGMEM in the screen buffer starting at the cursor position 389 | * 390 | */ 391 | void host_outputProgMemString(const char *p) { 392 | while (1) { 393 | unsigned char c = pgm_read_byte(p++); 394 | if (c == 0) break; 395 | host_outputChar(c); 396 | } 397 | } 398 | 399 | /* 400 | * host_outputChar 401 | * Puts a char in the screen buffer starting at the cursor position 402 | * 403 | */ 404 | void host_outputChar(char c) { 405 | int pos = curY * fontStatus.nbCar + curX; 406 | lineDirty[pos / fontStatus.nbCar] = 1; 407 | screenBuffer[pos++] = c; 408 | if (pos >= fontStatus.bufSize) { 409 | scrollBuffer(); 410 | pos -= fontStatus.nbCar; 411 | } 412 | curX = pos % fontStatus.nbCar; 413 | curY = pos / fontStatus.nbCar; 414 | } 415 | 416 | /* 417 | * host_outputInt 418 | * Puts an int in the screen buffer starting at the cursor position 419 | * 420 | */ 421 | int host_outputInt(long num) { 422 | // returns len 423 | long i = num, xx = 1; 424 | int c = 0; 425 | do { 426 | c++; 427 | xx *= 10; 428 | i /= 10; 429 | } 430 | while (i); 431 | 432 | for (int i = 0; i < c; i++) { 433 | xx /= 10; 434 | char digit = ((num / xx) % 10) + '0'; 435 | host_outputChar(digit); 436 | } 437 | return c; 438 | } 439 | 440 | /* 441 | * host_floatToStr 442 | * Converts a float to its string representation 443 | * 444 | */ 445 | char *host_floatToStr(float f, char *buf) { 446 | // floats have approx 7 sig figs 447 | float a = fabs(f); 448 | if (f == 0.0f) { 449 | buf[0] = '0'; 450 | buf[1] = 0; 451 | } 452 | else if (a < 0.0001 || a > 1000000) { 453 | // this will output -1.123456E99 = 13 characters max including trailing nul 454 | dtostre(f, buf, 6, 0); 455 | } 456 | else { 457 | int decPos = 7 - (int)(floor(log10(a)) + 1.0f); 458 | dtostrf(f, 1, decPos, buf); 459 | if (decPos) { 460 | // remove trailing 0s 461 | char *p = buf; 462 | while (*p) p++; 463 | p--; 464 | while (*p == '0') { 465 | *p-- = 0; 466 | } 467 | if (*p == '.') *p = 0; 468 | } 469 | } 470 | return buf; 471 | } 472 | 473 | /* 474 | * host_outputFloat 475 | * Prints a float in the screen buffer starting at the cursor position 476 | * 477 | */ 478 | void host_outputFloat(float f) { 479 | char buf[16]; 480 | host_outputString(host_floatToStr(f, buf)); 481 | } 482 | 483 | #if SHOW_SPLASHSCREEN 484 | // host_splashscreen 485 | // displays a greeting screen 486 | // 487 | void host_splashscreen(void) { 488 | const int startHit = 138; 489 | const int lengthArrow = 84; 490 | const int startArrow = startHit + 132; 491 | int pos = startArrow; 492 | unsigned long pauseArrow = 500ul; 493 | unsigned long lastTimeArrow = 0; 494 | unsigned long currenTime = millis(); 495 | 496 | SRXELoadBitmapRLE(0, 0, bitmap_logo2_rle); 497 | SRXEWriteString(startHit, 110, "Hit a key ", FONT_MEDIUM, 3, 1); 498 | while (!SRXEGetKey()) { 499 | currenTime = millis(); 500 | if (currenTime - lastTimeArrow >= pauseArrow) { 501 | SRXEWriteString(pos, 110, " ", FONT_MEDIUM, 3, 1); 502 | pos += 12; 503 | if (pos > startHit + 120 + lengthArrow) pos = startArrow; 504 | SRXEWriteString(pos, 110, ">", FONT_MEDIUM, 3, 1); 505 | lastTimeArrow = currenTime; 506 | } 507 | }; 508 | } 509 | #endif 510 | 511 | /* 512 | * host_newLine 513 | * Generates a linefeed 514 | * Increments line position and scroll buffer if necessary 515 | * 516 | */ 517 | void host_newLine() { 518 | curX = 0; 519 | curY++; 520 | if (curY == fontStatus.nbLine) 521 | scrollBuffer(); 522 | memset(screenBuffer + fontStatus.nbCar * (curY), 32, fontStatus.nbCar); 523 | lineDirty[curY] = 1; 524 | } 525 | 526 | /* 527 | * host_newLine 528 | * Generates a linefeed 529 | * Increments line position and scroll buffer if necessary 530 | * If one screen length has been scrolled wait for a keypress before scrolling 531 | * 532 | * Input 533 | * num : if num = 0 init lineCount else test if bottom of screen has been reached 534 | * 535 | */ 536 | void host_newLine(byte num) { 537 | if (num == 0){ 538 | lineCount=1; // initialize count 539 | } else { 540 | if(lineCount >= fontStatus.nbLine){ // if bottom of screen is reach 541 | lineCount=1; // reset count 542 | while(SRXEGetKey() == 0); // wait for a keypress 543 | } 544 | } 545 | host_newLine(); 546 | } 547 | 548 | 549 | // Function that put the terminal in sleep mode 550 | // When waking up restores the display and the ADC configuration 551 | void host_goToSleep(void) { 552 | if (ledPin) digitalWrite(ledPin, LOW); // if LED present switch it off 553 | #if SD_CARD 554 | host_unmountSD(); // unmount SD card so it can be unplugged while the terminal is sleeping 555 | #endif 556 | SRXESleep(); // go into sleep mode and wait for an event (on/off button) 557 | // returning from sleep 558 | #if SD_CARD 559 | host_mountSD(); // try mounting the SD card just in case.... 560 | #endif 561 | // restore screen 562 | #if SHOW_SPLASHSCREEN 563 | host_splashscreen(); 564 | #endif 565 | memset(lineDirty, 1, fontStatus.nbLine); // all line dirty so the screen will be completely refreshed 566 | host_showBuffer(); 567 | if (ledPin) digitalWrite(ledPin, ledState); // If LED present restore its state 568 | host_setBatStat(); // restore ADC configuration 569 | } 570 | 571 | // host_setConf 572 | // Modifies configuration 573 | // UP and DOWN change font 574 | // LEFT and RIGHT change LCD contrast 575 | // MENU or ENTER returns to calling function 576 | // input 577 | // nothing 578 | // 579 | // output 580 | // true if display need to be updated (if font changed) 581 | // 582 | boolean host_setConf(void) { 583 | char c; 584 | boolean done = false; 585 | boolean needRedraw = false; 586 | unsigned char font = fontStatus.currentFont; // get current font 587 | while (!done) { 588 | while (c = SRXEGetKey()) { // get a key 589 | switch (c) { 590 | case KEY_UP: 591 | font += 1; // next font 592 | needRedraw = true; 593 | break; 594 | case KEY_DOWN: 595 | font -= 1; // previous font 596 | needRedraw = true; 597 | break; 598 | case KEY_LEFT: 599 | SRXEDecreaseVop(); 600 | break; 601 | case KEY_RIGHT: 602 | SRXEIncreaseVop(); 603 | break; 604 | case KEY_MENU: 605 | case KEY_ENTER: 606 | done = true; // exit setConf 607 | break; 608 | } 609 | if(needRedraw){ 610 | font &= 3; // limit font number to 0..3 range 611 | host_setFont(font); // update font parameters 612 | host_printStatus(true); // display updated status 613 | //delay(100); // little delay just in case 614 | } 615 | } 616 | } 617 | return(needRedraw); 618 | } 619 | 620 | /* 621 | * host_readLine 622 | * high level keyboard scanning loop 623 | * wait for keyboard press 624 | * keeps status line updated 625 | * tests if power button depressed --> goto sleep 626 | * tests if menu key depressed --> goto into configuration mode 627 | * fills input buffer until enter key is depressed 628 | * 629 | */ 630 | char *host_readLine() { 631 | inputMode = 1; 632 | 633 | if (curX == 0) memset(screenBuffer + fontStatus.nbCar * (curY), 32, fontStatus.nbCar); 634 | else host_newLine(); 635 | 636 | int startPos = curY * fontStatus.nbCar + curX; 637 | int pos = startPos; 638 | 639 | bool done = false; 640 | while (!done) { 641 | host_printStatus(false); 642 | // test if we want to enter sleep mode 643 | if (digitalRead(POWER_BUTTON) == 0) { 644 | host_goToSleep(); 645 | } 646 | while (char c = SRXEGetKey()) { 647 | 648 | host_click(); 649 | // read the next key 650 | lineDirty[pos / fontStatus.nbCar] = 1; 651 | //char c = keyboard.read(); 652 | if (c >= 32 && c <= 126) 653 | screenBuffer[pos++] = c; 654 | else if (c == KEY_DELETE && pos > startPos) 655 | screenBuffer[--pos] = 0; 656 | else if ((c == KEY_MENU)) { // if we want to change configuration 657 | if(host_setConf()){ // call setConf to change font 658 | // update screen content if new font 659 | // move current line to top of screen 660 | memcpy(screenBuffer, screenBuffer + startPos, pos - startPos); 661 | pos = pos - startPos; 662 | startPos = 0; 663 | // clear from current pos to end of buffer 664 | memset(screenBuffer + pos, 32, fontStatus.bufSize - pos); // screenBuffer + pos + 1 or + pos??? 665 | // tels that all the lines have changed 666 | memset(lineDirty, 1, fontStatus.nbLine); 667 | } 668 | } 669 | else if (c == KEY_ENTER) 670 | done = true; 671 | curX = pos % fontStatus.nbCar; 672 | curY = pos / fontStatus.nbCar; 673 | // scroll if we need to 674 | if (curY == fontStatus.nbLine) { 675 | if (startPos >= fontStatus.nbCar) { 676 | startPos -= fontStatus.nbCar; 677 | pos -= fontStatus.nbCar; 678 | scrollBuffer(); 679 | } 680 | else 681 | { 682 | screenBuffer[--pos] = 0; 683 | curX = pos % fontStatus.nbCar; 684 | curY = pos / fontStatus.nbCar; 685 | } 686 | } 687 | redraw = 1; 688 | } 689 | if (redraw) 690 | host_showBuffer(); 691 | } 692 | screenBuffer[pos] = 0; 693 | inputMode = 0; 694 | // remove the cursor 695 | lineDirty[curY] = 1; 696 | host_showBuffer(); 697 | return &screenBuffer[startPos]; 698 | } 699 | 700 | /* 701 | * host_getKey 702 | * non blocking keyboard read 703 | * returns 0 if no key pressed 704 | * 705 | */ 706 | char host_getKey() { 707 | char c = inkeyChar; 708 | inkeyChar = 0; 709 | //if (c >= 32 && c <= 126) 710 | return c; 711 | //else return 0; 712 | } 713 | 714 | /* 715 | * host_ESCPressed 716 | * updates the status line and tests if the escape key is depressed 717 | * used during program execution to halt the interpreter 718 | * escape key is square root 719 | * 720 | */ 721 | bool host_ESCPressed() { 722 | int c; 723 | 724 | host_printStatus(false); 725 | while (c = SRXEGetKey()) { 726 | inkeyChar = c; 727 | if (inkeyChar == KEY_ESC) 728 | return true; 729 | } 730 | return false; 731 | } 732 | 733 | void host_outputFreeMem(unsigned int val) 734 | { 735 | host_newLine(); 736 | host_outputInt(val); 737 | host_outputChar(' '); 738 | host_outputProgMemString(bytesFreeStr); 739 | } 740 | 741 | void host_saveProgram(bool autoexec) { 742 | EEPROM.write(0, autoexec ? MAGIC_AUTORUN_NUMBER : 0x00); 743 | EEPROM.write(1, sysPROGEND & 0xFF); 744 | EEPROM.write(2, (sysPROGEND >> 8) & 0xFF); 745 | for (int i = 0; i < sysPROGEND; i++) 746 | EEPROM.write(3 + i, mem[i]); 747 | } 748 | 749 | void host_loadProgram() { 750 | // skip the autorun byte 751 | sysPROGEND = EEPROM.read(1) | (EEPROM.read(2) << 8); 752 | for (int i = 0; i < sysPROGEND; i++) 753 | mem[i] = EEPROM.read(i + 3); 754 | } 755 | 756 | #if EXTERNAL_EEPROM 757 | #include 758 | extern TwiMaster rtc; 759 | 760 | void writeExtEEPROM(unsigned int address, byte data) 761 | { 762 | if (address % 32 == 0) host_click(); 763 | rtc.start((EXTERNAL_EEPROM_ADDR << 1) | I2C_WRITE); 764 | rtc.write((int)(address >> 8)); // MSB 765 | rtc.write((int)(address & 0xFF)); // LSB 766 | rtc.write(data); 767 | rtc.stop(); 768 | delay(5); 769 | } 770 | 771 | byte readExtEEPROM(unsigned int address) 772 | { 773 | rtc.start((EXTERNAL_EEPROM_ADDR << 1) | I2C_WRITE); 774 | rtc.write((int)(address >> 8)); // MSB 775 | rtc.write((int)(address & 0xFF)); // LSB 776 | rtc.restart((EXTERNAL_EEPROM_ADDR << 1) | I2C_READ); 777 | byte b = rtc.read(true); 778 | rtc.stop(); 779 | return b; 780 | } 781 | 782 | // get the EEPROM address of a file, or the end if fileName is null 783 | unsigned int getExtEEPROMAddr(char *fileName) { 784 | unsigned int addr = 0; 785 | while (1) { 786 | unsigned int len = readExtEEPROM(addr) | (readExtEEPROM(addr + 1) << 8); 787 | if (len == 0) break; 788 | 789 | if (fileName) { 790 | bool found = true; 791 | for (int i = 0; i <= strlen(fileName); i++) { 792 | if (fileName[i] != readExtEEPROM(addr + 2 + i)) { 793 | found = false; 794 | break; 795 | } 796 | } 797 | if (found) return addr; 798 | } 799 | addr += len; 800 | } 801 | return fileName ? EXTERNAL_EEPROM_SIZE : addr; 802 | } 803 | 804 | void host_directoryExtEEPROM() { 805 | unsigned int addr = 0; 806 | while (1) { 807 | unsigned int len = readExtEEPROM(addr) | (readExtEEPROM(addr + 1) << 8); 808 | if (len == 0) break; 809 | int i = 0; 810 | while (1) { 811 | char ch = readExtEEPROM(addr + 2 + i); 812 | if (!ch) break; 813 | host_outputChar(readExtEEPROM(addr + 2 + i)); 814 | i++; 815 | } 816 | addr += len; 817 | host_outputChar(' '); 818 | } 819 | host_outputFreeMem(EXTERNAL_EEPROM_SIZE - addr - 2); 820 | } 821 | 822 | bool host_removeExtEEPROM(char *fileName) { 823 | unsigned int addr = getExtEEPROMAddr(fileName); 824 | if (addr == EXTERNAL_EEPROM_SIZE) return false; 825 | unsigned int len = readExtEEPROM(addr) | (readExtEEPROM(addr + 1) << 8); 826 | unsigned int last = getExtEEPROMAddr(NULL); 827 | unsigned int count = 2 + last - (addr + len); 828 | while (count--) { 829 | byte b = readExtEEPROM(addr + len); 830 | writeExtEEPROM(addr, b); 831 | addr++; 832 | } 833 | return true; 834 | } 835 | 836 | bool host_loadExtEEPROM(char *fileName) { 837 | unsigned int addr = getExtEEPROMAddr(fileName); 838 | if (addr == EXTERNAL_EEPROM_SIZE) return false; 839 | // skip filename 840 | addr += 2; 841 | while (readExtEEPROM(addr++)) ; 842 | sysPROGEND = readExtEEPROM(addr) | (readExtEEPROM(addr + 1) << 8); 843 | for (int i = 0; i < sysPROGEND; i++) 844 | mem[i] = readExtEEPROM(addr + 2 + i); 845 | } 846 | 847 | bool host_saveExtEEPROM(char *fileName) { 848 | unsigned int addr = getExtEEPROMAddr(fileName); 849 | if (addr != EXTERNAL_EEPROM_SIZE) 850 | host_removeExtEEPROM(fileName); 851 | addr = getExtEEPROMAddr(NULL); 852 | unsigned int fileNameLen = strlen(fileName); 853 | unsigned int len = 2 + fileNameLen + 1 + 2 + sysPROGEND; 854 | if ((long)EXTERNAL_EEPROM_SIZE - addr - len - 2 < 0) 855 | return false; 856 | // write overall length 857 | writeExtEEPROM(addr++, len & 0xFF); 858 | writeExtEEPROM(addr++, (len >> 8) & 0xFF); 859 | // write filename 860 | for (int i = 0; i < strlen(fileName); i++) 861 | writeExtEEPROM(addr++, fileName[i]); 862 | writeExtEEPROM(addr++, 0); 863 | // write length & program 864 | writeExtEEPROM(addr++, sysPROGEND & 0xFF); 865 | writeExtEEPROM(addr++, (sysPROGEND >> 8) & 0xFF); 866 | for (int i = 0; i < sysPROGEND; i++) 867 | writeExtEEPROM(addr++, mem[i]); 868 | // 0 length marks end 869 | writeExtEEPROM(addr++, 0); 870 | writeExtEEPROM(addr++, 0); 871 | return true; 872 | } 873 | 874 | #endif 875 | 876 | // fdufnews 6/08/2020 877 | #if SD_CARD 878 | /* 879 | host_directorySD 880 | 881 | Print the directory listing of the SD Card on screen 882 | 883 | INPUT nothing 884 | 885 | OUTPUT nothing 886 | 887 | */ 888 | void host_directorySD(void) { 889 | if (!sdCardPresent) return; 890 | File dir, entry; 891 | dir = SD.open("/"); // open card root 892 | host_newLine(0); // initialize count of newlines 893 | do { 894 | entry = dir.openNextFile(); // get the next file 895 | if (!entry) break; // if no more entry exit the loop 896 | if (!entry.isDirectory()) { // if it is not a directory display info 897 | host_outputString(entry.name()); // display name of file 898 | host_moveCursor(16, curY); // move cursor to align size information 899 | host_outputInt(entry.size()); // display size of file 900 | host_showBuffer(); // display the new line 901 | host_newLine(1); // scroll one line and wait if necessary 902 | } 903 | entry.close(); // close file 904 | } while (true); 905 | dir.close(); // close root dir 906 | } 907 | 908 | 909 | /* 910 | host_saveSD 911 | 912 | Save the current BASIC program to a file on SD Card 913 | If the file already exist it is deleted first 914 | 915 | INPUT 916 | filename : the name of the file is a 8+3 string 917 | 918 | OUTPUT 919 | true if succeeded 920 | 921 | */ 922 | boolean host_saveSD(char *filename) { 923 | File myFile; 924 | if (!sdCardPresent) return false; 925 | if (SD.exists(filename)) SD.remove(filename); // if file already exist then delete it 926 | myFile = SD.open(filename, FILE_WRITE); // open the file in write mode 927 | unsigned long count = myFile.write(mem, sysPROGEND); // write basic program buffer to sd card 928 | myFile.close(); 929 | if (count == (unsigned long)sysPROGEND) { 930 | strcpy(currentFile, filename); 931 | return true; 932 | } else return false; 933 | } 934 | 935 | /* 936 | host_loadSD 937 | 938 | Load a file from the SD Card into memory as the current BASIC program 939 | 940 | INPUT 941 | filename : the name of the file is a 8+3 string 942 | 943 | OUTPUT 944 | true if succeeded 945 | 946 | */ 947 | boolean host_loadSD(char *filename) { 948 | File myFile; 949 | if (!sdCardPresent) return false; 950 | myFile = SD.open(filename, FILE_READ); // open the file in read mode 951 | if (!myFile) return false; 952 | unsigned long filesize = myFile.size(); 953 | unsigned long count = myFile.read(mem, filesize); // copy file to basic program buffer 954 | myFile.close(); 955 | sysPROGEND = (unsigned int)filesize; 956 | strcpy(currentFile, filename); 957 | return true; 958 | } 959 | 960 | /* 961 | host_removeSD 962 | 963 | Delete a file from the SD Card 964 | 965 | INPUT 966 | filename : the name of the file is a 8+3 string 967 | 968 | OUTPUT 969 | true if succeeded 970 | 971 | */ 972 | boolean host_removeSD(char *filename) { 973 | if (!sdCardPresent) return false; 974 | if (SD.exists(filename)) SD.remove(filename); 975 | return true; 976 | } 977 | 978 | /* 979 | host_unmountSD 980 | 981 | unmount the SD card in order to extract it 982 | 983 | INPUT nothing 984 | 985 | OUTPUT nothing 986 | */ 987 | void host_unmountSD(void) { 988 | SD.end(); 989 | sdCardPresent = false; 990 | strcpy_P(currentFile, ko_card); 991 | // digitalWrite(SD_CARD_CS,HIGH); 992 | host_printStatus(true); 993 | } 994 | 995 | /* 996 | host_mountSD 997 | 998 | mount the SD card 999 | 1000 | INPUT nothing 1001 | 1002 | OUTPUT nothing 1003 | */ 1004 | void host_mountSD(void) { 1005 | if (sdCardPresent = SD.begin(SD_CARD_CS)) // true if an sd card is present 1006 | strcpy_P(currentFile, ok_card); 1007 | else 1008 | strcpy_P(currentFile, ko_card); 1009 | host_printStatus(true); 1010 | } 1011 | 1012 | 1013 | /* 1014 | void host_openSD(); 1015 | void host_readSD(); 1016 | void host_writeSD(); 1017 | */ 1018 | 1019 | #endif 1020 | -------------------------------------------------------------------------------- /logo2_rle.h: -------------------------------------------------------------------------------- 1 | // Original file LogoSMART2.data 2 | 3 | #include 4 | 5 | const uint8_t bitmap_logo2_rle[] PROGMEM = { 6 | 0x80, 0x01, // image width=384 7 | 0x88, 0x00, // image height=136 8 | 0XFE, 0x00, 0XFE, 0x00, 0XFE, 0x00, 0X89, 0x00, 0X01, 0x03, 0X22, 0xff, 0X01, 0xfc, 0X5C, 0x00, 9 | 0X01, 0xfc, 0X22, 0x00, 0X01, 0x1f, 0X5B, 0x00, 0X01, 0x03, 0X01, 0x00, 0X22, 0x49, 0X01, 0x00, 10 | 0X01, 0xfc, 0X5A, 0x00, 0X01, 0xfc, 0X01, 0x09, 0X22, 0x49, 0X01, 0x48, 0X01, 0xe3, 0X5A, 0x00, 11 | 0X01, 0xe0, 0X01, 0x49, 0X22, 0xff, 0X01, 0x49, 0X01, 0xff, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 12 | 0X01, 0x4b, 0X22, 0xff, 0X01, 0xe9, 0X01, 0x5f, 0X01, 0xe3, 0X17, 0x00, 0X01, 0x13, 0X01, 0xf1, 13 | 0X01, 0x40, 0X01, 0x00, 0X01, 0xff, 0X01, 0x91, 0X11, 0x00, 0X01, 0x9f, 0X01, 0x91, 0X06, 0x00, 14 | 0X01, 0x01, 0X05, 0x00, 0X01, 0x40, 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X22, 0xff, 0X01, 0xfd, 15 | 0X01, 0x5f, 0X01, 0x1c, 0X15, 0x00, 0X01, 0x02, 0X01, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfd, 16 | 0X01, 0x01, 0X02, 0xff, 0X01, 0xe8, 0X01, 0x00, 0X01, 0x11, 0X01, 0x00, 0X01, 0x40, 0X01, 0x00, 17 | 0X01, 0x88, 0X01, 0x00, 0X01, 0x88, 0X01, 0x00, 0X01, 0x40, 0X01, 0x00, 0X01, 0x0b, 0X01, 0x80, 18 | 0X03, 0x00, 0X02, 0xff, 0X01, 0xe8, 0X02, 0x00, 0X01, 0x02, 0X02, 0x00, 0X01, 0x13, 0X01, 0xe8, 19 | 0X01, 0x00, 0X01, 0x11, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xfd, 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5f, 20 | 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X14, 0x00, 0X01, 0x01, 0X01, 0xf3, 0X01, 0xe0, 21 | 0X01, 0x02, 0X01, 0x93, 0X01, 0xfe, 0X01, 0x00, 0X01, 0x12, 0X01, 0x9f, 0X01, 0xfc, 0X01, 0x00, 22 | 0X01, 0x9f, 0X01, 0x01, 0X01, 0xf0, 0X01, 0x01, 0X01, 0xfc, 0X01, 0x02, 0X01, 0xfd, 0X01, 0x01, 23 | 0X01, 0xf0, 0X01, 0x00, 0X01, 0x9f, 0X01, 0xfd, 0X03, 0x00, 0X01, 0x12, 0X01, 0x9f, 0X01, 0xfc, 24 | 0X01, 0x00, 0X01, 0x01, 0X01, 0xf3, 0X01, 0xe0, 0X01, 0x01, 0X01, 0xff, 0X01, 0xfe, 0X01, 0x00, 25 | 0X01, 0x5f, 0X01, 0x00, 0X01, 0x5f, 0X01, 0xff, 0X01, 0x40, 0X1B, 0x00, 0X01, 0xe1, 0X01, 0x5f, 26 | 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X14, 0x00, 0X01, 0x0b, 0X01, 0xff, 0X01, 0xe0, 27 | 0X01, 0x0b, 0X01, 0xe8, 0X01, 0x5f, 0X01, 0x40, 0X01, 0x5f, 0X01, 0x41, 0X01, 0xfd, 0X01, 0x01, 28 | 0X01, 0xfe, 0X01, 0x03, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xf0, 0X01, 0x0b, 0X01, 0xfd, 0X01, 0x03, 29 | 0X01, 0xf0, 0X01, 0x03, 0X01, 0xfe, 0X01, 0xff, 0X01, 0x40, 0X02, 0x00, 0X01, 0x1f, 0X01, 0x41, 30 | 0X01, 0xfd, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xff, 0X01, 0xe0, 0X01, 0x0b, 0X01, 0xfd, 0X01, 0xfe, 31 | 0X01, 0x00, 0X01, 0xfe, 0X01, 0x01, 0X01, 0xfe, 0X01, 0x1f, 0X01, 0x80, 0X1B, 0x00, 0X01, 0xe1, 32 | 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 33 | 0X14, 0x00, 0X01, 0x9f, 0X01, 0x53, 0X01, 0x80, 0X01, 0x1f, 0X01, 0x80, 0X01, 0x9f, 0X01, 0x40, 34 | 0X01, 0xfe, 0X01, 0x01, 0X01, 0xfd, 0X01, 0x03, 0X01, 0xf0, 0X01, 0x0b, 0X01, 0xe8, 0X01, 0x13, 35 | 0X01, 0xe0, 0X01, 0x13, 0X01, 0xfd, 0X01, 0x13, 0X01, 0xe0, 0X01, 0x1f, 0X01, 0xe0, 0X01, 0x13, 36 | 0X03, 0x00, 0X01, 0x9f, 0X01, 0x01, 0X01, 0xfe, 0X01, 0x00, 0X01, 0x9f, 0X01, 0x53, 0X01, 0x80, 37 | 0X01, 0x1f, 0X01, 0xe1, 0X01, 0xfd, 0X01, 0x02, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xf0, 0X01, 0x5f, 38 | 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 39 | 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 40 | 0X01, 0x1c, 0X13, 0x00, 0X01, 0x1f, 0X01, 0xfd, 0X01, 0x1f, 0X01, 0x40, 0X01, 0x9f, 0X01, 0x4b, 41 | 0X01, 0xfe, 0X01, 0x02, 0X01, 0xfc, 0X01, 0x02, 0X01, 0xfc, 0X01, 0x0b, 0X01, 0xe8, 0X01, 0x1f, 42 | 0X01, 0x80, 0X01, 0x5f, 0X01, 0x40, 0X01, 0x5f, 0X01, 0xfd, 0X01, 0x1f, 0X01, 0x40, 0X01, 0x9f, 43 | 0X01, 0x40, 0X01, 0x92, 0X02, 0x00, 0X01, 0x01, 0X01, 0xfd, 0X01, 0x53, 0X01, 0xf0, 0X01, 0x1f, 44 | 0X01, 0xfd, 0X01, 0x1f, 0X01, 0x40, 0X01, 0x1f, 0X01, 0x82, 0X01, 0xf0, 0X01, 0x0b, 0X01, 0xe8, 45 | 0X01, 0x13, 0X01, 0xe0, 0X01, 0xfe, 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x00, 46 | 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 47 | 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X13, 0x00, 0X01, 0x1f, 0X01, 0xfc, 0X01, 0x5f, 48 | 0X01, 0x00, 0X01, 0xfe, 0X01, 0x9f, 0X01, 0xe8, 0X01, 0x0b, 0X01, 0xe8, 0X01, 0x0b, 0X01, 0xe8, 49 | 0X01, 0x1f, 0X01, 0x80, 0X01, 0x9f, 0X01, 0x00, 0X01, 0x9e, 0X01, 0x00, 0X01, 0xfe, 0X01, 0x9d, 50 | 0X01, 0x9e, 0X01, 0x00, 0X01, 0xfd, 0X01, 0x01, 0X01, 0xfd, 0X02, 0x00, 0X01, 0x03, 0X02, 0xff, 51 | 0X01, 0x80, 0X01, 0x1f, 0X01, 0xfc, 0X01, 0x5f, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xe0, 0X01, 0x40, 52 | 0X01, 0x13, 0X01, 0x80, 0X01, 0x5f, 0X01, 0x40, 0X01, 0x50, 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5f, 53 | 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X13, 0x00, 54 | 0X01, 0x1f, 0X01, 0xff, 0X01, 0x9e, 0X01, 0x02, 0X01, 0xff, 0X01, 0xfe, 0X01, 0x00, 0X01, 0x13, 55 | 0X01, 0xe0, 0X01, 0x5f, 0X01, 0x80, 0X01, 0x5f, 0X01, 0x00, 0X01, 0xfe, 0X01, 0x01, 0X01, 0xfd, 56 | 0X01, 0x01, 0X01, 0xfc, 0X01, 0x9e, 0X01, 0xfd, 0X01, 0x02, 0X01, 0xfc, 0X01, 0x02, 0X01, 0xfc, 57 | 0X02, 0x00, 0X01, 0x0b, 0X01, 0xf3, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X01, 0x9e, 58 | 0X01, 0x00, 0X01, 0x03, 0X01, 0xf0, 0X01, 0x00, 0X01, 0x5f, 0X01, 0x40, 0X01, 0x9e, 0X1E, 0x00, 59 | 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 60 | 0X01, 0xe3, 0X13, 0x00, 0X01, 0x9e, 0X01, 0x1f, 0X01, 0xfd, 0X01, 0x03, 0X01, 0xeb, 0X01, 0xf0, 61 | 0X01, 0x00, 0X01, 0x1f, 0X01, 0x42, 0X01, 0xfe, 0X01, 0x00, 0X01, 0xfe, 0X01, 0x02, 0X01, 0xfc, 62 | 0X01, 0x03, 0X01, 0xf0, 0X01, 0x03, 0X01, 0xe8, 0X01, 0x9f, 0X01, 0xf0, 0X01, 0x03, 0X01, 0xf0, 63 | 0X01, 0x13, 0X01, 0xe8, 0X02, 0x00, 0X01, 0x1f, 0X01, 0x81, 0X01, 0xfe, 0X01, 0x00, 0X01, 0x9e, 64 | 0X01, 0x1f, 0X01, 0xfd, 0X01, 0x0b, 0X01, 0x41, 0X01, 0xfc, 0X01, 0x00, 0X01, 0xfe, 0X01, 0x00, 65 | 0X01, 0xfd, 0X1E, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 66 | 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X12, 0x00, 0X01, 0x01, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xfc, 67 | 0X01, 0x13, 0X01, 0xe1, 0X01, 0xfd, 0X01, 0x00, 0X02, 0x9f, 0X01, 0xf0, 0X01, 0x01, 0X01, 0xfd, 68 | 0X01, 0x9f, 0X01, 0xf0, 0X01, 0x0b, 0X01, 0xe8, 0X01, 0x0b, 0X01, 0xe0, 0X01, 0x5f, 0X01, 0xe8, 69 | 0X01, 0x03, 0X01, 0xe8, 0X01, 0x9f, 0X01, 0x80, 0X02, 0x00, 0X01, 0x5f, 0X01, 0x00, 0X01, 0x5f, 70 | 0X01, 0x01, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xfc, 0X01, 0x1f, 0X01, 0xf1, 0X01, 0xfd, 0X01, 0x01, 71 | 0X01, 0xfd, 0X01, 0x01, 0X01, 0xfc, 0X01, 0x0a, 0X01, 0xe0, 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5f, 72 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 73 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X12, 0x00, 0X01, 0x03, 74 | 0X01, 0xe8, 0X01, 0x03, 0X01, 0xe8, 0X01, 0x1f, 0X01, 0x40, 0X01, 0x9f, 0X01, 0x00, 0X01, 0x5f, 75 | 0X01, 0xff, 0X01, 0x40, 0X01, 0x02, 0X02, 0xff, 0X01, 0xe0, 0X01, 0x1f, 0X01, 0x80, 0X01, 0x1f, 76 | 0X01, 0x80, 0X01, 0x5f, 0X01, 0x80, 0X01, 0x03, 0X01, 0xf3, 0X01, 0xfe, 0X03, 0x00, 0X01, 0x9f, 77 | 0X01, 0x53, 0X01, 0xff, 0X01, 0x03, 0X01, 0xe8, 0X01, 0x03, 0X01, 0xe8, 0X01, 0x02, 0X02, 0xff, 78 | 0X01, 0x03, 0X01, 0xf0, 0X01, 0x02, 0X02, 0xff, 0X01, 0xe0, 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5f, 79 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 80 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X12, 0x00, 0X01, 0x02, 81 | 0X01, 0xe0, 0X01, 0x03, 0X01, 0xe0, 0X01, 0x02, 0X01, 0x00, 0X01, 0x1f, 0X01, 0x40, 0X01, 0x0b, 82 | 0X01, 0x88, 0X02, 0x00, 0X01, 0x9f, 0X01, 0x4b, 0X01, 0x80, 0X01, 0x0b, 0X01, 0x40, 0X01, 0x0b, 83 | 0X01, 0x00, 0X01, 0x5f, 0X01, 0x40, 0X01, 0x01, 0X01, 0xff, 0X01, 0xe8, 0X03, 0x00, 0X01, 0x9f, 84 | 0X01, 0xff, 0X01, 0xf0, 0X01, 0x02, 0X01, 0xe0, 0X01, 0x03, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x53, 85 | 0X01, 0xfd, 0X01, 0x01, 0X01, 0xe8, 0X01, 0x00, 0X01, 0xff, 0X01, 0xfe, 0X1D, 0x00, 0X01, 0xe1, 86 | 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 87 | 0X15, 0x00, 0X01, 0x40, 0X02, 0x00, 0X01, 0x08, 0X05, 0x00, 0X01, 0x08, 0X01, 0x01, 0X01, 0x40, 88 | 0X04, 0x00, 0X01, 0x09, 0X02, 0x00, 0X01, 0x51, 0X04, 0x00, 0X01, 0x13, 0X01, 0xf1, 0X04, 0x00, 89 | 0X01, 0x40, 0X02, 0x00, 0X01, 0x50, 0X03, 0x00, 0X01, 0x11, 0X01, 0x40, 0X1D, 0x00, 0X01, 0xe1, 90 | 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 91 | 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 92 | 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 93 | 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 94 | 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x00, 95 | 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 96 | 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 97 | 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 98 | 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 99 | 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 100 | 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 101 | 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 102 | 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X25, 0x00, 0X01, 0x90, 0X33, 0x00, 0X01, 0xe1, 0X01, 0x5f, 103 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 104 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X24, 0x00, 0X01, 0x02, 105 | 0X01, 0xfe, 0X33, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 106 | 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X24, 0x00, 0X01, 0x13, 0X01, 0x5e, 0X01, 0x02, 0X01, 0xf0, 107 | 0X01, 0x02, 0X01, 0x80, 0X2F, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x92, 0X01, 0x1f, 108 | 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X24, 0x00, 0X01, 0x5e, 0X01, 0x5c, 0X01, 0x1f, 109 | 0X01, 0x9e, 0X01, 0x0b, 0X01, 0xf2, 0X01, 0xe8, 0X2E, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 110 | 0X16, 0x92, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X24, 0x00, 0X01, 0x9c, 111 | 0X01, 0xf0, 0X01, 0x9d, 0X01, 0x1e, 0X01, 0x03, 0X01, 0xff, 0X01, 0x88, 0X2E, 0x00, 0X01, 0xe1, 112 | 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 113 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X23, 0x00, 114 | 0X01, 0x09, 0X01, 0xea, 0X01, 0xe0, 0X01, 0xf0, 0X01, 0x5d, 0X02, 0x13, 0X2F, 0x00, 0X01, 0xe1, 115 | 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X03, 0xff, 0X16, 0x92, 0X01, 0x1f, 0X01, 0xff, 116 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x49, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X23, 0x00, 117 | 0X01, 0x0b, 0X01, 0xf3, 0X01, 0x4b, 0X01, 0xe9, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x1e, 0X01, 0x08, 118 | 0X2E, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x00, 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 119 | 0X01, 0x5f, 0X01, 0x1c, 0X23, 0x00, 0X01, 0x03, 0X01, 0xff, 0X01, 0xf2, 0X01, 0xf3, 0X01, 0xf2, 120 | 0X01, 0x88, 0X01, 0x9e, 0X01, 0xfc, 0X2E, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X06, 0xff, 0X16, 0x00, 121 | 0X01, 0x1f, 0X05, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X23, 0x00, 0X01, 0x0b, 0X01, 0x51, 122 | 0X01, 0x00, 0X01, 0x5e, 0X02, 0x00, 0X01, 0x9f, 0X01, 0x80, 0X2E, 0x00, 0X01, 0xe1, 0X01, 0x5f, 123 | 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 0X23, 0x00, 0X01, 0x13, 0X05, 0x00, 0X01, 0x08, 124 | 0X2F, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X23, 0x00, 125 | 0X01, 0x1e, 0X35, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 126 | 0X23, 0x00, 0X01, 0x1d, 0X35, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 127 | 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x1c, 128 | 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5f, 0X22, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 129 | 0X01, 0xe1, 0X01, 0x4b, 0X22, 0xff, 0X01, 0xe9, 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 130 | 0X01, 0x49, 0X22, 0xff, 0X01, 0x49, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 0X24, 0x49, 131 | 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X24, 0x49, 0X01, 0x5f, 0X01, 0xe3, 0X1C, 0x00, 132 | 0X01, 0xfe, 0X01, 0x88, 0X05, 0x00, 0X01, 0x1f, 0X01, 0x91, 0X19, 0x00, 0X01, 0xff, 0X01, 0x89, 133 | 0X19, 0x00, 0X01, 0xe1, 0X24, 0x49, 0X01, 0x5f, 0X01, 0x1c, 0X12, 0x00, 0X01, 0x0a, 0X01, 0x80, 134 | 0X01, 0x00, 0X01, 0x10, 0X04, 0x00, 0X01, 0x08, 0X01, 0x01, 0X02, 0xff, 0X01, 0x01, 0X01, 0xf2, 135 | 0X01, 0x40, 0X02, 0x00, 0X01, 0x5f, 0X01, 0xff, 0X01, 0xe0, 0X15, 0x00, 0X01, 0x09, 0X02, 0x00, 136 | 0X02, 0xff, 0X01, 0xe0, 0X18, 0x00, 0X01, 0xe1, 0X24, 0x49, 0X01, 0x5f, 0X01, 0xe3, 0X12, 0x00, 137 | 0X01, 0x5f, 0X01, 0xfd, 0X01, 0x00, 0X01, 0x9f, 0X01, 0x00, 0X01, 0x9d, 0X01, 0x00, 0X01, 0x02, 138 | 0X01, 0x5e, 0X01, 0x00, 0X01, 0x1e, 0X01, 0x9f, 0X01, 0x81, 0X01, 0xff, 0X01, 0xfe, 0X01, 0x40, 139 | 0X01, 0x00, 0X01, 0x03, 0X01, 0x93, 0X01, 0xf0, 0X15, 0x00, 0X01, 0x9f, 0X01, 0x40, 0X01, 0x48, 140 | 0X01, 0x1d, 0X01, 0x9f, 0X01, 0xe0, 0X18, 0x00, 0X01, 0xe1, 0X24, 0x49, 0X01, 0x5f, 0X01, 0x1c, 141 | 0X11, 0x00, 0X01, 0x02, 0X01, 0xfd, 0X01, 0x9d, 0X01, 0x01, 0X01, 0xff, 0X01, 0x41, 0X01, 0xff, 142 | 0X01, 0x00, 0X01, 0x1f, 0X01, 0xfe, 0X01, 0x00, 0X01, 0x9e, 0X01, 0x0b, 0X01, 0xe0, 0X01, 0x0b, 143 | 0X01, 0x9f, 0X01, 0x80, 0X01, 0x00, 0X01, 0x13, 0X01, 0x81, 0X01, 0xfc, 0X01, 0x01, 0X01, 0x40, 144 | 0X01, 0x00, 0X01, 0x01, 0X01, 0x00, 0X01, 0x01, 0X03, 0x00, 0X01, 0x50, 0X05, 0x00, 0X01, 0x40, 145 | 0X01, 0x00, 0X01, 0x09, 0X03, 0x00, 0X01, 0x93, 0X01, 0x82, 0X01, 0xfc, 0X01, 0x9e, 0X01, 0x01, 146 | 0X01, 0x40, 0X18, 0x00, 0X01, 0xe1, 0X24, 0x49, 0X01, 0x5f, 0X01, 0xe3, 0X11, 0x00, 0X01, 0x0b, 147 | 0X01, 0xe8, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xff, 0X01, 0x03, 0X01, 0xfe, 0X01, 0x09, 0X01, 0xfe, 148 | 0X01, 0x9d, 0X01, 0x01, 0X01, 0xfc, 0X01, 0x13, 0X01, 0xe0, 0X01, 0x1f, 0X01, 0x4a, 0X01, 0x40, 149 | 0X01, 0x00, 0X01, 0x5f, 0X01, 0x02, 0X01, 0xfc, 0X01, 0x13, 0X01, 0xe8, 0X01, 0x00, 0X01, 0x5f, 150 | 0X01, 0xe8, 0X01, 0x0b, 0X01, 0x49, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xff, 0X01, 0x00, 0X01, 0x5d, 151 | 0X01, 0x10, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xfd, 0X01, 0x00, 0X01, 0x9f, 0X01, 0x40, 0X02, 0x00, 152 | 0X01, 0x0b, 0X01, 0x9f, 0X01, 0xe9, 0X01, 0xfc, 0X1A, 0x00, 0X01, 0xe1, 0X01, 0x49, 0X22, 0xff, 153 | 0X01, 0x49, 0X01, 0x5f, 0X01, 0x1c, 0X11, 0x00, 0X01, 0x03, 0X01, 0xe1, 0X01, 0xe8, 0X01, 0x13, 154 | 0X01, 0x9e, 0X01, 0x1f, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0xe8, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xe9, 155 | 0X01, 0xfe, 0X01, 0x00, 0X01, 0x9f, 0X03, 0x00, 0X01, 0xfd, 0X01, 0x5f, 0X01, 0x80, 0X01, 0xfe, 156 | 0X01, 0x9d, 0X01, 0x01, 0X01, 0xfd, 0X01, 0xfc, 0X01, 0x1f, 0X01, 0x9f, 0X01, 0x80, 0X01, 0x1f, 157 | 0X01, 0x8b, 0X01, 0x80, 0X01, 0xfe, 0X01, 0xff, 0X01, 0x00, 0X02, 0x5f, 0X01, 0x03, 0X01, 0xf2, 158 | 0X01, 0xe8, 0X02, 0x00, 0X01, 0x0b, 0X01, 0xfe, 0X01, 0x03, 0X01, 0xff, 0X01, 0x88, 0X19, 0x00, 159 | 0X01, 0xe1, 0X01, 0x4b, 0X01, 0xe9, 0X20, 0x49, 0X01, 0x4b, 0X01, 0xe9, 0X01, 0x5f, 0X01, 0xe3, 160 | 0X11, 0x00, 0X01, 0x02, 0X01, 0xf0, 0X01, 0x00, 0X01, 0x5e, 0X01, 0x5d, 0X01, 0x9d, 0X01, 0xfc, 161 | 0X01, 0x1f, 0X01, 0xf1, 0X01, 0xfc, 0X01, 0x0b, 0X01, 0xff, 0X01, 0xe8, 0X01, 0x00, 0X01, 0xfd, 162 | 0X02, 0x00, 0X01, 0x01, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x02, 0X01, 0xf1, 0X01, 0xfc, 0X01, 0x03, 163 | 0X01, 0xe9, 0X01, 0xf0, 0X01, 0x9f, 0X01, 0xf3, 0X01, 0x80, 0X01, 0x9e, 0X01, 0x0b, 0X01, 0x42, 164 | 0X01, 0xff, 0X01, 0x9f, 0X01, 0x00, 0X01, 0xfd, 0X01, 0x5e, 0X01, 0x13, 0X01, 0x8b, 0X01, 0xe0, 165 | 0X02, 0x00, 0X01, 0x1f, 0X01, 0xe8, 0X01, 0x13, 0X01, 0x93, 0X01, 0xe0, 0X19, 0x00, 0X01, 0xe1, 166 | 0X01, 0x5f, 0X22, 0x49, 0X01, 0xe9, 0X01, 0x5f, 0X01, 0x1c, 0X11, 0x00, 0X01, 0x40, 0X01, 0xfc, 167 | 0X01, 0x00, 0X01, 0xfd, 0X01, 0x9e, 0X01, 0xf1, 0X01, 0xf0, 0X01, 0x9e, 0X01, 0xff, 0X01, 0xe8, 168 | 0X01, 0x1f, 0X01, 0x9f, 0X01, 0x00, 0X01, 0x02, 0X01, 0xf0, 0X02, 0x00, 0X01, 0x03, 0X01, 0xf3, 169 | 0X01, 0xe0, 0X01, 0x03, 0X01, 0xff, 0X01, 0x80, 0X01, 0x41, 0X01, 0xfc, 0X01, 0x40, 0X01, 0xfe, 170 | 0X01, 0x0b, 0X01, 0x40, 0X01, 0xfc, 0X01, 0x1f, 0X01, 0x4b, 0X01, 0xf1, 0X01, 0x5e, 0X01, 0x00, 171 | 0X01, 0x5f, 0X01, 0x08, 0X01, 0x5f, 0X01, 0xfe, 0X02, 0x00, 0X01, 0x01, 0X01, 0xff, 0X01, 0xe0, 172 | 0X01, 0x1f, 0X1B, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 173 | 0X10, 0x00, 0X01, 0x02, 0X01, 0xf0, 0X01, 0x9e, 0X01, 0x01, 0X01, 0xf0, 0X01, 0x9f, 0X01, 0x83, 174 | 0X01, 0xe1, 0X01, 0xfc, 0X01, 0x13, 0X01, 0xe0, 0X01, 0x5e, 0X01, 0x1f, 0X01, 0x40, 0X01, 0x0b, 175 | 0X01, 0xe0, 0X02, 0x00, 0X01, 0x0b, 0X01, 0x83, 0X01, 0xe8, 0X01, 0x0b, 0X01, 0x80, 0X01, 0x53, 176 | 0X01, 0xf0, 0X01, 0x5f, 0X01, 0x4b, 0X01, 0xf0, 0X01, 0x1f, 0X01, 0x53, 0X01, 0xf0, 0X01, 0x9f, 177 | 0X01, 0xff, 0X01, 0x80, 0X01, 0xfd, 0X01, 0x9e, 0X01, 0x0b, 0X01, 0xe9, 0X01, 0xfe, 0X01, 0x01, 178 | 0X01, 0x9e, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xea, 0X01, 0xe8, 0X01, 0x9f, 0X01, 0x40, 0X1A, 0x00, 179 | 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 0X10, 0x00, 0X01, 0x01, 180 | 0X01, 0xff, 0X01, 0x9f, 0X01, 0x03, 0X01, 0xe8, 0X01, 0xff, 0X01, 0x0b, 0X01, 0x82, 0X01, 0xe8, 181 | 0X01, 0x13, 0X01, 0x80, 0X01, 0x9d, 0X01, 0x0b, 0X01, 0xe8, 0X01, 0x13, 0X01, 0x80, 0X02, 0x00, 182 | 0X01, 0x13, 0X01, 0x41, 0X01, 0xfd, 0X01, 0x0b, 0X01, 0xff, 0X01, 0xfd, 0X01, 0x9e, 0X01, 0x53, 183 | 0X01, 0xff, 0X01, 0xe9, 0X01, 0xff, 0X01, 0xf2, 0X01, 0xf3, 0X02, 0xff, 0X01, 0x42, 0X02, 0xff, 184 | 0X01, 0x8a, 0X03, 0xff, 0X01, 0xe8, 0X01, 0x00, 0X01, 0x1f, 0X01, 0x41, 0X01, 0xf0, 0X01, 0x1f, 185 | 0X01, 0xff, 0X01, 0x80, 0X19, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 186 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 187 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 188 | 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 189 | 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x41, 190 | 0X01, 0x5f, 0X01, 0xe3, 0X11, 0x00, 0X01, 0x0b, 0X01, 0xfe, 0X01, 0x01, 0X01, 0x80, 0X01, 0x9c, 191 | 0X01, 0x13, 0X01, 0x42, 0X01, 0xe0, 0X01, 0x13, 0X01, 0x00, 0X01, 0x10, 0X01, 0x02, 0X01, 0xe8, 192 | 0X01, 0x0b, 0X01, 0x40, 0X02, 0x00, 0X01, 0x02, 0X01, 0x00, 0X01, 0x9d, 0X01, 0x01, 0X01, 0xfe, 193 | 0X01, 0x40, 0X01, 0x0b, 0X01, 0xff, 0X01, 0x93, 0X01, 0x9f, 0X01, 0xf1, 0X01, 0x00, 0X01, 0x9f, 194 | 0X01, 0x80, 0X01, 0x13, 0X01, 0x02, 0X01, 0xfe, 0X01, 0x01, 0X01, 0xff, 0X01, 0xf1, 0X01, 0x0b, 195 | 0X01, 0xf1, 0X02, 0x00, 0X01, 0x09, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x02, 0X01, 0x9f, 0X01, 0x40, 196 | 0X19, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 197 | 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 198 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 199 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 200 | 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 201 | 0X12, 0x00, 0X01, 0x50, 0X02, 0x00, 0X01, 0x08, 0X01, 0x02, 0X02, 0x00, 0X01, 0x01, 0X0E, 0x00, 202 | 0X01, 0x01, 0X01, 0x40, 0X01, 0x13, 0X01, 0x01, 0X02, 0x00, 0X01, 0x09, 0X03, 0x00, 0X01, 0x40, 203 | 0X01, 0x00, 0X01, 0x48, 0X07, 0x00, 0X01, 0x48, 0X1C, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x4b, 204 | 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 205 | 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 206 | 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X01, 0x41, 207 | 0X01, 0x5f, 0X01, 0xe3, 0X2A, 0x00, 0X01, 0x5e, 0X2E, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 208 | 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 0X2A, 0x00, 0X01, 0x9d, 0X2E, 0x00, 0X01, 0xe1, 0X01, 0x5d, 209 | 0X22, 0x49, 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 0X2A, 0x00, 0X01, 0x10, 0X2E, 0x00, 0X01, 0xe1, 210 | 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 211 | 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 212 | 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 213 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 214 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 215 | 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 216 | 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 217 | 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 218 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 219 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 220 | 0X01, 0x5d, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 221 | 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 222 | 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 223 | 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 224 | 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 0X01, 0x41, 225 | 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 226 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 227 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 228 | 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 229 | 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x41, 230 | 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 231 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 232 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 233 | 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 234 | 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x41, 235 | 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 236 | 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 237 | 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 238 | 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 239 | 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 240 | 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 241 | 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 242 | 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 243 | 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 244 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X05, 0x00, 0X01, 0xe9, 0X01, 0x41, 245 | 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 246 | 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 247 | 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 248 | 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 249 | 0X01, 0x40, 0X05, 0x00, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 250 | 0X01, 0x5d, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 251 | 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 252 | 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x41, 0X05, 0x49, 0X01, 0xe9, 0X01, 0x41, 253 | 0X01, 0x5f, 0X01, 0x1c, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X1B, 0x49, 0X01, 0x41, 0X05, 0x49, 254 | 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0xe3, 0X59, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X1B, 0x49, 255 | 0X01, 0x41, 0X05, 0x49, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 0X06, 0x00, 0X01, 0x12, 256 | 0X13, 0x00, 0X01, 0x08, 0X05, 0x00, 0X01, 0x02, 0X01, 0x80, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xf2, 257 | 0X01, 0x40, 0X02, 0x00, 0X01, 0x01, 0X01, 0x40, 0X01, 0x00, 0X01, 0x1d, 0X04, 0x00, 0X01, 0x5f, 258 | 0X01, 0x91, 0X01, 0x0b, 0X01, 0xf1, 0X01, 0x00, 0X01, 0x01, 0X05, 0x00, 0X01, 0x01, 0X01, 0xfe, 259 | 0X01, 0x88, 0X01, 0x0b, 0X01, 0xf1, 0X02, 0x00, 0X01, 0x48, 0X02, 0x00, 0X01, 0x5f, 0X01, 0x80, 260 | 0X12, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 261 | 0X01, 0x09, 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 262 | 0X01, 0xe9, 0X04, 0x00, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 263 | 0X01, 0x00, 0X01, 0x09, 0X01, 0x5d, 0X01, 0x41, 0X05, 0x49, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 264 | 0X01, 0xe3, 0X06, 0x00, 0X01, 0x9f, 0X01, 0x80, 0X12, 0x00, 0X01, 0x9c, 0X05, 0x00, 0X01, 0x13, 265 | 0X01, 0xf0, 0X01, 0x00, 0X01, 0x02, 0X01, 0x93, 0X01, 0xe8, 0X02, 0x00, 0X01, 0x02, 0X01, 0xe0, 266 | 0X01, 0x00, 0X01, 0x1d, 0X04, 0x00, 0X01, 0x53, 0X01, 0xff, 0X01, 0x93, 0X01, 0xff, 0X01, 0xe0, 267 | 0X01, 0x9f, 0X01, 0x41, 0X01, 0x40, 0X02, 0x00, 0X01, 0x4b, 0X01, 0x40, 0X01, 0x92, 0X01, 0xfd, 268 | 0X01, 0x13, 0X01, 0xff, 0X01, 0xe0, 0X01, 0x02, 0X01, 0xfe, 0X02, 0x00, 0X01, 0x93, 0X01, 0xf0, 269 | 0X01, 0x00, 0X01, 0x9d, 0X01, 0x00, 0X01, 0x52, 0X01, 0x00, 0X01, 0x12, 0X01, 0x08, 0X0B, 0x00, 270 | 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 0X01, 0x09, 271 | 0X01, 0x5d, 0X01, 0x40, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 272 | 0X04, 0x00, 0X01, 0x01, 0X01, 0x4b, 0X01, 0x48, 0X01, 0x00, 0X01, 0x49, 0X01, 0xe9, 0X01, 0x00, 273 | 0X01, 0x09, 0X01, 0x5d, 0X01, 0x41, 0X05, 0x49, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 274 | 0X05, 0x00, 0X01, 0x02, 0X01, 0xeb, 0X01, 0x82, 0X01, 0x80, 0X01, 0x00, 0X01, 0x13, 0X01, 0x80, 275 | 0X05, 0x00, 0X01, 0x08, 0X01, 0x01, 0X02, 0x00, 0X01, 0x13, 0X01, 0x80, 0X01, 0x12, 0X01, 0x00, 276 | 0X01, 0x01, 0X01, 0xf0, 0X02, 0x00, 0X01, 0x02, 0X01, 0xf0, 0X01, 0x00, 0X01, 0x9d, 0X01, 0xf0, 277 | 0X01, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xf0, 0X01, 0x13, 0X01, 0x80, 0X01, 0x0b, 0X01, 0x40, 278 | 0X01, 0x00, 0X01, 0x40, 0X04, 0x00, 0X01, 0x1f, 0X01, 0x0a, 0X01, 0x42, 0X01, 0x93, 0X01, 0xe9, 279 | 0X01, 0xff, 0X01, 0x42, 0X01, 0xe0, 0X01, 0xe8, 0X01, 0x01, 0X01, 0xff, 0X01, 0x40, 0X01, 0xfc, 280 | 0X01, 0x1e, 0X01, 0x02, 0X01, 0x93, 0X01, 0xe8, 0X01, 0x13, 0X01, 0x93, 0X01, 0x40, 0X02, 0x00, 281 | 0X01, 0xfc, 0X01, 0x0b, 0X01, 0xf3, 0X01, 0x82, 0X01, 0xff, 0X01, 0x40, 0X01, 0xfd, 0X01, 0x5d, 282 | 0X0B, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 283 | 0X01, 0x5f, 0X02, 0xff, 0X01, 0x4b, 0X02, 0xff, 0X01, 0xe9, 0X06, 0xff, 0X01, 0x4b, 0X02, 0xff, 284 | 0X01, 0xe9, 0X02, 0xff, 0X01, 0xfd, 0X01, 0x5f, 0X05, 0xff, 0X01, 0xe9, 0X01, 0x41, 0X01, 0x5f, 285 | 0X01, 0xe3, 0X05, 0x00, 0X01, 0x0b, 0X01, 0x8b, 0X01, 0x0b, 0X01, 0xf2, 0X01, 0xe8, 0X01, 0xfe, 286 | 0X01, 0xf0, 0X01, 0x13, 0X01, 0x11, 0X01, 0x01, 0X02, 0x00, 0X01, 0x1e, 0X01, 0x03, 0X01, 0x42, 287 | 0X01, 0x80, 0X01, 0xfe, 0X01, 0xf0, 0X01, 0x5f, 0X01, 0x93, 0X01, 0x42, 0X01, 0xe2, 0X01, 0xe8, 288 | 0X01, 0x00, 0X01, 0x1f, 0X01, 0x9e, 0X01, 0x01, 0X01, 0xf1, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x13, 289 | 0X01, 0x4b, 0X01, 0xe8, 0X01, 0xfe, 0X01, 0xf0, 0X01, 0x1f, 0X01, 0x90, 0X01, 0x02, 0X01, 0xe0, 290 | 0X01, 0x9c, 0X01, 0x88, 0X02, 0x00, 0X01, 0x9d, 0X01, 0x00, 0X01, 0x0b, 0X01, 0xe0, 0X01, 0xf0, 291 | 0X01, 0x1e, 0X01, 0x0b, 0X01, 0x82, 0X01, 0xe8, 0X01, 0x53, 0X01, 0x93, 0X01, 0x42, 0X01, 0xe9, 292 | 0X01, 0xfd, 0X01, 0x0b, 0X01, 0xe0, 0X01, 0xf0, 0X01, 0x9e, 0X01, 0x1e, 0X02, 0x00, 0X01, 0x0b, 293 | 0X01, 0xf0, 0X01, 0x1f, 0X01, 0x43, 0X01, 0x81, 0X01, 0x5f, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xf0, 294 | 0X0B, 0x00, 0X01, 0xe1, 0X01, 0x5d, 0X22, 0x49, 0X01, 0x41, 0X01, 0x5f, 0X01, 0x1c, 0X05, 0x00, 295 | 0X01, 0x13, 0X01, 0x1e, 0X01, 0x03, 0X01, 0xff, 0X01, 0x8a, 0X01, 0xe8, 0X01, 0xf0, 0X01, 0x5e, 296 | 0X01, 0xff, 0X01, 0x5f, 0X01, 0x40, 0X01, 0x00, 0X01, 0x5d, 0X01, 0x13, 0X01, 0x43, 0X01, 0x82, 297 | 0X01, 0xe8, 0X01, 0xf0, 0X01, 0x1f, 0X01, 0xfe, 0X01, 0x4b, 0X01, 0x9f, 0X01, 0x40, 0X01, 0x00, 298 | 0X01, 0x9d, 0X01, 0x1e, 0X01, 0x02, 0X01, 0xe3, 0X01, 0x80, 0X01, 0x00, 0X01, 0x5f, 0X01, 0x9f, 299 | 0X01, 0x42, 0X01, 0xe8, 0X01, 0xf0, 0X01, 0x5f, 0X01, 0x9f, 0X01, 0x0b, 0X01, 0x41, 0X01, 0xf3, 300 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x01, 0X01, 0xff, 0X01, 0xe8, 0X01, 0x13, 0X01, 0x41, 0X01, 0xe8, 301 | 0X01, 0x5d, 0X01, 0x1f, 0X01, 0x0b, 0X01, 0x81, 0X01, 0xfe, 0X01, 0x1f, 0X01, 0x0b, 0X01, 0xf3, 302 | 0X01, 0xe8, 0X01, 0x13, 0X01, 0x41, 0X01, 0xe8, 0X01, 0x5e, 0X01, 0x11, 0X01, 0x00, 0X01, 0x02, 303 | 0X01, 0xff, 0X01, 0x80, 0X01, 0x9d, 0X01, 0x03, 0X01, 0x80, 0X01, 0x5d, 0X01, 0x13, 0X01, 0x42, 304 | 0X01, 0xe0, 0X0B, 0x00, 0X01, 0xe1, 0X01, 0x40, 0X22, 0x49, 0X01, 0x01, 0X01, 0x5f, 0X01, 0xe3, 305 | 0X04, 0x00, 0X01, 0x01, 0X01, 0x5d, 0X01, 0x9c, 0X02, 0x13, 0X01, 0x03, 0X01, 0x81, 0X01, 0xe8, 306 | 0X01, 0x9f, 0X01, 0x5f, 0X01, 0xf3, 0X01, 0x40, 0X01, 0x00, 0X01, 0x9c, 0X01, 0x5e, 0X01, 0x13, 307 | 0X01, 0x03, 0X01, 0x81, 0X01, 0xe8, 0X02, 0x9c, 0X01, 0x13, 0X01, 0xf0, 0X02, 0x00, 0X01, 0xf0, 308 | 0X01, 0x5d, 0X01, 0x8b, 0X01, 0x53, 0X02, 0x00, 0X01, 0xff, 0X01, 0xf0, 0X01, 0x03, 0X01, 0x81, 309 | 0X01, 0xe8, 0X01, 0x9d, 0X01, 0x1e, 0X01, 0x13, 0X01, 0x02, 0X01, 0xfe, 0X01, 0xfc, 0X01, 0x00, 310 | 0X01, 0x03, 0X01, 0xe1, 0X01, 0x40, 0X01, 0x5e, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xfc, 0X01, 0xfe, 311 | 0X01, 0x1f, 0X01, 0x01, 0X01, 0xff, 0X01, 0x9e, 0X01, 0x1f, 0X01, 0xfe, 0X01, 0x00, 0X01, 0x5e, 312 | 0X01, 0x03, 0X01, 0xe0, 0X01, 0x13, 0X02, 0x00, 0X01, 0x5f, 0X01, 0xe8, 0X01, 0x00, 0X01, 0x9c, 313 | 0X01, 0x0b, 0X01, 0x80, 0X01, 0xf0, 0X01, 0x0b, 0X01, 0xff, 0X01, 0x80, 0X0B, 0x00, 0X01, 0xe1, 314 | 0X01, 0x48, 0X01, 0x09, 0X20, 0x49, 0X01, 0x48, 0X01, 0x09, 0X01, 0x5f, 0X01, 0x1c, 0X04, 0x00, 315 | 0X01, 0x01, 0X01, 0xfe, 0X01, 0xe9, 0X01, 0xfd, 0X01, 0x1e, 0X01, 0x13, 0X01, 0x4b, 0X01, 0xff, 316 | 0X01, 0xf0, 0X01, 0x5e, 0X01, 0x13, 0X01, 0x08, 0X01, 0x00, 0X01, 0xf1, 0X01, 0xfd, 0X01, 0x9d, 317 | 0X01, 0x53, 0X01, 0x4b, 0X01, 0xff, 0X01, 0xe8, 0X01, 0xf0, 0X01, 0x9e, 0X01, 0xe8, 0X01, 0x48, 318 | 0X01, 0x01, 0X01, 0xe9, 0X02, 0xff, 0X01, 0x9d, 0X01, 0x50, 0X01, 0x01, 0X01, 0xf1, 0X01, 0xf0, 319 | 0X01, 0x0b, 0X01, 0x4b, 0X01, 0xff, 0X01, 0xf0, 0X01, 0x5d, 0X01, 0x9d, 0X01, 0x1f, 0X01, 0x81, 320 | 0X01, 0xe9, 0X01, 0x40, 0X01, 0x0b, 0X01, 0x88, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x5f, 0X01, 0x41, 321 | 0X01, 0xf2, 0X01, 0xfd, 0X01, 0x9d, 0X01, 0x03, 0X01, 0xea, 0X01, 0xfd, 0X02, 0x5e, 0X01, 0x00, 322 | 0X01, 0xfc, 0X01, 0x5f, 0X01, 0x4a, 0X01, 0x4b, 0X01, 0x80, 0X01, 0x00, 0X01, 0x5e, 0X02, 0x00, 323 | 0X01, 0x9c, 0X01, 0x1f, 0X01, 0x02, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x5f, 0X01, 0xf0, 0X0B, 0x00, 324 | 0X01, 0xe0, 0X01, 0x49, 0X22, 0x00, 0X01, 0x49, 0X01, 0xff, 0X01, 0xe3, 0X05, 0x00, 0X01, 0xff, 325 | 0X01, 0xfe, 0X01, 0x88, 0X01, 0x9e, 0X01, 0xff, 0X01, 0x9f, 0X01, 0x93, 0X01, 0xe0, 0X01, 0xfc, 326 | 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x01, 0X01, 0xf3, 0X01, 0xfe, 0X02, 0xff, 0X01, 0x9f, 0X01, 0x92, 327 | 0X01, 0x42, 0X01, 0xf3, 0X01, 0xfc, 0X01, 0xff, 0X01, 0xf0, 0X01, 0x01, 0X01, 0xf3, 0X01, 0xf2, 328 | 0X01, 0x5f, 0X01, 0xff, 0X01, 0x88, 0X01, 0x02, 0X01, 0xe0, 0X01, 0xfd, 0X01, 0x0b, 0X01, 0x9f, 329 | 0X01, 0x93, 0X01, 0xe8, 0X02, 0xfe, 0X01, 0xff, 0X01, 0x03, 0X01, 0xff, 0X01, 0x80, 0X01, 0x03, 330 | 0X01, 0xff, 0X01, 0xe1, 0X01, 0xf2, 0X01, 0xfd, 0X01, 0x01, 0X01, 0xff, 0X01, 0x9f, 0X01, 0xe8, 331 | 0X01, 0x13, 0X01, 0x40, 0X01, 0xf0, 0X01, 0x9c, 0X01, 0x1f, 0X01, 0x41, 0X01, 0xf2, 0X01, 0xfd, 332 | 0X01, 0x0b, 0X01, 0xf3, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x1f, 0X01, 0xf2, 0X01, 0x00, 0X01, 0x9d, 333 | 0X01, 0xfd, 0X01, 0x0b, 0X01, 0x80, 0X01, 0x00, 0X01, 0x5d, 0X01, 0x48, 0X0B, 0x00, 0X01, 0x1c, 334 | 0X01, 0x09, 0X22, 0x49, 0X01, 0x4b, 0X01, 0xfc, 0X01, 0x1c, 0X04, 0x00, 0X01, 0x01, 0X01, 0xea, 335 | 0X01, 0x40, 0X01, 0x00, 0X01, 0x9f, 0X01, 0x81, 0X01, 0xf0, 0X01, 0x02, 0X01, 0x81, 0X01, 0xe8, 336 | 0X01, 0x9f, 0X01, 0x40, 0X01, 0x01, 0X01, 0xfe, 0X01, 0x9f, 0X01, 0x92, 0X01, 0x41, 0X01, 0xf0, 337 | 0X01, 0x00, 0X01, 0x02, 0X01, 0xfe, 0X01, 0xf0, 0X01, 0x9e, 0X01, 0x40, 0X01, 0x00, 0X01, 0x5e, 338 | 0X01, 0x00, 0X01, 0x5d, 0X01, 0x88, 0X02, 0x00, 0X01, 0x40, 0X01, 0x5d, 0X01, 0x01, 0X01, 0xf0, 339 | 0X01, 0x03, 0X01, 0xff, 0X01, 0xfe, 0X01, 0x9f, 0X01, 0x52, 0X01, 0x0b, 0X01, 0xf0, 0X01, 0x00, 340 | 0X01, 0x01, 0X01, 0x93, 0X01, 0x81, 0X01, 0xff, 0X01, 0xe8, 0X01, 0x00, 0X01, 0x9d, 0X01, 0x5f, 341 | 0X01, 0x40, 0X01, 0x12, 0X01, 0x01, 0X01, 0xe8, 0X01, 0x08, 0X01, 0x0b, 0X01, 0x41, 0X01, 0xff, 342 | 0X01, 0xe8, 0X01, 0x00, 0X01, 0x9f, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x0a, 0X01, 0x9e, 0X01, 0x00, 343 | 0X01, 0x5f, 0X01, 0xe8, 0X01, 0x0b, 0X02, 0x00, 0X01, 0x5c, 0X0C, 0x00, 0X01, 0x03, 0X01, 0x1f, 344 | 0X22, 0x49, 0X01, 0xff, 0X01, 0xfc, 0X01, 0xe3, 0X04, 0x00, 0X01, 0x02, 0X01, 0xe0, 0X02, 0x00, 345 | 0X01, 0x08, 0X05, 0x00, 0X01, 0x08, 0X02, 0x00, 0X01, 0x48, 0X01, 0x09, 0X05, 0x00, 0X01, 0x40, 346 | 0X06, 0x00, 0X01, 0x9c, 0X08, 0x00, 0X01, 0x4a, 0X01, 0x40, 0X01, 0x08, 0X07, 0x00, 0X01, 0x51, 347 | 0X03, 0x00, 0X01, 0x08, 0X03, 0x00, 0X01, 0x40, 0X03, 0x00, 0X01, 0x51, 0X02, 0x00, 0X01, 0x01, 348 | 0X01, 0x40, 0X04, 0x00, 0X01, 0x01, 0X12, 0x00, 0X24, 0xff, 0X01, 0xe3, 0X01, 0x1c, 0X04, 0x00, 349 | 0X01, 0x03, 0X01, 0x80, 0X19, 0x00, 0X01, 0xf0, 0X39, 0x00, 0X01, 0x03, 0X01, 0x1f, 0X22, 0xff, 350 | 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe0, 0X04, 0x00, 0X01, 0x03, 0X01, 0x40, 0X19, 0x00, 0X01, 0xe8, 351 | 0X3A, 0x00, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 352 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 353 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 354 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 355 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X5B, 0x00, 0X01, 0x1c, 356 | 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 357 | 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 358 | 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 359 | 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 360 | 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X5B, 0x00, 0X01, 0x03, 0X01, 0x1c, 0X01, 0xe3, 361 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 362 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 363 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 364 | 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 0X01, 0x1c, 0X01, 0xe3, 365 | 0X01, 0x1c, 0X01, 0xe0, 0X95, 0x00, 0X01, 0x40, 0X04, 0x00, 0X01, 0x01, 0X01, 0x40, 0X04, 0x00, 366 | 0X01, 0x01, 0X01, 0x40, 0X67, 0x00, 0X01, 0x02, 0X01, 0x40, 0X03, 0x00, 0X01, 0x0b, 0X03, 0x00, 367 | 0X01, 0x02, 0X01, 0x40, 0X01, 0x01, 0X01, 0xe8, 0X04, 0x00, 0X01, 0x0b, 0X01, 0xe8, 0X01, 0x00, 368 | 0X01, 0x11, 0X02, 0x00, 0X01, 0x0b, 0X01, 0xe8, 0X0A, 0x00, 0X01, 0x1f, 0X01, 0x80, 0X01, 0x01, 369 | 0X01, 0x88, 0X01, 0x03, 0X01, 0xf0, 0X01, 0x00, 0X01, 0x51, 0X55, 0x00, 0X01, 0x0b, 0X04, 0x00, 370 | 0X01, 0x12, 0X01, 0x40, 0X01, 0x48, 0X01, 0x00, 0X01, 0x0b, 0X01, 0x00, 0X01, 0x02, 0X01, 0x88, 371 | 0X01, 0x01, 0X01, 0x40, 0X02, 0x00, 0X01, 0x1e, 0X01, 0x88, 0X01, 0x00, 0X01, 0x5c, 0X01, 0x40, 372 | 0X01, 0x00, 0X01, 0x1e, 0X01, 0x88, 0X02, 0x00, 0X01, 0x09, 0X04, 0x00, 0X01, 0x08, 0X02, 0x00, 373 | 0X01, 0x09, 0X01, 0xf0, 0X01, 0x13, 0X01, 0x9d, 0X01, 0x01, 0X01, 0x5e, 0X01, 0x02, 0X01, 0xf3, 374 | 0X01, 0x40, 0X51, 0x00, 0X01, 0x13, 0X01, 0xf0, 0X01, 0x13, 0X01, 0xfd, 0X01, 0x13, 0X01, 0xf0, 375 | 0X01, 0x1d, 0X01, 0x88, 0X01, 0x5f, 0X01, 0xe2, 0X01, 0xfd, 0X01, 0x13, 0X01, 0xfd, 0X01, 0x00, 376 | 0X01, 0x0b, 0X01, 0xfd, 0X01, 0x03, 0X01, 0x4b, 0X02, 0x00, 0X01, 0x9d, 0X01, 0xe0, 0X01, 0x9f, 377 | 0X01, 0xe8, 0X01, 0xe9, 0X01, 0x80, 0X01, 0x9d, 0X01, 0xe0, 0X01, 0xe9, 0X01, 0x40, 0X01, 0x9f, 378 | 0X03, 0x43, 0X01, 0x41, 0X01, 0xfe, 0X02, 0x00, 0X01, 0x01, 0X01, 0xf0, 0X01, 0x5d, 0X01, 0x1d, 379 | 0X01, 0x00, 0X01, 0x5e, 0X01, 0x0b, 0X01, 0x43, 0X01, 0x40, 0X51, 0x00, 0X01, 0x5d, 0X01, 0x9c, 380 | 0X01, 0x5d, 0X01, 0x9c, 0X01, 0x5d, 0X01, 0x9c, 0X01, 0x5f, 0X01, 0xfc, 0X01, 0x90, 0X01, 0x13, 381 | 0X02, 0x5d, 0X01, 0x9c, 0X01, 0x00, 0X01, 0x13, 0X01, 0x93, 0X02, 0x12, 0X02, 0x00, 0X01, 0xeb, 382 | 0X01, 0x81, 0X01, 0xea, 0X02, 0xe2, 0X01, 0x80, 0X01, 0xeb, 0X01, 0x81, 0X01, 0xff, 0X01, 0xe2, 383 | 0X01, 0xeb, 0X01, 0x4b, 0X02, 0x13, 0X01, 0x03, 0X01, 0x93, 0X02, 0x00, 0X01, 0x53, 0X01, 0xe0, 384 | 0X01, 0xf0, 0X01, 0x1d, 0X01, 0x0a, 0X01, 0xfc, 0X01, 0x1e, 0X01, 0x03, 0X01, 0x40, 0X51, 0x00, 385 | 0X06, 0xe8, 0X01, 0xfd, 0X01, 0x91, 0X01, 0xe8, 0X01, 0x5f, 0X01, 0xf0, 0X02, 0xe8, 0X01, 0x00, 386 | 0X01, 0x1d, 0X01, 0x12, 0X02, 0x5d, 0X01, 0x00, 0X01, 0x0a, 0X01, 0xf2, 0X01, 0x0b, 0X02, 0x43, 387 | 0X01, 0x4b, 0X01, 0x53, 0X01, 0xf2, 0X01, 0x0b, 0X01, 0xea, 0X01, 0x83, 0X01, 0xfe, 0X01, 0x1e, 388 | 0X01, 0x5e, 0X01, 0x5d, 0X01, 0x02, 0X01, 0x89, 0X01, 0x00, 0X01, 0x02, 0X01, 0xfd, 0X01, 0x00, 389 | 0X01, 0xe8, 0X01, 0x5d, 0X01, 0x9f, 0X01, 0x40, 0X01, 0x1d, 0X01, 0x0b, 0X51, 0x00, 0X01, 0x01, 390 | 0X01, 0xeb, 0X01, 0xf3, 0X01, 0xeb, 0X01, 0xf3, 0X01, 0xeb, 0X01, 0xf3, 0X01, 0xe1, 0X02, 0xf3, 391 | 0X01, 0xfd, 0X01, 0x0b, 0X01, 0xeb, 0X01, 0xf3, 0X01, 0x40, 0X01, 0x5d, 0X01, 0x5e, 0X01, 0xfd, 392 | 0X01, 0xfe, 0X01, 0xe8, 0X01, 0x03, 0X01, 0xff, 0X01, 0xf3, 0X01, 0x5f, 0X01, 0x9f, 0X02, 0x5f, 393 | 0X02, 0xff, 0X01, 0x0b, 0X01, 0x9f, 0X01, 0x41, 0X02, 0xfd, 0X01, 0xfe, 0X01, 0xfc, 0X01, 0x9e, 394 | 0X01, 0xe0, 0X01, 0x0b, 0X01, 0x40, 0X01, 0x00, 0X01, 0xea, 0X01, 0xf1, 0X01, 0xe8, 0X01, 0x00, 395 | 0X01, 0x1d, 0X01, 0x9e, 0X27, 0x00, 0X02, 0xff, 0X29, 0x00, 0X01, 0x9e, 0X01, 0xf1, 0X01, 0x9e, 396 | 0X01, 0xf1, 0X01, 0x9e, 0X01, 0xf3, 0X01, 0x9f, 0X01, 0x88, 0X01, 0x9e, 0X01, 0x13, 0X01, 0x91, 397 | 0X01, 0x9e, 0X01, 0xf1, 0X01, 0x00, 0X01, 0x9f, 0X01, 0xfe, 0X01, 0x5f, 0X01, 0xff, 0X01, 0x40, 398 | 0X01, 0x0b, 0X01, 0x91, 0X01, 0x02, 0X01, 0xf3, 0X01, 0x8b, 0X02, 0xf3, 0X01, 0x91, 0X01, 0x12, 399 | 0X01, 0x13, 0X01, 0x8a, 0X01, 0xf2, 0X01, 0x5f, 0X01, 0x9f, 0X01, 0x92, 0X01, 0x53, 0X01, 0xf1, 400 | 0X01, 0x00, 0X01, 0x03, 0X01, 0xfe, 0X01, 0x00, 0X01, 0x9f, 0X01, 0x40, 0X01, 0xff, 0X01, 0x80, 401 | 0X01, 0x13, 0X01, 0xe8, 0X26, 0x00, 0X01, 0x03, 0X02, 0xff, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x1c, 402 | 0X2B, 0x00, 0X01, 0x0b, 0X08, 0x00, 0X01, 0x01, 0X01, 0x40, 0X01, 0x9d, 0X01, 0xe8, 0X01, 0x00, 403 | 0X01, 0x12, 0X03, 0x00, 0X01, 0x01, 0X01, 0x41, 0X01, 0x12, 0X05, 0x00, 0X01, 0x08, 0X01, 0x09, 404 | 0X01, 0x00, 0X01, 0x01, 0X03, 0x00, 0X01, 0x52, 0X03, 0x00, 0X01, 0x0a, 0X01, 0x80, 0X29, 0x00, 405 | 0X02, 0xff, 0X02, 0x00, 0X01, 0xfc, 0X2B, 0x00, 0X01, 0x12, 0X09, 0x00, 0X01, 0x01, 0X01, 0xe2, 406 | 0X01, 0x80, 0X01, 0x00, 0X01, 0x1d, 0X05, 0x00, 0X01, 0x1d, 0X3B, 0x00, 0X01, 0x03, 0X01, 0xfc, 407 | 0X01, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X2B, 0x00, 0X01, 0x09, 0X09, 0x00, 0X01, 0x01, 0X01, 0x9f, 408 | 0X01, 0x40, 0X01, 0x00, 0X01, 0x08, 0X05, 0x00, 0X01, 0x08, 0X3B, 0x00, 0X01, 0x03, 0X01, 0xfc, 409 | 0X01, 0x00, 0X01, 0x03, 0X01, 0x00, 0X01, 0x03, 0X01, 0xfc, 0X03, 0x00, 0X01, 0xfc, 0X02, 0x00, 410 | 0X01, 0x03, 0X03, 0xff, 0X0B, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xfc, 0X61, 0x00, 0X01, 0x03, 411 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x03, 0X01, 0x00, 0X01, 0x03, 0X01, 0xff, 0X02, 0x00, 0X01, 0x03, 412 | 0X01, 0xfc, 0X02, 0x00, 0X01, 0x1f, 0X03, 0xff, 0X01, 0xe3, 0X01, 0xfc, 0X03, 0x00, 0X01, 0xfc, 413 | 0X02, 0x00, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X03, 0xff, 0X01, 0xfc, 0X02, 0x00, 0X01, 0x1f, 414 | 0X02, 0xff, 0X5B, 0x00, 0X01, 0x03, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x03, 0X01, 0xff, 0X01, 0xe0, 415 | 0X01, 0x00, 0X01, 0x1f, 0X01, 0xfc, 0X02, 0x00, 0X04, 0xff, 0X01, 0xe3, 0X01, 0xff, 0X02, 0x00, 416 | 0X01, 0x03, 0X01, 0xfc, 0X02, 0x00, 0X01, 0xfc, 0X01, 0x00, 0X02, 0xff, 0X01, 0xfc, 0X01, 0x03, 417 | 0X01, 0xff, 0X01, 0x1f, 0X04, 0xff, 0X5B, 0x00, 0X01, 0x03, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x03, 418 | 0X01, 0xff, 0X01, 0xe0, 0X01, 0x00, 0X01, 0xff, 0X01, 0xfc, 0X02, 0x00, 0X01, 0xff, 0X02, 0x00, 419 | 0X01, 0x03, 0X01, 0xe3, 0X01, 0xff, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x1f, 0X01, 0xfc, 0X01, 0x00, 420 | 0X01, 0x03, 0X01, 0xff, 0X01, 0x00, 0X02, 0xff, 0X02, 0x00, 0X01, 0x1f, 0X05, 0xff, 0X5B, 0x00, 421 | 0X01, 0x03, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x03, 0X01, 0xe3, 0X01, 0xe0, 0X01, 0x00, 0X01, 0xff, 422 | 0X01, 0xfc, 0X02, 0x00, 0X01, 0xff, 0X03, 0x00, 0X01, 0x03, 0X01, 0xff, 0X01, 0xe0, 0X01, 0x00, 423 | 0X01, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x03, 0X01, 0xff, 0X01, 0x00, 0X01, 0xff, 0X01, 0xe0, 424 | 0X02, 0x00, 0X01, 0x03, 0X03, 0xff, 0X5D, 0x00, 0X01, 0x03, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x03, 425 | 0X01, 0xe0, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xfc, 0X02, 0x00, 0X01, 0x1f, 0X03, 0x00, 426 | 0X01, 0x03, 0X01, 0xe3, 0X01, 0xe0, 0X01, 0x00, 0X01, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x03, 427 | 0X01, 0xe3, 0X01, 0xe0, 0X01, 0x1f, 0X01, 0xe0, 0X02, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X01, 0x00, 428 | 0X01, 0xff, 0X5D, 0x00, 0X01, 0x0b, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xfc, 429 | 0X01, 0x03, 0X01, 0xe0, 0X01, 0xfc, 0X02, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X02, 0x00, 0X01, 0x03, 430 | 0X01, 0xe0, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X01, 0x03, 431 | 0X01, 0xe0, 0X01, 0x1f, 0X01, 0xe0, 0X02, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X01, 0x03, 0X01, 0xfc, 432 | 0X5D, 0x00, 0X01, 0x1f, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xff, 0X01, 0x1f, 433 | 0X01, 0xe0, 0X01, 0xfc, 0X02, 0x00, 0X01, 0x03, 0X01, 0xfc, 0X02, 0x00, 0X01, 0x03, 0X01, 0xe0, 434 | 0X01, 0xfc, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X01, 0x03, 0X01, 0xe0, 435 | 0X01, 0x1f, 0X01, 0xe0, 0X02, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X01, 0x03, 0X01, 0xfc, 0X5D, 0x00, 436 | 0X01, 0x1f, 0X01, 0xe8, 0X03, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X02, 0x1f, 0X01, 0x00, 0X01, 0xfc, 437 | 0X03, 0x00, 0X01, 0xff, 0X02, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X01, 0xff, 0X01, 0x1f, 0X01, 0xe0, 438 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x1f, 0X01, 0xe0, 0X01, 0x00, 439 | 0X01, 0x1f, 0X01, 0xff, 0X01, 0x00, 0X01, 0x03, 0X01, 0xfc, 0X5D, 0x00, 0X01, 0x1f, 0X01, 0xe0, 440 | 0X03, 0x00, 0X01, 0x1f, 0X01, 0x00, 0X02, 0x1f, 0X01, 0x00, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x03, 441 | 0X01, 0xff, 0X01, 0x00, 0X01, 0x03, 0X01, 0xe0, 0X02, 0x1f, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x00, 442 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x03, 0X03, 0xff, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x03, 443 | 0X01, 0xfc, 0X5D, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X03, 0x00, 0X01, 0x1f, 0X01, 0x00, 0X01, 0x1f, 444 | 0X01, 0xff, 0X01, 0x00, 0X01, 0xfc, 0X04, 0x00, 0X01, 0xff, 0X01, 0xe0, 0X01, 0x1f, 0X01, 0x00, 445 | 0X02, 0x1f, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X01, 0x03, 446 | 0X03, 0xff, 0X01, 0xe0, 0X01, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X5D, 0x00, 0X01, 0x1f, 0X01, 0xe0, 447 | 0X03, 0x00, 0X01, 0x1f, 0X01, 0x00, 0X01, 0x03, 0X01, 0xfc, 0X01, 0x00, 0X01, 0xfc, 0X04, 0x00, 448 | 0X01, 0x03, 0X01, 0xfc, 0X01, 0x1f, 0X01, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X01, 0x00, 0X01, 0xfc, 449 | 0X01, 0x00, 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X01, 0x03, 0X01, 0xfc, 0X01, 0xff, 0X03, 0x00, 450 | 0X01, 0x1f, 0X01, 0xe0, 0X5D, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X03, 0x00, 0X01, 0x1f, 0X02, 0x00, 451 | 0X01, 0xe0, 0X01, 0x00, 0X01, 0xfc, 0X05, 0x00, 0X01, 0xff, 0X01, 0x1f, 0X01, 0x00, 0X01, 0x03, 452 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0xfc, 0X01, 0x03, 0X03, 0xff, 0X01, 0x03, 0X01, 0xfc, 0X01, 0x1f, 453 | 0X01, 0xe0, 0X02, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X5D, 0x00, 0X01, 0xff, 0X01, 0xe0, 0X03, 0x00, 454 | 0X01, 0x1f, 0X04, 0x00, 0X01, 0xfc, 0X05, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X02, 0x00, 0X01, 0xe0, 455 | 0X01, 0x00, 0X01, 0xfc, 0X01, 0x03, 0X03, 0xff, 0X01, 0xe3, 0X01, 0xfc, 0X01, 0x03, 0X01, 0xfc, 456 | 0X02, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X5D, 0x00, 0X01, 0xff, 0X04, 0x00, 0X01, 0x1f, 0X04, 0x00, 457 | 0X01, 0x1f, 0X05, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X04, 0x00, 0X01, 0xfc, 0X01, 0x03, 0X03, 0xff, 458 | 0X01, 0xe3, 0X01, 0xfc, 0X01, 0x00, 0X01, 0xff, 0X02, 0x00, 0X01, 0xff, 0X5D, 0x00, 0X01, 0x1f, 459 | 0X01, 0xff, 0X01, 0xe0, 0X03, 0x00, 0X01, 0x1f, 0X04, 0x00, 0X01, 0x1f, 0X02, 0x00, 0X01, 0xfc, 460 | 0X02, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X04, 0x00, 0X02, 0x1f, 0X02, 0x00, 0X01, 0x03, 0X01, 0xe3, 461 | 0X01, 0xfc, 0X01, 0x00, 0X01, 0x1f, 0X01, 0xfc, 0X01, 0x00, 0X01, 0xff, 0X5D, 0x00, 0X02, 0xff, 462 | 0X01, 0xfc, 0X03, 0x00, 0X01, 0x1f, 0X04, 0x00, 0X01, 0x1f, 0X02, 0x00, 0X05, 0xff, 0X04, 0x00, 463 | 0X01, 0x1f, 0X01, 0xff, 0X03, 0x00, 0X01, 0xff, 0X01, 0xfc, 0X02, 0x00, 0X01, 0xff, 0X01, 0x00, 464 | 0X01, 0xff, 0X5D, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X01, 0xe0, 0X03, 0x00, 0X01, 0x1f, 0X04, 0x00, 465 | 0X01, 0x1f, 0X02, 0x00, 0X04, 0xff, 0X01, 0x1f, 0X04, 0x00, 0X01, 0x1f, 0X01, 0xff, 0X03, 0x00, 466 | 0X01, 0xff, 0X01, 0xfc, 0X02, 0x00, 0X01, 0x1f, 0X01, 0xe0, 0X01, 0xff, 0X68, 0x00, 0X01, 0x1f, 467 | 0X02, 0x00, 0X01, 0x1f, 0X02, 0xff, 0X01, 0xfc, 0X01, 0x1f, 0X04, 0x00, 0X01, 0x1f, 0X01, 0xfc, 468 | 0X03, 0x00, 0X01, 0x1f, 0X01, 0xfc, 0X02, 0x00, 0X01, 0x03, 0X01, 0xff, 0X01, 0xfc, 0X74, 0x00, 469 | 0X01, 0x1f, 0X01, 0xfc, 0X03, 0x00, 0X01, 0x1f, 0X01, 0xfc, 0X03, 0x00, 0X01, 0xff, 0X01, 0xfc, 470 | 0XFE, 0x00, 0XFE, 0x00, 0XFE, 0x00, 0XFE, 0x00, 0XFE, 0x00, 0XE5, 0x00, 0X00, 0x00 471 | }; 472 | -------------------------------------------------------------------------------- /basic.cpp: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | Basic Interpreter 3 | Robin Edwards 2014 4 | --------------------------------------------------------------------------- 5 | This BASIC is modelled on Sinclair BASIC for the ZX81 and ZX Spectrum. It 6 | should be capable of running most of the examples in the manual for both 7 | machines, with the exception of anything machine specific (i.e. graphics, 8 | sound & system variables). 9 | 10 | Notes 11 | - All numbers (except line numbers) are floats internally 12 | - Multiple commands are allowed per line, seperated by : 13 | - LET is optional e.g. LET a = 6: b = 7 14 | - MOD provides the modulo operator which was missing from Sinclair BASIC. 15 | Both numbers are first rounded to ints e.g. 5 mod 2 = 1 16 | - CONT can be used to continue from a STOP. It does not continue from any 17 | other error condition. 18 | - Arrays can be any dimension. There is no single char limit to names. 19 | - Like Sinclair BASIC, DIM a(10) and LET a = 5 refer to different 'a's. 20 | One is a simple variable, the other is an array. There is no ambiguity 21 | since the one being referred to is always clear from the context. 22 | - String arrays differ from Sinclair BASIC. DIM a$(5,5) makes an array 23 | of 25 strings, which can be any length. e.g. LET a$(1,1)="long string" 24 | - functions like LEN, require brackets e.g. LEN(a$) 25 | - String manipulation functions are LEFT$, MID$, RIGHT$ 26 | - RND is a nonary operator not a function i.e. RND not RND() 27 | - PRINT AT x,y ... is replaced by POSITION x,y : PRINT ... 28 | - LIST takes an optional start and end e.g. LIST 1,100 or LIST 50 29 | - INKEY$ reads the last key pressed from the keyboard, or an empty string 30 | if no key pressed. The (single key) buffer is emptied after the call. 31 | e.g. a$ = INKEY$ 32 | - LOAD/SAVE load and save the current program to the EEPROM (1k limit). 33 | SAVE+ will set the auto-run flag, which loads the program automatically 34 | on boot. With a filename e.g. SAVE "test" saves to an external EEPROM. 35 | - DIR/DELETE "filename" - list and remove files from external EEPROM. 36 | - PINMODE , - sets the pin mode (0=input, 1=output, 2=pullup) 37 | - PIN , - sets the pin high (non zero) or low (zero) 38 | - PINREAD(pin) returns pin value, ANALOGRD(pin) for analog pins 39 | --------------------------------------------------------------------------- 40 | fdufnews 2019/12 41 | added ASC(string) 42 | ASC returns the ASCII code of the first car in the string 43 | modified INKEY$ 44 | INKEY$ returns any key code (even if less than 32) in order to receive function keys of the SMART Response Terminal 45 | Menu = 1 46 | Cursor keys : LEFT = 2, RIGHT = 3, UP= 4, DOWN = 5 47 | Function keys from F1 = 240 to F10 = 249 (F1 is top left and F10 is bottom right) 48 | added SLEEP 49 | SLEEP put terminal in low power mode, exit with power button 50 | fdufnews 2020/7/15 51 | added TONE 52 | Set frequency generated on buzzer pin 53 | added NOTONE 54 | Stop frequency generated on buzzer pin 55 | fdufnews 2020/08/06 56 | added MOUNT 57 | added UNMOUNT 58 | fdufnews 2021/04/01 59 | added HELP 60 | fdufnews 2021/07/22 61 | added PEEK 62 | returns content of a memory cell 63 | added POKE 64 | write a value in a memory cell 65 | 66 | */ 67 | 68 | // TODO 69 | // ABS, SIN, COS, EXP etc 70 | // DATA, READ, RESTORE 71 | 72 | // TODO fdufnews 73 | // EDIT mode using screen buffer 74 | 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | 83 | #include "basic.h" 84 | #include "host.h" 85 | 86 | #include 87 | 88 | int sysPROGEND; 89 | int sysSTACKSTART, sysSTACKEND; 90 | int sysVARSTART, sysVAREND; 91 | int sysGOSUBSTART, sysGOSUBEND; 92 | 93 | const char string_0[] PROGMEM = "OK"; 94 | const char string_1[] PROGMEM = "Bad number"; 95 | const char string_2[] PROGMEM = "Line too long"; 96 | const char string_3[] PROGMEM = "Unexpected input"; 97 | const char string_4[] PROGMEM = "Unterminated string"; 98 | const char string_5[] PROGMEM = "Missing bracket"; 99 | const char string_6[] PROGMEM = "Error in expr"; 100 | const char string_7[] PROGMEM = "Numeric expr expected"; 101 | const char string_8[] PROGMEM = "String expr expected"; 102 | const char string_9[] PROGMEM = "Line number too big"; 103 | const char string_10[] PROGMEM = "Out of memory"; 104 | const char string_11[] PROGMEM = "Div by zero"; 105 | const char string_12[] PROGMEM = "Variable not found"; 106 | const char string_13[] PROGMEM = "Bad command"; 107 | const char string_14[] PROGMEM = "Bad line number"; 108 | const char string_15[] PROGMEM = "Break pressed"; 109 | const char string_16[] PROGMEM = "NEXT without FOR"; 110 | const char string_17[] PROGMEM = "STOP statement"; 111 | const char string_18[] PROGMEM = "Missing THEN in IF"; 112 | const char string_19[] PROGMEM = "RETURN without GOSUB"; 113 | const char string_20[] PROGMEM = "Wrong array dims"; 114 | const char string_21[] PROGMEM = "Bad array index"; 115 | const char string_22[] PROGMEM = "Bad string index"; 116 | const char string_23[] PROGMEM = "Error in VAL input"; 117 | const char string_24[] PROGMEM = "Bad parameter"; 118 | 119 | //PROGMEM const char *errorTable[] = { 120 | const char* const errorTable[] PROGMEM = { 121 | string_0, string_1, string_2, string_3, 122 | string_4, string_5, string_6, string_7, 123 | string_8, string_9, string_10, string_11, 124 | string_12, string_13, string_14, string_15, 125 | string_16, string_17, string_18, string_19, 126 | string_20, string_21, string_22, string_23, 127 | string_24 128 | }; 129 | 130 | // Token flags 131 | // bits 1+2 number of arguments 132 | #define TKN_ARGS_NUM_MASK 0x03 133 | // bit 3 return type (set if string) 134 | #define TKN_RET_TYPE_STR 0x04 135 | // bits 4-6 argument type (set if string) 136 | #define TKN_ARG1_TYPE_STR 0x08 137 | #define TKN_ARG2_TYPE_STR 0x10 138 | #define TKN_ARG3_TYPE_STR 0x20 139 | 140 | #define TKN_ARG_MASK 0x38 141 | #define TKN_ARG_SHIFT 3 142 | // bits 7,8 formatting 143 | #define TKN_FMT_POST 0x40 144 | #define TKN_FMT_PRE 0x80 145 | 146 | // fdufnews 2019/12 147 | // added ASC(string) 148 | // added SLEEP 149 | // fdufnews 2020/7 150 | // added TONE(int) 151 | // added NOTONE 152 | // fdufnews 2020/08/06 153 | // added MOUNT 154 | // added UNMOUNT 155 | // fdufnews 2021/04/01 156 | // added HELP 157 | // fdufnews 2021/07/22 158 | // added PEEK 159 | 160 | PROGMEM const TokenTableEntry tokenTable[] = { 161 | {0, 0}, {0, 0}, {0, 0}, {0, 0}, 162 | {0, 0}, {0, 0}, {0, 0}, {0, 0}, 163 | {"(", 0}, {")", 0}, {"+", 0}, {"-", 0}, 164 | {"*", 0}, {"/", 0}, {"=", 0}, {">", 0}, 165 | {"<", 0}, {"<>", 0}, {">=", 0}, {"<=", 0}, 166 | {":", TKN_FMT_POST}, {";", 0}, {",", 0}, {"AND", TKN_FMT_PRE | TKN_FMT_POST}, 167 | {"OR", TKN_FMT_PRE | TKN_FMT_POST}, {"NOT", TKN_FMT_POST}, {"PRINT", TKN_FMT_POST}, {"LET", TKN_FMT_POST}, 168 | {"LIST", TKN_FMT_POST}, {"RUN", TKN_FMT_POST}, {"GOTO", TKN_FMT_POST}, {"REM", TKN_FMT_POST}, 169 | {"STOP", TKN_FMT_POST}, {"INPUT", TKN_FMT_POST}, {"CONT", TKN_FMT_POST}, {"IF", TKN_FMT_POST}, 170 | {"THEN", TKN_FMT_PRE | TKN_FMT_POST}, {"LEN", 1 | TKN_ARG1_TYPE_STR}, {"VAL", 1 | TKN_ARG1_TYPE_STR}, {"RND", 0}, 171 | {"INT", 1}, {"STR$", 1 | TKN_RET_TYPE_STR}, {"FOR", TKN_FMT_POST}, {"TO", TKN_FMT_PRE | TKN_FMT_POST}, 172 | {"STEP", TKN_FMT_PRE | TKN_FMT_POST}, {"NEXT", TKN_FMT_POST}, {"MOD", TKN_FMT_PRE | TKN_FMT_POST}, {"NEW", TKN_FMT_POST}, 173 | {"GOSUB", TKN_FMT_POST}, {"RETURN", TKN_FMT_POST}, {"DIM", TKN_FMT_POST}, {"LEFT$", 2 | TKN_ARG1_TYPE_STR | TKN_RET_TYPE_STR}, 174 | {"RIGHT$", 2 | TKN_ARG1_TYPE_STR | TKN_RET_TYPE_STR}, {"MID$", 3 | TKN_ARG1_TYPE_STR | TKN_RET_TYPE_STR}, {"CLS", TKN_FMT_POST}, {"PAUSE", TKN_FMT_POST}, 175 | {"POSITION", TKN_FMT_POST}, {"PIN", TKN_FMT_POST}, {"PINMODE", TKN_FMT_POST}, {"INKEY$", 0}, 176 | {"SAVE", TKN_FMT_POST}, {"LOAD", TKN_FMT_POST}, {"PINREAD", 1}, {"ANALOGRD", 1}, 177 | {"DIR", TKN_FMT_POST}, {"DELETE", TKN_FMT_POST}, {"ASC", 1 | TKN_ARG1_TYPE_STR}, {"SLEEP", TKN_FMT_POST}, 178 | {"TONE", 1}, {"NOTONE", TKN_FMT_POST}, {"MOUNT", TKN_FMT_POST}, {"UNMOUNT", TKN_FMT_POST}, 179 | {"HELP",TKN_FMT_POST}, {"PEEK", 1}, {"POKE", 2} 180 | }; 181 | 182 | 183 | /* ************************************************************************** 184 | PROGRAM FUNCTIONS 185 | * **************************************************************************/ 186 | void printTokens(unsigned char *p) { 187 | int modeREM = 0; 188 | while (*p != TOKEN_EOL) { 189 | if (*p == TOKEN_IDENT) { 190 | p++; 191 | while (*p < 0x80) 192 | host_outputChar(*p++); 193 | host_outputChar((*p++) - 0x80); 194 | } 195 | else if (*p == TOKEN_NUMBER) { 196 | p++; 197 | host_outputFloat(*(float*)p); 198 | p += 4; 199 | } 200 | else if (*p == TOKEN_INTEGER) { 201 | p++; 202 | host_outputInt(*(long*)p); 203 | p += 4; 204 | } 205 | else if (*p == TOKEN_STRING) { 206 | p++; 207 | if (modeREM) { 208 | host_outputString((char*)p); 209 | p += 1 + strlen((char*)p); 210 | } 211 | else { 212 | host_outputChar('\"'); 213 | while (*p) { 214 | if (*p == '\"') host_outputChar('\"'); 215 | host_outputChar(*p++); 216 | } 217 | host_outputChar('\"'); 218 | p++; 219 | } 220 | } 221 | else { 222 | uint8_t fmt = pgm_read_byte_near(&tokenTable[*p].format); 223 | if (fmt & TKN_FMT_PRE) 224 | host_outputChar(' '); 225 | host_outputString((char *)pgm_read_word(&tokenTable[*p].token)); 226 | if (fmt & TKN_FMT_POST) 227 | host_outputChar(' '); 228 | if (*p == TOKEN_REM) 229 | modeREM = 1; 230 | p++; 231 | } 232 | } 233 | } 234 | 235 | void listProg(uint16_t first, uint16_t last) { 236 | unsigned char *p = &mem[0]; 237 | host_newLine(0); 238 | while (p < &mem[sysPROGEND]) { 239 | uint16_t lineNum = *(uint16_t*)(p + 2); 240 | if ((!first || lineNum >= first) && (!last || lineNum <= last)) { 241 | host_outputInt(lineNum); 242 | host_outputChar(' '); 243 | printTokens(p + 4); 244 | host_showBuffer(); 245 | host_newLine(1); 246 | } 247 | p += *(uint16_t *)p; 248 | } 249 | } 250 | 251 | unsigned char *findProgLine(uint16_t targetLineNumber) { 252 | unsigned char *p = &mem[0]; 253 | while (p < &mem[sysPROGEND]) { 254 | uint16_t lineNum = *(uint16_t*)(p + 2); 255 | if (lineNum >= targetLineNumber) 256 | break; 257 | p += *(uint16_t *)p; 258 | } 259 | return p; 260 | } 261 | 262 | void deleteProgLine(unsigned char *p) { 263 | uint16_t lineLen = *(uint16_t*)p; 264 | sysPROGEND -= lineLen; 265 | memmove(p, p + lineLen, &mem[sysPROGEND] - p); 266 | } 267 | 268 | int doProgLine(uint16_t lineNumber, unsigned char* tokenPtr, int tokensLength) 269 | { 270 | // find line of the at or immediately after the number 271 | unsigned char *p = findProgLine(lineNumber); 272 | uint16_t foundLine = 0; 273 | if (p < &mem[sysPROGEND]) 274 | foundLine = *(uint16_t*)(p + 2); 275 | // if there's a line matching this one - delete it 276 | if (foundLine == lineNumber) 277 | deleteProgLine(p); 278 | // now check to see if this is an empty line, if so don't insert it 279 | if (*tokenPtr == TOKEN_EOL) 280 | return 1; 281 | // we now need to insert the new line at p 282 | int bytesNeeded = 4 + tokensLength; // length, linenum + tokens 283 | if (sysPROGEND + bytesNeeded > sysVARSTART) 284 | return 0; 285 | // make room if this isn't the last line 286 | if (foundLine) 287 | memmove(p + bytesNeeded, p, &mem[sysPROGEND] - p); 288 | *(uint16_t *)p = bytesNeeded; 289 | p += 2; 290 | *(uint16_t *)p = lineNumber; 291 | p += 2; 292 | memcpy(p, tokenPtr, tokensLength); 293 | sysPROGEND += bytesNeeded; 294 | return 1; 295 | } 296 | 297 | /* ************************************************************************** 298 | CALCULATOR STACK FUNCTIONS 299 | * **************************************************************************/ 300 | 301 | // Calculator stack starts at the start of memory after the program 302 | // and grows towards the end 303 | // contains either floats or null-terminated strings with the length on the end 304 | 305 | int stackPushNum(float val) { 306 | if (sysSTACKEND + sizeof(float) > sysVARSTART) 307 | return 0; // out of memory 308 | unsigned char *p = &mem[sysSTACKEND]; 309 | *(float *)p = val; 310 | sysSTACKEND += sizeof(float); 311 | return 1; 312 | } 313 | float stackPopNum() { 314 | sysSTACKEND -= sizeof(float); 315 | unsigned char *p = &mem[sysSTACKEND]; 316 | return *(float *)p; 317 | } 318 | int stackPushStr(char *str) { 319 | int len = 1 + strlen(str); 320 | if (sysSTACKEND + len + 2 > sysVARSTART) 321 | return 0; // out of memory 322 | unsigned char *p = &mem[sysSTACKEND]; 323 | strcpy((char*)p, str); 324 | p += len; 325 | *(uint16_t *)p = len; 326 | sysSTACKEND += len + 2; 327 | return 1; 328 | } 329 | char *stackGetStr() { 330 | // returns string without popping it 331 | unsigned char *p = &mem[sysSTACKEND]; 332 | int len = *(uint16_t *)(p - 2); 333 | return (char *)(p - len - 2); 334 | } 335 | char *stackPopStr() { 336 | unsigned char *p = &mem[sysSTACKEND]; 337 | int len = *(uint16_t *)(p - 2); 338 | sysSTACKEND -= (len + 2); 339 | return (char *)&mem[sysSTACKEND]; 340 | } 341 | 342 | void stackAdd2Strs() { 343 | // equivalent to popping 2 strings, concatenating them and pushing the result 344 | unsigned char *p = &mem[sysSTACKEND]; 345 | int str2len = *(uint16_t *)(p - 2); 346 | sysSTACKEND -= (str2len + 2); 347 | char *str2 = (char*)&mem[sysSTACKEND]; 348 | p = &mem[sysSTACKEND]; 349 | int str1len = *(uint16_t *)(p - 2); 350 | sysSTACKEND -= (str1len + 2); 351 | char *str1 = (char*)&mem[sysSTACKEND]; 352 | p = &mem[sysSTACKEND]; 353 | // shift the second string up (overwriting the null terminator of the first string) 354 | memmove(str1 + str1len - 1, str2, str2len); 355 | // write the length and update stackend 356 | int newLen = str1len + str2len - 1; 357 | p += newLen; 358 | *(uint16_t *)p = newLen; 359 | sysSTACKEND += newLen + 2; 360 | } 361 | 362 | // mode 0 = LEFT$, 1 = RIGHT$ 363 | void stackLeftOrRightStr(int len, int mode) { 364 | // equivalent to popping the current string, doing the operation then pushing it again 365 | unsigned char *p = &mem[sysSTACKEND]; 366 | int strlen = *(uint16_t *)(p - 2); 367 | len++; // include trailing null 368 | if (len > strlen) len = strlen; 369 | if (len == strlen) return; // nothing to do 370 | sysSTACKEND -= (strlen + 2); 371 | p = &mem[sysSTACKEND]; 372 | if (mode == 0) { 373 | // truncate the string on the stack 374 | *(p + len - 1) = 0; 375 | } 376 | else { 377 | // copy the rightmost characters 378 | memmove(p, p + strlen - len, len); 379 | } 380 | // write the length and update stackend 381 | p += len; 382 | *(uint16_t *)p = len; 383 | sysSTACKEND += len + 2; 384 | } 385 | 386 | void stackMidStr(int start, int len) { 387 | // equivalent to popping the current string, doing the operation then pushing it again 388 | unsigned char *p = &mem[sysSTACKEND]; 389 | int strlen = *(uint16_t *)(p - 2); 390 | len++; // include trailing null 391 | if (start > strlen) start = strlen; 392 | start--; // basic strings start at 1 393 | if (start + len > strlen) len = strlen - start; 394 | if (len == strlen) return; // nothing to do 395 | sysSTACKEND -= (strlen + 2); 396 | p = &mem[sysSTACKEND]; 397 | // copy the characters 398 | memmove(p, p + start, len - 1); 399 | *(p + len - 1) = 0; 400 | // write the length and update stackend 401 | p += len; 402 | *(uint16_t *)p = len; 403 | sysSTACKEND += len + 2; 404 | } 405 | 406 | /* ************************************************************************** 407 | VARIABLE TABLE FUNCTIONS 408 | * **************************************************************************/ 409 | 410 | // Variable table starts at the end of memory and grows towards the start 411 | // Simple variable 412 | // table +--------+-------+-----------------+-----------------+ . . . 413 | // <--- | len | type | name | value | 414 | // grows | 2bytes | 1byte | null terminated | float/string | 415 | // +--------+-------+-----------------+-----------------+ . . . 416 | // 417 | // Array 418 | // +--------+-------+-----------------+----------+-------+ . . .+-------+-------------+. . 419 | // | len | type | name | num dims | dim1 | | dimN | elem(1,..1) | 420 | // | 2bytes | 1byte | null terminated | 2bytes | 2bytes| | 2bytes| float | 421 | // +--------+-------+-----------------+----------+-------+ . . .+-------+-------------+. . 422 | 423 | // variable type byte 424 | #define VAR_TYPE_NUM 0x1 425 | #define VAR_TYPE_FORNEXT 0x2 426 | #define VAR_TYPE_NUM_ARRAY 0x4 427 | #define VAR_TYPE_STRING 0x8 428 | #define VAR_TYPE_STR_ARRAY 0x10 429 | 430 | unsigned char *findVariable(char *searchName, int searchMask) { 431 | unsigned char *p = &mem[sysVARSTART]; 432 | while (p < &mem[sysVAREND]) { 433 | int type = *(p + 2); 434 | if (type & searchMask) { 435 | unsigned char *name = p + 3; 436 | if (strcasecmp((char*)name, searchName) == 0) 437 | return p; 438 | } 439 | p += *(uint16_t *)p; 440 | } 441 | return NULL; 442 | } 443 | 444 | void deleteVariableAt(unsigned char *pos) { 445 | int len = *(uint16_t *)pos; 446 | if (pos == &mem[sysVARSTART]) { 447 | sysVARSTART += len; 448 | return; 449 | } 450 | memmove(&mem[sysVARSTART] + len, &mem[sysVARSTART], pos - &mem[sysVARSTART]); 451 | sysVARSTART += len; 452 | } 453 | 454 | // todo - consistently return errors rather than 1 or 0? 455 | 456 | int storeNumVariable(char *name, float val) { 457 | // these can be modified in place 458 | int nameLen = strlen(name); 459 | unsigned char *p = findVariable(name, VAR_TYPE_NUM | VAR_TYPE_FORNEXT); 460 | if (p != NULL) 461 | { // replace the old value 462 | // (could either be VAR_TYPE_NUM or VAR_TYPE_FORNEXT) 463 | p += 3; // len + type; 464 | p += nameLen + 1; 465 | *(float *)p = val; 466 | } 467 | else 468 | { // allocate a new variable 469 | int bytesNeeded = 3; // len + flags 470 | bytesNeeded += nameLen + 1; // name 471 | bytesNeeded += sizeof(float); // val 472 | 473 | if (sysVARSTART - bytesNeeded < sysSTACKEND) 474 | return 0; // out of memory 475 | sysVARSTART -= bytesNeeded; 476 | 477 | unsigned char *p = &mem[sysVARSTART]; 478 | *(uint16_t *)p = bytesNeeded; 479 | p += 2; 480 | *p++ = VAR_TYPE_NUM; 481 | strcpy((char*)p, name); 482 | p += nameLen + 1; 483 | *(float *)p = val; 484 | } 485 | return 1; 486 | } 487 | 488 | int storeForNextVariable(char *name, float start, float step, float end, uint16_t lineNum, uint16_t stmtNum) { 489 | int nameLen = strlen(name); 490 | int bytesNeeded = 3; // len + flags 491 | bytesNeeded += nameLen + 1; // name 492 | bytesNeeded += 3 * sizeof(float); // vals 493 | bytesNeeded += 2 * sizeof(uint16_t); 494 | 495 | // unlike simple numeric variables, these are reallocated if they already exist 496 | // since the existing value might be a simple variable or a for/next variable 497 | unsigned char *p = findVariable(name, VAR_TYPE_NUM | VAR_TYPE_FORNEXT); 498 | if (p != NULL) { 499 | // check there will actually be room for the new value 500 | uint16_t oldVarLen = *(uint16_t*)p; 501 | if (sysVARSTART - (bytesNeeded - oldVarLen) < sysSTACKEND) 502 | return 0; // not enough memory 503 | deleteVariableAt(p); 504 | } 505 | 506 | if (sysVARSTART - bytesNeeded < sysSTACKEND) 507 | return 0; // out of memory 508 | sysVARSTART -= bytesNeeded; 509 | 510 | p = &mem[sysVARSTART]; 511 | *(uint16_t *)p = bytesNeeded; 512 | p += 2; 513 | *p++ = VAR_TYPE_FORNEXT; 514 | strcpy((char*)p, name); 515 | p += nameLen + 1; 516 | *(float *)p = start; 517 | p += sizeof(float); 518 | *(float *)p = step; 519 | p += sizeof(float); 520 | *(float *)p = end; 521 | p += sizeof(float); 522 | *(uint16_t *)p = lineNum; 523 | p += sizeof(uint16_t); 524 | *(uint16_t *)p = stmtNum; 525 | return 1; 526 | } 527 | 528 | int storeStrVariable(char *name, char *val) { 529 | int nameLen = strlen(name); 530 | int valLen = strlen(val); 531 | int bytesNeeded = 3; // len + type 532 | bytesNeeded += nameLen + 1; // name 533 | bytesNeeded += valLen + 1; // val 534 | 535 | // strings and arrays are re-allocated if they already exist 536 | unsigned char *p = findVariable(name, VAR_TYPE_STRING); 537 | if (p != NULL) { 538 | // check there will actually be room for the new value 539 | uint16_t oldVarLen = *(uint16_t*)p; 540 | if (sysVARSTART - (bytesNeeded - oldVarLen) < sysSTACKEND) 541 | return 0; // not enough memory 542 | deleteVariableAt(p); 543 | } 544 | 545 | if (sysVARSTART - bytesNeeded < sysSTACKEND) 546 | return 0; // out of memory 547 | sysVARSTART -= bytesNeeded; 548 | 549 | p = &mem[sysVARSTART]; 550 | *(uint16_t *)p = bytesNeeded; 551 | p += 2; 552 | *p++ = VAR_TYPE_STRING; 553 | strcpy((char*)p, name); 554 | p += nameLen + 1; 555 | strcpy((char*)p, val); 556 | return 1; 557 | } 558 | 559 | int createArray(char *name, int isString) { 560 | // dimensions and number of dimensions on the calculator stack 561 | int nameLen = strlen(name); 562 | int bytesNeeded = 3; // len + flags 563 | bytesNeeded += nameLen + 1; // name 564 | bytesNeeded += 2; // num dims 565 | int numElements = 1; 566 | int i = 0; 567 | int numDims = (int)stackPopNum(); 568 | // keep the current stack position, since we'll need to pop these values again 569 | int oldSTACKEND = sysSTACKEND; 570 | for (int i = 0; i < numDims; i++) { 571 | int dim = (int)stackPopNum(); 572 | numElements *= dim; 573 | } 574 | bytesNeeded += 2 * numDims + (isString ? 1 : sizeof(float)) * numElements; 575 | // strings and arrays are re-allocated if they already exist 576 | unsigned char *p = findVariable(name, (isString ? VAR_TYPE_STR_ARRAY : VAR_TYPE_NUM_ARRAY)); 577 | if (p != NULL) { 578 | // check there will actually be room for the new value 579 | uint16_t oldVarLen = *(uint16_t*)p; 580 | if (sysVARSTART - (bytesNeeded - oldVarLen) < sysSTACKEND) 581 | return 0; // not enough memory 582 | deleteVariableAt(p); 583 | } 584 | 585 | if (sysVARSTART - bytesNeeded < sysSTACKEND) 586 | return 0; // out of memory 587 | sysVARSTART -= bytesNeeded; 588 | 589 | p = &mem[sysVARSTART]; 590 | *(uint16_t *)p = bytesNeeded; 591 | p += 2; 592 | *p++ = (isString ? VAR_TYPE_STR_ARRAY : VAR_TYPE_NUM_ARRAY); 593 | strcpy((char*)p, name); 594 | p += nameLen + 1; 595 | *(uint16_t *)p = numDims; 596 | p += 2; 597 | sysSTACKEND = oldSTACKEND; 598 | for (int i = 0; i < numDims; i++) { 599 | int dim = (int)stackPopNum(); 600 | *(uint16_t *)p = dim; 601 | p += 2; 602 | } 603 | memset(p, 0, numElements * (isString ? 1 : sizeof(float))); 604 | return 1; 605 | } 606 | 607 | int _getArrayElemOffset(unsigned char **p, int *pOffset) { 608 | // check for correct dimensionality 609 | int numArrayDims = *(uint16_t*)*p; 610 | *p += 2; 611 | int numDimsGiven = (int)stackPopNum(); 612 | if (numArrayDims != numDimsGiven) 613 | return ERROR_WRONG_ARRAY_DIMENSIONS; 614 | // now lookup the element 615 | int offset = 0; 616 | int base = 1; 617 | for (int i = 0; i < numArrayDims; i++) { 618 | int index = (int)stackPopNum(); 619 | int arrayDim = *(uint16_t*)*p; 620 | *p += 2; 621 | if (index < 1 || index > arrayDim) 622 | return ERROR_ARRAY_SUBSCRIPT_OUT_RANGE; 623 | offset += base * (index - 1); 624 | base *= arrayDim; 625 | } 626 | *pOffset = offset; 627 | return 0; 628 | } 629 | 630 | int setNumArrayElem(char *name, float val) { 631 | // each index and number of dimensions on the calculator stack 632 | unsigned char *p = findVariable(name, VAR_TYPE_NUM_ARRAY); 633 | if (p == NULL) 634 | return ERROR_VARIABLE_NOT_FOUND; 635 | p += 3 + strlen(name) + 1; 636 | 637 | int offset; 638 | int ret = _getArrayElemOffset(&p, &offset); 639 | if (ret) return ret; 640 | 641 | p += sizeof(float) * offset; 642 | *(float *)p = val; 643 | return ERROR_NONE; 644 | } 645 | 646 | int setStrArrayElem(char *name) { 647 | // string is top of the stack 648 | // each index and number of dimensions on the calculator stack 649 | 650 | // keep the current stack position, since we can't overwrite the value string 651 | int oldSTACKEND = sysSTACKEND; 652 | // how long is the new value? 653 | char *newValPtr = stackPopStr(); 654 | int newValLen = strlen(newValPtr); 655 | 656 | unsigned char *p = findVariable(name, VAR_TYPE_STR_ARRAY); 657 | unsigned char *p1 = p; // so we can correct the length when done 658 | if (p == NULL) 659 | return ERROR_VARIABLE_NOT_FOUND; 660 | 661 | p += 3 + strlen(name) + 1; 662 | 663 | int offset; 664 | int ret = _getArrayElemOffset(&p, &offset); 665 | if (ret) return ret; 666 | 667 | // find the correct element by skipping over null terminators 668 | int i = 0; 669 | while (i < offset) { 670 | if (*p == 0) i++; 671 | p++; 672 | } 673 | int oldValLen = strlen((char*)p); 674 | int bytesNeeded = newValLen - oldValLen; 675 | // check if we've got enough room for the new value 676 | if (sysVARSTART - bytesNeeded < oldSTACKEND) 677 | return 0; // out of memory 678 | // correct the length of the variable 679 | *(uint16_t*)p1 += bytesNeeded; 680 | memmove(&mem[sysVARSTART - bytesNeeded], &mem[sysVARSTART], p - &mem[sysVARSTART]); 681 | // copy in the new value 682 | strcpy((char*)(p - bytesNeeded), newValPtr); 683 | sysVARSTART -= bytesNeeded; 684 | return ERROR_NONE; 685 | } 686 | 687 | float lookupNumArrayElem(char *name, int *error) { 688 | // each index and number of dimensions on the calculator stack 689 | unsigned char *p = findVariable(name, VAR_TYPE_NUM_ARRAY); 690 | if (p == NULL) { 691 | *error = ERROR_VARIABLE_NOT_FOUND; 692 | return 0.0f; 693 | } 694 | p += 3 + strlen(name) + 1; 695 | 696 | int offset; 697 | int ret = _getArrayElemOffset(&p, &offset); 698 | if (ret) { 699 | *error = ret; 700 | return 0.0f; 701 | } 702 | p += sizeof(float) * offset; 703 | return *(float *)p; 704 | } 705 | 706 | char *lookupStrArrayElem(char *name, int *error) { 707 | // each index and number of dimensions on the calculator stack 708 | unsigned char *p = findVariable(name, VAR_TYPE_STR_ARRAY); 709 | if (p == NULL) { 710 | *error = ERROR_VARIABLE_NOT_FOUND; 711 | return NULL; 712 | } 713 | p += 3 + strlen(name) + 1; 714 | 715 | int offset; 716 | int ret = _getArrayElemOffset(&p, &offset); 717 | if (ret) { 718 | *error = ret; 719 | return NULL; 720 | } 721 | // find the correct element by skipping over null terminators 722 | int i = 0; 723 | while (i < offset) { 724 | if (*p == 0) i++; 725 | p++; 726 | } 727 | return (char *)p; 728 | } 729 | 730 | float lookupNumVariable(char *name) { 731 | unsigned char *p = findVariable(name, VAR_TYPE_NUM | VAR_TYPE_FORNEXT); 732 | if (p == NULL) { 733 | return FLT_MAX; 734 | } 735 | p += 3 + strlen(name) + 1; 736 | return *(float *)p; 737 | } 738 | 739 | char *lookupStrVariable(char *name) { 740 | unsigned char *p = findVariable(name, VAR_TYPE_STRING); 741 | if (p == NULL) { 742 | return NULL; 743 | } 744 | p += 3 + strlen(name) + 1; 745 | return (char *)p; 746 | } 747 | 748 | ForNextData lookupForNextVariable(char *name) { 749 | ForNextData ret; 750 | unsigned char *p = findVariable(name, VAR_TYPE_NUM | VAR_TYPE_FORNEXT); 751 | if (p == NULL) 752 | ret.val = FLT_MAX; 753 | else if (*(p + 2) != VAR_TYPE_FORNEXT) 754 | ret.step = FLT_MAX; 755 | else { 756 | p += 3 + strlen(name) + 1; 757 | ret.val = *(float *)p; 758 | p += sizeof(float); 759 | ret.step = *(float *)p; 760 | p += sizeof(float); 761 | ret.end = *(float *)p; 762 | p += sizeof(float); 763 | ret.lineNumber = *(uint16_t *)p; 764 | p += sizeof(uint16_t); 765 | ret.stmtNumber = *(uint16_t *)p; 766 | } 767 | return ret; 768 | } 769 | 770 | /* ************************************************************************** 771 | GOSUB STACK 772 | * **************************************************************************/ 773 | // gosub stack (if used) is after the variables 774 | int gosubStackPush(int lineNumber, int stmtNumber) { 775 | int bytesNeeded = 2 * sizeof(uint16_t); 776 | if (sysVARSTART - bytesNeeded < sysSTACKEND) 777 | return 0; // out of memory 778 | // shift the variable table 779 | memmove(&mem[sysVARSTART] - bytesNeeded, &mem[sysVARSTART], sysVAREND - sysVARSTART); 780 | sysVARSTART -= bytesNeeded; 781 | sysVAREND -= bytesNeeded; 782 | // push the return address 783 | sysGOSUBSTART = sysVAREND; 784 | uint16_t *p = (uint16_t*)&mem[sysGOSUBSTART]; 785 | *p++ = (uint16_t)lineNumber; 786 | *p = (uint16_t)stmtNumber; 787 | return 1; 788 | } 789 | 790 | int gosubStackPop(int *lineNumber, int *stmtNumber) { 791 | if (sysGOSUBSTART == sysGOSUBEND) 792 | return 0; 793 | uint16_t *p = (uint16_t*)&mem[sysGOSUBSTART]; 794 | *lineNumber = (int) * p++; 795 | *stmtNumber = (int) * p; 796 | int bytesFreed = 2 * sizeof(uint16_t); 797 | // shift the variable table 798 | memmove(&mem[sysVARSTART] + bytesFreed, &mem[sysVARSTART], sysVAREND - sysVARSTART); 799 | sysVARSTART += bytesFreed; 800 | sysVAREND += bytesFreed; 801 | sysGOSUBSTART = sysVAREND; 802 | return 1; 803 | } 804 | 805 | /* ************************************************************************** 806 | LEXER 807 | * **************************************************************************/ 808 | 809 | static unsigned char *tokenIn, *tokenOut; 810 | static int tokenOutLeft; 811 | 812 | // nextToken returns -1 for end of input, 0 for success, +ve number = error code 813 | int nextToken() 814 | { 815 | // Skip any whitespace. 816 | while (isspace(*tokenIn)) 817 | tokenIn++; 818 | // check for end of line 819 | if (*tokenIn == 0) { 820 | *tokenOut++ = TOKEN_EOL; 821 | tokenOutLeft--; 822 | return -1; 823 | } 824 | // Number: [0-9.]+ 825 | // TODO - handle 1e4 etc 826 | if (isdigit(*tokenIn) || *tokenIn == '.') { // Number: [0-9.]+ 827 | int gotDecimal = 0; 828 | char numStr[MAX_NUMBER_LEN + 1]; 829 | int numLen = 0; 830 | do { 831 | if (numLen == MAX_NUMBER_LEN) return ERROR_LEXER_BAD_NUM; 832 | if (*tokenIn == '.') { 833 | if (gotDecimal) return ERROR_LEXER_BAD_NUM; 834 | else gotDecimal = 1; 835 | } 836 | numStr[numLen++] = *tokenIn++; 837 | } 838 | while (isdigit(*tokenIn) || *tokenIn == '.'); 839 | 840 | numStr[numLen] = 0; 841 | if (tokenOutLeft <= 5) return ERROR_LEXER_TOO_LONG; 842 | tokenOutLeft -= 5; 843 | if (!gotDecimal) { 844 | long val = strtol(numStr, 0, 10); 845 | if (val == LONG_MAX || val == LONG_MIN) 846 | gotDecimal = true; 847 | else { 848 | *tokenOut++ = TOKEN_INTEGER; 849 | *(long*)tokenOut = (long)val; 850 | tokenOut += sizeof(long); 851 | } 852 | } 853 | if (gotDecimal) 854 | { 855 | *tokenOut++ = TOKEN_NUMBER; 856 | *(float*)tokenOut = (float)strtod(numStr, 0); 857 | tokenOut += sizeof(float); 858 | } 859 | return 0; 860 | } 861 | // identifier: [a-zA-Z][a-zA-Z0-9]*[$] 862 | if (isalpha(*tokenIn)) { 863 | char identStr[MAX_IDENT_LEN + 1]; 864 | int identLen = 0; 865 | identStr[identLen++] = *tokenIn++; // copy first char 866 | while (isalnum(*tokenIn) || *tokenIn == '$') { 867 | if (identLen < MAX_IDENT_LEN) 868 | identStr[identLen++] = *tokenIn; 869 | tokenIn++; 870 | } 871 | identStr[identLen] = 0; 872 | // check to see if this is a keyword 873 | for (int i = FIRST_IDENT_TOKEN; i <= LAST_IDENT_TOKEN; i++) { 874 | if (strcasecmp(identStr, (char *)pgm_read_word(&tokenTable[i].token)) == 0) { 875 | if (tokenOutLeft <= 1) return ERROR_LEXER_TOO_LONG; 876 | tokenOutLeft--; 877 | *tokenOut++ = i; 878 | // special case for REM 879 | if (i == TOKEN_REM) { 880 | *tokenOut++ = TOKEN_STRING; 881 | // skip whitespace 882 | while (isspace(*tokenIn)) 883 | tokenIn++; 884 | // copy the comment 885 | while (*tokenIn) { 886 | *tokenOut++ = *tokenIn++; 887 | } 888 | *tokenOut++ = 0; 889 | } 890 | return 0; 891 | } 892 | } 893 | // no matching keyword - this must be an identifier 894 | // $ is only allowed at the end 895 | char *dollarPos = strchr(identStr, '$'); 896 | if (dollarPos && dollarPos != &identStr[0] + identLen - 1) return ERROR_LEXER_UNEXPECTED_INPUT; 897 | if (tokenOutLeft <= 1 + identLen) return ERROR_LEXER_TOO_LONG; 898 | tokenOutLeft -= 1 + identLen; 899 | *tokenOut++ = TOKEN_IDENT; 900 | strcpy((char*)tokenOut, identStr); 901 | tokenOut[identLen - 1] |= 0x80; 902 | tokenOut += identLen; 903 | return 0; 904 | } 905 | // string 906 | if (*tokenIn == '\"') { 907 | *tokenOut++ = TOKEN_STRING; 908 | tokenOutLeft--; 909 | if (tokenOutLeft <= 1) return ERROR_LEXER_TOO_LONG; 910 | tokenIn++; 911 | while (*tokenIn) { 912 | if (*tokenIn == '\"' && *(tokenIn + 1) != '\"') 913 | break; 914 | else if (*tokenIn == '\"') 915 | tokenIn++; 916 | *tokenOut++ = *tokenIn++; 917 | tokenOutLeft--; 918 | if (tokenOutLeft <= 1) return ERROR_LEXER_TOO_LONG; 919 | } 920 | if (!*tokenIn) return ERROR_LEXER_UNTERMINATED_STRING; 921 | tokenIn++; 922 | *tokenOut++ = 0; 923 | tokenOutLeft--; 924 | return 0; 925 | } 926 | // handle non-alpha tokens e.g. = 927 | for (int i = LAST_NON_ALPHA_TOKEN; i >= FIRST_NON_ALPHA_TOKEN; i--) { 928 | // do this "backwards" so we match >= correctly, not as > then = 929 | int len = strlen((char *)pgm_read_word(&tokenTable[i].token)); 930 | if (strncmp((char *)pgm_read_word(&tokenTable[i].token), (char*)tokenIn, len) == 0) { 931 | if (tokenOutLeft <= 1) return ERROR_LEXER_TOO_LONG; 932 | *tokenOut++ = i; 933 | tokenOutLeft--; 934 | tokenIn += len; 935 | return 0; 936 | } 937 | } 938 | return ERROR_LEXER_UNEXPECTED_INPUT; 939 | } 940 | 941 | int tokenize(unsigned char *input, unsigned char *output, int outputSize) 942 | { 943 | tokenIn = input; 944 | tokenOut = output; 945 | tokenOutLeft = outputSize; 946 | int ret; 947 | while (1) { 948 | ret = nextToken(); 949 | if (ret) break; 950 | } 951 | return (ret > 0) ? ret : 0; 952 | } 953 | 954 | /* ************************************************************************** 955 | PARSER / INTERPRETER 956 | * **************************************************************************/ 957 | 958 | static char executeMode; // 0 = syntax check only, 1 = execute 959 | uint16_t lineNumber, stmtNumber; 960 | // stmt number is 0 for the first statement, then increases after each command seperator (:) 961 | // Note that IF a=1 THEN PRINT "x": print "y" is considered to be only 2 statements 962 | static uint16_t jumpLineNumber, jumpStmtNumber; 963 | static uint16_t stopLineNumber, stopStmtNumber; 964 | static char breakCurrentLine; 965 | 966 | static unsigned char *tokenBuffer, *prevToken; 967 | static int curToken; 968 | static char identVal[MAX_IDENT_LEN + 1]; 969 | static char isStrIdent; 970 | static float numVal; 971 | static char *strVal; 972 | static long numIntVal; 973 | 974 | int getNextToken() 975 | { 976 | prevToken = tokenBuffer; 977 | curToken = *tokenBuffer++; 978 | if (curToken == TOKEN_IDENT) { 979 | int i = 0; 980 | while (*tokenBuffer < 0x80) 981 | identVal[i++] = *tokenBuffer++; 982 | identVal[i] = (*tokenBuffer++) - 0x80; 983 | isStrIdent = (identVal[i++] == '$'); 984 | identVal[i++] = '\0'; 985 | } 986 | else if (curToken == TOKEN_NUMBER) { 987 | numVal = *(float*)tokenBuffer; 988 | tokenBuffer += sizeof(float); 989 | } 990 | else if (curToken == TOKEN_INTEGER) { 991 | // these are really just for line numbers 992 | numVal = (float)(*(long*)tokenBuffer); 993 | tokenBuffer += sizeof(long); 994 | } 995 | else if (curToken == TOKEN_STRING) { 996 | strVal = (char*)tokenBuffer; 997 | tokenBuffer += 1 + strlen(strVal); 998 | } 999 | return curToken; 1000 | } 1001 | 1002 | // value (int) returned from parseXXXXX 1003 | #define ERROR_MASK 0x0FFF 1004 | #define TYPE_MASK 0xF000 1005 | #define TYPE_NUMBER 0x0000 1006 | #define TYPE_STRING 0x1000 1007 | 1008 | #define IS_TYPE_NUM(x) ((x & TYPE_MASK) == TYPE_NUMBER) 1009 | #define IS_TYPE_STR(x) ((x & TYPE_MASK) == TYPE_STRING) 1010 | 1011 | // forward declarations 1012 | int parseExpression(); 1013 | int parsePrimary(); 1014 | int expectNumber(); 1015 | 1016 | // parse a number 1017 | int parseNumberExpr() 1018 | { 1019 | if (executeMode && !stackPushNum(numVal)) 1020 | return ERROR_OUT_OF_MEMORY; 1021 | getNextToken(); // consume the number 1022 | return TYPE_NUMBER; 1023 | } 1024 | 1025 | // parse (x1,....xn) e.g. DIM a(10,20) 1026 | int parseSubscriptExpr() { 1027 | // stacks x1, .. xn followed by n 1028 | int numDims = 0; 1029 | if (curToken != TOKEN_LBRACKET) return ERROR_EXPR_MISSING_BRACKET; 1030 | getNextToken(); 1031 | while (1) { 1032 | numDims++; 1033 | int val = expectNumber(); 1034 | if (val) return val; // error 1035 | if (curToken == TOKEN_RBRACKET) 1036 | break; 1037 | else if (curToken == TOKEN_COMMA) 1038 | getNextToken(); 1039 | else 1040 | return ERROR_EXPR_MISSING_BRACKET; 1041 | } 1042 | getNextToken(); // eat ) 1043 | if (executeMode && !stackPushNum(numDims)) 1044 | return ERROR_OUT_OF_MEMORY; 1045 | return 0; 1046 | } 1047 | 1048 | // parse a function call e.g. LEN(a$) 1049 | int parseFnCallExpr() { 1050 | int op = curToken; 1051 | int fnSpec = pgm_read_byte_near(&tokenTable[curToken].format); 1052 | getNextToken(); 1053 | // get the required arguments and types from the token table 1054 | if (curToken != TOKEN_LBRACKET) return ERROR_EXPR_MISSING_BRACKET; 1055 | getNextToken(); 1056 | 1057 | int reqdArgs = fnSpec & TKN_ARGS_NUM_MASK; 1058 | int argTypes = (fnSpec & TKN_ARG_MASK) >> TKN_ARG_SHIFT; 1059 | int ret = (fnSpec & TKN_RET_TYPE_STR) ? TYPE_STRING : TYPE_NUMBER; 1060 | for (int i = 0; i < reqdArgs; i++) { 1061 | int val = parseExpression(); 1062 | if (val & ERROR_MASK) return val; 1063 | // check we've got the right type 1064 | if (!(argTypes & 1) && !IS_TYPE_NUM(val)) 1065 | return ERROR_EXPR_EXPECTED_NUM; 1066 | if ((argTypes & 1) && !IS_TYPE_STR(val)) 1067 | return ERROR_EXPR_EXPECTED_STR; 1068 | argTypes >>= 1; 1069 | // if this isn't the last argument, eat the , 1070 | if (i + 1 < reqdArgs) { 1071 | if (curToken != TOKEN_COMMA) 1072 | return ERROR_UNEXPECTED_TOKEN; 1073 | getNextToken(); 1074 | } 1075 | } 1076 | // now all the arguments will be on the stack (last first) 1077 | if (executeMode) { 1078 | int tmp; 1079 | switch (op) { 1080 | case TOKEN_INT: 1081 | stackPushNum((float)floor(stackPopNum())); 1082 | break; 1083 | case TOKEN_STR: 1084 | { 1085 | char buf[16]; 1086 | if (!stackPushStr(host_floatToStr(stackPopNum(), buf))) 1087 | return ERROR_OUT_OF_MEMORY; 1088 | } 1089 | break; 1090 | case TOKEN_LEN: 1091 | tmp = strlen(stackPopStr()); 1092 | if (!stackPushNum(tmp)) return ERROR_OUT_OF_MEMORY; 1093 | break; 1094 | // added fdufnews 26/12/2019 1095 | case TOKEN_ASC: 1096 | tmp = (int ) * stackPopStr() & 0xFF; // keep it positive 1097 | if (!stackPushNum(tmp)) return ERROR_OUT_OF_MEMORY; 1098 | break; 1099 | case TOKEN_VAL: 1100 | { 1101 | // tokenise str onto the stack 1102 | int oldStackEnd = sysSTACKEND; 1103 | unsigned char *oldTokenBuffer = prevToken; 1104 | int val = tokenize((unsigned char*)stackGetStr(), &mem[sysSTACKEND], sysVARSTART - sysSTACKEND); 1105 | if (val) { 1106 | if (val == ERROR_LEXER_TOO_LONG) return ERROR_OUT_OF_MEMORY; 1107 | else return ERROR_IN_VAL_INPUT; 1108 | } 1109 | // set tokenBuffer to point to the new set of tokens on the stack 1110 | tokenBuffer = &mem[sysSTACKEND]; 1111 | // move stack end to the end of the new tokens 1112 | sysSTACKEND = tokenOut - &mem[0]; 1113 | getNextToken(); 1114 | // then parseExpression 1115 | val = parseExpression(); 1116 | if (val & ERROR_MASK) { 1117 | if (val == ERROR_OUT_OF_MEMORY) return val; 1118 | else return ERROR_IN_VAL_INPUT; 1119 | } 1120 | if (!IS_TYPE_NUM(val)) 1121 | return ERROR_EXPR_EXPECTED_NUM; 1122 | // read the result from the stack 1123 | float f = stackPopNum(); 1124 | // pop the tokens from the stack 1125 | sysSTACKEND = oldStackEnd; 1126 | // and pop the original string 1127 | stackPopStr(); 1128 | // finally, push the result and set the token buffer back 1129 | stackPushNum(f); 1130 | tokenBuffer = oldTokenBuffer; 1131 | getNextToken(); 1132 | } 1133 | break; 1134 | case TOKEN_LEFT: 1135 | tmp = (int)stackPopNum(); 1136 | if (tmp < 0) return ERROR_STR_SUBSCRIPT_OUT_RANGE; 1137 | stackLeftOrRightStr(tmp, 0); 1138 | break; 1139 | case TOKEN_RIGHT: 1140 | tmp = (int)stackPopNum(); 1141 | if (tmp < 0) return ERROR_STR_SUBSCRIPT_OUT_RANGE; 1142 | stackLeftOrRightStr(tmp, 1); 1143 | break; 1144 | case TOKEN_MID: 1145 | { 1146 | tmp = (int)stackPopNum(); 1147 | int start = stackPopNum(); 1148 | if (tmp < 0 || start < 1) return ERROR_STR_SUBSCRIPT_OUT_RANGE; 1149 | stackMidStr(start, tmp); 1150 | } 1151 | break; 1152 | case TOKEN_PINREAD: 1153 | tmp = (int)stackPopNum(); 1154 | if (!stackPushNum(host_digitalRead(tmp))) return ERROR_OUT_OF_MEMORY; 1155 | break; 1156 | case TOKEN_ANALOGRD: 1157 | tmp = (int)stackPopNum(); 1158 | if (!stackPushNum(host_analogRead(tmp))) return ERROR_OUT_OF_MEMORY; 1159 | break; 1160 | case TOKEN_PEEK: 1161 | tmp = (uint16_t)stackPopNum(); 1162 | uint8_t* ptr; 1163 | ptr = (uint8_t*)tmp; 1164 | if (!stackPushNum(*ptr)) return ERROR_OUT_OF_MEMORY; 1165 | break; 1166 | default: 1167 | return ERROR_UNEXPECTED_TOKEN; 1168 | } 1169 | } 1170 | if (curToken != TOKEN_RBRACKET) return ERROR_EXPR_MISSING_BRACKET; 1171 | getNextToken(); // eat ) 1172 | return ret; 1173 | } 1174 | 1175 | // parse an identifer e.g. a$ or a(5,3) 1176 | int parseIdentifierExpr() { 1177 | char ident[MAX_IDENT_LEN + 1]; 1178 | if (executeMode) 1179 | strcpy(ident, identVal); 1180 | int isStringIdentifier = isStrIdent; 1181 | getNextToken(); // eat ident 1182 | if (curToken == TOKEN_LBRACKET) { 1183 | // array access 1184 | int val = parseSubscriptExpr(); 1185 | if (val) return val; 1186 | if (executeMode) { 1187 | if (isStringIdentifier) { 1188 | int error = 0; 1189 | char *str = lookupStrArrayElem(ident, &error); 1190 | if (error) return error; 1191 | else if (!stackPushStr(str)) return ERROR_OUT_OF_MEMORY; 1192 | } 1193 | else { 1194 | int error = 0; 1195 | float f = lookupNumArrayElem(ident, &error); 1196 | if (error) return error; 1197 | else if (!stackPushNum(f)) return ERROR_OUT_OF_MEMORY; 1198 | } 1199 | } 1200 | } 1201 | else { 1202 | // simple variable 1203 | if (executeMode) { 1204 | if (isStringIdentifier) { 1205 | char *str = lookupStrVariable(ident); 1206 | if (!str) return ERROR_VARIABLE_NOT_FOUND; 1207 | else if (!stackPushStr(str)) return ERROR_OUT_OF_MEMORY; 1208 | } 1209 | else { 1210 | float f = lookupNumVariable(ident); 1211 | if (f == FLT_MAX) return ERROR_VARIABLE_NOT_FOUND; 1212 | else if (!stackPushNum(f)) return ERROR_OUT_OF_MEMORY; 1213 | } 1214 | } 1215 | } 1216 | return isStringIdentifier ? TYPE_STRING : TYPE_NUMBER; 1217 | } 1218 | 1219 | // parse a string e.g. "hello" 1220 | int parseStringExpr() { 1221 | if (executeMode && !stackPushStr(strVal)) 1222 | return ERROR_OUT_OF_MEMORY; 1223 | getNextToken(); // consume the string 1224 | return TYPE_STRING; 1225 | } 1226 | 1227 | // parse a bracketed expressed e.g. (5+3) 1228 | int parseParenExpr() { 1229 | getNextToken(); // eat ( 1230 | int val = parseExpression(); 1231 | if (val & ERROR_MASK) return val; 1232 | if (curToken != TOKEN_RBRACKET) 1233 | return ERROR_EXPR_MISSING_BRACKET; 1234 | getNextToken(); // eat ) 1235 | return val; 1236 | } 1237 | 1238 | int parse_RND() { 1239 | getNextToken(); 1240 | if (executeMode && !stackPushNum((float)rand() / (float)RAND_MAX)) 1241 | return ERROR_OUT_OF_MEMORY; 1242 | return TYPE_NUMBER; 1243 | } 1244 | 1245 | int parse_INKEY() { 1246 | getNextToken(); 1247 | if (executeMode) { 1248 | char str[2]; 1249 | str[0] = host_getKey(); 1250 | str[1] = 0; 1251 | if (!stackPushStr(str)) 1252 | return ERROR_OUT_OF_MEMORY; 1253 | } 1254 | return TYPE_STRING; 1255 | } 1256 | 1257 | int parseUnaryNumExp() 1258 | { 1259 | int op = curToken; 1260 | getNextToken(); 1261 | int val = parsePrimary(); 1262 | if (val & ERROR_MASK) return val; 1263 | if (!IS_TYPE_NUM(val)) 1264 | return ERROR_EXPR_EXPECTED_NUM; 1265 | switch (op) { 1266 | case TOKEN_MINUS: 1267 | if (executeMode) stackPushNum(stackPopNum() * -1.0f); 1268 | return TYPE_NUMBER; 1269 | case TOKEN_NOT: 1270 | if (executeMode) stackPushNum(stackPopNum() ? 0.0f : 1.0f); 1271 | return TYPE_NUMBER; 1272 | default: 1273 | return ERROR_UNEXPECTED_TOKEN; 1274 | } 1275 | } 1276 | 1277 | /// primary 1278 | int parsePrimary() { 1279 | switch (curToken) { 1280 | case TOKEN_IDENT: 1281 | return parseIdentifierExpr(); 1282 | case TOKEN_NUMBER: 1283 | case TOKEN_INTEGER: 1284 | return parseNumberExpr(); 1285 | case TOKEN_STRING: 1286 | return parseStringExpr(); 1287 | case TOKEN_LBRACKET: 1288 | return parseParenExpr(); 1289 | 1290 | // "pseudo-identifiers" 1291 | case TOKEN_RND: 1292 | return parse_RND(); 1293 | case TOKEN_INKEY: 1294 | return parse_INKEY(); 1295 | 1296 | // unary ops 1297 | case TOKEN_MINUS: 1298 | case TOKEN_NOT: 1299 | return parseUnaryNumExp(); 1300 | 1301 | // functions 1302 | case TOKEN_INT: 1303 | case TOKEN_STR: 1304 | case TOKEN_LEN: 1305 | case TOKEN_ASC: 1306 | case TOKEN_VAL: 1307 | case TOKEN_LEFT: 1308 | case TOKEN_RIGHT: 1309 | case TOKEN_MID: 1310 | case TOKEN_PINREAD: 1311 | case TOKEN_ANALOGRD: 1312 | case TOKEN_PEEK: 1313 | return parseFnCallExpr(); 1314 | 1315 | default: 1316 | return ERROR_UNEXPECTED_TOKEN; 1317 | } 1318 | } 1319 | 1320 | int getTokPrecedence() { 1321 | if (curToken == TOKEN_AND || curToken == TOKEN_OR) return 5; 1322 | if (curToken == TOKEN_EQUALS || curToken == TOKEN_NOT_EQ) return 10; 1323 | if (curToken == TOKEN_LT || curToken == TOKEN_GT || curToken == TOKEN_LT_EQ || curToken == TOKEN_GT_EQ) return 20; 1324 | if (curToken == TOKEN_MINUS || curToken == TOKEN_PLUS) return 30; 1325 | else if (curToken == TOKEN_MULT || curToken == TOKEN_DIV || curToken == TOKEN_MOD) return 40; 1326 | else return -1; 1327 | } 1328 | 1329 | // Operator-Precedence Parsing 1330 | int parseBinOpRHS(int ExprPrec, int lhsVal) { 1331 | // If this is a binop, find its precedence. 1332 | while (1) { 1333 | int TokPrec = getTokPrecedence(); 1334 | 1335 | // If this is a binop that binds at least as tightly as the current binop, 1336 | // consume it, otherwise we are done. 1337 | if (TokPrec < ExprPrec) 1338 | return lhsVal; 1339 | 1340 | // Okay, we know this is a binop. 1341 | int BinOp = curToken; 1342 | getNextToken(); // eat binop 1343 | 1344 | // Parse the primary expression after the binary operator. 1345 | int rhsVal = parsePrimary(); 1346 | if (rhsVal & ERROR_MASK) return rhsVal; 1347 | 1348 | // If BinOp binds less tightly with RHS than the operator after RHS, let 1349 | // the pending operator take RHS as its LHS. 1350 | int NextPrec = getTokPrecedence(); 1351 | if (TokPrec < NextPrec) { 1352 | rhsVal = parseBinOpRHS(TokPrec + 1, rhsVal); 1353 | if (rhsVal & ERROR_MASK) return rhsVal; 1354 | } 1355 | 1356 | if (IS_TYPE_NUM(lhsVal) && IS_TYPE_NUM(rhsVal)) 1357 | { // Number operations 1358 | float r, l; 1359 | if (executeMode) { 1360 | r = stackPopNum(); 1361 | l = stackPopNum(); 1362 | } 1363 | if (BinOp == TOKEN_PLUS) { 1364 | if (executeMode) stackPushNum(l + r); 1365 | } 1366 | else if (BinOp == TOKEN_MINUS) { 1367 | if (executeMode) stackPushNum(l - r); 1368 | } 1369 | else if (BinOp == TOKEN_MULT) { 1370 | if (executeMode) stackPushNum(l * r); 1371 | } 1372 | else if (BinOp == TOKEN_DIV) { 1373 | if (executeMode) { 1374 | if (r) stackPushNum(l / r); 1375 | else return ERROR_EXPR_DIV_ZERO; 1376 | } 1377 | } 1378 | else if (BinOp == TOKEN_MOD) { 1379 | if (executeMode) { 1380 | if ((int)r) stackPushNum((float)((int)l % (int)r)); 1381 | else return ERROR_EXPR_DIV_ZERO; 1382 | } 1383 | } 1384 | else if (BinOp == TOKEN_LT) { 1385 | if (executeMode) stackPushNum(l < r ? 1.0f : 0.0f); 1386 | } 1387 | else if (BinOp == TOKEN_GT) { 1388 | if (executeMode) stackPushNum(l > r ? 1.0f : 0.0f); 1389 | } 1390 | else if (BinOp == TOKEN_EQUALS) { 1391 | if (executeMode) stackPushNum(l == r ? 1.0f : 0.0f); 1392 | } 1393 | else if (BinOp == TOKEN_NOT_EQ) { 1394 | if (executeMode) stackPushNum(l != r ? 1.0f : 0.0f); 1395 | } 1396 | else if (BinOp == TOKEN_LT_EQ) { 1397 | if (executeMode) stackPushNum(l <= r ? 1.0f : 0.0f); 1398 | } 1399 | else if (BinOp == TOKEN_GT_EQ) { 1400 | if (executeMode) stackPushNum(l >= r ? 1.0f : 0.0f); 1401 | } 1402 | else if (BinOp == TOKEN_AND) { 1403 | if (executeMode) stackPushNum(r != 0.0f ? l : 0.0f); 1404 | } 1405 | else if (BinOp == TOKEN_OR) { 1406 | if (executeMode) stackPushNum(r != 0.0f ? 1 : l); 1407 | } 1408 | else 1409 | return ERROR_UNEXPECTED_TOKEN; 1410 | } 1411 | else if (IS_TYPE_STR(lhsVal) && IS_TYPE_STR(rhsVal)) 1412 | { // String operations 1413 | if (BinOp == TOKEN_PLUS) { 1414 | if (executeMode) 1415 | stackAdd2Strs(); 1416 | } 1417 | else if (BinOp >= TOKEN_EQUALS && BinOp <= TOKEN_LT_EQ) { 1418 | if (executeMode) { 1419 | char *r = stackPopStr(); 1420 | char *l = stackPopStr(); 1421 | int ret = strcmp(l, r); 1422 | if (BinOp == TOKEN_EQUALS && ret == 0) stackPushNum(1.0f); 1423 | else if (BinOp == TOKEN_NOT_EQ && ret != 0) stackPushNum(1.0f); 1424 | else if (BinOp == TOKEN_GT && ret > 0) stackPushNum(1.0f); 1425 | else if (BinOp == TOKEN_LT && ret < 0) stackPushNum(1.0f); 1426 | else if (BinOp == TOKEN_GT_EQ && ret >= 0) stackPushNum(1.0f); 1427 | else if (BinOp == TOKEN_LT_EQ && ret <= 0) stackPushNum(1.0f); 1428 | else stackPushNum(0.0f); 1429 | } 1430 | lhsVal = TYPE_NUMBER; 1431 | } 1432 | else 1433 | return ERROR_UNEXPECTED_TOKEN; 1434 | } 1435 | else 1436 | return ERROR_UNEXPECTED_TOKEN; 1437 | } 1438 | } 1439 | 1440 | int parseExpression() 1441 | { 1442 | int val = parsePrimary(); 1443 | if (val & ERROR_MASK) return val; 1444 | return parseBinOpRHS(0, val); 1445 | } 1446 | 1447 | int expectNumber() { 1448 | int val = parseExpression(); 1449 | if (val & ERROR_MASK) return val; 1450 | if (!IS_TYPE_NUM(val)) 1451 | return ERROR_EXPR_EXPECTED_NUM; 1452 | return 0; 1453 | } 1454 | 1455 | int parse_RUN() { 1456 | getNextToken(); 1457 | uint16_t startLine = 1; 1458 | if (curToken != TOKEN_EOL) { 1459 | int val = expectNumber(); 1460 | if (val) return val; // error 1461 | if (executeMode) { 1462 | startLine = (uint16_t)stackPopNum(); 1463 | if (startLine <= 0) 1464 | return ERROR_BAD_LINE_NUM; 1465 | } 1466 | } 1467 | if (executeMode) { 1468 | // clear variables 1469 | sysVARSTART = sysVAREND = sysGOSUBSTART = sysGOSUBEND = MEMORY_SIZE; 1470 | jumpLineNumber = startLine; 1471 | stopLineNumber = stopStmtNumber = 0; 1472 | } 1473 | return 0; 1474 | } 1475 | 1476 | int parse_GOTO() { 1477 | getNextToken(); 1478 | int val = expectNumber(); 1479 | if (val) return val; // error 1480 | if (executeMode) { 1481 | uint16_t startLine = (uint16_t)stackPopNum(); 1482 | if (startLine <= 0) 1483 | return ERROR_BAD_LINE_NUM; 1484 | jumpLineNumber = startLine; 1485 | } 1486 | return 0; 1487 | } 1488 | 1489 | int parse_PAUSE() { 1490 | getNextToken(); 1491 | int val = expectNumber(); 1492 | if (val) return val; // error 1493 | if (executeMode) { 1494 | long ms = (long)stackPopNum(); 1495 | if (ms < 0) 1496 | return ERROR_BAD_PARAMETER; 1497 | host_sleep(ms); 1498 | } 1499 | return 0; 1500 | } 1501 | 1502 | // fdufnews 15/7/2020 1503 | int parse_TONE() { 1504 | getNextToken(); // eat tone 1505 | int val = expectNumber(); 1506 | if (val) return val; // error 1507 | if (executeMode) { 1508 | host_tone((uint16_t)stackPopNum()); 1509 | } 1510 | return 0; 1511 | } 1512 | 1513 | int parse_LIST() { 1514 | getNextToken(); 1515 | uint16_t first = 0, last = 0; 1516 | if (curToken != TOKEN_EOL && curToken != TOKEN_CMD_SEP) { 1517 | int val = expectNumber(); 1518 | if (val) return val; // error 1519 | if (executeMode) 1520 | first = (uint16_t)stackPopNum(); 1521 | } 1522 | if (curToken == TOKEN_COMMA) { 1523 | getNextToken(); 1524 | int val = expectNumber(); 1525 | if (val) return val; // error 1526 | if (executeMode) 1527 | last = (uint16_t)stackPopNum(); 1528 | } 1529 | if (executeMode) { 1530 | listProg(first, last); 1531 | host_showBuffer(); 1532 | } 1533 | return 0; 1534 | } 1535 | 1536 | int parse_PRINT() { 1537 | getNextToken(); 1538 | // zero + expressions seperated by semicolons 1539 | int newLine = 1; 1540 | while (curToken != TOKEN_EOL && curToken != TOKEN_CMD_SEP) { 1541 | int val = parseExpression(); 1542 | if (val & ERROR_MASK) return val; 1543 | if (executeMode) { 1544 | if (IS_TYPE_NUM(val)) 1545 | host_outputFloat(stackPopNum()); 1546 | else 1547 | host_outputString(stackPopStr()); 1548 | newLine = 1; 1549 | } 1550 | if (curToken == TOKEN_SEMICOLON) { 1551 | newLine = 0; 1552 | getNextToken(); 1553 | } 1554 | } 1555 | if (executeMode) { 1556 | if (newLine) 1557 | host_newLine(); 1558 | host_showBuffer(); 1559 | } 1560 | return 0; 1561 | } 1562 | 1563 | // POKE 1564 | // write value in memory at address 1565 | void parse_Poke(uint16_t address, uint8_t value){ 1566 | uint8_t* ptr; 1567 | 1568 | ptr = (uint8_t*) address; 1569 | *ptr = value; 1570 | } 1571 | 1572 | // parse a stmt that takes two int parameters 1573 | // e.g. POSITION 3,2 1574 | int parseTwoIntCmd() { 1575 | int op = curToken; 1576 | getNextToken(); 1577 | int val = expectNumber(); 1578 | if (val) return val; // error 1579 | if (curToken != TOKEN_COMMA) 1580 | return ERROR_UNEXPECTED_TOKEN; 1581 | getNextToken(); 1582 | val = expectNumber(); 1583 | if (val) return val; // error 1584 | if (executeMode) { 1585 | int second = (int)stackPopNum(); 1586 | int first = (int)stackPopNum(); 1587 | switch (op) { 1588 | case TOKEN_POSITION: 1589 | host_moveCursor(first, second); 1590 | break; 1591 | case TOKEN_PIN: 1592 | host_digitalWrite(first, second); 1593 | break; 1594 | case TOKEN_PINMODE: 1595 | host_pinMode(first, second); 1596 | break; 1597 | case TOKEN_POKE: 1598 | parse_Poke(first, second); 1599 | } 1600 | } 1601 | return 0; 1602 | } 1603 | 1604 | // this handles both LET a$="hello" and INPUT a$ type assignments 1605 | int parseAssignment(bool inputStmt) { 1606 | char ident[MAX_IDENT_LEN + 1]; 1607 | int val; 1608 | if (curToken != TOKEN_IDENT) return ERROR_UNEXPECTED_TOKEN; 1609 | if (executeMode) 1610 | strcpy(ident, identVal); 1611 | int isStringIdentifier = isStrIdent; 1612 | int isArray = 0; 1613 | getNextToken(); // eat ident 1614 | if (curToken == TOKEN_LBRACKET) { 1615 | // array element being set 1616 | val = parseSubscriptExpr(); 1617 | if (val) return val; 1618 | isArray = 1; 1619 | } 1620 | if (inputStmt) { 1621 | // from INPUT statement 1622 | if (executeMode) { 1623 | char *inputStr = host_readLine(); 1624 | if (isStringIdentifier) { 1625 | if (!stackPushStr(inputStr)) return ERROR_OUT_OF_MEMORY; 1626 | } 1627 | else { 1628 | float f = (float)strtod(inputStr, 0); 1629 | if (!stackPushNum(f)) return ERROR_OUT_OF_MEMORY; 1630 | } 1631 | host_newLine(); 1632 | host_showBuffer(); 1633 | } 1634 | val = isStringIdentifier ? TYPE_STRING : TYPE_NUMBER; 1635 | } 1636 | else { 1637 | // from LET statement 1638 | if (curToken != TOKEN_EQUALS) return ERROR_UNEXPECTED_TOKEN; 1639 | getNextToken(); // eat = 1640 | val = parseExpression(); 1641 | if (val & ERROR_MASK) return val; 1642 | } 1643 | // type checking and actual assignment 1644 | if (!isStringIdentifier) 1645 | { // numeric variable 1646 | if (!IS_TYPE_NUM(val)) return ERROR_EXPR_EXPECTED_NUM; 1647 | if (executeMode) { 1648 | if (isArray) { 1649 | val = setNumArrayElem(ident, stackPopNum()); 1650 | if (val) return val; 1651 | } 1652 | else { 1653 | if (!storeNumVariable(ident, stackPopNum())) return ERROR_OUT_OF_MEMORY; 1654 | } 1655 | } 1656 | } 1657 | else 1658 | { // string variable 1659 | if (!IS_TYPE_STR(val)) return ERROR_EXPR_EXPECTED_STR; 1660 | if (executeMode) { 1661 | if (isArray) { 1662 | // annoyingly, we've got the string at the top of the stack 1663 | // (from parseExpression) and the array index stuff (from 1664 | // parseSubscriptExpr) underneath. 1665 | val = setStrArrayElem(ident); 1666 | if (val) return val; 1667 | } 1668 | else { 1669 | if (!storeStrVariable(ident, stackGetStr())) return ERROR_OUT_OF_MEMORY; 1670 | stackPopStr(); 1671 | } 1672 | } 1673 | } 1674 | return 0; 1675 | } 1676 | 1677 | int parse_IF() { 1678 | getNextToken(); // eat if 1679 | int val = expectNumber(); 1680 | if (val) return val; // error 1681 | if (curToken != TOKEN_THEN) 1682 | return ERROR_MISSING_THEN; 1683 | getNextToken(); 1684 | if (executeMode && stackPopNum() == 0.0f) { 1685 | // condition not met 1686 | breakCurrentLine = 1; 1687 | return 0; 1688 | } 1689 | else return 0; 1690 | } 1691 | 1692 | int parse_FOR() { 1693 | char ident[MAX_IDENT_LEN + 1]; 1694 | float start, end, step = 1.0f; 1695 | getNextToken(); // eat for 1696 | if (curToken != TOKEN_IDENT || isStrIdent) return ERROR_UNEXPECTED_TOKEN; 1697 | if (executeMode) 1698 | strcpy(ident, identVal); 1699 | getNextToken(); // eat ident 1700 | if (curToken != TOKEN_EQUALS) return ERROR_UNEXPECTED_TOKEN; 1701 | getNextToken(); // eat = 1702 | // parse START 1703 | int val = expectNumber(); 1704 | if (val) return val; // error 1705 | if (executeMode) 1706 | start = stackPopNum(); 1707 | // parse TO 1708 | if (curToken != TOKEN_TO) return ERROR_UNEXPECTED_TOKEN; 1709 | getNextToken(); // eat TO 1710 | // parse END 1711 | val = expectNumber(); 1712 | if (val) return val; // error 1713 | if (executeMode) 1714 | end = stackPopNum(); 1715 | // parse optional STEP 1716 | if (curToken == TOKEN_STEP) { 1717 | getNextToken(); // eat STEP 1718 | val = expectNumber(); 1719 | if (val) return val; // error 1720 | if (executeMode) 1721 | step = stackPopNum(); 1722 | } 1723 | if (executeMode) { 1724 | if (!storeForNextVariable(ident, start, step, end, lineNumber, stmtNumber)) return ERROR_OUT_OF_MEMORY; 1725 | } 1726 | return 0; 1727 | } 1728 | 1729 | int parse_NEXT() { 1730 | getNextToken(); // eat next 1731 | if (curToken != TOKEN_IDENT || isStrIdent) return ERROR_UNEXPECTED_TOKEN; 1732 | if (executeMode) { 1733 | ForNextData data = lookupForNextVariable(identVal); 1734 | if (data.val == FLT_MAX) return ERROR_VARIABLE_NOT_FOUND; 1735 | else if (data.step == FLT_MAX) return ERROR_NEXT_WITHOUT_FOR; 1736 | // update and store the count variable 1737 | data.val += data.step; 1738 | storeNumVariable(identVal, data.val); 1739 | // loop? 1740 | if ((data.step >= 0 && data.val <= data.end) || (data.step < 0 && data.val >= data.end)) { 1741 | jumpLineNumber = data.lineNumber; 1742 | jumpStmtNumber = data.stmtNumber + 1; 1743 | } 1744 | } 1745 | getNextToken(); // eat ident 1746 | return 0; 1747 | } 1748 | 1749 | int parse_GOSUB() { 1750 | getNextToken(); // eat gosub 1751 | int val = expectNumber(); 1752 | if (val) return val; // error 1753 | if (executeMode) { 1754 | uint16_t startLine = (uint16_t)stackPopNum(); 1755 | if (startLine <= 0) 1756 | return ERROR_BAD_LINE_NUM; 1757 | jumpLineNumber = startLine; 1758 | if (!gosubStackPush(lineNumber, stmtNumber)) 1759 | return ERROR_OUT_OF_MEMORY; 1760 | } 1761 | return 0; 1762 | } 1763 | 1764 | // LOAD or LOAD "x" 1765 | // SAVE, SAVE+ or SAVE "x" 1766 | // DELETE "x" 1767 | int parseLoadSaveCmd() { 1768 | int op = curToken; 1769 | char autoexec = 0, gotFileName = 0; 1770 | getNextToken(); 1771 | if (op == TOKEN_SAVE && curToken == TOKEN_PLUS) { 1772 | getNextToken(); 1773 | autoexec = 1; 1774 | } 1775 | else if (curToken != TOKEN_EOL && curToken != TOKEN_CMD_SEP) { 1776 | int val = parseExpression(); 1777 | if (val & ERROR_MASK) return val; 1778 | if (!IS_TYPE_STR(val)) 1779 | return ERROR_EXPR_EXPECTED_STR; 1780 | gotFileName = 1; 1781 | } 1782 | 1783 | if (executeMode) { 1784 | if (gotFileName) { 1785 | #if EXTERNAL_EEPROM 1786 | char fileName[MAX_IDENT_LEN + 1]; 1787 | if (strlen(stackGetStr()) > MAX_IDENT_LEN) 1788 | return ERROR_BAD_PARAMETER; 1789 | strcpy(fileName, stackPopStr()); 1790 | if (op == TOKEN_SAVE) { 1791 | if (!host_saveExtEEPROM(fileName)) 1792 | return ERROR_OUT_OF_MEMORY; 1793 | } 1794 | else if (op == TOKEN_LOAD) { 1795 | reset(); 1796 | if (!host_loadExtEEPROM(fileName)) 1797 | return ERROR_BAD_PARAMETER; 1798 | } 1799 | else if (op == host_removeExtEEPROM) { 1800 | if (!host_removeExtEEPROM(fileName)) 1801 | return ERROR_BAD_PARAMETER; 1802 | } 1803 | #endif 1804 | #if SD_CARD 1805 | char fileName[MAX_FILENAME_LEN + 1]; 1806 | if (strlen(stackGetStr()) > MAX_FILENAME_LEN) 1807 | return ERROR_BAD_PARAMETER; 1808 | strcpy(fileName, stackPopStr()); 1809 | //strcat(fileName, ".BAS"); // add the extension 1810 | if (op == TOKEN_SAVE) { 1811 | if (!host_saveSD(fileName)) 1812 | return ERROR_OUT_OF_MEMORY; 1813 | } 1814 | else if (op == TOKEN_LOAD) { 1815 | reset(); 1816 | if (!host_loadSD(fileName)) 1817 | return ERROR_BAD_PARAMETER; 1818 | } 1819 | else if (op == TOKEN_DELETE) { 1820 | if (!host_removeSD(fileName)) 1821 | return ERROR_BAD_PARAMETER; 1822 | } 1823 | #endif 1824 | } 1825 | else { 1826 | if (op == TOKEN_SAVE) 1827 | host_saveProgram(autoexec); 1828 | else if (op == TOKEN_LOAD) { 1829 | reset(); 1830 | host_loadProgram(); 1831 | } 1832 | else 1833 | return ERROR_UNEXPECTED_CMD; 1834 | } 1835 | } 1836 | return 0; 1837 | } 1838 | 1839 | int printHelp(void){ 1840 | host_newLine(0); // initialize count of newlines 1841 | for (int i = FIRST_IDENT_TOKEN; i <= LAST_IDENT_TOKEN; i++){ 1842 | host_outputString((char*) pgm_read_word(&tokenTable[i].token)); 1843 | // if (i < LAST_IDENT_TOKEN) host_outputString(" ,"); 1844 | // if (i == LAST_IDENT_TOKEN) host_newLine(); 1845 | host_showBuffer(); 1846 | host_newLine(1); 1847 | } 1848 | return 0; 1849 | } 1850 | 1851 | int parseSimpleCmd() { 1852 | int op = curToken; 1853 | getNextToken(); // eat op 1854 | if (executeMode) { 1855 | switch (op) { 1856 | case TOKEN_NEW: 1857 | reset(); 1858 | breakCurrentLine = 1; 1859 | break; 1860 | case TOKEN_STOP: 1861 | stopLineNumber = lineNumber; 1862 | stopStmtNumber = stmtNumber; 1863 | return ERROR_STOP_STATEMENT; 1864 | case TOKEN_CONT: 1865 | if (stopLineNumber) { 1866 | jumpLineNumber = stopLineNumber; 1867 | jumpStmtNumber = stopStmtNumber + 1; 1868 | } 1869 | break; 1870 | case TOKEN_RETURN: 1871 | { 1872 | int returnLineNumber, returnStmtNumber; 1873 | if (!gosubStackPop(&returnLineNumber, &returnStmtNumber)) 1874 | return ERROR_RETURN_WITHOUT_GOSUB; 1875 | jumpLineNumber = returnLineNumber; 1876 | jumpStmtNumber = returnStmtNumber + 1; 1877 | break; 1878 | } 1879 | case TOKEN_CLS: 1880 | host_cls(); 1881 | host_showBuffer(); 1882 | break; 1883 | case TOKEN_SLEEP: 1884 | host_goToSleep(); 1885 | host_showBuffer(); 1886 | break; 1887 | // fdufnews 15/7/2020 1888 | case TOKEN_NOTONE: 1889 | host_notone(); 1890 | break; 1891 | // fdufnews 6/08/2020 1892 | case TOKEN_MOUNT: 1893 | #if SD_CARD 1894 | host_mountSD(); 1895 | #endif 1896 | break; 1897 | case TOKEN_UNMOUNT: 1898 | #if SD_CARD 1899 | host_unmountSD(); 1900 | #endif 1901 | break; 1902 | case TOKEN_DIR: 1903 | #if EXTERNAL_EEPROM 1904 | host_directoryExtEEPROM(); 1905 | #endif 1906 | #if SD_CARD 1907 | host_directorySD(); 1908 | #endif 1909 | break; 1910 | case TOKEN_HELP: 1911 | printHelp(); 1912 | break; 1913 | } 1914 | } 1915 | return 0; 1916 | } 1917 | 1918 | int parse_DIM() { 1919 | char ident[MAX_IDENT_LEN + 1]; 1920 | getNextToken(); // eat DIM 1921 | if (curToken != TOKEN_IDENT) return ERROR_UNEXPECTED_TOKEN; 1922 | if (executeMode) 1923 | strcpy(ident, identVal); 1924 | int isStringIdentifier = isStrIdent; 1925 | getNextToken(); // eat ident 1926 | int val = parseSubscriptExpr(); 1927 | if (val) return val; 1928 | if (executeMode && !createArray(ident, isStringIdentifier ? 1 : 0)) 1929 | return ERROR_OUT_OF_MEMORY; 1930 | return 0; 1931 | } 1932 | 1933 | static int targetStmtNumber; 1934 | int parseStmts() 1935 | { 1936 | int ret = 0; 1937 | breakCurrentLine = 0; 1938 | jumpLineNumber = 0; 1939 | jumpStmtNumber = 0; 1940 | 1941 | while (ret == 0) { 1942 | if (curToken == TOKEN_EOL) 1943 | break; 1944 | if (executeMode) 1945 | sysSTACKEND = sysSTACKSTART = sysPROGEND; // clear calculator stack 1946 | int needCmdSep = 1; 1947 | switch (curToken) { 1948 | case TOKEN_PRINT: ret = parse_PRINT(); break; 1949 | case TOKEN_LET: getNextToken(); ret = parseAssignment(false); break; 1950 | case TOKEN_IDENT: ret = parseAssignment(false); break; 1951 | case TOKEN_INPUT: getNextToken(); ret = parseAssignment(true); break; 1952 | case TOKEN_LIST: ret = parse_LIST(); break; 1953 | case TOKEN_RUN: ret = parse_RUN(); break; 1954 | case TOKEN_GOTO: ret = parse_GOTO(); break; 1955 | case TOKEN_REM: getNextToken(); getNextToken(); break; 1956 | case TOKEN_IF: ret = parse_IF(); needCmdSep = 0; break; 1957 | case TOKEN_FOR: ret = parse_FOR(); break; 1958 | case TOKEN_NEXT: ret = parse_NEXT(); break; 1959 | case TOKEN_GOSUB: ret = parse_GOSUB(); break; 1960 | case TOKEN_DIM: ret = parse_DIM(); break; 1961 | case TOKEN_PAUSE: ret = parse_PAUSE(); break; 1962 | case TOKEN_TONE: ret = parse_TONE(); break; 1963 | 1964 | case TOKEN_LOAD: 1965 | case TOKEN_SAVE: 1966 | case TOKEN_DELETE: 1967 | ret = parseLoadSaveCmd(); 1968 | break; 1969 | 1970 | case TOKEN_POSITION: 1971 | case TOKEN_PIN: 1972 | case TOKEN_PINMODE: 1973 | case TOKEN_POKE: 1974 | ret = parseTwoIntCmd(); 1975 | break; 1976 | 1977 | case TOKEN_NEW: 1978 | case TOKEN_STOP: 1979 | case TOKEN_CONT: 1980 | case TOKEN_RETURN: 1981 | case TOKEN_CLS: 1982 | case TOKEN_DIR: 1983 | case TOKEN_SLEEP: 1984 | // fdufnews 15/7/2020 1985 | case TOKEN_NOTONE: 1986 | case TOKEN_MOUNT: 1987 | case TOKEN_UNMOUNT: 1988 | case TOKEN_HELP: 1989 | ret = parseSimpleCmd(); 1990 | break; 1991 | 1992 | default: 1993 | ret = ERROR_UNEXPECTED_CMD; 1994 | } 1995 | // if error, or the execution line has been changed, exit here 1996 | if (ret || breakCurrentLine || jumpLineNumber || jumpStmtNumber) 1997 | break; 1998 | // it should either be the end of the line now, and (generally) a command seperator 1999 | // before the next command 2000 | if (curToken != TOKEN_EOL) { 2001 | if (needCmdSep) { 2002 | if (curToken != TOKEN_CMD_SEP) ret = ERROR_UNEXPECTED_CMD; 2003 | else { 2004 | getNextToken(); 2005 | // don't allow a trailing : 2006 | if (curToken == TOKEN_EOL) ret = ERROR_UNEXPECTED_CMD; 2007 | } 2008 | } 2009 | } 2010 | // increase the statement number 2011 | stmtNumber++; 2012 | // if we're just scanning to find a target statement, check 2013 | if (stmtNumber == targetStmtNumber) 2014 | break; 2015 | } 2016 | return ret; 2017 | } 2018 | 2019 | int processInput(unsigned char *tokenBuf) { 2020 | // first token can be TOKEN_INTEGER for line number - stored as a float in numVal 2021 | // store as WORD line number (max 65535) 2022 | tokenBuffer = tokenBuf; 2023 | getNextToken(); 2024 | // check for a line number at the start 2025 | uint16_t gotLineNumber = 0; 2026 | unsigned char *lineStartPtr = 0; 2027 | if (curToken == TOKEN_INTEGER) { 2028 | long val = (long)numVal; 2029 | if (val <= 65535) { 2030 | gotLineNumber = (uint16_t)val; 2031 | lineStartPtr = tokenBuffer; 2032 | getNextToken(); 2033 | } 2034 | else 2035 | return ERROR_LINE_NUM_TOO_BIG; 2036 | } 2037 | 2038 | executeMode = 0; 2039 | targetStmtNumber = 0; 2040 | int ret = parseStmts(); // syntax check 2041 | if (ret != ERROR_NONE) 2042 | return ret; 2043 | 2044 | if (gotLineNumber) { 2045 | if (!doProgLine(gotLineNumber, lineStartPtr, tokenBuffer - lineStartPtr)) 2046 | ret = ERROR_OUT_OF_MEMORY; 2047 | } 2048 | else { 2049 | // we start off executing from the input buffer 2050 | tokenBuffer = tokenBuf; 2051 | executeMode = 1; 2052 | lineNumber = 0; // buffer 2053 | unsigned char *p; 2054 | 2055 | while (1) { 2056 | getNextToken(); 2057 | 2058 | stmtNumber = 0; 2059 | // skip any statements? (e.g. for/next) 2060 | if (targetStmtNumber) { 2061 | executeMode = 0; 2062 | parseStmts(); 2063 | executeMode = 1; 2064 | targetStmtNumber = 0; 2065 | } 2066 | // now execute 2067 | ret = parseStmts(); 2068 | if (ret != ERROR_NONE) 2069 | break; 2070 | 2071 | // are we processing the input buffer? 2072 | if (!lineNumber && !jumpLineNumber && !jumpStmtNumber) 2073 | break; // if no control flow jumps, then just exit 2074 | 2075 | // are we RETURNing to the input buffer? 2076 | if (lineNumber && !jumpLineNumber && jumpStmtNumber) 2077 | lineNumber = 0; 2078 | 2079 | if (!lineNumber && !jumpLineNumber && jumpStmtNumber) { 2080 | // we're executing the buffer, and need to jump stmt (e.g. for/next) 2081 | tokenBuffer = tokenBuf; 2082 | } 2083 | else { 2084 | // we're executing the program 2085 | if (jumpLineNumber || jumpStmtNumber) { 2086 | // line/statement number was changed e.g. goto 2087 | p = findProgLine(jumpLineNumber); 2088 | } 2089 | else { 2090 | // line number didn't change, so just move one to the next one 2091 | p += *(uint16_t *)p; 2092 | } 2093 | // end of program? 2094 | if (p == &mem[sysPROGEND]) 2095 | break; // end of program 2096 | 2097 | lineNumber = *(uint16_t*)(p + 2); 2098 | tokenBuffer = p + 4; 2099 | // if the target for a jump is missing (e.g. line deleted) and we're on the next line 2100 | // reset the stmt number to 0 2101 | if (jumpLineNumber && jumpStmtNumber && lineNumber > jumpLineNumber) 2102 | jumpStmtNumber = 0; 2103 | } 2104 | if (jumpStmtNumber) 2105 | targetStmtNumber = jumpStmtNumber; 2106 | 2107 | if (host_ESCPressed()) 2108 | { 2109 | ret = ERROR_BREAK_PRESSED; 2110 | break; 2111 | } 2112 | } 2113 | } 2114 | return ret; 2115 | } 2116 | 2117 | void reset() { 2118 | // program at the start of memory 2119 | sysPROGEND = 0; 2120 | // stack is at the end of the program area 2121 | sysSTACKSTART = sysSTACKEND = sysPROGEND; 2122 | // variables/gosub stack at the end of memory 2123 | sysVARSTART = sysVAREND = sysGOSUBSTART = sysGOSUBEND = MEMORY_SIZE; 2124 | memset(&mem[0], 0, MEMORY_SIZE); 2125 | 2126 | stopLineNumber = 0; 2127 | stopStmtNumber = 0; 2128 | lineNumber = 0; 2129 | } 2130 | --------------------------------------------------------------------------------