about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-10-25 22:46:58 -0700
committerKartik K. Agaram <vc@akkartik.com>2021-10-25 22:50:04 -0700
commit0b16b27f49ca0206c73c6d6313dbe16f9b929732 (patch)
tree07512c96882d102f4a2b38a4e4c118469592dfce
parentd3be351847246cfbce93c7bf580c658426f8b76c (diff)
downloadmu-0b16b27f49ca0206c73c6d6313dbe16f9b929732.tar.gz
task: floating-point
I'm almost ready to throw in the towel. Writing out the rules makes it
obvious how hostile they are to most people.
-rw-r--r--mu.md25
-rw-r--r--tutorial/index.md30
-rw-r--r--tutorial/task8-hint1.mu58
-rw-r--r--tutorial/task8-hint2.mu60
-rw-r--r--tutorial/task8-hint3.mu58
-rw-r--r--tutorial/task8-solution1.mu57
-rw-r--r--tutorial/task8.mu57
7 files changed, 339 insertions, 6 deletions
diff --git a/mu.md b/mu.md
index 09e769fe..41f5c6ef 100644
--- a/mu.md
+++ b/mu.md
@@ -27,6 +27,9 @@ constants. Outputs are always variables in registers.
 Inouts in memory can be either inputs or outputs (if they're addresses being
 written to). Hence the name.
 
+Primitives usually require their inouts to be in specific combinations of
+memory and register. User-defined functions are flexible.
+
 Primitives can often write to arbitrary output registers. User-defined
 functions, however, require rigidly specified output registers.
 
@@ -113,8 +116,8 @@ Some notation for describing statement forms:
 
 ### Moving values around
 
-These instructions work with variables of any 32-bit type except `addr` and
-`float`.
+These instructions work with variables of any 32-bit type except `addr`,
+`byte` and `float`.
 
 ```
   var/reg <- copy var2/reg2
@@ -132,8 +135,8 @@ Byte variables have their own instructions:
   copy-byte-to *var1/reg1, var2/reg2  # var1 must have type (addr byte)
 ```
 
-Floating point instructions can be copied as well, but only to floating-point
-registers `xmm_`.
+Floating point variables can be copied as well, but only to or from
+floating-point registers `xmm_`.
 
 ```
   var/xreg <- copy var2/xreg2
@@ -163,6 +166,20 @@ Correspondingly, there are ways to convert floats into integers.
   var/reg <- truncate *var2/reg2
 ```
 
+Still, the absence of fractional literals is an annoyance. Mu provides some
+helpers to mitigate it somewhat:
+
+```
+  result/xmm0 <- rational nr: int, dr: int
+  fill-in-rational out: (addr float), nr: int, dr: int
+```
+
+These are functions, so the inouts have fewer restrictions while the outputs
+have more. The inouts can be registers, or memory, or even literals. The
+output for `rational` _must_ be in register `xmm0`. The `:` notation describes
+type constraints; `nr` and `dr` are required to be integers, while `out` must
+be an address to a float.
+
 ### Comparing values
 
 Work with variables of any 32-bit type. `addr` variables can only be compared
diff --git a/tutorial/index.md b/tutorial/index.md
index 9e30c9c6..c4cda66b 100644
--- a/tutorial/index.md
+++ b/tutorial/index.md
@@ -181,8 +181,8 @@ convert 42 to hexadecimal, or [this page on your web browser](http://akkartik.gi
 
 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 syntax description](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)
+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.
 
