Home |  Overview |  Core |  Level 0 |  Level 1 |  Level 2

A Scheme Interpreter for ARM Microcontrollers: Language

SourceForge.net Logo


The Armpit Scheme language implementation is based on the description of Scheme in the Revised^5 Report on the Algorithmic Language Scheme (r5rs), with few extensions and omissions. The language elements are separated into 7 categories: Core, Level 0 Library, Level 0 Addendum, Level 1 Library, Level 1 Addendum, Level 2 Library and Level 2 Addendum. The Core represents the functions, syntax and variables of r5rs that do not have a "library" or "optional" qualifier (see r5rs section 1.3.3). The Level 0 Library are procedures of "library" category that are needed by the Armpit Scheme Core (append). The Level 0 Addendum are additonal procedures used by the Core that are exposed at Top-Level (match, substitute, reduce). The Core, Level 0 Library and Level 0 Addendum form a functional scheme system without a Reader (it works with pre-parsed expressions only).

The Scheme Reader (parser), and the Scheme functions it uses (aside from Core/Level 0 functions), are implemented in the Level 1 Library. The Level 1 Addendum adds bitwise logical operations, writing scheme startup code to FLASH, inf, nan and miscellaneous functions useful for microcontroller operations. The Armpit Scheme Core, along with Level 0 and 1 Libraries and Addenda, forms an operational Scheme system with a Read-Eval-Print (REP) loop and is currently the minimal run-able version of Armpit Scheme. It is a version that fits within 28 kB of FLASH and the one used with LPC2131 MCUs.

The Level 2 Library consists of r5rs "library" procedures, implemented in assembly language, that are not needed by either the Core or lower level libraries. The Level 2 Addendum adds functions that are not part of r5rs but are useful for MCU operation (pack). The Level 2 Library and Addendum are part of Armpit Scheme implementations for all MCUs except the LPC2131 which does not have sufficient FLASH to store them.


Armpit Scheme Core:

The r5rs elements incorporated into the Armpit Scheme Core are listed below with the section number in which they appear within r5rs:

  4. Expressions
     4.1. Primitive expression types
  	4.1.1 variable reference:	[internal bndenv]
	4.1.2 literal expressions:	quote
	4.1.3 procedure calls:		[internal apply]
	4.1.4 procedures:		lambda
	4.1.5 conditionals:		if	
	4.1.6 assignments:		set!
     4.2. Derived expression types
	4.2.6 quasiquotation:		quasiquote
     4.3 Macros
       4.3.2 pattern language:		syntax-rules
  5. Program Structure
     5.2 definitions:			define
     5.3 syntax definitions:		define-syntax
  6. Standard Procedures
     6.1 equivalence predicates:	eqv?, eq?, equal?
     6.2 numbers
       6.2.5 numerical operations:	number?, integer?, =, <, >, <=, >=, +, *, -, /, quotient, remainder,
					modulo, floor, ceiling, truncate, round, exp, log, sin, cos, tan,
					asin, acos, atan, sqrt, expt
	6.2.6 numerical input/output:	number->string, string->number
     6.3 Other Data Types
       6.3.2 pairs and list:		pair?, cons, car, cdr, set-car!, set-cdr!
       6.3.3 symbols:			symbol?, symbol->string, string->symbol
       6.3.4 characters:		char?, char=?, char?, char<=?, char>=?, char->integer,
       6.3.5 strings:			string?, make-string, string, string-length, string-ref, string-set!
       6.3.6 vectors:			vector?, make-vector, vector, vector-length, vector-ref, vector-set!
     6.4 control features:		procedure?, apply, call/cc, values, call-with-values
     6.5 eval:				eval
     6.6 Input and Output
       6.6.1 ports:			current-input-port, current-output-port
       6.6.2 input:			read-char, peek-char, char-ready?
       6.6.3 output:			write-char

