MS-DOS 2.0 Device Drivers

INTRODUCTION

    In the  past,  DOS-device  driver  (BIOS for those who are
familiar with  CP/M)  communication  has  been  mediated  with
registers and   a  fixed-address  jump-table.   This  approach
has suffered heavily  from  the  following  two  observations:

    o   The old jump-table ideas of  the  past  are  fixed  in
        scope and allow no extensibility.

    o   The past  device  driver  interfaces have been written
        without regard for the true  power  of  the  hardware.
        When   a   multitasking  system  or  interrupt  driven
        hardware is installed  a  new  BIOS  must  be  written
        largely from scratch.

    In MSDOS  2.0, the DOS-device driver interface has changed
from the old jump-table style  to  one  in  which  the  device
drivers  are  linked  together  in  a  list.   This allows new
drivers for  optional  hardware  to  be  installed  (and  even
written) in  the  field  by other vendors or the user himself.
This flexibility is one of the major new  features  of  MS-DOS
2.0.

    Each driver  in  the  chain  defines two entry points; the
strategy routine and  the  interrupt  routine.   The  2.0  DOS
does not  really make use of two entry points (it simply calls
strategy, then immediately calls interrupt).  This dual  entry
point scheme  is  designed  to facilitate future multi-tasking
versions of MS-DOS.  In multi-tasking  environments  I/O  must
be asynchronous,  to  accomplish  this  the  strategy  routine
will be called to queue  (internally)  a  request  and  return
quickly.  It  is  then  the  responsibility  of  the interrupt
routine to perform the actual I/O at interrupt time by picking
requests  off  the  internal  queue  (set  up  by the strategy
routine), and process  them.   When  a  request  is  complete,
it is  flagged  as  "done"  by the interrupt routine.  The DOS
periodically scans the  list  of  requests  looking  for  ones
flagged as  done,  and  "wakes up" the process waiting for the
completion of the request.

    In order for requests to be  queued  as  above  it  is  no
longer sufficient  to pass I/O information in registers, since
many requests may be  pending  at  any  one  time.   Therefore
the new  device  interface uses data "packets" to pass request
information.  A device is called with a pointer to  a  packet,
this packet  is  linked  into  a  global  chain of all pending
I/O requests maintained by the DOS.   The  device  then  links
the packet  into  its  own  local  chain  of requests for this
particular  device.   The  device  interrupt   routine   picks
requests of  the  local  chain  for processing.  The DOS scans
the  global  chain  looking  for  completed  requests.   These
packets are  composed  of  two  pieces,  a  static piece which
has the same  format  for  all  requests  (called  the  static
request header),  which  is  followed  by information specific
to the request.  Thus packets have a variable size and format.

    At this points it should be  emphasized  that  MS-DOS  2.0
does not  implement most of these features, as future versions
will.  There is no global or local queue.   Only  one  request
is pending at any one time, and the DOS waits for this current
request to be completed.  For 2.0 it  is  sufficient  for  the
strategy routine  to  simply  store  the address of the packet
at a fixed location, and for the  interrupt  routine  to  then
process  this  packet  by  doing  the  request  and returning.
Remember:  the DOS just calls the strategy  routine  and  then
immediately calls  the  interrupt  routine, it is assumed that
the request is completed when the interrupt  routine  returns.
This additional  functionality  is  defined  at  this  time so
that people will be  aware  and  thinking  about  the  future.


FORMAT OF A DEVICE DRIVER

    A device  driver  is  simply  a  relocatable  memory image
with all of the code in  it  to  implement  the  device  (like
a .COM  file,  but  not ORGed at 100 Hex).  In addition it has
a special header at the front of it  which  identifies  it  as
a device,  defines  the  strategy  and interrupt entry points,
and defines various  attributes.   It  should  also  be  noted
that there are two basic types of devices.

    The first  is  character devices.  These are devices which
are designed to do character  I/O  in  a  serial  manner  like
CON, AUX,  and  PRN.   These devices are named (ie.  CON, AUX,
CLOCK, etc.), and users may open channels  (FCBs)  to  do  I/O
to them.

    The second  class  of  devices  is  block  devices.  These
