Geoff Chappell - Software Analyst
The settled form of this function reports which version, if any, of the Virtual Machine Manager (VMM) is running. The VMM was distributed with Windows 3.0 and higher as the operating system that underpins Windows Enhanced Mode, which by Windows 95 was the only mode that remained, and the VMM version number is in practice also the Windows version number. It is presumably from this interpretation that the subfunction number 00h gets its extant symbolic name W386_Get_Version, as defined in a header named INT2FAPI.INC which Microsoft distributed with the Device Driver Kit (DDK) from at least as far back as Windows 3.1.
Interpretation of int 2Fh function 1600h as a version enquiry does not apply to the pre-history of Windows versions before 3.0. The different implementation in Windows/386, in what was plainly the VMM’s immediate ancestor, is not incompatible with the VMM’s but neither is it as straightforwardly reconciled as might be thought from some listings on the Internet. As if this were not enough complication, it has long been rumoured that early builds of HIMEM.SYS have yet another implementation of int 2Fh function 1600h. I do not doubt the rumours, but I have never seen one of these early builds. The earliest HIMEM that I know to have been formally released, which is version 1.11 in Windows/286 version 2.10, already implements int 2Fh function 4300h and does not defend against the presence of any earlier version that implements int 2Fh function 1600h. It’s all very untidy, to say the least, not only for whatever Microsoft thought it was doing by way of overloading the interface but for how none of the industry’s observers at the time, myself included even though I didn’t start until 1989, seem to have given it any attention beyond gossip dressed as folklore.
The VMM implements int 2Fh function 1600h for both virtual-8086 and protected-mode callers. In Windows/386, what was then named the VDMM does not implement a DOS Protected Mode Interface (DPMI), or anything like it, and so its implementation of int 2Fh is for virtual-8086 callers only.
The function uses registers for both input and output. Segment registers, which matter only in version 2.01, are for real-mode addressing only.
|ax||1600h||2.01 and higher|
|bx||bl is interrupt number for mouse, else bx is FFFFh||2.01 only|
|es:di||return address for VDA calls||2.01 only|
All implementations except the first-known have the function number as the one and only input. The additional input for the early interface was reworked into int 2Fh function 1601h at least as soon as Windows/386 version 2.03. It is here thought that this complexity of int 2Fh function 1600h in version 2.01 was already an unwanted survival of first thoughts, as if the additional input’s separation to function 1601h had been planned concurrently with the creation of int 2Fh function 1602h but got delayed.
The input for bx is conveniently what the MouseGetIntVect function, as exported from MOUSE.DRV, returns in ax.
The input for es:di is given in anticipation of getting back an address in es:di. Executing at the output address will appear to the virtual-8086 caller like a jmp to the input address, after a transition to and from the 32-bit system in ring 0. The simplest use is to execute the output address by a far call which is then balanced by a far ret at the input address. Symbol tables in the various WIN386.386 binaries name the output address as VDA_Call_Adrs, VDA presumably standing for Virtual DOS Application.
|al||FFh for System VM||2.01 to 2.11|
|01h for other VM||2.03 to 2.11|
|80h||HIMEM.SYS 1.00 to 1.10|
|major version number of VMM||3.0 and higher|
|ah||minor version number of VMM||3.0 and higher|
|es:di||address of entry point for VDA calls||2.01 only|
The implementation for Windows/386 version 2.01 is shown above as returning FFh in al but it is returned as the whole of eax. Whether this is by design for the interface, or is a coding error, is unclear. Whatever the intended width of the returned value, it is returned only if the call is made in what is known for later versions as the System VM (and here means specifically the virtual machine whose VM ID is 1).
The successor implementations for Windows/386 are very careful to set only al but there are now two different possible values: FFh when called from the System VM, as before; but 01h from other VMs, which extends the earlier behaviour.
The entry point whose address the first-known implementation returns in es:di is exposed in later versions through int 2Fh function 1601h. Either way, it takes input in ax. The high byte selects a system component—what would later be either the VMM or a separate Virtual Device Driver (VxD)—and the low byte is then a function number. Interpretation of other registers depends on the component and function numbers. The very many valid combinations are presented separately, as the Windows/386 VDA Interface.
In the VMM from Windows 3.xx, the function’s implementation returns whatever major and minor version numbers it obtains from the VMM service Get_VMM_Version. Put another way, it necessarily returns to its ring 3 callers the same VMM version that is perceived by VxDs in ring 0.
The VMM that Microsoft distributes with Windows 95 ordinarily returns a hard-coded 0004h, meaning version 4.0, but it can instead fail the version enquiry, as if pretending to be not present, or return a hard-coded 0A03h, as if pretending to be the VMM from Windows 3.1. See below, under the heading Quirks.
If int 2Fh function 1600h is executed without the VMM being present, the outcome is by definition not in the VMM’s hands. The ordinary expectation is that no other software expresses an interest and the interrupt returns with no change of registers. This absence of implementation would not be worth mentioning as failure except that the VMM from Windows 95 can simulate it.
As already noted, the implementation for Windows/386 2.01 explicitly fails int 2Fh function 1600h, in this sense of leaving registers unchanged, unless called from the System VM.
The VMM that is distributed with Windows 95 does not necessarily return its true version or any version. Both these unusual cases are plainly by design.
The VMM from Windows 95 leaves all registers unchanged if the current virtual machine (VM) has been configured suitably and the current DOS process of the int 2Fh caller is not named WIN.
The suitable configuration is that the VM had the PRGINIT_WINLIE (0x0004) flag in the flPrgInit member of the PROPPRG structure for the VM’s GROUP_PRG properties as initially communicated to the SHELL VxD. The ordinary source of this configuration is a Program Information File (PIF), specifically from having a set fWinLie (01000000h) flag in the PfW386Flags member of the PIF’s W386PIF30 structure. These flags are typically, if not always, at offset 01C9h in the PIF. User-interface support is available from the PIF Manager as a checkbox labelled “Prevent MS-DOS-based programs from detecting Windows” in the Advanced Programs Settings dialog.
The current DOS process is represented by the Program Segment Prefix (PSP) whose segment address is at offset 0330h in the DOS kernel’s data. The 10h-byte paragraph immediately before the PSP is assumed to be a memory arena header with a program name at offset 08h. If this name is WIN, as three case-insensitive characters and a null byte, then the PRGINIT_WINLIE configuration is ignored. Presumably, the intention is that even in a VM that has been configured so that the DOS programs within do not perceive that they are running in Windows Enhanced Mode, an attempt at running WIN.COM would better still fail for noticing that Windows Enhanced Mode is already running.
The VMM for Windows 95 can have int 2Fh function 1600h return a hard-coded 0A03h, in effect pretending to be the VMM from Windows 3.1. This occurs if the bytes at the address that the interrupt would return to are exactly 3Ch, 03h, 75h, 0Ch. These are the bytes of a cmp al,03h instruction followed by a jnz that would jump 0Ch bytes if the VMM’s major version is anything but 3. That Microsoft checks for these bytes exactly is surely a special case made as a run-time bug fix or for compatibility (depending on perspective and generosity). The caller apparently needs to see 3 as the major version and is important enough to Microsoft that it should have its need met even when the VMM is version 4.
A problem with this, of course, is that these particular four bytes of instructions after the int 2Fh do not imply a need to see 3 as the major version. Imagine some program that is written for Windows 95 as its preferred version. It may check for major version 3 with the intention of gracefully adapting its behaviour if run on a lesser Windows. If it happens to do so with exactly the instructions of Microsoft’s special case, then its behaviour will be degraded needlessly. It is not known whether anyone’s code ever was stricken unintentionally by this false return of 3.10 as the VMM version. On the plus side is that this behaviour that would be very much unwanted by them would have been noticed immediately during their pre-release testing, and thus not have imposed a larger cost for responding to customers. Still, it could have been quite a head-scratcher for their programmers. Would Microsoft have helped, I wonder?
What is known about programs that were affected by this compatibility provision is that one of Microsoft’s own has exactly the expected sequence. The program is KRNL386.EXE from Windows 3.1. This is the 80386-specific version of the DOS program that starts the Windows GUI. It can be run under DOSX.EXE for Standard Mode or under the VMM for Enhanced Mode. To learn which, KRNL386 looks for whether int 2Fh function 1600h sets al to 03h, exactly. The inference it draws from this then has wide consequences, not least for what the GetWinFlags function tells arbitrary Windows programs of whether Windows is in Standard or Enhanced Mode.
It’s the sort of coding error that might never have mattered. KRNL386.EXE and the VMM (inside WIN386.EXE) are in practice distributed together and installed together. Microsoft’s programmers of KRNL386 for Windows 3.0 had already assumed that it would only ever be run on Windows 3.0 and so any VMM that answers int 2Fh function 1600h at all will answer as version 3.00 exactly, returning 0003h in the whole of ax, and any other answer means no VMM. This last part, and the inference of Standard Mode, is in practice no error as long as the KRNL386 from Windows 3.0 is only ever run on Windows 3.0.
For Windows 3.1, Microsoft’s programmers relaxed the check but kept the inference. Assume that the KRNL386 from Windows 3.1 will only ever be run on some Windows 3.xx, and then any VMM that answers int 2Fh function 1600h will answer with 03h in al and any other answer means no VMM. They weren’t alone in having too much else to notice or perhaps even in not caring: Matt Pietrek, for a representation in pseudo-code on page 25 of Windows Internals, ISBN 0-201-62217-3, published by Addison-Wesley in 1993, lets it pass without warning (having immediately before been picky about the code’s method of checking for a numerical coprocessor).
When requiring 3 as the VMM’s major version number for determining that KRNL386 is running for Windows Enhanced Mode may have become a problem is in the early development of the VMM’s version 4.0 for Windows 95. Microsoft’s bright and presumaby well-disciplined programmers of the new VMM will naturally have wanted some assurance that their many changes had not disturbed the execution of anything that the old VMM had run. Certainly the new VMM should be able to run the Windows 3.1 GUI in the System VM at least as well, and hopefully with better performance and more robustness, as did the old VMM. That the Windows 3.1 GUI then says it’s in Standard Mode will have been noticed almost immediately and the faulty VMM version detection by KRNL386 will have been found soon after. For Microsoft’s own testing during this early development, the coding error might well enough have been patched in the KRNL386 binary. The need for a run-time fix by the VMM presumably came later, once it was decided that Chicago beta testers with a new Windows GUI in the System VM could run their old (unpatched) Windows 3.1 GUI in a second VM.
Any number of programs written by any number of programmers might use int 2Fh function 1600h to detect whether they are running under Windows Enhanced Mode. This exposure of the function to arbitrary users is far too wide for a catalogue, but some use by Microsoft’s own programs is notable, e.g., for being first or for knowing more than was yet documented.
The function’s first known use by anyone is for the WINOLDAP.MOD from Windows/386 version 2.01 but its interest is in the complexity of the additional input and the returned access to the VDA interface, which all soon moved to int 2Fh function 1601h. The first known use of the function in its simpler form of having just the function number as its one input is by the WIN386.EXE from Windows/386 version 2.03.
WIN386.EXE is the DOS program that starts Windows/386. It executes in real mode to prepare the protected-mode execution of WIN386.386. This in turn is what the Intel literature of the time names a virtual-8086 monitor, on such a scale that it is in effect an operating system for a set of virtual-8086 machines. In the first of these, it runs WIN86.COM to start Windows, much as might have been done without the WIN386 components. What’s not wanted in this virtual machine, or in any, is an attempt to re-run WIN386.EXE. It won’t have been wanted even in version 2.01, but version 2.03 is when the defence begins. It calls int 2Fh function 1600h as a simple check for whether WIN386.386 is already running. If the function changes al from 00h, no matter what to, then WIN386.EXE complains:
Error: Windows/386 software already installed
By version 2.10, the check is elaborated to allow that a return of 80h in al does not mean that Windows/386 is already running. This is consistent with suppositions that 80h is the answer from early versions of HIMEM.SYS that had been developed for Windows/286. That it is loaded as a DOS device driver in anticipation of running some pre-release Windows/286 should not get in the way of running Windows/386. Indeed, it would not be long before the loading of (later versions of) HIMEM.SYS as a DOS device driver became a requirement for running any Windows.
Not in Windows 3.0, but certainly by Windows 3.1, detection of whether Windows is already running starts with the WIN.COM program. For this, WIN.COM itself implements int 2Fh function 160Ah, such that its easiest detection is of an earlier instance of itself as some ancestor process. Of course, this cannot be the whole detection. A Windows that is already running may be an earlier version or it may have been got running by means other than WIN.COM. For these cases, WIN.COM has more tests. Among them is whether int 2Fh function 1600h leaves 00h in al or sets al to 80h. Anything else is taken as revealing that Windows is already running. This is the same test that had been established years earlier for WIN386.EXE in Windows/386 version 2.10 (see above). What makes this test notable in the Windows 3.1 WIN.COM is that the explanation to the user is not necessarily that
You are already running Windows.
If WIN.COM finds itself running in the System VM, which it can learn from int 2Fh function 1683h, the complaint is instead that
The MS-DOS Protected Mode Interface (MSDPMI) is running on this computer. You cannot start Windows when it is running. To quit the MSDPMI, type exit and then press Enter.
A revision of this output for Windows 95 is less specific, talking only of a “DPMI program”, as if perhaps it’s not Microsoft’s. Either way, there is some inference that int 2Fh function 1600h was implemented separately from the VMM or that the VMM had been available separately from Windows, if only briefly or for a very narrow market.
As no small point for historians, note that DOS users had been clamouring for many years for a multi-tasking DOS, which Microsoft had delivered at least as early as 1987 but had mostly kept for Windows, apparently as anti-competitive leverage: if you want a new and improved DOS, buy Windows instead of its competitors. This error message from WIN.COM, and the attendant mystery of what this MSDPMI ever was, has some significance as a rare sign that Microsoft understood the VMM as separate from Windows.
Andrew Schulman, on page 173 of Unauthorized Windows 95, ISBN 01-56884-305-4, published by IDG Books in 1994, has it that “MSDPMI was a short-lived Microsoft DPMI server and DOS extender from the early days of the Microsoft C/C++ 7.0 (MSC7) beta test”, but although I have no reason to doubt him on this as a source of this MSDPMI program, neither have I ever seen it for myself. The released Microsoft C still required DPMI services but its README.TXT explains that “Windows provides DPMI services for you.”
Decades later, thanks to sites that archive old (and foreign) software as abandon-ware, anyone can now see that the VMM did get at least one formal release independently of Windows. A Japanese MS-DOS 5.00 [NEC PC-9800] has a DPMI.EXE dated 11th November 1992. Like the WIN386.EXE in Windows 3.xx,, it’s a collection in one DOS program of a VMM and assorted VxDs. The VMM is version 3.00. Several VxDs are specialised for the PC-9800 architecture, of course, but the main variation that distinguishes this packaging from Windows is that what the SHELL VxD asks the DOSMGR VxD to execute in the System VM is not KRNL386.EXE, as it would for Windows, but is instead a DOS program named DPMI.SYS which in turn executes the DOS command processor (“executes” here having the peculiar sense of the C Run-Time _exec family of functions).
Incidentally, seeing this message about MSDPMI as real output is easier than you might think. Assume for simplicity that your Windows 3.1 installation is the current drive and directory. Rename the KRNL386.EXE file in the SYSTEM subdirectory for safety. Copy COMMAND.COM from your DOS installation as your new KRNL386.EXE. To start the VMM without WIN.COM, enter the command system\win386. At the resulting DOS prompt, run win to see its interpretation of the VMM as MSDPMI. For bonus fun and games, while still at this DOS prompt, delete the KRNL386.EXE that is actually COMMAND.COM, restore the real KRNL386.EXE and run it as system\krnl386 to proceed to the Windows GUI.
You might think that use of int 2Fh function 1600h by the KRNL386.EXE from Windows 3.1 is already covered by the discussion above about the run-time fix of its faulty VMM version detection, but there is a second use and it is instructive for offering what may be the most comprehensive set of responses to what the function can return in al.
This second use applies only when KRNL386 is about to exit the Windows GUI and only then when the exit code is 0043h. This particular exit code is set by the ExitWindows function and is defined symbolically as EW_REBOOTSYSTEM. To KRNL386 it means that KRNL386 is preferably not to terminate as a protected-mode DOS program but is instead to restart the computer. The mechanism of this restarting depends on whether KRNL386 is running under the VMM or under DOSX. If in Enhanced Mode, under the VMM, then the only possible means of restarting the computer is to involve the REBOOT VxD. If in Standard Mode, the reboot mechanism is instead more like what a DOS programmer might try: int 21h function 0Dh to reset DOS’s disk handling (and that of most disk-caching software); a cache-flushing int 2Fh function FE03h that is apparently specific to the Norton Utilities; and then an int 19h. (For the low likelihood in practice that int 19h succeeds at reloading a working DOS, see DOS Internals, ISBN 0-201-60835-9, pages 33 to 47.)
To choose its method of restarting the computer, KRNL386 executes int 2Fh function 1600h again. Though whoever coded the first use treated Standard Mode as the default, with Enhanced Mode inferred from only one special case (03h), this second use defaults to Enhanced Mode but after allowing four special cases. The Standard Mode restart follows if the function leaves al as 00h or sets it to 80h. The other two special values are from the Windows/386 implementation. If int 2Fh function 1600h sets al to either 01h or FFh, then KRNL386 does not proceed with rebooting via either method but instead falls back to terminating.