# Drive

The Drive struct 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 the driver's responsibility to convert LBA
to CHS.

The /lib/drive unit contains utilities around the Drive structure. This unit in
split in two, between /lib/drivelo and lib/drive. /lib/drivelo is designed to be
used at boot time. /lib/drive complements it with additional features.

Fields:

secsz   size in bytes of each sector read and written by the driver.
        It's often 512.
seccnt  Number of sectors in the drive

Methods:

:sec@ ( sec dst drv -- )
  Read sector "sec" in memory address "dst", which needs to be a buffer at least
  "secsz" in size. "drv" is a pointer to a Drive structure.

:sec! ( sec src drv -- )
  Write sector "sec" from memory address "src".

## SectorWindow

A SectorWindow is a buffer referencing one or more sectors and holding one
sector in memory. It manages the "dirty" status of the buffer and makes it easy
to seek within the window, automatically managing buffer status.

For example, one could open a SectorWindow of 4 512b sectors, starting at sector
$2a. The buffer would begin holding sector $2a in its :buf(. The user could
:seek relative address $212 within it, which would make it select sector $2b and
return address $12 relative to :buf(. If the dirty flag is set, the buffer
ensures that the contents is saved before loading a new one.

Fields:

drv     Target Drive
fsec    First sector of the window
cnt     Number of sectors in the window
sec     Sector currently in buffer
dirty   Whether the buffer is dirty

Words:

:new ( drv -- secwin )
  Create a new buffer targeting Drive "drv", on sector 0, cnt 1. Current buffer
  starts uninitialized.

:buf( ( self -- a )
  Address of the buffer.

:)buf ( self -- a )
  End of the buffer.

:move ( fsec cnt self -- )
  Move window to "fsec" with "cnt" sectors. This doesn't change or flush the
  current buffer.

:fulldrv ( self -- )
  Move the window so that it spans the whole drive.

:load ( sec self -- )
  Load sector "sec" in the buffer. Aborts if outside the window.

:flush ( self -- )
  If buffer is dirty, save it.

:seek ( pos self -- a? n-or-0 )
  Load, if needed, the correct sector so that the position "pos", in bytes
  relative to the beginning of "fsec", is available in the buffer. Returns its
  address in "a" as well as the number of bytes that can be read before the end
  of the buffer is reaches, as "n". If "pos" is outside the window, "n" is 0 and
  "a" is absent.

:next ( self -- a? n-or-0 )
  :seek the first byte of the sector following the one currently in the buffer.

:dirty! ( self -- )
  Make the buffer dirty.

## DriveIO

DriveIO is a structure allowing direct access to a Drive through the convenience
of a IO API. Its buffer size is the drive's sector size. It lives on top of a
SectorWindow and by defaults targets the whole drive, although the :move word
can be used to reduce the scope of the file.

Fields:

secwin The target SectorWindow.

Words:

:new ( drv -- hdl )
  Create a new DriveIO interfacing Drive "drv".

:move ( fsec cnt self -- )
  Calls secwin's :move and seek pos 0.
