-
-
Save andypiper/1793460 to your computer and use it in GitHub Desktop.
Nanode Tiny Basic with temperature sensing by @ceejay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TinyBASIC.cpp : An implementation of TinyBASIC in C | |
// | |
// Author : Mike Field - hamster@snap.net.nz | |
// | |
// Based on TinyBasic for 68000, by Gordon Brandly | |
// (see http://members.shaw.ca/gbrandly/68ktinyb.html) | |
// | |
// which itself was Derived from Palo Alto Tiny BASIC as | |
// published in the May 1976 issue of Dr. Dobb's Journal. | |
// | |
// 0.03 21/01/2011 : Added INPUT routine | |
// : Reorganised memory layout | |
// : Expanded all error messages | |
// : Break key added | |
// : Removed the calls to printf (left by debugging) | |
#ifndef ARDUINO | |
#include "stdafx.h" | |
#include <conio.h> | |
#endif | |
// ASCII Characters | |
#define CR '\r' | |
#define NL '\n' | |
#define TAB '\t' | |
#define BELL '\b' | |
#define DEL '\177' | |
#define SPACE ' ' | |
#define CTRLC 0x03 | |
#define CTRLH 0x08 | |
#define CTRLS 0x13 | |
#define CTRLX 0x18 | |
typedef short unsigned LINENUM; | |
/***********************************************************/ | |
// Keyword table and constants - the last character has 0x80 added to it | |
static unsigned char keywords[] = { | |
'L','I','S','T'+0x80, | |
'L','O','A','D'+0x80, | |
'N','E','W'+0x80, | |
'R','U','N'+0x80, | |
'S','A','V','E'+0x80, | |
'N','E','X','T'+0x80, | |
'L','E','T'+0x80, | |
'I','F'+0x80, | |
'G','O','T','O'+0x80, | |
'G','O','S','U','B'+0x80, | |
'R','E','T','U','R','N'+0x80, | |
'R','E','M'+0x80, | |
'F','O','R'+0x80, | |
'I','N','P','U','T'+0x80, | |
'P','R','I','N','T'+0x80, | |
'P','O','K','E'+0x80, | |
'S','T','O','P'+0x80, | |
'B','Y','E'+0x80, | |
'D','O','U','T'+0x80, | |
'A','O','U','T'+0x80, | |
'S','L','E','E','P'+0x80, | |
0 | |
}; | |
#define KW_LIST 0 | |
#define KW_LOAD 1 | |
#define KW_NEW 2 | |
#define KW_RUN 3 | |
#define KW_SAVE 4 | |
#define KW_NEXT 5 | |
#define KW_LET 6 | |
#define KW_IF 7 | |
#define KW_GOTO 8 | |
#define KW_GOSUB 9 | |
#define KW_RETURN 10 | |
#define KW_REM 11 | |
#define KW_FOR 12 | |
#define KW_INPUT 13 | |
#define KW_PRINT 14 | |
#define KW_POKE 15 | |
#define KW_STOP 16 | |
#define KW_BYE 17 | |
#define KW_DOUT 18 | |
#define KW_AOUT 19 | |
#define KW_SLEEP 20 | |
#define KW_DEFAULT 21 | |
struct stack_for_frame { | |
char frame_type; | |
char for_var; | |
short int terminal; | |
short int step; | |
unsigned char *current_line; | |
unsigned char *txtpos; | |
}; | |
struct stack_gosub_frame { | |
char frame_type; | |
unsigned char *current_line; | |
unsigned char *txtpos; | |
}; | |
static unsigned char func_tab[] = { | |
'P','E','E','K'+0x80, | |
'A','B','S'+0x80, | |
'D','I','N'+0x80, | |
'A','I','N'+0x80, | |
'H','I','G','H'+0x80, | |
'L','O','W'+0x80, | |
'R','E','A','D','T','E','M','P'+0x80, | |
0 | |
}; | |
#define FUNC_PEEK 0 | |
#define FUNC_ABS 1 | |
#define FUNC_DIN 2 | |
#define FUNC_AIN 3 | |
#define FUNC_HIGH 4 | |
#define FUNC_LOW 5 | |
#define FUNC_READTEMP 6 | |
#define FUNC_UNKNOWN 7 | |
static unsigned char to_tab[] = { | |
'T','O'+0x80, | |
0 | |
}; | |
static unsigned char step_tab[] = { | |
'S','T','E','P'+0x80, | |
0 | |
}; | |
static unsigned char relop_tab[] = { | |
'>','='+0x80, | |
'<','>'+0x80, | |
'>'+0x80, | |
'='+0x80, | |
'<','='+0x80, | |
'<'+0x80, | |
0 | |
}; | |
#define RELOP_GE 0 | |
#define RELOP_NE 1 | |
#define RELOP_GT 2 | |
#define RELOP_EQ 3 | |
#define RELOP_LE 4 | |
#define RELOP_LT 5 | |
#define RELOP_UNKNOWN 6 | |
#define VAR_SIZE sizeof(short int) // Size of variables in bytes | |
static unsigned char memory[1400]; | |
static unsigned char *txtpos,*list_line; | |
static unsigned char expression_error; | |
static unsigned char *tempsp; | |
static unsigned char *stack_limit; | |
static unsigned char *program_start; | |
static unsigned char *program_end; | |
static unsigned char *stack; // Software stack for things that should go on the CPU stack | |
static unsigned char *variables_table; | |
static unsigned char *current_line; | |
static unsigned char *sp; | |
#define STACK_GOSUB_FLAG 'G' | |
#define STACK_FOR_FLAG 'F' | |
static unsigned char table_index; | |
static LINENUM linenum; | |
static const unsigned char okmsg[] = "OK"; | |
static const unsigned char badlinemsg[] = "Invalid line number"; | |
static const unsigned char invalidexprmsg[] = "Invalid expression"; | |
static const unsigned char syntaxmsg[] = "Syntax Error"; | |
static const unsigned char badinputmsg[] = "\nBad number"; | |
static const unsigned char nomemmsg[] = "Not enough memory!"; | |
static const unsigned char initmsg[] = "NanoBasic in C"; | |
static const unsigned char memorymsg[] = " bytes free."; | |
static const unsigned char breakmsg[] = "break!"; | |
static const unsigned char stackstuffedmsg[] = "Stack is stuffed!\n"; | |
static const unsigned char unimplimentedmsg[] = "Unimplemented"; | |
static const unsigned char backspacemsg[] = "\b \b"; | |
static int inchar(void); | |
static void outchar(unsigned char c); | |
static void line_terminator(void); | |
static short int expression(void); | |
static unsigned char breakcheck(void); | |
/***************************************************************************/ | |
static void ignore_blanks(void) | |
{ | |
while(*txtpos == SPACE || *txtpos == TAB) | |
txtpos++; | |
} | |
/***************************************************************************/ | |
static void scantable(unsigned char *table) | |
{ | |
int i = 0; | |
ignore_blanks(); | |
table_index = 0; | |
while(1) | |
{ | |
// Run out of table entries? | |
if(table[0] == 0) | |
return; | |
// Do we match this character? | |
if(txtpos[i] == table[0]) | |
{ | |
i++; | |
table++; | |
} | |
else | |
{ | |
// do we match the last character of keywork (with 0x80 added)? If so, return | |
if(txtpos[i]+0x80 == table[0]) | |
{ | |
txtpos += i+1; // Advance the pointer to following the keyword | |
ignore_blanks(); | |
return; | |
} | |
// Forward to the end of this keyword | |
while((table[0] & 0x80) == 0) | |
table++; | |
// Now move on to the first character of the next word, and reset the position index | |
table++; | |
table_index++; | |
i = 0; | |
} | |
} | |
} | |
/***************************************************************************/ | |
static void pushb(unsigned char b) | |
{ | |
sp--; | |
*sp = b; | |
} | |
/***************************************************************************/ | |
static unsigned char popb() | |
{ | |
unsigned char b; | |
b = *sp; | |
sp++; | |
return b; | |
} | |
/***************************************************************************/ | |
static void printnum(int num) | |
{ | |
int digits = 0; | |
if(num < 0) | |
{ | |
num = -num; | |
outchar('-'); | |
} | |
do { | |
pushb(num%10+'0'); | |
num = num/10; | |
digits++; | |
} | |
while (num > 0); | |
while(digits > 0) | |
{ | |
outchar(popb()); | |
digits--; | |
} | |
} | |
/***************************************************************************/ | |
static unsigned short testnum(void) | |
{ | |
unsigned short num = 0; | |
ignore_blanks(); | |
while(*txtpos>= '0' && *txtpos <= '9' ) | |
{ | |
// Trap overflows | |
if(num >= 0xFFFF/10) | |
{ | |
num = 0xFFFF; | |
break; | |
} | |
num = num *10 + *txtpos - '0'; | |
txtpos++; | |
} | |
return num; | |
} | |
/***************************************************************************/ | |
unsigned char check_statement_end(void) | |
{ | |
ignore_blanks(); | |
return (*txtpos == NL) || (*txtpos == ':'); | |
} | |
/***************************************************************************/ | |
static void printmsgNoNL(const unsigned char *msg) | |
{ | |
while(*msg) | |
{ | |
outchar(*msg); | |
msg++; | |
} | |
} | |
/***************************************************************************/ | |
static unsigned char print_quoted_string(void) | |
{ | |
int i=0; | |
unsigned char delim = *txtpos; | |
if(delim != '"' && delim != '\'') | |
return 0; | |
txtpos++; | |
// Check we have a closing delimiter | |
while(txtpos[i] != delim) | |
{ | |
if(txtpos[i] == NL) | |
return 0; | |
i++; | |
} | |
// Print the characters | |
while(*txtpos != delim) | |
{ | |
outchar(*txtpos); | |
txtpos++; | |
} | |
txtpos++; // Skip over the last delimiter | |
ignore_blanks(); | |
return 1; | |
} | |
/***************************************************************************/ | |
static void printmsg(const unsigned char *msg) | |
{ | |
printmsgNoNL(msg); | |
line_terminator(); | |
} | |
/***************************************************************************/ | |
unsigned char getln(char prompt) | |
{ | |
outchar(prompt); | |
txtpos = program_end+sizeof(LINENUM); | |
while(1) | |
{ | |
char c = inchar(); | |
switch(c) | |
{ | |
case CR: | |
case NL: | |
line_terminator(); | |
// Terminate all strings with a NL | |
txtpos[0] = NL; | |
return 1; | |
case CTRLC: | |
return 0; | |
case CTRLH: | |
if(txtpos == program_end) | |
break; | |
txtpos--; | |
printmsgNoNL(backspacemsg); | |
break; | |
default: | |
// We need to leave at least one space to allow us to shuffle the line into order | |
if(txtpos == sp-2) | |
outchar(BELL); | |
else | |
{ | |
txtpos[0] = c; | |
txtpos++; | |
outchar(c); | |
} | |
} | |
} | |
} | |
/***************************************************************************/ | |
static unsigned char *findline(void) | |
{ | |
unsigned char *line = program_start; | |
while(1) | |
{ | |
if(line == program_end) | |
return line; | |
if(((LINENUM *)line)[0] >= linenum) | |
return line; | |
// Add the line length onto the current address, to get to the next line; | |
line += line[sizeof(LINENUM)]; | |
} | |
} | |
/***************************************************************************/ | |
static void toUppercaseBuffer(void) | |
{ | |
unsigned char *c = program_end+sizeof(LINENUM); | |
unsigned char quote = 0; | |
while(*c != NL) | |
{ | |
// Are we in a quoted string? | |
if(*c == quote) | |
quote = 0; | |
else if(*c == '"' || *c == '\'') | |
quote = *c; | |
else if(quote == 0 && *c >= 'a' && *c <= 'z') | |
*c = *c + 'A' - 'a'; | |
c++; | |
} | |
} | |
/***************************************************************************/ | |
void printline() | |
{ | |
LINENUM line_num; | |
line_num = *((LINENUM *)(list_line)); | |
list_line += sizeof(LINENUM) + sizeof(char); | |
// Output the line */ | |
printnum(line_num); | |
outchar(' '); | |
while(*list_line != NL) | |
{ | |
outchar(*list_line); | |
list_line++; | |
} | |
list_line++; | |
line_terminator(); | |
} | |
/***************************************************************************/ | |
static short int expr4(void) | |
{ | |
short int a = 0; | |
if(*txtpos == '0') | |
{ | |
txtpos++; | |
a = 0; | |
goto success; | |
} | |
if(*txtpos >= '1' && *txtpos <= '9') | |
{ | |
do { | |
a = a*10 + *txtpos - '0'; | |
txtpos++; | |
} while(*txtpos >= '0' && *txtpos <= '9'); | |
goto success; | |
} | |
// Is it a function or variable reference? | |
if(txtpos[0] >= 'A' && txtpos[0] <= 'Z') | |
{ | |
// Is it a variable reference (single alpha) | |
if(txtpos[1] < 'A' || txtpos[1] > 'Z') | |
{ | |
a = ((short int *)variables_table)[*txtpos - 'A']; | |
txtpos++; | |
goto success; | |
} | |
// Is it a function with a single parameter | |
scantable(func_tab); | |
if(table_index == FUNC_UNKNOWN) | |
goto expr4_error; | |
unsigned char f = table_index; | |
// Pseudo Functions added by DCJ for things that need no parms | |
if (f == FUNC_HIGH) { | |
a=1; | |
goto success; | |
} | |
if (f == FUNC_LOW) { | |
a=0; | |
goto success; | |
} | |
if (f == FUNC_READTEMP) { | |
a = getTempInt(); | |
goto success; | |
} | |
if(*txtpos != '(') | |
goto expr4_error; | |
txtpos++; | |
a = expression(); | |
if(*txtpos != ')') | |
goto expr4_error; | |
txtpos++; | |
switch(f) | |
{ | |
case FUNC_PEEK: | |
a = memory[a]; | |
goto success; | |
case FUNC_ABS: | |
if(a < 0) | |
a = -a; | |
goto success; | |
case FUNC_DIN: | |
pinMode(a, INPUT); | |
a = digitalRead(a); | |
goto success; | |
case FUNC_AIN: | |
pinMode(a, INPUT); | |
a = analogRead(a); | |
goto success; | |
} | |
} | |
if(*txtpos == '(') | |
{ | |
txtpos++; | |
a = expression(); | |
if(*txtpos != ')') | |
goto expr4_error; | |
txtpos++; | |
goto success; | |
} | |
expr4_error: | |
expression_error = 1; | |
success: | |
ignore_blanks(); | |
return a; | |
} | |
/***************************************************************************/ | |
static short int expr3(void) | |
{ | |
short int a,b; | |
a = expr4(); | |
while(1) | |
{ | |
if(*txtpos == '*') | |
{ | |
txtpos++; | |
b = expr4(); | |
a *= b; | |
} | |
else if(*txtpos == '/') | |
{ | |
txtpos++; | |
b = expr4(); | |
if(b != 0) | |
a /= b; | |
else | |
expression_error = 1; | |
} | |
else | |
return a; | |
} | |
} | |
/***************************************************************************/ | |
static short int expr2(void) | |
{ | |
short int a,b; | |
if(*txtpos == '-' || *txtpos == '+') | |
a = 0; | |
else | |
a = expr3(); | |
while(1) | |
{ | |
if(*txtpos == '-') | |
{ | |
txtpos++; | |
b = expr3(); | |
a -= b; | |
} | |
else if(*txtpos == '+') | |
{ | |
txtpos++; | |
b = expr3(); | |
a += b; | |
} | |
else | |
return a; | |
} | |
} | |
/***************************************************************************/ | |
static short int expression(void) | |
{ | |
short int a,b; | |
a = expr2(); | |
// Check if we have an error | |
if(expression_error) return a; | |
scantable(relop_tab); | |
if(table_index == RELOP_UNKNOWN) | |
return a; | |
switch(table_index) | |
{ | |
case RELOP_GE: | |
b = expr2(); | |
if(a >= b) return 1; | |
break; | |
case RELOP_NE: | |
b = expr2(); | |
if(a != b) return 1; | |
break; | |
case RELOP_GT: | |
b = expr2(); | |
if(a > b) return 1; | |
break; | |
case RELOP_EQ: | |
b = expr2(); | |
if(a == b) return 1; | |
break; | |
case RELOP_LE: | |
b = expr2(); | |
if(a <= b) return 1; | |
break; | |
case RELOP_LT: | |
b = expr2(); | |
if(a < b) return 1; | |
break; | |
} | |
return 0; | |
} | |
/***************************************************************************/ | |
void loop() | |
{ | |
unsigned char *start; | |
unsigned char *newEnd; | |
unsigned char linelen; | |
variables_table = memory; | |
program_start = memory + 27*VAR_SIZE; | |
program_end = program_start; | |
sp = memory+sizeof(memory); // Needed for printnum | |
printmsg(initmsg); | |
printnum(sp-program_end); | |
printmsg(memorymsg); | |
// Handle the default ethernet things for nanode | |
// if (ether.dhcpExpired()) { | |
// ether.dhcpSetup(); | |
// } | |
// word len = ether.packetLoop(ether.packetReceive()); | |
warmstart: | |
// this signifies that it is running in 'direct' mode. | |
current_line = 0; | |
sp = memory+sizeof(memory); | |
printmsg(okmsg); | |
prompt: | |
while(!getln('>')) | |
line_terminator(); | |
toUppercaseBuffer(); | |
txtpos = program_end+sizeof(unsigned short); | |
// Find the end of the freshly entered line | |
while(*txtpos != NL) | |
txtpos++; | |
// Move it to the end of program_memory | |
{ | |
unsigned char *dest; | |
dest = sp-1; | |
while(1) | |
{ | |
*dest = *txtpos; | |
if(txtpos == program_end+sizeof(unsigned short)) | |
break; | |
dest--; | |
txtpos--; | |
} | |
txtpos = dest; | |
} | |
// Now see if we have a line number | |
linenum = testnum(); | |
ignore_blanks(); | |
if(linenum == 0) | |
goto direct; | |
if(linenum == 0xFFFF) | |
goto badline; | |
// Find the length of what is left, including the (yet-to-be-populated) line header | |
linelen = 0; | |
while(txtpos[linelen] != NL) | |
linelen++; | |
linelen++; // Include the NL in the line length | |
linelen += sizeof(unsigned short)+sizeof(char); // Add space for the line number and line length | |
// Now we have the number, add the line header. | |
txtpos -= 3; | |
*((unsigned short *)txtpos) = linenum; | |
txtpos[sizeof(LINENUM)] = linelen; | |
// Merge it into the rest of the program | |
start = findline(); | |
// If a line with that number exists, then remove it | |
if(start != program_end && *((LINENUM *)start) == linenum) | |
{ | |
unsigned char *dest, *from; | |
unsigned tomove; | |
from = start + start[sizeof(LINENUM)]; | |
dest = start; | |
tomove = program_end - from; | |
while( tomove > 0) | |
{ | |
*dest = *from; | |
from++; | |
dest++; | |
tomove--; | |
} | |
program_end = dest; | |
} | |
if(txtpos[sizeof(LINENUM)+sizeof(char)] == NL) // If the line has no txt, it was just a delete | |
goto prompt; | |
// Make room for the new line, either all in one hit or lots of little shuffles | |
while(linelen > 0) | |
{ | |
unsigned int tomove; | |
unsigned char *from,*dest; | |
unsigned int space_to_make; | |
space_to_make = txtpos - program_end; | |
if(space_to_make > linelen) | |
space_to_make = linelen; | |
newEnd = program_end+space_to_make; | |
tomove = program_end - start; | |
// Source and destination - as these areas may overlap we need to move bottom up | |
from = program_end; | |
dest = newEnd; | |
while(tomove > 0) | |
{ | |
from--; | |
dest--; | |
*dest = *from; | |
tomove--; | |
} | |
// Copy over the bytes into the new space | |
for(tomove = 0; tomove < space_to_make; tomove++) | |
{ | |
*start = *txtpos; | |
txtpos++; | |
start++; | |
linelen--; | |
} | |
program_end = newEnd; | |
} | |
goto prompt; | |
unimplemented: | |
printmsg(unimplimentedmsg); | |
goto prompt; | |
badline: | |
printmsg(badlinemsg); | |
goto prompt; | |
invalidexpr: | |
printmsg(invalidexprmsg); | |
goto prompt; | |
syntaxerror: | |
printmsg(syntaxmsg); | |
if(current_line != (void *)0) | |
{ | |
unsigned char tmp = *txtpos; | |
if(*txtpos != NL) | |
*txtpos = '^'; | |
list_line = current_line; | |
printline(); | |
*txtpos = tmp; | |
} | |
line_terminator(); | |
goto prompt; | |
stackstuffed: | |
printmsg(stackstuffedmsg); | |
goto warmstart; | |
nomem: | |
printmsg(nomemmsg); | |
goto warmstart; | |
run_next_statement: | |
while(*txtpos == ':') | |
txtpos++; | |
ignore_blanks(); | |
if(*txtpos == NL) | |
goto execnextline; | |
goto interperateAtTxtpos; | |
direct: | |
txtpos = program_end+sizeof(LINENUM); | |
if(*txtpos == NL) | |
goto prompt; | |
interperateAtTxtpos: | |
if(breakcheck()) | |
{ | |
printmsg(breakmsg); | |
goto warmstart; | |
} | |
scantable(keywords); | |
ignore_blanks(); | |
switch(table_index) | |
{ | |
case KW_LIST: | |
goto list; | |
case KW_LOAD: | |
goto unimplemented; ///////////////// | |
case KW_NEW: | |
if(txtpos[0] != NL) | |
goto syntaxerror; | |
program_end = program_start; | |
goto prompt; | |
case KW_RUN: | |
current_line = program_start; | |
goto execline; | |
case KW_SAVE: | |
goto unimplemented; ////////////////////// | |
case KW_NEXT: | |
goto next; | |
case KW_LET: | |
goto assignment; | |
case KW_IF: | |
{ | |
short int val; | |
expression_error = 0; | |
val = expression(); | |
if(expression_error || *txtpos == NL) | |
goto invalidexpr; | |
if(val != 0) | |
goto interperateAtTxtpos; | |
goto execnextline; | |
} | |
case KW_GOTO: | |
expression_error = 0; | |
linenum = expression(); | |
if(expression_error || *txtpos != NL) | |
goto invalidexpr; | |
current_line = findline(); | |
goto execline; | |
case KW_GOSUB: | |
goto gosub; | |
case KW_RETURN: | |
goto gosub_return; | |
case KW_REM: | |
goto execnextline; // Ignore line completely | |
case KW_FOR: | |
goto forloop; | |
case KW_INPUT: | |
goto input; | |
case KW_PRINT: | |
goto print; | |
case KW_POKE: | |
goto poke; | |
case KW_STOP: | |
// This is the easy way to end - set the current line to the end of program attempt to run it | |
if(txtpos[0] != NL) | |
goto syntaxerror; | |
current_line = program_end; | |
goto execline; | |
case KW_BYE: | |
// Leave the basic interperater | |
return; | |
case KW_DOUT: | |
goto dout; | |
case KW_AOUT: | |
goto aout; | |
case KW_SLEEP: | |
goto sleep; | |
case KW_DEFAULT: | |
goto assignment; | |
default: | |
break; | |
} | |
execnextline: | |
if(current_line == (void *)0) // Processing direct commands? | |
goto prompt; | |
current_line += current_line[sizeof(LINENUM)]; | |
execline: | |
if(current_line == program_end) // Out of lines to run | |
goto warmstart; | |
txtpos = current_line+sizeof(LINENUM)+sizeof(char); | |
goto interperateAtTxtpos; | |
input: | |
{ | |
unsigned char isneg=0; | |
unsigned char *temptxtpos; | |
short int *var; | |
ignore_blanks(); | |
if(*txtpos < 'A' || *txtpos > 'Z') | |
goto syntaxerror; | |
var = ((short int *)variables_table)+*txtpos-'A'; | |
txtpos++; | |
if(!check_statement_end()) | |
goto syntaxerror; | |
again: | |
temptxtpos = txtpos; | |
if(!getln('?')) | |
goto warmstart; | |
// Go to where the buffer is read | |
txtpos = program_end+sizeof(LINENUM); | |
if(*txtpos == '-') | |
{ | |
isneg = 1; | |
txtpos++; | |
} | |
*var = 0; | |
do { | |
*var = *var*10 + *txtpos - '0'; | |
txtpos++; | |
} while(*txtpos >= '0' && *txtpos <= '9'); | |
ignore_blanks(); | |
if(*txtpos != NL) | |
{ | |
printmsg(badinputmsg); | |
goto again; | |
} | |
if(isneg) | |
*var = -*var; | |
goto run_next_statement; | |
} | |
forloop: | |
{ | |
unsigned char var; | |
short int initial, step, terminal; | |
if(*txtpos < 'A' || *txtpos > 'Z') | |
goto syntaxerror; | |
var = *txtpos; | |
txtpos++; | |
scantable(relop_tab); | |
if(table_index != RELOP_EQ) | |
goto syntaxerror; | |
expression_error = 0; | |
initial = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
scantable(to_tab); | |
if(table_index != 0) | |
goto syntaxerror; | |
terminal = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
scantable(step_tab); | |
if(table_index == 0) | |
{ | |
step = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
} | |
else | |
step = 1; | |
if(!check_statement_end()) | |
goto syntaxerror; | |
if(!expression_error && *txtpos == NL) | |
{ | |
struct stack_for_frame *f; | |
if(sp + sizeof(struct stack_for_frame) < stack_limit) | |
goto nomem; | |
sp -= sizeof(struct stack_for_frame); | |
f = (struct stack_for_frame *)sp; | |
((short int *)variables_table)[var-'A'] = initial; | |
f->frame_type = STACK_FOR_FLAG; | |
f->for_var = var; | |
f->terminal = terminal; | |
f->step = step; | |
f->txtpos = txtpos; | |
f->current_line = current_line; | |
goto run_next_statement; | |
} | |
} | |
goto syntaxerror; | |
gosub: | |
expression_error = 0; | |
linenum = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
if(!expression_error && *txtpos == NL) | |
{ | |
struct stack_gosub_frame *f; | |
if(sp + sizeof(struct stack_gosub_frame) < stack_limit) | |
goto nomem; | |
sp -= sizeof(struct stack_gosub_frame); | |
f = (struct stack_gosub_frame *)sp; | |
f->frame_type = STACK_GOSUB_FLAG; | |
f->txtpos = txtpos; | |
f->current_line = current_line; | |
current_line = findline(); | |
goto execline; | |
} | |
goto syntaxerror; | |
next: | |
// Fnd the variable name | |
ignore_blanks(); | |
if(*txtpos < 'A' || *txtpos > 'Z') | |
goto syntaxerror; | |
txtpos++; | |
if(!check_statement_end()) | |
goto syntaxerror; | |
gosub_return: | |
// Now walk up the stack frames and find the frame we want, if present | |
tempsp = sp; | |
while(tempsp < memory+sizeof(memory)-1) | |
{ | |
switch(tempsp[0]) | |
{ | |
case STACK_GOSUB_FLAG: | |
if(table_index == KW_RETURN) | |
{ | |
struct stack_gosub_frame *f = (struct stack_gosub_frame *)tempsp; | |
current_line = f->current_line; | |
txtpos = f->txtpos; | |
sp += sizeof(struct stack_gosub_frame); | |
goto run_next_statement; | |
} | |
// This is not the loop you are looking for... so Walk back up the stack | |
tempsp += sizeof(struct stack_gosub_frame); | |
break; | |
case STACK_FOR_FLAG: | |
// Flag, Var, Final, Step | |
if(table_index == KW_NEXT) | |
{ | |
struct stack_for_frame *f = (struct stack_for_frame *)tempsp; | |
// Is the the variable we are looking for? | |
if(txtpos[-1] == f->for_var) | |
{ | |
short int *varaddr = ((short int *)variables_table) + txtpos[-1] - 'A'; | |
*varaddr = *varaddr + f->step; | |
// Use a different test depending on the sign of the step increment | |
if((f->step > 0 && *varaddr <= f->terminal) || (f->step < 0 && *varaddr >= f->terminal)) | |
{ | |
// We have to loop so don't pop the stack | |
txtpos = f->txtpos; | |
current_line = f->current_line; | |
goto run_next_statement; | |
} | |
// We've run to the end of the loop. drop out of the loop, popping the stack | |
sp = tempsp + sizeof(struct stack_for_frame); | |
goto run_next_statement; | |
} | |
} | |
// This is not the loop you are looking for... so Walk back up the stack | |
tempsp += sizeof(struct stack_for_frame); | |
break; | |
default: | |
goto stackstuffed; | |
} | |
} | |
// Didn't find the variable we've been looking for | |
goto syntaxerror; | |
assignment: | |
{ | |
short int value; | |
short int *var; | |
if(*txtpos < 'A' || *txtpos > 'Z') | |
goto syntaxerror; | |
var = (short int *)variables_table + *txtpos - 'A'; | |
txtpos++; | |
ignore_blanks(); | |
if (*txtpos != '=') | |
goto syntaxerror; | |
txtpos++; | |
ignore_blanks(); | |
expression_error = 0; | |
value = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
// Check that we are at the end of the statement | |
if(!check_statement_end()) | |
goto syntaxerror; | |
*var = value; | |
} | |
goto run_next_statement; | |
sleep: | |
{ | |
short int value; | |
expression_error = 0; | |
value = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
delay(value); | |
} | |
goto run_next_statement; | |
dout: | |
{ | |
short int value; | |
short int var; | |
// OK - we only handle pins 0 to 9 - but nanode uses 10 upwards for other stuff (mostly ;-) | |
if(*txtpos < '0' || *txtpos > '9') | |
goto syntaxerror; | |
var = *txtpos - '0'; | |
txtpos++; | |
ignore_blanks(); | |
if (*txtpos != '=') | |
goto syntaxerror; | |
txtpos++; | |
ignore_blanks(); | |
expression_error = 0; | |
value = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
// Check that we are at the end of the statement | |
if(!check_statement_end()) | |
goto syntaxerror; | |
pinMode(var, OUTPUT); | |
if(value < 0 || value > 1) | |
goto syntaxerror; | |
if(value == 1) | |
digitalWrite(var, HIGH); | |
else | |
digitalWrite(var, LOW); | |
} | |
goto run_next_statement; | |
aout: | |
{ | |
short int value; | |
short int var; | |
// OK - we only handle pins 0 to 9 - but nanode uses 10 upwards for other stuff (mostly ;-) | |
if(*txtpos < '0' || *txtpos > '9') | |
goto syntaxerror; | |
var = *txtpos - '0'; | |
txtpos++; | |
ignore_blanks(); | |
if (*txtpos != '=') | |
goto syntaxerror; | |
txtpos++; | |
ignore_blanks(); | |
expression_error = 0; | |
value = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
// Check that we are at the end of the statement | |
if(!check_statement_end()) | |
goto syntaxerror; | |
pinMode(var, OUTPUT); | |
if (value < 0 || value > 255) | |
goto syntaxerror; | |
analogWrite(var, value); | |
} | |
goto run_next_statement; | |
poke: | |
{ | |
short int value; | |
unsigned char *address; | |
// Work out where to put it | |
expression_error = 0; | |
value = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
address = (unsigned char *)value; | |
// check for a comma | |
ignore_blanks(); | |
if (*txtpos != ',') | |
goto syntaxerror; | |
txtpos++; | |
ignore_blanks(); | |
// Now get the value to assign | |
expression_error = 0; | |
value = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
// printf("Poke %p value %i\n",address, (unsigned char)value); | |
// Check that we are at the end of the statement | |
if(!check_statement_end()) | |
goto syntaxerror; | |
} | |
goto run_next_statement; | |
list: | |
linenum = testnum(); // Retuns 0 if no line found. | |
// Should be EOL | |
if(txtpos[0] != NL) | |
goto syntaxerror; | |
// Find the line | |
list_line = findline(); | |
while(list_line != program_end) | |
printline(); | |
goto warmstart; | |
print: | |
// If we have an empty list then just put out a NL | |
if(*txtpos == ':' ) | |
{ | |
line_terminator(); | |
txtpos++; | |
goto run_next_statement; | |
} | |
if(*txtpos == NL) | |
{ | |
goto execnextline; | |
} | |
while(1) | |
{ | |
ignore_blanks(); | |
if(print_quoted_string()) | |
{ | |
; | |
} | |
else if(*txtpos == '"' || *txtpos == '\'') | |
goto syntaxerror; | |
else | |
{ | |
short int e; | |
expression_error = 0; | |
e = expression(); | |
if(expression_error) | |
goto invalidexpr; | |
printnum(e); | |
} | |
// At this point we have three options, a comma or a new line | |
if(*txtpos == ',') | |
txtpos++; // Skip the comma and move onto the next | |
else if(txtpos[0] == ';' && (txtpos[1] == NL || txtpos[1] == ':')) | |
{ | |
txtpos++; // This has to be the end of the print - no newline | |
break; | |
} | |
else if(check_statement_end()) | |
{ | |
line_terminator(); // The end of the print statement | |
break; | |
} | |
else | |
goto syntaxerror; | |
} | |
goto run_next_statement; | |
} | |
/***********************************************************/ | |
static void line_terminator(void) { | |
outchar(NL); | |
outchar(CR); | |
} | |
/***********************************************************/ | |
static unsigned char breakcheck(void) { | |
#ifdef ARDUINO | |
if(Serial.available()) | |
return Serial.read() == CTRLC; | |
return 0; | |
#else | |
if(kbhit()) | |
return getch() == CTRLC; | |
else | |
return 0; | |
#endif | |
} | |
/***********************************************************/ | |
static int inchar() { | |
#ifdef ARDUINO | |
while(1) { | |
if(Serial.available()) | |
return Serial.read(); | |
} | |
#else | |
return getch(); | |
#endif | |
} | |
/***********************************************************/ | |
static void outchar(unsigned char c) { | |
#ifdef ARDUINO | |
Serial.write(c); | |
#else | |
putch(c); | |
#endif | |
} | |
/***********************************************************/ | |
#ifdef ARDUINO | |
void setup() { | |
Serial.begin(115200); // opens serial port, sets data rate to 115200 bps | |
} | |
#endif | |
/***********************************************************/ | |
#ifndef ARDUINO | |
int main() { | |
while(1) | |
loop(); | |
} | |
#endif | |
/**********************************************************/ | |
// Extra functions added by DC-J for grins. | |
int getTempInt() { | |
int result = (int) (getTempFloat() + 0.5); | |
return result; | |
} | |
float getTempFloat() { | |
float result; | |
int res; | |
static uint8_t saveADMUX, saveADCSRA; | |
saveADMUX = ADMUX; | |
saveADCSRA = ADCSRA; | |
// Read temperature sensor against 1.1V reference | |
ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3); | |
delay(10); // Wait for Vref to settle | |
ADCSRA |= _BV(ADSC); // Convert | |
while (bit_is_set(ADCSRA,ADSC)); | |
res = ADCL; | |
res |= ADCH<<8; | |
result = (float) res; | |
//result = ((result - 125) * 1075)/10; | |
result = ((result - 336.59) / 1.17); | |
ADMUX = saveADMUX; | |
ADCSRA = saveADCSRA; | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment