Accessing the FAT
The PIC18F4550 FAT16 firmware that follows shows how to convert a data-cluster number to a logical block address, how to read and write to the FAT, how to find a file’s next cluster, and how to find an empty cluster.
A DISK structure can hold in information about a volume and its FAT:
#define FAT16 2
#define FAT32 3
byte* buffer; // pointer to a buffer equal to one sector
dword firsts; // LBA of the volume’s first sector
dword fat; // LBA of the volume’s FAT
dword root; // LBA of the volume’s root directory
dword data; // LBA of the volume’s data area
word maxroot; // maximum number of entries in the root directory
dword maxcls; // maximum number of data clusters in the volume
word fatsize; // number of sectors in the FAT
byte fatcopy; // number of copies of the FAT
byte SecPerClus; // number of sectors per cluster
byte type; // type of FAT (FAT16, FAT32)
byte mount; // TRUE if the media is mounted, FALSE if not mounted)
A FILE structure can store information about a file, including its location in a volume and a location currently being accessed in the file. (Chapter 9 has more about directories, and Chapter 10 has more about accessing files.)
// A short file name has 11 or fewer characters, not counting the dot.
#define FILE_NAME_SIZE 11
unsigned write :1; // Set if the file was opened for writing.
unsigned FileWriteEOF :1; // Set if writing and have reached the end of the file.
DISK *dsk; // a DISK structure for the volume containing the file
word cluster; // number of the first file’s cluster
word ccls; // current cluster
word sec; // current sector in the current cluster
word pos; // current byte location in the current sector
dword seek; // current byte location in the file
dword size; // file size
FileFlags Flags; // write mode and end-of-file indicators
word time; // last update time
word date; // last update date
char name[FILE_NAME_SIZE]; // file name
word entry; // position of the file’s entry in its directory
word chk; // FILE structure checksum = ~( entry + name)
word attributes; // file’s attributes
word dirclus; // first cluster of the file’s directory
word dirccls; // current cluster of the file’s directory
A FILEOBJ is a pointer to a FILE structure:
typedef FILE * FILEOBJ;
Functions in this chapter use these defines:
#define CLUSTER_FAIL 0xffff
#define LAST_CLUSTER 0xfff8
#define LAST_CLUSTER_FAT16 0xfff8
Obtaining a Cluster’s Logical Block Address
A file’s directory entry and FAT entries store cluster numbers. To read data from the storage media, firmware must specify a logical block address. The Cluster2Sector function accepts a pointer to a DISK structure and a cluster number and returns the LBA of the cluster’s first sector.
dword Cluster2Sector(DISK *dsk, word cluster)
// Data clusters 0 and 1 don’t exist.
// If cluster = 0 or 1, assume it’s the root directory.
if (cluster == 0 || cluster == 1)
sector = dsk -> root + cluster;
// The data area begins with cluster 2.
// Subtract 2 from the cluster number to get the cluster number within the data area.
// Multiply the result by the number of sectors per cluster to get the sector number
// within the data area.
// Add the number of the first sector in the data area to get the absolute sector
// number for the cluster.
sector = ((cluster – 2) * dsk -> SecPerClus) + dsk -> data;
Reading from the FAT
The FATread function accepts a pointer to a DISK structure (dsk) and a cluster number (ccls), reads the FAT entry for that cluster, and returns the value read, which is the number of the next cluster in the file or directory or an EOC marker. The function calls the SectorRead function from Chapter 5.
The function uses the RAMreadW macro to read a word at the address specified by a base address (a) plus an offset(f ):
#define RAMreadW(a, f) *(word *)(a + f)
word FATread(DISK *dsk, word ccls)
// Get the address of the file’s current cluster.
// The address is two bytes, LSB first.
p = ccls;
// The LBA of the FAT sector containing the cluster’s data is the FAT’s starting address
// plus the high byte of the current cluster.
// (Each sector contains 256 two-byte entries.)
l = dsk -> fat + (p >> 8);
// Read the sector.
if ( SectorRead( l, dsk -> buffer) != sdcValid)
// To get the value stored in the cluster’s entry,
// read 2 bytes in the buffer of retrieved data
// beginning at offset = low byte of current cluster’s address << 1.
// Shift left 1 (multiply by 2) because each entry is 2 bytes.
c = RAMreadW(dsk -> buffer, ((p & 0xFF) << 1));
if (c >= LAST_CLUSTER_FAT16)
// The entry is an EOC marker.
c = LAST_CLUSTER;