Построение рисунков. Варианты построения строк.
Чтение строки из видеопамяти
Во всех описанных выше подпрограммах производилось копирование содержимого оперативной памяти в видеопамять. На практике сравнительно часто приходится решать обратную задачу, т. е. копировать содержимое видеопамяти в оперативную память. Например, это может понадобиться для сохранения исходного фона перед построением рисунка. При работе с 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, и варианты его дополнительного ускорения, описанные в пояснениях к этому примеру.
Выше подчеркивалось, что если рисунок воспроизводится из файла, то проблема ускорения записи в видеопамять не столь актуальна. Однако если рисунок сохраняется в оперативной памяти или восстанавливается из образа, сохраненного в памяти, то от времени, затрачиваемого на копирование из одного вида памяти в другой, зависит быстродействие вашей задачи. В таком случае имеет смысл увеличивать размер подпрограммы для ускорения ее работы с видеопамятью.
Теперь мы располагаем, хотя и не полным, но вполне достаточным набором подпрограмм и это позволяет перейти к рассмотрению способов построения завершенных рисунков.