Saturday, January 4, 2014

All your IRQ are belong to us

I did some of my first real professional programming on an early IBM PC running MS-DOS.  Back then "DOS" stood for "Disk Operating System", as opposed to "Denial Of Service" and in the literal sense of "something that will operate a disk drive", that was accurate.  In other respects that "Operating System" implied, even at the time -- things like multitasking, so more than one program could run at once, or memory protection, so that a running program couldn't read or (worse) scribble on memory that didn't belong to it -- well, "Denial Of Service" might have been just as good a description.

Under MS-DOS's BIOS (Basic Input-Output System), applications talked to the system, and the hardware talked to the system, through "Interrupt Requests" or IRQs.  These were basically entries in a table of the form "When this happens, run the code at this address".  The entries in the table were called "vectors", and any particular IRQ had the address of a particular chunk of code, called an interrupt handler or interrupt routine.  For example, the IRQ for key clicks would be vectored to the code for dealing with a key click event.

Dealing with a key click event is not quite as simple as it sounds.  You had to do several things:
  • "Debounce" the key click -- I forget whether the PC did this in hardware or software, but a when a human presses a key on a keyboard, the corresponding circuit doesn't just close, at least not on those early keyboards.  It would go through a period of milliseconds in which the circuit would bounce back and forth between open and closed.  Even to an early PC a millisecond is a fair bit of time, and you wouldn't want to interpret that bouncing as someone typing really fast.
  • Keep track of which shift keys were pressed at the time.  You would do this by keeping a few bits around like "left shift key is up/down", "control key is up/down", etc.  The caps lock key acts differently from the other keys, of course.  Miss an event and you could get CAPITAL LETTERS when you wanted lowercase, or worse, control characters which could cause all kinds of fun.
  • Buffer up key presses in a block of memory so that if the user typed several keys while the main program was thinking, they would still be there to read when it got done thinking.  Actual applications would read characters from a buffer, as 'H', 'e', 'l', 'l', 'o', rather than catching a series of events like left-shift-key-down, h-key-down, h-key-up, shift-key-up, l-key-down ... directly from the keyboard IRQ.
  • Check for magic key sequences like "print screen" or the famous "ctrl-alt-del"
and this is not to mention things like actually displaying the typed character somewhere, or changing the state of a document being edited.  That was all done by the application code.

Keep in mind that the keyboard IRQ was just one IRQ.  There were IRQs for the system's internal timer, for communicating with the disk drive and the modem and printer ports, for applications to talk to the BIOS, and so forth, so imagine the discussion here multiplied by a dozen or so important IRQs.

I mentioned that most applications would be fine with just reading characters from the system's buffer, but some, for example many games, really were interested in the raw events.   There were also utilities you could buy that would allow you to do things like scroll back to text that had scrolled off the screen, or display a clock or check the spelling of what you'd just typed, if you hit a magic sequence of keys.  Because the DOS code sitting on top of the BIOS didn't directly support such things, such programs would "hook" the BIOS's IRQs by changing the IRQ to vector to their code.  Since DOS didn't do memory protection, anyone could Just Do That, and many did.

There are a couple of hazards to this approach.  For one, you didn't necessarily want to completely take over handling of the keyboard.  Many utilities just wanted to hook one magic key sequence to trigger what they did and pass the rest through untouched.  The usual approach to this was to "chain" -- the last thing that a newly-installed interrupt handler would do was to call the handler that had been there before it.  That means you don't care what happens down the line and you don't have to try to replicate what everything else was doing, but it leads to the second hazard.

Suppose I've written a nifty utility that pops up a calendar whenever the user presses ctrl-alt-C, and you've written a nifty utility that pops up a calculator whenever the user presses ctrl-alt-C.  Several things can happen if both of our utilities are installed:
  • Maybe mine was installed last, so that the IRQ is vectored to my handler.  You'll see a calendar when you hit ctrl-alt-C, and you may or may not see anything else
    • Most likely my handler will "eat" the magic keypress by only popping up the calendar,
    • but it might choose to go ahead and chain to whatever handler was there before.  In that case, your handler could also get called, depending on whether any other handlers were installed between ours, and what they do.
  • And likewise, of course, the other way around.
In other words, we have what is technically called "a mess" (or several other things you might imagine).  If your handler is installed last, it owns the world -- or at least the IRQ it handles.  If not, well, all kinds of things could happen, but a likely one is customers calling up saying "I installed your lousy utility and it doesn't work!"

The inevitable consequence: Every utility you bought would implore you to please, pretty please make sure that it's the last one in the AUTOEXEC.BAT script called at startup.  Or, more conveniently but also worse if you're trying to rein in this chaos, its handy installation script would edit AUTOEXEC.BAT to make sure it was the last one to run -- until the next utility with such an install script came along, or until you hand-edited AUTOEXEC.BAT to try to fix some conflict by moving some other utility to the bottom.

Ah, those wacky, wild and carefree days of the PC revolution.  Good thing this sort of thing doesn't happen any more in our modern, wonderful web.world.


Now where was I before this trip down memory lane?  Ah, right.  Cleaning up someone's system after  a couple of shiny-looking downloaded "utilities" reset the default browser, hijacked the search bar to point at a different search engine and left droppings in the startup folder offering to re-install something almost but not quite deleted.  Oh ... and fixing a driver that wasn't up to date.

Ah, progress.