Информационный сервер для программистов: Исходники со всего света. Паскальные исходники со всего света
  Powered by Поисковый сервер Яndex: Найдется ВСЁ!
На Главную Pascal Форум Информер Страны мира
   Графика    >>    vesaoldp
   
 

VESA: стандарт новый, проблемы старые

С. А. Андрианов
МИР ПК #07/98

Cтандарты VESA 1.2 и VBE 2.0, некоторые рекомендации по их использованию.

Прошло пять лет с момента публикации в нашем журнале описания стандарта VESA (VESA - Video Electronics Standards Association), и сейчас, наверное, даже в редакционном архиве трудновато будет отыскать эту статью. В то время большинство эксплуатируемых компьютеров имели 256 Кбайт видеопамяти (для новых машин эта величина уже достигала 512 Кбайт), к тому же поддержка VESA со стороны видеоBIOS у них отсутствовала. Потребности рынка и возможности процессора по обработке видеоданных также не выходили, как правило, за пределы стандартных VGA-режимов - 640x480x16 и 320x200x256 цветов, поэтому приведенные в статье данные вряд ли могли быть использованы тогда при программировании.

Сегодня ситуация несколько изменилась. Почти все работающие компьютеры поддерживают VESA 1.2 на уровне видеоBIOS и имеют не меньше 1 Мбайта видеопамяти, а практически все продаваемые - VESA 2.0 (иногда применяется аббревиатура VBE - VESA BIOS Extension) имеют видеопамять 2-4 Мбайт. Если же программа использует графический режим с числом цветов менее 256 и пространственным разрешением до 640x480, то она будет восприниматься, мягко говоря, как несколько устаревшая. Да и обидно использовать всего 60-150 Кбайт видеопамяти из как минимум 1 Мбайта.

Однако характер различных вопросов, обсуждаемых в эхоконференциях компьютерной сети Fidonet, показывает, что многие начинающие программисты, с одной стороны, испытывают потребность в использовании всех графических возможностей современных компьютеров, а с другой - либо вообще не знают о существовании стандарта VESA, либо представляют себе его лишь в общих чертах. Это нередко вызывает досадные ошибки при программировании, а иногда даже приводит к неверным выводам о неработоспособности конкретного "железа". И хотя сегодня эта тема утратила новизну, информация о VESA сейчас даже нужнее, чем раньше.

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

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

Первые 13h номеров видеорежимов были заданы де-факто стандартом VGA, и диапазон 14h-7Fh использовался фирмами-производителями видеоадаптеров по-разному, поэтому выделить в нем подобласть для введения нового стандарта, не "обижая" никого из разработчиков, оказалось невозможно. В связи с этим VESA решила вынести все вводимые новым стандартом функции, в том числе и установку видеорежима, в новую функцию прерывания 10h. В версии 2.0 на номер видеорежима отводилось уже не восемь, а шестнадцать бит, причем старший бит (теперь уже 15-й), как и раньше, управлял очисткой видеопамяти. Содержимое 8-го бита показывало, принадлежит данный режим стандарту VESA (единица) или нет (ноль). Такой подход позволил работать через интерфейс VESA как с вводимыми стандартом новыми режимами, так и со старыми, в том числе и режимами VGA.

Биты с 9-го по 14-й резервировались стандартом VESA 1.2 для дальнейших расширений, которые не заставили себя долго ждать. Уже в версии 2.0 для управления LFB (Linear/Flat Frame Buffer - кадровый буфер с линейной/прямой адресацией) был отведен 14-й бит. Список рекомендованных видеорежимов приведен в таблице, однако следует заметить, что производители видеоплат не обязаны полностью следовать ему. Номера поддерживаемых видеорежимов можно определить с помощью информационных функций VESA. Кроме того, по версии 2.0 уже не требуется обязательно соблюдать указанные номера видеорежимов.

