How Windows 3.11 Handled Clock Timer Interrupts
In Microsoft Windows 3.11, updating the desktop clock relied on a hybrid architecture that translated low-level hardware timer interrupts into cooperative user-mode application messages. Operating in 386 Enhanced Mode, Windows used its 32-bit Virtual Machine Manager (VMM) and dedicated virtual device drivers to capture hardware clock ticks from the system timer, route them through the Windows system driver, and post synthetic timer messages to update graphical clock utilities without requiring true preemptive multitasking for 16-bit applications.
At the lowest level, the physical Intel 8253/8254 Programmable
Interval Timer (PIT) fired hardware interrupt request 0 (IRQ 0) at
roughly 18.2 times per second (approximately every 54.9 milliseconds).
In Windows 3.11 running in 386 Enhanced Mode, WIN386.EXE
intercepted IRQ 0 via the Virtual Timer Device (*VTD), a
core Virtual Device Driver (VxD). Instead of passing raw interrupts
directly to user programs, the VTD virtualized the timer ticks, managing
time slicing between the System Virtual Machine (where Windows and Win16
apps ran) and any active MS-DOS virtual machines.
Within the System Virtual Machine, the virtualized tick was routed
through MS-DOS interrupt vectors (INT 08h and
INT 1Ch) and received by Windows core system drivers,
primarily SYSTEM.DRV and TIMER.DRV. These
drivers tracked elapsed time intervals based on counts requested by the
Windows USER.EXE module.
Applications displaying the time, such as the standard
CLOCK.EXE utility or third-party desktop shells, did not
process hardware interrupts directly. Instead, they initialized a timer
using the Win16 API function SetTimer(). When calling
SetTimer(), the application specified an interval in
milliseconds and either a callback function or an instruction to send
standard WM_TIMER messages to its window procedure.
Because Windows 3.11 utilized cooperative multitasking for Win16
programs, WM_TIMER was not an asynchronous interrupt. It
was a synthetic, low-priority message generated dynamically by the
Windows messaging subsystem. When an application called
GetMessage() or PeekMessage() inside its event
loop, the system checked the status of active timer structures. If the
application's message queue was empty of higher-priority events (such as
mouse movements, keyboard presses, or WM_PAINT calls) and
the required time interval had elapsed according to the timer driver,
the kernel generated a WM_TIMER message.
Upon receiving the WM_TIMER message, the clock
application queried the updated system time—typically via the standard
DOS INT 21h (Function 2Ch) or BIOS
services—and issued Graphics Device Interface (GDI) calls to redraw the
clock face, update hands, or refresh digital text. Once rendering was
complete, the application yielded execution back to the Windows
cooperative scheduler, allowing other processes to execute until the
next interval elapsed.