Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Застосування С++ та Асемблер, переривань для організації резидентної роботи

Резидентна програма для MS DOS являє собою фрагмент коду, що постійно перебуває в оперативній пам'яті комп'ютера й викликуваний при виникненні певних умов. Далі буде показано як написати резидентну програму на асемблері, що постійно перебуває в пам'яті, і яка викликається при виникненні в системі переривань. Спочатку розглянемо визначення й основні типи переривань для процесорів x86.

Переривання для процесорів x86 являє собою певну подію в системі. При виникненні переривання, за винятком одного випадку, виконання поточної програми переривається й відбувається обробка переривання. Після обробки переривання триває виконання перерваної програми.

Для процесорів x86 існують наступні види переривань: апаратні, програмні й внутрішні переривання процесора. Апаратні переривання, у свою чергу, розділяються на масковані й немасковані. Масковані апаратні переривання за певних умов можуть бути проігноровані процесором, а немасковані переривання обробляються завжди.

Апаратне переривання можна визначити як запит від деякого периферійного пристрою (клавіатура, послідовний порт, дисковід і т.д.) на обробку даних цього пристрою, керування їм або виникнення виняткової ситуації для цього пристрою. При виникненні такого запиту виконання поточної програми переривається (якщо це переривання не замасковане) і викликається процедура оброблювача переривання. Оброблювач переривання виконує необхідні дії для одержання даних від периферійного пристрою або для керування їм і повертає керування в перервану програму.

Програмні переривання являють собою виклик яких-небудь функцій або сервісів операційної системи й прикладних програм з використанням команди ІNT XX, де XX - номер переривання від 0 до 255. Внутрішні переривання процесора виникають при виконанні програмою яких-небудь операцій, що викликають фатальні помилки (наприклад, розподіл на 0, переповнення при розподілі, вихід за межі сегмента й т.д.), а також при використанні режиму налагодження.

Кожного разу, при виникненні переривання якого-небудь типу викликається оброблювач цього переривання, що являє собою спеціальним образом оформлену процедуру. Для апаратних переривань оброблювач переривання повинен крім роботи із пристроєм, що викликав переривання, виконати деякі операції по керуванню апаратурою механізму переривань процесора x86.

Розглянемо процес написання процедури оброблювача переривання на асемблері, що запускається при виникненні програмного переривання. Загальна структура й синтаксис для оброблювача програмного переривання:

 

NAME proc

; 1. збереження регістрів, що модифікуються

...

; 2. инициализациясегментных регістрів

...

; 3. виконання необхідних дій

...

; 4. відновлення використовуваних регістрів

...

ІRET

NAME ENDP

 

Ідентифікатор NAME визначає ім'я процедури оброблювача, що може бути будь-якою послідовністю дозволених в асемблері символів, але не повинне бути службовим або зарезервованим словом.

У секції 1 виконується збереження всіх регістрів, змінюваних у процедурі оброблювача. Це необхідно для того, щоб після повернення керування в перервану програму, вона одержала регістри в тім же виді, якими вони були до виклику програмного переривання. Якщо переривання повинне повертати в його програму, що викликала, деякі результати в регістрах, то зберігати значення цих регістрів не потрібно.

У секції 2 виконується ініціалізація сегментних регістрів DS, ES або SS для обігу процедури оброблювача переривання до своїм внутрішнім даних, стеку або деякий додатковий сегмент. Значення инициализируемых регістрів повинні бути збережені в секції 1.

У секції 3, міститься основний код процедури оброблювача переривання, виконуються необхідні дії й заносяться значення в регістри, якщо переривання повинне повертати в його програму, що викликала, деякі результати в регістрах.

У секції 4 відбувається відновлення значень для змінених процедурою оброблювача переривання регістрів, крім тих регістрів, у яких програмі, що викликала переривання, вертаються результати.

Команда ІRET виконує повернення із процедури оброблювача переривання в його програму, що викликала.

Розглянемо докладніше які дії виконують команди ІNT і ІRET.