Таблица 1. Стандартные видеорежимы VESA 1.2
Номер режима Тип Разрешение Число цветов Число бит на цвет
100h Графический 640x400 256 -
101h Графический 640x480 256 -
102h Графический 800x600 16 -
103h Графический 800x600 256 -
104h Графический 1024x768 16 -
105h Графический 1024x768 256 -
106h Графический 1280x1024 16 -
107h Графический 1280x1024 256 -
108h Текстовый 80x60 16 -
109h Текстовый 132x25 16 -
10Ah Текстовый 132x43 16 -
10Bh Текстовый 132x50 16 -
10Ch Текстовый 132x60 16 -
10Dh Графический 320x200 32K (1:5:5:5)
10Eh Графический 320x200 64K (5:6:5)
10Fh Графический 320x200 16.8M (8:8:8)
110h Графический 640x480 32K (1:5:5:5)
111h Графический 640x480 64K (5:6:5)
112h Графический 640x480 16.8M (8:8:8)
113h Графический 800x600 32K (1:5:5:5)
114h Графический 800x600 64K (5:6:5)
115h Графический 800x600 16.8M (8:8:8)
116h Графический 1024x768 32K (1:5:5:5)
117h Графический 1024x768 64K (5:6:5)
118h Графический 1024x768 16.8M (8:8:8)
119h Графический 1280x1024 32K (1:5:5:5)
11Ah Графический 1280x1024 64K (5:6:5)
11Bh Графический 1280x1024 16.8M (8:8:8)


Перед описанием функций VESA необходимо разъяснить некоторые термины. Поскольку объем видеопамяти существенно превосходит размер окна, выделяемого в адресном пространстве видеоадаптеру, его необходимо переключать на различные участки видеопамяти (обычно говорят: "переключать банки видеопамяти"). При этом минимальное расстояние между двумя адресами в видеопамяти, которые могут отображаться на один и тот же адрес в окне, называется granularity (от английского granulate - дробить). Размер окна (или банка) должен быть большим или равным granularity. Если мы разделим абсолютный адрес в видеопамяти на granularity, то частным будет номер банка, а остатком - смещение относительно начала окна. Похожий механизм адресации реализован в процессорах семейства х86, в которых размер одного сегмента (банка) равен 64 Кбайт, но можно изменять его положение в памяти, варьируя содержимое сегментного регистра, т. е. номер банка. Таким образом, granularity для сегментированного адреса процессора равно 16 байт.

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

Все функции VESA доступны через 4Fh функцию 10h прерывания (в регистр AH помещается число 4Fh, а в регистр AL - номер функции). В регистр AX при этом возвращается статус завершения функции, а в AL - 4Fh (обратите внимание, при вызове 4Fh помещается в AH, а вернуться оно должно в AL, если это не так, то стандарт не поддерживается). При успешном завершении функции в AH возвращается ноль, а при неудачном - код ошибки.

В версии 1.2 предусмотрено девять функций (с номерами от 0 до 8), а в версии 2.0 добавлено еще две. Мы рассмотрим только первые девять, поскольку они поддерживаются обеими версиями стандарта.

Функция 0 возвращает информацию о версии VESA и производителе видеоплаты. Перед ее вызовом необходимо выделить в нижней памяти буфер длиной 256 байт. В версии 2.0 функция позволяет получить некоторую дополнительную информацию, если в первые четыре байта выделенного буфера предварительно записать 4-байтную сигнатуру "VBE2". При этом размер буфера должен составлять 512 байт.
 На входе: 
 AX = 4F00h; 
 ES:DI - указатель на буфер, 
        в который надо поместить информацию, 
        в формате сегмент:смещение 
        (обратите внимание: именно сегмент 
        реального режима, а не селектор защищенного). 
 На выходе: 
 AX - статус завершения.


Информационный блок имеет следующую структуру:
 VESAInfoBlock	struc 
 VESASignature	db'VESA'	;VESA сигнатура 
 VESAVersion	dw?	;версия VESA (0100h, 0102h  
 		или 0200h) 
 OemStringPtr	dd?	;указатель на строку  
 		с именем производителя  
 		(заканчивается "0") 
 Capabilities	db 4 dup (?)	;флаги графических  
 		возможностей 
 VideoModePtr	dd?	;указатель на список  
 		видеорежимов 
 TotalMemory	dw?	;размер видеопамяти  
 		в 64-Кбайт блоках; 
 Дополнительно в VBE 2.0 добавлены поля: 
 OemSoftwareRev	dw?	;номер версии реализации VBE  
 OemVendorNamePtr	dd?	;указатель на строку  
 		с именем поставщика 
 OemProductNamePtr	dd?	;указатель на строку  
 		с названием продукта 
 OemProductRevPtr	dd?	;указатель на строку  
 		с версией продукта 
 Reserved	db 222 dup (?)	;зарезервировано;  
 		область расширения блока 
 OemData	db 256 dup (?)	;область данных для строк 
 		производителя 
 VESAInfoBlock ends 


Первые три поля информационного блока пояснений, пожалуй, не требуют, кроме того, что числу 0102h соответствует версия 1.2, а не 1.02.