In Armpit Scheme, ports are specified using their base address (obtained for the relevant peripherals from the MCU's User Manual). The functions current-input-port and current-output-port return the base address of the current input/output peripheral of the MCU (typically uart0 or usb). For example, on the Tiny_2106:

    (number->string (current-input-port) 16) ;  -> "E000C000" == base address of LPC2000 uart0

The functions read-char, peek-char, char-ready? and write-char work only with uart and usb ports. For example to read one character from uart0 on the LPC2000:

    (define uart0 #xE000C000)
    (read-char uart0)         ; type a character and see it echoed on your terminal

Armpit Scheme Level 0 Library and Addendum:

The r5rs "library" category functions incorporated into the Armpit Scheme Level 0 Library are listed below with the section number in which they appear within r5rs. The "library" function append is used by the Core macro quasiquote.

  6. Standard Procedures
     6.3 Other Data Types
       6.3.2 pairs and list:		append

The Level 0 Addendum adds 3 functions: match, substitute and reduce. Both match and substitute are used by the Core pattern language and reduce is used by Core arithemtic functions such as + and *. The function match is used to match a form to a pattern. The function substitute is used to substitute bindings into a template. The function reduce is used to reduce a list by applying a binary function between an accumulated object and successive elements of the list. The syntax of these functions are:

    (match form pattern initial-bindings literals)
    (substitute bindings template)
    (reduce function default list)

An example of the usage of match is:

    (define form         '(plus 2 3 4))
    (define pattern      '(_ x ...))
    (define old-bindings '((z . 1)))
    (define literals     '(else =>))
    (define new-bindings (eval `(match ,form ,pattern ,old-bindings ,literals)))
    new-bindings         ; -> ((x 4 3 2) (_ . plus) (z . 1)) == an a-list of bindings

The use of substitute is illustrated by continuing the above example:

    (define template '(+ z x ...))
    (define new-expr (eval `(substitute ,new-bindings ,template)))
    new-expr         ; -> (+ 1 2 3 4)
    (eval new-expr)  ; -> 10

An example of the use of reduce is:

    (define (binsum n1 n2) (+ n1 n2))
    (reduce binsum 0 '(2 3 5 7))      ; -> 17

Armpit Scheme Level 1 Library and Addendum:

The Armpit Scheme Level 1 Library are those "library" category functions of r5rs needed to implement the scheme Reader (parser) and the corresponding write functionality. These functions are listed below with the section number in which they appear within r5rs:

  6. Standard Procedures
     6.3 Other Data Types
       6.3.5 strings:			substring
       6.3.6 vectors:			vector->list, list->vector
     6.6 Input and Output
       6.6.2 input:			read
       6.6.3 output:			write, display, newline

The functions write and display are one and the same in Armpit Scheme. The functionality of the read and write functions has been extended to allow reading/writing from/to arbitrary memory locations (especially I/O registers, eg. ADC, PWM, GPIO, ...). These functions further allow reading/writing through I2C (TWI) channels (LPC2000 only in version 00.0036). The extended syntax of these functions is (with {} used to represent optional items):

    (read {port} {register} {number-of-bytes})
    (write object {port} {register} {number-of-bytes})

The optional {port} is the base address of the MCU peripheral to read/write from/to (eg. #xE000C000 for the LPC2000 uart0). The optional {register}, if it is an integer, is the offset of the register to read/write from/to relative to the peripheral's base address specified by {port}. In case of I2C ports, for Master mode read/write, {register} should be a vector, the first element of which is the I2C address of the slave device to read/write from/to and additional elements (up to three) are values of internal registers of the slave to read/write from/to. The optional {number-of-bytes} is used in Master mode I2C communications to transfer 1 to 3 raw bytes of data that are received into a scheme integer (read) or written out from a scheme integer, in most-significant-byte-first order. Overall, whenever {register} is specified, read/write operations are performed using internal representations of scheme objects rather than the conversion from/to external representation performed by (read {port}) and (write object {port}). The use of read/write extensions is illustrated in the Armpit Scheme Program Examples web pages.

The Level 1 Addendum adds 7 functions, 2 variables and 1 procedure to Armpit Scheme. Five of the new functions are used for bitwise logical operations and are similar to those found in GNU guile scheme: logior, logxor, logand, lognot and ash (arithmetic shift):

    (number->string (logior #b1100 #b1010) 2) ; -> "00000000000000000000000000001110"
    (number->string (logxor #b1100 #b1010) 2) ; -> "00000000000000000000000000000110"
    (number->string (logand #b1100 #b1010) 2) ; -> "00000000000000000000000000001000"
    (number->string (lognot #b1100) 2)        ; -> "11111111111111111111111111110011"
    (number->string (ash #b1100  4) 2)        ; -> "00000000000000000000000011000000"
    (number->string (ash #b1100 -2) 2)        ; -> "00000000000000000000000000000011"

The Addendum also defines the function defined? that identifies whether a symbol is part of the currently accessible environment or not:

   (defined? 'xyz)    ; -> #f
   (define xyz 10)
   (defined? 'xyz)    ; -> #t

The 7th added function is unpack. It takes a position-independent packed-object as input and returns the corresponding, unpacked, scheme object. Packed objects are produced by the function pack of the Level 2 Addendum. They are vectors of bytes that include all references from a given object and allow closures, heterogeneous vectors, environments, continuations and other compound objects to be transmitted form MCU to MCU via I2C (for example). Unpack is included in Level 1 (i.e. LPC2131 has it) such that all Armpit Scheme MCUs support multiprocessing in that they can receive packed objects (eg. packed by more memory endowed MCUs), unpack them and possibly execute them locally if they are appropriate closures or continuations (see unpack in Level 2 below, and see the Program Examples web pages).

The 2 variables added at Level 1 are inf and nan which are special floating point numbers that represent positive infinity and indefinite values according to the 30-bit adjusted version of IEEE-754 floating point numbers used in Armpit Scheme. The following examplifies:

    inf     ;  -> inf
    nan     ;  -> nan
    (- inf) ;  -> -inf
    (/ 0)   ;  -> inf
    (/ 0 0) ;  -> nan

The Level 1 Addendum further adds the procedure: flash. This procedure temporarily stops the REP and takes ensuing user input literally (without parsing or evaluation) as a sequence of ASCII characters representing a scheme startup program, made up of a sequence of scheme expressions, for the MCU. The user should end entry of this sequence of characters (i.e. entry of the Scheme startup program) with ctrl-d if he/she wants it to be stored in FLASH or with ctrl-c if he/she wants to cancel (i.e. not store) the sequence. Typing either ctrl-c or ctrl-d returns the user to the REP. If a startup program was indeed stored in FLASH, it will be executed (read, eval and print of each expression) prior to entering the main REP of Armpit Scheme whenever the MCU is reset, unless GPIO pin 3 is grounded (a safeguard against bad startup programs and to reclaim the MCU from startup programs that have infinite loops). The following interaction examplifies:

    31980 armpit> (flash)

    (define (square x) (* x x))

    (define (minus x) (- x))

    31980 armpit> ;;; reset the MCU: eg. turn power off then on

    33460 armpit> (square (minus 6))
    33460 armpit>

Armpit Scheme Level 2 Library and Addendum:

The Level 2 Library contains most of the remaining functions of "library" category from r5rs (Level 2 is not included on the LPC2131). These functions are:

  4. Expressions
     4.1. Primitive expression types
     4.2. Derived expression types
	4.2.1 conditionals:		cond, case, and, or
	4.2.2 binding constructs:	let, let*, letrec
	4.2.3 sequencing:		begin
	4.2.4 iteration:		do
	4.2.5 delayed evaluation:	delay
  6. Standard Procedures
     6.2 numbers
       6.2.5 numerical operations:	zero?, positive?, negative?, odd?, even?, max, min, abs, gcd, lcm
     6.3 Other Data Types
       6.3.1 booleans:			not, boolean?
       6.3.2 pairs and list:		caar, cadr, cdar, cddr, caaar, caadr, cadar, caddr, cdaar, cdadr,
					cddar, cdddr, caaaar, caaadr, caadar, caaddr, cadaar, cadadr, caddar,
					cadddr, cdaaar, cdaadr, cdadar, cdaddr, cddaar, cddadr, cdddar,
					cddddr, null?, list?, list, length, reverse, list-tail, list-ref,
					memv, memq, member, assv, assq, assoc
       6.3.5 strings:			string=?, string-append, string->list, list->string,
					string-copy, string-fill
       6.3.6 vectors:			vector-fill
     6.4 control features:		map, for-each, force

The Level 2 Addendum adds the function: pack. This function takes one input argument (a scheme object) and returns a position-independent packed copy of that object. The packed object is a special byte vector that contains the object and all scheme objects that it refers to up to, but not including, objects from the implementation environment (that are common to all Armpit Scheme MCUs, except for Level 2 items that are not on the LPC2131). A packed object can be transmitted from MCU to MCU (eg. via I2C) and, if it is a closure, it can be unpacked and executed on the receiving MCU (eg. for distributed processing). The following examplifies object packing and unpacking on a single MCU:

   (define x (lambda (y) (* y 3)))
   x                ;  -> #procedure
   (define z (pack x))
   z                ;  -> #packed-object
   (unpack z)       ;  -> #procedure
   ((unpack z) 10)  ;  -> 30 

Last updated February 7, 2007