Системи програмування та операційні системи

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Міністерство освіти України

Одеська державна академія холоду Інститут інформаційних технологій

Кафедра «Інформаційних систем»

Розробка резидентного обробника переривань від клавіатури

Курсовий проект з дисципліни

«Системи програмування та операційні системи»

Керівник Нєнов А. Д. Виконавець

Ст. гр. 333А Лазанюк А. С.

Зач. книжка № 983214

Захищений з оцінкою _____________________

(Особистий підпис)

_______________

м. Одеса 2000

Зміст:

1. Завдання ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... .2

2. Короткі теоретичні відомості

2.1. Резидентний обробник переривань ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 3

2.2. Захист резидентної програми від повторної установки ... ... ... ... ... ... ... ... ... ... ... ... ... .. 5

2.3. Вивантаження резидентної програми з пам'яті ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 8

2.4. Перехоплення переривань ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 11

2.5. Обробник переривань ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 12

2.6. Переривання від зовнішніх пристроїв ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... .. 12

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

3. Опис програми

3.1. Опис для користувача ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 19

3.2. Опис для програміста ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... .20

3.3. Лістинг програми ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... .. ... .24

3.4. Рекомендації щодо поліпшення ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 32

4. Список використаної літератури ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... .. ... .33

1. Завдання

Розробка резидентного обробника переривань від клавіатури з підключенням до системного. Даний обробник повинен робити запис скан-кодів всіх натиснутих клавіш, а також фіксувати байт прапорів клавіатури при кожному натисканні. Оброблювач повинен мати механізм вивантаження з оперативної пам'яті вбудований у нього самого. Також програма повинна мати захист від повторної установки в оперативну пам'ять.

2. Короткі теоретичні відомості

2.1. Резидентний обробник переривань

Великий клас програм, що забезпечують функціонування обчислювальної системи (драйвери, програми шифрування і захисту даних, русифікатори, обслуговуючі програми типу електронних блокнотів або калькуляторів тощо), повинні постійно знаходитися в пам'яті і швидко реагувати на запити користувача або на якісь події, що відбуваються в обчислювальній системі. Такі програми носять назви програм, резидентних в пам'яті (Terminate and Stay Resident, TSR), або просто резидентних програм. Зробити резидентної можна як програму типу СОМ, так і програму типу

ЕХЕ, проте з огляду на те, що резидентна програма повинна бути максимально компактною, найчастіше як резидентних використовують програми типу СОМ.

Розглянемо типову структуру резидентної програми та системні засоби залишення її в пам'яті після ініціалізації (рис. 2.1).

text segment 'code'

assume CS: text, DS: text

org 100h main proc

jmp init; Перехід на секцію ініціалізації

; Дані резидентної секції програми

. . .

entry:; Текст резидентної секції програми

. . .

main endp

init proc; Секція ініціалізації

. . .

mov DX, (init-main +10 Fh) / 16; Paзмер в параграфах

mov АН, 3100h; функція "Завершити й залишити в

int 21h; пам'яті "init endp text ends

end main

Рис 2.1. Типова структура резидентної програми.

Програма пишеться у форматі СОМ, тому в ній передбачається тільки один сегмент, з яким зв'язуються сегментні регістри CS і DS; на початку сегменту резервується l00h байт дня PSP.

При запуску програми з клавіатури управління передається (відповідно до параметром директиви end) на початок процедури main. Командою jmp відразу ж здійснюється перехід на секцію ініціалізації, яка може бути оформлена у вигляді окремої процедури або входити до складу процедури main. У секції ініціалізації, зокрема, готуються умови для роботи програми вже в резидентном стані. Останніми рядками секції ініціалізації викликається функція DOS 31h, яка виконує завершення програми із залишенням в пам'яті вказаній її частини. Ця функція не може залишати резидентними програми розміром більше 64 Кб, але багато програм, написаних на асемблері, відповідають цій умові. Так як резидентні програми зменшують обсяг основної пам'яті, їх завжди пишуть на асемблері й оптимізують для досягнення мінімального розміру.

Розмір резидентної частини програми (у параграфах) передається DOS в регістрі DX. Визначити розмір резидентної секції можна, наприклад, наступним чином. До різниці зсувів mil-main, яка дорівнює довжині резидентної частини програми в байтах, додається розмір PSP (l00h) і ще число 15 (Fh) для того, щоб після цілочисельного ділення на 16 результат був заокруглений в більшу сторону.

З метою економії пам'яті секція ініціалізації розташовується я кінці програми і відкидається при її завершенні.

Системи програмування та операційні системи


Точка входу ®    main

при завантаженні jmp init

. Резидентні

                                      :    поля даних Резидентні частина

Системи програмування та операційні системи Точка входу ®     entry програми

при виклику. Резидентні

                                       : Коди

                                     iret

Системи програмування та операційні системи init

                            .   Секція

                                 :   ініціалізації Завершення програми

Функція DOS 31h ®      з складанням у пам'яті    

Системи програмування та операційні системи її резидентної частини

Рис. 2.2 Взаємодія елементів резидентної програми.

Функція 31h, закріпивши за резидентної програмою необхідну для її функціонування пам'ять, передає управління командному процесору і обчислювальна система переходить в початковий стан. Наявність програми, резидентної в пам'яті, ніяк не відбивається на ходу обчислювального процесу, за винятком того, що зменшується обсяг вільної пам'яті. Одночасно в пам'ять може бути додано будь-яке число резидентних програм.

На рис. 2.2 показані елементи резидентної програми та їх взаємодія.

Будь-яка резидентна програма має принаймні дві точки входу. При запуску з клавіатури програми типу. СОМ управління завжди передається на перший байт після PSP (IP = l00h). Тому практично завжди першою командою резидентної програми є команда jmp, передає керування на початок секції ініціалізації.

Після відпрацювання функції DOS 31h програма залишається в пам'яті в пасивному стані. Для того, щоб активізувати резидентну програму, їй треба якось передати управління і, можливо, параметри. Викликати до життя резидентну програму можна різними способами, але найбільш вживаним є механізм апаратних або програмних переривань. У цьому випадку в секції ініціалізації необхідно заповнити відповідний вектор адресою резидентної частини програми (точка entry на рис. 2.2). Адреса entry утворює другу точку входу в програму, через яку здійснюється її активізація. Очевидно, що резидентна секція програми повинна закінчуватися командою виходу з переривання iret.

Поля даних резидентної частини програми перемістилися в початок програми після команди imp. Це досить природне місце дня резидентних даних, тому що і при першому запуску, і при активізації сюди ніколи не буде передане управління. При заповненні в секції ініціалізації векторів не виникає проблем з перенастроюванням регістру DS, так як в програмі типу СОМ всі регістри вказують на єдиний сегмент програми. У секції ініціалізації передбачений, як це зазвичай робиться, висновок на екран повідомлення про завантаження програми в пам'ять.

Після запуску програми вона залишається в пам'яті і, активізуючись фактично апаратними перериваннями від клавіатури (а точніше - програмою BIOS, що активізується апаратними перериваннями від клавіатури).

2.2. Захист резидентної програми від повторної установки

Як правило, в секції ініціалізації завантажуються вектори переривань, через які буде активізуватися програма. Останніми рядками секції ініціалізації викликається функція DOS 31h, яка виконує завершення програми із залишенням в пам'яті її резидентної частини.

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

