# FAT filesystem

The unit at [fs/fat.fs] implements the Filesystem API [fs/core] for the FAT
filesystem. It supports FAT12 and FAT16 but is still quite rough around the
edges.

* No FAT32
* No long filenames. It does gracefully ignore their presence though.
* No cluster reallocation. You are constantly leaking them through usage.

## Usage

You mount a FAT through "newfatfs ( blk -- fs )". For a given Blk [io/blk]
"blk" where the first sector has a proper FAT header, this call will yield a
struct that extends Filesystem and implements it entirely.

You might also be interested in [doc/fs/fatt], the "FAT tools" unit.

## direntry

In the code, a "direntry" is a small static buffer containing a single directory
entry at once. "readentry" reads the current walking position into it and
"writeentry" writes it to the current walking position.

## API

newfatfs ( blk -- fs )
  Initialize a new FATFS structure and yield it.

fatstorage! ( blk fs -- )
  Change the underlying storage for FAT fs "fs". You generally don't need to do
  that, but there's an exception with PCs, which boot using [drv/pc/int13h] and
  later need to upgrade to a "real" storage driver. Rather than creating a new
  FS structure entirely, it's better to just swap the underlying storage.

## Private API

### Structures

extends FS struct FATFS {
  uint widx wcl _pad _pad ;
  uint fatpart lastfreecluster ;
  [void,0] fatheader ;
}
addrof curfs FATFS bindstruct

offsetof fatheader const FATOFF
struct FATHeader +FATOFF {
  +$0b leshort fatsecsz ;
  +$0d uchar   secpercluster ;
  +$0e leshort reservedseccnt ;
  +$10 uchar   FATcnt ;
  +$11 leshort rootentcnt ;
  +$13 leshort fatseccnt ;
  +$16 leshort FATsz ;
}
addrof curfs FATHeader bindstruct

widx            The "walk index", the direntry index where the walk context
                points within the "wcl" cluster.
wcl             The "walk cluster", the cluster, which is expected to contain
                directory entries, where the walk context currently points.
fatpart         A [io/part] targeting the FAT sectors
lastfreecluster Cache of last findfreecluster result

All the other fields is the FAT reader, directly read into "fatheader" from
disk.

fatsecsz        BPB_BytsPerSec
secpercluster   BPB_SecPerClus
reservedseccnt  BPB_RsvdSecCnt
FATcnt          BPB_NumFATs
rootentcnt      BPB_RootEntCnt
fatseccnt       BPB_TotSec16
FATsz           BPB_FATsz16

extends Stream struct FATFile {
  uint cl0 clidx curcl adirty fcl fidx fat ;
}

cl0    First cluster of the file
clidx  Index in the chain where "curcl" is
curcl  Cluster containing current position
adirty Attributes dirty. When 1, must update entry on flush
fcl    The value of "wcl/widx" where the directory entry of this file is
fidx   located.
fat    Pointer to parent FATFS struct

### Words

NOTE: In the signatures below, we refer to "cluster" as the cluster number
      read from or written to the FAT table and "clidx" as the index in the
      table that is read from or written to. As per FAT design, both are
      clusters, but we need different names for clarity.

FAT@ ( clidx -- cluster )
  Yield the cluster present in in the FAT table at the specified cluster index.
  Automatically chooses between the 12-bit lookup or the 16-bit one.

  Note that clusters 0 and 1 are invalid.

FAT! ( cluster clidx -- )
  Set cluster in FAT table at clidx to specified cluster.

FAT@+ ( clidx -- cluster )
  Do a "FAT@" and if the result is End Of Chain, allocate a new cluster and
  then yield it. Otherwise, just yield the cluster.

clusterpos ( cluster -- off )
  Yields the offset in bytes, in the FAT storage blk, where the specified
  cluster begins.

  Errors out if cluster is out of range.

readentry ( -- )
  Read contents of wcl/widx into "dirent", a static 32-bytes buffer.

writeentry ( -- )
  Write contents of "dirent" into wcl/widx.

iterentry ( -- f )
  Increase widx by 1. If we cross a cluster boundary, correctly change wcl.
  Yield f=1 unless EOC was encountered when iterating wcl.

findfreecluster ( -- cl )
  Beginning at "lastfreecluster" (for speed), iterate over the FAT table until
  a free cluster is found, then yield it.

allocatecluster ( -- cl )
  Find a free cluster, mark it as End Of Chain, then yield it.

allocatecluster0 ( -- cl )
  Allocate a new cluster and fill the associated contents with zeroes.

.fat ( -- )
  Assuming that curfs is a FATFS, print its vital statistics.

## See also

* FAT: General Overview of On-Disk Format
