Last update : 26/06/1999

The FAT filesystem

1. Introduction

This page doesn't give a full description of the FAT filesystem. It only gives some details you need to understand because they are used in some of my programs.
A good description of the FAT filesystem is given in The Indispensable PC Hardware Book.

2. The original FAT filesystem

There are several versions of the FAT filesystem. The one I describe in this chapter is the first one. Later versions include VFAT and FAT32. In this version, the files can only have a name of 8 bytes maximum plus an extension of 3 bytes maximum.

2.1. Logical sectors

From a physical viewpoint, the data on floppy disks or hard disks is organized in tracks, heads and sectors. You can find the data on the disk if you know the track, the head and the sector where it is. The number of tracks, heads and sectors is dependent on the type of disk. FAT hides theses differences between disks by using the concept of logical sector. The physical sectors are assigned a serial number. This serial number is the logical sector number corresponding to the physical sector.

2.1.1. Floppy disks

For floppy disks, the serial numbers of the physical sectors are assigned as follows :

An example will clarify the method :
Imagine a 3"1/2 1.44 Mb floppy disk. Such floppies have 80 tracks, 18 sectors per track and 2 sides (heads).
Physical sector Logical sector
track 0, head 0, sector 1 0
track 0, head 0, sector 2 1
track 0, head 0, sectors 3 to 18 2-17
track 0, head 1, sector 1 18
track 0, head 1, sectors 2 to 18 19-35
track 1, head 0, sector 1 36
track 1, head 0, sectors 2 to 18 37-53
track 1, head 1, sectors 1 to 18 54-71
tracks 2 to 79, all heads and sectors 72-2879

However, the only way to access data on the disk is by means of the track, head and physical sector number. The informations needed to do the conversion between logicals sectors and physical sectors are the number of sectors per track and the number of heads. These numbers are stored in the boot sector.
The routine that must convert a logical sector number into a track, head and physical sector number or conversely can load the boot sector (its position is well defined on the disk) to retrieve the number of sectors per track and heads and then use the following formulas to do the conversion.
S is the physical sector number
H is the head number
T is the track number
lsn is the logical sector number
spt is the number of sectors per track
nh is the number of heads
/ is an integer division
mod is the modulo operation

2.1.2. Hard disks

Hard disk can be divided in several partitions. Each partition is treated as an individual FAT volume. This introduces a minor change to the assignment of the logical sector numbers. The logical sector number 0 is not assigned to the first physical sector of the disk but to the first sector of the partition. The rules for assigning the next serial number remains unchanged.

2.2. General organization of a FAT disk

A FAT disk has 5 important areas :
  1. Boot sector and other reserved sectors
  2. File Allocation Table (FAT)
  3. One or more copies of the FAT
  4. Root Directory
  5. Data area for the files and the subdirectories
These areas are always in this order on the disk but their length can change from one disk to another. The boot sector has the informations necessary to compute their lengths.

2.3. The boot sector

The boot sector is the name for the first sector of a floppy disk (head 0, track 0, sector 1) or for the first sector of a partition in the case of a hard disk. This location is accessible without knowing the exact type of the disk. This makes it appropriate for storing the disk parameters, which will then give enough information to access the other parts of the disk.

The following table shows the structure of the boot sector :

Offset Content Length/Type
0h Jump instruction to the boot routine 3 bytes
3h OEM name and number 8 bytes
0Bh Bytes per sector 1 WORD
0Dh Sectors per cluster 1 BYTE
0Eh Reserved sectors 1 WORD
10h Number of FATs 1 BYTE
11h Number of root directory entries 1 WORD
13h Number of logical sectors in the volume 1 WORD
15h Medium descriptor byte 1 BYTE
16h Sectors per FAT 1 WORD
18h Sectors per track 1 WORD
1Ah Number of heads 1 WORD
1Ch Number of hidden sectors 1 WORD

Jump instruction to the boot routine :
The first three bytes of the boot sector are either a near jump instruction (E9xxxxh) or a short jump followed by a NOP instruction EBxx90h. This instruction makes a jump to the boot code located somewhere in the boot sector. This instruction is necessary because the execution of a PC starts with the first bytes of the boot sector (see Boot sequence of a PC).

OEM name and number :
This field contain an identity that caracterizes the manufacturer and operating system.

