http://sulfurzona.com/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Forum
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 

Паскаль для новичков (часть 18)

 
Спрашивали? Отвечаю…

Запряжём клаву

 
Паскаль для новичковСегодня поговорим о клавиатуре, а вернее о том как получать информацию, введённую с клавиатуры. Конечно, в языке Turbo Pascal есть стандартные функции KeyPressed:boolean и ReadKey:char, но чтобы узнать была ли нажата клавиша, необходимо постоянно вызывать первую и если она возвращает true, то следует вызвать и вторую для получения кода нажатой клавиши. При этом следует проверить полученный символ на равенство #0 и если равенство не выполняется, то это значит, что была нажата символьная клавиша.
 
Ну а если всё же равенство выполняется, то необходимо вызвать ещё раз стандартную функцию ReadKey чтобы получить символ, соответствующий нажатой в данный момент специальной клавише, коей может быть любая функциональная клавиша, клавиши управления курсором. При этом следует знать, что клавиши Enter, Tab и BackSpace отнесены к символьным клавишам, т.е. после первого вызова ReadKey будет возвращён символ соответствующий этим клавишам, т.е. #13, #9, #8 – соответственно.
 
Используя данный способ получения информации с клавиатуры необходимо учесть тот факт, что символьные и специальные клавиши могут иметь одинаковые значения типа Char, что может внести путаницу при идентификации нажатой клавиши. Да и сама реализация проверки нажатия и получения кода нажатой клавиши получится несколько громоздкой.
 
Зачем нам такие сложности, тем более что придётся использовать стандартный модуль CRT, о нестабильной работе которого я уже упоминал в предыдущих статьях по практике.
 
А что если описать свою функцию, которая бы в результате вызова своим единственным возвращаемым результатом сразу давала бы знать нажата ли какая-нибудь клавиша и какая именно была нажата. При этом чтобы каждой клавише соответствовал бы уникальный код, который можно было бы применять в конструкциях условных операторов. Используя этот метод, можно будет легко сепарировать коды специальных и функциональных клавиш от символьных клавиш, т.е. сортировать их по функциональному признаку, что очень удобно, и без запутанных чтений расширенного кода клавиатуры, как это требуется при использовании стандартной функции ReadKey.
 
Если вы со мной согласны, то давайте начнём формировать код нашего нового модуля KEYBOARD.PAS. Для начала оформим заголовок модуля и опишем константы для наиболее необходимых кодов клавиш. Следуя традиции Windows называть коды клавиш как виртуальные клавиши (VK – Virtual Key), я предлагаю все константы, соответствующие клавишам называть, используя префикс “VK_”. Таким образом, если понадобится переносить Pascal-программу на Delphi, то с этой стороны проблем не возникнет, так как там приняты константы с аналогичными именами, за исключением некоторых.
 
В виду того, что константы, выстроенные в столбик, займут много строк, а место под статью ограничено, то я разместил десятичные константы в таблице, что экономит место. А вы уж сами перепишите их в модуль столбиком, или скачайте весь модуль целиком. Да, не за горами тот день, когда страницы журналов будут скроллируемые, и не будут рваться как бумага, так как будут из эластичной полимерной плёнки с изменяемой контрастностью. А ещё лучше, если журнал будущего будет иметь гнездо USB, чтобы можно было сразу применить его содержимое, а не бить руками клавиатуру, набирая ссылки, цитаты, сводные таблицы, примеры исходного кода программ и т.д. Что-то меня занесло не туда. Так о чём это я? Ах да…
 
Unit KeyBoard;
 
interface
 
function InKey : word;
function WaitKey : word;
 
Теперь можно приступить к написанию функций.
 
Функция InKeys предназначена для внутреннего использования, поэтому нет нужды её заголовок помещать в блок interface. Её код основан на вызове стандартной функции MS-DOS для получения кода нажатой клавиши без ожидания и без вывода её на экран, к тому же она совсем не реагирует на “Ctrl-Break”. Для вызова этой функции MS-DOS необходимо в регистр AH записать номер функции 6, затем в регистр DL надо поместить значение $0FF. Функция активируется вызовом программного прерывания INT $21, и если при возврате из функции флаг нуля установлен в 1, то буфер клавиатуры пуст, т.е. с момента последнего вызова этой функции новых нажатий клавиш не поступало. Если флаг нуля сброшен в 0, то код нажатой клавиши, а вернее сказать символ, соответствующий нажатой символьной клавише содержится в регистре AL. При этом, если значение регистра AL равно нулю, то значит была нажата специальная клавиша и её код, его ещё называют расширенным, следует получить посредством ещё одного вызова INT $21.
 
implementation
 
function InKeys : word; assembler;
asm
 mov ah,6; mov dl,0ffh; int 21h; jnz @k1
 mov ax,VK_KEYOFF; jmp @nd
@k1:cmp al,0; jz @k2
 mov ah,0; jmp @nd
