wok-tiny view bootlife/stuff/life.asm @ rev 187

Add bootlife (again)
author Pascal Bellard <pascal.bellard@slitaz.org>
date Sun Feb 04 18:58:39 2024 +0000 (7 months ago)
parents 34a0a4406539
children
line source
1 bits 16
3 %define ResetScreen +15 bytes
4 %define StepCounter +29 bytes
6 cpu 8086
7 org 0x7c00
9 ;; Constants
11 ;; Boot sector size in bytes
12 %assign BootSector.Size 512
14 ;; Words in 16 bit x86 are 2 bytes
15 %assign WordSize 2
17 %assign DigitsNumber 6
19 ;; This is the value to store in segment register to access the MDA and the VGA text buffer.
20 ;; In 16 bit x86, segmented memory accesses are of the form:
21 ;;
22 ;; (segment register) * 0x10 + (offset register)
23 ;;
24 ;; The MDA text buffer is at 0xb00000, so if 0xb000 is stored in a segment
25 ;; register, then memory access instructions will be relative to the MDA text
26 ;; buffer, allowing easier access. For example, trying to access the nth byte of
27 ;; memory will *actually* access the nth byte of the text buffer.
28 %assign TextBuf.Seg 0xb000
29 %assign Vga.TextBuf.Offset 0x8000
31 %assign BitMap 0x8000
33 ;; Dimensions of text buffer
34 %assign TextBuf.Width 80
35 %assign TextBuf.Height 25
36 %assign TextBuf.Size (TextBuf.Width * TextBuf.Height)
38 ;; Macro to get the index of a text buffer cell from coordinates
39 %define TextBuf.Index(y, x) ((y) * TextBuf.Width * 2 + (x) * 2)
41 %assign Pixel.UpDown 0xdb
42 %assign Pixel.Up 0xdf
43 %assign Pixel.Down 0xdc
44 %assign Pixel.Free 0x20
45 %assign Cursor.Up 0x1e
46 %assign Cursor.Down 0x1f
48 ;; Keyboard scan codes
49 ;; http://www.delorie.com/djgpp/doc/rbinter/it/06/0.html
50 %assign Key.ScanCode.Tab 0x0f
51 %assign Key.ScanCode.Space 0x39
52 %assign Key.ScanCode.Up 0x48
53 %assign Key.ScanCode.Down 0x50
54 %assign Key.ScanCode.Left 0x4b
55 %assign Key.ScanCode.Right 0x4d
56 %assign Key.ScanCode.Enter 0x1c
57 %assign Key.ScanCode.QuitGame 0x01
59 ;; BootLife is supported as both a DOS game and a boot sector game :)
61 ;; Entry point: set up graphics and run game
62 BootLife:
63 cld
64 sti ;allow interrupts
66 %ifdef ResetScreen
67 ; MDA/VGA text mode 0x03
68 ; 80x25 text resolution
69 mov ax, 3
70 int 0x10
72 ; Disable VGA text mode cursor
73 ; https://wiki.osdev.org/Text_Mode_Cursor#Disabling_the_Cursor
74 mov ah, 0x01
75 mov ch, 0x3f
76 int 0x10
77 %endif
79 ;; Run game (the game is restarted by jumping here)
80 RunGame:
81 push cs
82 pop ss
83 mov sp, TextBuf.Seg
85 ; Load VGA text buffer segment into segment registers
86 mov es, sp
87 push cs
88 pop ds
90 ;;
91 tick:
92 call get_data
93 data:
94 %define INITDATA_COLS 36
95 %define INITDATA_POS 24
96 %define INITDATA_OFFSET TextBuf.Width*5+10
97 datainit:
98 db 0x01
99 cursor: db 0x00
100 xpos: db 0x00,0x00,0x14
101 ypos: db 0x00
102 db 0x00,0x30,0x30
103 cmd: db 0x00,0x0c , 0x80,0x08,0x03,0xc0
104 db 0x03,0x04,0x31,0x00,0x30 , 0x40,0x34,0x14
105 cursor_pos:
106 db 0x00
107 db 0x00,0x04,0x01,0x01,0x00 , 0x80,0x08,0x00,0x00
108 db 0x00,0x30
109 datainit_end:
110 ;012345670123456701234567012345670123
111 ; X ;
112 ; X X ;
113 ; XX XX XX;
114 ; X X XX XX;
115 ;XX X X XX ;
116 ;XX X X XX X X ;
117 ; X X X ;
118 ; X X ;
119 ; XX ;
120 ;awk 'BEGIN { n = split("0x00,0x00...0x00", t,",")
121 ; i=1; x=strtonum(t[i]); b=8; c=36
122 ; while (i <= n) {
123 ; if (and(x,1) == 0) printf " "; else printf "X"
124 ; x=rshift(x,1)
125 ; if (--c == 0) { printf "\n" ; c=36 ; }
126 ; if (--b == 0) { x=strtonum(t[++i]) ; b=8 ; }
127 ; }
128 ; printf "\n"
129 ;}' < /dev/null
132 get_data:
133 pop bp
134 %define BP(x) [bp + x - data]
135 ZeroBuf:
136 mov dx, INITDATA_COLS-INITDATA_POS
137 mov cx, TextBuf.Size*2+DigitsNumber+1
138 mov bx, BitMap
139 .1:
140 mov byte [bx], dh
141 inc bx
142 loop .1
144 mov bx, BitMap+INITDATA_POS+INITDATA_OFFSET
145 mov si, bp
146 mov cl, datainit_end-datainit
147 .next_byte:
148 lodsb
149 mov ah,8
150 .next_bit:
151 shr al,1
152 adc byte [bx], dh
153 inc bx
154 dec dx
155 jnz .noln
156 mov dl, INITDATA_COLS
157 add bx, TextBuf.Width-INITDATA_COLS
158 .noln:
159 dec ah
160 jnz .next_bit
161 loop .next_byte
163 ;; Main loop to process key presses and update state
164 GameLoop:
165 ;; Display board
166 xor di, di
167 mov si, TextBuf.Width
168 mov bx, BitMap
169 mov dx, TextBuf.Height + 256
170 push bx
171 .line:
172 mov cx, si
173 .next:
174 mov ax, Pixel.Free + 256*Pixel.Up
175 test byte [bx + si], dh
176 jz .NotDown
177 mov ax, Pixel.Down + 256*Pixel.UpDown
178 .NotDown:
179 test byte [bx], dh
180 jz .NotUp
181 mov al, ah
182 .NotUp:
183 inc bx
184 call PutChar
185 loop .next
186 add bx, si
187 dec dl
188 jnz .line
189 pop bx
191 call PutCursor
193 %ifdef StepCounter
194 mov si, BitMap+TextBuf.Size*2+DigitsNumber
195 .next_digit:
196 mov byte [si], ch
197 dec si
198 inc byte [si]
199 cmp byte [si], 10
200 je .next_digit
201 mov cl, DigitsNumber
202 mov si, BitMap+TextBuf.Size*2
203 mov di, 6
204 .digit:
205 lodsb
206 or al, '0'
207 call PutChar
208 loop .digit
209 %endif
211 cmp byte BP(cmd), Key.ScanCode.Enter
212 jnz wait_key
214 mov ah,0x01 ; Any key pressed?
215 int 0x16
216 jz nokey ; No, go to main loop
217 wait_key:
218 mov ah,0x00
219 int 0x16 ; Get key
220 mov al, ah
221 mov byte BP(cmd), al
222 dec ah
223 jne notesc ; No, jump
224 %ifdef ResetScreen
225 mov al,3
226 int 0x10
227 %endif
228 int 0x20 ; Exit to DOS or to oblivion (boot sector)
229 int 0x19
231 notesc:
232 mov cx, BP(ypos)
233 mov di, BP(xpos)
235 cmp al, Key.ScanCode.Tab
236 jne not_tab
237 call GetTextBufIndex
238 xor byte [bx + si], 1
239 GoGameLoop:
240 jmp GameLoop
242 PutCursor:
243 xchg ax, dx
244 xchg al, BP(cursor)
245 mov di, BP(cursor_pos)
246 cmp al, 0
247 je Ret
248 PutChar:
249 mov [es:di + Vga.TextBuf.Offset], al
250 stosb
251 inc di
252 Ret: ret
254 not_tab:
255 CmpUp:
256 ; Move cursor up
257 dec cx
258 cmp al, Key.ScanCode.Up
259 je WrapCursor
260 inc cx
261 CmpDown:
262 ; Move cursor down
263 inc cx
264 cmp al, Key.ScanCode.Down
265 je WrapCursor
266 dec cx
267 CmpLeft:
268 ; Move cursor left
269 dec di
270 cmp al, Key.ScanCode.Left
271 je WrapCursor
272 inc di
273 CmpRight:
274 ; Move cursor right
275 inc di
276 cmp al, Key.ScanCode.Right
277 jne nokey
278 WrapCursor:
279 call GetTextBufIndex
280 mov BP(ypos), cx
281 mov BP(xpos), di
282 mov al, Cursor.Up
283 shr cx, 1
284 adc al, ch ; Cursor.Down = Cursor.Up + 1
285 add cx, cx
286 call GetTextBufIndex
287 add si, di
288 mov BP(cursor_pos), si
289 mov BP(cursor), al
290 GoGameLoop2:
291 jmp GoGameLoop
293 wait_tick:
294 hlt ; Wait for clock interrupt
295 nokey:
296 mov ah, 0x00 ; Use base clock tick
297 int 0x1a
298 cmp dx, BP(tick)
299 jz wait_tick
300 mov BP(tick), dx
302 ;; Compute next board
303 mov cx, TextBuf.Height*2-1
304 .loop_y:
305 mov di, TextBuf.Width-1
306 .loop_x:
307 call CountCells ; si = &TextBuf[cx = y][di = x]
308 sub al, 3 ; empty cell with 3 neighbors or full cell with 2 neighbors
309 je .alive
310 sub al, [bx + si] ; full cell with 3 neighbors
311 jne .dead
312 .alive:
313 or byte [bx + si], 2
314 .dead:
315 dec di
316 jns .loop_x
317 dec cx
318 jns .loop_y
320 mov cx, TextBuf.Size*2
321 .loop_shr:
322 shr byte [bx], 1
323 inc bx
324 loop .loop_shr
326 jmp GoGameLoop2
328 ; Count adjacent cells
329 CountCells:
330 xor ax, ax
332 ; Count left-row
333 dec di
334 call UpAndCountRow
336 ; Count center-row
337 call CountNextRow
339 ; Count right-row
340 call CountNextRow
342 ; re-center
343 dec cx
344 dec di
346 ;; Compute the text buffer index from y and x coordinates
347 ;;
348 ;; si = &TextBuf[cx = y][di = x]
349 ;;
350 ;; This computes the equivalent of the TextBuf.Index(y, x) macro, but at runtime
351 ;;
352 ;; Parameters:
353 ;; * cx - y coordinate
354 ;; * di - x coordinate
355 ;; Returns:
356 ;; * si - text buffer index
357 GetTextBufIndex:
358 ; Base case: bounds check y
359 mov si, TextBuf.Width
360 cmp cx, TextBuf.Height*2
361 jb .check_x
362 sub cx, TextBuf.Height*2 ; overflow ?
363 jns .check_x
364 add cx, TextBuf.Height*4 ; underflow !
365 .check_x:
366 ; Base case: bounds check x
367 cmp di, si
368 jb .check_ok
369 sub di, si
370 jns .check_ok
371 add di, TextBuf.Width*2
372 .check_ok:
373 xchg ax, si
374 imul cl
375 xchg ax, si
376 add si, di
377 ret
379 CountNextRow:
380 inc di
381 dec cx
382 UpAndCountRow:
383 dec cx
384 CountRow:
385 call Count
386 call CountDown
387 CountDown:
388 inc cx
389 Count:
390 ; si = &TextBuf[cx = y][di = x]
391 call GetTextBufIndex
392 %if 0
393 cmp [bx + si], ah
394 %else
395 test byte [bx + si], 1
396 %endif
397 je .Ret
398 inc ax
399 .Ret: ret
401 ;; Print program size at build time
402 %assign CodeSize $ - $$
403 %assign Size $ - BootLife
404 %warning Code is Size bytes
406 CodeEnd:
407 ; Pad to size of boot sector, minus the size of a word for the boot sector
408 ; magic value. If the code is too big to fit in a boot sector, the `times`
409 ; directive uses a negative value, causing a build error.
410 times (BootSector.Size - WordSize) - CodeSize db 0
412 ; Boot sector magic
413 dw 0xaa55