В данном разделе нас будет интересовать многофункциональная подпрограмма, способная выполнять различные манипуляции с графическими объектами прямоугольной формы. Ее составление возможно при условии, что требуемые действия выполняют специализированные вспомогательные подпрограммы.
При работе с графическими объектами после обработки каждой строки надо вычислять адрес начала следующей. В разделе 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, в тексте которой есть переменные команды, зависящие от видеорежима (см.