Практикум №2.
«Управление клавиатурой»
Цель работы: Изучение организации и принципов работы клавиатуры и закрепление практических навыков управления ею.
Краткие теоретические сведения
Сочетание «горячих» клавиш (клавиша быстрого доступа; англ. keyboard shortcut)— разновидность интерфейса взаимодействия с вычислительным устройством (компьютером, калькулятором), представляющая собой нажатие кнопки/клавиши (или сочетания клавиш) на клавиатуре, которому назначено (запрограммировано) некое действие — команды (операции), исполняемые данной системой. Как правило, частично дублирует интерфейс меню или кнопок и служит для значительного ускорения работы, также — увеличения количества возможных действий, выполняемых с помощью клавиатуры.
На персональных компьютерах чаще всего в сочетаниях используются клавиши Ctrl, Alt, ⇧Shift, а также Win или Command. Их часто называют клавиши-модификаторы. В сочетаниях можно зажимать не только одну из этих клавиш, а несколько одновременно.
Стоит отметить, что клавиша-модификатор (или управляющая клавиша) принципиально отличается от иных клавиш тем, что может:
· неограниченно долго находиться в зажатом (активном) состоянии, и
· будучи зажатой, не блокировать действия остальных клавиш — как модификаторов, так и обычных.
Такие клавиши обычно имеют отдельный вывод контроллера клавиатуры, в отличие от алфавитно-цифровых, организованных матрично из-за экономии выводов контроллера. Матричная клавиатура обычно рассчитана на нажатие клавиш только поодиночке, поэтому, например, одновременное зажатие Q и W ни к чему хорошему не приведёт. Для оповещения оператора о подобных проблемах в DOS выдавался сигнал на динамик ЭВМ при зажатии нескольких не управляющих клавиш.
Функция должна реализовывать поставленную задачу — и ничего более. Это означает, что функцию можно будет, например, перенести без изменений в любую другую программу, если спецификации функции удовлетворяют условиям задачи. Это также означает, что при ошибочном задании параметров или при каких-то особых случаях в их значениях функция не должна аварийно завершать программу или выводить какие-то сообщения на экран, но должна возвращать какое-то прогнозируемое значение, по которому та функция, которая вызвала нашу, может сделать вывод об ошибке или об особом случае.
Возможные возвращаемые значения функции установим: 1 (задание параметров правильное) и 0 (задание не правильное). Эти значения при обращениях к функции можно будет интерпретировать как «истина» или «ложь».
Заметим, что интерпретация конфигурации параметров как правильная/неправильная и выбор реакции на неправильное задание — дело исполнителя. Но исполнитель должен строго выполнять принятые правила. Возможен также случай, когда выходная строка выйдет большей длины, чем для нее отведено места в памяти. Однако, поскольку нашей функции неизвестен размер памяти, отведенный для строки, функция не может распознать и обработать этот случай — так же ведут себя и библиотечные функции языка C.
Алгоритм решения задачи
Шаг 1: Запомнить адрес старого обработчика прерывания 9h, вызывая функцию readvect(in) с параметром in=9.
Шаг 2: Записать в таблицу векторов прерываний адрес нового обработчика прерывания с помощью функции writevect().
Шаг 3: Прочитать флаг состояния клавиатуры: два байта по адресу 0040:0017: byte17 = peekb(0x40, 0x17).
Шаг 4: Прочитать флаг состояния клавиатуры: два байта по адресу 0040:0018: byte18 = peekb(0x40, 0x18).
Шаг 4: Выделить бит 1 в флаге по адресу 0040:0017: byte17&mask
(если он равен 1, то нажата клавиша левый Shift).
Шаг 5: Проверить, нажата ли комбинация клавиш CTRL+SHIFT+[3]. Для этого проверяется условие: (inportb(0x60) == F3_code) && (byte17&mask) && (byte17&mask17) && (!(byte18&mask18)).
Если byte17&mask17 равен 1, то нажата клавиша CTRL.
Если byte18&mask18 равен 1, то нажата клавиша SHIFT.
Если inportb(0x60) равен F3_code, то нажата клавиша «3».
Шаг 6: Если выполняется условие из шага 5, проверить, равен ли флаг блокирования нулю (разблокирована ли клавиатура).
Шаг 7: При выполнении условия из шага 6, клавиатура блокируется (f=1), иначе происходит разблокировка.
Шаг 8: Проверить условие: (f == 1) && (inportb(0x60) == key3_code).
Шаг 9: В случае выполнения условия из шага 8 послать подтверждение приема в клавиатуру. Для этого в порт 61h на короткое время выставить «1» по шине старшего разряда:
c = inportb(0x61);
outportb(0x61, c | 0x80);
outportb(0x61, c).
Сбросить контроллер прерываний, посылая код 20h в порт 20h: outportb(0x20, 0x20).
Шаг 10: Вызов обработчика прерываний(*old9()).
Шаг 11: Записать в таблицу векторов прерываний адрес нового обработчика прерывания с помощью функции writevect.
Схема алгоритма изображена на рис. 2.
Тестовый пример
Дано: комбинации «горячих» клавиш CTRL+SHIFT+[Любая клавиша]
Необходимо: Разработать программу обработки прерывания от клавиатуры, которая должна:
• при первом нажатии «горячей» комбинации переходить в режим блокировки ввода заданной клавиши, при втором — отменять этот режим;
• системная обработка всех других клавиш нарушаться не должна.
Для блокировки символа «3» необходимо нажать комбинацию клавиш CTRL+SHIFT+[Любая клавиша]. Для снятия блокировки также использовать комбинацию клавиш CTRL+SHIFT+[Любая клавиша].
Рис.1 – Работа программы
Описание логической структуры
Программа состоит из одного программного модуля — файл ConsoleApplication1. В состав модуля входит функция — main. Общих переменных в программе нет. Макроконстантой Nопределена максимальная длина строки — 80. Функция main является главной функцией программы, она предназначена для ввода исходных данных и вывода результатов.
Определим состав параметров функции. Глобальные переменные программы: old9 — адрес старого обработчика прерывания 9h; key3_code — скан-код клавиши «3», которая будет блокироваться/разблокироваться при каждом нажатии «горячей» комбинации клавиш; f — флаг, который при каждом нажатии «горячей» комбинации клавиш переключается из состояния 0 в 1 или из 1 в 0 (состояние 1 означает, что клавиша «3» заблокирована); rr и sr — переменные, которые используются для задания значений регистров общего назначения и сегментных регистров соответственно при вызове прерывания.
В главной программе использует символьный массив string для проверки работы программы.
Переменные процедуры обработки прерывания 9h:
· c — переменная, которая используется для подтверждения приема из клавиатуры, в случае, если была нажата клавиша «3», а флаг f показывал, что эта клавиша заблокирована;
Рис. 2 – Схема алгоритма
· x, y — переменные, которые используются для сохранения координат курсора на экране в момент вызова процедуры обработки прерывания;
· byte17 — байт флага состояния клавиатуры в области данных BIOS по адресу 0040:0017;
· byte18 — байт флага состояния клавиатуры в области данных BIOS по адресу 0040:0018;
· mask — маска, которая используется для определения нажатия клавиши левый Shift (в этом случае бит 1 в byte17 установлен в 1);
· mask17 — маска, которая используется для определения нажатия клавиши Сtrl (в этом случае бит 2 в byte17 установлен в 1);
· mask18 — маска, которая используется для определения нажатия клавиши левый Сtrl (в этом случае бит 0 в byte18 установлен в 1);
Варианты заданий
Nп/п | Сочетания "горячих" клавиш | Клавиша, которая блокируется | |||||
Shift | Ctrl | Alt | |||||
Лев. | Пр. | Лев. | Пр. | Лев. | Пр. | ||
+ | + | ||||||
+ | + | ||||||
+ | + | ||||||
+ | + | ||||||
+ | + | ||||||
+ | + | ||||||
+ | + | ||||||
+ | + | ||||||
+ | + | ||||||
+ | + |
6. Контрольные вопросы
1. Какое главное качество языка С?
2. Как представляется признак конца стоки в языке С?
3. Что такое аппаратное прерывание?
4. Что выполняет функция конкатенации?
5. Какой библиотечной функции является аналогичной функция конкатенации?
Приложение
/*_________Управление клавиатурой_______________*/
/* Подключение стандартных заголовков */
#include<dos.h>
void interrupt(*old9)(); /* Старый обработчик прерывания 9h */
void interruptnew9(); /* Новый обработчик прерывания 9h */
void *readvect(int in); /* Чтение вектора */
void writevect(int in, void *h); /* Запись вектора */
unsigned char F3_code = 61; /* scan_code "F3" */
unsigned char key3_code = 4;/* scan_code "3" */
char f = 0; /* Флаг */
union REGS rr;
struct SREGS sr;
/*________________________________________________*/
void main()
{
charstring[80]; /* Буфер для ввода текста */
textbackground(0);
clrscr();
textattr(0x0a);
cprintf("_______________");
cprintf("_______________");
cprintf("_______________");
cprintf(" Управление клавиатурой ");
cout<<L"Введите любой текст. Комбинация клавиш CTRL+SHIFT+[Любая клавиша] запретят к вводу символ '3'.\n";
cprintf("_______________");
old9 = readvect(9);
writevect(9, new9);
textattr(0x0c);
cprintf("\n\n\r\"горячая\" комбинация: ");
textattr(0x0a);
cprintf("Left Shift, Left Ctrl, [любаяклавиша]\n\r");
textattr(0x0b);
cprintf("Клавиша, котораяблокируется: ");
textattr(0x0f);
cprintf("3");
textattr(0x07);
cprintf("\r\nВводите строку символов>");
scanf("%s", string);
writevect(9, old9);
}
/* Чтение вектора */
void *readvect(intin)
{
rr.h.ah = 0x35;
rr.h.al = in;
intdosx(&rr, &rr, &sr);
return(MK_FP(sr.es, rr.x.bx));
}
/* Записьвектора */
void writevect(int in, void *h)
{
rr.h.ah = 0x25;
rr.h.al = in;
sr.ds = FP_SEG(h);
rr.x.dx = FP_OFF(h);
intdosx(&rr, &rr, &sr);
}
/* Новый обработчик 9_го прерыванмя */
void interrupt new9()
{
unsigned char c, x, y;
unsigned char byte17, byte18;
unsigned char mask = 0x02;
unsigned char mask17 = 0x04;
unsigned char mask18 = 0x01;
byte17 = peekb(0x40, 0x17);
byte18 = peekb(0x40, 0x18);
if ((inportb(0x60) == F3_code) && (byte17&mask) &&
(byte17&mask17) &&(!(byte18&mask18)))
{
cputs("\7");
x = wherex();
y = wherey();
gotoxy(55, 3);
textattr(0x1e);
if (f == 0)
{
f = 1;
cprintf("Cимвол '3' запрещен, снова нажмите комбинацию клавиш CTRL+SHIFT+[Любая клавиша] чтобы снять запрет.");
}
else
{
f = 0;
cprintf("Запрет снят, вы снова моежет использовать символ '3'.");
}
gotoxy(x, y);
textattr(0x07);
(*old9)();
}
if ((f == 1) && (inportb(0x60) == key3_code))
{
c = inportb(0x61);
outportb(0x61, c | 0x80);
outportb(0x61, c);
outportb(0x20, 0x20);
}
else
(*old9)();
}