Консультация № 20577
08.05.2005, 15:48
0.00 руб.
0 3 3
Здравствуйте, помогите, пожалуйста, бедному студенту.
Нужна помощь в написании программы по перехвату прерываний клавиатуры.
В книжках ничего конкретного не почерпнул, копаться в рассылках не охото, нормальные статьи найти немогу, преподователь злой и не помогает - вот к Вам обращаюсь.
Суть программы в том, что при нажатии на клавишу должен происходить перехват этого прерывания, и подмена выходных параметров. В частности, если нажата буква ‘a‘ - на экране должна появиться буква ‘b‘ и наоборот.
Кое-что, конечно, препод подсказал - но все равно ничего не работает. Если раньше прога хоть вешала намертво комп, то сейчас вообще ничего не происходит.
Помогите, пожалуйста. Расскажите, где основная ошибка и что надо откорректировать. Заранее списибо!

Приложение:
model small.386.stack 100h.data chr_out db ? off dw ? seg dw ?.code ; ф-ция intproc: mov ah,10h int 16h cmp ah,30 ;ScanCode - ‘a‘ je OK_flg_a jmp No_flg_a OK_flg_a: mov ah,48 ;ScanCode - ‘b‘ mov al,‘b‘ No_flg_a: cmp ah,48 ;ScanCode - ‘b‘ je OK_flg_b jmp No_flg_b OK_flg_b: mov ah,30 ;ScanCode - ‘a‘ mov al,‘a‘ No_flg_b: iret ; ф-ция закончена start: mov ax,@data mov ds,ax ; сохраняем перем-ю окружения mov ah,35h mov al,16h int 21h mov ax,es mov seg,ax mov off,bx mov ax,cs mov ds,ax mov dx, offset intproc mov ah,25h mov al,16h int 21h ; возвращение перем-й окружения mov ax,seg mov ds,ax mov bx,off; если откомментировать - вылетает; mov chr_out,al; mov ah, 09h; mov dx,offset chr_out; int 21h exit: mov ax,4C00h int 21h end start

Обсуждение

давно
Советник
419
1011
08.05.2005, 23:47
общий
это ответ
Здравствуйте, raynor_y!
сохраняй регистры в прерывании!!!!
и не вызывай из своего обработчика то же самое прерывание.
смотри файл
Неизвестный
11.05.2005, 14:39
общий
это ответ
Здравствуйте, raynor_y!
Хм... Ошибок много.
Во-первых, у тебя не резидентная программа! Вот трассировка действий твоей программы:
1. Установить сегмент данных - ок.
2. Получить адрес старого обработчика 16-го прерывания - ок.
3. Сохранить адрес в переменных - ок.
4. Установить свой обработчик - ок. Обсуждение написания обработчика - дальше.
5. Установить ds:bx на адрес старого обработчика. Хм. А зачем? Непонятное действие.
6. Комментированные строки. Записать по адресу ds:chr_out значение регистра al. А куда указывает ds? правильно - на сегмент старого обработчика. Интересно, я бы, пожалуй, тоже обиделся на такую запись.
7. Вывести на экран строку, завершающуюся символом ‘$‘ и начинающуюся с адреса ds:chr_out. Ну, сегментный регистр по-прежнему указывает на сегмент старого обработчика, а вот будет ли там символ доллара - я не уверен. Кстати, прога вылетает, скорее всего, еще на шаге 6.
8. Завершить программу с выходом в ДОС.
Итак, что ты имеешь:
1. Неправильная установка регистра ds
2. После возможного завершения программы ты теряешь адрес старого обработчика и при запуске следующей программы вызов 16-го прерывания все равно приведет к ошибке
3. Программа не резидентная, т.к. не остается в памяти после завершения.
Теперь по поводу обработчика. Допустим, что его вызвали. Что о делает. А делает он то, что в первой же строке обращается к самому себе! Рекурсия, причем безусловная! Т.е. зацикливание. Очень быстро произойдет переполнение стека - и опаньки! Неправильный алгоритм.
И последнее. Насколько я понял из задания, тебе нужно отлавливать нажатия на клавиши и подменять ‘a‘ на ‘b‘, а ‘b‘ на ‘a‘. но все дело в том, что 16-е прерывание - программное! И работает только при прямом обращении. Т.е. если программа не будет вызывать 16-е прерывание, то и работать это не будет.
В то же время любое нажатие или отпускание клавиши генерирует на аппаратном уровне прерывание IRQ1, которое соответствует вектору 9. Т.е. любое нажатие клавиши вызывает обработчик 9-го прерывания! Может, с ним лучше поиграть?
И еще два вопроса. Зачем ты при написании резидента создаешь EXE-программу? Да, это не ошибка, но написание резидента в виде EXE-модуля гораздо сложнее написания такого же резидента, но в виде COM-программы.
Второй вопрос - ты используешь 32-разрядные регистры или команды 386-го процессора? Если нет - зачем использовать .386? Достаточно .286, хотя твой код вполне компилируем и на 8088. Это, как ты понимаешь, не ошибка, но если бы я был твоим преподом, то в первую очередь задал бы этот вопрос, чтобы выяснить, а понимаешь ли ты вообще эту директиву.
В общем, см. в приложении резидент на 16-е прерывание в виде COM-программы. Он очень простой, практически не делает никаких действий, только устанавливает новый обработчик прерывания и завершается резидентно. Теперь при любом получении нажатия клавиши через 16-е прерывание будет осуществляться проверка на скен-коды клавиш A и B и их подмена. Вывод на экран также будет формироваться внешней программой.
От этого можешь отталкиваться.

