Loading a file 1 sector at a time
Posted: Tue Aug 03, 2010 3:27 pm
I just finished writing my own bootsector. It reads the kernel file from a FAT12 file system.
When loading the kernel, i follow the trail of clusters until i reach a cluster that's not contiguous then load all contiguous clusters up to that point and then carry on from the new cluster until i reach EOF. This is advantageous because it means for most kernel files without fragmentation it's just 2 int 0x13 requests, one for the FAT and Root and another for the actual file.
The problem is doing more than 1 sector at a time is you can't ask for more than 127 at a time, and you can't pass a cylinder boundary. At the moment i just check for the former, but not the latter.
The easiest solution is to just load 1 sector at a time, which is slower but how much slower? Boot sector speed is not really important, But when your working a with a real mechanical floppy drive, stopping and starting it seems like it could add considerable delays. How bad is it? would i get noticeable slowdown when using a real floppy if i loaded one sector at a time?
If it turns out loading 1 sector at a time is only very slightly slower then i'll quite happily do that instead. As it would simply the code and allow me to have higher quality and more debug messages.
Heres my bootsector is anyone is interested:
When loading the kernel, i follow the trail of clusters until i reach a cluster that's not contiguous then load all contiguous clusters up to that point and then carry on from the new cluster until i reach EOF. This is advantageous because it means for most kernel files without fragmentation it's just 2 int 0x13 requests, one for the FAT and Root and another for the actual file.
The problem is doing more than 1 sector at a time is you can't ask for more than 127 at a time, and you can't pass a cylinder boundary. At the moment i just check for the former, but not the latter.
The easiest solution is to just load 1 sector at a time, which is slower but how much slower? Boot sector speed is not really important, But when your working a with a real mechanical floppy drive, stopping and starting it seems like it could add considerable delays. How bad is it? would i get noticeable slowdown when using a real floppy if i loaded one sector at a time?
If it turns out loading 1 sector at a time is only very slightly slower then i'll quite happily do that instead. As it would simply the code and allow me to have higher quality and more debug messages.
Heres my bootsector is anyone is interested:
Code: Select all
use16
org 0x7C00
jmp short start ;shorts should just be an offset so cs being 0 or 7C0 shouldnt matter
nop ;pad the jump to 3 bytes so bpb is in the right place
;--------------------------
; BIOS parameter block
;--------------------------
constantBpbBytesPerSector = 512 ;i set some of the properties using constants so i can do preprocessor maths
constantBpbSectorsPerCluster = 1
constantBpbReservedSectors = 1
constantBpbNumberOfFATs = 2
constantBpbRootEntries = 224
constantBpbSectorsPerFAT = 9
constantBpbSectorsPerTrack = 18
constantBpbHeadsPerCylinder = 2
constantBsDriveNumber = 0
bpbOEM db "My OS "
bpbBytesPerSector: dw constantBpbBytesPerSector
bpbSectorsPerCluster: db constantBpbSectorsPerCluster
bpbReservedSectors: dw constantBpbReservedSectors
bpbNumberOfFATs: db constantBpbNumberOfFATs
bpbRootEntries: dw constantBpbRootEntries
bpbTotalSectors: dw 2880
bpbMedia: db 0xF8
bpbSectorsPerFAT: dw constantBpbSectorsPerFAT
bpbSectorsPerTrack: dw constantBpbSectorsPerTrack
bpbHeadsPerCylinder: dw constantBpbHeadsPerCylinder
bpbHiddenSectors: dd 0
bpbTotalSectorsBig: dd 0
bsDriveNumber: db constantBsDriveNumber
bsUnused: db 0
bsExtBootSignature: db 0x29
bsSerialNumber: dd 0xa0a1a2a3
bsVolumeLabel: db "My OS "
bsFileSystem: db "FAT12 "
;--------------------------
; Variables
;--------------------------
startOfClusterQueue dd 0x0
endOfClusterQueue dd 0x0
currentCluster dd 0x0
imageName db "KERNEL BIN"
errorFindingFile db 0x0D, 0x0A, "KERNEL.BIN Not Found!", 0x0D, 0x0A, 0x00
;--------------------------
; Entry point
;--------------------------
start:
jmp 0x0:@F ;make sure cs is 7C0 as some BIOS's do 0
@@:
cli
xor eax,eax
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov esp,0xFFFF
;--------------------------
; Load FAT and ROOT
;--------------------------
LBA_OF_FATROOT = constantBpbReservedSectors
SIZE_OF_FATROOT = (constantBpbNumberOfFATs*constantBpbSectorsPerFAT)+((0x20*constantBpbRootEntries)/constantBpbBytesPerSector)
CYLINDER_OF_FATROOT = (LBA_OF_FATROOT/constantBpbSectorsPerTrack)/constantBpbHeadsPerCylinder
HEAD_OF_FATROOT = (LBA_OF_FATROOT/constantBpbSectorsPerTrack) MOD constantBpbHeadsPerCylinder
SECTOR_OF_FATROOT = (LBA_OF_FATROOT MOD constantBpbSectorsPerTrack)+1
@@:
mov eax,(0x2 SHL 8) + SIZE_OF_FATROOT ;function 2 in ah, size in al
mov ebx,0x7E00 ;where to read it in es:bx
mov ecx,(CYLINDER_OF_FATROOT SHL 8) + SECTOR_OF_FATROOT ;put sector in cl, cylinder in ch
mov edx,(HEAD_OF_FATROOT SHL 8) + constantBsDriveNumber ;put head in dh, drive num in dl
int 0x13
jnc @F
xor eax,eax ;if error then reset and try again
int 0x13
jmp @B
@@:
;--------------------------
; Get first cluster
;--------------------------
mov edi,0x7E00+((constantBpbNumberOfFATs*constantBpbSectorsPerFAT)*512) ;offset to the root
mov ecx,constantBpbRootEntries
checkRootEntry: ;search all root entries at location of root directory
push ecx
mov ecx,0xB ;search 11 chars for imageName match
mov esi,imageName
push edi
rep cmpsb
pop edi
je clusterFound
add edi,0x20
pop ecx
loop checkRootEntry
mov si,errorFindingFile ;if file is not found then print an error message and hlt
call print
hlt
clusterFound:
xor edx,edx
mov dx,WORD[edi+0x001A] ;save starting cluster of boot image
mov [startOfClusterQueue],edx ;file's first cluster
mov [currentCluster],edx
mov ebx,0x500 ;where to save file
;--------------------------
; buildClusterQueue
;--------------------------
buildClusterQueue: ;load a queue of sequential clusters until one unsequential cluster is found (or maximun count is exceeded)
mov eax,[currentCluster] ;load next cluster
mov [endOfClusterQueue],eax ;update end of queue
mov ecx,eax
mov edx,eax
shr edx,0x1
add ecx,edx
xor edx,edx
mov dx,WORD[0x7E00 + ecx]
test eax,0x1
jnz oddCluster
evenCluster:
and edx,111111111111b
jmp clusterCalculated
oddCluster:
shr edx,0x4
clusterCalculated:
mov [currentCluster],edx
add eax,constantBpbSectorsPerCluster ;if the cluster is not sequential jump ahead and load all queud clusters before it (between start and previous)
cmp eax,[currentCluster]
jne readQueuedClusters
sub eax,[startOfClusterQueue] ;elif the amount of queued clusters exceeds 255 then load queud clusters
cmp eax,((127)/constantBpbSectorsPerCluster)-1 ;minus one because read queued adds one, must not write past es register, must not cross cylinder boundary
jae readQueuedClusters
jmp buildClusterQueue ;else load next cluster
;--------------------------
; readQueuedClusters
;--------------------------
readQueuedClusters: ;load queued clusters between start and previous and if current cluster is not EOF jump back and load next set
mov eax,[startOfClusterQueue]
sub eax,2
add eax,((0x20*constantBpbRootEntries)/constantBpbBytesPerSector)+(constantBpbNumberOfFATs*constantBpbSectorsPerFAT)+constantBpbReservedSectors
xor edx,edx
div WORD[bpbSectorsPerTrack]
inc edx
mov cl,dl
xor edx,edx
div WORD[bpbHeadsPerCylinder]
mov dh,dl
mov ch,al
mov dl,constantBsDriveNumber
mov eax,[endOfClusterQueue] ;calculate how many queued there are
sub eax,[startOfClusterQueue]
add eax,constantBpbSectorsPerCluster
mov ah,0x2
int 0x13
jnc clustersRead
xor eax,eax
int 0x13
jmp readQueuedClusters
clustersRead:
and eax,11111111b ;adjust ebx
mov ecx,constantBpbBytesPerSector*constantBpbSectorsPerCluster
mul ecx
add ebx,eax
mov eax,[currentCluster] ;setup next pass if the next cluster is not EOF
mov [startOfClusterQueue],eax
cmp eax,0xFF0
jb buildClusterQueue
;--------------------------
; jump to kernel
;--------------------------
mov edx,0x3F2 ;turn off floppy motors
mov eax,0xC
out dx,al
jmp 0x500 ;jump to kernel
;--------------------------
; Print Routine
;--------------------------
print:
lodsb
or al,al
jz @F
mov ah,0xE
int 0x10
jmp print
@@:
ret
;--------------------------
; Pad + signature
;--------------------------
TIMES 510-($-$$) db 0 ;pad to 512 bytes
dw 0xAA55 ;bootable signature