Visualizing programs with side-effects in a postfix shell with a live-updating text-mode environment. Built all the way up from machine code without any dependencies (except an x86 processor and Linux kernel).


Project page: github.com/akkartik/mu

More context: mastodon.social/@akkartik/1048

*Editing functions in the Mu shell*


Long delay since my last video. Printing floating-point numbers is *really* hard. I'm still half-assing it.

As a follow-up to merveilles.town/@akkartik/1053, I'm tightening focus to two threads:

a) These Mu shell experiments, and
b) An extremely skeletal OS to drop the Linux kernel dependency.

Deprioritized for now:
a) Other processors: RISCV, ARM, RPi, etc.
b) Graphics, mouse, etc. Device priorities for the OS are disk then ethernet.

*Towards running Mu without Linux*

All Mu really needs so far is to print to screen and read from the keyboard. Here's a 2-minute video about achieving that:


It seems such a small thing. But I needed lots of help, as you can see from the additions to my credits: github.com/akkartik/mu/compare

Merry Christmas to all! What a beautiful world.

Project page: github.com/akkartik/mu

More context: mastodon.social/@akkartik/1048

*Switching gears to pure graphics*

Until now Mu has followed classic Unix: stdin, stdout, pure text mode.

But giving up an OS kernel requires controlling the screen myself. Which requires various complicated probing for hardware. Then programs handling various screen sizes.

Easier: just always assume some common graphics mode. Say 1024x768 with 256 colors.

Interestingly, the default palette has far fewer than 256 colors. (Pic: 1024 cols each contain color `col%256`.)


*2020: Flood-filling the Mu computer*

A year ago I had a prototype of a C-level programming language mapping 1:1 to Assembly that I _thought_ could be type-safe.

Since then, I:

* wrote an academic paper on it
* made it type-safe
* began a high-level language atop it
* got into video, with 15 2-minute screencasts
* and ran programs written in it on bare metal, without an OS, like, 5 years before I expected to.

❤️ to everyone who inspired, taught, debated, encouraged.


*Rendering text atop baremetal*

Mu can now render text atop baremetal x86.

Try clicking around from akkartik.github.io/mu/html/bar

The boot-up machine code reads a few sectors from disk, configures a keyboard handler, and loads a bitmap font (2KB for ASCII, with the option for more).

I use GNU Unifont. I believe that means Mu is now GPL v2. So stated. IANAL and I try not to think about software IP. But a font? Copyright seems reasonable there.

Next up: a text editor!


*A more international interface for rendering text*

New 2-minute video: archive.org/details/akkartik-2

You get just one fixed screen resolution: 1024x768, 256 colors. Widely available on modern machines, no drivers needed.

You get just one fixed-width bitmap font. No bold/italics, no anti-aliasing.

BUT it won't make assumptions about English and left-to-right order. I eventually want anybody to be able to customize it to their language.

Main project page: github.com/akkartik/mu

I've been trying to visualize the default 256-color palette I get on baremetal.


To my eyes it looks like I can/should just live in the first 128 colors.

I built a game of "snakes", but it came out more like an etch-a-sketch 😄


Play it on any non-windows:

git clone github.com/akkartik/mu
cd mu
./translate_mu_baremetal_emulated baremetal/ex7.mu
qemu-system-i386 disk.img

h/j/k/l to draw

It took a while, but I've finally ported a pre-existing Mu program to baremetal: an RPN calculator.

This was _hard_, purely because of cursor management. I have a greater appreciation for everything that display hardware and terminal emulators provide for text mode. Mu so far puts the onus on the programmer.


In the end it's interesting to visualize the changes I had to make:

vim -d apps/rpn.mu baremetal/rpn.mu

They're entirely in `main`; the rest is unchanged.

I've been reimplementing my from-scratch live-updating postfix shell to _really_ from scratch (no more OS kernel), while at the same time rewriting the prototype with lots of tests and actually giving the language some sort of rigorous basis. No demos yet, but in the meantime here's Conway's Game of Life running on baremetal Mu.


Sources: akkartik.github.io/mu/html/bar

Main project page: github.com/akkartik/mu

The Mu shell is now off Linux


Starting point for the sources: akkartik.github.io/mu/html/bar

