duskos

dusk os fork
git clone git://git.alexwennerberg.com/duskos
Log | Files | Refs | README | LICENSE

commit b3ce4a8da5d95243b805e161159815eab497719e
parent 3019081ab376cc809daceb75b8b02737f412ec0d
Author: Virgil Dupras <hsoft@hardcoded.net>
Date:   Tue, 19 Jul 2022 16:08:10 -0400

Begin I/O restructuring

See doc/io for details.

Diffstat:
Afs/doc/io.txt | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfs/fs/fat.fs | 5+++--
Mfs/fs/fatlo.fs | 58+++++++++++++++++++++++++++-------------------------------
Mfs/tests/fs/fat.fs | 6+++---
Mfs/xcomp/glue2.fs | 2+-
5 files changed, 99 insertions(+), 37 deletions(-)

diff --git a/fs/doc/io.txt b/fs/doc/io.txt @@ -0,0 +1,65 @@ +# Input/Output + +(this document doesn't describe things as they are now, but as they're going to +be soon) + +The lib/io subsystem offers a unified API to read and write on any device or +filesystem. All words in this API revolves around a structures that we call the +"IO handle". These handle represent one "place" we read and write to, saving, if +needed, all states needed to fulfill the API's specifications. + +I/O operations are byte based. + +When "EOF" is mentioned, it means "end of file". In same case, it's a misnomer, +but the idea is the same: if all possible data from the I/O source have been +consumed, we're in EOF condition. + +Position management is opaque to the I/O words. + +The structure of these handle will vary depending on the driver or filesystem, +but they all start with the same structure, which is a list of 4b pointers to +words, in this order: + +readbuf +writebuf +flush + +These words have the following API. In all words, "hdl" is a pointer to a IO +handle. If the handle has no concept of "flush", it can point to "drop" (as a +noop). + +readbuf ( n hdl -- a? read-n ) + Try to read "n" bytes from file from current position. Unless EOF is reached, + at least 1 byte must be read, but otherwise "read-n" can be lower than n, even + if that doesn't take the handle to EOF. The idea is to take the handle, at + most, to the end of its internal buffer. Return the number of bytes read and, + if it was nonzero, return an address to the beginning of that buffer. + + After the read operation, advance current position by "read-n" bytes. + +writebuf ( a n hdl -- written-n ) + Try to write "n" bytes from buffer "a" to file from current position, growing + the file if needed. The idea is the same as with freadbuf: the filesystem can + proceed as is best for its implementation, as long as it writes at least 1 + byte. If no data can be written, return 0. + + After the write operation, advance current position by "written-n" bytes. + +flush ( hdl -- ) + If the handle's buffer is "dirty" (has been changed compared to the permanent + storage it represents), save that data to permanent storage and flag the + handle as clean. + +## Generic API + +From the base API of IO handlers, lib/io has these generic words: + +getc ( hdl -- b ) + Read 1 byte from hdl an return it. Advance position by 1 byte. Return -1 on + EOF. + +putc ( b hdl -- ) + Write 1 byte to hdl. Advance position by 1 byte. Aborts if unable to write. + +It also has, for each IO handler word, a helper to directly call it. For +example, "hdl flush" simply does "hdl 8 + @ execute". diff --git a/fs/fs/fat.fs b/fs/fs/fat.fs @@ -74,7 +74,7 @@ $ffff const EOC over 2 - $fff6 > if abort" cluster out of range!" then swap FirstSectorOfCluster ( dst sec ) swap BPB_SecPerClus swap writesectors ; -: _ ( fcursor -- ) \ fatflush +: fatflush ( fcursor -- ) dup FCUR_dirty? not if drop exit then ( fcursor ) \ save buffer dup FCUR_cluster over FCUR_buf( writecluster ( fcursor ) @@ -83,7 +83,6 @@ $ffff const EOC writecursector \ undirty the cursor dup FCUR_flags $fffffffd and swap FCUR_flags! ; -current to fatflush \ grow fcursor to newsz, if needed : fatgrow ( newsz fcursor -- ) @@ -104,3 +103,5 @@ current to fatflush r@ FCUR_)buf r@ FCUR_bufpos - ( src n nmax ) min ( src n ) r> FCUR_bufpos swap ( src dst n ) dup >r move r> ( n ) ; + +: fatopen fatopenlo ['] fatwritebuf over 4 + ! ['] fatflush over 8 + ! ; diff --git a/fs/fs/fatlo.fs b/fs/fs/fatlo.fs @@ -141,6 +141,7 @@ here const )fnbuf fatfindpath ( dentry ) curdir( DIRENTRYSZ move ; \ File cursor +\ 12b IO handler prelude \ 4b flags. all zeroes = free cursor \ b0 = used \ b1 = buffer is dirty @@ -152,25 +153,25 @@ here const )fnbuf \ 4b file size \ Xb current cluster X=ClusterSize 10 const FCURSORCNT \ maximum number of opened files -: FCursorSize ClusterSize 24 + ; +: FCursorSize ClusterSize 36 + ; : FCUR_flags ( fcur -- n ) @ ; : FCUR_free? ( fcur -- f ) FCUR_flags not ; : FCUR_dirty? ( fcur -- f ) FCUR_flags 2 and ; : FCUR_flags! ( n fcur -- ) ! ; -: FCUR_cluster ( fcur -- n ) 4 + @ ; -: FCUR_cluster! ( n fcur -- ) 4 + ! ; -: FCUR_clusteridx ( fcur -- n ) 8 + @ ; -: FCUR_clusteridx! ( n fcur -- n ) 8 + ! ; -: FCUR_pos ( fcur -- n ) 16 + @ ; -: FCUR_pos! ( n fcur -- n ) 16 + ! ; -: FCUR_pos+ ( n fcur -- ) 16 + +! ; -: FCUR_size ( fcur -- n ) 20 + @ ; -: FCUR_size! ( n fcur -- ) 20 + ! ; -: FCUR_buf( ( fcur -- a ) 24 + ; +: FCUR_cluster ( fcur -- n ) 16 + @ ; +: FCUR_cluster! ( n fcur -- ) 16 + ! ; +: FCUR_clusteridx ( fcur -- n ) 20 + @ ; +: FCUR_clusteridx! ( n fcur -- n ) 20 + ! ; +: FCUR_pos ( fcur -- n ) 28 + @ ; +: FCUR_pos! ( n fcur -- n ) 28 + ! ; +: FCUR_pos+ ( n fcur -- ) 28 + +! ; +: FCUR_size ( fcur -- n ) 32 + @ ; +: FCUR_size! ( n fcur -- ) 32 + ! ; +: FCUR_buf( ( fcur -- a ) 36 + ; : FCUR_)buf ( fcur -- a ) FCUR_buf( ClusterSize + ; : FCUR_bufpos ( fcur -- a ) dup FCUR_pos ClusterSize mod swap FCUR_buf( + ; : FCUR_dirent ( fcur -- dirent ) - 12 + @ BPB_BytsPerSec /mod ( offset sec ) 1 readsector ( off ) fatbuf( + ; + 24 + @ BPB_BytsPerSec /mod ( offset sec ) 1 readsector ( off ) fatbuf( + ; : FCUR_cluster0 ( fcur -- cl ) FCUR_dirent DIR_Cluster ; create fcursors( FCursorSize FCURSORCNT * allot0 @@ -189,29 +190,12 @@ create fcursors( FCursorSize FCURSORCNT * allot0 over 2 - $fff6 > if abort" cluster out of range!" then 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 ) - findfreecursor >r - 0 r@ FCUR_cluster! ( dirent ) 1 r@ FCUR_flags! - dup fatbuf( - bufsec BPB_BytsPerSec * + ( dirent doffset ) r@ 12 + ! - -1 r@ FCUR_clusteridx! 0 r@ FCUR_pos! - DIR_FileSize r@ FCUR_size! ( ) r> ; - -: fatopen ( path -- fcursor ) fatfindpath openfile ; - -\ if the cursor is dirty, write its buffer to disk as well as its size to the -\ direntry on disk. Unsets the dirty flag. -\ as long as fs/fat is not loaded, we can't write anything to files, so fatflush -\ will always be a noop. -alias drop fatflush ( fcursor -- ) - \ set fcursor to pos. If new pos crosses cluster boundaries compared to current \ pos, flush current buffer and read a new sector from disk. : fatseek ( pos fcursor -- ) over 0< if abort" can't seek to negative pos" then over ClusterSize / over FCUR_clusteridx = not if - dup fatflush >r ( pos ) + dup dup 8 + @ ( 'flush ) execute >r ( pos ) dup ClusterSize / dup r@ FCUR_clusteridx! ( pos idx ) r@ FCUR_cluster0 ( pos idx cl ) swap ?dup if >r begin ( pos cl ) FAT@ next then ( pos cl ) @@ -228,4 +212,16 @@ alias drop fatflush ( fcursor -- ) rot min ( a n ) dup r> FCUR_pos+ ( a n ) ; -: fatclose ( fcursor ) dup fatflush 0 swap FCUR_flags! ; +\ Open the specified "path" into one of the free cursors and return that +\ cursor. This is the "low" part. Complete open is finalized in fs/fat +: fatopenlo ( path -- fcursor ) + fatfindpath findfreecursor >r + \ write IO handle prelude: readbuf, writebuf, flush + ['] fatreadbuf r@ ! ['] abort r@ 4 + ! ['] drop r@ 8 + ! + \ write the rest + 0 r@ FCUR_cluster! ( dirent ) 1 r@ FCUR_flags! + dup fatbuf( - bufsec BPB_BytsPerSec * + ( dirent doffset ) r@ 24 + ! + -1 r@ FCUR_clusteridx! 0 r@ FCUR_pos! + DIR_FileSize r@ FCUR_size! ( ) r> ; + +: fatclose ( fcursor ) dup dup 8 + @ ( 'flush ) execute 0 swap FCUR_flags! ; diff --git a/fs/tests/fs/fat.fs b/fs/tests/fs/fat.fs @@ -8,8 +8,8 @@ : fatgetc ( fcursor -- c ) 1 swap fatreadbuf if c@ else -1 ( EOF ) then ; testbegin \ Tests for fs/fat -S" tests/fattest" fatfindpath ( dirent ) -openfile ( fcursor ) dup fatgetc 'T' #eq +S" tests/fattest" fatopen ( fcursor ) +dup fatgetc 'T' #eq $100 over fatseek dup fatgetc 'f' #eq dup fatgetc 'o' #eq dup fatgetc 'o' #eq $200 over fatseek @@ -30,7 +30,7 @@ S" /lib/str.fs" fatfindpath # \ found! S" newfile" fatnewfile # S" /newfile" fatfindpath # \ found! \ let's try writing to it -S" /newfile" fatfindpath openfile ( fc ) +S" /newfile" fatopen ( fc ) dup FCUR_cluster0 0 #eq \ no cluster allocated yet dup S" 42" c@+ rot fatwritebuf 2 #eq ( fc ) fclose f<< /newfile 42 #eq diff --git a/fs/xcomp/glue2.fs b/fs/xcomp/glue2.fs @@ -1,5 +1,5 @@ bootfile xcomp/glue2.fs \ Glue code that goes between the filesystem part and boothi -alias fatopen fopen +alias fatopenlo fopen alias fatreadbuf freadbuf alias fatclose fclose