Having reached the point of getting the Suite-16 simulator to run "Hello World!" and print ascii characters to the serial terminal, I have paused for thought for a couple of days to contemplate my next step.
I started reading up on various Forth implementations, and realised that I was not quite ready to take on the complexity, but set myself an easier task, to achieve motivation and more experience working with this new alien instruction set that I created in barely a week.
A typical Forth implementation can be between 4K and 8K bytes, and so I am looking for something a lot simpler to test the instruction set - and fortunately I have the very thing in my toolbox.
Back in May 2013, I came across Txtzyme by Ward Cunningham. Txtzyme is described as a nano-interpreter for DIY Domain Specific Languages. This might sound a bit complicated, so I prefer to call it a tiny-Forthlike language with a kernel that will fit into about 1K bytes.
Txtzyme consists of a case statement contained within a loop, which is more or less exactly what my Suite-16 simulator consists of.
Txtzyme receives serial ascii characters one at a time from an input buffer and uses them to invoke certain functions. Originally Txtzyme only had 13 commands - and these were generally used to control the I/O found on the Arduino board.
I very quickly realised that there are 96 printable ascii characters, and all of them could be used as an individual comma commands. All of a sudden we have an instruction set for a virtual machine with up to 96 instructions.
So starting with the basic Txtzyme interpreter commands, I added math functions, conditional branching and most importantly I borrowed from Forth the idea of the colon definition. I now had the basis of a tiny extensible language - so I gave it a name: SIMPL (serial interpreted minimal programming language) - because everybody enjoys a corny acronym (not).
I developed a version of SIMPL in C++ using the Arduino IDE and ported it to several popular microcontrollers including AVR, ARM and MSP430. In fact almost all of the microcontroller boards I have developed in the last 5 or 6 years - SIMPL is usually the first thing I load onto them.
SIMPL is my own private indulgence, it's not a proper grown-up language and probably never will be. It's an old, familiar friend who assists me during the "bring up" phase of any new hardware I have developed.
SIMPL is a serial command interpreter - if you receive this command, invoke this function then return to the interpreter loop.
Some more ancient history (dull but relevant)
When porting SIMPL to the MSP430 LaunchPad using Energia - I rapidly became aware that the Arduino built in functions - such as Serial.print() and Digital.write() were slow and very inefficient in their use of program space. I decided to write my own functions for serial input and output (getchar and putchar) and also for manipulating the digital I/O.
By using my own routines I managed to shave thousands of bytes from the code size needed for SIMPL. At that time Hackaday was running the "1K Byte Challenge" for the best application written in 1K bytes of code. Whilst I didn't enter, it inspired me to learn sufficient MSP430 assembly language - so I could implement SIMPL in assembly. Most of this happened over the Christmas break of 2016, so by early January 2017 I had a usable version of SIMPL running in under 1K bytes of MSP430 assembly.
So almost 3 years on, I have the need to port SIMPL to an unfamiliar new target - Suite-16. Fortunately, Suite-16 is a 16-bit machine and quite a lot of the addressing modes of the MSP430 have been adopted.
Whilst the Suite-16 instruction set does not allow inter-register moves or operations - the Accumulator always forms one operator, it does have sufficient mechanics to implement the SIMPL language.
What is Needed?
SIMPL is implemented using just three top level routines which form the interpreter:
textRead: Get a serial character from the UART and store it in the input buffer
textCheck: Check if the character is a colon : - if so store the remainder of the line of text at a specific address
textEval: Use the ascii value of the character to index into a jump table and pick up the address of the code to be executed
So for example if I type p followed by return, the ascii value of p (0x70) is used to locate a value in the jump table which is used as the address where the routine associated with p is located. This code is then executed and program control returns to textRead. because we are dealing with single ascii character commands - we can choose them to have a high mnemonic value. p was chosen to stand for printnum and the code associated with it prints the top member of the stack as a decimal number to the serial port.
With 96 printable ascii characters I had to devise a plan to keep things memorable.
Lower case characters would be used for some native function that was included within the SIMPL kernel.
Upper case characters would be used primarily for User Defined functions
Symbols such a punctuation marks and arithmetic characters would be used to invoke instruction primitives
In order to automate the mechanics of SIMPL, I needed a few basic helper functions.
As we are dealing with (only) serial input and output, I needed the equivalent of getchar and putchar.
Next I needed the ability to interpret a sequence of ascii characters and see if they were numerical digits. If so, the string would be converted to a 16-bit number and placed on the stack. This function is called "number".
For numerical output, I needed the reverse process - take the contents of the top of stack and print it out in ascii as a decimal number. This routine is called printnum - described above.
With the 3 routines that form the interpreter, and the helper routines, the rest of SIMPL becomes one large jump table that access a bunch of functions.
Implementing SIMPL on Suite-16
Well that's my task for the rest of this week. I will start by coding "number" and "printnum" in Suite-16 assembler, and when I get these working and fully tested, I can start on the routines that form the interpreter. With the interpreter core and jump table in place, I can start writing as many of the functions as I think I need. Maths functions, text string output and the program flow structures such as looping will probably be the first to be coded.
I have blogged a lot about SIMPL over the years - most of it unread.