fat: rework bootloader

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

441
fat.asm
View File

@ -1,241 +1,242 @@
; Memory layout: ; Bootsector for FAT12 filesystems
%define segment 0x00100 ; Memory layout:
; 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
; sector number in cluster
csec: dw 0
jmp short init times (0x0B - ($-$$)) db 0
fdc: ; sector size
.ss: dw 0x200
; sectors per cluster
.sc: db 2
; reserved sector count
.rsc: dw 1
.fn: ; number of file allocation tables
db 2
.rde: ; number of root directory entries
dw 0x70
.ts: ; total number of sectors
dw 720
.mid: db 0xFD
.sf: dw 2
.spt: dw 9
.nos: dw 2
.po: dd 0
.lrgts: ; large sector count - relevant if .ts is 0
dd 0
cluster_offset: ; mformat keeps writing until here
dw 0 ; if we place init earlier, code gets overwritten
times (62 - ($-$$)) nop
times (0x0B - ($-$$)) db 0 init: xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x0600
fdc: ; Relocate and jump
.ss: mov di, sp
dw 0x200 ; sector size mov si, 0x7C00
.sc: mov cx, 0x0100
db 2 ; sectors per cluster rep movsw
.rsc: jmp 0:main
dw 1 ; reserved sector count
.fn:
db 2 ; number of file allocation tables
.rde:
dw 0x70 ; number of root directory entries
.ts:
dw 720 ; total number of sectors
.mi: ; medium identifier
db 0xFD ; 5.25-inch Double sided, 40 tracks per side, 9 sectors per track (360 KB)
.sf: ; sectors per fat
dw 2
.spt:
dw 9 ; sectors per track
.nos:
dw 2 ; number of sides (heads)
.po:
dd 0 ; partition offset (in LBA blocks)
.lrgts:
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 ; Set adress for sector read/write
; if we place init earlier, code gets overwritten ; Number is relative to current partition
times (62 - ($-$$)) nop ; IN dx:ax sector number
seek: push ax
push dx
push ds
push cs
pop ds
; add partition offset (required for HDD)
add ax, [fdc.po]
adc dx, [fdc.po+2]
; ax:temp = (lba / spt)
; dx:sector = (lba % spt) + 1
div word [fdc.spt]
inc dx
; Sector number (bits 0-5) into CL
; This is blindly assumed to never exeed 63
mov byte [.cl], dl
; ax:cylinder = (tmp / heads)
; dx:head = (tmp % heads)
xor dx, dx
div word [fdc.nos]
; Cylinder number bit 0-7 into CH
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
init: ; Read sector into ES:BX
cli ; Sector number has been previously set with seek
jmp segment:main 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]
main: int 0x13
; Stack grows down from 64k
mov ax, cs
mov ss, ax
mov sp, prog
mov ds, ax
mov es, ax
mov [fdc.drv], dl ; save drive number in fd pop dx
sti pop cx
pop ax
ret
; load fat table into memory ; Get start sector of root dirrectory
mov ax, [fdc.rsc] ; OUT ax sector number
mov cx, [fdc.sf] dirsec: ; Multiply sectors per fat with number of fats
xor dx, dx mov ax, [cs:fdc.sf]
mov bx, fattab mul byte [cs:fdc.fn]
call load_sectors ; Add number of reserved sectors
add ax, [cs:fdc.rsc]
ret
; calculate length of rootdir ; Get root directory length in sectors
mov ax, [fdc.rde] ; OUT cx sectors
mov cl, 4 dirlen: push ax
shr ax, cl ; 32 bytes per entry mov ax, [cs:fdc.rde]
mov cx, ax mov cl, 4
shr ax, cl ; 32 bytes per entry
mov cx, ax
pop ax
ret
; load root dir ; Get first sector of data area
xor dx, dx ; OUT ax sector number
mov ax, [fdc.sf] clusec: push cx
mul byte [fdc.fn] call dirsec
add ax, [fdc.rsc] call dirlen
mov bx, rootdir add ax, cx
call load_sectors pop cx
ret: ret
; remember where we left off ; Get total number of sectors
; clusters start after rootdir ; OUT: dx:ax number of sectors
mov [cluster_offset], ax 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
; Load kernel ; Get number of clusters
mov bx, prog ; OUT: ax number of clusters
mov ax, kernel_name ; Cant overflow for FAT16 :P
call load_file clunum: push cx
mov bp, 0x3332 push dx
jc error 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
mov dl, [fdc.drv] ; 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"
; jump into kernel open:
jmp segment:prog push ax
push cx
push dx
push bx
push ds
push es
xor ax, ax
mov ds, ax
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
; Load a file into memory main: mov [seek.dl], dl ; save drive number
; 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 int 3
; into the target program area call open
; in ax cluster number int 3
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 .hlt: hlt
mov si, ax jmp .hlt
shr si, 1
add si, ax
add si, fattab
; load entry from FAT, truncate to 12 bit ; Padding and signature
mov dx, [si] times (0x1FE - ($-$$)) db 0
test ax, 1 dw 0xAA55
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
; add partition offset (required for HDD)
add ax, [fdc.po]
adc dx, [fdc.po+2]
; calculate CHS data
div word [cs:fdc.spt] ; ax:temp = (lba / spt)
inc dx ; dx:sector = (lba % spt) + 1
mov cl, dl ; sector number
xor dx, dx
div word [cs:fdc.nos] ; ax:cylinder = (tmp / heads)
; dx:head = (tmp % heads)
mov ch, al ; cylinder number
mov dh, dl ; head number
mov dl, [cs:fdc.drv] ; driver number
mov ax, 0x0201 ; ah=0x02 al=0x01
int 0x13
mov bp, 0x3331
jc error
pop dx
pop cx
pop ax
; count up for next sector
add bx, 0x0200
inc ax
loop .loop
ret
error:
mov ax, bp
mov ah, 0x0e
mov bx, 7
int 0x10
mov al, 0x21
int 0x10
xor ax, ax
int 0x16
int 0x19
kernel_name:
db "BDOS BIN"
times (0x1FE - ($-$$)) db 0
dw 0xAA55