{$ifndef unix}
{$A+,B-,D+,E+,F-,G+,I+,L+,N+,O-,P+,Q-,R-,S+,T-,V-,X+,Y+}
{$else}
{$B-,I+,S+,T-,X+}
{$endif}
unit kb;

(* Erweiterte Tastaturfunktionen, erweiterte Warteroutine
   337 Byte Code, 6 Byte Daten

  unterstützt erweiterte Tasten und Tastenkombinationen
  (F12,CTRL-Up...) und korrigiert ein Problem mit CRT.DELAY:
  Auf unterschiedlichen Rechnern ist eine konstante Wartezeit
  erreichbar. Es ist möglich, bis zu eine Stunde zu warten
  ( Werte von 2.000.000.000µs-4.000.000.000µs als 2.000.000.000+?
    eingeben ($Q-) )


  Scancodes:
  ( Alle Zahlen Hexadezimal, deutsche Tastatur )
  ( * = ab AT )
  ( # = zweiter Code nur über kbScanCode ermittelbar )
  ( + = NUMLOCK an )
  ( - = NUMLOCK aus )
  ( Wenn kbenable=FALSE (Standard) erhält man für den
    ersten Scancode statt $E0 $00 )
  ( Kombinierte Scancodes in der Form 1.Code 2.Code )


	Taste	+SHIFT	+CTRL	+ALT

  ESC	1b/01	1b/01	1b/01	00/01

  F1    00/3b	00/54	00/5e	00/68
  F2	00/3c	00/55	00/5f	00/69
  F3	00/3d	00/56	00/60	00/6a
  F4	00/3e	00/57	00/61	00/6b

  F5	00/3f	00/58	00/62	00/6c
  F6	00/40	00/59	00/63	00/6d
  F7	00/41	00/5a	00/64	00/6e
  F8	00/42	00/5b	00/65	00/6f

  F9	00/43	00/5c	00/66	00/70
  F10	00/44	00/5d	00/67	00/71
  F11	00/85*	00/87*	00/89*	00/8b*
  F12	00/86*	00/88*	00/8a*	00/8c*

  ^°    5e/29#	f8/29#  -----	00/29
  1!	31/02#	21/02#	-----	00/78
  2"²	32/03#	22/03#  00/03	00/79
  3§�	33/04#	15/04#	-----	00/7a
  4$	34/05#	24/05#	-----	00/7b
  5%	35/06#	25/06#	-----	00/7c
  6&	36/07#	26/07#  1e/07#	00/7d
  7/{	37/08#	2f/08#	-----	00/08 oder 00/7e
  8([	38/09#	28/09#	-----	00/7f
  9)]	39/0a#	29/0a#	-----	00/80
  0=}	30/0b#	3d/0b#  -----	00/81
  ß?\	e1/0c#  3f/0c#	1f/0c#  00/82
  '`	27/0d#	60/0d#  -----	00/83
 BACKSP 08/0e#  08/0e#  7f/0e	00/0e

  TAB	09/0f#	15/15#	00/94*  00/a5*
  qQ@	71/10#	51/10#	11/10#	00/10
  wW	77/11#	57/11#	17/11#  00/11
  eE	64/12#	44/12#	05/12#  00/12
  rR	72/13#	52/13#	12/13#  00/13
  tT	74/14#	54/14#	14/14#  00/14
  zZ	7a/2c#	5a/2c#	1a/2c#  00/2c
  uU	75/16#	55/16#	15/16#  00/16
  iI	69/17#	49/17#	09/17#  00/17
  oO	6f/18#	4f/18#	0f/18#  00/18
  pP	70/19#	50/19#	10/19#  00/19
  üÜ	81/1a#	9a/1a#	1b/1a#  00/1a
  +*~	2b/1b#	2a/1b#	1d/1b#	00/1b
  <ŽÙ	0d/1c#	0d/1c#	0a/1c#	00/1c*

  aA    61/1e#	41/1e#	01/1e#	00/1e
  sS	73/1f#	53/1f#	13/1f#	00/1f
  dD	64/20#	44/20#	04/20#	00/20
  fF	66/21#	46/21#	06/21#	00/21
  gG	67/22#	47/22#	07/22#	00/22
  hH	68/23#	48/23#	08/23#	00/23
  jJ	6a/24#	4a/24#	0a/24#	00/24
  kK	6b/25#	4b/25#	0b/25#	00/25
  lL	6c/26#	4c/26#	0c/26#	00/26
  öÖ	94/27#	99/27#	-----	00/27
  äÄ	84/28#	8e/28#	-----	00/28
  #'	23/29#	27/29#	1c/29#	00/29

  <>|	3c/56#	3e/56#	-----	-----
  yY	79/15#	59/15#	19/15#	00/15
  xX	78/2d#	58/2d#	18/2d#	00/2d
  cC	63/2e#	43/2e#	03/2e#	00/2e
  vV	76/2f#	56/2f#	16/2f#	00/2f
  bB	62/30#	42/30#	02/30#	00/30
  nN	6e/31#	4e/31#	0e/31#	00/31
  mM	6d/32#	4d/32#	0d/32#	00/32
  ,;	2c/33#	3b/33#	-----	00/33
  .:	2e/34#	3a/34#	-----	00/34
  -_	2d/35#	5f/35#	-----	00/35

 SPACE	20/57#	20/57#	20/57#	20/57#

ZIFFERNBLOCK:
~~~~~~~~~~~~~
  /     2f/e0#   2f/e0#  00/95* 00/a4*
  *     2a/e0#   2a/e0#  00/96* 00/00*???
  -     2d/e0#   2d/e0#  00/8e* 00/4a*
  +     2b/e0#   2b/e0#  00/90* 00/4e*
 ENTER  0d/e0#   0d/e0#  0a/e0* 00/a6*
  1     31/4f+#  00/4f+  00/75  -----
 END    00/4f-   31/4f-# 00/75  -----
  2     32/50+#  00/50+  00/91* -----
 DOWN   00/50-   32/50-# 00/91* -----
  3     33/51+#  00/51+  00/76  -----
 DgDn   00/51-   33/51-# 00/76  -----
  4     34/4b+#  00/4b+  00/73  -----
 LEFT   00/4b-   34/4b-# 00/73  -----
  5     35/4c+#  00/4c+  00/8f  -----
        00/4c-   35/4c-# 00/8f  -----
  6     36/4d+#  00/4d+  00/74  -----
 RIGHT  00/4d-   36/4d-# 00/74  -----
  7     37/47+#  00/47+  00/77  -----
 HOME   00/47-   37/47-# 00/77  -----
  8     38/48+#  00/48+  00/8d* -----
 UP     00/48-   38/48-# 00/8d* -----
  9     39/49+#  00/49+  00/84  -----
 PgUp   00/49-   39/49-# 00/84  -----
  0     30/52+#  00/50+  00/92* -----
 INSERT 00/52-   30/50-# 00/92* -----
    Ú . 2e/53+#  00/50+  00/93* -----
oder´   00/53-   2e/50-# 00/93* -----
    À , 2c/53+#  00/50+  00/93* -----
 DEL    00/53-   2c/50-# 00/93* -----

CURSORBLOCK:
~~~~~~~~~~~~
 UP     e0/48	e0/48	e0/8d*	00/98
 DOWN	e0/50	e0/50	e0/91*	00/a0
 LEFT	e0/4b	e0/4b	e0/73	00/9b
 RIGHT	e0/4d	e0/4d	e0/74	00/9d
 INSERT	e0/52	e0/52	e0/92*	00/a2
 DELETE	e0/53	e0/53	e0/93*	00/a3
 HOME	e0/47	e0/47	e0/77	00/97
 END	e0/4f	e0/4f	e0/75	00/9f
 PgUp	e0/49	e0/49	e0/84	00/99
 PgDn	e0/51	e0/51	e0/76	00/a1 *)

interface

const
 kbenable:boolean=false;
 { Erlaubt erweiterte Scancodesequenzen mit #$E0
   Ist kbenable=TRUE, so kann #$E0 sowohl als
   Kennzeichen für erweiterte Tastenkombinationen
   oder als #224='…' auftreten. Im zweiten Fall
   liefert kbscancode das Ergebniss 0, ein erneutes
   kbreadkey würde auf den nächsten Tastendruck warten. }

type
 kb_delay=0..3;
 kb_rate=0..31;

function kbkeypressed:boolean;
{ True, wenn noch auslesbare Zeichen vorhanden }

function kbreadkey:char;
{ Liest Zeichen von der Tastatur ein.
  Erweiterten Zeichencodes werden zurückgeliefert,
  in dem zunächst das Zeichen #0/#$E0 und bei einem
  weiteren Aufruf der BIOS-Scancode zurückgeliefert
  wird. Ist kbenable=True, so kann
  Falls keine Zeichen eingegeben wurden, wird
  auf den nächsten Tastendruck gewartet.
  ACHTUNG: #$E0 kann mit ALT-224 eingegeben werden!
  In diesem Fall wird kein zweiter Code zurückgeliefert!
  Ist kbenable=true muß dies mit scancode geprüft werden
  (scancode=0). }

function kbxreadkey:char;
{ Liest Zeichen von der Tastatur ein.
  Erweiterten Zeichencodes werden zurückgeliefert,
  in dem zunächst das Zeichen #0/#$E0 und bei einem
  weiteren Aufruf der BIOS-Scancode zurückgeliefert
  wird.
  Falls keine Zeichen eingegeben wurden, wird
  die Folge #0 #255 zurückgeliefert.
  ACHTUNG: #$E0 kann mit ALT-224 eingegeben werden!
  In diesem Fall wird kein zweiter Code zurückgeliefert!
  Ist kbenable=true muß dies mit scancode geprüft werden
  (scancode=0). }

function kbscancode:byte;
{ Liefert den Scancode des zuletzt mit (x)readkey
  ausgelesenen Zeichens zurück. Wurde (x)readkey
  noch nicht angewendet, ist das Ergebnis undefiniert }

function kbstatus:word;
{ Liefert den Tastaturstatus zurück
   Bit #	Bedeutung
	 0	rechte Shift-Taste gedrückt
	 1	linke Shift-Taste gedrückt
	 2	Ctrl-Taste (Strg) gedrückt
	 3	Alt-Taste gedrückt
	 4	Scroll-Lock an
	 5	Num-Lock an
	 6	Caps-Lock an
	 7	Insert an
	 8	Linke Ctrl-Taste gedrückt
	 9	Linke ALT-Taste gedrückt
	10	*Sys Rq gedrückt
	11	*Pause aktiv
	12	*Scroll-Lock gedrückt
	13	*Num-Lock gedrückt
	14	*Caps-Lock gedrückt
	15	*Insert gedrückt

	*=ab AT unterstützt
}

procedure kbsetrate(r:kb_rate;d:kb_delay);
{ Setzt die Wiederholungsrate für gedrückt gehaltene Tasten

  rate:	Wert	Wiederholungsrate(Zeichen/Sekunde)
	00	30.0
	01	26.7
	02	24.0
	03	21.8
	04	20.0
	05	18.5
	06	17.1
	07	16.0
	08	15.0
	09	13.3
	10	12.0
	11	10.9
	12	10.0
	13	 9.2
	14	 8.6
	15	 8.0
	16	 7.5
	17	 6.7
	18	 6.0
	19	 5.5                  
	20	 5.0
	21	 4.6
	22	 4.3
	23	 4.0
	24	 3.7
	25	 3.3
	26	 3.0
	27	 2.7
	28	 2.5
	29	 2.3
	30	 2.1
	31	 2.0

 delay: Wert	Verzögerung in Sekunden
	0	¼
	1        ½
	2       ¼
	3	 1
}

procedure kbdelay(ms:longint);
{ Wartet ms µs (1.000.000 µs = 1 Sekunde)
  Maximale Wartezeiz: 1 h (3.600.824.176)
  (CRT.DELAY : 1 min)
  Genauigkeit:	±27 485,8 µs (27,5 ms) }

procedure KBWrite(c,s:char);
procedure KBreset;
const
 kbfunc:byte=0;
 { 0:PC(86) 1:MF2(101/102) 2:122 Tasten }

implementation

{$ifndef unix}
var
 kbscan:byte;
{$else}
uses xcrt;
{$endif}
const
 kbxkey:boolean=false;

procedure KBreset;
begin
 while kbkeypressed do kbreadkey;
end;

procedure KBWrite(c,s:char);
{$ifndef unix}
assembler;
asm
 mov ah,5
 mov cl,c
 mov ch,s
 int $16
{$else}
begin
{$endif}
end;

function kbkeypressed:boolean;
{$ifndef unix}
assembler;
asm
 mov al,kbxkey
 or al,al
 jnz @kbpressed
 mov ah,[kbfunc]
 inc ah
 int 16h
@kbpressed:
 mov al,1
 jnz @notempty
 xor al,al
@notempty:
{$else}
begin
	return Keypressed;	
{$endif}
end;

function kbreadkey:char;
{$ifndef unix}
assembler;
asm
 mov al,0
 or al,[kbxkey]
 jz @newkread
 dec [kbxkey]
 mov al,[kbscan]
 jmp @ende
@newkread:
 mov ah,[kbfunc]
 int 16h
 mov [kbscan],ah
 or al,al
 jnz @noxkbdpress1
 inc [kbxkey]
@noxkbdpress1:
 cmp al,$e0
 jne @noxkbdpress
 or ah,ah
 je @noxkbdpress
 inc [kbxkey]
@noxkbdpress:
@ende:
 cmp al,$e0
 jne @nochange
 xor bx,bx
 or kbenable,bl
 jnz @nochange
 xor ax,ax
@nochange:
{$else}
begin
	return readkey;
{$endif}
end;

function kbxreadkey:char;
{$ifndef unix}
assembler;
asm
 mov al,0
 or al,[kbxkey]
 jz @newkread
 dec [kbxkey]
 mov al,[kbscan]
 jmp @ende
@newkread:
 call kbkeypressed
 or al,al
 jnz @readkb
 inc al
 mov [kbxkey],al
 dec al
 dec al
 mov [kbscan],al
 inc al
 jmp @ende
@readkb:
 mov ah,[kbfunc]
 int 16h
 mov [kbscan],ah
 or al,al
 jnz @noxkbdpress1
 inc [kbxkey]
@noxkbdpress1:
 cmp al,$e0
 jne @noxkbdpress
 or ah,ah
 je @noxkbdpress
 inc [kbxkey]
@noxkbdpress:
@ende:
 cmp al,$e0
 jne @nochange
 xor bx,bx
 or kbenable,bl
 jnz @nochange
 xor ax,ax
@nochange:
{$else}
begin
	if kbxkey then begin
		kbxkey:=false;
		return #255;
	end else if keypressed then begin
		return readkey;
	end else begin
		kbxkey:=true;
		return #0;
	end;
{$endif}
end;


function kbscancode:byte;
{$ifndef unix}
assembler;
asm
 mov al,[kbscan]
{$else}
begin
	return X.kbscan;
{$endif}
end;

function kbstatus:word;
{$ifndef unix}
assembler;
asm
 mov al,[kbfunc]
 or al,al
 jz @xt
 mov ah,12h
 int 16h
 jmp @ende
@xt:
 mov ah,2
 int 16h
 mov ah,al
 shr ah,1
 shr ah,1
 and ah,3
@ende:
{$else}
begin
	return 0;
{$endif}
end;

procedure kbsetrate(r:kb_rate;d:kb_delay);
{$ifndef unix}
assembler;
asm
 mov ax,0305h
 mov bh,[d]
 mov bl,[r]
 int 16h
{$else}
begin
{$endif}
end;

procedure kbdelay(ms:longint);
{$ifndef unix}
assembler;
asm
(*
 xor ax,ax
 or al,[kbfunc]
 jz @olddelay
 mov ah,86h
 mov dx,word ptr [ms]
 mov cx,word ptr [ms+2]
 int 15h		{ AT-BIOS-Warteroutine aufrufen }
 jmp @waitend
@olddelay:
*)
 mov ax,$40
 mov es,ax
 mov ax,word ptr [ms]
 mov dx,word ptr [ms+2]
 add ax,27022
 adc dx,0		{ dx:ax=Wartezeit }
 mov bx,54945
 div bx
 or ax,ax               { ax:Wartezeit in 1/18,2 sec }
 jz @waitend
 mov cx,[es:$6c]
 mov bx,cx	{ bx=Zählerstand Anfang }
 add cx,ax	{ cx=Zählerstand Ende	}
 jnc @warpend 	{ Wenn kein Zählerüberlauf während Warteperiode }
@warploop:	{ Überlaufwarteschleife überspringen }
 mov dx,[es:$6c]
 cmp dx,bx
 jae @warploop  { Warten bis Zählerüberlauf erfolgt }
@warpend:
@waitloop:
 mov dx,[es:$6c]
 cmp dx,cx
 jnge @waitloop	{ Warten, bis Endzählerstand erreicht/überschritten }
@waitend:
{$else}
begin
	delay(ms div 1000);
{$endif}
end;

begin
{$ifndef unix}
 asm
  mov ax,0900h
  int 16h
  mov ah,al
  xor al,al
  test ah,40h
  jz @no_122
  mov al,20h
  jmp @ende
 @no_122:
  test ah,20h
  jnz @no_101_102
  add al,10h
 @no_101_102:
 @ende:
  mov [kbfunc],al
 end;
{$else}
{$endif}
end.