(Продолжение, начало см. МК № 46, 51—52, 4, 6—7, 10, 12—13, 16—18, 22, 24, 29, 34 (165, 170—171, 175, 177—178, 181, 183—184, 187—189, 193, 195, 200, 205)). В письмах, пришедших за последнее время на мой адрес — тот, что чуть повыше :-) — читатели просят спрашивают об альтернативе стандартному модулю CRT, ведь на современных процессорах Am5x86, Pentium, Celeron, Pentium III и выше использование его приводит к неработоспособности программы и возникает ошибка выполнения 200.

Работаем с текстовым режимом

Раз уж читатели интересуются модулем CRT, то вероятнее всего, им необходимо, чтобы их программы могли работать с текстовым режимом. На этот случай я могу предложить написать некоторый модуль VESACRT.PAS, который позволил бы работать с шестью текстовыми режимами в экранном разрешении от 80x25 до 132x60 символов и при этом загружать пользовательские масштабируемые растровые шрифты, осуществлять ввод с клавиатуры и работать с мышью.

Что ж, если это вас заинтересовало, начнем.

Что такое VESA и с чем ее едят?

Производители вычислительной техники как-то раз объединились и создали ассоциацию стандартов на видеоэлектронику (Video Electronics Standards Association — VESA). Именно ей было дано право издавать стандарты. Было решено установить минимальный набор особых свойств, которыми должна обладать каждая карта Super VGA. Ассоциация подтолкнула программистов к написанию драйверов для самых распространенных программ, использующих преимущества плат Super VGA любого производителя. Стандарт поддерживается большинством современных SVGA-плат и акселераторов на уровне BIOS-платы.

Я бы с удовольствием рассказал обо всех версиях VBE (VESA BIOS Extension) и их возможностях, но все это невозможно вместить даже в объем двух статей. Гораздо проще скачать необходимую документацию с сайта http://amonit.boom.ru. Здесь же можно раздобыть Norton Guide.

Итак, при работе видеоадаптера в текстовом режиме память, отведенная под видеобуфер, имеет следующую структуру: каждый отображаемый на экране символ представляется парой байт, где четный байт несет информацию об ASCII-коде отображаемого символа, а нечетный байт содержит его цветовую информацию (атрибут). В свою очередь, атрибут состоит из двух частей. Первая расположена в младших четырех битах и содержит код цвета символа 0..15, вторую составляют старшие четыре бита, код цвета фона —0..7, при этом самый старший бит отвечает за мерцание символа (1 = true). Коды цветов соответствуют стандартным константам Black..White Turbo Pascal.

На мой взгляд, целесообразно использование шести текстовых режимов, из которых первый режим является стандартным для базового видео BIOS’а, а пять остальных — для VESA BIOS’а. Теперь можно начать составление модуля с описания типов, которые могут понадобиться в дальнейшем:

unit VesaCrt; interface type TSymbRec = record Symb : char; Attr : byte; end; TScrLine = array [0..131] of TSymbRec; PScrLine = ^TScrLine; TVesaTextMode = record Mode, Width, Height, CharWidth, CharHeight, CharBytes : word; end; TScreenMetrix = record Mode, Width, Height, MinX, MaxX, MinY, MaxY, CharWidth, CharHeight, CharBytes : word; end; TWinRect = record Left, Top, Right, Bottom, Width, Height : word; end; const {--- Colors ---} clBlack = 0; clBlue = 1; clGreen = 2; clCyan = 3; clRed = 4; clMagenta = 5; clBrown = 6; clLightGray = 7; clDarkGray = 8; clLightBlue = 9; clLightGreen = 10; clLightCyan = 11; clLightRed = 12; clLightMagenta = 13; clYellow = 14; clWhite = 15; {--- Screen Modes ---} VesaDefault = 0; Vesa80x25 = VESADefault; Vesa80x60 = 1; Vesa132x25 = 2; Vesa132x43 = 3; Vesa132x50 = 4; Vesa132x60 = 5; Metrix : Array[0..5] of TVesaTextMode = ((mode:$003;Width:80;Height:25;CharWidth:8; CharHeight:8;CharBytes:17), (mode:$108;Width:80;Height:60;CharWidth:8; CharHeight:8;CharBytes:8), (mode:$109;Width:132;Height:25;CharWidth:8; CharHeight:8;CharBytes:17), (mode:$10A;Width:132;Height:43;CharWidth:8; CharHeight:16;CharBytes:10), (mode:$10B;Width:132;Height:50;CharWidth:8; CharHeight:16;CharBytes:9), (mode:$10C;Width:132;Height:60;CharWidth:8; CharHeight:16;CharBytes:9)); ScrAddr = $0b800; var Screen : TScreenMetrix; WinRect : TWinRect; function SetTextMode( num : word ) : boolean; procedure SetCharTable( var CharTable; FirstChar, CharCount : word; BytePerChar : byte); function InstallFont( FileName : string; FirstChar, CharCount, BytePerChar : word) : word; procedure SetWindow( MinX, MinY, MaxX, MaxY : byte ); procedure DefaultWindow; procedure SetCurSize( Start, Finish : byte ); procedure SetCurPos( x, y : byte); procedure CursorHide; procedure CursorShow; procedure ClrScr; procedure FillScr( Symbol : char; TextColor, BackColor : byte ); procedure FillRect( Left, Top, Right, Bottom : byte; Symbol : char; TextColor, BackColor : byte ); procedure TextOut( x, y : byte; s : string ); procedure SetTextColor( color : byte ); function GetTextColor : byte; procedure SetBackColor( color : byte ); function GetBackColor : byte; implementation const TextAttr : byte = clLightGray;

