Sometimes it's fun to dig into some technical topic as low as you can. In this note I'll talk about some experiments with creating software that runs right after the BIOS has done it's job.
Master Boot Record
The Master Boot Record or mbr is a 512 byte file which is loaded by the BIOS right after your computer boots up. The BIOS searches for this file in configured locations, it can be a harddisk, floppy disk, usb drive or a cd/dvd. The file is loaded by your machine into physical memory at offset 0x7c00 and then the CPU starts executing the instructions in that file.
The mbr often does boring things like finding partition tables, locating disks and loading and calling other bootloaders. However since this is just code then we can build our own master boot record that does just what we need.
Our computers are made for just burning through instructions one at a time and I wish there was a bootloader which just used these cycles to do nothing at all. So let's create one of those.
The first thing we need to know is how do we program these bootloaders. Well it turns out that when an ordinary PC boots up, it is as dumb as sheep. The BIOS sets the CPU into something called "real mode" which means that the CPU is executing 16bit 8086 instructions. It also means that the CPU is doing segmented adressing so we have to use segment registers to get at certain memory locations. It should really be called "old mode"
So since there are no modern compilers which will produce this old style binaries then we will use an assembler and we will be programming in assembler language. The good stuff. I will be using the
nasm assembler for these experiments.
[bits 16] [org 0x7C00] jmp $ times 510 - ($ - $$) db 0 dw 0xAA55
This is it... This is the source code for a mbr bootloader which does absolutly nothing. We can compile and test it with the following Makefile.
boot.bin: bootloader.asm nasm $^ -f bin -o $@ test: boot.bin qemu-system-i386 -hda boot.bin
The source code is small and delicious, first we say to nasm that it should produce 16 bit code. Then we say that the code is going to be loaded into address offset 0x7C00. Next we have one x86 instruction which says "jump to the current location". Then we fill the rest of the file with 0 and end with the two magic bytes 0xAA55. The last two bytes are a signature that tells the BIOS that this is in fact a bootloader.
Running the previous code a few times is fun, but we need something more to keep the bootloader interresting. So we will produce some code to write a string to the screen.
[bits 16] [org 0x7c00] loop: mov si, my_string call print_string jmp loop print_char: mov ah, 0x0e mov bh, 0x00 mov bl, 0x07 int 0x10 ret print_string: next_char: mov al, [si] inc si or al, al jz exit_function call print_char jmp next_char exit_function: ret my_string db 'all work and no play', 0 times 510 - ($ - $$) db 0 dw 0xaa55
Awsome, now we have something to show off. If you are more of the colorful type then we can create another bootloader which shows colors on the screen.
[bits 16] [org 0x7c00] start: ;; Call VideoService interrupt 0x10 ;; ah = 0x00 -> SetVideoMode ;; al = 0x13 -> 320x200 16 color mov ah, 0x00 mov al, 0x13 int 0x10 ;; Setup registers to point to video address ;; es:di = 0xa000:0x0000 mov ax, 0xa000 mov es, ax xor di, di ;; Fill screen with color from al register ;; it will start with color 0x00 mov cx, 0x0000 .loop: mov byte [es:di], al inc di cmp di, 0xffff jnz .loop ;; Increment color inc al ;; Set data register to 0 for beginning of video address xor di, di jmp .loop times 510 - ($ - $$) db 0 dw 0xaa55
Now I'm happy with my new bootloader. It does what it's supposed to do, be a colorful addition to my workplace.
For more information on master boot records take a look at osdev. The osdev site is a good source for juicy details on operating systems and other low level fruits.