Skip to content

Commit

Permalink
Conway's Game of Life
Browse files Browse the repository at this point in the history
Pick a seed lasting over 1200 generations.

Keep state as two bits in a 64*16 buffer instead of the obvious way (as
characters in two 80x40 buffers). This will permit us to fit in the
available SRAM for the current HDL implementation on Spartan6.

This currently requires a separate object (linked last) to contain the
buffer, in order not to relocate code too far away for a type{0,1,2}
instruction to access. We should be able to rewrite accesses to use
type3 for data, but we can't control what other codes are going to do,
so we should trap the too-large relocation offsets in the assembler.
This remains to be done.
  • Loading branch information
kulp committed Mar 9, 2015
1 parent a698f5c commit 2c07dcd
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 1 deletion.
4 changes: 3 additions & 1 deletion ex/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ vpath %.tas.cpp $(TOP)/lib $(TOP)/lib/dword

TARGETS = \
bsearch_demo.texe \
bm_conway.texe \
bm_snake.texe \
bm_mults.texe \
compare.texe \
Expand Down Expand Up @@ -47,10 +48,11 @@ bsearch_demo.texe: puts.to
qsort_demo.texe: qsort.to puts.to memcpy.to
irc.texe: puts.to strcmp.to udiv.to umod.to strtol.to
parse_hex.texe: strtol.to
bm_snake.texe: rand.to
bm_snake.texe bm_conway.texe: rand.to
bm_fib.texe bm_mults.texe: putnum.to
clock.texe: sleep.to
trailz_demo.texe: puts.to trailz.to
bm_conway.texe: bm_conway_buffer.to

$(patsubst %.tas.cpp,%.tas,$(CPP_FILES)): %.tas: $(TOP)/lib/common.th

187 changes: 187 additions & 0 deletions ex/bm_conway.tas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#include "common.th"
#include "vga.th"

#define THRESHOLD 64

// The operations later only work if INSET_{ROW,COL}S end up to be a power of 2
#define OFFSET_ROWS 4
#define OFFSET_COLS 8

#define INSET_ROWS (ROWS - (OFFSET_ROWS * 2))
#define INSET_COLS (COLS - (OFFSET_COLS * 2))

_start:
bare_metal_init() // TODO this shouldn't be necessary
prologue

call(init_display)
call(disable_cursor)

// m is generation
m <- 0

// i is global video base
i <- VGA_BASE
// g is global flip buffer
g <- rel(databuf)
// h is global flip buffer bit index
h <- 0

c <- i
d <- 0xa // in our font, a 50% greyscale
call(blank_area)

call(randomise_board)
call(flip)

forever:
m -> [0x100]

call(compute)
call(flip)

m <- m + 1
goto(forever)

blank_area:
pushall(j,n)
j <- COLS
j <- j * ROWS
blank_area_loop:
j <- j - 1
d -> [c + j]
n <- j > 0
jnzrel(n,blank_area_loop)
popall_ret(j,n)

randomise_board:
pushall(j,k,c,d,l,n)
c <- [rel(seed)]
call(srand)
j <- (INSET_ROWS - 1)
randomise_board_rows:
k <- (INSET_COLS - 1)
randomise_board_cols:
call(rand)
b <- b >> 23

l <- b >= THRESHOLD

b <- j * INSET_COLS + k
l -> [b + g] // write to buf0

k <- k - 1
n <- k >= 0
jnzrel(n,randomise_board_cols)
j <- j - 1
n <- j >= 0
jnzrel(n,randomise_board_rows)
popall_ret(j,k,c,d,l,n)

flip:
pushall(j,k,n)
j <- (INSET_ROWS - 1)
flip_rows:
k <- (INSET_COLS - 1)
flip_cols:
b <- j * INSET_COLS + k
b <- [b + g] // read from buf0

b <- b @ h

n <- 'o' & b
b <- ' ' &~ b
n <- b | n
c <- j
d <- k
call(get_real_offset)
n -> [b + i] // write to display

k <- k - 1
n <- k >= 0
jnzrel(n,flip_cols)
j <- j - 1
n <- j >= 0
jnzrel(n,flip_rows)

h <- h ^ 1
popall_ret(j,k,n)

compute:
pushall(c,d,e,f,j,k,l)
j <- (INSET_ROWS - 1)
compute_rows:
k <- (INSET_COLS - 1)
compute_cols:

c <- j
d <- k
call(get_neighbour_count)
e <- b

b <- j * INSET_COLS + k
f <- [b + g] // read from buf1
// E is neighbour count, F is current state
c <- h ^ 1
n <- f @ c
n <- - n
// N is 1 if alive
e <- e | n // tricky
e <- e == 3
n <- 1 << h
e <- e & n
f <- f &~ n
f <- f | e
f -> [b + g] // write to buf0

k <- k - 1
n <- k >= 0
jnzrel(n,compute_cols)
j <- j - 1
n <- j >= 0
jnzrel(n,compute_rows)
popall_ret(c,d,e,f,j,k,l)

get_neighbour_count:
pushall(e,f,j,k,l,m,n)
m <- 0
j <- c
k <- d

e <- -1
neighbour_loop_rows:
f <- -1
neighbour_loop_cols:
// essentially doing `(a + 31) % 32` to get `a - 1` safely
c <- j + e + INSET_ROWS ; c <- c & (INSET_ROWS - 1)
d <- k + f + INSET_COLS ; d <- d & (INSET_COLS - 1)

b <- c * INSET_COLS + d
l <- [b + g]
n <- e | f
n <- n == 0
// skip case where e == f == 0 (self)

c <- h ^ 1
l <- l @ c ; l <- l &~ n

m <- m - l

f <- f + 1
n <- f <= 1
jnzrel(n,neighbour_loop_cols)
e <- e + 1
n <- e <= 1
jnzrel(n,neighbour_loop_rows)

b <- m
popall_ret(e,f,j,k,l,m,n)

get_real_offset:
b <- c + OFFSET_ROWS
b <- b * COLS + d
b <- b + OFFSET_COLS
ret

seed: .word 0x99999999

13 changes: 13 additions & 0 deletions ex/bm_conway_buffer.tas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "vga.th"

#define THRESHOLD 64

// The operations later only work if INSET_{ROW,COL}S end up to be a power of 2
#define OFFSET_ROWS 4
#define OFFSET_COLS 8

#define INSET_ROWS (ROWS - (OFFSET_ROWS * 2))
#define INSET_COLS (COLS - (OFFSET_COLS * 2))

.global databuf
databuf: .zero (INSET_ROWS * INSET_COLS)
3 changes: 3 additions & 0 deletions lib/vga.th
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#define ROWS 40
#define COLS 80
#define VGA_BASE 0x10000

0 comments on commit 2c07dcd

Please sign in to comment.