494 lines
11 KiB
C++
494 lines
11 KiB
C++
/*
|
||
User Extensions
|
||
*/
|
||
|
||
// Utility functions
|
||
uint8_t
|
||
dec_to_bcd(uint8_t n)
|
||
{
|
||
uint8_t bcd = 0;
|
||
uint8_t tens = n / 10;
|
||
|
||
bcd = tens << 4;
|
||
tens *= 10;
|
||
bcd += (n - tens) & 0x0f;
|
||
return bcd;
|
||
}
|
||
|
||
|
||
uint8_t
|
||
bcd_to_dec(uint8_t n)
|
||
{
|
||
return ((n>>4) * 10) + (n&0x0f);
|
||
}
|
||
|
||
/*
|
||
* Standard definitions
|
||
*
|
||
* These definitions should be the same on every platform.
|
||
*/
|
||
object *
|
||
fn_platform(object *args, object *env)
|
||
{
|
||
(void) args;
|
||
(void) env;
|
||
|
||
#if defined(PLATFORM_PICOCALC)
|
||
const char platformName[] = ":picocalc";
|
||
#elif defined(TDECK_PERI_POWERON) /* first t-deck define */
|
||
const char platformName[] = ":t-deck";
|
||
#elif defined(ARDUINO_TEENSY41)
|
||
const char platformName[] = ":teensy41";
|
||
#else
|
||
const char platformName[] = ":unknown";
|
||
#endif
|
||
return internlong((char *)platformName);
|
||
}
|
||
|
||
object *
|
||
fn_bcd_to_dec(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
|
||
object *arg = car(args);
|
||
|
||
int n = checkinteger(arg);
|
||
if ((n < 0) || (n > 153)) {
|
||
error2("number not in the range [0...#x99].");
|
||
}
|
||
|
||
uint8_t result = bcd_to_dec(static_cast<uint8_t>(n));
|
||
return number(static_cast<int>(result));
|
||
}
|
||
|
||
object *
|
||
fn_dec_to_bcd(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
|
||
object *arg = car(args);
|
||
|
||
int n = checkinteger(arg);
|
||
if ((n < 0) || (n > 99)) {
|
||
error2("number not in the range [0...99].");
|
||
}
|
||
|
||
uint8_t result = dec_to_bcd(static_cast<uint8_t>(n));
|
||
return number(static_cast<int>(result));
|
||
}
|
||
|
||
object *
|
||
fn_now(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
static unsigned long Offset;
|
||
unsigned long now = millis()/1000;
|
||
int nargs = listlength(args);
|
||
|
||
// Set time
|
||
if (nargs == 3) {
|
||
Offset = (unsigned long)((checkinteger(first(args))*60 \
|
||
+ checkinteger(second(args)))*60 \
|
||
+ checkinteger(third(args)) - now);
|
||
} else if (nargs > 0) {
|
||
error2(PSTR("wrong number of arguments"));
|
||
}
|
||
|
||
// Return time
|
||
unsigned long secs = Offset + now;
|
||
object *seconds = number(secs%60);
|
||
object *minutes = number((secs/60)%60);
|
||
object *hours = number((secs/3600)%24);
|
||
|
||
return cons(hours, cons(minutes, cons(seconds, NULL)));
|
||
}
|
||
|
||
|
||
void
|
||
hyperprint(object *form, int lm, pfun_t pfun)
|
||
{
|
||
if (atom(form)) {
|
||
if (isbuiltin(form, NOTHING)) {
|
||
printsymbol(form, pfun);
|
||
} else {
|
||
printobject(form, pfun);
|
||
}
|
||
} else if (quoted(form)) {
|
||
pfun('\'');
|
||
hyperprint(car(cdr(form)), lm + 1, pfun);
|
||
} else {
|
||
lm = lm + PPINDENT;
|
||
bool fits = (subwidth(form, PPWIDTH - lm - PPINDENT) >= 0);
|
||
int special = 0, extra = 0; bool separate = true;
|
||
object *arg = car(form);
|
||
|
||
if (symbolp(arg) && builtinp(arg->name)) {
|
||
uint8_t minmax = getminmax(builtin(arg->name));
|
||
if (minmax == 0327 || minmax == 0313) {
|
||
special = 2; // defun, setq, setf, defvar
|
||
} else if (minmax == 0317 || minmax == 0017 ||
|
||
minmax == 0117 || minmax == 0123) {
|
||
special = 1;
|
||
}
|
||
}
|
||
|
||
while (form != NULL) {
|
||
if (atom(form)) {
|
||
pfstring(PSTR(" . "), pfun);
|
||
printobject(form, pfun);
|
||
pfun(')');
|
||
return;
|
||
} else if (separate) {
|
||
pfun('(');
|
||
separate = false;
|
||
} else if (special) {
|
||
pfun(' ');
|
||
special--;
|
||
} else if (fits) {
|
||
pfun(' ');
|
||
} else {
|
||
pln(pfun);
|
||
indent(lm, ' ', pfun);
|
||
}
|
||
|
||
hyperprint(car(form), lm+extra, pfun);
|
||
form = cdr(form);
|
||
}
|
||
pfun(')');
|
||
}
|
||
}
|
||
|
||
object *
|
||
fn_sym_def(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
|
||
object *pair = findvalue(obj, env);
|
||
object *var = car(pair);
|
||
object *val = cdr(pair);
|
||
pln(pfun);
|
||
|
||
if (consp(val) && symbolp(car(val)) && builtin(car(val)->name) == LAMBDA) {
|
||
hyperprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun);
|
||
} else {
|
||
hyperprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun);
|
||
}
|
||
|
||
pln(pfun);
|
||
ppwidth = PPWIDTH;
|
||
return bsymbol(NOTHING);
|
||
}
|
||
|
||
object *
|
||
fn_listlibrary2(object *args, object *env)
|
||
{
|
||
(void) args, (void) env;
|
||
object *lst = nil;
|
||
|
||
GlobalStringIndex = 0;
|
||
object *line = read(glibrary);
|
||
while (line != NULL) {
|
||
builtin_t bname = builtin(first(line)->name);
|
||
if (bname == DEFUN || bname == DEFVAR) {
|
||
lst = cons(second(line), lst);
|
||
}
|
||
line = read(glibrary);
|
||
}
|
||
|
||
return lst;
|
||
}
|
||
|
||
|
||
|
||
object *
|
||
fn_lambdap(object *arg, object *env)
|
||
{
|
||
(void) env;
|
||
|
||
if (consp(arg)) {
|
||
arg = car(arg);
|
||
}
|
||
|
||
if (builtin(arg->name) == LAMBDA) {
|
||
return tee;
|
||
}
|
||
|
||
return nil;
|
||
}
|
||
|
||
// SD card standard library.
|
||
|
||
#if defined(sdcardsupport)
|
||
object *
|
||
fn_sd_rename(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
char buffer1[BUFFERSIZE];
|
||
char buffer2[BUFFERSIZE];
|
||
|
||
|
||
object *pathFrom = car(args);
|
||
if (!stringp(pathFrom)) {
|
||
error2("filenames must be strings.");
|
||
}
|
||
|
||
object *pathTo = car(cdr(args));
|
||
if (!stringp(pathTo)) {
|
||
error2("filenames must be strings.");
|
||
}
|
||
|
||
if (!SD.rename((const char *)MakeFilename(pathFrom, buffer1),
|
||
(const char *)MakeFilename(pathTo, buffer2))) {
|
||
return nil;
|
||
}
|
||
|
||
return tee;
|
||
}
|
||
|
||
object *
|
||
fn_sd_remove(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
char buffer[BUFFERSIZE];
|
||
|
||
|
||
object *arg = car(args);
|
||
if (!SD.remove(MakeFilename(arg, buffer))) {
|
||
return nil;
|
||
}
|
||
|
||
return tee;
|
||
}
|
||
|
||
object *
|
||
fn_sd_existsp(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
|
||
char buffer[BUFFERSIZE];
|
||
|
||
object *arg = car(args);
|
||
if (!SD.exists(MakeFilename(arg, buffer))) {
|
||
return nil;
|
||
}
|
||
|
||
return tee;
|
||
}
|
||
|
||
/*
|
||
(sd-make-dir path)
|
||
Create a directory on the SD card.
|
||
This will also create any intermediate directories that don’t already exists;
|
||
e.g. SD.mkdir("a/b/c") will create a, b, and c.
|
||
*/
|
||
object *
|
||
fn_sd_mkdir(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
|
||
char buffer[BUFFERSIZE];
|
||
object *arg = car(args);
|
||
|
||
if (SD.mkdir(MakeFilename(arg, buffer))) {
|
||
return tee;
|
||
}
|
||
|
||
return nil;
|
||
}
|
||
|
||
|
||
/*
|
||
(sd-remove-dir path)
|
||
Remove a directory from the SD card. The directory must be empty.
|
||
*/
|
||
object *
|
||
fn_sd_rmdir(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
|
||
char buffer[BUFFERSIZE];
|
||
object *arg = car(args);
|
||
|
||
if (SD.rmdir(MakeFilename(arg, buffer))) {
|
||
return tee;
|
||
}
|
||
|
||
return nil;
|
||
}
|
||
|
||
|
||
/*
|
||
* (sd-list)
|
||
*/
|
||
object *
|
||
fn_sd_list(object *args, object *env)
|
||
{
|
||
(void) env;
|
||
|
||
char *sd_path_buf = NULL;
|
||
|
||
SDBegin();
|
||
File root;
|
||
object *result = cons(NULL, NULL);
|
||
object *ptr = result;
|
||
|
||
if (args != NULL) {
|
||
object *arg1 = checkstring(first(args));
|
||
int len = stringlength(arg1) + 2; //make it longer for the initial slash and the null terminator
|
||
sd_path_buf = (char*)malloc(len);
|
||
|
||
if (sd_path_buf != NULL) {
|
||
cstring(arg1, &sd_path_buf[1], len-1);
|
||
sd_path_buf[0] = '/'; //really weird way to add a slash at the front...
|
||
root = SD.open(sd_path_buf);
|
||
}
|
||
} else{
|
||
root = SD.open("/");
|
||
}
|
||
|
||
while (true) {
|
||
File entry = root.openNextFile();
|
||
if (!entry) {
|
||
break; // no more files
|
||
}
|
||
object *filename = lispstring((char*)entry.name());
|
||
|
||
if (entry.isDirectory()) {
|
||
cdr(ptr) = cons(filename, NULL);
|
||
} else{
|
||
cdr(ptr) = cons(cons(filename, number(entry.size())), NULL);
|
||
}
|
||
|
||
ptr = cdr(ptr);
|
||
entry.close();
|
||
}
|
||
|
||
if (sd_path_buf != NULL) {
|
||
free(sd_path_buf);
|
||
}
|
||
|
||
root.close();
|
||
return cdr(result);
|
||
}
|
||
#endif
|
||
|
||
|
||
/*
|
||
* SYMBOL NAMES
|
||
*
|
||
* Define symbol names as const char[] PROGMEM here.
|
||
*/
|
||
const char stringlambdap[] PROGMEM = "lambdap";
|
||
const char stringnow[] PROGMEM = "now";
|
||
const char string_sym_def[] PROGMEM = "symdef";
|
||
const char stringbcd_to_dec[] PROGMEM = "bcd-to-dec";
|
||
const char stringdec_to_bcd[] PROGMEM = "dec-to-bcd";
|
||
const char stringlist_library2[] PROGMEM = "list-library2";
|
||
const char stringplatform[] PROGMEM = "platform";
|
||
|
||
#if defined(sdcardsupport)
|
||
const char stringsd_rename[] PROGMEM = "sd-rename";
|
||
const char stringsd_remove[] PROGMEM = "sd-remove";
|
||
const char stringsd_existsp[] PROGMEM = "sd-exists-p";
|
||
const char stringsd_mkdir[] PROGMEM = "sd-make-dir";
|
||
const char stringsd_rmdir[] PROGMEM = "sd-remove-dir";
|
||
const char stringsd_dir[] PROGMEM = "sd-list";
|
||
#endif
|
||
|
||
/*
|
||
* DOCUMENTATION STRINGS
|
||
*
|
||
* Define documentation strings as const char[] PROGMEM here.
|
||
*/
|
||
|
||
const char doclambdap[] PROGMEM = "(lambdap x)"
|
||
"Returns t if the form passed in is a lambda.";
|
||
|
||
const char docnow[] PROGMEM = "(now [hh mm ss])\n"
|
||
"Sets the current time, or with no arguments returns the current time\n"
|
||
"as a list of three integers (hh mm ss).";
|
||
|
||
const char doc_sym_def[] PROGMEM = "(symbol-def symbol [str])\n"
|
||
"Prints the definition of a symbol (variable or function) defined in\n"
|
||
"ulisp using the pretty printer."
|
||
"If str is specified it prints to the specified stream.\n"
|
||
"It returns no value.";
|
||
|
||
const char doc_bcd_to_dec[] PROGMEM = "(bcd-to-dec n)\n"
|
||
"Convert the BCD-encoded number n to decimal.";
|
||
|
||
const char doc_dec_to_bcd[] PROGMEM = "(dec-to-bcd n)\n"
|
||
"BCD encode n.";
|
||
|
||
const char doc_list_library2[] PROGMEM = "(list-library2)\n"
|
||
"Return a list of all symbols in the current Lisp library.";
|
||
|
||
const char doc_platform[] PROGMEM = "(platform)\n"
|
||
"Returns a keyword with the current platform. Supports :picocalc,\n"
|
||
":t-deck, and :teensy41. Otherwise, returns :unknown.";
|
||
|
||
#if defined(sdcardsupport)
|
||
const char docsd_rename[] PROGMEM = "(sd-rename from to)\n"
|
||
"Renames the file named by 'from' to 'to.'";
|
||
|
||
const char docsd_remove[] PROGMEM = "(sd-remove file)\n"
|
||
"Removes the named file from the filesystem. Returns t if the file\n"
|
||
"was remove successfully.";
|
||
|
||
const char docsd_existsp[] PROGMEM = "(sd-exists-p file)\n"
|
||
"Returns t if the named file exists.";
|
||
|
||
const char docsd_dir[] PROGMEM = "(sd-list [directory])\n"
|
||
"Returns a list of filenames in the root or named directory.";
|
||
|
||
const char docsd_mkdir[] PROGMEM = "(sd-make-dir directory)\n"
|
||
"Create a directory with the specified name. Returns t if \n"
|
||
"successful, otherwise nil.";
|
||
|
||
const char docsd_rmdir[] PROGMEM = "(sd-remove-dir directory)\n"
|
||
"Remove the named directory. Returns t if successful, otherwise nil.";
|
||
#endif
|
||
|
||
|
||
// Symbol lookup table
|
||
const tbl_entry_t lookup_table2[] PROGMEM = {
|
||
{ stringlambdap, fn_lambdap, 0211, doclambdap },
|
||
{ stringnow, fn_now, 0203, docnow },
|
||
{ string_sym_def, fn_sym_def, 0212, doc_sym_def },
|
||
{ stringbcd_to_dec, fn_bcd_to_dec, 0211, doc_bcd_to_dec },
|
||
{ stringdec_to_bcd, fn_dec_to_bcd, 0211, doc_dec_to_bcd },
|
||
{ stringlist_library2, fn_listlibrary2, 0200, doc_list_library2 },
|
||
{ stringplatform, fn_platform, 0200, doc_platform },
|
||
|
||
#if defined(sdcardsupport)
|
||
{ stringsd_rename, fn_sd_rename, 0222, docsd_rename },
|
||
{ stringsd_remove, fn_sd_remove, 0211, docsd_remove },
|
||
{ stringsd_existsp, fn_sd_existsp, 0211, docsd_existsp },
|
||
{ stringsd_dir, fn_sd_list, 0201, docsd_dir },
|
||
{ stringsd_mkdir, fn_sd_mkdir, 0211, docsd_mkdir },
|
||
{ stringsd_rmdir, fn_sd_rmdir, 0211, docsd_rmdir },
|
||
#endif
|
||
};
|
||
|
||
// Table cross-reference functions
|
||
|
||
tbl_entry_t *tables[] = {lookup_table, lookup_table2};
|
||
const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) };
|
||
|
||
const tbl_entry_t *
|
||
table(int n)
|
||
{
|
||
return tables[n];
|
||
}
|
||
|
||
unsigned int
|
||
tablesize(int n)
|
||
{
|
||
return tablesizes[n];
|
||
}
|