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

MS-DOS для программиста

© Александр Фролов, Григорий Фролов
Том 19, М.: Диалог-МИФИ, 1995, 253 стр.

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

1.3. Характеристики дисковых накопителей

Прежде чем начать работу с дисками на физическом уровне, необходимо выяснить конфигурацию дисковой системы - сколько дисководов и какого типа подключено к компьютеру, сколько дорожек и головок имеется на каждом из дисководов и т. д. Способ, которым определяется конфигурация дисковой системы, зависит от модели компьютера (PC, XT, AT), поэтому вначале займемся определением типа персонального компьютера.

Определение типа компьютера

ПЗУ базовой системы ввода/вывода BIOS содержит по адресу FFFFh:FFFEh байт конфигурации, значение которого можно использовать для идентификации типа компьютера:

Значение Тип компьютера
FFh Оригинальный IBM PC
FEh IBM PC/XT, Portable PC
FDh PCjr
FCh IBM PC/AT
FBh IBM PC/XT с памятью 640 Кбайт на материнской плате
F9h Convertible PC

Для компьютеров IBM PC и IBM PC/XT конфигурация дисковой системы определяется установкой переключателей на основной плате, в частности, переключателями устанавливается количество подключенных к системе НГМД.

Компьютеры IBM PC/AT (и более высокого класса) имеют на основной плате CMOS-память с малым энергопотреблением, которая питается от аккумулятора. В CMOS-памяти хранится информация о конфигурации дисковой системы. В процессе инициализации BIOS считывает эту информацию и записывает ее в свою внутреннюю область данных.

Проанализировав значение байта конфигурации, можно сделать предварительное заключение о составе дисковой системы компьютера. Если оно равно FFh, FDh, F9h, то наш компьютер не имеет НМД - это одна из разновидностей IBM PC. Значения FEh, FBh могут соответствовать IBM PC/XT и совместимым с ним компьютерам. Такие компьютеры могут быть оборудованы НМД. И, наконец, значение FCh соответствует IBM PC/AT. Для этого компьютера конфигурация дисковой системы должна определяться исходя из содержимого CMOS-памяти.

Следует заметить, что новые модели компьютеров могут иметь и другие, не перечисленные выше, коды идентификации.

Прерывание INT 11h

Прерывание базовой системы ввода/вывода INT 11h возвращает в регистре AX байт конфигурации системы, который можно использовать для определения количества НГМД и наличия НМД. Самый младший бит байта конфигурации (бит 0) - признак наличия в системе НМД. Если этот бит установлен в 1, то компьютер оборудован НМД, иначе дисковая система состоит только из накопителей на гибких магнитных дисках.

Биты 7 и 6 содержат информацию о количестве НГМД:

Содержимое бит 7 и 6 Количество установленных НГМД
00 1
01 2
10 3
11 4

Это прерывание лучше всего использовать для IBM PC/XT и IBM PC. Для IBM PC/AT необходимо исследовать содержимое CMOS-памяти. Займемся этим.

Анализ содержимого CMOS-памяти

Программа не может непосредственно адресовать CMOS-память , как обычную оперативную память. Для работы с CMOS-памятью необходимо использовать порты ввода/вывода с адресами 70h и 71h, причем процедура записи или чтения состоит из двух шагов.

На первом шаге операции чтения или записи программа должна записать в порт 70h номер нужной ячейки CMOS-памяти (0...3Fh). На втором шаге программа должна обратиться к порту 71h для выполнения записи в указанную ячейку памяти или чтения из нее.

Приведем фрагмент программы, составленной на языке ассемблера, который считывает байт из CMOS-памяти с адресом 12h:

mov	al,12h
out	70h,al	; задаем адрес в CMOS-памяти
jmp	$+2	; небольшая задержка
in	al,71h	; записываем в AL считанное значение

Запись в CMOS-память выполняется аналогично.

При анализе конфигурации дисковой системы для нас представляют наибольший интерес ячейки CMOS-памяти со следующими адресами:

  • 14h - байт конфигурации

Биты 7, 6 этого байта имеют такое же значение, что и в младшем байте слова конфигурации, возвращаемого прерыванием INT 11h - они содержат информацию о количестве установленных в компьютере НГМД.

Значение бита 0, равное нулю, говорит о том, что в системе нет ни одного НГМД.

  • 10h - тип НГМД

