1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */#ifndef RF_IGNORE_H #define RF_IGNORE_H #define IGNORE_SIZE 100 #ifdef _WIN32 #define MAXPATHLEN 1024 # Mu: a human-scale computer 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. ```sh $ git clone https://github.com/akkartik/mu $ cd mu $ ./translate_mu apps/ex2.mu # emit a.elf $ ./a.elf # add 3 and 4 $ echo $? 7 ``` 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.](http://akkartik.name/akkartik-convivial-20200607.pdf)) Currently Mu requires a 32-bit x86 processor. ## Goals In priority order: - [Reward curiosity.](http://akkartik.name/about) - Easy to build, easy to run. [Minimal dependencies](https://news.ycombinator.com/item?id=16882140#16882555), so that installation is always painless. - All design decisions comprehensible to a single individual. (On demand.) - All design decisions comprehensible without needing to talk to anyone. (I always love talking to you, but I try hard to make myself redundant.) - [A globally comprehensible _codebase_ rather than locally clean code.](http://akkartik.name/post/readable-bad) - Clear error messages over expressive syntax. - Safe. - Thorough test coverage. If you break something you should immediately see an error message. If you can manually test for something you should be able to write an automated test for it. - Memory leaks over memory corruption. - Teach the computer bottom-up. ## Non-goals - Speed. Staying close to machine code should naturally keep Mu fast enough. - Efficiency. Controlling the number of abstractions should naturally keep Mu using far less than the gigabytes of memory modern computers have. - Portability. Mu will run on any computer as long as it's x86. I will enthusiastically contribute to support for other processors -- in separate forks. Readers shouldn't have to think about processors they don't have. - Compatibility. The goal is to get off mainstream stacks, not to perpetuate them. Sometimes the right long-term solution is to [bump the major version number](http://akkartik.name/post/versioning). - Syntax. Mu code is meant to be comprehended by [running, not just reading](http://akkartik.name/post/comprehension). For now it's a thin veneer over machine code. I'm working on memory safety before expressive syntax. ## Toolchain The Mu stack consists of: - the Mu type-safe language; - SubX, an unsafe notation for a subset of x86 machine code; and - _bare_ SubX, a more rudimentary form of SubX without certain syntax sugar. 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](subx_debugging.md). Mu programs build natively either on Linux or on Windows using [WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install-win10). For Macs and other Unix-like systems, use the emulator: ```sh $ ./translate_mu_emulated apps/ex2.mu # ~2 mins to emit a.elf $ ./bootstrap run ./a.elf # run in the emulator $ echo $? ``` Mu programs can be written for two very different environments: * With just a Linux kernel. This is the environment that Mu bootstraps itself into, and it's the option for programming with stdin, stdout and file descriptors. * Without an OS, by interacting directly with the screen and keyboard. This is the option for rudimentary pixel graphics. There's currently no mouse, no hardware acceleration, no virtual memory, no process separation, no multi-tasking, no persistent storage, no network. These programs have not yet been tested on native hardware, only on on Qemu and Bochs. But these _baremetal_ programs build from scratch, without any reliance on C. This is the future. Here is Conway's Game of Life on Mu: ```sh $ ./translate_mu_baremetal baremetal/life.mu # emit disk.img $ qemu-system-i386 disk.img ``` <img alt='screenshot of Game of Life running on Mu without any intervening Operating System' src='html/baremetal-life.png'> Use `translate_mu` to build programs for Linux, and `translate_mu_baremetal` for running without Linux. The standard libraries are totally separate for the two options, so programs for one won't run on the other. ### incomplete tools There's a prototype live-updating programming environment for a postfix language: ```sh $ ./translate_mu apps/tile/*.mu $ ./a.elf screen ``` Once generated, ELF binaries can be packaged up with a Linux kernel into a bootable disk image. Here's how the postfix environment might look on startup: ```sh $ ./translate_mu apps/tile/*.mu # emit a.elf # dependencies $ sudo apt install build-essential flex bison wget libelf-dev libssl-dev xorriso $ tools/iso/linux a.elf $ qemu-system-x86_64 -m 256M -cdrom mu_linux.iso -boot d ``` <img alt='screenshot of Mu running on Qemu' src='html/20201114-qemu.png'> The disk image also runs on [any cloud server that supports custom images](http://akkartik.name/post/iso-on-linode). Mu also runs on the minimal hobbyist OS [Soso](https://github.com/ozkl/soso). (Requires graphics and sudo access. Currently doesn't work on a cloud server.) ```sh $ ./translate_mu apps/ex2.mu # emit a.elf # dependencies $ sudo apt install build-essential util-linux nasm xorriso # maybe also dosfstools and mtools $ tools/iso/soso a.elf # requires sudo $ qemu-system-i386 -cdrom mu_soso.iso ``` ## Syntax 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: <img alt='ex2.mu' src='html/ex2.mu.png' width='400px'> [More details on Mu syntax →](mu.md) Here's an example program in SubX: ```sh == code Entry: # ebx = 1 bb/copy-to-ebx 1/imm32 # increment ebx 43/increment-ebx # exit(ebx) e8/call syscall_exit/disp32 ``` [More details on SubX syntax →](subx.md) ## Forks 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! - [mu-normie](https://git.sr.ht/~akkartik/mu-normie): with a more standard build system that organizes the repo by header files and compilation units. Stays in sync with this repo. - [mu-x86\_64](https://git.sr.ht/~akkartik/mu-x86_64): experimental fork for 64-bit x86 in collaboration with [Max Bernstein](https://bernsteinbear.com). It's brought up a few concrete open problems that I don't have good solutions for yet. - [uCISC](https://github.com/grokthis/ucisc): a 16-bit processor being designed from scratch by [Robert Butler](https://www.youtube.com/channel/UCh4OpfF7T7UtezGejRTLxCw) and programmed with a SubX-like syntax. - [subv](https://git.s-ol.nu/subv): experimental SubX-like syntax by [s-ol bekic](https://mmm.s-ol.nu) for the RISC-V instruction set. ## Desiderata If you're still reading, here are some more things to check out: - The references on [Mu](mu.md) and [SubX](subx.md) syntax, and also [bare SubX](subx_bare.md) without any syntax sugar. - [Some 2-minute videos demonstrating Mu programs](https://archive.org/details/@kartik_agaram). - [How to get your text editor set up for Mu and SubX programs.](editor.md) - [Some tips for debugging SubX programs.](subx_debugging.md) - [Shared vocabulary of data types and functions shared by Mu programs.](vocabulary.md) Mu programs can transparently call low-level functions written in SubX. - [A summary](mu_instructions) of how the Mu compiler translates instructions to SubX. ([colorized version](http://akkartik.github.io/mu/html/mu_instructions.html)) - [Some starter exercises for learning SubX](https://github.com/akkartik/mu/pulls) (labelled `hello`). Feel free to [ping me](mailto:ak@akkartik.com) with any questions. - [Commandline reference for the bootstrap C++ program.](bootstrap.md) - The [list of x86 opcodes](subx_opcodes) supported in SubX: `./bootstrap help opcodes`. - [Some details on the unconventional organization of this project.](http://akkartik.name/post/four-repos) - Previous prototypes: [mu0](https://github.com/akkartik/mu0), [mu1](https://github.com/akkartik/mu1). ## Credits Mu builds on many ideas that have come before, especially: - [Peter Naur](http://akkartik.name/naur.pdf) for articulating the paramount problem of programming: communicating a codebase to others; - [Christopher Alexander](http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512) and [Richard Gabriel](https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf) for the intellectual tools for reasoning about the higher order design of a codebase; - [David Parnas](http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf) and others for highlighting the value of separating concerns and stepwise refinement; - The folklore of debugging by print and the trace facility in many Lisp systems; - Automated tests for showing the value of developing programs inside an elaborate harness; 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: - [“Bootstrapping a compiler from nothing”](http://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html) by Edmund Grumley-Evans. - [StoneKnifeForth](https://github.com/kragen/stoneknifeforth) by [Kragen Sitaker](http://canonical.org/~kragen), including [a tiny sketch of an ELF loader](https://github.com/kragen/stoneknifeforth/blob/master/386.c). - [“Creating tiny ELF executables”](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) by Brian Raiter. - [Single-page cheatsheet for the x86 ISA](https://net.cs.uni-bonn.de/fileadmin/user_upload/plohmann/x86_opcode_structure_and_instruction_overview.pdf) by Daniel Plohmann ([cached local copy](https://github.com/akkartik/mu/blob/master/cheatsheet.pdf)) - [Minimal Linux Live](http://minimal.linux-bg.org) for teaching how to create a bootable disk image using the syslinux bootloader. - [“Writing a bootloader from scratch”](https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf) by Nick Blundell. - Wikipedia on BIOS interfaces: [Int 10h](https://en.wikipedia.org/wiki/INT_10H), [Int 13h](https://en.wikipedia.org/wiki/INT_13H). - [Some tips on programming bootloaders](https://stackoverflow.com/questions/43786251/int-13h-42h-doesnt-load-anything-in-bochs/43787939#43787939) by Michael Petch. - [xv6, the port of Unix Version 6 to x86 processors](https://github.com/mit-pdos/xv6-public) - Some tips on handling keyboard interrupts by [Alex Dzyoba](https://alex.dzyoba.com/blog/os-interrupts) and [Michael Petch](https://stackoverflow.com/questions/37618111/keyboard-irq-within-an-x86-kernel).
#ifndef RF_IGNORE_H #define RF_IGNORE_H #define IGNORE_SIZE 100 #ifdef _WIN32 #define MAXPATHLEN 1024 # Mu: a human-scale computer 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. ```sh $ git clone https://github.com/akkartik/mu $ cd mu $ ./translate_mu apps/ex2.mu # emit a.elf $ ./a.elf # add 3 and 4 $ echo $? 7 ``` 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.](http://akkartik.name/akkartik-convivial-20200607.pdf)) Currently Mu requires a 32-bit x86 processor. ## Goals In priority order: - [Reward curiosity.](http://akkartik.name/about) - Easy to build, easy to run. [Minimal dependencies](https://news.ycombinator.com/item?id=16882140#16882555), so that installation is always painless. - All design decisions comprehensible to a single individual. (On demand.) - All design decisions comprehensible without needing to talk to anyone. (I always love talking to you, but I try hard to make myself redundant.) - [A globally comprehensible _codebase_ rather than locally clean code.](http://akkartik.name/post/readable-bad) - Clear error messages over expressive syntax. - Safe. - Thorough test coverage. If you break something you should immediately see an error message. If you can manually test for something you should be able to write an automated test for it. - Memory leaks over memory corruption. - Teach the computer bottom-up. ## Non-goals - Speed. Staying close to machine code should naturally keep Mu fast enough. - Efficiency. Controlling the number of abstractions should naturally keep Mu using far less than the gigabytes of memory modern computers have. - Portability. Mu will run on any computer as long as it's x86. I will enthusiastically contribute to support for other processors -- in separate forks. Readers shouldn't have to think about processors they don't have. - Compatibility. The goal is to get off mainstream stacks, not to perpetuate them. Sometimes the right long-term solution is to [bump the major version number](http://akkartik.name/post/versioning). - Syntax. Mu code is meant to be comprehended by [running, not just reading](http://akkartik.name/post/comprehension). For now it's a thin veneer over machine code. I'm working on memory safety before expressive syntax. ## Toolchain The Mu stack consists of: - the Mu type-safe language; - SubX, an unsafe notation for a subset of x86 machine code; and - _bare_ SubX, a more rudimentary form of SubX without certain syntax sugar. 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](subx_debugging.md). Mu programs build natively either on Linux or on Windows using [WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install-win10). For Macs and other Unix-like systems, use the emulator: ```sh $ ./translate_mu_emulated apps/ex2.mu # ~2 mins to emit a.elf $ ./bootstrap run ./a.elf # run in the emulator $ echo $? ``` Mu programs can be written for two very different environments: * With just a Linux kernel. This is the environment that Mu bootstraps itself into, and it's the option for programming with stdin, stdout and file descriptors. * Without an OS, by interacting directly with the screen and keyboard. This is the option for rudimentary pixel graphics. There's currently no mouse, no hardware acceleration, no virtual memory, no process separation, no multi-tasking, no persistent storage, no network. These programs have not yet been tested on native hardware, only on on Qemu and Bochs. But these _baremetal_ programs build from scratch, without any reliance on C. This is the future. Here is Conway's Game of Life on Mu: ```sh $ ./translate_mu_baremetal baremetal/life.mu # emit disk.img $ qemu-system-i386 disk.img ``` <img alt='screenshot of Game of Life running on Mu without any intervening Operating System' src='html/baremetal-life.png'> Use `translate_mu` to build programs for Linux, and `translate_mu_baremetal` for running without Linux. The standard libraries are totally separate for the two options, so programs for one won't run on the other. ### incomplete tools There's a prototype live-updating programming environment for a postfix language: ```sh $ ./translate_mu apps/tile/*.mu $ ./a.elf screen ``` Once generated, ELF binaries can be packaged up with a Linux kernel into a bootable disk image. Here's how the postfix environment might look on startup: ```sh $ ./translate_mu apps/tile/*.mu # emit a.elf # dependencies $ sudo apt install build-essential flex bison wget libelf-dev libssl-dev xorriso $ tools/iso/linux a.elf $ qemu-system-x86_64 -m 256M -cdrom mu_linux.iso -boot d ``` <img alt='screenshot of Mu running on Qemu' src='html/20201114-qemu.png'> The disk image also runs on [any cloud server that supports custom images](http://akkartik.name/post/iso-on-linode). Mu also runs on the minimal hobbyist OS [Soso](https://github.com/ozkl/soso). (Requires graphics and sudo access. Currently doesn't work on a cloud server.) ```sh $ ./translate_mu apps/ex2.mu # emit a.elf # dependencies $ sudo apt install build-essential util-linux nasm xorriso # maybe also dosfstools and mtools $ tools/iso/soso a.elf # requires sudo $ qemu-system-i386 -cdrom mu_soso.iso ``` ## Syntax 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: <img alt='ex2.mu' src='html/ex2.mu.png' width='400px'> [More details on Mu syntax →](mu.md) Here's an example program in SubX: ```sh == code Entry: # ebx = 1 bb/copy-to-ebx 1/imm32 # increment ebx 43/increment-ebx # exit(ebx) e8/call syscall_exit/disp32 ``` [More details on SubX syntax →](subx.md) ## Forks 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! - [mu-normie](https://git.sr.ht/~akkartik/mu-normie): with a more standard build system that organizes the repo by header files and compilation units. Stays in sync with this repo. - [mu-x86\_64](https://git.sr.ht/~akkartik/mu-x86_64): experimental fork for 64-bit x86 in collaboration with [Max Bernstein](https://bernsteinbear.com). It's brought up a few concrete open problems that I don't have good solutions for yet. - [uCISC](https://github.com/grokthis/ucisc): a 16-bit processor being designed from scratch by [Robert Butler](https://www.youtube.com/channel/UCh4OpfF7T7UtezGejRTLxCw) and programmed with a SubX-like syntax. - [subv](https://git.s-ol.nu/subv): experimental SubX-like syntax by [s-ol bekic](https://mmm.s-ol.nu) for the RISC-V instruction set. ## Desiderata If you're still reading, here are some more things to check out: - The references on [Mu](mu.md) and [SubX](subx.md) syntax, and also [bare SubX](subx_bare.md) without any syntax sugar. - [Some 2-minute videos demonstrating Mu programs](https://archive.org/details/@kartik_agaram). - [How to get your text editor set up for Mu and SubX programs.](editor.md) - [Some tips for debugging SubX programs.](subx_debugging.md) - [Shared vocabulary of data types and functions shared by Mu programs.](vocabulary.md) Mu programs can transparently call low-level functions written in SubX. - [A summary](mu_instructions) of how the Mu compiler translates instructions to SubX. ([colorized version](http://akkartik.github.io/mu/html/mu_instructions.html)) - [Some starter exercises for learning SubX](https://github.com/akkartik/mu/pulls) (labelled `hello`). Feel free to [ping me](mailto:ak@akkartik.com) with any questions. - [Commandline reference for the bootstrap C++ program.](bootstrap.md) - The [list of x86 opcodes](subx_opcodes) supported in SubX: `./bootstrap help opcodes`. - [Some details on the unconventional organization of this project.](http://akkartik.name/post/four-repos) - Previous prototypes: [mu0](https://github.com/akkartik/mu0), [mu1](https://github.com/akkartik/mu1). ## Credits Mu builds on many ideas that have come before, especially: - [Peter Naur](http://akkartik.name/naur.pdf) for articulating the paramount problem of programming: communicating a codebase to others; - [Christopher Alexander](http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512) and [Richard Gabriel](https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf) for the intellectual tools for reasoning about the higher order design of a codebase; - [David Parnas](http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf) and others for highlighting the value of separating concerns and stepwise refinement; - The folklore of debugging by print and the trace facility in many Lisp systems; - Automated tests for showing the value of developing programs inside an elaborate harness; 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: - [“Bootstrapping a compiler from nothing”](http://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html) by Edmund Grumley-Evans. - [StoneKnifeForth](https://github.com/kragen/stoneknifeforth) by [Kragen Sitaker](http://canonical.org/~kragen), including [a tiny sketch of an ELF loader](https://github.com/kragen/stoneknifeforth/blob/master/386.c). - [“Creating tiny ELF executables”](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) by Brian Raiter. - [Single-page cheatsheet for the x86 ISA](https://net.cs.uni-bonn.de/fileadmin/user_upload/plohmann/x86_opcode_structure_and_instruction_overview.pdf) by Daniel Plohmann ([cached local copy](https://github.com/akkartik/mu/blob/master/cheatsheet.pdf)) - [Minimal Linux Live](http://minimal.linux-bg.org) for teaching how to create a bootable disk image using the syslinux bootloader. - [“Writing a bootloader from scratch”](https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf) by Nick Blundell. - Wikipedia on BIOS interfaces: [Int 10h](https://en.wikipedia.org/wiki/INT_10H), [Int 13h](https://en.wikipedia.org/wiki/INT_13H). - [Some tips on programming bootloaders](https://stackoverflow.com/questions/43786251/int-13h-42h-doesnt-load-anything-in-bochs/43787939#43787939) by Michael Petch. - [xv6, the port of Unix Version 6 to x86 processors](https://github.com/mit-pdos/xv6-public) - Some tips on handling keyboard interrupts by [Alex Dzyoba](https://alex.dzyoba.com/blog/os-interrupts) and [Michael Petch](https://stackoverflow.com/questions/37618111/keyboard-irq-within-an-x86-kernel).
# Mu: a human-scale computer 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. ```sh $ git clone https://github.com/akkartik/mu $ cd mu $ ./translate_mu apps/ex2.mu # emit a.elf $ ./a.elf # add 3 and 4 $ echo $? 7 ``` 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.](http://akkartik.name/akkartik-convivial-20200607.pdf)) Currently Mu requires a 32-bit x86 processor. ## Goals In priority order: - [Reward curiosity.](http://akkartik.name/about) - Easy to build, easy to run. [Minimal dependencies](https://news.ycombinator.com/item?id=16882140#16882555), so that installation is always painless. - All design decisions comprehensible to a single individual. (On demand.) - All design decisions comprehensible without needing to talk to anyone. (I always love talking to you, but I try hard to make myself redundant.) - [A globally comprehensible _codebase_ rather than locally clean code.](http://akkartik.name/post/readable-bad) - Clear error messages over expressive syntax. - Safe. - Thorough test coverage. If you break something you should immediately see an error message. If you can manually test for something you should be able to write an automated test for it. - Memory leaks over memory corruption. - Teach the computer bottom-up. ## Non-goals - Speed. Staying close to machine code should naturally keep Mu fast enough. - Efficiency. Controlling the number of abstractions should naturally keep Mu using far less than the gigabytes of memory modern computers have. - Portability. Mu will run on any computer as long as it's x86. I will enthusiastically contribute to support for other processors -- in separate forks. Readers shouldn't have to think about processors they don't have. - Compatibility. The goal is to get off mainstream stacks, not to perpetuate them. Sometimes the right long-term solution is to [bump the major version number](http://akkartik.name/post/versioning). - Syntax. Mu code is meant to be comprehended by [running, not just reading](http://akkartik.name/post/comprehension). For now it's a thin veneer over machine code. I'm working on memory safety before expressive syntax. ## Toolchain The Mu stack consists of: - the Mu type-safe language; - SubX, an unsafe notation for a subset of x86 machine code; and - _bare_ SubX, a more rudimentary form of SubX without certain syntax sugar. 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](subx_debugging.md). Mu programs build natively either on Linux or on Windows using [WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install-win10). For Macs and other Unix-like systems, use the emulator: ```sh $ ./translate_mu_emulated apps/ex2.mu # ~2 mins to emit a.elf $ ./bootstrap run ./a.elf # run in the emulator $ echo $? ``` Mu programs can be written for two very different environments: * With just a Linux kernel. This is the environment that Mu bootstraps itself into, and it's the option for programming with stdin, stdout and file descriptors. * Without an OS, by interacting directly with the screen and keyboard. This is the option for rudimentary pixel graphics. There's currently no mouse, no hardware acceleration, no virtual memory, no process separation, no multi-tasking, no persistent storage, no network. These programs have not yet been tested on native hardware, only on on Qemu and Bochs. But these _baremetal_ programs build from scratch, without any reliance on C. This is the future. Here is Conway's Game of Life on Mu: ```sh $ ./translate_mu_baremetal baremetal/life.mu # emit disk.img $ qemu-system-i386 disk.img ``` <img alt='screenshot of Game of Life running on Mu without any intervening Operating System' src='html/baremetal-life.png'> Use `translate_mu` to build programs for Linux, and `translate_mu_baremetal` for running without Linux. The standard libraries are totally separate for the two options, so programs for one won't run on the other. ### incomplete tools There's a prototype live-updating programming environment for a postfix language: ```sh $ ./translate_mu apps/tile/*.mu $ ./a.elf screen ``` Once generated, ELF binaries can be packaged up with a Linux kernel into a bootable disk image. Here's how the postfix environment might look on startup: ```sh $ ./translate_mu apps/tile/*.mu # emit a.elf # dependencies $ sudo apt install build-essential flex bison wget libelf-dev libssl-dev xorriso $ tools/iso/linux a.elf $ qemu-system-x86_64 -m 256M -cdrom mu_linux.iso -boot d ``` <img alt='screenshot of Mu running on Qemu' src='html/20201114-qemu.png'> The disk image also runs on [any cloud server that supports custom images](http://akkartik.name/post/iso-on-linode). Mu also runs on the minimal hobbyist OS [Soso](https://github.com/ozkl/soso). (Requires graphics and sudo access. Currently doesn't work on a cloud server.) ```sh $ ./translate_mu apps/ex2.mu # emit a.elf # dependencies $ sudo apt install build-essential util-linux nasm xorriso # maybe also dosfstools and mtools $ tools/iso/soso a.elf # requires sudo $ qemu-system-i386 -cdrom mu_soso.iso ``` ## Syntax 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: <img alt='ex2.mu' src='html/ex2.mu.png' width='400px'> [More details on Mu syntax →](mu.md) Here's an example program in SubX: ```sh == code Entry: # ebx = 1 bb/copy-to-ebx 1/imm32 # increment ebx 43/increment-ebx # exit(ebx) e8/call syscall_exit/disp32 ``` [More details on SubX syntax →](subx.md) ## Forks 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! - [mu-normie](https://git.sr.ht/~akkartik/mu-normie): with a more standard build system that organizes the repo by header files and compilation units. Stays in sync with this repo. - [mu-x86\_64](https://git.sr.ht/~akkartik/mu-x86_64): experimental fork for 64-bit x86 in collaboration with [Max Bernstein](https://bernsteinbear.com). It's brought up a few concrete open problems that I don't have good solutions for yet. - [uCISC](https://github.com/grokthis/ucisc): a 16-bit processor being designed from scratch by [Robert Butler](https://www.youtube.com/channel/UCh4OpfF7T7UtezGejRTLxCw) and programmed with a SubX-like syntax. - [subv](https://git.s-ol.nu/subv): experimental SubX-like syntax by [s-ol bekic](https://mmm.s-ol.nu) for the RISC-V instruction set. ## Desiderata If you're still reading, here are some more things to check out: - The references on [Mu](mu.md) and [SubX](subx.md) syntax, and also [bare SubX](subx_bare.md) without any syntax sugar. - [Some 2-minute videos demonstrating Mu programs](https://archive.org/details/@kartik_agaram). - [How to get your text editor set up for Mu and SubX programs.](editor.md) - [Some tips for debugging SubX programs.](subx_debugging.md) - [Shared vocabulary of data types and functions shared by Mu programs.](vocabulary.md) Mu programs can transparently call low-level functions written in SubX. - [A summary](mu_instructions) of how the Mu compiler translates instructions to SubX. ([colorized version](http://akkartik.github.io/mu/html/mu_instructions.html)) - [Some starter exercises for learning SubX](https://github.com/akkartik/mu/pulls) (labelled `hello`). Feel free to [ping me](mailto:ak@akkartik.com) with any questions. - [Commandline reference for the bootstrap C++ program.](bootstrap.md) - The [list of x86 opcodes](subx_opcodes) supported in SubX: `./bootstrap help opcodes`. - [Some details on the unconventional organization of this project.](http://akkartik.name/post/four-repos) - Previous prototypes: [mu0](https://github.com/akkartik/mu0), [mu1](https://github.com/akkartik/mu1). ## Credits Mu builds on many ideas that have come before, especially: - [Peter Naur](http://akkartik.name/naur.pdf) for articulating the paramount problem of programming: communicating a codebase to others; - [Christopher Alexander](http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512) and [Richard Gabriel](https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf) for the intellectual tools for reasoning about the higher order design of a codebase; - [David Parnas](http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf) and others for highlighting the value of separating concerns and stepwise refinement; - The folklore of debugging by print and the trace facility in many Lisp systems; - Automated tests for showing the value of developing programs inside an elaborate harness; 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: - [“Bootstrapping a compiler from nothing”](http://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html) by Edmund Grumley-Evans. - [StoneKnifeForth](https://github.com/kragen/stoneknifeforth) by [Kragen Sitaker](http://canonical.org/~kragen), including [a tiny sketch of an ELF loader](https://github.com/kragen/stoneknifeforth/blob/master/386.c). - [“Creating tiny ELF executables”](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) by Brian Raiter. - [Single-page cheatsheet for the x86 ISA](https://net.cs.uni-bonn.de/fileadmin/user_upload/plohmann/x86_opcode_structure_and_instruction_overview.pdf) by Daniel Plohmann ([cached local copy](https://github.com/akkartik/mu/blob/master/cheatsheet.pdf)) - [Minimal Linux Live](http://minimal.linux-bg.org) for teaching how to create a bootable disk image using the syslinux bootloader. - [“Writing a bootloader from scratch”](https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf) by Nick Blundell. - Wikipedia on BIOS interfaces: [Int 10h](https://en.wikipedia.org/wiki/INT_10H), [Int 13h](https://en.wikipedia.org/wiki/INT_13H). - [Some tips on programming bootloaders](https://stackoverflow.com/questions/43786251/int-13h-42h-doesnt-load-anything-in-bochs/43787939#43787939) by Michael Petch. - [xv6, the port of Unix Version 6 to x86 processors](https://github.com/mit-pdos/xv6-public) - Some tips on handling keyboard interrupts by [Alex Dzyoba](https://alex.dzyoba.com/blog/os-interrupts) and [Michael Petch](https://stackoverflow.com/questions/37618111/keyboard-irq-within-an-x86-kernel).