
3. Прерывания
Мы выделяем рассмотрение этих средств языка Си в отдельный раздел, так как обсуждение этой темы выходит за рамки собственно языка Си и требует рассмотрения также и некоторых особенностей Ассемблерного программирования.
3.1. Генерация программных прерываний
Для этих целей в Турбо-Си имеется целый ряд функций.
Функция генерации программного прерывания:
int int86(int int_num, union REGS *inregs,
union REGS *outregs);
Функция выполняет прерывание с номером int_num, причем, перед выдачей команды INT содержимое полей объединения inregs копируется в регистры микропроцессора, а после возврата из прерывания - содержимое регистров - в поля объединения outregs.
Функция обращения к DOS:
int intdos(union REGS *inregs, union REGS *outregs);
Вызов этой функции эквивалентен вызову функции int86 со значением параметра int_num = 0x21.
В приведенных выше функциях для передачи параметров используются только регистры общего назначения. Если для передачи параметров требуется использовать также и сегментные регистры, то можно воспользоваться функциями:
int int86x(int int_num, union REGS *inregs,
union REGS *outregs, struct SREGS *segregs);
int intdosx(union REGS *inregs, union REGS *outregs,
struct SREGS *segregs);
Здесь структура segregs, на которую указывает дополнительный параметр, служит для задания и входных, и выходных значений сегментных регистров.
Наконец, наиболее полный набор регистров передается прерыванию функцией: void intr(int int_num, struct REGPACK *regs); Структура regs также содержит и входные, и выходные значения. Но использование этой функции нами, к сожалению, ограничено ошибкой, имеющейся в системе программирования Турбо-Си 2.0. Ошибка заключается в том, что значение регистра BP, передаваемое в составе структуры regs, функция не отрабатывает. Поэтому в наших примерах мы в случаях, когда необходимо передать регистр BP, пользуемся функцией geninterrupt:
void geninterrupt(int int_num);
geninterrupt представляет собой функцию, основное содержание которой составляет единственная команда Ассемблера INT int_num. Значения регистров можно передать прерыванию через псевдорегистры - _AX, _BX и т.д., из них же получить и результаты. Но применение этой функции требует большой осторожности: во-первых, при формировании входных регистров имеется риск в процессе формирования второго и последующих испортить содержимое регистров, сформированных ранее, во-вторых, при выполнении прерывания может быть изменено содержимое регистров - в первую очередь DS и ES, а также и BP, DI, SI (поэтому мы всегда, применяя geninterrupt, сохраняем их содержимое в статической памяти).
Еще несколько подобных функций мы опускаем, так как они не используются в программах нашего пособия.
3.2. Программы обработки прерываний
На языке Си мы можем писать и собственные программы обработки прерываний. Такая программа является функцией в программе пользователя и должна иметь описание типа:
void interrupt int_handler (int bp,int di,int si, int ds,
int es,int dx,int cx,int bx,int ax,
int ip,int cs,int flags);
Рассмотрим элементы описания подробнее. Имя функции (у нас int_handler) может быть произвольным. Тип функции void очевиден - программа обработки прерывания не может возвращать значение. Описатель interrupt заставляет Си транслятор генерировать некоторые дополнительные коды для этой функции. Как известно, по команде INT в стек заносится содержимое регистра флагов и регистров CS:IP. Дополнительные коды, гене- рируемые для interrupt-функции, обеспечивают сохранение в стеке остальных регистров. При возврате управления из interrupt-функции содержимое регистров восстанавливается, и возврат выполняется командой IRET. (При программировании на языке Ассемблера программист должен сам заботиться о сохранении и восстановлении регистров). Поскольку для обычных (не interrupt) функций Си через стек передаются значения параметров, в функции обработки прерывания могут быть описаны параметры, как это показано в нашем примере, и программист может работать с регистрами как с параметрами, переданными его функции. Порядок сохранения регистров в стеке - всегда такой как показано в примере. Более того, в отличие от обычных функций Си параметры interrupt-функции являются также и выходными. Поскольку содержимое регистров перед возвратом восстанавливается из стека, любое изменение параметра будет произведено в стеке, и при возврате восстановится измененное содержимое регистра. Если же в программе обработки прерываний не требуется обработки содержимого регистров, она может быть описана как функция без параметров. И еще одно требование к функции обработки прерывания. Если она обрабатывает аппаратное прерывание, то такую функцию необходимо заканчивать оператором сброса контроллера прерываний:
outportb(0x20,0x20);.