5.9.3. Выгрузка резидентной программы из памяти

Чтобы выгрузить резидентную программу из памяти, необходимо сделать три вещи: закрыть открытые программой файлы и устройства, восстановить все пере­хваченные векторы прерываний, и наконец, освободить всю занятую программой память. Трудность может вызвать второй шаг, так как после нашего резидента могли быть загружены другие программы, перехватившие те же прерывания. Если в такой ситуации восстановить вектор прерывания в значение, которое он имел до загрузки нашего резидента, программы, загруженные позже, не будут получать управление. Более того, они не будут получать управление только по тем Преры­ваниям, которые у них совпали с прерываниями, перехваченными нашей програм­мой, в то время как другие векторы прерываний будут все еще указывать на их

обработчики, что почти наверняка приведет к ошибкам. Поэтому, если хоть один

вектор прерывания не указывает на наш обработчик, выгружать резидентную про­грамму нельзя. Это всегда было главным вопросом, и спецификации AMIS и IBM

ISP (см. предыдущий раздел) являются возможным решением обозначенной

Резидентные программы Щ

проблемы. Если вектор прерывания не указывает на нас, имеет смысл проверить, не указывает ли он на ISP-блок (первые два байта должны быть OEBh 10h, а бай­ты 6 и 7 - К и В), и, если это так, взять в качестве вектора значение из этого блока и т. д. Кроме того, программы могут изменять порядок, в котором обработчики од­ного и того же прерывания вызывают друг друга.

Последний шаг в выгрузке программы - освобождение памяти - можно вы­полнить вручную, вызывая функцию DOS 49h на каждый блок памяти, который программа выделяла через функцию 48h, на блок с окружением DOS, если он не освобождался при загрузке, и наконец, на саму программу. Однако есть способ заставить DOS сделать все это (а также закрыть открытые файлы и вернуть код

возврата) автоматически, вызвав функцию 4Ch и объявив резидент текущим про­цессом. Посмотрим, как это делается на примере резидентной программы, зани­мающей много места в памяти. Кроме того, этот пример реализует все приемы, использующиеся для вызова функций DOS из обработчиков аппаратных ваний, о которых рассказано в разделе 5.8.3.

; scrgrb.asm

; Резидентная программа, сохраняющая изображение с экрана в файл.

; Поддерживается толвко видеорежим 13п  (320x200x256)   и толвко один файл.

HCI:

; Нажатие Alt-G создает файл scrgrb.bmp в текущей директории с изображением,

находившимся на экране в момент нажатия клавиши. ; Запуск с командной строкой /и выгружает программу из памяти.

API:

Программа занимает первую свободную функцию прерывания 2Dh (кроме нуля) в соответствии со спецификацией AMIS 3.6.

Поддерживаемые подфункции AMIS: 00h, 02h, 03h, 04h, 05h.

Все обработчики прерываний построены в соответствии с 1MB ISP.

Резидентная часть занимает в памяти 1056 байт,  если присутствует

и 66 160 байт, если EMS не обнаружен. tiny

.code

.1В6 Для сдвигов и команд

org 2Ch

envseg dw 7 ; Сегментный адрес окружения,

org 80h

cmd_len db 7 ; Длина командной строки,

cmd.line db 7 ;  Командная строка,

org        lOOh ; СОМ-программа.*

start:

imp        initialize ; Переход на инициализирующую часты.

; Обработчик прерывания 09h (IRQ1).

int09h_handler   proc far

jmp     short actual_int09h_handler        Пропустить ISP.

old_int09h dd •?

dw 424Bh

db OOh jmp       short hw_reset

db        7 dup (0)

actual Jnt09h_handler: ; Начало собственно обработчика INT 09h.

pushf

call      dword ptr cs:old_int09h ;  Сначала вызвать старый

; обработчик, чтобы он завершил аппаратное ; прерывание и передал код в буфер.

pusha

push ds push es

push

pop i mov cmp je

mov cmp

jne

mov

test jz

mov

; Это аппаратное прерывание сохранить все регистры.

надо

0040h

ds ; DS

di.word ptr ds:001Ah di.word ptr d's:001Ch exit_09h_handler

ax, word ptr [di] ah,22h

