
Mu: A minimal hobbyist computing stack - panic
http://akkartik.name/post/mu-2019-1
======
perfunctory
> I'd like Mu to be a stack a single person can hold in their head all at
> once, and modify in radical ways.

This reminds me of Alan Kay's STEPS project - an attempt to reinvent personal
computer in 20K lines of code [0].

Always happy to see efforts in this direction.

[0]
[http://www.vpri.org/pdf/tr2011004_steps11.pdf](http://www.vpri.org/pdf/tr2011004_steps11.pdf)

------
Quequau
This dudes stuff has been posted here recently and it's worth it go read what
he's said about his work.

[https://news.ycombinator.com/item?id=21242190](https://news.ycombinator.com/item?id=21242190)

~~~
minxomat
Announcement link from that thread:
[https://lists.tildeverse.org/hyperkitty/list/tildeclub@lists...](https://lists.tildeverse.org/hyperkitty/list/tildeclub@lists.tildeverse.org/message/Z7EBQ2ZCBIQ7YMA7Q3RUJWWB4LBIFS3M)

------
eterps
Interesting project.

So far the only desktop OS + compiler I know that can be reasonably understood
in its entirety is
[http://www.projectoberon.com](http://www.projectoberon.com)

~~~
e12e
Some interesting parallels to choices made in Pascal in the second level
language, too.

I'm not convinced that this approach is the best - the first level language is
a bit like "literal programming" machine code (not assembler) - but I think a
level of simple substitution would help a bit (think m4 as an assembler) -
allowing at least mnemonics. The insitence on numbers seem somewhat arbitrary
as they still need to be translated from strings (letter sequences) to actual
integers[e] - I don't think a table/variable look up would be too onerous.

On the whole this feels a bit more convoluted than building a Forth as level
one - but no matter what I applaud the effort.

[e] assuming program source code is text, not binary?

If it's binary I suppose "filtering out" comments might be an approach as
opposed to "translating" the source.

~~~
todd8
An interesting thought experiment that I've pondered is what I would do if I
was given a system that I had to program without even an assembler.

I've programmed on systems where the only access was through direct memory
access to the machines memory, and other systems that had a boot loading
initiated by a button that started a paper tape reader. I've also used
minicomputers that had to have instructions manually keyed in on binary panel
switches directly into memory to start a card reader where the cards contained
machine instructions to bootstrap the rest of the boot loader off of the
remaining cards.

In every case though, somewhere I had an assembler to write the code I needed.
What would I do if I didn't even have an assembler? I could write one, but I
don't want to do it in machine code. Now days, the easy way would be to simply
construct a cross-compiler or cross-assembler that was written in say python
on a different machine.

One alternative is to build a hand assembled, really simple macro system.
m4[0] or Calvin Mooers' TRAC[1] are possible prototypes, but these flexible
macro systems would be difficult to hand compiler into machine code so one
might get by with a really basic, more limited macro system. Then each machine
instruction could be encoded as a macro and then a primitive assembler could
be written as macros.

However, real assemblers do more work, they generally take two passes because
they allow symbols to be used instead of literal addresses. So the next step
would be to program a real assembler using the primitive assembly language.

After that I suppose a compiler for a simple language like Pascal, basic Lisp,
or Per Brinch Hansen's Edison[2] language could be written in assembly
language.

Per Brinch Hansen provides the basic directions for construction a very simple
operating system from scratch, including the assembler and compiler in the
book _Programming a Personal Computer_ [3].

[0] [https://www.gnu.org/software/m4/](https://www.gnu.org/software/m4/)

[1]
[https://dl.acm.org/citation.cfm?id=365270](https://dl.acm.org/citation.cfm?id=365270)

[2] [http://brinch-hansen.net/papers/1981b.pdf](http://brinch-
hansen.net/papers/1981b.pdf)

[3] [https://www.amazon.com/Programming-personal-computer-
Brinch-...](https://www.amazon.com/Programming-personal-computer-Brinch-
Hansen/dp/0137302673)

~~~
e12e
I seem to recall an old text file on programming with just debug.exe - I
thought it was an early phrack magazine article - but I can't seem to find it
now. The idea was similar to 5a here:

[http://phrack.org/issues/62/7.html](http://phrack.org/issues/62/7.html)

But the text I'm thinking of was more a how-to on bootstrapping development
under win 3.11 if you had no outside resorces, and just a plain install with
no other development tools.

------
akkartik
Author here; I just found this thread. Feel free to ask me anything.

~~~
bitminer
What's old is new again.

Your thoughts on the benefits of language slightly higher level than assembler
are interesting. Others have trod this path before.

Bliss32 was the systems language for vax/vms back in the 1980s through 2000s.
Most of vms was written in it. It was heavily influenced by PL/360 from the
days when IBM 360 or 370 mainframes were mostly programmed in assembler.

Both languages emitted very predictable assembler from their higher level
constructs. They had no optimizations. But their objectives seemed to be
focused on readability and predictable output, like your system.

The source code for 99% of vms was published on microfiche and distributed
with the binary tapes. Worth finding a copy and the accompanying text on vms
internals and data structures. An excellent intro to nonTorvalds operating
system architecture.

~~~
akkartik
Fascinating. Glad to have this on my radar.

------
kbob
This reminds me very much of 1970s Unix. Unix was also an research exercise
into OS and userland minimalism and a new, streamlined language (yes, C) that
created a new niche in the "like asm but better" space.

In other words, it had nothing in common with today's Linux. (-:

~~~
akkartik
Indeed, much of Mu's design was motivated by the question, "if we imitated the
codesign of OS and language that led to Unix and C, given what we know now,
what would we create?" This question doesn't seem to be often asked in recent
decades, with languages and OSs evolving separately.

------
eterps
akkartik, I see the following 3 syntaxes for 'MOV ESP, EBP':

    
    
        89/<-                       ebp  4/r32/esp  # copy esp to ebp    
        89/<-                5/rm32/ebp  4/r32/esp  # copy esp to ebp    
        89/<-  3/mod/direct  5/rm32/ebp  4/r32/esp  # copy esp to ebp
    

Only the last one contains all information needed to encode the instruction.
And the first one is used in the factorial example. Can you explain why all
the 3 syntaxes are in use?

~~~
akkartik
Yeah, the last one is the 'ground truth' that both translators understand.
sigils.subx adds some syntax sugar so you can say

    
    
        89/<- %ebp 4/r32/esp
    

By my self-imposed rules I can't use this sugar until I build it, so there's
little code that uses it at the moment. But any future Subx code will use it,
such as the implementation of Level 2. You can also see it used in calls.subx,
which is another later layer of syntax sugar.

Could you point me at where you see your first two versions?

~~~
eterps
In [http://akkartik.name/post/mu-2019-1](http://akkartik.name/post/mu-2019-1)
if you search for the pattern '/89' you'll find the first two versions.

The 3rd one is here:
[https://github.com/akkartik/mu/blob/master/apps/factorial.su...](https://github.com/akkartik/mu/blob/master/apps/factorial.subx#L23)

Can you explain in a sentence what rules are used to complete the encoding for
the variant:

> 89/<\- %ebp 4/r32/esp

~~~
akkartik
Ah, I see. Yes, I mentioned in the post that I was dropping some magic
numbers. Mu is intended to be read in a text editor, and I didn't want to
scare people too much while reading the post in a browser. Slightly evil, I
know.

The concrete rules are illustrated at
[https://github.com/akkartik/mu/blob/469a1a9ace9b290658beb9d0...](https://github.com/akkartik/mu/blob/469a1a9ace9b290658beb9d0799b58ce3bc2a4b4/apps/sigils.subx#L9).
In this case `%<reg>` always translates to `3/mod <reg-code>/rm32`.

------
ntoll
Note to self (as the author of the "Mu" editor,
[https://codewith.mu/](https://codewith.mu/)) - two letter project names are
just asking for name collisions. ;-)

~~~
svxml
And also
[https://www.djcbsoftware.nl/code/mu/mu4e.html](https://www.djcbsoftware.nl/code/mu/mu4e.html)
the mail client for emacs :)

------
carapace
I find this really inspiring! I've got a similar hobby project (that I've been
neglecting recently...) but with some different design choices. I don't want
to hijack the thread but I thought it might be interesting to compare and
contrast.

Instead of targeting x86 it uses Prof. Wirth's RISC CPU for Project Oberon.
This is a really simple chip, there is a model function for it written in
Oberon that is less than a page of code. It's also very well documented, and
emulators exist in Java, C, Python, and Javascript, and there is a VHDL (or is
it Verilog?) description which people have used to make FPGA-based
workstations.

For the basis notation I'm using a dialect of Joy, an extremely simple and
elegant purely functional, stack-based, concatinative language. It's like a
combination of the best parts of Forth and Lisp.
[https://en.wikipedia.org/wiki/Joy_(programming_language)](https://en.wikipedia.org/wiki/Joy_\(programming_language\))

(I'm pretty sure there's no simpler _useful_ language than Joy.)

To bridge Joy to the CPU I took the high road: a compiler written in Prolog.
It would be possible to use Forth lore to bootstrap from the metal up, but I
was gobsmacked by David Warren's "Logic Programming and Compiler Writing" (
[https://news.ycombinator.com/item?id=17674859](https://news.ycombinator.com/item?id=17674859)
) and have spent the last year "moulting" from Python to Prolog programmer.

Anyhow, the compiler so far is concise but the model it implements for Joy on
the CPU is still very crude. Between life, work, and learning more Prolog I
haven't had much time to improve it.

The thing is, Prolog is a very simple language, the compiler is very simple
and small, and Joy is also very very simple and small (especially if it's
implemented _in_ Prolog), and the underlying CPU is very simple and small. The
tricky bit would be to, say, implement a Prolog interpreter in Joy. But if you
do that you've closed the loop for self-hosting: Prolog-in-Joy, running on
Joy-on-the-metal, implementing a compiler for Joy to the metal, bootstrapped
through Prolog on the host.

(It would be simple to convert the Joy compiler in Prolog to a Joy compiler in
Joy using the Prolog interpreter in Joy and partial evaluation. FWIW.)

For the OS I'm going to build a simple clone of OberonOS but using Joy rather
than Oberon language. I made a model of this OS in Python and Tkinter to guide
the reimplementation in Joy. This will also serve as a stress test for Joy:
_can_ you implement a whole (albeit simple) "OS" in a purely functional,
stack-based, concatinative language? Maybe it sucks?

To sum up, a simple text-based UI/OS inspired by and loosely imitating
OberonOS, targeting a simple but capable 32-bit RISC chip, with Joy as the
shell and glue language and Prolog as the under-the-hood powerhouse systems
language. And the whole thing should fit in under a hundred pages of code.

~~~
akkartik
This sounds really interesting. I'm very happy you shared it. I'd love to see
it when you publish it. Based on your description it seems like a lot of code,
so I'm curious to see how tight we can keep the dependency chain.

~~~
carapace
Cheers! The project is here: [http://joypy.osdn.io/](http://joypy.osdn.io/)
FWIW.

The Prolog code is under the ./thun subdir. I apologize for the state of the
repo & docs. I need to go through and update everything.

If you look at the Python code the vast majority of it is the type inference
module and the library of functions and combinators. The parser, main loop,
and most of the library are trivial. Most of the joy.utils.types module is a
crude re-implementation of a subset of Prolog and can be discarded. The Prolog
version of Joy is even shorter than the Python version.

The Joy compiler (which is only about half done) is really tight and simple
(Joy doesn't use variable so no need for a symbol table; Joy doesn't use
subroutines so no need for a call stack) and the last half of the
functionality won't require double the code.

Meta-programming in both Joy and Prolog is really easy and powerful, and Joy
in particular lends itself to a kind of Huffman encoding of your graph (Joy
code forms a simple DAG.)

\- - - -

I want to repeat, Mu is really inspiring. I'm looking forward to watching your
progress. :-)