Bytes per sector :
This word gives the number of bytes per sector. In the PC world, this seems to be always 512.

Sectors per cluster :
This byte gives the number of sectors per cluster. You'll learn what is a cluster later.

Reserved sectors :
This word gives the number of reserved sectors. The boot sector is included in this count. This value is usually 1. DOS disks always use 1 reserved sector : the boot sector. However, there doesn't seem to be any problem to handle other values.

Number of FATs :
This bytes gives the number of FATs (File Allocation Table). Only one FAT is needed but, as the data in it is very sensitive, one or more copies are written on the disk. The number of FATs is usually two.

Number of root directory entries :
This word gives the number of root directory entries available. The number of root directory entries is fixed. This explains why you can't have more than a certain amount of files and/or subdirectories in the root directory.

Number of logical sectors in the volume :
This word gives the number of logical sectors on the disk or in the partition in the case of a hard disk. The maximum number of sectors in the volume is therefore 65535*512 for a disk with 512 bytes per sector. This gives a maximum of about 33 Mb. This is not enough for the currently available hard disks. This is the reason why the new field Big number of logical sectors in the volume has been added. If the word at offset 13h is 0, this means that Big number of logical sectors in the volume gives the number of sectors of the volume.

Medium descriptor byte :
The BIOS and DOS use this entry to determine the disk type. See Appendix A for a table with the valid values for this byte. As some values are used for several disk geometries, it is better to use the detailed information of the boot sector than the medium descriptor byte.

Sectors per FAT :
This word gives the number of sectors per FAT.

Sectors per track :
This word gives the number of sectors per track.

Number of heads :
This word gives the number of read/write heads.

Number of hidden sectors :
This word gives the number of hidden sectors.

2.4. The File Allocation Table and clustering

The File Allocation Table or FAT is the structure used by the filesystem to know where the files have been saved on the disk. The FAT links a file to the sectors of the disk where its content has been stored.
There are two versions of FAT, one with entries of 12 bits and another with entries of 16 bits. If the entries where used to store a logical sector number, the maximum number would be 65535 and if the sectors have 512 bytes, this leads to a maximum disk capacity of 33Mb. To overcome this limitation, sectors have been grouped into clusters.

2.4.1. Clusters

A cluster is a set of 1 or more sectors and it is the smallest storage unit. A cluster is either free or used, it can't be shared by several files. Clustering allows the increase of storage space because the FAT refers to cluster numbers instead of logical sector numbers. For instance, if you put 4 sectors into each cluster, each cluster will hold 2048 bytes, the maximum cluster number is 65535 for FAT16 and this gives a capacity of about 120 Mb.
The drawback to this method is waste of storage space. If a cluster is only partially used by a file, the remaining part of the cluster contains no useful data but is unusable for other files. If you use clusters of 1 sector and the sector size is 512 bytes, this leads to a waste of maximum 511 bytes (when the file uses 1 byte of the cluster). But when the cluster size is 4 sectors with the same sector size, this leads to a waste of maximum 2047 bytes. The more sectors you group into one cluster, the more space you waste. This is especially annoying when you have a lot of small files.

2.4.2. Structure of the FAT

The FAT is a large array of cluster numbers. Each entry of this array is assigned a cluster : cluster number 2 is assigned to FAT entry number 2, cluster number 3 is assigned to FAT entry number 3 and so on. The enumeration starts with 2 because the first two entries are reserved. They are occupied by a copy of the Medium Descriptor Byte followed by 2 bytes (12-bit FAT) or 3 bytes (16-bit FAT) with the value FFh. As a consequence, the number of the first cluster is 2 and not 0.
The value stored in an entry of the table indicates the current use of the related cluster. The following table shows the possible values of an entry and their meanings.

12-bit 16-bit Meaning
000h 0000h free
FF0h to FF6h FFF0h to FFF6 reserved
FF7h FFF7h bad sector, unusable
FF8h to FFFh FFF8h to FFFFh last cluster of a file/directory
Any other value Any other value next cluster of a file/directory

