ORG 7C00h BITS 16 jmp short main nop ; Define Fat12 header bdb_oem: db 'MSWIN4.1' ; ignore bdb_bytes_per_sector: dw 200h ; = 512d bdb_sectors_per_cluster: db 01h ; sector = cluster bdb_reserved_sectors: dw 01h bdb_fat_count: db 02h ; We've got a fat1 and fat2 bdb_dir_entries_count: dw 0E0h ; Maximum number of root directory entries bdb_total_sectors: dw 0B40h ; = 2880d bdb_media_descriptor_type: db 0F0h ; ignore bdb_sectors_per_fat: dw 09h bdb_sectors_per_track: dw 12h ; = 18d bdb_number_of_heads: dw 02h ; top and bottom of the floppy disk bdb_hidden_sectors: dd 0 ; ignore bdb_large_sector_count: dd 0 ; total sector count for fat32 (0 for fat12 or fat16) ; extended boot record ebr_drive_number: db 0 ; ignore db 0 ; ignore ebr_signature: db 29h ; boot signature, indicates that the next three fields are present (0x29) ebr_volume_id: db 12h, 34h, 56h, 78h ; unique id for volume tracking ebr_volume_label: db 'CrawShaw OS' ; must be 11 bytes ebr_system_id: db 'FAT12 ' ' ; must be 8 bytes main: ; Setup registers mov ax, 0 mov ds, ax ; ds = data segment pointer mov es, ax ; es = extra segment pointer mov ss, ax ; ss = stack pointer mov sp, 7C00h ; Read from disk ; mov [ebr_drive_number], dl ; mov ax, 1 ; mov cl, 1 ; mov bx, 7E00h ; call disk_read ; Output boot text mov si, boot_text call print_string ; Load kernel ; 4 segments: ; reserved segment (bdb_reserved_segments, 1) ; FAT: (sectors_per_fat * fat_count) 9*2=18 ; root dir: gives location of data ; data: stores the actual data ; Get LBA of root dir (sectors per fat * fat count) + number of reserved sectors mov ax, [bdb_sectors_per_fat] ; ax = 09h mov bl, [bdb_fat_count] ; bl = 02h xor bh,bh ; clear bh mul bx ; ax*bx = sectors per fat * fat count = 09h * 02h = 18 add ax, [bdb_reserved_sectors] ; then add on the reserved sector (1) = The LBA of root dir = 19d = 13h push ax ; Push to stack ; the top of the ax stack now stores the LBA of the root directory ; determine the size of the root directory mov ax, [bdb_dir_entries_count] ; move the number of root directory entries into ax (E0h) shl ax,5 ; ax *= 32 (shifting 5 times) xor dx,dx ; Clear dx (remainder) div word [bdb_bytes_per_sector] ;(32*num of entries)/bytes per sector = total number of sectors we need to read test dx,dx ; See if there's a remainder jz root_dir_after inc ax ; Add one if there's a remainder (this is like rounding up) ; read the root directory tree into memory root_dir_after: ; read the data from the root directory from disk mov cl, al pop ax ; LBA of root dir mov dl, [ebr_drive_number] mov bx, buffer call disk_read ; convert the LBA of the root directory to a CHS xor bx,bx ; clear bx mov di, buffer ; Loaded root dir into memory, now we need to find the kernel ; di points to the start of this memory ; Search for 'KERNEL BIN' in a loop until all root files entries have been checked search_for_kernel: mov si, file_kernel_bin ; Move the name of the kernel into si (string pointer) mov cx, 11 ; length of 'KERNEL BIN' push di ; Preserve di repe cmpsb ; Repeat a comparison of bytes between kernel name and current bytes until it finds a match pop di ; Retrieve di je found_kernel add di, 32 ; increment di to the next directory entry inc bx cmp bx, [bdb_dir_entries_count] ; Have we reached the number of directories that exist jl search_for_kernel ; Repeat search jmp kernel_not_found ; If the last dir has been searched, then there is no kernel ; The kernel has not been found so output an error and halt kernel_not_found: mov si, kernel_load_error call print_string hlt jmp halt ; The kernel has been found found_kernel: mov si, kernel_found_text call print_string ; Find kernel cluster mov ax, [di+26] ; di is the address of the kernel, 26 is the offset mov [kernel_cluster], ax mov ax, [bdb_reserved_sectors] mov bx, buffer mov cl, [bdb_sectors_per_fat] mov dl, [ebr_drive_number] call disk_read ; Load FAT from disk into memory ; Load kernel into memory mov bx, kernel_load_segment mov es, bx mov bx, kernel_load_offset ; Setup memory that kernel will be loaded into load_kernel_loop: mov ax, [kernel_cluster] add ax, 31 ; Setup offset so we can read the particular cluster mov cl, 1 ;sectors to read mov dl, [ebr_drive_number] call disk_read add bx, [bdb_bytes_per_sector] ; (kernel_cluster * 3) / 2 mov ax, [kernel_cluster] mov cx, 3 mul cx mov cx, 2 div cx ; setup buffers mov si, buffer add si, ax mov ax, [ds:si] or dx,dx jz even odd: shr ax, 4 ;shift ax 4 jmp next_cluster_after even: and ax, 0fffh ; Gives the other 12 bits next_cluster_after: cmp ax, 0ff8h ; If the value is ff8h or bigger then we've reached the end of FAT jae read_finish mov [kernel_cluster], ax jmp load_kernel_loop read_finish: ; Load kernel mov si, kernel_loading call print_string mov dl, [ebr_drive_number] mov ax, kernel_load_segment mov ds, ax mov es, ax jmp kernel_load_segment:kernel_load_offset ; Jump to the kernel code hlt halt: jmp halt ; LBA = index of data segment on disk ; CHS = cylinder, header, sector ; T = LBA/sectors per track ; S = (LBA%sectors per track) + 1 ; H = T % heads ; C = T / headers ; input, LBA index: ax ; sector number: cl ; cylinder: ch ; head: dh ; Example where LBA = 50h (CHS = 2,0,9) ; ax = 0050h, push this to the stack ; dx = 0000h ; dx = 50h % 12h = 0008h ; ax = 50h / 12h = 0004h ; dx = 0009h ; cx = 0009h ; dx = 0000h ; dx = 04h % 02h = 0000h ; ax = 04h / 02h = 0002h ; dh = 00h (dx = 0000h) ; ch = 02h (cx = 0209h) ; ah = 00h (ax = 0002h) ; cl = 09h OR 00h = 09h (cx = 0209h) ; ax = 0050h ; dl = 50h (dx = 0050h) ; ax = 0050h ; thus: ; cylinder (ch) = 02h ; head (cl) = 00h ; sector (dh) = 09h lba_to_chs: push ax push dx xor dx,dx ; clear dx div word [bdb_sectors_per_track] ; (LBA % sectors per track) + 1 = sector inc dx ; sector, dx stores the remainder so we increment that. mov cx,dx xor dx,dx ; clear dx div word [bdb_number_of_heads] mov dh,dl ; head, dx stores remainder so we move that up 8 bits to dh mov ch,al shl ah, 6 ; * 32 or cl, ah ; cylinder pop ax mov dl,al pop ax ret ; int 13h/ah = 02h read disk sectors into memory ; al = number of sectors to read ; ch = cylinder number ; cl = sector number ; dh = head number ; dl = drive number ; es:bx = points to data buffer disk_read: push ax push bx push cx push dx push di ; cl = sector ; ch = cylinder ; dh = head ; dl = drive call lba_to_chs ; Get the chs address mov ah, 02h ; BIOS ah code for read disk sectors to memory ; repeat drive read 3 times (incase of random error) mov di, 3 ; counter ; This is basically ; for di in range(3,1,1): ; We test to see if we can read the disk 3 times retry: ; When the disk is tried to be read, the CF flag will be ; set if there's an error, so we just clear it to return it to ; the default state stc ; sets the cf flag int 13h jnc done_read ; jump if cf = 0 call disk_reset ; Reset drivers of disk dec di ; di -= 1 test di, di ; if di = 0 jnz retry fail_disk_read: mov si, disk_read_fail call print_string hlt jmp halt ; int 13h / ah = 00h reset disk system disk_reset: pusha mov ah, 0 ; Reset drive stc int 13h jc fail_disk_read popa ret done_read: pop di pop dx pop cx pop bx pop ax ret ; SI = pointer to start of string to be printed print_string: push si push ax push bx mov ah, 0Eh ; int 10h teletype function, we're telling the BIOS we will print something .repeat: lodsb ; Get char from si into al cmp al, 0 ; Compare al to 0 je .done ; If char is zero, end of string mov bh, 0 int 10h ; Otherwise, print it jmp .repeat ; And move on to next char .done: ; Print newline mov ah, 0Eh mov al, 13 int 10h mov al, 10 int 10h ; Exit function pop bx pop ax pop si ret boot_text: db 'OK] Boot sequence begun', 0 kernel_found_text: db 'OK] Kernel located', 0 kernel_loading: db 'OK] Loading kernel', 0 disk_read_fail: db 'ERR] Disk read fail', 0 file_kernel_bin: db 'KERNEL BIN' ; Must have a double space between KERNEL and BIN kernel_load_error: db 'ERR] Kernel not found', 0 kernel_cluster: dw 0 kernel_load_segment: equ 2000h ; an area in memory we know should be available kernel_load_offset: equ 0 times 510-($-$$) db 0 ; Fill up the next 510 bits with 0's dw 0AA55h ; D buffer: