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

Операционная система MS-DOS

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

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

1.3. Характеристики дисководов

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

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

FF оригинальный IBM PC
FE XT, Portable PC
FD PCjr
FC AT
FB XT с памятью 640 К на материнской плате
F9 Convertible PC

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

Машины IBM AT (и машины более высокого класса) имеют на материнской плате КМОП-память с малым энергопотреблением и питающуюся от аккумулятора (КМОП - это технология изготовления микросхем - КОМПЛЕМЕНТАРНАЯ пара МЕТАЛЛ-ОКИСЕЛ-ПОЛУПРОВОДНИК). В КМОП-памяти хранится информация о конфигурации дисковой подсистемы, при инициализации BIOS считывает эту информацию и записывает ее в свою внутреннюю область данных.

Для определения модели компьютера мы предлагаем следующую функцию:

/**
*.Name      pc_model
*
*.Title     Определить модель компьютера
*
*.Descr     Функция возвращает байт, идентифицирующий
*           модель персонального компьютера
*
*.Params    Нет
*
*.Return    Код модели персонального компьютера:
*
*              0xff - оригинальный PC;
*              0xfe - XT, Portable PC;
*              0xfd - PCjr;
*              0xfc - AT;
*              0xfb - XT с памятью 640К;
*              0xf9 - Convertible PC.
**/

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

char unsigned pc_model(void) {

        char unsigned _far *modptr;

        modptr = FP_MAKE(0xf000,0xfffe);

        return *modptr;
}


Проанализировав значение, возвращаемое этой функцией, можно сделать предварительное заключение о конфигурации дисковой подсистемы компьютера. Если мы получили значения 0xff, 0xfd, 0xf9, то наш компьютер не имеет НМД - это одна из разновидностей IBM PC. Значения 0xfe, 0xfb могут соответствовать IBM XT и совместимым с ним машинам. Такие машины могут быть оборудованы НМД. И, наконец, значение 0xfc соответствует IBM AT. Для этой машины конфигурация дисковой подсистемы должна определяться исходя из содержимого КМОП-памяти.

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

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

Биты 7 и 6 содержат информацию о количестве флоппи-дисков:

Содержимое битов 7 и 6 Количество установленных флоппи-дисков
00 1
01 2
10 3
11 4

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

КМОП-память не адресуема непосредственно из программы, как обычная оперативная память. Для работы с ней необходимо использовать команды ввода/вывода в порты с адресами 70h и 71h. Перед началом операции чтения/записи в порт 70h надо записать адрес для КМОП-памяти (0...3Fh). Затем из порта 71h можно прочитать содержимое требуемой ячейки КМОП-памяти или записать в этот порт байт, который будет записан в КМОП-память.

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

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

Запись в КМОП-память выполняется аналогично.

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

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

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

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

10h - тип используемых флоппи-дисков

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

0000 дисковод не установлен;
0001 дисковод на 360К;
0010 дисковод на 1,2М.
0011 дисковод на 720К.
0100 дисковод на 1.44М.
12h - тип жестких дисков C: и D:

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

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

Если используется "дисковый" BIOS, то он сам инициализирует таблицу параметров диска и выполняет обработку дискового прерывания INT 13h. Так как MS-DOS для работы использует именно это прерывание, то не возникает никаких проблем, связанных с отсутствием типа диска в КМОП-памяти. Другие операционные системы, такие как XENIX и OS/2, могут использовать для работы с диском собственные драйверы. При установке они могут запрашивать информацию о типе установленного диска.

Если ваша машина содержит дисковый BIOS, то не исключено, что у вас будут проблемы при установке операционных систем XENIX и OS/2. В этом случае необходимо убедиться в том, что устанавливаемая операционная система содержит драйверы для работы с вашим типом диска.

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

Приведем функцию, которая заполнит структуру типа DISK_CONFIG, описанную в файле sysp.h, информацией о конфигурации дисковой подсистемы.

Структура DISK_CONFIG содержит поля:

n_floppy количество установленных в системе НГМД.
n_hard количество установленных жестких НМД.
t_floppy1 тип первого НГМД.
t_floppy2 тип второго НГМД.
t_hard1 тип первого НМД.
t_hard2 тип второго НМД.
/**
*.Name      disk_cfg
*
*.Title     Определить конфигурацию дисковой подсистемы
*
*.Descr     Функция заполняет структуру, описывающую
*           конфигурацию дисковой подсистемы:
*
*              typedef struct _DISK_CONFIG_ {
*                 int  n_floppy;
*                 int  n_hard;
*                 int  t_floppy1;
*                 int  t_floppy2;
*                 int  t_hard1;
*                 int  t_hard2;
*              } DISK_CONFIG;
*
*
*.Params    Нет
*
*.Return    Ничего
**/

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

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 = FP_MAKE(0xf000,0xfffe);
        pc_type = *modptr;

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

        switch (pc_type) {

                case 0xfc:

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

// Считываем байт конфигурации

                        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 машин невозможно
// определить тип диска, так как в КМОП-памяти для
// типа диска установлено значение 0, несмотря на то,
// что диск установлен (например машина Bondwell,
// модель В-300). В таких случаях можно определить
// наличие жесткого диска, используя слово
// конфигурации, возвращаемое прерыванием 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;

        }

}

