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.
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.
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.
For floppy disks, the serial numbers of the physical sectors are assigned as follows :
| 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 |
lsn = S-1 + (T*nh + H) * sptS = (lsn mod spt) + 1
H = (lsn / spt) mod nh
T = lsn / (spt*nh)
S is the physical sector numberH is the head numberT is the track numberlsn is the logical sector numberspt is the number of sectors per tracknh is the number of heads/ is an integer divisionmod is the modulo operationHard 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.
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.
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.
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.
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 |
| 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. |
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
| 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 |
| 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 | |
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.
| 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 |