Установка обработчика прерывания
Регистрация (или установка) подпрограммы обработки прерываний заключается том, что мы помещаем ее начальный адрес в соответствующее поле таблицы векторов прерываний. Это можно сделать двумя способами:
Мы можем просто изменить адрес прерывания таким образом, чтобы он Указывал на наш собственный обработчик и так все и оставить. Однако при этом, старый обработчик прерывания никогда больше вызываться не будет.
Кроме того, а что произойдет, если прерывание будет сгенерировано именно в тот момент, когда мы будем заниматься заменой адреса в таблице векторов? Р-р-раз и «зависли» — вот что случится! Именно поэтому пусть это действие за вас выполнит DOS при помощи функции _dos_setvect(). Для сохранения старого вектора прерывания следует использовать функцию _dos_getvect(). Обе функции гарантировано изменяют вектора без ущерба для операционной системы;
Очень часто нам требуется расширить функциональные возможности системы без полной замены уже работающих функций. Тогда мы используем цепочку прерываний. Под цепочкой прерывания понимается следующее: мы запоминаем старый адрес процедуры обслуживания прерывания и заменяем его на собственный, затем, по окончании нашей процедуры обработки прерывания, передаем управление старому обработчику. Таким образом, нам удается сохранить чужие обработчики прерываний или резидентные программы - если, конечно, нам это надо!
Взгляните на рисунок 12.3, на котором представлены два способа установки обработчиков прерываний.
![](image/index-image118.jpg)
Листинг 12.1 демонстрирует установку нашей процедуры обслуживания прерывания timer () (которая по-прежнему ничего не делает).
Листинг 12.1. Установка процедуры обслуживания прерывания.
void (_interrupt _far old_isr) (); // Указатель для сохранения
// вектора старого
// обработчика прерываний
// Сохранить вектор старого обработчика прерывания таймера
old_isr = _dos_getvect( 0х1C);
// Установить новую процедуру обработки прерывания
_dos_setvect(0x1C, Timer);
Ну как, трудно? Да вовсе нет! Для восстановления старого обработчика нам потребуется сделать только:
_dos_setvect(0x1C, old_isr);
Вот и все. Теперь, для примера, заставим наш обработчик прерывания делать что-нибудь полезное, например, обновлять значения глобальной переменной. В Листинге 12.1 показан текст программы SPY.С, которая представляет собой бесконечный цикл, печатающий значение переменной. Единственный способ изменить значение этой переменной - присвоить ей значение из другой программы, например, из обработчика прерывания.
Листинг 12.2. Шпионим за часами (SPY.C)._________________
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ///////////////////////////////////////////////
#include <dos.h>
#include <bios.h>
#nclude <stdio.h>
#include <math.h>
#include <conio.h>
#include <graph.h>
// ОПРЕДЕЛЕНИЯ//////////////////////////////////
#define TIME_KEEPER_INT 0x1C
// ГЛОБАЛЬНЫЕ
ПЕРЕМЕННЫЕ
///////////////////////////////
void (_interrupt _far *old_Isr)();
// хранит старый обработчик прерывания
long time=0;
// функции ////////////////////////////////////
void _interrupt _far Timer()
{
// Увеличивает глобальную переменную.
// Еще раз отметим, что мы можем это делать, так как при входе
// в процедуру обработки прерывания регистр DS
указывает на сегмент
// глобальных данных нашей программы,
time++;
} // конец Timer
// ОСНОВНАЯ ПРОГРАММА ////////////////////////////////
void main(void)
{
// установка процедуры обработки прерывания
Old_Isr = _dos_getvect(TIME_KEEPER_INT);
_dos_setvect(TIME_KEEPER_INT, Timer) ;
// ожидание нажатия клавиши пользователем
while(!kbhit())
{
// вывод переменной. Примечание: сама по себе функция main
// значение этой переменной не изменяет...
_settextposition(0,0);
printf("\nThe timer reads:%ld ",time);
} // конец while
// восстановление старого обработчика прерывания
_dos_setvect(TIME_KEEPER_INT, Old_Isr) ;
} // конец функции main
Запустив программу, приведенную в Листинге 12.2, вы увидите, что приращение счетчика происходит очень быстро. Фактически, значение этой переменной увеличивается каждую 1/18.2 секунды — так настроены по умолчанию внутренние системные часы. (Не переживайте, очень скоро мы научимся управлять и ими).Главное, что сама программа не увеличивает значенй переменной time. Этим занимается исключительно подпрограмма обслужива ния прерывания.
Итак, мы создали процедуру обслуживания прерывания, установили ее и убедились в том, что она работает - и все это мы сделали, написав лишь несколько строк кода. Восхитительно, не правда ли? Более сложными обработчиками прерываний мы займемся чуть позже. Сейчас же давайте сменим тему и поговорим об игровом цикле.