- 0Dh - область часов реального времени
Ячейки с адресами 00h - 0Dh используются часами реального времени. Часам реального времени будет посвящена отдельная глава, поэтому сейчас мы не станем останавливаться на этих ячейках.
-1Ah типы первого и второго НМД
Эти ячейки содержат типы, соответственно, первого и второго НМД, если соответствующий тип имеет значение, большее 15 (см. описание ячейки 12h).
-3Fh - зарезервировано
Это поле вы можете использовать по своему усмотрению, например, хранить здесь пароль.
PS/2 использует ячейку с адресом 37h для хранения номера текущего столетия. Ячейки 38h-3Fh в модели 50 компьютера PS/2 используются для хранения пароля. Обращение к этим ячейкам выполняется по адресам 78h-7Fh, которые аппаратно отображаются на адреса 38h-3Fh.
Приведем две маленькие программы, демонстрирующие приемы работы с КМОП-памятью. Первая программа записывает в ячейки 34h-3Fh строку символов, вторая отображает эту строку, а также некоторые другие ячейки.
#include <stdio.h> #include <stdlib.h>
main() {
// Эта строка будет записана в КМОП-память
static char password[12] = "!Frolov A.V."; int i,j;
printf("\n*Запись в CMOS* ©Фролов A. 1991\n\n");
for(i=0x34,j=0; i<0x40; i++,j++) {
// Задаем адрес ячейки КМОП-памяти
outp(0x70,i);
// Выполняем запись в эту ячейку
outp(0x71,password[j]); } }
Программа для чтения содержимого КМОП-памяти:
#include <stdio.h> #include <stdlib.h>
main() { unsigned char cmos[164]; int i;
printf("\n*Чтение из CMOS* ©Фролов A. 1991\n\n");
// Читаем все 64 ячейки КМОП-памяти в массив cmos
for(i=0; i<64; i++) { outp(0x70,i); cmos[i]=inp(0x71); }
// Отображаем ячейки часов реального времени
printf("\nЯчейки часов реального времени: ");
for(i=0; i<0xd; i++) { printf("%02.2x ",(unsigned)cmos[i]); }
// Отображаем состояние байта диагностики // после включения питания
printf("\nБайт диагностики: %02.2x",cmos[0xe]);
// Отображаем содержимое байта отключения
printf("\nБайт отключения: %02.2x\n",cmos[0xf]);
// Отображаем содержимое зарезервированных ячеек
printf("\nPassword : "); for(i=0x34; i<0x40; i++) { printf("%02.2x ",(unsigned)cmos[i]); }
// Выводим это же еще раз в виде текстовой строки
cmos[0x40]=0; printf(">%s<\n",&cmos[0x34]); }
Некоторую помощь в определении конфигурации компьютера вам может оказать прерывание BIOS INT11h, которое мы рассмотрим ниже.
-16H - объем основной памяти
Ячейка 15h содержит младший байт, а ячейка 16h - старший байт объема основной памяти. Например:
0100h - 256K
0200h - 512K
0280h - 640K
-18H - объем дополнительной памяти
Ячейки 17h и 18h содержат, соответственно, младший и старший байты размера дополнительной памяти (расположенной выше границы 1 М) в килобайтах.
-31H - объем дополнительной памяти
Ячейки 30h и 31h содержат, соответственно, младший и старший байты размера дополнительной памяти (расположенной выше границы 1 М) в килобайтах.
Эта информация дублирует аналогичную информацию, расположенную в ячейках с адресами 17h-18h.
H-2Dh - зарезервировано
Эти ячейки КМОП-памяти зарезервированы для дальнейшего развития системы.
2Eh-2Fh - контрольная сумма ячеек 10h - 20h
Для ячеек КМОП-памяти с адресами от 10h до 20h при инициализации системы BIOS выполняет проверку контрольной суммы. Эта контрольная сумма хранится также в КМОП-памяти в ячейках 2Eh и 2Fh (соответственно, старший и младший байты).
H - байт состояния диагностики
Байт состояния диагностики (расположенный в КМОП-памяти по адресу 0Eh) содержит результаты выполнения диагностики при включении питания компьютера. Выполнив анализ содержимого байта 0Eh, программа может выявить неисправность НМД, часов реального времени, разрядку аккумулятора и ошибки в конфигурации. Приведем формат этого байта:
Бит Значение
0-1 Не используется, равно 0;
2 0 - неправильная установка часов реального времени; 1 - часы реального времени установлены правильно;
3 1 - неисправность НМД, невозможно загрузить операционную систему с жесткого диска; 0 - НМД исправен;
4 1 - фактический размер оперативной памяти не соответствует указанному в КМОП-памяти; 0 - размер оперативной памяти указан правильно;
5 1 - ошибка в конфигурации системы, фактическая конфигурация не соответствует указанной в байте конфигурации оборудования (адрес 14h); 0 - конфигурация указана правильно;
6 1 - ошибка в контрольной сумме КМОП-памяти; 0 - контрольная сумма КМОП-памяти правильная;
7 1 - разрядка аккумулятора, питающего КМОП-память и часы реального времени; 0 - аккумулятор исправен и заряжен.
H - байт состояния отключения
Байт состояния отключения 0Fh используется процессорами 80286, 80386 и 80486 для определения способа возврата из защищенного режима в реальный после аппаратного сброса.
Вы, вероятно, знаете, что эти процессоры могут работать либо в реальном режиме, который соответствует режиму работы процессора 8086, либо в защищенном. Защищенный режим работы используется такими операционными системами, как OS/2, UNIX, XENIX, а также операционными оболочками WINDOWS/386 и WINDOWS версии 3.0. В этом режиме процессор может непосредственно адресовать всю память, лежащую выше границы 1 мегабайт.
Подробное рассмотрение защищенного режима работы выходит за рамки данной книги. Расскажем кратко о переходе из реального режима в защищенный и обратно для иллюстрации использования ячейки КМОП-памяти с адресом 0Fh.
Для перевода процессора 80286 из реального режима в защищенный можно использовать специальную команду LMSW:
mov ax,1 lmsw ax
Разумеется, двух строк, приведенных выше, недостаточно для правильной работы процессора в защищенном режиме.
Для того, чтобы вернуть процессор 80286 из защищенного режима в реальный, необходимо выполнить аппаратный сброс (отключение) процессора. Это можно сделать следующим образом:
mov ax, 0FEh ; команда отключения out 64h, ax
Перед выдачей команды отключения программа должна записать в ячейку 0Fh КМОП-памяти причину отключения:
Значение Причина отключения
0 Программный сброс при нажатии комбинации клавиш CTRL-ALT-DEL или неожиданный сброс. Выполняется обычный перезапуск системы, но процедуры тестирования при включении питания не выполняются.
1 Сброс после определения объема памяти.
2 Сброс после тестирования памяти.
3 Сброс после обнаружения ошибки в памяти (контроль четности).
4 Сброс с запросом перезагрузки.
5 После сброса перезапускается контроллер прерываний, затем управление передается по адресу, который находится в области данных BIOS 0000:0467h.
6,7,8 Сброс после выполнения теста работы процессора в защищенном режиме.
9 Сброс после выполнения пересылки блока памяти из основной памяти в расширенную.
0Ah После сброса управление немедленно передается по адресу, взятому из области данных BIOS 0000:0467h.
Для перевода процессоров 80386 и 80486 из реального режима в защищенный и обратно можно использовать загрузку специального управляющего регистра CR0 обычной командой MOV. Однако будет работать и метод, основанный на применении команды LMSW и команды отключения.
Вы можете использовать сведения о команде отключения для организации программного перезапуска системы.
Использование BIOS для определения конфигурации
Как мы уже говорили, BIOS в процессе инициализации опрашивает состояние перемычек и анализирует содержимое КМОП-памяти (на тех машинах, где эта память установлена). После анализа BIOS записывает в свою область данных по адресу 0000h:0410h слово конфигурации. Отдельные биты этого слова содержат информацию о наличии в системе различного оборудования. Это слово можно получить с помощью прерывания INT 11h, которое возвращает его в регистре AX.
Приведем назначение отдельных битов слова конфигурации:
Биты Значение
0 1 - система содержит НМД; 0 - система не содержит НМД.
1 1 - установлен арифметический сопроцессор; 0 - арифметический сопроцессор не установлен.
2-3 Объем основной памяти, установленной на материнской плате:
Биты: 3 2
0 1 - 16К; 1 0 - 32К; 1 1 - 64К и более.
4-5 Тип дисплейного контроллера и его режим:
Биты: 5 4
0 0 - не используется или EGA; 0 1 - CGA, EGA, VGA в режиме 40x25; 1 0 - CGA, EGA, VGA в режиме 80x25; 1 1 - монохромный контроллер.
6-7 Количество установленных НГМД:
Биты: 7 6
0 0 - установлен 1 НГМД; 0 1 - установлено 2 НГМД; 1 0 - установлено 3 НГМД; 1 1 - установлено 4 НГМД.
8 1 - используется контроллер прямого доступа к памяти; 0 - контроллер прямого доступа к памяти не используется.
9-11 Количество установленных портов последовательной передачи данных RS232S:
000 - нет портов; 001 - используется один порт; ............. 111 - используется 7 портов.
12 1 - используется игровой адаптер (джойстик); 0 - игровой адаптер не используется.
13 1 - установлен последовательный принтер (только для PC Jr).
14-15 Количество установленных принтеров:
00 - нет принтеров; 01 - используется 1 принтер; 10 - используется 2 принтера; 11 - используется 3 принтера.
КМОП-память и конфигурация компьютера
Назначение некоторых ячеек КМОП-памяти мы уже рассматривали в разделе, посвященному конфигурации дисковой подсистемы. В этом разделе мы расскажем о назначении остальных ячеек.
В КМОП-памяти хранится текущее время и дата, сведения о конфигурации системы, результат тестирования при включении питания и другая информация, приведенная в следующей таблице:
Адрес ячейки Содержимое
00h - 0Dh Используются часами реального времени
0Eh Байт состояния диагностики при включении питания
0Fh Байт состояния отключения
10h Тип используемого НГМД
11h Зарезервировано
12h Тип НМД (если тип меньше 15)
13h Зарезервировано
14h Конфигурация оборудования
15h - 16h Объем основной памяти
17h - 18h Объем расширенной (extended) памяти
19h Тип первого НМД (если тип > 15)
1Ah Тип второго НМД (если тип > 15)
1Bh - 20h Зарезервировано
21h - 2Dh Зарезервировано
2Eh - 2Fh Контрольная сумма ячеек 10h - 20h
30h - 31h Объем расширенной (extended) памяти
32h Текущее столетие в двоично-десятичном коде (19h для 19-го столетия)
33h Различная информация
34h - 3Fh Зарезервировано
Рассмотрим подробно назначение отдельных ячеек КМОП-памяти.
- Конфигурация оборудования
В этом байте находится информация о количестве установленных НГМД, о наличии арифметического сопроцессора 80287 или 80387 и о типе используемого дисплейного контроллера. Приведем формат байта конфигурации:
Бит Значение
0 1 - в системе установлены НГМД; 0 - НГМД не используются;
1 1 - установлен арифметический сопроцессор 80287 или 80387; 0 - арифметический сопроцессор не установлен;
2-3 не используются, равны 0;
4-5 тип дисплейного контроллера и его режим:
Биты: 5 4
0 0 - не используется или EGA; 0 1 - CGA, EGA, VGA в режиме 40x25; 1 0 - CGA, EGA, VGA в режиме 80x25; 1 1 - монохромный контроллер;
6-7 количество используемых НГМД:
Биты: 7 6
0 0 - установлен 1 НГМД 0 1 - установлен 2 НГМД 1 0 - установлен 3 НГМД 1 1 - установлен 4 НГМД
Конфигурация персонального компьютера
1.1.
1.2.
1.3.
1.4.
1.5.
В настоящее время широко используются персональные компьютеры самых разных типов (IBM PC, IBM XT, IBM AT, PS/2, Compaq-386 и т.д.). Если вы создаете коммерческое программное обеспечение, следует позаботиться о том, чтобы оно работало на всех типах компьютеров, использующих процессоры серии Intel 8086/80286/80386/80486. Для обеспечения такой совместимости программа должна уметь определять тип используемого центрального процессора и, разумеется, тип используемого компьютера. Почему это так важно?
Корпорация Intel, разрабатывая свою серию процессоров 8086/80286/80386/80486, уделила много внимания обеспечению совместимости снизу-вверх. Такая совместимость гарантирует возможность непосредственного выполнения старшими моделями процессоров любых программ, подготовленных для младших моделей. При этом не требуется повторной компиляции или редактирования этих программ.
Однако совместимость снизу-вверх не гарантирует (разумеется!) обратного - возможности выполнения младшими моделями программ, предназначенных для старших моделей. Поэтому, если вы желаете добиться от вашей программы максимальной эффективности и быстродействия, имеет смысл использовать архитектурные особенности старших моделей процессоров. Однако это не означает, что ваша программа должна всегда максимально использовать все возможности процессора 80486, так как в этом случае она не сможет правильно работать на широко распространенном компьютере IBM AT, использующим процессор 80286.
Лучше всего было бы сделать так, чтобы программа динамически (в процессе своей работы) определяла тип используемого процессора и вызывала соответствующие модули, рассчитанные на применение конкретной модели процессора. Вы можете выделить эти модули в самостоятельные программные единицы (драйверы или оверлеи) и подгружать их в оперативную память при необходимости. Такой подход позволит вам в будующем легко реализовать возможности процессоров 80586 или 80986 - вам будет достаточно изготовить новый драйвер (или оверлей) и подключить его к уже готовой программе.
Другая проблема связана с большим разнообразием "не вполне" совместимых с IBM PC/XT/AT компьютеров, выпускающихся разными "третьими" фирмами. К этой категории можно также отнести и отечественную ППЭВМ ЕС-1841 - это вроде бы IBM XT, но не вполне, поэтому некоторые программы на этой машине работать не будут.
К счастью, фирмы-производители оборудования записывают в определенные ячейки ПЗУ BIOS некоторый код, по которому можно определить тип компьютера. Можно очень легко отличить IBM XT от IBM AT.
По типу компьютера программа может сделать предварительные, а в некоторых случаях и окончательные выводы о наличии того или иного оборудования. Например, IBM PC или IBM XT не содержат расширенной памяти или дисковода для работы с гибкими дисками диаметром 3,5 дюйма. Разные модели, использующие процессоры 80286/80386/80486 могут использовать различные способы управления двадцатой адресной линией (она используется при работе с расширенной памятью, подробности - в 10 главе). Кроме того, от модели компьютера зависит способ, которым программа может определить конфигурацию дисковой подсистемы. Об этом мы говорили в третьей книге первого тома "Библиотеки системного программиста", посвященной дисковой подсистеме.
Прежде чем использовать какие-либо аппаратные ресурсы компьютера, программа должна убедиться в том, что эти ресурсы имеются в составе системы. Попытка обращения программы к несуществующему устройству может привести, например, к зависанию операционной системы.
Как программа может определить конфигурацию подсистем компьютера?
Для машин класса IBM PC и IBM XT конфигурация задается установкой перемычек на материнской плате и платах контроллеров периферийных устройств. Программа может получить информацию об установленных перемычках, прочитав состояние определенных портов компьютера.
Мы уже говорили о том, что в машинах класса IBM AT, IBM PS/2 и машинах более высокого класса установлена КМОП-память - память с малым энергопотреблением. Эта память питается от аккумуляторов и содержит информацию о конфигурации многих подсистем (в том числе дисковой подсистемы).
Во время инициализации системы BIOS опрашивает порты, к которым подключены перемычки и ячейки КМОП-памяти, содержащие информацию о конфигурации компьютера. Результат записывается в область данных BIOS - в слово конфигурации с адресом 0000:0410.
BIOS также предоставляет программам некоторые средства для определения конфигурации компьютера. В частности, с помощью прерывания INT 11h программа может получить в регистре AX слово конфигурации из области данных BIOS.
Определение типа компьютера и версии BIOS
У вас есть две возможности определить модель компьютера и получить некоторую информацию о конфигурации - прочитать эту информацию из ячеек ПЗУ BIOS, или вызвать одну из функций прерывания INT15h, возвращающую адрес таблицы конфигурации.
ПЗУ BIOS содержит по адресу FFFF:FFFE байт, значение которого можно использовать для идентификации типа компьютера:
FF | оригинальный IBM PC; |
FE | XT, Portable PC; |
FD | PCjr; |
FC | AT; |
FB | XT с памятью 640 К на материнской плате; |
FA | PS/2 модель 25 или 30; |
F9 | Convertible PC; |
F8 | PS/2 модели 55SX, 70, 80; |
9A | Compaq XT, Compaq Plus; |
30 | Sperry PC; |
2D | Compaq PC, Compaq Deskpro |
Для определения модели компьютера таким способом мы предлагаем следующую функцию:
/** *.Name pc_model * *.Title Определить модель компьютера * *.Descr Функция возвращает байт, идентифицирующий * модель персонального компьютера * *.Params Нет * *.Return Код модели персонального компьютера: * * FF оригинальный IBM PC; * FE XT, Portable PC; * FD PCjr; * FC AT; * FB XT с памятью 640 К на материнской плате; * FA PS/2 модель 25 или 30; * F9 Convertible PC; * F8 PS/2 модели 55SX, 70, 80; * 9A Compaq XT, Compaq Plus; * 30 Sperry PC; * 2D Compaq PC, Compaq Deskpro. * **/
#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; }
Функция pc_model() возвращает байт, идентифицирующий код компьютера. В большинстве случаев вам достаточно проверить этот байт и сделать вывод о типе компьютера и составе его аппаратных средств.
Более подробную информацию можно получить, вызвав функцию C0h прерывания BIOS INT 15h:
На входе: AH = C0h
На выходе: ES:BX = адрес таблицы конфигурации, таблица находится в ПЗУ BIOS;
CF = 0 при успешном вызове прерывания; CF = 1 если данная версия BIOS не поддерживает функцию C0h.
После выполнения прерывания регистры ES:BX будут указывать на таблицу в области ПЗУ BIOS. В этой таблице имеется более точная информация о типе компьютера, номер версии BIOS, сведения об аппаратных особенностях конкретной модели.
Приведем формат указанной таблицы:
Смещение Описание и размер
(+0) 2 размер таблицы в байтах
(+2) 1 код модели
(+3) 1 дополнительный код модели
(+4) 1 версия BIOS revision: 0 для первой реализации; 2 для второй и т.д.
(+5) 1 байт конфигурации оборудования:
бит 7 = канал 3 контроллера прямого доступа к памяти используется дисковой системой базового ввода-вывода (дисковой BIOS)
бит 6 = установлен второй контроллер прерываний 8259
бит 5 = установлены часы реального времени
бит 4 = каждый раз после вызова прерывания от клавиатуры INT 9h вызывается функция 4Fh прерывания INT 15h
бит 3 = BIOS поддерживает ожидание внешнего события
бит 2 = используется расширенная область данных BIOS
бит 1 = если этот бит установлен в 1, то используется шина Micro Channel, в противном случае - ISA
бит 0 зарезервирован
(+6) 2 зарезервировано и равно 0 (+8) 2 зарезервировано и равно 0
В следующей таблице приведены коды моделей, дополнительные коды моделей и версии BIOS для некоторых широко распространенных типов компьютеров:
Код Доп.код Версия BIOS Тип компьютера модели модели
FFh * * 04/24/81 оригинальная версия IBM PC
FFh * * 10/19/81 IBM PC, в этой версии BIOS исправлены некоторые ошибки
FFh * * 10/27/82 IBM PC, используется накопитель на магнитном диске (НМД), оперативная память 640 К, поддерживается адаптер дисплея EGA
FEh * * 08/16/82 IBM PC XT
FEh * * 11/08/82 IBM PC XT, Portable
FDh * * 06/01/83 PCjr
FCh * * 01/10/84 IBM AT, модели 068, 099, частота тактового генератора 6 MHz, емкость НМД - 20MB
FCh 00h 01h 06/10/85 IBM AT, модель 239, частота тактового генератора 6 MHz, емкость НМД - 30MB
FCh 01h 00h 11/15/85 IBM AT, модели 319, 339, частота тактового генератора 8 MHz, используются расширенная клавиатура, BIOS может работать с накопителями на гибких магнитных дисках формата 3,5 дюйма
FCh 01h - Compaq 286/386
FCh 02h 00h 04/21/86 IBM PC XT-286
FCh 04h 00h 02/13/87 PS/2 модель 50
FCh 04h 03h 04/18/88 PS/2 модель 50Z
FCh 05h 00h 02/13/87 PS/2 модель 60
FCh 06h - 7552 "Gearbox"
FCh 09h 02h 06/28/89 PS/2 модель 30-286
FCh 81h 00h 01/15/ 88 Phoenix 386 BIOS, версия 1.10
FBh 00h 01h 01/10/86 IBM PC XT, расширенная клавиатура, BIOS может работать с накопителями на гибких магнитных дисках формата 3,5 дюйма
FBh 00h 02h 05/09/86 IBM PC XT
FAh 00h 00h 09/02/86 PS/2 модель 30
FAh 00h 01h 12/12/86 PS/2 модель 30
FAh 01h 00h - PS/2 модель 25
F9h 00h 00h 09/13/85 PC Convertible
F8h 00h 00h 03/30/87 PS/2 модель 80 16MHz
F8h 01h 00h 10/07/87 PS/2 модель 80 20MHz
F8h 04h 02h 04/11/88 PS/2 модель 70
F8h 04h 03h 03/17/89 PS/2 модель 70
F8h 09h - - PS/2 модель 70
F8h 09h 02h 04/11/88 PS/2 модель 70
F8h 09h 03h 03/17/89 PS/2 модель 70
F8h 0Ch 00h 11/02/88 PS/2 модель 55SX
F8h 1Bh 00h 10/02/89 PS/2 модель 70-486
9Ah * * - Compaq XT или Compaq Plus
30h - - - Sperry PC
2Dh * * - Compaq PC или Compaq Deskpro
Следует заметить, что функция C0h прерывания INT 15h поддерживается не всеми версиями BIOS, а только теми, которые были изготовлены после 10 января 1986 года. Если вы используете более старые версии BIOS, дополнительный код модели, версия BIOS и байт конфигурации вам недоступны.
Кроме того, BIOS изготовленный 10 января 1986 года и установленный в IBM XT возвращает неправильное значение байта конфигурации.
Символ "*" в таблице означает, что функция C0h прерывания INT 15h для данной версии BIOS не реализована. Все что вы можете сделать в этом случае для идентификации BIOS - получить байт кода модели по адресу F000h:FFFEh и дату изготовления BIOS, занимающую восемь байтов начиная с адреса F000h:FFF5h. Дата хранится в формате ASCII.
Приведем текст программы, которая поможет вам определить версию BIOS и дату ее изготовления, а также получить всю остальную информацию из таблицы конфигурации. Программа отображает также адрес этой таблицы.
#include <stdio.h> #include <dos.h> #include "sysp.h"
void main(void);
void main(void) {
union REGS rg; struct SREGS srg; int i; BIOSINFO far *biosinf_ptr;
printf("\n*BIOSTEST* Информация о BIOS, " "© Фролов А., 1991");
// Конструируем указатель на дату изготовления // BIOS. Эта дата записана в ПЗУ по адресу F000h:FFF5h.
biosinf_ptr = FP_MAKE(0xf000, 0xfff5);
// Выводим дату на экран
printf("\n\nДата изготовления BIOS: "); for(i=0; i<8; i++) putch(*((char far *)biosinf_ptr + i));
// Вызываем функцию C0h для получения адреса // таблицы конфигурации компьютера.
rg.h.ah = 0xc0; int86x(0x15, &rg, &rg, &srg);
// Если данная функция не поддерживается BIOS, // читаем код модели компьютера из ПЗУ // по адресу F000h:FFFEh.
if(rg.x.cflag == 1) { printf("\nФункция C0h прерывания INT 15h " "данной версией BIOS не поддерживается\n");
// Конструируем указатель на код модели
biosinf_ptr = FP_MAKE(0xf000, 0xfffe);
// Выводим код модели компьютера на экран
printf("\nКод модели: %02.2X", (unsigned char)(*(char far *)biosinf_ptr));
exit(-1); }
// Конструируем укзатель на таблицу конфигурации
biosinf_ptr = FP_MAKE(srg.es, rg.x.bx);
// Выводим на экран содержимое таблицы
printf("\nАдрес таблицы конфигурации: %Fp" "\nРазмер таблицы в байтах: %d" "\nКод модели: %02.2X" "\nДополнительный код модели: %d" "\nВерсия BIOS: %d" "\nКонфигурация оборудования: %02.2X", biosinf_ptr, biosinf_ptr->size, biosinf_ptr->model, biosinf_ptr->submodel, biosinf_ptr->version, biosinf_ptr->hardcfg);
// Определяем конфигурацию компьютера
printf("\n\nКонфигурация оборудования компьютера" "\n------------------------------------");
// Запоминаем байт конфигурации
i = biosinf_ptr->hardcfg;
// Расшифровываем байт конфигурации
if(i & 0x80) printf("\nКанал 3 контроллера DMA используется" " дисковой BIOS");
if(i & 0x40) printf("\nУстановлен второй контроллер" " прерываний 8259");
if(i & 0x20) printf("\nУстановлены часы реального времени");
if(i & 0x10) printf("\ nПосле INT 9h вызывается функция 4Fh" " прерывания INT 15h");
if(i & 0x8) printf("\nBIOS поддерживает функцию ожидания" " внешнего события");
if(i & 0x4) printf("\nИспользуется расширенная" " область данных BIOS");
if(i & 0x2) printf("\nИспользуется шина Micro Channel");
if(!(i & 0x2)) printf("\nИспользуется шина ISA");
exit(0); }
Мы запустили эту программу на компьютере Datamini 386 Tower, и вот что она показала на экране:
02/25/89 *BIOSTEST* Информация о BIOS, © Фролов А., 1991
Дата изготовления BIOS: Адрес таблицы конфигурации: F000:E6F5 Размер таблицы в байтах: 8 Код модели: FC Дополнительный код модели: 1 Версия BIOS: 0 Конфигурация оборудования: 70
Конфигурация оборудования компьютера ------------------------------------ Установлен второй контроллер прерываний 8259 Установлены часы реального времени После INT 9h вызывается функция 4Fh прерывания INT 15h Используется шина ISA
Для изменения конфигурации компьютера (добавления новых устройств или удаления устаревших), может потребоваться изменение установки перемычек на материнской плате компьютера и/или на плате контроллера устанавливаемого устройства. Поэтому теперь мы расскажем об установке перемычек на материнской плате.
Определение типа процессора
Мы уже говорили о том, для чего может потребоваться программе определять тип используемого процессора.
Для определения типа процессора можно использовать следующую программу:
.MODEL tiny .STACK 100h
.DATA
msg1 db "Тип вашего процессора: ", "$" m_8086 db "8086","$" m_80286 db "80286","$" m_80386 db "80386","$"
.CODE .STARTUP
mov ah, 9h mov dx, OFFSET msg1 int 21h
; Записываем 0 в регистр флагов
xor ax,ax push ax popf
; Переписываем регистр флагов через ; стек в регистр AX
pushf pop ax
; Проверяем установку старших четырех битов
and ax,0F000h cmp ax,0F000h
; Если эти биты установлены, программа ; работает на процессоре 8086
je short CPU_8086
; Записываем 0F000h в регистр флагов
mov ax,0F000h push ax popf
; Переписываем регистр флагов через ; стек в регистр AX
pushf pop ax
; Проверяем установку старших четырех битов
and ax,0F000h
; Если эти биты установлены, программа ; работает на процессоре 80286
jz short CPU_80286
; Если биты не установлены, то программа ; работает на процессоре 80386
mov dx, OFFSET m_80386 jmp end_program
CPU_80286: mov dx, OFFSET m_80286 jmp end_program
CPU_8086: mov dx, OFFSET m_8086 end_program:
mov ah, 9h int 21h
.EXIT 0
END
Работа программы основывается на записи в стек слова состояния процессора и последующего извлечения его. При этом проверяется содержимое старших извлеченных из стека битов.
- Различная информация
Для IBM AT этот байт используется программой SETUP.
Текущее столетие
В машинах IBM AT этот байт содержит текущее столетие в двоично-десятичном коде, т.е. 19 столетие записано как 19h.
PS/2 использует эту ячейку вместе с ячейкой 33h для хранения контрольной суммы ячеек с адресами от 10h до 31h. При этом старший байт контрольной суммы хранится в ячейке 32h, а младший - 33h.
- Тип используемых флоппи-дисков;
Младшая и старшая тетрады этого байта описывают соответственно второй и первый НГМД:
0000 - дисковод не установлен;
0001 - дисковод на 360К;
0010 - дисковод на 1,2М;
0011 - дисковод на 720К;
0100 - дисковод на 1.44М.
- Типы первого и второго НМД
Этот байт разделен на две тетрады аналогично байту, описывающему НГМД. Однако в тетраде можно закодировать только 16 различных значений, а типов НМД значительно больше. Поэтому тип 15 используется специальным образом - если тип НМД в младшей тетраде (дискC:) равен 15, то правильное значение типа находится в КМОП-памяти по адресу 19h. Аналогично для диска D: этот тип можно взять из байта по адресу 1Ah (если старшая тетрада байта с адресом 12h равна 15).
Таблица используемых типов дисков была приведена в третьей книге первого тома, в разделе, посвященном конфигурации дисковой подсистемы. Кроме того, сведения о типах дисков, задаваемых программой SETUP, обычно приводятся в документации, поставляемой вместе с компьютером.
Установка перемычек на материнской плате
Для правильной установки перемычек, находящихся на материнской плате и платах контроллеров периферийных устройств вам необходима соответствующая документация. Это руководства пользователя, поставляющиеся вместе с компьютером или контроллерами.
Обозначения перемычек и их назначение различны не только для разных типов компьютеров (PC, XT, AT, PS/2), но и для машин разных фирм-производителей оборудования. Поэтому в этой книге не приводятся конкретные сведения об установке перемычек для оборудования, имеющегося в распоряжении авторов - у вас может оказаться другая модель компьютера.
Вместо этого мы приведем общие сведения об установке перемычек. Пользуясь фирменной документацией, вы найдете нужные перемычки на материнской плате вашего компьютера и сможете их правильно установить.
Оригинальный компьютер IBM PC в его первоначальном виде в настоящее время используется крайне редко, поэтому мы не будем рассказывать о перемычках для IBM PC.
Машина IBM XT имеет один банк перемычек, определяющих количество установленных накопителей на гибких магнитных дисках (НГМД), тип используемого дисплейного адаптера, объем оперативной памяти, установленной на материнской плате, использование арифметического сопроцессора 8087.
Как правило, если вы не меняете конфигурацию системы, у вас не возникает необходимость в изменении установки перемычек. Если же вы добавляете еще один НГМД, изменяете тип дисплейного адаптера или устанавливаете (снимаете) арифметический сопроцессор, вам необходимо переустановить соответствующие перемычки на материнской плате.
Приведем таблицу перемычек банка SW1 для IBM XT (используя эту таблицу, обязательно сверьте ее с приведенной в документации на материнскую плату вашего компьютера):
Номер перемычки Назначение и установка
1 Зацикливание процедуры POST (тест после включения питания). Эта перемычка должна находиться в состоянии OFF.
2 Наличие арифметического сопроцессора 8087:
- 8087 установлен - OFF; - 8087 не установлен - ON.
3-4 Объем оперативной памяти, установленной на материнской плате компьютера:
3 4
OFF ON - 128 K;
ON OFF - 192 K;
OFF OFF - 256 K.
5-6 Тип дисплейного контроллера:
5 6
ON ON - не подключен или EGA;
OFF ON - CGA в режиме 40x25;
ON OFF - CGA в режиме 80x25;
OFF OFF - MDA или два контроллера: MDA и CGA.
7-8 Количество установленных НГМД:
7 8
ON ON - установлен 1 НГМД;
OFF ON - установлено 2 НГМД;
ON OFF - установлено 3 НГМД;
OFF OFF - установлено 4 НГМД.
Если вы изменяете объем оперативной памяти, установленной на материнской плате, вам не надо переустанавливать перемычки 3 и 4. Это связано с тем, что BIOS во время инициализации системы после включения питания сам определяет объем установленной памяти - он сканирует всю имеющуюся память. Объем проверенной памяти обычно отображается во время теста.
Конфигурация компьютера IBM AT определяется, в основном, не установкой перемычек, а содержимым энергонезависимой КМОП-памяти. Мы уже говорили об этом при обсуждении конфигурации дисковой подсистемы.
Для работы с КМОП-памятью BIOS содержит специальную программу, называемую SETUP-программой. Аналогичная программа содержится на диагностической дискете, поставляемой вместе с компьютером.
После перезагрузки или включении питания BIOS обычно предоставляет возможность запустить программу SETUP. Для этого, как правило, надо нажать клавишу DEL во время инициализации системы. Однако некоторые старые версии BIOS для AT не предоставляют возможности запуска программы SETUP при перезапуске системы. При использовании этих версий SETUP запускается сам, если в процессе тестирования обнаружились неисправности в оборудовании. Например, вы отключили накопитель на жестком магнитном диске (НМД) и включили компьютер. Процедуры тестирования, запускаемые в процессе инициализации, обнаружат неисправность в НМД и предоставят вам возможность работать с программой SETUP, находящейся в BIOS.
Другая возможность - загрузиться с диагностической дискеты и запустить программу SETUP, находящуюся на этой дискете.
Перемычка, влияющая на конфигурацию компьютера IBM AT - SW1. Она определяет тип дисплейного контроллера, используемого программой SETUP. Состояние этой перемычки анализируется при разрушении содержимого КМОП-памяти (например, при разряде аккумулятора, питающего КМОП-память и часы реального времени).
Если перемычка SW1 установлена в положение OFF, используется контроллер CGA, в противном случае - MDA, EGA, VGA. Обычно вам не требуется переустанавливать эту перемычку.
- Зарезервировано
Эта ячейка КМОП-памяти зарезервирована для дальнейшего развития системы.
Аппаратное прерывание клавиатуры
Клавиатура подключена к линии прерывания IRQ1. Этой линии соответствует прерывание INT09h.
Клавиатурное прерывание обслуживается модулями BIOS. Драйверы клавиатуры и резидентные программы могут организовывать дополнительную обработку прерывания INT 09h. Для этого может быть использована цепочка обработчиков прерывания. В первой книге первого тома мы приводили примеры расширения обработчика прерывания INT 09h.
Как работает стандартный обработчик клавиатурного прерывания, входящий в состав BIOS?
Этот обработчик выполняет следующие действия:
читает из порта 60h скан-код нажатой клавиши;
записывает вычисленное по скан-коду значение ASCII-кода нажатой клавиши в специальный буфер клавиатуры, расположенный в области данных BIOS;
устанавливает в 1 бит 7 порта 61h, разрешая дальнейшую работу клавиатуры;
возвращает этот бит в исходное состояние;
записывает в порт 20h значение 20h для правильного завершения обработки аппаратного прерывания.
Обработчик прерывания INT 09h не просто записывает значение ASCII-кода в буфер клавиатуры. Дополнительно отслеживаются нажатия таких комбинаций клавиш, как Ctrl-Alt-Del, обрабатываются специальные клавиши PrtSc и SysReq. При вычислении кода ASCII нажатой клавиши учитывается состояние клавиш Shift и CapsLock.
Буфер клавиатуры имеет длину 32 байта и расположен по адресу 0000h:041Eh для машин IBM PC/XT.
В IBM AT и PS/2 расположение клавиатурного буфера задается содержимым двух слов памяти с адресами 0000h:0480h (компонента смещения адреса начала буфера) и 0000h:0482h (смещение конца буфера). Обычно в IBM AT эти ячейки памяти содержат значения, соответственно, 001Eh и 003Eh. Так как смещения заданы относительно сегментного адреса 0040h, то видно, что обычное расположение клавиатурного буфера в IBM AT и PS/2 соответствует его расположению в IBM PC/XT.
Клавиатурный буфер организован циклически. Это означает, что при его переполнении самые старые значения будут потеряны. Две ячейки памяти, находящиеся в области данных BIOS с адресами 0000h:041Ah и 0000h:041Ch содержат, соответственно, указатели на начало и конец буфера. Если значения этих указателей равны друг другу, буфер пуст. (Можно удалить все символы из буфера клавиатуры, установив оба указателя на начало буфера. Однако есть более предпочтительный способ с использованием прерывания BIOS INT 16h).
Указателями на начало и конец клавиатурного буфера обычно управляют обработчики прерываний INT 09h и INT 16h.
Программа извлекает из буфера коды нажатых клавиш, используя различные функции прерывания INT 16h.
Помимо управления содержимым буфера клавиатуры, обработчик прерывания INT 09h отслеживает нажатия на так называемые переключающие клавиши - NumLock, ScrollLock, CapsLock, Ins. Состояние этих клавиш записывается в область данных BIOS в два байта с адресами 0000h:0417h и 0000h:0418h.
Формат байта 0000h:0417h:
Биты Значение
0 Нажата правая клавиша Shift.
1 Нажата левая клавиша Shift.
2 Нажата комбинация клавиш Ctrl-Shift с любой стороны.
3 Нажата комбинация клавиш Alt-Shift с любой стороны.
4 Состояние клавиши ScrollLock.
5 Состояние клавиши NumLock.
6 Состояние клавиши CapsLock.
7 Состояние клавиши Insert.
Формат байта 0000h:0418h:
Биты Значение
0 Нажата левая клавиша Shift вместе с клавишей Ctrl.
1 Нажата левая клавиша Shift вместе с клавишей Alt.
2 Нажата клавиша SysReq.
3 Состояние клавиши Pause.
4 Нажата клавиша ScrollLock.
5 Нажата клавиша NumLock.
6 Нажата клавиша CapsLock.
7 Нажата клавиша Insert.
Если вы изменяете состояние светодиодов на панели клавиатуры, не забывайте устанавливать соответствующие биты в байтах состояния клавиатуры.
Программой обработки прерывания INT 09h отслеживаются некоторые комбинации клавиш. В таблице приведены эти комбинации и действия, выполняемые обработчиком прерывания при их обнаружении:
Комбинация клавиш Выполняемые действия
Ctrl-Alt-Del Сброс и перезагрузка системы.
Ctrl-NumLock, Перевод машины в состояние ожидания Pause до нажатия любой клавиши.
Shift-PrtSc Распечатка на принтере содержимого видеопамяти.
Ctrl-Break Выполнение прерывания INT 1Bh, завершающего работу программы.
Многие типы клавиатур имеют отдельную альтернативную цифровую панель, напоминающую клавиатуру калькулятора. Если одновременно с нажатием на клавишу Alt набрать число на этой панели (не большее, чем 255 и не равное 0), то это число будет помещено в буфер клавиатуры, как будто бы оно было введено нажатием на одну клавишу. Это число будет также записано в слове по адресу 0000h:0419h в области данных BIOS.
При переполнении внутреннего буфера клавиатуры или буфера, расположенного в области данных BIOS, программа-обработчик прерывания INT 09h генерирует звуковой сигнал.
При составлении программ для MS-DOS у вас едва ли появится необходимость непосредственного манипулирования содержимым буфера клавиатуры - вы можете использовать прерывание BIOS INT 16h для выполнения практически всех клавиатурных операций.
В следующем разделе мы займемся непосредственно изучением средств работы с клавиатурой, предоставляемых в распоряжение прерыванием BIOS INT 16h.
Буферизованный ввод без эхо-вывода
На входе: AH = 08h.
На выходе: AL = ASCII-код символа или 0. Если регистр содержит 0, то следующий вызов этой же функции возвратит в регистре AL расширенный ASCII-код символа.
Функция проверяет комбинации клавиш Ctrl-C и Ctrl-Break.
Функция аналогична предыдущей. Она читает символы со стандартного устройства ввода. Если стандартным устройством ввода является клавиатура, и буфер клавиатуры пуст, выполнение программы задерживается до нажатия на любую клавишу.
Эту функцию необходимо использовать в тех случаях, когда не требуется автоматически дублировать на экране вводимые с клавиатуры символы. Например, с ее помощью можно организовать ввод паролей.
Буферизованный ввод с эхо-выводом
На входе: AH = 01h.
На выходе: AL = ASCII-код символа или 0. Если регистр содержит 0, то следующий вызов этой же функции возвратит в регистре AL расширенный ASCII-код символа.
Функция проверяет комбинации клавиш Ctrl-C и Ctrl-Break.
Функция читает символы со стандартного устройства ввода. Если стандартным устройством ввода является клавиатура, и буфер клавиатуры пуст, выполнение программы задерживается до нажатия на любую клавишу.
Введенный символ выводится на стандартное устройство вывода.
Если программа в качестве ASCII-кода получила 0, она должна вызвать эту функцию еще один раз. Во второй раз регистр AL будет содержать расширенный ASCII-код нажатой клавиши.
Чтение символа с ожиданием
Функция 00h выполняет чтение кода символа из буфера клавиатуры, если он там есть. Если буфер клавиатуры пуст, программа переводится в состояние ожидания до тех пор, пока не будет нажата какая-нибудь клавиша. Скан-код и ASCII-код нажатой клавиши передаются программе.
Приведем формат вызова функции:
На входе: AH = 00h
На выходе: AL = ASCII-код символа или 0, если AH содержит расширенный ASCII-код символа;
AH = скан-код или расширенный ASCII-код символа, если AL=0;
Приведем таблицу скан-кодов для клавиатуры IBM PC/XT:
----------T----------T------------T-------------T---------------¬ ¦01 Esc ¦12 E ¦23 H ¦34 . > ¦45 NumLock ¦ ¦02 1 ! ¦13 R ¦24 J ¦35 / ? ¦46 ScrollLock¦ ¦03 2 @ ¦14 T ¦25 K ¦36 Shft(прав)¦47 Home [7] ¦ ¦04 3 # ¦15 Y ¦26 L ¦37 * PrtSc ¦48 Up [8] ¦ ¦05 4 $ ¦16 U ¦27 ; : ¦38 Alt ¦49 PgUp [9] ¦ ¦06 5 % ¦17 I ¦28 " ' ¦39 Пробел ¦4a K - ¦ ¦07 6 ^ ¦18 O ¦29 ` ~ ¦3a CapsLock¦4b <- [4] ¦ ¦08 7 & ¦19 P ¦2a Shft(лев)¦3b F1 ¦4c [5] ¦ ¦09 8 * ¦1a [ { ¦2b \ | ¦3c F2 ¦4d -> [6] ¦ ¦0a 9 ( ¦1b ] } ¦2c Z ¦3d F3 ¦4e K + ¦ ¦0b 0 ) ¦1c Enter¦2d X ¦3e F4 ¦4f End [1] ¦ ¦0c - _ ¦1d Ctrl ¦2e C ¦3f F5 ¦50 Dn [2] ¦ ¦0d + = ¦1e A ¦2f V ¦40 F6 ¦51 PgDn [3] ¦ ¦0e Bksp¦1f S ¦30 B ¦41 F7 ¦52 Ins [0] ¦ ¦0f Tab ¦20 D ¦31 N ¦42 F8 ¦53 Del [.] ¦ ¦10 Q ¦21 F ¦32 M ¦43 F9 ¦ ¦ ¦11 W ¦22 G ¦33 , < ¦44 F10 ¦ ¦ L---------¦----------¦------------¦-------------¦----------------
Для остальных клавиш функция 00h прерывания INT16h возвращает расширенный ASCII-код:
г--------T--------------T--------------T-------------¬ ¦ F1 3b¦ Shift-F1 54¦ Ctrl-F1 5e ¦ Alt-F1 68 ¦ ¦ F2 3c¦ Shift-F2 55¦ Ctrl-F2 5f ¦ Alt-F2 69 ¦ ¦ F3 3d¦ Shift-F3 56¦ Ctrl-F3 60 ¦ Alt-F3 6a ¦ ¦ F4 3e¦ Shift-F4 57¦ Ctrl-F4 61 ¦ Alt-F4 6b ¦ ¦ F5 3f¦ Shift-F5 58¦ Ctrl-F5 62 ¦ Alt-F5 6c ¦ ¦ F6 40¦ Shift-F6 59¦ Ctrl-F6 63 ¦ Alt-F6 6d ¦ ¦ F7 41¦ Shift-F7 5a¦ Ctrl-F7 64 ¦ Alt-F7 6e ¦ ¦ F8 42¦ Shift-F8 5b¦ Ctrl-F8 65 ¦ Alt-F8 6f ¦ ¦ F9 43¦ Shift-F9 5c¦ Ctrl-F9 66 ¦ Alt-F9 70 ¦ ¦ F10 44¦ Shift-F10 5d¦ Ctrl-F10 67 ¦ Alt-F10 71 ¦ L--------¦--------------¦--------------¦--------------
г----------T----------T-------------T-------------¬ ¦ Alt-A 1e¦ Alt-P 19¦ Alt- 3 7a¦ Down Dn 50¦ ¦ Alt-B 30¦ Alt-Q 10¦ Alt-4 7b¦ Left <- 4b¦ ¦ Alt-C 2e¦ Alt-R 13¦ Alt-5 7c¦ Right -> 4d¦ ¦ Alt-D 20¦ Alt-S 1f¦ Alt-6 7d¦ Up Up 48¦ ¦ Alt-E 12¦ Alt-T 14¦ Alt-7 7e¦ End 4f¦ ¦ Alt-F 21¦ Alt-U 16¦ Alt-8 7f¦ Home 47¦ ¦ Alt-G 22¦ Alt-V 2f¦ Alt-9 80¦ PgDn 51¦ ¦ Alt-H 23¦ Alt-W 11¦ Alt-- 82¦ PgUp 49¦ ¦ Alt-I 17¦ Alt-X 2d¦ Alt-= 83¦ ¦ ¦ Alt-J 24¦ Alt-Y 15¦ ¦ ^Left 73¦ ¦ Alt-K 25¦ Alt-Z 2c¦ ¦ ^Right 74¦ ¦ Alt-L 26¦ ¦ Shift-Tab 0f¦ ^End 75¦ ¦ Alt-M 32¦ Alt-0 81¦ Ins 52¦ ^Home 77¦ ¦ Alt-N 31¦ Alt-1 78¦ Del 53¦ ^PgDn 76¦ ¦ Alt-O 18¦ Alt-2 79¦ ^PrtSc 72¦ ^PgUp 84¦ L----------¦----------¦-------------¦--------------
В следующей таблице приведены скан-коды клавиш, имеющихся только на 101-клавишной клавиатуре:
г-------------T------------------T------------------¬ ¦ F11 85¦ Alt-Bksp 0e ¦ Alt- Д / a4¦ ¦ F12 86¦ Alt-Enter 1c ¦ Alt- Д * 37¦ ¦ Shft-F11 87¦ Alt-Esc 01 ¦ Alt- Д - 4a¦ ¦ Shft-F12 88¦ Alt-Tab a5 ¦ Alt- Д + 4e¦ ¦ Ctrl-F11 89¦ Ctrl-Tab 94 ¦ Alt- Д Enter a6¦ ¦ Ctrl-F12 8a¦ ¦ ¦ ¦ Alt-F11 8b¦ Alt-up Up 98 ¦ Ctrl- Д / 95¦ ¦ Alt-F12 8c¦ Alt-down Dn a0 ¦ Ctrl- Д * 96¦ ¦ Alt-[ 1a¦ Alt-left <- 9b ¦ Ctrl- Д - 8e¦ ¦ Alt-] 1b¦ Alt-right -> 9d ¦ Ctrl- Д + 90¦ ¦ Alt-; 27¦ ¦ ¦ ¦ Alt-' 28¦ Alt-Delete a3 ¦ Ctrl- Д Up [8] 8d¦ ¦ Alt-` 29¦ Alt-End 9f ¦ Ctrl- Д 5 [5] 8f¦ ¦ Alt-\ 2b¦ Alt-Home 97 ¦ Ctrl- Д Dn [2] 91¦ ¦ Alt-, 33¦ Alt-Insert a2 ¦ Ctrl- Д Ins[0] 92¦ ¦ Alt-. 34¦ Alt-PageUp 99 ¦ Ctrl- Д Del[.] 93¦ L-------------¦------------------¦-------------------
Буква "Д" в последней таблице обозначает дополнительную ("калькуляторную") клавиатуру.
Для демонстрации использования функции 00h прерывания INT 16h мы подготовили программу, выводящую на экран скан-коды и ASCII-коды нажимаемых клавиш:
#include <stdio.h> #include <dos.h>
void main(void);
void main(void) {
union REGS rg;
printf("\nОпределение скан-кода и ASCII-кода клавиш." "\nДля завершения работы нажмите клавишу ESC.\n\n");
for(;;) {
// Вызываем прерывание INT 16h
rg.h.ah = 0; int86(0x16, &rg, &rg);
// Выводим на экран содержимое регистров AH и AL, // содержащих, соответственно, скан-код и ASCII-код // нажатой клавиши.
printf("\nScan = %02.2X Ascii = %02.2X", rg.h.ah, rg.h.al);
// Если была нажата клавиша ESC, завершаем работу программы
if(rg.h.ah == 1) break;
} }
Чтение символа с ожиданием для 101-клавишной клавиатуры
Функция 10h полностью аналогична функции 00h, но она предназначена для работы с клавиатурой, имеющей 101 клавишу.
Приведем формат вызова функции:
На входе: AH = 10h.
На выходе: AL = ASCII-код символа или 0, если AH содержит расширенный ASCII-код символа;
AH = скан-код или расширенный ASCII-код символа, если AL=0.
Функция определена для BIOS, изготовленной не раньше 15 декабря 1985 года.
КЛАВИАТУРА
2.1.
2.2.
2.3.
2.4.
2.5.
2.6.
В этом разделе мы подробно рассмотрим одно из важнейших устройств персонального компьютера - клавиатуру. Практически ни одна программа не обходится без обращения к клавиатуре.
Программа может использовать клавиатуру по-разному. Она может задержать свое выполнение до тех пор, пока оператор не введет какое-нибудь число или пока не нажмет какую-нибудь клавишу. Выполняя некоторую работу, программа может периодически проверять, не нажал ли оператор на клавишу, изменяющую режим работы программы. Резидентные программы могут контролировать все нажатия на клавиши, активизируясь при нажатии определенной заранее комбинации. Можно использовать прерывание, вырабатываемое клавиатурой, например, для завершения работы программы.
Мы расскажем о работе с клавиатурой на разных уровнях - от использования клавиатурных портов ввода/вывода до средств, предоставляемых стандартными библиотеками трансляторов Microsoft QC 2.5 и C 6.0. Какой уровень вам следует выбрать, зависит от решаемой задачи. Единственное, что можно порекомендовать - это использовать по возможности средства высокого уровня. Если ваша программа работает с клавиатурой на уровне портов ввода/вывода, ее работа может оказаться зависимой от типа клавиатуры и от типа компьютера.
Клавиатурные функции библиотеки Microsoft C
Стандартные библиотеки трансляторов Microsoft QuickC и C 6.0 содержат набор функций, предназначенных для работы с клавиатурой. Эти функции повторяют и немного дополняют возможности функций MS-DOS и BIOS, обслуживающих клавиатуру.
Самые простые из них - getch() и getche(). Они описаны в файле conio.h.
Функция getch() имеет следующий прототип:
int getch(void);
Эта функция возвращает ASCII-код прочитанного из клавиатурного буфера символа, причем прочитанный символ не отображается на экране. Если была нажата функциональная клавиша или клавиша перемещения курсора, функция возвращает 0. В этом случае функцию надо вызвать еще раз для получения расширенного ASCII-кода нажатой клавиши.
Функция обрабатывает клавиши Ctrl-С и Ctrl-Break - при вводе этих комбинаций клавиш работа программы завершается.
Если клавиатурный буфер пуст, программа переводится в состояние ожидания.
Функция getche() полностью аналогична функции getch(), за исключением того, что прочитанный символ отображается на экране. Приведем прототип функции getche():
int getche(void);
Приведем пример программы, отображающей на экране ASCII-коды и расширенные ASCII-коды нажимаемых клавиш:
#include <conio.h> #include <ctype.h> #include <stdio.h>
void main() { int key;
// Читаем в цикле символы с клавиатуры и отображаем // ASCII-коды нажатых клавиш. // Выходим из цикла при нажатии не клавишу ESC
for(;;) {
// Читаем символ
key = getch();
// Если прочитанный символ равен 0, вызываем функцию getch() // для получения расширенного ASCII-кода нажатой клавиши
if( (key == 0) (key == 0xe0) ) { key = getch(); printf( "Расширенный ASCII-код:\t" ); }
else printf( "ASCII-код:\t");
printf("%d\n",key);
// При нажатии на клавишу ESC выходим из цикла
if( key == 27) break; } }
Для проверки буфера клавиатуры на наличие символов можно использовать функцию kbhit(). Она также описана в файле conio.h:
int kbhit(void);
Если буфер клавиатуры не пуст, функция возвращает ненулевое значение. В этом случае программа может прочитать символы из буфера клавиатуры при помощи фукнкций getch() и getche(). Если буфер клавиатуры пуст, функция возвращает нулевое значение.
bufptr = cgets(buf);
// Преобразуем введенное число к формату int // и выводим его
i = atoi(bufptr); printf("\nВы ввели число %d", i); }
Существует и более удобная для использования функция, позволяющая вводить строку с клавиатуры, а точнее, из стандартного потока ввода. Это функция gets():
char *gets(char *buffer);
Функция gets() описана в файле stdio.h.
Эта функция читает строку из стандартного потока ввода stdin и запоминает ее в буфере buffer. Символ новой строки \n в конце введенной строки функция заменяет на ноль.
После завершения ввода функция возвращает указатель на заполненный буфер или NULL в случае ошибки или условия "Конец файла".
Обратим ваше внимание на отличия между функциями cgets() и gets():
Функция cgets() позволяет редактировать вводимую строку символов, функция gets() просто записывает в буфер все символы подряд (в том числе и коды клавиш редактирования).
Программе, использующей для ввода с клавиатуры функцию cgets(), недоступны средства переназначения ввода операционной системы. Если же программа использует функцию gets(), которая читает строку из стандартного потока ввода, можно использовать средства переназначения.
Перед вызовом функции cgets() необходимо специальным образом подготовить буфер для вводимой строки (записать в первый байт буфера длину вводимой строки). Функция gets() не требует никакой подготовки буфера.
Еще одна полезная функция, которую можно использовать для ввода с клавиатуры - scanf(). Эта функция подробно описана во всех книгах по языку программирования Си, поэтому мы не будем ее подробно рассматривать. Отметим только, что с помощью этой функции можно организовать ввод чисел в заданном формате. Однако можно сначала ввести строку при помощи функций cgets() или gets(), а уже потом выполнять все необходимые проверки и преобразования этой строки.
Нефильтрованный ввод без эхо-вывода
На входе: AH = 07h.
На выходе: AL = ASCII-код символа или 0. Если регистр содержит 0, то следующий вызов этой же функции возвратит в регистре AL расширенный ASCII-код символа.
Функция не проверяет комбинации клавиш Ctrl-C и Ctrl-Break.
Если буфер клавиатуры пуст, выполнение программы задерживается до нажатия на любую клавишу.
Эту функцию удобно использовать в тех случаях, когда завершение программы по нажатию комбинаций клавиш Ctrl-C или Ctrl-Break по тем или иным причинам нежелательно. Например, программа держит в оперативной памяти буфера для данных, которые перед завершением работы обязательно должны быть записаны на диск. Если оператор в неподходящий момент времени нажал Ctrl-C и программа аварийно завершила работу, содержимое буферов будет потеряно.
Получение состояния переключающих клавиш
На входе: AH = 02h
На выходе: AL = Байт состояния переключающих клавиш
Функция возвращает в регистре AL состояние переключающих клавиш (Shift, Ctrl, Alt, ScrollLock, NumLock, CapsLock, Ins). Формат байта состояния соответствует формату байта, находящегося в области данных BIOS по адресу 0000h:0417h:
Биты Значение
0 Нажата правая клавиша Shift.
1 Нажата левая клавиша Shift.
2 Нажата комбинация клавиш Ctrl-Shift с любой стороны.
3 Нажата комбинация клавиш Alt-Shift с любой стороны.
4 Состояние клавиши ScrollLock.
5 Состояние клавиши NumLock.
6 Состояние клавиши CapsLock.
7 Состояние клавиши Insert.
Функция может быть использована для анализа текущего состояния переключающих клавиш.
Изменим текст предыдущей программы таким образом, чтобы завершение ее работы происходило лишь в том случае, если переключающая клавиша CapsLock находится в выключенном состоянии (соответствующий светодиод не горит):
#include <stdio.h> #include <dos.h>
void main(void);
void main(void) {
union REGS rg; int i, zflag;
for(;;) {
// Выводим в цикле символ '*'
putchar('*');
// Небольшая задержка во времени
for(i=0; i<1000; i++);
// Вызываем прерывание INT 16h для проверки буфера клавиатуры
// Устанавливаем флаг, который будет сброшен при нажатии на // любую клавишу
zflag = 1;
_asm {
mov ax, 0100h int 16h
// Если нажатия не было, // продолжаем выполнение программы
jz nokey
// При нажатии на любую клавишу // сбрасываем флаг
mov zflag, 0 nokey:
}
if(zflag == 0) {
// Если флаг сброшен, читаем код нажатой клавиши из буфера // при помощи функции 01h прерывания INT 16h
rg.h.ah = 0; int86(0x16, &rg, &rg);
// Если была нажата клавиша ESC, завершаем работу программы, // при условии, что переключатель CapsLock выключен
if(rg.h.ah == 1) {
// Дополнительно проверяем состояние клавиши CapsLock, // этой клавише соответствует бит 0x40 в слове состояния
rg.h.ah = 2; int86(0x16, &rg, &rg);
if((rg.h.al & 0x40) == 0) break;
else printf("\nДля завершения нажмите" " ESC " "при выключенной клавише CapsLock.\n"); } else printf("\nДля завершения нажмите ESC " "при выключенной клавише CapsLock.\n"); } } }
На входе: AH = 12h.
На выходе: AL = Байт состояния переключающих клавиш.
Функция возвращает в регистре AL состояние переключающих клавиш (Shift, Ctrl, Alt, ScrollLock, NumLock, CapsLock, Ins):
Биты Значение
0 Нажата левая клавиша Shift вместе с Ctrl. 1 Нажата левая клавиша Shift вместе с Alt. 2 Нажата правая клавиша Shift вместе с Ctrl. 3 Нажата правая клавиша Shift вместе с Alt. 4 Нажата клавиша ScrollLock. 5 Нажата клавиша NumLock. 6 Нажата клавиша CapsLock. 7 Нажата клавиша SysReq.
Функция 12h аналогична функции 02h, но она предназначена для работы с клавиатурой, имеющей 101 клавишу и имеет другой формат байта состояния.
Эта функция определена для BIOS, изготовленной не раньше 15 декабря 1985 года.
Порты для работы с клавиатурой
Для работы с клавиатурой типа PC/XT используются порты с адресами 60h и 61h.
Порт 60h при чтении содержит скан-код последней нажатой клавиши.
Порт 61h управляет не только клавиатурой, но и другими устройствами компьютера, например, работой встроенного динамика. Этот порт доступен как для чтения, так и для записи. Для нас важен самый старший бит этого порта. Если в старший бит порта 61h записать значение 1, клавиатура будет заблокирована, если 0 - разблокирована.
Так как порт 61h управляет не только клавиатурой, при изменении содержимого старшего бита необходимо сохранить состояние остальных битов этого порта. Для этого можно сначала выполнить чтение содержимого порта в регистр, изменить состояние старшего бита, затем выполнить запись нового значения в порт:
.........
in al, 61h or al, 80h out 61h, al
.........
Компьютер типа IBM AT позволяет управлять скоростными характеристиками клавиатуры, а также зажигать или гасить светодиоды на лицевой панели клавиатуры - Scroll Lock, Num Lock, Caps Lock.
Для расширенного управления клавиатурой используется порт 60h в режиме записи. Этот порт используются для управления подчиненным процессором Intel 8042, ответственным за обмен данными с клавиатурным компьютером.
При использовании порта 60h на запись программа дополнительно получает следующие возможности:
установка времени ожидания перед переходом клавиатуры в режим автоповтора;
установка периода генерации скан-кода в режиме автоповтора;
управление светодиодами, расположенными на лицевой панели клавиатуры - Scroll Lock, Num Lock, Caps Lock.
Процессор 8042 обслуживает не только клавиатуру, но и другие системы компьютера. Через порт 64h, например, выполняется сброс (отключение) процессора 80286 для возврата из защищенного режима работы в реальный.
Для посылки команды процессору 8042 вначале необходимо убедиться в том, что его внутренняя очередь команд пуста. Это можно сделать, прочитав слово состояния 8042 из порта с адресом 64h. Бит с номером 1 должен быть равен нулю.
Приведем фрагмент программы, составленной на языке ассемблера, проверяющий состояние очереди команд процессора 8042:
..........
mov cx,0 ; счетчик для ограничения времени ; ожидания готовности 8042
wait_loop:
; читаем порт состояния процессора 8042
in al,64h and al,00000010b ; флаг готовности
; ожидаем готовность процессора 8042
loopnz wait_loop ; к приему команды ..........
После того, как программа дождется готовности процессора 8042, она может послать ему команду, записав ее в порт с адресом 60h:
..........
mov al,cmd ; команда для 8042 out 60h ,al ; вывод команды в 8042
..........
Некоторые команды состоят более чем из одного байта. Остальные байты команды необходимо записать в порт 60h, предварительно убедившись в готовности процессора 8042 с помощью последовательности команд, приведенной выше. В большинстве случаев можно также использовать простую временную задержку:
..........
mov al, cmd_byte1 out 60h, al
mov cx, 2000h
wait_loop: loop wait_loop
mov al, cmd_byte2 out 60h, al ..........
Мы приведем формат двух команд процессора 8042, имеющих отношение к работе с клавиатурой - команду установки задержки и периода автоповтора и команду управления светодиодами, расположенными на клавиатуре.
Для установки характеристик режима автоповтора в порт 60h необходимо записать код команды 0F3h, затем байт, определяющий характеристики режима:
Биты Значение
0-4 Период автоповтора:
0 - 30.0; 0Ah - 10.0; 1 - 26.7; 0Dh - 9.2; 2 - 24.0; 10h - 7.5; 4 - 20.0; 14h - 5.0; 8 - 15.0; 1Fh - 2.0.
Период автоповтора определяет количество посылок скан-кода, генерируемых процессором клавиатуры в одну секунду. Можно использовать не только те значения, которые приведены выше, но и промежуточные, например, 9 или 15h.
5-6 Задержка включения режима автоповтора:
00 - 250 мс; 01 - 500 мс; 10 - 750 мс; 11 - 1000 мс.
7 Зарезервировано, должно быть равно 0.
Первоначально при инициализации системы период задержки для включения режима автоповтора устанавливается модулями BIOS равным 500 мс при периоде автоповтора, равном 10 повторам в секунду. Если это слишком медленно для вас, вы можете установить другие значения. Некоторые прикладные программы, например, текстовый процессор Microsoft Word, содержат средства для управления временными характеристиками клавиатуры.
Для управления светодиодами, расположенными на лицевой панели клавиатуры, используйте команду 0EDh. Вслед за этой командой в порт 60h необходимо записать байт, имеющий следующий формат:
Биты Значение
0 1 - включить светодиод Scroll Lock;
1 1 - включить светодиод Num Lock;
2 1 - включить светодиод Caps Lock;
3-7 не используются.
Приведем пример простейшей программы, управляющей светодиодами на лицевой панели компьютера. Такое управление может выполняться только при использовании порта 60h управления клавиатурой, так как BIOS не содержит соответствующей поддержки. Наша программа после запуска включит все светодиоды и будет ожидать нажатия на любую клавишу. После нажатия программа выключит светодиоды.
#include <stdio.h>
void main(void);
void main(void) {
int i;
// Посылаем процессору клавиатуры // команду управления светодиодами
outp(0x60,0xed);
// Перед посылкой второго байта команды // выполняем небольшую задержку
for(i=0; i<4000; i++);
// Выводим второй байт команды, // младшие три бита которого определяют // состояние светодиодов на лицевой панели // клавиатуры.
outp(0x60,7);
// Ожидаем нажатия на любую клавишу.
getch();
// Выключаем все светодиоды.
outp(0x60,0xed); for(i=0; i<4000; i++); outp(0x60,0);
exit(0); }
Принципы работы клавиатуры
Клавиатура выполнена, как правило, в виде отдельного устройства, подключаемого к компьютеру тонким кабелем. Малогабаритные компьютеры Lap-Top используют встроенную клавиатуру.
Что же находится внутри клавиатуры? Оказывается, там есть компьютер! Только этот компьютер состоит из одной микросхемы и выполняет специализированные функции. Он отслеживает нажатия на клавиши и посылает номер нажатой клавиши в центральный компьютер.
Если рассмотреть сильно упрощенную принципиальную схему клавиатуры, представленную на рисунке, можно заметить, что все клавиши находятся в узлах матрицы:
Рис.1. Упрощенная схема клавиатуры
Все горизонтальные линии матрицы подключены через резисторы к источнику питания +5 В. Клавиатурный компьютер имеет два порта - выходной и входной. Входной порт подключен к горизонтальным линиям матрицы (X0-X4), а выходной - к вертикальным (Y0-Y5).
Устанавливая по очереди на каждой из вертикальных линий уровень напряжения, соответствующий логическому 0, клавиатурный компьютер опрашивает состояние горизонтальных линий. Если ни одна клавиша не нажата, уровень напряжения на всех горизонтальных линиях соответствует логической 1 (т.к. все эти линии подключены к источнику питания +5 В через резисторы).
Если оператор нажмет на какую-либо клавишу, то соответствующая вертикальная и горизонтальная линии окажутся замкнутыми. Когда на этой вертикальной линии процессор установит значение логического 0, то уровень напряжения на горизонтальной линии также будет соответствовать логическому0.
Как только на одной из горизонтальных линий появится уровень логического 0, клавиатурный процессор фиксирует нажатие на клавишу. Он посылает в центральный компьютер запрос на прерывание и номер клавиши в матрице. Аналогичные действия выполняются и тогда, когда оператор отпускает нажатую ранее клавишу.
Номер клавиши, посылаемый клавиатурным процессором, однозначно связан с распайкой клавиатурной матрицы и не зависит напрямую от обозначений, нанесенных на поверхность клавиш. Этот номер называется скан-кодом (Scan Code).
Слово scan ("сканирование"), подчеркивает тот факт, что клавиатурный компьютер сканирует клавиатуру для поиска нажатой клавиши.
Но программе нужен не порядковый номер нажатой клавиши, а соответствующий обозначению на этой клавише ASCII-код. Этот код не зависит однозначно от скан-кода, т.к. одной и той же клавише могут соответствовать несколько значений ASCII-кода. Это зависит от состояния других клавиш. Например, клавиша с обозначением '1' используется еще и для ввода символа '!' (если она нажата вместе с клавишей SHIFT).
Поэтому все преобразования скан-кода в ASCII-код выполняются программным обеспечением. Как правило, эти преобразования выполняют модули BIOS. Для использования символов кириллицы эти модули расширяются клавиатурными драйверами.
Если нажать на клавишу и не отпускать ее, клавиатура перейдет в режим автоповтора. В этом режиме в центральный компьютер автоматически через некоторый период времени, называемый периодом автоповтора, посылается код нажатой клавиши. Режим автоповтора облегчает ввод с клавиатуры большого количества одинаковых символов.
Следует отметить, что клавиатура содержит внутренний 16-байтовый буфер, через который она осуществляет обмен данными с компьютером.
В настоящее время существует три различных типа клавиатуры. Это клавиатура для компьютеров IBM PC/XT, 84-клавишная клавиатура для IBM AT и 101-клавишная (расширенная) клавиатура для IBM AT. Некоторые клавиатуры имеют переключатель режима работы (XT/AT), расположенный на нижней крышке. Он должен быть установлен в правильное положение.
Проверка буфера на наличие в нем символов
На входе: AH = 01h.
На выходе: ZF = 0, если в буфере имеется код нажатой на клавиатуре клавиши;
ZF = 1, если буфер клавиатуры пуст;
AL = ASCII-код символа или 0, если AH содержит расширенный ASCII-код символа;
AH = скан-код или расширенный ASCII-код символа, если AL=0.
Функция 01h поможет вам проверить состояние буфера клавиатуры - есть там коды нажатых клавиш или нет. При этом программа не переводится в состояние ожидания, даже если буфер клавиатуры пуст. В этом случае в регистре флагов устанавливается в единицу флаг ZF и управление возвращается программе.
Эту функцию удобно использовать во время выполнения какого-либо длительного процесса (например, форматирования диска или передачи данных по линии связи) для прерывания этого процесса по запросу оператора.
Кроме того, функцию можно использовать вместе с функцией 00h для сброса содержимого клавиатурного буфера. Для этого в цикле повторяют вызов функции 01h, вслед за которым идет вызов функции 00h при условии, что буфер клавиатуры не пуст. Сброс клавиатурного буфера полезно выполнять перед вводом ответственной информации, так как из-за случайного двойного или тройного нажатия на клавишу в буфере клавиатуры могут оказаться лишние символы.
Приведем текст программы, выводящей на экран в цикле символ '*'. При нажатии на любую клавишу, кроме ESC, программа выводит на экран строку текста - инструкцию для завершения работы программы. Если нажать на клавишу ESC, работа программы будет завершена.
#include <stdio.h> #include <dos.h>
void main(void);
void main(void) {
union REGS rg; int i, zflag;
for(;;) {
// Выводим в цикле символ '*'
putchar('*');
// Небольшая задержка во времени
for(i=0; i<1000; i++);
// Вызываем прерывание INT 16h для проверки буфера клавиатуры
// Устанавливаем флаг, который будет сброшен при нажатии на // любую клавишу
zflag = 1;
_asm {
mov ax, 0100h int 16h
// Если нажатия не было, // продолжаем выполнение программы
jz nokey
// При нажатии на любую клавишу // сбрасываем флаг
mov zflag, 0 nokey:
}
if(zflag == 0) {
// Если флаг сброшен, читаем код нажатой клавиши из буфера // при помощи функции 01h прерывания INT 16h
rg.h.ah = 0; int86(0x16, &rg, &rg);
// Если была нажата клавиша ESC, завершаем работу программы
if(rg.h.ah == 1) {
// Выводим на экран содержимое регистров AH и AL, // содержащих, соответственно, скан-код и ASCII-код // нажатой клавиши.
printf("\nScan = %02.2X Ascii = %02.2X", rg.h.ah, rg.h.al);
break; } else printf("\nДля завершения нажмите ESC\n"); } } }
Проверка буфера на наличие в нем символов для 101-клавишной клавиатуры
На входе: AH = 11h.
На выходе: ZF = 0, если в буфере имеется код нажатой на клавиатуре клавиши;
ZF = 1, если буфер клавиатуры пуст;
AL = ASCII-код символа или 0, если AH содержит расширенный ASCII-код символа;
AH = скан-код или расширенный ASCII-код символа, если AL=0.
Функция 11h полностью аналогична функции 01h, но она предназначена для работы с клавиатурой, имеющей 101 клавишу.
Эта функция определена для BIOS, изготовленной не раньше 15 декабря 1985 года.
Проверка состояния стандартного ввода
На входе: AH = 0Bh.
На выходе: AL = 0FFh, если в буфере имеется код нажатой на клавиатуре клавиши;
AL = 0, если буфер клавиатуры пуст;
Функция проверяет комбинации клавиш Ctrl-C и Ctrl-Break.
Эта функция проверяет состояние клавиатурного буфера. Вы можете вызывать ее перед функциями 01h, 07h, 08h для того, чтобы избежать ожидания нажатия на клавишу.
Если ваша программа выполняет какую-либо длительную обработку (копирование файлов, форматирование дисков и т.п.), вы можете вызывать эту функцию в процессе обработки для проверки нажатия комбинации клавиш, прерывающих работу программы.
Сброс буфера клавиатуры
На входе: AH = 0Ch;
AL = 1, 6, 7, 8 или 0Ah.
На выходе: не опеределены.
Функция очищает клавиатурный буфер, затем вызывает клавиатурную функцию MS-DOS, номер которой определяется содержимым регистра AL. Если же регистр AL содержит другое значение, кроме приведенных выше, функция просто сбрасывает содержимое буфера и не выполняет никаких других действий.
Эту функцию удобно использовать тогда, когда перед вводом символа необходимо убедиться в том, что буфер клавиатуры пуст.
Средства BIOS для работы с клавиатурой
Набор функций для работы с клавиатурой, предоставляемый в распоряжение программиста прерыванием BIOS INT 16h, включает в себя функции для выборки кода нажатого символа из буфера с ожиданием нажатия, функции для проверки содержимого буфера и для управления содержимым буфера, функции для изменения скоростных характеристик клавиатуры.
2.4.1.
2.4.2.
2.4.3.
2.4.4.
2.4.5.
2.4.6.
2.4.7.
2.4.8.
Средства MS-DOS для работы с клавиатурой
К сожалению, MS-DOS не предоставляет программам каких-либо существенных дополнительных возможностей по сравнению с функциями прерывания BIOS INT16h. Поэтому многие программы работают с клавиатурой через BIOS.
Однако, если ваша программа пользуется клавиатурными функциями MS-DOS, то ей доступно средство переназначения ввода операционной системы. Это возможно благодаря тому, что клавиатурные функции MS-DOS являются функциями, работающими со стандартным вводом MS-DOS, а стандартный ввод может быть переназначен.
Кроме того, некоторые клавиатурные функции автоматически посылают введенные символы на устройство стандартного вывода. По умолчанию это дисплей, но устройство стандартного вывода может быть переназначено для вывода в файл, на принтер или другое устройство.
Вообще говоря, клавиатурные функции MS-DOS больше всего подходят для тех программ, которые ведут с оператором "построчный" диалог. Для таких программ при использовании средств переназначения ввода/вывода возможна организация автоматического "пакетного" выполнения, когда все сообщения выводятся в файл, а все данные, которые обычно вводятся с клавиатуры, считываются из заранее подготовленного файла "ответов".
Некоторые клавиатурные функции MS-DOS отслеживают комбинации клавиш Ctrl-C и Ctrl-Break. Если оператор ввел такую комбинацию клавиш, вызывается прерывание INT 23h, завершающее работу текущей программы. Если ваша программа не должна завершаться при нажатии этих комбинаций клавиш, можно либо создать и подключить собственный обработчик для INT 23h, либо использовать те клавиатурные функции MS-DOS, которые не выполняют проверку указанных выше комбинаций клавиш.
Приведем подробное описание клавиатурных функций прерывания MS-DOS INT 21h.
2.5.1.
2.5.2.
2.5.3.
2.5.4.
2.5.5.
2.5.6.
2.5.7.
Установка временных характеристик клавиатуры
На входе: AH = 03h;
AL = 05h;
BL = Период автоповтора (количество повторов за одну секунду):
0 - 30.0; 0Ah - 10.0; 1 - 26.7; 0Dh - 9.2; 2 - 24.0; 10h - 7.5; 4 - 20.0; 14h - 5.0; 8 - 15.0; 1Fh - 2.0.
BH = Задержка включения режима автоповтора:
0 - 250 мс; 1 - 500 мс; 2 - 750 мс; 3 - 1000 мс.
На выходе: Не используются.
Мы уже рассказывали о возможности изменения временных характеристик клавиатуры. Если BIOS, установленная в вашей машине, изготовлена после 15 декабря 1985 года, вы можете воспользоваться этой функцией для ускорения (или замедления) работы клавиатуры.
В качестве примера приведем две программы. Первая программа увеличивает быстродействие клавиатуры до его верхнего предела, вторая восстанавливает исходные значения временных характеристик.
#include <stdio.h> #include <dos.h>
void main(void);
void main(void) {
union REGS rg;
rg.h.al = 5; rg.h.ah = 3;
// Устанавливаем максимальное быстродействие клавиатуры
rg.h.bl = 0; rg.h.bh = 0;
int86(0x16, &rg, &rg);
} #include <stdio.h> #include <dos.h>
void main(void);
void main(void) {
union REGS rg;
rg.h.al = 5; rg.h.ah = 3;
// Восстанавливаем исходное быстродействие клавиатуры
rg.h.bl = 0xa; rg.h.bh = 1;
int86(0x16, &rg, &rg);
}
Ввод строки символов
На входе: AH = 0Ah;
DS:DX = адрес буфера для ввода строки;
На выходе: Буфер содержит введенную строку.
Функция проверяет комбинации клавиш Ctrl-C и Ctrl-Break.
Функция предназначена для ввода с клавиатуры строки символов. Перед вызовом функции необходимо специальным образом подготовить буфер, адрес которого передается в регистрах DS:DX - в первый байт буфера следует записать максимальную длину вводимой строки (в диапазоне от 1 до244):
----T---T---T---T---T---T- - - ¦max¦ ? ¦ ? ? ? ? ? L-T-+---+---+---+---+---+ - - ¦ L----> максимальная длина вводимой строки
После возврата из функции буфер будет иметь следующий формат:
----T---T---T---T---T---T- - - ¦max¦len¦ T E X T 0Dh L-T-+-T-+---+---+---+---+ - - ¦ ¦ ¦ L------- длина введенной строки (без учета ¦ завершающего символа CR) L----------- остается без изменений
Ввод осуществляется до тех пор, пока либо количество введенных символов не достигнет max-1, либо пока не будет нажата клавиша Enter (код 0Dh). Если оператор уже ввел max-1 символ и продолжает вводить символы дальше, функция выдает звуковой сигнал на каждое нажатие и игнорирует вводимые символы до тех пор, пока не будет нажата клавиша Enter.
При вводе строки можно использовать стандартные средства редактирования MS-DOS, используемые при вводе команд в режиме командной строки.
Ввод/вывод на консоль
На входе: AH = 06h;
DL = 0FFh - для ввода символа с консоли; или: DL = код символа, не равный 0FFh - для вывода символа на консоль.
На выходе: ZF = 0, если в буфере имеется код нажатой на клавиатуре клавиши;
ZF = 1, если буфер клавиатуры пуст;
AL = ASCII-код символа или 0, если AH содержит расширенный ASCII-код символа.
Функция проверяет комбинации клавиш Ctrl-C и Ctrl-Break.
Функция 06h может использоваться как для ввода с консоли, так и для вывода символов на консоль. Режим работы функции зависит от содержимого регистра DL при вызове функции. Если этот регистр содержит значение 0FFh, функция выполняет ввод с консоли, в противном случае символ, код которого записан в этот регистр, выводится на консоль.
Очевидно, что с помощью этой функции нельзя вывести на консоль символ с кодом 0FFh.
Основное отличие функции 06h от всех описанных ранее заключается в том, что эта функция не ожидает, пока оператор нажмет на клавишу. Если буфер клавиатуры пуст, функция просто устанавливает флаг процессора ZF в 1.
Если в буфере клавиатуры имеются символы, флаг ZF сбрасывается и в регистр AL функция записывает ASCII-код символа.
Запись символов в буфер клавиатуры
На входе: AH = 05h;
CL = ASCII-код записываемого символа;
CH = скан-код записываемого символа, или 0.
На выходе: AL = 0 - запись выполнена успешно; 1 - буфер клавиатуры переполнен.
С помощью этой функции можно вставить символы в буфер клавиатуры, как будто они были введены оператором.
Приведенная программа записывает в буфер клавиатуры пять символов '*'. Запустите ее и посмотрите на системное приглашение. Вы увидите что-нибудь похожее на C:\>*****.
#include <stdio.h> #include <dos.h>
void main(void);
void main(void) {
union REGS rg; int i;
for(i=0; i<5; i++) {
rg.h.ah = 5;
rg.h.cl = '*'; rg.h.ch = 9;
int86(0x16, &rg, &rg);
} }