roboforum.ru

Технический форум по робототехнике.

Basic и Arduino

Как собрать и запрограммировать робота на Arduino(Freeduino, Roboduino, Seeduino ...). Используем Wiring и Processing.

Re: Basic и Arduino

Сообщение elmot » 09 июл 2016, 21:42

Eruman писал(а):Согласен. Нужно немного времени, с коллегами обсудим, и я отпишусь. Скажем через неделю. Допустимо?

Можно не торопиться, через неделю я буду в отпуске. И да, я живу не в РФ, соответственно пересылка плат тоже займет время. Я не хочу их слать почтой, т.е. в целом смогу переслать, когда я доеду до рф.
Аватара пользователя
elmot
 
Сообщения: 5691
Зарегистрирован: 10 ноя 2011, 12:02
Откуда: Turku, Finland
Skype: elmot73
прог. языки: Java и все-все=все
ФИО: Илья

Re: Basic и Arduino

Сообщение Eruman » 09 июл 2016, 23:18

ALS писал(а):К устройствам вывода можно добавить светодиодные матрицы 8х8, видел на али сдвоенные с драйвером на борту.
Добавятся игружки с несложной графикой, демки всякие...


Хорошая мысль. Надо попробовать. Жаль, Китай далеко, ждать придется.

Добавлено спустя 6 минут 50 секунд:
elmot писал(а):Можно не торопиться
ОК, будем обсуждать медленно. :)
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение Eruman » 11 июл 2016, 09:52

Приведу пример добавления новых команд интерпретатора:"TONEW частота, длительность", "TONE частота, длительность" и "NOTONE". Эти команды присутствуют в списке команд второго примера. Если используете первый пример, то нужно их дописать в keywords[], ну и соответственно уникальный id добавить пониже. Тут мы развиваем второй пример.

Раскомментируем следующие строки в коде:
Код: Выделить всёРазвернуть
    case KW_TONEW:
      alsoWait = true; // Признак доп.паузы
    case KW_TONE:
      goto tonegen;
    case KW_NOTONE:
      goto tonestop;


В шапке программы определим пин, к которому подключим излучатель
Код: Выделить всёРазвернуть
#define kPiezoPin 5


В теле программы добавим обработчики команд
Код: Выделить всёРазвернуть
tonestop:
  noTone( kPiezoPin );
  goto run_next_statement;

tonegen:
  {
    // TONE частота, длительность
    // если что-то равно 0, то отключаем
    short int freq;
    short int duration;

    //Получаем частоту
    expression_error = 0;
    freq = expression();
    if(expression_error)
       goto syntaxerror;

    ignore_blanks();
    if (*txtpos != ',')
       goto syntaxerror;
    txtpos++;
    ignore_blanks();

    //Получаем длительность
    expression_error = 0;
    duration = expression();
    if(expression_error)
       goto syntaxerror;

    if( freq == 0 || duration == 0 )
      goto tonestop;

    tone( kPiezoPin, freq, duration );
    if( alsoWait ) {
      delay( duration );
      alsoWait = false;
    }
    goto run_next_statement;
  }


Добавлено спустя 6 минут 36 секунд:
В новой версии также добавлена функция возврата последнего символа из терминала "KEY(n)", где n=0...2. Параметр добавлен для отбрасывания крайнего байта/двух. Например, если ваша терминалка добавляет 0х0D, можно использовать KEY(1).

Код: Выделить всёРазвернуть
// 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;

#define kPiezoPin 5

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

char keypress, oldkeypress, oldoldkeypress;

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

/***********************************************************/
// 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,
  'K','E','Y'+0x80,
  0
};
#define FUNC_PEEK    0
#define FUNC_ABS   1
#define FUNC_AREAD   2
#define FUNC_DREAD   3
#define FUNC_RND     4
#define FUNC_KEY   5
#define FUNC_UNKNOWN 6

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();
    oldoldkeypress = oldkeypress;
    oldkeypress = keypress;
    keypress = c;
    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;
      case FUNC_KEY:
        expression_error = 0;
        if(Serial.available()) {
          char c = Serial.read();
          oldoldkeypress = oldkeypress;
          oldkeypress = keypress;
          keypress = c;
        }
        if (a==0) a = keypress;
        if (a==1) a = oldkeypress;
        if (a==2) a = oldoldkeypress;
        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;