Capabilities - 32-битное поле, каждый бит которого сообщает о наличии тех или иных особенностей:

D0 = 0 - фиксированная ширина DAC (Digital to Analog Converter - устройство, преобразующее номер цвета в видеопамяти в сигналы интенсивности цветовых составляющих при управлении дисплеем) равна 6 бит на основной цвет;

D0 = 1 - ширина DAC переключается на 8 бит на основной цвет.

В версии 2.0 добавлено:

D1 = 0 - VGA-совместимый контроллер;

D1 = 1 - VGA-совместимость отсутствует;

D2 = 0 - обычные операции с DAC;

D2 = 1 - DAC рекомендуется программировать только во время импульса обратного хода луча;

D3 - D31 - зарезервированы.

VideoModePtr - указатель на список всех видеорежимов, потенциально поддерживаемых данным видеоадаптером. Для некоторых из них это невозможно из-за ограничений на величину видеопамяти или по каким-либо другим причинам. Факт поддержки проверяется функцией 1. Каждый видеорежим в списке представлен словом (два байта), а сам список заканчивается словом 0FFFFh.

Функция 1 возвращает информацию о конкретном видеорежиме. Она выдает расширенную информацию о каждом режиме из списка, возвращаемого функцией 0. Требует выделения 256-байтного блока.
 На входе: 
 AX = 4F01h; 
 CX - номер видеорежима; 
 ES:DI - указатель на буфер, 
     в который надо поместить информацию, 
     в формате сегмент:смещение. 
 На выходе: 
 AX - статус завершения.


Информационный блок имеет следующую структуру:
 ModeInfoBlock	struc	;обязательная информация для 
 		всех версий VESA 
 ModeAttributes	dw?	;атрибуты видеорежима 
 WinAAttributes	db?	;атрибуты окна А 
 WinBAttributes	db?	;атрибуты окна В 
 WinGranularity	dw?	;granularity 
 WinSize	dw?;	размер окна 
 WinASegment	dw?	;начальный сегмент окна A 
 WinBSegment	dw?	;начальный сегмент окна B 
 WinFuncPtr	dd?	;указатель на оконные функции 
 BytesPerScanLine	dw?	;количество байт в строке 
 		растра; обязательная информация 
 		для версии VESA 1.2 и старше 
 Xresolution	dw?	;горизонтальное разрешение 
 		в точках или символах 
 Yresolution	dw?	;вертикальное разрешение 
 		в точках или символах 
 XCharSize	db?	;ширина знакоместа в точках 
 YCharSize	db?	;высота знакоместа в точках 
 NumberOfPlanes	db?	;количество плоскостей  
 		видеопамяти 
 BitsPerPixel	db?	;глубина цвета (бит на точку) 
 NumberOfBanks	db?	;количество банков видеопамяти 
 MemoryModel	db?	;тип модели памяти 
 BankSize	db?	;размер банка в килобайтах 
 NumberOfImagePages	db?	;количество экранных страниц 
 Reserved	db?	;зарезервировано для оконных 
 		функций;поля прямой кодировки 
 		цвета (для видеорежимов 
 		с непосредственным представле- 
 		нием цвета) 
 RedMaskSize	db?	;глубина красного цвета в битах 
 RedFieldPosition	db?	;смещение маски красного цвета 
 GreenMaskSize	db?	;глубина зеленого цвета  
 		в битах 
 GreenFieldPosition	db?	;смещение маски зеленого цвета 
 BlueMaskSize	db?	;глубина синего цвета в битах 
 BlueFieldPosition	db?	;смещение маски синего цвета 
 RsvdMaskSize	db?	;резерв для глубины цвета  
 		в битах 
 RsvdFieldPosition	db?	;резерв для смещения маски 
 		цвета 
 DirectColorModeInfo	db?	;флаги для режимов с непосред- 
 		ственным представлением цвета; 
 		обязательная информация для 
 		версии VBE 2.0 и выше 
 PhysBasePtr	dd?	;физический адрес LFB 
 OffScreenMemOffset	dd?	;смещение свободной части 
 		видеопамяти 
 OffScreenMemSize	dw?	;размер свободной части 
 		видеопамяти в килобайтах 
 Reserved	db 206 	;остаток ModeInfoBlock 
 	dup (?) 
 ModeInfoBlock ends 


