That E1 would be just another E0 if it wasn't for the fact that E0 14 is right control already. It's not a special code that means Pause, it's just that that's the only place they needed to go to a second level of 'extension' in the PS/2's 'extended' set 2.
There's a ton of room. It could have easily been 0E or E0 01, list goes on for a mile. E1 was always an obviously conscious decision. It is literally the only scan code beginning E1, and the standalone E1 is explicitly prohibited. They didn't
need to 'extend' further - they had well over 100 other unallocated, unreserved codes available. More to the point, Break predates the XT significantly and has been carried forward time and time and time again. Pause also predates PC-AT - it used to be Ctrl-Numlock. (And those following along in the audience just went "... oh @#%*. I SEE IT TOO NOW.")
Wanna know how it worked? Went like this:
LCtrl MAKE -> Num Lock MAKE -> LCtrl BREAK -> Num Lock BREAK
or in Set2 parlance 14, 77, F0 14, F0 77So, like I said, E1 was a deliberate choice to ensure differentiation
without being selective about which Ctrl modifier it was.
E0 14, 77... would be the exact same, except with RCtrl. However, they could have used
E0 1E 77... or
E0 24 77.... But the E0 24 could be confused for an explicit "RCtrl", 84 and 7F were odd BIOS-level functions as I recall. Compare for yourself here:
14, 77, F0 14, F0 77 /* LCtrl + NumLock */
E0 14, 77, E0 F0 14, F0 77 /* RCtrl + NumLock */
E1 14, 77, E1 F0 14, F0 77 /* Break */
The machinations that are gone through to make the cursor keys and nav block compatible with an AT are nasty...
(image snip)
This just proves that how USB HID does it
is incredibly stupid. Well, and that PC-AT remains an ugly kludge in many regards. What's really funny? Peripheral side complexity was added - to reduce it on the host side. (Instead of a check for Shift/Ctrl/Alt in buffer, give it a unique scan code, as those combinations had specific functions. Plus if someone did "BLAHBLAHBLAHBLAH PgUp" you likely only got PgUp.) Remember that HID has the capability for "pages" of an essentially arbitrary nature. But it's not. They crammed two distinct scan codes into one and instead you get stuck with a modifier check dance. Which was my point - which I get the impression you actually
agree with.
Well especially since a lot of that dancing was IBM specific to my knowledge. Modern ITE parts for Shift-PgUp from relatively modern keyboards?
14, E0 7D, E0 F0 7D, F0 14.
They could have basically kept the sane nature of Break/SysRq but instead they did something completely idiotic by cramming them onto the exact same code and increasing the duct-tape-it-together solution illustrated above. It's like when people reimplement the broken parts of software when they rewrite it, but that's not good enough, so they expand the breakage to affect other parts and make it more severe. Despite the fact that there's no actual requirement or reason to maintain the breakage, and a perfectly good solution for removing it.
Remember that in USB HID active and available code pages are 01, 07, 0C and each code page (excepting 07) has a range of 0-FFFF (not 0000). And there is also a high level of host interpretation. Meaning they could have, in fact, put the number pad with NumLock off as 0C,(54-63) and done Pause as 07,48 and Break as 0C,48 - which of course, greatly simplifies the host side since it can now make accurate presumptions about keyboard state with a minimum of overhead on both sides. Instead, they elected to make things more difficult by imposing a new host-side test for Pause/Break differentiation
that could have been avoided. To illustrate more succinctly, here's the gymnastics for USB - atkbd.c watches for 0xE1, if code is not 0x45 carry on, if it is then set 0x68. (atkbd.c has to handle dozens of layouts plus ISA, PCI, MCA and oddities like OFW (Mac), PSM, keyboard MUXing, and ADB to name a few.)
uhid.c:
case 0x68: /* pause/break */
if (sc->sc_flags & CTLS) {
keycode = 0x6c; /* break */
}
But to get back on topic as to the original question:On modern PS/2 keyboards, the keyboard sends the Make code for Shift and only sends the Break code when Shift is released. Until the Break code is received, the host controller (AKA i8042) is supposed to consider the Shift key to be on.
Caps Lock relies on a combination of peripheral plus host test - when pressed, it transmits
58 but the host may send an inquiry of
E0 00000100 (though I think that's set not inquiry) which the keyboard will reply to with
58 for on and
F0 58 for off. YMMV, because honestly, I don't remember the exact inquiry/set methods, but you get the general idea of it.
On USB keyboards, well, this code snippet from FreeBSD ukbd.c is probably a better explanation than I can manage:
uint16_t sc_modifiers;
#define UKBD_FLAG_SHIFT_L 0x00001000
if ((sc->sc_flags & UKBD_FLAG_SHIFT_L) &&
(id == sc->sc_id_shift_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_shift_l))
sc->sc_modifiers |= MOD_SHIFT_L;
else
sc->sc_modifiers &= ~MOD_SHIFT_L;
}
Where sc->sc_buffer is is UKBD_BUFFER_SIZE (64 bytes) and the actual UKBD_NKEYCODE is 6 units.
The complete source code is probably more enlightening, since the FreeBSD UHID implementation's primary function is to translate USB HID into AT. (ukbd_trtab[256] is left as an
exercise to the reader.)
(edit 1; just some formatting cleanup)