exit_09h_handler

al.byte ptr ds:0017h al,08h

exit_09h_handler

word ptr ds:001Ch,di

do_grab

byte ptr cs:io_needed,

safe_check

exit_09h_handler

call

mov cli

call

jc sti

call do_io

exit_09h_handler:

pop es pop ds popa iret

int09h_handler endp hw_reset:retf

; Обработчик INT 08h (IRQO)

сегментный адрес области данных BIOS.

Адрес головы буфера клавиатуры. Если он равен адресу хвоста, буфер пуст и нам делать нечего.

Иначе: считать символ. Если это не G (скан-код 22h),

выйти.

Байт состояния клавиатуры. Если Alt не нажата, выйти.

Иначе: установить адреса головы хвоста буфера равными, то есть опустошить его.

Подготовить BMP-файл с изображением" Установить флаг требующейся записи на диск.

Проверить, можно ли вызвать DOS.

Если да - записатв файл на диск.

Восстановитв регистры

и вернутвся в прерванную программу.

-

1

int08h_handler    proc far

jmp        short actual_int08h_handler ; Пропуститв ISP.

old int08h

?

dd

dw 424Bh

db OOh

short hw.reset .   db        7 dup actual_int08h_handler: pushf

jrap

(О)

Собственно обработчик.

call

pusha push

cli

cmp

je

call

jc

sti

call

no_io_needed:

pop popa і ret

int08h handler

