Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29228
Скачиваний: 1689
Часть III. Файловая оболочка, вирус, резидент
144
Что такое линейный адрес и зачем он нужен?
Как вы уже знаете, то, что находится на экране, расположено по адресу от
0B800:0000h
до
0B800:1000h
(нулевая страница). Видеокарта не знает, что такое
колонка и строка. Для нее все данные расположены в линейном массиве, т. е. от
0
до
1000h
. Так как один символ занимает 2 байта (символ и атрибут), то одна строка
режима 3 занимает 160 байт (80 строк 2 = 160). Для того чтобы отобразить сим-
вол в первой строке (точнее, во второй, т. к. отсчет начинается с нуля), нам нужно
выводить его по смещению 161, сегмента
0B800h
. Следовательно, если мы указыва-
ем строку/столбец, то нам необходимо перевести эти числа в линейный массив,
"понятный" видеокарте. Вот пример (разбираем внимательно!):
;dh обычно указывает на строку
mov dh,0
;а dl — на столбец (это мы и будем делать во всех наших процедурах)
mov dl,15
;таким образом, мы будем выводить в нулевой строке
;(т. е. самой верхней на экране), 15 столбце.
Переводим 2 числа (строка и колонка) в линейный массив (переменная
Linear
будет содержать в итоге адрес для вывода символа на экран):
;Умножаем на 2 адрес строки, т. к. один символ на экране занимает
;два байта: символ + его атрибут
Linear = dl*2
Linear = Linear + dh*160
Теперь переменная
Linear
равна 30. Вот он, линейный адрес:
0B800:001Eh
(
1Eh=30
). Еще пример:
mov dh,8 ;Строка 8
mov dl,56 ;Столбец 56
Linear = dl*2 ;Теперь Linear = 112
Linear = Linear + dh*160 ;Теперь Linear = 1392 или 570h
И еще произведем вычисления для последнего символа на экране (правый ниж-
ний угол):
Linear
= (24 × 160 + 80 × 2) – 2 =
0F9Eh
.
Здесь:
80 — количество столбцов на экране, которое нужно умножить на 2 (учитывая
не только символ, но и его атрибут).
24 — количество строк на экране, начиная с 0. Так как в одной строке
80 символов (начиная с 1): 80 × 2 = 160, то, для того чтобы получить адрес само-
го последнего (24-го) ряда, нужно 24 умножить на 160.
Затем все это складываем (адрес 24-й строки + адрес последнего символа – 2).
Еще раз:
(24-я строка × 160 + 80-й символ × 2) – 2
или
(24 × 160 + 80 × 2) – 2 = 3998 или
0F9Eh
.
Глава 14. Вывод окна в центре экрана
145
14.4. Практика
Теперь рассмотрим, как это выглядит на практике. Итак, процедура (
Get_linear
,
display.asm) (листинг 14.2).
Листинг 14.2. Процедура Get_linear
...
;умножаем dl на 2 (dl=dl*2). Просто сдвинем биты на 1 влево (по принципу
;деления; см. предыдущий раздел).
shl dl,1
mov al,dh ;в al — ряд,
mov bl,160 ;который нужно умножить на 160
mul bl ;умножаем: al (ряд) * 160; результат — в ax
;Здесь видим новый оператор mul — деление, который рассмотрим позже.
mov di, ax ;результат умножения — в di
xor dh, dh ;аннулируем dh
add di,dx ;теперь в di линейный адрес в видеобуфере.
...
14.5. Новые операторы
В табл. 14.3—14.5 приведено описание новых операторов. В листингах 14.3—
14.5 показаны примеры использования этих операторов, а на рис. 14.3—14.5 — ре-
зультаты их работы.
Таблица 14.3. Оператор
shl
Команда
Перевод
Назначение
Процессор
shl источник, приемник
Shift left
— сдвиг влево
Сдвиг бит
8086
Листинг 14.3. Пример использования оператора shl
mov ax,100b
shl ax,1 ;Теперь в ax число 1000b
Таблица 14.4. Оператор
shr
Команда
Перевод
Назначение
Процессор
shr источник, приемник
Shift right
— сдвиг вправо Сдвиг бит
8086
Часть III. Файловая оболочка, вирус, резидент
146
Рис. 14.3. Результат работы оператора shl
Рис. 14.4. Результат работы оператора shr
Листинг 14.4. Пример использования оператора shr
mov ax,100b
shr ax,1 ;Теперь в ax число 10b
Таблица 14.5. Оператор
mul
Команда
Перевод
Назначение
Процессор
mul bl
Multiplex
— умножение
Умножение al на bl 8086
Умножаем
al
на
bl
, помещая результат в
ax
.
Глава 14. Вывод окна в центре экрана
147
Листинг 14.5. Пример использования оператора mul
mov al,5
mov bl,2
mul bl ;Теперь в ax число 10
Рис. 14.5. Результат работы оператора mul bl
Ну вот, собственно, и все. Остальное должно быть понятно из комментариев
к программе.
Глава 15
Обработка
аппаратных прерываний
В этой главе рассмотрим "многофункциональный" резидент. Будет сложно, но
интересно. Наша резидентная программа выполняет следующие действия:
1. Записывает в файл содержимое текстового экрана (то, что в момент нажатия
определенных клавиш находится в области экрана).
2. Меняет на экране все символы "A" и "a" на "О" и "о" соответственно.
3. Передает неверные данные о файлах оболочкам Norton Commander, Volkov
Commander, Dos Navigator.
И все это умеет один резидент, размер которого всего 746 байт!
15.1. Теория
Разобраться в инициализации резидента (метка
Init
) труда не составит. Говоря
в двух словах, мы делаем следующее: проверяем на повторную загрузку в память
путем отправления нашего "позывного" (в данном случае —
9889h
в
ax
) и получе-
ния или неполучения "отклика" (число
8998h
также в
ax
). Если после вызова пре-
рывания
21h
с числом
9889h
в аккумулятор возвращается
8998h
, то наш резидент
уже в памяти (он-то и меняет местами
ah
и
al
). Обратите внимание на первые че-
тыре строки процедуры
Int_21h_proc
, которые как раз меняют местами
ah
/
al
, но
делают это только в том случае, если наш резидент уже загружен в память.
Проведем эксперимент. Перенесем приведенные ниже строки после установки
всех прерываний, но перед вызовом
int 27h
(листинг 15.1).
Листинг 15.1. Проверка на повторную загрузку резидента в память
...
mov ax,9889h
int 21h
cmp ax,8998h
jne Set_resident
...
В этом случае наша программа выдаст сообщение о том, что она уже загружена
в память, хотя и загружается первый раз. Следовательно, наш обработчик
Int_21h_proc