DOS, перехват прырвания 09h и ввод с клавиатуры во время обработки - фантастика?
В вузе поставили задачу - написать резидента, который ловит нажатие клавиш, по Ctrl+X выгружается, по Ctrl+S просит ввести имя диска и выводит информацию о нём.
С этой и аналогичными (с вводом) задачами у всего потока возникли проблемы. Я свою работу сдал, но живёт оно на честном слове и вешает систему на раз два.
Поясню ситуацию, объясню - как её вижу я, и хочу услышать авторитетное мнение - прав ли я?
Ситуация:
Идеальный обработчик прерывания:
pushf
; Наш код
call _originalHandler
sti
iret
call _originalHandler
; Ждём ввода
mov ah, 01h
int 21h
; Наш код
iret
Как я это вижу:
Ядро вызывает обработчик прерывания. Сохраняет адрес возврата, флаги.
Мы вызвали оригинальный обработчик. Управление системе через iret не вернули.
Но её было достаточно того, что выполнился оригинальный обработчик, и она готова к новому вводу.
Вызывается новый обработчик (н.п. наш ввод, или мы просто отпустили Ctrl+S).
Он заменяет флаги и адрес возврата. Выполняется. Допустим, это было не Ctrl+S/X, мы просто вызвали оригинальный обраточик и вернули управление системе. Всё отлично.
Заканчивает работу наш первый обработчик. Ищет куда бы ему вернуться, и возвращается в какое-то совершенно незнакомое место, так как другой обработчик уже заменил его адрес вызова своим, выполнился и вернулся.
Результат не предсказуем.
Как надо было сделать:
Разбиваваем обработчик на несколько стадий.
Стадия 1:
Cli. Ловим Ctrl+S/Ctrl+X. Обрабатываем Ctrl+X. При Ctrl+S переключаемся на стадию 2 и завершаем обработчик. Sti.
Стадия 2:
Cli. Ctrl+S/Ctrl+X не обрабатываются. Весь ввод, воспинимается, как ввод имени диска (для простоты - один пришедьший скан-код). Выводим информацию о диске. Переключаемся на стадию 1. Завершаем обработчик. Sti.
И никаких int21h(01h). Оригинальный обработчик вызывается в конце, а не в начале.
Верно ли моё видение ситуации?
Является ли последнее верным и\или единственно верным вариантом?
(Ввод с мыши, COM-порта и др. устройств не рассматривается).
P.S. Извините за некоторый сумбур, последнее решение пришло в голову в процессе написания поста, а изначально он выглядел, как: что за хрень, это задание вообще не выполнимо. :D
В общем, проблема решена. Действительно помогли отказ от чтения и системным вызовом и печати строки через него же.
Обоим отписавшимся спасибо.
int 21h
А в отладчик посмотреть?
Там ещё должно быть сохранение и восстановление регистров - но это уже лирика.
cli запрещает повторный вызов прерывания, до вызова sti. В том то и беда, что мы не трогаем этот флаг, так как ожидаем ввода с клавиатуры в процессе обработки прерывания клавиатуры (даже звучит, как верх кретинизма :D). И решение я вижу только одно - изложенное выше.
У нас задача именно написание резидентной программы, которая подменяет обработчик прерывания, оставляет кусок себя в памяти, а сама завершается. С обычной консолкой и проблем бы не было. Ну, а для мониторинга чего либо, нужно предыдущее состояние и текущее. А у нас прерывание дёргается на каждое отжатие\нажатие кнопки.
А в отладчике, как я и писал, такая ситуация:
Вызов обработчика 2 - возврат в xxxx:zzzz
Завершение обработчика 2 - возвращаемся в xxxx:zzzz
Завершение обработчика 1 - возвращаемся хрен знает куда и, можем повесить систему
И, да, с popf — это я ошибся, он не нужен.
вам нужно полностью подменить обработчик прерывания своим .
алгоритм обработчика прост как два пальца :
1.считать скан-код с клавы .
2.отфильтровать нужное вам.
3.отфильтровать не нужное никому .
4.преобразовать скан-код в аски-код и поместить в буфер .
end
если бы у вас не было требования на Ctrl+X и Ctrl+S , то можно было бы упростить .
и выше прерывания клавы только прерывания таймера .
cli и sti не нужны .
Тобишь, ожидание ввода символа, заменить на
in al, 60h
test al, al
jz loop
УТВР в такой же ситуации с вводи через системный вызов? :D
Читать книжки по данной проблеме не хочется, так как никто больше мне не даст работать в реальном режиме, и эти знания представляются мне чистой воды теорией.
вам нужно полностью подменить обработчик прерывания своим .
алгоритм обработчика прост как два пальца :
1.считать скан-код с клавы .
2.отфильтровать нужное вам.
3.отфильтровать не нужное никому .
4.преобразовать скан-код в аски-код и поместить в буфер .
end
если бы у вас не было требования на Ctrl+X и Ctrl+S , то можно было бы упростить .
и ещё . прерывания в досе работают в режиме с приоритетами .
и выше прерывания клавы только прерывания таймера .
cli и sti не нужны
Проблем с Ctrl+X / Ctrl+S я не вижу. Резидент в любом случае нужен. А на что его вешать - какая разница? Проблема пока именно с вводом.
Что же касается подмены своим: а это как? Откуда же я знаю - что из этого не нужно никому? В какой буфер помещать? Наконец, до меня могло быть уже зарегистрировано с десяток резидентов и они вызывают друг-друга цепочкой (н.п. я работаю в нортоне, у него есть хоткеи, как я понимаю - он подменяет прерывания своими, и я обязан вызывать его, иначе у меня нортон повиснит).
Тоесть на мой взгляд, проблема именно в том, что обработчик дёргается повторно до того, как закончил работать первый и адрес возврата из обработчика становится не валидным. Возможно, я ошибаюсь. Возможно, мы говорим об одном и том же разными словами.
А я точно не помню, что там за код должен быть. Открой Питера Абеля и почитай, ёпт. Я вчера у него нашёл код с портами в разделе о резедентных программах.
УМВР, когда нужно было написать резедентную программу, которая выводит текст на консоль и по нажатию кнопки меняет его цвет. Кнопка была одна, может, в этом дело. Но, увы, исходников не сохранилось. Проблемы были только с некоторыми кнопками, которые не хотели отлавливаться в командной строке Windows, какой-нибудь эмулятор DOS отработал бы нормально.
И почитай о буффере клавиатуры же.