dword.ptr cs:old_int08h    ;  Сначала вызвать стандартный обработчик, ; чтобы он завершил аппаратное прерывание ;   (пока оно не завершено,  запись на диске

ds

; Между любой проверкой глобальной переменной и принятием

; решения по ее значению - не повторно входимая область, ; прерывания должны быть запрещены, byte ptr cs:io_needed,0    ; Проверитв,

no_io_needed

safe_check no_io_needed

do_io

ds

;  нужно ли писатв на диск. Проверитв,

можно ли писатв на диск.

Разрешитв прерывания на время записи.

Записв на диск.

endp

Обработчик INT 13п.

Поддерживает флаг занятости INT 13h,  который тоже надо проверять перед записью на диск.

proc far

short actual_int13h_handler

dd ? dw 424Bh

db OOh

short hw reset db       7 dup (0)

;

pushf

byte ptr CS:bios_busy ;

int13h_handler

jmp old_int13h

jmp

Пропустить ISP.

inc

cli

call

pushf dec popf ret

ptr cs:old_int13h byte ptr

Собственно обработчик.

Увеличить счетчик занятости INT 13h.

Уменьшить счетчик.

2

endp

Имитация команды IRET, не восстанавливающая флаги из стека, так как обработчик INT 13h возвращает некоторые результаты в регистре флагов, а не в его копии, хранящейся в стеке.  Он тоже завершается командой ret 2.

Обработчик INT 28h.

Вызывается DOS, когда она ожидает ввода пользоваться.

с клавиатуры и функциями DOS

proc far

short actual_int28h_handler

dd ?

dw 424Bh

db OOh

short hw_reset

db   . 7 dup (0) actual int28h handler:"

int28h_handler jmp

old_int28h

imp

Пропустить ISP.

pushf

 

 

push

di

 

push

ds

 

push

cs

 

pop

ds

 

cli .

 

 

cmp

byte ptr io_needed,0

Проверить,

je

n.o_io_needed2

нужно ли писать

Ids

di.dword ptr in dos addr

 

cmp

byte ptr [di+1],1

Проверить,

ja

no_io_needed2

можно ли писать

 

 

занятости DOS не

sti

 

 

call

do_io

Запись на диск.

на диск.

на диск (флаг должен быть больше

1).

no_lo_needed2:

pop pop

popf

jmp

int28h handler

ds di

ptr

endp

cs:old_int28h

Переход на       обработчик INT

; Процедура do_grab.

; Помещает в буфер палитру и содержимое видеопамяти, формируя ВМР-файл. ;  Считает, что текущий видеорежим - 13п. do_grab

 

proc near

 

push

cs

 

pop

ds

 

call

ems_init

Отобразить наш буфер в окно EMS.

mov

dx.word ptr cs:buffer_seg

 

mov

es, dx

Поместить сегмент с буфером в ES и

mov

ds, dx

для следующих шагов процедуры.

mov

ax,1017h

Функция       - чтение палитры VGA

mov

bx,0

начиная с регистра палитры 0.

mov

cx,256

Все 256 регистров.

mov

dx,BMP_header_length

Начало палитры в BMP.

int

10h

Видеосервис BIOS.

Перевести палитру из формата,   в котором ее показывает (три байта на цвет,  в каждом байте 6 значимых бит), в формат, используемый в ВМР-файлах

ІІІМІ

функция

(4

байта на

std

fflOV

mov

mov

adj_pal: mov stosb lodsb shl

push

lodsb

shl

push

lodsb

shl

stosb

pop

stosb

pop

stosb loop цвет,  в каждом байте 8 значимых бит)

. si,BMP_header_length+256*3-1 ;

di,BMP_header_lengtn+256'4-l ;

cx,256 ;

al,0

al,2

ax

al,2

ax

al,2

ax

Движение от конца к началу. 81 - конец 3-байтной палитры. Ш - конец 4-байтной палитры. СХ - число цветов.

Записать четвертый байт (0).

Прочитать третий байт.

Масштабировать до 8 бит.

Прочитать второй байт. Масштабировать до 8 бит.

Прочитать третий байт. Масштабировать до 8 бит

и записать эти три байта в обратном порядке.

adj_pal

Копирование видеопамяти в BMP. В формате BMP строки изображения первый байт соответствует нижнему

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

eld

 

у Движение от начала к концу (по строке).

push

' OAOOOh

 

pop

ds

 

mov

Si,320*200

;; DS:SI - начало последней строки на экране.

mov

di,bfoffbits

„ ES:DI - начало данных в BMP.

mov

dx,200

„ Счетчик строк.

е_1оор:

 

 

mov

cx,320/2

у Счетчик символов в строке.

rep

movsw

у  Копировать целыми словами, так быстрее.

sub

si,320.2

у  Перевести SI на начало предыдущей строки.

dec

dx

у Уменьшить счетчик строк.

jnz

bmp_write_loop

у Если С - выйти из цикла.

call

ems_reset

у Восстановить состояние EMS до вызова do_grab.

ret

 

 

 

endp

 

do_grab

do_io.

Создает файл и записывает

do io

push cs

pop ds

proc

в него содержимое буфера. near

ax

282

ваші:

программирования

mov

byte ptr io_needed,0

Сбросить флаг требующейся записи на

call

ems_init

Отобразить, в окно EMS наш буфер.

mov

ah,6Ch

; Функция DOS 6Ch.

mov

bx,2

; Доступ - на чтение/запись.

mov

cx,0

Атрибуты - обычный файл.

mov

dx,12h

Заменять файл, если он существуету

 

 

создавать,   если нет.

mov

filespec

;  DS-.SI - имя файла.

int

21h

Создать/открыть файл.

mov

bx,ax

; Идентификатор файла - в ВХ.

mov

ah,40h

Функция DOS 40h.

mov

cx,bfsize

;  Размер BMP-файла.

mov

ds.word ptr buffer_seg

 

mov

dx,0

DS:DX - буфер для файла.

int

21h

Запись в файл или устройство.

mov

ah,68h

Сбросить буфера на диск.

int

21h

 

mov

ah,3Eh

Закрыть файл.

int

21h

 

call

ems_reset

 

ret

do_io endp

у Процедура

;  Если буфер расположен в EMS, подготавливает, его для чтения/записи.

ems_init

 

near

 

cmp

ptr

Если не используется EMS

cmp

dx,0

(EMS-идентификаторы начинаются с

je

ems_init_exit

ничего не делать.

mov

ax,4700h

Функция EMS 47h: ■

int

67h

сохранить EMS-контекст.

mov

ax,4100h

Функция EMS

int

67h

определить адрес окна EMS.

mov

word ptr buffer_seg, bx

Сохранить его.

mov

ax,4400h

Функция EMS 44h:

mov

bx,0

Начиная со страницы 0,

int

67h

отобразить страницы EMS в окно.

mov

ax,4401h

 

inc

bx

 

int

67h

;   Страница 1 .

mov

ax,4402h

 

inc

bx

 

int

67h

;   Страница 2.

mov

ax,4403h

 

inc bx

int 67h ems_init_exit'.

ret

ems^init endp

; Процедура

; Восстанавливает состояние ems_reset proc near

; Страница 3.

EMS.

mov

ptr

cmp

dx,0

 

 

je

ems_reset_exit

 

mov

ax,4800h

 

Функция EMS 48h:

int

67h

 

восстановить

ems_reset_exit:

 

 

 

ret

 

 

 

ems reset

endp

 

 

; Процедура

 

 

; Возвращает CF

= О,  если в данный момент

можно пользоваться функциями DOS,

; и CF = 1, если нельзя.

 

 

safe check

proc

near

 

push

es

 

 

push

cs

 

 

pop

ds

 

 

les

di, dword

ptr in_dos_addr ;

Адрес флагов занятости DOS.

cmp

word, ptr

es:[di],0

Если один из них не О,

pop

es

 

 

jne

safe_check_falled

пользоваться DOS нельзя.

cmp

byte ptr

bios_busy,0

Если выполняется прерывание

jne

safe_check_failed

тоже нельзя.

clc

 

 

CF = О.

ret

 

 

 

safe_check_failed:

 

 

stc

 

 

CF = 1.

ret

 

 

 

safe_check

endp

 

 

in_dos_addr

dd

9

Адрес флагов занятости DOS.

io needed

db

о

І, если надо записать файл на диск.

bios busy

db

О

1, если выполняется прерывание INT 13h.

buffer_seg

dw

О

Сегментный адрес буфера для файла.

ems handle

dw

0

Идентификатор EMS.

filespec

db

'scrgrb.bmp',0

Имя файла.

; Обработчик INT

2Dh

 

 

hw reset20:retf

 

 

 

int2Dh_handler

proc

far

 

jmp

short actual int2Dh_handler

; Пропустить ISP.


Сложные приемы программирования

old_int2Dh

dd ?

 

 

dw 424Bh

 

 

db

 

jmp

short hw_reset2D

 

 

db        7 dup (0)

 

actual_int2Dh_

handler:

Собственно обработчик.

 

db

Начало команды CMP АН, число.

mux_id

db ?

Идентификатор программы.

je

its us

Если вызывают с чужим АН - это не нас.

jmp

dword ptr cs:old_int2Dh

 

its_us:

 

 

cmp

al,06

Функции AMIS 06h и выше

jae

int2D_no

не

cbw

 

АХ = номер функции.

mov

di, ax

DI = номер функции.

shl

di,1

х 2, так как jumptable - таблица слов.

jmp

word ptr cs:jumptable[di]

Переход на обработчик функции.

jumptable

dw      offset int2D_no

 

dw       offset int2D 02,offset int2D no

 

dw        offset int2D 04,offset int2D 05

int2D_ 00:

 

Проверка наличия.

mov

al.OFFh

Этот номер занят.

mov

cx,O10Oh

Номер версии программы 1.0.

push

cs

 

pop

dx

РХ:Р1 - адрес АМ1Б-сигнатуры.

mov

di,offset amis_sign

 

iret

 

 

int2D_no:

 

Неподдерживаемая функция.

mov

al.OOh

Функция не поддерживается.

iret

 

 

unload_failed:

; Сюда передается управление,  если хотв один из векторов

 

;   прерываний был перехвачен кем-то после нас.

mov

al,01h ;

Выгрузка программы не удаласв.

iret

 

 

int2D_ 02:

 

Выгрузка программы из памяти.

cli

 

Критический участок.

push

О

 

pop

ds

; Р8 - сегментный адрес

 

 

; таблицы векторов прерываний.

mov

ах, cs

Наш сегментный адрес.

; Проверить,

все ли перехваченные прерывания по-прежнему указывают на нас.

Обычно достаточно проверить только сегментные адреса  (DOS не загрузит другую

программу с

нашим сегментным адресом).

 

cmp

ах,word ptr ds:[09h*4+2]

 

]ne

unloadjailed

 

cmp

ax,word ptr ds:[13h*4+2]

 

]ne

