Обработка программных прерываний

Программное прерывание - это ситуация, возникающая, когда дальнейшее выполнение программы невозможно. Например, деление на ноль, переполнение, ошибка Range check error, обращение по неверному адресу, попытка открыть для чтения несуществующий файл и т.п. Обычная реакция программы на такие события - вывод сообщения об ошибке и аварийное завершение. Мы уже знаем, как предотвращать возникновение некоторых из этих ошибок. Но Паскаль предоставляет программисту возможность обработки прерывания уже после того, как оно произошло. Для этого используются стандартные переменные ExitProc,ExitCode, ErrorAdr и процедура RunError.

Переменная ExitProc типа Pointer может содержать адрес процедуры, которая будет обрабатывать программные прерывания. Эта процедура должна иметь дальний адрес. Если такая процедура предусмотрена в программе, ее адрес (с помощью операции @) нужно присвоить переменной ExitProc. После этого данная процедура будет вызываться всякий раз, когда произойдет программное прерывание. Следует учитывать, что нормальное завершение программы и остановка программы процедурой Halt также являются прерываниями и, следовательно, процедура будет выполняться при любом завершении программы. Запишем пока тривиальную программу:

 

Procedure MyExitProc; Far;

Begin

WriteLn('произошло прерывание');

End;

Var x : Real;

Begin

ExitProc:=@MyExitProc;

Write('Введите число ');

Read(x);

WriteLn('это число в степени 33.3 равно ',Exp(33.3*Ln(x)));

End.

 

Запустим программу и введем число 10, программа выведет на экран результат, а затем сообщение “произошло прерывание”. Теперь введем число20 - программа выведет сообщение “это число в степени 33.3 равно произошло прерывание”, затем сообщение об ошибке “Runtime error 205 at 0000:00F9”, т.е. завершится все равно аварийно. Используем теперь переменные ExitCode иErrorAddr, которые возвращают:

- при нормальном завершении ExitCode = 0 , ErrorAddr = Nil;

- при выполнении процедуры Halt(n) ExitCode = n, ErrorAddr = Nil;

- при аварийном завершении ExitCode = коду ошибки, ErrorAddr = адресу прерывания.

Будем выводить сообщение только при аварийном завершении, т.е. когда ErrorAddr не равен Nil, и менять в этом случае значениеErrorAddr, чтобы предотвратить появление стандартного сообщения об ошибке:

 

Procedure MyExitProc;

Begin If ErrorAddr<>Nil Then Begin

WriteLn('произошло прерывание !!!');

ErrorAddr:=Nil;

End;

End;

 

С такой процедурой наша программа будет выполняться обычным образом для приемлемых чисел, а для неприемлемых станет сообщать “произошло прерывание”. Никаких других сообщений об ошибках не будет. Теперь немного усовершенствуем нашу процедуру, чтобы она сообщала, какая именно ошибка произошла:

 

Procedure MyExitProc;

Begin

If ErrorAddr<>Nil Then Begin

Write('Произошло прерывание');

Case ExitCode Of

106 : WriteLn(' (ошибка ввода)');

207 : WriteLn(' (основание степени <0'));

205 : WriteLn(' (слишком большое число)');

Else WriteLn(' (неизвестная ошибка)');

End;

ErrorAddr:=Nil;

End;

End;

Введем “abc - программа сообщит “ошибка ввода”; введем “-2” - программа сообщит “основание степени отрицательно”; введем “100” - программа сообщит “слишком большое число”. Процедура

Procedure RunError(Errorcode:Byte);

используется главным образом при отладке программ, она генерирует прерывание с заданным кодом. Например, для полной проверки нашей процедуры вставим в текст программы оператор RunError(200); - программа сообщит “неизвестная ошибка”. Это был самый простой пример использования средств обработки прерываний - мы использовали одну нерекурсивную процедуру. Программа может содержать много процедур, обрабатывающих прерывания, и в разных местах программы может быть записано любое количество операторов ExitProc:=@процедура; Кроме того, такие операторы могут быть внутри самой процедуры-обработчика или она может быть рекурсивной, тогда при любом прерывании начнет работать так называемая цепочка процедур, обрабатывающих прерывания. Не составляет большого труда написать программу, в которой весь алгоритм будет записан в процедуре-обработчике прерываний, а главная программа будет состоять всего из двух операторов

ExitProc:=@процедура; RunError(код ошибки);

а все остальное будет делать процедура-обработчик прерываний (или несколько таких процедур), которая будет сразу же вызвана, так как RunError сгенерирует прерывание с заданным кодом.