Конечно, неизбежное "сваливание" программы при возникновении любой ошибки – ситуация довольно неприятная. Было бы неплохо, если при ошибке выполнялась бы какая-то собственная процедура, а затем выполнение программы продолжалось. К счастью, в программировании нет ничего невозможного.
В самых младших адресах оперативной памяти операционная система хранит векторы прерываний (interrupt vectors). Вектор прерывания представляет собой указатель на адрес в памяти, начиная с которого расположена программа обработки этого прерывания. Самое важное – вектор прерывания можно изменить и заставить компьютер при возникновении ошибки выполнять собственный программный код. Каждое прерывание (и его вектор) имеет свой номер от 0 до 255. Например, нажатие клавиши вызывает прерывание с номером 9, движение мыши – с номером 13 и т.д.
В программе надо будет сначала запомнить указатель на старый обработчик прерывания, затем заменить старый вектор новым, а в конце работы программы обязательно все вернуть на место. Если этого не сделать, возникнет катастрофическая ситуация: ваша программа перехватила прерывание клавиатуры, при любом нажатии клавиши выполняется кусочек кода вашей программы. Затем программа прекратила работу, ее больше нет в памяти, а вектор прерывания по-прежнему указывает на то место, где раньше располагалась программа. В итоге первое же нажатие клавиши вполне может привести к зависанию компьютера.
Кстати, ряд прерываний происходит достаточно часто. Пользователь нажимает на клавиши, двигает мышь… Прерывание таймера компьютера происходит каждую 1/18 с. Соответственно программа-обработчик обязана быть небольшой, чтобы не тормозить работу машины.
Помимо этого, обработчик прерывания должен обеспечивать выполнение очень важного условия. Обработчик вклинивается в поток команд процессора. Чтобы ничего не нарушить, после окончания работы обработчика все регистры процессора должны находиться в том же состоянии, что и в момент начала его работы. Тогда компьютер "не заметит" прерывания и будет работать дальше, как ни в чем не бывало (Рис. 15.4).
Рис. 19.4. Функции обработчика прерывания.
Как этого достичь? Очевидно, в начале работы обработчик должен где-то запомнить содержимое регистров процессора, а при окончании - восстановить его.