20.2. Организация доступа к видеопамяти
Структура видеопамяти проста. Видимые строки экрана хранятся последовательно, выстраиваясь в цепочку от стартового адреса видеопамяти. Единственная сложность в том, что один символ на экране занимает два байта в памяти: первый байт содержит его ASCII-код, а второй — его цветовой атрибут (значение TextAttr для этого символа). Поэтому все четные адреса видеопамяти, начиная с нуля, содержат символы, а нечетные — их цвета.
Доступ к видеопамяти можно организовать, наложив на нее регулярную структуру в виде одномерного массива двухбайтовых элементов (рис. 20.4).
Запись VideoWord содержит два поля: первое соответствует четным адресам видеопамяти и хранит в себе код выводимого символа; а второе поле (нечетные адреса) содержит его цветовой атрибут.
- 475 -
| TYPE
| VideoWord = RECORD { Пара байтов: }
| Symbol : Char; { символ, }
| Attrib : Byte { его цвет. }
| END;
| TYPE
| VideoText = Array [1..50*80] of VideoWord;
| { Максимальный размер экрана - 80x50 }
| { MDA —> 80x25; }
| { CGA --> 80x25 и 40x25; }
| { EGA --> 40x25, 80x25, 40x43 и 80x43; }
| { VGA —> 40x25, 80x25, 40x50 и 80x50. }
| VideoTextPtr = ^VideoText;
| { ссылка на образ экрана в памяти ПЭВМ }
Рис. 20.4
Размер массива VideoText определяется максимальной емкостью экрана 80x50 для адаптера VGA. Все менее емкие режимы вполне уместятся в этой структуре. Непосредственное обращение к видеопамяти производится через динамическую переменную типа «ссылка на структуру VideoText». В процедурах на рис. 20.5 такая переменная обозначена именем VT. Инициализация динамической переменной производится без вызовов процедур New или GetMem. Вместо того ей просто присваивается адрес начала видеопамяти и тем самым устанавливается, что ссылки на элементы структуры VT^ будут соответствовать ячейкам видеопамяти.
Если известно текущее число столбцов M, то преобразование двумерных координат X и Y в номер i элемента массива V^[i] поизводится по формуле
i := М*(Y-1) + X
Используя это соотношение, можно считать или записать пару символ-атрибут» в любое место видеопамяти, задавая координаты символа обычным способом через X и Y.
На рис. 20.5 приводятся две процедуры. Одна из них, FillArea, записывает прямо в видеопамять постоянную пару значений типа VideoWord, тем самым максимально быстро заполняя заданную прямоугольную область на экране. Вторая процедура, ChangeAttr, меняет цвет любой прямоугольной области экрана, задаваемой координатами X1, Y1, X2, Y2. Ее действие таково: сначала в данной координате считывается код находящегося там символа, а после этого
- 476 -
пара «символ-атрибут» с предварительно измененным атрибутом цвета записывается на то же место в структуре видеопамяти.
Процедуры работают во всех текстовых режимах, но не содержат в себе контроля корректности координат области (см. рис. 20.5). Параметр VT в процедурах — это ссылка на начало экранной памяти, на которую наложена структура VideoText. Цвет символов задается параметром Attr так же, как переменная TextAttr.
| USES CRT;
| TYPE
| VideoWord = RECORD { Пара байтов: }
| Symbol : Char; { символ, }
| Attrib : Byte { его цвет. }
| END;
| VideoText = Array [1..50*80] of VideoWord;
| { Максимальный размер экрана - 80x50 символов. }
| VideoTextPtr = ^VideoText;
| { ссылка на образ экрана в памяти ПЭВМ }
| { Процедура закраски области экрана символом Ch }
| PROCEDURE FillArea(VT : VideoTextPtr;X1,Y1,X2,Y2 : Byte;
| Ch : Char; Attr : Byte);
| VAR
| i, j, k : Word; { счетчики циклов и пр. }
| BuffWord : VideoWord; { символ и его атрибут }
| M : Byte absolute 0:$44А; { текущее число столбцов }
| BEGIN
| with BuffWord do begin
| { определение символа }
| Symbol := Ch;
| Attrib := Attr
| end;
| for j:=Y1 to Y2 do begin { Запись в видеопамять }
| k := M*(j-1); { символов Ch в цвете Attr }
| for i:=X1 to X2 do
| VT^[k+i] := BuffWord;
| end; {for j}
| END;
| { Процедура смены цвета прямоугольной области экрана }
| PROCEDURE ChangeAttr( VT : VideoTextPtr; X1,Y1,X2,Y2,
| Attr : Byte );
| VAR
| VW : VideoWord; { один символ и его цвет }
Рис. 20.5
- 477 -
| i, j, k, n : Word; M : Byte absolute 0:$44A; { число столбцов }
| BEGIN
| VW.Attrib := Attr;
| { запись нового цвета }
| for j:=Y1 to Y2 do
| begin { цикл обновления цвета }
| k :=M*(j-1);
| for i:=X1 to X2 do
| begin n :=k+i; { индекс доступа }
| VW.Symbol := VT^[n].Symbol;
| { запоминание символа }
| VT[n] := VW { вывод его в новом цвете}
| end {for i}
| end {for j}
| END;
| {$I get_ptr.inc}
| { функция GetScreenPtr (см. рис. 20.1) }
| VAR V : Pointer; { основной блок примера }
| BEGIN V := GetScreenPtr; { адрес видеопамяти }
| if Seg( V^ )=$8000
| then TextMode( Mono )
| else TextMode( C080);
| Write( 'Нажимайте клавишу ввода ...' );
| ReadLn;
| FillArea( V, 2, 2, 80, 25, #177, White );
| { Весь экран (режим С080) закрашивается сим- }
| { волом 177 белого цвета. }
| ReadLn; FillArea( V, 5, 5, 40, 20, #176, Blue+16*lightGray );
| { Область закрашивается символом 176, цвет - }
| { синий по белому. }
| ReadLn;
| ChangeAttr( V, 5, 5, 40, 20, Yellow+16*Red );
| { Область меняет цвет на желтый по красному. }
| ReadLn;
| ChangeAttr( V, 25, 15, 75, 24, Green+16*Cyan );
| { Область меняет цвет на зеленый по голубому.}
| ReadLn;
| { пауза до нажатия ввода }
| ClrScr { очистка экрана }
| END.
Рис. 20.5 (окончание)
В качестве входного параметра процедуры используют параметр VT, совместимый по типу со значениями типа Pointer. Такой подход предоставляет еще большие возможности. Так, можно передавать адрес различных видеостраниц (если адаптер ПЭВМ их
- 478 -
поддерживает) или даже просто областей динамической памяти. Это позволяет заполнять альтернативные невидимые экраны и после этого копировать их в активную видеопамять командой Move (или переключать страницы), получая эффекты быстрой смены кадров.
Подобные действия с видеопамятью используются при организации накладывающихся окон. Перед созданием окна весь экран или его часть сохраняется (запоминается вся структура или подынтервалы из нее), а после уничтожения окна восстанавливается вновь. Стандартная процедура Window всегда уничтожает изображение «под окном».