SIMPL - a Tiny Interactive  Language for Microcontrollers or Experimental CPUs.

SIMPL is a symbolic language for programming microcontrollers based on printable ascii characters.

The idea of using single printable characters to represent instructions, commands, functions or even complete programs is not new:

MOUSE - a language for Microcomputers, Peter Grogono 1969

Txtzyme - a Nano Interpreter for Domain Specific Languages, Ward Cunningham 2010

Stable - An Extremely Small and Fast Forth VM, Sandor Schneider, 2016

SIMPL is derived from Txtzyme and development began in May 2013

The advantage of using a single character to represent an instruction or function is that it only takes up a single byte of storage, whilst function calls in other languages are often expressed as 16-bit addresses. 

Secondly, we are taught to read, recognise  and manipulate alphanumerical characters and symbols from an early age. This makes it easier to express a function or program symbolically rather than a series of numerical commands or lists of addresses. This may seem obvious to anyone who began their interests in computing programming by hand in assembly language and converting each instruction to hexadecimal characters to enter into the machine.

This leads to further compactness of the source code, for example a typical line of assembly language might be written:

ADD R0,R1 

With SIMPL this is reduced to just the single "+" character, and the source and destination registers are defined implicitly in the design of the virtual machine.

SIMPL is not a large language. It's kernel can be defined in less than 200 lines of C++ code, which makes it suitable for small microcontrollers such as any of the Arduino compatible boards.

Neither is it a complicated language, it is confined by the limitations of the printable ascii characters meaning that you are restricted to a core of just 32 symbolic instructions. Variables and functions must be selected from the uppercase A-Z and the lowercase a-z characters which imposes a further restriction on the scope of the language. 

SIMPL - A Brief Description

Each character, read in turn from RAM is interpreted and forms either an instruction, or a jump to an executable block of code, running on a virtual machine. The virtual machine is assumed to have a 16-bit wordsize and a 64Kbyte address space.

For example + will perform a 16-bit addition on the top two elements of the stack, whereas p will take the top element of the stack and print it to the terminal as a 16-bit decimal number.

In each case, the character is used in a look-up table or switch-case structure, to generate a unique jump to a block of code in ROM, which performs the command action.

Characters can be chosen by the programmer to have a strong mnemonic value,  such as +  for ADD and p for PRINT. Reducing these instructions to single characters makes the language very concise.

Thus SIMPL provides a shorthand means to communicate with a microcontroller or other computing device using an interactive command shell.  This allows control the microcontroller and its harware peripherals using short snippets of code, or "microscripts"  typed at the keyboard, or sent as a text file from a terminal emulator.

The interpreter can be pointed to any location in memory to begin parsing SIMPL code from there.  Any location in memory containing a valid, printable ascii character (ascii  $20 to $7E) will result in a jump to a legitimate code block, or trapped as an unused command.

In the case of commands typed from the keyboard, this memory will be an array in RAM that forms the Text Input Buffer (TIB). The code in the TIB will be executed immediately when the parser receives a carriage return character.  This is called Immediate Mode.

The interpreter may also be pointed to a location in RAM containing the User's program. It will begin execution there and will continue until it finds a non-printable ascii character.  This could be a carriage return or a null character.  This is called Run Mode.  A user program might be very short, or it might involve many nested loops - for example printing out a hex-dump of a block of memory.
 
A third mode of operation is possible, where SIMPL code is executed from ROM. This allows commonly used routines to be stored permanently in ROM. Again, the SIMPL code is likely to be more concise than the native assembly language of the microcontroller, as a single byte character represents an instruction or even a complete routine.

A SIMPL Example

SIMPL consists of short snippets of code, which I will call "microscripts".

A microscript consists of a series of commands, normally in lowercase alpha ascii characters, with numerical parameters to define the behaviour of the commands.

For example, to flash a LED on an Arduino, the microscript would be as follows:

13d10{h100ml100m}   - this gives 10 short 100mS flashes of the LED on pin 13

We cam modify this example easily - to play a tone on a small speaker connected to a digital pin. In this case we would use the microseconds delay command u contained within a loop.

