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
3.4. Рекомендации по улучшению
– Главным недостатком этой программы является неудобное визуальное восприятие записей в файле. Т.е. мы видим не ASCII-код который образовался в результате нажатия клавиши, а так называемый скэн-код (номер клавиши) и состояние байта флагов клавиатуры, в котором он находился при этом нажатии. При необходимости можно написать процедуру в нашем обработчике либо в виде отдельной программы, которая анализировала бы байт флагов и в зависимости от этого подставляла ASCII-код соответствующий скэн-коду нажатой клавиши.
– Вторым недостатком нашей программы является не всегда удобный механизм выгрузки программы из оперативной памяти. Можно предусмотреть выгрузку нашей программы специальной не стандартной комбинацией клавиш.
– Третий существенный недостаток программы состоит в том, что наш обработчик не реагирует на сочетание клавиш Clrl+Alt+Del. Так как наш обработчик перехватывает прерывания от клавиатуры раньше чем системный обработчик “int09h”, то было бы целесообразно при этом сочетании сбрасывать содержимое буфера в файл, а затем передавать управление системному обработчику.
– Можно предусмотреть запись в файл autoexec.bat либо config.sys строки с путём к нашему файлу, при запуске программы с параметром вводимым с командной строки.
– Можно предусмотреть коррекцию размеров буфера, а также задавать имя рабочего файла с помощью всё тех же параметров вводимых с командной строки.
– В зависимости от того в каких целях применяется данный обработчик, можно запретить нажатие какой либо клавиши, комбинации клавиш или последовательности.
Данная программа является шаблоном для резидентных обработчиков прерываний, в частности обработчиков прерываний от клавиатуры, и является огромным полем для творчества.
4. Список используемой литературы
1. П.И.Рудаков, К.Г.Финогенов «Программируем на языке ассемблера IBM PC», Обнинск 1997г.
2. Зубков С.В. «Assembler для DOS, Windows и UNIX», Москва 2000г.
3. Богумирский Б.С. «Руководство пользователя ПЭВМ», Санкт–Петербург 1994г.