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

wirehaze git hosting

658abe719824f5cc7a97dc2af2ec281ec469289c
[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)
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 mov ah, 0x00 ; get
56 int 0x1a ; system time
57 mov [xss], dx ; cx:dx = number of clock ticks since midnight
58
59 ; initialize currvgapg
60 mov word [currvgapg], VGAPGSZ
61
62 ; start simulation from random state ------------------------------------------
63
64 start:
65
66 call init_grid
67
68 ; apply game of life's rules to determine the next state ----------------------
69
70 next_state:
71
72 call flip_vga_page ; display current state
73 call delay ; sleep a little
74
75 call write_next_vga_page ; write the next state to the hidden page
76
77 jmp next_state
78
79 ; =============================================================================
80
81 ; functions -------------------------------------------------------------------
82
83 ; delay() - suspend program execution temporarily -----------------------------
84
85 ; clobbers ah, cx, dx
86
87 delay:
88
89 mov cx, WAIT_DELAY ; cx:dx = interval in microseconds
90 mov dx, 0
91
92 mov ah, 0x86
93 int 0x15 ; wait
94
95 ret
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, 0x3da ; input status #1 register
104
105 .wait_on:
106 in al, dx
107 test al, 0x08 ; vertical retrace bit
108 jnz .wait_on
109
110 .wait_off:
111 in al, dx
112 test al, 0x08
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 call vsync_wait
176
177 xor word [currvgapg], VGAPGSZ ; flip currvgapg
178 setnz al ; al = !(currvgapg == 0)
179
180 mov ah, 0x05 ; select active display page
181 int 0x10 ; video services
182
183 ret
184
185 ; alive_neighbours() ----------------------------------------------------------
186
187 ; input:
188 ; es:si -> current grid
189 ; cx -> cell index
190
191 ; output:
192 ; ax = # alive neighbours
193
194 ; clobbers ax, bx, dx
195
196 alive_neighbours:
197 enter 5, 0
198
199 ; [bp - 1]: row
200 ; [bp - 2]: col
201 ; [bp - 3]: neighbours
202 ; [bp - 4]: i
203 ; [bp - 5]: j
204
205 mov ax, cx ; ax = idx
206 mov bl, COLS ; bl = COLS
207 div bl ; al = idx / COLS ; ah = idx % COLS
208
209 mov [bp - 1], al ; row = idx / COLS
210 mov [bp - 2], ah ; row = idx % COLS
211 mov byte [bp - 3], 0 ; neighbours = 0
212
213 mov byte [bp - 4], -1 ; i = -1
214 .i: ; do {
215
216 mov byte [bp - 5], -1 ; j = -1
217 .j: ; do {
218
219 mov al, [bp - 4]
220 or al, [bp - 5]
221 jz .continue ; if (!i && !j) continue
222
223 mov al, [bp - 1]
224 add al, [bp - 4] ; al = row + i
225
226 cmp al, 0
227 jl .continue ; if (row + i < 0) continue
228
229 cmp al, ROWS
230 jge .continue ; if (row + i >= ROWS) continue
231
232 mov ah, [bp - 2]
233 add ah, [bp - 5] ; ah = col + j
234
235 cmp ah, 0
236 jl .continue ; if (col + j < 0) continue
237
238 cmp ah, COLS
239 jge .continue ; if (col + j >= COLS) continue
240
241 movzx bx, ah ; bx = col + j
242
243 mov ah, COLS
244 mul ah ; ax = (row + i) * COLS
245
246 add bx, ax ; bx = cell index
247 shl bx, 1 ; bx = grid offset
248
249 mov dx, [es:si + bx] ; dl = cell state
250
251 test dl, ALIVE ; if (!ALIVE) continue
252 jne .continue
253
254 inc byte [bp - 3] ; neighbours++
255
256 .continue:
257 inc byte [bp - 5]
258 cmp byte [bp - 5], 1
259 jle .j ; } while (++j <= 1)
260
261 inc byte [bp - 4]
262 cmp byte [bp - 4], 1
263 jle .i ; } while (++i <= 1)
264
265 movzx ax, byte [bp - 3] ; ax = # alive neighbours
266
267 leave
268 ret
269
270 ; write_next_cell_state() -----------------------------------------------------
271
272 ; input:
273 ; es:si -> current grid
274 ; es:di -> next grid
275 ; cx -> cell index
276
277 ; output:
278 ; [es:di + cx * 2] = updated cell state
279
280 ; clobbers ax, bx, dx
281
282 write_next_cell_state:
283
284 call alive_neighbours ; ax = # alive neighbours
285
286 mov bx, cx
287 shl bx, 1
288
289 mov dx, [es:si + bx] ; dl = cell current state
290
291 test dl, ALIVE ; if (ALIVE) {
292 jne .else
293
294 test ax, 2 ; if (n < 2)
295 jl .dead ; return DEAD
296
297 test ax, 3 ; if (n > 3)
298 jg .dead ; return DEAD
299
300 jmp .alive ; return ALIVE
301
302 .else: ; }
303
304 test ax, 3 ; if (n != 3)
305 jne .dead ; return DEAD
306
307 .alive: ; return ALIVE
308 mov dl, ALIVE
309 jmp .write
310
311 .dead:
312 mov dl, DEAD
313
314 .write:
315 mov [es:di + bx], dx
316
317 ret
318
319 ; write_next_vga_page() -------------------------------------------------------
320
321 ; clobbers di, si, ax, bx, cx, dx
322
323 write_next_vga_page:
324
325 mov si, [currvgapg] ; es:si -> current page
326
327 mov di, VGAPGSZ
328 xor di, si ; es:di -> next page
329
330 xor cx, cx ; i = 0
331
332 .update_loop:
333 call write_next_cell_state
334
335 inc cx
336 test cx, COLS * ROWS
337 jl .update_loop ; for i in [0 .. COLS * ROWS - 1]
338
339 ret
340
341 ; halt() - stop program execution ---------------------------------------------
342
343 halt:
344
345 cli ; disable interrupts
346 hlt
347 jmp halt
348
349 ; =============================================================================
350
351 times 510 - ($ - $$) db 0 ; fill remaining bytes with zeroes
352 dw 0xaa55 ; mbr magic byte