devices are the "disk drives"  on  the  system,  they  can  do
random I/O  in  pieces  called  blocks  (usually  the physical
sector size) and  hence  the  name.   These  devices  are  not
"named" as  the  character  devices  are, and therefore cannot
be "opened" directly.   Instead  they  are  "mapped"  via  the
drive letters (A,B,C, etc.).

    Block devices  also  have  units.  In other words a single
driver may be responsible for one or more  disk  drives.   For
instance block device driver ALPHA (please note that we cannot
actually  refer  to  block  devices  by  a  name!)    may   be
responsible for  drives  A,B,C  and  D, this simply means that
it has four units (0-3) defined and therefore  takes  up  four
drive letters.   Which units correspond to which drive letters
is determined by the position  of  the  driver  in  the  chain
of all  drivers:   if  driver  ALPHA is the first block driver
in the device chain, and it defines 4 units (0-3),  then  they
will be  A,B,C  and  D.   If  BETA  is the second block driver
and defines three units (0-2),  then  they  will  be  E,F  and
G and  so  on.   MS-DOS  2.0 is not limited to 16 block device
units, as  previous  versions  were.   The  theoretical  limit
is 63  (2^6  -  1),  but  it should be noted that after 26 the
drive letters get a little strange (like ] \  and  ^).   NOTE:
Character devices  cannot  define multiple units (this because
they have only one name).


Here is what that special device header looks like:

           +--------------------------------------+
           | DWORD Pointer to next device         |
           | (Must be set to -1)                  |
           +--------------------------------------+
           | WORD Attributes                      |
           |  Bit 15 = 1 if char device 0 if blk  |
           |  if bit 15 is 1                      |
           |      Bit 0 = 1 if Current sti device |
           |      Bit 1 = 1 if Current sto output |
           |      Bit 2 = 1 if Current NUL device |
           |      Bit 3 = 1 if Current CLOCK dev  |
           |      Bit 4 = 1 if SPECIAL            |
           |  Bit 14 is the IOCTL bit (see below) |
           |  Bit 13 is the NON IBM FORMAT bit    |
           +--------------------------------------+
           | WORD Pointer to Device strategy      |
           |      entry point                     |
           +--------------------------------------+
           | WORD Pointer to Device interrupt     |
           |      entry point                     |
           +--------------------------------------+
           | 8-BYTE character device name field   |
           | Character devices set a device name  |
           | For block devices the first byte is  |
           | The number of units                  |
           +--------------------------------------+

    Note that the device entry points are  words.   They  must
be offsets  from  the  same  segment  number  used to point to
this table.  Ie.  if XXX.YYY  points  to  the  start  of  this
table, then  XXX.strategy  and  XXX.interrupt  are  the  entry
points.

    A word about the Attribute  field.   This  field  is  used
most importantly  to  tell  the  system whether this device is
a block or character device (bit  15).   Most  of  other  bits
are used  to  give  selected character devices certain special
treatment (NOTE: these bits mean nothing on a  block  device).
Let's say  a  user  has  a new device driver which he wants to
be the standard input and  output.   Besides  just  installing
the driver  he  needs  to  tell  SYSINIT (and the DOS) that he
wishes his new driver to override  the  current  sti  and  sto
(the "CON"  device).   This  is  accomplished  by  setting the
attributes to the desired characteristics,  so  he  would  set
Bits 0  and 1 to 1 (note that they are separate!!).  Similarly
a  new  CLOCK  device  could  be  installed  by  setting  that
attribute, see  the  section  at  the end on the CLOCK device.
NOTE: that although there  is  a  NUL  device  attribute,  the
NUL device  cannot  be  re-assigned.   This  attribute  exists
for the DOS so that it can tell if the  NUL  device  is  being
used.

    The NON  IBM  FORMAT  bit  applies  only  to block devices
and effects the operation of the  get  BPB  device  call  (see
below).

    The other  bit  of  interest  is  the  IOCTL bit which has
