Резидентный оброблювач клавіатури (перехоплення натискань клавіш і запис в файл)
Збережемо використовувані регістри 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, al; Запишемо в буфер скэн-код клавіші inc bl; Збільшимо усунення… Читати ще >
Резидентный оброблювач клавіатури (перехоплення натискань клавіш і запис в файл) (реферат, курсова, диплом, контрольна)
року міністерство освіти Украины.
Одеська державна академія холода.
Інститут інформаційних технологий.
Кафедра «Інформаційних систем».
Розробка резидентного оброблювача переривань від клавиатуры.
Курсової проект дисциплине.
«Системи програмування і операційні системы».
Руководитель Ненов А. Д. Исполнитель.
У розділі ст. грн. 333А Лазанюк А. С.
Зач. книжка № 983 214.
Защищён з оцінкою _____________________.
(особиста підпис) _______________.
р. Одеса 2000 г.
1. Задание…2.
2. Стислі теоретичні сведенья.
1. Резидентный оброблювач прерываний…3.
2. Захист резидентной програми від повторної установки…5.
3. Розвантаження резидентной програми з памяти…8.
4. Перехоплення прерываний…11.
5. Оброблювач прерываний…12.
6. Переривання від зовнішніх устройств…12.
7. Резидентный оброблювач переривань від клавіатури із залученням до системного обработчика…14.
3. Опис программы.
1. Опис для пользователя…19.
2. Опис для программиста…20.
3. Лістинг программы…24.
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+10Fh)/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 передбачені особливі випадки, коли процесор припиняє (перериває) виконання поточної програми розвитку й негайно передає управління программе-обработчику, спеціально написаної для обробки цій ситуації. Такі особливі ситуації діляться на два твань: переривання і виключення, залежно від цього, викликало цю ситуацію якесь зовнішнє пристрій чи виконувана процесором команда. Винятки діляться далі втричі типу: помилки, пастки і остановы, в залежність від того, коли з відношення до що отримала їх команді вони відбуваються. Помилки з’являються перед виконанням команди, тому оброблювач такого винятку отримає у ролі адреси повернення адресу помилковою команди (починаючи з процесорів 80 286). Пастки відбуваються відразу після виконання команди, отже оброблювач одержує у ролі адреси повернення адресу наступній команди. І, насамкінець, остановы можуть бути будь-якої миті і взагалі передбачати коштів повернення управління у программу.
Команда 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 «. У цьому фіксуються лише натискання клавіш. Запис відбувається за кожному XVI натисканні клавіші. Це зроблено, по-перше, зменшення ймовірності втрати «цінних» натискань при екстреному вимиканні комп’ютера, по-друге, для економії оперативної пам’яті, по-третє, задля збереження нормальної працездатності комп’ютера. Файл «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 з функцією 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 з функцією 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+10fh)/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 байта, тому після кожного XVI натискання необхідно зберегти буфер у робочому файлі. Для підрахунку натискань введена переменная-счётчик 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 «123 456 789 »; Таблиця для перекладу 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+10fh)/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 г.