Для вывода точки заданного цвета в нужное место экрана надо связать координаты этого места с адресом видеопамяти, по которому должен быть записан код точки. Поэтому мы вновь возвращаемся к вопросам, рассмотренным в разделе, но с учетом особенностей режимов direct color.
После чтения массива info задаче доступны две величины, имеющие отношение к разрешающей способности режима.
При работе в режимах PPG обе величины совпадают, поэтому мы использовали только первую из них. Теперь нам может пригодиться и вторая величина, поэтому после чтения массива info ее значение надо присвоить переменной bperiine, она была описана в примере 2.11, но не применялась.
В некоторых случаях нам будет нужен размер кода точки в байтах. Такой величины в массиве info нет, но байт со смещением 19h содержит количество разрядов в коде точки. Если его сдвинуть на три разряда вправо и результат преобразовать в слово, то получится нужная нам переменная.
Если копию этой переменной сдвинуть еще на 1 разряд вправо, то получится еще одна переменная, содержащая количество слов в коде точки. Имена и описания новых переменных следующие:
bperiine dw 2560 ; размер строки отображаемой на экране
в байтах
bytppnt dw 0004 ; размер кода точки, выраженный в байтах
wrdppnt dw 0002 ; размер кода точки, выраженный в словах
В этом описании переменных указаны значения, которые получаются при установке режима H2h — True color, 640x480 точек.
Преобразование координат в адрес выполняется перед началом работы с большинством графических объектов. Примеры таких преобразований при работе в режимах PPG приводились неоднократно. Здесь нас будет интересовать универсальный вариант преобразования, который можно использовать при работе во всех видеорежимах VESA.
При описании подпрограммы примера 6.13 (перемещение курсора) было рекомендовано
для учета размера кода точки, после вычисления адреса, сдвинуть результат
на 1 или 2 разряда влево, что равносильно умножению на 2 или на 4. Это
самый простой, но не универсальный способ, поскольку при составлении программы
надо знать величину сдвига, которая зависит от видеорежима. Кроме того,
с помощью сдвигов невозможно выполнить умножение на 3, нужны дополнительные
команды сложения. Поэтому, в общем случае, целесообразно выполнять умножение,
а не сдвиг.
Формулу для вычисления адреса по заданным значениям координат х и у можно
записать в следующем виде:
Address = (Y*horsize t- X) *bytppnt
При замене умножения на bytppnt сдвигами действия выполнялись в той последовательности, в какой они указаны в формуле — сначала умножение, затем сложение и, наконец, сдвиг. Если же сдвиг заменить умножением, то последовательность действий придется изменить.
Результат заключенных в скобки действий расположен в двух регистрах, dx содержит его старшую часть, а ах — младшую. Для умножения двойного слова (или содержимого двух регистров) на значение переменной bytppnt потребуется много вспомогательных действий. Чтобы упростить вычисления в приведенной выше формуле, надо раскрыть скобки и учесть, что bperiine = horsize*bytppnt, в результате получится следующее выражение:
Address = Y*bperline + X*bytppnt
Подпрограмма Caladdr
В примере 7.3 приведен текст подпрограммы, выполняющей вычисления по этой формуле. Перед ее вызовом в регистре сх указывается номер столбца (координата х), а в регистре dx — номер строки (координата у). Вычисленный адрес помещается в регистры dx:ax, т. е. в ах находится значение окна, а в dx — адрес (смещение) в этом окне.
Пример 7.3. Универсальная подпрограмма вычисления видеоадреса
Daladdr: mov ax, bperline ax = размер строки в байтах
mul dx dx:ax = Y*bperline
push dx сохраняем старшую часть результата
xchg ax, ex обмен содержимого регистров
mul bytppnt ах = X*bytppnt, dx = 0
add ax, ex вычисляем младшую часть адреса
mov dx, ax и сохраняем ее в регистре dx
pop ax ах = старшая часть Y*bperline
adc ax, 00 учитываем возможность переноса
mul byte ptr GrUnit ах = al * GrUnit
add ax, Base win ! ! если используется базовое окно
ret выход из подпрограммы
Текст примера 7.3 не нуждается в подробных пояснениях, обращаем ваше внимание только на следующие особенности. Содержимое регистра dx (старшую часть произведения y*bperiine) надо сохранить в стеке потому, что оно будет испорчено при втором умножении. После второго умножения и вычисления младшей части адреса старшая часть выталкивается из стека в регистр ах. К ней прибавляется единица переноса, которая могла возникнуть, если при выполнении команды add ax, сх произошло переполнение и был установлен С-разряд регистра флагов (признак Carry). Команды пересылки и выталкивания из стека не изменяют состояние С-разряда. Поэтому если он был установлен, то adc ax, 00 прибавит единицу к содержимому регистра ах.
Можно изменить текст примера 7.3 так, чтобы вычисленный адрес возвращался в регистре di, значение окна присваивалось переменной cur_win и выполнялась установка окна (call setwin). В результате получится вариант подпрограммы Caiiwin, описанной в примере 3.4, применимый в любых видеорежимах VESA.
Другой вариант Caladdr
В примере 7.4 показан вариант подпрограммы caiaddr, в котором вместо умножения x*bytppnt содержимое регистра сх сдвигается на к разрядов влево. В зависимости от видеорежима, в команде сдвига букву к надо заменить цифрами 1 или 2, т. е. эта подпрограмма не универсальна.
Пример 7.4. Пересчет координат в адрес с использованием сдвига
Caladdr: mov ax, bperline ax = размер строки в байтах
raul dx dx:ax = Y*bperline
shl ex, k k=l для Hi-Color; k=2 для True Color
add ax, ex вычисляем младшую часть адреса
adc dx, 00 учитываем возможность переноса
xchg ax, dx обмен содержимого регистров
mul byte ptr GrUnit ax = al * GrUnit
add ax, Base_win !! если используется базовое окно
ret выход из подпрограммы
В примере 7.4 сдвигается не результат умножения, а только значение координаты х (содержимое регистра сх). Это возможно потому, что значение координаты Y (содержимое регистра dx) умножается не на Horsize, а на bperline = horsize*bytppnt.
Особенность операций сдвигов заключается в том, что величина сдвига может либо находиться в регистре el (младший байт регистра сх), либо указываться непосредственно в команде, других вариантов нет. Поэтому для автоматического выбора величины сдвига в примере 7.4 вместо двух подряд расположенных команд shl ex, k и add ax, сх надо записать следующие:
mov bx, wrdppnt ; bx = величина сдвига
xchg bx, ex ; обмен содержимого регистров
shl bx, cl ; сдвиг значения координаты X
add ax, bx ; вычисляем младшую часть адреса
Напомним, что если видеокарта в режиме True color поддерживает трехбайтовый код точки, то заменять умножение сдвигами не целесообразно.
Подведем итог. Первая из двух описанных подпрограмм универсальная, а вторая специализированная. Вопрос о том, какая из них лучше, вообще говоря, не корректен. Корректен другой вопрос — в каких случаях нужны универсальные подпрограммы, а в каких специализированные. Первые целесообразно составлять при разработке библиотечных модулей, особенно для языков высокого уровня. А если вы разрабатываете задачу, в которой большинство подпрограмм специализировано, то целесообразность включения в ее текст одной или нескольких универсальных подпрограмм весьма проблематична.
Координаты и адреса смежных точек
Значения координат нужны для вычисления базового адреса видеопамяти, соответствующего некой опорной точке, как правило, левого верхнего угла, графического объекта. В процессе работы с объектом адреса остальных точек вычисляются упрощенным способом исходя из текущего значения адреса, т. е. учитывается зависимость приращения адресов от взаимного расположения точек на экране.
Если базовая точка не лежит на одной из четырех границ экрана, то ее окружает восемь смежных точек. В табл. 7.4 показаны приращения значений координат и адресов смежных точек. Базовая точка имеет координаты х, Y, а приращение ее адреса равно нулю. В правой части таблицы буква k соответствует переменной bytppnt, а буква w — переменной bperline.
Таблица 7.4. Приращения координат и адресов смежных точек
Приращение смешанных координат |
Приращение их адресов |
||||
X-1.Y-1 |
X, Y-1 |
X+1.Y-1 |
k-w |
W |
k-w |
X-1.Y |
X, Y |
X+1.Y |
k |
0 |
k |
X-1.Y+1 |
X, Y+1 |
X+1.Y+1 |
w-k |
w |
w+k |
Из табл. 7.4, в частности, следует, что при перемещении по горизонтали адреса точек уменьшаются или увеличиваются на значение переменной bytppnt. Если для работы с кодами точек используются обычные операции, то после их выполнения текущий адрес надо изменить на bytppnt. Если же применяются строковые операции, то они автоматически изменяют текущий адрес на размер операнда. При обработке точек в естественном порядке, т. е. в сторону увеличения значений их координат, строковые операции увеличивают адрес, а при обработке точек в обратном порядке уменьшают его. Таким образом, при последовательной обработке точек строки для получения адреса очередной точки достаточно простой переадресации операндов.
Адрес следующей строки
Если строки графического объекта обрабатываются последовательно друг
за другом, то после построения текущей строки надо определить адрес начала
следующей (или предыдущей) строки. В этом случае простой переадресации
операндов недостаточно.
Первые точки строк прямоугольной области расположены на экране в одном
столбце. Значения координаты х у них совпадают, а координаты Y различаются
на величину, кратную значению переменной bperline.
Это и надо учесть при вычислениях.
Адрес начала текущей строки можно хранить в специально выделенном месте и для перехода к следующей строке увеличивать или уменьшать его значение на bperiine. Однако, как уже говорилось в разделе, неизвестно, какому окну принадлежит сохраненный адрес, поскольку при обработке строки могла произойти смена окна. Поэтому при таком способе вычислений будет нужен специальный признак переключения окна и анализ его состояния.
Для упрощения вычислений надо использовать адрес, полученный в конце обработки текущей строки. Он заведомо принадлежит установленному окну, а отличается от адреса начала строки на ширину прямоугольной области, выраженную в байтах. Поэтому если к нему прибавить значение переменной bperiine, уменьшенное на ширину прямоугольной области, то получится адрес начала следующей строки. Ширина прямоугольной области задается в виде количества точек, которое надо умножить на размер кода точки. Приведенные рассуждения можно записать в виде следующей формулы вычисления константы коррекции адреса (offsline обозначает смещение строки).
offsline = bperiine — widthrect*bytppnt = (horsize — widthrect)*bytppnt.
В этих формулах widthrect обозначает ширину прямоугольной области, выраженную в точках, имена остальных переменных вам известны. Оба варианта формулы равноценны по количеству выполняемых действий.
При работе в режимах PPG bytppnt=1 и формула превращается в разность (horsize - widthrect), которая и вычислялась в приведенных ранее примерах. В разделe и разделe при описании подпрограмм построения и перемещения изображения курсора мы советовали в режимах direct color сдвигать указанную разность на 1 или 2 разряда влево. Такой прием прост, но не универсален. Универсальные способы вычисления значения offsline для режимов direct color описаны ниже в