This page presents an updated ARMSchembler and a compiler for ARMPit Scheme (both preliminary, but working). Both programs can run either on-chip (for boards with sufficient RAM) or off-chip (in guile 1.7 or 1.8). The resulting code can contain references to internal ARMPit Scheme symbols (primitives) and these references are resolved by a linker prior to installing the resulting machine code above the heap or in a FLASH library (where available, i.e. for MCUs with on-chip FLASH).
The ARMSchembler of previous versions was extended to be compatible with guile and to produce both ARM and Thumb2 machine code. The result has been separated into 5 files, three of which need to be loaded as follows: 1) the common code is needed in all cases; 2) either the ARM sub-component or the Thumb2 sub-component is needed based on the desired target (ARM7TDMI-ARM920T-Cortex-A8 or Cortex-M3); 3) either the on-chip or the off-chip utility is needed based on whether the ARMSchembler is running in armpit scheme (on-chip) or in guile (off-chip).
For code that does not involve calling built-in primitives, the ARMSchembler can be used in the same way as in previous versions. For example, a function of no arguments that returns 1 can be ARMSchembled, displayed, installed and applied using:
; Armpit Scheme simple ARMSchembly Example (on- or off-chip)
(define vcod
(assemble 'var 0 '((set! sv1 5) (set! pc cnt))))
; Display resulting machine code in hexadecimal (on- or off-chip) (optional)
(bspl vcod)
; - > ARM result: #xaf #x0 #xef #x0 #x4005 #xe3a0 #xf001 #xe1a0 #t
; - > Thumb2 result: #xaf #x0 #xef #x0 #xf04f #x405 #x468f #xbf00 #t
; Install resulting function above heap (on-chip)
(define one (install vcod))
; Test (on-chip)
(one) ; -> 1
For code that refers to internal ARMPit Scheme symbols, the ARMSchembly can still be performed on- or off-chip but the result has to be linked to the system on-chip before calling install. The ARMPit Scheme linker (12/31/09) is used for this purpose. The input to the linker is a code-vector that consists of 4 elements: 1) the non-linked, installable, machine code vector produced by the ARMSchembler; 2) the *asm-comp-link* list produced by the ARMSchembler (null if code is not from compiler); 3) the *compile-syms* list produced by the compiler (use null if not compiled), and; 4) the *long-jumps* list produced by the ARMSchembler. The following illustrates the process, using the tak function as an example:
; Armpit Scheme ARMSchembly and Linking Example
; ARMSchemble the tak function (on- or off-chip)
(define cvec
(assemble
'var 3 ; sv1 <- x, sv2 <- y, sv3 <- z
'(takin ; [internal entry]
(cmp sv2 sv1) ; done?
(it pl #t) ; T2 if-then (i.e.: itT pl)
(pl set! sv1 sv3) ; if so, sv1 <- z, result
(pl set! pc cnt) ; if so, return
(save sv1 sv2 sv3 cnt) ; dts <- (x y z cnt ...)
(sub sv1 sv1 4) ; sv1 <- (- x 1)
(call takin) ; sv1 <- xnew = (tak sv1 sv2 sv3)
(snoc! sv3 sv4 dts) ; sv3 <- x, sv4 <- (y z cnt ...)
(snoc! sv4 sv5 sv4) ; sv4 <- y, sv5 <- (z cnt ...)
(car sv2 sv5) ; sv2 <- z
(save sv1) ; dts <- (xnew x y z cnt ...)
(sub sv1 sv4 4) ; sv1 <- (- y 1)
(call takin) ; sv1 <- ynew = (tak sv1 sv2 sv3)
(cdr sv4 dts) ; sv4 <- (x y z cnt ...)
(snoc! sv2 sv4 sv4) ; sv2 <- x, sv4 <- (y z cnt ...)
(snoc! sv3 sv4 sv4) ; sv3 <- y, sv4 <- (z cnt ...)
(car sv4 sv4) ; sv4 <- z
(save sv1) ; dts <- (ynew xnew x y z cnt ...)
(sub sv1 sv4 4) ; sv1 <- (- z 1)
(call takin) ; sv1 <- znew = (tak sv1 sv2 sv3)
(set! sv3 sv1) ; sv3 <- znew
(restore sv2 sv1) ; sv1 <- xnew, sv2 <- ynew, dts <- (x y z cnt ...)
(cdddr dts dts) ; dts <- (cnt ...)
(restore cnt) ; cnt <- cnt, dts <- (...)
(b takin)))) ; jump to compute (tak sv1 sv2 sv3)
; Display resulting machine code in hexadecimal (on- or off-chip) (optional)
(bspl cvec)
; - > ARM result:
; #xaf #x0 #x3ef #x0
; #x4 #xe155 #xc00c #xe1a0 #x4006 #x51a0 #xf001 #x51a0
; #x22 #xeb00 #x1000 #xe58a #x20 #xeb00 #x6000 #xe58a
; #x1e #xeb00 #x5000 #xe58a #x1c #xeb00 #x4000 #xe58a
; #x4004 #xe244 #x100f #xe1a0 #xfff0 #xeaff #xc0 #xe89a
; #x180 #xe897 #x5000 #xe598 #x14 #xeb00 #x4000 #xe58a
; #x4004 #xe247 #x100f #xe1a0 #xffe8 #xeaff #x7004 #xe59a
; #xa0 #xe897 #xc0 #xe897 #x7000 #xe597 #xb #xeb00
; #x4000 #xe58a #x4004 #xe247 #x100f #xe1a0 #xffdf #xeaff
; #x6004 #xe1a0 #x420 #xe89a #x410 #xe89a #xa004 #xe59a
; #xa004 #xe59a #xa004 #xe59a #x402 #xe89a #xffd7 #xeaff
; #xf004 #xe51f #x0 #x0 #t
; - > Thumb2 result:
; #xaf #x0 #x3ef #x0
; #xebb5 #xf04 #xbf00 #xbf5c #xea4f #x406 #x468f #xbf00
; #xf000 #xf846 #xf8ca #x1000 #xf000 #xf842 #xf8ca #x6000
; #xf000 #xf83e #xf8ca #x5000 #xf000 #xf83a #xf8ca #x4000
; #xf1a4 #x404 #xf10f #x104 #xf7ff #xbfe2 #xe89a #xc0
; #xe897 #x180 #xf8d8 #x5000 #xf000 #xf82a #xf8ca #x4000
; #xf1a7 #x404 #xf10f #x104 #xf7ff #xbfd2 #xf8da #x7004
; #xe897 #xa0 #xe897 #xc0 #xf8d7 #x7000 #xf000 #xf818
; #xf8ca #x4000 #xf1a7 #x404 #xf10f #x104 #xf7ff #xbfc0
; #xea4f #x604 #xe89a #x420 #xe89a #x410 #xf8da #xa004
; #xf8da #xa004 #xf8da #xa004 #xe89a #x402 #xf7ff #xbfb0
; #x4a00 #x4697 #x0 #x0 #t
; Link and install resulting function above heap (on-chip)
(define tak (install (link (vector cvec *asm-comp-link* '() *long-jumps*))))
; Test (on-chip)
(tak 18 12 6) ; -> 7
The ARMPit Scheme compiler is adapted from that presented in Abelson and Sussman's Structure and Interpretation of Computer Programs. It is not an optimizing compiler and produces code that (at present) appears to run only mildly faster than interpreted code (20% to 30% faster). It is also limited in that it does not handle quasiquoted expressions, improper lists and pointer constants that contain other pointer constants (eg. lists of vectors, vectors of strings, vectors of complex numbers, etc...). Additionally, on-chip, it does not handle MIT-style function definitions (implicit lambdas) and, off-chip, (on guile) it does hot handle internal defines (defines found within a function). The main utility of the compiler, at this stage, may be to produce code for on-chip FLASH libraries (where available), as such code uses less RAM than when interpreted or compiled and installed above the heap (compiled code is much longer than its Scheme source). On systems with ample RAM, installing compiled code above the heap can also help to reduce the load on the garbage collector.
In similarity to the ARMSchembler, the compiler can run on- or off-chip. To run it off-chip (on guile 1.7 or 1.8) the ARMPit Scheme macro definitions in the compatibility code file have to be loaded into guile first. To run it on-chip, the MCU needs sufficient RAM (currently, 1MB is ok, 64KB is not). The main compiler code is common to both on- and off-chip compilation and requires that the ARMSchembler is also loaded for proper operation. The compiler emits ARMSchembly code from scheme (eg. 'cxcompile) and further defines a macro to produce linker-ready machine code-vectors (i.e. 'escompile). The last component of the compiler is a set of on-chip utility functions that can compile and install predefined user procedures or add them to a FLASH library. These utility functions can be used, for example, to conveniently compile the bulk of the ARMSchembler and/or compiler, on-chip. The utilities also include the 'add-to-lib helper function that adds an on- or off-chip produced code-vector to a FLASH library.
The code examples below illustrate the application of the ARMPit Scheme compiler. Additional examples of on-chip compilation are presented here (12/31/09).
; Armpit Scheme Compilation Examples
; compile a statement defining the identity function to ARMSchembly (on- or off-chip)
(cxcompile (define id (lambda (x) x)))
; -> ((save cnt) (lldr sv1 const-0) (adr sv2 entry-0) (lldr sv3 after-lambda-0)
; (cons sv1 $cpld env sv1 sv2) (set! pc sv3)
; entry-0 (lldr sv1 const-1) (bl _bnc) (null? sv3) (it eq) (eq b _err)
; (set! sv1 sv5) (set! pc cnt)
; after-lambda-0 (0 . 4026531840)
; (set! sv4 sv1) (lldr sv1 const-2) (call _dfv) (set-cdr! sv2 sv4)
; (set! sv1 $c0) (restore cnt) (set! pc cnt)
; const-0 (0 . 805306368) (128 . 431) (0 . 15) const-1 (128 . 431) const-2 (128 . 687))
; display the related constants
; note: (128 . 431) links to 32769, (128 . 687) links to 32770
; eg. ((lambda (cn) (logior (ash (car cn) 8) (ash (cdr cn) -8))) '(128 . 431))
*compile-syms* ; -> ((32769 . "x") (32770 . "id"))
; compile a statement defining the identity function to a code-vector (on- or off-chip)
(define cvec
(escompile (define id (lambda (x) x))))
; display the result (on- or off-chip) (optional)
(esdisplay cvec)
; - > ARM result:
; '#(#(175 0 239 0 43 60160 4096 58762 16528 58783 49164 57760
; 20544 57999 24668 58783 49164 57760 38 60160 48 59554 16392 57922
; 2 58242 34 60160 49156 57760 4608 59554 16392 57922 2 58242
; 29 60160 12543 58272 24 59554 16392 57922 2 58242 61446 57760
; 16460 58783 49164 57760 23 60160 15 58166 49164 57760 22 2560
; 16392 57760 61441 57760 -536870880 0 28676 57760 16424 58783 49164 57760
; 4111 57760 16 59904 28676 58757 16447 58272 1026 59546 61441 57760
; -536870870 0 431 128 15 0 431 128 687 128 61444 58655
; 0 0 61444 58655 0 0 61444 58655 0 0 61444 58655
; 0 0 61444 58655 0 0 )
; ((92 . 32770) (90 . 32769) (86 . 32769) )
; ((32769 . "x") (32770 . "id") )
; ((112 . "_dfv") (108 . "_err") (104 . "_bnc")
; (100 . "_cns") (96 . "_sav") ))
; - > Thumb2 result:
; '#(#(175 0 239 0
; 61440 63584 63690 4096 63711 16548 59983 3084
; 59983 3084 61967 1352 63711 24680 59983 3084 59983 3084 61440 63570
; 59554 48 61858 1032 61506 2 61440 63562 59983 3076 59554 4608
; 61858 1032 61506 2 61440 63552 61519 1023 59554 24 61858 1032
; 61506 2 18103 48896 63711 16472 59983 3084 59983 3084 61440 63538
; 61590 3855 48896 48904 61440 47152 59983 1032 18063 48896 -536870877 0
; 59983 1796 63711 16432 59983 3084 59983 3084 61711 260 61440 47138
; 63685 28676 61519 1087 59546 1026 18063 48896 -536870866 0 431 128
; 15 0 431 128 687 128 18944 18071 0 0 18944 18071
; 0 0 18944 18071 0 0 18944 18071 0 0 18944 18071
; 0 0 )
; ((100 . 32770) (98 . 32769) (94 . 32769) )
; ((32769 . "x") (32770 . "id") )
; ((120 . "_dfv") (116 . "_err") (112 . "_bnc")
; (108 . "_cns") (104 . "_sav") ))
; link, install and execute the compiled statement (on-chip)
((install (link cvec)))
; check #1 (on-chip)
id ; -> #compiled>
; check #2 (on-chip)
(id '(1 3 (4 5) #(9 8 7) "woades")) ; -> (1 3 (4 5) #(9 8 7) "woades")
; add the compiled statement to FLASH library (tested on TINY-2106) (on-chip) (optional)
(add-to-lib cvec)
; manually reset the chip and re-run checks #1 and #2 above (on-chip) (optional)
; erase the FLASH library (on-chip) (optional)
(lib-erase)
; manually reset the chip and re-run check #1 above (on-chip) (optional)
id ; -> (core throw id)