как узнать сканкод клавиши windows

Работа с клавиатурой. Читаем виртуальные и скан-коды клавиш.

В начале, как полагается, разберемся с вопросом: зачем нам нужны виртуальные и скан-коды клавиш?

Лично для меня изучить этот вопрос сподвигла небольшая проблема при тестировании одной из программ в отделе экологов. Дело в том, что встречается такой тип людей, которые почему-то страх как боятся компьютеров, несмотря на то, что без них не могут обойтись, да к тому же не могут толком объяснить, в случае проблемы, что собственно случилось.

Начинаешь тестировать программу, устанавливаешь на компьютер пользователя, объясняешь все популярно (тот, естественно, что-то помечает на обрывках бумажек), спрашиваешь “Понял?”, тот – “Да, понял.” Уходишь, через день приходишь и начинается “Программа не работает”, “Там что-то какое-то окошко появилось” и т.д. и т.п. Спрашиваешь “Что делал?” Показывает действие за действием – ошибок нет…Как работал человек? Не понятно. Сидеть целый день за спиной у него и наблюдать – не вариант. Заставлять записывать за собой всё, что делал – изуверство. Интерфейс упростил дальше некуда, даже не Hint’ы сделал, а в тупую подписи под каждым контролом, что куда писать. Не помогло. В итоге, родилась простая, на первый взгляд идея – установить на компьютер пользователя небольшую программку-шпиона, чтобы отслеживала все действия, которые тот творит над программой. Естественно, предупредив перед этим работника, что теперь все его действия над программой сохраняются (только не сказал куда…мало ли). Вот в процессе работы над моим псевдо-шпионом мне и потребовались дополнительные знания по работе с клавиатурой.

Вы же можете использовать полученные знания где угодно:

Сразу оговорюсь – в этой статье я не буду выкладывать весь исходник моего “шпиона”, только ту часть, которая отвечает за перехват нажатия клавиши. Это связано с тем, что для полноценной работы моей программы мне также пришлось разобраться немного с Windows API, чтобы отслеживать действия только с моей программой, а это уже целая отдельная статья.

Теперь непосредственно по скан-кодам. В буфере клавиатуры отводится два байта на каждый символ (весь размер буфера – 32 байта, то есть 16 нажатий клавиш) – один байт представляет собой скан-код, второй – символьный код (для управляющих клавиш он равен нулю). В числом виде скан-коды требуются на практике очень редко, но чтобы их узнать, можно воспользоваться небольшой программкой на Pascal’е:

То есть после нажатия какой-нибудь клавиши в регистре al окажется код символа, а в ahскан код.

Теперь про виртуальные коды.

Виртуальные коды – это то, что использует система для идентификации клавиш. То есть, зная виртуальный код клавиши, можно со 100% уверенностью хоть через неделю сказать пользователю, что он такого нажимал (и в какой последовательности). Дополнительно, зная текущую раскладку, можно также определить, что писал пользователь, но это уже немного другой вопрос.

Теперь напишем небольшую ловушку для клавиатуры, которая будет отлавливать все нажатия клавиш и писать лог-файл, в котором будет содержаться скан-код и виртуальный код нажатой клавиши.

Собственно, опытный глаз программиста сразу может увидеть недостаток, а именно – работа с файлом из DLL, что не рекомендуется. В идеале в DLL следует только определять коды и отсылать и в основную программу, а та в свою очередь пусть пишет их файл, но, как показала практика, может сгодиться и такой вариант.

Обратите внимание на выражение

здесь определяется была ли нажата клавиша. Если да, то записываем файл. Если это условие убрать, то каждое действие на клавиатуре будет дублироваться, т.е. в файл запишется нажатая клавиша и отпущенная.

Теперь пишем основное приложение. Для демонстрации действия ловушки я сделал вот такую форму:

как узнать сканкод клавиши windows. Смотреть фото как узнать сканкод клавиши windows. Смотреть картинку как узнать сканкод клавиши windows. Картинка про как узнать сканкод клавиши windows. Фото как узнать сканкод клавиши windowsВ верхний Memo записываем какой-либо текст, а в нижнем, по действию таймера, выписывается содержимое лог-файла. Таким образом Вы сможете практически “на лету” видеть, что происходит, какие данные передаются, что нажимается, какой параметр изменяется и т.д. и т.п.

Кстати, советую поставить по-больше задержку на таймере, иначе не будете успевать просмотреть всё содержимое.

Теперь делаем ссылки на необходимые нам процедуры из DLL. Будем загружать все необходимые процедуры (а их аж 2 штуки) сразу же после запуска программы. После описания формы пишем:

На событие onCreate необходимо открыть или создать лог-файл и запустить нашу ловушку.

Соответственно, на закрытие приложения ловушку необходимо выгрузить:

Вывод содержимого лог-файла в Memo делается проще простого:

Вот и все, что от нас требовалось. Теперь запускаем приложение (предварительно положив рядом с ним созданную DLL) и любуемся результатом:

как узнать сканкод клавиши windows. Смотреть фото как узнать сканкод клавиши windows. Смотреть картинку как узнать сканкод клавиши windows. Картинка про как узнать сканкод клавиши windows. Фото как узнать сканкод клавиши windowsКак видите в wParam мы получаем необходимый виртуальный код, а в LParam – скан-код и дополнительные сведения, например, признак расширенной клавиши. Все данные сохраняются в HEX-форме и при желании вы их сможете легко расшифровать. Если возникнут трудности – обращайтесь, разберемся вместе.

