Двоично-десятичные числа
Пример 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.