Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Аппаратное обеспечение IBM PC

© Александр Фролов, Григорий Фролов
Том 2, книга 1, М.: Диалог-МИФИ, 1992.

[Назад] [Содеожание] [Дальше]

9.2. Контроллер прямого доступа для IBM AT

Контроллер 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 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 килобайтов.

[Назад] [Содеожание] [Дальше]