rseed:
  {
    short int value;
    //Get the pin number
    expression_error = 0;
    value = expression();
    if(expression_error)
       goto syntaxerror;
    randomSeed( value );
    goto run_next_statement;
  }

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

    ignore_blanks();
    if (*txtpos != ',')
       goto syntaxerror;
    txtpos++;
    ignore_blanks();


    //Get the duration
    expression_error = 0;
    duration = expression();
    if(expression_error)
       goto syntaxerror;

    if( freq == 0 || duration == 0 )
      goto tonestop;

    tone( kPiezoPin, freq, duration );
    if( alsoWait ) {
      delay( duration );
      alsoWait = false;
    }
    goto run_next_statement;
  }

elist:
  {
    int i;
    byte expr = expression();
    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 );
      }
    }
  }
  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( '.' ); }
      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);
        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);
        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)
{
  if(Serial.available())
    return Serial.read() == CTRLC;
  return 0;
}
/***********************************************************/
static int inchar()
{
  int v;
  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.
}

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

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


Добавлено спустя 24 минуты 2 секунды:
Тем, кому необходима SD-карта, рекомендую изучить исходник TinyBASIC Plus. У нас пока её использование не предполагается.
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение Eruman » 15 июл 2016, 10:12

Последняя сборка получила ряд новых операторов, команд и функций.

Помимо стандартных операторов "+","-","/" и "*" добавились "%", "&", "|".

Появились функции:
EPEEK(Адр) - читаем значение из ППЗУ по адресу Адр
FRE() - получаем количество свободной оперативной памяти
SYS(1) - адрес текущей ячейки для команды SEQ, DATA и READ. см.ниже
SYS(2) - кратность значений секвенсора SEQ см.ниже
SYS(3) - кратность пауз секвенсора SEQ, см.ниже
SYS(4) - число миллисекунд от старта до минуты
SYS(5) - число секунд от старта
SYS(6) - число минут от старта
SYS(7) - номер данной сборки. при компиляции правим "#define version ..."

Новые команды:
EPOKE Адр,Число - Запись байта по адресу в ППЗУ
CPRINT Код - Вывод символа по коду
EPRINT Адр - Вывод цепочки символов по адресу из ППЗУ
READ Перем1 <,Перем2...> - Чтение значений переменных из ППЗУ, адрес+2*N
RESTORE Адр - Установка значения адреса для SEQ, DATA и READ
DATA Число1 <,Число2...> - Запись значений переменных в ПЗУ, адрес+2*N
SEQ Перем1 <,Перем2...> - Чтение байта из ППЗУ с паузой, адрес+2*N
MSEQ Кратность1 <, Кратность2> - Установка кратности секвенсора: для значений <и пауз>. Если Кратность2 не определено, то Кратность1 применяется к обоим множителям.

Думаю, наиболее интересным выглядит применение секвенсора.
Мы его применяли для генерации мелодий и азбуки морзе. Также эта команда применима для управления ботом, когда значения из ППЗУ напрямую указывают на номер подпрограммы, например так: "SEQ A : GOSUB A". Причем, если в переменной А указана несуществующая строка, то вызовется следующая по порядку.

Необходимо учитывать, что в режиме командной строки значение адреса для SEQ/DATA автоматически выставляется равным 256. Кратности секвенсора становятся 1 и 1. Т.о., если в командной строке ввести "DATA 25, 10, 88, 5", то эти значения запишутся начиная с адреса 256. Их можно увидеть командой "mem 1". Повторная команда, например "DATA 0,0,0,0" заменит ранее записанные значения нулями.

Секвенсор в принципе можно использовать при управлении сервомоторами, подключая паттерны движений, однако пока данный функционал не реализован.

Программы можно немного сократить, если повсеместно убирать пробелы.
Строки "? sys(4)" и "?sys(4)" для интерпретатора равнозначны.
Вложения
TinyBasicEruman02.ino
для версии Arduino IDE 1.6.5
(43.82 КиБ) Скачиваний: 1632
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение Madf » 15 июл 2016, 12:34

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

