48 Глава 2

2.2. МОДУЛЬ F_Jump

Процедура UserExit не делает главного: она не предохраняет программу пользователя от аварийного завершения. Чтобы продолжить исполнение программы после обработки исключительной ситуации, нужно уметь передавать управление из процедуры выхода в нужную точку программы. На первый взгляд такие действия нетрудно реализовать с помощью оператора GOTO, однако вспомним, что все метки локализуются в той подпрограмме, где они описаны, причем никакая метка не может быть глобальной. Иными словами, в Турбо Паскале невозможно осуществить выход из процедуры оператором

GOTO <ВНЕШНЯЯ_МЕТКА>.

Для наших целей придется использовать возможности языка ассембле — ра. Учтем, что работа любой программы на уровне машинных команд управляется содержимым нескольких регистров центрального процессора. Для вычисления адреса очередной команды микропроцессор извлекает номер сегмента из регистра CS, а смещение — из регистра IP; для доступа к данным номер сегмента берется из регистра DS, а смещение — из ВХ или индексного регистра (5/ или DI); наконец, для доступа к программному стеку используется содержимое SS как сегмент стека, а содержимое одного из регистров—указателей [SP или ВР} — как смещение. Таким образом, для организации правильного выхода из процедуры завершения в нужную точку программы необходимо где-то запомнить содержимое этих регистров, которое соответствует корректному исполнению программы с нового места. На практике достаточно запомнить содержимое лишь четырех регистров — SP, ВР, CS и IP. Остальные регистры в ходе работы программы, написанной на Турбо Паскале, или не меняются, или автоматически обновляются.

Для запоминания указанных регистров используем переменные следующего типа:

type

JumpRec = record

SPsave, {Здесь сохраняется SP} BPsave: Word; {Здесь - регистр ВР} JmpPtr: Pointer {Здесь - CS и IP}

end;

В программе можно объявить сколько угодно переменных этого типа и связать их содержимое с различными точками выхода.

Общая схема управления программой, в которой осуществляется блокировка аварийного завершения, должна быть такой. Перед выполнением некоторого критического фрагмента программы, т.е. фрагмента, в котором может возникнуть исключительная ситуация, необходимо запомнить в переменной типа JumpRec нужную информацию (как это делается, мы обсудим ниже) и указать в переменной ExitProc адрес процедуры выхода. Если в ходе исполнения фрагмента возникнет исключительная ситуация, управление будет передано процедуре выхода, в конце которой следует