roboforum.ru

Технический форум по робототехнике.
Текущее время: 30 ноя 2024, 03:26

Часовой пояс: UTC + 4 часа




Начать новую тему Ответить на тему  [ Сообщений: 65 ]  На страницу 1, 2, 3, 4, 5  След.
Автор Сообщение
 Заголовок сообщения: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 12:18 
Не в сети
Аватара пользователя

Зарегистрирован: 12 авг 2010, 15:10
Сообщения: 897
Откуда: Астраханская обл.
В силу обстоятельств имеется в наличии изрядный запас ORduino Nano v.1.1 на mega168a, детвору поучаю.
Задумал прикрутить к плате интерпретатор языка BASIC, чтоб было веселее самодельными роботами управлять. Однако поиск по просторам выдал варианты на использование чипов с 32К на борту, что меня не устраивало. В итоге творческой переработки найденных материалов и неспешного изучения сайтов получился данный проект. Компилил Arduino IDE 1.6.5, на последних версиях не проверял.

Код:
// Used:
//
// TinyBASIC in C v0.03
// Author : Mike Field - hamster@snap.net.nz
// http://hamsterworks.co.nz/mediawiki/index.php/Tinybasic_source
//
// TinyBASIC Plus  v0.13
//          Scott Lawrence <yorgle@gmail.com>
// https://github.com/BleuLlama/TinyBasicPlus
//
// 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. 
//

#ifndef ARDUINO
#include "stdafx.h"
#include <conio.h>
#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;

//Global variables use 710 bytes (69%) of dynamic memory, leaving 314 bytes for local variables.
/***********************************************************/
// Keyword table and constants - the last character has 0x80 added to it

PROGMEM const 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,
  0
};
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_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;
};

PROGMEM const 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

PROGMEM const static unsigned char to_tab[] = {
  'T','O'+0x80,
  0
};

PROGMEM const static unsigned char step_tab[] = {
  'S','T','E','P'+0x80,
  0
};

PROGMEM const 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

PROGMEM const 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 memory[500]; // 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;