Re: Basic и Arduino

Сообщение Eruman » 15 июл 2016, 14:22

Код открытый, можно вставить любой словарь. Вы про CPRINT? Только у него логика работы изменена, остальные команды вполне узнаваемые. Секвенсора я не помню в бейсике.
Мне показалось, что так удобнее. Предлагайте, как на ваш взгляд лучше будет. Обсудим.

Добавлено спустя 3 минуты 55 секунд:
Или AND OR MOD нужны? Так значками короче, и более распространено. Вы, надеюсь, помните, что для программы в ОЗУ не так много места, поэтому приходится экономить на байтах. Потому и такое отношение к синтаксису: все что можно разместить в ППЗУ, пишем туда. В т.ч. текстовые сообщения, константы, массивы для обработки. Вспоминаем Синклер, там тоже от канона отходили.

Из WIKI про Бейсик 70-х: "его основной задачей было предоставить студентам-непрограммистам возможность после минимального обучения самостоятельно писать простейшие программы для собственных нужд, чтобы не отвлекать на эту работу специалистов. Этой задаче язык вполне соответствует." https://ru.wikipedia.org/wiki/Бейсик

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

Re: Basic и Arduino

Сообщение Madf » 15 июл 2016, 15:43

Чтобы экономить место, надо делать систему как в ZX-spectrum, там одна команда бэйсика в памяти занимала 1 байт, а не целое текстовое слово.
Да в бэйсике есть "^" и "mod"...всё это нужно, если делаете математику.
EPOKE - должно быть без "Е".
PRINT - должна быть классическая реализация в рамках железа/функционала.
Если у вас есть принципиально новые возможности, тогда можете придумывать новые операторы.
Суть Бэйсика, научить детей: операторам, функциям, синтаксису - так сказать озы. Остальные доп. вещи - это уже особенность самого бэйсика (его специфика), назовите его например "Atmega Basic" или "Duni Basic"...чтобы было оригинальное название (не встречающееся и узнаваемое).
Madf
 
Сообщения: 3298
Зарегистрирован: 03 янв 2012, 12:55
Откуда: Москва
прог. языки: VB6, BASCOM, ASM...

Re: Basic и Arduino

Сообщение Eruman » 15 июл 2016, 16:41

Madf писал(а):Чтобы экономить место, надо делать систему как в ZX-spectrum, там одна команда бэйсика в памяти занимала 1 байт, а не целое текстовое слово.
Уже говорил, пока это не удобно. В данной версии можно сделать вариант типа шитого кода. Покажу позже, как реализуется.
Madf писал(а):Да в бэйсике есть "^" и "mod"...всё это нужно, если делаете математику.

Проблема, куда её разместить, математику. В степень нам не нужно возводить. С точкой числа не нужны. А % тупо короче mod. Хотя, возможно, и MOD AND OR прикручу. Хотя сервами ворочать, и ИК коды принимать/передавать тоже хочется. :)
Madf писал(а):
EPOKE - должно быть без "Е".
Уже есть PEEK/POKE, они для ОЗУ
Madf писал(а):PRINT - должна быть классическая реализация в рамках железа/функционала.
Да, поправлю, можно сделать короче в рамках одного оператора. Пока остановились на таком варианте:
A = 65
PRINT A -> 65
PRINT $A -> A
PRINT @A -> (Текстовая цепочка, расположенная в ППЗУ, начиная с адреса 65, до нуля)
Madf писал(а):
Остальные доп. вещи - это уже особенность самого бэйсика (его специфика), назовите его например "Atmega Basic" или "Duni Basic"...чтобы было оригинальное название (не встречающееся и узнаваемое).
Пока у данного бейсика нету названия. Условно называется TinyBasicEruman. Ваши варианты неплохи, но пока надо подумать.
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение Eruman » 03 авг 2016, 14:34

Очередной вариант Бейсика для ORduino168 c дополнительным ППЗУ 2К. Если дополнительной памяти не имеется, то следует все просто заменить обращениями к внутренней памяти. Комментарии не вычищал, чтобы было видно, что менялось.

