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

wirehaze git hosting

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