{ KeyBoard Driver v.2.0 }
{ Demjanishin Vladislav
  E-Mail:nitromanit@mail.ru
  http://amonit.boom.ru
  http://amonit.narod.ru}
Unit keydrv;

interface

Uses dos;

const
      ctKEYOFF=0; { ни одна клавиша в данный момент не нажата(пустая клавиша) }
      ctEsc=1;
      ctENTER=28;
      ctUP=72;
      ctDOWN=80;
      ctLEFT=75;
      ctRIGHT=77;
      ctSpace=57;
      ctLSHIFT=42;
      ctCTRL=29;
      ctF1=59;
      ctF2=60;
      ctF3=61;
      ctF4=62;
      ctF5=63;
      ctF6=64;
      ctF7=65;
      ctF8=66;
      ctF9=67;
      ctF10=68;
      ct1=2;
      ct2=3;
      ct3=4;
      ct4=5;
      ct5=6;
      ct6=7;
      ct7=8;
      ct8=9;
      ct9=10;
      ct0=11;
      ctMINES=12;
      ctPLUS=13;
      ctQ=16;
      ctW=17;
      ctE=18;
      ctR=19;
      ctT=20;
      ctY=21;
      ctU=22;
      ctI=23;
      ctO=24;
      ctP=25;
      ctLscob=26;
      ctRscob=27;
      ctA=30;
      ctS=31;
      ctD=32;
      ctF=33;
      ctG=34;
      ctH=35;
      ctJ=36;
      ctK=37;
      ctL=38;
      ctTwoPoint=39;
      ctApostraf=40;
      ctZ=44;
      ctX=45;
      ctC=46;
      ctV=47;
      ctB=48;
      ctN=49;
      ctM=50;
      ctRusB=51;
      ctRusU=52;
      ctRusE=53;

var
    { массив состояния клавиш }

    Button:array [1..128] of boolean;

procedure InitKeyDrv(self:boolean);
procedure KeyDrvDone;
function GetKeyCode:byte;
function InKey:word;
function WaitKey:byte;
procedure ClearKeyBoardBuffer;

implementation

type TRec=record
          l,h:word;
          end;

var int9_old:pointer;
    code,oldcod,key:byte;
    addr:TRec absolute int9_old;
    aseg,aoff:word;

{ инициализируем таблицу клавиш как все отжатые}

procedure InitCodeTab;
var i:word;
begin
for i:=1 to 128 do Button[i]:=false;
end;

{$F+}
{ несамостоятельный обработчик прерывания клавиатуры }

Procedure Int9_newhelp;interrupt;
Begin
 asm
     in al,60h     { читаем скан-код клавиши из порта клавиатуры }
     mov code,al
 end;
if Code<128 then Button[code and $7f]:=true
   else Button[code and $7f]:=false;
 asm
     jmp @int
 @l1:dw 0
 @l2:dw 0
@int:mov ax,aoff          { подготавливаем адрес для прыжка}
     mov word ptr @l1,ax
     mov ax,aseg
     mov word ptr @l2,ax
     pop bp               { восстанавливаем реистры, так как старый их не знает, что их надо восстановить }
     pop es
     pop ds
     pop di
     pop si
     pop dx
     pop cx
     pop bx
     pop ax
     jmp dword ptr CS:[@l1]  { прыжок на старый обработчик прерывания }
 end;
End;

{ несамостоятельный обработчик прерывания клавиатуры }

Procedure Int9_newself;interrupt;
Begin
  asm
     in al,60h    { читаем скан-код клавиши из порта клавиатуры }
     mov code,al
  end;
{Button[code and $7f]:=true;}
if Code<128 then Button[code and $7f]:=true
   else Button[code and $7f]:=false;
  asm
@int:IN     AL,61h   { читаем значение из порта управления устройствами }
     MOV    AH,AL    { дублируем старое значение порта 61H }
     OR     AL,80h   { устанавливаем бит 7 в единицу для строба клавиатуры }
     OUT    61h,AL   { стробируем }
     XCHG   AH,AL    { восстанавливаем старое значение порта 61H }
     OUT    61h,AL   { стбрасываем бит 7 в ноль и этим заканчиваем строб }
     MOV    AL,20h   { эти две строки должны завершать обработчик аппаратного}
     OUT    20h,AL   { прерывания чтобы контроллер узнал, что прерывание
                       обработано, и что можно снять с ножки процессора
                       сигнал возбуждения аппаратного прерывания,
                       эти две строки только для прерываний первого контроллера(IRQ0..7),
                       для второго контроллера(IRQ8..15) во второй сроке должно стоять 0A0h }
  end;
End;
{$F-}

{ читаем адрес старого обработчика прерывания клавиатуры из переменной int9_old }

procedure GetAddr;
begin
aoff:=addr.l;
aseg:=addr.h;
end;

{ Если Self=True - переводим обработку прерываний от клавиатуры полностью на
  свой обработчик, этот режим под отладкой(запуском) в Turbo.exe лучше не
  применять, так как при выходе из отлаживаемой программы
  управление через клавиатуру будет работать не совсем полноценно,
  вообще при этом режиме MSDOS не имеет возможности получать коды
  нажатий клавиш и при переполнении буфера клавиатуры(в области данных BIOS)
  не будет звучать звуковой сигнал PCSpeaker'а

  Если Self=False - переводим обработку прерывания от клавиатуры частично на
  свой обработчик, т.е. просто будем шпионить за нажатием и
  отпусканием клавиш, этот режим безопасен даже под отладкой(запуском)
  в Turbo.exe, но есть один недостаток, так как MSDOS будет иметь
  доступ к кодам клавиш и естественно будет помещать их в буфер клавиатуры
  (в области данных BIOS) и при его переполнении будет звучать звуковой
  сигнал PCSpeaker'а, а при удержании клавиши нажатой естественно очень
  скоро наступит переполнение буфера клавиатуры(в области данных BIOS) и
  звуковой сигнал будет раздражать}

procedure InitKeyDrv(self:boolean);
begin
oldcod:=ctKEYOFF; {1;}
Initcodetab;
GetIntVec(9,int9_old);
GetAddr;
if self then SetIntVec(9,@Int9_newself)
   else SetIntVec(9,@Int9_newhelp);
end;

{ если в начале программы был выполнен вызов процедуры InitKeyDrv, то
  ОЧЕНЬ ВАЖНО незабыть вызвать функцию KeyDrvDone в конце программы перед
  выходом в DOS}

procedure KeyDrvDone;
begin
SetIntVec(9,int9_old);
end;

{ выдаёт новый код нажатой(отпущеной клавиши) и тут же помечает его как
  старый код клавиши, чтобы небыло повторного срабатывания от одной и той же
  клавиши }

function GetKeyCode:byte;assembler;
asm
    mov al,code
    cmp al,oldcod
    je @n1
    mov oldcod,al
    jmp @nd
@n1:mov al,ctKEYOFF
@nd:
end;

{ просто описываем процедуру с приятным названием Inkey для получения
  кода нажатой(keycode) или отпущеной клавиши(keycode+128) }

function InKey:word;
begin
inkey:=GetKeyCode;
end;

{ новый код нажатой(отпущеной клавиши) помечает как старый код клавиши,
  чтобы небыло повторного срабатывания от одной и той же клавиши }

procedure ClearCod;
begin
oldcod:=code;
end;

{ просто описываем процедуру для ожидания нажатия клавиши }

function WaitKey:byte;
var key:byte;
begin
clearcod;
key:=GetKeyCode;
while (key=ctKEYOFF) or (key>=128) do key:=GetKeyCode;
WaitKey:=key;
end;

{ очищает буфер клавиатуры, для того чтобы небыло визга PC'спикера }

procedure ClearKeyBoardBuffer; assembler;
asm
    mov AH,0ch
    mov AH,1
    int 21h
end;

end.