СОДЕРЖАНИЕ
2. Программная модель микропроцессора 8086
2.4. Способы задания операндов команды
3. Основные понятия языка ассемблера
3.2. Директивы определения данных
4. Сегментированная модель памяти
4.3. Общая структура программы
5.1. Соглашению по описанию команд
5.6. Команды организации циклов
5.11. Команды управления микропроцессором
7. Разработка одномодульной программы
7.1. Трансляция и компоновка программы
8. Разработка многомодульных программ
8.1. Принципы разработки модулей
8.2. Расширенное применение директивы сегментации
Программирование микропроцессорных устройств
10. Программирование системного таймера
10.1. Описание таймера-счетчика 8254
10.3. Структура регистров таймера
11. Программирование контроллера прерываний
11.1. Механизм обработки прерываний
11.4. Контроллер прерываний 8259
11.5. Идентификация прерываний
11.6. Прерывания bios и ms-dos
12. Программирование параллельного порта
12.2. Работа с параллельным портом на низком уровне
12.3. Стандартные средства работы с параллельным портом
13. Программирование последовательного порта
13.1. Основы последовательной передачи данных
13.2. Последовательный интерфейс rs-232c
13.3. Универсальный асинхронный приемо-передатчик 8250
13.4. Порты асинхронного адаптера
13.5. Стандартные средства программирования последовательного порта
Пример 1. Используя встроенный в среду Borland® Delphi ассемблер, разработать две подпрограммы для определения текущего состояния счетчика и его значения. Обе подпрограммы должны быть функциями. Первая подпрограмма должна использовать регистр задвижки и возвращать текущее значение счетчика. Вторая подпрограмма должна использовать режим «Read back command» и в зависимости от переданного параметра возвращать режим работы счетчика либо его текущее значение.
Решение. Функция LatchCNT() возвращает текущее значение счетчика с помощью задвижки и поэтому может работать с обеими микросхемами таймера 8253(4). Единственным параметром функции является порядковый номер канала CNT, который может принимать значения [0,1,2]. Формат управляющего регистра показан в табл. 88, а алгоритм работы функции – на рис. 38.
Табл. 88. Формат управляющего регистра.
|
Биты |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
Значение |
SC1 |
SC0 |
0 |
0 |
x |
x |
x |
x |
Исходный текст функции приведен ниже.
function LatchCNT(CNT: Byte): Word;
begin
asm
{ Сохранение в стеке используемых регистров }
PUSH AX
PUSH DX
{ Фиксировать текущее значение счетчика }
MOV AL, CNT
SHL AL, 6
MOV DX, 43h
OUT DX, AL
{ Код выбора сначала LSB, а затем MSB }
OR AL, 00110000b
MOV DX, 43h
OUT DX, AL
{ Адрес регистра счетчика }
MOV DX, 40h
ADD DL, CNT
{ Получение младшего байта регистра счетчика }
IN AL, DX
{ Сохранение младшего байта в AH }
MOV AH, AL
{ Получение старшего байта регистра счетчика }
IN AL, DX
{ Обмен младшего и старшего байтов аккумулятора }
XCHG AL, AH
{ Функция возвращает результат }
MOV @Result, AX
{ Восстановление из стека содержимого регистров }
POP DX
POP AX
end
end;