Найбільш поширеним методом захисту резидентної програми від повторної установки є використання переривання 2Fh, спеціально призначеного для зв'язку з резидентними програмами. При виклику цього переривання в регістрі АН задається номер функції (від 00h до FFh), а в регістрі AL - номер підфункції (в тому ж діапазоні). 00h - 7Fh зарезервовано для DOS / Windows 0B8h - 0BFh зарезервовано для мережевих функцій 0C0h - 0FFh відводиться для програм.

Для того, щоб резидентна програма могла відгукнутися на виклик переривання int 2Fh, в ній повинен бути обробник цього переривання. Фактично всі резидентні програми, як системні, так і прикладні, мають такі обробники, через які здійснюється не тільки перевірка на повторну установку, але і взагалі зв'язок з резидентної програмою: зміна режиму її роботи або одержання від неї в транзитну програму якихось параметрів. Завдання дії, яке належить виконати оброблювачу переривання 2Fh конкретної резидентної програми, здійснюється за допомогою номера підфункції, що розміщується перед викликом переривання в регістр AL

Таким чином, обробник переривання 2Fh резидентної програми повинен, перш за все, перевірити номер функції в регістрі АН; при виявленні "своєї" функції обробник аналізує вміст регістра AL і виконує витребувані дії, після чого командою iret передасть управління викликала його програмі. Якщо, проте, обробник виявив в регістрі АН "чужу" функцію, він повинен командою jmp CS: old_2fh передати управління по ланцюжку того оброблювачу, адреса якого був раніше у векторі 2Fh. У результаті виклик int 2Fh з будь-якої програми буде проходити по ланцюжку через всі завантажені резидентні програми, поки не досягне "своєї" програми або не поверне управління в викликала програму через обробник DOS (який, очевидно, завжди буде останнім у ланцюжку).

Природно, для комунікації з резидентної програмою повинен бути встановлений певний інтерфейс. Зазвичай при перевірці на повторну установку резидентна програма, якщо вона вже знаходиться в пам'яті, повертає в регістрі AL значення FFh, яке є ознакою заборони вторинної завантаження. Іноді для більшої надійності ідентифікації "своєї" функції резидентна програма, крім значення FFh в регістрі AL, повертає ще якісь обумовлені заздалегідь коди в інших регістрах. Часто через додаткові регістри передасться символьна інформація, наприклад, ім'я програми. У цьому випадку, якщо викликала програма з ім'ям DUMP.COM (тобто друга копія резидентної програми, яка з'ясовує, чи можна їй залишитися резидентної в пам'яті) отримує після виклику int 2Fh в регістрі AL значення FFh, а в регістрах СХ і DX символьні коди 'DU' і 'МР', вона може бути впевнена, що її перша копія вже знаходиться в пам'яті. Якщо ж у регістрі AL повернувся код FFh, а в регістрах СХ і DX-коди, наприклад, 'ОК' і 'RB', це, швидше за все означає, що закріплена за нашою програмою функція мультиплексного переривання вусі використовується інший резидентної програмою. У цьому випадку варто змінити функцію, щоб не порушувати конфліктних ситуацій.

