about summary refs log tree commit diff stats
path: root/tutorial/index.md
blob: 72917533b469db723a8664fd92bc3e1b36da465b (plain) (blame)
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# A slow tour through Mu software on x86 computers

[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)).

This page is a guided tour through Mu's Readme 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.

_Prerequisites_

You will need:

* A computer with an x86 processor running Linux. We're going to slowly escape
  Linux, but we'll need it at the start. Mu works on other platforms, but be
  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.
* 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.

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.

## Task 1: getting started

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:

```
./translate tutorial/task1.mu
qemu-system-i386 code.img
```

If you aren't on Linux, the command for creating `code.img` will be slightly
different:

```
./translate_emulated tutorial/task1.mu
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).

(You can look at `tutorial/task1.mu` at this point if you like. It's just 3
lines long. But don't worry if it doesn't make much sense.)

## Task 2: running tests

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'>

Each of the dots represents a _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
Task 1 because there were no failures. They were printed on the screen and
then immediately erased. In Task 2, however, we've deliberately included a
failing test. When any tests fail, Mu will immediately stop, showing you
messages from failing tests and implicitly asking you to first fix them.

(Don't worry just yet about what the message in the middle of all the dots means.)

## Task 3: configure your text editor

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.

## Task 4: your first Mu statement

Mu is a statement-oriented language. Most statements translate into a single
instruction to the x86 processor. Quickly read the first two sections of the
[Mu reference](https://github.com/akkartik/mu/blob/main/mu.md) (about
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.

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
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.

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).

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.

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
convert 42 to hexadecimal, or [this page on your web browser](http://akkartik.github.io/mu/tutorial/converter.html).

## Task 5: variables in registers, variables in memory

We'll now practice managing one variable in a register (like last time) and
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)
also provides a useful cheatsheet of the different forms of instructions you
will need.

Here's the exercise, with comments starting with `#` highlighting the gaps in
the program:
```
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
```

The section on [integer arithmetic](https://github.com/akkartik/mu/blob/main/mu.md#integer-arithmetic)
shows that Mu consistently follows a few rules:
* 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.

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.

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.

## 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.

(Also, don't forget to delete the statement you typed in before you move on to
trying out the next one.)

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.

## 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: 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.
* 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
  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/task8.mu
* tutorial/task8-hint1.mu
* tutorial/task8-hint2.mu
* tutorial/task8-hint3.mu