Графические устройства

         

Подпрограммы для построения строк

Строки отличаются от линий тем, что в оперативной памяти хранится их точечный образ. Он может быть получен в результате чтения файла, содержащего рисунок, сохранения изображения находившегося на экране или любым другим способом, для нас это не имеет значения. Однако сам факт существования образа (или заготовки) строки в отличие от ее рисования непосредственно в процессе вывода на экран существенно изменяет структуру подпрограмм.

Если не требуется дополнительных преобразований кодов точек, то построение строки рисунка сводится к пересылке заданного количества байтов из оперативной в видеопамять или в обратном направлении (для сохранения содержимого видеопамяти). Следовательно, возможно составление универсальных подпрограмм, выполняющихся в любом видеорежиме.

Подпрограммы, выполняющие различные манипуляции со строками при работе в видеорежимах PPG, описаны в разделе. Здесь мы продолжим эту тему применительно к режимам direct color.

Для всех вариантов подпрограммы построения строки мы сохраним то расположение входных параметров в регистрах, которое было принято в разделе.


Адрес оперативной памяти (источника) указывается в паре регистров fs:si, а адрес видеопамяти (приемника) — в регистре di. Предварительно устанавливается окно видеопамяти, которому принадлежит адрес первой точки (указанный в di). Регистр es должен содержать код сегмента видеобуфера, хранящийся в переменной vbuff.

Исходный вариант подпрограммы

Для дальнейших рассуждений нам нужен простой вариант подпрограммы, иллюстрирующий последовательность действий при построении строки. Он приведен в примере 7.10.

Пример 7.10. Цикл построения строки в режиме Hi-Color

drawline: mov ax, fs: [si] ; ! ! для True Color — mov eax, fs[si]
mov es:[di], ax ; !! для True Color — mov es:[di], eax
add si, bytppnt переадресация операнда источника
add di, bytppnt переадресация операнда приемника
jne @F переход, если не нуль
call NxtWin установка следующего окна
@@: loop drawline управление повторами цикла
ret возврат из подпрограммы

В комментариях к тексту примера 7.10 показано, как надо изменить две первые команды для того, чтобы подпрограмма drawline могла использоваться при работе в режимах True color.

Вариант со строковой операцией

Команды пересылки целесообразно использовать только в тех случаях, когда переадресацию операнда надо отложить на некоторое время или когда приращение адреса не совпадает с размером операнда.

При простом копировании строк размер операнда совпадает с кодом точки (если он не трехбайтовый) и возможна коррекция адреса сразу после записи точки. Поэтому в примере 7.10 команды пересылки имеет смысл заменить строковой операцией. Одна строковая операция заменяет четыре первые команды — две пересылки и две переадресации операндов. Измененный цикл построения строки приведен в примере 7.11. Его можно использовать в тех случаях, когда код точки занимает 2 или 4 байта.

Пример 7.11. Улучшенный цикл построения строки в режиме Hi-Color

drawline: movs word ptr [di], f s: [si] ; !! movs dword ptr [di], f s: [si]
or di, di ; начало нового сегмента ?
jne @F ; -> нет
call NxtWin ; установка следующего окна
I @@: loop drawline ; управление повторами цикла
ret ; возврат из подпрограммы

Первая команда примера 7.И переменная, способ ее записи для пересылки 32-разрядных кодов (режим True color) показан в комментарии.

Для использования всех преимуществ строковой операции из цикла надо исключить проверку значений адресов, т. е. пересылать строку по частям так, как это делалось в примере 7.7. В зависимости от видеорежима, основные действия в нем выполняли команды rep stosw (режим Hi-Color) или rep stosd (режим True color). При подстановке в текст примера 7. И их надо изменить так, как показано ниже.

rep stosw заменяется командой rep movs word ptr [di], fs:[si]
rep stosd заменяется командой rep movs dword ptr [di], fs:[si]

Мы не будем приводить измененный вариант примера 7.7, а перейдем к описанию универсального способа пересылки.

Универсальная подпрограмма пересылки

Давайте вернемся к постановке вопроса. Задана строка, содержащая N точек, код каждой из них занимает м байтов. Эту строку надо скопировать из одного места памяти в другое. Мы специально употребили выражение "место памяти", поскольку пересылка из оперативной в видеопамять является частным случаем.

При такой формулировке задачи напрашивается очевидный способ ее решения. Надо вычислить количество байтов в строке (L = N * м) и переслать L байтов из одного места памяти в другое. Обратите внимание, в результате умножения мы избавились от размера кода точки и при составлении подпрограммы учитывается только количество пересылаемых байтов.

Частные случаи решения такой задачи обсуждались уже несколько раз. Наиболее подробно был рассмотрен один из них при описании способов ускорения рисования линии в режимах PPG (примеры 3.8—3.10). Остается собрать указанные примеры в одну подпрограмму, включив в нее вычисление количества байтов в строке. Это и сделано в примере 7.12.

Пример 7.12. Универсальный (цикл пересылки) способ построения строк