PROGMEM static const unsigned char okmsg[] = "OK";
PROGMEM static const unsigned char badlinemsg[] = "Invalid line number";
PROGMEM static const unsigned char invalidexprmsg[] = "Invalid expression";
PROGMEM static const unsigned char syntaxmsg[] = "Syntax Error";
PROGMEM static const unsigned char badinputmsg[] = "\nBad number";
PROGMEM static const unsigned char nomemmsg[] = "Not enough memory!";
PROGMEM static const unsigned char initmsg[] = "TinyBasicEruman";
PROGMEM static const unsigned char memorymsg[] = " bytes free.";
PROGMEM static const unsigned char breakmsg[] = "break!";
PROGMEM static const unsigned char stackstuffedmsg[] = "Stack is stuffed!\n";
PROGMEM static const unsigned char unimplimentedmsg[] = "Unimplemented";
PROGMEM 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 scantable_P(PROGMEM const unsigned char *table)
{
  int i = 0;
  ignore_blanks();
  table_index = 0;
  while(1)
  {
    // Run out of table entries?
    if((unsigned char)pgm_read_word(&(table[0])) == 0)
            return;

    // Do we match this character?
    if(txtpos[i] == (unsigned char)pgm_read_word(&(table[0])))
    {
      i++;
      table++;
    }
    else
    {
      // do we match the last character of keywork (with 0x80 added)? If so, return
      if(txtpos[i]+0x80 == (unsigned char)pgm_read_word(&(table[0])))
      {
        txtpos += i+1;  // Advance the pointer to following the keyword
        ignore_blanks();
        return;
      }

      // Forward to the end of this keyword
      while(((unsigned char)pgm_read_word(&(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;
      //Serial.print ((unsigned char)pgm_read_word(&(table[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 void printmsgNoNL_P(PROGMEM const unsigned char *msg)
{
  while((unsigned char)pgm_read_word(&(*msg)))
  {
    outchar((unsigned char)pgm_read_word(&(*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();
}

static void printmsg_P(const unsigned char *msg)
{
  printmsgNoNL_P(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_P(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:
        a =  memory[a];
        goto success;
      case FUNC_ABS:
        if(a < 0)
          a = -a;
        goto success;
      case FUNC_AREAD:
        pinMode( a, INPUT );
        a = analogRead( a );                       
        goto success;
      case FUNC_DREAD:
        pinMode( a, INPUT );
        a = digitalRead( a );
        goto success;
      case FUNC_RND:
        a = random( 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_P(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;
  boolean isDigital;
  boolean alsoWait = false;
  int val;
 
  variables_table = memory;
  program_start = memory + 27*VAR_SIZE;
  program_end = program_start;
  sp = memory+sizeof(memory);  // Needed for printnum
  printmsg_P(initmsg);
  printnum(sp-program_end);
  printmsg_P(memorymsg);

warmstart:
  // this signifies that it is running in 'direct' mode.
  current_line = 0;
  sp = memory+sizeof(memory); 
  printmsg_P(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_P(unimplimentedmsg);
  goto prompt;

badline: 
  printmsg_P(badlinemsg);
  goto prompt;
invalidexpr:
  printmsg_P(invalidexprmsg);
  goto prompt;
syntaxerror:
  printmsg_P(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_P(stackstuffedmsg);
  goto warmstart;
nomem: 
  printmsg_P(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_P(breakmsg);
          goto warmstart;
        }

  scantable_P(keywords);
  ignore_blanks();

  switch(table_index)
  {
    case KW_DELAY:
    {
      expression_error = 0;
      //val = expression();
      delay( expression() );
      goto execnextline;
    }
    case KW_LIST:
      goto list;
    case KW_LOAD:
      goto unimplemented; /////////////////
    case KW_MEM:
    goto mem;
    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: 
    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_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_AWRITE:  // AWRITE <pin>, HIGH|LOW
      isDigital = false;
      goto awrite;
    case KW_DWRITE:  // DWRITE <pin>, HIGH|LOW
      isDigital = true;
      goto dwrite;

     
    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_P(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_P(relop_tab);
    if(table_index != RELOP_EQ)
      goto syntaxerror;

    expression_error = 0;
    initial = expression();
    if(expression_error)
      goto invalidexpr;
 
    scantable_P(to_tab);
    if(table_index != 0)
      goto syntaxerror;
 
    terminal = expression();
    if(expression_error)
      goto invalidexpr;
 
    scantable_P(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;
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;

mem:
  // memory free
  printnum(sp-program_end);
  printmsg_P(memorymsg);
  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 invalidexpr;

    // check for a comma
    ignore_blanks();
    if (*txtpos != ',')
      goto invalidexpr;
    txtpos++;
    ignore_blanks();


    txtposBak = txtpos;
    scantable_P(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 invalidexpr;
    }
    pinMode( pinNo, OUTPUT );
    if( isDigital ) {
      digitalWrite( pinNo, value );
    }
    else {
      analogWrite( pinNo, value );
    }
  }
  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(9600); // opens serial port, sets data rate to 9600 bps
  //Serial.println("TinyBasic");
}
#endif

#ifndef ARDUINO
//***********************************************************/
int main()
{
while(1)
  loop();
}
/***********************************************************/
#endif


_________________
Соединяй и здравствуй.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 12:27 
Не в сети

Зарегистрирован: 23 июл 2012, 15:24
Сообщения: 1480
А как ножками дрыгать? И ещё:
"...нам бы схемку, аль чертёж..." (с) :D


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 16:13 
Не в сети
Аватара пользователя

Зарегистрирован: 12 авг 2010, 15:10
Сообщения: 897
Откуда: Астраханская обл.
Проект занятен тем, что позволяет дописать любые команды по вкусу. Достаточно посмотреть, как реализовано подключение DELAY N, и RND(N).

Данная версия умеет:
работать с переменными (A-Z),
использует команды LIST,NEW,RUN,NEXT,LET,IF,GOTO,GOSUB,RETURN,REM,FOR,INPUT,PRINT,POKE,STOP,
BYE,MEM,?,\,AWRITE,DWRITE,DELAY,END;
часть команд зарезервирована на будущее (FILES,SAVE,LOAD);
имеются функции PEEK(N), ABS(N), AREAD(N), DREAD(N), RND(N).

Работает довольно стабильно, советы-комментарии приветствуются.
Авторы упомянуты в шапке.

Добавлено спустя 5 минут 21 секунду:
esisl писал(а):
А как ножками дрыгать? И ещё:
"...нам бы схемку, аль чертёж..." (с) :D


Специально не прикручивал ничего. Беру голую плату, и балдею.

Например, оценить работу аналогового входа можно так:

Код:
10 PRINT AREAD(0)
20 GOTO 10
run


классический BLINK
Код:
10 DWRITE 13,HIGH
20 DELAY 500
30 DWRITE 13,LOW
40 DELAY 500
50 GOTO 10


Добавлено спустя 4 минуты 1 секунду:
Объем программы 446 символов (оперативка), на "поиграться" хватает. В планах прикрутить EEPROM 24С16, и хранить листинг там.

Добавлено спустя 22 минуты 18 секунд:
Для любителей поиграть :)
Код:
10 ? "Загадай число от 0 до 100"
20 INPUT A
30 LET B = RND(100) : ? "Я загадала ", B
40 IF A>B GOTO 60
50 ? "Я победила!"
60 STOP


Добавлено спустя 46 секунд:
программа занимает в памяти 130 байт

Добавлено спустя 19 минут 7 секунд:
Еще один вариант мигалки
Код:
5 LET A = 1 
10 DWRITE 13, A
20 DELAY 100
30 A=ABS(A-1)
40 GOTO 10


Добавлено спустя 2 часа 45 минут 3 секунды:
Как записать во все цифровые порты HIGH ? Программа 44 байт
Код:
10 FOR I = 1 TO 13
20 DWRITE I, 1
30 NEXT I


А если нужно записать что-то на выбор?
Код:
10 FOR I = 1 TO 13
20 DWRITE I, A
30 NEXT I

let a = 1
run
a = 0
run


Добавлено спустя 2 минуты 57 секунд:
Или можно использовать INPUT
Код:
5 INPUT A
10 FOR I = 1 TO 13
20 DWRITE I, A : NEXT I

_________________
Соединяй и здравствуй.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 20:13 
Не в сети
Аватара пользователя

Зарегистрирован: 24 окт 2011, 23:32
Сообщения: 803
Откуда: Севастополь
прог. языки: асм
По-моему, отлично.
Ардуина с Бэйсиком на борту - именно то, что нужно детям на начальном этапе.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 20:32 
Не в сети

Зарегистрирован: 23 июл 2012, 15:24
Сообщения: 1480
Дети сейчас очень привыкли ко всяким планшетам :(


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 23:38 
Не в сети
Аватара пользователя

Зарегистрирован: 12 авг 2010, 15:10
Сообщения: 897
Откуда: Астраханская обл.
esisl писал(а):
Дети сейчас очень привыкли ко всяким планшетам :(

У планшета колёсиков нету. :)
Кстати, с планшета можно ардуину программировать. По BT, например.

_________________
Соединяй и здравствуй.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 23:41 
Не в сети

Зарегистрирован: 23 июл 2012, 15:24
Сообщения: 1480
Можно. Но это будет уже недостаточно лампово.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 06 июл 2016, 23:56 
Не в сети
Аватара пользователя

Зарегистрирован: 13 янв 2011, 15:25
Сообщения: 8033
Откуда: Санкт-Петербург
хе-хе, прикольно, работает :)
Я так понял, прога хранится в текстовом виде в озу. А если после ввода одной строки, интерпретатор будет преобразовывать строку программы в байт-код? Довольно просто сделать. Бомба будет :)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 07 июл 2016, 07:49 
Не в сети
Аватара пользователя

Зарегистрирован: 12 авг 2010, 15:10
Сообщения: 897
Откуда: Астраханская обл.
Думал, но пока прицелился на перенос листинга в EEPROM. С байт-кодом, как мне кажется, выигрыш памяти будет не великий. Для примера, берем код
Код:
10 ? "Загадай число от 0 до 100"
20 INPUT A
30 LET B = RND(100) : ? "Я загадала ", B
40 IF A>B GOTO 60
50 ? "Я победила!"
60 STOP

Он занимает 130 байт.
При оптимизации (сокращении операторов до байта, и еще по мелочи) получаем около 100 байт. Пусть даже сможем 50% сократить, при объеме памяти 500 байт на программу наша экономия не позволит создать что-то разветвленное.

_________________
Соединяй и здравствуй.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 07 июл 2016, 11:03 
Не в сети

Зарегистрирован: 23 июл 2012, 15:24
Сообщения: 1480
Не... чистый ардуиноБасик - это только поиграться.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 07 июл 2016, 15:39 
Не в сети
Аватара пользователя

Зарегистрирован: 13 янв 2011, 15:25
Сообщения: 8033
Откуда: Санкт-Петербург
Eruman писал(а):
Думал, но пока прицелился на перенос листинга в EEPROM. С байт-кодом, как мне кажется, выигрыш памяти будет не великий.

Да, согласен, в этом примере мало экономии, слишком много там текста для print.
А почему в EEPROM? Может лучше в основную память? 16к-7к= целых 9к под код без переменных. Не думаю что детки ушатают память переписыванием прог. А если ушатают, то это уже будет программист :) Одна ордуина = один программист :)
Правда если еще будут доработки интерпретатора, то может и не остаться свободной памяти...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 07 июл 2016, 17:24 
Не в сети

Зарегистрирован: 03 янв 2012, 12:55
Сообщения: 3298
Откуда: Москва
прог. языки: VB6, BASCOM, ASM...
Пользуйтесь Bascom-ом, чисто бэйсик.)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 07 июл 2016, 20:55 
Не в сети
Аватара пользователя

Зарегистрирован: 12 авг 2010, 15:10
Сообщения: 897
Откуда: Астраханская обл.
Madf писал(а):
Пользуйтесь Bascom-ом, чисто бэйсик.)


На борту ардуинки крутится интерпретатор. Позволяет оперативно ловить ошибки, и добиваться работоспособности кода. При пользовании Баскомом придется постоянно перепрошивать чип. Это не всегда удобно, особенно если занятие длится 45 мин.

Добавлено спустя 1 минуту 37 секунд:
Dmitry__ писал(а):
Правда если еще будут доработки интерпретатора, то может и не остаться свободной памяти...

Нет предела совершенству :)
Уже 11к потратил, а еще идей полно не реализованных.

_________________
Соединяй и здравствуй.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 07 июл 2016, 22:51 
Не в сети
Аватара пользователя

Зарегистрирован: 13 янв 2011, 15:25
Сообщения: 8033
Откуда: Санкт-Петербург
Eruman писал(а):
Уже 11к потратил, а еще идей полно не реализованных.

Ну круто. Очень наглядный код, прям для начинающих компилятор/ос писателей, если так и дальше пойдет, то можно и на 328-е перейти. Можно еще и пайку преподавать - проапгрейдь свою дуину :)


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Basic и Arduino
СообщениеДобавлено: 08 июл 2016, 09:35 
Не в сети
Аватара пользователя

Зарегистрирован: 12 авг 2010, 15:10
Сообщения: 897
Откуда: Астраханская обл.
Главная идея - чтобы было недорого и легко программировалось. Дуино - недороги, но заморачиваться с прошивкой, поиском ошибок и изучением вайринга не каждый преподаватель решится.

Развитие программки сделал с дополнительной памятью.
Код:
// Used:
//
// TinyBASIC in C v0.03
// Author : Mike Field - hamster@snap.net.nz
// http://hamsterworks.co.nz/mediawiki/index.php/Tinybasic_source
//
// TinyBASIC Plus  v0.13
//          Scott Lawrence <yorgle@gmail.com>
// https://github.com/BleuLlama/TinyBasicPlus
//
// 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. 
//
// +
// https://github.com/blins/c24LC16B
#include <Wire.h>
#include <c24LC16B.h>
c24LC16B memory = c24LC16B();
int eepos = 0;
int eemax = 2047;

#ifndef ARDUINO
#include "stdafx.h"
#include <conio.h>
#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 DEL '\177'
#define SPACE   ' '
#define SQUOTE  '\''
#define DQUOTE  '\"'
#define CTRLC 0x03
#define CTRLH 0x08
#define CTRLS 0x13
#define CTRLX 0x18

typedef short unsigned LINENUM;

//Global variables use 710 bytes (69%) of dynamic memory, leaving 314 bytes for local variables.
/***********************************************************/
// Keyword table and constants - the last character has 0x80 added to it

PROGMEM const 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,
  'T','O','N','E','W'+0x80,
  'T','O','N','E'+0x80,
  'N','O','T','O','N','E'+0x80,
  '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,
  0
};
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_TONEW, KW_TONE, KW_NOTONE,
  KW_ECHAIN, KW_ELIST, KW_ELOAD, KW_EFORMAT, KW_ESAVE,
  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;
};

PROGMEM const 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

PROGMEM const static unsigned char to_tab[] = {
  'T','O'+0x80,
  0
};

PROGMEM const static unsigned char step_tab[] = {
  'S','T','E','P'+0x80,
  0
};

PROGMEM const 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

PROGMEM const 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 bmemory[312]; // 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;

PROGMEM static const unsigned char okmsg[] = "OK";
PROGMEM static const unsigned char badlinemsg[] = "Invalid line number";
PROGMEM static const unsigned char invalidexprmsg[] = "Invalid expression";
PROGMEM static const unsigned char syntaxmsg[] = "Syntax Error";
PROGMEM static const unsigned char badinputmsg[] = "\nBad number";
PROGMEM static const unsigned char nomemmsg[] = "Not enough memory!";
PROGMEM static const unsigned char initmsg[] = "TinyBasicEruman V0.02.";
PROGMEM static const unsigned char memorymsg[] = " bytes free.";
PROGMEM static const unsigned char breakmsg[] = "break!";
PROGMEM static const unsigned char stackstuffedmsg[] = "Stack is stuffed!\n";
PROGMEM static const unsigned char unimplimentedmsg[] = "Unimplemented";
PROGMEM static const unsigned char backspacemsg[] = "\b \b";

PROGMEM static const unsigned char eeprommsg[]         = " EEProm bytes total.";
PROGMEM static const unsigned char eepromamsg[]        = " EEProm bytes available.";

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 scantable_P(PROGMEM const unsigned char *table)
{
  int i = 0;
  ignore_blanks();
  table_index = 0;
  while(1)
  {
    // Run out of table entries?
    if((unsigned char)pgm_read_word(&(table[0])) == 0)
            return;

    // Do we match this character?
    if(txtpos[i] == (unsigned char)pgm_read_word(&(table[0])))
    {
      i++;
      table++;
    }
    else
    {
      // do we match the last character of keywork (with 0x80 added)? If so, return
      if(txtpos[i]+0x80 == (unsigned char)pgm_read_word(&(table[0])))
      {
        txtpos += i+1;  // Advance the pointer to following the keyword
        ignore_blanks();
        return;
      }

      // Forward to the end of this keyword
      while(((unsigned char)pgm_read_word(&(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;
      //Serial.print ((unsigned char)pgm_read_word(&(table[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 void printmsgNoNL_P(PROGMEM const unsigned char *msg)
{
  while((unsigned char)pgm_read_word(&(*msg)))
  {
    outchar((unsigned char)pgm_read_word(&(*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();
}

static void printmsg_P(const unsigned char *msg)
{
  printmsgNoNL_P(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_P(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:
        a =  bmemory[a];
        goto success;
      case FUNC_ABS:
        if(a < 0)
          a = -a;
        goto success;
      case FUNC_AREAD:
        pinMode( a, INPUT );
        a = analogRead( a );                       
        goto success;
      case FUNC_DREAD:
        pinMode( a, INPUT );
        a = digitalRead( a );
        goto success;
      case FUNC_RND:
        a = random( 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_P(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;
  boolean isDigital;
  boolean alsoWait = false;
  int val;
 
  variables_table = bmemory;
  program_start = bmemory + 27*VAR_SIZE;
  program_end = program_start;
  sp = bmemory+sizeof(bmemory);  // Needed for printnum
  printmsg_P(initmsg);
  printnum(sp-program_end);
  printmsg_P(memorymsg);

warmstart:
  // this signifies that it is running in 'direct' mode.
  current_line = 0;
  sp = bmemory+sizeof(bmemory); 
  printmsg_P(okmsg);

prompt:
  if( triggerRun ){
    triggerRun = false;
    current_line = program_start;
    goto execline;
  }
 
  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_P(unimplimentedmsg);
  goto prompt;

badline: 
  printmsg_P(badlinemsg);
  goto prompt;
invalidexpr:
  printmsg_P(invalidexprmsg);
  goto prompt;
syntaxerror:
  printmsg_P(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_P(stackstuffedmsg);
  goto warmstart;
nomem: 
  printmsg_P(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_P(breakmsg);
          goto warmstart;
        }

  scantable_P(keywords);
  ignore_blanks();

  switch(table_index)
  {
    case KW_DELAY:
    {
      expression_error = 0;
      //val = expression();
      delay( expression() );
      goto execnextline;
    }
    case KW_LIST:
      goto list;
    case KW_LOAD:
      goto unimplemented; /////////////////
    case KW_MEM:
    goto mem;
    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 test_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 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: 
    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_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_AWRITE:  // AWRITE <pin>, HIGH|LOW
      isDigital = false;
      goto awrite;
    case KW_DWRITE:  // DWRITE <pin>, HIGH|LOW
      isDigital = true;
      goto dwrite;

/*    case KW_TONEW:
      alsoWait = true;
    case KW_TONE:
      goto tonegen;
    case KW_NOTONE:
      goto tonestop;
*/
    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;
    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;

test_save:
  {
  //memory.write(0, 1, 1);
  char data[] = "10 LIST : REM **" ;
  memory.write(0, (byte *)data, sizeof(data));
  }
  goto execnextline;

elist:
  {
    int i;
    byte expr = expression();
    //Serial.print(eepos);
    //Serial.print(" bytes. EEPROM LIST:" )
    Serial.print("EEPROM LIST:" );
    Serial.println( expr );
    for( i = 0 ; i < (eemax +1) ; i++ )
    {
      val = (char)memory.read_byte(i+expr*256);
      //val = memory.read_byte(i);
      if( val == '\0' ) {
        goto execnextline;
      }
     
      if( ((val < ' ') || (val  > '~')) && (val != NL) && (val != CR) && (val >= 0))  {
        outchar( '?' );
      }
      else {
        outchar( val );
      }
    }
  }
  //Serial.print(eepos);
  //Serial.println(" bytes EEPOS.");
  goto execnextline;

eformat:
  {
    static unsigned char data[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } ;
    for( int i = 0 ; i < eemax/16 ; i++ )
    {
      if( (i & 0x03f) == 0x20 ) { outchar( '.' ); }
      //EEPROM.write( i, 0 );
      //memory.write(i, 255);
      memory.write(i*16, (byte *)data, sizeof(data));
    }
    outchar( LF );
  }
  goto execnextline;



esave:
  {
    outStream = kStreamEEProm;
    //eepos = 0;
    eepos = expression()*256;
    // copied from "List"
    list_line = findline();

    while(list_line != program_end) {
      printline();
    }
    memory.write(eepos, 0);
    // 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;
  eepos = expression()*256;
  inStream = kStreamEEProm;
  inhibitOutput = true;
  goto warmstart;

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_P(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_P(relop_tab);
    if(table_index != RELOP_EQ)
      goto syntaxerror;

    expression_error = 0;
    initial = expression();
    if(expression_error)
      goto invalidexpr;
 
    scantable_P(to_tab);
    if(table_index != 0)
      goto syntaxerror;
 
    terminal = expression();
    if(expression_error)
      goto invalidexpr;
 
    scantable_P(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 < bmemory+sizeof(bmemory)-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())
    //if(*txtpos != NL && *txtpos != ':')
      goto syntaxerror;
    *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;

mem:
  // memory free
  printnum(sp-program_end);
  printmsg_P(memorymsg);

    // eprom size
    printnum( eemax+1 );
    printmsg_P( eeprommsg );
   
    // figure out the memory usage;
    val = ' ';
    int i;   
    for( i=0 ; (i<(eemax+1)) && (val != '\0') ; i++ ) {
      val = (char)memory.read_byte(i); // access the first address from the memory   
    }
    printnum( (eemax +1) - (i-1) );
    printmsg_P( eepromamsg );

   
    for( int ii=0 ; (ii<16) ; ii++ ) {
      Serial.print(" 00");
      if (ii<1) Serial.print("0");
      Serial.print(ii*16, HEX);
      for( int i=0 ; (i<(16)) ; i++ ) {
        val = (unsigned char)memory.read_byte(i+ii*16); // access the first address from the memory   
        Serial.print(" ");
        if (val<16) Serial.print("0");
        Serial.print(val, HEX);
      }
      Serial.print("   ");
      for( int i=0 ; (i<(16)) ; i++ ) {
        val = (char)memory.read_byte(i+ii*16); // access the first address from the memory   
        if ((val > 31) || (val < 0) )  Serial.print((char)val);
        else Serial.print(".");
      }
      Serial.println("");
    }

  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 invalidexpr;

    // check for a comma
    ignore_blanks();
    if (*txtpos != ',')
      goto invalidexpr;
    txtpos++;
    ignore_blanks();

    txtposBak = txtpos;
    scantable_P(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 invalidexpr;
    }
    pinMode( pinNo, OUTPUT );
    if( isDigital ) {
      digitalWrite( pinNo, value );
    }
    else {
      analogWrite( pinNo, value );
    }
  }
  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()
{
  int v;
#ifdef ARDUINO
  switch( inStream ) {
  case( kStreamFile ):
    break;
  case( kStreamEEProm ):
    v = memory.read_byte( eepos++ );
    if( v == '\0' ) {
      goto inchar_loadfinish;
    }
    return v;
    break;
  case( kStreamSerial ):
  default:
    while(1)
    {
      if(Serial.available())
        return Serial.read();
    }
  }
inchar_loadfinish:
  inStream = kStreamSerial;
  inhibitOutput = false;

  if( runAfterLoad ) {
    runAfterLoad = false;
    triggerRun = true;
  }
  return NL; // trigger a prompt.
#else
  return getch();
#endif
}

/***********************************************************/
static void outchar(unsigned char c)
{
  if( inhibitOutput ) return;
#ifdef ARDUINO
  if( outStream == kStreamEEProm ) {
      //EEPROM.write( eepos++, c );
      char aout[] = { 0 };
      aout[0] = (char)c;
      memory.write(eepos, (byte *)aout, sizeof(aout));
        eepos++;
    }
    else
      Serial.write(c);
#else
  putch(c);
#endif
}

#ifdef ARDUINO
/***********************************************************/
void setup()
{
  Serial.begin(9600);
  int val = memory.read_byte(0);
  if( val >= '0' && val <= '9' ) {
    program_end = program_start;
    inStream = kStreamEEProm;
    eepos = 0;
    inhibitOutput = true;
    runAfterLoad = true;
  }
}
#endif

#ifndef ARDUINO
//***********************************************************/
int main()
{
while(1)
  loop();
}
/***********************************************************/
#endif


После подключения микросхемы Переписываемого Постоянного Запоминающего Устройства (ППЗУ) появилась возможность сохранять наши нетленки на десятки лет в памяти устройства. Сейчас память делится на восемь страниц по 256 байт, в которых могут храниться соответственно восемь кусков кода. При чтении листинга программа не изменяет переменные, поэтому можно запускать листинги по очереди.

Если у вас в самой первой ячейке ППЗУ находится номер строки листинга (символ от 0 до 9), то она автоматически запускается на исполнение. Поэтому не рекомендую использовать в этом листинге команду "ECHAIN" или "ECHAIN 0", ардуинка зациклится и придется перепрошивать. Временно команда "SAVE" заполняет первые ячейки ППЗУ безобидным кодом для теста.

Дополнил работу MEM. Теперь команда выводит дамп начальных 256 байт ППЗУ.
Добавлены новые команды:
ECHAIN запускает на выполнение нулевой листинг из ППЗУ (у меня с 2Кб их восемь), с параметром - соответствующий номер (0-7). "ECHAIN 0" аналог "ECHAIN".
ELIST - выводит на печать нулевой листинг из ППЗУ. Можно использовать параметры (0-7)
ELOAD - загружает из ППЗУ нулевой листинг. С параметром - соответствующий номеру.
EFORMAT - очищает все ППЗУ.
ESAVE - сохраняет текущий листинг в ППЗУ. С параметром тоже.

Добавлено спустя 32 минуты 2 секунды:
Если применяете отличную от 24LC16 микросхему, то придется поправить переменные, отвечающие за максимальный размер ППЗУ, и прописать соответствующую библиотеку.

Немного позже расскажу, как подключить дополнительные команды на примере команды TONE.


Вложения:
Комментарий к файлу: Подключение РПЗУ (EEPROM). Подключение аналогично 24LC16,24LC64,24LC256,24LC512.
Снимок.PNG
Снимок.PNG [ 88.4 КиБ | Просмотров: 4002 ]

_________________
Соединяй и здравствуй.
Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 65 ]  На страницу 1, 2, 3, 4, 5  След.

Часовой пояс: UTC + 4 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB
phpBB SEO