You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

426 lines
8.0 KiB
Plaintext

(module
(memory (export "shared_memory") 1)
(global $boardWidth (mut i32) (i32.const 0))
(global $boardHeight (mut i32) (i32.const 0))
(global $boardBufferLength (mut i32) (i32.const 0))
(global $currentBuffer (mut i32) (i32.const -1))
(func (export "initializeBoard") (param $width i32) (param $height i32)
;; Store width and height for later
(global.set $boardWidth (local.get $width))
(global.set $boardHeight (local.get $height))
;; Compute total cells per board
local.get $width
local.get $height
i32.mul
global.set $boardBufferLength
;; Request enough memory for both boards
global.get $boardBufferLength
i32.const 2
i32.mul
call $growMemoryForBoards
;; Set current board
(global.set $currentBuffer (i32.const 0))
)
(func (export "getPagesUsed") (result i32)
memory.size
)
(func $growMemoryForBoards (param $totalBytes i32)
(local $targetPages i32)
;; figure out target page size
local.get $totalBytes
i32.const 1
i32.sub
i32.const 65536 ;; size of a page
i32.div_u
i32.const 1
i32.add
;; get difference
memory.size
i32.sub
;; grow
memory.grow
drop ;; ignore result, we're gonna crash anyways
;; perhaps we should have a way to report errors back to JS next time
)
(func $getBoardPtr (export "getBoardPointer") (result i32)
global.get $currentBuffer
global.get $boardBufferLength
i32.mul
)
(func (export "getBoardLength") (result i32)
global.get $boardBufferLength
)
(func $swapBoards
global.get $currentBuffer
i32.eqz
global.set $currentBuffer
)
(func $positionOutOfRange (param $position i32) (param $max i32) (result i32)
local.get $position
i32.const 0
i32.lt_s
local.get $position
local.get $max
i32.ge_s
i32.or
)
(func $getIndexForPosition (param $row i32) (param $column i32) (result i32)
(local $result i32)
local.get $row
global.get $boardHeight
call $positionOutOfRange
local.get $column
global.get $boardWidth
call $positionOutOfRange
i32.and
if
i32.const -1
return
end
global.get $boardWidth
local.get $row
i32.mul
local.get $column
i32.add
)
(func $getValueAtPosition (export "getValueAtPosition") (param $row i32) (param $column i32) (result i32)
(local $position i32)
local.get $row
local.get $column
call $getIndexForPosition
local.tee $position
i32.const 0
i32.lt_s
if
i32.const 0
return
end
local.get $position
call $getBoardPtr
i32.add
i32.load8_u
)
(func $setValueAtPosition (export "setValueAtPosition") (param $row i32) (param $column i32) (param $value i32)
(local $position i32)
local.get $row
local.get $column
call $getIndexForPosition
local.tee $position
i32.const 0
i32.lt_s
if
return
end
local.get $position
call $getBoardPtr
i32.add
local.get $value
i32.store8
)
(func $setNewValueAtPosition (param $row i32) (param $column i32) (param $value i32)
(local $position i32)
local.get $row
local.get $column
call $getIndexForPosition
local.tee $position
i32.const 0
i32.lt_s
if
return
end
global.get $boardBufferLength
call $getBoardPtr
i32.xor
local.get $position
i32.add
local.get $value
i32.store8
)
(func $getNewValueAtPosition (param $row i32) (param $column i32) (result i32)
(local $count i32)
(local $current i32)
local.get $row
i32.const 1
i32.lt_u
local.get $row
global.get $boardHeight
i32.const 1
i32.sub
i32.ge_u
local.get $column
i32.const 1
i32.lt_u
local.get $column
global.get $boardWidth
i32.const 1
i32.sub
i32.ge_u
;; if any of the boundary conditions are true
i32.or
i32.or
i32.or
if (result i32 i32)
local.get $row
local.get $column
call $getNeighborCountBoundary
else
local.get $row
local.get $column
call $getNeighborCountCenter
end
call $getOutcomeFromCountAndCurrent
)
(func $getNeighborCountBoundary (param $row i32) (param $column i32) (result i32 i32)
local.get $row
i32.const 1
i32.sub
local.get $column
call $getValueAtPosition
local.get $row
i32.const 1
i32.add
local.get $column
call $getValueAtPosition
local.get $row
local.get $column
i32.const 1
i32.sub
call $getValueAtPosition
local.get $row
local.get $column
i32.const 1
i32.add
call $getValueAtPosition
local.get $row
i32.const 1
i32.sub
local.get $column
i32.const 1
i32.sub
call $getValueAtPosition
local.get $row
i32.const 1
i32.add
local.get $column
i32.const 1
i32.sub
call $getValueAtPosition
local.get $row
i32.const 1
i32.sub
local.get $column
i32.const 1
i32.add
call $getValueAtPosition
local.get $row
i32.const 1
i32.add
local.get $column
i32.const 1
i32.add
call $getValueAtPosition
;; sum neightbor count
i32.add
i32.add
i32.add
i32.add
i32.add
i32.add
i32.add
;; get current
local.get $row
local.get $column
call $getValueAtPosition
)
(func $getNeighborCountCenter (param $row i32) (param $column i32) (result i32 i32) ;; neighborCount, currentValue
(local $origIndex i32)
(local $rowAbove i32)
(local $rowCenter i32)
(local $rowBelow i32)
;; store current cell's memory position
call $getBoardPtr
local.get $row
local.get $column
call $getIndexForPosition
i32.add
local.tee $origIndex
;; load the three rows
i32.const 1
i32.sub
i32.load
local.set $rowCenter
local.get $origIndex
i32.const 1
i32.sub
global.get $boardWidth
i32.sub
i32.load
local.set $rowAbove
local.get $origIndex
i32.const 1
i32.sub
global.get $boardWidth
i32.add
i32.load
local.set $rowBelow
;; count the number of alive neighbors in each row
local.get $rowAbove
i32.const 0x00_01_01_01
i32.and
i32.popcnt
local.get $rowCenter
i32.const 0x00_01_00_01
i32.and
i32.popcnt
local.get $rowBelow
i32.const 0x00_01_01_01
i32.and
i32.popcnt
;; sum neighbor count
i32.add
i32.add
;; get current
local.get $rowCenter
i32.const 0x00_00_01_00
i32.and
i32.popcnt
)
(func $getOutcomeFromCountAndCurrent (param $neighborCount i32) (param $current i32) (result i32)
;; get sentinal value for current count
i32.const 1
local.get $neighborCount
i32.shl
;; get value mask for current state
i32.const 0xC ;;alive mask (1100, 3 or 2 neighbors)
i32.const 0x8 ;;dead mask (1000, exactly three neighbors)
local.get $current
select
;; mask out to see if we still have a bit
i32.and
i32.popcnt
)
(func $tick (export "tick")
(local $row i32)
(local $column i32)
(local $value i32)
i32.const 0
local.set $row
loop $rows
;; start at the beginning of a row
i32.const 0
local.set $column
;; for every column in the row
loop $columns
;; compute new value
local.get $row
local.get $column
call $getNewValueAtPosition
local.set $value
;; place in next board
local.get $row
local.get $column
local.get $value
call $setNewValueAtPosition
;; increment column
local.get $column
i32.const 1
i32.add
local.tee $column
;; loop back if less than width
global.get $boardWidth
i32.lt_s
br_if $columns
end
;;increment row
local.get $row
i32.const 1
i32.add
local.tee $row
;; loop back if less than height
global.get $boardHeight
i32.lt_s
br_if $rows
end
;; swap to the new board
call $swapBoards
)
)