diff --git a/LispLibrary.h b/LispLibrary.h index 0cb93ff..076f41e 100644 --- a/LispLibrary.h +++ b/LispLibrary.h @@ -180,6 +180,33 @@ Appends item to list destructively; lst will be altered with itm appended to the end of the list." (nconc lst (list itm))) +(defun user-symbols () + "(user-symbols) +Returns a list of all the symbols add by a user after boot." + (let ((library (list-library2))) + (remove-if (lambda (sym) (member sym library)) (globals)))) + +(defun reset-user-environment () + "(reset-user-environment) +Removes all user-defined symbols." + (mapcar 'makunbound (user-symbols))) + +(defun keyword-string (k) + "(keyword-string k) +Returns the keyword as a string, or nil if the arg isn't a keyword." + (when (keywordp k) + (subseq (string k) 1))) + +(defun load-platform () + "(load-platform) +Load-platform specific code if present, found on the SD card as +platform.lsp (e.g. picocalc.lsp)." + (let ((platform-file (concatenate 'string (keyword-string (platform)) ".lsp"))) + (when (sd-exists-p platform-file) + (load platform-file)))) + +(load-platform) + (defvar *pkg* nil) (defun lp () diff --git a/lisp/pkg.lsp b/lisp/pkg.lsp index d79ad10..1b425e4 100644 --- a/lisp/pkg.lsp +++ b/lisp/pkg.lsp @@ -21,4 +21,4 @@ (defun add-to-package (filename list) (with-sd-card (str filename 1) (dolist (f lst) - (symbol-def f str)))) + (symdef str)))) diff --git a/lisp/tools.lsp b/lisp/tools.lsp index c5132f6..39b809d 100644 --- a/lisp/tools.lsp +++ b/lisp/tools.lsp @@ -25,6 +25,6 @@ the stream, which follows the 'format directives." (defun i2c-scan2 (port) (dotimes (addr 127) (with-i2c (str port addr) - (format t "~2,0'x: " addr) + (format t "~,20'x: " addr) (if str (print t) (print nil))))) diff --git a/ulisp-arm.ino b/ulisp-arm.ino index 6e57719..0607a67 100644 --- a/ulisp-arm.ino +++ b/ulisp-arm.ino @@ -11,7 +11,7 @@ // Compile options #define resetautorun -// #define printfreespace +#define printfreespace // #define printgcs #define sdcardsupport #define gfxsupport @@ -21,7 +21,7 @@ #define vt100 #define extensions -#define CPI_PICOCALC 1 +#define PLATFORM_PICOCALC 1 #define i2ckbd #define BAUDRATE 96 @@ -39,7 +39,7 @@ defined(ARDUINO_RASPBERRY_PI_PICO_W) || \ defined(ARDUINO_RASPBERRY_PI_PICO_2) #define RASPBERRY_PI_PICO_PLATFORM -#if defined(CPI_PICOCALC) +#if defined(PLATFORM_PICOCALC) #define IS_PICOCALC #endif #endif @@ -245,7 +245,7 @@ #define CPU_RP2040 #if defined(gfxsupport) #include // Hardware-specific library - #if defined(CPI_PICOCALC) + #if defined(PLATFORM_PICOCALC) #include PCKeyboard pc_kbd; TFT_eSPI tft = TFT_eSPI(320,320); @@ -318,7 +318,7 @@ #define CPU_RP2350j #if defined(gfxsupport) #include // Hardware-specific library - #if defined(CPI_PICOCALC) + #if defined(PLATFORM_PICOCALC) #include PCKeyboard pc_kbd; TFT_eSPI tft = TFT_eSPI(320,320); @@ -1575,7 +1575,7 @@ saveimage(object *arg) #if defined(sdcardsupport) unsigned int imagesize = compactimage(&arg); #if defined(RASPBERRY_PI_PICO_PLATFORM) -#if defined(CPI_PICOCALC) +#if defined(PLATFORM_PICOCALC) if(!SD.begin(SDCARD_SS_PIN, (uint32_t) SPI_HALF_SPEED, SPI)){ error2(PSTR("failed to init SD card")); @@ -1728,7 +1728,7 @@ loadimage(object *arg) { #if defined(sdcardsupport) #if defined(RASPBERRY_PI_PICO_PLATFORM) -#if defined(CPI_PICOCALC) +#if defined(PLATFORM_PICOCALC) if(!SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI)){ error2(PSTR("failed to init SD card")); return 0; @@ -1743,11 +1743,16 @@ loadimage(object *arg) File file; if (stringp(arg)) { char buffer[BUFFERSIZE]; + file = SD.open(MakeFilename(arg, buffer)); if (!file) { error2("problem loading from SD card or invalid filename"); } } else if (arg == NULL) { + if (!SD.exists("/ULISP.IMG")) { + error2("workspace image not found"); + } + file = SD.open("/ULISP.IMG"); if (!file) { error2("problem loading from SD card"); @@ -1868,7 +1873,7 @@ autorunimage() { #if defined(sdcardsupport) #if defined(RASPBERRY_PI_PICO_PLATFORM) -#if defined(CPI_PICOCALC) +#if defined(PLATFORM_PICOCALC) if(!SD.begin(SDCARD_SS_PIN, (uint32_t) SPI_HALF_SPEED, SPI)){ error2(PSTR("failed to init SD card")); return; @@ -3587,7 +3592,7 @@ void serialbegin (int address, int baud) { if (address == 1) Serial1.begin((long)baud*100); else if (address == 2) Serial2.begin((long)baud*100); #elif defined(ULISP_SERIAL1) - #if defined(CPI_PICOCALC) + #if defined(PLATFORM_PICOCALC) // Waveshare uses Serial1 for default. if (address == 1) {} #else @@ -3609,7 +3614,7 @@ void serialend (int address) { if (address == 1) {Serial1.flush(); Serial1.end(); } else if (address == 2) {Serial2.flush(); Serial2.end(); } #elif defined(ULISP_SERIAL1) - #if defined(CPI_PICOCALC) + #if defined(PLATFORM_PICOCALC) if (address == 1) {Serial1.flush();} #else if (address == 1) {Serial1.flush(); Serial1.end(); } @@ -4706,7 +4711,7 @@ object *sp_withsdcard (object *args, object *env) { if (!stringp(filename)) error("filename is not a string", filename); params = cdr(params); #if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) - #if defined(CPI_PICOCALC) + #if defined(PLATFORM_PICOCALC) if(!SD.begin(SDCARD_SS_PIN,(uint32_t) SPI_HALF_SPEED, SPI )){ error2(PSTR("failed to init SD card")); return nil; @@ -7837,30 +7842,6 @@ object *fn_invertdisplay (object *args, object *env) { return nil; } -char getKey () { - char temp; - - if (pc_kbd.keyCount() > 0) { - do { - const PCKeyboard::KeyEvent key = pc_kbd.keyEvent(); - if (key.state == PCKeyboard::StatePress) { - char temp = key.key; - if ((temp != 0) && (temp !=255) && (temp != 0xA1) && (temp != 0xA2) && (temp != 0xA3) && (temp != 0xA4) && (temp != 0xA5)) { - ProcessKey(temp); - } - } - } while ((temp == 0) || (temp ==255)); - if (temp == '@') temp = '~'; - if (temp == '_') temp = '\\'; - } - return temp; -} - -object *fn_getkey (object *args, object *env) { - (void) env, (void) args; - return character(getKey()); -} - // Built-in symbol names const char string0[] = "nil"; const char string1[] = "t"; @@ -8111,7 +8092,6 @@ const char string245[] = "invert-display"; const char string246[] = ":led-builtin"; const char string247[] = ":high"; const char string248[] = ":low"; -const char string262[] = "get-key"; #if defined(CPU_ATSAMD21) const char string249[] = ":input"; const char string250[] = ":input-pullup"; @@ -8864,8 +8844,6 @@ const char doc244[] = "(set-rotation option)\n" "Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3."; const char doc245[] = "(invert-display boolean)\n" "Mirror-images the display."; -const char doc262[] = "(get-key)\n" -"Get the next key from the keyboard."; // Built-in symbol lookup table const tbl_entry_t lookup_table[] = { @@ -9118,7 +9096,6 @@ const tbl_entry_t lookup_table[] = { { string246, (fn_ptr_type)LED_BUILTIN, 0, NULL }, { string247, (fn_ptr_type)HIGH, DIGITALWRITE, NULL }, { string248, (fn_ptr_type)LOW, DIGITALWRITE, NULL }, - { string262, fn_getkey, 0200, doc262 }, #if defined(CPU_ATSAMD21) { string249, (fn_ptr_type)INPUT, PINMODE, NULL }, { string250, (fn_ptr_type)INPUT_PULLUP, PINMODE, NULL }, @@ -10474,7 +10451,7 @@ initgfx() digitalWrite(34, HIGH); #elif defined(RASPBERRY_PI_PICO_PLATFORM) tft.init(); -#if defined(CPI_PICOCALC) +#if defined(PLATFORM_PICOCALC) tft.setRotation(0); tft.invertDisplay(1); #endif @@ -10505,7 +10482,7 @@ void setup () { } #if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2W) -#if defined(sdcardsupport) && defined(CPI_PICOCALC) +#if defined(sdcardsupport) && defined(PLATFORM_PICOCALC) // picocalc doesn't have touch screen pinMode(SDCARD_SS_PIN,OUTPUT); digitalWrite(SDCARD_SS_PIN,1); @@ -10521,7 +10498,6 @@ void setup () { initkybd(); #endif pfstring(PSTR("uLisp 4.7b "), pserial); pln(pserial); - loadimage(NULL); } // Read/Evaluate/Print loop diff --git a/ulisp-extensions.ino b/ulisp-extensions.ino index 3e9618f..95d0c56 100644 --- a/ulisp-extensions.ino +++ b/ulisp-extensions.ino @@ -22,7 +22,61 @@ bcd_to_dec(uint8_t n) return ((n>>4) * 10) + (n&0x0f); } -// Definitions +/* + * 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(n)); + return number(static_cast(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(n)); + return number(static_cast(result)); +} + object * fn_now(object *args, object *env) { @@ -130,6 +184,27 @@ fn_sym_def(object *args, object *env) 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) { @@ -146,35 +221,190 @@ fn_lambdap(object *arg, object *env) 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_backlight(object *args, object *env) +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 (arg != nil) { - if (!(floatp(arg))) { - error2(notanumber); - } - - pc_kbd.setBacklight(arg->single_float); + if (!SD.exists(MakeFilename(arg, buffer))) { + return nil; } - float level = pc_kbd.backlight(); - - return makefloat(level); + 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; } -// Symbol names -const char stringlambdap[] PROGMEM = "lambdap"; -const char stringnow[] PROGMEM = "now"; -const char string_sym_def[] PROGMEM = "symbol-def"; -const char string_backlight[] PROGMEM = "backlight"; +/* + (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; -// Documentation strings + 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."; @@ -183,18 +413,46 @@ 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 docpform[] PROGMEM = "(pform form str)\n" -"Print a form to a stream in a manner suitable for writing to storage."; - 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_backlight[] PROGMEM = "(backlight level)\n" -"If level is nil, return the current backlight level. Otherwise, set" -"the backlight to the level. Returns the current backlight level."; +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 @@ -202,7 +460,19 @@ 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 }, - { string_backlight, fn_backlight, 0201, doc_backlight }, + { 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