Спецификация для Windows. При нажатии любой клавиши контроллер клавиатуры вырабатывает скан-код, соответствующей позиции клавиши, который передаются в компьютер. Служебные коды, которые может вырабатывать контроллер клавиатуры, передаются для обработки подпрограммам BIOS. В буфере клавиатуры для кода клавиши отводится по 2 байта, т. е. он рассчитан на 16 символов.
Как определить Скан-код клавиши
Узнать Скан-код необходимой для ремаппинга клавиши вы можете здесь, а если у вас супер новая клавиатура с кнопками космического происхождения, то отловить скан-код вы можете используя программу «KeyboardTest» от разработчика Passmark Software. Программка платная, но бесплатным триалом. Или OpenSource Программу SharpKeys. Она потребовалась для определения скан-кода левой «\», так как многие наивно полагали что сигналы у обеих одинаковые, а значит и не подлежат ремаппингу. На деле оказалось не так.
И так, мы определили скан-код нашей незадачливой кнопки, теперь нам нужно найти скан-код «LShift». Его можно найти по данной выше ссылке и он равен: 2A.00
Ремаппинг клавиш
Есть куча приложений которые делают этот ремаппинг, многие за базу принимают существующие скан-коды, многие не имеют сканера, что бы определить скан-код клавиши которой нет в базе. Да и устанавливать лишнее приложение очень не хотелось. По-гуглив я остановился на возможностях Windows. а точнее редактора реестра (Пуск-Выполнить: regedit). Или вышеупомянутую программу SharpKeys, которая имеет GUI и выполняет такую же замену в реестре
1. В ветке HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout создайте бинарный параметр с именем «Scancode Map» 2. В параметр «Scancode Map» записываем следующие значения: 00.00.00.00.00.00.00.00.02 — девятый байт (02) в этой записи означает количество клавиш которое мы будем менять (и равен количеству клавиш +1) в нашем случае это 02, так как мы меняем значения одной клавиши. После этого вписываем еще три «пустых» байта, и теперь наш параметр выглядит так: 00.00.00.00.00.00.00.00.02.00.00.00. Поскольку мы уже определили все Скан-коды, то продолжая наш параметр, нам следует указать скан-код значения новой кнопки (в нашем случае это 2A.00), а затем скан-код старой (заменяемой) кнопки 56.00. Теперь необходимо закрыть эту функцию четырьмя «пустыми» байтами, то есть 00.00.00.00:
3. Перезагружаем компьютер и пользуемся нашими новыми кнопками.
PS: Если нужно сделать ремаппинг не на всем ПК, а только в вашем сеансе, то значения необходимо менять в HKEY_CURRENT_USER\SYSTEM\CurrentControlSet\Control\Keyboard Layout
UPD: Если вы боитесь работать в реесте, то создайте текстовый файл с расширением *.reg, и впишите туда следующее: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] «Scancode Map»=hex:00,00,00,00,00,00,00,00,02,00,00,00,2a,00,56,00,00,00,00,00 затем сохраните, запустите, и перезагрузите ПК.
UPD2: Перенос Скриншотов на 1. Левый Win-key выглядит так: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] «Scancode Map»=hex:00,00,00,00,00,00,00,00,02,00,00,00,37,E0,5B,E0,00,00,00,00 Правый Win-key выглядит так: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] «Scancode Map»=hex:00,00,00,00,00,00,00,00,02,00,00,00,37,E0,5C,E0,00,00,00,00
Опубликовано: 27.08.2005 Исправлено: 10.12.2016 Версия текста: 1.0
Каждый знает, что такое клавиатура и для чего она предназначена, но далеко не все знают, что и как происходит при нажатии той или иной клавиши.
В этой статье я расскажу о низкоуровневой работе с клавиатурой и приведу пример реализации простого обработчика клавиатурного прерывания для реального режима (драйвер).
При нажатии на какую либо клавишу электроника клавиатуры генерирует скан-код клавиши длиной от 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
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
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. Конструктивная критика, исправления и дополнения крайне приветствуются.