ChecksMix - Blazing Fast MMIX Emulator
MMIX assembler and emulator with fast feedback for learning, experimenting, and debugging Knuth’s 64-bit machine. MIX source still parses and emulates, but the checksmix now focuses on MMIX with .mms and .mmo workflows.
Try it in your browser: playmmix.2ad.com. A playground built on this crate — edit MMIX assembly, run it, step through with breakpoints, and watch the registers and memory change. No toolchain to install.
What’s inside
checksmix: execute.mmsassembly directly or run prebuilt.mmoobject files.mmixasm: assemble.mmsto.mmofor reuse or distribution.mmixdb: interactive source-level debugger for.mmsprograms (step, breakpoints, print, Emacs GUD mode).- Emulator: 256 general-purpose registers, 32 special registers, sparse 64-bit address space, and basic TRAP support (Halt and Fputs for console output).
Quick start
- Install Rust (stable toolchain).
- From the repo root, run an example immediately:
Or build once for repeat runs:
Set RUST_LOG=checksmix=debug to see instruction decoding and TRAP handling while you experiment.
Common workflows
Run MMIX assembly directly
checksmix parses and executes .mms without producing an object file:
Assemble to MMO, then emulate
Generate a reusable object file with mmixasm, then run it with checksmix:
# Assemble
# Execute the MMO
Use your own program
- Write MMIX assembly (see the snippet below).
- Run it directly with
checksmixor assemble withmmixasmand run the resulting.mmo. - Inspect register and memory dumps printed before and after execution.
Example programs
examples/exit_code.mms: two instructions.TRAP 0,Halt,0returns$255as the process exit status.examples/hello_world.mms: prints a string viaTRAP 0,Fputs,StdOut.examples/function.mms: the MMIX calling convention.PUSHJslides the register window so the caller's$X+1,$X+2arrive as the callee's$0,$1, andPOP 1returns a value to the caller's hole.examples/fibonacci.mms: that convention applied to an iterativefib(20).examples/remeuclid.mms: Euclidean remainder over eight cases, accumulating a pass bit-field in$20.examples/linked_list.mms: walks a statically allocated list and sums node values.examples/time.mms: reads the host clock throughTRAP 0,Time,2.examples/all_instructions_test.mms: broad instruction coverage for regression checks.examples/example.mix: a MIXAL program, for the legacy MIX path below.
Hello World (trimmed):
LOC Data_Segment
GREG @
Text BYTE "Hello world!",'\n',0
LOC #100
Main LDA $0,Text
TRAP 0,Fputs,StdOut
TRAP 0,Halt,0
mmixdb — the interactive debugger
mmixdb is an interactive source-level debugger for MMIX .mms programs: step, breakpoint,
inspect registers/memory, and see the current source line as you go.
mmixdb handles .mms sources only -- source-line debugging requires the
original source. .mmo object files carry no source map and are out of scope.
--fullname is auto-enabled when the INSIDE_EMACS environment variable is
set (i.e. when run from Emacs's gud-mode).
| Command | Forms | Semantics |
|---|---|---|
| step (into) | s, step |
Execute one source line, following into calls/branches. |
| next (over) | n, next |
Execute one source line, stepping over any call it makes. |
| stepi | si, stepi |
Execute exactly one instruction, following into calls/branches. |
| continue | c, continue |
Resume, single-stepping until a breakpoint or halt. |
| run/reset | r, run |
Reset to the freshly-loaded image, then run on; a breakpoint on the entry point fires. |
| break | b <line>, b <label>, break … |
Set a breakpoint at a source line or label. |
p <arg>, print <arg> |
Print a register ($N/N), special register (rJ, rA, ...), label address, IS/GREG symbol, or memory octa (0x.../#...). |
|
| state | bt, backtrace, info reg, info registers |
Print the full register dump. |
| list | l, list |
Print source lines around the current PC. |
| help | h, help, ? |
Show this help. |
| quit | q, quit, exit |
Exit the debugger. |
Blank input repeats the last command -- most debugging is stepping. Once the
program has exited, step, stepi, next and continue are refused; run
restarts it.
Emacs users: contrib/mmixdb.el provides M-x mmixdb under gud-mode. Put
contrib/ on load-path and require it:
(add-to-list 'load-path "/path/to/checksmix/contrib")
(require 'mmixdb)
or autoload it instead:
(autoload 'mmixdb "mmixdb" "Run mmixdb under gud-mode." t)
Legacy MIX support
.mix and .mixal files still run through checksmix, but MMIX is the primary target. Prefer .mms/.mmo for new work.
Using checksmix as a library
checksmix is usable as a library, independent of the three binaries above. The
clap, rustyline, and tracing-subscriber dependencies the CLIs need live behind
the cli feature, which is on by default. A library-only consumer — notably one
targeting wasm32-unknown-unknown, where rustyline does not build — turns it off.
Available from 0.3.0; earlier versions have no features and always pull the CLI tree.
playmmix is built this way, on checksmix = { version = "0.3", default-features = false }, and runs the emulator in the browser as wasm.
# default — library plus the CLI dependency tree
= "0.3"
# library only
= { = "0.3", = false }
Either form gives you the library. The checksmix, mmixasm, and mmixdb
executables come from cargo install checksmix, not from a [dependencies] entry.
Capturing what a program emits
By default an MMix writes to the process's stdout and stderr. Implement
Host to intercept that instead — what the program writes, the clock behind
the Time trap, diagnostics, and every recognized TRAP:
use ;
use RefCell;
use Rc;
;
let out = new;
let mut mmix = with_host;
Clone the buffer handle before moving the host in — with_host consumes it
and hands back no way to reach it again.
Debugger::load_with_host takes a host the same way, for programs you want to
step through rather than run straight out. Debugger::load installs StdHost,
so a debugged program's output goes to the process and never reaches you.
An MMix holds its host as Box<dyn Host> and so is none of Send, Sync,
UnwindSafe, or RefUnwindSafe; a Debugger holds an MMix and inherits that — a change from 0.2.23, where it was all
four. Construct one on the thread that runs it, and wrap it in
std::panic::AssertUnwindSafe to put it through catch_unwind.
Learning MMIX
MMIX is Knuth's own "pretty clean" machine architecture, and he documented it himself:
- Knuth's MMIX page — the canonical home: design rationale, current news, and why MIX was retired.
- MMIXware: A RISC Computer for the Third Millennium — the complete specification, with Knuth's own assembler and simulator (Springer LNCS 1750, 1999).
- The Art of Computer Programming, Volume 1 Fascicle 1, MMIX: A RISC Computer for the New Millennium (2005) — the instruction set as Knuth teaches it, and the shortest path in.
- The MMIX Home Page — Martin Ruckert's collection at Munich University of Applied Sciences: documentation, sources, binaries, worked examples, and The MMIX Supplement.
checksmix follows the same instruction set, so a program written from any of these
runs here — and in playmmix without installing anything.
Tribute
Donald Knuth has been one of the formative influences in my career. Early on—as a junior developer just beginning to feel like a mid-level engineer—I implemented his external, file-based merge sort to collate insurance datasets that were far too large for memory. That experience taught me alot about how to think about programming and system design.
Knuth’s blend of rigor, playfulness, and generosity has shaped how I write code and how I view the craft of software. Some time later, I submitted a “bug” in The Art of Computer Programming- to earn the coveted Knuth “hexadecimal dollar.” His reply was short and perfect:
“e is as real as any other number.”
Evidently!
This project carries a little of that spirit forward: curiosity, precision, and the belief that programming can be serious fun.