A file occupies a given number of clusters. The question is which ones and in what order. The solution is to build an ordered list whose elements are the clusters occupied by the file.
The FAT allows to build such a list. If you know the number of a cluster of the file (we'll call this cluster the current cluster), the next cluster of the file is found by looking at the value of the FAT entry relative to the current cluster. As indicated by the above table, if this value is not a value with a special meaning, it is the number of the next cluster. In the FAT entry relative to this next cluster, the number of the following cluster will be found and so on. Using the FAT entries, we build an ordered list of all the clusters occupied by the file. This ordered list is called a cluster chain. We know when we have reached the end of the chain when we encounter the value FF8h or FFF8h in a FAT entry, which means that the current cluster is the last of the chain.
An info is still missing to allow us to build the chain : the first cluster of the file. Whithout it, we don't know where to start. It's the Root Directory (see Root Directory later) that keeps the number of the starting cluster of the file.

Example :
This example shows what the FAT looks like for the storage of a 1200 bytes file with 1 sector per cluster and 512 bytes per sector. The FAT is supposed to be a 12-bit FAT and the first cluster of the file is cluster number 2.
The values of the first bytes of the FAT are : F0 FF FF 03 40 00 FF 0F 00 00 00 ...
We must group these bytes into entries of 12 bits. This gives :
F0 FF FF The medium descriptor byte followed by two bytes with value FFh.
003 Entry relative to second cluster : the cluster following cluster 2 is cluster 3.
004 Entry relative to third cluster : the cluster following cluster 3 is cluster 4.
FFF Entry relative to fourth cluster : the cluster 4 is the last of the chain.

The file is thus stored in clusters 2, 3 and 4.

Note : The byte ordering is assumed to be little endian. This is why the three bytes 03 40 00 give the two entries of 12 bits : 003 and 004. In little endian, the most significant bits are stored at the higher memory addresses. Therefore the memory representation of these three bytes is : 11000000 00000010 00000000. This gives the first 12 bits : 1100 0000 0000. In little endian, this is the number 3. The next 12 bits are : 0010 0000 0000. In little endian, this is the number 4.

2.4.3. Location of the FATs and the clusters

To access a file, you still need to learn where to find the FAT and a given cluster.
The first FAT is right after the last reserved sector. The following formula gives the logical sector number where the first FAT starts.

lsnFAT = hs + rs
where
lsnFAT is the number of the logical sector where the FAT starts,
hs is the number of hidden sectors and
rs is the number of reserved sectors.
These two values can be found in the boot sector.

There are N copies of the FAT on the disk. N is given by the boot sector. The following formula gives the number of the logical sector where the ith copy starts.

lsnFAT[i] = hs + rs + i*spf
where
lsnFAT[i] is the number of the logical sector where the ith FAT starts (0 =< i < N).
hs is the number of hidden sectors
rs is the number of reserved sectors
spf is the number of sectors per FAT

Each copy occupies the number of sectors given by the boot sector.

To obtain the logical sector number of the first sector of a cluster, the following formula can be used :

lsn = hs + rs + N*spf + nsird + (cln - 2)*spc
where
lsn is the number of the logical sector where the cluster starts
hs is the number of hidden sectors
rs is the number of reserved sectors
N is the number of FATs
spf is the number of sectors per FAT
nsird is the number of sectors occupied by the root directory
cln is the cluster number
spc is the number of sectors per cluster

The formula is obvious when you know that the first cluster has number 2 and that it comes right after the Root Directory. The clusters follow eachother on the disk.
Only the term nsird in the above formula is not straightforward to compute. The following formula gives an easy way to do so.

nsird = (nerd*32 + bps - 1) / bps
where
nsird is the number of sectors occupied by the Root Directory
nerd is the number of entries in the Root Directory
bps is the number of bytes per sector

Once you've got the number of the logical sector of the first sector of a cluster, the other sectors of the cluster are obtained by incrementing the logical sector number because the sectors of a cluster follow eachother. As a consequence, the following formula gives the logical sector number of the ith sector of cluster cln.

lsn = hs + rs + N*spf + nsird + (cln - 2)*spc + i
where
lsn is the number of the logical sector of the ith sector of the cluster (0 =< i < spc)
hs is the number of hidden sectors
rs is the number of reserved sectors
N is the number of FATs
spf is the number of sectors per FAT
nsird is the number of sectors occupied by the root directory
cln is the cluster number
spc is the number of sectors per cluster

2.5. The Root Directory

Structure of a directory entry
Offset Content Length (bytes)
00 File Name 8
08 File Extension 3
0B File Attribute 1
0C Reserved 10
16 Time of Creation/Last Change 2
18 Date of Creation/Last Change 2
1A First Cluster of the File 2
1C File Length 4

Example of a boot sector

Here's a copy of the boot sector of a 1.44 Mb floppy formatted by Windows 95.

Offset Hexadecimal code Instruction or data
00 EB 3E 90
03 29 3E 54 29 7D 49 48 43 OEM name and number
0B 00 02 Bytes per sector.
The number of bytes per sector is 200h (512).
0D 01 Sectors per cluster
There is 1 sector per cluster
0E 01 00 Reserved sectors
There is 1 reserved sector. This means that there are no other sectors reserved beside the boot sector.
10 02 Number of FATs
There are 2 FATs. This means 1 original and 1 copy.
11 E0 00 Number of root directory entries
There are 00E0h (224) entries available in the root directory.
13 40 0B Number of logical sectors in the volume
There are 0B40h (2880) logical sectors on the disk. The capacity of the disk is thus 2880*512 = 1474560 bytes.
15 F0 Medium descriptor byte
The code F0 stands for a double sided 3"1/2 disk with 80 tracks and 18 sectors per track. This corresponds to a 1.44 Mb floppy. This gives 2*80*18 = 2880 sectors, which is consistent with the number written at offset 13h
16 09 00 Sectors per FAT
Each FAT occupies 9 sectors. As there are 2 FATs, 18 sectors are used to store them.
18 12 00 Sectors per track
There are 0012h (18) sectors per track. This is consistent with the Medium Descriptor Byte
1A 02 00 Number of heads
There are 2 heads. This is consistent with the Medium Descriptor Byte.
1C 00 00 00 00 Number of hidden sectors
There are no hidden sectors. The length of this field is 4 bytes because the byte at offset 26h is equal to 29h. If it was not the case, this field would be only 2 bytes long.
20 00 00 00 00 This field is only valid if the byte at offset 26h is 29h and if the number of sectors (word at offset 13h) is 0. In this case, it gives the number of sectors in the volume. It is used when the number of sectors is too big to be stored in a word.
24 00 This is the drive number. The DOS boot code uses the value stored here to know if the boot code is located on a floppy disk or on a hard disk. It needs to know this in order to know where to load the IO.SYS file from. The value of 0 here indicates the A: drive.
25 00 Reserved
26 29 Extended Boot Record Signature
If the value stored here is 29h, it indicates that the fields at offset 1Eh and above are valid.
27 4D 24 F3 17 This a serial number. It has nothing to do with the serial number of the floppy given by the manufacturer. I don't know what Windows 95 writes here but it changes each time the disk is formatted.
2B 4E 4F 20 4E 41 4D 45 20 20 20 20 This is the volume label. In this case, the string is "NO NAME".
36 46 41 54 31 32 20 20 20 This is a string which identifies the filesystem. In this case, the string is "FAT12".
3E F1 7D This word is a pointer to the name of the file which will be loaded by the boot code. As the boot code is loaded at 0000:7C00h, the value 7DF1h points to offset 7DF1h - 7C00h = 1F1h of the boot sector. This is the string : "WINBOOT.SYS".
This is the start of the boot code. The short jump at offset 0 points to offset 40h.
40 FA cli
41 33 C9 xor cx,cx
43 8E D1 mov ss,cx
45 BC FC 7B mov sp,7BFC
48 16 push ss
49 07 pop es
4A BD 78 00 mov bp,0078
4D C5 76 00 lds si,[bp]
50 1E push ds
51 56 push si
52 16 push ss
53 55 push bp
54 BF 22 05 mov di,0522
57 89 7E 00 mov [bp],di
5A 89 4E 02 mov [bp+02],cx
5D B1 0B mov cl,0B
5F FC cld
60 F3 A4 rep movsb
62 06 push es
63 1F pop ds
64 BD 00 7C mov bp,7C00
67 C6 45 FE 0F mov [byte ptr di-02],0F
6B 8B 46 18 mov ax,[bp+18]
6E 88 45 F9 mov [di-07],al
71 FB sti
72 38 66 24 cmp [bp+24],ah
75 7C 04 jl 7B
77 CD 13 int 13
79 72 3C jc B7
7B 8A 46 10 mov al,[bp+10]
7E 98 cbw
7F F7 66 16 mul [word ptr bp+16]
82 03 46 1C add ax,[bp+1C]
85 13 56 1E adc dx,[bp+1E]
88 03 46 0E add ax,[bp+0E]
8B 13 D1 adc dx,cx
8D 50 push ax
8E 52 push dx
8F 89 46 FC mov [bp-04],ax
92 89 56 FE mov [bp-02],dx
95 B8 20 00 mov ax,0020
98 8B 76 11 mov si,[bp+11]
9B F7 E6 mul si
9D 8B 5E 0B mov bx,[bp+0B]
A0 03 C3 add ax,bx
A2 48 dec ax
A3 F7 F3 div bx
A5 01 46 FC add [bp-04],ax
A8 11 4E FE adc [bp-02],cx
AB 5A pop dx
AC 58 pop ax
AD BB 00 07 mov bx,0700
B0 8B FB mov di,bx
B2 B1 01 mov cl,01
B4 E8 94 00 call 14B
B7 72 47 jc 100
B9 38 2D cmp [di],ch
BB 74 19 je D6
BD B1 0B mov cl,0B
BF 56 push si
C0 8B 76 3E mov si,[bp+3E]
C3 F3 A6 repe cmpsb
C5 5E pop si
C6 74 4A je 112
C8 4E dec si
C9 74 0B je D6
CB 03 F9 add di,cx
CD 83 C7 15 add di,0015
D0 3B FB cmp di,bx
D2 72 E5 jl B9
D4 EB D7 jmp AD
D6 2B C9 sub cx,cx
D8 B8 D8 7D mov ax,7DD8
DB 87 46 3E xchg [bp+3E],ax
DE 3C D8 cmp al,D8
E0 75 99 jne 7B
E2 BE 80 7D mov si,7D80
E5 AC lodsb
E6 98 cbw
E7 03 F0 add si,ax
E9 AC lodsb
EA 84 C0 test al,al
EC 74 17 je 105
EE 3C FF cmp al,FF
F0 74 09 je FB
F2 B4 0E mov ah,0E
F4 BB 07 00 mov bx,0007
F7 CD 10 int 10
F9 EB EE jmp E9
FB BE 83 7D mov si,7D83
FE EB E5 jmp E5
100 BE 81 7D mov si,7D81
103 EB E0 jmp E5
105 33 C0 xor ax,ax
107 CD 16 int 16
109 5E pop si
10A 1F pop ds
10B 8F 04 pop [word ptr si]
10D 8F 44 02 pop [word ptr si+02]
110 CD 19 int 19
112 BE 82 7D mov si,7D82
115 8B 7D 0F mov di,[di+0F]
118 83 FF 02 cmp di,0002
11B 72 C8 jl E5
11D 8B C7 mov ax,di
11F 48 dec ax
120 48 dec ax
121 8A 4E 0D mov cl,[bp+0D]
124 F7 E1 mul cx
126 03 46 FC add ax,[bp-04]
129 13 56 FE adc dx,[bp-02]
12C BB 00 07 mov bx,0700
12F 53 push bx
130 B1 04 mov cl,04
132 E8 16 00 call 14B
135 5B pop bx
136 72 C8 jc 100
138 81 3F 4D 5A cmp [word ptr bx],5A4D
13C 75 A7 jne E5
13E 81 BF 00 02 42 4A cmp [word ptr bx+0200],4A42
144 75 9F jne E5
146 EA 00 02 70 00 jmp 0070:0200
14B 50 push ax
14C 52 push dx
14D 51 push cx
14E 91 xchg cx,ax
14F 92 xchg dx,ax
150 33 D2 xor dx,dx
152 F7 76 18 div [word ptr bp+18]
155 91 xchg cx,ax
156 F7 76 18 div [word ptr bp+18]
159 42 inc dx
15A 87 CA xchg dx,cx
15C F7 76 1A div [word ptr bp+1A]
15F 8A F2 mov dh,dl
161 8A 56 24 mov dl,[bp+24]
164 8A E8 mov ch,al
166 D0 CC ror ah,1
168 D0 CC ror ah,1
16A 0A CC or cl,ah
16C B8 01 02 mov ax,0201
16F CD 13 int 13
171 59 pop cx
172 5A pop dx
173 58 pop ax
174 72 09 jc 17F
176 40 inc ax
177 75 01 jne 17A
179 42 inc dx
17A 03 5E 0B add bx,[bp+0B]
17D E2 CC loop 14B
17F C3 ret
180 03
181 18
182 01
183 27
184 0D 0A 44 69 73 71 75 65 20 6E 6F 6E 20 73 79 73 74 65 6D 65 20 FF The string : "Disque non systeme" preceeded by a carriage return and followed by FFh
19A 0D 0A 45 72 72 65 75 72 20 64 27 45 2F 53 20 20 FF The string : "Erreur d'E/S" preceeded by a carriage return and followed by FFh.
1AB 0D 0A 52 65 6D 70 6C 61 63 65 7A 2D 6C 65 20 65 74 20 61 70 70 75 79 65 7A 20 73 75 72 20 75 6E 65 20 74 6F 75 63 68 65 20 20 0D 0A 00 The string : "Remplacez-le et appuyez sur une touche", preceded by a carriage return and followed by a carriage return and 0.
1D8 49 4F 20 20 20 20 20 20 53 59 53 The filename : "IO.SYS"
1E3 4D 53 44 4F 53 20 20 20 53 59 53 The filename : "MSDOS.SYS"
1EE 80 01 00
1F1 57 49 4E 42 4F 4F 54 20 53 59 53 The filename : "WINBOOT.SYS"
1FC 00 00 55 AA
Note : when a jump is made in this table, the value that follows the instruction refers to the offset in the table, you don't have to add this value to the offset of the instruction (i.e. je 48h sends you to offset 48h).

40-49 :
The code disables the interrupts while it initializes the segment registers. ss = es = 0 and sp = 7BFCh. The boot code is loaded by the bootstrap at 0000:7C00, the stack begins 4 bytes before this position. These 4 bytes, at 0000:7BFC will be used later.

4A-4D :
The pointer at 0000:0078 is loaded into ds:si. This is the pointer to the drive parameter table.

50-53 :
ds, si, ss and bp are pushed onto the stack. They don't seem to be popped afterwards so I don't know what this is used for.

54-5A :
The address of the drive parameter table is now 0000:0522.

5D-60 :
The drive parameter table, whose length is 12 bytes, is moved from its old location to its new location (0000:0522).

62-64 :
ds = 0 and bp = 7C00h. bp points to the start of the image of the boot sector. The value of bp won't change anymore, so you can keep in mind that bp points to the boot sector.

67-6E :
This portion of the code sets the parameters of the drive parameters table in function of the data of the boot sector of the disk.
Due to the rep movsb instruction, di points to the end of the drive parameters table.
di-02 points to the 11th byte of the table, this is the head settle time. The head settle time is set to 15 milliseconds.
bp+18 points to the number of sectors per track. di-07 points to the 4th byte of the parameter table, this is the number of sectors per track. This number is set to the number given by the boot sector.

71 :
This reenables the interrupts.

72-75 :
This code is a mystery for me. Anyway, for a floppy, [bp+24h] should be 0 and ah too, and the jump shouldn't be made.

77-79 :
As ah and dl are equal to 0, the call to interrupt 13h recalibrates the floppy drive. If the recalibration fails, a jump is made to the code at B7.

Note : the code that follows is particularly interesting as it shows how to find the start of the Root Directory and the start of the space where the files are stored.

7B-7F :
The number of FATs is transferred into al. al is then sign-extended and ax is multiplied by the number of sectors per FAT. dx:ax is now the number of sectors used to store all the FATs.

82-85 :
The number of hidden sectors is added to dx:ax. The number of hidden sectors occupies 4 bytes because the byte at offset 26h of the boot sector is 29h. Otherwise, it would be only 2 bytes long.

88-92 :
The number of reserved sectors is added to dx:ax. At this stage, dx:ax is the number of the first logical sector of the Root Directory.
ax and dx are pushed onto the stack. They are also stored at 0000:7BFC and 0000:7BFE, the 4 bytes between the bottom of the stack and the boot code.

95-A8 :
ax is loaded with the value 20h (32) because an entry of the Root Directory is 32 bytes long. si is loaded with the number of entries of the Root Directory. After ax has been multiplied by si, dx:ax is the length of the Root Directory measured in bytes.
bx is loaded with the number of bytes per sector.
bx is then added to ax and then ax is decremented by one. This is a little trick. The number of bytes used for the Root Directory is not required to be a multiple of the number of bytes per sector. If it's not a multiple, the last sector is not completely used. But even if the Root Directory only uses one byte of the last sector, this sector must be reserved. By adding the number of bytes per sector - 1 to the number of bytes of the Root Directory, you are sure to get the correct number of sectors after a division by the number of sectors per byte. In our case, the number of entries in the Root Directory is 224 and the number of bytes per sector 512. 7168 bytes are used to store the Root Directory. 7168 is exactly 14*512. 14 sectors are thus needed. If we add 511 (number of bytes per sectors - 1) to 7168, we get 7679. A division by 512 gives 14, which is the result expected. Now imagine we have 225 entries instead of 224. 7200 bytes are needed for the Root Directory. 14 full sectors are used and 32 bytes of the last sector, this means 15 sectors. We add 511 to 7200, this gives 7711. We divide this result by 512 and we get the correct answer :15.
Now let's get back to our code.
We divide dx:ax by bx. ax is now the number of sectors used by the Root Directory.
We add this value to the value stored at 0000:7BFC. The value stored there is now the number of the first logical sector that can be used to store the content of a file.

AB-B7 :
The function at 14Bh loads a logical sector of the disk. dx:ax is the number of the sector, bx is the memory location where the sector will be written and cx is the number of sectors to read.
In this case, we pop ax and dx. If you remember, this is the number of the first logical sector of the Root Directory. bx is set to 0700h. di is also set to 0700h but it is not used by the function at 14Bh. It's for a future use. cl is set to 1. One sector will thus be read from the disk.
If the carry is set, it means there was an error. The code at offset 100h handles this situation. This is why we have the instruction jc 100.

B9-BB :
We go through the Root Directory entries to find a given file. To do so, we use the register di which points to the current position in the Root Directory. Initially, it is 0700h, the start of the Root Directory in memory.
As the content of register ch is 0, we compare [di] to 0. For the FAT filesystem, an entry starting with 0 means the end of the Root Directory. If we encounter a 0, it means that we have gone through all the directory without finding the file. The code at offset 0D6h handles this situation hence the instruction je D6.

BD-C6 :
If the entry doesn't start with 0, we compare it with the filename. We compare the 11 first bytes of the entry with the filename because these are the bytes which hold the filename.
The comparison is made by the repe cmpsb instruction. As a consequence, the registers si and di must hold the start addresses of the strings to compare. di is already pointing to the current entry of the Root Directory and si is loaded with the content of [bp+3E]. This is the word at offset 3Eh of the boot sector. si is thus equal to 1DF7h. This value corresponds to the offset 1F1h of the boot sector because 7DF1h - 7C00h = 1F1h. And at offset 1F1h of the boot sector, we find the string "WINBOOT.SYS". The file we are looking for is thus "WINBOOT.SYS". The register cl must hold the length of the strings : 11 bytes in the present case.
If we have found the file, we jump at offset 112h.
The content of tbe register si saved on the stack and then restored.

C8-C9 :
si is the number of entries in the Root Directory that have not been examined yet. We decrement it by 1 as we have just tested one entry. If si reaches 0, then there are no more entries and the file has not been found. The code at offset D6h handles this situation.

CB-D4 :
The result of the 2 additions is that di now points to the next entry of the Root Directory. di is then compared to bx. This is to make sure that we have not reached the end of the sector of the Root Directory that was loaded into memory by the call to the function at offset 14Bh. As a result of this call, bx points to the end of the loaded sector. If we are not at the end of the loaded sector, we return to offset BDh, where a new comparison is made between the file name and the entry where the new di points to.
If we are at the end of the loaded sector, we must load the next sector. Therefore we return at offset ADh. The registers dx and ax have not been modified since the last call to the function at 14Bh. As a consequence, they hold the number of the next logical sector to load.

D6-E0 :
This code is called when we reach the end of the Root Directory. cx is reset to 0. The word at offset 3Eh of the boot sector is set to 7DD8h. Remember that the offset 3Eh holds the pointer to the file name. We are now looking for another filename. This file name is located at offset 7DD8h - 7C00h = 1D8h of the boot sector. This is the string "IO.SYS".
We compare the old value of offset 3Eh with the new one. If they are the same, it means that we have already done the previous procedure before. We have looked for "IO.SYS" but we haven't found it, so we are here for the second time.
If they are not the same, then we must look for "IO.SYS" in the Root Directory. To do so, we return to the code at offset 7Bh. This means that we are almost doing everything again but with another value of the word at offset 3Eh of the boot sector.

E2 :
We reach this point if the files "WINBOOT.SYS" and "IO.SYS" haven't been found. The register si is loaded with the value 7D80h. This is a pointer to offset 7D80h - 7C00h = 180h of the boot sector. The value stored there is 3.

E5-F9 :
The register ax is loaded with the content of the memory location indicated by si. ax is then added to si. ax is again loaded with the content of [si].
al is tested. If it is equal 0, the code at offset 105h is executed. If it is equal to FFh, the code at offset FBh is executed. If it is not equal to one of these values the content of al is displayed by a call to interrupt 10. After that, we return to offset E9h. The instruction lodsb loads the next character which will then be displayed. This loop only stops when al is loaded with 0 or FFh.


FB-FE :
The register si is loaded with 7D83h and the code at E5 is executed. The content of 0000:7D83h is the content of offset 183h of the boot sector : 27h.

100-103 :
The register si is loaded with 7D81h and the code at E5 is then executed. The content of 0000:7D81h is the content of offset 7D81h - 7C00h = 181h of the boot sector : 18h.

105-110 :
This code is called whenever an error hinders the launch of the operating system. The code waits for the user to hit a key and then restarts the computer. See Interrupt 16h, function 00h and Interrupt 19h for more details about these two BIOS functions.

112-11B :
This code is executed when the file we are looking for has been found. The number of the first cluster of the file is moved into di. The content of di is compared with 0002h. A cluster number is never less than 2. If it is less than 2, we have an error and the code at E5h is executed. It displays the string "Disque non systeme".

11D-146 :
We have found the file and the cluster number is valid. We will load the first 4 sectors of the file into memory. We move di into ax and decrement ax twice. This is because the first cluster of a FAT disk has index 2. We move the number of sectors per cluster into cl. We multiply ax by cx. dx:ax is now the number of sectors occupied by the clusters before the cluster we want to load. We add the number of the logical sector which comes right after the Root Directory, which is the first logical sector of the data area. dx:ax is now the number of the logical sector we want to load. We want to load the 4 first sectors of the file at address 0000:0700h. We move 700h into bx and 04h into cl and we call the function at offset 14Bh. If there was an error, we jump at offset 100h. If there wasn't any error, the content of the four first sectors of the file has been transferred at 0000:0700h.
Important note : The code used here assumes that the four sectors of the file are contiguous. But the file might have been fragmented. In this case, the file will not have been correctly loaded into memory. The file that is loaded by this program must not be fragmented.
The program now test if some words have given values to make sure the correct file has been loaded and then jumps to the memory location where it has loaded the second sector of the file. The boot code has thus finished its job and passed control to the loaded file.

14B-17F :
This code is the code of a function that loads a given number of logical sectors from the disk. The first logical sector to read is given in the registers dx:ax. cx is the number of sectors to read. es:bx is the address where the sectors will be loaded.


Appendix A

These are the values of the Medium Descriptor Byte.

Value Medium
F0h 3"1/2, double sided, 80 tracks, 18 sectors per track
3"1/2, double sided, 80 tracks, 36 sectors per track
F8h Hard disk
F9h 5"1/4, double sided, 80 track, 15 sectors per track
3"1/2, double sided, 80 tracks, 9 sectors per track
FAh 5"1/4, single sided, 80 tracks, 8 sectors per track
3"1/2, single sided, 80 tracks, 8 sectors per track
FBh 5"1/4, double sided, 80 tracks, 8 sectors per track
3"1/2, double sided, 80 tracks, 8 sectors per track
FCh 5"1/4, single sided, 40 tracks, 9 sectors per track
FDh 5"1/4, double sided, 40 tracks, 9 sectors per track
8", double sided, 77 tracks, 26 sectors per track
FEh 5"1/4, single sided, 40 tracks, 8 sectors per track
8", single sided, 77 tracks, 2 sectors per track
8", single sided, 77 tracks, 6 sectors per track
8", single sided, 77 track, 8 sectors per track
FFh 5"1/4, double sided, 40 tracks, 8 sectors per track


[ Return to General Information Index ] [ Return to main index ]

1999 - Xavier Leclercq <xavierleclercq@iname.com>