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 уже не требуется обязательно соблюдать указанные номера видеорежимов.
Перед описанием функций 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:
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 Некоторые советы по программированиюModeAttributes. Если они поддерживаются, то становятся доступными по стандартным номерам прерываний, а если нет - программа не должна ничего выводить с помощью стандартных функций BIOS, даже текст сообщения об ошибке. В случае, когда такой текст приходится выводить, может возникнуть ошибка, и тогда программа должна либо предусматривать предварительное переключение в видеорежим, в котором функции BIOS доступны, либо выводить сообщения на экран методом прямого отображения в видеопамять. Другими словами, в языках высокого уровня при использовании режимов VESA необходимо перехватывать и самостоятельно обрабатывать сообщения об ошибках. Листинг 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. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||