The architecture is now much cleaner. Functions contain lines, lines contain words, words contain gap buffers. Rendering a thing renders its constituent things. Render takes a top-left coordinate and returns a bottom-right coordinate. Each thing knows which constituent thing has its cursor, shows its cursor when rendering, redirects incoming keystrokes to it.


An experimental way to do control flow in the postfix Mu shell

The screenshot below shows an idea I've been playing with.

The conventional way Forth does control flow is a little confusing with words like `if` and `then` showing up at the end.

Factor uses quotations to put code blocks on the stack. But then you see a potentially complex chunk of code executing "all at once".

Main project page: github.com/akkartik/mu

Drilling into computations on the Mu shell

This was one of the more difficult things I've built, and yet all I've gotten working so far is some rudimentary tokenization. The reason is one little feature.. well, take a look for yourself.

archive.org/details/akkartik-2 (video; 2 mins)

As always, built all the way up from machine code, and designed primarily to be easy to build, easy to run, comprehensible to others. Also this time with lots of tests


Today was documentation day

Primitives available in the Mu computer when running without an OS: github.com/akkartik/mu/blob/ma

Primitives available when running on Linux: github.com/akkartik/mu/blob/ma

For starters I focused just on making things more discoverable. These files are optimized for opening in your text editor, jumping to definitions to see type signatures, etc. See github.com/akkartik/mu/blob/ma for a ctags configuration for Mu and SubX programs.

Main project page: github.com/akkartik/mu

Mu can now read from an ATA (IDE) disk drive on Qemu.


It wouldn't have been possible without the lovely folks over on . And the inspiration of ColorForth (merveilles.town/@akkartik/1059), though I still don't understand how that driver works.

The Mu computer now has drivers for disk and mouse.

Still extremely klunky. IDE disk drives only, and the mouse driver uses polling because configuring IRQ 12 is still beyond me.

Example programs (as usual memory safe and translating 1:1 to x86 machine code)

Disk: akkartik.github.io/mu/html/ex9

Mouse: akkartik.github.io/mu/html/ex1

Here's video of the mouse example. There's no pointer so you have to imagine me moving the mouse around.

Like I said. Klunky.

Main project page: github.com/akkartik/mu

What should the signature of a program look like?

Typed languages have a fixed signature for function `main`. A list of strings, a window context, or an IO monad.

Here's the signature on the Mu computer:

fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk)

A rudimentary, hokey capability system. No mouse yet. 'screen' is only used for text; pixel graphics currently go around it. 'data-disk' can't access code, and will eventually include finer-grained restrictions.

Mu's HLL is now Turing-complete, I think.

Things to notice:
* Wordstar-style menu at the bottom.
* List of available primitive functions in bottom left.
* List of globals on the left side that updates as I add definitions.
* Matching parens highlighted as I type.
* Drilling down into the trace to understand how the program was evaluated.


Main project page: github.com/akkartik/mu


Prototyping on the Mu computer

A 3-minute video showing what it's currently like to prototype programs on the Mu computer. There are lots of limitations. It's slow, and it can only handle short runs.

While these limitations will be relaxed over time, the goal is partly to nudge people to throw the prototype away once they know what they want, and rewrite it one level down. Therefore: encourage people to write lots of tests.


Main project page: github.com/akkartik/mu

· · Web · 2 · 3 · 5

I suck at colors. Anybody wanna help me paint this bikeshed for the mind?

Constraint: I only have 256 colors.


The Mu computer now has Lisp macros

Here's the Bresenham algorithm for drawing circles using a few macros. My current style is to keep line width to 41 characters and lay out two columns of functions.

* No nested backquotes yet.
* I can't draw circles too far down the screen due to a strange error.
* In general, error messages have been a mess ever since I stopped relying on Linux. A heavy exercise in humility.

Main project page: github.com/akkartik/mu

Playing with the kids on a Saturday morning

(Much faster on Linux, thanks to -enable-kvm.)

git clone github.com/akkartik/mu

cd mu

dd if=/dev/zero of=data.img count=20160

dd of=data.img conv=notrunc < shell/data.limg

