https://github.com/akkartik/mu/blob/main/hest-life.mu
   1 # Conway's Game of Life in a Hestified way
   2 #   https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
   3 #   https://ivanish.ca/hest-podcast
   4 #
   5 # To build:
   6 #   $ ./translate hest-life.mu
   7 # I run it on my 2.5GHz Linux laptop like this:
   8 #   $ qemu-system-i386 -enable-kvm code.img
   9 #
  10 # If things seem too fast or too slow on your computer, adjust the loop bounds
  11 # in the function `linger` at the bottom. Its value will depend on how you
  12 # accelerate Qemu. Mu will eventually get a clock to obviate the need for this
  13 # tuning.
  14 #
  15 # Keyboard shortcuts:
  16 #   space: pause/resume
  17 #   0: restart time
  18 #   l: start looping from 0 to curren time
  19 #   L: stop looping
  20 #   +: zoom in
  21 #   -: zoom out
  22 
  23 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
  24   var env-storage: environment
  25   var env/esi: (addr environment) <- address env-storage
  26   initialize-environment env
  27   var second-buffer: screen
  28   var second-screen/edi: (addr screen) <- address second-buffer
  29   initialize-screen second-screen, 0x80, 0x30, 1/include-pixels
  30   render second-screen, env
  31   copy-pixels second-screen, screen
  32   {
  33     edit keyboard, env
  34     var play?/eax: (addr boolean) <- get env, play?
  35     compare *play?, 0/false
  36     {
  37       break-if-=
  38       step env
  39       render second-screen, env
  40       copy-pixels second-screen, screen
  41     }
  42     linger env
  43     loop
  44   }
  45 }
  46 
  47 type environment {
  48   data: (handle array handle array cell)
  49   zoom: int  # 0 = 1024 px per cell; 5 = 4px per cell; each step adjusts by a factor of 4
  50   tick: int
  51   play?: boolean
  52   loop: int  # if non-zero, return tick to 0 after this point
  53 }
  54 
  55 type cell {
  56   curr: boolean
  57   next: boolean
  58 }
  59 
  60 fn render screen: (addr screen), _self: (addr environment) {
  61   var self/esi: (addr environment) <- copy _self
  62   var zoom/eax: (addr int) <- get self, zoom
  63   compare *zoom, 0
  64   {
  65     break-if-!=
  66     clear-screen screen
  67     render0 screen, self
  68   }
  69   compare *zoom, 1
  70   {
  71     break-if-!=
  72     clear-screen screen
  73     render1 screen, self
  74   }
  75   compare *zoom, 4
  76   {
  77     break-if-!=
  78     render4 screen, self
  79   }
  80   # clock
  81   var tick-a/eax: (addr int) <- get self, tick
  82   set-cursor-position screen, 0x78/x, 0/y
  83   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, *tick-a, 7/fg 0/bg
  84 }
  85 
  86 # Lots of hardcoded constants for now.
  87 # TODO: split this up into a primitive to render a single cell and its
  88 # incoming edges (but not the neighboring nodes they emanate from)
  89 fn render0 screen: (addr screen), _self: (addr environment) {
  90   var self/esi: (addr environment) <- copy _self
  91   # cell border
  92   draw-vertical-line   screen,  0xc0/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey
  93   draw-vertical-line   screen, 0x340/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey
  94   draw-horizontal-line screen,  0x40/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey
  95   draw-horizontal-line screen, 0x2c0/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey
  96   # neighboring inputs, corners
  97   var color/eax: int <- state-color self, 0x7f/cur-topleftx, 0x5f/cur-toplefty
  98   draw-rect screen,  0x90/xmin   0x10/ymin,    0xb0/xmax   0x30/ymax,  color
  99   color <- state-color self, 0x81/cur-toprightx, 0x5f/cur-toprighty
 100   draw-rect screen, 0x350/xmin   0x10/ymin,   0x370/xmax   0x30/ymax,  color
 101   color <- state-color self, 0x7f/cur-botleftx, 0x61/cur-botlefty
 102   draw-rect screen,  0x90/xmin  0x2d0/ymin,    0xb0/xmax  0x2f0/ymax,  color
 103   color <- state-color self, 0x81/cur-botrightx, 0x61/cur-botrighty
 104   draw-rect screen, 0x350/xmin  0x2d0/ymin,   0x370/xmax  0x2f0/ymax,  color
 105   # neighboring inputs, edges
 106   color <- state-color self, 0x80/cur-topx, 0x5f/cur-topy
 107   draw-rect screen, 0x1f0/xmin   0x10/ymin,   0x210/xmax   0x30/ymax,  color
 108   color <- state-color self, 0x7f/cur-leftx, 0x60/cur-lefty
 109   draw-rect screen,  0x90/xmin  0x170/ymin,    0xb0/xmax  0x190/ymax,  color
 110   color <- state-color self, 0x80/cur-botx, 0x61/cur-boty
 111   draw-rect screen, 0x1f0/xmin  0x2d0/ymin,   0x210/xmax  0x2f0/ymax,  color
 112   color <- state-color self, 0x81/cur-rightx, 0x60/cur-righty
 113   draw-rect screen, 0x350/xmin  0x170/ymin,   0x370/xmax  0x190/ymax,  color
 114   # sum node
 115   draw-rect screen, 0x170/xsmin 0x140/ysmin,  0x190/xsmax 0x160/ysmax, 0x40/color
 116   set-cursor-position screen, 0x2d/scol, 0x13/srow
 117   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "+", 0xf/color, 0/bg
 118   # conveyors from neighboring inputs to sum node
 119   draw-monotonic-bezier screen,  0xa0/x0  0x20/y0,  0x100/x1 0x150/ys,  0x180/xs 0x150/ys,  4/color
 120   draw-monotonic-bezier screen,  0xa0/x0 0x180/y0,   0xc0/x1 0x150/ys,  0x180/xs 0x150/ys,  4/color
 121   draw-monotonic-bezier screen,  0xa0/x0 0x2e0/y0,  0x100/x1 0x150/ys,  0x180/xs 0x150/ys,  4/color
 122   draw-monotonic-bezier screen, 0x200/x0  0x20/y0,  0x180/x1  0x90/y1,  0x180/xs 0x150/ys,  4/color
 123   draw-monotonic-bezier screen, 0x200/x0 0x2e0/y0,  0x180/x1 0x200/y1,  0x180/xs 0x150/ys,  4/color
 124   draw-monotonic-bezier screen, 0x360/x0  0x20/y0,  0x180/x1  0xc0/y1,  0x180/xs 0x150/ys,  4/color
 125   draw-monotonic-bezier screen, 0x360/x0 0x180/y0,  0x35c/x1 0x150/ys,  0x180/xs 0x150/ys,  4/color
 126   draw-monotonic-bezier screen, 0x360/x0 0x2e0/y0,  0x180/x1 0x200/y1,  0x180/xs 0x150/ys,  4/color
 127   # filter node
 128   draw-rect screen, 0x200/xfmin 0x1c0/yfmin, 0x220/xfmax 0x1e0/yfmax, 0x31/color
 129   set-cursor-position screen, 0x40/fcol, 0x1b/frow
 130   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "?", 0xf/color, 0/bg
 131   # conveyor from sum node to filter node
 132   draw-line screen 0x180/xs, 0x150/ys, 0x210/xf, 0x1d0/yf, 0xa2/color
 133   # cell outputs at corners
 134   var color/eax: int <- state-color self, 0x80/curx, 0x60/cury
 135   draw-rect screen,  0xd0/xmin  0x50/ymin,  0xf0/xmax  0x70/ymax, color
 136   draw-rect screen, 0x310/xmin  0x50/ymin, 0x330/xmax  0x70/ymax, color
 137   draw-rect screen,  0xd0/xmin 0x290/ymin,  0xf0/xmax 0x2b0/ymax, color
 138   draw-rect screen, 0x310/xmin 0x290/ymin, 0x330/xmax 0x2b0/ymax, color
 139   # cell outputs at edges
 140   draw-rect screen, 0x1f0/xmin  0x50/ymin, 0x210/xmax  0x70/ymax, color
 141   draw-rect screen,  0xd0/xmin 0x170/ymin,  0xf0/xmax 0x190/ymax, color
 142   draw-rect screen, 0x1f0/xmin 0x290/ymin, 0x210/xmax 0x2b0/ymax, color
 143   draw-rect screen, 0x310/xmin 0x170/ymin, 0x330/xmax 0x190/ymax, color
 144   # conveyors from filter to outputs
 145   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x1c0/x1  0x60/y1,  0xe0/x2   0x60/y2,  0x2a/color
 146   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,   0xe0/x1 0x1c0/y1,  0xe0/x2  0x180/y2,  0x2a/color
 147   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x1c0/x1 0x2a0/y1,  0xe0/x2  0x2a0/y2,  0x2a/color
 148   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x210/x1  0x60/y1, 0x200/x2   0x60/y2,  0x2a/color
 149   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x210/x1 0x230/y1, 0x200/x2  0x2a0/y2,  0x2a/color
 150   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x320/x1 0x120/y1, 0x320/x2   0x60/y2,  0x2a/color
 151   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x320/x1 0x1c0/y1  0x320/x2  0x180/y2,  0x2a/color
 152   draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf,  0x320/x1 0x230/y1, 0x320/x2  0x2a0/y2,  0x2a/color
 153   # time-variant portion: 16 repeating steps
 154   var tick-a/eax: (addr int) <- get self, tick
 155   var progress/eax: int <- copy *tick-a
 156   progress <- and 0xf
 157   # 7 time steps for getting inputs to sum
 158   {
 159     compare progress, 7
 160     break-if->=
 161     var u/xmm7: float <- convert progress
 162     var six/eax: int <- copy 6
 163     var six-f/xmm0: float <- convert six
 164     u <- divide six-f
 165     # points on conveyors from neighboring cells
 166     draw-bezier-point screen, u,  0xa0/x0  0x20/y0, 0x100/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius
 167     draw-bezier-point screen, u,  0xa0/x0 0x180/y0,  0xc0/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius
 168     draw-bezier-point screen, u,  0xa0/x0 0x2e0/y0, 0x100/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius
 169     draw-bezier-point screen, u, 0x200/x0  0x20/y0, 0x180/x1  0x90/y1, 0x180/xs 0x150/ys, 7/color, 4/radius
 170     draw-bezier-point screen, u, 0x200/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/xs 0x150/ys, 7/color, 4/radius
 171     draw-bezier-point screen, u, 0x360/x0  0x20/y0, 0x180/x1  0xc0/y1, 0x180/xs 0x150/ys, 7/color, 4/radius
 172     draw-bezier-point screen, u, 0x360/x0 0x180/y0, 0x35c/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius
 173     draw-bezier-point screen, u, 0x360/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/xs 0x150/ys, 7/color, 4/radius
 174     return
 175   }
 176   # two time steps for getting count to filter
 177   progress <- subtract 7
 178   {
 179     compare progress, 2
 180     break-if->=
 181     progress <- increment  # (0, 1) => (1, 2)
 182     var u/xmm7: float <- convert progress
 183     var three/eax: int <- copy 3
 184     var three-f/xmm0: float <- convert three
 185     u <- divide three-f
 186     draw-linear-point screen, u, 0x180/xs, 0x150/ys, 0x210/xf, 0x1d0/yf, 7/color, 4/radius
 187     set-cursor-position screen, 0x3a/scol, 0x18/srow
 188     var n/eax: int <- num-live-neighbors self, 0x80/curx, 0x60/cury
 189     draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg
 190     return
 191   }
 192   # final 7 time steps for updating output
 193   progress <- subtract 2
 194   # points on conveyors to outputs
 195   var u/xmm7: float <- convert progress
 196   var six/eax: int <- copy 6
 197   var six-f/xmm0: float <- convert six
 198   u <- divide six-f
 199   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x1c0/x1  0x60/y1,  0xe0/x2   0x60/y2, 7/color, 4/radius
 200   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,   0xe0/x1 0x1c0/y1,  0xe0/x2  0x180/y2, 7/color, 4/radius
 201   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x1c0/x1 0x2a0/y1,  0xe0/x2  0x2a0/y2, 7/color, 4/radius
 202   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x210/xf  0x60/y1, 0x200/x2   0x60/y2, 7/color, 4/radius
 203   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x210/xf 0x230/y1, 0x200/x2  0x2a0/y2, 7/color, 4/radius
 204   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x320/x1 0x120/y1, 0x320/x2   0x60/y2, 7/color, 4/radius
 205   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x320/x1 0x1c0/y1, 0x320/x2  0x180/y2, 7/color, 4/radius
 206   draw-bezier-point screen, u, 0x210/xf 0x1d0/yf,  0x320/x1 0x230/y1, 0x320/x2  0x2a0/y2, 7/color, 4/radius
 207 }
 208 
 209 fn render1 screen: (addr screen), _self: (addr environment) {
 210   var self/esi: (addr environment) <- copy _self
 211   # cell borders
 212   draw-vertical-line   screen,  0xe0/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey
 213   draw-vertical-line   screen, 0x200/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey
 214   draw-vertical-line   screen, 0x320/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey
 215   draw-horizontal-line screen,  0x60/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey
 216   draw-horizontal-line screen, 0x180/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey
 217   draw-horizontal-line screen, 0x2a0/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey
 218   # cell 0: outputs
 219   var color/eax: int <- state-color self, 0x80/curx, 0x60/cury
 220   draw-rect screen,  0xe8/xmin  0x68/ymin, 0x118/xmax   0x98/ymax, color
 221   draw-rect screen,  0xe8/xmin  0xd0/ymin, 0x118/xmax  0x100/ymax, color
 222   draw-rect screen,  0xe8/xmin 0x148/ymin, 0x118/xmax  0x178/ymax, color
 223   draw-rect screen, 0x158/xmin  0x68/ymin, 0x188/xmax   0x98/ymax, color
 224   draw-rect screen, 0x158/xmin 0x148/ymin, 0x188/xmax  0x178/ymax, color
 225   draw-rect screen, 0x1c8/xmin  0x68/ymin, 0x1f8/xmax   0x98/ymax, color
 226   draw-rect screen, 0x1c8/xmin  0xd0/ymin, 0x1f8/xmax  0x100/ymax, color
 227   draw-rect screen, 0x1c8/xmin 0x148/ymin, 0x1f8/xmax  0x178/ymax, color
 228   # cell 1: outputs
 229   var color/eax: int <- state-color self, 0x81/curx, 0x60/cury
 230   draw-rect screen, 0x208/xmin  0x68/ymin, 0x238/xmax   0x98/ymax, color
 231   draw-rect screen, 0x208/xmin  0xd0/ymin, 0x238/xmax  0x100/ymax, color
 232   draw-rect screen, 0x208/xmin 0x148/ymin, 0x238/xmax  0x178/ymax, color
 233   draw-rect screen, 0x278/xmin  0x68/ymin, 0x2a8/xmax   0x98/ymax, color
 234   draw-rect screen, 0x278/xmin 0x148/ymin, 0x2a8/xmax  0x178/ymax, color
 235   draw-rect screen, 0x2e8/xmin  0x68/ymin, 0x318/xmax   0x98/ymax, color
 236   draw-rect screen, 0x2e8/xmin  0xd0/ymin, 0x318/xmax  0x100/ymax, color
 237   draw-rect screen, 0x2e8/xmin 0x148/ymin, 0x318/xmax  0x178/ymax, color
 238   # cell 2: outputs
 239   var color/eax: int <- state-color self, 0x80/curx, 0x61/cury
 240   draw-rect screen,  0xe8/xmin 0x188/ymin, 0x118/xmax  0x1b8/ymax, color
 241   draw-rect screen,  0xe8/xmin 0x1f0/ymin, 0x118/xmax  0x220/ymax, color
 242   draw-rect screen,  0xe8/xmin 0x268/ymin, 0x118/xmax  0x298/ymax, color
 243   draw-rect screen, 0x158/xmin 0x188/ymin, 0x188/xmax  0x1b8/ymax, color
 244   draw-rect screen, 0x158/xmin 0x268/ymin, 0x188/xmax  0x298/ymax, color
 245   draw-rect screen, 0x1c8/xmin 0x188/ymin, 0x1f8/xmax  0x1b8/ymax, color
 246   draw-rect screen, 0x1c8/xmin 0x1f0/ymin, 0x1f8/xmax  0x220/ymax, color
 247   draw-rect screen, 0x1c8/xmin 0x268/ymin, 0x1f8/xmax  0x298/ymax, color
 248   # cell 3: outputs
 249   var color/eax: int <- state-color self, 0x81/curx, 0x61/cury
 250   draw-rect screen, 0x208/xmin 0x188/ymin, 0x238/xmax  0x1b8/ymax, color
 251   draw-rect screen, 0x208/xmin 0x1f0/ymin, 0x238/xmax  0x220/ymax, color
 252   draw-rect screen, 0x208/xmin 0x268/ymin, 0x238/xmax  0x298/ymax, color
 253   draw-rect screen, 0x278/xmin 0x188/ymin, 0x2a8/xmax  0x1b8/ymax, color
 254   draw-rect screen, 0x278/xmin 0x268/ymin, 0x2a8/xmax  0x298/ymax, color
 255   draw-rect screen, 0x2e8/xmin 0x188/ymin, 0x318/xmax  0x1b8/ymax, color
 256   draw-rect screen, 0x2e8/xmin 0x1f0/ymin, 0x318/xmax  0x220/ymax, color
 257   draw-rect screen, 0x2e8/xmin 0x268/ymin, 0x318/xmax  0x298/ymax, color
 258   # neighboring nodes
 259   var color/eax: int <- state-color self, 0x7f/curx, 0x5f/cury
 260   draw-rect screen,  0xa8/xmin  0x28/ymin,  0xd8/xmax   0x58/ymax, color
 261   var color/eax: int <- state-color self, 0x80/curx, 0x5f/cury
 262   draw-rect screen, 0x158/xmin  0x28/ymin, 0x188/xmax   0x58/ymax, color
 263   draw-rect screen, 0x1c8/xmin  0x28/ymin, 0x1f8/xmax   0x58/ymax, color
 264   var color/eax: int <- state-color self, 0x81/curx, 0x5f/cury
 265   draw-rect screen, 0x208/xmin  0x28/ymin, 0x238/xmax   0x58/ymax, color
 266   draw-rect screen, 0x278/xmin  0x28/ymin, 0x2a8/xmax   0x58/ymax, color
 267   var color/eax: int <- state-color self, 0x82/curx, 0x5f/cury
 268   draw-rect screen, 0x328/xmin  0x28/ymin, 0x358/xmax   0x58/ymax, color
 269   var color/eax: int <- state-color self, 0x7f/curx, 0x60/cury
 270   draw-rect screen,  0xa8/xmin  0xd0/ymin,  0xd8/xmax  0x100/ymax, color
 271   draw-rect screen,  0xa8/xmin 0x148/ymin,  0xd8/xmax  0x178/ymax, color
 272   var color/eax: int <- state-color self, 0x82/curx, 0x60/cury
 273   draw-rect screen, 0x328/xmin  0xd0/ymin, 0x358/xmax  0x100/ymax, color
 274   draw-rect screen, 0x328/xmin 0x148/ymin, 0x358/xmax  0x178/ymax, color
 275   var color/eax: int <- state-color self, 0x7f/curx, 0x61/cury
 276   draw-rect screen,  0xa8/xmin 0x188/ymin,  0xd8/xmax  0x1b8/ymax, color
 277   draw-rect screen,  0xa8/xmin 0x1f0/ymin,  0xd8/xmax  0x220/ymax, color
 278   var color/eax: int <- state-color self, 0x82/curx, 0x61/cury
 279   draw-rect screen, 0x328/xmin 0x188/ymin, 0x358/xmax  0x1b8/ymax, color
 280   draw-rect screen, 0x328/xmin 0x1f0/ymin, 0x358/xmax  0x220/ymax, color
 281   var color/eax: int <- state-color self, 0x7f/curx, 0x62/cury
 282   draw-rect screen,  0xa8/xmin 0x2a8/ymin,  0xd8/xmax  0x2d8/ymax, color
 283   var color/eax: int <- state-color self, 0x80/curx, 0x62/cury
 284   draw-rect screen, 0x158/xmin 0x2a8/ymin, 0x188/xmax  0x2d8/ymax, color
 285   draw-rect screen, 0x1c8/xmin 0x2a8/ymin, 0x1f8/xmax  0x2d8/ymax, color
 286   var color/eax: int <- state-color self, 0x81/curx, 0x62/cury
 287   draw-rect screen, 0x208/xmin 0x2a8/ymin, 0x238/xmax  0x2d8/ymax, color
 288   draw-rect screen, 0x278/xmin 0x2a8/ymin, 0x2a8/xmax  0x2d8/ymax, color
 289   var color/eax: int <- state-color self, 0x82/curx, 0x62/cury
 290   draw-rect screen, 0x328/xmin 0x2a8/ymin, 0x358/xmax  0x2d8/ymax, color
 291   # cell 0: sum and filter nodes
 292   draw-rect screen, 0x148/xsmin  0xc8/ysmin, 0x158/xsmax  0xd8/ysmax, 0x40/color
 293   draw-rect screen, 0x180/xfmin  0xf8/yfmin, 0x190/xfmax 0x108/yfmax, 0x31/color
 294   # cell 1: sum and filter nodes
 295   draw-rect screen, 0x268/xsmin  0xc8/ysmin, 0x278/xsmax  0xd8/ysmax, 0x40/color
 296   draw-rect screen, 0x2a0/xfmin  0xf8/yfmin, 0x2b0/xfmax 0x108/yfmax, 0x31/color
 297   # cell 2: sum and filter nodes
 298   draw-rect screen, 0x148/xsmin 0x1e8/ysmin, 0x158/xsmax 0x1f8/ysmax, 0x40/color
 299   draw-rect screen, 0x180/xfmin 0x218/yfmin, 0x190/xfmax 0x228/yfmax, 0x31/color
 300   # cell 3: sum and filter nodes
 301   draw-rect screen, 0x268/xsmin 0x1e8/ysmin, 0x278/xsmax 0x1f8/ysmax, 0x40/color
 302   draw-rect screen, 0x2a0/xfmin 0x218/yfmin, 0x2b0/xfmax 0x228/yfmax, 0x31/color
 303   # neighbor counts
 304   var n/eax: int <- num-live-neighbors self, 0x80/curx, 0x60/cury
 305   set-cursor-position screen, 0x2d, 0xe
 306   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg
 307   var n/eax: int <- num-live-neighbors self, 0x81/curx, 0x60/cury
 308   set-cursor-position screen, 0x52, 0xe
 309   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg
 310   var n/eax: int <- num-live-neighbors self, 0x80/curx, 0x61/cury
 311   set-cursor-position screen, 0x2d, 0x20
 312   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg
 313   var n/eax: int <- num-live-neighbors self, 0x81/curx, 0x61/cury
 314   set-cursor-position screen, 0x52, 0x20
 315   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg
 316   # cell 0: conveyors from neighboring inputs to sum node
 317   draw-monotonic-bezier screen,  0xc0/x0  0x40/y0,  0x100/x1  0xd0/ys, 0x150/xs  0xd0/ys,  4/color
 318   draw-monotonic-bezier screen,  0xc0/x0  0xe8/y0,   0xc0/x1  0xd0/ys, 0x150/xs  0xd0/ys,  4/color
 319   draw-monotonic-bezier screen,  0xc0/x0 0x1a0/y0,   0xe0/x1  0xd0/ys, 0x150/xs  0xd0/ys,  4/color
 320   draw-monotonic-bezier screen, 0x170/x0  0x40/y0,  0x150/x1  0x80/y1, 0x150/xs  0xd0/ys,  4/color
 321   draw-monotonic-bezier screen, 0x170/x0 0x1a0/y0,  0x150/x1 0x1a0/y1, 0x150/xs  0xd0/ys,  4/color
 322   draw-monotonic-bezier screen, 0x220/x0  0x40/y0,  0x150/x1  0x80/y1, 0x150/xs  0xd0/ys,  4/color
 323   draw-monotonic-bezier screen, 0x220/x0  0xe8/y0,  0x220/x1  0xd0/y1, 0x150/xs  0xd0/ys,  4/color
 324   draw-monotonic-bezier screen, 0x220/x0 0x1a0/y0,  0x180/x1 0x1a0/y1, 0x150/xs  0xd0/ys,  4/color
 325   # cell 0: conveyors from filter to outputs
 326   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x160/x1  0x8c/y1, 0x100/x2  0x80/y2,  0x2a/color
 327   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x100/x1 0x100/y1, 0x100/x2  0xe8/y2,  0x2a/color
 328   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x100/x1 0x100/y1, 0x100/x2 0x160/y2,  0x2a/color
 329   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x188/x1  0x80/y1, 0x170/x2  0x80/y2,  0x2a/color
 330   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x188/x1 0x160/y1, 0x170/x2 0x160/y2,  0x2a/color
 331   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x1e0/x1 0x100/y1, 0x1e0/x2  0x80/y2,  0x2a/color
 332   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x1e0/x1 0x100/y1  0x1e0/x2  0xe8/y2,  0x2a/color
 333   draw-monotonic-bezier screen, 0x188/xf 0x100/yf,  0x1e0/x1 0x100/y1, 0x1e0/x2 0x160/y2,  0x2a/color
 334   # cell 0: time-variant portion: 16 repeating steps
 335   $render1:cell0: {
 336     var tick-a/eax: (addr int) <- get self, tick
 337     var progress/eax: int <- copy *tick-a
 338     progress <- and 0xf
 339     # cell 0: 7 time steps for getting inputs to sum
 340     {
 341       compare progress, 7
 342       break-if->=
 343       var u/xmm7: float <- convert progress
 344       var six/eax: int <- copy 6
 345       var six-f/xmm0: float <- convert six
 346       u <- divide six-f
 347       # points on conveyors from neighboring cells
 348       draw-bezier-point screen, u,  0xc0/x0  0x40/y0, 0x100/x1  0xd0/ys, 0x150/xs  0xd0/ys, 7/color, 4/radius
 349       draw-bezier-point screen, u,  0xc0/x0  0xe8/y0,  0xc0/x1  0xd0/ys, 0x150/xs  0xd0/ys, 7/color, 4/radius
 350       draw-bezier-point screen, u,  0xc0/x0 0x1a0/y0,  0xe0/x1  0xd0/ys, 0x150/xs  0xd0/ys, 7/color, 4/radius
 351       draw-bezier-point screen, u, 0x170/x0  0x40/y0, 0x150/x1  0x80/y1, 0x150/xs  0xd0/ys, 7/color, 4/radius
 352       draw-bezier-point screen, u, 0x170/x0 0x1a0/y0, 0x150/x1 0x1a0/y1, 0x150/xs  0xd0/ys, 7/color, 4/radius
 353       draw-bezier-point screen, u, 0x220/x0  0x40/y0, 0x150/x1  0x80/y1, 0x150/xs  0xd0/ys, 7/color, 4/radius
 354       draw-bezier-point screen, u, 0x220/x0  0xe8/y0, 0x220/x1  0xd0/y1, 0x150/xs  0xd0/ys, 7/color, 4/radius
 355       draw-bezier-point screen, u, 0x220/x0 0x1a0/y0, 0x180/x1 0x1a0/y1, 0x150/xs  0xd0/ys, 7/color, 4/radius
 356       break $render1:cell0
 357     }
 358     # cell 0: two time steps for getting count to filter
 359     progress <- subtract 7
 360     {
 361       compare progress, 2
 362       break-if->=
 363       break $render1:cell0
 364     }
 365     # cell 0: final 7 time steps for updating output
 366     progress <- subtract 2
 367     # cell 0: points on conveyors to outputs
 368     var u/xmm7: float <- convert progress
 369     var six/eax: int <- copy 6
 370     var six-f/xmm0: float <- convert six
 371     u <- divide six-f
 372     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x160/x1  0x8c/y1, 0x100/x2  0x80/y2, 7/color, 4/radius
 373     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x100/x1 0x100/y1, 0x100/x2  0xe8/y2, 7/color, 4/radius
 374     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x100/x1 0x100/y1, 0x100/x2 0x160/y2, 7/color, 4/radius
 375     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x188/xf  0x80/y1, 0x170/x2  0x80/y2, 7/color, 4/radius
 376     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x188/xf 0x160/y1, 0x170/x2 0x160/y2, 7/color, 4/radius
 377     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x1e0/x1 0x100/y1, 0x1e0/x2  0x80/y2, 7/color, 4/radius
 378     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x1e0/x1 0x100/y1, 0x1e0/x2  0xe8/y2, 7/color, 4/radius
 379     draw-bezier-point screen, u, 0x188/xf 0x100/yf,  0x1e0/x1 0x100/y1, 0x1e0/x2 0x160/y2, 7/color, 4/radius
 380   }
 381   # cell 1: conveyors from neighboring inputs to sum node
 382   draw-monotonic-bezier screen, 0x1e0/x0  0x40/y0,  0x220/x1  0xd0/ys, 0x270/xs  0xd0/ys,  4/color
 383   draw-monotonic-bezier screen, 0x1e0/x0  0xe8/y0,  0x1e0/x1  0xd0/ys, 0x270/xs  0xd0/ys,  4/color
 384   draw-monotonic-bezier screen, 0x1e0/x0 0x1a0/y0,  0x200/x1  0xd0/ys, 0x270/xs  0xd0/ys,  4/color
 385   draw-monotonic-bezier screen, 0x290/x0  0x40/y0,  0x270/x1  0x80/y1, 0x270/xs  0xd0/ys,  4/color
 386   draw-monotonic-bezier screen, 0x290/x0 0x1a0/y0,  0x270/x1 0x1a0/y1, 0x270/xs  0xd0/ys,  4/color
 387   draw-monotonic-bezier screen, 0x340/x0  0x40/y0,  0x270/x1  0x80/y1, 0x270/xs  0xd0/ys,  4/color
 388   draw-monotonic-bezier screen, 0x340/x0  0xe8/y0,  0x340/x1  0xd0/y1, 0x270/xs  0xd0/ys,  4/color
 389   draw-monotonic-bezier screen, 0x340/x0 0x1a0/y0,  0x2a0/x1 0x1a0/y1, 0x270/xs  0xd0/ys,  4/color
 390   # cell 1: conveyors from filter to outputs
 391   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x280/x1  0x8c/y1, 0x220/x2  0x80/y2,  0x2a/color
 392   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x220/x1 0x100/y1, 0x220/x2  0xe8/y2,  0x2a/color
 393   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x220/x1 0x100/y1, 0x220/x2 0x160/y2,  0x2a/color
 394   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x2a8/x1  0x80/y1, 0x290/x2  0x80/y2,  0x2a/color
 395   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x2a8/x1 0x160/y1, 0x290/x2 0x160/y2,  0x2a/color
 396   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x300/x1 0x100/y1, 0x300/x2  0x80/y2,  0x2a/color
 397   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x300/x1 0x100/y1  0x300/x2  0xe8/y2,  0x2a/color
 398   draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf,  0x300/x1 0x100/y1, 0x300/x2 0x160/y2,  0x2a/color
 399   # cell 1: time-variant portion: 16 repeating steps
 400   $render1:cell1: {
 401     var tick-a/eax: (addr int) <- get self, tick
 402     var progress/eax: int <- copy *tick-a
 403     progress <- and 0xf
 404     # cell 1: 7 time steps for getting inputs to sum
 405     {
 406       compare progress, 7
 407       break-if->=
 408       var u/xmm7: float <- convert progress
 409       var six/eax: int <- copy 6
 410       var six-f/xmm0: float <- convert six
 411       u <- divide six-f
 412       # points on conveyors from neighboring cells
 413       draw-bezier-point screen, u, 0x1e0/x0  0x40/y0, 0x220/x1  0xd0/ys, 0x270/xs  0xd0/ys, 7/color, 4/radius
 414       draw-bezier-point screen, u, 0x1e0/x0  0xe8/y0, 0x1e0/x1  0xd0/ys, 0x270/xs  0xd0/ys, 7/color, 4/radius
 415       draw-bezier-point screen, u, 0x1e0/x0 0x1a0/y0, 0x200/x1  0xd0/ys, 0x270/xs  0xd0/ys, 7/color, 4/radius
 416       draw-bezier-point screen, u, 0x290/x0  0x40/y0, 0x270/x1  0x80/y1, 0x270/xs  0xd0/ys, 7/color, 4/radius
 417       draw-bezier-point screen, u, 0x290/x0 0x1a0/y0, 0x270/x1 0x1a0/y1, 0x270/xs  0xd0/ys, 7/color, 4/radius
 418       draw-bezier-point screen, u, 0x340/x0  0x40/y0, 0x270/x1  0x80/y1, 0x270/xs  0xd0/ys, 7/color, 4/radius
 419       draw-bezier-point screen, u, 0x340/x0  0xe8/y0, 0x340/x1  0xd0/y1, 0x270/xs  0xd0/ys, 7/color, 4/radius
 420       draw-bezier-point screen, u, 0x340/x0 0x1a0/y0, 0x2a0/x1 0x1a0/y1, 0x270/xs  0xd0/ys, 7/color, 4/radius
 421       break $render1:cell1
 422     }
 423     # cell 1: two time steps for getting count to filter
 424     progress <- subtract 7
 425     {
 426       compare progress, 2
 427       break-if->=
 428       break $render1:cell1
 429     }
 430     # cell 1: final 7 time steps for updating output
 431     progress <- subtract 2
 432     # cell 1: points on conveyors to outputs
 433     var u/xmm7: float <- convert progress
 434     var six/eax: int <- copy 6
 435     var six-f/xmm0: float <- convert six
 436     u <- divide six-f
 437     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x280/x1  0x8c/y1, 0x220/x2  0x80/y2, 7/color, 4/radius
 438     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x220/x1 0x100/y1, 0x220/x2  0xe8/y2, 7/color, 4/radius
 439     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x220/x1 0x100/y1, 0x220/x2 0x160/y2, 7/color, 4/radius
 440     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x2a8/xf  0x80/y1, 0x290/x2  0x80/y2, 7/color, 4/radius
 441     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x2a8/xf 0x160/y1, 0x290/x2 0x160/y2, 7/color, 4/radius
 442     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x300/x1 0x100/y1, 0x300/x2  0x80/y2, 7/color, 4/radius
 443     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x300/x1 0x100/y1, 0x300/x2  0xe8/y2, 7/color, 4/radius
 444     draw-bezier-point screen, u, 0x2a8/xf 0x100/yf,  0x300/x1 0x100/y1, 0x300/x2 0x160/y2, 7/color, 4/radius
 445   }
 446   # cell 2: conveyors from neighboring inputs to sum node
 447   draw-monotonic-bezier screen,  0xc0/x0 0x160/y0,  0x100/x1 0x1f0/ys, 0x150/xs 0x1f0/ys,  4/color
 448   draw-monotonic-bezier screen,  0xc0/x0 0x208/y0,   0xc0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys,  4/color
 449   draw-monotonic-bezier screen,  0xc0/x0 0x2c0/y0,   0xe0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys,  4/color
 450   draw-monotonic-bezier screen, 0x170/x0 0x160/y0,  0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys,  4/color
 451   draw-monotonic-bezier screen, 0x170/x0 0x2c0/y0,  0x150/x1 0x2c0/y1, 0x150/xs 0x1f0/ys,  4/color
 452   draw-monotonic-bezier screen, 0x220/x0 0x160/y0,  0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys,  4/color
 453   draw-monotonic-bezier screen, 0x220/x0 0x208/y0,  0x220/x1 0x1f0/y1, 0x150/xs 0x1f0/ys,  4/color
 454   draw-monotonic-bezier screen, 0x220/x0 0x2c0/y0,  0x180/x1 0x2c0/y1, 0x150/xs 0x1f0/ys,  4/color
 455   # cell 2: conveyors from filter to outputs
 456   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x160/x1 0x1ac/y1, 0x100/x2 0x1a0/y2,  0x2a/color
 457   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x100/x1 0x220/y1, 0x100/x2 0x208/y2,  0x2a/color
 458   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x100/x1 0x220/y1, 0x100/x2 0x280/y2,  0x2a/color
 459   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x188/x1 0x1a0/y1, 0x170/x2 0x1a0/y2,  0x2a/color
 460   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x188/x1 0x280/y1, 0x170/x2 0x280/y2,  0x2a/color
 461   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x1e0/x1 0x220/y1, 0x1e0/x2 0x1a0/y2,  0x2a/color
 462   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x1e0/x1 0x220/y1  0x1e0/x2 0x208/y2,  0x2a/color
 463   draw-monotonic-bezier screen, 0x188/xf 0x220/yf,  0x1e0/x1 0x220/y1, 0x1e0/x2 0x280/y2,  0x2a/color
 464   # cell 2: time-variant portion: 16 repeating steps
 465   $render1:cell2: {
 466     var tick-a/eax: (addr int) <- get self, tick
 467     var progress/eax: int <- copy *tick-a
 468     progress <- and 0xf
 469     # cell 2: 7 time steps for getting inputs to sum
 470     {
 471       compare progress, 7
 472       break-if->=
 473       var u/xmm7: float <- convert progress
 474       var six/eax: int <- copy 6
 475       var six-f/xmm0: float <- convert six
 476       u <- divide six-f
 477       # points on conveyors from neighboring cells
 478       draw-bezier-point screen, u,  0xc0/x0 0x160/y0, 0x100/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 479       draw-bezier-point screen, u,  0xc0/x0 0x208/y0,  0xc0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 480       draw-bezier-point screen, u,  0xc0/x0 0x2c0/y0,  0xe0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 481       draw-bezier-point screen, u, 0x170/x0 0x160/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 482       draw-bezier-point screen, u, 0x170/x0 0x2c0/y0, 0x150/x1 0x2c0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 483       draw-bezier-point screen, u, 0x220/x0 0x160/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 484       draw-bezier-point screen, u, 0x220/x0 0x208/y0, 0x220/x1 0x1f0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 485       draw-bezier-point screen, u, 0x220/x0 0x2c0/y0, 0x180/x1 0x2c0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius
 486       break $render1:cell2
 487     }
 488     # cell 2: two time steps for getting count to filter
 489     progress <- subtract 7
 490     {
 491       compare progress, 2
 492       break-if->=
 493       break $render1:cell2
 494     }
 495     # cell 2: final 7 time steps for updating output
 496     progress <- subtract 2
 497     # cell 2: points on conveyors to outputs
 498     var u/xmm7: float <- convert progress
 499     var six/eax: int <- copy 6
 500     var six-f/xmm0: float <- convert six
 501     u <- divide six-f
 502     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x160/x1 0x1ac/y1, 0x100/x2 0x1a0/y2, 7/color, 4/radius
 503     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x100/x1 0x220/y1, 0x100/x2 0x208/y2, 7/color, 4/radius
 504     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x100/x1 0x220/y1, 0x100/x2 0x280/y2, 7/color, 4/radius
 505     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x188/xf 0x1a0/y1, 0x170/x2 0x1a0/y2, 7/color, 4/radius
 506     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x188/xf 0x280/y1, 0x170/x2 0x280/y2, 7/color, 4/radius
 507     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x1e0/x1 0x220/y1, 0x1e0/x2 0x1a0/y2, 7/color, 4/radius
 508     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x1e0/x1 0x220/y1, 0x1e0/x2 0x208/y2, 7/color, 4/radius
 509     draw-bezier-point screen, u, 0x188/xf 0x220/yf,  0x1e0/x1 0x220/y1, 0x1e0/x2 0x280/y2, 7/color, 4/radius
 510   }
 511   # cell 3: conveyors from neighboring inputs to sum node
 512   draw-monotonic-bezier screen, 0x1e0/x0 0x160/y0,  0x220/x1 0x1f0/ys, 0x270/xs 0x1f0/ys,  4/color
 513   draw-monotonic-bezier screen, 0x1e0/x0 0x208/y0,  0x1e0/x1 0x1f0/ys, 0x270/xs 0x1f0/ys,  4/color
 514   draw-monotonic-bezier screen, 0x1e0/x0 0x2c0/y0,  0x200/x1 0x1f0/ys, 0x270/xs 0x1f0/ys,  4/color
 515   draw-monotonic-bezier screen, 0x290/x0 0x160/y0,  0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys,  4/color
 516   draw-monotonic-bezier screen, 0x290/x0 0x2c0/y0,  0x270/x1 0x2c0/y1, 0x270/xs 0x1f0/ys,  4/color
 517   draw-monotonic-bezier screen, 0x340/x0 0x160/y0,  0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys,  4/color
 518   draw-monotonic-bezier screen, 0x340/x0 0x208/y0,  0x340/x1 0x1f0/y1, 0x270/xs 0x1f0/ys,  4/color
 519   draw-monotonic-bezier screen, 0x340/x0 0x2c0/y0,  0x2a0/x1 0x2c0/y1, 0x270/xs 0x1f0/ys,  4/color
 520   # cell 3: conveyors from filter to outputs
 521   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x280/x1 0x1ac/y1, 0x220/x2 0x1a0/y2,  0x2a/color
 522   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x220/x1 0x220/y1, 0x220/x2 0x208/y2,  0x2a/color
 523   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x220/x1 0x220/y1, 0x220/x2 0x280/y2,  0x2a/color
 524   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x2a8/x1 0x1a0/y1, 0x290/x2 0x1a0/y2,  0x2a/color
 525   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x2a8/x1 0x280/y1, 0x290/x2 0x280/y2,  0x2a/color
 526   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x300/x1 0x220/y1, 0x300/x2 0x1a0/y2,  0x2a/color
 527   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x300/x1 0x220/y1  0x300/x2 0x208/y2,  0x2a/color
 528   draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf,  0x300/x1 0x220/y1, 0x300/x2 0x280/y2,  0x2a/color
 529   # cell 3: time-variant portion: 16 repeating steps
 530   $render1:cell3: {
 531     var tick-a/eax: (addr int) <- get self, tick
 532     var progress/eax: int <- copy *tick-a
 533     progress <- and 0xf
 534     # cell 3: 7 time steps for getting inputs to sum
 535     {
 536       compare progress, 7
 537       break-if->=
 538       var u/xmm7: float <- convert progress
 539       var six/eax: int <- copy 6
 540       var six-f/xmm0: float <- convert six
 541       u <- divide six-f
 542       # points on conveyors from neighboring cells
 543       draw-bezier-point screen, u, 0x1e0/x0 0x160/y0, 0x220/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 544       draw-bezier-point screen, u, 0x1e0/x0 0x208/y0, 0x1e0/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 545       draw-bezier-point screen, u, 0x1e0/x0 0x2c0/y0, 0x200/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 546       draw-bezier-point screen, u, 0x290/x0 0x160/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 547       draw-bezier-point screen, u, 0x290/x0 0x2c0/y0, 0x270/x1 0x2c0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 548       draw-bezier-point screen, u, 0x340/x0 0x160/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 549       draw-bezier-point screen, u, 0x340/x0 0x208/y0, 0x340/x1 0x1f0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 550       draw-bezier-point screen, u, 0x340/x0 0x2c0/y0, 0x2a0/x1 0x2c0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius
 551       break $render1:cell3
 552     }
 553     # cell 3: two time steps for getting count to filter
 554     progress <- subtract 7
 555     {
 556       compare progress, 2
 557       break-if->=
 558       break $render1:cell3
 559     }
 560     # cell 3: final 7 time steps for updating output
 561     progress <- subtract 2
 562     # cell 3: points on conveyors to outputs
 563     var u/xmm7: float <- convert progress
 564     var six/eax: int <- copy 6
 565     var six-f/xmm0: float <- convert six
 566     u <- divide six-f
 567     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x280/x1 0x1ac/y1, 0x220/x2 0x1a0/y2, 7/color, 4/radius
 568     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x220/x1 0x220/y1, 0x220/x2 0x208/y2, 7/color, 4/radius
 569     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x220/x1 0x220/y1, 0x220/x2 0x280/y2, 7/color, 4/radius
 570     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x2a8/xf 0x1a0/y1, 0x290/x2 0x1a0/y2, 7/color, 4/radius
 571     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x2a8/xf 0x280/y1, 0x290/x2 0x280/y2, 7/color, 4/radius
 572     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x300/x1 0x220/y1, 0x300/x2 0x1a0/y2, 7/color, 4/radius
 573     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x300/x1 0x220/y1, 0x300/x2 0x208/y2, 7/color, 4/radius
 574     draw-bezier-point screen, u, 0x2a8/xf 0x220/yf,  0x300/x1 0x220/y1, 0x300/x2 0x280/y2, 7/color, 4/radius
 575   }
 576 }
 577 
 578 fn draw-bezier-point screen: (addr screen), u: float, x0: int, y0: int, x1: int, y1: int, x2: int, y2: int, color: int, radius: int {
 579   var _cy/eax: int <- bezier-point u, y0, y1, y2
 580   var cy/ecx: int <- copy _cy
 581   var cx/eax: int <- bezier-point u, x0, x1, x2
 582   draw-disc screen, cx, cy, radius, color, 0xf/border-color=white
 583 }
 584 
 585 fn draw-linear-point screen: (addr screen), u: float, x0: int, y0: int, x1: int, y1: int, color: int, radius: int {
 586   var _cy/eax: int <- line-point u, y0, y1
 587   var cy/ecx: int <- copy _cy
 588   var cx/eax: int <- line-point u, x0, x1
 589   draw-disc screen, cx, cy, radius, color, 0xf/border-color=white
 590 }
 591 
 592 fn edit keyboard: (addr keyboard), _self: (addr environment) {
 593   var self/esi: (addr environment) <- copy _self
 594   var key/eax: byte <- read-key keyboard
 595   # space: play/pause
 596   {
 597     compare key, 0x20/space
 598     break-if-!=
 599     var play?/eax: (addr boolean) <- get self, play?
 600     compare *play?, 0/false
 601     {
 602       break-if-=
 603       copy-to *play?, 0/false
 604       return
 605     }
 606     copy-to *play?, 1/true
 607     return
 608   }
 609   # 0: back to start
 610   {
 611     compare key, 0x30/0
 612     break-if-!=
 613     clear-environment self
 614     return
 615   }
 616   # l: loop from here to start
 617   {
 618     compare key, 0x6c/l
 619     break-if-!=
 620     var tick-a/eax: (addr int) <- get self, tick
 621     var tick/eax: int <- copy *tick-a
 622     var loop/ecx: (addr int) <- get self, loop
 623     copy-to *loop, tick
 624     return
 625   }
 626   # L: reset loop
 627   {
 628     compare key, 0x4c/L
 629     break-if-!=
 630     var loop/eax: (addr int) <- get self, loop
 631     copy-to *loop, 0
 632     return
 633   }
 634   # -: zoom out
 635   {
 636     compare key, 0x2d/-
 637     break-if-!=
 638     var zoom/eax: (addr int) <- get self, zoom
 639     compare *zoom, 1
 640     {
 641       break-if-!=
 642       copy-to *zoom, 4
 643     }
 644     compare *zoom, 0
 645     {
 646       break-if-!=
 647       copy-to *zoom, 1
 648     }
 649     # set tick to a multiple of zoom
 650     var tick-a/edx: (addr int) <- get self, tick
 651     clear-lowest-bits tick-a, *zoom
 652     return
 653   }
 654   # +: zoom in
 655   {
 656     compare key, 0x2b/+
 657     break-if-!=
 658     var zoom/eax: (addr int) <- get self, zoom
 659     compare *zoom, 1
 660     {
 661       break-if-!=
 662       copy-to *zoom, 0
 663     }
 664     compare *zoom, 4
 665     {
 666       break-if-!=
 667       copy-to *zoom, 1
 668     }
 669     # set tick to a multiple of zoom
 670     var tick-a/edx: (addr int) <- get self, tick
 671     clear-lowest-bits tick-a, *zoom
 672     return
 673   }
 674 }
 675 
 676 fn step _self: (addr environment) {
 677   var self/esi: (addr environment) <- copy _self
 678   var tick-a/ecx: (addr int) <- get self, tick
 679   var zoom/edx: (addr int) <- get self, zoom
 680   compare *zoom, 0
 681   {
 682     break-if-!=
 683     increment *tick-a
 684   }
 685   compare *zoom, 1
 686   {
 687     break-if-!=
 688     # I wanted to speed up time, but that doesn't seem very usable.
 689 #?     add-to *tick-a, 2
 690     increment *tick-a
 691   }
 692   compare *zoom, 4
 693   {
 694     break-if-!=
 695     add-to *tick-a, 0x10
 696   }
 697   var tick/eax: int <- copy *tick-a
 698   tick <- and 0xf
 699   compare tick, 0
 700   {
 701     break-if-!=
 702     step4 self
 703   }
 704   var loop-a/eax: (addr int) <- get self, loop
 705   compare *loop-a, 0
 706   {
 707     break-if-=
 708     var loop/eax: int <- copy *loop-a
 709     compare *tick-a, loop
 710     break-if-<
 711     clear-environment self
 712   }
 713 }
 714 
 715 fn initialize-environment _self: (addr environment) {
 716   var self/esi: (addr environment) <- copy _self
 717   var zoom/eax: (addr int) <- get self, zoom
 718   copy-to *zoom, 0
 719   var play?/eax: (addr boolean) <- get self, play?
 720   copy-to *play?, 1/true
 721   var data-ah/eax: (addr handle array handle array cell) <- get self, data
 722   populate data-ah, 0x100
 723   var data/eax: (addr array handle array cell) <- lookup *data-ah
 724   var y/ecx: int <- copy 0
 725   {
 726     compare y, 0xc0
 727     break-if->=
 728     var dest-ah/eax: (addr handle array cell) <- index data, y
 729     populate dest-ah, 0x100
 730     y <- increment
 731     loop
 732   }
 733   set self, 0x80, 0x5f, 1/alive
 734   set self, 0x81, 0x5f, 1/alive
 735   set self, 0x7f, 0x60, 1/alive
 736   set self, 0x80, 0x60, 1/alive
 737   set self, 0x80, 0x61, 1/alive
 738   flush self
 739 }
 740 
 741 fn clear-environment _self: (addr environment) {
 742   var self/esi: (addr environment) <- copy _self
 743   var tick/eax: (addr int) <- get self, tick
 744   copy-to *tick, 0
 745   # don't touch zoom or play settings
 746   var data-ah/eax: (addr handle array handle array cell) <- get self, data
 747   var data/eax: (addr array handle array cell) <- lookup *data-ah
 748   var y/ecx: int <- copy 0
 749   {
 750     compare y, 0xc0
 751     break-if->=
 752     var row-ah/eax: (addr handle array cell) <- index data, y
 753     var row/eax: (addr array cell) <- lookup *row-ah
 754     var x/edx: int <- copy 0
 755     {
 756       compare x, 0x100
 757       break-if->=
 758       var dest/eax: (addr cell) <- index row, x
 759       clear-object dest
 760       x <- increment
 761       loop
 762     }
 763     y <- increment
 764     loop
 765   }
 766   set self, 0x80, 0x5f, 1/alive
 767   set self, 0x81, 0x5f, 1/alive
 768   set self, 0x7f, 0x60, 1/alive
 769   set self, 0x80, 0x60, 1/alive
 770   set self, 0x80, 0x61, 1/alive
 771   flush self
 772 }
 773 
 774 fn set _self: (addr environment), _x: int, _y: int, _val: boolean {
 775   var self/esi: (addr environment) <- copy _self
 776   var data-ah/eax: (addr handle array handle array cell) <- get self, data
 777   var data/eax: (addr array handle array cell) <- lookup *data-ah
 778   var y/ecx: int <- copy _y
 779   var row-ah/eax: (addr handle array cell) <- index data, y
 780   var row/eax: (addr array cell) <- lookup *row-ah
 781   var x/ecx: int <- copy _x
 782   var cell/eax: (addr cell) <- index row, x
 783   var dest/eax: (addr boolean) <- get cell, next
 784   var val/ecx: boolean <- copy _val
 785   copy-to *dest, val
 786 }
 787 
 788 fn state _self: (addr environment), _x: int, _y: int -> _/eax: boolean {
 789   var self/esi: (addr environment) <- copy _self
 790   var x/ecx: int <- copy _x
 791   var y/edx: int <- copy _y
 792   # clip at the edge
 793   compare x, 0
 794   {
 795     break-if->=
 796     return 0/false
 797   }
 798   compare y, 0
 799   {
 800     break-if->=
 801     return 0/false
 802   }
 803   compare x, 0x100/width
 804   {
 805     break-if-<
 806     return 0/false
 807   }
 808   compare y, 0xc0/height
 809   {
 810     break-if-<
 811     return 0/false
 812   }
 813   var data-ah/eax: (addr handle array handle array cell) <- get self, data
 814   var data/eax: (addr array handle array cell) <- lookup *data-ah
 815   var row-ah/eax: (addr handle array cell) <- index data, y
 816   var row/eax: (addr array cell) <- lookup *row-ah
 817   var cell/eax: (addr cell) <- index row, x
 818   var src/eax: (addr boolean) <- get cell, curr
 819   return *src
 820 }
 821 
 822 fn state-color _self: (addr environment), x: int, y: int -> _/eax: int {
 823   var self/esi: (addr environment) <- copy _self
 824   var color/ecx: int <- copy 0x1a/dead
 825   {
 826     var state/eax: boolean <- state self, x, y
 827     compare state, 0/dead
 828     break-if-=
 829     color <- copy 0xf/alive
 830   }
 831   return color
 832 }
 833 
 834 fn flush  _self: (addr environment) {
 835   var self/esi: (addr environment) <- copy _self
 836   var data-ah/eax: (addr handle array handle array cell) <- get self, data
 837   var _data/eax: (addr array handle array cell) <- lookup *data-ah
 838   var data/esi: (addr array handle array cell) <- copy _data
 839   var y/ecx: int <- copy 0
 840   {
 841     compare y, 0xc0/height
 842     break-if->=
 843     var row-ah/eax: (addr handle array cell) <- index data, y
 844     var _row/eax: (addr array cell) <- lookup *row-ah
 845     var row/ebx: (addr array cell) <- copy _row
 846     var x/edx: int <- copy 0
 847     {
 848       compare x, 0x100/width
 849       break-if->=
 850       var cell-a/eax: (addr cell) <- index row, x
 851       var curr-a/edi: (addr boolean) <- get cell-a, curr
 852       var next-a/esi: (addr boolean) <- get cell-a, next
 853       var val/eax: boolean <- copy *next-a
 854       copy-to *curr-a, val
 855       copy-to *next-a, 0/dead
 856       x <- increment
 857       loop
 858     }
 859     y <- increment
 860     loop
 861   }
 862 }
 863 
 864 fn render4 screen: (addr screen), _self: (addr environment) {
 865   var self/esi: (addr environment) <- copy _self
 866   var y/ecx: int <- copy 0
 867   {
 868     compare y, 0xc0/height
 869     break-if->=
 870     var x/edx: int <- copy 0
 871     {
 872       compare x, 0x100/width
 873       break-if->=
 874       var state/eax: boolean <- state self, x, y
 875       compare state, 0/false
 876       {
 877         break-if-=
 878         render4-cell screen, x, y, 0xf/alive
 879       }
 880       compare state, 0/false
 881       {
 882         break-if-!=
 883         render4-cell screen, x, y, 0x1a/dead
 884       }
 885       x <- increment
 886       loop
 887     }
 888     y <- increment
 889     loop
 890   }
 891 }
 892 
 893 fn render4-cell screen: (addr screen), x: int, y: int, color: int {
 894   var xmin/eax: int <- copy x
 895   xmin <- shift-left 2
 896   var xmax/ecx: int <- copy xmin
 897   xmax <- add 4
 898   var ymin/edx: int <- copy y
 899   ymin <- shift-left 2
 900   var ymax/ebx: int <- copy ymin
 901   ymax <- add 4
 902   draw-rect screen, xmin ymin, xmax ymax, color
 903 }
 904 
 905 fn step4 _self: (addr environment) {
 906   var self/esi: (addr environment) <- copy _self
 907   var y/ecx: int <- copy 0
 908   {
 909     compare y, 0xc0/height
 910     break-if->=
 911     var x/edx: int <- copy 0
 912     {
 913       compare x, 0x100/width
 914       break-if->=
 915       var n/eax: int <- num-live-neighbors self, x, y
 916       # if neighbors < 2, die of loneliness
 917       {
 918         compare n, 2
 919         break-if->=
 920         set self, x, y, 0/dead
 921       }
 922       # if neighbors > 3, die of overcrowding
 923       {
 924         compare n, 3
 925         break-if-<=
 926         set self, x, y, 0/dead
 927       }
 928       # if neighbors = 2, preserve state
 929       {
 930         compare n, 2
 931         break-if-!=
 932         var old-state/eax: boolean <- state self, x, y
 933         set self, x, y, old-state
 934       }
 935       # if neighbors = 3, cell quickens to life
 936       {
 937         compare n, 3
 938         break-if-!=
 939         set self, x, y, 1/live
 940       }
 941       x <- increment
 942       loop
 943     }
 944     y <- increment
 945     loop
 946   }
 947   flush self
 948 }
 949 
 950 fn num-live-neighbors _self: (addr environment), x: int, y: int -> _/eax: int {
 951   var self/esi: (addr environment) <- copy _self
 952   var result/edi: int <- copy 0
 953   # row above: zig
 954   decrement y
 955   decrement x
 956   var s/eax: boolean <- state self, x, y
 957   {
 958     compare s, 0/false
 959     break-if-=
 960     result <- increment
 961   }
 962   increment x
 963   s <- state self, x, y
 964   {
 965     compare s, 0/false
 966     break-if-=
 967     result <- increment
 968   }
 969   increment x
 970   s <- state self, x, y
 971   {
 972     compare s, 0/false
 973     break-if-=
 974     result <- increment
 975   }
 976   # curr row: zag
 977   increment y
 978   s <- state self, x, y
 979   {
 980     compare s, 0/false
 981     break-if-=
 982     result <- increment
 983   }
 984   subtract-from x, 2
 985   s <- state self, x, y
 986   {
 987     compare s, 0/false
 988     break-if-=
 989     result <- increment
 990   }
 991   # row below: zig
 992   increment y
 993   s <- state self, x, y
 994   {
 995     compare s, 0/false
 996     break-if-=
 997     result <- increment
 998   }
 999   increment x
1000   s <- state self, x, y
1001   {
1002     compare s, 0/false
1003     break-if-=
1004     result <- increment
1005   }
1006   increment x
1007   s <- state self, x, y
1008   {
1009     compare s, 0/false
1010     break-if-=
1011     result <- increment
1012   }
1013   return result
1014 }
1015 
1016 fn linger _self: (addr environment) {
1017   var self/esi: (addr environment) <- copy _self
1018   var i/ecx: int <- copy 0
1019   {
1020     compare i, 0x10000000  # Kartik's Linux with -enable-kvm
1021 #?     compare i, 0x8000000  # Kartik's Mac with -accel tcg
1022     break-if->=
1023     i <- increment
1024     loop
1025   }
1026 }