Предыдущий раздел | ПРЕРЫВАНИЯ И РЕЗИДЕНТНЫЕ ПРОГРАММЫ | Следующий раздел |
Сделаем резидентным базовый вариант редактора, полученный на лабораторной работе 13. В основе работы резидентного редактора лежит перехват прерываний № 9 – прерываний от клавиатуры. Вносимые при этом изменения затронут лишь главную подпрограмму редактора – Dispatcher. Данная подпрограмма теперь будет иметь две части – резидентную и инициализирующую. Функции инициализирующей части не отличаются от функций соответствующей части для приведенного выше примера перехвата прерываний с номером 0. Что касается резидентной части, то ее алгоритм существенно сложнее, чем алгоритм рассмотренной в предыдущем разделе программы для обработки прерывания 0. Этот алгоритм приведен на рис.67.
На первом этапе алгоритма производится сравнение с нулем значения переменной RR – режим редактирования. Это глобальная переменная (один байт), имеющая два значения: 0 – редактирование в данный момент не производится, 1 – производится. Первоначальное значение RR = 0. Это значение записывает или транслятор (при обработке оператора db) или инициализирующая часть программы редактора.
Наличие переменной RR обусловлено тем, что наш резидентный редактор будет инициироваться при нажатии любой клавиши на клавиатуре. Поэтому если RR = 1, то нажатие клавиши относится к текущей работе редактора, и никаких особых действий со стороны резидентной части выполнять не следует.
Рис. 67. Алгоритм резидентной части редактора
Единственное – следует передать управление системному обработчику прерываний от клавиатуры. Для этого следует выполнить безусловный переход на то двойное слово ОП, в котором инициализирующая часть сохранила ранее стартовый адрес системного обработчика прерываний клавиатуры.
Несмотря на то, что в следующем разделе мы будем рассматривать работу системного обработчика прерываний клавиатуры достаточно подробно, краткое представление об этой работе мы должны иметь уже сейчас: системный обработчик прерываний клавиатуры выполняет перенос кода нажатой клавиши (или нескольких одновременно нажатых клавиш) из контроллера клавиатуры в системный буфер клавиатуры. Системный буфер (как и обработчик прерываний) находится в драйвере клавиатуры и представляет собой очередь, в которую помещаются коды нажатых клавиш в порядке их нажатия.
Так как наш резидентный редактор, являясь одновременно и обработчиком прерываний клавиатуры, никаких операций ни с системным буфером клавиатуры, ни тем более с контроллером клавиатуры, выполнять «не желает», ему приходится достаточно тесно взаимодействовать с системным обработчиком, который выполняет эти операции.
Если RR=0, то никаких операций по редактированию информации наш редактор в данный момент времени не выполняет. Поэтому вновь пришедший символ не обязательно относится к редактору и, возможно, он должен быть обработан совсем другой программой. Естественно, что прежде, чем проанализировать вновь пришедший символ, наша программа должна получить его код, используя подпрограммы системного обработчика клавиатуры.
Процесс получения кода символа начинается с извлечения из стека старого адреса возврата. Вспомним, что в момент прерывания ЦП помещает в стек прерванной программы (этот стек наследуется обработчиком прерываний) три слова – (FLAGS), (CS), (IP). С помощью операторов pop эти слова извлекаются из стека, а затем сохраняются в трех словах ОП. Далее на следующем этапе в стек помещается новый адрес возврата из трех слов: 1) текущее содержимое FLAGS, 2) текущее содержимое CS; 3) адрес-смещение того байта программы, который расположен в памяти сразу же за командой перехода на системный обработчик прерываний клавиатуры.
Например, допустим, что переход на системный обработчик выполняет оператор:
jmp far [Old]
.M:
где Old – двойное слово ОП, в которое ранее инициализирующая часть поместила стартовый адрес системного обработчика. Тогда для записи третьего слова в стек может использоваться оператор:
push .M
Интересно отметить, что для перехода на системный обработчик можно использовать не только оператор jmp, но и оператор call:
call far [Old],
который выполняет дальний косвенный вызов процедуры. Выполнение данного оператора похоже на выполнение оператора jmp с тем лишь отличием, что он сам заносит в программный стек адрес возврата – сначала (CS), а затем (IP). Поэтому из трех операторов записи в стек остается лишь оператор pushf.
В результате записи в стек своего адреса возврата наша программа делает системный обработчик прерываний «ручным». Это означает, что после своего выполнения системный обработчик возвратит управление в резидентную часть на метку .M. Напомним, что во время этого выполнения системный обработчик выполнил перенос кода нажатой клавиши из контроллера клавиатуры в системный буфер.
Для того чтобы выполнить анализ введенного кода, наша программа обращается за помощью к BIOS, используя системный вызов “int 16h”, функция 01h. Особенностью данного системного вызова является то, что, выполняя чтение из системного буфера кода символа, он этот код из буфера не извлекает.
Кроме этого, данный системный вызов отличается от ранее рассмотренного “int 16h”, функция 00h, тем, что он не дожидается появления в системном буфере кода символа, а при его отсутствии возвращает управление в вызывающую программу, установив флаг ZF = 1. Если символ в системном буфере есть, то в результате выполнения данного системного вызова флаг ZF = 0, а в регистрах: AH – скан-код символа, AL – ASCII-код символа.
На следующем этапе алгоритма проверяется, не является ли код символа, скопированный на предыдущем шаге из системного буфера, кодом функциональной клавиши <Fi>. Для этого сначала проверяется на равенство нулю ASCII-код символа, и если это выполняется, то проверяется принадлежность скан-кода символа к диапазону от 3Bh до 44h. В случае успеха в переменную RR записывется 1 (редактор начинает выполнять свою команду), а затем производится выборка символа из системного буфера (для этого используется вызов “int 16h”, функция 00h).
Дальнейшее выполнение процедуры Dispatcher (и всего редактора) по выполнению команды не отличается от нерезидентного варианта. Единственное отличие – после завершения выполнения команды не делается переход на начало процедуры Dispatcher (для получения следующей команды), а делается запись RR=0, после чего обработчик прерываний, то есть весь резидентный редактор, завершается восстановлением содержимого регистров и завершающей командой iret.
Если символ, скопированный из системного буфера, не является командой редактора, то делается переход (с помощью команды jmp) на старый адрес возврата из прерывания. Напомним, что этот адрес был ранее извлечен нами из стека и предусмотрительно запомнен. Перед тем, как сделать переход, необходимо восстановить требуемое содержимое регистра FLAGS. Для этого следует поместить в стек сохраненное ранее значение данного слова (оператором push), а затем извлечь его оттуда и поместить в регистр флагов (оператором popf).
В заключении заметим, что в отличие от всех других ранее рассмотренных алгоритмов, алгоритм резидентного редактора не является последовательным. При этом напомним, что принципиальной особенностью последовательного алгоритма является невозможность одновременного выполнения более чем одного этапа алгоритма. Для резидентного редактора это свойство не выполняется, так как одновременно могут выполняться этапы «выполнение команды» и «переход на системный обработчик» (при RR=1). Причиной такой непоследовательности является то, что одна и та же программа одновременно предназначена и для выполнения высокоуровневых функций по редактированию информации, и для выполнения низкоуровневых функций в качестве обработчика прерываний клавиатуры.
Предыдущий раздел | В начало | Следующий раздел |