24 KiB
mu808: an even more ultralight numeric VM designed to run everywhere
mu808 (always lowercase, pronounced as micro-bob) is a minimalist virtual machine specification that is based on the 1V0 and 808UL ideas but has an even leaner instruction set and is even easier to implement in any modern or even old programming language.
Overview
mu808 is a Harvard-architecture virtual machine with separate memory space for programs and data. The only available data type is a floating point number, although some implementations may use fixed point numbers instead.
Unlike 808UL, mu808 strictly defines support of up to 16384 data cells and up to 16383 program steps, with the program step 0 being unavailable, the data cell 0 always returning 0 and the data cell 127 always returning 1.
On the machine start, both memory areas are initialized with zeroes and the interactive mode is entered which accepts instructions from the platform's standard input.
Overall features
- 16 runtime instructions (from 0 to 15, including NOP as instruction 0);
- ability to enter integer commands (positive and negative) into program memory;
- ability to enter floating (or fixed) point numbers (positive and negative) at program runtime;
- immutability of the data address 0 (must always return 0 when accessed);
- immutability of the data address 127 (must always return 1 when accessed).
The following things are implementation-dependent:
- instruction and data internal representation;
- tracing capabilities;
- runlimit (the amount of program steps before forcefully halting the program).
mu808 instruction set
Unlike 808UL, mu808 only has a core instruction set. Also, regardless of the instruction number, all instructions strictly contain five numbers separated by a whitespace:
[lno]- instruction number,[cmd]- command,[arg1]- argument 1,[arg2]- argument 2,[arg3]- argument 3.
Any mu808 implementation must not distinguish between a whitespace and a newline character: both of them, if supported, must be used to delimit individual parts of an instruction and not instructions as a whole. Instructions as a whole, however, are separated naturally by reading five delimited numbers at a time.
This rule applies to the plaintext format of the machine code. See the section about mu808 file formats to find out about the binary machine code format.
Instruction number semantics
- Any positive integer: the instruction is written into the program memory under this address (any previous instruction at this address is overwritten).
- 0: the instruction is NOT written into the program memory and is immediately executed instead. If the instruction is a jump instruction that jumps into a valid positive address, program execution in the program memory begins until the program memory is exhausted.
- -1: displays a range of instructions from address
[cmd]to[arg1](incl). - -2: if
[cmd]is 0, clears a range of instructions from address[arg1]to[arg2](inclusively), otherwise clears a range of data from address[cmd]to[arg2](inclusively). - -3: if supported, turns off execution tracing output if the
[cmd]value is equal to 0, otherwise turns it on. - -4: if supported, changes the program runlimit to the value of
[cmd]. - -5: if supported, the VM exits to the OS environment, otherwise resets to its initial state (both memory areas filled with zeroes).
For the data entry and display, a runtime command is required instead (see below for details).
Runtime command set
The following commands are required for all mu808 ports and implementations.
The following notation is in place for this list:
A1- address passed as the first command argument;A2- address passed as the second command argument;A3- address passed as the third command argument;V1- numeric value stored at the addressA1;V2- numeric value stored at the addressA2;V3- numeric value stored at the addressA3.
The mu808 runtime command list follows along with their mnemonics.
- 0 (NOP): No operation. The instruction is ignored regardless of parameters.
- 1 (JMP): Jump. This is the most complicated operation in the list, see the next section ("Jump operation semantics") for the full explanation how it works.
- 2 (IAT): Indirect addressing toggle. This instruction overrides the next
instruction by providing
V1,V2andV3as the parameters for the next instruction's command. The actual command parameters provided with the next instruction will be ignored. - 3 (OUT): Output. Print the range of values stored from the address
A1to the addressA2(inclusively) to the port number inA3(0 is the platform's standard output). - 4 (INP): Input. Prompt the user to enter the range of values to be stored from
the address
A1to the addressA2(inclusively) from the port number inA3(0 is the platform's standard input). - 5 (SET): Set value at address. Interpret
A1andA2as two direct integer values and then set the value atA3to(A1 mod 10000) + (A2 mod 10000) / 10000. - 6 (CPY): Direct/indirect value copy. If
A1is 0, then set the value atA3toA2. IfA1is 1, set the value atA3toV2. Otherwise, set the value atV3to the contents of the addressV2(converted to integer). - 7 (FMA): Fused multiply-add. Set the value at
A3toV1 + (V2 * V3). - 8 (SUB): Subtract. Set the value at
A3toV1 - V2. - 9 (DIV): Divide. Set the value at
A3to 0 ifV2is 0, otherwise set it toV1 / V2. - 10 (MDF): Modulo/floor. Interpret
V1andV2as integers and set the value atA3toV1 mod V2ifV2is not 0, otherwise set the result to the integer part ofV1. - 11 (ABS): Absolute value. Set the value at
A3to|V2|. - 12 (SQR): Square root. Set the value at
A3to the square root of|V2|. - 13 (NEL): Natural exponent/logarithm. If the value of
A1is 0, set the value atA3toe ** V2, otherwise set it toln |V2|. - 14 (TRI): Trigonometric function. If the value of
A1is 0, set the value atA3tosin V2, if the value ofA1is 1, set the value atA3tocos V2, otherwise set it toarctg V2. The parameters are given in radians. - 15 (RND): Random number. Set the value at
A3to a random integer number betweenV1andV2(inclusively).
Jump operation semantics
The jump operation (instruction command 1) in mu808 is almost identical to the corresponding operation in 808UL, which, in turn, was directly modeled after the corresponding operation in the 1V0 TZ IV variant. It allows for all kinds of conditional and unconditional, direct and indirect jumps based on three input parameters.
Using the same notation as above, the jump operation semantics is as follows:
- If
V2== 0 andA1== 0, or - if
V2> 0 andA1== 1, or - if
V2< 0 andA1== 2, or - if
V2>= 0 andA1== 3, or - if
V2<= 0 andA1== 4, or - if
V2!= 0 andA1== 5, or - if
A1== 6,
then jump to the address A3.
- If
V2== 0 andA1== 7, or - if
V2> 0 andA1== 8, or - if
V2< 0 andA1== 9, or - if
V2>= 0 andA1== 10, or - if
V2<= 0 andA1== 11, or - if
V2!= 0 andA1== 12, or - if
A1== 13,
then jump to the address V3 (converting it to an integer first).
Note that the order of operands has changed compared to the 1V0/808UL
specifications: here, it's [selector] [value] [jumpaddr] as opposed to
[value] [jumpaddr] [selector]. This has been done for more convenient
jump programming by selecting the condition as the first operand.
Shortcut mnemonics
When writing MU8A assembly (see below), programmers are allowed to use some shortcut mnemonics for easier code readability. Those exist for all jump operations and several other opcodes.
The shortcut mnemonics for the jump operations are as follows:
jeqis a shortcut tojmp 0(direct jump if equals to zero)jgtis a shortcut tojmp 1(direct jump if greater than zero)jltis a shortcut tojmp 2(direct jump if less than zero)jgeis a shortcut tojmp 3(direct jump if greater than or equals to zero)jleis a shortcut tojmp 4(direct jump if less than or equals to zero)jneis a shortcut tojmp 5(direct jump if not equals to zero)jucis a shortcut tojmp 6 0(direct unconditional jump)ieqis a shortcut tojmp 7(indirect jump if equals to zero)igtis a shortcut tojmp 8(indirect jump if greater than zero)iltis a shortcut tojmp 9(indirect jump if less than zero)igeis a shortcut tojmp 10(indirect jump if greater than or equals to zero)ileis a shortcut tojmp 11(indirect jump if less than or equals to zero)ineis a shortcut tojmp 12(indirect jump if not equals to zero)iucis a shortcut tojmp 13 0(indirect unconditional jump)
The shortcut mnemonics for other operations are as follows:
dcais a shortcut tocpy 0(direct constant assignment)dvais a shortcut tocpy 1(direct value assignment)ivais a shortcut tocpy 2(indirect value assignment)incis a shortcut tofma 127 127(increment)mulis a shortcut tofma 0(multiplication)expis a shortcut tonel 0(natural exponent)logis a shortcut tonel 1(natural logarithm)sinis a shortcut totri 0(sine)cosis a shortcut totri 1(cosine)atnis a shortcut totri 2(arctangent)nnnis a shortcut tonop 0 0 0(a short alias for NOP)
Port input and output
For the commands 3 and 4, mu808 supports optional non-zero port numbers, where port 1 is the character output port and port 2 is the character input port respectively. Additional custom ports may be implementation-defined.
File and data formats related to mu808
This specification also defines several file formats that can be used for mu808 programming process.
Plain text machine code format (MU8)
This is the main format to store and enter mu808 programs in. It essentially duplicates what's being entered into the mu808 REPL, with the exception of the negative instruction numbers. Unlike 808UL, it only allows specifying digits and whitespace in the file. Implementations are only required to support the space character (32, 0x20) as the delimiter, but should also support newline, tabs and other kinds of whitespace in the same way.
The recommended file suffix for mu808 plain text machine code is .mu8.
Binary machine code format (MU8B)
Unlike 808UL, mu808 also defines a MU8B binary format to store every instruction in 64 bits of data in the following order (for easier processing):
- 4 higher bits are unused,
- 4 bits are used for the command opcode (0 to 15),
- 14 bits are used for the instruction number (can't be 0),
- 14 bits are used for the command argument 1,
- 14 bits are used for the command argument 2,
- 14 bits are used for the command argument 3.
Hence, every instruction can only contain the number from 0 to 16383 in any of its command arguments, and from 1 to 16383 in its instruction number field.
The recommended file suffix for mu808 binaries is .mu8b or .bin. Implementors are encouraged (but not required) to provide tools for conversion between the binary and text representations of mu808 machine code.
When exporting the entire program memory to the MU8B format, NOPs may or may not be omitted. If a mu808 implementation supports MU8B file import, it must behave the same way as if loading MU8 (plain text) machine code with the same instructions or entering them manually.
Assembly source code file format (MU8A)
In addition to direct machine code in the plain text or MU8B formats, the mu808 VM also allows using an assembly-like language to write programs using labels and the above mnemonics (case-insensitive). The recommended file suffix is .mu8a.
An assembly line looks like this (the optional parts are enclosed in square
brackets): [:lbl] MNEMONIC arg1 arg2 arg3 [;comment]. Labels are optional but
must start with a colon (:) and be on the same line before the instruction
they label. The mnemonics are specified above in the core opcode list.
Here, the exact assembly algorithm is specified step-by-step for each line in the MU8A assembly file to convert it into a plain text based machine code representation:
- Remove all comments (starting with
;until the end of the line). - Replace all jump/function shortcuts according to the above mnemonics.
- Record the current line number N (not counting completely empty lines), starting with 1.
- Split the line into space-delimited fields (1-based numbering as well).
- Check if the first field starts with
:. If so, mark the mapping between the field text and the number N, then remove the field from the set (so that the field 2 becomes field 1 and so on). - If the field 1 is not already a number, replace the field 1 mnemonic text with the numeric opcode if it can be found. If it's not a number and the mnemonic cannot be found, report an error and halt the process.
- Prepend N and the space to the current line and write the result into the target file.
- After the steps 1 to 7 are complete for every line, replace every label occurrence in the mapping with the corresponding number in the target file.
Examples
The examples subdirectory contains several MU8A source code file examples for mu808 (some of which are ports of the same 808UL example programs), namely:
- Compound interest calculator,
- Linear regression calculator,
- Hellorld! (a tribute to @UsagiElectric) (requires I/O port 1 support),
- FizzBuzz classic program (requires I/O port 1 support),
- A simple 10-character echo test (requires both I/O port 1 and port 2 support),
- Bulls and Cows game,
- Lunar Lander game,
- NumberJack port of a Blackjack game, utilizing some advanced techniques (see the comments in the beginning on how to play it).
You can assemble them using any of the reference assemblers provided within the repository, or even by hand (by numbering lines, resolving the labels and replacing mnemonics with corresponding opcodes).
If you just want to test an implementation, assembled MU8 machine code files
(in the plaintext format) are stored in the examples/assembled subdirectory.
After loading into the REPL, you can run each of them with the 0 1 0 0 1 or
0 1 6 0 1 sequence.
Reference implementations
mu808 VM reference implementations
- Python 3 implementation: the first one where all prototyping and initial spec development has been done. Also compatible with MicroPython an tested on it. Supports the entire documented mu808 instruction set, including the two extended I/O ports (requires termios library to work with the character input port correctly on desktop OSes but falls back to a simple stream file read in case the library is not found). Accepts a file name to preload a MU8 (plain text) machine code program from the OS command line.
- ISO C99 implementation: supports the entire specification the
same way as the Python implementation, and also makes use of the optimized
fma()call that appeared in the C99 standard. Accepts a file name to preload a MU8 machine code program from the OS command line. Compile the source with:cc -std=c99 -O2 -s -lm -o mu808 mu808.c - POSIX AWK implementation: supports the entire mu808 specification
sans the (non-portable) I/O port 2. Run it like this:
LC_ALL=C awk -f mu808.awk [- input_program.mu8], where the dash-delimited program file name is optional and used for preloading MU8 program files into VM's memory. The Busybox AWK implementation is compact but quite slow, so use it as a last resort solution when no other programming environment is available. - Web (HTML5/JS) implementation aka mu8web: supports the entire mu808 spec except the I/O port 2 and offers a way to load program files in the same MU8 format via a convenient UI. Uses a convoluted flow to achieve true synchronous input. Since there's no exiting to the OS environment, the -5 instruction number resets the VM instead (by zeroing out both memory areas and the counter). Entry conventions within UI are pretty much the same as for 808UL. Tested on various devices with physical keypads and touch screens. The source code can be found in the mu8web directory of this repo.
Assembler reference implementations
- Python 3: supports assembling MU8A source files into both
plain text (MU8) and binary (MU8B) machine code formats. Also supports
disassembling MU8 and MU8B files into their MU8A sources and converting
machine code between MU8 and MU8B formats. Additionally, this assembler
implementation provides the
t74mode to convert plaintext MU8 machine code into the format suitable for entering into TI-74 BASIC for running on the TI-74 port of mu808 (see below).
These lists are going to be expanded as soon as new reference implementations appear.
Web reference implementation keyboard controls
The virtual keyboard (unavailable on the smallest screens) is duplicated with the following keys at all times in the mu808 Web reference implementation:
0-9: decimal digits input;*(star), comma, dot: decimal point input;- Space,
+,/, down arrow, Call key (if present): whitespace input; - Backspace, Delete, left arrow,
-: minus or backspace input (as the minus sign can only be entered at the beginning of the instruction or data input line); #(pound, hash sign), Enter: newline input;l,L, up arrow, Left Soft key (if present): open a program file loading dialogue.
12-key (DTMF) input method proposal
For hobbyist projects and other environments where all input is limited to 12 keys of the standard phone keypad, the following convention is recommended:
- the
*(star) key is inputting a minus if this is the first position in the entry, otherwise it's inputting a decimal point; - the
#(pound) key actually confirms entering a single number field, and all instructions are required to contain exactly five numbers.
Retro/minimal platform implementations
Here's the list of known/existing mu808 implementations that adhere to the specification to most extent and run compatible machine code but cannot provide full operating environment functionality due to the limits of the platform itself.
TI-74 BASIC
The mu808.b74 file contains a BASIC port of mu808 for the Texas Instruments TI-74 portable computer (tested on the TI-74S variant). Due to the resource constraints, the following limitations apply:
- only 149 program steps and 256 data cells supported (adjustable at line 110),
- no interactive mode (the
RUNcommand directly executes the predefined program in the VM), - the program itself is entered into the DATA statements in the so-called MU74 format (see below),
- no support for tracing or intruction dumping (I/O ports 1-2 supported though),
- no boundary checks for the addresses inside the program.
Also, since the VM runs on top of a BASIC interpreter, program execution is extremely slow most of the time. Keep in mind, this is more of a proof of concept than a viable solution, and using the "native" TI BASIC is preferred for any serious computing on that machine.
In order to store your programs on the device to run via this mu808 VM version,
you need to enter them in DATA statements in two-number pairs starting from
DATA 0,0 at the BASIC line 5000. The last DATA line of the program must be
DATA -1,0. In between these two, every program instruction lno cmd a1 a2 a3
must be compressed into the form:
[5000+lno] DATA [cmd*10000 + a1], [a2 * 10000 + a3]
This storage format is called MU74. For example, the compound interest calculator from the official mu808 examples will transform into:
5000 DATA 0,0
5001 DATA 50100,1
5002 DATA 40002,30000
5003 DATA 90002,10005
5004 DATA 70127,1270005
5005 DATA 130001,50005
5006 DATA 70000,30005
5007 DATA 130000,50005
5008 DATA 30005,50000
5009 DATA -1,0
To make the conversion easier, the official mu808 assembler, mu808asm.py, now
supports the t74 mode that accepts a plain MU8 machine code file and outputs
the same program in the MU74 format.
FAQ
Why another 1V0 derivative that's even code-incompatible with 808UL?
808UL has served its purpose of fulfilling the author's vision on what an 1V0-like (virtual) system should look like. However, its ISA still somewhat lacked rigidity and refinedness in terms of an optimal functionality distribution: there were a lot of instructions that didn't use all three command arguments, there were some instructions that could be parameterized better to avoid functionality duplication, and dedicated port I/O subcommands seemed like a hack on top of already available I/O routines. The mu808 ISA does a pretty good job at polishing those bits while retaining all the main features of the 808UL specification, also adding much stricter constraints on how the operating environment is to be organized.
What can be considered a minimum compatible mu808 VM implementation?
A minimum implementation must implement all runtime instruction commands (from 0 to 15 inclusive) and the interactive loop that accepts the following instruction numbers: any positive number, 0, -1, -2 and -5.
A minimum implementation is required to support floating point numbers as a data type, or, in case it's technically impossible, fixed point numbers with at least two digits after the decimal point (four or more recommended).
808UL supported sparse instructions, is this feature gone in mu808?
No, not at all. It's the MU8A assembly language that doesn't (yet) support this feature and autonumbers instructions based on their position in the source code. When writing machine code directly, you can specify any instruction numbers you like in the program.
Why combine multiplication and addition into a single instruction command?
Because it is faster on modern architectures if both of them are required, and doesn't add any complexity if only one of them is. Additionally, the FMA operation is included in IEEE 754-2008, and all reference mu808 implementations are using it in case the target platform allows them to.
How to do simple addition and simple multiplication, given only the FMA?
It might seem inconvenient at first, but the FMA operation is quite capable. Remember that you already have a constant 0 always stored at the location 0 and a constant 1 stored at the location 127. This will allow you to do any addition and multiplication combo separately even without the shortcut mnemonics:
- Incrementing a memory location is as easy as
fma 127 127 [loc]. - Adding two numbers at loc1 and loc2 into loc2:
fma [loc1] 127 [loc2]. - Multiplying two numbers at loc1 and loc2 into loc2:
fma 0 [loc1] [loc2].
The only inconvenience with the FMA operation is that you need to sacrifice one of the operand locations to store the result. If you want to fully emulate the 808UL's behavior, then you have to copy one of the operands into the target memory location first and then use it as the last parameter to the FMA command.
Why is the "Hellorld!" example much shorter than in 808UL?
Because mu808 unifies the standard and port I/O, thus allowing to specify a range of memory locations to the port output routine as well. Since the example arranges all the message bytes in order, a single call to the output command will suffice.
Credits
Created by Luxferre in 2025, released into public domain with no warranties.