Материал: Системное программирование

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

10.4. Упражнения

Пример 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.

Источник: https://files.student-it.ru/previewfile/284711