#define kVersion "v0.14"
char eliminateCompileErrors = 1;
#ifdef FORCE_DESKTOP
#undef ARDUINO
#include "desktop.h"
#else
#define ARDUINO 1
#endif
#undef ENABLE_FILEIO
// this turns on "autorun". if there's FileIO, and a file "autorun.bas",
// then it will load it and run it when starting up
//#define ENABLE_AUTORUN 1
#undef ENABLE_AUTORUN
// and this is the file that gets run
#define kAutorunFilename "autorun.bas"
// this is the alternate autorun. Autorun the program in the eeprom.
// it will load whatever is in the EEProm and run it
#define ENABLE_EAUTORUN 1
//#undef ENABLE_EAUTORUN
// this will enable the "TONE", "NOTONE" command using a piezo
// element on the specified pin. Wire the red/positive/piezo to the kPiezoPin,
// and the black/negative/metal disc to ground.
// it adds 1.5k of usage as well.
//#define ENABLE_TONES 1
#undef ENABLE_TONES
#define kPiezoPin 5
// we can use the EEProm to store a program during powerdown. This is
// 1kbyte on the '328, and 512 bytes on the '168. Enabling this here will
// allow for this funcitonality to work. Note that this only works on AVR
// arduino. Disable it for DUE/other devices.
#define ENABLE_EEPROM 1
//#undef ENABLE_EEPROM
// Sometimes, we connect with a slower device as the console.
// Set your console D0/D1 baud rate here (9600 baud default)
//#define kConsoleBaud 9600
#define kConsoleBaud 2400 //-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
#ifdef ARDUINO
#ifndef RAMEND
// okay, this is a hack for now
// if we're in here, we're a DUE probably (ARM instead of AVR)
#define RAMEND 4096-1
// turn off EEProm
#undef ENABLE_EEPROM
#undef ENABLE_TONES
#else
// we're an AVR!
// we're moving our data strings into progmem
#include <avr/pgmspace.h>
#endif
// includes, and settings for Arduino-specific functionality
#ifdef ENABLE_EEPROM
#include <EEPROM.h> /* NOTE: case sensitive */
int eepos = 0;
#endif
//---------------------------------------------------------------------------------------------------------------------------------------------------
#include <PS2Keyboard.h>
const int DataPin = 8;
const int IRQpin = 3;
PS2Keyboard keyboard;
//-------------------------------------------------------------------------------------------------------------------------------------------------------
#ifdef ENABLE_FILEIO
#include <SD.h>
#include <SPI.h> /* needed as of 1.5 beta */
// Arduino-specific configuration
// set this to the card select for your SD shield
#define kSD_CS 10
#define kSD_Fail 0
#define kSD_OK 1
File fp;
#endif
// set up our RAM buffer size for program and user input
// NOTE: This number will have to change if you include other libraries.
#ifdef ARDUINO
#ifdef ENABLE_FILEIO
#define kRamFileIO (1030) /* approximate */
#else
#define kRamFileIO (0)
#endif
#ifdef ENABLE_TONES
#define kRamTones (40)
#else
#define kRamTones (0)
#endif
#endif /* ARDUINO */
#define kRamSize (RAMEND - 1160 - kRamFileIO - kRamTones)
#ifndef ARDUINO
// Not arduino setup
#include <stdio.h>
#include <stdlib.h>
#undef ENABLE_TONES
// size of our program ram
#define kRamSize 4096 /* arbitrary */
#ifdef ENABLE_FILEIO
FILE * fp;
#endif
#endif
#ifdef ENABLE_FILEIO
// functions defined elsehwere
void cmd_Files( void );
#endif
////////////////////
#ifndef boolean
#define boolean int
#define true 1
#define false 0
#endif
#endif
#ifndef byte
typedef unsigned char byte;
#endif
// some catches for AVR based text string stuff...
#ifndef PROGMEM
#define PROGMEM
#endif
#ifndef pgm_read_byte
#define pgm_read_byte( A ) *(A)
#endif
////////////////////
#ifdef ENABLE_FILEIO
unsigned char * filenameWord(void);
static boolean sd_is_initialized = false;
#endif
boolean inhibitOutput = false;
static boolean runAfterLoad = false;
static boolean triggerRun = false;
// these will select, at runtime, where IO happens through for load/save
enum {
kStreamSerial = 0,
kStreamEEProm,
kStreamFile
};
static unsigned char inStream = kStreamSerial;
static unsigned char outStream = kStreamSerial;
////////////////////////////////////////////////////////////////////////////////
// ASCII Characters
#define CR '\r'
#define NL '\n'
#define LF 0x0a
#define TAB '\t'
#define BELL '\b'
#define SPACE ' '
#define SQUOTE '\''
#define DQUOTE '\"'
#define CTRLC 0x03
#define CTRLH 0x08
#define CTRLS 0x13
#define CTRLX 0x18
typedef short unsigned LINENUM;
#ifdef ARDUINO
#define ECHO_CHARS 1
#else
#define ECHO_CHARS 0
#endif
static unsigned char program[kRamSize];
static const char * sentinel = "HELLO";
static unsigned char *txtpos,*list_line, *tmptxtpos;
static unsigned char expression_error;
static unsigned char *tempsp;
/***********************************************************/
// Keyword table and constants - the last character has 0x80 added to it
const static unsigned char keywords[] PROGMEM = {
'L','I','S','T'+0x80,
'L','O','A','D'+0x80,
'N','E','W'+0x80,
'R','U','N'+0x80,
'S','A','V','E'+0x80,
'N','E','X','T'+0x80,
'L','E','T'+0x80,
'I','F'+0x80,
'G','O','T','O'+0x80,
'G','O','S','U','B'+0x80,
'R','E','T','U','R','N'+0x80,
'R','E','M'+0x80,
'F','O','R'+0x80,
'I','N','P','U','T'+0x80,
'P','R','I','N','T'+0x80,
'P','O','K','E'+0x80,
'S','T','O','P'+0x80,
'B','Y','E'+0x80,
'F','I','L','E','S'+0x80,
'M','E','M'+0x80,
'?'+ 0x80,
'\''+ 0x80,
'A','W','R','I','T','E'+0x80,
'D','W','R','I','T','E'+0x80,
'D','E','L','A','Y'+0x80,
'E','N','D'+0x80,
'R','S','E','E','D'+0x80,
'C','H','A','I','N'+0x80,
#ifdef ENABLE_TONES
'T','O','N','E','W'+0x80,
'T','O','N','E'+0x80,
'N','O','T','O','N','E'+0x80,
#endif
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
'E','C','H','A','I','N'+0x80,
'E','L','I','S','T'+0x80,
'E','L','O','A','D'+0x80,
'E','F','O','R','M','A','T'+0x80,
'E','S','A','V','E'+0x80,
#endif
#endif
0
};
// by moving the command list to an enum, we can easily remove sections
// above and below simultaneously to selectively obliterate functionality.
enum {
KW_LIST = 0,
KW_LOAD, KW_NEW, KW_RUN, KW_SAVE,
KW_NEXT, KW_LET, KW_IF,
KW_GOTO, KW_GOSUB, KW_RETURN,
KW_REM,
KW_FOR,
KW_INPUT, KW_PRINT,
KW_POKE,
KW_STOP, KW_BYE,
KW_FILES,
KW_MEM,
KW_QMARK, KW_QUOTE,
KW_AWRITE, KW_DWRITE,
KW_DELAY,
KW_END,
KW_RSEED,
KW_CHAIN,
#ifdef ENABLE_TONES
KW_TONEW, KW_TONE, KW_NOTONE,
#endif
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
KW_ECHAIN, KW_ELIST, KW_ELOAD, KW_EFORMAT, KW_ESAVE,
#endif
#endif
KW_DEFAULT /* always the final one*/
};
struct stack_for_frame {
char frame_type;
char for_var;
short int terminal;
short int step;
unsigned char *current_line;
unsigned char *txtpos;
};
struct stack_gosub_frame {
char frame_type;
unsigned char *current_line;
unsigned char *txtpos;
};
const static unsigned char func_tab[] PROGMEM = {
'P','E','E','K'+0x80,
'A','B','S'+0x80,
'A','R','E','A','D'+0x80,
'D','R','E','A','D'+0x80,
'R','N','D'+0x80,
0
};
#define FUNC_PEEK 0
#define FUNC_ABS 1
#define FUNC_AREAD 2
#define FUNC_DREAD 3
#define FUNC_RND 4
#define FUNC_UNKNOWN 5
const static unsigned char to_tab[] PROGMEM = {
'T','O'+0x80,
0
};
const static unsigned char step_tab[] PROGMEM = {
'S','T','E','P'+0x80,
0
};
const static unsigned char relop_tab[] PROGMEM = {
'>','='+0x80,
'<','>'+0x80,
'>'+0x80,
'='+0x80,
'<','='+0x80,
'<'+0x80,
'!','='+0x80,
0
};
#define RELOP_GE 0
#define RELOP_NE 1
#define RELOP_GT 2
#define RELOP_EQ 3
#define RELOP_LE 4
#define RELOP_LT 5
#define RELOP_NE_BANG 6
#define RELOP_UNKNOWN 7
const static unsigned char highlow_tab[] PROGMEM = {
'H','I','G','H'+0x80,
'H','I'+0x80,
'L','O','W'+0x80,
'L','O'+0x80,
0
};
#define HIGHLOW_HIGH 1
#define HIGHLOW_UNKNOWN 4
#define STACK_SIZE (sizeof(struct stack_for_frame)*5)
#define VAR_SIZE sizeof(short int) // Size of variables in bytes
static unsigned char *stack_limit;
static unsigned char *program_start;
static unsigned char *program_end;
static unsigned char *stack; // Software stack for things that should go on the CPU stack
static unsigned char *variables_begin;
static unsigned char *current_line;
static unsigned char *sp;
#define STACK_GOSUB_FLAG 'G'
#define STACK_FOR_FLAG 'F'
static unsigned char table_index;
static LINENUM linenum;
static const unsigned char okmsg[] PROGMEM = "OK";
static const unsigned char whatmsg[] PROGMEM = "What? ";
static const unsigned char howmsg[] PROGMEM = "How?";
static const unsigned char sorrymsg[] PROGMEM = "Sorry!";
static const unsigned char initmsg[] PROGMEM = "TinyBasic Plus " kVersion;
static const unsigned char memorymsg[] PROGMEM = " bytes free.";
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
static const unsigned char eeprommsg[] PROGMEM = " EEProm bytes total.";
static const unsigned char eepromamsg[] PROGMEM = " EEProm bytes available.";
#endif
#endif
static const unsigned char breakmsg[] PROGMEM = "break!";
static const unsigned char unimplimentedmsg[] PROGMEM = "Unimplemented";
static const unsigned char backspacemsg[] PROGMEM = "\b \b";
static const unsigned char indentmsg[] PROGMEM = " ";
static const unsigned char sderrormsg[] PROGMEM = "SD card error.";
static const unsigned char sdfilemsg[] PROGMEM = "SD file error.";
static const unsigned char dirextmsg[] PROGMEM = "(dir)";
static const unsigned char slashmsg[] PROGMEM = "/";
static const unsigned char spacemsg[] PROGMEM = " ";
static int inchar(void);
static void outchar(unsigned char c);
static void line_terminator(void);
static short int expression(void);
static unsigned char breakcheck(void);
/***************************************************************************/
static void ignore_blanks(void)
{
while(*txtpos == SPACE || *txtpos == TAB)
txtpos++;
}
/***************************************************************************/
static void scantable(const unsigned char *table)
{
int i = 0;
table_index = 0;
while(1)
{
// Run out of table entries?
if(pgm_read_byte( table ) == 0)
return;
// Do we match this character?
if(txtpos[i] == pgm_read_byte( table ))
{
i++;
table++;
}
else
{
// do we match the last character of keywork (with 0x80 added)? If so, return
if(txtpos[i]+0x80 == pgm_read_byte( table ))
{
txtpos += i+1; // Advance the pointer to following the keyword
ignore_blanks();
return;
}
// Forward to the end of this keyword
while((pgm_read_byte( table ) & 0x80) == 0)
table++;
// Now move on to the first character of the next word, and reset the position index
table++;
table_index++;
ignore_blanks();
i = 0;
}
}
}
/***************************************************************************/
static void pushb(unsigned char b)
{
sp--;
*sp = b;
}
/***************************************************************************/
static unsigned char popb()
{
unsigned char b;
b = *sp;
sp++;
return b;
}
/***************************************************************************/
void printnum(int num)
{
int digits = 0;
if(num < 0)
{
num = -num;
outchar('-');
}
do {
pushb(num%10+'0');
num = num/10;
digits++;
}
while (num > 0);
while(digits > 0)
{
outchar(popb());
digits--;
}
}
void printUnum(unsigned int num)
{
int digits = 0;
do {
pushb(num%10+'0');
num = num/10;
digits++;
}
while (num > 0);
while(digits > 0)
{
outchar(popb());
digits--;
}
}
/***************************************************************************/
static unsigned short testnum(void)
{
unsigned short num = 0;
ignore_blanks();
while(*txtpos>= '0' && *txtpos <= '9' )
{
// Trap overflows
if(num >= 0xFFFF/10)
{
num = 0xFFFF;
break;
}
num = num *10 + *txtpos - '0';
txtpos++;
}
return num;
}
/***************************************************************************/
static unsigned char print_quoted_string(void)
{
int i=0;
unsigned char delim = *txtpos;
if(delim != '"' && delim != '\'')
return 0;
txtpos++;
// Check we have a closing delimiter
while(txtpos[i] != delim)
{
if(txtpos[i] == NL)
return 0;
i++;
}
// Print the characters
while(*txtpos != delim)
{
outchar(*txtpos);
txtpos++;
}
txtpos++; // Skip over the last delimiter
return 1;
}
/***************************************************************************/
void printmsgNoNL(const unsigned char *msg)
{
while( pgm_read_byte( msg ) != 0 ) {
outchar( pgm_read_byte( msg++ ) );
};
}
/***************************************************************************/
void printmsg(const unsigned char *msg)
{
printmsgNoNL(msg);
line_terminator();
}
/***************************************************************************/
static void getln(char prompt)
{
outchar(prompt);
txtpos = program_end+sizeof(LINENUM);
while(1)
{
char c = inchar();
switch(c)
{
case NL:
//break;
case CR:
line_terminator();
// Terminate all strings with a NL
txtpos[0] = NL;
return;
case CTRLH:
if(txtpos == program_end)
break;
txtpos--;
printmsg(backspacemsg);
break;
default:
// We need to leave at least one space to allow us to shuffle the line into order
if(txtpos == variables_begin-2)
outchar(BELL);
else
{
txtpos[0] = c;
txtpos++;
outchar(c);
}
}
}
}
/***************************************************************************/
static unsigned char *findline(void)
{
unsigned char *line = program_start;
while(1)
{
if(line == program_end)
return line;
if(((LINENUM *)line)[0] >= linenum)
return line;
// Add the line length onto the current address, to get to the next line;
line += line[sizeof(LINENUM)];
}
}
/***************************************************************************/
static void toUppercaseBuffer(void)
{
unsigned char *c = program_end+sizeof(LINENUM);
unsigned char quote = 0;
while(*c != NL)
{
// Are we in a quoted string?
if(*c == quote)
quote = 0;
else if(*c == '"' || *c == '\'')
quote = *c;
else if(quote == 0 && *c >= 'a' && *c <= 'z')
*c = *c + 'A' - 'a';
c++;
}
}
/***************************************************************************/
void printline()
{
LINENUM line_num;
line_num = *((LINENUM *)(list_line));
list_line += sizeof(LINENUM) + sizeof(char);
// Output the line */
printnum(line_num);
outchar(' ');
while(*list_line != NL)
{
outchar(*list_line);
list_line++;
}
list_line++;
line_terminator();
}
/***************************************************************************/
static short int expr4(void)
{
// fix provided by Jurg Wullschleger wullschleger@gmail.com
// fixes whitespace and unary operations
ignore_blanks();
if( *txtpos == '-' ) {
txtpos++;
return -expr4();
}
// end fix
if(*txtpos == '0')
{
txtpos++;
return 0;
}
if(*txtpos >= '1' && *txtpos <= '9')
{
short int a = 0;
do {
a = a*10 + *txtpos - '0';
txtpos++;
}
while(*txtpos >= '0' && *txtpos <= '9');
return a;
}
// Is it a function or variable reference?
if(txtpos[0] >= 'A' && txtpos[0] <= 'Z')
{
short int a;
// Is it a variable reference (single alpha)
if(txtpos[1] < 'A' || txtpos[1] > 'Z')
{
a = ((short int *)variables_begin)[*txtpos - 'A'];
txtpos++;
return a;
}
// Is it a function with a single parameter
scantable(func_tab);
if(table_index == FUNC_UNKNOWN)
goto expr4_error;
unsigned char f = table_index;
if(*txtpos != '(')
goto expr4_error;
txtpos++;
a = expression();
if(*txtpos != ')')
goto expr4_error;
txtpos++;
switch(f)
{
case FUNC_PEEK:
return program[a];
case FUNC_ABS:
if(a < 0)
return -a;
return a;
#ifdef ARDUINO
case FUNC_AREAD:
pinMode( a, INPUT );
return analogRead( a );
case FUNC_DREAD:
pinMode( a, INPUT );
return digitalRead( a );
#endif
case FUNC_RND:
#ifdef ARDUINO
return( random( a ));
#else
return( rand() % a );
#endif
}
}
if(*txtpos == '(')
{
short int a;
txtpos++;
a = expression();
if(*txtpos != ')')
goto expr4_error;
txtpos++;
return a;
}
expr4_error:
expression_error = 1;
return 0;
}
/***************************************************************************/
static short int expr3(void)
{
short int a,b;
a = expr4();
ignore_blanks(); // fix for eg: 100 a = a + 1
while(1)
{
if(*txtpos == '*')
{
txtpos++;
b = expr4();
a *= b;
}
else if(*txtpos == '/')
{
txtpos++;
b = expr4();
if(b != 0)
a /= b;
else
expression_error = 1;
}
else
return a;
}
}
/***************************************************************************/
static short int expr2(void)
{
short int a,b;
if(*txtpos == '-' || *txtpos == '+')
a = 0;
else
a = expr3();
while(1)
{
if(*txtpos == '-')
{
txtpos++;
b = expr3();
a -= b;
}
else if(*txtpos == '+')
{
txtpos++;
b = expr3();
a += b;
}
else
return a;
}
}
/***************************************************************************/
static short int expression(void)
{
short int a,b;
a = expr2();
// Check if we have an error
if(expression_error) return a;
scantable(relop_tab);
if(table_index == RELOP_UNKNOWN)
return a;
switch(table_index)
{
case RELOP_GE:
b = expr2();
if(a >= b) return 1;
break;
case RELOP_NE:
case RELOP_NE_BANG:
b = expr2();
if(a != b) return 1;
break;
case RELOP_GT:
b = expr2();
if(a > b) return 1;
break;
case RELOP_EQ:
b = expr2();
if(a == b) return 1;
break;
case RELOP_LE:
b = expr2();
if(a <= b) return 1;
break;
case RELOP_LT:
b = expr2();
if(a < b) return 1;
break;
}
return 0;
}
/***************************************************************************/
void loop()
{
Serial.println("with MRETV composit video output");
Serial.println("Version beta 1.0");
unsigned char *start;
unsigned char *newEnd;
unsigned char linelen;
boolean isDigital;
boolean alsoWait = false;
int val;
#ifdef ARDUINO
#ifdef ENABLE_TONES
noTone( kPiezoPin );
#endif
#endif
program_start = program;
program_end = program_start;
sp = program+sizeof(program); // Needed for printnum
stack_limit = program+sizeof(program)-STACK_SIZE;
variables_begin = stack_limit - 27*VAR_SIZE;
// memory free
printnum(variables_begin-program_end);
printmsg(memorymsg);
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
// eprom size
printnum( E2END+1 );
printmsg( eeprommsg );
#endif /* ENABLE_EEPROM */
#endif /* ARDUINO */
warmstart:
// this signifies that it is running in 'direct' mode.
current_line = 0;
sp = program+sizeof(program);
printmsg(okmsg);
prompt:
if( triggerRun ){
triggerRun = false;
current_line = program_start;
goto execline;
}
getln( '>' );
toUppercaseBuffer();
txtpos = program_end+sizeof(unsigned short);
// Find the end of the freshly entered line
while(*txtpos != NL)
txtpos++;
// Move it to the end of program_memory
{
unsigned char *dest;
dest = variables_begin-1;
while(1)
{
*dest = *txtpos;
if(txtpos == program_end+sizeof(unsigned short))
break;
dest--;
txtpos--;
}
txtpos = dest;
}
// Now see if we have a line number
linenum = testnum();
ignore_blanks();
if(linenum == 0)
goto direct;
if(linenum == 0xFFFF)
goto qhow;
// Find the length of what is left, including the (yet-to-be-populated) line header
linelen = 0;
while(txtpos[linelen] != NL)
linelen++;
linelen++; // Include the NL in the line length
linelen += sizeof(unsigned short)+sizeof(char); // Add space for the line number and line length
// Now we have the number, add the line header.
txtpos -= 3;
*((unsigned short *)txtpos) = linenum;
txtpos[sizeof(LINENUM)] = linelen;
// Merge it into the rest of the program
start = findline();
// If a line with that number exists, then remove it
if(start != program_end && *((LINENUM *)start) == linenum)
{
unsigned char *dest, *from;
unsigned tomove;
from = start + start[sizeof(LINENUM)];
dest = start;
tomove = program_end - from;
while( tomove > 0)
{
*dest = *from;
from++;
dest++;
tomove--;
}
program_end = dest;
}
if(txtpos[sizeof(LINENUM)+sizeof(char)] == NL) // If the line has no txt, it was just a delete
goto prompt;
// Make room for the new line, either all in one hit or lots of little shuffles
while(linelen > 0)
{
unsigned int tomove;
unsigned char *from,*dest;
unsigned int space_to_make;
space_to_make = txtpos - program_end;
if(space_to_make > linelen)
space_to_make = linelen;
newEnd = program_end+space_to_make;
tomove = program_end - start;
// Source and destination - as these areas may overlap we need to move bottom up
from = program_end;
dest = newEnd;
while(tomove > 0)
{
from--;
dest--;
*dest = *from;
tomove--;
}
// Copy over the bytes into the new space
for(tomove = 0; tomove < space_to_make; tomove++)
{
*start = *txtpos;
txtpos++;
start++;
linelen--;
}
program_end = newEnd;
}
goto prompt;
unimplemented:
printmsg(unimplimentedmsg);
goto prompt;
qhow:
printmsg(howmsg);
goto prompt;
qwhat:
printmsgNoNL(whatmsg);
if(current_line != NULL)
{
unsigned char tmp = *txtpos;
if(*txtpos != NL)
*txtpos = '^';
list_line = current_line;
printline();
*txtpos = tmp;
}
line_terminator();
goto prompt;
qsorry:
printmsg(sorrymsg);
goto warmstart;
run_next_statement:
while(*txtpos == ':')
txtpos++;
ignore_blanks();
if(*txtpos == NL)
goto execnextline;
goto interperateAtTxtpos;
direct:
txtpos = program_end+sizeof(LINENUM);
if(*txtpos == NL)
goto prompt;
interperateAtTxtpos:
if(breakcheck())
{
printmsg(breakmsg);
goto warmstart;
}
scantable(keywords);
switch(table_index)
{
case KW_DELAY:
{
#ifdef ARDUINO
expression_error = 0;
val = expression();
delay( val );
goto execnextline;
#else
goto unimplemented;
#endif
}
case KW_FILES:
goto files;
case KW_LIST:
goto list;
case KW_CHAIN:
goto chain;
case KW_LOAD:
goto load;
case KW_MEM:
goto mem;
case KW_NEW:
if(txtpos[0] != NL)
goto qwhat;
program_end = program_start;
goto prompt;
case KW_RUN:
current_line = program_start;
goto execline;
case KW_SAVE:
goto save;
case KW_NEXT:
goto next;
case KW_LET:
goto assignment;
case KW_IF:
short int val;
expression_error = 0;
val = expression();
if(expression_error || *txtpos == NL)
goto qhow;
if(val != 0)
goto interperateAtTxtpos;
goto execnextline;
case KW_GOTO:
expression_error = 0;
linenum = expression();
if(expression_error || *txtpos != NL)
goto qhow;
current_line = findline();
goto execline;
case KW_GOSUB:
goto gosub;
case KW_RETURN:
goto gosub_return;
case KW_REM:
case KW_QUOTE:
goto execnextline; // Ignore line completely
case KW_FOR:
goto forloop;
case KW_INPUT:
goto input;
case KW_PRINT:
case KW_QMARK:
goto print;
case KW_POKE:
goto poke;
case KW_END:
case KW_STOP:
// This is the easy way to end - set the current line to the end of program attempt to run it
if(txtpos[0] != NL)
goto qwhat;
current_line = program_end;
goto execline;
case KW_BYE:
// Leave the basic interperater
return;
case KW_AWRITE: // AWRITE <pin>, HIGH|LOW
isDigital = false;
goto awrite;
case KW_DWRITE: // DWRITE <pin>, HIGH|LOW
isDigital = true;
goto dwrite;
case KW_RSEED:
goto rseed;
#ifdef ENABLE_TONES
case KW_TONEW:
alsoWait = true;
case KW_TONE:
goto tonegen;
case KW_NOTONE:
goto tonestop;
#endif
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
case KW_EFORMAT:
goto eformat;
case KW_ESAVE:
goto esave;
case KW_ELOAD:
goto eload;
case KW_ELIST:
goto elist;
case KW_ECHAIN:
goto echain;
#endif
#endif
case KW_DEFAULT:
goto assignment;
default:
break;
}
execnextline:
if(current_line == NULL) // Processing direct commands?
goto prompt;
current_line += current_line[sizeof(LINENUM)];
execline:
if(current_line == program_end) // Out of lines to run
goto warmstart;
txtpos = current_line+sizeof(LINENUM)+sizeof(char);
goto interperateAtTxtpos;
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
elist:
{
int i;
for( i = 0 ; i < (E2END +1) ; i++ )
{
val = EEPROM.read( i );
if( val == '\0' ) {
goto execnextline;
}
if( ((val < ' ') || (val > '~')) && (val != NL) && (val != CR)) {
outchar( '?' );
}
else {
outchar( val );
}
}
}
goto execnextline;
eformat:
{
for( int i = 0 ; i < E2END ; i++ )
{
if( (i & 0x03f) == 0x20 ) outchar( '.' );
EEPROM.write( i, 0 );
}
outchar( LF );
}
goto execnextline;
esave:
{
outStream = kStreamEEProm;
eepos = 0;
// copied from "List"
list_line = findline();
while(list_line != program_end) {
printline();
}
outchar('\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;
inStream = kStreamEEProm;
inhibitOutput = true;
goto warmstart;
#endif /* ENABLE_EEPROM */
#endif
input:
{
unsigned char var;
int value;
ignore_blanks();
if(*txtpos < 'A' || *txtpos > 'Z')
goto qwhat;
var = *txtpos;
txtpos++;
ignore_blanks();
if(*txtpos != NL && *txtpos != ':')
goto qwhat;
inputagain:
tmptxtpos = txtpos;
getln( '?' );
toUppercaseBuffer();
txtpos = program_end+sizeof(unsigned short);
ignore_blanks();
expression_error = 0;
value = expression();
if(expression_error)
goto inputagain;
((short int *)variables_begin)[var-'A'] = value;
txtpos = tmptxtpos;
goto run_next_statement;
}
forloop:
{
unsigned char var;
short int initial, step, terminal;
ignore_blanks();
if(*txtpos < 'A' || *txtpos > 'Z')
goto qwhat;
var = *txtpos;
txtpos++;
ignore_blanks();
if(*txtpos != '=')
goto qwhat;
txtpos++;
ignore_blanks();
expression_error = 0;
initial = expression();
if(expression_error)
goto qwhat;
scantable(to_tab);
if(table_index != 0)
goto qwhat;
terminal = expression();
if(expression_error)
goto qwhat;
scantable(step_tab);
if(table_index == 0)
{
step = expression();
if(expression_error)
goto qwhat;
}
else
step = 1;
ignore_blanks();
if(*txtpos != NL && *txtpos != ':')
goto qwhat;
if(!expression_error && *txtpos == NL)
{
struct stack_for_frame *f;
if(sp + sizeof(struct stack_for_frame) < stack_limit)
goto qsorry;
sp -= sizeof(struct stack_for_frame);
f = (struct stack_for_frame *)sp;
((short int *)variables_begin)[var-'A'] = initial;
f->frame_type = STACK_FOR_FLAG;
f->for_var = var;
f->terminal = terminal;
f->step = step;
f->txtpos = txtpos;
f->current_line = current_line;
goto run_next_statement;
}
}
goto qhow;
gosub:
expression_error = 0;
linenum = expression();
if(!expression_error && *txtpos == NL)
{
struct stack_gosub_frame *f;
if(sp + sizeof(struct stack_gosub_frame) < stack_limit)
goto qsorry;
sp -= sizeof(struct stack_gosub_frame);
f = (struct stack_gosub_frame *)sp;
f->frame_type = STACK_GOSUB_FLAG;
f->txtpos = txtpos;
f->current_line = current_line;
current_line = findline();
goto execline;
}
goto qhow;
next:
// Fnd the variable name
ignore_blanks();
if(*txtpos < 'A' || *txtpos > 'Z')
goto qhow;
txtpos++;
ignore_blanks();
if(*txtpos != ':' && *txtpos != NL)
goto qwhat;
gosub_return:
// Now walk up the stack frames and find the frame we want, if present
tempsp = sp;
while(tempsp < program+sizeof(program)-1)
{
switch(tempsp[0])
{
case STACK_GOSUB_FLAG:
if(table_index == KW_RETURN)
{
struct stack_gosub_frame *f = (struct stack_gosub_frame *)tempsp;
current_line = f->current_line;
txtpos = f->txtpos;
sp += sizeof(struct stack_gosub_frame);
goto run_next_statement;
}
// This is not the loop you are looking for... so Walk back up the stack
tempsp += sizeof(struct stack_gosub_frame);
break;
case STACK_FOR_FLAG:
// Flag, Var, Final, Step
if(table_index == KW_NEXT)
{
struct stack_for_frame *f = (struct stack_for_frame *)tempsp;
// Is the the variable we are looking for?
if(txtpos[-1] == f->for_var)
{
short int *varaddr = ((short int *)variables_begin) + txtpos[-1] - 'A';
*varaddr = *varaddr + f->step;
// Use a different test depending on the sign of the step increment
if((f->step > 0 && *varaddr <= f->terminal) || (f->step < 0 && *varaddr >= f->terminal))
{
// We have to loop so don't pop the stack
txtpos = f->txtpos;
current_line = f->current_line;
goto run_next_statement;
}
// We've run to the end of the loop. drop out of the loop, popping the stack
sp = tempsp + sizeof(struct stack_for_frame);
goto run_next_statement;
}
}
// This is not the loop you are looking for... so Walk back up the stack
tempsp += sizeof(struct stack_for_frame);
break;
default:
//printf("Stack is stuffed!\n");
goto warmstart;
}
}
// Didn't find the variable we've been looking for
goto qhow;
assignment:
{
short int value;
short int *var;
if(*txtpos < 'A' || *txtpos > 'Z')
goto qhow;
var = (short int *)variables_begin + *txtpos - 'A';
txtpos++;
ignore_blanks();
if (*txtpos != '=')
goto qwhat;
txtpos++;
ignore_blanks();
expression_error = 0;
value = expression();
if(expression_error)
goto qwhat;
// Check that we are at the end of the statement
if(*txtpos != NL && *txtpos != ':')
goto qwhat;
*var = value;
}
goto run_next_statement;
poke:
{
short int value;
unsigned char *address;
// Work out where to put it
expression_error = 0;
value = expression();
if(expression_error)
goto qwhat;
address = (unsigned char *)value;
// check for a comma
ignore_blanks();
if (*txtpos != ',')
goto qwhat;
txtpos++;
ignore_blanks();
// Now get the value to assign
expression_error = 0;
value = expression();
if(expression_error)
goto qwhat;
//printf("Poke %p value %i\n",address, (unsigned char)value);
// Check that we are at the end of the statement
if(*txtpos != NL && *txtpos != ':')
goto qwhat;
}
goto run_next_statement;
list:
linenum = testnum(); // Retuns 0 if no line found.
// Should be EOL
if(txtpos[0] != NL)
goto qwhat;
// Find the line
list_line = findline();
while(list_line != program_end)
printline();
goto warmstart;
print:
// If we have an empty list then just put out a NL
if(*txtpos == ':' )
{
line_terminator();
txtpos++;
goto run_next_statement;
}
if(*txtpos == NL)
{
goto execnextline;
}
while(1)
{
ignore_blanks();
if(print_quoted_string())
{
;
}
else if(*txtpos == '"' || *txtpos == '\'')
goto qwhat;
else
{
short int e;
expression_error = 0;
e = expression();
if(expression_error)
goto qwhat;
printnum(e);
}
// At this point we have three options, a comma or a new line
if(*txtpos == ',')
txtpos++; // Skip the comma and move onto the next
else if(txtpos[0] == ';' && (txtpos[1] == NL || txtpos[1] == ':'))
{
txtpos++; // This has to be the end of the print - no newline
break;
}
else if(*txtpos == NL || *txtpos == ':')
{
line_terminator(); // The end of the print statement
break;
}
else
goto qwhat;
}
goto run_next_statement;
mem:
// memory free
printnum(variables_begin-program_end);
printmsg(memorymsg);
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
{
// eprom size
printnum( E2END+1 );
printmsg( eeprommsg );
// figure out the memory usage;
val = ' ';
int i;
for( i=0 ; (i<(E2END+1)) && (val != '\0') ; i++ ) {
val = EEPROM.read( i );
}
printnum( (E2END +1) - (i-1) );
printmsg( eepromamsg );
}
#endif /* ENABLE_EEPROM */
#endif /* ARDUINO */
goto run_next_statement;
/*************************************************/
#ifdef ARDUINO
awrite: // AWRITE <pin>,val
dwrite:
{
short int pinNo;
short int value;
unsigned char *txtposBak;
// Get the pin number
expression_error = 0;
pinNo = expression();
if(expression_error)
goto qwhat;
// check for a comma
ignore_blanks();
if (*txtpos != ',')
goto qwhat;
txtpos++;
ignore_blanks();
txtposBak = txtpos;
scantable(highlow_tab);
if(table_index != HIGHLOW_UNKNOWN)
{
if( table_index <= HIGHLOW_HIGH ) {
value = 1;
}
else {
value = 0;
}
}
else {
// and the value (numerical)
expression_error = 0;
value = expression();
if(expression_error)
goto qwhat;
}
pinMode( pinNo, OUTPUT );
if( isDigital ) {
digitalWrite( pinNo, value );
}
else {
analogWrite( pinNo, value );
}
}
goto run_next_statement;
#else
pinmode: // PINMODE <pin>, I/O
awrite: // AWRITE <pin>,val
dwrite:
goto unimplemented;
#endif
/*************************************************/
files:
// display a listing of files on the device.
// version 1: no support for subdirectories
#ifdef ENABLE_FILEIO
cmd_Files();
goto warmstart;
#else
goto unimplemented;
#endif // ENABLE_FILEIO
chain:
runAfterLoad = true;
load:
// clear the program
program_end = program_start;
// load from a file into memory
#ifdef ENABLE_FILEIO
{
unsigned char *filename;
// Work out the filename
expression_error = 0;
filename = filenameWord();
if(expression_error)
goto qwhat;
#ifdef ARDUINO
// Arduino specific
if( !SD.exists( (char *)filename ))
{
printmsg( sdfilemsg );
}
else {
fp = SD.open( (const char *)filename );
inStream = kStreamFile;
inhibitOutput = true;
}
#else // ARDUINO
// Desktop specific
#endif // ARDUINO
// this will kickstart a series of events to read in from the file.
}
goto warmstart;
#else // ENABLE_FILEIO
goto unimplemented;
#endif // ENABLE_FILEIO
save:
// save from memory out to a file
#ifdef ENABLE_FILEIO
{
unsigned char *filename;
// Work out the filename
expression_error = 0;
filename = filenameWord();
if(expression_error)
goto qwhat;
#ifdef ARDUINO
// remove the old file if it exists
if( SD.exists( (char *)filename )) {
SD.remove( (char *)filename );
}
// open the file, switch over to file output
fp = SD.open( (const char *)filename, FILE_WRITE );
outStream = kStreamFile;
// copied from "List"
list_line = findline();
while(list_line != program_end)
printline();
// go back to standard output, close the file
outStream = kStreamSerial;
fp.close();
#else // ARDUINO
// desktop
#endif // ARDUINO
goto warmstart;
}
#else // ENABLE_FILEIO
goto unimplemented;
#endif // ENABLE_FILEIO
rseed:
{
short int value;
//Get the pin number
expression_error = 0;
value = expression();
if(expression_error)
goto qwhat;
#ifdef ARDUINO
randomSeed( value );
#else // ARDUINO
srand( value );
#endif // ARDUINO
goto run_next_statement;
}
#ifdef ENABLE_TONES
tonestop:
noTone( kPiezoPin );
goto run_next_statement;
tonegen:
{
// TONE freq, duration
// if either are 0, tones turned off
short int freq;
short int duration;
//Get the frequency
expression_error = 0;
freq = expression();
if(expression_error)
goto qwhat;
ignore_blanks();
if (*txtpos != ',')
goto qwhat;
txtpos++;
ignore_blanks();
//Get the duration
expression_error = 0;
duration = expression();
if(expression_error)
goto qwhat;
if( freq == 0 || duration == 0 )
goto tonestop;
tone( kPiezoPin, freq, duration );
if( alsoWait ) {
delay( duration );
alsoWait = false;
}
goto run_next_statement;
}
#endif /* ENABLE_TONES */
}
// returns 1 if the character is valid in a filename
static int isValidFnChar( char c )
{
if( c >= '0' && c <= '9' ) return 1; // number
if( c >= 'A' && c <= 'Z' ) return 1; // LETTER
if( c >= 'a' && c <= 'z' ) return 1; // letter (for completeness)
if( c == '_' ) return 1;
if( c == '+' ) return 1;
if( c == '.' ) return 1;
if( c == '~' ) return 1; // Window~1.txt
return 0;
}
unsigned char * filenameWord(void)
{
// SDL - I wasn't sure if this functionality existed above, so I figured i'd put it here
unsigned char * ret = txtpos;
expression_error = 0;
// make sure there are no quotes or spaces, search for valid characters
//while(*txtpos == SPACE || *txtpos == TAB || *txtpos == SQUOTE || *txtpos == DQUOTE ) txtpos++;
while( !isValidFnChar( *txtpos )) txtpos++;
ret = txtpos;
if( *ret == '\0' ) {
expression_error = 1;
return ret;
}
// now, find the next nonfnchar
txtpos++;
while( isValidFnChar( *txtpos )) txtpos++;
if( txtpos != ret ) *txtpos = '\0';
// set the error code if we've got no string
if( *ret == '\0' ) {
expression_error = 1;
}
return ret;
}
/***************************************************************************/
static void line_terminator(void)
{
outchar(NL);
outchar(CR);
}
/***********************************************************/
void setup()
{
#ifdef ARDUINO
Serial.begin(kConsoleBaud); // opens serial port
while( !Serial ); // for Leonardo
keyboard.begin(DataPin, IRQpin); //----------------------------------------------------------------------------------------------------------------
//Serial.println("Keyboard Test:");
Serial.println( sentinel );
printmsg(initmsg);
#ifdef ENABLE_FILEIO
initSD();
#ifdef ENABLE_AUTORUN
if( SD.exists( kAutorunFilename )) {
program_end = program_start;
fp = SD.open( kAutorunFilename );
inStream = kStreamFile;
inhibitOutput = true;
runAfterLoad = true;
}
#endif /* ENABLE_AUTORUN */
#endif /* ENABLE_FILEIO */
#ifdef ENABLE_EEPROM
#ifdef ENABLE_EAUTORUN
// read the first byte of the eeprom. if it's a number, assume it's a program we can load
int val = EEPROM.read(0);
if( val >= '0' && val <= '9' ) {
program_end = program_start;
inStream = kStreamEEProm;
eepos = 0;
inhibitOutput = true;
runAfterLoad = true;
}
#endif /* ENABLE_EAUTORUN */
#endif /* ENABLE_EEPROM */
#endif /* ARDUINO */
}
/***********************************************************/
static unsigned char breakcheck(void)
{
#ifdef ARDUINO
if(Serial.available())
return Serial.read() == CTRLC;
return 0;
#else
#ifdef __CONIO__
if(kbhit())
return getch() == CTRLC;
else
#endif
return 0;
#endif
}
/***********************************************************/
static int inchar()
{
int v;
#ifdef ARDUINO
switch( inStream ) {
case( kStreamFile ):
#ifdef ENABLE_FILEIO
v = fp.read();
if( v == NL ) v=CR; // file translate
if( !fp.available() ) {
fp.close();
goto inchar_loadfinish;
}
return v;
#else
#endif
break;
case( kStreamEEProm ):
#ifdef ENABLE_EEPROM
#ifdef ARDUINO
v = EEPROM.read( eepos++ );
if( v == '\0' ) {
goto inchar_loadfinish;
}
return v;
#endif
#else
inStream = kStreamSerial;
return NL;
#endif
break;
case( kStreamSerial ):
default:
while(1)
{
//----------- the following is the key modification -------------------------------------------------------------------------------------------------
//----------- where the code get the variables from the PS2 keyboard --------------------------------------------------------------------------------
//----------- and treat them as the ones from the PC keyboard ---------------------------------------------------------------------------------------
if (keyboard.available()) {
// read the next key
char c = keyboard.read();
//Serial.print(c);
return c;
}
//------------ end of modification -------------------------------------------------------------------------------------------------------------------
if(Serial.available())
return Serial.read();
}
}
inchar_loadfinish:
inStream = kStreamSerial;
inhibitOutput = false;
if( runAfterLoad ) {
runAfterLoad = false;
triggerRun = true;
}
return NL; // trigger a prompt.
#else
// otherwise. desktop!
int got = getchar();
// translation for desktop systems
if( got == LF ) got = CR;
return got;
#endif
}
/***********************************************************/
static void outchar(unsigned char c)
{
if( inhibitOutput ) return;
#ifdef ARDUINO
#ifdef ENABLE_FILEIO
if( outStream == kStreamFile ) {
// output to a file
fp.write( c );
}
else
#endif
#ifdef ARDUINO
#ifdef ENABLE_EEPROM
if( outStream == kStreamEEProm ) {
EEPROM.write( eepos++, c );
}
else
#endif /* ENABLE_EEPROM */
#endif /* ARDUINO */
Serial.write(c);
#else
putchar(c);
#endif
}
/***********************************************************/
/* SD Card helpers */
#if ARDUINO && ENABLE_FILEIO
static int initSD( void )
{
// if the card is already initialized, we just go with it.
// there is no support (yet?) for hot-swap of SD Cards. if you need to
// swap, pop the card, reset the arduino.)
if( sd_is_initialized == true ) return kSD_OK;
// due to the way the SD Library works, pin 10 always needs to be
// an output, even when your shield uses another line for CS
pinMode(10, OUTPUT); // change this to 53 on a mega
if( !SD.begin( kSD_CS )) {
// failed
printmsg( sderrormsg );
return kSD_Fail;
}
// success - quietly return 0
sd_is_initialized = true;
// and our file redirection flags
outStream = kStreamSerial;
inStream = kStreamSerial;
inhibitOutput = false;
return kSD_OK;
}
#endif
#if ENABLE_FILEIO
void cmd_Files( void )
{
File dir = SD.open( "/" );
dir.seek(0);
while( true ) {
File entry = dir.openNextFile();
if( !entry ) {
entry.close();
break;
}
// common header
printmsgNoNL( indentmsg );
printmsgNoNL( (const unsigned char *)entry.name() );
if( entry.isDirectory() ) {
printmsgNoNL( slashmsg );
}
if( entry.isDirectory() ) {
// directory ending
for( int i=strlen( entry.name()) ; i<16 ; i++ ) {
printmsgNoNL( spacemsg );
}
printmsgNoNL( dirextmsg );
}
else {
// file ending
for( int i=strlen( entry.name()) ; i<17 ; i++ ) {
printmsgNoNL( spacemsg );
}
printUnum( entry.size() );
}
line_terminator();
entry.close();
}
dir.close();
}
#endif
Your topic has been moved to a more suitable location on the forum.
Please use code tags when posting code so it's easier to read and copy; the forum software has corrupted your code.
Please post in English, use Google translate if needed.
Konunuz forumda daha uygun bir yere taşınmıştır.
Lütfen kodu gönderirken kod etiketlerini kullanın, böylece okunması ve kopyalanması daha kolay olur; forum yazılımı kodunuzu bozmuş.
Lütfen İngilizce yayınlayın, gerekirse Google çeviri kullanın.
Might be helpful if you tell us how much memory the program uses.
or what the 1820 line program is intended to do?
A BASIC interpreter… yikes.
a7
Can you remove the 0x80 from your char arrays?
EDIT: Oh, and as @srnet says, how much RAM does your code currently use. Is it a few bytes over, in which case a solution may be relatively easy, or is it 100's of bytes over, which may require more thought (or a different micro).
EDIT2: Which micro are you trying to build this for? A quick lashup with an Arduino UNO as the target device gave:
Sketch uses 9422 bytes (29%) of program storage space. Maximum is 32256 bytes.
Global variables use 1540 bytes (75%) of dynamic memory, leaving 508 bytes for local variables. Maximum is 2048 bytes.
Basic'te bir mikrodenetleyici programlamak istiyorsanız:
https://www.mouser.de/ProductDetail/Parallax/BS2-IC
If you want to program in basic
card = arduino uno memory used = %75
@markd833 as you gave as an example.
What I want to do is a basic computer with arduino uno, but arduino uno memory is not enough and there is a library for this code, if you want, I can share it.
Þú virðist ekki fá það. ÞÚ vilt aðstoð. Það þýðir að ÞÚ verður að láta þýða tyrkneska textann á ensku með google-translate!
Bild auf dem Fernseher
75 Euro
759,11 türkische Lira
Please use English. I've translated the opening post as a favour but you can't expect us to use google translate every time that you post.
Lütfen İngilizce kullanın. Açılış gönderisini bir iyilik olarak çevirdim ama her gönderi yaptığınızda google translate kullanmamızı bekleyemezsiniz.
And I don't know anything about coding
Please quote whomever you replying to. Anyone reading this thread has no clue who "you" refers to.
Or use the Reply button alongside the post that you are replying to rather than the Reply button at the end of the topic
You have a computer which you use for posting here in the forum.
You can use this computer to program the Arduino-Uno directly with C++.
What is the reason why you want to put a basic-compiler on such a small microcontroller like an Arduino Uno?
If you want a small cheap computer buy a raspberry 400. You just need a HDMI-cable and then plug it into any TV-screen that has a HDMI-input and your small computer is up and running.
best regards Stefan
If your really need to put a Basic interpreter on a micro controller for some reason, get one with more RAM - there are some much more capable ones than the Uno for tiny money.
That they are both labeled "Reply" is at the least, confusing. Perhaps, "Reply to post" and "Reply to thread"
Their tooltips tell you plainly which each of them do and they are also different in appearance