Спорные команды SEQ/MSEQ пока отключены, но добавились PUT/GET, по свойствам аналогичные DATA/READ, только работают с байтом данных. Указатель адреса сбрасывается по команде STOP в значение 256. Оператор RESTORE устанавливает значение по параметру.

Новый оператор '@' позволяет запускать команды из ППЗУ, причем, в отличие от ECHAIN, не сбрасывая текущую программу. После выполнения команды из ППЗУ интерпретатор начинает выполнение программы в ОЗУ. Так удобно запускать программы с параметрами, записав их предварительно, например командами:
Код: Выделить всёРазвернуть
RESTORE512:PUT"A=100",10,13
PUT"Е=100:B=256",10,13,0

Запускаем программу с установленными параметрами:
Код: Выделить всёРазвернуть
@512

или
Код: Выделить всёРазвернуть
A=512:@A


Как уже упоминалось, пока не планируется оптимизация кода для сохранения операторов одним байтом. Сделано это для наглядности. Однако объем памяти ограничен, и пришлось идти на небольшую хитрость. В язык были введены синонимы, с помощью которых можно существенно сократить объем программы.

Список синонимов операторов:
'R' аналог RETURN
'C' аналог GOSUB
'H' аналог DWRITE HIGH // Не перестраивает порт в режим OUTPUT
'L' аналог DWRITE LOW // Не перестраивает порт в режим OUTPUT
'W' аналог AWRITE
'M' аналог RESTORE
'D' аналог DELAY
'N' аналог NEXT
'F' аналог FOR
'J' аналог GOTO
'G' аналог GET
'P' аналог PUT
'T' аналог TONE
'I' аналог INPUT
'E' аналог EPOKE
Отдельно следует упомянуть оператор '_', который позволяет выполнить оператор с заданным номером. Например, команды 'TONEW' и '_26' аналогичны. Если переменная А=26, то '_A' тоже будет аналогом TONEW.

Также добавлены синонимы функций
'A' аналог AREAD // Не перестраивает порт в режим INPUT
'D' аналог DREAD // Не перестраивает порт в режим INPUT
'E' аналог EPEEK

Добавлены операторы OR, AND, MOD.

Для примера, программа распечатки дампа памяти.
Начальный код (свободно 58 байт из 256):
Код: Выделить всёРазвернуть
5 ?@2041,A,"-",A+255:FOR J=0TO15:?A+J*16,$9;
10 FOR I=0TO15:?#EPEEK(I+A+J*16)," ";:NEXT I:?'  ';
20 FOR I=0TO15:Q=EPEEK(I+A+J*16):IFQ<0 GOTO60
30 IFQ<31Q=46
60 ?$Q;:NEXT I:?"":NEXT J:?FRE(),@2027


Сжатый код (свободно 91 байт):
Код: Выделить всёРазвернуть
5 ?@2041,A,"-",A+255:FJ=0TO15:?A+J*16,$9;
10 FI=0TO15:?#E(I+A+J*16)," ";:NI:?'  ';
20 FI=0TO15:Q=E(I+A+J*16):IFQ<0J60
30 IFQ<31Q=46
60 ?$Q;:NI:?"":NJ:?FRE(),@2027


?# - печатает форматированное десятизначное число, вида 000,
?@ - выводит на печать цепочку символов из ППЗУ, начиная с заданного адреса,
?$ - выводит на печать символ с заданным кодом.

С помощью интерпретатора легко пишутся короткие программки для управления железом. Для сравнения, программа печати дампа ПЗУ занимает менее 200 байт. Аналогичный кусок кода на Wiring поглотил более 500 (см.метку mem). Его можно сократить, но на Бейсике код располагается, помимо прочего, в ППЗУ, и это, как мне кажется, немного удобнее.
Вложения
TinyBasicEruman06.ino
Текущая версия на 03.08.2016
(52.18 КиБ) Скачиваний: 1651
Последний раз редактировалось Eruman 03 авг 2016, 15:31, всего редактировалось 1 раз.
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение esisl » 03 авг 2016, 15:30

