Mu is a minimal-dependency hobbyist computing stack (everything above the processor).
Mu is not designed to operate in large clusters providing services for millions of people. Mu is designed for you, to run one computer. (Or a few.) Running the code you want to run, and nothing else.
Here’s the Mu computer running Conway’s Game of Life.
$ git clone https://github.com/akkartik/mu
$ cd mu
$ ./translate life.mu # emit a bootable disk.img
$ qemu-system-i386 disk.img
(Colorized sources. This is memory-safe code, and most statements map to a single instruction of machine code.)
Rather than start from some syntax and introduce layers of translation to implement it, Mu starts from the processor’s instruction set and tries to get to some safe and clear syntax with as few layers of translation as possible. The emphasis is on internal consistency at any point in time rather than compatibility with the past. (More details.)
Tests are a key mechanism here for creating a computer that others can make their own. I want to encourage a style of active and interactive reading with Mu. If something doesn’t make sense, try changing it and see what tests break. Any breaking change should break some well-named test somewhere. This requirement implies that any manual test should be easy to turn into a reproducible automated test. Mu is a testbed for providing this guarantee. It exposes testable interfaces for hardware using dependency injection so that tests can run on – and make assertions against – fake hardware. It also is an experiment in automated white-box testing which promises robust tests for performance, concurrency, fault-tolerance, etc.
Currently Mu requires a 32-bit x86 processor.
In priority order:
The Mu stack consists of:
All Mu programs get translated through these layers into tiny zero-dependency binaries that run natively. The translators for most levels are built out of lower levels. The translator from Mu to SubX is written in SubX, and the translator from SubX to bare SubX is built in bare SubX. There is also an emulator for Mu’s supported subset of x86, that’s useful for debugging SubX programs.
Mu programs build natively either on Linux or on Windows using WSL 2. For Macs and other Unix-like systems, use the (much slower) emulator:
$ ./translate_emulated ex2.mu # ~2 mins to emit disk.img
Mu programs can be written for two very different environments:
At the top-level, Mu programs emit a bootable image that runs without an OS (under emulation; I haven’t tested on native hardware yet). There’s just a screen and a keyboard, and that’s it. No mouse, no hardware acceleration, no virtual memory, no process separation, no multi-tasking, no persistent storage, no network.
The top-level is built using tools created under the linux/
sub-directory.
This sub-directory contains an entirely separate set of standard libraries
intended for building programs that run with just a Linux kernel, reading
from stdin and writing to stdout. The Mu compiler is such a program, at
linux/mu.subx
.
While I currently focus on programs without an OS, the linux/
sub-directory
is fairly ergonomic. There’s a couple of dozen example programs to try out
there. It is likely to be the option for a network stack in the foreseeable
future; I have no idea how to write to disk or interact on the network without
Linux.
The entire stack shares certain properties and conventions. Programs consist
of functions and functions consist of statements, each performing a single
operation. Operands to statements are always variables or constants. You can’t
perform a + b*c
in a single statement; you have to break it up into two.
Variables can live in memory or in registers. Registers must be explicitly
specified. There are some shared lexical rules. Comments always start with
‘#’. Numbers are always written in hex. Many terms can have context-dependent
metadata attached after ‘/’.
Here’s an example program in Mu:
Here’s an example program in SubX:
== code
Entry:
# ebx = 1
bb/copy-to-ebx 1/imm32
# increment ebx
43/increment-ebx
# exit(ebx)
e8/call syscall_exit/disp32
Forks of Mu are encouraged. If you don’t like something about this repo, feel free to make a fork. If you show it to me, I’ll link to it here. I might even pull your changes into this repo!
If you’re still reading, here are some more things to check out:
The references on Mu and SubX syntax, and also bare SubX without any syntax sugar.
Some 2-minute videos demonstrating Mu programs. Many of them involve this prototype live-updating programming environment for a postfix language that I might work on again one day:
$ cd linux
$ ./translate tile/*.mu
$ ./a.elf screen
How to get your text editor set up for Mu and SubX programs.
Shared vocabulary of data types and functions shared by Mu programs. Mu programs can transparently call low-level functions written in SubX.
A summary of how the Mu compiler translates statements to SubX. Most Mu statements map to a single x86 instruction. (colorized version)
Some starter exercises for learning SubX
(labelled hello
). Feel free to ping me with any questions.
The list of x86 opcodes supported in SubX: linux/bootstrap/bootstrap help opcodes
.
Some details on the unconventional organization of this project.
Mu builds on many ideas that have come before, especially:
On a more tactical level, this project has made progress in a series of bursts as I discovered the following resources. In autobiographical order, with no claims of completeness: