Файл: О.А.Калашников. Ассемблер Это Просто. Учимся программировать.pdf
Добавлен: 16.02.2019
Просмотров: 29227
Скачиваний: 1689
Глава 14. Вывод окна в центре экрана
139
14.2. Оболочка SuperShell
14.2.1. Управление курсором
Сразу заглядываем в файл main.asm. Нам необходимо скрыть курсор (
Hide_cursor
,
display.asm). Прежде чем это делать, необходимо запомнить его текущую позицию.
Это позволяет сделать функция
03h
прерывания
10h
(табл. 14.1).
Таблица 14.1. Функция
03h
прерывания
10h
:
получить текущую позицию курсора
Вход
Выход
ah = 3
bh
= видеостраница, с которой получить
текущую позицию курсора
dx
= текущая позиция курсора (dh — стро-
ка, dl — колонка)
Как видите,
bh
должен указывать на видеостраницу, с которой нужно сосчитать
позицию курсора. Как уже вам известно, видеокарта имеет достаточно памяти для
размещения 8 видеостраниц. Каждая видеостраница имеет свой курсор. Но мы-то
будем отображать все на нулевой странице, следовательно, необходимо в
bh
загру-
зить 0. На выходе
dx
будет содержать текущую позицию курсора:
dh
— строку,
а
dl
— колонку. Причем отсчет начинается с нуля (рис. 14.1).
Рис. 14.1. Получение текущей позиции курсора
После того как получили текущую позицию курсора, мы можем его спрятать.
Самый распространенный метод — установка курсора за границу экрана. В нашем
случае — установка на 25-ю строку. Режим-то у нас будет 3 (25 строк, 80 колонок).
Часть III. Файловая оболочка, вирус, резидент
140
Так как отсчет идет с нуля, то последняя позиция курсора (самая нижняя строка) —
24. Следовательно, установив курсор на строку 25 (19h), мы его просто "прячем"!
Установить курсор на нужную позицию на экране позволяет функция
02h
прерыва-
ния
10h
(табл. 14.2).
Таблица 14.2. Функция
02h
прерывания
10h
: позиционирование курсора
Вход
Выход
ah = 2
bh
= видеостраница, на которой будем устанавливать курсор
dh
= строка, dl = столбец для установки
Ничего
14.2.2. Операторы работы
со стеком процессора 80286+
После того как мы спрятали курсор, можем переходить к сохранению текущего
содержания нулевой видеостраницы. Как вы знаете, все уважающие себя оболочки
сохраняют и, при необходимости, показывают пользователю то, что изображено на
экране. Например, Norton Commander или Far Manager, которые отображают поль-
зовательский экран при нажатии комбинации клавиш <Ctrl>+<O>.
Как сохранить то, что находится на экране в данный момент, а затем, при необ-
ходимости, показать это пользователю?
Мы уже упоминали о том, что у видеокарты есть достаточно памяти для разме-
щения 8 видеостраниц. Почему бы нам не скопировать содержимое пользователь-
ской, нулевой страницы в первую? Вот мы это и делаем (
Save_mainscr
,
display.asm). Здесь мы встречаем новые операторы:
pusha
и
popa
(
pusha
от англ.
push all — втолкнуть все;
popa
от англ. pop all — вытолкнуть все). Оператор
pusha
толкает в стек регистры в следующем порядке:
ax
,
cx
,
dx
,
bx
,
sp
,
bp
,
si
и
di
, и, со-
ответственно, оператор
popa
выталкивает их из стека в обратном порядке:
di
,
si
,
bp
,
sp
,
bx
,
dx
,
cx
и
ax
.
На рис. 14.2 показано состояние стека после того, как в нем были сохранены пе-
речисленные выше регистры командой
pusha
.
Данные операторы используются только процессором 286+. Когда пишут, на-
пример, что такой-то оператор используется в процессоре 386+, то это значит, что
он будет работать на процессорах 80386, 80486, Pentium и т. д., но никак не на 8086
(PC/XT) и 80286 (PC/AT). В нашем случае команды
pusha
и
popa
будут работать на
80286 и последующих (более современных) процессорах. Для этого в самом начале
файла sshell.asm мы устанавливаем директиву
.286
. Эта директива указывает ас-
семблеру (MASM, TASM), что в данной программе могут встречаться команды
(инструкции) не только процессора 8086, но и 80286.
"Почему же мы раньше ничего подобного не писали?" — спросит внимательный
читатель.
Глава 14. Вывод окна в центре экрана
141
Рис. 14.2. Результат работы команды pusha
Если ничего подобного не указывать (как мы делали до сих пор), то ассемблер
(MASM, TASM) считает, что в программе используются только инструкции про-
цессора 8086.
Безусловно, использование команд
pusha
и
popa
удобно в тех случаях, когда нам
нужно сохранить в стеке более одного регистра. Они уменьшают размер програм-
мы, однако в то же время необходимо следить за тем, чтобы не произошло пере-
полнения стека.
Обратите также внимание, как мы заносим в сегментный регистр число:
...
push 0B800h
pop es
...
Это получается на 1 байт короче, чем если бы мы делали следующим образом:
...
mov ax,0B800h
mov es,ax
...
Более того, в этом случае мы не затрагиваем регистр
ax
, что иногда бывает по-
лезно.
Команды вида
push 1234h
работают также на процессорах 80286+, а если быть
точным, то и на процессорах 80186+. Но процессоры 80186 не получили большого
распространения, о них вообще мало кто знает, поскольку через несколько месяцев
после их выпуска появились 80286.
Все остальное в данной процедуре (
Save_mainscr
, display.asm) должно быть по-
нятно. Как и в процедуре восстановления экрана (
Restore_mainscr
, display.asm).
Часть III. Файловая оболочка, вирус, резидент
142
14.3.
Процедура рисования рамки (окна)
14.3.1. Прямое отображение в видеобуфер
Курсор спрятали, экран пользовательский сохранили. Сейчас можно и окно ри-
совать. Будем это делать, не прибегая ни к каким прерываниям, т. к. они работают
очень медленно. Воспользуемся выводом символов на экран путем прямого ото-
бражения в видеобуфер. С данным методом мы уже сталкивались, когда пытались
выводить на экран "рожицу". Теперь рассмотрим его более подробно.
Метод прямого отображения символов в видеобуфер — вывод символов на эк-
ран, путем записи их напрямую в сегменты видеобуфера (в память видеокарты),
минуя сервис прерываний. Стоит отметить, что это самый быстрый способ вывода
символов на экран. Быстрее не существует. Здесь все зависит от профессионализма
программиста и — как следствие — алгоритма вывода, скорости его выполнения.
Рассмотрим вкратце модели мониторов, многие из которых уже ушли в про-
шлое, и только старые и добрые игрушки периодически напоминают об их былом
существовании. Вот ряд от старых к современным:
MDA (Monochrome Display Adapter — монохромный видеоадаптер);
Hercules (улучшенный графический адаптер "Геркулес");
CGA (Color Graphics Adapter — цветной графический адаптер);
EGA (Enchanced Graphics Adapter — улучшенный графический адаптер);
VGA (Video Graphics Array — видеографическая матрица);
MCGA (Multi-Color Graphics Adapter — подобие VGA);
sVGA (Super Video Graphics Array — то, что в настоящий момент используют
в работе).
И
З И С Т О Р И И
При попытке вывода символа в мониторе CGA и подобных ему возникает так назы-
ваемый эффект "снега". Эта недоработка (ошибка?) была исправлена только в EGA.
"Снег" не возникал, если символы выводились при помощи прерываний 10h или 21h.
Однако эти прерывания использовали процедуру, которая проверяла состояние хода
луча монитора (символы выводились в тот момент, когда луч совершал обратный
ход), в связи с чем скорость вывода символов существенно замедлялась. Не знаем,
есть ли в настоящих "ПЗУшках" и в прерывании 21h подобная процедура или нет.
Главное — при выводе символов через прерывания скорость резко снижается. Пола-
гая, что никто из вас не использует сейчас CGA-монитор, будем выводить символы
прямым отображением в видеобуфер. Повторим еще раз: это очень быстро даже на
процессорах 8086. Очистка экрана в PC/XT происходит мгновенно. И уж тем более на
процессорах 80286+...
Для процедуры рисования окна (а может и не только для нее) заведем две пере-
менные:
Height_X
и
Width_Y
. Пока временно. В дальнейшем научимся передавать
данные в стеке. Переменная
Height_X
содержит высоту окна, а
Width_Y
— его ши-
рину.
Num_attr
— цвет рамки (
Main_proc
, main.asm). Можно вызывать процедуру
Draw_frame
, которая находится в файле display.asm.
Глава 14. Вывод окна в центре экрана
143
14.3.2. Процедура Draw_frame
Вот сейчас и рассмотрим самое главное. Итак, многофункциональная процедура
Draw_frame
(рисование рамки любого размера в центре экрана).
Перед вызовом данной процедуры необходимо занести некоторые числа в соот-
ветствующие переменные (main.asm), а именно:
Height_X
— высота окна;
Width_Y
— ширина окна;
Num_attr
— атрибуты окна.
Пока все. Дальше будем изучать — добавим еще переменных.
Итак, занесли. Вызываем процедуру
Draw_frame
(display.asm).
Теперь внимание! Перед тем, как вывести рамку в центр экрана, нужно произве-
сти некоторые расчеты. Для этого необходимо разделить высоту рамки на 2 и вы-
честь из полученного числа середину высоты. Вот так:
...
(1) mov ax,Height_X
(2) shr al,1
(3) mov dh,11
(4) sub dh,al
...
В строке (1) заносим в
ax
высоту рамки, которую указали перед вызовом про-
цедуры. Затем, в строке (2), пока еще неизвестный нам оператор
shr
сдвигает все
биты в регистре
al
на 1 вправо.
Что делает оператор
shr
? Вот пример:
mov ax,16 ;ax = 10000b, т. е. 16
shr ax,1 ;ax = 1000b, т. е. 8
shr ax,1 ;ax = 100b, т. е. 4
shr ax,1 ;ax = 10b, т. е. 2
shr ax,1 ;ax = 1b, т. е. 1
Таким образом, сдвиг битов вправо на единицу делит число на 2. На двойку —
в четыре раза и т. д. Это гораздо быстрее, чем если бы мы пользовались операто-
ром
div
— деление (который, кстати, мы еще не рассматривали). В строке (3) зано-
сим в
dh
середину экрана по горизонтали, т. е. 11. А затем вычитаем из 11 получен-
ный результат.
Например, мы выводим рамку высотой в 5 строк. Вот как производим вычисления:
ax = 10
ax = ax/2 = 5
ax = 11-5 = 6
Таким образом, вывод начнем со строки 6. Все то же самое мы делаем с колон-
ками (вертикально). Посмотрите в прилагаемом примере. В принципе, если что-то
пока не совсем понятно, то не заостряйте внимание на этом: со временем придет
опыт и более точное понимание.
Как только мы вычислили координаты для вывода окна в центре экрана, мы пе-
реводим высоту/ширину в линейный адрес.