Двоично-десятичные числа
В гл. 2 уже говорилось о двоично-десятичных числах – специальном формате хранения данных, используемом в ряде технических приложений. Часто эти числа называют BCD-числами (от binary-coded decimal, двоично-кодированные десятичные числа). Для обработки BCD-чисел (сложения, вычитания, умножения и деления) в МП 86 предусмотрены специальные команды. Рассмотрим этот вопрос на комплексном примере обработки показаний КМОП-часов реального времени.
Как известно, в современных компьютеров имеются два независимых таймера. Один из них ("часы реального времени") включен в состав микросхемы с очень низким потреблением тока, питается от батарейки или аккумулятора, находящегося на системной плате, и работает даже на выключенной из сети машине. В этом таймере хранится и автоматически наращивается текущее календарное время (год, месяц, день, час, минута и секунда).
После включения компьютера вступает в работу другой таймер, который обычно называют системным. Датчиком сигналов времени для него служит кварцевый генератор, работающий на частоте 1.19318 МГц, сигналы от которого, после пересчета в отношении 65536:1, поступают в контроллер прерываний и инициируют прерывания через вектор 8 с частотой 18.2065 Гц. Эти прерывания активизируют программу BIOS, периодически выполняющую инкремент содержимого четырехбайтовой ячейки памяти с текущим временем, находящейся по адресу 46Ch. После включения машины программы BIOS считывают из часов реального времени текущее время суток, преобразуют его в число тактов системного таймера (т.е. в число интервалов по 1/18.2065 с) и записывают в ячейку текущего времени. Далее содержимое этой ячейки наращивается уже системным таймером, работающим в режиме прерываний.
Для определения текущего времени прикладная программа может вызвать соответствующие функции прерывания 21h DOS (конкретно, с номером 2Ah для получения даты и 2Ch для получения времени суток), а может прочитать время непосредственно из часов реального времени с помощью прерывания lAh BIOS. При этом прерывание 1А1г позволяет, помимо чтения текущего времени (функция 02h) и текущей даты (функция 04h), выполнять и целый ряд других функций, среди которых мы отметим только возможность установить "будильник", т.е. записать в микросхему часов значение календарного времени, когда часы должны выдать сигнал аппаратного прерывания. Этот сигнал через вектор 70h инициирует обработчик прерываний, входящий в состав BIOS, который проверяет, возникло ли данное прерывание в результате достижения времени установки будильника (часы реального времени могут инициировать прерывания и по других причинам), тестирует заодно батарейное питание микросхемы, а затем посылает в оба контроллера прерываний команды конца прерываний и завершается командой iret.
Однако по ходу своего выполнения обработчик прерывания 70h выполняет команду hit 4Ah, которая передает управление на обработчик этого прерывания, тоже входящий в состав BIOS. Системный обработчик прерывания 4Ah ничего особенно полезного не делает, в сущности представляя собой просто программу-заглушку. Однако программист имеет возможность записать в вектор 4Ah адрес прикладного обработчика прерываний, который будет активизироваться прерыванием будильника. Функции прикладного обработчика определяет программист.
В примере 3.9 устанавливается прикладной обработчик прерывания 4All, который сам по себе вызваться никогда не будет, так как по умолчанию будильник часов реального не работает. Если, однако, прочитать системное время с помощью функции 02h прерывания lAh, прибавить к нему некоторую величину, например, 1 секунду, и установить будильник на это время (с помощью функции 06h прерывания lAh), то через одну секунду будет активизирован наш обработчик. В примере 3-9 этот процесс сделан бесконечным: в обработчике прерываний будильника снова выполняется чтение времени, прибавление к нему 1 секунды и установка будильника на новое время. В результате наш обработчик будет вызываться каждую секунду до завершения всей программы.
Помимо служебной функции установки будильника на следующую секунду, обработчик прерываний выполняет и полезную работу: он выводит текущее время в определенное место экрана. Поскольку обработчик активизируется каждую секунду, выводимое значение времени будет обновляться каждую секунду.
Как уже говорилось, в часах реального времени значение времени хранится в виде упакованных двоично-десятичных чисел. При выполнении арифметических операций с числами BCD (а нашем случае операции заключаются в прибавлении 1) необходимо использовать предназначенные для этого команды процессора. В примере проиллюстрировано использование одной из этих команд, конкретно, команды daa.
Для того, чтобы вывести на экран значение времени, его надо преобразовать в последовательность кодов ASCII. Процедура преобразования упакованных двоично-десятичных чисел в строку символов также включена в рассматриваемый пример.