Источник

Нихт ферштейн: учим пингвина понимать мультимедийные клавиши

Содержание статьи

Определение скан-кода клавиш

Что бы ты там не нажимал на своей клавиатуре, X-серверу и ядру, в общем-то, все равно, что на ней написано или нарисовано. Их интересуют исключительно скан-код кнопки, причем сначала иксы считывают таблицу кодов клавиш ядра, а затем уже код клавиши привязывается к собственной таблице кодов. Если в Windows проблемы настройки мультимедийных клавиш в консоли как таковой не существует, то в Linux приходится отдельно настраивать реакцию на нажатие кнопок в консоли и в X-Window.

Чтобы узнать код клавиши, следует использовать утилиту xev, входящую в состав Х-сервера. После ее запуска появляется окно Event Tester, теперь последовательно нажимаем клавиши, запоминая выдаваемый код:

$ xev
.
KeyRelease event, serial 31, synthetic NO, window 0x3e00001,
root 0x67, subw 0x0, time 279734676, (311,611), root:(1104,687),
state 0x2000, keycode 236 (keysym 0x1008ff19, XF86Mail), same_screen YES,
XLookupString gives 0 bytes:
XFilterEvent returns: False
KeyRelease event, serial 31, synthetic NO, window 0x2600001,
root 0x67, subw 0x0, time 265877259, (883,334), root:(886,358),
state 0x0, keycode 161 (keysym 0x0, NoSymbol), same_screen YES,
XLookupString gives 0 bytes:
XFilterEvent returns: False

Вывод может быть огромен, так как отслеживается каждое движение мышки при проходе над окном Event Tester. Клавишу описывает блок KeyRelease, в частности, значение keycode как раз и является скан-кодом, который мы хотим узнать. В приведенном примере нажаты две клавиши. Клавише с кодом 236 соответствует код клавиши для X-сервера, указанный в keysym, а также действие XF86Mail, которое в KDE запускает используемый по умолчанию почтовый клиент. Для клавиши с номером 161 код и действие не определены.

Возможна ситуация, когда клавиша нажимается, но ее скан-код не выдается. Это означает, что ядро не может найти соответствующее значение. В выводе dmesg должна быть такая строка:

Use ‘setkeycodes 0xec ‘ to make it known.

То есть тебе предлагают установить скан-код клавиши самостоятельно при помощи setkeycodes, при этом значение keycode выбрать очень просто. Переведи полученную цифру в десятичное число (большинство калькуляторов это умеют) и прибавь 128. В данном примере 0xec=236, то есть получаем скан-код 364. Если есть сомнения, список задействованных и незадействованных скан-кодов можно просмотреть, запустив в консоли утилиту getkeycodes или dumpkeys. Например, если вывод «getkeycodes | grep » ничего не дал, значит этот код можно смело использовать.

Помочь определить скан-код способна и утилита ХKeycaps (www.jwz.org/xkeycaps), которая является графическим фронт-эндом к Xmodmap.

В консоли программа xev, разумеется, не работает. Чтобы узнать скан-код, выдаваемый ядром, следует использовать утилиту showkey или getkeycodes:

$ showkey
клавиатура была в режиме UNICODE
нажмите любую клавишу (программа завершится через 10 сек после последнего нажатия).
0xe0 0x6c 0xe0 0xec

Первые две цифры соответствуют нажатой клавише, вторые – отсутствию нажатия.

Настройка привязки скан-кодов в X-Window

$ cat /usr/include/X11/XF86keysym.h
/*
* Keys found on some «Internet» keyboards.
*/
#define XF86XK_Standby 0x1008FF10
#define XF86XK_AudioLowerVolume 0x1008FF11
#define XF86XK_AudioRaiseVolume 0x1008FF13
#define XF86XK_AudioPlay 0x1008FF14
#define XF86XK_AudioStop 0x1008FF15
#define XF86XK_Mail 0x1008FF19

keycode 161 XF86Calculator
keycode 174 XF86AudioLowerVolume
keycode 176 XF86AudioRaiseVolume
keycode 162 XF86AudioPause

И так далее, принцип, думаю, ясен. Причем код клавиш можно заносить как в десятичном, так и шестнадцатеричном виде. По моим наблюдениям, коды большинства клавиш стандартизированы. Поэтому, если ты один раз настроишь реакцию на нажатие клавиши и перенесешь файл на другой комп, есть вероятность, что на другой клаве реакция на нажатие также подписанной клавиши будет аналогичная. Пользователи рабочего стола Gnome с GDM могут прописать все эти строки в общесистемный файл /etc/X11/Xmodmap.

В результате находим прелюбопытнейший файл /etc/X11/Xsession.d/80ubuntu-xmodmap такого содержания:

/usr/bin/xmodmap /usr/share/apps/kxkb/ubuntu.xmodmap || true

/.icewm, появляющийся после первого запуска, следует добавить строку:

В Fluxbox строка для запуска проигрывателя будет выглядеть так:

В конфигах обычно есть примеры, поэтому с остальными оконными менеджерами, думаю, ты без труда разберешься сам.

Настройка реакции в консоли

$ setkeycodes 0xec 118

Посмотреть свободные значения можно в файле текущей клавиатурной раскладки. В Ubuntu и всех дистрибутивах, базирующихся на Debian, это обычно /etc/console-setup/boottime.kmap.gz. Если после запуска проблем с клавишами нет, заносим эту строку в один из стартовых скриптов, например в /etc/init.d/rc.local.

Теперь осталось задать соответствие клавиши и выполняемого действия. Здесь простор для творчества даже больше, чем в иксах. В keymaps(5) процедура установки соответствия keycode выглядит следующим образом:

< plain | >keycode keynumber = keysym

# Переключение консоли на одну назад при нажатии на клавишу с кодом 105
keycode 105 = Decr_Console
# Переключение консоли на одну вперед при нажатии на и клавишу с кодом 106
alt keycode 106 = Incr_Console

Но можно создавать и свои варианты, указывая команду в переменных:

Другими словами, по нажатии клавиши с кодом 120 будет выполнено действие, указанное в переменной F100; в нашем случае задано выключение компьютера. Вместо F100, естественно, можно использовать любое другое имя.

/.console-setup), все настройки нужно будет перенести в другой файл, что несколько
неудобно. Если ты все-таки решишься на этот шаг, используй имеющиеся записи как шаблон, ничего не записывая на первую позицию, а в конце не забудь оставить пустую строку.

Немного о ноутбуке

$ sudo mcedit /etc/acpi/events/lid

$ sudo mcedit /etc/acpi/events/power

Это несколько упрощенные варианты, в KUbuntu ты найдешь более сложные скрипты. После этого требуется перезапуск демона acpid:

$ sudo /etc/init.d/acpid restart

Программы настройки

Возможности KeyTouch (keytouch.sf.net) несколько скромнее, эта утилита применяется исключительно для настройки мультимедийных клавиш. Хотя с ее помощью любой клавише можно назначить свое действие, отличающееся от установок по умолчанию. На сайте программы, кроме исходных текстов и пакетов для некоторых дистрибутивов, можно найти готовые настройки для мультимедийных клавиатур большинства известных производителей.

как узнать сканкод клавиши windows. Смотреть фото как узнать сканкод клавиши windows. Смотреть картинку как узнать сканкод клавиши windows. Картинка про как узнать сканкод клавиши windows. Фото как узнать сканкод клавиши windows
Полную версию статьи
читай в январском номере Хакера!

Источник

2 Клавиатура

Программа может использовать клавиатуру по-разному. Она может задерживать свое выполнение до тех пор, пока пользователь не введет какое-нибудь число или пока не нажмет какую-нибудь клавишу. Выполняя некоторую работу, программа может периодически проверять, была ли нажата клавиша, изменяющая режим работы программы. Резидентные программы могут активизироваться, когда пользователь нажимает заранее определенную комбинацию. Можно использовать прерывание, вырабатываемое клавиатурой, например, для завершения работы программы.

Что же касается методики работы с клавиатурой в операционных системах Microsoft Windows, то она была описана в 11 томе «Библиотеки системного программиста», который называется «Операционная система Microsoft Windows для программиста».

Как работает клавиатура

Клавиатура выполнена, как правило, в виде отдельного устройства, подключаемого к компьютеру тонким кабелем. Малогабаритные блокнотные компьютеры содержат встроенную клавиатуру.

Что же находится внутри клавиатуры?

Оказывается, там есть компьютер! Только этот компьютер состоит из одной микросхемы и выполняет специализированные функции. Когда вы нажимаете на клавиши, он посылает номер нажатой клавиши в центральный компьютер.

Клавиатурная матрица

Если рассмотреть сильно упрощенную принципиальную схему клавиатуры, представленную на рисунке, можно заметить, что все клавиши находятся в узлах матрицы (рис. 2.1).

Рис.2.1. Упрощенная схема клавиатуры

Устанавливая по очереди на каждой из вертикальных линий уровень напряжения, соответствующий логическому нулю, клавиатурный компьютер опрашивает состояние горизонтальных линий. Если ни одна клавиша не нажата, уровень напряжения на всех горизонтальных линиях соответствует логической единице (так как все эти линии подключены к источнику питания +5 В через резисторы).

Если вы нажмете на какую-либо клавишу, то соответствующая вертикальная и горизонтальная линии окажутся замкнутыми. Когда на этой вертикальной линии процессор установит значение логического нуля, то уровень напряжения на горизонтальной линии также будет соответствовать логическому нулю.

Как только на одной из горизонтальных линий появится уровень логического нуля, клавиатурный процессор фиксирует нажатие на клавишу. Он посылает в центральный компьютер запрос на прерывание и номер клавиши в матрице. Аналогичные действия выполняются и тогда, когда вы отпускаете нажатую ранее клавишу.

Скан-код клавиши

Номер клавиши, посылаемый клавиатурным процессором, однозначно зависит от схемы клавиатурной матрицы, но не от обозначений, нанесенных на поверхность клавиш. Этот номер называется скан-кодом (Scan Code). Слово scan («сканирование»), подчеркивает тот факт, что клавиатурный компьютер сканирует клавиатуру для поиска нажатой клавиши.

Код ASCII нажатой клавиши

Обычно программе нужен не порядковый номер нажатой клавиши, а код, соответствующий обозначению на этой клавише (код ASCII).

Код ASCII не связан напрямую со скан-кодом, так как одной и той же клавише могут соответствовать несколько значений кода ASCII в зависимости от состояния других клавиш. Например, клавиша с обозначением «1» используется еще и для ввода символа «!» (если она была нажата вместе с клавишей ).

