Скачать Замена обработчика прерываний клавиатуры

22.11.1996
Скачать файл (3,06 Кб)

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.
Собственно, причина зависания именно в этом.
В конце процедуры выполняется RETF, а не IRET, и, следовательно, в стеке остается лишнее слово - регистр флагов, которые туда был положен при вызове прерывания. Можно, конечно, извратиться и написать

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 байт в стеке.

В случае сложных обработчиков я пользуюсь явным переключением стека
(SwapStackAndCall из той же Turbo Professional)

Вообще, я бы написал обработчик для данной ситуации вот так:

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) из стека.