_____ ______ ___ ___ ___ ___ ___ _______ ________
|\ _ \ _ \|\ \|\ \ |\ \ / /|\ \ |\ ___ \ |\ __ \
\ \ \\\__\ \ \ \ \\\ \ \ \ \/ / \ \ \ \ \ __/ \ \ \|\ \
\ \ \\|__| \ \ \ \\\ \ \ \ / / \ \ \ \ \ \ _\ \ \\\ \
\ \ \ \ \ \ \ \\\ \ / \/ \ \ \____\ \ \_|\ \ \ \\\ \
\ \__\ \ \__\ \_______\/ /\ \ \ \_______\ \_______\ \_____ \
\|__| \|__|\|_______/__/ /\ __\ \|_______|\|_______|\|___| \__\
|__|/ \|__| \|__|
MUXLEQ is a minimalist esoteric machine. Its core is two instructions: the
classic SUBLEQ plus a multiplexing (MUX) operation that adds single-instruction
data movement and boolean logic. A small, disciplined set of native primitives is
layered on top by encoding them in otherwise-unused operand values; today that is
a single right-shift op. The result runs faster and in fewer cells than pure
SUBLEQ, and this project ships a complete, self-hosting development environment
for it.
MUXLEQ is a 32-bit-cell, cell-addressed VM. With no argument it runs the
self-hosting eForth image; given a FILE it loads and runs that standalone
MUXLEQ image instead: ./build/muxleq image.dec.
This repository contains a full toolchain for the MUXLEQ architecture:
- An assembler for the MUXLEQ instruction set.
- A virtual machine built upon the assembler.
- A cross-compiler that targets the VM with a version of the eForth language.
rvopt, a standalone RV32I-to-MUXLEQ compiler for wide native MUXLEQ images.
SUBLEQ is a Turing-complete One-Instruction Set Computer (OISC). Running a high-level language on one is a demonstration of computational minimalism, and this project is a platform for exploring how far that goes. The system is self-hosted: the eForth environment compiles new versions of itself from source, so it can be modified and extended from within.
eForth on MUXLEQ runs this same VM and this
same eForth image in a browser, as an interactive tutorial with a live stack
viewer, a 24x24 display mapped onto Forth memory, and a playable snake. Its text
is adapted from Easy Forth by Nick Morgan and
rewritten for a real eForth: the words this system actually has, the throw codes
it actually produces, and a closing chapter on the two instructions everything
above it is built from. The sources are wasm/, and a push to the default branch
that touches them republishes the page.
Building locally needs a C compiler, Gforth, and GNU Make.
- macOS:
brew install gforth - Ubuntu/Debian:
sudo apt-get install gforth build-essential
$ make run # build build/muxleq, then start the eForth interpreterForth calls its commands "words," and a session is just words applied to a stack:
words \ list every word in the dictionary
21 21 + . cr \ RPN: push 21, push 21, add, print "42"
: hello ." Hello, World!" cr ; \ define a word
hello \ run it by naming it
bye
make checkis the pre-commit gate: byte-exact golden output, theseeand PTY-editor goldens, eForth smokes, CSR/timer lowering, RV32I and RTOS coverage, the WebAssembly build, and the self-hosting bootstrap.make check-alladds wide native-image emission, loader rejection, the prebuilt-release contract test, differentialrvoptfuzzing, static analysis (clang--analyze, cppcheck, shellcheck, all clean), and ASan/UBSan. Members whose tools are absent (emcc, node, headless Chrome) skip rather than fail.make helplists every target;make benchtimes the eForth kernels and rvopt-lowered DureMark.rvoptis a standalone ahead-of-time compiler that lowers an RV32I ELF32/flat binary to a native MUXLEQ image running on the two ops directly, with no interpreter layer:rvopt mux prog > prog.dec, then./build/muxleq prog.dec. Two opt-in flags serve kernel-style code,--indirectfor computed jump targets and--timerfor block-boundary timer interrupts, which is what the RTOS experiment intests/rv32i/rtos/runs on. Seedocs/rvopt-native-muxleq.md.- RV32I test programs: freestanding RISC-V demos, benchmarks, and the official
rv32ui conformance suite live in
tests/rv32i/. DureMark asserts a deterministic checksum, so it gates rvopt lowering and VM execution end to end.make check-rv32ibuilds them from source when a bare-metalriscv-none-elf-*toolchain is installed, such as the xPack GNU RISC-V toolchain, and otherwise falls back to prebuilt images, so no cross toolchain is required. Those come from the rollingrv32i-latestpre-release, which CI republishes from the default branch whenever the payload changes, with per-file digests inrv32i/MANIFEST.sha256; every run also uploads arv32i-binariesartifact. docs/manual.mdis the reference manual: the instruction set, memory image and self-modifying-operand rules, the build/bootstrap pipeline, the interpreter, and the eForth environment.
The MUXLEQ architecture extends the classic SUBLEQ OISC with a second instruction to improve performance without significantly increasing implementation complexity. Existing SUBLEQ programs are generally compatible with MUXLEQ.
A SUBLEQ instruction consists of three operands, a, b, and c,
which are addresses pointing to memory locations.
a b c
The instruction performs the following operation:
# Pseudo-code for a single SUBLEQ instruction
Mem[b] = Mem[b] - Mem[a]
if Mem[b] <= 0:
pc = cSpecial operand values trigger I/O or halt the machine:
- Input: If
ais -1, a byte is read from input and stored at the addressb. - Output: If
bis -1, the byte at addressais sent to the output. - Halt: a taken branch to a negative address halts the machine: the program
counter itself goes negative. By convention the halt target is -1 (
Z, Z, -1).
MUXLEQ adds a multiplexing (MUX) instruction by encoding it into the c operand.
If c has its high bit set but is not -1 (0xffffffff, which stays the
halt/branch target), the MUX operation is performed instead of a branch.
This avoids needing a separate opcode, preserving the simple a b c instruction format.
The core MUXLEQ logic is as follows (one reserved mask value is additionally dispatched as a native shift; see "Native primitives and their limits" below):
# Pseudo-code for the MUXLEQ virtual machine; cells are unsigned 32-bit
while not (pc & 0x80000000): # run until the PC's high bit is set (halt)
# every Mem[] index below is masked into the bounded host arena
a = Mem[pc + 0]
b = Mem[pc + 1]
c = Mem[pc + 2]
pc += 3
if a == 0xFFFFFFFF: # -1: input
Mem[b] = get_byte()
elif b == 0xFFFFFFFF: # -1: output
put_byte(Mem[a])
elif (c & 0x80000000) and c != 0xFFFFFFFF: # high bit set: MUX or a native escape
mask_addr = c & 0x7FFFFFFF # low 31 bits address the mask cell
if mask_addr == 0x7FFFFFFE: # reserved: native shift-right-by-1
Mem[b] = Mem[a] >> 1
else:
mask = Mem[mask_addr] # cell 6 is the zero register: a zero mask is a MOVE
Mem[b] = (Mem[a] & ~mask) | (Mem[b] & mask) # Multiplex
else: # SUBLEQ
Mem[b] = Mem[b] - Mem[a]
if Mem[b] == 0 or (Mem[b] & 0x80000000): # result <= 0 (signed)
pc = c # BranchMUX with constants 0 and -1 implements any boolean function: AND selects on
the second operand, OR on the complement of the first, NOT swaps the true and
false values, and XOR combines several MUX steps. Each takes dozens of
instructions in pure SUBLEQ. A mask of 0 likewise gives a single-instruction
MOVE in place of SUBLEQ's multi-instruction copy sequence.
Because MUX is a same-lane selector, though, it cannot move a bit between positions (it cannot shift). That gap is what the native-primitive mechanism below fills.
Further instructions are encoded in otherwise-unused operand values: a MUX whose
mask address is a reserved, out-of-range value is dispatched as a native op
instead. The machine reserves exactly one such value today, a right shift
(Mem[b] = Mem[a] >> 1), which the eForth shift word uses in place of a
bit-serial loop.
That one op is not arbitrary; it marks the boundary of what belongs in the ISA.
The core is cheap at same-lane logic (MUX), arithmetic and branching (SUBLEQ), and
upward bit movement (a left shift is just x + x, since a carry propagates low
to high). Moving a bit the other way, downward, it can do only through a
bit-serial loop, so a right shift is the single primitive that turns that loop
into one step. Everything else is a composition of it: a variable shift is a loop
of right shifts (a barrel shift does the same in one step but adds no new
capability), multiply is shift-and-add, divide is shift-and-subtract, all in
software. Native byte load/store are deliberately excluded too: on a cell-addressed
machine they would bake a byte-packing convention into the VM. (A comparison op would not
qualify either: SUBLEQ already subtracts and branches, so it is no gap.) The one
further candidate that does fit the rule is a bit-reversal, genuinely
cross-lane, which the core cannot do, and per "Subleq: An Area-Efficient
Two-Instruction-Set Computer" an
efficient route to arithmetic shifts, were a workload ever to justify it.
The Forth environment provided is a variant of eForth, designed by Bill Muench and C.H. Ting for portability and efficiency. It is implemented with a small set of assembly primitives, making it ideal for unconventional targets like MUXLEQ.
The cross-compiler source lives in the numbered forth/*.fth modules, which
concatenate in order into the generated build/muxleq.fth, a Forth program
that translates eForth source into a MUXLEQ memory image. Edit the modules, not
the generated file. The cross-compilation proceeds in four stages:
- Assembler: define the MUXLEQ machine-code primitives.
- Virtual machine: build a VM layer over the assembler for high-level Forth.
- Forth dictionary: define the core words.
- Image generation: write the final memory image to standard output.
The system's correctness is validated through a self-hosting build process, often called "meta-compilation" in the Forth community. If a compiled system can recompile itself and produce a byte-for-byte identical output, the compiler is considered correct.
This validation can be run with a single command:
$ make check-bootstrapThis command performs the following steps, all under build/:
- Concatenate the
forth/*.fthmodules intobuild/muxleq.fthand run it through Gforth to produce the first image,build/stage0.dec. - Compile the VM (
cc -Ibuild -o build/muxleq muxleq.c), which#includes the generatedbuild/stage0.c. - Run
build/muxleqonbuild/muxleq.fthto produce a second image,build/stage1.dec. - Compare the two images.
If build/stage0.dec and build/stage1.dec are byte-for-byte identical, the
bootstrap succeeds.
MUXLEQ is available under a permissive
MIT-style license.
Use of this source code is governed by a MIT license that can be found
in the LICENSE file.
It was originally written by Richard James Howe.