Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

MOV ES:[BX + 2], CS. 11.5  Процедура выгрузки резидентной части. INT 21H   ; освобождение блока памяти. 11.6  Пример резидентной программы




MOV ES: [BX + 2], CS

STI

; завершение программы и загрузка резидентной части

MOV DX, (Init – Start + 10FH) / 16

MOV AH, 31H

MOV AL, 0

INT 21H

CODE ENDS

END Start

В примере функция DOS 4CH завершает программу. В регистре AL записывается код возврата (обычно 0).

Команды резидентной части программы можно передавать так же, как и проверять ее наличие – через прерывание 2FH с помощью регистров.

 

11. 5  Процедура выгрузки резидентной части

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

1. Восстановить все измененные вектора прерываний, включая 2FH;

2. Получить номер блока памяти, отведенного для окружения программы;

3. Освободить блок памяти, занимаемый окружением программы;

4. Освободить блок памяти, занимаемый самой программой.

Для получения блока памяти, занимаемого окружением программы, нужно прочитать значение слова, расположенного по смещению 2CH относительно префикса программного сегмента. Для программ типа COM это сделать легко, поскольку регистр CS настроен на начало префикса программного сегмента. Для программ типа EXE сегмент префикса программного сегмента можно получить, используя функцию 51H прерывания DOS 21H, которая в регистре BX возвращает значение сегмента префикса программного сегмента. При этом значение сегмента префикса программного сегмента должно быть сохранено в переменных резидентной программы, поскольку вызов функции 51H из резидентной части даст неверное значение сегмента префикса программного сегмента.

Для освобождения блока памяти можно использовать функцию DOS 49H, для которой в регистре ES указывается сегмент освобождаемого блока памяти.

Следующий пример демонстрирует процедуру обработки мультиплексного прерывания 2FH, которая освобождает занимаемую ей память по команде от нерезидентной части (программа типа COM).

OLD_2FH DD?

. . .

New2FH PROC

CMP CX, 1234H           ; проверка на “свою” программу

JNE Exit

CMP DX, 9876H

JNE Exit

CMP AL, 0

JNE M1

MOV CX, 4321H           ; запись в регистры ответа

MOV DX, 6789H

JMP M2

M1: CMP AL, 1

JNE M2

CALL Unload          ; вызов процедуры выгрузки

M2: IRET                      ; возврат из процедуры

Exit:     

JMP CS: OLD_2FH           ; передача управления по цепочке

New2FH ENDP

 

Unload PROC NEAR        ; процедура выгрузки

PUSH AX                  ; сохранение регистров

PUSH BX

PUSH ES

MOV BX, 2FH * 4    ; вычисление смещения

XOR AX, AX

MOV ES, AX

; восстановление старого значения вектора прерывания

MOV AX, WORD PTR CS: OLD_2FH

MOV ES: [BX], AX

MOV AX, WORD PTR CS: OLD_2FH + 2

MOV ES: [BX + 2], AX

; получение сегмента блока памяти переменных окружения

MOV ES, CS: [2Ch]

MOV AH, 49H          

INT 21H                              ; освобождение блока памяти

PUSH CS

POP ES

MOV AH, 49H          

INT 21H                              ; освобождение блока памяти

POP ES

POP BX

POP AX

RET

Unload ENDP

 

Процедура New2FH использует значение регистра AL, передаваемое нерезидентной частью, в качестве команды для выполнения различных действий. При нулевом значении регистра AL процедура заносит в регистры CX и DX ответ нерезидентной части. При единичном значении регистра AL вызывается процедура выгрузки резидентной части из памяти.

Следующий пример процедуры Unload демонстрирует выгрузку резидентной части из памяти для программы типа EXE.

OLD_2FH DD?

PSP DW?

Unload PROC NEAR        ; процедура выгрузки

PUSH AX            ; сохранение регистров

PUSH BX

PUSH ES

MOV BX, 2FH * 4    ; вычисление смещения

XOR AX, AX

MOV ES, AX

; восстановление старого значения вектора прерывания

MOV AX, WORD PTR CS: OLD_2FH

MOV ES: [BX], AX

