about summary refs log tree commit diff stats
path: root/apps/mandelbrot.mu
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-07-16 08:09:42 -0700
committerKartik K. Agaram <vc@akkartik.com>2021-07-16 08:28:56 -0700
commit44d26b77c45668c9b0c99894a4294cec004361fe (patch)
tree68a5dcd4971873efd4ce184e9bf9a531c2161813 /apps/mandelbrot.mu
parentac45f097153afd3a89f43886e4124c5b2c26b98a (diff)
downloadmu-44d26b77c45668c9b0c99894a4294cec004361fe.tar.gz
.
Diffstat (limited to 'apps/mandelbrot.mu')
-rw-r--r--apps/mandelbrot.mu179
1 files changed, 179 insertions, 0 deletions
diff --git a/apps/mandelbrot.mu b/apps/mandelbrot.mu
new file mode 100644
index 00000000..218fd4fa
--- /dev/null
+++ b/apps/mandelbrot.mu
@@ -0,0 +1,179 @@
+# Mandelbrot set
+#
+# Install:
+#   $ git clone https://github.com/akkartik/mu
+#   $ cd mu
+# Build on Linux:
+#   $ ./translate apps/mandelbrot.mu
+# Build on other platforms (slow):
+#   $ ./translate_emulated apps/mandelbrot.mu
+# Run:
+#   $ qemu-system-i386 code.img
+
+fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
+  # Initially the viewport is centered at 0, 0 in the scene.
+  var zero: float
+  var scene-cx/xmm1: float <- copy zero
+  var scene-cy/xmm2: float <- copy zero
+  # Initially the viewport shows a section of the scene 4 units wide.
+  # scene-width-scale = 0.5
+  var scene-width-scale: float
+  var dest/eax: (addr float) <- address scene-width-scale
+  fill-in-rational dest, 1, 2
+  # scene-width = 4
+  var four: float
+  var dest/eax: (addr float) <- address four
+  fill-in-rational dest, 4, 1
+  var scene-width/xmm3: float <- copy four
+  {
+    mandelbrot screen scene-cx, scene-cy, scene-width
+    # move the center some % of the current screen-width
+    var adj/xmm0: float <- rational 2, 0x1c/28
+    adj <- multiply scene-width
+    scene-cx <- subtract adj
+    scene-cy <- add adj
+    # slowly shrink the scene width to zoom in
+    scene-width <- multiply scene-width-scale
+    loop
+  }
+}
+
+fn mandelbrot screen: (addr screen), scene-cx: float, scene-cy: float, scene-width: float {
+  var a/eax: int <- copy 0
+  var b/ecx: int <- copy 0
+  a, b <- screen-size screen
+  var width/esi: int <- copy a
+  width <- shift-left 3/log2-font-width
+  var height/edi: int <- copy b
+  height <- shift-left 4/log2-font-height
+  var y/ecx: int <- copy 0
+  {
+    compare y, height
+    break-if->=
+    var imaginary/xmm1: float <- viewport-to-imaginary y, width, height, scene-cy, scene-width
+    var x/ebx: int <- copy 0
+    {
+      compare x, width
+      break-if->=
+      var real/xmm0: float <- viewport-to-real x, width, scene-cx, scene-width
+      var iterations/eax: int <- mandelbrot-iterations-for-point real, imaginary, 0x400/max
+      iterations <- shift-right 3
+      var color/edx: int <- copy 0
+      iterations, color <- integer-divide iterations, 0x18/24/size-of-cycle-0
+      color <- add 0x20/cycle-0
+      pixel screen, x, y, color
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+}
+
+fn mandelbrot-iterations-for-point real: float, imaginary: float, max: int -> _/eax: int {
+  var zero: float
+  var x/xmm0: float <- copy zero
+  var y/xmm1: float <- copy zero
+  var iterations/ecx: int <- copy 0
+  {
+    var done?/eax: boolean <- mandelbrot-done? x, y
+    compare done?, 0/false
+    break-if-!=
+    compare iterations, max
+    break-if->=
+    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
+    loop
+  }
+  return iterations
+}
+
+fn mandelbrot-done? x: float, y: float -> _/eax: boolean {
+  # x*x + y*y > 4
+  var x2/xmm0: float <- copy x
+  x2 <- multiply x
+  var y2/xmm1: float <- copy y
+  y2 <- multiply y
+  var sum/xmm0: float <- copy x2
+  sum <- add y2
+  var four/eax: int <- copy 4
+  var four-f/xmm1: float <- convert four
+  compare sum, four-f
+  {
+    break-if-float>
+    return 0/false
+  }
+  return 1/true
+}
+
+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 real
+  return result
+}
+
+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 imaginary
+  return result
+}
+
+# Scale (x, y) pixel coordinates to a complex plane where the viewport width
+# ranges from -2 to +2. Viewport height just follows the viewport's aspect
+# ratio.
+
+fn viewport-to-real x: int, width: int, scene-cx: float, scene-width: float -> _/xmm0: float {
+  # 0 in the viewport       goes to scene-cx - scene-width/2 
+  # width in the viewport   goes to scene-cx + scene-width/2
+  # Therefore:
+  # x in the viewport       goes to (scene-cx - scene-width/2) + x*scene-width/width
+  # At most two numbers being multiplied before a divide, so no risk of overflow.
+  var result/xmm0: float <- convert x
+  result <- multiply scene-width
+  var width-f/xmm1: float <- convert width
+  result <- divide width-f
+  result <- add scene-cx
+  var two/eax: int <- copy 2
+  var two-f/xmm2: float <- convert two
+  var half-scene-width/xmm1: float <- copy scene-width
+  half-scene-width <- divide two-f
+  result <- subtract half-scene-width
+  return result
+}
+
+fn viewport-to-imaginary y: int, width: int, height: int, scene-cy: float, scene-width: float -> _/xmm1: float {
+  # 0 in the viewport       goes to scene-cy - scene-width/2*height/width
+  # height in the viewport  goes to scene-cy + scene-width/2*height/width
+  # Therefore:
+  # y in the viewport       goes to (scene-cy - scene-width/2*height/width) + y*scene-width/width
+  #  scene-cy - scene-width/width * (height/2 + y)
+  # At most two numbers being multiplied before a divide, so no risk of overflow.
+  var result/xmm0: float <- convert y
+  result <- multiply scene-width
+  var width-f/xmm1: float <- convert width
+  result <- divide width-f
+  result <- add scene-cy
+  var two/eax: int <- copy 2
+  var two-f/xmm2: float <- convert two
+  var second-term/xmm1: float <- copy scene-width
+  second-term <- divide two-f
+  var height-f/xmm2: float <- convert height
+  second-term <- multiply height-f
+  var width-f/xmm2: float <- convert width
+  second-term <- divide width-f
+  result <- subtract second-term
+  return result
+}