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 -2 to +2. Viewport height just follows the viewport's aspect
    136 # 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   #  scene-cy - scene-width/width * (height/2 + y)
    163   # At most two numbers being multiplied before a divide, so no risk of overflow.
    164   var result/xmm0: float <- convert y
    165   result <- multiply scene-width
    166   var width-f/xmm1: float <- convert width
    167   result <- divide width-f
    168   result <- add scene-cy
    169   var two/eax: int <- copy 2
    170   var two-f/xmm2: float <- convert two
    171   var second-term/xmm1: float <- copy scene-width
    172   second-term <- divide two-f
    173   var height-f/xmm2: float <- convert height
    174   second-term <- multiply height-f
    175   var width-f/xmm2: float <- convert width
    176   second-term <- divide width-f
    177   result <- subtract second-term
    178   return result
    179 }