unit rezklav;
{Rezidentni obsluha klavesnice. Cte polohove kody, ktere pak pomoci tabulek}
{prevedu do ASCII znaku. Vse si udelam sam a nebudu zavisly na DOSovych}
{ovladacich klavesnice}
{Tato unita umi rozlisovat mezi normalnimi sipkami a sipkami na numericke
klavesnici. Pro pohodli programatora ale rovnez definuje kody, ktere mezi
nimi neodlisuji.
napr. Sipka vpravo na numericke klavesnici dava:
KEY_NUM_6 = true a KEY_CURSOR_RIGHT = true
Seda sipka vpravo dava:
KEY_G_CURSOR_RIGHT = true a KEY_CURSOR_RIGHT = true.
Vidite, ze KEY_CURSOR_RIGHT zustava spolecne.

Jestlize zavolate ZapniObsluhuKlavesnice s parametrem KL_S_BIOSEM, tak dal
budou normalne fungovat standardni funkce klavesnice jako
ReadKey, KeyPressed a vubec cely BIOS okolo klavesnice. Na druhou stranu se
musite starat, aby klavesnice nepipala.
Kdyz zavolate ZapniObsluhuKlavesnice(KL_BEZ_BIOSU) tak pipat zarucene nebude,
ale prestanou fungovat standardni pascali funkce okolo klavesnice.}


{Pri pohledu do zdrojaku zarazi velice bizarni chovani klavesy Pause.
Posila velice exotickou sekvenci a hlavne nesignalizuje uvolneni klavesy.
Zvlastne se taky taky chova Printscreen. Pri zmacknuti totiz vysle 4-bajtovou
sekvenci. Pokud ho ale nepustime, ale drzime dal, tak periodicky vysila
jinou, a to 2-bajtovou sekvenci. Periodicke vysilani maji rovnez oba Alty.}


{$IFDEF FPC}
{$MODE FPC}
  {$IFDEF VER2}{$DEFINE NEWFPC}{$ENDIF}
  {$IFDEF VER3}{$DEFINE NEWFPC}{$ENDIF}
  {$IFDEF NEWFPC}{$CALLING OLDFPCCALL}{$ENDIF}
{$ENDIF}

{$Q-}     {debugovaci informace musi byt v kazdem pripade povypinana}
{$R-}     {jinak se to cele zhrouti}
{$S-}
{$D-}
{$F+}
{$DEFINE OPATRNOST}   {Zda se, ze je mozne ji vypnout...}

interface
{$IFDEF FPC}
uses go32;
{$ELSE}
uses dos;
{$ENDIF}

const KL_S_BIOSEM  = true;
      KL_BEZ_BIOSU = false;
procedure ZapniObsluhuKlavesnice(rezim:boolean);
procedure VypniObsluhuKlavesnice;

Function Rez_Keypressed:boolean;

{Procedure CtiKod;}

{$I rezklav.inc}  {kody klaves}
var

rezklav_interface_lock_start:byte;
kl_kod:word;
kl_zmena:boolean;
vsechny_klavesy : Array[0..160] of boolean;
AltBuf:word;
kl_skipbios:boolean;
rezklav_interface_lock_end:byte;

implementation
const kbdint = $9;
      levy_shift = 42;
      pravy_shift = 54;
      odpocet:byte=0;

var
    {$IFDEF FPC}
    oldint9_handler:tseginfo;
    newint9_handler:tseginfo;
    backupDS:Word; external name '___v2prt0_ds_alias';
    {$ELSE}
    oldint9_handler:pointer;
    newint9_handler:pointer;
    {$ENDIF}

    paltbuf:array[0..6] of byte; {je to zahada, ale se stringem mi to nefungovalo}
    obsluha:pointer;
    bios:boolean;

procedure CtiKod;{$IFNDEF FPC}interrupt;{$ENDIF}
  procedure ZpracujJednotu(alternativa,spolecna,ja:byte);
  begin
  if ja<>0 then vsechny_klavesy[ja]:=(kl_kod<128);
  if kl_kod<128 then vsechny_klavesy[spolecna]:=true else
     if vsechny_klavesy[alternativa]=false then
        vsechny_klavesy[spolecna]:=false;
  end;

  procedure PrectiAltovyBuffer;
  var i,j:longint;
  begin
  if vsechny_klavesy[KEY_ALT]=false then
     begin
     altbuf:=0;
     j:=1;
     for i:=paltbuf[0] downto 1 do
         begin
         altbuf:=altbuf+paltbuf[i]*j;
         j:=j*10;
         end;
     paltbuf[0]:=0;
     end;
  end;

var klmod128,op:byte;
    ab:byte;
    cislo:boolean;

begin
{$IFDEF FPC}kl_kod:=InPortB($60);{$ELSE}kl_kod:=Port[$60];{$ENDIF}
if kl_kod=$e1 then
   if odpocet=0 then odpocet:=7 else else {krkolome osetreni Pause}
if kl_kod=$e0 then
   if odpocet=0 then odpocet:=2 else else

