Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Vstup.docx
Скачиваний:
53
Добавлен:
28.09.2019
Размер:
6.42 Mб
Скачать

4.4. Особенности обработки аппаратных прерываний

Аппаратные прерывания вырабатываются устройствами компьютера, как правило, при завершении ими операций обмена данными или при изменении состояния. В зависимости от типа устройства обработчик прерывания может выполнять те или иные функции. Например, по прерыванию таймера соответствующий обработчик увеличивает содержимое счетчика, расположенного в оперативной памяти. По содержимому этого счетчика программы могут определить текущее время.

В отличие от программных прерываний, вызываемых запланировано программой или драйвером, аппаратные прерывания всегда происходят асинхронно по отношению к выполняющимся программам. Кроме того, может возникнуть одновременно сразу несколько прерываний!

Для того чтобы система "не растерялась", решая какое прерывание обслуживать в первую очередь, существует специальная схема приоритетов. Каждому прерыванию назначается свой приоритет. Если происходит одновременно несколько прерываний, система отдает предпочтение самому высокоприоритетному, откладывая на время обработку остальных прерываний.

Система приоритетов реализована на двух микросхемах Intel 8259 (или аналогичных). Каждая микросхема является контроллером прерывания и обслуживает до восьми приоритетов. Микросхемы можно объединять (каскадировать) для увеличения количества уровней приоритетов в системе.

Уровни приоритетов обозначаются сокращенно IRQ0 - IRQ15 .

В компьютере типа IBMPC/XT была установлена только одна микросхема контроллера прерывания. Приоритеты линейно зависели от номера уровня прерывания. Прерывание IRQ0 соответствовало самому высокому приоритету, за ним шли прерывания IRQ1 , IRQ2 , IRQ3 и так далее.

Прерывание IRQ2 в компьютерах IBMPC/XT было зарезервировано для дальнейшего расширения системы. В компьютерах IBMPC/AT прерывание IRQ2 стало использоваться для каскадирования двух контроллеров прерывания 8259. Добавленные приоритетные уровни прерываний IRQ8 - IRQ15 в этих компьютерах располагаются по приоритету между прерываниями IRQ1 и IRQ3 .

Приведем список аппаратных прерываний, расположенных в порядке убывания приоритета:Номер Описание

8 IRQ0 - прерывание интервального таймера, возникает 18,2 раза в секунду

9 IRQ1 - прерывание от клавиатуры

A IRQ2 - используется для каскадирования аппаратных прерываний

70 IRQ8 - прерывание от часов реального времени

71 IRQ9 - прерывание от контроллера EGA

72 IRQ10 - зарезервировано

73 IRQ11 - зарезервировано

74 IRQ12 - зарезервировано

75 IRQ13 - прерывание от арифметического сопроцессора

76 IRQ14 - прерывание от контроллера жесткого диска

77 IRQ15 - зарезервировано

B IRQ3 - прерывание асинхронного порта COM2

C IRQ4 - прерывание асинхронного порта COM1

D IRQ5 - прерывание от контроллера жесткого диска (только в компьютерах IBMPC/XT)

E IRQ6 - прерывание генерируется контроллером НГМД

F IRQ7 - прерывание принтера

Из этого списка видно, что самый высокий приоритет у прерываний от интервального таймера, затем идет прерывание от клавиатуры. Наименьший приоритет имеет прерывание принтера.

Для управления схемами приоритетов необходимо знать внутреннее устройство контроллера прерываний 8259.

Поступающие прерывания запоминаются в регистре запроса на прерывание IRR. Каждый бит из восьми в этом регистре соответствует своему прерыванию.

Перед выдачей в процессор запроса на прерывание проверяется содержимое восьмибитового регистра маски прерываний IMR. Если прерывание данного уровня не замаскировано, то запрос на прерывание выдается.

Наиболее интересными с точки зрения программирования контроллера прерываний являются регистры маски прерываний IMR и управляющий регистр прерываний.

В компьютерах IBMPC/XT регистр маски прерываний имеет адрес 21h, управляющий регистр прерываний - 20h. В компьютерах IBMPC/AT первый контроллер 8259 имеет такие же адреса, что и в IBMPC/XT. Регистр маски прерываний второго контроллера имеет адрес A1h, управляющий регистр прерываний - адрес A0h.