Приложение:
.Model TINY.286.Code.Startup jmp Initold_int16_o dw ? ; смещение старого обработчикаold_int16_s dw ? ; сегмент старого обработчикаInt_16 proc far ; проверить функцию and ah, 0fh ; чтение клавиши - только функция с 0 в младшей тетраде! jz @@ChangeKey ; вызвать старый обработчик без возврата в наш обработчик jmp dword ptr cs:[old_int16_o]@@ChangeKey: ; вызвать старый обработчик с возвратом в наш обработчик pushf call dword ptr cs:[old_int16_o] cmp ah, 30 ; ScanCode ‘A‘ jne @@Not_A mov ax, (48 shl 8) or ‘b‘ iret@@Not_A: cmp ah, 48 ; ScanCode ‘B‘ jne @@Exit mov ax, (30 shl 8) or ‘a‘@@Exit: iretInt_16 endpFINISH EQU $Init: mov ax, 3516h int 21h mov [old_int16_o], bx mov [old_int16_s], es mov ax, 2516h mov dx, OFFSET Int_16 int 21h mov dx, FINISH int 27hend
Неизвестный
13.05.2005, 00:17
общий
это ответ
Здравствуйте, raynor_y!
1) накой тебе сдался PSP, Chr_out (который ты почему-то 9 функцией 21 прерывания пытаешься вывести)?
2) вызывая Int 16 из Int 16 ты намертво зацикливаешся.
3) mov ax,4C00h int 21h выгружает твою прогу из памяти ПОЛНОСТЬЮ, при этом Int16 yt восстанавливаешь. Тоже ерунда.
4) Вообще в данном случае перехват Int16 не лучший вариант, есть много других способов получить код нажатой клавиши минуя ah=0 int 16h.
Тем не менее вот такой вариант: (COM файл)
.model tiny
.386
.code
org 100h
start:
jmp begin
; ф-ция
intproc:
or ah,ah
jnz loc1
pushf
call dword ptr cs:oldint16
cmp ax,1e61h
jnz loc2
mov ax,3062h
loc2:
iret
loc1:
jmp dword ptr cs:oldint16
oldint16:
_off dw ?
_seg dw ?
; ф-ция закончена
begin:
mov ax,3516h
int 21h
mov ax,es
mov _seg,ax ;Запомнить старый Int16
mov _off,bx
mov ax,cs
mov ds,ax
lea dx,intproc
mov ax,2516h
int 21h ;Установить новый
lea dx,begin ;Оставить резидентом!!!
int 27h
end start
А вот более приемлимый вариант перехватывающий INT9:
.model tiny
.386
.code
org 100h
start:
jmp begin
; ф-ция
intproc:
pushf
call dword ptr cs:oldint9 ;пусть отработает оригинальный и положит в буфер клавиатуры код
push ds
push bx
push ax
push 40h
pop ds
mov bx,ds:[1ch] ;какой последний код был положен в буффер
cmp bx,1eh
jnz loc1
add bx,20h; буффер круговой
loc1:
sub bx,2
mov ax,[bx]
cmp ax,1e61h ;только маленькая ‘a‘ !!!! большая будет:1e41 (c ctrl-a =1e01h) и т.д.
jnz loc2
mov ax,3062h ;большая 3042h
mov [bx],ax ;вот собственно и подмена
loc2:
pop ax
pop bx
pop ds
iret
oldint9:
_off dw ?
_seg dw ?
; ф-ция закончена
begin:
mov ax,3509h
int 21h
mov ax,es
mov _seg,ax ;Запомнить старый Int09
mov _off,bx
mov ax,cs
mov ds,ax
lea dx,intproc
mov ax,2509h
int 21h ;Установить новый
lea dx,begin ;Оставить резидентом!!!
int 27h
end start
Есть еще несколько вариантов решения той же проблемы.
Форма ответа