802 lines
37 KiB
Plaintext
802 lines
37 KiB
Plaintext
MS-DOS 2.0 Device Drivers
|
||
|
||
INTRODUCTION
|
||
|
||
In the past, DOS-device driver (BIOS for those who are
|
||
familiar with CP/M) communication has been mediated with
|
||
registers and a fixed-address jump-table. This approach
|
||
has suffered heavily from the following two observations:
|
||
|
||
o The old jump-table ideas of the past are fixed in
|
||
scope and allow no extensibility.
|
||
|
||
o The past device driver interfaces have been written
|
||
without regard for the true power of the hardware.
|
||
When a multitasking system or interrupt driven
|
||
hardware is installed a new BIOS must be written
|
||
largely from scratch.
|
||
|
||
In MSDOS 2.0, the DOS-device driver interface has changed
|
||
from the old jump-table style to one in which the device
|
||
drivers are linked together in a list. This allows new
|
||
drivers for optional hardware to be installed (and even
|
||
written) in the field by other vendors or the user himself.
|
||
This flexibility is one of the major new features of MS-DOS
|
||
2.0.
|
||
|
||
Each driver in the chain defines two entry points; the
|
||
strategy routine and the interrupt routine. The 2.0 DOS
|
||
does not really make use of two entry points (it simply calls
|
||
strategy, then immediately calls interrupt). This dual entry
|
||
point scheme is designed to facilitate future multi-tasking
|
||
versions of MS-DOS. In multi-tasking environments I/O must
|
||
be asynchronous, to accomplish this the strategy routine
|
||
will be called to queue (internally) a request and return
|
||
quickly. It is then the responsibility of the interrupt
|
||
routine to perform the actual I/O at interrupt time by picking
|
||
requests off the internal queue (set up by the strategy
|
||
routine), and process them. When a request is complete,
|
||
it is flagged as "done" by the interrupt routine. The DOS
|
||
periodically scans the list of requests looking for ones
|
||
flagged as done, and "wakes up" the process waiting for the
|
||
completion of the request.
|
||
|
||
In order for requests to be queued as above it is no
|
||
longer sufficient to pass I/O information in registers, since
|
||
many requests may be pending at any one time. Therefore
|
||
the new device interface uses data "packets" to pass request
|
||
information. A device is called with a pointer to a packet,
|
||
this packet is linked into a global chain of all pending
|
||
I/O requests maintained by the DOS. The device then links
|
||
the packet into its own local chain of requests for this
|
||
particular device. The device interrupt routine picks
|
||
requests of the local chain for processing. The DOS scans
|
||
the global chain looking for completed requests. These
|
||
packets are composed of two pieces, a static piece which
|
||
has the same format for all requests (called the static
|
||
request header), which is followed by information specific
|
||
to the request. Thus packets have a variable size and format.
|
||
|
||
At this points it should be emphasized that MS-DOS 2.0
|
||
does not implement most of these features, as future versions
|
||
will. There is no global or local queue. Only one request
|
||
is pending at any one time, and the DOS waits for this current
|
||
request to be completed. For 2.0 it is sufficient for the
|
||
strategy routine to simply store the address of the packet
|
||
at a fixed location, and for the interrupt routine to then
|
||
process this packet by doing the request and returning.
|
||
Remember: the DOS just calls the strategy routine and then
|
||
immediately calls the interrupt routine, it is assumed that
|
||
the request is completed when the interrupt routine returns.
|
||
This additional functionality is defined at this time so
|
||
that people will be aware and thinking about the future.
|
||
|
||
|
||
FORMAT OF A DEVICE DRIVER
|
||
|
||
A device driver is simply a relocatable memory image
|
||
with all of the code in it to implement the device (like
|
||
a .COM file, but not ORGed at 100 Hex). In addition it has
|
||
a special header at the front of it which identifies it as
|
||
a device, defines the strategy and interrupt entry points,
|
||
and defines various attributes. It should also be noted
|
||
that there are two basic types of devices.
|
||
|
||
The first is character devices. These are devices which
|
||
are designed to do character I/O in a serial manner like
|
||
CON, AUX, and PRN. These devices are named (ie. CON, AUX,
|
||
CLOCK, etc.), and users may open channels (FCBs) to do I/O
|
||
to them.
|
||
|
||
The second class of devices is block devices. These
|
||
devices are the "disk drives" on the system, they can do
|
||
random I/O in pieces called blocks (usually the physical
|
||
sector size) and hence the name. These devices are not
|
||
"named" as the character devices are, and therefore cannot
|
||
be "opened" directly. Instead they are "mapped" via the
|
||
drive letters (A,B,C, etc.).
|
||
|
||
Block devices also have units. In other words a single
|
||
driver may be responsible for one or more disk drives. For
|
||
instance block device driver ALPHA (please note that we cannot
|
||
actually refer to block devices by a name!) may be
|
||
responsible for drives A,B,C and D, this simply means that
|
||
it has four units (0-3) defined and therefore takes up four
|
||
drive letters. Which units correspond to which drive letters
|
||
is determined by the position of the driver in the chain
|
||
of all drivers: if driver ALPHA is the first block driver
|
||
in the device chain, and it defines 4 units (0-3), then they
|
||
will be A,B,C and D. If BETA is the second block driver
|
||
and defines three units (0-2), then they will be E,F and
|
||
G and so on. MS-DOS 2.0 is not limited to 16 block device
|
||
units, as previous versions were. The theoretical limit
|
||
is 63 (2^6 - 1), but it should be noted that after 26 the
|
||
drive letters get a little strange (like ] \ and ^). NOTE:
|
||
Character devices cannot define multiple units (this because
|
||
they have only one name).
|
||
|
||
|
||
Here is what that special device header looks like:
|
||
|
||
+--------------------------------------+
|
||
| DWORD Pointer to next device |
|
||
| (Must be set to -1) |
|
||
+--------------------------------------+
|
||
| WORD Attributes |
|
||
| Bit 15 = 1 if char device 0 if blk |
|
||
| if bit 15 is 1 |
|
||
| Bit 0 = 1 if Current sti device |
|
||
| Bit 1 = 1 if Current sto output |
|
||
| Bit 2 = 1 if Current NUL device |
|
||
| Bit 3 = 1 if Current CLOCK dev |
|
||
| Bit 4 = 1 if SPECIAL |
|
||
| Bit 14 is the IOCTL bit (see below) |
|
||
| Bit 13 is the NON IBM FORMAT bit |
|
||
+--------------------------------------+
|
||
| WORD Pointer to Device strategy |
|
||
| entry point |
|
||
+--------------------------------------+
|
||
| WORD Pointer to Device interrupt |
|
||
| entry point |
|
||
+--------------------------------------+
|
||
| 8-BYTE character device name field |
|
||
| Character devices set a device name |
|
||
| For block devices the first byte is |
|
||
| The number of units |
|
||
+--------------------------------------+
|
||
|
||
Note that the device entry points are words. They must
|
||
be offsets from the same segment number used to point to
|
||
this table. Ie. if XXX.YYY points to the start of this
|
||
table, then XXX.strategy and XXX.interrupt are the entry
|
||
points.
|
||
|
||
A word about the Attribute field. This field is used
|
||
most importantly to tell the system whether this device is
|
||
a block or character device (bit 15). Most of other bits
|
||
are used to give selected character devices certain special
|
||
treatment (NOTE: these bits mean nothing on a block device).
|
||
Let's say a user has a new device driver which he wants to
|
||
be the standard input and output. Besides just installing
|
||
the driver he needs to tell SYSINIT (and the DOS) that he
|
||
wishes his new driver to override the current sti and sto
|
||
(the "CON" device). This is accomplished by setting the
|
||
attributes to the desired characteristics, so he would set
|
||
Bits 0 and 1 to 1 (note that they are separate!!). Similarly
|
||
a new CLOCK device could be installed by setting that
|
||
attribute, see the section at the end on the CLOCK device.
|
||
NOTE: that although there is a NUL device attribute, the
|
||
NUL device cannot be re-assigned. This attribute exists
|
||
for the DOS so that it can tell if the NUL device is being
|
||
used.
|
||
|
||
The NON IBM FORMAT bit applies only to block devices
|
||
and effects the operation of the get BPB device call (see
|
||
below).
|
||
|
||
The other bit of interest is the IOCTL bit which has
|
||
meaning on character or block devices. This bit tells the
|
||
DOS whether this device can handle control strings (via the
|
||
IOCTL system call).
|
||
|
||
If a driver cannot process control strings, it should
|
||
initially set this bit to 0. This tells the DOS to return
|
||
an error if an attempt is made (via IOCTL system call) to
|
||
send or receive control strings to this device. A device
|
||
which can process control strings should initialize it to
|
||
1. For drivers of this type, the DOS will make calls to
|
||
the IOCTL INPUT and OUTPUT device functions to send and
|
||
receive IOCTL strings (see IOCTL in the SYSTEM-CALLS
|
||
document).
|
||
|
||
The IOCTL functions allow data to be sent and received
|
||
by the device itself for its own use (to set baud rate, stop
|
||
bits, form length etc., etc.), instead of passing data over
|
||
the device channel as a normal read or write does. The
|
||
interpretation of the passed information is up to the device,
|
||
but it MUST NOT simply be treated as a normal I/O.
|
||
|
||
The SPECIAL bit applies only to character drivers and
|
||
more particularly to CON drivers. The new 2.0 interface
|
||
is a much more general and consistent interface than the
|
||
old 1.25 DOS interface. It allows for a number of additional
|
||
features of 2.0. It is also slower than 1.25 if old style
|
||
"single byte" system calls are made. To make most efficient
|
||
use of the interface all applications should block their
|
||
I/O as much as possible. This means make one XENIX style
|
||
system call to output X bytes rather than X system calls
|
||
to output one byte each. Also putting a device channel in
|
||
RAW mode (see IOCTL) provides a means of putting out
|
||
characters even FASTER than 1.25. To help alleviate the
|
||
CON output speed problem for older programs which use the
|
||
1 - 12 system calls to output large amounts of data the
|
||
SPECIAL bit has been implemented. If this bit is 1 it means
|
||
the device is the CON output device, and has implemented
|
||
an interrupt 29 Hex handler, where the 29 Hex handler is
|
||
defined as follows:
|
||
|
||
Interrupt 29h handlers
|
||
|
||
Input:
|
||
Character in AL
|
||
|
||
Function:
|
||
output the character in al to the user
|
||
screen.
|
||
Output:
|
||
None
|
||
Registers:
|
||
all registers except bx must be preserved.
|
||
No registers except for al have a known or
|
||
consistent value.
|
||
|
||
If a character device implements the SPECIAL bit, it
|
||
is the responsibility of the driver to install an address
|
||
at the correct location in the interrupt table for interrupt
|
||
29 Hex as part of its INIT code. IMPLICATION: There can
|
||
be only one device driver with the SPECIAL bit set in the
|
||
system. There is no check to insure this state.
|
||
|
||
WARNING: THIS FEATURE WILL NOT BE SUPPORTED IN FUTURE VERSIONS
|
||
OF THE OPERATING SYSTEM. IMPLICATION: Any application
|
||
(not device driver) which uses INT 29H directly will
|
||
not work on future versions, YOU HAVE BEEN WARNED.
|
||
|
||
In order to "make" a device driver that SYSINIT can
|
||
install, a memory image or .EXE (non-IBM only) format file
|
||
must be created with the above header at the start. The
|
||
link field should be initialized to -1 (SYSINIT fills it
|
||
in). The attribute field and entry points must be set
|
||
correctly, and if the device is a character device, the name
|
||
field must be filled in with the name (if a block device
|
||
SYSINIT will fill in the correct unit count). This name
|
||
can be any 8 character "legal" file name. In fact SYSINIT
|
||
always installs character devices at the start of the device
|
||
list, so if you want to install a new CON device all you
|
||
have to do is name it "CON". The new one is ahead of the
|
||
old one in the list and thus preempts the old one as the
|
||
search for devices stops on the first match. Be sure to
|
||
set the sti and sto bits on a new CON device!
|
||
|
||
NOTE: Since SYSINIT may install the driver anywhere, you
|
||
must be very careful about FAR memory references. You
|
||
should NOT expect that your driver will go in the same
|
||
place every time (The default BIOS drivers are exempted
|
||
from this of course).
|
||
|
||
|
||
INSTALLATION OF DEVICE DRIVERS
|
||
|
||
Unlike past versions MS-DOS 2.0 allows new device drivers
|
||
to be installed dynamically at boot time. This is
|
||
accomplished by the new SYSINIT module supplied by Microsoft,
|
||
which reads and processes the CONFIG.SYS file. This module
|
||
is linked together with the OEM default BIOS in a similar
|
||
manner to the way FORMAT is built.
|
||
|
||
One of the functions defined for each device is INIT.
|
||
This routine is called once when the device is installed,
|
||
and never again. The only thing returned by the init routine
|
||
is a location (DS:DX) which is a pointer to the first free
|
||
byte of memory after the device driver, (like a terminate
|
||
and stay resident). This pointer method can be used to "throw
|
||
away" initialization code that is only needed once, saving
|
||
on space.
|
||
|
||
Block devices are installed the same way and also return
|
||
a first free byte pointer as above, additional information
|
||
is also returned:
|
||
|
||
o The number of units is returned, this determines
|
||
logical device names. If the current maximum logical
|
||
device letter is F at the time of the install call,
|
||
and the init routine returns 4 as the number of units,
|
||
then they will have logical names G, H, I and J.
|
||
This mapping is determined by by the position of
|
||
the driver in the device list and the number of units
|
||
on the device (stored in the first byte of the device
|
||
name field).
|
||
|
||
o A pointer to a BPB (Bios Parameter Block) pointer
|
||
array is also returned. This will be similar to
|
||
the INIT table used in previous versions, but will
|
||
have more information in it. There is one table
|
||
for each unit defined. These blocks will be used
|
||
to build a DPB (Drive Parameter Block) for each of
|
||
the units. The pointer passed to the DOS from the
|
||
driver points to an array of n word pointers to BPBs
|
||
where n is the number of units defined. In this
|
||
way if all units are the same, all of the pointers
|
||
can point to the same BPB, saving space. NOTE: this
|
||
array must be protected (below the free pointer set
|
||
by the return) since the DPB will be built starting
|
||
at the byte pointed to by the free pointer. The
|
||
sector size defined must be less than or equal to
|
||
the maximum sector size defined at default BIOS init
|
||
time. If it isn't the install will fail. One new
|
||
piece of DPB info set from this table will be a "media
|
||
descriptor byte". This byte means nothing to the
|
||
DOS, but is passed to devices so that they know what
|
||
form of a DPB the DOS is currently using for a
|
||
particular Drive-Unit.
|
||
|
||
Block devices may take several approaches; they may be
|
||
dumb or smart. A dumb device would define a unit (and
|
||
therefore a DPB) for each possible media drive combination.
|
||
Unit 0 = drive 0 single side, unit 1 = drive 0 double side,
|
||
etc. For this approach media descriptor bytes would mean
|
||
nothing. A smart device would allow multiple media per unit,
|
||
in this case the BPB table returned at init must define space
|
||
large enough to accommodate the largest possible media
|
||
supported. Smart drivers will use the "media byte" to pass
|
||
around info about what media is currently in a unit. NOTE:
|
||
If the DPB is a "hybrid" made to get the right sizes, it
|
||
should give an invalid "media byte" back to the DOS.
|
||
|
||
The BOOT (default BIOS) drivers are installed pretty
|
||
much as above. The preset device list is scanned. If block
|
||
drivers are encountered they are installed as above (with
|
||
the exception that the break is not moved since the drivers
|
||
are already resident in the BIOS). Note that the logical
|
||
drive letters are assigned in list order, thus the driver
|
||
which is to have logical A must be the first unit of the
|
||
first block device in the list. The order of character
|
||
devices is also important. There must be at least 4 character
|
||
devices defined at boot which must be the first four devices
|
||
(of either type), the first will become standard input,
|
||
standard output, and standard error output. The second will
|
||
become standard auxiliary input and output, the third will
|
||
become standard list output, and the forth will become the
|
||
date/time (CLOCK) device. Thus the BIOS device list must
|
||
look like this:
|
||
|
||
->CON->AUX->PRN->CLOCK->any other block or character devices
|
||
|
||
THE DRIVER
|
||
|
||
A device driver will define the following functions:
|
||
|
||
Command Function
|
||
Code
|
||
|
||
0 INIT
|
||
1 MEDIA CHECK (Block only, NOP for character)
|
||
2 BUILD BPB " " " " "
|
||
3 IOCTL INPUT (Only called if device has IOCTL)
|
||
4 INPUT (read)
|
||
5 NON-DESTRUCTIVE INPUT NO WAIT (Char devs only)
|
||
6 INPUT STATUS " " "
|
||
7 INPUT FLUSH " " "
|
||
8 OUTPUT (write)
|
||
9 OUTPUT (Write) with verify
|
||
10 OUTPUT STATUS " " "
|
||
11 OUTPUT FLUSH " " "
|
||
12 IOCTL OUTPUT (Only called if device has IOCTL)
|
||
|
||
As mentioned before, the first entry point is the strategy
|
||
routine which is called with a pointer to a data block. This
|
||
call does not perform the request, all it does is queue it
|
||
(save the data block pointer). The second interrupt entry
|
||
point is called immediately after the strategy call. The
|
||
"interrupt" routine is called with no parameters, its primary
|
||
function is to perform the operation based on the queued
|
||
data block and set up any returns.
|
||
|
||
The "BUILD BPB" and "MEDIA CHECK" are the interesting
|
||
new ones, these are explained by examining the sequence of
|
||
events in the DOS which occurs when a drive access call (other
|
||
than read or write) is made:
|
||
|
||
I. Turn drive letter into DPB pointer by looking
|
||
for DPB with correct driver-unit number.
|
||
|
||
II. Call device driver and request media check for
|
||
Drive-Unit. DOS passes its current Media
|
||
descriptor byte (from DPB). Call returns:
|
||
|
||
Media Not Changed
|
||
Media Changed
|
||
Not Sure
|
||
Error
|
||
|
||
Error - If an error occurs the error code should
|
||
be set accordingly.
|
||
|
||
Media Not changed - Current DPB and media byte
|
||
are OK, done.
|
||
|
||
Media Changed - Current DPB and media are wrong,
|
||
invalidate any buffers for this unit, and
|
||
goto III.
|
||
|
||
Not Sure - If there are dirty buffers for this
|
||
unit, assume DPB and media byte are OK and
|
||
done. If nothing dirty, assume media changed,
|
||
invalidate any buffers for unit, and goto
|
||
III.
|
||
|
||
NOTE: If a hybrid DPB was built at init and
|
||
an invalid Media byte was set, the driver
|
||
should return media changed when this invalid
|
||
media byte is encountered.
|
||
|
||
III. Call device driver to build BPB with media byte
|
||
and buffer.
|
||
|
||
What the driver must do at step III is determine the
|
||
correct media that is currently in the unit, and return a
|
||
pointer to a BPB table (same as for the install call). This
|
||
table will be used as at init to build a correct DPB for
|
||
the unit If the determined media descriptor byte in the table
|
||
turns out to be the same as the one passed in, then the DOS
|
||
will not build a new table, but rather just use the old one.
|
||
Therefore in this case the driver doesn't have to correctly
|
||
fill in the other entries if desired.
|
||
|
||
The build BPB call also gets a pointer to a one sector
|
||
buffer. What this buffer contains is determined by the NON
|
||
IBM FORMAT bit in the attribute field. If the bit is zero
|
||
(device is IBM format compatible) then the buffer contains
|
||
the first sector of the first FAT, in particular the FAT
|
||
ID byte is the first byte of this buffer. NOTE: It must
|
||
be true that the BPB is the same, as far as location of the
|
||
FAT is concerned, for all possible media. This is because
|
||
this first FAT sector must be read BEFORE the actual BPB
|
||
is returned. If the NON IBM FORMAT bit is set then the
|
||
pointer points to one sector of scratch space which may be
|
||
used for anything.
|
||
|
||
CALL FORMAT
|
||
|
||
When the DOS calls a device driver to perform a finction,
|
||
it passes a structure (Drive Request Structure) in ES:BX
|
||
to perform operations and does a long call to the driver's
|
||
strategy entry point. This structure is a fixed length header
|
||
(Static Request Header) followed by data pertinent to the
|
||
operation being performed. NOTE: It is the drivers
|
||
responsibility to preserve machine state.
|
||
|
||
STATIC REQUEST HEADER ->
|
||
+-----------------------------+
|
||
| BYTE length of record |
|
||
| Length in bytes of this |
|
||
| Drive Request Structure |
|
||
+-----------------------------+
|
||
| BYTE unit code |
|
||
| The subunit the operation |
|
||
| is for (minor device) |
|
||
| (no meaning on character |
|
||
| devices) |
|
||
+-----------------------------+
|
||
| BYTE command code |
|
||
+-----------------------------+
|
||
| WORD Status |
|
||
+-----------------------------+
|
||
| 8 bytes reserved here for |
|
||
| two DWORD links. One will |
|
||
| be a link for the DOS queue |
|
||
| The other for the device |
|
||
| queue |
|
||
+-----------------------------+
|
||
|
||
STATUS WORD
|
||
|
||
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||
+---+---+--+--+--+--+---+---+--+--+--+--+--+--+--+--+
|
||
| E | | B | D | |
|
||
| R | RESERVED | U | O | ERROR CODE (bit 15 on)|
|
||
| R | | I | N | |
|
||
+---+---+--+--+--+--+---+---+--+--+--+--+--+--+--+--+
|
||
|
||
The status word is zero on entry and is set by the driver
|
||
interrupt routine on return.
|
||
|
||
Bit 8 is the done bit, it means the operation is complete.
|
||
For the moment the Driver just sets it to one when it exits,
|
||
in the future this will be set by the interrupt routine to
|
||
tell the DOS the operation is complete.
|
||
|
||
Bit 15 is the error bit, if it is set then the low 8
|
||
bits indicate the error:
|
||
|
||
0 Write Protect violation
|
||
(NEW) 1 Unknown Unit
|
||
2 Drive not ready
|
||
(NEW) 3 Unknown command
|
||
4 CRC error
|
||
(NEW) 5 Bad Drive Request Structure length
|
||
6 Seek error
|
||
(NEW) 7 Unknown media
|
||
8 Sector not found
|
||
(NEW) 9 Printer out of paper
|
||
A Write Fault
|
||
(NEW) B Read Fault
|
||
C General Failure
|
||
|
||
Bit 9 is the busy bit which is set only by status calls (see
|
||
STATUS CALL below).
|
||
|
||
|
||
Here is the data block format for each function:
|
||
|
||
READ or WRITE - ES:BX (Including IOCTL) ->
|
||
+------------------------------------+
|
||
| 13-BYTE Static Request Header |
|
||
+------------------------------------+
|
||
| BYTE Media descriptor from DPB |
|
||
+------------------------------------+
|
||
| DWORD transfer address |
|
||
+------------------------------------+
|
||
| WORD byte/sector Count |
|
||
---+------------------------------------+---
|
||
| WORD starting sector number |
|
||
| (ignored on Char Devs) |
|
||
+------------------------------------+
|
||
|
||
In addition to setting the status word, the driver must
|
||
set the Sector count to the actual number of sectors (or
|
||
bytes) transferred. NOTE: No error check is performed on
|
||
an IOCTL I/O call, driver MUST correctly set the return sector
|
||
(byte) count to the actual number of bytes transferred,
|
||
however.
|
||
|
||
NOTE: THE FOLLOWING APPLIES TO BLOCK DEVICE DRIVERS.
|
||
|
||
Under certain circumstances the BIOS may be asked to
|
||
do a write operation of 64K bytes which seems to be a "wrap
|
||
around" of the transfer address in the BIOS I/O packet. This
|
||
arises due to an optimization added to the write code in
|
||
MS-DOS. It will only manifest on user WRITEs which are within
|
||
a sector size of 64K bytes on files which are "growing" past
|
||
the current EOF. IT IS ALLOWABLE FOR THE BIOS TO IGNORE
|
||
THE BALANCE OF THE WRITE WHICH "WRAPS AROUND" IF IT SO
|
||
CHOOSES. For instance a WRITE of 10000H bytes worth of
|
||
sectors with a transfer address of XXX:1 could ignore the
|
||
last two bytes (remember that a user program can never request
|
||
an I/O of more than FFFFH bytes and cannot wrap around (even
|
||
to 0) in his transfer segment, so in this case the last two
|
||
bytes can be ignored).
|
||
|
||
|
||
NON DESRUCTIVE READ NO WAIT - ES:BX ->
|
||
+------------------------------------+
|
||
| 13-BYTE Static Request Header |
|
||
+------------------------------------+
|
||
| BYTE read from device |
|
||
+------------------------------------+
|
||
|
||
This call is analogous to the console input status call
|
||
on MS-DOS 1.25. If the character device returns Busy bit
|
||
= 0 (characters in buffer), then the next character that
|
||
would be read is returned. This character is NOT removed
|
||
from the input buffer (hence the term Non Destructive Read).
|
||
In essence this call allows the DOS to look ahead one input
|
||
character.
|
||
|
||
|
||
MEDIA CHECK - ES:BX ->
|
||
+------------------------------------+
|
||
| 13-BYTE Static Request Header |
|
||
+------------------------------------+
|
||
| BYTE Media Descriptor from DPB |
|
||
+------------------------------------+
|
||
| BYTE returned |
|
||
+------------------------------------+
|
||
|
||
In addition to setting status word, driver must set the
|
||
return byte.
|
||
|
||
Return Byte :
|
||
-1 Media has been changed
|
||
0 Don't know if media has been changed
|
||
1 Media has not been changed
|
||
|
||
If the driver can return -1 or 1 (by having a door-lock
|
||
or other interlock mechanism) the performance of MSDOS 2.0
|
||
is enhanced as the DOS need not reread the FAT for each
|
||
directory access.
|
||
|
||
|
||
BUILD BPB - ES:BX ->
|
||
+------------------------------------+
|
||
| 13-BYTE Static Request Header |
|
||
+------------------------------------+
|
||
| BYTE Media Descriptor from DPB |
|
||
+------------------------------------+
|
||
| DWORD Transfer Address |
|
||
| (points to one sectors worth of |
|
||
| scratch space or first sector |
|
||
| of FAT depending on the value |
|
||
| of the NON IBM FORMAT bit) |
|
||
+------------------------------------+
|
||
| DWORD Pointer to BPB |
|
||
+------------------------------------+
|
||
|
||
If the NON IBM FORMAT bit of the device is set, then
|
||
the DWORD Transfer Address points to a one sector buffer
|
||
which can be used for any purpose. If the NON IBM FORMAT
|
||
bit is 0, then this buffer contains the first sector of the
|
||
FAT; in this case the driver must not alter this buffer (this
|
||
mode is useful if all that is desired is to read the FAT
|
||
ID byte).
|
||
|
||
If IBM compatible format is used (NON IBM FORMAT BIT
|
||
= 0), then it must be true that the first sector of the first
|
||
FAT is located at the same sector on all possible media.
|
||
This is because the FAT sector will be read BEFORE the media
|
||
is actually determined.
|
||
|
||
In addition to setting status word, driver must set the
|
||
Pointer to the BPB on return.
|
||
|
||
|
||
In order to allow for many different OEMs to read each
|
||
other's disks, the following standard is suggested: The
|
||
information relating to the BPB for a particular piece of
|
||
media is kept in the boot sector for the media. In
|
||
particular, the format of the boot sector is:
|
||
|
||
+------------------------------------+
|
||
| 3 BYTE near JUMP to boot code |
|
||
+------------------------------------+
|
||
| 8 BYTES OEM name and version |
|
||
---+------------------------------------+---
|
||
B | WORD bytes per sector |
|
||
P +------------------------------------+
|
||
B | BYTE sectors per allocation unit |
|
||
+------------------------------------+
|
||
| | WORD reserved sectors |
|
||
V +------------------------------------+
|
||
| BYTE number of FATs |
|
||
+------------------------------------+
|
||
| WORD number of root dir entries |
|
||
+------------------------------------+
|
||
| WORD number of sectors in logical |
|
||
^ | image |
|
||
| +------------------------------------+
|
||
B | BYTE media descriptor |
|
||
P +------------------------------------+
|
||
B | WORD number of FAT sectors |
|
||
---+------------------------------------+---
|
||
| WORD sectors per track |
|
||
+------------------------------------+
|
||
| WORD number of heads |
|
||
+------------------------------------+
|
||
| WORD number of hidden sectors |
|
||
+------------------------------------+
|
||
|
||
The three words at the end are optional, the DOS doesn't
|
||
care about them (since they are not part of the BPB). They
|
||
are intended to help the BIOS understand the media. Sectors
|
||
per track may be redundant (could be figured out from total
|
||
size of the disk). Number of heads is useful for supporting
|
||
different multi-head drives which have the same storage
|
||
capacity, but a different number of surfaces. Number of
|
||
hidden sectors is useful for supporting drive partitioning
|
||
schemes.
|
||
|
||
|
||
Currently, the media descriptor byte has been defined
|
||
for a small range of media:
|
||
|
||
5 1/4" diskettes:
|
||
|
||
Flag bits:
|
||
01h - on -> 2 double sided
|
||
|
||
All other bits must be on.
|
||
|
||
8" disks:
|
||
FEh - IBM 3740 format, singled-sided, single-density,
|
||
128 bytes per sector, soft sectored, 4 sectors
|
||
per allocation unit, 1 reserved sector, 2 FATs,
|
||
68 directory entries, 77*26 sectors
|
||
|
||
FDh - 8" IBM 3740 format, singled-sided,
|
||
single-density, 128 bytes per sector, soft
|
||
sectored, 4 sectors per allocation unit, 4
|
||
reserved sectors, 2 FATs, 68 directory entries,
|
||
77*26 sectors
|
||
|
||
FEh - 8" Double-sided, double-density, 1024 bytes
|
||
per sector, soft sectored, 1 sector per allocation
|
||
unit, 1 reserved sector, 2 FATs, 192 directory
|
||
entries, 77*8*2 sectors
|
||
|
||
|
||
STATUS Calls - ES:BX ->
|
||
+------------------------------------+
|
||
| 13-BYTE Static Request Header |
|
||
+------------------------------------+
|
||
|
||
All driver must do is set status word accordingly and
|
||
set the busy bit as follows:
|
||
|
||
o For output on character devices: If it is 1 on
|
||
return, a write request (if made) would wait for
|
||
completion of a current request. If it is 0, there
|
||
is no current request and a write request (if made)
|
||
would start immediately.
|
||
|
||
o For input on character devices with a buffer a return
|
||
of 1 means, a read request (if made) would go to
|
||
the physical device. If it is 0 on return, then
|
||
there are characters in the devices buffer and a
|
||
read would return quickly, it also indicates that
|
||
the user has typed something. The DOS assumes all
|
||
character devices have an input type ahead buffer.
|
||
Devices which don't have them should always return
|
||
busy = 0 so that the DOS won't hang waiting for
|
||
something to get into a buffer which doesn't exist.
|
||
|
||
|
||
FLUSH Calls - ES:BX ->
|
||
+------------------------------------+
|
||
| 13-BYTE Static Request Header |
|
||
+------------------------------------+
|
||
|
||
This call tells the driver to flush (terminate) all
|
||
pending requests that it has knowledge of. Its primary use
|
||
is to flush the input queue on character devices.
|
||
|
||
|
||
INIT - ES:BX ->
|
||
+------------------------------------+
|
||
| 13-BYTE Static Request Header |
|
||
+------------------------------------+
|
||
| BYTE # of units |
|
||
+------------------------------------+
|
||
| DWORD Break Address |
|
||
---+------------------------------------+---
|
||
| DWORD Pointer to BPB array |
|
||
| (not set by Character devices) |
|
||
+------------------------------------+
|
||
|
||
The number of units, break address, and BPB pointer are
|
||
set by the driver.
|
||
|
||
|
||
FORMAT OF BPB (Bios Parameter Block) -
|
||
|
||
+------------------------------------+
|
||
| WORD Sector size in Bytes |
|
||
| Must be at least 32 |
|
||
+------------------------------------+
|
||
| BYTE Sectors/Allocation unit |
|
||
| Must be a power of 2 |
|
||
+------------------------------------+
|
||
| WORD Number of reserved sectors |
|
||
| May be zero |
|
||
+------------------------------------+
|
||
| BYTE Number of FATS |
|
||
+------------------------------------+
|
||
| WORD Number of directory entries |
|
||
+------------------------------------+
|
||
| WORD Total number of sectors |
|
||
+------------------------------------+
|
||
| BYTE Media descriptor |
|
||
+------------------------------------+
|
||
| WORD Number of sectors occupied by |
|
||
| FAT |
|
||
+------------------------------------+
|
||
|
||
|
||
THE CLOCK DEVICE
|
||
|
||
One of the most popular add on boards seems to be "Real
|
||
Time CLOCK Boards". To allow these boards to be integrated
|
||
into the system for TIME and DATE, there is a special device
|
||
(determined by the attribute word) which is the CLOCK device.
|
||
In all respects this device defines and performs functions
|
||
like any other character device (most functions will be "set
|
||
done bit, reset error bit, return). When a read or write
|
||
to this device occurs, exactly 6 bytes are transferred. This
|
||
I/O can be thought of as transferring 3 words which correspond
|
||
exactly to the values of AX, CX and DX which were used in
|
||
the old 1.25 DOS date and time routines. Thus the first
|
||
two bytes are a word which is the count of days since 1-1-80.
|
||
The third byte is minutes, the fourth hours, the fifth
|
||
hundredths of seconds, and the sixth seconds. Reading the
|
||
CLOCK device gets the date and time, writing to it sets the
|
||
date and time.
|
||
|