Поэтому все преобразования скан-кода в код ASCII выполняются программно. Как правило, в операционной системе MS-DOS эти преобразования выполняют модули BIOS. Для использования символов кириллицы эти модули расширяются клавиатурными драйверами, как входящими в состав локализованных версий MS-DOS, так и созданными в виде отдельных программ.

Режим автоповтора

Если нажать на клавишу и не отпускать ее, клавиатура перейдет в режим автоповтора. При этом в компьютер автоматически через некоторый период времени, называемый периодом автоповтора, посылается код нажатой клавиши. Режим автоповтора облегчает ввод с клавиатуры большого количества одинаковых символов.

Следует отметить, что клавиатура содержит внутренний 16-байтовый буфер, через который она осуществляет обмен данными с компьютером.

Типы клавиатур

До недавнего времени существовали три различных типа клавиатуры. Это клавиатура для компьютеров IBM PC/XT, 84-клавишная клавиатура для IBM PC/AT и 101-клавишная (расширенная) клавиатура для IBM PC/AT. Некоторые клавиатуры имеют переключатель режима работы (XT/AT), расположенный на нижней крышке. Он должен быть установлен в правильное положение.

После того как операционная система Microsoft Windows получила широкое распространение, специально для нее был создан новый тип клавиатуры. К обычной клавиатуре типа IBM PC/AT были добавлены две кнопки, первая из которых дублирует вызов меню Start, выполняемый при помощи левой клавиши мыши, а вторая – вызов того же меню при помощи правой клавиши мыши.

Порты для работы с клавиатурой

Назначение портов, предназначенных для работы с клавиатурой, зависят от типа компьютера.

Компьютер IBM PC/XT

Для работы с клавиатурой типа IBM PC/XT используются порты с адресами 60h и 61h.

Порт 60h доступен только для чтения. После выполнения этой операции он содержит скан-код последней нажатой клавиши.

Так как порт 61h управляет не только клавиатурой, при изменении содержимого старшего бита необходимо сохранить состояние остальных битов этого порта. Для этого можно сначала выполнить чтение содержимого порта в регистр, изменить состояние старшего бита, затем выполнить запись нового значения в порт:

Современные компьютеры

Современные компьютеры позволяют управлять скоростными характеристиками клавиатуры, а также зажигать или гасить светодиоды Scroll Lock, Num Lock и Caps Lock, расположенные на лицевой панели клавиатуры.

Для расширенного управления клавиатурой применяется порт 60h в режиме записи. Этот порт служит для управления подчиненным процессором Intel 8042, ответственным за обмен данными с клавиатурным компьютером, или его аналогом, установленным на системной плате компьютера.

При использовании порта 60h на запись программа дополнительно получает следующие возможности:

· установка времени ожидания перед переходом клавиатуры в режим автоповтора;

· установка периода генерации скан-кода в режиме автоповтора;

· управление светодиодами, расположенными на лицевой панели клавиатуры.

Процессор 8042 обслуживает не только клавиатуру, но и другие системы компьютера. Через порт 64h, например, выполняется сброс (отключение) процессора 80286 для возврата из защищенного режима работы в реальный. Подробности об этом вы можете узнать из 6 тома «Библиотеки системного программиста», который называется «Защищенный режим процессоров Intel 80286/80386/80486».

Перед тем как посылать команду процессору 8042, необходимо убедиться в том, что его внутренняя очередь команд пуста. Это можно сделать, прочитав слово состояния 8042 из порта с адресом 64h. Бит с номером 1 должен быть равен нулю.

Приведем фрагмент программы, составленной на языке ассемблера, проверяющий состояние очереди команд процессора 8042:

После того, как программа дождется готовности процессора 8042, она может послать ему команду, записав ее в порт с адресом 60h:

Некоторые команды состоят более чем из одного байта. Второй и следующие байты таких команд необходимо записывать в порт 60h, предварительно убедившись в готовности процессора 8042 с помощью приведенной выше последовательности команд. В большинстве случаев можно также использовать простую временную задержку:

Для установки характеристик режима автоповтора в порт 60h необходимо записать код команды 0F3h, затем байт, определяющий характеристики режима. Ниже вы найдете описание полей байта режима автоповтора:

Период автоповтора, который определяет количество посылок скан-кода, генерируемых процессором клавиатуры в одну секунду. Можно использовать не только те значения, которые приведены ниже, но и промежуточные, например, 9 или 15h.

Задержка включения режима автоповтора, mc:

Зарезервировано и должно быть равно нулю

Первоначально при инициализации системы BIOS устанавливает период задержки для включения режима автоповтора равным 500 мс при периоде автоповтора, равным 10 повторам в секунду. Если это слишком медленно для вас, вы можете установить другие значения.

Для управления светодиодами, расположенными на лицевой панели клавиатуры, используйте команду 0EDh. Вслед за этой командой в порт 60h необходимо записать байт, имеющий следующий формат:

1 – включить светодиод Scroll Lock;

0 – выключить светдиод Scroll Lock

1 – включить светодиод Num Lock;

0 – выключить светодиод Num Lock

1 – включить светодиод Caps Lock;

0 – включить светодиод Caps Lock

Программа KBDLED

Приведем пример простейшей программы KBDLED, управляющей светодиодами, расположенными на лицевой панели клавиатуры (листинг 2.1). Такое управление может выполняться только при использовании порта 60h, так как BIOS не содержит никаких подходящих для этого функций. Наша программа после запуска включит все светодиоды и будет ожидать, пока вы не нажмете любую клавишу. Затем программа выключит светодиоды.

