dk lab
Homepage Карта сайта Версия для печати Статьи:
. .
Apache
Содержание
Основы работы с векторами прерываний
Программирование клавиатуры
Программирование таймера
dkLab | Статьи | Программирование клавиатуры
Система Orphus

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

Дмитрий Котеров

На странице:

1. Введение
2. Принцип работы контроллера клавиатуры и его интерфейс с BIOS
3. Программирование клавиатуры
4. Другие возможности

http://formula7r.ru/category/snegohody/ski-doo/utilitarnye/

1. Введение

Зачем может понадобиться низкоуровневое программирование клавиатуры? Самое, пожалуй, главное, это то, что при написании очень многих программ (в основном, конечно, игр) необходимо уметь "улавливать" одновременное нажатие нескольких клавиш (например, одновременное нажатие стрелки вверх и пробела и т.д). Стандартные средства BIOS позволяют это делать, но только не с любыми клавишами, а с функциональными (такими, как Shift, Alt и др). В самом деле, у неискушенного программиста может сложиться впечатление, что, например, Shift - клавиша особенная, так как она, якобы, изменяет значение остальных, в то время как на самом деле она с точки зрения контроллера клавиатуры абсолютно ничем не отличается от всех остальных клавиш. Различия осуществляются только на уровне BIOS.
Другая причина прямого программирования контроллера клавиатуры - это нежелание программиста разрешать BIOS обработку нажатых клавиш, например, с целью блокирования работы комбинаций Ctrl+Break или Ctrl+Alt+Del. Отказ от использования буфера ввода - тоже вынужденная необходимость, так как некоторые версии BIOS при нажатии на клавишу выдают очень короткий звуковой сигнал, который, конечно, будет порить собственные звуки программы (например фоновую музыку).

2. Принцип работы контроллера клавиатуры и его интерфейс с BIOS

Контроллер клавиатуры работает следующим образом: при нажатии или отпускании любой клавиши генерируется байт (так называемый скан-код), первые 7 битов которого содержат порядковый номер клавиши, а последний, седьмой бит, сброшен, если клавиша была нажата, и установлен, если отпущена. Этот скан-код можно прочитать через порт 60h (на самом деле внутри контроллера клавиатуры есть некая очередь скан-кодов, а порт 60h лишь отображает верхний код, но об этом можно и не знать). И еще. Как только клавиша нажимается или отпускается, вызывается 9-е прерывание (IRQ 1).
Но не всегда нажатие или отпускание клавиши генерирует один скан-код. Например, нажатие клавиши Pause вызывает генерацию сразу 5-и кодов. Нажатие белой стрелки вверх вызывает скан-код 72, а черной стрелки вверх - сразу 2 кода: 224 и 72. И для каждого из этих скан-кодов вызывается 9-е прерывание.
Таким образом, процедура обработки клавиатуры BIOS, "сидящая" на 9-м прерывании, просто анализирует значение 60h-го порта и соответствующим образом модифицирует буфер ввода. Все процедуры BIOS далее работают не с текущем значением порта, а с буфером ввода, что позволяет осуществлять ввод с опережением, то есть даже тогда, когда система занята.

3. Программирование клавиатуры

Если встала необходимость прямой работы с клавиатурой, применяется следующий метод: на 9-е прерывание устанавливается "заплата", которая первым делом обрабатывает состояние порта, а затем, при желании, передает управление старому обработчику клавиатуры BIOS. Если управление BIOS не передается, необходимо не забыть команду вывода значениа 20h а порт 20h, чтобы разрешить следующие прерывания от клавиатуры.
Теперь рассмотрим, в чем же заключается обработка порта клавиатуры. Необходимо обеспечить такой механизм, при котором программа смогла бы узнать, какие клавиши в данный момент нажаты, то есть иметь информацию о состоянии каждой клавиши. Предположим, это будет массив из 256-и элементов, по одному на каждую клавишу, где нулевое значение говорит о том, что соответствующая клавиша отпущена, и ненулевое, что нажата. Если полученный процедурой обработки 9-го прерывания скан-код имеет установленный 7-й бит, это значит, что клавиша отпущена, то есть нужно обнулить соответствующий элемент массива, а если сброшен - то присвоить соответствующий элемент в 1. Серые и белые клавиши (например, стрелки) считаются различными. Например, код серой стрелки вверх будет (128+72), а белой - 72.
Рассмотрим примеры работы с клавиатурой на различных языках программирования. В главной программе для "открытия" клавиатуры нужно вызывать OpenKeyboard с параметром, который говорит, нужно ли блокировать обработчик BIOS.

