fat: rework bootloader

This commit is contained in:
Nero 2020-03-02 19:44:37 +00:00
parent 598a2c0fdf
commit 9288ad46a4

419
fat.asm
View File

@ -1,241 +1,242 @@
; Bootsector for FAT12 filesystems
; Memory layout: ; Memory layout:
%define segment 0x00100 ; 0000-03FF IVT
; 0400-0500 Bios data area
; 0501-0600 Stack for us
; 0600-0800 This code
org 0x0600
; 0800-0A00 Buffer for disk
buf: equ 0x0800
%define self (0x7C00-(segment<<4)) ; 1 sector jmp near init
%define fattab (self+0x200) ; variable size
%define rootdir 0x00100 ; variable size
%define prog 0x0F000 ; 4K at the end for OS
org self ; current cluster number
cclus: dw 0
jmp short init ; sector number in cluster
csec: dw 0
cluster_offset:
dw 0
times (0x0B - ($-$$)) db 0 times (0x0B - ($-$$)) db 0
fdc: ; sector size
fdc: .ss: dw 0x200
.ss: ; sectors per cluster
dw 0x200 ; sector size .sc: db 2
.sc: ; reserved sector count
db 2 ; sectors per cluster .rsc: dw 1
.rsc: .fn: ; number of file allocation tables
dw 1 ; reserved sector count db 2
.fn: .rde: ; number of root directory entries
db 2 ; number of file allocation tables dw 0x70
.rde: .ts: ; total number of sectors
dw 0x70 ; number of root directory entries dw 720
.ts: .mid: db 0xFD
dw 720 ; total number of sectors .sf: dw 2
.mi: ; medium identifier .spt: dw 9
db 0xFD ; 5.25-inch Double sided, 40 tracks per side, 9 sectors per track (360 KB) .nos: dw 2
.sf: ; sectors per fat .po: dd 0
dw 2 .lrgts: ; large sector count - relevant if .ts is 0
.spt:
dw 9 ; sectors per track
.nos:
dw 2 ; number of sides (heads)
.po:
dd 0 ; partition offset (in LBA blocks)
.lrgts:
dd 0 dd 0
.drv:
db 0 ; drive number
db 0
db 0x29 ; efdc signature
.vid:
dd 0 ; volume id
.vlabel:
db "2B"
times (54 - ($-$$)) db " "
.fstype:
db "FAT12"
times (62 - ($-$$)) db " "
; mformat keeps writing until here ; mformat keeps writing until here
; if we place init earlier, code gets overwritten ; if we place init earlier, code gets overwritten
times (62 - ($-$$)) nop times (62 - ($-$$)) nop
init: init: xor ax, ax
cli
jmp segment:main
main:
; Stack grows down from 64k
mov ax, cs
mov ss, ax
mov sp, prog
mov ds, ax mov ds, ax
mov es, ax mov es, ax
mov ss, ax
mov sp, 0x0600
mov [fdc.drv], dl ; save drive number in fd ; Relocate and jump
sti mov di, sp
mov si, 0x7C00
mov cx, 0x0100
rep movsw
jmp 0:main
; load fat table into memory ; Set adress for sector read/write
mov ax, [fdc.rsc] ; Number is relative to current partition
mov cx, [fdc.sf] ; IN dx:ax sector number
xor dx, dx seek: push ax
mov bx, fattab
call load_sectors
; calculate length of rootdir
mov ax, [fdc.rde]
mov cl, 4
shr ax, cl ; 32 bytes per entry
mov cx, ax
; load root dir
xor dx, dx
mov ax, [fdc.sf]
mul byte [fdc.fn]
add ax, [fdc.rsc]
mov bx, rootdir
call load_sectors
; remember where we left off
; clusters start after rootdir
mov [cluster_offset], ax
; Load kernel
mov bx, prog
mov ax, kernel_name
call load_file
mov bp, 0x3332
jc error
mov dl, [fdc.drv]
; jump into kernel
jmp segment:prog
; Load a file into memory
; IN AX pointer to 8.3 filename
; ES:BX pointer to target area
; OUT CF flag set if error
; DI file size in bytes (<64K)
load_file:
mov si, rootdir
mov cx, [fdc.rde]
.search:
push cx
push si
mov di, ax
mov cx, 11
repe cmpsb
pop si
pop cx
je .read
add si, 0x20
loop .search
xor di, di
stc
ret
.read:
mov ax, [si+0x1A]
mov di, [si+0x1C]
jmp read_clusters
; Read the file given by cluster number
; into the target program area
; in ax cluster number
read_clusters:
; read cluster into area for target file
push ax
sub ax, 2
mul BYTE [fdc.sc]
add ax, [cluster_offset]
xor dx, dx
mov cx, [fdc.sc]
xor ch, ch
call load_sectors
pop ax
; calculate index in FAT
mov si, ax
shr si, 1
add si, ax
add si, fattab
; load entry from FAT, truncate to 12 bit
mov dx, [si]
test ax, 1
jz .noshift
mov cl, 4
shr dx, 4
.noshift:
mov ax, dx
and ax, 0x0FFF
cmp ax, 0x0FF8
jc read_clusters
ret
; Read sectors from disk
; Does not return on error
; ax and bx will be incremented, cx decremented
; in dx:ax sector number
; es:bx buffer
; cx number of sectors to read
; out dx:ax next sector to read
; es:bx next free buffer
; cx zero
load_sectors:
; fail instantly if reading sectors > 16 bit
test dx, dx
mov bp, 0x3330
jnz error
.loop:
push ax
push cx
push dx push dx
push ds
push cs
pop ds
; add partition offset (required for HDD) ; add partition offset (required for HDD)
add ax, [fdc.po] add ax, [fdc.po]
adc dx, [fdc.po+2] adc dx, [fdc.po+2]
; ax:temp = (lba / spt)
; calculate CHS data ; dx:sector = (lba % spt) + 1
div word [cs:fdc.spt] ; ax:temp = (lba / spt) div word [fdc.spt]
inc dx ; dx:sector = (lba % spt) + 1 inc dx
mov cl, dl ; sector number ; Sector number (bits 0-5) into CL
xor dx, dx ; This is blindly assumed to never exeed 63
div word [cs:fdc.nos] ; ax:cylinder = (tmp / heads) mov byte [.cl], dl
; ax:cylinder = (tmp / heads)
; dx:head = (tmp % heads) ; dx:head = (tmp % heads)
mov ch, al ; cylinder number xor dx, dx
mov dh, dl ; head number div word [fdc.nos]
mov dl, [cs:fdc.drv] ; driver number ; Cylinder number bit 0-7 into CH
mov ax, 0x0201 ; ah=0x02 al=0x01 mov byte [.ch], al
; Head number into DL
mov byte [.dh], dl
; Bits 8-9 of cylinder number into bits 6-7 of CL
and ah, 0x03
ror ah, 1
ror ah, 1
or byte [.cl], ah
pop ds
pop dx
pop ax
ret
.cl: db 0
.ch: db 0
.dl: db 0
.dh: db 0
; Read sector into ES:BX
; Sector number has been previously set with seek
read: push ax
push cx
push dx
; ah = int13 subfunction 2
; al = read 1 sector
mov ax, 0x0201
; load seek data into registers
mov cx, word [cs:seek.cl]
mov dx, word [cs:seek.dl]
int 0x13 int 0x13
mov bp, 0x3331
jc error
pop dx pop dx
pop cx pop cx
pop ax pop ax
; count up for next sector
add bx, 0x0200
inc ax
loop .loop
ret ret
error: ; Get start sector of root dirrectory
mov ax, bp ; OUT ax sector number
mov ah, 0x0e dirsec: ; Multiply sectors per fat with number of fats
mov bx, 7 mov ax, [cs:fdc.sf]
int 0x10 mul byte [cs:fdc.fn]
mov al, 0x21 ; Add number of reserved sectors
int 0x10 add ax, [cs:fdc.rsc]
ret
; Get root directory length in sectors
; OUT cx sectors
dirlen: push ax
mov ax, [cs:fdc.rde]
mov cl, 4
shr ax, cl ; 32 bytes per entry
mov cx, ax
pop ax
ret
; Get first sector of data area
; OUT ax sector number
clusec: push cx
call dirsec
call dirlen
add ax, cx
pop cx
ret: ret
; Get total number of sectors
; OUT: dx:ax number of sectors
secnum: xor dx, dx
mov ax, [cs:fdc.ts]
test ax, ax
jnz ret
mov ax, [cs:fdc.lrgts]
mov dx, [cs:fdc.lrgts+2]
ret
; Get number of clusters
; OUT: ax number of clusters
; Cant overflow for FAT16 :P
clunum: push cx
push dx
call clusec
mov cx, ax
call secnum
sub ax, cx
sbb dx, 0
mov cl, byte [cs:fdc.sc]
xor ch, ch
div cx
pop dx
pop cx
ret
; Scan current buffer for matching file
; IN ds = cs
; es = cs
; OUT bx start cluster if file found
; trashed if no match
; carry is set if no file was found
scadir:
push cx
push si
mov si, buf
mov cx, 0x10
.loop: push cx
push si
mov bx, [si+0x1A]
mov di, .name
mov cx, 11
repe cmpsb
pop si
pop cx
je .found
add si, 0x20
loop .loop
stc
.found: pop si
pop cx
ret
.name: db "HELLO "
.ext: db "COM"
open:
push ax
push cx
push dx
push bx
push ds
push es
xor ax, ax xor ax, ax
int 0x16 mov ds, ax
int 0x19 mov es, ax
mov [csec], ax
call dirsec
call dirlen
.loop: xor dx, dx
mov bx, buf
call seek
call read
call scadir
jnc .found
inc ax
loop .loop
stc
.found:
mov [cclus], bx
pop es
pop ds
pop bx
pop dx
pop cx
pop ax
ret
kernel_name: main: mov [seek.dl], dl ; save drive number
db "BDOS BIN"
int 3
call open
int 3
.hlt: hlt
jmp .hlt
; Padding and signature
times (0x1FE - ($-$$)) db 0 times (0x1FE - ($-$$)) db 0
dw 0xAA55 dw 0xAA55