Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Финогенов-основы_языка_ассемблера.doc
Скачиваний:
26
Добавлен:
17.09.2019
Размер:
3.35 Mб
Скачать

Глава 3

Команды и алгоритмы

113

его полного адреса — в паре регистров DS:DX. Здесь нас подстерегает неприятность. Занести в регистр DX смещение нашего обработчика new_08 не составляет труда, это делается командой

mov DX,ofTset new_08 ;Смещение нашего обработчика

Однако регистр DS у нас занят — в нем хранится сегментный адрес сегмента данных. Придется на какое-то время сохранить этот адрес, для чего удобнее всего воспользоваться стеком. Откуда взять сегментный ад­рес обработчика' Между прочим, в языке ассемблера существует специ­альная конструкция, позволяющая определить сегментный адрес любого поля. В нашем случае она выглядела бы таким образом:

mov AX,seg new_08 mov DS.AX

; Получим сегмент с процедурой new_08 ;Псрешлем его в DS

В примере 3-3 использован другой прием — содержимое CS отправля­ется в стек и тут же извлекается оттуда в регистр DS:

push CS pop DS

После возврата из DOS надо не забыть восстановить исходное содер­жимое DS, сохраненное в стеке. Инициализация обработчика прерыва­ний закончена. Начиная с этого момента, каждый сигнал таймера будет приводить к прерыванию продолжающейся основной программы и пере­даче управления на процедуру new_08.

Перед завершением программы необходимо поместить в вектор 8 адрес исходного, системного обработчика, который был сохранен в двухсловном поле old_08. Перед вызовом функции 25U установки вектора в регистры DS:DX надо занести содержимое этого двухсловного поля. Эту операцию можно выполнить одной командой Ids, если указать в качестве ее первого операнда регистр DX, а в качестве второго — адрес двухсловной ячейки, в нашем случае old_08. Именно имея в виду использование этой команды, мы и объявили поле для хранения вектора двухсловным, отчего возникли некоторые трудности при его заполнении командами mov. Если бы мы ис­пользовали второй предложенный выше вариант и отвели для хранения вектора две однословные ячейки (old_08_pfls и old_08_scg), то команду Ids пришлось бы снабдить описателем изменения размера ячейки:

Ids DX.dword ptr old_08_offs

Между прочим, здесь так же разрушается содержимое DS, но поскольку сразу же вслед за функцией 25h вызывается функция 4Ch завершения программы, это не имеет значения.

Последнее, что нам осталось рассмотреть — это стандартные действия по завершению самого обработчика прерываний. Выше уже говорилось, что последней командой обработчика должна быть команда irct, возвра­щающая управление в прерванную программу. Однако перед ней необхо­димо выполнить еще одно обязательное действие — послать в контроллер

прерываний команду конца прерываний. Дело в том, что контроллер пре­рываний, передав в процессор сигнал прерывания INT, блокирует внут­ри себя линии прерываний, начиная с той, которая вызвала данное пре­рывание, и до последней в порядке возрастания номеров IRQ. Таким об­разом, прерывание, пришедшее, например, по линии IRQ 6 (гибкий диск) заблокирует дальнейшую обработку прерываний по линиям 6 и 7, а пре­рывание от таймера (IRQO) блокирует вообще все прерывания (IRQO...IRQ7, а также и IRQ8...IRQ15, поскольку все они являются раз­ветвлением уровня IRQ2, см. гл. 1, рис. 1.11). Любой обработчик аппарат­ного прерывания обязан перед своим завершением снять блокировку в контроллере прерываний, иначе вся система прерываний выйдет из строя. Снятие блокировки осуществляется посылкой команды с кодом 20h в один из двух портов, закрепленных за контроллером прерываний., Для ве­дущего контроллера эта команда посылается в порт 20h, для ведомого — в порт AOh. Таким образом, если бы мы обрабатывали прерывания от часов реального времени (линия прерываний IRQ8, вектор 70h, ведомый контроллер), то команда конца прерывания выглядела бы так:

mov AL,20h ;Команда конца прерывания

out AOh,AL ;Пошлем ее в порт ведомого ;контроллера

Указанную последовательность команд иногда называют приказом, или командой EOI (от end of interrupt, конец прерывания).

Разобравшись в этих общих вопросах, рассмотрим пример реальной программы, включающей обработчик прерываний от таймера. Для того, чтобы приведенную выше фрагментарную программу преобразовать в дей­ствующую, надо написать содержательную часть самого обработчика, а также придумать, что будет делать основная программа после инициали­зации прерываний. Все это сделать очень просто.

Пусть наш обработчик в ответ на каждое прерывание от таймера вы­водит на экран какой-нибудь символ. Для этого можно воспользоваться функцией OEU прерывания BIOS 10h. Это прерывание обслуживает боль­шое количество различных функций, обеспечивающих управление экра­ном. Сюда входят функции вывода символов и строк, настройки режимов видеосистемы, загрузки нестандартных таблиц символов и многие другие. Функция OEh предназначена для вывода на экран отдельных символов. Она требует указания в регистре AL кода выводимого символа. Процедура new_08 будет выглядеть в этом случае следующим образом:

;Обработчик прерываний для примера 3-3

new_08

proc

push

AX

mov

AH.OEh

mov

AL,'@'

hit

lOh

mov

AL,20h

out

20h,AL

;Сохраним исходное значение АХ ;Функция вывода символа ;Выводимый символ ;Переход в BIOS разблокировка прерываний ;в контроллере прерываний

114