Заметим, что программа KBDLED может не работать на виртуальной машине DOS, создаваемой, например, в операционной системе Microsoft Windows NT.

Листинг 2.1. Файл kbdled\kbdled.c

Аппаратное прерывание клавиатуры

Клавиатура подключена к линии прерывания IRQ1. Этой линии соответствует прерывание INT 09h.

Клавиатурное прерывание обслуживается BIOS, однако драйверы клавиатуры и резидентные программы могут организовывать дополнительную обработку прерывания INT 09h. Для этого может быть использована цепочка обработчиков прерывания.

Стандартный обработчик прерывания INT 09h

Как работает стандартный обработчик клавиатурного прерывания, входящий в состав BIOS?

Этот обработчик выполняет следующие действия:

· читает из порта 60h скан-код нажатой клавиши;

· записывает вычисленное по скан-коду значение кода ASCII нажатой клавиши в специальный буфер клавиатуры, расположенный в области данных BIOS;

· устанавливает в единицу бит 7 порта 61h, разрешая дальнейшую работу клавиатуры;

· возвращает этот бит в исходное состояние;

· записывает в порт 20h значение 20h для правильного завершения обработки аппаратного прерывания.

Буфер клавиатуры

Буфер клавиатуры имеет длину 32 байта и расположен в компьютере IBM PC/XT по адресу 0000h:041Eh.

В компьютерах моделей IBM PC/AT и IBM PS/2 расположение клавиатурного буфера задается содержимым двух слов памяти с адресами 0000h:0480h (смещение адреса начала буфера) и 0000h:0482h (смещение конца буфера). Обычно эти ячейки памяти содержат значения, соответственно, 001Eh и 003Eh. Так как смещения заданы относительно сегментного адреса 0040h, то стандартное расположение клавиатурного буфера в IBM PC/AT и IBM PS/2 соответствует его расположению в IBM PC/XT.

Буфер клавиатуры организован циклически. Это означает, что при его переполнении самые старые значения будут потеряны. Две ячейки памяти, находящиеся в области данных BIOS с адресами 0000h:041Ah и 0000h:041Ch содержат, соответственно, указатели на начало и конец буфера. Если значения этих указателей равны друг другу, буфер пуст.

Заметим, что вы можете удалить все символы из буфера клавиатуры, установив оба указателя на начало буфера. Однако есть более предпочтительный способ с использованием прерывания BIOS INT 16h, функции которого мы опишем позже в этой главе.

Указателями на начало и конец клавиатурного буфера обычно управляют обработчики прерываний INT 09h и INT 16h. Программа извлекает из буфера коды нажатых клавиш, используя различные функции прерывания INT 16h.

При переполнении внутреннего буфера клавиатуры или буфера, расположенного в области данных BIOS программа-обработчик прерывания INT 09h генерирует звуковой сигнал.

Переключающие клавиши

Источник

Работа с клавиатурой AT и PS/2

В заметке об обработке прерываний мы столкнулись с контроллером клавиатуры. Работа с контроллером клавиатуры заслуживает отдельной заметки. Вот она.

Литература

Сразу представлю список источников:

Типы клавиатур

В литературе я встречал упоминания нескольких типов клавиатур:

Some chipsets support emulating USB keyboards and mice as standard PS/2 devices, but many chipsets don’t.

В этой заметке мы рассмотрим работу с клавиатурой, совместимой с AT-PS/2.

Действующие лица

При работе с клавиатурой мы имеем следующих участников:

В дальнейшем важно не путать понятия контроллер клавиатуры (микросхема, расположенная на материнской плате компьютера) и клавиатура (устройство с клавишами, которое подключается к системному блоку).

Скан-коды

Когда пользователь нажимает клавишу на клавиатуре, клавиатура посылает контроллеру клавиатуры т. н. скан-код — один или несколько байт, которые содержат информацию о том, какая клавиша была задействована и что именно произошло (нажали ее или отпустили). То есть при нажатии клавиши клавиатура посылает один скан-код (make), а при отпускании — другой (break). Существует три набора скан-кодов, которые так и называются: набор 1, набор 2 и набор 3 (scan codes set 1, set 2, set 3). Клавиатура XT поддерживает только набор 1. Для клавиатур, совместимых с AT-PS/2, гарантированным является только набор 2 [Chapweske], однако контроллер клавиатуры умеет преобразовывать набор 2 в набор 1 (эту опцию контролера клавиатуры можно включать и выключать, об этом ниже).

Scan codes set 1

Большинство скан-кодов набора 1 (как нажатия, так и отпускания) состоит из одного байта. Код отпускания формируется так: break = make AND 0x80, где break — код отпускания, make — код нажатия, AND — побитовое И. Встречаются и двухбайтовые коды. Если код двухбайтовый, то первым байтом всегда является байт 0xE0. В таблице ниже приведено несколько примеров. Особняком стоят клавиши Insert и Pause. У Insert скан-код состоит из четырех байт, у Pause — из шести, причем кода отпускания у Pause нет.

КлавишаКод нажатия (make)Код отпускания (break)
A0x1E0x9E
Insert0xE0, 0x520xE0, 0xD2
Print Screen0xE0, 0x2A, 0xE0, 0x370xE0, 0xB7, 0xE0, 0xAA
Pause0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5

Scan codes set 2

