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