MOV AX, WORD PTR CS: OLD_2FH + 2

MOV ES: [BX + 2], AX

; получение сегмента блока памяти переменных окружения

MOV ES, CS: PSP

PUSH ES

MOV ES, ES: 2CH

MOV AH, 49H         

INT 21H                    ; освобождение блока памяти

POP ES

MOV AH, 49H          

INT 21H                    ; освобождение блока памяти

POP ES

POP BX

POP AX

RET

PSP DW?

Unload ENDP

 

Все символы, которые были введены с клавиатуры после имени программы при ее запуске из командной строки, хранятся в префиксе программного сегмента начиная со смещения 81H. По смещению 80H хранится байт, значение которого представляет длину строки введенных символов.

Процедура обработки ключей командной строки сначала ищет признак начала ключа, например, это может быть символ ‘/’ или ‘–’. Затем процедура проверяет символы после начала ключа и до следующего символа пробела или начала ключа. Если эти символы представляет собой ключ программы, то процедура обработки ключей переходит к выполнению необходимых действий. Если эти символы не являются ключом программы, выдается сообщение об ошибке. Можно при отсутствии параметров программы, когда значение байта по смещению 80H равно 0, выдавать информацию о программе и ее ключах.

 

11. 6  Пример резидентной программы

Далее приводится пример полностью законченной резидентной программы типа EXE, в которой используются все элементы обобщенной структуры резидентной программы. Программа перехватывает прерывание таймера и выводит в правом верхнем углу экрана значение переменной от 0 до 9, которое увеличивается с приходом каждого прерывания от таймера.

 

STACK_SIZE EQU 128   ; размер стека 128 байт

 

Data1 SEGMENT              ; сегмент данных резидентной части

   VAR DB 0                   ; переменная для вывода на экран

Data1 ENDS

 

Stack1 SEGMENT PARA      ; сегмент стека резидентной части

   DB STACK_SIZE DUP (? ); область памяти под стек

Stack1 ENDS

 

Resident SEGMENT         ; сегмент команд резидентной части

   ASSUME CS: Resident, DS: Data1

; процедура обработки прерывания таймера

Timer PROC                  

      PUSHF              ; создание в стеке структуры для IRET

   CALL CS: OLD_TIMER; вызов старого обработчика

   MOV CS: OLD_AX, AX; сохранение AX

   MOV CS: OLD_SS, SS    ; сохранение SS

   MOV CS: OLD_SP, SP    ; сохранение SP

   MOV AX, Stack1          ; в AX – сегмент стека

   MOV SS, AX             ; переключение стека

   MOV SP, STACK_SIZE

   PUSH DS                        ; сохранение DS

   PUSH ES                        ; сохранение ES

   PUSH BX                       ; сохранение BX       

   MOV AX, Data1            ; в AX – сегмент данных

   MOV DS, AX             ; настройка DS на сегмент данных

   MOV AL, VAR          ; загрузка в AL значения VAR

   ADD AL, '0'        ; преобразование в ASCII-код цифры

   MOV AH, 1FH          ; атрибут символа

   MOV BX, 0B800H    ; настройка ES

   MOV ES, BX    ; на сегмент видеопамяти

; запись символа в видеопамять

   MOV WORD PTR ES: 158, AX      

   INC VAR          ; увеличение значения переменной

   CMP VAR, 10  ; если меньше 10,

   JB M9                ; то переход далее

   MOV VAR, 0    ; иначе – обнуление значения

M9: POP BX         ; восстановление BX

   POP ES              ; восстановление ES

   POP DS    ; восстановление DS

   MOV AX, CS: OLD_SS; в AX – старое значение SS

   MOV SS, AX; переключение стека

   MOV SP, CS: OLD_SP

   MOV AX, CS: OLD_AX; восстановление AX

   IRET             ; возврат из процедуры

Timer ENDP

; процедура обработки мультиплексного прерывания

