MINT - a Minimal Interpreter for resource limited cpus

Way back in 2013, when I started working with the STM32 ARM on a motion control project, I needed a simple serial connection so that I could control the fledgling product.

I settled for a very simple set of commands, each using a capital letter, and a single numerical parameter, so 100U would move the machine head 100mm up and 100D would move it down by the same amount.

As the project developed, more commands were added, again with capital letters, chosen for their mnemonic value.

About the same time, I read about the first Cambridge EDSAC which had an instruction set based on the 5 bit baudot code used by punched tape teleprinters, again using upper case letters for their mnemonic value.

So I pondered whether I could create a very lightweight programming language using single character ascii commands, chosen for their mnemonic value, brevity and human readability.

So I pottered along with some ideas and created a very simple interpreter, coded in C which allowed me to sequence a train of commands sent to the STM32 hardware. It provided basic interaction with the hardware from a serial terminal application running on a laptop.

Having had a little experience with Forth, my ideas converged on a Forth-like RPN language. But as a lazy programmer, I wondered if I could throw away 75% of Forth and still have something of value. So out went the dictionary and the multi-character parsing that went with it. The compile mode and 16 bit addressing were also ditched, leaving a very simple bytecode interpreter that used the printable ascii character set as it’s instruction set.

30 instructions, chosen from appropriate mathematical, logical and punctuation characters.

26 user routines identified by a single uppercase letter

26 user variables identified by a lowercase letter.

If this is starting to sound familiar, it is the Tiny BASIC equivalent of Forth. Unlike VTL that has features in common with BASIC, the MINT interpreter has a lot in common with RPN stack-based languages.

Minimalist languages have been around since the start of the electronic computing era. I was delighted to discover that way back around 1969, a lightweight character based interpreter called MUSYS was written for the PDP-8, by Peter Grogono, in order to simplify the task of electronic music composition. He later evolved this into MOUSE - a language for microcomputers around 1980. Having recently bought a copy of Grogono’s book to obtain the source code, I can see that MINT is very close to MOUSE in functionality, but about 20% smaller in terms of source code.

MINT is also quite similar in nature to desk calculator (dc).

MINT uses a look-up table that converts the 96 printable ascii characters into a jump address to a page of memory that contains the primitive routines, and the routines to handle numbers and variables.

The most commonly used routines (arithmetical and logical functions) are accessed on a single 256 byte page, less frequent routines have an intermediate jump to allow access to the next page. This method was chosen to provide the fastest means of accessing a routine on most 8-bit processors. Arithmetic is 16-bit integer and there are routines to accept numbers in both decimal and hexadecimal.

MINT is a very terse language with only 30 primitive instructions. These provide arithmetical and logical instructions, loops, arrays and conditional branching.

There is a data stack and a return stack, just like Forth. However, there are 26 user “registers” available, and after some months of using MINT, it became apparent that using these registers for temporary storage can save a lot of the stack juggling operations that are so common in Forth.

In 2021 I collaborated with John Hardy and Craig Jones, both from Melbourne Australia and we created the first fully documented version of MINT. Initially for the Z80, as John created the TEC-1 Z80 SBC, way back in 1983, and MINT provides an alternative means to program the TEC-1 from a serial terminal other than in Z80 hex codes.

We are hoping to port MINT to several of the classic 8-bit micros, including 6502, 1802, 6800/6809 as well as PIC and AVR and 8051. We are also looking at a cut-down version of MINT providing the bare minimum for a 16-bit VM in about 1Kbytes.

There is also a C simulator for the MINT interpreter which runs to about 80 lines of fairly dense code.

Looking forwards, we see MINT as being a very lightweight 16-bit virtual machine, which can be easily implemented on any resource limited cpu.

It’s bytecode instruction set is human readable and we see it as a means of passing small programs between systems that do not necessarily share the same CPU. Some might call this a security nightmare, but I look at it as an opportunity.

I have a colleague in Malaysia who deals with commercial greenhouse automation systems using distributed 8051 based control nodes. MINT can provide a command shell to update the system to provide a control environment that will respond to variations in temperature, lighting and mositure/humidity.

On github you can find MINT here:


Thanks for the new topic, @monsonite (previously MINT was mentioned here) - it does look very interesting to me, a small Forth with a small portable virtual machine. Might yet be applicable to one or other of the OPC machines!

I will publish the C simulator in due course. I look at MINT as a simple low overhead tool that gets you out of cpu specific machine code.

For 6502 Gurus, that can port VTL from 6800 to 6502, the mechanics of MINT will be trivial.

I’m still looking for an 1802 Guru, because my 1802 SBC is not yet working.

When we met in Cambridge in 2017 it was little more than an idea.
John Hardy and Craig Jones have been instrumental in putting flesh onto the bare bones.

1 year on, I can say that it is not a user friendly language for beginners because of its RPN and very terse commands.

So we are now looking at ways to use it as a tiny VM in under 1K bytes, for code portability, brevity and simplicity. We might even end up with a python app that spits out MINT code.

I took on the last 2 Christmas challenges - just to prove to myself that MINT can be viable for these simple applications.

Sometimes I play with Arduino compatible boards, but when it took 22K bytes on the Teensy 4 just to flash an LED, I felt that there must be a better way.

I wonder, does it feel a bit like programming an RPN calculator? I imagine it might.

Most of the time you get stuck on a function to manipulate data, but not usually on simple maths.

This one converts an 8 bit number on the stack into individual bits so as to print out graphics for the Christmas Tree challenge last year.

:S 32\E;
:X 42\E;
:Q "(42\E)0=(32\E);						
:P b!8(128b@&128=Qb@{b!);

S prints a space, X prints an asterix, Q and P work together to analyse a number bit by bit to decide whether to print space or star. b is a local variable and { is a left shift

Like I said earlier, it is very terse.


I know it’s a stretch (and not a dis of MINT!), but this reminds me of TECO, which started life as a powerful text editor, but became a programming language in its own right, most famously for being the implementation language of the first EMACS. TECO didn’t stop at printable characters—there weren’t enough of those—and quickly added control characters to its command set. Formatting for readability? No. Indentation? Maybe. And so TECO programs gained a playful reputation among its fans as resembling “line noise,” which many of us are familiar with from the TTY days.

1 Like

Actually it reminds me of just about every intermediate code for a portable compiler.For example:

! OR               G  ALIAS            c MCODE
" JUMPIFD          H BEGIN             d DIM
# BNE              I unused            e EVENT
$ DEF              J JUMP              f FOR
% XOR              K FALSE             g unused
& AND              L LABEL             h ALTBEG
' PUSHS            M MAP               i INDEX
( unused           N PUSHI             j JAM
) unused           0 LINE              k RELEASE
* MUL              P PLANT             l LANG
+ ADD              Q DIVIDE            m MONITOR
- SUB              R RETURN            n SELECT
. CONCAT           S ASSVAL            o ON
/ QUOT             T TRUE              p ASSPAR
: LOCATE           U NEGATE            q ALTEND
; END              V RESULT            r RESOLVE
< unused           W SJUMP             s STOP
= unused           X IEXP              t unused
> unused           Y DEFAULT           u ADDA
? JUMPIF           Z ASSREF            v MOD
@ PUSH             [ LSH               w SUBA
A INIT             \ NOT               x REXP
B REPEAT           ] RSH               y DIAG
C JUMPIFA          ^ PROC              z CONTROL
D PUSHR            _ SLABEL            { START
E CALL             a ACCESS            | ALT
F GOTO             b BOUNDS            } FINISH

(the opcodes being explained in I-code V1.3 Working-Notes )