Си

void interrupt (*SvInt09)(void)=NULL; 
int IsBIOSActive=1; 
char KeyPressed[256]; 
char CurKey;
void ProcessKeyb(void)
 { static PrevKey=0;
   char key,IsGray; 
   key=inportb(0x60);
   if(PrevKey==224) IsGray=0x80; else IsGray=0; 
   if(key!=224) /* если не признак черной клавиши"... */
    { if(key&0x80) /* клавиша отпущена */
        KeyPressed[(key&0x7F)|IsGray]=0; 
      else /* клавиша нажата */
        KeyPressed[(key&0x7F)|IsGray]=1; 
    } 
   if(!(key&0x80)) CurKey=key|IsGray;
   PrevKey=key; 
 }
 
void interrupt NewInt09(void)
 { ProcessKeyb();
   if(IsBIOSActive) SvInt09(); /* не блокировать BIOS? */
     else outportb(0x20,0x20); /* ... нужно блокировать */ 
 }
 
void CloseKeyboard(void);  /* предварительное определение */
void OpenKeyboard(int LockBIOS)
 { memset(KeyPressed,0,256); CurKey=0;
   SvInt09=getvect(9); 
   setvect(9,NewInt09); 
   IsBIOSActive=!LockBIOS; 
   atexit(CloseKeyboard); 
 }
 
void CloseKeyboard(void)
 { if(!SvInt09) return; /* клавиатура не открыта */
   setvect(9,SvInt09); SvInt09=NULL; 
 }

Паскаль:

var SvInt09 : procedure;
SvExitProc  : pointer; 
IsBIOSActive: boolean; 
KeyPressed  : array[0..255] of boolean; 
CurKey      : byte;    

procedure ProcessKeyb;
  const PrevKey: byte=0; 
  var key,IsGray: byte; 
  begin
    key:=Port[$60]; 
    if PrevKey=224 then IsGray:=$80 else IsGray:=0; 
    if key<>224 then { если не признак черной клавиши" }
      begin
        if key>127 then { клавиша отпущена }
          KeyPressed[key-128+IsGray]:=false 
        else { клавиша нажата }
          KeyPressed[key+IsGray]:=true;
      end;
    if key<128 then CurKey:=key;
    PrevKey:=key; 
  end;

procedure NewInt09; interrupt;
  begin
    ProcessKeyb; 
    if IsBIOSActive then { не блокировать BIOS }
      begin Inline($9C); SvInt09 end; 
    else Port[$20]:=$20; { если нужно блокировать } 
  end;
 
procedure CloseKeyboard; forward; 
procedure OpenKeyboard(LockBIOS: boolean)
  begin
    FillChar(KeyPressed,256,0); CurKey:=0; 
    GetIntVec(9,@SvInt09); 
    SetIntVec(9,@NewInt09); 
    IsBIOSActive:=not LockBIOS; 
    SvExitProc:=ExitProc; 
    ExitProc:=@CloseKeyboard; 
  end;
 
procedure CloseKeyboard;
  begin
    if @SvInt09=nil then Exit; { клавиатура не открыта }
    SetIntVec(9,@SvInt09); 
    @SvInt09:=nil; 
    ExitProc:=SvExitProc; 
  end;
Теперь в любом (!) месте программы можно анализировать массив KeyPressed, не задумываясь о том, каким образом он обновляется. Например:

Си

if(KeyPressed[72]&&KeyPressed[77]) {...} 
/* если одновременно нажаты вверх и вправо, то ... */ 

Паскаль:

if KeyPressed[72] and KeyPressed[77] then ...
{ если одновременно нажаты вверх и вправо, то ... }

4. Другие возможности

Безусловно, существуют и другие возможности по программированию контроллера клавиатуры (например, включение/выключение ее лампочек). Однако эти возможности используются уж очень редко.
В заключение можно привести одну полезную информацию об обработчике клавиатуры BIOS. Байт памяти с адресом 40h:17h содержит информацию о состоянии специальных клавиш клавиатуры:
Бит 7- INSert активен
Бит 6- CapsLock активен
Бит 5- NumLock активен
Бит 4- ScrollLock активен
Бит 3- Alt нажат
Бит 2- Ctrl нажат
Бит 1- LeftShift нажат
Бит 0- RightShift нажат

Символическое обозначение этого байта можно представить так:

Паскаль:

var BIOSKeybState: byte absolute $40:$17;

Си:

#define BIOSKeybState (*(char far *)0x00400017L)

3 ноября 2000, 17:21
Дмитрий Котеров
dkLab, ©1999-2016