Пользуясь приведенной выше функцией мы всегда сможем определить количество дисководов для флоппи-дисков и жестких дисков, но не всегда сможем определить их тип. Это само по себе не страшно, так как для работы с дисками на физическом уровне нам надо знать не столько тип диска, сколько другие его характеристики, такие как количество головок, секторов и др. Эти характеристики можно определить из таблиц параметров для дискет и жестких дисков, заполняемых модулями BIOS процессе инициализации системы.

Приведем сокращенную таблицу параметров для стандартных типов жестких дисков, возвращаемых функцией disk_cfg. Информация, которая содержится в этой таблице, используется BIOS процессе инициализации, когда модули 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 XT комплектуется обычно НМД с типом 1, тип 2 используется стандартной IBM AT. Остальные типы НМД поддерживаются не всеми версиями BIOS, например, типы 16...23 поддерживаются BIOS только тех версий, которые были изготовлены не позднее 15/11/85.

Наиболее широко распространены флоппи-диски емкостью 360К, 1.2М, 720К, 1.44М. Их параметры приведены в следующей таблице:

Тип Емкость, Кбайтов Диаметр, дюймы Количество секторов на одну дорожку Количество цилиндров
1 360 5 9 40
2 1200 5 15 80
3 720 3 9 40
4 1440 3 18 80

Тип дискеты в приведенной таблице соответствует возвращаемому функцией disk_cfg.

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

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

(0) 1 srt_hut Биты 0...3 - SRT (Step Rate Time) - задержка для переключения головок, лежит в пределах 1-16 мс и задается с интервалом 1 мс (0Fh - 1mc, 0Eh - 2 mc, 0Dh - 3 mc, ...);
биты 4...7 - задержка разгрузки головки, лежит в пределах 16-240 мс и задается с интервалом 16 мс (1 - 16 mc, 2 - 32 mc, ..., 0Fh - 240 mc).
(+1) 1 dma_hlt Бит 0 - значение этого бита, равное 1, говорит о том, что используется прямой доступ к памяти (DMA);
биты 2...7 - время загрузки головок HLT - интервал между сигналом загрузки головок и началом операции чтение/запись, лежит в пределах 2-254 мс и задается с интервалом 2 мс (1 - 2 mc, 2 - 4 mc, ..., 0FFh - 254 mc).
(+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 МГц.

Для удобства работы с таблицей параметров дискеты файл sysp.h содержит определение типа DPT:

#pragma pack(1)

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;

#pragma pack()

Адреса таблиц параметров жестких дисков 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 Зарезервировано.

Файл sysp.h содержит соответствующее определение типа HDPT:

#pragma pack(1)

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;

#pragma pack()

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

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

Для удобства использования таблиц параметров дискет и дисков мы подготовили следующие функции:

/**
*.Name      get_dpt
*
*.Title     Вычислить адрес таблицы параметров дискеты
*
*.Descr     Функция возвращает указатель на таблицу
*           параметров дискеты
*
*.Params    Нет
*
*.Return    Указатель на таблицу параметров дискеты DPT
**/

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

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

        ptr = (void _far * _far *)FP_MAKE(0x0,0x78);
        return(*ptr);
}
/**
*.Name      get_hdp1
*
*.Title     Вычислить адрес таблицы параметров диска 1
*
*.Descr     Функция возвращает указатель на таблицу
*           параметров диска 1
*
*.Params    Нет
*
*.Return    Указатель на таблицу параметров диска 1 HDPT
**/

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

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

        ptr = (void _far * _far *)FP_MAKE(0x0,0x104);
        return(*ptr);

}
/**
*.Name      get_hdp2
*
*.Title     Вычислить адрес таблицы параметров диска 2
*
*.Descr     Функция возвращает указатель на таблицу
*           параметров диска 2
*
*.Params    Нет
*
*.Return    Указатель на таблицу параметров диска 2 HDPT
**/

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

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

        ptr = (void _far * _far *)FP_MAKE(0x0,0x118);
        return(*ptr);

}

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

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

void main(void);
void main(void) {

  DISK_CONFIG cfg;
  DPT  _far *dpt_ptr;
  HDPT _far *hdpt1_ptr;
  HDPT _far *hdpt2_ptr;

  printf("\n"
                        "\nКонфигурация дисковой подсистемы"
                        "\n  (C)Фролов А., 1991"
                        "\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);

}

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