unload_failed

 

cmp

ptr

 

jne

unload_failed

 

cmp

ptr

 

jne

unloacLfailed

 

cmp

ax,word ptr ds:[2Dh»4+2]

 

jne

unload_failed

 

push

bx

 

 

push

dx

 

 

новить

старые обработчики прерываний.

 

mov

ax,2509h

 

 

Ids

dx, dword

ptr

 

int

21h

 

 

mov

ax,2513h

 

 

Ids

dx, dword

ptr

 

int

21h

 

 

mov

ax,2508h

 

 

Ids

dx,dword

ptr

 

int

21h

 

 

mov

ax,2528h

 

 

Ids

dx,dword

ptr cs:old_int28h

 

int

21h

 

 

mov

ax,252Dh

 

 

Ids

dx, dword

ptr cs:old_int2Dh

 

int

21h

 

 

mov

ptr

 

cmp

dx,0

 

 

je

no_ems_to

_unhook

 

mov

ax,4500h

;

 

int

67h

 

 

jmp

short ems

_unhooked

 

Адрес возврата

no_ems_to_unhook: ems_unhooked:

; Собственно выгрузка резидента. mov

int 21h

mov word ptr cs:[16h],bx

pop dx

pop bx

mov word ptr cs:[0Ch],dx

mov word ptr cs:[0Ah],bx

