В состав клавиатуры входит встроенный микропроцессор, основной функцией которого является опрос (сканирование) клавишей и формирование кода нажатой клавиши. Этот код называется кодом сканирования (скан-кодом) и представляет собой порядковый номер клавиши. Кодов сканирования ровно столько, сколько клавишей в клавиатуре. Микропроцессор следит также за временем, в течение которого клавиша удерживается в нажатом состоянии. Если это время превышает 0,5 c, то генерируется повторение скан-кодов с частотой 10 гц.
Примеры скан-кодов:
Escape 1
Enter 28
L (l, Д, д) 38
Если микропроцессор обнаружил нажатие клавиши, то вырабатывается прерывание $9, центральный процессор прекращает выполнение программы, после чего начинает работать подпрограмма обработки прерывания $9. Эту подпрограмму называют драйвером клавиатуры, она входит в состав MS DOS.
Драйвер клавиатуры анализирует скан-коды нажатых клавишей, формирует код символа и помещает его в буфер клавиатуры, который является областью оперативной памяти, способной запомнить до 15 вводимых символов. Буфер клавиатуры организован по принципу циклической очереди. При переполнении буфера поступающие символы игнорируются, а драйвер клавиатуры генерирует звуковой сигнал.
Имеются два типа кодов символов: коды ASCII и расширенные коды. Коды ASCII - это однобайтные числа, соответствующие таблице ASCII. В этой таблице первые 32 символа - управляющие коды, которые обычно используются для передачи команд периферийным устройствам. После них идут цифры, большие и малые латинские буквы, специальные символы, буквы национального алфавита, символы псевдографики и др.
Расширенные коды присвоены клавишам или комбинациям клавишей, которые не имеют представляющего их символа ASCII. Это функциональные клавиши F1 .. F10, комбинации с клавишей Alt, клавиши PgUp, PgDn и др. Расширенные коды имеют длину два байта, причем первый байт всегда содержит код ASCII #0.
Примеры расширенных кодов (указывается значение второго байта):
Shift + Tab 15
Alt + A 30
F1 59
Home 71
Ctrl + End 117
Исключением являются четыре функциональные клавиши, для которых генерируется однобайтный код:
BackSpace 8
Tab 9
Enter 13
Escape 27
В буфере клавиатуры для каждого символа отводится два байта. Для однобайтных кодов первый байт содержит код ASCII, а второй - скан-код клавиши. Для расширенных кодов в первом байте содержится нулевое значение, а во втором - номер расширенного кода, который в большинстве случаев совпадает с кодом сканирования.
Процедуры Read, Readln и функции KeyPressed, ReadKey, выполняющие ввод с клавиатуры, работают фактически с буфером клавиатуры.
Функция KeyPressed, предописанная в модуле Crt, возвращает значение true, если в буфере клавиатуры имеется хотя бы один символ, и false в противном случае.
При старте программы буфер клавиатуры обычно пустой. Любое нажатие клавишей, которым соответствует код ASCII или расширенный код, заносит код символа в буфер клавиатуры. Коды символов хранятся в буфере до тех пор, пока они либо не будут считаны, либо буфер не будет очищен самой программой.
Полностью очищают буфер процедуры Read и Readln. Функция ReadKey считывает из буфера клавиатуры первый символ и удаляет его из состава этого буфера. Если буфер пуст, то функция ReadKey приостанавливает работу программы и ждет, пока не будет нажата какая-либо клавиша, генерирующая символьный код.
Если в программе имеется фраза
IfKeyPressed then
Оператор,
то это не рассматривается как реакция на сиюминутное нажатие клавиши. Это будет реакция на состояние буфера клавиатуры, куда могли быть занесены ранее символы при случайном нажатии клавишей.
Перед опросом клавиатуры и в конце работы программы рекомендуется очищать буфер клавиатуры. Это можно выполнить, например, c помощью такой процедуры:
Procedure ClearKeyBuffer;
Var ch : char;
Begin
While KeyPressed do
ch:=ReadKey;
End { ClearKeyBuffer };
Процедура ClearKeyBuffer удаляет из буфера клавиатуры с помощью функции ReadKey по одному символу до тех пор, пока не будет очищен весь буфер.
При выводе информации на экран возникает необходимость приостанавливать работу программы до тех пор, пока не будет нажата определенная клавиша, например клавиша Esc. Такую работу можно выполнить с помощью следующей процедуры:
Procedure WaitEscape;
Varch : char;
Begin
ClearKeyBuffer;
Repeat
ch:=ReadKey;
Untilch=#27;
End { WaitEscape };
Для определения кода клавиши или сочетания клавишей можно использовать приведенную ниже программу KeyTest.
Program KeyTest;
UsesCrt;
Varch : char;
Begin
ClearKeyBuffer;
Repeat
ch := ReadKey;
Write(ch,' ',ord(ch));
Iford(ch)=0 then
Begin
ch:=ReadKey;
Writeln(' ',ch,' ',ord(ch));
End
Else
Writeln;
Until ord(ch)=13;
End.
Программа KeyTest опрашивает клавиши до тех пор, пока не будет нажата клавиша Enter, имеющая код ASCII #13.
В различных формах диалога с пользователем (выбор позиции меню, нажатие клавишей по указаниям информационной строки экрана, ответ на прямой вопрос программы и т.п.) программа ожидает нажатия одной из определенных заранее клавишей или группы клавишей. Опрос клавиатуры в этом случае может быть обеспечен с помощью приведенной ниже функции GetKey.
Function GetKey : byte;
Var ch : char;
Begin
ch:=ReadKey;
Iford(ch)=0 then { Расширенный код символа }
GetKey:=ord(ReadKey)
Else
GetKey:=ord(ch); { Нормальный код символа }
End { GetKey };
Следует отметить, что функциональная надежность использования функции GetKey может быть обеспечена лишь в случае, когда в процессе диалога используются только управляющие клавиши (одинарные или комбинации клавишей). Это связано с тем, что второй байт расширенного кода, как правило, совпадает с нормальным кодом какого-либо алфавитно-цифрового символа. Примеры таких совпадений приведены ниже в таблице.
Таблица. Примеры совпадения расширенного и нормального кодов
Управляющие
клавиши
Второй байт
расш.кода
Алфавитно-
цифр.символы
Нормальный
код символа
PgDn
F7
F10
Alt+N
Alt+M
Shift+F2
Q
A
D
U
Если в программе в процессе диалога могут одновременно использоваться управляющие и алфавитно-цифровые символы, то рекомендуется применять приведенную ниже функцию GetKeyWord.
Function GetKeyWord : word;
Var ch : char;
Begin
ch:=ReadKey;
If ord(ch)=0 then { Расширенный код символа }
GetKeyWord:=word(ord(ReadKey))+256
Else
GetKeyWord:=ord(ch); { Нормальный код символа }
End{ GetKeyWord };
Алфавитно-цифровые клавиши многозначны в смысле генерируемых при их нажатии кодов. В ряде случаев в программе нужно обеспечить реакцию на нажатие определенной клавиши вне зависимости от связанных с этой клавишей кодов (верхний или нижний регистр, латинский или русский алфавит). Например, такая необходимость возникает при ответе "Да" или "Нет" на вопрос программы. Альтернативный выбор в таком случае можно обеспечить, например, с помощью приведенной ниже функции GetYesNo.
FunctionGetYesNo:boolean;
Varch : char;
Cond : boolean;
Begin
Cond:=false;
Repeat
ch:=ReadKey;
If ch in ['Д','д','L','l'] then
Begin
Cond:=true; GetYesNo:=true;
End
Else
Ifch in['Н','н','Y','y'] then
Begin
Cond:=true; GetYesNo:=false;
End;
Until Cond;
End { GetYesNo }:
Цикл Repeat работает здесь до тех пор, пока не будет нажата клавиша, соответствующая букве «Д», или клавиша, определенная для буквы «Н».
При утвердительном ответе ("Да") выходное значение функции GetYesNo равно true, при отрицательном - false.