New2FH PROC             

   CMP CX, 1234H       ; проверка

   JNE Exit                     ; на «свою»

   CMP DX, 9876H       ; программу

   JNE Exit

   CMP AL, 1                 ; если AL=1

   JE M1                          ; переход на выгрузку программы

   MOV CX, 4321H      ; запись в регистры

   MOV DX, 6789H      ; уникальных значений

   IRET                           ; возврат из процедуры

M1: CALL Unload        ; вызов процедуры выгрузки

   IRET                           ; возврат из процедуры

Exit: JMP CS: OLD_2FH; переход на старый обработчик

New2FH ENDP

 

   ASSUME DS: Resident

 

Unload PROC NEAR        ; процедура выгрузки

   PUSH AX                   ; сохранение AX

   PUSH BX                   ; сохранение BX

   PUSH DS                    ; сохранение DS

   PUSH ES                    ; сохранение ES        

   MOV AX, Resident   ; в AX – сегмент команд

   MOV DS, AX             ; настройка DS на сегмент команд

   MOV BX, 1CH * 4; вычисление смещения

   XOR AX, AX             ; настройка ES на начало

   MOV ES, AX        ; таблицы векторов прерываний

; восстановление старых значений векторов прерываний

   MOV AX, WORD PTR OLD_TIMER

   MOV ES: [BX], AX

   MOV AX, WORD PTR OLD_TIMER + 2

   MOV ES: [BX + 2], AX

   MOV BX, 2FH * 4

   MOV AX, WORD PTR OLD_2FH

   MOV ES: [BX], AX

   MOV AX, WORD PTR OLD_2FH + 2

   MOV ES: [BX + 2], AX

; настройка ES на префикс программного сегмента

   MOV ES, PSP                 

   PUSH ES                    ; сохранение ES

   MOV ES, ES: 2CH; в ES – сегмент окружения программы

   MOV AH, 49H           ; функция освобождения блока памяти

   INT 21H                ; вызов DOS

   POP ES        ; в ES – сегмент блока памяти программы

   MOV AH, 49H ; функция освобождения блока памяти

   INT 21H                     ; вызов DOS

   POP ES                       ; восстановление ES

   POP DS             ; восстановление DS

   POP BX                      ; восстановление BX

   POP AX                      ; восстановление AX

   RET                             ; возврат из процедуры

Unload ENDP

   OLD_AX DW?          ; старое значение AX

   OLD_SS DW?           ; старое значение SS

   OLD_SP DW?           ; старое значение SP

   PSP DW?                    ; значение сегмента префикса

; старое значение вектора прерывания от таймера

   OLD_TIMER DD?                  

; старое значение вектора мультиплексного прерывания

   OLD_2FH DD?

Resident ENDS

 

Code SEGMENT     ; сегмент команд нерезидентной части

   ASSUME CS: Code, DS: Data2

Start: MOV AX, Data2     ; в AX – сегмент данных

   MOV DS, AX             ; настройка DS на сегмент данных

; вызов процедуры обработки командной строки    

   CALL CmdLine              

   CMP BX, 0                 ; проверка возвращенного значения

   JNE M2

   MOV DX, OFFSET Msg1 ; смещение сообщения о программе

   JMP EndMsg              ; переход на выдачу сообщения

M2: MOV CX, 1234H  ; запись в регистры

   MOV DX, 9876H      ; уникальных значений

   MOV AL, 0                ; команда резидентной части

   INT 2FH                     ; вызов мультиплексного прерывания

   CMP CX, 4321H       ; проверка значений,

   JNE NoProg               ; возвращаемых

   CMP DX, 6789H       ; резидентной

   JNE NoProg          ; частью

   CMP BX, 2                 ; если BX< > 2, то

   JNE M8             ; переход на выдачу сообщения

   CALL Unload2          ; вызов процедуры выгрузки

   MOV DX, OFFSET Msg3 ; смещение сообщения о выгрузке

   JMP EndMsg              ; переход на выдачу сообщения

M8: MOV DX, OFFSET Msg4; смещение сообщения об ошибке

   JMP EndMsg              ; переход на выдачу сообщения

NoProg: CMP BX, 1         ; если BX=1, то переход на

   JE Load                  ; загрузку резидентной части

   MOV DX, OFFSET Msg2 ; смещение сообщения об ошибке