ModeAttributes определяет наиболее важные характеристики видеорежима. Имеет следующую структуру:
 D0 = 0 - видеорежим не поддерживается; 
 D0 = 1 - видеорежим поддерживается; 
 D1 = 1 - зарезервировано; 
 D2 = 0 - стандартные функции вывода не поддерживаются BIOS; 
 D2 = 1 - стандартные функции вывода поддерживаются BIOS; 
 D3 = 0 - монохромный режим; 
 D3 = 1 - цветной режим; 
 D4 = 0 - текстовый режим; 
 D4 = 1 - графический режим; 
 D5 = 0 - режим, совместимый с VGA; 
 D5 = 1 - режим, несовместимый с VGA; 
 D6 = 0 - доступен оконный режим, совместимый с VGA; 
 D6 = 1 - оконный режим, совместимый с VGA, недоступен; 
 D7 = 0 - нет LFB; 
 D7 = 1 - LFB доступен; 
 D8-D15 - резерв.


Как уже было сказано, не все видеорежимы, номера которых выдаются функцией 0, могут быть доступны из-за ограничений, связанных с установленным объемом видеопамяти и т. п. Если видеорежим недоступен в данной конфигурации, бит D0 устанавливается в ноль.

Если бит D2 установлен в единицу, то BIOS поддерживает функции установки размера и положения курсора, вывод символа и автоматический скроллинг экрана, если в ноль - не поддерживает.

Установка в единицу бита D5 сигнализирует о том, что видеоадаптер несовместим с VGA на уровне регистров, иными словами, перепрограммировать эти регистры не рекомендуется.

Доступ процессора к видеопамяти возможен в двух режимах: оконном - через окна, на которые указывают WinASegment и WinBSegment; режиме LFB - через одно большое непрерывное окно, расположенное в верхних адресах физической памяти. Видеоадаптер может обеспечивать работу либо в обоих режимах, либо только в одном из них. О доступности оконного режима сигнализирует установка в ноль бита D6, а о доступности режима LFB - установка в единицу бита D7. Доступность режимов можно проверить по табл. 2:

Таблица 2.
Доступные режимы: D7 D6
Оконный (VGA) 0 0
-- 0 1
Оконный и LFB 1 0
LFB 1 1


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

WinAAttributes и WinBAttributes описывают способ доступа процессора к видеопамяти при оконном режиме.
 D0 = 0 - доступно единственное неперемещаемое окно (как в режиме 13h); 
 D0 = 1 - поддерживается механизм переключения окон; 
 D1 = 0 - окно недоступно для чтения; 
 D1 = 1 - окно доступно для чтения; 
 D2 = 0 - окно недоступно для записи; 
 D2 = 1 - окно доступно для записи; 
 D3-D7 - зарезервировано.  


WinGranularity и WinSize показывают granularity и размер окна в килобайтах соответственно.

WinASegment и WinBSegment - сегментные адреса (физические) в адресном пространстве, по которым процессор может получить доступ к соответствующему окну.

WinFunPtr - адрес оконной функции. Поскольку прерывание 10h - одно из самых насыщенных по количеству функций, многие драйверы и резидентные программы перехватывают его. Это значительно замедляет обращение к 10h. Для ускорения процедуры переключения окон в информационном блоке содержится длинный сегментированный адрес процедуры управления ими, зависящий от видеорежима. Обращение к ней аналогично обращению к функции 5 VESA за исключением того, что в версиях стандарта до 2.0 не обеспечивалось возвращения в регистре AX значения статуса завершения.

Xresolution и Yresolution в текстовых режимах указывают размеры в строках и символах, а в графических - в точках растра.

NumberOfPlanes указывает число битовых плоскостей. В 16-цветных режимах оно составляет, как правило, четыре (как в 12h), а во всех остальных - одну.

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

MemoryModel определяет типы организации памяти:
 00h - текстовый режим; 
 01h - графический режим CGA; 
 02h - графический режим Hercules; 
 03h - режим плоскостей; 
 04h - режим индексного представления цвета; 
 05h - 256-цветный режим без сцепления плоскостей; 
 06h - режим непосредственного представления цвета; 
 07h - режим YUV (сигнал яркости и две цветоразностных составляющих);  
 08h-0Fh - зарезервировано для дальнейшего расширения VESA; 
 10h-FFh - отведено для определения производителями.  


NumberOfImagePages - доступное в данном режиме количество экранных страниц, уменьшенное на единицу, т. е. при доступных восьми страницах в этом поле будет записано число семь.

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

RedMaskSize, GreenMaskSize, BlueMaskSize и RsvdMaskSize определяют глубину цвета в битах для каждой из цветовых составляющих. Например, в режиме прямого кодирования 64 Кбайт цветов 5:6:5 эти величины будут иметь значения 5, 6, 5 и 0 соответственно. При кодировании в режиме YUV цветовые координаты будут следовать в таком порядке: V, Y, U.

