I’ve recently been working on an experimental assembler written in uLisp that generates and runs ARM Thumb code from within uLisp on an ATSAMD21 M0 board or similar.
The project is currently a work in progress, but I thought it would be interesting to describe where I’ve got to, and I’d be interested to hear suggestions for extensions or improvements.
The assembler generates ARM Thumb code, and I’ve tested it on ARM M0 platforms, such as the Adafruit ItsyBitsy M0, and ARM M4 platforms, such as the Adafruit Metro M4. It is currently restricted to generating one assembler routine with up to three integer parameters, and returning an integer result.
The assembler uses a special version of uLisp to which I’ve added two functions:
The assemble function takes a series of 16-bit integer arguments comprising the machine code of the routine being assembled. These are written into RAM. Any argument can also return nil, in which case it is ignored.
The call function will then call the assembler routine in RAM, and return the result.
The actual instruction generation is handled by an assembler written in Lisp. This makes it easy to check the generated code, and extend the assembler to handle additional instructions and addressing modes.
The assembler is written using Lisp functions, and where possible the syntax is very similar to standard ARM assembler syntax.
For example, here’s a simple routine to calculate the Greatest Common Divisor:
; Greatest Common Divisor (let ((swap 0) (again 0) (minus 0)) (dotimes (p 2) (setq *pc* 0) (assemble ($push '(r4 lr)) ($label 'swap) ($mov 'r4 'r1) ($mov 'r1 'r0) ($label 'again) ($mov 'r0 'r4) ($label 'minus) ($sub 'r4 'r4 'r1) ($blt 'swap) ($bne 'again) ($pop '(r4 pc)))))
For example, to find the GCD of 3287 and 3460:
> (call 3287 3460) 173
For more information see: ARM Assembler in Lisp.