21.6. Скэн-коды клавиатуры и работа с ними

We use cookies. Read the Privacy and Cookie Policy

Чтение скэн-кодов клавиш — это самый низкий уровень программного опроса клавиатуры. Каждая нажатая клавиша выдает свой

- 506 -

уникальный скэн-код, который зависит только от ее местоположения на клавиатуре и никак не зависит от того, что под ней понимается. Так, арифметические знаки на алфавитной и цифровой клавиатурах имеют различные скэн-коды. Скэн-коды полностью совместимой с IBM PC ПЭВМ приведены в табл. 21. 2.

Таблица 21. 2

Клавиша

Скэн-код

(16)

Скэн-код

(10)

Клавиша

Скэн-код

(16)

Скэн-код

(10)

Esc

$01

1

Z

$2C

44

!1

$02

2

X

$2D

45

@2

$03

3

C

$2E

46

#3

$04

4

V

$2F

47

$4

$05

5

B

$30

48

%5

$06

6

N

$31

49

^6

$07

7

M

$32

50

&7

$08

8

<,

$33

51

*8

$09

9

>.

$34

52

(9

$0A

10

?/

$35

53

)0

$0B

11

Shift(правый)

$36

54

_-

$0C

12

PrintScreen

$37

55

+=

$0D

13

Alt

$38

56

BackSpace

$0E

14

Пробел

$39

57

TAB

$0F

15

CapsLock

$3A

58

Q

$10

16

F1

$3B

59

W

$11

17

F2

$3C

60

E

$12

18

F3

$3D

61

R

$13

19

F4

$3E

62

T

$14

20

F5

$3F

63

Y

$15

21

F6

$40

64

U

$16

22

F7

$41

65

I

$17

23

F8

$42

66

O

$18

24

F9

$43

67

P

$19

25

F10

$44

68

{[

$1A

26

NumLock

$45

69

}]

$1B

27

ScrollLock

$46

70

Enter

$1C

28

7 Home

$47

71

Ctrl

$1D

29

8 Вверх

$48

72

- 507 -

A

$1E

30

9 PgUp

$49

73

S

$1F

31

Серый -

$4A

74

D

$20

32

4 Влево

$4B

75

F

$21

33

$4C

76

G

$22

34

6 Вправо

$4D

77

H

$23

35

Серый +

$4E

78

J

$24

36

1 End

$4F

79

K

$25

37

2 Вниз

$50

80

L

$26

38

3 PgDn

$51

81

:;

$27

39

0 Ins

$52

82

“ '

$28

40

. Del

$53

83

