|
|||
Скачать Замена обработчика прерываний клавиатуры
22.11.1996 KeyPressed вызывает функцию BIOS (INT 16H AH=01H) для проверки наличия символа в буфере. Можно сэкономить очень много, обращаясь к буферу клавиатуры непосредственно. Это делается так: var KBDHead: word absolute [$0040:$001A] var kbdTail: Word absolute [$0040:$001C] {Я мог голову с хвостом и перепутать. Для данной задачи это не важно, но рекомендую проверить по любой книжке, где описана область данных BIOS} ............... While not PassStarted do begin if KbdTail<>KbdHead then begin PassStarted:=False; exit; end; end; ............. Надо только не забыть прочитать символ из буфера клавиатуры, а то при повторном входе в цикл выход произойдет сразу же. Кстати, прямой доступ к буферу клавиатуры открывает неожиданные возможности: Например, он позволяет отличить Enter на цифровой клавиатуре от обычного и обрабатывать Alt-стрелки Вариант с перехватом 9-го прерывания имеет свои преимущества. Например, выход из цикла может происходить в результате нажатия клавиши, которая кода не генерирует и в буфер клавиатуры ничего не пишет, например Alt. Но приведенный в письме обработчик содержит ряд ошибок: [skipped] Обpaботчик пpеpывaния имеет следующий вид: {$F+} procedure KeyDown; Обработчик прерывания ОБЯЗАН быть описан как Interrupt. procedure KeyDown(dummy:integer); заставив процедуру рассматривать это слово как параметр, и выполнять RETF 2. Но кто будет заботится о сохранности ВСЕХ регистров и флагов? Interrupt это делает. Короче, писать надо так: procedure KeyDown;interrupt; begin asm CLI end; inline ($9C); { PUSHF -- Push flags } Почему не asm CLI PUSHF end По-моему смешение в одной процедуре кусков asm и inline - дурной тон,
если это не вызвано какой-то необходимостью. procedure CLI;inline (...); procedure PushF; inline($9C); что сделало бы код более читаемым. OldIntKbdVector; KeyFlag:=true; Если вызывается OldIntKbdVector, то нет никакой необходимости в нижеследующей ассемблерной вставке. В старом обработчике это все равно сделано. Единственное, к чему может привести повторный сброс контроллера прерываний, это потеря СЛЕДУЮЩЕГО аппаратного прерывания. (Какое оно будет - таймер, клавиатура или что еще - это уж как повезет) asm MOV AL, $20 OUT $20, AL MOV AL, $20 OUT $A0, AL STI end; end; {$F-} Вообще, писать обработчики прерываний на Pascal дело довольно опасное. Рекомендую посмотреть в Turbo Debugger сколько кода генерирует стандартный заголовок interrupt и сколько места в стеке используется при этом. Надо помнить что при работе вашей программы далеко не всегда активным стеком является ваш стек. Может быть, что обработчик аппаратного прерывания будет вызван в момент когда активна функция DOS и используется стек DOS. Тогда каждый лишний байт в стеке может оказаться смертельным. Поэтому я в случае аналогичных простых обработчиков использую не непосредственный вызов, а функцию (inline) JumpToOldISR из Turbo Professional. Она мне экономит как минимум 6 байт в стеке. В случае сложных обработчиков я пользуюсь явным переключением стека Вообще, я бы написал обработчик для данной ситуации вот так: function KeyDown;assembler; asm cli push AX push DS mov AX,@DATA mov DS,AX mov ax,True mov KeyFlag,1 pushf call dword ptr OldIntKbdVector pop DS pop ax pop BP;{Для компенсации стандартного startup-кода assembler процедуры} iret{ и явный выход по iret} end; По сравнению с процедурой, описанной как Interrupt, это экономит место в стеке за счет того, что не сохраняются те регистры которые не используются в обработчике. Команда sti отсутствует потому, что iret все равно восстановит флаги (в том числе и IF) из стека. | |||
© 2009–2024 Russian Pascal Developer Network.
Техническая площадка: ISBIZ Хостинг |