A port of TinyBasic Plus (https://github.com/BleuLlama/TinyBasicPlus) to mbed (focus on Nucleo)

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
tomvdb
Date:
Wed Jan 21 15:32:36 2015 +0000
Commit message:
initial commit

Changed in this revision

main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r c52ead8719b3 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Jan 21 15:32:36 2015 +0000
@@ -0,0 +1,1773 @@
+/*
+
+A C implementation of Tiny Basic, with a focus on support for mbed/nucleo. This has been ported from TinyBasic Plus (https://github.com/BleuLlama/TinyBasicPlus)
+
+Nucleo Port by Tom Van den Bon 
+
+*/
+
+#define kVersion "v0.1"
+
+/*
+    v0.1: 2015/01/21
+    
+    Initial code from TinyBasic Plus ported to run on the Nucleo
+    
+    - filesystem, eeprom and sdcard not implemented yet
+*/
+
+
+
+#include "mbed.h"
+
+Serial pc(SERIAL_TX, SERIAL_RX);
+ 
+#ifndef pgm_read_byte
+#define pgm_read_byte( A ) *(A)
+#endif
+
+#ifndef boolean 
+#define boolean int
+#define true 1
+#define false 0
+#endif
+
+boolean inhibitOutput = false;
+static boolean runAfterLoad = false;
+static boolean triggerRun = false;
+
+// 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
+
+// size of our program ram
+#define kRamSize   4096 /* arbitrary */
+
+typedef short unsigned LINENUM;
+
+static unsigned char program[kRamSize];
+static const char *  sentinel = "HELLO";
+static unsigned char *txtpos,*list_line;
+static unsigned char expression_error;
+static unsigned char *tempsp;
+
+// 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,
+  '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
+#ifdef ENABLE_EEPROM
+  'E','C','H','A','I','N'+0x80,
+  'E','L','I','S','T'+0x80,
+  'E','L','O','A','D'+0x80,
+  'E','F','O','R','M','A','T'+0x80,
+  'E','S','A','V','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
+#ifdef ENABLE_EEPROM
+  KW_ECHAIN, KW_ELIST, KW_ELOAD, KW_EFORMAT, KW_ESAVE, 
+#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;
+};
+
+static unsigned char func_tab[] = {
+  '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
+
+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,
+  '!','='+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
+
+static unsigned char highlow_tab[]  = { 
+  'H','I','G','H'+0x80,
+  'H','I'+0x80,
+  'L','O','W'+0x80,
+  'L','O'+0x80,
+  0
+};
+#define HIGHLOW_HIGH    1
+#define HIGHLOW_UNKNOWN 4
+
+#define STACK_SIZE (sizeof(struct stack_for_frame)*5)
+#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[]            = "OK";
+static const unsigned char whatmsg[]          = "What? ";
+static const unsigned char howmsg[]           = "How?";
+static const unsigned char sorrymsg[]         = "Sorry!";
+static const unsigned char initmsg[]          = "TinyBasic Nucleo";
+static const unsigned char memorymsg[]        = " bytes free.";
+#ifdef ENABLE_EEPROM
+static const unsigned char eeprommsg[]        = " EEProm bytes total.";
+static const unsigned char eepromamsg[]       = " EEProm bytes available.";
+#endif
+static const unsigned char breakmsg[]         = "break!";
+static const unsigned char unimplimentedmsg[] = "Unimplemented";
+static const unsigned char backspacemsg[]     = "\b \b";
+static const unsigned char indentmsg[]        = "    ";
+static const unsigned char sderrormsg[]       = "SD card error.";
+static const unsigned char sdfilemsg[]        = "SD file error.";
+static const unsigned char dirextmsg[]        = "(dir)";
+static const unsigned char slashmsg[]         = "/";
+static const unsigned char spacemsg[]         = " ";
+
+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;
+  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)
+{
+  outchar(prompt);
+  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--;
+
+      printmsg(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++;
+  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:
+      return program[a];
+      
+    case FUNC_ABS:
+      if(a < 0) 
+        return -a;
+      return a;
+
+/*
+// fix
+#ifdef ARDUINO
+    case FUNC_AREAD:
+      pinMode( a, INPUT );
+      return analogRead( a );                        
+    case FUNC_DREAD:
+      pinMode( a, INPUT );
+      return digitalRead( a );
+#endif
+*/
+
+    case FUNC_RND:
+        return 0;
+/*    
+#ifdef ARDUINO
+      return( random( a ));
+#else
+      return( rand() % a );
+#endif
+*/
+    }
+  }
+
+  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;
+
+#ifdef ENABLE_TONES
+  noTone( kPiezoPin );
+#endif
+
+  program_start = program;
+  program_end = program_start;
+  sp = program+sizeof(program);  // Needed for printnum
+  stack_limit = program+sizeof(program)-STACK_SIZE;
+  variables_begin = stack_limit - 27*VAR_SIZE;
+
+  // memory free
+  printnum(variables_begin-program_end);
+  printmsg(memorymsg);
+#ifdef ENABLE_EEPROM
+  // eprom size
+  printnum( E2END+1 );
+  printmsg( eeprommsg );
+#endif /* ENABLE_EEPROM */
+
+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;
+  *((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);
+
+  switch(table_index)
+  {
+  case KW_DELAY:
+    {
+
+      expression_error = 0;
+      val = expression();
+      wait_ms( val );
+      goto execnextline;
+    }
+
+  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:
+    // Leave the basic interperater
+    return;
+
+  case KW_AWRITE:  // AWRITE <pin>, HIGH|LOW
+    isDigital = false;
+    goto awrite;
+  case KW_DWRITE:  // DWRITE <pin>, 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
+
+#ifdef ENABLE_EEPROM
+  case KW_EFORMAT:
+    goto eformat;
+  case KW_ESAVE:
+    goto esave;
+  case KW_ELOAD:
+    goto eload;
+  case KW_ELIST:
+    goto elist;
+  case KW_ECHAIN:
+    goto echain;
+#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;
+
+#ifdef ENABLE_EEPROM
+elist:
+  {
+    int i;
+    for( i = 0 ; i < (E2END +1) ; i++ )
+    {
+      val = EEPROM.read( i );
+
+      if( val == '\0' ) {
+        goto execnextline;
+      }
+
+      if( ((val < ' ') || (val  > '~')) && (val != NL) && (val != CR))  {
+        outchar( '?' );
+      } 
+      else {
+        outchar( val );
+      }
+    }
+  }
+  goto execnextline;
+
+eformat:
+  {
+    for( int i = 0 ; i < E2END ; i++ )
+    {
+      if( (i & 0x03f) == 0x20 ) outchar( '.' );
+      EEPROM.write( i, 0 );
+    }
+    outchar( LF );
+  }
+  goto execnextline;
+
+esave:
+  {
+    outStream = kStreamEEProm;
+    eepos = 0;
+
+    // copied from "List"
+    list_line = findline();
+    while(list_line != program_end)
+      printline();
+
+    // go back to standard output, close the file
+    outStream = kStreamSerial;
+    
+    goto warmstart;
+  }
+  
+  
+echain:
+  runAfterLoad = true;
+
+eload:
+  // clear the program
+  program_end = program_start;
+
+  // load from a file into memory
+  eepos = 0;
+  inStream = kStreamEEProm;
+  inhibitOutput = true;
+  goto warmstart;
+#endif /* ENABLE_EEPROM */
+
+input:
+  {
+    unsigned char var;
+    ignore_blanks();
+    if(*txtpos < 'A' || *txtpos > 'Z')
+      goto qwhat;
+    var = *txtpos;
+    txtpos++;
+    ignore_blanks();
+    if(*txtpos != NL && *txtpos != ':')
+      goto qwhat;
+    ((short int *)variables_begin)[var-'A'] = 99;
+
+    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);
+
+#ifdef ENABLE_EEPROM
+  {
+    // eprom size
+    printnum( E2END+1 );
+    printmsg( eeprommsg );
+    
+    // figure out the memory usage;
+    val = ' ';
+    int i;   
+    for( i=0 ; (i<(E2END+1)) && (val != '\0') ; i++ ) {
+      val = EEPROM.read( i );    
+    }
+    printnum( (E2END +1) - (i-1) );
+    
+    printmsg( eepromamsg );
+  }
+#endif /* ENABLE_EEPROM */
+  goto run_next_statement;
+
+
+  /*************************************************/
+
+awrite: // AWRITE <pin>,val
+dwrite:
+  {
+    short int pinNo;
+    short int value;
+    unsigned char *txtposBak;
+
+    // Get the pin number
+    expression_error = 0;
+    pinNo = expression();
+    if(expression_error)
+      goto qwhat;
+
+    // check for a comma
+    ignore_blanks();
+    if (*txtpos != ',')
+      goto qwhat;
+    txtpos++;
+    ignore_blanks();
+
+
+    txtposBak = txtpos; 
+    scantable(highlow_tab);
+    if(table_index != HIGHLOW_UNKNOWN)
+    {
+      if( table_index <= HIGHLOW_HIGH ) {
+        value = 1;
+      } 
+      else {
+        value = 0;
+      }
+    } 
+    else {
+
+      // and the value (numerical)
+      expression_error = 0;
+      value = expression();
+      if(expression_error)
+        goto qwhat;
+    }
+    /* fix
+    pinMode( pinNo, OUTPUT );
+    if( isDigital ) {
+      digitalWrite( pinNo, value );
+    } 
+    else {
+      analogWrite( pinNo, value );
+    }
+    */
+  }
+  goto run_next_statement;
+
+  /*************************************************/
+files:
+  // display a listing of files on the device.
+  // version 1: no support for subdirectories
+
+#ifdef ENABLE_FILEIO
+    cmd_Files();
+  goto warmstart;
+#else
+  goto unimplemented;
+#endif // ENABLE_FILEIO
+
+
+chain:
+  runAfterLoad = true;
+
+load:
+  // clear the program
+  program_end = program_start;
+
+  // load from a file into memory
+#ifdef ENABLE_FILEIO
+  {
+    unsigned char *filename;
+
+    // Work out the filename
+    expression_error = 0;
+    filename = filenameWord();
+    if(expression_error)
+      goto qwhat;
+
+/*
+#ifdef ARDUINO
+    // Arduino specific
+    if( !SD.exists( (char *)filename ))
+    {
+      printmsg( sdfilemsg );
+    } 
+    else {
+
+      fp = SD.open( (const char *)filename );
+      inStream = kStreamFile;
+      inhibitOutput = true;
+    }
+#else // ARDUINO
+    // Desktop specific
+#endif // ARDUINO
+*/
+    // this will kickstart a series of events to read in from the file.
+
+  }
+  goto warmstart;
+#else // ENABLE_FILEIO
+  goto unimplemented;
+#endif // ENABLE_FILEIO
+
+
+
+save:
+  // save from memory out to a file
+#ifdef ENABLE_FILEIO
+  {
+    unsigned char *filename;
+
+    // Work out the filename
+    expression_error = 0;
+    filename = filenameWord();
+    if(expression_error)
+      goto qwhat;
+/*
+#ifdef ARDUINO
+    // remove the old file if it exists
+    if( SD.exists( (char *)filename )) {
+      SD.remove( (char *)filename );
+    }
+
+    // open the file, switch over to file output
+    fp = SD.open( (const char *)filename, FILE_WRITE );
+    outStream = kStreamFile;
+
+    // copied from "List"
+    list_line = findline();
+    while(list_line != program_end)
+      printline();
+
+    // go back to standard output, close the file
+    outStream = kStreamSerial;
+
+    fp.close();
+#else // ARDUINO
+    // desktop
+#endif // ARDUINO
+*/
+    goto warmstart;
+  }
+#else // ENABLE_FILEIO
+  goto unimplemented;
+#endif // ENABLE_FILEIO
+
+rseed:
+  {
+    short int value;
+
+    //Get the pin number
+    expression_error = 0;
+    value = expression();
+    if(expression_error)
+      goto qwhat;
+
+
+/* fix
+#ifdef ARDUINO
+    randomSeed( value );
+#else // ARDUINO
+    srand( value );
+#endif // ARDUINO
+*/
+    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;
+}
+
+unsigned char * filenameWord(void)
+{
+  // SDL - I wasn't sure if this functionality existed above, so I figured i'd put it here
+  unsigned char * ret = txtpos;
+  expression_error = 0;
+
+  // make sure there are no quotes or spaces, search for valid characters
+  //while(*txtpos == SPACE || *txtpos == TAB || *txtpos == SQUOTE || *txtpos == DQUOTE ) txtpos++;
+  while( !isValidFnChar( *txtpos )) txtpos++;
+  ret = txtpos;
+
+  if( *ret == '\0' ) {
+    expression_error = 1;
+    return ret;
+  }
+
+  // now, find the next nonfnchar
+  txtpos++;
+  while( isValidFnChar( *txtpos )) txtpos++;
+  if( txtpos != ret ) *txtpos = '\0';
+
+  // set the error code if we've got no string
+  if( *ret == '\0' ) {
+    expression_error = 1;
+  }
+
+  return ret;
+}
+
+/***************************************************************************/
+static void line_terminator(void)
+{
+  outchar(NL);
+  outchar(CR);
+}
+/***********************************************************/
+static unsigned char breakcheck(void)
+{
+  if(pc.readable())
+    return pc.getc() == CTRLC;
+  return 0;
+}
+/***********************************************************/
+static int inchar()
+{
+  int v;
+  
+  switch( inStream ) {
+  case( kStreamFile ):
+#ifdef ENABLE_FILEIO
+    v = fp.read();
+    if( v == NL ) v=CR; // file translate
+    if( !fp.available() ) {
+      fp.close();
+      goto inchar_loadfinish;
+    }
+    return v;    
+#else
+#endif
+     break;
+  case( kStreamEEProm ):
+#ifdef ENABLE_EEPROM
+    v = EEPROM.read( eepos++ );
+    if( v == '\0' ) {
+      goto inchar_loadfinish;
+    }
+    return v;
+#endif
+     break;
+  case( kStreamSerial ):
+  default:
+    while(1)
+    {
+      if(pc.readable())
+        return pc.getc();
+    }
+  }
+  
+inchar_loadfinish:
+  inStream = kStreamSerial;
+  inhibitOutput = false;
+
+  if( runAfterLoad ) {
+    runAfterLoad = false;
+    triggerRun = true;
+  }
+  return NL; // trigger a prompt.
+  
+}
+/***********************************************************/
+static void outchar(unsigned char c)
+{
+  if( inhibitOutput ) return;
+
+  pc.putc(c);
+}
+/***********************************************************/
+int main() 
+{
+    while (1)
+    {
+        loop();
+    }
+}
+ 
\ No newline at end of file
diff -r 000000000000 -r c52ead8719b3 mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Jan 21 15:32:36 2015 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/4fc01daae5a5
\ No newline at end of file