В наборе 2 большинство скан-кодов нажатия (make) однобайтовые. Коды же отпускания состоят из двух байтов, первый из которых 0xF0, а второй совпадает со скан-кодом нажатия. Как и в наборе 1, в наборе 2 встречаются двухбайтовые коды нажатия. И так же как в наборе 1, в наборе 2 первым байтом в двухбайтовом коде нажатия является 0xE0. Если код нажатия двухбайтовый, то код отпускания — трехбайтовый, причем байты идут в следующем порядке: 0xE0, 0xF0, второй байт скан-кода нажатия. И снова особняком стоят клавиши Insert и Pause. У Insert код нажатия состоит их четырех байт, код отпускания — из шести. У Pause код нажатия состоит из восьми байт, а кода отпускания нет вовсе. В таблице ниже приведены примеры скан-кодов набора 2.

КлавишаКод нажатия (make)Код отпускания (break)
A0x1C0xF0, 0x1C
Insert0xE0, 0x700xE0, 0xF0, 0x70
Print Screen0xE0, 0x12, 0xE0, 0x7C0xE0, 0xF0, 0x7C, 0xE0, 0xF0, 0x12
Pause0xE1, 0x14, 0x77, 0xE1, 0xF0, 0x14, 0xF0, 0x77

Scan codes set 3

Набор 3 выглядит лучше, чем два его предшественника, поскольку он более регулярный: в нем все коды нажатия однобайтовые, а все коды отпускания двухбайтовые (первый байт — 0xF0, второй — код нажатия). Примеры приведены в таблице ниже.

КлавишаКод нажатия (make)Код отпускания (break)
A0x1C0xF0, 0x1C
Insert0x670xF0, 0x67
Print Screen0x570xF0, 0x57
Pause0x620xF0, 0x62

Преобразование скан-кодов в ASCII коды

Самый простой вариант преобразования скан-кодов в коды ASCII — это использование массива, элементами которого являются ASCII коды, а индексами — скан-коды. Чтобы описать такой массив для набора скан-кодов 2 на языке ассемблера, мне пришлось скопировать таблицу скан-кодов из Интернета в Microsoft Excel, отсортировать ее по возрастанию скан-кода и, поскольку скан-коды идут не подряд, заполнить пустые места нулевыми символами. Столбец таблицы, в котором содержались ASCII коды, я скопировал в файл с исходным кодом на языке ассемблера и получился вот такой массив:

Порты ввода-вывода 0x64 и 0x60

Как я уже говорил, для общения с контроллером клавиатуры процессор использует порты ввода-вывода 0x64 и 0x60 (эти порты связаны с несколькими регистрами контроллера клавиатуры; размер каждого из регистров — 1 байт). В таблице ниже показано, что означают операции чтения и записи в эти порты.

ПортДоступОписаниеАссемблерная команда
0x60ЧтениеЧитать выходной буфер контроллера клавиатурыin al, 0x60
0x60ЗаписьПисать во входной буфер контроллера клавиатурыout 0x60, al
0x64ЧтениеЧитать регистр статуса контроллера клавиатурыin al, 0x64
0x64ЗаписьПослать команду контроллеру клавиатурыout 0x64, al

Как отмечает в своей статье Adam Chapweske, существует путаница в понятиях «выходной буфер» и «входной буфер». Причем путаница присутствует и в его статье (использование им флагов IBF и OBF не соответствует его же понятиям о входном и выходном буферах). Мы примем следующие определения: если процессор считывает данные из порта 0x60 (это могут быть данные, присылаемые клавиатурой контроллеру клавиатуры, либо данные, которые контроллер клавиатуры формирует самостоятельно в ответ на команду от процессора), то он считывает их из выходного буфера контроллера клавиатуры; если процессор записывает данные в порт 0x60 (он может делать это, что послать данные клавиатуре либо чтобы передать аргумент для команды, которую он послал контроллеру клавиатуры — о командах читайте раздел ниже), то он записывает их во входной буфер контроллера клавиатуры.

Нам понадобится как минимум читать выходной буфер контроллера клавиатуры, потому что именно так мы можем получить скан-коды нажимаемых пользователем клавиш. Когда в выходной буфер контроллера клавиатуры поступает очередной байт, присланный клавиатурой, устанавливается в 1 флаг под названием Output Buffer Full (OBF) — самый младший бит регистра статуса контроллера клавиатуры, и генерируется прерывание IRQ1. Когда вы считываете байт из выходного буфера контроллера клавиатуры, флаг OBF сбрасывается в 0. Перед тем, как считывать данные из выходного буфера контроллера клавиатуры, следует убедиться, что флаг OBF установлен в 1 — для этого надо считать значение регистра статуса. Операцию чтения байта из выходного буфера контроллера клавиатуры я поместил в функцию Keyboard_ReadOutputBuffer (см. раздел Исходный код). Однако если вы читаете байты из выходного буфера внутри обработчика прерывания IRQ1, то и так понятно, что флаг OBF установлен и убеждаться в этом лишний раз нет необходимости.

У нас также может возникнуть необходимость записать что-нибудь во входной буфер контроллера клавиатуры. Такая необходимость может возникнуть в двух случаях:

В любом случае, при записи данных во входной буфер контроллера клавиатуры надо проверять флаг Input Buffer Full (IBF) — следующий после OBF бит регистра статуса контроллера клавиатуры. Записать очередной байт во входной буфер можно только если флаг IBF сброшен в 0. Операцию записи байта в входной буфер контроллера клавиатуры я поместил в функцию Keyboard_WriteInputBuffer (см. раздел Исходный код).

