| Работа с прямоугольными областями
В данном разделе нас будет интересовать многофункциональная подпрограмма,
способная выполнять различные манипуляции с графическими объектами прямоугольной
формы. Ее составление возможно при условии, что требуемые действия выполняют
специализированные вспомогательные подпрограммы.
Переадресация строк
При работе с графическими объектами после обработки каждой строки надо
вычислять адрес начала следующей. В разделе 7.2 было рекомендовано использовать
для этого константу коррекции адреса видеопамяти, которая вычисляется
по следующей формуле:
offsline = bperline — widthrect*bytppnt = (horsize — widthrect)*bytppnt.
Как видно из этой формулы, значение offsline
зависит от видеорежима (переменные Horsize и
bytppnt) и от ширины объекта (переменная widthrect),
поэтому его приходится вычислять в каждом конкретном случае.
При работе в видеорежимах PPG команды для вычисления значения
offsline (пересылка в и вычитание) мы включали в тексты примеров.
При работе в видеорежимах direct color увеличивается
не только количество команд, вычисляющих значение offsline,
но и количество регистров, в которых расположены операнды этих команд.
Поэтому имеет смысл составить короткую подпрограмму, выполняющую необходимые
вычисления и учитывающую характеристики установленного видеорежима.
Варианты подпрограммы calloffs. В примере 7.13 показаны два варианта
подпрограмм, вычисляющие значение offsline с использованием команд умножения
или сдвигов. Входным параметром является значение переменной widthrect,
которое указывается в регистре dx. Этот регистр выбран потому, что во
всех примерах он использовался для указания ширины прямоугольной области.
Для совместимости с ранее приведенными примерами результат вычислений
находится в регистре bх.
Пример 7.13. Варианты подпрограмм для вычисления offsline
; Вариант 1 — вычисление off-sline
с использованием сдвигов
calloffs: push ex сохранение содержимого сх
mov ex, wrdppnt ex = величина сдвига
mov bx, horsize bx = ширина экрана в точках
sub bx, dx bx = horsize — widthrect
shl bx, cl bx = (horsize - widthreet) * bytppnt
pop ex восстановление содержимого сх
ret возврат из подпрограммы
; Вариант 2 — вычисле ние offsline с использованием
умножения
calloffs: push dx сохранение содержимого dx
xchg ax, bx обмен содержимого регистров ах, bx
mov ax, horsize ах = ширина экрана в точках
sub ax, dx ах = horsize — widthrect
mul bytppnt ах = (horsize — widthrect)* bytppnt
xchg ax, bx ; обмен содержимого регистров ах, bх pop dx ; восстановление
содержимого dx ret ; возврат из подпрограммы
Первый вариант подпрограммы примера 7.13 короче на одну команду и выполняется
немного быстрее второго, но его можно использовать только в тех случаях,
когда код точки занимает 1, 2 или 4 байта.
Второй вариант длиннее первого на одну команду и выполняется немного
дольше, но его можно использовать при любом размере кода точки. Выбор
конкретного варианта подпрограммы остается за вами.
Пересылка в видеопамять
При работе с графикой достаточно часто приходится сохранять и восстанавливать
содержимое видеопамяти. Это делается, например, при каждом перемещении
курсора.
В примере 7.14 приведен текст подпрограммы, выполняющей копирование
содержимого оперативной памяти в видеопамять. Входными параметрами подпрограммы
являются размер прямоугольной области и адреса операндов источника и приемника.
Ширина прямоугольной области помещается в регистр dx, а высота в регистр
сх. Адрес оперативной памяти (источника) указывается в регистрах fs:si.
Адрес видеопамяти (приемника) помещается в регистр di и устанавливается
окно видеопамяти, которому принадлежит этот адрес. В регистре es должен
находиться код видеосегмента (A000h).
Пример 7.14. Подпрограмма пересылки из оперативной в видеопамять
Rstreg: PushReg <bx, ex, di, si, Cur_win> ; сохранение
в стеке
call calloffs ; вычисление константы offsline
mvsr: push сх сохранение значения счетчика строк
mov ex, dx задание количества точек в строке
call drawline копируем очередную строку
add di, bx адрес начала следующей строки
jnc @F -> адрес в пределах окна
call Nxtwin установка следующего окна
@@: pop сх восстановление счетчика строк
loop mvsr управление повторами цикла
PopReg <Cur_win, si, di, ex, bx> ; восстановление из стека
call setwin восстановление исходного окна
ret возврат из подпрограммы
Текст примера не требует особых пояснений — подобные циклы мы описывали
неоднократно, например, в разделе
(подпрограмма draw). Поговорим о том, что явно
не следует из текста.
Зависимость от установленного видеорежима в данном примере скрыта в
подпрограммах calloffs и drawline.
Если вы выберете второй вариант подпрограммы caiioffs примера 7.13 и подпрограмму
drawiine, текст которой описан в примере 7.12, то подпрограмма Rstreg
будет выполняться в любом видеорежиме, независимо от размера кода точки.
Размер прямоугольной области, выраженный в байтах, не должен превышать
размера стандартного сегмента памяти, т. е. 65 536 байтов. Это ограничение
связано с тем, что пересылаемые данные находятся в оперативной памяти,
которая сегментирована так же, как и видеопамять, а в примере 7.14 отсутствует
контроль значения адресов оперативной памяти.
Способы контроля значений адресов оперативной и видеопамяти ничем не
отличаются друг от друга, но существенно различаются способы переключения
сегментов, которые зависят еще и от типа оперативной памяти. Они подробно
описаны в приложении Б данной книги. Там же приведен пример подпрограммы,
выполняющей сохранение или восстановление содержимого всего пространства
видеопамяти отображаемого на экране (см. примеры Б.7 и Б.8).
Пересылка из видеопамяти
Для сохранения исходного содержимого видеопамяти производится его копирование
(пересылка) в оперативную память. Нас интересуют универсальные процедуры
пересылки, основанные на применении строковых операций. Однако у строковых
операций фиксированы эегистры, содержащие адреса операндов источника и
приемника. Поэтому при пересылке в обратном направлении в es:di
должен находиться адрес шеративной памяти, а в
fs-.si — адрес видеопамяти. Для удобства лучше сохранить единообразный
способ указания адресов в регистрах и изменять его в подпрограмме на время
пересылки.
Заметим, что и после перестановки адресов использовать подпрограмму
drawiine из примера 7.12 нельзя. При ее составлении
предполагалось, что адрес видеопамяти находится в регистре di, а он оказался
в регистре si. Поэтому надо сделать копию примера 7.12, присвоить ей новое
имя, например saveiine, и заменить в двух командах
копии имя регистра di именем регистра si. В
результате получится универсальная подпрограмма, выполняющая сохранение
строки видеопамяти в оперативной памяти.
Подпрограмма Savereg
В примере 7.15 показано, как можно переставить адреса операндов на время
выполнения цикла пересылки, а затем восстановить их исходное расположение
в регистрах. Входные параметры в данном случае задаются так же, как для
примера 7.14.
Пример 7.15. Подпрограмма пересылки из видеопамяти в оперативную
Savereg: PushReg <bx, ex, di, si, es, Cur_win> ;
сохранение в стеке
mov bx, fs ; копируем код сегмента из fs в bx
mov es, bx ; копируем код сегмента из bx в es
mov fs, Vbuff ; fs = сегмент видеобуфера
ЮЗак П78
xchg di, si перестановка адресов di и si
call calloffs вычисление константы offsline
svrg: push ex сохранение значения счетчика строк
mov ex, dx задание количества точек в строке
call saveline копируем очередную строку
add si, bx адрес начала следующей строки
jnc @F -> адрес в пределах окна
call Nxtwin установка следующего окна
@@: pop ex восстановление счетчика строк
loop svrg управление повторами цикла
push es помещаем код сегмента из es в стек
pop fs и выталкиваем его из стека в fs
PopReg <Cur_win, es, si, di, ex, bx> ; восстановление из стека
call setwin восстановление исходного окна
ret возврат из подпрограммы
Напомним, что команда xchg не работает с сегментными
регистрами, а у команды mov только один операнд может быть именем сегментного
регистра. Поэтому для пересылки содержимого одного сегментного регистра
в другой приходится использовать либо регистр-посредник, либо стек. Оба
этих способа показаны в примере 7.15.
Основной цикл пересылки примера 7.15 имеет имя svrg,
он отличается от аналогичного цикла примера 7.14 (mvsr)
только одной командой. При вычислении адреса следующей строки константа
коррекции прибавляется к содержимому регистра si,
а не di, как это делалось в примере 7.14.
Заливка прямоугольной области
Изменим текст примера 7.14 так, чтобы его можно было использовать для
окрашивания прямоугольной области заданным цветом. В этом случае при выполнении
цикла должна вызываться подпрограмма horiine, а не drawline.
Регистр si не используется, поэтому его ИМЯ Исключается ИЗ СПИСКОВ PushReg
И PopReg.
Измененный текст подпрограммы показан в примере 7.16. При ее вызове
код цвета указывается в регистрах ах или еах (в зависимости от видеорежима).
Ширина прямоугольной области задается в регистре dx, а высота — в сх.
Адрес видеопамяти для левого верхнего угла должен находиться в регистре
di, регистры fs:si не используются. Предполагается, что es содержит код
видеосегмента и установлено исходное окно видеопамяти.
Пример 7.16. Окрашивание прямоугольной области заданным
цветом
Fillreg: PushReg <bx, ex, di, Cur_win> ; сохранение
в стеке
call calloffs ; вычисление константы offsline
fillrg: push ex ; сохранение значения счетчика строк
mov ex, dx ; задание количества точек в строке
call horline рисуем очередную строку
add di, bx адрес начала следующей строки
jnc @F -> адрес в пределах окна
call Nxtwin установка следующего окна
@@: pop ex восстановление счетчика строк
loop fillrg управление повторами цикла
PopReg <Cur_win, di, ex, bx> ; восстановление из стека
jmp setwin установка окна и выход
В отличие от примеров 7.14 и 7.15, в данном случае размер закрашиваемой
области экрана не ограничен, лишь бы хватило памяти, установленной на
видеокарте. Например, для окрашивания всей рабочей поверхности экрана
в нужный цвет, перед вызовом подпрограммы код цвета помещается в регистре
bx (или еах), в сх копируется переменная versize, а в dx — Horsize, регистр
di очищается и устанавливается нулевое окно видеопамяти.
Текст примера 7.16 не зависит от видеорежима, но в нем вызывается подпрограмма
horline, в тексте которой есть переменные команды, зависящие от видеорежима
(см. раздел). Поэтому, в отличие от подпрограмм
пересылки, подпрограмма Filireg не является универсальной.
Многофункциональная подпрограмма
Основное различие текстов примеров 7.14 и 7.16 заключается в имени вспомогательной
подпрограммы, используемой для выполнения конкретных действий.
Для того чтобы приведенная в примере 7.14 подпрограмма стала многофункциональной,
команду call drawiine надо заменить командой call bp, a перед вызовом
указывать в регистре bр адрес вспомогательной подпрограммы. Аналогичный
прием описан в разделе, на примере
подпрограммы draw. Там же показано, как формируется адрес в регистре bр.
После указанного изменения подпрограмму Rstreg можно использовать, например,
для построения рисунков, изменения их цвета, окрашивания прямоугольной
области и любых других действий, которые способны выполнять вызываемые
вспомогательные подпрограммы. |