RedFieldPosition, GreenFieldPosition, BlueFieldPosition и RsvdFieldPosition - смещения в битах младшего разряда соответствующего цвета от младшего бита последовательности, отводимой на точку. Таким образом, чтобы расставить цвета "по своим местам", каждый из них надо сдвинуть влево на соответствующее число разрядов. Например, для режима 5:6:5 эти величины будут равны 11, 5, 0 и 0, а для 5:5:5-10, 5, 0 и 15.

DirectColorModeInfo содержит флаги основных характеристик режимов с непосредственным кодированием цвета. В настоящее время используются два бита:
 D0 = 0 - цветовой клин фиксирован; 
 D0 = 1 - цветовой клин можно перепрограммировать; 
 D1 = 0 - биты в полях Rsvd зарезервированы; 
 D1 = 1 - биты в полях Rsvd могут использоваться приложением.


Видеоадаптер CGA содержал всего 16 Кбайт видеопамяти, но в адресном пространстве компьютера IBM PC было зарезервировано на эти цели 128 Кбайт. Однако с появлением VGA стало ясно, что такой размер окна явно недостаточен. Уже в эпоху господства 286-х процессоров у некоторых разработчиков возникла идея увеличить размер окна видеоадаптера, переместив его в область старших адресов - тогда это был 16-й мегабайт адресного пространства. Сегодня он явно попадает в область обычной оперативной памяти, но в 386-м процессоре, в отличие от его предшественников, адресное пространство было значительно увеличено, поэтому его запаса должно было хватать для того, чтобы в течение некоторого времени участки обычной оперативной памяти и видеопамяти не пересекались. Версия 2.0 ввела информационную поддержку для режима LFB, а также способ перевода в него видеоадаптера, когда он не совмещается с оконным.

PhysBasePtr - физический адрес начала области в адресном пространстве процессора, на которую происходит отображение видеопамяти одним нефрагментированным блоком (режим LFB). Если эта переменная равна нулю, то данный режим адресации не поддерживается. Интересно, что будет еще через 10-12 лет, когда объем оперативной памяти среднестатистического компьютера превысит 2 Гбайт?

OffScreenMemOffset - переменная, содержащая смещение на начало свободного участка видеопамяти, доступного для приложений, относительно начала LFB. Этот участок можно использовать для вертикального скроллинга изображения или организации нескольких страниц видеопамяти с помощью функции 7. Следует отметить, что эта переменная трактуется некоторыми разработчиками не как смещение, а как абсолютный физический адрес.

OffScreenMemSize - размер свободного участка видеопамяти в килобайтах.

Функция 2 устанавливает видеорежим.
 На входе: 
 AX = 4F02h; 
 BX - номер видеорежима и ряд флагов: 
 	D0-D8 - номер режима; 
 	D9-D13 - зарезервировано (должно быть 0); 
 	D14 = 0 - использовать оконный режим; 
 		= 1 - использовать режим LFB; 
 	D15 = 0 - очищать видеопамять; 
 		= 1 - не очищать видеопамять.  
 На выходе: 
 AX - статус завершения.  


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

При установке в ноль старшего бита очищается не вся видеопамять, а только та ее область, которая отображается на экран, в графических режимах в память записываются нули, в текстовых - 20h 07h. При установленном в ноль 8-м бите (для стандартных VGA-режимов) роль индикатора, сообщающего о необходимости очистки видеопамяти, играет 7-й бит.

Если видеорежим поддерживается, но недоступен режим адресации, заданный 14-м битом, то функция возвращает код ошибки, равный 2.

Функция 3 возвращает текущий видеорежим.
На входе:
AX = 4F03h.
На выходе:
BX - номер текущего видеорежима, включая флаги. 


Функция 4 сохраняет/восстанавливает состояние. Она является развитием функции 1Ch прерывания 10h. Ее введение вызвано необходимостью сохранять и восстанавливать состояние расширенных регистров.
 На входе: 
 AX = 4F04h; 
 DL = 0 - возвратить длину буфера сохранения; 
    = 1 - сохранить состояние; 
    = 2 - восстановить состояние; 
 CX - характеристика состояния: 
 D0 - состояние оборудования контроллера; 
 D1 - состояние данных BIOS; 
 D2 - состояние регистров DAC; 
 D3 - состояние расширенных регистров; 
 ES:BX - указатель на буфер при ненулевом DL.  
 На выходе: 
 AX - статус завершения; 
 BX - длина буфера для сохранения состояния
      в 64-байтных блоках (при DL = 0).


