From 9a829fc71c17a324d747835da2b15dda6f7ea01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Wed, 30 Jun 2021 20:35:07 +0300 Subject: [PATCH] Load kernel off of FAT12 disk --- Makefile | 4 +- bootsector.asm | 185 +++++++++++++++++++++++++++++++++++++++++++------ kernel.asm | 8 +++ 3 files changed, 173 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index 98490f7..b658092 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,12 @@ all: nor86.img -nor86.img: bootsector.bin kernel.bin +nor86.img: bootsector.bin kernel.bin CC0 README.md rm -f $@ mkdosfs -C $@ 1440 rw -i bootsector.bin -o $@ + mcopy -i $@ CC0 :: + mcopy -i $@ README.md :: mcopy -i $@ kernel.bin :: .asm.bin: diff --git a/bootsector.asm b/bootsector.asm index 040fa1e..bf4fea6 100644 --- a/bootsector.asm +++ b/bootsector.asm @@ -45,7 +45,7 @@ _start: ; Save bootdrive mov [drivenumber], dl -root_dir: +calc_constants: ; Disk organization: ; Reserved sectors (MBR) ; FAT1 @@ -58,16 +58,51 @@ root_dir: add ax, [reservedsectors] mov [rootdir], ax + ; Data area comes after root dir, so we need to add the size of + ; the root dir in sectors + ; One root dir entry is 32 bytes, and one sector has 512 bytes + ; Therefore 512 / 32 = 16 entries correspond to a sector + ; However, we can't just do a shift, since we need to round up + ; For integers, ceil(x/y) = floor((x+y-1)/y) + mov ax, [rootdirentries] + add ax, 15 + shr ax, 1 + shr ax, 1 + shr ax, 1 + shr ax, 1 + mov [rootdirsectors], ax + +fat: + ; FAT1 (the main one) follows right after bootsector(s) + mov ax, [reservedsectors] call chs + ; TODO: handle possible crossing of track boundary + mov ax, [sectorsperfat] + mov ah, 2 + mov dl, [drivenumber] + mov bx, 0x8000 + int 0x13 + +root_dir: + mov ax, [rootdir] + call chs + + ; TODO: handle possible crossing of track boundary + mov ax, [rootdirsectors] mov ah, 2 - mov al, 14 ; TODO: don't hardcode mov dl, [drivenumber] mov bx, 0x500 int 0x13 -print_root: - mov byte [0x500 + 32*224], 0 ; TODO: don't hardcode +search_root: + mov bx, [rootdirentries] + shr ax, 1 + shr ax, 1 + shr ax, 1 + shr ax, 1 + shr ax, 1 + mov byte [bx + 0x500], 0 mov si, 0x500 .entry: cmp byte [si], 0 @@ -80,36 +115,137 @@ print_root: jmp .entry .isfile: - mov ah, 0xe - mov cx, 8 - .name: + mov cx, 11 + mov bx, kernel_name + + .compare: lodsb - int 0x10 - loop .name + cmp al, [bx] + jne .nomatch + inc bx + loop .compare - mov al, ' ' - int 0x10 + jmp found - mov cx, 3 - .ext: - lodsb - int 0x10 - loop .ext - - mov al, 13 - int 0x10 - mov al, 10 - int 0x10 - - add si, 32 - 11 + .nomatch: + ; Each entry is 32 bytes long + ; During each iteration of the compare loop, si is + ; incremented and cx is decremented + ; At the top of the loop, si + cx = entry_start + 11, but + ; when we jump here si has been incremented by one already + ; while cx is still the same + ; Therefore, si + cx is one more, so add extra - 1 here + add si, 32 - 11 - 1 + add si, cx jmp .entry .end: +notfound: + ; TODO: Better error messages + mov ax, 0x0e00 + '?' + int 0x10 + hang: hlt jmp hang +found: + ; SI points to 11 bytes after the start of the entry, so adjust all + ; offsets + ; TODO: Handle zero-length files + mov ax, [si - 11 + 26] ; First cluster + push ax + + ; Load OS at the start of the memory + mov bx, 0x500 + + push bx + + .tosector + ; Adjust the cluster number to account for first two + ; "clusters" in the FAT being used for metadata + sub ax, 2 + + ; Scale by number of sectors per cluster + xor bx, bx + mov bl, [sectorspercluster] + mul word bx + + ; Offset by number of non-data sectors before data area + add ax, [rootdir] + add ax, [rootdirsectors] + + ; Load sectors + pop bx + xor cx, cx + mov cl, [sectorspercluster] + mov dl, [drivenumber] + .loop: + push ax + push cx + + call chs + + mov ah, 2 + mov al, 1 + int 0x13 + + pop cx + pop ax + + add bx, 512 + inc ax + loop .loop + + .next: + pop ax + + ; Multiply by 1.5 to get offset into the table + ; This rounds down, which is what we want in order to get + ; the first byte of the address + mov si, ax + shl si, 1 + add si, ax + shr si, 1 + + ; Load the entry from FAT + mov dx, [si + 0x8000] + + ; Two clusters, 0xABC and 0xXYZ, are stored as + ; BC ZA XY + ; ^ where we start reading on even cluster numbers + ; ^^^^^ loading word → 0xZABC + ; ^ where we start reading on odd cluster numbers + ; ^^^^^ loading word: 0xXYZA + test ax, 1 + jnz .odd + + .even: + and dx, 0x0fff + jmp .check_cluster + + .odd: + shr dx, 1 + shr dx, 1 + shr dx, 1 + shr dx, 1 + + .check_cluster: + mov ax, dx + + ; End of chain + cmp ax, 0xFF8 + jg execute_kernel + + push ax + push bx + jmp .tosector + +execute_kernel: + mov dl, [drivenumber] + jmp 0:0x500 + chs: push ax push bx @@ -154,7 +290,10 @@ chs: %include "hexprint.inc" +kernel_name: db "KERNEL BIN" + times 510-($-$$) db 0 db 0x55, 0xaa _end: rootdir equ _end +rootdirsectors equ _end + 2 diff --git a/kernel.asm b/kernel.asm index e69de29..b7c1e40 100644 --- a/kernel.asm +++ b/kernel.asm @@ -0,0 +1,8 @@ +org 0x500 + +mov ax, 0x0e40 +int 0x10 + +hang: + hlt + jmp hang