Scancode
A scancode (or scan code) is a low-level numeric code generated by a computer keyboard's hardware to indicate which specific physical key has been pressed or released, irrespective of the keyboard layout, language, or character mapping.[1][2] These codes are transmitted from the keyboard controller to the host computer via protocols like PS/2 or USB, serving as the foundational input signal before higher-level processing into keycodes or characters.[3][4] The origins of scancode-like encoding trace back to the 1960s with early electronic keyboards, such as Invac's light-beam designs using binary codes from photodetectors, evolving through diode matrix systems in the late 1960s that generated unique codes per key via electrical grids.[5] By the 1970s, matrix scanning techniques introduced by companies like Cherry and Micro Switch enabled more efficient debounce and rollover handling, with microprocessor-based keyboards (e.g., using Intel 8048 chips) standardizing the process by the decade's end.[5] The modern PC scancode system was formalized in 1981 with the IBM PC/XT, introducing Set 1 scancodes—a compact 7-bit scheme for the 83-key layout—followed by Set 2 in 1984 for the enhanced 101/102-key AT keyboard, which remains the most widely used due to its support for extended keys and better encoding of modifiers like Shift.[6][7] Set 3, introduced for the IBM 3270 PC, offered further enhancements but saw limited adoption.[7][8] In operation, a typical scancode consists of one to three bytes: a make code for key press, optionally prefixed with 0xE0 or 0xF0 for extended or release signals, allowing distinction between actions on up to 128 keys.[1][9] This hardware abstraction is crucial for operating systems and applications, enabling consistent input handling across diverse keyboards while accommodating features like n-key rollover (NKRO) for simultaneous presses.[2] In contemporary USB keyboards, scancodes are largely supplanted by HID usage codes defined in the USB standard, though legacy PS/2 emulation and virtual keyboard drivers continue to rely on traditional sets for compatibility.[4][10]Fundamentals
Definition
A scancode is a low-level numeric code generated by keyboard hardware to report key presses or releases, representing the physical position of a key independent of any operating system or software interpretation of its function or character.[1][11] The keyboard's embedded microcontroller continuously scans a key matrix—a grid of interconnected rows and columns where each key switch resides at an intersection—to detect activations and translates the row-column coordinates into the appropriate scancode.[12][6] This process ensures that the scancode identifies the key's location on the hardware layout, such as QWERTY or AZERTY, without regard to regional language mappings or user configurations.[1] Once generated, the scancode is transmitted from the keyboard to the host computer's controller using standardized protocols. In the PS/2 interface, common for traditional wired keyboards, scancodes are sent serially over a bidirectional clocked data line to the system's keyboard controller, which may perform translation between scancode sets if needed.[13] For modern USB keyboards, the microcontroller converts the matrix-derived scancode into a USB HID (Human Interface Device) report, where keys are encoded as usage identifiers within the HID keyboard page; these reports are then transmitted over the USB bus to the host, maintaining the hardware-level abstraction.[14][11] This transmission occurs asynchronously upon key events, with separate codes for press (make) and release (break) actions to enable full key state tracking.[1] The basic anatomy of a scancode involves compact 1- to 2-byte sequences, though extended or multi-key combinations can extend to more bytes for clarity in handling modifiers or special functions. For instance, in the PS/2 Set 1 scancode set, pressing the 'A' key produces 0x1E, while releasing it yields 0x9E; extended keys like the right arrow prepend 0xE0, resulting in sequences such as 0xE0 0x4D.[15] In USB HID, the 'A' key is reported using usage ID 0x04 within the keyboard usage page (0x07), as defined in the report descriptor and included in the input report.[14] Unlike higher-level keycodes, which map to specific characters or actions based on software, scancodes remain tied solely to hardware positions.[1]Distinction from Keycodes
Scancodes represent the lowest level in the keyboard input hierarchy, consisting of hardware-generated, device-dependent codes that identify specific physical key presses and releases without regard to the keyboard layout or operating system interpretation. In contrast, keycodes serve as an OS-level abstraction, translating these raw scancodes into standardized, device-independent values that denote the key's functional purpose or position.[1][16] This distinction allows scancodes to remain immutable and tied to the hardware's electrical signaling, while keycodes provide a normalized interface for software to process input consistently across compatible devices.[17] The standard keyboard input pipeline flows from scancode to keycode and finally to character code, with each layer building on the previous one to produce usable textual or functional output. The keyboard controller sends a scancode upon key activation—for instance, 0x1C in PS/2 scan code set 2 for the physical key typically labeled 'A' in QWERTY layouts—which the OS driver maps to a keycode such as VK_A (0x41) in Windows or KEY_A (30) in Linux.[1][16][13] This keycode remains fixed for the physical key regardless of the user's locale or layout settings, ensuring that applications like games can detect positional input (e.g., the 'W' key for forward movement) without layout variations affecting the mapping.[1] Only at the character code stage—derived via layout-specific translation—does the output vary; for example, the same keycode might yield Unicode U+0061 ('a') in a QWERTY layout or U+0071 ('q') in an AZERTY layout.[16] Keycodes incorporate handling of modifier states, such as Shift or Ctrl, but do not alter the core keycode value for the primary key; instead, the modifier's own keycode (e.g., VK_SHIFT or KEY_LEFTSHIFT) is tracked separately to influence downstream processing. For instance, pressing Shift followed by the 'A' key generates the Shift keycode alongside the 'A' keycode, but the combined state during character translation produces an uppercase 'A' (U+0041) in QWERTY rather than lowercase.[1] This separation ensures scancodes focus solely on raw hardware events, keycodes on logical key identification, and character codes on context-aware text generation, preventing layout-dependent ambiguities in low-level input handling.[16]Historical Development
Origins in Early PCs
The IBM Personal Computer (model 5150), released on August 12, 1981, introduced scancodes as a fundamental element of its keyboard input system, enabling the detection and transmission of key presses from the attached 83-key Model F keyboard. This keyboard utilized capacitive switches and featured an Intel 8048 microcontroller embedded within it to scan an 8x16 key matrix, generating 8-bit scancodes that represented row and column intersections for each key.[18] The scancodes were transmitted serially over a 5-pin DIN connector to the system board, where the Intel 8255 Programmable Peripheral Interface chip handled input at port 60h, triggering an interrupt (IRQ1, INT 9) for each code received by the ROM BIOS.[18] Unlike ASCII codes, these raw scancodes provided hardware-level identification of keys without character mapping, allowing the BIOS to interpret them based on shift states and produce extended ASCII outputs or special commands. The design employed make codes for key presses (e.g., 01h for the '1' key) and break codes for releases (formed by adding 0x80 to the make code, such as 81h for '1' release), a scheme that addressed the limitations of the 8-bit format in distinguishing press from release events on the fixed 83-key layout.[18] This make/break variant was essential due to the keyboard's constrained matrix, which supported only a finite number of unique positions for the typewriter section, 10 function keys, and 16-key numeric keypad, without room for additional keys or complex modifiers in the initial hardware.[18] The 8048 microcontroller also managed debouncing, buffering up to 20 scancodes internally before transmission, and basic diagnostics like stuck-key detection during power-on self-test, mitigating early reliability issues in a system operating at 4.77 MHz with limited buffering (16 entries in the system memory keyboard buffer at 0040h:001Eh-003Dh).[18][19] The open architecture of the IBM PC 5150 facilitated rapid adoption of its scancode system by compatible clones from manufacturers like Compaq and Dell starting in the mid-1980s, establishing it as a de facto standard for PC keyboard interfaces and ensuring software compatibility across the burgeoning market.[20] This widespread emulation of the original scancode set (later termed Set 1 or XT scancodes) propelled the platform's dominance, as third-party systems replicated the serial transmission and interrupt-driven processing to run IBM PC software without modification.[21]Evolution of Standards
The evolution of scancode standards began with IBM's proprietary implementations for personal computers, transitioning through iterative refinements to accommodate expanding hardware capabilities. In 1981, the original IBM PC (model 5150) introduced Scancode Set 1, which utilized a simple scheme of make and break codes transmitted over a 5-pin DIN connector to report key presses and releases.[21] This set formed the foundation for keyboard input in early PCs, prioritizing compatibility with the system's BIOS interrupt handling. By 1984, with the release of the IBM PC/AT (model 5170), IBM shifted to Scancode Set 2 to support the enhanced 84-key keyboard layout, introducing prefix bytes for extended keys and improving efficiency in code transmission over the same connector type.[21] This change addressed limitations in Set 1, such as handling additional function keys, while maintaining backward compatibility through translation mechanisms in the keyboard controller. Scancode Set 3 was introduced earlier in 1983 with the IBM 3270 PC for compatibility with terminal systems, using an F0 prefix for break codes and offering a streamlined structure, but it saw limited adoption outside specific professional environments due to lack of broad OS support.[6] Further advancements came in 1986 with the introduction of the enhanced 101-key keyboard layout, which added dedicated cursor and navigation clusters along with additional function keys, paired with Scancode Set 2 for improved handling of the expanded array, including greater flexibility for key repeat and break code control via the existing prefix system. This layout became standard with the IBM Model M keyboard and marked a significant step toward more robust input handling in professional computing environments.[21][22] These IBM-specific sets remained proprietary but influenced clone manufacturers, fostering a semi-standardized ecosystem within the PC industry. The 1990s saw a shift toward broader industry adoption and formalization of the PS/2 protocol, introduced by IBM in 1987 but widely embraced as a de facto open standard for keyboard interfaces on IBM-compatible systems. This protocol, using a 6-pin mini-DIN connector, extended the AT-style scancode transmission with bidirectional communication and power delivery, enabling features like LED control and enhanced diagnostics, and became ubiquitous in PCs by the mid-1990s due to its integration into motherboard designs. The introduction of USB in 1996 revolutionized keyboard standards by moving away from hardware-specific scancodes toward a universal protocol under the Human Interface Device (HID) specification in USB 1.0, where key reports use standardized Usage IDs from predefined tables rather than vendor-proprietary codes.[23] This shift promoted interoperability across devices and operating systems, reducing the need for custom drivers and enabling plug-and-play functionality for keyboards. Subsequent refinements in HID 1.11, released in 2001, addressed emerging needs for wireless connectivity—via the contemporaneous Bluetooth HID profile—and virtual keyboards by expanding Usage Tables to include more international key mappings, such as dead keys and language-specific modifiers, enhancing global compatibility.[24] These updates ensured HID's adaptability to diverse input scenarios without reverting to legacy scancode dependencies.Variants
Make and Break Codes
Make codes are generated by the keyboard controller when a key is pressed, consisting of a single byte that identifies the specific key independent of its position or modifiers. For instance, in scancode set 1, the make code for the 'A' key is 0x1E. These codes enable the host system to recognize key activations without ambiguity.[25][26][27] Break codes, in contrast, are transmitted upon key release to signal the end of the key event. In set 1, the break code is derived from the make code by setting the highest bit (equivalent to adding 0x80), resulting in 0x9E for the 'A' key release.[25] Set 2 employs a different scheme, where the break code is a two-byte sequence beginning with the prefix 0xF0 followed by the make code; for 'A', this is 0xF0 0x1C, with the make code itself being 0x1C in this set.[27] Set 1 relies on bit manipulation without an explicit prefix, using dedicated scan lines to distinguish press and release states.[6] The primary purpose of distinguishing make and break codes is to provide complete information on key states, allowing software to detect held keys and implement features like auto-repeat, where the make code is retransmitted at regular intervals while the key remains depressed.[27] This mechanism ensures precise control over input timing and duration, foundational for responsive user interfaces in computing systems.[25]Extended Codes
Extended codes represent a mechanism in keyboard scancodes to expand beyond the 128-key limit of basic Set 2 and Set 3 encodings, enabling support for additional keys on enhanced layouts without altering the core protocol. These codes employ multi-byte sequences, typically starting with a prefix byte that signals the extension, allowing the keyboard controller to transmit information for keys like right-side modifiers or specialized functions. This approach maintains backward compatibility with earlier systems by treating the prefix as an ignorable or special byte in legacy environments.[25] In scancode set 2, the standard prefix for most extended codes is 0xE0, followed immediately by the conventional scancode byte for the key in question; for break (release) events, an additional 0xF0 byte precedes the final scancode after the prefix. This two- or three-byte structure distinguishes extended keys from standard ones—for instance, the right Control key generates a make code of0xE0 0x14, while its break code is 0xE0 0xF0 0x14. Similarly, the right Alt key uses 0xE0 0x11 for make.[13][15] The 0xE0 prefix originated as a way to encode "grey" duplicate keys on early enhanced keyboards but evolved into a general expansion tool for scancode space. A special case is the Pause/Break key, which uses a 0xE1 prefix and produces an eight-byte make sequence: 0xE1 0x14 0x77 0xE1 0xF0 0x14 0xF0 0x77, with no distinct break code due to its latching behavior.[13][28]
Introduced with IBM's 101/102-key "enhanced" keyboard layout in 1986, extended codes primarily addressed the addition of dedicated navigation keys (such as separate arrow keys and Insert/Delete), right-side Control and Alt modifiers, and expanded function keys (F11 and F12) to improve usability in productivity applications. By the 1990s, as keyboards incorporated Windows-specific features and early multimedia controls, the 0xE0 prefix accommodated these expansions; the Microsoft Natural Keyboard of 1994, for example, assigned 0xE0 0x5B to the left Windows key, 0xE0 0x5C to the right Windows key, and 0xE0 0x5D to the Application (Menu) key, facilitating OS-level shortcuts and application navigation. These extensions proved essential for 101- and 104-key layouts, supporting the growing complexity of user interfaces without requiring a full scancode overhaul.[21][9]
Despite their utility, extended codes carry limitations in legacy hardware and software environments. Systems with older keyboard controllers, such as those predating the i8042 PS/2 interface, may interpret the 0xE0 prefix as a standalone invalid or error code (often 0xE0 itself, reserved for protocol resets), leading to dropped inputs or conflicts for extended keys. Additionally, certain scancode ranges (e.g., above 0x7F or specific reserved bytes like 0x00 and 0xFF) are avoided to prevent ghosting in multi-key combinations or incompatibility with basic drivers, restricting the total addressable keyset even with prefixes. Proper handling requires firmware and driver support attuned to these sequences, which not all vintage PCs provide.[25]
Scancode Sets
PC-Compatible Sets
The PC-compatible scancode sets refer to the standardized mappings developed by IBM for its personal computer keyboards and adopted in compatible systems, evolving from the original IBM PC (Model 5150) to later AT and PS/2 architectures. These sets define the 8-bit binary codes transmitted over the keyboard interface to indicate key presses (make codes) and releases (break codes), without reliance on higher-level protocols like USB.[29] Scancode Set 1 was introduced with the original 83-key IBM PC/XT keyboard in 1981, using simple 8-bit codes without prefixes for basic keys. Make codes range from 0x00 to 0x7F, while break codes are formed by ORing the make code with 0x80 (e.g., 0xB9 for spacebar release). This set lacks support for extended keys, relying on a fixed matrix scan for the compact layout. For example, the spacebar produces make code 0x39.[30][31] Scancode Set 2 became the standard for 84-key IBM PC/AT keyboards starting in 1984 and extended to 101/102-key enhanced layouts in PS/2 systems, incorporating prefixes to handle additional keys and break signals. Make codes are single bytes from 0x00 to 0xFF, with break codes prefixed by 0xF0 followed by the make code (e.g., 0xF0 0x5A for Enter release). The 0xE0 prefix denotes extended keys, such as right-side modifiers or arrows, allowing compatibility with expanded layouts. In this set, the main Enter key uses make code 0x5A, differing from Set 1 to accommodate the taller key profile.[29][32] Scancode Set 3 was introduced in 1983 for the IBM 3270 Personal Computer and certain terminal keyboards (e.g., 122-key Model F), featuring make codes similar to Set 2 for core alphanumeric keys but with differences for other keys and enhanced support for break codes via keyboard commands. It saw limited adoption in standard consumer PCs due to its specialized use. In typical PC-compatible systems, the keyboard controller (e.g., Intel 8042) can instead translate native Set 2 codes to Set 1 equivalents for backward compatibility with legacy software and BIOS routines. This translation mode, enabled via controller commands, was commonly used in Windows 95 and later to ensure integration with applications expecting Set 1, without exposing Set 2 codes directly. For instance, a Set 2 Enter (0x5A) translates to Set 1's 0x1C.[32][29][6] The following table enumerates selected common key mappings across the sets for alphanumeric, Enter, spacebar, and arrow keys (make codes in hexadecimal; break codes omitted for brevity, following set-specific rules):| Key | Set 1 | Set 2 | Translated (Set 2 to Set 1) |
|---|---|---|---|
| A | 0x1E | 0x1C | 0x1E |
| Spacebar | 0x39 | 0x29 | 0x39 |
| Enter | 0x1C | 0x5A | 0x1C |
| Up Arrow | N/A | 0xE0 0x48 | N/A |
| Left Arrow | N/A | 0xE0 0x4B | N/A |
USB HID Sets
In USB Human Interface Device (HID) keyboards, traditional scancodes are supplanted by a system of 16-bit Usage IDs defined in the HID Usage Tables, enabling standardized key identification across devices. These Usage IDs combine a 16-bit Usage Page with a 16-bit Usage ID; for keyboards, Usage Page 0x07 is employed, where the lower 8 or 16 bits specify the key function. For instance, the letter 'A' corresponds to Usage ID 0x04, while international keys, such as those for non-US layouts (e.g., alternate # and @ symbols), may use IDs like 0x32 for the Non-US # and ~ key. This approach, formalized in the HID Usage Tables starting from version 1.0 in 1996 and updated through version 1.5 in 2004, ensures consistent mapping without reliance on platform-specific scancode sequences.[33] Keyboard report descriptors outline the format for transmitting these Usage IDs to the host, typically via 8-byte input reports in the boot protocol for compatibility. The structure includes a first byte as an 8-bit modifier bitmap (bits for Left/Right Ctrl, Shift, Alt, and GUI keys), a reserved second byte (set to 0x00), and six subsequent bytes each holding a single 8-bit Usage ID for pressed keys, allowing up to six simultaneous non-modifier keys. In the report protocol, custom descriptors can extend this to support more keys by using bitfields or larger arrays. Unlike legacy PC-compatible scancode sets that transmit sequential byte codes, HID reports focus on current key states for efficient, event-driven communication.[34] The HID system offers key advantages, including platform independence, as Usage IDs are universally recognized by compliant operating systems without custom drivers, facilitating plug-and-play operation across Windows, macOS, Linux, and embedded systems. It also enables n-key rollover (NKRO) through flexible report configurations, where devices can report all pressed keys simultaneously via multiple reports or expanded arrays, surpassing the limitations of fixed-rollover legacy protocols. For backward compatibility with PS/2 interfaces, USB-to-PS/2 adapters incorporate translation tables that map HID Usage IDs to PS/2 scancode Set 2 or Set 3 equivalents, ensuring functionality on older hardware.[34][15]Implementation
Hardware Generation
In keyboard hardware, scancodes are generated by an embedded microcontroller that continuously scans the key matrix to detect user input. The key matrix is a grid of rows and columns interconnected by mechanical switches under each keycap. The microcontroller sequentially drives one row (or column) high while monitoring the corresponding columns (or rows) for a low signal, indicating a closed switch and thus a pressed key. This row-column intersection uniquely identifies the key position, which the microcontroller maps to a specific scancode value based on the keyboard's predefined layout.[35][36] The microcontroller, often integrated into the keyboard's printed circuit board, handles the scanning and code generation. In legacy PS/2 keyboards, an onboard microcontroller (such as those based on the Intel 8048) monitors the matrix, debounces keys, and produces raw make (press) and break (release) scancodes in one of several sets, which are then transmitted to the host computer's Intel 8042 keyboard controller for further processing.[37] Modern USB keyboards typically employ AVR-series microcontrollers, such as the AT90USB family from Microchip, which perform matrix scanning via dedicated tasks and generate scancodes compliant with USB HID standards. For instance, in AVR-based implementations, a polling routine likekeyboard_task detects key hits and populates an input report buffer with the appropriate scancodes before transmission.[38]
Scancodes are transmitted serially from the keyboard to the host. In PS/2 interfaces, the keyboard's microcontroller uses two dedicated lines—clock and data—for synchronous serial communication, where each 8-bit scancode is framed by a start bit, the data bits (least significant first), an odd parity bit, and a stop bit. The host's 8042 controller receives the codes, verifies the odd parity; if mismatched, it issues a resend command (0xFE) to the keyboard, with persistent errors flagging a parity error in the controller status. In contrast, USB keyboards transmit scancodes within HID input reports over an interrupt endpoint, polled by the host at a default rate of 125 Hz (8 ms intervals for full-speed devices). Error handling occurs at the USB protocol level, where 16-bit cyclic redundancy check (CRC) fields in data packets ensure integrity, with automatic retries or NAK responses for detected errors.[37][39][40]