PlayMMIX: MMIX in the Browser

Posted on Sat 05 September 2026 in tech

The 2ad MMIX playground, playmmix, puts an MMIX machine in your browser.

MMIX is Knuth's 64-bit RISC architecture. I built checksmix to assemble and run programs for it. playmmix wraps that same engine with an editor and a live debugger, in Rust, Yew and WebAssembly.

Editing the source re-assembles and reloads the machine at its entry point, the same way mmixdb does. Nothing executes until you click Run.

playmmix stepping through the primality test on 97: $1 takes N, Next runs IsPrime, and the program halts with exit 0

Driving the MMIX machine

Six controls drive execution. Each button's bold amber first letter is its keyboard shortcut:

  • Run (r) starts over from the top, wherever the program is paused, and runs until it halts or hits a breakpoint.
  • Continue (c) resumes a paused run in place: it executes the instruction at the PC, then runs until the program halts or hits a breakpoint.
  • Step (s or F11) executes one line, following a call into the subroutine and stopping at its first line.
  • Next (n or F10) executes one line. On a call it runs the whole subroutine and stops at the line after the call, unless a breakpoint or halt stops it first.
  • Interrupt (i) pauses a Run, Continue or Next in progress.
  • Reset reloads the current source, clearing output and change highlights and keeping breakpoints. It has no key.

On desktop the controls sit top left. On a phone they move to a bar pinned to the top of the screen, and on a full-screen iPad to the bottom.

Click a line number to set a breakpoint on an instruction. The machine pane highlights values that changed since the previous pause. Step once and see what moved; Run to a breakpoint and see what changed along the way.

Testing a small prime

Trial division tries D = 2, 3, 4, … against N. If D divides N, N is composite. If D * D exceeds N first, N is prime.

Paste this into playmmix:

        LOC     #100
Main    SETL    $1,97            # N
        PUSHJ   $0,IsPrime       # $0 = 1 if prime, 0 if not
        XOR     $255,$0,1        # exit 0 if prime, 1 if not
        TRAP    0,Halt,0

IsPrime CMPU    $1,$0,2
        BN      $1,NotPrime      # 0 and 1 are neither prime nor composite
        SETL    $1,2             # D
Loop    MULU    $2,$1,$1
        CMPU    $2,$2,$0
        BP      $2,Prime         # D * D > N
        DIVU    $2,$0,$1
        GET     $2,rR            # N mod D
        BZ      $2,NotPrime
        ADDU    $1,$1,1
        JMP     Loop
Prime   SETL    $0,1
        POP     1,0
NotPrime SETL   $0,0
        POP     1,0

Click Run. The program exits 0, prime: the loop tries 2 through 9 and stops at 10, since 100 exceeds 97. Change 97 to 91 and run again: it exits 1, composite, found at 7.

Comments follow Knuth's remark specification, from The Art of Computer Programming, Volume 1, Fascicle 1, Section 1.3.2. An MMIX statement is a LABEL, an OP and an EXPR. Everything to the right of the EXPR is a comment unless it starts with a digit or an operator. The # above carries no meaning; it gives the eye something to lock on.

Following the call

Put 97 back and click Reset. One click on Step loads N into $1. The next instruction is PUSHJ $0,IsPrime.

Click Step again. We're at IsPrime, call depth is 1, and N is in $0.

Set a breakpoint on Loop and click Run. Run starts over from the top and pauses at Loop; $1, which holds D, reads 2. Each Continue stops at the next pass. D climbs and N stays put; at 10, 10 * 10 passes 97 and BP goes to Prime.

Reset, Step once and press Next on the PUSHJ. Execution stops at Loop, the breakpoint inside the call. Click the line number to remove the breakpoint and Continue: IsPrime returns and the program halts. Now Reset, Step once and press Next on the PUSHJ again. With no breakpoint set, Next runs IsPrime to its return and pauses at the XOR with $0 = 1.

Getting home

PUSHJ writes the return address to the special register rJ. POP 1,0 returns one value, placing the callee's $0 in Main's $0, the register its PUSHJ named. The MMIX instruction reference has the full register-stack rules.

Under the hood

checksmix is the assembler and the emulator: registers, memory and TRAP handling. playmmix uses its public MMixAssembler and MMix APIs. The editor, execution controls and machine display live in playmmix.

Re-assembly pauses four tenths of a second after typing stops, so an unfinished line doesn't flash an error on every keystroke. Continue, Step and Next flush any pending edit before executing; Run starts over from the current source. A successful assembly starts a fresh machine.

Next single-steps the callee itself, checking the call depth after every instruction until execution returns to where it started. A breakpoint inside the callee fires under Next because Next makes the same per-instruction check as Run and Continue.

Long runs execute in bounded chunks, yielding to the browser between them. That keeps Interrupt available, including while Next is working through a call. The arithmetic runs locally in WebAssembly.

For .mmo object files or a shell, use checksmix, also available on crates.io. Its command-line debugger, mmixdb, has its own worked example.

Open playmmix, paste the program and try 49. Watch which branch it takes when the divisor's square equals the number. The source is on GitHub.