EndMsg: MOV AH, 09H           ; функция вывода строки

   INT 21H                         ; вызов DOS

   MOV AX, 4C00H      ; функция завершения программы

   INT 21H                         ; вызов DOS

 

   ASSUME DS: Resident

 

; процедура загрузки резидентной части

Load PROC                             

   PUSH DS                    ; сохранение DS

; в AX – сегмент команд резидентной части

   MOV AX, Resident        

; настройка DS на сегмент команд резидентной части

   MOV DS, AX                  

   XOR AX, AX             ; настройка ES на начало

   MOV ES, AX             ; таблицы векторов прерываний

   CLI                              ; запрет маскируемых прерываний

   MOV BX, 1CH * 4; вычисление смещения

; установка нового значения вектора прерывания таймера

   MOV AX, ES: [BX]

   MOV WORD PTR OLD_TIMER, AX

   MOV AX, ES: [BX + 2]

   MOV WORD PTR OLD_TIMER + 2, AX

   MOV AX, OFFSET Timer

   MOV ES: [BX], AX

   MOV AX, Resident

   MOV ES: [BX + 2], AX

   MOV BX, 2FH * 4; вычисление смещения

; установка нового значения вектора мультиплексного прерывания

   MOV AX, ES: [BX]

   MOV WORD PTR OLD_2FH, AX

   MOV AX, ES: [BX + 2]

   MOV WORD PTR OLD_2FH + 2, AX

   MOV AX, OFFSET New2FH

   MOV ES: [BX], AX

   MOV AX, Resident

   MOV ES: [BX + 2], AX

   STI                     ; разрешение маскируемых прерываний

   POP DS    ; восстановление DS

   MOV DX, OFFSET Msg5 ; смещение сообщения о загрузке

   MOV AH, 09H ; функция вывода строки

   INT 21H            ; вызов DOS

   MOV DX, Code; вычисление размера

   SUB DX, Data1; резидентной части

   ADD DX, 10H  ; в параграфах

; функция сохранения резидентной программы в памяти

   MOV AX, 3100H           

   INT 21H            ; вызов DOS

Load ENDP

 

Unload2 PROC         ; процедура передачи команды выгрузки

   MOV CX, 1234H; запись в регистры

   MOV DX, 9876H; уникальных значений

   MOV AL, 1       ; команда на выгрузку

   INT 2FH            ; вызов мультиплексного прерывания

   RET                   ; возврат из процедуры

Unload2 ENDP

 

   ASSUME DS: Resident

 

CmdLine PROC        ; процедура обработки командной строки

; функция получения сегмента префикса программного сегмента

   MOV AH, 51H                

   INT 21H            ; вызов DOS

   PUSH DS          ; сохранение DS

; в AX – сегмент команд резидентной части

   MOV AX, Resident

; настройка DS на сегмент команд резидентной части

   MOV DS, AX

; запись в переменную резидентной части

; значения сегмента префикса программного сегмента

   MOV PSP, BX

; настройка DS на префикс программного сегмента

   MOV DS, BX

; настройка SI на начало командной строки

  MOV SI, 80H

   LODSB                       ; загрузка длины строки

   CMP AL, 0                 ; если строка не нулевой длины

   JNE M3             ; переход на ее обработку

   XOR BX, BX    ; возвращаемое значение 0

   JMP M4                      ; переход на конец процедуры

M3: LODSB              ; загрузка очередного символа

   CMP AL, ' '                 ; пропуск пробелов

   JE M3                          ; в начале строки

   CMP AL, '/'                 ; проверка на признак ключа

   JE M5                          ; переход на обработку ключа

   XOR BX, BX    ; возвращаемое значение 0

   JMP M4                      ; переход на конец процедуры

M5: LODSB                   ; загрузка символа ключа

   CMP AL, 'i'                 ; проверки на возможные ключи

   JE M6

   CMP AL, 'I'

   JE M6

   CMP AL, 'u'

   JE M7

   CMP AL, 'U'

   JE M7

   XOR BX, BX    ; возвращаемое значение 0

   JMP M4                      ; переход на конец процедуры

