(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 $buffer0ptr (mut i32) (i32.const -1)) (global $buffer1ptr (mut i32) (i32.const -1)) (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 pointer locations for our two boards (global.set $buffer0ptr (i32.const 0)) (global.set $buffer1ptr (global.get $boardBufferLength)) ;; 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 (result i32) global.get $currentBuffer i32.eqz if (result i32) global.get $buffer0ptr else global.get $buffer1ptr end ) (func $swapBoards global.get $currentBuffer i32.eqz if (result i32) i32.const 1 else i32.const 0 end global.set $currentBuffer ) (func $positionInRange (param $position i32) (param $min i32) (param $max i32) (result i32) local.get $position local.get $min i32.lt_s if i32.const 0 return end local.get $position local.get $max i32.ge_s if i32.const 0 return end i32.const 1 return ) (func $getIndexForPosition (param $row i32) (param $column i32) (result i32) local.get $row i32.const 0 global.get $boardHeight call $positionInRange local.get $column i32.const 0 global.get $boardWidth call $positionInRange i32.and i32.eqz 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 $getNewValueAtPosition (param $row i32) (param $column i32) (result i32) (local $count 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 i32.add i32.add i32.add i32.add i32.add i32.add i32.add ;; Exactly 3 neighbors local.tee $count i32.const 3 i32.eq if ;; becomes or stays alive i32.const 1 return end ;; If currently dead local.get $row local.get $column call $getValueAtPosition i32.eqz if ;; Stay dead i32.const 0 return end ;; 2 neighbors local.get $count i32.const 2 i32.eq if i32.const 1 return end i32.const 0 return ) (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 call $swapBoards local.get $row local.get $column local.get $value call $setValueAtPosition call $swapBoards ;; 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 ) )