Browse Source

lesson 7, reading from disk

Carlos Fenollosa 10 năm trước cách đây
mục cha
commit
972701d279

+ 54 - 0
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`
+

+ 46 - 0
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

+ 2 - 0
07-bootsector-disk/boot_sect_fakedisk.asm

@@ -0,0 +1,2 @@
+times 256 dw 0x1234
+times 256 dw 0xFECB

+ 32 - 0
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