M6: MOV BX, 1            ; возвращаемое значение 1 - загрузка

   JMP M4                      ; переход на конец процедуры

M7: MOV BX, 2            ; возвращаемое значение 2 - выгрузка

M4: POP DS                  ; восстановление DS

   RET                             ; возврат из процедуры

CmdLine ENDP

 

Code ENDS

 

Data2 SEGMENT    ; сегмент данных нерезидентной части

   Msg1 DB 'TIMER. EXE < /i | /u> $'

   Msg2 DB 'Program is not yet loaded! $'

   Msg3 DB 'Program successfully unloaded! $'

   Msg4 DB 'Program already loaded! $'

   Msg5 DB 'Program successfully loaded! $'

Data2 ENDS

 

; сегмент стека нерезидентной части

Stack2 SEGMENT PARA STACK

   DB 128 DUP (? ); область памяти под стек

Stack2 ENDS

 

   END Start          ; точка входа

 

Прерывание таймера 1CH вызывается обработчиком аппаратного прерывания таймера 08H, поэтому посылать в контроллер прерываний сигнал EOI в конце процедуры обработчика прерывания таймера Timer не нужно – это сделает системный обработчик.

В этом примере используется шесть программных сегментов – по 3 на каждую часть программы (резидентную и нерезидентную). Каждая часть имеет сегмент команд, сегмент данных и сегмент стека. В нерезидентной части кроме своего сегмента данных в процедуре Load используется сегмент команд резидентной части Resident для сохранения старых значений векторов прерываний 2FH (мультиплексное прерывание) и 1CH (прерывание таймера) в переменных резидентной части. Эти переменные не вынесены в сегмент данных резидентной части, так как требуется вызывать старый обработчик за пределами группы команд переключения стека, а в резидентной части стек прерванной программы не используется. В результате нет возможности сохранить значения сегментных регистров для настройки на сегмент Data1 и приходится переменные со значениями векторов хранить в сегменте команд резидентной части.

Процедура Load нерезидентной части выполняет следующие действия:

1. Сохраняет в переменных резидентной части старые значения векторов прерываний;

2. Устанавливает в таблице векторов прерываний новые значения;

3. Загружает в память резидентную часть и завершает нерезидентную часть с выдачей сообщения об успешной загрузке.

Следует обратить внимание на то, как вызывается процедура Load. Поскольку эта процедура завершает выполнение нерезидентной части, она не возвращает управление вызвавшей ее программе, поэтому для вызова этой процедуры используется команда безусловного перехода JMP.

Процедура Unload2 передает резидентной части команду на выгрузку. После вызова этой процедуры выводится сообщение об успешной выгрузке резидентной части программы из памяти.

Процедура CmdLine выполняет разбор командной строки и возвращает в регистре BX следующие значения:

1. 0, если ключ командной строки неверный или отсутствуют параметры программы;

2. 1, если в командной строке указан ключ загрузки программы в память;

3. 2, если в командной строке указан ключ выгрузки программы из памяти.

Процедура Unload резидентной части выполняет выгрузку резидентной части программы типа EXE из памяти, как это было описано выше.

Процедура обработки прерывания таймера Timer выполняет переключение стека и выводит на экран значение переменной VAR. Для вывода на экран используется запись в видеопамять. Сегмент видеопамяти для текстового режима 80 x 25 имеет значение B800H. Это значение помещается в регистр ES и затем по смещению 158H, что соответствует верхнему правому углу экрана, выводится символ, представляющий значение переменной VAR, и атрибут 1FH – белый текст на синем фоне.

Процедура New2FH аналогична рассмотренной выше и выполняет команды, поступившие от нерезидентной части – возвращает ответ или запускает процедуру выгрузки Unload.

Для вывода сообщения на экран используется функция DOS 09H. Для выполнения этой функции в регистр AH необходимо занести значение 09H, а в регистр DX – смещение строки выводимых символов. Строка символов должна заканчиваться символом ‘$’, который на экран не выводится, но служит для DOS признаком конца строки. После занесения в регистры необходимых значений вызывается прерывание DOS 21H.

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...