Итак, тип TSymbRec отражает вышеописанную структуру информации о символе на экране. Для удобства доступа к строке символов на экране описываем тип TScrLine. Тип TVesaTextMode описывает параметры видеорежима из таблицы режимов Metrix. Тип TScreenMetrix описывает параметры текущего режима, которые будут храниться в переменной Screen. Тип TWinRect описывает границы текущего окна, которые будут храниться в переменной WinRect. Помимо этого инициализируем атрибут символов TextAttr цветом clLightGray и адрес начала видеобуфера ScrAddr номером сегмента $0b800.

Как видно, кроме всего этого еще описаны константы цветов clBlack..clWhite, которые применимы не только для кодирования цвета символа, но и для цвета фона.

Ну и, наконец, индексы шести текстовых режимов VesaDefault..Vesa132x60, где VesaDefault — стандартный текстовый режим, который установлен по умолчанию и в который необходимо переходить перед завершением программы.

Теперь, используя вызов документированной функции VESA BIOS по переключению видеорежимов, опишем функцию установки текстового режима по индексу:

function SetTextMode( num : word ) : boolean; var Res : boolean; begin Screen.Mode := Metrix[num].Mode; Res := false; asm mov bx,Screen.Mode; mov ax,4F02h; int 10h; cmp ah,0; jnz @err; mov Res,true @err: end; SetTextMode := Res; if Res then begin with Screen do begin Width := Metrix[num].Width; Height := Metrix[num].Height; MaxX := Width-1; MaxY := Height-1; MinX := 0; MinY := 0; CharWidth := Metrix[num].CharWidth; CharHeight := Metrix[num].CharHeight; CharBytes := Metrix[num].CharBytes; end; DefaultWindow; end; end;

Ассемблерный оператор asm..end заносит в регистр BX номер устанавливаемого режима из массива Metrix по указанному индексу num, а в регистр AX — номер функции $4F сервиса VESA и номер подфункции $02 (установка видеорежима), вдобавок, вызовом программного прерывания int 10h активизирует подфункцию установки видеорежима. При успешном вызове в регистре AL появится значение $4F — это значит, что функция поддерживается, — а в регистре AH — значение 0: функция выполнена успешно, т.е. требуемый режим включен. А раз включен, то необходимо проинициализировать переменные Screen и WinRect. Последняя инициализируется процедурой DefaultWindow, о которой я расскажу позднее. Сразу после установки видеорежима координаты и размеры текущего окна совпадают с соответствующими параметрами всего текстового экрана. Пример использования функции SetTextMode:

Uses VesaCRT; begin if not SetTextMode(Vesa80x60) then begin writeln('Error: Bad screen mode'); halt; end; … if SetTextMode(VesaDefault) then; end.

Как переводить видеоадаптер в нужный видеорежим, теперь понятно. Следующее, чему предстоит научиться, — это очищать экран с помощью процедуры ClrScr. Но перед этим опишем универсальную процедуру, заполняющую область с координатами в пределах текущего окна указанным символом и цветом:

procedure FillRect( Left, Top, Right, Bottom : byte; Symbol : char; TextColor, BackColor : byte ); var x, y : word; TextScreen : PScrLine; Attribute: byte; begin Left := Left+WinRect.Left; Right := Right+WinRect.Left; Top := Top+WinRect.Top; Bottom := Bottom+WinRect.Top; if (Left > WinRect.Right) or (Top > WinRect.Bottom) then exit; if Right > WinRect.Right then Right := WinRect.Right; if Bottom > WinRect.Bottom then Bottom := WinRect.Bottom; Attribute := (BackColor shl 4) or (TextColor and $0f); for y := Top to Bottom do begin TextScreen := ptr(ScrAddr, y*Screen.Width*2); for x := Left to Right do with TextScreen^[x] do begin Symb := Symbol; Attr := Attribute; end; end; end;

