Virtual Developer Workshop: Containerized Development with Docker
Systems programming has always been an attraction to the programming geeks. Gone are the days when we used to use DOS, but DOS is still a programmer's heaven. Technology is churning out more sophisticated and intricate tools to help out research and development, yet you need to retrace your footprints to the realm of DOS. This is a research article and the main focus is on the DIB (Dos Info Block). I have formed an easy-to-use structure and chalked down many areas of DIB to facilitate the programmers for system and low-level programming. Only a few people know what the DIB structure is and what kind of information it contains. When I tried to utilize the Path Table and Device Parameter Block, I found very little information about it and I feel the need to synthesize these undocumented areas.
Hitherto, many reference books discuss undocumented information about DOS, but I have found some DOS structures still undocumented—such as the DOS Info Block (DIB). In fact, certain kinds of programming depend on this structure and some applications couldn't be created without them. For example, hard disk management utilities extensively use these undocumented structures.
The DOS Info Block (DIB)
The DOS Info Block (DIB) is the key to accessing the most important DOS structures. This block contains pointers to several DOS structures and to other information. The DIB is useful to a program only if its address in memory is known. This address isn't in a fixed memory location, and cannot be obtained with any of the documented functions of DOS interrupt 21H. However, the undocumented function 52H can help locate this address. Calling function 52H returns the address of the DOS Info Block to the ES: BX register pair.
Interrupt 21H, Function 52H
Determines the pointer to the DOS-Info-Block.
This function returns a pointer to the DOS-Info-Block (DIB). With this function, much interesting information can be obtained, which otherwise would not be accessible to a program.
|Input:||AH = 52H|
|Output:||Carry-Flag = 0: ok, in this case|
ES: BX = FAR-pointer to the DIB
Carry-Flag = 1: Error, in this case
AX = Error Code
The contents of the CX, DX, SI, DI, BP, CS, DS, and SS registers are not affected by this function.
Unlike other DOS functions that retrieve pointers to a structure or date area, the contents of the ES: BX register pair point to the second, instead of the first, field within the DIB after the function call.
The first field in the DIB contains a pointer to the Memory Control Block (MCB) of the first allocated memory area.
|DOS Info Block (DIB) Structure|
|-04H||Pointer to MCB||1 ptr|
|ES:BX||Pointer to first Drive Parameter Block (DPB)||1 ptr|
|+04H||Pointer to last DOS buffer||1 ptr|
|+08H||Pointer to clock driver (CLOCK)||1 ptr|
|+0CH||Pointer to console driver (CON)||1 ptr|
|+10H||Sector length (based on connected drives)||1 word|
|+12H||Pointer to first DOS buffer||1 ptr|
|+16H||Pointer to path table||1 ptr|
|+1AH||Pointer to System File Table (SFT)||1 ptr|
|Length: 30 bytes|
Drive parameter block
The pointer in the second field of the DIB provides access to information that cannot be accessed in any other way. It points to the first Drive Parameter Block (DPB), which is a structure that DOS creates for all mass storage devices (floppy diskettes, hard disks, tape drives, and so forth).
|Drive Parameter Block (DPB) Structure|
|+00H||Drive number or character (0 = A, 1 = B, and so on)||1 byte|
|+01H||Sub-unit of device driver for drive||1 byte|
|+02H||Bytes per sector||1 word|
|+04H||Interleave factor||1 byte|
|+05H||Sectors per cluster||1 byte|
|+06H||Reserved sectors (for boot sector)||1 word|
|+08H||Number of File Allocation Tables (FATs)||1 byte|
|+09H||Number of entries in root directory||1 word|
|+0BH||First occupied sector||1 word|
|+0DH||Last occupied cluster||1 word|
|+0FH||Sectors per FAT||1 byte|
|+10H||First data sector||1 word|
|+12H||Pointer to header (corresponding device driver)||1 ptr|
|+16H||Media Descriptor||1 byte|
|+17H||Used flag (0FFH = Device not yet enabled)||1 byte|
|+18H||Pointer to next DPB (XXXX: FFFF = last DPB)||1 ptr|
|Length: 1CH (28) bytes|
The first field of the DPB indicates the device to which the block belongs. 0 represents drive A, 1 represents B, 2 represents C, and so on. The second field specifies the number of the subunit. To understand the meaning of this field, remember that access to the individual devices occurs through the device driver. DOS doesn't perform direst access to a disk drive or hard disk. So, DOS doesn't have to deal with the physical characteristics of a mass storage device. Instead, DOS calls a device driver, which acts as mediator between DOS and hardware.
Obviously, not every device has a separate device driver, because one device driver can support many devices. For example, the device driver built into DOS manages the floppy disk drives and the first available hard disk. Because DOS configures a DPB for each device, a hard disk system automatically has 3 DPBs available. (A DPB is always configured for floppy disk drive B, even if only one floppy disk drive is actually available.) Each device receives a number between 0 and the total number of devices minus 1, to help each driver identify the devices it manages. This is the number found in the subunit field.
The next field lists the number of bytes per sector. Under DOS, this is usually 512. After this is the interleaf factor, which provides the number of logical sectors displayed by physical sectors when the medium is formatted. This value can be 1 for floppy disk drives, 6 for the XT hard disk, and 3 for the AT hard disk. For floppy disk drives, this field can also have the value FEH if the disk in the drive hasn't been accessed. The value FEH indicates that the interleaf factor is currently unknown.
Several other fields are related to these two fields. Among other things, these fields describe the status and the size of the structures DOS created to manage mass storage devices. A pointer to the header of the device driver is located within these fields. DOS uses this pointer when accessing the device. Additional information can be obtained with this pointer because, for example, the driver attribute is listed in the header of the device driver.
Following this field is the media descriptor to which the used flag is connected. As long as the device hasn't been accessed, this flag contains the value 0FFH. After the first access, it changes to 0 and remains unchanged until a system reset. The DPB ends with a pointer that establishes communication with the next DPB. Because every DPB defines its end with such a pointer, a kind of chain is created, through which all DPBs can be reached. To signal the end of the chain, the offset address of this pointer in the last DPB contains the value 0FFFFH.
When a program needs the information within the DPB, there are many ways to find the address of the desired DPB. One method is to follow the chain previously described by first determining the address of the DIB. This gives you the pointer to the first DPB, from which you can follow the chain until you reach the desired DPB.
However, there's a better method, which isn't as susceptible to changes within the DIB. This method involves two undocumented DOS functions, 1FH and 32H functions. Although these functions have been included in DOS since Version 2.0, Microsoft didn't document them. When called, both return a pointer to a DPB to the DS: BX register pair. Although function 1FH always delivers a pointer to the DPB of the current disk driver, the address delivered by function 32H refers to the device whose number is passed to the function in the DL register at the time it's called (0 represents the current drive, 1 is drive A, 2 drive B, and so forth). Function 32H is much more flexible than function 1FH.
Interrupt 21H, Function 1FH
Gets the DPB pointer to the current drive.
Gets the pointer to a DOS Parameter Block for the current disk drive.
|Input:||AH = 1FH|
|Output||AL = 000H: Operation successful|
AL= 0FFH: Error
DS: BX = FAR pointer to DPB structure
Interrupt 21H, Function 32H
Gets the DPB pointer for any drive.
Gets the pointer to a DOS Parameter Block for any disk drive.
|Input:||AH = 32H|
DL = Drive code (0 = current, 1 = A, 2 = B, etc.)
|Output||Carry flag = 1: Error|
Carry flag = 0: Ok
DS: BX = FAR pointer to the DPB
If the device indicated is a diskette drive, DOS accesses the drive to fill the DPB with data about the drive and the format of the diskette. For hard disks, this information is already stored in the memory and no hardware access is required.
An error during the function call can only occur if an invalid drive code was indicated. If this occurs, error code 15 is returned in the AL register.
The construction of the DOS Parameter Block varies between DOS versions. The contents of the AH, CX, DX, SI, DI, BP, CS, SS, and ES registers are not affected by this function.
Using 1FH and 32H to access the various DPBs is also useful because it forces DOS to retrieve other information, such as the interleaf factor and the media descriptor byte, which is determined for the disk drive only after the first access. If you get to the DPB through the pointer in the DIB block, the various fields may not have been initialized, and could contain the wrong values.
The DOS Buffer
Besides the pointer to the first DPB, the DIB also contains the pointer to the first DOS buffer at address 12H. These DOS buffers store individual sectors, so that the sectors don't have to be repeatedly loaded from disk. The DOS buffers are most effective when they're used to store disk sectors that are frequently needed by the currently running program. Besides the FAT, these sectors include the root directory and its subdirectories. The number of buffers can be defined by the user in the CONFIG.SYS file. If this number exceeds those needed for the FAT, root directory, and subdirectories, normal sectors can also be temporarily stored here. This is done so that, if they are called again in the near future, they can be taken directly from the buffer.
The individual sectors are linked together. This enables DOS to quickly check each buffer for the desired sector with each read operation.
|DOS Buffer Structure|
|+00H||Pointer to next DOS buffer||1 byte|
|+04H||Drive number (0 = A, 1 = B, and so on)||1 byte|
|+06H||Sector number||1 word|
|+0AH||Contents of buffered sector||512 bytes|
|Length: 210H (528) bytes|
As with DPBs, this occurs with the help of a pointer that appears at the start of every buffer. Also, the last buffer is reached when the offset address of the pointer contains the value 0FFFFH. After the field linking one buffer to the next is the number of the drive where the buffered sector originates. The value is 0 for drive A, 1 for B, 2 for C, and the like. Besides the drive number, the identification of a sector requires a sector number. This is located beginning at position 06H in the DOS buffer. The last field in the buffer header stores a pointer to the corresponding DPB, so that DOS can obtain information about the device that loaded the buffered sector. Although this is the last field in the header of the DOS buffer, the buffered sector doesn't end immediately after this field. There are two more bytes that follow. The reason for this is that the DOS code is written in machine language. So, when working with memory blocks, it's most efficient to have the buffered sector begin with an address that is divisible by 16.
The Path Table
The header of the DOS buffer isn't the last place you encounter the DPB. It appears again in the path table, which starts at address 16H in the DIB. This table contains the current path for each drive as well as a pointer to its DPB.
As long as the LASTDRIVE command is in the system's configuration file, the table will have entries for drives A through the one specified by LASTDRIVE. If this command is missing, however, the table will have entries only for each device supported by the installed device driver(s). If you change the entries in this table, you can divert one drive to another. The JOIN and SUBSTDOS commands also utilize this by manipulating the path table entry of the drive to be diverted.