358 lines
9.6 KiB
NASM
Executable File
358 lines
9.6 KiB
NASM
Executable File
; ==================================================================
|
|
; The Mike Operating System bootloader
|
|
; Copyright (C) 2006 - 2022 MikeOS Developers -- see doc/LICENSE.TXT
|
|
;
|
|
; Based on a free boot loader by E Dehling. It scans the FAT12
|
|
; floppy for KERNEL.BIN (the MikeOS kernel), loads it and executes it.
|
|
; This must grow no larger than 512 bytes (one sector), with the final
|
|
; two bytes being the boot signature (AA55h). Note that in FAT12,
|
|
; a cluster is the same as a sector: 512 bytes.
|
|
; ==================================================================
|
|
|
|
|
|
BITS 16
|
|
|
|
jmp short bootloader_start ; Jump past disk description section
|
|
nop ; Pad out before disk description
|
|
|
|
|
|
; ------------------------------------------------------------------
|
|
; Disk description table, to make it a valid floppy
|
|
; Note: some of these values are hard-coded in the source!
|
|
; Values are those used by IBM for 1.44 MB, 3.5" diskette
|
|
|
|
OEMLabel db "MIKEBOOT" ; Disk label
|
|
BytesPerSector dw 512 ; Bytes per sector
|
|
SectorsPerCluster db 1 ; Sectors per cluster
|
|
ReservedForBoot dw 1 ; Reserved sectors for boot record
|
|
NumberOfFats db 2 ; Number of copies of the FAT
|
|
RootDirEntries dw 224 ; Number of entries in root dir
|
|
; (224 * 32 = 7168 = 14 sectors to read)
|
|
LogicalSectors dw 2880 ; Number of logical sectors
|
|
MediumByte db 0F0h ; Medium descriptor byte
|
|
SectorsPerFat dw 9 ; Sectors per FAT
|
|
SectorsPerTrack dw 18 ; Sectors per track (36/cylinder)
|
|
Sides dw 2 ; Number of sides/heads
|
|
HiddenSectors dd 0 ; Number of hidden sectors
|
|
LargeSectors dd 0 ; Number of LBA sectors
|
|
DriveNo dw 0 ; Drive No: 0
|
|
Signature db 41 ; Drive signature: 41 for floppy
|
|
VolumeID dd 00000000h ; Volume ID: any number
|
|
VolumeLabel db "MIKEOS "; Volume Label: any 11 chars
|
|
FileSystem db "FAT12 " ; File system type: don't change!
|
|
|
|
|
|
; ------------------------------------------------------------------
|
|
; Main bootloader code
|
|
|
|
bootloader_start:
|
|
mov ax, 07C0h ; Set up 4K of stack space above buffer
|
|
add ax, 544 ; 8k buffer = 512 paragraphs + 32 paragraphs (loader)
|
|
cli ; Disable interrupts while changing stack
|
|
mov ss, ax
|
|
mov sp, 4096
|
|
sti ; Restore interrupts
|
|
|
|
mov ax, 07C0h ; Set data segment to where we're loaded
|
|
mov ds, ax
|
|
|
|
; NOTE: A few early BIOSes are reported to improperly set DL
|
|
|
|
cmp dl, 0
|
|
je no_change
|
|
mov [bootdev], dl ; Save boot device number
|
|
mov ah, 8 ; Get drive parameters
|
|
int 13h
|
|
jc fatal_disk_error
|
|
and cx, 3Fh ; Maximum sector number
|
|
mov [SectorsPerTrack], cx ; Sector numbers start at 1
|
|
movzx dx, dh ; Maximum head number
|
|
add dx, 1 ; Head numbers start at 0 - add 1 for total
|
|
mov [Sides], dx
|
|
|
|
no_change:
|
|
mov eax, 0 ; Needed for some older BIOSes
|
|
|
|
|
|
; First, we need to load the root directory from the disk. Technical details:
|
|
; Start of root = ReservedForBoot + NumberOfFats * SectorsPerFat = logical 19
|
|
; Number of root = RootDirEntries * 32 bytes/entry / 512 bytes/sector = 14
|
|
; Start of user data = (start of root) + (number of root) = logical 33
|
|
|
|
floppy_ok: ; Ready to read first block of data
|
|
mov ax, 19 ; Root dir starts at logical sector 19
|
|
call l2hts
|
|
|
|
mov si, buffer ; Set ES:BX to point to our buffer (see end of code)
|
|
mov bx, ds
|
|
mov es, bx
|
|
mov bx, si
|
|
|
|
mov ah, 2 ; Params for int 13h: read floppy sectors
|
|
mov al, 14 ; And read 14 of them
|
|
|
|
pusha ; Prepare to enter loop
|
|
|
|
|
|
read_root_dir:
|
|
popa ; In case registers are altered by int 13h
|
|
pusha
|
|
|
|
stc ; A few BIOSes do not set properly on error
|
|
int 13h ; Read sectors using BIOS
|
|
|
|
jnc search_dir ; If read went OK, skip ahead
|
|
call reset_floppy ; Otherwise, reset floppy controller and try again
|
|
jnc read_root_dir ; Floppy reset OK?
|
|
|
|
jmp reboot ; If not, fatal double error
|
|
|
|
|
|
search_dir:
|
|
popa
|
|
|
|
mov ax, ds ; Root dir is now in [buffer]
|
|
mov es, ax ; Set DI to this info
|
|
mov di, buffer
|
|
|
|
mov cx, word [RootDirEntries] ; Search all (224) entries
|
|
mov ax, 0 ; Searching at offset 0
|
|
|
|
|
|
next_root_entry:
|
|
xchg cx, dx ; We use CX in the inner loop...
|
|
|
|
mov si, kern_filename ; Start searching for kernel filename
|
|
mov cx, 11
|
|
rep cmpsb
|
|
je found_file_to_load ; Pointer DI will be at offset 11
|
|
|
|
add ax, 32 ; Bump searched entries by 1 (32 bytes per entry)
|
|
|
|
mov di, buffer ; Point to next entry
|
|
add di, ax
|
|
|
|
xchg dx, cx ; Get the original CX back
|
|
loop next_root_entry
|
|
|
|
mov si, file_not_found ; If kernel is not found, bail out
|
|
call print_string
|
|
jmp reboot
|
|
|
|
|
|
found_file_to_load: ; Fetch cluster and load FAT into RAM
|
|
mov ax, word [es:di+0Fh] ; Offset 11 + 15 = 26, contains 1st cluster
|
|
mov word [cluster], ax
|
|
|
|
mov ax, 1 ; Sector 1 = first sector of first FAT
|
|
call l2hts
|
|
|
|
mov di, buffer ; ES:BX points to our buffer
|
|
mov bx, di
|
|
|
|
mov ah, 2 ; int 13h params: read (FAT) sectors
|
|
mov al, 9 ; All 9 sectors of 1st FAT
|
|
|
|
pusha ; Prepare to enter loop
|
|
|
|
|
|
read_fat:
|
|
popa ; In case registers are altered by int 13h
|
|
pusha
|
|
|
|
stc
|
|
int 13h ; Read sectors using the BIOS
|
|
|
|
jnc read_fat_ok ; If read went OK, skip ahead
|
|
call reset_floppy ; Otherwise, reset floppy controller and try again
|
|
jnc read_fat ; Floppy reset OK?
|
|
|
|
; ******************************************************************
|
|
fatal_disk_error:
|
|
; ******************************************************************
|
|
mov si, disk_error ; If not, print error message and reboot
|
|
call print_string
|
|
jmp reboot ; Fatal double error
|
|
|
|
|
|
read_fat_ok:
|
|
popa
|
|
|
|
mov ax, 2000h ; Segment where we'll load the kernel
|
|
mov es, ax
|
|
mov bx, 0
|
|
|
|
mov ah, 2 ; int 13h floppy read params
|
|
mov al, 1
|
|
|
|
push ax ; Save in case we (or int calls) lose it
|
|
|
|
|
|
; Now we must load the FAT from the disk. Here's how we find out where it starts:
|
|
; FAT cluster 0 = media descriptor = 0F0h
|
|
; FAT cluster 1 = filler cluster = 0FFh
|
|
; Cluster start = ((cluster number) - 2) * SectorsPerCluster + (start of user)
|
|
; = (cluster number) + 31
|
|
|
|
load_file_sector:
|
|
mov ax, word [cluster] ; Convert sector to logical
|
|
add ax, 31
|
|
|
|
call l2hts ; Make appropriate params for int 13h
|
|
|
|
mov ax, 2000h ; Set buffer past what we've already read
|
|
mov es, ax
|
|
mov bx, word [pointer]
|
|
|
|
pop ax ; Save in case we (or int calls) lose it
|
|
push ax
|
|
|
|
stc
|
|
int 13h
|
|
|
|
jnc calculate_next_cluster ; If there's no error...
|
|
|
|
call reset_floppy ; Otherwise, reset floppy and retry
|
|
jmp load_file_sector
|
|
|
|
|
|
; In the FAT, cluster values are stored in 12 bits, so we have to
|
|
; do a bit of maths to work out whether we're dealing with a byte
|
|
; and 4 bits of the next byte -- or the last 4 bits of one byte
|
|
; and then the subsequent byte!
|
|
|
|
calculate_next_cluster:
|
|
mov ax, [cluster]
|
|
mov dx, 0
|
|
mov bx, 3
|
|
mul bx
|
|
mov bx, 2
|
|
div bx ; DX = [cluster] mod 2
|
|
mov si, buffer
|
|
add si, ax ; AX = word in FAT for the 12 bit entry
|
|
mov ax, word [ds:si]
|
|
|
|
or dx, dx ; If DX = 0 [cluster] is even; if DX = 1 then it's odd
|
|
|
|
jz even ; If [cluster] is even, drop last 4 bits of word
|
|
; with next cluster; if odd, drop first 4 bits
|
|
|
|
odd:
|
|
shr ax, 4 ; Shift out first 4 bits (they belong to another entry)
|
|
jmp short next_cluster_cont
|
|
|
|
|
|
even:
|
|
and ax, 0FFFh ; Mask out final 4 bits
|
|
|
|
|
|
next_cluster_cont:
|
|
mov word [cluster], ax ; Store cluster
|
|
|
|
cmp ax, 0FF8h ; FF8h = end of file marker in FAT12
|
|
jae end
|
|
|
|
add word [pointer], 512 ; Increase buffer pointer 1 sector length
|
|
jmp load_file_sector
|
|
|
|
|
|
end: ; We've got the file to load!
|
|
pop ax ; Clean up the stack (AX was pushed earlier)
|
|
mov dl, byte [bootdev] ; Provide kernel with boot device info
|
|
|
|
jmp 2000h:0000h ; Jump to entry point of loaded kernel!
|
|
|
|
|
|
; ------------------------------------------------------------------
|
|
; BOOTLOADER SUBROUTINES
|
|
|
|
reboot:
|
|
mov ax, 0
|
|
int 16h ; Wait for keystroke
|
|
mov ax, 0
|
|
int 19h ; Reboot the system
|
|
|
|
|
|
print_string: ; Output string in SI to screen
|
|
pusha
|
|
|
|
mov ah, 0Eh ; int 10h teletype function
|
|
|
|
.repeat:
|
|
lodsb ; Get char from string
|
|
cmp al, 0
|
|
je .done ; If char is zero, end of string
|
|
int 10h ; Otherwise, print it
|
|
jmp short .repeat
|
|
|
|
.done:
|
|
popa
|
|
ret
|
|
|
|
|
|
reset_floppy: ; IN: [bootdev] = boot device; OUT: carry set on error
|
|
push ax
|
|
push dx
|
|
mov ax, 0
|
|
mov dl, byte [bootdev]
|
|
stc
|
|
int 13h
|
|
pop dx
|
|
pop ax
|
|
ret
|
|
|
|
|
|
l2hts: ; Calculate head, track and sector settings for int 13h
|
|
; IN: logical sector in AX, OUT: correct registers for int 13h
|
|
push bx
|
|
push ax
|
|
|
|
mov bx, ax ; Save logical sector
|
|
|
|
mov dx, 0 ; First the sector
|
|
div word [SectorsPerTrack]
|
|
add dl, 01h ; Physical sectors start at 1
|
|
mov cl, dl ; Sectors belong in CL for int 13h
|
|
mov ax, bx
|
|
|
|
mov dx, 0 ; Now calculate the head
|
|
div word [SectorsPerTrack]
|
|
mov dx, 0
|
|
div word [Sides]
|
|
mov dh, dl ; Head/side
|
|
mov ch, al ; Track
|
|
|
|
pop ax
|
|
pop bx
|
|
|
|
mov dl, byte [bootdev] ; Set correct device
|
|
|
|
ret
|
|
|
|
|
|
; ------------------------------------------------------------------
|
|
; STRINGS AND VARIABLES
|
|
|
|
kern_filename db "KERNEL BIN" ; MikeOS kernel filename
|
|
|
|
disk_error db "Floppy error! Press any key...", 0
|
|
file_not_found db "KERNEL.BIN not found!", 0
|
|
|
|
bootdev db 0 ; Boot device number
|
|
cluster dw 0 ; Cluster of the file we want to load
|
|
pointer dw 0 ; Pointer into Buffer, for loading kernel
|
|
|
|
|
|
; ------------------------------------------------------------------
|
|
; END OF BOOT SECTOR AND BUFFER START
|
|
|
|
times 510-($-$$) db 0 ; Pad remainder of boot sector with zeros
|
|
dw 0AA55h ; Boot signature (DO NOT CHANGE!)
|
|
|
|
|
|
buffer: ; Disk buffer begins (8k after this, stack starts)
|
|
|
|
|
|
; ==================================================================
|
|
|