MS-DOS(R)Q&A

by Jeff Prosise

Jeff Prosise writes extensively about DOS programming and is a contributing editor to several computer magazines. He is the author of DOS 5 Techniques and Utilities (Ziff-Davis, 1991).

{ewc navigate.dll, ewbutton, /Bcodeview /T"Click to open or copy the code samples from this article." /C"samples_1}

QI’m curious. Does MS-DOSÒ 5 provide any support at all for reading a disk’s serial number, which is stored in the boot sector right after the BIOS Parameter Block (BPB)?

AIf you’ve browsed through the MicrosoftÒ MS-DOS Programmer’s Reference, which was recently updated for MS-DOS operating system version 5, you may have noticed the documentation for a pair of new IOCTL functions--function 440DH minor code 66H, Get Media ID (also referred to as function 440D-66H), and function 440DH minor code 46H, Set Media ID (440D-46H). These functions read and write the volume information stored in a disk’s boot sector. Both functions existed in MS-DOS1 version 4 but were undocumented.

Function 440D-66H takes as input a function code in AX (440DH), the minor code in CL (66H), a device category code in CH (08H for disk drives), a drive code in BX (0=default, 1=a:, 2=b:, and so on), and an address in DS:DX. This is the address of a 25-byte buffer that is filled with a structure of type MediaID (or MID) when the function returns (see Figure 1). The word at offset 00H of the MID structure, described only as "information level" in the Programmer’s Reference, is reserved. The doubleword at offset 02H contains the disk’s serial number; the field beginning at offset 06H contains an 11-byte ASCII string (padded with spaces) denoting the volume label. The final field in the structure, at offset 11H, contains an ASCII string (again padded with spaces) that identifies the file system used for the block device. At present, this field will either contain FAT12 or FAT16, identifying a 12- or 16-bit FAT file system. Presumably, this field could be used to identify other MS-DOS-supported file systems (including installable file systems) in future releases.

Figure 1 The MS-DOS MediaID Structure

Offset Length Description

00H 1 word Information level (should be 0)
02H 1 dword Serial number (8-digit hex number)
06H 11 bytes Volume label (ASCII format)
11H 8 bytes File system ID (ASCII format)

FAT12 = 12-bit FAT

FAT16 = 16-bit FAT


The counterpart to Get Media ID, function 440D-46H, writes the data stored in an MID structure to the boot sector of the disk in the specified drive. On entry, AX holds the function code, CL holds the minor code, CH holds the device category (again 08H), BX holds a drive code, and DS:DX points to the MID structure. When calling this function, the first word in MID should be set to 0. On return, the carry flag is clear if the function succeeded or set if it failed.

These functions make it feasible for an MS-DOS program to identify a disk by its serial number or volume label without having to resort to sector-level reads. This could be useful for setup programs or other programs that ask for a certain disk and must recognize if the wrong disk was inserted. The following assembly code illustrates how a program would read the serial number of a disk in drive a: using the MS-DOS 5 Get Media ID IOCTL function, aborting if the serial number is other than 5A5A-0001.

MIDStruc STRUC

InfoLevel dw ?

SerialNumber dd ?

VolumeLabel db 11 dup (?)

FileSystemID db 8 dup (?)

MIDStruc ENDS

MediaID MIDStruc <>

o

o

o

mov ax,440Dh

mov bx,01h

mov cx,0866h

mov dx,seg MediaID

mov ds,dx

mov dx,offset MediaID

int 21h

jc IoctlError

cmp word ptr MediaID.SerialNumber,0001h

jne WrongDisk

cmp word ptr MediaID.SerialNumber[2],5A5Ah

jne WrongDisk

The carry flag will return set if an invalid drive number or function code was specified, if the disk doesn’t contain a valid BPB, or if the extended BPB signature (a byte with the value 29H at offset 26H from the beginning of the boot sector) is missing. Disks formatted with versions of MS-DOS prior to 4 will not contain an extended BPB signature. If critical errors are a concern (say the function calls are directed to drives that support removable media), the program should take the precaution of setting up a critical error handler through interrupt 24H before calling the Get or Set Media ID functions.

One nuance you should be aware of is that the volume label that’s set and returned by these functions won’t necessarily match the volume label reported by MS-DOS commands such as VOL, DIR, and CHKDSK. What you normally think of as the volume label is stored as a 32-byte entry in the disk’s root directory, with the volume label attribute bit set to 1. The "volume label" handled by functions 440D-46H and 440D-66H is stored in the disk’s boot sector at offset 2BH and doesn’t necessarily match what, if anything, is stored in the root directory. As long as you use MS-DOS utilities such as format and label to set the volume label, the two should stay in sync. To read the real volume label, programs should still call function 4EH (Find First File) with bit 3 of the file attribute word in CX set to 1.

QIs there a documented method for application programs to determine whether the MS-DOS 5 kernel is loaded in the High Memory Area (HMA) or in conventional memory?

AInterrupt 21H, function 3306H returns two bit flags in DH that indicate where MS-DOS is loaded and whether the kernel is located in RAM or in ROM. Figure 2 shows the mapping of the bits. If bit 3 is clear, MS-DOS is in RAM; if the bit is set, MS-DOS is loaded in ROM. If bit 4 is clear, the MS-DOS kernel is loaded low; otherwise, it’s loaded in the HMA. All other bits are reserved.

Figure 2 Flags Indicating Location of MS-DOS 5

Function 3306H, introduced in MS-DOS 5, returns other information about MS-DOS. On return, BL holds the major version number of the MS-DOS version that’s running (for example, the 5 in 5.0) and the minor version number in BH. On the surface, this might seem unnecessary, since function 30H also returns version information. However, function 3306H is not affected by the SETVER command, while function 30H is. In addition, function 3306H returns a value in DL that reveals the MS-DOS revision number. Only the lower three bits are significant; the others are reserved and should be ignored.

MS-DOS 5 documents another subfunction to 33H, function 3305H, that will also be of interest to programmers. This function actually existed in MS-DOS 4 but was undocumented. The function takes no parameters other than the function and subfunction numbers in AH and AL; it returns a drive code in DL (1=a:, 2=b:, and so on) that identifies the startup drive--the drive that MS-DOS was loaded from. This API call will be particularly useful for setup programs that need to examine the contents of CONFIG.SYS or AUTOEXEC.BAT to recommend or perform configuration changes. In previous versions of MS-DOS, there was no documented way to determine what drive the operating system was loaded from. If MS-DOS is in ROM, function 3305H returns the number of the drive that contains CONFIG.SYS.

QEveryone knows you read the command line from an application program by inspecting the information MS-DOS places at offset 80H in a program’s Program Segment Prefix (PSP). But how do you read information passed to a device driver on the line that loads it in CONFIG.SYS? Examining the information stored at DS:[80H] doesn’t seem to work.

AIt’s easy once you know how. For an application program, MS-DOS makes the text entered on the command line available by copying it to the program’s PSP. The byte at offset 80H holds the count of characters entered, and the succeeding bytes hold the text itself. The end of the command line is marked with the carriage return character, ASCII 13. It’s easy to find the PSP, because both DS and ES point to it when an EXE file is loaded. All four segment registers point to the PSP when a COM file is loaded, so using DS:[80H] as a reference is always sound as long as DS remains as it was when the program was started.

For a device driver, MS-DOS makes command line text available by passing the driver a pointer to the command line (actually the line that loaded the driver in CONFIG.SYS) when it calls the driver’s INIT routine at startup. As usual, MS-DOS calls the strategy routine first with the address of a request header in ES:BX. Then it calls the driver’s interrupt routine. The doubleword at offset 12H in the request header holds the address of the first character following "DEVICE=" on the line that loaded the driver. Characters are converted to uppercase as if passed to a conventional application. There is no count byte, but the line is delimited by a carriage return or linefeed character. To get to the first parameter entered after the driver name, you must parse the string, scanning past the driver name and any path information that precedes it. A driver shouldn’t alter the text at this location, because the address that MS-DOS passes to it is the actual address of the line in a text buffer internal to the data space in MS-DOS, not the address of a copy.

QI’d like you to tell me how the bit masks whose addresses are passed to function 9 of the Microsoft mouse driver (MOUSE.COM or MOUSE.SYS) set the shape and color of the mouse cursor in graphics mode. The Microsoft Mouse Programmer’s Reference notes that the screen mask "determines whether the cursor pixels are part of the shape or part of the background," while the cursor mask "determines how the pixels under the cursor contribute to the color of the cursor when the video adapter is in text mode." Exactly what does this mean? How would you go about writing a program that changes the graphics cursor to, say, an hourglass? Futhermore, it looks like you can only use two colors--black and white--in the graphics cursor. Is it possible to use others?

AWhen you call mouse function 9 (Set Graphics Cursor Block) to set the characteristics of the mouse cursor in graphics mode, you pass it three parameters. BX and CX hold the horizontal and vertical coordinates of the cursor hot spot (the pixel that determines what position the mouse driver reports when a program asks for the location of the graphics cursor) relative to the upper-left corner of the cursor block. ES:DX points to the location of a pair of 16 x 16 bit arrays that determine how the cursor is mapped onto the screen. The first array, called the screen mask, is logically ANDed with the contents of video memory to shape the cursor. The second array, the cursor mask, is XORed with the corresponding bits in video memory, enabling you to toggle individual bits on and off. Each bit in the mask arrays corresponds to one pixel on the screen. The two masks together determine the final shape and appearance of the mouse cursor.

To create a mouse cursor shaped like an hourglass, you’d define a screen mask that contains a filled outline of the hourglass rendered in 0s against a field of 1s (see Figure 3).

When the mouse driver acts upon this information, every bit in video memory ANDed with a 0 is set to 0, and every bit ANDed with a 1 remains unchanged. In graphics modes that use multiple bits to represent the color of the pixel (for example, in 16-color EGA and VGA screen modes, where there are four bits assigned to each pixel), every bit that determines the pixel’s color is ANDed with a 0 if the screen mask contains 0 or with a 1 if the screen mask contains 1. Thus, the color of pixels paired with 0s in the screen mask is set to 0, while the color of other pixels is unaffected.

The next step is to define a cursor mask that renders the desired hourglass image in the region zeroed by the screen mask. The second mask values in Figure 3 produce a white hourglass outlined in black. Because the bits assigned to the pixels inside the hourglass were previously set to 0, bits XORed with 1 become 1 and bits XORed with 0 remain 0. In 16-color graphics mode, pixels XORed with 1 become 0FH (1111 binary), which by default is white. This would not be true had the pixels not been zeroed by the screen mask first. If a pixel’s color is other than 0, a cursor mask value of 1 inverts the color by flipping the bits. The MCURSOR program (see Figure 3) switches a VGA adapter to 640 x 480 16-color mode and sets the graphics cursor using the bit masks listed above. The output of MCURSOR is shown in Figure 4.

Figure 4 The hourglass cursor of MCURSOR.

Another way to think about the interaction of the screen mask and the cursor mask is to realize that there are four things you can do to any pixel that falls within the 16 x 16 pixel cursor block. You can set it to 0, you can set all the bits in its color value to 1, you can invert it (reverse its color), or you can leave it alone. A screen mask value of 0 and a cursor mask value of 0 sets the pixel’s color to 0; a screen mask of 0 and a cursor mask of 1 sets all the bits in the color value to 1; a screen mask of 1 and a cursor mask of 1 inverts the color, changing all 1s to 0s and all 0s to 1s; and a screen mask of 1 and a cursor mask of 0 leaves the color unchanged (see Figure 5).

Figure 5 Mask Values Determine Pixel Color

Screen Mask Cursor Mask Pixel Color

0 0 Black
0 1 White
1 0 Transparent
1 1 Inverted

On the surface, you only have two colors to choose from: black and white. Black because 0 normally corresponds to black; white because setting all of a pixel’s color bits to 1 normally produces white. But there’s one thing left to be considered. On EGA, MCGA, and VGA adapters, these values aren’t the final color determinants; instead, they’re merely indexes that refer to color values stored in the adapters’ palette registers. Therefore, you can select other colors by rewriting the palette registers. In C, you’d accomplish this with the _remapallpalette or _remappalette functions. The limitation is that all other pixels on the screen with the same color indexes will change, too. One solution to this admittedly sticky problem is to reserve the highest and lowest color index numbers (for example, colors 0 and 15 on an EGA or VGA) for the mouse cursor. Then the palette settings for those colors could be manipulated without affecting other parts of the display.

One caveat you should be aware of when programming the mouse graphics cursor in MicrosoftÒ C concerns the _clearscreen function. This function works by zeroing out all the color bits. You can call _setbkcolor to assign a new color value to palette register 0. This sets the color the screen will be cleared to when you specify the color index 0. However, if you use _setbkcolor to set the background color to anything other than black and then call _clearscreen before calling mouse function 9, you may not get the cursor you expect because color number 0 will no longer be black. To preserve the palette register settings and prevent them from adversely affecting the mouse cursor, you should avoid _clearscreen and clear the screen to the desired color by drawing a filled rectangle.

1For ease of reading, "MS-DOS" refers to the Microsoft MS-DOS operating system. MS-DOS is a trademark that refers only to this Microsoft product.