I am a beginner in ulisp. I read http://www.ulisp.com/show?19Q4 and tried to integrate the function of my esp32c3 Bluetooth keyboard and BLE uart into it. ulisp is very useful because many codes can be debugged in ulisp’s REPL. However, it requires implementing interfaces in C for ulisp to call, and when there are changes in the C code, it needs to be compiled and uploaded, which takes quite some time.
I couldn’t find any particularly good methods on the forum, maybe I missed something. However, I did discover a fairly good approach to invoke C/C functions without the need to modify the C/C code, thus reducing compilation time. The specific method involves defining a ulisp function called call-c-fun
. When ulisp calls call-c-fun
, the corresponding C++ code of call-c-fun
will invoke the C/C++ function based on the ulisp parameters.
/*
* call c function
* (call-c-fun address [arg0 arg1 arg2])
*/
object *fn_call_c_fun (object *args, object *env);
int test_cfun(int n, char c) {
printf("call %s %d %d\n", __FUNCTION__ , n, c);
return 100;
}
/*
I can use
**nm ulisp-esp.ino.elf | grep test_cfun**
in the Arduino compilation directory to get the function address of test_cfun, and then (defvar test_cfun 1107296638)
*/
//in ulisp call c fun:
>(defvar test_cfun 1107296638)
>(call-c-fun test_cfun 32 65)
addr=1107296638 arg_len=2
get_value value=32
get_value value=65
call test_cfun 32 65
100
The code implementation can be found at: https://github.com/hyj0/ulisp-esp/tree/hyj.
To achieve more complex functionality, you have implemented call-c-fun2
, which allows parameters and return values to be objects or other types.
/*
* call c function
* (call-c-fun2 '(t (t nil nil...)) address arg0 arg1 ...)
*/
object *fn_call_c_fun2 (object *args, object *env)
// t indicates that the parameter is of object type, nil indicates C/C++ element type
for example:
/*
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)
//we use nm to get stringlength's address
//in ulisp
>(defvar stringlength-addr #x42000d08)
>(let* ((lisp-string "abcd")
(length (call-c-fun2 '(nil (t)) stringlength-addr lisp-string)))
(print (cons "length" length)))
("length" . 4)
Sometimes we need to change the Function pointer to execute our ulisp code. I provided a ulisp function:
(defvar g_hook_fun nil)
;return (name addr arglen)
(defun getHookFun (sign fun)
(let* ((all (call-c-fun2 '(t ()) getAllHookFunList))
(lst0 (mapcan (lambda (x) (if (assoc (car x) g_hook_fun)
nil
(list x)))
all))
(lst1 (mapcan (lambda (x) (if (= 0 (length (second sign)))
(if (= 0 (third x))
(list x)
nil)
(if (= 1 (third x))
(list x)
nil)))
lst0)))
(if lst1
(let ((one (first lst1)))
(setq g_hook_fun (cons (list (first one) sign fun) g_hook_fun))
one)
nil)))
(defun releaseHootFun (name)
(setq g_hook_fun (mapcan (lambda (x) (if (eq name (car x))
nil
(list x)))
g_hook_fun)))
;getAllHookFunList is c function
```
for example i want to print ulisp object to somewhere:
```
//we call printobject
>(defvar printobject-addr #x42002376)
>(let ((call_c_fun_debug #x3fc91a24))
(register call_c_fun_debug 0)) ;close log output
>(let* ((lisp-obj "xyz")
(my-printc (getHookFun '(nil (nil)) (lambda (c)
(print "get c")
(print c))))
(my-printc-addr (second my-printc))
(my-printc-name (first my-printc)))
(unwind-protect
(call-c-fun2 '(nil (t nil)) printobject-addr lisp-obj my-printc-addr)
(releaseHootFun my-printc-name)))
;output:
"get c"
34
"get c"
120
"get c"
121
"get c"
122
"get c"
34
34
getHookFun return (name addr arglen), “name” is passed to the “releaseHootFun” function for resource release, while “addr” is a disguised function address. When the function at “addr” is called, it invokes the defined “ulisp” function. In this example, the “printobject” function takes “lisp-obj” and our forged function as parameters, allowing us to control the output.
The code implementation can be found at my github: https://github.com/hyj0/ulisp-esp/tree/hyj.
I have used it on my ESP32-C3. Everyone is welcome to give it a try.