Ассемблер (assembler). Разработка резидентых обработчиков

1. Задание
Разработать резидент-замедлитель исполнения программ. Замедление реализуется процедурой задержки, вставленной в обработчик прерывания пользователя по таймеру (int 1Ch). Инициализация резидента осуществляется через посредство комбинации клавиш . Значение символа цифровой клавиши влияет на скорость замедления.
2. Структура и описание разработки
Вся разработка состоит из двух файлов: *.com – непосредственно резидента и *.exe – демонстрационной программы. Рассмотрим сначала структуру и принцип действия резидента. В состав *.com файла входят следующие обработчики: пользовательский обработчик new_1Ch, заменяющий прерывание 1Сh, пользовательский обработчик клавиатуры new_09h, заменяющий системный обработчик 09h, а также обработчик new_2Fh, заменяющее соответствующее прерывание. Также в файле находятся дополнительные поля и методы, необходимые для корректного функционирования резидента.
Рассмотрим работу пользовательского прикладного обработчика прерываний new_1Ch, заменяющего на время работы резидента прикладной аппаратный обработчик прерываний DOS Int 1Ch, входящего в состав системного обработчика BIOS 08h Прерывание DOS Int 1Ch служит для перехвата тактов системного таймера (18,206 Гц), не нарушая его работу, и изначально содержит лишь одну команду Iret. Новый обработчик содержит алгоритм замедления исполнения программ, основывающийся на том факте, что прерывание 1Ch вызывается примерно 18 раз в секунду. Получается, каждый вызов происходит примерно через каждые 55 мс. Таким образом можно для замедления исполнения программ пропускать каждые n-е вызовы, а остальные задерживать специальным алгоритмом. Получаем замедлитель в n раз исполнения программ, где n- это коэффициент замедления, который регулируется переменной cnt, которая находится в кодовом сегменте. Эта переменная может принимать значения от 0 до 8. Следует добавить, что обработчик new_1Ch несёт ещё одну функцию – демонстрационную. Он выводит на экран не замедленное системное время. Это означает, что в каком ни было режиме замедления обработчик не находился бы, на экране будет отображаться системное время. Рассмотрим теперь работу алгоритма задержки. При очередном вызове обработчика new_1Ch происходит проверка режима работы замедлителя. Режим работы характеризуется состоянием переменной fn, находящейся в кодовом сегменте. Эта переменная может принимать значения от 0 до 8. Значение переменной “0” соответствует “скрытому” режиму работы, при котором на экран не выводится системное время, а так же не происходит никакого замедления. Значения переменной от 1 до 8 характеризуют соответствующий коэффициент задержки при выводимом на экран системном времени. Итак, при установлении режима работы резидента ( по умолчанию идёт 0 ), происходит выставление в переменную cnt, хранящуюся там же коэффициента замедления, а так же по режиму работы определяется выводить или нет на экран системного времени. Коэффициент замедления – это количество вызовов процедуры задержки исполнения программы, время исполнения которой примерно равно 55 мс. Далее происходит анализ переменной f1 на тот факт, что производит на этот раз: проход на Iret, или же производить задержку исполнения. Если fn==0, то идём на выход и выставляем fn=1, если же fn==1, то запускаем алгоритм задержки с возможностью вывода системного или без оного. По завершению работы алгоритма fn выставляется в 0, т.е. при следующем вызове обработчика задержки происходить не будет.
Стоит заметить, что переменная fn, характеризующая режим работы резидента находится хоть и сегменте кода, но не в пределах функции new_1Ch, а вне её. Это необходимо для общедоступности этой переменной, т.к. её использует пользовательский обработчик new_09h для выставления режима работы, а так же обработчик new_2Fh для считывания его.
Теперь рассмотри работу пользовательского обработчика прерываний от клавиатуры new_09h. Проанализируем введённый символ с клавиатуры, если он принадлежит заданному диапазону значений, то продолжим, иначе передадим управление системному обработчику, адрес которого изначально был сохранён. Если всё нормально, то проверяем, включён ли режим Scroll Lock, если нет , то на выход, иначе выставляем в переменной соответствующий номер режима, восстанавливаем все регистры и выходим из обработчика.
Мультиплексное прерывание new_2Fh работает по следующему принципу: сначала проверяется содержимое регистра ax на запрос режима работы резидента, если такой запрос имеет место, то возвращается содержимое переменной fn в том же регистре и происходит выход из процедуры. Если такого запроса не поступило, то происходи анализ того же регистра на наличие нашей функции в памяти. Стоит отметить, что функцией присвоен идентификатор 0C88h. При наличие резидента в памяти произойдёт возвращение в младшем байте регистра ax значения 0ffh. Если и этого запроса не поступило, то проверяется наличие запроса на деинсталляцию. При положительном результате происходит восстановление всех ранее перехваченных векторов и выгрузка из памяти резидента вместе с PSP. При отсутствии всех выше перечисленных запросов произойдёт переход в следующий по цепочке обработчик.
В секции инициализации Init Выполняется проверка на наличие в памяти первого экземпляра резидента. Если первый экземпляр не обнаружен, то независимо от вида запускающей программу команды ;(с опцией или без неё) происходит переход на метку ОК с установкой программы в памяти. При обнаружении первого экземпляра программы начинается сравнение опции команды с ожидаемой. Если результат сравнения оказался отрицательным (опция есть, но другая), программа завершается выводом сообщения о невозможности повторной установки. При идентичности опции ожидаемой резидент выгружается из памяти с выводом соответствующего сообщения.
Демонстрация работы резидента основывается на показательном сравнении “заторможенного” и “не заторможенного” системного времени. “Заторможенное” системное время получается в результате работы резидента. При этом происходит задержка вывода времени на экран. “Не заторможенное” время выводится непосредственно прикладным обработчиком 1Ch. Дело в том, что какой бы то ни было коэффициент замедления не был установлен, время в любом случае будет выводится на экран, так как функция вывода системного времени установлена в алгоритме замедления, суть которого заключается в многократном вызове циклов (порядка не скольких сотен тысяч раз в секунду). Таким образом можно сравнить реально идущее время с заторможенным, а так же можно не вооруженным глазом определить коэффициент замедления.
Весь интерфейс демонстрационной программы оформлен в псевдографике, имеет несколько информационных окон – статус резидента, «заторможенное время», «незаторможенное время», коэффициент замедления.
3. Результаты демонстрации программы.
При запуске .com программы из командной строки было выведено соответствующее сообщение об удачной загрузке резидента в память. При повторном запуске .com приложения было выведено сообщение, что резидент уже загружен. Для дальнейшего продолжения демонстрации работы запустили демонстрационную программу со специальным экранным интерфейсом, в котором отображалось состояние резидента. В окне Статус высвечивается статус резидента, в После предварительного включения режима Scroll Lock последовательно жмём на цифровые клавиши, при этом начинает изменяться информация о состоянии резидента. Заметим, что по умолчанию резидент находился в “скрытом” состоянии, что характеризовалось отсутствием выводимого системного времени на экране, а так же коэффициентом замедления 1. Стоит отметить, что изменение коэффициента замедление не происходит мгновенно, а только в зависимости от предыдущего значения коэффициента замедления
Выгрузка резидента происходит следующим образом: в командной строке прописывается имя файла «пробел» off, после этого будет выведено сообщение об выгрузке резидента из памяти.
4. Листинг программ
Файл RES.asm
;Студент: Шевченко Н.С. преподаватель: Афанасьев В.А.
;Вариант 11
;Задание: Реализовать Резидент-замедлитель исполнения программ.
;Замедление реализуется поцедурой задержки, вставленной
; в обработчик прерывания пользователя по таймеру (int 1Ch).
;Инициализация резидента осуществляется через
;посредство комбинации клавиш .
;Значение символа цифровой клавиши влияет на скорость замедления
;Res.com – основнной модуль, содержащий резидент с пользовательскими
;обработчиками new_1Ch, new_09h и new_2Fh заменяющими соостветствующие
;обработчики 1Ch, 09h и 2Fh
;test.exe – демонстрационная программа, показывающая на примере
;вывода системного времени работу резидента
IDEAL
MODEL TINY
p486n
CODESEG
org 100h ;Установим IP на адрес после PSP
MACRO cprintf mes,len,attrib,x,y,n
mov ah,13h
mov al,1 ;Признак смещения курсора в конец строки
mov bh,n ;Номер видеостраницы
mov bl,attrib
mov cx,len
mov dh,y
mov dl,x
mov bp,offset mes ;Адрес строки ES:BP
int 10h
ENDM cprintf
;———————————————————————-
Proc resident
jmp init ; Переход на секцию инициализации
old_1Ch dd 0 ; Адреса заменяемых обработчиков
old_2Fh dd 0 ;
old_09h dd 0 ;
ten db 10 ; Основание сисемы исчисления
fn db 0 ; Режим работы резидента
Proc new_1Ch ; Пользовательский обработчик new_1Ch
pusha
push ds es
mov ax,cs
mov ds,ax ;ds=cs
cmp [cs:cnt],0 ; время замедления равно 0
je end_out ; то идём на выход
mov cx,[cs:cnt] ;Число внешних циклов
outer: push cx ;собственно задержка
mov cx,0 ;
;
inner: loop inner ;0FFFFh – число внутренних циклов
pop cx ;
loop outer ;
mov ah,02h ; иначе выведем на экран
int 1Ah ; в определённые координаты (для демонстрации)
; строку системного время с атрибутом
; показать замедленное время
mov al,ch
call bin_asc ; вызовем функцию перевода в ASCII символы
mov [cs:clock],ah ; сохраним значения
mov [cs:clock+2],al ;
mov al,cl
call bin_asc
mov [cs:clock+6],ah
mov [cs:clock+8],al
mov al,dh
call bin_asc
mov [cs:clock+12],ah
mov [cs:clock+14],al
mov cx,clock_len
mov ax,0B800h
mov es,ax
mov di,[cs:locate]
mov si,offset clock ; выведем строку на экран
cld
rep movsb
end_out:
pop es ds ; восстановим все регистры
popa
jmp [dword cs:old_1Ch]
iret
; выйдем из процедуры
Endp new_1Ch
;—————————————————
Proc bin_asc ; Процедура преобразования BCD-кода
;mov ah, al
;sub ah, al
;shr ah, 1
;sub al, 10
;add ah, ‘0’
;add al, ‘0’
mov ah,al ; из регистра al в ASCII-символы
and al,0Fh
shr ah,4
or ax,3030h
ret
Endp bin_asc
;————————————
clock DB 2 DUP(20h,1Fh),”:”,1Fh ; строка системного времени с атрибутом
DB 2 DUP(20h,1Fh),”:”,1Fh,2 DUP(20h,1Fh)
clock_len = $-clock ; длинна строки
locate DW 1352 ; 1352 позиция точки ввода на экране
cnt DW 0 ; коэффициент замедления
f1 DB 0 ; 0- не производить замедление 1- наоборот
;————————————
Proc new_09h ; пользовательский обработчик new_09h
pusha ; Сохраним регистры
push es
push ds
in al,60h ; Введём скан-код
cmp al,0Bh
je h0
cmp al,02h
je h1
cmp al,03h
je h2
cmp al,04h
je h3
cmp al,05h
je h4
cmp al,06h
je h5
cmp al,07h
je h6
cmp al,08h
je h7
cmp al,09h
je h8
xxx:
pop ds ; Если ни одна из комбинаций клавиш не
pop es ; присутствует, то восстановим регистры
popa ; и в системный обработчик без вовзврата
jmp [dword cs:old_09h]
h0: mov ax,40h ;Настроим ES на сегмент данных BIOS
mov es,ax
mov al,[es:17h] ;Получим первый байт флагов
and al,00010000b ;выделяем 4-ый бит
cmp al,0 ;Клавиша Scroll Loсk нажата?
je xxx
;test al,10h ;Клавиша Scroll Loсk нажата?
;je xxx ;если нет, то выйдем
mov [cs:fn],0h ; если гажата, то выставим соответствующий режим
mov [cs:cnt], 0
jmp xxxx ; работы резидента, и пойдём на выход
h1: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],1h
mov [cs:cnt], 0
jmp xxxx
h2: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],2h
mov [cs:cnt],160
jmp xxxx
h3: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],3h
mov [cs:cnt], 240
jmp xxxx
h4: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],4h
mov [cs:cnt],320
jmp xxxx
h5: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],5h
mov [cs:cnt],400
jmp xxxx
h6: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],6h
mov [cs:cnt], 480
jmp xxxx
h7: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],7h
mov [cs:cnt],560
jmp xxxx
h8: mov ax,40h
mov es,ax
mov al,[es:17h]
test al,10h
je xxx
mov [cs:fn],8h
mov [cs:cnt],640
jmp xxxx
xxxx: in al,61h ;Введём содержимое порта 61h
or al,80h ;Подтвердим приём кода, записав 1 в старший
out 61h,al ;бит порта 61h
and al,7Fh ;Снова разрешим работу контроллера клавиатуры
out 61h,al ;сбросив старший бит порта 61h
mov al,20h ;20h – команда EOI
out 20h,al ;20h – порт контроллера
pop ds ;Восстановим регистры
pop es
popa
iret ; выйдем из прерывания
Endp new_09h
Proc new_2Fh ; мультиплексное прерывание
cmp ah,0C7h ; Если поступил запрос на режим работы резидента
jne dalshe ; то вернём его в младшем байте регистра ax
mov al,[cs:fn] ; иначе продолжим .
iret ;
dalshe:
cmp ah,0C8h ; Наша функция?
jne out_2Fh ; Не наша, – на выход
cmp al,00h ; Подфункция проверки на повторную установку
je i_here ; Да, сообщим о невозможности повторной установки
cmp al,01h ; Подфункция выгрузки?
je uninst ; Да, на выгрузку
jmp short out_2Fh ; Неизвестная подфункция, на выход
i_here: mov al,0ffh
iret
out_2Fh:jmp [dword cs:old_2Fh] ; Переход в следующий по цепочке обработчик
uninst: push ds
push es
push dx
mov ax,251Ch ; Восстановим вектор 1Ch
lds dx,[cs:old_1Ch]
int 21h
mov ax,252Fh ; Восстановим вектор 2Fh
lds dx,[cs:old_2Fh]
int 21h
mov ax,2509h ; Восстановим вектор 09h
lds dx,[cs:old_09h]
int 21h
; Получим из PSP адрес собственного окружения и выгрузим его
mov es,[cs:2Ch] ; es mov ah,49h ; Функция освобождения блока памяти
int 21h
; Выгрузим теперь программу из памяти вместе с PSP
push cs
pop es ; es снова указывает на начало PSP
mov ah,49h ; Функция освобождения блока памяти
int 21h
pop dx
pop es
pop ds
iret
Endp new_2Fh
Endp resident
;Секция инициализации
;- Выполняется проверка на наличие в памяти первого экземпляра резидента.
;- Если первый экземпляр не обнаружен, то независимо от вида запускающей программу команды
;(с опцией или без неё) происходит переход на метку ОК с установкой программы в памяти
;- При обнаружении первого экземпляра программы начинается сравнение опции команды с
;ожидаемой
;- Если результат сравнения оказался отрицательным (опция есть, но другая), программа завершается
;выводом сообщения о невозможности повторной установки
;- При идентичности опции ожидаемой резидент выгружается из памяти с выводом
соответствующего сообщения
Proc init
mov ax,0C800h ;Запрос на наличие в памяти первого
int 2Fh ;экземпляра программы
cmp al,0ffh ;Вернулся код 0ffh?
jne ok ;Нет, данная программа в памяти отсутствует. Выполним её
;установку, перейдя на метку оk
;Первый экземпляр обнаружен. Была ли у команды опция ‘off’?
mov cl,[es:80h] ;Получим длину хвоста из PSP
cmp cl,0 ;Длина хвоста = 0?
je fin ;Да, программа запущена без него
xor ch,ch ;Пусть сх=cl=длина хвоста
mov di,81h ;Адрес хвоста es:di в PSP
mov al,’ ‘ ;Уберём пробелы из начала хвоста
cld ;просмотр вперёд
repe scasb ;Сканируем хвост (al – dst), пока пробелы
dec di ;di- первый символ после пробела
mov cx,3 ;Ожидаемая длина опции
mov si,offset option ;Адрес ожидаемой опции (option) ds:si
repe cmpsb ;Сравниваем введённую опцию с ожидаемой
jne fin ;Опции не совпали. На выход
mov ax,0C801h ;Опции совпали, пошлём в резидентную
int 2Fh ;программу команду (al=01) на выгрузку. По окончанию
mov dx,offset msg3 ;выгрузки выведем сообщение об этом
jmp fin1
fin: mov dx,offset msg2 ;Попытка вторичной установки
fin1: mov ah,09h
int 21h
mov ax,04C00h ;Функция DOS 4Сh: выход из программы
int 21h ;Вызов DOS. Останов
;Первой экземпляр (резидент) отсутствует. Установим программу
ok: mov ax,351Ch ;Чтение и сохранение вектора 1Ch
int 21h
mov [word cs:old_1Ch],bx
mov [word cs:old_1Ch+2],es
mov ax,3509h ;Чтение и сохранение вектора 09h
int 21h
mov [word cs:old_09h],bx
mov [word cs:old_09h+2],es
mov ax,352Fh ;Чтение и сохранение вектора 2Fh
int 21h
mov [word cs:old_2Fh],bx
mov [word cs:old_2Fh+2],es
mov ax,251Ch ;Установка обработчика 1Ch
mov dx,offset new_1Ch
int 21h
mov ax,2509h ;Установка обработчика 09h
mov dx,offset new_09h
int 21h
mov ax,252Fh ;Установка обработчика 2Fh
mov dx,offset new_2Fh
int 21h
mov ah,09h
mov dx,offset msg1 ;Сообщение об успешной установке программы
int 21h
mov ax,3100h
mov dx,(init-resident+10Fh)/16
int 21h
Endp init
;———————————————-
msg1 db ‘Резидентный обработчик установлен$’
msg2 db ‘Резидент уже установлен. Выгрузка через опцию off$’
msg3 db ‘Резидент выгружен из памяти$’
option db ‘off’
END resident
Файл TEST.asm
;Студент: Шевченко Н.С. преподаватель: Афанасьев В.А.
;Вариант 11
;Задание: Реализовать Резидент-замедлитель исполнения программ.
;Замедление реализуется поцедурой задержки, вставленной
; в обработчик прерывания пользователя по таймеру (int 1Ch).
;Инициализация резидента осуществляется через
;посредство комбинации клавиш .
;Значение символа цифровой клавиши влияет на скорость замедления
;R.com – основнной модуль, содержащий резидент с пользовательскими
;обработчиками new_1Ch, new_09h и new_2Fh заменяющими соостветствующие
;обработчики 1Ch, 09h и 2Fh
;DEMO.exe – демонстрационная программа, показывающая на примере
;вывода системного времени работу резидента
Ideal
Model small
P486N
Stack 256
MACRO window N,attrib,y1,x1,y2,x2
mov ah,06h
mov al,N
mov bh,attrib
mov ch,y1
mov cl,x1
mov dh,y2
mov dl,x2
int 10h
ENDM window
MACRO locate y,x
mov ah,02h
mov bh,0
mov dh,y
mov dl,x
int 10h
ENDM locate
MACRO cprintf mes,len,attrib,x,y,n
mov ah,13h
mov al,1 ;Признак смещения курсора в конец строки
mov bh,n ;Номер видеостраницы
mov bl,attrib
mov cx,len
mov dh,y
mov dl,x
mov bp,offset mes ;Адрес строки ES:BP
int 10h
ENDM cprintf
MACRO out_str mes
mov ah,09h
lea dx,[mes]
int 21h
ENDM out_str
DATASEG
intro db 13,10,” ╔═════════════════════════════════════╗”
db 13,10,” ║ Курсовая работа по Ассемблеру ║”
db 13,10,” ║ Студент: Шевченко Н.С. Преподаватель: Афанасьев В.А. ║”
db 13,10,” ╚═════════════════════════════════════╝”,13,10,’$’
mes_t11
db 13,10,” ╔═════════════════════════════════╗”
db 13,10,” ║ Текущее время: ║”
db 13,10,” ║ Реальное время: ║”
db 13,10,” ╚═════════════════════════════════╝$”
status db 13,10,” ╔════════════════════════════════╗”
db 13,10,” ║ Статус: ║”
db 13,10,” ╚════════════════════════════════╝$”
mode db 13,10,” ╔════════════════════════════════╗”
db 13,10,” ║ Коэффициент замедления: ║”
db 13,10,” ╚════════════════════════════════╝$”
info1 db 13,10,” ╔═════════════════════════════════════════════╗”
db 13,10,” ║ Нажмите комбинацию клавиш вида ║”
db 13,10,” ║ где цифра = 1 . 8 (коэффициент замедления) ║”
db 13,10,” ║ цифра ‘1’ означает, что замедление отсутствует ║”
db 13,10,” ║ Примечание : по умолчанию замедление отсутствует! ║”
db 13,10,” ╚═════════════════════════════════════════════╝$”
str1 db “1/1$”
str2 db “1/2$”
str3 db “1/3$”
str4 db “1/4$”
str5 db “1/5$”
str6 db “1/6$”
str7 db “1/7$”
str8 db “1/8$”
len7 = $-str8
error db 13,10,13,10, “Неверная комбинация клавиш. Попробуйте еще раз. $”
temp db ?
temp1 db ?
clocks db “00:00:00”
sclock_len = $-clocks
yes db “Резидент обнаружен”
yes_len = $-yes
no db “Резидент не обнаружен”
no_len = $-no
;————————————————————–
Codeseg
Start:
mov ax,@data
mov ds,ax
mov es,ax
;вывод пользовательского меню
window 0,8Fh,0,0,25,80
locate 0,0
out_str intro
out_str mes_t11
out_str status
out_str mode
out_str info1
mov ax,0C800h
int 2Fh
cmp al,0ffh
jne ok
cprintf yes,yes_len,2Fh,12,11,0
mov [temp1],1
jmp work
; резидента в памяти нет – выход
ok:
cprintf no,no_len,4Fh,12,11,0
jmp quit
; вывод на экран незамедленного (системного) времени
work:
mov ah, 2Ch
int 21h
jc end_out
mov al,ch
call bcd_asc
mov [clocks],ah
mov [clocks+1],al
mov al,cl
call bcd_asc
mov [clocks+3],ah
mov [clocks+4],al
mov al,dh
call bcd_asc
mov [clocks+6],ah
mov [clocks+7],al
cprintf clocks,sclock_len,1Fh,36,7,0
end_out:
;——————————————————————–
push ax
mov ax,0C700h
int 2Fh
mov [temp],al
locate 14,30
cmp [temp],0h
jne del1
cmp [temp1],1h
jne del1
out_str str1,len7,1Eh,63,15,0
del1: cmp [temp],01h
jne del2
out_str str1,len7,1Eh,63,15,0
del2: cmp [temp],02h
jne del3
out_str str2,len7,1Eh,63,15,0
del3: cmp [temp],03h
jne del4
out_str str3,len7,1Eh,63,15,0
del4: cmp [temp],04h
jne del5
out_str str4,len7,1Eh,63,15,0
del5: cmp [temp],05h
jne del6
out_str str5,len7,1Eh,63,15,0
del6: cmp [temp],06h
jne del7
out_str str6,len7,1Eh,63,15,0
del7: cmp [temp],07h
jne del8
out_str str7,len7,1Eh,63,15,0
del8: cmp [temp],08h
jne del9
out_str str8,len7,1Eh,63,15,0
del9:
pop ax
;—————————————————————
; проверка на нажатие клавиши
quit: mov ah, 01h
int 16h
je work
exit:
mov ax,4C00h
int 21h
;—————————————————————
; процедура преобразования числа в ASCII-форму
Proc bcd_asc
xor bx,bx
mov bl, al
mov bh, 00h
mov ax,bx
mov cl, 10 ; 10 == 1010b
div cl
add ah, ‘0’
add al, ‘0’
xchg ah,al
ret
Endp bcd_asc
;—————————————————————
End start ; конец программы/точка выхода