if odpocet<3 then
   begin
   kl_zmena:=true;
   klmod128:=kl_kod mod 128;

   vsechny_klavesy[KEY_PAUSE]:=false;     {vopruz z Pause}
   vsechny_klavesy[KEY_CTRLBREAK]:=false; {stejne se chova CTRL-break}

   op:=odpocet;
   odpocet:=0;
   case op of
      2:case klmod128 of
           69:vsechny_klavesy[KEY_PAUSE]:=true;         {Pause}
           70:vsechny_klavesy[KEY_CTRLBREAK]:=true;     {CTRL-break}
           55:vsechny_klavesy[KEY_PRINT]:=(kl_kod<128); {Printscreen poprve}
        end;

      1:case klmod128 of
           71..83:ZpracujJednotu(klmod128,klmod128+Priznak_jednoty,klmod128+Priznak_sedych_sipek);
           28:ZpracujJednotu(KEY_G_ENTER,KEY_ENTER,KEY_NUM_ENTER);{sedy Enter}
           56:begin
              ZpracujJednotu(KEY_LALT,KEY_ALT,KEY_PALT);          {pravy Alt}
              PrectiAltovyBuffer;
              end;
           29:ZpracujJednotu(KEY_LCTRL,KEY_CTRL,KEY_PCTRL);       {pravy Ctrl}
           55:vsechny_klavesy[KEY_PRINT]:=(kl_kod<128); {Printscreen opakovane}
           42,70:odpocet:=4;  {4-bajtova sekvence E0,neco,E0,neco}
           else vsechny_klavesy[klmod128]:=(kl_kod<128);
        end;

      0:begin
        vsechny_klavesy[klmod128]:=(kl_kod<128);
        case klmod128 of
           71..83:ZpracujJednotu(klmod128+Priznak_sedych_sipek,klmod128+Priznak_jednoty,klmod128);
           28:ZpracujJednotu(KEY_NUM_ENTER,KEY_ENTER,0); {num Enter}
           56:begin
              ZpracujJednotu(KEY_PALT,KEY_ALT,0);        {levy Alt}
              PrectiAltovyBuffer;
              end;
           29:ZpracujJednotu(KEY_PCTRL,KEY_CTRL,0);      {l. Ctrl}
           42:ZpracujJednotu(KEY_PSHIFT,KEY_SHIFT,0);    {l. Shift}
           54:ZpracujJednotu(KEY_LSHIFT,KEY_SHIFT,0);    {p. Shift}
        end;

        if (kl_kod<128) and (vsechny_klavesy[KEY_ALT]) then
           begin {drzime Alt a pritom byla}{zmacknuta klavesa na numericke klavesnici?}
           case kl_kod of
              71:ab:=7;
              72:ab:=8;
              73:ab:=9;
              75:ab:=4;
              76:ab:=5;
              77:ab:=6;
              79:ab:=1;
              80:ab:=2;
              81:ab:=3;
              82:ab:=0;
              56:ab:=11;    {komplikace - periodicke vysilani Altu}
              else ab:=10;
           end;
           if ab<>11 then
              if ab=10 then paltbuf[0]:=0 else
                 if paltbuf[0]<5 then
                    begin
                    inc(paltbuf[0]);
                    paltbuf[paltbuf[0]]:=ab;
                    end
                    else begin
                    paltbuf[0]:=1;
                    paltbuf[1]:=ab;
                    end;
           end;
        end;
   end; {case odpocet}
   if kl_kod>127 then kl_kod:=0 else
      if vsechny_klavesy[KEY_SHIFT] then inc(kl_kod,1000); {musim mit na pameti shifty}
   end;

if odpocet>0 then dec(odpocet);

kl_skipbios:=false;
{KL_skipbios = true: jsou stiskle klavesy, ktere nejsou vhodne na nacitani
                     pres BIOS a je vhodne pouzit prime cteni pole
                     vsechny_klavesy

 KL_sklipbios = false: melo by byt bezpecne dalsi predani klavesy BIOSovemu
                       handleru}

cislo:=false;
for ab:=KEY_1 to KEY_0 do
    if vsechny_klavesy[ab] then cislo:=true;   {je zmackle cislo v hor.rade?}

(*
{????? -- kdyz nasledujici blok zakomentuju, tak v Blocku pri drzeni ALTu
          normalne blika kurzor a lze neco delat}
if vsechny_klavesy[KEY_ALT] then
   if vsechny_klavesy[KEY_SHIFT]
      then kl_skipbios:=kl_skipbios
      else kl_skipbios:=true;
{????? -- kdyz ho necham aktivni, tak se pri drzeni ALTu program jakovy zastavi}
*)


(*{and (cislo=true)} then kl_skipbios:=true;*)


if vsechny_klavesy[KEY_CTRL] then   {zablokuje pro BIOS nektere komb. c CTRL}
   if vsechny_klavesy[KEY_C] then kl_skipbios:=true else
   if cislo=true then kl_skipbios:=true;

{BIOSovy ovladac klavesnice "polyka" kombinace CTRL-C a CTRL-cislo.
 Proto pokud takovou situaci zdetekuji, tak BIOSovy handler preskocim}


