]> wirehaze git hosting - lifeboot.git/blob - lifeboot.asm

wirehaze git hosting

9b03696aa827eb2e63b7d2d3c609533fa85b1807
[lifeboot.git] / lifeboot.asm
1 bits 16
2 org 7c00h
3
4 ; =============================================================================
5
6 ; settings --------------------------------------------------------------------
7
8 %define DEAD ' ' ; char to represent a dead cell
9 %define ALIVE '.' ; char to represent an alive cell
10 %define PRINT_COLOR 07h ; grey on black
11 %define ITER_LIM 500 ; reset the simulation after ITER_LIM iterations
12
13 ; constants -------------------------------------------------------------------
14
15 %define COLS 80
16 %define ROWS 25
17 %define VGAPGSZ 1000h
18
19 ; memory layout ---------------------------------------------------------------
20
21 %define ORG 7c00h
22 %define VGA 0b8000h
23 %define DAT ORG + 512
24
25 ; static variables ------------------------------------------------------------
26
27 %define xss DAT ; xs() state (word)
28 %define currvgapg DAT + 2 ; current vga page near pointer (word)
29 %define iter DAT + 4 ; current iteration # (word)
30
31 ; =============================================================================
32
33 ; entry point -----------------------------------------------------------------
34
35 ; initialize segment registers
36 mov ax, VGA >> 4
37 mov es, ax
38
39 xor ax, ax
40 mov ds, ax
41 mov ss, ax
42
43 ; set stack pointers
44 mov bp, ORG
45 mov sp, bp
46
47 ; clear direction flag
48 cld
49
50 ; disable cursor
51 mov ch, 3fh ; cursor start and options
52 mov ah, 01h ; set text-mode cursor shape
53 int 10h ; video services
54
55 ; initialize xss
56
57 set_xss_seed:
58
59 mov ah, 00h ; get
60 int 1ah ; system time
61
62 cmp dx, 0
63 je set_xss_seed ; if seed is 0, xs wont work properly
64
65 mov [xss], dx ; cx:dx = number of clock ticks since midnight
66
67 ; initialize currvgapg
68 mov word [currvgapg], 0
69
70 ; start simulation from random state ------------------------------------------
71
72 start:
73
74 call init_grid
75 mov word [iter], 1
76
77 ; apply game of life's rules to determine the next state ----------------------
78
79 next_state:
80
81 call vsync_wait
82 call write_next_vga_page
83
84 call vsync_wait
85 call flip_vga_page
86
87 inc word [iter]
88 cmp word [iter], ITER_LIM
89 jle next_state
90
91 jmp start ; reset
92
93 ; =============================================================================
94
95 ; functions -------------------------------------------------------------------
96
97 ; vsync_wait() - wait for display to enter the next VBlank cycle --------------
98
99 ; clobbers ax, dx
100
101 vsync_wait:
102
103 mov dx, 3dah ; input status #1 register
104
105 .wait_on:
106 in al, dx
107 test al, 08h ; vertical retrace bit
108 jnz .wait_on
109
110 .wait_off:
111 in al, dx
112 test al, 08h
113 jz .wait_off
114
115 ret
116
117 ; xs() - xorshift pseudorandom number generator -------------------------------
118
119 ; output:
120 ; [xss] = updated state
121
122 ; clobbers bx, dx
123
124 xs:
125
126 mov bx, [xss]
127 mov dx, bx
128
129 shl dx, 1 ; dx = xss << 1
130 xor bx, dx ; bx = xss ^ (xss << 1)
131 mov dx, bx
132
133 shr dx, 3 ; dx = xss' >> 3
134 xor bx, dx ; bx = xss' ^ (xss' >> 3)
135 mov dx, bx
136
137 shl dx, 10 ; dx = xss'' << 10
138 xor bx, dx ; bx = xss'' ^ (xss'' << 10)
139 mov [xss], bx
140
141 ret
142
143 ; init_grid() - initialize grid cells randomly --------------------------------
144
145 ; clobbers ax, bx, cx, dx, di
146
147 init_grid:
148
149 mov ah, PRINT_COLOR ; ah = color attribute
150 mov di, [currvgapg] ; es:di -> grid start
151 mov cx, COLS * ROWS ; for each cell
152
153 .write_cell: ; do {
154 call xs ; [xss] = rand
155 mov al, DEAD ; al = DEAD (likely)
156
157 test word [xss], 0b11
158
159 jnz .nz ; if ([xss] % 4 == 0)
160 mov al, ALIVE ; al = ALIVE
161
162 .nz:
163 stosw ; [es:di] = ax ; di += 2
164
165 loop .write_cell ; } while (--cx)
166
167 ret
168
169 ; flip_vga_page() - flip active display page ----------------------------------
170
171 ; clobbers ax, dx
172
173 flip_vga_page:
174
175 xor word [currvgapg], VGAPGSZ ; flip currvgapg
176 setnz al ; al = !(currvgapg == 0)
177
178 mov ah, 05h ; select active display page
179 int 10h ; video services
180
181 ret
182
183 ; alive_neighbours() ----------------------------------------------------------
184
185 ; input:
186 ; es:si -> current grid
187 ; cx -> cell index
188
189 ; output:
190 ; ax = # alive neighbours
191
192 ; clobbers ax, bx, dx
193
194 alive_neighbours:
195 enter 5, 0
196
197 ; [bp - 1]: row
198 ; [bp - 2]: col
199 ; [bp - 3]: neighbours
200 ; [bp - 4]: i
201 ; [bp - 5]: j
202
203 mov ax, cx ; ax = idx
204 mov bl, COLS ; bl = COLS
205 div bl ; al = idx / COLS ; ah = idx % COLS
206
207 mov [bp - 1], al ; row = idx / COLS
208 mov [bp - 2], ah ; row = idx % COLS
209 mov byte [bp - 3], 0 ; neighbours = 0
210
211 mov byte [bp - 4], -1 ; i = -1
212 .i: ; do {
213
214 mov byte [bp - 5], -1 ; j = -1
215 .j: ; do {
216
217 mov al, [bp - 4]
218 or al, [bp - 5]
219 jz .continue ; if (!i && !j) continue
220
221 mov al, [bp - 1]
222 add al, [bp - 4] ; al = row + i
223
224 cmp al, 0
225 jl .continue ; if (row + i < 0) continue
226
227 cmp al, ROWS
228 jge .continue ; if (row + i >= ROWS) continue
229
230 mov ah, [bp - 2]
231 add ah, [bp - 5] ; ah = col + j
232
233 cmp ah, 0
234 jl .continue ; if (col + j < 0) continue
235
236 cmp ah, COLS
237 jge .continue ; if (col + j >= COLS) continue
238
239 movzx bx, ah ; bx = col + j
240
241 mov ah, COLS
242 mul ah ; ax = (row + i) * COLS
243
244 add bx, ax ; bx = cell index
245 shl bx, 1 ; bx = grid offset
246
247 mov dx, [es:si + bx] ; dl = cell state
248
249 cmp dl, ALIVE ; if (!ALIVE) continue
250 jne .continue
251
252 inc byte [bp - 3] ; neighbours++
253
254 .continue:
255 inc byte [bp - 5]
256 cmp byte [bp - 5], 1
257 jle .j ; } while (++j <= 1)
258
259 inc byte [bp - 4]
260 cmp byte [bp - 4], 1
261 jle .i ; } while (++i <= 1)
262
263 movzx ax, byte [bp - 3] ; ax = # alive neighbours
264
265 leave
266 ret
267
268 ; write_next_cell_state() -----------------------------------------------------
269
270 ; input:
271 ; es:si -> current grid
272 ; es:di -> next grid
273 ; cx -> cell index
274
275 ; output:
276 ; [es:di + cx * 2] = updated cell state
277
278 ; clobbers ax, bx, dx
279
280 write_next_cell_state:
281
282 call alive_neighbours ; ax = # alive neighbours
283
284 mov bx, cx
285 shl bx, 1
286
287 mov dx, [es:si + bx] ; dl = cell current state
288
289 cmp dl, ALIVE ; if (ALIVE) {
290 jne .else
291
292 cmp ax, 2 ; if (n < 2)
293 jl .dead ; return DEAD
294
295 cmp ax, 3 ; if (n > 3)
296 jg .dead ; return DEAD
297
298 jmp .alive ; return ALIVE
299
300 .else: ; }
301
302 cmp ax, 3 ; if (n != 3)
303 jne .dead ; return DEAD
304
305 .alive: ; return ALIVE
306 mov dl, ALIVE
307 jmp .write
308
309 .dead:
310 mov dl, DEAD
311
312 .write:
313 mov [es:di + bx], dx
314
315 ret
316
317 ; write_next_vga_page() -------------------------------------------------------
318
319 ; clobbers di, si, ax, bx, cx, dx
320
321 write_next_vga_page:
322
323 mov si, [currvgapg] ; es:si -> current page
324
325 mov di, VGAPGSZ
326 xor di, si ; es:di -> next page
327
328 xor cx, cx ; i = 0
329
330 .update_loop:
331 call write_next_cell_state
332
333 inc cx
334 cmp cx, COLS * ROWS
335 jl .update_loop ; for i in [0 .. COLS * ROWS - 1]
336
337 ret
338
339 ; halt() - stop program execution ---------------------------------------------
340
341 halt:
342
343 cli ; disable interrupts
344 hlt
345 jmp halt
346
347 ; =============================================================================
348
349 times 510 - ($ - $$) db 0 ; fill remaining bytes with zeroes
350 dw 0aa55h ; mbr magic byte