Untitled
9. КОНТРОЛЛЕР ПРЯМОГО ДОСТУПА К ПАМЯТИ
9.1. Контроллер прямого доступа для IBM PC/XT
9.2. Контроллер прямого доступа для IBM AT
Прямой доступ к памяти (Direct Memory Access - DMA) используется
для выполнения операций передачи данных непосредственно между
оперативной памятью и устройствами ввода/вывода. Обычно это такие
устройства, как НГМД, НМД, кассетные накопители на магнитной ленте
КНМЛ (стримеры).
При использовании DMA процессор не участвует в операциях ввода/вывода,
контроллер прямого доступа сам формирует все сигналы, необходимые
для обмена данными с устройством. Скорость такого непосредственного
обмена значительно выше, чем при традиционном вводе/выводе с использованием
центрального процессора и команд INP, OUT.
Мы уже немного рассказывали о контроллере прямого доступа к памяти
в третьей книге первого тома, в разделе, посвященном работе с
НГМД на уровне команд ввода/вывода. Была приведена программа,
использующая DMA для чтения секторов дискеты. В этом разделе мы
подробнее рассмотрим порты контроллера DMA.
Распространены два типа контроллеров DMA - контроллеры для IBM
PC/XT и контроллеры для IBM AT. Вначале мы расскажем о первом
типе контроллеров, затем займемся контроллером DMA компьютера
IBM AT.
Контроллер прямого доступа для IBM PC/XT реализован на базе
микросхемы Intel 8237A и содержит четыре канала. Эти каналы используются
следующим образом:
0 | обновление содержимого динамической памяти компьютера, этот канал имеет наивысший приоритет;
|
1 | не используется; |
2 | адаптер накопителя на гибком магнитном диске (НГМД);
|
3 | адаптер накопителя на магнитном диске (НМД) - этот канал имеет низший приоритет.
|
9.1.1. Регистры каналов DMA
Каждый канал содержит 16-разрядные регистры:
- регистр текущего адреса CAR, содержит текущий адрес ячейки
памяти при выполнении операции обмена данными с использованием
DMA;
- регистр циклов прямого доступа к памяти CWR, содержит число
слов, предназначенных для передачи минус единица; при выполнении
обмена данными регистр работает в режиме вычитания;
- регистр хранения базового адреса BAR, используется для хранения
базового адреса памяти, используемого при передачи данных; в процессе
работы канала DMA содержимое этого регистра не изменяется;
- регистр хранения базового числа циклов прямого доступа к памяти
WCR; он хранит число циклов DMA, его содержимое также не изменяестя;
- регистр режима MR, определяющий режим работы канала.
Приведем адреса регистров и их форматы для компьютеров IBM PC/XT.
Порты 00h - 07h
Эти регистры содержат базовые адреса и счетчики передаваемых данных
каналов 0 - 3. Их назначение приводится в следующей таблице:
00h | Запись: | Базовый адрес канала 0
|
| Чтение: | Текущий адрес
|
01h | Запись: | Счетчик канала 0
|
| Чтение: | Текущий адрес
|
02h | Запись: | Базовый адрес канала 1
|
| Чтение: | Текущий адрес
|
03h | Запись: | Счетчик канала 1
|
| Чтение: | Текущий адрес
|
04h | Запись: | Базовый адрес канала 2
|
| Чтение: | Текущий адрес
|
05h | Запись: | Счетчик канала 2
|
| Чтение: | Текущий адрес
|
06h | Запись: | Базовый адрес канала 3
|
| Чтение: | Текущий адрес
|
07h | Запись: | Счетчик канала 3
|
| Чтение: | Текущий адрес
|
Порт 08h.
Этот порт используется при записи в качестве управляющего регистра
и при чтении как регистр состояния.
Формат управляющего регистра:
7 6 5 4 3 2 1 0
T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
LT+T+T+T+T+T+T+T-
¦ ¦ ¦ ¦ ¦ ¦ ¦ L= 1 - использование режима память-память;
¦ ¦ ¦ ¦ ¦ ¦ ¦ 0 - обычный режим работы;
¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦ L=== если используется режим память-память,
¦ ¦ ¦ ¦ ¦ ¦ то 1 в этом разряде разрешает захват
¦ ¦ ¦ ¦ ¦ ¦ канала, 0 - запрещает;
¦ ¦ ¦ ¦ ¦ ¦ в обычном режиме работы состояние этого
¦ ¦ ¦ ¦ ¦ ¦ бита безразлично;
¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ L===== 1 - запрет работы DMA;
¦ ¦ ¦ ¦ ¦ 0 - разрешение работы DMA;
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ L======= 1 - использование сжатия во времени, если
¦ ¦ ¦ ¦ установлен бит обычного режима работы;
¦ ¦ ¦ ¦ 0 - обычный режим работы;
¦ ¦ ¦ ¦
¦ ¦ ¦ L========= 1 - вращение приоритетов;
¦ ¦ ¦ 0 - фиксированные приоритеты;
¦ ¦ ¦
¦ ¦ L=========== 1 - удлиненный цикл записи;
¦ ¦ 0 - нормальный цикл записи;
¦ ¦
¦ L============= 1 - используется низкий уровень для
¦ сигнала запроса на DMA DREQ;
¦ 0 - используется высокий уровень;
¦
L=============== 1 - используется высокий уровень для
сигнала подтверждения DMA DACK;
0 - используется низкий уровень;
Обычно этот регистр инициализируется BIOS в процессе тестирования
системы и впоследствии изменять режим работы контроллера DMA не
требуется. Ошибки при инициализации этого порта могут привести
к "зависанию" системы.
При чтении из порта 08h программа получает слово состояния контроллера
DMA:
7 6 5 4 3 2 1 0
T-T-T-T-T-T-T-¬
¦ ¦ ¦
LT+-+-+T+T+-+-+T-
L==T==- L=====¦= биты 0-3 устанавливаются в 1 при
¦ достижении счетчиками каналов 0-3
¦ конечных значений;
¦
L============ биты 4-7 установлены в 1, если
имеется разрешение на DMA
соответственно, каналов 0-3.
Порт 09h.
Регистр запроса. Предназначен для организации программного (а
не аппаратного) запроса на DMA. Для использования программного
запроса канал должен быть запрограммирован в режиме блочной передачи.
Формат регистра:
7 6 5 4 3 2 1 0
T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦
LT+-+-+-+T+T+T+T-
L===T===- ¦ L=¦= номер используемого канала:
¦ ¦ 00 - канал 0;
¦ ¦ 01 - канал 1;
¦ ¦ 10 - канал 2;
¦ ¦ 11 - канал 3;
¦ ¦
¦ L===== 0 - установить запрос;
¦ 1 - сбросить запрос;
¦
L=========== не используются.
Порт 0Ah
Регистр маски. Используется для маскирования запросов на прямой
доступ для отдельных каналов:
7 6 5 4 3 2 1 0
T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦
LT+-+-+-+T+T+T+T-
L===T===- ¦ L=¦= номер канала:
¦ ¦ 00 - канал 0;
¦ ¦ 01 - канал 1;
¦ ¦ 10 - канал 2;
¦ ¦ 11 - канал 3;
¦ ¦
¦ L===== 0 - установить маску;
¦ 1 - сбросить маску;
¦
L=========== не используются.
Порт 0Bh
Регистр режима. Служит для определения режимов работы каналов
контроллера DMA:
7 6 5 4 3 2 1 0
T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦ ¦ ¦
LT+T+T+T+T+T+T+T-
L=¦ ¦ ¦ L=¦ L=¦= номер канала:
¦ ¦ ¦ ¦ 00 - канал 0;
¦ ¦ ¦ ¦ 01 - канал 1;
¦ ¦ ¦ ¦ 10 - канал 2;
¦ ¦ ¦ ¦ 11 - канал 3;
¦ ¦ ¦ ¦
¦ ¦ ¦ L===== тип цикла DMA:
¦ ¦ ¦ 00 - цикл проверки;
¦ ¦ ¦ 01 - цикл записи;
¦ ¦ ¦ 10 - цикл чтения;
¦ ¦ ¦ 11 - запрещенная комбинация;
¦ ¦ ¦
¦ ¦ L========= 1 - режим автоинициализации;
¦ ¦
¦ L=========== приращение адреса:
¦ 0 - инкрементирование;
¦ 1 - декрементирование;
¦
L============= режим обслуживания:
00 - передача по требованию;
01 - одиночная передача;
10 - блочная передача;
11 - каскадироание.
Порт 0Ch
Сброс триггера байтов. Для загрузки внутренних 16-разрядных регистров
контроллера используется последовательный вывод младшего, затем
старшего байтов слова. После сброса триггера байтов можно начинать
загрузку 16-разрядных регистров.
Порт 0Dh
Запись в этот порт вызывает сброс контроллера. Для дальнейшего
использования контроллер должен быть заново проинициализирован.
Порт 0Eh
Сброс регистра маски. После записи в этот регистр любого значения
разрешается работа всех четырех каналов прямого доступа.
Порт 0Fh
Маскирование/размаскирование каналов. С помощью этого порта можно
выполнить одновременное маскирование или размаскирование нескольких
каналов:
7 6 5 4 3 2 1 0
T-T-T-T-T-T-T-¬
¦ ¦ ¦ ¦ ¦ ¦
LT+-+-+T+T+T+T+T-
L==T==- ¦ ¦ ¦ L= 1 - маскирование канала 0;
¦ ¦ ¦ ¦ 0 - разрешение канала 0;
¦ ¦ ¦ ¦
¦ ¦ ¦ L=== 1 - маскирование канала 1;
¦ ¦ ¦ 0 - разрешение канала 1;
¦ ¦ ¦
¦ ¦ L===== 1 - маскирование канала 2;
¦ ¦ 0 - разрешение канала 2;
¦ ¦
¦ L======= 1 - маскирование канала 3;
¦ 0 - разрешение канала 3;
¦
L============ не используются.
Порты 81h-8Fh
Это порты регистров страниц.
Для работы с памятью контроллер прямого доступа использует 20-разрядные
физические адреса. Шестнадцать младших битов адреса необходимо
записать в регистр базового адреса канала. Старшие четыре бита
- биты 16-19 - должны быть записаны в соответствующие порты регистров
страниц.
При инициализации регистров базового адреса и регистра страниц
необходимо следить за тем, чтобы в процессе передачи данных не
происходил переход за границу 64 килобайта.
Для адресации регистров страниц можно использовать следующие порты:
81h | Регистр страниц канала 2
|
82h | Регистр страниц канала 3
|
83h | Регистр страниц канала 1
|
9.1.2. Инициализация канала DMA
Для инициализации канала программа должна выполнить следующие
шаги:
- сбросить триггер байтов командой записи в регистр 0Ch;
- задать режим работы канала, выполнив запись по адресу 0Bh
в регистр режима MR;
- заслать младшие 16 битов 20-битового адреса области памяти,
которая будет использована для передачи данных, в регистр базового
адеса (адрес порта зависит от номера канала: 0 канал использует
адрес 00h, 1 канал - 02h, 2 канал - 04h, 3 канал - 06h);
- заслать номер страницы (старшие 4 бита 20-битового адреса)
в регистр страниц 81h;
- загрузить регистр циклов прямого доступа к памяти CWR значением,
на 1 меньше требуемого количества передаваемых байтов (адреса
этих портов для каналов 0...3, соответственно, 01h, 03h, 05h,
07h;
- разрешить работу канала, выполнив запись в регистр маски каналов
по адресу 0Ah.
Сразу после разрешения канал начинает передачу данных. После окончания
передачи данных устройство обычно вырабатывает прерывание, которое
служит признаком окончания передачи данных.
Контроллер DMA компьютера IBM AT совместим снизу вверх с контролером
IBM PC/XT. Он состоит из двух каскадно включенных микросхем Intel
8237A-5. Второй контроллер обслуживает каналы DMA с номерами 4-7.
Приведем назначение каналов DMA для IBM AT:
0 | зарезервировано; |
1 | управление синхронной передачей данных SDLC (Synchronous Data Link Control);
|
2 | адаптер накопителя на гибком магнитном диске (НГМД);
|
3 | адаптер накопителя на магнитном диске (НМД);
|
4 | используется для каскадного соединения с первым контроллером DMA;
|
5-6 | зарезервировано. |
Другое отличие - это разрядность каналов. Каналы 0-3 являются
каналами 8-битовой передачи данных, а каналы 4-7 обеспечивают
16-битовую передачу данных. В связи с этим используются все 8
битов регистров страниц. Формируется 24-битовый адрес из 16-ти
младших битов адреса, записываемых в базовые регистры и 8-ми старших
битов адреса, записываемых в регистры страниц.
Размер страницы составляет 128 килобайт, поэтому при передаче
данных с использованием DMA не должна пересекаться граница 128
килобайт.
Приведем назначение и адреса регистров страниц контроллера для
IBM AT:
81h | Регистр страниц канала 2
|
82h | Регистр страниц канала 3
|
83h | Регистр страниц канала 1
|
87h | Регистр страниц канала 0
|
89h | Регистр страниц канала 6
|
8Bh | Регистр страниц канала 5
|
8Ah | Регистр страниц канала 7
|
8Fh | Регенерация динамической памяти
|
Для 16-битовых каналов 4-7 передача данных начинается с границы
слова и все адреса относятся к 16-битовым словам.
Порты 0C0h - 0DFh
Эти регистры содержат базовые адреса и счетчики передаваемых данных
каналов 4-7. Их назначение приводится в следующей таблице:
0C0h | Запись: | Базовый адрес канала 4
|
| Чтение: | Текущий адрес
|
0C2h | Запись: | Счетчик канала 4
|
| Чтение: | Текущий адрес
|
0C4h | Запись: | Базовый адрес канала 5
|
| Чтение: | Текущий адрес
|
0C6h | Запись: | Счетчик канала 5
|
| Чтение: | Текущий адрес
|
0C8h | Запись: | Базовый адрес канала 6
|
| Чтение: | Текущий адрес
|
0CAh | Запись: | Счетчик канала 6
|
| Чтение: | Текущий адрес
|
0CCh | Запись: | Базовый адрес канала 7
|
| Чтение: | Текущий адрес
|
0CEh | Запись: | Счетчик канала 7
|
| Чтение: | Текущий адрес
|
Порты 0D0h-0DFh
Это управляющие порты и порты состояния второй микросхемы 8237A-5.
По формату и назначению они соответствуют рассмотренным ранее
для контроллера DMA компьютеров IBM PC/XT:
0D0h | Управляющий регистр / регистр состояния
|
0D2h | Регистр запроса |
0D4h | Регистр маски |
0D6h | Регистр режима |
0D8h | Сброс триггера байтов
|
0DAh | Сброс контроллера |
0DCh | Сброс регистра маски |
0DEh | Маскирование/размаскирование каналов
|
В качестве примера использования контроллера прямого доступа к
памяти приведем программу чтения сектора флоппи-диска. Мы уже
описывали ее в предыдущем томе. Поэтому здесь мы не будем описывать
команды контроллера НГМД и другие тонкости, имеющие отношение
к работе с флоппи-дисками.
Перед началом инициализации КПДП программа должна послать в порты
0Bh и 0Ch код операции, которая будет выполняться КПДП - 46h для
операции чтения и 4Ah для операции записи.
В процессе инициализации программа должна сообщить КПДП адрес
буфера, куда ему следует поместить данные или откуда надо взять
данные, и длину передаваемых данных в байтах.
Адрес необходимо представить в виде номера страницы и смещения.
Для КПДП машины AT используется восьмибитовый номер страницы и
16-битовое смещение. Например, для адреса 23456 номер страницы
- 2, смещение - 3456.
Для программирования канала 2 КПДП программа должна сначала вывести
младший байт смещения в порт с адресом 4, затем вывести в этот
же порт старший байт смещения и, наконец, вывести байт номера
страницы в порт с адресом 81h.
Длина передаваемых данных выводится аналогично в порт с адресом
5 - сначала младший байт длины, затем старший.
После определения режима работы канала, адреса буфера и длины
передаваемых данных, программа должна разрешить работу КПДП, выдав
в порт с адресом 0Ch байт 2. Теперь канал прямого доступа готов
к работе и будет ждать данных от контроллера НГМД.
Приведенная ниже демонстрационная программа использует несколько
наиболее характерных команд контроллера НГМД. Она предназначена
для работы на машине AT. Для того, чтобы она правильно работала
и на машинах PC/XT, ее надо немного изменить. Изменения касаются
программирования контроллера ПДП и программирования скорости передачи
контроллера НГМД.
Контроллер КПДП PC/XT использует 4-битовый номер страницы буфера
вместо 8-битового. Скорость передачи контроллера НГМД в машинах
PC/XT не программируется, вам надо убрать соответствующие строки
из программы. Еще надо обратить внимание на различное быстродействие
машин AT и PC/XT и скорректировать константы в строках программы,
выполняющих задержку.
Программа не проверяет, установлен ли флоппи-диск в приемный карман
дисковода, поэтому перед запуском не забудьте установить диск.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include "sysp.h"
#define CYL 0
void main(void);
void fdc_out(unsigned char byte);
int fdc_inp(void);
void int_wait(void);
void dma_init(char *);
void main(void) {
unsigned i;
long l;
char buffer[512];
char status[7], main_status;
DPT _far *fdpt;
FILE *sect;
printf("n"
"nРабота с контроллером НГМД"
"n ©Фролов А., 1991"
"n");
// Эта программа предназначена только для IBM AT
if(pc_model() != 0xfc) {
printf("Эта программа предназначена только для IBM ATn");
exit(-1);
}
// Открываем файл, в который будем записывать
// содержимое самого первого сектора на дискете
sect = fopen("!sector.dat","wb+");
// Устанавливаем указатель на таблицу
// параметров дискеты
fdpt = get_dpt();
// Включаем мотор дисковода А:
// Перед этим разрешаем прерывания
_enable();
outp(0x3F2, 0x1C);
// Выполняем задержку для разгона двигателя
for(l=0;l<200000;l++);
// Показываем содержимое регистра основного
// состояния контроллера
printf("Мотор включен.tt");
printf("Основное состояние: %02.2Xn",inp(0x3F4));
// Перед чтением сектора необходимо установить
// головку на нужную дорожку, в нашем случае это
// дорожка с номером CYL.
// Выдаем контроллеру команду "Поиск"
fdc_out(0xf);
// Для команды "Поиск" требуется два байта параметров:
// номер головки/номер накопителя и номер дорожки.
// Мы работаем с нулевой головкой накопителя А:,
// поэтому первый параметр равен 0, второй - CYL
fdc_out(0);
fdc_out(CYL);
// Показываем содержимое регистра основного
// состояния контроллера
printf("n<<<Поиск>>> tt");
printf("Основное состояние: %02.2Xn",inp(0x3F4));
// Ожидаем прерывание по завершению операции
int_wait();
// Задержка для позиционирования головки
for(l=0;l<20000;l++);
// Для проверки результата выполнения команды
// "Поиск" выдаем контроллеру команду
// "Чтение состояния прерывания"
// Выводим содержимое регистра состояния
// ST0 и номер дорожки после выполнения команды
// "Поиск" PCN
fdc_out(0x8);
printf("Состояние прерывания:t");
printf(" ST0: %02.2X, t", fdc_inp());
printf("PCN: %02.2Xn", fdc_inp());
// Для более глубокой диагностики состояния
// контроллера выдаем контроллеру команду
// "Чтение состояния накопителя", выводим
// содержимое регистра состояния ST3
fdc_out(4);
fdc_out(0);
printf("Состояние накопителя:t ST3: %02.2Xn",fdc_inp());
// Устанавливаем скорость передачи данных 500 Кбайтов/с,
// это значение может различаться для разных типов дискет
outp(0x3F7, 0);
// Инициализация канала прямого
// доступа к памяти
dma_init(buffer);
// Выдаем команду "Чтение данных"
fdc_out(0x66);
fdc_out(0x0); // накопитель 0, головка 0
fdc_out(CYL); // цилиндр CYL
fdc_out(0); // головка 0
fdc_out(1); // номер сектора - 1
// Передаем контроллеру технические параметры
// дисковода, берем их из таблицы параметров дискеты.
// Это такие параметры:
// - размер сектора;
// - номер последнего сектора на дорожке;
// - размер промежутка;
// - число считываемых/записываемых байтов
fdc_out(fdpt->sec_size);
fdc_out(fdpt->eot);
fdc_out(fdpt->gap_rw);
fdc_out(fdpt->dtl);
// Ожидаем прерывание по завершению операции
int_wait();
// Считываем и выводим на экран байты результата
// операции "Чтение данных"
printf("n<<<Чтение сектора>>> n");
printf(" Байты состояния (ST0,ST1,ST2,C,H,R,N):n");
for(i=0; i<7; i++) printf("%02.2Xt", (char) fdc_inp());
printf("n");
// Выводим содержимое считанного сектора в файл
for(i=0; i<512; i++) fputc(buffer[i],sect);
fclose(sect);
// Выключаем мотор
outp(0x3F2, 0xC);
}
// Вывод байта в контроллер дисковода
void fdc_out(unsigned char parm) {
_asm {
mov dx,3F4h // Порт основного состояния
loop_fdc_out:
in al,dx
test al,80h // Проверяем готовность
jz loop_fdc_out // контроллера
inc dx // Выводим байт в порт данных
mov al, parm // контроллера
out dx, al
}
}
// Ввод байта из порта данных контроллера дисковода
int fdc_inp(void) {
_asm {
mov dx,3F4h // Порт основного состояния
loop_fdc_inp:
in al,dx
test al,80h // Проверяем готовность
jz loop_fdc_inp // контроллера
inc dx // Введенный байт записываем
in al, dx // в регистр AX
}
}
// Ожидание прерывания от контроллера
void int_wait(void) {
// Разрешаем прерывания
_enable();
_asm {
mov ax,40h // После прихода прерывания
mov es,ax // программа обработки прерывания
mov bx,3Eh // устанавливает в 1 старший бит
wait_loop: // байта в области данных BIOS
mov dl,es:[bx] // по адресу 0040:003E.
test dl,80h // Мы ждем, когда этот бит будет
jz wait_loop // установлен в 1, а затем
// сбрасываем его.
and dl,01111111b
mov es:[bx],dl
}
}
// Инициализация канала прямого доступа к памяти
void dma_init(char *buf) {
unsigned long f_adr;
unsigned sg, of;
// Вычисляем 24-разрядный адрес буфера для данных
f_adr = ((unsigned long)_psp << 4)
+ (((unsigned long)buf) & 0xffff);
// Расщепляем адрес на номер страницы
// и смещение
sg = (f_adr >> 16) & 0xff;
of = f_adr & 0xffff;
// На время программирования контроллера прямого
// доступа запрещаем прерывания
_disable();
_asm {
mov al,46h // Команда чтения данных от
// контроллера НГМД.
out 12,al // Сброс триггера-указателя байта
// для работы с 16-разрядными портами.
// Следующий байт, выводимый в 16-разрядный
// порт будет интерпретироваться
// как младший.
out 11,al // Установка режима контроллера ПДП
mov ax,of // Смещение буфера, младший байт
out 4,al
mov al,ah // Смещение буфера, старший байт
out 4,al
mov ax,sg // Номер страницы
out 81h,al
mov ax,511 // Длина передаваемых данных
out 5,al
mov al,ah
out 5,al
mov al,2 // Разблокировка канала 2 контроллера ПДП
out 10,al
}
// Инициализация контроллера закончена,
// разрешаем прерывания.
_enable();
}
Остальные команды вы можете попробовать сами. Для получения дополнительной
информации по контроллеру НГМД обратитесь к техническому руководству
по IBM PC. Многое можно почерпнуть из описания микросхем дискового
контроллера 765 фирмы NEC и аналогов этой микросхемы - Intel 8272A
и отечественной КР1810ВГ72А.
На этом мы завершим обсуждение контроллера DMA. Советуем вам еще
раз посмотреть программу, читающую сектора диска с использованием
канала прямого доступа в памяти, которую мы приводили в третьей
книге первого тома. Вы можете самостоятельно внести в нее некоторые
усовершенствования, например, проверку перехода адреса в процессе
работы канала прямого доступа через границу 128 килобайтов.
|