Младшая и старшая тетрады этого байта описывают, соответственно, второй и первый НГМД:

Значение Емкость, Кбайт Диаметр Количество секторов на одну дорожку Количество дорожек
0000 НГМД не установлен - - -
0001 360 5,25" 9 40
0010 1200 5,25" 15 80
0011 720 3,5" 9 40
0100 1440 3,5" 18 80
  • 12h - тип НМД C: и D:

Этот байт разделен на две тетрады аналогично байту, который описывает НГМД. Однако в тетраде можно закодировать только 16 значений, а различных типов НМД значительно больше. Поэтому тип 15 используется специальным образом - если тип НМД в младшей тетраде (диск C:) равен 15, то правильное значение типа находится в CMOS-памяти по адресу 19h. Аналогично для диска D: этот тип можно взять из байта по адресу 1Ah (если содержимое старшей тетрады байта с адресом 12h равно 15).

Если в вашем компьютере установлен НМД с интерфейсом ESDI , SCSI или другим специализированным интерфейсом, то, как правило, для работы с ними используется специальная "дисковая" базовая система ввода/вывода. Соответствующая микросхема ПЗУ может быть расположена непосредственно в контроллере. При этом в CMOS-памяти в ячейке 12h для типа диска может быть указано нулевое значение, несмотря на то, что диск установлен. Прерывание INT 11h , тем не менее, скажет вам, что в системе имеется НМД.

Если используется "дисковая" базовая система ввода/вывода, то она сама инициализирует таблицу параметров диска (будет описана позже) и выполняет обработку прерывания INT 13h . Так как MS-DOS при обращении к дискам использует именно это прерывание, то не возникает никаких проблем, связанных с отсутствием типа диска в CMOS-памяти. Другие операционные системы, такие как Windows NT и OS/2 , используют для работы с дисками специальные драйверы.

Приведем сокращенную таблицу параметров для стандартных типов НМД (зависит от версии BIOS):

Тип Количество цилиндров Количество головок Емкость диска в байтах
1 306 4 10.653.696
2 615 4 21.411.840
3 615 6 32.117.760
4 940 8 65.454.080
5 940 6 49.090.560
6 615 4 21.411.840
7 462 8 32.169.984
8 733 5 31.900.160
9 900 15 117.504.000
10 820 3 21.411.840
11 855 5 37.209.600
12 855 7 52.093.440
13 306 8 21.307.392
14 733 7 44.660.224
15 0 0 0
16 612 4 21.307.392
17 977 5 42.519.040
18 977 7 59.526.656
19 1024 7 62.390.272
20 733 5 31.900.160
21 733 7 44.660.224
22 733 5 31.900.160
23 306 4 10.653.696
24 977 5 42.519.040
25 1024 9 80.216.064
26 1224 7 74.575.872
27 1224 11 117.190.656
28 1224 15 159.805.440
29 1024 8 71.303.168
30 1024 11 98.041.856
31 918 11 87.892.992
32 925 9 72.460.800
33 1024 10 89.128.960
34 1024 12 106.954.752
35 1024 13 115.867.648
36 1024 14 124.780.544
37 1024 2 17.825.792
38 1024 16 142.606.336
39 918 15 119.854.080
40 820 6 42.823.680

Для всех приведенных в таблице типов дисков на одной дорожке располагается 17 секторов.

Стандартный компьютер IBM PC/XT комплектуется обычно НМД с типом 1, тип 2 используется в стандартном компьютере IBM PC/AT. Остальные типы НМД используются главным образом старыми версиями BIOS.

Таблицы параметров НМД и НГМД

Для работы с диском на физическом уровне необходимо знать такие его характеристики, как количество головок, секторов и др. Эти характеристики можно определить из таблиц параметров НГМД и НМД, заполняемых BIOS в процессе инициализации системы.

Анализируя содержимое CMOS-памяти в компьютерах IBM PC/AT или установку переключателей конфигурации на основной плате в компьютерах IBM PC и IBM PC/XT, BIOS в процессе инициализации создает таблицу параметров дискеты DPT (Diskette Parameter Table ), а также одну или две таблицы параметров жесткого диска HDPT (Hard Disk Parameter Table). Если имеется специальная "дисковая" система ввода/вывода, то она сама создает таблицы HDPT.

