Skip to content

Instantly share code, notes, and snippets.

@andreer
Last active March 4, 2022 00:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andreer/e6bf7798df38cc79ac9473b431e157d6 to your computer and use it in GitHub Desktop.
Save andreer/e6bf7798df38cc79ac9473b431e157d6 to your computer and use it in GitHub Desktop.
// This is a slightly edited version of uLisp for the Adafruit nrf52840 express board,
// connected to a 4.4 inch 320x240px SHARP memory display.
// It uses the "Adafruit SHARP Memory Display" arduino library - tested with version 1.1.0 only.
// Display was wired using the breakout board as in this [1] tutorial, but I connected the larger
// 4.4 inch LS044Q7DH01 display instead.
// There are way too many calls to tft.refresh() which makes it slow - deciding when to refresh
// is probably better left to the user code.
// WARNING: The screen should be refreshed regularly to avoid image burn-in - see the datasheet.
// [1] https://learn.adafruit.com/diy-rpn-desktop-calculator-with-circuitpython/3d-printing-wiring-assembly
/* uLisp ARM Version 4.1a - www.ulisp.com
David Johnson-Davies - www.technoblogy.com - 15th February 2022
Licensed under the MIT license: https://opensource.org/licenses/MIT
*/
// Lisp Library
const char LispLibrary[] PROGMEM = "";
// Compile options
// #define resetautorun
#define printfreespace
// #define printgcs
// #define sdcardsupport
#define gfxsupport
// #define lisplibrary
#define assemblerlist
// #define lineeditor
// #define vt100
// Includes
// #include "LispLibrary.h"
#include <setjmp.h>
#include <SPI.h>
#include <Wire.h>
#include <limits.h>
#if defined(gfxsupport)
#include <Adafruit_GFX.h> // Core graphics library
//#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <Adafruit_SharpMem.h>
#define COLOR_WHITE 0xffff
#define COLOR_BLACK 0
#define SHARP_SCK PIN_SPI_SCK
#define SHARP_MOSI PIN_SPI_MOSI
#define SHARP_SS PIN_SERIAL1_RX
// Adafruit PyBadge/PyGamer
//#define TFT_CS 44 // Chip select
//#define TFT_RST 46 // Display reset
//#define TFT_DC 45 // Display data/command select
//#define TFT_BACKLIGHT 47 // Display backlight pin
//#define TFT_MOSI 41 // Data out
//#define TFT_SCLK 42 // Clock out
Adafruit_SharpMem tft(SHARP_SCK, SHARP_MOSI, SHARP_SS, 320, 240);
#endif
#if defined(sdcardsupport)
#include <SD.h>
#define SDSIZE 91
#else
#define SDSIZE 0
#endif
// Platform specific settings
#define WORDALIGNED __attribute__((aligned (4)))
#define BUFFERSIZE 36 // Number of bits+4
#define RAMFUNC __attribute__ ((section (".ramfunctions")))
#define MEMBANK
#if defined(ARDUINO_GEMMA_M0) || defined(ARDUINO_SEEED_XIAO_M0) || defined(ARDUINO_QTPY_M0)
#define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */
#define EEPROMFLASH
#define FLASHSIZE 32768 /* Bytes */
#define CODESIZE 128 /* Bytes */
#define STACKDIFF 320
#define CPU_ATSAMD21
#elif defined(ARDUINO_ITSYBITSY_M0) || defined(ARDUINO_SAMD_FEATHER_M0_EXPRESS)
#define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */
#define DATAFLASH
#define FLASHSIZE 2048000 /* 2 MBytes */
#define CODESIZE 128 /* Bytes */
#define SDCARD_SS_PIN 4
#define STACKDIFF 320
#define CPU_ATSAMD21
#elif defined(ADAFRUIT_FEATHER_M0) /* Feather M0 without DataFlash */
#define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */
#define EEPROMFLASH
#define FLASHSIZE 32768 /* Bytes */
#define CODESIZE 128 /* Bytes */
#define SDCARD_SS_PIN 4
#define STACKDIFF 320
#define CPU_ATSAMD21
#elif defined(ARDUINO_METRO_M4) || defined(ARDUINO_ITSYBITSY_M4) || defined(ARDUINO_FEATHER_M4) || defined(ARDUINO_PYBADGE_M4) || defined(ARDUINO_PYGAMER_M4)
#define WORKSPACESIZE (20608-SDSIZE) /* Objects (8*bytes) */
#define DATAFLASH
#define FLASHSIZE 2048000 /* 2 MBytes */
#define CODESIZE 256 /* Bytes */
#define SDCARD_SS_PIN 10
#define STACKDIFF 400
#define CPU_ATSAMD51
#elif defined(ARDUINO_GRAND_CENTRAL_M4)
#define WORKSPACESIZE (28800-SDSIZE) /* Objects (8*bytes) */
#define DATAFLASH
#define FLASHSIZE 8192000 /* 8 MBytes */
#define CODESIZE 256 /* Bytes */
#define STACKDIFF 400
#define CPU_ATSAMD51
#elif defined(ARDUINO_SAMD_MKRZERO)
#define WORKSPACESIZE (2640-SDSIZE) /* Objects (8*bytes) */
#define EEPROMFLASH
#define FLASHSIZE 32768 /* Bytes */
#define SYMBOLTABLESIZE 512 /* Bytes */
#define CODESIZE 128 /* Bytes */
#define STACKDIFF 840
#define CPU_ATSAMD21
#elif defined(ARDUINO_SAMD_ZERO) /* Put this last, otherwise overrides the Adafruit boards */
#define WORKSPACESIZE (2640-SDSIZE) /* Objects (8*bytes) */
#define EEPROMFLASH
#define FLASHSIZE 32768 /* Bytes */
#define CODESIZE 128 /* Bytes */
#define SDCARD_SS_PIN 10
#define STACKDIFF 320
#define CPU_ATSAMD21
#elif defined(ARDUINO_BBC_MICROBIT)
#define WORKSPACESIZE 1344 /* Objects (8*bytes) */
#define CODESIZE 64 /* Bytes */
#define STACKDIFF 320
#define CPU_NRF51822
#elif defined(ARDUINO_BBC_MICROBIT_V2)
#define WORKSPACESIZE 12928 /* Objects (8*bytes) */
#define CODESIZE 128 /* Bytes */
#define STACKDIFF 320
#define CPU_NRF52833
#elif defined(ARDUINO_CALLIOPE_MINI)
#define WORKSPACESIZE 3392 /* Objects (8*bytes) */
#define CODESIZE 64 /* Bytes */
#define STACKDIFF 320
#define CPU_NRF51822
#elif defined(ARDUINO_SINOBIT)
#define WORKSPACESIZE 1344 /* Objects (8*bytes) */
#define CODESIZE 64 /* Bytes */
#define STACKDIFF 320
#define CPU_NRF51822
#elif defined(ARDUINO_NRF52840_ITSYBITSY) || defined(ARDUINO_NRF52840_CLUE)
#define WORKSPACESIZE (21120-SDSIZE) /* Objects (8*bytes) */
#define DATAFLASH
#define FLASHSIZE 2048000 /* 2 MBytes */
#define CODESIZE 256 /* Bytes */
#define STACKDIFF 1200
#define CPU_NRF52840
#elif defined(ARDUINO_NRF52840_FEATHER)
#define WORKSPACESIZE (21120-SDSIZE) /* Objects (8*bytes) */
#define DATAFLASH
#define FLASHSIZE 2048000 /* 2 MBytes */
#define CODESIZE 256 /* Bytes */
#define STACKDIFF 1200
#define CPU_NRF52840
#elif defined(MAX32620)
#define WORKSPACESIZE (24704-SDSIZE) /* Objects (8*bytes) */
#define SYMBOLTABLESIZE 1024 /* Bytes */
#define CODESIZE 256 /* Bytes */
#define STACKDIFF 320
#define CPU_MAX32620
#define Wire1 Wire2
#elif defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
#define WORKSPACESIZE 60000 /* Objects (8*bytes) */
#define LITTLEFS (960 * 1024)
#include <LittleFS.h>
LittleFS_Program LittleFS;
#define CODESIZE 256 /* Bytes */
#define STACKDIFF 15000
#define CPU_iMXRT1062
#define SDCARD_SS_PIN BUILTIN_SDCARD
#define BitOrder uint8_t
#undef RAMFUNC
#define RAMFUNC FASTRUN
#undef MEMBANK
#define MEMBANK DMAMEM
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040)
#define WORKSPACESIZE (22912-SDSIZE) /* Objects (8*bytes) */
#define LITTLEFS
#include <LittleFS.h>
#define FILE_WRITE_BEGIN "w"
#define FILE_READ "r"
#define CODESIZE 256 /* Bytes */
#define STACKDIFF 320
#define CPU_RP2040
#else
#error "Board not supported!"
#endif
// C Macros
#define nil NULL
#define car(x) (((object *) (x))->car)
#define cdr(x) (((object *) (x))->cdr)
#define first(x) (((object *) (x))->car)
#define second(x) (car(cdr(x)))
#define cddr(x) (cdr(cdr(x)))
#define third(x) (car(cdr(cdr(x))))
#define push(x, y) ((y) = cons((x),(y)))
#define pop(y) ((y) = cdr(y))
#define integerp(x) ((x) != NULL && (x)->type == NUMBER)
#define floatp(x) ((x) != NULL && (x)->type == FLOAT)
#define symbolp(x) ((x) != NULL && (x)->type == SYMBOL)
#define stringp(x) ((x) != NULL && (x)->type == STRING)
#define characterp(x) ((x) != NULL && (x)->type == CHARACTER)
#define arrayp(x) ((x) != NULL && (x)->type == ARRAY)
#define streamp(x) ((x) != NULL && (x)->type == STREAM)
#define mark(x) (car(x) = (object *)(((uintptr_t)(car(x))) | MARKBIT))
#define unmark(x) (car(x) = (object *)(((uintptr_t)(car(x))) & ~MARKBIT))
#define marked(x) ((((uintptr_t)(car(x))) & MARKBIT) != 0)
#define MARKBIT 1
#define setflag(x) (Flags = Flags | 1<<(x))
#define clrflag(x) (Flags = Flags & ~(1<<(x)))
#define tstflag(x) (Flags & 1<<(x))
#define issp(x) (x == ' ' || x == '\n' || x == '\r' || x == '\t')
#define longsymbolp(x) (((x)->name & 0x03) == 0)
#define twist(x) ((uint32_t)((x)<<2) | (((x) & 0xC0000000)>>30))
#define untwist(x) (((x)>>2 & 0x3FFFFFFF) | ((x) & 0x03)<<30)
#define PACKEDS 0x43238000
#define BUILTINS 0xF4240000
// Code marker stores start and end of code block
#define startblock(x) ((x->integer) & 0xFFFF)
#define endblock(x) ((x->integer) >> 16 & 0xFFFF)
// Constants
const int TRACEMAX = 3; // Number of traced functions
enum type { ZZERO=0, SYMBOL=2, CODE=4, NUMBER=6, STREAM=8, CHARACTER=10, FLOAT=12, ARRAY=14, STRING=16, PAIR=18 }; // ARRAY STRING and PAIR must be last
enum token { UNUSED, BRA, KET, QUO, DOT };
enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM, STRINGSTREAM, GFXSTREAM };
// Stream names used by printobject
const char serialstream[] PROGMEM = "serial";
const char i2cstream[] PROGMEM = "i2c";
const char spistream[] PROGMEM = "spi";
const char sdstream[] PROGMEM = "sd";
const char stringstream[] PROGMEM = "string";
const char gfxstream[] PROGMEM = "gfx";
const char *const streamname[] PROGMEM = {serialstream, i2cstream, spistream, sdstream, stringstream, gfxstream};
// Typedefs
typedef uint32_t symbol_t;
typedef struct sobject {
union {
struct {
sobject *car;
sobject *cdr;
};
struct {
unsigned int type;
union {
symbol_t name;
int integer;
int chars; // For strings
float single_float;
};
};
};
} object;
typedef object *(*fn_ptr_type)(object *, object *);
typedef void (*mapfun_t)(object *, object **);
typedef int (*intfn_ptr_type)(int w, int x, int y, int z);
typedef const struct {
const char *string;
fn_ptr_type fptr;
uint8_t minmax;
} tbl_entry_t;
typedef int (*gfun_t)();
typedef void (*pfun_t)(char);
enum builtin_t { NIL, TEE, NOTHING, OPTIONAL, INITIALELEMENT, ELEMENTTYPE, BIT, AMPREST, LAMBDA, LET,
LETSTAR, CLOSURE, PSTAR, SPECIAL_FORMS, QUOTE, OR, DEFUN, DEFVAR, SETQ, LOOP, RETURN, PUSH, POP, INCF,
DECF, SETF, DOLIST, DOTIMES, TRACE, UNTRACE, FORMILLIS, TIME, WITHOUTPUTTOSTRING, WITHSERIAL, WITHI2C,
WITHSPI, WITHSDCARD, WITHGFX, DEFCODE, TAIL_FORMS, PROGN, IF, COND, WHEN, UNLESS, CASE, AND, FUNCTIONS,
NOT, NULLFN, CONS, ATOM, LISTP, CONSP, SYMBOLP, ARRAYP, BOUNDP, SETFN, STREAMP, EQ, CAR, FIRST, CDR, REST,
CAAR, CADR, SECOND, CDAR, CDDR, CAAAR, CAADR, CADAR, CADDR, THIRD, CDAAR, CDADR, CDDAR, CDDDR, LENGTH,
ARRAYDIMENSIONS, LIST, MAKEARRAY, REVERSE, NTH, AREFF, ASSOC, MEMBER, APPLY, FUNCALL, APPEND, MAPC, MAPCAR,
MAPCAN, ADD, SUBTRACT, MULTIPLY, DIVIDE, MOD, ONEPLUS, ONEMINUS, ABS, RANDOM, MAXFN, MINFN, NOTEQ, NUMEQ,
LESS, LESSEQ, GREATER, GREATEREQ, PLUSP, MINUSP, ZEROP, ODDP, EVENP, INTEGERP, NUMBERP, FLOATFN, FLOATP,
SIN, COS, TAN, ASIN, ACOS, ATAN, SINH, COSH, TANH, EXP, SQRT, LOG, EXPT, CEILING, FLOOR, TRUNCATE, ROUND,
CHAR, CHARCODE, CODECHAR, CHARACTERP, STRINGP, STRINGEQ, STRINGLESS, STRINGGREATER, SORT, STRINGFN,
CONCATENATE, SUBSEQ, READFROMSTRING, PRINCTOSTRING, PRIN1TOSTRING, LOGAND, LOGIOR, LOGXOR, LOGNOT, ASH,
LOGBITP, EVAL, GLOBALS, LOCALS, MAKUNBOUND, BREAK, READ, PRIN1, PRINT, PRINC, TERPRI, READBYTE, READLINE,
WRITEBYTE, WRITESTRING, WRITELINE, RESTARTI2C, GC, ROOM, SAVEIMAGE, LOADIMAGE, CLS, PINMODE, DIGITALREAD,
DIGITALWRITE, ANALOGREAD, ANALOGREFERENCE, ANALOGREADRESOLUTION, ANALOGWRITE, ANALOGWRITERESOLUTION,
DELAY, MILLIS, SLEEP, NOTE, REGISTER, EDIT, PPRINT, PPRINTALL, FORMAT, REQUIRE, LISTLIBRARY, DRAWPIXEL,
DRAWLINE, DRAWRECT, FILLRECT, DRAWCIRCLE, FILLCIRCLE, DRAWROUNDRECT, FILLROUNDRECT, DRAWTRIANGLE,
FILLTRIANGLE, DRAWCHAR, SETCURSOR, SETTEXTCOLOR, SETTEXTSIZE, SETTEXTWRAP, FILLSCREEN, SETROTATION,
INVERTDISPLAY, KEYWORDS,
K_LED_BUILTIN, K_HIGH, K_LOW,
#if defined(CPU_ATSAMD21)
K_INPUT, K_INPUT_PULLUP, K_INPUT_PULLDOWN, K_OUTPUT, K_AR_DEFAULT, K_AR_INTERNAL1V0, K_AR_INTERNAL1V65,
K_AR_INTERNAL2V23, K_AR_EXTERNAL, K_PA_DIR, K_PA_DIRCLR, K_PA_DIRSET, K_PA_DIRTGL, K_PA_OUT, K_PA_OUTCLR,
K_PA_OUTSET, K_PA_OUTTGL, K_PA_IN, K_PB_DIR, K_PB_DIRCLR, K_PB_DIRSET, K_PB_DIRTGL, K_PB_OUT, K_PB_OUTCLR,
K_PB_OUTSET, K_PB_OUTTGL, K_PB_IN,
#elif defined(CPU_ATSAMD51)
K_INPUT, K_INPUT_PULLUP, K_INPUT_PULLDOWN, K_OUTPUT, K_AR_DEFAULT, K_AR_INTERNAL1V0, K_AR_INTERNAL1V1,
K_AR_INTERNAL1V2, K_AR_INTERNAL1V25, K_AR_INTERNAL1V65, K_AR_INTERNAL2V0, K_AR_INTERNAL2V2,
K_AR_INTERNAL2V23, K_AR_INTERNAL2V4, K_AR_INTERNAL2V5, K_AR_EXTERNAL, K_PA_DIR, K_PA_DIRCLR, K_PA_DIRSET,
K_PA_DIRTGL, K_PA_OUT, K_PA_OUTCLR, K_PA_OUTSET, K_PA_OUTTGL, K_PA_IN, K_PB_DIR, K_PB_DIRCLR, K_PB_DIRSET,
K_PB_DIRTGL, K_PB_OUT, K_PB_OUTCLR, K_PB_OUTSET, K_PB_OUTTGL, K_PB_IN,
#elif defined(CPU_NRF51822)
K_INPUT, K_INPUT_PULLUP, K_INPUT_PULLDOWN, K_OUTPUT, K_AR_DEFAULT, K_AR_VBG, K_AR_SUPPLY_ONE_HALF,
K_AR_SUPPLY_ONE_THIRD, K_AR_EXT0, K_AR_EXT1, K_P0_OUT, K_P0_OUTSET, K_P0_OUTCLR, K_P0_IN, K_P0_DIR,
K_P0_DIRSET, K_P0_DIRCLR,
#elif defined(CPU_NRF52840)
K_INPUT, K_INPUT_PULLUP, K_INPUT_PULLDOWN, K_OUTPUT, K_AR_DEFAULT, K_AR_INTERNAL, K_AR_INTERNAL_3_0,
K_AR_INTERNAL_2_4, K_AR_INTERNAL_1_8, K_AR_INTERNAL_1_2, K_AR_VDD4, K_P0_OUT, K_P0_OUTSET, K_P0_OUTCLR,
K_P0_IN, K_P0_DIR, K_P0_DIRSET, K_P0_DIRCLR, K_P1_OUT, K_P1_OUTSET, K_P1_OUTCLR, K_P1_IN, K_P1_DIR,
K_P1_DIRSET, K_P1_DIRCLR,
#elif defined(CPU_NRF52833)
K_INPUT, K_INPUT_PULLUP, K_INPUT_PULLDOWN, K_OUTPUT, K_AR_DEFAULT, K_AR_INTERNAL, K_AR_VDD4, K_P0_OUT,
K_P0_OUTSET, K_P0_OUTCLR, K_P0_IN, K_P0_DIR, K_P0_DIRSET, K_P0_DIRCLR, K_P1_OUT, K_P1_OUTSET, K_P1_OUTCLR,
K_P1_IN, K_P1_DIR, K_P1_DIRSET, K_P1_DIRCLR,
#elif defined(CPU_iMXRT1062)
K_INPUT, K_INPUT_PULLUP, K_INPUT_PULLDOWN, K_OUTPUT, K_OUTPUT_OPENDRAIN,
#elif defined(CPU_MAX32620)
K_INPUT, K_INPUT_PULLUP, K_OUTPUT, K_DEFAULT, K_EXTERNAL,
#elif defined(CPU_RP2040)
K_INPUT, K_INPUT_PULLUP, K_INPUT_PULLDOWN, K_OUTPUT,
K_GPIO_IN, K_GPIO_OUT, K_GPIO_OUT_SET, K_GPIO_OUT_CLR, K_GPIO_OUT_XOR, K_GPIO_OE, K_GPIO_OE_SET, K_GPIO_OE_CLR, K_GPIO_OE_XOR,
#endif
USERFUNCTIONS, ENDFUNCTIONS, SET_SIZE = INT_MAX };
// Global variables
object Workspace[WORKSPACESIZE] WORDALIGNED MEMBANK;
#if defined(CODESIZE)
RAMFUNC uint8_t MyCode[CODESIZE] WORDALIGNED;
#endif
jmp_buf exception;
unsigned int Freespace = 0;
object *Freelist;
unsigned int I2CCount;
unsigned int TraceFn[TRACEMAX];
unsigned int TraceDepth[TRACEMAX];
object *GlobalEnv;
object *GCStack = NULL;
object *GlobalString;
object *GlobalStringTail;
int GlobalStringIndex = 0;
uint8_t PrintCount = 0;
uint8_t BreakLevel = 0;
char LastChar = 0;
char LastPrint = 0;
// Flags
enum flag { PRINTREADABLY, RETURNFLAG, ESCAPE, EXITEDITOR, LIBRARYLOADED, NOESC, NOECHO };
volatile uint8_t Flags = 0b00001; // PRINTREADABLY set by default
// Forward references
object *tee;
// Error handling
void errorsub (symbol_t fname, PGM_P string) {
pfl(pserial); pfstring(PSTR("Error: "), pserial);
if (fname != sym(NIL)) {
pserial('\'');
psymbol(fname, pserial);
pserial('\''); pserial(' ');
}
pfstring(string, pserial);
}
void errorsym (symbol_t fname, PGM_P string, object *symbol) {
errorsub(fname, string);
pserial(':'); pserial(' ');
printobject(symbol, pserial);
errorend();
}
void errorsym2 (symbol_t fname, PGM_P string) {
errorsub(fname, string);
errorend();
}
void error (builtin_t fname, PGM_P string, object *symbol) {
errorsym(sym(fname), string, symbol);
}
void error2 (builtin_t fname, PGM_P string) {
errorsym2(sym(fname), string);
}
void errorend () { pln(pserial); GCStack = NULL; longjmp(exception, 1); }
// Save space as these are used multiple times
const char notanumber[] PROGMEM = "argument is not a number";
const char notaninteger[] PROGMEM = "argument is not an integer";
const char notastring[] PROGMEM = "argument is not a string";
const char notalist[] PROGMEM = "argument is not a list";
const char notasymbol[] PROGMEM = "argument is not a symbol";
const char notproper[] PROGMEM = "argument is not a proper list";
const char toomanyargs[] PROGMEM = "too many arguments";
const char toofewargs[] PROGMEM = "too few arguments";
const char noargument[] PROGMEM = "missing argument";
const char nostream[] PROGMEM = "missing stream argument";
const char overflow[] PROGMEM = "arithmetic overflow";
const char divisionbyzero[] PROGMEM = "division by zero";
const char indexnegative[] PROGMEM = "index can't be negative";
const char invalidarg[] PROGMEM = "invalid argument";
const char invalidkey[] PROGMEM = "invalid keyword";
const char illegalclause[] PROGMEM = "illegal clause";
const char invalidpin[] PROGMEM = "invalid pin";
const char oddargs[] PROGMEM = "odd number of arguments";
const char indexrange[] PROGMEM = "index out of range";
const char canttakecar[] PROGMEM = "can't take car";
const char canttakecdr[] PROGMEM = "can't take cdr";
const char unknownstreamtype[] PROGMEM = "unknown stream type";
// Set up workspace
void initworkspace () {
Freelist = NULL;
for (int i=WORKSPACESIZE-1; i>=0; i--) {
object *obj = &Workspace[i];
car(obj) = NULL;
cdr(obj) = Freelist;
Freelist = obj;
Freespace++;
}
}
object *myalloc () {
if (Freespace == 0) error2(NIL, PSTR("no room"));
object *temp = Freelist;
Freelist = cdr(Freelist);
Freespace--;
return temp;
}
inline void myfree (object *obj) {
car(obj) = NULL;
cdr(obj) = Freelist;
Freelist = obj;
Freespace++;
}
// Make each type of object
object *number (int n) {
object *ptr = myalloc();
ptr->type = NUMBER;
ptr->integer = n;
return ptr;
}
object *makefloat (float f) {
object *ptr = myalloc();
ptr->type = FLOAT;
ptr->single_float = f;
return ptr;
}
object *character (uint8_t c) {
object *ptr = myalloc();
ptr->type = CHARACTER;
ptr->chars = c;
return ptr;
}
object *cons (object *arg1, object *arg2) {
object *ptr = myalloc();
ptr->car = arg1;
ptr->cdr = arg2;
return ptr;
}
object *symbol (symbol_t name) {
object *ptr = myalloc();
ptr->type = SYMBOL;
ptr->name = name;
return ptr;
}
inline object *bsymbol (builtin_t name) {
return intern(twist(name+BUILTINS));
}
object *codehead (int entry) {
object *ptr = myalloc();
ptr->type = CODE;
ptr->integer = entry;
return ptr;
}
object *intern (symbol_t name) {
for (int i=0; i<WORKSPACESIZE; i++) {
object *obj = &Workspace[i];
if (obj->type == SYMBOL && obj->name == name) return obj;
}
return symbol(name);
}
bool eqsymbols (object *obj, char *buffer) {
object *arg = cdr(obj);
int i = 0;
while (!(arg == NULL && buffer[i] == 0)) {
if (arg == NULL || buffer[i] == 0 ||
arg->chars != (buffer[i]<<24 | buffer[i+1]<<16 | buffer[i+2]<<8 | buffer[i+3])) return false;
arg = car(arg);
i = i + 4;
}
return true;
}
object *internlong (char *buffer) {
for (int i=0; i<WORKSPACESIZE; i++) {
object *obj = &Workspace[i];
if (obj->type == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) return obj;
}
object *obj = lispstring(buffer);
obj->type = SYMBOL;
return obj;
}
object *stream (uint8_t streamtype, uint8_t address) {
object *ptr = myalloc();
ptr->type = STREAM;
ptr->integer = streamtype<<8 | address;
return ptr;
}
object *newstring () {
object *ptr = myalloc();
ptr->type = STRING;
ptr->chars = 0;
return ptr;
}
// Garbage collection
void markobject (object *obj) {
MARK:
if (obj == NULL) return;
if (marked(obj)) return;
object* arg = car(obj);
unsigned int type = obj->type;
mark(obj);
if (type >= PAIR || type == ZZERO) { // cons
markobject(arg);
obj = cdr(obj);
goto MARK;
}
if (type == ARRAY) {
obj = cdr(obj);
goto MARK;
}
if ((type == STRING) || (type == SYMBOL && longsymbolp(obj))) {
obj = cdr(obj);
while (obj != NULL) {
arg = car(obj);
mark(obj);
obj = arg;
}
}
}
void sweep () {
Freelist = NULL;
Freespace = 0;
for (int i=WORKSPACESIZE-1; i>=0; i--) {
object *obj = &Workspace[i];
if (!marked(obj)) myfree(obj); else unmark(obj);
}
}
void gc (object *form, object *env) {
#if defined(printgcs)
int start = Freespace;
#endif
markobject(tee);
markobject(GlobalEnv);
markobject(GCStack);
markobject(form);
markobject(env);
sweep();
#if defined(printgcs)
pfl(pserial); pserial('{'); pint(Freespace - start, pserial); pserial('}');
#endif
}
// Compact image
void movepointer (object *from, object *to) {
for (int i=0; i<WORKSPACESIZE; i++) {
object *obj = &Workspace[i];
unsigned int type = (obj->type) & ~MARKBIT;
if (marked(obj) && (type >= ARRAY || type==ZZERO || (type == SYMBOL && longsymbolp(obj)))) {
if (car(obj) == (object *)((uintptr_t)from | MARKBIT))
car(obj) = (object *)((uintptr_t)to | MARKBIT);
if (cdr(obj) == from) cdr(obj) = to;
}
}
// Fix strings and long symbols
for (int i=0; i<WORKSPACESIZE; i++) {
object *obj = &Workspace[i];
if (marked(obj)) {
unsigned int type = (obj->type) & ~MARKBIT;
if (type == STRING || (type == SYMBOL && longsymbolp(obj))) {
obj = cdr(obj);
while (obj != NULL) {
if (cdr(obj) == to) cdr(obj) = from;
obj = (object *)((uintptr_t)(car(obj)) & ~MARKBIT);
}
}
}
}
}
uintptr_t compactimage (object **arg) {
markobject(tee);
markobject(GlobalEnv);
markobject(GCStack);
object *firstfree = Workspace;
while (marked(firstfree)) firstfree++;
object *obj = &Workspace[WORKSPACESIZE-1];
while (firstfree < obj) {
if (marked(obj)) {
car(firstfree) = car(obj);
cdr(firstfree) = cdr(obj);
unmark(obj);
movepointer(obj, firstfree);
if (GlobalEnv == obj) GlobalEnv = firstfree;
if (GCStack == obj) GCStack = firstfree;
if (*arg == obj) *arg = firstfree;
while (marked(firstfree)) firstfree++;
}
obj--;
}
sweep();
return firstfree - Workspace;
}
// Make SD card filename
char *MakeFilename (object *arg, char *buffer) {
int max = BUFFERSIZE-1;
buffer[0]='/';
int i = 1;
do {
char c = nthchar(arg, i-1);
if (c == '\0') break;
buffer[i++] = c;
} while (i<max);
buffer[i] = '\0';
return buffer;
}
// Save-image and load-image
#if defined(sdcardsupport)
void SDWrite32 (File file, int data) {
file.write(data & 0xFF); file.write(data>>8 & 0xFF);
file.write(data>>16 & 0xFF); file.write(data>>24 & 0xFF);
}
int SDRead32 (File file) {
uintptr_t b0 = file.read(); uintptr_t b1 = file.read();
uintptr_t b2 = file.read(); uintptr_t b3 = file.read();
return b0 | b1<<8 | b2<<16 | b3<<24;
}
#elif defined(LITTLEFS)
void FSWrite32 (File file, uint32_t data) {
union { uint32_t data2; uint8_t u8[4]; };
data2 = data;
if (file.write(u8, 4) != 4) error2(SAVEIMAGE, PSTR("not enough room"));
}
uint32_t FSRead32 (File file) {
union { uint32_t data; uint8_t u8[4]; };
file.read(u8, 4);
return data;
}
#elif defined(DATAFLASH)
// Winbond DataFlash support for Adafruit M4 Express boards
#define PAGEPROG 0x02
#define READSTATUS 0x05
#define READDATA 0x03
#define WRITEENABLE 0x06
#define BLOCK64K 0xD8
#define READID 0x90
// Arduino pins used for dataflash
#if defined(ARDUINO_ITSYBITSY_M0) || defined(ARDUINO_SAMD_FEATHER_M0_EXPRESS)
const int sck = 38, ssel = 39, mosi = 37, miso = 36;
#elif defined(EXTERNAL_FLASH_USE_QSPI)
const int sck = PIN_QSPI_SCK, ssel = PIN_QSPI_CS, mosi = PIN_QSPI_IO0, miso = PIN_QSPI_IO1;
#endif
void FlashBusy () {
digitalWrite(ssel, 0);
FlashWrite(READSTATUS);
while (FlashReadByte() & 1 != 0);
digitalWrite(ssel, 1);
}
inline void FlashWrite (uint8_t data) {
shiftOut(mosi, sck, MSBFIRST, data);
}
inline uint8_t FlashReadByte () {
return shiftIn(miso, sck, MSBFIRST);
}
void FlashWriteByte (uint32_t *addr, uint8_t data) {
// New page
if (((*addr) & 0xFF) == 0) {
digitalWrite(ssel, 1);
FlashBusy();
FlashWriteEnable();
digitalWrite(ssel, 0);
FlashWrite(PAGEPROG);
FlashWrite((*addr)>>16);
FlashWrite((*addr)>>8);
FlashWrite(0);
}
FlashWrite(data);
(*addr)++;
}
void FlashWriteEnable () {
digitalWrite(ssel, 0);
FlashWrite(WRITEENABLE);
digitalWrite(ssel, 1);
}
bool FlashCheck () {
uint8_t manID, devID;
digitalWrite(ssel, HIGH); pinMode(ssel, OUTPUT);
pinMode(sck, OUTPUT);
pinMode(mosi, OUTPUT);
pinMode(miso, INPUT);
digitalWrite(sck, LOW); digitalWrite(mosi, HIGH);
digitalWrite(ssel, LOW);
FlashWrite(READID);
for(uint8_t i=0; i<4; i++) manID = FlashReadByte();
devID = FlashReadByte();
digitalWrite(ssel, HIGH);
return (devID == 0x14 || devID == 0x15 || devID == 0x16); // true = found correct device
}
void FlashBeginWrite (uint32_t *addr, uint32_t bytes) {
*addr = 0;
uint32_t blocks = (bytes+65535)/65536;
// Erase 64K
for (int b=0; b<blocks; b++) {
FlashWriteEnable();
digitalWrite(ssel, 0);
FlashWrite(BLOCK64K);
FlashWrite(b); FlashWrite(0); FlashWrite(0);
digitalWrite(ssel, 1);
FlashBusy();
}
}
void FlashWrite32 (uint32_t *addr, uint32_t data) {
FlashWriteByte(addr, data & 0xFF); FlashWriteByte(addr, data>>8 & 0xFF);
FlashWriteByte(addr, data>>16 & 0xFF); FlashWriteByte(addr, data>>24 & 0xFF);
}
inline void FlashEndWrite (uint32_t *addr) {
(void) addr;
digitalWrite(ssel, 1);
FlashBusy();
}
void FlashBeginRead (uint32_t *addr) {
*addr = 0;
FlashBusy();
digitalWrite(ssel, 0);
FlashWrite(READDATA);
FlashWrite(0); FlashWrite(0); FlashWrite(0);
}
uint32_t FlashRead32 (uint32_t *addr) {
(void) addr;
uint8_t b0 = FlashReadByte(); uint8_t b1 = FlashReadByte();
uint8_t b2 = FlashReadByte(); uint8_t b3 = FlashReadByte();
return b0 | b1<<8 | b2<<16 | b3<<24;
}
inline void FlashEndRead(uint32_t *addr) {
(void) addr;
digitalWrite(ssel, 1);
}
#elif defined(EEPROMFLASH)
// For ATSAMD21
__attribute__((__aligned__(256))) static const uint8_t flash_store[FLASHSIZE] = { };
void row_erase (const volatile void *addr) {
NVMCTRL->ADDR.reg = ((uint32_t)addr) / 2;
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
while (!NVMCTRL->INTFLAG.bit.READY);
}
void page_clear () {
// Execute "PBC" Page Buffer Clear
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC;
while (NVMCTRL->INTFLAG.bit.READY == 0);
}
void page_write () {
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP;
while (NVMCTRL->INTFLAG.bit.READY == 0);
}
bool FlashCheck() {
return true;
}
void FlashBeginWrite(uint32_t *addr, uint32_t bytes) {
(void) bytes;
*addr = (uint32_t)flash_store;
// Disable automatic page write
NVMCTRL->CTRLB.bit.MANW = 1;
}
void FlashWrite32 (uint32_t *addr, uint32_t data) {
if (((*addr) & 0xFF) == 0) row_erase((const volatile void *)(*addr));
if (((*addr) & 0x3F) == 0) page_clear();
*(volatile uint32_t *)(*addr) = data;
(*addr) = (*addr) + 4;
if (((*addr) & 0x3F) == 0) page_write();
}
void FlashEndWrite (uint32_t *addr) {
if (((*addr) & 0x3F) != 0) page_write();
}
void FlashBeginRead(uint32_t *addr) {
*addr = (uint32_t)flash_store;
}
uint32_t FlashRead32 (uint32_t *addr) {
uint32_t data = *(volatile const uint32_t *)(*addr);
(*addr) = (*addr) + 4;
return data;
}
void FlashEndRead (uint32_t *addr) {
(void) addr;
}
#endif
int saveimage (object *arg) {
#if defined(sdcardsupport)
unsigned int imagesize = compactimage(&arg);
SD.begin(SDCARD_SS_PIN);
File file;
if (stringp(arg)) {
char buffer[BUFFERSIZE];
file = SD.open(MakeFilename(arg, buffer), O_RDWR | O_CREAT | O_TRUNC);
if (!file) error2(SAVEIMAGE, PSTR("problem saving to SD card or invalid filename"));
arg = NULL;
} else if (arg == NULL || listp(arg)) {
file = SD.open("/ULISP.IMG", O_RDWR | O_CREAT | O_TRUNC);
if (!file) error2(SAVEIMAGE, PSTR("problem saving to SD card"));
}
else error(SAVEIMAGE, invalidarg, arg);
SDWrite32(file, (uintptr_t)arg);
SDWrite32(file, imagesize);
SDWrite32(file, (uintptr_t)GlobalEnv);
SDWrite32(file, (uintptr_t)GCStack);
for (int i=0; i<CODESIZE; i++) file.write(MyCode[i]);
for (unsigned int i=0; i<imagesize; i++) {
object *obj = &Workspace[i];
SDWrite32(file, (uintptr_t)car(obj));
SDWrite32(file, (uintptr_t)cdr(obj));
}
file.close();
return imagesize;
#elif defined(LITTLEFS)
unsigned int imagesize = compactimage(&arg);
LittleFS.begin(LITTLEFS);
File file;
if (stringp(arg)) {
char buffer[BUFFERSIZE];
file = LittleFS.open(MakeFilename(arg, buffer), FILE_WRITE_BEGIN);
if (!file) error2(SAVEIMAGE, PSTR("problem saving to LittleFS or invalid filename"));
arg = NULL;
} else if (arg == NULL || listp(arg)) {
file = LittleFS.open("/ULISP.IMG", FILE_WRITE_BEGIN);
if (!file) error2(SAVEIMAGE, PSTR("problem saving to LittleFS"));
} else error(SAVEIMAGE, invalidarg, arg);
FSWrite32(file, (uintptr_t)arg);
FSWrite32(file, imagesize);
FSWrite32(file, (uintptr_t)GlobalEnv);
FSWrite32(file, (uintptr_t)GCStack);
if (file.write(MyCode, CODESIZE) != CODESIZE) error2(SAVEIMAGE, PSTR("not enough room"));
for (unsigned int i=0; i<imagesize; i++) {
object *obj = &Workspace[i];
FSWrite32(file, (uintptr_t)car(obj));
FSWrite32(file, (uintptr_t)cdr(obj));
}
file.close();
return imagesize;
#elif defined(DATAFLASH) || defined(EEPROMFLASH)
unsigned int imagesize = compactimage(&arg);
if (!(arg == NULL || listp(arg))) error(SAVEIMAGE, invalidarg, arg);
if (!FlashCheck()) error2(SAVEIMAGE, PSTR("flash not available"));
// Save to flash
uint32_t bytesneeded = 16 + CODESIZE + imagesize*8;
if (bytesneeded > FLASHSIZE) error(SAVEIMAGE, PSTR("image too large"), number(imagesize));
uint32_t addr;
FlashBeginWrite(&addr, bytesneeded);
FlashWrite32(&addr, (uintptr_t)arg);
FlashWrite32(&addr, imagesize);
FlashWrite32(&addr, (uintptr_t)GlobalEnv);
FlashWrite32(&addr, (uintptr_t)GCStack);
for (int i=0; i<CODESIZE; i=i+4) {
union { uint32_t u32; uint8_t u8[4]; };
u8[0] = MyCode[i]; u8[1] = MyCode[i+1]; u8[2] = MyCode[i+2]; u8[3] = MyCode[i+3];
FlashWrite32(&addr, u32);
}
for (unsigned int i=0; i<imagesize; i++) {
object *obj = &Workspace[i];
FlashWrite32(&addr, (uintptr_t)car(obj));
FlashWrite32(&addr, (uintptr_t)cdr(obj));
}
FlashEndWrite(&addr);
return imagesize;
#else
(void) arg;
error2(SAVEIMAGE, PSTR("not available"));
return 0;
#endif
}
int loadimage (object *arg) {
#if defined(sdcardsupport)
SD.begin(SDCARD_SS_PIN);
File file;
if (stringp(arg)) {
char buffer[BUFFERSIZE];
file = SD.open(MakeFilename(arg, buffer));
if (!file) error2(LOADIMAGE, PSTR("problem loading from SD card or invalid filename"));
}
else if (arg == NULL) {
file = SD.open("/ULISP.IMG");
if (!file) error2(LOADIMAGE, PSTR("problem loading from SD card"));
}
else error(LOADIMAGE, invalidarg, arg);
SDRead32(file);
unsigned int imagesize = SDRead32(file);
GlobalEnv = (object *)SDRead32(file);
GCStack = (object *)SDRead32(file);
for (int i=0; i<CODESIZE; i++) MyCode[i] = file.read();
for (unsigned int i=0; i<imagesize; i++) {
object *obj = &Workspace[i];
car(obj) = (object *)SDRead32(file);
cdr(obj) = (object *)SDRead32(file);
}
file.close();
gc(NULL, NULL);
return imagesize;
#elif defined(LITTLEFS)
LittleFS.begin(LITTLEFS);
File file;
if (stringp(arg)) {
char buffer[BUFFERSIZE];
file = LittleFS.open(MakeFilename(arg, buffer), FILE_READ);
if (!file) error2(LOADIMAGE, PSTR("problem loading from LittleFS or invalid filename"));
}
else if (arg == NULL) {
file = LittleFS.open("/ULISP.IMG", FILE_READ);
if (!file) error2(LOADIMAGE, PSTR("problem loading from LittleFS"));
}
else error(LOADIMAGE, invalidarg, arg);
FSRead32(file);
unsigned int imagesize = FSRead32(file);
GlobalEnv = (object *)FSRead32(file);
GCStack = (object *)FSRead32(file);
file.read(MyCode, CODESIZE);
for (unsigned int i=0; i<imagesize; i++) {
object *obj = &Workspace[i];
car(obj) = (object *)FSRead32(file);
cdr(obj) = (object *)FSRead32(file);
}
file.close();
gc(NULL, NULL);
return imagesize;
#elif defined(DATAFLASH) || defined(EEPROMFLASH) || defined(EEPROMLIBRARY)
if (!FlashCheck()) error2(LOADIMAGE, PSTR("flash not available"));
uint32_t addr;
FlashBeginRead(&addr);
FlashRead32(&addr); // Skip eval address
uint32_t imagesize = FlashRead32(&addr);
if (imagesize == 0 || imagesize == 0xFFFFFFFF) error2(LOADIMAGE, PSTR("no saved image"));
GlobalEnv = (object *)FlashRead32(&addr);
GCStack = (object *)FlashRead32(&addr);
for (int i=0; i<CODESIZE; i=i+4) {
union { uint32_t u32; uint8_t u8[4]; };
u32 = FlashRead32(&addr);
MyCode[i] = u8[0]; MyCode[i+1] = u8[1]; MyCode[i+2] = u8[2]; MyCode[i+3] = u8[3];
}
for (uint32_t i=0; i<imagesize; i++) {
object *obj = &Workspace[i];
car(obj) = (object *)FlashRead32(&addr);
cdr(obj) = (object *)FlashRead32(&addr);
}
FlashEndRead(&addr);
gc(NULL, NULL);
return imagesize;
#else
(void) arg;
error2(LOADIMAGE, PSTR("not available"));
return 0;
#endif
}
void autorunimage () {
#if defined(sdcardsupport)
SD.begin(SDCARD_SS_PIN);
File file = SD.open("/ULISP.IMG");
if (!file) error2(NIL, PSTR("problem autorunning from SD card"));
object *autorun = (object *)SDRead32(file);
file.close();
if (autorun != NULL) {
loadimage(NULL);
apply(NIL, autorun, NULL, NULL);
}
#elif defined(LITTLEFS)
LittleFS.begin(LITTLEFS);
File file = LittleFS.open("/ULISP.IMG", FILE_READ);
if (!file) error2(NIL, PSTR("problem autorunning from LittleFS"));
object *autorun = (object *)FSRead32(file);
file.close();
if (autorun != NULL) {
loadimage(NULL);
apply(NIL, autorun, NULL, NULL);
}
#elif defined(DATAFLASH) || defined(EEPROMFLASH) || defined(EEPROMLIBRARY)
if (!FlashCheck()) error2(NIL, PSTR("flash not available"));
uint32_t addr;
FlashBeginRead(&addr);
object *autorun = (object *)FlashRead32(&addr);
FlashEndRead(&addr);
if (autorun != NULL && (unsigned int)autorun != 0xFFFFFFFF) {
loadimage(nil);
apply(NIL, autorun, NULL, NULL);
}
#else
error2(NIL, PSTR("autorun not available"));
#endif
}
// Tracing
int tracing (symbol_t name) {
int i = 0;
while (i < TRACEMAX) {
if (TraceFn[i] == name) return i+1;
i++;
}
return 0;
}
void trace (symbol_t name) {
if (tracing(name)) error(TRACE, PSTR("already being traced"), symbol(name));
int i = 0;
while (i < TRACEMAX) {
if (TraceFn[i] == 0) { TraceFn[i] = name; TraceDepth[i] = 0; return; }
i++;
}
error2(TRACE, PSTR("already tracing 3 functions"));
}
void untrace (symbol_t name) {
int i = 0;
while (i < TRACEMAX) {
if (TraceFn[i] == name) { TraceFn[i] = 0; return; }
i++;
}
error(UNTRACE, PSTR("not tracing"), symbol(name));
}
// Helper functions
bool consp (object *x) {
if (x == NULL) return false;
unsigned int type = x->type;
return type >= PAIR || type == ZZERO;
}
#define atom(x) (!consp(x))
bool listp (object *x) {
if (x == NULL) return true;
unsigned int type = x->type;
return type >= PAIR || type == ZZERO;
}
#define improperp(x) (!listp(x))
object *quote (object *arg) {
return cons(bsymbol(QUOTE), cons(arg,NULL));
}
// Radix 40 encoding
builtin_t builtin (symbol_t name) {
return (builtin_t)(untwist(name) - BUILTINS);
}
symbol_t sym (builtin_t x) {
return twist(x + BUILTINS);
}
int8_t toradix40 (char ch) {
if (ch == 0) return 0;
if (ch >= '0' && ch <= '9') return ch-'0'+1;
if (ch == '-') return 37; if (ch == '*') return 38; if (ch == '$') return 39;
ch = ch | 0x20;
if (ch >= 'a' && ch <= 'z') return ch-'a'+11;
return -1; // Invalid
}
char fromradix40 (char n) {
if (n >= 1 && n <= 9) return '0'+n-1;
if (n >= 11 && n <= 36) return 'a'+n-11;
if (n == 37) return '-'; if (n == 38) return '*'; if (n == 39) return '$';
return 0;
}
uint32_t pack40 (char *buffer) {
int x = 0;
for (int i=0; i<6; i++) x = x * 40 + toradix40(buffer[i]);
return x;
}
bool valid40 (char *buffer) {
if (toradix40(buffer[0]) < 11) return false;
for (int i=1; i<6; i++) if (toradix40(buffer[i]) < 0) return false;
return true;
}
int8_t digitvalue (char d) {
if (d>='0' && d<='9') return d-'0';
d = d | 0x20;
if (d>='a' && d<='f') return d-'a'+10;
return 16;
}
int checkinteger (builtin_t name, object *obj) {
if (!integerp(obj)) error(name, notaninteger, obj);
return obj->integer;
}
int checkbitvalue (builtin_t name, object *obj) {
if (!integerp(obj)) error(name, notaninteger, obj);
int n = obj->integer;
if (n & ~1) error(name, PSTR("argument is not a bit value"), obj);
return n;
}
float checkintfloat (builtin_t name, object *obj){
if (integerp(obj)) return obj->integer;
if (!floatp(obj)) error(name, notanumber, obj);
return obj->single_float;
}
int checkchar (builtin_t name, object *obj) {
if (!characterp(obj)) error(name, PSTR("argument is not a character"), obj);
return obj->chars;
}
object *checkstring (builtin_t name, object *obj) {
if (!stringp(obj)) error(name, notastring, obj);
return obj;
}
int isstream (object *obj){
if (!streamp(obj)) error(NIL, PSTR("not a stream"), obj);
return obj->integer;
}
int isbuiltin (object *obj, builtin_t n) {
return symbolp(obj) && obj->name == sym(n);
}
bool builtinp (symbol_t name) {
return (untwist(name) > BUILTINS && untwist(name) < ENDFUNCTIONS+BUILTINS);
}
int keywordp (object *obj) {
if (!symbolp(obj)) return false;
builtin_t name = builtin(obj->name);
return ((name > KEYWORDS) && (name < USERFUNCTIONS));
}
int checkkeyword (builtin_t name, object *obj) {
if (!keywordp(obj)) error(name, PSTR("argument is not a keyword"), obj);
builtin_t kname = builtin(obj->name);
uint8_t context = getminmax(kname);
if (context != 0 && context != name) error(name, invalidkey, obj);
return ((int)lookupfn(kname));
}
void checkargs (builtin_t name, object *args) {
int nargs = listlength(name, args);
checkminmax(name, nargs);
}
int eq (object *arg1, object *arg2) {
if (arg1 == arg2) return true; // Same object
if ((arg1 == nil) || (arg2 == nil)) return false; // Not both values
if (arg1->cdr != arg2->cdr) return false; // Different values
if (symbolp(arg1) && symbolp(arg2)) return true; // Same symbol
if (integerp(arg1) && integerp(arg2)) return true; // Same integer
if (floatp(arg1) && floatp(arg2)) return true; // Same float
if (characterp(arg1) && characterp(arg2)) return true; // Same character
return false;
}
int listlength (builtin_t name, object *list) {
int length = 0;
while (list != NULL) {
if (improperp(list)) error2(name, notproper);
list = cdr(list);
length++;
}
return length;
}
// Association lists
object *assoc (object *key, object *list) {
while (list != NULL) {
if (improperp(list)) error(ASSOC, notproper, list);
object *pair = first(list);
if (!listp(pair)) error(ASSOC, PSTR("element is not a list"), pair);
if (pair != NULL && eq(key,car(pair))) return pair;
list = cdr(list);
}
return nil;
}
object *delassoc (object *key, object **alist) {
object *list = *alist;
object *prev = NULL;
while (list != NULL) {
object *pair = first(list);
if (eq(key,car(pair))) {
if (prev == NULL) *alist = cdr(list);
else cdr(prev) = cdr(list);
return key;
}
prev = list;
list = cdr(list);
}
return nil;
}
// Array utilities
int nextpower2 (int n) {
n--; n |= n >> 1; n |= n >> 2; n |= n >> 4;
n |= n >> 8; n |= n >> 16; n++;
return n<2 ? 2 : n;
}
object *buildarray (int n, int s, object *def) {
int s2 = s>>1;
if (s2 == 1) {
if (n == 2) return cons(def, def);
else if (n == 1) return cons(def, NULL);
else return NULL;
} else if (n >= s2) return cons(buildarray(s2, s2, def), buildarray(n - s2, s2, def));
else return cons(buildarray(n, s2, def), nil);
}
object *makearray (builtin_t name, object *dims, object *def, bool bitp) {
int size = 1;
object *dimensions = dims;
while (dims != NULL) {
int d = car(dims)->integer;
if (d < 0) error2(name, PSTR("dimension can't be negative"));
size = size * d;
dims = cdr(dims);
}
// Bit array identified by making first dimension negative
if (bitp) { size = (size + 31)/32; car(dimensions) = number(-(car(dimensions)->integer)); }
object *ptr = myalloc();
ptr->type = ARRAY;
object *tree = nil;
if (size != 0) tree = buildarray(size, nextpower2(size), def);
ptr->cdr = cons(tree, dimensions);
return ptr;
}
object **arrayref (object *array, int index, int size) {
int mask = nextpower2(size)>>1;
object **p = &car(cdr(array));
while (mask) {
if ((index & mask) == 0) p = &(car(*p)); else p = &(cdr(*p));
mask = mask>>1;
}
return p;
}
object **getarray (builtin_t name, object *array, object *subs, object *env, int *bit) {
int index = 0, size = 1, s;
*bit = -1;
bool bitp = false;
object *dims = cddr(array);
while (dims != NULL && subs != NULL) {
int d = car(dims)->integer;
if (d < 0) { d = -d; bitp = true; }
if (env) s = checkinteger(name, eval(car(subs), env)); else s = checkinteger(name, car(subs));
if (s < 0 || s >= d) error(name, PSTR("subscript out of range"), car(subs));
size = size * d;
index = index * d + s;
dims = cdr(dims); subs = cdr(subs);
}
if (dims != NULL) error2(name, PSTR("too few subscripts"));
if (subs != NULL) error2(name, PSTR("too many subscripts"));
if (bitp) {
size = (size + 31)/32;
*bit = index & 0x1F; index = index>>5;
}
return arrayref(array, index, size);
}
void rslice (object *array, int size, int slice, object *dims, object *args) {
int d = first(dims)->integer;
for (int i = 0; i < d; i++) {
int index = slice * d + i;
if (!consp(args)) error2(NIL, PSTR("initial contents don't match array type"));
if (cdr(dims) == NULL) {
object **p = arrayref(array, index, size);
*p = car(args);
} else rslice(array, size, index, cdr(dims), car(args));
args = cdr(args);
}
}
object *readarray (int d, object *args) {
object *list = args;
object *dims = NULL; object *head = NULL;
int size = 1;
for (int i = 0; i < d; i++) {
if (!listp(list)) error2(NIL, PSTR("initial contents don't match array type"));
int l = listlength(NIL, list);
if (dims == NULL) { dims = cons(number(l), NULL); head = dims; }
else { cdr(dims) = cons(number(l), NULL); dims = cdr(dims); }
size = size * l;
if (list != NULL) list = car(list);
}
object *array = makearray(NIL, head, NULL, false);
rslice(array, size, 0, head, args);
return array;
}
object *readbitarray (gfun_t gfun) {
char ch = gfun();
object *head = NULL;
object *tail = NULL;
while (!issp(ch) && ch != ')' && ch != '(') {
if (ch != '0' && ch != '1') error2(NIL, PSTR("illegal character in bit array"));
object *cell = cons(number(ch - '0'), NULL);
if (head == NULL) head = cell;
else tail->cdr = cell;
tail = cell;
ch = gfun();
}
LastChar = ch;
int size = listlength(NIL, head);
object *array = makearray(NIL, cons(number(size), NULL), 0, true);
size = (size + 31) / 32;
int index = 0;
while (head != NULL) {
object **loc = arrayref(array, index>>5, size);
int bit = index & 0x1F;
*loc = number((((*loc)->integer) & ~(1<<bit)) | (car(head)->integer)<<bit);
index++;
head = cdr(head);
}
return array;
}
void pslice (object *array, int size, int slice, object *dims, pfun_t pfun, bool bitp) {
bool spaces = true;
if (slice == -1) { spaces = false; slice = 0; }
int d = first(dims)->integer;
if (d < 0) d = -d;
for (int i = 0; i < d; i++) {
if (i && spaces) pfun(' ');
int index = slice * d + i;
if (cdr(dims) == NULL) {
if (bitp) pint(((*arrayref(array, index>>5, size))->integer)>>(index & 0x1f) & 1, pfun);
else printobject(*arrayref(array, index, size), pfun);
} else { pfun('('); pslice(array, size, index, cdr(dims), pfun, bitp); pfun(')'); }
}
}
void printarray (object *array, pfun_t pfun) {
object *dimensions = cddr(array);
object *dims = dimensions;
bool bitp = false;
int size = 1, n = 0;
while (dims != NULL) {
int d = car(dims)->integer;
if (d < 0) { bitp = true; d = -d; }
size = size * d;
dims = cdr(dims); n++;
}
if (bitp) size = (size+31)/32;
pfun('#');
if (n == 1 && bitp) { pfun('*'); pslice(array, size, -1, dimensions, pfun, bitp); }
else {
if (n > 1) { pint(n, pfun); pfun('A'); }
pfun('('); pslice(array, size, 0, dimensions, pfun, bitp); pfun(')');
}
}
// String utilities
void indent (uint8_t spaces, char ch, pfun_t pfun) {
for (uint8_t i=0; i<spaces; i++) pfun(ch);
}
object *startstring (builtin_t name) {
(void) name;
object *string = newstring();
GlobalString = string;
GlobalStringTail = string;
return string;
}
void buildstring (char ch, object **tail) {
object *cell;
if (cdr(*tail) == NULL) {
cell = myalloc(); cdr(*tail) = cell;
} else if (((*tail)->chars & 0xFFFFFF) == 0) {
(*tail)->chars = (*tail)->chars | ch<<16; return;
} else if (((*tail)->chars & 0xFFFF) == 0) {
(*tail)->chars = (*tail)->chars | ch<<8; return;
} else if (((*tail)->chars & 0xFF) == 0) {
(*tail)->chars = (*tail)->chars | ch; return;
} else {
cell = myalloc(); car(*tail) = cell;
}
car(cell) = NULL; cell->chars = ch<<24; *tail = cell;
}
object *copystring (object *arg) {
object *obj = newstring();
object *ptr = obj;
arg = cdr(arg);
while (arg != NULL) {
object *cell = myalloc(); car(cell) = NULL;
if (cdr(obj) == NULL) cdr(obj) = cell; else car(ptr) = cell;
ptr = cell;
ptr->chars = arg->chars;
arg = car(arg);
}
return obj;
}
object *readstring (uint8_t delim, gfun_t gfun) {
object *obj = newstring();
object *tail = obj;
int ch = gfun();
if (ch == -1) return nil;
while ((ch != delim) && (ch != -1)) {
if (ch == '\\') ch = gfun();
buildstring(ch, &tail);
ch = gfun();
}
return obj;
}
int stringlength (object *form) {
int length = 0;
form = cdr(form);
while (form != NULL) {
int chars = form->chars;
for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
if (chars>>i & 0xFF) length++;
}
form = car(form);
}
return length;
}
uint8_t nthchar (object *string, int n) {
object *arg = cdr(string);
int top;
if (sizeof(int) == 4) { top = n>>2; n = 3 - (n&3); }
else { top = n>>1; n = 1 - (n&1); }
for (int i=0; i<top; i++) {
if (arg == NULL) return 0;
arg = car(arg);
}
if (arg == NULL) return 0;
return (arg->chars)>>(n*8) & 0xFF;
}
int gstr () {
if (LastChar) {
char temp = LastChar;
LastChar = 0;
return temp;
}
char c = nthchar(GlobalString, GlobalStringIndex++);
if (c != 0) return c;
return '\n'; // -1?
}
void pstr (char c) {
buildstring(c, &GlobalStringTail);
}
object *lispstring (char *s) {
object *obj = newstring();
object *tail = obj;
while(1) {
char ch = *s++;
if (ch == 0) break;
if (ch == '\\') ch = *s++;
buildstring(ch, &tail);
}
return obj;
}
// Lookup variable in environment
object *value (symbol_t n, object *env) {
while (env != NULL) {
object *pair = car(env);
if (pair != NULL && car(pair)->name == n) return pair;
env = cdr(env);
}
return nil;
}
bool boundp (object *var, object *env) {
symbol_t varname = var->name;
if (value(varname, env) != NULL) return true;
if (value(varname, GlobalEnv) != NULL) return true;
return false;
}
object *findvalue (object *var, object *env) {
symbol_t varname = var->name;
object *pair = value(varname, env);
if (pair == NULL) pair = value(varname, GlobalEnv);
if (pair == NULL) error(NIL, PSTR("unknown variable"), var);
return pair;
}
// Handling closures
object *closure (int tc, symbol_t name, object *function, object *args, object **env) {
object *state = car(function);
function = cdr(function);
int trace = 0;
if (name) trace = tracing(name);
if (trace) {
indent(TraceDepth[trace-1]<<1, ' ', pserial);
pint(TraceDepth[trace-1]++, pserial);
pserial(':'); pserial(' '); pserial('('); printsymbol(symbol(name), pserial);
}
object *params = first(function);
if (!listp(params)) errorsym(name, notalist, params);
function = cdr(function);
// Dropframe
if (tc) {
if (*env != NULL && car(*env) == NULL) {
pop(*env);
while (*env != NULL && car(*env) != NULL) pop(*env);
} else push(nil, *env);
}
// Push state
while (consp(state)) {
object *pair = first(state);
push(pair, *env);
state = cdr(state);
}
// Add arguments to environment
bool optional = false;
while (params != NULL) {
object *value;
object *var = first(params);
if (isbuiltin(var, OPTIONAL)) optional = true;
else {
if (consp(var)) {
if (!optional) errorsym(name, PSTR("invalid default value"), var);
if (args == NULL) value = eval(second(var), *env);
else { value = first(args); args = cdr(args); }
var = first(var);
if (!symbolp(var)) errorsym(name, PSTR("illegal optional parameter"), var);
} else if (!symbolp(var)) {
errorsym(name, PSTR("illegal function parameter"), var);
} else if (isbuiltin(var, AMPREST)) {
params = cdr(params);
var = first(params);
value = args;
args = NULL;
} else {
if (args == NULL) {
if (optional) value = nil;
else errorsym2(name, toofewargs);
} else { value = first(args); args = cdr(args); }
}
push(cons(var,value), *env);
if (trace) { pserial(' '); printobject(value, pserial); }
}
params = cdr(params);
}
if (args != NULL) errorsym2(name, toomanyargs);
if (trace) { pserial(')'); pln(pserial); }
// Do an implicit progn
if (tc) push(nil, *env);
return tf_progn(function, *env);
}
object *apply (builtin_t name, object *function, object *args, object *env) {
if (symbolp(function)) {
builtin_t fname = builtin(function->name);
if ((fname > FUNCTIONS) && (fname < KEYWORDS)) {
checkargs(fname, args);
return ((fn_ptr_type)lookupfn(fname))(args, env);
} else function = eval(function, env);
}
if (consp(function) && isbuiltin(car(function), LAMBDA)) {
object *result = closure(0, sym(name), function, args, &env);
return eval(result, env);
}
if (consp(function) && isbuiltin(car(function), CLOSURE)) {
function = cdr(function);
object *result = closure(0, sym(name), function, args, &env);
return eval(result, env);
}
error(name, PSTR("illegal function"), function);
return NULL;
}
// In-place operations
object **place (builtin_t name, object *args, object *env, int *bit) {
*bit = -1;
if (atom(args)) return &cdr(findvalue(args, env));
object* function = first(args);
if (symbolp(function)) {
symbol_t sname = function->name;
if (sname == sym(CAR) || sname == sym(FIRST)) {
object *value = eval(second(args), env);
if (!listp(value)) error(name, canttakecar, value);
return &car(value);
}
if (sname == sym(CDR) || sname == sym(REST)) {
object *value = eval(second(args), env);
if (!listp(value)) error(name, canttakecdr, value);
return &cdr(value);
}
if (sname == sym(NTH)) {
int index = checkinteger(NTH, eval(second(args), env));
object *list = eval(third(args), env);
if (atom(list)) error(name, PSTR("second argument to nth is not a list"), list);
while (index > 0) {
list = cdr(list);
if (list == NULL) error2(name, PSTR("index to nth is out of range"));
index--;
}
return &car(list);
}
if (sname == sym(AREFF)) {
object *array = eval(second(args), env);
if (!arrayp(array)) error(AREFF, PSTR("first argument is not an array"), array);
return getarray(AREFF, array, cddr(args), env, bit);
}
}
error2(name, PSTR("illegal place"));
return nil;
}
// Checked car and cdr
object *carx (object *arg) {
if (!listp(arg)) error(NIL, canttakecar, arg);
if (arg == nil) return nil;
return car(arg);
}
object *cdrx (object *arg) {
if (!listp(arg)) error(NIL, canttakecdr, arg);
if (arg == nil) return nil;
return cdr(arg);
}
// I2C interface for up to two ports
void I2Cinit (TwoWire *port, bool enablePullup) {
(void) enablePullup;
port->begin();
}
int I2Cread (TwoWire *port) {
return port->read();
}
void I2Cwrite (TwoWire *port, uint8_t data) {
port->write(data);
}
bool I2Cstart (TwoWire *port, uint8_t address, uint8_t read) {
int ok = true;
if (read == 0) {
port->beginTransmission(address);
ok = (port->endTransmission(true) == 0);
port->beginTransmission(address);
}
else port->requestFrom(address, I2CCount);
return ok;
}
bool I2Crestart (TwoWire *port, uint8_t address, uint8_t read) {
int error = (port->endTransmission(false) != 0);
if (read == 0) port->beginTransmission(address);
else port->requestFrom(address, I2CCount);
return error ? false : true;
}
void I2Cstop (TwoWire *port, uint8_t read) {
if (read == 0) port->endTransmission(); // Check for error?
}
// Streams
// Simplify board differences
#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_GRAND_CENTRAL_M4) || defined(ARDUINO_PYBADGE_M4) || defined(ARDUINO_PYGAMER_M4) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(ARDUINO_RASPBERRY_PI_PICO)
#define ULISP_SPI1
#endif
#if defined(ARDUINO_BBC_MICROBIT_V2) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(MAX32620) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040)
#define ULISP_I2C1
#endif
#if defined(ARDUINO_SAM_DUE) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
#define ULISP_SERIAL3
#elif defined(ARDUINO_RASPBERRY_PI_PICO)
#define ULISP_SERIAL2
#elif !defined(CPU_NRF51822) && !defined(CPU_NRF52833) && !defined(ARDUINO_FEATHER_F405) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040)
#define ULISP_SERIAL1
#endif
inline int spiread () { return SPI.transfer(0); }
#if defined(ULISP_SPI1)
inline int spi1read () { return SPI1.transfer(0); }
#endif
inline int i2cread () { return I2Cread(&Wire); }
#if defined(ULISP_I2C1)
inline int i2c1read () { return I2Cread(&Wire1); }
#endif
#if defined(ULISP_SERIAL3)
inline int serial3read () { while (!Serial3.available()) testescape(); return Serial3.read(); }
#endif
#if defined(ULISP_SERIAL3) || defined(ULISP_SERIAL2)
inline int serial2read () { while (!Serial2.available()) testescape(); return Serial2.read(); }
#endif
#if defined(ULISP_SERIAL3) || defined(ULISP_SERIAL2) || defined(ULISP_SERIAL1)
inline int serial1read () { while (!Serial1.available()) testescape(); return Serial1.read(); }
#endif
#if defined(sdcardsupport)
File SDpfile, SDgfile;
inline int SDread () {
if (LastChar) {
char temp = LastChar;
LastChar = 0;
return temp;
}
return SDgfile.read();
}
#endif
void serialbegin (int address, int baud) {
#if defined(ULISP_SERIAL3)
if (address == 1) Serial1.begin((long)baud*100);
else if (address == 2) Serial2.begin((long)baud*100);
else if (address == 3) Serial3.begin((long)baud*100);
#elif defined(ULISP_SERIAL2)
if (address == 1) Serial1.begin((long)baud*100);
else if (address == 2) Serial2.begin((long)baud*100);
#elif defined(ULISP_SERIAL1)
if (address == 1) Serial1.begin((long)baud*100);
#else
(void) baud;
if (false);
#endif
else error(WITHSERIAL, PSTR("port not supported"), number(address));
}
void serialend (int address) {
#if defined(ULISP_SERIAL3)
if (address == 1) {Serial1.flush(); Serial1.end(); }
else if (address == 2) {Serial2.flush(); Serial2.end(); }
else if (address == 3) {Serial3.flush(); Serial3.end(); }
#elif defined(ULISP_SERIAL2)
if (address == 1) {Serial1.flush(); Serial1.end(); }
else if (address == 2) {Serial2.flush(); Serial2.end(); }
#elif defined(ULISP_SERIAL1)
if (address == 1) {Serial1.flush(); Serial1.end(); }
#else
(void) baud;
if (false);
#endif
else error(WITHSERIAL, PSTR("port not supported"), number(address));
}
gfun_t gstreamfun (object *args) {
int streamtype = SERIALSTREAM;
int address = 0;
gfun_t gfun = gserial;
if (args != NULL) {
int stream = isstream(first(args));
streamtype = stream>>8; address = stream & 0xFF;
}
if (streamtype == I2CSTREAM) {
if (address < 128) gfun = i2cread;
#if defined(ULISP_I2C1)
else gfun = i2c1read;
#endif
} else if (streamtype == SPISTREAM) {
if (address < 128) gfun = spiread;
#if defined(ULISP_SPI1)
else gfun = spi1read;
#endif
}
else if (streamtype == SERIALSTREAM) {
if (address == 0) gfun = gserial;
#if defined(ULISP_SERIAL3)
else if (address == 1) gfun = serial1read;
else if (address == 2) gfun = serial2read;
else if (address == 3) gfun = serial3read;
#elif defined(ULISP_SERIAL2)
else if (address == 1) gfun = serial1read;
else if (address == 2) gfun = serial2read;
#elif defined(ULISP_SERIAL1)
else if (address == 1) gfun = serial1read;
#endif
}
#if defined(sdcardsupport)
else if (streamtype == SDSTREAM) gfun = (gfun_t)SDread;
#endif
else error2(NIL, PSTR("unknown stream type"));
return gfun;
}
inline void spiwrite (char c) { SPI.transfer(c); }
#if defined(ULISP_SPI1)
inline void spi1write (char c) { SPI1.transfer(c); }
#endif
inline void i2cwrite (char c) { I2Cwrite(&Wire, c); }
#if defined(ULISP_I2C1)
inline void i2c1write (char c) { I2Cwrite(&Wire1, c); }
#endif
#if defined(SERIAL3)
inline void serial1write (char c) { Serial1.write(c); }
inline void serial2write (char c) { Serial2.write(c); }
inline void serial3write (char c) { Serial3.write(c); }
#elif defined(SERIAL1)
inline void serial1write (char c) { Serial1.write(c); }
#endif
#if defined(sdcardsupport)
inline void SDwrite (char c) { SDpfile.write(c); }
#endif
#if defined(gfxsupport)
inline void gfxwrite (char c) { tft.write(c); if (c=='\n') tft.refresh(); }
#endif
pfun_t pstreamfun (object *args) {
int streamtype = SERIALSTREAM;
int address = 0;
pfun_t pfun = pserial;
if (args != NULL && first(args) != NULL) {
int stream = isstream(first(args));
streamtype = stream>>8; address = stream & 0xFF;
}
if (streamtype == I2CSTREAM) {
if (address < 128) pfun = i2cwrite;
#if defined(ULISP_I2C1)
else pfun = i2c1write;
#endif
} else if (streamtype == SPISTREAM) {
if (address < 128) pfun = spiwrite;
#if defined(ULISP_SPI1)
else pfun = spi1write;
#endif
} else if (streamtype == SERIALSTREAM) {
if (address == 0) pfun = pserial;
#if defined(SERIAL3)
else if (address == 1) pfun = serial1write;
else if (address == 2) pfun = serial2write;
else if (address == 3) pfun = serial3write;
#elif defined(SERIAL2)
else if (address == 1) pfun = serial1write;
else if (address == 2) pfun = serial2write;
#elif defined(SERIAL1)
else if (address == 1) pfun = serial1write;
#endif
}
else if (streamtype == STRINGSTREAM) {
pfun = pstr;
}
#if defined(sdcardsupport)
else if (streamtype == SDSTREAM) pfun = (pfun_t)SDwrite;
#endif
#if defined(gfxsupport)
else if (streamtype == GFXSTREAM) pfun = (pfun_t)gfxwrite;
#endif
else error2(NIL, PSTR("unknown stream type"));
return pfun;
}
// Check pins - these are board-specific not processor-specific
void checkanalogread (int pin) {
#if defined(ARDUINO_SAM_DUE)
if (!(pin>=54 && pin<=65)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_SAMD_ZERO)
if (!(pin>=14 && pin<=19)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_SAMD_MKRZERO)
if (!(pin>=15 && pin<=21)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_ITSYBITSY_M0)
if (!(pin>=14 && pin<=25)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_GEMMA_M0)
if (!(pin>=8 && pin<=10)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_QTPY_M0)
if (!((pin>=0 && pin<=3) || (pin>=6 && pin<=10))) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_SEEED_XIAO_M0)
if (!(pin>=0 && pin<=10)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_METRO_M4)
if (!(pin>=14 && pin<=21)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_ITSYBITSY_M4)
if (!(pin>=14 && pin<=20)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_FEATHER_M4)
if (!(pin>=14 && pin<=20)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_GRAND_CENTRAL_M4)
if (!((pin>=67 && pin<=74) || (pin>=54 && pin<=61))) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_BBC_MICROBIT)
if (!((pin>=0 && pin<=4) || pin==10)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_BBC_MICROBIT_V2)
if (!((pin>=0 && pin<=4) || pin==10 || pin==29)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_CALLIOPE_MINI)
if (!(pin==1 || pin==2 || (pin>=4 && pin<=6) || pin==21)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_SINOBIT)
if (!((pin>=0 && pin<=4) || pin==10)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_NRF52840_ITSYBITSY)
if (!(pin>=14 && pin<=20)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_NRF52840_CLUE)
if (!((pin>=0 && pin<=4) || pin==10 || pin==12 || pin==16)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(MAX32620)
if (!(pin>=49 && pin<=52)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_TEENSY40)
if (!((pin>=14 && pin<=27))) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_TEENSY41)
if (!((pin>=14 && pin<=27) || (pin>=38 && pin<=41))) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040)
if (!(pin>=26 && pin<=29)) error(ANALOGREAD, invalidpin, number(pin));
#endif
}
void checkanalogwrite (int pin) {
#if defined(ARDUINO_SAM_DUE)
if (!((pin>=2 && pin<=13) || pin==66 || pin==67)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_SAMD_ZERO)
if (!((pin>=3 && pin<=6) || (pin>=8 && pin<=13) || pin==14)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_SAMD_MKRZERO)
if (!((pin>=0 && pin<=8) || pin==10 || pin==18 || pin==19)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_ITSYBITSY_M0)
if (!((pin>=3 && pin<=6) || (pin>=8 && pin<=13) || (pin>=15 && pin<=16) || (pin>=22 && pin<=25))) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_GEMMA_M0)
if (!(pin==0 || pin==2 || pin==9 || pin==10)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_QTPY_M0)
if (!(pin==0 || (pin>=2 && pin<=10))) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_SEEED_XIAO_M0)
if (!(pin>=0 && pin<=10)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_METRO_M4)
if (!(pin>=0 && pin<=15)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_ITSYBITSY_M4)
if (!(pin==0 || pin==1 || pin==4 || pin==5 || pin==7 || (pin>=9 && pin<=15) || pin==21 || pin==22)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_FEATHER_M4)
if (!(pin==0 || pin==1 || (pin>=4 && pin<=6) || (pin>=9 && pin<=13) || pin==14 || pin==15 || pin==17 || pin==21 || pin==22)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_GRAND_CENTRAL_M4)
if (!((pin>=2 && pin<=9) || pin==11 || (pin>=13 && pin<=45) || pin==48 || (pin>=50 && pin<=53) || pin==58 || pin==61 || pin==68 || pin==69)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_BBC_MICROBIT)
if (!(pin>=0 && pin<=32)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_BBC_MICROBIT_V2)
if (!(pin>=0 && pin<=32)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_CALLIOPE_MINI)
if (!(pin>=0 && pin<=30)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_SINOBIT)
if (!(pin>=0 && pin<=32)) error(ANALOGREAD, invalidpin, number(pin));
#elif defined(ARDUINO_NRF52840_ITSYBITSY)
if (!(pin>=0 && pin<=25)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_NRF52840_CLUE)
if (!(pin>=0 && pin<=46)) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(MAX32620)
if (!((pin>=20 && pin<=29) || pin==32 || (pin>=40 && pin<=48))) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_TEENSY40)
if (!((pin>=0 && pin<=15) || (pin>=18 && pin<=19) || (pin>=22 && pin<=25) || (pin>=28 && pin<=29) || (pin>=33 && pin<=39))) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_TEENSY41)
if (!((pin>=0 && pin<=15) || (pin>=18 && pin<=19) || (pin>=22 && pin<=25) || (pin>=28 && pin<=29) || pin==33 || (pin>=36 && pin<=37))) error(ANALOGWRITE, invalidpin, number(pin));
#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040)
if (!(pin>=0 && pin<=29)) error(ANALOGWRITE, invalidpin, number(pin));
#endif
}
// Note
const int scale[] PROGMEM = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902};
void playnote (int pin, int note, int octave) {
#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040)
if (!(pin>=26 && pin<=29)) error(ANALOGREAD, invalidpin, number(pin));
int prescaler = 8 - octave - note/12;
if (prescaler<0 || prescaler>8) error(NOTE, PSTR("octave out of range"), number(prescaler));
tone(pin, scale[note%12]>>prescaler);
#endif
}
void nonote (int pin) {
#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040)
noTone(pin);
#endif
}
// Sleep
#if defined(CPU_ATSAMD21)
void WDT_Handler(void) {
// ISR for watchdog early warning
WDT->CTRL.bit.ENABLE = 0; // Disable watchdog
while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
WDT->INTFLAG.bit.EW = 1; // Clear interrupt flag
}
#endif
void initsleep () {
#if defined(CPU_ATSAMD21)
// One-time initialization of watchdog timer.
// Generic clock generator 2, divisor = 32 (2^(DIV+1))
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
// Enable clock generator 2 using low-power 32KHz oscillator.
// With /32 divisor above, this yields 1024Hz clock.
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
GCLK_GENCTRL_GENEN |
GCLK_GENCTRL_SRC_OSCULP32K |
GCLK_GENCTRL_DIVSEL;
while(GCLK->STATUS.bit.SYNCBUSY);
// WDT clock = clock gen 2
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN_GCLK2;
// Enable WDT early-warning interrupt
NVIC_DisableIRQ(WDT_IRQn);
NVIC_ClearPendingIRQ(WDT_IRQn);
NVIC_SetPriority(WDT_IRQn, 0); // Top priority
NVIC_EnableIRQ(WDT_IRQn);
#endif
}
void sleep (int secs) {
#if defined(CPU_ATSAMD21)
WDT->CTRL.reg = 0; // Disable watchdog for config
while(WDT->STATUS.bit.SYNCBUSY);
WDT->INTENSET.bit.EW = 1; // Enable early warning interrupt
WDT->CONFIG.bit.PER = 0xB; // Period = max
WDT->CONFIG.bit.WINDOW = 0x7; // Set time of interrupt = 1024 cycles = 1 sec
WDT->CTRL.bit.WEN = 1; // Enable window mode
while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
SysTick->CTRL = 0; // Stop SysTick interrupts
while (secs > 0) {
WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;// Clear watchdog interval
while(WDT->STATUS.bit.SYNCBUSY);
WDT->CTRL.bit.ENABLE = 1; // Start watchdog now!
while(WDT->STATUS.bit.SYNCBUSY);
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Deepest sleep
__DSB();
__WFI(); // Wait for interrupt
secs--;
}
SysTick->CTRL = 7; // Restart SysTick interrupts
#else
delay(1000*secs);
#endif
}
// Prettyprint
const int PPINDENT = 2;
const int PPWIDTH = 80;
const int GFXPPWIDTH = 52; // 320 pixel wide screen
int ppwidth = PPWIDTH;
void pcount (char c) {
if (c == '\n') PrintCount++;
PrintCount++;
}
uint8_t atomwidth (object *obj) {
PrintCount = 0;
printobject(obj, pcount);
return PrintCount;
}
uint8_t basewidth (object *obj, uint8_t base) {
PrintCount = 0;
pintbase(obj->integer, base, pcount);
return PrintCount;
}
bool quoted (object *obj) {
return (consp(obj) && car(obj) != NULL && car(obj)->name == sym(QUOTE) && consp(cdr(obj)) && cddr(obj) == NULL);
}
int subwidth (object *obj, int w) {
if (atom(obj)) return w - atomwidth(obj);
if (quoted(obj)) obj = car(cdr(obj));
return subwidthlist(obj, w - 1);
}
int subwidthlist (object *form, int w) {
while (form != NULL && w >= 0) {
if (atom(form)) return w - (2 + atomwidth(form));
w = subwidth(car(form), w - 1);
form = cdr(form);
}
return w;
}
void superprint (object *form, int lm, pfun_t pfun) {
if (atom(form)) {
if (symbolp(form) && form->name == sym(NOTHING)) printsymbol(form, pfun);
else printobject(form, pfun);
}
else if (quoted(form)) { pfun('\''); superprint(car(cdr(form)), lm + 1, pfun); }
else if (subwidth(form, ppwidth - lm) >= 0) supersub(form, lm + PPINDENT, 0, pfun);
else supersub(form, lm + PPINDENT, 1, pfun);
}
const int ppspecials = 19;
const char ppspecial[ppspecials] PROGMEM =
{ DOTIMES, DOLIST, IF, SETQ, TEE, LET, LETSTAR, LAMBDA, WHEN, UNLESS, WITHI2C, WITHSERIAL, WITHSPI, WITHSDCARD,
WITHGFX, WITHOUTPUTTOSTRING, FORMILLIS, DEFVAR, CASE };
void supersub (object *form, int lm, int super, pfun_t pfun) {
int special = 0, separate = 1;
object *arg = car(form);
if (symbolp(arg)) {
symbol_t sname = arg->name;
if (sname == sym(DEFUN) || sname == sym(DEFCODE)) special = 2;
else for (int i=0; i<ppspecials; i++) {
if (sname == sym((builtin_t)ppspecial[i])) { special = 1; break; }
}
}
while (form != NULL) {
if (atom(form)) { pfstring(PSTR(" . "), pfun); printobject(form, pfun); pfun(')'); return; }
else if (separate) { pfun('('); separate = 0; }
else if (special) { pfun(' '); special--; }
else if (!super) pfun(' ');
else { pln(pfun); indent(lm, ' ', pfun); }
superprint(car(form), lm, pfun);
form = cdr(form);
}
pfun(')'); return;
}
// Assembler
object *call (int entry, int nargs, object *args, object *env) {
#if defined(CODESIZE)
(void) env;
int param[4];
for (int i=0; i<nargs; i++) {
object *arg = first(args);
if (integerp(arg)) param[i] = arg->integer;
else param[i] = (uintptr_t)arg;
args = cdr(args);
}
int w = ((intfn_ptr_type)&MyCode[entry])(param[0], param[1], param[2], param[3]);
return number(w);
#else
return nil;
#endif
}
void putcode (object *arg, int origin, int pc) {
#if defined(CODESIZE)
int code = checkinteger(DEFCODE, arg);
MyCode[origin+pc] = code & 0xff;
MyCode[origin+pc+1] = (code>>8) & 0xff;
#if defined(assemblerlist)
printhex4(pc, pserial);
printhex4(code, pserial);
#endif
#endif
}
int assemble (int pass, int origin, object *entries, object *env, object *pcpair) {
int pc = 0; cdr(pcpair) = number(pc);
while (entries != NULL) {
object *arg = first(entries);
if (symbolp(arg)) {
if (pass == 2) {
#if defined(assemblerlist)
printhex4(pc, pserial);
indent(5, ' ', pserial);
printobject(arg, pserial); pln(pserial);
#endif
} else {
object *pair = findvalue(arg, env);
cdr(pair) = number(pc);
}
} else {
object *argval = eval(arg, env);
if (listp(argval)) {
object *arglist = argval;
while (arglist != NULL) {
if (pass == 2) {
putcode(first(arglist), origin, pc);
#if defined(assemblerlist)
if (arglist == argval) superprint(arg, 0, pserial);
pln(pserial);
#endif
}
pc = pc + 2;
cdr(pcpair) = number(pc);
arglist = cdr(arglist);
}
} else if (integerp(argval)) {
if (pass == 2) {
putcode(argval, origin, pc);
#if defined(assemblerlist)
superprint(arg, 0, pserial); pln(pserial);
#endif
}
pc = pc + 2;
cdr(pcpair) = number(pc);
} else error(DEFCODE, PSTR("illegal entry"), arg);
}
entries = cdr(entries);
}
// Round up to multiple of 4 to give code size
if (pc%4 != 0) pc = pc + 4 - pc%4;
return pc;
}
// Special forms
object *sp_quote (object *args, object *env) {
(void) env;
checkargs(QUOTE, args);
return first(args);
}
object *sp_or (object *args, object *env) {
while (args != NULL) {
object *val = eval(car(args), env);
if (val != NULL) return val;
args = cdr(args);
}
return nil;
}
object *sp_defun (object *args, object *env) {
(void) env;
checkargs(DEFUN, args);
object *var = first(args);
if (!symbolp(var)) error(DEFUN, notasymbol, var);
object *val = cons(bsymbol(LAMBDA), cdr(args));
object *pair = value(var->name,GlobalEnv);
if (pair != NULL) cdr(pair) = val;
else push(cons(var, val), GlobalEnv);
return var;
}
object *sp_defvar (object *args, object *env) {
checkargs(DEFVAR, args);
object *var = first(args);
if (!symbolp(var)) error(DEFVAR, notasymbol, var);
object *val = NULL;
args = cdr(args);
if (args != NULL) { setflag(NOESC); val = eval(first(args), env); clrflag(NOESC); }
object *pair = value(var->name, GlobalEnv);
if (pair != NULL) cdr(pair) = val;
else push(cons(var, val), GlobalEnv);
return var;
}
object *sp_setq (object *args, object *env) {
object *arg = nil;
while (args != NULL) {
if (cdr(args) == NULL) error2(SETQ, oddargs);
object *pair = findvalue(first(args), env);
arg = eval(second(args), env);
cdr(pair) = arg;
args = cddr(args);
}
return arg;
}
object *sp_loop (object *args, object *env) {
object *start = args;
for (;;) {
args = start;
while (args != NULL) {
object *result = eval(car(args),env);
if (tstflag(RETURNFLAG)) {
clrflag(RETURNFLAG);
return result;
}
args = cdr(args);
}
}
}
object *sp_return (object *args, object *env) {
object *result = eval(tf_progn(args,env), env);
setflag(RETURNFLAG);
return result;
}
object *sp_push (object *args, object *env) {
int bit;
checkargs(PUSH, args);
object *item = eval(first(args), env);
object **loc = place(PUSH, second(args), env, &bit);
push(item, *loc);
return *loc;
}
object *sp_pop (object *args, object *env) {
int bit;
checkargs(POP, args);
object **loc = place(POP, first(args), env, &bit);
object *result = car(*loc);
pop(*loc);
return result;
}
// Accessors
object *sp_incf (object *args, object *env) {
int bit;
checkargs(INCF, args);
object **loc = place(INCF, first(args), env, &bit);
args = cdr(args);
object *x = *loc;
object *inc = (args != NULL) ? eval(first(args), env) : NULL;
if (bit != -1) {
int increment;
if (inc == NULL) increment = 1; else increment = checkbitvalue(INCF, inc);
int newvalue = (((*loc)->integer)>>bit & 1) + increment;
if (newvalue & ~1) error2(INCF, PSTR("result is not a bit value"));
*loc = number((((*loc)->integer) & ~(1<<bit)) | newvalue<<bit);
return number(newvalue);
}
if (floatp(x) || floatp(inc)) {
float increment;
float value = checkintfloat(INCF, x);
if (inc == NULL) increment = 1.0; else increment = checkintfloat(INCF, inc);
*loc = makefloat(value + increment);
} else if (integerp(x) && (integerp(inc) || inc == NULL)) {
int increment;
int value = x->integer;
if (inc == NULL) increment = 1; else increment = inc->integer;
if (increment < 1) {
if (INT_MIN - increment > value) *loc = makefloat((float)value + (float)increment);
else *loc = number(value + increment);
} else {
if (INT_MAX - increment < value) *loc = makefloat((float)value + (float)increment);
else *loc = number(value + increment);
}
} else error2(INCF, notanumber);
return *loc;
}
object *sp_decf (object *args, object *env) {
int bit;
checkargs(DECF, args);
object **loc = place(DECF, first(args), env, &bit);
args = cdr(args);
object *x = *loc;
object *dec = (args != NULL) ? eval(first(args), env) : NULL;
if (bit != -1) {
int decrement;
if (dec == NULL) decrement = 1; else decrement = checkbitvalue(DECF, dec);
int newvalue = (((*loc)->integer)>>bit & 1) - decrement;
if (newvalue & ~1) error2(INCF, PSTR("result is not a bit value"));
*loc = number((((*loc)->integer) & ~(1<<bit)) | newvalue<<bit);
return number(newvalue);
}
if (floatp(x) || floatp(dec)) {
float decrement;
float value = checkintfloat(DECF, x);
if (dec == NULL) decrement = 1.0; else decrement = checkintfloat(DECF, dec);
*loc = makefloat(value - decrement);
} if (integerp(x) && (integerp(dec) || dec == NULL)) {
int decrement;
int value = x->integer;
if (dec == NULL) decrement = 1; else decrement = dec->integer;
if (decrement < 1) {
if (INT_MAX + decrement < value) *loc = makefloat((float)value - (float)decrement);
else *loc = number(value - decrement);
} else {
if (INT_MIN + decrement > value) *loc = makefloat((float)value - (float)decrement);
else *loc = number(value - decrement);
}
} else error2(DECF, notanumber);
return *loc;
}
object *sp_setf (object *args, object *env) {
int bit;
object *arg = nil;
while (args != NULL) {
if (cdr(args) == NULL) error2(SETF, oddargs);
object **loc = place(SETF, first(args), env, &bit);
arg = eval(second(args), env);
if (bit == -1) *loc = arg;
else *loc = number((checkinteger(SETF,*loc) & ~(1<<bit)) | checkbitvalue(SETF,arg)<<bit);
args = cddr(args);
}
return arg;
}
// Other special forms
object *sp_dolist (object *args, object *env) {
if (args == NULL || listlength(DOLIST, first(args)) < 2) error2(DOLIST, noargument);
object *params = first(args);
object *var = first(params);
object *list = eval(second(params), env);
push(list, GCStack); // Don't GC the list
object *pair = cons(var,nil);
push(pair,env);
params = cdr(cdr(params));
args = cdr(args);
while (list != NULL) {
if (improperp(list)) error(DOLIST, notproper, list);
cdr(pair) = first(list);
object *forms = args;
while (forms != NULL) {
object *result = eval(car(forms), env);
if (tstflag(RETURNFLAG)) {
clrflag(RETURNFLAG);
pop(GCStack);
return result;
}
forms = cdr(forms);
}
list = cdr(list);
}
cdr(pair) = nil;
pop(GCStack);
if (params == NULL) return nil;
return eval(car(params), env);
}
object *sp_dotimes (object *args, object *env) {
if (args == NULL || listlength(DOTIMES, first(args)) < 2) error2(DOTIMES, noargument);
object *params = first(args);
object *var = first(params);
int count = checkinteger(DOTIMES, eval(second(params), env));
int index = 0;
params = cdr(cdr(params));
object *pair = cons(var,number(0));
push(pair,env);
args = cdr(args);
while (index < count) {
cdr(pair) = number(index);
object *forms = args;
while (forms != NULL) {
object *result = eval(car(forms), env);
if (tstflag(RETURNFLAG)) {
clrflag(RETURNFLAG);
return result;
}
forms = cdr(forms);
}
index++;
}
cdr(pair) = number(index);
if (params == NULL) return nil;
return eval(car(params), env);
}
object *sp_trace (object *args, object *env) {
(void) env;
while (args != NULL) {
object *var = first(args);
if (!symbolp(var)) error(TRACE, notasymbol, var);
trace(var->name);
args = cdr(args);
}
int i = 0;
while (i < TRACEMAX) {
if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args);
i++;
}
return args;
}
object *sp_untrace (object *args, object *env) {
(void) env;
if (args == NULL) {
int i = 0;
while (i < TRACEMAX) {
if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args);
TraceFn[i] = 0;
i++;
}
} else {
while (args != NULL) {
object *var = first(args);
if (!symbolp(var)) error(UNTRACE, notasymbol, var);
untrace(var->name);
args = cdr(args);
}
}
return args;
}
object *sp_formillis (object *args, object *env) {
if (args == NULL) error2(FORMILLIS, noargument);
object *param = first(args);
unsigned long start = millis();
unsigned long now, total = 0;
if (param != NULL) total = checkinteger(FORMILLIS, eval(first(param), env));
eval(tf_progn(cdr(args),env), env);
do {
now = millis() - start;
testescape();
} while (now < total);
if (now <= INT_MAX) return number(now);
return nil;
}
object *sp_time (object *args, object *env) {
unsigned long start = millis();
object *result = eval(first(args), env);
unsigned long elapsed = millis() - start;
printobject(result, pserial);
pfstring(PSTR("\nTime: "), pserial);
if (elapsed < 1000) {
pint(elapsed, pserial);
pfstring(PSTR(" ms\n"), pserial);
} else {
elapsed = elapsed+50;
pint(elapsed/1000, pserial);
pserial('.'); pint((elapsed/100)%10, pserial);
pfstring(PSTR(" s\n"), pserial);
}
return bsymbol(NOTHING);
}
object *sp_withoutputtostring (object *args, object *env) {
if (args == NULL) error2(WITHOUTPUTTOSTRING, noargument);
object *params = first(args);
if (params == NULL) error2(WITHOUTPUTTOSTRING, nostream);
object *var = first(params);
object *pair = cons(var, stream(STRINGSTREAM, 0));
push(pair,env);
object *string = startstring(WITHOUTPUTTOSTRING);
push(string, GCStack);
object *forms = cdr(args);
eval(tf_progn(forms,env), env);
pop(GCStack);
return string;
}
object *sp_withserial (object *args, object *env) {
object *params = first(args);
if (params == NULL) error2(WITHSERIAL, nostream);
object *var = first(params);
int address = checkinteger(WITHSERIAL, eval(second(params), env));
params = cddr(params);
int baud = 96;
if (params != NULL) baud = checkinteger(WITHSERIAL, eval(first(params), env));
object *pair = cons(var, stream(SERIALSTREAM, address));
push(pair,env);
serialbegin(address, baud);
object *forms = cdr(args);
object *result = eval(tf_progn(forms,env), env);
serialend(address);
return result;
}
object *sp_withi2c (object *args, object *env) {
object *params = first(args);
if (params == NULL) error2(WITHI2C, nostream);
object *var = first(params);
int address = checkinteger(WITHI2C, eval(second(params), env));
params = cddr(params);
if ((address == 0 || address == 1) && params != NULL) {
address = address * 128 + checkinteger(WITHI2C, eval(first(params), env));
params = cdr(params);
}
int read = 0; // Write
I2CCount = 0;
if (params != NULL) {
object *rw = eval(first(params), env);
if (integerp(rw)) I2CCount = rw->integer;
read = (rw != NULL);
}
// Top bit of address is I2C port
TwoWire *port = &Wire;
#if defined(ULISP_I2C1)
if (address > 127) port = &Wire1;
#endif
I2Cinit(port, 1); // Pullups
object *pair = cons(var, (I2Cstart(port, address & 0x7F, read)) ? stream(I2CSTREAM, address) : nil);
push(pair,env);
object *forms = cdr(args);
object *result = eval(tf_progn(forms,env), env);
I2Cstop(port, read);
return result;
}
object *sp_withspi (object *args, object *env) {
object *params = first(args);
if (params == NULL) error2(WITHSPI, nostream);
object *var = first(params);
params = cdr(params);
if (params == NULL) error2(WITHSPI, nostream);
int pin = checkinteger(WITHSPI, eval(car(params), env));
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
params = cdr(params);
int clock = 4000, mode = SPI_MODE0, address = 0; // Defaults
BitOrder bitorder = MSBFIRST;
if (params != NULL) {
clock = checkinteger(WITHSPI, eval(car(params), env));
params = cdr(params);
if (params != NULL) {
bitorder = (checkinteger(WITHSPI, eval(car(params), env)) == 0) ? LSBFIRST : MSBFIRST;
params = cdr(params);
if (params != NULL) {
int modeval = checkinteger(WITHSPI, eval(car(params), env));
mode = (modeval == 3) ? SPI_MODE3 : (modeval == 2) ? SPI_MODE2 : (modeval == 1) ? SPI_MODE1 : SPI_MODE0;
params = cdr(params);
if (params != NULL) {
address = checkinteger(WITHSPI, eval(car(params), env));
}
}
}
}
object *pair = cons(var, stream(SPISTREAM, pin + 128*address));
push(pair,env);
SPIClass *spiClass = &SPI;
#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_GRAND_CENTRAL_M4) || defined(ARDUINO_PYBADGE_M4) || defined(ARDUINO_PYGAMER_M4) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
if (address == 1) spiClass = &SPI1;
#endif
spiClass->begin();
spiClass->beginTransaction(SPISettings(((unsigned long)clock * 1000), bitorder, mode));
digitalWrite(pin, LOW);
object *forms = cdr(args);
object *result = eval(tf_progn(forms,env), env);
digitalWrite(pin, HIGH);
spiClass->endTransaction();
return result;
}
object *sp_withsdcard (object *args, object *env) {
#if defined(sdcardsupport)
object *params = first(args);
if (params == NULL) error2(WITHSDCARD, nostream);
object *var = first(params);
object *filename = eval(second(params), env);
params = cddr(params);
SD.begin(SDCARD_SS_PIN);
int mode = 0;
if (params != NULL && first(params) != NULL) mode = checkinteger(WITHSDCARD, first(params));
int oflag = O_READ;
if (mode == 1) oflag = O_RDWR | O_CREAT | O_APPEND; else if (mode == 2) oflag = O_RDWR | O_CREAT | O_TRUNC;
if (mode >= 1) {
char buffer[BUFFERSIZE];
SDpfile = SD.open(MakeFilename(filename, buffer), oflag);
if (!SDpfile) error2(WITHSDCARD, PSTR("problem writing to SD card or invalid filename"));
} else {
char buffer[BUFFERSIZE];
SDgfile = SD.open(MakeFilename(filename, buffer), oflag);
if (!SDgfile) error2(WITHSDCARD, PSTR("problem reading from SD card or invalid filename"));
}
object *pair = cons(var, stream(SDSTREAM, 1));
push(pair,env);
object *forms = cdr(args);
object *result = eval(tf_progn(forms,env), env);
if (mode >= 1) SDpfile.close(); else SDgfile.close();
return result;
#else
(void) args, (void) env;
error2(WITHSDCARD, PSTR("not supported"));
return nil;
#endif
}
object *sp_withgfx (object *args, object *env) {
#if defined(gfxsupport)
object *params = first(args);
object *var = first(params);
object *pair = cons(var, stream(GFXSTREAM, 1));
push(pair,env);
object *forms = cdr(args);
object *result = eval(tf_progn(forms,env), env);
return result;
#else
(void) args, (void) env;
error2(WITHGFX, PSTR("not supported"));
return nil;
#endif
}
// Assembler
object *sp_defcode (object *args, object *env) {
#if defined(CODESIZE)
setflag(NOESC);
checkargs(DEFCODE, args);
object *var = first(args);
object *params = second(args);
if (!symbolp(var)) error(DEFCODE, PSTR("not a symbol"), var);
// Make parameters into synonyms for registers r0, r1, etc
int regn = 0;
while (params != NULL) {
if (regn > 3) error(DEFCODE, PSTR("more than 4 parameters"), var);
object *regpair = cons(car(params), bsymbol((builtin_t)((toradix40('r')*40+toradix40('0')+regn)*2560000))); // Symbol for r0 etc
push(regpair,env);
regn++;
params = cdr(params);
}
// Make *pc* a local variable for program counter
object *pcpair = cons(bsymbol(PSTAR), number(0));
push(pcpair,env);
args = cdr(args);
// Make labels into local variables
object *entries = cdr(args);
while (entries != NULL) {
object *arg = first(entries);
if (symbolp(arg)) {
object *pair = cons(arg,number(0));
push(pair,env);
}
entries = cdr(entries);
}
// First pass
int origin = 0;
int codesize = assemble(1, origin, cdr(args), env, pcpair);
// See if it will fit
object *globals = GlobalEnv;
while (globals != NULL) {
object *pair = car(globals);
if (pair != NULL && car(pair) != var && consp(cdr(pair))) { // Exclude me if I already exist
object *codeid = second(pair);
if (codeid->type == CODE) {
codesize = codesize + endblock(codeid) - startblock(codeid);
}
}
globals = cdr(globals);
}
if (codesize > CODESIZE) error(DEFCODE, PSTR("not enough room for code"), var);
// Compact the code block, removing gaps
origin = 0;
object *block;
int smallest;
do {
smallest = CODESIZE;
globals = GlobalEnv;
while (globals != NULL) {
object *pair = car(globals);
if (pair != NULL && car(pair) != var && consp(cdr(pair))) { // Exclude me if I already exist
object *codeid = second(pair);
if (codeid->type == CODE) {
if (startblock(codeid) < smallest && startblock(codeid) >= origin) {
smallest = startblock(codeid);
block = codeid;
}
}
}
globals = cdr(globals);
}
// Compact fragmentation if necessary
if (smallest == origin) origin = endblock(block); // No gap
else if (smallest < CODESIZE) { // Slide block down
int target = origin;
for (int i=startblock(block); i<endblock(block); i++) {
MyCode[target] = MyCode[i];
target++;
}
block->integer = target<<16 | origin;
origin = target;
}
} while (smallest < CODESIZE);
// Second pass - origin is first free location
codesize = assemble(2, origin, cdr(args), env, pcpair);
object *val = cons(codehead((origin+codesize)<<16 | origin), args);
object *pair = value(var->name, GlobalEnv);
if (pair != NULL) cdr(pair) = val;
else push(cons(var, val), GlobalEnv);
clrflag(NOESC);
return var;
#else
error2(DEFCODE, PSTR("not available"));
return nil;
#endif
}
// Tail-recursive forms
object *tf_progn (object *args, object *env) {
if (args == NULL) return nil;
object *more = cdr(args);
while (more != NULL) {
object *result = eval(car(args),env);
if (tstflag(RETURNFLAG)) return result;
args = more;
more = cdr(args);
}
return car(args);
}
object *tf_if (object *args, object *env) {
if (args == NULL || cdr(args) == NULL) error2(IF, toofewargs);
if (eval(first(args), env) != nil) return second(args);
args = cddr(args);
return (args != NULL) ? first(args) : nil;
}
object *tf_cond (object *args, object *env) {
while (args != NULL) {
object *clause = first(args);
if (!consp(clause)) error(COND, illegalclause, clause);
object *test = eval(first(clause), env);
object *forms = cdr(clause);
if (test != nil) {
if (forms == NULL) return quote(test); else return tf_progn(forms, env);
}
args = cdr(args);
}
return nil;
}
object *tf_when (object *args, object *env) {
if (args == NULL) error2(WHEN, noargument);
if (eval(first(args), env) != nil) return tf_progn(cdr(args),env);
else return nil;
}
object *tf_unless (object *args, object *env) {
if (args == NULL) error2(UNLESS, noargument);
if (eval(first(args), env) != nil) return nil;
else return tf_progn(cdr(args),env);
}
object *tf_case (object *args, object *env) {
object *test = eval(first(args), env);
args = cdr(args);
while (args != NULL) {
object *clause = first(args);
if (!consp(clause)) error(CASE, illegalclause, clause);
object *key = car(clause);
object *forms = cdr(clause);
if (consp(key)) {
while (key != NULL) {
if (eq(test,car(key))) return tf_progn(forms, env);
key = cdr(key);
}
} else if (eq(test,key) || eq(key,tee)) return tf_progn(forms, env);
args = cdr(args);
}
return nil;
}
object *tf_and (object *args, object *env) {
if (args == NULL) return tee;
object *more = cdr(args);
while (more != NULL) {
if (eval(car(args), env) == NULL) return nil;
args = more;
more = cdr(args);
}
return car(args);
}
// Core functions
object *fn_not (object *args, object *env) {
(void) env;
return (first(args) == nil) ? tee : nil;
}
object *fn_cons (object *args, object *env) {
(void) env;
return cons(first(args), second(args));
}
object *fn_atom (object *args, object *env) {
(void) env;
return atom(first(args)) ? tee : nil;
}
object *fn_listp (object *args, object *env) {
(void) env;
return listp(first(args)) ? tee : nil;
}
object *fn_consp (object *args, object *env) {
(void) env;
return consp(first(args)) ? tee : nil;
}
object *fn_symbolp (object *args, object *env) {
(void) env;
object *arg = first(args);
return (arg == NULL || symbolp(arg)) ? tee : nil;
}
object *fn_arrayp (object *args, object *env) {
(void) env;
return arrayp(first(args)) ? tee : nil;
}
object *fn_boundp (object *args, object *env) {
(void) env;
object *var = first(args);
if (!symbolp(var)) error(BOUNDP, notasymbol, var);
return boundp(var, env) ? tee : nil;
}
object *fn_setfn (object *args, object *env) {
object *arg = nil;
while (args != NULL) {
if (cdr(args) == NULL) error2(SETFN, oddargs);
object *pair = findvalue(first(args), env);
arg = second(args);
cdr(pair) = arg;
args = cddr(args);
}
return arg;
}
object *fn_streamp (object *args, object *env) {
(void) env;
object *arg = first(args);
return streamp(arg) ? tee : nil;
}
object *fn_eq (object *args, object *env) {
(void) env;
return eq(first(args), second(args)) ? tee : nil;
}
// List functions
object *fn_car (object *args, object *env) {
(void) env;
return carx(first(args));
}
object *fn_cdr (object *args, object *env) {
(void) env;
return cdrx(first(args));
}
object *cxxxr (object *args, uint8_t pattern) {
object *arg = first(args);
while (pattern != 1) {
if ((pattern & 1) == 0) arg = carx(arg); else arg = cdrx(arg);
pattern = pattern>>1;
}
return arg;
}
object *fn_caar (object *args, object *env) {
(void) env;
return cxxxr(args, 0b100);
}
object *fn_cadr (object *args, object *env) {
(void) env;
return cxxxr(args, 0b101);
}
object *fn_cdar (object *args, object *env) {
(void) env;
return cxxxr(args, 0b110);
}
object *fn_cddr (object *args, object *env) {
(void) env;
return cxxxr(args, 0b111);
}
object *fn_caaar (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1000);
}
object *fn_caadr (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1001);;
}
object *fn_cadar (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1010);
}
object *fn_caddr (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1011);
}
object *fn_cdaar (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1100);
}
object *fn_cdadr (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1101);
}
object *fn_cddar (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1110);
}
object *fn_cdddr (object *args, object *env) {
(void) env;
return cxxxr(args, 0b1111);
}
object *fn_length (object *args, object *env) {
(void) env;
object *arg = first(args);
if (listp(arg)) return number(listlength(LENGTH, arg));
if (stringp(arg)) return number(stringlength(arg));
if (!(arrayp(arg) && cdr(cddr(arg)) == NULL)) error(LENGTH, PSTR("argument is not a list, 1d array, or string"), arg);
return number(abs(first(cddr(arg))->integer));
}
object *fn_arraydimensions (object *args, object *env) {
(void) env;
object *array = first(args);
if (!arrayp(array)) error(ARRAYDIMENSIONS, PSTR("argument is not an array"), array);
object *dimensions = cddr(array);
return (first(dimensions)->integer < 0) ? cons(number(-(first(dimensions)->integer)), cdr(dimensions)) : dimensions;
}
object *fn_list (object *args, object *env) {
(void) env;
return args;
}
object *fn_makearray (object *args, object *env) {
(void) env;
object *def = nil;
bool bitp = false;
object *dims = first(args);
if (dims == NULL) error2(MAKEARRAY, PSTR("dimensions can't be nil"));
else if (atom(dims)) dims = cons(dims, NULL);
args = cdr(args);
while (args != NULL && cdr(args) != NULL) {
object *var = first(args);
if (isbuiltin(first(args), INITIALELEMENT)) def = second(args);
else if (isbuiltin(first(args), ELEMENTTYPE) && isbuiltin(second(args), BIT)) bitp = true;
else error(MAKEARRAY, PSTR("argument not recognised"), var);
args = cddr(args);
}
if (bitp) {
if (def == nil) def = number(0);
else def = number(-checkbitvalue(MAKEARRAY, def)); // 1 becomes all ones
}
return makearray(MAKEARRAY, dims, def, bitp);
}
object *fn_reverse (object *args, object *env) {
(void) env;
object *list = first(args);
object *result = NULL;
while (list != NULL) {
if (improperp(list)) error(REVERSE, notproper, list);
push(first(list),result);
list = cdr(list);
}
return result;
}
object *fn_nth (object *args, object *env) {
(void) env;
int n = checkinteger(NTH, first(args));
if (n < 0) error(NTH, indexnegative, first(args));
object *list = second(args);
while (list != NULL) {
if (improperp(list)) error(NTH, notproper, list);
if (n == 0) return car(list);
list = cdr(list);
n--;
}
return nil;
}
object *fn_areff (object *args, object *env) {
(void) env;
int bit;
object *array = first(args);
if (!arrayp(array)) error(AREFF, PSTR("first argument is not an array"), array);
object *loc = *getarray(AREFF, array, cdr(args), 0, &bit);
if (bit == -1) return loc;
else return number((loc->integer)>>bit & 1);
}
object *fn_assoc (object *args, object *env) {
(void) env;
object *key = first(args);
object *list = second(args);
return assoc(key,list);
}
object *fn_member (object *args, object *env) {
(void) env;
object *item = first(args);
object *list = second(args);
while (list != NULL) {
if (improperp(list)) error(MEMBER, notproper, list);
if (eq(item,car(list))) return list;
list = cdr(list);
}
return nil;
}
object *fn_apply (object *args, object *env) {
object *previous = NULL;
object *last = args;
while (cdr(last) != NULL) {
previous = last;
last = cdr(last);
}
object *arg = car(last);
if (!listp(arg)) error(APPLY, notalist, arg);
cdr(previous) = arg;
return apply(APPLY, first(args), cdr(args), env);
}
object *fn_funcall (object *args, object *env) {
return apply(FUNCALL, first(args), cdr(args), env);
}
object *fn_append (object *args, object *env) {
(void) env;
object *head = NULL;
object *tail;
while (args != NULL) {
object *list = first(args);
if (!listp(list)) error(APPEND, notalist, list);
while (consp(list)) {
object *obj = cons(car(list), cdr(list));
if (head == NULL) head = obj;
else cdr(tail) = obj;
tail = obj;
list = cdr(list);
if (cdr(args) != NULL && improperp(list)) error(APPEND, notproper, first(args));
}
args = cdr(args);
}
return head;
}
object *fn_mapc (object *args, object *env) {
object *function = first(args);
args = cdr(args);
object *result = first(args);
object *params = cons(NULL, NULL);
push(params,GCStack);
// Make parameters
while (true) {
object *tailp = params;
object *lists = args;
while (lists != NULL) {
object *list = car(lists);
if (list == NULL) {
pop(GCStack);
return result;
}
if (improperp(list)) error(MAPC, notproper, list);
object *obj = cons(first(list),NULL);
car(lists) = cdr(list);
cdr(tailp) = obj; tailp = obj;
lists = cdr(lists);
}
apply(MAPC, function, cdr(params), env);
}
}
void mapcarfun (object *result, object **tail) {
object *obj = cons(result,NULL);
cdr(*tail) = obj; *tail = obj;
}
void mapcanfun (object *result, object **tail) {
if (cdr(*tail) != NULL) error(MAPCAN, notproper, *tail);
while (consp(result)) {
cdr(*tail) = result; *tail = result;
result = cdr(result);
}
}
object *mapcarcan (builtin_t name, object *args, object *env, mapfun_t fun) {
object *function = first(args);
args = cdr(args);
object *params = cons(NULL, NULL);
push(params,GCStack);
object *head = cons(NULL, NULL);
push(head,GCStack);
object *tail = head;
// Make parameters
while (true) {
object *tailp = params;
object *lists = args;
while (lists != NULL) {
object *list = car(lists);
if (list == NULL) {
pop(GCStack);
pop(GCStack);
return cdr(head);
}
if (improperp(list)) error(name, notproper, list);
object *obj = cons(first(list),NULL);
car(lists) = cdr(list);
cdr(tailp) = obj; tailp = obj;
lists = cdr(lists);
}
object *result = apply(name, function, cdr(params), env);
fun(result, &tail);
}
}
object *fn_mapcar (object *args, object *env) {
return mapcarcan(MAPCAR, args, env, mapcarfun);
}
object *fn_mapcan (object *args, object *env) {
return mapcarcan(MAPCAN, args, env, mapcanfun);
}
// Arithmetic functions
object *add_floats (object *args, float fresult) {
while (args != NULL) {
object *arg = car(args);
fresult = fresult + checkintfloat(ADD, arg);
args = cdr(args);
}
return makefloat(fresult);
}
object *fn_add (object *args, object *env) {
(void) env;
int result = 0;
while (args != NULL) {
object *arg = car(args);
if (floatp(arg)) return add_floats(args, (float)result);
else if (integerp(arg)) {
int val = arg->integer;
if (val < 1) { if (INT_MIN - val > result) return add_floats(args, (float)result); }
else { if (INT_MAX - val < result) return add_floats(args, (float)result); }
result = result + val;
} else error(ADD, notanumber, arg);
args = cdr(args);
}
return number(result);
}
object *subtract_floats (object *args, float fresult) {
while (args != NULL) {
object *arg = car(args);
fresult = fresult - checkintfloat(SUBTRACT, arg);
args = cdr(args);
}
return makefloat(fresult);
}
object *negate (object *arg) {
if (integerp(arg)) {
int result = arg->integer;
if (result == INT_MIN) return makefloat(-result);
else return number(-result);
} else if (floatp(arg)) return makefloat(-(arg->single_float));
else error(SUBTRACT, notanumber, arg);
return nil;
}
object *fn_subtract (object *args, object *env) {
(void) env;
object *arg = car(args);
args = cdr(args);
if (args == NULL) return negate(arg);
else if (floatp(arg)) return subtract_floats(args, arg->single_float);
else if (integerp(arg)) {
int result = arg->integer;
while (args != NULL) {
arg = car(args);
if (floatp(arg)) return subtract_floats(args, result);
else if (integerp(arg)) {
int val = (car(args))->integer;
if (val < 1) { if (INT_MAX + val < result) return subtract_floats(args, result); }
else { if (INT_MIN + val > result) return subtract_floats(args, result); }
result = result - val;
} else error(SUBTRACT, notanumber, arg);
args = cdr(args);
}
return number(result);
} else error(SUBTRACT, notanumber, arg);
return nil;
}
object *multiply_floats (object *args, float fresult) {
while (args != NULL) {
object *arg = car(args);
fresult = fresult * checkintfloat(MULTIPLY, arg);
args = cdr(args);
}
return makefloat(fresult);
}
object *fn_multiply (object *args, object *env) {
(void) env;
int result = 1;
while (args != NULL){
object *arg = car(args);
if (floatp(arg)) return multiply_floats(args, result);
else if (integerp(arg)) {
int64_t val = result * (int64_t)(arg->integer);
if ((val > INT_MAX) || (val < INT_MIN)) return multiply_floats(args, result);
result = val;
} else error(MULTIPLY, notanumber, arg);
args = cdr(args);
}
return number(result);
}
object *divide_floats (object *args, float fresult) {
while (args != NULL) {
object *arg = car(args);
float f = checkintfloat(DIVIDE, arg);
if (f == 0.0) error2(DIVIDE, divisionbyzero);
fresult = fresult / f;
args = cdr(args);
}
return makefloat(fresult);
}
object *fn_divide (object *args, object *env) {
(void) env;
object* arg = first(args);
args = cdr(args);
// One argument
if (args == NULL) {
if (floatp(arg)) {
float f = arg->single_float;
if (f == 0.0) error2(DIVIDE, PSTR("division by zero"));
return makefloat(1.0 / f);
} else if (integerp(arg)) {
int i = arg->integer;
if (i == 0) error2(DIVIDE, PSTR("division by zero"));
else if (i == 1) return number(1);
else return makefloat(1.0 / i);
} else error(DIVIDE, notanumber, arg);
}
// Multiple arguments
if (floatp(arg)) return divide_floats(args, arg->single_float);
else if (integerp(arg)) {
int result = arg->integer;
while (args != NULL) {
arg = car(args);
if (floatp(arg)) {
return divide_floats(args, result);
} else if (integerp(arg)) {
int i = arg->integer;
if (i == 0) error2(DIVIDE, PSTR("division by zero"));
if ((result % i) != 0) return divide_floats(args, result);
if ((result == INT_MIN) && (i == -1)) return divide_floats(args, result);
result = result / i;
args = cdr(args);
} else error(DIVIDE, notanumber, arg);
}
return number(result);
} else error(DIVIDE, notanumber, arg);
return nil;
}
object *fn_mod (object *args, object *env) {
(void) env;
object *arg1 = first(args);
object *arg2 = second(args);
if (integerp(arg1) && integerp(arg2)) {
int divisor = arg2->integer;
if (divisor == 0) error2(MOD, PSTR("division by zero"));
int dividend = arg1->integer;
int remainder = dividend % divisor;
if ((dividend<0) != (divisor<0)) remainder = remainder + divisor;
return number(remainder);
} else {
float fdivisor = checkintfloat(MOD, arg2);
if (fdivisor == 0.0) error2(MOD, PSTR("division by zero"));
float fdividend = checkintfloat(MOD, arg1);
float fremainder = fmod(fdividend , fdivisor);
if ((fdividend<0) != (fdivisor<0)) fremainder = fremainder + fdivisor;
return makefloat(fremainder);
}
}
object *fn_oneplus (object *args, object *env) {
(void) env;
object* arg = first(args);
if (floatp(arg)) return makefloat((arg->single_float) + 1.0);
else if (integerp(arg)) {
int result = arg->integer;
if (result == INT_MAX) return makefloat((arg->integer) + 1.0);
else return number(result + 1);
} else error(ONEPLUS, notanumber, arg);
return nil;
}
object *fn_oneminus (object *args, object *env) {
(void) env;
object* arg = first(args);
if (floatp(arg)) return makefloat((arg->single_float) - 1.0);
else if (integerp(arg)) {
int result = arg->integer;
if (result == INT_MIN) return makefloat((arg->integer) - 1.0);
else return number(result - 1);
} else error(ONEMINUS, notanumber, arg);
return nil;
}
object *fn_abs (object *args, object *env) {
(void) env;
object *arg = first(args);
if (floatp(arg)) return makefloat(abs(arg->single_float));
else if (integerp(arg)) {
int result = arg->integer;
if (result == INT_MIN) return makefloat(abs((float)result));
else return number(abs(result));
} else error(ABS, notanumber, arg);
return nil;
}
object *fn_random (object *args, object *env) {
(void) env;
object *arg = first(args);
if (integerp(arg)) return number(random(arg->integer));
else if (floatp(arg)) return makefloat((float)rand()/(float)(RAND_MAX/(arg->single_float)));
else error(RANDOM, notanumber, arg);
return nil;
}
object *fn_maxfn (object *args, object *env) {
(void) env;
object* result = first(args);
args = cdr(args);
while (args != NULL) {
object *arg = car(args);
if (integerp(result) && integerp(arg)) {
if ((arg->integer) > (result->integer)) result = arg;
} else if ((checkintfloat(MAXFN, arg) > checkintfloat(MAXFN, result))) result = arg;
args = cdr(args);
}
return result;
}
object *fn_minfn (object *args, object *env) {
(void) env;
object* result = first(args);
args = cdr(args);
while (args != NULL) {
object *arg = car(args);
if (integerp(result) && integerp(arg)) {
if ((arg->integer) < (result->integer)) result = arg;
} else if ((checkintfloat(MINFN, arg) < checkintfloat(MINFN, result))) result = arg;
args = cdr(args);
}
return result;
}
// Arithmetic comparisons
object *fn_noteq (object *args, object *env) {
(void) env;
while (args != NULL) {
object *nargs = args;
object *arg1 = first(nargs);
nargs = cdr(nargs);
while (nargs != NULL) {
object *arg2 = first(nargs);
if (integerp(arg1) && integerp(arg2)) {
if ((arg1->integer) == (arg2->integer)) return nil;
} else if ((checkintfloat(NOTEQ, arg1) == checkintfloat(NOTEQ, arg2))) return nil;
nargs = cdr(nargs);
}
args = cdr(args);
}
return tee;
}
object *compare (builtin_t name, object *args, bool lt, bool gt, bool eq) {
object *arg1 = first(args);
args = cdr(args);
while (args != NULL) {
object *arg2 = first(args);
if (integerp(arg1) && integerp(arg2)) {
if (!lt && ((arg1->integer) < (arg2->integer))) return nil;
if (!eq && ((arg1->integer) == (arg2->integer))) return nil;
if (!gt && ((arg1->integer) > (arg2->integer))) return nil;
} else {
if (!lt && (checkintfloat(name, arg1) < checkintfloat(name, arg2))) return nil;
if (!eq && (checkintfloat(name, arg1) == checkintfloat(name, arg2))) return nil;
if (!gt && (checkintfloat(name, arg1) > checkintfloat(name, arg2))) return nil;
}
arg1 = arg2;
args = cdr(args);
}
return tee;
}
object *fn_numeq (object *args, object *env) {
(void) env;
return compare(NUMEQ, args, false, false, true);
}
object *fn_less (object *args, object *env) {
(void) env;
return compare(LESS, args, true, false, false);
}
object *fn_lesseq (object *args, object *env) {
(void) env;
return compare(LESSEQ, args, true, false, true);
}
object *fn_greater (object *args, object *env) {
(void) env;
return compare(GREATER, args, false, true, false);
}
object *fn_greatereq (object *args, object *env) {
(void) env;
return compare(GREATEREQ, args, false, true, true);
}
object *fn_plusp (object *args, object *env) {
(void) env;
object *arg = first(args);
if (floatp(arg)) return ((arg->single_float) > 0.0) ? tee : nil;
else if (integerp(arg)) return ((arg->integer) > 0) ? tee : nil;
else error(PLUSP, notanumber, arg);
return nil;
}
object *fn_minusp (object *args, object *env) {
(void) env;
object *arg = first(args);
if (floatp(arg)) return ((arg->single_float) < 0.0) ? tee : nil;
else if (integerp(arg)) return ((arg->integer) < 0) ? tee : nil;
else error(MINUSP, notanumber, arg);
return nil;
}
object *fn_zerop (object *args, object *env) {
(void) env;
object *arg = first(args);
if (floatp(arg)) return ((arg->single_float) == 0.0) ? tee : nil;
else if (integerp(arg)) return ((arg->integer) == 0) ? tee : nil;
else error(ZEROP, notanumber, arg);
return nil;
}
object *fn_oddp (object *args, object *env) {
(void) env;
int arg = checkinteger(ODDP, first(args));
return ((arg & 1) == 1) ? tee : nil;
}
object *fn_evenp (object *args, object *env) {
(void) env;
int arg = checkinteger(EVENP, first(args));
return ((arg & 1) == 0) ? tee : nil;
}
// Number functions
object *fn_integerp (object *args, object *env) {
(void) env;
return integerp(first(args)) ? tee : nil;
}
object *fn_numberp (object *args, object *env) {
(void) env;
object *arg = first(args);
return (integerp(arg) || floatp(arg)) ? tee : nil;
}
// Floating-point functions
object *fn_floatfn (object *args, object *env) {
(void) env;
object *arg = first(args);
return (floatp(arg)) ? arg : makefloat((float)(arg->integer));
}
object *fn_floatp (object *args, object *env) {
(void) env;
return floatp(first(args)) ? tee : nil;
}
object *fn_sin (object *args, object *env) {
(void) env;
return makefloat(sin(checkintfloat(SIN, first(args))));
}
object *fn_cos (object *args, object *env) {
(void) env;
return makefloat(cos(checkintfloat(COS, first(args))));
}
object *fn_tan (object *args, object *env) {
(void) env;
return makefloat(tan(checkintfloat(TAN, first(args))));
}
object *fn_asin (object *args, object *env) {
(void) env;
return makefloat(asin(checkintfloat(ASIN, first(args))));
}
object *fn_acos (object *args, object *env) {
(void) env;
return makefloat(acos(checkintfloat(ACOS, first(args))));
}
object *fn_atan (object *args, object *env) {
(void) env;
object *arg = first(args);
float div = 1.0;
args = cdr(args);
if (args != NULL) div = checkintfloat(ATAN, first(args));
return makefloat(atan2(checkintfloat(ATAN, arg), div));
}
object *fn_sinh (object *args, object *env) {
(void) env;
return makefloat(sinh(checkintfloat(SINH, first(args))));
}
object *fn_cosh (object *args, object *env) {
(void) env;
return makefloat(cosh(checkintfloat(COSH, first(args))));
}
object *fn_tanh (object *args, object *env) {
(void) env;
return makefloat(tanh(checkintfloat(TANH, first(args))));
}
object *fn_exp (object *args, object *env) {
(void) env;
return makefloat(exp(checkintfloat(EXP, first(args))));
}
object *fn_sqrt (object *args, object *env) {
(void) env;
return makefloat(sqrt(checkintfloat(SQRT, first(args))));
}
object *fn_log (object *args, object *env) {
(void) env;
object *arg = first(args);
float fresult = log(checkintfloat(LOG, arg));
args = cdr(args);
if (args == NULL) return makefloat(fresult);
else return makefloat(fresult / log(checkintfloat(LOG, first(args))));
}
int intpower (int base, int exp) {
int result = 1;
while (exp) {
if (exp & 1) result = result * base;
exp = exp / 2;
base = base * base;
}
return result;
}
object *fn_expt (object *args, object *env) {
(void) env;
object *arg1 = first(args); object *arg2 = second(args);
float float1 = checkintfloat(EXPT, arg1);
float value = log(abs(float1)) * checkintfloat(EXPT, arg2);
if (integerp(arg1) && integerp(arg2) && ((arg2->integer) >= 0) && (abs(value) < 21.4875))
return number(intpower(arg1->integer, arg2->integer));
if (float1 < 0) {
if (integerp(arg2)) return makefloat((arg2->integer & 1) ? -exp(value) : exp(value));
else error2(EXPT, PSTR("invalid result"));
}
return makefloat(exp(value));
}
object *fn_ceiling (object *args, object *env) {
(void) env;
object *arg = first(args);
args = cdr(args);
if (args != NULL) return number(ceil(checkintfloat(CEILING, arg) / checkintfloat(CEILING, first(args))));
else return number(ceil(checkintfloat(CEILING, arg)));
}
object *fn_floor (object *args, object *env) {
(void) env;
object *arg = first(args);
args = cdr(args);
if (args != NULL) return number(floor(checkintfloat(FLOOR, arg) / checkintfloat(FLOOR, first(args))));
else return number(floor(checkintfloat(FLOOR, arg)));
}
object *fn_truncate (object *args, object *env) {
(void) env;
object *arg = first(args);
args = cdr(args);
if (args != NULL) return number((int)(checkintfloat(TRUNCATE, arg) / checkintfloat(TRUNCATE, first(args))));
else return number((int)(checkintfloat(TRUNCATE, arg)));
}
int myround (float number) {
return (number >= 0) ? (int)(number + 0.5) : (int)(number - 0.5);
}
object *fn_round (object *args, object *env) {
(void) env;
object *arg = first(args);
args = cdr(args);
if (args != NULL) return number(myround(checkintfloat(ROUND, arg) / checkintfloat(ROUND, first(args))));
else return number(myround(checkintfloat(ROUND, arg)));
}
// Characters
object *fn_char (object *args, object *env) {
(void) env;
object *arg = first(args);
if (!stringp(arg)) error(CHAR, notastring, arg);
char c = nthchar(arg, checkinteger(CHAR, second(args)));
if (c == 0) error2(CHAR, indexrange);
return character(c);
}
object *fn_charcode (object *args, object *env) {
(void) env;
return number(checkchar(CHARCODE, first(args)));
}
object *fn_codechar (object *args, object *env) {
(void) env;
return character(checkinteger(CODECHAR, first(args)));
}
object *fn_characterp (object *args, object *env) {
(void) env;
return characterp(first(args)) ? tee : nil;
}
// Strings
object *fn_stringp (object *args, object *env) {
(void) env;
return stringp(first(args)) ? tee : nil;
}
bool stringcompare (builtin_t name, object *args, bool lt, bool gt, bool eq) {
object *arg1 = checkstring(name, first(args));
object *arg2 = checkstring(name, second(args));
arg1 = cdr(arg1);
arg2 = cdr(arg2);
while ((arg1 != NULL) || (arg2 != NULL)) {
if (arg1 == NULL) return lt;
if (arg2 == NULL) return gt;
if (arg1->chars < arg2->chars) return lt;
if (arg1->chars > arg2->chars) return gt;
arg1 = car(arg1);
arg2 = car(arg2);
}
return eq;
}
object *fn_stringeq (object *args, object *env) {
(void) env;
return stringcompare(STRINGEQ, args, false, false, true) ? tee : nil;
}
object *fn_stringless (object *args, object *env) {
(void) env;
return stringcompare(STRINGLESS, args, true, false, false) ? tee : nil;
}
object *fn_stringgreater (object *args, object *env) {
(void) env;
return stringcompare(STRINGGREATER, args, false, true, false) ? tee : nil;
}
object *fn_sort (object *args, object *env) {
if (first(args) == NULL) return nil;
object *list = cons(nil,first(args));
push(list,GCStack);
object *predicate = second(args);
object *compare = cons(NULL, cons(NULL, NULL));
push(compare,GCStack);
object *ptr = cdr(list);
while (cdr(ptr) != NULL) {
object *go = list;
while (go != ptr) {
car(compare) = car(cdr(ptr));
car(cdr(compare)) = car(cdr(go));
if (apply(SORT, predicate, compare, env)) break;
go = cdr(go);
}
if (go != ptr) {
object *obj = cdr(ptr);
cdr(ptr) = cdr(obj);
cdr(obj) = cdr(go);
cdr(go) = obj;
} else ptr = cdr(ptr);
}
pop(GCStack); pop(GCStack);
return cdr(list);
}
object *fn_stringfn (object *args, object *env) {
return fn_princtostring(args, env);
}
object *fn_concatenate (object *args, object *env) {
(void) env;
object *arg = first(args);
if (builtin(arg->name) != STRINGFN) error2(CONCATENATE, PSTR("only supports strings"));
args = cdr(args);
object *result = newstring();
object *tail = result;
while (args != NULL) {
object *obj = checkstring(CONCATENATE, first(args));
obj = cdr(obj);
while (obj != NULL) {
int quad = obj->chars;
while (quad != 0) {
char ch = quad>>((sizeof(int)-1)*8) & 0xFF;
buildstring(ch, &tail);
quad = quad<<8;
}
obj = car(obj);
}
args = cdr(args);
}
return result;
}
object *fn_subseq (object *args, object *env) {
(void) env;
object *arg = checkstring(SUBSEQ, first(args));
int start = checkinteger(SUBSEQ, second(args));
if (start < 0) error(SUBSEQ, indexnegative, second(args));
int end;
args = cddr(args);
if (args != NULL) end = checkinteger(SUBSEQ, car(args)); else end = stringlength(arg);
object *result = newstring();
object *tail = result;
for (int i=start; i<end; i++) {
char ch = nthchar(arg, i);
if (ch == 0) error2(SUBSEQ, indexrange);
buildstring(ch, &tail);
}
return result;
}
object *fn_readfromstring (object *args, object *env) {
(void) env;
object *arg = checkstring(READFROMSTRING, first(args));
GlobalString = arg;
GlobalStringIndex = 0;
return read(gstr);
}
object *fn_princtostring (object *args, object *env) {
(void) env;
object *arg = first(args);
object *obj = startstring(PRINCTOSTRING);
prin1object(arg, pstr);
return obj;
}
object *fn_prin1tostring (object *args, object *env) {
(void) env;
object *arg = first(args);
object *obj = startstring(PRIN1TOSTRING);
printobject(arg, pstr);
return obj;
}
// Bitwise operators
object *fn_logand (object *args, object *env) {
(void) env;
int result = -1;
while (args != NULL) {
result = result & checkinteger(LOGAND, first(args));
args = cdr(args);
}
return number(result);
}
object *fn_logior (object *args, object *env) {
(void) env;
int result = 0;
while (args != NULL) {
result = result | checkinteger(LOGIOR, first(args));
args = cdr(args);
}
return number(result);
}
object *fn_logxor (object *args, object *env) {
(void) env;
int result = 0;
while (args != NULL) {
result = result ^ checkinteger(LOGXOR, first(args));
args = cdr(args);
}
return number(result);
}
object *fn_lognot (object *args, object *env) {
(void) env;
int result = checkinteger(LOGNOT, car(args));
return number(~result);
}
object *fn_ash (object *args, object *env) {
(void) env;
int value = checkinteger(ASH, first(args));
int count = checkinteger(ASH, second(args));
if (count >= 0) return number(value << count);
else return number(value >> abs(count));
}
object *fn_logbitp (object *args, object *env) {
(void) env;
int index = checkinteger(LOGBITP, first(args));
int value = checkinteger(LOGBITP, second(args));
return (bitRead(value, index) == 1) ? tee : nil;
}
// System functions
object *fn_eval (object *args, object *env) {
return eval(first(args), env);
}
object *fn_globals (object *args, object *env) {
(void) args;
if (GlobalEnv == NULL) return nil;
return fn_mapcar(cons(bsymbol(CAR),cons(GlobalEnv,nil)), env);
}
object *fn_locals (object *args, object *env) {
(void) args;
return env;
}
object *fn_makunbound (object *args, object *env) {
(void) env;
object *var = first(args);
if (!symbolp(var)) error(MAKUNBOUND, notasymbol, var);
delassoc(var, &GlobalEnv);
return var;
}
object *fn_break (object *args, object *env) {
(void) args;
pfstring(PSTR("\nBreak!\n"), pserial);
BreakLevel++;
repl(env);
BreakLevel--;
return nil;
}
object *fn_read (object *args, object *env) {
(void) env;
gfun_t gfun = gstreamfun(args);
return read(gfun);
}
object *fn_prin1 (object *args, object *env) {
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
printobject(obj, pfun);
return obj;
}
object *fn_print (object *args, object *env) {
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
pln(pfun);
printobject(obj, pfun);
pfun(' ');
return obj;
}
object *fn_princ (object *args, object *env) {
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
prin1object(obj, pfun);
return obj;
}
object *fn_terpri (object *args, object *env) {
(void) env;
pfun_t pfun = pstreamfun(args);
pln(pfun);
return nil;
}
object *fn_readbyte (object *args, object *env) {
(void) env;
gfun_t gfun = gstreamfun(args);
int c = gfun();
return (c == -1) ? nil : number(c);
}
object *fn_readline (object *args, object *env) {
(void) env;
gfun_t gfun = gstreamfun(args);
return readstring('\n', gfun);
}
object *fn_writebyte (object *args, object *env) {
(void) env;
int value = checkinteger(WRITEBYTE, first(args));
pfun_t pfun = pstreamfun(cdr(args));
(pfun)(value);
return nil;
}
object *fn_writestring (object *args, object *env) {
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
char temp = Flags;
clrflag(PRINTREADABLY);
printstring(obj, pfun);
Flags = temp;
return nil;
}
object *fn_writeline (object *args, object *env) {
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
char temp = Flags;
clrflag(PRINTREADABLY);
printstring(obj, pfun);
pln(pfun);
Flags = temp;
return nil;
}
object *fn_restarti2c (object *args, object *env) {
(void) env;
int stream = first(args)->integer;
args = cdr(args);
int read = 0; // Write
I2CCount = 0;
if (args != NULL) {
object *rw = first(args);
if (integerp(rw)) I2CCount = rw->integer;
read = (rw != NULL);
}
int address = stream & 0xFF;
if (stream>>8 != I2CSTREAM) error2(RESTARTI2C, PSTR("not an i2c stream"));
TwoWire *port;
if (address < 128) port = &Wire;
#if defined(ULISP_I2C1)
else port = &Wire1;
#endif
return I2Crestart(port, address & 0x7F, read) ? tee : nil;
}
object *fn_gc (object *obj, object *env) {
int initial = Freespace;
unsigned long start = micros();
gc(obj, env);
unsigned long elapsed = micros() - start;
pfstring(PSTR("Space: "), pserial);
pint(Freespace - initial, pserial);
pfstring(PSTR(" bytes, Time: "), pserial);
pint(elapsed, pserial);
pfstring(PSTR(" us\n"), pserial);
return nil;
}
object *fn_room (object *args, object *env) {
(void) args, (void) env;
return number(Freespace);
}
object *fn_saveimage (object *args, object *env) {
if (args != NULL) args = eval(first(args), env);
return number(saveimage(args));
}
object *fn_loadimage (object *args, object *env) {
(void) env;
if (args != NULL) args = first(args);
return number(loadimage(args));
}
object *fn_cls (object *args, object *env) {
(void) args, (void) env;
pserial(12);
return nil;
}
// Arduino procedures
object *fn_pinmode (object *args, object *env) {
(void) env; int pin;
object *arg = first(args);
if (keywordp(arg)) pin = checkkeyword(NIL, arg);
else pin = checkinteger(PINMODE, first(args));
int pm = INPUT;
arg = second(args);
if (keywordp(arg)) pm = checkkeyword(PINMODE, arg);
else if (integerp(arg)) {
int mode = arg->integer;
if (mode == 1) pm = OUTPUT; else if (mode == 2) pm = INPUT_PULLUP;
#if defined(INPUT_PULLDOWN)
else if (mode == 4) pm = INPUT_PULLDOWN;
#endif
} else if (arg != nil) pm = OUTPUT;
pinMode(pin, pm);
return nil;
}
object *fn_digitalread (object *args, object *env) {
(void) env;
int pin = checkinteger(DIGITALREAD, first(args));
if (digitalRead(pin) != 0) return tee; else return nil;
}
object *fn_digitalwrite (object *args, object *env) {
(void) env;
int pin;
object *arg = first(args);
if (keywordp(arg)) pin = checkkeyword(NIL, arg);
else pin = checkinteger(DIGITALWRITE, arg);
arg = second(args);
int mode;
if (keywordp(arg)) mode = checkkeyword(DIGITALWRITE, arg);
else if (integerp(arg)) mode = arg->integer ? HIGH : LOW;
else mode = (arg != nil) ? HIGH : LOW;
digitalWrite(pin, mode);
return arg;
}
object *fn_analogread (object *args, object *env) {
(void) env;
int pin;
object *arg = first(args);
if (keywordp(arg)) pin = checkkeyword(ANALOGREAD, arg);
else {
pin = checkinteger(ANALOGREAD, arg);
checkanalogread(pin);
}
return number(analogRead(pin));
}
object *fn_analogreference (object *args, object *env) {
(void) env;
object *arg = first(args);
#if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(MAX32620) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040)
error2(ANALOGREFERENCE, PSTR("not supported"));
#else
analogReference((eAnalogReference)checkkeyword(ANALOGREFERENCE, arg));
#endif
return arg;
}
object *fn_analogreadresolution (object *args, object *env) {
(void) env;
object *arg = first(args);
#if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040)
error2(ANALOGREADRESOLUTION, PSTR("not supported"));
#else
analogReadResolution(checkinteger(ANALOGREADRESOLUTION, arg));
#endif
return arg;
}
object *fn_analogwrite (object *args, object *env) {
(void) env;
int pin;
object *arg = first(args);
if (keywordp(arg)) pin = checkkeyword(NIL, arg);
else pin = checkinteger(ANALOGWRITE, arg);
checkanalogwrite(pin);
object *value = second(args);
analogWrite(pin, checkinteger(ANALOGWRITE, value));
return value;
}
object *fn_analogwriteresolution (object *args, object *env) {
(void) env;
object *arg = first(args);
analogWriteResolution(checkinteger(ANALOGWRITERESOLUTION, arg));
return arg;
}
object *fn_delay (object *args, object *env) {
(void) env;
object *arg1 = first(args);
delay(checkinteger(DELAY, arg1));
return arg1;
}
object *fn_millis (object *args, object *env) {
(void) args, (void) env;
return number(millis());
}
object *fn_sleep (object *args, object *env) {
(void) env;
object *arg1 = first(args);
sleep(checkinteger(SLEEP, arg1));
return arg1;
}
object *fn_note (object *args, object *env) {
(void) env;
static int pin = 255;
if (args != NULL) {
pin = checkinteger(NOTE, first(args));
int note = 0;
if (cddr(args) != NULL) note = checkinteger(NOTE, second(args));
int octave = 0;
if (cddr(args) != NULL) octave = checkinteger(NOTE, third(args));
playnote(pin, note, octave);
} else nonote(pin);
return nil;
}
object *fn_register (object *args, object *env) {
(void) env;
object *arg = first(args);
int addr;
if (keywordp(arg)) addr = checkkeyword(REGISTER, arg);
else addr = checkinteger(REGISTER, first(args));
if (cdr(args) == NULL) return number(*(uint32_t *)addr);
(*(uint32_t *)addr) = checkinteger(REGISTER, second(args));
return second(args);
}
// Tree Editor
object *fn_edit (object *args, object *env) {
object *fun = first(args);
object *pair = findvalue(fun, env);
clrflag(EXITEDITOR);
object *arg = edit(eval(fun, env));
cdr(pair) = arg;
return arg;
}
object *edit (object *fun) {
while (1) {
if (tstflag(EXITEDITOR)) return fun;
char c = gserial();
if (c == 'q') setflag(EXITEDITOR);
else if (c == 'b') return fun;
else if (c == 'r') fun = read(gserial);
else if (c == '\n') { pfl(pserial); superprint(fun, 0, pserial); pln(pserial); }
else if (c == 'c') fun = cons(read(gserial), fun);
else if (atom(fun)) pserial('!');
else if (c == 'd') fun = cons(car(fun), edit(cdr(fun)));
else if (c == 'a') fun = cons(edit(car(fun)), cdr(fun));
else if (c == 'x') fun = cdr(fun);
else pserial('?');
}
}
// Pretty printer
object *fn_pprint (object *args, object *env) {
(void) env;
object *obj = first(args);
pfun_t pfun = pstreamfun(cdr(args));
#if defined(gfxsupport)
if (pfun == gfxwrite) ppwidth = GFXPPWIDTH;
#endif
pln(pfun);
superprint(obj, 0, pfun);
ppwidth = PPWIDTH;
return bsymbol(NOTHING);
}
object *fn_pprintall (object *args, object *env) {
(void) env;
pfun_t pfun = pstreamfun(args);
#if defined(gfxsupport)
if (pfun == gfxwrite) ppwidth = GFXPPWIDTH;
#endif
object *globals = GlobalEnv;
while (globals != NULL) {
object *pair = first(globals);
object *var = car(pair);
object *val = cdr(pair);
pln(pfun);
if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) {
superprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun);
} else if (consp(val) && car(val)->type == CODE) {
superprint(cons(bsymbol(DEFCODE), cons(var, cdr(val))), 0, pfun);
} else {
superprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun);
}
pln(pfun);
testescape();
globals = cdr(globals);
}
ppwidth = PPWIDTH;
return bsymbol(NOTHING);
}
// Format
void formaterr (object *formatstr, PGM_P string, uint8_t p) {
pln(pserial); indent(4, ' ', pserial); printstring(formatstr, pserial); pln(pserial);
indent(p+5, ' ', pserial); pserial('^');
error2(FORMAT, string);
pln(pserial);
GCStack = NULL;
longjmp(exception, 1);
}
object *fn_format (object *args, object *env) {
(void) env;
pfun_t pfun = pserial;
object *output = first(args);
object *obj;
if (output == nil) { obj = startstring(FORMAT); pfun = pstr; }
else if (output != tee) pfun = pstreamfun(args);
object *formatstr = checkstring(FORMAT, second(args));
object *save = NULL;
args = cddr(args);
int len = stringlength(formatstr);
uint8_t n = 0, width = 0, w, bra = 0;
char pad = ' ';
bool tilde = false, mute = false, comma, quote;
while (n < len) {
char ch = nthchar(formatstr, n);
char ch2 = ch & ~0x20; // force to upper case
if (tilde) {
if (ch == '}') {
if (save == NULL) formaterr(formatstr, PSTR("no matching ~{"), n);
if (args == NULL) { args = cdr(save); save = NULL; } else n = bra;
mute = false; tilde = false;
}
else if (!mute) {
if (comma && quote) { pad = ch; comma = false, quote = false; }
else if (ch == '\'') {
if (comma) quote = true;
else formaterr(formatstr, PSTR("quote not valid"), n);
}
else if (ch == '~') { pfun('~'); tilde = false; }
else if (ch >= '0' && ch <= '9') width = width*10 + ch - '0';
else if (ch == ',') comma = true;
else if (ch == '%') { pln(pfun); tilde = false; }
else if (ch == '&') { pfl(pfun); tilde = false; }
else if (ch == '^') {
if (save != NULL && args == NULL) mute = true;
tilde = false;
}
else if (ch == '{') {
if (save != NULL) formaterr(formatstr, PSTR("can't nest ~{"), n);
if (args == NULL) formaterr(formatstr, noargument, n);
if (!listp(first(args))) formaterr(formatstr, notalist, n);
save = args; args = first(args); bra = n; tilde = false;
if (args == NULL) mute = true;
}
else if (ch2 == 'A' || ch2 == 'S' || ch2 == 'D' || ch2 == 'G' || ch2 == 'X' || ch2 == 'B') {
if (args == NULL) formaterr(formatstr, noargument, n);
object *arg = first(args); args = cdr(args);
uint8_t aw = atomwidth(arg);
if (width < aw) w = 0; else w = width-aw;
tilde = false;
if (ch2 == 'A') { prin1object(arg, pfun); indent(w, pad, pfun); }
else if (ch2 == 'S') { printobject(arg, pfun); indent(w, pad, pfun); }
else if (ch2 == 'D' || ch2 == 'G') { indent(w, pad, pfun); prin1object(arg, pfun); }
else if (ch2 == 'X' || ch2 == 'B') {
if (integerp(arg)) {
uint8_t base = (ch2 == 'B') ? 2 : 16;
uint8_t hw = basewidth(arg, base); if (width < hw) w = 0; else w = width-hw;
indent(w, pad, pfun); pintbase(arg->integer, base, pfun);
} else {
indent(w, pad, pfun); prin1object(arg, pfun);
}
}
tilde = false;
} else formaterr(formatstr, PSTR("invalid directive"), n);
}
} else {
if (ch == '~') { tilde = true; pad = ' '; width = 0; comma = false; quote = false; }
else if (!mute) pfun(ch);
}
n++;
}
if (output == nil) return obj;
else return nil;
}
// LispLibrary
object *fn_require (object *args, object *env) {
object *arg = first(args);
object *globals = GlobalEnv;
if (!symbolp(arg)) error(REQUIRE, notasymbol, arg);
while (globals != NULL) {
object *pair = first(globals);
object *var = car(pair);
if (symbolp(var) && var == arg) return nil;
globals = cdr(globals);
}
GlobalStringIndex = 0;
object *line = read(glibrary);
while (line != NULL) {
// Is this the definition we want
symbol_t fname = first(line)->name;
if ((fname == sym(DEFUN) || fname == sym(DEFVAR)) && symbolp(second(line)) && second(line)->name == arg->name) {
eval(line, env);
return tee;
}
line = read(glibrary);
}
return nil;
}
object *fn_listlibrary (object *args, object *env) {
(void) args, (void) env;
GlobalStringIndex = 0;
object *line = read(glibrary);
while (line != NULL) {
builtin_t bname = builtin(first(line)->name);
if (bname == DEFUN || bname == DEFVAR) {
printsymbol(second(line), pserial); pserial(' ');
}
line = read(glibrary);
}
return bsymbol(NOTHING);
}
// Graphics functions
object *fn_drawpixel (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t colour = COLOR_WHITE;
if (cddr(args) != NULL) colour = checkinteger(DRAWPIXEL, third(args));
tft.drawPixel(checkinteger(DRAWPIXEL, first(args)), checkinteger(DRAWPIXEL, second(args)), colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_drawline (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[4], colour = COLOR_WHITE;
for (int i=0; i<4; i++) { params[i] = checkinteger(DRAWLINE, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(DRAWLINE, car(args));
tft.drawLine(params[0], params[1], params[2], params[3], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_drawrect (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[4], colour = COLOR_WHITE;
for (int i=0; i<4; i++) { params[i] = checkinteger(DRAWRECT, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(DRAWRECT, car(args));
tft.drawRect(params[0], params[1], params[2], params[3], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_fillrect (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[4], colour = COLOR_WHITE;
for (int i=0; i<4; i++) { params[i] = checkinteger(FILLRECT, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(FILLRECT, car(args));
tft.fillRect(params[0], params[1], params[2], params[3], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_drawcircle (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[3], colour = COLOR_WHITE;
for (int i=0; i<3; i++) { params[i] = checkinteger(DRAWCIRCLE, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(DRAWCIRCLE, car(args));
tft.drawCircle(params[0], params[1], params[2], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_fillcircle (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[3], colour = COLOR_WHITE;
for (int i=0; i<3; i++) { params[i] = checkinteger(FILLCIRCLE, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(FILLCIRCLE, car(args));
tft.fillCircle(params[0], params[1], params[2], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_drawroundrect (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[5], colour = COLOR_WHITE;
for (int i=0; i<5; i++) { params[i] = checkinteger(DRAWROUNDRECT, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(DRAWROUNDRECT, car(args));
tft.drawRoundRect(params[0], params[1], params[2], params[3], params[4], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_fillroundrect (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[5], colour = COLOR_WHITE;
for (int i=0; i<5; i++) { params[i] = checkinteger(FILLROUNDRECT, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(FILLROUNDRECT, car(args));
tft.fillRoundRect(params[0], params[1], params[2], params[3], params[4], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_drawtriangle (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[6], colour = COLOR_WHITE;
for (int i=0; i<6; i++) { params[i] = checkinteger(DRAWTRIANGLE, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(DRAWTRIANGLE, car(args));
tft.drawTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_filltriangle (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t params[6], colour = COLOR_WHITE;
for (int i=0; i<6; i++) { params[i] = checkinteger(FILLTRIANGLE, car(args)); args = cdr(args); }
if (args != NULL) colour = checkinteger(FILLTRIANGLE, car(args));
tft.fillTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_drawchar (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t colour = COLOR_WHITE, bg = COLOR_BLACK, size = 1;
object *more = cdr(cddr(args));
if (more != NULL) {
colour = checkinteger(DRAWCHAR, car(more));
more = cdr(more);
if (more != NULL) {
bg = checkinteger(DRAWCHAR, car(more));
more = cdr(more);
if (more != NULL) size = checkinteger(DRAWCHAR, car(more));
}
}
tft.drawChar(checkinteger(DRAWCHAR, first(args)), checkinteger(DRAWCHAR, second(args)), checkchar(DRAWCHAR, third(args)),
colour, bg, size);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_setcursor (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
tft.setCursor(checkinteger(SETCURSOR, first(args)), checkinteger(SETCURSOR, second(args)));
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_settextcolor (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
if (cdr(args) != NULL) tft.setTextColor(checkinteger(SETTEXTCOLOR, first(args)), checkinteger(SETTEXTCOLOR, second(args)));
else tft.setTextColor(checkinteger(SETTEXTCOLOR, first(args)));
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_settextsize (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
tft.setTextSize(checkinteger(SETTEXTSIZE, first(args)));
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_settextwrap (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
tft.setTextWrap(first(args) != NULL);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_fillscreen (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
uint16_t colour = COLOR_BLACK;
if (args != NULL) colour = checkinteger(FILLSCREEN, first(args));
tft.fillScreen(colour);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_setrotation (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
tft.setRotation(checkinteger(SETROTATION, first(args)));
tft.refresh();
#else
(void) args;
#endif
return nil;
}
object *fn_invertdisplay (object *args, object *env) {
(void) env;
#if defined(gfxsupport)
tft.invertDisplay(first(args) != NULL);
tft.refresh();
#else
(void) args;
#endif
return nil;
}
// Insert your own function definitions here
// Built-in symbol names
const char string0[] PROGMEM = "nil";
const char string1[] PROGMEM = "t";
const char string2[] PROGMEM = "nothing";
const char string3[] PROGMEM = "&optional";
const char string4[] PROGMEM = ":initial-element";
const char string5[] PROGMEM = ":element-type";
const char string6[] PROGMEM = "bit";
const char string7[] PROGMEM = "&rest";
const char string8[] PROGMEM = "lambda";
const char string9[] PROGMEM = "let";
const char string10[] PROGMEM = "let*";
const char string11[] PROGMEM = "closure";
const char string12[] PROGMEM = "*pc*";
const char string13[] PROGMEM = "";
const char string14[] PROGMEM = "quote";
const char string15[] PROGMEM = "or";
const char string16[] PROGMEM = "defun";
const char string17[] PROGMEM = "defvar";
const char string18[] PROGMEM = "setq";
const char string19[] PROGMEM = "loop";
const char string20[] PROGMEM = "return";
const char string21[] PROGMEM = "push";
const char string22[] PROGMEM = "pop";
const char string23[] PROGMEM = "incf";
const char string24[] PROGMEM = "decf";
const char string25[] PROGMEM = "setf";
const char string26[] PROGMEM = "dolist";
const char string27[] PROGMEM = "dotimes";
const char string28[] PROGMEM = "trace";
const char string29[] PROGMEM = "untrace";
const char string30[] PROGMEM = "for-millis";
const char string31[] PROGMEM = "time";
const char string32[] PROGMEM = "with-output-to-string";
const char string33[] PROGMEM = "with-serial";
const char string34[] PROGMEM = "with-i2c";
const char string35[] PROGMEM = "with-spi";
const char string36[] PROGMEM = "with-sd-card";
const char string37[] PROGMEM = "with-gfx";
const char string38[] PROGMEM = "defcode";
const char string39[] PROGMEM = "";
const char string40[] PROGMEM = "progn";
const char string41[] PROGMEM = "if";
const char string42[] PROGMEM = "cond";
const char string43[] PROGMEM = "when";
const char string44[] PROGMEM = "unless";
const char string45[] PROGMEM = "case";
const char string46[] PROGMEM = "and";
const char string47[] PROGMEM = "";
const char string48[] PROGMEM = "not";
const char string49[] PROGMEM = "null";
const char string50[] PROGMEM = "cons";
const char string51[] PROGMEM = "atom";
const char string52[] PROGMEM = "listp";
const char string53[] PROGMEM = "consp";
const char string54[] PROGMEM = "symbolp";
const char string55[] PROGMEM = "arrayp";
const char string56[] PROGMEM = "boundp";
const char string57[] PROGMEM = "set";
const char string58[] PROGMEM = "streamp";
const char string59[] PROGMEM = "eq";
const char string60[] PROGMEM = "car";
const char string61[] PROGMEM = "first";
const char string62[] PROGMEM = "cdr";
const char string63[] PROGMEM = "rest";
const char string64[] PROGMEM = "caar";
const char string65[] PROGMEM = "cadr";
const char string66[] PROGMEM = "second";
const char string67[] PROGMEM = "cdar";
const char string68[] PROGMEM = "cddr";
const char string69[] PROGMEM = "caaar";
const char string70[] PROGMEM = "caadr";
const char string71[] PROGMEM = "cadar";
const char string72[] PROGMEM = "caddr";
const char string73[] PROGMEM = "third";
const char string74[] PROGMEM = "cdaar";
const char string75[] PROGMEM = "cdadr";
const char string76[] PROGMEM = "cddar";
const char string77[] PROGMEM = "cdddr";
const char string78[] PROGMEM = "length";
const char string79[] PROGMEM = "array-dimensions";
const char string80[] PROGMEM = "list";
const char string81[] PROGMEM = "make-array";
const char string82[] PROGMEM = "reverse";
const char string83[] PROGMEM = "nth";
const char string84[] PROGMEM = "aref";
const char string85[] PROGMEM = "assoc";
const char string86[] PROGMEM = "member";
const char string87[] PROGMEM = "apply";
const char string88[] PROGMEM = "funcall";
const char string89[] PROGMEM = "append";
const char string90[] PROGMEM = "mapc";
const char string91[] PROGMEM = "mapcar";
const char string92[] PROGMEM = "mapcan";
const char string93[] PROGMEM = "+";
const char string94[] PROGMEM = "-";
const char string95[] PROGMEM = "*";
const char string96[] PROGMEM = "/";
const char string97[] PROGMEM = "mod";
const char string98[] PROGMEM = "1+";
const char string99[] PROGMEM = "1-";
const char string100[] PROGMEM = "abs";
const char string101[] PROGMEM = "random";
const char string102[] PROGMEM = "max";
const char string103[] PROGMEM = "min";
const char string104[] PROGMEM = "/=";
const char string105[] PROGMEM = "=";
const char string106[] PROGMEM = "<";
const char string107[] PROGMEM = "<=";
const char string108[] PROGMEM = ">";
const char string109[] PROGMEM = ">=";
const char string110[] PROGMEM = "plusp";
const char string111[] PROGMEM = "minusp";
const char string112[] PROGMEM = "zerop";
const char string113[] PROGMEM = "oddp";
const char string114[] PROGMEM = "evenp";
const char string115[] PROGMEM = "integerp";
const char string116[] PROGMEM = "numberp";
const char string117[] PROGMEM = "float";
const char string118[] PROGMEM = "floatp";
const char string119[] PROGMEM = "sin";
const char string120[] PROGMEM = "cos";
const char string121[] PROGMEM = "tan";
const char string122[] PROGMEM = "asin";
const char string123[] PROGMEM = "acos";
const char string124[] PROGMEM = "atan";
const char string125[] PROGMEM = "sinh";
const char string126[] PROGMEM = "cosh";
const char string127[] PROGMEM = "tanh";
const char string128[] PROGMEM = "exp";
const char string129[] PROGMEM = "sqrt";
const char string130[] PROGMEM = "log";
const char string131[] PROGMEM = "expt";
const char string132[] PROGMEM = "ceiling";
const char string133[] PROGMEM = "floor";
const char string134[] PROGMEM = "truncate";
const char string135[] PROGMEM = "round";
const char string136[] PROGMEM = "char";
const char string137[] PROGMEM = "char-code";
const char string138[] PROGMEM = "code-char";
const char string139[] PROGMEM = "characterp";
const char string140[] PROGMEM = "stringp";
const char string141[] PROGMEM = "string=";
const char string142[] PROGMEM = "string<";
const char string143[] PROGMEM = "string>";
const char string144[] PROGMEM = "sort";
const char string145[] PROGMEM = "string";
const char string146[] PROGMEM = "concatenate";
const char string147[] PROGMEM = "subseq";
const char string148[] PROGMEM = "read-from-string";
const char string149[] PROGMEM = "princ-to-string";
const char string150[] PROGMEM = "prin1-to-string";
const char string151[] PROGMEM = "logand";
const char string152[] PROGMEM = "logior";
const char string153[] PROGMEM = "logxor";
const char string154[] PROGMEM = "lognot";
const char string155[] PROGMEM = "ash";
const char string156[] PROGMEM = "logbitp";
const char string157[] PROGMEM = "eval";
const char string158[] PROGMEM = "globals";
const char string159[] PROGMEM = "locals";
const char string160[] PROGMEM = "makunbound";
const char string161[] PROGMEM = "break";
const char string162[] PROGMEM = "read";
const char string163[] PROGMEM = "prin1";
const char string164[] PROGMEM = "print";
const char string165[] PROGMEM = "princ";
const char string166[] PROGMEM = "terpri";
const char string167[] PROGMEM = "read-byte";
const char string168[] PROGMEM = "read-line";
const char string169[] PROGMEM = "write-byte";
const char string170[] PROGMEM = "write-string";
const char string171[] PROGMEM = "write-line";
const char string172[] PROGMEM = "restart-i2c";
const char string173[] PROGMEM = "gc";
const char string174[] PROGMEM = "room";
const char string175[] PROGMEM = "save-image";
const char string176[] PROGMEM = "load-image";
const char string177[] PROGMEM = "cls";
const char string178[] PROGMEM = "pinmode";
const char string179[] PROGMEM = "digitalread";
const char string180[] PROGMEM = "digitalwrite";
const char string181[] PROGMEM = "analogread";
const char string182[] PROGMEM = "analogreference";
const char string183[] PROGMEM = "analogreadresolution";
const char string184[] PROGMEM = "analogwrite";
const char string185[] PROGMEM = "analogwriteresolution";
const char string186[] PROGMEM = "delay";
const char string187[] PROGMEM = "millis";
const char string188[] PROGMEM = "sleep";
const char string189[] PROGMEM = "note";
const char string190[] PROGMEM = "register";
const char string191[] PROGMEM = "edit";
const char string192[] PROGMEM = "pprint";
const char string193[] PROGMEM = "pprintall";
const char string194[] PROGMEM = "format";
const char string195[] PROGMEM = "require";
const char string196[] PROGMEM = "list-library";
const char string197[] PROGMEM = "draw-pixel";
const char string198[] PROGMEM = "draw-line";
const char string199[] PROGMEM = "draw-rect";
const char string200[] PROGMEM = "fill-rect";
const char string201[] PROGMEM = "draw-circle";
const char string202[] PROGMEM = "fill-circle";
const char string203[] PROGMEM = "draw-round-rect";
const char string204[] PROGMEM = "fill-round-rect";
const char string205[] PROGMEM = "draw-triangle";
const char string206[] PROGMEM = "fill-triangle";
const char string207[] PROGMEM = "draw-char";
const char string208[] PROGMEM = "set-cursor";
const char string209[] PROGMEM = "set-text-color";
const char string210[] PROGMEM = "set-text-size";
const char string211[] PROGMEM = "set-text-wrap";
const char string212[] PROGMEM = "fill-screen";
const char string213[] PROGMEM = "set-rotation";
const char string214[] PROGMEM = "invert-display";
const char string215[] PROGMEM = "";
const char string216[] PROGMEM = ":led-builtin";
const char string217[] PROGMEM = ":high";
const char string218[] PROGMEM = ":low";
#if defined(CPU_ATSAMD21)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":input-pulldown";
const char string222[] PROGMEM = ":output";
const char string223[] PROGMEM = ":ar-default";
const char string224[] PROGMEM = ":ar-internal1v0";
const char string225[] PROGMEM = ":ar-internal1v65";
const char string226[] PROGMEM = ":ar-internal2v23";
const char string227[] PROGMEM = ":ar-external";
const char string228[] PROGMEM = ":pa-dir";
const char string229[] PROGMEM = ":pa-dirclr";
const char string230[] PROGMEM = ":pa-dirset";
const char string231[] PROGMEM = ":pa-dirtgl";
const char string232[] PROGMEM = ":pa-out";
const char string233[] PROGMEM = ":pa-outclr";
const char string234[] PROGMEM = ":pa-outset";
const char string235[] PROGMEM = ":pa-outtgl";
const char string236[] PROGMEM = ":pa-in";
const char string237[] PROGMEM = ":pb-dir";
const char string238[] PROGMEM = ":pb-dirclr";
const char string239[] PROGMEM = ":pb-dirset";
const char string240[] PROGMEM = ":pb-dirtgl";
const char string241[] PROGMEM = ":pb-out";
const char string242[] PROGMEM = ":pb-outclr";
const char string243[] PROGMEM = ":pb-outset";
const char string244[] PROGMEM = ":pb-outtgl";
const char string245[] PROGMEM = ":pb-in";
const char string246[] PROGMEM = "";
#elif defined(CPU_ATSAMD51)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":input-pulldown";
const char string222[] PROGMEM = ":output";
const char string223[] PROGMEM = ":ar-default";
const char string224[] PROGMEM = ":ar-internal1v0";
const char string225[] PROGMEM = ":ar-internal1v1";
const char string226[] PROGMEM = ":ar-internal1v2";
const char string227[] PROGMEM = ":ar-internal1v25";
const char string228[] PROGMEM = ":ar-internal1v65";
const char string229[] PROGMEM = ":ar-internal2v0";
const char string230[] PROGMEM = ":ar-internal2v2";
const char string231[] PROGMEM = ":ar-internal2v23";
const char string232[] PROGMEM = ":ar-internal2v4";
const char string233[] PROGMEM = ":ar-internal2v5";
const char string234[] PROGMEM = ":ar-external";
const char string235[] PROGMEM = ":pa-dir";
const char string236[] PROGMEM = ":pa-dirclr";
const char string237[] PROGMEM = ":pa-dirset";
const char string238[] PROGMEM = ":pa-dirtgl";
const char string239[] PROGMEM = ":pa-out";
const char string240[] PROGMEM = ":pa-outclr";
const char string241[] PROGMEM = ":pa-outset";
const char string242[] PROGMEM = ":pa-outtgl";
const char string243[] PROGMEM = ":pa-in";
const char string244[] PROGMEM = ":pb-dir";
const char string245[] PROGMEM = ":pb-dirclr";
const char string246[] PROGMEM = ":pb-dirset";
const char string247[] PROGMEM = ":pb-dirtgl";
const char string248[] PROGMEM = ":pb-out";
const char string249[] PROGMEM = ":pb-outclr";
const char string250[] PROGMEM = ":pb-outset";
const char string251[] PROGMEM = ":pb-outtgl";
const char string252[] PROGMEM = ":pb-in";
const char string253[] PROGMEM = "";
#elif defined(CPU_NRF51822)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":input-pulldown";
const char string222[] PROGMEM = ":output";
const char string223[] PROGMEM = ":ar-default";
const char string224[] PROGMEM = ":ar-vbg";
const char string225[] PROGMEM = ":ar-supply-one-half";
const char string226[] PROGMEM = ":ar-supply-one-third";
const char string227[] PROGMEM = ":ar-ext0";
const char string228[] PROGMEM = ":ar-ext1";
const char string229[] PROGMEM = ":p0-out";
const char string230[] PROGMEM = ":p0-outset";
const char string231[] PROGMEM = ":p0-outclr";
const char string232[] PROGMEM = ":p0-in";
const char string233[] PROGMEM = ":p0-dir";
const char string234[] PROGMEM = ":p0-dirset";
const char string235[] PROGMEM = ":p0-dirclr";
const char string236[] PROGMEM = "";
#elif defined(CPU_NRF52840)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":input-pulldown";
const char string222[] PROGMEM = ":output";
const char string223[] PROGMEM = ":ar-default";
const char string224[] PROGMEM = ":ar-internal";
const char string225[] PROGMEM = ":ar-internal-3-0";
const char string226[] PROGMEM = ":ar-internal-2-4";
const char string227[] PROGMEM = ":ar-internal-1-8";
const char string228[] PROGMEM = ":ar-internal-1-2";
const char string229[] PROGMEM = ":ar-vdd4";
const char string230[] PROGMEM = ":p0-out";
const char string231[] PROGMEM = ":p0-outset";
const char string232[] PROGMEM = ":p0-outclr";
const char string233[] PROGMEM = ":p0-in";
const char string234[] PROGMEM = ":p0-dir";
const char string235[] PROGMEM = ":p0-dirset";
const char string236[] PROGMEM = ":p0-dirclr";
const char string237[] PROGMEM = ":p1-out";
const char string238[] PROGMEM = ":p1-outset";
const char string239[] PROGMEM = ":p1-outclr";
const char string240[] PROGMEM = ":p1-in";
const char string241[] PROGMEM = ":p1-dir";
const char string242[] PROGMEM = ":p1-dirset";
const char string243[] PROGMEM = ":p1-dirclr";
const char string244[] PROGMEM = "";
#elif defined(CPU_NRF52833)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":input-pulldown";
const char string222[] PROGMEM = ":output";
const char string223[] PROGMEM = ":ar-default";
const char string224[] PROGMEM = ":ar-internal";
const char string225[] PROGMEM = ":ar-vdd4";
const char string226[] PROGMEM = ":p0-out";
const char string227[] PROGMEM = ":p0-outset";
const char string228[] PROGMEM = ":p0-outclr";
const char string229[] PROGMEM = ":p0-in";
const char string230[] PROGMEM = ":p0-dir";
const char string231[] PROGMEM = ":p0-dirset";
const char string232[] PROGMEM = ":p0-dirclr";
const char string233[] PROGMEM = ":p1-out";
const char string234[] PROGMEM = ":p1-outset";
const char string235[] PROGMEM = ":p1-outclr";
const char string236[] PROGMEM = ":p1-in";
const char string237[] PROGMEM = ":p1-dir";
const char string238[] PROGMEM = ":p1-dirset";
const char string239[] PROGMEM = ":p1-dirclr";
const char string240[] PROGMEM = "";
#elif defined(CPU_iMXRT1062)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":input-pulldown";
const char string222[] PROGMEM = ":output";
const char string223[] PROGMEM = ":output-opendrain";
const char string224[] PROGMEM = "";
#elif defined(CPU_MAX32620)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":output";
const char string222[] PROGMEM = ":default";
const char string223[] PROGMEM = ":external";
const char string224[] PROGMEM = "";
#elif defined(CPU_RP2040)
const char string219[] PROGMEM = ":input";
const char string220[] PROGMEM = ":input-pullup";
const char string221[] PROGMEM = ":input-pulldown";
const char string222[] PROGMEM = ":output";
const char string223[] PROGMEM = ":gpio-in";
const char string224[] PROGMEM = ":gpio-out";
const char string225[] PROGMEM = ":gpio-out-set";
const char string226[] PROGMEM = ":gpio-out-clr";
const char string227[] PROGMEM = ":gpio-out-xor";
const char string228[] PROGMEM = ":gpio-oe";
const char string229[] PROGMEM = ":gpio-oe-set";
const char string230[] PROGMEM = ":gpio-oe-clr";
const char string231[] PROGMEM = ":gpio-oe-xor";
const char string232[] PROGMEM = "";
#endif
// Insert your own function names here
// Built-in symbol lookup table
const tbl_entry_t lookup_table[] PROGMEM = {
{ string0, NULL, 0x00 },
{ string1, NULL, 0x00 },
{ string2, NULL, 0x00 },
{ string3, NULL, 0x00 },
{ string4, NULL, 0x00 },
{ string5, NULL, 0x00 },
{ string6, NULL, 0x00 },
{ string7, NULL, 0x00 },
{ string8, NULL, 0x0F },
{ string9, NULL, 0x0F },
{ string10, NULL, 0x0F },
{ string11, NULL, 0x0F },
{ string12, NULL, 0x0F },
{ string13, NULL, 0x00 },
{ string14, sp_quote, 0x11 },
{ string15, sp_or, 0x0F },
{ string16, sp_defun, 0x2F },
{ string17, sp_defvar, 0x13 },
{ string18, sp_setq, 0x2F },
{ string19, sp_loop, 0x0F },
{ string20, sp_return, 0x0F },
{ string21, sp_push, 0x22 },
{ string22, sp_pop, 0x11 },
{ string23, sp_incf, 0x12 },
{ string24, sp_decf, 0x12 },
{ string25, sp_setf, 0x2F },
{ string26, sp_dolist, 0x1F },
{ string27, sp_dotimes, 0x1F },
{ string28, sp_trace, 0x01 },
{ string29, sp_untrace, 0x01 },
{ string30, sp_formillis, 0x1F },
{ string31, sp_time, 0x11 },
{ string32, sp_withoutputtostring, 0x1F },
{ string33, sp_withserial, 0x1F },
{ string34, sp_withi2c, 0x1F },
{ string35, sp_withspi, 0x1F },
{ string36, sp_withsdcard, 0x2F },
{ string37, sp_withgfx, 0x1F },
{ string38, sp_defcode, 0x0F },
{ string39, NULL, 0x00 },
{ string40, tf_progn, 0x0F },
{ string41, tf_if, 0x23 },
{ string42, tf_cond, 0x0F },
{ string43, tf_when, 0x1F },
{ string44, tf_unless, 0x1F },
{ string45, tf_case, 0x1F },
{ string46, tf_and, 0x0F },
{ string47, NULL, 0x00 },
{ string48, fn_not, 0x11 },
{ string49, fn_not, 0x11 },
{ string50, fn_cons, 0x22 },
{ string51, fn_atom, 0x11 },
{ string52, fn_listp, 0x11 },
{ string53, fn_consp, 0x11 },
{ string54, fn_symbolp, 0x11 },
{ string55, fn_arrayp, 0x11 },
{ string56, fn_boundp, 0x11 },
{ string57, fn_setfn, 0x2F },
{ string58, fn_streamp, 0x11 },
{ string59, fn_eq, 0x22 },
{ string60, fn_car, 0x11 },
{ string61, fn_car, 0x11 },
{ string62, fn_cdr, 0x11 },
{ string63, fn_cdr, 0x11 },
{ string64, fn_caar, 0x11 },
{ string65, fn_cadr, 0x11 },
{ string66, fn_cadr, 0x11 },
{ string67, fn_cdar, 0x11 },
{ string68, fn_cddr, 0x11 },
{ string69, fn_caaar, 0x11 },
{ string70, fn_caadr, 0x11 },
{ string71, fn_cadar, 0x11 },
{ string72, fn_caddr, 0x11 },
{ string73, fn_caddr, 0x11 },
{ string74, fn_cdaar, 0x11 },
{ string75, fn_cdadr, 0x11 },
{ string76, fn_cddar, 0x11 },
{ string77, fn_cdddr, 0x11 },
{ string78, fn_length, 0x11 },
{ string79, fn_arraydimensions, 0x11 },
{ string80, fn_list, 0x0F },
{ string81, fn_makearray, 0x15 },
{ string82, fn_reverse, 0x11 },
{ string83, fn_nth, 0x22 },
{ string84, fn_areff, 0x2F },
{ string85, fn_assoc, 0x22 },
{ string86, fn_member, 0x22 },
{ string87, fn_apply, 0x2F },
{ string88, fn_funcall, 0x1F },
{ string89, fn_append, 0x0F },
{ string90, fn_mapc, 0x2F },
{ string91, fn_mapcar, 0x2F },
{ string92, fn_mapcan, 0x2F },
{ string93, fn_add, 0x0F },
{ string94, fn_subtract, 0x1F },
{ string95, fn_multiply, 0x0F },
{ string96, fn_divide, 0x1F },
{ string97, fn_mod, 0x22 },
{ string98, fn_oneplus, 0x11 },
{ string99, fn_oneminus, 0x11 },
{ string100, fn_abs, 0x11 },
{ string101, fn_random, 0x11 },
{ string102, fn_maxfn, 0x1F },
{ string103, fn_minfn, 0x1F },
{ string104, fn_noteq, 0x1F },
{ string105, fn_numeq, 0x1F },
{ string106, fn_less, 0x1F },
{ string107, fn_lesseq, 0x1F },
{ string108, fn_greater, 0x1F },
{ string109, fn_greatereq, 0x1F },
{ string110, fn_plusp, 0x11 },
{ string111, fn_minusp, 0x11 },
{ string112, fn_zerop, 0x11 },
{ string113, fn_oddp, 0x11 },
{ string114, fn_evenp, 0x11 },
{ string115, fn_integerp, 0x11 },
{ string116, fn_numberp, 0x11 },
{ string117, fn_floatfn, 0x11 },
{ string118, fn_floatp, 0x11 },
{ string119, fn_sin, 0x11 },
{ string120, fn_cos, 0x11 },
{ string121, fn_tan, 0x11 },
{ string122, fn_asin, 0x11 },
{ string123, fn_acos, 0x11 },
{ string124, fn_atan, 0x12 },
{ string125, fn_sinh, 0x11 },
{ string126, fn_cosh, 0x11 },
{ string127, fn_tanh, 0x11 },
{ string128, fn_exp, 0x11 },
{ string129, fn_sqrt, 0x11 },
{ string130, fn_log, 0x12 },
{ string131, fn_expt, 0x22 },
{ string132, fn_ceiling, 0x12 },
{ string133, fn_floor, 0x12 },
{ string134, fn_truncate, 0x12 },
{ string135, fn_round, 0x12 },
{ string136, fn_char, 0x22 },
{ string137, fn_charcode, 0x11 },
{ string138, fn_codechar, 0x11 },
{ string139, fn_characterp, 0x11 },
{ string140, fn_stringp, 0x11 },
{ string141, fn_stringeq, 0x22 },
{ string142, fn_stringless, 0x22 },
{ string143, fn_stringgreater, 0x22 },
{ string144, fn_sort, 0x22 },
{ string145, fn_stringfn, 0x11 },
{ string146, fn_concatenate, 0x1F },
{ string147, fn_subseq, 0x23 },
{ string148, fn_readfromstring, 0x11 },
{ string149, fn_princtostring, 0x11 },
{ string150, fn_prin1tostring, 0x11 },
{ string151, fn_logand, 0x0F },
{ string152, fn_logior, 0x0F },
{ string153, fn_logxor, 0x0F },
{ string154, fn_lognot, 0x11 },
{ string155, fn_ash, 0x22 },
{ string156, fn_logbitp, 0x22 },
{ string157, fn_eval, 0x11 },
{ string158, fn_globals, 0x00 },
{ string159, fn_locals, 0x00 },
{ string160, fn_makunbound, 0x11 },
{ string161, fn_break, 0x00 },
{ string162, fn_read, 0x01 },
{ string163, fn_prin1, 0x12 },
{ string164, fn_print, 0x12 },
{ string165, fn_princ, 0x12 },
{ string166, fn_terpri, 0x01 },
{ string167, fn_readbyte, 0x02 },
{ string168, fn_readline, 0x01 },
{ string169, fn_writebyte, 0x12 },
{ string170, fn_writestring, 0x12 },
{ string171, fn_writeline, 0x12 },
{ string172, fn_restarti2c, 0x12 },
{ string173, fn_gc, 0x00 },
{ string174, fn_room, 0x00 },
{ string175, fn_saveimage, 0x01 },
{ string176, fn_loadimage, 0x01 },
{ string177, fn_cls, 0x00 },
{ string178, fn_pinmode, 0x22 },
{ string179, fn_digitalread, 0x11 },
{ string180, fn_digitalwrite, 0x22 },
{ string181, fn_analogread, 0x11 },
{ string182, fn_analogreference, 0x11 },
{ string183, fn_analogreadresolution, 0x11 },
{ string184, fn_analogwrite, 0x22 },
{ string185, fn_analogwriteresolution, 0x11 },
{ string186, fn_delay, 0x11 },
{ string187, fn_millis, 0x00 },
{ string188, fn_sleep, 0x11 },
{ string189, fn_note, 0x03 },
{ string190, fn_register, 0x12 },
{ string191, fn_edit, 0x11 },
{ string192, fn_pprint, 0x12 },
{ string193, fn_pprintall, 0x01 },
{ string194, fn_format, 0x2F },
{ string195, fn_require, 0x11 },
{ string196, fn_listlibrary, 0x00 },
{ string197, fn_drawpixel, 0x23 },
{ string198, fn_drawline, 0x45 },
{ string199, fn_drawrect, 0x45 },
{ string200, fn_fillrect, 0x45 },
{ string201, fn_drawcircle, 0x34 },
{ string202, fn_fillcircle, 0x34 },
{ string203, fn_drawroundrect, 0x56 },
{ string204, fn_fillroundrect, 0x56 },
{ string205, fn_drawtriangle, 0x67 },
{ string206, fn_filltriangle, 0x67 },
{ string207, fn_drawchar, 0x36 },
{ string208, fn_setcursor, 0x22 },
{ string209, fn_settextcolor, 0x12 },
{ string210, fn_settextsize, 0x11 },
{ string211, fn_settextwrap, 0x11 },
{ string212, fn_fillscreen, 0x01 },
{ string213, fn_setrotation, 0x11 },
{ string214, fn_invertdisplay, 0x11 },
{ string215, NULL, 0x00 },
{ string216, (fn_ptr_type)LED_BUILTIN, 0 },
{ string217, (fn_ptr_type)HIGH, DIGITALWRITE },
{ string218, (fn_ptr_type)LOW, DIGITALWRITE },
#if defined(CPU_ATSAMD21)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)INPUT_PULLDOWN, PINMODE },
{ string222, (fn_ptr_type)OUTPUT, PINMODE },
{ string223, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE },
{ string224, (fn_ptr_type)AR_INTERNAL1V0, ANALOGREFERENCE },
{ string225, (fn_ptr_type)AR_INTERNAL1V65, ANALOGREFERENCE },
{ string226, (fn_ptr_type)AR_INTERNAL2V23, ANALOGREFERENCE },
{ string227, (fn_ptr_type)AR_EXTERNAL, ANALOGREFERENCE },
{ string228, (fn_ptr_type)&PORT->Group[0].DIR.reg, REGISTER },
{ string229, (fn_ptr_type)&PORT->Group[0].DIRCLR.reg, REGISTER },
{ string230, (fn_ptr_type)&PORT->Group[0].DIRSET.reg, REGISTER },
{ string231, (fn_ptr_type)&PORT->Group[0].DIRTGL.reg, REGISTER },
{ string232, (fn_ptr_type)&PORT->Group[0].OUT.reg, REGISTER },
{ string233, (fn_ptr_type)&PORT->Group[0].OUTCLR.reg, REGISTER },
{ string234, (fn_ptr_type)&PORT->Group[0].OUTSET.reg, REGISTER },
{ string235, (fn_ptr_type)&PORT->Group[0].OUTTGL.reg, REGISTER },
{ string236, (fn_ptr_type)&PORT->Group[0].IN.reg, REGISTER },
{ string237, (fn_ptr_type)&PORT->Group[1].DIR.reg, REGISTER },
{ string238, (fn_ptr_type)&PORT->Group[1].DIRCLR.reg, REGISTER },
{ string239, (fn_ptr_type)&PORT->Group[1].DIRSET.reg, REGISTER },
{ string240, (fn_ptr_type)&PORT->Group[1].DIRTGL.reg, REGISTER },
{ string241, (fn_ptr_type)&PORT->Group[1].OUT.reg, REGISTER },
{ string242, (fn_ptr_type)&PORT->Group[1].OUTCLR.reg, REGISTER },
{ string243, (fn_ptr_type)&PORT->Group[1].OUTSET.reg, REGISTER },
{ string244, (fn_ptr_type)&PORT->Group[1].OUTTGL.reg, REGISTER },
{ string245, (fn_ptr_type)&PORT->Group[1].IN.reg, REGISTER },
{ string246, NULL, 0x00 },
#elif defined(CPU_ATSAMD51)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)INPUT_PULLDOWN, PINMODE },
{ string222, (fn_ptr_type)OUTPUT, PINMODE },
{ string223, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE },
{ string224, (fn_ptr_type)AR_INTERNAL1V0, ANALOGREFERENCE },
{ string225, (fn_ptr_type)AR_INTERNAL1V1, ANALOGREFERENCE },
{ string226, (fn_ptr_type)AR_INTERNAL1V2, ANALOGREFERENCE },
{ string227, (fn_ptr_type)AR_INTERNAL1V25, ANALOGREFERENCE },
{ string228, (fn_ptr_type)AR_INTERNAL1V65, ANALOGREFERENCE },
{ string229, (fn_ptr_type)AR_INTERNAL2V0, ANALOGREFERENCE },
{ string230, (fn_ptr_type)AR_INTERNAL2V2, ANALOGREFERENCE },
{ string231, (fn_ptr_type)AR_INTERNAL2V23, ANALOGREFERENCE },
{ string232, (fn_ptr_type)AR_INTERNAL2V4, ANALOGREFERENCE },
{ string233, (fn_ptr_type)AR_INTERNAL2V5, ANALOGREFERENCE },
{ string234, (fn_ptr_type)AR_EXTERNAL, ANALOGREFERENCE },
{ string235, (fn_ptr_type)&PORT->Group[0].DIR.reg, REGISTER },
{ string236, (fn_ptr_type)&PORT->Group[0].DIRCLR.reg, REGISTER },
{ string237, (fn_ptr_type)&PORT->Group[0].DIRSET.reg, REGISTER },
{ string238, (fn_ptr_type)&PORT->Group[0].DIRTGL.reg, REGISTER },
{ string239, (fn_ptr_type)&PORT->Group[0].OUT.reg, REGISTER },
{ string240, (fn_ptr_type)&PORT->Group[0].OUTCLR.reg, REGISTER },
{ string241, (fn_ptr_type)&PORT->Group[0].OUTSET.reg, REGISTER },
{ string242, (fn_ptr_type)&PORT->Group[0].OUTTGL.reg, REGISTER },
{ string243, (fn_ptr_type)&PORT->Group[0].IN.reg, REGISTER },
{ string244, (fn_ptr_type)&PORT->Group[1].DIR.reg, REGISTER },
{ string245, (fn_ptr_type)&PORT->Group[1].DIRCLR.reg, REGISTER },
{ string246, (fn_ptr_type)&PORT->Group[1].DIRSET.reg, REGISTER },
{ string247, (fn_ptr_type)&PORT->Group[1].DIRTGL.reg, REGISTER },
{ string248, (fn_ptr_type)&PORT->Group[1].OUT.reg, REGISTER },
{ string249, (fn_ptr_type)&PORT->Group[1].OUTCLR.reg, REGISTER },
{ string250, (fn_ptr_type)&PORT->Group[1].OUTSET.reg, REGISTER },
{ string251, (fn_ptr_type)&PORT->Group[1].OUTTGL.reg, REGISTER },
{ string252, (fn_ptr_type)&PORT->Group[1].IN.reg, REGISTER },
{ string253, NULL, 0x00 },
#elif defined(CPU_NRF51822)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)INPUT_PULLDOWN, PINMODE },
{ string222, (fn_ptr_type)OUTPUT, PINMODE },
{ string223, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE },
{ string224, (fn_ptr_type)AR_VBG, ANALOGREFERENCE },
{ string225, (fn_ptr_type)AR_SUPPLY_ONE_HALF, ANALOGREFERENCE },
{ string226, (fn_ptr_type)AR_SUPPLY_ONE_THIRD, ANALOGREFERENCE },
{ string227, (fn_ptr_type)AR_EXT0, ANALOGREFERENCE },
{ string228, (fn_ptr_type)AR_EXT1, ANALOGREFERENCE },
{ string229, (fn_ptr_type)&NRF_GPIO->OUT, REGISTER },
{ string230, (fn_ptr_type)&NRF_GPIO->OUTSET, REGISTER },
{ string231, (fn_ptr_type)&NRF_GPIO->OUTCLR, REGISTER },
{ string232, (fn_ptr_type)&NRF_GPIO->IN, REGISTER },
{ string233, (fn_ptr_type)&NRF_GPIO->DIR, REGISTER },
{ string234, (fn_ptr_type)&NRF_GPIO->DIRSET, REGISTER },
{ string235, (fn_ptr_type)&NRF_GPIO->DIRCLR, REGISTER },
{ string236, NULL, 0x00 },
#elif defined(CPU_NRF52840)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)INPUT_PULLDOWN, PINMODE },
{ string222, (fn_ptr_type)OUTPUT, PINMODE },
{ string223, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE },
{ string224, (fn_ptr_type)AR_INTERNAL, ANALOGREFERENCE },
{ string225, (fn_ptr_type)AR_INTERNAL_3_0, ANALOGREFERENCE },
{ string226, (fn_ptr_type)AR_INTERNAL_2_4, ANALOGREFERENCE },
{ string227, (fn_ptr_type)AR_INTERNAL_1_8, ANALOGREFERENCE },
{ string228, (fn_ptr_type)AR_INTERNAL_1_2, ANALOGREFERENCE },
{ string229, (fn_ptr_type)AR_VDD4, ANALOGREFERENCE },
{ string230, (fn_ptr_type)&NRF_P0->OUT, REGISTER },
{ string231, (fn_ptr_type)&NRF_P0->OUTSET, REGISTER },
{ string232, (fn_ptr_type)&NRF_P0->OUTCLR, REGISTER },
{ string233, (fn_ptr_type)&NRF_P0->IN, REGISTER },
{ string234, (fn_ptr_type)&NRF_P0->DIR, REGISTER },
{ string235, (fn_ptr_type)&NRF_P0->DIRSET, REGISTER },
{ string236, (fn_ptr_type)&NRF_P0->DIRCLR, REGISTER },
{ string237, (fn_ptr_type)&NRF_P1->OUT, REGISTER },
{ string238, (fn_ptr_type)&NRF_P1->OUTSET, REGISTER },
{ string239, (fn_ptr_type)&NRF_P1->OUTCLR, REGISTER },
{ string240, (fn_ptr_type)&NRF_P1->IN, REGISTER },
{ string241, (fn_ptr_type)&NRF_P1->DIR, REGISTER },
{ string242, (fn_ptr_type)&NRF_P1->DIRSET, REGISTER },
{ string243, (fn_ptr_type)&NRF_P1->DIRCLR, REGISTER },
{ string244, NULL, 0x00 },
#elif defined(CPU_NRF52833)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)INPUT_PULLDOWN, PINMODE },
{ string222, (fn_ptr_type)OUTPUT, PINMODE },
{ string223, (fn_ptr_type)AR_DEFAULT, ANALOGREFERENCE },
{ string224, (fn_ptr_type)AR_INTERNAL, ANALOGREFERENCE },
{ string225, (fn_ptr_type)AR_VDD4, ANALOGREFERENCE },
{ string226, (fn_ptr_type)&NRF_P0->OUT, REGISTER },
{ string227, (fn_ptr_type)&NRF_P0->OUTSET, REGISTER },
{ string228, (fn_ptr_type)&NRF_P0->OUTCLR, REGISTER },
{ string229, (fn_ptr_type)&NRF_P0->IN, REGISTER },
{ string230, (fn_ptr_type)&NRF_P0->DIR, REGISTER },
{ string231, (fn_ptr_type)&NRF_P0->DIRSET, REGISTER },
{ string232, (fn_ptr_type)&NRF_P0->DIRCLR, REGISTER },
{ string233, (fn_ptr_type)&NRF_P1->OUT, REGISTER },
{ string234, (fn_ptr_type)&NRF_P1->OUTSET, REGISTER },
{ string235, (fn_ptr_type)&NRF_P1->OUTCLR, REGISTER },
{ string236, (fn_ptr_type)&NRF_P1->IN, REGISTER },
{ string237, (fn_ptr_type)&NRF_P1->DIR, REGISTER },
{ string238, (fn_ptr_type)&NRF_P1->DIRSET, REGISTER },
{ string239, (fn_ptr_type)&NRF_P1->DIRCLR, REGISTER },
{ string240, NULL, 0x00 },
#elif defined(CPU_iMXRT1062)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)INPUT_PULLDOWN, PINMODE },
{ string222, (fn_ptr_type)OUTPUT, PINMODE },
{ string223, (fn_ptr_type)OUTPUT_OPENDRAIN, PINMODE },
{ string224, NULL, 0x00 },
#elif defined(CPU_MAX32620)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)OUTPUT, PINMODE },
{ string222, (fn_ptr_type)DEFAULT, ANALOGREFERENCE },
{ string223, (fn_ptr_type)EXTERNAL, ANALOGREFERENCE },
{ string224, NULL, 0x00 },
#elif defined(CPU_RP2040)
{ string219, (fn_ptr_type)INPUT, PINMODE },
{ string220, (fn_ptr_type)INPUT_PULLUP, PINMODE },
{ string221, (fn_ptr_type)INPUT_PULLDOWN, PINMODE },
{ string222, (fn_ptr_type)OUTPUT, PINMODE },
{ string223, (fn_ptr_type)(SIO_BASE+SIO_GPIO_IN_OFFSET), REGISTER },
{ string224, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_OFFSET), REGISTER },
{ string225, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_SET_OFFSET), REGISTER },
{ string226, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_CLR_OFFSET), REGISTER },
{ string227, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OUT_XOR_OFFSET), REGISTER },
{ string228, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_OFFSET), REGISTER },
{ string229, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_SET_OFFSET), REGISTER },
{ string230, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_CLR_OFFSET), REGISTER },
{ string231, (fn_ptr_type)(SIO_BASE+SIO_GPIO_OE_XOR_OFFSET), REGISTER },
{ string232, NULL, 0x00 },
#endif
// Insert your own table entries here
};
// Table lookup functions
builtin_t lookupbuiltin (char* n) {
int entry = 0;
while (entry < ENDFUNCTIONS) {
if (strcasecmp(n, (char*)lookup_table[entry].string) == 0)
return (builtin_t)entry;
entry++;
}
return ENDFUNCTIONS;
}
intptr_t lookupfn (builtin_t name) {
return (intptr_t)lookup_table[name].fptr;
}
uint8_t getminmax (builtin_t name) {
uint8_t minmax = lookup_table[name].minmax;
return minmax;
}
void checkminmax (builtin_t name, int nargs) {
uint8_t minmax = getminmax(name);
if (nargs<(minmax >> 4)) error2(name, toofewargs);
if ((minmax & 0x0f) != 0x0f && nargs>(minmax & 0x0f)) error2(name, toomanyargs);
}
void testescape () {
if (Serial.read() == '~') error2(NIL, PSTR("escape!"));
}
// Main evaluator
#if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
#define ENDSTACK _ebss
#else
#define ENDSTACK end
#endif
extern uint32_t ENDSTACK; // Bottom of stack
object *eval (object *form, object *env) {
register int *sp asm ("r13");
int TC=0;
EVAL:
// Enough space?
// Serial.println((uint32_t)sp - (uint32_t)&ENDSTACK); // Find best STACKDIFF value
if (((uint32_t)sp - (uint32_t)&ENDSTACK) < STACKDIFF) error2(NIL, PSTR("stack overflow"));
if (Freespace <= WORKSPACESIZE>>4) gc(form, env); // GC when 1/16 of workspace left
// Escape
if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2(NIL, PSTR("escape!"));}
if (!tstflag(NOESC)) testescape();
if (form == NULL) return nil;
if (form->type >= NUMBER && form->type <= STRING) return form;
if (symbolp(form)) {
symbol_t name = form->name;
object *pair = value(name, env);
if (pair != NULL) return cdr(pair);
pair = value(name, GlobalEnv);
if (pair != NULL) return cdr(pair);
else if (builtinp(name)) return form;
error(NIL, PSTR("undefined"), form);
}
#if defined(CODESIZE)
if (form->type == CODE) error2(NIL, PSTR("can't evaluate CODE header"));
#endif
// It's a list
object *function = car(form);
object *args = cdr(form);
if (function == NULL) error(NIL, PSTR("illegal function"), nil);
if (!listp(args)) error(NIL, PSTR("can't evaluate a dotted pair"), args);
// List starts with a symbol?
if (symbolp(function)) {
builtin_t name = builtin(function->name);
if ((name == LET) || (name == LETSTAR)) {
int TCstart = TC;
if (args == NULL) error2(name, noargument);
object *assigns = first(args);
if (!listp(assigns)) error(name, notalist, assigns);
object *forms = cdr(args);
object *newenv = env;
push(newenv, GCStack);
while (assigns != NULL) {
object *assign = car(assigns);
if (!consp(assign)) push(cons(assign,nil), newenv);
else if (cdr(assign) == NULL) push(cons(first(assign),nil), newenv);
else push(cons(first(assign),eval(second(assign),env)), newenv);
car(GCStack) = newenv;
if (name == LETSTAR) env = newenv;
assigns = cdr(assigns);
}
env = newenv;
pop(GCStack);
form = tf_progn(forms,env);
TC = TCstart;
goto EVAL;
}
if (name == LAMBDA) {
if (env == NULL) return form;
object *envcopy = NULL;
while (env != NULL) {
object *pair = first(env);
if (pair != NULL) push(pair, envcopy);
env = cdr(env);
}
return cons(bsymbol(CLOSURE), cons(envcopy,args));
}
if ((name > SPECIAL_FORMS) && (name < TAIL_FORMS)) {
return ((fn_ptr_type)lookupfn(name))(args, env);
}
if ((name > TAIL_FORMS) && (name < FUNCTIONS)) {
form = ((fn_ptr_type)lookupfn(name))(args, env);
TC = 1;
goto EVAL;
}
if (((name > 0) && (name < SPECIAL_FORMS)) || ((name > KEYWORDS) && (name < USERFUNCTIONS))) error2(name, PSTR("can't be used as a function"));
}
// Evaluate the parameters - result in head
object *fname = car(form);
int TCstart = TC;
object *head = cons(eval(fname, env), NULL);
push(head, GCStack); // Don't GC the result list
object *tail = head;
form = cdr(form);
int nargs = 0;
while (form != NULL){
object *obj = cons(eval(car(form),env),NULL);
cdr(tail) = obj;
tail = obj;
form = cdr(form);
nargs++;
}
function = car(head);
args = cdr(head);
if (symbolp(function)) {
builtin_t bname = builtin(function->name);
if (!builtinp(function->name)) error(NIL, PSTR("not valid here"), fname);
checkminmax(bname, nargs);
object *result = ((fn_ptr_type)lookupfn(bname))(args, env);
pop(GCStack);
return result;
}
if (consp(function)) {
symbol_t name = sym(NIL);
if (!listp(fname)) name = fname->name;
if (isbuiltin(car(function), LAMBDA)) {
form = closure(TCstart, name, function, args, &env);
pop(GCStack);
int trace = tracing(fname->name);
if (trace) {
object *result = eval(form, env);
indent((--(TraceDepth[trace-1]))<<1, ' ', pserial);
pint(TraceDepth[trace-1], pserial);
pserial(':'); pserial(' ');
printobject(fname, pserial); pfstring(PSTR(" returned "), pserial);
printobject(result, pserial); pln(pserial);
return result;
} else {
TC = 1;
goto EVAL;
}
}
if (isbuiltin(car(function), CLOSURE)) {
function = cdr(function);
form = closure(TCstart, name, function, args, &env);
pop(GCStack);
TC = 1;
goto EVAL;
}
if (car(function)->type == CODE) {
int n = listlength(DEFCODE, second(function));
if (nargs<n) errorsym2(fname->name, toofewargs);
if (nargs>n) errorsym2(fname->name, toomanyargs);
uint32_t entry = startblock(car(function)) + 1;
pop(GCStack);
return call(entry, n, args, env);
}
}
error(NIL, PSTR("illegal function"), fname); return nil;
}
// Print functions
void pserial (char c) {
LastPrint = c;
if (c == '\n') Serial.write('\r');
Serial.write(c);
}
const char ControlCodes[] PROGMEM = "Null\0SOH\0STX\0ETX\0EOT\0ENQ\0ACK\0Bell\0Backspace\0Tab\0Newline\0VT\0"
"Page\0Return\0SO\0SI\0DLE\0DC1\0DC2\0DC3\0DC4\0NAK\0SYN\0ETB\0CAN\0EM\0SUB\0Escape\0FS\0GS\0RS\0US\0Space\0";
void pcharacter (uint8_t c, pfun_t pfun) {
if (!tstflag(PRINTREADABLY)) pfun(c);
else {
pfun('#'); pfun('\\');
if (c <= 32) {
const char *p = ControlCodes;
while (c > 0) {p = p + strlen(p) + 1; c--; }
pfstring(p, pfun);
} else if (c < 127) pfun(c);
else pint(c, pfun);
}
}
void pstring (char *s, pfun_t pfun) {
while (*s) pfun(*s++);
}
void plispstring (object *form, pfun_t pfun) {
plispstr(form->name, pfun);
}
void plispstr (symbol_t name, pfun_t pfun) {
object *form = (object *)name;
while (form != NULL) {
int chars = form->chars;
for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) {
char ch = chars>>i & 0xFF;
if (tstflag(PRINTREADABLY) && (ch == '"' || ch == '\\')) pfun('\\');
if (ch) pfun(ch);
}
form = car(form);
}
}
void printstring (object *form, pfun_t pfun) {
if (tstflag(PRINTREADABLY)) pfun('"');
plispstr(form->name, pfun);
if (tstflag(PRINTREADABLY)) pfun('"');
}
void pbuiltin (builtin_t name, pfun_t pfun) {
int p = 0;
const char *s = lookup_table[name].string;
while (1) {
char c = s[p++];
if (c == 0) return;
pfun(c);
}
}
void pradix40 (symbol_t name, pfun_t pfun) {
uint32_t x = untwist(name);
for (int d=102400000; d>0; d = d/40) {
uint32_t j = x/d;
char c = fromradix40(j);
if (c == 0) return;
pfun(c); x = x - j*d;
}
}
void printsymbol (object *form, pfun_t pfun) {
psymbol(form->name, pfun);
}
void psymbol (symbol_t name, pfun_t pfun) {
if ((name & 0x03) == 0) plispstr(name, pfun);
else {
uint32_t value = untwist(name);
if (value < PACKEDS) error2(NIL, PSTR("invalid symbol"));
else if (value >= BUILTINS) pbuiltin((builtin_t)(value-BUILTINS), pfun);
else pradix40(name, pfun);
}
}
void pfstring (const char *s, pfun_t pfun) {
int p = 0;
while (1) {
char c = s[p++];
if (c == 0) return;
pfun(c);
}
}
void pint (int i, pfun_t pfun) {
uint32_t j = i;
if (i<0) { pfun('-'); j=-i; }
pintbase(j, 10, pfun);
}
void pintbase (uint32_t i, uint8_t base, pfun_t pfun) {
int lead = 0; uint32_t p = 1000000000;
if (base == 2) p = 0x80000000; else if (base == 16) p = 0x10000000;
for (uint32_t d=p; d>0; d=d/base) {
uint32_t j = i/d;
if (j!=0 || lead || d==1) { pfun((j<10) ? j+'0' : j+'W'); lead=1;}
i = i - j*d;
}
}
void printhex4 (int i, pfun_t pfun) {
int p = 0x1000;
for (int d=p; d>0; d=d/16) {
int j = i/d;
pfun((j<10) ? j+'0' : j + 'W');
i = i - j*d;
}
pfun(' ');
}
void pmantissa (float f, pfun_t pfun) {
int sig = floor(log10(f));
int mul = pow(10, 5 - sig);
int i = round(f * mul);
bool point = false;
if (i == 1000000) { i = 100000; sig++; }
if (sig < 0) {
pfun('0'); pfun('.'); point = true;
for (int j=0; j < - sig - 1; j++) pfun('0');
}
mul = 100000;
for (int j=0; j<7; j++) {
int d = (int)(i / mul);
pfun(d + '0');
i = i - d * mul;
if (i == 0) {
if (!point) {
for (int k=j; k<sig; k++) pfun('0');
pfun('.'); pfun('0');
}
return;
}
if (j == sig && sig >= 0) { pfun('.'); point = true; }
mul = mul / 10;
}
}
void pfloat (float f, pfun_t pfun) {
if (isnan(f)) { pfstring(PSTR("NaN"), pfun); return; }
if (f == 0.0) { pfun('0'); return; }
if (isinf(f)) { pfstring(PSTR("Inf"), pfun); return; }
if (f < 0) { pfun('-'); f = -f; }
// Calculate exponent
int e = 0;
if (f < 1e-3 || f >= 1e5) {
e = floor(log(f) / 2.302585); // log10 gives wrong result
f = f / pow(10, e);
}
pmantissa (f, pfun);
// Exponent
if (e != 0) {
pfun('e');
pint(e, pfun);
}
}
inline void pln (pfun_t pfun) {
pfun('\n');
}
void pfl (pfun_t pfun) {
if (LastPrint != '\n') pfun('\n');
}
void plist (object *form, pfun_t pfun) {
pfun('(');
printobject(car(form), pfun);
form = cdr(form);
while (form != NULL && listp(form)) {
pfun(' ');
printobject(car(form), pfun);
form = cdr(form);
}
if (form != NULL) {
pfstring(PSTR(" . "), pfun);
printobject(form, pfun);
}
pfun(')');
}
void pstream (object *form, pfun_t pfun) {
pfun('<');
pfstring(streamname[(form->integer)>>8], pfun);
pfstring(PSTR("-stream "), pfun);
pint(form->integer & 0xFF, pfun);
pfun('>');
}
void printobject (object *form, pfun_t pfun) {
if (form == NULL) pfstring(PSTR("nil"), pfun);
else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring(PSTR("<closure>"), pfun);
else if (listp(form)) plist(form, pfun);
else if (integerp(form)) pint(form->integer, pfun);
else if (floatp(form)) pfloat(form->single_float, pfun);
else if (symbolp(form)) { if (form->name != sym(NOTHING)) printsymbol(form, pfun); }
else if (characterp(form)) pcharacter(form->chars, pfun);
else if (stringp(form)) printstring(form, pfun);
else if (arrayp(form)) printarray(form, pfun);
else if (form->type == CODE) pfstring(PSTR("code"), pfun);
else if (streamp(form)) pstream(form, pfun);
else error2(NIL, PSTR("error in print"));
}
void prin1object (object *form, pfun_t pfun) {
char temp = Flags;
clrflag(PRINTREADABLY);
printobject(form, pfun);
Flags = temp;
}
// Read functions
int glibrary () {
if (LastChar) {
char temp = LastChar;
LastChar = 0;
return temp;
}
char c = LispLibrary[GlobalStringIndex++];
return (c != 0) ? c : -1; // -1?
}
void loadfromlibrary (object *env) {
GlobalStringIndex = 0;
object *line = read(glibrary);
while (line != NULL) {
push(line, GCStack);
eval(line, env);
pop(GCStack);
line = read(glibrary);
}
}
// For line editor
const int TerminalWidth = 80;
volatile int WritePtr = 0, ReadPtr = 0;
const int KybdBufSize = 333; // 42*8 - 3
char KybdBuf[KybdBufSize];
volatile uint8_t KybdAvailable = 0;
// Parenthesis highlighting
void esc (int p, char c) {
Serial.write('\e'); Serial.write('[');
Serial.write((char)('0'+ p/100));
Serial.write((char)('0'+ (p/10) % 10));
Serial.write((char)('0'+ p % 10));
Serial.write(c);
}
void hilight (char c) {
Serial.write('\e'); Serial.write('['); Serial.write(c); Serial.write('m');
}
void Highlight (int p, int wp, uint8_t invert) {
wp = wp + 2; // Prompt
#if defined (printfreespace)
int f = Freespace;
while (f) { wp++; f=f/10; }
#endif
int line = wp/TerminalWidth;
int col = wp%TerminalWidth;
int targetline = (wp - p)/TerminalWidth;
int targetcol = (wp - p)%TerminalWidth;
int up = line-targetline, left = col-targetcol;
if (p) {
if (up) esc(up, 'A');
if (col > targetcol) esc(left, 'D'); else esc(-left, 'C');
if (invert) hilight('7');
Serial.write('('); Serial.write('\b');
// Go back
if (up) esc(up, 'B'); // Down
if (col > targetcol) esc(left, 'C'); else esc(-left, 'D');
Serial.write('\b'); Serial.write(')');
if (invert) hilight('0');
}
}
void processkey (char c) {
if (c == 27) { setflag(ESCAPE); return; } // Escape key
#if defined(vt100)
static int parenthesis = 0, wp = 0;
// Undo previous parenthesis highlight
Highlight(parenthesis, wp, 0);
parenthesis = 0;
#endif
// Edit buffer
if (c == '\n' || c == '\r') {
pserial('\n');
KybdAvailable = 1;
ReadPtr = 0;
return;
}
if (c == 8 || c == 0x7f) { // Backspace key
if (WritePtr > 0) {
WritePtr--;
Serial.write(8); Serial.write(' '); Serial.write(8);
if (WritePtr) c = KybdBuf[WritePtr-1];
}
} else if (WritePtr < KybdBufSize) {
KybdBuf[WritePtr++] = c;
Serial.write(c);
}
#if defined(vt100)
// Do new parenthesis highlight
if (c == ')') {
int search = WritePtr-1, level = 0;
while (search >= 0 && parenthesis == 0) {
c = KybdBuf[search--];
if (c == ')') level++;
if (c == '(') {
level--;
if (level == 0) {parenthesis = WritePtr-search-1; wp = WritePtr; }
}
}
Highlight(parenthesis, wp, 1);
}
#endif
return;
}
int gserial () {
if (LastChar) {
char temp = LastChar;
LastChar = 0;
return temp;
}
#if defined(lineeditor)
while (!KybdAvailable) {
while (!Serial.available());
char temp = Serial.read();
processkey(temp);
}
if (ReadPtr != WritePtr) return KybdBuf[ReadPtr++];
KybdAvailable = 0;
WritePtr = 0;
return '\n';
#else
unsigned long start = millis();
while (!Serial.available()) if (millis() - start > 1000) clrflag(NOECHO);
char temp = Serial.read();
if (temp != '\n' && !tstflag(NOECHO)) pserial(temp);
return temp;
#endif
}
object *nextitem (gfun_t gfun) {
int ch = gfun();
while(issp(ch)) ch = gfun();
if (ch == ';') {
do { ch = gfun(); if (ch == ';' || ch == '(') setflag(NOECHO); }
while(ch != '(');
}
if (ch == '\n') ch = gfun();
if (ch == -1) return nil;
if (ch == ')') return (object *)KET;
if (ch == '(') return (object *)BRA;
if (ch == '\'') return (object *)QUO;
// Parse string
if (ch == '"') return readstring('"', gfun);
// Parse symbol, character, or number
int index = 0, base = 10, sign = 1;
char buffer[BUFFERSIZE];
int bufmax = BUFFERSIZE-3; // Max index
unsigned int result = 0;
bool isfloat = false;
float fresult = 0.0;
if (ch == '+') {
buffer[index++] = ch;
ch = gfun();
} else if (ch == '-') {
sign = -1;
buffer[index++] = ch;
ch = gfun();
} else if (ch == '.') {
buffer[index++] = ch;
ch = gfun();
if (ch == ' ') return (object *)DOT;
isfloat = true;
}
// Parse reader macros
else if (ch == '#') {
ch = gfun();
char ch2 = ch & ~0x20; // force to upper case
if (ch == '\\') { // Character
base = 0; ch = gfun();
if (issp(ch) || ch == ')' || ch == '(') return character(ch);
else LastChar = ch;
} else if (ch == '|') {
do { while (gfun() != '|'); }
while (gfun() != '#');
return nextitem(gfun);
} else if (ch2 == 'B') base = 2;
else if (ch2 == 'O') base = 8;
else if (ch2 == 'X') base = 16;
else if (ch == '\'') return nextitem(gfun);
else if (ch == '.') {
setflag(NOESC);
object *result = eval(read(gfun), NULL);
clrflag(NOESC);
return result;
}
else if (ch == '(') { LastChar = ch; return readarray(1, read(gfun)); }
else if (ch == '*') return readbitarray(gfun);
else if (ch >= '1' && ch <= '9' && (gfun() & ~0x20) == 'A') return readarray(ch - '0', read(gfun));
else error2(NIL, PSTR("illegal character after #"));
ch = gfun();
}
int valid; // 0=undecided, -1=invalid, +1=valid
if (ch == '.') valid = 0; else if (digitvalue(ch)<base) valid = 1; else valid = -1;
bool isexponent = false;
int exponent = 0, esign = 1;
buffer[2] = '\0'; buffer[3] = '\0'; buffer[4] = '\0'; buffer[5] = '\0'; // In case symbol is < 5 letters
float divisor = 10.0;
while(!issp(ch) && ch != ')' && ch != '(' && index < bufmax) {
buffer[index++] = ch;
if (base == 10 && ch == '.' && !isexponent) {
isfloat = true;
fresult = result;
} else if (base == 10 && (ch == 'e' || ch == 'E')) {
if (!isfloat) { isfloat = true; fresult = result; }
isexponent = true;
if (valid == 1) valid = 0; else valid = -1;
} else if (isexponent && ch == '-') {
esign = -esign;
} else if (isexponent && ch == '+') {
} else {
int digit = digitvalue(ch);
if (digitvalue(ch)<base && valid != -1) valid = 1; else valid = -1;
if (isexponent) {
exponent = exponent * 10 + digit;
} else if (isfloat) {
fresult = fresult + digit / divisor;
divisor = divisor * 10.0;
} else {
result = result * base + digit;
}
}
ch = gfun();
}
buffer[index] = '\0';
if (ch == ')' || ch == '(') LastChar = ch;
if (isfloat && valid == 1) return makefloat(fresult * sign * pow(10, exponent * esign));
else if (valid == 1) {
if (base == 10 && result > ((unsigned int)INT_MAX+(1-sign)/2))
return makefloat((float)result*sign);
return number(result*sign);
} else if (base == 0) {
if (index == 1) return character(buffer[0]);
const char* p = ControlCodes; char c = 0;
while (c < 33) {
if (strcasecmp(buffer, p) == 0) return character(c);
p = p + strlen(p) + 1; c++;
}
if (index == 3) return character((buffer[0]*10+buffer[1])*10+buffer[2]-5328);
error2(NIL, PSTR("unknown character"));
}
builtin_t x = lookupbuiltin(buffer);
if (x == NIL) return nil;
if (x != ENDFUNCTIONS) return bsymbol(x);
else if ((index <= 6) && valid40(buffer)) return intern(twist(pack40(buffer)));
buffer[index+1] = '\0'; buffer[index+2] = '\0'; buffer[index+3] = '\0'; // For internlong
return internlong(buffer);
}
object *readrest (gfun_t gfun) {
object *item = nextitem(gfun);
object *head = NULL;
object *tail = NULL;
while (item != (object *)KET) {
if (item == (object *)BRA) {
item = readrest(gfun);
} else if (item == (object *)QUO) {
item = cons(bsymbol(QUOTE), cons(read(gfun), NULL));
} else if (item == (object *)DOT) {
tail->cdr = read(gfun);
if (readrest(gfun) != NULL) error2(NIL, PSTR("malformed list"));
return head;
} else {
object *cell = cons(item, NULL);
if (head == NULL) head = cell;
else tail->cdr = cell;
tail = cell;
item = nextitem(gfun);
}
}
return head;
}
object *read (gfun_t gfun) {
object *item = nextitem(gfun);
if (item == (object *)KET) error2(NIL, PSTR("incomplete list"));
if (item == (object *)BRA) return readrest(gfun);
if (item == (object *)DOT) return read(gfun);
if (item == (object *)QUO) return cons(bsymbol(QUOTE), cons(read(gfun), NULL));
return item;
}
// Setup
void initgfx () {
#if defined(gfxsupport)
tft.begin();
tft.clearDisplay();
tft.drawPixel(10, 10, COLOR_BLACK);
tft.drawPixel(14, 10, COLOR_BLACK);
tft.drawPixel(10, 13, COLOR_BLACK);
tft.drawPixel(11, 14, COLOR_BLACK);
tft.drawPixel(12, 14, COLOR_BLACK);
tft.drawPixel(13, 14, COLOR_BLACK);
tft.drawPixel(14, 13, COLOR_BLACK);
tft.refresh();
#endif
}
void initenv () {
GlobalEnv = NULL;
tee = bsymbol(TEE);
}
void setup () {
Serial.begin(9600);
int start = millis();
while ((millis() - start) < 5000) { if (Serial) break; }
initworkspace();
initenv();
initsleep();
initgfx();
pfstring(PSTR("uLisp 4.1a "), pserial); pln(pserial);
}
// Read/Evaluate/Print loop
void repl (object *env) {
for (;;) {
randomSeed(micros());
gc(NULL, env);
#if defined (printfreespace)
pint(Freespace, pserial);
#endif
if (BreakLevel) {
pfstring(PSTR(" : "), pserial);
pint(BreakLevel, pserial);
}
pserial('>'); pserial(' ');
object *line = read(gserial);
if (BreakLevel && line == nil) { pln(pserial); return; }
if (line == (object *)KET) error2(NIL, PSTR("unmatched right bracket"));
push(line, GCStack);
pfl(pserial);
line = eval(line, env);
pfl(pserial);
printobject(line, pserial);
pop(GCStack);
pfl(pserial);
pln(pserial);
}
}
void loop () {
if (!setjmp(exception)) {
#if defined(resetautorun)
volatile int autorun = 12; // Fudge to keep code size the same
#else
volatile int autorun = 13;
#endif
if (autorun == 12) autorunimage();
}
// Come here after error
delay(100); while (Serial.available()) Serial.read();
clrflag(NOESC); BreakLevel = 0;
for (int i=0; i<TRACEMAX; i++) TraceDepth[i] = 0;
#if defined(sdcardsupport)
SDpfile.close(); SDgfile.close();
#endif
#if defined(lisplibrary)
if (!tstflag(LIBRARYLOADED)) { setflag(LIBRARYLOADED); loadfromlibrary(NULL); }
#endif
repl(NULL);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment