commit f8f78e92da936ffbd9b1ed00f18ec3d61a1b9f32
parent 53e59e63ddb85c84c1d7aba4a56a73385d7a2e04
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Sun, 31 Jul 2022 08:36:33 -0400
Introduce the Drive API
Diffstat:
15 files changed, 71 insertions(+), 85 deletions(-)
diff --git a/fs/doc/drive.txt b/fs/doc/drive.txt
@@ -0,0 +1,25 @@
+# Drive API
+
+The sys/drive subsystem offers a unified API to read and write to mass storage.
+It does so in blocks of bytes of a definite size which we call "sector".
+Sectors in the Drive API are organized in a linear manner (LBA, Logical Block
+Addressing). If the underlying mass storage driver interfaces with a CHS
+(cylinder, head, sector) system, it's its responsibility to convert LBA to CHS.
+
+The drive API exposes the features of the underlying driver through a specific
+data structure, called a Drive, which is 12 bytes in length in the form of 3
+fields of 4 bytes:
+
+sectorsize
+drv@
+drv!
+
+sectorsize is an integer describing, in bytes, the size of sectors read and
+written by the driver. It's often 512 bytes.
+
+drv@ ( sec dst drv -- )
+ Read sector "sec" in memory address "dst", which needs to be a buffer at least
+ "sectorsize" in size. "drv" is a pointer to a Drive structure.
+
+drv! ( sec src drv -- )
+ Write sector "sec" from memory address "src".
diff --git a/fs/doc/io.txt b/fs/doc/io.txt
@@ -1,8 +1,5 @@
# 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
diff --git a/fs/drv/pc/ata.fs b/fs/drv/pc/ata.fs
@@ -42,13 +42,20 @@ $ec const IDENTIFY
dup 8 rshift _cyllo pc!
16 rshift _cylhi pc! ;
-: ata@ ( sec dst -- )
- swap _locate $20 ( read sectors ) _cmd pc! _wait ( dst )
+: ata@ ( sec dst drv -- )
+ drop swap _locate $20 ( read sectors ) _cmd pc! _wait ( dst )
A>r ( dst ) >A $100 >r begin _data pw@ A> w! A+ A+ next r>A ;
+: ata! abort" ATA driver doesn't support writing" ;
+
: .ata ( -- ) atabus . ':' emit atadrive . spc> ataidentify .x1 ;
: .ataall ( -- )
atadrive atabus
0 to atabus 0 to atadrive .ata nl> 1 to atadrive .ata nl>
1 to atabus 0 to atadrive .ata nl> 1 to atadrive .ata nl>
to atabus to atadrive ;
+
+create ATADrive
+ ATASECSZ ,
+ ' ata@ ,
+ ' ata! ,
diff --git a/fs/drv/pc/int13h.fs b/fs/drv/pc/int13h.fs
@@ -8,6 +8,13 @@ $7c1a w@ const NUMHEADS
$7c24 c@ const DRVNO
$80 const DRVNO
-: biossec@ ( sec dst -- )
- >r DRVNO swap SECPERTRK /mod ( drv sec trk )
+: biossec@ ( sec dst drv -- )
+ drop >r DRVNO swap SECPERTRK /mod ( drv sec trk )
NUMHEADS /mod ( drv sec head cyl ) rot 1+ r> int13h ;
+
+: biossec! abort" int13h driver doesn't support writing" ;
+
+create INT13hDrive
+ BIOSSECSZ ,
+ ' biossec@ ,
+ ' biossec! ,
diff --git a/fs/drv/ramdrive.fs b/fs/drv/ramdrive.fs
@@ -1,14 +1,17 @@
\ Drive in RAM
-\ This implements the "drive" protocol, which for now is undocumented, on top
-\ of an area in RAM.
+\ TODO: support more than one RAM drive at once.
512 const RAMDRVSECSZ
0 value ramdrv(
: _addr ( blkno -- a ) RAMDRVSECSZ * ramdrv( + ;
-: ramdrv@ ( blkno buf -- )
- swap _addr swap ( src buf ) RAMDRVSECSZ move ;
-: ramdrv! ( blkno buf -- )
- swap _addr ( buf dst ) RAMDRVSECSZ move ;
+: ramdrv@ ( blkno buf drv -- )
+ drop swap _addr swap ( src buf ) RAMDRVSECSZ move ;
+: ramdrv! ( blkno buf drv -- )
+ drop swap _addr ( buf dst ) RAMDRVSECSZ move ;
+create RAMDrive
+ RAMDRVSECSZ ,
+ ' ramdrv@ ,
+ ' ramdrv! ,
diff --git a/fs/fs/fat.fs b/fs/fs/fat.fs
@@ -22,7 +22,7 @@
$ffff const EOC
-: writecursector ( -- ) bufsec fatbuf( (drv!) ;
+: writecursector ( -- ) bufsec fatbuf( fatdrv drv! ;
: FAT12! ( entry cluster -- )
dup FAT@ ( entry cl old ) over FAT12' ( entry cl old a )
@@ -31,14 +31,14 @@ $ffff const EOC
else ( entry a old )
$f000 and rot $fff and or then ( a n )
over w! ( a ) )fatbuf 1- = if \ end-of-sector cross-over!
- bufsec 1+ (drv@) )fatbuf 1+ c@ fatbuf( c! bufsec 1+ (drv!) then ;
+ bufsec 1+ fatdrv drv@ )fatbuf 1+ c@ fatbuf( c! bufsec 1+ fatdrv drv! then ;
: FAT16! ( entry cluster -- ) FAT16' w! ;
: FAT! ( entry cluster ) FAT12? if FAT12! else FAT16! then writecursector ;
: zerocluster ( cluster -- )
fatbuf( BPB_BytsPerSec 0 fill
FirstSectorOfCluster ( sec ) BPB_SecPerClus >r begin ( sec )
- fatbuf( (drv!) 1+ next drop ;
+ fatbuf( fatdrv drv! 1+ next drop ;
\ find a free cluster in the FAT
: findfreecluster ( -- cluster )
@@ -77,7 +77,7 @@ $ffff const EOC
\ write multiple sectors from buf
: writesectors ( sec u buf -- )
A>r swap >r swap >A begin ( buf )
- A> over (drv!) A+ drvblksz + next ( buf ) drop r>A ;
+ A> over fatdrv drv! A+ fatdrv sectorsize + next ( buf ) drop r>A ;
: writecluster ( cluster src -- )
over 2 - $fff6 > if abort" cluster out of range!" then
diff --git a/fs/fs/fatlo.fs b/fs/fs/fatlo.fs
@@ -10,13 +10,13 @@
\ of logic from this unit in fs/fat. All in all, "read and core stucture" is
\ here, "write" is in fs/fat.
-\ This unit has access to a very small set of words, that it, words implemented
-\ by boot.fs as well as (drv@) and drvblksz from the "drive" protocol, which is
-\ implemented by a driver that is also inserted in the boot sequence.
+\ Before loading this unit, you need to define a "fatdrv" value that is a
+\ pointer to a Drive (doc/drive) structure.
\ See fs/fat.fs for complete implementation details.
-create bpb 0 here (drv@) $18 allot
+\ TODO: assert that fatdrv's sector size is the same as in the FAT's BPB
+create bpb 0 here fatdrv drv@ $18 allot
: BPB_BytsPerSec bpb $0b + w@ ;
: BPB_SecPerClus bpb $0d + c@ ;
@@ -50,13 +50,13 @@ here const )fatbuf
\ "cnt" is the number of sectors ahead of "sec" that are available for a
\ seqential read.
-: readsector ( sec cnt -- ) to bufseccnt dup to bufsec fatbuf( (drv@) ;
+: readsector ( sec cnt -- ) to bufseccnt dup to bufsec fatbuf( fatdrv drv@ ;
: FAT12' ( cluster -- 'entry )
dup >> + ( cl offset ) BPB_BytsPerSec /mod ( cl secoff sec )
BPB_RsvdSecCnt +
over 1+ BPB_BytsPerSec = if \ end-of-sector cross-over!
- dup 1 + fatbuf( (drv@) fatbuf( c@ )fatbuf c! then
+ dup 1 + fatbuf( fatdrv drv@ fatbuf( c@ )fatbuf c! then
0 readsector ( cl secoff )
fatbuf( + ;
: FAT12@ ( cluster -- entry )
@@ -182,7 +182,7 @@ create fcursors( FCursorSize FCURSORCNT * allot0
\ 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 ;
+ A> over fatdrv drv@ A+ BPB_BytsPerSec + next ( buf ) drop r>A ;
: readcluster ( cluster dst -- )
over 2 - $fff6 > if abort" cluster out of range!" then
diff --git a/fs/sys/drive.fs b/fs/sys/drive.fs
@@ -1,41 +0,0 @@
-\ Drive subsystem
-
-\ Allow access to mass storage in a standardized manner. This subsystem
-\ defines the (drv@) and (drv!) aliases and a storage driver is expected to plug
-\ into it.
-
-\ Blocks: this subsystem deals with the concept of "blocks", which is a bunch
-\ of bytes of a definite size, always the same for a given device. It's the
-\ driver that defines the size of those blocks and sets the "drvblksz" value to
-\ this proper value.
-
-\ (drv@) and (drv!) expect block number arguments, which means that we consider
-\ the data held by the storage device to be an array of contiguous blocks of
-\ "drvblksz" bytes in size.
-
-\ The Drive subsystem holds a temporary buffer and manages it. This buffer
-\ should be at least twice as large as the largest drvblksz it's ever going to
-\ handle. Being able to hold 2 blocks in memory is necessary for "drvseek".
-
-$1000 const DRVBUFSZ
-create drvbuf( DRVBUFSZ allot
-DRVBUFSZ >> value drvblksz
-\ Block number currently in drvbuf(
--1 value drvcurblk
-
-\ (drv@) is declared in /xcomp/bootlo
-
-( blkno buf -- )
-alias abort (drv!)
-
-: drv@ ( blkno -- ) dup to drvcurblk drvbuf( (drv@) ;
-: ?drv@ ( blkno -- ) dup drvcurblk = if drop else drv@ then ;
-
-\ Ensure that the block containing offset "off" (in bytes) is loaded and that
-\ there's at least u bytes following that offset that is present in the buffer.
-\ u cannot be larger than drvblksz.
-: drvseek ( off u -- a )
- dup drvblksz > if abort" can't ensure that many bytes in drv buffer" then
- swap drvblksz /mod ( u r q ) ?drv@ ( u r )
- tuck + drvblksz >= if drvcurblk 1+ drvbuf( drvblksz + (drv@) then ( r )
- drvbuf( + ( a ) ;
diff --git a/fs/tests/sys/all.fs b/fs/tests/sys/all.fs
@@ -1,3 +1,2 @@
\ Run all sys test suites
-f<< /tests/sys/drive.fs
f<< /tests/sys/file.fs
diff --git a/fs/tests/sys/drive.fs b/fs/tests/sys/drive.fs
@@ -1,6 +0,0 @@
-?f<< tests/harness.fs
-testrequires /sys/drive.fs
-testbegin
-\ Tests for sys/ramdrive (already loaded in init.fs)
-$36 8 drvseek ( a ) 8 []>str S" FAT12 " #s=
-testend
diff --git a/fs/xcomp/bootlo.fs b/fs/xcomp/bootlo.fs
@@ -99,10 +99,11 @@ alias else endof immediate
\ Return whether strings s1 and s2 are equal
: s= ( s1 s2 -- f ) over c@ 1+ []= ;
-\ These aliases belong to /sys/drive, but we need them earlier in the boot
-\ process, so we anticipate.
-$200 value drvblksz
-alias abort (drv@)
+\ Drive API
+\ Anticipating lib/drive
+: sectorsize ( drv -- ) @ ;
+: drv@ ( sec dst drv -- ) dup 4 + @ execute ;
+: drv! ( sec src drv -- ) dup 8 + @ execute ;
\ Autoloading
0 value floaded \ address of the current "loaded file" structure
diff --git a/fs/xcomp/pc/glue1.fs b/fs/xcomp/pc/glue1.fs
@@ -1 +1 @@
-' biossec@ to (drv@)
+INT13hDrive value fatdrv
diff --git a/fs/xcomp/pc/init.fs b/fs/xcomp/pc/init.fs
@@ -14,7 +14,7 @@ f<< /drv/pc/ata.fs
\ under QEMU, but not on real hardware.
\ Update: hum no, it seems that my notebook doesn't list anything at all on its
\ IDE buses. Maybe I'll have to go straight to AHCI...
-\ ' ata@ to (drv@)
+\ ATADrive to fatdrv
f<< /drv/pc/idt.fs
diff --git a/posix/glue1.fs b/posix/glue1.fs
@@ -1,4 +1,3 @@
\ Glue code between the storage driver and the FS handler
fatfs( to ramdrv(
-RAMDRVSECSZ to drvblksz
-' ramdrv@ to (drv@)
+RAMDrive value fatdrv
diff --git a/posix/init.fs b/posix/init.fs
@@ -2,9 +2,4 @@
0 S" sys" fchild S" file.fs" fchild fload
\ We now have f<<
f<< sys/doc.fs
-f<< sys/drive.fs
-RAMDRVSECSZ to drvblksz
-fatfs( to ramdrv(
-' ramdrv@ to (drv@)
-' ramdrv! to (drv!)
f<< fs/fat.fs