{"id":1686,"date":"2011-03-15T00:21:00","date_gmt":"2011-03-15T00:21:00","guid":{"rendered":"https:\/\/pjgcreations.co.uk\/wordpress\/?p=46"},"modified":"2017-07-24T08:33:19","modified_gmt":"2017-07-24T07:33:19","slug":"the-fat16-file-system-with-sd-cards","status":"publish","type":"post","link":"https:\/\/www.pjgcreations.co.uk\/the-fat16-file-system-with-sd-cards\/","title":{"rendered":"The FAT16 File System (with SD cards)"},"content":{"rendered":"

I have been working on a\u00a0 project where the client requires their device to have it\u2019s firmware updated from a Secure Digital (SD) Card.<\/p>\n

The standard way to store and access data on SD cards is to use the MS FAT File System.<\/p>\n

There are several libraries available dotted around the Internet which allow developers to access SD using FAT. However, if you are on a tight program space budget, such as in the Boot-Loader of an embedded Microcontroller, then you may need to program the FAT File System access manually.<\/p>\n

This process is reasonably difficult to accomplish in raw code as there are quite a few steps to perform before you gain access to the actual file you are after.<\/p>\n

I\u2019ll outline here the basic features of FAT16, which for the purposes of this example, will be the simplest to begin with.<\/p>\n


\n

The FAT16 Layout<\/h3>\n

The layout of the MS FAT16 File System can be seen below;<\/p>\n

\"FAT16<\/a><\/p>\n

The Master Boot Record (MBR):
<\/h3>\n
\n\n\n\n\n
Offset<\/strong><\/td>\n0x0000<\/td>\n<\/tr>\n
Size<\/strong><\/td>\n512 Bytes<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n

\u00a0<\/p>\n

\u00a0<\/p>\n

The Master Boot Record is the first location that the Host File System parses in order to navigate the File Structure of the SD Card. It is always located at Sector zero and not only sets out the addressing parameters of the File System but also contains operating code run by the host at startup.<\/p>\n

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Address<\/strong>
(Offset)<\/td>\n
Item<\/strong><\/td>\nLength<\/strong><\/td>\nNotes<\/strong><\/td>\n<\/tr>\n
0x0000<\/td>\nExecutable Code<\/td>\n446 Bytes<\/td>\nExecuted by the Host system at startup<\/td>\n<\/tr>\n
0x01BE
(00h)<\/td>\n
Partition 1:
State
(Partition 1 Entry Begin)<\/em><\/td>\n
1 Byte<\/td>\nBeginning of the Partition 1 Entry. 16 Bytes Total.<\/p>\n

The State determines if the Partition is Active (80h) or Inactive (00h)<\/td>\n<\/tr>\n

0x01BF
(01h)<\/td>\n
Partition 1:
Start Head<\/td>\n
1 Byte<\/td>\n\u00a0<\/td>\n<\/tr>\n
0x01C0
(02h)<\/td>\n
Partition 1:
Start Sector \/ Cylinder<\/td>\n
2 Bytes<\/td>\nDefines the Start Sector and Cylinder of Partition 1<\/td>\n<\/tr>\n
0x01C2
(04h)<\/td>\n
Partition 1:
Partition Type<\/td>\n
1 Byte<\/td>\nDefines the Type of Partition;
01h = 12bit FAT
04h = 16bit FAT (<32Mb)
05h = Ex MSDOS
06h = 16bit FAT (>32Mb)
0Bh = 32bit FAT
(<2Gb)<\/td>\n<\/tr>\n
0x01C3
(05h)<\/td>\n
Partition 1:
End Head<\/td>\n
1 Byte<\/td>\n\u00a0<\/td>\n<\/tr>\n
0x01C4
(06h)<\/td>\n
Partition 1:
End Sector \/ Cylinder<\/td>\n
2 Bytes<\/td>\nDefines the End Sector and Cylinder of Partition 1<\/td>\n<\/tr>\n
0x01C6
(08h)<\/td>\n
Partition 1:
Start Sector<\/td>\n
4 Bytes<\/td>\nContains the Start Sector Address of Partition 1.
Defined as the number of Sectors between the MBR and the first Sector of the Partition.<\/td>\n<\/tr>\n
0x01CA
(0Ch)<\/td>\n
Partition 1:
Partition Length
(Partition 1 Entry End)<\/em><\/td>\n
4 Bytes<\/td>\nEnd of the Partition 1 Entry. 16 Bytes Total.<\/p>\n

Defines the number of Sectors in the Partition<\/td>\n<\/tr>\n

0x01CE<\/td>\nPartition 2 Entry<\/td>\n16 Bytes<\/td>\nDefined as above<\/td>\n<\/tr>\n
0x01DE<\/td>\nPartition 3 Entry<\/td>\n16 Bytes<\/td>\nDefined as above<\/td>\n<\/tr>\n
0x01EE<\/td>\nPartition 4 Entry<\/td>\n16 Bytes<\/td>\nDefined as above<\/td>\n<\/tr>\n
0x01FE<\/td>\nExecutable Marker<\/td>\n2 Bytes<\/td>\nSet as 0x55AA<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n
\u00a0<\/div>\n

The Boot Sector Entry (BSE):
<\/h3>\n\n\n\n\n\n
Offset<\/strong><\/td>\n1st sector of Partition<\/td>\n<\/tr>\n
Size<\/strong><\/td>\n512 Bytes<\/td>\n<\/tr>\n
Address Location Calculation<\/strong><\/td>\nPartition Start Sector (From MBR)<\/sup><\/em> * Bytes \/ Sector<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

The Boot Sector Entry contains all the pertinent information regarding the Partitions\u2019 structure. The basic layout is shown below;<\/p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Offset<\/strong><\/td>\nItem<\/strong><\/td>\nLength<\/strong><\/td>\nNotes<\/strong><\/td>\n<\/tr>\n
00h<\/td>\nJump Code<\/td>\n3 Bytes<\/td>\nContains executable Machine code to allow the Host System to jump past the BSE<\/td>\n<\/tr>\n
03h<\/td>\nOEM ID<\/td>\n8 Bytes<\/td>\nThe system name which formatted the device<\/td>\n<\/tr>\n
0Bh<\/td>\nBytes Per Sector<\/td>\n2 Bytes<\/td>\nNormally 0x2000
(512 )<\/td>\n<\/tr>\n
0Dh<\/td>\nSectors Per Cluster<\/td>\n1 Byte<\/td>\n\u00a0<\/td>\n<\/tr>\n
0Eh<\/td>\nNo of Reserved Sectors<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
10h<\/td>\nNo of FATs<\/td>\n1 Byte<\/td>\nThe Number of FAT Entries<\/td>\n<\/tr>\n
11h<\/td>\nMax Root Directory Entries<\/td>\n2 Bytes<\/td>\nFor SD Cards this is usually 0x2000
(512)<\/td>\n<\/tr>\n
13h<\/td>\nTotal Sectors in Partition<\/td>\n2 Bytes<\/td>\nOnly set if the device is < 32Mb.
Will likely be unset 0x0000<\/td>\n<\/tr>\n
15h<\/td>\nMedia Type<\/td>\n1 Byte<\/td>\n0xF8 = Hard Drive<\/td>\n<\/tr>\n
16h<\/td>\nSectors Per FAT<\/td>\n2 Bytes<\/td>\nUsed for reaching the Root Directory<\/td>\n<\/tr>\n
18h<\/td>\nSectors Per Track<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
1Ah<\/td>\nNo of Heads<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
1Ch<\/td>\nNo of Hidden Sectors<\/td>\n4 Bytes<\/td>\nNumber of hidden sectors between the start of the disk and the BSE<\/td>\n<\/tr>\n
20h<\/td>\nNo of Sectors in a Partition<\/td>\n4 Bytes<\/td>\nTotal number of Sectors in the Partition<\/td>\n<\/tr>\n
24h<\/td>\nLogical Drive No<\/td>\n1 Byte<\/td>\n0x80 for Hard Drives<\/td>\n<\/tr>\n
25h<\/td>\nHead No<\/td>\n1 Byte<\/td>\nUsually 0x00<\/td>\n<\/tr>\n
26h<\/td>\nExtended Boot Signature<\/td>\n1 Byte<\/td>\nAlways 0x29 denoting that Serial No, Label and Type fields are valid<\/td>\n<\/tr>\n
27h<\/td>\nSerial No<\/td>\n4 Bytes<\/td>\nSerial No of the Device<\/td>\n<\/tr>\n
2Bh<\/td>\nPartition Name<\/td>\n11 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
36h<\/td>\nFAT Type<\/td>\n8 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
3Eh<\/td>\nExecutable Boot Code<\/td>\n448 Bytes<\/td>\nAllows the host to find the first file which boots the system<\/td>\n<\/tr>\n
0x01FE<\/td>\nExecutable Signature<\/td>\n2 Bytes<\/td>\nEnd of BSE<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

FAT Entries (FAT):
<\/h3>\n\n\n\n\n\n
Offset<\/strong><\/td>\nFAT1 = 1st sector after Reserved Sectors<\/td>\n<\/tr>\n
Size<\/strong><\/td>\nSectors Per FAT (From BSE)<\/sup><\/em><\/td>\n<\/tr>\n
Address Location Calculation<\/strong><\/td>\nFAT1 = (Partition Start Sector(From MBR) +<\/sup><\/em> Reserved Sectors(From BSE)<\/sup><\/em>) * Bytes \/ Sector<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

The FAT Entries lay out how the Partitions\u2019 identically sized clusters are laid out. There are normally two FAT Entries in order to help prevent FAT corruption problems.<\/p>\n

The FAT is located in the first sector after the Reserved Sectors. All sectors between Partition Start Sector and the FAT, which includes the Boot Sector Entry, are contained within the Reserved Sectors Allocation.<\/p>\n

So, to find the first FAT we navigate from the beginning of the Device, adding together the Partition Start Sector and the Number of bytes contained in the Reserved Sectors section.<\/p>\n

Root Directory Entry (RDE):
<\/h3>\n\n\n\n\n\n
Offset<\/strong><\/td>\nAfter last FAT<\/td>\n<\/tr>\n
Size<\/strong><\/td>\nNo of Root Entries(From BSE)<\/sup><\/em> * 32<\/td>\n<\/tr>\n
Address Location Calculation<\/strong><\/td>\nRDE = FAT1 Start Address +
((No of FATS(From BSE)<\/sup><\/em> *
Sectors Per FAT(From BSE)<\/sup><\/em>) * Bytes Per Sector)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

The Root Directory contains the Main Directory and File Structure for the entire Partition<\/p>\n

The Root directory is almost identical to a standard File System Directory with only a few differences, namely; The first byte is the first character of the Partition Name. The Create Date and Time are set to 0x00.<\/p>\n

The Root directory appears after the last FAT, and so can be found by adding the Start Address of the first FAT to the number of Bytes occupied by all FAT\u2019s, which is invariably 2 for SD Cards. Thus the formula can be simplifed to;<\/p>\n

\n

RDE = FAT1 Start Address +
\u00a0\u00a0\u00a0\u00a0\u00a0 ((2 * Sectors Per FAT) * 512)<\/em><\/p>\n<\/blockquote>\n

Each entry in the Root Directory consumes a total of 32 Bytes as shown below;<\/p>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Offset<\/strong><\/td>\nItem<\/strong><\/td>\nLength<\/strong><\/td>\nNotes<\/strong><\/td>\n<\/tr>\n
00h<\/td>\nDOS Filename<\/td>\n8 Bytes<\/td>\nFor all other Directories the first byte has special values.<\/td>\n<\/tr>\n
08h<\/td>\nDOS File Extension<\/td>\n3 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
0Bh<\/td>\nFile Attributes<\/td>\n1 Byte<\/td>\n8 bits to denote various attributes<\/td>\n<\/tr>\n
0Ch<\/td>\nNT Case Info<\/td>\n1 Byte<\/td>\nUsed for Windows NT Casing Info<\/td>\n<\/tr>\n
0Dh<\/td>\nCreate Time (ms)<\/td>\n1 Byte<\/td>\n10ms Units<\/td>\n<\/tr>\n
0Eh<\/td>\nCreate Time (Hrs\/Mins\/Secs)<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
10h<\/td>\nCreate Date<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
12h<\/td>\nLast Access Date<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
14h<\/td>\nFile \/ Folder
Start Cluster (High)<\/td>\n
2 Bytes<\/td>\nOnly used in FAT32 Systems<\/td>\n<\/tr>\n
16h<\/td>\nLast Modified Time<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
18h<\/td>\nLast Modified Date<\/td>\n2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
1Ah<\/td>\nFile \/ Folder
Start Cluster (Low)<\/td>\n
2 Bytes<\/td>\n\u00a0<\/td>\n<\/tr>\n
1Ch<\/td>\nFile Size (Bytes)<\/td>\n4 Bytes<\/td>\nFolders will have a File Size of 0x0000<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

Sub Directory \/ Files:
<\/h3>\n\n\n\n\n\n
Offset<\/strong><\/td>\nAfter Root Directory<\/td>\n<\/tr>\n
Size<\/strong><\/td>\nN\/a<\/td>\n<\/tr>\n
Address Location Calculation<\/strong><\/td>\nRoot Folder Address +
(Max Root Directory Entries(From BSE) <\/sup><\/em>* 32) +
((( File \/ Folder Start Cluster(From RDE) <\/sup><\/em>\u2013 2) *
Sectors Per Cluster(From BSE) <\/sup><\/em>) *
Bytes Per Sector(From BSE) <\/sup><\/em>)<\/p>\n

Normally For SD Cards using FAT16;<\/p>\n

Max Root Directory Entries = 512
Bytes Per Sector = 512 <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

A Sub Directory takes on almost exactly the same format as the root Directory above.<\/p>\n

All data stored after the Root Directory is located in the third Data Cluster, thus in order to find the location of all data we must subtract these two clusters from the Start Cluster given in the File \/ Folder Start Cluster Field.<\/p>\n

The first data is found using the formula above, however for SD Cards using FAT16, the maximum Root Directory Entries and Bytes Per Sector are invariably 512. Thus the formula can be simplified to the following;<\/p>\n

\n

First Data = Root Folder Address + 16384 +
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 (((File\/Folder St Cluster \u2013 2) *
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Sectors Per Cluster) * 512)<\/em><\/p>\n<\/blockquote>\n

A Worked Example:
<\/h3>\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Card Type<\/strong><\/td>\nSandisk 2Gb SD<\/td>\n<\/tr>\n
MBR Length<\/strong><\/td>\n512<\/td>\n<\/tr>\n
Partition 1 Start Sector<\/strong><\/td>\n129<\/td>\n<\/tr>\n
Bytes Per Sector<\/strong><\/td>\n512<\/td>\n<\/tr>\n
Sectors Per Cluster<\/strong><\/td>\n64<\/td>\n<\/tr>\n
Reserved Sectors<\/strong><\/td>\n2<\/td>\n<\/tr>\n
Number of FAT\u2019s<\/strong><\/td>\n2<\/td>\n<\/tr>\n
Sectors Per FAT<\/strong><\/td>\n239<\/td>\n<\/tr>\n
Hidden Sectors<\/strong><\/td>\n129<\/td>\n<\/tr>\n
BSE Length<\/strong><\/td>\n512<\/td>\n<\/tr>\n
Number of Root Entries<\/strong><\/td>\n512<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

The table above gives all the information taken from the MBR and BSE of the Card.<\/p>\n

Calculating the BSE Address:<\/h3>\n
\n

BSE = Partition Start Sector *
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/em>Bytes Per Sector<\/em><\/p>\n

BSE = 129 * 512<\/em><\/p>\n

BSE = 66048 = 0x10200<\/em><\/p>\n<\/blockquote>\n

Calculating the FAT1 Address:<\/h3>\n
\n

FAT1 = (Partition Start Sector +
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 <\/em>Reserved Sectors) * Bytes Per Sector) <\/em><\/p>\n

FAT1 = (129 + 2) * 512<\/em><\/p>\n

FAT1 = 131 * 512<\/em><\/p>\n

FAT1 = 67072 = 0x10600<\/em><\/p>\n<\/blockquote>\n

Calculating the Root Directory Address:<\/h3>\n
\n

RDE = FAT1 Start Address +
\u00a0\u00a0\u00a0\u00a0\u00a0 ((No of FATS * Sectors Per FAT) *
\u00a0\u00a0\u00a0\u00a0\u00a0 <\/em>Bytes Per Sector)<\/em><\/p>\n

RDE = 67072 +
\u00a0\u00a0\u00a0\u00a0\u00a0 ((2 * 239) *
\u00a0\u00a0\u00a0\u00a0\u00a0 512)<\/em><\/p>\n

RDE = 67072 + (478 * 512)<\/em><\/p>\n

RDE = 67072 + 244736<\/em><\/p>\n

RDE = 311808 = 0x4C200<\/em><\/p>\n<\/blockquote>\n

Calculating the First File Address:<\/h3>\n

For a file or folder which has a starting cluster value of 0x04, then the following will locate the start address;<\/p>\n

\n

File = Root Folder Address +
\u00a0\u00a0\u00a0\u00a0\u00a0 (Max Root Entries * 32) +
\u00a0\u00a0\u00a0\u00a0\u00a0 (((File\/Folder St Cluster \u2013 2) *
\u00a0\u00a0\u00a0\u00a0\u00a0 Sectors Per Cluster) *
\u00a0\u00a0\u00a0\u00a0\u00a0 Bytes Per sector)<\/em><\/p>\n

File = 311808 +
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 (512 * 32) +
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 (((4 \u2013 2) *
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 64) *
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 512)<\/em><\/p>\n

File = 311808 +
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 16384 +
\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ((2 * 64) * 512)<\/em><\/p>\n

File = 311808 + 16384 + (128 * 512)<\/em><\/p>\n

File = 311808 + 16384 + 65536<\/em><\/p>\n

File = 393728 = 0x60200
<\/em><\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"

I have been working on a\u00a0 project where the client requires their device to have it\u2019s firmware updated from a Secure Digital (SD) Card. The standard way to store and access data on SD cards is to use the MS FAT File System. There are several libraries available dotted around the Internet which allow developers […]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[78,79,80,81],"tags":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/posts\/1686"}],"collection":[{"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/comments?post=1686"}],"version-history":[{"count":0,"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/posts\/1686\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/media?parent=1686"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/categories?post=1686"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pjgcreations.co.uk\/wp-json\/wp\/v2\/tags?post=1686"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}