[Назад] [Далее]

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

Когда в реальном режиме выполняется команда INT, управление передается по адресу, который считывается из специального массива, таблицы векторов прерываний, начинающегося в памяти по адресу 0000h:0000h. Каждый элемент этого массива представляет собой дальний адрес обработчика прерывания в формате сегмент:смещение или 4 нулевых байта, если обработчик не установлен. Команда INT помещает в стек регистр флагов и дальний адрес возврата, поэтому, чтобы завершить обработчик, надо выполнить команды popf и retf или одну команду iret, которая в реальном режиме полностью им аналогична.

; Пример обработчика программного прерывания
int_handler     proc     far
                mov      ax,0
                iret
int_handler     endp

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

                push     0         ; сегментный адрес таблицы
                                   ; векторов прерываний
                pop      es        ; в ES
                pushf              ; поместить регистр флагов в стек
                cli                ; запретить прерывания
; (чтобы не произошло аппаратного прерывания между следующими
; командами, обработчик которого теоретически может вызвать INT 87h
; в тот момент, когда смещение уже будет записано, а сегментный
; адрес еще нет, что приведет к передаче управления
; в неопределенную область памяти)
; поместить дальний адрес обработчика int_handler в таблицу
; векторов прерываний, в элемент номер 87h (одно из неиспользуемых прерываний)
                mov      word ptr es:[87h*4], offset int_handler
                mov      word ptr es:[87h*4+2], seg int_handler
                popf               ; восстановить исходное значение флага IF

Теперь команда INT 87h будет вызывать наш обработчик, то есть приводить к записи 0 в регистр АХ.

Перед завершением работы программа должна восстанавливать все старые обработчики прерываний, даже если это были неиспользуемые прерывания типа 87h — автор какой-нибудь другой программы мог подумать точно так же. Для этого надо перед предыдущим фрагментом кода сохранить адрес старого обработчика, так что полный набор действий для программы, перехватывающей прерывание 87h, будет выглядеть следующим образом:

                push     0
                pop      es
; скопировать адрес предыдущего обработчика в переменную old_handler
                mov      eax,dword ptr es:[87h*4]
                mov      dword ptr old_handler,eax
; установить наш обработчик
                pushf
                cli
                mov      word ptr es:[87h*4], offset int_handler
                mov      word ptr es:[87h*4+2], seg int_handler
                popf
; тело программы
[...]
; восстановить предыдущий обработчик
                push     0
                pop      es
                pushf
                cli
                mov      eax,word ptr old_handler
                mov      word ptr es:[87h*4],eax
                popf

Хотя прямое изменение таблицы векторов прерываний и кажется достаточно удобным, все-таки это не лучший подход к установке обработчика прерывания, и пользоваться им следует только в случаях крайней необходимости, например внутри обработчиков прерываний. Для обычных программ DOS предоставляет две системные функции: 25h и 35h — установить и считать адрес обработчика прерывания, которые и рекомендуются к использованию в обычных условиях:

; скопировать адрес предыдущего обработчика в переменную old_handler
                mov      ax,3587h       ; АН = 35h, AL = номер прерывания
                int      21h            ; функция DOS: считать
                                        ; адрес обработчика прерывания
                mov      word ptr old_handler,bx    ; возвратить
                                                    ; смещение в ВХ
                mov      word ptr old_handler+2,es  ; и сегментный
                                                    ; адрес в ES,
; установить наш обработчик
                mov      ax,2587h       ; АН = 25h, AL = номер прерывания
                mov      dx,seg int_handler         ; сегментный адрес
                mov      ds,dx                      ; в DS
                mov      dx,offset int_handler      ; смещение в DX
                int      21h                        ; функция DOS: установить
                                                    ; обработчик
; (не забывайте, что ES изменился после вызова функции 35h!)
[...]
; восстановить предыдущий обработчик
                lds      dx,old_handler ; сегментный адрес в DS и смещение в DX
                mov      ax,2587h       ; АН = 25h, AL = номер прерывания
                int      21h            ; установить обработчик

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

; Процедура minmax
; находит минимальное и максимальное значения в массиве слов
; Ввод: DS:BX = адрес начала массива
;       СХ = число элементов в массиве
; Вывод:
;       АХ = максимальный элемент
        ВХ = минимальный элемент
minmax          proc     near
; установить наш обработчик прерывания 5
                push     0
                pop      es
                mov      еах,dword ptr es:[5*4]
                mov      dword ptr old_int5,eax
                mov      word ptr es:[5*4],offset int5_handler
                mov      word ptr es:[5*4]+2,cs
; инициализировать минимум и максимум первым элементом массива
                mov      ax,word ptr [bx]
                mov      word ptr lower_bound,ax
                mov      word ptr upper_bound,ax
; обработать массив
                mov      di,2            ; начать со второго элемента
bcheck:
                mov      ax,word ptr [bx][di] ; считать элемент в АХ
                bound    ax,bounds       ; команда BOUND вызывает
                                         ; исключение - ошибку 5,
; если АХ не находится в пределах lower_bound/upper_bound
                add      di,2            ; следующий элемент
                loop     bcheck          ; цикл на все элементы
; восстановить предыдущий обработчик
                mov      eax,dword ptr old_int5
                mov      dword ptr es:[5*4],eax
; вернуть результаты
                mov      ax,word ptr upper_bound
                mov      bx,word ptr lower_bound
                ret

bounds:
lower_bound     dw       ?
upper_bound     dw       ?
old_int5        dd       ?

; обработчик INT 5 для процедуры minmax
; сравнить АХ со значениями upper_bound и lower_bound и копировать
; AX в один из них, обработчик не обрабатывает конфликт между
; исключением BOUND и программным прерыванием распечатки экрана INT 5.
; Нажатие клавиши PrtScr в момент работы процедуры minmax приведет
; к ошибке. Чтобы это исправить, можно, например, проверять байт,
; на который указывает адрес возврата, если это CDh
; (код команды INT), то обработчик был вызван как INT 5
int5_handler    proc     far
                cmp      ax,word ptr lower_bound  ; сравнить АХ с нижней границей,
                jl       its_lower                ; если не меньше -
                                                  ; это было нарушение
                mov      word ptr upper_bound,ax  ; верхней границы
                iret
its_lower:
                mov      word ptr lower_bound,ax  ; если это было нарушение
                iret                              ; нижней границы
int5_handler    endp
minmax          endp

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

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


п»ї
"target=_blank><\/a>") //-->