├── TFT_Terminal.ino └── M5Stack_TinyBasicPlus.ino /TFT_Terminal.ino: -------------------------------------------------------------------------------- 1 | 2 | /************************************************************* 3 | This sketch implements a simple serial receive terminal 4 | program for monitoring serial debug messages from another 5 | board. 6 | 7 | Connect GND to target board GND 8 | Connect RX line to TX line of target board 9 | Make sure the target and terminal have the same baud rate 10 | and serial stettings! 11 | 12 | The sketch works with the ILI9341 TFT 240x320 display and 13 | the called up libraries. 14 | 15 | The sketch uses the hardware scrolling feature of the 16 | display. Modification of this sketch may lead to problems 17 | unless the ILI9341 data sheet has been understood! 18 | 19 | Updated by Bodmer 21/12/16 for TFT_eSPI library: 20 | https://github.com/Bodmer/TFT_eSPI 21 | 22 | BSD license applies, all text above must be included in any 23 | redistribution 24 | *************************************************************/ 25 | 26 | #include 27 | 28 | // The scrolling area must be a integral multiple of TEXT_HEIGHT 29 | #define TEXT_HEIGHT 16 // Height of text to be printed and scrolled 30 | #define TOP_FIXED_AREA 14 // Number of lines in top fixed area (lines counted from top of screen) 31 | #define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen) 32 | #define YMAX 240 // Bottom of screen area 33 | 34 | // The initial y coordinate of the top of the scrolling area 35 | uint16_t yStart = 16; 36 | // yArea must be a integral multiple of TEXT_HEIGHT 37 | uint16_t yArea = YMAX-TOP_FIXED_AREA-BOT_FIXED_AREA; 38 | // The initial y coordinate of the top of the bottom text line 39 | // uint16_t yDraw = YMAX - BOT_FIXED_AREA - TEXT_HEIGHT; 40 | uint16_t yDraw = 0; 41 | 42 | // Keep track of the drawing x coordinate 43 | uint16_t xPos = 0; 44 | 45 | // For the byte we read from the serial port 46 | byte data = 0; 47 | 48 | // A few test variables used during debugging 49 | boolean change_colour = 1; 50 | boolean selected = 1; 51 | 52 | // We have to blank the top line each time the display is scrolled, but this takes up to 13 milliseconds 53 | // for a full width line, meanwhile the serial buffer may be filling... and overflowing 54 | // We can speed up scrolling of short text lines by just blanking the character we drew 55 | int blank[19]; // We keep all the strings pixel lengths to optimise the speed of the top line blanking 56 | 57 | void termInit() { 58 | // Setup the TFT display 59 | // M5.begin(); 60 | // M5.Lcd.setRotation(5); // Must be setRotation(0) for this sketch to work correctly 61 | M5.Lcd.fillScreen(TFT_BLACK); 62 | 63 | // Setup baud rate and draw top banner 64 | // Serial.begin(115200); 65 | 66 | // M5.Lcd.setTextColor(TFT_WHITE, TFT_BLUE); 67 | // M5.Lcd.fillRect(0,0,320,TEXT_HEIGHT, TFT_BLUE); 68 | // M5.Lcd.drawCentreString(" Serial Terminal - 115200 baud ",320/2,0,2); 69 | 70 | // Change colour for scrolling zone text 71 | M5.Lcd.setTextColor(GREEN, TFT_BLACK); 72 | 73 | // Setup scroll area 74 | // setupScrollArea(TOP_FIXED_AREA, BOT_FIXED_AREA); 75 | setupScrollArea(0, 0); 76 | 77 | // Zero the array 78 | // for (byte i = 0; i<18; i++) blank[i]=0; 79 | } 80 | 81 | 82 | void termPutchar(unsigned char data) { 83 | // These lines change the text colour when the serial buffer is emptied 84 | // These are test lines to see if we may be losing characters 85 | // Also uncomment the change_colour line below to try them 86 | // 87 | // if (change_colour){ 88 | // change_colour = 0; 89 | // if (selected == 1) {M5.Lcd.setTextColor(TFT_CYAN, TFT_BLACK); selected = 0;} 90 | // else {M5.Lcd.setTextColor(TFT_MAGENTA, TFT_BLACK); selected = 1;} 91 | //} 92 | 93 | // while (Serial.available()) 94 | { 95 | // data = Serial.read(); 96 | // If it is a CR or we are near end of line then scroll one line 97 | if (data == '\r' || xPos>311) { 98 | xPos = 0; 99 | yDraw = scroll_line(); // It can take 13ms to scroll and blank 16 pixel lines 100 | } 101 | if (data > 31 && data < 128) { 102 | xPos += M5.Lcd.drawChar(data,xPos,yDraw,2); 103 | // blank[(18+(yStart-TOP_FIXED_AREA)/TEXT_HEIGHT)%19]=xPos; // Keep a record of line lengths 104 | } 105 | //change_colour = 1; // Line to indicate buffer is being emptied 106 | } 107 | } 108 | 109 | void printString(unsigned char *str) 110 | { 111 | while(*str) termPutchar(*str++); 112 | } 113 | 114 | // ############################################################################################## 115 | // Call this function to scroll the display one text line 116 | // ############################################################################################## 117 | int scroll_line() { 118 | int yTemp = yStart; // Store the old yStart, this is where we draw the next line 119 | // Use the record of line lengths to optimise the rectangle size we need to erase the top line 120 | // M5.Lcd.fillRect(0,yStart,blank[(yStart-TOP_FIXED_AREA)/TEXT_HEIGHT],TEXT_HEIGHT, TFT_BLACK); 121 | M5.Lcd.fillRect(0,yStart,320,TEXT_HEIGHT, TFT_BLACK); 122 | 123 | // Change the top of the scroll area 124 | yStart+=TEXT_HEIGHT; 125 | // The value must wrap around as the screen memory is a circular buffer 126 | // if (yStart >= YMAX - BOT_FIXED_AREA) yStart = TOP_FIXED_AREA + (yStart - YMAX + BOT_FIXED_AREA); 127 | if (yStart >= YMAX) yStart = 0; 128 | // Now we can scroll the display 129 | scrollAddress(yStart); 130 | return yTemp; 131 | } 132 | 133 | // ############################################################################################## 134 | // Setup a portion of the screen for vertical scrolling 135 | // ############################################################################################## 136 | // We are using a hardware feature of the display, so we can only scroll in portrait orientation 137 | void setupScrollArea(uint16_t tfa, uint16_t bfa) { 138 | M5.Lcd.writecommand(ILI9341_VSCRDEF); // Vertical scroll definition 139 | M5.Lcd.writedata(tfa >> 8); // Top Fixed Area line count 140 | M5.Lcd.writedata(tfa); 141 | M5.Lcd.writedata((YMAX-tfa-bfa)>>8); // Vertical Scrolling Area line count 142 | M5.Lcd.writedata(YMAX-tfa-bfa); 143 | M5.Lcd.writedata(bfa >> 8); // Bottom Fixed Area line count 144 | M5.Lcd.writedata(bfa); 145 | } 146 | 147 | // ############################################################################################## 148 | // Setup the vertical scrolling start address pointer 149 | // ############################################################################################## 150 | void scrollAddress(uint16_t vsp) { 151 | M5.Lcd.writecommand(ILI9341_VSCRSADD); // Vertical scrolling pointer 152 | M5.Lcd.writedata(vsp>>8); 153 | M5.Lcd.writedata(vsp); 154 | } 155 | -------------------------------------------------------------------------------- /M5Stack_TinyBasicPlus.ino: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // TinyBasic Plus 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // 5 | // Authors: Mike Field 6 | // Scott Lawrence 7 | // Brian O'Dell 8 | 9 | #define kVersion "v0.14" 10 | 11 | // v0.14: 2013-11-07 12 | // Input command always set the variable to 99 13 | // Modified Input command to accept an expression using getn() 14 | // Syntax is "input x" where x is any variable 15 | // 16 | // v0.13: 2013-03-04 17 | // Support for Arduino 1.5 (SPI.h included, additional changes for DUE support) 18 | // 19 | // v0.12: 2013-03-01 20 | // EEPROM load and save routines added: EFORMAT, ELIST, ELOAD, ESAVE, ECHAIN 21 | // added EAUTORUN option (chains to EEProm saved program on startup) 22 | // Bugfixes to build properly on non-arduino systems (PROGMEM #define workaround) 23 | // cleaned up a bit of the #define options wrt TONE 24 | // 25 | // v0.11: 2013-02-20 26 | // all display strings and tables moved to PROGMEM to save space 27 | // removed second serial 28 | // removed pinMode completely, autoconf is explicit 29 | // beginnings of EEPROM related functionality (new,load,save,list) 30 | // 31 | // v0.10: 2012-10-15 32 | // added kAutoConf, which eliminates the "PINMODE" statement. 33 | // now, DWRITE,DREAD,AWRITE,AREAD automatically set the PINMODE appropriately themselves. 34 | // should save a few bytes in your programs. 35 | // 36 | // v0.09: 2012-10-12 37 | // Fixed directory listings. FILES now always works. (bug in the SD library) 38 | // ref: http://arduino.cc/forum/index.php/topic,124739.0.html 39 | // fixed filesize printouts (added printUnum for unsigned numbers) 40 | // #defineable baud rate for slow connection throttling 41 | //e 42 | // v0.08: 2012-10-02 43 | // Tone generation through piezo added (TONE, TONEW, NOTONE) 44 | // 45 | // v0.07: 2012-09-30 46 | // Autorun buildtime configuration feature 47 | // 48 | // v0.06: 2012-09-27 49 | // Added optional second serial input, used for an external keyboard 50 | // 51 | // v0.05: 2012-09-21 52 | // CHAIN to load and run a second file 53 | // RND,RSEED for random stuff 54 | // Added "!=" for "<>" synonym 55 | // Added "END" for "STOP" synonym (proper name for the functionality anyway) 56 | // 57 | // v0.04: 2012-09-20 58 | // DELAY ms - for delaying 59 | // PINMODE , INPUT|IN|I|OUTPUT|OUT|O 60 | // DWRITE , HIGH|HI|1|LOW|LO|0 61 | // AWRITE , [0..255] 62 | // fixed "save" appending to existing files instead of overwriting 63 | // Updated for building desktop command line app (incomplete) 64 | // 65 | // v0.03: 2012-09-19 66 | // Integrated Jurg Wullschleger whitespace,unary fix 67 | // Now available through github 68 | // Project renamed from "Tiny Basic in C" to "TinyBasic Plus" 69 | // 70 | // v0.02b: 2012-09-17 Scott Lawrence 71 | // Better FILES listings 72 | // 73 | // v0.02a: 2012-09-17 Scott Lawrence 74 | // Support for SD Library 75 | // Added: SAVE, FILES (mostly works), LOAD (mostly works) (redirects IO) 76 | // Added: MEM, ? (PRINT) 77 | // Quirk: "10 LET A=B+C" is ok "10 LET A = B + C" is not. 78 | // Quirk: INPUT seems broken? 79 | 80 | 81 | 82 | // IF testing with Visual C, this needs to be the first thing in the file. 83 | //#include "stdafx.h" 84 | 85 | 86 | char eliminateCompileErrors = 1; // fix to suppress arduino build errors 87 | 88 | // hack to let makefiles work with this file unchanged 89 | #ifdef FORCE_DESKTOP 90 | #undef ARDUINO 91 | #include "desktop.h" 92 | #else 93 | #define ARDUINO 1 94 | #endif 95 | 96 | 97 | //////////////////////////////////////////////////////////////////////////////// 98 | // Feature option configuration... 99 | 100 | // This enables LOAD, SAVE, FILES commands through the Arduino SD Library 101 | // it adds 9k of usage as well. 102 | //#define ENABLE_FILEIO 1 103 | #undef ENABLE_FILEIO 104 | 105 | // this turns on "autorun". if there's FileIO, and a file "autorun.bas", 106 | // then it will load it and run it when starting up 107 | //#define ENABLE_AUTORUN 1 108 | #undef ENABLE_AUTORUN 109 | // and this is the file that gets run 110 | #define kAutorunFilename "autorun.bas" 111 | 112 | // this is the alternate autorun. Autorun the program in the eeprom. 113 | // it will load whatever is in the EEProm and run it 114 | #define ENABLE_EAUTORUN 1 115 | //#undef ENABLE_EAUTORUN 116 | 117 | // this will enable the "TONE", "NOTONE" command using a piezo 118 | // element on the specified pin. Wire the red/positive/piezo to the kPiezoPin, 119 | // and the black/negative/metal disc to ground. 120 | // it adds 1.5k of usage as well. 121 | #define ENABLE_TONES 1 122 | #undef ENABLE_TONES 123 | #define kPiezoPin 5 124 | 125 | // we can use the EEProm to store a program during powerdown. This is 126 | // 1kbyte on the '328, and 512 bytes on the '168. Enabling this here will 127 | // allow for this funcitonality to work. Note that this only works on AVR 128 | // arduino. Disable it for DUE/other devices. 129 | #define ENABLE_EEPROM 1 130 | #undef ENABLE_EEPROM 131 | 132 | // Sometimes, we connect with a slower device as the console. 133 | // Set your console D0/D1 baud rate here (9600 baud default) 134 | #define kConsoleBaud 115200 135 | 136 | //////////////////////////////////////////////////////////////////////////////// 137 | #ifdef ARDUINO 138 | #include 139 | #ifndef RAMEND 140 | // okay, this is a hack for now 141 | // if we're in here, we're a DUE probably (ARM instead of AVR) 142 | 143 | #define RAMEND 4096-1 144 | 145 | // turn off EEProm 146 | #undef ENABLE_EEPROM 147 | #undef ENABLE_TONES 148 | 149 | #else 150 | // we're an AVR! 151 | 152 | // we're moving our data strings into progmem 153 | #include 154 | #endif 155 | 156 | // includes, and settings for Arduino-specific functionality 157 | #ifdef ENABLE_EEPROM 158 | #include /* NOTE: case sensitive */ 159 | int eepos = 0; 160 | #endif 161 | 162 | 163 | #ifdef ENABLE_FILEIO 164 | #include 165 | #include /* needed as of 1.5 beta */ 166 | #include 167 | 168 | // Arduino-specific configuration 169 | // set this to the card select for your SD shield 170 | #define kSD_CS 4 171 | 172 | #define kSD_Fail 0 173 | #define kSD_OK 1 174 | 175 | File fp; 176 | #endif 177 | 178 | // set up our RAM buffer size for program and user input 179 | // NOTE: This number will have to change if you include other libraries. 180 | #ifdef ARDUINO 181 | #ifdef ENABLE_FILEIO 182 | #define kRamFileIO (1030) /* approximate */ 183 | #else 184 | #define kRamFileIO (0) 185 | #endif 186 | #ifdef ENABLE_TONES 187 | #define kRamTones (40) 188 | #else 189 | #define kRamTones (0) 190 | #endif 191 | #endif /* ARDUINO */ 192 | #define kRamSize (RAMEND - 1160 - kRamFileIO - kRamTones) 193 | 194 | #ifndef ARDUINO 195 | // Not arduino setup 196 | #include 197 | #include 198 | #undef ENABLE_TONES 199 | 200 | // size of our program ram 201 | // #define kRamSize 4096 /* arbitrary */ 202 | #define kRamSize 4096*2 /* arbitrary */ 203 | 204 | #ifdef ENABLE_FILEIO 205 | FILE * fp; 206 | #endif 207 | #endif 208 | 209 | #ifdef ENABLE_FILEIO 210 | // functions defined elsehwere 211 | void cmd_Files( void ); 212 | #endif 213 | 214 | //////////////////// 215 | 216 | #ifndef boolean 217 | #define boolean int 218 | #define true 1 219 | #define false 0 220 | #endif 221 | #endif 222 | 223 | #ifndef byte 224 | typedef unsigned char byte; 225 | #endif 226 | 227 | // some catches for AVR based text string stuff... 228 | #ifndef PROGMEM 229 | #define PROGMEM 230 | #endif 231 | #ifndef pgm_read_byte 232 | #define pgm_read_byte( A ) *(A) 233 | #endif 234 | 235 | //////////////////// 236 | 237 | #ifdef ENABLE_FILEIO 238 | unsigned char * filenameWord(void); 239 | static boolean sd_is_initialized = false; 240 | #endif 241 | 242 | boolean inhibitOutput = false; 243 | static boolean runAfterLoad = false; 244 | static boolean triggerRun = false; 245 | 246 | // these will select, at runtime, where IO happens through for load/save 247 | enum { 248 | kStreamSerial = 0, 249 | kStreamEEProm, 250 | kStreamFile 251 | }; 252 | static unsigned char inStream = kStreamSerial; 253 | static unsigned char outStream = kStreamSerial; 254 | 255 | 256 | //////////////////////////////////////////////////////////////////////////////// 257 | // ASCII Characters 258 | #define CR '\r' 259 | #define NL '\n' 260 | #define LF 0x0a 261 | #define TAB '\t' 262 | #define BELL '\b' 263 | #define SPACE ' ' 264 | #define SQUOTE '\'' 265 | #define DQUOTE '\"' 266 | #define CTRLC 0x03 267 | #define CTRLH 0x08 268 | #define CTRLS 0x13 269 | #define CTRLX 0x18 270 | 271 | typedef short unsigned LINENUM; 272 | #ifdef ARDUINO 273 | #define ECHO_CHARS 1 274 | #else 275 | #define ECHO_CHARS 0 276 | #endif 277 | 278 | 279 | static unsigned char program[kRamSize]; 280 | static const char * sentinel = "HELLO"; 281 | static unsigned char *txtpos,*list_line, *tmptxtpos; 282 | static unsigned char expression_error; 283 | static unsigned char *tempsp; 284 | 285 | /***********************************************************/ 286 | // Keyword table and constants - the last character has 0x80 added to it 287 | const static unsigned char keywords[] PROGMEM = { 288 | 'L','I','S','T'+0x80, 289 | 'L','O','A','D'+0x80, 290 | 'N','E','W'+0x80, 291 | 'R','U','N'+0x80, 292 | 'S','A','V','E'+0x80, 293 | 'N','E','X','T'+0x80, 294 | 'L','E','T'+0x80, 295 | 'I','F'+0x80, 296 | 'G','O','T','O'+0x80, 297 | 'G','O','S','U','B'+0x80, 298 | 'R','E','T','U','R','N'+0x80, 299 | 'R','E','M'+0x80, 300 | 'F','O','R'+0x80, 301 | 'I','N','P','U','T'+0x80, 302 | 'P','R','I','N','T'+0x80, 303 | 'P','O','K','E'+0x80, 304 | 'S','T','O','P'+0x80, 305 | 'B','Y','E'+0x80, 306 | 'F','I','L','E','S'+0x80, 307 | 'M','E','M'+0x80, 308 | '?'+ 0x80, 309 | '\''+ 0x80, 310 | 'A','W','R','I','T','E'+0x80, 311 | 'D','W','R','I','T','E'+0x80, 312 | 'D','E','L','A','Y'+0x80, 313 | 'E','N','D'+0x80, 314 | 'R','S','E','E','D'+0x80, 315 | 'C','H','A','I','N'+0x80, 316 | #ifdef ENABLE_TONES 317 | 'T','O','N','E','W'+0x80, 318 | 'T','O','N','E'+0x80, 319 | 'N','O','T','O','N','E'+0x80, 320 | #endif 321 | #ifdef ARDUINO 322 | #ifdef ENABLE_EEPROM 323 | 'E','C','H','A','I','N'+0x80, 324 | 'E','L','I','S','T'+0x80, 325 | 'E','L','O','A','D'+0x80, 326 | 'E','F','O','R','M','A','T'+0x80, 327 | 'E','S','A','V','E'+0x80, 328 | #endif 329 | #endif 330 | 0 331 | }; 332 | 333 | // by moving the command list to an enum, we can easily remove sections 334 | // above and below simultaneously to selectively obliterate functionality. 335 | enum { 336 | KW_LIST = 0, 337 | KW_LOAD, KW_NEW, KW_RUN, KW_SAVE, 338 | KW_NEXT, KW_LET, KW_IF, 339 | KW_GOTO, KW_GOSUB, KW_RETURN, 340 | KW_REM, 341 | KW_FOR, 342 | KW_INPUT, KW_PRINT, 343 | KW_POKE, 344 | KW_STOP, KW_BYE, 345 | KW_FILES, 346 | KW_MEM, 347 | KW_QMARK, KW_QUOTE, 348 | KW_AWRITE, KW_DWRITE, 349 | KW_DELAY, 350 | KW_END, 351 | KW_RSEED, 352 | KW_CHAIN, 353 | #ifdef ENABLE_TONES 354 | KW_TONEW, KW_TONE, KW_NOTONE, 355 | #endif 356 | #ifdef ARDUINO 357 | #ifdef ENABLE_EEPROM 358 | KW_ECHAIN, KW_ELIST, KW_ELOAD, KW_EFORMAT, KW_ESAVE, 359 | #endif 360 | #endif 361 | KW_DEFAULT /* always the final one*/ 362 | }; 363 | 364 | struct stack_for_frame { 365 | char frame_type; 366 | char for_var; 367 | short int terminal; 368 | short int step; 369 | unsigned char *current_line; 370 | unsigned char *txtpos; 371 | }; 372 | 373 | struct stack_gosub_frame { 374 | char frame_type; 375 | unsigned char *current_line; 376 | unsigned char *txtpos; 377 | }; 378 | 379 | const static unsigned char func_tab[] PROGMEM = { 380 | 'P','E','E','K'+0x80, 381 | 'A','B','S'+0x80, 382 | 'A','R','E','A','D'+0x80, 383 | 'D','R','E','A','D'+0x80, 384 | 'R','N','D'+0x80, 385 | 0 386 | }; 387 | #define FUNC_PEEK 0 388 | #define FUNC_ABS 1 389 | #define FUNC_AREAD 2 390 | #define FUNC_DREAD 3 391 | #define FUNC_RND 4 392 | #define FUNC_UNKNOWN 5 393 | 394 | const static unsigned char to_tab[] PROGMEM = { 395 | 'T','O'+0x80, 396 | 0 397 | }; 398 | 399 | const static unsigned char step_tab[] PROGMEM = { 400 | 'S','T','E','P'+0x80, 401 | 0 402 | }; 403 | 404 | const static unsigned char relop_tab[] PROGMEM = { 405 | '>','='+0x80, 406 | '<','>'+0x80, 407 | '>'+0x80, 408 | '='+0x80, 409 | '<','='+0x80, 410 | '<'+0x80, 411 | '!','='+0x80, 412 | 0 413 | }; 414 | 415 | #define RELOP_GE 0 416 | #define RELOP_NE 1 417 | #define RELOP_GT 2 418 | #define RELOP_EQ 3 419 | #define RELOP_LE 4 420 | #define RELOP_LT 5 421 | #define RELOP_NE_BANG 6 422 | #define RELOP_UNKNOWN 7 423 | 424 | const static unsigned char highlow_tab[] PROGMEM = { 425 | 'H','I','G','H'+0x80, 426 | 'H','I'+0x80, 427 | 'L','O','W'+0x80, 428 | 'L','O'+0x80, 429 | 0 430 | }; 431 | #define HIGHLOW_HIGH 1 432 | #define HIGHLOW_UNKNOWN 4 433 | 434 | #define STACK_SIZE (sizeof(struct stack_for_frame)*5) 435 | #define VAR_SIZE sizeof(short int) // Size of variables in bytes 436 | 437 | static unsigned char *stack_limit; 438 | static unsigned char *program_start; 439 | static unsigned char *program_end; 440 | static unsigned char *stack; // Software stack for things that should go on the CPU stack 441 | static unsigned char *variables_begin; 442 | static unsigned char *current_line; 443 | static unsigned char *sp; 444 | #define STACK_GOSUB_FLAG 'G' 445 | #define STACK_FOR_FLAG 'F' 446 | static unsigned char table_index; 447 | static LINENUM linenum; 448 | 449 | static const unsigned char okmsg[] PROGMEM = "OK"; 450 | static const unsigned char whatmsg[] PROGMEM = "What? "; 451 | static const unsigned char howmsg[] PROGMEM = "How?"; 452 | static const unsigned char sorrymsg[] PROGMEM = "Sorry!"; 453 | static const unsigned char initmsg[] PROGMEM = "TinyBasic Plus " kVersion; 454 | static const unsigned char memorymsg[] PROGMEM = " bytes free."; 455 | #ifdef ARDUINO 456 | #ifdef ENABLE_EEPROM 457 | static const unsigned char eeprommsg[] PROGMEM = " EEProm bytes total."; 458 | static const unsigned char eepromamsg[] PROGMEM = " EEProm bytes available."; 459 | #endif 460 | #endif 461 | static const unsigned char breakmsg[] PROGMEM = "break!"; 462 | static const unsigned char unimplimentedmsg[] PROGMEM = "Unimplemented"; 463 | static const unsigned char backspacemsg[] PROGMEM = "\b \b"; 464 | static const unsigned char indentmsg[] PROGMEM = " "; 465 | static const unsigned char sderrormsg[] PROGMEM = "SD card error."; 466 | static const unsigned char sdfilemsg[] PROGMEM = "SD file error."; 467 | static const unsigned char dirextmsg[] PROGMEM = "(dir)"; 468 | static const unsigned char slashmsg[] PROGMEM = "/"; 469 | static const unsigned char spacemsg[] PROGMEM = " "; 470 | 471 | static int inchar(void); 472 | static void outchar(unsigned char c); 473 | static void line_terminator(void); 474 | static short int expression(void); 475 | static unsigned char breakcheck(void); 476 | /***************************************************************************/ 477 | static void ignore_blanks(void) 478 | { 479 | while(*txtpos == SPACE || *txtpos == TAB) 480 | txtpos++; 481 | } 482 | 483 | 484 | /***************************************************************************/ 485 | static void scantable(const unsigned char *table) 486 | { 487 | int i = 0; 488 | table_index = 0; 489 | while(1) 490 | { 491 | // Run out of table entries? 492 | if(pgm_read_byte( table ) == 0) 493 | return; 494 | 495 | // Do we match this character? 496 | if(txtpos[i] == pgm_read_byte( table )) 497 | { 498 | i++; 499 | table++; 500 | } 501 | else 502 | { 503 | // do we match the last character of keywork (with 0x80 added)? If so, return 504 | if(txtpos[i]+0x80 == pgm_read_byte( table )) 505 | { 506 | txtpos += i+1; // Advance the pointer to following the keyword 507 | ignore_blanks(); 508 | return; 509 | } 510 | 511 | // Forward to the end of this keyword 512 | while((pgm_read_byte( table ) & 0x80) == 0) 513 | table++; 514 | 515 | // Now move on to the first character of the next word, and reset the position index 516 | table++; 517 | table_index++; 518 | ignore_blanks(); 519 | i = 0; 520 | } 521 | } 522 | } 523 | 524 | /***************************************************************************/ 525 | static void pushb(unsigned char b) 526 | { 527 | sp--; 528 | *sp = b; 529 | } 530 | 531 | /***************************************************************************/ 532 | static unsigned char popb() 533 | { 534 | unsigned char b; 535 | b = *sp; 536 | sp++; 537 | return b; 538 | } 539 | 540 | /***************************************************************************/ 541 | void printnum(int num) 542 | { 543 | int digits = 0; 544 | 545 | if(num < 0) 546 | { 547 | num = -num; 548 | outchar('-'); 549 | } 550 | do { 551 | pushb(num%10+'0'); 552 | num = num/10; 553 | digits++; 554 | } 555 | while (num > 0); 556 | 557 | while(digits > 0) 558 | { 559 | outchar(popb()); 560 | digits--; 561 | } 562 | } 563 | 564 | void printUnum(unsigned int num) 565 | { 566 | int digits = 0; 567 | 568 | do { 569 | pushb(num%10+'0'); 570 | num = num/10; 571 | digits++; 572 | } 573 | while (num > 0); 574 | 575 | while(digits > 0) 576 | { 577 | outchar(popb()); 578 | digits--; 579 | } 580 | } 581 | 582 | /***************************************************************************/ 583 | static unsigned short testnum(void) 584 | { 585 | unsigned short num = 0; 586 | ignore_blanks(); 587 | 588 | while(*txtpos>= '0' && *txtpos <= '9' ) 589 | { 590 | // Trap overflows 591 | if(num >= 0xFFFF/10) 592 | { 593 | num = 0xFFFF; 594 | break; 595 | } 596 | 597 | num = num *10 + *txtpos - '0'; 598 | txtpos++; 599 | } 600 | return num; 601 | } 602 | 603 | /***************************************************************************/ 604 | static unsigned char print_quoted_string(void) 605 | { 606 | int i=0; 607 | unsigned char delim = *txtpos; 608 | if(delim != '"' && delim != '\'') 609 | return 0; 610 | txtpos++; 611 | 612 | // Check we have a closing delimiter 613 | while(txtpos[i] != delim) 614 | { 615 | if(txtpos[i] == NL) 616 | return 0; 617 | i++; 618 | } 619 | 620 | // Print the characters 621 | while(*txtpos != delim) 622 | { 623 | outchar(*txtpos); 624 | txtpos++; 625 | } 626 | txtpos++; // Skip over the last delimiter 627 | 628 | return 1; 629 | } 630 | 631 | 632 | /***************************************************************************/ 633 | void printmsgNoNL(const unsigned char *msg) 634 | { 635 | while( pgm_read_byte( msg ) != 0 ) { 636 | outchar( pgm_read_byte( msg++ ) ); 637 | }; 638 | } 639 | 640 | /***************************************************************************/ 641 | void printmsg(const unsigned char *msg) 642 | { 643 | printmsgNoNL(msg); 644 | line_terminator(); 645 | } 646 | 647 | /***************************************************************************/ 648 | static void getln(char prompt) 649 | { 650 | outchar(prompt); 651 | txtpos = program_end+sizeof(LINENUM); 652 | 653 | while(1) 654 | { 655 | char c = inchar(); 656 | switch(c) 657 | { 658 | case NL: 659 | //break; 660 | case CR: 661 | line_terminator(); 662 | // Terminate all strings with a NL 663 | txtpos[0] = NL; 664 | return; 665 | case CTRLH: 666 | if(txtpos == program_end) 667 | break; 668 | txtpos--; 669 | 670 | printmsg(backspacemsg); 671 | break; 672 | default: 673 | // We need to leave at least one space to allow us to shuffle the line into order 674 | if(txtpos == variables_begin-2) 675 | outchar(BELL); 676 | else 677 | { 678 | txtpos[0] = c; 679 | txtpos++; 680 | outchar(c); 681 | } 682 | } 683 | } 684 | } 685 | 686 | /***************************************************************************/ 687 | static unsigned char *findline(void) 688 | { 689 | unsigned char *line = program_start; 690 | while(1) 691 | { 692 | if(line == program_end) 693 | return line; 694 | 695 | if(((LINENUM *)line)[0] >= linenum) 696 | return line; 697 | 698 | // Add the line length onto the current address, to get to the next line; 699 | line += line[sizeof(LINENUM)]; 700 | } 701 | } 702 | 703 | /***************************************************************************/ 704 | static void toUppercaseBuffer(void) 705 | { 706 | unsigned char *c = program_end+sizeof(LINENUM); 707 | unsigned char quote = 0; 708 | 709 | while(*c != NL) 710 | { 711 | // Are we in a quoted string? 712 | if(*c == quote) 713 | quote = 0; 714 | else if(*c == '"' || *c == '\'') 715 | quote = *c; 716 | else if(quote == 0 && *c >= 'a' && *c <= 'z') 717 | *c = *c + 'A' - 'a'; 718 | c++; 719 | } 720 | } 721 | 722 | /***************************************************************************/ 723 | void printline() 724 | { 725 | LINENUM line_num; 726 | 727 | line_num = *((LINENUM *)(list_line)); 728 | list_line += sizeof(LINENUM) + sizeof(char); 729 | 730 | // Output the line */ 731 | printnum(line_num); 732 | outchar(' '); 733 | while(*list_line != NL) 734 | { 735 | outchar(*list_line); 736 | list_line++; 737 | } 738 | list_line++; 739 | line_terminator(); 740 | } 741 | 742 | /***************************************************************************/ 743 | static short int expr4(void) 744 | { 745 | // fix provided by Jurg Wullschleger wullschleger@gmail.com 746 | // fixes whitespace and unary operations 747 | ignore_blanks(); 748 | 749 | if( *txtpos == '-' ) { 750 | txtpos++; 751 | return -expr4(); 752 | } 753 | // end fix 754 | 755 | if(*txtpos == '0') 756 | { 757 | txtpos++; 758 | return 0; 759 | } 760 | 761 | if(*txtpos >= '1' && *txtpos <= '9') 762 | { 763 | short int a = 0; 764 | do { 765 | a = a*10 + *txtpos - '0'; 766 | txtpos++; 767 | } 768 | while(*txtpos >= '0' && *txtpos <= '9'); 769 | return a; 770 | } 771 | 772 | // Is it a function or variable reference? 773 | if(txtpos[0] >= 'A' && txtpos[0] <= 'Z') 774 | { 775 | short int a; 776 | // Is it a variable reference (single alpha) 777 | if(txtpos[1] < 'A' || txtpos[1] > 'Z') 778 | { 779 | a = ((short int *)variables_begin)[*txtpos - 'A']; 780 | txtpos++; 781 | return a; 782 | } 783 | 784 | // Is it a function with a single parameter 785 | scantable(func_tab); 786 | if(table_index == FUNC_UNKNOWN) 787 | goto expr4_error; 788 | 789 | unsigned char f = table_index; 790 | 791 | if(*txtpos != '(') 792 | goto expr4_error; 793 | 794 | txtpos++; 795 | a = expression(); 796 | if(*txtpos != ')') 797 | goto expr4_error; 798 | txtpos++; 799 | switch(f) 800 | { 801 | case FUNC_PEEK: 802 | return program[a]; 803 | 804 | case FUNC_ABS: 805 | if(a < 0) 806 | return -a; 807 | return a; 808 | 809 | #ifdef ARDUINO 810 | case FUNC_AREAD: 811 | pinMode( a, INPUT ); 812 | return analogRead( a ); 813 | case FUNC_DREAD: 814 | pinMode( a, INPUT ); 815 | return digitalRead( a ); 816 | #endif 817 | 818 | case FUNC_RND: 819 | #ifdef ARDUINO 820 | return( random( a )); 821 | #else 822 | return( rand() % a ); 823 | #endif 824 | } 825 | } 826 | 827 | if(*txtpos == '(') 828 | { 829 | short int a; 830 | txtpos++; 831 | a = expression(); 832 | if(*txtpos != ')') 833 | goto expr4_error; 834 | 835 | txtpos++; 836 | return a; 837 | } 838 | 839 | expr4_error: 840 | expression_error = 1; 841 | return 0; 842 | 843 | } 844 | 845 | /***************************************************************************/ 846 | static short int expr3(void) 847 | { 848 | short int a,b; 849 | 850 | a = expr4(); 851 | 852 | ignore_blanks(); // fix for eg: 100 a = a + 1 853 | 854 | while(1) 855 | { 856 | if(*txtpos == '*') 857 | { 858 | txtpos++; 859 | b = expr4(); 860 | a *= b; 861 | } 862 | else if(*txtpos == '/') 863 | { 864 | txtpos++; 865 | b = expr4(); 866 | if(b != 0) 867 | a /= b; 868 | else 869 | expression_error = 1; 870 | } 871 | else 872 | return a; 873 | } 874 | } 875 | 876 | /***************************************************************************/ 877 | static short int expr2(void) 878 | { 879 | short int a,b; 880 | 881 | if(*txtpos == '-' || *txtpos == '+') 882 | a = 0; 883 | else 884 | a = expr3(); 885 | 886 | while(1) 887 | { 888 | if(*txtpos == '-') 889 | { 890 | txtpos++; 891 | b = expr3(); 892 | a -= b; 893 | } 894 | else if(*txtpos == '+') 895 | { 896 | txtpos++; 897 | b = expr3(); 898 | a += b; 899 | } 900 | else 901 | return a; 902 | } 903 | } 904 | /***************************************************************************/ 905 | static short int expression(void) 906 | { 907 | short int a,b; 908 | 909 | a = expr2(); 910 | 911 | // Check if we have an error 912 | if(expression_error) return a; 913 | 914 | scantable(relop_tab); 915 | if(table_index == RELOP_UNKNOWN) 916 | return a; 917 | 918 | switch(table_index) 919 | { 920 | case RELOP_GE: 921 | b = expr2(); 922 | if(a >= b) return 1; 923 | break; 924 | case RELOP_NE: 925 | case RELOP_NE_BANG: 926 | b = expr2(); 927 | if(a != b) return 1; 928 | break; 929 | case RELOP_GT: 930 | b = expr2(); 931 | if(a > b) return 1; 932 | break; 933 | case RELOP_EQ: 934 | b = expr2(); 935 | if(a == b) return 1; 936 | break; 937 | case RELOP_LE: 938 | b = expr2(); 939 | if(a <= b) return 1; 940 | break; 941 | case RELOP_LT: 942 | b = expr2(); 943 | if(a < b) return 1; 944 | break; 945 | } 946 | return 0; 947 | } 948 | 949 | /***************************************************************************/ 950 | void loop() 951 | { 952 | unsigned char *start; 953 | unsigned char *newEnd; 954 | unsigned char linelen; 955 | boolean isDigital; 956 | boolean alsoWait = false; 957 | int val; 958 | 959 | #ifdef ARDUINO 960 | #ifdef ENABLE_TONES 961 | // noTone( kPiezoPin ); 962 | M5.Speaker.mute(); 963 | #endif 964 | #endif 965 | 966 | program_start = program; 967 | program_end = program_start; 968 | sp = program+sizeof(program); // Needed for printnum 969 | stack_limit = program+sizeof(program)-STACK_SIZE; 970 | variables_begin = stack_limit - 27*VAR_SIZE; 971 | 972 | // memory free 973 | printnum(variables_begin-program_end); 974 | printmsg(memorymsg); 975 | #ifdef ARDUINO 976 | #ifdef ENABLE_EEPROM 977 | // eprom size 978 | printnum( E2END+1 ); 979 | printmsg( eeprommsg ); 980 | #endif /* ENABLE_EEPROM */ 981 | #endif /* ARDUINO */ 982 | 983 | warmstart: 984 | // this signifies that it is running in 'direct' mode. 985 | current_line = 0; 986 | sp = program+sizeof(program); 987 | printmsg(okmsg); 988 | 989 | prompt: 990 | if( triggerRun ){ 991 | triggerRun = false; 992 | current_line = program_start; 993 | goto execline; 994 | } 995 | 996 | getln( '>' ); 997 | toUppercaseBuffer(); 998 | 999 | txtpos = program_end+sizeof(unsigned short); 1000 | 1001 | // Find the end of the freshly entered line 1002 | while(*txtpos != NL) 1003 | txtpos++; 1004 | 1005 | // Move it to the end of program_memory 1006 | { 1007 | unsigned char *dest; 1008 | dest = variables_begin-1; 1009 | while(1) 1010 | { 1011 | *dest = *txtpos; 1012 | if(txtpos == program_end+sizeof(unsigned short)) 1013 | break; 1014 | dest--; 1015 | txtpos--; 1016 | } 1017 | txtpos = dest; 1018 | } 1019 | 1020 | // Now see if we have a line number 1021 | linenum = testnum(); 1022 | ignore_blanks(); 1023 | if(linenum == 0) 1024 | goto direct; 1025 | 1026 | if(linenum == 0xFFFF) 1027 | goto qhow; 1028 | 1029 | // Find the length of what is left, including the (yet-to-be-populated) line header 1030 | linelen = 0; 1031 | while(txtpos[linelen] != NL) 1032 | linelen++; 1033 | linelen++; // Include the NL in the line length 1034 | linelen += sizeof(unsigned short)+sizeof(char); // Add space for the line number and line length 1035 | 1036 | // Now we have the number, add the line header. 1037 | txtpos -= 3; 1038 | *((unsigned short *)txtpos) = linenum; 1039 | txtpos[sizeof(LINENUM)] = linelen; 1040 | 1041 | 1042 | // Merge it into the rest of the program 1043 | start = findline(); 1044 | 1045 | // If a line with that number exists, then remove it 1046 | if(start != program_end && *((LINENUM *)start) == linenum) 1047 | { 1048 | unsigned char *dest, *from; 1049 | unsigned tomove; 1050 | 1051 | from = start + start[sizeof(LINENUM)]; 1052 | dest = start; 1053 | 1054 | tomove = program_end - from; 1055 | while( tomove > 0) 1056 | { 1057 | *dest = *from; 1058 | from++; 1059 | dest++; 1060 | tomove--; 1061 | } 1062 | program_end = dest; 1063 | } 1064 | 1065 | if(txtpos[sizeof(LINENUM)+sizeof(char)] == NL) // If the line has no txt, it was just a delete 1066 | goto prompt; 1067 | 1068 | 1069 | 1070 | // Make room for the new line, either all in one hit or lots of little shuffles 1071 | while(linelen > 0) 1072 | { 1073 | unsigned int tomove; 1074 | unsigned char *from,*dest; 1075 | unsigned int space_to_make; 1076 | 1077 | space_to_make = txtpos - program_end; 1078 | 1079 | if(space_to_make > linelen) 1080 | space_to_make = linelen; 1081 | newEnd = program_end+space_to_make; 1082 | tomove = program_end - start; 1083 | 1084 | 1085 | // Source and destination - as these areas may overlap we need to move bottom up 1086 | from = program_end; 1087 | dest = newEnd; 1088 | while(tomove > 0) 1089 | { 1090 | from--; 1091 | dest--; 1092 | *dest = *from; 1093 | tomove--; 1094 | } 1095 | 1096 | // Copy over the bytes into the new space 1097 | for(tomove = 0; tomove < space_to_make; tomove++) 1098 | { 1099 | *start = *txtpos; 1100 | txtpos++; 1101 | start++; 1102 | linelen--; 1103 | } 1104 | program_end = newEnd; 1105 | } 1106 | goto prompt; 1107 | 1108 | unimplemented: 1109 | printmsg(unimplimentedmsg); 1110 | goto prompt; 1111 | 1112 | qhow: 1113 | printmsg(howmsg); 1114 | goto prompt; 1115 | 1116 | qwhat: 1117 | printmsgNoNL(whatmsg); 1118 | if(current_line != NULL) 1119 | { 1120 | unsigned char tmp = *txtpos; 1121 | if(*txtpos != NL) 1122 | *txtpos = '^'; 1123 | list_line = current_line; 1124 | printline(); 1125 | *txtpos = tmp; 1126 | } 1127 | line_terminator(); 1128 | goto prompt; 1129 | 1130 | qsorry: 1131 | printmsg(sorrymsg); 1132 | goto warmstart; 1133 | 1134 | run_next_statement: 1135 | while(*txtpos == ':') 1136 | txtpos++; 1137 | ignore_blanks(); 1138 | if(*txtpos == NL) 1139 | goto execnextline; 1140 | goto interperateAtTxtpos; 1141 | 1142 | direct: 1143 | txtpos = program_end+sizeof(LINENUM); 1144 | if(*txtpos == NL) 1145 | goto prompt; 1146 | 1147 | interperateAtTxtpos: 1148 | if(breakcheck()) 1149 | { 1150 | printmsg(breakmsg); 1151 | goto warmstart; 1152 | } 1153 | 1154 | scantable(keywords); 1155 | 1156 | switch(table_index) 1157 | { 1158 | case KW_DELAY: 1159 | { 1160 | #ifdef ARDUINO 1161 | expression_error = 0; 1162 | val = expression(); 1163 | delay( val ); 1164 | goto execnextline; 1165 | #else 1166 | goto unimplemented; 1167 | #endif 1168 | } 1169 | 1170 | case KW_FILES: 1171 | goto files; 1172 | case KW_LIST: 1173 | goto list; 1174 | case KW_CHAIN: 1175 | goto chain; 1176 | case KW_LOAD: 1177 | goto load; 1178 | case KW_MEM: 1179 | goto mem; 1180 | case KW_NEW: 1181 | if(txtpos[0] != NL) 1182 | goto qwhat; 1183 | program_end = program_start; 1184 | goto prompt; 1185 | case KW_RUN: 1186 | current_line = program_start; 1187 | goto execline; 1188 | case KW_SAVE: 1189 | goto save; 1190 | case KW_NEXT: 1191 | goto next; 1192 | case KW_LET: 1193 | goto assignment; 1194 | case KW_IF: 1195 | short int val; 1196 | expression_error = 0; 1197 | val = expression(); 1198 | if(expression_error || *txtpos == NL) 1199 | goto qhow; 1200 | if(val != 0) 1201 | goto interperateAtTxtpos; 1202 | goto execnextline; 1203 | 1204 | case KW_GOTO: 1205 | expression_error = 0; 1206 | linenum = expression(); 1207 | if(expression_error || *txtpos != NL) 1208 | goto qhow; 1209 | current_line = findline(); 1210 | goto execline; 1211 | 1212 | case KW_GOSUB: 1213 | goto gosub; 1214 | case KW_RETURN: 1215 | goto gosub_return; 1216 | case KW_REM: 1217 | case KW_QUOTE: 1218 | goto execnextline; // Ignore line completely 1219 | case KW_FOR: 1220 | goto forloop; 1221 | case KW_INPUT: 1222 | goto input; 1223 | case KW_PRINT: 1224 | case KW_QMARK: 1225 | goto print; 1226 | case KW_POKE: 1227 | goto poke; 1228 | case KW_END: 1229 | case KW_STOP: 1230 | // This is the easy way to end - set the current line to the end of program attempt to run it 1231 | if(txtpos[0] != NL) 1232 | goto qwhat; 1233 | current_line = program_end; 1234 | goto execline; 1235 | case KW_BYE: 1236 | // Leave the basic interperater 1237 | return; 1238 | 1239 | case KW_AWRITE: // AWRITE , HIGH|LOW 1240 | isDigital = false; 1241 | goto awrite; 1242 | case KW_DWRITE: // DWRITE , HIGH|LOW 1243 | isDigital = true; 1244 | goto dwrite; 1245 | 1246 | case KW_RSEED: 1247 | goto rseed; 1248 | 1249 | #ifdef ENABLE_TONES 1250 | case KW_TONEW: 1251 | alsoWait = true; 1252 | case KW_TONE: 1253 | goto tonegen; 1254 | case KW_NOTONE: 1255 | goto tonestop; 1256 | #endif 1257 | 1258 | #ifdef ARDUINO 1259 | #ifdef ENABLE_EEPROM 1260 | case KW_EFORMAT: 1261 | goto eformat; 1262 | case KW_ESAVE: 1263 | goto esave; 1264 | case KW_ELOAD: 1265 | goto eload; 1266 | case KW_ELIST: 1267 | goto elist; 1268 | case KW_ECHAIN: 1269 | goto echain; 1270 | #endif 1271 | #endif 1272 | 1273 | case KW_DEFAULT: 1274 | goto assignment; 1275 | default: 1276 | break; 1277 | } 1278 | 1279 | execnextline: 1280 | if(current_line == NULL) // Processing direct commands? 1281 | goto prompt; 1282 | current_line += current_line[sizeof(LINENUM)]; 1283 | 1284 | execline: 1285 | if(current_line == program_end) // Out of lines to run 1286 | goto warmstart; 1287 | txtpos = current_line+sizeof(LINENUM)+sizeof(char); 1288 | goto interperateAtTxtpos; 1289 | 1290 | #ifdef ARDUINO 1291 | #ifdef ENABLE_EEPROM 1292 | elist: 1293 | { 1294 | int i; 1295 | for( i = 0 ; i < (E2END +1) ; i++ ) 1296 | { 1297 | val = EEPROM.read( i ); 1298 | 1299 | if( val == '\0' ) { 1300 | goto execnextline; 1301 | } 1302 | 1303 | if( ((val < ' ') || (val > '~')) && (val != NL) && (val != CR)) { 1304 | outchar( '?' ); 1305 | } 1306 | else { 1307 | outchar( val ); 1308 | } 1309 | } 1310 | } 1311 | goto execnextline; 1312 | 1313 | eformat: 1314 | { 1315 | for( int i = 0 ; i < E2END ; i++ ) 1316 | { 1317 | if( (i & 0x03f) == 0x20 ) outchar( '.' ); 1318 | EEPROM.write( i, 0 ); 1319 | } 1320 | outchar( LF ); 1321 | } 1322 | goto execnextline; 1323 | 1324 | esave: 1325 | { 1326 | outStream = kStreamEEProm; 1327 | eepos = 0; 1328 | 1329 | // copied from "List" 1330 | list_line = findline(); 1331 | while(list_line != program_end) { 1332 | printline(); 1333 | } 1334 | outchar('\0'); 1335 | 1336 | // go back to standard output, close the file 1337 | outStream = kStreamSerial; 1338 | 1339 | goto warmstart; 1340 | } 1341 | 1342 | 1343 | echain: 1344 | runAfterLoad = true; 1345 | 1346 | eload: 1347 | // clear the program 1348 | program_end = program_start; 1349 | 1350 | // load from a file into memory 1351 | eepos = 0; 1352 | inStream = kStreamEEProm; 1353 | inhibitOutput = true; 1354 | goto warmstart; 1355 | #endif /* ENABLE_EEPROM */ 1356 | #endif 1357 | 1358 | input: 1359 | { 1360 | unsigned char var; 1361 | int value; 1362 | ignore_blanks(); 1363 | if(*txtpos < 'A' || *txtpos > 'Z') 1364 | goto qwhat; 1365 | var = *txtpos; 1366 | txtpos++; 1367 | ignore_blanks(); 1368 | if(*txtpos != NL && *txtpos != ':') 1369 | goto qwhat; 1370 | inputagain: 1371 | tmptxtpos = txtpos; 1372 | getln( '?' ); 1373 | toUppercaseBuffer(); 1374 | txtpos = program_end+sizeof(unsigned short); 1375 | ignore_blanks(); 1376 | expression_error = 0; 1377 | value = expression(); 1378 | if(expression_error) 1379 | goto inputagain; 1380 | ((short int *)variables_begin)[var-'A'] = value; 1381 | txtpos = tmptxtpos; 1382 | 1383 | goto run_next_statement; 1384 | } 1385 | 1386 | forloop: 1387 | { 1388 | unsigned char var; 1389 | short int initial, step, terminal; 1390 | ignore_blanks(); 1391 | if(*txtpos < 'A' || *txtpos > 'Z') 1392 | goto qwhat; 1393 | var = *txtpos; 1394 | txtpos++; 1395 | ignore_blanks(); 1396 | if(*txtpos != '=') 1397 | goto qwhat; 1398 | txtpos++; 1399 | ignore_blanks(); 1400 | 1401 | expression_error = 0; 1402 | initial = expression(); 1403 | if(expression_error) 1404 | goto qwhat; 1405 | 1406 | scantable(to_tab); 1407 | if(table_index != 0) 1408 | goto qwhat; 1409 | 1410 | terminal = expression(); 1411 | if(expression_error) 1412 | goto qwhat; 1413 | 1414 | scantable(step_tab); 1415 | if(table_index == 0) 1416 | { 1417 | step = expression(); 1418 | if(expression_error) 1419 | goto qwhat; 1420 | } 1421 | else 1422 | step = 1; 1423 | ignore_blanks(); 1424 | if(*txtpos != NL && *txtpos != ':') 1425 | goto qwhat; 1426 | 1427 | 1428 | if(!expression_error && *txtpos == NL) 1429 | { 1430 | struct stack_for_frame *f; 1431 | if(sp + sizeof(struct stack_for_frame) < stack_limit) 1432 | goto qsorry; 1433 | 1434 | sp -= sizeof(struct stack_for_frame); 1435 | f = (struct stack_for_frame *)sp; 1436 | ((short int *)variables_begin)[var-'A'] = initial; 1437 | f->frame_type = STACK_FOR_FLAG; 1438 | f->for_var = var; 1439 | f->terminal = terminal; 1440 | f->step = step; 1441 | f->txtpos = txtpos; 1442 | f->current_line = current_line; 1443 | goto run_next_statement; 1444 | } 1445 | } 1446 | goto qhow; 1447 | 1448 | gosub: 1449 | expression_error = 0; 1450 | linenum = expression(); 1451 | if(!expression_error && *txtpos == NL) 1452 | { 1453 | struct stack_gosub_frame *f; 1454 | if(sp + sizeof(struct stack_gosub_frame) < stack_limit) 1455 | goto qsorry; 1456 | 1457 | sp -= sizeof(struct stack_gosub_frame); 1458 | f = (struct stack_gosub_frame *)sp; 1459 | f->frame_type = STACK_GOSUB_FLAG; 1460 | f->txtpos = txtpos; 1461 | f->current_line = current_line; 1462 | current_line = findline(); 1463 | goto execline; 1464 | } 1465 | goto qhow; 1466 | 1467 | next: 1468 | // Fnd the variable name 1469 | ignore_blanks(); 1470 | if(*txtpos < 'A' || *txtpos > 'Z') 1471 | goto qhow; 1472 | txtpos++; 1473 | ignore_blanks(); 1474 | if(*txtpos != ':' && *txtpos != NL) 1475 | goto qwhat; 1476 | 1477 | gosub_return: 1478 | // Now walk up the stack frames and find the frame we want, if present 1479 | tempsp = sp; 1480 | while(tempsp < program+sizeof(program)-1) 1481 | { 1482 | switch(tempsp[0]) 1483 | { 1484 | case STACK_GOSUB_FLAG: 1485 | if(table_index == KW_RETURN) 1486 | { 1487 | struct stack_gosub_frame *f = (struct stack_gosub_frame *)tempsp; 1488 | current_line = f->current_line; 1489 | txtpos = f->txtpos; 1490 | sp += sizeof(struct stack_gosub_frame); 1491 | goto run_next_statement; 1492 | } 1493 | // This is not the loop you are looking for... so Walk back up the stack 1494 | tempsp += sizeof(struct stack_gosub_frame); 1495 | break; 1496 | case STACK_FOR_FLAG: 1497 | // Flag, Var, Final, Step 1498 | if(table_index == KW_NEXT) 1499 | { 1500 | struct stack_for_frame *f = (struct stack_for_frame *)tempsp; 1501 | // Is the the variable we are looking for? 1502 | if(txtpos[-1] == f->for_var) 1503 | { 1504 | short int *varaddr = ((short int *)variables_begin) + txtpos[-1] - 'A'; 1505 | *varaddr = *varaddr + f->step; 1506 | // Use a different test depending on the sign of the step increment 1507 | if((f->step > 0 && *varaddr <= f->terminal) || (f->step < 0 && *varaddr >= f->terminal)) 1508 | { 1509 | // We have to loop so don't pop the stack 1510 | txtpos = f->txtpos; 1511 | current_line = f->current_line; 1512 | goto run_next_statement; 1513 | } 1514 | // We've run to the end of the loop. drop out of the loop, popping the stack 1515 | sp = tempsp + sizeof(struct stack_for_frame); 1516 | goto run_next_statement; 1517 | } 1518 | } 1519 | // This is not the loop you are looking for... so Walk back up the stack 1520 | tempsp += sizeof(struct stack_for_frame); 1521 | break; 1522 | default: 1523 | //printf("Stack is stuffed!\n"); 1524 | goto warmstart; 1525 | } 1526 | } 1527 | // Didn't find the variable we've been looking for 1528 | goto qhow; 1529 | 1530 | assignment: 1531 | { 1532 | short int value; 1533 | short int *var; 1534 | 1535 | if(*txtpos < 'A' || *txtpos > 'Z') 1536 | goto qhow; 1537 | var = (short int *)variables_begin + *txtpos - 'A'; 1538 | txtpos++; 1539 | 1540 | ignore_blanks(); 1541 | 1542 | if (*txtpos != '=') 1543 | goto qwhat; 1544 | txtpos++; 1545 | ignore_blanks(); 1546 | expression_error = 0; 1547 | value = expression(); 1548 | if(expression_error) 1549 | goto qwhat; 1550 | // Check that we are at the end of the statement 1551 | if(*txtpos != NL && *txtpos != ':') 1552 | goto qwhat; 1553 | *var = value; 1554 | } 1555 | goto run_next_statement; 1556 | poke: 1557 | { 1558 | short int value; 1559 | unsigned char *address; 1560 | 1561 | // Work out where to put it 1562 | expression_error = 0; 1563 | value = expression(); 1564 | if(expression_error) 1565 | goto qwhat; 1566 | address = (unsigned char *)value; 1567 | 1568 | // check for a comma 1569 | ignore_blanks(); 1570 | if (*txtpos != ',') 1571 | goto qwhat; 1572 | txtpos++; 1573 | ignore_blanks(); 1574 | 1575 | // Now get the value to assign 1576 | expression_error = 0; 1577 | value = expression(); 1578 | if(expression_error) 1579 | goto qwhat; 1580 | //printf("Poke %p value %i\n",address, (unsigned char)value); 1581 | // Check that we are at the end of the statement 1582 | if(*txtpos != NL && *txtpos != ':') 1583 | goto qwhat; 1584 | } 1585 | goto run_next_statement; 1586 | 1587 | list: 1588 | linenum = testnum(); // Retuns 0 if no line found. 1589 | 1590 | // Should be EOL 1591 | if(txtpos[0] != NL) 1592 | goto qwhat; 1593 | 1594 | // Find the line 1595 | list_line = findline(); 1596 | while(list_line != program_end) 1597 | printline(); 1598 | goto warmstart; 1599 | 1600 | print: 1601 | // If we have an empty list then just put out a NL 1602 | if(*txtpos == ':' ) 1603 | { 1604 | line_terminator(); 1605 | txtpos++; 1606 | goto run_next_statement; 1607 | } 1608 | if(*txtpos == NL) 1609 | { 1610 | goto execnextline; 1611 | } 1612 | 1613 | while(1) 1614 | { 1615 | ignore_blanks(); 1616 | if(print_quoted_string()) 1617 | { 1618 | ; 1619 | } 1620 | else if(*txtpos == '"' || *txtpos == '\'') 1621 | goto qwhat; 1622 | else 1623 | { 1624 | short int e; 1625 | expression_error = 0; 1626 | e = expression(); 1627 | if(expression_error) 1628 | goto qwhat; 1629 | printnum(e); 1630 | } 1631 | 1632 | // At this point we have three options, a comma or a new line 1633 | if(*txtpos == ',') 1634 | txtpos++; // Skip the comma and move onto the next 1635 | else if(txtpos[0] == ';' && (txtpos[1] == NL || txtpos[1] == ':')) 1636 | { 1637 | txtpos++; // This has to be the end of the print - no newline 1638 | break; 1639 | } 1640 | else if(*txtpos == NL || *txtpos == ':') 1641 | { 1642 | line_terminator(); // The end of the print statement 1643 | break; 1644 | } 1645 | else 1646 | goto qwhat; 1647 | } 1648 | goto run_next_statement; 1649 | 1650 | mem: 1651 | // memory free 1652 | printnum(variables_begin-program_end); 1653 | printmsg(memorymsg); 1654 | #ifdef ARDUINO 1655 | #ifdef ENABLE_EEPROM 1656 | { 1657 | // eprom size 1658 | printnum( E2END+1 ); 1659 | printmsg( eeprommsg ); 1660 | 1661 | // figure out the memory usage; 1662 | val = ' '; 1663 | int i; 1664 | for( i=0 ; (i<(E2END+1)) && (val != '\0') ; i++ ) { 1665 | val = EEPROM.read( i ); 1666 | } 1667 | printnum( (E2END +1) - (i-1) ); 1668 | 1669 | printmsg( eepromamsg ); 1670 | } 1671 | #endif /* ENABLE_EEPROM */ 1672 | #endif /* ARDUINO */ 1673 | goto run_next_statement; 1674 | 1675 | 1676 | /*************************************************/ 1677 | 1678 | #ifdef ARDUINO 1679 | awrite: // AWRITE ,val 1680 | dwrite: 1681 | { 1682 | short int pinNo; 1683 | short int value; 1684 | unsigned char *txtposBak; 1685 | 1686 | // Get the pin number 1687 | expression_error = 0; 1688 | pinNo = expression(); 1689 | if(expression_error) 1690 | goto qwhat; 1691 | 1692 | // check for a comma 1693 | ignore_blanks(); 1694 | if (*txtpos != ',') 1695 | goto qwhat; 1696 | txtpos++; 1697 | ignore_blanks(); 1698 | 1699 | 1700 | txtposBak = txtpos; 1701 | scantable(highlow_tab); 1702 | if(table_index != HIGHLOW_UNKNOWN) 1703 | { 1704 | if( table_index <= HIGHLOW_HIGH ) { 1705 | value = 1; 1706 | } 1707 | else { 1708 | value = 0; 1709 | } 1710 | } 1711 | else { 1712 | 1713 | // and the value (numerical) 1714 | expression_error = 0; 1715 | value = expression(); 1716 | if(expression_error) 1717 | goto qwhat; 1718 | } 1719 | pinMode( pinNo, OUTPUT ); 1720 | if( isDigital ) { 1721 | digitalWrite( pinNo, value ); 1722 | } 1723 | else { 1724 | // analogWrite( pinNo, value ); 1725 | delayMicroseconds(1); 1726 | } 1727 | } 1728 | goto run_next_statement; 1729 | #else 1730 | pinmode: // PINMODE , I/O 1731 | awrite: // AWRITE ,val 1732 | dwrite: 1733 | goto unimplemented; 1734 | #endif 1735 | 1736 | /*************************************************/ 1737 | files: 1738 | // display a listing of files on the device. 1739 | // version 1: no support for subdirectories 1740 | 1741 | #ifdef ENABLE_FILEIO 1742 | cmd_Files(); 1743 | goto warmstart; 1744 | #else 1745 | goto unimplemented; 1746 | #endif // ENABLE_FILEIO 1747 | 1748 | 1749 | chain: 1750 | runAfterLoad = true; 1751 | 1752 | load: 1753 | // clear the program 1754 | program_end = program_start; 1755 | 1756 | // load from a file into memory 1757 | #ifdef ENABLE_FILEIO 1758 | { 1759 | unsigned char *filename; 1760 | 1761 | // Work out the filename 1762 | expression_error = 0; 1763 | filename = filenameWord(); 1764 | if(expression_error) 1765 | goto qwhat; 1766 | 1767 | #ifdef ARDUINO 1768 | // Arduino specific 1769 | if( !SD.exists( (char *)filename )) 1770 | { 1771 | printmsg( sdfilemsg ); 1772 | } 1773 | else { 1774 | 1775 | fp = SD.open( (const char *)filename ); 1776 | inStream = kStreamFile; 1777 | inhibitOutput = true; 1778 | } 1779 | #else // ARDUINO 1780 | // Desktop specific 1781 | #endif // ARDUINO 1782 | // this will kickstart a series of events to read in from the file. 1783 | 1784 | } 1785 | goto warmstart; 1786 | #else // ENABLE_FILEIO 1787 | goto unimplemented; 1788 | #endif // ENABLE_FILEIO 1789 | 1790 | 1791 | 1792 | save: 1793 | // save from memory out to a file 1794 | #ifdef ENABLE_FILEIO 1795 | { 1796 | unsigned char *filename; 1797 | 1798 | // Work out the filename 1799 | expression_error = 0; 1800 | filename = filenameWord(); 1801 | if(expression_error) 1802 | goto qwhat; 1803 | 1804 | #ifdef ARDUINO 1805 | // remove the old file if it exists 1806 | if( SD.exists( (char *)filename )) { 1807 | SD.remove( (char *)filename ); 1808 | } 1809 | 1810 | // open the file, switch over to file output 1811 | fp = SD.open( (const char *)filename, FILE_WRITE ); 1812 | outStream = kStreamFile; 1813 | 1814 | // copied from "List" 1815 | list_line = findline(); 1816 | while(list_line != program_end) 1817 | printline(); 1818 | 1819 | // go back to standard output, close the file 1820 | outStream = kStreamSerial; 1821 | 1822 | fp.close(); 1823 | #else // ARDUINO 1824 | // desktop 1825 | #endif // ARDUINO 1826 | goto warmstart; 1827 | } 1828 | #else // ENABLE_FILEIO 1829 | goto unimplemented; 1830 | #endif // ENABLE_FILEIO 1831 | 1832 | rseed: 1833 | { 1834 | short int value; 1835 | 1836 | //Get the pin number 1837 | expression_error = 0; 1838 | value = expression(); 1839 | if(expression_error) 1840 | goto qwhat; 1841 | 1842 | #ifdef ARDUINO 1843 | randomSeed( value ); 1844 | #else // ARDUINO 1845 | srand( value ); 1846 | #endif // ARDUINO 1847 | goto run_next_statement; 1848 | } 1849 | 1850 | #ifdef ENABLE_TONES 1851 | tonestop: 1852 | // noTone( kPiezoPin ); 1853 | M5.Speaker.mute(); 1854 | goto run_next_statement; 1855 | 1856 | tonegen: 1857 | { 1858 | // TONE freq, duration 1859 | // if either are 0, tones turned off 1860 | short int freq; 1861 | short int duration; 1862 | 1863 | //Get the frequency 1864 | expression_error = 0; 1865 | freq = expression(); 1866 | if(expression_error) 1867 | goto qwhat; 1868 | 1869 | ignore_blanks(); 1870 | if (*txtpos != ',') 1871 | goto qwhat; 1872 | txtpos++; 1873 | ignore_blanks(); 1874 | 1875 | 1876 | //Get the duration 1877 | expression_error = 0; 1878 | duration = expression(); 1879 | if(expression_error) 1880 | goto qwhat; 1881 | 1882 | if( freq == 0 || duration == 0 ) 1883 | goto tonestop; 1884 | 1885 | // tone( kPiezoPin, freq, duration ); 1886 | M5.Speaker.tone( freq, duration ); 1887 | if( alsoWait ) { 1888 | delay( duration ); 1889 | alsoWait = false; 1890 | } 1891 | goto run_next_statement; 1892 | } 1893 | #endif /* ENABLE_TONES */ 1894 | } 1895 | 1896 | // returns 1 if the character is valid in a filename 1897 | static int isValidFnChar( char c ) 1898 | { 1899 | if( c >= '0' && c <= '9' ) return 1; // number 1900 | if( c >= 'A' && c <= 'Z' ) return 1; // LETTER 1901 | if( c >= 'a' && c <= 'z' ) return 1; // letter (for completeness) 1902 | if( c == '_' ) return 1; 1903 | if( c == '+' ) return 1; 1904 | if( c == '.' ) return 1; 1905 | if( c == '~' ) return 1; // Window~1.txt 1906 | 1907 | return 0; 1908 | } 1909 | 1910 | unsigned char * filenameWord(void) 1911 | { 1912 | // SDL - I wasn't sure if this functionality existed above, so I figured i'd put it here 1913 | unsigned char * ret = txtpos; 1914 | expression_error = 0; 1915 | 1916 | // make sure there are no quotes or spaces, search for valid characters 1917 | //while(*txtpos == SPACE || *txtpos == TAB || *txtpos == SQUOTE || *txtpos == DQUOTE ) txtpos++; 1918 | while( !isValidFnChar( *txtpos )) txtpos++; 1919 | ret = txtpos; 1920 | 1921 | if( *ret == '\0' ) { 1922 | expression_error = 1; 1923 | return ret; 1924 | } 1925 | 1926 | // now, find the next nonfnchar 1927 | txtpos++; 1928 | while( isValidFnChar( *txtpos )) txtpos++; 1929 | if( txtpos != ret ) *txtpos = '\0'; 1930 | 1931 | // set the error code if we've got no string 1932 | if( *ret == '\0' ) { 1933 | expression_error = 1; 1934 | } 1935 | 1936 | return ret; 1937 | } 1938 | 1939 | /***************************************************************************/ 1940 | static void line_terminator(void) 1941 | { 1942 | outchar(NL); 1943 | outchar(CR); 1944 | } 1945 | 1946 | /***********************************************************/ 1947 | void setup() 1948 | { 1949 | #ifdef ARDUINO 1950 | // Serial.begin(kConsoleBaud); // opens serial port 1951 | M5.begin(); 1952 | Wire.begin(); // 加入 i2c 总线,作为主机 1953 | termInit(); 1954 | 1955 | // while( !Serial ); // for Leonardo 1956 | 1957 | Serial.println( sentinel ); 1958 | 1959 | // const unsigned char initlogo[] = "\n" 1960 | // " ___ __ __\r\n" 1961 | // " / _ )/ /_ _____ / /__\r\n" 1962 | // " / _ / / // / _ \\/ '_/\r\n" 1963 | // " /____/_/\\_, /_//_/_/\\_\\\r\n" 1964 | // " /___/ v" " on " "\r\n" 1965 | // ; 1966 | 1967 | // const unsigned char initLogo[] = 1968 | // " __ __ ____ \r\n" 1969 | // " | \\/ | ___| \r\n" 1970 | // " | |\\/| |___ \\ \r\n" 1971 | // " | | | |___) |\r\n" 1972 | // " |_| |_|____/ \r\n" 1973 | // ; 1974 | 1975 | const unsigned char initLogo[] = 1976 | "__ __ ____ ____ _ _ \r\n" 1977 | "| \\/ | ___/ ___|| |_ __ _ ___| | __\r\n" 1978 | "| |\\/| |___ \\___ \\| __/ _` |/ __| |/ / \r\n" 1979 | "| | | |___) |__) | || (_| | (__| < \r\n" 1980 | "|_| |_|____/____/ \\__\\__,_|\\___|_|\\_\\ \r\n" 1981 | ; 1982 | 1983 | const unsigned char initLogox[] = 1984 | "## ## ######## ###### ######## ### ###### ## ## \r\n" 1985 | "### ### ## ## ## ## ## ## ## ## ## ## \r\n" 1986 | "#### #### ## ## ## ## ## ## ## ## \r\n" 1987 | "## ### ## ####### ###### ## ## ## ## ##### \r\n" 1988 | "## ## ## ## ## ######### ## ## ## \r\n" 1989 | "## ## ## ## ## ## ## ## ## ## ## ## ## \r\n" 1990 | "## ## ###### ###### ## ## ## ###### ## ##"; 1991 | 1992 | const unsigned char initLogok[] = 1993 | "## ## ######## \r\n" 1994 | "### ### ## \r\n" 1995 | "#### #### ## \r\n" 1996 | "## ### ## ####### \r\n" 1997 | "## ## ## \r\n" 1998 | "## ## ## ## \r\n" 1999 | "## ## ###### \r\n"; 2000 | 2001 | const char initLogoz[] = 2002 | "'##::::'##:'########:\r\n" 2003 | "###::'###: ##.....::\r\n" 2004 | "####'####: ##:::::::\r\n" 2005 | "## ### ##: #######::\r\n" 2006 | "##. #: ##:...... ##:\r\n" 2007 | "##:.:: ##:'##::: ##:\r\n" 2008 | "##:::: ##:. ######::\r\n" 2009 | "..:::::..:::......:::\r\n"; 2010 | 2011 | const unsigned char initLogop[] = 2012 | " DD \r\n" 2013 | " DG DD \r\n" 2014 | " LD; # DD \r\n" 2015 | " DD # j## DD \r\n" 2016 | " DD # # # G\r\n" 2017 | " DD ## G, DDD\r\n" 2018 | " DD # DD D\r\n" 2019 | " D DD DD D\r\n" 2020 | " D DDGD. D\r\n" 2021 | " D D\r\n" 2022 | " Dj DD\r\n" 2023 | " iDD DG \r\n" 2024 | " DD DD \r\n" 2025 | " DD DG \r\n" 2026 | " GDDD \r\n"; 2027 | 2028 | // M5.Lcd.print(initLogoz); 2029 | printString((unsigned char*)initLogok); 2030 | printString((unsigned char*)"\r\nhttp://www.m5stack.com\r\n"); 2031 | printString((unsigned char*)"Basic on the M5Stack library 0.1.2\r\n"); 2032 | printmsg(initmsg); 2033 | // printmsg(initlogo); 2034 | 2035 | #ifdef ENABLE_FILEIO 2036 | initSD(); 2037 | 2038 | #ifdef ENABLE_AUTORUN 2039 | if( SD.exists( kAutorunFilename )) { 2040 | program_end = program_start; 2041 | fp = SD.open( kAutorunFilename ); 2042 | inStream = kStreamFile; 2043 | inhibitOutput = true; 2044 | runAfterLoad = true; 2045 | } 2046 | #endif /* ENABLE_AUTORUN */ 2047 | 2048 | #endif /* ENABLE_FILEIO */ 2049 | 2050 | #ifdef ENABLE_EEPROM 2051 | #ifdef ENABLE_EAUTORUN 2052 | // read the first byte of the eeprom. if it's a number, assume it's a program we can load 2053 | int val = EEPROM.read(0); 2054 | if( val >= '0' && val <= '9' ) { 2055 | program_end = program_start; 2056 | inStream = kStreamEEProm; 2057 | eepos = 0; 2058 | inhibitOutput = true; 2059 | runAfterLoad = true; 2060 | } 2061 | #endif /* ENABLE_EAUTORUN */ 2062 | #endif /* ENABLE_EEPROM */ 2063 | 2064 | #endif /* ARDUINO */ 2065 | } 2066 | 2067 | 2068 | /***********************************************************/ 2069 | static unsigned char breakcheck(void) 2070 | { 2071 | // #ifdef ARDUINO 2072 | // Wire.requestFrom(0x88, 1); 2073 | // if(Wire.available()) 2074 | // return Wire.read() == CTRLC; 2075 | // return 0; 2076 | 2077 | // if(Serial.available()) 2078 | // return Serial.read() == CTRLC; 2079 | // return 0; 2080 | 2081 | // #else 2082 | #ifdef __CONIO__ 2083 | if(kbhit()) 2084 | return getch() == CTRLC; 2085 | else 2086 | #endif 2087 | return 0; 2088 | // #endif 2089 | } 2090 | /***********************************************************/ 2091 | static int inchar() 2092 | { 2093 | int v; 2094 | #ifdef ARDUINO 2095 | 2096 | switch( inStream ) { 2097 | case( kStreamFile ): 2098 | #ifdef ENABLE_FILEIO 2099 | v = fp.read(); 2100 | if( v == NL ) v=CR; // file translate 2101 | if( !fp.available() ) { 2102 | fp.close(); 2103 | goto inchar_loadfinish; 2104 | } 2105 | return v; 2106 | #else 2107 | #endif 2108 | break; 2109 | case( kStreamEEProm ): 2110 | #ifdef ENABLE_EEPROM 2111 | #ifdef ARDUINO 2112 | v = EEPROM.read( eepos++ ); 2113 | if( v == '\0' ) { 2114 | goto inchar_loadfinish; 2115 | } 2116 | return v; 2117 | #endif 2118 | #else 2119 | inStream = kStreamSerial; 2120 | return NL; 2121 | #endif 2122 | break; 2123 | case( kStreamSerial ): 2124 | default: 2125 | while(1) 2126 | { 2127 | Wire.requestFrom(0x88, 1); 2128 | if(Wire.available()) { // slave may send less than requested 2129 | char c = Wire.read(); // receive a byte as character 2130 | if(c!=0) { 2131 | return c; 2132 | } 2133 | } 2134 | delay(10); 2135 | } 2136 | } 2137 | 2138 | // inchar_loadfinish: 2139 | inStream = kStreamSerial; 2140 | inhibitOutput = false; 2141 | 2142 | if( runAfterLoad ) { 2143 | runAfterLoad = false; 2144 | triggerRun = true; 2145 | } 2146 | return NL; // trigger a prompt. 2147 | 2148 | #else 2149 | // otherwise. desktop! 2150 | int got = getchar(); 2151 | 2152 | // translation for desktop systems 2153 | if( got == LF ) got = CR; 2154 | 2155 | return got; 2156 | #endif 2157 | } 2158 | 2159 | /***********************************************************/ 2160 | static void outchar(unsigned char c) 2161 | { 2162 | if( inhibitOutput ) return; 2163 | 2164 | #ifdef ARDUINO 2165 | #ifdef ENABLE_FILEIO 2166 | if( outStream == kStreamFile ) { 2167 | // output to a file 2168 | fp.write( c ); 2169 | } 2170 | else 2171 | #endif 2172 | #ifdef ARDUINO 2173 | #ifdef ENABLE_EEPROM 2174 | if( outStream == kStreamEEProm ) { 2175 | EEPROM.write( eepos++, c ); 2176 | } 2177 | else 2178 | #endif /* ENABLE_EEPROM */ 2179 | #endif /* ARDUINO */ 2180 | // Serial.write(c); 2181 | // M5.Lcd.write(c); 2182 | termPutchar(c); 2183 | 2184 | #else 2185 | putchar(c); 2186 | #endif 2187 | } 2188 | 2189 | /***********************************************************/ 2190 | /* SD Card helpers */ 2191 | 2192 | #if ARDUINO && ENABLE_FILEIO 2193 | 2194 | static int initSD( void ) 2195 | { 2196 | // if the card is already initialized, we just go with it. 2197 | // there is no support (yet?) for hot-swap of SD Cards. if you need to 2198 | // swap, pop the card, reset the arduino.) 2199 | 2200 | if( sd_is_initialized == true ) return kSD_OK; 2201 | 2202 | // due to the way the SD Library works, pin 10 always needs to be 2203 | // an output, even when your shield uses another line for CS 2204 | pinMode(10, OUTPUT); // change this to 53 on a mega 2205 | 2206 | if( !SD.begin( kSD_CS )) { 2207 | // failed 2208 | printmsg( sderrormsg ); 2209 | return kSD_Fail; 2210 | } 2211 | // success - quietly return 0 2212 | sd_is_initialized = true; 2213 | 2214 | // and our file redirection flags 2215 | outStream = kStreamSerial; 2216 | inStream = kStreamSerial; 2217 | inhibitOutput = false; 2218 | 2219 | return kSD_OK; 2220 | } 2221 | #endif 2222 | 2223 | #if ENABLE_FILEIO 2224 | void cmd_Files( void ) 2225 | { 2226 | File dir = SD.open( "/" ); 2227 | dir.seek(0); 2228 | 2229 | while( true ) { 2230 | File entry = dir.openNextFile(); 2231 | if( !entry ) { 2232 | entry.close(); 2233 | break; 2234 | } 2235 | 2236 | // common header 2237 | printmsgNoNL( indentmsg ); 2238 | printmsgNoNL( (const unsigned char *)entry.name() ); 2239 | if( entry.isDirectory() ) { 2240 | printmsgNoNL( slashmsg ); 2241 | } 2242 | 2243 | if( entry.isDirectory() ) { 2244 | // directory ending 2245 | for( int i=strlen( entry.name()) ; i<16 ; i++ ) { 2246 | printmsgNoNL( spacemsg ); 2247 | } 2248 | printmsgNoNL( dirextmsg ); 2249 | } 2250 | else { 2251 | // file ending 2252 | for( int i=strlen( entry.name()) ; i<17 ; i++ ) { 2253 | printmsgNoNL( spacemsg ); 2254 | } 2255 | printUnum( entry.size() ); 2256 | } 2257 | line_terminator(); 2258 | entry.close(); 2259 | } 2260 | dir.close(); 2261 | } 2262 | #endif 2263 | --------------------------------------------------------------------------------