home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / docum / advdos.doc / s1c14 < prev    next >
Encoding:
Text File  |  1992-04-21  |  83.0 KB  |  1,931 lines

  1. ────────────────────────────────────────────────────────────────────────────
  2. Chapter 14  Installable Device Drivers
  3.  
  4.   Device drivers are the modules of an operating system that control the
  5.   hardware. They isolate the operating-system kernel from the specific
  6.   characteristics and idiosyncrasies of the peripheral devices interfaced to
  7.   the central processor. Thus, the driver's relationship to the kernel is
  8.   analogous to the operating system's relationship to application programs.
  9.  
  10.   The installable device drivers that were introduced in MS-DOS version 2
  11.   give the user great flexibility. They allow the user to customize and
  12.   configure the computer for a wide range of peripheral devices, with a
  13.   minimum of troublesome interactions and without having to "patch" the
  14.   operating system. Even the most inexperienced user can install a new
  15.   device into a system by plugging in a card, copying a driver file to the
  16.   boot disk, and editing the system configuration file.
  17.  
  18.   For those inclined to do their own programming, the MS-DOS installable
  19.   device drivers are interfaced to the hardware-independent kernel through a
  20.   simple and clearly defined scheme of function codes and data structures.
  21.   Given adequate information about the hardware, any competent assembly-
  22.   language programmer can expect to successfully interface even the most
  23.   bizarre device to MS-DOS without altering the operating system in the
  24.   slightest and without acquiring any special or proprietary knowledge about
  25.   its innards.
  26.  
  27.   In retrospect, installable device drivers have proven to be one of the key
  28.   usability features of MS-DOS. I feel that they have been largely
  29.   responsible for the rapid proliferation and competitive pricing of
  30.   high-speed mass-storage devices for MS-DOS machines, and for the growing
  31.   confidence of the average user toward "tampering with" (upgrading) his or
  32.   her machine.
  33.  
  34.  
  35. MS-DOS Device-Driver Types
  36.  
  37.   Drivers written for MS-DOS fall into two distinct classes:
  38.  
  39.   ■  Block-device drivers
  40.  
  41.   ■  Character-device drivers
  42.  
  43.   A driver's class determines what functions it must support, how it is
  44.   viewed by MS-DOS, and how it makes the associated physical device appear
  45.   to behave when an application program makes a request for I/O.
  46.  
  47. Character-Device Drivers
  48.  
  49.   Character-device drivers control peripheral devices that perform input and
  50.   output one character (or byte) at a time, such as a terminal or printer. A
  51.   single character-device driver ordinarily supports a single hardware unit.
  52.   Each character device has a one-to-eight-character logical name, and an
  53.   application program can use this name to open the device for input or
  54.   output, as though it were a file. The logical name is strictly a means of
  55.   identification for MS-DOS and has no physical equivalent on the device.
  56.  
  57.   MS-DOS's built-in character-device drivers for the console, serial port,
  58.   and printer are unique in that an application program can access them in
  59.   three different ways:
  60.  
  61.   ■  It can open them by name (CON, AUX, PRN, etc.) for input and output,
  62.      like any other character device.
  63.  
  64.   ■  It can use the special-purpose MS-DOS function calls (Int 21H Functions
  65.      01-0CH).
  66.  
  67.   ■  It can use the default handles (standard input, standard output,
  68.      standard error, standard auxiliary, and standard printer), which do not
  69.      need to be opened to be used.
  70.  
  71.   The number of additional character-device drivers that can be installed is
  72.   limited only by available memory and by the requirement that each driver
  73.   have a unique logical name. If more than one driver uses the same logical
  74.   name, the last driver to be loaded will supersede any others and will
  75.   receive all I/O requests addressed to that logical name. This fact can
  76.   occasionally be turned to advantage; for example, it allows the user to
  77.   replace the system's default CON driver, which does not support cursor
  78.   positioning or character attributes, with the more powerful ANSI.SYS
  79.   driver.
  80.  
  81.   ASCII vs Binary Mode
  82.  
  83.   MS-DOS regards a handle associated with a character device to be in either
  84.   ASCII (cooked) mode or binary (raw) mode. The mode affects MS-DOS's
  85.   buffering of data for read and write requests. The driver itself is not
  86.   aware of the mode, and the mode does not affect its operation. An
  87.   application can select the mode of a handle with the IOCTL function (Int
  88.   21H Function 44H).
  89.  
  90.   During ASCII-mode input, MS-DOS requests characters one at a time from the
  91.   driver and places them into its own internal buffer, echoing each to the
  92.   screen (if the input device is the keyboard) and checking each character
  93.   for a Ctrl-C (03H). When the number of characters requested by the
  94.   application program has been received, when a Ctrl-Z is detected, or when
  95.   the Enter key is pressed (in the case of the keyboard), MS-DOS terminates
  96.   the input and copies the data from its internal buffer into the requesting
  97.   program's buffer. Similarly, during ASCII-mode output, MS-DOS passes the
  98.   characters to the device driver one at a time and checks for a Ctrl-C
  99.   pending at the keyboard between each character. When a Ctrl-C is detected,
  100.   MS-DOS aborts the input or output operation and transfers to the routine
  101.   whose address is stored in the Int 23H vector.
  102.  
  103.   In binary mode, MS-DOS reads or writes the exact number of bytes requested
  104.   by the application program, without regard to any control characters such
  105.   as Enter or Ctrl-C. MS-DOS passes the entire request through to the driver
  106.   in a single operation, instead of breaking it into single-character reads
  107.   or writes, and transfers the characters directly to or from the requesting
  108.   program's buffer.
  109.  
  110. Block-Device drivers
  111.  
  112.   Block-device drivers usually control random-access mass-storage devices
  113.   such as floppy-disk drives and fixed disks, although they can also be used
  114.   to control non-random-access devices such as magnetic-tape drives. Block
  115.   devices transfer data in chunks, rather than one byte at a time. The size
  116.   of the blocks may be either fixed (disk drives) or variable (tape drives).
  117.  
  118.   A block driver can support more than one hardware unit, map a single
  119.   physical unit onto two or more logical units, or both. Block devices do
  120.   not have file-like logical names, as character devices do. Instead, MS-DOS
  121.   assigns drive designators to the block-device units or logical drives in
  122.   an alphabetic sequence: A, B, and so forth. Each logical drive contains a
  123.   file system: boot block, file allocation table, root directory, and so
  124.   forth. (See Chapter 10.)
  125.  
  126.   A block-device driver's position in the chain of all drivers determines
  127.   the first letter assigned to that driver. The number of logical drive
  128.   units that the driver supports determines the total number of letters
  129.   assigned to it.
  130.  
  131.   Block-device drivers always read or write exactly the number of sectors
  132.   requested (barring hardware or addressing errors) and never filter or
  133.   otherwise manipulate the contents of the blocks being transferred.
  134.  
  135.  
  136. Structure of an MS-DOS Device Driver
  137.  
  138.   A device driver consists of three major parts (Figure 14-1):
  139.  
  140.   ■  A device header
  141.  
  142.   ■  A strategy (strat) routine
  143.  
  144.   ■  An interrupt (intr) routine
  145.  
  146.   We'll discuss each of these in more detail as we work through this
  147.   chapter.
  148.  
  149.   ┌────────────────────────┬─────────────────────────┐
  150.   │                        │     Initialization      │
  151.   │                        ├─────────────────────────┤
  152.   │                        │       Media check       │
  153.   │                        ├─────────────────────────┤
  154.   │                        │        Build BPB        │
  155.   │                        ├─────────────────────────┤
  156.   │                        │  IOCTL read and write   │
  157.   │                        ├─────────────────────────┤
  158.   │                        │         Status          │
  159.   │                        ├─────────────────────────┤
  160.   │                        │          Read           │
  161.   │                        ├─────────────────────────┤
  162.   │                        │   Write, write/verify   │
  163.   │                        ├─────────────────────────┤
  164.   │   Interrupt routine    │    Output until busy    │
  165.   │                        ├─────────────────────────┤
  166.   │                        │      Flush buffers      │
  167.   │                        ├─────────────────────────┤
  168.   │                        │       Device open       │
  169.   │                        ├─────────────────────────┤
  170.   │                        │      Device close       │
  171.   │                        ├─────────────────────────┤
  172.   │                        │ Check whether removable │
  173.   │                        ├─────────────────────────┤
  174.   │                        │     Generic IOCTL       │
  175.   │                        ├─────────────────────────┤
  176.   │                        │ Get/Set logical device  │
  177.   │                        └─────────────────────────┤
  178.   ├──────────────────────────────────────────────────┤
  179.   │                 Strategy routine                 │
  180.   ├──────────────────────────────────────────────────┤
  181.   │               Device-driver header               │
  182.   └──────────────────────────────────────────────────┘
  183.  
  184.   Figure 14-1.  General structure of an MS-DOS installable device driver.
  185.  
  186. The Device Header
  187.  
  188.   The device header (Figure 14-2) lies at the beginning of the driver. It
  189.   contains a link to the next driver in the chain, a set of attribute flags
  190.   for the device (Figure 14-3), offsets to the executable strategy and
  191.   interrupt routines for the device, and the logical-device name (if it is a
  192.   character device such as PRN or COM1) or the number of logical units (if
  193.   it is a block device).
  194.  
  195.   Byte offset
  196.  
  197.   00H ┌──────────────────────────────────────────────┐
  198.       │         Link to next driver, offset          │
  199.   02H ├──────────────────────────────────────────────┤
  200.       │         Link to next driver, segment         │
  201.   04H ├──────────────────────────────────────────────┤
  202.       │            Device attribute word             │
  203.   06H ├──────────────────────────────────────────────┤
  204.       │         Strategy entry point, offset         │
  205.   08H ├──────────────────────────────────────────────┤
  206.       │        Interrupt entry point, offset         │
  207.   0AH ├──────────────────────────────────────────────┤
  208.       │  Logical name (8 bytes) if character device  │
  209.       │  Number of units (1 byte) if block device,   │
  210.       │    followed by 7 bytes of reserved space     │
  211.       └──────────────────────────────────────────────┘
  212.  
  213.   Figure 14-2.  Device-driver header. The offsets to the strat and intr
  214.   routines are offsets from the same segment used to point to the device
  215.   header.
  216.  
  217.  
  218.   Bit            Significance
  219.   ──────────────────────────────────────────────────────────────────────────
  220.   15             1 if character device, 0 if block device
  221.   14             1 if IOCTL read and write supported
  222.   13             for block devices:
  223.                  1 if BIOS parameter block in boot sector should be used to
  224.                  determine media characteristics, 0 if media ID byte should
  225.                  be used
  226.                  for character devices:
  227.                  1 if output until busy supported
  228.   12             Reserved (should be 0)
  229.   11             1 if open/close/removable media supported (MS-DOS 3.0 and
  230.                  later)
  231.   7─10           Reserved (should be 0)
  232.   6              1 if generic IOCTL and get/set logical drive supported
  233.                  (MS-DOS 3.2 and later)
  234.   5              Reserved (should be 0)
  235.   4              1 if CON driver and Int 29H fast-output function supported
  236.   3              1 if current CLOCK$ device
  237.   2              1 if current NUL device
  238.   1              for block devices:
  239.                  1 if driver supports 32-bit sector addressing (MS-DOS 4.0)
  240.                  for character devices:
  241.                  1 if standard output device (stdout)
  242.   0              1 if current standard input device (stdin)
  243.   ──────────────────────────────────────────────────────────────────────────
  244.  
  245.  
  246.   Figure 14-3.  Device attribute word in device header. In block-device
  247.   drivers, only bits 6, 11, and 13─15 (and bit 1 in MS-DOS version 4.0) have
  248.   significance; the remainder should always be zero.
  249.  
  250. The Strategy Routine
  251.  
  252.   MS-DOS calls the strategy routine (strat) for the device when the driver
  253.   is first loaded and installed, and again whenever an application program
  254.   issues an I/O request for the device. MS-DOS passes the strategy routine a
  255.   double-word pointer to a data structure called a request header. This
  256.   structure contains information about the type of operation to be
  257.   performed. In current versions of MS-DOS, the strategy routine never
  258.   actually performs any I/O operation but simply saves the pointer to the
  259.   request header. The strat routine must not make any Int 21H function
  260.   calls.
  261.  
  262.   The first 13 bytes of the request header are the same for all
  263.   device-driver functions and are therefore referred to as the static
  264.   portion of the header. The number and contents of the subsequent bytes
  265.   vary according to the type of function being requested (Figure 14-4).
  266.   Both MS-DOS and the driver read and write information in the request
  267.   header.
  268.  
  269.   The request header's most important component is a command code, or
  270.   function number, passed in its third byte to select a driver subfunction
  271.   such as read, write, or status. Other information passed to the driver in
  272.   the header includes unit numbers, transfer addresses, and sector or byte
  273.   counts.
  274.  
  275.   ──────────────────────────────────────────────────────────────────────────
  276.   ;
  277.   ; MS-DOS request header structure definition
  278.   ;
  279.   Request         struc                  ; request header template structure
  280.  
  281.   Rlength         db    ?                ; 0  length of request header
  282.   Unit            db    ?                ; 1  unit number for this request
  283.   Command         db    ?                ; 2  request header's command code
  284.   Status          dw    ?                ; 3  driver's return status word
  285.   Reserve         db    8 dup (?)        ; 5  reserved area
  286.   Media           db    ?                ; 13 media descriptor byte
  287.   Address         dd    ?                ; 14 memory address for transfer
  288.   Count           dw    ?                ; 18 byte/sector count value
  289.   Sector          dw    ?                ; 20 starting sector value
  290.  
  291.   Request         ends                   ; end of request header template
  292.   ──────────────────────────────────────────────────────────────────────────
  293.  
  294.   Figure 14-4.  Format of request header. Only the first 13 bytes are common
  295.   to all driver functions; the number and definition of the subsequent bytes
  296.   vary, depending upon the function type. The structure shown here is the
  297.   one used by the read and write subfunctions of the driver.
  298.  
  299. The Interrupt Routine
  300.  
  301.   The last and most complex part of a device driver is the interrupt routine
  302.   (intr), which MS-DOS calls immediately after it calls the strategy
  303.   routine. The interrupt routine implements the device driver proper; it
  304.   performs (or calls other resident routines to perform) the actual input or
  305.   output operations, based on the information passed in the request header.
  306.   The strat routine may not make any Int 21H function calls, except for a
  307.   restricted set during driver initialization.
  308.  
  309.   When an I/O function is completed, the interrupt routine uses the status
  310.   field in the request header to inform the DOS kernel about the outcome of
  311.   the requested I/O operation. It can use other fields in the request header
  312.   to pass back such useful information as counts of the actual sectors or
  313.   bytes transferred.
  314.  
  315.   The interrupt routine usually consists of the following elements:
  316.  
  317.   ■  A collection of subroutines to implement the various function types
  318.      that may be requested by MS-DOS (sometimes called the command-code
  319.      routines)
  320.  
  321.   ■  A centralized entry point that saves all affected registers, extracts
  322.      the desired function code from the request header, and branches to the
  323.      appropriate command-code routine (typically accomplished with a jump
  324.      table)
  325.  
  326.   ■  A centralized exit point that stores status and error codes into the
  327.      request header (Figures 14-5 and 14-6) and restores the previous
  328.      contents of the affected registers
  329.  
  330.   The command-code routines that implement the various functions supported
  331.   by an installable device driver are discussed in detail in the following
  332.   pages.
  333.  
  334.   Bit(s)         Significance
  335.   ──────────────────────────────────────────────────────────────────────────
  336.   15             Error
  337.   12─14          Reserved
  338.   9              Busy
  339.   8              Done
  340.   0─7            Error code if bit 15 = 1
  341.   ──────────────────────────────────────────────────────────────────────────
  342.  
  343.   Figure 14-5.  Values for the return status word of the request header.
  344.  
  345.   Code           Meaning
  346.   ──────────────────────────────────────────────────────────────────────────
  347.   0              Write-protect violation
  348.   1              Unknown unit
  349.   2              Drive not ready
  350.   3              Unknown command
  351.   4              Data error (CRC)
  352.   5              Bad request-structure length
  353.   6              Seek error
  354.   7              Unknown medium
  355.   8              Sector not found
  356.   9              Printer out of paper
  357.   0AH            Write fault
  358.   0BH            Read fault
  359.   0CH            General failure
  360.   0D─0EH         Reserved
  361.   0FH            Invalid disk change (MS-DOS versions 3.0 and later)
  362.   ──────────────────────────────────────────────────────────────────────────
  363.  
  364.   Figure 14-6.  Driver error codes returned in bits 0 through 7 of the
  365.   return status word of the request header.
  366.  
  367.   Although its name suggests otherwise, the interrupt routine is never
  368.   entered asynchronously (on an I/O completion interrupt, for example).
  369.   Thus, the division of function between strategy and interrupt routines is
  370.   completely artificial in the current versions of MS-DOS.
  371.  
  372.  
  373. The Command-Code Routines
  374.  
  375.   A total of 20 command codes are defined for MS-DOS device drivers. The
  376.   command codes (which are not consecutive), the names of the associated
  377.   driver-interrupt routines, and the MS-DOS versions in which they are first
  378.   supported are as follows:
  379.  
  380.  
  381.   Command     Function                 Character   Block        MS-DOS
  382.   code                                 driver      driver       version
  383.   ──────────────────────────────────────────────────────────────────────────
  384.   0           Init (Initialization)    X           X            2.0
  385.   1           Media Check                          X            2.0
  386.   2           Build BPB                            X            2.0
  387.   3           IOCTL Read               X           X            2.0
  388.   4           Read                     X           X            2.0
  389.   5           Nondestructive Read      X                        2.0
  390.   6           Input Status             X                        2.0
  391.   7           Flush Input Buffers      X                        2.0
  392.   8           Write                    X           X            2.0
  393.   9           Write with Verify                    X            2.0
  394.   10          Output Status            X                        2.0
  395.   11          Flush Output Buffers     X                        2.0
  396.   12          IOCTL Write              X           X            2.0
  397.   13          Device Open              X           X            3.0
  398.   14          Device Close             X           X            3.0
  399.   15          Removable Media                      X            3.0
  400.   16          Output Until Busy        X                        3.0
  401.   19          Generic IOCTL            X           X            3.2
  402.   23          Get Logical Device                   X            3.2
  403.   24          Set Logical Device                   X            3.2
  404.   ──────────────────────────────────────────────────────────────────────────
  405.  
  406.  
  407.   As you can see from the preceding table, a driver's interrupt section must
  408.   support functions 0 through 12 under all versions of MS-DOS. Drivers
  409.   tailored for MS-DOS 3.0 and 3.1 can optionally support an additional four
  410.   functions, and MS-DOS drivers for versions 3.2 and later can support three
  411.   more (for a total of 20). MS-DOS inspects the bits in the attribute word
  412.   of the device-driver header to determine which of the optional functions a
  413.   driver supports, if any.
  414.  
  415.   Some of the functions are relevant only for character-device drivers and
  416.   some only for block-device drivers; a few have meaning to both types. In
  417.   any case, both driver types should have an executable routine present for
  418.   each function, even if it does nothing except set the done flag in the
  419.   status word of the request header.
  420.  
  421.   In the command-code descriptions that follow, RH refers to the request
  422.   header whose address was passed to the strategy routine in ES:BX, BYTE is
  423.   an 8-bit parameter, WORD is a 16-bit parameter, and DWORD is a far pointer
  424.   (a 16-bit offset followed by a 16-bit segment).
  425.  
  426. Function 00H (0): Driver Initialization
  427.  
  428.   MS-DOS requests the driver's initialization function (init) only once,
  429.   when the driver is first loaded. This function performs any necessary
  430.   device hardware initialization, setup of interrupt vectors, and so forth.
  431.   The initialization routine must return the address of the position where
  432.   free memory begins after the driver code (the break address), so that
  433.   MS-DOS knows where it can build certain control structures and then load
  434.   the next installable driver. If this is a block-device driver, init must
  435.   also return the number of units and the address of a BPB pointer array.
  436.  
  437.   MS-DOS uses the number of units returned by a block driver in the request
  438.   header to assign drive identifiers. For example, if the current maximum
  439.   drive is D and the driver being initialized supports four units, MS-DOS
  440.   will assign it the drive letters E, F, G, and H. Although the
  441.   device-driver header also has a field for number of units, MS-DOS does not
  442.   inspect it.
  443.  
  444.   The BPB pointer array is an array of word offsets to BIOS parameter blocks
  445.   (Figure 14-7). Each unit defined by the driver must have one entry in the
  446.   array, although the entries can all point to the same BPB to conserve
  447.   memory. During the operating-system boot sequence, MS-DOS scans all the
  448.   BPBs defined by all the units in all the block-device drivers to determine
  449.   the largest sector size that exists on any device in the system and uses
  450.   this information to set its cache buffer size.
  451.  
  452.   The operating-system services that the initialization code can invoke at
  453.   load time are very limited only Int 21H Functions 01H through 0CH and
  454.   30H. These are just adequate to check the MS-DOS version number and
  455.   display a driver-identification or error message.
  456.  
  457.   Many programmers position the initialization code at the end of the driver
  458.   and return that address as the location of the first free memory, so that
  459.   MS-DOS will reclaim the memory occupied by the initialization routine
  460.   after the routine is finished with its work. If the initialization routine
  461.   finds that the device is missing or defective and wants to abort the
  462.   installation of the driver completely so that it does not occupy any
  463.   memory, it should return number of units as zero and set the free memory
  464.   address to CS:0000H. (A character-device driver that wants to abort its
  465.   installation should clear bit 15 of the attribute word in the driver
  466.   header and then set the units field and free memory address as though it
  467.   were a block-device driver.)
  468.  
  469.   Byte(s)                  Contents
  470.   ──────────────────────────────────────────────────────────────────────────
  471.   00─01H                   Bytes per sector
  472.   02H                      Sectors per allocation unit (power of 2)
  473.   03H─04H                  Number of reserved sectors (starting at sector 0)
  474.   05H                      Number of file allocation tables
  475.   06H─07H                  Maximum number of root-directory entries
  476.   08H─09H                  Total number of sectors in medium
  477.   0AH                      Media descriptor byte
  478.   0BH─0CH                  Number of sectors occupied by a single FAT
  479.   0DH─0EH                  Sectors per track (versions 3.0 and later)
  480.   0FH─10H                  Number of heads (versions 3.0 and later)
  481.   11H─12H                  Number of hidden sectors (versions 3.0 and later)
  482.   13H─14H                  High-order word of number of hidden sectors
  483.                            (version 4.0)
  484.   15H─18H                  If bytes 8─9 are zero, total number of sectors in
  485.                            medium (version 4.0)
  486.   19H─1EH                  Reserved, should be zero (version 4.0)
  487.   ──────────────────────────────────────────────────────────────────────────
  488.  
  489.   Figure 14-7.  Structure of a BIOS parameter block (BPB). Every formatted
  490.   disk contains a copy of its BPB in the boot sector. (See Chapter 10.)
  491.  
  492.   The initialization function is called with
  493.  
  494.   ──────────────────────────────────────────────────────────────────────────
  495.   RH + 2             BYTE              Command code = 0
  496.  
  497.   RH + 18            DWORD             Pointer to character after equal sign
  498.                                        on CONFIG.SYS line that loaded driver
  499.                                        (this information is read-only)
  500.  
  501.   RH + 22            BYTE              Drive number for first unit of this
  502.                                        block driver (0 = A, 1 = B, and so
  503.                                        forth) (MS-DOS version 3 only)
  504.   ──────────────────────────────────────────────────────────────────────────
  505.  
  506.   It returns:
  507.  
  508.   ──────────────────────────────────────────────────────────────────────────
  509.   RH + 3             WORD              Status
  510.  
  511.   RH + 13            BYTE              Number of units (block devices only)
  512.  
  513.   RH + 14            DWORD             Address of first free memory above
  514.                                        driver (break address)
  515.  
  516.   RH + 18            DWORD             BPB pointer array (block devices
  517.                                        only)
  518.   ──────────────────────────────────────────────────────────────────────────
  519.  
  520. Function 01H (1): Media Check
  521.  
  522.   The media-check function applies only to block devices, and in
  523.   character-device drivers it should do nothing except set the done flag.
  524.   This function is called when a drive-access call other than a simple file
  525.   read or write is pending. MS-DOS passes to the function the media
  526.   descriptor byte for the disk that it assumes is in the drive (Figure
  527.   14-8). If feasible, the media-check routine returns a code indicating
  528.   whether the disk has been changed since the last transfer. If the
  529.   media-check routine can assert that the disk has not been changed, MS-DOS
  530.   can bypass rereading the FAT before a directory access, which improves
  531.   overall performance.
  532.  
  533.   Code                     Meaning
  534.   ──────────────────────────────────────────────────────────────────────────
  535.   0F0H                     3.5", 2-sided, 18-sector
  536.   0F8H                     fixed disk
  537.   0F9H                     3.5", 2-sided, 9-sector
  538.   0F9H                     5.25", 2-sided, 15-sector
  539.   0FCH                     5.25", 1-sided, 9-sector
  540.   0FDH                     5.25", 2-sided, 9-sector
  541.   0FEH                     5.25", 1-sided, 8-sector
  542.   0FFH                     5.25", 2-sided, 8-sector
  543.   ──────────────────────────────────────────────────────────────────────────
  544.  
  545.   Figure 14-8.  Current valid MS-DOS codes for the media descriptor byte of
  546.   the request header, assuming bit 13 in the attribute word of the driver
  547.   header is zero.
  548.  
  549.   MS-DOS responds to the results of the media-check function in the
  550.   following ways:
  551.  
  552.   ■  If the disk has not been changed, MS-DOS proceeds with the disk access.
  553.  
  554.   ■  If the disk has been changed, MS-DOS invalidates all buffers associated
  555.      with this unit, including buffers containing data waiting to be written
  556.      (this data is simply lost), performs a BUILD BPB call, and then reads
  557.      the disk's FAT and directory.
  558.  
  559.   ■  If the disk-change status is unknown, the action taken by MS-DOS
  560.      depends upon the state of its internal buffers. If data that needs to
  561.      be written out is present in the buffers, MS-DOS assumes no disk change
  562.      has occurred and writes the data (taking the risk that, if the disk
  563.      really was changed, the file structure on the new disk may be damaged).
  564.      If the buffers are empty or have all been previously flushed to the
  565.      disk, MS-DOS assumes that the disk was changed, and then proceeds as
  566.      described above for the disk-changed return code.
  567.  
  568.   If bit 11 of the device-header attribute word is set (that is, the driver
  569.   supports the optional open/close/removable-media functions), the host
  570.   system is MS-DOS version 3.0 or later, and the function returns the
  571.   disk-changed code (-1), the function must also return the segment and
  572.   offset of the ASCIIZ volume label for the previous disk in the drive. (If
  573.   the driver does not have the volume label, it can return a pointer to the
  574.   ASCIIZ string NO NAME.) If MS-DOS determines that the disk was changed
  575.   with unwritten data still present in its buffers, it issues a
  576.   critical-error 0FH (invalid disk change). Application programs can trap
  577.   this critical error and prompt the user to replace the original disk.
  578.  
  579.   The media-check function is called with
  580.  
  581.   ──────────────────────────────────────────────────────────────────────────
  582.   RH + 1             BYTE              Unit code
  583.  
  584.   RH + 2             BYTE              Command code = 1
  585.  
  586.   RH + 13            BYTE              Media descriptor byte
  587.   ──────────────────────────────────────────────────────────────────────────
  588.  
  589.   It returns
  590.  
  591.   ──────────────────────────────────────────────────────────────────────────
  592.   RH + 3             WORD              Status
  593.  
  594.   RH + 14            BYTE              Media-change code:
  595.  
  596.                                        -1 if disk changed
  597.  
  598.                                        0 if don't know whether disk changed
  599.  
  600.                                        1 if disk not changed
  601.  
  602.   RH + 15            DWORD             Pointer to previous volume label, if
  603.                                        device attribute bit 11 = 1 and disk
  604.                                        has been changed (MS-DOS versions 3.0
  605.                                        and later)
  606.   ──────────────────────────────────────────────────────────────────────────
  607.  
  608. Function 02H (2): Build BIOS Parameter Block (BPB)
  609.  
  610.   The build BPB function applies only to block devices, and in
  611.   character-device drivers should do nothing except set the done flag. The
  612.   kernel uses this function to get a pointer to the valid BPB (see Figure
  613.   14-7) for the current disk and calls it when the disk-changed code is
  614.   returned by the media-check routine or the don't-know code is returned and
  615.   there are no dirty buffers (buffers with changed data that have not yet
  616.   been written to disk). Thus, a call to this function indicates that the
  617.   disk has been legally changed.
  618.  
  619.   The build BPB function receives a pointer to a one-sector buffer in the
  620.   request header. If bit 13 in the driver header's attribute word is zero,
  621.   the buffer contains the first sector of the FAT (which includes the media
  622.   identification byte) and should not be altered by the driver. If bit 13 is
  623.   set, the driver can use the buffer as scratch space.
  624.  
  625.   The build BPB function is called with
  626.  
  627.   ──────────────────────────────────────────────────────────────────────────
  628.   RH + 1             BYTE              Unit code
  629.  
  630.   RH + 2             BYTE              Command code = 2
  631.  
  632.   RH + 13            BYTE              Media descriptor byte
  633.  
  634.   RH + 14            DWORD             Buffer address
  635.   ──────────────────────────────────────────────────────────────────────────
  636.  
  637.   It returns
  638.  
  639.   ──────────────────────────────────────────────────────────────────────────
  640.   RH + 3             WORD              Status
  641.  
  642.   RH + 18            DWORD             Pointer to new BPB
  643.   ──────────────────────────────────────────────────────────────────────────
  644.  
  645.   Under MS-DOS versions 3.0 and later, if bit 11 of the header's device
  646.   attribute word is set, this routine should also read the volume label off
  647.   the disk and save it.
  648.  
  649. Function 03H (3): I/O-Control Read
  650.  
  651.   The IOCTL read function allows the device driver to pass information
  652.   directly to the application program. This function is called only if bit
  653.   14 is set in the device attribute word. MS-DOS performs no error check on
  654.   IOCTL I/O calls.
  655.  
  656.   The IOCTL read function is called with
  657.  
  658.   ──────────────────────────────────────────────────────────────────────────
  659.   RH + 1             BYTE              Unit code (block devices)
  660.  
  661.   RH + 2             BYTE              Command code = 3
  662.  
  663.   RH + 13            BYTE              Media descriptor byte
  664.  
  665.   RH + 14            DWORD             Transfer address
  666.  
  667.   RH + 18            WORD              Byte/sector count
  668.  
  669.   RH + 20            WORD              Starting sector number (block
  670.                                        devices)
  671.   ──────────────────────────────────────────────────────────────────────────
  672.  
  673.   It returns
  674.  
  675.   ──────────────────────────────────────────────────────────────────────────
  676.   RH + 3             WORD              Status
  677.  
  678.   RH + 18            WORD              Actual bytes or sectors transferred
  679.   ──────────────────────────────────────────────────────────────────────────
  680.  
  681. Function 04H (4): Read
  682.  
  683.   The read function transfers data from the device into the specified memory
  684.   buffer. If an error is encountered during the read, the function must set
  685.   the error status and, in addition, report the number of bytes or sectors
  686.   successfully transferred; it is not sufficient to simply report an error.
  687.  
  688.   The read function is called with
  689.  
  690.   ──────────────────────────────────────────────────────────────────────────
  691.   RH + 1             BYTE              Unit code (block devices)
  692.  
  693.   RH + 2             BYTE              Command code = 4
  694.  
  695.   RH + 13            BYTE              Media descriptor byte
  696.  
  697.   RH + 14            DWORD             Transfer address
  698.  
  699.   RH + 18            WORD              Byte/sector count
  700.  
  701.   RH + 20            WORD              Starting sector number (block
  702.                                        devices)
  703.   ──────────────────────────────────────────────────────────────────────────
  704.  
  705.   For block-device read operations in MS-DOS version 4, if the logical unit
  706.   is larger than 32 MB and bit 1 of the driver's attribute word is set, the
  707.   following request structure is used instead:
  708.  
  709.   ──────────────────────────────────────────────────────────────────────────
  710.   RH + 1             BYTE              Unit code
  711.  
  712.   RH + 2             BYTE              Command code = 4
  713.  
  714.   RH + 13            BYTE              Media descriptor byte
  715.  
  716.   RH + 14            DWORD             Transfer address
  717.  
  718.   RH + 18            WORD              Sector count
  719.  
  720.   RH + 20            WORD              Contains -1 to signal use of 32-bit
  721.                                        sector number
  722.  
  723.   RH + 26            DWORD             32-bit starting sector number
  724.   ──────────────────────────────────────────────────────────────────────────
  725.  
  726.   The read function returns
  727.  
  728.   ──────────────────────────────────────────────────────────────────────────
  729.   RH + 3             WORD              Status
  730.  
  731.   RH + 18            WORD              Actual bytes or sectors transferred
  732.  
  733.   RH + 22            DWORD             Pointer to volume label if error 0FH
  734.                                        is returned (MS-DOS versions 3.0 and
  735.                                        later)
  736.   ──────────────────────────────────────────────────────────────────────────
  737.  
  738.   Under MS-DOS versions 3.0 and later, this routine can use the count of
  739.   open files maintained by the open and close functions (0DH and 0EH) and
  740.   the media descriptor byte to determine whether the disk has been illegally
  741.   changed.
  742.  
  743. Function 05H (5): Nondestructive Read
  744.  
  745.   The nondestructive read function applies only to character devices, and in
  746.   block devices it should do nothing except set the done flag. It returns
  747.   the next character that would be obtained with a read function (command
  748.   code 4), without removing that character from the driver's internal
  749.   buffer. MS-DOS uses this function to check the console driver for pending
  750.   Control-C characters during other operations.
  751.  
  752.   The nondestructive read function is called with
  753.  
  754.   ──────────────────────────────────────────────────────────────────────────
  755.   RH + 2             BYTE              Command code = 5
  756.   ──────────────────────────────────────────────────────────────────────────
  757.  
  758.   It returns
  759.  
  760.   ──────────────────────────────────────────────────────────────────────────
  761.   RH + 3             WORD              Status
  762.  
  763.                                        If busy bit = 0, at least one
  764.                                        character is waiting
  765.  
  766.                                        If busy bit = 1, no characters are
  767.                                        waiting
  768.  
  769.   RH + 13            BYTE              Character (if busy bit = 0)
  770.   ──────────────────────────────────────────────────────────────────────────
  771.  
  772. Function 06H (6): Input Status
  773.  
  774.   The input-status function applies only to character devices, and in
  775.   block-device drivers it should do nothing except set the done flag. This
  776.   function returns the current input status for the device, allowing MS-DOS
  777.   to test whether characters are waiting in a type-ahead buffer. If the
  778.   character device does not have a type-ahead buffer, the input-status
  779.   routine should always return the busy bit equal to zero, so that MS-DOS
  780.   will not wait forever to call the read (04H) or nondestructive read (05H)
  781.   function.
  782.  
  783.   The input-status function is called with
  784.  
  785.   ──────────────────────────────────────────────────────────────────────────
  786.   RH + 2             BYTE              Command code = 6
  787.   ──────────────────────────────────────────────────────────────────────────
  788.  
  789.   It returns
  790.  
  791.   ──────────────────────────────────────────────────────────────────────────
  792.   RH + 3             WORD              Status:
  793.  
  794.                                        If busy bit = 1, read request goes to
  795.                                        physical device.
  796.  
  797.                                        If busy bit = 0, characters already
  798.                                        in device buffer and read request
  799.                                        returns quickly.
  800.   ──────────────────────────────────────────────────────────────────────────
  801.  
  802. Function 07H (7): Flush Input Buffers
  803.  
  804.   The flush-input-buffers function applies only to character devices, and in
  805.   block-device drivers it should do nothing except set the done flag. This
  806.   function causes any data waiting in the input buffer to be discarded.
  807.  
  808.   The flush-input-buffers function is called with
  809.  
  810.   ──────────────────────────────────────────────────────────────────────────
  811.   RH + 2             BYTE              Command code = 7
  812.   ──────────────────────────────────────────────────────────────────────────
  813.  
  814.   It returns
  815.  
  816.   ──────────────────────────────────────────────────────────────────────────
  817.   RH + 3             WORD              Status
  818.  
  819.   ──────────────────────────────────────────────────────────────────────────
  820.  
  821. Function 08H (8): Write
  822.  
  823.   The write function transfers data from the specified memory buffer to the
  824.   device. If an error is encountered during the write, the write function
  825.   must set the error status and, in addition, report the number of bytes or
  826.   sectors successfully transferred; it is not sufficient to simply report an
  827.   error.
  828.  
  829.   The write function is called with
  830.  
  831.   ──────────────────────────────────────────────────────────────────────────
  832.   RH + 1             BYTE              Unit code (block devices)
  833.  
  834.   RH + 2             BYTE              Command code = 8
  835.  
  836.   RH + 13            BYTE              Media descriptor byte
  837.  
  838.   RH + 14            DWORD             Transfer address
  839.  
  840.   RH + 18            WORD              Byte/sector count
  841.  
  842.   RH + 20            WORD              Starting sector number (block
  843.                                        devices)
  844.   ──────────────────────────────────────────────────────────────────────────
  845.  
  846.   For block-device write operations in MS-DOS version 4, if the logical unit
  847.   is larger than 32 MB and bit 1 of the driver's attribute word is set, the
  848.   following request structure is used instead:
  849.  
  850.   ──────────────────────────────────────────────────────────────────────────
  851.   RH + 1             BYTE              Unit code
  852.  
  853.   RH + 2             BYTE              Command code = 8
  854.  
  855.   RH + 13            BYTE              Media descriptor byte
  856.  
  857.   RH + 14            DWORD             Transfer address
  858.  
  859.   RH + 18            WORD              Sector count
  860.  
  861.   RH + 20            WORD              Contains -1 to signal use of 32-bit
  862.                                        sector number
  863.  
  864.   RH + 26            DWORD             32-bit starting sector number
  865.   ──────────────────────────────────────────────────────────────────────────
  866.  
  867.   The write function returns
  868.  
  869.   ──────────────────────────────────────────────────────────────────────────
  870.   RH + 3             WORD              Status
  871.  
  872.   RH + 18            WORD              Actual bytes or sectors transferred
  873.  
  874.   RH + 22            DWORD             Pointer to volume label if error 0FH
  875.                                        returned (MS-DOS versions 3.0 and
  876.                                        later)
  877.   ──────────────────────────────────────────────────────────────────────────
  878.  
  879.   Under MS-DOS versions 3.0 and later, this routine can use the reference
  880.   count of open files maintained by the open and close functions (0DH and
  881.   0EH) and the media descriptor byte to determine whether the disk has been
  882.   illegally changed.
  883.  
  884. Function 09H (9): Write with Verify
  885.  
  886.   The write-with-verify function transfers data from the specified memory
  887.   buffer to the device. If feasible, it should perform a read-after-write
  888.   verification of the data to confirm that the data was written correctly.
  889.   Otherwise, Function 09H is exactly like Function 08H.
  890.  
  891. Function 0AH (10): Output Status
  892.  
  893.   The output-status function applies only to character devices, and in
  894.   block-device drivers it should do nothing except set the done flag. This
  895.   function returns the current output status for the device.
  896.  
  897.   The output-status function is called with
  898.  
  899.   ──────────────────────────────────────────────────────────────────────────
  900.   RH + 2             BYTE              Command code = 10 (0AH)
  901.   ──────────────────────────────────────────────────────────────────────────
  902.  
  903.   It returns
  904.  
  905.   ──────────────────────────────────────────────────────────────────────────
  906.   RH + 3             WORD              Status:
  907.  
  908.                                        If busy bit = 1, write request waits
  909.                                        for completion of current request.
  910.  
  911.                                        If busy bit = 0, device idle and
  912.                                        write request starts immediately.
  913.   ──────────────────────────────────────────────────────────────────────────
  914.  
  915. Function 0BH (11): Flush Output Buffers
  916.  
  917.   The flush-output-buffers function applies only to character devices, and
  918.   in block-device drivers it should do nothing except set the done flag.
  919.   This function empties the output buffer, if any, and discards any pending
  920.   output requests.
  921.  
  922.   The flush-output-buffers function is called with
  923.  
  924.   ──────────────────────────────────────────────────────────────────────────
  925.   RH + 2             BYTE              Command code = 11 (0BH)
  926.   ──────────────────────────────────────────────────────────────────────────
  927.  
  928.   It returns
  929.  
  930.   ──────────────────────────────────────────────────────────────────────────
  931.   RH + 3             WORD              Status
  932.  
  933.   ──────────────────────────────────────────────────────────────────────────
  934.  
  935. Function 0CH (12): I/O-Control Write
  936.  
  937.   The IOCTL write function allows an application program to pass control
  938.   information directly to the driver. This function is called only if bit 14
  939.   is set in the device attribute word. MS-DOS performs no error check on
  940.   IOCTL I/O calls.
  941.  
  942.   The IOCTL write function is called with
  943.  
  944.   ──────────────────────────────────────────────────────────────────────────
  945.   RH + 1             BYTE              Unit code (block devices)
  946.  
  947.   RH + 2             BYTE              Command code = 12 (0CH)
  948.  
  949.   RH + 13            BYTE              Media descriptor byte
  950.  
  951.   RH + 14            DWORD             Transfer address
  952.  
  953.   RH + 18            WORD              Byte/sector count
  954.  
  955.   RH + 20            WORD              Starting sector number (block
  956.                                        devices)
  957.   ──────────────────────────────────────────────────────────────────────────
  958.  
  959.   It returns
  960.  
  961.   ──────────────────────────────────────────────────────────────────────────
  962.   RH + 3             WORD              Status
  963.  
  964.   RH + 18            WORD              Actual bytes or sectors transferred
  965.   ──────────────────────────────────────────────────────────────────────────
  966.  
  967. Function 0DH (13): Device Open
  968.  
  969.   The device-open function is supported only under MS-DOS versions 3.0 and
  970.   later and is called only if bit 11 is set in the device attribute word of
  971.   the device header.
  972.  
  973.   On block devices, the device-open function can be used to manage local
  974.   buffering and to increment a reference count of the number of open files
  975.   on the device. This capability must be used with care, however, because
  976.   programs that access files through FCBs frequently fail to close them,
  977.   thus invalidating the open-files count. One way to protect against this
  978.   possibility is to reset the open-files count to zero, without flushing the
  979.   buffers, whenever the answer to a media-change call is yes and a
  980.   subsequent build BPB call is made to the driver.
  981.  
  982.   On character devices, the device-open function can be used to send a
  983.   device-initialization string (which can be set into the driver by an
  984.   application program by means of an IOCTL write function) or to deny
  985.   simultaneous access to a character device by more than one process. Note
  986.   that the predefined handles for the CON, AUX, and PRN devices are always
  987.   open.
  988.  
  989.   The device-open function is called with
  990.  
  991.   ──────────────────────────────────────────────────────────────────────────
  992.   RH + 1             BYTE              Unit code (block devices)
  993.  
  994.   RH + 2             BYTE              Command code = 13 (0DH)
  995.   ──────────────────────────────────────────────────────────────────────────
  996.  
  997.   It returns
  998.  
  999.   ──────────────────────────────────────────────────────────────────────────
  1000.   RH + 3             WORD              Status
  1001.   ──────────────────────────────────────────────────────────────────────────
  1002.  
  1003. Function 0EH (14): Device Close
  1004.  
  1005.   The device-close function is supported only under MS-DOS versions 3.0 and
  1006.   later and is called only if bit 11 is set in the device attribute word of
  1007.   the device header.
  1008.  
  1009.   On block devices, this function can be used to manage local buffering and
  1010.   to decrement a reference count of the number of open files on the device;
  1011.   when the count reaches zero, all files have been closed and the driver
  1012.   should flush buffers because the user may change disks.
  1013.  
  1014.   On character devices, the device-close function can be used to send a
  1015.   device-dependent post-I/O string such as a formfeed. (This string can be
  1016.   set into the driver by an application program by means of an IOCTL write
  1017.   function.) Note that the predefined handles for the CON, PRN, and AUX
  1018.   devices are never closed.
  1019.  
  1020.   The device-close function is called with
  1021.  
  1022.   ──────────────────────────────────────────────────────────────────────────
  1023.   RH + 1             BYTE              Unit code (block devices)
  1024.  
  1025.   RH + 2             BYTE              Command code = 14 (0EH)
  1026.   ──────────────────────────────────────────────────────────────────────────
  1027.  
  1028.   It returns
  1029.  
  1030.   ──────────────────────────────────────────────────────────────────────────
  1031.   RH + 3             WORD              Status
  1032.   ──────────────────────────────────────────────────────────────────────────
  1033.  
  1034. Function 0FH (15): Removable Media
  1035.  
  1036.   The removable-media function is supported only under MS-DOS versions 3.0
  1037.   and later and only on block devices; in character-device drivers it should
  1038.   do nothing except set the done flag. This function is called only if bit
  1039.   11 is set in the device attribute word in the device header.
  1040.  
  1041.   The removable-media function is called with
  1042.  
  1043.   ──────────────────────────────────────────────────────────────────────────
  1044.   RH + 1             BYTE              Unit code
  1045.  
  1046.   RH + 2             BYTE              Command code = 15 (0FH)
  1047.   ──────────────────────────────────────────────────────────────────────────
  1048.  
  1049.   It returns
  1050.  
  1051.   ──────────────────────────────────────────────────────────────────────────
  1052.   RH + 3             WORD              Status:
  1053.  
  1054.                                        If busy bit = 1, medium nonremovable
  1055.  
  1056.                                        If busy bit = 0, medium removable
  1057.   ──────────────────────────────────────────────────────────────────────────
  1058.  
  1059. Function 10H (16): Output Until Busy
  1060.  
  1061.   The output-until-busy function is supported only under MS-DOS versions 3.0
  1062.   and later, and only on character devices; in block-device drivers it
  1063.   should do nothing except set the done flag. This function transfers data
  1064.   from the specifi
  1065. d memory buffer to a device, continuing to transfer bytes
  1066.   until the device is busy. It is called only if bit 13 of the device
  1067.   attribute word is set in the device header.
  1068.  
  1069.   This function is an optimization included specifically for the use of
  1070.   print spoolers. It is not an error for this function to return a number of
  1071.   bytes transferred that is less than the number of bytes requested.
  1072.  
  1073.   The output-until-busy function is called with
  1074.  
  1075.   ──────────────────────────────────────────────────────────────────────────
  1076.   RH + 2             BYTE              Command code = 16 (10H)
  1077.  
  1078.   RH + 14            DWORD             Transfer address
  1079.  
  1080.   RH + 18            WORD              Byte count
  1081.   ──────────────────────────────────────────────────────────────────────────
  1082.  
  1083.   It returns
  1084.  
  1085.   ──────────────────────────────────────────────────────────────────────────
  1086.   RH + 3             WORD              Status
  1087.  
  1088.   RH + 18            WORD              Actual bytes transferred
  1089.   ──────────────────────────────────────────────────────────────────────────
  1090.  
  1091. Function 13H (19) Generic IOCTL
  1092.  
  1093.   The generic IOCTL function is supported only under MS-DOS versions 3.2 and
  1094.   later and is called only if bit 6 is set in the device attribute word of
  1095.   the device header. This function corresponds to the MS-DOS generic IOCTL
  1096.   service supplied to application programs by Int 21H Function 44H
  1097.   Subfunctions 0CH and 0DH.
  1098.  
  1099.   The generic IOCTL function is passed a category (major) code, a function
  1100.   (minor) code, the contents of the SI and DI registers at the point of the
  1101.   IOCTL call, and the segment and offset of a data buffer. This buffer in
  1102.   turn contains other information whose format depends on the major and
  1103.   minor IOCTL codes passed in the request header. The driver must interpret
  1104.   the major and minor codes in the request header and the contents of the
  1105.   additional buffer to determine which operation it will carry out, then set
  1106.   the done flag in the request-header status word, and return any other
  1107.   applicable information in the request header or the data buffer.
  1108.  
  1109.   Services that the generic IOCTL function may invoke, if the driver
  1110.   supports them, include configuration of the driver for nonstandard disk
  1111.   formats, reading and writing entire disk tracks of data, and formatting
  1112.   and verifying tracks. The generic IOCTL function has been designed to be
  1113.   open-ended, so that it can be used to easily extend the device-driver
  1114.   definition under future versions of MS-DOS.
  1115.  
  1116.   The generic IOCTL function is called with
  1117.  
  1118.   ──────────────────────────────────────────────────────────────────────────
  1119.   RH + 1             BYTE              Unit number (block devices)
  1120.  
  1121.   RH + 2             BYTE              Command code = 19 (13H)
  1122.  
  1123.   RH + 13            BYTE              Category (major) code
  1124.  
  1125.   RH + 14            BYTE              Function (minor) code
  1126.  
  1127.   RH + 15            WORD              SI register contents
  1128.  
  1129.   RH + 17            WORD              DI register contents
  1130.  
  1131.   RH + 19            DWORD             Address of generic IOCTL data packet
  1132.   ──────────────────────────────────────────────────────────────────────────
  1133.  
  1134.   It returns
  1135.  
  1136.   ──────────────────────────────────────────────────────────────────────────
  1137.   RH + 3             WORD              Status
  1138.   ──────────────────────────────────────────────────────────────────────────
  1139.  
  1140. Function 17H (23): Get Logical Device
  1141.  
  1142.   The get-logical-device function is supported only under MS-DOS versions
  1143.   3.2 and later and only on block devices; in character-device drivers it
  1144.   should do nothing except set the done bit in the status word. This
  1145.   function is called only if bit 6 is set in the device attribute word of
  1146.   the device header. It corresponds to the get-logical-device-map service
  1147.   supplied to application programs through Int 21H Function 44H Subfunction
  1148.   0EH.
  1149.  
  1150.   The get-logical-device function returns a code for the last drive letter
  1151.   used to reference the device; if only one drive letter is assigned to the
  1152.   device, the returned unit code should be zero. Thus, this function can be
  1153.   used to determine whether more than one drive letter is assigned to the
  1154.   same physical device.
  1155.  
  1156.   The get-logical-device function is called with
  1157.  
  1158.   ──────────────────────────────────────────────────────────────────────────
  1159.   RH + 1             BYTE              Unit code
  1160.  
  1161.   RH + 2             BYTE              Command code = 23 (17H)
  1162.   ──────────────────────────────────────────────────────────────────────────
  1163.  
  1164.   It returns
  1165.  
  1166.   ──────────────────────────────────────────────────────────────────────────
  1167.   RH + 1             BYTE              Last unit referenced, or zero
  1168.  
  1169.   RH + 3             WORD              Status
  1170.   ──────────────────────────────────────────────────────────────────────────
  1171.  
  1172. Function 18H (24): Set Logical Device
  1173.  
  1174.   The set-logical-device function is supported only under MS-DOS versions
  1175.   3.2 and later and only on block devices; in character-device drivers it
  1176.   should do nothing except set the done bit in the status word. This
  1177.   function is called only if bit 6 is set in the device attribute word of
  1178.   the device header. It corresponds to the set-logical-device-map service
  1179.   supplied to application programs by MS-DOS through Int 21H Function 44H
  1180.   Subfunction 0FH.
  1181.  
  1182.   The set-logical-device function informs the driver of the next
  1183.   logical-drive identifier that will be used to reference the physical
  1184.   device. The unit code passed by the MS-DOS kernel in this case is
  1185.   zero-based relative to the number of logical drives supported by this
  1186.   particular driver. For example, if the driver supports two floppy-disk
  1187.   units (A and B), only one physical floppy-disk drive exists in the system,
  1188.   and the set-logical-device function is called with a unit number of 1, the
  1189.   driver is being informed that the next read or write request from the
  1190.   kernel will be directed to drive B.
  1191.  
  1192.   The set-logical-device function is called with
  1193.  
  1194.   ──────────────────────────────────────────────────────────────────────────
  1195.   RH + 1             BYTE              Unit code
  1196.  
  1197.   RH + 2             BYTE              Command code = 24 (18H)
  1198.   ──────────────────────────────────────────────────────────────────────────
  1199.  
  1200.   It returns
  1201.  
  1202.   ──────────────────────────────────────────────────────────────────────────
  1203.   RH + 3             WORD              Status
  1204.   ──────────────────────────────────────────────────────────────────────────
  1205.  
  1206.  
  1207. The Processing of a Typical I/O Request
  1208.  
  1209.   An application program requests an I/O operation from MS-DOS by loading
  1210.   registers with the appropriate values and executing an Int 21H. This
  1211.   results in the following sequence of actions:
  1212.  
  1213.   1.  MS-DOS inspects its internal tables and determines which device driver
  1214.       should receive the I/O request.
  1215.  
  1216.   2.  MS-DOS creates a request-header data packet in a reserved area of
  1217.       memory. (Disk I/O requests are transformed from file and record
  1218.       information into logical-sector requests by MS-DOS's interpretation of
  1219.       the disk directory and FAT.)
  1220.  
  1221.   3.  MS-DOS calls the device driver's strat entry point, passing the
  1222.       address of the request header in the ES:BX registers.
  1223.  
  1224.   4.  The device driver saves the address of the request header in a local
  1225.       variable and performs a FAR RETURN.
  1226.  
  1227.   5.  MS-DOS calls the device driver's intr entry point.
  1228.  
  1229.   6.  The interrupt routine saves all registers, retrieves the address of
  1230.       the request header that was saved by the strategy routine, extracts
  1231.       the function code, and branches to the appropriate command-code
  1232.       subroutine to perform the function.
  1233.  
  1234.   7.  If a data transfer on a block device was requested, the driver's read
  1235.       or write subroutine translates the logical-sector number into a head,
  1236.       track, and physical-sector address for the requested unit and then
  1237.       performs the I/O operation. Because a multiple-sector transfer can be
  1238.       requested in a single request header, a single request by MS-DOS to
  1239.       the driver can result in multiple read or write commands to the disk
  1240.       controller.
  1241.  
  1242.   8.  When the requested function is complete, the interrupt routine sets
  1243.       the status word and any other required information into the request
  1244.       header, restores all registers to their state at entry, and performs a
  1245.       FAR RETURN.
  1246.  
  1247.   9.  MS-DOS translates the driver's return status into the appropriate
  1248.       return code and carry-flag status for the MS-DOS Int 21H function that
  1249.       was requested and returns control to the application program.
  1250.  
  1251.   Note that a single request by an application program can result in MS-DOS
  1252.   passing many request headers to the driver. For example, attempting to
  1253.   open a file in a subdirectory on a previously unaccessed disk drive might
  1254.   require the following actions:
  1255.  
  1256.   ■  Reading the disk's boot sector to get the BPB
  1257.  
  1258.   ■  Reading from one to many sectors of the root directory to find the
  1259.      entry for the subdirectory and obtain its starting-cluster number
  1260.  
  1261.   ■  Reading from one to many sectors of both the FAT and the subdirectory
  1262.      itself to find the entry for the desired file
  1263.  
  1264.  
  1265. The CLOCK Driver: A Special Case
  1266.  
  1267.   MS-DOS uses the CLOCK device for marking file control blocks and directory
  1268.   entries with the date and time, as well as for providing the date and time
  1269.   services to application programs. This device has a unique type of
  1270.   interaction with MS-DOS──a 6-byte sequence is read from or written to the
  1271.   driver that obtains or sets the current date and time. The sequence has
  1272.   the following format:
  1273.  
  1274.   ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
  1275.   │    0    │    1    │    2    │    3    │   4     │    5    │
  1276.   │  Days   │  Days   │ Minutes │  Hours  │Seconds/ │ Seconds │
  1277.   │low byte │high byte│         │         │  100    │         │
  1278.   └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
  1279.  
  1280.   The value passed for days is a 16-bit integer representing the number of
  1281.   days elapsed since January 1, 1980.
  1282.  
  1283.   The clock driver can have any logical-device name because MS-DOS uses the
  1284.   CLOCK bit in the device attribute word of the driver's device header to
  1285.   identify the device, rather than its name. On IBM PC systems, the clock
  1286.   device has the logical-device name CLOCK$.
  1287.  
  1288.  
  1289. Writing and Installing a Device Driver
  1290.  
  1291.   Now that we have discussed the structure and capabilities of installable
  1292.   device drivers for the MS-DOS environment, we can discuss the mechanical
  1293.   steps of assembling and linking them.
  1294.  
  1295. Assembly
  1296.  
  1297.   Device drivers for MS-DOS always have an origin of zero but are otherwise
  1298.   assembled, linked, and converted into an executable module as though they
  1299.   were .COM files. (Although MS-DOS is also capable of loading installable
  1300.   drivers in the .EXE file format, this introduces unnecessary complexity
  1301.   into writing and debugging drivers and offers no significant advantages.
  1302.   In addition, it is not possible to use .EXE-format drivers with some IBM
  1303.   versions of MS-DOS because the .EXE loader is located in COMMAND.COM,
  1304.   which is not present when the installable device drivers are being
  1305.   loaded.) The driver should not have a declared stack segment and must, in
  1306.   general, follow the other restrictions outlined in Chapter 3 for
  1307.   memory-image (.COM) programs. A driver can be loaded anywhere, so beware
  1308.   that you do not make any assumptions in your code about the driver's
  1309.   location in physical memory. Figure 14-9 presents a skeleton example that
  1310.   you can follow as you read the next few pages.
  1311.  
  1312.   ──────────────────────────────────────────────────────────────────────────
  1313.           name    driver
  1314.           page    55,132
  1315.           title   DRIVER.ASM Device-Driver Skeleton
  1316.  
  1317.   ;
  1318.   ; DRIVER.ASM   MS-DOS device-driver skeleton
  1319.   ;
  1320.   ; The driver command-code routines are stubs only and have
  1321.   ; no effect but to return a nonerror "done" status.
  1322.   ;
  1323.   ; Copyright 1988 Ray Duncan
  1324.   ;
  1325.  
  1326.   _TEXT   segment word public 'CODE'
  1327.  
  1328.           assume  cs:_TEXT,ds:_TEXT,es:NOTHING
  1329.  
  1330.           org     0
  1331.  
  1332.   MaxCmd  equ     24              ; maximum allowed command code:
  1333.                                   ; 12 for MS-DOS 2
  1334.                                   ; 16 for MS-DOS 3.0-3.1
  1335.                                   ; 24 for MS-DOS 3.2-3.3
  1336.   cr      equ     0dh             ; ASCII carriage return
  1337.   lf      equ     0ah             ; ASCII linefeed
  1338.   eom     equ     '$'             ; end-of-message signal
  1339.  
  1340.  
  1341.   Header:                         ; device-driver header
  1342.           dd      -1              ; link to next device driver
  1343.           dw      0c840h          ; device attribute word
  1344.           dw      Strat           ; "strategy" routine entry point
  1345.           dw      Intr            ; "interrupt" routine entry point
  1346.           db      'SKELETON'      ; logical-device name
  1347.  
  1348.  
  1349.   RHPtr   dd      ?               ; pointer to request header, passed
  1350.                                   ; by MS-DOS kernel to strategy routine
  1351.  
  1352.  
  1353.   Dispatch:                       ; interrupt-routine command-code
  1354.                                   ; dispatch table:
  1355.           dw      Init            ; 0  = initialize driver
  1356.           dw      MediaChk        ; 1  = media check
  1357.           dw      BuildBPB        ; 2  = build BPB
  1358.           dw      IoctlRd         ; 3  = IOCTL read
  1359.           dw      Read            ; 4  = read
  1360.           dw      NdRead          ; 5  = nondestructive read
  1361.           dw      InpStat         ; 6  = input status
  1362.           dw      InpFlush        ; 7  = flush input buffers
  1363.           dw      Write           ; 8  = write
  1364.           dw      WriteVfy        ; 9  = write with verify
  1365.           dw      OutStat         ; 10 = output status
  1366.           dw      OutFlush        ; 11 = flush output buffers
  1367.           dw      IoctlWt         ; 12 = IOCTL write
  1368.           dw      DevOpen         ; 13 = device open       (MS-DOS 3.0+)
  1369.           dw      DevClose        ; 14 = device close      (MS-DOS 3.0+)
  1370.           dw      RemMedia        ; 15 = removable media  (MS-DOS 3.0+)
  1371.           dw      OutBusy         ; 16 = output until busy (MS-DOS 3.0+)
  1372.           dw      Error           ; 17 = not used
  1373.           dw      Error           ; 18 = not used
  1374.           dw      GenIOCTL        ; 19 = generic IOCTL     (MS-DOS 3.2+)
  1375.           dw      Error           ; 20 = not used
  1376.           dw      Error           ; 21 = not used
  1377.           dw      Error           ; 22 = not used
  1378.           dw      GetLogDev       ; 23 = get logical device (MS-DOS 3.2+)
  1379.           dw      SetLogDev       ; 24 = set logical device (MS-DOS 3.2+)
  1380.   Strat   proc    far             ; device-driver strategy routine,
  1381.                                   ; called by MS-DOS kernel with
  1382.                                   ; ES:BX = address of request header
  1383.  
  1384.                                   ; save pointer to request header
  1385.           mov     word ptr cs:[RHPtr],bx
  1386.           mov     word ptr cs:[RHPtr+2],es
  1387.  
  1388.           ret                     ; back to MS-DOS kernel
  1389.  
  1390.   Strat   endp
  1391.  
  1392.  
  1393.   Intr    proc  far               ; device-driver interrupt routine,
  1394.                                   ; called by MS-DOS kernel immediately
  1395.                                   ; after call to strategy routine
  1396.  
  1397.           push    ax              ; save general registers
  1398.           push    bx
  1399.           push    cx
  1400.           push    dx
  1401.           push    ds
  1402.           push    es
  1403.           push    di
  1404.           push    si
  1405.           push    bp
  1406.  
  1407.           push    cs              ; make local data addressable
  1408.           pop     ds              ; by setting DS = CS
  1409.  
  1410.           les     di,[RHPtr]      ; let ES:DI = request header
  1411.  
  1412.                                   ; get BX = command code
  1413.           mov     bl,es:[di+2]
  1414.           xor     bh,bh
  1415.           cmp     bx,MaxCmd       ; make sure it's legal
  1416.           jle     Intr1           ; jump, function code is ok
  1417.           call    Error           ; set error bit, "unknown command" code
  1418.           jmp     Intr2
  1419.  
  1420.   Intr1:  shl     bx,1            ; form index to dispatch table
  1421.                                   ; and branch to command-code routine
  1422.           call    word ptr [bx+Dispatch]
  1423.  
  1424.           les     di,[RHPtr]      ; ES:DI = addr of request header
  1425.  
  1426.   Intr2:  or      ax,0100h        ; merge 'done' bit into status and
  1427.           mov     es:[di+3],ax    ; store status into request header
  1428.  
  1429.           pop     bp              ; restore general registers
  1430.           pop     si
  1431.           pop     di
  1432.           pop     es
  1433.           pop     ds
  1434.           pop     dx
  1435.           pop     cx
  1436.           pop     bx
  1437.           pop     ax
  1438.           ret                     ; back to MS-DOS kernel
  1439.  
  1440.  
  1441.   ; Command-code routines are called by the interrupt routine
  1442.   ; via the dispatch table with ES:DI pointing to the request
  1443.   ; header.  Each routine should return AX = 0 if function was
  1444.   ; completed successfully or AX = (8000h + error code) if
  1445.   ; function failed.
  1446.  
  1447.  
  1448.   MediaChk proc   near            ; function 1 = media check
  1449.  
  1450.           xor     ax,ax
  1451.           ret
  1452.  
  1453.   MediaChk endp
  1454.  
  1455.  
  1456.   BuildBPB proc   near            ; function 2 = build BPB
  1457.  
  1458.           xor     ax,ax
  1459.           ret
  1460.  
  1461.   BuildBPB endp
  1462.  
  1463.  
  1464.   IoctlRd proc    near            ; function 3 = IOCTL read
  1465.  
  1466.           xor     ax,ax
  1467.           ret
  1468.  
  1469.   IoctlRd endp
  1470.   Read    proc    near            ; function 4 = read (input)
  1471.  
  1472.           xor     ax,ax
  1473.           ret
  1474.  
  1475.   Read    endp
  1476.  
  1477.  
  1478.   NdRead  proc    near            ; function 5 = nondestructive read
  1479.  
  1480.           xor     ax,ax
  1481.           ret
  1482.  
  1483.   NdRead  endp
  1484.  
  1485.  
  1486.   InpStat proc    near            ; function 6 = input status
  1487.  
  1488.           xor     ax,ax
  1489.           ret
  1490.  
  1491.   InpStat endp
  1492.  
  1493.  
  1494.   InpFlush proc   near            ; function 7 = flush input buffers
  1495.  
  1496.           xor     ax,ax
  1497.           ret
  1498.  
  1499.   InpFlush endp
  1500.  
  1501.  
  1502.   Write   proc    near            ; function 8 = write (output)
  1503.  
  1504.           xor     ax,ax
  1505.           ret
  1506.  
  1507.   Write   endp
  1508.  
  1509.  
  1510.   WriteVfy proc   near            ; function 9 = write with verify
  1511.  
  1512.           xor     ax,ax
  1513.           ret
  1514.   endp
  1515.  
  1516.  
  1517.   OutStat proc    near            ; function 10 = output status
  1518.  
  1519.           xor     ax,ax
  1520.           ret
  1521.  
  1522.   OutStat endp
  1523.  
  1524.  
  1525.   OutFlush proc   near            ; function 11 = flush output buffers
  1526.  
  1527.           xor     ax,ax
  1528.           ret
  1529.  
  1530.   OutFlush endp
  1531.  
  1532.  
  1533.   IoctlWt proc    near            ; function 12 = IOCTL write
  1534.  
  1535.           xor     ax,ax
  1536.           ret
  1537.  
  1538.   IoctlWt endp
  1539.  
  1540.  
  1541.   DevOpen proc    near            ; function 13 = device open
  1542.  
  1543.           xor     ax,ax
  1544.           ret
  1545.  
  1546.   DevOpen endp
  1547.  
  1548.  
  1549.   DevClose proc   near            ; function 14 = device close
  1550.  
  1551.           xor     ax,ax
  1552.           ret
  1553.  
  1554.   DevClose endp
  1555.   RemMedia proc   near            ; function 15 = removable media
  1556.  
  1557.           xor     ax,ax
  1558.           ret
  1559.  
  1560.   RemMedia endp
  1561.  
  1562.  
  1563.   OutBusy proc    near            ; function 16 = output until busy
  1564.  
  1565.           xor     ax,ax
  1566.           ret
  1567.  
  1568.   OutBusy endp
  1569.  
  1570.  
  1571.   GenIOCTL proc   near            ; function 19 = generic IOCTL
  1572.  
  1573.           xor     ax,ax
  1574.           ret
  1575.  
  1576.   GenIOCTL endp
  1577.  
  1578.  
  1579.   GetLogDev proc  near            ; function 23 = get logical device
  1580.  
  1581.           xor     ax,ax
  1582.           ret
  1583.  
  1584.   GetLogDev endp
  1585.  
  1586.  
  1587.   SetLogDev proc  near            ; function 24 = set logical device
  1588.  
  1589.           xor     ax,ax
  1590.           ret
  1591.  
  1592.   SetLogDev endp
  1593.  
  1594.  
  1595.   Error   proc    near            ; bad command code in request header
  1596.  
  1597.           mov     ax,8003h        ; error bit + "unknown command" code
  1598.           ret
  1599.     endp
  1600.  
  1601.  
  1602.   Init    proc    near            ; function 0 = initialize driver
  1603.  
  1604.           push    es              ; save address of request header
  1605.           push    di
  1606.  
  1607.           mov     ax,cs           ; convert load address to ASCII
  1608.           mov     bx,offset Ident1
  1609.           call    hexasc
  1610.  
  1611.           mov     ah,9            ; display driver sign-on message
  1612.           mov     dx,offset Ident
  1613.           int     21h
  1614.  
  1615.           pop     di              ; restore request-header address
  1616.           pop     es
  1617.  
  1618.                                   ; set address of free memory
  1619.                                   ; above driver (break address)
  1620.           mov     word ptr es:[di+14],offset Init
  1621.           mov     word ptr es:[di+16],cs
  1622.  
  1623.           xor     ax,ax           ; return status
  1624.           ret
  1625.  
  1626.   Init    endp
  1627.  
  1628.  
  1629.   hexasc  proc    near            ; converts word to hex ASCII
  1630.                                   ; call with AX = value,
  1631.                                   ; DS:BX = address for string
  1632.                                   ; returns AX, BX destroyed
  1633.  
  1634.           push    cx              ; save registers
  1635.           push    dx
  1636.  
  1637.           mov     dx,4            ; initialize character counter
  1638.           mov     cx,4            ; isolate next four bits
  1639.           rol     ax,cl
  1640.           mov     cx,ax
  1641.           and     cx,0fh
  1642.           add     cx,'0'          ; convert to ASCII
  1643.           cmp     cx,'9'          ; is it 0-9?
  1644.           jbe     hexasc2         ; yes, jump
  1645.           add     cx,'A'-'9'-1    ; add fudge factor for A-F
  1646.  
  1647.   hexasc2:                        ; store this character
  1648.           mov     [bx],cl
  1649.           inc     bx              ; bump string pointer
  1650.  
  1651.           dec     dx              ; count characters converted
  1652.           jnz     hexasc1         ; loop, not four yet
  1653.  
  1654.           pop     dx              ; restore registers
  1655.           pop     cx
  1656.           ret                     ; back to caller
  1657.  
  1658.   hexasc  endp
  1659.  
  1660.  
  1661.   Ident   db      cr,lf,lf
  1662.           db      'Advanced MS-DOS Example Device Driver'
  1663.           db      cr,lf
  1664.           db      'Device driver header at: '
  1665.   Ident1  db      'XXXX:0000'
  1666.           db      cr,lf,lf,eom
  1667.  
  1668.   Intr    endp
  1669.  
  1670.   _TEXT   ends
  1671.  
  1672.           end
  1673.   ──────────────────────────────────────────────────────────────────────────
  1674.  
  1675.   Figure 14-9.  DRIVER.ASM: A functional skeleton from which you can
  1676.   implement your own working device driver.
  1677.  
  1678.   The driver's device header must be located at the beginning of the file
  1679.   (offset 0000H). Both words in the link field in the header should be set
  1680.   to -1. The attribute word must be set up correctly for the device type and
  1681.   other options. The offsets to the strategy and interrupt routines must be
  1682.   relative to the same segment base as the device header itself. If the
  1683.   driver is for a character device, the name field should be filled in
  1684.   properly with the device's logical name. The logical name can be any legal
  1685.   8-character filename, padded with spaces and without a colon. Beware of
  1686.   accidentally duplicating the names of existing character devices, unless
  1687.   you are intentionally superseding a resident driver.
  1688.  
  1689.   MS-DOS calls the strategy and interrupt routines for the device by means
  1690.   of an intersegment call (CALL FAR) when the driver is first loaded and
  1691.   installed and again whenever an application program issues an I/O request
  1692.   for the device. MS-DOS uses the ES:BX registers to pass the strat routine
  1693.   a double-word pointer to the request header; this address should be saved
  1694.   internally in the driver so that it is available for use during the
  1695.   subsequent call to the intr routine.
  1696.  
  1697.   The command-code routines for function codes 0 through 12 (0CH) must be
  1698.   present in every installable device driver, regardless of device type.
  1699.   Functions 13 (0DH) and above are optional for drivers used with MS-DOS
  1700.   versions 3.0 and later and can be handled in one of the following ways:
  1701.  
  1702.   ■  Don't implement them, and leave the associated bits in the device
  1703.      header cleared. The resulting driver will work in either version 2 or
  1704.      version 3 but does not take full advantage of the augmented
  1705.      functionality of version 3.
  1706.  
  1707.   ■  Implement them, and test the MS-DOS version during the initialization
  1708.      sequence, setting bits 6 and 11 of the device header appropriately.
  1709.      Write all command-code routines so that they test this bit and adjust
  1710.      to accommodate the host version of MS-DOS. Such a driver requires more
  1711.      work and testing but will take full advantage of both the version 2 and
  1712.      the version 3 environments.
  1713.  
  1714.   ■  Implement them, and assume that all the version 3 facilities are
  1715.      available. With this approach, the resulting driver may not work
  1716.      properly under version 2.
  1717.  
  1718.   Remember that device drivers must preserve the integrity of MS-DOS. The
  1719.   driver must preserve all registers, including flags (especially the
  1720.   direction flag and interrupt enable bits), and if the driver makes heavy
  1721.   use of the stack, it should switch to an internal stack of adequate depth
  1722.   (the MS-DOS stack has room for only 40 to 50 bytes when a driver is
  1723.   called).
  1724.  
  1725.   If you install a new CON driver, be sure to set the bits for standard
  1726.   input and standard output in the device attribute word in the device
  1727.   header.
  1728.  
  1729.   You'll recall that one file can contain multiple drivers. In this case,
  1730.   the device-header link field of each driver should point to the segment
  1731.   offset of the next, all using the same segment base, and the link field
  1732.   for the last driver in the file should be set to -1,-1. The initialization
  1733.   routines for all the drivers in the file should return the same break
  1734.   address.
  1735.  
  1736. Linking
  1737.  
  1738.   Use the standard MS-DOS linker to transform the .OBJ file that is output
  1739.   from the assembler into a relocatable .EXE module. Then, use the EXE2BIN
  1740.   utility (see Chapter 4) to convert the .EXE file into a memory-image
  1741.   program. The extension on the final driver file can be anything, but .BIN
  1742.   and .SYS are most commonly used in MS-DOS systems, and it is therefore
  1743.   wise to follow one of these conventions.
  1744.  
  1745. Installation
  1746.  
  1747.   After the driver is assembled, linked, and converted to a .BIN or .SYS
  1748.   file, copy it to the root directory of a bootable disk. If it is a
  1749.   character-device driver, do not use the same name for the file as you used
  1750.   for the logical device listed in the driver's header, or you will not be
  1751.   able to delete, copy, or rename the file after the driver is loaded.
  1752.  
  1753.   Use your favorite text editor to add the line
  1754.  
  1755.     DEVICE=[D:][PATH]FILENAME.EXT
  1756.  
  1757.   to the CONFIG.SYS file on the bootable disk. (In this line, D: is an
  1758.   optional drive designator and FILENAME.EXT is the name of the file
  1759.   containing your new device driver. You can include a path specification in
  1760.   the entry if you prefer not to put the driver file in your root
  1761.   directory.) Now restart your computer system to load the modified
  1762.   CONFIG.SYS file.
  1763.  
  1764.   During the MS-DOS boot sequence, the SYSINIT module (which is part of
  1765.   IO.SYS) reads and processes the CONFIG.SYS file. It loads the driver into
  1766.   memory and inspects the device header. If the driver is a character-device
  1767.   driver, SYSINIT links it into the device chain ahead of the other
  1768.   character devices; if it is a block-device driver, SYSINIT places it
  1769.   behind all previously linked block devices and the resident block devices
  1770.   (Figures 14-10, 14-11, and 14-12). It accomplishes the linkage by
  1771.   updating the link field in the device header to point to the segment and
  1772.   offset of the next driver in the chain. The link field of the last driver
  1773.   in the chain contains -1,-1.
  1774.  
  1775.   Next, SYSINIT calls the strat routine with a request header that contains
  1776.   a command code of zero, and then it calls the intr routine. The driver
  1777.   executes its initialization routine and returns the break address, telling
  1778.   MS-DOS how much memory to reserve for this driver. Now MS-DOS can proceed
  1779.   to the next entry in the CONFIG.SYS file.
  1780.  
  1781.   You cannot supersede a built-in block-device driver──you can only add
  1782.   supplemental block devices. However, you can override the default system
  1783.   driver for a character device (such as CON) with an installed driver by
  1784.   giving it the same logical-device name in the device header. When
  1785.   processing a character I/O request, MS-DOS always scans the list of
  1786.   installed drivers before it scans the list of default devices and takes
  1787.   the first match.
  1788.  
  1789.              NUL
  1790.               │
  1791.               
  1792.              CON
  1793.               │
  1794.               
  1795.              AUX
  1796.               │
  1797.               
  1798.              PRN
  1799.               │
  1800.               
  1801.             CLOCK
  1802.               │
  1803.               
  1804.   Any other resident block
  1805.     or character devices
  1806.  
  1807.   Figure 14-10.  MS-DOS device-driver chain before any installable device
  1808.   drivers have been loaded.
  1809.  
  1810.              NUL
  1811.               │
  1812.               
  1813.    Installable character-
  1814.        device drivers
  1815.               │
  1816.               
  1817.              CON
  1818.               │
  1819.               
  1820.              AUX
  1821.               │
  1822.               
  1823.              PRN
  1824.               │
  1825.               
  1826.             CLOCK
  1827.               │
  1828.               
  1829.   Any other resident block
  1830.     or character devices
  1831.               │
  1832.               
  1833.      Installable block-
  1834.        device drivers
  1835.  
  1836.   Figure 14-11.  MS-DOS device-driver chain after installable device drivers
  1837.   have been loaded.
  1838.  
  1839.   Address          Attribute  Strategy    Interrupt  Type  Units Name
  1840.                               routine     routine
  1841.   ──────────────────────────────────────────────────────────────────────────
  1842.   00E3:0111        8004       0FD5        0FE0       C           NUL
  1843.   0070:0148        8013       008E        0099       C           CON
  1844.   0070:01DD        8000       008E        009F       C           AUX
  1845.   0070:028E        8000       008E        00AE       C           PRN
  1846.   0070:0300        8008       008E        00C3       C           CLOCK
  1847.   0070:03CC        0000       008E        00C9       B     02
  1848.   0070:01EF        8000       008E        009F       C           COM1
  1849.   0070:02A0        8000       008E        00AE       C           LPT1
  1850.   0070:06F0        8000       008E        00B4       C           LPT2
  1851.   0070:0702        8000       008E        00BA       C           LPT3
  1852.   0070:0714        8000       008E        00A5       C           COM2
  1853.   End of
  1854.   device chain
  1855.   ──────────────────────────────────────────────────────────────────────────
  1856.  
  1857.   Figure 14-12.  Example listing of device chain under MS-DOS version 2.1,
  1858.   "plain vanilla" IBM PC with no fixed disks or user device drivers.
  1859.   (C=character device, B=block device)
  1860.  
  1861.  
  1862. Debugging a Device Driver
  1863.  
  1864.   The most important thing to remember when testing new device drivers is to
  1865.   maintain adequate backups and a viable fallback position. Don't modify the
  1866.   CONFIG.SYS file and install the new driver on your fixed disk before it is
  1867.   proven! Be prudent──create a bootable floppy disk and put the modified
  1868.   CONFIG.SYS file and the new driver on that for debugging. When everything
  1869.   is working properly, copy the finished product to its permanent storage
  1870.   medium.
  1871.  
  1872.   The easiest way to test a new device driver is to write a simple
  1873.   assembly-language front-end routine that sets up a simulated request
  1874.   packet and then performs FAR CALLs to the strat and intr entry points,
  1875.   exactly as MS-DOS would. You can then link the driver and the front end
  1876.   together into a .COM or .EXE file that can be run under the control of
  1877.   CodeView or another debugger. This arrangement makes it easy to trace each
  1878.   of the command-code routines individually, to observe the results of the
  1879.   I/O, and to examine the status codes returned in the request header.
  1880.  
  1881.   Tracing the installed driver when it is linked into the MS-DOS system in
  1882.   the normal manner is more difficult. Breakpoints must be chosen carefully,
  1883.   to yield the maximum possible information per debugging run. Because
  1884.   current versions of MS-DOS maintain only one request header internally,
  1885.   the request header that was being used by the driver you are tracing will
  1886.   be overwritten as soon as your debugger makes an output request to display
  1887.   information. You will find it helpful to add a routine to your
  1888.   initialization subroutine that displays the driver's load address on the
  1889.   console when you boot MS-DOS; you can then use this address to inspect the
  1890.   device-driver header and set breakpoints within the body of the driver.
  1891.  
  1892.   Debugging a device driver can also be somewhat sticky when interrupt
  1893.   handling is involved, especially if the device uses the same
  1894.   interrupt-request priority level (IRQ level) as other peripherals in the
  1895.   system. Cautious, conservative programming is needed to avoid unexpected
  1896.   and unreproducible interactions with other device drivers and interrupt
  1897.   handlers. If possible, prove out the basic logic of the driver using
  1898.   polled I/O, rather than interrupt-driven I/O, and introduce interrupt
  1899.   handling only when you know the rest of the driver's logic to be solid.
  1900.  
  1901.   Typical device-driver errors or problems that can cause system crashes or
  1902.   strange system behavior include the following:
  1903.  
  1904.   ■  Failure to set the linkage address of the last driver in a file to -1
  1905.  
  1906.   ■  Overflow of the MS-DOS stack by driver-initialization code, corrupting
  1907.      the memory image of MS-DOS (can lead to unpredictable behavior during
  1908.      boot; remedy is to use a local stack)
  1909.  
  1910.   ■  Incorrect break-address reporting by the initialization routine (can
  1911.      lead to a system crash if the next driver loaded overwrites vital parts
  1912.      of the driver)
  1913.  
  1914.   ■  Improper BPBs supplied by the build BPB routine, or incorrect BPB
  1915.      pointer array supplied by the initialization routine (can lead to many
  1916.      confusing problems, ranging from out-of-memory errors to system boot
  1917.      failure)
  1918.  
  1919.   ■  Incorrect reporting of the number of bytes or sectors successfully
  1920.      transferred at the time an I/O error occurs (can manifest itself as a
  1921.      system crash after you enter R to the Abort, Retry, Ignore? prompt)
  1922.  
  1923.   Although the interface between the DOS kernel and the device driver is
  1924.   fairly simple, it is also quite strict. The command-code routines must
  1925.   perform exactly as they are defined, or the system will behave
  1926.   erratically. Even a very subtle discrepancy in the action of a
  1927.   command-code routine can have unexpectedly large global effects.
  1928.  
  1929.  
  1930.  
  1931.