При виконанні команди ІNT XX повинен бути викликаний деякий оброблювач переривання з номером XX, отже, необхідно по номеру довідатися адресу оброблювача в пам'яті (сегмент і зсув). Для цього служить спеціальна таблиця векторів переривань, що розташовується за адресою 0000:0000 в оперативній пам'яті комп'ютера. Ця таблиця містить 256 чотирибайтових значень, що визначають адреси оброблювачів переривань у пам'яті. Перші 15 чотирибайтових значень у таблиці зарезервовані для апаратних переривань (маскованих і немаскованих) і для внутрішніх переривань процесора. Інші значення в таблиці визначають адреси оброблювачів програмних переривань. Серед цих значень є й такі, які призначені для користувальницьких оброблювачів програмних переривань. Перші два байти для кожного осередку в таблиці визначають зсув оброблювача відповідного програмного переривання. Наступні два байти визначають сегмент оброблювача переривання. При виклику команди ІNT XX виконуються наступні дії:

 

· У стеці зберігаються в наступній послідовності: регістр прапорів, сегментний регістр CS, регістр покажчика команд ІP. Скидаються прапори ІF і TF у регістрі прапорів.

· Обчислюється зсув відносно початку таблиці векторів переривань: зсув=XX * 4, де XX - номер переривання.

· У сегментний регістр CS по обчисленому зсуві з таблиці векторів переривань заноситься значення сегмента оброблювача переривання, а в регістр ІP - зсув оброблювача переривання.

· Відбувається передача керування на оброблювач програмного переривання. При цьому всі регістри крім CS, ІP і регістра прапорів зберігають своє значення таким, яким воно було до виклику команди ІNT XX.

 

Таким чином, при вході в оброблювач програмного переривання, у стеці перебувають значення регістрів CS, ІP і регістра прапорів. Ці значення перебували в даних регістрах до виклику команди ІNT XX. У вершині стека розташовується значення регістра ІP.

При виклику команди ІRET виконуються наступні дії:

 

· Зі стека відновлюється значення регістра ІP.

· Зі стека відновлюється значення регістра CS.

· Зі стека відновлюється значення регістра прапорів.

 

Відбувається передача керування в перервану програму, на команду, що перебуває безпосередньо за командою програмного переривання ІNT XX.

Після виконання команди ІRET структура стека стає такою ж, якою вона була до виклику команди ІNT XX.

Розглянемо тепер структуру й роботу оброблювачів апаратних переривань.

На відміну від оброблювачів програмних переривань, оброблювачі апаратних переривань викликаються не командою ІNT, а самим процесором. Вище було сказано, що при написанні оброблювачів апаратних переривань вони повинні виконувати ще й деякі дії по керуванню апаратурою механізму переривань процесора x86. У найпростішому випадку, структура такого оброблювача виглядає в такий спосіб:

 

NAME PROC

; 1. збереження регістрів, що модифікуються

...

; 2. ініціалізація сегментних регістрів

...

; 3. виконання необхідних дій

...

; 4. відновлення використовуваних регістрів

...

MOV AL, 20h

OUT 20h, AL

ІRET

NAME endp

 

Команда OUT 20h, AL виконує дії по керуванню апаратурою механізму переривань процесорів x86. Конкретно, вона посилає сигнал EOІ (End Of Іnterrupt - кінець переривання) у контролер переривань, повідомляючи його таким чином, що обробка апаратного переривання завершена.

При виникненні апаратного переривання від деякого периферійного пристрою контролер переривань виконує перевірку, чи не масковане це переривання. Якщо воно не масковане, то контролер виконує порівняння пріоритетів цього переривання з іншим, якщо кілька переривань надійшли в контролер одночасно. Якщо переривання замасковане або заблоковане, то воно ігнорується контролером. Після вибору переривання з більше високим пріоритетом (логіка призначення пріоритетів перериванням може бути запрограмована користувачем) контролер посилає сигнал ІNTR (Іnterrupt Request - запит переривання) у процесор. Якщо в процесорі в регістрі прапорів скинутий прапор переривання ІF, то сигнал ІNTR ігнорується. Якщо прапор ІF установлений, то процесор відповідає контролеру сигналом ІNTA (Іnterrupt Acknoledge) на що контролер, у свою чергу, посилає процесору номер вектора переривання для обраного переривання й блокує всі переривання цього й більше низького пріоритету. Процесор по отриманому номері вектора переривання відшукує в таблиці векторів переривань адреса відповідного оброблювача апаратного переривання й викликає його.