drawl ine: push dx сохранение содержимого регистра dx
xchg ax, ex обмен содержимого регистров (ах = N)
mul bytppnt dxrax = L =-N * М
xchg CX j 3X обмен содержимого регистров (сх = L)
pop dx восстановление содержимого регистра dx
drawalt : push dx сохранение содержимого регистра dx
mov dx, di копирование адреса в регистр dx
add dx, ex dx = исходный адрес + L
jc @F -> прямая расположена в двух окнах
xor dx, dx остаток в dx равен нулю
@@: sub ex, dx количество байтов в текущем окне
call moveto строим первую часть строки
mov ex, dx сх = оставшееся количество байтов
pop dx восстановление содержимого регистра dx
or di, di адрес в пределах текущего окна ?
jne d exit -> да, строка построена полностью
call NxtWin установка следующего окна
moveto: shr ex, 01 преобразуем байты в слова
jnc @F -> четное число байтов
movs byte ptr [di] , fs:[si] ; пересылка одного байта
@@: shr ex, 01 преобразуем слова в двойные слова
jnc @F -> четное число слов
movs word ptr [di], fs:[si] ; пересылка одного слова
@@: je d exit ; -> пересылать больше нечего
rep movs dword ptr [di], fs:[si] ; основной цикл пересылки
d exit: ret ; возврат из подпрограммы

Выполнение примера 7.12 начинается с вычисления количества байтов в строке. При умножении используются регистры dx и ах, поэтому содержимое dx сохраняется в стеке, а содержимое ах — за счет двухкратного использования команды xchg. Произведение находится в регистрах dx:ax. Мы будем считать, что оно меньше чем 65 536, т. е. dx содержит 0. Команда обмена xchg помешает результат в сх, одновременно восстанавливая исходное состояние ах, а из стека выталкивается исходное содержимое регистра dx.

Если количество байтов в строке известно заранее, то заново вычислять его не имеет смысла. Оно указывается в регистре сх, а для вызова подпрограммы используется вторая точка входа, имеющая имя drawait.

Команда с меткой drawait сохраняет в стеке содержимое регистра dx, затем в него копируется адрес видеопамяти, который увеличивается на размер строки в байтах. Если при сложении произойдет переполнение (установка С-разряда), то команда jc @F исключает очистку dx. В противном случае строка помещается в текущем окне и регистр dx очищается. Затем вычисляется количество выводимых точек, и подпрограмма moveto строит первую часть строки.

После построения первой части строки в регистр сх копируется содержимое dx (остаток строки). Регистр dx освободился и надо восстановить его исходное состояние. Для выбора дальнейших действий проверяется текущий адрес в регистре di, если он отличен от нуля, то построение строки завершено и выполняется команда ret. В противном случае устанавливается следующее окно видеопамяти, и подпрограмма moveto строит остаток линии. После ее выполнения завершится работа основной подпрограммы, т. к. в верхушке стека находится адрес возврата на вызывающий модуль.

В подпрограмме moveto команда rep movs dword ptr [di], f s: [si] является основной, она пересылает группу 32-разрядных слов. Однако количество байтов в строке не обязательно кратно четырем. Поэтому нужна предварительная проверка и пересылка от одного до трех "лишних" байтов, так чтобы остаток был кратен четырем.

Команда, имеющая метку moveto, сдвигает содержимое регистра сх на разряд вправо. Если оно было нечетным, то пересылается первый байт строки, в противном случае jnc @F исключает эту пересылку.

Затем содержимое сх еще раз сдвигается на разряд вправо. Если оно было нечетным, то пересылается слово (два байта строки), в противном случае jnc @F исключает эту пересылку.

В результате выполнения двух сдвигов и пересылки "лишних" байтов в строке остается целое число 32-разрядных слов, количество которых находится в регистре ex. Если оно равно нулю, то произойдет переход на команду ret, в противном случае выполняется микропрограммный цикл копирования 32-разрядных слов. После этого выполняется команда ret.

Вторая точка входа drawait введена для тех случаев, когда известен размер строки в байтах. Приведем несколько примеров таких случаев. При работе в режимах PPG количество байтов совпадает с количеством точек. Если размер строки равен ширине экрана, то ее размер в байтах указывает переменная bperiine и вычислять его нет смысла. При построении рисунка, содержащего большое количество строк, целесообразно один раз вычислить размер строки в байтах, а не повторять одни и те же вычисления при построении каждой строки. Наконец, возможны также случаи, когда количество байтов вычисляется нестандартным способом, например, зависит от определенных условий.

Обсуждение результатов

Описанная подпрограмма не только не зависит от размера кода точек, но и затрачивает минимально возможное время на построение строки, что особенно важно при работе в режимах direct color. Поэтому мы советуем использовать именно ее в тех случаях, когда возможна простая пересылка строк графических объектов.

Подпрограмма может записывать в видеопамять не более чем 65 536 байтов. Это ограничение связано с тем, что источник или преемник расположен в оперативной памяти. Мы исходили из предположения, что он полностью помещается в одном сегменте, и поэтому исключили контроль адресов. Если же это условие нарушено, то в подпрограмму придется добавить контроль адресов и переключение сегментов оперативной памяти. Способ переключения сегментов зависит от того, в какой части оперативной памяти они расположены (см. приложение Б).

Если при работе со строкой выполняется не простое копирование, а более сложные действия, то для сокращения количества проверок адресов видеопамяти можно использовать работу с двумя частями строки. Алгоритм работы с двумя частями приведен в примере 7.6 (подпрограмма Twopart). Для выполнения конкретных действий надо составить подпрограмму baselp, которая в примере 7.6 вызывается для обработки каждой из двух частей строки. Например, такая подпрограмма может считывать код очередной точки, как-то обрабатывать его и возвращать результат на старое место. Вопрос о целесообразности работы с двумя частями строки решается с учетом конкретных особенностей алгоритма работы с графическим объектом.


Содержание раздела