diff --git a/html/mandelbrot-fixed.mu.html b/html/mandelbrot-fixed.mu.html index 3a09bf53..23fcdd66 100644 --- a/html/mandelbrot-fixed.mu.html +++ b/html/mandelbrot-fixed.mu.html @@ -59,223 +59,228 @@ if ('onhashchange' in window) {
1 # Mandelbrot set using fixed-point numbers. 2 # - 3 # To build: - 4 # $ ./translate mandelbrot-fixed.mu - 5 # To run: - 6 # $ qemu-system-i386 code.img - 7 - 8 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { - 9 mandelbrot screen - 10 } - 11 - 12 # Since they still look like int types, we'll append a '-f' suffix to variable - 13 # names to designate fixed-point numbers. - 14 - 15 fn int-to-fixed in: int -> _/eax: int { - 16 var result-f/eax: int <- copy in - 17 result-f <- shift-left 8/fixed-precision - 18 { - 19 break-if-not-overflow - 20 abort "int-to-fixed: overflow" - 21 } - 22 return result-f - 23 } - 24 - 25 fn fixed-to-int in-f: int -> _/eax: int { - 26 var result/eax: int <- copy in-f - 27 result <- shift-right-signed 8/fixed-precision - 28 return result - 29 } - 30 - 31 # The process of throwing bits away always adjusts a number towards -infinity. - 32 fn test-fixed-conversion { - 33 # 0 - 34 var f/eax: int <- int-to-fixed 0 - 35 var result/eax: int <- fixed-to-int f - 36 check-ints-equal result, 0, "F - test-fixed-conversion - 0" - 37 # 1 - 38 var f/eax: int <- int-to-fixed 1 - 39 var result/eax: int <- fixed-to-int f - 40 check-ints-equal result, 1, "F - test-fixed-conversion - 1" - 41 # -1 - 42 var f/eax: int <- int-to-fixed -1 - 43 var result/eax: int <- fixed-to-int f - 44 check-ints-equal result, -1, "F - test-fixed-conversion - -1" - 45 # 0.5 = 1/2 - 46 var f/eax: int <- int-to-fixed 1 - 47 f <- shift-right-signed 1 - 48 var result/eax: int <- fixed-to-int f - 49 check-ints-equal result, 0, "F - test-fixed-conversion - 0.5" - 50 # -0.5 = -1/2 - 51 var f/eax: int <- int-to-fixed -1 + 3 # Install: + 4 # $ git clone https://github.com/akkartik/mu + 5 # $ cd mu + 6 # Build on Linux: + 7 # $ ./translate mandelbrot-fixed.mu + 8 # Build on other platforms (slow): + 9 # $ ./translate_emulated mandelbrot-fixed.mu + 10 # To run: + 11 # $ qemu-system-i386 code.img + 12 + 13 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + 14 mandelbrot screen + 15 } + 16 + 17 # Since they still look like int types, we'll append a '-f' suffix to variable + 18 # names to designate fixed-point numbers. + 19 + 20 fn int-to-fixed in: int -> _/eax: int { + 21 var result-f/eax: int <- copy in + 22 result-f <- shift-left 8/fixed-precision + 23 { + 24 break-if-not-overflow + 25 abort "int-to-fixed: overflow" + 26 } + 27 return result-f + 28 } + 29 + 30 fn fixed-to-int in-f: int -> _/eax: int { + 31 var result/eax: int <- copy in-f + 32 result <- shift-right-signed 8/fixed-precision + 33 return result + 34 } + 35 + 36 # The process of throwing bits away always adjusts a number towards -infinity. + 37 fn test-fixed-conversion { + 38 # 0 + 39 var f/eax: int <- int-to-fixed 0 + 40 var result/eax: int <- fixed-to-int f + 41 check-ints-equal result, 0, "F - test-fixed-conversion - 0" + 42 # 1 + 43 var f/eax: int <- int-to-fixed 1 + 44 var result/eax: int <- fixed-to-int f + 45 check-ints-equal result, 1, "F - test-fixed-conversion - 1" + 46 # -1 + 47 var f/eax: int <- int-to-fixed -1 + 48 var result/eax: int <- fixed-to-int f + 49 check-ints-equal result, -1, "F - test-fixed-conversion - -1" + 50 # 0.5 = 1/2 + 51 var f/eax: int <- int-to-fixed 1 52 f <- shift-right-signed 1 - 53 var result/eax: int <- fixed-to-int f - 54 check-ints-equal result, -1, "F - test-fixed-conversion - -0.5" - 55 # 1.5 = 3/2 - 56 var f/eax: int <- int-to-fixed 3 + 53 var result/eax: int <- fixed-to-int f + 54 check-ints-equal result, 0, "F - test-fixed-conversion - 0.5" + 55 # -0.5 = -1/2 + 56 var f/eax: int <- int-to-fixed -1 57 f <- shift-right-signed 1 - 58 var result/eax: int <- fixed-to-int f - 59 check-ints-equal result, 1, "F - test-fixed-conversion - 1.5" - 60 # -1.5 = -3/2 - 61 var f/eax: int <- int-to-fixed -3 + 58 var result/eax: int <- fixed-to-int f + 59 check-ints-equal result, -1, "F - test-fixed-conversion - -0.5" + 60 # 1.5 = 3/2 + 61 var f/eax: int <- int-to-fixed 3 62 f <- shift-right-signed 1 - 63 var result/eax: int <- fixed-to-int f - 64 check-ints-equal result, -2, "F - test-fixed-conversion - -1.5" - 65 # 1.25 = 5/4 - 66 var f/eax: int <- int-to-fixed 5 - 67 f <- shift-right-signed 2 - 68 var result/eax: int <- fixed-to-int f - 69 check-ints-equal result, 1, "F - test-fixed-conversion - 1.25" - 70 # -1.25 = -5/4 - 71 var f/eax: int <- int-to-fixed -5 + 63 var result/eax: int <- fixed-to-int f + 64 check-ints-equal result, 1, "F - test-fixed-conversion - 1.5" + 65 # -1.5 = -3/2 + 66 var f/eax: int <- int-to-fixed -3 + 67 f <- shift-right-signed 1 + 68 var result/eax: int <- fixed-to-int f + 69 check-ints-equal result, -2, "F - test-fixed-conversion - -1.5" + 70 # 1.25 = 5/4 + 71 var f/eax: int <- int-to-fixed 5 72 f <- shift-right-signed 2 - 73 var result/eax: int <- fixed-to-int f - 74 check-ints-equal result, -2, "F - test-fixed-conversion - -1.25" - 75 } - 76 - 77 # special routines for multiplying and dividing fixed-point numbers - 78 - 79 fn multiply-fixed a: int, b: int -> _/eax: int { - 80 var result/eax: int <- copy a - 81 result <- multiply b - 82 { - 83 break-if-not-overflow - 84 abort "multiply-fixed: overflow" - 85 } - 86 result <- shift-right-signed 8/fixed-precision - 87 return result - 88 } - 89 - 90 fn divide-fixed a-f: int, b-f: int -> _/eax: int { - 91 var result-f/eax: int <- copy a-f - 92 result-f <- shift-left 8/fixed-precision - 93 { - 94 break-if-not-overflow - 95 abort "divide-fixed: overflow" - 96 } - 97 var dummy-remainder/edx: int <- copy 0 - 98 result-f, dummy-remainder <- integer-divide result-f, b-f - 99 return result-f -100 } -101 -102 # multiplying or dividing by an integer can use existing instructions. -103 -104 # adding and subtracting two fixed-point numbers can use existing instructions. -105 -106 fn mandelbrot screen: (addr screen) { -107 var a/eax: int <- copy 0 -108 var b/ecx: int <- copy 0 -109 a, b <- screen-size screen -110 var width-f/esi: int <- copy a -111 width-f <- shift-left 0xb/log2-font-width-and-fixed-precision # 3 + 8 = 11 -112 var height-f/edi: int <- copy b -113 height-f <- shift-left 0xc/log2-font-height-and-fixed-precision # 4 + 8 = 12 -114 var y/ecx: int <- copy 0 -115 { -116 compare y, height-f -117 break-if->= -118 var imaginary-f/ebx: int <- viewport-to-imaginary-f y, width-f, height-f -119 var x/eax: int <- copy 0 -120 { -121 compare x, width-f -122 break-if->= -123 var real-f/edx: int <- viewport-to-real-f x, width-f -124 var iterations/esi: int <- mandelbrot-iterations-for-point real-f, imaginary-f, 0x400/max -125 compare iterations, 0x400/max -126 { -127 break-if->= -128 pixel screen, x, y, 0xf/white -129 } + 73 var result/eax: int <- fixed-to-int f + 74 check-ints-equal result, 1, "F - test-fixed-conversion - 1.25" + 75 # -1.25 = -5/4 + 76 var f/eax: int <- int-to-fixed -5 + 77 f <- shift-right-signed 2 + 78 var result/eax: int <- fixed-to-int f + 79 check-ints-equal result, -2, "F - test-fixed-conversion - -1.25" + 80 } + 81 + 82 # special routines for multiplying and dividing fixed-point numbers + 83 + 84 fn multiply-fixed a: int, b: int -> _/eax: int { + 85 var result/eax: int <- copy a + 86 result <- multiply b + 87 { + 88 break-if-not-overflow + 89 abort "multiply-fixed: overflow" + 90 } + 91 result <- shift-right-signed 8/fixed-precision + 92 return result + 93 } + 94 + 95 fn divide-fixed a-f: int, b-f: int -> _/eax: int { + 96 var result-f/eax: int <- copy a-f + 97 result-f <- shift-left 8/fixed-precision + 98 { + 99 break-if-not-overflow +100 abort "divide-fixed: overflow" +101 } +102 var dummy-remainder/edx: int <- copy 0 +103 result-f, dummy-remainder <- integer-divide result-f, b-f +104 return result-f +105 } +106 +107 # multiplying or dividing by an integer can use existing instructions. +108 +109 # adding and subtracting two fixed-point numbers can use existing instructions. +110 +111 fn mandelbrot screen: (addr screen) { +112 var a/eax: int <- copy 0 +113 var b/ecx: int <- copy 0 +114 a, b <- screen-size screen +115 var width-f/esi: int <- copy a +116 width-f <- shift-left 0xb/log2-font-width-and-fixed-precision # 3 + 8 = 11 +117 var height-f/edi: int <- copy b +118 height-f <- shift-left 0xc/log2-font-height-and-fixed-precision # 4 + 8 = 12 +119 var y/ecx: int <- copy 0 +120 { +121 compare y, height-f +122 break-if->= +123 var imaginary-f/ebx: int <- viewport-to-imaginary-f y, width-f, height-f +124 var x/eax: int <- copy 0 +125 { +126 compare x, width-f +127 break-if->= +128 var real-f/edx: int <- viewport-to-real-f x, width-f +129 var iterations/esi: int <- mandelbrot-iterations-for-point real-f, imaginary-f, 0x400/max 130 compare iterations, 0x400/max 131 { -132 break-if-< -133 pixel screen, x, y, 0/black +132 break-if->= +133 pixel screen, x, y, 0xf/white 134 } -135 x <- increment -136 loop -137 } -138 y <- increment -139 loop -140 } -141 } -142 -143 fn mandelbrot-iterations-for-point real-f: int, imaginary-f: int, max: int -> _/esi: int { -144 var x-f/esi: int <- copy 0 -145 var y-f/edi: int <- copy 0 -146 var iterations/ecx: int <- copy 0 -147 { -148 var done?/eax: boolean <- mandelbrot-done? x-f, y-f -149 compare done?, 0/false -150 break-if-!= -151 compare iterations, max -152 break-if->= -153 var x2-f/edx: int <- mandelbrot-x x-f, y-f, real-f -154 var y2-f/ebx: int <- mandelbrot-y x-f, y-f, imaginary-f -155 x-f <- copy x2-f -156 y-f <- copy y2-f -157 iterations <- increment -158 loop -159 } -160 return iterations -161 } -162 -163 fn mandelbrot-done? x-f: int, y-f: int -> _/eax: boolean { -164 # x*x + y*y > 4 -165 var tmp-f/eax: int <- multiply-fixed x-f, x-f -166 var result-f/ecx: int <- copy tmp-f -167 tmp-f <- multiply-fixed y-f, y-f -168 result-f <- add tmp-f -169 compare result-f, 0x400/4 -170 { -171 break-if-> -172 return 0/false -173 } -174 return 1/true -175 } -176 -177 fn mandelbrot-x x-f: int, y-f: int, real-f: int -> _/edx: int { -178 # x*x - y*y + real -179 var tmp-f/eax: int <- multiply-fixed x-f, x-f -180 var result-f/ecx: int <- copy tmp-f -181 tmp-f <- multiply-fixed y-f, y-f -182 result-f <- subtract tmp-f -183 result-f <- add real-f -184 return result-f -185 } -186 -187 fn mandelbrot-y x-f: int, y-f: int, imaginary-f: int -> _/ebx: int { -188 # 2*x*y + imaginary -189 var result-f/eax: int <- copy x-f -190 result-f <- shift-left 1/log2 -191 result-f <- multiply-fixed result-f, y-f -192 result-f <- add imaginary-f -193 return result-f -194 } -195 -196 # Scale (x, y) pixel coordinates to a complex plane where the viewport width -197 # ranges from -2 to +2. Viewport height just follows the viewport's aspect -198 # ratio. -199 -200 fn viewport-to-real-f x: int, width-f: int -> _/edx: int { -201 # (x - width/2)*4/width -202 var result-f/eax: int <- int-to-fixed x -203 var half-width-f/ecx: int <- copy width-f -204 half-width-f <- shift-right-signed 1/log2 -205 result-f <- subtract half-width-f -206 result-f <- shift-left 2/log4 -207 result-f <- divide-fixed result-f, width-f -208 return result-f -209 } -210 -211 fn viewport-to-imaginary-f y: int, width-f: int, height-f: int -> _/ebx: int { -212 # (y - height/2)*4/width -213 var result-f/eax: int <- int-to-fixed y -214 shift-right-signed height-f, 1/log2 -215 result-f <- subtract height-f -216 result-f <- shift-left 2/log4 -217 result-f <- divide-fixed result-f, width-f -218 return result-f -219 } +135 compare iterations, 0x400/max +136 { +137 break-if-< +138 pixel screen, x, y, 0/black +139 } +140 x <- increment +141 loop +142 } +143 y <- increment +144 loop +145 } +146 } +147 +148 fn mandelbrot-iterations-for-point real-f: int, imaginary-f: int, max: int -> _/esi: int { +149 var x-f/esi: int <- copy 0 +150 var y-f/edi: int <- copy 0 +151 var iterations/ecx: int <- copy 0 +152 { +153 var done?/eax: boolean <- mandelbrot-done? x-f, y-f +154 compare done?, 0/false +155 break-if-!= +156 compare iterations, max +157 break-if->= +158 var x2-f/edx: int <- mandelbrot-x x-f, y-f, real-f +159 var y2-f/ebx: int <- mandelbrot-y x-f, y-f, imaginary-f +160 x-f <- copy x2-f +161 y-f <- copy y2-f +162 iterations <- increment +163 loop +164 } +165 return iterations +166 } +167 +168 fn mandelbrot-done? x-f: int, y-f: int -> _/eax: boolean { +169 # x*x + y*y > 4 +170 var tmp-f/eax: int <- multiply-fixed x-f, x-f +171 var result-f/ecx: int <- copy tmp-f +172 tmp-f <- multiply-fixed y-f, y-f +173 result-f <- add tmp-f +174 compare result-f, 0x400/4 +175 { +176 break-if-> +177 return 0/false +178 } +179 return 1/true +180 } +181 +182 fn mandelbrot-x x-f: int, y-f: int, real-f: int -> _/edx: int { +183 # x*x - y*y + real +184 var tmp-f/eax: int <- multiply-fixed x-f, x-f +185 var result-f/ecx: int <- copy tmp-f +186 tmp-f <- multiply-fixed y-f, y-f +187 result-f <- subtract tmp-f +188 result-f <- add real-f +189 return result-f +190 } +191 +192 fn mandelbrot-y x-f: int, y-f: int, imaginary-f: int -> _/ebx: int { +193 # 2*x*y + imaginary +194 var result-f/eax: int <- copy x-f +195 result-f <- shift-left 1/log2 +196 result-f <- multiply-fixed result-f, y-f +197 result-f <- add imaginary-f +198 return result-f +199 } +200 +201 # Scale (x, y) pixel coordinates to a complex plane where the viewport width +202 # ranges from -2 to +2. Viewport height just follows the viewport's aspect +203 # ratio. +204 +205 fn viewport-to-real-f x: int, width-f: int -> _/edx: int { +206 # (x - width/2)*4/width +207 var result-f/eax: int <- int-to-fixed x +208 var half-width-f/ecx: int <- copy width-f +209 half-width-f <- shift-right-signed 1/log2 +210 result-f <- subtract half-width-f +211 result-f <- shift-left 2/log4 +212 result-f <- divide-fixed result-f, width-f +213 return result-f +214 } +215 +216 fn viewport-to-imaginary-f y: int, width-f: int, height-f: int -> _/ebx: int { +217 # (y - height/2)*4/width +218 var result-f/eax: int <- int-to-fixed y +219 shift-right-signed height-f, 1/log2 +220 result-f <- subtract height-f +221 result-f <- shift-left 2/log4 +222 result-f <- divide-fixed result-f, width-f +223 return result-f +224 }