- •Введение
- •Достоинства и недостатки языка c
- •Типы данных в с
- •Модификаторы
- •Константы
- •Арифметические операции
- •Логические операции
- •Оператор присваивания
- •Организация множественных выражений
- •Комбинированные операции
- •Локальные и глобальные объекты
- •Модификатор extern
- •Статические и динамические объекты
- •Регистровые переменные
- •Описание неизменяемых объектов
- •Указатели
- •Явное преобразование типа
- •Оператор if
- •Операторы циклов Оператор while
- •Оператор do ... While
- •Оператор for
- •Оператор switch (переключатель)
- •Операторы передачи управления
- •Прототипы функций
- •Передача данных в функцию
- •Структуры и определения типов пользователя
- •Битовые поля
- •Объединения
- •Псевдофункция sizeof
- •Основные директивы препроцессора
- •Основные директивы условной компиляции
- •Дополнительные данные по Turbo-c
- •Организация памяти на машинах класса pc. Виды указателей
- •Работа с указателями на функцию
- •Основные регистры общего назначения процессора
- •Модели памяти
- •Использование стека
- •Организация прерываний в программе. Модификатор volatile
- •Порядок передачи данных в функцию. Модификатор pascal
- •Передача данных в программу. Функция main
- •Разбор типовых ошибок и недочетов при программировании
- •Некоторые функции библиотеки Turbo-c
- •Функции работы с оперативной памятью эвм
- •Функции работы с клавиатурой
- •Работа с дисплеем в символьном режиме
- •Работа с дисплеем в графическом режиме
- •Функции работы с дисками
- •Функции работы с временными интервалами и звуком
- •Функции преобразования данных
- •Функции работы со строками
- •Математические функции
- •Функции работы с файлами и каталогами
- •Функции работы со временем и датой
- •Функции процессов
- •Функции работы с портами машины
- •Интерфейс с дос
- •Задачи на практические занятия
- •Литература
- •Оглавление
Использование стека
При вызове функции адрес возврата из функции и параметры заносятся в стек. При входе в программу обработки прерываний содержимое регистров общего назначения и адрес возврата также заносится в стек. Поэтому бывают случаи, например, при использовании рекурсии, что стандартного размера стека (4 Кбт) не хватает для выполнения данной программы. Для задания нового размера стека программы можно использовать глобальную переменную стека _stklen типа unsigned int. Например, для задания стека в 20000 Бт можно использовать конструкцию:
unsigned _stklen=20000;
main()
{
. . .
}
Организация прерываний в программе. Модификатор volatile
Прерывание программы происходит в случае, когда какое-либо событие требует немедленного вмешательства процессора.
Прерывания бывают программные и аппаратные. Программные прерывания вызываются в самой программе и служат, как правило, для вызова какой-либо функции операционной системы. Из С они задаются путем вызова соответствующих функций, поэтому здесь не рассматриваются. Аппаратные прерывания вызываются устройствами ПЭВМ. Например, при нажатии клавиши на клавиатуре текущая выполняемая программа прерывается и процессор переходит к выполнению программы обработки данного прерывания. При этом адрес программы находится в фиксированном месте, определяемом номером вектора прерывания (для клавиатуры фиксированный - 9 (линия IRQ1)). В стеке при этом запоминаются состояния ключевых регистров общего назначения процессора и адрес возврата (адрес команды, на которой прервалось выполнение текущей программы). После выполнения программы обработки прерывания по команде возврата из прерывания происходит восстановление значения ключевых регистров общего назначения процессора и переход на продолжение выполнения прерванной программы. Например, при обработке прерываний с клавиатуры принимается код клавиши и заносится в буфер клавиатуры по установленному адресу.
Функция обработки прерываний должна быть описана с модификатором interrupt. Ее тип - void, в качестве параметров можно тоже указать void. Обмен данными с остальными программами программа обработки прерываний может вести через глобальные объекты.
Для задания новой программы обработки прерываний нужно осуществить следующие действия:
1. Запретить процессору обрабатывать аппаратные прерывания. Это нужно для того, чтобы не произошло прерывание в момент смены адреса программы его обработки. Сделать это можно, вызвав, например, функцию disable. Ее прототип
void disable(void);
2. Если надо, сохранить старое значение вектора прерывания для последующего восстановления с помощью функции getvect. Ее прототип
void interrupt (*getvect(номер вектора прерывания))(void);
3. Задать новое значение вектора прерывания с помощью функции setvect. Ее прототип
void setvect(номер вектора прерывания,void interrupt (*vect)(void));
где vect - имя программы обработки прерывания.
4. Разрешить аппаратные прерывания, вызвав функцию enable. Ее прототип
void enable(void);
Как только вызвано аппаратное прерывание, происходит выход в программу его обработки. При этом на IBM-совместимых компьютерах происходит блокировка всех остальных аппаратных прерываний. Это связано с аппаратной их реализацией. Для того, чтобы разрешить дальше обрабатывать аппаратные прерывания, необходимо сбросить контроллер прерываний, выдав код 20 по адресу регистра контроллера прерываний (устройств, подключенных к линиям IRQ0-7, например, клавиатуры и таймера, 20) (код и адрес шестнадцатиричные). Это можно сделать, вызвав функцию outp с параметрами 0x20, 0x20.
Прерывания от конкретных устройств можно как запретить, так и разрешить, задавая значения битов регистров маски контроллера прерываний. Например, для таймера (его вектор прерывания 8) путем задания значения младшего бита регистра по адресу 21 (шестнадцатиричный) можно разрешить или запретить (соответственно 0 или 1) прерывания от него. Эти регистры также можно читать, например, с помощью вызова функции inp.
Для устройств, подключенных к линиям прерываний IRQ8-15 шестнадцатиричные адреса контроллера прерываний и маски соответственно a0 и a1. При этом номера адресов векторов прерываний для этих линий начинаются с шестнадцатиричного адреса 70.
При компиляции программы С производит анализ, задается ли начальное значение переменной до момента ее использования и, если не задается, то выдает предупреждающее сообщение. Если же программа использует прерывания, то значение переменных может задаваться в программе обработки прерываний. В этом случае переменную можно описать с модификатором volatile. Тогда компилятор не будет проверять переменную на изменение и генерировать сообщение о возможном использовании до задания значения.
Пример: перехват прерываний клавиатуры (ее вектор 9), принимаем сканкоды, при нажатии Escape (сканкод 1) выдаем звуковой сигнал, при нажатии 1 (сканкод 2) - выход из программы.
#include <dos.h>
#include <stdio.h>
/* программа обработки прерываний */
void interrupt prcl(void)
{
extern int zero;
extern char ch;
/* считывание кода */
/* код - из порта с 16-тиричным адресом 60 */
ch=inp(0x60);
/* для сброса контроллера клавиатуры выдаем восьмеричные
коды 300 и 100 по 16-тиричному адресу 61 */
outp(0x61,0300);
outp(0x61,0100);
/* признак нажатой клавиши */
zero=1;
/* разрешение следующего прерывания */
outp(0x20,0x20);
return;
}
int zero;
char ch;
main()
{
void interrupt (*cl)(void);
void interrupt prcl(void);
zero=0;
/* считывание адреса вектора прерываний от клавиатуры */
cl=getvect(9);
/* присвоение нового значения вектора прерываний */
/* запрещение прерываний */
disable();
/* новое значение вектора ... */
setvect(9,prcl);
/* разрешение прерываний */
enable();
/* общий цикл ... */
for(;;)
{
/* ждем нажатия клавиши */
while (!zero) ;
zero=0; /* сбрасываем признак нажатия клавиши */
/* анализ знака ... */
/* если клавиша ESC */
if (ch==1)
{
/* звук частотой 1000 Гц в течение 500 мс */
sound(1000);
delay(500);
nosound();
}
/* если клавиша 1 */
if (ch==2) break;
}
/* запрещение прерываний */
disable();
/* восстановление старого вектора ... */
setvect(9,cl);
/* разрешение прерываний */
enable();
}