Разряды регистра маски прерываний соответствуют номерам IRQ. Для того чтобы замаскировать аппаратное прерывание какого-либо уровня, надо записать в регистр маски байт масок. В этом байте следует установить в 1 те биты, которые соответствуют маскируемым прерываниям . Например, для маскирования прерываний от НГМД в порт 21h надо записать двоичное число 01000000.

Приведем строку программы, маскирующей прерывание от флоппи-диска:

outp(0x21, 0x40);

Чтобы "оживить" прерывания от НГМД , используйте следующую строку (которая размаскирует все прерывания):

outp(0x21, 0);

Заметьте, что в приведенном выше примере мы замаскировали прерывание именно от НГМД , все остальные устройства продолжали нормально работать. Если бы мы выдали машинную команду CLI, то отключились бы все аппаратные прерывания. Это привело бы, например, к тому, что клавиатура была бы заблокирована.

Еще одно замечание, касающееся обработки аппаратных прерываний.

Если вы полностью заменяете стандартный обработчик аппаратного прерывания, не забудьте в конце программы записать байт 20h в порт с адресом 20h (A0h для второго контроллера 8259). Эти действия необходимы для очистки регистра обслуживания прерывания ISR. При этом разрешается обработка прерываний с более низким приоритетом чем то, которое только что обрабатывалось.

Если вы обрабатываете прерывание 1Ch, то указанная выше добавка в конце программы обработки прерывания не нужна, так как это прерывание является программным и вызывается из обработчика аппаратного прерывания таймера.

Перед тем как завершить изучение прерываний, зададимся вопросом - можно ли замаскировать немаскируемое прерывание ? Оказывается можно!

Конечно, если сигнал прерывания пришел на вход немаскируемого прерывания процессора, ничего сделать нельзя - прерывание произойдет неизбежно. Но в компьютере предусмотрены схемы, блокирующие вход немаскируемого прерывания процессора NMI.

Для компьютера IBMPC/XT маскированием немаскируемого прерывания управляет порт с адресом 0A0h. Если записать в него 0, немаскируемое прерывание будет запрещено, если 80h - разрешено.

Аналогично для IBMPC/AT маскированием немаскируемого прерывания управляет бит 7 порта 70h. Запись байта 0ADh в порт 70h запретит немаскируемое прерывание, а байта 2Dh - разрешит прохождение прерывания.

Заметим, что мы не запрещаем немаскируемое прерывание "внутри" процессора - это невозможно по определению, мы "не пускаем" сигнал прерывания на вход NMI.

4.7 Структура Windows-програми, призначення складових частин (Головна функція Windows-програми, її прототип та призначення параметрів. Створення вікна. Призначення та структура головної функції вікна. Цикл обробки повідомлень).

Структура программы

Программа для Win32 обычно состоит из следующих блоков:

#include<windows.h>

