Предыдущий раздел ПРЕРЫВАНИЯ И РЕЗИДЕНТНЫЕ ПРОГРАММЫ Следующая глава

19.2.3. Перехват прерываний

Перейдем к рассмотрению более сложного случая, когда обработчик прерываний должен использовать уже занятый вектор прерываний. В этом случае требуется выполнить перехват прерываний – запись в вектор прерываний стартового адреса нового обработчика. Это нетрудно сделать, используя рассмотренный ранее системный вызов “int 21h”, функция 25h. Но дополнительная задача заключается в том, что обычно требуется сохранить прежнее содержимое вектора прерываний для того, чтобы прежний обработчик прерываний можно было бы впоследствии инициировать.

Для чтения прежнего содержимого вектора прерываний используется системный вызов “int 21h”, функция 35h. Перед выполнением вызова программа должна поместить в регистр AL номер прерывания. В результате выполнения вызова регистры ES и BX содержат соответственно адрес-сегмент и адрес-смещение, находящиеся в векторе прерываний. Далее эти адреса могут быть переписаны для сохранения в любое место ОП.

Инициирование прежнего обработчика прерываний может потребоваться в двух случаях. Во-первых, в том случае, если новый обработчик прерываний не заменяет, а лишь расширяет функции, выполнявшиеся прежним обработчиком. При этом после того, как новый обработчик выполнится, он должен передать управление старому обработчику. Во-вторых, часто обработчик прерываний должен находиться в ОП не до конца работы системы. В этом случае перед тем, как освободить память, необходимо восстановить старый обработчик прерываний. Это нетрудно сделать с помощью системного вызова “int 21h”, функция 25h. Рассмотрим реализацию первого случая.

Пример. Следующая резидентная программа “дополняет” обработку прерывания с номером 0. Системный обработчик данного прерывания-исключения прекращает выполнение текущей программы, если ее команда выполнила деление на 0. Наш обработчик спрашивает пользователя о желании завершить программу. В случае нажатия клавиши y (или Y) выполнение программы прекращается. При нажатии любого другого символа выполнение программы продолжается.

 

;    Резидентная программа выполняет обработку исключения 0 (деление на 0) 

;    ------------------------------------------------------------------------------------------------

;               

cr               equ             0dh                      ;  Код ASCII возврата каретки

lf                equ             0ah                      ;  Код ASCII перевода строки

absolute     2ch                      ; Следующий байт имеет адрес-смещение 2ch

Blocokr       resw  1                                   ; Адрес-сегмент блока окружения

segment      code                     ; Виртуальный сегмент кода

             org    100h

Start:          jmp    Init                                ;  Переход на инициализацию

;       Обработчик прерываний

Msg            db     ‘Завершить программу ?’, cr, lf, ‘$’     ; Выводимое сообщение

Old             dd     0                                    ; Прежнее содержимое вектора пр-й

Begin:         sti                                            ;Разрешить маскируемые прерывания

push           ax                         ; Сохранение содержимого

push           dx                         ;      регистров

push           ds                         ;           в стеке

pushf                                      ; Сохранение регистра флагов

mov            ax, cs                    ; DS будет адресовать данные

mov            ds, ax                    ;      резидентной программы

mov            dx, Msg                ;  DX ß адрес строки-сообщения

mov            ah, 9                    ;  Вывод строки

int              21h                      ;    на экран

mov            ah,1                     ; Ввод символа с

int              21h                      ;    клавиатуры

or               al, 20h                 ; Перевод символа на нижний регистр

cmp            al, ‘y’                   ; Введен символ “y” ?

jnz              .Exit                     ; Если нет, на выход из прерывания

popf                                        ; Восстановление регистра флагов

pop            ds                         ; Восстановление содержимого

pop            dx                         ;      регистров

pop            ax                         ;           из стека

 

jmp            far    [Old]           ; Вызов прежнего обработчика

.Exit:      mov            ah, 2                    ; Перевод

mov            dl, lf                     ;    строки

int              21h                      ;        экрана

popf                                       ; Восстановление регистра флагов

pop            ds                         ; Восстановление содержимого

pop            dx                         ;      регистров

pop            ax                         ;           из стека

iret                                         ; Возврат из прерывания

;     Инициализационная часть программы

Init:            mov            ax, 3500h             ; Сохранение прежнего содержимого

int              21h                      ;    вектора 0 в регистрах ES и BX

mov            [Old], bx              ; А затем

mov            [Old+2], es          ;     в поле Old

mov            ax, 2500h             ;Запись стартового адреса обработчика

mov            dx, Begin              ;      в вектор прерываний

int              21h                      ;      с номером 0