meaning on character or block devices.   This  bit  tells  the
DOS whether  this  device  can handle control strings (via the
IOCTL system call).

    If a driver cannot  process  control  strings,  it  should
initially set  this  bit  to  0.  This tells the DOS to return
an error if an attempt is made  (via  IOCTL  system  call)  to
send or  receive  control  strings  to  this device.  A device
which can process control  strings  should  initialize  it  to
1.  For  drivers  of  this  type,  the  DOS will make calls to
the IOCTL INPUT  and  OUTPUT  device  functions  to  send  and
receive   IOCTL   strings   (see  IOCTL  in  the  SYSTEM-CALLS
document).

    The IOCTL functions allow data to  be  sent  and  received
by the  device  itself for its own use (to set baud rate, stop
bits, form length etc., etc.), instead of  passing  data  over
the  device  channel  as  a  normal  read  or write does.  The
interpretation of the passed information is up to the  device,
but it MUST NOT simply be treated as a normal I/O.

    The SPECIAL  bit  applies  only  to  character drivers and
more particularly to  CON  drivers.   The  new  2.0  interface
is a  much  more  general  and  consistent  interface than the
old 1.25 DOS interface.  It allows for a number of  additional
features of  2.0.   It  is  also slower than 1.25 if old style
"single byte" system calls are made.  To make  most  efficient
use of  the  interface  all  applications  should  block their
I/O as much as possible.  This  means  make  one  XENIX  style
system call  to  output  X  bytes  rather  than X system calls
to output one byte each.  Also putting  a  device  channel  in
RAW   mode  (see  IOCTL)  provides  a  means  of  putting  out
characters even FASTER  than  1.25.   To  help  alleviate  the
CON output  speed  problem  for  older  programs which use the
1 - 12 system calls  to  output  large  amounts  of  data  the
SPECIAL bit  has  been implemented.  If this bit is 1 it means
the device is the  CON  output  device,  and  has  implemented
an interrupt  29  Hex  handler,  where  the  29 Hex handler is
defined as follows:

        Interrupt 29h handlers

        Input:
                Character in AL

        Function:
                output the character in al to the user
                screen.
        Output:
                None
        Registers:
                all registers except bx must be preserved.
                No registers except for al have a known or
                consistent value.

    If a  character  device  implements  the  SPECIAL  bit, it
is the responsibility of the  driver  to  install  an  address
at the  correct  location in the interrupt table for interrupt
29 Hex as part of  its  INIT  code.   IMPLICATION:  There  can
be only  one  device  driver  with  the SPECIAL bit set in the
system.  There is no check to insure this state.

WARNING: THIS FEATURE WILL NOT BE SUPPORTED IN FUTURE VERSIONS
    OF THE  OPERATING  SYSTEM.   IMPLICATION:  Any application
    (not device driver)  which  uses  INT  29H  directly  will
    not work on future versions, YOU HAVE BEEN WARNED.

    In  order  to  "make"  a  device  driver  that SYSINIT can
install, a memory image or .EXE  (non-IBM  only)  format  file
must be  created  with  the  above  header  at the start.  The
link field should be  initialized  to  -1  (SYSINIT  fills  it
in).   The  attribute  field  and  entry  points  must  be set
correctly, and if the device is a character device,  the  name
field must  be  filled  in  with  the  name (if a block device
SYSINIT will fill in  the  correct  unit  count).   This  name
can be  any  8  character  "legal" file name.  In fact SYSINIT
always installs character devices at the start of  the  device
list, so  if  you  want  to  install  a new CON device all you
have to do is name it "CON".  The new  one  is  ahead  of  the
old one  in  the  list  and  thus  preempts the old one as the
search for devices stops on  the  first  match.   Be  sure  to
set the sti and sto bits on a new CON device!

NOTE:  Since  SYSINIT  may  install  the  driver anywhere, you
    must be very careful about  FAR  memory  references.   You
    should NOT  expect  that  your  driver will go in the same
    place every time (The default BIOS  drivers  are  exempted
    from this of course).