@k2:int 21h; mov ah,1
@nd:
end;
 
Следует отметить, что функция InKeys проверяет буфер клавиатуры, и если он пуст, то она возвращает значение специальной пустой клавиши VK_KEYOFF=511, что значит, что нажатия клавиши не было. Если к моменту вызова функции InKeys произошло нажатие клавиши, то полученный код анализируется, и если этот код соответствует нажатию символьной клавиши, то функция возвращает код соответствующего символа, т.е. результат колеблется в пределах 0..255, который затем легко преобразовать в тип Char. В том случае, когда нажата специальная клавиша, то функция запрашивает её расширенный код и прибавляет к нему число 256, в итоге значения кодов для специальных клавиш будут варьироваться в пределах 256..510.
 
Следующая функция InKey предназначена для внешнего использования и возвращает код нажатой клавиши в соответствии с тем, что я говорил в предыдущем абзаце. Правда, данная функция, для большего удобства, преобразует коды клавиш Escape, Enter, BackSpace и Tab из символьных в специальные.
 
function InKey : word;
var key : word;
begin
key := inkeys;
 case key of
   27:InKey := VK_ESC;
   13:InKey := VK_ENTER;
     8:InKey := VK_BACKSPACE;
     9:InKey := VK_TAB;
 else InKey := key;
 end;
end;
 
Имея такую функцию легко сконструировать некоторый процесс, который должен завершаться при нажатии на клавишу ESC:
 
uses Keyboard;
begin
repeat
 … { процесс }
until InKey = VK_ESC;
end.
 
Ну и чтобы снова и снова не изобретать велосипед, опишем функцию WaitKey, которая дождётся нажатия клавиши, и вернёт её код:
 
function WaitKey : word;
var key : word;
begin
key := InKey;
while key = VK_KEYOFF do key := InKey;
WaitKey := key;
end;
 
Например, может понадобиться выдать сообщение “Прервать работу программы и выйти в систему?(Y/N)”, и дождаться ответа от пользователя:
 
uses Keyboard;
var key : word;
begin
writeln(‘Выйти в систему?(Y/N)’);
Key := WaitKey;
if (Key = word(‘Y’)) or (Key = word(‘y’)) then halt;
writeln(‘Продолжаем работу.’);
end.
 
Собственно, функции InKey и WaitKey предоставляют исчерпывающий сервис для осуществления управления программой с клавиатуры. Читатели, которые знакомы хотя бы с классическим Бейсиком, наверняка заметили в названии функции InKey что-то до боли знакомое. Совершенно верно, я позаимствовал этот идентификатор из языка программирования Бейсик, ведь я из того поколения программистов, которые начинали с Бейсика, хотя и не VB.
 
Но при разработке программ может понадобиться осуществить интенсивный опрос клавиатуры, и чтобы это сделать, может пригодиться функция:
 
procedure SetKeySpeed( Delay, Rate : byte ); assembler;
asm
 mov ax,0305h; mov bh,Delay; and bh,3;
 mov bl,Rate; and bl,1Fh; int 16h
end;
 
которая позволяет управлять задержкой Delay перед первым повторением нажатия клавиши (допустимые значения указаны в таблице 3) , и автоповтор Rate нажатия клавиши при удержании клавиши в нажатом состоянии (допустимые значения указаны в таблице 4). Из таблиц 3 и 4 следует, что если установить оба параметра функции SetKeySpeed в 0, получим максимально быстрый ввод с клавиатуры, который может пригодиться в текстовом редакторе или в игре.
 
Рискну предположить, что кому-то может понадобиться манипулировать светодиодами на панели клавиатуры, с целью создания примитивной цветомузыки ;-) или при написании собственного драйвера клавиатуры. Для управления светодиодами клавиатуры существует двухбайтная команда $0ED, которую следует послать контроллеру клавиатуры (микросхема микропроцессора Intel 8042), затем дождаться готовности контроллера и послать байт-маску светодиодов, где бит 0, равный 1 включает индикатор ScrollLock, установленный бит 1 включает индикатор NumLock, и бит 2 соответственно – CapsLock. Остальные биты игнорируются. Если сбросить один из трёх бит в 0, то соответствующий светодиод погаснет.
 
Итак, приступим к написанию процедуры SetLed:
 
procedure SetLed( mask : byte ); assembler;
asm
 mov al,0EDh; out 60h,al; call WaitChip8042;
 mov al,Mask; out 60h,al
end;
 
Для посылки управляющей команды следует поместить её код в регистр AL, и послать его в порт $60, после этого дождаться момента, когда буфер очереди команд контроллера клавиатуры окажется пуст, что будет означать, что команда принята к исполнению и контроллер ожидает байт данных для последней команды. Готовность контроллера можно определить с помощью процедуры WaitChip8042, которая будет рассмотрена далее. Когда контроллер будет готов к приёму байта данных, следует в регистр AL поместить байт-маску для светодиодов, структура которого рассматривалась выше и послать его в порт $60.
 
