Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МЕТ_ОРГ_1.doc
Скачиваний:
29
Добавлен:
18.11.2019
Размер:
1.27 Mб
Скачать
    1. Использование средств 32-разрядных процессоров в программировании

Как уже отмечалось, при разработке 16-разрядных программ реального режима, предназначенных для выполнения под управлением операционной системы MS-DOS, вполне допустимо использование ряда дополнительных возможностей 32-разрядных процессоров. В реальном режиме можно использовать:

32-разрядные операнды;

дополнительные команды и расширенные возможности команд МП 86;

дополнительные режимы адресации;

четыре сегментных регистра для адресации данных вместо двух.

Для того чтобы транслятор распознавал все эти средства, необходимо начать программу с директивы .586 (или, при желании, .486 или .386) и указать при этом для сегментов команд и данных описатель use16, чтобы программа осталась 16-разрядной.

Следует заметить, что возможности использования в программах реального режима дополнительных средств 32-разрядных процессоров, хотя и кажутся привлекательными, в действительности весьма ограничены. Новых команд не так уж много, и они не имеют принципиального характера: 32-разрядные данные используются в прикладных программах относительно редко (если не касаться вычислительных программ, содержащих действительные числа, но такие программы редко пишут на языке ассемблера); расширенные возможности адресации в полной мере проявляются лишь в 32-разрядных программах, не работающих в DOS. Тем не менее в каких-то случаях привлечение средств 32-разрядных процессоров может оказаться полезным и в 16-разрядных программах, приведем пример их использования.

Среди системных данных DOS и BIOS есть данные, требующие для своего размещения 2 слов. К таким данным, в частности, относится системное время, накапливаемое в 4х-байтовой ячейке с абсолютным адресом 0046Ch. В процессе начальной загрузки компьютера в ячейку с адресом 46Ch переносится из часов реального времени время, истекшее от начала суток, а затем содержимое этой ячейки увеличивается на 1 каждым прерыванием от системного таймера, подключенного к вектору 8. Чтение ячейки 46Ch позволяет определить текущее время с погрешностью приблизительно в 1/18 секунды, что позволяет достаточно точно измерять интервалы времени. Арифметические действия с системным временем удобно выполнять в расширенных 32-разрядных регистрах.

Рассмотрим программу, которая позволяет установить требуемый временной интервал и отработать некоторым образом его окончание (см. также пример 2.2 из раздела 2.4). Поскольку MS-DOS является однозадачной системой, единственным способом организации параллельных процессов - выполнения программы и ожидания окончания временного интервала - является использование механизма прерываний. В нашем случае программа содержит обработчик прерываний от системного таймера, который 18 раз в секунду читает системное время и сравнивает его значение с заданной заранее величиной.

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

Приведенный ниже пример выполнен в виде программы типа .СОМ. Такая организация программы упрощает обработчик прерываний и облегчает его написание. Дело заключается в том, что процессор, переходя по аппаратному прерыванию на обработчик прерывания, модифицирует только регистры CS:IP (значениями, полученными из вектора прерываний). Все остальные регистры, в том числе и сегментные, сохраняют те значения, которые они имели на момент прерывания. Значения эти могут быть какими угодно, особенно, если основная программа вызывает функции DOS. Поэтому, если в обработчике прерываний необходимо обратиться к данным, хранящимся в основной программе, нам необходимо настроить какой-либо из сегментных регистров (например, DS или ES) на сегментный адрес сегмента данных основной программы. Если же программа написана в формате .СОМ, то ее поля данных входят в тот же (единственный) сегмент, где расположены команды, и для обращения к данным можно воспользоваться регистром CS, который при вызове обработчика настраивается процессором.

Чтение и сравнение системного времени по прерываниям от таймера

.586 ; Будут 32-разрядные операнды

assume CS : code, DS:code

code segment use16 ;16-разрядное приложение

org 100h ;Формат .COM

main proc

mov AX, 3508h ;Сохраним исходный вектор

int 21h

mov word ptr old_08, BX

mov word ptr old_08+2,ES

mov AX,2508h ;Установим наш обработчик

mov DX,offset new_08

int21h

;Прочитаем системное время, прибавим требуемый интервал

;и сохраним в двухсловной ячейке памяти

mov AX, 40h ;Настройка ES на

mov ES, AX ;область данных BIOS

mov EAX, ES : 6Ch ;Получаем системное время

add EAX, time_int ;Прибавить интервал

mov time_end, EAX ;Сохраним в памяти

;Имитация рабочего цикла программы с опросом флага

again: test flag, 0FFh ;Проверка флага готовности

jnz ok ;Если установлен, на OK

mov АН, 02h ;B ожидании окончания

mov DL,'.' ;временного интервала

int2Ih ;выводим на экран точки

jmp again ;И снова на проверку флага

ok: mov АН, 09h ;Интервал завершен.

mov DX, offset msg ;Выполним, что хотели

int 2Ih ;Завершим программу, восстановив сначала исходный вектор

lds DX, old_08

mov AX, 2508h

int 21h

mov AX, 4C00h

int 21n

main endp

;Наш обработчик прерываний от системного таймера

new_08 proc

pushf ;Запишем флаги в стек

call CS:old_08 ;и вызовем системный обработчик

push EAX ;Сохраним используемые

push ES ;регистры

mov AX,40h ;Настроим ES

mov ES,AX ;на область данных BIOS

mov EAX,ES:6Ch ;Прочитаем текущее время

cmp EAX,CS:time_end ;Сравним с вычисленным

jb e_nd ;Если меньше, на выход

inc CS:flag ;Интервал истек, установим флаг

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

out 20h,AL ;в контроллер прерываний

pop ES ;Восстановим

pop EAX ;сохраненные регистры

iret ;Выход из обработчика

new_08 endp

;Поля данных программы

old_08 dd 0 ;Для хранения исходного вектора

time_int dd 18*2 ;Требуемый интервал (~2с)

time_end dd 0 ;Момент истечения интервала

flag db 0 ;Флаг истечения интервала

msg db "Время истекло !$' ;Информационное сообщение

code ends

end main

Установка обработчика в рассматриваемом примере выполняется немного проще, так как нет необходимости настраивать регистр DS на сегмент данных - он и так уже настроен на единственный сегмент программы. Установив обработчик, программа настраивает регистр ES на область данных BIOS и считывает из ячейки с адресом 46Ch текущее системное время командой add к нему прибавляется заданный в ячейке time_int интервал (в примере - приблизительно 2 с), и результат сохраняется в ячейке timc_cnd. Действия по установке обработчика закончены, и программа может приступить к выполнению запланированных для нее действий. В данном примере программа в цикле вызывает функцию DOS 02h вывода на экран символа точки; в действительности она может, например, выполнять обработку и вывод на экран некоторых данных. В каждом шаге цикла происходит тестирование флага окончания временного интервала flag, который должен быть установлен обработчиком прерываний по истечении заданного временного интервала. Пока флаг сброшен, цикл продолжается. Как только флаг окажется установлен, программа приступает к выполнению действий по отработке этого события. В рассматриваемом примере выполняется вывод на экран информационного сообщения и завершение программы с обязательным восстановлением исходного содержимого вектора 8. Обработчик прерываний new_08 прежде всего выполняет вызов исходного обработчика, адрес которого мы сохранили в ячейке old_08. В данном случае сцепление обработчиков необходимо, так как подключение к вектору 8 нашего обработчика не должно нарушить ход системных часов.

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