Таблица параметров дискеты DPT имеет длину 10 байт, ее адрес располагается в области данных BIOS по адресу 0000h:0078h, что соответствует вектору прерывания INT 1Eh . Таблица содержит следующие параметры:

Смещение, байт Размер, байт Имя поля Описание
0 1 srt_hut Биты 0...3:SRT (Step Rate Time) - задержка для переключения головок, лежит в пределах 1 - 16 мс и задается с интервалом 1 мс (0Fh - 1 мс, 0Eh - 2 мс, 0Dh - 3 мс, ...).Биты 4...7:Задержка разгрузки головки , лежит в пределах 16 - 240 мс и задается с интервалом 16 мс (1 - 16 мс, 2 - 32 мс, ..., 0Fh - 240 mc)
1 1 dma_hlt Бит 0:Значение этого бита, равное 1, говорит о том, что используется прямой доступ к памяти;
Биты 2...7:Время загрузки головок HLT - интервал между сигналом загрузки головок и началом операции чтения или записи, лежит в пределах 2 - 254 мс и задается с интервалом 2 мс (1 - 2 мс, 2 - 4 мс, ..., 0FFh - 254 мс)
2 1 motor_w Задержка перед выключением двигателя
3 1 sec_size Код размера сектора в байтах:0 - 128;1 - 256;2 - 512;3 - 1024
4 1 eot Номер последнего сектора на дорожке
5 1 gap_rw Длина межсекторного промежутка для чтения или записи
6 1 dtl Максимальная длина передаваемых данных. Используется, когда не задана длина сектора
7 1 gap_f Длина межсекторного промежутка для операции форматирования
8 1 fill_char Байт-заполнитель для форматирования, обычно используется F6h
9 1 hst Время установки головки в мс
10 1 mot_start Время запуска двигателя в 1/8 долях секунды

Все времена зависят от частоты тактового генератора контроллера НГМД, приведенные значения соответствуют частоте 8 МГц.

Адреса таблиц параметров жестких дисков HDPT расположены по адресам, соответствующим векторам прерываний INT 41h (для первого физического диска) и INT 46h (для второго физического диска). Эти таблицы имеют следующий формат:

Смещение, байт Размер, байт Имя поля Описание
0 2 max_cyl Максимальное количество дорожек на диске
2 1 max_head Максимальное количество магнитных головок
3 2 srwcc Начальная дорожка для предварительной записи (Starting reduced-write current cylinder)
5 2 swpc Начальная дорожка для предварительной компенсации при записи (Starting write precompensation cylinder)
7 1 max_ecc Максимальная длина блока коррекции ошибок ECC (Maximum ECC data burst length)
8 1 dstopt Параметры устройства:
бит 7 - запрет восстановления;
бит 6 - запрет восстановления по блоку коррекции ошибок ECC (Error Correction Code);
биты 2-0 - дополнительные параметры устройства
9 1 st_del Стандартная величина задержки
10 1 fm_del Величина задержки для форматирования диска
11 1 chk_del Величина задержки для проверки диска
12 4 reserve Зарезервировано

Наиболее полезная информация, которую можно извлечь из таблицы параметров дискеты - это код размера сектора . Если вам когда-либо придется работать с нестандартным размером сектора (отличным от 512 байт), вам не обойтись без этой таблицы.

Таблица параметров жесткого диска содержит такие важнейшие значения, как максимальное количество дорожек и максимальное количество головок. Если вам не удалось определить тип диска, то таблица HDPT - единственное надежное место, откуда можно получить информацию о количестве дорожек и головок.

Программа DISKINFO

Для иллюстрации описанных выше приемов определения конфигурации дисковой системы компьютера приведем исходные тексты программы DISKINFO (листинг 1.1). Она определяет конфигурацию дисковой подсистемы и отображает основные характеристики используемых дисководов. Программа DISKINFO обращается к таблицам параметров НГМД и НМД.

В процессе своей работы программа вызывает функцию disk_cfg, которая заполняет поля структуры DISK_CONFIG сведениями о конфигурации дисковой системы:

Имя поля Описание
n_floppy Количество НГМД, установленных в системе
n_hard Количество НМД, установленных в системе
t_floppy1 Тип первого НГМД
t_floppy2 Тип второго НГМД
t_hard1 Тип первого НМД
t_hard2 Тип второго НМД

Листинг 1.1. Файл diskinfo\diskinfo.cpp


