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

Новосибирский государственный технический университет
                         Кафедра вычислительной техники                         
     

Курсовой проект по дисциплине

лПрограммирование Вариант: 11 Группа: АМ-311 Выполнил: Шевченко Н.С. Проверил: Афанасьев В.А.

Новосибирск 2005

1. Задание Разработать резидент-замедлитель исполнения программ. Замедление реализуется процедурой задержки, вставленной в обработчик прерывания пользователя по таймеру (int 1Ch). Инициализация резидента осуществляется через посредство комбинации клавиш <Scroll Lock+Цифровая клавиша>. Значение символа цифровой клавиши влияет на скорость замедления. 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). ;Инициализация резидента осуществляется через ;посредство комбинации клавиш <Scroll Lock+Цифровая клавиша>. ;Значение символа цифровой клавиши влияет на скорость замедления ;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). ;Инициализация резидента осуществляется через ;посредство комбинации клавиш <Scroll Lock+Цифровая клавиша>. ;Значение символа цифровой клавиши влияет на скорость замедления ;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," ║ Нажмите комбинацию клавиш вида <Scroll Lock + цифра> ║" 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 ; конец программы/точка выхода