mu/apps/life.mu

266 lines
5.9 KiB
Forth

# Conway's Game of Life
#
# To build:
# $ ./translate apps/life.mu
# To run:
# $ qemu-system-i386 code.img
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, 0x80/width
{
break-if-<
return 0/false
}
compare y, 0x60/height
{
break-if-<
return 0/false
}
var idx/eax: int <- copy y
idx <- shift-left 7/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 7/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, 0x60/height
break-if->=
var x/edx: int <- copy 0
{
compare x, 0x80/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 3/log2side
var side/ebx: int <- copy 1
side <- shift-left 3/log2side
var ymax/ecx: int <- copy y
ymax <- add side
{
compare y, ymax
break-if->=
{
var x/eax: int <- copy _x
x <- shift-left 3/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, 0/black
}
compare state, 0/false
{
break-if-!=
render-square x, y, 3/cyan
}
x <- increment
loop
}
y <- increment
loop
}
}
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
#? # 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, 0x3000 # 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, 0x3000 # width * height
var _grid2/eax: (addr array boolean) <- lookup *grid2-ah
var grid2/edi: (addr array boolean) <- copy _grid2
# initialize grid1
set-state grid1, 0x40, 0x2f, 1/live
set-state grid1, 0x41, 0x2f, 1/live
set-state grid1, 0x3f, 0x30, 1/live
set-state grid1, 0x40, 0x30, 1/live
set-state grid1, 0x40, 0x31, 1/live
# render grid1
render grid1
{
var key/eax: byte <- read-key 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
#? linger
# iter: grid2 -> grid1
step grid2, grid1
render grid1
#? linger
loop
}
}
fn linger {
var i/ecx: int <- copy 0
{
compare i, 0x10000000 # Kartik's Linux with -accel kvm
#? compare i, 0x8000000 # Kartik's Mac with -accel tcg
break-if->=
i <- increment
loop
}
}