Теперь рассмотрим процедуру WaitChip8042:
 
procedure WaitChip8042; assembler;
asm
 push cx; mov cx,0FFFFh
@kb:in al,64h; test al,00000010b; loopnz @kb
 pop cx
end;
 
которая позволяет определить, готов ли контроллер клавиатуры к приёму информации. Для этого достаточно организовать цикл, например, в $0FFFF итераций, в теле которого будет происходить чтение статуса контроллера (команда in al,64h) в регистр AL, затем производится проверка состояния бита готовности 1 байта статуса (команда test al,00000010b) и если бит сброшен в 0, то цикл прекращается, т.е. мы дождались своего часа ;O)
 
Только при отладке программы не вздумайте выполнять процедуру SetLed в пошаговом режиме. При посылке контроллеру клавиатуры управляющей команды, за которой должен следовать байт данных, произойдёт блокирование ввода с клавиатуры до того момента, пока не будет получен ожидаемый байт данных.
 
Следовательно, очередное нажатие на клавишу F7 или F8 ни к чему не приведут, так как компьютер зависнет (или зависнет задача MS-DOS, если под Windows). Это происходит вследствие того, что контроллер клавиатуры работает в однозадачном режиме, и поэтому если занят приёмом команды, то уж конечно не может сканировать состояние клавиш. Вот когда контроллеры клавиатуры станут многозадачными, уж тогда то … ;o)
 
В качестве примера послужит программка, зажигающая индикаторы клавиатуры:
 
Uses Keyboard, Profiler;
 
begin  
repeat
 SetLed(random(8));
 pause(4);
until InKey = VK_ESC;
end.
 
Мне осталось лишь откланяться, что я и делаю.
 
Литература та же.
 
(функциональные клавиши) Таблица 1
Константа
Код
Константа
Код
VK_F1
VK_F2
VK_F3  
VK_F4  
VK_F5  
VK_F6   
VK_F7   
VK_F8  
VK_F9
VK_F10  
VK_F11  
VK_F12
315
316
317
318
319
320
321
322
323
324
389
390
VK_ShiftF1
VK_ShiftF2
VK_ShiftF3
VK_ShiftF4
VK_ShiftF5
VK_ShiftF6
VK_ShiftF7
VK_ShiftF8
VK_ShiftF9
VK_ShiftF10
VK_ShiftF11
VK_ShiftF12
340
341
342
343
344
345
346
347
348
349
391
392
VK_CtrlF1
VK_CtrlF2
VK_CtrlF3
VK_CtrlF4
VK_CtrlF5
VK_CtrlF6
VK_CtrlF7
VK_CtrlF8
VK_CtrlF9
VK_CtrlF10
VK_CtrlF11
VK_CtrlF12
350
351
352
353
354
355
356
357
358
359
393
394
VK_AltF1
VK_AltF2
VK_AltF3
VK_AltF4
VK_AltF5
VK_AltF6
VK_AltF7
VK_AltF8
VK_AltF9
VK_AltF10
VK_AltF11
VK_AltF12
360
361
362
363
364
365
366
367
368
369
395
396
 
(остальные специальные клавиши) Таблица 2
Константа
Код
Константа
Код
VK_Up
VK_Down
VK_Left
VK_Right
VK_PgUp
VK_PgDown
VK_Home
VK_End
328
336
331
333
329
337
327
335
VK_CtrlUp
VK_CtrlDown
VK_CtrlLeft
VK_CtrlRight
VK_CtrlPgUp
VK_CtrlPgDown
VK_CtrlHome
VK_CtrlEnd
397
401
371
372
388
374
375
373
VK_AltUp
VK_AltDown
VK_AltLeft
VK_AltRight
VK_AltPgUp
VK_AltPgDown
VK_AltHome
VK_AltEnd
VK_Space
408
416
411
413
409
417
407
415
32
VK_ESC
VK_Escape
VK_Enter
VK_Return
VK_BackSpace
VK_Back
VK_Tab
VK_Insert
VK_Delete
283
283
269
269
264
264
265
338
339
 
VK_KeyOff
 
VK_AltQ
VK_AltP
 
511
 
272
281
VK_AltA
VK_AltL
VK_AltZ
VK_AltM
286
394
300
306
 
(задержка перед первым повторением нажатия клавиши) Таблица 3
Delay
Задержка, мс
0
1
2
3
250
500
750
1000
 
(автоповтор нажатия клавиши) Таблица 4
Rate
Скорость, раз/сек
0
..4
..8
..20
..31
30
..20
..15
..5
..2
 
Продолжение следует…
 
© Владислав Демьянишин
 
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
 

Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 18): Запряжём клаву
 
 
 
261
 
ВКонтакте
Facebook
 
 
 
На главную страницу На предыдущую страницу На начало страницы
 
 
Украинский портАл Украина онлайн Рейтинг@Mail.ru Рейтинг Сайтов YandeG Rambler's Top100