У резидентну частину слід включити обробник переривання 2Fh. Його розташування в межах тексту програми не має особливого значення; ми помістили його на початку резидентної частини. Секція ініціалізації зазнала великі зміни. Вона повинна починатися з виклику переривання 2Fh з відповідною функцій для перевірки на повторну установку. Якщо перша копія програми вже завантажена, поточну програму слід завершити не функцією 3th (завершити і залишити в пам'яті), а звичайною функцією завершення 4Ch. Якщо ж нашої програми в пам'яті немає, то в секції ініціалізації, крім заповнення її "робочого" вектора, в даному випадку 03h, слід також встановити наш обробник мультиплексного переривання.

Серед функцій мультиплексного переривання, призначених для прикладних програм, ми довільно вибрали для нашої програми функцію F1h, а для перевірки на повторну установку підфункцію 00h. Резидентний обробник переривання 2Fh, що входить у нашу програму, перевіряє номери функції і підфункції і при виявленні будь-яких інших кодів передає управління наступному обробнику цього переривання. Якщо ж викликана функція F1h з підфункції 00h, обробник встановлює в регістрі AL значення FFh ("я вже завантажений") і повертає керування в викликала програму командою iret.

Секція ініціалізації починається з перевірки на повторну установку. Після завантаження в регістр АН номери функції (F1h), а в регістр AL - номери підфункції (00h), викликається переривання 2Fh. Після повернення з переривання аналізується вміст регістра AL Якщо обробник повернув значення FFh, програма повинна завершитися без залишення в пам'яті. Ці дії виконуються за міткою installed. Якщо повернуто інше значення, ініціалізація триває (для надійності варто було перевірити, повернутий чи саме 0). Зберігається старе вміст вектора 2Fh, встановлюється наш обробник цього переривання, після чого виконуються всі дії по встановленню, передбачені в старому варіанті програми динамічного дампа. При переході на мітку installed на екран виводиться повідомлення про неможливість повторної установки і виконується функція завершення 4Сh з кодом повернення 01h. Останнє, звичайно, має символічний характер, оскільки цей код в подальшому не аналізується.

2.3. Вивантаження резидентної програми з пам'яті

Слід зауважити, що в DOS відсутні кошти вивантаження резидентних програм. Єдиний передбачений для цього механізм - перезавантаження комп'ютера. Практично, однак, більшість резидентних

програмних продуктів мають вбудовані засоби вивантаження. Зазвичай вивантаження резидентної програми здійснюється відповідною командою, яка подається з клавіатури і сприймають резидентної програмою. Для цього резидентна програма повинна перехоплювати переривання, що надходять з клавіатури, і "виловлювати" команди вивантаження. Інший, боже простий спосіб полягає в запуску деякої програми, яка за допомогою, наприклад, мультиплексного переривання 2Fh передає резидентної програмі команду вивантаження. Найчастіше в якості "вивантажуються" використовують саму резидентну програму, точніше, її другу копію, яка, якщо її запустити в певному режимі, не тільки не намагається залишитися в пам'яті, але, навпаки, вивантажує з пам'яті свою першу копію.

Вивантаження резидентної програми з пам'яті можна здійснити різними способами. Найбільш простий - звільнити блоки пам'яті, які займає програмою (власне програмою та її оточенням) за допомогою функції DOS 49h. Інший, більш складний - використовувати в вивантажуються програмі функцію завершення 4Ch, змусивши її завершити не саму вивантажують, а резидентну програму, та ще й після цього повернути управління в вивантажують. У будь-якому випадку перед звільненням пам'яті необхідно відновити всі вектори переривань, перехоплені резидентної програмою. Слід підкреслити, що відновлення векторів представляє в загальному випадку значну і іноді навіть нерозв'язну проблему. По-перше, старе вміст вектора, що зберігається десь у полях даних резидентної програми, неможливо витягнути "зовні", з іншої програми, так як немає ніяких способів визначити, де саме його сховала резидентна програма в процесі ініціалізації. Тому вивантаження резидентної програми легше здійснити з неї самої, ніж з іншої програми. По-друге, навіть якщо вивантаження здійснює сама резидентна програма, вона може правильно відновити старе вміст вектора лише в тому випадку, якщо цей вектор не був пізніше перехоплений інший резидентної програмою. Якщо ж це відбулося, в таблиці векторів знаходиться вже адреса не вивантажується, а наступної резидентної програми, і якщо відновити старе вміст вектора, ця наступна програма "зависне", позбувшись коштів свого запуску. Тому надійно можна вивантажити тільки останню із завантажених резидентних програм.

У нашій програмі подфункция 00h переривання 2Fh служить для перевірки на повторну установку, а подфункция 01h - для вивантаження. У секцію ініціалізації додано рядки збереження старого вмісту вектора 09h. Це виконується точно так само, як і для вектора 2Fh - за допомогою функції DOS 35h. Старий вектор зберігається в комірці old_09h, що розміщується в резидентної частини програми. Оскільки вивантаження програми виконується за допомогою переривання 2Fh, текст обробника цього переривання ускладнюється.

Резидентний обробник переривання 2Fh насамперед перевіряє номер функції, що надійшов в регістрі АН, Якщо цей номер відрізняється від F1h, управління передається наступному обробнику по ланцюжку. Далі аналізується вміст регістра AL. Якщо AL = 00h, виконуються дії по захисту від повторної завантаження. Якщо AL = 01h, здійснюється перехід на мітку uninstall для виконання дій з вивантаження програми. При будь-якому іншому номері підфункції управління передається наступному обробнику по ланцюжку.

За мітці uninstall здійснюється збереження використовуваних далі регістрів (що робиться швидше для краси, ніж з потреби) та функції DOS 25h відновлюється з осередків old_09h і old_2Fh вихідне вміст відповідних векторів. Далі з осередку зі зміщенням 2Ch щодо початку PSP в регістр ES завантажується адреса оточення програми. Сегментний адресу вивільняється блоку пам'яті - єдиний параметр, необхідний для виконання функції DOS 49h. Розмір вивільняється блоку DOS відомий, він зберігається в блоці управління пам'яттю (МСВ). Далі звільняється блок пам'яті з самою програмою. Сегментний адресу цього блоку (адреса PSP) знаходиться в регістрі CS. Нарешті, командою iret управління передасться в програму, що викликала переривання 2Fh.

Функція 49h оповіщає DOS про те, що даний блок пам'яті вільний і може надалі використовуватись DOS. Це, однак, не заважає виконуватися завершальним рядках програми (в даному випадку - команді iret), оскільки звільнення пам'яті не руйнує її вмісту. Наша резидентна програма фізично зітреться лише після того, як в пам'ять буде завантажена чергова виконувана програма.

Якщо програма запускається з клавіатури з зазначенням будь-яких параметрів (імен файлів, ключів, які визначають режим роботи програми та інш.), То DOS, завантаживши програму в пам'ять, поміщає всі символи, введені після імені програми (так званий хвіст команди) в префікс програмного сегмента програми, починаючи з відносного адреси 80h. Хвіст команди поміщається в PSP в цілком певному форматі. У байт за адресою 80h DOS заносять число символів в хвості команди (включаючи пробіл, що розділяє на командному рядку саму команду і її хвіст). Далі (починаючи з байта за адресою 81h) слідують всі символи, введені з клавіатури до натискання клавіші. Завершується хвіст колом повернення каретки (13).

До даних секції ініціалізації додався рядок з очікуваним хвостом команди і байтовий прапор запиту на вивантаження.

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

Обнуленням регістру СН довжина хвоста "розширюється" на весь регістр СХ, що потрібно для організації циклу. Регістр DI налаштовується на перший байт хвоста, а регістр SI - на початок поля tail з очікуваною формою параметра. Регістр AL готується для виконання команди сканування рядки. Команда scasb порівнює в циклі байти хвоста з вмістом AL (кодом пропуску). Порівняння ведеться до тих пір, поки не буде знайдений перший символ, відмінний від пропуску. Ця операція необхідна через те, що оператор при введенні команди вивантаження може відокремити параметр команди від самої команди будь-яким числом пробілів, які потраплять у хвіст команди в PSP і перешкодять аналізувати введений параметр.

Вихід з циклу виконання команди scasb здійснюється, коли команда проаналізувала перший після пробілу символ. Після цього регістр DI вказує на другий символ параметра. Команда dec DI коригує покажчик DI, спрямовуючи його на перший значущий символ введеного параметра. Далі командою порівняння рядків cmpsb здійснюється порівняння трьох символів, що залишились хвоста. Якщо символи збігаються з параметром 'off', записаним у програмі, встановлюється прапор запиту на вивантаження. Якщо результат порівняння виявився негативним, прапор запиту не встановлюється (і, отже, неправильний параметр просто не сприймається). У будь-якій випадку здійснюється перехід на продовження програми, що починає перевіряти, чи не встановлена ​​чи вже ця програма в пам'яті. Якщо програма ще не встановлена, введений параметр не має сенсу. Ініціалізація здійснюється звичайним чином: зберігаються і встановлюються вектори і програма завершується із залишенням в пам'яті.

При наявності в пам'яті резидентного копії цієї програми здійснюється перехід на мітку installed, де насамперед перевіряється, чи встановлений прапор запиту на вивантаження. Якщо прапор скинутий, виводиться повідомлення про неможливість повторного завантаження і програма завершується з кодом повернення 1. Якщо прапор запиту встановлено, виконується вивантаження програми, яка полягає у виклику мультиплексного переривання 2Fh з функцією F1h і підфункції 01h. Резидентний обробник цього переривання, включений до складу нашої резидентної програми, відпрацює цю підфункцію, відновить вектори і звільнить зайняті програмою блоки пам'яті. Після повернення управління з обробника в поточну програму буде виведено повідомлення про успішну вивантаження та програма буде завершена функцією 4Ch з нульовим кодом повернення.

Укладена нами програма не позбавлена ​​від недоліків. Так, в ній аналізуються завжди тільки 3 значущих символу хвоста. Таким чином, програма буде вивантажено і при введенні команди (ім'я). Com onset. Інший недолік полягає в тому, що результат порівняння записаного в програмі хвоста з введеним з клавіатури параметром буде позитивним, тільки якщо з клавіатури введені малі літери. Команда (ім'я) OFF не призведе до вивантаження програми. По-справжньому слід було включити в програму перед аналізом хвоста перетворення символів параметра в прописні букви.

2.4. Перехоплення переривань

В архітектурі процесорів 80х86 передбачені особливі випадки, коли процесор припиняє (перериває) виконання поточної програми і негайно передає управління програмі-обробникові, спеціально написаної для обробки подібної ситуації. Такі особливі ситуації діляться на два буваючи переривання і виключення, в залежності від того, чи викликало цю ситуацію яке-небудь зовнішній пристрій або виконує процесор команда. Винятки діляться далі на три типи: помилки, пастки і аррестори, в залежності від того, коли по відношенню до причини, що їх команді вони відбуваються. Помилки з'являються перед виконанням команди, тому обробник такого вилучення отримає як адреси повернення адресу помилковою команди (починаючи з процесорів 80286). Пастки відбуваються відразу після виконання команди, так що обробник отримує як адреси повернення адреса наступної команди. І нарешті, аррестори можуть виникати в будь-який момент і взагалі не передбачати засобів повернення управління в програму.

Команда INT (а також INTO і INT3) використовується в програмах якраз для того, щоб викликати обробники переривань (або виключень). Фактично вони є винятками пастки, оскільки адреса повернення, який передасться оброблювачу, вказує на наступну команду, але так як ці команди були введені до поділу особливих ситуацій на переривання і виключення, їх практично завжди називають командами виклику переривань. З огляду на те, що обробники переривань і виключень в DOS зазвичай не розрізняють механізм виклику, за допомогою команди INT можна передавати управління, як на обробники переривань, так і винятків. Як показано в розділі 4, програмні переривання, тобто передача керування за допомогою команди INT, є основним засобом виклику процедур DOS і BIOS, тому що на відміну від виклику через команду CALL тут не потрібно знати адреси викликається процедури - досить тільки номери. З іншого боку інтерфейсу розглянемо, як будується обробник програмного переривання.

2.5. Обробники переривань

Коли в реальному режимі виконується команда INT, управління передається за адресою, що зчитується із спеціального масиву, таблиці векторів переривань, що починається в пам'яті за адресою 0000h: 0000h. Кожен елемент такого масиву представляє собою дальній адресу обробника переривання у форматі сегмент: зсув або 4 нульових байта, якщо обробник не встановлено. Команда INT поміщає в стек регістр прапорів і дальній адресу повернення, тому, щоб завершити обробник, треба виконати команди popf і retf або одну команду iret, яка в реальному режимі повністю їм аналогічна.

Після того як обробник написаний, наступний крок - прив'язка його до вибраного номера переривання. Це можна зробити, прямо записавши його адресу в таблицю векторів переривань.

Хоча пряму зміну таблиці векторів переривань і здається досить зручним, все-таки це не найкращий підхід до встановлення обробника переривання, і користуватися ним слід тільки у виняткових випадках, наприклад, всередині обробників переривань. Для звичайних програм DOS надає дві системні функції: 25h і 35h - встановити і вважати адресу обробника переривання, які і рекомендуються до використання в звичайних умовах.

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

2.6. Переривання від зовнішніх пристроїв

Переривання від зовнішніх пристроїв або апаратні переривання, - це те, що розуміється під терміном «переривання». Зовнішні пристрої (клавіатура, дисковод, таймер, звукова карта і т. д.) подають сигнал, по якому процесор перериває виконання програми і передає управління на обробник переривання. Всього на персональних комп'ютерах використовується 15 апаратних переривань, хоча теоретично можливості архітектури дозволяють довести їх кількість до 64.

- IRQ1 (INT 9) - переривання клавіатури, викликається при кожному натисканні та відпусканні клавіші на клавіатурі. Стандартний обробник цього переривання виконує досить багато функцій, починаючи з перезавантаження по Ctrl-Alt-Del і закінчуючи приміщенням коду клавіші в буфер клавіатури BIOS.

Найкорисніші для програм апаратні переривання - переривання системного таймера і клавіатури. Оскільки стандартні обробники цих переривань виконують безліч функцій, від яких залежить робота системи, їх не можна замінювати повністю.

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

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

Зрозуміло, обробка переривань не повинна займати багато часу: якщо переривання відбувається досить часто (наприклад, переривання послідовного порту може відбуватися 28 800 разів на секунду), його обробник обов'язково повинен виконуватися за більш короткий час. Якщо, наприклад, обробник переривання таймера буде виконуватися 1 / 32, 4 секунди, тобто половину часу між перериваннями, вся система стане працювати в два рази повільніше. А якщо ще одна програма з таким же довгим обробником перехопить це переривання, система зупиниться зовсім. Саме тому обробники переривань прийнято писати виключно на асемблері.

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

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

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

Системи програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системи IRQ INT Адреса системного     

Системи програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системи Апаратне Контролер Мікро-обробника int09h

Системи програмування та операційні системиСистеми програмування та операційні системи переривання переривань Вектор09 процесор з вектора 09

Системи програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системи на IRQ1

IRQ7 Запуск систем

Натискання або обробника int09h

Системи програмування та операційні системи відпускання Байт прапорів

Системи програмування та операційні системи будь-якої клавіші Системний клавіатури

Системи програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системи Контролер Порт 60h обробник [40h: 17h]

Системи програмування та операційні системиСистеми програмування та операційні системи клавіатури скан-код int09h | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |

Системи програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системи Клавіатура

Системи програмування та операційні системиСистеми програмування та операційні системи Ins

Системи програмування та операційні системи Скан-код Код Caps Lock

Системи програмування та операційні системи Кільцевій буфер ASCII Num Lock

Системи програмування та операційні системиСистеми програмування та операційні системи 40h: 1Eh введення Scroll Lock

Системи програмування та операційні системиСистеми програмування та операційні системи 40h: 1Ah Alt

Системи програмування та операційні системиСистеми програмування та операційні системиСистеми програмування та операційні системи Адреса головного скан ASCII Ctrl

Системи програмування та операційні системиСистеми програмування та операційні системи символу скан ASCII Shift лівий

Системи програмування та операційні системиСистеми програмування та операційні системи Скан ASCII Shift правий

Системи програмування та операційні системиСистеми програмування та операційні системи Програма скан ASCII

користувача Адреса хвостового

Системи програмування та операційні системиСистеми програмування та операційні системи · · · Символу

Системи програмування та операційні системи Запит на введення 40h: 3Ch

з клавіатури Введення самого

"Старого" символу

Рис. 2.3. Процес взаємодії системи з клавіатурою.

Роботою клавіатури управляє спеціальна електронна схема - контролер клавіатури. У його функції входить розпізнавання натиснутою клавіші і приміщення закріпленого за нею коду у свій вихідний регістр (порт) з номером 60h. Код клавіші, що надходить в порт, називається скан-кодом і є, по суті, порядковим номером кла-

виші. При цьому кожній клавіші присвоєні два скан-коду, що відрізняються один від одного на 80h. Один скан-код (менший, код натискання) засилається контролером в порт 60h при натисканні клавіші, інший (більший, код відпускання) - при її відпусканні.

Скан-код однозначно вказує на натиснуту клавішу, однак, за ним не можна визначити, чи працює користувач на нижньому або верхньому регістрі. З іншого боку, скан-коди присвоєні всім клавішах клавіатури, у тому числі керуючим клавішах,,, та ін Таким чином, очевидно, що визначення введеного символу має включати в себе не тільки зчитування скан-коду натиснутої клавіші, а й з'ясування того, чи не були перед цим натиснуті, наприклад, клавіші (верхній регістр) або (фіксація верхнього регістру). Усім цим аналізом займається програма обробки переривань від клавіатури.

Як натискання, так і відпускання будь-якої клавіші викликає сигнал апаратного переривання, що змушує процесор перервати виконувану програму і перейти на програму системного обробника переривань від клавіатури, що входить в систему BIOS. Оскільки обробник викликається через вектор 09h, його іноді називають програмою int09h.

Програма int09h, крім порту 60h, працює ще з двома областями оперативної пам'яті: кільцевим буфером введення, що розташовуються за адресами від 40h: lEh до 40h: 3Dh, куди, зрештою, поміщаються коди ASCII натиснутих клавіш, і бітом прапорів клавіатури, що знаходиться за адресою 40h : 17h, де фіксується стан керуючих клавіш (,, та ін.)

Програма int09h, отримавши управління в результаті переривання від клавіатури, зчитує з порту 60h скан-код і аналізує його значення. Якщо скан-код належить одній з керуючих клавіш, і, до того ж, являє собою код натискання, в байті прапорів клавіатури встановлюється біт (прапор), відповідний натиснутій клавіші. Наприклад, при натисканні правої клавіші в байті прапорів встановлюється біт 0, при натисненні лівої клавіші - біт 1, при натисненні будь-якої клавіші - біт 2 і т.д. Біти прапорів зберігають свій стан, поки клавіші (поодинці або в будь-яких комбінаціях) залишаються натиснутими. Якщо управляюча клавіша відпускається, програма int09h отримує скан-код відпускання і скидає відповідний біт у байті прапорів. Окрім стану зазначених клавіш, в байті прапорів фіксуються ще режими,, і (див. рис. 2.3).

Комп'ютери PC / AT мають другий байт прапорів клавіатури, що знаходиться за адресою 40h: 18h, і відображає стан керуючих клавіш на розширеній (101-клавішною) клавіатурі.

При натисканні звичайної, не керуючою клавіші, програма int09h зчитує з порту 60h її скан-код натискання і по таблиці трансляції скан-кодів в коди ASCII формує багатобайтових код, старший байт якого містить скан-код, а молодший код ASCII. При цьому якщо скан-код характеризує клавішу, то код ASCII визначає закріплений за нею символ.

Оскільки за кожною клавішею закріплено, як правило, не менше двох символів ("а" і "А", "1" і "!", "2" і "@" і т.д.), то кожному скан-коду відповідають , як мінімум, два коди ASCII. У процесі трансляції програма int09h аналізує стан прапорів, так що якщо натиснута, наприклад, клавіша Q (скан-код 10h, код ASCII літери Q - 51h, а букви q - 7lh), то формується багатобайтових код 1071h, але якщо клавіша Q натиснута при самій клавіші (зміна регістру), то результат трансляції складе 1051h. Той самий код 1051h вийде, якщо при натисканні клавіші Q був включений режим (великі літери), однак при включеному режимі і самій клавіші утворюється код 1071h, оскільки в такій ситуації клавіша на час натискання переводить клавіатуру в режим нижнього регістрі (малі літери).

Отриманий в результаті трансляції багатобайтових код засилається програмою int09h в кільцевої буфер введення, який служить для синхронізації процесів введення даних з клавіатури і прийому їх виконуваної комп'ютером програмою. Обсяг буфера складає 16 слів, при цьому коди символів витягуються з нього в тому ж порядку, в якому вони в нього надходили. За станом буфера стежать два покажчики. У хвостовому покажчику (слово за адресою 40: lCh) зберігається адресу першої вільної комірки, в головному покажчику (40: 1Ah) - адресу самого старого коду, прийнятого з клавіатури і ще не затребуваного програмою. Обидва адреси представляють собою зміщення відносно початку області даних BIOS, тобто числа від 1Eh до 3Ch. На початку роботи, коли буфер порожній, обидва покажчика - і хвостовий, і головний, вказують на першу клітинку буфера.

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

Виконувана програма, бажаючи отримати код натиснутої клавіші, повинна звернутися для цього до будь-яких системних засобів - функцій введення з клавіатури DOS (переривання 21h) або BIOS (переривання 16h). Системні програми за допомогою драйвера клавіатури (точніше кажучи, об'єднаного драйвера клавіатури і екрану, так званого драйвера консолі з ім'ям CON) зчитують з кільцевого буфера вміст клітинки, адреса якої знаходиться в головному покажчику, і збільшує цю адресу на 2. Таким чином, програмний запит на введення з клавіатури фактично виконує прийом коду не з клавіатури, а з кільцевого буфера.

Хвостовий покажчик, переміщаючись по буферу в процесі занесення до нього кодів, доходить, нарешті, до кінця буфера (адреса 40h: 3Ch). У цьому випадку під час вступу чергового коду адреса в покажчику не збільшується, а, навпаки, зменшується на довжину буфера. Тим самим покажчик повертається в початок буфера, потім продовжує переміщатися по буферу до його кінця, знову повертається в початок і так далі по кільцю. Аналогічні маніпуляції виконуються і з головним покажчиком.

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

Якщо комп'ютер не виконує ніякої програми, то активною є програма командного процесора COMMAND.COM. Активність COMMAND.COM полягає в тому, що він, поставивши запит до DOS на введення з клавіатури (з допомогою функції 0Ah переривання 21h) очікує введення з клавіатури чергової команди користувача. Як тільки в кільцевому буфері введення з'являється код символу, функція 0Ah переносить його у внутрішній буфер DOS, очищаючи при цьому кільцевий буфер введення, а також виводить символ на екран. При одержанні коду клавіші (0Dh) функція 0Ah завершує свою роботу, а командний процесор припускає, що введення команди закінчений, аналізує вміст буфера DOS і приступає до виконання введеної команди. При цьому командний процесор працює практично лише з молодшими половинами багатобайтових кодів символів, саме, з кодами ASCII.

Якщо комп'ютер виконує будь-яку програму, провідну діалог з оператором, то, як вусі зазначалося, введення даних з клавіатури (а точніше з кільцевого буфера введення) і виведення їх на екран з метою луна контролю організовує ця програма, звертаючись безпосередньо до драйвера BIOS ( int 16h) або до відповідної функції DOS (int 21h). Може трапитися, однак, що виконуваної програмі не потрібне введення з клавіатури, а оператор натиснув якісь клавіші. У цьому випадку вводяться символи накопичуються (за допомогою програми int09h) в кільцевому буфері введення і, природно, не відображаються на екрані. Так можна ввести до 15 символів. Коли програма завершиться, управління буде передано COMMAND. СОМ, який відразу ж виявить наявність символів в кільцевому буфері, витягне їх звідти і відобразить на екрані. Такий введення з клавіатури називають введенням з попередженням.

До цих пір мова йшла про символи і коди ASCII, яким відповідають певні клавіші терміналу і які можна відобразити на екрані. Це букви (прописні і рядкові), цифри, знаки пунктуації та спеціальні знаки, які використовуються в програмах і командних рядках, наприклад, |, $, * та ін Однак є ряд клавіш, яким не призначено на екрані символи. Це, наприклад, функціональні клавіші, ...; клавіші управління курсором,,,,, та ін При натисканні цих клавіш в кільцевої буфер введення засилається розширений код ASCII, в якому молодший байт дорівнює нулю, а старший є скан-кодом натиснутоюклавіші . Розширений коди ASCII надходять у буфер введення і у випадку натискання комбінацій керуючих і функціональних клавіш, наприклад, /, / (на додатковій цифровій клавіатурі), / і ін У цьому випадку, однак, в старший байт розширеного коду ASCII поміщається вже не скан- код клавіші, а певний код, спеціально призначений цієї комбінації клавіш. Природно, цього коду немає серед "звичайних" скан-кодів. Наприклад, клавіша, скан-код якої дорівнює 3Bh, може генерувати такі розширені коди ASCII:

ЗB00h / 5E00h / 6800h / 5400h

Отже, переривання, що виникає при натисканні відпуску будь-якої клавіші, обробляється за відносно складного алгоритму з системним обробником, що містяться в BIOS. Розглянемо приклади втручання в цей процес. Нижче наведено приклад прикладної програми, що виконує деяку обробку що надходять з клавіатури даних ще до активізації системного обробника.

3. Опис програми

3.1. Опис для користувача

Наведена нижче програма здійснює перехоплення переривання від клавіатури, і робить запис скан-кодів клавіш і байта прапорів клавіатури у файл з ім'ям «s_code & f.txt». При цьому фіксуються тільки натискання клавіш. Запис відбувається при кожному шістнадцятому натисканні клавіші. Це зроблено, по-перше, для зменшення ймовірності втрати «цінних» натискань при екстреному виключенні комп'ютера, по-друге, для економії оперативної пам'яті, по-третє, для збереження нормальної працездатності комп'ютера. Файл «s_code & f.txt» створюється в батьківському каталозі програми. Якщо при інсталяції файл вже існує, то програма, автоматично, запише в кінець поточну дату і час, після цього буде здійснюватися запис скан-кодів і прапорів у звичайному режимі після дати і часу. Програма є резидентної. Після того як вона буде успішно інстальована, на екрані з'явиться відповідний напис "Program installed". У ній передбачено захист від повторної установки. Таким чином одночасно в оперативній пам'яті комп'ютера не може знаходиться більше однієї копії програми, що практично зводить до нуля шанси не коректної роботи. При спробі запустити програму після того, як вона вже була інстальована, на екрані з'явиться відповідний напис "Program already installed". Також цю програму можна вивантажити з оперативної пам'яті після того як потреба в ній відпаде. Для цього слід запустити програму з ключем "off", тобто в командному рядку написати off. Після цього ви побачите рядок "Program is DIE", що сигналізує про успішну вивантаженні програми. При цьому вміст буфера буде записано у файл. Таким чином, у файл будуть записані всі натиснення клавіш аж до вивантаження програми. Якщо дану програму записати, наприклад, в autoexec.bat, то можна буде простежити час початку роботи користувача і які кнопки він після цього натискав.

Дана програма працює тільки в середовищі MS-DOS.

3.2 Опис для програміста

Програма пишеться у форматі СОМ, тому в ній передбачається тільки один сегмент, з яким зв'язуються сегментні регістри CS і DS; на початку сегменту резервується 256 байт дня PSP.

Ініціалізація.

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

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

mov cl, es: 80h

cmp cl, 0

je live

Якщо хвіст присутній, перевіримо чи не був введений очікуваний параметр "off". При позитивному результаті перевірки встановлюємо прапор вимоги вивантаження "flag" в одиницю і переходимо на подальший аналіз.

Потім викликаємо мультиплікувати переривання int2Fh c функцією F1h і підфункції перевірки на повторну установку 00h. Якщо наш обробник знаходиться в оперативній пам'яті - він поверне AL = FFH, і програма перейде на мітку installed. Перевіримо чи встановлений прапор вимоги вивантаження "flag". Якщо flag = 1 перейдемо на мітку unins, де перешлемо в першу (резидентну) копію програми запит на вивантаження з оперативної пам'яті за коштами переривання int2Fh і функцією F2h з підфункції 01h. Після чого відбувається виведення рядка "Program is DIE" на екран сигналізує про успішне видаленні резидентної частини програми. Після чого вийдемо з програми, звичайним чином, функцією 4С00h.

Якщо прапор вимоги вивантаження "flag" = 0, це говорить про те, що введена невідома команда, а наш резидент вже інстальований. У цьому випадку виведемо на екран попереджувальний напис про неможливість повторної установки програми "Program already installed" супроводжувану звуковим сигналом. Після цього завершимо програму функцією 4Ch з кодом повернення 01h.

Якщо після переривання int2Fh c функцією F200h, повернутися AL ¹ FFh, то нашого обробника в пам'яті не виявилося. Збережемо зміщення і сегменти системних обробників int09h і int2Fh, а потім заповнимо вектори зміщеннями наших обробників.

mov ax, 352fh

int 21h

mov word ptr cs: old_2fh, bx

mov word ptr cs: old_2fh +2, es

mov ax, 252fh

mov dx, offset new_2fh

int 21h

mov ax, 3509h

int 21h

mov word ptr cs: old_09h, bx

mov word ptr cs: old_09h +2, es

mov ax, 2509h

mov dx, offset new_09h

int 21h

Після цього зробимо пошук робочого файлу «s_code & f.txt» в поточному каталозі. Якщо файл не буде знайдений, то запуститься процедура div_f, яка створить робочий файл і запише в нього рядок «Skencode & Klav_flag file». Надалі в цей файл будуть записуватися скан-коди і байт прапорів клавіатури. Якщо файл вже існує, буде викликана процедура div2_f, яка допише в кінець файлу поточну дату і час.

Виведемо на екран рядок «Program installed» підтверджує установку програми. Останніми рядками цієї частини ініціалізації викликається функція DOS 31h, яка виконує завершення програми із залишенням в пам'яті вказаній її частини. Розмір резидентної частини програми (у параграфах) передається DOS в регістрі DX. Розмір резидентної секції визначається різницею зсувів end_res-main, яка дорівнює довжині резидентної частини програми в байтах, додається розмір PSP (l00h) і ще число 15 (Fh) для того, щоб після цілочисельного ділення на 16 результат був заокруглений в більшу сторону.

mov ax, 3100h mov dx, (end_res-main +10 fh) / 16 int 21h

З метою економії пам'яті секція ініціалізації розташовується я кінці програми і відкидається при її завершенні.

Функція 31h, закріпивши за резидентної програмою необхідну для її функціонування пам'ять, передає управління командному процесору і обчислювальна система переходить в початковий стан. Наявність програми, резидентної в пам'яті, ніяк не відбивається на ходу обчислювального процесу, за винятком того, що зменшується обсяг вільної пам'яті. Одночасно в пам'ять може бути додано будь-яке число резидентних програм.

Резидентна частина обробника.

Ця секція програми має дві точки входу:

1. Перехоплення переривання int09h (клавіатура). У результаті натиснення або відпускання клавіші на клавіатурі запускається процедура new_09h.

2. Перехоплення мультиплексорне переривання int2Fh. У результаті перехоплення мультиплексорне переривання запускається процедура new_2fh.

Обробник переривання від клавіатури.

Після запуску процедури new_09h збережемо використовувані регістри. Потім отримаємо скан-код останньої натиснутою клавіші. В іншому випадку відновимо регістри і передамо управління наступному по ланцюжку оброблювачу клавіатури (швидше за все це буде BIOS-івський обробник «int09h»).

in al, 60h

cmp al, 80h

ja exit

Потім запишемо цей скан-код в буфер, вважаємо байт прапорів клавіатури з області даних BIOS і також занесемо в буфер.

Наш буфер має об'єм 32 байта, тому після кожного шістнадцятого натискання необхідно зберігати буфер в робочому файлі. Для підрахунку натисків введена змінна-лічильник sch.

Збільшимо лічильник на 2, потім перевіримо повний чи буфер, порівнявши лічильник з 32. Якщо буфер не повний, збережемо використовувалися регістри і передамо управління наступному по ланцюжку оброблювачу клавіатури. Якщо буфер забитий, передамо управління процедурі fil.

Ця процедура відкриє наш робочий файл, встановить покажчик в кінець і допише стільки байт з буфера починаючи спочатку, скільки вкаже їй мінлива-лічильник.

mov ah, 40h

mov cl, sch

mov dx, offset bufer

int 21h

Це зроблено для того, щоб при видаленні програми з пам'яті в файл були записані всі скан-коди включаючи команду на видалення. Цей випадок розглянемо нижче. Після того як дані будуть збережені, відновимо використовувалися регістри і передамо управління наступному по ланцюжку оброблювачу клавіатури.

Оброблювач мультиплексорне переривання

Процедура new_2fh перехопить переривання 2Fh, і якщо переривання викликано разом з функцією F1h, то залежно від підфункції значення якої знаходиться в AL виконає наступні дії:

1. Якщо подфункция знаходиться в AL = 00h (код наявності в пам'яті нашого обробника), то наш обробник поверне в AL = FFh і вийде з переривання.

cmp al, 00h

je inst

...

inst: mov al, 0ffh

iret

2. Якщо подфункция знаходиться в AL = 01h (команда на видалення з пам'яті обробника), то збережемо використовувані регістри, викличемо процедуру fil (робота цієї процедури була описана вище), а потім звільнимо блоки пам'яті зайняті нашим обробником, відновимо старі вектори 09h і 2Fh. Відновимо використовувалися регістри і вийдемо з переривання.

Якщо мультиплексорне переривання було викликано з іншою функцією або з нашої функцією але з іншими підфункції, то обробник передасть управління наступному по ланцюжку оброблювачу мультиплексорне переривання.

cmp ah, 0f1h

jne out_2fh

cmp al, 00h

je inst

cmp al, 01h

je off

jmp short out_2fh

inst: mov al, 0ffh

iret

out_2fh:

3.3. Лістинг програми

text segment 'code'

assume cs: text, ds: text

org 256

main proc

jmp init

; Поля даних резидентної секції

old_2fh dd 0; Осередок для збереження системного вектора 2Fh

old_09h dd 0; Осередок для збереження системного вектора 09h

bufer db 34 dup (?); Буфер для скан-кодів і прапорів клавіатури

sch db 0; Лічильник натискань клавіш

filename db 's_code & f.txt', 0; Константа містить ім'я файлу з яким працює програма

; Оброблювач від клавіатури

new_09h proc

; Збережемо використовувані регістри

push ax

push bx

push cx

push dx

push ds

push cs; Набудуємо DS на наш сегмент для простоти програмування

pop ds

in al, 60h; Отримаємо скан-код клавіші

cmp al, 80h; Перевіримо, чи є скан-код кодом натискання

ja exit; Ні - на вихід

mov bh, 0, 0 ® BH

mov bl, sch; Поточне значення лічильника в BL

mov [bufer + bx], al; Запишемо в буфер скан-код клавіші

inc bl; Збільшимо зсув буфера

push es; Збережемо регістр ES

mov ax, 40h; Набудуємо ES на початок області даних BIOS

mov es, ax

mov al, es: [17h]; Занесемо байт прапорів клавіатури в AL

pop es; Відновимо ES

mov [bufer + bx], al; Запишемо байт прапорів у буфер

inc bl; Збільшимо зсув на 1

add sch, 2; Лічильник натискань +2

cmp sch, 32; Пора скидати буфер у файл?

je go; Так - на процедуру запису в файл

jmp exit; Ні - на вихід

go: call fil; Виклик процедури запису в файл

; Відновимо використовувалися регістри

exit: pop ds

pop dx

pop cx

pop bx

pop ax

jmp cs: old_09h; Передамо управління системного обробнику "int09h"

new_09h endp; Кінець процедури обробника від клавіатури

; Процедура запису у файл скан-кодів і прапорів клавіатури

fil proc

push cs; Набудуємо DS на наш сегмент

pop ds

mov ah, 3dh; Функція відкриття файлу

mov al, 1; для запису

mov dx, offset filename; DS: DX ¬ ASCIIZ імені файлу

int 21h

mov bx, ax; Дескриптор в ВХ

xor cx, cx; відчистить СХ

xor dx, dx; і DX

mov ax, 4202h; Функція установки покажчика в кінець файлу

int 21h

mov ah, 40h; Функція запису в файл

mov cl, sch; CL ¬ кількість байт

mov dx, offset bufer; DS: DX ¬ адреса буфера

int 21h

mov ah, 3eh; Функція закриття файлу

int 21h

mov sch, 0; обнулити лічильник

ret; Вихід з процедури

fil endp; Кінець процедури запису в файл

; Оброблювач мультиплексорне переривання

new_2fh proc

cmp ah, 0f1h; Перевіримо номер функції мультиплексорне переривання

jne out_2fh; Не наша - на вихід

cmp al, 00h; подфункция перевірки на повторну установку?

je inst; Так, повідомимо про неможливість повторної установки

cmp al, 01h; подфункция вивантаження?

je off; Так - на вивантаження

jmp short out_2fh; Невідома подфункция, на вихід

inst: mov al, 0ffh; Програма вже встановлена

iret; Вихід з переривання

out_2fh:

jmp cs: old_2fh; Перехід в наступний по ланцюжку обробник переривання 2Fh

; Вивантажимо програму з пам'яті, попередньо відновивши всі перехоплені нею вектори

; Збережемо використовувані регістри

off: push ds

push es

push dx

push ax

push bx

push cx

call fil; Виклик процедури запису у файл вмісту буфера

; Відновимо використовувалися регістри

pop cx

pop bx

pop ax

; Відновимо вектор 09h

mov ax, 2509h; Функція установки вектора

lds dx, cs: old_09h; Заповнимо DS: DX

int 21h

; Відновимо вектор 2fh

mov ax, 252fh; Функція установки вектора

lds dx, cs: old_2fh; Заповнимо DS: DX

int 21h

; Отримаємо з PSP адресу власного оточення і вивантажимо його

mov es, cs: 2ch; ES ® оточення

mov ah, 49h; Функція звільнення блоку пам'яті

int 21h

; Вивантажимо тепер саму програму

push cs; Завантажимо в ES вміст CS, тобто сегментний адресу PSP

pop es

mov ah, 49h; Функція звільнення блоку пам'яті

int 21h

; Відновимо використовувалися регістри

pop dx

pop es

pop ds

iret; Повернення в викликала програму

new_2fh endp; Кінець процедури обробки переривання 2Fh

end_res = $; Зміщення кінця резидентної частини програми

main endp

tail db 'off'; Очікуваний хвіст команди

flag db 0; Прапор вимоги вивантаження

tabl db '0123456789 '; Таблиця для перекладу BCD коду в ASCII

time db 25 dup (?); Осередок для збереження поточної дати і часу

; Процедура створення файлу

div_f proc

mov ah, 3ch; Функція створення файлу

mov cx, 0; Без атрибутів

lea dx, filename; DS: DX ¬ ASCIIZ імені файлу

int 21h

mov bx, ax; Дескриптор в ВХ

mov ah, 40h; Функція запису в файл

mov cx, buflen; CХ ¬ кількість байт

lea dx, buf; DS: DX ¬ адреса рядка

int 21h

mov ah, 3eh; Функція закриття файлу

int 21h

ret; Вихід з процедури

div_f endp; Кінець процедури створення файлу

; Процедура відкриття файлу і запису в нього поточної дати і часу

div2_f proc

mov [time], 0ah; Запис в змінну time маркерів

mov [time +1], 0dh; переходу на наступний рядок

mov ah, 3dh; Функція відкриття файлу

mov al, 1; для запису

mov dx, offset filename; DS: DX ¬ ASCIIZ імені файлу

int 21h

mov bx, ax; Дескриптор в ВХ

push bx; Збережемо дескриптор

xor cx, cx; відчистить СХ

xor dx, dx; і DX

mov ax, 4202h; Функція установки покажчика в кінець файлу

int 21h

mov ah, 02h; Функція читання часу з «постійних» «CMOS» годин реального часу

int 1ah; Переривання введення - виведення для часу

mov bx, offset tabl; DS: DX ¬ адреса таблиці

mov si, 2; Встановимо зсув для змінної time

mov ax, cx; Годинники і хвилини збережемо в AX

mov cx, 12; Встановимо лічильник зсуву

next: push ax; Збережемо AX

shr ax, cl; Зрушимо AX на CL

and al, 0fh; Отримаємо номер комірки в таблиці додавши маску

xlat; Отримаємо ASCII код числа

mov [time + si], al; Занесемо його в змінну time

inc si; Збільшимо на 1 зсув

cmp si, 4; Зміщення = 4?

je ras; Так, перехід на мітку ras

vw: sub cl, 4; Ні, зменшимо CL на 4

pop ax; Відновимо AX

cmp cl, -4; Порівняємо CL з -4

jne next; Не одно - виконаємо ще раз

jmp ent1; Так само - перехід на ent1

ras: mov [time + si ],':'; Запишемо в змінну time - «:»

inc si; Збільшимо на 1 зсув

jmp vw; Перейдемо на мітку vw

ent1: mov [time + si], ''; Запишемо в змінну time - «»

inc si; Збільшимо на 1 зсув

mov ah, 04h; Функція читання дати з «постійних» «CMOS» годин реального часу

int 1ah; Переривання введення - виведення для часу

mov ax, dx; Дату збережемо в AX

mov cx, 12; Встановимо лічильник зсуву

next1: push ax; Збережемо AX

shr ax, cl; Зрушимо AX на CL

and al, 0fh; Отримаємо номер комірки в таблиці додавши маску

xlat; Отримаємо ASCII код числа

mov [time + si], al; Занесемо його в змінну time

inc si; Збільшимо на 1 зсув

cmp si, 10; Зміщення = 10?

je ras1; Так, перехід на мітку ras1

vw1: sub cl, 4; Ні, зменшимо CL на 4

pop ax; Відновимо AX

cmp cl, -4; Порівняємо CL з -4

jne next1; Не одно - виконаємо ще раз

jmp ent2; Так само - перехід на ent2

ras1: mov [time + si ],'.' ; Запишемо в змінну time - «.»

inc si; Збільшимо на 1 зсув

jmp vw1; Перейдемо на мітку vw1

ent2: mov [time + si], 0ah; Запис в змінну time маркерів

mov [time + si +1], 0dh; переходу на наступний рядок

pop bx; Відновимо дескриптор

mov ah, 40h; Функція запису в файл

mov cx, 20; CХ ¬ кількість байт

mov dx, offset time; DS: DX ¬ адреса рядка

int 21h

mov ah, 3eh; Функція закриття файлу

int 21h

ret; Вихід з процедури

div2_f endp; Кінець процедури підготовки файлу

; Процедура ініціалізації

init proc

mov cl, es: 80h; Отримаємо довжину хвоста PSP

cmp cl, 0; Довжина хвоста = 0?

je live; Та програма запущена без параметрів

xor ch, ch; Тепер CX = CL = довжина хвоста

mov di, 81h; DS: SI ® хвіст в PSP

lea si, tail; DS: SI ® полі tail

mov al, ''; Приберемо пробіли з початку хвоста

repe scasb; Скануємо хвіст, поки прогалини

dec di; DI ® перший символ після прогалин

mov cx, 3; Очікувана довжина параметра

repe cmpsb; Порівнюємо введений хвіст з очікуваним

jne live; Введена невідома команда

inc flag; Введено «off», встановимо прапор запиту на вивантаження

; Перевіримо, не встановлена ​​чи вже дана програма

live: mov ah, 0f1h; Встановимо нашу функцію

mov al, 0; і підфункцію на наявність нашої програми в оперативній пам'яті

int 2fh

cmp al, 0ffh; встановлена ​​програма?

je installed; Так, за наявності запиту на вивантаження її можна вивантажити

; Збережемо вектор 2fh

mov ax, 352fh; Функція отримання вектора 2fh

int 21h

mov word ptr cs: old_2fh, bx; Збережемо зсув системного обробника

mov word ptr cs: old_2fh +2, es; Збережемо сегмент системного обробника

; Заповнимо вектор 2fh

mov ax, 252fh; Функція встановлення вектора переривання 2fh

mov dx, offset new_2fh; Зсув нашого обробника

int 21h

; Збережемо вектор 09h

mov ax, 3509h; Функція отримання вектора 09h

int 21h

mov word ptr cs: old_09h, bx; Збережемо зсув системного обробника

mov word ptr cs: old_09h +2, es; Збережемо сегмент системного обробника

; Заповнимо вектор 09h

mov ax, 2509h; Функція встановлення вектора переривання 09h

mov dx, offset new_09h; Зсув нашого обробника

int 21h

mov ah, 4eh; Функція пошуку файлу

lea dx, filename; DS: DX ¬ ASCIIZ імені файлу

int 21h

cmp ax, 12h; Файл не знайдено?

je creat; Так, створимо файл

call div2_f; Ні, виклик процедури відкриття файлу і запису в нього поточної дати і часу

jmp by; Перехід на мітку by

creat: call div_f; Виклик процедури створення файлу

; Виведемо на екран інформаційне повідомлення

by: mov ah, 09h; Функція виводу на екран

lea dx, mes; DS: DX ¬ адреса рядка

int 21h

mov ax, 3100h; Функція «завершитися і залишитися резидентним»

mov dx, (end_res-main +10 fh) / 16; Розмір в параграфах

int 21h

installed:

cmp flag, 1; Запит на вивантаження встановлений?

je unins; Так, на вивантаження

; Виведемо на екран інформаційне повідомлення

mov ah, 09h; Функція виводу на екран

lea dx, mes1; DS: DX ¬ адреса рядка

int 21h

; Виведемо застережливий звуковий сигнал

mov cx, 5; Кількість гудків

mov ah, 02h; Функція виводу на екран

l: mov dl, 07h; ASCII код зумера

int 21h

loop l; Повторимо CX раз

mov ax, 4c01h; Функція завершення з кодом повернення

int 21h

unins:

; Перейшла у перший (резидентну) копію програми запит на вивантаження

mov ax, 0f101h; Наша функція з підфункції вивантаження

int 2fh; Мультиплексні переривання

; Виведемо на екран інформаційне повідомлення

mov ah, 09h; Функція виводу на екран

lea dx, mes2; DS: DX ¬ адреса рядка

int 21h

mov ax, 4c00h; Функція завершення програми

int 21h

buf db 'Skencode & Klav_flag file', 0ah, 0dh

buflen equ $-buf

mes db 'Program installed $'

mes1 db 'Program already installed $'

mes2 db 'Program is DIE $'

init endp

text ends

end main

3.4. Рекомендації щодо поліпшення

- Головним недоліком цієї програми є незручне візуальне сприйняття записів у файлі. Тобто ми бачимо не ASCII-код який утворився в результаті натискання клавіші, а так званий скан-код (номер клавіші) і стан байта прапорів клавіатури, в якому він знаходився при цьому натисканні. При необхідності можна написати процедуру в нашому обробнику або у вигляді окремої програми, яка б аналізувала байт прапорів і залежно від цього підставляла ASCII-код відповідний скан-коду натиснутої клавіші.

- Другим недоліком нашої програми є не завжди зручний механізм вивантаження програми з оперативної пам'яті. Можна передбачити вивантаження нашої програми спеціальної не стандартною комбінацією клавіш.

- Третій суттєвий недолік програми полягає в тому, що наш обробник не реагує на клавіші Clrl + Alt + Del. Так як наш обробник перехоплює переривання від клавіатури раніше ніж системний обробник "int09h", то було б доцільно при цьому поєднанні скидати вміст буфера в файл, а потім передавати управління системного обробнику.

- Можна передбачити запис у файл autoexec.bat або config.sys рядка з шляхом до нашого файлу, при запуску програми з параметром що вводиться з командного рядка.

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

- Залежно від того в яких цілях застосовується даний обробник, можна заборонити натискання будь-якої клавіші, комбінації клавіш або послідовності.

Дана програма є шаблоном для резидентних обробників переривань, зокрема обробників переривань від клавіатури, і є величезним полем для творчості.

4. Список використаної літератури

1. П. І. Рудаков, К. Г. Фіногенов «Програмуємо на мові асемблера IBM PC», Обнінськ 1997р.

2. Зубков С.В. «Assembler для DOS, Windows і UNIX», Москва 2000р.

3. Богумірскій Б.С. «Керівництво користувача ПЕОМ», Санкт-Петербург 1994р.


Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Реферат
122.9кб. | скачати


Схожі роботи:
Системне програмування і операційні системи
Системне програмування та операційні системи
Операційні системи файлові системи
Операційні системи 2
Операційні системи
Мережеві операційні системи
Дискові операційні системи
Операційні системи Windows
Операційні системи 2 лютого
© Усі права захищені
написати до нас