https://github.com/akkartik/mu/blob/main/shell/life.mu
  1 fn state _grid: (addr array boolean), x: int, y: int -> _/eax: boolean {
  2   # clip at the edge
  3   compare x, 0
  4   {
  5     break-if->=
  6     return 0/false
  7   }
  8   compare y, 0
  9   {
 10     break-if->=
 11     return 0/false
 12   }
 13   compare x, 0x100/width
 14   {
 15     break-if-<
 16     return 0/false
 17   }
 18   compare y, 0xc0/height
 19   {
 20     break-if-<
 21     return 0/false
 22   }
 23   var idx/eax: int <- copy y
 24   idx <- shift-left 8/log2width
 25   idx <- add x
 26   var grid/esi: (addr array boolean) <- copy _grid
 27   var result/eax: (addr boolean) <- index grid, idx
 28   return *result
 29 }
 30 
 31 fn set-state _grid: (addr array boolean), x: int, y: int, val: boolean {
 32   # don't bother checking bounds
 33   var idx/eax: int <- copy y
 34   idx <- shift-left 8/log2width
 35   idx <- add x
 36   var grid/esi: (addr array boolean) <- copy _grid
 37   var result/eax: (addr boolean) <- index grid, idx
 38   var src/ecx: boolean <- copy val
 39   copy-to *result, src
 40 }
 41 
 42 fn num-live-neighbors grid: (addr array boolean), x: int, y: int -> _/eax: int {
 43   var result/edi: int <- copy 0
 44   # row above: zig
 45   decrement y
 46   decrement x
 47   var s/eax: boolean <- state grid, x, y
 48   {
 49     compare s, 0/false
 50     break-if-=
 51     result <- increment
 52   }
 53   increment x
 54   s <- state grid, x, y
 55   {
 56     compare s, 0/false
 57     break-if-=
 58     result <- increment
 59   }
 60   increment x
 61   s <- state grid, x, y
 62   {
 63     compare s, 0/false
 64     break-if-=
 65     result <- increment
 66   }
 67   # curr row: zag
 68   increment y
 69   s <- state grid, x, y
 70   {
 71     compare s, 0/false
 72     break-if-=
 73     result <- increment
 74   }
 75   subtract-from x, 2
 76   s <- state grid, x, y
 77   {
 78     compare s, 0/false
 79     break-if-=
 80     result <- increment
 81   }
 82   # row below: zig
 83   increment y
 84   s <- state grid, x, y
 85   {
 86     compare s, 0/false
 87     break-if-=
 88     result <- increment
 89   }
 90   increment x
 91   s <- state grid, x, y
 92   {
 93     compare s, 0/false
 94     break-if-=
 95     result <- increment
 96   }
 97   increment x
 98   s <- state grid, x, y
 99   {
100     compare s, 0/false
101     break-if-=
102     result <- increment
103   }
104   return result
105 }
106 
107 fn step old-grid: (addr array boolean), new-grid: (addr array boolean) {
108   var y/ecx: int <- copy 0
109   {
110     compare y, 0xc0/height
111     break-if->=
112     var x/edx: int <- copy 0
113     {
114       compare x, 0x100/width
115       break-if->=
116       var n/eax: int <- num-live-neighbors old-grid, x, y
117       # if neighbors < 2, die of loneliness
118       {
119         compare n, 2
120         break-if->=
121         set-state new-grid, x, y, 0/dead
122       }
123       # if neighbors > 3, die of overcrowding
124       {
125         compare n, 3
126         break-if-<=
127         set-state new-grid, x, y, 0/dead
128       }
129       # if neighbors = 2, preserve state
130       {
131         compare n, 2
132         break-if-!=
133         var old-state/eax: boolean <- state old-grid, x, y
134         set-state new-grid, x, y, old-state
135       }
136       # if neighbors = 3, cell quickens to life
137       {
138         compare n, 3
139         break-if-!=
140         set-state new-grid, x, y, 1/live
141       }
142       x <- increment
143       loop
144     }
145     y <- increment
146     loop
147   }
148 }
149 
150 # color a square of size 'side' starting at x*side, y*side
151 fn render-square _x: int, _y: int, color: int {
152   var y/edx: int <- copy _y
153   y <- shift-left 2/log2side
154   var side/ebx: int <- copy 1
155   side <- shift-left 2/log2side
156   var ymax/ecx: int <- copy y
157   ymax <- add side
158   {
159     compare y, ymax
160     break-if->=
161     {
162       var x/eax: int <- copy _x
163       x <- shift-left 2/log2side
164       var xmax/ecx: int <- copy x
165       xmax <- add side
166       {
167         compare x, xmax
168         break-if->=
169         pixel-on-real-screen x, y, color
170         x <- increment
171         loop
172       }
173     }
174     y <- increment
175     loop
176   }
177 }
178 
179 fn render grid: (addr array boolean) {
180   var y/ecx: int <- copy 0
181   {
182     compare y, 0xc0/height
183     break-if->=
184     var x/edx: int <- copy 0
185     {
186       compare x, 0x100/width
187       break-if->=
188       var state/eax: boolean <- state grid, x, y
189       compare state, 0/false
190       {
191         break-if-=
192         render-square x, y, 3/cyan
193       }
194       compare state, 0/false
195       {
196         break-if-!=
197         render-square x, y, 0/black
198       }
199       x <- increment
200       loop
201     }
202     y <- increment
203     loop
204   }
205 }
206 
207 fn life {
208 #?   # allocate on the stack
209 #?   var grid1-storage: (array boolean 0xc000)  # width * height
210 #?   var grid1/esi: (addr array boolean) <- address grid1-storage
211 #?   var grid2-storage: (array boolean 0xc000)  # width * height
212 #?   var grid2/edi: (addr array boolean) <- address grid2-storage
213   # allocate on the heap
214   var grid1-storage: (handle array boolean)
215   var grid1-ah/eax: (addr handle array boolean) <- address grid1-storage
216   populate grid1-ah, 0xc000  # width * height
217   var _grid1/eax: (addr array boolean) <- lookup *grid1-ah
218   var grid1/esi: (addr array boolean) <- copy _grid1
219   var grid2-storage: (handle array boolean)
220   var grid2-ah/eax: (addr handle array boolean) <- address grid2-storage
221   populate grid2-ah, 0xc000  # width * height
222   var _grid2/eax: (addr array boolean) <- lookup *grid2-ah
223   var grid2/edi: (addr array boolean) <- copy _grid2
224   # initialize grid1
225   set-state grid1, 0x80, 0x5f, 1/live
226   set-state grid1, 0x81, 0x5f, 1/live
227   set-state grid1, 0x7f, 0x60, 1/live
228   set-state grid1, 0x80, 0x60, 1/live
229   set-state grid1, 0x80, 0x61, 1/live
230   # render grid1
231   render grid1
232   {
233     var key/eax: byte <- read-key 0/keyboard
234     compare key, 0
235 #?     loop-if-=  # press key to step
236     break-if-!=  # press key to quit  # comment this out to run under bochs; I'm not sure why there's a newline in the keyboard buffer
237     # iter: grid1 -> grid2
238     step grid1, grid2
239     render grid2
240     # iter: grid2 -> grid1
241     step grid2, grid1
242     render grid1
243     loop
244   }
245 }