nor86/boot.asm

402 lines
6.7 KiB
NASM

cpu 8086
org 0x7c00
jmp short _code
nop
%ifdef F1440
; 1440K floppy
; BPB
oemidentifier db "nor86 "
byterpersector dw 512
sectorspercluster db 1
reservedsectors dw 1
fats db 2
rootdirentries dw 224
totalsectors dw 2880
mediadescription db 0xf0
sectorsperfat dw 9
sectorspertrack dw 18
heads dw 2
hiddensectors dd 0
totalsectorslarge dd 0
%elifdef F360
; 360K floppy
; BPB
oemidentifier db "nor86 "
byterpersector dw 512
sectorspercluster db 2
reservedsectors dw 1
fats db 2
rootdirentries dw 112
totalsectors dw 720
mediadescription db 0xfd
sectorsperfat dw 2
sectorspertrack dw 9
heads dw 2
hiddensectors dd 0
totalsectorslarge dd 0
%else
%error "No valid floppy format specified, specify -d F1440 or -d F360"
%endif
; EBPB
drivenumber db 0 ; useless on-disk, used as a variable
reserved db 0 ; winnt flags
signature db 0x29 ; mkdosfs uses this, dunno how 0x28 differs
serial dd 0
volumelabel db "nor86 boot "
fstype db "FAT12 "
_code:
jmp 0:_start
_start:
cld
; Set up segments
mov ax, cs
mov ds, ax
mov es, ax
cli
mov ss, ax
mov sp, 0x7c00
sti
; Save bootdrive
mov [drivenumber], dl
calc_constants:
; Disk organization:
; Reserved sectors (MBR)
; FAT1
; FAT2
; Root dir
; → Root dir starts at LBA reservedsectors + fats*sectorsperfat
xor ah, ah
mov al, [fats]
mul word [sectorsperfat]
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]
mov bx, 0x8000
mov cx, [sectorsperfat]
call loadsectors
root_dir:
mov ax, [rootdir]
mov bx, 0x500
mov cx, [rootdirsectors]
call loadsectors
search_root:
mov bx, [rootdirentries]
shl bx, 1
shl bx, 1
shl bx, 1
shl bx, 1
shl bx, 1
mov byte [bx + 0x500], 0
mov si, 0x500
.entry:
cmp byte [si], 0
je .end
; Deleted file?
cmp byte [si], 0xe5
je .skipentry
test byte [si + 11], 0x08 + 0x10
jnz .skipentry
; Make sure the file has non-zero size
; File size must be <32K anyways so don't bother with >64K
cmp word [si + 28], 0
jne .isfile
.skipentry:
add si, 32
jmp .entry
.isfile:
.nor86:
cmp word [si + 8], 'KR'
jne .ettinos
cmp byte [si + 10], 'N'
jne .ettinos
mov ax, [si + 26] ; First cluster
mov [kernel_cluster], ax
jmp .skipentry
.ettinos:
mov cx, 11
mov bx, ettinos_kernel_name
.compare:
lodsb
cmp al, [bx]
jne .nextentry
inc bx
loop .compare
; SI points to 11 bytes after the start of the
; entry, so adjust offset
mov ax, [si - 11 + 26] ; First cluster
mov [ettinos_kernel_cluster], ax
inc cx ; Offset so that falling through will work
.nextentry:
; 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:
which_found:
mov bx, [kernel_cluster]
mov cx, [ettinos_kernel_cluster]
test bx, bx
jz .no_nor86
test cx, cx
jz found
mov si, choose_msg
.loop:
lodsb
test al, al
jz .end
mov ah, 0xe
int 0x10
jmp .loop
.end:
xor ax, ax
int 0x16
cmp ah, 0x12 ; E-key
je found_cx
jmp found
.no_nor86:
test cx, cx
jnz found_cx
notfound:
mov si, notfound_msg
jmp fatal_error
found_cx:
mov bx, cx
found:
mov ax, bx
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
dec ax
dec ax
; 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:
; Load sectors
pop bx
xor cx, cx
mov cl, [sectorspercluster]
call loadsectors
.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
jz .even
.odd:
shr dx, 1
shr dx, 1
shr dx, 1
shr dx, 1
.even:
and dx, 0x0fff
.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
; Note: bx will point to after the read data
; Note: ax, cx, dx, and di will be clobbered
loadsectors:
.loop:
mov di, 3 + 1 ; Retry thrice, + 1 is since we dec first
.retry:
push ax
push cx
.chs:
xor dx, dx
; cylinder (track) - head - sector
; cylinder = LBA / sectorspertrack / heads
; head = LBA / sectorspertrack % heads
; sector = LBA % sectorspertrack + 1
div word [sectorspertrack]
; ax = LBA / sectorspertrack
; dx = LBA % sectorspertrack
; sector
mov cl, dl
inc cl
xor dx, dx
div word [heads]
; ax = LBA / sectorspertrack / heads
; dx = LBA / sectorspertrack % heads
; head
mov dh, dl
; cylinder (track)
mov ch, al
;shr ax, 1
;shr ax, 1
;and al, 0xC0
;or cl, al
mov ax, 0x0201
mov dl, [drivenumber]
int 0x13
jc .error
mov ax, 0x0e00 + '.'
int 0x10
pop cx
pop ax
inc ax
add bx, 512
loop .loop
ret
.error:
; Do we still have retries remaining?
mov si, diskerror_msg
dec di
jz fatal_error ; No, fail
; Yes, reset disk
xor ah, ah
int 0x13
; Execute the loop again
pop cx
pop ax
jmp .retry
fatal_error:
mov cx, 9
.loop:
lodsb
mov ah, 0xe
int 0x10
loop .loop
hang:
hlt
jmp hang
kernel_cluster dw 0
ettinos_kernel_cluster dw 0
ettinos_kernel_name db "SYSTEM BIN"
notfound_msg db "No kernel"
diskerror_msg db "Disk error"
choose_msg db 13, 10, "EttinOS/Nor86?", 0
times 510-($-$$) db 0
db 0x55, 0xaa
_end:
rootdir equ _end
rootdirsectors equ _end + 2