How Windows 98 Handled USB Gamepad Input
Microsoft Windows 98 introduced robust, native support for Universal Serial Bus (USB) peripherals through a newly implemented driver model. When a user connected a USB gamepad, the operating system used the Human Interface Device (HID) architecture to detect the hardware, parse its physical capabilities, poll for state changes, and translate those hardware signals into standard input data for video games via DirectInput and the Win32 multimedia subsystem.
Hardware Enumeration and the USB Stack
When a USB gamepad is plugged into a Windows 98 PC, the USB host
controller (operating via either Open Host Controller Interface or
Universal Host Controller Interface drivers, OPENHCI.SYS or
UHCD.SYS) detects the voltage change on the data lines. The
operating system initiates device enumeration by assigning the device a
unique bus address and requesting its device descriptors.
Windows 98 identifies the device's class code as 0x03,
designating it as a Human Interface Device (HID). It then binds the
generic USB HID minidriver (HIDUSB.SYS) to the device,
alongside the parent class driver (HIDCLASS.SYS). Because
the HID specification standardizes communication for input peripherals,
Windows 98 did not require proprietary, vendor-specific drivers for
standard USB gamepads to function.
The HID Descriptor and State Translation
Once bound, the operating system requests the gamepad's HID Report Descriptor. This descriptor acts as a data contract describing the format of the data packets the gamepad will transmit. It informs Windows of:
- The number of buttons present and their bit representations.
- The directional controls, defined as X and Y axes.
- The presence of additional inputs, such as throttles, rudders, or hat switches (directional pads).
- The minimum and maximum numerical ranges for each analog axis.
The HIDCLASS.SYS driver uses this descriptor to build a
parser table. This allows the OS to understand raw input bytes without
hardcoded knowledge of the physical controller layout.
Interrupt Polling and Event Extraction
USB is a host-controlled bus, meaning the gamepad cannot push data to the PC asynchronously. Instead, the USB host controller actively polls the gamepad's interrupt IN endpoint at a fixed interval specified by the device (typically between 8 to 10 milliseconds).
- Polling: The host controller sends a
token INpacket to the gamepad. - Transmission: If the user has pressed a button or moved a stick, the gamepad sends an input report containing the current state of all controls.
- Low-Level Assembly: The USB stack receives the
data, verifies packet integrity via CRC checks, and hands the raw
payload up to
HIDUSB.SYS. - Parsing:
HIDCLASS.SYScross-references the incoming raw byte stream with the parsed Report Descriptor, separating button bitmasks and axis values into structured HID event packets in kernel memory.
Exposing Input to Applications
To allow DOS legacy software, 16-bit Windows games, and modern 32-bit DirectX titles to read the input, Windows 98 channeled the parsed HID data into two primary application programming interfaces:
1. DirectInput
Part of the DirectX suite, DirectInput bypasses the standard Windows
message queue (WM_KEYDOWN, etc.) to provide
high-performance, low-latency gaming input. DirectInput communicates
directly with HIDCLASS.SYS through the DirectInput HID
mapper (dinput.dll). It reads the structured HID states
directly from the kernel ring-0 buffer, exposing them to the game loop
as axis positions and button presses.
2. VJoyD and the Multimedia API (WinMM)
For older titles relying on the traditional Windows Multimedia API
(joyGetPos and joyGetPosEx), Windows 98
utilized the Virtual Joystick Driver (VJOYD.VXD). Windows
98 included an intermediate translation layer (JOYHID.VXD)
that read inputs from the WDM HID stack and presented them to
VJoyD as standard analog joystick signals. This
backward-compatibility mechanism ensured that vintage games expecting an
analog PC gameport controller could seamlessly read inputs from modern
USB gamepads.