This write-up deals with character devices under MS-DOS and related operating systems.
All device drivers under DOS fall into one of two categories: block device and character device. The latter is the focus of this write-up. Character devices are designed to allow user and application access to a physical device. However, because of their convenient place in traditional DOS load order (after kernel, before user programs), they are often used to load services that require initialization before other resident programs. In other words, they are frequently used to serve as glorified TSRs.
Device drivers are distributed as files, usually with a .SYS extension, although any extension is allowed. Despite its extension, CONFIG.SYS is not a device driver. Rather, CONFIG.SYS is a text file parsed at boot time by the MS-DOS kernel. It contains several important configuration settings, including any number of DEVICE statements that indicate a device driver to load.
Device driver files behave much like ordinary programs: they accept parameters, they have an entry point. However, their underlying format is quite different. The beginning of the device driver file must be a device driver header, which specifies, among other information, the address of the driver dispatch routine, and the name of the phantom file. Multiple device headers may be included in a single driver file, allowing creation of unlimited number of phantom files (or as many as you can fit in the 64K you're allowed to use). EXE-format files are allowed as well, and the MZ header is skipped if it is found. The fact that an MZ header is allowed, though, means that device drivers can have several entry points, allowing multiple uses. A good example of this is the SETVER.EXE utility shipped since DOS 5.0. It can be loaded as a device driver, or can be run from the command line as a program. COM-format device drivers may not have multiple entry points, since .COM programs expect to enter at the beginning of the image, which is already reserved for use by the driver header.
Let us return to the notion of phantom file introduced without explanation above. This notion is one borrowed (poorly) from Unix system. The idea is that the user should be able to refer to character devices (that is, devices that perform I/O on a stream of characters) through an object that behaves in all ways like a file, but whose I/O is directly mapped to that device, rather than to a file on disk. In recent use, very few drivers actually implement this functionality, but it is not without its uses.
Although character device drivers may expose an interface to applications in any number of ways, including through interrupts, they all provide some minimal, and often entirely defunct, interface to the user in the form of phantom files. When referring to character device files, it is for some reason traditional to suffix them with a colon (for example, CON:). Some DOS commands, such as MODE, encourage this seemingly meaningless syntax.
In addition to the character devices supplied by loaded device drivers, the kernel itself (IO.SYS) provides some. Character device drivers phantom files built-in to the MS-DOS kernel include:
- CON: Maps to the screen for write, to the keyboard for read. CON is short for console.
- NUL: Data sink. Always empty on read, disposes of data written. Corresponds to /dev/null in Unix.
- LPT1, LPT2, LPT3: Maps to the first three printer ports. LPT stands for Line PrinTer.
- COM1, COM2, COM3, COM4: Maps to the first four serial communication ports.
- AUX, PRN: Maps to the first serial communication port and first printer port, respectively.
Unlike their Unix equivelents, DOS phantom files are accessible in every directory. Therefore, it is not possible to create, open, delete, or rename a file that has the name of a currently installed phantom file. In addition, the semantics of phantom files ignore extensions; therefore CON.XXX exists, as well as CON.YYY. If you do have a file that you need to work with that has the name of a phantom file, it will be necessary to remove that device driver, reboot, and then manipulate the file. If the file has the name of a built-in phantom file, it may be necessary to patch the kernel, or use a direct disk editing program like Norton DiskEdit.
Later versions of DOS (including the DOS distributed with Windows) install optional drivers like SETVER and HIMEM automatically, and these drivers, as well, create phantom files. The name of the phantom file created by a device driver can be determined by examining the driver header at the beginning of the file. Also, some programs will examine the DOS driver chain and will be able to tell you which phantom files are currently resident.
Later versions of DOS also include CONFIG$ and CLOCK$, and others depending on which optional device drivers you have installed. Windows, including those based on the NT codebase, and OS/2, also have a superset of those character device drivers supported by DOS.
It should be noted the well-known way of using phantom files to determine if a directory exists. The IF EXIST syntax is frequently used in DOS batch files to determine if a file exists; however, this does not work for directories. However, if a directory exists, then all currently loaded phantom files will exist within that directory. Therefore, to test if the directory C:\FOO exists, use IF EXIST to test for C:\FOO\NUL.
Another interesting special case worth noting is the failure that some application programs have with phantom files. There is a well-known bug in some version of Netscape and the Windows file system driver that cause it to crash horribly if it attempts to open the file C:\CON\CON.