Процедура new_2fh перехватит прерывание 2Fh, и если прерывание вызвано вместе с функцией F1h, то в зависимости от подфункции значение которой находится в AL выполнит следующие действия:
1. Если подфункция находящаяся в AL=00h (код наличия в памяти нашего обработчика), то наш обработчик возвратит в AL=FFh и выйдет из прерывания.
cmp al,00h
je inst
…
inst: mov al,0ffh
iret
2. Если подфункция находящаяся в AL=01h (команда на удаление из памяти обработчика), то сохраним используемые регистры, вызовем процедуру fil (работа этой процедуры была описана выше), а затем освободим блоки памяти занятые нашим обработчиком, восстановим старые векторы 09h и 2Fh. Восстановим использовавшиеся регистры и выйдем из прерывания.
Если мультиплексорное прерывание было вызвано с другой функцией либо с нашей функцией но с другими подфункциями, то обработчик передаст управление следующему по цепочке обработчику мультиплексорного прерывания.
cmp ah,0f1h
jne out_2fh
cmp al,00h
je inst
cmp al,01h
je off
jmp short out_2fh
inst: mov al,0ffh
iret
out_2fh:
Листинг программы
text segment 'code'
assume cs:text,ds:text
org 256
main proc
jmp init
; Поля данных резидентной секции
old_2fh dd 0; Ячейка для сохранения системного вектора 2Fh
old_09h dd 0; Ячейка для сохранения системного вектора 09h
bufer db 34 dup(?); Буфер для скэн-кодов и флагов клавиатуры
sch db 0; Счётчик нажатий клавиш
filename db 's_code&f.txt',0; Константа содержащая имя файла с которым работает программа
; Обработчик от клавиатуры
new_09h proc
; Сохраним используемые регистры
push ax
push bx
push cx
push dx
push ds
push cs; Настроим DS на наш сегмент для простоты программирования
pop ds
in al,60h; Получим скэн-код клавиши
cmp al,80h; Проверим, является ли скэн-код кодом нажатия
ja exit; Нет – на выход
mov bh,0; 0®BH
mov bl,sch; Текущее значения счётчика в BL
|
mov [bufer+bx],al; Запишем в буфер скэн-код клавиши
inc bl; Увеличим смещение буфера
push es; Сохраним регистр ES
mov ax,40h; Настроим ES на начало области данных BIOS
mov es,ax
mov al,es:[17h]; Занесём байт флагов клавиатуры в AL
pop es; Восстановим ES
mov [bufer+bx],al; Запишем байт флагов в буфер
inc bl; Увеличим смещение на 1
add sch,2; Счётчик нажатий +2
cmp sch,32; Пора скидывать буфер в файл?
je go; Да – на процедуру записи в файл
jmp exit; Нет – на выход
go: call fil; Вызов процедуры записи в файл
; Восстановим использовавшиеся регистры
exit: pop ds
pop dx
pop cx
pop bx
pop ax
jmp cs:old_09h; Передадим управление системному обработчику “int09h”
new_09h endp; Конец процедуры обработчика от клавиатуры
; Процедура записи в файл скэн-кодов и флагов клавиатуры
fil proc
push cs; Настроим DS на наш сегмент
pop ds
mov ah,3dh; Функция открытия файла
mov al,1; для записи
mov dx,offset filename; DS:DX ASCIIZ имени файла
int 21h
mov bx,ax; Дескриптор в ВХ
xor cx,cx; Отчистим СХ
xor dx,dx; и DX
mov ax,4202h; Функция установки указателя в конец файла
int 21h
mov ah,40h; Функция записи в файл
mov cl,sch; CL количество байт
mov dx,offset bufer; DS:DX адрес буфера
int 21h
mov ah,3eh; Функция закрытия файла
int 21h
mov sch,0; Обнулим счётчик
ret; Выход из процедуры
fil endp; Конец процедуры записи в файл
; Обработчик мультиплексорного прерывания
new_2fh proc
cmp ah,0f1h; Проверим номер функции мультиплексорного прерывания
jne out_2fh; Не наша – на выход
cmp al,00h; Подфункция проверки на повторную установку?
je inst; Да, сообщим о невозможности повторной установки
cmp al,01h; Подфункция выгрузки?
je off; Да – на выгрузку
jmp short out_2fh; Неизвестная подфункция, на выход
inst: mov al,0ffh; Программа уже установлена
|
iret; Выход из прерывания
out_2fh:
jmp cs:old_2fh; Переход в следующий по цепочке обработчик прерывания 2Fh
; Выгрузим программу из памяти, предварительно восстановив все перехваченные ею векторы
; Сохраним используемые регистры
off: push ds
push es
push dx
push ax
push bx
push cx
call fil; Вызов процедуры записи в файл содержимого буфера
; Восстановим использовавшиеся регистры
pop cx
pop bx
pop ax
; Восстановим вектор 09h
mov ax,2509h; Функция установки вектора
lds dx,cs:old_09h; Заполним DS:DX
int 21h
; Восстановим вектор 2fh
mov ax,252fh; Функция установки вектора
lds dx,cs:old_2fh; Заполним DS:DX
int 21h
; Получим из PSP адрес собственного окружения и выгрузим его
mov es,cs:2ch; ES ® окружение
mov ah,49h; Функция освобождения блока памяти
int 21h
; Выгрузим теперь саму программу
push cs; Загрузим в ES содержимое CS, т.е. сегментный адрес PSP
pop es
mov ah,49h; Функция освобождения блока памяти
int 21h
; Восстановим использовавшиеся регистры
pop dx
pop es
pop ds
iret; Возврат в вызвавшую программу
new_2fh endp; Конец процедуры обработки прерывания 2Fh
end_res=$; Смещение конца резидентной части программы
main endp
tail db 'off'; Ожидаемый хвост команды
flag db 0; Флаг требования выгрузки
tabl db '0123456789'; Таблица для перевода BCD кода в ASCII
time db 25 dup(?); Ячейка для сохранения текущей даты и времени
; Процедура создания файла
div_f proc
mov ah,3ch; Функция создания файла
mov cx,0; Без атрибутов
lea dx,filename; DS:DX ASCIIZ имени файла
int 21h
mov bx,ax; Дескриптор в ВХ
mov ah,40h; Функция записи в файл
mov cx,buflen; CХ количество байт
lea dx,buf; DS:DX адрес строки
int 21h
mov ah,3eh; Функция закрытия файла
int 21h
ret; Выход из процедуры
div_f endp; Конец процедуры создания файла
; Процедура открытия файла и записи в него текущей даты и времени
|
div2_f proc
mov [time],0ah; Запись в переменную time маркеров
mov [time+1],0dh; перехода на следующую строку
mov ah,3dh; Функция открытия файла
mov al,1; для записи
mov dx,offset filename; DS:DX ASCIIZ имени файла
int 21h
mov bx,ax; Дескриптор в ВХ
push bx; Сохраним дескриптор
xor cx,cx; Отчистим СХ
xor dx,dx; и DX
mov ax,4202h; Функция установки указателя в конец файла
int 21h
mov ah,02h; Функция чтения времени из «постоянных» «CMOS» часов реального времени
int 1ah; Прерывание ввода – вывода для времени
mov bx,offset tabl; DS:DX адрес таблицы
mov si,2; Установим смещение для переменной time
mov ax,cx; Часы и минуты сохраним в AX
mov cx,12; Установим счётчик сдвига
next: push ax; Сохраним AX
shr ax,cl; Сдвинем AX на CL
and al,0fh; Получим номер ячейки в таблице прибавив маску
xlat; Получим ASCII код числа
mov [time+si],al; Занесём его в переменную time
inc si; Увеличим на 1 смещение
cmp si,4; Смещение = 4?
je ras; Да, переход на метку ras
vw: sub cl,4; Нет, уменьшим CL на 4
pop ax; Восстановим AX
cmp cl,-4; Сравним CL с -4
jne next; Не равно – выполним ещё раз
jmp ent1; Равно – переход на ent1
ras: mov [time+si],':'; Запишем в переменную time – «:»
inc si; Увеличим на 1 смещение
jmp vw; Перейдём на метку vw
ent1: mov [time+si],' '; Запишем в переменную time – «»
inc si; Увеличим на 1 смещение
mov ah,04h; Функция чтения даты из «постоянных» «CMOS» часов реального времени
int 1ah; Прерывание ввода – вывода для времени
mov ax,dx; Дату сохраним в AX
mov cx,12; Установим счётчик сдвига
next1: push ax; Сохраним AX
shr ax,cl; Сдвинем AX на CL
and al,0fh; Получим номер ячейки в таблице прибавив маску
xlat; Получим ASCII код числа
mov [time+si],al; Занесём его в переменную time
inc si; Увеличим на 1 смещение
cmp si,10; Смещение = 10?
je ras1; Да, переход на метку ras1
vw1: sub cl,4; Нет, уменьшим CL на 4
pop ax; Восстановим AX
cmp cl,-4; Сравним CL с -4
jne next1; Не равно – выполним ещё раз
jmp ent2; Равно – переход на ent2
ras1: mov [time+si],'.'; Запишем в переменную time – «.»
inc si; Увеличим на 1 смещение
jmp vw1; Перейдём на метку vw1
ent2: mov [time+si],0ah; Запись в переменную time маркеров
mov [time+si+1],0dh; перехода на следующую строку
pop bx; Восстановим дескриптор
mov ah,40h; Функция записи в файл
mov cx,20; CХ количество байт
mov dx,offset time; DS:DX адрес строки
int 21h
mov ah,3eh; Функция закрытия файла
int 21h
ret; Выход из процедуры
div2_f endp; Конец процедуры подготовки файла
; Процедура инициализации
init proc
mov cl,es:80h; Получим длину хвоста PSP
cmp cl,0; Длина хвоста = 0?
je live; Да программа запущена без параметров
xor ch,ch; Теперь CX=CL=длина хвоста
mov di,81h; DS:SI ® хвост в PSP
lea si,tail; DS:SI ® поле tail
mov al,' '; Уберём пробелы из начала хвоста
repe scasb; Сканируем хвост, пока пробелы
dec di; DI ® первый символ после пробелов
mov cx,3; Ожидаемая длина параметра
repe cmpsb; Сравниваем введённый хвост с ожидаемым
jne live; Введена неизвестная команда
inc flag; Введено «off», установим флаг запроса на выгрузку
; Проверим, не установлена ли уже данная программа
live: mov ah,0f1h; Установим нашу функцию
mov al,0; и подфункцию на наличие нашей программы в оперативной памяти
int 2fh
cmp al,0ffh; Программа установлена?
je installed; Да, при наличии запроса на выгрузку её можно выгрузить
; Сохраним вектор 2fh
mov ax,352fh; Функция получения вектора 2fh
int 21h
mov word ptr cs:old_2fh,bx; Сохраним смещение системного обработчика
mov word ptr cs:old_2fh+2,es; Сохраним сегмент системного обработчика
; Заполним вектор 2fh
mov ax,252fh; Функция установления вектора прерывания 2fh
mov dx,offset new_2fh; Смещение нашего обработчика
int 21h
; Сохраним вектор 09h
mov ax,3509h; Функция получения вектора 09h
int 21h
mov word ptr cs:old_09h,bx; Сохраним смещение системного обработчика
mov word ptr cs:old_09h+2,es; Сохраним сегмент системного обработчика
; Заполним вектор 09h
mov ax,2509h; Функция установления вектора прерывания 09h
mov dx,offset new_09h; Смещение нашего обработчика
int 21h
mov ah,4eh; Функция поиска файла
lea dx,filename; DS:DX ASCIIZ имени файла
int 21h
cmp ax,12h; Файл не найден?
je creat; Да, создадим файл
call div2_f; Нет, вызов процедуры открытия файла и записи в него текущей даты и времени
jmp by; Переход на метку by
creat: call div_f; Вызов процедуры создания файла
; Выведем на экран информационное сообщение
by: mov ah,09h; Функция вывода на экран
lea dx,mes; DS:DX адрес строки
int 21h
mov ax,3100h; Функция «завершиться и остаться резидентным»
mov dx,(end_res-main+10fh)/16; Размер в параграфах
int 21h
installed:
cmp flag,1; Запрос на выгрузку установлен?
je unins; Да, на выгрузку
; Выведем на экран информационное сообщение
mov ah,09h; Функция вывода на экран
lea dx,mes1; DS:DX адрес строки
int 21h
; Выведем предупреждающий звуковой сигнал
mov cx,5; Количество гудков
mov ah,02h; Функция вывода на экран
l: mov dl,07h; ASCII код зуммера
int 21h
loop l; Повторим CX раз
mov ax,4c01h; Функция завершения с кодом возврата
int 21h
unins:
; Перешлём в первую (резидентную) копию программы запрос на выгрузку
mov ax,0f101h; Наша функция с подфункцией выгрузки
int 2fh; Мультиплексное прерывание
; Выведем на экран информационное сообщение
mov ah,09h; Функция вывода на экран
lea dx,mes2; DS:DX адрес строки
int 21h
mov ax,4c00h; Функция завершения программы
int 21h
buf db 'Skencode&Klav_flag file',0ah,0dh
buflen equ $-buf
mes db 'Program installed$'
mes1 db 'Program already installed$'
mes2 db 'Program is DIE$'
init endp
text ends
end main