#include <stdio.h>
#include <dos.h>

typedef struct _DISK_CONFIG_
{
  int  n_floppy;
  int  n_hard;
  int  t_floppy1;
  int  t_floppy2;
  int  t_hard1;
  int  t_hard2;
} DISK_CONFIG;

typedef struct _DPT _
{
  unsigned char srt_hut;
  unsigned char dma_hlt;
  unsigned char motor_w;
  unsigned char sec_size;
  unsigned char eot;
  unsigned char gap_rw;
  unsigned char dtl;
  unsigned char gap_f;
  unsigned char fill_char;
 unsigned char hst;
 unsigned char mot_start;
} DPT ;

typedef struct _HDPT _
{
  unsigned max_cyl;
  unsigned char max_head;
  unsigned srwcc;
  unsigned swpc;
  unsigned char max_ecc;
  unsigned char dstopt;
  unsigned char st_del;
  unsigned char fm_del;
  unsigned char chk_del;
  char reserve[4];
} HDPT ;

void disk_cfg(DISK_CONFIG* cfg);
DPT  far *get_dpt(void);
HDPT  far *get_hdp1(void);
HDPT  far *get_hdp2(void);

void main(void)
{
  DISK_CONFIG cfg;
  DPT   far *dpt_ptr;
  HDPT  far *hdpt1_ptr;
  HDPT  far *hdpt2_ptr;

  printf("\n"
    "\nКонфигурация дисковой подсистемы"
    "\n  (C)Фролов А., 1995\n");

  // Определяем конфигурацию дисковой подсистемы
  disk_cfg(&cfg);

  printf("\nУстановлено:"
    "\n   НГМД: %d"
    "\n   НМД:  %d",
    cfg.n_floppy, cfg.n_hard);

  printf("\nТип НГМД:  A: - %d, B: - %d"
         "\nТип НМД:   C: - %d, D: - %d",
    cfg.t_floppy1, cfg.t_floppy2,
    cfg.t_hard1,   cfg.t_hard2);

  // Получаем адрес таблицы параметров дискеты
  dpt_ptr = get_dpt();

  printf("\n"
    "\nКод размера сектора дискеты:           %d"
    "\nЗаполняющий символ для форматирования: %2.2X",
    dpt_ptr->sec_size, dpt_ptr->fill_char);

  // Получаем адреса первой и второй таблицы
  // параметров жесткого диска
  hdpt1_ptr = get_hdp1();
  hdpt2_ptr = get_hdp2();

  printf("\n"
    "\nПараметры первого НМД:"
    "\n   Количество дорожек:       %d"
    "\n   Количество головок:       %d"
    "\n"
    "\nПараметры второго диска:"
    "\n   Количество дорожек:       %d"
    "\n   Количество головок:       %d",
    hdpt1_ptr->max_cyl,  hdpt1_ptr->max_head,
    hdpt2_ptr->max_cyl,  hdpt2_ptr->max_head);
}

/**
* disk_cfg
*
* Определить конфигурацию дисковой подсистемы
*
* Функция заполняет структуру, описывающую
* конфигурацию дисковой подсистемы:
*
* typedef struct _DISK_CONFIG_
* {
*   int  n_floppy;
*   int  n_hard;
*   int  t_floppy1;
*   int  t_floppy2;
*   int  t_hard1;
*   int  t_hard2;
* } DISK_CONFIG;
*
**/

