commit c19708929d22fbedb8cf4147d1a087cf7cd10e1d
parent 077c0f357402032abc4978e7f5e20ca310a3d628
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Tue, 5 Jul 2022 14:04:31 -0400
fs/fat: use smaller buffers
My first implementation has the whole FAT in memory at once and a buffer large
enough to contain all of the root dirs in memory at once. This commit reduces
this to a single sector buffer.
The primary goal is not to save memory (although it does save some), but to
simplify the caching logic when writes are going to be involved. With the whole
FAT in memory, every change in cluster would involve either detecting dirty
areas or always writing the whole FAT. Those options are either too complicated
or too wasteful.
It's simpler to always access the FAT from disk and to always write the FAT
directly, at sector precision, when changes are made to file allocation.
Diffstat:
3 files changed, 55 insertions(+), 41 deletions(-)
diff --git a/fs/fs/fatlo.fs b/fs/fs/fatlo.fs
@@ -23,7 +23,9 @@ create bpb 0 here (drv@) $18 allot
: RootDirSectors
BPB_RootEntCnt 32 * BPB_BytsPerSec /mod ( r q ) swap if 1+ then ;
: FirstDataSector BPB_RsvdSecCnt BPB_NumFATs BPB_FATSz16 * + RootDirSectors + ;
-: FirstSectorOfCluster ( n -- sec ) 1- 1- BPB_SecPerClus * FirstDataSector + ;
+: FirstSectorOfCluster ( n -- sec )
+ dup << BPB_BytsPerSec BPB_FATSz16 * >= if abort" cluster out of range" then
+ 1- 1- BPB_SecPerClus * FirstDataSector + ;
: FirstRootDirSecNum BPB_RsvdSecCnt BPB_NumFATs BPB_FATSz16 * + ;
: ClusterSize BPB_SecPerClus BPB_BytsPerSec * ;
: DataSec
@@ -31,13 +33,40 @@ create bpb 0 here (drv@) $18 allot
: CountOfClusters DataSec BPB_SecPerClus / ;
: FAT12? CountOfClusters 4085 < ;
-\ read multiple sectors in buf
-: readsectors ( sec u buf -- )
- A>r swap >r swap >A begin ( buf )
- A> over (drv@) A+ drvblksz + next ( buf ) drop r>A ;
+\ Buffer for either reading FAT sectors or Directory contents. It is one sector
+\ in size and knows the number of sequential sector read it has in front of it.
+create buf( BPB_BytsPerSec allot
+here const )buf
+0 value bufsec \ sector number of current buf
+0 value bufseccnt \ number of sectors ahead for sequential read
+0 value bufcluster \ cluster number of current buf
+
+\ "cnt" is the number of sectors ahead of "sec" that are available for a
+\ seqential read.
+: readsector ( sec cnt -- ) to bufseccnt dup to bufsec buf( (drv@) ;
+
+: FAT12@ ( cluster -- entry )
+ dup dup >> + ( cl offset ) BPB_BytsPerSec /mod ( cl secoff sec )
+ BPB_RsvdSecCnt + 0 readsector ( cl secoff )
+ buf( + w@ ( cl entry )
+ swap 1 and if 4 rshift else $fff and then ;
+: FAT16@ ( cluster -- entry )
+ << ( offset ) BPB_BytsPerSec /mod ( secoff sec )
+ BPB_RsvdSecCnt + 0 readsector ( secoff )
+ buf( + w@ ;
+: FAT@ ( cluster -- entry ) FAT12? if FAT12@ else FAT16@ then ;
+
+: EOC? ( cluster -- f )
+ FAT12? if $ff8 else $fff8 then tuck and = ;
-create FAT( BPB_BytsPerSec BPB_FATSz16 * allot
-: readFAT BPB_RsvdSecCnt BPB_FATSz16 FAT( readsectors ;
+\ Read next sector if a sequential read is available, else return false.
+: nextsector? ( -- f )
+ bufseccnt if \ still on a sector streak
+ bufseccnt 1+ bufseccnt 1- readsector 1
+ else \ out of sector, try next cluster
+ bufcluster FAT@ dup EOC? if drop 0 else \ we have another cluster
+ dup to bufcluster FirstSectorOfCluster BPB_SecPerClus readsector 1
+ then then ;
32 const DIRENTRYSZ
11 const FNAMESZ
@@ -45,11 +74,6 @@ create FAT( BPB_BytsPerSec BPB_FATSz16 * allot
: DIR_Cluster ( direntry -- cluster ) 26 + w@ ;
: DIR_FileSize ( direntry -- sz ) 28 + @ ;
-\ A buffer where dir entries are copied before we search in them. It's big
-\ enough to hold the root dir entries. This means that no directory in the FS
-\ can have more than BPB_RootEntCnt entries.
-create dirbuf( RootDirSectors BPB_BytsPerSec * allot
-here const )dirbuf
create fnbuf( FNAMESZ allot
here const )fnbuf
@@ -62,43 +86,27 @@ here const )fnbuf
Ac@+ dup '.' = if ( a c ) 2drop fnbuf( 8 + else upcase swap c!+ then
next drop r>A ;
-: _findindir ( -- direntry )
- dirbuf( begin ( a )
- dup )dirbuf < while ( a )
+: _ ( -- direntry-or-0 )
+ buf( begin ( a )
+ dup )buf < while ( a )
fnbuf( over DIR_Name []= not while ( a ) DIRENTRYSZ + repeat
- ( success ) else ( not found ) abort" file not found" then ( a ) ;
+ ( success ) else ( not found ) drop 0 then ( a ) ;
+: _findindir ( -- direntry )
+ begin
+ _ ?dup not while nextsector? not if abort" file not found" then
+ repeat ;
\ Search in directory that is currently loaded in dirbuf.
\ Return address of directory entry, abort if not found.
: findindir ( fname -- direntry ) _tofnbuf _findindir ;
\ Make the current dir the root
-: readroot FirstRootDirSecNum RootDirSectors dirbuf( readsectors ;
-
-: EOC? ( cluster -- f )
- FAT12? if $ff8 else $fff8 then tuck and = ;
-
-\ get cluster following this one in the FAT
-: nextcluster ( cluster -- nextcluster )
- FAT12? if
- dup dup >> + ( cl off )
- FAT( + w@ ( cl ncl ) swap 1 and if 4 rshift else $fff and then
- else
- << FAT( + w@ then ;
-
-: readcluster ( cluster dst -- )
- over << BPB_BytsPerSec BPB_FATSz16 * >= if abort" cluster out of range" then
- swap FirstSectorOfCluster ( dst sec ) swap BPB_SecPerClus swap readsectors ;
+: readroot 1 to bufcluster FirstRootDirSecNum RootDirSectors readsector ;
\ Read specified "direntry" in dirbuf(
\ Errors out if it has more entries that BPB_RootEntCnt
: readdir ( direntry -- )
- DIR_Cluster ( cluster ) dirbuf( begin ( cluster buf )
- over EOC? not while
- 2dup readcluster
- ClusterSize + swap nextcluster swap repeat ( cluster buf )
- 2drop ;
-
+ DIR_Cluster dup to bufcluster FirstSectorOfCluster BPB_SecPerClus readsector ;
: findpath ( path -- direntry )
A>r fnbufclr fnbuf( >A c@+ >r readroot begin ( a )
@@ -135,6 +143,14 @@ create fcursors( FCursorSize FCURSORCNT * allot0
dup FCUR_cluster0 not if ( found! ) r~ exit then FCursorSize + next
abort" out of file cursors!" ;
+\ read multiple sectors in buf
+: readsectors ( sec u buf -- )
+ A>r swap >r swap >A begin ( buf )
+ A> over (drv@) A+ drvblksz + next ( buf ) drop r>A ;
+
+: readcluster ( cluster dst -- )
+ swap FirstSectorOfCluster ( dst sec ) swap BPB_SecPerClus swap readsectors ;
+
\ Open the specified "direntry" into one of the free cursors and return that
\ cursor.
: openfile ( direntry -- fcursor )
@@ -149,7 +165,7 @@ create fcursors( FCursorSize FCURSORCNT * allot0
dup FCUR_pos over FCUR_size = if drop -1 exit then
dup FCUR_pos+ ClusterSize mod over FCUR_buf( + c@ ( fc c )
over FCUR_pos ClusterSize mod not if ( fc c ) \ end of cluster, read next
- over FCUR_cluster nextcluster ( fc c cluster )
+ over FCUR_cluster FAT@ ( fc c cluster )
dup EOC? if drop else
dup 2 < if abort" cluster out of range" then
rot 2dup FCUR_cluster! ( c cluster fc )
diff --git a/fs/tests/fs/fat.fs b/fs/tests/fs/fat.fs
@@ -8,7 +8,6 @@
: readN ( fcursor n -- ) >r begin dup fat16getc drop next drop ;
testbegin
\ Tests for fs/boot
-readFAT
readroot
S" tests/fattest" findpath ( dirent )
openfile ( fcursor ) dup fat16getc 'T' #eq
diff --git a/fs/xcomp/glue2.fs b/fs/xcomp/glue2.fs
@@ -1,5 +1,4 @@
\ Glue code that goes between the filesystem part and boothi
-readFAT
alias fat16open fopen
alias fat16getc fgetc
alias fat16close fclose