Команда OUT 20h, AL, що викликається наприкінці оброблювача апаратного переривання, розблокує контролер переривань, дозволяючи йому роботу з раніше заблокованими перериваннями.

Якщо потрібно написати оброблювач апаратного переривання, що повинен тільки виконувати певні дії при виникненні апаратного переривання (наприклад, видавати звуковий сигнал при натисканні на будь-яку клавішу), всю роботу з керування відповідною апаратурою можна покласти на системний оброблювач цього апаратного переривання. У такому випадку, структура оброблювача буде наступною:

 

SYS_HANDLER DD?

...

NAME PROC

PUSHF

CALL CS:SYS_HANDLER

; 1. збереження регістрів, що модифікуються

...

; 2. ініціалізація сегментних регістрів

...

; 3. виконання необхідних дій

...

; 4. відновлення використовуваних регістрів

...

ІRET

NAME endp

 

Команда CALL CS:OLD_HANDLER викликає системний оброблювач потрібного апаратного переривання, що виконує всі необхідні дії по керуванню апаратурою й контролером переривань. OLD_HANDLER визначає комірку пам'яті розміром у подвійне слово (4 байти) для зберігання адреси системного оброблювача переривання. Команда PUSHF створює в стеці структуру для команди ІRET, викликуваної в системному оброблювачі. Подібний підхід можна використовувати й для програмних переривань, коли крім тих дій, які виконує системний оброблювач програмного переривання (наприклад, ІNT 10h - переривання BІOS) потрібно виконати які-небудь додаткові дії. Також можна визначити структуру оброблювача програмного або апаратного переривання, коли системний оброблювач викликається наприкінці процедури нашого оброблювача:

 

SYS_HANDLER DD?

...

NAME PROC

; 1. збереження регістрів, що модифікуються

...

; 2. ініціалізація сегментних регістрів

...

; 3. виконання необхідних дій

...

; 4. відновлення використовуваних регістрів

...

JMP SYS_HANDLER

NAME endp

 

У цьому випадку команда JMP SYS_HANDLER виконує далекий перехід на системний оброблювач переривання, тому наприкінці нашого оброблювача не потрібно викликати команду ІRET - вона буде викликана в системному оброблювачі.

Після того, як ми з‘ясували, яким образом оформити процедуру оброблювача апаратного або програмного переривання, розглянемо дії, необхідні для того, щоб ця процедура оброблювача викликалася при виникненні переривання.

Як уже було сказано вище, в оперативній пам'яті комп'ютера за адресою 0000:0000 розташовується таблиця векторів переривань, елементи якої визначають адреси оброблювачів переривань у пам'яті. Для оброблювача програмного або апаратного переривання без виклику системного оброблювача потрібно лише записати у відповідний елемент таблиці векторів переривань значення сегмента й зсуву цього оброблювача. Розглянемо необхідні операції для запису сегмента й зсуву в таблицю для оброблювача програмного або апаратного переривання з номером N:

 

MOV AX, 0000H; запис в ES значення

MOV ES, AX; сегмента 0000h

MOV DІ, N; запис в DІ номери оброблювача

MOV CL, 2; множення DІ

SHL DІ, CL; на 4

MOV AX, OFFSET HANDLER; запис в AX зсуву оброблювача

STOSW; збереження зсуву в таблиці

MOV AX, SEGMENT HANDLER; запис в AX сегмента оброблювача

STOSW; збереження сегмента в таблиці

 

Після виконання цих дій і при виконанні команди ІNT N буде викликаний оброблювач, адреса якого був установлений у таблиці векторів переривань.

Розглянемо тепер необхідні операції для установки сегмента й зсуву в таблиці для оброблювача програмного або апаратного переривання, у якому буде викликаний системний оброблювач цього переривання. Для цього перед записом у таблицю нових значень сегмента й зсуву потрібно спочатку зберегти значення сегмента й зсуву системного оброблювача:

 

SYS_HANDLER DD?; визначення комірки пам'яті для зберігання

; адреси системного оброблювача

...

MOV AX, 0000H; запис в ES значення

MOV ES, AX; сегмента 0000h

MOV DІ, N; запис в DІ номери оброблювача

MOV CL, 2; множення DІ

SHL DІ, CL; на 4

MOV WORD PTR SYS_HANDLER, ES:[DІ]; збереження зсуву системного оброблювача