void disk_cfg(DISK_CONFIG* cfg)
{
  char unsigned far *modptr;
  char unsigned pc_type;
  char cfg_byte;
  int  cfg_word;

  union REGS inregs, outregs;

  // Определяем тип компьютера
  modptr = (char unsigned far*)MK_FP(0xf000, 0xfffe);
  pc_type = *modptr;

  // В зависимости от типа компьютера выбираем
  // способ определения конфигурации дисковой
  // подсистемы
  switch (pc_type)
  {
    case 0xfc:

      // Для IBM AT считываем конфигурацию дисковой
      // подсистемы из CMOS-памяти

      // Считываем байт конфигурации
      outp(0x70, 0x14);
      cfg_byte = inp(0x71);

      // Определяем количество установленных НГМД
      if((cfg_byte & 1) == 0)
      {
        // Если младший бит байта конфигурации равен 0,
        // НГМД отсутствуют
        cfg->n_floppy  = 0;
        cfg->t_floppy1 = 0;
        cfg->t_floppy2 = 0;
      }
      else
      {
        // Определяем количество установленных НГМД
        cfg->n_floppy = ((cfg_byte >> 6) & 3) + 1;

        // Определяем типы НГМД
        outp(0x70, 0x10);
        cfg_byte = inp(0x71);

        cfg->t_floppy2 = cfg_byte & 0xf;
        cfg->t_floppy1 = (cfg_byte >> 4) & 0xf;
      }

      // Определяем конфигурацию НМД
      outp(0x70, 0x12);
      cfg_byte = inp(0x71);

      if(cfg_byte == 0)
      {
        // Если обе тетрады равны нулю, система
        // не содержит НМД
        cfg->n_hard  = 0;
        cfg->t_hard1 = 0;
        cfg->t_hard2 = 0;
      }
      else
      {
        // Определяем тип первого диска - диска C:
        if((cfg_byte & 0xf) != 0xf)
          cfg->t_hard1 = cfg_byte & 0xf;
        else
        {
          outp(0x70, 0x19);
          cfg->t_hard1 = inp(0x71);
        }

        // Определяем тип второго диска - диска D:
        if((cfg_byte & 0xf0) != 0xf0)
          cfg->t_hard2 = (cfg_byte >> 4) & 0xf;
        else
        {
          outp(0x70, 0x1a);
          cfg->t_hard2 = inp(0x71);
        }
      }

      // Вычисляем количество НМД, установленных
      // в системе
      cfg->n_hard = 0;
      if(cfg->t_hard1 != 0) cfg->n_hard++;
      if(cfg->t_hard2 != 0) cfg->n_hard++;

      // Для некоторых совместимых с IBM AT машин невозможно
      // определить тип диска, так как в CMOS-памяти для
      // типа диска установлено значение 0, несмотря на то,
      // что диск имеется. В таких случаях можно определить
      // наличие жесткого диска, используя слово
      // конфигурации, возвращаемое прерыванием INT 11h.

      if(cfg->n_hard == 0)
      {
        int86(0x11, &inregs, &outregs);
        cfg_word = outregs.x.ax;

        // Проверяем, есть ли НМД
        if((cfg_word & 1) != 0)
        {
          cfg->n_hard =  1;

          // Считаем, что тип используемого жесткого
          // диска неопределен
          cfg->t_hard1 = 0;
          cfg->t_hard2 = 0;
        }
      }
      break;

    default:

      // Для остальных типов компьютеров вызываем
      // прерывание INT 11h, используем возвращаемый
      // этим прерыванием байт конфигурации
      int86(0x11, &inregs, &outregs);
      cfg_word = outregs.x.ax;

      // Определяем количество установленных
      // НГМД
      cfg->n_floppy = ((cfg_word >> 6) & 3) + 1;

      // Считаем, что тип используемого НГМД
      // неопределен
      cfg->t_floppy1 = 0;
      cfg->t_floppy2 = 0;

      // Определяем наличие НМД
      if((cfg_word & 1) != 0)
      {
        cfg->n_hard =  1;

        // Считаем, что тип используемого НМД
        // неопределен
        cfg->t_hard1 = 0;
        cfg->t_hard2 = 0;
      }
      break;
  }
}

/**
* get_dpt
*
* Вычислить адрес таблицы параметров дискеты
*
* Функция возвращает указатель на таблицу
* параметров дискеты
*
**/

DPT  far *get_dpt(void)
{
  void far * far *ptr;

  ptr = (void far * far *)MK_FP(0x0, 0x78);
  return(DPT  far*)(*ptr);
}

/**
* get_hdp1
*
* Вычислить адрес первой таблицы параметров диска
*
* Функция возвращает указатель на первую таблицу
* параметров диска
*
**/

HDPT  far *get_hdp1(void)
{
  void far * far *ptr;

  ptr = (void far * far *)MK_FP(0x0, 0x104);
  return(HDPT  far*)(*ptr);
}

/**
* get_hdp2
*
* Вычислить адрес второй таблицы параметров диска
*
* Функция возвращает указатель на вторую таблицу
* параметров диска
*
**/

HDPT  far *get_hdp2(void)
{
  void far * far *ptr;

  ptr = (void far * far *)MK_FP(0x0, 0x118);
  return(HDPT  far*)(*ptr);
}

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