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

wirehaze git hosting

jae
[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 this # iterations
12
13 ; constants -------------------------------------------------------------------
14
15 %define COLS 80
16 %define ROWS 25
17 %define VGAPGSZ 1000h ; (bytes)
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 entry:
36
37 ; initialize segment registers
38 mov ax, VGA >> 4
39 mov es, ax ; es:0 -> video memory
40
41 xor ax, ax
42 mov ds, ax ; ds = 0
43 mov ss, ax ; ss = 0
44
45 ; set stack pointers
46 mov bp, ORG
47 mov sp, bp
48
49 ; clear direction flag
50 cld
51
52 ; disable cursor
53 mov ch, 3fh ; cursor start and options
54 mov ah, 01h ; set text-mode cursor shape
55 int 10h ; bios video services
56
57 ; initialize xss
58 .set_xss:
59 mov ah, 00h ; get system time
60 int 1ah ; bios time services
61 ; cx:dx = # clock ticks since midnight
62 or dx, dx
63 jz .set_xss ; wait for something meaningful
64
65 mov [xss], dx
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], 0
76
77 .next_state:
78
79 call vsync_wait
80 call update_grid
81
82 call vsync_wait
83 call flip_vgapg
84
85 inc word [iter]
86 cmp word [iter], ITER_LIM
87 jl .next_state
88
89 jmp start ; reset
90
91 ; =============================================================================
92
93 ; functions -------------------------------------------------------------------
94
95 ; vsync_wait() - wait for display to enter the next retrace cycle -------------
96
97 ; clobbers al, dx
98
99 vsync_wait:
100
101 mov dx, 3dah ; input status #1 register
102
103 .wait_retrace_end:
104 in al, dx
105 test al, 08h ; vertical retrace bit
106 jnz .wait_retrace_end
107
108 .wait_retrace_start:
109 in al, dx
110 test al, 08h
111 jz .wait_retrace_start
112
113 ret
114
115 ; xs() - xorshift pseudorandom number generator -------------------------------
116
117 ; output:
118 ; [xss] = updated state
119 ; bx = [xss]
120
121 ; clobbers bx, dx
122
123 xs:
124
125 mov bx, [xss]
126 mov dx, bx
127
128 shl dx, 1 ; dx = xss << 1
129 xor bx, dx ; bx = xss ^ (xss << 1)
130 mov dx, bx
131
132 shr dx, 3 ; dx = xss' >> 3
133 xor bx, dx ; bx = xss' ^ (xss' >> 3)
134 mov dx, bx
135
136 shl dx, 10 ; dx = xss'' << 10
137 xor bx, dx ; bx = xss'' ^ (xss'' << 10)
138 mov [xss], bx
139
140 ret
141
142 ; init_grid() - initialize grid cells randomly --------------------------------
143
144 ; clobbers ax, bx, cx, dx, di
145
146 init_grid:
147
148 mov ah, PRINT_COLOR ; ah = color attribute
149 mov di, [currvgapg] ; es:di -> grid start
150 mov cx, COLS * ROWS ; for each cell
151
152 .write_cell: ; do {
153 call xs ; bx = random value
154 mov al, DEAD ; al = DEAD
155
156 test bx, 0b11
157
158 jnz .nz ; if (bx % 4 == 0)
159 mov al, ALIVE ; al = ALIVE
160
161 .nz:
162 stosw ; [es:di] = ax, di += 2
163
164 loop .write_cell ; } while (--cx)
165
166 ret
167
168 ; flip_vgapg() - flip active display page -------------------------------------
169
170 ; output:
171 ; [currvgapg] ^= VGAPGSZ
172
173 ; clobbers ax
174
175 flip_vgapg:
176
177 xor word [currvgapg], VGAPGSZ
178
179 setnz al ; al = !(currvgapg == 0)
180 mov ah, 05h ; select active display page
181 int 10h ; bios video services
182
183 ret
184
185 ; alive_neighbours() - get number of alive adjacent cells ---------------------
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] ; if (!i && !j)
221 jz .continue ; continue
222
223 mov al, [bp - 1]
224 add al, [bp - 4] ; al = row + i
225
226 cmp al, ROWS ; if ((unsigned)(row + i) >= ROWS)
227 jae .continue ; continue
228
229 mov ah, [bp - 2]
230 add ah, [bp - 5] ; ah = col + j
231
232 cmp ah, COLS ; if ((unsigned)(col + j) >= COLS)
233 jae .continue ; continue
234
235 movzx bx, ah ; bx = col + j
236
237 mov ah, COLS
238 mul ah ; ax = (row + i) * COLS
239
240 add bx, ax ; bx = cell index
241 shl bx, 1 ; bx = grid offset
242
243 mov dx, [es:si + bx] ; dl = cell state
244
245 cmp dl, ALIVE ; if (!ALIVE)
246 jne .continue ; continue
247
248 inc byte [bp - 3] ; neighbours++
249
250 .continue:
251 inc byte [bp - 5]
252 cmp byte [bp - 5], 1
253 jle .j ; } while (++j <= 1)
254
255 inc byte [bp - 4]
256 cmp byte [bp - 4], 1
257 jle .i ; } while (++i <= 1)
258
259 movzx ax, byte [bp - 3] ; ax = # alive neighbours
260
261 leave
262 ret
263
264 ; update_cell() - write next cell state ---------------------------------------
265
266 ; input:
267 ; es:si -> current grid
268 ; es:di -> next grid
269 ; cx -> cell index
270
271 ; output:
272 ; [es:di + cx * 2] = updated cell state
273
274 ; clobbers ax, bx, dx
275
276 update_cell:
277
278 call alive_neighbours ; ax = # alive neighbours
279
280 mov bx, cx
281 shl bx, 1
282
283 mov dx, [es:si + bx] ; dl = cell current state
284
285 cmp dl, ALIVE ; if (ALIVE) {
286 jne .else
287
288 shr ax, 1
289 cmp ax, 1 ; if (n != 2 || n != 3)
290 jne .dead ; return DEAD
291
292 jmp .alive ; return ALIVE
293
294 .else: ; }
295
296 cmp ax, 3 ; if (n != 3)
297 jne .dead ; return DEAD
298
299 .alive: ; return ALIVE
300 mov dl, ALIVE
301 jmp .write
302
303 .dead:
304 mov dl, DEAD
305
306 .write:
307 mov [es:di + bx], dx
308
309 ret
310
311 ; update_grid() - write next grid (inactive vga page) -------------------------
312
313 ; clobbers di, si, ax, bx, cx, dx
314
315 update_grid:
316
317 mov si, [currvgapg] ; es:si -> current page
318
319 mov di, VGAPGSZ
320 xor di, si ; es:di -> next page
321
322 mov cx, COLS * ROWS - 1 ; for each cell (index)
323
324 .update_loop: ; do {
325
326 call update_cell
327
328 dec cx
329 jns .update_loop ; } while (--cx > 0)
330
331 ret
332
333 ; halt() - stop program execution ---------------------------------------------
334
335 halt:
336
337 cli ; disable interrupts
338 hlt
339 jmp halt
340
341 ; =============================================================================
342
343 times 510 - ($ - $$) db 0 ; fill remaining bytes with zeroes
344 dw 0aa55h ; mbr magic byte