https://github.com/akkartik/mu/blob/main/apps/mandelbrot.mu
  1 # Mandelbrot set
  2 #
  3 # Install:
  4 #   $ git clone https://github.com/akkartik/mu
  5 #   $ cd mu
  6 # Build on Linux:
  7 #   $ ./translate apps/mandelbrot.mu
  8 # Build on other platforms (slow):
  9 #   $ ./translate_emulated apps/mandelbrot.mu
 10 # Run:
 11 #   $ qemu-system-i386 code.img
 12 
 13 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
 14   # Initially the viewport is centered at 0, 0 in the scene.
 15   var zero: float
 16   var scene-cx/xmm1: float <- copy zero
 17   var scene-cy/xmm2: float <- copy zero
 18   # Initially the viewport shows a section of the scene 4 units wide.
 19   # scene-width-scale = 0.5
 20   var scene-width-scale: float
 21   var dest/eax: (addr float) <- address scene-width-scale
 22   fill-in-rational dest, 1, 2
 23   # scene-width = 4
 24   var four: float
 25   var dest/eax: (addr float) <- address four
 26   fill-in-rational dest, 4, 1
 27   var scene-width/xmm3: float <- copy four
 28   {
 29     mandelbrot screen scene-cx, scene-cy, scene-width
 30     # move the center some % of the current screen-width
 31     var adj/xmm0: float <- rational 2, 0x1c/28
 32     adj <- multiply scene-width
 33     scene-cx <- subtract adj
 34     scene-cy <- add adj
 35     # slowly shrink the scene width to zoom in
 36     scene-width <- multiply scene-width-scale
 37     loop
 38   }
 39 }
 40 
 41 fn mandelbrot screen: (addr screen), scene-cx: float, scene-cy: float, scene-width: float {
 42   var a/eax: int <- copy 0
 43   var b/ecx: int <- copy 0
 44   a, b <- screen-size screen
 45   var width/esi: int <- copy a
 46   width <- shift-left 3/log2-font-width
 47   var height/edi: int <- copy b
 48   height <- shift-left 4/log2-font-height
 49   var y/ecx: int <- copy 0
 50   {
 51     compare y, height
 52     break-if->=
 53     var imaginary/xmm1: float <- viewport-to-imaginary y, width, height, scene-cy, scene-width
 54     var x/ebx: int <- copy 0
 55     {
 56       compare x, width
 57       break-if->=
 58       var real/xmm0: float <- viewport-to-real x, width, scene-cx, scene-width
 59       var iterations/eax: int <- mandelbrot-iterations-for-point real, imaginary, 0x400/max
 60       iterations <- shift-right 3
 61       var color/edx: int <- copy 0
 62       iterations, color <- integer-divide iterations, 0x18/24/size-of-cycle-0
 63       color <- add 0x20/cycle-0
 64       pixel screen, x, y, color
 65       x <- increment
 66       loop
 67     }
 68     y <- increment
 69     loop
 70   }
 71 }
 72 
 73 fn mandelbrot-iterations-for-point real: float, imaginary: float, max: int -> _/eax: int {
 74   var zero: float
 75   var x/xmm0: float <- copy zero
 76   var y/xmm1: float <- copy zero
 77   var iterations/ecx: int <- copy 0
 78   {
 79     var done?/eax: boolean <- mandelbrot-done? x, y
 80     compare done?, 0/false
 81     break-if-!=
 82     compare iterations, max
 83     break-if->=
 84     var newx/xmm2: float <- mandelbrot-x x, y, real
 85     var newy/xmm3: float <- mandelbrot-y x, y, imaginary
 86     x <- copy newx
 87     y <- copy newy
 88     iterations <- increment
 89     loop
 90   }
 91   return iterations
 92 }
 93 
 94 fn mandelbrot-done? x: float, y: float -> _/eax: boolean {
 95   # x*x + y*y > 4
 96   var x2/xmm0: float <- copy x
 97   x2 <- multiply x
 98   var y2/xmm1: float <- copy y
 99   y2 <- multiply y
100   var sum/xmm0: float <- copy x2
101   sum <- add y2
102   var four/eax: int <- copy 4
103   var four-f/xmm1: float <- convert four
104   compare sum, four-f
105   {
106     break-if-float>
107     return 0/false
108   }
109   return 1/true
110 }
111 
112 fn mandelbrot-x x: float, y: float, real: float -> _/xmm2: float {
113   # x*x - y*y + real
114   var x2/xmm0: float <- copy x
115   x2 <- multiply x
116   var y2/xmm1: float <- copy y
117   y2 <- multiply y
118   var result/xmm0: float <- copy x2
119   result <- subtract y2
120   result <- add real
121   return result
122 }
123 
124 fn mandelbrot-y x: float, y: float, imaginary: float -> _/xmm3: float {
125   # 2*x*y + imaginary
126   var two/eax: int <- copy 2
127   var result/xmm0: float <- convert two
128   result <- multiply x
129   result <- multiply y
130   result <- add imaginary
131   return result
132 }
133 
134 # Scale (x, y) pixel coordinates to a complex plane where the viewport width
135 # ranges from (scene-cx - scene-width/2) to (scene-cx + scene-width/2).
136 # Viewport height just follows the viewport's aspect ratio.
137 
138 fn viewport-to-real x: int, width: int, scene-cx: float, scene-width: float -> _/xmm0: float {
139   # 0 in the viewport       goes to scene-cx - scene-width/2 
140   # width in the viewport   goes to scene-cx + scene-width/2
141   # Therefore:
142   # x in the viewport       goes to (scene-cx - scene-width/2) + x*scene-width/width
143   # At most two numbers being multiplied before a divide, so no risk of overflow.
144   var result/xmm0: float <- convert x
145   result <- multiply scene-width
146   var width-f/xmm1: float <- convert width
147   result <- divide width-f
148   result <- add scene-cx
149   var two/eax: int <- copy 2
150   var two-f/xmm2: float <- convert two
151   var half-scene-width/xmm1: float <- copy scene-width
152   half-scene-width <- divide two-f
153   result <- subtract half-scene-width
154   return result
155 }
156 
157 fn viewport-to-imaginary y: int, width: int, height: int, scene-cy: float, scene-width: float -> _/xmm1: float {
158   # 0 in the viewport       goes to scene-cy - scene-width/2*height/width
159   # height in the viewport  goes to scene-cy + scene-width/2*height/width
160   # Therefore:
161   # y in the viewport       goes to (scene-cy - scene-width/2*height/width) + y*scene-width/width
162   # At most two numbers being multiplied before a divide, so no risk of overflow.
163   var result/xmm0: float <- convert y
164   result <- multiply scene-width
165   var width-f/xmm1: float <- convert width
166   result <- divide width-f
167   result <- add scene-cy
168   var two/eax: int <- copy 2
169   var two-f/xmm2: float <- convert two
170   var second-term/xmm1: float <- copy scene-width
171   second-term <- divide two-f
172   var height-f/xmm2: float <- convert height
173   second-term <- multiply height-f
174   var width-f/xmm2: float <- convert width
175   second-term <- divide width-f
176   result <- subtract second-term
177   return result
178 }