8d1000{h500ul500u}   - this gives a 1kHz tone of 1 second duration to a small speaker connected to Digital Pin 8.

The microscripts give a great amount of flexibility, and as they are directly executed out of RAM, they can be edited "on the fly" and pasted back into the command line, to bring about an immediate change in function.

SIMPL was originally designed  to provide an easy means to control the common microcontroller peripherals (for example on Arduino) including UART, digital inputs and outputs, ADC, SPI, I2C etc. Parameters can be interactively passed to the peripheral driver routine, using a single character command to access that routine.  For example if user command S is used to call spi.transfer() SPI data can be sent directly from the keyboard interface.

It provides great flexibilty and can be adapted and extended to a wide range of applications.

The SIMPL Machine

SIMPL forms an instruction set for a virtual machine, running on a microcontroller or any other processing device. I will refer to this VM as the SIMPL Machine (SM).

The SIMPL Machine can be written in a high level language such as C, for portability, or it can be coded directly in the native assembly language of the microcontroller or processing device. The second approach will give a more compact implementation and it can be optimised for speed of execution.

At its minimum, the SIMPL Machine consists of a small interpreter program running on the microcontroller.  Unlike BASIC or Forth, which traditionally may occupy 8 to 16K bytes of ROM, the SIMPL interpreter is a very small kernel of code, only 1 to 2K bytes for a minimum system when written in assembly language. 

If written in C++ on the Arduino UNO, SIMPL may occupy about 7Kbytes of Flash - because of the large and mostly unnecessary overhead of the Arduino built in serial and hardware peripheral libraries.

In some cases, such as with experimental processing devices, there will not be a C compiler available, and so the SM must be written in the assembly language of the processor. 

Once the SM has been thus coded, the programmer can use SIMPL as an alternative to writing in assembly language. The SIMPL interpreter consists of only a few routines, amounting to only a few hundred bytes of code, making it relatively straight forward to code for a new processor.

The SIMPL interpreter provides the Read-Evaluate-Print Loop (REPL) characteristic of an interactive shell program. At its most basic form it consists of three main routines contained within a loop:

txtRead     Get the next character from the RAM buffer, check it's valid (ascii $20 to $7E) or a Return character

txtEval      If the character is a number, evaluate it as a 16-bit interger until a non-numeric character is found otherwise use the ascii value of                       the character to access a look up table or switch-case structure.

process    This is a collection of command actions which execute the function required by the command. Unlike txtRead and txtEval, which                      are of fixed length, this code may be extended from a minimal implementation with a few commands to a fully featured version.

In addition to these routines, there will be others neded for serial input and output, number conversion and number printing.

Extending SIMPL

SIMPL relies on the principle that any ascii character typed in, or read from RAM, can be used to invoke a block of code stored in Flash ROM.

For example, typing h will invoke a block of code that uses digitalWrite(HIGH) - thus setting a port pin HIGH.

Likewise, typing l will invoke a block of code that uses digitalWrite(LOW) - thus setting a port pin LOW.

SIMPL has great flexibility in this respect, and it is up to the User to decide what command is appropriate to invoke a particular function.

Personally, I chose commands that have a high mnemonic value, such as p for print, m for milliseconds and u for microseconds.

Lowercase alpha characters are generally reserved for inbuilt commands that invoke functions coded into the kernel in ROM.

SIMPL has been strongly influenced by Forth, and it uses a reverse polish notation RPN, such that the numerical parameter(s) are placed in front of the command operation. This makes the job of the interpreter much easier, and the code much simpler.

Like Forth, SIMPL has been designed to be an extensible language. Any code snippet or microscript may be allocated a "name" using a technique known as the Colon Definition.

Referring back to the 1kHz tone example above 8d1000{h500ul500u}  we can take this microscript and assign it to a single command. In this case we will use B for Beep. We start with a colon, then the name B, then the code to execute and finally terminate with a semicolon.

:B8d1000{h500ul500u} ;

Any time the interpreter encounters B, it will execute the associated code from the colon definition.

B100mB    - this will give 2 Beeps separated by 100mS

The SIMPL Machine will feature in future Project Logs, exploring how it can be extended to create a fully featured processing device.