./translate shell/*.mu # gen code.img

qemu-system-i386 -enable-kvm -m 2G -hda code.img -hdb data.img

The Mu computer now dumps a snapshot of the call stack when it fails catastrophically

This was not fun. And the debug information in the second half of the code disk is now larger than the code itself.


On the other hand, I hope debugging will now be more fun!

Using Mu to play with some ideas from Hest by Ivan Reese

archive.org/details/akkartik-m (video; 3.5 minutes)

Putting more animation and control of time into the debugging experience.

More info on Hest: ivanish.ca/hest-podcast

Main project page for Mu: github.com/akkartik/mu

I was just reminded of the existence of an idea called double-buffering.

It's easy to add: github.com/akkartik/mu/commit/

The current implementation is quite naive. Copies one byte at a time, makes several redundant copies per byte. In spite of all that, it makes a _huge_ difference in the video quality.

The Mu shell's error handling is now much improved. Errors in programs you typed in were already showing up consistently in a trace without crashing the computer. However, _writes to the trace_ could cause it to crash in cryptic ways. No more.

Now I'm back to my long-term plan: a prototyping environment that nudges people to write tests, so that it's easier to throw away the prototype and rewrite it from scratch. Making codebases rewrite-friendly.

Main project page: github.com/akkartik/mu

Managing side-effects on the Mu computer

archive.org/details/akkartik-m (video; 2 minutes)

The Mu computer's prototyping environment uses _traces_ to explain and debug programs. But traces are expensive to compute and made the environment slow and laggy.

I fixed things by collecting only a shallow trace at first, and iteratively deepening on demand by rerunning programs. This only works because it's safe to rerun functions. There are no side-effects in Mu.

Main project page: github.com/akkartik/mu

Some live-coding in my programming environment, running on my computing stack built up from scratch.

archive.org/details/akkartik-m (video; 6 minutes)

Main project page: github.com/akkartik/mu

Collaborating on a problem from Advent of Code in Mu

If anybody is into long and rambling 100-minute videos that taper off without a certain conclusion:


Mostly by Sumeet Agarwal (github.com/sumeet)

Here's the working solution if anyone wants to try it out it: akkartik.github.io/mu/html/lin

Main project page: github.com/akkartik/mu

Syntax sugar in the Mu shell

I like Lisp. But I also strongly believe anyone should be able to boot into a computer and immediately type in '1+1'. Get started using the computer as just a calculator. It's surprising how few computers satisfy that property. Now the Mu computer does.

archive.org/details/akkartik-m (video; 8 minutes)

Main project page: github.com/akkartik/mu

Starting to render images on the Mu computer

This screenshot shows a greyscale image dithered using just black and white pixels.

I rather suspect this isn't quite right. There are some suspicious streaks in various places. Rounding error, maybe.

Credit: tannerhelland.com/2012/12/28/d. I'm using standard Floyd-Steinberg.

Main project page: github.com/akkartik/mu

Generalizing dithering to color (assuming a fixed palette) turns out to be surprisingly complex. The r/g/b channels are mostly independent copies each analogous to the greyscale dither, but there's tangling in one place in the center that complicates everything.

I was just reminded after years of the "weird number" in 2's complement arithmetic: en.wikipedia.org/wiki/Two%27s_

The reason it came up: it's the result of trying to convert a floating-point Infinity or NaN to an integer.


Color dithering on the Mu computer.

Here is a before/after pair of images. Before has 256x256x256 colors. After has 256 colors.

Notice all the yellow pixels in the first image that turn into alternating greens and oranges in the second. Also, the stem looks very different. But overall, it looks gratifyingly similar to the original. My eyes took a while before they started to notice differences.

Main project page: github.com/akkartik/mu

@akkartik you want help picking colours from that given palette? or you want to pick which 256 colours should make it up?

@zens Good question! I'd like to stick with this palette for now please. I'm trying to configure as little on the hardware as possible, to minimize how much I have to debug on real hardware down the road.

@akkartik just ripoff one of the popular themes for VS Code or Textmate

@akkartik Solarized color scheme is a subset of the xterm 256 color pallette. I'm a big solarized dark fan (except maybe the green)

@alcinnz @zachdecook Thank you for suggesting Solarized! I just pushed an approximation of it: github.com/akkartik/mu/commit/

Unfortunately the default 256-color palette BIOS provides isn't the same as xterm's. Perhaps I should investigate switching it. The default isn't great: merveilles.town/@akkartik/1055

In the meantime this is definitely an improvement.

There might be a better color combination of backgrounds on the left. I plan for functions to order independently like in Tiddlywiki.

@alcinnz @zachdecook I initially wanted to write a Mu function that would return the closest available color given an RGB triple, after computing HSL distances. For now I made a small change to akkartik.github.io/mu/html/vga to edit the left column of squares in a browser's inspector to compare.

I found myself zooming way in to be able to tell subtle differences. So this is an ugly hack. The Mu function would be handy.

@akkartik Since you've bootstrapped to LISP, do you think you'll write more in that space than in the assembler language?

@neauoire Not sure 🤔 The Lisp is _really_ slow compared to the Mu language. And in the past making a fast HLL requires a lot of code. My current idea is to use the Lisp shell for prototyping. Once someone has something they know they want to build, it'll probably make sense to port it to low-level Mu for performance. It's pretty nice to program in, IMO, and also won't feel slow for most things.

@neauoire Oh, one clarification: the stack-trace stuff is not specific to Lisp. Any Mu program can now call `abort` when you're building something and don't want to think about the best way to recover from an error just yet.

@akkartik Hell ye, I've been waiting to see someone use functional purity this way! Another use case I've been considering and hope to explore one day is for caching and handling OOM conditions gracefully. If you know that a result is relatively cheap to compute again, you can just drop it and let other computations finish.

@akkartik looks like the error isn't diffusing! definitely not quite right for Floyd Steinberg.

@akkartik here's a Floyd Steinberg implementation of that image at about 400 pixels wide

@oppen Thanks! Yeah you're right. Here's what I get for a 16x16 image where every pixel is at level 128 of 0-255 levels of grey. Definitely doesn't look right past the first row of pixels.

"The diffusion coefficients have the property that if the original pixel values are exactly halfway in between the nearest available colors, the dithered result is a checkerboard pattern."


@oppen I wonder if it's the >>4 to simulate /16. Introduces some error. On the other hand it seems designed to not need division.

@oppen Looking better.

Every time I mutate a function's args, I live to regret it.

@oppen _Now_ giving it more precision yields a perfect checkerboard pattern.

@akkartik couldn’t this be simplified by forgetting about the different colour channels and defining error and colour to be a vector type from the start? then it doesn’t matter if it’s rgb or lab or grayscale except in the error function itself?

@akkartik then the error you diffuse is a vector quantity and, handling each dimension seperately becomes something for the compiler to deal with?

@akkartik particularly since error as you have here becomes a manhattan distance, not a euclidean distance as you might prefer- or better, a CIE delta_E function

@zens Well, there's no compiler here but what I've written 😄 You're right that I'm trying to implement a vector computation for myself that generalizes the single dimension of greyscale.

It's not Manhattan distance, though. The 'nearest' box is hiding a Euclidean measure which decomposes into the error arrays.

A different scale may well work better. I don't really know what I'm doing there. I built a nearest out of HSL but took it out. It's a cylindrical space so Euclidean doesn't seem right.

@zens Part of the problem is that en.wikipedia.org/wiki/Floyd%E2 adds errors into the input array. But input pixels have different ranges from errors. 1-byte ints vs fractions. So I've been tracking errors in a separate array.

A = input pixel + error
B = nearest pixel to A
C = error - B
Diffuse C to neighboring pixels

Perhaps there's a better way.

@akkartik my thinking lately has been this: most/all dithering algorithms were invented under the assumption that users expect extremely low 1980s era memory requirements, so only a few pixels are held in memory at a time. if you can hold all pixels in memory, error could be diffused in all directions iteratively.
step1. threshold work image
step2. error image is diff of threshold with original
step3. modify error image with diffuse(img)
step4. error image + original = work

@akkartik that said, there’s other benefits to dither algorithms that run streaming and in a single pass, just saying we are no longer limited to these

@zens Fascinating! Definitely worth researching. For now I just want to be able to display arbitrary images, say from Wikipedia, even though I have only 256 colors.

@akkartik i remember @makeworld had very interesting findings related to color when developing their dithering library a couple of months ago!

@chirrolafupa Indeed. There were a couple of blog posts a few months ago that had serendipitous timing for me (+- a few months) @makeworld

Sign in to participate in the conversation

Revel in the marvels of the universe. We are a collective of forward-thinking individuals who strive to better ourselves and our surroundings through constant creation. We express ourselves through music, art, games, and writing. We also put great value in play. A warm welcome to any like-minded people who feel these ideals resonate with them.