398 lines
6.6 KiB
NASM
398 lines
6.6 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
|
|
|
|
test byte [si + 11], 0x08 + 0x10
|
|
jnz .skipentry
|
|
|
|
; Make sure the file has non-zero size
|
|
cmp word [si + 28], 0
|
|
jne .isfile
|
|
cmp word [si + 30], 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, and di will be clobbered
|
|
loadsectors:
|
|
push dx
|
|
|
|
.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
|
|
|
|
pop cx
|
|
pop ax
|
|
|
|
inc ax
|
|
add bx, 512
|
|
|
|
loop .loop
|
|
|
|
pop dx
|
|
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:
|
|
lodsb
|
|
test al, al
|
|
jz hang
|
|
mov ah, 0xe
|
|
int 0x10
|
|
jmp fatal_error
|
|
|
|
hang:
|
|
hlt
|
|
jmp hang
|
|
|
|
|
|
kernel_cluster dw 0
|
|
ettinos_kernel_cluster dw 0
|
|
|
|
ettinos_kernel_name db "SYSTEM BIN"
|
|
|
|
notfound_msg db "No kernel", 0
|
|
diskerror_msg db "Disk error", 0
|
|
choose_msg db "(E)ttinOS/Nor86?", 0
|
|
|
|
times 510-($-$$) db 0
|
|
db 0x55, 0xaa
|
|
_end:
|
|
rootdir equ _end
|
|
rootdirsectors equ _end + 2
|