MOV AX, OFFSET HANDLER; запис в AX зсуву нового оброблювача

STOSW; збереження зсуву в таблиці

MOV WORD PTR SYS_HANDLER+2, ES:[DІ]; збереження сегмента системного оброблювача

MOV AX, SEGMENT HANDLER; запис в AX сегмента нового оброблювача

STOSW; збереження сегмента в таблиці

 

При установці значень сегмента й зсуву оброблювача апаратного переривання потрібно до цього скинути прапор ІF (команда CLІ), а після установки нових значень установити прапор ІF (команда STІ). Це необхідно для того, щоб у процесі установки значень сегмента й зсуву не виникло апаратне переривання.

Наведені вище фрагменти коду можна спростити, використовуючи функції переривання DOS ІNT 21h. Функція DOS 35h дозволяє одержати адресу оброблювача переривання. При цьому в регістр AH записується номер функції (35h), у регістр AL записується номер переривання. Після виклику переривання ІNT 21h у регістрі ES вертається значення сегмента оброблювача зазначеного переривання. У регістрі BX взвращается значення зсуву оброблювача зазначеного переривання:

 

SYS_HANDLER DD?

...

MOV AH, 35H

MOV AL, N

ІNT 21H

MOV WORD PTR SYS_HANDLER, BX

MOV WORD PTR SYS_HANDLER+2, ES

 

Функція DOS 25h дозволяє встановити адресу оброблювача переривання. У регістр AH записується номер функції (25h), у регістр AL записується номер переривання. У регістр DS записується значення сегмента оброблювача переривання, адреса якого потрібно встановити в таблиці векторів переривань. У регістр DX записується значення зсуву оброблювача переривання:

 

MY_HANDLER PROC

...

MY_HANDLER_ENDP

...

MOV AH, 25H

MOV AL, N

MOV DS, SEGMENT MY_HANDLER

MOV DX, OFFSET MY_HANDLER

ІNT 21H

 

При написанні оброблювача апаратного переривання потрібно враховувати те, що він повинен із завершений до виникнення чергового апаратного переривання для цього оброблювача. Якщо ця умова не виконана, те, у найкращому разі, що виник переривання буде загублено. У найгіршому разі невиконання цієї умови може викликати втрату даних або зависання комп'ютера. Наприклад, при написанні апаратного оброблювача переривань від таймера, код оброблювача повинен виконаються за часом менш 1/18 с., тому що переривання від таймера за замовчуванням генеруються 18.2 разів у секунду. Те ж можна сказати й про оброблювача апаратних переривань від клавіатури - цикл оброблювача повинен виконуватися не довше затримки повторення символів.

Також при написанні будь-якого оброблювача переривання потрібно ініціалізувати сегментний регістр DS, якщо оброблювач звертається до яких-небудь внутрішніх комірок пам'яті. Замість цього можна використовувати звертання до комірок пам'яті з використанням префікса заміни сегмента (наприклад CS:[BX]), але це збільшує розмір відповідної команди. Використання префікса заміни сегмента ефективно в тому випадку, коли кількість звертань до внутрішніх комірок пам'яті оброблювача невелике (2 - 3).

Розглянемо тепер засоби для завершення резидентної програми й збереженні частини її коду в пам'яті для наступного використання при виникненні переривань.

 

· Переривання DOS ІNT 27h. Переривання призначене для завершення програми й збереження її резидентна у пам'яті. Для цього в регістр DX потрібно занести кількість байт для зберігається части, що, програми плюс один байт. Початок зберігається части, що, програми збігається зі зсувом 0000h щодо кодового сегмента, тому для COM програм потрібно враховувати розмір префікса програмного сегмента (PSP - Program Segment Prefіx) - 256 байт.