INSTALLATION OF DEVICE DRIVERS

    Unlike past versions MS-DOS 2.0 allows new device  drivers
to   be   installed   dynamically   at  boot  time.   This  is
accomplished by the new SYSINIT module supplied by  Microsoft,
which reads  and  processes  the CONFIG.SYS file.  This module
is linked together with the OEM  default  BIOS  in  a  similar
manner to the way FORMAT is built.

    One of  the  functions  defined  for  each device is INIT.
This routine is called once  when  the  device  is  installed,
and never  again.  The only thing returned by the init routine
is a location (DS:DX) which is a pointer  to  the  first  free
byte of  memory  after  the  device  driver, (like a terminate
and stay resident).  This pointer method can be used to "throw
away" initialization  code  that  is  only needed once, saving
on space.

    Block devices are installed the same way and  also  return
a first  free  byte  pointer  as above, additional information
is also returned:

    o   The number  of  units  is  returned,  this  determines
        logical device  names.  If the current maximum logical
        device letter is F at the time of  the  install  call,
        and the init routine returns 4 as the number of units,
        then they will have logical  names  G,  H,  I  and  J.
        This mapping  is  determined  by  by  the  position of
        the driver in the device list and the number of  units
        on the  device (stored in the first byte of the device
        name field).

    o   A pointer to a  BPB  (Bios  Parameter  Block)  pointer
        array is  also  returned.   This  will  be  similar to
        the INIT table used in  previous  versions,  but  will
        have more  information  in  it.   There  is  one table
        for each unit defined.   These  blocks  will  be  used
        to build  a  DPB  (Drive  Parameter Block) for each of
        the units.  The pointer passed to  the  DOS  from  the
        driver points  to  an array of n word pointers to BPBs
        where n is the  number  of  units  defined.   In  this
        way if  all  units  are  the same, all of the pointers
        can point to the same BPB, saving space.   NOTE:  this
        array must  be  protected  (below the free pointer set
        by the return) since the DPB will  be  built  starting
        at the  byte  pointed  to  by  the  free pointer.  The
        sector size defined must be  less  than  or  equal  to
        the maximum  sector  size defined at default BIOS init
        time.  If it isn't the install  will  fail.   One  new
        piece of DPB info set from this table will be a "media
        descriptor byte".  This  byte  means  nothing  to  the
        DOS, but  is  passed to devices so that they know what
        form of a  DPB  the  DOS  is  currently  using  for  a
        particular Drive-Unit.

    Block devices  may  take  several  approaches; they may be
dumb or smart.   A  dumb  device  would  define  a  unit  (and
therefore a  DPB)  for  each possible media drive combination.
Unit 0 = drive 0 single side, unit 1 = drive  0  double  side,
etc.  For  this  approach  media  descriptor  bytes would mean
nothing.  A smart device would allow multiple media per  unit,
in this  case the BPB table returned at init must define space
large  enough  to  accommodate  the  largest  possible   media
supported.  Smart  drivers  will  use the "media byte" to pass
around info about what media is currently in  a  unit.   NOTE:
If the  DPB  is  a  "hybrid"  made  to get the right sizes, it
should give an invalid "media byte" back to the DOS.

    The BOOT  (default  BIOS)  drivers  are  installed  pretty
much as  above.   The preset device list is scanned.  If block
drivers are encountered they  are  installed  as  above  (with
the exception  that  the  break is not moved since the drivers
are already resident in the  BIOS).   Note  that  the  logical
drive letters  are  assigned  in  list  order, thus the driver
which is to have logical A must  be  the  first  unit  of  the
first  block  device  in  the  list.   The  order of character
devices is also important.  There must be at least 4 character
devices defined  at  boot which must be the first four devices
(of either  type),  the  first  will  become  standard  input,
standard output,  and  standard error output.  The second will
become standard auxiliary input and  output,  the  third  will
become standard  list  output,  and  the forth will become the
date/time (CLOCK) device.  Thus  the  BIOS  device  list  must
look like this:

->CON->AUX->PRN->CLOCK->any other block or character devices

