aboutsummaryrefslogtreecommitdiff
path: root/examples/life.s
blob: 50ce2c7c5e82ffe0d423ae3eb70aae100c08035f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
; Conway's Game of Life
; Renders a 64x64 field by writing characters
; on a 16x8 grid

ADD PC, 1
:randseed  
dat 0xACE1 ; change to get different initial states

; Initialize the screen
SET [0x8280], 0x4 ; red border color

; Set screen to the appropriate characters
SET A, 0xf000 ; white fg | black bg | char #

SET I, 0x8000
:loop_init
SET X, I
AND X, 0xf
SET Y, I
SHR Y, 1
AND Y, 0x70
BOR X, Y
BOR X, A
SET [I], X
ADD I, 1
IFN I, 0x8180
  SET PC, loop_init

; the internal grid is actually 66x66, to not
; have to check if an access is out-of-bounds
; (we set the border to do toroidal wrap-around)
:randomize_grid  ; set a random initial state
SET SP, 0x2105
:randomize_loop
JSR rand
AND A, 1
SET PUSH, A
IFN 0x0fff, SP
  SET PC, randomize_loop

; The core loop iterates over cells in a block pattern 
; it calculates 2x8 groups at a time, since that's
; the dimensions of one word of a character font

; C -- address of current field (since it's double-buffered)
; A, B -- coordinates inside current group
; X, Y -- coordinates of the current cell
; Z -- number of live neighbors
; SP -- address of last half-character we modified
; I -- top-left neighbor index
; J -- current half-character bitmap
;       we modify a character by doing SET PUSH, J

SET C, 0x1000 ; the live/dead cells are stored at 0x1000 and 0x3000

:loop_main
; copy cells to let us do toroidal wrap-around.
; we have an MxN matrix, and need to copy the 
; rows and columns to the opposite edges, and also
; do the corners properly

; Copy the M-1th row to the 1st row.
SET SP, C ; source
ADD SP, 0x1081 ; 66 * 64 + 1
SET I, C  ; target
SET X, I
ADD X, 65 ; the last element we write
:toroid_row_zero
ADD I, 1
SET [I], POP
IFN I, X
  SET PC, toroid_row_zero

; Copy the 2nd row to the Mth row.
SET SP, C ; source
ADD SP, 67
SET I, C  ; target
ADD I, 0x10c2 ; 66 * 65
SET X, I
ADD X, 65 ; the last element we write
:toroid_row_last
ADD I, 1
SET [I], POP
IFN I, X
  SET PC, toroid_row_last

; Do the columns.
SET I, C  ; left
SET J, C  ; right
ADD J, 64
SET X, I
ADD X, 0x1080 ; end address (X) (66 * 64)
SET A, 66   ; increment amount
:toroid_columns
ADD I, A
ADD J, A
SET [I], [J]
SET [J+1], [I+1]
IFN I, X
  SET PC, toroid_columns

; Do the corners.
SET [C], [C+0x10c0] ; (0,0) = (64,64)
SET [C+65], [C+0x1081] ; (65, 0) = (1, 64)
SET [C+0x10c2], [C+0x82] ; (0, 65) = (64, 1)
SET [C+0x1103], [C+67] ; (65, 65) = (1, 1)

SET X, 62 ; cell coords
SET Y, 56
SET SP, 0x8280 ; half-character address
:loop_group
SET A, 0
SET J, 0
  :loop_a
  SET B, 8

  SET I, Y ; I = (Y+A)*66 + (X+B) + C (index of top-left neighbor)
  BOR I, 7 ; hoisted out of the inner loop
  MUL I, 66
  ADD I, X
  ADD I, A
  ADD I, C

    :loop_b
    SUB B, 1

    ; count how many neighbors we have
    SET Z, [I]      ; -1, -1
    ADD Z, [I+0x1]  ;  0, -1
    ADD Z, [I+0x2]  ;  1, -1
    ADD Z, [I+0x42] ; -1,  0
    ADD Z, [I+0x44] ;  1,  0
    ADD Z, [I+0x84] ; -1,  1
    ADD Z, [I+0x85] ;  0,  1
    ADD Z, [I+0x86] ;  1,  1

    ; trick: cell is alive if (neighbors | alive) == 3
    BOR Z, [I+0x43]
    IFN Z, 3
      SET Z, 0
    AND Z, 1

    SHL J, 1
    IFE Z, 1
      XOR J, 1 ; set the font display

    XOR I, 0x4000 ; set the cell in the opposite page
    SET [I+0x43], Z
    XOR I, 0x4000

    SUB I, 66

    IFN B, 0
      SET PC, loop_b
  ADD A, 1
  IFN A, 2
    SET PC, loop_a
SET PUSH, J
SUB X, 2
IFN O, 0
  SET X, 62
IFN O, 0
  SUB Y, 8
IFG SP, 0x8180  ; have we written the last character?
  SET PC, loop_group

XOR C, 0x4000
SET PC, loop_main

:rand ; simple LFSR RNG -- only use the low bit!
  SET A, randseed
  SHR [A], 1
  IFN O, 0
     XOR [A], 0xB400
  SET A, [A]
  SET PC, POP