Функция, естественно, не сохраняет содержимое видеопамяти.

Функция 5 управляет экранным окном. Она позволяет переместить окно, через которое процессор обращается к различным участкам видеопамяти, а также узнать, в котором положении установлено текущее окно. Для повышения производительности эта функция может быть вызвана напрямую по адресу, возвращаемому функцией 1.
 На входе:	AX = 4F05h; 
 BH = 0 - функция установки положения окна; 
 	= 1 - функция запроса положения окна; 
 BL = 0 - окно А; 
 	= 1 - окно В; 
 DX - положение окна в видеопамяти 
 	(при BH = 0).  
 На выходе: 
 AX - статус завершения; 
 DX - положение окна (при BH = 1). 


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

Функция 6 управляет логической длиной строки. Она позволяет изменить логическую длину строки, что может понадобиться для реализации горизонтального скроллинга, а также узнать текущую длину строки. Сам горизонтальный скроллинг, так же как вертикальный, управляется функцией 7.
 На входе: 
 AX = 4F06h; 
 BL = 0 - установить длину строки (в точках растра); 
 	= 1 - возвратить длину строки; 
 	= 2 - установить длину строки (в байтах); 
 	= 3 - возвратить максимальную длину строки; 
 CX - длина строки в указанных в BL единицах (при установке). 
 На выходе: 
 AX - статус завершения; 
 BX - количество байт в строке; 
 CX - длина строки в точках растра; 
 DX - максимальный номер строки растра.


Функции BL = 2 и BL = 3 были добавлены в версию 2.0.

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

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

Функция 7 управляет положением экранного окна в видеопамяти. Она устанавливает, какой адрес видеопамяти будет отображаться в точку в верхнем левом углу, а также позволяет переключать экранные страницы и осуществлять вертикальный скроллинг, а при превышении логической ширины экрана над отображаемой - горизонтальный.
 На входе: 
 AX = 4F07h;  
 BX = 0 - установить положение экранного окна; 
 	= 1 - возвратить положение экранного окна; 
 	= 80h - производить установку во время импульса вертикального обратного хода; 
 CX - номер первой отображаемой точки в строке (при установке); 
 DX - номер первой отображаемой строки растра (при установке). 
 На выходе: 
 AX - статус завершения; 
 CX - номер первой отображаемой точки в строке (при запросе); 
 DX - номер первой отображаемой строки растра (при запросе).  


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

Функция 8 управляет форматом DAC палитры. Она позволяет управлять разрядностью цвета в регистрах палитры, если это поддерживается аппаратно (см. информационный блок поле Capabilities). По умолчанию в этих регистрах на каждый цвет отводится по шесть бит.
 На входе: 
 AX = 4F08h; 
 BL = 0 - установить разрядность DAC палитры; 
 	= 1 - возвратить разрядность DAC палитры; 
 BH - требуемое число разрядов на основной цвет  
 (при установке). 
 На выходе: 
 AX - статус завершения; 
 BH - количество разрядов, приходящееся на основной цвет. 


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

Об остальных функциях VESA, поддерживаемых только версией 2.0, а также об особенностях использования функций этого стандарта в 32-битном защищенном режиме будет рассказано в следующей статье.

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

Работа программы разбита на несколько этапов, иллюстрирующих различные возможности. Для перехода к очередному этапу выполнения необходимо нажать на клавишу. Программа последовательно выполняет следующее:

- запрашивает общую информацию VESA, выводит некоторые данные на экран и производит опрос параметров для видеорежима 800x600x256 цветов, который затем будет установлен. Номер видеорежима выбирается таким, чтобы он мог быть установлен даже на видеоадаптере с видеопамятью 512 Кбайт. В этом случае, правда, трудно рассчитывать на панорамирование и скроллинг изображения. Впрочем, сложности с этим возникают и на некоторых мегабайтных видеокартах (в частности, на микросхеме S3);

- переключает видеоадаптер в графический режим и заполняет каждый банк видеопамяти своим цветом, для чего производятся два обращения к процедуре заполнения памяти, так как наиболее часто встречаемое granularity, равное 64 Кбайт, превосходит максимальную длину области, заполняемой процедурой FillChar;

- выводит на экран многоцветную наклонную полосу с помощью процедуры рисования точки, образец которой помещен в основной программный модуль;

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