THE DRIVER

    A device driver will define the following functions:

  Command   Function
   Code

     0      INIT
     1      MEDIA CHECK (Block only, NOP for character)
     2      BUILD BPB      "    "     "    "   "
     3      IOCTL INPUT (Only called if device has IOCTL)
     4      INPUT (read)
     5      NON-DESTRUCTIVE INPUT NO WAIT (Char devs only)
     6      INPUT STATUS                    "     "    "
     7      INPUT FLUSH                     "     "    "
     8      OUTPUT (write)
     9      OUTPUT (Write) with verify
    10      OUTPUT STATUS                   "     "    "
    11      OUTPUT FLUSH                    "     "    "
    12      IOCTL OUTPUT (Only called if device has IOCTL)

    As mentioned before, the first entry point is the strategy
routine which  is called with a pointer to a data block.  This
call does not perform the request, all it  does  is  queue  it
(save the  data  block  pointer).   The second interrupt entry
point is called immediately  after  the  strategy  call.   The
"interrupt" routine  is called with no parameters, its primary
function is to perform  the  operation  based  on  the  queued
data block and set up any returns.

    The "BUILD  BPB"  and  "MEDIA  CHECK"  are the interesting
new ones, these are explained by  examining  the  sequence  of
events in the DOS which occurs when a drive access call (other
than read or write) is made:

        I.  Turn drive letter  into  DPB  pointer  by  looking
            for DPB with correct driver-unit number.

        II. Call device  driver  and  request  media check for
            Drive-Unit.   DOS   passes   its   current   Media
            descriptor byte (from DPB).  Call returns:

                Media Not Changed
                Media Changed
                Not Sure
                Error

            Error - If an error occurs the error  code  should
                be set accordingly.

            Media Not  changed  -  Current  DPB and media byte
                are OK, done.

            Media Changed - Current DPB and media  are  wrong,
                invalidate any  buffers  for  this  unit,  and
                goto III.

            Not Sure - If there are  dirty  buffers  for  this
                unit, assume  DPB  and  media  byte are OK and
                done.  If nothing dirty, assume media changed,
                invalidate any  buffers  for  unit,  and  goto
                III.

            NOTE:  If a hybrid  DPB  was  built  at  init  and
                an invalid  Media  byte  was  set,  the driver
                should return media changed when this  invalid
                media byte is encountered.

        III. Call device  driver  to build BPB with media byte
            and buffer.

    What the  driver  must  do  at  step  III is determine the
correct media that is currently in  the  unit,  and  return  a
pointer to  a  BPB table (same as for the install call).  This
table will be used as at init  to  build  a  correct  DPB  for
the unit  If the determined media descriptor byte in the table
turns out to be the same as the one passed in,  then  the  DOS
will not  build  a new table, but rather just use the old one.
Therefore in this case the driver doesn't  have  to  correctly
fill in the other entries if desired.

    The build  BPB  call  also  gets a pointer to a one sector
buffer.  What this buffer contains is determined  by  the  NON
IBM FORMAT  bit  in  the  attribute field.  If the bit is zero
(device is IBM format compatible)  then  the  buffer  contains
the first  sector  of  the  first  FAT,  in particular the FAT
ID byte is the first byte  of  this  buffer.   NOTE:  It  must
be true  that  the  BPB is the same, as far as location of the
FAT is concerned, for all possible  media.   This  is  because
this first  FAT  sector  must  be  read  BEFORE the actual BPB
is returned.  If the NON  IBM  FORMAT  bit  is  set  then  the
pointer points  to  one  sector  of scratch space which may be
used for anything.

CALL FORMAT

    When the  DOS calls a device driver to perform a finction,
it passes a  structure  (Drive  Request  Structure)  in  ES:BX
to perform  operations  and  does  a long call to the driver's
strategy entry point.  This structure is a fixed length header
(Static Request  Header)  followed  by  data  pertinent to the
operation  being  performed.   NOTE:   It   is   the   drivers
responsibility to preserve machine state.

