3.3.2 Функції DOS для роботи з клавіатурою
Функція 01h ( тобто переривання 21h при значенні AH=01h ) виконує введення з клавіатури з очікуванням натиснення клавіші, якщо буфер клавіатури порожній. Код введеного символу поміщається в регістр AL. Введений символ відображується на екрані ( эхо-отображение).
Функція 01h перевіряє також, чи не натискував користувач в ході роботи програми комбінацію клавіш Ctrl/C ( або Ctrl/Break ). В цьому випадку управління передається на підпрограму обробки Ctrl/C, яка зазвичай припиняє виконання програми користувача.
Якщо на клавіатурі натискувала одна з клавіш, яким не відповідає ніякий код ASCII, то функція 01h повертає в регістрі AL значення 0. У цих випадках слід ще раз викликати ту ж функцію, тоді буде виданий розширений код даної клавіші або комбінації клавіш.
Функція 08h працює аналогічно 01h, за винятком того, що не виконується эхо-отображение введеного символу.
Функція 07h працює аналогічно 01h, за винятком того, що не виконується эхо-отображение і не перевіряється натиснення Ctrl/C.
Функція 06h може виконувати як введення з клавіатури, так і вивід на екран. Якщо у момент виклику регістр DL містить значення 0ffh, то дана функція виконує введення без очікування. Якщо буфер не порожній, то прапор нуля ZF скидається в 0, а код символу з буфера заноситься в AL. Якщо ж буфер порожній, то встановлюється ZF=1, при цьому значення в AL не грає ролі. Таким чином, ця функція не чекає, поки натискуватиме клавіша, а відразу видає якийсь результат. Функція 06h не виконує эхо-отображения і не перевіряє натиснення Ctrl/C.
Функція 0Вh не виконує введення символу, а тільки перевіряє, чи є символи в буфері. Якщо є, то встановлюється AL=0ffh, якщо немає, то AL=00h. Виконується також перевірка на Ctrl/C.
Функція 0Аh виконує введення рядка, що буферизує, з клавіатури. При цьому символи вводяться один за іншим, як при багатократному застосуванні функції 01h, до тих пір, поки не буде введений код 0dh ( код клавіші "Enter" ), що завершує рядок. В ході введення рядка користувач може редагувати рядок, і зокрема - використовувати "забій". При введенні виконується також перевірка на Ctrl/C.
При виклику функції 0Аh потрібний, щоб в регістрі DX містив зсув ( у сегменті даних ) області пам'яті ( буфера ), в яку система помістить введений рядок. У першому байті цього буфера має бути записана його довжина, тобто максимальне число символів ( включаючи 0dh ), яке можна записати в буфер. Ця довжина має бути принаймні на 2 менше, ніж число зарезервованих байт. Після закінчення введення рядка функція поміщає в другий байт буфера дійсне число введених символів ( не рахуючи 0dh ), а зачинаючи з третього байта буфера розміщуються введені символи. Останнім завжди буде код 0dh.
Функція 0Сh спочатку очищає буфер клавіатури ( тобто "забуває" попередні натиснення клавіш ), а потім виконує будь-яку з функцій 01h, 06h, 07h, 08h або 0Аh. Номер цієї функції задається в регістрі AL.
Розглянемо приклади роботи з клавіатурою. У головній програмі для "відкриття" клавіатури потрібно викликати OpenKeyboard з параметром, який говорить, чи потрібно блокувати обробник BIOS.
void irq_keyboard(void)
{
char scan,tmp;
/* отримуємо сканкод */
scan = inb(0x60);
/* проводимо його обробку */
...
/********************************/
/* ось ця частка мені не зрозуміла */
/********************************/
/* через порт 0x61 відключають клавіатуру */
tmp = inb(0x61);
outb(tmp|0x80,0x61);
/* потім назад включають */
outb(tmp,0x61);
/********************************/
/* скидання контроллера переривань */
outb(0x20,0x20);
}
Якщо встала необхідність прямої роботи з клавіатурою, застосовується наступний метод: на 9-е переривання встановлюється "латка", яка насамперед обробляє стан порту, а потім, за бажання, передає управління старому обробникові клавіатури BIOS. Якщо управління BIOS не передається, необхідно не забути команду виведення значениа 20h а порт 20h, щоб вирішити наступні переривання від клавіатури.
IDEAL
P386
MODEL MEDIUM
STACK 400
DATASEG
PressCounter DW ?
OldKbdIntOffset DW ?
OldKbdIntSegment DW ?
CharInputFlag DB 0
ASCIICode DB 0
label RusNorm byte
DB 0,0,'1','2','3','4','5','6'
DB '7','8','9','0','-','=',0,0
DB 'й','ц','у','к','е','н','г'
DB 'ш','щ','з','х','ъ',0,0
DB 'ф','ы','в','а','п','р','о'
DB 'л','д','ж','э','Ё',0,'\'
DB 'я','ч','с','м','и','т','ь'
DB 'б','ю','.',0,'*',0,' '
ENDS
CODESEG
PROC KeyboardDriver
mov AX, DGROUP
mov DS,AX
mov [PressCounter],0
call SetKeyboardInterrupt
@@NextChar:
cmp [CharInputFlag],0
je @@NextChar
mov [CharInputFlag],0
mov DL,[ASCIICode]
mov AH,2h
int 21h
inc [PressCounter]
cmp [PressCounter],80
jb @@NextChar
call RestoreOldKeyboardInterrupt
mov AH,4Ch
int 21h
ENDP KeyboardDriver
proc KeyboardInterrupt far
pusha
push DS
mov AX,DGROUP
mov DS,AX
in AL,60h
push AX
mov AL,20h
out 20h,AL
pop AX
sti
cmp AL,39h
ja @@End
xor BX,BX
mov BL,AL
add BX, offset RusNorm
mov AL,[BX]
cmp AL,32
jb @@END
mov [ASCIICode],AL
mov [CharInputFlag],1
@@End: pop DS
popa
iret
endp KeyboardInterrupt
PROC SetKeyboardInterrupt NEAR
pusha
push ES
mov AX,0
mov ES,AX
mov AX,[ES:9*4]
mov [OldKbdIntOffset],AX
mov ax,[ES:9*4+2]
mov [OldKbdIntSegment],AX
cli
mov AX,offset KeyboardInterrupt
mov [ES:9*4],AX
mov AX,CS
mov [ES:9*4+2],AX
sti
pop ES
popa
ret
ENDP SetKeyboardInterrupt
PROC RestoreOldKeyboardInterrupt NEAR
pusha
push ES
mov AX,0
mov ES,AX
cli
mov AX,[OldKbdIntOffset]
mov [ES:9*4],AX
mov AX,[OldKbdIntSegment]
mov [ES:9*4+2],AX
sti
pop ES
popa
ret
ENDP RestoreOldKeyboardInterrupt
ENDS
END
А ось приклад розробки програми на мові С:
void interrupt (*SvInt09)(void)=NULL;
int IsBIOSActive=1;
char KeyPressed[256];
char CurKey;
void ProcessKeyb(void)
{ static PrevKey=0;
char key,IsGray;
key=inportb(0x60);
if(PrevKey==224) IsGray=0x80; else IsGray=0;
if(key!=224) /* если не признак черной клавиши"... */
{ if(key&0x80) /* клавиша отпущена */
KeyPressed[(key&0x7F)|IsGray]=0;
else /* клавиша нажата */
KeyPressed[(key&0x7F)|IsGray]=1;
}
if(!(key&0x80)) CurKey=key|IsGray;
PrevKey=key;
}
void interrupt NewInt09(void)
{ ProcessKeyb();
if(IsBIOSActive) SvInt09(); /* не блокировать BIOS? */
else outportb(0x20,0x20); /* ... нужно блокировать */
}
void CloseKeyboard(void); /* предварительное определение */
void OpenKeyboard(int LockBIOS)
{ memset(KeyPressed,0,256); CurKey=0;
SvInt09=getvect(9);
setvect(9,NewInt09);
IsBIOSActive=!LockBIOS;
atexit(CloseKeyboard);
}
void CloseKeyboard(void)
{ if(!SvInt09) return; /* клавиатура не открыта */
setvect(9,SvInt09); SvInt09=NULL;
}
Безумовно, існують і інші можливості по програмуванню контроллера клавіатури (наприклад, включение/выключение її лампочок). Проте ці можливості використовуються вже дуже рідко.
На закінчення можна привести одну корисну інформацію про обробника клавіатури BIOS. Байт пам'яті з адресою 40h:17h містить інформацію про стан спеціальних клавіш клавіатури:
Бит 7 - INSert активний
Бит 6 - CapsLock активний
Бит 5 - NumLock активний
Бит 4 - ScrollLock активний
Бит 3 - Alt натиснутий
Бит 2 - Ctrl натиснутий
Бит 1 - LeftShift натиснутий
Бит 0 - RightShift натиснутий
Висновок
Навіщо може знадобитися низькорівневе програмування клавіатури? Саме, мабуть, головне, це те, що при написанні дуже багатьох програм (в основному, звичайно, ігор) необхідно уміти "уловлювати" одночасне натиснення декількох клавіш (наприклад, одночасне натиснення стрілки вгору і пропуску і т.д). Стандартні засоби BIOS дозволяють це робити, але тільки не з будь-якими клавішами, а з функціональними (такими, як Shift, Alt і др). Насправді, у недосвідченого програміста може скластися враження, що, наприклад, Shift - клавіша особлива, оскільки вона, нібито, змінює значення останніх, тоді як насправді вона з погляду контроллера клавіатури абсолютно нічим не відрізняється від всіх останніх клавіш. Відмінності здійснюються тільки на рівні BIOS.
Інша причина прямого програмування контроллера клавіатури - це небажання програміста вирішувати BIOS обробку клавіш, що натискують, наприклад, з метою блокування роботи комбінацій Ctrl+Break або Ctrl+Alt+Del. Відмова від використання буфера введення - теж вимушена необхідність, оскільки деякі версії BIOS при натисненні на клавішу видають дуже короткий звуковий сигнал, який, звичайно, буде порить власні звуки програми (наприклад фонову музику).
Список літератури
1. Вострікова З. П. Программірованіє на мові асемблера ЄС ЕОМ. М.: Наука, 1985.
2. Галісєєв Г. В. Ассемблер для Win 32. Самовчитель. - М.: «Діалектика», 2007. - З. 368. - ISBN 978-5-8459-1197-1
3. Зубків С. В. Ассемблер для DOS, Windows і UNIX.
4. Кіп Ірвіна. Мова асемблера для процесорів Intel = Assembly Language for Intel-Based Computers. - М.: «Вільямс», 2005. - З. 912. - ISBN 0-13-091013-9
5. Магда Ю. З. Асемблер. Розробка і оптимізація Windows-приложений. СПб.: БХВ-Петербург, 2003.
6. Нортон П., Соухе Д. Язик асемблера для IBM РС. М.: Комп'ютер, 1992.
7. Владислав Пірогов. Асемблер для Windows. - СПб.: БХВ-Петербург, 2007. - 896 з. - ISBN 978-5-9775-0084-5
8. Владислав Пірогов. Асемблер і дизасемблювання.. - СПб.: БХВ-Петербург, 2006. - 464 з. - ISBN 5-94157-677-3
9. Сингер М. Міні-ЕВМ PDP-11: Програмування на мові асемблера і організація машини. М.: Мир, 1984.
10. Ськенлон Л. Персональниє ЕОМ IBM РС і XT. Програмування на мові асемблера. М.: Радіо і зв'язок, 1989.
11. Юров Ст, Хорошенко З. Assembler: учбовий курс. - СПб.: «Пітер», 2000. - З. 672. - ISBN 5-314-00047-4
12. Юров Ст І. Assembler : підручник для вузів. / 2-е видавництво СПб.: «Пітер», 2004.
13. Юров Ст І. Assembler. Практикум. : підручник для вузів / 2-е видавництво СПб.: «Пітер», 2004.
14. Юров Ст І. Assembler. Спеціальний довідник. СПб.: «Пітер», 2000.
15. Річард Саймон. Microsoft Windows API Довідник системного програміста.
16. Фрунзе А. Ст Мікроконтролери? Це ж просто! Т. 1.
... іщенні, так і на вулиці, на транспорті, тобто відкриваються практично необмежені можливості! Погодитеся, це вагомий аргумент на користь безпровідних систем відеоспостереження. Простота в настройці і експлуатації стала аргументом до використання безпровідного відеоспостереження в такій несподіваній якості як відеоняня. Завдяки ній ви завжди зможете дізнатися, що робить ваша дитина, чи не задумав ...
0 комментариев