diff --git a/builder/LICENSE b/builder/LICENSE new file mode 100644 index 0000000..6fc1358 --- /dev/null +++ b/builder/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 David Johnson-Davies + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/builder/Load Builder.lisp b/builder/Load Builder.lisp new file mode 100644 index 0000000..61240c1 --- /dev/null +++ b/builder/Load Builder.lisp @@ -0,0 +1,35 @@ +;; Options: :avr :avr-nano :arm :msp430 :esp :stm32 :badge :zero :riscv + +(push :avr-nano *features*) + +(defparameter *release* "4.7") +(defparameter *date* "5th November 2024") + +;*************************************** + +#+badge +(push :avr *features*) + +#+(or arm esp stm32 riscv) +(push :float *features*) + +#+(or arm esp stm32 riscv avr) +(push :arrays *features*) + +#+(or arm esp) +(push :wifi *features*) + +#+(or riscv arm esp) +(push :gfx *features*) + +#+(or arm esp riscv avr) +(push :doc *features*) + +#+(or arm esp riscv avr) +(push :errors *features*) + +(load "/Users/david/Projects/Builder/builder defsys.lisp") + +(map nil #'delete-file (directory "/Users/david/Projects/Builder/fasls/*")) + +(compile-system "builder" :load t) \ No newline at end of file diff --git a/builder/README.md b/builder/README.md new file mode 100644 index 0000000..03886d5 --- /dev/null +++ b/builder/README.md @@ -0,0 +1,6 @@ +# uLisp Builder +Builds a version of uLisp for a particular platform from a common repository of source files. + +Currently updated to Version 4.7 for the AVR, AVR-Nano, ARM, ESP32, and RISC-V platforms. + +For information see http://www.ulisp.com/show?3F07. diff --git a/builder/Test Suites/AutoTester 32-bit.lisp b/builder/Test Suites/AutoTester 32-bit.lisp new file mode 100644 index 0000000..312de4a --- /dev/null +++ b/builder/Test Suites/AutoTester 32-bit.lisp @@ -0,0 +1,937 @@ +; uLisp Auto Tester + +; Sharp-double-quote + +(defun sharp-double-quote-reader (stream sub-char numarg) + (declare (ignore sub-char numarg)) + (let (chars) + (do ((prev (read-char stream) curr) + (curr (read-char stream) (read-char stream))) + ((and (char= prev #\") (char= curr #\#))) + (push prev chars)) + (coerce (nreverse chars) 'string))) + +(set-dispatch-macro-character + #\# #\" #'sharp-double-quote-reader) + +; do (run-tests) + +;;; ================================================================ + +(eval-when (:compile-toplevel :load-toplevel :execute) + (require "serial-port")) + +(cl:in-package "CL-USER") + +;;; ================================================================ +;;; Class SERIAL-STREAM + +(defclass serial-stream (stream:fundamental-character-input-stream + stream:fundamental-character-output-stream) + ((serial-port :initform nil + :initarg :serial-port + :accessor stream-serial-port))) + +(defmethod initialize-instance :after ((stream serial-stream) + &key name (baud-rate 9600) (data-bits 8) (stop-bits 1) (parity :none) + &allow-other-keys) + (unless (stream-serial-port stream) + (check-type name string) + (setf (stream-serial-port stream) + (serial-port:open-serial-port name + :baud-rate baud-rate + :data-bits data-bits + :stop-bits stop-bits + :parity parity)))) + +(defmethod stream-element-type ((stream serial-stream)) + 'character) + +(defmethod input-stream-p ((stream serial-stream)) + t) + +(defmethod output-stream-p ((stream serial-stream)) + t) + +;;; ================================================================ +;;; Input + +(defmethod stream:stream-read-char ((stream serial-stream)) + (serial-port:read-serial-port-char (stream-serial-port stream))) + +(defmethod stream:stream-read-char-no-hang ((stream serial-stream)) + (when (stream:stream-listen stream) + (stream:stream-read-char stream))) + +(defmethod stream:stream-listen ((stream serial-stream)) + (serial-port:serial-port-input-available-p (stream-serial-port stream))) + +(defmethod stream:stream-clear-input ((stream serial-stream)) + (loop while (stream:stream-listen stream) + do (stream:stream-read-char stream)) + nil) + + +;;; ================================================================ +;;; Output + +(defmethod stream:stream-write-char ((stream serial-stream) char) + (serial-port:write-serial-port-char char (stream-serial-port stream))) + +(defmethod stream:stream-write-string ((stream serial-stream) string &optional (start 0) (end (length string))) + (serial-port:write-serial-port-string string (stream-serial-port stream) t :start start :end end)) + +(defmethod stream:stream-force-output ((stream serial-stream)) + nil) + +(defmethod stream:stream-finish-output ((stream serial-stream)) + nil) + +(defmethod stream:stream-clear-output ((stream serial-stream)) + nil) + +(defmethod close :after ((stream serial-stream) &key abort) + (declare (ignorable abort)) + (serial-port:close-serial-port (stream-serial-port stream))) + +;;; ================================================================ +;;; Example + +(defparameter *tests* + +#"#| Symbols |# + +(aeq 'let 123 (let ((cat 123)) cat)) +(aeq 'let 79 (let ((ca% 79)) ca%)) +(aeq 'let 83 (let ((1- 83)) 1-)) +(aeq 'let 13 (let ((12a 13)) 12a)) +(aeq 'let 17 (let ((-1- 17)) -1-)) +(aeq 'let 66 (let ((abcdef 66)) abcdef)) +(aeq 'let 77 (let ((abcdefg 77)) abcdefg)) +(aeq 'let 88 (let ((abcdefgh 88)) abcdefgh)) +(aeq 'let 99 (let ((abcdefghi 99)) abcdefghi)) +(aeq 'let 1010 (let ((abcdefghij 1010)) abcdefghij)) +(aeq 'let "ab9" (princ-to-string 'ab9)) +(aeq 'let t (eq 'me 'me)) +(aeq 'let t (eq 'fishcake 'fishcake)) +(aeq 'let nil (eq 'fishcak 'fishca)) + +#| Arithmetic |# + +(aeq '* 9 (* -3 -3)) +(aeq '* 32580 (* 180 181)) +(aeq '* 1 (*)) +(aeq '+ 32767 (+ 32765 1 1)) +(aeq '+ 0 (+)) +(aeq '+ -2 (+ -1 -1)) +(aeq '- -4 (- 4)) +(aeq '- 0 (- 4 2 1 1)) +(aeq '/ 2 (/ 60 10 3)) +(aeq '1+ 2 (1+ 1)) +(aeq '1+ 0 (1+ -1)) +(aeq '1- 0 (1- 1)) + +#| Comparisons |# + +(aeq '< t (< -32768 32767)) +(aeq '< t (< -1 0)) +(aeq '< t (< 1 2 3 4)) +(aeq '< nil (< 1 2 2 4)) +(aeq '< t (<= 1 2 2 4)) +(aeq '< nil (<= 1 3 2 4)) +(aeq '< t (> 4 3 2 1)) +(aeq '< nil (> 4 2 2 1)) +(aeq '< t (>= 4 2 2 1)) +(aeq '< nil (>= 4 2 3 1)) +(aeq '< t (< 1)) +(aeq '< nil (< 1 3 2)) +(aeq '< nil (< -1 -2)) +(aeq '< nil (< 10 10)) +(aeq '<= t (<= 10 10)) +(aeq '= t (= 32767 32767)) +(aeq '>= t (>= 10 10)) +(aeq '>= nil (>= 9 10)) +(aeq '/= t (/= 1)) +(aeq '/= nil (/= 1 2 1)) +(aeq '/= nil (/= 1 2 3 1)) +(aeq '/= t (/= 1 2 3 4)) +(aeq 'plusp t (plusp 1)) +(aeq 'plusp nil (plusp 0)) +(aeq 'plusp nil (plusp -1)) +(aeq 'minusp nil (minusp 1)) +(aeq 'minusp nil (minusp 0)) +(aeq 'minusp t (minusp -1)) +(aeq 'zerop nil (zerop 1)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop -1)) +(aeq 'evenp nil (evenp 1)) +(aeq 'evenp t (evenp 0)) +(aeq 'evenp nil (evenp -1)) +(aeq 'oddp t (oddp 1)) +(aeq 'oddp nil (oddp 0)) +(aeq 'oddp t (oddp -1)) + +#| Maths functions |# + +(aeq 'abs 10 (abs 10)) +(aeq 'abs 10 (abs -10)) +(aeq 'max 45 (max 23 45)) +(aeq 'max -23 (max -23 -45)) +(aeq 'min 23 (min 23 45)) +(aeq 'min -45 (min -23 -45)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop 32767)) +(aeq 'mod 1 (mod 13 4)) +(aeq 'mod 3 (mod -13 4)) +(aeq 'mod -3 (mod 13 -4)) +(aeq 'mod -1 (mod -13 -4)) + +#| Number entry |# + +(aeq 'hex -1 #xFFFFFFFF) +(aeq 'hex 1 #x0001) +(aeq 'hex 4112 #x1010) +(aeq 'oct 511 #o777) +(aeq 'oct 1 #o1) +(aeq 'oct 65535 #o177777) +(aeq 'bin -1 #b11111111111111111111111111111111) +(aeq 'bin 10 #b1010) +(aeq 'bin 0 #b0) +(aeq 'hash 12 #'12) +(aeq 'hash 6 (funcall #'(lambda (x) (+ x 2)) 4)) + +#| Boolean |# + +(aeq 'and 7 (and t t 7)) +(aeq 'and nil (and t nil 7)) +(aeq 'or t (or t nil 7)) +(aeq 'or 1 (or 1 2 3)) +(aeq 'or nil (or nil nil nil)) +(aeq 'or 'a (or 'a 'b 'c)) +(aeq 'or 1 (let ((x 0)) (or (incf x)) x)) + +#| Bitwise |# + +(aeq 'logand -1 (logand)) +(aeq 'logand 170 (logand #xAA)) +(aeq 'logand 0 (logand #xAAAA #x5555)) +(aeq 'logior 0 (logior)) +(aeq 'logior 170 (logior #xAA)) +(aeq 'logior #xFFFF (logior #xAAAA #x5555)) +(aeq 'logxor 0 (logxor)) +(aeq 'logxor 170 (logior #xAA)) +(aeq 'logxor 255 (logxor #xAAAA #xAA55)) +(aeq 'lognot -43691 (lognot #xAAAA)) +(aeq 'ash 492 (ash 123 2)) +(aeq 'ash 65535 (ash #xFFFF 0)) +(aeq 'ash 16383 (ash #xFFFF -2)) +(aeq 'ash 262140 (ash #xFFFF 2)) +(aeq 'ash 8191 (ash #x7FFF -2)) +(aeq 'logbitp t (logbitp 0 1)) +(aeq 'logbitp t (logbitp 1000 -1)) +(aeq 'logbitp nil (logbitp 1000 0)) + +#| Tests |# + +(aeq 'atom t (atom nil)) +(aeq 'atom t (atom t)) +(aeq 'atom nil (atom '(1 2))) +(aeq 'consp nil (consp 'b)) +(aeq 'consp t (consp '(a b))) +(aeq 'consp nil (consp nil)) +(aeq 'listp nil (listp 'b)) +(aeq 'listp t (listp '(a b))) +(aeq 'listp t (listp nil)) +(aeq 'numberp t (numberp (+ 1 2))) +(aeq 'numberp nil (numberp 'b)) +(aeq 'numberp nil (numberp nil)) +(aeq 'symbolp t (symbolp 'b)) +(aeq 'symbolp nil (symbolp 3)) +(aeq 'symbolp t (symbolp nil)) +(aeq 'streamp nil (streamp 'b)) +(aeq 'streamp nil (streamp nil)) +(aeq 'boundp t (let (x) (boundp 'x))) +(aeq 'boundp nil (let (x) (boundp 'y))) + +#| cxr operations |# + +(aeq 'car 'a (car '(a b c))) +(aeq 'car nil (car nil)) +(aeq 'first 'a (first '(a b c))) +(aeq 'first nil (first nil)) +(aeq 'cdr 'b (cdr '(a . b))) +(aeq 'cdr 'b (car (cdr '(a b)))) +(aeq 'cdr nil (cdr nil)) +(aeq 'rest 'b (rest '(a . b))) +(aeq 'rest 'b (car (rest '(a b)))) +(aeq 'rest nil (rest nil)) +(aeq 'caaar 'a (caaar '(((a))))) +(aeq 'caaar 'nil (caaar nil)) +(aeq 'caadr 'b (caadr '(a (b)))) +(aeq 'caadr 'nil (caadr nil)) +(aeq 'caar 'a (caar '((a)))) +(aeq 'caar 'nil (caar nil)) +(aeq 'cadar 'c (cadar '((a c) (b)))) +(aeq 'cadar 'nil (cadar nil)) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'cadr 'b (cadr '(a b))) +(aeq 'second 'nil (second '(a))) +(aeq 'second 'b (second '(a b))) +(aeq 'cadr 'nil (cadr '(a))) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'third 'c (third '(a b c))) +(aeq 'third 'nil (third nil)) +(aeq 'cdaar 'b (car (cdaar '(((a b)) b c)))) +(aeq 'cdaar 'nil (cdaar nil)) +(aeq 'cdadr 'c (car (cdadr '(a (b c))))) +(aeq 'cdadr 'nil (cdadr nil)) +(aeq 'cdar 'b (car (cdar '((a b c))))) +(aeq 'cdar 'nil (cdar nil)) +(aeq 'cddar 'c (car (cddar '((a b c))))) +(aeq 'cddar 'nil (cddar nil)) +(aeq 'cdddr 'd (car (cdddr '(a b c d)))) +(aeq 'cdddr nil (car (cdddr '(a b c)))) +(aeq 'cddr 'c (car (cddr '(a b c)))) +(aeq 'cddr 'nil (cddr '(a))) + +#| List operations |# + +(aeq 'cons 'a (car (cons 'a 'b))) +(aeq 'cons nil (car (cons nil 'b))) +(aeq 'append 6 (length (append '(a b c) '(d e f)))) +(aeq 'append nil (append nil nil)) +(aeq 'append '(1 2 3 4 5 . 6) (append '(1 2 3) '(4 5 . 6))) +(aeq 'list nil (car (list nil))) +(aeq 'list 'a (car (list 'a 'b 'c))) +(aeq 'reverse 'c (car (reverse '(a b c)))) +(aeq 'reverse nil (reverse nil)) +(aeq 'length 0 (length nil)) +(aeq 'length 4 (length '(a b c d))) +(aeq 'length 2 (length '(nil nil))) +(aeq 'assoc nil (assoc 'b nil)) +(aeq 'assoc nil (assoc 'b '(nil nil))) +(aeq 'assoc '(b . 12) (assoc 'b '((a . 10) (b . 12)))) +(aeq 'assoc '(nil . 12) (assoc nil '((a . 10) (nil . 12)))) +(aeq 'assoc '(b) (assoc 'b '((a . 10) (b)))) +(aeq 'assoc '("three" . 3) (assoc "three" '(("one" . 1) ("two" . 2) ("three" . 3)) :test string=)) +(aeq 'member '(3 4) (member 3 '(1 2 3 4))) +(aeq 'member nil (member 5 '(1 2 3 4))) +(aeq 'member '(3 4) (member 3 '(1 2 3 4) :test eq)) +(aeq 'member '("three" "four") (member "three" '("one" "two" "three" "four") :test string=)) +(aeq 'member '("two" "three" "four") (member "three" '("one" "two" "three" "four") :test string<)) + +#| map operations |# + +(aeq 'mapc 2 (cadr (mapc + '(1 2 3 4)))) +(aeq 'mapc 10 (let ((x 0)) (mapc (lambda (y) (incf x y)) '(1 2 3 4)) x)) +(aeq 'mapcar '(1 4 9 16) (mapcar (lambda (x) (* x x)) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16) (mapcar * '(1 2 3 4) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16 25) (mapcar (lambda (x) (* x x)) '(1 2 3 4 5))) +(aeq 'mapcan '(1 4 2 5 3 6) (mapcan #'list '(1 2 3) '(4 5 6))) +(aeq 'mapcan '(1 3 2 4) (mapcan list '(1 2) '(3 4))) +(aeq 'mapcan '(1 5 9 2 6 10 3 7 11) (mapcan list '(1 2 3 4) '(5 6 7 8) '(9 10 11))) +(aeq 'mapcan '(1 2 3 . 4) (mapcan (lambda (x) x) '((1) (2) (3 . 4)))) +(aeq 'mapcan '(2 3 . 4) (mapcan (lambda (x) x) '(nil (2) (3 . 4)))) +(aeq 'maplist '(((1 2 3) 6 7 8) ((2 3) 7 8) ((3) 8)) (maplist #'cons '(1 2 3) '(6 7 8))) +(aeq 'maplist '(1 2 3) (mapl #'cons '(1 2 3) '(6 7 8))) +(aeq 'mapcan '(3 7 11) (mapcon (lambda (x) (when (eq (first x) (second x)) (list (car x)))) '(1 2 3 3 5 7 7 8 9 11 11))) + +#| let/let*/lambda |# + +(aeq 'let 7 (let ((x 7)) (let ((x 6) (y x)) y))) +(aeq 'let* 6 (let* ((x 7)) (let* ((x 6) (y x)) y))) +(aeq 'let t (let ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'let* t (let* ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'lambda 2 ((lambda (x y) (setq y x) y) 2 3)) +(aeq 'lambda 9 ((lambda (&rest x) (apply + x)) 2 3 4)) +(aeq 'lambda 8 ((lambda (x &optional (y 4)) (* x y)) 2)) +(aeq 'lambda 6 ((lambda (x &optional (y 4)) (* x y)) 2 3)) +(aeq 'lambda 6 ((lambda (x &optional y) (* x y)) 2 3)) +(aeq 'lambda 123 ((lambda (list) list) 123)) + +#| loops and control |# + +(aeq 'progn 8 (let ((x 6)) (progn (incf x) (incf x)))) +(aeq 'dotimes 21 (let ((x 6)) (dotimes (y 6 x) (setq x (+ x y))))) +(aeq 'dotimes 6 (let ((x 6)) (dotimes (y 6 y) (setq x (+ x y))))) +(aeq 'dotimes 0 (let ((x 6)) (dotimes (y 0 y) (setq x (+ x y))))) +(aeq 'dolist 6 (let ((x 0)) (dolist (y '(1 2 3) x) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3)) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3) y) (setq x (+ x y))))) +(aeq 'loop 6 (let ((x 0)) (loop (when (= x 6) (return x)) (incf x)))) +(aeq 'loop 6 (let ((x 0)) (loop (unless (< x 6) (return x)) (incf x)))) +(aeq 'return 'a (let ((a 7)) (loop (progn (return 'a))))) +(aeq 'return nil (loop (return))) +(aeq 'return 'a (let ((a 7)) (loop (progn (return 'a) nil)))) +(aeq 'do 2 (do* ((x 1 (1+ x)) (y 0 (1+ x))) ((= 3 y) x))) +(aeq 'do 3 (do ((x 1 (1+ x)) (y 0 (1+ x))) ((= 3 y) x))) +(aeq 'do 720 (do* ((n 6) (f 1 (* j f)) (j n (- j 1))) ((= j 0) f))) +(aeq 'do 720 (let ((n 6)) (do ((f 1 (* j f)) (j n (- j 1)) ) ((= j 0) f)))) +(aeq 'do 10 (do (a (b 1 (1+ b))) ((> b 10) a) (setq a b))) + +#| conditions |# + +(aeq 'if 3 (let ((a 2)) (if (= a 2) 3 4))) +(aeq 'if 4 (let ((a 2)) (if (= a 3) 3 4))) +(aeq 'if 4 (let ((a 3)) (if (= a 3) 4))) +(aeq 'if nil (let ((a 4)) (if (= a 3) 4))) +(aeq 'when 4 (let ((a 3)) (when (= a 3) 4))) +(aeq 'when nil (let ((a 2)) (when (= a 3) 4))) +(aeq 'unless nil (let ((a 3)) (unless (= a 3) 4))) +(aeq 'unless 4 (let ((a 2)) (unless (= a 3) 4))) +(aeq 'cond 8 (let ((a 2)) (cond ((= a 3) 7) ((= a 2) 8) (t 9)))) +(aeq 'cond 9 (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8) (9)))) +(aeq 'cond nil (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8)))) +(aeq 'cond 12 (car (cond ((evenp 3) (list (* 2 3))) ((list (* 3 4)))))) +(aeq 'case 222 (let ((j 1)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 333 (let ((j t)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 444 (let ((j 2)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) + +#| eval/funcall/apply |# + +(aeq 'funcall 10 (funcall + 1 2 3 4)) +(aeq 'funcall 'a (funcall car '(a b c d))) +(aeq 'funcall 3 (let ((x 0)) (funcall (lambda (y) (incf x y)) 3) x)) +(aeq 'apply 10 (apply + '(1 2 3 4))) +(aeq 'apply 13 (apply + 1 2 '(1 2 3 4))) +(aeq 'eval 10 (eval (list + 1 2 3 4))) +(aeq 'eval nil (eval nil)) +(aeq 'funcall 999 (let ((x 999)) (funcall (lambda (x) x) x))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (funcall fun (funcall fun x)))))) (funcall (x2 '1+) 2))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (fun (fun x)))))) ((x2 '1+) 2))) +(aeq 'apply 5 (let* ((my (lambda (x y) (+ x y))) (han '(my))) (apply (first han) '(2 3)))) + +#| in-place operations |# + +(aeq 'incf 5 (let ((x 0)) (+ (incf x) (incf x 2) (incf x -2)))) +(aeq 'decf -5 (let ((x 0)) (+ (decf x) (decf x 2) (decf x -2)))) +(aeq 'incf 12 (let ((x 0)) (+ (incf x 2) (incf x 2) (incf x 2)))) +(aeq 'incf 36 (let ((n 10)) (let* ((f1 (lambda () (incf n) n))) (+ (funcall f1) (funcall f1) (funcall f1))))) +(aeq 'setf 25 (let ((a 3) (b 4)) (setf a (* a 3) b (* b 4)) (+ a b))) +(aeq 'setf 9 (let ((a '(2 3))) (setf (car a) 6) (apply + a))) +(aeq 'setf 12 (let ((a '(2 3))) (setf (cdr a) '(6)) (apply * a))) +(aeq 'setf 220 (let ((a '(2 3 4))) (setf (nth 1 a) 11 (nth 2 a) 10) (apply * a))) + +#| recursion |# + +(aeq 'lambda 55 (let ((fib (lambda (n) (if (< n 3) 1 (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))) +(aeq 'lambda 5040 (let ((f (lambda (n) (if (= n 0) 1 (* n (f (- n 1))))))) (f 7))) +(aeq 'lambda 10 (let ((a 0)) (let ((f (lambda (n) (incf a n) (when (> n 0) (f (1- n)))))) (f 4)) a)) + +#| streams |# + +(aeq 'stream "" (with-output-to-string (s) (princ s s))) +(aeq 'stream "12 23 34" (with-output-to-string (st) (format st "~a ~a ~a" 12 23 34))) + +#| features |# + +(aeq 'features t (not (not (member :floating-point *features*)))) +(aeq 'features t (not (not (member :arrays *features*)))) + +#| printing |# + +(aeq 'princ "hello" (princ-to-string "hello")) +(aeq 'princ "hello \"David\"" (princ-to-string "hello \"David\"")) +(aeq 'prin1 "\"hello\"" (prin1-to-string "hello")) +(aeq 'prin1 "\"hello \\\"David\\\"\"" (prin1-to-string "hello \"David\"")) + +#| prettyprinting |# + +(aeq 'princ "hello" (princ-to-string "hello")) +(aeq 'pprint 10996 (let ((n 0) (st (with-output-to-string (str) (pprint aeq str)))) (dotimes (i (length st) n) (incf n (char-code (char st i)))))) + +#| documentation |# + +(aeq 'apropos '(progn apropos apropos-list unwind-protect) (apropos-list 'pro)) +(aeq 'apropos '(progn apropos apropos-list unwind-protect) (apropos-list "pro")) +(aeq 'documentation 7397 (let ((n 0)) (let ((st (documentation '?))) (dotimes (i (length st) n) (incf n (char-code (char st i))))))) + +#| format |# + +(aeq 'format "hello" (format nil "hello")) +(aeq 'format "Hello23Goodbye" (format nil "Hello~aGoodbye" 23)) +(aeq 'format " 17" (format nil "~5x" 23)) +(aeq 'format " 10111" (format nil "~6b" 23)) +(aeq 'format " 17 23 23 " (format nil "~5x ~5d ~5a" 23 23 23)) +(aeq 'format "00017 00023" (format nil "~5,'0x ~5,'0d" 23 23)) +(aeq 'format "01-45-07" (format nil "~2,'0d-~2,'0d-~2,'0d" 1 45 7)) +(aeq 'format "Hello42" (format nil "Hello~a" 42)) +(aeq 'format "[1,2,3]" (format nil "[~{~a~^,~}]" '(1 2 3))) +(aeq 'format "0003.14159" (format nil "~10,'0g" 3.14159)) +(aeq 'format "nil nil" (format nil "~a ~{ ~a ~} ~a" nil nil nil)) + +#| strings |# + +(aeq 'stringp t (stringp "hello")) +(aeq 'stringp nil (stringp 5)) +(aeq 'stringp nil (stringp '(a b))) +(aeq 'numberp nil (numberp "hello")) +(aeq 'atom t (atom "hello")) +(aeq 'consp nil (consp "hello")) +(aeq 'eq nil (eq "hello" "hello")) +(aeq 'eq t (let ((a "hello")) (eq a a))) +(aeq 'length 0 (length "")) +(aeq 'length 5 (length "hello")) +(aeq 'concatenate t (string= (concatenate 'string "A" "B") "AB")) +(aeq 'concatenate 3 (length (concatenate 'string "A" "BC"))) +(aeq 'concatenate 0 (length (concatenate 'string))) +(aeq 'concatenate "ABCD" (concatenate 'string "AB" "CD")) +(aeq 'concatenate "ABCDE" (concatenate 'string "AB" "CDE")) +(aeq 'concatenate "ABCDE" (concatenate 'string "ABC" "DE")) +(aeq 'concatenate "ABCDEF" (concatenate 'string "ABC" "DEF")) +(aeq 'string= nil (string= "cat" "cat ")) +(aeq 'string= t (string= "cat" "cat")) +(aeq 'string/= 3 (string/= "cat" "catx")) +(aeq 'string/= nil (string/= "cat" "cat")) +(aeq 'string/= nil (string/= "catt" "catt")) +(aeq 'string< nil (string< "cat" "cat")) +(aeq 'string<= 3 (string<= "cat" "cat")) +(aeq 'string< 3 (string< "cat" "cat ")) +(aeq 'string< 4 (string< "fish" "fish ")) +(aeq 'string> nil (string> "cat" "cat")) +(aeq 'string>= 3 (string>= "cat" "cat")) +(aeq 'string>= 5 (string>= "cattx" "cattx")) +(aeq 'string> 0 (string> "c" "a")) +(aeq 'string> 1 (string> "fc" "fa")) +(aeq 'string> 2 (string> "ffc" "ffa")) +(aeq 'string> 3 (string> "fffc" "fffa")) +(aeq 'string> 4 (string> "ffffc" "ffffa")) +(aeq 'string> 5 (string> "fffffc" "fffffa")) +(aeq 'string> nil (string< "fffffc" "fffffa")) +(aeq 'string "albatross" (string "albatross")) +(aeq 'string "x" (string #\x)) +(aeq 'string "cat" (string 'cat)) +(aeq 'string "albatross" (string 'albatross)) + + +#| subseq and search |# + +(aeq 'subseq "hello" (subseq "hellofromdavid" 0 5)) +(aeq 'subseq "fromdavid" (subseq "hellofromdavid" 5)) +(aeq 'subseq '(2 3 4) (subseq '(0 1 2 3 4) 2)) +(aeq 'subseq '(2) (subseq '(0 1 2 3 4) 2 3)) +(aeq 'subseq nil (subseq '() 0)) +(aeq 'search 4 (search "cat" "the cat sat on the mat")) +(aeq 'search 19 (search "mat" "the cat sat on the mat")) +(aeq 'search nil (search "hat" "the cat sat on the mat")) +(aeq 'search 1 (search '(1 2) '( 0 1 2 3 4))) +(aeq 'search nil (search '(2 1 2 3 4 5) '(2 1 2 3 4))) + +#| characters |# + +(aeq 'char-code 97 (char-code #\a)) +(aeq 'char-code 13 (char-code #\return)) +(aeq 'char-code 255 (char-code #\255)) +(aeq 'code-char #\return (code-char 13)) +(aeq 'code-char #\a (code-char 97)) +(aeq 'code-char #\255 (code-char 255)) +(aeq 'eq t (eq #\b #\b)) +(aeq 'eq nil (eq #\b #\B)) +(aeq 'numberp nil (numberp #\b)) +(aeq 'characterp t (characterp #\b)) +(aeq 'char #\o (char "hello" 4)) +(aeq 'char #\h (char "hello" 0)) +(aeq 'char "A" (princ-to-string (code-char 65))) +(aeq 'char "[#\\Bell]" (format nil "[~s]" (code-char 7))) +(aeq 'char "[#\\Return]" (format nil "[~s]" #\return)) +(aeq 'char "[#\\127]" (format nil "[~s]" #\127)) +(aeq 'char "[#\\255]" (format nil "[~s]" #\255)) + +#| read-from-string |# + +(aeq 'read-from-string 123 (read-from-string "123")) +(aeq 'read-from-string 144 (eval (read-from-string "((lambda (x) (* x x)) 12)"))) +(aeq 'read-from-string t (eval (read-from-string "(eq (+ 2 3) 5)"))) +(aeq 'read-from-string nil (read-from-string "()")) + +#| closures |# + +(aeq 'closure 'lex (let ((lex nil)) (funcall (let ((lex t)) (lambda () (if lex 'lex 'dyn)))))) +(aeq 'closure 103 (let* ((c 100) (two (lambda (d) (+ c d))) (one (lambda (c) (funcall two 3)))) (funcall one 1))) +(aeq 'closure 4 (let ((x 0)) (funcall (lambda (y) (incf x y)) 4) x)) +(aeq 'closure 0 (let ((x 0)) (funcall (let ((x 7)) (lambda (y) (setq x (+ x y) ))) 4) x)) +(aeq 'closure '(8 10 13 17) (let ((x 0) (clo (lambda () (let ((x 7)) (lambda (y) (incf x y)))))) (mapcar (funcall clo) '(1 2 3 4)))) +(aeq 'closure 3 (let ((y 0) (test (lambda (x) (+ x 1)))) (dotimes (x 3 y) (progn (test (+ x 2))) (incf y x)))) + +#| arrays |# + +(aeq 'array '(0 0) (array-dimensions #2a())) +(aeq 'array '(1 0) (array-dimensions #2a(()))) +(aeq 'array '(2 0) (array-dimensions #2a(() ()))) +(aeq 'array '(0) (array-dimensions (make-array '(0)))) +(aeq 'array '(0) (array-dimensions (make-array 0))) +(aeq 'array 1 (let ((a (make-array 3 :initial-element 0))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array '(3) :initial-element 0))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array '(2 3) :initial-element 0))) (incf (aref a 1 (+ 1 1))) (aref a 1 2))) +(aeq 'array 1 (let ((a (make-array '(2 3 2 2) :initial-element 0))) (incf (aref a 1 (+ 1 1) 1 1)) (aref a 1 2 1 1))) +(aeq 'array 10 (length (make-array 10 :initial-element 1))) + +#| bit arrays |# + +(aeq 'array '(0) (array-dimensions (make-array '(0) :element-type 'bit))) +(aeq 'array '(1 1) (array-dimensions (make-array '(1 1) :element-type 'bit))) +(aeq 'array 10 (length (make-array '(10) :element-type 'bit))) +(aeq 'array 10 (length (make-array 10 :element-type 'bit))) +(aeq 'array 1 (let ((a (make-array 3 :element-type 'bit))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array 3 :initial-element 0 :element-type 'bit))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 0 (let ((a (make-array 10 :element-type 'bit :initial-element 1))) (decf (aref a 4)) (aref a 4))) +(aeq 'array 1 (let ((a (make-array 40 :element-type 'bit :initial-element 0))) (incf (aref a 39)) (aref a 39))) +(aeq 'array 0 (let ((a (make-array 40 :element-type 'bit :initial-element 0))) (incf (aref a 39)) (decf (aref a 39)) (aref a 39))) + +#| repl |# + +(aeq 'repl 23 (read-from-string "23(2)")) +(aeq 'repl nil (read-from-string "()23")) +(aeq 'repl 23 (read-from-string "23\"Hi\"")) +(aeq 'repl "Hi" (read-from-string "\"Hi\"23")) +(aeq 'repl #\1 (read-from-string " #\\1\"Hi\"")) +(aeq 'repl "Hi" (read-from-string (format nil "\"Hi\"~a~a" #\# "*0101"))) + +#| equal |# + +(aeq 'equal t (equal '(1 2 3) '(1 2 3))) +(aeq 'equal t (equal '(1 2 (4) 3) '(1 2 (4) 3))) +(aeq 'equal nil (equal '(1 2 (4) 3) '(1 2 (4 nil) 3))) +(aeq 'equal t (equal "cat" "cat")) +(aeq 'equal nil (equal "cat" "Cat")) +(aeq 'equal t (equal 'cat 'Cat)) +(aeq 'equal t (equal 2 (+ 1 1))) +(aeq 'equal t (equal '("cat" "dog") '("cat" "dog"))) +(aeq 'equal nil (equal '("cat" "dog") '("cat" "dig"))) +(aeq 'equal nil (equal '("cat" "dog") '("cat" "Dog"))) + +#| keywords |# + +(aeq 'keywordp t (keywordp :led-builtin)) +(aeq 'keywordp nil (keywordp print)) +(aeq 'keywordp nil (keywordp nil)) +(aeq 'keywordp nil (keywordp 12)) +(aeq 'keywordp t (keywordp :fred)) +(aeq 'keywordp t (keywordp :initial-element)) +(aeq 'keywordp t (keywordp :element-type)) + +#| errors |# + +(aeq 'error 7 (let ((x 7)) (ignore-errors (setq x (/ 1 0))) x)) +(aeq 'error 5 (unwind-protect (+ 2 3) 13)) + +#| Printing floats |# + +(aeq 'print t (string= (princ-to-string 101.0) "101.0")) +(aeq 'print t (string= (princ-to-string 1010.0) "1010.0")) +(aeq 'print t (string= (princ-to-string 10100.0) "10100.0")) +(aeq 'print t (string= (princ-to-string 101000.0) "1.01e5")) +(aeq 'print t (string= (princ-to-string 1010000.0) "1.01e6")) +(aeq 'print t (string= (princ-to-string 1.01E7) "1.01e7")) +(aeq 'print t (string= (princ-to-string 1.01E8) "1.01e8")) +(aeq 'print t (string= (princ-to-string 7.0) "7.0")) +(aeq 'print t (string= (princ-to-string 70.0) "70.0")) +(aeq 'print t (string= (princ-to-string 700.0) "700.0")) +(aeq 'print t (string= (princ-to-string 7000.0) "7000.0")) +(aeq 'print t (string= (princ-to-string 70000.0) "70000.0")) +(aeq 'print t (string= (princ-to-string 700000.0) "7.0e5")) +(aeq 'print t (string= (princ-to-string 0.7) "0.7")) +(aeq 'print t (string= (princ-to-string 0.07) "0.07")) +(aeq 'print t (string= (princ-to-string 0.007) "0.007")) +(aeq 'print t (string= (princ-to-string 7.0E-4) "7.0e-4")) +(aeq 'print t (string= (princ-to-string 7.0E-5) "7.0e-5")) +(aeq 'print t (string= (princ-to-string 7.0E-6) "7.0e-6")) +(aeq 'print t (string= (princ-to-string 0.9) "0.9")) +(aeq 'print t (string= (princ-to-string 0.99) "0.99")) +(aeq 'print t (string= (princ-to-string 0.999) "0.999")) +(aeq 'print t (string= (princ-to-string 0.9999) "0.9999")) +(aeq 'print t (string= (princ-to-string 0.99999) "0.99999")) +(aeq 'print t (string= (princ-to-string 0.999999) "0.999999")) +(aeq 'print t (string= (princ-to-string 0.9999999) "1.0")) +(aeq 'print t (string= (princ-to-string 1.0) "1.0")) +(aeq 'print t (string= (princ-to-string 10.0) "10.0")) +(aeq 'print t (string= (princ-to-string 100.0) "100.0")) +(aeq 'print t (string= (princ-to-string 1000.0) "1000.0")) +(aeq 'print t (string= (princ-to-string 10000.0) "10000.0")) +(aeq 'print t (string= (princ-to-string 100000.0) "1.0e5")) +(aeq 'print t (string= (princ-to-string 9.0) "9.0")) +(aeq 'print t (string= (princ-to-string 90.0) "90.0")) +(aeq 'print t (string= (princ-to-string 900.0) "900.0")) +(aeq 'print t (string= (princ-to-string 9000.0) "9000.0")) +(aeq 'print t (string= (princ-to-string 90000.0) "90000.0")) +(aeq 'print t (string= (princ-to-string 900000.0) "9.0e5")) +(aeq 'print t (string= (princ-to-string -9.0) "-9.0")) +(aeq 'print t (string= (princ-to-string -90.0) "-90.0")) +(aeq 'print t (string= (princ-to-string -900.0) "-900.0")) +(aeq 'print t (string= (princ-to-string -9000.0) "-9000.0")) +(aeq 'print t (string= (princ-to-string -90000.0) "-90000.0")) +(aeq 'print t (string= (princ-to-string -900000.0) "-9.0e5")) +(aeq 'print t (string= (princ-to-string 1.0) "1.0")) +(aeq 'print t (string= (princ-to-string 1.01) "1.01")) +(aeq 'print t (string= (princ-to-string 1.001) "1.001")) +(aeq 'print t (string= (princ-to-string 1.0001) "1.0001")) +(aeq 'print t (string= (princ-to-string 1.00001) "1.00001")) +(aeq 'print t (string= (princ-to-string 1.000001) "1.0")) +(aeq 'print t (string= (princ-to-string 0.0012345678) "0.00123457")) +(aeq 'print t (string= (princ-to-string 1.2345678E-4) "1.23457e-4")) +(aeq 'print t (string= (princ-to-string 1234567.9) "1.23457e6")) +(aeq 'print t (string= (princ-to-string 1.2345679E7) "1.23457e7")) +(aeq 'print t (string= (princ-to-string 1.2E-9) "1.2e-9")) +(aeq 'print t (string= (princ-to-string 9.9E-8) "9.9e-8")) +(aeq 'print t (string= (princ-to-string 9.9999E-5) "9.9999e-5")) +(aeq 'print t (string= (princ-to-string 9.01) "9.01")) +(aeq 'print t (string= (princ-to-string 0.9999999) "1.0")) +(aeq 'print t (string= (princ-to-string 0.8999999) "0.9")) +(aeq 'print t (string= (princ-to-string 0.01) "0.01")) +(aeq 'print t (string= (princ-to-string 1.2345679) "1.23457")) +(aeq 'print t (string= (princ-to-string 12.345679) "12.3457")) +(aeq 'print t (string= (princ-to-string 123.45679) "123.457")) +(aeq 'print t (string= (princ-to-string 1234.5679) "1234.57")) +(aeq 'print t (string= (princ-to-string 12345.679) "12345.7")) +(aeq 'print t (string= (princ-to-string 123456.79) "1.23457e5")) +(aeq 'print t (string= (princ-to-string 1234567.9) "1.23457e6")) +(aeq 'print t (string= (princ-to-string 0.12345679) "0.123457")) +(aeq 'print t (string= (princ-to-string 0.012345679) "0.0123457")) +(aeq 'print t (string= (princ-to-string 0.0012345678) "0.00123457")) +(aeq 'print t (string= (princ-to-string 1.2345679E-4) "1.23457e-4")) + +#| Arithmetic |# + +(aeq '= t (= (- 4 2 1 1) 0)) +(aeq '* 9 (* -3 -3)) +(aeq '* 32580 (* 180 181)) +(aeq '* 1 (*)) +(aeq '* t (string= "-4.29497e9" (princ-to-string (* 2 -2147483648)))) +(aeq '* -2147483648 (* 2 -1073741824)) +(aeq '+ 32767 (+ 32765 1 1)) +(aeq '+ 0 (+)) +(aeq '+ -2 (+ -1 -1)) +(aeq '- -4 (- 4)) +(aeq '/ 2 (/ 60 10 3)) +(aeq '1+ 2.5 (1+ 1.5)) +(aeq '1+ 2147483647 (1+ 2147483646)) +(aeq '1+ t (string= "2.14748e9" (princ-to-string (1+ 2147483647)))) +(aeq '1- 0.5 (1- 1.5)) +(aeq '1- -2147483648 (1- -2147483647)) +(aeq '1- t (string= "-2.14748e9" (princ-to-string (1- -2147483648)))) + +#| Arithmetic |# + +(aeq '/ 1.75 (/ 3.5 2)) +(aeq '/ 1.75 (/ 3.5 2.0)) +(aeq '/ 0.0625 (/ 1 16)) +(aeq '/ 0.0625 (/ 1.0 16)) +(aeq '/ 0.0625 (/ 1 16.0)) +(aeq '/ 2 (/ 12 2 3)) +(aeq '/ 2.0 (/ 12.0 2 3)) +(aeq '/ 2.0 (/ 12 2.0 3)) +(aeq '/ 2.0 (/ 12 2 3.0)) +(aeq '/ 1 (/ 1)) +(aeq '/ t (string= "2.14748e9" (princ-to-string (/ -2147483648 -1)))) +(aeq '/ 2147483647 (/ -2147483647 -1)) +(aeq '/ 0.5 (/ 2)) +(aeq '* 1.0 (* 0.0625 16)) +(aeq '* 1.0 (* 0.0625 16.0)) + +#| Place |# + +(aeq 'incf 5.4 (let ((x 0)) (+ (incf x) (incf x 0.2) (incf x 2)))) +(aeq 'decf -5.4 (let ((x 0)) (+ (decf x) (decf x 0.2) (decf x 2)))) +(aeq 'incf 30.6 (let ((n 10)) (let* ((f1 (lambda () (incf n 0.1) n))) (+ (funcall f1) (funcall f1) (funcall f1))))) +(aeq 'setf "hellx" (let ((s "hello")) (setf (char s 4) #\x) s)) + +#| Comparisons |# + +(aeq '< t (< 1 2 3 4)) +(aeq '< nil (< 1 2 3 2)) +(aeq '< t (< 1.0 2 3 4)) +(aeq '< nil (< 1 2 3 2)) +(aeq '< t (< 1.0 1.001 3 4)) +(aeq '< nil (< 1.001 1.0 3 4)) +(aeq '< t (< 1.001 1.002 1.003 1.004)) +(aeq '< t (< 1. 2. 3. 4.)) +(aeq '< nil (< 1. 2. 2. 4.)) +(aeq '< t (<= 1. 2. 2. 4.)) +(aeq '< nil (<= 1. 3. 2. 4.)) +(aeq '< t (> 4. 3. 2. 1.)) +(aeq '< nil (> 4. 2. 2. 1.)) +(aeq '< t (>= 4. 2. 2. 1.)) +(aeq '< nil (>= 4. 2. 3. 1.)) +(aeq '/= t (= 1. 1. 1. 1.)) +(aeq '/= nil (= 1. 1. 2. 1.)) +(aeq '/= nil (/= 1. 2. 3. 1.)) +(aeq '/= t (/= 1. 2. 3. 4.)) + +#| Transcendental |# + +(aeq 'sin 0.84147096 (sin 1)) +(aeq 'sin 0.0 (sin 0)) +(aeq 'sin 0.84147096 (sin 1.0)) +(aeq 'sin 0.0 (sin 0.0)) +(aeq 'cos 0.540302 (cos 1)) +(aeq 'cos 0.540302 (cos 1.0)) +(aeq 'tan 1.55741 (tan 1)) +(aeq 'tan 1.55741 (tan 1.0)) +(aeq 'asin 1.5707964 (asin 1)) +(aeq 'asin 1.5707964 (asin 1)) +(aeq 'asin 0.0 (asin 0)) +(aeq 'asin 0.0 (asin 0.0)) +(aeq 'acos 0.0 (acos 1)) +(aeq 'acos 0.0 (acos 1.0)) +(aeq 'acos 1.0471976 (acos 0.5)) +(aeq 'atan 0.4636476 (atan 0.5)) +(aeq 'atan 0.110657 (atan 1 9)) +(aeq 'atan 0.049958397 (atan 1 20)) +(aeq 'atan 0.785398 (atan 1 1)) +(aeq 'atan 0.785398 (atan .5 .5))x +(aeq 'sinh 1.1752 (sinh 1)) +(aeq 'sinh 1.1752 (sinh 1.0)) +(aeq 'sinh 0.0 (sinh 0)) +(aeq 'sinh 0.0 (sin 0.0)) +(aeq 'cosh 1.5430807 (cosh 1)) +(aeq 'cosh 1.5430807 (cosh 1.0)) +(aeq 'tanh 0.7615942 (tanh 1)) +(aeq 'tanh 0.7615942 (tanh 1.0)) + +#| Rounding |# + +(aeq 'truncate 3 (truncate 10 3)) +(aeq 'truncate 3 (truncate 3.3333333)) +(aeq 'ceiling 4 (ceiling 10 3)) +(aeq 'ceiling 4 (ceiling 3.3333333)) +(aeq 'round 3 (round 10 3)) +(aeq 'round 3 (round 3.3333333)) +(aeq 'floor 3 (floor 10 3)) +(aeq 'floor 3 (floor 3.3333333)) +(aeq 'truncate -3 (truncate -10 3)) +(aeq 'truncate -3 (truncate -3.3333333)) +(aeq 'ceiling -3 (ceiling -10 3)) +(aeq 'ceiling -3 (ceiling -3.3333333)) +(aeq 'round -3 (round -10 3)) +(aeq 'round -3 (round -3.3333333)) +(aeq 'floor -4 (floor -10 3)) +(aeq 'floor -4 (floor -3.3333333)) +(aeq 'abs 10.0 (abs 10.0)) +(aeq 'abs 10.0 (abs -10.0)) +(aeq 'abs t (string= "2.14748e9" (princ-to-string (abs -2147483648)))) +(aeq 'abs 2147483647 (abs -2147483647)) +(aeq 'mod 1.0 (mod 13.0 4)) +(aeq 'mod 3.0 (mod -13.0 4)) +(aeq 'mod -3.0 (mod 13.0 -4)) +(aeq 'mod -1.0 (mod -13.0 -4)) +(aeq 'mod -3.0 (mod 13.0 -4)) +(aeq 'mod 1.0 (mod -12.5 1.5)) +(aeq 'mod 0.5 (mod 12.5 1.5)) + +#| Log and exp |# + +(aeq 'exp 2.7182818 (exp 1)) +(aeq 'exp 2.7182818 (exp 1.0)) +(aeq 'exp 0.36787945 (exp -1)) +(aeq 'exp 0.36787945 (exp -1.0)) +(aeq 'exp 0.36787945 (exp -1.0)) +(aeq 'log 0.0 (log 1.0)) +(aeq 'log 4.0 (log 16 2)) +(aeq 'log 4.0 (log 16.0 2)) +(aeq 'log 4.0 (log 16 2.0)) +(aeq 'log 4.0 (log 16.0 2.0)) +(aeq 'log 1.0 (log 2 2)) +(aeq 'log 1.0 (log 2.5 2.5)) +(aeq 'log 2.3025852 (log 10)) +(aeq 'log 2.3025852 (log 10)) +(aeq 'expt 1024 (expt 2 10)) +(aeq 'expt 1024.0 (expt 2.0 10.0)) +(aeq 'expt 1073741824 (expt 2 30)) +(aeq 'expt t (string= "2.14748e9" (princ-to-string (expt 2 31)))) +(aeq 'expt t (string= "4.29497e9" (princ-to-string (expt 2 32)))) +(aeq 'expt 1024 (expt -2 10)) +(aeq 'expt -2048 (expt -2 11)) + +#| Tests |# + +(aeq 'floatp nil (floatp 1)) +(aeq 'floatp nil (floatp nil)) +(aeq 'floatp t (floatp 2.3)) +(aeq 'integerp t (integerp 1)) +(aeq 'integerp nil (integerp nil)) +(aeq 'integerp nil (integerp 2.3)) + +#| error checks |# + +(aeq 'dolist nothing (ignore-errors (dolist 12 (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist () (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist (x) (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist (x nil x x) (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes 12 (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes () (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes (x) (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes (x 1 x x) (print x)))) +(aeq 'for-millis nothing (ignore-errors (for-millis 12 (print 12)))) +(aeq 'for-millis nothing (ignore-errors (for-millis (12 12) (print 12)))) +(aeq 'push nothing (ignore-errors (let ((a #*00000000)) (push 1 (aref a 1)) a))) +(aeq 'setf nothing (ignore-errors (let ((s "hello")) (setf (char s 5) #\x) s))) +(aeq 'setf nothing (ignore-errors (let ((s "hello")) (setf (char s 20) #\x) s))) + +#| errors |# + +(aeq 'errors 0 ers) + +"#) + +(defun run-tests (&optional usb) + (let ((name (cond + ((numberp usb) (format nil "/dev/cu.usbmodem~a" usb)) + ((eq usb :esp) "/dev/cu.SLAB_USBtoUART") + ((eq usb :ftdi) "/dev/cu.usbserial-A104OVGT") + ;((eq usb :maix) "/dev/cu.usbserial-495223D74D0") + ((eq usb :maix) "/dev/cu.usbserial-xel_sipeed0") + ((eq usb :dock) "/dev/cu.wchusbserial1410") + ((eq usb :teensy) "/dev/cu.usbmodem7705521") + (t usb))) + (speed 1)) + (flet ((serial-write-exp (string stream) + (write-string string stream) + (write-char #\newline stream)) + ;; + (echo (s) + (sleep speed) + (loop + (let ((c (read-char-no-hang s))) + (unless c (return)) + (unless (eq c #\return) (write-char c)))) + (format t "~%")) + ;; + (read-serial (s) + (sleep speed) + (let ((string (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t))) + (loop + (let ((c (read-char-no-hang s))) + (unless c (return string)) + (vector-push-extend c string)))))) + ;; + (with-open-stream (s (make-instance 'serial-stream :name name)) + (echo s) + (echo s) + (serial-write-exp "(defvar ers 0)" s) + (echo s) + (serial-write-exp + "(defun aeq (tst x y) + (unless (or + (and (floatp x) (floatp y) (< (abs (- x y)) 0.000005)) + (equal x y)) + (incf ers) + (format t \"~a=~a/~a~%\" tst x y)))" + s) + (echo s) + ;; + ;; tests + ;; + (with-input-from-string (str *tests*) + (loop + (let ((line (read-line str nil nil))) + (unless line (return)) + (serial-write-exp line s) + (let ((output (read-serial s))) + (let* ((m1 (position #\return output)) + (m2 (when m1 (position #\return output :start (+ 2 m1))))) + (cond + ((null m2) (format t "~a~%" output)) + ((string= (subseq output (+ 2 m1) m2) "nil") nil) + (t (format t "*** ~a: ~a~%" (subseq output (+ 2 m1) m2) (subseq output 0 m1))))))))))))) diff --git a/builder/Test Suites/AutoTester AVR Nano.lisp b/builder/Test Suites/AutoTester AVR Nano.lisp new file mode 100644 index 0000000..b30f3b0 --- /dev/null +++ b/builder/Test Suites/AutoTester AVR Nano.lisp @@ -0,0 +1,576 @@ +; uLisp Auto Tester + +; Sharp-double-quote + +(defun sharp-double-quote-reader (stream sub-char numarg) + (declare (ignore sub-char numarg)) + (let (chars) + (do ((prev (read-char stream) curr) + (curr (read-char stream) (read-char stream))) + ((and (char= prev #\") (char= curr #\#))) + (push prev chars)) + (coerce (nreverse chars) 'string))) + +(set-dispatch-macro-character + #\# #\" #'sharp-double-quote-reader) + +; do (run-tests) + +;;; ================================================================ + +(eval-when (:compile-toplevel :load-toplevel :execute) + (require "serial-port")) + +(cl:in-package "CL-USER") + +;;; ================================================================ +;;; Class SERIAL-STREAM + +(defclass serial-stream (stream:fundamental-character-input-stream + stream:fundamental-character-output-stream) + ((serial-port :initform nil + :initarg :serial-port + :accessor stream-serial-port))) + +(defmethod initialize-instance :after ((stream serial-stream) + &key name (baud-rate 9600) (data-bits 8) (stop-bits 1) (parity :none) + &allow-other-keys) + (unless (stream-serial-port stream) + (check-type name string) + (setf (stream-serial-port stream) + (serial-port:open-serial-port name + :baud-rate baud-rate + :data-bits data-bits + :stop-bits stop-bits + :parity parity)))) + +(defmethod stream-element-type ((stream serial-stream)) + 'character) + +(defmethod input-stream-p ((stream serial-stream)) + t) + +(defmethod output-stream-p ((stream serial-stream)) + t) + +;;; ================================================================ +;;; Input + +(defmethod stream:stream-read-char ((stream serial-stream)) + (serial-port:read-serial-port-char (stream-serial-port stream))) + +(defmethod stream:stream-read-char-no-hang ((stream serial-stream)) + (when (stream:stream-listen stream) + (stream:stream-read-char stream))) + +(defmethod stream:stream-listen ((stream serial-stream)) + (serial-port:serial-port-input-available-p (stream-serial-port stream))) + +(defmethod stream:stream-clear-input ((stream serial-stream)) + (loop while (stream:stream-listen stream) + do (stream:stream-read-char stream)) + nil) + + +;;; ================================================================ +;;; Output + +(defmethod stream:stream-write-char ((stream serial-stream) char) + (serial-port:write-serial-port-char char (stream-serial-port stream))) + +(defmethod stream:stream-write-string ((stream serial-stream) string &optional (start 0) (end (length string))) + (serial-port:write-serial-port-string string (stream-serial-port stream) t :start start :end end)) + +(defmethod stream:stream-force-output ((stream serial-stream)) + nil) + +(defmethod stream:stream-finish-output ((stream serial-stream)) + nil) + +(defmethod stream:stream-clear-output ((stream serial-stream)) + nil) + +(defmethod close :after ((stream serial-stream) &key abort) + (declare (ignorable abort)) + (serial-port:close-serial-port (stream-serial-port stream))) + +;;; ================================================================ +;;; Example + +(defparameter *tests* + +#"#| Symbols |# + +(aeq 'let 123 (let ((cat 123)) cat)) +(aeq 'let 79 (let ((ca% 79)) ca%)) +(aeq 'let 83 (let ((1- 83)) 1-)) +(aeq 'let 13 (let ((12a 13)) 12a)) +(aeq 'let 17 (let ((-1- 17)) -1-)) +(aeq 'let 66 (let ((abcdef 66)) abcdef)) +(aeq 'let 77 (let ((abcdefg 77)) abcdefg)) +(aeq 'let 88 (let ((abcdefgh 88)) abcdefgh)) +(aeq 'let 99 (let ((abcdefghi 99)) abcdefghi)) +(aeq 'let 1010 (let ((abcdefghij 1010)) abcdefghij)) +(aeq 'let "ab9" (princ-to-string 'ab9)) + +#| Arithmetic |# + +(aeq '* 9 (* -3 -3)) +(aeq '* 32580 (* 180 181)) +(aeq '* 1 (*)) +(aeq '+ 32767 (+ 32765 1 1)) +(aeq '+ 0 (+)) +(aeq '+ -2 (+ -1 -1)) +(aeq '- -4 (- 4)) +(aeq '- 0 (- 4 2 1 1)) +(aeq '/ 2 (/ 60 10 3)) +(aeq '1+ 2 (1+ 1)) +(aeq '1+ 0 (1+ -1)) +(aeq '1- 0 (1- 1)) + +#| Comparisons |# + +(aeq '< t (< -32768 32767)) +(aeq '< t (< -1 0)) +(aeq '< t (< 1 2 3 4)) +(aeq '< nil (< 1 2 2 4)) +(aeq '< t (<= 1 2 2 4)) +(aeq '< nil (<= 1 3 2 4)) +(aeq '< t (> 4 3 2 1)) +(aeq '< nil (> 4 2 2 1)) +(aeq '< t (>= 4 2 2 1)) +(aeq '< nil (>= 4 2 3 1)) +(aeq '< t (< 1)) +(aeq '< nil (< 1 3 2)) +(aeq '< nil (< -1 -2)) +(aeq '< nil (< 10 10)) +(aeq '<= t (<= 10 10)) +(aeq '= t (= 32767 32767)) +(aeq '>= t (>= 10 10)) +(aeq '>= nil (>= 9 10)) +(aeq '/= t (/= 1)) +(aeq '/= nil (/= 1 2 1)) +(aeq '/= nil (/= 1 2 3 1)) +(aeq '/= t (/= 1 2 3 4)) +(aeq 'plusp t (plusp 1)) +(aeq 'plusp nil (plusp 0)) +(aeq 'plusp nil (plusp -1)) +(aeq 'minusp nil (minusp 1)) +(aeq 'minusp nil (minusp 0)) +(aeq 'minusp t (minusp -1)) +(aeq 'zerop nil (zerop 1)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop -1)) +(aeq 'evenp nil (evenp 1)) +(aeq 'evenp t (evenp 0)) +(aeq 'evenp nil (evenp -1)) +(aeq 'oddp t (oddp 1)) +(aeq 'oddp nil (oddp 0)) +(aeq 'oddp t (oddp -1)) + +#| Maths functions |# + +(aeq 'abs 10 (abs 10)) +(aeq 'abs 10 (abs -10)) +(aeq 'max 45 (max 23 45)) +(aeq 'max -23 (max -23 -45)) +(aeq 'min 23 (min 23 45)) +(aeq 'min -45 (min -23 -45)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop 32767)) +(aeq 'mod 1 (mod 13 4)) +(aeq 'mod 3 (mod -13 4)) +(aeq 'mod -3 (mod 13 -4)) +(aeq 'mod -1 (mod -13 -4)) + +#| Number entry |# + +(aeq 'hex -1 #xFFFF) +(aeq 'hex 1 #x0001) +(aeq 'hex 4112 #x1010) +(aeq 'oct 511 #o777) +(aeq 'oct 1 #o1) +(aeq 'oct -1 #o177777) +(aeq 'bin -1 #b1111111111111111) +(aeq 'bin 10 #b1010) +(aeq 'bin 0 #b0) +(aeq 'bin 12 #'12) +(aeq 'bin 6 (funcall #'(lambda (x) (+ x 2)) 4)) + +#| Boolean |# + +(aeq 'and 7 (and t t 7)) +(aeq 'and nil (and t nil 7)) +(aeq 'or t (or t nil 7)) +(aeq 'or 1 (or 1 2 3)) +(aeq 'or nil (or nil nil nil)) +(aeq 'or 'a (or 'a 'b 'c)) +(aeq 'or 1 (let ((x 0)) (or (incf x)) x)) + +#| Bitwise |# + +(aeq 'logand -1 (logand)) +(aeq 'logand 170 (logand #xAA)) +(aeq 'logand 0 (logand #xAAAA #x5555)) +(aeq 'logior 0 (logior)) +(aeq 'logior 170 (logior #xAA)) +(aeq 'logior #xFFFF (logior #xAAAA #x5555)) +(aeq 'logxor 0 (logxor)) +(aeq 'logxor 170 (logior #xAA)) +(aeq 'logxor 255 (logxor #xAAAA #xAA55)) +(aeq 'lognot #x5555 (lognot #xAAAA)) +(aeq 'ash 492 (ash 123 2)) +(aeq 'ash #xFFFF (ash #xFFFF 0)) +(aeq 'ash #xFFFF (ash #xFFFF -2)) +(aeq 'ash -4 (ash #xFFFF 2)) +(aeq 'ash 8191 (ash #x7FFF -2)) +(aeq 'logbitp t (logbitp 0 1)) +(aeq 'logbitp t (logbitp 1000 -1)) +(aeq 'logbitp nil (logbitp 1000 0)) + +#| Tests |# + +(aeq 'atom t (atom nil)) +(aeq 'atom t (atom t)) +(aeq 'atom nil (atom '(1 2))) +(aeq 'consp nil (consp 'b)) +(aeq 'consp t (consp '(a b))) +(aeq 'consp nil (consp nil)) +(aeq 'listp nil (listp 'b)) +(aeq 'listp t (listp '(a b))) +(aeq 'listp t (listp nil)) +(aeq 'numberp t (numberp (+ 1 2))) +(aeq 'numberp nil (numberp 'b)) +(aeq 'numberp nil (numberp nil)) +(aeq 'symbolp t (symbolp 'b)) +(aeq 'symbolp nil (symbolp 3)) +(aeq 'symbolp t (symbolp nil)) +(aeq 'streamp nil (streamp 'b)) +(aeq 'streamp nil (streamp nil)) +(aeq 'boundp t (let (x) (boundp 'x))) +(aeq 'boundp nil (let (x) (boundp 'y))) + +#| cxr operations |# + +(aeq 'car 'a (car '(a b c))) +(aeq 'car nil (car nil)) +(aeq 'first 'a (first '(a b c))) +(aeq 'first nil (first nil)) +(aeq 'cdr 'b (cdr '(a . b))) +(aeq 'cdr 'b (car (cdr '(a b)))) +(aeq 'cdr nil (cdr nil)) +(aeq 'rest 'b (rest '(a . b))) +(aeq 'rest 'b (car (rest '(a b)))) +(aeq 'rest nil (rest nil)) +(aeq 'caaar 'a (caaar '(((a))))) +(aeq 'caaar 'nil (caaar nil)) +(aeq 'caadr 'b (caadr '(a (b)))) +(aeq 'caadr 'nil (caadr nil)) +(aeq 'caar 'a (caar '((a)))) +(aeq 'caar 'nil (caar nil)) +(aeq 'cadar 'c (cadar '((a c) (b)))) +(aeq 'cadar 'nil (cadar nil)) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'cadr 'b (cadr '(a b))) +(aeq 'second 'nil (second '(a))) +(aeq 'second 'b (second '(a b))) +(aeq 'cadr 'nil (cadr '(a))) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'third 'c (third '(a b c))) +(aeq 'third 'nil (third nil)) +(aeq 'cdaar 'b (car (cdaar '(((a b)) b c)))) +(aeq 'cdaar 'nil (cdaar nil)) +(aeq 'cdadr 'c (car (cdadr '(a (b c))))) +(aeq 'cdadr 'nil (cdadr nil)) +(aeq 'cdar 'b (car (cdar '((a b c))))) +(aeq 'cdar 'nil (cdar nil)) +(aeq 'cddar 'c (car (cddar '((a b c))))) +(aeq 'cddar 'nil (cddar nil)) +(aeq 'cdddr 'd (car (cdddr '(a b c d)))) +(aeq 'cdddr nil (car (cdddr '(a b c)))) +(aeq 'cddr 'c (car (cddr '(a b c)))) +(aeq 'cddr 'nil (cddr '(a))) + +#| List operations |# + +(aeq 'cons 'a (car (cons 'a 'b))) +(aeq 'cons nil (car (cons nil 'b))) +(aeq 'append 6 (length (append '(a b c) '(d e f)))) +(aeq 'append nil (append nil nil)) +(aeq 'append '(1 2 3 4 5 . 6) (append '(1 2 3) '(4 5 . 6))) +(aeq 'list nil (car (list nil))) +(aeq 'list 'a (car (list 'a 'b 'c))) +(aeq 'reverse 'c (car (reverse '(a b c)))) +(aeq 'reverse nil (reverse nil)) +(aeq 'length 0 (length nil)) +(aeq 'length 4 (length '(a b c d))) +(aeq 'length 2 (length '(nil nil))) +(aeq 'assoc nil (assoc 'b nil)) +(aeq 'assoc nil (assoc 'b '(nil nil))) +(aeq 'assoc '(b . 12) (assoc 'b '((a . 10) (b . 12)))) +(aeq 'assoc '(nil . 12) (assoc nil '((a . 10) (nil . 12)))) +(aeq 'assoc '(b) (assoc 'b '((a . 10) (b)))) +(aeq 'mapc 2 (cadr (mapc + '(1 2 3 4)))) +(aeq 'mapc 10 (let ((x 0)) (mapc (lambda (y) (incf x y)) '(1 2 3 4)) x)) +(aeq 'mapcar '(1 4 9 16) (mapcar (lambda (x) (* x x)) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16) (mapcar * '(1 2 3 4) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16 25) (mapcar (lambda (x) (* x x)) '(1 2 3 4 5))) +(aeq 'mapcan '(1 4 2 5 3 6) (mapcan #'list '(1 2 3) '(4 5 6))) +(aeq 'mapcan '(1 3 2 4) (mapcan list '(1 2) '(3 4))) +(aeq 'mapcan '(1 5 9 2 6 10 3 7 11) (mapcan list '(1 2 3 4) '(5 6 7 8) '(9 10 11))) +(aeq 'mapcan '(1 2 3 . 4) (mapcan (lambda (x) x) '((1) (2) (3 . 4)))) +(aeq 'mapcan '(2 3 . 4) (mapcan (lambda (x) x) '(nil (2) (3 . 4)))) + +#| let/let*/lambda |# + +(aeq 'let 7 (let ((x 7)) (let ((x 6) (y x)) y))) +(aeq 'let* 6 (let* ((x 7)) (let* ((x 6) (y x)) y))) +(aeq 'let t (let ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'let* t (let* ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'lambda 2 ((lambda (x y) (setq y x) y) 2 3)) +(aeq 'lambda 9 ((lambda (&rest x) (apply + x)) 2 3 4)) +(aeq 'lambda 8 ((lambda (x &optional (y 4)) (* x y)) 2)) +(aeq 'lambda 6 ((lambda (x &optional (y 4)) (* x y)) 2 3)) +(aeq 'lambda 6 ((lambda (x &optional y) (* x y)) 2 3)) +(aeq 'lambda 123 ((lambda (list) list) 123)) + +#| loops and control |# + +(aeq 'progn 8 (let ((x 6)) (progn (incf x) (incf x)))) +(aeq 'dotimes 21 (let ((x 6)) (dotimes (y 6 x) (setq x (+ x y))))) +(aeq 'dotimes 6 (let ((x 6)) (dotimes (y 6 y) (setq x (+ x y))))) +(aeq 'dotimes 0 (let ((x 6)) (dotimes (y 0 y) (setq x (+ x y))))) +(aeq 'dolist 6 (let ((x 0)) (dolist (y '(1 2 3) x) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3)) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3) y) (setq x (+ x y))))) +(aeq 'loop 6 (let ((x 0)) (loop (when (= x 6) (return x)) (incf x)))) +(aeq 'loop 6 (let ((x 0)) (loop (unless (< x 6) (return x)) (incf x)))) + +#| conditions |# + +(aeq 'if 3 (let ((a 2)) (if (= a 2) 3 4))) +(aeq 'if 4 (let ((a 2)) (if (= a 3) 3 4))) +(aeq 'if 4 (let ((a 3)) (if (= a 3) 4))) +(aeq 'if nil (let ((a 4)) (if (= a 3) 4))) +(aeq 'when 4 (let ((a 3)) (when (= a 3) 4))) +(aeq 'when nil (let ((a 2)) (when (= a 3) 4))) +(aeq 'unless nil (let ((a 3)) (unless (= a 3) 4))) +(aeq 'unless 4 (let ((a 2)) (unless (= a 3) 4))) +(aeq 'cond 8 (let ((a 2)) (cond ((= a 3) 7) ((= a 2) 8) (t 9)))) +(aeq 'cond 9 (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8) (9)))) +(aeq 'cond nil (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8)))) +(aeq 'cond 12 (car (cond ((evenp 3) (list (* 2 3))) ((list (* 3 4)))))) +(aeq 'case 222 (let ((j 1)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 333 (let ((j t)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 444 (let ((j 2)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) + +#| eval/funcall/apply |# + +(aeq 'funcall 10 (funcall + 1 2 3 4)) +(aeq 'funcall 'a (funcall car '(a b c d))) +(aeq 'funcall 3 (let ((x 0)) (funcall (lambda (y) (incf x y)) 3) x)) +(aeq 'apply 10 (apply + '(1 2 3 4))) +(aeq 'apply 13 (apply + 1 2 '(1 2 3 4))) +(aeq 'eval 10 (eval (list + 1 2 3 4))) +(aeq 'eval nil (eval nil)) +(aeq 'funcall 999 (let ((x 999)) (funcall (lambda (x) x) x))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (funcall fun (funcall fun x)))))) (funcall (x2 '1+) 2))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (fun (fun x)))))) ((x2 '1+) 2))) +(aeq 'apply 5 (let* ((my (lambda (x y) (+ x y))) (han '(my))) (apply (first han) '(2 3)))) + +#| in-place operations |# + +(aeq 'incf 6 (let ((x 0)) (+ (incf x) (incf x) (incf x)))) +(aeq 'incf 12 (let ((x 0)) (+ (incf x 2) (incf x 2) (incf x 2)))) +(aeq 'incf 36 (let ((n 10)) (let* ((f1 (lambda () (incf n) n))) (+ (funcall f1) (funcall f1) (funcall f1))))) +(aeq 'setf 25 (let ((a 3) (b 4)) (setf a (* a 3) b (* b 4)) (+ a b))) +(aeq 'setf 9 (let ((a '(2 3))) (setf (car a) 6) (apply + a))) +(aeq 'setf 12 (let ((a '(2 3))) (setf (cdr a) '(6)) (apply * a))) +(aeq 'setf 220 (let ((a '(2 3 4))) (setf (nth 1 a) 11 (nth 2 a) 10) (apply * a))) + +#| recursion |# + +(aeq 'lambda 55 (let ((fib (lambda (n) (if (< n 3) 1 (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))) +(aeq 'lambda 5040 (let ((f (lambda (n) (if (= n 0) 1 (* n (f (- n 1))))))) (f 7))) +(aeq 'lambda 10 (let ((a 0)) (let ((f (lambda (n) (incf a n) (when (> n 0) (f (1- n)))))) (f 4)) a)) + +#| streams |# + +(aeq 'stream "" (with-output-to-string (s) (princ s s))) +(aeq 'stream "12 23 34" (with-output-to-string (st) (format st "~a ~a ~a" 12 23 34))) +(aeq 'pprint 8313 (let ((n 0) (st (with-output-to-string (str) (pprint aeq str)))) (dotimes (i (length st) n) (incf n (char-code (char st i)))))) + +#| printing |# + +(aeq 'princ "hello" (princ-to-string "hello")) +(aeq 'princ "hello \"David\"" (princ-to-string "hello \"David\"")) +(aeq 'prin1 "\"hello\"" (prin1-to-string "hello")) +(aeq 'prin1 "\"hello \\\"David\\\"\"" (prin1-to-string "hello \"David\"")) + +#| format |# + +(aeq 'format "hello" (format nil "hello")) +(aeq 'format "Hello23Goodbye" (format nil "Hello~aGoodbye" 23)) +(aeq 'format " 17" (format nil "~5x" 23)) +(aeq 'format " 10111" (format nil "~6b" 23)) +(aeq 'format " 17 23 23 " (format nil "~5x ~5d ~5a" 23 23 23)) +(aeq 'format "00017 00023" (format nil "~5,'0x ~5,'0d" 23 23)) +(aeq 'format "01-45-07" (format nil "~2,'0d-~2,'0d-~2,'0d" 1 45 7)) +(aeq 'format "Hello42" (format nil "Hello~a" 42)) +(aeq 'format "[1,2,3]" (format nil "[~{~a~^,~}]" '(1 2 3))) +(aeq 'format "nil nil" (format nil "~a ~{ ~a ~} ~a" nil nil nil)) + +#| strings |# + +(aeq 'stringp t (stringp "hello")) +(aeq 'stringp nil (stringp 5)) +(aeq 'stringp nil (stringp '(a b))) +(aeq 'numberp nil (numberp "hello")) +(aeq 'atom t (atom "hello")) +(aeq 'consp nil (consp "hello")) +(aeq 'eq nil (eq "hello" "hello")) +(aeq 'eq t (let ((a "hello")) (eq a a))) +(aeq 'length 0 (length "")) +(aeq 'length 5 (length "hello")) +(aeq 'subseq "hello" (subseq "hellofromdavid" 0 5)) +(aeq 'subseq "fromdavid" (subseq "hellofromdavid" 5)) +(aeq 'concatenate t (string= (concatenate 'string "A" "B") "AB")) +(aeq 'concatenate 3 (length (concatenate 'string "A" "BC"))) +(aeq 'concatenate 0 (length (concatenate 'string))) +(aeq 'concatenate "ABCD" (concatenate 'string "AB" "CD")) +(aeq 'concatenate "ABCDE" (concatenate 'string "AB" "CDE")) +(aeq 'concatenate "ABCDE" (concatenate 'string "ABC" "DE")) +(aeq 'concatenate "ABCDEF" (concatenate 'string "ABC" "DEF")) +(aeq 'string< nil (string< "cat" "cat")) +(aeq 'string< t (string< "cat" "cat ")) +(aeq 'string< t (string< "fish" "fish ")) +(aeq 'string> nil (string> "cat" "cat")) +(aeq 'string> t (string> "cat " "cat")) +(aeq 'string "albatross" (string "albatross")) +(aeq 'string "x" (string #\x)) +(aeq 'string "cat" (string 'cat)) +(aeq 'string "albatross" (string 'albatross)) + +#| characters |# + +(aeq 'char-code 97 (char-code #\a)) +(aeq 'char-code 13 (char-code #\return)) +(aeq 'char-code 255 (char-code #\255)) +(aeq 'code-char #\return (code-char 13)) +(aeq 'code-char #\a (code-char 97)) +(aeq 'code-char #\255 (code-char 255)) +(aeq 'eq t (eq #\b #\b)) +(aeq 'eq nil (eq #\b #\B)) +(aeq 'numberp nil (numberp #\b)) +(aeq 'characterp t (characterp #\b)) +(aeq 'char #\o (char "hello" 4)) +(aeq 'char #\h (char "hello" 0)) +(aeq 'char "A" (princ-to-string (code-char 65))) +(aeq 'char "[#\\Bell]" (format nil "[~s]" (code-char 7))) +(aeq 'char "[#\\Return]" (format nil "[~s]" #\return)) +(aeq 'char "[#\\127]" (format nil "[~s]" #\127)) +(aeq 'char "[#\\255]" (format nil "[~s]" #\255)) + +#| read-from-string |# + +(aeq 'read-from-string 123 (read-from-string "123")) +(aeq 'read-from-string 144 (eval (read-from-string "((lambda (x) (* x x)) 12)"))) +(aeq 'read-from-string t (eval (read-from-string "(eq (+ 2 3) 5)"))) +(aeq 'read-from-string nil (read-from-string "()")) + +#| closures |# + +(aeq 'closure 'lex (let ((lex nil)) (funcall (let ((lex t)) (lambda () (if lex 'lex 'dyn)))))) +(aeq 'closure 103 (let* ((c 100) (two (lambda (d) (+ c d))) (one (lambda (c) (funcall two 3)))) (funcall one 1))) +(aeq 'closure 4 (let ((x 0)) (funcall (lambda (y) (incf x y)) 4) x)) +(aeq 'closure 0 (let ((x 0)) (funcall (let ((x 7)) (lambda (y) (setq x (+ x y) ))) 4) x)) +(aeq 'closure '(8 10 13 17) (let ((x 0) (clo (lambda () (let ((x 7)) (lambda (y) (incf x y)))))) (mapcar (funcall clo) '(1 2 3 4)))) +(aeq 'closure 3 (let ((y 0) (tst (lambda (x) (+ x 1)))) (dotimes (x 3 y) (progn (tst (+ x 2))) (incf y x)))) + +#| repl |# + +(aeq 'repl 23 (read-from-string "23(2)")) +(aeq 'repl nil (read-from-string "()23")) +(aeq 'repl 23 (read-from-string "23\"Hi\"")) +(aeq 'repl "Hi" (read-from-string "\"Hi\"23")) +(aeq 'repl #\1 (read-from-string " #\\1\"Hi\"")) +(aeq 'repl "Hi" (read-from-string (format nil "\"Hi\"~a~a" #\# "*0101"))) + +#| subseq/equal |# + +(aeq 'subseq '(2 3 4) (subseq '(0 1 2 3 4) 2)) +(aeq 'subseq '(2) (subseq '(0 1 2 3 4) 2 3)) +(aeq 'subseq nil (subseq '() 0)) +(aeq 'equal t (equal '(1 2 3) '(1 2 3))) +(aeq 'equal t (equal '(1 2 (4) 3) '(1 2 (4) 3))) +(aeq 'equal nil (equal '(1 2 (4) 3) '(1 2 (4 nil) 3))) +(aeq 'equal t (equal "cat" "cat")) +(aeq 'equal nil (equal "cat" "Cat")) +(aeq 'equal t (equal 'cat 'Cat)) +(aeq 'equal t (equal 2 (+ 1 1))) + +#| keywords |# + +(aeq 'keywordp t (keywordp :led-builtin)) +(aeq 'keywordp nil (keywordp print)) +(aeq 'keywordp nil (keywordp nil)) +(aeq 'keywordp nil (keywordp 12)) + +"#) + +(defun run-tests (&optional (usb 1411)) ; "/dev/cu.usbserial-A104OVGT")) ; + (let ((name (cond + ((numberp usb) (format nil "/dev/cu.usbmodem~a" usb)) + ((eq usb :badge) "/dev/cu.usbserial-A104OVGT") ; "/dev/cu.usbserial-A602TRZF" + ((eq usb :star) "/dev/cu.usbserial-A10JYSPG") + (t usb))) + (speed 0.5)) + (flet ((serial-write-exp (string stream) + (write-string string stream) + (write-char #\newline stream)) + ;; + (echo (s) + (sleep speed) + (loop + (let ((c (read-char-no-hang s))) + (unless c (return)) + (unless (eq c #\return) (write-char c)))) + (format t "~%")) + ;; + (read-serial (s) + (sleep speed) + (let ((string (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t))) + (loop + (let ((c (read-char-no-hang s))) + (unless c (return string)) + (vector-push-extend c string)))))) + ;; + (with-open-stream (s (make-instance 'serial-stream :name name)) + (echo s) + (echo s) + (serial-write-exp "(defvar ers 0)" s) + (echo s) + (serial-write-exp + "(defun aeq (tst x y) + (unless (equal x y) + (incf ers) + (format t \"~a=~a/~a~%\" tst x y)))" + s) + (echo s) + ;; + ;; tests + ;; + (with-input-from-string (str *tests*) + (loop + (let ((line (read-line str nil nil))) + (unless line (return)) + (serial-write-exp line s) + (let ((output (read-serial s))) + (let* ((m1 (position #\return output)) + (m2 (when m1 (position #\return output :start (+ 2 m1))))) + (cond + ((null m2) (format t "~a~%" output)) + ((string= (subseq output (+ 2 m1) m2) "nil") nil) + (t (format t "*** ~a: ~a~%" (subseq output (+ 2 m1) m2) (subseq output 0 m1))))))))))))) \ No newline at end of file diff --git a/builder/Test Suites/AutoTester AVR.lisp b/builder/Test Suites/AutoTester AVR.lisp new file mode 100644 index 0000000..ce8909e --- /dev/null +++ b/builder/Test Suites/AutoTester AVR.lisp @@ -0,0 +1,700 @@ +; uLisp Auto Tester + +; Sharp-double-quote + +(defun sharp-double-quote-reader (stream sub-char numarg) + (declare (ignore sub-char numarg)) + (let (chars) + (do ((prev (read-char stream) curr) + (curr (read-char stream) (read-char stream))) + ((and (char= prev #\") (char= curr #\#))) + (push prev chars)) + (coerce (nreverse chars) 'string))) + +(set-dispatch-macro-character + #\# #\" #'sharp-double-quote-reader) + +; do (run-tests) + +;;; ================================================================ + +(eval-when (:compile-toplevel :load-toplevel :execute) + (require "serial-port")) + +(cl:in-package "CL-USER") + +;;; ================================================================ +;;; Class SERIAL-STREAM + +(defclass serial-stream (stream:fundamental-character-input-stream + stream:fundamental-character-output-stream) + ((serial-port :initform nil + :initarg :serial-port + :accessor stream-serial-port))) + +(defmethod initialize-instance :after ((stream serial-stream) + &key name (baud-rate 9600) (data-bits 8) (stop-bits 1) (parity :none) + &allow-other-keys) + (unless (stream-serial-port stream) + (check-type name string) + (setf (stream-serial-port stream) + (serial-port:open-serial-port name + :baud-rate baud-rate + :data-bits data-bits + :stop-bits stop-bits + :parity parity)))) + +(defmethod stream-element-type ((stream serial-stream)) + 'character) + +(defmethod input-stream-p ((stream serial-stream)) + t) + +(defmethod output-stream-p ((stream serial-stream)) + t) + +;;; ================================================================ +;;; Input + +(defmethod stream:stream-read-char ((stream serial-stream)) + (serial-port:read-serial-port-char (stream-serial-port stream))) + +(defmethod stream:stream-read-char-no-hang ((stream serial-stream)) + (when (stream:stream-listen stream) + (stream:stream-read-char stream))) + +(defmethod stream:stream-listen ((stream serial-stream)) + (serial-port:serial-port-input-available-p (stream-serial-port stream))) + +(defmethod stream:stream-clear-input ((stream serial-stream)) + (loop while (stream:stream-listen stream) + do (stream:stream-read-char stream)) + nil) + + +;;; ================================================================ +;;; Output + +(defmethod stream:stream-write-char ((stream serial-stream) char) + (serial-port:write-serial-port-char char (stream-serial-port stream))) + +(defmethod stream:stream-write-string ((stream serial-stream) string &optional (start 0) (end (length string))) + (serial-port:write-serial-port-string string (stream-serial-port stream) t :start start :end end)) + +(defmethod stream:stream-force-output ((stream serial-stream)) + nil) + +(defmethod stream:stream-finish-output ((stream serial-stream)) + nil) + +(defmethod stream:stream-clear-output ((stream serial-stream)) + nil) + +(defmethod close :after ((stream serial-stream) &key abort) + (declare (ignorable abort)) + (serial-port:close-serial-port (stream-serial-port stream))) + +;;; ================================================================ +;;; Example + +(defparameter *tests* + +#"#| Symbols |# + +(aeq 'let 123 (let ((cat 123)) cat)) +(aeq 'let 79 (let ((ca% 79)) ca%)) +(aeq 'let 83 (let ((1- 83)) 1-)) +(aeq 'let 13 (let ((12a 13)) 12a)) +(aeq 'let 17 (let ((-1- 17)) -1-)) +(aeq 'let 66 (let ((abcdef 66)) abcdef)) +(aeq 'let 77 (let ((abcdefg 77)) abcdefg)) +(aeq 'let 88 (let ((abcdefgh 88)) abcdefgh)) +(aeq 'let 99 (let ((abcdefghi 99)) abcdefghi)) +(aeq 'let 1010 (let ((abcdefghij 1010)) abcdefghij)) +(aeq 'let "ab9" (princ-to-string 'ab9)) +(aeq 'let t (eq 'me 'me)) +(aeq 'let t (eq 'fishcake 'fishcake)) +(aeq 'let nil (eq 'fishcak 'fishca)) + +#| Arithmetic |# + +(aeq '* 9 (* -3 -3)) +(aeq '* 32580 (* 180 181)) +(aeq '* 1 (*)) +(aeq '+ 32767 (+ 32765 1 1)) +(aeq '+ 0 (+)) +(aeq '+ -2 (+ -1 -1)) +(aeq '- -4 (- 4)) +(aeq '- 0 (- 4 2 1 1)) +(aeq '/ 2 (/ 60 10 3)) +(aeq '1+ 2 (1+ 1)) +(aeq '1+ 0 (1+ -1)) +(aeq '1- 0 (1- 1)) + +#| Comparisons |# + +(aeq '< t (< -32768 32767)) +(aeq '< t (< -1 0)) +(aeq '< t (< 1 2 3 4)) +(aeq '< nil (< 1 2 2 4)) +(aeq '< t (<= 1 2 2 4)) +(aeq '< nil (<= 1 3 2 4)) +(aeq '< t (> 4 3 2 1)) +(aeq '< nil (> 4 2 2 1)) +(aeq '< t (>= 4 2 2 1)) +(aeq '< nil (>= 4 2 3 1)) +(aeq '< t (< 1)) +(aeq '< nil (< 1 3 2)) +(aeq '< nil (< -1 -2)) +(aeq '< nil (< 10 10)) +(aeq '<= t (<= 10 10)) +(aeq '= t (= 32767 32767)) +(aeq '>= t (>= 10 10)) +(aeq '>= nil (>= 9 10)) +(aeq '/= t (/= 1)) +(aeq '/= nil (/= 1 2 1)) +(aeq '/= nil (/= 1 2 3 1)) +(aeq '/= t (/= 1 2 3 4)) +(aeq 'plusp t (plusp 1)) +(aeq 'plusp nil (plusp 0)) +(aeq 'plusp nil (plusp -1)) +(aeq 'minusp nil (minusp 1)) +(aeq 'minusp nil (minusp 0)) +(aeq 'minusp t (minusp -1)) +(aeq 'zerop nil (zerop 1)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop -1)) +(aeq 'evenp nil (evenp 1)) +(aeq 'evenp t (evenp 0)) +(aeq 'evenp nil (evenp -1)) +(aeq 'oddp t (oddp 1)) +(aeq 'oddp nil (oddp 0)) +(aeq 'oddp t (oddp -1)) + +#| Maths functions |# + +(aeq 'abs 10 (abs 10)) +(aeq 'abs 10 (abs -10)) +(aeq 'max 45 (max 23 45)) +(aeq 'max -23 (max -23 -45)) +(aeq 'min 23 (min 23 45)) +(aeq 'min -45 (min -23 -45)) +(aeq 'zerop t (zerop 0)) +(aeq 'zerop nil (zerop 32767)) +(aeq 'mod 1 (mod 13 4)) +(aeq 'mod 3 (mod -13 4)) +(aeq 'mod -3 (mod 13 -4)) +(aeq 'mod -1 (mod -13 -4)) + +#| Number entry |# + +(aeq 'hex -1 #xFFFF) +(aeq 'hex 1 #x0001) +(aeq 'hex 4112 #x1010) +(aeq 'oct 511 #o777) +(aeq 'oct 1 #o1) +(aeq 'oct -1 #o177777) +(aeq 'bin -1 #b1111111111111111) +(aeq 'bin 10 #b1010) +(aeq 'bin 0 #b0) +(aeq 'hash 12 #'12) +(aeq 'hash 6 (funcall #'(lambda (x) (+ x 2)) 4)) + +#| Boolean |# + +(aeq 'and 7 (and t t 7)) +(aeq 'and nil (and t nil 7)) +(aeq 'or t (or t nil 7)) +(aeq 'or 1 (or 1 2 3)) +(aeq 'or nil (or nil nil nil)) +(aeq 'or 'a (or 'a 'b 'c)) +(aeq 'or 1 (let ((x 0)) (or (incf x)) x)) + +#| Bitwise |# + +(aeq 'logand -1 (logand)) +(aeq 'logand 170 (logand #xAA)) +(aeq 'logand 0 (logand #xAAAA #x5555)) +(aeq 'logior 0 (logior)) +(aeq 'logior 170 (logior #xAA)) +(aeq 'logior #xFFFF (logior #xAAAA #x5555)) +(aeq 'logxor 0 (logxor)) +(aeq 'logxor 170 (logior #xAA)) +(aeq 'logxor 255 (logxor #xAAAA #xAA55)) +(aeq 'lognot #x5555 (lognot #xAAAA)) +(aeq 'ash 492 (ash 123 2)) +(aeq 'ash #xFFFF (ash #xFFFF 0)) +(aeq 'ash #xFFFF (ash #xFFFF -2)) +(aeq 'ash -4 (ash #xFFFF 2)) +(aeq 'ash 8191 (ash #x7FFF -2)) +(aeq 'logbitp t (logbitp 0 1)) +(aeq 'logbitp t (logbitp 1000 -1)) +(aeq 'logbitp nil (logbitp 1000 0)) + +#| Tests |# + +(aeq 'atom t (atom nil)) +(aeq 'atom t (atom t)) +(aeq 'atom nil (atom '(1 2))) +(aeq 'consp nil (consp 'b)) +(aeq 'consp t (consp '(a b))) +(aeq 'consp nil (consp nil)) +(aeq 'listp nil (listp 'b)) +(aeq 'listp t (listp '(a b))) +(aeq 'listp t (listp nil)) +(aeq 'numberp t (numberp (+ 1 2))) +(aeq 'numberp nil (numberp 'b)) +(aeq 'numberp nil (numberp nil)) +(aeq 'symbolp t (symbolp 'b)) +(aeq 'symbolp nil (symbolp 3)) +(aeq 'symbolp t (symbolp nil)) +(aeq 'streamp nil (streamp 'b)) +(aeq 'streamp nil (streamp nil)) +(aeq 'boundp t (let (x) (boundp 'x))) +(aeq 'boundp nil (let (x) (boundp 'y))) + +#| cxr operations |# + +(aeq 'car 'a (car '(a b c))) +(aeq 'car nil (car nil)) +(aeq 'first 'a (first '(a b c))) +(aeq 'first nil (first nil)) +(aeq 'cdr 'b (cdr '(a . b))) +(aeq 'cdr 'b (car (cdr '(a b)))) +(aeq 'cdr nil (cdr nil)) +(aeq 'rest 'b (rest '(a . b))) +(aeq 'rest 'b (car (rest '(a b)))) +(aeq 'rest nil (rest nil)) +(aeq 'caaar 'a (caaar '(((a))))) +(aeq 'caaar 'nil (caaar nil)) +(aeq 'caadr 'b (caadr '(a (b)))) +(aeq 'caadr 'nil (caadr nil)) +(aeq 'caar 'a (caar '((a)))) +(aeq 'caar 'nil (caar nil)) +(aeq 'cadar 'c (cadar '((a c) (b)))) +(aeq 'cadar 'nil (cadar nil)) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'cadr 'b (cadr '(a b))) +(aeq 'second 'nil (second '(a))) +(aeq 'second 'b (second '(a b))) +(aeq 'cadr 'nil (cadr '(a))) +(aeq 'caddr 'c (caddr '(a b c))) +(aeq 'caddr 'nil (caddr nil)) +(aeq 'third 'c (third '(a b c))) +(aeq 'third 'nil (third nil)) +(aeq 'cdaar 'b (car (cdaar '(((a b)) b c)))) +(aeq 'cdaar 'nil (cdaar nil)) +(aeq 'cdadr 'c (car (cdadr '(a (b c))))) +(aeq 'cdadr 'nil (cdadr nil)) +(aeq 'cdar 'b (car (cdar '((a b c))))) +(aeq 'cdar 'nil (cdar nil)) +(aeq 'cddar 'c (car (cddar '((a b c))))) +(aeq 'cddar 'nil (cddar nil)) +(aeq 'cdddr 'd (car (cdddr '(a b c d)))) +(aeq 'cdddr nil (car (cdddr '(a b c)))) +(aeq 'cddr 'c (car (cddr '(a b c)))) +(aeq 'cddr 'nil (cddr '(a))) + +#| List operations |# + +(aeq 'cons 'a (car (cons 'a 'b))) +(aeq 'cons nil (car (cons nil 'b))) +(aeq 'append 6 (length (append '(a b c) '(d e f)))) +(aeq 'append nil (append nil nil)) +(aeq 'append '(1 2 3 4 5 . 6) (append '(1 2 3) '(4 5 . 6))) +(aeq 'list nil (car (list nil))) +(aeq 'list 'a (car (list 'a 'b 'c))) +(aeq 'reverse 'c (car (reverse '(a b c)))) +(aeq 'reverse nil (reverse nil)) +(aeq 'length 0 (length nil)) +(aeq 'length 4 (length '(a b c d))) +(aeq 'length 2 (length '(nil nil))) +(aeq 'assoc nil (assoc 'b nil)) +(aeq 'assoc nil (assoc 'b '(nil nil))) +(aeq 'assoc '(b . 12) (assoc 'b '((a . 10) (b . 12)))) +(aeq 'assoc '(nil . 12) (assoc nil '((a . 10) (nil . 12)))) +(aeq 'assoc '(b) (assoc 'b '((a . 10) (b)))) +(aeq 'assoc '("three" . 3) (assoc "three" '(("one" . 1) ("two" . 2) ("three" . 3)) :test string=)) +(aeq 'member '(3 4) (member 3 '(1 2 3 4))) +(aeq 'member nil (member 5 '(1 2 3 4))) +(aeq 'member '(3 4) (member 3 '(1 2 3 4) :test eq)) +(aeq 'member '("three" "four") (member "three" '("one" "two" "three" "four") :test string=)) +(aeq 'member '("two" "three" "four") (member "three" '("one" "two" "three" "four") :test string<)) + +#| map operations |# + +(aeq 'mapc 2 (cadr (mapc + '(1 2 3 4)))) +(aeq 'mapc 10 (let ((x 0)) (mapc (lambda (y) (incf x y)) '(1 2 3 4)) x)) +(aeq 'mapcar '(1 4 9 16) (mapcar (lambda (x) (* x x)) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16) (mapcar * '(1 2 3 4) '(1 2 3 4))) +(aeq 'mapcar '(1 4 9 16 25) (mapcar (lambda (x) (* x x)) '(1 2 3 4 5))) +(aeq 'mapcan '(1 4 2 5 3 6) (mapcan #'list '(1 2 3) '(4 5 6))) +(aeq 'mapcan '(1 3 2 4) (mapcan list '(1 2) '(3 4))) +(aeq 'mapcan '(1 5 9 2 6 10 3 7 11) (mapcan list '(1 2 3 4) '(5 6 7 8) '(9 10 11))) +(aeq 'mapcan '(1 2 3 . 4) (mapcan (lambda (x) x) '((1) (2) (3 . 4)))) +(aeq 'mapcan '(2 3 . 4) (mapcan (lambda (x) x) '(nil (2) (3 . 4)))) +(aeq 'maplist '(((1 2 3) 6 7 8) ((2 3) 7 8) ((3) 8)) (maplist #'cons '(1 2 3) '(6 7 8))) +(aeq 'maplist '(1 2 3) (mapl #'cons '(1 2 3) '(6 7 8))) +(aeq 'mapcan '(3 7 11) (mapcon (lambda (x) (when (eq (first x) (second x)) (list (car x)))) '(1 2 3 3 5 7 7 8 9 11 11))) + + +#| let/let*/lambda |# + +(aeq 'let 7 (let ((x 7)) (let ((x 6) (y x)) y))) +(aeq 'let* 6 (let* ((x 7)) (let* ((x 6) (y x)) y))) +(aeq 'let t (let ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'let* t (let* ((x t) (y nil) (w) z) (and x (null y) (null w) (null z)))) +(aeq 'lambda 2 ((lambda (x y) (setq y x) y) 2 3)) +(aeq 'lambda 9 ((lambda (&rest x) (apply + x)) 2 3 4)) +(aeq 'lambda 8 ((lambda (x &optional (y 4)) (* x y)) 2)) +(aeq 'lambda 6 ((lambda (x &optional (y 4)) (* x y)) 2 3)) +(aeq 'lambda 6 ((lambda (x &optional y) (* x y)) 2 3)) +(aeq 'lambda 123 ((lambda (list) list) 123)) + +#| loops and control |# + +(aeq 'progn 8 (let ((x 6)) (progn (incf x) (incf x)))) +(aeq 'dotimes 21 (let ((x 6)) (dotimes (y 6 x) (setq x (+ x y))))) +(aeq 'dotimes 6 (let ((x 6)) (dotimes (y 6 y) (setq x (+ x y))))) +(aeq 'dotimes 0 (let ((x 6)) (dotimes (y 0 y) (setq x (+ x y))))) +(aeq 'dolist 6 (let ((x 0)) (dolist (y '(1 2 3) x) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3)) (setq x (+ x y))))) +(aeq 'dolist nil (let ((x 0)) (dolist (y '(1 2 3) y) (setq x (+ x y))))) +(aeq 'loop 6 (let ((x 0)) (loop (when (= x 6) (return x)) (incf x)))) +(aeq 'loop 6 (let ((x 0)) (loop (unless (< x 6) (return x)) (incf x)))) +(aeq 'return 'a (let ((a 7)) (loop (progn (return 'a))))) +(aeq 'return nil (loop (return))) +(aeq 'return 'a (let ((a 7)) (loop (progn (return 'a) nil)))) +(aeq 'do 2 (do* ((x 1 (1+ x)) (y 0 (1+ x))) ((= 3 y) x))) +(aeq 'do 3 (do ((x 1 (1+ x)) (y 0 (1+ x))) ((= 3 y) x))) +(aeq 'do 720 (do* ((n 6) (f 1 (* j f)) (j n (- j 1))) ((= j 0) f))) +(aeq 'do 720 (let ((n 6)) (do ((f 1 (* j f)) (j n (- j 1)) ) ((= j 0) f)))) +(aeq 'do 10 (do (a (b 1 (1+ b))) ((> b 10) a) (setq a b))) + +#| conditions |# + +(aeq 'if 3 (let ((a 2)) (if (= a 2) 3 4))) +(aeq 'if 4 (let ((a 2)) (if (= a 3) 3 4))) +(aeq 'if 4 (let ((a 3)) (if (= a 3) 4))) +(aeq 'if nil (let ((a 4)) (if (= a 3) 4))) +(aeq 'when 4 (let ((a 3)) (when (= a 3) 4))) +(aeq 'when nil (let ((a 2)) (when (= a 3) 4))) +(aeq 'unless nil (let ((a 3)) (unless (= a 3) 4))) +(aeq 'unless 4 (let ((a 2)) (unless (= a 3) 4))) +(aeq 'cond 8 (let ((a 2)) (cond ((= a 3) 7) ((= a 2) 8) (t 9)))) +(aeq 'cond 9 (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8) (9)))) +(aeq 'cond nil (let ((a 1)) (cond ((= a 3) 7) ((= a 2) 8)))) +(aeq 'cond 12 (car (cond ((evenp 3) (list (* 2 3))) ((list (* 3 4)))))) +(aeq 'case 222 (let ((j 1)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 333 (let ((j t)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) +(aeq 'case 444 (let ((j 2)) (case j ((0 1) 111 222) ((t) 333) (t 444)))) + +#| eval/funcall/apply |# + +(aeq 'funcall 10 (funcall + 1 2 3 4)) +(aeq 'funcall 'a (funcall car '(a b c d))) +(aeq 'funcall 3 (let ((x 0)) (funcall (lambda (y) (incf x y)) 3) x)) +(aeq 'apply 10 (apply + '(1 2 3 4))) +(aeq 'apply 13 (apply + 1 2 '(1 2 3 4))) +(aeq 'eval 10 (eval (list + 1 2 3 4))) +(aeq 'eval nil (eval nil)) +(aeq 'funcall 999 (let ((x 999)) (funcall (lambda (x) x) x))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (funcall fun (funcall fun x)))))) (funcall (x2 '1+) 2))) +(aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (fun (fun x)))))) ((x2 '1+) 2))) +(aeq 'apply 5 (let* ((my (lambda (x y) (+ x y))) (han '(my))) (apply (first han) '(2 3)))) + +#| in-place operations |# + +(aeq 'incf 5 (let ((x 0)) (+ (incf x) (incf x 2) (incf x -2)))) +(aeq 'decf -5 (let ((x 0)) (+ (decf x) (decf x 2) (decf x -2)))) +(aeq 'incf 6 (let ((x 0)) (+ (incf x) (incf x) (incf x)))) +(aeq 'incf 12 (let ((x 0)) (+ (incf x 2) (incf x 2) (incf x 2)))) +(aeq 'incf 36 (let ((n 10)) (let* ((f1 (lambda () (incf n) n))) (+ (funcall f1) (funcall f1) (funcall f1))))) +(aeq 'setf 25 (let ((a 3) (b 4)) (setf a (* a 3) b (* b 4)) (+ a b))) +(aeq 'setf 9 (let ((a '(2 3))) (setf (car a) 6) (apply + a))) +(aeq 'setf 12 (let ((a '(2 3))) (setf (cdr a) '(6)) (apply * a))) +(aeq 'setf 220 (let ((a '(2 3 4))) (setf (nth 1 a) 11 (nth 2 a) 10) (apply * a))) + +#| recursion |# + +(aeq 'lambda 55 (let ((fib (lambda (n) (if (< n 3) 1 (+ (fib (- n 1)) (fib (- n 2))))))) (fib 10))) +(aeq 'lambda 5040 (let ((f (lambda (n) (if (= n 0) 1 (* n (f (- n 1))))))) (f 7))) +(aeq 'lambda 10 (let ((a 0)) (let ((f (lambda (n) (incf a n) (when (> n 0) (f (1- n)))))) (f 4)) a)) + +#| streams |# + +(aeq 'stream "" (with-output-to-string (s) (princ s s))) +(aeq 'stream "12 23 34" (with-output-to-string (st) (format st "~a ~a ~a" 12 23 34))) + +#| features |# + +(aeq 'features nil (member :floating-point *features*)) +(aeq 'features ":arrays" (princ-to-string (first (member :arrays *features*)))) + +#| printing |# + +(aeq 'princ "hello" (princ-to-string "hello")) +(aeq 'princ "hello \"David\"" (princ-to-string "hello \"David\"")) +(aeq 'prin1 "\"hello\"" (prin1-to-string "hello")) +(aeq 'prin1 "\"hello \\\"David\\\"\"" (prin1-to-string "hello \"David\"")) + +#| prettyprinting |# + +(aeq 'princ "hello" (princ-to-string "hello")) +(aeq 'pprint 8313 (let ((n 0) (st (with-output-to-string (str) (pprint aeq str)))) (dotimes (i (length st) n) (incf n (char-code (char st i)))))) + +#| documentation |# + +(aeq 'apropos '(progn apropos apropos-list unwind-protect) (apropos-list 'pro)) +(aeq 'apropos '(progn apropos apropos-list unwind-protect) (apropos-list "pro")) +(aeq 'documentation 7397 (let ((n 0)) (let ((st (documentation '?))) (dotimes (i (length st) n) (incf n (char-code (char st i))))))) + +#| format |# + +(aeq 'format "hello" (format nil "hello")) +(aeq 'format "Hello23Goodbye" (format nil "Hello~aGoodbye" 23)) +(aeq 'format " 17" (format nil "~5x" 23)) +(aeq 'format " 10111" (format nil "~6b" 23)) +(aeq 'format " 17 23 23 " (format nil "~5x ~5d ~5a" 23 23 23)) +(aeq 'format "00017 00023" (format nil "~5,'0x ~5,'0d" 23 23)) +(aeq 'format "01-45-07" (format nil "~2,'0d-~2,'0d-~2,'0d" 1 45 7)) +(aeq 'format "Hello42" (format nil "Hello~a" 42)) +(aeq 'format "[1,2,3]" (format nil "[~{~a~^,~}]" '(1 2 3))) +(aeq 'format "nil nil" (format nil "~a ~{ ~a ~} ~a" nil nil nil)) + +#| strings |# + +(aeq 'stringp t (stringp "hello")) +(aeq 'stringp nil (stringp 5)) +(aeq 'stringp nil (stringp '(a b))) +(aeq 'numberp nil (numberp "hello")) +(aeq 'atom t (atom "hello")) +(aeq 'consp nil (consp "hello")) +(aeq 'eq nil (eq "hello" "hello")) +(aeq 'eq t (let ((a "hello")) (eq a a))) +(aeq 'length 0 (length "")) +(aeq 'length 5 (length "hello")) +(aeq 'concatenate t (string= (concatenate 'string "A" "B") "AB")) +(aeq 'concatenate 3 (length (concatenate 'string "A" "BC"))) +(aeq 'concatenate 0 (length (concatenate 'string))) +(aeq 'concatenate "ABCD" (concatenate 'string "AB" "CD")) +(aeq 'concatenate "ABCDE" (concatenate 'string "AB" "CDE")) +(aeq 'concatenate "ABCDE" (concatenate 'string "ABC" "DE")) +(aeq 'concatenate "ABCDEF" (concatenate 'string "ABC" "DEF")) +(aeq 'string= nil (string= "cat" "cat ")) +(aeq 'string= t (string= "cat" "cat")) +(aeq 'string/= 3 (string/= "cat" "catx")) +(aeq 'string/= nil (string/= "cat" "cat")) +(aeq 'string/= nil (string/= "catt" "catt")) +(aeq 'string< nil (string< "cat" "cat")) +(aeq 'string<= 3 (string<= "cat" "cat")) +(aeq 'string< 3 (string< "cat" "cat ")) +(aeq 'string< 4 (string< "fish" "fish ")) +(aeq 'string> nil (string> "cat" "cat")) +(aeq 'string>= 3 (string>= "cat" "cat")) +(aeq 'string>= 5 (string>= "cattx" "cattx")) +(aeq 'string> 0 (string> "c" "a")) +(aeq 'string> 1 (string> "fc" "fa")) +(aeq 'string> 2 (string> "ffc" "ffa")) +(aeq 'string> 3 (string> "fffc" "fffa")) +(aeq 'string> 4 (string> "ffffc" "ffffa")) +(aeq 'string> 5 (string> "fffffc" "fffffa")) +(aeq 'string> nil (string< "fffffc" "fffffa")) +(aeq 'string "albatross" (string "albatross")) +(aeq 'string "x" (string #\x)) +(aeq 'string "cat" (string 'cat)) +(aeq 'string "albatross" (string 'albatross)) + +#| subseq and search |# + +(aeq 'subseq "hello" (subseq "hellofromdavid" 0 5)) +(aeq 'subseq "fromdavid" (subseq "hellofromdavid" 5)) +(aeq 'subseq '(2 3 4) (subseq '(0 1 2 3 4) 2)) +(aeq 'subseq '(2) (subseq '(0 1 2 3 4) 2 3)) +(aeq 'subseq nil (subseq '() 0)) +(aeq 'search 4 (search "cat" "the cat sat on the mat")) +(aeq 'search 19 (search "mat" "the cat sat on the mat")) +(aeq 'search nil (search "hat" "the cat sat on the mat")) +(aeq 'search 1 (search '(1 2) '( 0 1 2 3 4))) +(aeq 'search nil (search '(2 1 2 3 4 5) '(2 1 2 3 4))) + +#| characters |# + +(aeq 'char-code 97 (char-code #\a)) +(aeq 'char-code 13 (char-code #\return)) +(aeq 'char-code 255 (char-code #\255)) +(aeq 'code-char #\return (code-char 13)) +(aeq 'code-char #\a (code-char 97)) +(aeq 'code-char #\255 (code-char 255)) +(aeq 'eq t (eq #\b #\b)) +(aeq 'eq nil (eq #\b #\B)) +(aeq 'numberp nil (numberp #\b)) +(aeq 'characterp t (characterp #\b)) +(aeq 'char #\o (char "hello" 4)) +(aeq 'char #\h (char "hello" 0)) +(aeq 'char "A" (princ-to-string (code-char 65))) +(aeq 'char "[#\\Bell]" (format nil "[~s]" (code-char 7))) +(aeq 'char "[#\\Return]" (format nil "[~s]" #\return)) +(aeq 'char "[#\\127]" (format nil "[~s]" #\127)) +(aeq 'char "[#\\255]" (format nil "[~s]" #\255)) + +#| read-from-string |# + +(aeq 'read-from-string 123 (read-from-string "123")) +(aeq 'read-from-string 144 (eval (read-from-string "((lambda (x) (* x x)) 12)"))) +(aeq 'read-from-string t (eval (read-from-string "(eq (+ 2 3) 5)"))) +(aeq 'read-from-string nil (read-from-string "()")) + +#| closures |# + +(aeq 'closure 'lex (let ((lex nil)) (funcall (let ((lex t)) (lambda () (if lex 'lex 'dyn)))))) +(aeq 'closure 103 (let* ((c 100) (two (lambda (d) (+ c d))) (one (lambda (c) (funcall two 3)))) (funcall one 1))) +(aeq 'closure 4 (let ((x 0)) (funcall (lambda (y) (incf x y)) 4) x)) +(aeq 'closure 0 (let ((x 0)) (funcall (let ((x 7)) (lambda (y) (setq x (+ x y) ))) 4) x)) +(aeq 'closure '(8 10 13 17) (let ((x 0) (clo (lambda () (let ((x 7)) (lambda (y) (incf x y)))))) (mapcar (funcall clo) '(1 2 3 4)))) +(aeq 'closure 3 (let ((y 0) (test (lambda (x) (+ x 1)))) (dotimes (x 3 y) (progn (test (+ x 2))) (incf y x)))) + +#| arrays |# + +(aeq 'array '(0 0) (array-dimensions #2a())) +(aeq 'array '(1 0) (array-dimensions #2a(()))) +(aeq 'array '(2 0) (array-dimensions #2a(() ()))) +(aeq 'array '(0) (array-dimensions (make-array '(0)))) +(aeq 'array '(0) (array-dimensions (make-array 0))) +(aeq 'array 1 (let ((a (make-array 3 :initial-element 0))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array '(3) :initial-element 0))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array '(2 3) :initial-element 0))) (incf (aref a 1 (+ 1 1))) (aref a 1 2))) +(aeq 'array 1 (let ((a (make-array '(2 3 2 2) :initial-element 0))) (incf (aref a 1 (+ 1 1) 1 1)) (aref a 1 2 1 1))) +(aeq 'array 10 (length (make-array 10 :initial-element 1))) + +#| bit arrays |# + +(aeq 'array '(0) (array-dimensions (make-array '(0) :element-type 'bit))) +(aeq 'array '(1 1) (array-dimensions (make-array '(1 1) :element-type 'bit))) +(aeq 'array 10 (length (make-array '(10) :element-type 'bit))) +(aeq 'array 10 (length (make-array 10 :element-type 'bit))) +(aeq 'array 1 (let ((a (make-array 3 :element-type 'bit))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 1 (let ((a (make-array 3 :initial-element 0 :element-type 'bit))) (incf (aref a (+ 1 1))) (aref a 2))) +(aeq 'array 0 (let ((a (make-array 10 :element-type 'bit :initial-element 1))) (decf (aref a 4)) (aref a 4))) +(aeq 'array 1 (let ((a (make-array 40 :element-type 'bit :initial-element 0))) (incf (aref a 39)) (aref a 39))) +(aeq 'array 0 (let ((a (make-array 40 :element-type 'bit :initial-element 0))) (incf (aref a 39)) (decf (aref a 39)) (aref a 39))) + +#| repl |# + +(aeq 'repl 23 (read-from-string "23(2)")) +(aeq 'repl nil (read-from-string "()23")) +(aeq 'repl 23 (read-from-string "23\"Hi\"")) +(aeq 'repl "Hi" (read-from-string "\"Hi\"23")) +(aeq 'repl #\1 (read-from-string " #\\1\"Hi\"")) +(aeq 'repl "Hi" (read-from-string (format nil "\"Hi\"~a~a" #\# "*0101"))) + +#| equal |# + +(aeq 'equal t (equal '(1 2 3) '(1 2 3))) +(aeq 'equal t (equal '(1 2 (4) 3) '(1 2 (4) 3))) +(aeq 'equal nil (equal '(1 2 (4) 3) '(1 2 (4 nil) 3))) +(aeq 'equal t (equal "cat" "cat")) +(aeq 'equal nil (equal "cat" "Cat")) +(aeq 'equal t (equal 'cat 'Cat)) +(aeq 'equal t (equal 2 (+ 1 1))) +(aeq 'equal t (equal '("cat" "dog") '("cat" "dog"))) +(aeq 'equal nil (equal '("cat" "dog") '("cat" "dig"))) + +#| keywords |# + +(aeq 'keywordp t (keywordp :led-builtin)) +(aeq 'keywordp nil (keywordp print)) +(aeq 'keywordp nil (keywordp nil)) +(aeq 'keywordp nil (keywordp 12)) +(aeq 'keywordp t (keywordp :fred)) +(aeq 'keywordp t (keywordp :initial-element)) +(aeq 'keywordp t (keywordp :element-type)) + +#| errors |# + +(aeq 'error 7 (let ((x 7)) (ignore-errors (setq x (/ 1 0))) x)) +(aeq 'error 5 (unwind-protect (+ 2 3) 13)) + +#| Place |# + +(aeq 'setf "hellx" (let ((s "hello")) (setf (char s 4) #\x) s)) + +#| error checks |# + +(aeq 'dolist nothing (ignore-errors (dolist 12 (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist () (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist (x) (print x)))) +(aeq 'dolist nothing (ignore-errors (dolist (x nil x x) (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes 12 (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes () (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes (x) (print x)))) +(aeq 'dotimes nothing (ignore-errors (dotimes (x 1 x x) (print x)))) +(aeq 'for-millis nothing (ignore-errors (for-millis 12 (print 12)))) +(aeq 'for-millis nothing (ignore-errors (for-millis (12 12) (print 12)))) +(aeq 'push nothing (ignore-errors (let ((a #*00000000)) (push 1 (aref a 1)) a))) +(aeq 'setf nothing (ignore-errors (let ((s "hello")) (setf (char s 5) #\x) s))) +(aeq 'setf nothing (ignore-errors (let ((s "hello")) (setf (char s 20) #\x) s))) + +#| errors |# + +(aeq 'errors 0 ers) + +"#) + +(defun run-tests (&optional (usb 1411)) ; "/dev/cu.usbserial-A104OVGT")) ; + (let ((name (cond + ((numberp usb) (format nil "/dev/cu.usbmodem~a" usb)) + ((eq usb :badge2) "/dev/cu.usbserial-A104OVGT") + ((eq usb :badge) "/dev/cu.usbserial-A10L2FSQ") ; "/dev/cu.usbserial-A602TRZF" + ((eq usb :avrfeather) "/dev/cu.SLAB_USBtoUART") + (t usb))) + (speed 0.5)) + (flet ((serial-write-exp (string stream) + (write-string string stream) + (write-char #\newline stream)) + ;; + (echo (s) + (sleep speed) + (loop + (let ((c (read-char-no-hang s))) + (unless c (return)) + (unless (eq c #\return) (write-char c)))) + (format t "~%")) + ;; + (read-serial (s) + (sleep speed) + (let ((string (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t))) + (loop + (let ((c (read-char-no-hang s))) + (unless c (return string)) + (vector-push-extend c string)))))) + ;; + (with-open-stream (s (make-instance 'serial-stream :name name)) + (sleep 5) + (echo s) + (echo s) + (serial-write-exp "(defvar ers 0)" s) + (echo s) + (serial-write-exp + "(defun aeq (tst x y) + (unless (equal x y) + (incf ers) + (format t \"~a=~a/~a~%\" tst x y)))" + s) + (echo s) + ;; + ;; tests + ;; + (with-input-from-string (str *tests*) + (loop + (let ((line (read-line str nil nil))) + (unless line (return)) + (serial-write-exp line s) + (let ((output (read-serial s))) + (let* ((m1 (position #\return output)) + (m2 (when m1 (position #\return output :start (+ 2 m1))))) + (cond + ((null m2) (format t "~a~%" output)) + ((string= (subseq output (+ 2 m1) m2) "nil") nil) + (t (format t "*** ~a: ~a~%" (subseq output (+ 2 m1) m2) (subseq output 0 m1))))))))))))) \ No newline at end of file diff --git a/builder/arm.lisp b/builder/arm.lisp new file mode 100644 index 0000000..d9a02e3 --- /dev/null +++ b/builder/arm.lisp @@ -0,0 +1,630 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +; Arm + +(defparameter *title-arm* +#"/* uLisp ARM Release ~a - www.ulisp.com + David Johnson-Davies - www.technoblogy.com - ~a + + Licensed under the MIT license: https://opensource.org/licenses/MIT +*/"#) + +(defparameter *header-arm* #" +// 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 +// #define extensions + +// Includes + +// #include "LispLibrary.h" +#include +#include +#include +#include + +#if defined(sdcardsupport) +#include +#define SDSIZE 720 +#else +#define SDSIZE 0 +#endif"#) + +(defparameter *workspace-arm* #" +// Platform specific settings + +#define WORDALIGNED __attribute__((aligned (4))) +#define BUFFERSIZE 36 // Number of bits+4 +#define RAMFUNC __attribute__ ((section (".ramfunctions"))) +#define MEMBANK + +// ATSAMD21 boards *************************************************************** + +#if defined(ARDUINO_GEMMA_M0) || defined(ARDUINO_SEEED_XIAO_M0) || defined(ARDUINO_QTPY_M0) + #define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */ + #define CPUFLASH + #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 STACKDIFF 320 + #define SDCARD_SS_PIN 4 + #define CPU_ATSAMD21 + +#elif defined(ADAFRUIT_FEATHER_M0) /* Feather M0 without DataFlash */ + #define WORKSPACESIZE (2816-SDSIZE) /* Objects (8*bytes) */ + #define CPUFLASH + #define FLASHSIZE 32768 /* Bytes */ + #define CODESIZE 128 /* Bytes */ + #define STACKDIFF 320 + #define SDCARD_SS_PIN 4 + #define CPU_ATSAMD21 + +#elif defined(ARDUINO_SAMD_MKRZERO) + #define WORKSPACESIZE (2640-SDSIZE) /* Objects (8*bytes) */ + #define CPUFLASH + #define FLASHSIZE 32768 /* 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 CPUFLASH + #define FLASHSIZE 32768 /* Bytes */ + #define CODESIZE 128 /* Bytes */ + #define STACKDIFF 320 + #define SDCARD_SS_PIN 10 + #define CPU_ATSAMD21 + +// ATSAMD51 boards *************************************************************** + +#elif defined(ARDUINO_METRO_M4) || defined(ARDUINO_ITSYBITSY_M4) || defined(ARDUINO_FEATHER_M4) + #define WORKSPACESIZE (20608-SDSIZE) /* Objects (8*bytes) */ + #define DATAFLASH + #define FLASHSIZE 2048000 /* 2 MBytes */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 400 + #define SDCARD_SS_PIN 10 + #define CPU_ATSAMD51 + +#elif 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 STACKDIFF 400 + #define SDCARD_SS_PIN 10 + #define CPU_ATSAMD51 + #if defined(gfxsupport) + const int COLOR_WHITE = 0xffff, COLOR_BLACK = 0, TFT_BACKLIGHT = 47; + #include // Core graphics library + #include // Hardware-specific library for ST7735 + Adafruit_ST7735 tft = Adafruit_ST7735(44, 45, 41, 42, 46); + #endif + +#elif defined(ARDUINO_WIO_TERMINAL) + #define WORKSPACESIZE (20480-SDSIZE) /* Objects (8*bytes) */ + #define DATAFLASH + #define FLASHSIZE 2048000 /* 2 MBytes */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 400 + #define CPU_ATSAMD51 + #define EXTERNAL_FLASH_USE_QSPI + #if defined(gfxsupport) + const int COLOR_WHITE = 0xffff, COLOR_BLACK = 0; + #include // Hardware-specific library + TFT_eSPI tft = TFT_eSPI(); + #endif + +#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 440 + #define CPU_ATSAMD51 + +// nRF51 boards *************************************************************** + +#elif defined(ARDUINO_BBC_MICROBIT) || defined(ARDUINO_SINOBIT) + #define WORKSPACESIZE 1344 /* Objects (8*bytes) */ + #define CODESIZE 64 /* Bytes */ + #define STACKDIFF 320 + #define CPU_NRF51822 + +#elif defined(ARDUINO_CALLIOPE_MINI) + #define WORKSPACESIZE 3392 /* Objects (8*bytes) */ + #define CODESIZE 64 /* Bytes */ + #define STACKDIFF 320 + #define CPU_NRF51822 + +// nRF52 boards *************************************************************** + +#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_NRF52840_ITSYBITSY) || defined(ARDUINO_Seeed_XIAO_nRF52840) \ + || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense) || defined(ARDUINO_NRF52840_CIRCUITPLAY) + #define WORKSPACESIZE (21120-SDSIZE) /* Objects (8*bytes) */ + #define DATAFLASH + #define FLASHSIZE 2048000 /* 2 MBytes */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 8 + #define CPU_NRF52840 + +#elif defined(ARDUINO_NRF52840_CLUE) + #define WORKSPACESIZE (21120-SDSIZE) /* Objects (8*bytes) */ + #define DATAFLASH + #define FLASHSIZE 2048000 /* 2 MBytes */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 8 + #define CPU_NRF52840 + #if defined(gfxsupport) + const int COLOR_WHITE = 0xffff, COLOR_BLACK = 0; + #include + #include + Adafruit_ST7789 tft = Adafruit_ST7789(&SPI1, PIN_TFT_CS, PIN_TFT_DC, PIN_TFT_RST); + #endif + +// MAX32620 boards *************************************************************** + +#elif defined(MAX32620) + #define WORKSPACESIZE (24704-SDSIZE) /* Objects (8*bytes) */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 320 + #define CPU_MAX32620 + #define Wire1 Wire2 + +// iMXRT1062 boards *************************************************************** + +#elif defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) + #define WORKSPACESIZE 60000 /* Objects (8*bytes) */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 15000 + #define LITTLEFS (960 * 1024) + #include + LittleFS_Program LittleFS; + #define FS_FILE_WRITE FILE_WRITE_BEGIN + #define FS_FILE_READ FILE_READ + #define CPU_iMXRT1062 + #define SDCARD_SS_PIN BUILTIN_SDCARD + #define BitOrder uint8_t + #undef RAMFUNC + #define RAMFUNC FASTRUN + #undef MEMBANK + #define MEMBANK DMAMEM + +// RP2040 boards *************************************************************** + +#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2040) + #define WORKSPACESIZE (23000-SDSIZE) /* Objects (8*bytes) */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 480 + #define LITTLEFS + #include + #define FS_FILE_WRITE "w" + #define FS_FILE_READ "r" + #define CPU_RP2040 + #if defined(gfxsupport) + const int COLOR_WHITE = 0xffff, COLOR_BLACK = 0; + #include // Core graphics library + #include // Hardware-specific library for ST7789 + Adafruit_ST7789 tft = Adafruit_ST7789(5, 1, 3, 2, 0); // TTGO RP2040 TFT + #define TFT_BACKLIGHT 4 + #define TFT_I2C_POWER 22 + #endif + +#elif defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) + #define WORKSPACESIZE 23000 /* Objects (8*bytes) */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 480 + #define LITTLEFS + #include + #define FS_FILE_WRITE "w" + #define FS_FILE_READ "r" + #define SDCARD_SS_PIN 23 + #define CPU_RP2040 + +#elif defined(ARDUINO_RASPBERRY_PI_PICO_W) + #define WORKSPACESIZE (15536-SDSIZE) /* Objects (8*bytes) */ + #define CODESIZE 256 /* Bytes */ + #define STACKDIFF 480 + #define LITTLEFS + #include + #include + #define FS_FILE_WRITE "w" + #define FS_FILE_READ "r" + #define CPU_RP2040 + +// RP2350 boards *************************************************************** + +#elif defined(ARDUINO_RASPBERRY_PI_PICO_2) + #if defined(__riscv) + #define WORKSPACESIZE (42500-SDSIZE) /* Objects (8*bytes) */ + #define STACKDIFF 580 + #else + #define WORKSPACESIZE (47000-SDSIZE) /* Objects (8*bytes) */ + #define STACKDIFF 520 + #endif + #define CODESIZE 256 /* Bytes */ + #define LITTLEFS + #include + #define FS_FILE_WRITE "w" + #define FS_FILE_READ "r" + #define CPU_RP2350 + +#elif defined(ARDUINO_PIMORONI_PICO_PLUS_2) + //#define BOARD_HAS_PSRAM /* Uncomment to use PSRAM */ + #if defined(BOARD_HAS_PSRAM) + #undef MEMBANK + #define MEMBANK PSRAM + #define WORKSPACESIZE 1000000 /* Objects (8*bytes) */ + #define STACKDIFF 580 + #elif defined(__riscv) + #define WORKSPACESIZE (42000-SDSIZE) /* Objects (8*bytes) */ + #define STACKDIFF 580 + #else + #define WORKSPACESIZE (46500-SDSIZE) /* Objects (8*bytes) */ + #define STACKDIFF 520 + #endif + #define CODESIZE 256 /* Bytes */ + #define LITTLEFS + #include + #define FS_FILE_WRITE "w" + #define FS_FILE_READ "r" + #define SDCARD_SS_PIN 10 + #define CPU_RP2350 + +#elif defined(ARDUINO_PIMORONI_TINY2350) + #if defined(__riscv) + #define WORKSPACESIZE (42500-SDSIZE) /* Objects (8*bytes) */ + #define STACKDIFF 580 + #else + #define WORKSPACESIZE (47000-SDSIZE) /* Objects (8*bytes) */ + #define STACKDIFF 520 + #endif + #define CODESIZE 256 /* Bytes */ + #define LITTLEFS + #include + #define FS_FILE_WRITE "w" + #define FS_FILE_READ "r" + #define CPU_RP2350 + +// RA4M1 boards *************************************************************** + +#elif defined(ARDUINO_MINIMA) + #define WORKSPACESIZE (2032-SDSIZE) /* Objects (8*bytes) */ + #include + #define EEPROMFLASH + #define FLASHSIZE 8192 /* Bytes */ + #define CODESIZE 128 /* Bytes */ + #define STACKDIFF 320 + #define eAnalogReference ar_aref + #define CPU_RA4M1 + #define SDCARD_SS_PIN 10 + +#elif defined(ARDUINO_UNOWIFIR4) + #define WORKSPACESIZE (1610-SDSIZE) /* Objects (8*bytes) */ + #include + #include "WiFiS3.h" + #define EEPROMFLASH + #define FLASHSIZE 8192 /* Bytes */ + #define CODESIZE 128 /* Bytes */ + #define STACKDIFF 320 + #define eAnalogReference ar_aref + #define CPU_RA4M1 + #define SDCARD_SS_PIN 10 + +#else +#error "Board not supported!" +#endif"#) + +(defparameter *check-pins-arm* #" +// Check pins - these are board-specific not processor-specific + +void checkanalogread (int pin) { +#if defined(ARDUINO_SAM_DUE) + if (!(pin>=54 && pin<=65)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_SAMD_ZERO) + if (!(pin>=14 && pin<=19)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_SAMD_MKRZERO) + if (!(pin>=15 && pin<=21)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_ITSYBITSY_M0) + if (!(pin>=14 && pin<=25)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_NEOTRINKEY_M0) + if (!(pin==1 || pin==2 || pin==6)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_GEMMA_M0) + if (!(pin>=8 && pin<=10)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_QTPY_M0) + if (!((pin>=0 && pin<=3) || (pin>=6 && pin<=10))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_SEEED_XIAO_M0) + if (!(pin>=0 && pin<=10)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_METRO_M4) + if (!(pin>=14 && pin<=21)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_ITSYBITSY_M4) || defined(ARDUINO_FEATHER_M4) + if (!(pin>=14 && pin<=20)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_PYBADGE_M4) + if (!(pin>=14 && pin<=23)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_PYGAMER_M4) + if (!(pin>=14 && pin<=25)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_WIO_TERMINAL) + if (!((pin>=0 && pin<=8))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_GRAND_CENTRAL_M4) + if (!((pin>=67 && pin<=74) || (pin>=54 && pin<=61))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_BBC_MICROBIT) || defined(ARDUINO_SINOBIT) + if (!((pin>=0 && pin<=4) || pin==10)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_BBC_MICROBIT_V2) + if (!((pin>=0 && pin<=4) || pin==10 || pin==29)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_CALLIOPE_MINI) + if (!(pin==1 || pin==2 || (pin>=4 && pin<=6) || pin==21)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_NRF52840_ITSYBITSY) + if (!(pin>=14 && pin<=20)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_Seeed_XIAO_nRF52840) || defined(ARDUINO_Seeed_XIAO_nRF52840_Sense) + if (!(pin>=0 && pin<=5)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_NRF52840_CLUE) + if (!((pin>=0 && pin<=4) || pin==10 || pin==12 || pin==16)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_NRF52840_CIRCUITPLAY) + if (!(pin==0 || (pin>=2 && pin<=3) || pin==6 || (pin>=9 && pin<=10) || (pin>=22 && pin<=23))) error(invalidpin, number(pin)); +#elif defined(MAX32620) + if (!(pin>=49 && pin<=52)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_TEENSY40) + if (!((pin>=14 && pin<=27))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_TEENSY41) + if (!((pin>=14 && pin<=27) || (pin>=38 && pin<=41))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) \ + || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_SEEED_XIAO_RP2040) \ + || defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_PIMORONI_PICO_PLUS_2) + if (!(pin>=26 && pin<=29)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_MINIMA) || defined(ARDUINO_UNOWIFIR4) + if (!((pin>=14 && pin<=21))) error(invalidpin, number(pin)); +#endif +} + +void checkanalogwrite (int pin) { +#if defined(ARDUINO_SAM_DUE) + if (!((pin>=2 && pin<=13) || pin==66 || pin==67)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_SAMD_ZERO) + if (!((pin>=3 && pin<=6) || (pin>=8 && pin<=13) || pin==14)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_SAMD_MKRZERO) + if (!((pin>=0 && pin<=8) || pin==10 || pin==18 || pin==19)) error(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(invalidpin, number(pin)); +#elif defined(ARDUINO_NEOTRINKEY_M0) + error2("not supported"); +#elif defined(ARDUINO_GEMMA_M0) + if (!(pin==0 || pin==2 || pin==9 || pin==10)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_QTPY_M0) + if (!(pin==0 || (pin>=2 && pin<=10))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_SEEED_XIAO_M0) + if (!(pin>=0 && pin<=10)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_METRO_M4) + if (!(pin>=0 && pin<=15)) error(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(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(invalidpin, number(pin)); +#elif defined(ARDUINO_PYBADGE_M4) + if (!(pin==4 || pin==7 || pin==9 || (pin>=12 && pin<=13) || (pin>=24 && pin<=25) || (pin>=46 && pin<=47))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_PYGAMER_M4) + if (!(pin==4 || pin==7 || pin==9 || (pin>=12 && pin<=13) || (pin>=26 && pin<=27) || (pin>=46 && pin<=47))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_WIO_TERMINAL) + if (!((pin>=0 && pin<=2) || pin==6 || pin==8 || (pin>=12 && pin<=20) || pin==24)) error(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(invalidpin, number(pin)); +#elif defined(ARDUINO_BBC_MICROBIT) || defined(ARDUINO_BBC_MICROBIT_V2) || defined(ARDUINO_SINOBIT) + if (!(pin>=0 && pin<=32)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_CALLIOPE_MINI) + if (!(pin>=0 && pin<=30)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_NRF52840_ITSYBITSY) + if (!(pin>=0 && pin<=25)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_NRF52840_CLUE) + if (!(pin>=0 && pin<=46)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_NRF52840_CIRCUITPLAY) + if (!(pin>=0 && pin<=35)) error(invalidpin, number(pin)); +#elif defined(MAX32620) + if (!((pin>=20 && pin<=29) || pin==32 || (pin>=40 && pin<=48))) error(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(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(invalidpin, number(pin)); +#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) \ + || defined(ARDUINO_SEEED_XIAO_RP2040) || defined(ARDUINO_RASPBERRY_PI_PICO_2) \ + || defined(ARDUINO_PIMORONI_PICO_PLUS_2) + if (!(pin>=0 && pin<=29)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_RASPBERRY_PI_PICO_W) + if (!((pin>=0 && pin<=29) || pin == 32)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_MINIMA) || defined(ARDUINO_UNOWIFIR4) + if (!((pin>=0 && pin<=21))) error(invalidpin, number(pin)); +#endif +}"#) + +(defparameter *note-arm* #" +// Note + +const int scale[] = {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_NRF52840_CIRCUITPLAY) \ + || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) \ + || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_WIO_TERMINAL) \ + || defined(ARDUINO_SEEED_XIAO_RP2040) || defined(ARDUINO_RASPBERRY_PI_PICO_2) \ + || defined(ARDUINO_PIMORONI_PICO_PLUS_2) + int oct = octave + note/12; + int prescaler = 8 - oct; + if (prescaler<0 || prescaler>8) error("octave out of range", number(oct)); + tone(pin, scale[note%12]>>prescaler); +#else + (void) pin, (void) note, (void) octave; +#endif +} + +void nonote (int pin) { +#if defined(ARDUINO_NRF52840_CLUE) || defined(ARDUINO_NRF52840_CIRCUITPLAY) \ + || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \ + || defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) \ + || defined(ARDUINO_WIO_TERMINAL) || defined(ARDUINO_SEEED_XIAO_RP2040) \ + || defined(ARDUINO_PIMORONI_PICO_PLUS_2) + noTone(pin); +#else + (void) pin; +#endif +}"#) + +(defparameter *sleep-arm* #" +// 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 doze (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 +}"#) + +(defparameter *keywords-arm* + '((nil + ((NIL LED_BUILTIN) + (DIGITALWRITE HIGH LOW))) + ("CPU_ATSAMD21" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT) + (ANALOGREFERENCE AR_DEFAULT AR_INTERNAL1V0 AR_INTERNAL1V65 AR_INTERNAL2V23 AR_EXTERNAL) + (REGISTER (PA_DIR "PORT->Group[0].DIR.reg") (PA_DIRCLR "PORT->Group[0].DIRCLR.reg") (PA_DIRSET "PORT->Group[0].DIRSET.reg") + (PA_DIRTGL "PORT->Group[0].DIRTGL.reg") (PA_OUT "PORT->Group[0].OUT.reg") (PA_OUTCLR "PORT->Group[0].OUTCLR.reg") + (PA_OUTSET "PORT->Group[0].OUTSET.reg") (PA_OUTTGL "PORT->Group[0].OUTTGL.reg") (PA_IN "PORT->Group[0].IN.reg") + (PB_DIR "PORT->Group[1].DIR.reg") (PB_DIRCLR "PORT->Group[1].DIRCLR.reg") (PB_DIRSET "PORT->Group[1].DIRSET.reg") + (PB_DIRTGL "PORT->Group[1].DIRTGL.reg") (PB_OUT "PORT->Group[1].OUT.reg") (PB_OUTCLR "PORT->Group[1].OUTCLR.reg") + (PB_OUTSET "PORT->Group[1].OUTSET.reg") (PB_OUTTGL "PORT->Group[1].OUTTGL.reg") (PB_IN "PORT->Group[1].IN.reg")))) + ("CPU_ATSAMD51" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT) + (ANALOGREFERENCE AR_DEFAULT AR_INTERNAL1V0 AR_INTERNAL1V1 AR_INTERNAL1V2 AR_INTERNAL1V25 AR_INTERNAL1V65 AR_INTERNAL2V0 + AR_INTERNAL2V2 AR_INTERNAL2V23 AR_INTERNAL2V4 AR_INTERNAL2V5 AR_EXTERNAL) + (REGISTER (PA_DIR "PORT->Group[0].DIR.reg") (PA_DIRCLR "PORT->Group[0].DIRCLR.reg") (PA_DIRSET "PORT->Group[0].DIRSET.reg") + (PA_DIRTGL "PORT->Group[0].DIRTGL.reg") (PA_OUT "PORT->Group[0].OUT.reg") (PA_OUTCLR "PORT->Group[0].OUTCLR.reg") + (PA_OUTSET "PORT->Group[0].OUTSET.reg") (PA_OUTTGL "PORT->Group[0].OUTTGL.reg") (PA_IN "PORT->Group[0].IN.reg") + (PB_DIR "PORT->Group[1].DIR.reg") (PB_DIRCLR "PORT->Group[1].DIRCLR.reg") (PB_DIRSET "PORT->Group[1].DIRSET.reg") + (PB_DIRTGL "PORT->Group[1].DIRTGL.reg") (PB_OUT "PORT->Group[1].OUT.reg") (PB_OUTCLR "PORT->Group[1].OUTCLR.reg") + (PB_OUTSET "PORT->Group[1].OUTSET.reg") (PB_OUTTGL "PORT->Group[1].OUTTGL.reg") (PB_IN "PORT->Group[1].IN.reg")))) + ("CPU_NRF51822" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT) + (ANALOGREFERENCE AR_DEFAULT AR_VBG AR_SUPPLY_ONE_HALF AR_SUPPLY_ONE_THIRD AR_EXT0 AR_EXT1) + (REGISTER (P0_OUT "NRF_GPIO->OUT") (P0_OUTSET "NRF_GPIO->OUTSET") (P0_OUTCLR "NRF_GPIO->OUTCLR") (P0_IN "NRF_GPIO->IN") + (P0_DIR "NRF_GPIO->DIR") (P0_DIRSET "NRF_GPIO->DIRSET") (P0_DIRCLR "NRF_GPIO->DIRCLR")))) + ("CPU_NRF52840" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT) + (ANALOGREFERENCE AR_DEFAULT AR_INTERNAL AR_INTERNAL_3_0 AR_INTERNAL_2_4 AR_INTERNAL_1_8 AR_INTERNAL_1_2 AR_VDD4) + (REGISTER (P0_OUT "NRF_P0->OUT") (P0_OUTSET "NRF_P0->OUTSET") (P0_OUTCLR "NRF_P0->OUTCLR") (P0_IN "NRF_P0->IN") + (P0_DIR "NRF_P0->DIR") (P0_DIRSET "NRF_P0->DIRSET") (P0_DIRCLR "NRF_P0->DIRCLR") + (P1_OUT "NRF_P1->OUT") (P1_OUTSET "NRF_P1->OUTSET") (P1_OUTCLR "NRF_P1->OUTCLR") (P1_IN "NRF_P1->IN") + (P1_DIR "NRF_P1->DIR") (P1_DIRSET "NRF_P1->DIRSET") (P1_DIRCLR "NRF_P1->DIRCLR")))) + ("CPU_NRF52833" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT) + (ANALOGREFERENCE AR_DEFAULT AR_INTERNAL AR_VDD4) + (REGISTER (P0_OUT "NRF_P0->OUT") (P0_OUTSET "NRF_P0->OUTSET") (P0_OUTCLR "NRF_P0->OUTCLR") (P0_IN "NRF_P0->IN") + (P0_DIR "NRF_P0->DIR") (P0_DIRSET "NRF_P0->DIRSET") (P0_DIRCLR "NRF_P0->DIRCLR") + (P1_OUT "NRF_P1->OUT") (P1_OUTSET "NRF_P1->OUTSET") (P1_OUTCLR "NRF_P1->OUTCLR") (P1_IN "NRF_P1->IN") + (P1_DIR "NRF_P1->DIR") (P1_DIRSET "NRF_P1->DIRSET") (P1_DIRCLR "NRF_P1->DIRCLR")))) + ("CPU_iMXRT1062" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT OUTPUT_OPENDRAIN))) + ("CPU_MAX32620" + ((PINMODE INPUT INPUT_PULLUP OUTPUT) + (ANALOGREFERENCE DEFAULT EXTERNAL))) + ("CPU_RP2040" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT) + (REGISTER (GPIO_IN "(SIO_BASE+SIO_GPIO_IN_OFFSET)") (GPIO_OUT "(SIO_BASE+SIO_GPIO_OUT_OFFSET)") + (GPIO_OUT_SET "(SIO_BASE+SIO_GPIO_OUT_SET_OFFSET)") (GPIO_OUT_CLR "(SIO_BASE+SIO_GPIO_OUT_CLR_OFFSET)") + (GPIO_OUT_XOR "(SIO_BASE+SIO_GPIO_OUT_XOR_OFFSET)") (GPIO_OE "(SIO_BASE+SIO_GPIO_OE_OFFSET)") + (GPIO_OE_SET "(SIO_BASE+SIO_GPIO_OE_SET_OFFSET)") (GPIO_OE_CLR "(SIO_BASE+SIO_GPIO_OE_CLR_OFFSET)") + (GPIO_OE_XOR "(SIO_BASE+SIO_GPIO_OE_XOR_OFFSET)")))) + ("CPU_RP2350" + ((PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT) + (REGISTER (GPIO_IN "(SIO_BASE+SIO_GPIO_IN_OFFSET)") (GPIO_OUT "(SIO_BASE+SIO_GPIO_OUT_OFFSET)") + (GPIO_OUT_SET "(SIO_BASE+SIO_GPIO_OUT_SET_OFFSET)") (GPIO_OUT_CLR "(SIO_BASE+SIO_GPIO_OUT_CLR_OFFSET)") + (GPIO_OUT_XOR "(SIO_BASE+SIO_GPIO_OUT_XOR_OFFSET)") (GPIO_OE "(SIO_BASE+SIO_GPIO_OE_OFFSET)") + (GPIO_OE_SET "(SIO_BASE+SIO_GPIO_OE_SET_OFFSET)") (GPIO_OE_CLR "(SIO_BASE+SIO_GPIO_OE_CLR_OFFSET)") + (GPIO_OE_XOR "(SIO_BASE+SIO_GPIO_OE_XOR_OFFSET)")))) + ("CPU_RA4M1" + ((PINMODE INPUT INPUT_PULLUP OUTPUT OUTPUT_OPENDRAIN) + (ANALOGREFERENCE AR_DEFAULT AR_INTERNAL AR_EXTERNAL))))) \ No newline at end of file diff --git a/builder/assembler.lisp b/builder/assembler.lisp new file mode 100644 index 0000000..2856e7b --- /dev/null +++ b/builder/assembler.lisp @@ -0,0 +1,210 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +(defparameter *assembler* + '( + +#+avr +#" +// Assembler + +#if defined(CPU_ATmega1284P) +#define CODE_ADDRESS 0x1bb00 +#elif defined(CPU_AVR128DX48) +#define CODE_ADDRESS 0x1be00 +#endif + +object *call (int entry, int nargs, object *args, object *env) { +#if defined(CODESIZE) + (void) env; + int param[4]; + for (int i=0; iinteger; + else param[i] = (uintptr_t)arg; + args = cdr(args); + } + uint32_t address = (CODE_ADDRESS + entry)>>1; // Code addresses are word addresses on AVR + int w = ((intfn_ptr_type)address)(param[0], param[1], param[2], param[3]); + return number(w); +#else + return nil; +#endif +}"# + +#+arm +#" +// Assembler + +object *call (int entry, int nargs, object *args, object *env) { +#if defined(CODESIZE) + (void) env; + int param[4]; + for (int i=0; iinteger; + 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 +}"# + +#+riscv +#" +// Assembler + +object *call (int entry, int nargs, object *args, object *env) { +#if defined(CODESIZE) + (void) env; + int param[4]; + for (int i=0; iinteger; + else param[i] = (uintptr_t)arg; + args = cdr(args); + } + asm("fence.i"); + int w = ((intfn_ptr_type)&MyCode[entry])(param[0], param[1], param[2], param[3]); + return number(w); +#else + return nil; +#endif +}"# + +#+avr +#" +void putcode (object *arg, int origin, int pc) { +#if defined(CODESIZE) + int code = checkinteger(arg); + uint8_t hi = (code>>8) & 0xff; + uint8_t lo = code & 0xff; + MyCode[origin+pc] = lo; // Little-endian + MyCode[origin+pc+1] = hi; + #if defined(assemblerlist) + printhex2(pc>>8, pserial); printhex2(pc, pserial); pserial(' '); + printhex2(lo, pserial); pserial(' '); printhex2(hi, pserial); pserial(' '); + #endif +#endif +}"# + +#+(or arm riscv) +#" +void putcode (object *arg, int origin, int pc) { +#if defined(CODESIZE) + int code = checkinteger(arg); + MyCode[origin+pc] = code & 0xff; + MyCode[origin+pc+1] = (code>>8) & 0xff; + #if defined(assemblerlist) + printhex4(pc, pserial); + printhex4(code, pserial); + #endif +#endif +}"# + +#+avr +#" +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) + printhex2(pc>>8, pserial); printhex2(pc, pserial); + indent(7, ' ', 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(PSTR("illegal entry"), arg); + } + entries = cdr(entries); + } + // Round up to multiple of 2 to give code size + if (pc%2 != 0) pc = pc + 2 - pc%2; + return pc; +}"# + +#+(or arm riscv) +#" +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(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; +}"#)) + \ No newline at end of file diff --git a/builder/avr-nano.lisp b/builder/avr-nano.lisp new file mode 100644 index 0000000..2367e4b --- /dev/null +++ b/builder/avr-nano.lisp @@ -0,0 +1,288 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +; AVR + +(defparameter *title-avr-nano* +#"/* uLisp AVR-Nano Release ~a - www.ulisp.com + David Johnson-Davies - www.technoblogy.com - ~a + + Licensed under the MIT license: https://opensource.org/licenses/MIT +*/"#) + +(defparameter *header-avr-nano* #" +// Lisp Library +const char LispLibrary[] PROGMEM = ""; + +// Compile options + +#define checkoverflow +// #define resetautorun +#define printfreespace +// #define printgcs +// #define sdcardsupport +// #define lisplibrary +#define assemblerlist +// #define lineeditor +// #define vt100 + +// Includes + +// #include "LispLibrary.h" +#include +#include +#include +#include +#include + +#if defined(sdcardsupport) +#include +#define SDSIZE 172 +#else +#define SDSIZE 0 +#endif"#) + +(defparameter *workspace-avr-nano* #" +// Platform specific settings + +#define WORDALIGNED __attribute__((aligned (2))) +#define OBJECTALIGNED __attribute__((aligned (4))) +#define BUFFERSIZE 22 /* longest builtin name + 1 */ + +#if defined(ARDUINO_AVR_UNO) + #define WORKSPACESIZE (320-SDSIZE) /* Objects (4*bytes) */ + #define EEPROMSIZE 1024 /* Bytes */ + #define STACKDIFF 1 + #define CPU_ATmega328P + +#elif defined(ARDUINO_AVR_NANO_EVERY) + #define WORKSPACESIZE (1060-SDSIZE) /* Objects (4*bytes) */ + #define EEPROMSIZE 256 /* Bytes */ + #define STACKDIFF 160 + #define CPU_ATmega4809 + +#elif defined(ARDUINO_AVR_ATmega4809) /* Curiosity Nano using MegaCoreX */ + #define Serial Serial3 + #define WORKSPACESIZE (1065-SDSIZE) /* Objects (4*bytes) */ + #define EEPROMSIZE 256 /* Bytes */ + #define STACKDIFF 320 + #define CPU_ATmega4809 + +#elif defined(ARDUINO_AVR_ATtiny3227) + #define WORKSPACESIZE (514-SDSIZE) /* Objects (4*bytes) */ +// #define EEPROMSIZE 256 /* Bytes */ + #define STACKDIFF 1 + #define CPU_ATtiny3227 + +#elif defined(__AVR_AVR64DD28__) + #include + #define WORKSPACESIZE (1440-SDSIZE) /* Objects (4*bytes) */ + #define FLASHWRITESIZE 6144 /* Bytes */ + #define STACKDIFF 1 + #define CPU_AVR64DD28 + +#else +#error "Board not supported!" +#endif"#) + +(defparameter *watchdog-avr-nano* #" +// Watchdog + +void watchdogenable (int interval) { + int i = 5; + while (interval) { interval = interval>>1; i++; } + wdt_enable(i); +} + +void watchdogreset () { + wdt_reset(); +}"#) + + +(defparameter *check-pins-avr-nano* #" +// Check pins - these are board-specific not processor-specific + +void checkanalogread (int pin) { +#if defined(ARDUINO_AVR_UNO) + if (!(pin>=0 && pin<=5)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_AVR_NANO_EVERY) + if (!((pin>=14 && pin<=21))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_AVR_ATmega4809) /* MegaCoreX core */ + if (!((pin>=22 && pin<=33) || (pin>=36 && pin<=39))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_AVR_ATtiny3227) + if (!((pin>=0 && pin<=3) || (pin>=6 && pin<=7) || (pin>=10 && pin<=11) || pin==18)) error(invalidpin, number(pin)); +#endif +} + +void checkanalogwrite (int pin) { +#if defined(ARDUINO_AVR_UNO) + if (!(pin==3 || pin==5 || pin==6 || (pin>=9 && pin<=11))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_AVR_NANO_EVERY) + if (!(pin==3 || pin==5 || pin==6 || pin==9 || pin==10)) error(invalidpin, number(pin)); +#elif defined(ARDUINO_AVR_ATmega4809) /* MegaCoreX core */ + if (!((pin>=16 && pin<=19) || (pin>=38 && pin<=39))) error(invalidpin, number(pin)); +#elif defined(ARDUINO_AVR_ATtiny3227) + if (!((pin>=0 && pin<=1) || (pin>=9 && pin<=11) || pin==20)) error(invalidpin, number(pin)); +#endif +}"#) + +(defparameter *note-avr-nano* #" +// Note + +#if defined(CPU_ATtiny3227) || defined(CPU_AVR64DD28) +const int scale[] PROGMEM = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902}; +#else +const uint8_t scale[] PROGMEM = {239,226,213,201,190,179,169,160,151,142,134,127}; +#endif + +void playnote (int pin, int note, int octave) { +#if defined(CPU_ATmega328P) + if (pin == 3) { + DDRD = DDRD | 1<6) error(PSTR("octave out of range"), number(oct)); + OCR2A = pgm_read_byte(&scale[note%12]) - 1; + TCCR2B = 0<8) error(PSTR("octave out of range"), number(oct)); + tone(pin, scale[note%12]>>prescaler); + +#elif defined(CPU_AVR64DD28) + int oct = octave + note/12; + int prescaler = 8 - oct; + if (prescaler<0 || prescaler>8) error(PSTR("octave out of range"), number(oct)); + tone(pin, pgm_read_word(&scale[note%12])>>prescaler); +#endif +} + +void nonote (int pin) { +#if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) || defined(CPU_AVR64DD28) + noTone(pin); +#else + (void) pin; + TCCR2B = 0< 0) { sleep(); secs--; } + WDTCSR = 1< +#include +#include +#include + +#if defined(sdcardsupport) +#include +#define SDSIZE 172 +#else +#define SDSIZE 0 +#endif"#) + +(defparameter *workspace-avr* #" +// Platform specific settings + +#define WORDALIGNED __attribute__((aligned (2))) +#define OBJECTALIGNED __attribute__((aligned (4))) +#define BUFFERSIZE 22 /* longest builtin name + 1 */ + +#if defined(ARDUINO_AVR_MEGA2560) + #include + #define WORKSPACESIZE (1344-SDSIZE) /* Objects (4*bytes) */ + #define EEPROMSIZE 4096 /* Bytes */ + #define STACKDIFF 320 + #define CPU_ATmega2560 + +#elif defined(__AVR_ATmega1284P__) + #include "optiboot.h" + #define WORKSPACESIZE (2944-SDSIZE) /* Objects (4*bytes) */ +// #define EEPROMSIZE 4096 /* Bytes */ + #define FLASHWRITESIZE 16384 /* Bytes */ + #define CODESIZE 96 /* Bytes <= 256 */ + #define STACKDIFF 320 + #define CPU_ATmega1284P + +#elif defined(__AVR_AVR128DA48__) + #include + #define Serial Serial1 + #define WORKSPACESIZE (2920-SDSIZE) /* Objects (4*bytes) */ + #define FLASHWRITESIZE 15872 /* Bytes */ + #define CODESIZE 96 /* Bytes <= 512 */ + #define STACKDIFF 320 + #define CPU_AVR128DX48 + #define LED_BUILTIN 20 + +#elif defined(__AVR_AVR128DB48__) + #include + #define Serial Serial3 + #define WORKSPACESIZE (2920-SDSIZE) /* Objects (4*bytes) */ + #define FLASHWRITESIZE 15872 /* Bytes */ + #define CODESIZE 96 /* Bytes <= 512 */ + #define STACKDIFF 320 + #define CPU_AVR128DX48 + #define LED_BUILTIN 20 + +#else +#error "Board not supported!" +#endif"#) + +(defparameter *watchdog-avr* #" +// Watchdog + +void watchdogenable (int interval) { + int i = 5; + while (interval) { interval = interval>>1; i++; } + wdt_enable(i); +} + +void watchdogreset () { + wdt_reset(); +}"#) + + +(defparameter *check-pins-avr* #" +// Check pins - these are board-specific not processor-specific + +void checkanalogread (int pin) { +#if defined(ARDUINO_AVR_MEGA2560) + if (!(pin>=0 && pin<=15)) error(invalidpin, number(pin)); +#elif defined(__AVR_ATmega1284P__) + if (!(pin>=0 && pin<=7)) error(invalidpin, number(pin)); +#elif defined(__AVR_AVR128DA48__) + if (!(pin>=22 && pin<=39)) error(invalidpin, number(pin)); +#endif +} + +void checkanalogwrite (int pin) { +#if defined(ARDUINO_AVR_MEGA2560) + if (!((pin>=2 && pin<=13) || (pin>=44 && pin<=46))) error(invalidpin, number(pin)); +#elif defined(__AVR_ATmega1284P__) + if (!(pin==3 || pin==4 || pin==6 || pin==7 || (pin>=12 && pin<=15))) error(invalidpin, number(pin)); +#elif defined(__AVR_AVR128DA48__) + if (!((pin>=4 && pin<=5) || (pin>=8 && pin<=19) || (pin>=38 && pin<=39))) error(invalidpin, number(pin)); +#endif +}"#) + +(defparameter *note-avr* #" +// Note + +#if defined(CPU_AVR128DX48) +const int scale[] PROGMEM = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902}; +#else +const uint8_t scale[] PROGMEM = {239,226,213,201,190,179,169,160,151,142,134,127}; +#endif + +void playnote (int pin, int note, int octave) { +#if defined(CPU_ATmega2560) + if (pin == 9) { + DDRH = DDRH | 1<6) error(PSTR("octave out of range"), number(oct)); + OCR2A = pgm_read_byte(&scale[note%12]) - 1; + TCCR2B = 0<6) error(PSTR("octave out of range"), number(oct)); + OCR2A = pgm_read_byte(&scale[note%12]) - 1; + TCCR2B = 0<8) error(PSTR("octave out of range"), number(oct)); + tone(pin, pgm_read_word(&scale[note%12])>>prescaler); +#endif +} + +void nonote (int pin) { +#if defined(CPU_AVR128DX48) + noTone(pin); +#else + (void) pin; + TCCR2B = 0< 0) { sleep(); secs--; } + WDTCSR = 1< +#include +#include +#include +#include + +#if defined(gfxsupport) +#define COLOR_WHITE ST77XX_WHITE +#define COLOR_BLACK ST77XX_BLACK +#include // Core graphics library +#include // Hardware-specific library for ST7789 +#if defined(ARDUINO_ESP32_DEV) +Adafruit_ST7789 tft = Adafruit_ST7789(5, 16, 19, 18); +#define TFT_BACKLITE 4 +#else +Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, MOSI, SCK, TFT_RST); +#endif +#endif + +#if defined(sdcardsupport) + #include + #define SDSIZE 172 +#else + #define SDSIZE 0 +#endif"#) + +(defparameter *workspace-esp* #" +// Platform specific settings + +#define WORDALIGNED __attribute__((aligned (4))) +#define BUFFERSIZE 36 // Number of bits+4 + +// ESP32 boards *************************************************************** + +#if defined(ARDUINO_ESP32_DEV) /* For TTGO T-Display etc. */ + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 260000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (9216-SDSIZE) /* Objects (8*bytes) */ + #endif + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX6 + +#elif defined(ARDUINO_FEATHER_ESP32) + #define WORKSPACESIZE (9500-SDSIZE) /* Objects (8*bytes) */ + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define CPU_LX6 + +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 250000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (9500-SDSIZE) /* Objects (8*bytes) */ + #endif + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define CPU_LX6 + +#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) || defined(ARDUINO_ESP32_PICO) + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 250000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (9500-SDSIZE) /* Objects (8*bytes) */ + #endif + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX6 + +// ESP32-S2 boards *************************************************************** + +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 250000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (6500-SDSIZE) /* Objects (8*bytes) */ + #endif + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define CPU_LX7 + +#elif defined(ARDUINO_FEATHERS2) /* UM FeatherS2 */ + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 1000000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (8160-SDSIZE) /* Objects (8*bytes) */ + #endif + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX7 + +#elif defined(ARDUINO_ESP32S2_DEV) + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 260000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (8160-SDSIZE) /* Objects (8*bytes) */ + #endif + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX7 + +#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 260000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (7232-SDSIZE) /* Objects (8*bytes) */ + #endif + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX7 + +// ESP32-S3 boards *************************************************************** + +#elif defined(ARDUINO_ESP32S3_DEV) + #define WORKSPACESIZE (25000-SDSIZE) /* Objects (8*bytes) */ + #define MAX_STACK 6500 + #define LITTLEFS + #include + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX7 + +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_TFT) + #if defined(BOARD_HAS_PSRAM) + #define WORKSPACESIZE 250000 /* Objects (8*bytes) */ + #else + #define WORKSPACESIZE (22000-SDSIZE) /* Objects (8*bytes) */ + #endif + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX7 + +// ESP32-C3 boards *************************************************************** + +#elif defined(ARDUINO_ESP32C3_DEV) + #define WORKSPACESIZE (9216-SDSIZE) /* Objects (8*bytes) */ + #define MAX_STACK 7500 + #define LITTLEFS + #include + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_RISC_V + +#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) + #define WORKSPACESIZE (9216-SDSIZE) /* Objects (8*bytes) */ + #define MAX_STACK 8000 + #define LITTLEFS + #include + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_RISC_V + +// Legacy boards *************************************************************** + +#elif defined(ESP32) /* Generic ESP32 board */ + #define WORKSPACESIZE (9216-SDSIZE) /* Objects (8*bytes) */ + #define MAX_STACK 7000 + #define LITTLEFS + #include + #define analogWrite(x,y) dacWrite((x),(y)) + #define SDCARD_SS_PIN 13 + #define LED_BUILTIN 13 + #define CPU_LX6 + +#else +#error "Board not supported!" +#endif"#) + +(defparameter *check-pins-esp* #" +// Check pins + +void checkanalogread (int pin) { +#if defined(ESP32) || defined(ARDUINO_ESP32_DEV) + if (!(pin==0 || pin==2 || pin==4 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=36) || pin==39)) + error("invalid pin", number(pin)); +#elif defined(ARDUINO_FEATHER_ESP32) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) + if (!(pin==4 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=36) || pin==39)) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) + if (!(pin==8 || (pin>=14 && pin<=18))) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) + if (!(pin==4 || pin==7 || (pin>=12 && pin<=15) || (pin>=25 && pin<=27) || (pin>=32 && pin<=33))) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) + if (!((pin>=5 && pin<=9) || (pin>=16 && pin<=18))) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) + if (!((pin>=0 && pin<=1) || (pin>=3 && pin<=5))) error("invalid pin", number(pin)); +#elif defined(ARDUINO_FEATHERS2) || defined(ARDUINO_ESP32S2_DEV) + if (!((pin>=1 && pin<=20))) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ESP32C3_DEV) + if (!((pin>=0 && pin<=5))) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ESP32S3_DEV) + if (!((pin>=1 && pin<=20))) error("invalid pin", number(pin)); +#endif +} + +void checkanalogwrite (int pin) { +#if defined(ESP32) || defined(ARDUINO_FEATHER_ESP32) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) || defined(ARDUINO_ESP32_DEV) \ + || defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) + if (!(pin>=25 && pin<=26)) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) \ + || defined(ARDUINO_FEATHERS2) || defined(ARDUINO_ESP32S2_DEV) + if (!(pin>=17 && pin<=18)) error("invalid pin", number(pin)); +#elif defined(ARDUINO_ESP32C3_DEV) || defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) + error2(ANALOGWRITE, "not supported"); +#endif +}"#) + + +(defparameter *note-esp* #" +// Note + +void tone (int pin, int note) { + (void) pin, (void) note; +} + +void noTone (int pin) { + (void) pin; +} + +const int scale[] PROGMEM = {4186,4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902}; + +void playnote (int pin, int note, int octave) { + int oct = octave + note/12; + int prescaler = 8 - oct; + if (prescaler<0 || prescaler>8) error(PSTR("octave out of range"), number(oct)); + tone(pin, scale[note%12]>>prescaler); +} + +void nonote (int pin) { + noTone(pin); +}"#) + +(defparameter *sleep-esp* #" +// Sleep + +void initsleep () { } + +void doze (int secs) { + delay(1000 * secs); +}"#) + +(defparameter *keywords-esp* + '((nil + ((NIL LED_BUILTIN) + (DIGITALWRITE HIGH LOW) + (PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT))))) \ No newline at end of file diff --git a/builder/extras.lisp b/builder/extras.lisp new file mode 100644 index 0000000..f1f41bc --- /dev/null +++ b/builder/extras.lisp @@ -0,0 +1,125 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +; To run do (build) + +; Sharp-double-quote + +(defun sharp-double-quote-reader (stream sub-char numarg) + (declare (ignore sub-char numarg)) + (let (chars) + (do ((prev (read-char stream) curr) + (curr (read-char stream) (read-char stream))) + ((and (char= prev #\") (char= curr #\#))) + (push prev chars)) + (coerce (nreverse chars) 'string))) + +(set-dispatch-macro-character + #\# #\" #'sharp-double-quote-reader) + +; Code generation functions + +(defun float-function (str enum string comments) + (declare (ignore string)) + (format str " +~:[~2*~;/* + (~a number) + Returns ~a(number). +*/ +~]object *fn_~a (object *args, object *env) { + (void) env; + return makefloat(~a(checkintfloat(first(args)))); +}" + comments + (string-downcase enum) + (string-downcase enum) + (string-downcase enum) + (string-downcase enum))) + +(defun truncate-function (str enum string comments) + (declare (ignore string)) + (format str " +~:[~2*~;/* + (~a number [divisor]) + Returns ~a(number/divisor). If omitted, divisor is 1. +*/ +~]object *fn_~a (object *args, object *env) { + (void) env; + object *arg = first(args); + args = cdr(args); + if (args != NULL) return number(~a(checkintfloat(arg) / checkintfloat(first(args)))); + else return number(~a(checkintfloat(arg))); +}" + comments + (string-downcase enum) + (cdr (assoc enum '((CEILING . "ceil") (FLOOR . "floor") (TRUNCATE . "trunc") (ROUND . "round")))) + (string-downcase enum) + (cdr (assoc enum '((CEILING . "ceil") (FLOOR . "floor") (TRUNCATE . "trunc") (ROUND . "round")))) + (cdr (assoc enum '((CEILING . "ceil") (FLOOR . "floor") (TRUNCATE . "trunc") (ROUND . "round")))))) + +#| +(defun numeric1 (str enum string comments) + (declare (ignore string)) + (format str " +object *fn_~a (object *args, object *env) { + (void) env; + int arg = checkinteger(~a, first(args)); + return number(~a(arg)); +}" (string-downcase enum) enum (string-downcase enum))) +|# + +(defun bitwise (str enum string comments) + (declare (ignore string)) + (format str " +~:[~2*~;/* + (~a [value*]) + Returns the bitwise ~a of the values. +*/ +~]object *fn_~a (object *args, object *env) { + (void) env; + int result = ~a; + while (args != NULL) { + result = result ~a checkinteger(first(args)); + args = cdr(args); + } + return number(result); +}" + comments + (string-downcase enum) + (cdr (assoc enum '((LOGAND . "&") (LOGIOR . "|") (LOGXOR . "^")))) + (string-downcase enum) + (cdr (assoc enum '((LOGAND . "-1") (LOGIOR . "0") (LOGXOR . "0")))) + (cdr (assoc enum '((LOGAND . "&") (LOGIOR . "|") (LOGXOR . "^")))))) + +#| +; For max or min + +(defun numeric2 (str enum string comments) + (declare (ignore string)) + (format str " +object *fn_~a (object *args, object *env) { + (void) env; + int result = integer(first(args)); + args = cdr(args); + while (args != NULL) { + result = ~a(result,integer(car(args))); + args = cdr(args); + } + return number(result); +}" (string-downcase enum) (string-downcase enum))) +|# + +(defun split-into-lines (string &optional (indent 0)) + (let* ((linelen 106) + (start 0) + (end (- linelen indent)) + (length (length string)) + result) + (loop + (when (>= end length) + (push (subseq string start) result) + (return (reverse result))) + (let ((comma (position #\, string :start start :end end :from-end t))) + (push (subseq string start (1+ comma)) result) + (setq start (+ comma 2) end (+ comma 2 linelen)))))) \ No newline at end of file diff --git a/builder/functions.lisp b/builder/functions.lisp new file mode 100644 index 0000000..a39e0df --- /dev/null +++ b/builder/functions.lisp @@ -0,0 +1,6200 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +; Function definitions + +(defparameter *definitions* + + '((nil ;; Symbols + ((NIL "nil" 0 0 #" +/* + nil + A symbol equivalent to the empty list (). Also represents false. +*/"#) + + (TEE "t" 0 0 #" +/* + t + A symbol representing true. +*/"#) + + (NOTHING nil 0 0 #" +/* + nothing + A symbol with no value. + It is useful if you want to suppress printing the result of evaluating a function. +*/"#) + + (OPTIONAL "&optional" 0 0 #" +/* + &optional + Can be followed by one or more optional parameters in a lambda or defun parameter list. +*/"#) + + #-avr-nano + (FEATURES "*features*" 0 0 #" +/* + *features* + Returns a list of keywords representing features supported by this platform. +*/"#) + + #+arrays + (INITIALELEMENT ":initial-element" 0 0 nil) + + #+arrays + (ELEMENTTYPE ":element-type" 0 0 nil) + + #-avr-nano + (TEST ":test" 0 0 nil) + + #-avr-nano + (COLONA ":a" 0 0 nil) + + #-avr-nano + (COLONB ":b" 0 0 nil) + + #-avr-nano + (COLONC ":c" 0 0 nil) + + #+arrays + (BIT nil 0 0 nil) + + (AMPREST "&rest" 0 0 #" +/* + &rest + Can be followed by a parameter in a lambda or defun parameter list, + and is assigned a list of the corresponding arguments. +*/"#) + + (LAMBDA nil 1 127 #" +/* + (lambda (parameter*) form*) + Creates an unnamed function with parameters. The body is evaluated with the parameters as local variables + whose initial values are defined by the values of the forms after the lambda form. +*/"#) + + (LET "let" 1 127 #" +/* + (let ((var value) ... ) forms*) + Declares local variables with values, and evaluates the forms with those local variables. +*/"#) + + (LETSTAR "let*" 1 127 #" +/* + (let* ((var value) ... ) forms*) + Declares local variables with values, and evaluates the forms with those local variables. + Each declaration can refer to local variables that have been defined earlier in the let*. +*/"#) + + (CLOSURE nil 1 127 nil) + + #+avr + (PSTAR "*p*" 0 127 nil) + + #-(or avr avr-nano) + (PSTAR "*pc*" 0 127 nil)) + + "sy") + + ("Special forms" + ((QUOTE nil 1 1 " +object *sp_quote (object *args, object *env) { + (void) env; + return first(args); +}") + + (OR nil 0 127 " +/* + (or item*) + Evaluates its arguments until one returns non-nil, and returns its value. +*/ +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; +}") + + #+ignore + (LAMBDA nil 0 127 " +object *sp_lambda (object *args, object *env) { + return cons(symbol(CLOSURE), (cons(env,args))); +}") + + (DEFUN nil 2 127 #" +/* + (defun name (parameters) form*) + Defines a function. +*/ +object *sp_defun (object *args, object *env) { + (void) env; + object *var = first(args); + if (!symbolp(var)) error(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; +}"#) + + (DEFVAR nil 1 3 #" +/* + (defvar variable form) + Defines a global variable. +*/ +object *sp_defvar (object *args, object *env) { + object *var = first(args); + if (!symbolp(var)) error(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; +}"#) + + (SETQ nil 2 126 #" +/* + (setq symbol value [symbol value]*) + For each pair of arguments assigns the value of the second argument + to the variable specified in the first argument. +*/ +object *sp_setq (object *args, object *env) { + object *arg = nil; builtin_t setq = Context; + while (args != NULL) { + if (cdr(args) == NULL) { Context = setq; error2(oddargs); } + object *pair = findvalue(first(args), env); + arg = eval(second(args), env); + cdr(pair) = arg; + args = cddr(args); + } + return arg; +}"#) + + #-esp + (LOOP nil 0 127 " +/* + (loop forms*) + Executes its arguments repeatedly until one of the arguments calls (return), + which then causes an exit from the loop. +*/ +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); + } + testescape(); + } +}") + + #+esp + (LOOP nil 0 127 " +/* + (loop forms*) + Executes its arguments repeatedly until one of the arguments calls (return), + which then causes an exit from the loop. +*/ +object *sp_loop (object *args, object *env) { + object *start = args; + for (;;) { + yield(); + args = start; + while (args != NULL) { + object *result = eval(car(args),env); + if (tstflag(RETURNFLAG)) { + clrflag(RETURNFLAG); + return result; + } + args = cdr(args); + } + testescape(); + } +}") + + #+avr-nano + (PUSH nil 2 2 " +/* + (push item place) + Modifies the value of place, which should be a list, to add item onto the front of the list, + and returns the new list. +*/ +object *sp_push (object *args, object *env) { + object *item = eval(first(args), env); + object **loc = place(second(args), env); + push(item, *loc); + return *loc; +}") + + #-avr-nano + (PUSH nil 2 2 " +/* + (push item place) + Modifies the value of place, which should be a list, to add item onto the front of the list, + and returns the new list. +*/ +object *sp_push (object *args, object *env) { + int bit; + object *item = eval(first(args), env); + object **loc = place(second(args), env, &bit); + if (bit != -1) error2(invalidarg); + push(item, *loc); + return *loc; +}") + + #+avr-nano + (POP nil 1 1 " +/* + (pop place) + Modifies the value of place, which should be a non-nil list, to remove its first item, + and returns that item. +*/ +object *sp_pop (object *args, object *env) { + object *arg = first(args); + if (arg == NULL) error2(invalidarg); + object **loc = place(arg, env); + if (!consp(*loc)) error(notalist, *loc); + object *result = car(*loc); + pop(*loc); + return result; +}") + + #-avr-nano + (POP nil 1 1 " +/* + (pop place) + Modifies the value of place, which should be a non-nil list, to remove its first item, + and returns that item. +*/ +object *sp_pop (object *args, object *env) { + int bit; + object *arg = first(args); + if (arg == NULL) error2(invalidarg); + object **loc = place(arg, env, &bit); + if (bit < -1) error(invalidarg, arg); + if (!consp(*loc)) error(notalist, *loc); + object *result = car(*loc); + pop(*loc); + return result; +}")) "sp") + + + ("Accessors" + ( + #-float + (INCF nil 1 2 #" +/* + (incf place [number]) + Increments a place, which should have an numeric value, and returns the result. + The third argument is an optional increment which defaults to 1. +*/ +object *sp_incf (object *args, object *env) { + return incfdecf(args, 1, env); +}"#) + + #+float + (INCF nil 1 2 #" +/* + (incf place [number]) + Increments a place, which should have an numeric value, and returns the result. + The third argument is an optional increment which defaults to 1. +*/ +object *sp_incf (object *args, object *env) { + int bit; + object **loc = place(first(args), env, &bit); + if (bit < -1) error2(notanumber); + 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(inc); + int newvalue = (((*loc)->integer)>>bit & 1) + increment; + + if (newvalue & ~1) error2(PSTR("result is not a bit value")); + *loc = number((((*loc)->integer) & ~(1<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(notanumber); + return *loc; +}"#) + + #-float + (DECF nil 1 2 #" +/* + (decf place [number]) + Decrements a place, which should have an numeric value, and returns the result. + The third argument is an optional decrement which defaults to 1. +*/ +object *sp_decf (object *args, object *env) { + return incfdecf(args, -1, env); +}"#) + + #+float + (DECF nil 1 2 #" +/* + (decf place [number]) + Decrements a place, which should have an numeric value, and returns the result. + The third argument is an optional decrement which defaults to 1. +*/ +object *sp_decf (object *args, object *env) { + int bit; + object **loc = place(first(args), env, &bit); + if (bit < -1) error2(notanumber); + 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(dec); + int newvalue = (((*loc)->integer)>>bit & 1) - decrement; + + if (newvalue & ~1) error2(PSTR("result is not a bit value")); + *loc = number((((*loc)->integer) & ~(1<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(notanumber); + return *loc; +}"#) + + #+avr-nano + (SETF nil 2 126 #" +/* + (setf place value [place value]*) + For each pair of arguments modifies a place to the result of evaluating value. +*/ +object *sp_setf (object *args, object *env) { + builtin_t setf = Context; + object *arg = nil; + while (args != NULL) { + if (cdr(args) == NULL) { Context = setf; error2(oddargs); } + object **loc = place(first(args), env); + arg = eval(second(args), env); + *loc = arg; + args = cddr(args); + } + return arg; +}"#) + + #-avr-nano + (SETF nil 2 126 #" +/* + (setf place value [place value]*) + For each pair of arguments modifies a place to the result of evaluating value. +*/ +object *sp_setf (object *args, object *env) { + int bit; builtin_t setf = Context; + object *arg = nil; + while (args != NULL) { + if (cdr(args) == NULL) { Context = setf; error2(oddargs); } + object **loc = place(first(args), env, &bit); + arg = eval(second(args), env); + if (bit == -1) *loc = arg; + else if (bit < -1) (*loc)->chars = ((*loc)->chars & ~(0xff<<((-bit-2)<<3))) | checkchar(arg)<<((-bit-2)<<3); + else *loc = number((checkinteger(*loc) & ~(1<name); + args = cdr(args); + } + int i = 0; + while (i < TRACEMAX) { + if (TraceFn[i] != 0) args = cons(symbol(TraceFn[i]), args); + i++; + } + return args; +}"#) + + (UNTRACE nil 0 1 #" +/* + (untrace [function]*) + Turns off tracing of up to TRACEMAX user-defined functions, and returns a list of the functions untraced. + If no functions are specified it untraces all functions. +*/ +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(notasymbol, var); + untrace(var->name); + args = cdr(args); + } + } + return args; +}"#) + + (FORMILLIS "for-millis" 1 127 " +/* + (for-millis ([number]) form*) + Executes the forms and then waits until a total of number milliseconds have elapsed. + Returns the total number of milliseconds taken. +*/ +object *sp_formillis (object *args, object *env) { + object *param = checkarguments(args, 0, 1); + unsigned long start = millis(); + unsigned long now, total = 0; + if (param != NULL) total = checkinteger(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; +}") + + (TIME nil 1 1 #" +/* + (time form) + Prints the value returned by the form, and the time taken to evaluate the form + in milliseconds or seconds. +*/ +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); +}"#) + + (WITHOUTPUTTOSTRING "with-output-to-string" 1 127 " +/* + (with-output-to-string (str) form*) + Returns a string containing the output to the stream variable str. +*/ +object *sp_withoutputtostring (object *args, object *env) { + object *params = checkarguments(args, 1, 1); + object *var = first(params); + object *pair = cons(var, stream(STRINGSTREAM, 0)); + push(pair,env); + object *string = startstring(); + protect(string); + object *forms = cdr(args); + eval(tf_progn(forms,env), env); + unprotect(); + return string; +}") + + (WITHSERIAL "with-serial" 1 127 " +/* + (with-serial (str port [baud]) form*) + Evaluates the forms with str bound to a serial-stream using port. + The optional baud gives the baud rate divided by 100, default 96. +*/ +object *sp_withserial (object *args, object *env) { + object *params = checkarguments(args, 2, 3); + object *var = first(params); + int address = checkinteger(eval(second(params), env)); + params = cddr(params); + int baud = 96; + if (params != NULL) baud = checkinteger(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; +}") + + #-(or arm esp) + (WITHI2C "with-i2c" 1 127 " +/* + (with-i2c (str [port] address [read-p]) form*) + Evaluates the forms with str bound to an i2c-stream defined by address. + If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes + to be read from the stream. The port if specified is ignored. +*/ +object *sp_withi2c (object *args, object *env) { + object *params = checkarguments(args, 2, 4); + object *var = first(params); + int address = checkinteger(eval(second(params), env)); + params = cddr(params); + if (address == 0 && params != NULL) params = cdr(params); // Ignore port + int read = 0; // Write + I2Ccount = 0; + if (params != NULL) { + object *rw = eval(first(params), env); + if (integerp(rw)) I2Ccount = rw->integer; + read = (rw != NULL); + } + I2Cinit(1); // Pullups + object *pair = cons(var, (I2Cstart(address, read)) ? stream(I2CSTREAM, address) : nil); + push(pair,env); + object *forms = cdr(args); + object *result = eval(tf_progn(forms,env), env); + I2Cstop(read); + return result; +}") + + #+(or arm esp) + (WITHI2C "with-i2c" 1 127 " +/* + (with-i2c (str [port] address [read-p]) form*) + Evaluates the forms with str bound to an i2c-stream defined by address. + If read-p is nil or omitted the stream is written to, otherwise it specifies the number of bytes + to be read from the stream. If port is omitted it defaults to 0, otherwise it specifies the port, 0 or 1. +*/ +object *sp_withi2c (object *args, object *env) { + object *params = checkarguments(args, 2, 4); + object *var = first(params); + int address = checkinteger(eval(second(params), env)); + params = cddr(params); + if ((address == 0 || address == 1) && params != NULL) { + address = address * 128 + checkinteger(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; +}") + + #+(or avr avr-nano esp) + (WITHSPI "with-spi" 1 127 #" +/* + (with-spi (str pin [clock] [bitorder] [mode]) form*) + Evaluates the forms with str bound to an spi-stream. + The parameters specify the enable pin, clock in kHz (default 4000), + bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), and SPI mode (default 0). +*/ +object *sp_withspi (object *args, object *env) { + object *params = checkarguments(args, 2, 6); + object *var = first(params); + params = cdr(params); + if (params == NULL) error2(nostream); + int pin = checkinteger(eval(car(params), env)); + pinMode(pin, OUTPUT); + digitalWrite(pin, HIGH); + params = cdr(params); + int clock = 4000, mode = SPI_MODE0; // Defaults + int bitorder = MSBFIRST; + if (params != NULL) { + clock = checkinteger(eval(car(params), env)); + params = cdr(params); + if (params != NULL) { + bitorder = (checkinteger(eval(car(params), env)) == 0) ? LSBFIRST : MSBFIRST; + params = cdr(params); + if (params != NULL) { + int modeval = checkinteger(eval(car(params), env)); + mode = (modeval == 3) ? SPI_MODE3 : (modeval == 2) ? SPI_MODE2 : (modeval == 1) ? SPI_MODE1 : SPI_MODE0; + } + } + } + object *pair = cons(var, stream(SPISTREAM, pin)); + push(pair,env); + SPI.begin(); + SPI.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); + SPI.endTransaction(); + return result; +}"#) + + #+arm + (WITHSPI "with-spi" 1 127 #" +/* + (with-spi (str pin [clock] [bitorder] [mode] [port]) form*) + Evaluates the forms with str bound to an spi-stream. + The parameters specify the enable pin, clock in kHz (default 4000), + bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), SPI mode (default 0), and port 0 or 1 (default 0). +*/ +object *sp_withspi (object *args, object *env) { + object *params = checkarguments(args, 2, 6); + object *var = first(params); + params = cdr(params); + if (params == NULL) error2(nostream); + int pin = checkinteger(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(eval(car(params), env)); + params = cdr(params); + if (params != NULL) { + bitorder = (checkinteger(eval(car(params), env)) == 0) ? LSBFIRST : MSBFIRST; + params = cdr(params); + if (params != NULL) { + int modeval = checkinteger(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(eval(car(params), env)); + } + } + } + } + object *pair = cons(var, stream(SPISTREAM, pin + 128*address)); + push(pair,env); + SPIClass *spiClass = &SPI; + #if defined(ULISP_SPI1) + 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; +}"#) + + #+riscv + (WITHSPI "with-spi" 1 127 #" +/* + (with-spi (str pin [clock] [bitorder] [mode] [port]) form*) + Evaluates the forms with str bound to an spi-stream. + The parameters specify the enable pin, clock in kHz (default 4000), + bitorder 0 for LSBFIRST and 1 for MSBFIRST (default 1), SPI mode (default 0), and port 0 or 1 (default 0). +*/ +object *sp_withspi (object *args, object *env) { + object *params = checkarguments(args, 2, 6); + object *var = first(params); + params = cdr(params); + if (params == NULL) error2(nostream); + int pin = checkinteger(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(eval(car(params), env)); + params = cdr(params); + if (params != NULL) { + bitorder = (checkinteger(eval(car(params), env)) == 0) ? LSBFIRST : MSBFIRST; + params = cdr(params); + if (params != NULL) { + int modeval = checkinteger(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(eval(car(params), env)); + } + } + } + } + object *pair = cons(var, stream(SPISTREAM, pin + 128*address)); + push(pair,env); + SPIClass *spiClass = &SPI; + (*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; +}"#) + + #+arm + (WITHSDCARD "with-sd-card" 2 127 #" +/* + (with-sd-card (str filename [mode]) form*) + Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename. + If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite. +*/ +object *sp_withsdcard (object *args, object *env) { + #if defined(sdcardsupport) + object *params = checkarguments(args, 2, 3); + object *var = first(params); + params = cdr(params); + if (params == NULL) error2(PSTR("no filename specified")); + builtin_t temp = Context; + object *filename = eval(first(params), env); + Context = temp; + if (!stringp(filename)) error(PSTR("filename is not a string"), filename); + params = cdr(params); + SDBegin(); + int mode = 0; + if (params != NULL && first(params) != NULL) mode = checkinteger(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(PSTR("problem writing to SD card or invalid filename")); + } else { + char buffer[BUFFERSIZE]; + SDgfile = SD.open(MakeFilename(filename, buffer), oflag); + if (!SDgfile) error2(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(PSTR("not supported")); + return nil; + #endif +}"#) + + #+esp + (WITHSDCARD "with-sd-card" 2 127 #" +/* + (with-sd-card (str filename [mode]) form*) + Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename. + If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite. +*/ +object *sp_withsdcard (object *args, object *env) { + #if defined(sdcardsupport) + object *params = checkarguments(args, 2, 3); + object *var = first(params); + params = cdr(params); + if (params == NULL) error2(PSTR("no filename specified")); + builtin_t temp = Context; + object *filename = eval(first(params), env); + Context = temp; + if (!stringp(filename)) error(PSTR("filename is not a string"), filename); + params = cdr(params); + SDBegin(); + int mode = 0; + if (params != NULL && first(params) != NULL) mode = checkinteger(first(params)); + const char *oflag = FILE_READ; + if (mode == 1) oflag = FILE_APPEND; else if (mode == 2) oflag = FILE_WRITE; + if (mode >= 1) { + char buffer[BUFFERSIZE]; + SDpfile = SD.open(MakeFilename(filename, buffer), oflag); + if (!SDpfile) error2(PSTR("problem writing to SD card or invalid filename")); + } else { + char buffer[BUFFERSIZE]; + SDgfile = SD.open(MakeFilename(filename, buffer), oflag); + if (!SDgfile) error2(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(PSTR("not supported")); + return nil; + #endif +}"#) + + #+(or avr avr-nano) + (WITHSDCARD "with-sd-card" 2 127 #" +/* + (with-sd-card (str filename [mode]) form*) + Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename. + If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite. +*/ +object *sp_withsdcard (object *args, object *env) { + #if defined(sdcardsupport) + object *params = checkarguments(args, 2, 3); + object *var = first(params); + params = cdr(params); + if (params == NULL) error2(PSTR("no filename specified")); + builtin_t temp = Context; + object *filename = eval(first(params), env); + Context = temp; + if (!stringp(filename)) error(PSTR("filename is not a string"), filename); + params = cdr(params); + SD.begin(SDCARD_SS_PIN); + int mode = 0; + if (params != NULL && first(params) != NULL) mode = checkinteger(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(PSTR("problem writing to SD card or invalid filename")); + } else { + char buffer[BUFFERSIZE]; + SDgfile = SD.open(MakeFilename(filename, buffer), oflag); + if (!SDgfile) error2(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(PSTR("not supported")); + return nil; + #endif +}"#) + + #+riscv + (WITHSDCARD "with-sd-card" 2 127 #" +/* + (with-sd-card (str filename [mode]) form*) + Evaluates the forms with str bound to an sd-stream reading from or writing to the file filename. + If mode is omitted the file is read, otherwise 0 means read, 1 write-append, or 2 write-overwrite. +*/ +object *sp_withsdcard (object *args, object *env) { + #if defined(sdcardsupport) + object *params = checkarguments(args, 2, 3); + object *var = first(params); + params = cdr(params); + if (params == NULL) error2(PSTR("no filename specified")); + builtin_t temp = Context; + object *filename = eval(first(params), env); + Context = temp; + if (!stringp(filename)) error(PSTR("filename is not a string"), filename); + params = cdr(params); + if (!SD.begin(SS)) error2("problem initialising SD card"); + int mode = 0; + if (params != NULL && first(params) != NULL) mode = checkinteger(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(PSTR("problem writing to SD card or invalid filename")); + } else { + char buffer[BUFFERSIZE]; + SDgfile = SD.open(MakeFilename(filename, buffer), oflag); + if (!SDgfile) error2(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(PSTR("not supported")); + return nil; + #endif +}"#) + + #+msp430 + (WITHLCD "with-lcd" 1 127 #" +object *sp_withlcd (object *args, object *env) { + #if defined(__MSP430FR6989__) + myLCD.init(); + object *params = first(args); + object *var = first(params); + object *pair = cons(var, stream(LCDSTREAM, 1)); + push(pair,env); + object *forms = cdr(args); + object *result = eval(tf_progn(forms,env), env); + return result; + #else + (void) args, (void) env; + error(PSTR("with-lcd not supported")); + return nil; +#endif +}"#)) "sp") + + #+(or avr arm stm32 riscv) + ("Assembler" + + ( + #+avr + (DEFCODE nil 0 127 #" +/* + (defcode name (parameters) form*) + Creates a machine-code function called name from a series of 16-bit integers given in the body of the form. + These are written into RAM, and can be executed by calling the function in the same way as a normal Lisp function. +*/ +object *sp_defcode (object *args, object *env) { +#if defined(CODESIZE) + setflag(NOESC); + object *var = first(args); + if (!symbolp(var)) error(PSTR("not a symbol"), var); + + // Make *p* 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(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); iinteger = target<<8 | 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)<<8 | origin), args); + object *pair = value(var->name, GlobalEnv); + if (pair != NULL) cdr(pair) = val; + else push(cons(var, val), GlobalEnv); + + #if defined(CPU_ATmega1284P) + // Use Optiboot Flasher in MightyCore with 256 byte page from CODE_ADDRESS 0x1bb00 to 0x1bbff + optiboot_page_erase(CODE_ADDRESS); + for (unsigned int i=0; i 3) error(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(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); iinteger = 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(PSTR("not available")); + return nil; +#endif +}"#) + + #+riscv + (DEFCODE nil 0 127 #" +/* + (defcode name (parameters) form*) + Creates a machine-code function called name from a series of 16-bit integers given in the body of the form. + These are written into RAM, and can be executed by calling the function in the same way as a normal Lisp function. +*/ +object *sp_defcode (object *args, object *env) { + setflag(NOESC); + object *var = first(args); + object *params = second(args); + if (!symbolp(var)) error(PSTR("not a symbol"), var); + + // Make parameters into synonyms for registers a0, a1, etc + int regn = 0; + while (params != NULL) { + if (regn > 3) error(PSTR("more than 4 parameters"), var); + object *regpair = cons(car(params), bsymbol((builtin_t)((toradix40('a')*40+toradix40('0')+regn)*2560000))); // Symbol for a0 etc + push(regpair,env); + regn++; + params = cdr(params); + } + + // Make *pc* a local variable + 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(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); iinteger = 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; +}"#)) "sp") + + ("Tail-recursive forms" + ((PROGN nil 0 127 " +/* + (progn form*) + Evaluates several forms grouped together into a block, and returns the result of evaluating the last form. +*/ +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 quote(result); + args = more; + more = cdr(args); + } + return car(args); +}") + + (IF nil 2 3 #" +/* + (if test then [else]) + Evaluates test. If it's non-nil the form then is evaluated and returned; + otherwise the form else is evaluated and returned. +*/ +object *tf_if (object *args, object *env) { + if (args == NULL || cdr(args) == NULL) error2(toofewargs); + if (eval(first(args), env) != nil) return second(args); + args = cddr(args); + return (args != NULL) ? first(args) : nil; +}"#) + + (COND nil 0 127 #" +/* + (cond ((test form*) (test form*) ... )) + Each argument is a list consisting of a test optionally followed by one or more forms. + If the test evaluates to non-nil the forms are evaluated, and the last value is returned as the result of the cond. + If the test evaluates to nil, none of the forms are evaluated, and the next argument is processed in the same way. +*/ +object *tf_cond (object *args, object *env) { + while (args != NULL) { + object *clause = first(args); + if (!consp(clause)) error(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; +}"#) + + (WHEN nil 1 127 #" +/* + (when test form*) + Evaluates the test. If it's non-nil the forms are evaluated and the last value is returned. +*/ +object *tf_when (object *args, object *env) { + if (args == NULL) error2(noargument); + if (eval(first(args), env) != nil) return tf_progn(cdr(args),env); + else return nil; +}"#) + + (UNLESS nil 1 127 #" +/* + (unless test form*) + Evaluates the test. If it's nil the forms are evaluated and the last value is returned. +*/ +object *tf_unless (object *args, object *env) { + if (args == NULL) error2(noargument); + if (eval(first(args), env) != nil) return nil; + else return tf_progn(cdr(args),env); +}"#) + + (CASE nil 1 127 #" +/* + (case keyform ((key form*) (key form*) ... )) + Evaluates a keyform to produce a test key, and then tests this against a series of arguments, + each of which is a list containing a key optionally followed by one or more forms. +*/ +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(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; +}"#) + + (AND nil 0 127 " +/* + (and item*) + Evaluates its arguments until one returns nil, and returns the last value. +*/ +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); +}")) "tf") + + ("Core functions" + ((NOT nil 1 1 " +/* + (not item) + Returns t if its argument is nil, or nil otherwise. Equivalent to null. +*/ +object *fn_not (object *args, object *env) { + (void) env; + return (first(args) == nil) ? tee : nil; +}") + + (NULLFN "null" 1 1 (not)) + + (CONS nil 2 2 " +/* + (cons item item) + If the second argument is a list, cons returns a new list with item added to the front of the list. + If the second argument isn't a list cons returns a dotted pair. +*/ +object *fn_cons (object *args, object *env) { + (void) env; + return cons(first(args), second(args)); +}") + + (ATOM nil 1 1 " +/* + (atom item) + Returns t if its argument is a single number, symbol, or nil. +*/ +object *fn_atom (object *args, object *env) { + (void) env; + return atom(first(args)) ? tee : nil; +}") + + (LISTP nil 1 1 " +/* + (listp item) + Returns t if its argument is a list. +*/ +object *fn_listp (object *args, object *env) { + (void) env; + return listp(first(args)) ? tee : nil; +}") + + (CONSP nil 1 1 " +/* + (consp item) + Returns t if its argument is a non-null list. +*/ +object *fn_consp (object *args, object *env) { + (void) env; + return consp(first(args)) ? tee : nil; +}") + + (SYMBOLP nil 1 1 #" +/* + (symbolp item) + Returns t if its argument is a symbol. +*/ +object *fn_symbolp (object *args, object *env) { + (void) env; + object *arg = first(args); + return (arg == NULL || symbolp(arg)) ? tee : nil; +}"#) + + #-avr-nano + (ARRAYP nil 1 1 #" +/* + (arrayp item) + Returns t if its argument is an array. +*/ +object *fn_arrayp (object *args, object *env) { + (void) env; + return arrayp(first(args)) ? tee : nil; +}"#) + + (BOUNDP nil 1 1 #" +/* + (boundp item) + Returns t if its argument is a symbol with a value. +*/ +object *fn_boundp (object *args, object *env) { + return boundp(first(args), env) ? tee : nil; +}"#) + + #+avr-nano + (KEYWORDP nil 1 1 #" +/* + (keywordp item) + Returns t if its argument is a keyword. +*/ +object *fn_keywordp (object *args, object *env) { + (void) env; + return keywordp(first(args)) ? tee : nil; +}"#) + + #-avr-nano + (KEYWORDP nil 1 1 #" +/* + (keywordp item) + Returns t if its argument is a built-in or user-defined keyword. +*/ +object *fn_keywordp (object *args, object *env) { + (void) env; + object *arg = first(args); + if (!symbolp(arg)) return nil; + return (keywordp(arg) || colonp(arg->name)) ? tee : nil; +}"#) + + #-avr-nano + (SETFN "set" 2 126 #" +/* + (set symbol value [symbol value]*) + For each pair of arguments, assigns the value of the second argument to the value of the first argument. +*/ +object *fn_setfn (object *args, object *env) { + object *arg = nil; + while (args != NULL) { + if (cdr(args) == NULL) error2(oddargs); + object *pair = findvalue(first(args), env); + arg = second(args); + cdr(pair) = arg; + args = cddr(args); + } + return arg; +}"#) + + (STREAMP nil 1 1 #" +/* + (streamp item) + Returns t if its argument is a stream. +*/ +object *fn_streamp (object *args, object *env) { + (void) env; + object *arg = first(args); + return streamp(arg) ? tee : nil; +}"#) + + (EQ nil 2 2 " +/* + (eq item item) + Tests whether the two arguments are the same symbol, same character, equal numbers, + or point to the same cons, and returns t or nil as appropriate. +*/ +object *fn_eq (object *args, object *env) { + (void) env; + return eq(first(args), second(args)) ? tee : nil; +}") + + (EQUAL nil 2 2 " +/* + (equal item item) + Tests whether the two arguments are the same symbol, same character, equal numbers, + or point to the same cons, and returns t or nil as appropriate. +*/ +object *fn_equal (object *args, object *env) { + (void) env; + return equal(first(args), second(args)) ? tee : nil; +}"))) + + ("List functions" + + ((CAR nil 1 1 #" +/* + (car list) + Returns the first item in a list. +*/ +object *fn_car (object *args, object *env) { + (void) env; + return carx(first(args)); +}"#) + + (FIRST nil 1 1 (car)) + + (CDR nil 1 1 #" +/* + (cdr list) + Returns a list with the first item removed. +*/ +object *fn_cdr (object *args, object *env) { + (void) env; + return cdrx(first(args)); +}"#) + + (REST nil 1 1 (cdr)) + + (CAAR nil 1 1 #" +/* + (caar list) +*/ +object *fn_caar (object *args, object *env) { + (void) env; + return cxxxr(args, 0b100); +}"#) + + (CADR nil 1 1 #" +/* + (cadr list) +*/ +object *fn_cadr (object *args, object *env) { + (void) env; + return cxxxr(args, 0b101); +}"#) + + (SECOND nil 1 1 (cadr)) + + (CDAR nil 1 1 #" +/* + (cdar list) + Equivalent to (cdr (car list)). +*/ +object *fn_cdar (object *args, object *env) { + (void) env; + return cxxxr(args, 0b110); +}"#) + + (CDDR nil 1 1 #" +/* + (cddr list) + Equivalent to (cdr (cdr list)). +*/ +object *fn_cddr (object *args, object *env) { + (void) env; + return cxxxr(args, 0b111); +}"#) + + (CAAAR nil 1 1 #" +/* + (caaar list) + Equivalent to (car (car (car list))). +*/ +object *fn_caaar (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1000); +}"#) + + (CAADR nil 1 1 #" +/* + (caadr list) + Equivalent to (car (car (cdar list))). +*/ +object *fn_caadr (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1001);; +}"#) + + (CADAR nil 1 1 #" +/* + (cadar list) + Equivalent to (car (cdr (car list))). +*/ +object *fn_cadar (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1010); +}"#) + + (CADDR nil 1 1 #" +/* + (caddr list) + Equivalent to (car (cdr (cdr list))). +*/ +object *fn_caddr (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1011); +}"#) + + (THIRD nil 1 1 (caddr)) + + (CDAAR nil 1 1 #" +/* + (cdaar list) + Equivalent to (cdar (car (car list))). +*/ +object *fn_cdaar (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1100); +}"#) + + (CDADR nil 1 1 #" +/* + (cdadr list) + Equivalent to (cdr (car (cdr list))). +*/ +object *fn_cdadr (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1101); +}"#) + + (CDDAR nil 1 1 #" +/* + (cddar list) + Equivalent to (cdr (cdr (car list))). +*/ +object *fn_cddar (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1110); +}"#) + + (CDDDR nil 1 1 #" +/* + (cdddr list) + Equivalent to (cdr (cdr (cdr list))). +*/ +object *fn_cdddr (object *args, object *env) { + (void) env; + return cxxxr(args, 0b1111); +}"#) + + #-arrays + (LENGTH nil 1 1 #" +/* + (length item) + Returns the number of items in a list, or the length of a string. +*/ +object *fn_length (object *args, object *env) { + (void) env; + object *arg = first(args); + if (listp(arg)) return number(listlength(arg)); + if (!stringp(arg)) error(invalidarg, arg); + return number(stringlength(arg)); +}"#) + + #+arrays + (LENGTH nil 1 1 #" +/* + (length item) + Returns the number of items in a list, the length of a string, or the length of a one-dimensional array. +*/ +object *fn_length (object *args, object *env) { + (void) env; + object *arg = first(args); + if (listp(arg)) return number(listlength(arg)); + if (stringp(arg)) return number(stringlength(arg)); + if (!(arrayp(arg) && cdr(cddr(arg)) == NULL)) error(PSTR("argument is not a list, 1d array, or string"), arg); + return number(abs(first(cddr(arg))->integer)); +}"#) + + #+arrays + (ARRAYDIMENSIONS "array-dimensions" 1 1 #" +/* + (array-dimensions item) + Returns a list of the dimensions of an array. +*/ +object *fn_arraydimensions (object *args, object *env) { + (void) env; + object *array = first(args); + if (!arrayp(array)) error(PSTR("argument is not an array"), array); + object *dimensions = cddr(array); + return (first(dimensions)->integer < 0) ? cons(number(-(first(dimensions)->integer)), cdr(dimensions)) : dimensions; +}"#) + + (LIST nil 0 127 " +/* + (list item*) + Returns a list of the values of its arguments. +*/ +object *fn_list (object *args, object *env) { + (void) env; + return args; +}") + + #-avr-nano + (COPYLIST "copy-list" 1 1 " +/* + (copy-list list) + Returns a copy of a list. +*/ +object *fn_copylist (object *args, object *env) { + (void) env; + object *arg = first(args); + if (!listp(arg)) error(notalist, arg); + object *result = cons(NULL, NULL); + object *ptr = result; + while (arg != NULL) { + cdr(ptr) = cons(car(arg), NULL); + ptr = cdr(ptr); arg = cdr(arg); + } + return cdr(result); +}") + + #+arrays + (MAKEARRAY "make-array" 1 5 #" +/* + (make-array size [:initial-element element] [:element-type 'bit]) + If size is an integer it creates a one-dimensional array with elements from 0 to size-1. + If size is a list of n integers it creates an n-dimensional array with those dimensions. + If :element-type 'bit is specified the array is a bit array. +*/ +object *fn_makearray (object *args, object *env) { + (void) env; + object *def = nil; + bool bitp = false; + object *dims = first(args); + if (dims == NULL) error2(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(PSTR("argument not recognised"), var); + args = cddr(args); + } + if (bitp) { + if (def == nil) def = number(0); + else def = number(-checkbitvalue(def)); // 1 becomes all ones + } + return makearray(dims, def, bitp); +}"#) + + (REVERSE nil 1 1 #" +/* + (reverse list) + Returns a list with the elements of list in reverse order. +*/ +object *fn_reverse (object *args, object *env) { + (void) env; + object *list = first(args); + object *result = NULL; + while (list != NULL) { + if (improperp(list)) error(notproper, list); + push(first(list),result); + list = cdr(list); + } + return result; +}"#) + + (NTH nil 2 2 #" +/* + (nth number list) + Returns the nth item in list, counting from zero. +*/ +object *fn_nth (object *args, object *env) { + (void) env; + int n = checkinteger(first(args)); + if (n < 0) error(indexnegative, first(args)); + object *list = second(args); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + if (n == 0) return car(list); + list = cdr(list); + n--; + } + return nil; +}"#) + + #+arrays + (AREF nil 2 127 #" +/* + (aref array index [index*]) + Returns an element from the specified array. +*/ +object *fn_aref (object *args, object *env) { + (void) env; + int bit; + object *array = first(args); + if (!arrayp(array)) error(PSTR("first argument is not an array"), array); + object *loc = *getarray(array, cdr(args), 0, &bit); + if (bit == -1) return loc; + else return number((loc->integer)>>bit & 1); +}"#) + + #+avr-nano + (ASSOC nil 2 4 #" +/* + (assoc key list) + Looks up a key in an association list of (key . value) pairs, + and returns the matching pair, or nil if no pair is found. +*/ +object *fn_assoc (object *args, object *env) { + (void) env; + object *key = first(args); + object *list = second(args); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + object *pair = first(list); + if (!listp(pair)) error(PSTR("element is not a list"), pair); + if (pair != NULL && eq(key,car(pair))) return pair; + list = cdr(list); + } + return nil; +}"#) + + #-avr-nano + (ASSOC nil 2 4 #" +/* + (assoc key list [:test function]) + Looks up a key in an association list of (key . value) pairs, using eq or the specified test function, + and returns the matching pair, or nil if no pair is found. +*/ +object *fn_assoc (object *args, object *env) { + (void) env; + object *key = first(args); + object *list = second(args); + object *test = testargument(cddr(args)); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + object *pair = first(list); + if (!listp(pair)) error(PSTR("element is not a list"), pair); + if (pair != NULL && apply(test, cons(key, cons(car(pair), NULL)), env) != NULL) return pair; + list = cdr(list); + } + return nil; +}"#) + + #+avr-nano + (MEMBER nil 2 4 #" +/* + (member item list) + Searches for an item in a list, using eq, and returns the list starting from the first occurrence of the item, + or nil if it is not found. +*/ +object *fn_member (object *args, object *env) { + (void) env; + object *item = first(args); + object *list = second(args); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + if (eq(item,car(list))) return list; + list = cdr(list); + } + return nil; +}"#) + + #-avr-nano + (MEMBER nil 2 4 #" +/* + (member item list [:test function]) + Searches for an item in a list, using eq or the specified test function, and returns the list starting + from the first occurrence of the item, or nil if it is not found. +*/ +object *fn_member (object *args, object *env) { + (void) env; + object *item = first(args); + object *list = second(args); + object *test = testargument(cddr(args)); + while (list != NULL) { + if (improperp(list)) error(notproper, list); + if (apply(test, cons(item, cons(car(list), NULL)), env) != NULL) return list; + list = cdr(list); + } + return nil; +}"#) + + (APPLY nil 2 127 #" +/* + (apply function list) + Returns the result of evaluating function, with the list of arguments specified by the second parameter. +*/ +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(notalist, arg); + cdr(previous) = arg; + return apply(first(args), cdr(args), env); +}"#) + + (FUNCALL nil 1 127 " +/* + (funcall function argument*) + Evaluates function with the specified arguments. +*/ +object *fn_funcall (object *args, object *env) { + return apply(first(args), cdr(args), env); +}") + + (APPEND nil 0 127 #" +/* + (append list*) + Joins its arguments, which should be lists, into a single list. +*/ +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(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(notproper, first(args)); + } + args = cdr(args); + } + return head; +}"#) + + #+avr-nano + (MAPC nil 2 127 #" +/* + (mapc function list1 [list]*) + Applies the function to each element in one or more lists, ignoring the results. + It returns the first list argument. +*/ +object *fn_mapc (object *args, object *env) { + object *function = first(args); + args = cdr(args); + object *result = first(args); + push(result,GCStack); + 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); pop(GCStack); + return result; + } + if (improperp(list)) error(notproper, list); + object *obj = cons(first(list),NULL); + car(lists) = cdr(list); + cdr(tailp) = obj; tailp = obj; + lists = cdr(lists); + } + apply(function, cdr(params), env); + } +}"#) + + #-avr-nano + (MAPC nil 2 127 #" +/* + (mapc function list1 [list]*) + Applies the function to each element in one or more lists, ignoring the results. + It returns the first list argument. +*/ +object *fn_mapc (object *args, object *env) { + return mapcl(args, env, false); +}"#) + + #-avr-nano + (MAPL nil 2 127 #" +/* + (mapl function list1 [list]*) + Applies the function to one or more lists and then successive cdrs of those lists, + ignoring the results. It returns the first list argument. +*/ +object *fn_mapl (object *args, object *env) { + return mapcl(args, env, true); +}"#) + + #+avr-nano + (MAPCAR nil 2 127 #" +/* + (mapcar function list1 [list]*) + Applies the function to each element in one or more lists, and returns the resulting list. +*/ +object *fn_mapcar (object *args, object *env) { + return mapcarcan(args, env, mapcarfun); +}"#) + + #-avr-nano + (MAPCAR nil 2 127 #" +/* + (mapcar function list1 [list]*) + Applies the function to each element in one or more lists, and returns the resulting list. +*/ +object *fn_mapcar (object *args, object *env) { + return mapcarcan(args, env, mapcarfun, false); +}"#) + + #+avr-nano + (MAPCAN nil 2 127 #" +/* + (mapcan function list1 [list]*) + Applies the function to each element in one or more lists. The results should be lists, + and these are destructively concatenated together to give the value returned. +*/ +object *fn_mapcan (object *args, object *env) { + return mapcarcan(args, env, mapcanfun); +}"#) + + #-avr-nano + (MAPCAN nil 2 127 #" +/* + (mapcan function list1 [list]*) + Applies the function to each element in one or more lists. The results should be lists, + and these are destructively concatenated together to give the value returned. +*/ +object *fn_mapcan (object *args, object *env) { + return mapcarcan(args, env, mapcanfun, false); +}"#) + + #-avr-nano + (MAPLIST nil 2 127 #" +/* + (maplist function list1 [list]*) + Applies the function to one or more lists and then successive cdrs of those lists, + and returns the resulting list. +*/ +object *fn_maplist (object *args, object *env) { + return mapcarcan(args, env, mapcarfun, true); +}"#) + + #-avr-nano + (MAPCON nil 2 127 #" +/* + (mapcon function list1 [list]*) + Applies the function to one or more lists and then successive cdrs of those lists, + and these are destructively concatenated together to give the value returned. +*/ +object *fn_mapcon (object *args, object *env) { + return mapcarcan(args, env, mapcanfun, true); +}"#))) + + ("Arithmetic functions" + ( + + #-float + (ADD "+" 0 127 #" +/* + (+ number*) + Adds its arguments together. +*/ +object *fn_add (object *args, object *env) { + (void) env; + int result = 0; + while (args != NULL) { + int temp = checkinteger(car(args)); + #if defined(checkoverflow) + if (temp < 1) { if (INT_MIN - temp > result) error2(overflow); } + else { if (INT_MAX - temp < result) error2(overflow); } + #endif + result = result + temp; + args = cdr(args); + } + return number(result); +}"#) + + #+float + (ADD "+" 0 127 #" +/* + (+ number*) + Adds its arguments together. + If each argument is an integer, and the running total doesn't overflow, the result is an integer, + otherwise a floating-point number. +*/ +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(notanumber, arg); + args = cdr(args); + } + return number(result); +}"#) + + #-float + (SUBTRACT "-" 1 127 #" +/* + (- number*) + If there is one argument, negates the argument. + If there are two or more arguments, subtracts the second and subsequent arguments from the first argument. +*/ +object *fn_subtract (object *args, object *env) { + (void) env; + int result = checkinteger(car(args)); + args = cdr(args); + if (args == NULL) { + #if defined(checkoverflow) + if (result == INT_MIN) error2(overflow); + #endif + return number(-result); + } + while (args != NULL) { + int temp = checkinteger(car(args)); + #if defined(checkoverflow) + if (temp < 1) { if (INT_MAX + temp < result) error2(overflow); } + else { if (INT_MIN + temp > result) error2(overflow); } + #endif + result = result - temp; + args = cdr(args); + } + return number(result); +}"#) + + #+float + (SUBTRACT "-" 1 127 #" +/* + (- number*) + If there is one argument, negates the argument. + If there are two or more arguments, subtracts the second and subsequent arguments from the first argument. + If each argument is an integer, and the running total doesn't overflow, returns the result as an integer, + otherwise a floating-point number. +*/ +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(notanumber, arg); + args = cdr(args); + } + return number(result); + } else error(notanumber, arg); + return nil; +}"#) + + #-float + (MULTIPLY "*" 0 127 #" +/* + (* number*) + Multiplies its arguments together. +*/ +object *fn_multiply (object *args, object *env) { + (void) env; + int result = 1; + while (args != NULL){ + #if defined(checkoverflow) + signed long temp = (signed long) result * checkinteger(car(args)); + if ((temp > INT_MAX) || (temp < INT_MIN)) error2(overflow); + result = temp; + #else + result = result * checkinteger(car(args)); + #endif + args = cdr(args); + } + return number(result); +}"#) + + #+float + (MULTIPLY "*" 0 127 #" +/* + (* number*) + Multiplies its arguments together. + If each argument is an integer, and the running total doesn't overflow, the result is an integer, + otherwise it's a floating-point number. +*/ +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(notanumber, arg); + args = cdr(args); + } + return number(result); +}"#) + + #-float + (DIVIDE "/" 2 127 #" +/* + (/ number*) + Divides the first argument by the second and subsequent arguments. +*/ +object *fn_divide (object *args, object *env) { + (void) env; + int result = checkinteger(first(args)); + args = cdr(args); + while (args != NULL) { + int arg = checkinteger(car(args)); + if (arg == 0) error2(divisionbyzero); + #if defined(checkoverflow) + if ((result == INT_MIN) && (arg == -1)) error2(overflow); + #endif + result = result / arg; + args = cdr(args); + } + return number(result); +}"#) + + #-float + (TRUNCATE nil 1 2 (divide)) + + #+float + (DIVIDE "/" 1 127 #" +/* + (/ number*) + Divides the first argument by the second and subsequent arguments. + If each argument is an integer, and each division produces an exact result, the result is an integer; + otherwise it's a floating-point number. +*/ +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(divisionbyzero); + return makefloat(1.0 / f); + } else if (integerp(arg)) { + int i = arg->integer; + if (i == 0) error2(divisionbyzero); + else if (i == 1) return number(1); + else return makefloat(1.0 / i); + } else error(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(divisionbyzero); + 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(notanumber, arg); + } + return number(result); + } else error(notanumber, arg); + return nil; +}"#) + + #+avr-nano + (MOD nil 2 2 #" +/* + (mod number number) + Returns its first argument modulo the second argument. + If both arguments are integers the result is an integer; otherwise it's a floating-point number. +*/ +object *fn_mod (object *args, object *env) { + (void) env; + int arg1 = checkinteger(first(args)); + int arg2 = checkinteger(second(args)); + if (arg2 == 0) error2(divisionbyzero); + int r = arg1 % arg2; + if ((arg1<0) != (arg2<0)) r = r + arg2; + return number(r); +}"#) + + #-avr-nano + (MOD nil 2 2 #" +/* + (mod number number) + Returns its first argument modulo the second argument. + If both arguments are integers the result is an integer; otherwise it's a floating-point number. +*/ +object *fn_mod (object *args, object *env) { + (void) env; + return remmod(args, true); +}"#) + + #-avr-nano + (REM nil 2 2 #" +/* + (rem number number) + Returns the remainder from dividing the first argument by the second argument. + If both arguments are integers the result is an integer; otherwise it's a floating-point number. +*/ +object *fn_rem (object *args, object *env) { + (void) env; + return remmod(args, false); +}"#) + + #-float + (ONEPLUS "1+" 1 1 #" +/* + (1+ number) + Adds one to its argument and returns it. +*/ +object *fn_oneplus (object *args, object *env) { + (void) env; + int result = checkinteger(first(args)); + #if defined(checkoverflow) + if (result == INT_MAX) error2(overflow); + #endif + return number(result + 1); +}"#) + + #+float + (ONEPLUS "1+" 1 1 #" +/* + (1+ number) + Adds one to its argument and returns it. + If the argument is an integer the result is an integer if possible; + otherwise it's a floating-point number. +*/ +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(notanumber, arg); + return nil; +}"#) + + #-float + (ONEMINUS "1-" 1 1 #" +/* + (1- number) + Subtracts one from its argument and returns it. +*/ +object *fn_oneminus (object *args, object *env) { + (void) env; + int result = checkinteger(first(args)); + #if defined(checkoverflow) + if (result == INT_MIN) error2(overflow); + #endif + return number(result - 1); +}"#) + + #+float + (ONEMINUS "1-" 1 1 #" +/* + (1- number) + Subtracts one from its argument and returns it. + If the argument is an integer the result is an integer if possible; + otherwise it's a floating-point number. +*/ +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(notanumber, arg); + return nil; +}"#) + + #-float + (ABS nil 1 1 #" +/* + (abs number) + Returns the absolute, positive value of its argument. +*/ +object *fn_abs (object *args, object *env) { + (void) env; + int result = checkinteger(first(args)); + #if defined(checkoverflow) + if (result == INT_MIN) error2(overflow); + #endif + return number(abs(result)); +}"#) + + #+float + (ABS nil 1 1 #" +/* + (abs number) + Returns the absolute, positive value of its argument. + If the argument is an integer the result will be returned as an integer if possible, + otherwise a floating-point number. +*/ +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(notanumber, arg); + return nil; +}"#) + + #-float + (RANDOM nil 1 1 #" +/* + (random number) + Returns a random number between 0 and one less than its argument. +*/ +object *fn_random (object *args, object *env) { + (void) env; + int arg = checkinteger(first(args)); + return number(pseudoRandom(arg)); +}"#) + + #+float + (RANDOM nil 1 1 #" +/* + (random number) + If number is an integer returns a random number between 0 and one less than its argument. + Otherwise returns a floating-point number between zero and number. +*/ +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(notanumber, arg); + return nil; +}"#) + + #-float + (MAXFN "max" 1 127 #" +/* + (max number*) + Returns the maximum of one or more arguments. +*/ +object *fn_maxfn (object *args, object *env) { + (void) env; + int result = checkinteger(first(args)); + args = cdr(args); + while (args != NULL) { + int next = checkinteger(car(args)); + if (next > result) result = next; + args = cdr(args); + } + return number(result); +}"#) + + #+float + (MAXFN "max" 1 127 #" +/* + (max number*) + Returns the maximum of one or more arguments. +*/ +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(arg) > checkintfloat(result))) result = arg; + args = cdr(args); + } + return result; +}"#) + + #-float + (MINFN "min" 1 127 #" +/* + (min number*) + Returns the minimum of one or more arguments. +*/ +object *fn_minfn (object *args, object *env) { + (void) env; + int result = checkinteger(first(args)); + args = cdr(args); + while (args != NULL) { + int next = checkinteger(car(args)); + if (next < result) result = next; + args = cdr(args); + } + return number(result); +}"#) + + #+float + (MINFN "min" 1 127 #" +/* + (min number*) + Returns the minimum of one or more arguments. +*/ +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(arg) < checkintfloat(result))) result = arg; + args = cdr(args); + } + return result; +}"#))) + + ("Arithmetic comparisons" + + ( + + #-float + (NOTEQ "/=" 1 127 #" +/* + (/= number*) + Returns t if none of the arguments are equal, or nil if two or more arguments are equal. +*/ +object *fn_noteq (object *args, object *env) { + (void) env; + while (args != NULL) { + object *nargs = args; + int arg1 = checkinteger(first(nargs)); + nargs = cdr(nargs); + while (nargs != NULL) { + int arg2 = checkinteger(first(nargs)); + if (arg1 == arg2) return nil; + nargs = cdr(nargs); + } + args = cdr(args); + } + return tee; +}"#) + + #+float + (NOTEQ "/=" 1 127 #" +/* + (/= number*) + Returns t if none of the arguments are equal, or nil if two or more arguments are equal. +*/ +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(arg1) == checkintfloat(arg2))) return nil; + nargs = cdr(nargs); + } + args = cdr(args); + } + return tee; +}"#) + + (NUMEQ "=" 1 127 #" +/* + (= number*) + Returns t if all the arguments, which must be numbers, are numerically equal, and nil otherwise. +*/ +object *fn_numeq (object *args, object *env) { + (void) env; + return compare(args, false, false, true); +}"#) + + (LESS "<" 1 127 #" +/* + (< number*) + Returns t if each argument is less than the next argument, and nil otherwise. +*/ +object *fn_less (object *args, object *env) { + (void) env; + return compare(args, true, false, false); +}"#) + + (LESSEQ "<=" 1 127 #" +/* + (<= number*) + Returns t if each argument is less than or equal to the next argument, and nil otherwise. +*/ +object *fn_lesseq (object *args, object *env) { + (void) env; + return compare(args, true, false, true); +}"#) + + (GREATER ">" 1 127 #" +/* + (> number*) + Returns t if each argument is greater than the next argument, and nil otherwise. +*/ +object *fn_greater (object *args, object *env) { + (void) env; + return compare(args, false, true, false); +}"#) + + (GREATEREQ ">=" 1 127 #" +/* + (>= number*) + Returns t if each argument is greater than or equal to the next argument, and nil otherwise. +*/ +object *fn_greatereq (object *args, object *env) { + (void) env; + return compare(args, false, true, true); +}"#) + + #-float + (PLUSP nil 1 1 " +/* + (plusp number) + Returns t if the argument is greater than zero, or nil otherwise. +*/ +object *fn_plusp (object *args, object *env) { + (void) env; + int arg = checkinteger(first(args)); + if (arg > 0) return tee; + else return nil; +}") + + #+float + (PLUSP nil 1 1 " +/* + (plusp number) + Returns t if the argument is greater than zero, or nil otherwise. +*/ +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(notanumber, arg); + return nil; +}") + + #-float + (MINUSP nil 1 1 " +object *fn_minusp (object *args, object *env) { + (void) env; + int arg = checkinteger(first(args)); + if (arg < 0) return tee; + else return nil; +}") + + #+float + (MINUSP nil 1 1 " +/* + (minusp number) + Returns t if the argument is less than zero, or nil otherwise. +*/ +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(notanumber, arg); + return nil; +}") + + #-float + (ZEROP nil 1 1 " +/* + (zerop number) + Returns t if the argument is zero. +*/ +object *fn_zerop (object *args, object *env) { + (void) env; + int arg = checkinteger(first(args)); + return (arg == 0) ? tee : nil; +}") + + #+float + (ZEROP nil 1 1 " +/* + (zerop number) + Returns t if the argument is zero. +*/ +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(notanumber, arg); + return nil; +}") + + (ODDP nil 1 1 " +/* + (oddp number) + Returns t if the integer argument is odd. +*/ +object *fn_oddp (object *args, object *env) { + (void) env; + int arg = checkinteger(first(args)); + return ((arg & 1) == 1) ? tee : nil; +}") + + (EVENP nil 1 1 " +/* + (evenp number) + Returns t if the integer argument is even. +*/ +object *fn_evenp (object *args, object *env) { + (void) env; + int arg = checkinteger(first(args)); + return ((arg & 1) == 0) ? tee : nil; +}"))) + + ("Number functions" + + ((INTEGERP nil 1 1 #" +/* + (integerp number) + Returns t if the argument is an integer. +*/ +object *fn_integerp (object *args, object *env) { + (void) env; + return integerp(first(args)) ? tee : nil; +}"#) + + #-float + (NUMBERP nil 1 1 (integerp)) + + #+float + (NUMBERP nil 1 1 #" +/* + (numberp number) + Returns t if the argument is a number. +*/ +object *fn_numberp (object *args, object *env) { + (void) env; + object *arg = first(args); + return (integerp(arg) || floatp(arg)) ? tee : nil; +}"#))) + + #+float + ("Floating-point functions" + ((FLOATFN "float" 1 1 #" +/* + (float number) + Returns its argument converted to a floating-point number. +*/ +object *fn_floatfn (object *args, object *env) { + (void) env; + object *arg = first(args); + return (floatp(arg)) ? arg : makefloat((float)(arg->integer)); +}"#) + + (FLOATP nil 1 1 #" +/* + (floatp number) + Returns t if the argument is a floating-point number. +*/ +object *fn_floatp (object *args, object *env) { + (void) env; + return floatp(first(args)) ? tee : nil; +}"#) + + (SIN nil 1 1 float-function) + (COS nil 1 1 float-function) + (TAN nil 1 1 float-function) + (ASIN nil 1 1 float-function) + (ACOS nil 1 1 float-function) + (ATAN nil 1 2 #" +/* + (atan number1 [number2]) + Returns the arc tangent of number1/number2, in radians. If number2 is omitted it defaults to 1. +*/ +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(first(args)); + return makefloat(atan2(checkintfloat(arg), div)); +}"#) + + (SINH nil 1 1 float-function) + (COSH nil 1 1 float-function) + (TANH nil 1 1 float-function) + (EXP nil 1 1 float-function) + (SQRT nil 1 1 float-function) + + (LOG nil 1 2 #" +/* + (log number [base]) + Returns the logarithm of number to the specified base. If base is omitted it defaults to e. +*/ +object *fn_log (object *args, object *env) { + (void) env; + object *arg = first(args); + float fresult = log(checkintfloat(arg)); + args = cdr(args); + if (args == NULL) return makefloat(fresult); + else return makefloat(fresult / log(checkintfloat(first(args)))); +}"#) + + (EXPT nil 2 2 #" +/* + (expt number power) + Returns number raised to the specified power. + Returns the result as an integer if the arguments are integers and the result will be within range, + otherwise a floating-point number. +*/ +object *fn_expt (object *args, object *env) { + (void) env; + object *arg1 = first(args); object *arg2 = second(args); + float float1 = checkintfloat(arg1); + float value = log(abs(float1)) * checkintfloat(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(PSTR("invalid result")); + } + return makefloat(exp(value)); +}"#) + + (CEILING nil 1 2 truncate-function) + (FLOOR nil 1 2 truncate-function) + + (TRUNCATE nil 1 2 #" +/* + (truncate number [divisor]) + Returns the integer part of number/divisor. If divisor is omitted it defaults to 1. +*/ +object *fn_truncate (object *args, object *env) { + (void) env; + object *arg = first(args); + args = cdr(args); + if (args != NULL) return number((int)(checkintfloat(arg) / checkintfloat(first(args)))); + else return number((int)(checkintfloat(arg))); +}"#) + + (ROUND nil 1 2 #" +/* + (round number [divisor]) + Returns the integer closest to number/divisor. If divisor is omitted it defaults to 1. +*/ +object *fn_round (object *args, object *env) { + (void) env; + object *arg = first(args); + args = cdr(args); + if (args != NULL) return number(round(checkintfloat(arg) / checkintfloat(first(args)))); + else return number(round(checkintfloat(arg))); +}"#))) + + ("Characters" + + ((CHAR "char" 2 2 #" +/* + (char string n) + Returns the nth character in a string, counting from zero. +*/ +object *fn_char (object *args, object *env) { + (void) env; + object *arg = first(args); + if (!stringp(arg)) error(notastring, arg); + object *n = second(args); + char c = nthchar(arg, checkinteger(n)); + if (c == 0) error(indexrange, n); + return character(c); +}"#) + + (CHARCODE "char-code" 1 1 #" +/* + (char-code character) + Returns the ASCII code for a character, as an integer. +*/ +object *fn_charcode (object *args, object *env) { + (void) env; + return number(checkchar(first(args))); +}"#) + + (CODECHAR "code-char" 1 1 #" +/* + (code-char integer) + Returns the character for the specified ASCII code. +*/ +object *fn_codechar (object *args, object *env) { + (void) env; + return character(checkinteger(first(args))); +}"#) + + (CHARACTERP nil 1 1 #" +/* + (characterp item) + Returns t if the argument is a character and nil otherwise. +*/ +object *fn_characterp (object *args, object *env) { + (void) env; + return characterp(first(args)) ? tee : nil; +}"#))) + + ("Strings" + + ((STRINGP nil 1 1 " +/* + (stringp item) + Returns t if the argument is a string and nil otherwise. +*/ +object *fn_stringp (object *args, object *env) { + (void) env; + return stringp(first(args)) ? tee : nil; +}") + + #+avr-nano + (STRINGEQ "string=" 2 2 #" +/* + (string= string string) + Returns t if the two strings are the same, or nil otherwise. +*/ +object *fn_stringeq (object *args, object *env) { + (void) env; + return stringcompare(args, false, false, true) ? tee : nil; +}"#) + + #-avr-nano + (STRINGEQ "string=" 2 2 #" +/* + (string= string string) + Returns t if the two strings are the same, or nil otherwise. +*/ +object *fn_stringeq (object *args, object *env) { + (void) env; + int m = stringcompare(args, false, false, true); + return m == -1 ? nil : tee; +}"#) + + #+avr-nano + (STRINGLESS "string<" 2 2 #" +/* + (string< string string) + Returns t if the first string is alphabetically less than the second string, + or nil otherwise. +*/ +object *fn_stringless (object *args, object *env) { + (void) env; + return stringcompare(args, true, false, false) ? tee : nil; +}"#) + + #-avr-nano + (STRINGLESS "string<" 2 2 #" +/* + (string< string string) + Returns the index to the first mismatch if the first string is alphabetically less than the second string, + or nil otherwise. +*/ +object *fn_stringless (object *args, object *env) { + (void) env; + int m = stringcompare(args, true, false, false); + return m == -1 ? nil : number(m); +}"#) + + #+avr-nano + (STRINGGREATER "string>" 2 2 #" +/* + (string> string string) + Returns t if the first string is alphabetically greater than the second string, + or nil otherwise. +*/ +object *fn_stringgreater (object *args, object *env) { + (void) env; + return stringcompare(args, false, true, false) ? tee : nil; +}"#) + + #-avr-nano + (STRINGGREATER "string>" 2 2 #" +/* + (string> string string) + Returns the index to the first mismatch if the first string is alphabetically greater than the second string, + or nil otherwise. +*/ +object *fn_stringgreater (object *args, object *env) { + (void) env; + int m = stringcompare(args, false, true, false); + return m == -1 ? nil : number(m); +}"#) + + #-avr-nano + (STRINGNOTEQ "string/=" 2 2 #" +/* + (string/= string string) + Returns the index to the first mismatch if the two strings are not the same, or nil otherwise. +*/ +object *fn_stringnoteq (object *args, object *env) { + (void) env; + int m = stringcompare(args, true, true, false); + return m == -1 ? nil : number(m); +}"#) + + #-avr-nano + (STRINGLESSEQ "string<=" 2 2 #" +/* + (string<= string string) + Returns the index to the first mismatch if the first string is alphabetically less than or equal to + the second string, or nil otherwise. +*/ +object *fn_stringlesseq (object *args, object *env) { + (void) env; + int m = stringcompare(args, true, false, true); + return m == -1 ? nil : number(m); +}"#) + + #-avr-nano + (STRINGGREATEREQ "string>=" 2 2 #" +/* + (string>= string string) + Returns the index to the first mismatch if the first string is alphabetically greater than or equal to + the second string, or nil otherwise. +*/ +object *fn_stringgreatereq (object *args, object *env) { + (void) env; + int m = stringcompare(args, false, true, true); + return m == -1 ? nil : number(m); +}"#) + + (SORT "sort" 2 2 #" +/* + (sort list test) + Destructively sorts list according to the test function, using an insertion sort, and returns the sorted list. +*/ +object *fn_sort (object *args, object *env) { + if (first(args) == NULL) return nil; + object *list = cons(nil,first(args)); + protect(list); + object *predicate = second(args); + object *compare = cons(NULL, cons(NULL, NULL)); + protect(compare); + 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(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); + } + unprotect(); unprotect(); + return cdr(list); +}"#) + + (STRINGFN "string" 1 1 #" +/* + (string item) + Converts its argument to a string. +*/ +object *fn_stringfn (object *args, object *env) { + return fn_princtostring(args, env); +}"#) + + (CONCATENATE nil 1 127 #" +/* + (concatenate 'string string*) + Joins together the strings given in the second and subsequent arguments, and returns a single string. +*/ +object *fn_concatenate (object *args, object *env) { + (void) env; + object *arg = first(args); + if (builtin(arg->name) != STRINGFN) error2(PSTR("only supports strings")); + args = cdr(args); + object *result = newstring(); + object *tail = result; + while (args != NULL) { + object *obj = checkstring(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; +}"#) + + (SUBSEQ nil 2 3 #" +/* + (subseq seq start [end]) + Returns a subsequence of a list or string from item start to item end-1. +*/ +object *fn_subseq (object *args, object *env) { + (void) env; + object *arg = first(args); + int start = checkinteger(second(args)), end; + if (start < 0) error(indexnegative, second(args)); + args = cddr(args); + if (listp(arg)) { + int length = listlength(arg); + if (args != NULL) end = checkinteger(car(args)); else end = length; + if (start > end || end > length) error2(indexrange); + object *result = cons(NULL, NULL); + object *ptr = result; + for (int x = 0; x < end; x++) { + if (x >= start) { cdr(ptr) = cons(car(arg), NULL); ptr = cdr(ptr); } + arg = cdr(arg); + } + return cdr(result); + } else if (stringp(arg)) { + int length = stringlength(arg); + if (args != NULL) end = checkinteger(car(args)); else end = length; + if (start > end || end > length) error2(indexrange); + object *result = newstring(); + object *tail = result; + for (int i=start; i= 0) return number(value << count); + else return number(value >> abs(count)); +}") + + (LOGBITP nil 2 2 " +/* + (logbitp bit value) + Returns t if bit number bit in value is a '1', and nil if it is a '0'. +*/ +object *fn_logbitp (object *args, object *env) { + (void) env; + int index = checkinteger(first(args)); + int value = checkinteger(second(args)); + return (bitRead(value, index) == 1) ? tee : nil; +}"))) + + ("System functions" + ((EVAL nil 1 1 " +/* + (eval form*) + Evaluates its argument an extra time. +*/ +object *fn_eval (object *args, object *env) { + return eval(first(args), env); +}") + + (RETURN nil 0 1 " +/* + (return [value]) + Exits from a (dotimes ...), (dolist ...), or (loop ...) loop construct and returns value. +*/ +object *fn_return (object *args, object *env) { + (void) env; + setflag(RETURNFLAG); + if (args == NULL) return nil; else return first(args); +}") + + (GLOBALS nil 0 0 " +/* + (globals) + Returns a list of global variables. +*/ +object *fn_globals (object *args, object *env) { + (void) args, (void) env; + object *result = cons(NULL, NULL); + object *ptr = result; + object *arg = GlobalEnv; + while (arg != NULL) { + cdr(ptr) = cons(car(car(arg)), NULL); ptr = cdr(ptr); + arg = cdr(arg); + } + return cdr(result); +}") + + (LOCALS nil 0 0 " +/* + (locals) + Returns an association list of local variables and their values. +*/ +object *fn_locals (object *args, object *env) { + (void) args; + return env; +}") + + (MAKUNBOUND nil 1 1 #" +/* + (makunbound symbol) + Removes the value of the symbol from GlobalEnv and returns the symbol. +*/ +object *fn_makunbound (object *args, object *env) { + (void) env; + object *var = first(args); + if (!symbolp(var)) error(notasymbol, var); + delassoc(var, &GlobalEnv); + return var; +}"#) + + (BREAK nil 0 0 #" +/* + (break) + Inserts a breakpoint in the program. When evaluated prints Break! and reenters the REPL. +*/ +object *fn_break (object *args, object *env) { + (void) args; + pfstring(PSTR("\nBreak!\n"), pserial); + BreakLevel++; + repl(env); + BreakLevel--; + return nil; +}"#) + + (READ nil 0 1 " +/* + (read [stream]) + Reads an atom or list from the serial input and returns it. + If stream is specified the item is read from the specified stream. +*/ +object *fn_read (object *args, object *env) { + (void) env; + gfun_t gfun = gstreamfun(args); + return read(gfun); +}") + + (PRIN1 nil 1 2 " +/* + (prin1 item [stream]) + Prints its argument, and returns its value. + Strings are printed with quotation marks and escape characters. +*/ +object *fn_prin1 (object *args, object *env) { + (void) env; + object *obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + printobject(obj, pfun); + return obj; +}") + + (PRINT nil 1 2 " +/* + (print item [stream]) + Prints its argument with quotation marks and escape characters, on a new line, and followed by a space. + If stream is specified the argument is printed to the specified stream. +*/ +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; +}") + + (PRINC nil 1 2 " +/* + (princ item [stream]) + Prints its argument, and returns its value. + Characters and strings are printed without quotation marks or escape characters. +*/ +object *fn_princ (object *args, object *env) { + (void) env; + object *obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + prin1object(obj, pfun); + return obj; +}") + + (TERPRI nil 0 1 " +/* + (terpri [stream]) + Prints a new line, and returns nil. + If stream is specified the new line is written to the specified stream. +*/ +object *fn_terpri (object *args, object *env) { + (void) env; + pfun_t pfun = pstreamfun(args); + pln(pfun); + return nil; +}") + + (READBYTE "read-byte" 0 2 #" +/* + (read-byte stream) + Reads a byte from a stream and returns it. +*/ +object *fn_readbyte (object *args, object *env) { + (void) env; + gfun_t gfun = gstreamfun(args); + int c = gfun(); + return (c == -1) ? nil : number(c); +}"#) + + (READLINE "read-line" 0 1 #" +/* + (read-line [stream]) + Reads characters from the serial input up to a newline character, and returns them as a string, excluding the newline. + If stream is specified the line is read from the specified stream. +*/ +object *fn_readline (object *args, object *env) { + (void) env; + gfun_t gfun = gstreamfun(args); + return readstring('\n', false, gfun); +}"#) + + (WRITEBYTE "write-byte" 1 2 #" +/* + (write-byte number [stream]) + Writes a byte to a stream. +*/ +object *fn_writebyte (object *args, object *env) { + (void) env; + int value = checkinteger(first(args)); + pfun_t pfun = pstreamfun(cdr(args)); + (pfun)(value); + return nil; +}"#) + + (WRITESTRING "write-string" 1 2 #" +/* + (write-string string [stream]) + Writes a string. If stream is specified the string is written to the stream. +*/ +object *fn_writestring (object *args, object *env) { + (void) env; + object *obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printstring(obj, pfun); + Flags = temp; + return nil; +}"#) + + (WRITELINE "write-line" 1 2 #" +/* + (write-line string [stream]) + Writes a string terminated by a newline character. If stream is specified the string is written to the stream. +*/ +object *fn_writeline (object *args, object *env) { + (void) env; + object *obj = first(args); + pfun_t pfun = pstreamfun(cdr(args)); + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printstring(obj, pfun); + pln(pfun); + Flags = temp; + return nil; +}"#) + + #+(or arm esp) + (RESTARTI2C "restart-i2c" 1 2 #" +/* + (restart-i2c stream [read-p]) + Restarts an i2c-stream. + If read-p is nil or omitted the stream is written to. + If read-p is an integer it specifies the number of bytes to be read from the stream. +*/ +object *fn_restarti2c (object *args, object *env) { + (void) env; + int stream = isstream(first(args)); + 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(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; +}"#) + + #-(or arm esp) + (RESTARTI2C "restart-i2c" 1 2 #" +/* + (restart-i2c stream [read-p]) + Restarts an i2c-stream. + If read-p is nil or omitted the stream is written to. + If read-p is an integer it specifies the number of bytes to be read from the stream. +*/ +object *fn_restarti2c (object *args, object *env) { + (void) env; + int stream = isstream(first(args)); + 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(PSTR("not an i2c stream")); + return I2Crestart(address, read) ? tee : nil; +}"#) + + #+(or avr avr-nano) + (GC nil 0 1 #" +/* + (gc [print time]) + Forces a garbage collection and prints the number of objects collected, and the time taken. +*/ +object *fn_gc (object *args, object *env) { + if (args == NULL || first(args) != NULL) { + int initial = Freespace; + unsigned long start = micros(); + gc(args, 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); + } else gc(args, env); + return nil; +}"#) + + #-(or avr avr-nano) + (GC nil 0 1 #" +/* + (gc [print time]) + Forces a garbage collection and prints the number of objects collected, and the time taken. +*/ +object *fn_gc (object *args, object *env) { + if (args == NULL || first(args) != NULL) { + int initial = Freespace; + unsigned long start = micros(); + gc(args, env); + unsigned long elapsed = micros() - start; + pfstring("Space: ", pserial); + pint(Freespace - initial, pserial); + pfstring(" bytes, Time: ", pserial); + pint(elapsed, pserial); + pfstring(" us\n", pserial); + } else gc(args, env); + return nil; +}"#) + + (ROOM nil 0 0 #" +/* + (room) + Returns the number of free Lisp cells remaining. +*/ +object *fn_room (object *args, object *env) { + (void) args, (void) env; + return number(Freespace); +}"#) + + #-avr-nano + (BACKTRACE nil 0 1 #" +/* + (backtrace [on]) + Sets the state of backtrace according to the boolean flag 'on', + or with no argument displays the current state of backtrace. +*/ +object *fn_backtrace (object *args, object *env) { + (void) env; + if (args == NULL) return (tstflag(BACKTRACE)) ? tee : nil; + if (first(args) == NULL) clrflag(BACKTRACE); else setflag(BACKTRACE); + return first(args); +}"#) + + (SAVEIMAGE "save-image" 0 1 " +/* + (save-image [symbol]) + Saves the current uLisp image to non-volatile memory or SD card so it can be loaded using load-image. +*/ +object *fn_saveimage (object *args, object *env) { + if (args != NULL) args = eval(first(args), env); + return number(saveimage(args)); +}") + + (LOADIMAGE "load-image" 0 1 " +/* + (load-image [filename]) + Loads a saved uLisp image from non-volatile memory or SD card. +*/ +object *fn_loadimage (object *args, object *env) { + (void) env; + if (args != NULL) args = first(args); + return number(loadimage(args)); +}") + + #+ignore + (DUMPIMAGE "dump-image" 0 0 #" +object *fn_dumpimage(object *args, object *env) { + (void) args, (void) env; + int imagesize = workspacesize; // compactimage(NULL); + char tmp[16]; + Serial.println(); + sprintf(tmp, "freelist: %04x, ", (int)freelist); + Serial.print(tmp); + sprintf(tmp, "GlobalEnv: %04x, ", (int)GlobalEnv); + Serial.print(tmp); + sprintf(tmp, "GCStack: %04x, ", (int)GCStack); + Serial.print(tmp); + + for (int i=0; iinteger; + 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; +}") + + #+stm32 + (PINMODE nil 2 2 " +/* + (pinmode pin mode) + Sets the input/output mode of an Arduino pin number, and returns nil. + The mode parameter can be an integer, a keyword, or t or nil. +*/ +object *fn_pinmode (object *args, object *env) { + (void) env; + int pin = checkinteger(first(args)); + int pm = INPUT; + object *mode = second(args); + if (integerp(mode)) { + int nmode = checkinteger(mode); + if (nmode == 1) pm = OUTPUT; else if (nmode == 2) pm = INPUT_PULLUP; + #if defined(INPUT_PULLDOWN) + else if (nmode == 4) pm = INPUT_PULLDOWN; + #endif + } else if (mode != nil) pm = OUTPUT; + pinMode(pin, (WiringPinMode)pm); + return nil; +}") + + (DIGITALREAD nil 1 1 " +/* + (digitalread pin) + Reads the state of the specified Arduino pin number and returns t (high) or nil (low). +*/ +object *fn_digitalread (object *args, object *env) { + (void) env; + int pin; + object *arg = first(args); + if (keywordp(arg)) pin = checkkeyword(arg); + else pin = checkinteger(arg); + if (digitalRead(pin) != 0) return tee; else return nil; +}") + + (DIGITALWRITE nil 2 2 " +/* + (digitalwrite pin state) + Sets the state of the specified Arduino pin number. +*/ +object *fn_digitalwrite (object *args, object *env) { + (void) env; + int pin; + object *arg = first(args); + if (keywordp(arg)) pin = checkkeyword(arg); + else pin = checkinteger(arg); + arg = second(args); + int mode; + if (keywordp(arg)) mode = checkkeyword(arg); + else if (integerp(arg)) mode = arg->integer ? HIGH : LOW; + else mode = (arg != nil) ? HIGH : LOW; + digitalWrite(pin, mode); + return arg; +}") + + (ANALOGREAD nil 1 1 #" +/* + (analogread pin) + Reads the specified Arduino analogue pin number and returns the value. +*/ +object *fn_analogread (object *args, object *env) { + (void) env; + int pin; + object *arg = first(args); + if (keywordp(arg)) pin = checkkeyword(arg); + else { + pin = checkinteger(arg); + checkanalogread(pin); + } + return number(analogRead(pin)); +}"#) + + #+(or avr avr-nano) + (ANALOGREFERENCE nil 1 1 #" +/* + (analogreference keyword) + Specifies a keyword to set the analogue reference voltage used for analogue input. +*/ +object *fn_analogreference (object *args, object *env) { + (void) env; + object *arg = first(args); + analogReference(checkkeyword(arg)); + return arg; +}"#) + + #+arm + (ANALOGREFERENCE nil 1 1 #" +/* + (analogreference keyword) + Specifies a keyword to set the analogue reference voltage used for analogue input. +*/ +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_RASPBERRY_PI_PICO_W) \ + || defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_PIMORONI_PICO_PLUS_2) \ + || defined(ARDUINO_PIMORONI_TINY2350) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) \ + || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_NANO_MATTER) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) + error2("not supported"); + #else + analogReference((eAnalogReference)checkkeyword(arg)); + #endif + return arg; +}"#) + + #+(or avr avr-nano) + (ANALOGREADRESOLUTION nil 1 1 #" +/* + (analogreadresolution bits) + Specifies the resolution for the analogue inputs on platforms that support it. + The default resolution on all platforms is 10 bits. +*/ +object *fn_analogreadresolution (object *args, object *env) { + (void) env; + object *arg = first(args); + #if defined(CPU_AVR128DX48) + uint8_t res = checkinteger(arg); + if (res == 10) analogReadResolution(10); + else if (res == 12) analogReadResolution(12); + else error(PSTR("invalid resolution"), arg); + #else + error2(PSTR("not supported")); + #endif + return arg; +}"#) + + #+arm + (ANALOGREADRESOLUTION nil 1 1 #" +/* + (analogreadresolution bits) + Specifies the resolution for the analogue inputs on platforms that support it. + The default resolution on all platforms is 10 bits. +*/ +object *fn_analogreadresolution (object *args, object *env) { + (void) env; + object *arg = first(args); + #if defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) \ + || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) + error2("not supported"); + #else + analogReadResolution(checkinteger(arg)); + #endif + return arg; +}"#) + + #+esp + (ANALOGREADRESOLUTION nil 1 1 #" +/* + (analogreadresolution bits) + Specifies the resolution for the analogue inputs on platforms that support it. + The default resolution on all platforms is 10 bits. +*/ +object *fn_analogreadresolution (object *args, object *env) { + (void) env; + object *arg = first(args); + #if defined(ESP32) + analogReadResolution(checkinteger(arg)); + #else + error2(PSTR("not supported")); + #endif + return arg; +}"#) + + #+riscv + (ANALOGREADRESOLUTION nil 1 1 #" +/* + (analogreadresolution bits) + Specifies the resolution for the analogue inputs on platforms that support it. + The default resolution on all platforms is 10 bits. +*/ +object *fn_analogreadresolution (object *args, object *env) { + (void) env; + object *arg = first(args); + analogReadResolution(checkinteger(arg)); + return arg; +}"#) + + (ANALOGWRITE nil 2 2 #" +/* + (analogwrite pin value) + Writes the value to the specified Arduino pin number. +*/ +object *fn_analogwrite (object *args, object *env) { + (void) env; + int pin; + object *arg = first(args); + if (keywordp(arg)) pin = checkkeyword(arg); + else pin = checkinteger(arg); + checkanalogwrite(pin); + object *value = second(args); + analogWrite(pin, checkinteger(value)); + return value; +}"#) + + #+(or arm riscv) + (ANALOGWRITERESOLUTION nil 1 1 #" +/* + (analogwrite pin value) + Sets the analogue write resolution. +*/ +object *fn_analogwriteresolution (object *args, object *env) { + (void) env; + object *arg = first(args); + analogWriteResolution(checkinteger(arg)); + return arg; +}"#) + + #+(or avr avr-nano) + (DACREFERENCE nil 1 1 #" +/* + (dacreference value) + Sets the DAC voltage reference. AVR128DX48 only. +*/ +object *fn_dacreference (object *args, object *env) { + (void) env; + object *arg = first(args); + #if defined(CPU_AVR128DX48) + int ref = checkinteger(arg); + DACReference(ref); + #endif + return arg; +}"#) + + (DELAY nil 1 1 " +/* + (delay number) + Delays for a specified number of milliseconds. +*/ +object *fn_delay (object *args, object *env) { + (void) env; + object *arg1 = first(args); + unsigned long start = millis(); + unsigned long total = checkinteger(arg1); + do testescape(); + while (millis() - start < total); + return arg1; +}") + + (MILLIS nil 0 0 #" +/* + (millis) + Returns the time in milliseconds that uLisp has been running. +*/ +object *fn_millis (object *args, object *env) { + (void) args, (void) env; + return number(millis()); +}"#) + + #+(or avr avr-nano) + (SLEEP nil 0 1 #" +/* + (sleep secs) + Puts the processor into a low-power sleep mode for secs. + Only supported on some platforms. On other platforms it does delay(1000*secs). +*/ +object *fn_sleep (object *args, object *env) { + (void) env; + if (args == NULL || first(args) == NULL) { sleep(); return nil; } + object *arg1 = first(args); + doze(checkinteger(arg1)); + return arg1; +}"#) + + #-(or avr avr-nano) + (SLEEP nil 0 1 #" +/* + (sleep secs) + Puts the processor into a low-power sleep mode for secs. + Only supported on some platforms. On other platforms it does delay(1000*secs). +*/ +object *fn_sleep (object *args, object *env) { + (void) env; + object *arg1 = first(args); + doze(checkinteger(arg1)); + return arg1; +}"#) + + #+ignore + (SHIFTOUT nil 4 4 " +object *fn_shiftout (object *args, object *env) { + (void) env; + int datapin = integer(first(args)); + int clockpin = integer(second(args)); + int order = (third(args) != nil); + object *value = fourth(args); + shiftOut(datapin, clockpin, order, integer(value)); + return value; +}") + + #+ignore + (SHIFTIN nil 3 3 " +object *fn_shiftin (object *args, object *env) { + (void) env; + int datapin = integer(first(args)); + int clockpin = integer(second(args)); + int order = (third(args) != nil); + int value = shiftIn(datapin, clockpin, order); + return number(value); +}") + + (NOTE nil 0 3 #" +/* + (note [pin] [note] [octave]) + Generates a square wave on pin. + note represents the note in the well-tempered scale. + The argument octave can specify an octave; default 0. +*/ +object *fn_note (object *args, object *env) { + (void) env; + static int pin = 255; + if (args != NULL) { + pin = checkinteger(first(args)); + int note = 48, octave = 0; + if (cdr(args) != NULL) { + note = checkinteger(second(args)); + if (cddr(args) != NULL) octave = checkinteger(third(args)); + } + playnote(pin, note, octave); + } else nonote(pin); + return nil; +}"#) + + #+(or avr avr-nano) + (REGISTER nil 1 2 #" +/* + (register address [value]) + Reads or writes the value of a peripheral register. + If value is not specified the function returns the value of the register at address. + If value is specified the value is written to the register at address and the function returns value. +*/ +object *fn_register (object *args, object *env) { + (void) env; + object *arg = first(args); + int addr; + if (keywordp(arg)) addr = checkkeyword(arg); + else addr = checkinteger(first(args)); + if (cdr(args) == NULL) return number(*(volatile uint8_t *)addr); + (*(volatile uint8_t *)addr) = checkinteger(second(args)); + return second(args); +}"#) + + #+(or arm esp riscv) + (REGISTER nil 1 2 #" +/* + (register address [value]) + Reads or writes the value of a peripheral register. + If value is not specified the function returns the value of the register at address. + If value is specified the value is written to the register at address and the function returns value. +*/ +object *fn_register (object *args, object *env) { + (void) env; + object *arg = first(args); + int addr; + if (keywordp(arg)) addr = checkkeyword(arg); + else addr = checkinteger(first(args)); + if (cdr(args) == NULL) return number(*(uint32_t *)addr); + (*(uint32_t *)addr) = checkinteger(second(args)); + return second(args); +}"#) + + #+interrupts + (ATTACHINTERRUPT "attach-interrupt" 1 3 #" +object *fn_attachinterrupt (object *args, object *env) { + (void) env; + object *number = first(args); + if (number == NULL) { + int n = NINTERRUPTS; + args = cdr(args); + delassoc(number,&Events); + push(cons(number,first(args)),Events); + InterruptCount[n] = 0; + TCCR1A = 0; // CTC mode + TCCR1B = 1<=NINTERRUPTS-1) error3(ATTACHINTERRUPT, PSTR("invalid interrupt")); + args = cdr(args); + delassoc(number,&Events); + if (args == NULL || first(args) == NULL) { + EIMSK &= ~(1<3) error3(ATTACHINTERRUPT, PSTR("invalid mode")); + EIMSK |= 1< 4 + else { n = n & 0x03; EICRB = (EICRB & ~(3<name) == LAMBDA) { + superprint(cons(bsymbol(DEFUN), cons(var, cdr(val))), 0, pfun); + #if defined(CODESIZE) + } else if (consp(val) && car(val)->type == CODE) { + superprint(cons(bsymbol(DEFCODE), cons(var, cdr(val))), 0, pfun); + #endif + } else { + superprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun); + } + pln(pfun); + testescape(); + globals = cdr(globals); + } + return bsymbol(NOTHING); +}"#) + + #+ignore + (PPRINTALL nil 0 1 #" +/* + (pprintall [str]) + Pretty-prints the definition of every function and variable defined in the uLisp workspace. + If str is specified it prints to the specified stream. It returns no value. +*/ +object *fn_pprintall (object *args, object *env) { + (void) env; + pfun_t pfun = pstreamfun(args); + 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)) && car(val)->name == LAMBDA) { + superprint(cons(symbol(DEFUN), cons(var, cdr(val))), 0, pfun); + } else if (consp(val) && car(val)->type == CODE) { + superprint(cons(symbol(DEFCODE), cons(var, cdr(val))), 0, pfun); + } else { + superprint(cons(symbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun); + } + pln(pfun); + testescape(); + globals = cdr(globals); + } + return symbol(NOTHING); +}"#) + + #+esp + (PPRINTALL nil 0 1 #" +/* + (pprintall [str]) + Pretty-prints the definition of every function and variable defined in the uLisp workspace. + If str is specified it prints to the specified stream. It returns no value. +*/ +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 { + superprint(cons(bsymbol(DEFVAR), cons(var, cons(quote(val), NULL))), 0, pfun); + } + pln(pfun); + testescape(); + globals = cdr(globals); + } + ppwidth = PPWIDTH; + return bsymbol(NOTHING); +}"#) + + #+(or riscv arm) + (PPRINTALL nil 0 1 #" +/* + (pprintall [str]) + Pretty-prints the definition of every function and variable defined in the uLisp workspace. + If str is specified it prints to the specified stream. It returns no value. +*/ +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" + + ((FORMAT nil 2 127 #" +/* + (format output controlstring [arguments]*) + Outputs its arguments formatted according to the format directives in controlstring. +*/ +object *fn_format (object *args, object *env) { + (void) env; + pfun_t pfun = pserial; + object *output = first(args); + object *obj; + if (output == nil) { obj = startstring(); pfun = pstr; } + else if (!eq(output, tee)) pfun = pstreamfun(args); + object *formatstr = checkstring(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 = false, quote = false; + 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" + + ( + (REQUIRE nil 1 1 #" +/* + (require 'symbol) + Loads the definition of a function defined with defun, or a variable defined with defvar, from the Lisp Library. + It returns t if it was loaded, or nil if the symbol is already defined or isn't defined in the Lisp Library. +*/ +object *fn_require (object *args, object *env) { + object *arg = first(args); + object *globals = GlobalEnv; + if (!symbolp(arg)) error(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; +}"#) + + (LISTLIBRARY "list-library" 0 0 #" +/* + (list-library) + Prints a list of the functions defined in the List Library. +*/ +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); +}"#))) + +#+doc +("Documentation" + + ((HELP "?" 1 1 #" +/* + (? item) + Prints the documentation string of a built-in or user-defined function. +*/ +object *sp_help (object *args, object *env) { + if (args == NULL) error2(noargument); + object *docstring = documentation(first(args), env); + if (docstring) { + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printstring(docstring, pserial); + Flags = temp; + } + return bsymbol(NOTHING); +}"#)) "sp") + + #+doc + (nil + ((DOCUMENTATION nil 1 2 #" +/* + (documentation 'symbol [type]) + Returns the documentation string of a built-in or user-defined function. The type argument is ignored. +*/ +object *fn_documentation (object *args, object *env) { + return documentation(first(args), env); +}"#) + + (APROPOS nil 1 1 #" +/* + (apropos item) + Prints the user-defined and built-in functions whose names contain the specified string or symbol. +*/ +object *fn_apropos (object *args, object *env) { + (void) env; + apropos(first(args), true); + return bsymbol(NOTHING); +}"#) + + (APROPOSLIST "apropos-list" 1 1 #" +/* + (apropos-list item) + Returns a list of user-defined and built-in functions whose names contain the specified string or symbol. +*/ +object *fn_aproposlist (object *args, object *env) { + (void) env; + return apropos(first(args), false); +}"#))) + +#+errors +("Error handling" + + ((UNWINDPROTECT "unwind-protect" 0 127 #" +/* + (unwind-protect form1 [forms]*) + Evaluates form1 and forms in order and returns the value of form1, + but guarantees to evaluate forms even if an error occurs in form1. +*/ +object *sp_unwindprotect (object *args, object *env) { + if (args == NULL) error2(toofewargs); + object *current_GCStack = GCStack; + jmp_buf dynamic_handler; + jmp_buf *previous_handler = handler; + handler = &dynamic_handler; + object *protected_form = first(args); + object *result; + + bool signaled = false; + if (!setjmp(dynamic_handler)) { + result = eval(protected_form, env); + } else { + GCStack = current_GCStack; + signaled = true; + } + handler = previous_handler; + + object *protective_forms = cdr(args); + while (protective_forms != NULL) { + eval(car(protective_forms), env); + if (tstflag(RETURNFLAG)) break; + protective_forms = cdr(protective_forms); + } + + if (!signaled) return result; + GCStack = NULL; + longjmp(*handler, 1); +}"#) + +(IGNOREERRORS "ignore-errors" 0 127 #" +/* + (ignore-errors [forms]*) + Evaluates forms ignoring errors. +*/ +object *sp_ignoreerrors (object *args, object *env) { + object *current_GCStack = GCStack; + jmp_buf dynamic_handler; + jmp_buf *previous_handler = handler; + handler = &dynamic_handler; + object *result = nil; + + bool muffled = tstflag(MUFFLEERRORS); + setflag(MUFFLEERRORS); + bool signaled = false; + if (!setjmp(dynamic_handler)) { + while (args != NULL) { + result = eval(car(args), env); + if (tstflag(RETURNFLAG)) break; + args = cdr(args); + } + } else { + GCStack = current_GCStack; + signaled = true; + } + handler = previous_handler; + if (!muffled) clrflag(MUFFLEERRORS); + + if (signaled) return bsymbol(NOTHING); + else return result; +}"#) + +(ERROR nil 1 127 #" +/* + (error controlstring [arguments]*) + Signals an error. The message is printed by format using the controlstring and arguments. +*/ +object *sp_error (object *args, object *env) { + object *message = eval(cons(bsymbol(FORMAT), cons(nil, args)), env); + if (!tstflag(MUFFLEERRORS)) { + flags_t temp = Flags; + clrflag(PRINTREADABLY); + pfstring(PSTR("Error: "), pserial); printstring(message, pserial); + Flags = temp; + pln(pserial); + } + GCStack = NULL; + longjmp(*handler, 1); +}"#)) "sp") + + #+(or arm esp riscv avr) +("SD Card utilities" + ( + (DIRECTORY nil 0 0 #" +/* + (directory) + Returns a list of the filenames of the files on the SD card. +*/ +object *fn_directory (object *args, object *env) { + (void) args, (void) env; + #if defined(sdcardsupport) + SDBegin(); + File root = SD.open("/"); + if (!root) error2("problem reading from SD card"); + object *result = cons(NULL, NULL); + object *ptr = result; + while (true) { + File entry = root.openNextFile(); + if (!entry) break; + object *filename = lispstring((char*)entry.name()); + cdr(ptr) = cons(filename, NULL); + ptr = cdr(ptr); + entry.close(); + } + root.close(); + return cdr(result); + #else + error2("not supported"); + return nil; + #endif +}"#))) + +#+wifi +("Wi-Fi" + ( + #+arm + (WITHCLIENT "with-client" 1 127 #" +/* + (with-client (str [address port]) form*) + Evaluates the forms with str bound to a wifi-stream. +*/ +object *sp_withclient (object *args, object *env) { + #if defined(ULISP_WIFI) + object *params = checkarguments(args, 1, 3); + object *var = first(params); + char buffer[BUFFERSIZE]; + params = cdr(params); + int n; + if (params == NULL) { + client = server.available(); + if (!client) return nil; + n = 2; + } else { + object *address = eval(first(params), env); + object *port = eval(second(params), env); + int success; + if (stringp(address)) success = client.connect(cstring(address, buffer, BUFFERSIZE), checkinteger(port)); + else if (integerp(address)) success = client.connect(address->integer, checkinteger(port)); + else error2(PSTR("invalid address")); + if (!success) return nil; + n = 1; + } + object *pair = cons(var, stream(WIFISTREAM, n)); + push(pair,env); + object *forms = cdr(args); + object *result = eval(tf_progn(forms,env), env); + client.stop(); + return result; + #else + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + +#+esp + (WITHCLIENT "with-client" 1 127 #" +/* + (with-client (str [address port]) form*) + Evaluates the forms with str bound to a wifi-stream. +*/ +object *sp_withclient (object *args, object *env) { + object *params = checkarguments(args, 1, 3); + object *var = first(params); + char buffer[BUFFERSIZE]; + params = cdr(params); + int n; + if (params == NULL) { + client = server.available(); + if (!client) return nil; + n = 2; + } else { + object *address = eval(first(params), env); + object *port = eval(second(params), env); + int success; + if (stringp(address)) success = client.connect(cstring(address, buffer, BUFFERSIZE), checkinteger(port)); + else if (integerp(address)) success = client.connect(address->integer, checkinteger(port)); + else error2(PSTR("invalid address")); + if (!success) return nil; + n = 1; + } + object *pair = cons(var, stream(WIFISTREAM, n)); + push(pair,env); + object *forms = cdr(args); + object *result = eval(tf_progn(forms,env), env); + client.stop(); + return result; +}"#)) "sp") + +#+wifi +(nil + ( + #+esp + (AVAILABLE nil 1 1 #" +/* + (available stream) + Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available. +*/ +object *fn_available (object *args, object *env) { + (void) env; + if (isstream(first(args))>>8 != WIFISTREAM) error2(PSTR("invalid stream")); + return number(client.available()); +}"#) + + #+arm + (AVAILABLE nil 1 1 #" +/* + (available stream) + Returns the number of bytes available for reading from the wifi-stream, or zero if no bytes are available. +*/ +object *fn_available (object *args, object *env) { + #if defined (ULISP_WIFI) + (void) env; + if (isstream(first(args))>>8 != WIFISTREAM) error2(PSTR("invalid stream")); + return number(client.available()); + #else + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+esp + (WIFISERVER "wifi-server" 0 0 #" +/* + (wifi-server) + Starts a Wi-Fi server running. It returns nil. +*/ +object *fn_wifiserver (object *args, object *env) { + (void) args, (void) env; + server.begin(); + return nil; +}"#) + + #+arm + (WIFISERVER "wifi-server" 0 0 #" +/* + (wifi-server) + Starts a Wi-Fi server running. It returns nil. +*/ +object *fn_wifiserver (object *args, object *env) { + #if defined (ULISP_WIFI) + (void) args, (void) env; + server.begin(); + return nil; + #else + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+esp + (WIFISOFTAP "wifi-softap" 0 4 #" +/* + (wifi-softap ssid [password channel hidden]) + Set up a soft access point to establish a Wi-Fi network. + Returns the IP address as a string or nil if unsuccessful. +*/ +object *fn_wifisoftap (object *args, object *env) { + (void) env; + char ssid[33], pass[65]; + if (args == NULL) return WiFi.softAPdisconnect(true) ? tee : nil; + object *first = first(args); args = cdr(args); + if (args == NULL) WiFi.softAP(cstring(first, ssid, 33)); + else { + object *second = first(args); + args = cdr(args); + int channel = 1; + bool hidden = false; + if (args != NULL) { + channel = checkinteger(first(args)); + args = cdr(args); + if (args != NULL) hidden = (first(args) != nil); + } + WiFi.softAP(cstring(first, ssid, 33), cstring(second, pass, 65), channel, hidden); + } + return iptostring(WiFi.softAPIP()); +}"#) + + #+arm + (WIFISOFTAP "wifi-softap" 0 4 #" +/* + (wifi-softap ssid [password channel hidden]) + Set up a soft access point to establish a Wi-Fi network. + Returns the IP address as a string or nil if unsuccessful. +*/ +object *fn_wifisoftap (object *args, object *env) { + #if defined (ULISP_WIFI) + (void) env; + char ssid[33], pass[65]; + object *first = first(args); args = cdr(args); + if (args == NULL) WiFi.beginAP(cstring(first, ssid, 33)); + else { + object *second = first(args); + args = cdr(args); + int channel = 1; + if (args != NULL) { + channel = checkinteger(first(args)); + args = cdr(args); + } + WiFi.beginAP(cstring(first, ssid, 33), cstring(second, pass, 65), channel); + } + return iptostring(WiFi.localIP()); + #else + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+esp + (CONNECTED nil 1 1 #" +/* + (connected stream) + Returns t or nil to indicate if the client on stream is connected. +*/ +object *fn_connected (object *args, object *env) { + (void) env; + if (isstream(first(args))>>8 != WIFISTREAM) error2(PSTR("invalid stream")); + return client.connected() ? tee : nil; +}"#) + + #+arm + (CONNECTED nil 1 1 #" +/* + (connected stream) + Returns t or nil to indicate if the client on stream is connected. +*/ +object *fn_connected (object *args, object *env) { + #if defined (ULISP_WIFI) + (void) env; + if (isstream(first(args))>>8 != WIFISTREAM) error2(PSTR("invalid stream")); + return client.connected() ? tee : nil; + #else + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+esp + (WIFILOCALIP "wifi-localip" 0 0 #" +/* + (wifi-localip) + Returns the IP address of the local network as a string. +*/ +object *fn_wifilocalip (object *args, object *env) { + (void) args, (void) env; + return iptostring(WiFi.localIP()); +}"#) + + #+arm + (WIFILOCALIP "wifi-localip" 0 0 #" +/* + (wifi-localip) + Returns the IP address of the local network as a string. +*/ +object *fn_wifilocalip (object *args, object *env) { + #if defined (ULISP_WIFI) + (void) args, (void) env; + return iptostring(WiFi.localIP()); + #else + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+riscv + (WIFILOCALIP "wifi-localip" 0 0 #" +/* + (wifi-localip) + Returns the IP address of the local network as a string. +*/ +object *fn_wifilocalip (object *args, object *env) { + (void) args, (void) env; + return iptostring(WiFi.localIP()); +}"#) + + #+esp + (WIFICONNECT "wifi-connect" 0 3 #" +/* + (wifi-connect [ssid pass]) + Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string. +*/ +object *fn_wificonnect (object *args, object *env) { + (void) env; + char ssid[33], pass[65]; + if (args == NULL) { WiFi.disconnect(true); return nil; } + if (cdr(args) == NULL) WiFi.begin(cstring(first(args), ssid, 33)); + else WiFi.begin(cstring(first(args), ssid, 33), cstring(second(args), pass, 65)); + int result = WiFi.waitForConnectResult(); + if (result == WL_CONNECTED) return iptostring(WiFi.localIP()); + else if (result == WL_NO_SSID_AVAIL) error2(PSTR("network not found")); + else if (result == WL_CONNECT_FAILED) error2(PSTR("connection failed")); + else error2(PSTR("unable to connect")); + return nil; +}"#) + + #+arm + (WIFICONNECT "wifi-connect" 0 3 #" +/* + (wifi-connect [ssid pass]) + Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string. +*/ +object *fn_wificonnect (object *args, object *env) { + #if defined (ULISP_WIFI) + (void) env; + char ssid[33], pass[65]; + int result = 0; + if (args == NULL) { WiFi.disconnect(); return nil; } + if (cdr(args) == NULL) WiFi.begin(cstring(first(args), ssid, 33)); + else { + if (cddr(args) != NULL) WiFi.config(ipstring(third(args))); + result = WiFi.begin(cstring(first(args), ssid, 33), cstring(second(args), pass, 65)); + } + if (result == WL_CONNECTED) return iptostring(WiFi.localIP()); + else if (result == WL_NO_SSID_AVAIL) error2(PSTR("network not found")); + else if (result == WL_CONNECT_FAILED) error2(PSTR("connection failed")); + else error2(PSTR("unable to connect")); + return nil; + #else + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+riscv + (WIFICONNECT "wifi-connect" 0 2 #" +/* + (wifi-connect [ssid pass]) + Connects to the Wi-Fi network ssid using password pass. It returns the IP address as a string. +*/ +object *fn_wificonnect (object *args, object *env) { + (void) env; + char ssid[33], pass[65]; + int status = WL_IDLE_STATUS; // the Wifi radio's status + // if (args == NULL) { WiFi.disconnect(true); return nil; } + // if (cdr(args) == NULL) WiFi.begin(cstring(first(args), ssid, 33)); + while ( status != WL_CONNECTED) { + status = WiFi.begin(cstring(first(args), ssid, 33), cstring(second(args), pass, 65)); + // int result = WiFi.waitForConnectResult(); + } + return nil; +}"#))) + + #+gfx + ("Graphics functions" + + ((WITHGFX "with-gfx" 1 127 #" +/* + (with-gfx (str) form*) + Evaluates the forms with str bound to an gfx-stream so you can print text + to the graphics display using the standard uLisp print commands. +*/ +object *sp_withgfx (object *args, object *env) { +#if defined(gfxsupport) + object *params = checkarguments(args, 1, 1); + 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(PSTR("not supported")); + return nil; +#endif +}"#)) "sp" ) + + + #+gfx + (nil + + ((DRAWPIXEL "draw-pixel" 2 3 #" +/* + (draw-pixel x y [colour]) + Draws a pixel at coordinates (x,y) in colour, or white if omitted. +*/ +object *fn_drawpixel (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + uint16_t colour = COLOR_WHITE; + if (cddr(args) != NULL) colour = checkinteger(third(args)); + tft.drawPixel(checkinteger(first(args)), checkinteger(second(args)), colour); + #else + (void) args; + #endif + return nil; +}"#) + + (DRAWLINE "draw-line" 4 5 #" +/* + (draw-line x0 y0 x1 y1 [colour]) + Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawLine(params[0], params[1], params[2], params[3], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (DRAWRECT "draw-rect" 4 5 #" +/* + (draw-rect x y w h [colour]) + Draws an outline rectangle with its top left corner at (x,y), with width w, + and with height h. The outline is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawRect(params[0], params[1], params[2], params[3], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (FILLRECT "fill-rect" 4 5 #" +/* + (fill-rect x y w h [colour]) + Draws a filled rectangle with its top left corner at (x,y), with width w, + and with height h. The outline is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillRect(params[0], params[1], params[2], params[3], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (DRAWCIRCLE "draw-circle" 3 4 #" +/* + (draw-circle x y r [colour]) + Draws an outline circle with its centre at (x, y) and with radius r. + The circle is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawCircle(params[0], params[1], params[2], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (FILLCIRCLE "fill-circle" 3 4 #" +/* + (fill-circle x y r [colour]) + Draws a filled circle with its centre at (x, y) and with radius r. + The circle is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillCircle(params[0], params[1], params[2], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (DRAWROUNDRECT "draw-round-rect" 5 6 #" +/* + (draw-round-rect x y w h radius [colour]) + Draws an outline rounded rectangle with its top left corner at (x,y), with width w, + height h, and corner radius radius. The outline is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawRoundRect(params[0], params[1], params[2], params[3], params[4], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (FILLROUNDRECT "fill-round-rect" 5 6 #" +/* + (fill-round-rect x y w h radius [colour]) + Draws a filled rounded rectangle with its top left corner at (x,y), with width w, + height h, and corner radius radius. The outline is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillRoundRect(params[0], params[1], params[2], params[3], params[4], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (DRAWTRIANGLE "draw-triangle" 6 7 #" +/* + (draw-triangle x0 y0 x1 y1 x2 y2 [colour]) + Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3). + The outline is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (FILLTRIANGLE "fill-triangle" 6 7 #" +/* + (fill-triangle x0 y0 x1 y1 x2 y2 [colour]) + Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3). + The outline is drawn in colour, or white if omitted. +*/ +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(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); + #else + (void) args; + #endif + return nil; +}"#) + + (DRAWCHAR "draw-char" 3 6 #" +/* + (draw-char x y char [colour background size]) + Draws the character char with its top left corner at (x,y). + The character is drawn in a 5 x 7 pixel font in colour against background, + which default to white and black respectively. + The character can optionally be scaled by size. +*/ +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(car(more)); + more = cdr(more); + if (more != NULL) { + bg = checkinteger(car(more)); + more = cdr(more); + if (more != NULL) size = checkinteger(car(more)); + } + } + tft.drawChar(checkinteger(first(args)), checkinteger(second(args)), checkchar(third(args)), + colour, bg, size); + #else + (void) args; + #endif + return nil; +}"#) + + (SETCURSOR "set-cursor" 2 2 #" +/* + (set-cursor x y) + Sets the start point for text plotting to (x, y). +*/ +object *fn_setcursor (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + tft.setCursor(checkinteger(first(args)), checkinteger(second(args))); + #else + (void) args; + #endif + return nil; +}"#) + + (SETTEXTCOLOR "set-text-color" 1 2 #" +/* + (set-text-color colour [background]) + Sets the text colour for text plotted using (with-gfx ...). +*/ +object *fn_settextcolor (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + if (cdr(args) != NULL) tft.setTextColor(checkinteger(first(args)), checkinteger(second(args))); + else tft.setTextColor(checkinteger(first(args))); + #else + (void) args; + #endif + return nil; +}"#) + + (SETTEXTSIZE "set-text-size" 1 1 #" +/* + (set-text-size scale) + Scales text by the specified size, default 1. +*/ +object *fn_settextsize (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + tft.setTextSize(checkinteger(first(args))); + #else + (void) args; + #endif + return nil; +}"#) + + (SETTEXTWRAP "set-text-wrap" 1 1 #" +/* + (set-text-wrap boolean) + Specified whether text wraps at the right-hand edge of the display; the default is t. +*/ +object *fn_settextwrap (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + tft.setTextWrap(first(args) != NULL); + #else + (void) args; + #endif + return nil; +}"#) + + (FILLSCREEN "fill-screen" 0 1 #" +/* + (fill-screen [colour]) + Fills or clears the screen with colour, default black. +*/ +object *fn_fillscreen (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + uint16_t colour = COLOR_BLACK; + if (args != NULL) colour = checkinteger(first(args)); + tft.fillScreen(colour); + #else + (void) args; + #endif + return nil; +}"#) + + (SETROTATION "set-rotation" 1 1 #" +/* + (set-rotation option) + Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3. +*/ +object *fn_setrotation (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + tft.setRotation(checkinteger(first(args))); + #else + (void) args; + #endif + return nil; +}"#) + + (INVERTDISPLAY "invert-display" 1 1 #" +/* + (invert-display boolean) + Mirror-images the display. +*/ +object *fn_invertdisplay (object *args, object *env) { + (void) env; + #if defined(gfxsupport) + tft.invertDisplay(first(args) != NULL); + #else + (void) args; + #endif + return nil; +}"#) + + #+ignore + (GETPIXEL "get-pixel" 2 2 #" +#if defined(gfxsupport) +uint16_t Technoblogy_ST7735::getPixel (uint16_t x, uint16_t y) { + uint32_t ret = 0; + startWrite(); + setAddrWindow(x, y, 1, 1); + writeCommand(ST77XX_RAMRD); + pinMode(TFT_MOSI, INPUT); + pinMode(TFT_SCLK, OUTPUT); + for (int i=0; i<33; i++) { + digitalWrite(TFT_SCLK, HIGH); + ret = ret<<1 | digitalRead(TFT_MOSI); + digitalWrite(TFT_SCLK, LOW); + } + pinMode(TFT_MOSI, OUTPUT); + endWrite(); + return ((ret & 0xf80000)>>8 | (ret & 0xfc00)>>5 | (ret & 0xf8)>>3); +} +#endif + +object *fn_getpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + return number(tft.getPixel(checkinteger(first(args)), checkinteger(second(args)))); + #endif +}"#) + + #+ignore + (GETPIXEL "get-pixel" 2 2 #" +object *fn_getpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) args, (void) env; + error2(PSTR("not supported")); + #endif + return nil; +}"#) + + #+ignore + (XORPIXEL "xor-pixel" 2 3 #" +#if defined(gfxsupport) +void Technoblogy_ST7735::xorPixel (uint16_t x, uint16_t y, uint16_t color) { + uint16_t lastcolor = getPixel(x, y); + if ((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { + startWrite(); + writeCommand(ST77XX_RAMWR); + SPI_WRITE16(color ^ lastcolor); + endWrite(); + } +} +#endif + +object *fn_xorpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t colour = COLOR_WHITE; + if (cddr(args) != NULL) colour = checkinteger(third(args)); + tft.xorPixel(checkinteger(first(args)), checkinteger(second(args)), colour); + #endif + return nil; +}"#) + + #+ignore + (XORPIXEL "xor-pixel" 2 3 #" +object *fn_xorpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) args, (void) env; + error2(PSTR("not supported")); + #endif + return nil; +}"#) + + #+ignore + (XORSPRITE "xor-sprite" 4 5 #" +#if defined(gfxsupport) +void Technoblogy_ST7735::xorSprite (uint16_t x, uint16_t y, uint32_t top, uint32_t bottom, uint16_t color) { + uint16_t row[8]; + uint32_t col = 0; + bool bit; + if ((x >= 0) && (x+7 < _width) && (y >= 0) && (y+7 < _height)) { + for (int yd=0; yd<8; yd++) { + startWrite(); + setAddrWindow(x, y+yd, 8, 1); + writeCommand(ST77XX_RAMRD); + pinMode(TFT_MOSI, INPUT); + pinMode(TFT_SCLK, OUTPUT); + for (int i=0; i<9; i++) { + digitalWrite(TFT_SCLK, HIGH); + digitalWrite(TFT_SCLK, LOW); + } + for (int xd=0; xd<8; xd++) { + for (int i=0; i<24; i++) { + digitalWrite(TFT_SCLK, HIGH); + col = col<<1 | digitalRead(TFT_MOSI); + digitalWrite(TFT_SCLK, LOW); + } + row[xd] = ((col & 0xf80000)>>8 | (col & 0xfc00)>>5 | (col & 0xf8)>>3); + } + pinMode(TFT_MOSI, OUTPUT); + endWrite(); + startWrite(); + writeCommand(ST77XX_RAMWR); + for (int xd=0; xd<8; xd++) { + if (yd < 4) bit = top>>(31 - xd - yd*8) & 1; + else bit = bottom>>(31 - xd - (yd-4)*8) & 1; + if (bit) SPI_WRITE16(row[xd] ^ color); + else SPI_WRITE16(row[xd]); + } + endWrite(); + } + } +} +#endif + +object *fn_xorsprite (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint32_t params[4]; uint16_t colour = COLOR_WHITE; + for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.xorSprite(params[0], params[1], params[2], params[3], colour); + #endif + return nil; +}"#) + + #+ignore + (XORSPRITE "xor-sprite" 4 5 #" +object *fn_xorsprite (object *args, object *env) { + #if defined(gfxsupport) + (void) args, (void) env; + error2(PSTR("not supported")); + #endif + return nil; +}"#))) + + #+ignore + ("Graphics functions" + + ((DRAWPIXEL "draw-pixel" 2 3 #" +/* + (draw-pixel x y [colour]) + Draws a pixel at coordinates (x,y) in colour, or white if omitted. +*/ +object *fn_drawpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t colour = COLOR_WHITE; + if (cddr(args) != NULL) colour = checkinteger(third(args)); + tft.drawPixel(checkinteger(first(args)), checkinteger(second(args)), colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (DRAWLINE "draw-line" 4 5 #" +/* + (draw-line x0 y0 x1 y1 [colour]) + Draws a line from (x0,y0) to (x1,y1) in colour, or white if omitted. +*/ +object *fn_drawline (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[4], colour = COLOR_WHITE; + for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawLine(params[0], params[1], params[2], params[3], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (DRAWRECT "draw-rect" 4 5 #" +/* + (draw-rect x y w h [colour]) + Draws an outline rectangle with its top left corner at (x,y), with width w, + and with height h. The outline is drawn in colour, or white if omitted. +*/ +object *fn_drawrect (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[4], colour = COLOR_WHITE; + for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawRect(params[0], params[1], params[2], params[3], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (FILLRECT "fill-rect" 4 5 #" +/* + (fill-rect x y w h [colour]) + Draws a filled rectangle with its top left corner at (x,y), with width w, + and with height h. The outline is drawn in colour, or white if omitted. +*/ +object *fn_fillrect (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[4], colour = COLOR_WHITE; + for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillRect(params[0], params[1], params[2], params[3], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (DRAWCIRCLE "draw-circle" 3 4 #" +/* + (draw-circle x y r [colour]) + Draws an outline circle with its centre at (x, y) and with radius r. + The circle is drawn in colour, or white if omitted. +*/ +object *fn_drawcircle (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[3], colour = COLOR_WHITE; + for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawCircle(params[0], params[1], params[2], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (FILLCIRCLE "fill-circle" 3 4 #" +/* + (fill-circle x y r [colour]) + Draws a filled circle with its centre at (x, y) and with radius r. + The circle is drawn in colour, or white if omitted. +*/ +object *fn_fillcircle (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[3], colour = COLOR_WHITE; + for (int i=0; i<3; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillCircle(params[0], params[1], params[2], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (DRAWROUNDRECT "draw-round-rect" 5 6 #" +/* + (draw-round-rect x y w h radius [colour]) + Draws an outline rounded rectangle with its top left corner at (x,y), with width w, + height h, and corner radius radius. The outline is drawn in colour, or white if omitted. +*/ +object *fn_drawroundrect (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[5], colour = COLOR_WHITE; + for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawRoundRect(params[0], params[1], params[2], params[3], params[4], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (FILLROUNDRECT "fill-round-rect" 5 6 #" +/* + (fill-round-rect x y w h radius [colour]) + Draws a filled rounded rectangle with its top left corner at (x,y), with width w, + height h, and corner radius radius. The outline is drawn in colour, or white if omitted. +*/ +object *fn_fillroundrect (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[5], colour = COLOR_WHITE; + for (int i=0; i<5; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillRoundRect(params[0], params[1], params[2], params[3], params[4], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (DRAWTRIANGLE "draw-triangle" 6 7 #" +/* + (draw-triangle x0 y0 x1 y1 x2 y2 [colour]) + Draws an outline triangle between (x1,y1), (x2,y2), and (x3,y3). + The outline is drawn in colour, or white if omitted. +*/ +object *fn_drawtriangle (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[6], colour = COLOR_WHITE; + for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.drawTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (FILLTRIANGLE "fill-triangle" 6 7 #" +/* + (fill-triangle x0 y0 x1 y1 x2 y2 [colour]) + Draws a filled triangle between (x1,y1), (x2,y2), and (x3,y3). + The outline is drawn in colour, or white if omitted. +*/ +object *fn_filltriangle (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t params[6], colour = COLOR_WHITE; + for (int i=0; i<6; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.fillTriangle(params[0], params[1], params[2], params[3], params[4], params[5], colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (DRAWCHAR "draw-char" 3 6 #" +/* + (draw-char x y char [colour background size]) + Draws the character char with its top left corner at (x,y). + The character is drawn in a 5 x 7 pixel font in colour against background, + which default to white and black respectively. + The character can optionally be scaled by size. +*/ +object *fn_drawchar (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t colour = COLOR_WHITE, bg = COLOR_BLACK, size = 1; + object *more = cdr(cddr(args)); + if (more != NULL) { + colour = checkinteger(car(more)); + more = cdr(more); + if (more != NULL) { + bg = checkinteger(car(more)); + more = cdr(more); + if (more != NULL) size = checkinteger(car(more)); + } + } + tft.drawChar(checkinteger(first(args)), checkinteger(second(args)), checkchar(DRAWCHAR, third(args)), + colour, bg, size); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (SETCURSOR "set-cursor" 2 2 #" +/* + (set-cursor x y) + Sets the start point for text plotting to (x, y). +*/ +object *fn_setcursor (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + tft.setCursor(checkinteger(first(args)), checkinteger(second(args))); + #endif + return nil; +}"#) + + (SETTEXTCOLOR "set-text-color" 1 2 #" +/* + (set-text-color colour [background]) + Sets the text colour for text plotted using (with-gfx ...). +*/ +object *fn_settextcolor (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + if (cdr(args) != NULL) tft.setTextColor(checkinteger(first(args)), checkinteger(second(args))); + else tft.setTextColor(checkinteger(first(args))); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (SETTEXTSIZE "set-text-size" 1 1 #" +/* + (set-text-size scale) + Scales text by the specified size, default 1. +*/ +object *fn_settextsize (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + tft.setTextSize(checkinteger(first(args))); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (SETTEXTWRAP "set-text-wrap" 1 1 #" +/* + (set-text-wrap boolean) + Specified whether text wraps at the right-hand edge of the display; the default is t. +*/ +object *fn_settextwrap (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + tft.setTextWrap(first(args) != NULL); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (FILLSCREEN "fill-screen" 0 1 #" +/* + (fill-screen [colour]) + Fills or clears the screen with colour, default black. +*/ +object *fn_fillscreen (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t colour = COLOR_BLACK; + if (args != NULL) colour = checkinteger(first(args)); + tft.fillScreen(colour); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (SETROTATION "set-rotation" 1 1 #" +/* + (set-rotation option) + Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3. +*/ +object *fn_setrotation (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + tft.setRotation(checkinteger(first(args))); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + (INVERTDISPLAY "invert-display" 1 1 #" +/* + (invert-display boolean) + Mirror-images the display. +*/ +object *fn_invertdisplay (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + tft.invertDisplay(first(args) != NULL); + tft.display(); + #else + (void) args, (void) env; + #endif + return nil; +}"#) + + #+ignore + (GETPIXEL "get-pixel" 2 2 #" +#if defined(gfxsupport) +uint16_t Technoblogy_ST7735::getPixel (uint16_t x, uint16_t y) { + uint32_t ret = 0; + startWrite(); + setAddrWindow(x, y, 1, 1); + writeCommand(ST77XX_RAMRD); + pinMode(TFT_MOSI, INPUT); + pinMode(TFT_SCLK, OUTPUT); + for (int i=0; i<33; i++) { + digitalWrite(TFT_SCLK, HIGH); + ret = ret<<1 | digitalRead(TFT_MOSI); + digitalWrite(TFT_SCLK, LOW); + } + pinMode(TFT_MOSI, OUTPUT); + endWrite(); + return ((ret & 0xf80000)>>8 | (ret & 0xfc00)>>5 | (ret & 0xf8)>>3); +} +#endif + +object *fn_getpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + return number(tft.getPixel(checkinteger(first(args)), checkinteger(second(args)))); + #endif +}"#) + + #+ignore + (GETPIXEL "get-pixel" 2 2 #" +object *fn_getpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+ignore + (XORPIXEL "xor-pixel" 2 3 #" +#if defined(gfxsupport) +void Technoblogy_ST7735::xorPixel (uint16_t x, uint16_t y, uint16_t color) { + uint16_t lastcolor = getPixel(x, y); + if ((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { + startWrite(); + writeCommand(ST77XX_RAMWR); + SPI_WRITE16(color ^ lastcolor); + endWrite(); + } +} +#endif + +object *fn_xorpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint16_t colour = COLOR_WHITE; + if (cddr(args) != NULL) colour = checkinteger(third(args)); + tft.xorPixel(checkinteger(first(args)), checkinteger(second(args)), colour); + #endif + return nil; +}"#) + + #+ignore + (XORPIXEL "xor-pixel" 2 3 #" +object *fn_xorpixel (object *args, object *env) { + #if defined(gfxsupport) + (void) args, (void) env; + error2(PSTR("not supported")); + return nil; + #endif +}"#) + + #+ignore + (XORSPRITE "xor-sprite" 4 5 #" +#if defined(gfxsupport) +void Technoblogy_ST7735::xorSprite (uint16_t x, uint16_t y, uint32_t top, uint32_t bottom, uint16_t color) { + uint16_t row[8]; + uint32_t col = 0; + bool bit; + if ((x >= 0) && (x+7 < _width) && (y >= 0) && (y+7 < _height)) { + for (int yd=0; yd<8; yd++) { + startWrite(); + setAddrWindow(x, y+yd, 8, 1); + writeCommand(ST77XX_RAMRD); + pinMode(TFT_MOSI, INPUT); + pinMode(TFT_SCLK, OUTPUT); + for (int i=0; i<9; i++) { + digitalWrite(TFT_SCLK, HIGH); + digitalWrite(TFT_SCLK, LOW); + } + for (int xd=0; xd<8; xd++) { + for (int i=0; i<24; i++) { + digitalWrite(TFT_SCLK, HIGH); + col = col<<1 | digitalRead(TFT_MOSI); + digitalWrite(TFT_SCLK, LOW); + } + row[xd] = ((col & 0xf80000)>>8 | (col & 0xfc00)>>5 | (col & 0xf8)>>3); + } + pinMode(TFT_MOSI, OUTPUT); + endWrite(); + startWrite(); + writeCommand(ST77XX_RAMWR); + for (int xd=0; xd<8; xd++) { + if (yd < 4) bit = top>>(31 - xd - yd*8) & 1; + else bit = bottom>>(31 - xd - (yd-4)*8) & 1; + if (bit) SPI_WRITE16(row[xd] ^ color); + else SPI_WRITE16(row[xd]); + } + endWrite(); + } + } +} +#endif + +object *fn_xorsprite (object *args, object *env) { + #if defined(gfxsupport) + (void) env; + uint32_t params[4]; uint16_t colour = COLOR_WHITE; + for (int i=0; i<4; i++) { params[i] = checkinteger(car(args)); args = cdr(args); } + if (args != NULL) colour = checkinteger(car(args)); + tft.xorSprite(params[0], params[1], params[2], params[3], colour); + return nil; + #endif +}"#) + + #+ignore + (XORSPRITE "xor-sprite" 4 5 #" +object *fn_xorsprite (object *args, object *env) { + #if defined(gfxsupport) + (void) args, (void) env; + error2(PSTR("not supported")); + #endif + return nil; +}"#))) + + #+badge + ("Lisp Badge plotting" + + ((PLOT nil 0 6 #" +void plotsub (uint8_t x, uint8_t y, uint8_t n, int ys[5]) { + if (y<64) { + uint8_t grey = 0x0F-n*3; + uint8_t blob = grey; + if ((x&1) == 0) { blob = grey<<4; ys[n] = y; } + else { + for (int i=0; i<5; i++) { + if (y == ys[i]) blob = (0x0F-i*3)<<4 | grey; + } + } + PlotByte(x>>1, y, blob); + } +} + +object *fn_plot (object *args, object *env) { + int ys[5] = {-1, -1, -1, -1, -1}; + int xaxis = -1, yaxis = -1; + delay(20); + ClearDisplay(0); // Clear display + if (args != NULL && integerp(first(args))) { xaxis = checkinteger(first(args)); args = cdr(args); } + if (args != NULL && integerp(first(args))) { yaxis = checkinteger(first(args)); args = cdr(args); } + int nargs = min(listlength(args),4); + for (int x=0; x<256; x++) { + object *rest = args; + for (int n=0; n>1, y, blob); + } + } + } + while (!tstflag(ESCAPE)); clrflag(ESCAPE); + return symbol(NOTHING); +}"#) + + (GLYPHPIXEL "glyph-pixel" 3 3 #" +extern const uint8_t CharMap[96][6] PROGMEM; + +object *fn_glyphpixel (object *args, object *env) { + (void) env; + uint8_t c = 0, x = 6, y = 8; + c = checkchar(GLYPHPIXEL, first(args)); + x = checkinteger(second(args)); + y = checkinteger(third(args)); + if (x > 5 || y > 7) return number(0); + return pgm_read_byte(&CharMap[(c & 0x7f) - 32][x]) & 1 << (7 - y) ? number(15) : number(0); +}"#) + + (PLOTPIXEL "plot-pixel" 2 3 #" +object *fn_plotpixel (object *args, object *env) { + (void) env; + int x = checkinteger(first(args)); + int y = checkinteger(second(args)); + args = cddr(args); + uint8_t grey = 0xff; + if (args != NULL) grey = checkinteger(first(args)); + PlotByte(x, y, grey); + return nil; +}"#) + + (FILLSCREEN "fill-screen" 0 1 #" +object *fn_fillscreen (object *args, object *env) { + (void) env; + uint8_t grey = 0; + if (args != NULL) grey = checkinteger(first(args)); + ClearDisplay(grey); + return nil; +}"#))) + +)) \ No newline at end of file diff --git a/builder/postscript.lisp b/builder/postscript.lisp new file mode 100644 index 0000000..ba1bda8 --- /dev/null +++ b/builder/postscript.lisp @@ -0,0 +1,2507 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +; Postscript + +(defparameter *table* + + '( + + #-avr-nano + #" +#if !defined(extensions) +// Table cross-reference functions + +tbl_entry_t *tables[] = {lookup_table, NULL}; +const unsigned int tablesizes[] = { arraysize(lookup_table), 0 }; + +const tbl_entry_t *table (int n) { + return tables[n]; +} + +unsigned int tablesize (int n) { + return tablesizes[n]; +} +#endif"# + +#" +// Table lookup functions"# + + #+avr-nano +#" +/* + lookupbuiltin - looks up a string in lookup_table[], and returns the index of its entry, or ENDFUNCTIONS + if no match is found. Doesn't support an extensions file. +*/ +builtin_t lookupbuiltin (char* n) { + int entries = arraysize(lookup_table); + for (int entry = 0; entry < entries; entry++) { + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + if (strcasecmp(n, (char*)lookup_table[entry].string) == 0) + #else + if (strcasecmp_P(n, (char*)pgm_read_ptr(&lookup_table[entry].string)) == 0) + #endif + return (builtin_t)entry; + } + return ENDFUNCTIONS; +}"# + + #+avr +#" +/* + lookupbuiltin - looks up a string in lookup_table[], and returns the index of its entry, or ENDFUNCTIONS + if no match is found. Allows definitions in an extension file to override the built-in functions. +*/ +builtin_t lookupbuiltin (char* c) { + unsigned int start = tablesize(0); + for (int n=1; n>=0; n--) { + int entries = tablesize(n); + for (int i=0; i=0; n--) { + int entries = tablesize(n); + for (int i=0; i> 3) & 0x07)) error2(toofewargs); + if ((minmax & 0x07) != 0x07 && nargs>(minmax & 0x07)) error2(toomanyargs); +}"# + + #+avr + #" +/* + lookupdoc - looks up the documentation string for the built-in function name +*/ +char *lookupdoc (builtin_t name) { + bool n = namechars)>>((sizeof(int)-1)*8) & 0xFF) == ':'); +}"# + + #+avr-nano + #" +/* + keywordp - check that obj is a keyword +*/ +bool keywordp (object *obj) { + if (!(symbolp(obj) && builtinp(obj->name))) return false; + builtin_t name = builtin(obj->name); + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + PGM_P s = lookup_table[name].string; + char c = s[0]; + #else + PGM_P s = (char*)pgm_read_ptr(&lookup_table[name].string); + char c = pgm_read_byte(s); + #endif + return (c == ':'); +}"# + + #+avr + #" +/* + keywordp - check that obj is a keyword +*/ +bool keywordp (object *obj) { + if (!(symbolp(obj) && builtinp(obj->name))) return false; + builtin_t name = builtin(obj->name); + bool n = namename))) return false; + builtin_t name = builtin(obj->name); + bool n = name>4) gc(form, env); // GC when 1/16 of workspace left + // Escape + if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2(PSTR("escape!"));} + if (!tstflag(NOESC)) testescape();"# + +#+arm +#" +/* + eval - the main Lisp evaluator +*/ +object *eval (object *form, object *env) { + register int *sp asm ("sp"); + 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) { Context = NIL; error2(PSTR("stack overflow")); } + if (Freespace <= WORKSPACESIZE>>4) gc(form, env); // GC when 1/16 of workspace left + // Escape + if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2(PSTR("escape!"));} + if (!tstflag(NOESC)) testescape();"# + +#+riscv +#" +/* + eval - the main Lisp evaluator +*/ +object *eval (object *form, object *env) { + register int *sp asm ("sp"); + int TC=0; + EVAL: + // Enough space? + // Serial.println((uintptr_t)sp - (uintptr_t)end); + if ((uintptr_t)sp - (uintptr_t)end < STACKDIFF) { Context = NIL; error2(PSTR("stack overflow")); } + if (Freespace <= WORKSPACESIZE>>4) gc(form, env); + // Escape + if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2(PSTR("escape!"));} + if (!tstflag(NOESC)) testescape();"# + +#+esp +#" +/* + eval - the main Lisp evaluator +*/ +object *eval (object *form, object *env) { + bool stackpos; + static unsigned long start = 0; + int TC=0; + EVAL: + // Enough space? + // Serial.println((uint32_t)StackBottom - (uint32_t)&stackpos); // Find best MAX_STACK value + if ((uint32_t)StackBottom - (uint32_t)&stackpos > MAX_STACK) { Context = NIL; error2("stack overflow"); } + if (Freespace <= WORKSPACESIZE>>4) gc(form, env); + // Escape + if (tstflag(ESCAPE)) { clrflag(ESCAPE); error2("escape!");} + if (!tstflag(NOESC)) testescape(); +"# + +#+avr-nano +#" + 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; + Context = NIL; + error(PSTR("undefined"), form); + }"# + +#-avr-nano +#" + if (form == NULL) return nil; + + if (form->type >= NUMBER && form->type <= STRING) return form; + + if (symbolp(form)) { + symbol_t name = form->name; + if (colonp(name)) return form; // Keyword + 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)) { + if (name == sym(FEATURES)) return features(); + return form; + } + Context = NIL; + error(PSTR("undefined"), form); + }"# + +#+(or avr avr-nano arm riscv) +#" + #if defined(CODESIZE) + if (form->type == CODE) error2(PSTR("can't evaluate CODE header")); + #endif"# + +#" + // It's a list + object *function = car(form); + object *args = cdr(form); + + if (function == NULL) error(illegalfn, function); + if (!listp(args)) error(PSTR("can't evaluate a dotted pair"), args); + + // List starts with a builtin symbol? + if (symbolp(function) && builtinp(function->name)) { + builtin_t name = builtin(function->name); + + if ((name == LET) || (name == LETSTAR)) { + if (args == NULL) error2(noargument); + object *assigns = first(args); + if (!listp(assigns)) error(notalist, assigns); + object *forms = cdr(args); + object *newenv = env; + protect(newenv); + 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; + unprotect(); + form = tf_progn(forms,env); + 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)); + } + + switch(fntype(name)) { + case SPECIAL_FORMS: + Context = name; + checkargs(args); + return ((fn_ptr_type)lookupfn(name))(args, env); + + case TAIL_FORMS: + Context = name; + checkargs(args); + form = ((fn_ptr_type)lookupfn(name))(args, env); + TC = 1; + goto EVAL; + + case OTHER_FORMS: error(illegalfn, function); + } + }"# + +#+avr-nano +#" + // Evaluate the parameters - result in head + int TCstart = TC; + object *head = cons(eval(function, env), NULL); + protect(head); // 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++; + }"# + +#-avr-nano +#" + // Evaluate the parameters - result in head + int TCstart = TC; + object *head; + if (consp(function) && !(isbuiltin(car(function), LAMBDA) || isbuiltin(car(function), CLOSURE) + || car(function)->type == CODE)) { Context = NIL; error(illegalfn, function); } + if (symbolp(function)) { + object *pair = findpair(function, env); + if (pair != NULL) head = cons(cdr(pair), NULL); else head = cons(function, NULL); + } else head = cons(eval(function, env), NULL); + protect(head); // 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++; + }"# + +#" + object *fname = function; + function = car(head); + args = cdr(head); + + if (symbolp(function)) { + if (!builtinp(function->name)) { Context = NIL; error(illegalfn, function); } + builtin_t bname = builtin(function->name); + Context = bname; + checkminmax(bname, nargs); + object *result = ((fn_ptr_type)lookupfn(bname))(args, env); + unprotect(); + return result; + } + + if (consp(function)) { + symbol_t name = sym(NIL); + if (!listp(fname)) name = fname->name;"# + +#+avr-nano +#" + if (isbuiltin(car(function), LAMBDA)) { + form = closure(TCstart, name, function, args, &env); + unprotect(); + int trace = tracing(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(" returned ", pserial); + printobject(result, pserial); pln(pserial); + return result; + } else { + TC = 1; + goto EVAL; + } + }"# + +#-avr-nano +#" + if (isbuiltin(car(function), LAMBDA)) { + if (tstflag(BACKTRACE)) backtrace(name); + form = closure(TCstart, name, function, args, &env); + unprotect(); + int trace = tracing(name); + if (trace || tstflag(BACKTRACE)) { + object *result = eval(form, env); + if (trace) { + indent((--(TraceDepth[trace-1]))<<1, ' ', pserial); + pint(TraceDepth[trace-1], pserial); + pserial(':'); pserial(' '); + printobject(fname, pserial); pfstring(" returned ", pserial); + printobject(result, pserial); pln(pserial); + } + if (tstflag(BACKTRACE)) TraceTop = modbacktrace(TraceTop-1); + return result; + } else { + TC = 1; + goto EVAL; + } + }"# + + +#+avr-nano +#" + if (isbuiltin(car(function), CLOSURE)) { + function = cdr(function); + form = closure(TCstart, name, function, args, &env); + unprotect(); + TC = 1; + goto EVAL; + }"# + +#-avr-nano +#" + if (isbuiltin(car(function), CLOSURE)) { + function = cdr(function); + if (tstflag(BACKTRACE)) backtrace(name); + form = closure(TCstart, name, function, args, &env); + unprotect(); + if (tstflag(BACKTRACE)) { + object *result = eval(form, env); + TraceTop = modbacktrace(TraceTop-1); + return result; + } else { + TC = 1; + goto EVAL; + } + }"# + +#+avr +#" + #if defined(CODESIZE) + if (car(function)->type == CODE) { + int n = listlength(second(function)); + if (nargsname, toofewargs); + if (nargs>n) errorsym2(fname->name, toomanyargs); + uint32_t entry = startblock(car(function)); + unprotect(); + return call(entry, n, args, env); + } + #endif"# + +#+arm +#" + if (car(function)->type == CODE) { + int n = listlength(second(function)); + if (nargsname, toofewargs); + if (nargs>n) errorsym2(fname->name, toomanyargs); + uint32_t entry = startblock(car(function)) + 1; + unprotect(); + return call(entry, n, args, env); + }"# + +#+riscv +#" + if (car(function)->type == CODE) { + int n = listlength(second(function)); + if (nargsname, toofewargs); + if (nargs>n) errorsym2(fname->name, toomanyargs); + uint32_t entry = startblock(car(function)); + unprotect(); + return call(entry, n, args, env); + }"# + +#" + } + error(illegalfn, fname); return nil; +}"#)) + +(defparameter *print-functions* + + '(#" +// Print functions"# + + #-badge + #" +/* + pserial - prints a character to the serial port +*/ +void pserial (char c) { + LastPrint = c; + if (c == '\n') Serial.write('\r'); + Serial.write(c); +}"# + + + #+badge + #" +/* + pserial - prints a character to the serial port +*/ +void pserial (char c) { + LastPrint = c; + Display(c); + #if defined (serialmonitor) + if (c == '\n') Serial.write('\r'); + Serial.write(c); + #endif +}"# + + #+(or avr avr-nano) + #" +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"; + +/* + pcharacter - prints a character to a stream, escaping special characters if PRINTREADABLY is false + If <= 32 prints character name; eg #\Space + If < 127 prints ASCII; eg #\A + Otherwise prints decimal; eg #\234 +*/ +void pcharacter (uint8_t c, pfun_t pfun) { + if (!tstflag(PRINTREADABLY)) pfun(c); + else { + pfun('#'); pfun('\\'); + if (c <= 32) { + PGM_P p = ControlCodes; + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + while (c > 0) {p = p + strlen(p) + 1; c--; } + #else + while (c > 0) {p = p + strlen_P(p) + 1; c--; } + #endif + pfstring(p, pfun); + } else if (c < 127) pfun(c); + else pint(c, pfun); + } +}"# + + #+ignore ; was badge + #" +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"; + +/* + pcharacter - prints a character to a stream, escaping special characters if PRINTREADABLY is false + If <= 32 prints character name; eg #\Space + If < 127 prints ASCII; eg #\A + Otherwise prints decimal; eg #\234 +*/ +void pcharacter (uint8_t c, pfun_t pfun) { + if (!tstflag(PRINTREADABLY)) pfun(c); + else { + pfun('#'); pfun('\\'); + if (c <= 32) { + PGM_P p = ControlCodes; + while (c > 0) {p = p + strlen_P(p) + 1; c--; } + pfstring(p, pfun); + } else if (c < 127) pfun(c); + else pint(c, pfun); + } +}"# + + #+(or arm esp riscv) + #" +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"; + +/* + pcharacter - prints a character to a stream, escaping special characters if PRINTREADABLY is false + If <= 32 prints character name; eg #\Space + If < 127 prints ASCII; eg #\A + Otherwise prints decimal; eg #\234 +*/ +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); + } +}"# + + #" +/* + pstring - prints a C string to the specified stream +*/ +void pstring (char *s, pfun_t pfun) { + while (*s) pfun(*s++); +}"# + + #" +/* + plispstring - prints a Lisp string object to the specified stream +*/ +void plispstring (object *form, pfun_t pfun) { + plispstr(form->name, pfun); +} + +/* + plispstr - prints a Lisp string name to the specified stream +*/ +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); + } +} + +/* + printstring - prints a Lisp string object to the specified stream + taking account of the PRINTREADABLY flag +*/ +void printstring (object *form, pfun_t pfun) { + if (tstflag(PRINTREADABLY)) pfun('"'); + plispstr(form->name, pfun); + if (tstflag(PRINTREADABLY)) pfun('"'); +}"# + + #+avr-nano + #" +/* + pbuiltin - prints a built-in symbol to the specified stream +*/ +void pbuiltin (builtin_t name, pfun_t pfun) { + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + PGM_P s = lookup_table[name].string; + #else + PGM_P s = (char*)pgm_read_ptr(&lookup_table[name].string); + #endif + while (1) { + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + char c = @s++; + #else + char c = pgm_read_byte(s++); + #endif + if (c == 0) return; + pfun(c); + } +}"# + + #+avr + #" +/* + pbuiltin - prints a built-in symbol to the specified stream +*/ +void pbuiltin (builtin_t name, pfun_t pfun) { + int n = name0; d = d/40) { + uint16_t j = x/d; + char c = fromradix40(j); + if (c == 0) return; + pfun(c); x = x - j*d; + } +}"# + + #+(or arm esp riscv) + #" +/* + pradix40 - prints a radix 40 symbol to the specified stream +*/ +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; + } +}"# + + #" +/* + printsymbol - prints any symbol from a symbol object to the specified stream +*/ +void printsymbol (object *form, pfun_t pfun) { + psymbol(form->name, pfun); +}"# + + #+(or avr avr-nano) + #" +/* + psymbol - prints any symbol from a symbol name to the specified stream +*/ +void psymbol (symbol_t name, pfun_t pfun) { + if (longnamep(name)) plispstr(name, pfun); + else { + uint16_t value = untwist(name); + if (value < PACKEDS) error2(PSTR("invalid symbol")); + else if (value >= BUILTINS) pbuiltin((builtin_t)(value-BUILTINS), pfun); + else pradix40(name, pfun); + } +}"# + + #+(or arm esp riscv) + #" +/* + psymbol - prints any symbol from a symbol name to the specified stream +*/ +void psymbol (symbol_t name, pfun_t pfun) { + if (longnamep(name)) plispstr(name, pfun); + else { + uint32_t value = untwist(name); + if (value < PACKEDS) error2(PSTR("invalid symbol")); + else if (value >= BUILTINS) pbuiltin((builtin_t)(value-BUILTINS), pfun); + else pradix40(name, pfun); + } +}"# + + #+avr-nano + #" +/* + pfstring - prints a string from flash memory to the specified stream +*/ +void pfstring (PGM_P s, pfun_t pfun) { + while (1) { + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + char c = *s++; + #else + char c = pgm_read_byte(s++); + #endif + if (c == 0) return; + pfun(c); + } +}"# + + #+avr + #" +/* + pfstring - prints a string from flash memory to the specified stream +*/ +void pfstring (PGM_P s, pfun_t pfun) { + while (1) { + char c = pgm_read_byte(s++); + if (c == 0) return; + pfun(c); + } +}"# + + #+(or arm riscv esp) + #" +/* + pfstring - prints a string from flash memory to the specified stream +*/ +void pfstring (const char *s, pfun_t pfun) { + while (1) { + char c = *s++; + if (c == 0) return; + pfun(c); + } +}"# + +#+msp430 + #" +/* + pfstring - prints a string from flash memory to the specified stream +*/ +void pfstring (PGM_P s, pfun_t pfun) { + intptr_t p = (intptr_t)s; + while (1) { + char c = pgm_read_byte(p++); + if (c == 0) return; + pfun(c); + } +}"# + + #+(or avr avr-nano) + #" +/* + pint - prints an integer in decimal to the specified stream +*/ +void pint (int i, pfun_t pfun) { + uint16_t j = i; + if (i<0) { pfun('-'); j=-i; } + pintbase(j, 10, pfun); +} + +/* + pintbase - prints an integer in base 'base' to the specified stream +*/ +void pintbase (uint16_t i, uint8_t base, pfun_t pfun) { + uint8_t lead = 0; uint16_t p = 10000; + if (base == 2) p = 0x8000; else if (base == 16) p = 0x1000; + for (uint16_t d=p; d>0; d=d/base) { + uint16_t j = i/d; + if (j!=0 || lead || d==1) { pfun((j<10) ? j+'0' : j+'W'); lead=1;} + i = i - j*d; + } +}"# + + #+(or arm esp riscv) + #" +/* + pint - prints an integer in decimal to the specified stream +*/ +void pint (int i, pfun_t pfun) { + uint32_t j = i; + if (i<0) { pfun('-'); j=-i; } + pintbase(j, 10, pfun); +} + +/* + pintbase - prints an integer in base 'base' to the specified stream +*/ +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; + } +}"# + + #+avr + #" +/* + pinthex2 - prints a two-digit hexadecimal number with leading zeros to the specified stream +*/ +void printhex2 (int i, pfun_t pfun) { + for (unsigned int d=0x10; d>0; d=d>>4) { + unsigned int j = i/d; + pfun((j<10) ? j+'0' : j+'W'); + i = i - j*d; + } +}"# + + #+(or riscv arm) + #" +/* + pinthex4 - prints a four-digit hexadecimal number with leading zeros to the specified stream +*/ +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(' '); +}"# + + #+float + #" +/* + pmantissa - prints the mantissa of a floating-point number to the specified stream +*/ +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= 0) { pfun('.'); point = true; } + mul = mul / 10; + } +} + +/* + pfloat - prints a floating-point number to the specified stream +*/ +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); + } +}"# + + #" +/* + pln - prints a newline to the specified stream +*/ +inline void pln (pfun_t pfun) { + pfun('\n'); +}"# + + #" +/* + pfl - prints a newline to the specified stream if a newline has not just been printed +*/ +void pfl (pfun_t pfun) { + if (LastPrint != '\n') pfun('\n'); +}"# + + #" +/* + plist - prints a list to the specified stream +*/ +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(')'); +}"# + + #+avr-nano + #" +/* + pstream - prints a stream name to the specified stream +*/ +void pstream (object *form, pfun_t pfun) { + pfun('<'); + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + PGM_P s = streamname[(form->integer)>>8]; + #else + PGM_P s = (char*)pgm_read_ptr(&streamname[(form->integer)>>8]); + #endif + pfstring(s, pfun); + pfstring(PSTR("-stream "), pfun); + pint(form->integer & 0xFF, pfun); + pfun('>'); +}"# + + #+avr + #" +/* + pstream - prints a stream name to the specified stream +*/ +void pstream (object *form, pfun_t pfun) { + pfun('<'); + PGM_P s = (char*)pgm_read_ptr(&streamname[(form->integer)>>8]); + pfstring(s, pfun); + pfstring(PSTR("-stream "), pfun); + pint(form->integer & 0xFF, pfun); + pfun('>'); +}"# + + #+(or arm riscv esp) + #" +/* + pstream - prints a stream name to the specified stream +*/ +void pstream (object *form, pfun_t pfun) { + pfun('<'); + pfstring(streamname[(form->integer)>>8], pfun); + pfstring(PSTR("-stream "), pfun); + pint(form->integer & 0xFF, pfun); + pfun('>'); +}"# + + #+avr-nano + #" +/* + printobject - prints any Lisp object to the specified stream +*/ +void printobject (object *form, pfun_t pfun) { + if (form == NULL) pfstring(PSTR("nil"), pfun); + else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring(PSTR(""), pfun); + else if (listp(form)) plist(form, pfun); + else if (integerp(form)) pint(form->integer, 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); + #if defined(CODESIZE) + else if (form->type == CODE) pfstring(PSTR("code"), pfun); + #endif + else if (streamp(form)) pstream(form, pfun); + else error2(PSTR("error in print")); +}"# + + #+avr + #" +/* + printobject - prints any Lisp object to the specified stream +*/ +void printobject (object *form, pfun_t pfun) { + if (form == NULL) pfstring(PSTR("nil"), pfun); + else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring(PSTR(""), pfun); + else if (listp(form)) plist(form, pfun); + else if (integerp(form)) pint(form->integer, 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); + #if defined(CODESIZE) + else if (form->type == CODE) pfstring(PSTR("code"), pfun); + #endif + else if (streamp(form)) pstream(form, pfun); + else error2(PSTR("error in print")); +}"# + + #+(or arm riscv) + #" +/* + printobject - prints any Lisp object to the specified stream +*/ +void printobject (object *form, pfun_t pfun) { + if (form == NULL) pfstring(PSTR("nil"), pfun); + else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring(PSTR(""), 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(PSTR("error in print")); +}"# + +; Has LCDSTREAM + #+msp430 + #" +/* + printobject - prints any Lisp object to the specified stream +*/ +void printobject (object *form, pfun_t pfun) { + if (form == NULL) pfstring(PSTR("nil"), pfun); + else if (listp(form) && issymbol(car(form), CLOSURE)) pfstring(PSTR(""), pfun); + else if (listp(form)) { + 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(')'); + } else if (integerp(form)) pint(form->integer, pfun); + else if (symbolp(form)) { if (form->name != NOTHING) pstring(symbolname(form->name), pfun); } + else if (characterp(form)) pcharacter(form->chars, pfun); + else if (stringp(form)) printstring(form, pfun); + else if (streamp(form)) { + pfun('<'); + if ((form->integer)>>8 == SPISTREAM) pfstring(PSTR("spi"), pfun); + else if ((form->integer)>>8 == I2CSTREAM) pfstring(PSTR("i2c"), pfun); + else if ((form->integer)>>8 == SDSTREAM) pfstring(PSTR("sd"), pfun); + else if ((form->integer)>>8 == STRINGSTREAM) pfstring(PSTR("string"), pfun); + else if ((form->integer)>>8 == LCDSTREAM) pfstring(PSTR("lcd"), pfun); + else pfstring(PSTR("serial"), pfun); + pfstring(PSTR("-stream "), pfun); + pint((form->integer) & 0xFF, pfun); + pfun('>'); + } else error2(PSTR("error in print")); +}"# + + #+esp + #" +/* + printobject - prints any Lisp object to the specified stream +*/ +void printobject (object *form, pfun_t pfun) { + if (form == NULL) pfstring(PSTR("nil"), pfun); + else if (listp(form) && isbuiltin(car(form), CLOSURE)) pfstring(PSTR(""), 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 (streamp(form)) pstream(form, pfun); + else error2(PSTR("error in print")); +}"# + +#" +/* + prin1object - prints any Lisp object to the specified stream escaping special characters +*/ +void prin1object (object *form, pfun_t pfun) { + flags_t temp = Flags; + clrflag(PRINTREADABLY); + printobject(form, pfun); + Flags = temp; +}"#)) + +(defparameter *read-functions* + + '( + + #+badge + #" +// For Lisp Badge +volatile int WritePtr = 0, ReadPtr = 0; +const int KybdBufSize = 333; // 42*8 - 3 +char KybdBuf[KybdBufSize]; +volatile uint8_t KybdAvailable = 0;"# + + #+avr-nano + #" +// Read functions + +/* + glibrary - reads a character from the Lisp Library +*/ +int glibrary () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + char c = LispLibrary[GlobalStringIndex++]; + #else + char c = pgm_read_byte(&LispLibrary[GlobalStringIndex++]); + #endif + return (c != 0) ? c : -1; // -1? +}"# + + #+avr + #" +// Read functions + +/* + glibrary - reads a character from the Lisp Library +*/ +int glibrary () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + char c = pgm_read_byte(&LispLibrary[GlobalStringIndex++]); + return (c != 0) ? c : -1; // -1? +}"# + + #+(or arm riscv esp) + #" +// Read functions + +/* + glibrary - reads a character from the Lisp Library +*/ +int glibrary () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + char c = LispLibrary[GlobalStringIndex++]; + return (c != 0) ? c : -1; // -1? +}"# + + #+(or msp430 badge) + #" +// Read functions + +/* + glibrary - reads a character from the Lisp Library +*/ +int glibrary () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + char c = pgm_read_byte(&LispLibrary[GlobalStringIndex++]); + return (c != 0) ? c : -1; // -1? +}"# + + +#" +/* + loadfromlibrary - reads and evaluates a form from the Lisp Library +*/ +void loadfromlibrary (object *env) { + GlobalStringIndex = 0; + object *line = read(glibrary); + while (line != NULL) { + protect(line); + eval(line, env); + unprotect(); + line = read(glibrary); + } +}"# + + #-badge + #" +// For line editor +const int TerminalWidth = 80; +volatile int WritePtr = 0, ReadPtr = 0, LastWritePtr = 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'); +} + +/* + Highlight - handles parenthesis highlighting with the line editor +*/ +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'); + } +} + +/* + processkey - handles keys in the line editor +*/ +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; LastWritePtr = WritePtr; + 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 (c == 9) { // tab or ctrl-I + for (int i = 0; i < LastWritePtr; i++) Serial.write(KybdBuf[i]); + WritePtr = LastWritePtr; + } 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; +}"# + + #+(and (or avr avr-nano) (not badge)) + #" +/* + gserial - gets a character from the serial port +*/ +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'; +#elif defined(CPU_ATmega328P) || defined(CPU_ATtiny3227) + while (!Serial.available()); + char temp = Serial.read(); + if (temp != '\n') pserial(temp); + return temp; +#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 +}"# + + #-(or avr avr-nano badge esp) + #" +/* + gserial - gets a character from the serial port +*/ +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 +}"# + + #+esp + #" +/* + gserial - gets a character from the serial port +*/ +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()) { delay(1); if (millis() - start > 1000) clrflag(NOECHO); } + char temp = Serial.read(); + if (temp != '\n' && !tstflag(NOECHO)) pserial(temp); + return temp; +#endif +}"# + + #+badge + #" +/* + gserial - gets a character from the serial port +*/ +int gserial () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + #if defined (serialmonitor) + unsigned long start = millis(); + while (!Serial.available() && !KybdAvailable) if (millis() - start > 1000) clrflag(NOECHO); + if (Serial.available()) { + char temp = Serial.read(); + if (temp != '\n' && !tstflag(NOECHO)) Serial.print(temp); // Don't print on Lisp Badge + return temp; + } else { + if (ReadPtr != WritePtr) { + char temp = KybdBuf[ReadPtr++]; + Serial.write(temp); + return temp; + } + KybdAvailable = 0; + WritePtr = 0; + return '\n'; + } + #else + while (!KybdAvailable); + if (ReadPtr != WritePtr) return KybdBuf[ReadPtr++]; + KybdAvailable = 0; + WritePtr = 0; + return '\n'; + #endif +}"# + + #+avr-nano + #" +/* + nextitem - reads the next token from the specified stream +*/ +object *nextitem (gfun_t gfun) { + int ch = gfun(); + while(issp(ch)) ch = gfun(); + + #if defined(CPU_ATmega328P) || defined(CPU_ATtiny3227) + if (ch == ';') { + while(ch != '(') ch = gfun(); + } + #else + if (ch == ';') { + do { ch = gfun(); if (ch == ';' || ch == '(') setflag(NOECHO); } + while(ch != '('); + } + #endif + 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; + if (ch == '.') return (object *)DOT; + + // Parse string + if (ch == '"') return readstring('"', true, gfun); + + // Parse symbol, character, or number + int index = 0, base = 10, sign = 1; + char buffer[BUFFERSIZE]; + int bufmax = BUFFERSIZE-1; // Max index + unsigned int result = 0; + if (ch == '+' || ch == '-') { + buffer[index++] = ch; + if (ch == '-') sign = -1; + ch = gfun(); + } + + // 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) || isbr(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 error2(PSTR("illegal character after #")); + ch = gfun(); + } + + int isnumber = (digitvalue(ch)= '1' && ch <= '9' && (gfun() & ~0x20) == 'A') return readarray(ch - '0', read(gfun)); + else error2(PSTR("illegal character after #")); + ch = gfun(); + } + + int isnumber = (digitvalue(ch)= '1' && ch <= '9' && (gfun() & ~0x20) == 'A') return readarray(ch - '0', read(gfun)); + else error2(PSTR("illegal character after #")); + ch = gfun(); + } + int valid; // 0=undecided, -1=invalid, +1=valid + if (ch == '.') valid = 0; else if (digitvalue(ch) ((unsigned int)INT_MAX+(1-sign)/2)) + error2(PSTR("Number out of range")); + return number(result*sign); + } else if (base == 0) { + if (index == 1) return character(buffer[0]); + PGM_P p = ControlCodes; char c = 0; + while (c < 33) { + #if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) + if (strcasecmp(buffer, p) == 0) return character(c); + p = p + strlen(p) + 1; c++; + #else + if (strcasecmp_P(buffer, p) == 0) return character(c); + p = p + strlen_P(p) + 1; c++; + #endif + } + if (index == 3) return character((buffer[0]*10+buffer[1])*10+buffer[2]-5328); + error2(PSTR("unknown character")); + }"# + + #+avr-nano + #" + builtin_t x = lookupbuiltin(buffer); + if (x == NIL) return nil; + if (x != ENDFUNCTIONS) return bsymbol(x); + if (index <= 3 && valid40(buffer)) return intern(twist(pack40(buffer))); + buffer[index+1] = '\0'; // For internlong + return internlong(buffer); +}"# + + #+avr + #" + builtin_t x = lookupbuiltin(buffer); + if (x == NIL) return nil; + if (x != ENDFUNCTIONS) return bsymbol(x); + if (index <= 3 && valid40(buffer)) return intern(twist(pack40(buffer))); + return internlong(buffer); +}"# + + #+(or arm riscv esp) + #" + buffer[index] = '\0'; + if (isbr(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(PSTR("unknown character")); + } + + builtin_t x = lookupbuiltin(buffer); + if (x == NIL) return nil; + if (x != ENDFUNCTIONS) return bsymbol(x); + if (index <= 6 && valid40(buffer)) return intern(twist(pack40(buffer))); + return internlong(buffer); +}"# + + #+msp430 + #" + buffer[index] = '\0'; + if (isbr(ch)) LastChar = ch; + + if (isnumber) { + if (base == 10 && result > ((unsigned int)INT_MAX+(1-sign)/2)) + error2(0, PSTR("Number out of range")); + return number(result*sign); + } else if (base == 0) { + if (index == 1) return character(buffer[0]); + PGM_P p = ControlCodes; char c = 0; + while (c < 33) { + if (strcasecmp_P(buffer, p) == 0) return character(c); + p = p + strlen_P(p) + 1; c++; + } + if (index == 3) return character((buffer[0]*10+buffer[1])*10+buffer[2]-5328); + error2(0, PSTR("unknown character")); + } + + int x = builtin(buffer); + if (x == NIL) return nil; + if (x < ENDFUNCTIONS) return newsymbol(x); + else if (index < 4 && valid40(buffer)) return newsymbol(pack40(buffer)); + else return newsymbol(longsymbol(buffer)); +}"# + + #" +/* + readrest - reads the remaining tokens from the specified stream +*/ +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(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; +}"# + + #" +/* + read - recursively reads a Lisp object from the stream gfun and returns it +*/ +object *read (gfun_t gfun) { + object *item = nextitem(gfun); + if (item == (object *)KET) error2(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; +}"#)) + + +(defparameter *setup1* '( + +#" +// Setup"# + +#" +/* + initenv - initialises the uLisp environment +*/ +void initenv () { + GlobalEnv = NULL; + tee = bsymbol(TEE); +}"# + +#+arm +#" +/* + initgfx - initialises the graphics +*/ +void initgfx () { + #if defined(gfxsupport) + #if defined(ARDUINO_PYBADGE_M4) || defined(ARDUINO_PYGAMER_M4) + tft.initR(INITR_BLACKTAB); + tft.setRotation(1); + pinMode(TFT_BACKLIGHT, OUTPUT); + digitalWrite(TFT_BACKLIGHT, HIGH); + tft.fillScreen(0); + #elif defined(ARDUINO_WIO_TERMINAL) + tft.init(); + tft.setRotation(3); + tft.fillScreen(TFT_BLACK); + #elif defined(ARDUINO_NRF52840_CLUE) + tft.init(240, 240); + tft.setRotation(1); + tft.fillScreen(0); + pinMode(34, OUTPUT); // Backlight + digitalWrite(34, HIGH); + #elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_2) + tft.init(135, 240); + pinMode(TFT_I2C_POWER, OUTPUT); + digitalWrite(TFT_I2C_POWER, HIGH); + tft.setRotation(1); + tft.fillScreen(ST77XX_BLACK); + pinMode(TFT_BACKLIGHT, OUTPUT); + digitalWrite(TFT_BACKLIGHT, HIGH); + #endif + #endif +}"# + +#+esp +#" +/* + initgfx - initialises the graphics +*/ +void initgfx () { + #if defined(gfxsupport) + tft.init(135, 240); + #if defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT) + pinMode(TFT_I2C_POWER, OUTPUT); + digitalWrite(TFT_I2C_POWER, HIGH); + tft.setRotation(3); + #else + tft.setRotation(1); + #endif + tft.fillScreen(ST77XX_BLACK); + pinMode(TFT_BACKLITE, OUTPUT); + digitalWrite(TFT_BACKLITE, HIGH); + #endif +}"# + +#+riscv +#" +/* + initgfx - initialises the graphics +*/ +void initgfx () { + #if defined(gfxsupport) + tft.begin(15000000, COLOR_BLACK); + tft.setRotation(2); + #endif +}"#)) + + +#+(and (or avr avr-nano) (not badge)) +(defparameter *setup2* #" +// Entry point from the Arduino IDE +void setup () { + Serial.begin(9600); + int start = millis(); + while ((millis() - start) < 5000) { if (Serial) break; } + initworkspace(); + initenv(); + initsleep(); + pfstring(PSTR("uLisp ~a "), pserial); pln(pserial); +}"#) + +#+badge +(defparameter *setup2* #" +// Entry point from the Arduino IDE +void setup () { + InitDisplay(); + InitKybd(); + #if defined (serialmonitor) + pinMode(8, INPUT_PULLUP); // RX0 + Serial.begin(9600); + int start = millis(); + while (millis() - start < 5000) { if (Serial) break; } + #endif + initworkspace(); + initenv(); + initsleep(); + pfstring(PSTR("uLisp ~a "), pserial); pln(pserial); +}"#) + +#+arm +(defparameter *setup2* #" +// Entry point from the Arduino IDE +void setup () { + Serial.begin(9600); + int start = millis(); + while ((millis() - start) < 5000) { if (Serial) break; } + initworkspace(); + initenv(); + initsleep(); + initgfx(); + pfstring(PSTR("uLisp ~a "), pserial); pln(pserial); +}"#) + +#+esp +(defparameter *setup2* #" +void setup () { + Serial.begin(9600); + int start = millis(); + while ((millis() - start) < 5000) { if (Serial) break; } + #if defined(BOARD_HAS_PSRAM) + if (!psramInit()) { Serial.print("the PSRAM couldn't be initialized"); for(;;); } + Workspace = (object*) ps_malloc(WORKSPACESIZE*8); + if (!Workspace) { Serial.print("the Workspace couldn't be allocated"); for(;;); } + #endif + int stackhere = 0; StackBottom = &stackhere; + initworkspace(); + initenv(); + initsleep(); + initgfx(); + pfstring(PSTR("uLisp ~a "), pserial); pln(pserial); +}"#) + +#+riscv +(defparameter *setup2* #" +// Entry point from the Arduino IDE +void setup () { + Serial.begin(9600); + int start = millis(); + while ((millis() - start) < 5000) { if (Serial) break; } + #if (WORKSPACESIZE > 80000) + Workspace = (object*) malloc(WORKSPACESIZE); + if (!Workspace) { Serial.print("the workspace couldn't be initialized"); for(;;); } + #endif + initworkspace(); + initenv(); + initsleep(); + initgfx(); + pfstring(PSTR("uLisp ~a "), pserial); pln(pserial); +}"#) + +(defparameter *repl* '( + +#" +// Read/Evaluate/Print loop"# + + #+avr-nano + #" +/* + repl - the Lisp Read/Evaluate/Print loop +*/ +void repl (object *env) { + for (;;) { + RandomSeed = micros(); + #if defined(printfreespace) + if (!tstflag(NOECHO)) gc(NULL, env); + pint(Freespace+1, pserial); + #endif + if (BreakLevel) { + pfstring(PSTR(" : "), pserial); + pint(BreakLevel, pserial); + } + pserial('>'); pserial(' '); + Context = NIL; + object *line = read(gserial); + if (BreakLevel && line == nil) { pln(pserial); return; } + if (line == (object *)KET) error2(PSTR("unmatched right bracket")); + protect(line); + pfl(pserial); + line = eval(line, env); + pfl(pserial); + printobject(line, pserial); + unprotect(); + pfl(pserial); + pln(pserial); + } +}"# + + + #+avr + #" +/* + repl - the Lisp Read/Evaluate/Print loop +*/ +void repl (object *env) { + for (;;) { + RandomSeed = micros(); + #if defined(printfreespace) + if (!tstflag(NOECHO)) gc(NULL, env); + pint(Freespace+1, pserial); + #endif + if (BreakLevel) { + pfstring(PSTR(" : "), pserial); + pint(BreakLevel, pserial); + } + pserial('>'); pserial(' '); + Context = NIL; + object *line = read(gserial); + // Break handling + if (BreakLevel) { + if (line == nil || line == bsymbol(COLONC)) { + pln(pserial); return; + } else if (line == bsymbol(COLONA)) { + pln(pserial); pln(pserial); + GCStack = NULL; + longjmp(*handler, 1); + } else if (line == bsymbol(COLONB)) { + pln(pserial); printbacktrace(); + line = bsymbol(NOTHING); + } + } + if (line == (object *)KET) error2(PSTR("unmatched right bracket")); + protect(line); + pfl(pserial); + line = eval(line, env); + pfl(pserial); + printobject(line, pserial); + unprotect(); + pfl(pserial); + pln(pserial); + } +}"# + +#+arm +#" +/* + repl - the Lisp Read/Evaluate/Print loop +*/ +void repl (object *env) { + for (;;) { + randomSeed(micros()); + #if defined(printfreespace) + if (!tstflag(NOECHO)) gc(NULL, env); + pint(Freespace+1, pserial); + #endif + if (BreakLevel) { + pfstring(PSTR(" : "), pserial); + pint(BreakLevel, pserial); + } + pserial('>'); pserial(' '); + Context = NIL; + object *line = read(gserial); + #if defined(CPU_NRF52840) + Serial.flush(); + #endif + // Break handling + if (BreakLevel) { + if (line == nil || line == bsymbol(COLONC)) { + pln(pserial); return; + } else if (line == bsymbol(COLONA)) { + pln(pserial); pln(pserial); + GCStack = NULL; + longjmp(*handler, 1); + } else if (line == bsymbol(COLONB)) { + pln(pserial); printbacktrace(); + line = bsymbol(NOTHING); + } + } + if (line == (object *)KET) error2(PSTR("unmatched right bracket")); + protect(line); + pfl(pserial); + line = eval(line, env); + pfl(pserial); + printobject(line, pserial); + unprotect(); + pfl(pserial); + pln(pserial); + } +}"# + +#+(or esp riscv) +#" +/* + repl - the Lisp Read/Evaluate/Print loop +*/ +void repl (object *env) { + for (;;) { + randomSeed(micros()); + #if defined(printfreespace) + if (!tstflag(NOECHO)) gc(NULL, env); + pint(Freespace+1, pserial); + #endif + if (BreakLevel) { + pfstring(PSTR(" : "), pserial); + pint(BreakLevel, pserial); + } + pserial('>'); pserial(' '); + Context = NIL; + object *line = read(gserial); + // Break handling + if (BreakLevel) { + if (line == nil || line == bsymbol(COLONC)) { + pln(pserial); return; + } else if (line == bsymbol(COLONA)) { + pln(pserial); pln(pserial); + GCStack = NULL; + longjmp(*handler, 1); + } else if (line == bsymbol(COLONB)) { + pln(pserial); printbacktrace(); + line = bsymbol(NOTHING); + } + } + if (line == (object *)KET) error2(PSTR("unmatched right bracket")); + protect(line); + pfl(pserial); + line = eval(line, env); + pfl(pserial); + printobject(line, pserial); + unprotect(); + pfl(pserial); + pln(pserial); + } +}"#)) + +(defparameter *loop* '( + +#-errors +#" +/* + loop - the Arduino IDE main execution loop +*/ +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(); + } + ulisperror(); + repl(NULL); +}"# + +#+errors +#" +/* + loop - the Arduino IDE main execution loop +*/ +void loop () { + if (!setjmp(toplevel_handler)) { + #if defined(resetautorun) + volatile int autorun = 12; // Fudge to keep code size the same + #else + volatile int autorun = 13; + #endif + if (autorun == 12) autorunimage(); + } + ulisperror(); + repl(NULL); +}"# + +#+avr-nano +#" +void ulisperror () { + // Come here after error + #if defined (serialmonitor) + delay(100); while (Serial.available()) Serial.read(); + #endif + clrflag(NOESC); BreakLevel = 0; + for (int i=0; icar) +#define cdr(x) (((object *) (x))->cdr) + +#define first(x) car(x) +#define rest(x) cdr(x) +#define second(x) first(rest(x)) +#define cddr(x) cdr(cdr(x)) +#define third(x) first(cddr(x)) + +#define push(x, y) ((y) = cons((x),(y))) +#define pop(y) ((y) = cdr(y)) + +#define protect(y) push((y), GCStack) +#define unprotect() pop(GCStack) + +#define integerp(x) ((x) != NULL && (x)->type == NUMBER) +#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 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 isbr(x) (x == ')' || x == '(' || x == '"' || x == '#' || x == '\'') +#define fntype(x) (getminmax((uint16_t)(x))>>6) +#define longsymbolp(x) (((x)->name & 0x03) == 0) +#define longnamep(x) (((x) & 0x03) == 0) +#define twist(x) ((uint16_t)((x)<<2) | (((x) & 0xC000)>>14)) +#define untwist(x) (((x)>>2 & 0x3FFF) | ((x) & 0x03)<<14) +#define arraysize(x) (sizeof(x) / sizeof(x[0])) +#define stringifyX(x) #x +#define stringify(x) stringifyX(x) +#define PACKEDS 17600 +#define BUILTINS 64000 +#define ENDFUNCTIONS 1536 + +#define SDCARD_SS_PIN 10 + +#if defined(CPU_ATmega4809) || defined(CPU_ATtiny3227) +#define PROGMEM +#define PSTR(s) (s) +#endif"# + +#+avr +#" +// C Macros + +#define nil NULL +#define car(x) (((object *) (x))->car) +#define cdr(x) (((object *) (x))->cdr) + +#define first(x) car(x) +#define rest(x) cdr(x) +#define second(x) first(rest(x)) +#define cddr(x) cdr(cdr(x)) +#define third(x) first(cddr(x)) + +#define push(x, y) ((y) = cons((x),(y))) +#define pop(y) ((y) = cdr(y)) + +#define protect(y) push((y), GCStack) +#define unprotect() pop(GCStack) + +#define integerp(x) ((x) != NULL && (x)->type == NUMBER) +#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 isbr(x) (x == ')' || x == '(' || x == '"' || x == '#' || x == '\'') +#define fntype(x) (getminmax((uint16_t)(x))>>6) +#define longsymbolp(x) (((x)->name & 0x03) == 0) +#define longnamep(x) (((x) & 0x03) == 0) +#define twist(x) ((uint16_t)((x)<<2) | (((x) & 0xC000)>>14)) +#define untwist(x) (((x)>>2 & 0x3FFF) | ((x) & 0x03)<<14) +#define arraysize(x) (sizeof(x) / sizeof(x[0])) +#define stringifyX(x) #x +#define stringify(x) stringifyX(x) +#define PACKEDS 17600 +#define BUILTINS 64000 +#define ENDFUNCTIONS 1536 + +// Code marker stores start and end of code block (max 256 bytes) +#define startblock(x) ((x->integer) & 0xFF) +#define endblock(x) ((x->integer) >> 8 & 0xFF) + +#define SDCARD_SS_PIN 10"# + +#+(or arm riscv esp) +#" +// C Macros + +#define nil NULL +#define car(x) (((object *) (x))->car) +#define cdr(x) (((object *) (x))->cdr) + +#define first(x) car(x) +#define rest(x) cdr(x) +#define second(x) first(rest(x)) +#define cddr(x) cdr(cdr(x)) +#define third(x) first(cddr(x)) + +#define push(x, y) ((y) = cons((x),(y))) +#define pop(y) ((y) = cdr(y)) + +#define protect(y) push((y), GCStack) +#define unprotect() pop(GCStack) + +#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 isbr(x) (x == ')' || x == '(' || x == '"' || x == '#' || x == '\'') +#define fntype(x) (getminmax((uint16_t)(x))>>6) +#define longsymbolp(x) (((x)->name & 0x03) == 0) +#define longnamep(x) (((x) & 0x03) == 0) +#define twist(x) ((uint32_t)((x)<<2) | (((x) & 0xC0000000)>>30)) +#define untwist(x) (((x)>>2 & 0x3FFFFFFF) | ((x) & 0x03)<<30) +#define arraysize(x) (sizeof(x) / sizeof(x[0])) +#define stringifyX(x) #x +#define stringify(x) stringifyX(x) +#define PACKEDS 0x43238000 +#define BUILTINS 0xF4240000 +#define ENDFUNCTIONS 0x0BDC0000"# + +#+(or arm riscv) +#" +// Code marker stores start and end of code block +#define startblock(x) ((x->integer) & 0xFFFF) +#define endblock(x) ((x->integer) >> 16 & 0xFFFF)"#)) + +(defparameter *constants* +'( +#+avr-nano +#" +// Constants + +const int TRACEMAX = 3; // Number of traced functions +enum type { ZZERO=0, SYMBOL=2, CODE=4, NUMBER=6, STREAM=8, CHARACTER=10, STRING=12, PAIR=14 }; // STRING and PAIR must be last +enum token { UNUSED, BRA, KET, QUO, DOT }; +enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM, STRINGSTREAM }; +enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS }; + +// 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"; +PGM_P const streamname[] PROGMEM = {serialstream, i2cstream, spistream, sdstream, stringstream};"# + +#+avr +#" +// Constants + +#define TRACEMAX 3 // Maximum number of traced functions +enum type { ZZERO=0, SYMBOL=2, CODE=4, NUMBER=6, STREAM=8, CHARACTER=10, ARRAY=12, STRING=14, PAIR=16 }; // ARRAY STRING and PAIR must be last +enum token { UNUSED, BRA, KET, QUO, DOT }; +enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM, STRINGSTREAM }; +enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS }; + +// 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"; +PGM_P const streamname[] PROGMEM = {serialstream, i2cstream, spistream, sdstream, stringstream};"# + +#+arm +#" +// Constants + +#define TRACEMAX 3 // Maximum 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, WIFISTREAM, STRINGSTREAM, GFXSTREAM }; +enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS }; + +// 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 wifistream[] PROGMEM = "wifi"; +const char stringstream[] PROGMEM = "string"; +const char gfxstream[] PROGMEM = "gfx"; +const char *const streamname[] PROGMEM = {serialstream, i2cstream, spistream, sdstream, wifistream, stringstream, gfxstream};"# + +#+esp +#" +// Constants + +#define TRACEMAX 3 // Maximum 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, WIFISTREAM, STRINGSTREAM, GFXSTREAM }; +enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS }; + +// 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 wifistream[] PROGMEM = "wifi"; +const char stringstream[] PROGMEM = "string"; +const char gfxstream[] PROGMEM = "gfx"; +PGM_P const streamname[] PROGMEM = {serialstream, i2cstream, spistream, sdstream, wifistream, stringstream, gfxstream};"# + +#+riscv +#" +// Constants + +#define TRACEMAX 3 // Maximum 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 }; // STRING and PAIR must be last +enum token { UNUSED, BRA, KET, QUO, DOT }; +enum stream { SERIALSTREAM, I2CSTREAM, SPISTREAM, SDSTREAM, STRINGSTREAM, GFXSTREAM }; +enum fntypes_t { OTHER_FORMS, TAIL_FORMS, FUNCTIONS, SPECIAL_FORMS }; + +// 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};"#)) + +#+avr-nano +(defparameter *typedefs* #" +// Typedefs + +typedef uint16_t symbol_t; +typedef uint16_t builtin_t; +typedef uint16_t chars_t; + +typedef struct sobject { + union { + struct { + sobject *car; + sobject *cdr; + }; + struct { + unsigned int type; + union { + symbol_t name; + int integer; + chars_t chars; // For strings + }; + }; + }; +} 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);"#) + +#+avr +(defparameter *typedefs* #" +// Typedefs + +typedef uint16_t symbol_t; +typedef uint16_t builtin_t; +typedef uint16_t chars_t; + +typedef struct sobject { + union { + struct { + sobject *car; + sobject *cdr; + }; + struct { + unsigned int type; + union { + symbol_t name; + int integer; + chars_t chars; // For strings + }; + }; + }; +} 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; + const char *doc; +} tbl_entry_t; + +typedef int (*gfun_t)(); +typedef void (*pfun_t)(char);"#) + +#+msp430 +(defparameter *typedefs* #" +// Typedefs + +typedef unsigned int 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 + }; + }; + }; +} object; + +typedef object *(*fn_ptr_type)(object *, object *); +typedef void (*mapfun_t)(object *, object **); + +typedef const struct { + PGM_P string; + fn_ptr_type fptr; + uint8_t funtype: 2; + uint8_t minargs: 3; + uint8_t maxargs: 3; + const char *doc; +} tbl_entry_t; + +typedef int (*gfun_t)(); +typedef void (*pfun_t)(char); + +typedef uint16_t builtin_t;"#) + +#+arm +(defparameter *typedefs* #" +// Typedefs + +typedef uint32_t symbol_t; +typedef uint32_t builtin_t; +typedef uint32_t chars_t; + +typedef struct sobject { + union { + struct { + sobject *car; + sobject *cdr; + }; + struct { + unsigned int type; + union { + symbol_t name; + int integer; + chars_t 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; + const char *doc; +} tbl_entry_t; + +typedef int (*gfun_t)(); +typedef void (*pfun_t)(char);"#) + +#+riscv +(defparameter *typedefs* #" +// Typedefs + +typedef uint32_t symbol_t; +typedef uint32_t builtin_t; +typedef uint32_t chars_t; + +typedef struct sobject { + union { + struct { + sobject *car; + sobject *cdr; + }; + struct { + uintptr_t type; + union { + symbol_t name; + int integer; + chars_t 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; + const char *doc; +} tbl_entry_t; + +typedef int (*gfun_t)(); +typedef void (*pfun_t)(char); +typedef int PinMode;"#) + +#+esp +(defparameter *typedefs* #" +// Typedefs + +typedef uint32_t symbol_t; +typedef uint32_t builtin_t; +typedef uint32_t chars_t; + +typedef struct sobject { + union { + struct { + sobject *car; + sobject *cdr; + }; + struct { + unsigned int type; + union { + symbol_t name; + int integer; + chars_t chars; // For strings + float single_float; + }; + }; + }; +} object; + +typedef object *(*fn_ptr_type)(object *, object *); +typedef void (*mapfun_t)(object *, object **); + +typedef const struct { + PGM_P string; + fn_ptr_type fptr; + uint8_t minmax; + const char *doc; +} tbl_entry_t; + +typedef int (*gfun_t)(); +typedef void (*pfun_t)(char);"#) + +(defparameter *global-variables* + '( + +#+avr-nano +#" +// Global variables + +uint8_t FLAG __attribute__ ((section (".noinit"))); + +object Workspace[WORKSPACESIZE] OBJECTALIGNED;"# + +#+avr +#" +// Global variables + +uint8_t FLAG __attribute__ ((section (".noinit"))); + +object Workspace[WORKSPACESIZE] OBJECTALIGNED; +#if defined(CODESIZE) +uint8_t MyCode[CODESIZE] WORDALIGNED; // Must be even +#endif"# + + +#+arm +#" +// Global variables + +object Workspace[WORKSPACESIZE] WORDALIGNED MEMBANK; +#if defined(CODESIZE) +RAMFUNC uint8_t MyCode[CODESIZE] WORDALIGNED; +#endif"# + +#+esp +#" +// Global variables + +#if defined(BOARD_HAS_PSRAM) +object *Workspace WORDALIGNED; +#else +object Workspace[WORKSPACESIZE] WORDALIGNED; +#endif"# + +#+riscv +#" +// Global variables + +#if (WORKSPACESIZE > 80000) +object *Workspace WORDALIGNED; +#else +object Workspace[WORKSPACESIZE] WORDALIGNED; +#endif +uint8_t MyCode[CODESIZE] WORDALIGNED;"# + +#+avr-nano +#" +jmp_buf exception; +unsigned int Freespace = 0; +object *Freelist; +unsigned int I2Ccount; +unsigned int TraceFn[TRACEMAX]; +unsigned int TraceDepth[TRACEMAX]; +builtin_t Context; + +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; +uint16_t RandomSeed;"# + +#+avr +#" +jmp_buf toplevel_handler; +jmp_buf *handler = &toplevel_handler; +unsigned int Freespace = 0; +object *Freelist; +unsigned int I2Ccount; +unsigned int TraceFn[TRACEMAX]; +unsigned int TraceDepth[TRACEMAX]; +builtin_t Context; +#define BACKTRACESIZE 8 +uint8_t TraceStart = 0, TraceTop = 0; +symbol_t Backtrace[BACKTRACESIZE]; + +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; +uint16_t RandomSeed;"# + +#+(or arm riscv) +#" +jmp_buf toplevel_handler; +jmp_buf *handler = &toplevel_handler; +unsigned int Freespace = 0; +object *Freelist; +unsigned int I2Ccount; +unsigned int TraceFn[TRACEMAX]; +unsigned int TraceDepth[TRACEMAX]; +builtin_t Context; +#define BACKTRACESIZE 8 +uint8_t TraceStart = 0, TraceTop = 0; +symbol_t Backtrace[BACKTRACESIZE]; + +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;"# + +#+esp +#" +jmp_buf toplevel_handler; +jmp_buf *handler = &toplevel_handler; +unsigned int Freespace = 0; +object *Freelist; +unsigned int I2Ccount; +unsigned int TraceFn[TRACEMAX]; +unsigned int TraceDepth[TRACEMAX]; +builtin_t Context; +#define BACKTRACESIZE 8 +uint8_t TraceStart = 0, TraceTop = 0; +symbol_t Backtrace[BACKTRACESIZE]; + +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; +void* StackBottom;"# + + +#-errors +#" +// Flags +enum flag { PRINTREADABLY, RETURNFLAG, ESCAPE, EXITEDITOR, LIBRARYLOADED, NOESC, NOECHO, BACKTRACE }; +typedef uint8_t flags_t; +volatile flags_t Flags = 1<integer, base, pcount); + return PrintCount; +} + +/* + quoted - tests whether an object is quoted +*/ +bool quoted (object *obj) { + return (consp(obj) && car(obj) != NULL && car(obj)->name == sym(QUOTE) && consp(cdr(obj)) && cddr(obj) == NULL); +} + +/* + subwidth - returns the space left from w after printing object +*/ +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); +} + +/* + subwidth - returns the space left from w after printing a list +*/ +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; +}"# + +#-gfx +#" +/* + superprint - handles pretty-printing +*/ +void superprint (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('\''); + superprint(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); } + superprint(car(form), lm+extra, pfun); + form = cdr(form); + } + pfun(')'); + } +}"# + +#+gfx +#" +/* + superprint - handles pretty-printing +*/ +void superprint (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('\''); + superprint(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); } + superprint(car(form), lm+extra, pfun); + form = cdr(form); + } + pfun(')'); + } +}"# + +#" +/* + edit - the Lisp tree editor + Steps through a function definition, editing it a bit at a time, using single-key editing commands. +*/ +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('?'); + } +}"#)) + \ No newline at end of file diff --git a/builder/riscv.lisp b/builder/riscv.lisp new file mode 100644 index 0000000..762b383 --- /dev/null +++ b/builder/riscv.lisp @@ -0,0 +1,133 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +; RISC-V + +(defparameter *title-riscv* +#"/* uLisp RISC-V Release ~a - www.ulisp.com + David Johnson-Davies - www.technoblogy.com - ~a + + Licensed under the MIT license: https://opensource.org/licenses/MIT +*/"#) + +(defparameter *header-riscv* #" +// 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 +// #define extensions + +// Includes + +// #include "LispLibrary.h" +#include +#include +#include +#include + +#if defined(gfxsupport) +#include +SPIClass spi_(SPI0); // MUST be SPI0 for Maix series on board LCD +Sipeed_ST7789 tft(320, 240, spi_); +#endif + +#if defined(sdcardsupport) +#include +#define SDSIZE 172 +#else +#define SDSIZE 0 +#endif"#) + +(defparameter *workspace-riscv* #" +// Platform specific settings + +#define WORDALIGNED __attribute__((aligned (8))) +#define BUFFERSIZE 36 // Number of bits+4 +#define RAMFUNC __attribute__ ((section (".ramfunctions"))) + +#if defined(BOARD_SIPEED_MAIX_DUINO) + #define WORKSPACESIZE 500000 /* Objects (16*bytes) */ + #define CODESIZE 512 /* Bytes */ + #define STACKDIFF 4096 + #define CPU_K210 + +#elif defined(BOARD_SIPEED_MAIX_BIT) + #define WORKSPACESIZE 500000 /* Objects (16*bytes) */ + #define CODESIZE 512 /* Bytes */ + #define STACKDIFF 4096 + #define CPU_K210 + +#elif defined(BOARD_SIPEED_MAIX_ONE_DOCK) + #define WORKSPACESIZE 500000 /* Objects (16*bytes) */ + #define CODESIZE 512 /* Bytes */ + #define STACKDIFF 4096 + #define CPU_K210 + +#else +#error "Board not supported!" +#endif"#) + +(defparameter *check-pins-riscv* #" +// Check pins + +void checkanalogread (int pin) { +#if defined(BOARD_SIPEED_MAIX_DUINO) + if (!((pin>=32 && pin<=36) || pin==39)) error(invalidpin, number(pin)); +#endif +} + +void checkanalogwrite (int pin) { +#if defined(BOARD_SIPEED_MAIX_DUINO) + if (!(pin>=0 && pin<=13)) error(invalidpin, number(pin)); +#elif defined(BOARD_SIPEED_MAIX_BIT) + if (!(pin>=0 && pin<=35)) error(invalidpin, number(pin)); +#elif defined(BOARD_SIPEED_MAIX_ONE_DOCK) + if (!(pin>=0 && pin<=47)) error(invalidpin, number(pin)); +#endif +}"#) + +(defparameter *note-riscv* #" +// 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(BOARD_SIPEED_MAIX_DUINO) + int oct = octave + note/12; + int prescaler = 8 - oct; + if (prescaler<0 || prescaler>8) error(PSTR("octave out of range"), number(oct)); + tone(pin, scale[note%12]>>prescaler); +#endif +} + +void nonote (int pin) { +#if defined(BOARD_SIPEED_MAIX_DUINO) + noTone(pin); +#endif +}"#) + +(defparameter *sleep-riscv* #" +// Sleep + +void initsleep () { } + +void doze (int secs) { + delay(1000 * secs); +}"#) + +(defparameter *keywords-riscv* + '((nil + ((NIL LED_BUILTIN) + (DIGITALWRITE HIGH LOW) + (PINMODE INPUT INPUT_PULLUP INPUT_PULLDOWN OUTPUT))))) diff --git a/builder/saveload.lisp b/builder/saveload.lisp new file mode 100644 index 0000000..319c7d4 --- /dev/null +++ b/builder/saveload.lisp @@ -0,0 +1,1582 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +(defparameter *compactimage* '( + +#" +// Compact image"# + +#+avr-nano +#" +/* + movepointer - corrects pointers to an object that has moved from 'from' to 'to' +*/ +void movepointer (object *from, object *to) { + for (int i=0; itype) & ~MARKBIT; + if (marked(obj) && (type >= STRING || 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; itype) & ~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); + } + } + } + } +}"# + +#-avr-nano +#" +/* + movepointer - Corrects pointers to an object that has been moved from 'from' to 'to'. + Only need to scan addresses below 'from' as there are no accessible objects above that. +*/ +void movepointer (object *from, object *to) { + uintptr_t limit = ((uintptr_t)(from) - (uintptr_t)(Workspace))/sizeof(uintptr_t); + for (uintptr_t i=0; itype) & ~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 (uintptr_t i=0; itype) & ~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); + } + } + } + } +}"# + +#" +/* + compactimage - Marks all accessible objects. Moves the last marked object down to the first free space gap, correcting + pointers by calling movepointer(). Then repeats until there are no more gaps. +*/ +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; +}"#)) + +(defparameter *make-filename* '( + +#-(or esp arm) +#" +// Make SD card filename + +char *MakeFilename (object *arg, char *buffer) { + int max = BUFFERSIZE-1; + int i = 0; + do { + char c = nthchar(arg, i); + if (c == '\0') break; + buffer[i++] = c; + } while (i>8 & 0xFF); +} + +int SDReadInt (File file) { + uint8_t b0 = file.read(); uint8_t b1 = file.read(); + return b0 | b1<<8; +} +#elif defined(FLASHWRITESIZE) +#if defined (CPU_AVR64DD28) +// save-image area is the 6144 bytes (12 x 512-byte pages) from 0xE600 to 0xFE00 +// Leave 512 bytes at the top for DxCore +const uint32_t BaseAddress = 0xE600; +uint8_t FlashCheck() { + return Flash.checkWritable(); +} + +void FlashWriteInt (uint32_t *addr, int data) { + if (((*addr) & 0x1FF) == 0) Flash.erasePage(BaseAddress + ((*addr) & 0xFE00)); + Flash.writeWord(BaseAddress + *addr, data); + (*addr)++; (*addr)++; +} + +void FlashEndWrite (uint32_t *addr) { + (void) addr; +} + +uint8_t FlashReadByte (uint32_t *addr) { + return Flash.readByte(BaseAddress + (*addr)++); +} + +int FlashReadInt (uint32_t *addr) { + int data = Flash.readWord(BaseAddress + *addr); + (*addr)++; (*addr)++; + return data; +} +#endif +#else +void EEPROMWriteInt (unsigned int *addr, int data) { + EEPROM.write((*addr)++, data & 0xFF); EEPROM.write((*addr)++, data>>8 & 0xFF); +} + +int EEPROMReadInt (unsigned int *addr) { + uint8_t b0 = EEPROM.read((*addr)++); uint8_t b1 = EEPROM.read((*addr)++); + return b0 | b1<<8; +} +#endif + +/* + saveimage - saves an image of the workspace to the persistent storage selected for the platform. +*/ +unsigned int saveimage (object *arg) { +#if defined(sdcardsupport) + unsigned int imagesize = compactimage(&arg); + SDBegin(); + File file; + if (stringp(arg)) { + char buffer[BUFFERSIZE]; + file = SD.open(MakeFilename(arg, buffer), O_RDWR | O_CREAT | O_TRUNC); + if (!file) error2(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(PSTR("problem saving to SD card")); + } + else error(invalidarg, arg); + SDWriteInt(file, (uintptr_t)arg); + SDWriteInt(file, imagesize); + SDWriteInt(file, (uintptr_t)GlobalEnv); + SDWriteInt(file, (uintptr_t)GCStack); + #if defined(CODESIZE) + for (int i=0; i FLASHWRITESIZE) error(PSTR("image too large"), number(imagesize)); + uint32_t addr = 0; + FlashWriteInt(&addr, (uintptr_t)arg); + FlashWriteInt(&addr, imagesize); + FlashWriteInt(&addr, (uintptr_t)GlobalEnv); + FlashWriteInt(&addr, (uintptr_t)GCStack); + #if defined(CODESIZE) + for (int i=0; i EEPROMSIZE) error(PSTR("image too large"), number(imagesize)); + unsigned int addr = 0; + EEPROMWriteInt(&addr, (unsigned int)arg); + EEPROMWriteInt(&addr, imagesize); + EEPROMWriteInt(&addr, (unsigned int)GlobalEnv); + EEPROMWriteInt(&addr, (unsigned int)GCStack); + for (unsigned int i=0; i>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(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 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++) FlashReadByte(); + devID = FlashReadByte(); + digitalWrite(ssel, HIGH); + return (devID >= 0x14 && devID <= 0x17); // true = found correct device +} + +void FlashBeginWrite (uint32_t *addr, uint32_t bytes) { + *addr = 0; + uint8_t blocks = (bytes+65535)/65536; + // Erase 64K + for (uint8_t b=0; b>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(CPUFLASH) +// 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; +} +#elif defined(EEPROMFLASH) + +bool FlashCheck() { + return (EEPROM.length() == FLASHSIZE); +} + +void FlashBeginWrite(uint32_t *addr, uint32_t bytes) { + (void) bytes; + *addr = 0; +} + +void FlashWrite32 (uint32_t *addr, uint32_t data) { + EEPROM.put(*addr, data); + (*addr) = (*addr) + 4; +} + +void FlashEndWrite (uint32_t *addr) { + (void) addr; +} + +void FlashBeginRead(uint32_t *addr) { + *addr = 0; +} + +uint32_t FlashRead32 (uint32_t *addr) { + uint32_t data; + EEPROM.get(*addr, data); + (*addr) = (*addr) + 4; + return data; +} + +void FlashEndRead (uint32_t *addr) { + (void) addr; +} +#endif + +/* + saveimage - saves an image of the workspace to the persistent storage selected for the platform. +*/ +int saveimage (object *arg) { +#if defined(sdcardsupport) + unsigned int imagesize = compactimage(&arg); + SDBegin(); + File file; + if (stringp(arg)) { + char buffer[BUFFERSIZE]; + file = SD.open(MakeFilename(arg, buffer), O_RDWR | O_CREAT | O_TRUNC); + if (!file) error2(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(PSTR("problem saving to SD card")); + } else error(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 FLASHSIZE) error(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>8 & 0xFF); + file.write(data>>16 & 0xFF); file.write(data>>24 & 0xFF); +} + +int SDReadInt (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(PSTR("not enough room")); +} + +uint32_t FSRead32 (File file) { + union { uint32_t data; uint8_t u8[4]; }; + file.read(u8, 4); + return data; +} +#else +void EpromWriteInt(int *addr, uintptr_t data) { + EEPROM.write((*addr)++, data & 0xFF); EEPROM.write((*addr)++, data>>8 & 0xFF); + EEPROM.write((*addr)++, data>>16 & 0xFF); EEPROM.write((*addr)++, data>>24 & 0xFF); +} + +int EpromReadInt (int *addr) { + uint8_t b0 = EEPROM.read((*addr)++); uint8_t b1 = EEPROM.read((*addr)++); + uint8_t b2 = EEPROM.read((*addr)++); uint8_t b3 = EEPROM.read((*addr)++); + return b0 | b1<<8 | b2<<16 | b3<<24; +} +#endif + +/* + saveimage - saves an image of the workspace to the persistent storage selected for the platform. +*/ +unsigned int saveimage (object *arg) { +#if defined(sdcardsupport) + unsigned int imagesize = compactimage(&arg); + SDBegin(); + File file; + if (stringp(arg)) { + char buffer[BUFFERSIZE]; + file = SD.open(MakeFilename(arg, buffer), FILE_WRITE); + if (!file) error2(PSTR("problem saving to SD card or invalid filename")); + arg = NULL; + } else if (arg == NULL || listp(arg)) { + file = SD.open("/ULISP.IMG", FILE_WRITE); + if (!file) error2(PSTR("problem saving to SD card")); + } else error(invalidarg, arg); + SDWriteInt(file, (uintptr_t)arg); + SDWriteInt(file, imagesize); + SDWriteInt(file, (uintptr_t)GlobalEnv); + SDWriteInt(file, (uintptr_t)GCStack); + for (unsigned int i=0; i bytesavailable) error("image too large by", number(bytesneeded - bytesavailable)); + File file; + if (stringp(arg)) { + char buffer[BUFFERSIZE]; + file = LittleFS.open(MakeFilename(arg, buffer), "w"); + if (!file) error2(PSTR("problem saving to LittleFS or invalid filename")); + arg = NULL; + } else if (arg == NULL || listp(arg)) { + file = LittleFS.open("/ULISP.IMG", "w"); + if (!file) error2(PSTR("problem saving to LittleFS")); + } else error(invalidarg, arg); + FSWrite32(file, (uintptr_t)arg); + FSWrite32(file, imagesize); + FSWrite32(file, (uintptr_t)GlobalEnv); + FSWrite32(file, (uintptr_t)GCStack); + for (unsigned int i=0; i EEPROMSIZE) error("image too large by", number(bytesneeded - EEPROMSIZE)); + EEPROM.begin(EEPROMSIZE); + int addr = 0; + EpromWriteInt(&addr, (uintptr_t)arg); + EpromWriteInt(&addr, imagesize); + EpromWriteInt(&addr, (uintptr_t)GlobalEnv); + EpromWriteInt(&addr, (uintptr_t)GCStack); + for (unsigned int i=0; i BUFFERSIZE + SDWriteInt(file, (uintptr_t)SymbolTop); + int SymbolUsed = SymbolTop - SymbolTable; + for (int i=0; i BUFFERSIZE + SymbolTop = (char *)SDReadInt(file); + int SymbolUsed = SymbolTop - SymbolTable; + for (int i=0; i>8 & 0xFF); + file.write(data>>16 & 0xFF); file.write(data>>24 & 0xFF); +} +#else +void FlashSetup () { + FLASH_Unlock(); + uint16_t Status; + for (int page = Eeprom; page < 0x8020000; page = page + 0x400) { + Status = FLASH_ErasePage(page); + if (Status != FLASH_COMPLETE) error2(PSTR("flash erase failed")); + } +} + +void FlashWrite16 (unsigned int *addr, uint16_t data) { + uint16_t Status = FLASH_ProgramHalfWord((*addr) + Eeprom, data); + if (Status != FLASH_COMPLETE) error2(PSTR("flash write failed")); + (*addr) = (*addr) + 2; +} + +void FlashWriteInt (unsigned int *addr, int data) { + FlashWrite16(addr, data & 0xFFFF); FlashWrite16(addr, data>>16 & 0xFFFF); +} +#endif + +/* + saveimage - saves an image of the workspace to the persistent storage selected for the platform. +*/ +int saveimage (object *arg) { + unsigned int imagesize = compactimage(&arg); +#if defined(sdcardsupport) + File file; + if (stringp(arg)) { + file = SD.open(MakeFilename(arg), O_RDWR | O_CREAT | O_TRUNC); + arg = NULL; + } else if (arg == NULL || listp(arg)) file = SD.open("ULISP.IMG", O_RDWR | O_CREAT | O_TRUNC); + else error3(SAVEIMAGE, PSTR("illegal argument")); + if (!file) error(PSTR("Problem saving to SD card")); + SDWriteInt(file, (uintptr_t)arg); + SDWriteInt(file, imagesize); + SDWriteInt(file, (uintptr_t)GlobalEnv); + SDWriteInt(file, (uintptr_t)GCStack); + #if SYMBOLTABLESIZE > BUFFERSIZE + SDWriteInt(file, (uintptr_t)SymbolTop); + for (int i=0; i EEPROMSIZE) { + pfstring(PSTR("Error: image too large: "), pserial); + pint(imagesize, pserial); pln(pserial); + GCStack = NULL; + longjmp(exception, 1); + } + unsigned int addr = 0; + FlashWriteInt(&addr, (uintptr_t)arg); + FlashWriteInt(&addr, imagesize); + FlashWriteInt(&addr, (uintptr_t)GlobalEnv); + FlashWriteInt(&addr, (uintptr_t)GCStack); + #if SYMBOLTABLESIZE > BUFFERSIZE + FlashWriteInt(&addr, (uintptr_t)SymbolTop); + for (int i=0; i BUFFERSIZE + SymbolTop = (char *)SDReadInt(file); + for (int i=0; i BUFFERSIZE + SymbolTop = (char *)FlashReadInt(&addr); + for (int i=0; i>8 & 0xFF; + } + #endif + for (unsigned int i=0; i>8 & 0xFF); +} +#elif defined(__MSP430F5529__) +#include "MspFlash.h" +const int segmentsize = 0x200; // 512 +unsigned char image[13*segmentsize] PERSIST; // We need 12*512 in the middle of this +#define FLASH SEGPTR(image) +#elif defined(__MSP430FR5969__) || defined(__MSP430FR5994__) || defined(__MSP430FR6989__) +struct image_struct { + object *eval; + unsigned int datasize; + object *globalenv; + object *gcstack; + #if SYMBOLTABLESIZE > BUFFERSIZE + char *symboltop; + char table[SYMBOLTABLESIZE]; + #endif + object data[IMAGEDATASIZE]; +}; + +struct image_struct image PERSIST; +#endif + +/* + saveimage - saves an image of the workspace to the persistent storage selected for the platform. +*/ +int saveimage (object *arg) { + unsigned int imagesize = compactimage(&arg); +#if defined(sdcardsupport) + File file; + if (stringp(arg)) { + file = SD.open(MakeFilename(arg), O_RDWR | O_CREAT | O_TRUNC); + arg = NULL; + } else if (arg == NULL || listp(arg)) file = SD.open("/ULISP.IMG", O_RDWR | O_CREAT | O_TRUNC); + else error3(SAVEIMAGE, PSTR("illegal argument")); + if (!file) error(PSTR("Problem saving to SD card")); + SDWriteInt(file, (uintptr_t)arg); + SDWriteInt(file, imagesize); + SDWriteInt(file, (uintptr_t)GlobalEnv); + SDWriteInt(file, (uintptr_t)GCStack); + #if SYMBOLTABLESIZE > BUFFERSIZE + SDWriteInt(file, (uintptr_t)SymbolTop); + for (int i=0; i IMAGEDATASIZE) { + pfstring(PSTR("Error: image too large: "), pserial); + pint(imagesize, pserial); pln(pserial); + GCStack = NULL; + longjmp(exception, 1); + } + // Erase flash + for (int i=0; i<12; i++) Flash.erase(FLASH + i*segmentsize); + unsigned char *workstart = FLASH+8; + Flash.write(FLASH, (unsigned char*)&imagesize, 2); + Flash.write(FLASH+2, (unsigned char*)&arg, 2); + Flash.write(FLASH+4, (unsigned char*)&GlobalEnv, 2); + Flash.write(FLASH+6, (unsigned char*)&GCStack, 2); + #if SYMBOLTABLESIZE > BUFFERSIZE + Flash.write(FLASH+8, (unsigned char*)&SymbolTop, 2); + Flash.write(FLASH+10, (unsigned char*)SymbolTable, SYMBOLTABLESIZE); + workstart = FLASH + SYMBOLTABLESIZE + 10; + #endif + Flash.write(workstart, (unsigned char*)Workspace, imagesize*4); + return imagesize; +#elif defined(__MSP430FR5969__) || defined(__MSP430FR5994__) || defined(__MSP430FR6989__) + if (!(arg == NULL || listp(arg))) error3(SAVEIMAGE, PSTR(" illegal argument")); + int bytesneeded = imagesize*4 + SYMBOLTABLESIZE + 10; + if (imagesize > IMAGEDATASIZE) { + pfstring(PSTR("Error: image too large: "), pserial); + pint(imagesize, pserial); pln(pserial); + GCStack = NULL; + longjmp(exception, 1); + } + image.datasize = imagesize; + image.eval = arg; + image.globalenv = GlobalEnv; + image.gcstack = GCStack; + #if SYMBOLTABLESIZE > BUFFERSIZE + image.symboltop = SymbolTop; + for (int i=0; i BUFFERSIZE + SymbolTop = (char *)SDReadInt(file); + for (int i=0; i BUFFERSIZE + Flash.read(FLASH+8, (unsigned char*)&SymbolTop, 2); + Flash.read(FLASH+10, (unsigned char*)SymbolTable, SYMBOLTABLESIZE); + workstart = FLASH + SYMBOLTABLESIZE + 10; + #endif + Flash.read(workstart, (unsigned char*)Workspace, imagesize*4); + gc(NULL, NULL); + return imagesize; +#elif defined(__MSP430FR5969__) || defined(__MSP430FR5994__) || defined(__MSP430FR6989__) + unsigned int imagesize; + imagesize = image.datasize; + GlobalEnv = image.globalenv; + GCStack = image.gcstack; + #if SYMBOLTABLESIZE > BUFFERSIZE + SymbolTop = image.symboltop; + for (int i=0; ibegin(); +} + +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? + // Release pins + port->end(); +}"# + +#-(or avr avr-nano badge arm esp) +#" +// I2C interface for one port, using Arduino Wire + +void I2Cinit (bool enablePullup) { + (void) enablePullup; + Wire.begin(); +} + +int I2Cread () { + return Wire.read(); +} + +void I2Cwrite (uint8_t data) { + Wire.write(data); +} + +bool I2Cstart (uint8_t address, uint8_t read) { + int ok = true; + if (read == 0) { + Wire.beginTransmission(address); + ok = (Wire.endTransmission(true) == 0); + Wire.beginTransmission(address); + } + else Wire.requestFrom(address, I2Ccount); + return ok; +} + +bool I2Crestart (uint8_t address, uint8_t read) { + int error = (Wire.endTransmission(false) != 0); + if (read == 0) Wire.beginTransmission(address); + else Wire.requestFrom(address, I2Ccount); + return error ? false : true; +} + +void I2Cstop (uint8_t read) { + if (read == 0) Wire.endTransmission(); // Check for error? +}"# + +#+badge +#" +// I2C interface for AVR platforms, uses much less RAM than Arduino Wire + +uint8_t const TWI_SDA_PIN = 17; +uint8_t const TWI_SCL_PIN = 16; + +uint32_t const F_TWI = 400000L; // Hardware I2C clock in Hz +uint8_t const TWSR_MTX_DATA_ACK = 0x28; +uint8_t const TWSR_MTX_ADR_ACK = 0x18; +uint8_t const TWSR_MRX_ADR_ACK = 0x40; +uint8_t const TWSR_START = 0x08; +uint8_t const TWSR_REP_START = 0x10; +uint8_t const I2C_READ = 1; +uint8_t const I2C_WRITE = 0; + +void I2Cinit (bool enablePullup) { + TWSR = 0; // no prescaler + TWBR = (F_CPU/F_TWI - 16)/2; // set bit rate factor + if (enablePullup) { + digitalWrite(TWI_SDA_PIN, HIGH); + digitalWrite(TWI_SCL_PIN, HIGH); + } +} + +int I2Cread () { + if (I2Ccount != 0) I2Ccount--; + TWCR = 1<>8; address = stream & 0xFF; + } + if (streamtype == I2CSTREAM) gfun = (gfun_t)I2Cread; + else if (streamtype == SPISTREAM) gfun = spiread; + else if (streamtype == SERIALSTREAM) { + if (address == 0) gfun = gserial; + #if defined(CPU_ATmega1284P) || defined(CPU_AVR128DX48) + else if (address == 1) gfun = serial1read; + #elif defined(CPU_ATmega2560) + else if (address == 1) gfun = serial1read; + else if (address == 2) gfun = serial2read; + else if (address == 3) gfun = serial3read; + #endif + } + #if defined(sdcardsupport) + else if (streamtype == SDSTREAM) gfun = (gfun_t)SDread; + #endif + else error2(unknownstreamtype); + return gfun; +} + +inline void spiwrite (char c) { SPI.transfer(c); } +#if defined(CPU_ATmega1284P) || defined(CPU_AVR128DX48) +inline void serial1write (char c) { Serial1.write(c); } +#elif defined(CPU_ATmega2560) +inline void serial1write (char c) { Serial1.write(c); } +inline void serial2write (char c) { Serial2.write(c); } +inline void serial3write (char c) { Serial3.write(c); } +#endif +#if defined(sdcardsupport) +inline void SDwrite (char c) { int w = SDpfile.write(c); if (w != 1) { Context = NIL; error2(PSTR("failed to write to file")); } } +#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) pfun = (pfun_t)I2Cwrite; + else if (streamtype == SPISTREAM) pfun = spiwrite; + else if (streamtype == SERIALSTREAM) { + if (address == 0) pfun = pserial; + #if defined(CPU_ATmega1284P) || defined(CPU_AVR128DX48) + else if (address == 1) pfun = serial1write; + #elif defined(CPU_ATmega2560) + else if (address == 1) pfun = serial1write; + else if (address == 2) pfun = serial2write; + else if (address == 3) pfun = serial3write; + #endif + } + else if (streamtype == STRINGSTREAM) { + pfun = pstr; + } + #if defined(sdcardsupport) + else if (streamtype == SDSTREAM) pfun = (pfun_t)SDwrite; + #endif + else error2(unknownstreamtype); + return pfun; +}"# + +#+arm +#" +// 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) \ + || defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_RASPBERRY_PI_PICO_2) \ + || defined(ARDUINO_PIMORONI_PICO_PLUS_2) +#define ULISP_SPI1 +#endif +#if defined(ARDUINO_WIO_TERMINAL) || defined(ARDUINO_BBC_MICROBIT_V2) \ + || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(MAX32620) \ + || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \ + || defined(ARDUINO_ADAFRUIT_QTPY_RP2040) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) \ + || defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_PIMORONI_PICO_PLUS_2) \ + || defined(ARDUINO_GRAND_CENTRAL_M4) || defined(ARDUINO_NRF52840_CIRCUITPLAY) +#define ULISP_I2C1 +#endif +#if defined(ARDUINO_SAM_DUE) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) +#define ULISP_SERIAL3 +#elif defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_RASPBERRY_PI_PICO_W) \ + || defined(ARDUINO_RASPBERRY_PI_PICO_2) || defined(ARDUINO_PIMORONI_PICO_PLUS_2) +#define ULISP_SERIAL2 +#elif !defined(CPU_NRF51822) && !defined(CPU_NRF52833) && !defined(ARDUINO_FEATHER_F405) +#define ULISP_SERIAL1 +#endif +#if defined(ARDUINO_RASPBERRY_PI_PICO_W) || defined(ARDUINO_UNOWIFIR4) +#define ULISP_WIFI +#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 + +#if defined(ULISP_WIFI) +WiFiClient client; +WiFiServer server(80); + +inline int WiFiread () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + while (!client.available()) testescape(); + return client.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("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 + if (false); + #endif + else error("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 + #if defined(ULISP_WIFI) + else if (streamtype == WIFISTREAM) gfun = (gfun_t)WiFiread; + #endif + else error2("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(ULISP_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(ULISP_SERIAL2) +inline void serial2write (char c) { Serial2.write(c); } +inline void serial1write (char c) { Serial1.write(c); } +#elif defined(ULISP_SERIAL1) +inline void serial1write (char c) { Serial1.write(c); } +#endif +#if defined(sdcardsupport) +inline void SDwrite (char c) { SDpfile.write(uint8_t(c)); } // Fix for RP2040 +#endif +#if defined(ULISP_WIFI) +inline void WiFiwrite (char c) { client.write(c); } +#endif +#if defined(gfxsupport) +inline void gfxwrite (char c) { tft.write(c); } +#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(ULISP_SERIAL3) + else if (address == 1) pfun = serial1write; + else if (address == 2) pfun = serial2write; + else if (address == 3) pfun = serial3write; + #elif defined(ULISP_SERIAL2) + else if (address == 1) pfun = serial1write; + else if (address == 2) pfun = serial2write; + #elif defined(ULISP_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 + #if defined(ULISP_WIFI) + else if (streamtype == WIFISTREAM) pfun = (pfun_t)WiFiwrite; + #endif + else error2("unknown stream type"); + return pfun; +}"# + +#+esp +#" +// Streams + +// Simplify board differences +#if defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) +#define ULISP_I2C1 +#endif + +inline int spiread () { return SPI.transfer(0); } +inline int i2cread () { return I2Cread(&Wire); } +#if defined(ULISP_I2C1) +inline int i2c1read () { return I2Cread(&Wire1); } +#endif +inline int serial1read () { while (!Serial1.available()) testescape(); return Serial1.read(); } +#if defined(sdcardsupport) +File SDpfile, SDgfile; +inline int SDread () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + return SDgfile.read(); +} +#endif + +WiFiClient client; +WiFiServer server(80); + +inline int WiFiread () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + while (!client.available()) testescape(); + return client.read(); +} + +void serialbegin (int address, int baud) { + if (address == 1) Serial1.begin((long)baud*100); + else error(PSTR("port not supported"), number(address)); +} + +void serialend (int address) { + if (address == 1) {Serial1.flush(); Serial1.end(); } + else error(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) gfun = spiread; + else if (streamtype == SERIALSTREAM) { + if (address == 0) gfun = gserial; + else if (address == 1) gfun = serial1read; + } + #if defined(sdcardsupport) + else if (streamtype == SDSTREAM) gfun = (gfun_t)SDread; + #endif + else if (streamtype == WIFISTREAM) gfun = (gfun_t)WiFiread; + else error2(PSTR("unknown stream type")); + return gfun; +} + +inline void spiwrite (char c) { SPI.transfer(c); } +inline void i2cwrite (char c) { I2Cwrite(&Wire, c); } +#if defined(ULISP_I2C1) +inline void i2c1write (char c) { I2Cwrite(&Wire1, c); } +#endif +inline void serial1write (char c) { Serial1.write(c); } +inline void WiFiwrite (char c) { client.write(c); } +#if defined(sdcardsupport) +inline void SDwrite (char c) { SDpfile.write(c); } +#endif +#if defined(gfxsupport) +inline void gfxwrite (char c) { tft.write(c); } +#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) pfun = spiwrite; + else if (streamtype == SERIALSTREAM) { + if (address == 0) pfun = pserial; + else if (address == 1) pfun = serial1write; + } + 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 if (streamtype == WIFISTREAM) pfun = (pfun_t)WiFiwrite; + else error2(PSTR("unknown stream type")); + return pfun; +}"# + +#+riscv +#" +// Streams + +inline int spiread () { return SPI.transfer(0); } +#if defined(BOARD_SIPEED_MAIX_DUINO) +inline int serial1read () { while (!Serial1.available()) testescape(); return Serial1.read(); } +inline int serial2read () { while (!Serial2.available()) testescape(); return Serial2.read(); } +inline int serial3read () { while (!Serial3.available()) testescape(); return Serial3.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(BOARD_SIPEED_MAIX_DUINO) + 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); + else error(PSTR("port not supported"), number(address)); + #endif +} + +void serialend (int address) { + #if defined(BOARD_SIPEED_MAIX_DUINO) + if (address == 1) {Serial1.flush(); Serial1.end(); } + else if (address == 2) {Serial2.flush(); Serial2.end(); } + else if (address == 3) {Serial3.flush(); Serial3.end(); } + #endif +} + +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) gfun = (gfun_t)I2Cread; + else if (streamtype == SPISTREAM) { + if (address < 128) gfun = spiread; + } + else if (streamtype == SERIALSTREAM) { + if (address == 0) gfun = gserial; + #if defined(BOARD_SIPEED_MAIX_DUINO) + else if (address == 1) gfun = serial1read; + else if (address == 2) gfun = serial2read; + else if (address == 3) gfun = serial3read; + #endif + } + #if defined(sdcardsupport) + else if (streamtype == SDSTREAM) gfun = (gfun_t)SDread; + #endif + else error2(PSTR("unknown stream type")); + return gfun; +} + +inline void spiwrite (char c) { SPI.transfer(c); } +#if defined(BOARD_SIPEED_MAIX_DUINO) +inline void serial1write (char c) { Serial1.write(c); } +inline void serial2write (char c) { Serial2.write(c); } +inline void serial3write (char c) { Serial3.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); } +#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) pfun = (pfun_t)I2Cwrite; + else if (streamtype == SPISTREAM) { + if (address < 128) pfun = spiwrite; + } + else if (streamtype == SERIALSTREAM) { + if (address == 0) pfun = pserial; + #if defined(BOARD_SIPEED_MAIX_DUINO) + else if (address == 1) pfun = serial1write; + else if (address == 2) pfun = serial2write; + else if (address == 3) pfun = serial3write; + #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(PSTR("unknown stream type")); + return pfun; +}"#)) \ No newline at end of file diff --git a/builder/utilities.lisp b/builder/utilities.lisp new file mode 100644 index 0000000..b2b571c --- /dev/null +++ b/builder/utilities.lisp @@ -0,0 +1,2633 @@ +;;;-*- Mode: Lisp; Package: cl-user -*- + +(in-package :cl-user) + +(defparameter *error-handling* '( + +#" +// Error handling"# + +#-avr-nano +#" +int modbacktrace (int n) { + return (n+BACKTRACESIZE) % BACKTRACESIZE; +}"# + +#+avr +#" +/* + printbacktrace - prints a call backtrace for error messages and break. +*/ +void printbacktrace () { + if (TraceStart != TraceTop) pserial('['); + int tracesize = modbacktrace(TraceTop-TraceStart); + for (int i=1; i<=tracesize; i++) { + printsymbol(symbol(Backtrace[modbacktrace(TraceTop-i)]), pserial); + if (i!=tracesize) pfstring(PSTR(" <- "), pserial); + } + if (TraceStart != TraceTop) pserial(']'); +}"# + +#+avr-nano +#" +/* + errorsub - used by all the error routines. + Prints: "Error: 'fname' string", where fname is the name of the Lisp function in which the error occurred. +*/ +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); +}"# + +#+avr +#" +/* + errorsub - used by all the error routines. + Prints: "Error: 'fname' string", where fname is the name of the Lisp function in which the error occurred. +*/ +void errorsub (symbol_t fname, PGM_P string) { + pfl(pserial); pfstring(PSTR("Error"), pserial); + if (TraceStart != TraceTop) pserial(' '); + printbacktrace(); + pfstring(PSTR(": "), pserial); + if (fname != sym(NIL)) { + pserial('\''); + psymbol(fname, pserial); + pserial('\''); pserial(' '); + } + pfstring(string, pserial); +}"# + +#-(or avr avr-nano) +#" +/* + printbacktrace - prints a call backtrace for error messages and break. +*/ +void printbacktrace () { + if (TraceStart != TraceTop) pserial('['); + int tracesize = modbacktrace(TraceTop-TraceStart); + for (int i=1; i<=tracesize; i++) { + printsymbol(symbol(Backtrace[modbacktrace(TraceTop-i)]), pserial); + if (i!=tracesize) pfstring(" <- ", pserial); + } + if (TraceStart != TraceTop) pserial(']'); +} + +/* + errorsub - used by all the error routines. + Prints: "Error: 'fname' string", where fname is the name of the Lisp function in which the error occurred. +*/ +void errorsub (symbol_t fname, const char *string) { + pfl(pserial); pfstring("Error", pserial); + if (TraceStart != TraceTop) pserial(' '); + printbacktrace(); + pfstring(": ", pserial); + if (fname != sym(NIL)) { + pserial('\''); + psymbol(fname, pserial); + pserial('\''); pserial(' '); + } + pfstring(string, pserial); +}"# + +#-errors +#" +void errorend () { pln(pserial); GCStack = NULL; longjmp(exception, 1); }"# + +#+errors +#" +void errorend () { GCStack = NULL; longjmp(*handler, 1); }"# + +#-errors +#" +/* + errorsym - prints an error message and reenters the REPL. + Prints: "Error: 'fname' string: symbol", where fname is the name of the user Lisp function in which the error occurred, + and symbol is the object generating the error. +*/ +void errorsym (symbol_t fname, PGM_P string, object *symbol) { + errorsub(fname, string); + pserial(':'); pserial(' '); + printobject(symbol, pserial); + errorend(); +} + +/* + errorsym2 - prints an error message and reenters the REPL. + Prints: "Error: 'fname' string", where fname is the name of the user Lisp function in which the error occurred. +*/ +void errorsym2 (symbol_t fname, PGM_P string) { + errorsub(fname, string); + errorend(); +}"# + +#+errors +#" +/* + errorsym - prints an error message and reenters the REPL. + Prints: "Error: 'fname' string: symbol", where fname is the name of the user Lisp function in which the error occurred, + and symbol is the object generating the error. +*/ +void errorsym (symbol_t fname, PGM_P string, object *symbol) { + if (!tstflag(MUFFLEERRORS)) { + errorsub(fname, string); + pserial(':'); pserial(' '); + printobject(symbol, pserial); + pln(pserial); + } + errorend(); +} + +/* + errorsym2 - prints an error message and reenters the REPL. + Prints: "Error: 'fname' string", where fname is the name of the user Lisp function in which the error occurred. +*/ +void errorsym2 (symbol_t fname, PGM_P string) { + if (!tstflag(MUFFLEERRORS)) { + errorsub(fname, string); + pln(pserial); + } + errorend(); +}"# + +#" +/* + error - prints an error message and reenters the REPL. + Prints: "Error: 'Context' string: symbol", where Context is the name of the built-in Lisp function in which the error occurred, + and symbol is the object generating the error. +*/ +void error (PGM_P string, object *symbol) { + errorsym(sym(Context), string, symbol); +} + +/* + error2 - prints an error message and reenters the REPL. + Prints: "Error: 'Context' string", where Context is the name of the built-in Lisp function in which the error occurred. +*/ +void error2 (PGM_P string) { + errorsym2(sym(Context), string); +}"# + +#" +/* + formaterr - displays a format error with a ^ pointing to the error +*/ +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(string); + pln(pserial); + errorend(); +}"# + +#" +// 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 illegalfn[] PROGMEM = "illegal function"; +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";"#)) + +(defparameter *setup-workspace* #" +// Set up workspace + +/* + initworkspace - initialises the workspace into a linked list of free objects +*/ +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++; + } +} + +/* + myalloc - returns the first object from the linked list of free objects +*/ +object *myalloc () { + if (Freespace == 0) { Context = NIL; error2(PSTR("no room")); } + object *temp = Freelist; + Freelist = cdr(Freelist); + Freespace--; + return temp; +} + +/* + myfree - adds obj to the linked list of free objects. + inline makes gc significantly faster +*/ +inline void myfree (object *obj) { + car(obj) = NULL; + cdr(obj) = Freelist; + Freelist = obj; + Freespace++; +}"#) + +(defparameter *make-objects* + + '(#" +// Make each type of object + +/* + number - make an integer object with value n and return it +*/ +object *number (int n) { + object *ptr = myalloc(); + ptr->type = NUMBER; + ptr->integer = n; + return ptr; +}"# + + #+float + #" +/* + makefloat - make a floating point object with value f and return it +*/ +object *makefloat (float f) { + object *ptr = myalloc(); + ptr->type = FLOAT; + ptr->single_float = f; + return ptr; +}"# + + #" +/* + character - make a character object with value c and return it +*/ +object *character (uint8_t c) { + object *ptr = myalloc(); + ptr->type = CHARACTER; + ptr->chars = c; + return ptr; +}"# + + #" +/* + cons - make a cons with arg1 and arg2 return it +*/ +object *cons (object *arg1, object *arg2) { + object *ptr = myalloc(); + ptr->car = arg1; + ptr->cdr = arg2; + return ptr; +}"# + + #" +/* + symbol - make a symbol object with value name and return it +*/ +object *symbol (symbol_t name) { + object *ptr = myalloc(); + ptr->type = SYMBOL; + ptr->name = name; + return ptr; +}"# + + #" +/* + bsymbol - make a built-in symbol +*/ +inline object *bsymbol (builtin_t name) { + return intern(twist(name+BUILTINS)); +}"# + + #+(or avr arm riscv) + #" +/* + codehead - make a code header object with value entry and return it +*/ +object *codehead (int entry) { + object *ptr = myalloc(); + ptr->type = CODE; + ptr->integer = entry; + return ptr; +}"# + + #+(or avr avr-nano) +#" +/* + intern - looks through the workspace for an existing occurrence of symbol name and returns it, + otherwise calls symbol(name) to create a new symbol. +*/ +object *intern (symbol_t name) { + for (int i=0; itype == SYMBOL && obj->name == name) return obj; + } + return symbol(name); +}"# + + #+(or esp arm) +#" +/* + intern - unless PSRAM: looks through the workspace for an existing occurrence of symbol name and returns it, + otherwise calls symbol(name) to create a new symbol. +*/ +object *intern (symbol_t name) { + #if !defined(BOARD_HAS_PSRAM) + for (int i=0; itype == SYMBOL && obj->name == name) return obj; + } + #endif + return symbol(name); +}"# + + #+riscv +#" +/* + intern - unless large-RAM looks through the workspace for an existing occurrence of symbol name and returns it, + otherwise calls symbol(name) to create a new symbol. +*/ +object *intern (symbol_t name) { + #if (WORKSPACESIZE <= 80000) + for (int i=0; itype == SYMBOL && obj->name == name) return obj; + } + #endif + return symbol(name); +}"# + + #+avr-nano + #" +/* + eqsymbols - compares the long string/symbol obj with the string in buffer. +*/ +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]<<8 | buffer[i+1])) return false; + arg = car(arg); + i = i + 2; + } + return true; +}"# + + #+avr + #" +/* + eqsymbols - compares the long string/symbol obj with the string in buffer. +*/ +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) return false; + chars_t test = 0; int shift = 8; + for (int j=0; j<2; j++, i++) { + if (buffer[i] == 0) break; + test = test | buffer[i]<chars != test) return false; + arg = car(arg); + } + return true; +}"# + + #+(or arm esp riscv) + #" +/* + eqsymbols - compares the long string/symbol obj with the string in buffer. +*/ +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) return false; + chars_t test = 0; int shift = 24; + for (int j=0; j<4; j++, i++) { + if (buffer[i] == 0) break; + test = test | buffer[i]<chars != test) return false; + arg = car(arg); + } + return true; +}"# + + #+(or avr avr-nano) + #" +/* + internlong - looks through the workspace for an existing occurrence of the long symbol in buffer and returns it, + otherwise calls lispstring(buffer) to create a new symbol. +*/ +object *internlong (char *buffer) { + for (int i=0; itype == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) return obj; + } + object *obj = lispstring(buffer); + obj->type = SYMBOL; + return obj; +}"# + + #+(or arm esp) + #" +/* + internlong - unless PSRAM looks through the workspace for an existing occurrence of the long symbol in buffer and returns it, + otherwise calls lispstring(buffer) to create a new symbol. +*/ +object *internlong (char *buffer) { + #if !defined(BOARD_HAS_PSRAM) + for (int i=0; itype == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) return obj; + } + #endif + object *obj = lispstring(buffer); + obj->type = SYMBOL; + return obj; +}"# + + #+riscv + #" +/* + internlong - unless large-RAM looks through the workspace for an existing occurrence of the long symbol in buffer and returns it, + otherwise calls lispstring(buffer) to create a new symbol. +*/ +object *internlong (char *buffer) { + #if (WORKSPACESIZE <= 80000) + for (int i=0; itype == SYMBOL && longsymbolp(obj) && eqsymbols(obj, buffer)) return obj; + } + #endif + object *obj = lispstring(buffer); + obj->type = SYMBOL; + return obj; +}"# + + #" +/* + stream - makes a stream object defined by streamtype and address, and returns it +*/ +object *stream (uint8_t streamtype, uint8_t address) { + object *ptr = myalloc(); + ptr->type = STREAM; + ptr->integer = streamtype<<8 | address; + return ptr; +}"# + + #" +/* + newstring - makes an empty string object and returns it +*/ +object *newstring () { + object *ptr = myalloc(); + ptr->type = STRING; + ptr->chars = 0; + return ptr; +}"#)) + +(defparameter *garbage-collection* '( + +#" +// Garbage collection"# + +#+avr-nano +#" +/* + markobject - recursively marks reachable objects, starting from obj +*/ +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 == STRING) || (type == SYMBOL && longsymbolp(obj))) { + obj = cdr(obj); + while (obj != NULL) { + arg = car(obj); + mark(obj); + obj = arg; + } + } +}"# + +#-avr-nano +#" +/* + markobject - recursively marks reachable objects, starting from obj +*/ +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; + } + } +}"# + +#" +/* + sweep - goes through the workspace freeing objects that have not been marked, + and unmarks marked objects +*/ +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); + } +} + +/* + gc - performs garbage collection by calling markobject() on each of the pointers to objects in use, + followed by sweep() to free unused objects. +*/ +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 +}"#)) + +#+avr +(defparameter *feature-list* #" +// Features + +const char arrays[] PROGMEM = ":arrays"; +const char doc[] PROGMEM = ":documentation"; +const char machinecode[] PROGMEM = ":machine-code"; +const char errorhandling[] PROGMEM = ":error-handling"; +const char sdcard[] PROGMEM = ":sd-card"; + +/* + copyprogmemstring - copy a PROGMEM string to RAM. +*/ +char *copyprogmemstring (PGM_P s, char *buffer) { + int max = BUFFERSIZE-1; + int i = 0; + do { + char c = pgm_read_byte(s++); + buffer[i++] = c; + if (c == 0) break; + } while (itype; + return type >= PAIR || type == ZZERO; +} + +/* + atom - implements Lisp atom +*/ +#define atom(x) (!consp(x)) + +/* + listp - implements Lisp listp +*/ +bool listp (object *x) { + if (x == NULL) return true; + unsigned int type = x->type; + return type >= PAIR || type == ZZERO; +} + +/* + improperp - tests whether x is an improper list +*/ +#define improperp(x) (!listp(x)) + +object *quote (object *arg) { + return cons(bsymbol(QUOTE), cons(arg,NULL)); +}"# + + #" +// Radix 40 encoding + +/* + builtin - converts a symbol name to builtin +*/ +builtin_t builtin (symbol_t name) { + return (builtin_t)(untwist(name) - BUILTINS); +} + +/* + sym - converts a builtin to a symbol name +*/ +symbol_t sym (builtin_t x) { + return twist(x + BUILTINS); +} + +/* + toradix40 - returns a number from 0 to 39 if the character can be encoded, or -1 otherwise. +*/ +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 +} + +/* + fromradix40 - returns the character encoded by the number n. +*/ +char fromradix40 (char n) { + if (n >= 1 && n <= 10) 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; +}"# + + #+avr-nano + #" +/* + pack40 - packs three radix40-encoded characters from buffer into a 16-bit number and returns it. +*/ +uint16_t pack40 (char *buffer) { + return (((toradix40(buffer[0]) * 40) + toradix40(buffer[1])) * 40 + toradix40(buffer[2])); +}"# + + #+(or avr msp430 badge) + #" +/* + pack40 - packs three radix40-encoded characters from buffer into a 16-bit number and returns it. +*/ +uint32_t pack40 (char *buffer) { + int x = 0, j = 0; + for (int i=0; i<3; i++) { + x = x * 40 + toradix40(buffer[j]); + if (buffer[j] != 0) j++; + } + return x; +}"# + + #+(or arm esp riscv) + #" +/* + pack40 - packs six radix40-encoded characters from buffer into a 32-bit number and returns it. +*/ +uint32_t pack40 (char *buffer) { + int x = 0, j = 0; + for (int i=0; i<6; i++) { + x = x * 40 + toradix40(buffer[j]); + if (buffer[j] != 0) j++; + } + return x; +}"# + + #+avr-nano + #" +/* + valid40 - returns true if the symbol in buffer can be encoded as three radix40-encoded characters. +*/ +bool valid40 (char *buffer) { + return (toradix40(buffer[0]) >= 11 && toradix40(buffer[1]) >= 0 && toradix40(buffer[2]) >= 0); +}"# + + #+(or avr msp430 badge) + #" +/* + valid40 - returns true if the symbol in buffer can be encoded as three radix40-encoded characters. +*/ +bool valid40 (char *buffer) { + int t = 11; + for (int i=0; i<3; i++) { + if (toradix40(buffer[i]) < t) return false; + if (buffer[i] == 0) break; + t = 0; + } + return true; +}"# + + #+(or arm esp riscv) + #" +/* + valid40 - returns true if the symbol in buffer can be encoded as six radix40-encoded characters. +*/ +bool valid40 (char *buffer) { + int t = 11; + for (int i=0; i<6; i++) { + if (toradix40(buffer[i]) < t) return false; + if (buffer[i] == 0) break; + t = 0; + } + return true; +}"# + + #" +/* + digitvalue - returns the numerical value of a hexadecimal digit, or 16 if invalid. +*/ +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; +}"# + + #" +/* + checkinteger - check that obj is an integer and return it +*/ +int checkinteger (object *obj) { + if (!integerp(obj)) error(notaninteger, obj); + return obj->integer; +}"# + + #+arrays + #" +/* + checkbitvalue - check that obj is an integer equal to 0 or 1 and return it +*/ +int checkbitvalue (object *obj) { + if (!integerp(obj)) error(notaninteger, obj); + int n = obj->integer; + if (n & ~1) error(PSTR("argument is not a bit value"), obj); + return n; +}"# + + #+float + #" +/* + checkintfloat - check that obj is an integer or floating-point number and return the number +*/ +float checkintfloat (object *obj) { + if (integerp(obj)) return (float)obj->integer; + if (!floatp(obj)) error(notanumber, obj); + return obj->single_float; +}"# + + #" +/* + checkchar - check that obj is a character and return the character +*/ +int checkchar (object *obj) { + if (!characterp(obj)) error(PSTR("argument is not a character"), obj); + return obj->chars; +} + +/* + checkstring - check that obj is a string +*/ +object *checkstring (object *obj) { + if (!stringp(obj)) error(notastring, obj); + return obj; +} + +int isstream (object *obj){ + if (!streamp(obj)) error(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); +} + +int checkkeyword (object *obj) { + if (!keywordp(obj)) error(PSTR("argument is not a keyword"), obj); + builtin_t kname = builtin(obj->name); + uint8_t context = getminmax(kname); + if (context != 0 && context != Context) error(invalidkey, obj); + return ((int)lookupfn(kname)); +} + +/* + checkargs - checks that the number of objects in the list args + is within the range specified in the symbol lookup table +*/ +void checkargs (object *args) { + int nargs = listlength(args); + checkminmax(Context, nargs); +}"# + + #+(or arm esp riscv) + #" +/* + eqlongsymbol - checks whether two long symbols are equal +*/ +bool eqlongsymbol (symbol_t sym1, symbol_t sym2) { + object *arg1 = (object *)sym1; object *arg2 = (object *)sym2; + while ((arg1 != NULL) || (arg2 != NULL)) { + if (arg1 == NULL || arg2 == NULL) return false; + if (arg1->chars != arg2->chars) return false; + arg1 = car(arg1); arg2 = car(arg2); + } + return true; +} + +/* + eqsymbol - checks whether two symbols are equal +*/ +bool eqsymbol (symbol_t sym1, symbol_t sym2) { + if (!longnamep(sym1) && !longnamep(sym2)) return (sym1 == sym2); // Same short symbol + if (longnamep(sym1) && longnamep(sym2)) return eqlongsymbol(sym1, sym2); // Same long symbol + return false; +}"# + + #+(or avr avr-nano) + #" +/* + eq - implements Lisp eq +*/ +bool 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 (characterp(arg1) && characterp(arg2)) return true; // Same character + return false; +}"# + + #+(or arm esp) + #" +/* + eq - implements Lisp eq, taking into account PSRAM +*/ +bool eq (object *arg1, object *arg2) { + if (arg1 == arg2) return true; // Same object + if ((arg1 == nil) || (arg2 == nil)) return false; // Not both values + #if !defined(BOARD_HAS_PSRAM) + if (arg1->cdr != arg2->cdr) return false; // Different values + if (symbolp(arg1) && symbolp(arg2)) return true; // Same symbol + #else + if (symbolp(arg1) && symbolp(arg2)) return eqsymbol(arg1->name, arg2->name); // Same symbol? + if (arg1->cdr != arg2->cdr) return false; // Different values + #endif + 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; +}"# + + #+riscv + #" +/* + eq - implements Lisp eq, taking into account large-RAM +*/ +bool eq (object *arg1, object *arg2) { + if (arg1 == arg2) return true; // Same object + if ((arg1 == nil) || (arg2 == nil)) return false; // Not both values + #if (WORKSPACESIZE <= 80000) + if (arg1->cdr != arg2->cdr) return false; // Different values + if (symbolp(arg1) && symbolp(arg2)) return true; // Same symbol + #else + if (symbolp(arg1) && symbolp(arg2)) return eqsymbol(arg1->name, arg2->name); // Same symbol? + if (arg1->cdr != arg2->cdr) return false; // Different values + #endif + 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; +}"# + + #+avr-nano + #" +/* + equal - implements Lisp equal +*/ +bool equal (object *arg1, object *arg2) { + if (stringp(arg1) && stringp(arg2)) return stringcompare(cons(arg1, cons(arg2, nil)), false, false, true); + if (consp(arg1) && consp(arg2)) return (equal(car(arg1), car(arg2)) && equal(cdr(arg1), cdr(arg2))); + return eq(arg1, arg2); +}"# + + #+(or avr arm esp riscv) + #" +/* + equal - implements Lisp equal +*/ +bool equal (object *arg1, object *arg2) { + if (stringp(arg1) && stringp(arg2)) return (stringcompare(cons(arg1, cons(arg2, nil)), false, false, true) != -1); + if (consp(arg1) && consp(arg2)) return (equal(car(arg1), car(arg2)) && equal(cdr(arg1), cdr(arg2))); + return eq(arg1, arg2); +}"# + + #" +/* + listlength - returns the length of a list +*/ +int listlength (object *list) { + int length = 0; + while (list != NULL) { + if (improperp(list)) error2(notproper); + list = cdr(list); + length++; + } + return length; +}"# + + #+avr-nano + #" +/* + checkarguments - checks the arguments list in a special form such as with-xxx, + dolist, or dotimes. +*/ +object *checkarguments (object *args, uint8_t min, uint8_t max) { + if (args == NULL) error2(noargument); + args = first(args); + if (!listp(args)) error(notalist, args); + uint8_t length = listlength(args); + if (length < min) error(toofewargs, args); + if (length > max) error(toomanyargs, args); + return args; +}"# + + #-avr-nano + #" +/* + checkarguments - checks the arguments list in a special form such as with-xxx, + dolist, or dotimes. +*/ +object *checkarguments (object *args, int min, int max) { + if (args == NULL) error2(noargument); + args = first(args); + if (!listp(args)) error(notalist, args); + int length = listlength(args); + if (length < min) error(toofewargs, args); + if (length > max) error(toomanyargs, args); + return args; +}"# + +#" +// Mathematical helper functions"# + + #+(or avr avr-nano) + #" +/* + pseudoRandom - returns a pseudorandom number from 0 to range-1 + For an explanation of the dummy line see: http://forum.ulisp.com/t/compiler-mystery-any-suggestions/854 +*/ +uint16_t pseudoRandom (int range) { + if (RandomSeed == 0) RandomSeed++; + uint16_t l = RandomSeed & 1; + RandomSeed = RandomSeed >> 1; + if (l == 1) RandomSeed = RandomSeed ^ 0xD295; + int dummy; if (RandomSeed == 0) Serial.print((int)&dummy); // Do not remove! + return RandomSeed % range; +}"# + + #+float + #" +/* + add_floats - used by fn_add + Converts the numbers in args to floats, adds them to fresult, and returns the sum as a Lisp float. +*/ +object *add_floats (object *args, float fresult) { + while (args != NULL) { + object *arg = car(args); + fresult = fresult + checkintfloat(arg); + args = cdr(args); + } + return makefloat(fresult); +} + +/* + subtract_floats - used by fn_subtract with more than one argument + Converts the numbers in args to floats, subtracts them from fresult, and returns the result as a Lisp float. +*/ +object *subtract_floats (object *args, float fresult) { + while (args != NULL) { + object *arg = car(args); + fresult = fresult - checkintfloat(arg); + args = cdr(args); + } + return makefloat(fresult); +} + +/* + negate - used by fn_subtract with one argument + If the result is an integer, and negating it doesn't overflow, keep the result as an integer. + Otherwise convert the result to a float, negate it, and return the result as a Lisp float. +*/ +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(notanumber, arg); + return nil; +} + +/* + multiply_floats - used by fn_multiply + Converts the numbers in args to floats, adds them to fresult, and returns the result as a Lisp float. +*/ +object *multiply_floats (object *args, float fresult) { + while (args != NULL) { + object *arg = car(args); + fresult = fresult * checkintfloat(arg); + args = cdr(args); + } + return makefloat(fresult); +} + +/* + divide_floats - used by fn_divide + Converts the numbers in args to floats, divides fresult by them, and returns the result as a Lisp float. +*/ +object *divide_floats (object *args, float fresult) { + while (args != NULL) { + object *arg = car(args); + float f = checkintfloat(arg); + if (f == 0.0) error2(divisionbyzero); + fresult = fresult / f; + args = cdr(args); + } + return makefloat(fresult); +}"# + + #+avr +#" +/* + remmod - implements rem (mod = false) and mod (mod = true). +*/ +object *remmod (object *args, bool mod) { + int arg1 = checkinteger(first(args)); + int arg2 = checkinteger(second(args)); + if (arg2 == 0) error2(divisionbyzero); + int r = arg1 % arg2; + if (mod && (arg1<0) != (arg2<0)) r = r + arg2; + return number(r); +}"# + + #+(or arm esp risc-v) +#" +/* + remmod - implements rem (mod = false) and mod (mod = true). +*/ +object *remmod (object *args, bool mod) { + object *arg1 = first(args); + object *arg2 = second(args); + if (integerp(arg1) && integerp(arg2)) { + int divisor = arg2->integer; + if (divisor == 0) error2(divisionbyzero); + int dividend = arg1->integer; + int remainder = dividend % divisor; + if (mod && (dividend<0) != (divisor<0)) remainder = remainder + divisor; + return number(remainder); + } else { + float fdivisor = checkintfloat(arg2); + if (fdivisor == 0.0) error2(divisionbyzero); + float fdividend = checkintfloat(arg1); + float fremainder = fmod(fdividend , fdivisor); + if (mod && (fdividend<0) != (fdivisor<0)) fremainder = fremainder + fdivisor; + return makefloat(fremainder); + } +}"# + + #+(or avr avr-nano) +#" +/* + compare - a generic compare function + Used to implement the other comparison functions. + If lt is true the result is true if each argument is less than the next argument. + If gt is true the result is true if each argument is greater than the next argument. + If eq is true the result is true if each argument is equal to the next argument. +*/ +object *compare (object *args, bool lt, bool gt, bool eq) { + int arg1 = checkinteger(first(args)); + args = cdr(args); + while (args != NULL) { + int arg2 = checkinteger(first(args)); + if (!lt && (arg1 < arg2)) return nil; + if (!eq && (arg1 == arg2)) return nil; + if (!gt && (arg1 > arg2)) return nil; + arg1 = arg2; + args = cdr(args); + } + return tee; +}"# + +#-(or avr avr-nano) +#" +/* + compare - a generic compare function + Used to implement the other comparison functions. + If lt is true the result is true if each argument is less than the next argument. + If gt is true the result is true if each argument is greater than the next argument. + If eq is true the result is true if each argument is equal to the next argument. +*/ +object *compare (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(arg1) < checkintfloat(arg2))) return nil; + if (!eq && (checkintfloat(arg1) == checkintfloat(arg2))) return nil; + if (!gt && (checkintfloat(arg1) > checkintfloat(arg2))) return nil; + } + arg1 = arg2; + args = cdr(args); + } + return tee; +}"# + +#" +/* + intpower - calculates base to the power exp as an integer +*/ +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; +}"#)) + +#+avr-nano +(defparameter *association-lists* '(#" +// Association lists + +/* + delassoc - deletes the pair matching key from an association list and returns the key, or nil if not found +*/ +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; +}"#)) + +#-avr-nano +(defparameter *association-lists* '(#" +// Association lists + +/* + testargument - handles the :test argument for functions that accept it +*/ +object *testargument (object *args) { + object *test = bsymbol(EQ); + if (args != NULL) { + if (cdr(args) == NULL) error2(PSTR("unpaired keyword")); + if ((isbuiltin(first(args), TEST))) test = second(args); + else error(PSTR("unsupported keyword"), first(args)); + } + return test; +} + +/* + delassoc - deletes the pair matching key from an association list and returns the key, or nil if not found +*/ +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; +}"#)) + +(defparameter *array-utilities* '( + +#+arrays +#" +// Array utilities"# + +#+(and arrays avr) +#" +/* + nextpower2 - returns the smallest power of 2 that is equal to or greater than n +*/ +int nextpower2 (int n) { + n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; + n |= n >> 8; n++; + return n<2 ? 2 : n; +}"# + +#+(and arrays (not avr)) +#" +/* + nextpower2 - returns the smallest power of 2 that is equal to or greater than n +*/ +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; +}"# + +#+arrays +#" +/* + buildarray - builds an array with n elements using a tree of size s which must be a power of 2 + The elements are initialised to the default def +*/ +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 (object *dims, object *def, bool bitp) { + int size = 1; + object *dimensions = dims; + while (dims != NULL) { + int d = car(dims)->integer; + if (d < 0) error2(PSTR("dimension can't be negative")); + size = size * d; + dims = cdr(dims); + } + // Bit array identified by making first dimension negative + if (bitp) { + size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); + 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; +} + +/* + arrayref - returns a pointer to the element specified by index in the array of size s +*/ +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; +} + +/* + getarray - gets a pointer to an element in a multi-dimensional array, given a list of the subscripts subs + If the first subscript is negative it's a bit array and bit is set to the bit number +*/ +object **getarray (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(eval(car(subs), env)); else s = checkinteger(car(subs)); + if (s < 0 || s >= d) error(PSTR("subscript out of range"), car(subs)); + size = size * d; + index = index * d + s; + dims = cdr(dims); subs = cdr(subs); + } + if (dims != NULL) error2(PSTR("too few subscripts")); + if (subs != NULL) error2(PSTR("too many subscripts")); + if (bitp) { + size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); + *bit = index & (sizeof(int)==4 ? 0x1F : 0x0F); + index = index>>(sizeof(int)==4 ? 5 : 4); + } + return arrayref(array, index, size); +} + +/* + rslice - reads a slice of an array recursively +*/ +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(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); + } +} + +/* + readarray - reads a list structure from args and converts it to a d-dimensional array. + Uses rslice for each of the slices of the array. +*/ +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(PSTR("initial contents don't match array type")); + int l = listlength(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(head, NULL, false); + rslice(array, size, 0, head, args); + return array; +} + +/* + readbitarray - reads an item in the format #*1010101000110 by reading it and returning a list of integers, + and then converting that to a bit array +*/ +object *readbitarray (gfun_t gfun) { + char ch = gfun(); + object *head = NULL; + object *tail = NULL; + while (!issp(ch) && !isbr(ch)) { + if (ch != '0' && ch != '1') error2(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(head); + object *array = makearray(cons(number(size), NULL), number(0), true); + size = (size + sizeof(int)*8 - 1)/(sizeof(int)*8); + int index = 0; + while (head != NULL) { + object **loc = arrayref(array, index>>(sizeof(int)==4 ? 5 : 4), size); + int bit = index & (sizeof(int)==4 ? 0x1F : 0x0F); + *loc = number((((*loc)->integer) & ~(1<integer)<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>>(sizeof(int)==4 ? 5 : 4), size))->integer)>> + (index & (sizeof(int)==4 ? 0x1F : 0x0F)) & 1, pfun); + else printobject(*arrayref(array, index, size), pfun); + } else { pfun('('); pslice(array, size, index, cdr(dims), pfun, bitp); pfun(')'); } + } +} + +/* + printarray - prints an array in the appropriate Lisp format +*/ +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 + sizeof(int)*8 - 1)/(sizeof(int)*8); + 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(')'); + } +}"#)) + +(defparameter *string-utilities* + + '(#" +// String utilities + +void indent (uint8_t spaces, char ch, pfun_t pfun) { + for (uint8_t i=0; ichars & 0xFF) == 0) { + (*tail)->chars |= ch; return; + } else { + cell = myalloc(); car(*tail) = cell; + } + car(cell) = NULL; cell->chars = ch<<8; *tail = cell; +}"# + + + #+(or arm esp riscv) + #" +/* + buildstring - adds a character on the end of a string + Handles Lisp strings packed four characters per 32-bit word +*/ +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 |= ch<<16; return; + } else if (((*tail)->chars & 0xFFFF) == 0) { + (*tail)->chars |= ch<<8; return; + } else if (((*tail)->chars & 0xFF) == 0) { + (*tail)->chars |= ch; return; + } else { + cell = myalloc(); car(*tail) = cell; + } + car(cell) = NULL; cell->chars = ch<<24; *tail = cell; +}"# + + #" +/* + copystring - returns a copy of a Lisp string +*/ +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; +} + +/* + readstring - reads characters from an input stream up to delimiter delim + and returns a Lisp string +*/ +object *readstring (uint8_t delim, bool esc, gfun_t gfun) { + object *obj = newstring(); + object *tail = obj; + int ch = gfun(); + if (ch == -1) return nil; + while ((ch != delim) && (ch != -1)) { + if (esc && ch == '\\') ch = gfun(); + buildstring(ch, &tail); + ch = gfun(); + } + return obj; +} + +/* + stringlength - returns the length of a Lisp string + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word +*/ +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; +}"# + +#+avr-nano +#" +/* + nthchar - returns the nth character from a Lisp string +*/ +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; ichars)>>(n*8) & 0xFF; +}"# + +#-avr-nano +#" +/* + getcharplace - gets character n in a Lisp string, and sets shift to (- the shift position -2) + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word. +*/ +object **getcharplace (object *string, int n, int *shift) { + object **arg = &cdr(string); + int top; + if (sizeof(int) == 4) { top = n>>2; *shift = 3 - (n&3); } + else { top = n>>1; *shift = 1 - (n&1); } + *shift = - (*shift + 2); + for (int i=0; ichars)>>((-shift-2)<<3)) & 0xFF; +}"# + +#" +/* + gstr - reads a character from a string stream +*/ +int gstr () { + if (LastChar) { + char temp = LastChar; + LastChar = 0; + return temp; + } + char c = nthchar(GlobalString, GlobalStringIndex++); + if (c != 0) return c; + return '\n'; // -1? +} + +/* + pstr - prints a character to a string stream +*/ +void pstr (char c) { + buildstring(c, &GlobalStringTail); +} + +/* + lispstring - converts a C string to a Lisp string +*/ +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; +}"# + +#+avr-nano +#" +/* + stringcompare - a generic string compare function + Used to implement the other string comparison functions. + If lt is true the result is true if each argument is less than the next argument. + If gt is true the result is true if each argument is greater than the next argument. + If eq is true the result is true if each argument is equal to the next argument. +*/ +bool stringcompare (object *args, bool lt, bool gt, bool eq) { + object *arg1 = checkstring(first(args)); + object *arg2 = checkstring(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; +}"# + +#-avr-nano +#" +/* + stringcompare - a generic string compare function + Used to implement the other string comparison functions. + Returns -1 if the comparison is false, or the index of the first mismatch if it is true. + If lt is true the result is true if the first argument is less than the second argument. + If gt is true the result is true if the first argument is greater than the second argument. + If eq is true the result is true if the first argument is equal to the second argument. +*/ +int stringcompare (object *args, bool lt, bool gt, bool eq) { + object *arg1 = checkstring(first(args)); + object *arg2 = checkstring(second(args)); + arg1 = cdr(arg1); arg2 = cdr(arg2); + int m = 0; chars_t a = 0, b = 0; + while ((arg1 != NULL) || (arg2 != NULL)) { + if (arg1 == NULL) return lt ? m : -1; + if (arg2 == NULL) return gt ? m : -1; + a = arg1->chars; b = arg2->chars; + if (a < b) { if (lt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; } + if (a > b) { if (gt) { m = m + sizeof(int); while (a != b) { m--; a = a >> 8; b = b >> 8; } return m; } else return -1; } + arg1 = car(arg1); arg2 = car(arg2); + m = m + sizeof(int); + } + if (eq) { m = m - sizeof(int); while (a != 0) { m++; a = a << 8;} return m;} else return -1; +}"# + +#+(and doc (or avr esp)) +#" +/* + documentation - returns the documentation string of a built-in or user-defined function. +*/ +object *documentation (object *arg, object *env) { + if (arg == NULL) return nil; + if (!symbolp(arg)) error(notasymbol, arg); + object *pair = findpair(arg, env); + if (pair != NULL) { + object *val = cdr(pair); + if (listp(val) && first(val)->name == sym(LAMBDA) && cdr(val) != NULL && cddr(val) != NULL) { + if (stringp(third(val))) return third(val); + } + } + symbol_t docname = arg->name; + if (!builtinp(docname)) return nil; + char *docstring = lookupdoc(builtin(docname)); + if (docstring == NULL) return nil; + object *obj = startstring(); + pfstring(docstring, pstr); + return obj; +}"# + +#+(and doc (or arm riscv)) +#" +/* + documentation - returns the documentation string of a built-in or user-defined function. +*/ +object *documentation (object *arg, object *env) { + if (arg == NULL) return nil; + if (!symbolp(arg)) error(notasymbol, arg); + object *pair = findpair(arg, env); + if (pair != NULL) { + object *val = cdr(pair); + if (listp(val) && first(val)->name == sym(LAMBDA) && cdr(val) != NULL && cddr(val) != NULL) { + if (stringp(third(val))) return third(val); + } + } + symbol_t docname = arg->name; + if (!builtinp(docname)) return nil; + char *docstring = lookupdoc(builtin(docname)); + if (docstring == NULL) return nil; + object *obj = startstring(); + pfstring(docstring, pstr); + return obj; +}"# + +#+doc +#" +/* + apropos - finds the user-defined and built-in functions whose names contain the specified string or symbol, + and prints them if print is true, or returns them in a list. +*/ +object *apropos (object *arg, bool print) { + char buf[17], buf2[33]; + char *part = cstring(princtostring(arg), buf, 17); + object *result = cons(NULL, NULL); + object *ptr = result; + // User-defined? + object *globals = GlobalEnv; + while (globals != NULL) { + object *pair = first(globals); + object *var = car(pair); + object *val = cdr(pair); + char *full = cstring(princtostring(var), buf2, 33); + if (strstr(full, part) != NULL) { + if (print) { + printsymbol(var, pserial); pserial(' '); pserial('('); + if (consp(val) && isbuiltin(car(val), LAMBDA)) pfstring("user function", pserial); + else if (consp(val) && car(val)->type == CODE) pfstring(PSTR("code"), pserial); + else pfstring(PSTR("user symbol"), pserial); + pserial(')'); pln(pserial); + } else { + cdr(ptr) = cons(var, NULL); ptr = cdr(ptr); + } + } + globals = cdr(globals); + testescape(); + } + // Built-in? + int entries = tablesize(0) + tablesize(1); + for (int i = 0; i < entries; i++) { + if (findsubstring(part, (builtin_t)i)) { + if (print) { + uint8_t fntype = fntype(i); + pbuiltin((builtin_t)i, pserial); pserial(' '); pserial('('); + if (fntype == FUNCTIONS) pfstring(PSTR("function"), pserial); + else if (fntype == SPECIAL_FORMS || fntype == TAIL_FORMS) pfstring(PSTR("special form"), pserial); + else pfstring(PSTR("symbol/keyword"), pserial); + pserial(')'); pln(pserial); + } else { + cdr(ptr) = cons(bsymbol(i), NULL); ptr = cdr(ptr); + } + } + testescape(); + } + return cdr(result); +}"# + + #-avr-nano + #" +/* + cstring - converts a Lisp string to a C string in buffer and returns buffer + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word +*/ +char *cstring (object *form, char *buffer, int buflen) { + form = cdr(checkstring(form)); + int index = 0; + while (form != NULL) { + int chars = form->integer; + for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { + char ch = chars>>i & 0xFF; + if (ch) { + if (index >= buflen-1) error2(PSTR("no room for string")); + buffer[index++] = ch; + } + } + form = car(form); + } + buffer[index] = '\0'; + return buffer; +}"# + +#+wifi +#" +/* + iptostring - converts a 32-bit IP address to a lisp string +*/ +object *iptostring (uint32_t ip) { + union { uint32_t data2; uint8_t u8[4]; }; + object *obj = startstring(); + data2 = ip; + for (int i=0; i<4; i++) { + if (i) pstr('.'); + pintbase(u8[i], 10, pstr); + } + return obj; +} + +/* + ipstring - parses an IP address from a Lisp string and returns it as an IPAddress type (uint32_t) + Handles Lisp strings packed two characters per 16-bit word, or four characters per 32-bit word +*/ +uint32_t ipstring (object *form) { + form = cdr(checkstring(form)); + int p = 0; + union { uint32_t ipaddress; uint8_t ipbytes[4]; } ; + ipaddress = 0; + while (form != NULL) { + int chars = form->integer; + for (int i=(sizeof(int)-1)*8; i>=0; i=i-8) { + char ch = chars>>i & 0xFF; + if (ch) { + if (ch == '.') { p++; if (p > 3) error2(PSTR("illegal IP address")); } + else ipbytes[p] = (ipbytes[p] * 10) + ch - '0'; + } + } + form = car(form); + } + return ipaddress; +}"#)) + +(defparameter *closures* '( + + #+(or avr avr-nano) + #" +/* + value - 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; +}"# + + #+(or arm esp) + #" +/* + value - lookup variable in environment, taking into account PSRAM +*/ +object *value (symbol_t n, object *env) { + while (env != NULL) { + object *pair = car(env); + #if !defined(BOARD_HAS_PSRAM) + if (pair != NULL && car(pair)->name == n) return pair; + #else + if (pair != NULL && eqsymbol(car(pair)->name, n)) return pair; + #endif + env = cdr(env); + } + return nil; +}"# + + #+riscv + #" +/* + value - lookup variable in environment, taking into account large-RAM +*/ +object *value (symbol_t n, object *env) { + while (env != NULL) { + object *pair = car(env); + #if (WORKSPACESIZE <= 80000) + if (pair != NULL && car(pair)->name == n) return pair; + #else + if (pair != NULL && eqsymbol(car(pair)->name, n)) return pair; + #endif + env = cdr(env); + } + return nil; +}"# + +#" +/* + findpair - returns the (var . value) pair bound to variable var in the local or global environment +*/ +object *findpair (object *var, object *env) { + symbol_t name = var->name; + object *pair = value(name, env); + if (pair == NULL) pair = value(name, GlobalEnv); + return pair; +} + +/* + boundp - tests whether var is bound to a value +*/ +bool boundp (object *var, object *env) { + if (!symbolp(var)) error(notasymbol, var); + return (findpair(var, env) != NULL); +} + +/* + findvalue - returns the value bound to variable var, or gives an error if unbound +*/ +object *findvalue (object *var, object *env) { + object *pair = findpair(var, env); + if (pair == NULL) error(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 = 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 (object *function, object *args, object *env) { + if (symbolp(function)) { + builtin_t fname = builtin(function->name); + if ((fname < ENDFUNCTIONS) && (fntype(fname) == FUNCTIONS)) { + Context = fname; + checkargs(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(NIL), function, args, &env); + return eval(result, env); + } + if (consp(function) && isbuiltin(car(function), CLOSURE)) { + function = cdr(function); + object *result = closure(0, sym(NIL), function, args, &env); + return eval(result, env); + } + error(illegalfn, function); + return NULL; +}"#)) + +(defparameter *in-place* '( + +#" +// In-place operations"# + +#+avr-nano ; no arrays or char +#" +/* + place - returns a pointer to an object referenced in the second argument of an + in-place operation such as setf. +*/ +object **place (object *args, object *env) { + 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(canttakecar, value); + return &car(value); + } + if (sname == sym(CDR) || sname == sym(REST)) { + object *value = eval(second(args), env); + if (!listp(value)) error(canttakecdr, value); + return &cdr(value); + } + if (sname == sym(NTH)) { + int index = checkinteger(eval(second(args), env)); + object *list = eval(third(args), env); + if (atom(list)) { Context = NTH; error(PSTR("second argument is not a list"), list); } + int i = index; + while (i > 0) { + list = cdr(list); + if (list == NULL) { Context = NTH; error(indexrange, number(index)); } + i--; + } + return &car(list); + } + } + error2(PSTR("illegal place")); + return nil; +}"# + +#-avr-nano +#" +/* + place - returns a pointer to an object referenced in the second argument of an + in-place operation such as setf. bit is used to indicate the bit position in a bit array +*/ +object **place (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(canttakecar, value); + return &car(value); + } + if (sname == sym(CDR) || sname == sym(REST)) { + object *value = eval(second(args), env); + if (!listp(value)) error(canttakecdr, value); + return &cdr(value); + } + if (sname == sym(NTH)) { + int index = checkinteger(eval(second(args), env)); + object *list = eval(third(args), env); + if (atom(list)) { Context = NTH; error(PSTR("second argument is not a list"), list); } + int i = index; + while (i > 0) { + list = cdr(list); + if (list == NULL) { Context = NTH; error(indexrange, number(index)); } + i--; + } + return &car(list); + } + if (sname == sym(CHAR)) { + int index = checkinteger(eval(third(args), env)); + object *string = checkstring(eval(second(args), env)); + object **loc = getcharplace(string, index, bit); + if ((*loc) == NULL || (((((*loc)->chars)>>((-(*bit)-2)<<3)) & 0xFF) == 0)) { Context = CHAR; error(indexrange, number(index)); } + return loc; + } + if (sname == sym(AREF)) { + object *array = eval(second(args), env); + if (!arrayp(array)) { Context = AREF; error(PSTR("first argument is not an array"), array); } + return getarray(array, cddr(args), env, bit); + } + } + error2(PSTR("illegal place")); + return nil; +}"# + +#+avr-nano +#" +/* + incfdecf() - Increments/decrements a place by 'increment', and returns the result. + Calls place() to get a pointer to the numeric value. +*/ +object *incfdecf (object *args, int increment, object *env) { + object **loc = place(first(args), env); + int result = checkinteger(*loc); + args = cdr(args); + if (args != NULL) increment = checkinteger(eval(first(args), env)) * increment; + #if defined(checkoverflow) + if (increment < 1) { if (INT_MIN - increment > result) error2(overflow); } + else { if (INT_MAX - increment < result) error2(overflow); } + #endif + result = result + increment; + *loc = number(result); + return *loc; +}"# + +#+avr +#" +/* + incfdecf() - Increments/decrements a place by 'increment', and returns the result. + Calls place() to get a pointer to the numeric value. +*/ +object *incfdecf (object *args, int increment, object *env) { + int bit; + object **loc = place(first(args), env, &bit); + if (bit < -1) error2(notanumber); + int result = checkinteger(*loc); + args = cdr(args); + object *inc = (args != NULL) ? eval(first(args), env) : NULL; + + if (bit != -1) { + if (inc != NULL) increment = checkbitvalue(inc); + int newvalue = (((*loc)->integer)>>bit & 1) + increment; + + if (newvalue & ~1) error2(PSTR("result is not a bit value")); + *loc = number((((*loc)->integer) & ~(1< result) error2(overflow); } + else { if (INT_MAX - increment < result) error2(overflow); } + #endif + result = result + increment; + *loc = number(result); + return *loc; +}"# + +#" +// Checked car and cdr + +/* + carx - car with error checking +*/ +object *carx (object *arg) { + if (!listp(arg)) error(canttakecar, arg); + if (arg == nil) return nil; + return car(arg); +} + +/* + cdrx - cdr with error checking +*/ +object *cdrx (object *arg) { + if (!listp(arg)) error(canttakecdr, arg); + if (arg == nil) return nil; + return cdr(arg); +} + +/* + cxxxr - implements a general cxxxr function, + pattern is a sequence of bits 0b1xxx where x is 0 for a and 1 for d. +*/ +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; +}"# + +#-avr-nano +#" +// Mapping helper functions + +/* + mapcl - handles either mapc when mapl=false, or mapl when mapl=true +*/ +object *mapcl (object *args, object *env, bool mapl) { + object *function = first(args); + args = cdr(args); + object *result = first(args); + protect(result); + object *params = cons(NULL, NULL); + protect(params); + // Make parameters + while (true) { + object *tailp = params; + object *lists = args; + while (lists != NULL) { + object *list = car(lists); + if (list == NULL) { + unprotect(); unprotect(); + return result; + } + if (improperp(list)) error(notproper, list); + object *item = mapl ? list : first(list); + object *obj = cons(item, NULL); + car(lists) = cdr(list); + cdr(tailp) = obj; tailp = obj; + lists = cdr(lists); + } + apply(function, cdr(params), env); + } +}"# + +#" +/* + mapcarfun - function specifying how to combine the results in mapcar +*/ +void mapcarfun (object *result, object **tail) { + object *obj = cons(result,NULL); + cdr(*tail) = obj; *tail = obj; +} + +/* + mapcanfun - function specifying how to combine the results in mapcan +*/ +void mapcanfun (object *result, object **tail) { + if (cdr(*tail) != NULL) error(notproper, *tail); + while (consp(result)) { + cdr(*tail) = result; *tail = result; + result = cdr(result); + } +}"# + +#+avr-nano +#" +/* + mapcarcan - function used by marcar and mapcan when maplist=false, and maplist when maplist=true + It takes the arguments, the env, a function specifying how the results are combined, and a bool. +*/ +object *mapcarcan (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(notproper, list); + object *obj = cons(first(list),NULL); + car(lists) = cdr(list); + cdr(tailp) = obj; tailp = obj; + lists = cdr(lists); + } + object *result = apply(function, cdr(params), env); + fun(result, &tail); + } +}"# + +#-avr-nano +#" +/* + mapcarcan - function used by marcar and mapcan when maplist=false, and maplist when maplist=true + It takes the arguments, the env, a function specifying how the results are combined, and a bool. +*/ +object *mapcarcan (object *args, object *env, mapfun_t fun, bool maplist) { + object *function = first(args); + args = cdr(args); + object *params = cons(NULL, NULL); + protect(params); + object *head = cons(NULL, NULL); + protect(head); + object *tail = head; + // Make parameters + while (true) { + object *tailp = params; + object *lists = args; + while (lists != NULL) { + object *list = car(lists); + if (list == NULL) { + unprotect(); unprotect(); + return cdr(head); + } + if (improperp(list)) error(notproper, list); + object *item = maplist ? list : first(list); + object *obj = cons(item, NULL); + car(lists) = cdr(list); + cdr(tailp) = obj; tailp = obj; + lists = cdr(lists); + } + object *result = apply(function, cdr(params), env); + fun(result, &tail); + } +}"# + +#-avr-nano +#" +/* + dobody - function used by do when star=false and do* when star=true +*/ +object *dobody (object *args, object *env, bool star) { + object *varlist = first(args), *endlist = second(args); + object *head = cons(NULL, NULL); + protect(head); + object *ptr = head; + object *newenv = env; + while (varlist != NULL) { + object *varform = first(varlist); + object *var, *init = NULL, *step = NULL; + if (atom(varform)) var = varform; + else { + var = first(varform); + varform = cdr(varform); + if (varform != NULL) { + init = eval(first(varform), env); + varform = cdr(varform); + if (varform != NULL) step = cons(first(varform), NULL); + } + } + object *pair = cons(var, init); + push(pair, newenv); + if (star) env = newenv; + object *cell = cons(cons(step, pair), NULL); + cdr(ptr) = cell; ptr = cdr(ptr); + varlist = cdr(varlist); + } + env = newenv; + head = cdr(head); + object *endtest = first(endlist), *results = cdr(endlist); + while (eval(endtest, env) == NULL) { + object *forms = cddr(args); + while (forms != NULL) { + object *result = eval(car(forms), env); + if (tstflag(RETURNFLAG)) { + clrflag(RETURNFLAG); + return result; + } + forms = cdr(forms); + } + object *varlist = head; + int count = 0; + while (varlist != NULL) { + object *varform = first(varlist); + object *step = car(varform), *pair = cdr(varform); + if (step != NULL) { + object *val = eval(first(step), env); + if (star) { + cdr(pair) = val; + } else { + push(val, GCStack); + push(pair, GCStack); + count++; + } + } + varlist = cdr(varlist); + } + while (count > 0) { + cdr(car(GCStack)) = car(cdr(GCStack)); + pop(GCStack); pop(GCStack); + count--; + } + } + unprotect(); + return eval(tf_progn(results, env), env); +}"#)) + \ No newline at end of file