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