Добавил в басик возможность объявлять "специальные команды".
Мне это правда, для управления конфетораздаточным аппаратом. Потому, сейчас позволяет объявлять 3 команды длиной до 20 символов, в т.ч. на русском.
Ребёнок вытаскивает конфеты, выискивая на клавиатуры буквы русского алфавита.

Добавляем ключевое слово ink
Код: Выделить всёРазвернуть
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,
        'I','N','K'+0x80,
   0
};


Объявляем INK и фиктивную команду KW_TAKE_JUCE
Код: Выделить всёРазвернуть
#define KW_LIST      0
#define KW_LOAD      1
#define KW_NEW      2
#define KW_RUN      3
#define KW_SAVE      4
#define KW_NEXT      5
#define KW_LET      6
#define KW_IF      7
#define KW_GOTO      8
#define KW_GOSUB   9
#define KW_RETURN   10
#define KW_REM      11
#define KW_FOR      12
#define KW_INPUT   13
#define KW_PRINT   14
#define KW_POKE      15
#define KW_STOP      16
#define KW_BYE      17
#define KW_INK      18
#define KW_DEFAULT   19
#define KW_TAKE_JUCE    20


В функции scantable
Код: Выделить всёРазвернуть
static void scantable(unsigned char *table)
{
   int i = 0;
   table_index = 0;
   while(1)
   {
      // Run out of table entries?
      if(table[0] == 0){
                  //вставить проверку на ключевые слова из quest
                  i=0;
//                  Serial.println((char)EEPROM[i]);
//                  Serial.println((char)txtpos[i]);
   
                  while(EEPROM[i]!=' '){
                    if(txtpos[i]!=EEPROM[i]) return;
//Serial.println((char)EEPROM[i]);
//Serial.println((char)txtpos[i]);
                    i++;
                  }
                  table_index=20;
                  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++;
         ignore_blanks();
         i = 0;
      }
   }
}

Добавляем проверку. Если ключевое слово не найдено (ни какое), то искать ключевое слово в EEPROM вдруг совпало в первых 20 байтах?

Если оно совпало с введённым в командную строку, то:
Код: Выделить всёРазвернуть
take_juce:
        //проскролить строки quest
//чтобы в следующий раз использовать следующую из трёх команд
        for(int i=0;i<40;i++){
          EEPROM.get(i+20,c);
          EEPROM.put(i, c);
        }
        Wire.beginTransmission(3); // начало передачи устройству с адресом 3 - это конфетораздаточный аппарат
        Wire.write('r');            // посылаем значение
        Wire.endTransmission();     // конец передачи
        goto warmstart;


Если найдена команда INK
Код: Выделить всёРазвернуть
ink:
        //проскролить строки quest
//потому, что каждая новая команда добавляется "с хвоста"
        for(int i=0;i<40;i++){
          EEPROM.get(i+20,c);
          EEPROM.put(i, c);
        }
        //заложить последний
        ignore_blanks();
        i=0;
//и кладём в хвост аргумент команды INK
        while(txtpos[0]!=NL && i<20){
          EEPROM.put(40+i, txtpos[0]);
          txtpos++;
          i++;
        }
//лишнее тупо забиваем пробелами
        while(i<20){
          EEPROM.put(40+i, ' ');
          i++;
        }
        goto warmstart;


Работает так:
1. Я загружаю конфеты, булочки, сок и шоколадки в гнёзда аппарата
2. Набираю в командной строке бейсика команды:
>ink АБРИКОС
ok
>ink ПОМИДОР
ok
>ink ОГУРЕЦ
ok

3. Ребёнок хватает клавиатуру и набирает:
>АБРИКОС
ok

аппарат прокручивается на одно гнездо и выбрасывает чего-нибудь в лоток.
Слово АБРИКОС больше не используемом

набирает
>ПОМИДОР
ok

получает следующий продукт
набирает
>ОГУРЕЦ
ok

получает последнее чего-нибудь
После чего аппарат пуст, хотя если набирать
>ОГУРЕЦ
ok

будет прокручивать гнёзда. я уже не стал возиться.

Между прочим, так и не понял логики работы EEPROM[..] в качестве массива. Тупо пишу в ячейку одно, извлекаю, почему-то другое. .get .put работают нормально.
esisl
 
Сообщения: 1482
Зарегистрирован: 23 июл 2012, 15:24

