mu/tutorial/index.md

643 lines
25 KiB
Markdown
Raw Permalink Normal View History

# A guided tour of Mu on x86 computers
_by Kartik Agaram and [sejo](https://compudanzas.net)_
2021-10-20 18:51:24 +00:00
[Mu](https://github.com/akkartik/mu) shrinks all the software in a computer
until it can (in principle) fit in a single head. Sensible error messages with
as little code as possible, starting all the way from your (x86) processor's
instruction set. Everything easy to change to your needs
([habitable](http://akkartik.name/post/habitability)), everything easy to
check up on ([auditable](http://akkartik.name/post/neighborhood)).
2021-11-30 05:55:33 +00:00
This page is a guided tour through [Mu's Readme](https://github.com/akkartik/mu#readme)
2021-10-29 14:37:09 +00:00
and reference documentation. We'll start out really slow and gradually
accelerate as we build up skills. By the end of it all, I hope you'll be able
to program your processor to run some small graphical programs. The programs
will only use a small subset of your computer's capabilities; there's still a
lot I don't know and therefore cannot teach. However, the programs will run on
a _real_ processor without needing any other intermediary software.
2021-10-20 18:51:24 +00:00
_Prerequisites_
You will need:
2021-10-29 14:37:09 +00:00
* A computer with an x86 processor running Linux. Mu is designed to eventually
escape Linux, but still needs some _host_ environment for now. Other
platforms will also do (BSD, Mac OS, Windows Subsystem for Linux), but be
2021-10-20 18:51:24 +00:00
warned that things will be _much_ (~20x) slower.
* Some fluency in typing commands at the terminal and interpreting their
output.
* Fluency with some text editor. Things like undo, copying and pasting text,
and saving work in files. A little experience programming in _some_ language
is also handy.
* [Git](https://git-scm.com) for version control.
* [QEMU](https://www.qemu.org) for emulating a processor without Linux.
* Basic knowledge of number bases, and the difference between decimal and
hexadecimal numbers.
2021-10-21 05:59:42 +00:00
* Basic knowledge of the inside of a processor, such as the difference between
a small number of registers and a large number of locations in memory.
2021-10-20 18:51:24 +00:00
If you have trouble with any of this, [I'm always nearby and available to
answer questions](http://akkartik.name/contact). The prerequisites are just
things I haven't figured out how to explain yet. In particular, I want this
page to be accessible to people who are in the process of learning
programming, but I'm sure it isn't good enough yet for that. Ask me questions
and help me improve it.
2021-10-20 19:32:58 +00:00
## Task 1: getting started
2021-10-20 18:51:24 +00:00
Read the first half of [the Readme](https://github.com/akkartik/mu/blob/main/README.md)
2021-10-31 21:06:57 +00:00
(stop before the section on “Syntax”). Can you figure out the
commands to prepare Mu on your computer?
2021-10-20 18:51:24 +00:00
Open a terminal and run the following commands to prepare Mu on your computer:
```
git clone https://github.com/akkartik/mu
cd mu
```
Run a small program to start:
```
2021-10-21 04:51:47 +00:00
./translate tutorial/task1.mu
2021-10-20 18:51:24 +00:00
qemu-system-i386 code.img
```
2021-10-31 21:13:00 +00:00
If you aren't on Linux (or Windows Subsystem for Linux), the command for
creating `code.img` will be slightly different:
2021-10-20 18:51:24 +00:00
```
2021-10-21 04:51:47 +00:00
./translate_emulated tutorial/task1.mu
2021-10-20 18:51:24 +00:00
qemu-system-i386 code.img
```
Either way, you should see this:
<img alt='screenshot of hello world on the Mu computer' src='task1.png'>
If you have any trouble at this point, don't waste _any_ time thinking about
it. Just [get in touch](http://akkartik.name/contact).
2021-10-21 04:51:47 +00:00
(You can look at `tutorial/task1.mu` at this point if you like. It's just 3
2021-10-20 18:51:24 +00:00
lines long. But don't worry if it doesn't make much sense.)
2021-10-27 17:11:11 +00:00
## Task 2: running automated tests
2021-10-20 18:51:24 +00:00
Here's a new program to run:
```
./translate tutorial/task2.mu
qemu-system-i386 code.img
```
(As before, I'll leave you to substitute `translate` with `translate_emulated`
if you're not on Linux.)
This time the screen will look like this:
<img alt='screenshot of failing test on the Mu computer' src='task2.png'>
2021-10-27 17:11:11 +00:00
Each of the dots represents an automated _test_, a little self-contained and
automated program run and its results verified. Mu comes with a lot of tests
(every function starting with 'test-' is a test), and it always runs all tests
on boot before it runs any program. You may have missed the dots when you ran
2021-10-21 04:51:47 +00:00
Task 1 because there were no failures. They were printed on the screen and
2021-10-20 18:51:24 +00:00
then immediately erased. In Task 2, however, we've deliberately included a
failing test. When any tests fail, Mu will immediately stop, showing you
2021-10-27 17:11:11 +00:00
messages from failing tests and implicitly asking you to first fix them. A lot
of learning programming is about building a sense for when you need to write
tests for the code you write.
2021-10-20 18:51:24 +00:00
(Don't worry just yet about what the message in the middle of all the dots means.)
2021-10-20 19:32:58 +00:00
## Task 3: configure your text editor
2021-10-20 18:51:24 +00:00
So far we haven't used a text editor yet, but we will now be starting to do
so. Before we do, it's worth spending a little bit of time setting your
preferred editor up to be a little more ergonomic. Mu comes with _syntax
highlighting_ settings for a few common text editors in the `editor/`
sub-directory. If you don't see your text editor there, or if you don't know
what to do with those files, [get in touch!](http://akkartik.name/contact)
Here's what my editor (Vim) looks like with these settings on the program of
Task 1:
<img alt='Vim text editor rendering some colors in a Mu program' src='task3.png'>
It's particularly useful to highlight _comments_ which the computer ignores
(everything on a line after a `#` character) and _strings_ within `""` double
quotes.
2021-10-20 19:32:58 +00:00
## Task 4: your first Mu statement
2021-10-20 18:51:24 +00:00
2021-10-24 06:38:14 +00:00
Mu is a statement-oriented language. Most statements translate into a single
2021-10-24 07:10:33 +00:00
instruction to the x86 processor. Quickly read the first two sections of the
2021-10-27 04:27:01 +00:00
[Mu reference](https://github.com/akkartik/mu/blob/main/mu.md) (about
2021-10-24 07:10:33 +00:00
functions and variables) to learn a little bit about it. It's ok if it doesn't
all make sense just yet. We'll reread it later.
2021-10-20 18:51:24 +00:00
Here's a skeleton of a Mu function that's missing a single statement.
```
fn the-answer -> _/eax: int {
var result/eax: int <- copy 0
# insert your statement below {
# }
return result
}
```
Try running it now:
```
./translate tutorial/task4.mu
qemu-system-i386 code.img
```
(As before, I'll leave you to substitute `translate` with `translate_emulated`
if you're not on Linux.)
You should see a failing test that looks something like this:
<img alt='screenshot of the initial (failing) state of task 4' src='task4-initial.png'>
Open `tutorial/task4.mu` in your text editor. Think about how to add a line
between the `{}` lines to make `the-answer` return 42. Rerun the above
2021-10-20 19:45:22 +00:00
commands. You'll know you got it right when all the tests pass, i.e. when the
rows of dots and text above are replaced by an empty screen.
2021-10-20 18:51:24 +00:00
Don't be afraid to run the above commands over and over again as you try out
different solutions. Here's a way to run them together so they're easy to
repeat.
```
./translate tutorial/task4.mu && qemu-system-i386 code.img
```
In programming there is no penalty for making mistakes, and once you arrive at
the correct solution you have it forever. As always, [feel free to ping me and
ask questions or share your experience](http://akkartik.name/contact).
2021-10-20 21:49:10 +00:00
Mu statements can have _outputs_ on the left (before the `<-`) and _inouts_
(either inputs or outputs) on the right, after the instruction name. The order
matters.
2021-10-20 21:47:18 +00:00
2021-10-20 18:51:24 +00:00
One gotcha to keep in mind is that numbers in Mu must always be in hexadecimal
notation, starting with `0x`. Use a calculator on your computer or phone to
2021-10-20 19:25:06 +00:00
convert 42 to hexadecimal, or [this page on your web browser](http://akkartik.github.io/mu/tutorial/converter.html).
2021-10-20 21:35:18 +00:00
## Task 5: variables in registers, variables in memory
We'll now practice managing one variable in a register (like last time) and
2021-10-24 06:38:14 +00:00
a second one in memory. To prepare for this, reread the first two sections of
the [Mu reference](https://github.com/akkartik/mu/blob/main/mu.md). The
section on [integer arithmetic](https://github.com/akkartik/mu/blob/main/mu.md#integer-arithmetic)
2021-10-20 21:47:18 +00:00
also provides a useful cheatsheet of the different forms of instructions you
will need.
2021-10-20 21:35:18 +00:00
2021-10-20 22:01:36 +00:00
Here's the exercise, with comments starting with `#` highlighting the gaps in
the program:
2021-10-20 21:35:18 +00:00
```
fn foo -> _/eax: int {
var x: int
# statement 1: store 3 in x
# statement 2: define a new variable 'y' in register eax and store 4 in it
# statement 3: add y to x, storing the result in x
return x
}
```
Again, you're encouraged to repeatedly try out your programs by running this
command as often as you like:
```
./translate tutorial/task5.mu && qemu-system-i386 code.img
```
2021-10-20 21:42:57 +00:00
2021-10-24 06:38:14 +00:00
The section on [integer arithmetic](https://github.com/akkartik/mu/blob/main/mu.md#integer-arithmetic)
2021-10-20 21:51:05 +00:00
shows that Mu consistently follows a few rules:
2021-10-20 21:42:57 +00:00
* Instructions that write to a register always have an output before the `<-`.
* Instructions that use an argument in memory always have it as the first
inout.
* Instructions that write to memory have a preposition in their name. Contrast
`add` to a register vs `add-to` a memory location, `subtract` from a
register vs `subtract-from` a memory location, and so on.
2021-10-20 21:58:45 +00:00
If you're stuck, as always, [my door is open](http://akkartik.name/contact).
You can also see a solution in the repository, though I won't link to it lest
it encourage peeking.
2021-10-22 03:46:04 +00:00
2021-10-27 05:23:29 +00:00
Where possible, try to store variables in registers rather than the stack. The
two main reasons to use the stack are:
* when you need lots of variables and run out of registers, and
* when you have types that don't fit in 32 bits.
2021-10-22 03:46:04 +00:00
## Task 6: getting used to a few error messages
If you're like me, seeing an error message can feel a bit stressful. It
usually happens when you're trying to get somewhere, it can feel like the
computer is being deliberately obtrusive, there's uncertainty about what's
wrong.
Well, I'd like to share one trick I recently learned to stop fearing error
messages: deliberately trigger them at a time and place of your choosing, when
you're mentally prepared to see them. That takes the stress right out.
Here's the skeleton for `tutorial/task6.mu`:
```
fn main {
var m: int
var r/edx: int <- copy 0
# insert a single statement below
}
```
(Reminder: `m` here is stored somewhere in memory, while `r` is stored in
register `edx`. Variables in registers must always be initialized when they're
created. Variables in memory must never be initialized, because they're always
implicitly initialized to 0.)
Now, starting from this skeleton, type the following statements in, one at a
time. Your program should only ever have one more statement than the above
skeleton. We'll try out the following statements, one by one:
* `m <- copy 3`
* `r <- copy 3`
* `copy-to r, 3`
* `copy-to m, 3`
Before typing in each one, write down whether you expect an error. After
trying it out, compare your answer. It can also be useful to write down the
exact error you see, and what it means, in your own words.
2021-10-22 03:46:04 +00:00
(Also, don't forget to delete the statement you typed in before you move on to
trying out the next one.)
2021-10-22 03:56:49 +00:00
Making notes about error messages is an example of a more general trick called
a [runbook](https://en.wikipedia.org/wiki/Runbook). Runbooks are aids to
memory, scripts for what to do when you run into a problem. People think worse
in the presence of stress, and runbooks can help reduce the need for thinking
in the presence of stress. They're a way of programming people (your future
self or others) rather than computers.
2021-10-22 04:02:37 +00:00
## Task 7: variables in registers, variables in memory (again)
Go back to your program in Task 5. Replace the first statement declaring
variable `x`:
```
var x: int
```
so it looks like this:
```
var x/edx: int <- copy 0
```
Run `translate` (or `translate_emulated`) as usual. Use your runbook from Task
6 to address the errors that arise.
## Task 8: primitive statements vs function calls
Managing variables in memory vs register is one of two key skills to
programming in Mu. The second key skill is calling primitives (which are
provided by the x86 instruction set) vs functions (which are defined in terms
of primitives).
To prepare for this task, reread the very first section of the Mu reference,
on [functions and function calls](https://github.com/akkartik/mu/blob/main/mu.md#functions).
Now look at the following programs. In each case, write down whether you
expect translation to return any errors and why.
```
fn f a: int {
}
fn main {
f 0
var r/eax: int <- copy 3
f r
var m: int
f m
}
```
(When you're ready, try the above program out as `./translate tutorial/task8a.mu`.)
```
fn f -> _/eax: int {
var result/ecx: int <- copy 0
return result
}
fn main {
var x/eax: int <- f
}
```
(When you're ready, try the above program out as `./translate tutorial/task8b.mu`.)
```
fn f -> _/eax: int {
return 3
}
fn main {
var x/ecx: int <- f
}
```
(When you're ready, try the above program out as `./translate tutorial/task8c.mu`.)
Functions have fewer restrictions than primitives on inouts, but more
restrictions on outputs. Inouts can be registers, or memory, or even literals.
This is why the first example above is legal. Outputs, however, _must_
hard-code specific registers, and function calls must write their outputs to
matching registers. This is why the third example above is illegal.
One subtlety here is that we only require agreement on output registers
between function call and function header. We don't actually have to `return`
the precise register a function header specifies. The return value can even be
a literal integer or in memory somewhere. The `return` is really just a `copy`
to the appropriate register(s). This is why the second example above is legal.
## Task 9: juggling registers between function calls
Here's a program:
```
fn f -> _/eax: int {
return 2
}
fn g -> _/eax: int {
return 3
}
fn add-f-and-g -> _/eax: int {
var x/eax: int <- f
var y/eax: int <- g
x <- add y
return x
}
```
What's wrong with this program? How can you fix it and pass all tests by
modifying just function `add-f-and-g`?
By convention, most functions in Mu return their results in register `eax`.
That creates a fair bit of contention for this register, and we often end up
having to move the output of a function call around to some other location to
free up space for the next function we need to call.
An alternative approach would be to distribute the load between registers so
that different functions use different output registers. That would reduce the
odds of conflict, but not eradicate them entirely. It would also add some
difficulty in calling functions; now you have to remember what register they
write their outputs to. It's unclear if the benefits of this alternative
outweigh the costs, so Mu follows long-established conventions in other
Assembly languages. I do, however, violate the `eax` convention in some cases
where a helper function is only narrowly useful in a single sort of
circumstance and registers are at a premium. See, for example, the definition
of the helper `_read-dithering-error` [when rendering images](http://akkartik.github.io/mu/html/511image.mu.html).
The leading underscore indicates that it's an internal detail of
`render-image`, and not really intended to be called by itself.
## Task 10: operating with fractional numbers
All our variables so far have had type `int` (integer), but there are limits
to what you can do with just whole integers. For example, here's the formula
a visitor to the US will require to convert distances mentioned on road signs
from miles to kilometers:
```
distance * 1.609
```
Write a function to perform this conversion. Some starting points:
* Reread [the section on variables and registers](https://github.com/akkartik/mu/blob/main/mu.md#variables-registers-and-memory)
with special attention to the `float` type.
2021-10-27 05:04:26 +00:00
* Read [the section on fractional arithmetic](https://github.com/akkartik/mu/blob/main/mu.md#fractional-arithmetic).
* One wrinkle is that the x86 instruction set doesn't permit literal
fractional arguments. So you'll need to _create_ 1.609 somehow. See the
2021-10-27 05:02:23 +00:00
section on moving values around under [operations on simple types](https://github.com/akkartik/mu/blob/main/mu.md#operations-on-simple-types).
This task has four source files in the repo that reveal more and more of the
answer. Start from the first, and bump down if you need a hint.
* tutorial/task10.mu
* tutorial/task10-hint1.mu
* tutorial/task10-hint2.mu
* tutorial/task10-hint3.mu
2021-10-27 06:57:25 +00:00
## Task 11: conditionally executing statements
Here's a fragment of Mu code:
```
{
compare x, 0
2021-10-27 16:37:35 +00:00
break-if->=
2021-10-27 06:57:25 +00:00
x <- copy 0
}
```
The combination of `compare` and `break` results in the variable `x` being
assigned 0 _if and only if_ its value was less than 0 at the beginning. The
`break` family of instructions is used to jump to the end of an enclosing `{}`
2021-10-27 16:37:35 +00:00
block if some condition is satisfied, skipping all intervening instructions.
2021-10-27 06:57:25 +00:00
To prepare for this task, read the sections in the Mu reference on
[`compare`](https://github.com/akkartik/mu/blob/main/mu.md#comparing-values)
and [branches](https://github.com/akkartik/mu/blob/main/mu.md#branches).
Now make the tests pass in `tutorial/task11.mu`. The goal is to implement our
colloquial understanding of the &ldquo;difference&rdquo; between two numbers.
In lay English, we say the difference between the first-place and third-place
runner in a race is two places. This answer doesn't depend on the order in
which we mention the runners; the difference between third and first is also
two.
The section on [integer arithmetic](https://github.com/akkartik/mu/blob/main/mu.md#integer-arithmetic)
is again worth referring to when working on this task.
2021-10-27 17:11:11 +00:00
2021-10-30 07:32:29 +00:00
## Task 12: fun with graphics
2021-10-27 17:11:11 +00:00
Here's a program to draw a rectangle on screen:
```
fn main screen: (addr screen) {
draw-line screen, 0x100/x1 0x100/y1, 0x300/x2 0x100/y2, 3/color=green
draw-line screen, 0x100/x1 0x200/y1, 0x300/x2 0x200/y2, 3/color=green
draw-line screen, 0x100/x1 0x100/y1, 0x100/x2 0x200/y2, 3/color=green
draw-line screen, 0x300/x1 0x100/y1, 0x300/x2 0x200/y2, 3/color=green
}
```
Play around with this function a bit, commenting out some statements with a
leading `#` and rerunning the program. Build up a sense for how the statements
map to lines on screen.
Modify the rectangle to start at the top-left corner on screen. How about
other corners?
Notice the `screen` variable. The `main` function always has access to a
2021-10-30 19:09:50 +00:00
`screen` variable, and any function wanting to draw to the screen will need
this variable. Later you'll learn to create and pass _fake screens_ within
2021-10-27 17:11:11 +00:00
automated tests, so that we can maintain confidence that our graphics
functions work as expected.
The &ldquo;real&rdquo; screen on a Mu computer is sized to 1024 (0x400) pixels
2021-10-30 19:09:50 +00:00
wide and 768 (0x300) pixels tall by default. Each pixel can take on [256 colors](http://akkartik.github.io/mu/html/vga_palette.html).
Many other screen configurations are possible, but it'll be up to you to learn
how to get to them.
2021-10-27 17:11:11 +00:00
Graphics in Mu often involve literal integer constants. To help remember what
they mean, you can attach _comment tokens_ -- any string without whitespace --
to a literal integer after a `/`. For example, an argument of `1` can
sometimes mean the width of a line, and at other times mean a boolean with a
true value. Getting into the habit of including comment tokens is an easy way
to make your programs easier to understand.
Another thing to notice in this program is the commas. Commas are entirely
optional in Mu, and it can be handy to drop them selectively to group
arguments together.
2021-10-30 07:26:42 +00:00
This is a good time to skim [Mu's vocabulary of functions for pixel graphics](https://github.com/akkartik/mu/blob/main/vocabulary.md#pixel-graphics).
2021-10-30 19:09:50 +00:00
They're fun to play with. Once you want to use a specific function, look for
details on the arguments it expects in `signatures.mu`.
2021-10-30 08:15:23 +00:00
## Task 13: reading input from keyboard
Read the section on [events](https://github.com/akkartik/mu/blob/main/vocabulary.md#events)
from Mu's vocabulary. Write a program to read a key from the keyboard. Mu
receives a keyboard object as the second argument of `main`:
```
fn main screen: (addr screen), keyboard: (addr keyboard) {
2021-10-30 19:25:17 +00:00
# edit in here {
# }
2021-10-30 08:15:23 +00:00
}
```
The _signature_ of `read-key` -- along with many other functions -- is in
[400.mu](https://github.com/akkartik/mu/blob/main/400.mu).
One wrinkle in this problem is that `read-key` may not actually return a key.
You have to keep retrying until it does. You may have already encountered the
list of `loop` operations in the section on [branches](https://github.com/akkartik/mu/blob/main/mu.md#branches).
It might be a good time to refresh your knowledge there.
## Task 14: streams and scanning input from the keyboard
2021-10-31 17:10:20 +00:00
Here's a skeleton of a program for processing text typed in at a keyboard:
2021-10-30 08:15:23 +00:00
```
fn main screen: (addr screen), keyboard: (addr keyboard) {
var in-storage: (stream byte 0x80)
var in/esi: (addr stream byte) <- address in-storage
read-line-from-keyboard keyboard, in, screen, 0xf/fg 0/bg
{
var done?/eax: boolean <- stream-empty? in
compare done?, 0/false
break-if-!=
var g/eax: code-point-utf8 <- read-code-point-utf8 in
2021-10-31 17:10:20 +00:00
# do stuff with g here
2021-10-30 08:15:23 +00:00
loop
}
}
```
`read-line-from-keyboard` reads keystrokes from the keyboard until you press
2021-10-31 17:10:20 +00:00
the `Enter` (also called `newline`) key, and accumulates them into a _stream_
of bytes. The loop then repeatedly reads _code points_ from the stream, and
these code points are encoded in a special encoding of Unicode called UTF-8.
Mu doesn't yet support non-Qwerty keyboards, but support for keyboards in
other parts of the world should be easy to add thanks to our support for
UTF-8.
2021-10-31 17:10:20 +00:00
This is a good time to skim the section in the Mu reference on
[streams](https://github.com/akkartik/mu/blob/main/mu.md#streams), just to
2021-10-31 17:10:20 +00:00
give yourself a sense of what you can do with them. Does the above program
make sense now? Feel free to experiment to make sense of it.
Can you modify it to print out the line a second time, after you've typed it
out until the `Enter` key? Can you print a space after every code point when
you print the line out a second time? You'll need to skim the section on
[printing to screen](https://github.com/akkartik/mu/blob/main/vocabulary.md#printing-to-screen)
from Mu's vocabulary. One common pattern: Mu programs read characters in
UTF-8, but print raw `code-point`s. Use `to-code-point` to decode UTF-8 and
get at the underlying raw `code-point`, and use `to-utf8` to encode a
`code-point` into a `code-point-utf8`. (This stuff is still under
construction; I hope to simplify it over time.)
2021-10-30 08:15:23 +00:00
## Task 15: generating cool patterns
Back to drawing to screen. Here's a program that draws every pixel on `screen`
with a `color` equal to the value of its `x` coordinate.
```
fn main screen: (addr screen) {
var y/eax: int <- copy 0
{
compare y, 0x300/screen-height=768
break-if->=
var x/edx: int <- copy 0
{
compare x, 0x400/screen-width=1024
break-if->=
var color/ecx: int <- copy x
pixel screen x, y, color
x <- increment
loop
}
y <- increment
loop
}
}
```
Before you run it, form a hypothesis about what the picture will look like.
The screen is 1024 pixels wide, but there are only 256 colors. What are the
implications of these facts?
After you run this program, try to modify it so every pixel gets a `color`
equal to the sum of its `x` and `y` coordinates. Can you guess what pattern
will result? Play around with more complex formulae. I particularly like the
sum of squares of `x` and `y` coordinates. Check out the [Mandelbrot set](https://github.com/akkartik/mu/blob/main/apps/mandelbrot-silhouette.mu)
for a really complex example of this sort of _procedural graphics_.
## Task 16: a simple app
We now know how to read keys from keyboard and draw on the screen. Try out
2021-10-30 08:15:23 +00:00
[tutorial/counter.mu](https://github.com/akkartik/mu/blob/main/tutorial/counter.mu)
which implements a simple counter app.
<img alt='screenshot of the counter app' src='counter.png'>
Now look at its code. Do all the parts make sense? Reread the extensive
vocabulary of functions for [drawing text to screen](https://github.com/akkartik/mu/blob/main/vocabulary.md#printing-to-screen).
2021-10-30 08:15:23 +00:00
---
Here's a more challenging problem. Build an app to convert celsius to
fahrenheit and vice versa. Two text fields, the `<Tab>` key to move the cursor
between them, type in either field and hit `<Enter>` to populate the other
field.
After you build it, compare your solution with [tutorial/converter.mu](https://github.com/akkartik/mu/blob/main/tutorial/converter.mu).
A second version breaks the program down into multiple functions, in
[tutorial/converter2.mu](https://github.com/akkartik/mu/blob/main/tutorial/converter.mu).
Can you see how the two do the same thing? Which one do you like better?
---
There's lots more programs in this repository. Look in the `apps/` directory.
Check out the Mu `shell/`, which persists data between runs to a separate data
disk. Hopefully this gives you some sense for how little software it takes to
build useful programs for yourself. Do you have any new ideas for programs to
write in Mu? [Tell me about them!](http://akkartik.name/about) I'd love to jam
with you.