diff --git a/07-bootsector-disk/README.md b/07-bootsector-disk/README.md new file mode 100644 index 0000000..1d1fa79 --- /dev/null +++ b/07-bootsector-disk/README.md @@ -0,0 +1,54 @@ +*Concepts you may want to Google beforehand: hard disk, cylinder, head, sector, +carry bit* + +**Goal: Let the bootsector load data from disk in order to boot the kernel** + +Our OS won't fit inside the bootsector 512 bytes, so we need to read data from +a disk in order to run the kernel. + +Thankfully, we don't have to deal with turning spinning platters on and off, +we can just call some BIOS routines, like we did to print characters on the screen. +To do so, we set `al` to `0x02` (and other registers with the required cylinder, head +and sector) and raise `int 0x13` + +You can access [a detailed int 13h guide here](http://stanislavs.org/helppc/int_13-2.html) + +On this lesson we will use for the first time the *carry bit*, which is an extra bit +present on each register which stores when an operation has overflowed its current +capacity: + +```nasm +mov ax, 0xFFFF +add ax, 1 ; ax = 0x0000 and carry = 1 +``` + +The carry isn't accessed directly but used as a control structure by other operators, +like `jc` (jump if the carry bit is set) + +The BIOS also sets `al` to the number of sectors read, so always compare it +to the expected number. + + +Code +---- + +Open and examine `boot_sect_disk.asm` for the complete routine that +reads from disk. + +`boot_sect_main.asm` prepares the parameters for disk read and calls `disk_load`. +Notice how we write some extra data which does not actually belong to the boot +sector, since it is outside the 512 bits mark. + +The boot sector is actually sector 1 (the first one, sectors start at 1) +of cylinder 0 of head 0 of hdd 0. + +Thus, any bytes after byte 512 correspond to sector 2 of cylinder 0 of head 0 of hdd 0 + +The main routine will fill it with sample data and then let the bootsector +read it. + +**Note: if you keep getting errors and your code seems fine, make sure that qemu +is booting from the right drive and set the drive on `dl` accordingly** + +Try the flag `-fda` for example, qemu -fda boot_sect_main.bin` and set `dl` as `0x00` + diff --git a/07-bootsector-disk/boot_sect_disk.asm b/07-bootsector-disk/boot_sect_disk.asm new file mode 100644 index 0000000..be3d0a9 --- /dev/null +++ b/07-bootsector-disk/boot_sect_disk.asm @@ -0,0 +1,46 @@ +; load 'dh' sectors from drive 'dl' into ES:BX +disk_load: + pusha + ; reading from disk requires setting specific values in all registers + ; so we will overwrite our input parameters from 'dx'. Let's save it + ; to the stack for later use. + push dx + + mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' + mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) + mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) + ; 0x01 is our boot sector, 0x02 is the first 'available' sector + mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') + ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS + ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) + mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) + + ; [es:bx] <- pointer to buffer where the data will be stored + ; caller sets it up for us, and it is actually the standard location for int 13h + int 0x13 ; BIOS interrupt + jc disk_error ; if error (stored in the carry bit) + + pop dx + cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. + jne sectors_error + popa + ret + + +disk_error: + mov bx, DISK_ERROR + call print + call print_nl + mov dh, ah ; ah = error code, dl = disk drive that dropped the error + call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html + jmp disk_loop + +sectors_error: + mov bx, SECTORS_ERROR + call print + +disk_loop: + jmp $ + +DISK_ERROR: db "Disk read error", 0 +SECTORS_ERROR: db "Incorrect number of sectors read", 0 diff --git a/07-bootsector-disk/boot_sect_fakedisk.asm b/07-bootsector-disk/boot_sect_fakedisk.asm new file mode 100644 index 0000000..fac7728 --- /dev/null +++ b/07-bootsector-disk/boot_sect_fakedisk.asm @@ -0,0 +1,2 @@ +times 256 dw 0x1234 +times 256 dw 0xFECB diff --git a/07-bootsector-disk/boot_sect_main.asm b/07-bootsector-disk/boot_sect_main.asm new file mode 100644 index 0000000..22b603a --- /dev/null +++ b/07-bootsector-disk/boot_sect_main.asm @@ -0,0 +1,32 @@ +[org 0x7c00] + mov bp, 0x8000 ; set the stack safely away from us + mov sp, bp + + mov bx, 0x9000 ; es:bx = 0x0000:0x9000 = 0x09000 + mov dh, 2 ; read 5 sectors + ; the bios sets 'dl' for our boot disk number + ; if you have trouble, use the '-fda' flag: 'qemu -fda file.bin' + call disk_load + + mov dx, [0x9000] ; retrieve the first loaded word, 0xdada + call print_hex + + call print_nl + + mov dx, [0x9000 + 512] ; first word from second loaded sector, 0xface + call print_hex + + jmp $ + +%include "../05-bootsector-functions-strings/boot_sect_print.asm" +%include "../05-bootsector-functions-strings/boot_sect_print_hex.asm" +%include "boot_sect_disk.asm" + +; Magic number +times 510 - ($-$$) db 0 +dw 0xaa55 + +; boot sector = sector 1 of cyl 0 of head 0 of hdd 0 +; from now on = sector 2 ... +times 256 dw 0xdada ; sector 2 = 512 bytes +times 256 dw 0xface ; sector 3 = 512 bytes