Windows 3 is often said to be just an UI on top of DOS. This article presents some of the inner side of Windows 3.x and will show that it is more ambitious than that; especially when running on a 386 computer. Windows 3 gradually added 32-bit capabilities to its 386 only “Enhanced Mode”, progressively paving the road to Windows 95.
This glossary explains technical terms that will be useful to understand the points. Most of them are related to the x86 CPUs, their memory models and how to circumvent them.
Segmented memory is a way to address more than 64KiB of RAM with a 16-bit CPU.
In real mode, a 16-bit segment address is shifted left by four bits then a 16-bit offset is added to compute the physical address. Thus, the CPU can generate addresses up to 20-bits and can access up to 1 MiB.
In 16-bit protected mode (the protected mode introduced by the 286), the 16-bit segment register contains an index pointing to a 24-bit segment address stored in a table. It is added to a 16-bit offset, making it possible to access up to 16MiB of memory.
In 32-bit protected mode (introduced by the 386) the segment and offset are both 32-bit wide, giving the possibility of a flat memory model such as on the 68000.
Real mode is the original operating mode of the 8086. It gives direct access to 1MiB of segmented memory and there is no restriction accessing all addressable memory and I/O. All x86 start in real mode when reset. DOS and the BIOS run in real mode.
Protected mode was introduced by the 80286 then extended by the 80386. The memory is still segmented with 16-bit segments on a 286 and 32-bit segments on a 386, giving the illusion of a flat address space on the later. A protected mode program cannot interact [directly] with DOS and BIOS, as they run in real mode.
Virtual 8086 mode
The Virtual 8086 mode, sometimes called V86, is a hardware virtualization technique introduced with the 80386 that allows it to emulate many 8086 chips running programs in real mode, while the CPU can run other protected mode “tasks”. It requires a “monitor” running in “ring 0” (privileged mode) while the 8086 programs run in “ring 3” (user mode).
DPMI / DOS Extender
DPMI stands for DOS Protected Mode Interface. It is a specification established by Microsoft along with various vendors. Following this specification, DOS provides support for DPMI hosts (or servers) which offer their services to DPMI clients (“extended” programs). An extended program runs in protected mode and can interface with real mode software such as DOS and BIOS through the DPMI. DOS Extenders are extended programs whose goals are to launch an application in protected mode and service them with an extended version of the APIs that DOS and the BIOS provide for real mode applications. There are 16-bit DOS Extenders for the 286 and 32-bit ones that can take advantage of the 386.
DOS/4GW was a well known DOS Extender among gamers of the late DOS era
The eXtended Memory Specification describes ways to access the memory beyond the first MiB in real mode. It is only available to a 286+ CPU. This memory is made available by a DOS real mode driver: HIMEM.SYS.
The Expanded Memory Specification is a way to access the memory beyond the first MiB in real mode via bank switching. On 8086 and 286, this requires special hardware and a driver. On a 386, no specific hardware is required as EMS is emulated by EMM386.EXE.
Windows 3.x family
- Windows 3.0 was launched on May 22th 1990. It introduces most of the major features and components that will be discussed here. Its new shell, “Program Manager”, was acclaimed at the time.
- Windows 3.1 was released April 6th 1992, introducing among other things the True Type fonts, 32-bit access to the disk and the infamous Registry.
- Windows 3.11 and 3.2 were minor updates.
- Windows for Workgroups 3.11 released November 1st 1993 added various network oriented services and 32-bit [file] access that bypassed DOS and BIOS.
Some of these terms are explained with more details in my previous article about MS-DOS memory management.
Quick overview of Windows’ architecture
Modes of operation
Windows 3.x has three modes of operation:
- Real Mode. Windows and the applications are subjected to the limitation of the 8086. They can easily access the first MiB of memory, 4 MiB with an expansion memory card and an EMS driver. This mode was abandoned by Windows 3.1
- Standard Mode. Windows and the applications run in the 16-bit protected mode. The environment has access to 16-MiB of memory and, as we will see, all applications and DOS-boxes run “cooperatively” in a shared memory space. It requires at least a 286 to run.
- Enhanced Mode. This is the only mode supported by Windows for Workgroup 3.11. This mode requires a 386 plus 2MiB of RAM and is far more advanced. It is built on the 32-bit “Virtual Machine Manager” (VMM) that leverages the virtual 8086 mode to create and manage several virtual machines: one for the Windows Operating Environment, the System VM, and one for each DOS session. Enhanced Mode also introduces Virtual Device Drivers (VxDs): 32-bit protected mode drivers that can share system resources to more than one process.
A view of the VMM and the V86 virtual machines, running in different rings
Most of this article will describe Standard and Enhanced Modes
Windows is architectured in layers. From top to down, it consists of
- An API (later called Win16) which Windows applications interact with. It allows developers to target windows without knowing its inner details, nor the drivers installed or even the hardware configuration.
- A middle layer consisting of Core components and dynamically loaded extensions.
- The Core is made of three sub-components:
- KERNEL: Handles basic operating systems such as memory management, process and thread scheduling, and file input/output (depending on DOS functions)
- USER: Manages the GUI, the events, the messages between the applications and the user I/O devices (mouse + keyboard)
- GDI: An abstraction to draw on the screen or to a printer.
- The extensions are made of DLLs and could add extra functionality such as multimedia support or whatever a 3rd party would want to add. They make Windows very flexible.
- The Core is made of three sub-components:
- At the bottom, drivers provide support for the hardware. DOS is used for low level duties like file and memory operations, via a DOS Extender and a DPMI server.
The 16-bit Windows Operating Environment
Some say that Windows is a DOS Expander with an Operating Environment. That is not a bad description.
Windows is invoked from DOS by WIN.COM, a real mode program. WIN.COM tests the type of the CPU and decides to launch one of the three flavors of Windows: Real Mode, Standard Mode or Enhanced Mode.
Real Mode is compatible with all x86 CPUs but windows will run in real mode, meaning that it will suffer the same memory constraint as DOS itself. Standard Mode requires a 286 and will run in protected mode, allowing the use of all the memory addressable by a 286. Enhanced Mode also runs in protected mode, but provides many advantages that the required 386 and its MMU permit.
For Enhanced Mode, WIN.COM checks if a “386 memory manager” is already running and uses its page mapping tables before Windows takes over the memory management.
If the Real Mode flavor is not chosen, WIN.COM will then launch a DPMI host and a DOS Extender that will allow protected mode Windows to dialog with DOS and BIOS. Then, it launches a task switcher and the “kernel” itself.
The executables are different depending on the desired mode and the CPU:
- Standard Mode: WSAP.EXE (task switcher), DOSX.EXE (DPMI host/DOS eXtender), KRNL286.EXE or KRNL386.EXE (depending on the CPU type)
- Enhanced Mode: WIN386.EXE (task switcher, DPMI host/DOS eXtender and Virtual Machine Manager), KRNL386.EXE
The mode is chosen based on the CPU, as a 386 is required for Enhanced Mode, and the amount of memory. Even with a 386, on a machine with less than 2MiB, Windows will start in Standard Mode.
In Standard Mode, DOSX.EXE starts executing in real mode, performs its initialization then loads KRNLx86.EXE.
In Enhanced Mode, the setup is more complex but will lead to a way more advanced system. WIN386.EXE is not only a DPMI host/DOS Extender, but also sports a pre-emptive task switcher, manages the Enhanced Mode only “Virtual Device Drivers” (VxD) and hosts the “Virtual Machine Manager” (VMM). WIN386.EXE loads all the VxDs then launches the VMM. The VxDs and the VMM switch to 32-bit protected mode and initialize themselves, the VMM then creates a first Virtual 8086 session that runs in real mode. That session is the System Virtual Machine and the kernel will launch inside it!
KRNLx86 is loaded as a plain DOS executable. It bootstraps itself into memory, switches to protected mode, replaces some DOS interrupt vectors by its own, and allocates a “Global Heap” with the help of the DPMI server. KRNLx86 then reloads itself as a Windows DLL, so it can populate its exported functions table, making them available to user code, as any subsequently loaded DLL will do.
At this point, Windows KERNEL runs in protected mode, and all the necessary low-level tools to manage the memory are available. The DPMI services, and the DOS extender using them, will take care of switching between real mode and protected mode and will handle the various calls to DOS and BIOS APIs, as their “legacy” functions must be executed in real mode.
Windows finishes starting-up by reading the configuration from SYTEM.INI and loads the corresponding DLLs and DRVto bring the SYSTEM, USER and GDI module into memory, along with all the 16-bit device drivers (DRV files), system extensions and fonts.
The initialization of USER brings the window manager online.
Finally, the kernel loading process jumps into the scheduler loop, and Windows is up and running!
Welcomed by Program Manager
Windows services and operations
When Windows is fully loaded, DOS is still present in memory. It is used mainly for low-level memory management and file operations, via the DPMI server. But from a high-level point of view, a programmer will interact with the Windows API (Win16) and never with DOS itself. From that point of view, Windows will behave like a fully-fledged operating system.
KERNEL offers the high-level interface to handle memory operations while the low-level stuff, such as handling page faults, are processed by the DPMI server.
Real Mode is very limited and subject to the same constraints concerning access to memory as DOS is. It can access, with the same restriction, to up to 4MiB of RAM, making use of EMS memory.
In Standard and Enhanced Mode, all allocations are done from XMS memory via the HIMEM.SYS driver. One is provided with Windows, but HIMEM.SYS from DOS 5.0+ can also be used. Windows 3.0 is limited to a maximum of 16MiB of memory while Windows 3.1 on a 386+ can manage up to 256MiB of memory.
Most of the differences between Standard Mode and Enhanced Mode relate to the capabilities and services offered by their DPMI servers, DOSX.EXE and WIN386.EXE. WIN386 is built on the virtual memory and paging capabilities of the 386 and is much more than a mere DOS Extender. It can offer a bigger memory space than what is physically present in the machine and provides a private address space to each VM. This means that in Enhanced Mode, KERNEL/USER/GDI, Windows/DOS applications and 16-bit drivers cannot write outside their private memory space nor in ring 0 where the VMM and the various VxDs are running.
The KERNEL’s job is to provide contiguous blocks of memory to Win16 applications. From its point of view, all these lower level differences are transparent.
In Real Mode, 256KiB “large frames” are supported and KERNEL hides the peculiarities of accessing EMS. It gives easy access to this cumbersome pool via the Win16 API. But his task is hard: the first MiB physical addresses are the direct translation of the segment addresses plus the offsets, with no indirection. Thus, KERNEL has to update many segment addresses throughout its code when it has to shuffle things in memory; for instance to avoid fragmentation or make space for a new allocated block that does not fit in an available memory hole. The EMS pool is easier to manage, as it is an indirect access to the expanded memory.
Illustration of how memory can become fragmented
In Standard and Enhanced Mode, managing memory is easier as physical addresses are computed using a selector table. Thus, updating physical addresses can be done simply by updating this table. But even when running in protected mode and on a 386, Windows must deal with some of the complexities of the 16-bit x86 segmented memory model and its 64 KiB segments.
In order to hide most of the details to user programs, KERNEL maintains a “big” Global Heap that tracks all code and data allocations from all 16-bit DLLs and programs. From this Global Heap, it carves blocks for Local Heaps and stacks, which are dedicated to each program, faster to access, but limited to 64KiB total.
Being 16-bit and supposed to run on older x86s, programs are still limited to 64KiB structures at the lowest level. But KERNEL offers the possibility to handle bigger structures at a higher level by “tiling” segments. “Huge” (a.k.a > 64KiB) data structures can be requested from the Global Heap. C and C++ compilers obviously take advantage of KERNEL and handling huge data structures “only” requires the use of non-standard keywords.
As compilers share a common memory model, make use of system calls to manage memory and follow Windows’ ABI, any program can call functions hosted in DLLs, even if they were compiled with another compiler.
Finally, KERNEL also tries to automatically attenuate the memory fragmentation and can move memory blocks when they are not locked by a program using them. Allocating memory in Win16 API returns “handle” to the memory. If the program wants to manipulate the corresponding pointer, it has to lock it so that it won’t be shuffled around.
But even in Enhanced Mode, there is no memory protection for Windows and the programs running inside the System VM. Unlike in UNIX, Windows NT or OS/2, all programs and 16-bit DLLs, including those forming Windows itself, share the same address space. A program can inadvertently overwrite any portion of this physical memory, leading to instabilities and crashes. Concerning DOS sessions, in Enhanced Mode, each one (and the programs inside) runs in Virtual 8086 mode and gets a separate address space. VxDs and the VMM runs in a privileged ring and cannot be modified by a rogue pointer from the System VM or a DOS session. But a buggy VxD can bring the whole system down, as it runs in the same privileged mode as the VMM.
The Windows operating environment sports a cooperative (non-preemptive) scheduler: a program must voluntarily “yield”, to give back the control to the scheduler which can then wake another process. Yielding can occur by calling the “Yield()” function, but will also happen when using some API calls such as those used to manage the events and messages passed to the program or sent away. A well written program will have mechanisms to periodically yield. If not, even the system itself won’t get CPU cycles, leading to a sluggish interface or even a complete hang, if the program is stuck in a long loop!
This kind of scheduler is way more primitive than a preemptive scheduler such as those already found in UNIX and OS/2 in the late 80s.
Nonetheless it supports some niceties such as task priorities. Indeed, tasks have a priority level. When given the hand, the scheduler will wake the task that has the highest priority level among the tasks that have events in their queue.
The task are stored as a priority ordered linked list
If no task has a pending event, the scheduler enters the Idle Loop. This loop contains code to perform the memory housekeeping then calls the DOS idle interrupt, which will execute DOS TSRs (resident programs). It will also launch the screensaver if required, and it will enable the power management functions of a laptop.
As most programs will run at the same priority, the scheduler can modify this property so they each get an equitable amount of time-slices.
Concerning the Enhanced Mode, it is not entirely true to state that there is only cooperative multitasking. Indeed each DOS session runs in a Virtual Machine in Virtual 8086 mode. Each of these virtual machines is pre-emptively scheduled by the Virtual Machine Manager. Furthermore, the cooperative scheduler itself runs inside the System VM which is also scheduled by the VMM. So there are two levels of scheduling: DOS applications are pre-emptively scheduled along with the System VM which hosts the Windows applications. And inside the System VM, those applications are cooperatively scheduled!
Scheduling in Enhanced Mode
In Real and Standard Modes, all file operations are performed by DOS and BIOS via their INT 0x21 and INT 0x13 real mode API. Switching to real mode and back to protected mode via DPMI is slow. Furthermore, these functions are not reentrant and often blocking. Thus, the calls must be serialized, which impedes the performance even more.
Windows 3.1 in Enhanced Mode offers 32-bit disk access based on protected mode device drivers that handle the interrupt 13h BIOS calls. The real mode BIOS function managing the low-level functions to read and write sectors on the disk are replaced by 32-bit equivalents. Not only this avoids the costs associated with BIOS calls, but on later 486 machines, they can take advantage of the 32-bit interface with the hard disk controller. A 32-bit VxD driver, WDCTRL, is supplied by default with Windows. But it only handles IDE hard drives with some limitations to their geometry. For instance there is a hard limitation to 504MiB. In order to bypass these limits or manage SCSI or any other kinds of disk controllers, 3rd parties provided custom 32-bit drivers.
Windows 3.11 adds 32-bit file access. It is a high level file access and caching method that bypasses DOS entirely by trapping the interrupt 21h. It can interact directly with the 32-bit disc access functions to the disk controller and can also interact with the real mode functions provided by BIOS.
Win32s is an optional 32-bit runtime environment that was introduced in 1992 for Windows 3.1 and 3.11 and must be installed separately. It is a partial implementation of the Win32 API used by programs targeting Windows NT and Windows 95. Win32s applications interact with a subset of the Win32 API, missing Win95/NT only functionalities such as threading or network services. The Win32s calls are translated into Win16 calls via thunking: the Win32s functions act as “facades”. They translate and pass the calls to the underlying 16-bit API. Indeed, a Win32s application could not call the Win16 API by itself as the 32-bit and 16-bit memory models are very different beasts.
Thunking is required as 16-bit and 32-bit memory models are different
The advantages are that Win32s applications are fully 32-bit: they can do 32-bit operations on 32-bit data and handle the allocated memory as a flat address space, with no segment!
But interactions with the underlying system are still 16-bit. The compatibility with true Win32 programs is of course lacking due to the incompleteness of Win32s. And there are other limitations such as the impossibility to launch a Win32s app from a DOS prompt. Indeed: DOS runs in its own VM and has no idea of what a Win32s application is.
When loading a Win32s program, the standard 16-bit WinExec() function is called. It will call LoadModule(), but as the program is not a standard Win16 New Executable file, LoadModule will return an error code that will execute a function in KERNEL tailored to load the newest Win32’s Portable Executable format: ExecPE(). That indicates that the support for PE files was supported by Windows 3.1 from the beginning and that Win32s was not an afterthought!
When Windows loads, DOS drivers stay in memory. Some of the Windows 16-bit drivers may use them underneath. And some DOS applications were programmed to interact directly with them. So the DOS drivers need to be available.
Many 16-bit drivers interact with their device through DOS or BIOS calls. For instance they can call BIOS standard methods to get a device’s configuration options. Such calls require the use of the services of the DPMI server. Interacting with a TSR loaded from DOS as well. Unfortunately, switching back and forth from protected mode to real mode is costly. And BIOS calls are blocking by nature, and very little can be done until they complete.
For better performances, 16-bit drivers can also communicate directly with I/O and memory mapped devices, perform DMA transfers and handle interrupts. Of course, in Enhanced Mode, instructions forbidden to user mode will be trapped and processed or forward by a VxD.
Most memory mapped devices are located in address space between 0xA0000 and 0xF0000 and occupy less than 64KiB, just as in DOS. Such devices can be directly accessed, as they would be in real mode. However some do not fit in 64KiB or are located higher than 1MiB. A call to the DPMI services is then necessary.
In fact, 16-bit drivers are pretty much like standard applications that interface with the system via fancy entry points. They use the memory management provided by KERNEL and can ask for their code and data memory to be fixed (non-moveable), page-locked, and non-discardable.
VxD, Virtual Device Driver is only supported by the Enhanced Mode, although it was introduced earlier by Windows/386 2.x.
It was a necessary architecture change because of the introduction of the various Virtual Machines (the System VM and the various DOS boxes). In the laters, each application thinks it owns exclusive access to the hardware. Inside their VM, DOS applications or the Windows Operating Environment run in “ring 3” the limited “user mode” of x86. In this ring, IN and OUT operations are forbidden. An exception is thrown that will give control to the VxD driver that virtualizes the device the application tries to reach. The VxD drivers are reentrant and can manage a state for each application so that the illusion that they have exclusive control on the virtual device is maintained. Hardware interrupts are also handled by the VxD then reflected to every VM that needs them.
Virtual device drivers generally work along the corresponding 16-bit driver, which still performs most of the heavy-work, specific to the task and the device. In fact as the Windows environment is aware that it does not own a device exclusively for itself. As it does not require the driver to be virtualized, Win16 code often works exclusively with the 16-bit driver, without interacting with the VxD.
Win16 and even a DOS application can “conscientiously” interact with a VxD if it exports an “API procedure”. In order to request it from ring 3, it fires the INT 0x2F and gives the desired parameters through registers AX and BX. The VMM will trap the interrupt, get the function pointer to call the “API procedure” from the VxD, and pass it in its reply to the Win16/Win32s/DOS program.
A VxD does not require to be a virtual driver, or even a driver! Some VxDs act as a device driver, but don’t virtualize the device, while some VxDs don’t interact with any device; they exist merely to provide a service to other VxDs or to applications and can be a way to extend Windows’ functionalities.
As the VXDs run in privileged mode, they have unrestricted access to all x86 instructions and can access to any location in the memory. They can issue IN and OUT instructions without trouble and can directly talk to a memory mapped device. They can also work closely with the VMM and freely examine operating system data structures, such as descriptor and page tables. But a misbehaving VxD can also crash the entire system!
All VxDs must be loaded during startup: all must be declared in SYSTEM.INI with a device= statement. Some of them are supplied by Windows inside WIN386.EXE, while others are supplied as .386 files.
The VxD environment was extended to become the driver model supported by Windows 95.
16-bit DRV, 32-bit VxD, both… or neither?
In Enhanced Mode both a VxD and a 16-bit drivers are required to support most devices. But there are some exceptions.
|Serial / Parallel Port||COMM.DRV||VCD.DRV|
|Network||DOS device driver or TSR|
|Block Device (HDD, CDROM)||DOS device driver||WDCTRL (if possible) or 3rd party|
This article is not exhaustive and did not mention GDI (that was not introduced by Windows 3) nor WinG, a kind of predecessor to DirectW. But it is enough to say that Windows 3.x is not just a mere “GUI” that was stuck above MS-DOS. It provides a full-fledged operating environment that is up to the early 90s standards, almost technically on par with what System 6 and 7 could offer on a Macintosh. But what really shines is the Enhanced Mode, which offers a taste of 32-bitness with Win32s and the various VxDs and operates many preemptively scheduled VM by the way of what could be called a “hypervisor”, the VMM. In Enhanced Mode, the VMM can be considered as the true kernel of Windows.
Of course, DOS is still necessary. But its presence is lighter than most people think and it is only a step away from becoming a mere bootloader. This step was taken by Windows 95, but this is a story for another day.
Execution environments in Enhanced Mode
Physical execution environments associated with various 80386+ processor modes
|32-bit Protected||16-bit Protected||V86|
|Total Address Space||4GiB||16MiB||1MiB|
|Address Translation||logical to linear selector lookup linear to physical page tables||logical to linear selector lookup linear to physical page tables||logical to linear segment « 4 linear to physical page tables|
Windows execution environments associated with various process types
|Process type||Processor mode||Privilege||Bitness||Memory model||VM|
|VMM||Protected||Ring 0||32-bit||“flat”||Outside all|
|Win32s||Protected||Ring 3||32-bit||“flat”||System VM|
|Win16||Protected||Ring 3||16-bit||Segmented||System VM|
|DOS||V86||Ring 3||16-bit||Segmented||DOS VM|
- Windows 3.0 review in Byte Magazine
- History of Windows Device Drivers
- Windows Internals: The Implementation of the Windows Operating Environment
- Writing Windows VxDs and device drivers
- Unauthorized Windows 95
- The Anatomy of a VxD
- Windows 3.1 Memory Limits
- Virtual 8086 Mode
- The story of the mysterious WINA20.386 file