Команды

Возможность посылать команды контроллеру клавиатуры и самой клавиатуре появилась впервые в клавиатуре AT. В PS/2 были добавлены новые команды. Команды позволяют процессору получить от соответствующего устройства информацию (например, считать регистр статуса контроллера клавиатуры) или изменить поведение устройства (например, отключить генерацию прерывания IRQ1 контроллером клавиатуры). Команды могут иметь аргументы (аналогия с параметрами функции). В результате выполнения команды устройство, которому команда адресована, присылает в ответ некую информацию (аналогия с возвращаемым значением функции). Контроллер клавиатуры и сама клавиатура имеют отличающиеся наборы команд, они рассмотрены ниже.

Команды контроллеру клавиатуры

Команда представляет собой число размером один байт. Чтобы послать команду контроллеру клавиатуры, процессор должен записать ее в порт 0x64. Если у команды есть аргументы, то они должны быть последовательно записаны во входной буфер контроллера (порт 0x60). В результате выполнения некоторых команд контроллер помещает в свой выходной буфер число (значение которого можно считать из порта 0x60) — некий результат, аналог возвращаемого значения в функциях. В таблице ниже приведены некоторые команды для контроллера клавиатуры (более полный список есть в [Chapweske] и [Несвижский]).

Название командыКод командыАргументыВозвращаемое значение
Read Command Byte0x20нетcommand byte
Write Command Byte0x60command byteнет
Keyboard interface test0xABнет0x00 — ok;
0x01 — сlock line stuck low;
0x02 — clock line stuck high;
0x03 — data line stuck low;
0x04 — data line stuck high
Mouse interface test0xA9нет0x00 — ok;
0x01 — сlock line stuck low;
0x02 — clock line stuck high;
0x03 — data line stuck low;
0x04 — data line stuck high
Controller self-test0xAAнет0x55 — ok
Enable mouse interface0xA8нетнет
Disable mouse interface0xA7нетнет

Из всех команд контроллера клавиатуры наиболее важными мне кажутся команды Read Command Byte и Write Command Byte. Command Byte — это 8-разрядный регистр контроллера клавиатуры, который содержит флаги, которые влияют на поведение контроллера (см. таблицу ниже).

76543210
ATXLATPCENOVRSYSINT
PS/2XLATEN2ENSYSINT2INT

Наиболее важными мне кажутся следующие флаги (об остальных флагах читайте в [Chapweske]):

Запись байта в порт 0x64 я поместил в функцию Keyboard_SendCommand (см. раздел Исходный код). Поскольку некоторые принимают аргументы, а некоторые — нет, некоторые команды возвращают значение, а некоторые — нет, функцию Keyboard_SendCommand следует использовать вместе с функциями Keyboard_WriteInputBuffer (чтобы передать аргументы команды) и Keyboard_ReadOutputBuffer (чтобы считать возвращаемое значение). В качестве примера использования этих функций ниже приведен код, который отключает трансляцию скан-кодов в набор 1: сначала он считывает command byte из контроллера клавиатуры в регистр al, затем сбрасывает бит XLAT в регистре al в 0, а потом записывает содержимое регистра al обратно в command byte.

Команды клавиатуре

Команда клавиатуре представляет собой число размером 1 байт. Чтобы послать команду клавиатуре, ее надо записать во входной буфер контроллера клавиатуры (порт 0x60). Контроллер клавиатуры пересылает клавиатуре любые данные, записанные в его входной буфер (если только они не являются аргументами команды контроллеру клавиатуры). Команда клавиатуре может иметь аргументы. Аргументы должны быть последовательно записаны во входной буфер контроллера клавиатуры непосредственно после самой команды. В ответ клавиатура всегда присылает байт 0xFA (Acknowledge), который показывает, что команда принята или 0xFE (Resend), который показывает, что с командой что-то пошло не так. Этот байт нужно считать из выходного буфера контроллера клавиатуры. Некоторые наиболее важные с моей точки зрения команды клавиатуре перечислены в таблице ниже:

Название командыКод командыАргументыВозвращаемое значение
RESEND0xFEнетпоследний посланный байт или 0xFE (Resend)
DISABLE0xF5нет0xFA (Acknowledge) или 0xFE (Resend)
ENABLE0xF4нет0xFA (Acknowledge) или 0xFE (Resend)
SET_SCAN_CODE_SET0xF00x01, 0x02 или 0x030xFA (Acknowledge) или 0xFE (Resend)
Echo0xEEнет0xEE (Echo) или 0xFE (Resend)
Set/Reset LEDs0xEDбитовая маска: бит 0 — ScrollLock, бит 1 — NumLock, бит 2 — CapsLock0xFA (Acknowledge) или 0xFE (Resend)

В качестве примера посылки команд клавиатуре приведу код, который сначала посылает клавиатуре команду прекратить сканирование (в результате клавиатура не будет посылать системному блоку скан-коды при нажатии клавиш), а затем — команду возобновить сканирование.

; Disable keyboard
push word KEYBOARD_COMMAND_DISABLE
call Keyboard_WriteInputBuffer
call Keyboard_ReadOutputBuffer

; Enable keyboard
push word KEYBOARD_COMMAND_ENABLE
call Keyboard_WriteInputBuffer
call Keyboard_ReadOutputBuffer

