
- •Лучший курс лекций по лучшему языку программирования – языку ассемблера Блок управления памятью (mcb)
- •Программирование на Ассемблере для ibm pc
- •Программная модель 8086
- •Программная модель мп
- •Сегментная память
- •Вывод на экран
- •Вывод строки символов
- •Вывод на экран шестнадцатеричного числа
- •Процедуры
- •Ввод символов
- •Вывод на экран двоичного числа
- •Работа с дисковыми файлами
- •Префикс сегмента программы (psp)
- •Структура префикса программного сегмента.
- •Прерывания.
- •Табличные вызовы подпрограмм.
- •Макрокоманды.
- •Макроопределения.
- •Использование параметров в макроопределениях.
- •Директива local
- •Использование библиотек макроопределений.
- •Конкатенация. (&)
- •Директивы повторения.
- •Условные директивы.
- •Перечисление условных директив.
- •Пример использования ifnb.
- •Пример использования макроопределений.
- •Резидентные программы tsr (terminate and stay resident)
- •Защита резидентной программы от повторной загрузки.
- •Выгрузка резидентной программы.
- •Переключение стека в резидентной программе.
- •Обработчик прерываний от таймера
- •Контроллер прерываний и его программирование
- •Взаимодействие прикладных и системных обработчиков прерываний
- •Резидентный обработчик прерываний от клавиатуры с подключением до системного
- •Резидентный обработчик прерывания от клавиатуры с подключением после системного обработчика
- •Резидентный обработчик прерываний от клавиатуры с подключением как до, так и после системного.
- •Вывод на экран текста средствами bios
- •Режимы дисплея
- •Работа с видеобуфером.
- •Логическая организация текстового видеобуфера.
- •Программирование портов. Звук.
- •Программирование звукового канала таймера.
- •Интерфейс с Си
- •Программы с несколькими сегментами команд.
- •Программы с несколькими сегментами данных.
- •Директива assume. Инициализация сегментных регистров и замена сегментов.
- •Структуры и записи.
Резидентный обработчик прерывания от клавиатуры с подключением после системного обработчика
Рассмотрим подключение обработчика после системного.
Во многих случаях прикладная обработка выполняется в качестве дополнения к системной. Системная программа BIOS отработки прерывания от клавиатуры преобразует скен-коды в коды ASCII, кроме этого выполняет массу дополнительных операций:
- сходит за указателями КБВ и модифицирует их;
- отслеживает нажатие (Ctrl)/(Break) и выполняет при его вводе специфические действия;
- позволяет вводить в КБВ коды непосредственно в цифровом виде с помощью нажатия (Alt) и цифр на цифровой клавиатуре и т.д.
Т.к. такое большое число функций не целесообразно переносить в прикладной обработчик, то во многих случаях удобнее только изменять коды в КБВ. Например, русификатор, работающий после системного обработчика, и только изменять коды ASCII в КБВ, если включен режим кириллицы.
Рассмотрим простой пример. Предположим, что пользователь, работающий с простым редактором часто пользуется символами псевдографики. В этом случае целесообразно закрепить коды ASCII этих символов за какими-то клавишами или сочетаниями клавиш, заменив коды ASCII этих клавиш.
Например: (Alt)/(U) - - уголок (левый верхний уголок) и т.д.
CODE_SEG SEGMENT
ASSUME CS:CODE_SEG,DS:code_seg
ORG 100H
START:
JMP BEGIN
int_2Fh_vector DD ?
old_09h DD ?
;=============================================================================
;
;=============================================================================
new_09h proc far
pushf ; В системный обработчик
call CS:old_09h ; с возвратом
push AX
push BX
push ES
mov AX,40h ; Настроим ES на сегментный
mov ES,AX ; адрес области данных BIOS
mov BX,ES:[lCh] ; Адрес нового хвоста
dec BX ; Сместимся назад к последнему
dec BX ; введенному символу
cmp BX,lEh ; Хвост не вышел за пределы буфера?
jae go ; Нет, значит он был где-то внутри буфера
mov BX,3Ch ; Хвост после вычитания 2 вышел за пределы буфера,
; сл-но он был в самом начале, а последний введенный
; символ находится в самом конце буфера
go:
mov AX,ES:[BX] ; Получим последний символ из буфера
cmp AX,1600h ; Был введен расширенный код ASCII сочетания Alt/U?
jne go__out ; Нет
mov word PTR ES:[BX],OODAh ; Да, заменим код в буфере на код уголка
go_out:
pop ES
pop BX
pop AX
iret
new_09h endp
;=============================================================================
;
;=============================================================================
int_2Fh proc far
cmp AX,OC700h
jne pass_2Fh
mov AL,OFFh
iret
pass_2Fh:
jmp dword PTR CS:[int_2Fh_vector]
int_2Fh endp
;=============================================================================
;
;=============================================================================
begin:
mov AX,OC700h ; AH=OC7h номер процесса C7h
; AL=OOh -дать статус установки процесса
int 2Fh ; мультиплексное прерывание
cmp AL, 0
jz not_installed ; возвращает AL=0 если не установлена
lea DX,msg
call print
int 20h
msg DB 'Уже установлена',13,10,'$'
not_instailed:
mov AX,352Fh ; получить вектор
int 21h ; прерывания 2Fh
mov word ptr int_2Fh_vector,BX ; ES:BX - вектор
mov word ptr int_2Fh_vector+2,ES
mov DX,offset int_2Fh ; получить смещение точки входа в новый
; обработчик на DX
mov AX,252Fh ; функция установки прерывания
; изменить вектор 2Fh
int 21h ; AL - номер прерыв. DS:DX - указатель программы обработки прер.
mov AX,3509h ; получить вектор
int 21h ; прерывания 09h
mov word ptr old_09h,BX ; ES:BX - вектор
mov word ptr old_09h+2,ES ;
mov DX,offset new_09h ; получить смещение точки входа в новый
; обработчик на DX
mov AX,2509h ; функция установки прерывания
; изменить вектор 09h
int 21h ; AL - номер прерыв. DS:DX - указатель программы обработки прер.
;------------------------------------------------------------------------------
mov DX,offset begin ; оставить программу ...
int 27h ; ... резидентной и выйти
;=============================================================================
;
;=============================================================================
PRINT PROC NEAR
MOV АН,09H
INT 21H
RET
PRINT ENDP
;=============================================================================
CODE_SEG ENDS
END START
После системного управления передается в наш обработчик, а сегментный регистр EC настраивается на начало области данных BIOS (физический адрес 400h, сегментный адрес 40h).
Пусть перед вводом данного символа буфер был пуст.
40h:1Еh |
|
|
|
|
|
|
|
|
Мусор |
Мусор |
Хвост 1Сh |
|
|
|
|
|
|
|
40h:ЗСh |
Фактически в слове, на которое указывают оба указателя, находится код символа, введенного ранее и уже изъятого программой из буфера
Это адрес указателя
а не его
содержимое
Очередное нажатие клавиши вызывает нашу программу, а из нее - системный обработчик, который заносит двухбайтовый код нажатой клавиши в хвостовой элемент, перемещая хвостовой указатель на следующее слово буфера:
40h:1Eh |
|
|
|
|
|
|
|
|
Скан |
ASCII |
|
|
|
|
Хвост 16h |
|
|
|
|
|
|
|
40h:ЗСh |
На первый взгляд, получить код нажатой клавиши можно либо по адресу в головном указателе, либо вычтя 2 из адреса хвостового указателя.
Однако ни то, ни другое неверно.
Головной указатель действительно указывает на последний сохраненный в буфере код, лишь, если при запуске программы буфер был пуст. Если же в процессе загрузки программы пользователь нажимал какие-либо клавиши, их коды были помещены в буфер клавиатуры, и к моменту активизации нашей программы головной указатель будет указывать не на пустую ячейку буфера, а на самый старый из введенных символов.
При этом по мере занесения в буфер новых символов, обработке будет подвергаться все тот же самый старый символ. Следовательно, более надежно определять местонахождение последнего символа в буфере по адресу в хвостовом указателе. Однако не всегда этот адрес будет на 2 больше адреса последнего символа, так как буфер - кольцо. В том случае, когда очередная свободная ячейка буфера находилась в самом его конце, по адресу 40h:3Ch, после занесения туда очередного кода хвостовой указатель переместится в начало буфера, указывая на адрес 1Еh. В программе после вычитания 2 из адреса хвостового элемента, проверяется находится ли полученное значение в пределах буфера. Если оно равно или больше 1Еh, команда jae - выполняет переход на продолжение программы. Если полученное значение меньше 1Еh, то это значит, что хвостовой указатель указывает на самое начало буфера, а занесенный только что в буфер код находится в самом его конце:
|
|
|
Хвост 16h |
В этом случае в BХ |
|
|
|
|
заносится адрес последнего |
|
|
|
|
символа в явном виде |
|
|
|
|
|
|
|
|
|
|
|
Скан |
ASCII |
40h:ЗСh |
|
Затем код нажатой клавиши извлекается из буфера и сравнивается с двухбайтовыми кодами (Alt)/(U) и др. - которые являются расширенными кодами ASCII. Если нажато одно из сочетаний, расш. Код ASCII в буфере заменяется на код ASCII символа псевдографики, без смещения указателя и осуществляется переход на go_out для завершения программы.