Loading a file 1 sector at a time

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
calpol2004
Posts: 4
Joined: Tue Aug 03, 2010 2:52 pm

Loading a file 1 sector at a time

Post by calpol2004 »

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:

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
egos
Member
Member
Posts: 612
Joined: Fri Nov 16, 2007 1:59 pm

Re: Loading a file 1 sector at a time

Post by egos »

Display "Loading..." and relax (read sectors one by one).
If you have seen bad English in my words, tell me what's wrong, please.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Loading a file 1 sector at a time

Post by Combuster »

The bios isn't stupid. The floppy drive is kept running until several seconds after the last request. Also, the BIOS does not need to seek for every request.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Loading a file 1 sector at a time

Post by bewing »

As Combuster said, you don't need to worry about the motor, or seeking. If that really were a problem, the difference would be very large -- but it's not.
Answer 1) testing time tradeoffs is part of what osdeving is about. Try it both ways, time it, and find out.
Answer 2) if your code happens to cause a worst-case scenario for sector interleave timing, it could be a factor of 17 slower. In the real world, I'll guess that it's a factor of 2. But it's a one time thing, and the absolute times are short, so it's really not worth worrying about.
roboman
Posts: 24
Joined: Fri Feb 27, 2009 9:41 am
Location: USA
Contact:

Re: Loading a file 1 sector at a time

Post by roboman »

The old dos trick is to have a format program that will interleave the sectors. Normally the sectors are on the disk in order: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18. If you put them on the disk in the order 1, 10, 2, 11, 3 , 12, 4, 13, 5, 14, 6, 15, 7, 16, 8, 18, 9, 18... you have a little extra time between sector reads and by the time you figure out what one you want next, the most likely next one is getting ready to come up under the read head. It use to also help on the 20 meg hdd, but then 20 meg got replaced with 20 gig and is getting ready to be replaced with 20 t, and at speeds that interleave probably doesn't matter. Really, it's a floppy and just isn't going to be fast, the disk doesn't spin that fast and there are only 18-21 sectors per cylinder on an HD floppy disk. But it's all faster then tape or punch cards :)
Post Reply