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 | адаптер накопителя на магнитном диске (НМД) - этот канал имеет низший приоритет. |
Каждый канал содержит 16-разрядные регистры:
Приведем адреса регистров и их форматы для компьютеров IBM PC/XT.
Эти регистры содержат базовые адреса и счетчики передаваемых данных
каналов 0 - 3. Их назначение приводится в следующей таблице:
| 00h | Запись: | Базовый адрес канала 0 |
| Чтение: | Текущий адрес | |
| 01h | Запись: | Счетчик канала 0 |
| Чтение: | Текущий адрес | |
| 02h | Запись: | Базовый адрес канала 1 |
| Чтение: | Текущий адрес | |
| 03h | Запись: | Счетчик канала 1 |
| Чтение: | Текущий адрес | |
| 04h | Запись: | Базовый адрес канала 2 |
| Чтение: | Текущий адрес | |
| 05h | Запись: | Счетчик канала 2 |
| Чтение: | Текущий адрес | |
| 06h | Запись: | Базовый адрес канала 3 |
| Чтение: | Текущий адрес | |
| 07h | Запись: | Счетчик канала 3 |
| Чтение: | Текущий адрес |
Этот порт используется при записи в качестве управляющего регистра и при чтении как регистр состояния.
Формат управляющего регистра:
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.
Регистр запроса. Предназначен для организации программного (а не аппаратного) запроса на 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=========== не используются.
Регистр маски. Используется для маскирования запросов на прямой доступ для отдельных каналов:
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=========== не используются.
Регистр режима. Служит для определения режимов работы каналов контроллера 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 - каскадироание.
Сброс триггера байтов. Для загрузки внутренних 16-разрядных регистров контроллера используется последовательный вывод младшего, затем старшего байтов слова. После сброса триггера байтов можно начинать загрузку 16-разрядных регистров.
Запись в этот порт вызывает сброс контроллера. Для дальнейшего использования контроллер должен быть заново проинициализирован.
Сброс регистра маски. После записи в этот регистр любого значения разрешается работа всех четырех каналов прямого доступа.
Маскирование/размаскирование каналов. С помощью этого порта можно выполнить одновременное маскирование или размаскирование нескольких каналов:
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============ не используются.
Это порты регистров страниц.
Для работы с памятью контроллер прямого доступа использует 20-разрядные физические адреса. Шестнадцать младших битов адреса необходимо записать в регистр базового адреса канала. Старшие четыре бита - биты 16-19 - должны быть записаны в соответствующие порты регистров страниц.
При инициализации регистров базового адреса и регистра страниц необходимо следить за тем, чтобы в процессе передачи данных не происходил переход за границу 64 килобайта.
Для адресации регистров страниц можно использовать следующие порты:
| 81h | Регистр страниц канала 2 |
| 82h | Регистр страниц канала 3 |
| 83h | Регистр страниц канала 1 |
Для инициализации канала программа должна выполнить следующие шаги:
Сразу после разрешения канал начинает передачу данных. После окончания передачи данных устройство обычно вырабатывает прерывание, которое служит признаком окончания передачи данных.
Контроллер 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-битовым словам.
Эти регистры содержат базовые адреса и счетчики передаваемых данных
каналов 4-7. Их назначение приводится в следующей таблице:
| 0C0h | Запись: | Базовый адрес канала 4 |
| Чтение: | Текущий адрес | |
| 0C2h | Запись: | Счетчик канала 4 |
| Чтение: | Текущий адрес | |
| 0C4h | Запись: | Базовый адрес канала 5 |
| Чтение: | Текущий адрес | |
| 0C6h | Запись: | Счетчик канала 5 |
| Чтение: | Текущий адрес | |
| 0C8h | Запись: | Базовый адрес канала 6 |
| Чтение: | Текущий адрес | |
| 0CAh | Запись: | Счетчик канала 6 |
| Чтение: | Текущий адрес | |
| 0CCh | Запись: | Базовый адрес канала 7 |
| Чтение: | Текущий адрес | |
| 0CEh | Запись: | Счетчик канала 7 |
| Чтение: | Текущий адрес |
Это управляющие порты и порты состояния второй микросхемы 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 AT\n");
exit(-1);
}
// Открываем файл, в который будем записывать
// содержимое самого первого сектора на дискете
sect = fopen("!sector.dat","wb+");
// Устанавливаем указатель на таблицу
// параметров дискеты
fdpt = get_dpt();
// Включаем мотор дисковода А:
// Перед этим разрешаем прерывания
_enable();
outp(0x3F2, 0x1C);
// Выполняем задержку для разгона двигателя
for(l=0;l<200000;l++);
// Показываем содержимое регистра основного
// состояния контроллера
printf("Мотор включен.\t\t");
printf("Основное состояние: %02.2X\n",inp(0x3F4));
// Перед чтением сектора необходимо установить
// головку на нужную дорожку, в нашем случае это
// дорожка с номером CYL.
// Выдаем контроллеру команду "Поиск"
fdc_out(0xf);
// Для команды "Поиск" требуется два байта параметров:
// номер головки/номер накопителя и номер дорожки.
// Мы работаем с нулевой головкой накопителя А:,
// поэтому первый параметр равен 0, второй - CYL
fdc_out(0);
fdc_out(CYL);
// Показываем содержимое регистра основного
// состояния контроллера
printf("\n<<<Поиск>>> \t\t");
printf("Основное состояние: %02.2X\n",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.2X\n", fdc_inp());
// Для более глубокой диагностики состояния
// контроллера выдаем контроллеру команду
// "Чтение состояния накопителя", выводим
// содержимое регистра состояния ST3
fdc_out(4);
fdc_out(0);
printf("Состояние накопителя:\t ST3: %02.2X\n",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.2X\t", (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 килобайтов.