// TinyBASIC.cpp : An implementation of TinyBASIC in C to run on Nanode and Arduino // // This version now has the digital I/O, analogue I/O and sleep functions // // // Take on the Nanode Tiny Basic Christmas Challenge // // Help Hack Tiny Basic onto Nanode and kickstart a return to simpler and fun programming // Tiny Basic can help teach kids to program // // see http://sustburbia.blogspot.com/2011_12_01_archive.html // // HAPPY CHRISTMAS 2011 Everyone! // // Join us on the #Nanode IRC Channel http://webchat.freenode.net/?channels=nanode // // Arduino Tiny Basic Author : Mike Field - hamster@snap.net.nz // Thanks to Dave CJ (@ceejay on Twitter) for the digital, analogue and sleep functions // // Based on TinyBasic for 68000, by Gordon Brandly // (see http://members.shaw.ca/gbrandly/68ktinyb.html) // // which itself was Derived from Palo Alto Tiny BASIC as // published in the May 1976 issue of Dr. Dobb's Journal. // // 0.03 21/01/2011 : Added INPUT routine // : Reorganised memory layout // : Expanded all error messages // : Break key added // : Removed the calls to printf (left by debugging) #ifndef ARDUINO #include "stdafx.h" #include #endif // ASCII Characters #define CR '\r' #define NL '\n' #define TAB '\t' #define BELL '\b' #define DEL '\177' #define SPACE ' ' #define CTRLC 0x03 #define CTRLH 0x08 #define CTRLS 0x13 #define CTRLX 0x18 typedef short unsigned LINENUM; /***********************************************************/ // Keyword table and constants - the last character has 0x80 added to it static unsigned char keywords[] = { '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, 'D','O','U','T'+0x80, 'A','O','U','T'+0x80, 'S','L','E','E','P'+0x80, 0 }; #define KW_LIST 0 #define KW_LOAD 1 #define KW_NEW 2 #define KW_RUN 3 #define KW_SAVE 4 #define KW_NEXT 5 #define KW_LET 6 #define KW_IF 7 #define KW_GOTO 8 #define KW_GOSUB 9 #define KW_RETURN 10 #define KW_REM 11 #define KW_FOR 12 #define KW_INPUT 13 #define KW_PRINT 14 #define KW_POKE 15 #define KW_STOP 16 #define KW_BYE 17 #define KW_DOUT 18 #define KW_AOUT 19 #define KW_SLEEP 20 #define KW_DEFAULT 21 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; }; static unsigned char func_tab[] = { 'P','E','E','K'+0x80, 'A','B','S'+0x80, 'D','I','N'+0x80, 'A','I','N'+0x80, 'H','I','G','H'+0x80, 'L','O','W'+0x80, 0 }; #define FUNC_PEEK 0 #define FUNC_ABS 1 #define FUNC_DIN 2 #define FUNC_AIN 3 #define FUNC_HIGH 4 #define FUNC_LOW 5 #define FUNC_UNKNOWN 6 static unsigned char to_tab[] = { 'T','O'+0x80, 0 }; static unsigned char step_tab[] = { 'S','T','E','P'+0x80, 0 }; static unsigned char relop_tab[] = { '>','='+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_UNKNOWN 6 #define VAR_SIZE sizeof(short int) // Size of variables in bytes static unsigned char memory[1400]; static unsigned char *txtpos,*list_line; static unsigned char expression_error; static unsigned char *tempsp; 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_table; 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[] = "OK"; static const unsigned char badlinemsg[] = "Invalid line number"; static const unsigned char invalidexprmsg[] = "Invalid expression"; static const unsigned char syntaxmsg[] = "Syntax Error"; static const unsigned char badinputmsg[] = "\nBad number"; static const unsigned char nomemmsg[] = "Not enough memory!"; static const unsigned char initmsg[] = "NanoBasic in C"; static const unsigned char memorymsg[] = " bytes free."; static const unsigned char breakmsg[] = "break!"; static const unsigned char stackstuffedmsg[] = "Stack is stuffed!\n"; static const unsigned char unimplimentedmsg[] = "Unimplemented"; static const unsigned char backspacemsg[] = "\b \b"; 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(unsigned char *table) { int i = 0; ignore_blanks(); table_index = 0; while(1) { // Run out of table entries? if(table[0] == 0) return; // Do we match this character? if(txtpos[i] == table[0]) { i++; table++; } else { // do we match the last character of keywork (with 0x80 added)? If so, return if(txtpos[i]+0x80 == table[0]) { txtpos += i+1; // Advance the pointer to following the keyword ignore_blanks(); return; } // Forward to the end of this keyword while((table[0] & 0x80) == 0) table++; // Now move on to the first character of the next word, and reset the position index table++; table_index++; i = 0; } } } /***************************************************************************/ static void pushb(unsigned char b) { sp--; *sp = b; } /***************************************************************************/ static unsigned char popb() { unsigned char b; b = *sp; sp++; return b; } /***************************************************************************/ static 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--; } } /***************************************************************************/ 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; } /***************************************************************************/ unsigned char check_statement_end(void) { ignore_blanks(); return (*txtpos == NL) || (*txtpos == ':'); } /***************************************************************************/ static void printmsgNoNL(const unsigned char *msg) { while(*msg) { outchar(*msg); msg++; } } /***************************************************************************/ 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 ignore_blanks(); return 1; } /***************************************************************************/ static void printmsg(const unsigned char *msg) { printmsgNoNL(msg); line_terminator(); } /***************************************************************************/ unsigned char getln(char prompt) { outchar(prompt); txtpos = program_end+sizeof(LINENUM); while(1) { char c = inchar(); switch(c) { case CR: case NL: line_terminator(); // Terminate all strings with a NL txtpos[0] = NL; return 1; case CTRLC: return 0; 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 == sp-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++; line_terminator(); } /***************************************************************************/ static short int expr4(void) { short int a = 0; if(*txtpos == '0') { txtpos++; a = 0; goto success; } if(*txtpos >= '1' && *txtpos <= '9') { do { a = a*10 + *txtpos - '0'; txtpos++; } while(*txtpos >= '0' && *txtpos <= '9'); goto success; } // Is it a function or variable reference? if(txtpos[0] >= 'A' && txtpos[0] <= 'Z') { // Is it a variable reference (single alpha) if(txtpos[1] < 'A' || txtpos[1] > 'Z') { a = ((short int *)variables_table)[*txtpos - 'A']; txtpos++; goto success; } // Is it a function with a single parameter scantable(func_tab); if(table_index == FUNC_UNKNOWN) goto expr4_error; unsigned char f = table_index; // Serial.print("func value ="); // This is debug only // Serial.println(f); if (f == FUNC_HIGH) { a=1; goto success; } if (f == FUNC_LOW) { a=0; goto success; } if(*txtpos != '(') goto expr4_error; txtpos++; a = expression(); if(*txtpos != ')') goto expr4_error; txtpos++; switch(f) { case FUNC_PEEK: a = memory[a]; goto success; case FUNC_ABS: if(a < 0) a = -a; goto success; case FUNC_DIN: pinMode(a, INPUT); a = digitalRead(a); goto success; case FUNC_AIN: pinMode(a, INPUT); a = analogRead(a); goto success; } } if(*txtpos == '(') { txtpos++; a = expression(); if(*txtpos != ')') goto expr4_error; txtpos++; goto success; } expr4_error: expression_error = 1; success: ignore_blanks(); return a; } /***************************************************************************/ static short int expr3(void) { short int a,b; a = expr4(); 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: 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; variables_table = memory; program_start = memory + 27*VAR_SIZE; program_end = program_start; sp = memory+sizeof(memory); // Needed for printnum printmsg(initmsg); printnum(sp-program_end); printmsg(memorymsg); warmstart: // this signifies that it is running in 'direct' mode. current_line = 0; sp = memory+sizeof(memory); printmsg(okmsg); prompt: while(!getln('>')) line_terminator(); 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 = sp-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 badline; // 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; *((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; badline: printmsg(badlinemsg); goto prompt; invalidexpr: printmsg(invalidexprmsg); goto prompt; syntaxerror: printmsg(syntaxmsg); if(current_line != (void *)0) { unsigned char tmp = *txtpos; if(*txtpos != NL) *txtpos = '^'; list_line = current_line; printline(); *txtpos = tmp; } line_terminator(); goto prompt; stackstuffed: printmsg(stackstuffedmsg); goto warmstart; nomem: printmsg(nomemmsg); 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); ignore_blanks(); switch(table_index) { case KW_LIST: goto list; case KW_LOAD: goto unimplemented; ///////////////// case KW_NEW: if(txtpos[0] != NL) goto syntaxerror; program_end = program_start; goto prompt; case KW_RUN: current_line = program_start; goto execline; case KW_SAVE: goto unimplemented; ////////////////////// 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 invalidexpr; if(val != 0) goto interperateAtTxtpos; goto execnextline; } case KW_GOTO: expression_error = 0; linenum = expression(); if(expression_error || *txtpos != NL) goto invalidexpr; current_line = findline(); goto execline; case KW_GOSUB: goto gosub; case KW_RETURN: goto gosub_return; case KW_REM: goto execnextline; // Ignore line completely case KW_FOR: goto forloop; case KW_INPUT: goto input; case KW_PRINT: goto print; case KW_POKE: goto poke; 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 syntaxerror; current_line = program_end; goto execline; case KW_BYE: // Leave the basic interperater return; case KW_DOUT: goto dout; case KW_AOUT: goto aout; case KW_SLEEP: goto sleep; case KW_DEFAULT: goto assignment; default: break; } execnextline: if(current_line == (void *)0) // 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 isneg=0; unsigned char *temptxtpos; short int *var; ignore_blanks(); if(*txtpos < 'A' || *txtpos > 'Z') goto syntaxerror; var = ((short int *)variables_table)+*txtpos-'A'; txtpos++; if(!check_statement_end()) goto syntaxerror; again: temptxtpos = txtpos; if(!getln('?')) goto warmstart; // Go to where the buffer is read txtpos = program_end+sizeof(LINENUM); if(*txtpos == '-') { isneg = 1; txtpos++; } *var = 0; do { *var = *var*10 + *txtpos - '0'; txtpos++; } while(*txtpos >= '0' && *txtpos <= '9'); ignore_blanks(); if(*txtpos != NL) { printmsg(badinputmsg); goto again; } if(isneg) *var = -*var; goto run_next_statement; } forloop: { unsigned char var; short int initial, step, terminal; if(*txtpos < 'A' || *txtpos > 'Z') goto syntaxerror; var = *txtpos; txtpos++; scantable(relop_tab); if(table_index != RELOP_EQ) goto syntaxerror; expression_error = 0; initial = expression(); if(expression_error) goto invalidexpr; scantable(to_tab); if(table_index != 0) goto syntaxerror; terminal = expression(); if(expression_error) goto invalidexpr; scantable(step_tab); if(table_index == 0) { step = expression(); if(expression_error) goto invalidexpr; } else step = 1; if(!check_statement_end()) goto syntaxerror; if(!expression_error && *txtpos == NL) { struct stack_for_frame *f; if(sp + sizeof(struct stack_for_frame) < stack_limit) goto nomem; sp -= sizeof(struct stack_for_frame); f = (struct stack_for_frame *)sp; ((short int *)variables_table)[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 syntaxerror; gosub: expression_error = 0; linenum = expression(); if(expression_error) goto invalidexpr; if(!expression_error && *txtpos == NL) { struct stack_gosub_frame *f; if(sp + sizeof(struct stack_gosub_frame) < stack_limit) goto nomem; 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 syntaxerror; next: // Fnd the variable name ignore_blanks(); if(*txtpos < 'A' || *txtpos > 'Z') goto syntaxerror; txtpos++; if(!check_statement_end()) goto syntaxerror; gosub_return: // Now walk up the stack frames and find the frame we want, if present tempsp = sp; while(tempsp < memory+sizeof(memory)-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_table) + 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: goto stackstuffed; } } // Didn't find the variable we've been looking for goto syntaxerror; assignment: { short int value; short int *var; if(*txtpos < 'A' || *txtpos > 'Z') goto syntaxerror; var = (short int *)variables_table + *txtpos - 'A'; txtpos++; ignore_blanks(); if (*txtpos != '=') goto syntaxerror; txtpos++; ignore_blanks(); expression_error = 0; value = expression(); if(expression_error) goto invalidexpr; // Check that we are at the end of the statement if(!check_statement_end()) goto syntaxerror; *var = value; } goto run_next_statement; sleep: { short int value; expression_error = 0; value = expression(); if(expression_error) goto invalidexpr; delay(value); } goto run_next_statement; dout: { short int value; short int var; if(*txtpos < '0' || *txtpos > '9') goto syntaxerror; var = *txtpos - '0'; txtpos++; ignore_blanks(); if (*txtpos != '=') goto syntaxerror; txtpos++; ignore_blanks(); expression_error = 0; value = expression(); if(expression_error) goto invalidexpr; // Check that we are at the end of the statement if(!check_statement_end()) goto syntaxerror; pinMode(var, OUTPUT); if (value == 1) digitalWrite(var, HIGH); else digitalWrite(var, LOW); } goto run_next_statement; aout: { short int value; short int var; if(*txtpos < '0' || *txtpos > '9') goto syntaxerror; var = *txtpos - '0'; txtpos++; ignore_blanks(); if (*txtpos != '=') goto syntaxerror; txtpos++; ignore_blanks(); expression_error = 0; value = expression(); if(expression_error) goto invalidexpr; // Check that we are at the end of the statement if(!check_statement_end()) goto syntaxerror; pinMode(var, OUTPUT); analogWrite(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 invalidexpr; address = (unsigned char *)value; // check for a comma ignore_blanks(); if (*txtpos != ',') goto syntaxerror; txtpos++; ignore_blanks(); // Now get the value to assign expression_error = 0; value = expression(); if(expression_error) goto invalidexpr; // printf("Poke %p value %i\n",address, (unsigned char)value); // Check that we are at the end of the statement if(!check_statement_end()) goto syntaxerror; } goto run_next_statement; list: linenum = testnum(); // Retuns 0 if no line found. // Should be EOL if(txtpos[0] != NL) goto syntaxerror; // 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 syntaxerror; else { short int e; expression_error = 0; e = expression(); if(expression_error) goto invalidexpr; 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(check_statement_end()) { line_terminator(); // The end of the print statement break; } else goto syntaxerror; } goto run_next_statement; } /***********************************************************/ static void line_terminator(void) { outchar(NL); outchar(CR); } /***********************************************************/ static unsigned char breakcheck(void) { #ifdef ARDUINO if(Serial.available()) return Serial.read() == CTRLC; return 0; #else if(kbhit()) return getch() == CTRLC; else return 0; #endif } /***********************************************************/ static int inchar() { #ifdef ARDUINO while(1) { if(Serial.available()) return Serial.read(); } #else return getch(); #endif } /***********************************************************/ static void outchar(unsigned char c) { #ifdef ARDUINO Serial.write(c); #else putch(c); #endif } /***********************************************************/ #ifdef ARDUINO void setup() { Serial.begin(115200); // opens serial port, sets data rate to 115200 bps } #endif /***********************************************************/ #ifndef ARDUINO int main() { while(1) loop(); } #endif