Если ваша программа работает с устройствами компьютера на низком уровне или использует какие-либо аппаратные особенности периферии, она должна «уметь» определять конфигурацию аппаратных средств. В настоящее время выпускается много различных моделей персональных компьютеров и серверных платформ с процессором Intel, совместимых или не очень с оригинальным компьютером IBM PC/AT. В компьютере могут быть установлены процессоры различных моделей и различные версии BIOS. Что же касается номенклатуры периферийных устройств, таких как сетевые контроллеры, видеоадаптеры, сетевые и звуковые адаптеры, то она практически безгранична.
Для наиболее распространенных моделей персональных компьютеров конфигурация аппаратных средств задается установкой перемычек на системной плате (motherboard) и платах контроллеров периферийных устройств, а также записывается в область данных BIOS и в энергонезависимую память CMOS специальной программой BIOS Setup.
В следующем разделе мы рассмотрим средства BIOS, позволяющие определить конфигурацию компьютера. Об энергонезависимой памяти CMOS мы расскажем позже.
Во время инициализации системы BIOS опрашивает порты, к которым подключены перемычки, и определяет содержимое ячеек памяти CMOS, содержащие информацию о конфигурации компьютера. Результат записывается в область данных BIOS, откуда программа может его извлечь с помощью прерываний BIOS.
Сведения о наличии основных устройств компьютера записывается в область данных BIOS с адресом 0000:0410 размером в двухбайтовое слово - слово конфигурации. С помощью прерывания INT 11h программа может получить в регистре AX слово конфигурации из указанной выше области данных BIOS.
Биты регистра AX |
Содержимое |
0 |
В системе установлен накопитель на магнитном диске (НМД) |
1 |
В системе установлен арифметический сопроцессор |
2 – 3 (все модели, кроме IBM PS/2) |
Количество банков оперативной памяти на системной плате. Для компьютера IBM PC размер одного банка равен 16 Кбайт, для IBM PC/XT – 64 Кбайт. Для определения объема оперативной памяти в более современных компьютерах следует использовать другие средства, о которых мы расскажем позже |
2 (IBM PS/2) |
Компьютер IBM PS/2 оборудован мышью |
3 (IBM PS/2) |
Не используется в IBM PS/2 |
4 – 5 |
Начальный режим видеоадаптера: |
6 – 7 |
Количество установленных накопителей на гибких магнитных дисках (НГМД) |
8 |
Установлен контроллер прямого доступа к памяти DMA |
9 – 11 |
Количество установленных асинхронных последовательных портов |
12 |
Установлен игровой порт |
13 |
Установлен последовательный порт (только для компьютера PCjr) |
14 – 15 |
Количество установленных параллельных адаптеров |
Сейчас уже трудно найти компьютер, в котором не было бы жесткого диска (разве лишь вам встретится бездисковая рабочая станция для локальной сети или сетевой компьютер для Internet). Тем не менее, анализируя нулевой бит слова конфигурации, полученного в регистре AX от прерывания INT 11h, вы можете определить, оборудован ли данный компьютер жестким диском. Если этот бит установлен в единицу, то оборудован, если нет – жесткий диск отсутствует.
До появления процессора i486 арифметический сопроцессор, значительно ускоряющий вычисления с плавающей точкой, был необязательным устройством. На системной плате компьютера для него предусматривалось отдельное гнездо.
Процессор i486 выпускался в двух модификациях – с встроенным арифметическим сопроцессором и без сопроцессора. В современных компьютерах на базе процессора Pentium арифметический сопроцессор присутствует всегда и находится в корпусе центрального процессора.
Анализируя бит с номером 1 в значении, полученном в регистре AX от прерывания INT 11h, вы можете определить факт наличия в системе арифметического сопроцессора. Если сопроцессор установлен, программа может использовать его для выполнения вычислений. Каким именно образом - вы узнаете из главы нашей книги, посвященной этому устройству.
Биты с номерами 4 и 5 слова конфигурации содержат номер начального режима видеоадаптера. В современных компьютерах применяется, как правило, режим с номером 10b - цветной, 80 текстовых строк по 25 символов в каждой строке.
Поле размером два бита с номерами 6 и 7 содержит количество накопителей НГМД, установленных в системе, минус единица. То есть, если в компьютере установлен один НГМД, в этом поле записано нулевое значение, если два – значение 1 и так далее.
Современные модели компьютеров обычно оборудуют одним накопителем НГМД, предназначенным для чтения дискет размером 3,5 дюйма. Что же касается дискет размером 5,25 дюйма, то они уже ушли в прошлое.
Контроллер прямого доступа DMA применяется для непосредственной передачи данных из периферийных устройств в оперативную память компьютера, минуя центральный процессор. Этот контроллер есть во всех современных компьютерах, поэтому бит с номером 8 обычно установлен в единицу.
В поле, образованное битами 9, 10 и 11, хранится количество асинхронных последовательных адаптеров, установленных в системе и обнаруженных BIOS в процессе инициализации.
Если установлен бит с номером 12, к компьютеру подключен игровой адаптер, предназанченный для джойстика.
Маловероятно, что вам попадется в руки антикварный образец компьютера PCjr, но, тем не менее, установленный 13 бит слова конфигурации означает, что этот компьютер оборудован последовательным портом.
В поле, образованное битами с номерами 14 и 15, записывается количество параллельных адаптеров, обнаруженных на этапе инициализации.
В компьютере имеются различные виды оперативной памяти. В первых компьютерах применялся так называемый реальный режим работы процессора, не позволявший адресовать более 1 Мбайт памяти. Начальная область адресов от 0 до 640 Кбайт использовалась для оперативной памяти, а область от 640 Кбайт до 1 Мбайт – для памяти и регистров периферийных устройств, таких как видеоадаптеры и дисковые контроллеры.
Раньше оперативная память была очень дорогая, поэтому далеко не в каждом компьютере было установлен максимально возможный объем – 1 Мбайт. Можно было встретить компьютеры с объемом оперативной памяти, например, 512 Кбайт. Программы в те времена, конечно, тоже были не очень требовательны к объему памяти.
С появлением операционных систем Microsoft Windows и IBM OS/2 требования к объему памяти, установленной в компьютере, резко возросли. Компания Intel выпустила процессор i80286, способный адресовать в защищенном режиме до 16 Мбайт физической памяти.
Теперь память с адресами в пределах первого мегабайта стала называться стандартной, а выше этого предела - расширенной (extended). Размер расширенной памяти определяется во время инициализации компьютера и записывается в память CMOS, откуда ее нетрудно извлечь.
Прерывание INT 12h возвращает в регистре AX размер стандартной оперативной памяти в килобайтах. Заметим, что это значение, хранящееся в области данных BIOS с адресом 0040h:0013h, не превышает 640 Кбайт, даже если в компьютере установлено более 64 Мбайт оперативной памяти.
Функция 88h прерывания INT 15h позволяет определить размер доступной расширенной памяти в килобайтах, возвращая соответствующее значение в регистре AX.
Учтите, что если в системе установлен драйвер расширенной памяти, такой как, например, HIMEM.SYS, указанная выше функция может вернуть нулевое значение. Это происходит из-за того что драйвер берет на себя функции управления расширенной памятью. Заметим также, что с помощью параметра /int15 вы можете указать драйверу HIMEM.SYS размер зарезервированной расширенной памяти, доступной через интерфейс прерывания INT 15h:
device=c:\dos\himem.sys /int15=xxxx
Программа HDWCFG определяет конфигурацию аппаратных средств компьютера, пользуясь для этого описанными выше функциями BIOS. Полученная конфигурация отображается на консоли, как это показано ниже:
HDWCFG (C)A. Frolov, 1997 Configuration word: C823 HDD present NPU present RAM banks: 0 Video Mode: 2 Nubber of FDD: 1 Nubber of COM ports: 2 Number of LPT ports: 3 RAM istalled: 640 Kbytes Extended RAM istalled: 0
Исходный текст программы HDWCFG представлен в листинге 1.1.
Листинг 1.1. Файл hdwcfg\hdwcfg.c
// ===================================================== // Получение информации о конфигурации компьютера // при помощи BIOS // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ===================================================== #include <stdio.h> #include <conio.h> #include <memory.h> #include <dos.h> // Битовые поля слова конфигурации typedef struct _HDWCFG { unsigned HddPresent: 1; // 0 unsigned NpuPresent: 1; // 1 unsigned AmountOfRAM: 2; // 2-3 unsigned VideoMode: 2; // 4-5 unsigned NumberOfFdd: 2; // 6-7 unsigned DmaPresent: 1; // 8 unsigned NumberOfCom: 3; // 9-11 unsigned GamePresent: 1; // 12 unsigned JrComPresent: 1; // 13 unsigned NumberOfLpt: 2; // 14-15 } HDWCFG; int main(void) { union REGS rg; HDWCFG HdwCfg; unsigned uword; printf("\nHDWCFG (C)A. Frolov, 1997"); // Вызываем прерывание INT 11h для получения // слова конфигурации компьютера rg.h.ah = 0x0; int86(0x11, &rg, &rg); // Получаем слово конфигурации и сохраняем // его в структуре HdwCfg uword = (unsigned int)rg.x.ax; memcpy(&HdwCfg, &uword, 2); // Выводим на экран конфигурацию компьютера printf("\n\nConfiguration word: %04.4X", HdwCfg); if(HdwCfg.HddPresent) printf("\nHDD present"); if(HdwCfg.NpuPresent) printf("\nNPU present"); printf("\nRAM banks: %d", HdwCfg.AmountOfRAM); printf("\nVideo Mode: %d", HdwCfg.VideoMode); printf("\nNubber of FDD: %d", HdwCfg.NumberOfFdd + 1); if(HdwCfg.DmaPresent) printf("\nDMA present"); printf("\nNubber of COM ports: %d", HdwCfg.NumberOfCom); if(HdwCfg.GamePresent) printf("\nGame adapter present"); if(HdwCfg.JrComPresent) printf("\nPCjr Com present"); printf("\nNumber of LPT ports: %d", HdwCfg.NumberOfLpt); // Вызываем прерывание INT 12h для определения // объема основной оперативной памяти компьютера rg.h.ah = 0x0; int86(0x12, &rg, &rg); // Выводим объем оперативной памяти printf("\nRAM istalled: %d Kbytes", (unsigned int)rg.x.ax); // Получаем объем расширенной оперативной памяти, // доступной через прерывание INT 15h rg.h.ah = 0x88; int86(0x15, &rg, &rg); // Выводим объем расширенной оперативной памяти printf("\nExtended RAM istalled: %ld Kbytes", (unsigned int)rg.x.ax); getch(); return 0; }
На этапе инициализации BIOS записывает в свою область данных по адресу FFFFh:FFFEh байт идентификатора модели компьютера. Ниже мы привели возможные значения этого байта:
Байт |
Модель компьютера |
PRIVATEFF |
IBM PC |
FE |
IBM XT, Portable PC |
FD |
PCjr |
FC |
IBM PC/AT |
FB |
IBM XT с памятью 640 Кбайт на системной плате |
FA |
IBM PS/2 модель 25 или 30 |
F9 |
Convertible PC |
F8 |
IBM PS/2 модели 55SX, 70, 80 |
9A |
Compaq XT, Compaq Plus |
30 |
Sperry PC |
2D |
Compaq PC |
Более подробную информацию можно получить, вызвав функцию C0h прерывания BIOS INT 15h:
Регистры на входе: |
AH = C0h |
Регистры на выходе: |
ES:BX = адрес таблицы конфигурации, которая находится в ПЗУ BIOS; |
После выполнения прерывания регистры ES:BX будут указывать на таблицу в области ПЗУ BIOS. В этой таблице имеется более точная информация о типе компьютера, номер версии BIOS, сведения об аппаратных особенностях конкретной модели.
Приведем формат указанной таблицы:
Смещение |
Размер, байт |
Описание |
0 |
2 |
Размер таблицы в байтах |
2 |
1 |
Код модели |
3 |
1 |
Дополнительный код модели |
4 |
1 |
Модификация версии BIOS |
5 |
1 |
Байт конфигурации аппаратных средств |
6 |
2 |
Зарезервировано и равно 0 |
8 |
2 |
Зарезервировано и равно 0 |
Ниже мы привели описание отдельных бит байта конфигурации аппаратных средств.
Номер бита |
Описание |
0 |
Зарезервирован |
1 |
Если этот бит установлен в 1, то используется системная шина Micro Channel, в противном случае – шина ISA |
2 |
Применяется расширенная область данных BIOS |
3 |
В BIOS реализована функция ожидания внешнего события |
4 |
Каждый раз после вызова прерывания от клавиатуры INT 9h вызывается функция 4Fh прерывания INT 15h |
5 |
Установлены часы реального времени |
6 |
Установлен второй контроллер прерываний 8259 |
7 |
Канал DMA номер 3 используется BIOS для работы с диском |
В следующей таблице приведены коды моделей, дополнительные коды моделей и версии BIOS для некоторых известных типов компьютеров:
Код модели |
Доп. код модели |
Модификация версии BIOS |
Тип компьютера |
FFh |
- |
- |
Оригинальная версия IBM PC |
FEh |
- |
- |
IBM PC/XT |
FDh |
- |
- |
PCjr |
FCh |
00h |
01h |
IBM PC/AT, модель 239 |
FCh |
01h |
00h |
IBM PC/AT |
FCh |
02h |
00h |
IBM PC/XT-286 |
FCh |
04h |
00h |
IBM PS/2 модель 50 |
FCh |
05h |
00h |
IBM PS/2 модель 60 |
FBh |
00h |
01h |
IBM PC/XT |
FBh |
00h |
02h |
IBM PC/XT |
FAh |
00h |
00h |
IBM PS/2 модель 30 |
FAh |
00h |
01h |
IBM PS/2 модель 30 |
FAh |
01h |
00h |
IBM PS/2 модель 25 |
F9h |
00h |
00h |
PC Convertible |
F8h |
00h |
00h |
IBM PS/2 модель 80 с тактовой частотой 16 Мгц |
F8h |
01h |
00h |
IBM PS/2 модель 80 с тактовой частотой 20 Мгц |
F8h |
04h |
02h, 03h |
IBM PS/2 модель 70 |
F8h |
09h |
02h, 03h |
IBM PS/2 модель 70 |
F8h |
0Ch |
00h |
IBM PS/2 модель 55 SX |
F8h |
1Bhh |
00h |
IBM PS/2 модель 70-486 |
9Ah |
- |
- |
Compaq XT или Compaq Plus |
30h |
- |
- |
Sperry PC |
2Dh |
- |
- |
Compaq PC или Compaq Deskpro |
Символ "-" в приведенной выше таблице означает, что функция C0h прерывания INT 15h для данной версии BIOS не реализована. Все, что вы можете сделать в этом случае для идентификации BIOS, это получить байт кода модели по адресу F000h:FFFEh и дату изготовления BIOS, занимающую восемь байт начиная с адреса F000h:FFF5h. Дата хранится в формате ASCII.
Большинство современных так называемых IBM-совместимых персональных компьютеров имеют код модели FCh, а дополнительный код модели 01h. Такие значения, например, записаны в таблице конфигурации компьютера Compaq Deskpro 6000 с процессором Pentium Pro 200.
Программа BIOSINFO получает и отображает на консоли дату изготовления версии BIOS, а также содержимое таблицы конфигурации, адрес которой определяется с помощью функции C0h прерывания BIOS INT 15h:
BIOSINFO (C)A. Frolov, 1997 BIOS data: 04/18/97 BIOSINFO address: 0212:0190 BIOSINFO Size: 8 Model: FC SubModel: 1 BIOS Revision: 0 Hardvare Cfg: 70 Reserved1: 00 Reserved2: 00 Hardware configuration ---------------------- Second IRQ Controller 8259 Real Time Clock Used function 4Fh INT 15h ISA Bus installed
Исходный текст программы представлен в листинге 1.2.
Листинг 1.2. Файл biosinfo\biosinfo.c
// ===================================================== // Получение информации о BIOS // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или// http://www.dials.ccas.ru/frolov // ===================================================== #include <stdio.h> #include <conio.h> #include <dos.h> // Структура области данных с информацией о BIOS typedef struct _BIOSINFO { int nSize; // размер структуры unsigned char bModel; // код модели компьютера unsigned char bSubModel; // дополнительный код модели unsigned char bBIOSRevision; // номер изменений // версии BIOS unsigned char bHardwareCfg; // конфигурация аппаратуры int reserved1; // зарезервировано int reserved2; // зарезервировано } BIOSINFO; int main(void) { union REGS rg; struct SREGS srg; int i; BIOSINFO far *lpbi; void far* lp; unsigned char bHdwCfg; printf("\nBIOSINFO (C)A. Frolov, 1997"); // Конструируем указатель на дату изготовления // BIOS. Эта дата записана в ПЗУ по адресу F000h:FFF5h _FP_SEG(lp) = 0xf000; _FP_OFF(lp) = 0xfff5; // Выводим дату на экран printf("\n\nBIOS data: "); for(i=0; i<8; i++) putch(*((char far *)lp + i)); // Вызываем функцию C0h для получения адреса // таблицы конфигурации компьютера. rg.h.ah = 0xc0; int86x(0x15, &rg, &rg, &srg); // Если в BIOS нет данной функции, // читаем код модели компьютера // из ПЗУ по адресу F000h:FFFEh if(rg.x.cflag == 1) { printf("\nFunction C0h INT 15h not supported\n"); // Конструируем указатель на код модели _FP_SEG(lp) = 0xf000; _FP_OFF(lp) = 0xfffe; // Выводим код модели компьютера на экран printf("\nModel: %02.2X", (unsigned char)(*(char far *)lp)); return(-1); } // Конструируем укзатель на таблицу // информации о BIOS _FP_SEG(lpbi) = srg.es; _FP_OFF(lpbi) = rg.x.bx; // Выводим на экран содержимое таблицы printf("\nBIOSINFO address: %Fp" "\nBIOSINFO Size: %d" "\nModel: %02.2X" "\nSubModel: %d" "\nBIOS Revision: %d" "\nHardvare Cfg: %02.2X" "\nReserved1: %02.2X" "\nReserved2: %02.2X", lpbi, lpbi->nSize, lpbi->bModel, lpbi->bSubModel, lpbi->bBIOSRevision, lpbi->bHardwareCfg, lpbi->reserved1, lpbi->reserved2); // Определяем конфигурацию компьютера printf("\n\nHardware configuration" "\n----------------------"); // Запоминаем байт конфигурации bHdwCfg = lpbi->bHardwareCfg; // Расшифровываем байт конфигурации if(bHdwCfg & 0x80) printf("\nDMA Channel 3"); if(bHdwCfg & 0x40) printf("\nSecond IRQ Controller 8259"); if(bHdwCfg & 0x20) printf("\nReal Time Clock"); if(bHdwCfg & 0x10) printf("\nUsed function 4Fh INT 15h"); if(bHdwCfg & 0x8) printf("\nBIOS event wait supported"); if(bHdwCfg & 0x4) printf("\nExtended BIOS data used"); if(bHdwCfg & 0x2) printf("\nMicro Channel Bus"); if(!(bHdwCfg & 0x2)) printf("\nISA Bus installed\n"); getch(); return 0; }
Как мы уже говорили, энергонезависимая память CMOS используется в современных компьютерах для хранения текущей конфигурации аппаратных средств. Эта память состоит из набора ячеек, доступ к которым для чтения и записи выполняется через порты ввода и вывода с адресами 70h и 71h.
Процедура чтения ячейки памяти CMOS состоит из двух шагов. На первом шаге программа записывает в выходной порт с адресом 70h номер ячейки, из которой необходимо прочитать информацию. На втором шаге программа читает содержимое данной ячейки из входного порта с адресом 71h:
int nCellContent; outp(0x70, nCell); nCellContent = inp(0x71);
Запись данных в ячейку памяти CMOS выполняется аналогично, только после записи номера ячейки в порт 70h программа должна записать новое значение для этой ячейки в порт с адресом 71h:
outp(0x70, nCell); outp(0x71, nNewValue);
В памяти CMOS хранится текущее время и дата, сведения о конфигурации системы, результат тестирования при включении питания и другая информация, приведенная ниже:
Адрес ячейки |
Содержимое |
00h - 0Dh |
Используются часами реального времени |
0Eh |
Байт состояния диагностики при включении питания |
0Fh |
Байт состояния отключения |
10h |
Тип НГМД |
11h |
Зарезервировано |
12h |
Тип НМД (если меньше 15) |
13h |
Зарезервировано |
14h |
Конфигурация оборудования |
15h - 16h |
Объем основной памяти |
17h - 18h |
Объем расширенной памяти |
19h |
Тип первого НМД (если он больше 15) |
1Ah |
Тип второго НМД (если он больше 15) |
1Bh - 20h |
Зарезервировано |
21h - 2Dh |
Зарезервировано |
2Eh - 2Fh |
Контрольная сумма ячеек 10h - 20h |
30h - 31h |
Объем расширенной памяти |
32h |
Текущее столетие в двоично-десятичном коде (19h для 19-го столетия) |
33h |
Различная информация |
34h - 3Fh |
Зарезервировано |
Рассмотрим подробно назначение отдельных ячеек CMOS-памяти.
Ячейки с адресами 00h - 0Dh используются часами реального времени. Часам реального времени будет посвящена отдельная глава, поэтому сейчас мы не станем останавливаться на этих ячейках.
Байт диагностики (расположенный в памяти CMOS по адресу 0Eh) содержит результаты выполнения диагностики при включении питания компьютера. Выполнив анализ содержимого байта 0Eh, программа может выявить неисправность НМД, часов реального времени, разрядку аккумулятора и ошибки в конфигурации. Приведем формат этого байта:
Бит |
Описание |
0-1 |
Не используется, равно 0 |
2 |
0 - неправильная установка часов реального времени; |
3 |
0 - НМД исправен; |
4 |
0 - размер оперативной памяти указан правильно; |
5 |
0 - конфигурация указана правильно; |
6 |
0 - контрольная сумма памяти CMOS правильная; |
7 |
0 – аккумулятор, питающий память CMOS, исправен и заряжен; |
Байт отключения 0Fh используется процессорами 80286, 80386, 80486 и Pentium для определения способа возврата из защищенного режима в реальный после аппаратного сброса.
Вы, вероятно, знаете, что эти процессоры могут работать либо в реальном режиме, который соответствует режиму работы процессора 8086, либо в защищенном. Защищенный режим работы используется такими операционными системами, как Microsoft Windows, IBM OS/2 и UNIX. В этом режиме процессор может непосредственно адресовать всю память, лежащую выше границы 1 Мбайт.
Подробное рассмотрение защищенного режима работы мы привели в 6 томе «Библиотеки системного программиста», который так и называется – «Защищенный режим процессоров Intel 80286/80386/80486». Здесь же мы только кратко расскажем о переходе процессора 80286 из реального режима в защищенный и обратно для иллюстрации использования ячейки памяти CMOS с адресом 0Fh.
Для перевода процессора 80286 из реального режима в защищенный можно использовать специальную команду LMSW:
mov ax,1 lmsw ax
Разумеется, двух строк, приведенных выше, недостаточно для правильной работы процессора в защищенном режиме.
Для того, чтобы вернуть процессор 80286 из защищенного режима в реальный, необходимо выполнить аппаратный сброс (отключение) процессора. Это можно сделать следующим образом:
mov ax, 0FEh ; команда отключения out 64h, ax
Перед выдачей команды отключения программа должна записать в ячейку 0Fh памяти CMOS причину отключения:
Значение |
Причина отключения |
0 |
Программный сброс при помощи комбинации клавиш <Ctrl-Alt-Delete> или неожиданный сброс. Выполняется обычный перезапуск системы, но процедура тестирования при включении питания не запускается |
1 |
Сброс после определения объема памяти |
2 |
Сброс после тестирования памяти |
3 |
Сброс после обнаружения ошибки четности в оперативной памяти |
4 |
Сброс с запросом перезагрузки |
5 |
После сброса перезапускается контроллер прерываний, затем управление передается по адресу, который находится в области данных BIOS с адресом 0000h:0467h |
6, 7, 8 |
Сброс после выполнения проверки работы процессора в защищенном режиме |
9 |
Сброс после выполнения копирования блока памяти из основной памяти в расширенную |
0Ah |
После сброса управление немедленно передается по адресу, взятому из области данных BIOS с адресом 0000h:0467h |
Для перевода процессоров 80386, 80486 и Pentium из реального режима в защищенный и обратно можно использовать загрузку специального управляющего регистра CR0 обычной командой MOV. Однако будет работать и метод, основанный на применении команды LMSW и команды отключения.
Вы можете использовать сведения о команде отключения для организации программного перезапуска системы.
Младшая и старшая тетрады этого байта описывают, соответственно, второй и первый НГМД:
Значение |
Емкость НГМД |
0000 |
НГМД не установлен |
0001 |
360 Кбайт |
0010 |
1,2 Мбайт |
0011 |
720 Кбайт |
0100 |
1.44 Мбайт |
В компьютерах IBM PS/2 ячейки памяти CMOS с адресами 11h и 12h используются для хранения типа, соответственно, первого и второго НМД.
Этот байт разделен на две тетрады аналогично байту, описывающему НГМД. Однако в тетраде можно закодировать только 16 различных значений, а типов НМД значительно больше. Поэтому тип 15 используется специальным образом - если тип НМД в младшей тетраде (первый физический НМД) равен 15, то правильное значение типа находится в памяти CMOS по адресу 19h. Аналогично для второго физического НМД этот тип можно взять из байта по адресу 1Ah (если старшая тетрада байта с адресом 12h равна 15).
Таблица стандартных типов НМД была приведена в третьей книге первого тома, в разделе, посвященном конфигурации дисковой подсистемы. Кроме того, сведения о типах дисков, задаваемых программой BIOS Setup, обычно есть в документации, поставляемой вместе с компьютером.
В современных компьютерах установлены НМД с интерфейсом IDE или SCSI.
В первом случае тип диска устанавливается равным 47. Это означает. Что фактические параметры диска хранятся в старших ячейках памяти CMOS, номера которых зависят от изготовителя и версии BIOS.
Тип НМД с интерфейсом SCSI задается как 1 или 0. Фактические параметры НМД определяются BIOS, расположенной в контроллере SCSI.
Эта ячейка памяти CMOS зарезервирована.
В этом байте находится информация о количестве установленных НГМД, о наличии арифметического сопроцессора, а также о типе видеоадаптера, подключенного к системе. Приведем формат байта конфигурации:
Бит |
Описание |
0 |
1 - в системе установлены НГМД; |
1 |
1 - установлен арифметический сопроцессор; |
2-3 |
не используются, равны 0 |
4-5 |
Тип видеоадаптера и видеорежим: |
6-7 |
Количество установленных НГМД, уменьшенное на единицу; |
Ячейка 15h содержит младший байт, а ячейка 16h - старший байт объема основной памяти, например:
Ячейки 17h и 18h содержат, соответственно, младший и старший байты размера расширенной памяти (расположенной выше границы 1 Мбайт) в килобайтах.
Эти ячейки содержат типы, соответственно, первого и второго НМД, если соответствующий тип имеет значение, большее 15 (см. описание ячейки 12h).
Эти ячейки памяти CMOS зарезервированы.
Для ячеек памяти CMOS с адресами от 10h до 20h при инициализации системы BIOS выполняет проверку контрольной суммы. Эта контрольная сумма хранится также в памяти CMOS в ячейках 2Eh и 2Fh (соответственно, старший и младший байты).
Ячейки 30h и 31h содержат, соответственно, младший и старший байты размера расширенной памяти (расположенной выше границы 1 Мбайт) в килобайтах.
Эта значение дублирует значение, записанное в ячейках с адресами 17h-18h.
В компьютерах IBM PC/AT этот байт содержит текущее столетие в двоично-десятичном коде, то есть 19 столетие записано как 19h.
Компьютеры модели IBM PS/2 используют эту ячейку вместе с ячейкой 33h для хранения контрольной суммы ячеек с адресами от 10h до 31h. При этом старший байт контрольной суммы хранится в ячейке 32h, а младший - 33h.
В компьютерах IBM PC/AT этот байт используется программой BIOS Setup для собственных нужд.
Это поле зарезервировано, однако вы можете использовать по своему усмотрению, например, хранить здесь пароль.
Назначение описанных выше 64 ячеек памяти CMOS документировано и одинаково для BIOS различных изготовителей и различных версий. Тем не менее, есть исключения.
BIOS компьютера IBM PS/2 использует ячейку с адресом 37h для хранения номера текущего столетия. Ячейки 38h-3Fh в модели 50 компьютера IBM PS/2 используются для хранения пароля. Обращение к этим ячейкам выполняется по адресам 78h-7Fh, которые аппаратно отображаются на адреса 38h-3Fh.
Кроме того, имеются ячейки памяти CMOS с номерами, большими чем 3Fh. Их назначение зависит от изготовителя BIOS, поэтому обращайтесь к ним только в том случае, если у вас есть соответствующая документация.
Программа CMOSSHOW читает в массив первые 64 ячейки памяти CMOS и отображает содержимое некоторых из них:
CMOS Show (C)A. Frolov, 1997 RTC: 22 00 30 00 17 00 03 19 08 97 a6 02 00 Diagnostics byte: 08 Shutdown byte: 00 Reserved: 00 00 00 00 00 00 00 00 00 00 00 00 Extended RAM: 16384 Kbyte
Отображаются ячейки часов реального времени RTC, о которых мы расскажем позже в отдельной главе, диагностический байт и байт отключения, зарезервированные байты. Кроме того, на основании информации, хранящейся в ячейках 17h и 18h программа вычисляет размер расширенной памяти, установленной в компьютере.
Исходный текст программы CMOSSHOW вы найдете в листинге 1.3.
Листинг 1.3. Файл cmosshow\cmosshow.c
// ===================================================== // Чтение и отображение ячеек памяти CMOS // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ===================================================== #include <stdio.h> #include <conio.h> #include <stdlib.h> int main() { unsigned char cmos[64]; int i; unsigned long nExtRam; printf("\nCMOS Show (C)A. Frolov, 1997\n\n"); // Читаем 64 ячейки CMOS-памяти в массив cmos for(i=0; i<64; i++) { outp(0x70,i); cmos[i]=inp(0x71); } // Отображаем ячейки часов реального времени printf("\nRTC: "); for(i=0; i<0xd; i++) { printf("%02.2x ",(unsigned)cmos[i]); } // Отображаем состояние байта диагностики // после включения питания printf("\nDiagnostics byte: %02.2x",cmos[0xe]); // Отображаем содержимое байта отключения printf("\nShutdown byte: %02.2x\n",cmos[0xf]); // Отображаем содержимое зарезервированных ячеек printf("Reserved: "); for(i=0x34; i<0x40; i++) { printf("%02.2x ",(unsigned)cmos[i]); } // Вычисляем объем расширенной памяти и отображаем // его на консоли nExtRam = ((unsigned long)cmos[0x18] << 8) + cmos[0x17]; printf("\nExtended RAM: %ld Kbyte\n", nExtRam); getch(); return 0; }
В некоторых случаях эффективность работы программы можно заметно повысить, если использовать команды новых моделей процессоров Pentium, такие как, например, команды MMX. На сервере Intel с адресом http://www.intel.com вы найдете исчерпывающую информацию о том, как распознать различные модели процессоров, созданных этой фирмой. В нашей книге мы рассмотрим упрощенную методику, которая, тем не менее, может быть использована в большинстве случаев.
Способ распознавания процессоров Intel 8086/8088 основан на том факте, что биты 12-15 регистра FLAGS всегда установлены в единицу.
Прежде всего программа записывает текущее содержимое регистра FLAGS в регистр AX. Для этого используется стек:
pushf pop ax
Первоначальное содержимое регистра FLAGS сохраняется в регистре CX:
mov cx, ax
Далее программа пытается записать нулевые значения в биты 12-15 регистра FLAGS:
and ax, 0fffh push ax popf
Теперь нужно проверить, изменилось ли содержимое указанных битов регистра FLAGS. Для этого новое содержимое регистра FLAGS записывается через стек в регистр AX, а затем, после наложения маски 0f000h, сравнивается со значением 0f000h:
pushf pop ax and ax, 0f000h cmp ax, 0f000h je is_8086
Если биты 12-15 остались установленными в единичное значение, программа работает на процессоре Intel 8086/8088, если нет – в компьютере установлена более старшая модель процессора.
В процессоре Intel 80286, когда он работает в реальном режиме адресации, биты 12-15 регистра FLAGS всегда сброшены в нуль, что можно использовать для обнаружения этой модели процессора.
Следующий фрагмент кода пытается записать в эти биты единичное значение:
mov ax, 0f000h push ax popf
Затем новое содержимое регистра FLAGS переписывается в регистр AX:
pushf pop ax
Если содержимое битов 12-15 равно нулю, программа работает на процессоре Intel 80286:
and ax, 0f000h jz is_80286
В противном случае необходимо продолжить проверку модели процессора.
Для того чтобы отличить процессор Intel 80386 от процессоров старших моделей, можно попробовать установить в регистре EFLAGS бит 18. Этот бит был впервые определен в процессоре Intel 80486 для сигнализации ошибки выравнивания. Его невозможно установить в процессоре Intel 80386.
В процессе проверки программа вначале получает исходное содержимое регистра EFLAGS, записывая его в регистры EAX и ECX:
pushfd pop eax mov ecx, eax
Далее программа инвертирует значение бита 18 и записывает полученный результат в регистр EFLAGS:
xor eax, 40000h push eax popfd
На последнем шаге идентификации новое содержимое регистра EFLAGS извлекается и сравнивается со старым:
pushfd pop eax xor eax, ecx jz is_80386
Если бит 18 не изменил своего значения, мы имеем дело с процессором Intel 80386.
Отличительная особенность процессора Intel 80486 – невозможность изменения состояния бита 21 регистра EFLAGS. Этот бит используется процессорами Intel Pentium и более старшими моделями процессоров Intel для определения возможности вызова команды идентификации процессора CPUID, о которой мы скоро расскажем.
Фрагмент кода, который нужно использовать для обнаружения процессора Intel 80486, аналогичен фрагменту для процессора Intel 80386. Отличие заключается в том, что вместо бита 18 проверяется бит 21:
pushfd pop eax mov ecx, eax xor eax, 200000h push eax popfd pushfd pop eax xor eax, ecx je is_80486
Если же выяснилось, что содержимое бита 21 регистра EFLAGS можно изменять, дальнейшую идентификацию модели процессора следует выполнять с использованием команды CPUID, к описанию которой мы и переходим.
В новых процессорах фирмы Intel появилась команда CPUID, специально предназначенная для определения модели процессора. В своей программе, составленной на языке ассемблера, вы можете определить эту команду следующим образом:
CPU_ID MACRO db 0fh db 0a2h ENDM
Как работает команда CPUID?
Перед вызовом этой команды необходимо загрузить в регистр EAX значение, которое определяет выполняемые действия. В первый раз вы должны вызвать команду CPUID, загрузив предварительно в регистр EAX нулевое значение:
mov eax, 00h CPU_ID
Команда вернет в регистре EAX максимальное значение, которое можно передавать команде CPUID для определения выполняемых действий. Кроме того, в регистрах EBX, ECX и EDX будут находиться байты текстовой строки описания фирмы-изготовителя процессора. Для процессоров Intel это будет строка GenuineIntel.
Следующая последовательность команд перепишет эти байты в буфер _vendor_id_msg в формате, пригодном для вывода на консоль средствами BIOS:
_vendor_id_msg db "............", 0dh, 0ah, "$" . . . mov dword ptr _vendor_id_msg, ebx mov dword ptr _vendor_id_msg[+4], edx mov dword ptr _vendor_id_msg[+8], ecx
Для определения модели процессора следует вызвать команду CPUID, загрузив предварительно в регистр EAX значение 1:
mov eax, 1 CPU_ID
При этом в регистр EAX будет загружено слово сигнатуры, по которому можно определить модель процессора, а в регистр EDX – слово, состоящее из отдельных флагов, характеризующих возможности процессора (feature flags). Регистры EBX и ECX зарезервированы для моделей процессоров, которые будут разработаны в будущем.
Ниже приведен фрагмент кода, в котором слово сигнатуры и слово возможностей записываются в переменные _cpu_signature и _features_edx для дальнейшего анализа:
_cpu_signature dd 0 _features_edx dd 0 . . . mov _cpu_signature, eax mov _features_edx, edx
Рассмотрим формат слова сигнатуры, возвращаемое командой CPUID в регистре EAX:
Биты |
Описание |
0-3 |
Код модификации модели (stepping) |
4-7 |
Код модели |
8-11 |
Код семейства моделей |
12-13 |
Тип процессора |
14-31 |
Зарезервировано |
Слово сигнатуры состоит из отдельных полей, назначение которых будет описано ниже.
Биты 12 и 13 определяют тип процессора:
Значение битов 12 и 13 |
Тип процессора |
00 |
Процессор, изготовленный производителем OEM |
01 |
Процессор OverDrive |
10 |
Процессор типа Dual, который можно использовать в двухпроцессорных системах |
11 |
Зарезервировано |
Заметим, что многопроцессорные системы могут быть построены не только на базе процессоров типа Dual, но и на обычных процессорах.
Ниже мы привели таблицу, с помощью которой по содержимому трех полей слова сигнатуры (тип процессора, код семейства и код модели) можно распознать процессор:
Тип процессора |
Код семейства |
Код модели |
Описание процессора |
00 |
0100 |
0100 |
Intel 486 SL |
00 |
0100 |
0111 |
Intel DX2 |
00 |
0100 |
1000 |
Intel DX4 |
00, 01 |
0100 |
1000 |
Intel DX4 OverDrive |
00 |
0101 |
0001 |
Pentium 60, 66; |
00 |
0101 |
0010 |
Pentium 75, 90, 100, 120, 133, 150, 166, 200 |
01 |
0101 |
0010 |
Pentium OverDrive для процессоров Pentium 75, 90, 100, 120, 133 |
01 |
0101 |
0011 |
Pentium OverDrive для систем на базе процессора Intel 486 |
00 |
0101 |
0100 |
Pentium 166, 200 с командами MMX |
01 |
0101 |
0100 |
Зарезервировано. Будет использоваться процессорами Pentium OverDrive для процессоров Pentium 75, 90, 100, 120, 133 |
00 |
0110 |
0001 |
Pentium Pro |
00 |
0110 |
0011 |
Pentium II |
00 |
0110 |
0101 |
Зарезервировано для новых процессоров P6 |
01 |
0110 |
0011 |
Зарезервировано для процессоров Pentium OverDrive для процессоров Pentium Pro |
Что же касается кода модификации модели (stepping), то он определяет изменения в процессоре. Вы найдете описания этих кодов для процессора Pentium в руководстве Pentium Processor Specification Update, который можно приобрести в фирме Intel.
Приведем описание битов, возвращаемых командой CPUID в регистре EDX. Напомним, что это слово состоит из отдельных флагов, характеризующих возможности процессора (feature flags).
Бит |
Описание |
0 |
На кристалле процессора имеется арифметический сопроцессор, совместимый по командам с сопроцессором Intel 387 |
1 |
Процессор может работать в режиме виртуального процессора 8086 |
2 |
Процессор может работать с прерываниями ввода/вывода, а также с битом DE регистра CR4 |
3 |
Возможно использование страниц памяти размером 4 Мбайт |
4 |
В процессоре есть команда RDTSC, которая может работать с битом TSD регистра CR4 |
5 |
Набор регистров процессора, специфический для модели, доступен с помощью команд RDMSR, WRMSR |
6 |
Возможна физическая адресация памяти с использованием шины с шириной, большей чем 32 разряда |
7 |
В процессоре реализовано исключение Machine Check (исключение с номером 18). Возможно использование бита MCE регистра CR4 |
8 |
В процессоре реализована команда сравнения и обмена 8 байт данных CMPXCHG8 |
9 |
В процессоре есть локальный APIC |
10 |
Зарезервировано |
11 |
В процессоре реализованы команды быстрого вызова системы SYSENTER и SYSEXIT |
12 |
В процессоре есть регистры Memory Type Range |
13 |
Доступен глобальный бит в PDE и PTE, а также бит PGE в регистре CR4 |
14 |
Применена архитектура Machine Check Architecture |
15 |
В процессоре реализованы команды условного перемещения данных CMOVCC и (при установленном бите 0) FCMOVCC и FCOMI |
16-22 |
Зарезервировано |
23 |
Применена технология MMX |
24-31 |
Зарезервировано |
Еще одно применение для команды CPUID – определение характеристик кэша, установленного в процессоре. Для этого перед вызовом команды CPUID в регистр EAX необходимо загрузить значение 2. Подробное изучение этой возможности выходит за рамки нашей книги. При необходимости вы узнать все подробности о команде CPUID на сервере Intel в сети Internet по указанному нами ранее адресу.
Программа CPUINFO определяет модель и характеристики процессора, пользуясь только что описанной нами методикой. Полученная информация выводится на консоль в следующем виде (для процессора Pentium Pro):
*CPU Information*, (C) A. Frolov, 1997 CPU model: 5 Vendor ID: GenuineIntel CPU Signature 00000619 CPU Feature EDX 0000F9FF CPU type: 0 CPU family: 6 CPU model: 1 CPU stepping: 9 FPU detected
В листинге 1.4 вы найдете исходный текст модуля, составленного на языке ассемблера. В этом модуле определены все функции, необходимые для распознавания процессора и получения его характеристик.
Листинг 1.4. Файл cpuinfo\askcpu.asm
; ===================================================== ; Get CPU information ; ; (C) A. Frolov, 1997 ; ; E-mail: frolov@glas.apc.org ; WWW: http://www.glasnet.ru/~frolov ; or ; http://www.dials.ccas.ru/frolov ; ===================================================== .model small CPU_ID MACRO db 0fh db 0a2h ENDM .stack 100h .data public _vendor_id_msg public _cpu_model public _cpu_signature public _features_ecx public _features_edx public _features_ebx public _get_cpu_model _vendor_id_msg db "............", 0dh, 0ah, "$" _cpu_model db 0 _cpu_signature dd 0 _features_ecx dd 0 _features_edx dd 0 _features_ebx dd 0 .code ; ============================================ ; _get_cpu_model ; ============================================ .8086 _get_cpu_model proc call cpu_8086 cmp ax, 0 jz try_80286 mov _cpu_model, 0 jmp end_of_detect try_80286: call cpu_80286 cmp ax, 0 jz try_80386 mov _cpu_model, 2 jmp end_of_detect try_80386: call cpu_80386 cmp ax, 0 jz try_80486 mov _cpu_model, 3 jmp end_of_detect try_80486: call cpu_80486 cmp ax, 0 jz Pent_CPU mov _cpu_model, 4 jmp end_of_detect Pent_CPU: mov _cpu_model, 5 .386 pusha mov eax, 00h CPU_ID mov dword ptr _vendor_id_msg, ebx mov dword ptr _vendor_id_msg[+4], edx mov dword ptr _vendor_id_msg[+8], ecx cmp eax, 1 jl end_of_detect mov eax, 1 CPU_ID mov _cpu_signature, eax mov _features_ebx, ebx mov _features_edx, edx mov _features_ecx, ecx popa end_of_detect: .8086 ret _get_cpu_model endp ; ============================================ ; cpu_8086 ; ============================================ cpu_8086 proc pushf pop ax mov cx, ax and ax, 0fffh push ax popf pushf pop ax and ax, 0f000h cmp ax, 0f000h je is_8086 mov ax, 0 ret is_8086: mov ax, 1 ret cpu_8086 endp ; ============================================ ; cpu_80286 ; ============================================ .286 cpu_80286 proc mov ax, 0f000h push ax popf pushf pop ax and ax, 0f000h jz is_80286 mov ax, 0 ret is_80286: mov ax, 1 ret cpu_80286 endp ; ============================================ ; cpu_80386 ; ============================================ .386 cpu_80386 proc pushfd pop eax mov ecx, eax xor eax, 40000h push eax popfd pushfd pop eax xor eax, ecx jz is_80386 mov ax, 0 ret is_80386: push ecx popfd mov ax, 1 ret cpu_80386 endp ; ============================================ ; cpu_80486 ; ============================================ cpu_80486 proc pushfd pop eax mov ecx, eax xor eax, 200000h push eax popfd pushfd pop eax xor eax, ecx je is_80486 mov ax, 0 ret is_80486: mov ax, 1 ret cpu_80486 endp end
Данный файл был оттранслирован при помощи пакетного файла, представленного в листинге 1.5.
Листинг 1.5. Файл cpuinfo\mk.bat
masm /Zi askcpu.asm,,,,
Главный файл программы приведен в листинге 1.6.
Листинг 1.6. Файл cpuinfo\cpuinfo.c
// ===================================================== // Определение типа процессора // // (C) Фролов А.В, 1997 // // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // ===================================================== #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <memory.h> extern void GET_CPU_MODEL(void); extern char VENDOR_ID_MSG[12]; extern char CPU_MODEL; extern long CPU_SIGNATURE; extern long FEATURES_ECX; extern long FEATURES_EDX; extern long FEATURES_EBX; int main(void) { char buf[128]; printf("*CPU Information*, (C) A. Frolov, 1997\n\n"); GET_CPU_MODEL(); printf("CPU model: %d\n", (unsigned)CPU_MODEL); if(CPU_MODEL >= 5) { memcpy(buf, VENDOR_ID_MSG, 12); buf[12] = 0; printf("Vendor ID: %s\n\n", buf); printf("CPU Signature %08.8X\n", CPU_SIGNATURE); printf("CPU Feature EDX %08.8X\n\n", FEATURES_EDX); printf("CPU type: %d\n", (CPU_SIGNATURE & 0x3000) >> 12); printf("CPU family: %d\n", (CPU_SIGNATURE & 0xF00) >> 8); printf("CPU model: %d\n", (CPU_SIGNATURE & 0xF0) >> 4); printf("CPU stepping: %d\n\n", CPU_SIGNATURE & 0xF); if(FEATURES_EDX & 0x1) printf("FPU detected\n"); if(FEATURES_EDX & 0x800000) printf("MMX supported\n"); } getch(); return 0; }