From 0b16b27f49ca0206c73c6d6313dbe16f9b929732 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 25 Oct 2021 22:46:58 -0700 Subject: 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. --- mu.md | 25 ++++++++++++++++--- tutorial/index.md | 30 +++++++++++++++++++++-- tutorial/task8-hint1.mu | 58 +++++++++++++++++++++++++++++++++++++++++++ tutorial/task8-hint2.mu | 60 +++++++++++++++++++++++++++++++++++++++++++++ tutorial/task8-hint3.mu | 58 +++++++++++++++++++++++++++++++++++++++++++ tutorial/task8-solution1.mu | 57 ++++++++++++++++++++++++++++++++++++++++++ tutorial/task8.mu | 57 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 tutorial/task8-hint1.mu create mode 100644 tutorial/task8-hint2.mu create mode 100644 tutorial/task8-hint3.mu create mode 100644 tutorial/task8-solution1.mu create mode 100644 tutorial/task8.mu 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 { +} -- cgit 1.4.1-2-gfad0