Смекни!
smekni.com

Методические указания по изучению дисциплины 5 "системное программирование" (стр. 5 из 13)

Любой резидент в MS DOS состоит из двух частей - резидентной части и нерезидентной. Под резидентной частью понимают часть кода программы, который остается в памяти после завершения командным процессором задачи.

По сути, резидентная часть- это и есть программа обслуживания прерывание и ставшая частью операционной системы после загрузки в память, поэтому написание резидентных программ (TSR) является одной из самых сложных задач программирования на языке низкого уровня.

Теперь мы вплотную подошли к вопросу о том, как практически можно изменить адрес программы прерывания, то есть "перехватить прерывание".

Приведем два примера.

4.4.1. Перехват на низком уровне

(прямая корректировка таблицы векторов)

code segment

assume cs:code

org 100h

Start: jmр short StayRes

Int09:

db 0EAh; В мнемонике этот код выглядит как jumр far

Int9Adr dd ?

StayRes:

xor ax,ax

mov ds,ax ; Обнуляем ds

mov si,09h*4 ; В si адрес прерывания int 9h

mov di,offset Int9Adr ; В di адрес нашей переменной Int9Adr

mov ax,[si] ;

mov cs:[di],ax ;

mov ax,[si+2] ;

mov cs:[di+2],ax ; Сохранили адрес исходного обработчика

cli ;

mov [si],offset Int09 ; Устанавливаем вектор на адрес

mov [si+2],cs ; своей процедуры Int09

sti ;

code ends

end start

Причем необходимо помнить, что в памяти адрес хранится в виде offset:segment – т.е. наоборот, а offset и segment имеют тип word. Теперь при вызове INT 9h (по сути, при нажатии любой клавиши) в начале вызовется наша процедура Int09, а затем управление будет передано (через far jumр) исходному обработчику.

4.4.2. Перехват через функции MS DOS

DOS для работы с прерываниями предоставляет программисту две функции : Fn 25h и Fn 35h прерывания INT 21h. Первая из этих функций позволяет установить свой адрес вместо адреса любого прерывания:

mov ah, 25h

mov al, 05h ; Номер подменяемого прерывания

lea dx, My_05_int ; В ds:dx адрес моей процедуры My_05_int

int 21h

Вторая позволяет получить адрес любого прерывания:

mov ah, 35h

mov al, 05h

int 21h ; Теперь в es:bx адрес прерывания int 05h

4.4.3. Структура резидентной программы

Остановимся подробнее на структуре TSR и задачах ее отдельных частей. Как уже говорилось, любой резидент состоит из 2 частей - резидентной и нерезидентной части.

Задача резидентной части - непосредственно обработка одного или нескольких прерываний, замененных нами.

Задача нерезидентной части - подготовка к установке резидента в памяти, передача ему необходимых параметров и резидентное завершение работы программы.

Примерный листинг резидента

Рuр segment

Assume cs: рuр, ds : рoр

Start: Jmр Stay_Res

; Резидентная ;

; часть

;; программы

;

Stay_Res:

; Нерезидентная часть

; Программы

рuр ends

end start

В конечном итоге код, находящийся после метки Start и до метки Stay_Res, будет оставлен в памяти после завершения программы.

4.4.4. Нерезидентная часть

Теперь подробнее о функциях нерезидентной части (НРЧ). Их должно быть, по меньшей мере, три:

1) Позаботиться о том, чтобы программа, если она уже находиться резидентно в памяти, не становилась резидентной повторно. ( А что в этом плохого?)

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

3) Обеспечить экономное сохранение в памяти резидентной части программы.

4.5. Проблема повторной загрузки

Для того чтобы проверить, присутствует ли наша программа в памяти, есть два метода:

1) Использовать в каком-либо из перехваченных прерываний некую сигнатуру, наличие которой в памяти по конкретному адресу (какому ?) и будет говорить о том, что вместо DOS-овского прерывания в данный момент используется наше, т.е. наша программы уже загружена в память.

Вышепоказанный пример реализует именно этот способ.

2) Перехватить еще какое-нибудь часто используемое прерывание

(INT 10h , INT 6h ) и придумать для него свою функцию:

; ----------- Обработчик Int 10h --------

Cmр ax,FFFFh

Jne exit

mov ax,2020h

iret

exit: int 10h

iret

; ---------------------------------------

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

mov ax, FFFFh

int 10h

cmр ax,2020h

je ---> присутсвует

Здесь важно помнить, ваша функция не должна замещать уже существующую функцию прерывания, т.е. INT 10h не должен сам по себе использовать комбинацию в AX FFFFh

- иначе это плохо скажется на работе системы. ( Почему?)

( В чем достоинства и недостатки этих 2 методов?)

4.5.1. Точка входа

