fixed the speed in vertical motion of lovebug
[bootboot] / bb.asm
1 ; org: all instructions from now on start in 7c00h
2 ; 7c00h is where the bios loads the 1st sector
3 ; assembler formatting:
4 ; column 0: labels
5 ; tab: commands
6         org 7c00h
7 ; at boot: real mode (like it's 8086)
8 ; we have to tell assembler that code is 16bit mode
9         bits 16
10
11 ; macros
12 %macro SETPAL 4 ; 4 = num arguments of macro (index rgb)
13         mov dx, 3c8h    ; index register
14         mov al, %1
15         out dx, al
16         inc dx
17         mov al, %2 >> 2
18         out dx, al
19         mov al, %3 >> 2
20         out dx, al
21         mov al, %4 >> 2 ; shift to give the value from 0 - 255
22         out dx, al
23 %endmacro
24
25 start:
26         mov sp, 7c00h
27
28 ; initialize segment registers
29         mov ax, 0
30         mov ds, ax
31         mov es, ax
32         mov ss, ax
33
34 ; save dl value
35 ; bios sets in dl the num of the drive that
36 ; loaded you so if you overwrite dl you can reuse it
37         mov [saved_drive_num], dl
38
39 ; service 0: set videomode ah
40 ; param: which video mode (13h: 320x200, 256 colors) al
41 ; ax: ah (high 8 bits), al (low)
42 ; call 0 = set_videomode it expects the video mode in al
43 ; ah : 0, al: 13, ax:ahal
44         mov ax, 13h
45
46 ; calling video bios
47 ; software interrupt 10h
48 ; what's the value of ah? al=video mode when ah=0
49 ; which set which video mode
50         int 10h
51         mov ax, 3
52         call clearscreen
53
54 ; load 2nd sector from boot device and jump to it
55 ; bios helpers, 13h = disk io
56         mov ax, 0
57         mov ds, ax
58         mov ah, 02h                             ; call 2: read sectors into memory
59         mov al, 3                                       ; number of sectors to read
60         mov ch, 0                                       ; low 8 bits of cylinder number
61         mov cl, 2                                       ; sector number that starts from 1
62         mov dh, 0                                       ; head number
63         mov dl, [saved_drive_num]       ; 8bits
64         mov bx, sector_2
65         int 13h
66 ; error check: if carry flag isn't set jump to loaded code
67         jnc     sector_2
68 .inf_loop:
69         jmp .inf_loop
70
71
72 clearscreen:
73 ; video ram is mapped in a0000
74 ; first 64000 bytes appear immediately on screen
75 ; mem addresses in real mode have 2 parts: segment and offset
76 ; bits overlap: segment is shifted by 4 and is added to the
77 ; overlapping offset (20 bits number = x86's addressable space = 1MB)
78 ; segment register for the segment: es, ds, cs, ss (and: fs, gs)
79 ; default register = ds (data segment), es = extra segment
80 ; cs = code segment cs:ip (it points where to read instructions and is
81 ; automatically set to 7c0 (7c00h)
82 ; offset can be paired with any register
83 ; pair the registers
84         push ax
85         mov ax, 0a000h
86         mov ds, ax
87         mov di, 0
88         pop ax
89 ; counter cx
90         mov cx, 64000
91
92 .loop_begin:
93 ; dereferrence[] address
94         mov [di], al
95         inc     di
96         dec cx
97 ; when cx is 0, 0 flag is set
98         jnz .loop_begin
99         ret
100
101 saved_drive_num:
102         db 0 ; define byte 0
103
104 ; assembler trick: write as many 0 needed to fill 510 bytes
105 ; $ <- means here
106         times 510-($-$$) db 0
107         dw 0aa55h
108
109 sector_2:
110 ; disp palette
111         mov ax, 0a000h  ; video segment points to video memory
112         mov ds, ax
113         mov bx, 0               ; video offset
114         mov cx, 0               ; y
115 .y_loop:
116         mov dx, 0               ; x
117 .x_loop:
118         mov ax, dx
119         test ax, 0ff00h ; lower 8 bits 0, highest 1, Z flag is set while < 256, test = and but doesnt change ax 
120         jz      .skip_clear_ax
121         mov al, 0
122 .skip_clear_ax:
123         mov [bx], al    ; pixel written on screen
124         inc bx                  ; increment
125         inc dx
126         cmp dx, 320
127         jnz .x_loop
128         inc cx
129         cmp cx, 200
130         jnz .y_loop
131
132 ; setup grayscale palette (64 first colors because ramdac uses 6 bits / color so 2^6)
133 ; ram dac (digital to analog converter) tis vga
134 ; in a ^ register which index we want to write
135 ; 3 writes in another ramdac register (data)
136 ; ramdac feature: when you need to set a palette
137         mov al, 0               ; first index
138         mov dx, 3c8h    ; ramdac index registe
139         out dx, al              ; because io port (address) > 255
140         inc dx                  ; ramdac data register: 3c9h
141 .pal_loop:
142         out dx, al              ; r
143         out dx, al              ; g
144         out dx, al              ; b
145         inc al
146         test al, 3fh    ; test with 3fh to keep the lowest 6 bits of al
147         jnz .pal_loop
148
149 ; setup lovebug palette
150         SETPAL 64, 0, 255, 0    ; chr(64) = @
151         SETPAL 65, 0, 0, 0              ; A
152         SETPAL 66, 50, 50, 50   ; B
153         SETPAL 67, 57, 4, 4             ; C
154         SETPAL 68, 108, 9, 9    ; D
155         SETPAL 69, 164, 4, 4    ; E
156
157 ; disable all interrupts (for bios to not read the keyboard)
158         cli
159
160         ; ds on 0 because rand uses it for accesses to mem
161         xor ax, ax
162         mov ds, ax
163         mov ax, backbuffer
164         shr ax, 4 ; to use it as segment! (shift right)
165         mov es, ax
166 main_loop:
167 ; draw to back buffer
168         xor bx, bx              ; mov bx, 0 to clear the register
169 .static_loop:
170         call rand
171         and ax, 3fh             ; last six bits of eax (see rand)
172         mov [es:bx], al    ; [] -> bx offset and default segment ds we change it to es
173         inc bx
174         cmp bx, 64000   ; num pixels
175         jnz .static_loop
176
177         ; draw lovebug
178         mov di, (200 - 32) * 320 + 160 - 16
179         mov ax, [num_frames]
180         shr ax, 2               ; /4
181         cmp ax, 200 - 32
182         jz .end
183         mov bx, ax
184         shl ax, 8
185         shl bx, 6
186         add ax, bx
187         sub di, ax
188         call rand
189         and ax, 3               ; random value in 0, 15
190         sub ax, 1               ; random value in -3, 12
191         add di, ax
192         mov si, lovebug
193         call blit32
194
195 ; wait for vblank
196 .vsync_blank:
197         mov dx, 3dah
198         in al, dx
199         and al, 8
200         jnz     .vsync_blank
201 .vsync_visible:
202         in al, dx
203         and al, 8               ; the 4th bit is 1 when we are in the vertical blanking period
204         jz .vsync_visible
205
206 ; copy backbuffer to video memory
207         push es
208         push ds
209         mov ax, es
210         mov ds, ax                      ; ds points to backbuffer now (was in es)
211         xor si, si
212         mov ax, 0a000h
213         mov es, ax
214         xor di, di                      ; write in es:di
215         mov ecx, 16000          ; 16000 double words (dwords) = 64000 bytes
216         rep movsd
217         pop ds
218         pop es
219
220         inc word [num_frames]
221 ;   moved above:
222 ;       cmp word [num_frames], 200 - 32
223 ;       jz .end
224
225         in al, 64h              ; 60h = keyb data port, 64h = keyb status port
226         and al, 1               ; 1 = OUTBUF_FULL = the keyb controller out buf is full
227         jz main_loop    ; no key pressed, loop back
228         in al, 60h              ; reads the keyb that was pressed to reset the flag
229
230 .end:
231 ; re-enable all interrupts
232         sti
233
234 ; text mode
235         mov ax, 3               ; text mode
236         int 10h
237 ; use bios to print
238 ; Int 10/AH=0Eh
239         mov si, str_frames  
240         call print_str
241         mov ax, [num_frames]
242         call print_num
243         mov si, str_newline
244         call print_str
245 .inf_loop:
246         jmp .inf_loop
247
248 ; reads from ds:si writes to es:di (bug top left corner)
249 ; knows how many pixels on screen and how many in the lovebug
250 blit32:
251         mov cx, 1024    ; 32 x 32 lovebug
252 .loop:
253         mov al, [ds:si]
254         cmp al, 64
255         jz .skip_pixel
256         mov [es:di], al
257 .skip_pixel:
258         inc si
259         inc di
260
261         dec cx                          ; dec the num_pixels we want to write 
262         jz      .end                    ; all pixels have been written
263         test cx, 1fh            ; keep 5 least significant bits, 3 most significant bits 0
264         jnz .loop                       ; when this is 0 we are in a multiple of 32
265         add di, 320 - 32        ; screen width - bug width
266         jmp .loop
267 .end:
268         ret
269
270 print_str:
271         ;Int 10/AH=0Eh
272         mov al, [si]
273         test al, al
274         jz .end
275         mov ah, 0eh
276         xor bx, bx
277         int 10h
278         inc si
279         jmp print_str
280 .end:
281         ret
282
283 print_num:
284         ; converts digits to ascii - print
285         push word 0
286         test ax, ax
287         jnz .conv_loop
288         push word '0'
289         jmp .conv_end
290 .conv_loop:
291         test ax, ax
292         jz .conv_end
293         xor dx, dx
294         mov cx, 10
295         div cx  ; ax contains cx / 10
296         add dl, '0'
297         push dx
298         jmp .conv_loop
299 .conv_end:
300         mov bp, sp
301         mov ax, [ss:bp]
302         test ax, ax
303         jz .print_end
304         mov ah, 0eh
305         xor bx, bx
306         int 10h
307         add sp, 2
308         jmp .conv_end
309 .print_end:
310         add sp, 2
311         ret
312
313 ; random number generator
314 ; by nuclear
315 rand:
316         mov eax, [randval]
317         mul dword [randmul]
318         add eax, 12345
319         and eax, 0x7fffffff
320         mov [randval], eax
321         shr eax, 16
322         ret
323
324 randmul dd 1103515245
325 randval dd 0ace1h
326
327 num_frames dw 0
328 str_frames db 'frames: ', 0
329 str_newline db 13, 10, 0        ; crlf carriage return line feed newline
330
331 ; db to write pixels transparent 255
332 ; @: transparent
333 ; A: black
334 ; B: gray
335 ; C: dark red
336 ; D: medium red
337 ; E: red
338 lovebug:
339         db '@@@@BAAAB@@@@@BAAB@@@@@BAAAB@@@@'
340         db '@@@@@@@@AAAB@BAAAAB@BAAA@@@@@@@@'
341         db '@@@@@@@@@@@AAAAAAAAAA@@@@@@@@@@@'
342         db '@@@@@@@@@@@@AAAAAAAA@@@@@@@@@@@@'
343         db '@@@@@@@@@@@@AAAAAAAA@@@@@@@@@@@@'
344         db '@@@@@@@@@@@@BAAAAAAB@@@@@@@@@@@@'
345         db '@@@@@@@@@@@@@AAACAA@@@@@@@@@@@@@'
346         db '@@@@@@@@@@CDDEECDEEDDC@@@@@@@@@@'
347         db '@@@@@@@CDDEEEEEDCEEEEEDDC@@@@@@@'
348         db '@@@@@CDEEEEDCDECDEDCDEEEEDC@@@@@'
349         db '@@@@CDDCDEECACEDCECACEEDCDDC@@@@'
350         db '@@@CDECACEEDCDECDEDCDEECACEDC@@@'
351         db '@@@DEEDCDEEEEEEDCEEEEEEDCDEED@@@'
352         db '@@CEEEEEEEEEEEDCDDEEEEEEEEEEEC@@'
353         db '@@DEDCDEEEDCDECACCEDCDEEEDCDED@@'
354         db '@@EECACEEECACEDCDDECACEEECACEE@@'
355         db '@CEEDCDEEEDCDEEDCEEDCDEEEDCDEEC@'
356         db '@DEEEEEEEEEEEEDCCDEEEEEEEEEEEED@'
357         db 'CEEEEEEDCDEEEDCBACDEEEDCDEEEEEEC'
358         db 'CEDCDEECACEEDCAABACDEECACEEDCDEC'
359         db 'DECACEEDCDEDCAABAAACDEDCDEECACED'
360         db 'DEDCDEEEEEDCAAAABAAACDEEEEEDCDED'
361         db 'CEEEEEEEEECAAAABAAAAACEEEEEEEEEC'
362         db 'CEEEEEEEEDAAAAAABAAAAADEEEEEEEEC'
363         db '@DEDCDEEDCAAAAABAAAAAACDEEDCDED@'
364         db '@DECACEDCAAAAAAABAAAAAACDECACED@'
365         db '@DEDCDDCAAAAAAABAAAAAAAACDDCDED@'
366         db '@CEEEECAAAAAAAAABAAAAAAAACEEEEC@'
367         db '@@EEED@AAAAAAAABAAAAAAAAA@DEEE@@'
368         db '@@EEDC@@AAAAAAAABAAAAAAA@@CDEE@@'
369         db '@@DEC@@@@@AAAAABAAAAAA@@@@@CED@@'
370         db '@@@C@@@@@@@@AAAABAAA@@@@@@@@C@@@'
371
372         align   16      ; if the address is not a multiple of 16 add some 0 to become one (because we'll use backbuffer as a segment)
373 backbuffer: