The Little Man Computer in your terminal: an interactive debugger plus commands for assembling, running and inspecting programs from scripts. Built on CoreLittleManComputer.
$ lmc countdown.lmc # open the debugger
$ lmc run countdown.lmc -i 3 # run it headless
3
2
1
0
swift build -c release
cp .build/release/lmc /usr/local/bin/
Requires macOS 14 and Swift 6.
lmc debug program.lmc, or just lmc program.lmc, opens the program in a
full-screen view of the machine: the 100 mailboxes, the registers, the
in-basket and out-basket, the program listing with the next instruction
marked, and a trace of what has run.
| Key | Action |
|---|---|
s or space |
Execute one instruction |
r |
Run at the current speed, or pause |
i |
Place a card in the in-basket |
e |
Change the mailbox under the cursor |
b |
Toggle a breakpoint at the cursor |
| arrows | Move the cursor around the mailboxes |
+ / - |
Run faster or slower (1 to 64 per second, or flat out) |
R |
Reset the program and in-basket |
o |
Switch overflow between fault and wrap |
? |
Help |
q |
Quit |
The mailbox holding the next instruction is green, the mailbox the last
instruction read is cyan, the one it wrote is yellow, breakpoints are red
and data is dimmed. When the program reaches INP with an empty in-basket
the input prompt opens by itself, and a run resumes once you supply a value.
Options: -i 1,2,3 pre-fills the in-basket, --speed 8 sets the run speed,
and --overflow wrap chooses wrap-around arithmetic. The debugger needs an
interactive terminal of at least 80 by 24 characters; in scripts and
pipelines use lmc run.
lmc run <program> [-i values] [--trace] [--json] [--overflow fault|wrap] [--max-cycles n] [--no-prompt]
runs a program and prints each output on its own line. When the program
needs input that -i did not supply it reads a line from standard input,
so printf '6\n7\n' | lmc run multiply.lmc works. --trace prints every
instruction as it executes; --json prints a report with the outputs and
the final machine state.
lmc assemble <file> [--format listing|words|json] [-o out] translates
assembly into machine words. The default listing shows address, word, label
and statement; words prints one word per line; json writes a program
file every other command accepts.
lmc disassemble <file | words...> turns words back into assembly:
lmc disassemble 901 902 000 prints INP, OUT, HLT.
lmc check <files...> assembles without running and reports problems as
file:line:column: error: message, so editors can jump to them.
lmc samples [name] [--run] lists the built-in sample programs, prints
one, or runs one with its documented inputs.
<program> is a .lmc assembly file, a .json program, or - for
standard input.
| Code | Meaning |
|---|---|
| 0 | The program halted |
| 1 | The source did not assemble |
| 2 | The program caused a machine fault |
| 3 | A file could not be read or written |
| 4 | The program needed input that was not available |
| 5 | The cycle limit was reached without halting |
| 6 | The command cannot run here, such as the debugger without a terminal |
Sources/lmc The executable entry point
Sources/LMCKit
├── Commands One file per subcommand
├── Support Program loading, listings, the synchronous runner, exit codes
├── Terminal Raw mode, key parsing, a styled canvas
└── Debugger State, pure reducer, renderer, and the session that ties them to a terminal
Tests/LMCKitTests Swift Testing suites for everything above the terminal itself
The debugger's logic is a pure reducer over a plain state value and a pure
renderer onto a canvas, so all of it is unit tested without a terminal;
DebuggerSession only merges keys, engine events and resizes into that
reducer and writes frames.