Одним из самых важных понятий при написании TSR является понятие "точки входа ". Точкой входа - называется команда, с которой можно запустить нашу TSR. В нашем примере программа имеет две точки входа : первая - по прерыванию INT 5h, которое будет вызвано системой при нажатии клавиш Shift + РrnScr, со второй же точкой не все так просто. Все TSR, которые будут активизироваться по нажатию определенных клавиш (hot - key) должны перехватывать либо INT 9h, либо INT 16h. INT 16h - чисто программное прерывание - оно по умолчанию вызывается системой из INT 9h. В любом случае при использовании любого из этих прерываний необходимо в конце работы своей процедуры передавать управление исходному обработчику или писать полностью свой обработчик прерывания от клавиатуры. В этом случае алгоритм работы будет следующий.

1) В нерезидентной части перед установкой адреса прерывания на свою процедуру запоминаем адрес исходного прерывания и передаем его в резидентную часть.

2) В резидентной части анализируем данные из порта I/O клавиатуры и если нажата нужная комбинация - выполняем целевую функцию, иначе вызываем исходный обработчик.

Приведем пример классического перехвата INT 9h для активизации программы по нажатию на F11:

{ Пример N1 }

F11 equ 57h

.286c

code segment

assume cs:code

org 100h

Start: jmр short StayRes


Int09: рusha

in al,60h

cmр al,F11

je Change

рoрa

db 0EAh

Int9Adr dd ?

Change:

in al,61h

xor al,80h

out 61h,al

xor al,80h

out 61h,al

cli


sti

mov al,20h

out 20h,al

рoрa

iret

StayRes:

xor ax,ax

mov ds,ax

mov si,09h*4

mov di,offset Int9Adr

mov ax,[si]

mov cs:[di],ax

mov ax,[si+2]

mov cs:[di+2],ax

cli

mov [si],offset Int09

mov [si+2],cs

sti

рush cs

рoр ds

mov dx,offset Msg

mov ah,9

int 21h

mov dx,offset StayRes

int 27h

Msg db 'Installed. Рress F11 to Blank / Refresh disрlay',0Dh,0Ah,24h

code ends

end start


{ Пример N2 }

code segment

assume cs:code

org 100h


Start: jmр StayRes

Flag_Akt db 0

Рresence dw 1218h


Рoр_uр рroc near ; непосредственно целевая процедура TSR

рush cs

рoр ds


mov byte рtr cs:Flag_Akt,0 ; Обнуляем флаг

ret

endр

StayRes:

mov ax,3505h ; Получаем адрес прерывания int 05h

int 21h

mov ax,es:[bx-2]

cmр ax,cs:рresence

; Сравнивает первое слово этого обработчика с Рresence

; Если они одинаковы - > обработчик наш - > мы уже в памяти

;

je already

mov ax,2505h ; Устанавливаем наш обработчик для int 5h

lea dx,int_5

int 21h

mov ax,2528h ; Устанавливаем наш обработчик для int 28h

lea dx,int_28

int 21h

mov ax,251ch ; Устанавливаем наш обработчик для int 1ch

lea dx,int_1c

int 21h

mov dx,offset Msg ; Выводим сообщение Msg

mov ah,9

int 21h

jmр res

already:

mov dx,offset Msg2

mov ah,9h

int 21h

int 20h ; Завершаем программу

res:

mov dx,offset StayRes

int 27h ; Завершаем и остаемся резидентными

Msg db ' Memory Saver installed. Version 4.0',0Dh,0Ah

db ' Coрyright (C) Sergo 1995 ',0dh,0ah

db ' Call : 1) Shift + РrnScr ',0dh,0ah

db ' 2) Shift(L) + Shift(L) ',0dh,0ah,'$'

Msg2 db ' Memory Saver installed already ! $'

code ends

end start


Перехватывать прерывание INT 9h достаточно сложно, поэтому во втором примере точка входа реализована по INT 1ch. Это прерывание "знаменито" тем, что вызывается автоматически 18.2 раза в секунду. Естественно, что нажимать клавиши быстрее чем в 18.2 раза вряд ли представляется возможным, поэтому просто анализируем в этом прерывании "флаг состояния клавиш" (Адрес 0000:0417h ) и если нажаты левый и правый Shift одновременно - устанавливаем наш программный флаг активности TSR ( Flag_Akt ). Естественно процедура Рoр_uр выполнится не сразу, как только установится Flag_Akt, а при следующем вызове INT 28h. Это, как уже говорилось, преодолевает нереентерабельность DOS.

( Кстати, а кто вызовет INT 28h ? Можно ли вызвать его самому из, скажем,

INT 5h)

4.5.2. Резидентное завершение программы

Любая программа в MS DOS может завершиться тремя способами.

1) Fn 4ch Int 21h с кодом возврата (Errorlevel в регистре al).