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) * spt
S = (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
wherelsnFAT
is
the number of the logical sector where the FAT starts,hs
is the
number of hidden sectors andrs
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
wherelsnFAT[i]
is the number of the logical
sector where the ith FAT starts (0 =< i <
N).hs
is the number of hidden sectorsrs
is the
number of reserved sectorsspf
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
wherelsn
is the number of the
logical sector where the cluster startshs
is the number of
hidden sectorsrs
is the number of reserved
sectorsN
is the number of FATsspf
is the
number of sectors per FATnsird
is the number of sectors
occupied by the root directorycln
is the cluster
numberspc
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
wherensird
is the number of sectors occupied by
the Root Directorynerd
is the number of entries in the Root
Directorybps
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
wherelsn
is the number of the logical
sector of the ith sector of the cluster (0 =< i <
spc)hs
is the number of hidden sectorsrs
is
the number of reserved sectorsN
is the number of
FATsspf
is the number of sectors per FATnsird
is the number of sectors occupied by the root directorycln
is
the cluster numberspc
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 |