diff --git a/mandelbrot-fixed.mu b/mandelbrot-fixed.mu new file mode 100644 index 00000000..d380c29c --- /dev/null +++ b/mandelbrot-fixed.mu @@ -0,0 +1,224 @@ +# Mandelbrot set using fixed-point numbers. +# +# To build: +# $ ./translate mandelbrot-fixed.mu +# To run: +# $ qemu-system-i386 code.img + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + mandelbrot screen +} + +# Since they still look like int types, we'll append a '-f' suffix to variable +# names to designate fixed-point numbers. + +fn int-to-fixed in: int -> _/eax: int { + var result-f/eax: int <- copy in + result-f <- shift-left 8/fixed-precision + { + break-if-not-overflow + abort "int-to-fixed: overflow" + } + return result-f +} + +fn fixed-to-int in-f: int -> _/eax: int { + var result/eax: int <- copy in-f + result <- shift-right-signed 8/fixed-precision + return result +} + +fn test-fixed-conversion { + # 0 + var f/eax: int <- int-to-fixed 0 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 0, "F - test-fixed-conversion - 0" + # 1 + var f/eax: int <- int-to-fixed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 1, "F - test-fixed-conversion - 1" + # -1 + var f/eax: int <- int-to-fixed -1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -1, "F - test-fixed-conversion - -1" + # 0.5 = 1/2 + var f/eax: int <- int-to-fixed 1 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 0, "F - test-fixed-conversion - 0.5" + # -0.5 = -1/2 + var f/eax: int <- int-to-fixed -1 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -1, "F - test-fixed-conversion - -0.5" + # 1.5 = 3/2 + var f/eax: int <- int-to-fixed 3 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 1, "F - test-fixed-conversion - 1.5" + # -1.5 = -3/2 + var f/eax: int <- int-to-fixed -3 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -2, "F - test-fixed-conversion - -1.5" + # 1.25 = 5/4 + var f/eax: int <- int-to-fixed 5 + f <- shift-right-signed 2 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 1, "F - test-fixed-conversion - 1.25" + # -1.25 = -5/4 + var f/eax: int <- int-to-fixed -5 + f <- shift-right-signed 2 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -2, "F - test-fixed-conversion - -1.25" +} + +# special routines for multiplying and dividing fixed-point numbers + +fn multiply-fixed a: int, b: int -> _/eax: int { + var result/eax: int <- copy a + result <- multiply b + { + break-if-not-overflow + abort "multiply-fixed: overflow" + } + result <- shift-right-signed 8/fixed-precision + return result +} + +fn divide-fixed a-f: int, b-f: int -> _/eax: int { + var result-f/eax: int <- copy a-f + result-f <- shift-left 8/fixed-precision + { + break-if-not-overflow + abort "divide-fixed: overflow" + } + var dummy-remainder/edx: int <- copy 0 + result-f, dummy-remainder <- integer-divide result-f, b-f + return result-f +} + +# multiplying or dividing by an integer can use existing instructions. + +# adding and subtracting two fixed-point numbers can use existing instructions. + +fn mandelbrot screen: (addr screen) { + var a/eax: int <- copy 0 + var b/ecx: int <- copy 0 + a, b <- screen-size screen + var width-f/esi: int <- copy a + width-f <- shift-left 0xb/log2-font-width-and-fixed-precision # 3 + 8 = 11 + var height-f/edi: int <- copy b + height-f <- shift-left 0xc/log2-font-height-and-fixed-precision # 4 + 8 = 12 + # it might make sense to keep x and y as regular ints + # treating them as fixed-point for demonstration purposes + var y-f/ecx: int <- copy 0 + { + compare y-f, height-f + break-if->= + var imaginary-f/ebx: int <- mandelbrot-min-y y-f, width-f, height-f + var x-f/eax: int <- copy 0 + { + compare x-f, width-f + break-if->= + var real-f/edx: int <- mandelbrot-min-x x-f, width-f + var iterations/esi: int <- mandelbrot-pixel real-f, imaginary-f, 0x400/max + { + var x: int + var y: int + var tmp/eax: int <- fixed-to-int x-f + copy-to x, tmp + tmp <- fixed-to-int y-f + copy-to y, tmp + compare iterations, 0x400/max + { + break-if->= + pixel screen, x, y, 0xf/white + } + compare iterations, 0x400/max + { + break-if-< + pixel screen, x, y, 0/black + } + } + x-f <- add 0x100/1 + loop + } + y-f <- add 0x100/1 + loop + } +} + +fn mandelbrot-pixel real-f: int, imaginary-f: int, max: int -> _/esi: int { + var x-f/esi: int <- copy 0 + var y-f/edi: int <- copy 0 + var iterations/ecx: int <- copy 0 + { + var done?/eax: boolean <- mandelbrot-done? x-f, y-f + compare done?, 0/false + break-if-!= + compare iterations, max + break-if->= + var x2-f/edx: int <- mandelbrot-x x-f, y-f, real-f + var y2-f/ebx: int <- mandelbrot-y x-f, y-f, imaginary-f + x-f <- copy x2-f + y-f <- copy y2-f + iterations <- increment + loop + } + return iterations +} + +fn mandelbrot-done? x-f: int, y-f: int -> _/eax: boolean { + # x*x + y*y > 4 + var tmp-f/eax: int <- multiply-fixed x-f, x-f + var result-f/ecx: int <- copy tmp-f + tmp-f <- multiply-fixed y-f, y-f + result-f <- add tmp-f + compare result-f, 0x400/4 + { + break-if-> + return 0/false + } + return 1/true +} + +fn mandelbrot-x x-f: int, y-f: int, real-f: int -> _/edx: int { + # x*x - y*y + real + var tmp-f/eax: int <- multiply-fixed x-f, x-f + var result-f/ecx: int <- copy tmp-f + tmp-f <- multiply-fixed y-f, y-f + result-f <- subtract tmp-f + result-f <- add real-f + return result-f +} + +fn mandelbrot-y x-f: int, y-f: int, imaginary-f: int -> _/ebx: int { + # 2*x*y + imaginary + var result-f/eax: int <- copy x-f + result-f <- shift-left 1/log2 + result-f <- multiply-fixed result-f, y-f + result-f <- add imaginary-f + return result-f +} + +fn mandelbrot-min-x x-f: int, width-f: int -> _/edx: int { + # (x - width/2)*4/width + var result-f/eax: int <- copy x-f + var half-width-f/ecx: int <- copy width-f + half-width-f <- shift-right-signed 1/log2 + result-f <- subtract half-width-f + result-f <- shift-left 2/log4 + result-f <- divide-fixed result-f, width-f + return result-f +} + +fn mandelbrot-min-y y-f: int, width-f: int, height-f: int -> _/ebx: int { + # (y - height/2)*4/width + var result-f/eax: int <- copy y-f + shift-right-signed height-f, 1/log2 + result-f <- subtract height-f + result-f <- shift-left 2/log4 + result-f <- divide-fixed result-f, width-f + return result-f +} diff --git a/mandelbrot.mu b/mandelbrot.mu index b1ba818e..46f91bd4 100644 --- a/mandelbrot.mu +++ b/mandelbrot.mu @@ -21,18 +21,18 @@ fn mandelbrot screen: (addr screen) { { compare y, height break-if->= -#? var new-x/eax: int <- render-float-decimal 0/screen, seed-y, 3, 0/x, 0/y, 7/fg, 0/bg - var seed-y/xmm1: float <- mandelbrot-min-y y, width, height +#? var new-x/eax: int <- render-float-decimal 0/screen, imaginary, 3, 0/x, 0/y, 7/fg, 0/bg + var imaginary/xmm1: float <- mandelbrot-min-y y, width, height var x/edx: int <- copy 0 { compare x, width break-if->= - var seed-x/xmm0: float <- mandelbrot-min-x x, width + var real/xmm0: float <- mandelbrot-min-x x, width var new-x/eax: int <- copy 0 - new-x <- render-float-decimal 0/screen, seed-x, 3, new-x, 0/y, 7/fg, 0/bg + new-x <- render-float-decimal 0/screen, real, 3, new-x, 0/y, 7/fg, 0/bg new-x <- increment - new-x <- render-float-decimal 0/screen, seed-y, 3, new-x, 0/y, 7/fg, 0/bg - var iterations/eax: int <- mandelbrot-pixel seed-x, seed-y, 0x400/max + new-x <- render-float-decimal 0/screen, imaginary, 3, new-x, 0/y, 7/fg, 0/bg + var iterations/eax: int <- mandelbrot-pixel real, imaginary, 0x400/max set-cursor-position 0/screen 0/x 1/y draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, iterations, 7/fg, 0/bg compare iterations, 0x400/max @@ -53,7 +53,7 @@ fn mandelbrot screen: (addr screen) { } } -fn mandelbrot-pixel seed-x: float, seed-y: float, max: int -> _/eax: int { +fn mandelbrot-pixel real: float, imaginary: float, max: int -> _/eax: int { var zero: float var x/xmm0: float <- copy zero var y/xmm1: float <- copy zero @@ -64,8 +64,8 @@ fn mandelbrot-pixel seed-x: float, seed-y: float, max: int -> _/eax: int { break-if-!= compare iterations, max break-if->= - var newx/xmm2: float <- mandelbrot-x x, y, seed-x - var newy/xmm3: float <- mandelbrot-y x, y, seed-y + var newx/xmm2: float <- mandelbrot-x x, y, real + var newy/xmm3: float <- mandelbrot-y x, y, imaginary x <- copy newx y <- copy newy iterations <- increment @@ -93,25 +93,25 @@ fn mandelbrot-done? x: float, y: float -> _/eax: boolean { return 1/true } -fn mandelbrot-x x: float, y: float, seed-x: float -> _/xmm2: float { - # x*x - y*y + seed-x +fn mandelbrot-x x: float, y: float, real: float -> _/xmm2: float { + # x*x - y*y + real var x2/xmm0: float <- copy x x2 <- multiply x var y2/xmm1: float <- copy y y2 <- multiply y var result/xmm0: float <- copy x2 result <- subtract y2 - result <- add seed-x + result <- add real return result } -fn mandelbrot-y x: float, y: float, seed-y: float -> _/xmm3: float { - # 2*x*y + seed-y +fn mandelbrot-y x: float, y: float, imaginary: float -> _/xmm3: float { + # 2*x*y + imaginary var two/eax: int <- copy 2 var result/xmm0: float <- convert two result <- multiply x result <- multiply y - result <- add seed-y + result <- add imaginary return result }