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

         

Варианты построения строк

Точечные рисунки, независимо от их конкретного содержания, всегда занимают на экране прямоугольную область, а их образы хранятся в файлах в виде последовательности строк одинакового размера. Поэтому построение
рисунка сводится к последовательному построению его строк на экране монитора. Изложение материала мы начнем с нескольких примеров подпрограмм для работы со строками рисунков.

Строки рисунков отличаются от линий геометрических фигур тем, что их образы существуют в оперативной или в видеопамяти. Поэтому в простых случаях надо просто переместить коды точек из одного места памяти в другое. В данной главе нас будут интересовать строки, у которых код точки занимает 1 байт.

Построение строки слева направо

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



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

Пример 3.15. Построение строки 256-цветного рисунка

drawline:

         
drwlinl: push bx сохраняем содержимое bp
mov bx, ex bp = ex (количество точек в строке)
Ipdrwll: lods byte ptr fs: [si] читаем в al код очередного байта
mov ah, al копируем коды из al в ah
mov ex, 08 количество повторов цикла распаковки
outSpnt: xor al, al очищаем регистр al
shl ah, 01 сдвигаем ah на разряд влево
adc al, 00 прибавляем переполнение к al
stosb записываем код очередной точки
or di, di начало нового сегмента ?

jne
@F
;
-> нет, обход команды call NxtWin

call
NxtWin
;
установка следующего окна
@@:
dec
bx

bx = bx - 1

je
drlret

если bx = 0, то строка построена

loop
outSpnt

управление внутренним циклом

jmp
short Ipdrwll

-> на обработку следующего байта
drlret :
pop
bx

восстановление содержимого bx

ret


выход из подпрограммы

Подпрограмма примера 3.18 представляет собой два вложенных цикла. Имя внешнего цикла ipdrwii, а внутреннего — outSpnt.

Внешний цикл считывает очередной байт образа строки, копирует его в регистр ah и задает количество повторов внутреннего цикла.

Во внутреннем цикле производится распаковка очередной группы точек и запись их кодов в видеопамять. Распаковку выполняют три первые команды внутреннего цикла. Первая из них очищает регистр ai, вторая сдвигает содержимое регистра ah на разряд влево. При сдвиге старший разряд регистра ah переносится в С-разряд регистра флагов, поэтому если он содержал единицу, то вырабатывается признак переполнения. Третья команда (adc ai, оо) прибавляет содержимое С-разряда к регистру ai. В результате, в зависимости от кода очередной точки, в регистре ai окажется 0 или 1. Полученный код точки команда stosb записывает в видеопамять. Затем проверяется текущий адрес видеопамяти и при необходимости устанавливается следующее окно видеопамяти.

После записи каждой точки содержимое регистра bx уменьшается на 1 и если оно окажется равным нулю, то происходит переход на метку drlret для завершения подпрограммы. В противном случае команда loop управляет выводом восьми точек. После этого происходит короткий безусловный переход на начало внешнего цикла для обработки следующего байта.

Чтение строки из видеопамяти.

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

В примере 3.19 приведена подпрограмма, выполняющая копирование строки из видеопамяти в оперативную память. При ее вызове адреса задаются
так же, как во всех предыдущих примерах, а именно, пара регистров es:di содержит адрес видеопамяти, а пара fs:si-- адрес оперативной памяти. Предварительно устанавливается окно, в котором расположено начало копируемой строки, а ее размер указывается в регистре сх.

Пример 3.19. Копирование строки из видеопамяти в ОЗУ

readlin: mov al, es:[di] ; чтение очередного байта видеопамяти
mov fs:[si], al ; запись кода точки в ОЗУ
inc si ; увеличение адреса ОЗУ
inc di ; увеличение адреса видеопамяти
jne @F ; -> адрес в пределах окна
call Nxtwin ; переход к следующему окну
@@: loop readlin ; управление внутренним циклом
ret ; выход из подпрограммы

В примере 3.19 использованы обычные команды пересылки, поэтому очередной байт сначала считывается из видеопамяти в регистр al, а затем содержимое ai копируется в оперативную память. После этого содержимое регистров si и di увеличивается на 1 и проверяется значение нового адреса видеопамяти. Если он окажется равным нулю, то выполняется команда call Nxtwin, в результате чего устанавливается следующее окно видеопамяти. Команда loop readlin повторяет выполнение цикла до тех пор, пока не будут скопированы все байты строки.

В рассмотренном варианте подпрограммы, если не происходит смена окна, то при пересылке одного байта выполняется 6 команд. Такое количество вспомогательных действий существенно замедляет пересылку, что будет особенно ощутимо при сохранении больших объемов видеопамяти. Для сокращения вспомогательных действий пересылку нужно выполнять с помощью строковой операции movs.

Улучшение цикла копирования

У операции movs фиксировано назначение индексных регистров di и si и сегментного регистра es. Поэтому для применения строковой операции надо изменить расположение адресов источника и приемника. Пара регистров fs:si должна содержать адрес видеопамяти, а пара es:di — адрес оперативной памяти, но для удобства лучше сохранить единообразный способ расположения адресов и временно изменять его в самой подпрограмме пересылки.

° примере 3.20 показано, как можно переставлять адреса источника и приемника в теле подпрограммы на время выполнения цикла пересылки. При обращении к подпрограмме этого примера регистры es:di, как обычно, Должны содержать адрес видеопамяти, а регистры fs:si — адрес оперативной памяти.

Пример 3.20. Копирование строки из видеопамяти в оперативную память

; Перестановка входных параметров
readlin: push fs ; сохраняем содержимое fs
pop es ; выталкиваем его в es
mov fs, Vbuff ; fs = Vbuff (код видеосегмента)
xchg di, si ; перестановка содержимого di и si
; Цикл копирования строки из видеопамяти в оперативную память
readlp: movs byte ptr [di], fs:[si]; копирование очередного байта
or si, si адрес в пределах сегмента 9
jne @F -> да, обход команды call NxtWin
call NxtWin установка следующего окна
@@: loop readlp управление повторами цикла
Восстановление исх здного расположения входных параметров
push es сохраняем содержимое es
pop fs сохраняем содержимое fs
mov es, Vbuff выталкиваем содержимое fs в es
xchg di, si перестановка содержимого di и si
ret ; возврат из подпрограммы

В примере 3.20 перед выполнением цикла пересылки содержимое регистров fs копируется в регистры es через стек, в fs помещается код видеосегмента (содержимое переменной vbuff) и переставляется содержимое индексных регистров di и si. Так получаются нужные адреса источника и приемника.

Цикл пересылки имеет метку readlp, он отличается от аналогичного цикла примера 3.15 только тем, что вместо команды or di, di используется or si, si, поэтому мы не будем повторять его описание. После пересылки восстанавливается исходное расположение входных параметров в сегментных и индексных регистрах и происходит выход из подпрограммы.

Что дает улучшение цикла

В примере 3.20 цикл readlp содержит на две команды меньше, чем цикл подпрограммы примера 3.19, т. е. количество вспомогательных команд сократилось на третью часть. На первый взгляд, это немного, но появилась возможность дальнейшего ускорения процесса копирования за счет использования микропрограммного цикла пересылки. Для этого применяется способ, показанный в примере 3.16, и варианты его дополнительного ускорения, описанные в пояснениях к этому примеру.

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

Теперь мы располагаем, хотя и не полным, но вполне достаточным набором подпрограмм и это позволяет перейти к рассмотрению способов построения завершенных рисунков.


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