Re: Basic и Arduino

Сообщение Eruman » 03 авг 2016, 15:45

Делал похожий функционал, но у нас он оказался не нужен. В листинге программы кусок кода остался. Возможно, восстановлю в следующей версии.

А так - классно получилось. :good:

Добавлено спустя 3 минуты 29 секунд:
Между прочим, так и не понял логики работы EEPROM[..] в качестве массива. Тупо пишу в ячейку одно, извлекаю, почему-то другое. .get .put работают нормально.


Может быть DELAY не хватает?
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение esisl » 03 авг 2016, 16:05

Вот у меня насчёт делай было подозрение, но настолько невнятное, что я сделал put/get и успокоился.
esisl
 
Сообщения: 1482
Зарегистрирован: 23 июл 2012, 15:24

Re: Basic и Arduino

Сообщение Eruman » 04 авг 2016, 14:46

Добавил похожий механизм, есть некоторые нововведения, пока тестируем.

Оператором SUB записываем в ППЗУ названия подпрограмм и номер строки. А затем интерпретатор ищет операторы в том числе и в данном списке. Использовать можно как в режиме прямого ввода, так и в программном.

Объявляем список подпрограмм. Сначала название, затем номер строки:
Код: Выделить всёРазвернуть
SUB"вперед",100,"назад",200,"разворот",300,0


Готовим рабочую программу:
Код: Выделить всёРазвернуть
10 ' Основная программа
...
50  вперед:разворот:вперед
...
90 STOP

100 ' ПП движения вперед
...
199 RETURN
200 ' ПП движения назад
...
299 RETURN
300 ' ПП разворота
...
399 RETURN
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение Eruman » 07 авг 2016, 11:10

Информация для новичков: Недавно мы приблизились к порогу памяти для программы. Некоторые вещи получилось ужать за счет изменения типов переменных (например, замену int на short int, а иногда и byte), алгоритмов (оптимизация ветвлений и циклов, например), и проч.
Кстати, для сокращения программы можно заменить void setup() и void loop() на int main(void). У меня получилось в бейсике высвободить 292 байта.
Было:
Код: Выделить всёРазвернуть
//Заголовки
void setup()
{
  //Настройки
}

void loop()
{
  //Программа
}


Стало:
Код: Выделить всёРазвернуть
//Заголовки
int main(void)
{
  //Настройки
  while(1)
  {
    //Программа
  }
}
Соединяй и здравствуй.
Аватара пользователя
Eruman
 
Сообщения: 897
Зарегистрирован: 12 авг 2010, 15:10
Откуда: Астраханская обл.

Re: Basic и Arduino

Сообщение petrov1962 » 25 мар 2017, 11:22

Eruman писал(а):В силу обстоятельств имеется в наличии изрядный запас ORduino Nano v.1.1 на mega168a, детвору поучаю.
Задумал прикрутить к плате интерпретатор языка BASIC, чтоб было веселее самодельными роботами управлять. Однако поиск по просторам выдал варианты на использование чипов с 32К на борту, что меня не устраивало. В итоге творческой переработки найденных материалов и неспешного изучения сайтов получился данный проект. Компилил Arduino IDE 1.6.5, на последних версиях не проверял.
[/code]


Здравствуйте, захотел повторить ваш проект только на меге328, все отлично работает.
Подключил микросхему памяти 24c16, установил библиотеку для нее и... скетч не компилится, кучу ошибок. Пробовал и с последней IDE, и 1.6.5.
Ошибка на
E:\arduino-1.6.5-r5\Sketch\libraries\c24LC16B-master\c24LC16B.cpp:4:22: fatal error: WProgram.h: No such file or directory
#include "WProgram.h"
В интернете нашел, что надо переименовать "WProgram.h" в "Arduino.h", но не помогло, теперь ошибок еще больше стало.
Ругается на "wire.h"
Библиотеку для 24с16 брал по ссылке в скетче.
Может, я что не так делаю?
Подскажите...
petrov1962
 
Сообщения: 7
Зарегистрирован: 25 мар 2017, 10:40

Пред.След.

Вернуться в Arduino и другие Xduino

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

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