int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR cmdline,int ss) {

/* Блок инициализации:

создание класса главного окна,

создание главного окна,

загрузка ресурсов и т.п. */

/* Цикл обработки событий: */

MSGmsg;

while (GetMessage(&msg,(HWND)NULL,0,0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

}

LRESULT CALLBACK MainWinProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp) {

/* Обработка сообщений главного окна */

switch (msg) {

case WM_CREATE:

/* ... */

return 0;

case WM_COMMAND:

/* ... */

return 0;

case WM_DESTROY:

/* ... */

PostQuitMessage(0);

return 0;

/* ... */

}

return DefWindowProc(hw,msg,wp,lp);

}

Каждое окно принадлежит определенному классу окон. Окна одного класса имеют схожий вид, обслуживаются общей процедурой обработки событий, имеют одинаковые иконки и меню. Обычно каждое приложение создает для главного окна программы свой класс. Если приложению требуются дополнительные нестандартные окна, оно регистрирует другие классы. Стандартные диалоги и управляющие элементы принадлежат к предопределенным классам окон, для них не надо регистрировать новые классы. Чтобы определить новый класс окон, надо заполнить структуру WNDCLASS, содержащую следующие поля:

UINTstyle - стиль (поведение) класса окон,

WNDPROClpfnWndProc - процедура обработки событий окна,

intcbClsExtra - размер дополнительной памяти в системной структуре класса для данных пользователя,

intcbWndExtra - размер дополнительной памяти в системной структуре окна для данных пользователя,

HINSTANCEhInstance - дескриптор модуля (экземпляра программы), в котором реализована процедура обработки,

HICONhIcon - дескриптор иконки окна,

HCURSORhCursor - дескриптор курсора мыши для окна,

HBRUSHhbrBackground - дескриптор "кисточки" для закрашивания фона окна,

LPCSTRlpszMenuName - имя ресурса, содержащего меню окна,

LPCSTRlpszClassName - имя класса.

Класс регистрируется при помощи функции:

WORD WINAPI RegisterClass(const WNDCLASS *lpwc)

При успешном завершении функция возвращает целочисленный код, соответствующий строке-имени класса в общесистемной таблице строк (такой код называется атомом). При ошибке возвращается 0.

Для создания окна вызывается функция:

HWNDWINAPICreateWindow(

LPCSTRlpClassName, /* имя класса */

LPCSTRlpWindowName, /* имя окна (заголовок) */

DWORDdwStyle, /* стиль (поведение) окна */

intx, /* горизонтальная позиция окна на экране */

inty, /* вертикальная позиция окна на экране */

intnWidth, /* ширина окна */

intnHeight, /* высота окна */

HWNDhWndParent, /* дескриптор родительского окна */

HMENUhMenu, /* дескриптор меню */

HANDLEhInstance, /* дескриптор экземпляра программы */

LPVOIDlpParam /* указатель на какую-нибудь ерунду */

)

Вместо параметров x, y, nWindth, nHeight допустимо передавать константу CW_USEDEFAULT, позволяющую операционной системе задать эти числа по ее усмотрению.

Интерпретация кода стиля определяется классом окна. Стиль определяет не только оформление окна, но и его поведение. Общие для всех классов константы стилей (при необходимости объединяются операцией побитовое ИЛИ):

WS_DISABLED - при создании окно заблокировано (не может получать реакцию от пользователя);

WS_VISIBLE - при создании окно сразу же отображается (не надо вызывать ShowWindow);

WS_CAPTION - у окна есть строка заголовка;

WS_SYSMENU - у окна есть системное меню;

WS_MAXIMIZEBOX - у окна есть кнопка разворачивания;

WS_MINIMIZEBOX - у окна есть кнопка сворачивания;

WS_SIZEBOX или WS_THICKFRAME - у окна есть рамка изменения размеров;

WS_BORDER - у окна есть рамка (не подразумевает изменение размеров);

WS_HSCROLL или WS_VSCROLL - у окна есть горизонтальная или вертикальная прокрутка;

WS_OVERLAPPED или WS_TILED - "перекрываемое" окно - обычное окно с рамкой и строкой заголовка;

WS_POPUP - "всплывающее" окно;

WS_OVERLAPPEDWINDOW - "перекрываемое" окно с системным меню, кнопками сворачивания/разворачивания, рамкой изменения размеров, короче, типичный стиль для главного окна приложения.

Во время выполнения функции CreateWindow процедуре обработки событий окна посылается сообщение WM_CREATE. При успешном выполнении функции возвращается дескриптор созданного окна, при неудаче - NULL.

После создания окна неплохо бы сделать его видимым (отобразить), если только оно не создано со стилем WS_VISIBLE:

BOOL WINAPI ShowWindow(HWND hw, int ss)

Второй параметр этой функции - код состояния отображения окна. В качестве этого кода можно взять значение четвертого параметра, с которым была запущена функция WinMain. Другие возможные значения этого параметра:

SW_SHOW - отобразить и активировать окно;

SW_HIDE - скрыть окно;

SW_MAXIMIZE - развернуть окно на весь экран;

SW_RESTORE - активировать окно и отобразить его в размерах по умолчанию;

SW_MINIMIZE - свернуть окно.

Если перед вызовом этой функции окно было видимым, функция возвращает TRUE, если же окно было скрыто - FALSE.

Если клиентская область главного окна приложения содержит объекты, прорисовываемые по сообщению WM_PAINT, имеет смысл прорисовать эти объекты сразу после отображения главного окна на экране. Функция UpdateWindow непосредственно вызывает процедуру обработки событий указанного окна с сообщением WM_PAINT (минуя очередь сообщений приложения):

BOOL WINAPI UpdateWindow(HWND hw)

Windows использует два способа доставки сообщений процедуре обработки событий окна:

непосредственный вызов процедуры обработки событий (внеочередные или неоткладываемые сообщения - nonqueuedmessages);

помещение сообщения в связанный с данным приложением буфер типа FIFO, называемый очередью сообщений - messagequeue (откладываемые сообщения - queuedmessages).

К внеочередным сообщениям относятся те сообщения, которые непосредственно влияют на окно, например, сообщение активации окна WM_ACTIVATE и т.п. Кроме того, вне очереди сообщений обрабатываются сообщения, сгенерированные различными вызовами Win32 API, такими как SetWindowPos, UpdateWindow, SendMessage, SendDlgItemMessage...

К откладываемым сообщениям относятся сообщения, связанные с реакцией пользователя: нажатие клавиш на клавиатуре, движение мышки и клики. Чтобы извлечь сообщение из очереди, программа вызывает функцию

BOOLWINAPIGetMessage(

MSG *lpmsg, /* сюда попадает сообщение со всякими параметрами */

HWNDhw, /* извлекать только сообщения для указанного окна (NULL - все) */

UINTwMsgFilterMin, /* фильтр сообщений (нам не надо - ставим 0) */

UINTwMsgFilterMax /* фильтр сообщений (нам не надо - ставим 0) */

)

Эта функция возвращает FALSE, если получено сообщение WM_QUIT, и TRUE в противном случае. Очевидно, что условием продолжения цикла обработки событий является результат этой функции. Если приложение хочет завершить свою работу, оно посылает само себе сообщение WM_QUIT при помощи функции

void WINAPI PostQuitMessage(int nExitCode)

Ее параметр - статус выхода приложения. Обычно эта функция вызывается в ответ на сообщение об уничтожении окна WM_DESTROY.

После извлечения сообщения из очереди следует вызвать функцию TranslateMessage, переводящую сообщения от нажатых клавиш в удобоваримый вид, а затем DispatchMessage, которая определяет предназначенное этому сообщению окно и вызывает соответствующую процедуру обработки событий.

BOOL WINAPI TranslateMessage(const MSG *lpmsg)

LONG WINAPI DispatchMessage(const MSG *lpmsg)

Результат возврата соответствует значению, которое вернула процедура обработки событий (обычно никому не нужен).

Процедура обработки сообщений окна должна быть объявлена по следующему прототипу:

LRESULT CALLBACK WindowProc(HWND hw,UINT msg,WPARAM wp,LPARAM lp)

Значения параметров: hw - дескриптор окна, которому предназначено сообщение, msg - код сообщения, wp и lp - 32-битные параметры сообщения, интерпретация которых зависит от кода сообщения. Зачастую старший/младший байт или старшее/младшее слово параметров сообщения несут независимый смысл, тогда удобно использовать определенные в windows.h макросы:

#define LOBYTE(w) ((BYTE) (w))

#define HIBYTE(w) ((BYTE) (((WORD) (w) >> 8) & 0xFF))

#define LOWORD(l) ((WORD) (l))

#define HIWORD(l) ((WORD) (((DWORD) (l) >> 16) & 0xFFFF))

Например, сообщение WM_COMMAND посылается окну в трех случаях:

пользователь выбрал какую-либо команду меню;

пользователь нажал "горячую" клавишу (accelerator);

в дочернем окне произошло определенное событие.

При этом параметры сообщения интерпретируются следующим образом. Старшее слово параметра WPARAM содержит: 0 в первом случае, 1 во втором случае и код события в третьем случае. Младшее слово WPARAM содержит целочисленный идентификатор пункта меню, "горячей" клавиши или дочернего управляющего элемента. Параметр LPARAM в первых двух случаях содержит NULL, а в третьем случае - дескриптор окна управляющего элемента.

Процедура обработки событий должна вернуть определенное 32-битное значение, интерпретация которого также зависит от типа сообщения. В большинстве случаев, если сообщение успешно обработано, процедура возвращает значение 0.

Процедура обработки событий не должна игнорировать сообщения. Если процедура не обрабатывает какое-то сообщение, она должна вернуть его системе для обработки по умолчанию. Для этого вызывается функция:

LRESULT WINAPI DefWindowProc(HWND hw, UINT msg, WPARAM wp, LPARAM lp)

Етапы чоздания окна:

1)зарегистрировать класс окна

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wcex.lpszMenuName = NULL;

wcex.lpszClassName = szWindowClass;

wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));

2)создать окно

HWND hWnd = CreateWindow(

szWindowClass,

szTitle,

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

500, 100,

NULL,

NULL,

hInstance,

NULL

);

if (!hWnd)

{

MessageBox(NULL,

_T("Call to CreateWindow failed!"),

_T("Win32 Guided Tour"),

NULL);

return 1;

}

3)отображение окна

ShowWindow(hWnd,

nCmdShow);

UpdateWindow(hWnd);

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]