Ben Eater
Because PS/2 keyboards emit raw multi-byte make-and-break scancodes without native ASCII mapping, host systems must rely on hardware buffers, interrupts, or preprocessors to reconstruct keystroke state.
AI-assisted summary of So how does a PS/2 keyboard interface work? — original source. Check the original for full context.
Source published:
Key ideas
- The standard PS/2 keyboard interface operates over four active lines—power, ground, clock, and serial data—transmitting 11-bit frames per event clocked on falling edges. (0:00)
- Converting serial PS/2 bitstreams to parallel data using 74HC595 shift registers requires inverting the keyboard clock and inserting an inverter delay stage to prevent latching hazards. (4:38)
- Key events are communicated via arbitrary scancodes, break prefixes (F0), and extended prefixes (E0), requiring the host system to manage key state, modifiers, and multi-byte framing. (23:41)
- PS/2 host interfaces can be implemented through cascaded shift register hardware buffers, direct interrupt service routines, or intermediate microcontrollers that translate scancodes directly to ASCII. (30:25)
Detailed notes
## Background This video was published on YouTube by Ben Eater on March 6, 2021. According to the page description, it explores how a PS/2 keyboard interface works, providing a link to a list of scancodes and pointing viewers to eater.net for computer engineering resources and 6502 computer kits.
## TL;DR Ben Eater reverse-engineers the signaling and protocol of a standard PS/2 keyboard to interface it with breadboard hardware. By probing the physical pinout and inspecting clock and data lines on an oscilloscope, he demonstrates that keystrokes transmit as 11-bit serial frames comprising start, data, parity, and stop bits. Using 74HC04 inverters to correct clock polarity and propagation delay, he cascades 74HC595 shift registers to capture, latch, and visually display the serial stream on LEDs. Through this hardware, he decodes raw scancodes, explains make and break codes, analyzes multi-byte sequences for extended keys, and discusses system-level interface architectures for host microprocessors like the 6502.
## Atmosphere & style The presentation carries Ben Eater's signature calm, methodical, and deeply instructional rhythm. It feels like leaning over a quiet electronics workbench alongside an experienced engineer who inspects every wire, waveform, and logic level with deliberate clarity. The tone is practical, patient, and inquisitive, shifting naturally from basic continuity testing to deep protocol exploration with quiet satisfaction as rows of breadboard LEDs progressively untangle the keyboard's serial stream.
## Bulletin - [00:00](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=0s) The standard PS/2 keyboard interface operates over four active lines—power, ground, clock, and serial data—transmitting 11-bit frames per event clocked on falling edges. - [04:38](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=278s) Converting serial PS/2 bitstreams to parallel data using 74HC595 shift registers requires inverting the keyboard clock and inserting an inverter delay stage to prevent latching hazards. - [23:41](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1421s) Key events are communicated via arbitrary scancodes, break prefixes (F0), and extended prefixes (E0), requiring the host system to manage key state, modifiers, and multi-byte framing. - [30:25](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1825s) PS/2 host interfaces can be implemented through cascaded shift register hardware buffers, direct interrupt service routines, or intermediate microcontrollers that translate scancodes directly to ASCII.
## Key takeaways - [00:00](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=0s) A standard PS/2 6-pin mini-DIN interface requires only four physical lines: +5V power, ground, synchronous clock, and serial data. - [03:25](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=205s) Oscilloscope inspection reveals that the keyboard outputs synchronous serial data bits aligned with dedicated clock pulses. - [04:38](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=278s) The PS/2 clock rests high and transmits data valid on its falling edge, necessitating an inverter to trigger rising-edge 74HC595 shift register clocks. - [09:50](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=590s) Connecting the shift clock directly to the latch clock introduces a race condition that can be resolved by passing the signal through two spare inverter gates for propagation delay. - [19:21](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1161s) Every PS/2 transmission consists of an 11-bit frame: a 0 start bit, eight data bits, an odd parity bit, and a 1 stop bit. - [13:10](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=790s) Cascading multiple 74HC595 shift registers by chaining serial out to serial in enables the capture of packets exceeding 8 bits. - [17:07](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1027s) PS/2 keyboards do not transmit ASCII characters; instead, they output arbitrary scan codes (e.g., 2E for '5', 16 for '1') that require external decoding. - [23:41](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1421s) Standard key releases trigger an explicit break code composed of prefix F0 followed by the key's original make code. - [21:15](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1275s) Extended keys, such as navigation keys, emit the E0 prefix during press and produce three-byte sequences (E0 F0 [scancode]) upon release. - [26:52](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1612s) The keyboard hardware has no internal concept of capitalization; typing capital 'A' produces separate make and break events for Shift and A that the host must track. - [28:47](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1727s) Many extended navigation keys share base scancodes with the numeric keypad (e.g., Home and Keypad 7 sharing 6C) due to historical PC keyboard evolution. - [28:47](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1727s) Special keys exhibit extreme protocol outliers, such as Pause sending an eight-byte sequence with no break code, and Print Screen requiring multi-byte make and break bursts. - [28:47](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1727s) Keyboard status LEDs (like Num Lock) are not toggled locally by the keyboard, but require bidirectional control commands from the host computer. - [30:25](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1825s) Interfacing a PS/2 keyboard to a microprocessor can be achieved via memory-mapped shift register buffers, CPU interrupt routines, or dedicated microcontroller preprocessors.
## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=0s) Identifying the PS/2 Connector Pinout
Keyboards remain among the most familiar input devices in computing, and for decades the PS/2 interface was ubiquitous across personal computers. Because of its conceptual and electrical simplicity, it serves as an ideal peripheral interface for breadboard projects, such as a custom home-built 6502 computer. Ben Eater points out that anyone interested in constructing one can find kits and design videos at eater.net/6502.
A standard PS/2 port features a 6-pin mini-DIN connector, but only four pins are actively utilized: two supply electrical power, while the remaining two carry the clock and data signals that convey keystroke information. To interface with the keyboard, Ben takes an extension cable with a mating female PS/2 receptacle, snips the opposite end off, and strips away the outer jacket. Inside the shielding lie four thin conductors: red, black, orange, and brown. Ben strips these delicate wires using his fingernails and twists the wire strands together to prepare them for testing.
```mermaid flowchart TD accTitle: PS/2 Mini-DIN Pinout Mapping accDescr: Mapping of the four active pins of a PS/2 mini-DIN connector to wire colors and roles.
subgraph Connector["PS/2 6-Pin Mini-DIN"] P3["Pin 3: Ground"] P4["Pin 4: +5V Power"] P1["Pin 1: Data"] P5["Pin 5: Clock"] end
subgraph Harness["Stripped Cable Wires"] W_Orange["Orange Wire (+ Shield)"] W_Black["Black Wire"] W_Red["Red Wire"] W_Brown["Brown Wire"] end
P3 --> W_Orange P4 --> W_Black P1 --> W_Red P5 --> W_Brown ```
Using a digital multimeter in continuity mode, Ben probes between the physical socket pins and the bare wires to determine the harness pinout. The pinout reveals a somewhat counterintuitive color scheme:
- **Pin 3 (Ground):** Connects to the orange wire; Ben twists this lead together with the cable's braided shield ground. - **Pin 4 (+5V VCC):** Traces to the black wire, prompting Ben to remark on the confusing choice: “So 5 volts is black that figures it's kind of confusing.” - **Pin 1 (Data):** Traces to the red wire. - **Pin 5 (Clock):** Traces to the brown wire.
Connecting a standalone 5V DC power adapter, Ben wires +5V to the black wire and ground to the orange wire and shield braid using jumper leads. Leaving clock and data unconnected, he plugs the keyboard into the cable socket. The keyboard status indicator LEDs flash momentarily on power-up, verifying that the keyboard is receiving power.
---
[03:25](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=205s) Probing Clock and Data Signals on the Oscilloscope
To observe the keystroke signaling directly, Ben attaches two oscilloscope probes to the harness: the primary ground clip clamps to the keyboard ground wire, channel one connects to the clock lead (brown), and channel two taps the serial data line (red).
Upon pressing and releasing keys on the keyboard, bursts of activity appear across both oscilloscope channels. Zooming in on the waveforms reveals a synchronous serial transmission: the clock line outputs a distinct train of discrete pulses, while the data trace transitions between logic high (+5V) and logic low (0V). On each individual clock cycle, the data line establishes a stable state representing a binary `0` or `1`. Ben observes:
> “And looks like we're getting some kind of signal here so we have our clock which has a bunch of clock pulses and then we have the data down here and it looks like on each clock pulse you know the data is either going to be a zero or a one so we're getting just a series of bits that are coming out of the keyboard which makes sense and you'd expect a different series of bits depending on which key we press.”
Different keys yield distinct digital bit sequences on the screen. The fundamental engineering challenge is converting this two-wire serial stream—operating bit-by-bit over time—into an 8-bit parallel byte that can be placed onto a standard computer system data bus.
---
[04:38](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=278s) Using a 74HC595 Shift Register and Inverting the Clock
Converting a serial bitstream into parallel data is the classic role of a shift register. Ben selects the 74HC595 8-bit serial-in, parallel-out shift register with output latches. Internally, each clock transition on the shift register advances the data: the state at serial input `SER` (pin 14) is sampled and shifted into the internal stage $Q_A$, while previous bits shift downstream along outputs $Q_A$ through $Q_H$. After eight consecutive clock pulses, eight bits reside across the register outputs. In the 74HC595, these internal stages feed into an output storage latch controlled by a separate latch clock (`RCLK`, pin 12), allowing an entire byte to appear simultaneously on the output pins.
```mermaid flowchart LR accTitle: Keyboard Clock Inversion and Shift Register Interfacing accDescr: Keyboard clock is inverted by a 74HC04 to match the 74HC595 rising edge trigger.
subgraph Keyboard["PS/2 Keyboard"] K_CLK["Clock (Normally High, Falls on Bit)"] K_DATA["Serial Data"] end
subgraph Logic["74HC04 Hex Inverter"] INV["Inverter Gate"] end
subgraph ShiftReg["74HC595 Shift Register"] SRCLK["SRCLK (Pin 11): Shift Clock (Active Rising)"] SER["SER (Pin 14): Serial Data Input"] QA_QH["QA-QH: Parallel Outputs"] end
K_CLK -->|Falling edge pulse| INV INV -->|Rising edge pulse| SRCLK K_DATA --> SER ```
Ben extends the four keyboard wires into breadboard-friendly male jumper pins, keeping the wire colors identical to the original harness (black for +5V, orange for ground, red for data, and brown for clock). He seats the 74HC595 on the breadboard, ties the top and bottom power rails together, and supplies VCC and ground to the chip. He then connects the keyboard data line (red) to pin 14 (serial data input `SER`) and pulls output enable ($\overline{\text{OE}}$, pin 13) low to ground, keeping the output drivers continuously active.
When inspecting the clock timing, Ben identifies a critical signaling mismatch between the PS/2 protocol and the 74HC595:
> “Although there's one little problem with that which is if you look at the clock signal that we're getting you can see it normally sits here at 5 volts and then the clock pulse is actually going low and so the way that this works is that each bit is clocked on the falling edge of the clock not on the rising edge whereas the shift clock on the 74HC595 this is this is going to be shifting on the rising edge and so to convert that falling edge to a rising edge what we could do is actually just invert this this clock signal because if we just run this whole clock signal through an inverter then instead of this being a falling edge it'll be a rising edge and it'll clock at the right time.”
To fix the polarity, Ben installs a 74HC04 hex inverter on the breadboard, connects its VCC and ground pins, feeds the keyboard clock line (brown) into one of the inverter inputs, and routes the inverted output directly to the 74HC595 shift clock (`SRCLK`, pin 11). As a result, the falling edge produced by the keyboard turns into a sharp rising edge that clocks serial data into the shift register at the precise midpoint of each data bit. Finally, Ben notes pin 10 ($\overline{\text{SRCLR}}$), the active-low shift register clear/reset pin, which must not be left floating or pulled low during normal shifting operation.
[09:50](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=590s) Addressing the Shift Register Propagation Delay
Ben Eater begins by finishing up the basic configuration of the first 74HC595 shift register. The reset pin (pin 10) is an active-low signal; leaving it unasserted is critical, as Eater notes: “we don't really want to do that in normal operations so normally we want this reset to be high.” He ties pin 10 directly high to +5V. To latch serial data onto the parallel output pins, the chip requires an active clock pulse on the latch clock pin. The most straightforward approach seems to be tying the latch clock directly to the shift clock, ensuring that whenever a clock pulse arrives, the bit shifts into the internal registers and immediately transfers to the external storage registers.
```mermaid flowchart LR accTitle: Clock Delay Circuit Using Inverters accDescr: Diagram illustrating how the PS/2 clock signal is inverted to create the shift clock and delayed via two extra inverters to drive the latch clock.
subgraph Inputs ["Inputs"] CLK["PS/2 Clock Signal"] end
subgraph DelayStages ["Inverter Delay Stages"] INV1["Inverter 1<br>(74HC04)"] INV2["Inverter 2<br>(74HC04)"] INV3["Inverter 3<br>(74HC04)"] end
subgraph Outputs ["Shift Register Clocks"] SRCLK["Shift Clock<br>(Shift Register)"] RCLK["Latch Clock<br>(Delayed ~doz ns)"] end
CLK --> INV1 INV1 --> SRCLK INV1 --> INV2 INV2 --> INV3 INV3 --> RCLK ```
However, a direct connection introduces a serious timing hazard: shifting and latching occur at the exact same instant, meaning the latch may capture old, unstabilized internal state before the newly shifted bit has propagated. Eater explains: “so ideally what we want is we want this latch clock to happen a little bit after the shift clock so we want the shift clock coming in to read that data put it out on the output of the shift register and then just just a little a moment later we want the latch clock to then you know once this data stabilized copy it to our outputs here.”
To create this tiny delay without complex hardware, he passes the inverted clock signal through two spare inverters on the 74HC04 hex inverter chip. The two extra inversions leave the logic polarity untouched, but they introduce a delay of a few dozen nanoseconds. Ben routes this delayed signal to the latch clock pin, guaranteeing reliable updates.
- Pin 10 (Reset / $\overline{\text{SRCLR}}$) tied high to +5V to prevent clearing outputs. - Inverter 1 outputs the rising-edge shift clock. - Inverters 2 and 3 add a series propagation delay of several dozen nanoseconds. - Delayed output drives pin 12 (Latch Clock / RCLK).
---
[13:10](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=790s) Cascading Shift Registers for 11-Bit Packets
With the clocking resolved, Eater prepares to visualize the output by placing an array of LEDs on the breadboard, pulling their cathodes to ground via 220-ohm current-limiting resistors. He notes a mismatch in bit counts: while the 74HC595 provides 8 bits of storage and an ordinary keystroke might be expected to occupy a single byte, counting the oscilloscope clock bursts reveals exactly 11 pulses per transmission. Because the PS/2 protocol sends 11 bits for every single key event, a single 8-bit shift register drops early data bits.
To capture the entire 11-bit frame, Eater adds a second 74HC595 shift register in cascade: “and so the 74HC595 has only got eight bits but fortunately it also has the serial data output which is really just taking that last bit and allowing you to feed it into the input of another 74HC595 so you can cascade these so that as bits shift through here as they kind of shift and fall out the the last bit here you can then feed them in to another one of these and they and they continue to to populate.”
```mermaid flowchart LR accTitle: Cascaded 74HC595 Shift Registers accDescr: Flow showing serial data entering the first 74HC595 shift register and cascading from pin 9 into pin 14 of the second register to capture 16 bits.
DATA["Serial Data<br>(from PS/2)"] -->|"Pin 14 (SER)"| U1["74HC595 (Chip 1)<br>Bits 0–7"] U1 -->|"Pin 9 (Q7S)"| U2["74HC595 (Chip 2)<br>Bits 8–15"]
CLK_S["Shift Clock"] -.-> U1 CLK_S -.-> U2
CLK_L["Latch Clock"] -.-> U1 CLK_L -.-> U2 ```
The wiring steps for chaining the two registers are: - Second 74HC595 power (pin 16 to +5V) and ground (pin 8 to 0V) connected. - Output Enable (pin 13, active-low) tied directly to ground to keep outputs enabled. - Shift Register Reset (pin 10, active-low) pulled high to +5V. - Serial Data Output (pin 9, $Q_{7S}$) of chip 1 connected to Serial Data Input (pin 14, $SER$) of chip 2. - Shift clocks (pin 11) and latch clocks (pin 12) wired in parallel across both chips.
With two cascaded chips, the circuit can record up to 16 bits; the first 11 bit outputs are connected directly to 11 LEDs (QA through QH on the first chip, followed by the first three outputs of the second chip), with the final three LEDs using different colors to help visually separate the payload from the framing bits.
---
[17:07](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1027s) Decoding Scancodes and the 11-Bit Protocol
Ben ties the breadboard’s power and ground rails to the external supply already powering the keyboard (orange wire to ground, black wire to +5V) and tests live keypresses. Striking keys immediately sets the LEDs into distinct binary patterns. Looking up these values on a standard PS/2 keyboard scancode table confirms the payload matches expectations. For example, pressing the "5" key yields an 8-bit visual pattern across the primary green LEDs of `11011110` in reverse order, which represents hex `2E`—the exact scancode for 5. Pressing "1" produces the scancode `16` in hexadecimal, perfectly verifying the electrical capture: “so that makes sense the keyboard is giving us eight bits of data and those eight bits of data convert into a key that was pressed.”
Eater then addresses why 11 total bits exist when only 8 are needed for the key scancode: “well that has to do with the protocol that the keyboard uses for error detection.” PS/2 frames every byte using fixed start and stop bits alongside an error-checking bit. The keyboard always issues a `0` as its initial start bit. As bits are clocked in sequentially, this first bit gets shoved all the way down the cascaded shift register to the very end of the LED line by the time the final bit lands.
- Key "5" scancode: `2E` (hex). - Key "1" scancode: `16` (hex). - Bit sequence structure: Starts with a dedicated `0` start bit, followed by 8 data bits, a parity bit, and a concluding stop bit (`1`). - The earliest received bits end up at the highest-index LED locations as subsequent bits push them down the register pipeline.
[19:21](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1161s) PS/2 Frame Structure and Parity Checking
Ben Eater breaks down the 11-bit frame emitted by the PS/2 keyboard. Reading the breadboard display from right to left, the very first bit shifted out is always a `0`. This is the start bit, an unalterable rule of the protocol. Following that initial zero are the eight data bits, then a parity bit, and finally a stop bit which must always be a `1`. Because these framing bits are fixed, inspecting the edges provides an instant diagnostic: if the rightmost bit is not `0` or the leftmost bit is not `1`, the receiver is out of synchronization or the data packet is corrupt. As Eater puts it: “And if that's ever not the case then that means that there was some kind of problem with the data that we got from the keyboard so either we're not synchronized with uh you know the 11 bits we're showing here aren't uh aren't you know a valid packet of 11 bits coming from the keyboard or you know some some other problem so that's a good sanity check to make sure that we're actually getting valid data from the keyboard is to make sure that the start bit and stop bit are set correctly.”
Directly following the eight data bits sits a yellow LED representing the parity bit. The PS/2 standard specifies odd parity, meaning that the sum of the logic `1` bits across the eight data bits plus the parity bit must always equal an odd number. Eater tests this on the board: pressing a key with three data bits set produces an odd count, so the parity bit remains `0`. When he presses the `2` key, its scan code is `1E` hex (`0001 1110` in binary), which has four bits set to `1`. To maintain an odd sum, the keyboard sets the parity bit to `1`, bringing the total to five. If any single bit flips during transmission, the parity check fails and alerts the system to the corruption.
```mermaid flowchart LR accTitle: PS/2 11-Bit Serial Frame Format accDescr: Layout of the 11-bit serial packet received from a PS/2 keyboard, showing the start bit, data byte, odd parity bit, and stop bit.
subgraph Frame["PS/2 11-Bit Data Packet"] direction LR S0["Bit 0: Start Bit<br/>(Always 0)"] D["Bits 1–8: Data Bits<br/>(Scan Code Byte, LSB First)"] P["Bit 9: Parity Bit<br/>(Odd Parity)"] S1["Bit 10: Stop Bit<br/>(Always 1)"]
S0 --> D D --> P P --> S1 end ```
- A complete PS/2 transmission frame consists of 11 bits: 1 start bit (`0`), 8 data bits (least significant bit first), 1 parity bit (odd parity), and 1 stop bit (`1`). - Frame boundary validation: checking that the start bit is `0` and the stop bit is `1` serves as an immediate framing sanity check. - Single-bit error detection: odd parity guarantees that an odd number of `1`s must appear among the nine non-framing bits (8 data + 1 parity).
[21:15](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1275s) Chaining Shift Registers for Multi-Byte Sequences
While one could theoretically build a rudimentary computer interface by piping the eight data bits into an input port on a peripheral interface adapter and reading the most recent keypress, the PS/2 protocol is far more complex than a simple one-to-one byte lookup. Looking down a table of standard scan codes reveals that some keys emit 16-bit sequences rather than single bytes. For instance, the `Home` key produces the scan code `E0 6C`. When Eater hits `Home` with only an 11-bit register connected, the LEDs display only `6C`—the final byte. The preceding `E0` byte arrives so rapidly that it is overwritten immediately. Looking at an oscilloscope zoomed out confirms that pressing `Home` transmits two distinct 11-bit bursts in rapid succession.
Capturing both bytes simultaneously requires expanding the circuit from 11 bits to 22 bits. Eater takes a second, identical breadboard module containing an 11-bit shift register and latch, placing it below the first. He ties their shift clocks and latch clocks together so they step and latch in lockstep. To chain the data flow, he routes the serial output of the 11th bit of the top register into the serial data input of the bottom register. As new bits enter the top register from the keyboard, the older bits spill out of its final stage and cascade into the lower register. Pressing and holding `Home` now cleanly displays `E0` on the bottom bank and `6C` on the top bank, capturing the entire 16-bit make code.
- Simple scan codes occupy 8 bits (1 byte), but extended keys require at least 16 bits (2 bytes). - The `Home` key make code is `E0 6C`; single-stage 11-bit capture only caught the second byte (`6C`) before the first byte could be examined. - Chaining two 11-bit shift registers creates a 22-bit buffer by wiring the 11th bit output of the primary register into the serial input of the secondary register. - Shared clock and latch lines ensure all stages advance and update output LEDs in unison.
[23:41](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1421s) Make Codes, Break Codes, and Extended Sequences
A curious behavior appears when releasing the `Home` key: the display switches to `F0 6C`. Zooming out further on the oscilloscope shows that while pressing `Home` sends two 11-bit frames, releasing it generates three full packets. Standard keys behave symmetrically: pressing `A` generates a single byte (`1C`), but releasing `A` sends two bytes (`F0 1C`). The keyboard explicitly informs the host computer when a key is released via a "break code." The byte `F0` is the release prefix; seeing `F0` followed by `1C` means that key `A` has just been released. Holding down `A` causes the LED to pulse every ~100 ms as the keyboard repeatedly retransmits the make code (`1C`) for typematic auto-repeat.
```mermaid flowchart TD accTitle: PS/2 Key Event Scan Code Sequences accDescr: Progression of byte sequences generated for standard keys versus extended keys during press, repeat, and release events.
subgraph StandardKey["Standard Key (e.g., 'A')"] direction TB A_Press["Press / Hold<br/>'1C' (Repeats)"] --> A_Rel["Release<br/>'F0 1C'"] end
subgraph ExtendedKey["Extended Key (e.g., 'Home')"] direction TB H_Press["Press / Hold<br/>'E0 6C' (Repeats)"] --> H_Rel["Release<br/>'E0 F0 6C'"] end ```
For extended keys, the sequence incorporates both modifiers. The `E0` byte marks an extended scan code, while `F0` indicates release. Thus, releasing `Home` transmits `E0 F0 6C`. To see all three bytes simultaneously, Eater wires in a third 11-bit module in series, connecting the output of register two's 11th bit to the input of register three. Pressing and releasing `Home` now visibly fills the registers in order: `E0`, then `F0`, and finally `6C`. As Eater explains: “And essentially the release code for for home is e0 to say that it's an extended scan code followed by the f0 to say it's being released and then the 6c.”
- Typematic repeat: holding down any key causes the keyboard to periodically resend its make code roughly every 100 milliseconds. - Break code prefix: standard keys emit `F0` followed by the key's scan code when released. - Extended break code structure: extended keys emit three consecutive bytes on release: the extended prefix `E0`, the release prefix `F0`, and the base code (e.g., `E0 F0 6C` for `Home`). - A 33-bit chain of three shift registers successfully buffers all three bytes of an extended release sequence.
[26:52](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1612s) Buffering a History and the Anatomy of Typing
Having proven the cascaded design, Ben Eater takes the idea to its logical conclusion: “And so you can see we're kind of keeping a history of all of the scan codes coming in and you know in principle we could keep adding shift registers to uh to be able to store as many scan codes or sort of buffer if you will as many scan codes as as we want and so that's pretty much what i did just keep building until i got bored.” With eight chained breadboard registers running, he can observe eight sequential scan codes scroll across the desk like a hardware FIFO queue.
Typing `abc` reveals the interleaved sequence of makes and breaks: pressing `A`, releasing `A` (`F0 1C`), pressing `B`, releasing `B`, pressing `C`, and releasing `C`. Testing a capitalized letter demonstrates that the keyboard hardware possesses no native concept of uppercase ASCII characters. Typing a capital `A` transmits four distinct actions: press `Shift`, press `A`, release `A`, and release `Shift`. Host software must track which keys are currently held down to infer capitalization or control commands.
Examining the scan code values themselves reveals little obvious pattern, but historical computer hardware design offers clues. The `Home` key uses extended code `E0 6C`. Looking up standalone `6C` reveals it belongs to the `7` key on the dedicated numeric keypad (distinct from top-row `7`, which is `3D`). Because older PC keyboards lacked dedicated navigation arrow and editing clusters, the numeric keypad doubled as navigation keys when Num Lock was off; modern keyboards preserve that legacy by sending the numeric keypad's scan code prefixed by `E0` when dedicated navigation keys are pressed.
- Chaining eight registers creates an 8-byte historical pipeline displaying keyboard event streams in real time. - Capitalization protocol: typing capital `A` sends four separate events: `Shift` make, `A` make, `A` break, and `Shift` break. - Host responsibility: character decoding, state tracking (Shift, Ctrl, Alt), and typematic interpretations are left entirely to the receiving computer. - Historical scan code reuse: extended navigation keys reuse numeric keypad scan codes prefixed with `E0` (e.g., dedicated `Home` is `E0 6C`, while keypad `7/Home` is plain `6C`).
[28:47](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1727s) Scan Code Oddities, Num Lock, and Multi-Byte Outliers
Ben Eater points out that the standalone Home key shares the exact same primary code as the numeric keypad's 7 key when Num Lock is turned off; the dedicated navigation key simply sends an extended prefix to distinguish itself. Despite these occasional logical pairings, Ben notes that the overall layout of PS/2 scan codes feels arbitrary: “But I have to say overall the scan codes don't make a ton of sense why they're assigned what they are; I'm sure there's some reason and maybe someone in the comments will know more about that.” Furthermore, the physical keyboard does not maintain state machine awareness for toggle keys. Pressing Num Lock emits a code of `77`, identical in handling to any ordinary character key. It is entirely up to the host computer's interface logic to track state and send control bytes back over the bidirectional PS/2 link to physically illuminate or extinguish the keyboard LEDs.
```mermaid flowchart TD accTitle: PS/2 Special Key Sequences accDescr: Diagram illustrating the byte patterns emitted by Pause and Print Screen keys.
subgraph PauseKey ["Pause / Break Key"] P_Press["Key Press (Make)"] --> P_Bytes["8 Continuous Bytes Emitted"] P_Release["Key Release (Break)"] --> P_None["No Break Code (Silent)"] end
subgraph PrintScreenKey ["Print Screen Key"] PS_Press["Key Press (Make)"] --> PS_Make["4 Bytes (32-bit sequence) repeats on hold"] PS_Release["Key Release (Break)"] --> PS_Break["6 Bytes (48-bit sequence)"] end ```
The protocol exhibits even stranger edge cases with keys like Pause and Print Screen. When the Pause key is pressed, it immediately bursts out an eight-byte make code and sends absolutely nothing upon release. By contrast, Print Screen generates a 32-bit (4-byte) make sequence that repeats while held down, followed by a massive 48-bit (6-byte) break sequence when let go. Ben admits that while historical quirks undoubtedly drove these implementations, they create bizarre exceptions that any decoder interface must handle.
- The numeric keypad's 7 key functions as Home when Num Lock is disengaged. - Num Lock emits scan code `77` on press; tracking state and toggling status LEDs requires sending commands from host to keyboard over the bidirectional line. - The Pause key emits an 8-byte make sequence on press and generates no break code upon release. - The Print Screen key emits a 4-byte make code on press and a 6-byte break code on release.
[30:25](https://www.youtube.com/watch?v=7aXbh9VUB3U&t=1825s) Evaluating Hardware Interface Architectures
Looking back at the breadboard contraption covered in LEDs, Ben reflects on whether building such a complex circuit just to read a keyboard is unreasonable: “At least in principle a design like this is not that crazy for a keyboard interface.” Although outfitting the entire signal chain with 88 individual LEDs looks wild on a workbench, those LEDs merely represent 88 bits—or 11 bytes—of shift register memory storage. When connecting to a custom computer system like his breadboard 6502 build, that shift register buffer can easily be mapped into a block of memory-mapped I/O addresses. A host CPU could then directly read incoming scan codes off the system bus at its leisure.
Ben outlines three distinct design architectures for handling PS/2 communication, ranging from passive hardware buffering to intelligent offloading:
```mermaid flowchart LR accTitle: PS/2 Interface Implementation Options accDescr: Three architectural methods for connecting a PS/2 keyboard to a host CPU.
subgraph Buffering ["Shift Register Buffering"] KB1["Keyboard"] --> SR["Shift Register Buffer"] SR -->|"Memory-mapped I/O"| CPU1["Host CPU"] end
subgraph Interrupt ["Direct Interrupts"] KB2["Keyboard"] -->|"Byte ready"| INT["Interrupt Request (IRQ)"] INT --> CPU2["CPU ISR Reads Byte"] end
subgraph Microcontroller ["Smart Preprocessor"] KB3["Keyboard"] --> MCU["Dedicated MCU"] MCU -->|"ASCII & modifier state"| CPU3["Host CPU"] end ```
The first approach uses dedicated hardware buffers (like the breadboard's shift registers) mapped across an address window where the CPU checks recent keystrokes. The second method connects the keyboard's clock or framing logic directly to a CPU interrupt line; each incoming scan code triggers an interrupt service routine that reads the byte immediately before another arrival overwrites it. The third, most sophisticated architecture uses an intermediate microcontroller to interpret the low-level PS/2 protocol. Because scan codes bear zero structural resemblance to ASCII characters—for instance, scan code `1` is `F9`, `3` is `F5`, and `4` is `F3`—a small dedicated MCU can maintain shift/modifier states and translate raw scancodes into ASCII before presenting clean data to the main processor.
- The demonstration breadboard uses 88 LEDs to visualize 11 bytes of shift register memory. - An I/O space with designated addresses allows the host CPU to read buffered scan codes directly off the bus. - An interrupt-driven architecture requires the CPU to service each incoming scan code immediately before the keyboard transmits the next byte. - PS/2 scan codes do not follow sequential character ordering or ASCII values (e.g., scan code `1` is `F9`, `3` is `F5`, and `4` is `F3`). - Kits for building a complete 6502-based computer system are available at Ben's website, `eater.net/6502`.
## Mentioned Ben Eater, PS/2, 6502, 74HC595, 74HC04
## Video description Check out https://eater.net for more computer engineering stuff! List of scancodes from the video: https://docs.google.com/document/d/1w--ouGvSSf93tFltmBcSRGMN8Rte7ayalQZRDPG0ezk/edit
Support these videos on Patreon: https://www.patreon.com/beneater or https://eater.net/support for other ways to support.
------------------
Social media: Website: https://www.eater.net Twitter: https://x.com/beneater Patreon: https://patreon.com/beneater Reddit: https://www.reddit.com/r/beneater
Special thanks to these supporters for making this video possible: Aleksey Smolenchuk, Alexander Wendland, Andrew C. Young, Anson VanDoren, Anthanasius, Armin Brauns, Ben Cochran, Ben Dyson, Ben Kamens, Ben Williams, Bill Cooksey, Binh Tran, Bouke Groenescheij, Bradley Pirtle, Bradley Stach, Brent Reusing, Brian T Hoover, Bryan Brickman, Bryan Glezerson, CaffeinatedClockmaker, Carlos Ambrozak, Christopher Blackmon, Dale Andrew Darling, Daniel Jeppsson, Daniel Tang, Dave Burley, Dave Walter, David Brown, David Clark, David Cox, David House, David Sastre Medina, David Turner, David Worsham, Dean Winger, Dissy, dko, Dmitry Guyvoronsky, dstensnes, Dušan Dželebdžić, Dzevad Trumic, Emilio Mendoza, Eric Busalacchi, Eric Dynowski, Erik Broeders, Eugene Bulkin, George Foot, George Miroshnykov, Harry McDow, Ingo Eble, Ivan Sorokin, james schaefer, Jared Dziedzic, Jason DeStefano, Jason Specland, JavaXP, Jay Binks, Jayne Gabriele, Jeffery Springs, Jeremy A., Jim Kelly, Jim Knowler, Jim Van Meggelen, Joe Beda, Joe OConnor, Joe Pregracke, Joel Jakobsson, Joel Miller, John Fenwick, John Meade, Jon Dugan, Joseph Portaro, Joshua King, Kai Wells, Kefen, Kenneth Christensen, Kent Collins, Koreo, Lambda GPU Workstations, Larry, László Bácsi, Lukasz Pacholik, Marcus Classon, Martin Roth, Mats Fredriksson, Matt Krueger, Matthäus Pawelczyk, melvin2001, Michael Garland, Michael Tedder, Michael Timbrook, Michael Weitman, Miguel Ríos, mikebad, Mikel Lindsaar, Miles Macchiaroli, moi n, Nicholas Counts, Nicholas Moresco, Nick, Örn Arnarson, Paul Pluzhnikov, Paul Randal, Pete Dietl, Phil Dennis, Philip Hofstetter, Phillip Glau, PixelSergey, ProgrammerDor, Randal Masutani, Randy True, Raoul van Prooijen, Renaldas Zioma, Ric King, Rob Bruno, Robert Comyn, Robert Diaz, Roland Bobek, sam raza, Sergey Kruk, SonOfSofaman, Stefan Nesinger, Stefanus Du Toit, Stephen, Stephen Riley, Stephen Smithstone, Steve Jones, Tayler Porter, TheWebMachine, Tom, Tyler Latham, Vincent Bernat, Vladimir Solomatin, Walter Montalvo, Warren Miller, xisente, Yee Lam Wan, דויד אללוף
[Source](https://www.youtube.com/watch?v=7aXbh9VUB3U)