- рисует многоцветную полосу заново с учетом новой геометрии экрана;

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

Следует отметить, что для максимального сокращения объема текста в программе отсутствуют анализ успешного завершения функций, проверка доступности окна А на запись и т. п.


Андрианов Сергей Андреевич - к.т.н., e-mail: andriano@divo.ru, Fidonet: 2:50/435.40

Некоторые советы по программированию



  • Стандарт VESA допускает, что может отсутствовать поддержка таких функций, как рисование точки и вывод символа или строки текста, которая подтверждается установкой в "1" бита D2 поля ModeInfoBlock.

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

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

  • Версия 2.0 не только не ввела новых номеров видеорежимов, но и фактически отменила саму идею стандартизации номеров, поэтому единственным надежным способом установки нужного режима является перебор всех доступных на данной карте номеров с запросом их параметров. Собственно, для разрешения выше 1280x1024 (например, 1600x1200) - это единственный способ выбора режима.

  • Иногда поле OffScreenMemOffset содержит не смещение относительно начала буфера, а полный физический адрес (в частности, этим грешат изделия Matrox), поэтому перед использованием следует выяснить, что это за величина. Можно либо проверять старший бит этого поля, либо сравнивать его значение с величиной PhysBasePtr.

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

  • Общий вывод следующий: все необходимые параметры следует проверять, так как не нужно надеяться, что у видеоплаты, с которой будет работать программа, параметры будут "стандартными" и что разработчики учли все требования стандарта при создании своих изделий. (Примечание. Программа, приведенная в качестве примера (Листинги 1, 2), этим рекомендациям не удовлетворяет, что обусловлено ограниченным объемом журнальной публикации.)


    Листинг 1
    program tves_as1; {проверка модуля vesa_as}
    
    uses vesa_as,crt;
    
    var
       LenLineP:word;  {длина строки растра в точках}
       LenLineB:word;  {длина строки растра в байтах}
       MaxLines:word;  {максимальное число строк растра}
       
    procedure putpixel(X,Y,Color:word); {вывод точки на экран}
    var
       bank,offs:word;
    begin
       asm
          xor bx,bx
          mov ax,Y
          mul LenLineB
          add ax,X
          adc dx,bx
          mov bank,dx    {предполагаем (не проверяя), что granularity = 64K}
          mov offs,ax
       end;
       SetVESABank(bank,0);   {вывод осуществляется в окно А}
       Mem[$A000:offs]:=lo(Color);
    end;   
    
    Procedure WaitRetrace;  {ожидание вертикального обратного хода луча}
    Begin
       While(Port[$3DA]and $08)=0 do;
    End;
    
    var
       i,j:word;          {переменные цикла, что ж еще?}
       b1:VesaInfoBlock;  {информационный блок VESA (для 0-й функции)}
       b2:ModeInfoBlock;  {информационный блок видеорежима (для 1-й функции)}
       NBanks:word;       {общее количество банков видеопамяти}
    begin
    {выясняем наличие VESA и выводим основные параметры}
       if GetVesaInfo(@b1) then begin
          for i := 0 to 3 do write(b1.VesaSignature[i]);
          write(', Ver ',hi(b1.VESAVersion),'.',lo(b1.VESAVersion));
          writeln(', ',b1.TotalMemory*64,'Kb videomemory on board ');
          i := 0;
          while b1.OEMStringPtr^[i] <> #0 do begin
             write(b1.OEMStringPtr^[i]);
             inc(i);
          end;
          writeln;
          i := 0;
          writeln('Modes:');
          while b1.VideoModePtr^[i] <> $FFFF do begin
             write(b1.VideoModePtr^[i],' '); {список видеорежимов}
             inc(i);
          end;
          writeln;
       end else writeln('Error');
    {получаем характеристики одного из видеорежимов}
       if GetModeInfo($103,@b2) then begin 
          writeln('Mode 103h, Granularity:',b2.WinGranularity,'Kb, Window Size:',
              b2.winsize,'Kb, ',
              b2.XResolution,'x',
              b2.YResolution,', ',
              b2.BitsPerPixel,
              ' bits per pixel');
          NBanks := (b1.TotalMemory * 64) div b2.WinGranularity;
       end else writeln('Error');
       readkey;
    {устанавливаем видеорежим, характеристики которого мы получили}
       if SetVesaMode($103) then begin
          LenLineB := b2.BytesPerScanLine;
    {закрашиваем каждый из банков своим цветом}
          for i := 0 to NBanks-1 do begin
             SetVESABank(i,0);
             fillchar(mem[$a000:0],b2.WinGranularity * 512,char(i+1));
             fillchar
                (mem[$a000:
                            b2.WinGranularity * 512],
                            b2.WinGranularity * 512,char(i+1));
                   {предполагаем, что сегмент окна А - 0А000h}
          end;
       end else writeln('Error');
       readkey;
    {поточечно рисуем диагональную многоцветную полосу}
       for i := 0 to 199 do
          for j := 0 to 599 do PutPixel(j+i,j,j);
       readkey;   
       LenLineP := 1024;
    {пытаемся изменить логическую длину строки}
       SetVESALenLine(LenLineP,LenLineB,MaxLines);
       readkey;
    {снова рисуем полосу}
       for i := 0 to 199 do
          for j := 0 to 1023 do PutPixel(j+i,j,j);
       readkey;
    {производим панорамирование ...}
       for i := 0 to (1023-800) do begin
          SetVESAStart(i,0);
          WaitRetrace;
       end;
    {... и скроллирование экрана}
       for j := 0 to (1023-600) do begin
          SetVESAStart(i,j);
          WaitRetrace;
       end;
       readkey;
    {возвращаем видеоадаптер в текстовый режим}
       asm
          mov ax,3
          int $10
       end;
    end.


    Листинг 2
    unit vesa_as; {сервис VESA вариант Borland Pascal Real Mode}
    Interface
    
    type
       VesaInfoBlock = record
    ................................{структуру см. в тексте}
       END;
       ModeInfoBlock = record
    ................................{структуру см. в тексте}
       END;
    
    function GetVESAInfo(Buffer:pointer):boolean;
       {функция 0, информация о VESA}
    function GetModeInfo(Mode:word;Buffer:pointer):boolean;
       {функция 1, информация о режиме}
    function SetVESAMode(Mode:word):boolean;
       {функция 2, установка видеорежима}
    function SetVESABank(Bank,Window:word):boolean;
       {функция 5, установка банка}
    function SetVESALenLine(var PLength,BLength,NLines:word):boolean; 
       {функция 6, установка логической длины линии растра}
    function SetVESAStart(XStart,YStart:word):boolean; 
       {функция 7, управление положением экранного окна в видеопамяти}
    
    Implementation
    
    function GetVESAInfo(Buffer:pointer):boolean; {информация о VESA}
    var
       RetCode : word;
    begin
       asm
          mov ax,$4f00
          les di,Buffer
          int $10
          mov RetCode,ax
       end;
       GetVESAInfo := RetCode = $004F; 
    end;
    
    function GetModeInfo(Mode:word;Buffer:pointer):boolean;
     {информация о режиме}
    var
       RetCode : word;
    begin
       asm
          mov ax,$4f01
          mov cx,Mode
          les di,Buffer
          int $10
          mov RetCode,ax
       end;
       GetModeInfo := RetCode = $004F; 
    end;
    
    function SetVESAMode(Mode:word):boolean;
    var RetCode:word;
    begin
       asm
          mov ax,$4f02
          mov bx,mode
          int $10
          mov RetCode,ax
       end;
       SetVESAMode := RetCode = $004f;
    end;
    
    const NumBank : integer = -1;
    
    function SetVESABank(Bank,Window:word):boolean;
    var RetCode:word;
    begin
       if Bank = NumBank then
          SetVESABank := TRUE
       else begin
          asm
             mov ax,$4f05
             mov bx,Window
             mov dx,Bank
             int $10
             mov RetCode,ax
          end;
          if RetCode = $004f then begin
             SetVESABank := TRUE;
             NumBank := Bank;
          end
          else SetVESABank := FALSE;
       end;
    end;
    
    function SetVESALenLine(var PLength,BLength,NLines:word):boolean;
    var RetCode:word;
    begin
       asm
          mov ax,$4f06
          les di,Plength
          mov cx,[di]
          xor bx,bx
          int $10
          les di,Plength
          mov [di],cx
          les di,Blength
          mov [di],bx
          les di,Nlines
          mov [di],dx
          mov RetCode,ax
       end;
       SetVESALenLine := RetCode = $004f;
    end;
    
    function SetVESAStart(XStart,YStart:word):boolean;
    var RetCode:word;
    begin
       asm
          mov ax,$4f07
          xor bx,bx
          mov cx,XStart
          mov dx,YStart
          int $10
          mov RetCode,ax
       end;
       SetVESAStart := RetCode = $004f;
    end;
    
    end.