1 ; author Eleni Maria Stea <elene.mst@gmail.com>
2 ; my x86 assembly helloworld :)
4 ; org: all instructions from now on start in 7c00h
5 ; 7c00h is where the bios loads the 1st sector
6 ; assembler formatting:
10 ; at boot: real mode (like it's 8086)
11 ; we have to tell assembler that code is 16bit mode
15 %macro SETPAL 4 ; 4 = num arguments of macro (index rgb)
16 mov dx, 3c8h ; index register
24 mov al, %4 >> 2 ; shift to give the value from 0 - 255
31 ; start of BPB at offset 3
32 db "BSPL 0.1" ; 03h: OEM ident, 8 bytes
33 dw 512 ; 0bh: bytes per sector
34 db 1 ; 0dh: sectors per cluster
35 dw 1 ; 0eh: reserved sectors (including boot record)
36 db 2 ; 10h: number of FATs
37 dw 224 ; 11h: number of dir entries
38 dw 2880 ; 13h: number of sectors in volume
39 db 0fh ; 15h: media descriptor type (f = 3.5" HD floppy)
40 dw 9 ; 16h: number of sectors per FAT
41 dw 18 ; 18h: number of sectors per track
42 dw 2 ; 1ah: number of heads
43 dd 0 ; 1ch: number of hidden sectors
44 dd 0 ; 20h: high bits of sector count
45 db 0 ; 24h: drive number
46 db 0 ; 25h: winnt flags
47 db 28h ; 26h: signature(?)
48 dd 0 ; 27h: volume serial number
49 db "BOOTBOOT "; 2bh: volume label, 11 bytes
50 db "FAT12 " ; 36h: filesystem id, 8 bytes
55 ; initialize segment registers
62 ; bios sets in dl the num of the drive that
63 ; loaded you so if you overwrite dl you can reuse it
64 mov [saved_drive_num], dl
66 ; service 0: set videomode ah
67 ; param: which video mode (13h: 320x200, 256 colors) al
68 ; ax: ah (high 8 bits), al (low)
69 ; call 0 = set_videomode it expects the video mode in al
70 ; ah : 0, al: 13, ax:ahal
74 ; software interrupt 10h
75 ; what's the value of ah? al=video mode when ah=0
76 ; which set which video mode
81 ; load 2nd sector from boot device and jump to it
82 ; bios helpers, 13h = disk io
85 mov ah, 02h ; call 2: read sectors into memory
86 mov al, 3 ; number of sectors to read
87 mov ch, 0 ; low 8 bits of cylinder number
88 mov cl, 2 ; sector number that starts from 1
89 mov dh, 0 ; head number
90 mov dl, [saved_drive_num] ; 8bits
93 ; error check: if carry flag isn't set jump to loaded code
100 ; video ram is mapped in a0000
101 ; first 64000 bytes appear immediately on screen
102 ; mem addresses in real mode have 2 parts: segment and offset
103 ; bits overlap: segment is shifted by 4 and is added to the
104 ; overlapping offset (20 bits number = x86's addressable space = 1MB)
105 ; segment register for the segment: es, ds, cs, ss (and: fs, gs)
106 ; default register = ds (data segment), es = extra segment
107 ; cs = code segment cs:ip (it points where to read instructions and is
108 ; automatically set to 7c0 (7c00h)
109 ; offset can be paired with any register
120 ; dereferrence[] address
124 ; when cx is 0, 0 flag is set
131 ; assembler trick: write as many 0 needed to fill 510 bytes
133 times 510-($-$$) db 0
138 mov ax, 0a000h ; video segment points to video memory
140 mov bx, 0 ; video offset
146 test ax, 0ff00h ; lower 8 bits 0, highest 1, Z flag is set while < 256, test = and but doesnt change ax
150 mov [bx], al ; pixel written on screen
159 ; setup grayscale palette (64 first colors because ramdac uses 6 bits / color so 2^6)
160 ; ram dac (digital to analog converter) tis vga
161 ; in a ^ register which index we want to write
162 ; 3 writes in another ramdac register (data)
163 ; ramdac feature: when you need to set a palette
164 mov al, 0 ; first index
165 mov dx, 3c8h ; ramdac index registe
166 out dx, al ; because io port (address) > 255
167 inc dx ; ramdac data register: 3c9h
173 test al, 3fh ; test with 3fh to keep the lowest 6 bits of al
176 ; setup lovebug palette
177 SETPAL 64, 0, 255, 0 ; chr(64) = @
178 SETPAL 65, 0, 0, 0 ; A
179 SETPAL 66, 50, 50, 50 ; B
180 SETPAL 67, 57, 4, 4 ; C
181 SETPAL 68, 108, 9, 9 ; D
182 SETPAL 69, 164, 4, 4 ; E
184 ; disable all interrupts (for bios to not read the keyboard)
187 ; ds on 0 because rand uses it for accesses to mem
191 shr ax, 4 ; to use it as segment! (shift right)
194 ; draw to back buffer
195 xor bx, bx ; mov bx, 0 to clear the register
198 and ax, 3fh ; last six bits of eax (see rand)
199 mov [es:bx], al ; [] -> bx offset and default segment ds we change it to es
201 cmp bx, 64000 ; num pixels
205 mov di, (200 - 32) * 320 + 160 - 16
216 and ax, 3 ; random value in 0, 2
217 sub ax, 1 ; random value in -1, 1
230 and al, 8 ; the 4th bit is 1 when we are in the vertical blanking period
233 ; copy backbuffer to video memory
237 mov ds, ax ; ds points to backbuffer now (was in es)
241 xor di, di ; write in es:di
242 mov ecx, 16000 ; 16000 double words (dwords) = 64000 bytes
247 inc word [num_frames]
249 ; cmp word [num_frames], 200 - 32
252 in al, 64h ; 60h = keyb data port, 64h = keyb status port
253 and al, 1 ; 1 = OUTBUF_FULL = the keyb controller out buf is full
254 jz main_loop ; no key pressed, loop back
255 in al, 60h ; reads the keyb that was pressed to reset the flag
258 ; re-enable all interrupts
262 mov ax, 3 ; text mode
273 mov dx, 80h ; default to load from drive 80h
274 cmp byte [saved_drive_num], 80h
278 ; load hard disk boot sector
292 ; reads from ds:si writes to es:di (bug top left corner)
293 ; knows how many pixels on screen and how many in the lovebug
295 mov cx, 1024 ; 32 x 32 lovebug
305 dec cx ; dec the num_pixels we want to write
306 jz .end ; all pixels have been written
307 test cx, 1fh ; keep 5 least significant bits, 3 most significant bits 0
308 jnz .loop ; when this is 0 we are in a multiple of 32
309 add di, 320 - 32 ; screen width - bug width
328 ; converts digits to ascii - print
339 div cx ; ax contains cx / 10
357 ; random number generator
368 randmul dd 1103515245
372 str_frames db 'frames: ', 0
373 str_newline db 13, 10, 0 ; crlf carriage return line feed newline
375 ; db to write pixels transparent 255
383 db '@@@@BAAAB@@@@@BAAB@@@@@BAAAB@@@@'
384 db '@@@@@@@@AAAB@BAAAAB@BAAA@@@@@@@@'
385 db '@@@@@@@@@@@AAAAAAAAAA@@@@@@@@@@@'
386 db '@@@@@@@@@@@@AAAAAAAA@@@@@@@@@@@@'
387 db '@@@@@@@@@@@@AAAAAAAA@@@@@@@@@@@@'
388 db '@@@@@@@@@@@@BAAAAAAB@@@@@@@@@@@@'
389 db '@@@@@@@@@@@@@AAACAA@@@@@@@@@@@@@'
390 db '@@@@@@@@@@CDDEECDEEDDC@@@@@@@@@@'
391 db '@@@@@@@CDDEEEEEDCEEEEEDDC@@@@@@@'
392 db '@@@@@CDEEEEDCDECDEDCDEEEEDC@@@@@'
393 db '@@@@CDDCDEECACEDCECACEEDCDDC@@@@'
394 db '@@@CDECACEEDCDECDEDCDEECACEDC@@@'
395 db '@@@DEEDCDEEEEEEDCEEEEEEDCDEED@@@'
396 db '@@CEEEEEEEEEEEDCDDEEEEEEEEEEEC@@'
397 db '@@DEDCDEEEDCDECACCEDCDEEEDCDED@@'
398 db '@@EECACEEECACEDCDDECACEEECACEE@@'
399 db '@CEEDCDEEEDCDEEDCEEDCDEEEDCDEEC@'
400 db '@DEEEEEEEEEEEEDCCDEEEEEEEEEEEED@'
401 db 'CEEEEEEDCDEEEDCBACDEEEDCDEEEEEEC'
402 db 'CEDCDEECACEEDCAABACDEECACEEDCDEC'
403 db 'DECACEEDCDEDCAABAAACDEDCDEECACED'
404 db 'DEDCDEEEEEDCAAAABAAACDEEEEEDCDED'
405 db 'CEEEEEEEEECAAAABAAAAACEEEEEEEEEC'
406 db 'CEEEEEEEEDAAAAAABAAAAADEEEEEEEEC'
407 db '@DEDCDEEDCAAAAABAAAAAACDEEDCDED@'
408 db '@DECACEDCAAAAAAABAAAAAACDECACED@'
409 db '@DEDCDDCAAAAAAABAAAAAAAACDDCDED@'
410 db '@CEEEECAAAAAAAAABAAAAAAAACEEEEC@'
411 db '@@EEED@AAAAAAAABAAAAAAAAA@DEEE@@'
412 db '@@EEDC@@AAAAAAAABAAAAAAA@@CDEE@@'
413 db '@@DEC@@@@@AAAAABAAAAAA@@@@@CED@@'
414 db '@@@C@@@@@@@@AAAABAAA@@@@@@@@C@@@'
416 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)