The other day I tried running NSNIPES, a multiplayer networked game that came with old versions of NetWare. The game worked fine, but I couldn’t get out of it. Esc did nothing, any “usual” combinations like Alt+X, Alt+Q or similar had no effect. So I found the game documentation (because of course it was documented in NetWare reference manuals just like any other program shipped with NetWare), and learned that to quit the game, one has to press Ctrl+Break.
Only pressing Ctrl+Break elicited no reaction from NSNIPES. But then I noticed that pressing Ctrl+Scroll Lock did. So… why is that?
There are really two mostly separate questions. Why does NSNIPES treat Ctrl+Scroll Lock as if it were Ctrl+Break, and why does actual Ctrl+Break not work, or only works very intermittently? Let’s answer that one at a time…
Finding the answer to the first question is pretty easy. The original PC had a Scroll Lock key (scan code 70 decimal or 46 hex). When pressed together with the Ctrl key, the Scroll Lock key functioned as a Break key because that’s how the PC BIOS treated it.
The Enhanced 101/102-key keyboards added a separate Pause key; the key is special and when pressed together with Ctrl, it produces a sequence simulating the press and release of a key with scan code 46h but prefixed with E0h. When the key is released, no further scan codes are generated. The Pause/Break key is the only key on a standard keyboard which behaves in this manner.
The 1986 PC/AT BIOS and later systems only trigger Ctrl+Break functionality when the E0h prefix is seen. Older BIOSes ignore the E0h prefix and trigger Ctrl+Break in response to both Ctrl+Break and Ctrl+Scroll Lock.
OK, so that probably explains why NSNIPES reacts to Ctrl+Scroll Lock, but not why it misses Ctrl+Break. Or at least misses Ctrl+Break most of the time.
As it turns out, NSNIPES has its own keyboard interrupt handler (IRQ 1) and completely bypasses the system BIOS. The strategy NSNIPES uses is not unreasonable: The program keeps an array reflecting the state of all keys indexed by their scan code, which is updated by the interrupt handler and periodically checked by the main routine.
The problem is apparently that the main routine does not run in a tight loop and “only” checks the keyboard state several times a second. If the user pressed and released a key really quickly, NSNIPES might miss it. That does not happen in practice because human fingers aren’t that fast.
But microcontrollers simulating human fingers are. Remember the Ctrl+Break key behavior which simulates both a press and a release when it is pressed? This poses no problem to the system BIOS which keeps track of the current modifier key state and processes each scan code as it comes in.
But NSNIPES is different. The simulated key press/release sent by Ctrl+Break on an Enhanced keyboard is fast (somewhere in the range of several milliseconds), much faster than a human. If the NSNIPES game loop does not happen to check the keyboard state in the brief instant between receiving the 46h scan code indicating a key press and the C6h scan code indicating a key release, it will completely miss the key.
And of course the reason why Ctrl+Scroll Lock actually works in NSNIPES is because it behaves like other keys, meaning that it does not send the key release scan code until after the user actually physically releases the key. Hence NSNIPES has enough time to see it pressed. And because NSNIPES is old, it has no special logic for Enhanced keyboards; as far as it is concerned, Ctrl+Scroll Lock is Ctrl+Break.
The joys of backward compatibility.