@@ -282,3 +282,29 @@ 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: floating point
+
+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 a distance on a road sign 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 floating-point arithmetic](https://github.com/akkartik/mu/blob/main/mu.md#floating-point-arithmetic).
+- One wrinkle is that the x86 instruction set doesn't permit literal
+  fractional arguments. So you'll need to _create_ 1.609 somehow. Relevant is
+  [the section on moving values around](https://github.com/akkartik/mu/blob/main/mu.md#moving-values-around).
+
+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
diff --git a/tutorial/task8-hint1.mu b/tutorial/task8-hint1.mu
new file mode 100644
index 00000000..903284f4
--- /dev/null
+++ b/tutorial/task8-hint1.mu
@@ -0,0 +1,58 @@
+fn to-km miles: float -> _/xmm1: float {
+  var result/xmm1: float <- copy miles
+  # fill in the blanks to compute miles * 1.609
+  # 1.609 = 1609 / 1000 (use function `rational`)
+
+  return result
+}
+
+fn test-to-km {
+  # 0 miles = 0 km
+  var zero: float  # Mu implicitly initializes variables in memory to 0
+  var result/xmm1: float <- to-km zero
+  compare result, zero
+  {
+    break-if-=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 0 miles = 0 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 1 mile = 1.609 km approximately
+  var one/eax: int <- copy 1
+  var one-float/xmm0: float <- convert one
+  result <- to-km one-float
+  var lower-bound/xmm0: float <- rational 0x649, 0x3e8  # 1609/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile > 1.609 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0x64a, 0x3e8  # 1610/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile < 1.610 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 2 miles = 3.218 km approximately
+  var two/eax: int <- copy 2
+  var two-float/xmm0: float <- convert two
+  result <- to-km two-float
+  var lower-bound/xmm0: float <- rational 0xc92, 0x3e8  # 3218/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles > 3.218 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0xc93, 0x3e8  # 3219/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles < 3.219 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+}
+
+fn main {
+}
diff --git a/tutorial/task8-hint2.mu b/tutorial/task8-hint2.mu
new file mode 100644
index 00000000..97ccfc51
--- /dev/null
+++ b/tutorial/task8-hint2.mu
@@ -0,0 +1,60 @@
+fn to-km miles: float -> _/xmm1: float {
+  var result/xmm1: float <- copy miles
+  # fill in the blanks to compute miles * 1.609
+  # 1.609 = 1609 / 1000 (use function `rational`)
+  # 1609 = 0x649 in hex
+  # 1000 = 0x3e8 in hex
+
+  return result
+}
+
+fn test-to-km {
+  # 0 miles = 0 km
+  var zero: float  # Mu implicitly initializes variables in memory to 0
+  var result/xmm1: float <- to-km zero
+  compare result, zero
+  {
+    break-if-=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 0 miles = 0 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 1 mile = 1.609 km approximately
+  var one/eax: int <- copy 1
+  var one-float/xmm0: float <- convert one
+  result <- to-km one-float
+  var lower-bound/xmm0: float <- rational 0x649, 0x3e8  # 1609/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile > 1.609 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0x64a, 0x3e8  # 1610/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile < 1.610 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 2 miles = 3.218 km approximately
+  var two/eax: int <- copy 2
+  var two-float/xmm0: float <- convert two
+  result <- to-km two-float
+  var lower-bound/xmm0: float <- rational 0xc92, 0x3e8  # 3218/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles > 3.218 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0xc93, 0x3e8  # 3219/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles < 3.219 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+}
+
+fn main {
+}
diff --git a/tutorial/task8-hint3.mu b/tutorial/task8-hint3.mu
new file mode 100644
index 00000000..e8a78c3d
--- /dev/null
+++ b/tutorial/task8-hint3.mu
@@ -0,0 +1,58 @@
+fn to-km miles: float -> _/xmm1: float {
+  var result/xmm1: float <- copy miles
+  var factor/xmm0: float <- rational 0x649, 0x3e8  # 1.609 = 1609/1000 in hex
+  # fill in the blanks to compute miles * factor
+
+  return result
+}
+
+fn test-to-km {
+  # 0 miles = 0 km
+  var zero: float  # Mu implicitly initializes variables in memory to 0
+  var result/xmm1: float <- to-km zero
+  compare result, zero
+  {
+    break-if-=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 0 miles = 0 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 1 mile = 1.609 km approximately
+  var one/eax: int <- copy 1
+  var one-float/xmm0: float <- convert one
+  result <- to-km one-float
+  var lower-bound/xmm0: float <- rational 0x649, 0x3e8  # 1609/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile > 1.609 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0x64a, 0x3e8  # 1610/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile < 1.610 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 2 miles = 3.218 km approximately
+  var two/eax: int <- copy 2
+  var two-float/xmm0: float <- convert two
+  result <- to-km two-float
+  var lower-bound/xmm0: float <- rational 0xc92, 0x3e8  # 3218/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles > 3.218 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0xc93, 0x3e8  # 3219/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles < 3.219 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+}
+
+fn main {
+}
diff --git a/tutorial/task8-solution1.mu b/tutorial/task8-solution1.mu
new file mode 100644
index 00000000..117bceb0
--- /dev/null
+++ b/tutorial/task8-solution1.mu
@@ -0,0 +1,57 @@
+fn to-km miles: float -> _/xmm1: float {
+  var result/xmm1: float <- copy miles
+  var factor/xmm0: float <- rational 0x649, 0x3e8  # 1.609 = 1609/1000 in hex
+  result <- multiply factor
+  return result
+}
+
+fn test-to-km {
+  # 0 miles = 0 km
+  var zero: float  # Mu implicitly initializes variables in memory to 0
+  var result/xmm1: float <- to-km zero
+  compare result, zero
+  {
+    break-if-=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 0 miles = 0 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 1 mile = 1.609 km approximately
+  var one/eax: int <- copy 1
+  var one-float/xmm0: float <- convert one
+  result <- to-km one-float
+  var lower-bound/xmm0: float <- rational 0x649, 0x3e8  # 1609/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile > 1.609 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0x64a, 0x3e8  # 1610/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile < 1.610 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 2 miles = 3.218 km approximately
+  var two/eax: int <- copy 2
+  var two-float/xmm0: float <- convert two
+  result <- to-km two-float
+  var lower-bound/xmm0: float <- rational 0xc92, 0x3e8  # 3218/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles > 3.218 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0xc93, 0x3e8  # 3219/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles < 3.219 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+}
+
+fn main {
+}
diff --git a/tutorial/task8.mu b/tutorial/task8.mu
new file mode 100644
index 00000000..e6bee1fa
--- /dev/null
+++ b/tutorial/task8.mu
@@ -0,0 +1,57 @@
+fn to-km miles: float -> _/xmm1: float {
+  var result/xmm1: float <- copy miles
+  # fill in the blanks to compute miles * 1.609
+
+  return result
+}
+
+fn test-to-km {
+  # 0 miles = 0 km
+  var zero: float  # Mu implicitly initializes variables in memory to 0
+  var result/xmm1: float <- to-km zero
+  compare result, zero
+  {
+    break-if-=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 0 miles = 0 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 1 mile = 1.609 km approximately
+  var one/eax: int <- copy 1
+  var one-float/xmm0: float <- convert one
+  result <- to-km one-float
+  var lower-bound/xmm0: float <- rational 0x649, 0x3e8  # 1609/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile > 1.609 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0x64a, 0x3e8  # 1610/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 1 mile < 1.610 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  # 2 miles = 3.218 km approximately
+  var two/eax: int <- copy 2
+  var two-float/xmm0: float <- convert two
+  result <- to-km two-float
+  var lower-bound/xmm0: float <- rational 0xc92, 0x3e8  # 3218/1000 in hex
+  {
+    compare result, lower-bound
+    break-if-float>=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles > 3.218 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+  var upper-bound/xmm0: float <- rational 0xc93, 0x3e8  # 3219/1000 in hex
+  {
+    compare result, upper-bound
+    break-if-float<=
+    draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "F - 2 miles < 3.219 km\n", 3/fg 0/bg
+    count-test-failure
+  }
+}
+
+fn main {
+}