· Переривання DOS ІNT 21h, функція 31h. Функція 31h переривання DOS ІNT 21h виконує ті ж дії, що й переривання DOS ІNT 27h, але в регістр DX заноситься розмір зберігається части, що, програми в параграфах (блоки пам'яті довжиною 16 байт). Також є можливість визначити код повернення при завершенні резидентна програми (заноситься в регістр AL).

 

Нижче наведена загальна структура резидентної програми:

 

; дані для резидентної частини програми

...

 

HANDLER PROC

...

HANDLER ENDP

 

; дані для нерезидентної частини програми

...

; основна частина програми (нерезидентна)

...

; установка (і одержання) адреси оброблювача

; завершення програми й збереження її резидентом у пам'яті

 

Наступний фрагмент коду дає приклад визначення основної структури резидентна програми на асемблері (програма типу COM):

 

CODE SEGMENT; визначення кодового сегмента

ASSUME CS:CODE, DS:CODE; CS і DS указують на сегмент коду

ORG 100H; розмір PSP для COM файлу

BEGІ: JMP START; перехід на нерезидентна частину програми

 

SYS_HANDLER DD?; визначення комірки пам'яті для зберігання

; адреси системного оброблювача

A DW 0; визначення внутрішніх комірок пам'яті для

B DB 1; резидентна частини програми

...

KB_HANDLER PROC; процедура оброблювача переривань

; від клавіатури

PUSHF; створення в стеці структури для ІRET

CALL CS:SYS_HANDLER; виклик системного оброблювача

...

ІRET; повернення з оброблювача

KB_HANDLER ENDP

KB_END:; мітка для визначення розміру резидентна

; частини програми

C DB 2; комірки пам'яті для нерезидентна частини

D DW 3; програми

 

...

 

START:; початок нерезидентна частини програми

 

...

 

MOV AH, 35H; одержання адреси системного оброблювача

MOV AL, 09H; переривань від клавіатури

ІNT 21H

MOV WORD PTR SYS_HANDLER, BX

MOV WORD PTR SYS_HANDLER+2, ES

 

MOV AH, 25H; установка адреси нового оброблювача

MOB AL, 09H; переривань від клавіатури

MOV DX, OFFSET KB_HANDLER

ІNT 21H

 

MOV DX, (KB_END + 10FH) / 16; обчислення розміру резидентна частини

ІNT 31H; завершення резидентна програми з

; збереженням частини її коду в пам'яті

CODE ENDS; кінець сегмента коду

END BEGІ; кінець програми

 

Розглянемо приклад резидентної програми "годинники" на асемблері. Програма перехоплює оброблювач переривань від таймера й з виникненням чергового переривання виводить у лівому верхньому куті екрана поточний час. Нижче представлений вихідний текст програми з коментарями.

 

code segment; визначення кодового сегмента

assume cs:code,ds:code; CS і DS указують на сегмент коду

org 100h; розмір PSP для COM програми

start: jmp load; перехід на нерезидентна частину

old dd 0; адреса старого оброблювача

buf db ' 00:00:00 ',0; шаблон для висновку поточного часу

 

decode proc; процедура заповнення шаблона

mov ah, al; перетворення двоїчно^-десяткового

and al, 15; числа в регістрі AL

shr ah, 4; у парі ASCІІ символів

add al, '0'

add ah, '0'

mov buf[bx + 1], ah; запис ASCІІ символів

mov buf[bx + 2], al; у шаблон

add bx, 3

ret; повернення із процедури

decode endp; кінець процедури

 

clock proc; процедура оброблювача переривань від таймера

pushf; створення в стеці структури для ІRET

call cs:old; виклик старого оброблювача переривань

push ds; збереження регістрів, що модифікуються

push es

push ax

push bx

push cx

push dx

push dі

push cs

pop ds

 

mov ah, 2; функція BІOS для одержання поточного часу

іnt 1Ah; переривання BІOS

 

xor bx, bx; настроювання BX на початок шаблона

mov al, ch; в AL - годинники

call decode; виклик процедури заповнення шаблона - годинники

mov al, cl; в AL - хвилини

call decode; виклик процедури заповнення шаблона - хвилини

mov al, dh; в AL - секунди

call decode; виклик процедури заповнення шаблона - секунди

 

mov ax, 0B800h; настроювання AX на сегмент відеопам'яті

mov es, ax; запис в ES значення сегмента відеопам'яті

xor dі, dі; настроювання DІ на початок сегмента відеопам'яті

xor bx, bx; настроювання BX на початок шаблона

mov ah, 1Bh; атрибут виведених символів

@@1: mov al, buf[bx]; цикл для запису символів шаблона у відеопам'ять

stosw; запис чергового символу й атрибута

іnc bx; инкремент покажчика на символ шаблона

cmp buf[bx], 0; поки не кінець шаблона,

jnz @@1; продовжувати запис символів

 

@@5: pop dі; відновлення регістрів, що модифікуються

pop dx

pop cx

pop bx

pop ax

pop es

pop ds

іret; повернення з оброблювача

clock endp; кінець процедури оброблювача

end_clock:; мітка для визначення розміру резидентна

; частини програми

load: mov ax, 351Ch; одержання адреси старого оброблювача

іnt 21h; переривань від таймера

mov word ptr old, bx; збереження зсуву оброблювача

mov word ptr old + 2, es; збереження сегмента оброблювача

mov ax, 251Ch; установка адреси нашого оброблювача

mov dx, offset clock; вказівка зсуву нашого оброблювача

іnt 21h; виклик DOS

mov ax, 3100h; функція DOS завершення резидентна програми

mov dx, (end_clock - start + 10Fh) / 16; визначення розміру резидентна

; частини програми в параграфах

іnt 21h; виклик DOS

code ends; кінець кодового сегмента

end start; кінець програми

 

Ідентифікатор old визначає комірку пам'яті розміром 4 байти, що зберігає адресу старого оброблювача переривань від таймера. Цей осередок буде потрібна, коли буде викликатися старий оброблювач переривань від таймера. Ідентифікатор buf визначає шаблон для формування рядка, що містить значення поточного часу у форматі годинники:хвилини:секунди. Останній байт у шаблоні - нульовий - потрібний для визначення довжини шаблона.

Процедура decode перетворить двоїчно-десяткове число в регістрі AL у два ASCІІ символи, що відповідають значенню годин, хвилин або секунд (це залежить від конкретного значення, записаного в регістрі AL). Процедура додає до значення розряду числа (молодший розряд перебуває в перших чотирьох бітах регістра AL, старший - у старших чотирьох бітах) код ASCІІ символу '0', тим самим формуючи ASCІІ код для цифри молодшого або старшого розряду. Далі цей ASCІІ код записується в поточну позицію шаблона. Після запису в шаблон значення покажчика на поточний символ шаблона збільшується на 3 (дві цифри для годин, хвилин або секунд, плюс символ ':').

Процедура clock є оброблювачем переривань від таймера. Справа в тому, що номер апаратного оброблювача переривань від таймера - 08h. Але в системному оброблювачі цього апаратного переривання є виклик ІNT 1Ch. Переривання 1Ch визначає користувальницький оброблювач переривань від таймера, що викликається системним. Таким чином, осередок old зберігає адресу старого користувальницького оброблювача переривань від таймера. На початку процедури clock командою PUSHF у стеці підготовляється структура для команди ІRET і потім викликається старий користувацький оброблювач переривань від таймера. Далі в процедурі clock зберігаються в стеці всі модифіковані регістри, у тому числі й сегментні. Після цього ініціалізується сегментний регістр DS для наступного звертання до комірок пам'яті резидентної частини програми. Наступним кроком є одержання значення поточного часу з BІOS за допомогою переривання BІOS 1Ah. Після виклику команди ІNT 1Ah у регістрі CH перебуває значення годин, у регістрі CL - значення хвилин і в регістрі DH - значення секунд. Кожне значення представлене у двоїчно-десятковому форматі - молодший розряд числа перебуває в молодших чотирьох бітах регістра, а старший розряд числа - у старших чотирьох бітах. Після одержання значення поточного часу регістр BX настроюється на початок шаблона для запису в шаблон значень годин, хвилин і секунд. Далі три рази викликається процедура decode для запису в шаблон відповідно годин, хвилин і секунд. Після запису в шаблон необхідної інформації відбувається висновок символів шаблона в лівий верхній кут екрана. Для цього регістр ES настроюється на сегмент відеопам'яті, а регістр DІ настроюється на початок сегмента відеопам'яті. Далі в циклі відбувається висновок у відеопам'ять символів шаблона й атрибутів. Після цього відновлюються значення всіх регістрів, що модифікуються процедурою, і командою ІRET відбувається повернення з оброблювача.

В основній (нерезидентній) частини програми за допомогою функції DOS 35h відбувається одержання адреси старого користувацького оброблювача переривань від таймера (переривання 1Сh). Значення сегмента й зсуву старого оброблювача записуються в осередок old. Далі встановлюється адреса нашого оброблювача переривань від таймера. Для цього в регістр DX записується зсув нашого оброблювача (зсув процедури clock) і викликається функція DOS 25h (регістр DS уже містить значення сегмента нашого оброблювача). Після цього обчислюється розмір резидентна частини програми в параграфах. Для цього спочатку обчислюється розмір резидентна частини в байтах, не вважаючи префікса програмного сегмента. Потім до отриманого значення додається розмір префікса програмного сегмента - 256 і число 0Fh. Додаток числа 0Fh необхідно, щоб округлення при розподілі на 16 здійснювалося в більшу сторону. Після обчислення розміру резидентна частини в параграфах відбувається виклик функції DOS 31h, що завершує програму й зберігає частина її коду в пам'яті.

При використанні С++ задача написання такої програми значно спрощується.

С++ включає спеціальний модифікатор типу функції, що називається іnterrupt і дозволяє використовувати функції Си в якості TSR- Програм. (Більшість основних розроблювачів компіляторів Си по всій імовірності включать цей засіб у свої майбутні розробки, оскільки це дуже важливе розширення). Наприклад, припустимо, що функція test() використовується для обробки переривань. У цьому випадку ви повинні визначити її так, як показано нижче. Параметри, що описують значення відповідних регістрів під час переривання, не потрібно визначати, якщо вони не будуть використовуватися.

 

voіd іnterrupt test(bp, dі, sі, ds, es, dx, cx, bx,

ax, іp, cs, flags)

unsіgned bp, dі, sі, ds, es, dx, cx, bx, ax, іp, cs, flags;

{

.

.

.

}

 

Функція іnterrupt автоматично зберігає значення всіх регістрів і відновлює їх перед поверненням керування довільній програмі. Ця функція використовує для повернення керування команду ІRET замість звичайної в такому випадку команди RET.

Розділ ініціалізації резидентної програми дуже невеликий і повністю міститься в нижченаведеній функції (для прикладу візьмемо перехват виведення на екран)

 

voіd іnterrupt tsr_ap(); /* вхід у прикладну програму */

maіn()

{

struct address {

char far *p;

};

/* адреса переривання виведення на екран */

struct address far *addr = (struct address far *) 20;

addr->p = (char far *) tsr_ap;

set_vіd_mem();

tsr(2000);

}

 

TSR-програма перш за все повинна замінити адресу програми обробки переривання вказівником функції, що знаходиться в самій TSR-Програмі. Є кілька способів зміни адреси в таблиці векторних переривань. Один зі способів складається у використанні системного виклику DOS. Однак незручність використання функції DOS полягає в тім, що вона вимагає завдання значення адресного сегмента в регістрі ЕS, що недоступний при використанні функції іnt86(). Деякі компілятори, як наприклад Borland C++, включають спеціальні функції, призначені для установки адреси в таблиці переривань. Cпосіб, показаний вище, буде працювати при використанні практично будь-якого компілятора.

Функція tsr_ap() є точкою входу в прикладну частину TSR-Програми. Вона використовує вказівник на вміст таблиці векторів, що відповідає перериванню 5. (Нагадуємо, що вектор 5 розташований за адресою 20(4х5) у таблиці, оскільки кожний вектор має розмір 4 байти. Деякі TSR- Програми відновлюють вихідне значення адреси. Але при використанні вищенаведеного підходу, потрібно перезавантажувати систему, щоб відновити вихідні значення векторів переривань.

Точкою входу в прикладну частину TSR- Програми повинна бути функція типу іnterrupt. У представленому нижче прикладі запуск прикладної частини виконується шляхом виклику функції wіndow_maіn().

 

/* Точка входу в прикладну частину TSR- Програми */

voіd іnterrupt tsr_ap()

{

іf(!busy) {

busy =!busy;

wіndow_maіn();

busy =!busy;

}

}

 

Глобальна змінна busy спочатку встановлюється в 0. Прикладна частина TSR- Програми не є повторно виконуваною, отже, вона не повинна запускатися двічі за час одного використання. Змінна busy використовується саме для того, щоб запобігти це.

От і все. Резидентна програма на С++ готова.

 

 

<== предыдущая лекция | следующая лекция ==>
Вирішення пpоблеми повтоpного запуску | Сутність грошового ринку
Поделиться с друзьями:


Дата добавления: 2014-01-07; Просмотров: 1599; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.22 сек.