16.5.1. Чтение и перестановка адресов подпрограмм прерываний

We use cookies. Read the Privacy and Cookie Policy

Действия с перестановкой адресов — работа несложная, но «опасная». Прежде чем пояснить этот тезис, дадим общее понятие о предмете. При загрузке операционной системы в ОЗУ модуль БСВВ располагает в самом начале памяти таблицу векторов прерываний. Под этим красивым названием хранится простой список адресов подпрограмм ОС или аппаратной части, выполняющих действия по тому или иному прерыванию. Выполнение прерывания означает

- 371 -

среди прочего чтение адреса из этой таблицы и передачу управления подпрограмме по этому адресу.

Очевидно, что если написать свою программу обработки какого-нибудь прерывания (что непросто без специальных знаний) и «подставить» ее адрес вместо «настоящего», то ряд функций ОС будет выполняться этой программой. Так работают все резидентные программы. Находясь постоянно в памяти, они перехватывают нужные им прерывания и анализируют их. Например, если резидентная программа активизируется нажатием каких-либо клавиш (таковы SideKick, драйверы клавиатуры и т.п.), то она настраивает на себя прерывания 9 и 16H, которые «включаются» при каждом нажатии клавиши на клавиатуре. Далее анализируется, что именно было нажато, и если это была запускающая комбинация, то начинает работать сама резидентная программа. Если же нет, то вызывается настоящая подпрограмма этого прерывания, как будто ничего не было. Чтение адреса подпрограммы отработки прерывания осуществляется процедурой

GetIntVec( N : Byte; VAR Adress : Pointer ).

Задав через параметр N номер интересующего нас прерывания, после вызова получим в переменной Adress адрес его подпрограммы, вызов процедуры вполне безопасен. Установка адреса новой подпрограммы делается процедурой

SetIntVec( N : Byte; Adress : Pointer ).

Здесь параметр N по-прежнему содержит номер прерывания, а параметр Adress должен содержать адрес специально оформленной процедуры, которую надо подставить в таблицу векторов.

Эксперименты с этой процедурой в высшей степени опасны: ошибка в задании адреса или неверное оформление процедуры способны, используя термины жаргона программистов, послать вектор прерывания «в космос». Реально же это означает полную непредсказуемость дальнейшего поведения ПЭВМ. Хорошо, если она просто «зависнет», а то — можно лишиться информации на диске и испортить ее.

Жесткие требования предъявляются к оформлению собственно процедур обработки прерываний. Описание заголовков таких процедур, должно выглядеть следующим образом:

PROCEDURE IntProc(Flags, CS, IP, АХ, ВХ, СХ, DX,

SI, DI, DS, ES, ВР: Word); INTERRUPT;

- 372 -

Список параметров представляет собой список регистров микропроцессора. Он может быть короче: разрешается опускать последовательности параметров начиная с первого и при этом оставлять неизменным и неразрывным оставшийся до конца список.

Например, заголовки

PROCEDURE IntProc2( SI, DI, OS, ES, BP : Word ); INTERRUPT;

PROCEDURE IntProc3( ES, BP : Word ); INTERRUPT;

верны, так как отброшены и оставлены две неразрывные части полного списка параметров, а заголовок

PROCEDURE IntProc4( Flags, ES, BP : Word ); INTERRUPT;

неверен, так как пропуск делает «дыру» в списке.

Названия параметров не должны меняться. Внутри процедуры прерывания с ними можно делать что угодно (опрашивать, изменять — одновременно с ними будет изменяться содержимое соответствующих им регистров). Процедура с указанием interrupt не может быть вызвана, как обычная процедура. Она начнет работать только после активации настроенного на нее прерывания, и она ничего не возвращает. Имена ее параметров можно использовать вне interrupt-процедуры.

Процедуры для прерываний низкого уровня (аппаратных — от клавиатуры, портов, таймера и т.п.), номера которых лежат в диапазоне от 0 до 31 (от 0 до 16H), не должны в общем случае содержать в себе вызовов процедур ввода-вывода Турбо Паскаля, команд динамического распределения памяти и вызовов функций ОС.

После того как процедура прерывания написана, ее необходимо подключить. Программа должна в этом случае иметь общую структуру, показанную на рис. 16.12.

| USES DOS;

| VAR

| GlobalVARI, GlobalVAR2, ..., GlobalVARK : Word;

| {$F+} {<-Interrupt-процедуры должны быть с таким ключом }

| PROCEDURE MyInterrupt(Flags, CS, IP, AX, BX, CX, DX,

| SI, DI, DS, ES, BP : Word); INTERRUPT;

| VAR

| ... { описание локальных переменных }

Рис. 16.12

- 373 -

| BEGIN

| GlobalVAR1:=AX; { Можно снять входные параметры}

| GIobaIVAR2:=Port[...]; { прерывания и запомнить их. }

| ...

| END; { Myinterrupt }

| {$F-}

| VAR

| N : Byte;

| OldlntVectN : Pointer; { буферная переменная }

| BEGIN

| N := { номер заменяемого прерывания };

| { Сохранение исходного вектора в буферной переменной:}

| GetIntVec( N, OldlntVectN );

| { Запись в вектор N адреса подставляемой процедуры: }

| SetIntVec( N, SMylnterrupt );

| ...

| { Программа работает с подмененным прерыванием: }

| Write( GlobalVARI, GlobalVAR2 GlobalVARK );

| ...

| SetIntVec( N, OldlntVectN );

| { Всегда надо восстанавливать исходные векторы, если }

| { только программа не остается резидентной в памяти. }

| END.

Рис. 16.12 (окончание)

Если потребуется модифицировать операционную систему, меняя вектора прерывания, то необходимо и желательно детально изучить техническое устройство механизма прерываний.