~`

$29

41

F11

$D9

217

Shift (левый)

$2A

42

F12

$DA

218

|

$2B

43

Проверить правильность этой таблицы на любой другой ПЭВМ (с MS-DOS и Турбо Паскалем, конечно) можно при помощи программы опроса скэн-кода нажатой клавиши (рис. 21. 10).

| USES CRT, DOS;

| VAR

| Ch, ExtCh : Char; {символы с клавиатуры }

| Scan, LastScan : Byte; {скэн-коды клавиш }

| OldInt09H : Pointer; {адрес старого вектора }

| {$F+}

| PROCEDURE IntProc; INTERRUPT; {перехват прерывания }

| BEGIN

| Scan:=Port[$60]; {чтение скэн-кода }

| Inline($FF/$1E/>OldInt09H); {возврат прерывания }

| END;

| {$F-}

| BEGIN

| GetIntVec($09, OldInt09H); {взятие адреса прерывания }

| SetIntVec($09, @IntProc); {подстановка перехватчика }

Рис. 21.10

- 508 -

| Scan := 128;

| { стартовое значение Scan }

| WriteLn('Нажимайте что угодно.', 'Esc - выход из программы.');

| repeat { Основной цикл опроса: }

| Ch := #0;

| ExtCh := #0;

| { сброс значений до опроса }

| repeat

| until Scan<128; { ожидание любого нажатия }

| Write( ' Скэн-код=', Scan:3 );

| if KeyPressed { Клавиша - не регистровая? }

| then Ch:=ReadKey; { да, ее код запоминается }

| if KeyPressed and (Ch=#0) {Клавиша - функциональная? }

| then ExtCh := ReadKey; { да, запоминается расш. код }

| { вывод итогов опроса: }

| Write ( 'Символ', Ch + ExtCh );

| GotoXY( 30, WhereY ); { нейтрализация кода 13 }

| WriteLn(('” Код=', Ord( Ch ):3, ' Расш. код=', Ord( ExtCh ) );

| LastScan := Scan; { нужен последний скэн-код }

| Scan := 128; { снова стартовое значение }

| until LastScan=1; { условие конца — нажата Esc }

| SetIntVec($09, OldInt09H);

| { восстановление прерывания }

| ReadLn { пауза до нажатия ввода }

| END.

Рис. 21.10 (окончание)

Программа перехватывает низкоуровневое прерывание номер 9 и запоминает содержимое порта, через который передаются коды нажатых клавиш, в глобальной переменной Scan. После этого анализируется, внесло ли нажатие что-либо в буфер ввода. Если да, то выводится информация о нажатой клавише. Используя перехват прерывания, кaк это сделано в примере, можно проводить и более сложный анализ (рис. 21.11). После каждого нажатия любой клавиши перехватчик записывает в Scan скэн-код. Но здесь есть особенность: при нажатии клавиши вырабатывается истинный скэн-код, а при отпускании — увеличенный на 128. Поэтому в примере ожидание нажатия возложено на цикл

repeat until Scan < 128;

который размыкается только при нажатии клавиши (Scan содержит число, меньшее 128) и не реагирует на отпускание их.

На рис. 21.11 рассматривается каркас Паскаль-программы, позволяющей «отлавливать» одновременное нажатие нескольких регистровых клавиш вместе с алфавитной клавишей или без нее.

Аналогичным методом можно определять факты нажатия практически всех распознаваемых ПЭВМ комбинаций клавиш. Надо

- 509 -

| { КАРКАС ПРОГРАММЫ, РЕАГИРУЮЩЕЙ НА СПЕЦИАЛЬНЫЕ КОМБИНАЦИИ}

| { НАЖАТИЙ НА КЛАВИАТУРЕ }

| USES CRT, DOS;

| {Необходим модуль DOS. CRT нужен для примера. }

| VAR { глобальные переменные программы : }

| OldInt09H : Pointer; { адрес прерывания 09 }

| CtrlRShiftD : Boolean; { флаг нажатия комбинации }

| CONST { Константы специальной комбинации клавиш: }

| HotKey = $20; { скэн-код клавиши [D]; }

| KlavReg = 1+4; { значение в байте $0:$0417 при нажатии }

| { левого регистра Shift вместе с Ctrl : }

| { 1 - нажата правая клавиша Shift (бит 0); }

| { 4 - Нажато Ctrl+Shift (бит номер 2). }

| {$F+}

| PROCEDURE IntProc; INTERRUPT; {перехват прерывания 09Н }

| VAR

| M: Byte absolute $000:$417; { байт состояния регистров }

| C,L,H : Byte; { значение скэн-кода и др. }

| BEGIN

| С := Port[$60]; { чтение скэн-кода }

{Устанавливаем флаг нажатия, анализируя скэн-код и состояние байта нажатия клавиш регистров: }

| CtrlRShiftD:=(C=HotKey) and ((M and KlavReg)=KlavReg);

| if CtrlRShiftD

| then begin { Специальная обработка }

| L:=Port[$61]; H:=L; { портов, если нажата }

| L:=L or $80; { требуемая комбинация }

| Port[$61] :=L;

| Port[$61]:=H;

| Port[$20] :=$20

| end

| else { Иначе пусть выполняется }

| inline($FF/$1E/>OldInt09H); { настоящее прерывание }

| END;

| {$F-}

| VAR

| 1 : Word; {=== ОСНОВНАЯ ЧАСТЬ ПРИМЕРА ==== }

| LABEL Stop;

| BEGIN

| CtrlRShiftD:=False; {обязательное стартовое значение! }

| ClrScr:

| Write('Нажатие Ctrl+Пр.Shift+D приостановит цикл. ');

| GetIntVec($09, OldInt09H); { сохраняем старый вектор }

| SetIntVec($09, @IntProc ); { подставляем новый вектор }

| {...}

Рис. 21.11

- 510 -

| for i:=1 to 30000 do

| begin { рабочий цикл }

| GotoXY( 5,5 );

| Write( 1:6, ' из 30000.');

| { Программа должна периодически проверять, было ли }

| { одновременное нажатие Ctrl+Правая Shift + D ? }

| if CtrlRShiftD

| then { Да. Обработка нажатия горячего ключа }

| begin

| Write('Комбинация нажата...Закончить(Y/N)? ');

| if UpCase( ReadKey )='Y'

| then

| Goto Stop;

| ClrScr;

| { ... }

| end

| else

| { Нет. Дальнейшие действия программы }

| begin

| {...}

| end; {if}

| end; {for i}

| { конец рабочего цикла }

| { ... }

| Stop: { метка выхода из цикла }

| SetIntVec{ $09, OldInt09H );

| { вернем исходный вектор }

| ClrScr

| END.

Рис. 21.11 (окончание)

только устанавливать соответствующие значения скэн-кодов «горячих» клавиш в HotKey и числа для побитового сравнения с байтами $417/$418 в KlavReg.

Недостатком примера является задержка между нажатием комбинации клавиш и началом анализа. Избежать ее можно, если вставить вызовы процедур реакции на «горячие» комбинации прямо в Interrupt-процедуру. Однако не советуем спешить это сделать. При такой организации программы необходимо принимать специальные меры по предохранению содержимого регистров процессора, блокированию прочих прерываний, их согласованию и т.п., о чем можно всегда прочитать в любой толстой книге по системному программированию на языке ассемблера. В большинстве же программ может пригодиться и предложенный способ.