STATIC REQUEST HEADER ->
               +-----------------------------+
               | BYTE length of record       |
               |  Length in bytes of this    |
               |  Drive Request Structure    |
               +-----------------------------+
               | BYTE unit code              |
               |  The subunit the operation  |
               |  is for (minor device)      |
               |  (no meaning on character   |
               |   devices)                  |
               +-----------------------------+
               | BYTE command code           |
               +-----------------------------+
               | WORD Status                 |
               +-----------------------------+
               | 8 bytes reserved here for   |
               | two DWORD links. One will   |
               | be a link for the DOS queue |
               | The other for the device    |
               | queue                       |
               +-----------------------------+

STATUS WORD

      15  14 13 12 11 10  9   8   7  6  5  4  3  2  1  0
    +---+---+--+--+--+--+---+---+--+--+--+--+--+--+--+--+
    | E |               | B | D |                       |
    | R |   RESERVED    | U | O | ERROR CODE (bit 15 on)|
    | R |               | I | N |                       |
    +---+---+--+--+--+--+---+---+--+--+--+--+--+--+--+--+

    The status  word is zero on entry and is set by the driver
interrupt routine on return.

    Bit 8 is the done bit, it means the operation is complete.
For the  moment  the Driver just sets it to one when it exits,
in the future this will be set by  the  interrupt  routine  to
tell the DOS the operation is complete.

    Bit 15 is the error bit, if it  is  set  then  the  low  8
bits indicate the error:

           0 Write Protect violation
    (NEW)  1 Unknown Unit
           2 Drive not ready
    (NEW)  3 Unknown command
           4 CRC error
    (NEW)  5 Bad Drive Request Structure length
           6 Seek error
    (NEW)  7 Unknown media
           8 Sector not found
    (NEW)  9 Printer out of paper
           A Write Fault
    (NEW)  B Read Fault
           C General Failure

Bit 9 is the busy bit which is set only by status  calls  (see
STATUS CALL below).


   Here is the data block format for each function:

READ or WRITE - ES:BX (Including IOCTL) ->
            +------------------------------------+
            | 13-BYTE  Static Request Header     |
            +------------------------------------+
            | BYTE Media descriptor  from DPB    |
            +------------------------------------+
            | DWORD transfer address             |
            +------------------------------------+
            | WORD byte/sector Count             |
         ---+------------------------------------+---
            | WORD starting sector number        |
            |  (ignored on Char Devs)            |
            +------------------------------------+

    In addition to setting the status word,  the  driver  must
set the  Sector  count  to  the  actual  number of sectors (or
bytes) transferred.  NOTE: No  error  check  is  performed  on
an IOCTL I/O call, driver MUST correctly set the return sector
(byte) count  to  the  actual  number  of  bytes  transferred,
however.

NOTE: THE FOLLOWING APPLIES TO BLOCK DEVICE DRIVERS.

    Under certain  circumstances  the  BIOS  may  be  asked to
do a write operation of 64K bytes which seems to  be  a  "wrap
around" of  the transfer address in the BIOS I/O packet.  This
arises due to an optimization  added  to  the  write  code  in
MS-DOS.  It will only manifest on user WRITEs which are within
a sector size of 64K bytes on files which are  "growing"  past
the current  EOF.   IT  IS  ALLOWABLE  FOR  THE BIOS TO IGNORE
THE BALANCE OF  THE  WRITE  WHICH  "WRAPS  AROUND"  IF  IT  SO
CHOOSES.   For  instance  a  WRITE  of  10000H  bytes worth of
sectors with a transfer address  of  XXX:1  could  ignore  the
last two bytes (remember that a user program can never request
an I/O of more than FFFFH bytes and cannot wrap  around  (even
to 0)  in  his  transfer segment, so in this case the last two
bytes can be ignored).


NON DESRUCTIVE READ NO WAIT - ES:BX ->
            +------------------------------------+
            | 13-BYTE Static Request Header      |
            +------------------------------------+
            | BYTE read from device              |
            +------------------------------------+

    This call is analogous to the console  input  status  call
