mirror of https://github.com/AR1972/DOS3.3
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3558 lines
81 KiB
3558 lines
81 KiB
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
|
|
2.1 Introduction
|
|
|
|
The io.sys file comprises the "resident" device drivers, which form the
|
|
MS-DOS BIOS. These drivers are called upon by MS-DOS to handle
|
|
input/output (I/O) requests initiated by application programs.
|
|
|
|
One of the most powerful features of MS-DOS is the ability to add new
|
|
devices such as printers, plotters, and mouse input devices without rewrit-
|
|
ing the BIOS. The MS-DOS BIOS is configurable; that is, new drivers can
|
|
be added and existing drivers can be preempted. Nonresident, or install-
|
|
able, device drivers may be easily added at boot time by including a
|
|
device command line in the config.sys file.
|
|
|
|
At boot time, a minimum of five resident device drivers must be present.
|
|
These drivers are in a linked list: the header of each one contains a
|
|
DWORD pointer to the next. The last driver in the chain has an end-of-list
|
|
marker of -1, -1 (all bits on).
|
|
|
|
Each driver in the chain has two entry points: the strategy entry point and
|
|
the interrupt entry point. MS-DOS does not take advantage of the two
|
|
entry points: it calls the strategy routine, then immediately calls the inter-
|
|
rupt routine.
|
|
|
|
The dual entry points will accomodate future multitasking versions of
|
|
MS-DOS and MS OS/2 operating systems. In multitasking environments,
|
|
I/O must be asynchronous; to accomplish this, the strategy routine will be
|
|
called to (internally) queue a request and return quickly. It is then the
|
|
responsibility of the interrupt routine to perform the I/O at interrupt time
|
|
by getting requests from the internal queue and processing them. When a
|
|
request is completed, it is flagged as "done" by the interrupt routine.
|
|
MS-DOS periodically scans the list of requests looking for those that are
|
|
flagged as done, and "wakes up" the process waiting for the completion of
|
|
the request.
|
|
|
|
When requests are queued in this manner, it is no longer sufficient to pass
|
|
I/O information in registers, since many requests may be pending at any
|
|
time. Therefore, the MS-DOS device interface uses "packets" to pass
|
|
request information. These request packets are of variable size and for-
|
|
mat, and are composed of two parts:
|
|
|
|
1. The static request header section, which has the same format for
|
|
all requests
|
|
|
|
2. A section which has information specific to the type of request
|
|
|
|
A driver is called with a pointer to a packet. In multitasking versions, this
|
|
packet will be linked into a global chain of all pending I/O requests main-
|
|
tained by MS-DOS.
|
|
|
|
|
|
3
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
MS-DOS does not implement a global or local queue. Only one request is
|
|
pending at any one time. The strategy routine must store the address of
|
|
the packet at a fixed location, and the interrupt routine, which is called
|
|
immediately after the strategy routine, should process the packet by com-
|
|
pleting the request and returning. It is assumed that the request is com-
|
|
pleted when the interrupt routine returns.
|
|
|
|
To make a device driver that sysinit can install, a .bin (core image) or .exe
|
|
format file must be created with the device driver header at the beginning
|
|
of the file. The link field should be initialized to -1 (sysinit fills it in). Dev-
|
|
ice drivers which are part of the BIOS should have their headers point to
|
|
the next device in the list and the last header should be initialized to
|
|
-1,-1. The BIOS must be a .bin (core image) format file.
|
|
|
|
The .exe format installable device drivers may be used in non-IBM versions
|
|
of MS-DOS. On the IBM Personal Computer, the .exe loader is located in
|
|
command.com which is not present at the time that installable devices are
|
|
being loaded.
|
|
|
|
2.2 Format of a Device Driver
|
|
|
|
A device driver is a program segment responsible for communication
|
|
between DOS and the system hardware. It has a special header at the
|
|
beginning identifying it as a device driver, defining entry points, and
|
|
describing various attributes of the device.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
For device drivers, the file must not use the ORG 100H (like .com files).
|
|
Because it does not use the Program Segment Prefix (PSP), the device
|
|
driver is simply loaded; therefore, the file must have an origin of zero
|
|
(ORG 0 or no ORG statement).
|
|
|
|
_ ________________________________________________________________
|
|
|
|
There are two kinds of device drivers:
|
|
|
|
o Character device drivers
|
|
|
|
o Block device drivers
|
|
|
|
Character devices perform serial character I/O. Examples are the console,
|
|
communications port and printer. These devices are named (i.e., CON,
|
|
AUX, CLOCK, etc.), and programs may open channels (handles or file
|
|
control blocks) to do I/O to them.
|
|
|
|
|
|
4
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
Block devices include all the disk drives on the system. They can perform
|
|
random I/O in structured pieces called blocks (usually the physical sector
|
|
size). These devices are not named as the character devices are, and there-
|
|
fore cannot be opened directly. Instead they have unit numbers and are
|
|
identified by drive letters such as A, B, and C.
|
|
|
|
A single block device driver may be responsible for one or more logically
|
|
contiguous disk drives. For example, block device driver ALPHA may be
|
|
responsible for drives A, B, C, and D. This means that it has four units
|
|
defined (0-3), and therefore, takes up four drive letters. The position of
|
|
the driver in the list of all drivers determines which units correspond to
|
|
which driver letters. If driver ALPHA is the first block driver in the device
|
|
list, and it defines four 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. The theoretical limit is 63, but it should be
|
|
noted that the device installation code will not allow the installation of a
|
|
device if it would result in a drive letter greater than Z (5AH). All block
|
|
device drivers present in the standard resident BIOS will be placed ahead
|
|
of installable block device drivers in the list.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
Because they have only one name, character devices cannot define mul-
|
|
tiple units.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
|
|
2.3 How to Create a Device Driver
|
|
|
|
To create a device driver that MS-DOS can install, you must create a
|
|
binary file (.com or .exe format) with a device header at the beginning of
|
|
the file. Note that for device drivers, the code should not be originated at
|
|
100H, but at 0. The device header contains a link field (a pointer to the
|
|
next device header) which should be -1, unless there is more than one dev-
|
|
ice driver in the file. The attribute field and entry points must be set
|
|
correctly.
|
|
|
|
If it is a character device, the name field should be filled in with the name
|
|
of that character device. The name can be any legal eight-character
|
|
filename. If the name is less than eight characters, it should be padded out
|
|
to eight characters with spaces (20H). Note that device names do not
|
|
include colons (:). The fact that CON is the same as CON: is a property of
|
|
the default MS-DOS command interpreter (command.com) and not of the
|
|
device driver or the MS-DOS interface. All character device names are
|
|
handled in this way.
|
|
|
|
|
|
5
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
MS-DOS always processes installable device drivers before handling the
|
|
default devices, so to install a new CON device, simply name the device
|
|
CON. Remember to set the standard input device and standard output
|
|
device bits in the attribute word on a new CON device. The scan of the
|
|
device list stops on the first match, so the installable device driver takes
|
|
precedence.
|
|
|
|
It is not possible to replace the resident disk block device driver with an
|
|
installable device driver the same way you can replace the other device
|
|
drivers in the BIOS. Block drivers can be used only for devices not directly
|
|
supported by the default disk drivers in the io.sys file.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
Because MS-DOS can install the driver anywhere in memory, care must
|
|
be taken when making far memory references. You should not expect
|
|
that your driver will always be loaded in the same place every time.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
|
|
2.3.1 Device Strategy Routine
|
|
|
|
The device strategy routine, which is called by MS-DOS for each device
|
|
driver service request, is primarily responsible for queuing these requests in
|
|
the order in which they are to be processed by the device interrupt rou-
|
|
tine. Such queuing can be a very important performance feature in a mul-
|
|
titasking environment, or where asynchronous I/O is supported. As
|
|
MS-DOS does not currently support these facilities, only one request can
|
|
be serviced at a time, and this routine is usually very short. In the coding
|
|
examples in Section 2.12, "Two Sample Device Drivers," each request is
|
|
simply stored in a single pointer area.
|
|
|
|
2.3.2 Device Interrupt Routine
|
|
|
|
The device interrupt routine contains the code necessary to process the
|
|
service request. It may interface to the hardware, or it may use ROM
|
|
BIOS calls. It usually consists of a series of procedures that handle the
|
|
specific command codes to be supported as well as some exit and error-
|
|
handling routines. See the coding examples in Section 2.12, "Two Sample
|
|
Device Drivers."
|
|
|
|
|
|
6
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
|
|
2.4 Installing Device Drivers
|
|
|
|
MS-DOS allows new device drivers to be installed dynamically at boot
|
|
time. This is accomplished by initialization code in the io.sys file that
|
|
reads and processes the config.sys file.
|
|
|
|
MS-DOS calls upon the device drivers to perform their function in the fol-
|
|
lowing manner:
|
|
|
|
1. MS-DOS makes a FAR call to the strategy entry.
|
|
|
|
2. MS-DOS passes device driver information in a request header to
|
|
the strategy routine.
|
|
|
|
3. MS-DOS makes a FAR call to the interrupt entry.
|
|
|
|
This calling structure is designed to be easily upgraded to support any
|
|
future multitasking environment.
|
|
|
|
|
|
7
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
|
|
2.5 Device Headers
|
|
|
|
A device header is required at the beginning of a device driver. A device
|
|
header looks like this:
|
|
|
|
+--------------------------------------+
|
|
| DWORD Pointer to next device |
|
|
| (Usually set to -1 if this driver |
|
|
| is the last or only driver in the |
|
|
| file) |
|
|
+--------------------------------------+
|
|
| WORD Attributes |
|
|
+--------------------------------------+
|
|
| 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. |
|
|
+--------------------------------------+
|
|
|
|
|
|
|
|
Figure 2.1 Sample Device Header
|
|
|
|
Note that the device entry points are words. They must be offsets from the
|
|
same segment number used to point to this table. For example, if xxx:yyy
|
|
points to the start of this table, then xxx:strategy and xxx:interrupt are the
|
|
entry points.
|
|
|
|
The device header fields are described in the following section.
|
|
|
|
2.5.1 Pointer to Next Device Field
|
|
|
|
The pointer to the next device header field is a double-word field (offset
|
|
followed by segment) that is set by MS-DOS to point at the next driver in
|
|
the system list at the time the device driver is loaded. It is important that
|
|
this field be set to -1 prior to load (when it is on the disk as a file) unless
|
|
there is more than one device driver in the file. If there is more than one
|
|
driver in the file, the first word of the double-word pointer should be the
|
|
offset of the next driver's device header.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
|
|
8
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
If there is more than one device driver in the file, the last driver in the
|
|
file must have the pointer to the next device header field set to -1.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
|
|
2.5.2 Attribute Field
|
|
|
|
The attribute field is used to identify the type of device for which this
|
|
driver is responsible. In addition to distinguishing between block and
|
|
character devices, these bits are used to give selected character devices
|
|
special treatment. (Note that if a bit in the attribute word is defined only
|
|
for one type of device, a driver for the other type of device must set that
|
|
bit to 0.)
|
|
|
|
|
|
Table 2.1
|
|
|
|
For Character Devices:
|
|
|
|
_ _________________________________________________________________________
|
|
|
|
Bit Value Meaning
|
|
|
|
_ _________________________________________________________________________
|
|
|
|
0 1 Device is console input (sti) device
|
|
1 1 Device is console output (sto) device
|
|
2 1 Device is nul device
|
|
3 1 Device is clock device
|
|
4-5 Reserved (must be 0)
|
|
6 1 Device supports 3.2 functions
|
|
7-10 Reserved (must be 0)
|
|
11 1 Device understands Open/Close
|
|
12 Reserved (must be 0)
|
|
13 1 Device supports Output Until Busy (OUB)
|
|
14 1 Device supports IOCtl control strings
|
|
15 1 Character device
|
|
|
|
_ _________________________________________________________________________
|
|
|
|
Table 2.2
|
|
|
|
For Block Devices:
|
|
|
|
_ _________________________________________________________________________
|
|
|
|
Bit Value Meaning
|
|
|
|
_ _________________________________________________________________________
|
|
|
|
0-5 Reserved (must be 0)
|
|
6 1 Device supports 3.2 functions and Generic IOCtl function calls
|
|
7-10 Reserved (must be 0)
|
|
11 1 Device understands Open/Close/Removable Media
|
|
12 Reserved (must be 0)
|
|
13 1 Device determines the media by examining the FATID byte
|
|
14 1 Device supports IOCtl control strings
|
|
15 0 Block device
|
|
|
|
_ _________________________________________________________________________
|
|
|
|
|
|
9
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
For example, assume that you have a new device driver that you want to
|
|
use as the standard input and output. In addition to installing the driver,
|
|
you must tell MS-DOS that you want the new driver to override the
|
|
current standard input and standard output (the CON device). This is
|
|
accomplished by setting the attributes to the desired characteristics, so
|
|
you 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. (Refer to
|
|
Section 2.10, "The Clock Device," in this chapter for more information.)
|
|
Although there is a NUL device attribute, the NUL device cannot be reas-
|
|
signed. This attribute exists so that MS-DOS can determine if the NUL
|
|
device is being used.
|
|
|
|
Bit 13 for block devices affects the operation of the Build BPB (BIOS
|
|
Parameter Block) device call. If set, it requires the first sector of the FAT
|
|
always to reside in the same place. This bit has a different meaning on
|
|
character devices. It indicates that the device implements the Output
|
|
Until Busy device call.
|
|
|
|
The IOCtl bit (bit 14) has meaning on character and block devices. The
|
|
IOCtl functions allow data to be sent and received by the device for its
|
|
own use (to set baud rate, stop bits, form length, etc.) instead of passing
|
|
data over the device channel as a normal read or write does. The interpre-
|
|
tation of the passed information is up to the device but it must not be
|
|
treated as normal I/O. This bit tells MS-DOS whether the device can han-
|
|
dle control strings by using the IOCtl system call, Function 44H.
|
|
|
|
If a driver cannot process control strings, it should initially set this bit to
|
|
0. This tells MS-DOS to return an error if an attempt is made (via Func-
|
|
tion 44H) to send or receive control strings to this device. A device which
|
|
can process control strings should initialize the IOCtl bit to 1. For drivers
|
|
of this type, MS-DOS will make calls to the IOCtl input and output device
|
|
functions to send and receive IOCtl strings.
|
|
|
|
The IOCtl functions allow data to be sent and received by the device for
|
|
its own use (for example, to set baud rate, stop bits, and form length),
|
|
instead of passing data over the device channel as does a normal read or
|
|
write. The interpretation of the passed information is up to the device, but
|
|
it must not be treated as a normal I/O request.
|
|
|
|
The Open/Close/Removable Media bit (bit 11) signals to MS-DOS 3.x
|
|
and later versions whether this driver supports additional MS-DOS 3.x
|
|
functionality. To support these old drivers, it is necessary to detect them.
|
|
This bit was reserved in MS-DOS 2.x, and is 0. All new devices should
|
|
support the Open, Close, and Removable Media calls and set this bit
|
|
to 1. Since MS-DOS 2.x never makes these calls, the driver will be
|
|
backward-compatible.
|
|
|
|
|
|
|
|
10
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
The MS-DOS 3.2 bit (bit 6) signals whether the device supports logical
|
|
drive mapping via Function 440EH (Get Logical Drive Map) and Function
|
|
440FH (Set Logical Drive Map). This bit also supports generic IOCtl func-
|
|
tions via Function 440CH (Generic IOCtl for Handles) and Function
|
|
440DH (Generic IOCtl for Block Devices).
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---------------------------------------------------------------+
|
|
| C | I | | | O | | | | | 3 | | | C | N | S | S |
|
|
| H | O | | | P | | | | | . | | | L | U | T | T |
|
|
| R | C | | | N | | | | | 2 | | | K | L | O | I |
|
|
+---------------------------------------------------------------+
|
|
|
|
|
|
Figure 2.2 Attribute Word for Character Devices
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---------------------------------------------------------------+
|
|
| | I | F | | O | | | | | 3 | | | | | | |
|
|
| | O | A | | P | | | | | . | | | | | | |
|
|
| | C | T | | N | | | | | 2 | | | | | | |
|
|
+---------------------------------------------------------------+
|
|
|
|
|
|
Figure 2.3 Attribute Word for Block Devices
|
|
|
|
|
|
2.5.3 Strategy and Interrupt Routines
|
|
|
|
These two fields are the pointers to the entry points of the strategy and
|
|
interrupt routines. They are word values, so they must be in the same seg-
|
|
ment as the device header.
|
|
|
|
2.5.4 Name Field
|
|
|
|
This is an eight-byte field that contains the name of a character device or
|
|
the number of units of a block device. If the field refers to a block device,
|
|
the number of units can be put in the first byte. This is optional, because
|
|
MS-DOS will fill in this location with the value returned by the driver's
|
|
Init code. For more information, see Section 2.4, "Installing Device
|
|
Drivers."
|
|
|
|
2.6 Request Header
|
|
|
|
When MS-DOS calls a device driver to perform a function, it passes a
|
|
request header in ES:BX to the strategy entry point. This is a fixed length
|
|
header, followed by data pertinent to the operation being performed.
|
|
Note that it is the device driver's responsibility to preserve the machine
|
|
|
|
11
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
state (for example, save all registers, including flags, on entry, and restore
|
|
them on exit). There is enough room on the stack when the strategy or
|
|
interrupt routine is called to do about 20 pushes. If more room on the
|
|
stack is needed, the driver should set up its own stack.
|
|
|
|
The following figure illustrates a request header.
|
|
|
|
REQUEST HEADER ->
|
|
+-----------------------------+
|
|
| BYTE Length of record |
|
|
| Length in bytes of this |
|
|
| request header |
|
|
+-----------------------------+
|
|
| BYTE Unit code |
|
|
| The subunit the operation |
|
|
| is for (minor device) |
|
|
| (no meaning on character |
|
|
| devices) |
|
|
+-----------------------------+
|
|
| BYTE Command code |
|
|
+-----------------------------+
|
|
| WORD Status |
|
|
+-----------------------------+
|
|
| 8 BYTES Reserved |
|
|
| |
|
|
|-----------------------------|
|
|
|
|
|
|
Figure 2.4 Request Header
|
|
|
|
The request header fields are described below.
|
|
|
|
2.6.1 Length of Record
|
|
|
|
This field contains the length (in bytes) of the request header.
|
|
|
|
2.6.2 Unit Code Field
|
|
|
|
The unit code field identifies which unit in your device driver the request is
|
|
for. For example, if your device driver has three units defined, then the
|
|
possible values of the unit code field would be 0, 1, and 2.
|
|
|
|
2.6.3 Command Code Field
|
|
|
|
The command code field in the request header can have the following
|
|
values:
|
|
|
|
Code
|
|
Function
|
|
_ ________________________________________________________________
|
|
|
|
12
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
0 Init
|
|
|
|
1 Media Check (Block devices only)
|
|
|
|
2 Build BPB (Block devices only)
|
|
|
|
3 IOCtl Input (Only called if device has IOCtl)
|
|
|
|
4 Input (Read)
|
|
|
|
5 Non-destructive Read, No Wait (Character devices only)
|
|
|
|
6 Input Status (Character devices only)
|
|
|
|
7 Input Flush (Character devices only)
|
|
|
|
8 Output (Write)
|
|
|
|
9 Output (Write) with Verify
|
|
|
|
10 Output Status (Character devices only)
|
|
|
|
11 Output Flush (Character devices only)
|
|
|
|
12 IOCtl Output (Only called if device has IOCtl)
|
|
|
|
13 Device Open (Only called if Open/Close/Removable Media
|
|
bit set)
|
|
|
|
14 Device Close (Only called if Open/Close/Removable Media
|
|
bit set)
|
|
|
|
15 Removable Media (Only called if Open/Close/Removable
|
|
Media bit set and device is block)
|
|
|
|
16 Output Until Busy (Only called if bit 13 is set on character dev-
|
|
ices)
|
|
|
|
19 Generic IOCtl Request
|
|
|
|
23 Get Logical Device
|
|
|
|
24 Set Logical Device
|
|
|
|
|
|
2.6.4 Status Field
|
|
|
|
The following figure illustrates the status field in the request header.
|
|
|
|
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 | | S | N | |
|
|
| | | Y | E | |
|
|
+---+---+--+--+--+--+---+---+--+--+--+--+--+--+--+--+
|
|
|
|
The status word is zero on entry and is set by the driver interrupt routine
|
|
on return.
|
|
|
|
|
|
13
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
Bit 8 is the done bit. When set, it means the operation has completed. The
|
|
driver sets it to 1 when it exits.
|
|
|
|
Bit 15 is the error bit. If it is set, then the low eight bits indicate the
|
|
error. The errors are as follows:
|
|
|
|
Error
|
|
Meaning
|
|
_ ________________________________________________________________
|
|
|
|
0 Write protect violation
|
|
|
|
1 Unknown unit
|
|
|
|
2 Drive not ready
|
|
|
|
3 Unknown command
|
|
|
|
4 CRC error
|
|
|
|
5 Bad drive request structure length
|
|
|
|
6 Seek error
|
|
|
|
7 Unknown media
|
|
|
|
8 Sector not found
|
|
|
|
9 Printer out of paper
|
|
|
|
A Write fault
|
|
|
|
B Read fault
|
|
|
|
C General failure
|
|
|
|
D Reserved
|
|
|
|
E Reserved
|
|
|
|
F Invalid disk change
|
|
|
|
Bit 9 is the busy bit, which is set only by Status calls and the
|
|
Removable Media call.
|
|
|
|
2.7 Device Driver Functions
|
|
|
|
Device drivers may perform all or some of these general functions. In some
|
|
cases, these functions break down into several command codes, for specific
|
|
cases. Each of the following general functions is described in this section.
|
|
|
|
o Init
|
|
|
|
o Media Check
|
|
|
|
|
|
|
|
14
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
o Build BPB
|
|
|
|
o Read, or Write, or Write Until Busy, or Write with Verify, or
|
|
Read IOCtl, or Write IOCtl
|
|
|
|
o Non-destructive Read, No Wait
|
|
|
|
o Open or Close (3.x)
|
|
|
|
o Removable Media (3.x)
|
|
|
|
o Status
|
|
|
|
o Flush
|
|
|
|
o Generic IOCtl
|
|
|
|
o Get or Set Logical Device
|
|
|
|
All strategy routines are called with ES:BX pointing to the request header.
|
|
The interrupt routines get the pointers to the request header from the
|
|
queue that the strategy routines store them in. The command code in the
|
|
request header tells the driver which function to perform and what data
|
|
follows the request header.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
All DWORD pointers are stored offset first, then segment.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
|
|
2.7.1 The Init Function
|
|
|
|
Command code = 0
|
|
|
|
INIT - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Request header |
|
|
+------------------------------------+
|
|
| BYTE Number of units |
|
|
+------------------------------------+
|
|
| DWORD End Address |
|
|
+------------------------------------+
|
|
| DWORD Pointer to BPB array |
|
|
| (Not set by character devices) |
|
|
+------------------------------------+
|
|
| BYTE Block device number |
|
|
+------------------------------------+
|
|
|
|
One of the functions defined for each device driver is Init. This routine is
|
|
called only once when the device is installed. The Init routine must return
|
|
the end address, which is a DWORD pointer to the end of the portion of
|
|
the device driver to remain resident. To save space, you can use this
|
|
|
|
15
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
pointer method to delete initialization code that is needed only once.
|
|
|
|
The number of units, end address, and BPB pointer are to be set by the
|
|
driver. However, on entry for installable device drivers, the DWORD that
|
|
is to be set by the driver to the BPB array (on block devices) points to the
|
|
character after the "=" on the line in config.sys that caused this device
|
|
driver to be loaded. This allows drivers to scan the config.sys invocation
|
|
line for parameters that might be passed to the driver. This line is ter-
|
|
minated by a RETURN or a linefeed character. This data is read-only and
|
|
allows the device to scan the config.sys command line for arguments.
|
|
|
|
device=\dev\vt52.sys /l
|
|
|
|
|
|_____BPB address points here
|
|
|
|
Also, for block devices only, the drive number assigned to the first unit
|
|
defined by this driver (A=0) as contained in the block device number field.
|
|
This is also read-only.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
The Init routine can issue only Functions 01H-0CH, 25H, 30H, and
|
|
35H.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
For installable character devices, the end address parameter must be
|
|
returned. This is a pointer to the first available byte of memory above the
|
|
driver and may be used to throw away initialization code.
|
|
|
|
Block devices must return the following information:
|
|
|
|
1. The number of units must be returned. MS-DOS uses this number
|
|
to determine 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 the position of the
|
|
driver in the device list, and by the number of units on the device
|
|
(stored in the first byte of the device name field).
|
|
|
|
2. A DWORD pointer to an array of word offsets (pointers) to BPBs
|
|
(BIOS Parameter Blocks) must be returned. The BPBs passed by
|
|
the device driver are used by MS-DOS to create an internal struc-
|
|
ture. There must be one entry in this array for each unit defined
|
|
by the device driver. In this way, if all units are the same, all the
|
|
pointers can point to the same BPB, saving space. If the device
|
|
driver defines two units, then the DWORD pointer points to the
|
|
first of two one-word offsets which in turn point to BPBs. The for-
|
|
mat of the BPB is described later in this chapter in Section 2.7.3,
|
|
|
|
16
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
"The Build BPB Function."
|
|
|
|
Note that this array of word offsets must be protected (below the
|
|
free pointer set by the return), since an internal DOS structure will
|
|
be built starting at the byte pointed to by the free pointer. The
|
|
defined sector size must be less than or equal to the maximum sec-
|
|
tor size defined by the resident device drivers (BIOS) during initial-
|
|
ization. If it isn't, the installation will fail.
|
|
|
|
3. The last thing that the Init function of a block device must pass
|
|
back is the media descriptor byte. This byte means nothing to
|
|
MS-DOS, but is passed to devices so that they know what parame-
|
|
ters MS-DOS is currently using for a particular drive.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
If there are multiple device drivers in a single file, MS-DOS uses the
|
|
ending address returned by the last Init function called. All device
|
|
drivers in a single file should return the same ending address. All dev-
|
|
ices in a single file should be grouped together low in memory with the
|
|
initialization code for all devices following it in memory.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
|
|
2.7.2 The Media Check Function
|
|
|
|
Command Code = 1
|
|
|
|
MEDIA CHECK - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Request header |
|
|
+------------------------------------+
|
|
| BYTE Media descriptor from BPB |
|
|
+------------------------------------+
|
|
| BYTE Returned |
|
|
+------------------------------------+
|
|
| Returned DWORD pointer to previous |
|
|
| Volume ID if bit 11 set and |
|
|
| Disk Changed is returned |
|
|
+------------------------------------+
|
|
|
|
The Media Check function is used only with block devices. It is called
|
|
when there is a pending drive-access call other than a file read or write,
|
|
such as Open, Close, delete, and rename. Its purpose is to determine
|
|
whether the media in the drive has been changed. If the driver can assure
|
|
that the media has not been changed (through a door-lock or other inter-
|
|
lock mechanism), MS-DOS performance is enhanced, because MS-DOS
|
|
does not need to reread the FAT and invalidate in-memory buffers for each
|
|
directory access.
|
|
|
|
|
|
17
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
When a disk-access call to the DOS occurs (other than a file read or write),
|
|
the following sequence of events takes place:
|
|
|
|
1. The DOS converts the drive letter into a unit number of a particu-
|
|
lar block device.
|
|
|
|
2. The device driver is then called to request a media check on that
|
|
subunit to see if the disk might have been changed. MS-DOS
|
|
passes the old media descriptor byte. The driver returns one of the
|
|
following:
|
|
|
|
|
|
Return
|
|
Meaning
|
|
_ _________________________________________________________
|
|
|
|
(1) Media not changed
|
|
|
|
(0) Don't know if changed
|
|
|
|
(-1) Media changed
|
|
|
|
value Error (value is a standard error code value)
|
|
|
|
If the media has not been changed, MS-DOS proceeds with the disk
|
|
access.
|
|
|
|
If the value returned is -1, then if there are any disk sectors that
|
|
have been modified and not written back out to the disk for this
|
|
unit, MS-DOS assumes that the disk has not been changed and
|
|
proceeds. MS-DOS invalidates any other buffers for the unit and
|
|
does a Build BPB call (see Step 3, following).
|
|
|
|
If the media has been changed, MS-DOS invalidates all buffers
|
|
associated with this unit including buffers with modified data that
|
|
are waiting to be written, and requests a new BIOS Parameter
|
|
Block via the Build BPB call (see Step 3).
|
|
|
|
3. Once the BPB has been returned, MS-DOS corrects its internal
|
|
structure for the drive from the new BPB and proceeds with the
|
|
access after reading the directory and the FAT.
|
|
|
|
Note that the previous media ID byte is passed to the device driver. If the
|
|
old media ID byte is the same as the new one, the disk might have been
|
|
changed and a new disk may be in the drive; therefore, all FAT, directory,
|
|
and data sectors that are buffered in memory for the drive are considered
|
|
invalid.
|
|
|
|
If the driver has bit 11 of the device attribute word set to 1, and the driver
|
|
returns -1 (Media Changed) the driver must set the DWORD pointer to
|
|
the previous Volume ID field. If the DOS determines that "Media changed"
|
|
is an error based on the state of the DOS buffer cache, the DOS will gen-
|
|
erate a 0FH error on behalf of the device. If the driver does not implement
|
|
volume ID support, but has bit 11 set, (it should set a static pointer to the
|
|
string "NO NAME" ,0.)
|
|
|
|
|
|
|
|
18
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
It is not possible for a user to change a disk in less than two seconds. So
|
|
when Media Check occurs within two seconds of a disk access, the driver
|
|
reports "Media not changed (1)." This improves performance tremen-
|
|
dously.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
Note
|
|
|
|
If the media ID byte in the returned BPB is the same as the previous
|
|
media ID byte, MS-DOS will assume that the format of the disk is the
|
|
same (even though the disk may have been changed) and will skip the
|
|
step of updating its internal structure. Therefore, all BPBs must have
|
|
unique media bytes regardless of FAT ID bytes.
|
|
|
|
_ ________________________________________________________________
|
|
|
|
|
|
2.7.3 The Build BPB Function
|
|
|
|
Command code = 2
|
|
|
|
BUILD BPB - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Request header |
|
|
+------------------------------------+
|
|
| BYTE Media descriptor from BPB |
|
|
+------------------------------------+
|
|
| DWORD Transfer address |
|
|
| (Points to one sector worth of |
|
|
| scratch space or first sector |
|
|
| of FAT depending on the value |
|
|
| of Bit 13 in the device attribute |
|
|
| word.) |
|
|
+------------------------------------+
|
|
| DWORD Pointer to BPB |
|
|
+------------------------------------+
|
|
|
|
The Build BPB function is used with block devices only. As described in
|
|
the Media Check function, the Build BPB function will be called any
|
|
time that a preceding Media Check call indicates that the disk has been
|
|
or might have been changed. The device driver must return a pointer to a
|
|
BPB. This is different from the Init call where the device driver returns a
|
|
pointer to an array of word offsets to BPBs.
|
|
|
|
The Build BPB call gets a DWORD pointer to a one-sector buffer. The
|
|
contents of this buffer are determined by the non-FAT ID bit (bit 13) in
|
|
the attribute field. If the bit is zero, then the buffer contains the first sec-
|
|
tor of the first FAT. The FAT ID byte is the first byte of this buffer. In
|
|
this case, the driver must not alter this buffer. Note that the location of
|
|
the FAT must be the same for all possible media because this first FAT
|
|
sector must be read before the actual BPB is returned. If the non-FAT ID
|
|
|
|
19
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
bit is set, the pointer points to one sector of scratch space (which may be
|
|
used for anything). For information on how to construct the BPB, see Sec-
|
|
tion 2.8, "The Media Descriptor Byte," and Section 2.9, "Format of a
|
|
Media Descriptor Table."
|
|
|
|
MS-DOS 3.x includes additional support for devices that have door-locks
|
|
or some other means of telling when a disk has been changed. There is a
|
|
new error that can be returned from the device driver (error 15). The
|
|
error means "the disk has been changed when it shouldn't have been," and
|
|
the user is prompted for the correct disk using a volume ID. The driver
|
|
may generate this error on read or write. The DOS may generate the error
|
|
on Media Check calls if the driver reports media changed, and there are
|
|
buffers in the DOS buffer cache that need to be flushed to the previous
|
|
disk.
|
|
|
|
For drivers that support this error, the Build BPB function is a trigger
|
|
that causes a new volume ID to be read off the disk. This action indicates
|
|
that the disk has been legally changed. A volume ID is placed on a disk by
|
|
the format command, and is simply an entry in the root directory of the
|
|
disk that has the Volume ID attribute. It is stored by the driver as an
|
|
ASCIZ string.
|
|
|
|
The requirement that the driver return a volume ID does not exclude some
|
|
other volume identifier scheme as long as the scheme uses ASCIZ strings. A
|
|
NUL (nonexistent or unsupported) volume ID is by convention the follow-
|
|
ing string:
|
|
|
|
DB "NO NAME ",0
|
|
|
|
|
|
2.7.4 The Read or Write Function
|
|
|
|
Command codes = 3,4,8,9,12, and 16
|
|
|
|
READ OR WRITE (Including IOCtl) or
|
|
OUTPUT UNTIL BUSY - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Request header |
|
|
+------------------------------------+
|
|
| BYTE Media descriptor from BPB |
|
|
+------------------------------------+
|
|
| DWORD Transfer address |
|
|
+------------------------------------+
|
|
| WORD Byte/sector count |
|
|
+------------------------------------+
|
|
| WORD Starting sector number |
|
|
| (Ignored on character devices) |
|
|
+------------------------------------+
|
|
| Returned DWORD pointer to requested|
|
|
| Volume ID if error 0FH |
|
|
+------------------------------------+
|
|
|
|
|
|
20
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
Command
|
|
code
|
|
Request
|
|
_ ________________________________________________________________
|
|
|
|
3 IOCtl read
|
|
|
|
4 Read (block or character device)
|
|
|
|
8 Write (block or character device)
|
|
|
|
9 Write with Verify
|
|
|
|
12 IOCtl Write
|
|
|
|
16 Output Until Busy (character device only)
|
|
|
|
The driver must perform the Read or Write call depending on which
|
|
command code is set. Block devices read or write sectors; character devices
|
|
read or write bytes.
|
|
|
|
When I/O completes, the device driver must set the status word and
|
|
report the number of sectors or bytes successfully transferred. This should
|
|
be done even if an error prevented the transfer from being completed. Set-
|
|
ting the error bit and error code alone is not sufficient.
|
|
|
|
In addition to setting the status word, the driver must set the sector count
|
|
to the actual number of sectors (or bytes) transferred. No error check is
|
|
performed on an IOCtl I/O call. The device driver must always set the
|
|
return byte/sector count to the actual number of bytes/sectors success-
|
|
fully transferred.
|
|
|
|
If the verify switch is on, the device driver will be called with command
|
|
code 9 (Write with Verify). Your device driver will be responsible for
|
|
verifying the write.
|
|
|
|
If the driver returns error code 0FH (Invalid disk change), it must return a
|
|
DWORD pointer to an ASCIZ string (which is the correct volume ID).
|
|
Returning this error code triggers the DOS to prompt the user to re-insert
|
|
the disk. The device driver should have read the volume ID as a result of
|
|
the Build BPB function.
|
|
|
|
Drivers may maintain a reference count of open files on the disk by moni-
|
|
toring the Open and Close functions. This allows the driver to determine
|
|
when to return error 0FH. If there are no open files (reference count = 0),
|
|
and the disk has been changed, the I/O is okay. If there are open files,
|
|
however, an 0FH error may exist.
|
|
|
|
The Output Until Busy call is a speed optimization on character devices
|
|
only for print spoolers. The device driver is expected to output all the
|
|
characters possible until the device returns busy. Under no circumstances
|
|
should the device driver block during this function. Note that it is not an
|
|
error for the device driver to return the number of bytes output as being
|
|
less than the number of bytes requested (or = 0).
|
|
|
|
21
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
The Output Until Busy call allows spooler programs to take advantage
|
|
of the "burst" behavior of most printers. Many printers have on-board
|
|
RAM buffers that typically hold a line or a fixed amount of characters.
|
|
These buffers fill up without the printer going busy, or going busy for a
|
|
short period (less than ten instructions) between characters. A line of
|
|
characters can be quickly output to the printer, after which the printer is
|
|
busy for a long time while the characters are being printed. This new dev-
|
|
ice call allows background spooling programs to use this burst behavior
|
|
efficiently. Rather than take the overhead of a device driver call for each
|
|
character, or risk getting stuck in the device driver outputting a block of
|
|
characters, this call allows a burst of characters to be output without the
|
|
device driver having to wait for the device to be ready.
|
|
|
|
The Following Applies to Block Device Drivers:
|
|
|
|
Under certain circumstances, the BIOS may be asked to perform a write
|
|
operation of 64K bytes, which seems to be a "wrap-around" of the transfer
|
|
address in the BIOS I/O packet. This request, which arises due to an
|
|
optimization added to the write code in MS-DOS, will manifest itself only
|
|
on user writes that are within a sector size of 64K bytes on files "growing"
|
|
past the current end-of-file (EOF) mark. It is allowable for the BIOS to
|
|
ignore the balance of the write that "wraps around" if it so chooses. For
|
|
example, a write of 10000H bytes worth of sectors with a transfer address
|
|
of xxx:1 could ignore the last two bytes. A user program can never request
|
|
an I/O of more than FFFFH bytes and cannot wrap around (even to 0) in
|
|
the transfer segment. Therefore, in this case, the last two bytes can be
|
|
ignored.
|
|
|
|
MS-DOS maintains two FATs. If the DOS has problems reading the first,
|
|
it automatically tries the second before reporting the error. The BIOS is
|
|
responsible for all retries.
|
|
|
|
Although the command.com handler does no automatic retries, there are
|
|
applications that have their own Interrupt 24H handlers that do
|
|
automatic retries on certain types of Interrupt 24H errors before reporting
|
|
them.
|
|
|
|
2.7.5 The Non-destructive Read, No Wait Function
|
|
|
|
Command code = 5
|
|
|
|
NON-DESTRUCTIVE READ NO WAIT - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Request header |
|
|
+------------------------------------+
|
|
| BYTE Read from device |
|
|
+------------------------------------+
|
|
|
|
The Non-destructive Read, No Wait function allows MS-DOS to look
|
|
|
|
22
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
ahead one input character. The device sets the done bit in the status
|
|
word.
|
|
|
|
If the character device returns busy bit = 0 (there are characters in the
|
|
buffer), then the next character that would be read is returned. This char-
|
|
acter is not removed from the input buffer (hence the term "Non-
|
|
destructive Read"). If the character device returns busy bit = 1, there are
|
|
no characters in the buffer.
|
|
|
|
2.7.6 The Open or Close Function
|
|
|
|
Command codes = 13 and 14
|
|
|
|
OPEN or CLOSE - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Static request header |
|
|
+------------------------------------+
|
|
|
|
The Open and Close functions are called by MS-DOS 3.x only if the dev-
|
|
ice driver sets the Open/Close/Removable Media attribute bit in the
|
|
device header. They are designed to inform the device about current file
|
|
activity on the device. On block devices, they can be used to manage local
|
|
buffering. The device can keep a reference count. Every Open causes the
|
|
device to increment the count, every Close to decrement. When the count
|
|
goes to zero, it means there are no open files on the device, and the device
|
|
should flush any buffers that have been written to that may have been
|
|
used inside the device, because it is now "legal" for the user to change the
|
|
media on a removable media drive.
|
|
|
|
There are problems with this mechanism on block devices because pro-
|
|
grams that use FCB calls can open files without closing them. It is there-
|
|
fore advisable to reset the count to zero without flushing the buffers when
|
|
the answer to "Has the media been changed?" is yes, and the Build BPB
|
|
call is made to the device.
|
|
|
|
These calls are more useful on character devices. The Open call, for
|
|
instance, can be used to send a device initialization string. On a printer,
|
|
this could cause a string for setting font and page size characteristics to be
|
|
sent to the printer so that it would always be in a known state at the start
|
|
of an I/O stream. Using IOCtl to set these pre- and post- strings provides
|
|
a flexible mechanism of serial I/O device stream control. The reference
|
|
count mechanism can also be used to detect a simultaneous access error. It
|
|
may be desirable to disallow more than one Open on a device at any given
|
|
time. In this case, a second Open would result in an error.
|
|
|
|
Note that since all processes have access to stdin, stdout, stderr, stdaux,
|
|
and stdprn (handles 0,1,2,3,4), the CON, AUX, and PRN devices are
|
|
always open.
|
|
|
|
|
|
23
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
|
|
2.7.7 The Removable Media Function
|
|
|
|
Command code = 15
|
|
|
|
REMOVABLE MEDIA - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Static request header |
|
|
+------------------------------------+
|
|
|
|
The Removable Media function is called by MS-DOS 3.x only if the dev-
|
|
ice driver sets the Open/Close/Removable Media attribute bit in the
|
|
device header. This call is given only to block devices by a subfunction of
|
|
the IOCtl system call. It is sometimes desirable for a utility to know
|
|
whether it is dealing with a nonremovable media drive (such as a hard
|
|
disk), or a removable media drive (like a floppy). An example is the
|
|
format command, which prints different versions of some of the prompts.
|
|
|
|
The information is returned in the busy bit of the status word. If the busy
|
|
bit is 1, then the media is nonremovable. If the busy bit is 0, then the
|
|
media is removable. Note that the error bit is not checked. It is assumed
|
|
that this call always succeeds.
|
|
|
|
2.7.8 The Status Function
|
|
|
|
Command codes = 6 and 10
|
|
|
|
STATUS Calls ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Request header |
|
|
+------------------------------------+
|
|
|
|
The Status function returns information to the DOS as to whether data is
|
|
waiting for input or output. All the driver must do is set the status word
|
|
and the busy bit as follows:
|
|
|
|
o For output on character devices \(em if the driver sets bit 9 to 1 on
|
|
return, it informs the DOS that 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 immedi-
|
|
ately.
|
|
|
|
o For input on character devices with a buffer \(em A return of 1 implies
|
|
that no characters are buffered and that a read request (if made)
|
|
would go to the physical device. If it is 0 on return, then there are
|
|
characters in the device buffer and a read would not be blocked. A
|
|
return of 0 implies that the user has typed something. MS-DOS
|
|
assumes that all character devices have an input type-ahead buffer.
|
|
|
|
Devices that do not have a type-ahead buffer should always return busy =
|
|
0 so that the DOS will not "hang" waiting for something to get into a
|
|
|
|
24
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
non-existent buffer.
|
|
|
|
2.7.9 The Flush Function
|
|
|
|
Command codes = 7 and 11
|
|
|
|
FLUSH Calls - ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Request header |
|
|
+------------------------------------+
|
|
|
|
The Flush function tells the driver to flush (terminate) all pending
|
|
requests. This call is used to flush the input queue on character devices.
|
|
|
|
The device driver performs the flush function, sets the status word, and
|
|
returns.
|
|
|
|
2.7.10 The Generic IOCtl Function
|
|
|
|
Command code = 19
|
|
|
|
ES:BX ->
|
|
+------------------------------------+
|
|
| 13-BYTE Static request header |
|
|
+------------------------------------+
|
|
| BYTE Category (Major) code |
|
|
+------------------------------------+
|
|
| BYTE Function (Minor) code |
|
|
+------------------------------------+
|
|
| WORD (SI) Contents |
|
|
+------------------------------------+
|
|
| WORD (DI) Contents |
|
|
+------------------------------------+
|
|
| DWORD Pointer to data buffer |
|
|
+------------------------------------+
|
|
|
|
The Generic IOCtl function provides a generic, expandable IOCtl facility
|
|
that replaces and makes the Read IOCtl and Write IOCtl device driver
|
|
functions obsolete. The MS-DOS 2.0 IOCtl functions remain to support
|
|
existing uses of the IOCtl system call (Subfunctions 2, 3, 4, and 5), but
|
|
new device drivers should use this generic MS-DOS IOCtl facility.
|
|
|
|
The Generic IOCtl function contains both a category and function code.
|
|
The DOS examines the category field in order to intercept and obey device
|
|
commands that are actually serviced by the DOS code; all other command
|
|
categories are forwarded to the device driver for servicing.
|
|
|
|
|
|
|
|
25
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
For more information on these category and function codes, refer to Func-
|
|
tion 440CH (Generic IOCtl for Handles) and Function 440DH (Generic
|
|
IOCtl for Block Devices) in Chapter 1, "System Calls."
|
|
|
|
2.7.11 The Get/Set Logical Drive Map Function
|
|
|
|
Command codes = 23 (Get) or 24 (Set)
|
|
|
|
+----------------------------------------+
|
|
| 13-BYTE Static request header |
|
|
+----------------------------------------+
|
|
| BYTE Input (unit code) |
|
|
+----------------------------------------+
|
|
| BYTE Output (last device referenced)|
|
|
+----------------------------------------+
|
|
| BYTE Command code |
|
|
+----------------------------------------+
|
|
| WORD Status |
|
|
+----------------------------------------+
|
|
| DWORD Reserved |
|
|
+----------------------------------------+
|
|
|
|
The Get/Set Logical Drive Map function is called by MS-DOS only if
|
|
the device driver sets the DOS 3.2 attribute bit in the device header. The
|
|
call is issued only to block devices by the subfunction of the IOCtl system
|
|
call. The logical drive to be mapped is passed in the Unit field of the
|
|
header to the device driver. The device driver returns the current logical
|
|
drive owner of the physical device that maps to the requested physical
|
|
drive. To detect whether a logical device currently owns the physical dev-
|
|
ice to which it is mapped, a program needs to verify that, after a call of
|
|
Function 440EH or 440FH (Get/Set Logical Drive Map), the value of
|
|
the Unit field is unchanged.
|
|
|
|
2.8 The Media Descriptor Byte
|
|
|
|
In MS-DOS, the media descriptor byte is used to inform the DOS that a
|
|
different type of media is present. The media descriptor byte can be any
|
|
value between 0 and FFH. It does not have to be the same as the FAT ID
|
|
byte. The FAT ID byte, which is the first byte of the FAT, was used in
|
|
MS-DOS 1.00 to distinguish between different types of disk media, and
|
|
may be used as well under 2.x and 3.x disk device drivers. However, FAT
|
|
ID bytes have significance only for block device drivers where the
|
|
non-FAT ID bit is not set (0).
|
|
|
|
|
|
|
|
26
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
Values of the media descriptor byte or the FAT ID byte have no
|
|
significance to MS-DOS. They are passed to the device driver so that pro-
|
|
grams can determine the media type.
|
|
|
|
2.9 Format of a Media Descriptor Table
|
|
|
|
The MS-DOS file system uses a linked list of pointers (one for each cluster
|
|
or allocation unit) called the File Allocation Table (FAT). Unused clusters
|
|
are represented by zero and end-of-file by FFFH (or FFFFH on units with
|
|
16-bit FAT entries). No valid entry should ever point to a zero entry, but
|
|
if one does, the first FAT entry (which would be pointed to by a zero
|
|
entry) was reserved and set to end of chain. Eventually, several end of
|
|
chain values were defined ([F]FF8-[F]FFFH), and these were used to dis-
|
|
tinguish different types of media.
|
|
|
|
A preferable technique is to write a complete media descriptor table in the
|
|
boot sector and use it for media identification. To ensure backward com-
|
|
patibility for systems whose drivers do not set the non-FAT ID bit
|
|
(including the IBM PC implementation), it is necessary also to write the
|
|
FAT ID bytes during the format process.
|
|
|
|
To allow more flexibility for supporting many different disk formats in the
|
|
future, it is recommended that the information relating to the BPB for a
|
|
particular piece of media be kept in the boot sector. Figure 2.3 shows the
|
|
format of such a boot sector.
|
|
|
|
+------------------------------------+
|
|
| 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 |
|
|
|
|
27
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
+------------------------------------+
|
|
| WORD Number of heads |
|
|
+------------------------------------+
|
|
| WORD Number of hidden sectors |
|
|
+------------------------------------+
|
|
| WORD High order number of hidden |
|
|
| sectors |
|
|
+------------------------------------+
|
|
| DWORD Number of logical sectors |
|
|
+------------------------------------+
|
|
|
|
|
|
Figure 2.5 Format of a Boot Sector
|
|
|
|
Although MS-DOS does not use the five fields that follow the BPB, these
|
|
fields may be used by a device driver to help it understand the media.
|
|
|
|
The "Sectors per track" and "Number of heads" fields are useful for sup-
|
|
porting different media which may have the same logical layout , but a
|
|
different physical layout (for example, 40-track, double-sided versus 80-
|
|
track, single-sided). "Sectors per track" tells the device driver how the log-
|
|
ical disk format is laid out on the physical disk.
|
|
|
|
The "Number of hidden sectors" and "High order number of hidden sec-
|
|
tors" fields may be used to support drive-partitioning schemes.
|
|
|
|
The "Number of logical sectors" field is not currently used, but will tell
|
|
the device driver how many sectors to reserve if the "Number of sectors in
|
|
logical image" field is zero. (Note that this is intended for supporting dev-
|
|
ices that access more than 32 megabytes.)
|
|
|
|
The following procedure is recommended for media determination by NON
|
|
FAT ID format drivers:
|
|
|
|
1. Read the boot sector of the drive into the one-sector scratch space
|
|
pointed to by the DWORD transfer address.
|
|
|
|
2. Determine if the first byte of the boot sector is an E9H or EBIT
|
|
(the first byte of a 3-byte NEAR or 2-byte SHORT jump) or an
|
|
EBH (the first byte of a 2-byte jump followed by an NOP). If so, a
|
|
BPB is located beginning at offset 3. Return a pointer to it.
|
|
|
|
3. If the boot sector does not have a BPB table, it is probably a disk
|
|
formatted under a 1.x version of MS-DOS and probably uses a FAT
|
|
ID byte for determining media.
|
|
|
|
The driver may optionally attempt to read the first sector of the
|
|
FAT into the one-sector scratch area and read the first byte to
|
|
determine media type based upon whatever FAT ID bytes may
|
|
have been used on disks that are expected to be read by this sys-
|
|
tem. Return a pointer to a hard-coded BPB.
|
|
|
|
|
|
|
|
28
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
|
|
2.10 The CLOCK Device
|
|
|
|
MS-DOS assumes that some sort of clock is available in the system. This
|
|
may either be a CMOS real-time clock or an interval timer that is initial-
|
|
ized at boot time by the user. The CLOCK device defines and performs
|
|
functions like any other character device, except that it is identified by a
|
|
bit in the attribute word. The DOS uses this bit to identify it; conse-
|
|
quently, the CLOCK device may take any name. The IBM implementation
|
|
uses the name $CLOCK so as not to conflict with existing files named
|
|
clock.
|
|
|
|
The CLOCK device is unique in that MS-DOS will read or write a 6-byte
|
|
sequence that encodes the date and time. A write to this device will set
|
|
the date and time; a read will get the date and time.
|
|
|
|
Figure 2.6 illustrates the binary time format used by the CLOCK device:
|
|
|
|
byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
|
|
+--------+--------+---------+--------+--------+---------+
|
|
| | | | | | |
|
|
|days since 1-1-80| minutes | hours | sec/100| seconds |
|
|
|low byte|hi byte | | | | |
|
|
+--------+--------+---------+--------+--------+---------+
|
|
|
|
|
|
Figure 2.6 Format of a Clock Device
|
|
|
|
|
|
2.11 Anatomy of a Device Call
|
|
|
|
The following steps illustrate what happens when MS-DOS calls on a block
|
|
device driver to perform a Write request:
|
|
|
|
1. MS-DOS writes a request packet in a reserved area of memory.
|
|
|
|
2. MS-DOS calls the strategy entry point of the block device driver.
|
|
|
|
3. The device driver saves the ES and BX registers (ES:BX points to
|
|
the request packet) and does a FAR return.
|
|
|
|
4. MS-DOS calls the interrupt entry point.
|
|
|
|
5. The device driver retrieves the pointer to the request packet and
|
|
reads the command code (offset 2) to determine that this is a
|
|
Write request. The device driver converts the command code to
|
|
an index into a dispatch table and control passes to the Write rou-
|
|
tine.
|
|
|
|
|
|
|
|
29
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
6. The device driver reads the unit code (offset 1) to determine which
|
|
disk drive it is supposed to write to.
|
|
|
|
7. Since the command is a disk Write, the device driver must get the
|
|
transfer address (offset 14), the sector count (offset 18), and the
|
|
start sector (offset 20) in the request packet.
|
|
|
|
8. The device driver translates the first logical sector number into a
|
|
track, head, and sector number.
|
|
|
|
9. The device driver writes the specified number of sectors, starting at
|
|
the beginning sector on the drive defined by the unit code (the
|
|
subunit defined by this device driver), and transfers data from the
|
|
transfer address indicated in the request packet. Note that this
|
|
may involve multiple Write commands to the disk controller.
|
|
|
|
10. After the transfer is complete, the device driver must report the
|
|
status of the request to MS-DOS by setting the done bit in the
|
|
status word (offset 3 in the request packet). It reports the number
|
|
of sectors actually transferred in the sector count area of the
|
|
request packet.
|
|
|
|
11. If an error occurs, the driver sets the done bit and the error bit in
|
|
the status word and fills in the error code in the lower half of the
|
|
status word. The number of sectors actually transferred must be
|
|
written in the request header. It is not sufficient just to set the
|
|
error bit of the status word.
|
|
|
|
12. The device driver does a FAR return to MS-DOS.
|
|
|
|
The device drivers should preserve the state of MS-DOS. This means that
|
|
all registers (including flags) should be preserved. The direction flag and
|
|
interrupt enable bits are critical. When the interrupt entry point in the
|
|
device driver is called, MS-DOS has room for about 40 to 50 bytes on its
|
|
internal stack. Your device driver should switch to a local stack if it uses
|
|
extensive stack operations.
|
|
|
|
2.12 Two Sample Device Drivers
|
|
|
|
The following two examples illustrate a block device driver and a charac-
|
|
ter device driver program. These examples are provided as guides for writ-
|
|
ing your own device drivers. However, since device drivers are hardware-
|
|
dependent, your device drivers will differ.
|
|
|
|
|
|
30
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
|
|
Block Device Driver
|
|
|
|
|
|
;********************* A Block Device *******************
|
|
|
|
Title 5.25-inch Disk Driver
|
|
|
|
;This driver is intended to drive up to four 5.25-inch
|
|
;drives hooked to a single disk controller. All standard
|
|
;IBM PC formats are supported.
|
|
|
|
|
|
FALSE EQU 0
|
|
TRUE EQU NOT FALSE
|
|
|
|
;The I/O port address of the disk controller
|
|
DISK EQU 0E0H
|
|
;DISK+0
|
|
; 1793 Command/Status
|
|
;DISK+1
|
|
; 1793 Track
|
|
;DISK+2
|
|
; 1793 Sector
|
|
;DISK+3
|
|
; 1793 Data
|
|
;DISK+4
|
|
; Aux Command/Status
|
|
;DISK+5
|
|
; Wait Sync
|
|
|
|
;Back side select bit
|
|
BACKBIT EQU 04H
|
|
;5 1/4" select bit
|
|
SMALBIT EQU 10H
|
|
;Double Density bit
|
|
DDBIT EQU 08H
|
|
|
|
;Done bit in status register
|
|
DONEBIT EQU 01H
|
|
|
|
;Use table below to select head step speed.
|
|
;Step times for 5" drives
|
|
;are double that shown in the table.
|
|
;
|
|
;Step value 1771 1793
|
|
;
|
|
; 0 6ms 3ms
|
|
; 1 6ms 6ms
|
|
; 2 10ms 10ms
|
|
; 3 20ms 15ms
|
|
;
|
|
STPSPD EQU 1
|
|
|
|
NUMERR EQU ERROUT-ERRIN
|
|
|
|
CR EQU 0DH
|
|
LF EQU 0AH
|
|
|
|
31
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
CODE SEGMENT
|
|
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
|
|
;-----------------------------------------------------
|
|
;
|
|
; Device Header
|
|
;
|
|
DRVDEV LABEL WORD
|
|
DW -1,-1
|
|
DW 0000 ;IBM format-compatible, Block
|
|
DW STRATEGY
|
|
DW DRV$IN
|
|
DRVMAX DB 4
|
|
|
|
DRVTBL LABEL WORD
|
|
DW DRV$INIT
|
|
DW MEDIA$CHK
|
|
DW GET$BPB
|
|
DW CMDERR
|
|
DW DRV$READ
|
|
DW EXIT
|
|
DW EXIT
|
|
DW EXIT
|
|
DW DRV$WRIT
|
|
DW DRV$WRIT
|
|
DW EXIT
|
|
DW EXIT
|
|
DW EXIT
|
|
|
|
;------------------------------------
|
|
;
|
|
; Strategy
|
|
|
|
PTRSAV DD 0
|
|
|
|
STRATP PROC FAR
|
|
STRATEGY:
|
|
MOV WORD PTR [PTRSAV],BX
|
|
MOV WORD PTR [PTRSAV+2],ES
|
|
RET
|
|
STRATP ENDP
|
|
|
|
;--------------------------------------
|
|
;
|
|
; Main Entry
|
|
|
|
|
|
CMDLEN = 0 ;Length of this command
|
|
UNIT = 1 ;Subunit specified
|
|
CMDC = 2 ;Command Code
|
|
STATUS = 3 ;Status
|
|
MEDIA = 13 ;Media Descriptor
|
|
TRANS = 14 ;Transfer Address
|
|
COUNT = 18 ;Count of blocks or characters
|
|
START = 20 ;First block to transfer
|
|
|
|
DRV$IN:
|
|
|
|
32
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
PUSH SI
|
|
PUSH AX
|
|
PUSH CX
|
|
PUSH DX
|
|
PUSH DI
|
|
PUSH BP
|
|
PUSH DS
|
|
PUSH ES
|
|
PUSH BX
|
|
|
|
LDS BX,[PTRSAV] ;Get pointer to I/O packet
|
|
|
|
MOV AL,BYTE PTR [BX].UNIT ;AL = Unit Code
|
|
MOV AH,BYTE PTR [BX].MEDIA ;AH = Media Descrip
|
|
MOV CX,WORD PTR [BX].COUNT ;CX = Count
|
|
MOV DX,WORD PTR [BX].START ;DX = Start Sector
|
|
PUSH AX
|
|
MOV AL,BYTE PTR [BX].CMDC ;Command code
|
|
CMP AL,15
|
|
JA CMDERRP ;Bad command
|
|
CBW
|
|
SHL AX,1 ;2 times command =
|
|
;word table index
|
|
MOV SI,OFFSET DRVTBL
|
|
ADD SI,AX ;Index into table
|
|
POP AX ;Get back media
|
|
;and unit
|
|
|
|
LES DI,DWORD PTR [BX].TRANS ;ES:DI = Transfer
|
|
;Address
|
|
|
|
PUSH CS
|
|
POP DS
|
|
|
|
ASSUME DS:CODE
|
|
|
|
JMP WORD PTR [SI] ;GO DO COMMAND
|
|
|
|
;----------------------------------------------------------
|
|
;
|
|
; EXIT - All Routines return through this path
|
|
;
|
|
ASSUME DS:NOTHING
|
|
CMDERRP:
|
|
POP AX ;Clean stack
|
|
CMDERR:
|
|
MOV AL,3 ;Unknown command error
|
|
JMP SHORT ERR$EXIT
|
|
|
|
ERR$CNT:LDS BX,[PTRSAV]
|
|
SUB WORD PTR [BX].COUNT,CX ;# OF SUCCESS. I/Os
|
|
|
|
ERR$EXIT:
|
|
;AL has error code
|
|
MOV AH,10000001B ;Mark error return
|
|
JMP SHORT ERR1
|
|
|
|
33
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
EXITP PROC FAR
|
|
|
|
EXIT: MOV AH,00000001B
|
|
ERR1: LDS BX,[PTRSAV]
|
|
MOV WORD PTR [BX].STATUS,AX
|
|
;Mark Operation
|
|
CompleteE
|
|
|
|
POP BX
|
|
POP ES
|
|
POP DS
|
|
POP BP
|
|
POP DI
|
|
POP DX
|
|
POP CX
|
|
POP AX
|
|
POP SI
|
|
RET ;Restore REGS and return
|
|
EXITP ENDP
|
|
|
|
CURDRV DB -1
|
|
|
|
TRKTAB DB -1,-1,-1,-1
|
|
|
|
SECCNT DW 0
|
|
|
|
DRVLIM = 8 ;Number of sectors on device
|
|
SECLIM = 13 ;Maximum Sector
|
|
HDLIM = 15 ;Maximum Head
|
|
|
|
;WARNING - preserve order of drive and curhd!
|
|
|
|
DRIVE DB 0 ;Physical Drive Code
|
|
CURHD DB 0 ;Current Head
|
|
CURSEC DB 0 ;Current Sector
|
|
CURTRK DW 0 ;Current Track
|
|
|
|
|
|
;
|
|
MEDIA$CHK: ;Always indicates Don't know
|
|
ASSUME DS:CODE
|
|
TEST AH,00000100B ;Test if Media Removable
|
|
JZ MEDIA$EXT
|
|
XOR DI,DI ;Say I Don't know
|
|
MEDIA$EXT:
|
|
LDS BX,[PTRSAV]
|
|
MOV WORD PTR [BX].TRANS,DI
|
|
JMP EXIT
|
|
|
|
BUILD$BPB:
|
|
ASSUME DS:CODE
|
|
MOV AH,BYTE PTR ES:[DI] ;Get FAT ID Byte
|
|
CALL BUILDBP ;Translate
|
|
SETBPB: LDS BX,[PTRSAV]
|
|
MOV [BX].MEDIA,AH
|
|
MOV [BX].COUNT,DI
|
|
|
|
34
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
MOV [BX].COUNT+2,CS
|
|
JMP EXIT
|
|
|
|
BUILDBP:
|
|
ASSUME DS:NOTHING
|
|
;AH is media byte on entry
|
|
;DI points to correct BPB on return
|
|
PUSH AX
|
|
PUSH CX
|
|
PUSH DX
|
|
PUSH BX
|
|
MOV CL,AH ;Save Media
|
|
AND CL,0F8H ;Normalize
|
|
CMP CL,0F8H ;Compare with Good Media Byte
|
|
JZ GOODID
|
|
MOV AH,0FEH ;Default to 8-sector,
|
|
;Single-sided
|
|
GOODID:
|
|
MOV AL,1 ;Set number of FAT sectors
|
|
MOV BX,64*256+8 ;Set Dir Entries and Sector Max
|
|
MOV CX,40*8 ;Set Size of Drive
|
|
MOV DX,01*256+1 ;Set Head Limit & Sec/All Unit
|
|
MOV DI,OFFSET DRVBPB
|
|
TEST AH,00000010B ;Test for 8 OR 9 Sectors
|
|
JNZ HAS8 ;NZ = has 8 sectors
|
|
INC AL ;Inc Number of FAT sectors
|
|
INC BL ;Inc Sector Max
|
|
ADD CX,40 ;Increase Size
|
|
HAS8: TEST AH,00000001B ;Test for 1 or 2 Heads
|
|
JZ HAS1 ;Z = 1 Head
|
|
ADD CX,CX ;Double Size of Disk
|
|
MOV BH,112 ;Increase # of Dir Entries
|
|
INC DH ;Inc Sec/All Unit
|
|
INC DL ;Inc Head Limit
|
|
HAS1: MOV BYTE PTR [DI].2,DH
|
|
MOV BYTE PTR [DI].6,BH
|
|
MOV WORD PTR [DI].8,CX
|
|
MOV BYTE PTR [DI].10,AH
|
|
MOV BYTE PTR [DI].11,AL
|
|
MOV BYTE PTR [DI].13,BL
|
|
MOV BYTE PTR [DI].15,DL
|
|
POP BX
|
|
POP DX
|
|
POP CX
|
|
POP AX
|
|
RET
|
|
|
|
|
|
;----------------------------------------------------------
|
|
;
|
|
; Disk I/O Handlers
|
|
;
|
|
;ENTRY:
|
|
; AL = Drive Number (0-3)
|
|
; AH = Media Descriptor
|
|
; CX = Sector Count
|
|
|
|
35
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
; DX = First Sector
|
|
; DS = CS
|
|
; ES:DI = Transfer Address
|
|
;EXIT:
|
|
; IF Successful Carry Flag = 0
|
|
; ELSE CF=1 AND AL contains (MS-DOS) Error Code,
|
|
CX # sectors NOT transferred
|
|
|
|
DRV$READ:
|
|
ASSUME DS:CODE
|
|
JCXZ DSKOK
|
|
CALL SETUP
|
|
JC DSK$IO
|
|
CALL DISKRD
|
|
JMP SHORT DSK$IO
|
|
|
|
DRV$WRIT:
|
|
ASSUME DS:CODE
|
|
JCXZ DSKOK
|
|
CALL SETUP
|
|
JC DSK$IO
|
|
CALL DISKWRT
|
|
ASSUME DS:NOTHING
|
|
DSK$IO: JNC DSKOK
|
|
JMP ERR$CNT
|
|
DSKOK: JMP EXIT
|
|
|
|
|
|
SETUP:
|
|
ASSUME DS:CODE
|
|
;Input same as above
|
|
;On output
|
|
; ES:DI = Trans addr
|
|
; DS:BX Points to BPB
|
|
; Carry set if error (AL is error code (MS-DOS))
|
|
; else
|
|
; [DRIVE] = Drive number (0-3)
|
|
; [SECCNT] = Sectors to transfer
|
|
; [CURSEC] = Sector number of start of I/O
|
|
; [CURHD] = Head number of start of I/O ;Set
|
|
; [CURTRK] = Track # of start of I/O ;Seek performed
|
|
; All other registers destroyed
|
|
|
|
XCHG BX,DI ;ES:BX = Transfer Address
|
|
CALL BUILDBP ;DS:DI = PTR to B.P.B
|
|
MOV SI,CX
|
|
ADD SI,DX
|
|
CMP SI,WORD PTR [DI].DRVLIM
|
|
;Compare Against Drive Max
|
|
JBE INRANGE
|
|
MOV AL,8
|
|
STC
|
|
RET
|
|
|
|
INRANGE:
|
|
MOV [DRIVE],AL
|
|
|
|
36
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
MOV [SECCNT],CX ;Save Sector Count
|
|
XCHG AX,DX ;Set Up Logical Sector
|
|
;For Divide
|
|
XOR DX,DX
|
|
DIV WORD PTR [DI].SECLIM ;Divide by Sec per Track
|
|
INC DL
|
|
MOV [CURSEC],DL ;Save Current Sector
|
|
MOV CX,WORD PTR [DI].HDLIM ;Get Number of Heads
|
|
XOR DX,DX ;Divide Tracks by Heads per Cylinder
|
|
DIV CX
|
|
MOV [CURHD],DL ;Save Current Head
|
|
MOV [CURTRK],AX ;Save Current Track
|
|
SEEK:
|
|
PUSH BX ;Xaddr
|
|
PUSH DI ;BPB pointer
|
|
CALL CHKNEW ;Unload head if change drives
|
|
CALL DRIVESEL
|
|
MOV BL,[DRIVE]
|
|
XOR BH,BH ;BX drive index
|
|
ADD BX,OFFSET TRKTAB ;Get current track
|
|
MOV AX,[CURTRK]
|
|
MOV DL,AL ;Save desired track
|
|
XCHG AL,DS:[BX] ;Make desired track current
|
|
OUT DISK+1,AL ;Tell Controller current track
|
|
CMP AL,DL ;At correct track?
|
|
JZ SEEKRET ;Done if yes
|
|
MOV BH,2 ;Seek retry count
|
|
CMP AL,-1 ;Position Known?
|
|
JNZ NOHOME ;If not home head
|
|
TRYSK:
|
|
CALL HOME
|
|
JC SEEKERR
|
|
NOHOME:
|
|
MOV AL,DL
|
|
OUT DISK+3,AL ;Desired track
|
|
MOV AL,1CH+STPSPD ;Seek
|
|
CALL DCOM
|
|
AND AL,98H ;Accept not rdy, seek, & CRC errors
|
|
JZ SEEKRET
|
|
JS SEEKERR ;No retries if not ready
|
|
DEC BH
|
|
JNZ TRYSK
|
|
SEEKERR:
|
|
MOV BL,[DRIVE]
|
|
XOR BH,BH ;BX drive index
|
|
ADD BX,OFFSET TRKTAB ;Get current track
|
|
MOV BYTE PTR DS:[BX],-1 ;Make current track
|
|
;unknown
|
|
CALL GETERRCD
|
|
MOV CX,[SECCNT] ;Nothing transferred
|
|
POP BX ;BPB pointer
|
|
POP DI ;Xaddr
|
|
RET
|
|
|
|
SEEKRET:
|
|
POP BX ;BPB pointer
|
|
|
|
37
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
POP DI ;Xaddr
|
|
CLC
|
|
RET
|
|
|
|
;---------------------------------------------
|
|
;
|
|
; Read
|
|
;
|
|
|
|
DISKRD:
|
|
ASSUME DS:CODE
|
|
MOV CX,[SECCNT]
|
|
RDLP:
|
|
CALL PRESET
|
|
PUSH BX
|
|
MOV BL,10 ;Retry count
|
|
MOV DX,DISK+3 ;Data port
|
|
RDAGN:
|
|
MOV AL,80H ;Read command
|
|
CLI ;Disable for 1793
|
|
OUT DISK,AL ;Output read command
|
|
MOV BP,DI ;Save address for retry
|
|
JMP SHORT RLOOPENTRY
|
|
RLOOP:
|
|
STOSB
|
|
RLOOPENTRY:
|
|
IN AL,DISK+5 ;Wait for DRQ or INTRQ
|
|
SHR AL,1
|
|
IN AL,DX ;Read data
|
|
JNC RLOOP
|
|
STI ;Ints OK now
|
|
CALL GETSTAT
|
|
AND AL,9CH
|
|
JZ RDPOP ;Ok
|
|
MOV DI,BP ;Get back transfer
|
|
DEC BL
|
|
JNZ RDAGN
|
|
CMP AL,10H ;Record not found?
|
|
JNZ GOT_CODE ;No
|
|
MOV AL,1 ;Map it
|
|
GOT_CODE:
|
|
CALL GETERRCD
|
|
POP BX
|
|
RET
|
|
|
|
RDPOP:
|
|
POP BX
|
|
LOOP RDLP
|
|
CLC
|
|
RET
|
|
|
|
|
|
;---------------------------------------------
|
|
;
|
|
; Write
|
|
;
|
|
|
|
38
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
DISKWRT:
|
|
ASSUME DS:CODE
|
|
MOV CX,[SECCNT]
|
|
MOV SI,DI
|
|
PUSH ES
|
|
POP DS
|
|
ASSUME DS:NOTHING
|
|
WRLP:
|
|
CALL PRESET
|
|
PUSH BX
|
|
MOV BL,10 ;Retry count
|
|
MOV DX,DISK+3 ;Data port
|
|
WRAGN:
|
|
MOV AL,0A0H ;Write command
|
|
CLI ;Disable for 1793
|
|
OUT DISK,AL ;Output write command
|
|
MOV BP,SI ;Save address for retry
|
|
WRLOOP:
|
|
IN AL,DISK+5
|
|
SHR AL,1
|
|
LODSB ;Get data
|
|
OUT DX,AL ;Write data
|
|
JNC WRLOOP
|
|
STI ;Ints OK now
|
|
DEC SI
|
|
CALL GETSTAT
|
|
AND AL,0FCH
|
|
JZ WRPOP ;Ok
|
|
MOV SI,BP ;Get back transfer
|
|
DEC BL
|
|
JNZ WRAGN
|
|
CALL GETERRCD
|
|
POP BX
|
|
RET
|
|
|
|
WRPOP:
|
|
POP BX
|
|
LOOP WRLP
|
|
CLC
|
|
RET
|
|
|
|
|
|
PRESET:
|
|
ASSUME DS:NOTHING
|
|
MOV AL,[CURSEC]
|
|
CMP AL,CS:[BX].SECLIM
|
|
JBE GOTSEC
|
|
MOV DH,[CURHD]
|
|
INC DH
|
|
CMP DH,CS:[BX].HDLIM
|
|
JB SETHEAD ;Select new head
|
|
CALL STEP ;Go on to next track
|
|
XOR DH,DH ;Select head zero
|
|
SETHEAD:
|
|
MOV [CURHD],DH
|
|
CALL DRIVESEL
|
|
|
|
39
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
MOV AL,1 ;First sector
|
|
MOV [CURSEC],AL ;Reset CURSEC
|
|
GOTSEC:
|
|
OUT DISK+2,AL ;Tell controller which sector
|
|
INC [CURSEC] ;We go on to next sector
|
|
RET
|
|
|
|
STEP:
|
|
ASSUME DS:NOTHING
|
|
MOV AL,58H+STPSPD ;Step in w/ update, no verify
|
|
CALL DCOM
|
|
PUSH BX
|
|
MOV BL,[DRIVE]
|
|
XOR BH,BH ;BX drive index
|
|
ADD BX,OFFSET TRKTAB ;Get current track
|
|
INC BYTE PTR CS:[BX] ;Next track
|
|
POP BX
|
|
RET
|
|
|
|
HOME:
|
|
ASSUME DS:NOTHING
|
|
MOV BL,3
|
|
TRYHOM:
|
|
MOV AL,0CH+STPSPD ;Restore with verify
|
|
CALL DCOM
|
|
AND AL,98H
|
|
JZ RET3
|
|
JS HOMERR ;No retries if not ready
|
|
PUSH AX ;Save real error code
|
|
MOV AL,58H+STPSPD ;Step in w/ update no verify
|
|
CALL DCOM
|
|
DEC BL
|
|
POP AX ;Get back real error code
|
|
JNZ TRYHOM
|
|
HOMERR:
|
|
STC
|
|
RET3: RET
|
|
|
|
|
|
CHKNEW:
|
|
ASSUME DS:NOTHING
|
|
MOV AL,[DRIVE] ;Get disk drive number
|
|
MOV AH,AL
|
|
XCHG AL,[CURDRV] ;Make new drive current.
|
|
CMP AL,AH ;Changing drives?
|
|
JZ RET1 ;No
|
|
; If changing drives, unload head so the head load delay
|
|
;one-shot will fire again. Do it by seeking to the same
|
|
;track with the H bit reset.
|
|
;
|
|
IN AL,DISK+1 ;Get current track number
|
|
OUT DISK+3,AL ;Make it the track to seek
|
|
MOV AL,10H ;Seek and unload head
|
|
|
|
DCOM:
|
|
ASSUME DS:NOTHING
|
|
|
|
40
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
OUT DISK,AL
|
|
PUSH AX
|
|
AAM ;Delay 10 microseconds
|
|
POP AX
|
|
GETSTAT:
|
|
IN AL,DISK+4
|
|
TEST AL,DONEBIT
|
|
JZ GETSTAT
|
|
IN AL,DISK
|
|
RET1: RET
|
|
|
|
DRIVESEL:
|
|
ASSUME DS:NOTHING
|
|
;Select the drive based on current info
|
|
;Only AL altered
|
|
MOV AL,[DRIVE]
|
|
OR AL,SMALBIT + DDBIT ;5 1/4" IBM PC disks
|
|
CMP [CURHD],0
|
|
JZ GOTHEAD
|
|
OR AL,BACKBIT ;Select side 1
|
|
GOTHEAD:
|
|
OUT DISK+4,AL ;Select drive and side
|
|
RET
|
|
|
|
GETERRCD:
|
|
ASSUME DS:NOTHING
|
|
PUSH CX
|
|
PUSH ES
|
|
PUSH DI
|
|
PUSH CS
|
|
POP ES ;Make ES the local segment
|
|
MOV CS:[LSTERR],AL ;Terminate list w/ error code
|
|
MOV CX,NUMERR ;Number of error conditions
|
|
MOV DI,OFFSET ERRIN ;Point to error conditions
|
|
REPNE SCASB
|
|
MOV AL,NUMERR-1[DI] ;Get translation
|
|
STC ;Flag error condition
|
|
POP DI
|
|
POP ES
|
|
POP CX
|
|
RET ;and return
|
|
|
|
;*********************************************************
|
|
; BPB for an IBM floppy disk, Various parameters are
|
|
; patched by BUILDBP to reflect the type of Media
|
|
; inserted
|
|
; This is a 9-sector, single-side BPB
|
|
DRVBPB:
|
|
DW 512 ;Physical sector size in bytes
|
|
DB 1 ;Sectors/allocation unit
|
|
DW 1 ;Reserved sectors for DOS
|
|
DB 2 ;# of allocation tables
|
|
DW 64 ;Number directory entries
|
|
DW 9*40 ;Number 512-byte sectors
|
|
DB 11111100B ;Media descriptor
|
|
DW 2 ;Number of FAT sectors
|
|
|
|
41
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
DW 9 ;Sector limit
|
|
DW 1 ;Head limit
|
|
|
|
|
|
INITAB DW DRVBPB ;Up to four units
|
|
DW DRVBPB
|
|
DW DRVBPB
|
|
DW DRVBPB
|
|
|
|
ERRIN: ;DISK ERRORS RETURNED FROM THE CONTROLLER
|
|
DB 80H ;No response
|
|
DB 40H ;Write protect
|
|
DB 20H ;Write Fault
|
|
DB 10H ;SEEK error
|
|
DB 8 ;CRC error
|
|
DB 1 ;Mapped from 10H
|
|
;(record not found) on Read
|
|
LSTERR DB 0 ;All other errors
|
|
|
|
ERROUT: ;RETURNED ERROR CODES CORRESPONDING TO ABOVE
|
|
DB 2 ;No response
|
|
DB 0 ;Write Attempt
|
|
;On Write-protected disk
|
|
DB 0AH ;Write fault
|
|
DB 6 ;SEEK Failure
|
|
DB 4 ;Bad CRC
|
|
DB 8 ;Sector not found
|
|
DB 12 ;General error
|
|
|
|
|
|
DRV$INIT:
|
|
;
|
|
; Determine number of physical drives by reading config.sys
|
|
;
|
|
ASSUME DS:CODE
|
|
PUSH DS
|
|
LDS SI,[PTRSAV]
|
|
ASSUME DS:NOTHING
|
|
LDS SI,DWORD PTR [SI.COUNT] ;DS:SI points to
|
|
;config.sys
|
|
SCAN_LOOP:
|
|
CALL SCAN_SWITCH
|
|
MOV AL,CL
|
|
OR AL,AL
|
|
JZ SCAN4
|
|
CMP AL,"s"
|
|
JZ SCAN4
|
|
|
|
WERROR: POP DS
|
|
ASSUME DS:CODE
|
|
MOV DX,OFFSET ERRMSG2
|
|
WERROR2: MOV AH,9
|
|
INT 21H
|
|
XOR AX,AX
|
|
PUSH AX ;No units
|
|
JMP SHORT ABORT
|
|
|
|
42
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
BADNDRV:
|
|
POP DS
|
|
MOV DX,OFFSET ERRMSG1
|
|
JMP WERROR2
|
|
|
|
SCAN4:
|
|
ASSUME DS:NOTHING
|
|
;BX is number of floppies
|
|
OR BX,BX
|
|
JZ BADNDRV ;User error
|
|
CMP BX,4
|
|
JA BADNDRV ;User error
|
|
POP DS
|
|
ASSUME DS:CODE
|
|
PUSH BX ;Save unit count
|
|
ABORT: LDS BX,[PTRSAV]
|
|
ASSUME DS:NOTHING
|
|
POP AX
|
|
MOV BYTE PTR [BX].MEDIA,AL ;Unit count
|
|
MOV [DRVMAX],AL
|
|
MOV WORD PTR [BX].TRANS,OFFSET DRV$INIT ;SET
|
|
;BREAK ADDRESS
|
|
MOV [BX].TRANS+2,CS
|
|
MOV WORD PTR [BX].COUNT,OFFSET INITAB
|
|
;SET POINTER TO BPB ARRAY
|
|
MOV [BX].ceOUNT+2,CS
|
|
JMP EXIT
|
|
;
|
|
; Put switch in CL, value in BX
|
|
;
|
|
SCAN_SWITCH:
|
|
XOR BX,BX
|
|
MOV CX,BX
|
|
LODSB
|
|
CMP AL,10
|
|
JZ NUMRET
|
|
CMP AL,"-"
|
|
JZ GOT_SWITCH
|
|
CMP AL,"/"
|
|
JNZ SCAN_SWITCH
|
|
GOT_SWITCH:
|
|
CMP BYTE PTR [SI+1],":"
|
|
JNZ TERROR
|
|
LODSB
|
|
OR AL,20H ; Convert to lowercase
|
|
MOV CL,AL ; Get switch
|
|
LODSB ; Skip ":"
|
|
;
|
|
; Get number pointed to by [SI]
|
|
;
|
|
; Wipes out AX,DX only BX returns number
|
|
;
|
|
GETNUM1:LODSB
|
|
SUB AL,"0"
|
|
JB CHKRET
|
|
CMP AL,9
|
|
|
|
43
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
JA CHKRET
|
|
CBW
|
|
XCHG AX,BX
|
|
MOV DX,10
|
|
MUL DX
|
|
ADD BX,AX
|
|
JMP GETNUM1
|
|
|
|
CHKRET: ADD AL,"0"
|
|
CMP AL," "
|
|
JBE NUMRET
|
|
CMP AL,"-"
|
|
JZ NUMRET
|
|
CMP AL,"/"
|
|
JZ NUMRET
|
|
TERROR:
|
|
POP DS ; Get rid of return address
|
|
JMP WERROR
|
|
NUMRET: DEC SI
|
|
RET
|
|
|
|
ERRMSG1 DB "SMLDRV: Bad number of drives",13,10,"$"
|
|
ERRMSG2 DB "SMLDRV: Invalid parameter",13,10,"$"
|
|
CODE ENDS
|
|
END
|
|
|
|
|
|
|
|
44
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
|
|
Character Device Driver
|
|
|
|
The following program illustrates a character device driver program.
|
|
|
|
|
|
;******************** A Character Device *******************
|
|
|
|
Title VT52 Console for 2.0 (IBM)
|
|
|
|
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
;
|
|
; IBM Addresses for I/O
|
|
;
|
|
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
|
|
CR=13 ;Carriage-Return
|
|
BACKSP=8 ;BACKSPACE
|
|
ESC=1BH
|
|
BRKADR=6CH ;006C Break vector address
|
|
ASNMAX=200 ;Size of key assignment buffer
|
|
|
|
CODE SEGMENT BYTE
|
|
|
|
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING
|
|
;----------------------------------------------------------
|
|
;
|
|
; C O N - Console Device Driver
|
|
;
|
|
CONDEV: ;Header for device "CON"
|
|
DW -1,-1
|
|
DW 1000000000010011B ;CON IN AND CON OUT
|
|
DW STRATEGY
|
|
DW ENTRY
|
|
DB 'CON '
|
|
|
|
;-----------------------------------------------------------
|
|
;
|
|
; Command JUMP Tables
|
|
CONTBL:
|
|
DW CON$INIT
|
|
DW EXIT
|
|
DW EXIT
|
|
DW CMDERR
|
|
DW CON$READ
|
|
DW CON$RDND
|
|
DW EXIT
|
|
DW CON$FLSH
|
|
DW CON$WRIT
|
|
DW CON$WRIT
|
|
DW EXIT
|
|
DW EXIT
|
|
|
|
CMDTABL DB 'A'
|
|
DW CUU ;cursor up
|
|
DB 'B'
|
|
DW CUD ;cursor down
|
|
|
|
45
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
DB 'C'
|
|
DW CUF ;cursor forward
|
|
DB 'D'
|
|
DW CUB ;cursor back
|
|
DB 'H'
|
|
DW CUH ;cursor position
|
|
DB 'J'
|
|
DW ED ;erase display
|
|
DB 'K'
|
|
DW EL ;erase line
|
|
DB 'Y'
|
|
DW CUP ;cursor position
|
|
DB 'j'
|
|
DW PSCP ;save cursor position
|
|
DB 'k'
|
|
DW PRCP ;restore cursor position
|
|
DB 'y'
|
|
DW RM ;reset mode
|
|
DB 'x'
|
|
DW SM ;set mode
|
|
DB 00
|
|
|
|
PAGE
|
|
;---------------------------------------------------
|
|
;
|
|
; Device entry point
|
|
;
|
|
CMDLEN = 0 ;Length of this command
|
|
UNIT = 1 ;Subunit Specified
|
|
CMD = 2 ;Command Code
|
|
STATUS = 3 ;Status
|
|
MEDIA = 13 ;Media Descriptor
|
|
TRANS = 14 ;Transfer Address
|
|
COUNT = 18 ;Count of blocks or characters
|
|
START = 20 ;First block to transfer
|
|
|
|
PTRSAV DD 0
|
|
|
|
STRATP PROC FAR
|
|
|
|
STRATEGY:
|
|
MOV WORD PTR CS:[PTRSAV],BX
|
|
MOV WORD PTR CS:[PTRSAV+2],ES
|
|
RET
|
|
|
|
STRATP ENDP
|
|
|
|
ENTRY:
|
|
PUSH SI
|
|
PUSH AX
|
|
PUSH CX
|
|
PUSH DX
|
|
PUSH DI
|
|
PUSH BP
|
|
PUSH DS
|
|
PUSH ES
|
|
|
|
46
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
PUSH BX
|
|
|
|
LDS BX,CS:[PTRSAV] ;GET POINTER TO I/O PACKET
|
|
|
|
MOV CX,WORD PTR DS:[BX].COUNT ;CX = COUNT
|
|
|
|
MOV AL,BYTE PTR DS:[BX].CMD
|
|
CBW
|
|
MOV SI,OFFSET CONTBL
|
|
ADD SI,AX
|
|
ADD SI,AX
|
|
CMP AL,11
|
|
JA CMDERR
|
|
|
|
LES DI,DWORD PTR DS:[BX].TRANS
|
|
|
|
PUSH CS
|
|
POP DS
|
|
|
|
ASSUME DS:CODE
|
|
|
|
JMP WORD PTR [SI] ;GO DO COMMAND
|
|
|
|
PAGE
|
|
;=====================================================
|
|
;=
|
|
;= Subroutines Shared by Multiple Devices
|
|
;=
|
|
;=====================================================
|
|
;-----------------------------------------------------
|
|
;
|
|
; EXIT - All routines return through this path
|
|
;
|
|
BUS$EXIT: ;Device Busy Exit
|
|
MOV AH,00000011B
|
|
JMP SHORT ERR1
|
|
|
|
CMDERR:
|
|
MOV AL,3 ;Unknown command error
|
|
|
|
ERR$EXIT:
|
|
MOV AH,10000001B ;Mark error Return
|
|
JMP SHORT ERR1
|
|
|
|
EXITP PROC FAR
|
|
|
|
EXIT: MOV AH,00000001B
|
|
ERR1: LDS BX,CS:[PTRSAV]
|
|
MOV WORD PTR [BX].STATUS,AX ;Mark
|
|
;Operation Complete
|
|
|
|
POP BX
|
|
POP ES
|
|
POP DS
|
|
POP BP
|
|
POP DI
|
|
|
|
47
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
POP DX
|
|
POP CX
|
|
POP AX
|
|
POP SI
|
|
RET ;Restore REGS and Return
|
|
EXITP ENDP
|
|
;-----------------------------------------------
|
|
;
|
|
; BREAK Key Handling
|
|
;
|
|
BREAK:
|
|
MOV CS:ALTAH,3 ;Indicate BREAK key Set
|
|
INTRET: IRET
|
|
|
|
PAGE
|
|
;
|
|
; WARNING - Variables are very order dependent,
|
|
so be careful when adding new ones!
|
|
;
|
|
WRAP DB 0 ; 0 = WRAP, 1 = NO WRAP
|
|
STATE DW S1
|
|
MODE DB 3
|
|
MAXCOL DB 79
|
|
COL DB 0
|
|
ROW DB 0
|
|
SAVCR DW 0
|
|
ALTAH DB 0 ;Special key handling
|
|
|
|
;-------------------------------------------------------
|
|
;
|
|
; CHROUT - Write out Char in AL using current attribute
|
|
;
|
|
ATTRW LABEL WORD
|
|
ATTR DB 00000111B ;Character Attribute
|
|
BPAGE DB 0 ;Base Page
|
|
base dw 0b800h
|
|
|
|
chrout: cmp al,13
|
|
jnz trylf
|
|
mov [col],0
|
|
jmp short setit
|
|
|
|
trylf: cmp al,10
|
|
jz lf
|
|
cmp al,7
|
|
jnz tryback
|
|
torom:
|
|
mov bx,[attrw]
|
|
and bl,7
|
|
mov ah,14
|
|
int 10h
|
|
ret5: ret
|
|
|
|
tryback:
|
|
cmp al,8
|
|
jnz outchr
|
|
|
|
48
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
cmp [col],0
|
|
jz ret5
|
|
dec [col]
|
|
jmp short setit
|
|
|
|
outchr:
|
|
mov bx,[attrw]
|
|
mov cx,1
|
|
mov ah,9
|
|
int 10h
|
|
inc [col]
|
|
mov al,[col]
|
|
cmp al,[maxcol]
|
|
jbe setit
|
|
cmp [wrap],0
|
|
jz outchr1
|
|
dec [col]
|
|
ret
|
|
outchr1:
|
|
mov [col],0
|
|
lf: inc [row]
|
|
cmp [row],24
|
|
jb setit
|
|
mov [row],23
|
|
call scroll
|
|
|
|
setit: mov dh,row
|
|
mov dl,col
|
|
xor bh,bh
|
|
mov ah,2
|
|
int 10h
|
|
ret
|
|
|
|
scroll: call getmod
|
|
cmp al,2
|
|
jz myscroll
|
|
cmp al,3
|
|
jz myscroll
|
|
mov al,10
|
|
jmp torom
|
|
myscroll:
|
|
mov bh,[attr]
|
|
mov bl,' '
|
|
mov bp,80
|
|
mov ax,[base]
|
|
mov es,ax
|
|
mov ds,ax
|
|
xor di,di
|
|
mov si,160
|
|
mov cx,23*80
|
|
cld
|
|
cmp ax,0b800h
|
|
jz colorcard
|
|
|
|
rep movsw
|
|
mov ax,bx
|
|
|
|
49
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
mov cx,bp
|
|
rep stosw
|
|
sret: push cs
|
|
pop ds
|
|
ret
|
|
|
|
colorcard:
|
|
mov dx,3dah
|
|
wait2: in al,dx
|
|
test al,8
|
|
jz wait2
|
|
mov al,25h
|
|
mov dx,3d8h
|
|
out dx,al ;turn off video
|
|
rep movsw
|
|
mov ax,bx
|
|
mov cx,bp
|
|
rep stosw
|
|
mov al,29h
|
|
mov dx,3d8h
|
|
out dx,al ;turn on video
|
|
jmp sret
|
|
|
|
GETMOD: MOV AH,15
|
|
INT 16 ;get column information
|
|
MOV BPAGE,BH
|
|
DEC AH
|
|
MOV WORD PTR MODE,AX
|
|
RET
|
|
;------------------------------------------------------
|
|
;
|
|
; Console Read Routine
|
|
;
|
|
CON$READ:
|
|
JCXZ CON$EXIT
|
|
CON$LOOP:
|
|
PUSH CX ;Save Count
|
|
CALL CHRIN ;Get Char in AL
|
|
POP CX
|
|
STOSB ;Store Char at ES:DI
|
|
LOOP CON$LOOP
|
|
CON$EXIT:
|
|
JMP EXIT
|
|
;---------------------------------------------------------
|
|
;
|
|
; Input Single Char into AL
|
|
;
|
|
CHRIN: XOR AX,AX
|
|
XCHG AL,ALTAH ;Get Character & Zero ALTAH
|
|
OR AL,AL
|
|
JNZ KEYRET
|
|
|
|
INAGN: XOR AH,AH
|
|
INT 22
|
|
ALT10:
|
|
OR AX,AX ;Check for non-key after BREAK
|
|
|
|
50
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
JZ INAGN
|
|
OR AL,AL ;Special case?
|
|
JNZ KEYRET
|
|
MOV ALTAH,AH ;Store special key
|
|
KEYRET: RET
|
|
;----------------------------------------------------------
|
|
;
|
|
; Keyboard Non-descructive Read, No Wait
|
|
;
|
|
CON$RDND:
|
|
MOV AL,[ALTAH]
|
|
OR AL,AL
|
|
JNZ RDEXIT
|
|
|
|
RD1: MOV AH,1
|
|
INT 22
|
|
JZ CONBUS
|
|
OR AX,AX
|
|
JNZ RDEXIT
|
|
MOV AH,0
|
|
INT 22
|
|
JMP CON$RDND
|
|
|
|
RDEXIT: LDS BX,[PTRSAV]
|
|
MOV [BX].MEDIA,AL
|
|
EXVEC: JMP EXIT
|
|
CONBUS: JMP BUS$EXIT
|
|
;----------------------------------------------------------
|
|
;
|
|
; Keyboard Flush Routine
|
|
;
|
|
CON$FLSH:
|
|
MOV [ALTAH],0 ;Clear out holding buffer
|
|
|
|
PUSH DS
|
|
XOR BP,BP
|
|
MOV DS,BP ;Select segment 0
|
|
MOV DS:BYTE PTR 41AH,1EH ;Reset KB queue head
|
|
;pointer
|
|
MOV DS:BYTE PTR 41CH,1EH ;Reset tail pointer
|
|
POP DS
|
|
JMP EXVEC
|
|
;----------------------------------------------------------
|
|
;
|
|
; Console Write Routine
|
|
;
|
|
CON$WRIT:
|
|
JCXZ EXVEC
|
|
PUSH CX
|
|
MOV AH,3 ;Set current cursor position
|
|
XOR BX,BX
|
|
INT 16
|
|
MOV WORD PTR [COL],DX
|
|
POP CX
|
|
|
|
CON$LP: MOV AL,ES:[DI] ;Get Char
|
|
|
|
51
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
INC DI
|
|
CALL OUTC ;Output Char
|
|
LOOP CON$LP ;Repeat until all through
|
|
JMP EXVEC
|
|
|
|
COUT: STI
|
|
PUSH DS
|
|
PUSH CS
|
|
POP DS
|
|
CALL OUTC
|
|
POP DS
|
|
IRET
|
|
|
|
OUTC: PUSH AX
|
|
PUSH CX
|
|
PUSH DX
|
|
PUSH SI
|
|
PUSH DI
|
|
PUSH ES
|
|
PUSH BP
|
|
CALL VIDEO
|
|
POP BP
|
|
POP ES
|
|
POP DI
|
|
POP SI
|
|
POP DX
|
|
POP CX
|
|
POP AX
|
|
RET
|
|
|
|
|
|
;----------------------------------------------------------
|
|
;
|
|
; Output Single Char in AL to Video Device
|
|
;
|
|
VIDEO: MOV SI,OFFSET STATE
|
|
JMP [SI]
|
|
|
|
S1: CMP AL,ESC ;Escape sequence?
|
|
JNZ S1B
|
|
MOV WORD PTR [SI],OFFSET S2
|
|
RET
|
|
|
|
S1B: CALL CHROUT
|
|
S1A: MOV WORD PTR [STATE],OFFSET S1
|
|
RET
|
|
|
|
|
|
S2: PUSH AX
|
|
CALL GETMOD
|
|
POP AX
|
|
MOV BX,OFFSET CMDTABL-3
|
|
S7A: ADD BX,3
|
|
CMP BYTE PTR [BX],0
|
|
JZ S1A
|
|
CMP BYTE PTR [BX],AL
|
|
|
|
52
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ _________________________________
|
|
|
|
JNZ S7A
|
|
JMP WORD PTR [BX+1]
|
|
|
|
|
|
MOVCUR: CMP BYTE PTR [BX],AH
|
|
JZ SETCUR
|
|
ADD BYTE PTR [BX],AL
|
|
SETCUR: MOV DX,WORD PTR COL
|
|
XOR BX,BX
|
|
MOV AH,2
|
|
INT 16
|
|
JMP S1A
|
|
|
|
|
|
CUP: MOV WORD PTR [SI],OFFSET CUP1
|
|
RET
|
|
CUP1: SUB AL,32
|
|
MOV BYTE PTR [ROW],AL
|
|
MOV WORD PTR [SI],OFFSET CUP2
|
|
RET
|
|
CUP2: SUB AL,32
|
|
MOV BYTE PTR [COL],AL
|
|
JMP SETCUR
|
|
|
|
SM: MOV WORD PTR [SI],OFFSET S1A
|
|
RET
|
|
|
|
|
|
CUH: MOV WORD PTR COL,0
|
|
JMP SETCUR
|
|
|
|
CUF: MOV AH,MAXCOL
|
|
MOV AL,1
|
|
CUF1: MOV BX,OFFSET COL
|
|
JMP MOVCUR
|
|
|
|
CUB: MOV AX,00FFH
|
|
JMP CUF1
|
|
|
|
CUU: MOV AX,00FFH
|
|
CUU1: MOV BX,OFFSET ROW
|
|
JMP MOVCUR
|
|
|
|
CUD: MOV AX,23*256+1
|
|
JMP CUU1
|
|
|
|
PSCP: MOV AX,WORD PTR COL
|
|
MOV SAVCR,AX
|
|
JMP SETCUR
|
|
|
|
PRCP: MOV AX,SAVCR
|
|
MOV WORD PTR COL,AX
|
|
JMP SETCUR
|
|
|
|
ED: CMP BYTE PTR [ROW],24
|
|
JAE EL1
|
|
|
|
53
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
MOV CX,WORD PTR COL
|
|
MOV DH,24
|
|
JMP ERASE
|
|
|
|
EL1: MOV BYTE PTR [COL],0
|
|
EL: MOV CX,WORD PTR [COL]
|
|
EL2: MOV DH,CH
|
|
ERASE: MOV DL,MAXCOL
|
|
MOV BH,ATTR
|
|
MOV AX,0600H
|
|
INT 16
|
|
ED3: JMP SETCUR
|
|
|
|
|
|
RM: MOV WORD PTR [SI],OFFSET RM1
|
|
RET
|
|
RM1: XOR CX,CX
|
|
MOV CH,24
|
|
JMP EL2
|
|
|
|
CON$INIT:
|
|
int 11h
|
|
and al,00110000b
|
|
cmp al,00110000b
|
|
jnz iscolor
|
|
mov [base],0b000h ;look for bw card
|
|
iscolor:
|
|
cmp al,00010000b ;look for 40 col mode
|
|
ja setbrk
|
|
mov [mode],0
|
|
mov [maxcol],39
|
|
|
|
setbrk:
|
|
XOR BX,BX
|
|
MOV DS,BX
|
|
MOV BX,BRKADR
|
|
MOV WORD PTR [BX],OFFSET BREAK
|
|
MOV WORD PTR [BX+2],CS
|
|
|
|
MOV BX,29H*4
|
|
MOV WORD PTR [BX],OFFSET COUT
|
|
MOV WORD PTR [BX+2],CS
|
|
|
|
LDS BX,CS:[PTRSAV]
|
|
MOV WORD PTR [BX].TRANS,OFFSET CON$INIT
|
|
;SET BREAK ADDRESS
|
|
MOV [BX].TRANS+2,CS
|
|
JMP EXIT
|
|
|
|
CODE ENDS
|
|
END
|
|
|
|
|
|
|
|
54
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
|
|
Chapter 2
|
|
|
|
MS-DOS Device Drivers
|
|
|
|
_ ________________________________________________________________
|
|
|
|
2.1 Introduction 3
|
|
|
|
2.2 Format of a Device Driver 4
|
|
|
|
2.3 How to Create a Device Driver 5
|
|
|
|
2.3.1 Device Strategy Routine 6
|
|
|
|
2.3.2 Device Interrupt Routine 6
|
|
|
|
2.4 Installing Device Drivers 7
|
|
|
|
2.5 Device Headers 8
|
|
|
|
2.5.1 Pointer to Next Device Field 8
|
|
|
|
2.5.2 Attribute Field 9
|
|
|
|
2.5.3 Strategy and Interrupt Routines 11
|
|
|
|
2.5.4 Name Field 11
|
|
|
|
2.6 Request Header 11
|
|
|
|
2.6.1 Length of Record 12
|
|
|
|
2.6.2 Unit Code Field 12
|
|
|
|
2.6.3 Command Code Field 12
|
|
|
|
2.6.4 Status Field 13
|
|
|
|
2.7 Device Driver Functions 14
|
|
|
|
2.7.1 The Init Function 15
|
|
|
|
2.7.2 The Media Check Function 17
|
|
|
|
2.7.3 The Build BPB Function 19
|
|
|
|
2.7.4 The Read or Write Function 20
|
|
|
|
2.7.5 The Non-destructive Read, No Wait Function 22
|
|
|
|
2.7.6 The Open or Close Function 23
|
|
|
|
2.7.7 The Removable Media Function 24
|
|
|
|
2.7.8 The Status Function 24
|
|
|
|
2.7.9 The Flush Function 25
|
|
|
|
1
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
2.7.10 The Generic IOCtl Function 25
|
|
|
|
2.7.11 The Get/Set Logical Drive Map Function 26
|
|
|
|
2.8 The Media Descriptor Byte 26
|
|
|
|
2.9 Format of a Media Descriptor Table 27
|
|
|
|
2.10 The CLOCK Device 29
|
|
|
|
2.11 Anatomy of a Device Call 29
|
|
|
|
2.12 Two Sample Device Drivers 30
|
|
|
|
|
|
|
|
2
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|
|
_ ______________
|
|
|
|
|
|
54
|
|
|
|
_ _ | | _ _
|
|
|
|
|
|
|