about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--shell/global.mu13
-rw-r--r--shell/life.mu245
2 files changed, 258 insertions, 0 deletions
diff --git a/shell/global.mu b/shell/global.mu
index c3df463e..9bc2aff4 100644
--- a/shell/global.mu
+++ b/shell/global.mu
@@ -48,7 +48,9 @@ fn initialize-globals _self: (addr global-table) {
   # for streams
   append-primitive self, "stream"
   append-primitive self, "write"
+  # misc
   append-primitive self, "abort"
+  append-primitive self, "life"
   # keep sync'd with render-primitives
 }
 
@@ -595,6 +597,13 @@ fn apply-primitive _f: (addr cell), args-ah: (addr handle cell), out: (addr hand
     apply-abort args-ah, out, trace
     return
   }
+  {
+    var life?/eax: boolean <- string-equal? f-name, "life"
+    compare life?, 0/false
+    break-if-=
+    apply-life args-ah, out, trace
+    return
+  }
   abort "unknown primitive function"
 }
 
@@ -1636,6 +1645,10 @@ fn apply-abort _args-ah: (addr handle cell), out: (addr handle cell), trace: (ad
   abort "aa"
 }
 
+fn apply-life _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
+  life
+}
+
 fn apply-columns _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
   trace-text trace, "eval", "apply columns"
   var args-ah/eax: (addr handle cell) <- copy _args-ah
diff --git a/shell/life.mu b/shell/life.mu
new file mode 100644
index 00000000..e4a2dbf7
--- /dev/null
+++ b/shell/life.mu
@@ -0,0 +1,245 @@
+fn state _grid: (addr array boolean), x: int, y: int -> _/eax: boolean {
+  # clip at the edge
+  compare x, 0
+  {
+    break-if->=
+    return 0/false
+  }
+  compare y, 0
+  {
+    break-if->=
+    return 0/false
+  }
+  compare x, 0x100/width
+  {
+    break-if-<
+    return 0/false
+  }
+  compare y, 0xc0/height
+  {
+    break-if-<
+    return 0/false
+  }
+  var idx/eax: int <- copy y
+  idx <- shift-left 8/log2width
+  idx <- add x
+  var grid/esi: (addr array boolean) <- copy _grid
+  var result/eax: (addr boolean) <- index grid, idx
+  return *result
+}
+
+fn set-state _grid: (addr array boolean), x: int, y: int, val: boolean {
+  # don't bother checking bounds
+  var idx/eax: int <- copy y
+  idx <- shift-left 8/log2width
+  idx <- add x
+  var grid/esi: (addr array boolean) <- copy _grid
+  var result/eax: (addr boolean) <- index grid, idx
+  var src/ecx: boolean <- copy val
+  copy-to *result, src
+}
+
+fn num-live-neighbors grid: (addr array boolean), x: int, y: int -> _/eax: int {
+  var result/edi: int <- copy 0
+  # row above: zig
+  decrement y
+  decrement x
+  var s/eax: boolean <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  increment x
+  s <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  increment x
+  s <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  # curr row: zag
+  increment y
+  s <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  subtract-from x, 2
+  s <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  # row below: zig
+  increment y
+  s <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  increment x
+  s <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  increment x
+  s <- state grid, x, y
+  {
+    compare s, 0/false
+    break-if-=
+    result <- increment
+  }
+  return result
+}
+
+fn step old-grid: (addr array boolean), new-grid: (addr array boolean) {
+  var y/ecx: int <- copy 0
+  {
+    compare y, 0xc0/height
+    break-if->=
+    var x/edx: int <- copy 0
+    {
+      compare x, 0x100/width
+      break-if->=
+      var n/eax: int <- num-live-neighbors old-grid, x, y
+      # if neighbors < 2, die of loneliness
+      {
+        compare n, 2
+        break-if->=
+        set-state new-grid, x, y, 0/dead
+      }
+      # if neighbors > 3, die of overcrowding
+      {
+        compare n, 3
+        break-if-<=
+        set-state new-grid, x, y, 0/dead
+      }
+      # if neighbors = 2, preserve state
+      {
+        compare n, 2
+        break-if-!=
+        var old-state/eax: boolean <- state old-grid, x, y
+        set-state new-grid, x, y, old-state
+      }
+      # if neighbors = 3, cell quickens to life
+      {
+        compare n, 3
+        break-if-!=
+        set-state new-grid, x, y, 1/live
+      }
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+}
+
+# color a square of size 'side' starting at x*side, y*side
+fn render-square _x: int, _y: int, color: int {
+  var y/edx: int <- copy _y
+  y <- shift-left 2/log2side
+  var side/ebx: int <- copy 1
+  side <- shift-left 2/log2side
+  var ymax/ecx: int <- copy y
+  ymax <- add side
+  {
+    compare y, ymax
+    break-if->=
+    {
+      var x/eax: int <- copy _x
+      x <- shift-left 2/log2side
+      var xmax/ecx: int <- copy x
+      xmax <- add side
+      {
+        compare x, xmax
+        break-if->=
+        pixel-on-real-screen x, y, color
+        x <- increment
+        loop
+      }
+    }
+    y <- increment
+    loop
+  }
+}
+
+fn render grid: (addr array boolean) {
+  var y/ecx: int <- copy 0
+  {
+    compare y, 0xc0/height
+    break-if->=
+    var x/edx: int <- copy 0
+    {
+      compare x, 0x100/width
+      break-if->=
+      var state/eax: boolean <- state grid, x, y
+      compare state, 0/false
+      {
+        break-if-=
+        render-square x, y, 3/cyan
+      }
+      compare state, 0/false
+      {
+        break-if-!=
+        render-square x, y, 0/black
+      }
+      x <- increment
+      loop
+    }
+    y <- increment
+    loop
+  }
+}
+
+fn life {
+#?   # allocate on the stack
+#?   var grid1-storage: (array boolean 0xc000)  # width * height
+#?   var grid1/esi: (addr array boolean) <- address grid1-storage
+#?   var grid2-storage: (array boolean 0xc000)  # width * height
+#?   var grid2/edi: (addr array boolean) <- address grid2-storage
+  # allocate on the heap
+  var grid1-storage: (handle array boolean)
+  var grid1-ah/eax: (addr handle array boolean) <- address grid1-storage
+  populate grid1-ah, 0xc000  # width * height
+  var _grid1/eax: (addr array boolean) <- lookup *grid1-ah
+  var grid1/esi: (addr array boolean) <- copy _grid1
+  var grid2-storage: (handle array boolean)
+  var grid2-ah/eax: (addr handle array boolean) <- address grid2-storage
+  populate grid2-ah, 0xc000  # width * height
+  var _grid2/eax: (addr array boolean) <- lookup *grid2-ah
+  var grid2/edi: (addr array boolean) <- copy _grid2
+  # initialize grid1
+  set-state grid1, 0x80, 0x5f, 1/live
+  set-state grid1, 0x81, 0x5f, 1/live
+  set-state grid1, 0x7f, 0x60, 1/live
+  set-state grid1, 0x80, 0x60, 1/live
+  set-state grid1, 0x80, 0x61, 1/live
+  # render grid1
+  render grid1
+  {
+    var key/eax: byte <- read-key 0/keyboard
+    compare key, 0
+#?     loop-if-=  # press key to step
+    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
+    # iter: grid1 -> grid2
+    step grid1, grid2
+    render grid2
+    # iter: grid2 -> grid1
+    step grid2, grid1
+    render grid1
+    loop
+  }
+}