push cs

pop bx mov

int 21 h

Если используется EMS.

Функция EMS 45h:

освободить выделенную память.

Функция DOS h:

получить сегментный адрес PSP прерванного процесса      данном случае PSP - копии

нашей программы, запущенной с ключом /и).

Поместить его в поле

"сегментный адрес предка" в нашем PSP.

Восстановить адрес возврата из стека

и поместить его в поле "адрес перехода при

завершении программы" в нашем PSP.

ВХ = наш сегментный адрес Функция DOS 50h: установить текущий PSP.

PSP.

в стек.

Теперь DOS считает наш резидент текущей программой, а scrgrb.com /и - вызвавшим его процессом,   которому и передаст управление после вызова следующей функции.

mov int

int2D_04:

mov mov

iret

int2D 05:

mov mov mov

iret

int2Dh_handler ax,4CFFh

21h

dx, cs

bx,offset amis_hooklist

al.OFFh dx, cs

bx,offset amis_hotkeys

; Функция DOS 4Ch: ;  завершить программу.

; Получить список перехваченных

; прерываний.

; Список в DX:BX.

Получить список "горячих" клавиш. Функция поддерживается. Список в DX:BX.

endp

; AMIS: Сигнатура для резидентной программы. amis_sign          db        "Cubbi..." ;  8 байт.

db        "ScrnGrab" ;  8 байт.

db        "Simple screen grabber using EMS",0

; AMIS: Список прерываний.

amis hooklist

; AMIS: Список amis_hotkeys db dw

db dw db

dw db

dw

'горячих'

db

db -

db dw

dw

db 09h

offset

08h

offset

28h

offset

2Dh

offset

клавиш.

1 1

22h 08h

0 1 int09h_handler

int08h handler

int28h_handler

int2Dh_handler

Скан-код клавиши (G). Требуемые флаги клавиатуры.

;  Конец резидентной части.

; Начало процедуры инициализации.

initialize proc near

jmp       short initialize_entry_point ;  Пропустить различные

; варианты выхода без установки резидента, помещенные здесь ;  потому, что на них передают управление команды условного

; перехода, имеющие короткий радиус действия.

exit_with_message:

mov

int 21h ret • Функция вывода строки на экран. ; Выход из программы.

■тшш

already_loaded:

 

Если программа уже загружена в памятв.

cmp

byte ptr unloading,1

Если мы не были вызваны с /и.

je

download

 

mov

dx,offset already_msg

 

jmp

short exit_with_message

 

no_more_mux:

;   Если свободный идентификатор INT 2Dh не найден.

mov

dx,offset no_more_mux_msg

 

jmp

short

 

cant_unload1:

 

Если нельзя выгрузить программу.

mov

dx,offset cant_unload1_msg

 

jmp

short

 

do_unload:

;  Выгрузка резидента: при

передаче управления сюда АН содержит

 