Рис. 38. Алгоритм работы функции LatchCNT().
Функция GetCNT() предназначена для работы только с микросхемой 8254, т.к. использует режим «Read back command». Первый параметр CNT по-прежнему задает номер канала, а параметр Mode – запрашиваемое действие. При Mode = 0 функция возвращает текущее значение, при Mode = 1 – режим работы указанного канала (табл. 89).
Табл. 89. Формат управляющего регистра.
|
Значение счетчика |
||||||||
|
Биты |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
Значение |
1 |
1 |
0 |
1 |
C2 |
C1 |
C0 |
X |
|
Статус канала |
||||||||
|
Биты |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
Значение |
1 |
1 |
1 |
0 |
C2 |
C1 |
C0 |
X |
Коды выбора счетчика перечислены в табл. 90. Для упрощения не используется комбинация (C2=1, C1=1, C0=1).
Табл. 90. Коды выбора счетчика.
|
Счетчик |
C2 |
C1 |
C0 |
|
0 |
0 |
0 |
1 |
|
1 |
0 |
1 |
0 |
|
2 |
1 |
0 |
0 |
Алгоритм работы функции GetCNT() может быть следующим (рис. 39). Вначале код канала CNT сдвигается влево на CNT+1 разрядов. Затем код необходимо сложить по маске 0E0h (получить статус канала) или по маске 0D0h (получить значение счетчика). Полученный управляющий код должен быть выведен в порт 43h. После этого может быть прочитано содержимое регистра счетчика, вначале младший значимый байт, а затем старший.
Исходный текст функции приведен ниже.
function GetCNT(CNT,Mode: Byte): Word;
begin
asm
{ Сохранение в стеке используемых регистров }
PUSH AX
PUSH BX
PUSH DX
{ Сохранение номера счетчика в AL }
MOV AL,CNT
{ Сдвиг на CNT+1 влево содержимого AL для размещения
номера канала в разрядах D3D2D1 управляющего кода }
MOV CL, CNT
INC CL
SHL AL,CL
{ Определение запрашиваемого действия }
CMP Mode, 0
JNE @@1
{ Сложение по маске $0E0 }
OR AL, 0E0h
JMP @@2
{ Сложение по маске $0D0 }
@@1: OR AL, 0D0h
@@2: MOV DX, 43h
{ Передача кода в управляющий регистр }
OUT DX, AL
{ Получить адрес счетчика }
MOV DH, 0
MOV DL, 40h
ADD DL, CNT
{ Получение младшего байта регистра счетчика }
IN AL, DX
MOV AH, 0
{ Вновь определение запрашиваемого действия }
CMPMode, 0
JNE@@3
{ Сохранение младшего байта в AH }
MOV AH, AL
{ Получение старшего байта регистра счетчика }
IN AL, DX
{ Обмен младшего и старшего байтов аккумулятора }
XCHGAL,AH
{ Функция возвращает результат }
@@3: MOV @Result, AX
{ Восстановление из стека содержимого регистров }
POP DX
POP BX
POP AX
end;
end;

Рис. 39. Алгоритм работы функции GetCNT().
Пример 2. Используя ассемблер TASM, разработать программу определения внутренней тактовой частоты микропроцессора в МГц. Использовать режим работы ассемблера IDEAL и упрощенные директивы сегментации. Для подсчета количества тактов процессора применить команду RDTSC (код операции 0F31h).
Решение. Команда RDTSC предназначена для загрузки в регистровую пару EDX:EAX содержимого 64-разрядного регистра MSR (Model Specific Register). Он используется для хранения значения счетчика тактовых импульсов TSC (Time Stamp Counter). Значение счетчика увеличивается на единицу с приходом очередного такта микропроцессора, а обнуление происходит при системном сбросе. Заметим, что команда RDTSC работает на микропроцессорах Intel Pentium и выше.
Внутренняя тактовая частота микропроцессора может быть рассчитана исходя из количества тактов, прошедших за временной интервал, равный периоду следования импульсов. Число тактов определяется с помощью команды RDTSC по содержимому регистра MSR в моменты изменения значения таймера. Эти изменения можно отследить с помощью переменной BIOS, расположенной по адресу 0040:006Ch. На рис. 40 показан алгоритм вычисления тактовый частоты.
Установить
начальные значения параметров для
интервалов




Нет
Получить
текущее время, выраженное в 1/18.2 долях
секунды
Да
Расчет
длительности интервалов в тактах

Задержка
на время 1/18.2
доли секунды
Расчет
среднего значения
длительности интервала
в тактах
Выполнить команду
RDTSC
Расчет
тактовой частоты
в МГц
Сохранить
значение счетчика TSC


Выход
в MS-DOS

Рис. 40. Алгоритм расчета внутренней тактовой частоты процессора.
Для повышения точности вычислений необходима выборка, по которой будут рассчитаны длительности каждого интервала в тактах, а также среднее значение длительности интервалов. Учитывая частоту генератора и максимальное значение коэффициента делителя для нулевого канала, расчетная формула внутренней частоты процессора будет иметь вид:

или
![]()
Количество замеров для вычисления среднего значения длительности интервалов может быть произвольным, исходя из принципа «чем больше, тем лучше». Однако в случае реализации на ассемблере оптимальным является выбор значения, на единицу большего числа, кратного степени 2. Связано это с тем, что операция деления на 2n в двоичной арифметике реализуется простым сдвигом на n разрядов вправо. Число итераций равно 33, а количество интервалов 32.