Каждый знает, что такое клавиатура и для чего она предназначена, но далеко не все знают, что и как происходит при нажатии той или иной клавиши.
В этой статье я расскажу о низкоуровневой работе с клавиатурой и приведу пример реализации простого обработчика клавиатурного прерывания для реального режима (драйвер).
При нажатии на какую либо клавишу электроника клавиатуры генерирует скан-код клавиши длиной от 1 до 6 байт, который можно получить чтением порта ввода-вывода 0x60. Скан-код – уникальное число, однозначно определяющее нажатую клавишу, но не ASCII-код. Скан-коды бывают двух видов: при нажатии клавиши генерируется так называемый Make-код, а при её отпускании Break-код. Отличаются они лишь тем, что в Break-коде старший бит каждого байта установлен в единицу. Скан-коды делятся на группы: обычные, расширенные, дополнительные и код клавиши Pause. Обычный скан-код состоит из одного байта, расширенный – из 2-х байт, первый из которых – 0xE0. Длина дополнительного кода – от 2 до 4 байт. Он так же начинается с 0xE0. Очень сильно из колеи выбивается Pause – это единственная клавиша, код которой состоит из 6 байт, причем Break-код у неё отсутствует.
Клавиатура может работать в двух режимах: по опросу и по прерыванию. Я рассматриваю только второй режим, так как первый менее эффективен и его реализация проще. Когда во входном буфере клавиатуры есть какие-либо данные, происходит запрос прерывания по линии IRQ1. Помимо самих скан-кодов, клавиатура может передавать компьютеру специальные коды при выполнении команд (например, изменение состояния светодиодов).
ПРИМЕЧАНИЕ По ходу статьи предполагается, что клавиатура работает в режиме «по умолчанию»: установлен набор скан-кодов Set2, большинство клавиш работают в режиме автоповтора и т. д. |
Key | Make (HEX) | Break (HEX) |
---|---|---|
Num Lock | 45 | C5 |
Num Divide | E0 35 | E0 B5 |
Num Multiply | 37 | B7 |
Num Minus | 4A | CA |
Num Plus | 4E | CE |
Num Enter | E0 1C | E0 9C |
Num Dot | 53 | D3 |
Num 0 | 52 | D2 |
Num 1 | 4F | CF |
Num 2 | 50 | D0 |
Num 3 | 51 | D1 |
Num 4 | 4B | CB |
Num 5 | 4C | CC |
Num 6 | 4D | CD |
Num 7 | 47 | C7 |
Num 8 | 48 | C8 |
Num 9 | 49 | C9 |
Print Screen | E0 2A E0 37 | E0 B7 E0 AA |
Scroll Lock | 46 | C6 |
Pause | E1 1D 45 E1 9D C5 | |
Insert | E0 2A E0 52 | E0 D2 E0 AA |
Home | E0 2A E0 47 | E0 C7 E0 AA |
Page Up | E0 2A E0 49 | E0 C9 E0 AA |
Delete | E0 2A E0 53 | E0 D3 E0 AA |
End | E0 2A E0 4F | E0 CF E0 AA |
Page Down | E0 2A E0 51 | E0 D1 E0 AA |
Arrow Up | E0 2A E0 48 | E0 C8 E0 AA |
Arrow Left | E0 2A E0 4B | E0 CB E0 AA |
Arrow Down | E0 2A E0 50 | E0 D0 E0 AA |
Arrow Right | E0 2A E0 4D | E0 CD E0 AA |
Power | E0 5E | E0 DE |
Sleep | E0 5F | E0 DF |
Wake Up | E0 63 | E0 E3 |
Esc | 01 | 81 |
F1 | 3B | BB |
F2 | 3C | BC |
F3 | 3D | BD |
F4 | 3E | BE |
F5 | 3F | BF |
F6 | 40 | C0 |
F7 | 41 | C1 |
F8 | 42 | C2 |
F9 | 43 | C3 |
F10 | 44 | C4 |
F11 | 57 | D7 |
F12 | 58 | D8 |
Tab | 0F | 8F |
Caps Lock | 3A | BA |
Shift Left | 2A | AA |
Shift Right | 36 | B6 |
Ctrl Left | 1D | 9D |
Ctrl Right | E0 1D | E0 9D |
Alt Left | 38 | B8 |
Alt Right | E0 38 | E0 B8 |
Win Left | E0 5B | E0 DB |
Win Right | E0 5C | E0 DC |
Applications | E0 5D | E0 DD |
Space | 39 | B9 |
Enter | 1C | 9C |
Back Space | 0E | 8E |
1 | 02 | 82 |
2 | 03 | 83 |
3 | 04 | 84 |
4 | 05 | 85 |
5 | 06 | 86 |
6 | 07 | 87 |
7 | 08 | 88 |
8 | 09 | 89 |
9 | 0A | 8A |
0 | 0B | 8B |
Q | 10 | 90 |
W | 11 | 91 |
E | 12 | 92 |
R | 13 | 93 |
T | 14 | 94 |
Y | 15 | 95 |
U | 16 | 96 |
I | 17 | 97 |
O | 18 | 98 |
P | 19 | 99 |
A | 1E | 9E |
S | 1F | 9F |
D | 20 | A0 |
F | 21 | A1 |
G | 22 | A2 |
H | 23 | A3 |
J | 24 | A4 |
K | 25 | A5 |
L | 26 | A6 |
Z | 2C | AC |
X | 2D | AD |
C | 2E | AE |
V | 2F | AF |
B | 30 | B0 |
N | 31 | B1 |
M | 32 | B2 |
~ | 29 | A9 |
- | 0C | 8C |
= | 0D | 8D |
\ | 2B | AB |
[ | 1A | 9A |
] | 1B | 9B |
; | 27 | A7 |
" | 28 | A8 |
< | 33 | B3 |
> | 34 | B4 |
? | 35 | B5 |
ПРИМЕЧАНИЕ Если повнимательнее присмотреться к скан-коду клавиши Pause (E1 1D 45 E1 9D C5), можно заметить, что первые 3 байта являются Make-кодом, а вторые Break-кодом. То есть клавиатура при её нажатии генерирует сразу же и код нажатия и отжатия. В дальнейшем это немного упростит нам жизнь. Если нажать клавишу и удерживать её, то через заданное время, называемое временем автоповтора, скан-код удерживаемой клавиши будет повторяться с частотой автоповтора. При автоповторе дополнительные скан-коды содержат два байта вместо четырех. Например, последовательность байт при удержании и отпускании клавиши Page Up (E0 2A E0 49) выглядит так: E0 2A E0 49 E0 49 E0 49 E0 49 E0 49 E0 49 E0 49 E0 C9 E0 AA. Обратите внимание: при отпускании клавиши с дополнительным кодом последовательность перевёрнута – признак дополнительного кода (E0 AA) идёт в конце, а не в начале. |
Следующие позиции в таблице скан-кодов заняты и однозначно не могут быть использованы другими клавишами (и, соответственно, нами):
ПРИМЕЧАНИЕ Всем расширенным скан-кодам предшествует байт 0x0E, а дополнительным – последовательность байт 0xE0, 0x2A, 0xE0. |
Если решать нашу задачу в лоб, то придётся проверять каждый байт, полученный от клавиатуры. А так как байты из набора обычных кодов пересекаются с последними байтами остальных наборов (имеются одинаковые значения), дополнительные коды при отпускании идут перевёрнутыми парами, прерывание возникает столько раз, сколько байт в скан-коде, имеется ещё и Pause, то определить, что именно за клавиша была нажата, становится не очень просто. В конечном итоге все эти аспекты приведут к реализации с кучей неудобоваримых ветвлений.
Честно говоря, даже не представляю, чем руководствовались разработчики, создавая такой беспорядок. В этой статье я сосредоточусь именно на том, как всё это безобразие привести в более простой и красивый вид.
Для информирования о том, что скан-код является расширенным или дополнительным, используются значения 0xE0, 0x2A/0xAA и 0xE1 для Pause. Этот факт однозначно говорит о том, что эти значения также не используются ни одной из клавиш – иначе определить нажатую/отжатую клавишу было бы просто невозможно.
Введём понятие виртуальной клавиши – это номер, определяющий функцию клавиши. Например: Ctrl исполнен в виде двух физических клавиш, но их виртуальный код – один и тот же.
ПРЕДУПРЕЖДЕНИЕ Мои определения виртуальных клавиш несколько отличаются от используемых в Windows, хотя и очень похожи на них. |
Создадим таблицу трансляции скан-кодов в виртуальные клавиши, состоящую из 256 элементов по 2 байта каждый. В первом байте мы будем хранить сам виртуальный код клавиши, а во втором – некоторые атрибуты.
Если бы все коды состояли из одного байта, то по младшим семи разрядам значения этого байта можно было бы однозначно идентифицировать клавишу. Впрочем, для большей части клавиш это условие выполняется, и их преобразование можно свести к такому правилу: обнулить старший разряд, и полученное значение использовать в качестве индекса в таблице трансляции, по которому находится виртуальный код клавиши независимо от того, нажимается она или отжимается.
Поскольку мы обнуляем старший разряд, все значения в промежутке [128 – 255] таблицы трансляции свободны для наших нужд. Виртуальные коды расширенных и дополнительных клавиш мы будем хранить именно в этом диапазоне. Поступим следующим образом: если скан-код является расширенным или дополнительным (Pause пока отбросим), то установим в единицу старший разряд в его последнем байте.
Всё осложняется тем, что за одно прерывание мы получаем лишь один байт скан-кода, и так просто установить старший разряд последнего байта не удастся.
Начнём с рассмотрения дополнительных клавиш. Все они начинаются с байта 0xE0. Модифицируем ранее приведённое правило: если виртуальный код, полученный из таблицы трансляции, равен нулю, то виртуальный код не готов и скан-код ещё не принят полностью. Отбросив старший разряд у 0xE0, мы получим значение 0x60 (которое также не используется ни одной клавишей), которое можно использовать в качестве индекса, по которому будет храниться значение 0. При обработке следующего прерывания мы получим последний байт скан-кода, у которого и должны установить старший разряд. Проще всего это сделать, модифицировав правило преобразования. Вспомним о том, что у нас имеется ещё и байт атрибутов в таблице, который до сих пор никак не задействован. Новое правило будет выглядеть так: при каждом получении значения из таблицы будем сохранять его в переменной, а при формировании индекса будем использовать какой-либо разряд байта атрибутов, отражающий, что необходимо изменить старший разряд полученного байта.
Теперь определимся, какой разряд мы будем использовать, и как с его помощью получать индекс. Как я уже сказал, нам необходимо установить самый старший бит. Получается всё очень просто и красиво: старший бит атрибутов, взятый из переменной, переносится в старший разряд полученного байта.
Давайте разберём всё вышеизложенное на примере клавиши Ctrl Right (E0 1D):
Назовём переменную, которая должна хранить байт виртуального кода и байт атрибутов, KeyInfo. Инициализируем ее значением 0. Первый получаемый байт имеет значение 0xE0. Обнуляем старший разряд и получаем 0x60. Берём старший разряд байта атрибутов из переменной KeyInfo и переносим его в старший разряд полученного индекса: (0xE0 & 0x7F) | ((KeyInfo >> 8) & 0x80). В итоге получаем индекс 0x60, по которому в таблице будет храниться виртуальный код с нулевым значением и байт атрибутов с установленным старшим разрядом: KeyInfo = 0x8000. Следующий байт в последовательности 0x1D: KeyInfo = Table[(0x1D & 0x7F) | ((KeyInfo >> 8) & 0x80)] == 0x9D – вот мы и получили конечный индекс, по которому в таблице будет находиться виртуальный код.
Едем дальше – на очереди дополнительные коды. Отличаются они лишь тем, что первые 2 байта имеют значение 0xE0, 0x2A. Самый простой способ их приёма даже не потребует изменений правила получения индекса. После получения 0xE0 байт 0x2A превратится в индекс 0xAA, по которому будем хранить значение 0, указывающее на то, что виртуальный код не готов, и модифицировать старший разряд следующего байта не нужно (как будто бы этой последовательности и вовсе не было). Следующие 2 байта последовательности ничем не отличаются от расширенного скан-кода, и для их приёма уже всё готово.
ПРИМЕЧАНИЕ Всё вышеизложенное прекрасно работает при получении 2 байт вместо 4 при автоповторе и обратном порядке следования пар при отпускании. Причём при отпускании первые 2 байта дадут виртуальный код отжатой клавиши, а последние 2 (E0 AA) будут преобразованы в 0x0000 (проигнорированы). |
Но как вы уже, наверное, забыли, мы отбросили клавишу Pause (E1 1D 45) – давайте теперь разберёмся и с ней. Если пойти по предыдущему пути, и по индексу 0x61 (0xE1 & 0x7F) хранить значение 0x8000, то мы получим коллизию, связанную с правым Ctrl’ом (E0 1D). Что же нам в этом случае делать? Ну что ж, будем в очередной раз модифицировать наше правило получения индекса: посмотрим на двоичное представление числа 0x1D – 0001 1101. Чтоб не возникло коллизий, можно, например, модифицировать 5-й, 6-й, или оба разряда вместе, или 7-й и 1-й, но раз уж мы начали модифицировать старшие разряды, то давайте продолжим. Новое правило получения индекса будет таким: (Value & 0x7F) | ((KeyInfo >> 8) & 0xC0). Но на этом наши мучения с клавишей Pause не закончились. По индексу 0x61 будем хранить значение KeyInfo = 0x4000 (не 0xC000, чтоб не возникло коллизии с Applications (E0 5D)), и, следуя новому правилу, получим: (1D & 0x7F) | ((KeyInfo >> 8) & 0xC0) == 0x5D. Но, поскольку код Pause состоит из трёх байт, то по этому индексу тоже должен быть какой-то модификатор – и о благо: если взять 0x8000, то никаких коллизий не возникнет, и по индексу 0xC5 будет находиться виртуальный код Pause.
ПРИМЕЧАНИЕ Несмотря на то, что KeyInfo изменяется после каждого принятого байта, никаких проблем при приёме следующего скан-кода не возникнет, так как для модификации принятого байта используется только байт атрибутов. Если конечный индекс получен, то по нему в таблице модифицирующие разряды отсутствуют (сброшены в ноль) и уже неважно, что находится в первом байте KeyInfo. |
Key | Index (HEX) | Virtual Key | Attribute | Comment |
---|---|---|---|---|
00 | VK_UNKNOWN | |||
Esc | 01 | VK_ESCAPE | ||
1 | 02 | VK_1 | ||
2 | 03 | VK_2 | ||
3 | 04 | VK_3 | ||
4 | 05 | VK_4 | ||
5 | 06 | VK_5 | ||
6 | 07 | VK_6 | ||
7 | 08 | VK_7 | ||
8 | 09 | VK_8 | ||
9 | 0A | VK_9 | ||
0 | 0B | VK_0 | ||
- | 0C | VK_OEM_MINUS | ||
= | 0D | VK_OEM_PLUS | ||
Back Space | 0E | VK_BACK | ||
Tab | 0F | VK_TAB | ||
Q | 10 | VK_Q | ||
W | 11 | VK_W | ||
E | 12 | VK_E | ||
R | 13 | VK_R | ||
T | 14 | VK_T | ||
Y | 15 | VK_Y | ||
U | 16 | VK_U | ||
I | 17 | VK_I | ||
O | 18 | VK_O | ||
P | 19 | VK_P | ||
[ | 1A | VK_OEM_4 | ||
] | 1B | VK_OEM_6 | ||
Enter | 1C | VK_RETURN | ||
Ctrl Left | 1D | VK_CONTROL | ||
A | 1E | VK_A | ||
S | 1F | VK_S | ||
D | 20 | VK_D | ||
F | 21 | VK_F | ||
G | 22 | VK_G | ||
H | 23 | VK_H | ||
J | 24 | VK_J | ||
K | 25 | VK_K | ||
L | 26 | VK_L | ||
; | 27 | VK_OEM_1 | ||
" | 28 | VK_OEM_7 | ||
~ | 29 | VK_OEM_3 | ||
Shift Left | 2A | VK_SHIFT | ||
\ | 2B | VK_OEM_5 | ||
Z | 2C | VK_Z | ||
X | 2D | VK_X | ||
C | 2E | VK_C | ||
V | 2F | VK_V | ||
B | 30 | VK_B | ||
N | 31 | VK_N | ||
M | 32 | VK_M | ||
< | 33 | VK_OEM_COMMA | ||
> | 34 | VK_OEM_PERIOD | ||
? | 35 | VK_OEM_2 | ||
Shift Right | 36 | VK_SHIFT | RIGHT | |
Num Multiply | 37 | VK_MULTIPLY | ||
Alt Left | 38 | VK_MENU | ||
Space | 39 | VK_SPACE | ||
Caps Lock | 3A | VK_CAPITAL | ||
F1 | 3B | VK_F1 | ||
F2 | 3C | VK_F2 | ||
F3 | 3D | VK_F3 | ||
F4 | 3E | VK_F4 | ||
F5 | 3F | VK_F5 | ||
F6 | 40 | VK_F6 | ||
F7 | 41 | VK_F7 | ||
F8 | 42 | VK_F8 | ||
F9 | 43 | VK_F9 | ||
F10 | 44 | VK_F10 | ||
Num Lock | 45 | VK_NUMLOCK | ||
Scroll Lock | 46 | VK_SCROLL | ||
Num 7 | 47 | VK_NUMPAD7 | ||
Num 8 | 48 | VK_NUMPAD8 | ||
Num 9 | 49 | VK_NUMPAD9 | ||
Num Minus | 4A | VK_SUBTRACT | ||
Num 4 | 4B | VK_NUMPAD4 | ||
Num 5 | 4C | VK_NUMPAD5 | ||
Num 6 | 4D | VK_NUMPAD6 | ||
Num Plus | 4E | VK_ADD | ||
Num 1 | 4F | VK_NUMPAD1 | ||
Num 2 | 50 | VK_NUMPAD2 | ||
Num 3 | 51 | VK_NUMPAD3 | ||
Num 0 | 52 | VK_NUMPAD0 | ||
Num Dot | 53 | VK_DECIMAL | ||
54 | VK_UNKNOWN | |||
55 | VK_UNKNOWN | |||
56 | VK_UNKNOWN | |||
F11 | 57 | VK_F11 | ||
F12 | 58 | VK_F12 | ||
59 | VK_UNKNOWN | |||
5A | VK_UNKNOWN | |||
5B | VK_UNKNOWN | |||
5C | VK_UNKNOWN | |||
5D | EXTEND | Pause (E1 1D) | ||
5E | VK_UNKNOWN | |||
5F | VK_UNKNOWN | |||
60 | EXTEND | Extended (E0) | ||
61 | PAUSE_EXTEND | Pause (E1) | ||
62 | VK_UNKNOWN | |||
63 | VK_UNKNOWN | |||
64 | VK_UNKNOWN | |||
65 | VK_UNKNOWN | |||
66 | VK_UNKNOWN | |||
67 | VK_UNKNOWN | |||
68 | VK_UNKNOWN | |||
69 | VK_UNKNOWN | |||
6A | VK_UNKNOWN | |||
6B | VK_UNKNOWN | |||
6C | VK_UNKNOWN | |||
6D | VK_UNKNOWN | |||
6E | VK_UNKNOWN | |||
6F | VK_UNKNOWN | |||
70 | VK_UNKNOWN | |||
71 | VK_UNKNOWN | |||
72 | VK_UNKNOWN | |||
73 | VK_UNKNOWN | |||
74 | VK_UNKNOWN | |||
75 | VK_UNKNOWN | |||
76 | VK_UNKNOWN | |||
77 | VK_UNKNOWN | |||
78 | VK_UNKNOWN | |||
79 | VK_UNKNOWN | |||
7A | Acknowledge (FA) | |||
7B | VK_UNKNOWN | |||
7C | VK_UNKNOWN | |||
7D | VK_UNKNOWN | |||
7E | VK_UNKNOWN | |||
7F | VK_UNKNOWN | |||
80 | VK_UNKNOWN | |||
81 | VK_UNKNOWN | |||
82 | VK_UNKNOWN | |||
83 | VK_UNKNOWN | |||
84 | VK_UNKNOWN | |||
85 | VK_UNKNOWN | |||
86 | VK_UNKNOWN | |||
87 | VK_UNKNOWN | |||
88 | VK_UNKNOWN | |||
89 | VK_UNKNOWN | |||
8A | VK_UNKNOWN | |||
8B | VK_UNKNOWN | |||
8C | VK_UNKNOWN | |||
8D | VK_UNKNOWN | |||
8E | VK_UNKNOWN | |||
8F | VK_UNKNOWN | |||
90 | VK_UNKNOWN | |||
91 | VK_UNKNOWN | |||
92 | VK_UNKNOWN | |||
93 | VK_UNKNOWN | |||
94 | VK_UNKNOWN | |||
95 | VK_UNKNOWN | |||
96 | VK_UNKNOWN | |||
97 | VK_UNKNOWN | |||
98 | VK_UNKNOWN | |||
99 | VK_UNKNOWN | |||
9A | VK_UNKNOWN | |||
9B | VK_UNKNOWN | |||
Num Enter | 9C | VK_RETURN | RIGHT | |
Ctrl Right | 9D | VK_CONTROL | RIGHT | |
9E | VK_UNKNOWN | |||
9F | VK_UNKNOWN | |||
A0 | VK_UNKNOWN | |||
A1 | VK_UNKNOWN | |||
A2 | VK_UNKNOWN | |||
A3 | VK_UNKNOWN | |||
A4 | VK_UNKNOWN | |||
A5 | VK_UNKNOWN | |||
A6 | VK_UNKNOWN | |||
A7 | VK_UNKNOWN | |||
A8 | VK_UNKNOWN | |||
A9 | VK_UNKNOWN | |||
AA | Additional (E0 2A) | |||
AB | VK_UNKNOWN | |||
AC | VK_UNKNOWN | |||
AD | VK_UNKNOWN | |||
AE | VK_UNKNOWN | |||
AF | VK_UNKNOWN | |||
B0 | VK_UNKNOWN | |||
B1 | VK_UNKNOWN | |||
B2 | VK_UNKNOWN | |||
B3 | VK_UNKNOWN | |||
B4 | VK_UNKNOWN | |||
Num Divide | B5 | VK_DIVIDE | ||
B6 | VK_UNKNOWN | |||
Print Screen | B7 | VK_SNAPSHOT | ||
Alt Right | B8 | VK_MENU | RIGHT | |
B9 | VK_UNKNOWN | |||
BA | VK_UNKNOWN | |||
BB | VK_UNKNOWN | |||
BC | VK_UNKNOWN | |||
BD | VK_UNKNOWN | |||
BE | VK_UNKNOWN | |||
BF | VK_UNKNOWN | |||
C0 | VK_UNKNOWN | |||
C1 | VK_UNKNOWN | |||
C2 | VK_UNKNOWN | |||
C3 | VK_UNKNOWN | |||
C4 | VK_UNKNOWN | |||
Pause | C5 | VK_PAUSE | ||
C6 | VK_UNKNOWN | |||
Home | C7 | VK_HOME | ||
Arrow Up | C8 | VK_UP | ||
Page Up | C9 | VK_PRIOR | ||
CA | VK_UNKNOWN | |||
Arrow Left | CB | VK_LEFT | ||
CC | VK_UNKNOWN | |||
Arrow Right | CD | VK_RIGHT | ||
CE | VK_UNKNOWN | |||
End | CF | VK_END | ||
Arrow Down | D0 | VK_DOWN | ||
Page Down | D1 | VK_NEXT | ||
Insert | D2 | VK_INSERT | ||
Delete | D3 | VK_DELETE | ||
D4 | VK_UNKNOWN | |||
D5 | VK_UNKNOWN | |||
D6 | VK_UNKNOWN | |||
D7 | VK_UNKNOWN | |||
D8 | VK_UNKNOWN | |||
D9 | VK_UNKNOWN | |||
DA | VK_UNKNOWN | |||
Win Left | DB | VK_WIN | ||
Win Right | DC | VK_WIN | RIGHT | |
Applications | DD | VK_APPS | ||
Power | DE | VK_POWER | ||
Sleep | DF | VK_SLEEP | ||
E0 | VK_UNKNOWN | |||
E1 | VK_UNKNOWN | |||
E2 | VK_UNKNOWN | |||
Wake Up | E3 | VK_WAKEUP | ||
E4 | VK_UNKNOWN | |||
E5 | VK_UNKNOWN | |||
E6 | VK_UNKNOWN | |||
E7 | VK_UNKNOWN | |||
E8 | VK_UNKNOWN | |||
E9 | VK_UNKNOWN | |||
EA | VK_UNKNOWN | |||
EB | VK_UNKNOWN | |||
EC | VK_UNKNOWN | |||
ED | VK_UNKNOWN | |||
EE | VK_UNKNOWN | |||
EF | VK_UNKNOWN | |||
F0 | VK_UNKNOWN | |||
F1 | VK_UNKNOWN | |||
F2 | VK_UNKNOWN | |||
F3 | VK_UNKNOWN | |||
F4 | VK_UNKNOWN | |||
F5 | VK_UNKNOWN | |||
F6 | VK_UNKNOWN | |||
F7 | VK_UNKNOWN | |||
F8 | VK_UNKNOWN | |||
F9 | VK_UNKNOWN | |||
FA | VK_UNKNOWN | |||
FB | VK_UNKNOWN | |||
FC | VK_UNKNOWN | |||
FD | VK_UNKNOWN | |||
FE | VK_UNKNOWN | |||
FF | VK_UNKNOWN |
ПРЕДУПРЕЖДЕНИЕ По индексу 0x7A хранится нулевое значение для игнорирования ответа от клавиатуры при приёме команды, но это не единственный возможный ответ, и все их при помощи данной таблицы игнорировать не удастся. Но эта задача решается крайне просто, например, при посылке команды можно отключить прерывания вообще и работать по опросу, или завести переменную, указывающую на то, что была послана команда, и трансляцию скан-кодов использовать не нужно. |
С трансляцией мы полностью разобрались, теперь давайте задействуем ещё некоторые разряды байта атрибутов. Например, будем хранить в 0-м разряде признак отпускания клавиши – при отжатии он будет устанавливаться в единицу. Как я уже говорил, виртуальный код определяет именно функцию клавиши, а не её физическое расположение. Но представьте, что эта информация может понадобиться, 1-й разряд будет признаком того, что клавиша является правой.
В тестовом примере реализован обработчик клавиатурного прерывания (работы с командами нет), который при нажатии или отпускании клавиши выводит информацию о том, что и с какой клавишей произошло.
СОВЕТ Определения самих таблиц вынесены в отдельные файлы (TrnslTbl.inc, NamesTbl.inc) в которых присутствуют только сами объявления, вследствие чего упрощается их модификация, например, если нам понадобится изменить значение кода 0xFE, то мы просто перейдём на строку 255 и сделаем необходимые изменения. |
P. S. Конструктивная критика, исправления и дополнения крайне приветствуются.