Исходный код — Функции для работы с клавиатурой

Функции и макроопределения для работы с клавиатурой я поместил в файл keyboard.inc.

; AT-PS/2 Keyboard Controller Status Register Bits
KEYBOARD_CONTROLLER_STATUS_PERR equ 10000000b
KEYBOARD_CONTROLLER_STATUS_RxTO equ 01000000b
KEYBOARD_CONTROLLER_STATUS_TO equ 01000000b
KEYBOARD_CONTROLLER_STATUS_TxTO equ 00100000b
KEYBOARD_CONTROLLER_STATUS_MOBF equ 00100000b
KEYBOARD_CONTROLLER_STATUS_INH equ 00010000b
KEYBOARD_CONTROLLER_STATUS_A2 equ 00001000b
KEYBOARD_CONTROLLER_STATUS_SYS equ 00000100b
KEYBOARD_CONTROLLER_STATUS_IBF equ 00000010b ; if IBF=0, Input Buffer is empty and you can write data to it; if IBF=1 Input Biffer is full and you shouldn’t write data to it
KEYBOARD_CONTROLLER_STATUS_OBF equ 00000001b ; if OBF=1, Output Buffer is full and you can read data from it; if OBF=0 Output Buffer is empty and you shouldn’t read data from it

; AT-PS/2 Keyboard Commands (do not confuse «Keyboard» and «Keyboard Controller»)
KEYBOARD_COMMAND_RESET equ 0xFF ; Causes keyboard to enter «Reset» mode
KEYBOARD_COMMAND_RESEND equ 0xFE ; Used to indicate an error in reception.
KEYBOARD_COMMAND_SET_KEY_TYPE_MAKE equ 0xFD ; Allows the host to specify a key that is to send only make codes.
KEYBOARD_COMMAND_SET_KEY_TYPE_MAKE_BREAK equ 0xFC ; Similar to «Set Key Type Make», but both make codes and break codes are enabled (typematic is disabled).
KEYBOARD_COMMAND_SET_KEY_TYPE_TYPEMATIC equ 0xFB ; Similar to previous two commands, except make and typematic is enabled; break codes are disabled.
KEYBOARD_COMMAND_SET_ALL_KEYS_TYPEMATIC_MAKE_BREAK equ 0xFA ; Default setting. Make codes, break codes, and typematic repeat enabled for all keys.
KEYBOARD_COMMAND_SET_ALL_KEYS_MAKE equ 0xF9 ; Causes only make codes to be sent; break codes and typematic repeat are disabled for all keys.
KEYBOARD_COMMAND_SET_ALL_KEYS_MAKE_BREAK equ 0xF8 ; Similar to the previous two commands, except only typematic repeat is disabled.
KEYBOARD_COMMAND_SET_ALL_KEYS_TYPEMATIC equ 0xF7 ; Similar to the previous three commands, except only break codes are disabled. Make codes and typematic repeat are enabled.
KEYBOARD_COMMAND_SET_DEFAULT equ 0xF6 ; Load default typematic rate/delay (10.9cps / 500ms), key types (all keys typematic/make/break), and scan code set (2).
KEYBOARD_COMMAND_DISABLE equ 0xF5 ; Keyboard stops scanning, loads default values (see «Set Default» command), and waits further instructions.
KEYBOARD_COMMAND_ENABLE equ 0xF4 ; Reenables keyboard after disabled using previous command.
KEYBOARD_COMMAND_SET_TYPEMATIC_RATE_DELAY equ 0xF3 ; Host follows this command with one argument byte that defines the typematic rate and delay.
KEYBOARD_COMMAND_SET_SCAN_CODE_SET equ 0xF0 ; Host follows this command with one argument byte, that specifies which scan code set the keyboard should use.
KEYBOARD_COMMAND_ECHO equ 0xEE ; The keyboard responds with «Echo» (0xEE).
KEYBOARD_COMMAND_SET_RESET_LEDS equ 0xED ; The host follows this command with one argument byte, that specifies the state of the keyboard’s Num Lock, Caps Lock, and Scroll Lock LEDs.

; Flags that can be combined to form an argument of command KEYBOARD_COMMAND_SET_RESET_LEDS
KEYBOARD_LED_CAPS_LOCK equ 00000100b
KEYBOARD_LED_NUM_LOCK equ 00000010b
KEYBOARD_LED_SCROLL_LOCK equ 00000001b

; Parts of scan codes
KEYBOARD_SPECIAL_KEY equ 0xE0
KEYBOARD_BREAK_KEY equ 0xF0

Обработка прерывания IRQ1 от контроллера клавиатуры

Прерывание IRQ1 генерируется для каждого байта скан-кода в отдельности, т. е. если например пользователь нажал клавишу Insert, скан-код которой в наборе 2 состоит из двух байт (0xE0, 0x70), то последовательно сгенерируется два прерывания IRQ1. код отпускания клавиши Insert состоит из трех байт, поэтому при ее отпускании сгенерируется три прерывания IRQ1. Ниже показан обработчик прерывания IRQ1, в котором предполагается, что используется scan code set 2. При нажатии на клавишу обработчик прерывания должен получить скан-код, перевести его в ASCII-код соответствующего символа и вывести символ на экран.

; cursor position (0. 1999). initial value of 160 places the cursor to the 4th row on the screen.
cursor dd 240

. exit :
pop edi
pop ax
jmp int_EOI

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *