Прямой доступ к памяти (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-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-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 килобайтов.