/* xsh_basic.c - xsh_basic */ #include #include #define NLINES 14 #define LINE_LEN 32 void outchar( char ch ); #ifndef boolean #define boolean int #define true 1 #define false 0 #endif #include "text_buffer.h" /*------------------------------------------------------------------------ * xhs_basic - basic interpreter *------------------------------------------------------------------------ */ shellcmd xsh_basic(int nargs, char *args[]) { int dev = 0; // printf("stk: 0x%08x\n", &dev); control(dev, TC_MODER, 0, 0); setup(); loop(); control(dev, TC_MODEC, 0, 0); // printf("stk: 0x%08x\n", &dev); return 0; } //////////////////////////////////////////////////////////////////////////////// // TinyBasic Plus //////////////////////////////////////////////////////////////////////////////// // // Authors: // Gordon Brandly (Tiny Basic for 68000) // Mike Field (Arduino Basic) (port to Arduino) // Scott Lawrence (TinyBasic Plus) (features, etc) // // Contributors: // Brian O'Dell (INPUT) // (full list tbd) // For full history of Tiny Basic, please see the wikipedia entry here: // https://en.wikipedia.org/wiki/Tiny_BASIC // LICENSING NOTES: // Mike Field based his C port of Tiny Basic on the 68000 // Tiny BASIC which carried the following license: /* ****************************************************************** * * * Tiny BASIC for the Motorola MC68000 * * * * Derived from Palo Alto Tiny BASIC as published in the May 1976 * * issue of Dr. Dobb's Journal. Adapted to the 68000 by: * * Gordon Brandly * * 12147 - 51 Street * * Edmonton AB T5W 3G8 * * Canada * * (updated mailing address for 1996) * * * * This version is for MEX68KECB Educational Computer Board I/O. * * * ****************************************************************** * Copyright (C) 1984 by Gordon Brandly. This program may be * * freely distributed for personal use only.function avr all arduino pins in c language All commercial * * rights are reserved. * ****************************************************************** */ // ref: http://members.shaw.ca:80/gbrandly/68ktinyb.html // // However, Mike did not include a license of his own for his // version of this. // ref: http://hamsterworks.co.nz/mediawiki/index.php/Arduino_Basic // // From discussions with him, I felt that the MIT license is // the most applicable to his intent. // // I am in the process of further determining what should be // done wrt licensing further. This entire header will likely // change with the next version 0.16, which will hopefully nail // down the whole thing so we can get back to implementing // features instead of licenses. Thank you for your time. #define kVersion "v0.15" // v0.15: 2018-06-23 // Integrating some contributions // Corrected some of the #ifdef nesting atop this page // Licensing issues beginning to be addressed // v0.14: 2013-11-07 // Modified Input command to accept an expression using getn() // Syntax is "input x" where x is any variable // NOTE: This only works for numbers, expressions. not strings. // // v0.13: 2013-03-04 // Support for Arduino 1.5 (SPI.h included, additional changes for DUE support) // // v0.12: 2013-03-01 // EEPROM load and save routines added: EFORMAT, ELIST, ELOAD, ESAVE, ECHAIN // added EAUTORUN option (chains to EEProm saved program on startup) // Bugfixes to build properly on non-arduino systems (PROGMEM #define workaround) // cleaned up a bit of the #define options wrt TONE // // v0.11: 2013-02-20 // all display strings and tables moved to PROGMEM to save space // removed second serial // removed pinMode completely, autoconf is explicit // beginnings of EEPROM related functionality (new,load,save,list) // // v0.10: 2012-10-15 // added kAutoConf, which eliminates the "PINMODE" statement. // now, DWRITE,DREAD,AWRITE,AREAD automatically set the PINMODE appropriately themselves. // should save a few bytes in your programs. // // v0.09: 2012-10-12 // Fixed directory listings. FILES now always works. (bug in the SD library) // ref: http://arduino.cc/forum/index.php/topic,124739.0.html // fixed filesize printouts (added printUnum for unsigned numbers) // #defineable baud rate for slow connection throttling //e // v0.08: 2012-10-02 // Tone generation through piezo added (TONE, TONEW, NOTONE) // // v0.07: 2012-09-30 // Autorun buildtime configuration feature // // v0.06: 2012-09-27 // Added optional second serial input, used for an external keyboard // // v0.05: 2012-09-21 // CHAIN to load and run a second file // RND,RSEED for random stuff // Added "!=" for "<>" synonym // Added "END" for "STOP" synonym (proper name for the functionality anyway) // // v0.04: 2012-09-20 // DELAY ms - for delaying // PINMODE , INPUT|IN|I|OUTPUT|OUT|O // DWRITE , HIGH|HI|1|LOW|LO|0 // AWRITE , [0..255] // fixed "save" appending to existing files instead of overwriting // Updated for building desktop command line app (incomplete) // // v0.03: 2012-09-19 // Integrated Jurg Wullschleger whitespace,unary fix // Now available through github // Project renamed from "Tiny Basic in C" to "TinyBasic Plus" // // v0.02b: 2012-09-17 Scott Lawrence // Better FILES listings // // v0.02a: 2012-09-17 Scott Lawrence // Support for SD Library // Added: SAVE, FILES (mostly works), LOAD (mostly works) (redirects IO) // Added: MEM, ? (PRINT) // Quirk: "10 LET A=B+C" is ok "10 LET A = B + C" is not. // Quirk: INPUT seems broken? // IF testing with Visual C, this needs to be the first thing in the file. //#include "stdafx.h" // char eliminateCompileErrors = 1; // fix to suppress arduino build errors // hack to let makefiles work with this file unchanged #undef ARDUINO //////////////////////////////////////////////////////////////////////////////// // Feature option configuration... // This enables LOAD, SAVE, FILES commands through the Arduino SD Library // it adds 9k of usage as well. //#define ENABLE_FILEIO 1 #undef ENABLE_FILEIO // this turns on "autorun". if there's FileIO, and a file "autorun.bas", // then it will load it and run it when starting up //#define ENABLE_AUTORUN 1 #undef ENABLE_AUTORUN // this will enable the "TONE", "NOTONE" command using a piezo // element on the specified pin. Wire the red/positive/piezo to the kPiezoPin, // and the black/negative/metal disc to ground. // it adds 1.5k of usage as well. //#define ENABLE_TONES 1 #define kPiezoPin 5 // we can use the EEProm to store a program during powerdown. This is // 1kbyte on the '328, and 512 bytes on the '168. Enabling this here will // allow for this funcitonality to work. Note that this only works on AVR // arduino. Disable it for DUE/other devices. // Sometimes, we connect with a slower device as the console. // Set your console D0/D1 baud rate here (9600 baud default) #define kConsoleBaud 9600 // Enable memory alignment for certain processers (e.g. some ESP8266-based devices) #ifdef ESP8266 // Uses up to one extra byte per program line of memory #define ALIGN_MEMORY 1 #else #undef ALIGN_MEMORY #endif // not an arduino, so we can disable these features. // turn off EEProm #undef ENABLE_EEPROM #undef ENABLE_TONES // includes, and settings for Arduino-specific features // set up file includes for things we need, or desktop specific stuff. #include #include // size of our program ram // RAFA #define kRamSize 64*1024 /* arbitrary - not dependant on libraries */ // RAFA #define kRamSize 320 /* arbitrary - not dependant on libraries */ #define kRamSize 260 /* arbitrary - not dependant on libraries */ // RAFA AGREGA // #define ALIGN_MEMORY 1 //////////////////// // memory alignment // necessary for some esp8266-based devices #ifdef ALIGN_MEMORY // Align memory addess x to an even page #define ALIGN_UP(x) ((unsigned char*)(((unsigned int)(x + 1) >> 1) << 1)) #define ALIGN_DOWN(x) ((unsigned char*)(((unsigned int)x >> 1) << 1)) #else #define ALIGN_UP(x) x #define ALIGN_DOWN(x) x #endif //////////////////// // various other desktop-tweaks and such. #ifndef boolean #define boolean int #define true 1 #define false 0 #endif #ifndef byte typedef unsigned char byte; #endif // some catches for AVR based text string stuff... #ifndef PROGMEM #define PROGMEM #endif #ifndef pgm_read_byte #define pgm_read_byte( A ) *(A) #endif //////////////////// // these will select, at runtime, where IO happens through for load/save enum { kStreamSerial = 0, kStreamEEProm, kStreamFile }; // static unsigned char inStream = kStreamSerial; // static unsigned char outStream = kStreamSerial; //////////////////////////////////////////////////////////////////////////////// // ASCII Characters #define CR '\r' #define NL '\n' #define LF 0x0a #define TAB '\t' #define BELL '\b' #define SPACE ' ' #define SQUOTE '\'' #define DQUOTE '\"' #define CTRLC 0x03 #define CTRLH 0x08 #define CTRLS 0x13 #define CTRLX 0x18 typedef short unsigned LINENUM; #define ECHO_CHARS 0 // RAFA // extern unsigned char program[NLINES*LINE_LEN]; // static const char * sentinel = "HELLO"; static unsigned char *txtpos,*list_line, *tmptxtpos; static unsigned char expression_error; /***********************************************************/ // Keyword table and constants - the last character has 0x80 added to it const static unsigned char keywords[] PROGMEM = { 'L','I','S','T'+0x80, 'L','O','A','D'+0x80, 'N','E','W'+0x80, 'R','U','N'+0x80, 'S','A','V','E'+0x80, 'N','E','X','T'+0x80, 'L','E','T'+0x80, 'I','F'+0x80, 'G','O','T','O'+0x80, 'G','O','S','U','B'+0x80, 'R','E','T','U','R','N'+0x80, 'R','E','M'+0x80, 'F','O','R'+0x80, 'I','N','P','U','T'+0x80, 'P','R','I','N','T'+0x80, 'P','O','K','E'+0x80, 'S','T','O','P'+0x80, 'B','Y','E'+0x80, 'F','I','L','E','S'+0x80, 'M','E','M'+0x80, '?'+ 0x80, '\''+ 0x80, 'A','W','R','I','T','E'+0x80, 'D','W','R','I','T','E'+0x80, 'D','E','L','A','Y'+0x80, 'E','N','D'+0x80, 'R','S','E','E','D'+0x80, 'C','H','A','I','N'+0x80, #ifdef ENABLE_TONES 'T','O','N','E','W'+0x80, 'T','O','N','E'+0x80, 'N','O','T','O','N','E'+0x80, #endif 0 }; // by moving the command list to an enum, we can easily remove sections // above and below simultaneously to selectively obliterate functionality. enum { KW_LIST = 0, KW_LOAD, KW_NEW, KW_RUN, KW_SAVE, KW_NEXT, KW_LET, KW_IF, KW_GOTO, KW_GOSUB, KW_RETURN, KW_REM, KW_FOR, KW_INPUT, KW_PRINT, KW_POKE, KW_STOP, KW_BYE, KW_FILES, KW_MEM, KW_QMARK, KW_QUOTE, KW_AWRITE, KW_DWRITE, KW_DELAY, KW_END, KW_RSEED, KW_CHAIN, #ifdef ENABLE_TONES KW_TONEW, KW_TONE, KW_NOTONE, #endif KW_DEFAULT /* always the final one*/ }; struct stack_for_frame { char frame_type; char for_var; short int terminal; short int step; unsigned char *current_line; unsigned char *txtpos; }; struct stack_gosub_frame { char frame_type; unsigned char *current_line; unsigned char *txtpos; }; const static unsigned char func_tab[] PROGMEM = { 'P','E','E','K'+0x80, 'A','B','S'+0x80, 'A','R','E','A','D'+0x80, 'D','R','E','A','D'+0x80, 'R','N','D'+0x80, 0 }; #define FUNC_PEEK 0 #define FUNC_ABS 1 #define FUNC_AREAD 2 #define FUNC_DREAD 3 #define FUNC_RND 4 #define FUNC_UNKNOWN 5 const static unsigned char to_tab[] PROGMEM = { 'T','O'+0x80, 0 }; const static unsigned char step_tab[] PROGMEM = { 'S','T','E','P'+0x80, 0 }; const static unsigned char relop_tab[] PROGMEM = { '>','='+0x80, '<','>'+0x80, '>'+0x80, '='+0x80, '<','='+0x80, '<'+0x80, '!','='+0x80, 0 }; #define RELOP_GE 0 #define RELOP_NE 1 #define RELOP_GT 2 #define RELOP_EQ 3 #define RELOP_LE 4 #define RELOP_LT 5 #define RELOP_NE_BANG 6 #define RELOP_UNKNOWN 7 const static unsigned char highlow_tab[] PROGMEM = { 'H','I','G','H'+0x80, 'H','I'+0x80, 'L','O','W'+0x80, 'L','O'+0x80, 0 }; #define HIGHLOW_HIGH 1 #define HIGHLOW_UNKNOWN 4 //RAFA ANTES DECIA 5 //#define STACK_SIZE (sizeof(struct stack_for_frame)*5) #define STACK_SIZE (sizeof(struct stack_for_frame)*3) #define VAR_SIZE sizeof(short int) // Size of variables in bytes static unsigned char *stack_limit; static unsigned char *program_start; static unsigned char *program_end; // static unsigned char *stack; // Software stack for things that should go on the CPU stack static unsigned char *variables_begin; static unsigned char *current_line; static unsigned char *sp; #define STACK_GOSUB_FLAG 'G' #define STACK_FOR_FLAG 'F' static unsigned char table_index; static LINENUM linenum; static const unsigned char okmsg[] PROGMEM = "OK"; static const unsigned char whatmsg[] PROGMEM = "What? "; static const unsigned char howmsg[] PROGMEM = "How?"; static const unsigned char sorrymsg[] PROGMEM = "Sorry!"; static const unsigned char initmsg[] PROGMEM = "\nWelcome to TinyBasic Plus interpreter " kVersion; static const unsigned char memorymsg[] PROGMEM = " bytes free."; static const unsigned char avr_vars_msg[] PROGMEM = "available VARS: A - J"; static const unsigned char breakmsg[] PROGMEM = "break!"; static const unsigned char unimplimentedmsg[] PROGMEM = "Unimplemented"; static const unsigned char backspacemsg[] PROGMEM = "\033[D \033[D"; //static const unsigned char backspacemsg[] PROGMEM = "\b \b"; static const unsigned char indentmsg[] PROGMEM = " "; static const unsigned char sderrormsg[] PROGMEM = "SD card error."; static const unsigned char sdfilemsg[] PROGMEM = "SD file error."; static const unsigned char dirextmsg[] PROGMEM = "(dir)"; static const unsigned char slashmsg[] PROGMEM = "/"; static const unsigned char spacemsg[] PROGMEM = " "; static int inchar(void); //static void outchar(unsigned char c); static void line_terminator(void); static short int expression(void); static unsigned char breakcheck(void); /***************************************************************************/ static void ignore_blanks(void) { while(*txtpos == SPACE || *txtpos == TAB) txtpos++; } /***************************************************************************/ static void scantable(const unsigned char *table) { int i = 0; table_index = 0; while(1) { // Run out of table entries? if(pgm_read_byte( table ) == 0) return; // Do we match this character? if(txtpos[i] == pgm_read_byte( table )) { i++; table++; } else { // do we match the last character of keywork (with 0x80 added)? If so, return if(txtpos[i]+0x80 == pgm_read_byte( table )) { txtpos += i+1; // Advance the pointer to following the keyword ignore_blanks(); return; } // Forward to the end of this keyword while((pgm_read_byte( table ) & 0x80) == 0) table++; // Now move on to the first character of the next word, and reset the position index table++; table_index++; ignore_blanks(); i = 0; } } } /***************************************************************************/ static void pushb(unsigned char b) { sp--; *sp = b; } /***************************************************************************/ static unsigned char popb() { unsigned char b; b = *sp; sp++; return b; } /***************************************************************************/ void printnum(int num) { int digits = 0; if(num < 0) { num = -num; outchar('-'); } do { pushb(num%10+'0'); num = num/10; digits++; } while (num > 0); while(digits > 0) { outchar(popb()); digits--; } } void printUnum(unsigned int num) { int digits = 0; do { pushb(num%10+'0'); num = num/10; digits++; } while (num > 0); while(digits > 0) { outchar(popb()); digits--; } } /***************************************************************************/ static unsigned short testnum(void) { unsigned short num = 0; ignore_blanks(); while(*txtpos>= '0' && *txtpos <= '9' ) { // Trap overflows if(num >= 0xFFFF/10) { num = 0xFFFF; break; } num = num *10 + *txtpos - '0'; txtpos++; } return num; } /***************************************************************************/ static unsigned char print_quoted_string(void) { int i=0; unsigned char delim = *txtpos; if(delim != '"' && delim != '\'') return 0; txtpos++; // Check we have a closing delimiter while(txtpos[i] != delim) { if(txtpos[i] == NL) return 0; i++; } // Print the characters while(*txtpos != delim) { outchar(*txtpos); txtpos++; } txtpos++; // Skip over the last delimiter return 1; } /***************************************************************************/ void printmsgNoNL(const unsigned char *msg) { while( pgm_read_byte( msg ) != 0 ) { outchar( pgm_read_byte( msg++ ) ); }; } /***************************************************************************/ void printmsg(const unsigned char *msg) { printmsgNoNL(msg); line_terminator(); } /***************************************************************************/ static void getln(char prompt) { char s = ' '; outchar(prompt); outchar(s); txtpos = program_end+sizeof(LINENUM); while(1) { char c = inchar(); switch(c) { case NL: //break; case CR: line_terminator(); // Terminate all strings with a NL txtpos[0] = NL; return; case CTRLH: if(txtpos == program_end) break; txtpos--; printmsgNoNL(backspacemsg); break; default: // We need to leave at least one space to allow us to shuffle the line into order if(txtpos == variables_begin-2) outchar(BELL); else { txtpos[0] = c; txtpos++; outchar(c); } } } } /***************************************************************************/ static unsigned char *findline(void) { unsigned char *line = program_start; while(1) { if(line == program_end) return line; if(((LINENUM *)line)[0] >= linenum) return line; // Add the line length onto the current address, to get to the next line; line += line[sizeof(LINENUM)]; } } /***************************************************************************/ static void toUppercaseBuffer(void) { unsigned char *c = program_end+sizeof(LINENUM); unsigned char quote = 0; while(*c != NL) { // Are we in a quoted string? if(*c == quote) quote = 0; else if(*c == '"' || *c == '\'') quote = *c; else if(quote == 0 && *c >= 'a' && *c <= 'z') *c = *c + 'A' - 'a'; c++; } } /***************************************************************************/ void printline() { LINENUM line_num; line_num = *((LINENUM *)(list_line)); list_line += sizeof(LINENUM) + sizeof(char); // Output the line */ printnum(line_num); outchar(' '); while(*list_line != NL) { outchar(*list_line); list_line++; } list_line++; #ifdef ALIGN_MEMORY // Start looking for next line on even page if (ALIGN_UP(list_line) != list_line) list_line++; #endif line_terminator(); } /***************************************************************************/ static short int expr4(void) { // fix provided by Jurg Wullschleger wullschleger@gmail.com // fixes whitespace and unary operations ignore_blanks(); if( *txtpos == '-' ) { txtpos++; return -expr4(); } // end fix if(*txtpos == '0') { txtpos++; return 0; } if(*txtpos >= '1' && *txtpos <= '9') { short int a = 0; do { a = a*10 + *txtpos - '0'; txtpos++; } while(*txtpos >= '0' && *txtpos <= '9'); return a; } // Is it a function or variable reference? if(txtpos[0] >= 'A' && txtpos[0] <= 'Z') { short int a; // Is it a variable reference (single alpha) if(txtpos[1] < 'A' || txtpos[1] > 'Z') { a = ((short int *)variables_begin)[*txtpos - 'A']; txtpos++; return a; } // Is it a function with a single parameter scantable(func_tab); if(table_index == FUNC_UNKNOWN) goto expr4_error; unsigned char f = table_index; if(*txtpos != '(') goto expr4_error; txtpos++; a = expression(); if(*txtpos != ')') goto expr4_error; txtpos++; switch(f) { case FUNC_PEEK: // RAFA return program[a]; return 0; case FUNC_ABS: if(a < 0) return -a; return a; case FUNC_RND: return( rand() % a ); } } if(*txtpos == '(') { short int a; txtpos++; a = expression(); if(*txtpos != ')') goto expr4_error; txtpos++; return a; } expr4_error: expression_error = 1; return 0; } /***************************************************************************/ static short int expr3(void) { short int a,b; a = expr4(); ignore_blanks(); // fix for eg: 100 a = a + 1 while(1) { if(*txtpos == '*') { txtpos++; b = expr4(); a *= b; } else if(*txtpos == '/') { txtpos++; b = expr4(); if(b != 0) a /= b; else expression_error = 1; } else return a; } } /***************************************************************************/ static short int expr2(void) { short int a,b; if(*txtpos == '-' || *txtpos == '+') a = 0; else a = expr3(); while(1) { if(*txtpos == '-') { txtpos++; b = expr3(); a -= b; } else if(*txtpos == '+') { txtpos++; b = expr3(); a += b; } else return a; } } /***************************************************************************/ static short int expression(void) { short int a,b; a = expr2(); // Check if we have an error if(expression_error) return a; scantable(relop_tab); if(table_index == RELOP_UNKNOWN) return a; switch(table_index) { case RELOP_GE: b = expr2(); if(a >= b) return 1; break; case RELOP_NE: case RELOP_NE_BANG: b = expr2(); if(a != b) return 1; break; case RELOP_GT: b = expr2(); if(a > b) return 1; break; case RELOP_EQ: b = expr2(); if(a == b) return 1; break; case RELOP_LE: b = expr2(); if(a <= b) return 1; break; case RELOP_LT: b = expr2(); if(a < b) return 1; break; } return 0; } /***************************************************************************/ void loop() { unsigned char *start; unsigned char *newEnd; unsigned char linelen; boolean isDigital; boolean alsoWait = false; int val; // unsigned char program[NLINES*LINE_LEN]; unsigned char program[kRamSize]; // some settings based things boolean inhibitOutput = false; boolean runAfterLoad = false; boolean triggerRun = false; unsigned char *tempsp; program_start = program; program_end = program_start; sp = program+sizeof(program); // Needed for printnum #ifdef ALIGN_MEMORY // Ensure these memory blocks start on even pages stack_limit = ALIGN_DOWN(program+sizeof(program)-STACK_SIZE); // RAFA variables_begin = ALIGN_DOWN(stack_limit - 27*VAR_SIZE); variables_begin = ALIGN_DOWN(stack_limit - 10*VAR_SIZE); #else stack_limit = program+sizeof(program)-STACK_SIZE; // RAFA variables_begin = stack_limit - 27*VAR_SIZE; variables_begin = stack_limit - 10*VAR_SIZE; #endif // memory free printmsg(initmsg); printnum(variables_begin-program_end); printmsg(memorymsg); printmsg(avr_vars_msg); warmstart: // this signifies that it is running in 'direct' mode. current_line = 0; sp = program+sizeof(program); printmsg(okmsg); prompt: if( triggerRun ){ triggerRun = false; current_line = program_start; goto execline; } getln( '>' ); toUppercaseBuffer(); txtpos = program_end+sizeof(unsigned short); // Find the end of the freshly entered line while(*txtpos != NL) txtpos++; // Move it to the end of program_memory { unsigned char *dest; dest = variables_begin-1; while(1) { *dest = *txtpos; if(txtpos == program_end+sizeof(unsigned short)) break; dest--; txtpos--; } txtpos = dest; } // Now see if we have a line number linenum = testnum(); ignore_blanks(); if(linenum == 0) goto direct; if(linenum == 0xFFFF) goto qhow; // Find the length of what is left, including the (yet-to-be-populated) line header linelen = 0; while(txtpos[linelen] != NL) linelen++; linelen++; // Include the NL in the line length linelen += sizeof(unsigned short)+sizeof(char); // Add space for the line number and line length // Now we have the number, add the line header. txtpos -= 3; #ifdef ALIGN_MEMORY // Line starts should always be on 16-bit pages if (ALIGN_DOWN(txtpos) != txtpos) { txtpos--; linelen++; // As the start of the line has moved, the data should move as well unsigned char *tomove; tomove = txtpos + 3; while (tomove < txtpos + linelen - 1) { *tomove = *(tomove + 1); tomove++; } } #endif *((unsigned short *)txtpos) = linenum; txtpos[sizeof(LINENUM)] = linelen; // Merge it into the rest of the program start = findline(); // If a line with that number exists, then remove it if(start != program_end && *((LINENUM *)start) == linenum) { unsigned char *dest, *from; unsigned tomove; from = start + start[sizeof(LINENUM)]; dest = start; tomove = program_end - from; while( tomove > 0) { *dest = *from; from++; dest++; tomove--; } program_end = dest; } if(txtpos[sizeof(LINENUM)+sizeof(char)] == NL) // If the line has no txt, it was just a delete goto prompt; // Make room for the new line, either all in one hit or lots of little shuffles while(linelen > 0) { unsigned int tomove; unsigned char *from,*dest; unsigned int space_to_make; space_to_make = txtpos - program_end; if(space_to_make > linelen) space_to_make = linelen; newEnd = program_end+space_to_make; tomove = program_end - start; // Source and destination - as these areas may overlap we need to move bottom up from = program_end; dest = newEnd; while(tomove > 0) { from--; dest--; *dest = *from; tomove--; } // Copy over the bytes into the new space for(tomove = 0; tomove < space_to_make; tomove++) { *start = *txtpos; txtpos++; start++; linelen--; } program_end = newEnd; } goto prompt; unimplemented: printmsg(unimplimentedmsg); goto prompt; qhow: printmsg(howmsg); goto prompt; qwhat: printmsgNoNL(whatmsg); if(current_line != NULL) { unsigned char tmp = *txtpos; if(*txtpos != NL) *txtpos = '^'; list_line = current_line; printline(); *txtpos = tmp; } line_terminator(); goto prompt; qsorry: printmsg(sorrymsg); goto warmstart; run_next_statement: while(*txtpos == ':') txtpos++; ignore_blanks(); if(*txtpos == NL) goto execnextline; goto interperateAtTxtpos; direct: txtpos = program_end+sizeof(LINENUM); if(*txtpos == NL) goto prompt; interperateAtTxtpos: if(breakcheck()) { printmsg(breakmsg); goto warmstart; } scantable(keywords); // RAFA // short int val; // int stk2 = 4; switch(table_index) { case KW_DELAY: { goto unimplemented; } case KW_FILES: goto files; case KW_LIST: goto list; case KW_CHAIN: goto chain; case KW_LOAD: goto load; case KW_MEM: goto mem; case KW_NEW: if(txtpos[0] != NL) goto qwhat; program_end = program_start; goto prompt; case KW_RUN: current_line = program_start; goto execline; case KW_SAVE: goto save; case KW_NEXT: goto next; case KW_LET: goto assignment; case KW_IF: // short int val; expression_error = 0; val = expression(); if(expression_error || *txtpos == NL) goto qhow; if(val != 0) goto interperateAtTxtpos; goto execnextline; case KW_GOTO: expression_error = 0; linenum = expression(); if(expression_error || *txtpos != NL) goto qhow; current_line = findline(); goto execline; case KW_GOSUB: goto gosub; case KW_RETURN: goto gosub_return; case KW_REM: case KW_QUOTE: goto execnextline; // Ignore line completely case KW_FOR: goto forloop; case KW_INPUT: goto input; case KW_PRINT: case KW_QMARK: goto print; case KW_POKE: goto poke; case KW_END: case KW_STOP: // This is the easy way to end - set the current line to the end of program attempt to run it if(txtpos[0] != NL) goto qwhat; current_line = program_end; goto execline; case KW_BYE: // printf("stk2: 0x%08x\n", &stk2); // Leave the basic interperater return; case KW_AWRITE: // AWRITE , HIGH|LOW isDigital = false; goto awrite; case KW_DWRITE: // DWRITE , HIGH|LOW isDigital = true; goto dwrite; case KW_RSEED: goto rseed; #ifdef ENABLE_TONES case KW_TONEW: alsoWait = true; case KW_TONE: goto tonegen; case KW_NOTONE: goto tonestop; #endif case KW_DEFAULT: goto assignment; default: break; } execnextline: if(current_line == NULL) // Processing direct commands? goto prompt; current_line += current_line[sizeof(LINENUM)]; execline: if(current_line == program_end) // Out of lines to run goto warmstart; txtpos = current_line+sizeof(LINENUM)+sizeof(char); goto interperateAtTxtpos; input: { unsigned char var; int value; ignore_blanks(); if(*txtpos < 'A' || *txtpos > 'Z') goto qwhat; var = *txtpos; txtpos++; ignore_blanks(); if(*txtpos != NL && *txtpos != ':') goto qwhat; inputagain: tmptxtpos = txtpos; getln( '?' ); toUppercaseBuffer(); txtpos = program_end+sizeof(unsigned short); ignore_blanks(); expression_error = 0; value = expression(); if(expression_error) goto inputagain; ((short int *)variables_begin)[var-'A'] = value; txtpos = tmptxtpos; goto run_next_statement; } forloop: { unsigned char var; short int initial, step, terminal; ignore_blanks(); if(*txtpos < 'A' || *txtpos > 'Z') goto qwhat; var = *txtpos; txtpos++; ignore_blanks(); if(*txtpos != '=') goto qwhat; txtpos++; ignore_blanks(); expression_error = 0; initial = expression(); if(expression_error) goto qwhat; scantable(to_tab); if(table_index != 0) goto qwhat; terminal = expression(); if(expression_error) goto qwhat; scantable(step_tab); if(table_index == 0) { step = expression(); if(expression_error) goto qwhat; } else step = 1; ignore_blanks(); if(*txtpos != NL && *txtpos != ':') goto qwhat; if(!expression_error && *txtpos == NL) { struct stack_for_frame *f; if(sp + sizeof(struct stack_for_frame) < stack_limit) goto qsorry; sp -= sizeof(struct stack_for_frame); f = (struct stack_for_frame *)sp; ((short int *)variables_begin)[var-'A'] = initial; f->frame_type = STACK_FOR_FLAG; f->for_var = var; f->terminal = terminal; f->step = step; f->txtpos = txtpos; f->current_line = current_line; goto run_next_statement; } } goto qhow; gosub: expression_error = 0; linenum = expression(); if(!expression_error && *txtpos == NL) { struct stack_gosub_frame *f; if(sp + sizeof(struct stack_gosub_frame) < stack_limit) goto qsorry; sp -= sizeof(struct stack_gosub_frame); f = (struct stack_gosub_frame *)sp; f->frame_type = STACK_GOSUB_FLAG; f->txtpos = txtpos; f->current_line = current_line; current_line = findline(); goto execline; } goto qhow; next: // Fnd the variable name ignore_blanks(); if(*txtpos < 'A' || *txtpos > 'Z') goto qhow; txtpos++; ignore_blanks(); if(*txtpos != ':' && *txtpos != NL) goto qwhat; gosub_return: // Now walk up the stack frames and find the frame we want, if present tempsp = sp; while(tempsp < program+sizeof(program)-1) { switch(tempsp[0]) { case STACK_GOSUB_FLAG: if(table_index == KW_RETURN) { struct stack_gosub_frame *f = (struct stack_gosub_frame *)tempsp; current_line = f->current_line; txtpos = f->txtpos; sp += sizeof(struct stack_gosub_frame); goto run_next_statement; } // This is not the loop you are looking for... so Walk back up the stack tempsp += sizeof(struct stack_gosub_frame); break; case STACK_FOR_FLAG: // Flag, Var, Final, Step if(table_index == KW_NEXT) { struct stack_for_frame *f = (struct stack_for_frame *)tempsp; // Is the the variable we are looking for? if(txtpos[-1] == f->for_var) { short int *varaddr = ((short int *)variables_begin) + txtpos[-1] - 'A'; *varaddr = *varaddr + f->step; // Use a different test depending on the sign of the step increment if((f->step > 0 && *varaddr <= f->terminal) || (f->step < 0 && *varaddr >= f->terminal)) { // We have to loop so don't pop the stack txtpos = f->txtpos; current_line = f->current_line; goto run_next_statement; } // We've run to the end of the loop. drop out of the loop, popping the stack sp = tempsp + sizeof(struct stack_for_frame); goto run_next_statement; } } // This is not the loop you are looking for... so Walk back up the stack tempsp += sizeof(struct stack_for_frame); break; default: //printf("Stack is stuffed!\n"); goto warmstart; } } // Didn't find the variable we've been looking for goto qhow; assignment: { short int value; short int *var; if(*txtpos < 'A' || *txtpos > 'Z') goto qhow; var = (short int *)variables_begin + *txtpos - 'A'; txtpos++; ignore_blanks(); if (*txtpos != '=') goto qwhat; txtpos++; ignore_blanks(); expression_error = 0; value = expression(); if(expression_error) goto qwhat; // Check that we are at the end of the statement if(*txtpos != NL && *txtpos != ':') goto qwhat; *var = value; } goto run_next_statement; poke: { short int value; unsigned char *address; // Work out where to put it expression_error = 0; value = expression(); if(expression_error) goto qwhat; address = (unsigned char *)value; // check for a comma ignore_blanks(); if (*txtpos != ',') goto qwhat; txtpos++; ignore_blanks(); // Now get the value to assign expression_error = 0; value = expression(); if(expression_error) goto qwhat; //printf("Poke %p value %i\n",address, (unsigned char)value); // Check that we are at the end of the statement if(*txtpos != NL && *txtpos != ':') goto qwhat; } goto run_next_statement; list: linenum = testnum(); // Retuns 0 if no line found. // Should be EOL if(txtpos[0] != NL) goto qwhat; // Find the line list_line = findline(); while(list_line != program_end) printline(); goto warmstart; print: // If we have an empty list then just put out a NL if(*txtpos == ':' ) { line_terminator(); txtpos++; goto run_next_statement; } if(*txtpos == NL) { goto execnextline; } while(1) { ignore_blanks(); if(print_quoted_string()) { ; } else if(*txtpos == '"' || *txtpos == '\'') goto qwhat; else { short int e; expression_error = 0; e = expression(); if(expression_error) goto qwhat; printnum(e); } // At this point we have three options, a comma or a new line if(*txtpos == ',') txtpos++; // Skip the comma and move onto the next else if(txtpos[0] == ';' && (txtpos[1] == NL || txtpos[1] == ':')) { txtpos++; // This has to be the end of the print - no newline break; } else if(*txtpos == NL || *txtpos == ':') { line_terminator(); // The end of the print statement break; } else goto qwhat; } goto run_next_statement; mem: // memory free printnum(variables_begin-program_end); printmsg(memorymsg); goto run_next_statement; /*************************************************/ pinmode: // PINMODE , I/O awrite: // AWRITE ,val dwrite: goto unimplemented; /*************************************************/ files: // display a listing of files on the device. // version 1: no support for subdirectories goto unimplemented; chain: runAfterLoad = true; load: // clear the program program_end = program_start; // load from a file into memory goto unimplemented; save: // save from memory out to a file goto unimplemented; rseed: { short int value; //Get the pin number expression_error = 0; value = expression(); if(expression_error) goto qwhat; srand( value ); goto run_next_statement; } #ifdef ENABLE_TONES tonestop: noTone( kPiezoPin ); goto run_next_statement; tonegen: { // TONE freq, duration // if either are 0, tones turned off short int freq; short int duration; //Get the frequency expression_error = 0; freq = expression(); if(expression_error) goto qwhat; ignore_blanks(); if (*txtpos != ',') goto qwhat; txtpos++; ignore_blanks(); //Get the duration expression_error = 0; duration = expression(); if(expression_error) goto qwhat; if( freq == 0 || duration == 0 ) goto tonestop; tone( kPiezoPin, freq, duration ); if( alsoWait ) { delay( duration ); alsoWait = false; } goto run_next_statement; } #endif /* ENABLE_TONES */ } // returns 1 if the character is valid in a filename static int isValidFnChar( char c ) { if( c >= '0' && c <= '9' ) return 1; // number if( c >= 'A' && c <= 'Z' ) return 1; // LETTER if( c >= 'a' && c <= 'z' ) return 1; // letter (for completeness) if( c == '_' ) return 1; if( c == '+' ) return 1; if( c == '.' ) return 1; if( c == '~' ) return 1; // Window~1.txt return 0; } /***************************************************************************/ static void line_terminator(void) { outchar(NL); outchar(CR); } /***********************************************************/ void setup() { } /***********************************************************/ static unsigned char breakcheck(void) { #ifdef __CONIO__ if(kbhit()) //return getch() == CTRLC; return getc(0) == CTRLC; else #endif return 0; } /***********************************************************/ static int inchar() { int v; // otherwise. desktop! // int got = getchar(); int got = getc(0); // translation for desktop systems if( got == LF ) got = CR; return got; } /***********************************************************/ /* static void outchar(unsigned char c) { if( inhibitOutput ) return; putchar(c); } */ /* these are used in the .ino */ void outchar( char ch ) { printf( "%c", ch ); } /* other helpers */ /* int cmd_Files( void ) { DIR * theDir; theDir = opendir( "." ); if( !theDir ) return -2; struct dirent *theDirEnt = readdir( theDir ); while( theDirEnt ) { printf( " %s\n", theDirEnt->d_name ); theDirEnt = readdir( theDir ); } closedir( theDir ); return 0; } */