353 lines
9.1 KiB
NASM
353 lines
9.1 KiB
NASM
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:
|