mov            ah, 49h                ; Освобождение области памяти,

mov            es, [Blocokr]        ;      занимаемой

int              21h                      ;       блоком окружения

mov            dx, Init                 ;  Возврат в DOS, оставшись

int              27h                      ;    резидентным

 

Как и предыдущая программа, данная программа состоит из инициализационной и резидентной частей. Инициализационная часть начинает свою работу с сохранения прежнего содержимого вектора прерываний 0 в двойном слове ОП с меткой Old. Затем она помещает в вектор 0 стартовый адрес нашего обработчика прерываний и сокращает объем занимаемой им памяти. В завершении инициализационная часть выполняет возврат в DOS, оставив в памяти обработчик прерываний.

Обработчик прерываний (резидентная часть) запускается в результате прерывания-исключения с номером 0. В начале своего выполнения он разрешает маскируемые внешние прерывания, а также сохраняет в стеке используемые им регистры. Обратите внимание, что среди сохраняемых регистров находится и регистр флагов. Это делается для того, чтобы прежний обработчик прерываний получил такое содержимое этого регистра, которое установила ранее программа. (На тот случай, если некоторые флаги используются для передачи информации на вход обработчика.)

Далее резидентная часть выводит на экран вопрос о желании прекратить выполнение программы, выполнившей деление на 0. Ответ пользователя (один символ) переводится в строчный символ. Такой перевод выполняется установкой единственного бита 5, которым отличаются коды строчной и прописной одноименных букв. Если пользователь не желает прекращать выполнение программы, то выполняется возврат из прерывания.

Иначе – управление передается системному обработчику. Это делается с помощью команды jmp, выполняющей безусловный переход на стартовый адрес этого обработчика. Рассмотрим внимательно соответствующий программный оператор:

jmp   far    [Old]           ; Вызов прежнего обработчика

Во-первых, данный оператор выполняет межсегментный переход. Об этом говорит наличие вспомогательного псевдооператора far (дальний). Кроме дальних переходов в программе возможны очень близкие переходы (не более чем на 128 байт) и близкие переходы – в пределах сегмента кода. Для задания таких переходов используются, соответственно, псевдооператоры short и near. По умолчанию оператор jmp выполняет очень близкий переход и поэтому оператор short можно не записывать. Запись оператора near аналогична записи оператора far – между jmp и операндом.

Во-вторых, рассматриваемый оператор выполняет косвенный переход. Об этом говорит наличие имени Old, заключенного в квадратные скобки. Данное имя является не именем процедуры, а именем поля данных длиной два слова (четыре байта). В первом из этих слов находится адрес-смещение первого байта запускаемой процедуры, а во втором слове – адрес-сегмент этого байта. Таким образом, поле Old содержит стартовый логический адрес вызываемой процедуры.

 Закономерен вопрос: как системный обработчик возвратит управление (если пожелает) в прерванную программу? Ответ: обычным способом, то есть с помощью команды iret. Это объясняется тем, что системный обработчик «наследует» от нашего обработчика стек прерванной программы, в который был помещен ранее адрес возврата (в момент прерывания).

Для проверки работоспособности рассмотренного обработчика прерываний необходимо: после того, как приведенная выше программа будет выполнена (сделав обработчик прерываний резидентным), запустить на выполнение простейшую прикладную программу, выполняющую деление на нуль в бесконечном цикле.

Если резидентная программа полностью заменяет систему в обработке каких-то типов прерываний, то по истечению какого-то времени у пользователя (или у прикладной программы) может возникнуть желание вернуться к системной обработке этих прерываний. Для этого следует выполнить следующие три действия:

1) закрыть файлы, открытые резидентной программой;

2) восстановить векторы прерываний так, чтобы они указывали на системные обработчики;

3) освободить память, занимаемую резидентной программой.

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

Для восстановления векторов прерываний можно использовать рассмотренные ранее системные вызовы “int 21h” (функции 35h и 25h). Первый из этих вызовов позволяет запомнить, а второй – восстановить прежнее содержимое вектора прерывания.

Для освобождения памяти, занимаемой резидентной программой, можно воспользоваться системным вызовом “int 21h”  (функция 49h), который требует задания в регистре ES адреса-сегмента области резидентной программы. Так как адрес-сегмент резидентной программы находится во время ее выполнения в регистре CS, следующий фрагмент программы показывает, как можно осуществить такой вызов:

 

push  cs                                  ; Копирование CS

pop   es                                  ;   в ES

mov   ah, 49h                         ; Освобождение

int     21h                               ;    памяти

.  .  .  .  .  .                             ; Восстановление регистров из стека

iret                                        ; Возврат из прерывания

 


Предыдущий раздел В начало Следующая глава