Иллюстрированный самоучитель по Assembler

Использование средств 32-разрядных процессоров в программировании

Обработчик прерываний new_08 прежде всего выполняет вызов исходного обработчика, адрес которого мы сохранили в ячейке old_08. Методика сцепления обработчиков прерываний рассматривалась в гл.3 (см. пример 3-4). В данном случае сцепление обработчиков необходимо, так как подключение к вектору 8 нашего обработчика не должно нарушить ход системных часов.

После возврата из системного обработчика выполняется сохранение используемых регистров, настройка регистра ES на область данных BIOS, чтение текущего времени и сравнение его с записанным в ячейке time_end. Пока текущее время меньше заданного, обработчик просто завершается командой iret, послав предварительно в контроллер прерываний команду конца прерывания EOI и восстановив сохраненные ранее регистры. Если же заданный временной интервал истек, и текущее время оказывается равным (или большим) значению в ячейке time_end, обработчик перед своим завершением устанавливает флаг flag, инициируя в основной программе запланированные для этого события действия. Если такими действиями должно быть, например, включение или выключение аппаратуры, подключенной к компьютеру, это можно сделать в самом обработчике прерываний. В этом случае флаг flag не нужен, и действия основной программы и обработчика прерывании протекают параллельно и независимо.

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

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

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

Такты = t*18 + t/5 + t/154

Отлаживая на машине пример 4.2, надо следить за тем, чтобы заданное время было больше текущего по машинным часам, иначе программа будет вечно ожидать установки флага. Попытка завершить ее нажатием комбинации CTRL + C приведет к зависанию системы, так как в этом случае не будут выполнены строки восстановления исходного содержимого перехваченного программой вектора. По-настоящему в программах, содержащих обработчики каких-либо прерываний, используемых системой, необходимо предусматривать собственные средства обработки нажатия CTRL + C, чтобы аварийное завершение программы выполнялось так же корректно, как и штатное, с предварительным с восстановлением векторов.

Пример 4.2. Ожидание заданного момента времени по прерываниям от таймера.

0.586
assume CS:code,DS:code
code segment use16
org 100h
main proc
;Сохраним исходный вектор
…
;Установим наш обработчик
…
;Преобразуем требуемое календарное время в количество
;интервалов по 55 мс
mov EAX,hour; Возьмем часы
mov EBX,3600; Коэффициент преобразования в секунды
mul EBX; Преобразуем часы в секунды в EDX:EAX
mov temp,EAX; Сохраним часы в temp
mov EAX,min; Возьмем минуты
mov EBX,60; Коэффициент преобразования в секунды
mul EBX; Преобразуем минуты в секунды в EDX:EAX
add temp,EAX; Прибавим минуты в temp
mov EAX,sec; Возьмем секунды
add temp,EAX; Прибавим секунды в temp
mov EAX,temp; Число секунд
mov EBX,18; Будем умножать на 18
mul EBX; Умножим на 18
mov time,EAX; Сохраним в time
xor EDX,EDX; Подготовимся к делению
mov EAX,temp; Будем делить число секунд
mov EBX,5; Будем делить на 5
div EBX; Поделим
add time,EAX; Прибавим к time
xor EDX,EDX; Подготовимся к делению
mov EAX,temp; Будем делить число секунд
mov EBX,154; Будем делить на 154
div EBX; Поделим
add time,EAX; Прибавим к time
;Имитация рабочего цикла программы с опросом флага
…
;Завершим программу, восстановив сначала исходный вектор
…
main endp
new_08 proc
…
new_08 endp
old_08 dd 0
hour dd 13; Часы
min dd 45; Минуты
sec dd 0; Секунды
time dd 0; Вычисленное время в тактах таймера
temp dd 0; Ячейка для промежуточного результат
flag db 0; Флаг наступления заданного времени
msg db "Время наступило!$'
code ends
end main
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.