; идентификатор программы

- 1.

inc

ah

 

- mov

al,02h

выгрузки резидента.

mov

dx, cs

Адрес возврата

mov

bx,offset exit_point

в DX:BX.

int

2Dh

Вызов нашего резидента через мультиплексор

push

cs

Если управление пришло сюда -

 

 

выгрузки не было.

pop

ds

 

mov

dx,offset cant unload2 msg

 

jmp

short exit_with_message

 

exit point:

;  Если управление пришло

сюда - выгрузка произошла.

push

cs

 

pop

ds

 

mov

dx,offset unloaded_msg

 

push

0

Чтобы сработала команда RET для выхода.

jmp

short exit_with_message

 

initialize_entry_point:

Сюда передается управление в самом начале

old

 

 

cmp

byte ptr cmd„line[1],7'

 

jne

not_unload.

 

cmp

byte ptr cmd line[2],'iT

Если нас вызвали с /и,

jne

not unload

 

mov

byte ptr unloading,1

выгрузить резидент.

not_unload:

 

 

mov

ah,9

 

mov

dx,offset usage

Вывод строки с информацией о программе.

int

21h

 

mov

ah, -1

Сканирование от FFh до 01h.

more mux:

 

 

mov

al.OOh

Функция AMIS OOh - проверка наличия

резидента.

not free

next_mux:

int

2Dh

: Мультиплексорное прерывание.

cmp

al.OOh

; Если   идентификатор свободен,

jne

not_free

 

mov

byte ptr mux.id.ah

;  вписать его сразу в код обработчика.

jmp

short nextjnux

 

mov

es, dx

; Иначе -           = адрес АМТЭ-сигнатуры

 

 

;  вызвавшей программы.

mov

si,offset amis„sign

; 08:51 = адрес нашей сигнатуры.

mov .

16

; Сравнить первые 16 байт.

repe

cmpsb

 

jcxz

,

already_loaded

Если они не

:

dec

ah

; перейти к следующему идентификатору.

jnz

morejnux

:  Если это 0

_found:

 

 

cmp

byte

; и если нас вызвали для выгрузки,

je

cant_unload1

а мы пришли сюда - программы нет

 

 

в памяти.

cmp

byte

Если при этом            все еще 0,

je

no_more_mux

идентификаторы кончились.

рка наличия устройства ЕММХХХХО.

 

mov

dx, offsetems.driver

 

mov

ax,3D00h

 

int

21h

;  Открыть файл/устройство.

jc .

no_emmx

 

mov

bx, ax

 

mov

ax,4400h

 

int

;         получить состояние файла/устройства.

jc

no_ems

 

test

; Если

старший бит DX = 0, ЕММХХХХО - файл.

jz

no_ems

 

ить память под буфер в EMS.

 

mov

ax,4100h

Функция EMS 41h:

int

67h

; получить адрес окна EMS.

mov

bp,bx

Сохранить его пока в ВР.

mov

ax,4300h

; Функция EMS 43h:

mov

bx,4

Нам надо 4 х 16 Кб.

int

67h

; Выделить EMS-память -(идентификатор в DX)

cmp

ah,0

Если произошла ошибка (нехватка памяти?)

jnz •

ems_failed

не будем пользоваться EMS.

mov

word ptr ems_handle,dx

Иначе: сохранить идентификатор

 

 

для резидента.

mov

ax,4400h

; Функция 44h - отобразитв

 

 

; EMS-страницы в окно.

mov

bx,0

 

int

67h

Страница 0.

mov

ax,4401h

 

Резидентные программы

inc

bx

 

int

 

Страница 1.

mov

ax,4402h

 

inc

bx

 

int

67h

Страница 2.

mov

ax,4403h

 

inc

bx

 

int

67h ;

Страница 3.

mov

ah,9

 

mov

dx,offset ems_msg;

Вывести сообщение об установке в EMS.

int

21h

 

mov

ax, bp

 

jmp

short ems„used

 

ems_failed: no_ems:

mov ah,3Eh

int 21h

no_emmx:

; Занять общую память.

Если EMS нет или он не работает, Закрыть файл/устройство ЕММХХХХО.

mov mov

int

mov mov

