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

wirehaze git hosting

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