on MS-DOS  1.25.   If  the  character  device returns Busy bit
= 0 (characters in  buffer),  then  the  next  character  that
would be  read  is  returned.   This  character is NOT removed
from the input buffer (hence the term Non  Destructive  Read).
In essence  this  call  allows the DOS to look ahead one input
character.


MEDIA CHECK - ES:BX ->
            +------------------------------------+
            | 13-BYTE  Static Request Header     |
            +------------------------------------+
            | BYTE Media Descriptor from DPB     |
            +------------------------------------+
            | BYTE returned                      |
            +------------------------------------+

    In addition to setting status word, driver  must  set  the
return byte.

    Return Byte :
        -1 Media has been changed
         0 Don't know if media has been changed
         1 Media has not been changed

    If the driver can return -1 or 1 (by  having  a  door-lock
or other  interlock  mechanism)  the  performance of MSDOS 2.0
is enhanced as the DOS  need  not  reread  the  FAT  for  each
directory access.


BUILD BPB - ES:BX ->
            +------------------------------------+
            | 13-BYTE Static Request Header      |
            +------------------------------------+
            | BYTE Media Descriptor from DPB     |
            +------------------------------------+
            | DWORD Transfer Address             |
            | (points to one sectors worth of    |
            |  scratch space or first sector     |
            |  of FAT depending on the value     |
            |  of the NON IBM FORMAT bit)        |
            +------------------------------------+
            | DWORD Pointer to BPB               |
            +------------------------------------+

    If the NON IBM FORMAT bit  of  the  device  is  set,  then
the DWORD  Transfer  Address  points  to  a  one sector buffer
which can be used for any purpose.   If  the  NON  IBM  FORMAT
bit is  0,  then  this buffer contains the first sector of the
FAT; in this case the driver must not alter this buffer  (this
mode is  useful  if  all  that  is  desired is to read the FAT
ID byte).

    If IBM compatible format  is  used  (NON  IBM  FORMAT  BIT
= 0),  then it must be true that the first sector of the first
FAT is located at the  same  sector  on  all  possible  media.
This is  because  the FAT sector will be read BEFORE the media
is actually determined.

    In addition to setting status word, driver  must  set  the
Pointer to the BPB on return.


    In order to allow for many different  OEMs  to  read  each
other's disks,  the  following  standard  is  suggested:   The
information relating to the BPB  for  a  particular  piece  of
media   is  kept  in  the  boot  sector  for  the  media.   In
particular, the format of the boot sector is:

            +------------------------------------+
            | 3 BYTE near JUMP to boot code      |
            +------------------------------------+
            | 8 BYTES OEM name and version       |
         ---+------------------------------------+---
         B  | WORD bytes per sector              |
         P  +------------------------------------+
         B  | BYTE sectors per allocation unit   |
            +------------------------------------+
         |  | WORD reserved sectors              |
         V  +------------------------------------+
            | BYTE number of FATs                |
            +------------------------------------+
            | WORD number of root dir entries    |
            +------------------------------------+
            | WORD number of sectors in logical  |
         ^  | image                              |
         |  +------------------------------------+
         B  | BYTE media descriptor              |
         P  +------------------------------------+
         B  | WORD number of FAT sectors         |
         ---+------------------------------------+---
            | WORD sectors per track             |
            +------------------------------------+
            | WORD number of heads               |
            +------------------------------------+
            | WORD number of hidden sectors      |
            +------------------------------------+

    The three  words  at the end are optional, the DOS doesn't
care about them (since they are not part of  the  BPB).   They
are intended  to  help the BIOS understand the media.  Sectors
per track may be redundant (could be figured  out  from  total
size of  the  disk).  Number of heads is useful for supporting
different  multi-head  drives  which  have  the  same  storage
capacity, but  a  different  number  of  surfaces.   Number of
hidden sectors is useful  for  supporting  drive  partitioning
schemes.


    Currently, the media  descriptor  byte  has  been  defined
