Системное программное обеспечение персональных ЭВМ

unsigned int es, cs, ss,



Пример 5

struct SREGS { unsigned int es, cs, ss, ds; };
Наконец, структура REGPACK, обеспечивающая наиболее полный набор регистров микропроцессора:



Пример 5

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-функции являются также и выходными. Поскольку содержимое регистров перед возвратом восстанавливается из стека, любое изменение параметра будет произведено в стеке, и при возврате восстановится измененное содержимое регистра. Если же в программе обработки прерываний не требуется обработки содержимого регистров, она может быть описана как функция без параметров.

И еще одно требование к функции обработки прерывания. Если она обрабатывает аппаратное прерывание, то такую функцию необходимо заканчивать оператором сброса контроллера прерываний:



Пример 5

/*== ПРИМЕР 6.4 ==*/ /*============== Сигнал тревоги в AT ================*/ #include <dos.h>
#include <stdio.h>


/* Выражения преобразования BCD->int и наоборот */ #define bcd_to_int(x) (x>>4)*10+(x&0x0f) #define int_to_bcd(x) ((x/10)=60) { m-=60; rr.h.ch=int_to_bcd(bcd_to_int(rr.h.ch)+1); } rr.h.cl=int_to_bcd(m); /* Запись увеличенного времени в регистры тревоги */ rr.h.ah=6; /* функция 6 */ int86(0x1a,&rr,&rr); rr.h.ah=0x2c; intdos(&rr,&rr); printf("\nВремя запуска - %02d:%02d:%02d\n", rr.h.ch,rr.h.cl,rr.h.dh); /* Ожидание тревоги */ flag=0; /* Переменная flag установится в 1 по сигналу тревоги в обработчике прерывания 4A */ while(flag==0); rr.h.ah=0x2c; intdos(&rr,&rr); printf("Время тревоги - %02d:%02d:%02d\n", rr.h.ch,rr.h.cl,rr.h.dh); /* Отмена тревоги */ rr.h.ah=7; /* функция 7 */ int86(0x1a,&rr,&rr); } /*==== Обработчик прерывания 4A - обработчик тревоги ====*/ void interrupt new4A() { putchar(7); /* Звуковой сигнал */ flag=1; /* Установка флага */ writevect(0x4a,old4A); /* Восстановление вектора */ } /*==== Получение старого вектора ====*/ void *readvect(int in) { rr.h.ah=0x35; rr.h.al=in; intdosx(&rr,&rr,&sr); return(MK_FP(sr.es,rr.x.bx)); } /*==== Запись нового вектора ====*/ void writevect(int in, void *h) { rr.h.ah=0x25; rr.h.al=in; sr.ds=FP_SEG(h); rr.x.dx=FP_OFF(h); intdosx(&rr,&rr,&sr); }



Пример 5

/*== ПРИМЕР 7.5 ==*/ /*============= Прерывание BIOS 16 ============*/ #include <dos.h>
main() { union REGS rr; int i; printf("\nПрерывание 0x16, функция 1 >"); /* Ожидание нажатия клавиши */ do { rr.h.ah=1; int86(0x16,&rr,&rr); } while((rr.x.flags&0x0040)!=0); printf("%02x %02x",rr.h.ah,rr.h.al); /* Прием клавиши */ rr.h.ah=0; int86(0x16,&rr,&rr); /* Здесь выведется то же, что и в предыдущей строке, т.к. клавиша, код которой прочитан по функции 1 из клавиа- туры не удалена. */ printf(" %02x %02x\n",rr.h.ah,rr.h.al); /* Прием новой клавиши с ожиданием */ printf("\nПрерывание 0x16, функция 0 >"); rr.h.ah=0; int86(0x16,&rr,&rr); printf("%02x %02x\n",rr.h.ah,rr.h.al); printf("\nПрерывание 0x16, функция 2 >"); /* Теперь надо нажимать клавиши состояний и смотреть, что выводится */ do { rr.h.ah=2; int86(0x16,&rr,&rr); printf("%02x ",rr.h.al); delay(300); /* Проверка нажатия любой (кроме переключателя) клавиши. Цикл продолжается до нажатия клавиши "пробел" */ rr.h.ah=1; int86(0x16,&rr,&rr); } while(rr.h.al!=13); /* Очистка буфера клавиатуры */ poke(0x40,0x1a,peek(0x40,0x1c)); }


7.3. Функции стандартного ввода DOS
В DOS нет функций, ориентированных непосредственно на клавиатуру. Функции ввода DOS работают с файлом стандартного ввода, который по умолчанию связан с клавиатурой. Но при переназначении стандартного ввода функции DOS будут успешно работать и с другим источником.
Коротко перечислим функции DOS.