LambLisp 01 Red Fox Alpha
From AI to Actuator
Loading...
Searching...
No Matches
ll_platform_generic.h
1// Copyright 2026 by Frobenius Norm LLC 2026-05-16
2// Free for non-commercial use. Commercial use requires a license.
3#ifndef LL_PLATFORM_GENERIC_H
4#define LL_PLATFORM_GENERIC_H
5
6#include <stdio.h>
7#include <stdint.h>
8#include <stdarg.h>
9#include <setjmp.h>
10#include <type_traits>
11#include <new>
12#include "assert.h"
13#include "unistd.h"
14
15// Default all LL_ feature flags to 0 so they can be used as C++ values, not just #if guards.
17#ifndef LL_ESP_ARDUINO
18#define LL_ESP_ARDUINO 0
19#endif
20
21#ifndef LL_ARDUINO
22#define LL_ARDUINO 0
23#endif
24
25#ifndef LL_POSIX
26#define LL_POSIX 0
27#endif
28
32#ifndef LL_WASM
33#define LL_WASM 0
34#endif
35#ifndef LL_WASI
36#define LL_WASI 0
37#endif
38#ifndef LL_EMSCRIPTEN
39#define LL_EMSCRIPTEN 0
40#endif
41
44#ifndef LL_WINDOWS
45#define LL_WINDOWS 0
46#endif
47#ifndef LL_WIFI
48#define LL_WIFI 0
49#endif
50#ifndef LL_LITTLEFS
51#define LL_LITTLEFS 0
52#endif
53
54#ifndef LL_GCCHK_VISITED_GROWBY
55 #if LL_ESP32
56 #define LL_GCCHK_VISITED_GROWBY 4096
57 #define LL_GCCHK_STACK_INIT 256
58 #define LL_GCCHK_STACK_GROWBY 8
59 #define LL_GCCHK_STACK_CAP 8192
60 #define LL_GCCHK_VECSTACK_INIT 64
61 #define LL_GCCHK_VECSTACK_GROWBY 8
62 #define LL_GCCHK_VECSTACK_CAP 1024
63 #else
64 #define LL_GCCHK_VISITED_GROWBY (-1)
65 #define LL_GCCHK_STACK_INIT 256
66 #define LL_GCCHK_STACK_GROWBY 256
67 #define LL_GCCHK_STACK_CAP (1<<20)
68 #define LL_GCCHK_VECSTACK_INIT 64
69 #define LL_GCCHK_VECSTACK_GROWBY 64
70 #define LL_GCCHK_VECSTACK_CAP (1<<16)
71 #endif
72#endif
73
74#ifndef LL_EVAL_STACK_BUDGET
75 #if LL_WASM
81 #define LL_EVAL_STACK_BUDGET (3UL * 1024 * 1024)
82 #elif LL_POSIX
83 #define LL_EVAL_STACK_BUDGET (6UL * 1024 * 1024)
84 #else
85 #define LL_EVAL_STACK_BUDGET (40UL * 1024)
86 #endif
87#endif
88
97#ifndef LL_EVAL_STACK_BUDGET_MIN
98#define LL_EVAL_STACK_BUDGET_MIN (16UL * 1024)
99#endif
100
104#ifndef LL_XTENSA
105#define LL_XTENSA 0
106#endif
107#ifndef LL_RISCV32
108#define LL_RISCV32 0
109#endif
110
112#ifndef LL_NCG_DIRAM_POOL
113 #if LL_ESP32 && LL_XTENSA
114 #define LL_NCG_DIRAM_POOL 1
115 #else
116 #define LL_NCG_DIRAM_POOL 0
117 #endif
118#endif
119
120#ifndef LL_NCG_RISCV_POOL
121 #if LL_RISCV32 && ((defined(LL_C5_PMP_RWX) && LL_C5_PMP_RWX) || \
122 (defined(LL_C5_NCG_ARENA) && LL_C5_NCG_ARENA))
123 #define LL_NCG_RISCV_POOL 1
124 #else
125 #define LL_NCG_RISCV_POOL 0
126 #endif
127#endif
128
130#ifndef LL_NCG_EXEC_FLOOR_DEFAULT
131 #define LL_NCG_EXEC_FLOOR_DEFAULT 24576
132#endif
133
134#ifndef LL_NCG_PSRAM_EXEC
135 #if defined(BOARD_HAS_PSRAM) && (LL_ESP32S3 || LL_RISCV32)
136 #define LL_NCG_PSRAM_EXEC 1
137 #else
138 #define LL_NCG_PSRAM_EXEC 0
139 #endif
140#endif
141
143#ifndef LL_EXTRAM_SEPARATE_IBUS
144 #if LL_ESP32 && LL_XTENSA
145 #define LL_EXTRAM_SEPARATE_IBUS 1
146 #else
147 #define LL_EXTRAM_SEPARATE_IBUS 0
148 #endif
149#endif
150
163#ifndef LL_NCG_PSRAM_ALIAS
164 #if LL_NCG_PSRAM_EXEC && LL_ESP32S3
165 #define LL_NCG_PSRAM_ALIAS 1
166 #else
167 #define LL_NCG_PSRAM_ALIAS 0
168 #endif
169#endif
170
173#ifndef LL_WIFI_RESERVE_BYTES
174 #define LL_WIFI_RESERVE_BYTES (48UL * 1024)
175#endif
176
186#ifndef LL_GC_BUDGET_WHEN_MAXED
187#define LL_GC_BUDGET_WHEN_MAXED 0
188#endif
189
190#ifndef LL_NCG_EXEC_POOL_BYTES
191 #if LL_NCG_RISCV_POOL
192 #define LL_NCG_EXEC_POOL_BYTES (32UL * 1024)
193 #elif LL_ESP32S3
194 #define LL_NCG_EXEC_POOL_BYTES (32UL * 1024)
195 #elif LL_ESP32
197 #define LL_NCG_EXEC_POOL_BYTES (32UL * 1024)
198 #else
199 #define LL_NCG_EXEC_POOL_BYTES (32UL * 1024)
200 #endif
201#endif
202
204#ifndef LL_ROOTSTACK_MAX
205 #if LL_WASM
207 #define LL_ROOTSTACK_MAX 131072
208 #elif LL_POSIX
209 #define LL_ROOTSTACK_MAX 262144
210 #else
211 #define LL_ROOTSTACK_MAX 65536
212 #endif
213#endif
214
217#ifndef LL_ROOTSTACK_SLACK
218#define LL_ROOTSTACK_SLACK 256
219
232#ifndef LL_ROOTSTACK_INIT
233 #define LL_ROOTSTACK_INIT 256
234#endif
235#ifndef LL_MARKSTACK_INIT
236 #define LL_MARKSTACK_INIT 256
237#endif
238#endif
239
241#ifndef LL_EVAL_ROOT_DEPTH_MAX
242#define LL_EVAL_ROOT_DEPTH_MAX (LL_ROOTSTACK_MAX - 536)
243#endif
244
258
283#ifndef LL_ROOT_HIGH_WATER
284 #if LL_POSIX
285 #define LL_ROOT_HIGH_WATER 1
286 #else
287 #define LL_ROOT_HIGH_WATER 0
288 #endif
289#endif
290
317#ifndef LL_ROOT_BALANCE
318 #if LL_POSIX
319 #define LL_ROOT_BALANCE 1
320 #else
321 #define LL_ROOT_BALANCE 0
322 #endif
323#endif
338#ifndef LL_HAVE_BACKTRACE
339 #if defined(__GLIBC__) && !LL_ESP32
340 #define LL_HAVE_BACKTRACE 1
341 #else
342 #define LL_HAVE_BACKTRACE 0
343 #endif
344#endif
345
346#ifndef LL_I2C
347#define LL_I2C 0
348#endif
349#ifndef LL_COMMONIO
350#define LL_COMMONIO 0
351#endif
352#ifndef LL_ESP32
353#define LL_ESP32 0
354#endif
355#ifndef LL_COMPLEX
356#define LL_COMPLEX 0
357#endif
358#ifndef LL_RATIONAL
359#define LL_RATIONAL 0
360#endif
361#ifndef LL_BIGNUM
362#define LL_BIGNUM 0
363#endif
364#ifndef LL_BIGNUM_LIMB_BITS
365#define LL_BIGNUM_LIMB_BITS 32
366#endif
368#ifndef LL_BIGNUM_STRICT_RT
369#define LL_BIGNUM_STRICT_RT 0
370#endif
371
372#ifndef LL_MODBUS
373#define LL_MODBUS 0
374#endif
375#ifndef LL_PROFIBUS
376#define LL_PROFIBUS 0
377#endif
378#ifndef LL_PROFINET
379#define LL_PROFINET 0
380#endif
381#ifndef LL_CUDA
382#define LL_CUDA 0
383#endif
384#ifndef LL_HIP
385#define LL_HIP 0
386#endif
387
388// Board-specific flags (set by platformio.ini build_flags per env)
389#ifndef LL_ESP32_S3_DEVKIT_C
390#define LL_ESP32_S3_DEVKIT_C 0
391#endif
392#ifndef LL_ESP32S3_N8R2
393#define LL_ESP32S3_N8R2 0
394#endif
395#ifndef LL_Freenove_4WD_Car_Kit_ESP32
396#define LL_Freenove_4WD_Car_Kit_ESP32 0
397#endif
398#ifndef LL_Freenove_ESP32_WROVER
399#define LL_Freenove_ESP32_WROVER 0
400#endif
401#ifndef LL_ESP32_N4R4
402#define LL_ESP32_N4R4 0
403#endif
404#ifndef LL_AMD64
405#define LL_AMD64 0
406#endif
407#ifndef LL_X86_64
408#define LL_X86_64 0
409#endif
410#ifndef LL_AARCH64
411#define LL_AARCH64 0
412#endif
413#ifndef LL_ARM64
414#define LL_ARM64 0
415#endif
416#ifndef LL_WASM32
417#define LL_WASM32 0
418#endif
419
435#ifndef LL_NETWORK
436 #if LL_WASM
437 #define LL_NETWORK 0
438 #else
439 #define LL_NETWORK ((LL_WIFI) || (LL_POSIX))
440 #endif
441#endif
444#define LL_POSIX_NET ((LL_POSIX) && (LL_NETWORK))
445
446#ifndef LL_TERMIOS
447 #if LL_WASM
448 #define LL_TERMIOS 0
449 #else
450 #define LL_TERMIOS (LL_POSIX)
451 #endif
452#endif
453
460#ifndef LL_NCG
461 #if LL_WASM
462 #define LL_NCG 0
463 #else
464 #define LL_NCG 1
465 #endif
466#endif
467
542#ifndef LL_AUTOCOMPILE
543 #define LL_AUTOCOMPILE 1
544#endif
545
546
574#ifndef LL_XTENSA
575#define LL_XTENSA 0
576#endif
577#ifndef LL_RISCV32
578#define LL_RISCV32 0
579#endif
580
581
584#if LL_ESP_ARDUINO
585#include "Arduino.h"
586#endif
587
591#if LL_ARDUINO
592#include <string.h>
593
594unsigned long millis();
595unsigned long micros();
609void delay_ms(unsigned long ms);
610
611static void delay(unsigned long ms) { delay_ms(ms); }
612
618#ifndef HIGH
619#define HIGH 1
620#define LOW 0
621#endif
622#ifndef INPUT
623#define INPUT 0x0
624#define OUTPUT 0x1
625#define INPUT_PULLUP 0x2
626#endif
627
628void pinMode(int pin, int mode);
629void digitalWrite(int pin, int val);
630int digitalRead(int pin);
631void analogWrite(int pin, int val);
632int analogRead(int pin);
633void tone(int pin, unsigned int freq);
634void tone(int pin, unsigned int freq, unsigned long dur);
635void noTone(int pin);
636void delayMicroseconds(unsigned long us);
637
640int ll_llarduino_pin_mode(int pin);
641int ll_llarduino_pin_duty(int pin);
642int ll_llarduino_pin_tone(int pin);
643int ll_llarduino_npins();
644const char *ll_llarduino_backend();
645
646typedef uint8_t byte;
647
698class LL_String {
699public:
700
701 LL_String() { _init(); set(""); }
702 LL_String(const char *other) { _init(); set(other); }
703 LL_String(const char *other, int n) { _init(); set(other, n); }
704 LL_String(const LL_String &other) { _init(); set(other.ptr, other.len); }
705
706 ~LL_String() { if (ptr) { delete[] ptr; ptr = 0; } len = cap = 0; }
707
708 void set(const char *s) { set(s, s ? (int) strlen(s) : 0); }
709
710 void set(const char *s, int n) {
711 if (!s || n <= 0) { _ensure(0); ptr[0] = '\0'; len = 0; return; }
712 if (ptr && s >= ptr && s <= ptr + len) {
713 const int off = (int) (s - ptr);
714 memmove(ptr, ptr + off, (size_t) n);
715 }
716 else {
717 _ensure(n);
718 memmove(ptr, s, (size_t) n);
719 }
720 ptr[n] = '\0';
721 len = n;
722 }
723
724 LL_String concat(const char *s1, const char *s2) {
725 LL_String r;
726 const int n1 = s1 ? (int) strlen(s1) : 0;
727 const int n2 = s2 ? (int) strlen(s2) : 0;
728 r._ensure(n1 + n2);
729 if (n1) memmove(r.ptr, s1, (size_t) n1);
730 if (n2) memmove(r.ptr + n1, s2, (size_t) n2);
731 r.ptr[n1 + n2] = '\0';
732 r.len = n1 + n2;
733 return r;
734 }
735
736 LL_String &append(const char *s1) {
737 if (s1 && *s1) {
738 const int n1 = (int) strlen(s1);
739 const bool self = (ptr && s1 >= ptr && s1 <= ptr + len);
740 const int off = self ? (int) (s1 - ptr) : 0;
741 _ensure(len + n1);
742 memmove(ptr + len, self ? ptr + off : s1, (size_t) n1);
743 len += n1;
744 ptr[len] = '\0';
745 }
746 return *this;
747 }
748
751 LL_String &append(char c) {
752 _ensure(len + 1);
753 ptr[len++] = c;
754 ptr[len] = '\0';
755 return *this;
756 }
757
758 LL_String substring(int start, int len_) { return LL_String(&(ptr[start]), len_); }
759
760 LL_String &operator=(const char *other) { set(other); return *this; }
761 LL_String &operator=(const LL_String &other) { if (this != &other) set(other.ptr, other.len); return *this; }
762
763 bool operator==(const char *other) { return strcmp(ptr, other) == 0; }
764 bool operator!=(const char *other) { return strcmp(ptr, other) != 0; }
765 bool operator==(const LL_String &other) { return strcmp(ptr, other.ptr) == 0; }
766 bool operator!=(const LL_String &other) { return strcmp(ptr, other.ptr) != 0; }
767 char &operator[](int ix) { return ptr[ix]; }
768
769 LL_String operator+(const char *other) { return concat(ptr, other); }
770 LL_String operator+(const LL_String &other) { return concat(ptr, other.ptr); }
771 LL_String operator+(char c) { char cstr[2]; cstr[0] = c; cstr[1] = '\0'; return concat(ptr, cstr); }
772
773 LL_String &operator+=(const char *other) { return append(other); }
774 LL_String &operator+=(const LL_String &other) { return append(other.ptr); }
775 LL_String &operator+=(char c) { return append(c); }
776
777 char *c_str() const { return ptr; }
778 int length() const { return len; }
779
780private:
781 void _init() { ptr = 0; len = 0; cap = 0; }
782
785 void _ensure(int need) {
786 if (cap >= need + 1) return;
787 int ncap = cap ? cap : 16;
788 while (ncap < need + 1) ncap += ncap;
790 char *np = new char[ncap];
791 if (ptr) { memmove(np, ptr, (size_t) len + 1); delete[] ptr; }
792 else { np[0] = '\0'; }
793 ptr = np;
794 cap = ncap;
795 }
796
797 char *ptr;
798 int len;
799 int cap;
800};
801
802typedef LL_String String;
803#endif
804
807#define NOTUSED __attribute__((__unused__))
808#define INLINE __attribute__((__inline__))
809#define NOINLINE __attribute__((__noinline__))
810#define CHECKPRINTF __attribute__((format(printf, 1, 2)))
811#define CHECKPRINTF_pos2 __attribute__((format(printf, 2, 3)))
812
817
825#if LL_ARDUINO
839class Print {
840public:
841 virtual ~Print() {}
842 virtual size_t write(uint8_t c) = 0;
843 virtual size_t write(const uint8_t *buf, size_t n);
844 size_t write(const char *s);
845 size_t print(char c);
846 size_t print(const char *s);
847 size_t print(const String &s);
848 size_t print(int n, int base = 10);
849 size_t print(long n, int base = 10);
850 size_t print(unsigned long n, int base = 10);
851 size_t print(double v, int digits = 2);
852 size_t println();
853 size_t println(char c);
854 size_t println(const char *s);
855 size_t println(const String &s);
856 size_t println(int n, int base = 10);
857 size_t println(long n, int base = 10);
858 size_t println(unsigned long n, int base = 10);
859 size_t println(double v, int digits = 2);
860 size_t printf(const char *fmt, ...) CHECKPRINTF_pos2;
861};
862
863class Stream : public Print {
864public:
865 virtual int available() = 0;
866 virtual int read() = 0;
867 virtual int peek() = 0;
868 virtual void flush() {}
869};
870
873class LLArduinoSerial : public Stream {
874 int _peeked;
875public:
876 LLArduinoSerial() : _peeked(-1) {}
877 void begin(unsigned long baud = 115200);
878 void end();
879 int available() override;
880 int availableForWrite();
881 int read() override;
882 int peek() override;
883 void flush() override;
884 size_t write(uint8_t c) override;
885 using Print::write;
886};
887
888extern LLArduinoSerial Serial;
889
890
891#endif // LL_ARDUINO -- Print/Stream/Serial
892
896#if LL_ARDUINO || LL_ESP_ARDUINO
897const char *ll_llarduino_fmt_int(long v, int base);
898const char *ll_llarduino_fmt_real(double v, int digits);
899const char *ll_llarduino_eol();
900#endif
901
902#define CHECKPRINTF_pos3 __attribute__((format(printf, 3, 4)))
903#define CHECKPRINTF_pos4 __attribute__((format(printf, 4, 5)))
904
905// On Arduino/ESP32, IRAM_ATTR and DRAM_ATTR are already defined by Arduino.h (esp_attr.h).
906// On other platforms (Linux, desktop simulation) define them as empty so the same source compiles.
907#if !defined(IRAM_ATTR)
908#define IRAM_ATTR
909#endif
910#if !defined(DRAM_ATTR)
911#define DRAM_ATTR
912#endif
913
916// [B410, B389, B396, P208] the DRAM budget behind this number
951#ifndef LL_STDIO_RX_BUFSIZE
952#define LL_STDIO_RX_BUFSIZE 0
953#endif
954
955const unsigned long toString_MAX_LENGTH = 8192;
958const unsigned long LL_ERRFORM_MAX = 400;
965const unsigned long LL_ENVFRAME_MAX = 32;
982const unsigned long LL_PRINT_LIST_MAX = 4096;
983String toString(const char *fmt, ...) CHECKPRINTF;
985
986
987#define ME(_me_) NOTUSED const char me[] = _me_
988#define ll_isdef(sym) (#sym[0])
989
990void global_printf(const char *fmt, ...);
991
1006void global_spill_begin();
1007
1021void global_term_quiet(bool echo, bool logs);
1022
1033#if LL_EMSCRIPTEN
1034void global_term_push_line(const char *s);
1035#endif
1036
1038
1057
1058typedef unsigned char Byte_t;
1059typedef bool Bool_t;
1069typedef char Char_t;
1070typedef int32_t ll_codepoint_t;
1071
1072typedef Char_t const *Charst_t;
1073typedef Byte_t const *Bytest_t;
1074typedef Char_t *CharVec_t;
1075typedef Byte_t *ByteVec_t;
1076
1094void *ll_payload_alloc_bytes(size_t nbytes);
1095void ll_payload_free(void *p);
1096
1098template <typename T> static inline T *ll_payload_new(size_t n)
1099{
1100 return (T *) ll_payload_alloc_bytes(n * sizeof(T));
1101}
1103
1136void *ll_dram_alloc_bytes(size_t nbytes);
1137void ll_dram_free(void *p);
1139
1162typedef uintptr_t Word_t;
1163typedef void *Ptr_t;
1164static_assert(sizeof(Word_t) >= sizeof(void *),
1165 "Word_t must hold a pointer -- see the P174 note above; LLP64 (Windows x64) breaks "
1166 "the old `unsigned long` spelling because long is 32 bits there and pointers are 64.");
1167
1168typedef int32_t LL_int32;
1169
1178
1181inline int ll_u8_seqlen(unsigned char b0)
1182{
1183 if (b0 < 0x80) return 1;
1184 if ((b0 & 0xE0) == 0xC0) return 2;
1185 if ((b0 & 0xF0) == 0xE0) return 3;
1186 if ((b0 & 0xF8) == 0xF0) return 4;
1187 return 1;
1188}
1189
1192inline ll_codepoint_t ll_u8_decode(const char *p, int *nb)
1193{
1194 unsigned char b0 = (unsigned char) p[0];
1195 int n = ll_u8_seqlen(b0);
1196 if (nb) *nb = n;
1197 if (n == 1) return (ll_codepoint_t) b0;
1198 ll_codepoint_t cp = b0 & (0xFF >> (n + 1));
1199 for (int i = 1; i < n; i++) {
1200 unsigned char c = (unsigned char) p[i];
1201 if ((c & 0xC0) != 0x80) { if (nb) *nb = 1; return (ll_codepoint_t) b0; }
1202 cp = (cp << 6) | (c & 0x3F);
1203 }
1204 return cp;
1205}
1206
1208inline int ll_u8_encode(ll_codepoint_t cp, char *out)
1209{
1210 if (cp < 0x80) { out[0] = (char) cp; return 1; }
1211 if (cp < 0x800) { out[0] = (char)(0xC0 | (cp >> 6)); out[1] = (char)(0x80 | (cp & 0x3F)); return 2; }
1212 if (cp < 0x10000) { out[0] = (char)(0xE0 | (cp >> 12)); out[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
1213 out[2] = (char)(0x80 | (cp & 0x3F)); return 3; }
1214 out[0] = (char)(0xF0 | (cp >> 18)); out[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
1215 out[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); out[3] = (char)(0x80 | (cp & 0x3F)); return 4;
1216}
1217
1219inline LL_int32 ll_u8_strlen(const char *s)
1220{
1221 LL_int32 n = 0;
1222 for (const char *p = s; *p; n++) p += ll_u8_seqlen((unsigned char) *p);
1223 return n;
1224}
1225
1232inline LL_int32 ll_u8_strlen_n(const char *s, LL_int32 nbytes)
1233{
1234 LL_int32 n = 0;
1235 for (LL_int32 i = 0; i < nbytes; n++) i += ll_u8_seqlen((unsigned char) s[i]);
1236 return n;
1237}
1238
1241inline const char *ll_u8_index_n(const char *s, LL_int32 nbytes, LL_int32 k)
1242{
1243 LL_int32 i = 0;
1244 for (LL_int32 c = 0; c < k; c++) {
1245 if (i >= nbytes) return 0;
1246 i += ll_u8_seqlen((unsigned char) s[i]);
1247 }
1248 return (i <= nbytes) ? (s + i) : 0;
1249}
1250
1253inline const char *ll_u8_index(const char *s, LL_int32 k)
1254{
1255 const char *p = s;
1256 for (LL_int32 i = 0; i < k; i++) {
1257 if (!*p) return 0;
1258 p += ll_u8_seqlen((unsigned char) *p);
1259 }
1260 return p;
1261}
1263
1264typedef int64_t LL_int64;
1265typedef float LL_float32;
1266typedef double LL_float64;
1267
1268static_assert(sizeof(Word_t) == sizeof(Ptr_t), "Word_t size must be == Ptr_t size\n");
1269static_assert(sizeof(Word_t) >= sizeof(LL_int32), "Word_t size must be >= LL_int32 size\n");
1270static_assert(sizeof(LL_float32) == 4, "LL_float32 must be 4 bytes\n");
1271static_assert(sizeof(LL_float64) == 8, "LL_float64 must be 8 bytes\n");
1273
1274//The AsciiConverter class mitigates printf formatting problems caused by different int sizes on different processors, when using printf-style format string (%u, %d %lu %ld etc).
1275class AsciiConverter {
1276public:
1277
1278 char *dec(Word_t n) {
1279 char *ptr = slot_end();
1280
1281 *ptr = '\0';
1282 do {
1283 Word_t d = n % 10;
1284 n /= 10;
1285 char c = d + '0';
1286 *--ptr = c;
1287 } while (n);
1288 return ptr;
1289 }
1290
1291 char *dec(LL_int32 n) {
1292 if (n >= 0) return dec((Word_t) n);
1293 if (n == INT32_MIN) return dec((LL_int64) n);
1294 char *ptr = dec(-n);
1295 *--ptr = '-';
1296 return ptr;
1297 }
1298
1313 template <typename T>
1314 typename std::enable_if<std::is_same<T, int>::value && !std::is_same<LL_int32, int>::value,
1315 char *>::type
1316 dec(T n) { return dec((LL_int32) n); }
1317
1318 char *dec(LL_int64 n) {
1319 // Full 64-bit signed decimal; avoids conflict with dec(Word_t) on 64-bit platforms.
1320 char *ptr = slot_end();
1321 bool neg = (n < 0);
1322 uint64_t uv = neg ? (n == INT64_MIN ? (uint64_t) 9223372036854775808ULL : (uint64_t) -n)
1323 : (uint64_t) n;
1324 do { *--ptr = (char)('0' + (uv % 10)); uv /= 10; } while (uv);
1325 if (neg) *--ptr = '-';
1326 return ptr;
1327 }
1328
1329 char *hex(Word_t n) {
1330 char *ptr = slot_end();
1331 LL_int32 nibs = sizeof(Word_t) * 2;
1332
1333 *ptr = '\0';
1334 while (nibs--) {
1335 Word_t d = n & 0x0f;
1336 n >>= 4;
1337 char c = (d < 10) ? (d + '0') : (d - 10 + 'a');
1338 *--ptr = c;
1339 }
1340 return ptr;
1341
1342 }
1343
1344 char *dec(LL_float32 n) {
1345 char *b = slot_base();
1346 snprintf(b, SLOT, "%f", (LL_float64) n);
1347 return b;
1348 }
1349
1350private:
1351 // [P238 Ph1]
1385 static const LL_int32 SLOTS = 16;
1386 static const LL_int32 SLOT = 64;
1387 static_assert(SLOT >= 3 + 20, "dec(LL_int64): sign + 20 digits + NUL");
1388 static_assert(SLOT >= 2 + sizeof(Word_t) * 2, "hex(Word_t): nibbles + NUL");
1389 static_assert(SLOT >= 48, "dec(LL_float32): %f of 3.4e38 needs 47 chars");
1390 char buf[SLOTS][SLOT];
1391 LL_int32 next = 0;
1392 char *slot_base() { next = (next + 1) % SLOTS; return buf[next]; }
1393 char *slot_end() { char *b = slot_base(); b[SLOT - 1] = '\0'; return &b[SLOT - 1]; }
1394};
1395
1396extern AsciiConverter ascii;
1397
1398class LambPlatform {
1399public:
1400
1403 LambPlatform() {}
1404 ~LambPlatform() { end(); }
1405
1406 void begin(void);
1407 void loop(void);
1408 void end(void);
1409
1410 void reboot(void);
1411
1412 const char *name();
1413 void identification(void);
1414
1415 LL_int32 free_heap();
1423 // [B508, B609, B676] why these four exist separately from free_heap()
1424 LL_int32 free_internal();
1425 LL_int32 free_dma();
1426 LL_int32 largest_free_internal();
1427 LL_int32 largest_free_dma();
1428 LL_int32 cell_pool_free();
1429
1430 // [B593] why the pool query was abstracted here
1448 void *cell_block_alloc(unsigned long nbytes);
1449 void cell_block_free(void *blk);
1450
1451 // [B593, B117]
1459 const char *mem_placement(void *p);
1460
1461 // [B627] the absence this records, and the write-cost measurement
1472 LL_int64 fs_total();
1473 LL_int64 fs_used();
1474 LL_int32 free_stack();
1475 Bool_t heap_integrity_check(Bool_t complain=false);
1476
1478 LL_float32 rand11() {
1479 const int max_int = (~((unsigned int) 0)) >> 1;
1480 const int min_int = -max_int - 1;
1481 const LL_float32 min = (LL_float32) min_int;
1482
1483 LL_int32 n;
1484 rand((byte *) &n, sizeof(n));
1485 return (n / min);
1486 }
1487
1488 LL_float32 rand01() { return (rand11() + 1.0) / 2.0; }
1489
1490 void rand(byte *buf, LL_int32 len);
1491 void rand11(LL_float32 *buf, LL_int32 n) { while (n--) *buf++ = rand11(); }
1492 void rand01(LL_float32 *buf, LL_int32 n) { while (n--) *buf++ = rand01(); }
1493
1494 LL_int32 loop_elapsed_ms() { return millis() - loop_start_ms; }
1495 LL_int32 loop_elapsed_us() { return micros() - loop_start_us; }
1496
1498
1499private:
1500 LL_int32 loop_start_ms;
1501 LL_int32 loop_start_us;
1502};
1503
1504extern LambPlatform lambPlatform;
1505
1508#if LL_LITTLEFS
1509#include "LittleFS.h"
1510typedef File File_Native;
1511#endif
1512
1513#if LL_POSIX
1514#include <stdio.h>
1515typedef FILE* File_Native;
1516#endif
1518
1529class LL_File {
1530public:
1531
1532 LL_File();
1533 ~LL_File();
1534
1535 bool isOpen() { return _path != ""; }
1536 int read(void);
1537 int write(byte b);
1538 int seek(unsigned long target, int whence=SEEK_SET);
1539 int tell();
1540 int size();
1541 int close();
1542
1543 int read(byte *b, int n) {
1544 LL_int32 nread = 0;
1545 while (n--) {
1546 int ch = read();
1547 if (ch == EOF) return nread;
1548 b[nread++] = ch;
1549 }
1550 return nread;
1551 }
1552
1553 int read(char *s, int n) { return read((byte *) s, n); }
1554 int write(const byte *b, int n) { while (n--) write(*b++); return n; }
1555 int write(const char *s, int n) { while (n--) write((byte) *s++); return n; }
1556 int peek();
1557
1558 File_Native _theFile;
1559 String _path;
1560 String _mode;
1561
1562private:
1563
1564};
1565
1566
1567// [P211 Ph1] and [B396], [B88], [B60], [B428], [B411], [B662] for the claims that collided
1595bool ll_fastram_reserve(const char *tag, size_t bytes);
1596
1601
1618bool ll_fastram_reserve_pinned(const char *tag, size_t bytes);
1619
1621void ll_fastram_release(const char *tag);
1622
1624void ll_fastram_release_unclaimed();
1626
1633
1634// Declarations lived in ll_platform_Windows.h until 2026-10-06; the implementation file is
1635// unchanged and is still the only place Win32 is named. [P264] [P174]
1636#if LL_WINDOWS
1637
1648size_t ll_win_self_exe_path(char *buf, size_t cap);
1649
1657int ll_win_mkdir(const char *path, int mode);
1658
1672int ll_win_set_system_time(long long utc_seconds);
1673
1674#endif // LL_WINDOWS
1675
1677
1699
1700// The interface lived in ll_panel.h until 2026-10-06; every LambLisp element now takes it
1701// from the generic platform. It was extracted from the camera driver before that. [P264] [P231]
1702class Lamb; // the operations below take `Lamb &`; function pointers need only an incomplete type
1703
1711#ifndef LL_PANEL_ST7789_EYE
1712 #define LL_PANEL_ST7789_EYE 0 // was documented as env-set while nothing set it [B557]
1713#endif
1714
1720#ifndef LL_PANEL_ST7701_AITRIP
1721 #define LL_PANEL_ST7701_AITRIP 0
1722#endif
1723
1728#if LL_PANEL_ST7789_EYE || LL_PANEL_ST7701_AITRIP
1729 #define LL_PANEL 1
1730#else
1731 #define LL_PANEL 0
1732#endif
1733
1735enum LL_PanelModel {
1736 LL_PANEL_PUSH = 0,
1737 LL_PANEL_SCAN = 1
1738};
1739
1741struct LL_Panel {
1742 const char *name;
1743 int w, h;
1744 LL_PanelModel model;
1745 bool (*init)(Lamb &lamb);
1746 void (*fill)(Lamb &lamb, int x, int y, int w, int h, uint16_t color);
1747 void (*blit)(Lamb &lamb, int x, int y, int w, int h, const uint16_t *src);
1748 uint16_t *(*fb)(void);
1749 void (*set_brightness)(Lamb &lamb, int percent);
1750 void (*on_off)(Lamb &lamb, bool on);
1751};
1752
1754const LL_Panel *ll_panel_active(void);
1755
1757void ll_panel_register(const LL_Panel *p);
1758
1760bool ll_panel_ready(void);
1761
1766void ll_panel_blit_rgb565(Lamb &lamb, int x, int y, int w, int h, const uint16_t *src);
1767
1769
1777public:
1778 LL_File *open(const char *path, const char *mode);
1779 bool exists(const char *path);
1780 int rm(const char *path);
1781 int mv(const char *from, const char *to);
1782 int mkdir(const char *path);
1783
1784private:
1785};
1786
1787extern LL_File_System ll_file_system;
1788
1792#if LL_WIRE
1793#include "Wire.h"
1794extern TwoWire *LL_Wire;
1795#endif
1796
1797#if LL_WIFI
1798#include "WiFi.h"
1799#include "WiFiClientSecure.h"
1800extern WiFiClass *LL_WiFi;
1801#endif
1803
1811public:
1812 int setTxBufferSize(int n);
1813 int setRxBufferSize(int n);
1814
1821 bool set_input_callback(void (*cb)());
1822
1823 void begin(void) { begin(115200); }
1824 void begin(unsigned long baudrate);
1825 void end();
1826 int available(void);
1827 int availableForWrite(void);
1828 int read(void);
1829 int write(uint8_t c);
1830 int write(char c) { return write((uint8_t) c); }
1831
1832 void flush(void);
1833
1834 int read(byte *buf, int max) {
1835 int nread = 0;
1836 while (max--) {
1837 int b = read();
1838 if (b == EOF) break;
1839 *buf++ = b;
1840 }
1841 return nread;
1842 }
1843
1844 int read(char *s, int max) { return read((uint8_t *) s, max); }
1845 int write(const char *s, int n) { return write((uint8_t *) s, n); }
1846 int write(const byte *b, size_t n) { int i=n; while (i--) write(*b++); return n; }
1847 int write(const char *s) { int i=0; while (*s) { write(*s++); i++; } return i; }
1848
1849 operator bool() { return true; }
1850};
1851
1852extern LambStdioClass LambStdio;
1853
1854typedef byte uuid_t[16];
1855
1857#define ll_once(_once_something) do { \
1858 static bool _visited_ = false; \
1859 if (!_visited_) { \
1860 _visited_ = true; \
1861 { _once_something; } \
1862 } \
1863 } while (0) \
1864 //
1865//
1866
1868#define ll_every(_every_so_often_ms, _every_something_to_do) do { \
1869 static unsigned long _every_next = 0; \
1870 unsigned long _every_now = millis(); \
1871 if (_every_now >= _every_next) { \
1872 { _every_something_to_do; } \
1873 _every_next = _every_now + (_every_so_often_ms); \
1874 } \
1875 } while (0) \
1876 //
1877//
1878
1879/*
1880 The embedded debug catcher ensures that an address is available to be set as a BREAKPOINT for a
1881 hardware debugger. This is useful in cases where the generated code has been heavily inlined --
1882 a catch arm that is inlined away has no address to break on, and this gives every catch one.
1883 Undefine this symbol if not using a hardware debugger, or redefine it to point to a different
1884 breakpoint target.
1885
1886 IT IS NOT A NO-OP, WHICH MATTERS BECAUSE IT LOOKS LIKE ONE AT ALL TEN USE SITES. A bare
1887 `ll_debug_catcher;` in a catch arm reads as a leftover, and in `main.cpp`'s hand-written copies
1888 of `ll_catch_terminal` it reads as a stray statement. It calls `embedded_debug_catcher()`
1889 (`ll_vm_util.cpp:22`), which does:
1890
1891 ll_every(10000, { magenta("%s caught\n", me); ll_term.flush(); delay(10); });
1892
1893 `ll_every`'s argument is MILLISECONDS, not a call count -- so the common path is one `millis()`
1894 and a compare, and at most once per TEN SECONDS it prints a magenta `caught` line, flushes the
1895 terminal, and stalls 10 ms.
1896
1897 TWO CONSEQUENCES WORTH KNOWING. First, that 10 ms stall is on the FAULT path of SHIPPED
1898 firmware: nothing undefines this symbol anywhere in the tree despite the advice above, so a
1899 customer program that throws carries it. For a hard-real-time node that is a real, if bounded,
1900 cost on the error path -- the same class of finding as [B765], where every raise logged. Second,
1901 it is NOT the cause of [B839]'s 6.4-29 ms per caught raise: at one firing per 10 s against
1902 `guard-raise`'s 3.2 s for 500 raises it contributes about 10 ms to the whole benchmark, ~0.3%.
1903 Ruled out by arithmetic rather than left as a suspicion.
1904*/
1905void embedded_debug_catcher();
1906#define ll_debug_catcher embedded_debug_catcher()
1907//#define ll_debug_catcher ll_term.flush()
1908
1909// ── Settings file paths ──────────────────────────────────────────────────────
1910#if LL_LITTLEFS
1911#define LAMB_SETTINGS_PATH "/Settings-Lamb.scm"
1912#define SETTINGS_PATH "/Settings.scm"
1913#else
1914#define LAMB_SETTINGS_PATH "Settings-Lamb.scm"
1915#define SETTINGS_PATH "Settings.scm"
1916#endif
1917
1918// ── Pre-allocation settings ──────────────────────────────────────────────────
1925 LL_int32 cell_block_size = 8 * 1024;
1926 LL_int32 extension_block_size = 4 * 1024;
1933 LL_int32 gc_total_marked = 0;
1934 // [B610]
1942 LL_int32 gc_pool_target = 0;
1943 LL_int32 ncg_frame_pool_size = 16;
1956 LL_int32 sint_in_iram = 1;
1957 LL_int32 rootstack_in_iram = 1;
1963 LL_int32 va_list_max = (LL_int32) toString_MAX_LENGTH;
1964 // [B441] and [B417] for the keys it must preserve
1971 LL_int32 boot_pending = 0;
1972 void load();
1981 bool apply_key(const char *key, long val);
1982};
1983
1986void ll_pre_settings_write(LL_int32 boot_pending);
1988void ll_settings_boot_ok(void);
1989
1995extern LambPreSettings ll_pre_settings;
1996
2016extern LL_int32 ll_cell_block_size_effective;
2017
2024extern LL_int32 ll_va_list_max;
2025
2029#define ll_try try
2030
2044extern const void *ll_catch_last_err;
2045extern int ll_catch_repeats;
2046#define LL_CATCH_REPEAT_LIMIT 4
2047
2048// [B665] why bad_alloc is converted at the first handler
2069// [B765] the 25,000-line noise measurement behind this
2091#define LL_ERR_IS_USER_RAISE(__e__) \
2092 ((__e__)->type() == Cell::T_ERROR && strcmp((__e__)->error_get_chars(), "__raise__") == 0)
2093
2094/* WHAT `ll_catch` DOES AND DOES NOT DO -- read this before deciding where a frame may go.
2095 *
2096 * IT LOGS AND RETHROWS. It does NOT touch the rootstack. That is deliberate and it is NOT a
2097 * gap: the rootstack is unwound at the MOP3 BOUNDARY, by `mop3_catch`, which restores the depth
2098 * `mop3_try` saved on entry. An `ll_catch` frame between the throw and that boundary has nothing
2099 * to restore -- it did not save a depth, and restoring to its own would discard entries its
2100 * caller still owns.
2101 *
2102 * SO THE THREE HANDLERS DO THREE DIFFERENT JOBS:
2103 * `mop3_catch` APPLICATION error -- restore to the mop3 boundary so the REPL continues.
2104 * `ll_catch` intermediate frame -- log once, annotate, rethrow. Rootstack untouched.
2105 * `ll_catch_terminal` SYSTEM RESCUE -- something escaped everything; keep running. Also does
2106 * not restore, because by the time it is reached the mop3 boundaries have.
2107 *
2108 * MEASURED, 2026-10-13, because this was a standing uncertainty and reading could not settle it:
2109 * a 50,000-deep recursion pushed 181,439 of 261,608 root slots and threw; the next error at the
2110 * REPL reported `entry=1`, i.e. the depth was fully restored on the way out. Six other error
2111 * kinds all reported `entry=1 catch=1` at the terminal frame. That is the [B652] probe in
2112 * `main.cpp` answered: the rootstack is NOT leaked across an unwind.
2113 *
2114 * IF YOU NEED A RESTORE ON THIS PATH, pass it in: `ll_catch(gc_root_setdepth(saved))` with the
2115 * depth saved BEFORE the try. `ll_vm_rdr.cpp:279` and `ll_vm_mop3_extra.cpp:54` do exactly that,
2116 * and they are the right pattern for a frame whose cleanup cannot wait for the mop3 boundary. */
2117#define ll_catch(__code_before_rethrow__) \
2118 catch (std::bad_alloc &) { \
2119 Sexpr_t __err__ = NIL->mk_error("%s out of memory (C++ allocation failed)", me); \
2120 ll_catch_last_err = (const void *) __err__; \
2121 ll_catch_repeats = 1; \
2122 global_printf("\r[%d] %s ll_catch(): %s\n", millis(), me, __err__->error_get_chars()); \
2123 ll_debug_catcher; \
2124 __code_before_rethrow__; \
2125 throw __err__; \
2126 } \
2127 catch (Sexpr_t __err__) { \
2128 if (__err__->type() != Cell::T_ERROR) \
2129 throw NIL->mk_error("ll_catch() BUG in %s bad type %s", me, __err__->dump().c_str()); \
2130 \
2131 if (!LL_ERR_IS_USER_RAISE(__err__)) { \
2132 if ((const void *) __err__ != ll_catch_last_err) { \
2133 ll_catch_last_err = (const void *) __err__; \
2134 ll_catch_repeats = 0; \
2135 } \
2136 if (++ll_catch_repeats <= LL_CATCH_REPEAT_LIMIT) \
2137 global_printf("\r[%d] %s ll_catch(): %s\n", millis(), me, __err__->error_get_chars()); \
2138 else if (ll_catch_repeats == LL_CATCH_REPEAT_LIMIT + 1) \
2139 global_printf("\r[%d] %s ll_catch(): ... same error still unwinding; further frames suppressed\n", millis(), me); \
2140 } \
2141 \
2142 ll_debug_catcher; \
2143 __code_before_rethrow__; \
2144 throw __err__; \
2145 } \
2146 //
2147
2148#define ll_catch_terminal \
2149 catch (std::bad_alloc &) { \
2150 global_printf("\r[%d] %s ll_catch_terminal: out of memory (C++ allocation failed)\n", \
2151 millis(), me); \
2152 ll_debug_catcher; \
2153 } \
2154 catch (Sexpr_t __err__) { \
2155 /* [B765] inside a macro a // comment would swallow the \ continuation, so block form. */ \
2156 \
2159 if (LL_ERR_IS_USER_RAISE(__err__)) \
2160 global_printf("\r[%d] %s ll_catch_terminal: UNCAUGHT raise: %s\n", \
2161 millis(), me, __err__->error_get_irritants()->str().c_str()); \
2162 \
2164 else if (__err__->type() != Cell::T_ERROR) \
2165 global_printf("\r[%d] %s ll_catch_terminal: non-error type %d\n", \
2166 millis(), me, __err__->type()); \
2167 else \
2168 global_printf("\r[%d] %s ll_catch_terminal: %s\n", \
2169 millis(), me, __err__->error_get_chars()); \
2170 ll_debug_catcher; \
2171 } \
2172 //
2174
2186#ifdef __cplusplus
2187const char *const *ll_feature_names(int *count);
2188#endif
2189
2200#ifdef __cplusplus
2201bool ll_term_ansi(void);
2202void ll_term_ansi_set(bool on);
2203bool ll_term_ansi_detect(void);
2204#if LL_WINDOWS
2205int ll_win_enable_vt(void);
2206void ll_win_yield_ms(unsigned ms);
2209#endif
2210#endif
2213
2214
2215#endif
Definition ll_platform_generic.h:873
Definition ll_platform_generic.h:1776
Definition ll_platform_generic.h:1529
Definition ll_platform_generic.h:698
LL_String & append(char c)
Definition ll_platform_generic.h:751
void set(const char *s, int n)
Definition ll_platform_generic.h:710
LL_String & append(const char *s1)
Definition ll_platform_generic.h:736
int length() const
O(1) since [B834]; was a strlen on every call.
Definition ll_platform_generic.h:778
Definition ll_platform_generic.h:1810
bool set_input_callback(void(*cb)())
JIT exception-escape context; opaque to library users (ll_vm_internal.h).
Definition LambLisp.h:1367
Definition ll_platform_generic.h:839
int peek() override
One byte of pushback, which is all Stream promises. LambStdioClass has no peek of its own.
Definition ll_llarduino_print.cpp:130
int ll_llarduino_pin_mode(int pin)
Definition ll_llarduino_null.cpp:251
size_t println()
CRLF, as Arduino does – not "\n".
Definition ll_llarduino_print.cpp:82
size_t printf(const char *fmt,...) CHECKPRINTF_pos2
HardwareSerial::printf, used by LL itself.
Definition ll_llarduino_print.cpp:93
void pinMode(int pin, int mode)
Public Arduino surface – one line each, straight through the table.
Definition ll_llarduino_null.cpp:242
int available() override
Definition ll_llarduino_print.cpp:117
WiFiClass * LL_WiFi
Definition ll_xmop3_WiFi.cpp:31
const char *const * ll_feature_names(int *count)
The feature identifiers reported by (features) and matched by cond-expand.
Definition ll_xmop3_platform_generic.cpp:504
void ll_wifi_boot_reserve()
Definition ll_xmop3_WiFi.cpp:589
A board's panel: geometry, model, and the five operations. See the note above on NULL.
Definition ll_platform_generic.h:1741
const char * name
"st7789-240x240-spi3", "co5300-410x502-qspi", "rgb565-480x480-par16"
Definition ll_platform_generic.h:1742
bool(* init)(Lamb &lamb)
bring up bus + controller; idempotent
Definition ll_platform_generic.h:1745
LL_PanelModel model
PUSH or SCAN – branch on this, not on the name.
Definition ll_platform_generic.h:1744
void(* set_brightness)(Lamb &lamb, int percent)
0..100, or NULL if the panel has none
Definition ll_platform_generic.h:1749
int h
panel geometry in pixels
Definition ll_platform_generic.h:1743
void(* blit)(Lamb &lamb, int x, int y, int w, int h, const uint16_t *src)
PUSH only; NULL on SCAN.
Definition ll_platform_generic.h:1747
void(* fill)(Lamb &lamb, int x, int y, int w, int h, uint16_t color)
filled rect, RGB565
Definition ll_platform_generic.h:1746
void(* on_off)(Lamb &lamb, bool on)
display on/off, or NULL
Definition ll_platform_generic.h:1750
Definition ll_platform_generic.h:1924
LL_int32 va_list_max
Definition ll_platform_generic.h:1963
LL_int32 extension_block_size
Definition ll_platform_generic.h:1926
LL_int32 gc_total_marked
Definition ll_platform_generic.h:1933
bool apply_key(const char *key, long val)
LL_int32 gc_pool_target
Definition ll_platform_generic.h:1942
LL_int32 cell_block_size
Cells in the initial GC block (8K default; perf pushes 16K to match comps).
Definition ll_platform_generic.h:1925
LL_int32 sint_in_iram
Definition ll_platform_generic.h:1956
LL_int32 boot_pending
Definition ll_platform_generic.h:1971
LL_int32 ncg_frame_pool_size
Definition ll_platform_generic.h:1943