{$IFDEF FPC}

if (bios=false) or (kl_skipbios=true) then
   asm
   mov dx,20h
   mov al,20h
   out dx,al
   stc
   end else
   asm
   clc;
   end;

{$ELSE}
if bios=false then
   Port[$20]:=$20 else
   begin
   asm
   call oldint9_handler
   end;
   end;
{$ENDIF}
end;
procedure CtiKod_dummy; begin end;

{$IFDEF FPC}
procedure int9_handler; {$IFDEF NEWFPC}nostackframe;{$ENDIF}assembler;
asm
cli
{$IFDEF OPATRNOST}
push ds
push es
push fs
push gs
pushad
{$ENDIF}
   mov ax,cs:[backupDS]
   mov ds,ax
   mov es,ax
   mov ax,dosmemselector
   mov fs,ax
   call obsluha
{$IFDEF OPATRNOST}
popad
pop gs
pop fs
pop es
pop ds
{$ENDIF}
jc @preskoc_orig_handler  {pokud OBSLUHA nastavi CF, tak preskoc orig. handler}
jmp cs:[oldint9_handler]
@preskoc_orig_handler:

sti
iret  {Nutne. Pokud totiz neskocime do orig. handleru, tak to za nas nikdo jiny nezavola}
end;
procedure int9_dummy; begin end;

procedure int9_nbhandler; {$IFDEF NEWFPC}nostackframe;{$ENDIF}assembler;
asm
cli
{$IFDEF OPATRNOST}
push ds
push es
push fs
push gs
pushad
{$ENDIF}
   mov ax,cs:[backupDS]
   mov ds,ax
   mov es,ax
   mov ax,dosmemselector
   mov fs,ax
   call obsluha
{$IFDEF OPATRNOST}
popad
pop gs
pop fs
pop es
pop ds
{$ENDIF}
sti
iret; {Navrat z preruseni. Variantou by bylo zde IRET vypustit, ale v tom}
end;  {pripade bychom museli celou proceduru volat s modifikatorem INTERRUPT}
procedure int9_nbdummy; begin end;
{$ENDIF FPC}

procedure ZapniObsluhuKlavesnice(rezim:boolean);
begin
kl_zmena:=false;
bios:=rezim;
altbuf:=0;
FillChar(paltbuf,sizeof(paltbuf),0);
FillChar(vsechny_klavesy,sizeof(vsechny_klavesy),0);
obsluha:=@CtiKod;

{$IFDEF FPC}
lock_data(rezklav_interface_lock_start,
          longint(@rezklav_interface_lock_end) - longint(@rezklav_interface_lock_start));

lock_data(obsluha, sizeof(obsluha));
lock_data(bios, sizeof(bios));
lock_data(odpocet, sizeof(odpocet));
lock_data(paltbuf, sizeof(paltbuf));
lock_data(dosmemselector, sizeof(dosmemselector));
lock_data(backupDS, sizeof(backupDS));
lock_data(oldint9_handler, sizeof(oldint9_handler));

lock_code(@CtiKod,longint(@CtiKod_dummy) - longint(@CtiKod));
if bios then
   begin
   lock_code(@int9_handler,longint(@int9_dummy)-longint(@int9_handler));
   newint9_handler.offset:=@int9_handler;
   end
   else begin
   lock_code(@int9_nbhandler,longint(@int9_nbdummy)-longint(@int9_nbhandler));
   newint9_handler.offset:=@int9_nbhandler;
   end;
newint9_handler.segment:=get_cs;
get_pm_interrupt(kbdint, oldint9_handler);
set_pm_interrupt(kbdint, newint9_handler);
{$ELSE}
newint9_handler:=@ctikod;
GetIntVec(kbdint, oldint9_handler);
SetIntVec(kbdint, newint9_handler);
{$ENDIF}
end;

procedure VypniObsluhuKlavesnice;
begin
{$IFDEF FPC}
set_pm_interrupt(kbdint, oldint9_handler);

unlock_data(rezklav_interface_lock_start,
          longint(@rezklav_interface_lock_end) - longint(@rezklav_interface_lock_start));

unlock_data(dosmemselector, sizeof(dosmemselector));
unlock_data(bios, sizeof(bios));
unlock_data(odpocet, sizeof(odpocet));
unlock_data(paltbuf, sizeof(paltbuf));
unlock_data(obsluha, sizeof(obsluha));
unlock_data(backupDS, sizeof(backupDS));
unlock_data(oldint9_handler, sizeof(oldint9_handler));

unlock_code(@CtiKod,longint(@CtiKod_dummy) - longint(@CtiKod));
if bios then
   unlock_code(@int9_handler,longint(@int9_dummy)-longint(@int9_handler)) else
   unlock_code(@int9_nbhandler,longint(@int9_nbdummy)-longint(@int9_nbhandler));
{$ELSE}
SetIntVec(kbdint, oldint9_handler);
{$ENDIF}
end;

Function Rez_Keypressed:boolean;
begin
Rez_Keypressed:=kl_zmena and (kl_kod>0);
kl_zmena:=false;
end;



end.

