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

Двоично-десятичные числа

Пример 3.9. Чтение и обработка показаний часов реального времени.

0.586; Будут использоваться дополнительные команды
assume CS:code,ds:data
code segment use 16
main proc
mov AX,data; Настроим DS наш
mov DS,Ax; сегмент данных
;Сохраним исходный вектор 4Ah
mov AX,354Ah
int 21h
mov word ptr old_4a,BX
mov word ptr old_4a+2,ES
;Установим наш обработчик прерываний 4Ah
mov AX,254Ah
push DS; Сохраним DS
push CS; Настроим DS на сегмент
pop DS; команд
mov DX,offset new_4a: DS:DX › new_4a
int 21h
pop DS; Восстановим DS
;Установим будильник
movAH,02h; Чтение текущего времени
int 1Ah
call add_time; Прибавим 1 секунду
mov AH,06h; Установим будильник на это время
int 1Ah
;Остановим программу, чтобы наблюдать прерывания
mov AH,01h; Функция ввода с клавиатуры
int 21h
;Завершим программу, прибрав за собой
mov AH,07h; Сброс будильника
int 1Ah
Ids DX,old_4a/DS:DX=исходный вектор
mov AX,254Ah; Установим исходный вектор
int 21h
mov AX,4C00h; Завершим программу
int 21h
main endp
;Наш обработчик прерывания от будильника new_4a proc
push a; Сохраним все регистры
push DS; Сохраним еще и
push ES; сегментные регистры
mov AX,seg hour; Настроим DS на наш
mov DX,AX; сегмент данных
mov AH,02h; Прочитаем текущее время
int 1Ah; из часов реального времени
push CX; Сохраним полученное
push DX; текущее время

В примере 3.9 используются несколько команд, отсутствующих в МП 86: команды сохранения в стеке и восстановления всех регистров общего назначения pusha и рора, а также команда сдвига shl с числовым операндом. Для того, чтобы эти команды распознавались ассемблером, в программу включена директива 0.586 (можно было бы обойтись и директивой 0.386). В этом случае необходимо оба сегмента объявить с описателем use16.

Программа состоит из главной процедуры main, процедуры new_4a обработчика прерываний от будильника, а также трех вспомогательных процедур-подпрограмм add_time, add_unit и conv. Главная процедура сохраняет исходный вектор прерывания 4Ah, устанавливает новый обработчик этого прерывания, читает текущее время и устанавливает будильник на время, отстоящее от текущего на 1 секунду, а затем останавливается в ожидании нажатия любой клавиши. Пока программа стоит, обрабатываются прерывания от будильника и в правый верхний угол экрана каждую секунду выводится текущее время. После нажатия любой клавиши программа завершается, предварительно сбросив будильник и восстановив исходное содержимое вектора 4Ah.

Легко видеть, что в предложенном варианте программа имеет мало практического смысла, так как она не выполняет, кроме вывода времени, никакой полезной работы. В то же время, пока эта программа не завершилась, запустить другую программу нельзя, так как DOS является однозадачной системой. Если, однако, написать нашу программу в формате .СОМ и сделать ее резидентной, мы получим возможность запускать любые программы и одновременно наблюдать на экране текущее время. Такого средства в DOS нет, и в какой-то ситуации оно может оказаться полезным. Методика разработки резидентных программ описана выше; читатель может выполнить необходимые преобразования самостоятельно.

Рассмотрим теперь программу обработчика прерываний будильника. Прежде всего в нем командой pusha (push all, сохранить все) сохраняются все регистры общего назначения и, кроме того, два сегментных регистра DS и ES, которые будут использоваться в обработчике. Далее регистр DS настраивается на сегментный адрес того сегмента, в который входит ячейка hour, т.е. фактически на наш сегмент команд. На первый взгляд это действие может показаться бессмысленным. Ведь в начале процедуры main в регистр DS уже был помещен адрес нашего сегмента данных data. Зачем же эту операцию повторять?

Дело в том, что процедура new_4a, будучи формально обработчиком программного прерывания 4Ah, фактически представляет собой обработчик аппаратного прерывания от часов реального времени, которое, как и любое аппаратное прерывание, может придти в любой момент времени. В принципе прерываемая программа в этот момент может выполнять любые действия, и содержимое регистра DS может быть любым. Если же говорить о нашей программе, то она находится в цикле ожидания нажатия клавиши. Этот цикл организует функция 01h DOS, которая, между прочим, время от времени обращается к своему драйверу клавиатуры, а тот – к программам BIOS ввода символа с клавиатуры. Вполне вероятно (а на самом деле так оно и есть), что при выполнении упомянутых операций используется регистр DS, который в этом случае указывает уже не на наш сегмент данных, а на различные системные области.

Другими словами, при входе в обработчик прерывания содержимое регистра DS неизвестно, и его следует инициализировать заново, обязательно сохранив исходное значение. Если перед выходом из обработчика это исходное значение не восстановить, будет неминуемо разрушена DOS.

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.