{$X+,S-,R-,I-,L-,O-,B-,D-} {*****************************************} {* Keyboard unit for BP 7.0 *} {* Direct INT 9h support *} {* Written by Alex Grischenko *} {* Modified by Olaf Bartelt for DPMI *} {* (C) AntSoft Lab , 1994 *} {* Version 1.0 30-06-94 *} {*****************************************} Unit Keyboard; interface type DoubleKey = object Left,Right : boolean; function Both : boolean; function Any : boolean; end; LockKey = record Pressed,Locked : boolean; end; KeyEvent = record case Integer of 0: (KeyCode : Word); 1: (CharCode: Char; ScanCode: Byte); end; const SEG0000 : WORD = $0000; k_LShift = $2A00; k_RShift = $3600; k_LAlt = $3800; k_RAlt = $3800 or $8000; k_LCtrl = $1D00; k_RCtrl = $1D00 or $8000; k_PrtScr = $F900; k_SysReg = $F800; k_Pause = $F700; k_Break = $F600; k_CapsLock = $3A00; k_NumLock = $4500; k_ScrollLock = $4600; k_AltCtrlDel = $F200; WasKeybEvent : boolean = false; { Was event from keyboard } Pressed : boolean = false; { TRUE - key pressed, FALSE - released } ESC : boolean = false; Alt : DoubleKey = ( Left : false; Right : false ); Ctrl : DoubleKey = ( Left : false; Right : false ); Shift : DoubleKey = ( Left : false; Right : false ); PrtScr : boolean = false; CapsLock : LockKey = ( Pressed : false; Locked : false ); NumLock : LockKey = ( Pressed : false; Locked : false ); ScrollLock: LockKey = ( Pressed : false; Locked : false ); Pause : boolean = false; CtrlBreak : boolean = false; AltCtrlDel: boolean = false; procedure InitKeyboard; { Initalize driver } procedure DoneKeyboard; { Uninstall driver } function ReadKeyboard : byte; { Read current scancode from keyboard ( } function KeyPressed : boolean; { Keys was pressed? } function ReadKey : char; { For using instead CRT.ReadKey } function ReadChar : char; { Converts scancode to ASC-key } procedure GetKeyEvent(var KEvent : KeyEvent); procedure NullProc; {procedure KeybLights(On : boolean; Light : byte);} const AltCtrlDelproc : procedure = NullProc; { Alt-Ctrl-Del Handler } implementation function DoubleKey.Both : boolean; begin Both:=Right and Left; end; function DoubleKey.Any : boolean; begin Any:=Right or Left; end; const Key : byte = 0; KeyboardSet : boolean = false; KeyCodes : array [1..$58] of word = ( {******** 85 - key **********} {ESC 1 2 3 4 5 6 7 8 9 0 - = BkSp} 27, 49,50,51,52,53,54,55,56,57,48,45,61, 8, {TAB Q W E R T Y U I O P [ ] Enter} 9, 81,87,69,82,84,89,85,73,79,80,91,93, 13, {LCtrl A S D F G H J K L ; ' `} k_LCtrl,65,83,68,70,71,72,74,75,76,59,39,96, {LShift \ Z X C V B N M , . / RShift} k_LShift,92,90,88,67,86,66,78,77,44,46,47, k_RShift, { * LAlt Space CapsLock} 42, k_LAlt, 32, k_CapsLock, {F1 F2 F3 F4 F5 F6 F7 F8 F9 F10} $3B00,$3C00,$3D00,$3E00,$3f00,$4000,$4100,$4200,$4300,$4400, { NumLock ScrollLock} k_NumLock, k_ScrollLock, {Home Up PgUp K - Left K 5 Right K +} $4700,$4800,$4900,$4A2D,$4b00,$4c00,$4d00,$4e2b, { End Down PgDn Ins Del} $4f00,$5000,$5100,$5200,$5300, {******** 101 - key **********} {AltPrtScr F11 F12} $5400, 0, 0, $5700, $5800); ExtCode : byte = 0; ExtExtCode : byte = 0; Extent : boolean = false; var oldint9seg,oldint9ofs : word; Lights : byte ; { Queue : array[0..30] of byte; } QHead,QTail : word; { - Wait keyboard } procedure WaitKeyb; near; assembler; asm push ax @@Wait: in al,64h test al,02h loopnz @@Wait pop ax end; { - Send byte to keyboard port } procedure SendIt; near; assembler; asm cli call WaitKeyb out 64h,al sti end; procedure SetLights; near; assembler; asm (* push ax mov al,0EDh { call SendIt} out 60h,al mov cx,200h @loop: loop @loop mov al,Lights { call SendIt } out 60h,al pop ax *) end; procedure MyInt9(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP: Word); interrupt; label IntEnd,SendEOI; begin asm mov ax, seg @data mov ds,ax mov al,0adh { Disable keyboard } call sendit cli call WaitKeyb { Wait } in al,60h { Get keycode } sti mov key,al; push ax mov al,0AEh call sendit mov al,20h out 20h,al pop ax @@keyEvent: mov WasKeybEvent,1 { Set event flag } mov ah,al and ah,0F0h { Was extented keystroke ? } cmp ah,0E0h jne @NormalCode (* jne @CheckAA { no, check next ext. code AAh } cmp ExtCode,0AAh { Was sequence E0 AA E0 ? } jne @ExtCode { No, set as firts extent code } mov Extent,0 { yes, clear exten flags } mov ExtCode,0 { mov al,91 { Return as Shift key pressed } jmp IntEnd *) @ExtCode: mov Extent,1 { yes, set flag and store extented code } mov ExtCode,al mov WasKeybEvent,0 jmp IntEnd { finish interrupt } @NormalCode: mov ah,al and al,7Fh { mask low 7 bits } cmp al,60h jb @@IsKey cmp al,0A0h jb IntEnd @@IsKey: and ah,80h { check pressing } je @@Pressed mov Pressed,0 { if higher bit set to 1, then key released } jmp @@1 @@Pressed: mov Pressed,1 @@1: mov key,al { store key } mov ah,Pressed {------------------------} cmp al,1 jne @PrtScr mov ESC,ah jmp IntEnd @PrtScr: cmp al,37h jne @next0 cmp ExtCode,0E0h jne IntEnd mov PrtScr,ah @next0: cmp al,2ah jne @next1 cmp ExtCode,0E0h jne @ShiftL @ExtShift: xor ax,ax mov WasKeybEvent,al mov ExtCode,al mov key,al jmp IntEnd @ShiftL: mov Shift.Left,ah jmp IntEnd @next1: cmp al,36h jne @next2 cmp ExtCode,0E0h je @ExtShift mov Shift.Right,ah jmp IntEnd @next2: cmp al,38h jne @next3 cmp ExtCode,0E0h je @RAlt mov Alt.Left,ah jmp IntEnd @Ralt: mov Alt.Right,ah jmp @@ResetExt @next3: cmp al,1Dh jne @next4 cmp ExtCode,0E0h je @RCtrl mov Ctrl.Left,ah jmp IntEnd @RCtrl: mov Ctrl.Right,ah jmp @@ResetExt @next4: cmp al,3ah jne @next5 mov CapsLock.Pressed,ah cmp ah,1 je IntEnd xor CapsLock.Locked,1 xor Lights,4 mov ax,0AEh { call SendIt} call SetLights jmp SendEOI @next5: cmp al,45h jne @next6 mov NumLock.Pressed,ah cmp ah,1 je IntEnd xor NumLock.Locked,1 xor Lights,2 mov ax,0AEh { call SendIt } call SetLights jmp SendEOI @next6: cmp al,46h jne @next7 mov ScrollLock.Pressed,ah cmp ah,1 je IntEnd xor ScrollLock.Locked,1 xor Lights,1 mov ax,0AEh { call SendIt} call SetLights jmp SendEOI @@ResetExt: xor ax,ax mov ExtCode,al mov Extent,al jmp IntEnd @next7: cmp al,53h jne IntEnd end; AltCtrlDel:=pressed and Alt.Any and Ctrl.Any; if AltCtrlDel then AltCtrlDelProc; IntEnd: asm { Interrupt end }{ mov al,0aeh call sendit } SendEOI: { mov al,20h out 20h,al } end; end; procedure InitKeyboard; assembler; asm cmp KeyboardSet,0 jne @@Quit @ClearBufferLoop: mov ah,1 int 16h jz @NoKeyb xor ax,ax int 16h jmp @ClearBufferLoop @NoKeyb: mov ax,3509h int 21h mov oldint9seg,es mov oldint9ofs,bx push ds push cs pop ds mov ax,2509h mov dx,offset MyInt9 int 21h pop ds cli xor ax,ax mov QHead,ax mov QTail,ax mov Key,al xor ax,ax mov es,SEG0000 mov al,byte ptr es:[417h] mov cl,4 shr al,cl mov Lights,al mov KeyboardSet,1 sti @@Quit: end; procedure DoneKeyboard; assembler; asm cmp KeyboardSet,0 je @@Quit xor ax,ax mov es,SEG0000 mov ax,word ptr es:[417h] mov bl,Lights mov cl,4 shl bl,cl and al,10001111b { Set Lights status } or al,bl and ax,111110011110000b mov word ptr es:[417h],ax push ds mov dx,oldint9ofs mov ax,oldint9seg mov ds,ax mov ax,2509h int 21h pop ds @@Quit: end; function ReadKeyboard : byte; Assembler; asm xor ax,ax mov al,Key; mov Key,ah; mov WasKeybEvent,ah end; function KeyPressed : boolean; begin KeyPressed:=WasKeybEvent and Pressed; end; function ReadKey : char; begin if KeyboardSet then begin end else begin Writeln(#7'KEYBOARD.TPU Error : use InitKeyboard first!'); halt; end; end; function ReadChar : char; assembler; const scancode : char = #0; asm cmp ScanCode,0 { if were extented keystrokes } je @@NoScanCode mov al,ScanCode { then return scan code } mov ScanCode,0 jmp @@Quit @@NoScanCode: mov al,0 cmp Key,0 je @@Quit mov bh,al mov bl,Key dec bl shl bx,1 mov ax,[offset KeyCodes + bx] cmp al,0 jne @@Quit mov ScanCode,ah @@Quit: mov key,0 end; procedure GetKeyEvent( var KEvent : KeyEvent); assembler; asm les di,KEvent mov word ptr es:[di],0 cmp WasKeybEvent,0 je @Quit xor bx,bx mov bl,key dec bx shl bx,1 mov ax,[offset KeyCodes + bx] cmp al,0 je @Store mov ah,key @Store: mov word ptr es:[di],ax mov WasKeybEvent,0 mov Key,0 @Quit: end; {-------------------------------} procedure KeybLights(On : boolean; Light : byte); var L : byte; begin if (Light>7) then exit; asm mov al,0EDh out 60h,al mov cx,2000h @loop: loop @loop end; if On then L := Lights or Light else L := Lights and not Light; port[$60]:=L; end; {-------------------------------} procedure NullProc; begin end; var OldExitProc : pointer; procedure ExitProcedure; far; begin DoneKeyboard; ExitProc:=OldExitProc; end; FUNCTION get_selector(segment : WORD) : WORD; VAR selector : WORD; BEGIN {$IFDEF DPMI} ASM MOV AX, $0002 MOV BX, segment INT $31 JNC @@1 MOV AX, segment @@1: MOV selector, AX END; {$ELSE} selector := segment; {$ENDIF} get_selector := selector; END; begin SEG0000 := get_selector($0000); OldExitProc:=ExitProc; ExitProc:=@ExitProcedure; end. { --------------------------- DEMO ------------------------------ } program KeybDemo; { Copyright (c) 1994 by Andrew Eigus Fidonet: 2:5100/33 } uses Crt, Keyboard; const Status : array[Boolean] of String[11] = ('Not pressed', 'Pressed '); Lock : array[Boolean] of String[10] = ('Not locked', 'Locked '); var key : KeyEvent; ch : char; CursorShape : word; Procedure SetCursor(CursorOnOff : boolean); assembler; Asm CMP CursorOnOff,True JNE @@2 CMP BYTE PTR [LastMode],Mono JE @@1 MOV CX,0607h JMP @@4 @@1: MOV CX,0B0Ch JMP @@4 @@2: CMP BYTE PTR [LastMode],Mono JE @@3 MOV CX,2000h JMP @@4 @@3: XOR CX,CX @@4: MOV AH,01h XOR BH,BH INT 10h End; { SetCursor } procedure AltCtrlDelp; far; begin Writeln(#13#10#10'That was it. Not bad, eh?'); SetCursor(True); Halt(1) end; Procedure WriteXY(X, Y : byte; S : string); Begin GotoXY(X, Y); Write(S) End; { WriteXY } Function Hex(W : Word) : string; const hexChars: array [0..$F] of Char = '0123456789ABCDEF'; Begin Hex[0] := #4; Hex[1] := hexChars[Hi(W) shr 4]; Hex[2] := hexChars[Hi(W) and $F]; Hex[3] := hexChars[Lo(W) shr 4]; Hex[4] := hexChars[Lo(W) and $F] End; { Hex } Begin InitKeyboard; AltCtrlDelproc:=AltCtrlDelp; SetCursor(False); TextAttr := LightGray; ClrScr; WriteLn('Keyboard unit demo by Andrew Eigus (c) 1994 Fidonet: 2:5100/33'); WriteLn('Hit any key to scan or Ctrl-Alt-Del to quit.'); repeat GetKeyEvent(Key); WriteXY(1, 5, 'Left Shift state : ' + Status[Shift.Left]); WriteXY(35, 5, 'Right Shift state : ' + Status[Shift.Right]); WriteXY(1, 6, 'Left Alt state : ' + Status[Alt.Left]); WriteXY(35, 6, 'Right Alt state : ' + Status[Alt.Right]); WriteXY(1, 7, 'Left Ctrl state : ' + Status[Ctrl.Left]); WriteXY(35, 7, 'Right Ctrl state : ' + Status[Ctrl.Right]); WriteXY(1, 9, 'Scroll Lock state : ' + Status[ScrollLock.Pressed]); WriteXY(35, 9, 'Scroll Lock toggle : ' + Lock[ScrollLock.Locked]); WriteXY(1, 10, 'Num Lock state : ' + Status[NumLock.Pressed]); WriteXY(35, 10, 'Num Lock toggle : ' + Lock[NumLock.Locked]); WriteXY(1, 11, 'Caps Lock state : ' + Status[CapsLock.Pressed]); WriteXY(35, 11, 'Caps Lock toggle : ' + Lock[CapsLock.Locked]); WriteXY(1, 13, 'PrtScr key state : ' + Status[PrtScr]); if Key.ScanCode and $F0 = $E0 then WriteXY(1, 15, 'Key code : ' + Hex(Key.ScanCode)) else begin WriteXY(1, 16, 'Scan code : ' + Hex(Key.ScanCode and $7F) + ',' + Hex(Key.ScanCode and $7F)); WriteXY(35, 16, 'Key state : ' + Status[Pressed]) end; WriteXY(1, 17, 'Key ASCII code : "' + Key.CharCode + '",' + Hex(Byte(Key.CharCode))); repeat until WasKeybEvent until False End.