Skip to content

Commit 2c07dcd

Browse files
committed
Conway's Game of Life
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.
1 parent a698f5c commit 2c07dcd

File tree

4 files changed

+206
-1
lines changed

4 files changed

+206
-1
lines changed

ex/Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ vpath %.tas.cpp $(TOP)/lib $(TOP)/lib/dword
1313

1414
TARGETS = \
1515
bsearch_demo.texe \
16+
bm_conway.texe \
1617
bm_snake.texe \
1718
bm_mults.texe \
1819
compare.texe \
@@ -47,10 +48,11 @@ bsearch_demo.texe: puts.to
4748
qsort_demo.texe: qsort.to puts.to memcpy.to
4849
irc.texe: puts.to strcmp.to udiv.to umod.to strtol.to
4950
parse_hex.texe: strtol.to
50-
bm_snake.texe: rand.to
51+
bm_snake.texe bm_conway.texe: rand.to
5152
bm_fib.texe bm_mults.texe: putnum.to
5253
clock.texe: sleep.to
5354
trailz_demo.texe: puts.to trailz.to
55+
bm_conway.texe: bm_conway_buffer.to
5456

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

ex/bm_conway.tas.cpp

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
#include "common.th"
2+
#include "vga.th"
3+
4+
#define THRESHOLD 64
5+
6+
// The operations later only work if INSET_{ROW,COL}S end up to be a power of 2
7+
#define OFFSET_ROWS 4
8+
#define OFFSET_COLS 8
9+
10+
#define INSET_ROWS (ROWS - (OFFSET_ROWS * 2))
11+
#define INSET_COLS (COLS - (OFFSET_COLS * 2))
12+
13+
_start:
14+
bare_metal_init() // TODO this shouldn't be necessary
15+
prologue
16+
17+
call(init_display)
18+
call(disable_cursor)
19+
20+
// m is generation
21+
m <- 0
22+
23+
// i is global video base
24+
i <- VGA_BASE
25+
// g is global flip buffer
26+
g <- rel(databuf)
27+
// h is global flip buffer bit index
28+
h <- 0
29+
30+
c <- i
31+
d <- 0xa // in our font, a 50% greyscale
32+
call(blank_area)
33+
34+
call(randomise_board)
35+
call(flip)
36+
37+
forever:
38+
m -> [0x100]
39+
40+
call(compute)
41+
call(flip)
42+
43+
m <- m + 1
44+
goto(forever)
45+
46+
blank_area:
47+
pushall(j,n)
48+
j <- COLS
49+
j <- j * ROWS
50+
blank_area_loop:
51+
j <- j - 1
52+
d -> [c + j]
53+
n <- j > 0
54+
jnzrel(n,blank_area_loop)
55+
popall_ret(j,n)
56+
57+
randomise_board:
58+
pushall(j,k,c,d,l,n)
59+
c <- [rel(seed)]
60+
call(srand)
61+
j <- (INSET_ROWS - 1)
62+
randomise_board_rows:
63+
k <- (INSET_COLS - 1)
64+
randomise_board_cols:
65+
call(rand)
66+
b <- b >> 23
67+
68+
l <- b >= THRESHOLD
69+
70+
b <- j * INSET_COLS + k
71+
l -> [b + g] // write to buf0
72+
73+
k <- k - 1
74+
n <- k >= 0
75+
jnzrel(n,randomise_board_cols)
76+
j <- j - 1
77+
n <- j >= 0
78+
jnzrel(n,randomise_board_rows)
79+
popall_ret(j,k,c,d,l,n)
80+
81+
flip:
82+
pushall(j,k,n)
83+
j <- (INSET_ROWS - 1)
84+
flip_rows:
85+
k <- (INSET_COLS - 1)
86+
flip_cols:
87+
b <- j * INSET_COLS + k
88+
b <- [b + g] // read from buf0
89+
90+
b <- b @ h
91+
92+
n <- 'o' & b
93+
b <- ' ' &~ b
94+
n <- b | n
95+
c <- j
96+
d <- k
97+
call(get_real_offset)
98+
n -> [b + i] // write to display
99+
100+
k <- k - 1
101+
n <- k >= 0
102+
jnzrel(n,flip_cols)
103+
j <- j - 1
104+
n <- j >= 0
105+
jnzrel(n,flip_rows)
106+
107+
h <- h ^ 1
108+
popall_ret(j,k,n)
109+
110+
compute:
111+
pushall(c,d,e,f,j,k,l)
112+
j <- (INSET_ROWS - 1)
113+
compute_rows:
114+
k <- (INSET_COLS - 1)
115+
compute_cols:
116+
117+
c <- j
118+
d <- k
119+
call(get_neighbour_count)
120+
e <- b
121+
122+
b <- j * INSET_COLS + k
123+
f <- [b + g] // read from buf1
124+
// E is neighbour count, F is current state
125+
c <- h ^ 1
126+
n <- f @ c
127+
n <- - n
128+
// N is 1 if alive
129+
e <- e | n // tricky
130+
e <- e == 3
131+
n <- 1 << h
132+
e <- e & n
133+
f <- f &~ n
134+
f <- f | e
135+
f -> [b + g] // write to buf0
136+
137+
k <- k - 1
138+
n <- k >= 0
139+
jnzrel(n,compute_cols)
140+
j <- j - 1
141+
n <- j >= 0
142+
jnzrel(n,compute_rows)
143+
popall_ret(c,d,e,f,j,k,l)
144+
145+
get_neighbour_count:
146+
pushall(e,f,j,k,l,m,n)
147+
m <- 0
148+
j <- c
149+
k <- d
150+
151+
e <- -1
152+
neighbour_loop_rows:
153+
f <- -1
154+
neighbour_loop_cols:
155+
// essentially doing `(a + 31) % 32` to get `a - 1` safely
156+
c <- j + e + INSET_ROWS ; c <- c & (INSET_ROWS - 1)
157+
d <- k + f + INSET_COLS ; d <- d & (INSET_COLS - 1)
158+
159+
b <- c * INSET_COLS + d
160+
l <- [b + g]
161+
n <- e | f
162+
n <- n == 0
163+
// skip case where e == f == 0 (self)
164+
165+
c <- h ^ 1
166+
l <- l @ c ; l <- l &~ n
167+
168+
m <- m - l
169+
170+
f <- f + 1
171+
n <- f <= 1
172+
jnzrel(n,neighbour_loop_cols)
173+
e <- e + 1
174+
n <- e <= 1
175+
jnzrel(n,neighbour_loop_rows)
176+
177+
b <- m
178+
popall_ret(e,f,j,k,l,m,n)
179+
180+
get_real_offset:
181+
b <- c + OFFSET_ROWS
182+
b <- b * COLS + d
183+
b <- b + OFFSET_COLS
184+
ret
185+
186+
seed: .word 0x99999999
187+

ex/bm_conway_buffer.tas.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "vga.th"
2+
3+
#define THRESHOLD 64
4+
5+
// The operations later only work if INSET_{ROW,COL}S end up to be a power of 2
6+
#define OFFSET_ROWS 4
7+
#define OFFSET_COLS 8
8+
9+
#define INSET_ROWS (ROWS - (OFFSET_ROWS * 2))
10+
#define INSET_COLS (COLS - (OFFSET_COLS * 2))
11+
12+
.global databuf
13+
databuf: .zero (INSET_ROWS * INSET_COLS)

lib/vga.th

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#define ROWS 40
2+
#define COLS 80
3+
#define VGA_BASE 0x10000

0 commit comments

Comments
 (0)