next_segment

next_segment

ah,9

dx,offset convjnsg

21h

Вывод сообщения об этом.

sp,length_of_program+100h+200h ; Перенести стек.

Функция DOS 4Ah.

length_of_program+100h+200h+0Fh

int

next_segment/16

bx,next_segment

21h

ah,48h

Такая запись нужна только для 1а)А8М,  остальным ассемблерам это можно было записать в одну строчку. Уменьшить занятую память, оставив текущую длину нашей программы +10011 на PSP +20011 на стек.

mov

bfsize_p = bfsize+OFh

bfsize_p = bfsize_p/16

mov bx,bfsize_p int 21h

Функция 48h

выделить память.

Размер BMP-файла 320x200x256 параграфах.

16-байтных

ems used:

mov word ptr buffer_seg,ax

Сохранить адрес буфера для резидента.

Скопировать заголовок BMP-файла в начало буфера, mov       сх,BMP_header_length mov       si,offset BMP_header mov di,0

mov

rep movsb

mov

в

Получить адреса флага занятости DOS и флага критической ошибки (считая,   что версия DOS старше 3.0).

mov ah,34h ; Функция 34h - получитв флаг занятости.

int 21h

dec bx ;  Уменвшитв адрес на 1 ,  чтобы он указывал

; на флаг критической ошибки,

mov word ptr in_dos_addr,bx

fflov word ptr in_dos_addr+2,es ; и сохранитв его для резидента.

Перехват прерываний.

mov ax,352Dh ; АН = 35h, AL = номер прерывания,

int 21h ; Получитв адрес обработчика INT 2Dh

mov word ptr old_int2Dh,bx ; и поместитв его в old_int2Dh.

mov word ptr old_int2Dh+2,es

mov ax,3528h ; AH = 35h, AD = номер прерывания,

int 21h ; Получитв адрес обработчика INT 28b

mov word ptr old_int28h, bx ; и поместитв его в old_int28h.

mov '     word ptr old„int28h+2,es

mov ax,3508h ; AH = 35h, AD = номер прерывания,

int 21h ; Получитв адрес обработчика INT 08h

mov '    word ptr old_int08h, bx ; и поместитв его в old_int08h.

mov word ptr old_int08h+2,es

mov ax,3513h ' ; AH = 35h, AD = номер прерывания,

int 21 h ; Получитв адрес обработчика INT 13h

mov word ptr old_intl3h, bx ; и поместитв его в old_int13h.

mov word ptr old_int13h+2,es

mov ax,3509h ; AH = 35h, AD = номер прерывания,

int 21 h . ; Получитв адрес обработчика INT 09h

mov word ptr old_int09h, bx ; и поместитв его в old^int09h.

mov word ptr oldlint09h+2,es

mov ax,252Dh ; AH = 25h, AD = номер прерывания,

mov dx, offset int2Dh_handler ; DS:DX - адрес обработчика,

int 21h ; Установитв новый обработчик INT 20h.

mov ax,2528h ; AH = 25h, AD = номер прерывания,

mov dx, offset int28h_handler ; 0S:0X - адрес обработчика,

int 21h , ; Установитв новый обработчик INT 2:8h.

mov ax,2508h ; AH = 25h,  AD = номер прерывания,

mov dx, offset int08h_handler ; 0S:DX - адрес обработчика,

int 211a ; Установитв новый обработчик INT 08h.

mov ax,2513h ; AH = 25h, AD = номер прерывания,

mov dx, offset int13h_handler ; DS:OX - адрес обработчика,

int 21 h ; Установитв новый обработчик INT 13h.

•   шоу ax,2509h ; AH = 25h, AD = номер прерывания. ■

mov dx,offset int09h_handler ; DS:DX - адрес обработчика,

int 21b ; Установитв новый обработчик INT 09ri.

Освободить память из-под окружения DOS.

mov ab,49h ■ ; функция  DOS 49h.

mov es.word ptr envseg : ES = сегментный адрес окружения DOS.

int 21 h ; Освободитв памятв.

; Оставить программу резидентной.

mov

initialize

РХ - адрес первого байта за концом

 

 

 

резидентной части.

int

27h

 