for a small range of media:

    5 1/4" diskettes:

        Flag bits:
            01h - on -> 2 double sided

        All other bits must be on.

    8" disks:
        FEh - IBM  3740 format, singled-sided, single-density,
            128 bytes per sector,  soft  sectored,  4  sectors
            per allocation  unit,  1  reserved sector, 2 FATs,
            68 directory entries, 77*26 sectors

        FDh -   8"    IBM    3740    format,    singled-sided,
            single-density,   128   bytes   per  sector,  soft
            sectored,  4  sectors  per  allocation   unit,   4
            reserved sectors,  2  FATs,  68 directory entries,
            77*26 sectors

        FEh - 8"  Double-sided,  double-density,  1024   bytes
            per sector, soft sectored, 1 sector per allocation
            unit, 1 reserved sector,  2  FATs,  192  directory
            entries, 77*8*2 sectors


STATUS Calls - ES:BX ->
            +------------------------------------+
            | 13-BYTE Static Request Header      |
            +------------------------------------+

    All driver must do is  set  status  word  accordingly  and
set the busy bit as follows:

    o   For output on  character  devices:   If  it  is  1  on
        return, a  write  request  (if  made)  would  wait for
        completion of a current request.  If it  is  0,  there
        is no  current  request  and a write request (if made)
        would start immediately.

    o   For input on character devices with a buffer a  return
        of 1  means,  a  read  request  (if  made) would go to
        the physical device.  If  it  is  0  on  return,  then
        there are  characters  in  the  devices  buffer  and a
        read would return  quickly,  it  also  indicates  that
        the user  has  typed  something.   The DOS assumes all
        character devices have an  input  type  ahead  buffer.
        Devices which  don't  have  them  should always return
        busy = 0 so  that  the  DOS  won't  hang  waiting  for
        something to  get  into  a buffer which doesn't exist.


FLUSH Calls - ES:BX ->
            +------------------------------------+
            | 13-BYTE Static Request Header      |
            +------------------------------------+

    This  call  tells  the  driver  to  flush  (terminate) all
pending requests  that  it  has knowledge of.  Its primary use
is to flush the input queue on character devices.


INIT - ES:BX ->
            +------------------------------------+
            | 13-BYTE Static Request Header      |
            +------------------------------------+
            | BYTE # of units                    |
            +------------------------------------+
            | DWORD Break Address                |
         ---+------------------------------------+---
            | DWORD Pointer to BPB array         |
            | (not set by Character devices)     |
            +------------------------------------+

    The number of units, break address, and  BPB  pointer  are
set by the driver.


FORMAT OF BPB (Bios Parameter Block) -

            +------------------------------------+
            | WORD Sector size in Bytes          |
            |    Must be at least 32             |
            +------------------------------------+
            | BYTE Sectors/Allocation unit       |
            |    Must be a power of 2            |
            +------------------------------------+
            | WORD Number of reserved sectors    |
            |        May be zero                 |
            +------------------------------------+
            | BYTE Number of FATS                |
            +------------------------------------+
            | WORD Number of directory entries   |
            +------------------------------------+
            | WORD Total number of sectors       |
            +------------------------------------+
            | BYTE Media descriptor              |
            +------------------------------------+
            | WORD Number of sectors occupied by |
            |      FAT                           |
            +------------------------------------+


THE CLOCK DEVICE

    One of  the  most  popular add on boards seems to be "Real
Time CLOCK Boards".  To allow these boards  to  be  integrated
into the  system  for TIME and DATE, there is a special device
(determined by the attribute word) which is the CLOCK  device.
In all  respects  this  device  defines and performs functions
like any other character device (most functions will  be  "set
done bit,  reset  error  bit,  return).   When a read or write
to this device occurs, exactly 6 bytes are transferred.   This
I/O can be thought of as transferring 3 words which correspond
exactly to the values of AX, CX and  DX  which  were  used  in
the old  1.25  DOS  date  and  time  routines.  Thus the first
two bytes are a word which is the count of days since  1-1-80.
The  third  byte  is  minutes,  the  fourth  hours,  the fifth
hundredths of seconds, and the  sixth  seconds.   Reading  the
CLOCK device  gets  the  date and time, writing to it sets the
date and time.
�����������������������������������������������������������������������