sketching out a slow tutorial
This commit is contained in:
parent
909a0e2530
commit
619dc31dfc
|
@ -0,0 +1,19 @@
|
|||
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
|
||||
var result/eax: int <- do-add 3, 4
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, result, 3/fg=cyan 0/bg
|
||||
}
|
||||
|
||||
fn do-add a: int, b: int -> _/eax: int {
|
||||
var result/eax: int <- copy a
|
||||
result <- add b
|
||||
return result
|
||||
}
|
||||
|
||||
fn test-do-add {
|
||||
var observed/eax: int <- do-add 0, 0
|
||||
check-ints-equal observed, 0, "F - 0+0"
|
||||
observed <- do-add 3, 0
|
||||
check-ints-equal observed, 3, "F - 3+0"
|
||||
observed <- do-add 3, 2
|
||||
check-ints-equal observed, 5, "F - 3+2"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
|
||||
var result/eax: int <- do-add 3, 4
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, result, 3/fg=cyan 0/bg
|
||||
}
|
||||
|
||||
fn do-add a: int, b: int -> _/eax: int {
|
||||
var result/eax: int <- copy a
|
||||
result <- add b
|
||||
return result
|
||||
}
|
||||
|
||||
fn test-do-add {
|
||||
var observed/eax: int <- do-add 0, 0
|
||||
check-ints-equal observed, 0, "F - 0+0"
|
||||
observed <- do-add 3, 0
|
||||
check-ints-equal observed, 3, "F - 3+0"
|
||||
observed <- do-add 3, 2
|
||||
check-ints-equal observed, 5, "F - 3+2"
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
# Temperature Converter app
|
||||
# https://eugenkiss.github.io/7guis/tasks/#temp
|
||||
#
|
||||
# To build:
|
||||
# $ ./translate converter.mu
|
||||
# To run:
|
||||
# $ qemu-system-i386 code.img
|
||||
|
||||
# todo:
|
||||
# less duplication
|
||||
# error checking for input without hard-aborting
|
||||
|
||||
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
|
||||
# celsius numeric representation
|
||||
var zero: float
|
||||
var celsius/xmm1: float <- fahrenheit-to-celsius zero
|
||||
# celsius string representation
|
||||
var s-storage: (stream byte 0x10)
|
||||
var s/ecx: (addr stream byte) <- address s-storage
|
||||
write-float-decimal-approximate s, celsius, 2/decimal-places
|
||||
# celsius input/display
|
||||
var celsius-input-storage: gap-buffer
|
||||
var celsius-input/esi: (addr gap-buffer) <- address celsius-input-storage
|
||||
initialize-gap-buffer celsius-input, 8/capacity
|
||||
load-gap-buffer-from-stream celsius-input, s
|
||||
var cursor-in-celsius?/edx: boolean <- copy 0xffffffff/true
|
||||
# fahrenheit numeric representation
|
||||
var fahrenheit/xmm2: float <- celsius-to-fahrenheit celsius
|
||||
# fahrenheit string representation
|
||||
clear-stream s
|
||||
write-float-decimal-approximate s, fahrenheit, 2/decimal-places
|
||||
# fahrenheit input/display
|
||||
var fahrenheit-input-storage: gap-buffer
|
||||
var fahrenheit-input/edi: (addr gap-buffer) <- address fahrenheit-input-storage
|
||||
initialize-gap-buffer fahrenheit-input, 8/capacity
|
||||
load-gap-buffer-from-stream fahrenheit-input, s
|
||||
var cursor-in-fahrenheit?/ebx: boolean <- copy 0/false # exactly one cursor boolean must be true at any time
|
||||
# widget title
|
||||
set-cursor-position screen, 0x1f/x 0xe/y
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " Converter ", 0xf/fg 0x16/bg
|
||||
# event loop
|
||||
{
|
||||
# draw current state to screen
|
||||
clear-rect screen, 0x1f/xmin 0xf/ymin, 0x45/xmax 0x14/ymax, 0xc5/color
|
||||
var x/eax: int <- render-gap-buffer screen, celsius-input, 0x20/x 0x10/y, cursor-in-celsius?, 7/fg 0/bg
|
||||
x <- draw-text-rightward screen, " celsius = ", x, 0x45/xmax, 0x10/y, 7/fg 0xc5/bg
|
||||
x <- render-gap-buffer screen, fahrenheit-input, x 0x10/y, cursor-in-fahrenheit?, 7/fg 0/bg
|
||||
x <- draw-text-rightward screen, " fahrenheit", x, 0x45/xmax, 0x10/y, 7/fg 0xc5/bg
|
||||
# render a menu bar
|
||||
set-cursor-position screen, 0x21/x 0x12/y
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " tab ", 0/fg 0x5c/bg=highlight
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " switch sides ", 7/fg 0xc5/bg
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " enter ", 0/fg 0x5c/bg=highlight
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " convert ", 7/fg 0xc5/bg
|
||||
# process a single keystroke
|
||||
$main:input: {
|
||||
var key/eax: byte <- read-key keyboard
|
||||
var key/eax: grapheme <- copy key
|
||||
compare key, 0
|
||||
loop-if-=
|
||||
# tab = switch cursor between input areas
|
||||
compare key, 9/tab
|
||||
{
|
||||
break-if-!=
|
||||
cursor-in-celsius? <- not
|
||||
cursor-in-fahrenheit? <- not
|
||||
break $main:input
|
||||
}
|
||||
# enter = convert in appropriate direction
|
||||
compare key, 0xa/newline
|
||||
{
|
||||
break-if-!=
|
||||
{
|
||||
compare cursor-in-celsius?, 0/false
|
||||
break-if-=
|
||||
clear-stream s
|
||||
emit-gap-buffer celsius-input, s
|
||||
celsius <- parse-float-decimal s
|
||||
fahrenheit <- celsius-to-fahrenheit celsius
|
||||
clear-stream s
|
||||
write-float-decimal-approximate s, fahrenheit, 2/decimal-places
|
||||
clear-gap-buffer fahrenheit-input
|
||||
load-gap-buffer-from-stream fahrenheit-input, s
|
||||
}
|
||||
{
|
||||
compare cursor-in-fahrenheit?, 0/false
|
||||
break-if-=
|
||||
clear-stream s
|
||||
emit-gap-buffer fahrenheit-input, s
|
||||
{
|
||||
var tmp/xmm1: float <- parse-float-decimal s
|
||||
fahrenheit <- copy tmp
|
||||
}
|
||||
celsius <- fahrenheit-to-celsius fahrenheit
|
||||
clear-stream s
|
||||
write-float-decimal-approximate s, celsius, 2/decimal-places
|
||||
clear-gap-buffer celsius-input
|
||||
load-gap-buffer-from-stream celsius-input, s
|
||||
}
|
||||
break $main:input
|
||||
}
|
||||
# otherwise pass key to appropriate input area
|
||||
compare cursor-in-celsius?, 0/false
|
||||
{
|
||||
break-if-=
|
||||
edit-gap-buffer celsius-input, key
|
||||
break $main:input
|
||||
}
|
||||
compare cursor-in-fahrenheit?, 0/false
|
||||
{
|
||||
break-if-=
|
||||
edit-gap-buffer fahrenheit-input, key
|
||||
break $main:input
|
||||
}
|
||||
}
|
||||
loop
|
||||
}
|
||||
}
|
||||
|
||||
fn fahrenheit-to-celsius f: float -> _/xmm1: float {
|
||||
var result/xmm1: float <- copy f
|
||||
var thirty-two/eax: int <- copy 0x20
|
||||
var thirty-two-f/xmm0: float <- convert thirty-two
|
||||
result <- subtract thirty-two-f
|
||||
var factor/xmm0: float <- rational 5, 9
|
||||
result <- multiply factor
|
||||
return result
|
||||
}
|
||||
|
||||
fn celsius-to-fahrenheit c: float -> _/xmm2: float {
|
||||
var result/xmm1: float <- copy c
|
||||
var factor/xmm0: float <- rational 9, 5
|
||||
result <- multiply factor
|
||||
var thirty-two/eax: int <- copy 0x20
|
||||
var thirty-two-f/xmm0: float <- convert thirty-two
|
||||
result <- add thirty-two-f
|
||||
return result
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
# Temperature Converter app
|
||||
# https://eugenkiss.github.io/7guis/tasks/#temp
|
||||
#
|
||||
# To build:
|
||||
# $ ./translate converter2.mu
|
||||
# To run:
|
||||
# $ qemu-system-i386 code.img
|
||||
|
||||
# todo:
|
||||
# error checking for input without hard-aborting
|
||||
|
||||
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
|
||||
# imgui approach
|
||||
forever {
|
||||
number-input fahrenheit, cursor-in-fahrenheit?
|
||||
number-input celsius, cursor-in-celsius?
|
||||
if (menu-key 9/tab "Tab" "switch sides") { # requires non-blocking input
|
||||
cursor-in-celsius? <- not
|
||||
cursor-in-fahrenheit? <- not
|
||||
}
|
||||
if (menu-key 0xa/newline "Enter" "convert") {
|
||||
if cursor-in-fahrenheit
|
||||
celsius = fahrenheit-to-celsius fahrenheit
|
||||
else
|
||||
fahrenheit = celsius-to-fahrenheit celsius
|
||||
}
|
||||
}
|
||||
# celsius numeric representation
|
||||
var zero: float
|
||||
var celsius/xmm1: float <- fahrenheit-to-celsius zero
|
||||
# celsius input/display
|
||||
var celsius-input-storage: gap-buffer
|
||||
var celsius-input/esi: (addr gap-buffer) <- address celsius-input-storage
|
||||
initialize-gap-buffer celsius-input, 8/capacity
|
||||
value-to-input celsius, celsius-input
|
||||
# fahrenheit numeric representation
|
||||
var fahrenheit/xmm2: float <- celsius-to-fahrenheit celsius
|
||||
# fahrenheit input/display
|
||||
var fahrenheit-input-storage: gap-buffer
|
||||
var fahrenheit-input/edi: (addr gap-buffer) <- address fahrenheit-input-storage
|
||||
initialize-gap-buffer fahrenheit-input, 8/capacity
|
||||
value-to-input fahrenheit, fahrenheit-input
|
||||
# cursor toggle
|
||||
var cursor-in-celsius?/edx: boolean <- copy 0xffffffff/true
|
||||
#
|
||||
render-title screen
|
||||
# event loop
|
||||
{
|
||||
# render
|
||||
render-state celsius-input, fahrenheit-input, cursor-in-celsius?
|
||||
render-menu-bar screen
|
||||
# process a single keystroke
|
||||
$main:input: {
|
||||
var key/eax: byte <- read-key keyboard
|
||||
var key/eax: grapheme <- copy key
|
||||
compare key, 0
|
||||
loop-if-=
|
||||
# tab = switch cursor between input areas
|
||||
compare key, 9/tab
|
||||
{
|
||||
break-if-!=
|
||||
cursor-in-celsius? <- not
|
||||
break $main:input
|
||||
}
|
||||
# enter = convert in appropriate direction
|
||||
compare key, 0xa/newline
|
||||
{
|
||||
break-if-!=
|
||||
{
|
||||
compare cursor-in-celsius?, 0/false
|
||||
break-if-=
|
||||
var tmp/xmm0: float <- input-to-value celsius-input
|
||||
celsius <- copy tmp
|
||||
fahrenheit <- celsius-to-fahrenheit celsius
|
||||
value-to-input fahrenheit, fahrenheit-input
|
||||
break $main:input
|
||||
}
|
||||
var tmp/xmm0: float <- input-to-value fahrenheit-input
|
||||
fahrenheit <- copy tmp
|
||||
celsius <- fahrenheit-to-celsius fahrenheit
|
||||
value-to-input celsius, celsius-input
|
||||
break $main:input
|
||||
}
|
||||
# otherwise pass key to appropriate input area
|
||||
compare cursor-in-celsius?, 0/false
|
||||
{
|
||||
break-if-=
|
||||
edit-gap-buffer celsius-input, key
|
||||
break $main:input
|
||||
}
|
||||
edit-gap-buffer fahrenheit-input, key
|
||||
}
|
||||
loop
|
||||
}
|
||||
}
|
||||
|
||||
# business logic
|
||||
|
||||
fn fahrenheit-to-celsius f: float -> _/xmm1: float {
|
||||
var result/xmm1: float <- copy f
|
||||
var thirty-two/eax: int <- copy 0x20
|
||||
var thirty-two-f/xmm0: float <- convert thirty-two
|
||||
result <- subtract thirty-two-f
|
||||
var factor/xmm0: float <- rational 5, 9
|
||||
result <- multiply factor
|
||||
return result
|
||||
}
|
||||
|
||||
fn celsius-to-fahrenheit c: float -> _/xmm2: float {
|
||||
var result/xmm1: float <- copy c
|
||||
var factor/xmm0: float <- rational 9, 5
|
||||
result <- multiply factor
|
||||
var thirty-two/eax: int <- copy 0x20
|
||||
var thirty-two-f/xmm0: float <- convert thirty-two
|
||||
result <- add thirty-two-f
|
||||
return result
|
||||
}
|
||||
|
||||
# helpers for UI
|
||||
|
||||
fn input-to-value in: (addr gap-buffer) -> _/xmm0: float {
|
||||
var s-storage: (stream byte 0x10)
|
||||
var s/ecx: (addr stream byte) <- address s-storage
|
||||
emit-gap-buffer in, s
|
||||
var result/xmm1: float <- parse-float-decimal s
|
||||
return result
|
||||
}
|
||||
|
||||
fn value-to-input in: float, out: (addr gap-buffer) {
|
||||
var s-storage: (stream byte 0x10)
|
||||
var s/ecx: (addr stream byte) <- address s-storage
|
||||
write-float-decimal-approximate s, in, 2/decimal-places
|
||||
clear-gap-buffer out
|
||||
load-gap-buffer-from-stream out, s
|
||||
}
|
||||
|
||||
# helpers for rendering to screen
|
||||
# magic constants here need to be consistent between functions
|
||||
|
||||
fn render-title screen: (addr screen) {
|
||||
set-cursor-position screen, 0x1f/x 0xe/y
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " Converter ", 0xf/fg 0x16/bg
|
||||
}
|
||||
|
||||
fn render-state screen: (addr screen), c: (addr gap-buffer), f: (addr gap-buffer), cursor-in-c?: boolean {
|
||||
clear-rect screen, 0x1f/xmin 0xf/ymin, 0x45/xmax 0x14/ymax, 0xc5/color
|
||||
var x/eax: int <- render-gap-buffer screen, c, 0x20/x 0x10/y, cursor-in-c?, 7/fg 0/bg
|
||||
x <- draw-text-rightward screen, " celsius = ", x, 0x45/xmax, 0x10/y, 7/fg 0xc5/bg
|
||||
var cursor-in-f?/ecx: boolean <- copy cursor-in-c?
|
||||
cursor-in-f? <- not
|
||||
x <- render-gap-buffer screen, f, x 0x10/y, cursor-in-f?, 7/fg 0/bg
|
||||
x <- draw-text-rightward screen, " fahrenheit", x, 0x45/xmax, 0x10/y, 7/fg 0xc5/bg
|
||||
}
|
||||
|
||||
fn render-menu-bar screen: (addr screen) {
|
||||
set-cursor-position screen, 0x21/x 0x12/y
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " tab ", 0/fg 0x5c/bg=highlight
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " switch sides ", 7/fg 0xc5/bg
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " enter ", 0/fg 0x5c/bg=highlight
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " convert ", 7/fg 0xc5/bg
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
# Counter app
|
||||
# https://eugenkiss.github.io/7guis/tasks/#counter
|
||||
#
|
||||
# To build:
|
||||
# $ ./translate counter.mu
|
||||
# To run:
|
||||
# $ qemu-system-i386 code.img
|
||||
|
||||
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
|
||||
var count/ecx: int <- copy 0
|
||||
# widget title
|
||||
set-cursor-position screen, 0x1f/x 0xe/y
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " Counter ", 0xf/fg 0x16/bg
|
||||
# event loop
|
||||
{
|
||||
# draw current state to screen
|
||||
clear-rect screen, 0x1f/xmin 0xf/ymin, 0x40/xmax 0x14/ymax, 0xc5/color
|
||||
set-cursor-position screen, 0x20/x 0x10/y
|
||||
draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, count, 7/fg 0xc5/bg
|
||||
# render a menu bar
|
||||
set-cursor-position screen, 0x24/x 0x12/y
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " enter ", 0/fg 0x5c/bg=highlight
|
||||
draw-text-rightward-from-cursor-over-full-screen screen, " increment ", 7/fg 0xc5/bg
|
||||
# process a single keystroke
|
||||
{
|
||||
var key/eax: byte <- read-key keyboard
|
||||
compare key, 0
|
||||
loop-if-=
|
||||
compare key, 0xa/newline
|
||||
break-if-!=
|
||||
count <- increment
|
||||
}
|
||||
loop
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
# 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.
|
||||
|
||||
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 apps/ex5.mu
|
||||
qemu-system-i386 code.img
|
||||
```
|
||||
|
||||
If you aren't on Linux, the command for creating `code.img` will be slightly
|
||||
different:
|
||||
|
||||
```
|
||||
./translate_emulated apps/ex5.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 `apps/ex5.mu` at this point if you like. It's just a few
|
||||
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 is a _test_, a little self-contained and automated program
|
||||
run with an expected result. Mu comes with a lot of tests, and it always runs
|
||||
all tests 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. Read the first section of the [Mu syntax
|
||||
description](https://github.com/akkartik/mu/blob/main/mu.md) to learn a little
|
||||
bit about it.
|
||||
|
||||
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 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).
|
||||
|
||||
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/task4-calculator.html).
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,7 @@
|
|||
fn test-1 {
|
||||
check-ints-equal 1, 2, "F - test-1"
|
||||
}
|
||||
|
||||
fn main {
|
||||
# do nothing
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
function convert() {
|
||||
var n = parseInt(document.getElementById('input').value);
|
||||
if (isNaN(n))
|
||||
document.getElementById('result').innerHTML = "not a number";
|
||||
else
|
||||
document.getElementById('result').innerHTML = "0x" + n.toString(16);
|
||||
event.preventDefault();
|
||||
}
|
||||
</script>
|
||||
<body style='font-size:200%'>
|
||||
<form style='margin:5em' onSubmit='convert()'>
|
||||
<input style='width:4em; font-size:100%' type='text' id='input'/> <input type='submit' style='width=6em; font-size:100%' value='convert'> = <span id='result' style='display:inline-block; min-width:4em; border-bottom:1px solid; text-align: right'></span>
|
||||
</form>
|
||||
</body>
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,15 @@
|
|||
fn the-answer -> _/eax: int {
|
||||
var result/eax: int <- copy 0
|
||||
# insert your statement below {
|
||||
result <- copy 0x2a
|
||||
# }
|
||||
return result
|
||||
}
|
||||
|
||||
fn test-the-answer {
|
||||
var result/eax: int <- the-answer
|
||||
check-ints-equal result, 0x2a, "F - the-answer should return 42, but didn't."
|
||||
}
|
||||
|
||||
fn main {
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
fn the-answer -> _/eax: int {
|
||||
var result/eax: int <- copy 0
|
||||
# insert your statement below {
|
||||
|
||||
# }
|
||||
return result
|
||||
}
|
||||
|
||||
fn test-the-answer {
|
||||
var result/eax: int <- the-answer
|
||||
check-ints-equal result, 0x2a, "F - the-answer should return 42, but didn't."
|
||||
}
|
||||
|
||||
fn main {
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
" when opening files in this directory, load vimrc from cwd (top-level)
|
||||
source vimrc.vim
|
Loading…
Reference in New Issue