Завершитв выполнение, оставшисв

 

 

 

резидентом.

initialize

endp

 

 

ems.driver

db

'EMMXXXXO',0

Имя ЕМБ-драйвера для проверки.

; Текст, который

выдает программа при запуске:

usage

db ■

'Простая программа для копирования экрана толвко из'

 

db

' видеорежима 13Ь' ,ОБЬ1;ОАЬл

 

db

' АН-й       - записатв копию экрана в scrgrb.bmp'

 

db

ODh.OAh

 

 

db

|   scrgrb.com /и -

выгрузиться из

 

db

'$'

 

; Тексты, которые выдает программа при успешном выполнении:

ems_msg

db

в

ODh.OAh,' $'

convjnsg

db

'Не загружена в ЕМЭ',ООп,ОАп, '$'

unloadedjnsg

db

успешно выгружена из

; Тексты, которые ввщает программа при ошибках:

alreadyjrisg

db

'Ошибка: Программа уже загружена" ,ODh,OAh, '$'

no_more„mux_msg

db

'Ошибка: Слишком

много резидентных

 

db

ООИ.ОАґі, '$'

 

cant_unload1_msg

db

'Ошибка: Программа не обнаружена в памяти' .ООп.ОАп, '$'

cant_unload2jnsg

db

'Ошибка: Другая программа перехватила прерывания'

 

db

ODh,OAh,'$'

 

unloading

db

О

1, если нас запустили с ключом /и.

;   BMP-файл (для

изображения 320x200x256)

 

BMP_header

label

byte

 

; Файловый заголовок.

 

 

BMP_file_header

db

"ВМ"

Сигнатура.

 

dd

bfsize

Размер файла.

 

dw

0,0

О

 

dd

bfoffbits

Адрес начала. ВМР_Оата.

Информационный

заголовок

 

BMP info header

dd

bi.size

Размер

 

dd

'320

Ширина.

 

dd

200

Высота.

 

dw

і

Число   цветовых плоскостей.

 

dw

з

Число битов на пиксел.

 

dd

0

Метод сжатия данных.

 

dd

320*200

Размер данных.

 

dd

OB13h

Разрешение по X (пиксел на метр).

 

dd

OB13h

Разрешение по У (пиксел на метр.).

 

dd

0

Число используемых цветов  (0 - все).

 

dd

0

Число важных цветов (0 - все).

bi_size = $-BMP_

info_header

Размер ВМР_іп1:о_пеаа'ег.

BMP header length = $-

BMP_header

Размер обоих заголовков.

bfoffbits = $-BMP_file

_header+256*4

Размер заголовков + размер палитры.

bfsize = $-BMP_file_header+256*4+320*200    ;  Размер заголовков +

;  размер палитры + размер данных.

length_of_progran = $-start end start

В этом примере, достаточно сложном из-за необходимости избегать всех слу­чаев повторного вызова прерываний DOS и BIOS, добавилась еще одна мера пре­досторожности - сохранение состояния EMS-памяти перед работой с ней и вос­становление в исходное состояние. Действительно, если наш резидент активизируется в тот момент, когда какая-то программа работает с EMS, и не выполнит это требование, программа будет читать/писать уже не в свой EMS-страницы, а в наши. Аналогичные предосторожности следует предпринимать вся­кий раз, когда вызываются, функции, затрагивающие какие-нибудь глобальные структуры данных. Например: функции поиска файлов используют буфер DTA, адрес которого надо сохранить (функция DOS 2Fh), затем создать собственный (функция DOS и в конце восстановить DTA прерванного процесса по со­храненному адресу (функция lAh). Таким образом надо сохранять/восстанавли­вать состояние адресной линии А20 (функции XMS 07h и 03h), если резидентная программа хранит часть своих данных или кода в области НМА, сохранять состоя­ние драйвера мыши (INT33h, функции 16пи 17h), сохранять информацию о пос­ледней ошибке DOS (функции DOS 59h и 5DOAh), и так с каждым ресурсом, который затрагивает резидентная программа.

Писать полноценные резидентные программы в DOS очень сложно, но, если не выходить за рамки реального режима, это самое эффективное средство управ­ления системой и реализации всего, что только можно сделать в DOS.