В переменной TextScreen формируется указатель на начало области памяти видеобуфера, совпадающей с очередной строкой экрана с номером в переменной Y. Выражение y*Screen.Width*2 говорит о том, что мы вынуждены вычислять смещение для нужной строки, умножая ее номер на ширину строки в байтах. Так как в переменной Screen.Width хранится ширина строки экрана в символах, а мы знаем, что каждый символ занимает в видеопамяти 2 байта, то еще раз умножаем на 2. Ну а дальше, как говорится, дело техники. Т.е. в каждую позицию строки заносим символ Symbol и устанавливаем его цвет в TextColor, а цвет фона — в BackColor.

Не могу умолчать о том, что ассемблерный вариант данной процедуры выполняется в два раза быстрее.

procedure ClrScr; Begin FillRect(0, 0, WinRect.Width, WinRect.Height, ' ', clLightGray, clBlack); end;

Данная процедура очищает текущее окно исходя из его параметров, хранящихся в переменной WinRect. Т.е. в каждую позицию строки заносим символ «пробел» и устанавливаем его цвет в clLightGray, цвет фона будет черный.

Теперь рассмотрим долгожданный вывод текста на экран.

procedure TextOut( x, y : byte; s : string); var j, Count : integer; TextScreen : PScrLine; begin x := x + WinRect.Left; y := y + WinRect.Top; if (x>WinRect.Right) or (y>WinRect.Bottom) then exit; TextScreen := ptr(ScrAddr, y*Screen.Width*2); Count := length(s) — 1; if x + Count>WinRect.Right then Count := WinRect.Right — x; for j := 0 to Count do with TextScreen^[x + j] do begin Symb := s[j+1]; Attr := TextAttr; end; end;

Процедура TextOut выводит строку S начиная с позиции X, Y текущего окна. При этом координаты верхнего левого угла —(0,0), а координаты нижнего правого —(WinRect.Width-1,WinRect.Height-1). Строка выводится с атрибутом TextAttr, который следует устанавливать предварительно. Если она окажется настолько длинной, что будет выходить за правый край окна, то прямо под этот край и будет урезана. Это позволит избежать неуместного переноса выводимой строки на следующую строку экрана.

Теперь осталось научиться управлять цветом — и дело в шляпе :-).

Процедура SetTextColor устанавливает цвет символа (см. константы clBlack..clWhite) в переменную TextAttr, которая будет использоваться при очередном выводе текста на экран. При этом информация о цвете фона изменена не будет.

procedure SetTextColor( color : byte ); begin TextAttr := (TextAttr and $f0) or (color and $0f); end;

Следующая функция позволяет узнать, какой цвет символа установлен.

function GetTextColor : byte; begin GetTextColor := TextAttr and $0f; end;

Процедура SetBackColor устанавливает цвет фона символа (можно использовать константы cl???) в переменную TextAttr. При этом информация о цвете символа изменена не будет. Следует, однако, помнить, что для установки фона без мерцания надо указывать константы clBlack..clLightGray, а для включения мерцания — соответственно clDarkGray..clWhite.

procedure SetBackColor( color : byte ); begin TextAttr := (TextAttr and $0f) or (color shl 4); end;

Следующая функция позволяет узнать, какой цвет фона установлен.

function GetBackColor : byte; begin GetBackColor := TextAttr shr 4; end;

Рассмотрим использование этих подпрограмм на примере.

… ClrScr; SetTextColor(clWhite); { строка будет выведена белым цветом } TextOut(0, 1, 'Hello All My Friends!'); SetTextColor(clGreen); { строка будет выведена зеленым цветом } TextOut(0, 2, 'Hello All My Friends!'); SetBackColor(clLightGray); { строка будет выведена зеленым цветом на сером фоне } TextOut(0, 3, 'Hello All My Friends!'); …

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

(Продолжение следует)

Литература:

1. Р. Джордейн. Справочник программиста персональных компьютеров типа IBM PC, XT и AT. — М.: Финансы и статистика, 1992. — 543 с.

2. Диалоговая справочная система Norton Guide.

3. VESA BIOS EXTENSION (VBE) Core Functions Version: 2.0