В истории вычислительной техники развитие программных и аппаратных средств тесно переплетено друг с другом. Новые технические возможности поззволяют программистам сделать очередной шаг вперед и при этом у них просто появляются новые требования к аппаратуре. Основное влияние на расширение функций видеокарт оказало стремление получить на экране монитора объемное движущееся изображение, построенное с учетом перспективы и распределения света и тени. Пионерами этого направления были разработчики компьютерных игр.
Нулевое слово информационного массива, возвращаемого по запросу 4FOih, содержит характеристики видеорежима, которые называются атрибутами. Каждый разряд этого слова имеет конкретное назначение, кроме первого, который зарезервирован. Стандарты VBE 1.2 и 2.0 описывают только назначение разрядов младшего байта этого слова (его адpec в массиве 0), а старший байт зарезервирован. В табл. 1.3 показано, что обозначает 1 в каждом из разрядов.
Набор выполняемых функций зависит от конкретного назначения акселератора. По личному опыту вы знаете, что при рисовании и черчении приходится иметь дело с различными объектами и с разными способами их изображения. Соответственно, функции акселераторов, предназначенных для ускорения геометрических построений и для работы с художественными изображениями, различаются весьма существенно. Некую комбинацию из этих функций могут поддерживать акселераторы, предназначенные для систем автоматизации проектирования. Специфический набор функций поддерживают акселераторы, используемые для игровых приложений. В этих случаях основными требованиями являются быстрота смены картинки и возможность создания различных спецэффектов, а точность построения самого изображения не столь существенна.
На сегодняшний день невозможно выделить некий стандартный набор функций, выполняемых акселераторами. У разработчиков отсутствует достаточный практический опыт, поэтому они просто воплощают функции, поддерживаемые графическими библиотеками Direct3D и OpenGL. К ним относятся раскрашивание треугольников по заданным образцам (наложение текстур), альфа смешение (см. раздел 7.6), создание эффекта тумана (см. раздел 7.6), вычисление распределения света и тени по методу Гуро (Gouraud Shading), коррекция перспективы и некоторые другие.
Кроме того, у акселераторов появилась функция иного назначения. Это преобразование телевизионного изображения в компьютерное и обратно. В таких случаях на видеокарте имеется разъем для подключения телевизора или видеомагнитофона. В качестве примера можно привести изделия фирм ATI и S3. Подчеркнем, что речь идет не о приеме телевизионных сигналов — для этого существуют специальные платы, а об аппаратном преобразовании сигналов из телевизионного стандарта (NTSC, PAL и т) в последовательность кодов точек, записываемых в видеопамять. Такое преобразование является двухсторонним, т. е. коды хранящихся в видеопамяти точек могут преобразовываться в один из телевизионных стандартов. Такие функции расширяют возможности компьютерной обработки телевизионных изображений и делают ее более доступной для пользователей.
Прежде всего, видеорежимы делятся на текстовые и графические. В зависимости от типа режима прикладная задача записывает в видеопамять или коды символов в стандарте ASCII, или коды отдельных точек графического объекта. При работе в графических режимах видеоконтроллер просто выводит на экран точки, коды которых хранятся в видеопамяти. При работе в текстовых режимах он, по кодам символов, выбирает их изображения из специальных таблиц, а затем выводит точки изображений на экран.
Другой важной характеристикой является разрешающая способность. В зависимости от типа видеорежима она измеряется количеством символов или точек, которое можно разместить по горизонтали и вертикали в пределах рабочей области экрана. Количество точек является основной, а количество символов — производной единицей, т. к. оно зависит от первой величины и от размеров ячейки (знакоместа), отведенной для размещения одного символа.
Точки, расположенные по горизонтали, образуют строку, а по вертикали — столбец (в документации на BIOS используются термины row (ряд) и column (столбец). Количество точек в строке и в столбце не может быть произвольным, оно всегда кратно восьми. Максимально возможное количество точек в строке зависит от разрешающей способности монитора и его геометрических размеров. У современных мониторов минимальное расстояние между центрами смежных точек составляет от 0,28 до 0,26 мм. При размере экрана 14 дюймов по диагонали количество точек в строке не превышает 1024. У 15-дюймовых мониторов оно достигает значения 1280. Однако возможность работы в режимах с высоком разрешением зависит еще и от видеокарты, о чем будет сказано ниже.
Расстояние между соседними точками, расположенными по горизонтали и вертикали, подбирается одинаковым, для того чтобы изображение квадрата на экране выглядело как квадрат, а не как прямоугольник. Обычно количество точек по горизонтали больше, чем по вертикали, но существуют мониторы и с вертикальной ориентацией страницы.
Рабочая область никогда не заполняет всю видимую часть экрана. Во всех видеорежимах ее окружает пространство, которое в документации называется overscan или border (граница, кайма). Поэтому в разных режимах геометрические размеры рабочей области могут не совпадать.
Важной характеристикой видеорежимов является количество цветов, которое можно одновременно отобразить на экране. Во всех графических режимах цвет получается в результате совмещения в одной точке экрана трех базовых цветов (красного, зеленого и синего) разной интенсивности. В зависимости от видеорежима коды базовых цветов располагаются либо в специальных регистрах видеокарты, либо в видеопамяти, т. е. непосредственно в коде точки. Первую категорию режимов принято называть packed pixel graphics (упакованная точечная графика), а вторую- direct color (непосредственный цвет). Вторая категория, в свою очередь, делится на режимы Hi-Color и True Color. В любом случае от видеорежима зависят размер кода точки и размеры кодов базовых цветов.
Видеосистемы и стандарт VESA Персональный компьютер ( далее ПК или PC) не был бы таковым при отсутствии внешних устройств. К ним относятся различные клавиатуры, "мыши", джойстики, принтеры, сканеры, модемы, звуковые карты, накопители на гибких, жестких, оптических и прочих дисках и, конечно же, мониторы. Пожалуй, наиболее важным из всех внешних устройств является оперативная память, поскольку без нее процессор просто не работоспособен. Вообще, внешним является любое устройство, не входящее в состав процессора (точнее микропроцессора). Процессор не может непосредственно управлять работой внешнего устройства. Для этого нужен посредник — контроллер, который участвует в обмене данными между процессором и устройством и выполняет специфические действия, зависящие от особенностей устройства. Обычно контроллер обслуживает одно устройство. Исключением является контроллер ввода-вывода, обслуживающий все дисководы, а также порты параллельного и последовательного интерфейсов, к которым подключаются принтеры, мыши, джойстики и некоторые другие устройства. Нас будут интересовать видеоконтроллеры, к которым подключаются мониторы. Большинство из них выполнено в виде отдельной платы, но в последние годы наметилась тенденция выпуска материнских плат с расположенными на ней ("интегрированными") видеоконтроллерами. Преимущество отдельных плат в том, что их всегда можно заменить другими с лучшими параметрами. Для поддержки работы с любым контроллером требуется специальное программное обеспечение. Обычно оно записано на гибких или лазерных дисках, прилагаемых к контроллеру, или входит в комплект операционной системы, например Windows 9Х (95, 98, ME) и 2000. В любом случае при вводе нового устройства в эксплуатацию производится установка соответствующего программного обеспечения. |
Авторы стандарта VESA стремились не только облегчить работу программистов, но и не ограничивать разработчиков в выборе способов улучшения характеристик видеокарт. Компромиссным решением было включение в состав BIOS специальных информационных блоков с основными данными о видеокарте. В частности, они содержат список и характеристики всех поддерживаемых видеорежимов. В данном разделе описана структура основных информационных блоков и способ доступа к ним прикладных задач.
Код используется не только при установке видеорежима, но и во многих других случаях. Поэтому, со времен IBM, он хранится в специальном байте оперативной памяти, расположенном в области данных BIOS с абсолютным адресом 0449h. Старший разряд этого байта имеет специальное назначение, поэтому для записи кода видеорежима остается 7 разрядов и он может изменяться от о до 7Fh. Первые 20 значений (от о до 13h) отведены для кодов режимов IBM. Использование остальных значений кодов ничем не регламентировано.
Значения кодов видеорежимов, соответствующих стандарту VESA, изменяются от100 до 11Bh. Такие числа не могут быть записаны в байт и, тем более, в его семь разрядов. Поэтому разработчики видеокарт по своему усмотрению заменяют 9-разрядные коды VESA 7-разрядными кодами OEM. Original Equipment Manufacturer (OEM) в дословном переводе означает "изготовитель оригинального оборудования", в нашем случае — изготовитель видеокарты, которая может собираться из микросхем и деталей других фирм. После установки режимов VESA в байте с адресом 0449h хранятся коды OEM, а в состав BIOS входит специальная таблица их соответствия режимам VESA. Коды OEM уникальны для каждой модели и могут не совпадать даже у видеокарт одного семейства.
По своему усмотрению разработчики видеокарт могут вводить дополнительные режимы, отличающиеся по характеристикам от режимов VESA и IBM. Например, у акселераторов фирм ATI и S3 добавлены режимы с разрешением 320x400, 400x300 и 512x384 точек. Можно предположить, что их удобно использовать при работе с кадрами телевизионных изображений. В соответствии с требованиями VESA коды и характеристики дополнительных режимов должны быть указаны в информационных блоках, хранящихся в области BIOS. Структура этих блоков и способ доступа к ним описаны в следующем разделе данной главы.
Набор режимов, введенный в VBE 1.2, VESA больше никогда не изменяла. В настоящее время выпускаются 20-дюймовые мониторы и видеокарты с объемом памяти 4 Мбайт и более. Это позволяет вводить новые видеорежимы с разрешением 1600x1200 точек. Такие видеорежимы поддерживают, например, видеокарты фирм S3 (VIRGE) и Matrox, в обоих случаях объем видеопамяти составляет 4 Мбайт. При этом код режима packed pixel graphics с разрешением 1600x1200 точек в одном случае I20h, а в другом -- ись. Кроме того, карты Matrox поддерживают режимы Hi-color с указанным разрешением.
Из всего сказанного следует, что работу с новой для вас видеокартой надо начинать с получения исчерпывающей информации о кодах поддерживаемых видеорежимов и их характеристиках. Для этого имеет смысл составить простую программу, которая будет выводить на экран (а лучше в файл) все нужные данные. Способ определения этих данных не сложен, он описан в следующем разделе данной главы.
Защищенный режим отличается от реального тем, что не только данные, но и адреса содержат 32 разряда. Соответственно, размер адресуемого в командах пространства составляет 4 294 967 296 байтов или 4 Гбайт (4 биллиона байтов). У современных ПК реальный объем оперативной памяти намного меньше указанной величины, любой адрес оперативной памяти не только помещается в 32-разрядном регистре, но и не заполняет весь регистр, часть старших разрядов остается свободной.
Оперативная память занимает младшую часть всего пространства адресов, поэтому BIOS и область ввода-вывода перенесены в его старшую часть. Самые старшие адреса отведены для BIOS, например, при перезагрузке ПК происходит обращение к адресу FFFFFFF0II.
Видеопамять, как и оперативная, образует линейное пространство адресов, которое в документации VESA называется FFB (Flat Frame Buffer) или LFB
(Linear Frame Buffer). В Защищенном режиме возможен произвольный доступ к любым адресам видеопамяти без использования механизма переключения окон. Физический (абсолютный) адрес начала LFB хранится в поле 28h (см. табл. 1.2), он не зависит от видеорежима. В качестве примера в табл. 1.2 приведено значение FS000000H.
Содержимое следующих двух полей (2Ch и 3оh) зависит от видеорежима. Эти поля заполнены не у всех видеокарт, а в VBE з.о вообще объявлены резерв-
ными потому, что их содержимое можно легко вычислить. В поле 2Ch должна храниться сумма адреса начала LFB и размера рабочей области памяти. В поле зоь должна находиться разность между общим объемом видеопамяти и размером ее рабочей области.
Замечание 1
Замечание 1
Напомним, что размер рабочей области вычисляется как произведение величин, указанных в полях 10h (размер строки в байтах) и I4h (количество строк) (см. табл. 1.2).
физический адрес нельзя использовать для работы с видеопамятью, он должен быть предварительно преобразован в линейный адрес. Способ такого преобразования и сегментный регистр, указываемый при работе с LFB, зависят от используемого задачей распределения памяти. Например, если применяется простая линейная (гладкая — flat) модель, то физический адрес просто уменьшается на базовый адрес области данных, а для доступа к любым адресам используется сегментный регистр DS.
Для того чтобы видеоконтроллер поддерживал работу с LFB при установке видеорежима (функция 4F02), в регистре bx кроме указания кода режима надо установить 14-й разряд, например, bx = 4ioih для установки режима lOih и разрешения работы с LFB.
В большинстве случаев после разрешения работы с LFB исключается возможность работы с окнами видеопамяти. При этом запросы функции 4F05h отвергаются и в регистре ah возвращается код ошибки 3. Но в литературе встречаются сведения о существовании видеокарт, без уточнения их названия, одновременно допускающих оба способа работы с видеопамятью.
Следует заметить, что поддержка LFB является самым существенным нововведением VBE 2.0. При работе с LFB исключается необходимость контроля адресов видеопамяти в задачах для определения моментов, когда надо изменять текущее окно. Это значительно упрощает и ускоряет манипуляции с графическими объектами.
Акселератор (accelerator, ускоритель) является специализированным вычислительным устройством, предназначенным для ускорения процесса построения или преобразования графических изображений. В отличие от обычных видеокарт акселератор получает от процессора не завершенный образ изображения, а более общую и сжатую информацию, на основании которой он, а не процессор, вычисляет точечный образ рисунка и записывает его в видеопамять.
Первые модели акселераторов выпускались в виде отдельных плат, которые обрабатывали данные, поступающие от процессора, и передавали их обычной видеокарте. Но очень скоро вычислительный микропроцессор начали
располагать на плате видеокарты, а затем его и видеоконтроллер объединили в одну большую микросхему (чип). Акселераторы могут предназначаться для ускорения работы с двухмерными или трехмерными графическими объектами. В названии первых присутствует обозначение 2D, а в названии вторых — 3D, где D является первой буквой слова direction — направление.
Акселераторы могут быть рассчитаны на установку в гнездо шины PCI или в специализированный разъем AGP, который появился на материнских платах ПК после выпуска микропроцессора Pentium II. Разъем AGP имеет 128-разрядную шину данных, что существенно ускоряет процесс обмена между центральным процессором и видеокартой.
При вычислениях используется видеопамять, расположенная на плате акселератора, поэтому ее объем всегда больше того, который нужен для работы видеокарты в обычном режиме. В настоящее время в продаже имеются акселераторы AGP с объемом видеопамяти 32 и 64 Мбайт.
Современные модели акселераторов чаще всего собираются на базе специализированного графического процессора, выполненного в виде большой интегральной микросхемы (чипа), которые выпускают более десятка различных фирм. Корпорация Intel выпустила свой чип i740, но пока он не получил широкого распространения. По данным агентства Mercury Research на сентябрь 1998 года, в первую пятерку производителей графических чипов входят: ATI, S3, Cirrus Logis, Silicon Integrated System (SIS) и Trident Microsystems. Кроме основных чипов эти фирмы выпускают комплекты сопутствующих микросхем и полностью завершенную продукцию, т. е. видеокарты и акселераторы. Возглавляющая список ATI Technologies производит 27% всех графических чипов. Поэтому вероятность приобрести акселератор, собранный из ее комплектующих, весьма велика.
Акселераторы существенно различаются по цене, но возможности дешевых моделей ограничены. Обычно у них разрешающая способность не превышает 800x600 точек, а из полноцветных видеорежимов поддерживается только Hi-coior. Однако при использовании акселератора в качестве обычной видеокарты эти ограничения отсутствуют.
Основными элементами компьютерных видеосистем являются мониторы и видеокарты. Кроме того, к ним относятся графические ускорители и платы для работы с телевизионным изображением, которые используются в зависимости от особенностей решаемых задач.
Монитор или дисплей является одним из важнейших узлов современного персонального компьютера, предназначенным для визуализации (т. е. для представления в виде, доступном для человеческого глаза) выводимой информации. За время существования ПК было выпущено достаточно много мониторов различных типов. По степени улучшения технических характеристик их можно расположить в такой последовательности: Color Graphics Adapter (CGA), Enhanced Graphics Adapter (EGA), Video Graphics Array (VGA), Super Video Graphics Array (SVGA). Этот перечень не претендует на полноту, но каждая из названных моделей получила, в свое время, наибольшее распространение. Мониторы CGA, EGA в настоящее время безнадежно устарели и не выпускаются промышленностью, однако некоторые из них еще находятся в эксплуатации.
Для получения изображения на экране монитора требуется выполнение специальных действий, поэтому неотъемлемой частью монитора является встроенное в него устройство управления. Это еще не контроллер, речь идет о совершенно других функциях, поэтому в англоязычной литературе для названия таких устройств используется термин unit. Именно благодаря встроенному устройству управления все современные мониторы (VGA и SVGA) подключаются к стандартному разъему компьютера и поддерживают стандартный аппаратный интерфейс, т. е. определенную последовательность управляющих сигналов, их полярность, уровень и форму. Поэтому мониторы выпускаются и продаются как отдельные функциональные устройства без контроллера (так же, как, например, принтеры).
Для получения изображения в современных настольных мониторах используется электронно-лучевая трубка (кинескоп). Модели таких мониторов различаются по размеру экрана, разрешающей способности, используемому кинескопу, способам настройки (простые или с цифровым управлением). Эти характеристики определяют качество изображения и стоимость изделия, но никак не влияют на интерфейс с контроллером и способы программирования.
В переносных и портативных компьютерах применяются мониторы, в которых изображение получается на матрице из жидких кристаллов. На сегодняшний день качество такого изображения хуже, чем у мониторов с обычным кинескопом. Однако поиски новых принципов получения изображения на экране идут весьма активно и, возможно, в ближайшем будущем, будут достигнуты большие успехи в этом направлении.
Как любое внешнее устройство монитор не может быть подключен к ПК без специального контроллера, являющегося посредником между ним и центральным процессором (ЦП). Эти контроллеры легко отличить от других плат, поскольку на них расположен специальный разъем для подключения монитора. При описании такого типа контроллеров используют термины "видеоконтроллер", "видеокарта", "видеоплата", реже "видеоадаптер".
Video Electronics Standards Association (ассоциация стандартизации видеоэлектроники), сокращенно VESA, была основана в 1989 году. В августе того же года она опубликовала свой первый стандарт для 16-цветного видеорежима SVGA с разрешением 800x600 точек. С тех пор ассоциация выпустила множество различных стандартов, охватывающих широкий спектр видеооборудования. Одной из ее известных разработок является стандарт на системную шину VLB (VESA Local BUS) для микропроцессора Intel 486. Однако, как уже говорилось, эта шина не прижилась.
Если у вас есть доступ к сети Internet, то подробные сведения об ассоциации VESA и ее продукции можно найти на серверах www.vesa.org и ftp.vesa.org.
Нас будут интересовать стандарты VESA, регламентирующие способы программирования видеокарт. Первый завершенный стандарт появился в октябре 1991 года, он определял полный набор видеорежимов SVGA и дополнительных функций BIOS И Назывался VESA BIOS Extension (VBE) version 1.2.
Это функции той части BIOS, которая расположена на видеокартах и обслуживает видеосервис. Стандарт объединил предыдущие версии VBE 1.0 и VBE 1.1. Ему соответствуют практически все видеокарты, изготовленные начиная с 1992 года. Более современные видеокарты поддерживают версию VBE 2.0, которая совместима (сверху вниз) с версией VBE 1.2. Поэтому учет рекомендаций VESA при программировании работы с графикой позволяет создавать переносимые задачи, которые будут правильно работать независимо от модели видеокарты, установленной на конкретном компьютере.
Сразу после установки видеорежима на экране отображается содержимое младшей части памяти. Будем называть ее рабочей или отображаемой областью. Размер рабочей области зависит от характеристик режима и равен произведению размера строки в байтах на количество строк, помещающихся на экране.
В процессе выполнения задачи может возникнуть необходимость перемещения рабочей области в другой участок видеопамяти. Например, для просмотра отдельных частей большого рисунка или текста, который полностью не помещается на экране. В англоязычной литературе в таких случаях используется специальный термин scrolling — прокрутка, перемещение, просмотр. Механизм прокрутки используют многие приложения для Windows, oн реализуется в виде горизонтального и вертикального лифтов.
Для реализации механизма прокрутки, в первую очередь должна существовать возможность размещения в видеопамяти большого изображения. Высота изображения (количество строк) может быть произвольной, лишь бы хватило видеопамяти. Но ширина ограничена величиной, которая в документации называется logical scan line. В процессе отображения видеопамяти контроллер, отсчитав указанное в ней количество байтов, начинает выводить следующую строку на экране. Если ширина рисунка больше чем scan line, то продолжение текущей строки окажется на экране в следующей строке и изображение будет искажено. Чтобы это не произошло, надо установить значение scan line равным ширине рисунка.
В табл. 1.2 значение scan line расположено в поле 10h, оно равно произведению количества точек в строке (поле I2h табл. 1.2) на размер кода точки в байтах. В поле I9h табл. 1.2 указано количество разрядов в коде точки. Количество байтов определяется делением количества разрядов на 8.
Функция 4F06h Get/Set Logical Scan Line Length позволяет прочитать или
изменить логический размер строки, т. е. определить или изменить адрес видеопамяти, начиная с которого контроллер выводит новую строку на экран.
Перед обращением к BIOS в регистр ы помещается код подфункции: 0 для установки (записи) и 1 для чтения логического размера строки, который указывается или возвращается в регистре сх в виде количества точек.
В обоих случаях после исполнения функции в регистрах находятся следующие величины: bx — количество байтов в строке, сх — количество точек в строке, dx — максимально возможное количество строк указанного размера. Процедура BIOS вычисляет содержимое dx путем деления объема памяти, установленной на видеокарте, на размер строки в байтах. На практике содержимое dx используется крайне редко.
Начиная с VBE 2.0, введены еще два варианта запроса функции 4F06h. Код 2 в регистре ы отличается от кода 0 только тем, что в регистре сх указывается размер строки в байтах, а не в точках. Код 3 позволяет определить максимально возможный логический размер строки для установленного видеорежима. Процедура BIOS вычисляет его исходя из характеристик режима и установленного на видеокарте объема памяти.
Проверка трех видеокарт, соответствующих VBE 2.0, показала, что BIOS видеокарты VIRGE /DX /GX содержит ошибку и при указании кода 3 в регистре ы функция 4F06h выдает совершенную чушь. При кодах 0, 1 и 2 функция выполняется правильно.
Предположим, что при работе в видеорежиме с разрешением 640x480 точек установлен логический размер строки 736 точек. В таком случае при обработке каждой строки видеоконтроллер выводит на экран первые 640 точек, а остальные 96 просто пропускает. На экране будет видна левая верхняя часть изображения размером 640x480 точек. Для того чтобы увидеть его остальную часть, надо переместить рабочую область видеопамяти.
функция 4F07h Get/Set Display start устанавливает или читает координаты левого верхнего угла видимой области видеопамяти, выраженные в виде номеров строки и столбца.
Перед обращением к BIOS в регистре bl указывается 0 для установки новых значений координат или 1 -- для чтения ранее установленных (текущих) координат. Для установки в регистре сх указывается номер столбца, а в регистре dx — номер строки (номера строк и столбцов начинаются с нуля).
При чтении в сх и dx возвращаются текущие значения указанных величин.
Начиная с VBE 2.0, появилась возможность синхронизировать установку новых значений координат рабочей области с моментом обратного хода луча. Для этого в регистре ы указывается код 80h.
Таким образом, функция 4F06h позволяет создать условия для прокрутки в горизонтальном направлении, а функция 4F07h выполняет указанную прокрутку.
Основные (вычислительные) функции акселераторов выполняются в 32-разрядном (защищенном) режиме работы ПК. Речь идет о разрядности адресов, данные могут содержать меньшее или большее количество разрядов. Выполнение прикладных задач в защищенном режиме поддерживают, например, Windows 9X/2000/NT и OS/2. DOS является операционной системой реального (16-разрядного) режима, но существуют так называемые расширители (DOS extenders), которые подключаются к прикладной задаче и создают на время ее выполнения вычислительную среду, необходимую для работы в защищенном режиме. Наиболее известными из них являются DOS4GW, DOS32A, PMODE/W.
При выборе операционной системы приходится учитывать тот факт, что в настоящее время не существует стандарта на программирование функций акселераторов, хотя ассоциация VESA предпринимает активные усилия по его разработке. Первый документ VBE/AF Standard 1.0 был выпущен в августе 1996 года. В настоящее время опубликована третья редакция этого документа, но в ее первых строках подчеркивается, что она содержит черновые предложения и не более того.
Отсутствие стандартов означает, что непосредственно взаимодействующая с акселератором задача не будет переносимой. Она будет выполняться только на тех ПК, на которых установлена соответствующая модель акселератора. Существует довольно много компьютерных игр, созданных для определенных моделей акселераторов, в остальных случаях они либо вообще не работают, либо работают медленно, если вычисления выполняет процессор.
Проблема переносимости частично решается с помощью драйверов, которые продаются вместе с акселератором. Они составлены для определенной операционной системы и рассчитаны на взаимодействие с одной из распространенных графических библиотек.
Среда Windows позволяет создавать любые графические приложения. Разработчикам доступны графические библиотеки Direct3D и OpenGL, которые хорошо документированы и общедоступны. Если установлен соответствующий драйвер, то они используют возможности акселератора, в противном случае требуемые действия выполняются программно, что замедляет процесс выполнения задач, но решает проблему их переносимости.
Пакет Direct3D разработан Microsoft и является одной из частей библиотеки DirectX, входящей в состав Windows 9X, начиная с версии 98, Windows NT и 2000. Он предназначен для ускорения выполнения игровых задач в среде Windows. Первая версия пакета была выпущена в 1996 году.
Библиотека OpenGL была создана в 1993 году фирмой Silicon Graphics для компьютеров совершенно другого класса и для иной операционной системы. В 1995 году совместно с Microsoft она адаптировала ее для IBM PC. С этой библиотекой работает, например, последняя версия компилятора Фортрана для Windows.
В отличие от Direct3D, OpenGL более гибкая и многофункциональная библиотека. Изначально она создавалась для применения трехмерной графики в системах автоматизированного проектирования. Однако она не содержит средств, позволяющих работать непосредственно с видеопамятью в обход интерфейса графических устройств (GDI), который существенно замедляет выполнение прикладных задач. Для этого нужна дополнительная библиотека WinG или DirectDraw, которая является частью библиотеки DirectX.
Еще совсем недавно мысль о том, что можно создать хорошее трехмерное графическое приложение под Windows казалась совершенно нелепой любому программисту, имеющему дело с этой системой. С появлением библиотеки DirectX ситуация изменилась в лучшую сторону. Но, тем не менее, среда Windows остается весьма инерционной, и разработчики компьютерных игр продолжают и, вероятно, еще долго будут продолжать использовать для ускорения процесса игры расширитель DOS4GW и ему подобные.
После включения ПК и загрузки операционной системы, неважно какой, акселератор работает как обычная видеокарта. Такой режим необходим для нормальной работы операционных систем и многих прикладных задач.
В режимах packed pixel graphics коды цветов точек хранятся в регистрах палитры (их 256). Эти регистры недоступны обычным командам. Для записи или чтения их содержимого необходимо обращение к внутренним регистрам видеокарты.
В VBE 1.2 отсутствует специальная функция, выполняющая чтение или изменение содержимого регистров DAC палитры. Прикладные задачи могут использовать для этой цели стандартные функции IBM VGA, примеры работы с ними описаны в главе 4 данной книги.
VBE 2.0 такая функция введена. В отличие от функций IBM VGA она использует другой формат палитры, позволяет изменять содержимое блока регистров DAC во время обратного хода луча и поддерживает работу с дополнительным набором регистров палитры, если таковой имеется.
Функция 4F09h Get/Set Block DAC Registers позволяет сохранить или изменить текущую палитру цветов (содержимое блока регистров DAC). Код выполняемого действия указывается в регистре bl. При сохранении блока регистров bl=1, а при записи bl=о. В регистре сх задается количество сохраняемых или изменяемых регистров (размер блока), а в регистре dx — номер первого сохраняемого регистра (0 — 255).
Для размещения палитры в оперативной памяти надо выделить массив размером 4*N байтов, где N — размер блока, указанный в регистре сх (напомним, что он не может быть больше чем 256). Содержимое каждого регистра занимает 4 подряд расположенных байта, в первых трех находятся коды синего, зеленого и красного цветов, а четвертый очищен. Полный адрес массива задается в регистрах es-.di (es — сегмент, di — смещение).
Такое расположение базовых цветов принято в палитре формата BMP (см. приложение А данной книги). Отметим, что в формате BMP код базового цвета занимает 8 разрядов, а у большинства видеокарт он составляет 6 разрядов. Поэтому при использовании данной функции может потребоваться преобразование хранящихся в файле кодов базовых цветов. Подробнее об этом сказано в главе 4 данной книги.
Некоторые модели видеокарт содержат дополнительную группу регистров палитры. Для работы с дополнительной группой в регистре ы указываются коды 2 или 3 (вместо 0 или 1). Если дополнительная палитра отсутствует, то при возврате из BIOS в регистре ah находится код ошибки 2.
В некоторых моделях видеокарт содержимое регистров палитры можно изменять только во время обратного хода луча, в противном случае на экране появляются помехи ("снег"). Признаком такой особенности видеокарты является установка бита 2 в поле capabilities (см. описание функции 4F00h). В таком случае вместо кода 0 в регистре ы указывается 8оь.
Интерфейс защищенного режима. Большинство функций BIOS, в том числе и функций VEE, независимо от версии, рассчитано на выполнение в реальном (16-разрядном) режиме работы микропроцессора. Если задача выполняется в защищенном (32-разрядном) режиме, то для обращения к функциям BIOS необходим временный переход в реальный режим работы микропроцессора. Это увеличивает количество вспомогательных действий при вызове функций BIOS и замедляет процесс их выполнения. Замедление становится ощутимым, если функции вызываются часто.
Разработчики VBE 2.0 предусмотрели возможность непосредственного вызова процедур, дублирующих функции 4F05h, 4F07h и 4F09h, но рассчитанных на выполнение в защищенном режиме. Прикладная задача определяет адреса точек входа в указанные процедуры следующим способом.
Функция 4FOAh Return VBE 2.0 Protected Mode Interface Возвращает адрес массива, содержащего указанные выше процедуры и некоторые дополнительные данные. Перед ее вызовом надо очистить регистр bl. После исполнения запроса в регистры возвращаются следующие величины:
es — сегмент массива, расположенного в области BIOS в формате для реального режима (чаще всего код соооh); di — адрес (смещение) начала массива в этом сегменте; сх — размер массива в байтах.Первые три слова массива es: [di+o], es-. [di+2] и es: [di+4] содержат адреса (смещения относительно начала массива) точек входа в процедуры, дублирующие функции 4F05h, 4F07h и 4F09h для защищенного режима.
Процедуры полностью перемещаемые, они могут выполняться как непосредственно в ROM BIOS, так и в оперативной памяти, разумеется, после предварительного копирования, для чего и нужен размер массива, возвращаемый в регистре сх.
Указанные процедуры должны вызываться как ближние, т. е. без смены сегментного регистра (см. приложение В данной книги). Если задача использует простую линейную модель памяти, то доступ к области BIOS происходит без смены сегментного регистра и нет необходимости копировать процедуры в оперативную память. Если же пространство адресов сегментировано, то процедуры надо скопировать в сегмент кодов. В таком случае их вызов будет происходить без смены сегментного регистра.
Фактически процедуры не являются полными аналогами функций, выполняемых в реальном режиме. Имеются следующие различия:
Аналог 4F05h поддерживает работу только с одним окном д. Аналог 4F07h лишь устанавливает новое начало отображаемого участка видеопамяти, причем вместо номера строки и столбца при вызове указывается полный (32-разрядный) адрес начала отображаемой области. Его старшая часть помещается в регистр dx, а младшая — в сх. Аналог 4F09h поддерживает только основной набор регистров DAC.Кроме перечисленных функций описываемый массив может содержать перечень номеров портов видеокарты и адресов, которые задача может использовать для ввода и вывода данных. Если такой список присутствует, то его смещение относительно начала массива указано в слове es: [di+6].
Если это слово очищено, то списка в массиве нет. Для большинства программистов этот список не представляет интереса, поскольку не известно назначение указанных в нем портов и адресов. Стандарт VBE 2.0 оговаривает только способ их хранения в таблице, но не назначение.
Замечание 2
Замечание 2
Целесообразность введения функции 4FOAh не очевидна, поэтому в версии VBE 3.0 она не относится к числу обязательных.
Программирование работы в защищенном режиме в данной книге не рассматривается. Если вас интересует этот вопрос, то советуем прочитать статьи Андрианова С. А. в журналах "Мир ПК", в них приведены простые примеры работы с описанными функциями. При наличии доступа
К Сети Internet подшивки этих журналов можно найти на www.opensystems.ru.
На видеокартах имеется 256 регистров DAC, в которых хранятся коды базовых цветов. Они применяются только при работе в видеорежимах packed pixel graphics и не используются в режимах direct color. Базовых цветов три — красный, зеленый и синий. В соответствии со стандартом IBM VGA код базового цвета занимает 6 двоичных разрядов. У некоторых современных видеокарт, например Matrox, появилась возможность увеличения размера кода до 8 разрядов. В таком случае в поле capabilities будет установлен нулевой разряд (см, описание функции 4F00h).
Функция 4F08h Get/set DAC Palette Control предназначена для определения или изменения размера кода базовых цветов, хранящихся в регистрах цвета. Для установки нового размера регистр ы очищается (код 0), а в регистр bh записывается нужный размер кода базового цвета в битах. Для чтения установленного размера базовых цветов в регистр ы записывается код 1, а текущий размер возвращается в регистре bh.
Если видеокарта не позволяет изменить размер кода, то при попытке установки указанная в bh величина заменяется на 6.
Таков полный перечень функций VBE 1.2. Возможно, вы обратили внимание на отсутствие в нем функций, выполняющих запись кодов точек в видеопамять или их чтение. Такие функции просто не нужны, поскольку возможны непосредственные запись и чтение содержимого байтов, слов или двойных слов видеопамяти, т. е. в ней могут находиться операнды команд.
В расположенной на видеокарте BIOS сохранились функции записи и чтения кодов точек во всех графических стандартах IBM. Их вызов осуществляется
через прерывание int 10h, коды запросов ось для записи точки и ODh для чтения. Эти функции нужны только в тех случаях, когда при работе с видеопамятью используются внутренние регистры видеокарты. Уже с появлением режима VGA IBM необходимость в их использовании при программировании отпала.
В заключение следует отметить, что последующие версии VBE совместимы сверху вниз с VBE 1.2, поэтому описанные функции выполняют все современные видеокарты. Вероятность массового выпуска видеокарт, рассчитанных только на 32-разрядный режим работы, маловероятна из соображений совместимости.
Конструктивное исполнение видеокарт соответствует определенным техническим стандартам. Каждая из них способна взаимодействовать только с конкретным типом системной шины. Внешним признаком этого является форма разъема (гнезда) материнской платы, в которое устанавливается видеокарта. Системная шина расположена на материнской плате и представляет собой совокупность проводящих линий, по которым передаются данные, адреса и управляющие сигналы. От нее зависит такая важная характеристика, как скорость передачи данных, а следовательно, и время, затрачиваемое на построение изображения на экране.
На материнских платах компьютеров, собранных на базе процессоров Intel 80286 и Intel 80386 применялась шина ISA (industry standard Architecture), при использовании которой обмен данными между видеокартой и процессором производится словами или байтами. Для процессора Intel 486 была разработана новая системная шина VLB (VESA Local BUS), но ее очень скоро вытеснила шина PCI (Peripheral Component Interconnect). Обе шины позволяли передавать данные двойными словами. Большинство имеющихся в продаже современных видеокарт выполнено в стандарте PCI. С выпуском процессоров Pentium ll на системной плате появилась специальная 128-разрядная шина для обмена данными с видеокартой. Она заканчивается гнездом AGP (Accelerated Graphics Port). В Настоящее Время видеокарты, выполненные в стандарте AGP, преобладают на компьютерном рынке.
Для каждого типа шин выпускались и продолжают выпускаться не только видеокарты, но и платы различного назначения. Поэтому на материнских платах обычно имеются разъемы для установки карт, выполненных в стандартах ISA и PCI. Например, одна из современных материнских плат фирмы Intel (ее тип AL 440 LX) содержит 2 разъема ISA, 4 разъема PCI и 1 разъем AGP. Вполне возможно, что на современном ПК с процессором Pentium вы обнаружите видеокарту, выполненную в стандарте PCI и даже в устаревшем стандарте ISA.
Описанные различия видеокарт учитываются при их программировании лишь в особых случаях, когда требуется максимальная производительность видеосистемы. Большинство прикладных задач в этом не нуждается. Поэтому для нас более важно знать тип монитора, для обслуживания которого предназначена видеокарта, поскольку от этого зависят основные особенности ее программирования и структура графических задач.
Имеется в виду сохранение текущего содержимого регистров цвета видеокарты (DAC) и некоторых величин, хранящихся в области данных BIOS. Напомним, что регистры DAC используются только при работе В режимах packed pixel graphics.
Функция 4F04h Save/Restore Video State выполняет Копирование вышеназванных званных величин в указанный массив или, наоборот, из указанного массива в регистры DAC и в область данных BIOS. Кроме того, она позволяет определить размер массива, необходимый для размещения сохраняемых величин. Перед вызовом функции заполняется несколько регистров, каких — это зависит от запрашиваемого действия.
Обязательно заполняются регистры сх и dx. В регистре сх используются 4 младших разряда, установка каждого из которых определяет группу сохраняемых или восстанавливаемых величин:
бит 0 — характеристики оборудования из области данных BIOS; бит 1 — характеристики видеорежима из области данных BIOS; бит 2 — содержимое регистров DAC; бит 3 — содержимое регистров.В регистре dx указывается код запрашиваемого действия:
0 — определить размер буфера для размещения сохраняемых величин; 1 — сохранить состояние; 2 — восстановить ранее сохраненное состояние. Если регистр dx очищен, то регистры их и еs не заполняются. После исполнения запроса в их находится количество байтов памяти, которое надо выделить для сохранения указанных в сх групп величин. Теперь можно выбрать расположение массива в памяти и запросить сохранение состояния.
Если в dx задан код 1 или 2, то в паре регистров es:bx указывается полный адрес массива, в котором надо сохранить или из которого нужно восстановить ранее сохраненные величины.
В соответствии со стандартом VGA IBM функция lch прерывания int 10h имеет аналогичное название и выполняет аналогичные действия. В описании VBE дана ссылка на эту функцию и разработчики видеокарт приняли ее как установку к действию. Анализ восстановленного текста BIOS у нескольких видеокарт с версиями VBE 1.2 и VBE 2.0 показал, что выполнение функции 4F04h сводится к проверке кода, указанного в dx. Если он больше чем 2, то запрос отвергается, в противном случае происходит обращение к функции ich. Никакие другие действия не выполняются.
В этой связи возникает вопрос: каким действиям соответствует установка бита 3 регистра сх? Дело в том, что функция ich сохраняет только фрагменты из области данных BIOS и содержимое регистров DAC. Никакие другие величины не сохраняются и не восстанавливаются.
Возможно, что разработчики видеокарт не придали указанному факту должного значения по той причине, что функция 4F04h редко используется при программировании, без нее можно обойтись.
Переключение окон видеопамяти. Окна видеопамяти используются при работе микропроцессора в реальном режиме. В этом случае командам доступно пространство адресов размером не более 64 Кбайт. У современных карт объем видеопамяти намного (в десятки раз) превосходит указанную величину. Поэтому видеопамять делится на одинаковые сегменты размером 64 Кбайт, которые принято называть окнами.
Для доступа к видеопамяти выделяется видеобуфер (или видеосегмент). Это пространство адресов размером 64 Кбайт, в графических режимах его адрес (код) обычно, но не обязательно, равен АОООЬ, а в текстовых вбооь. Код видеосегмента это не более чем признак. Обнаружив его, видеоконтроллер записывает данные в текущее окно видеопамяти или считывает их оттуда.
Текущий номер окна хранится в одном из регистров видеоконтроллера и является частью абсолютного адреса видеопамяти. BIOS поддерживает работу с этим регистром.
Функция 4F05h CPU Video Memory Control читает или изменяет номер теку-
щего окна видеопамяти. Наличие этой функции позволяет задачам работать со всем пространством видеопамяти.
Перед вызовом функции 4F05h в регистре bх указываются номер окна и запрашиваемое действие. Нуль в регистре bh (старший байт регистра bx) означает установку нового окна с номером, указанным в dx. Единица в регистре bh означает чтение номера текущего окна, он возвращается в регистре dx. В главе 2 подробно описана техника переключения окон и формат, в котором указываются их номера.
Стандартом VESA предусмотрена возможность существования у видеокарты двух окон (А и в). Нуль в регистре bl (младшем байте регистра bх) соответствует окну А, а единица — в. Почти все исследованные автором видеокарты поддерживали работу только с окном А. Исключением являются видеокарты фирмы ATI Technologies, у которых окно А доступно только для записи данных в видеопамять, а окно в — только для чтения.
Программная реализация функции 4F05h такова, что основные действия выполняет отдельная процедура (подпрограмма), к которой задача может обращаться непосредственно, т. е. минуя прерывание int 10h. Функция 4FOih размещает адрес этой подпрограммы в выходном массиве в двойном слове со смещением ось (см. табл. 1.2). Стандарт VESA рекомендует прямое обращение к подпрограмме вместо использования функции 4F05h.
Понятие "видеорежим" является обобщенной характеристикой текущего состояния видеоконтроллера. Основная функция видеоконтроллера состоит в отображении содержимого видеопамяти на экране монитора. Выполнение этой функции зависит от множества величин, хранящихся во внутренних регистрах видеоконтроллера. Значения этих величин определяются при установке видеорежима. Нас интересуют те из них, которые не только влияют на работу видеокарты, но и должны учитываться в прикладных задачах.
После неудачи со стандартом VGA IBM прекратила работы по стандартизации видеооборудования. А поскольку никто этим не занимался, то наступил период "разброда и шатаний". Каждая фирма проектировала платы по своему усмотрению, не заботясь о каком-либо общем стандарте, кроме собственного. В результате было выпущено много хороших, но не совместимых друг с другом видеокарт, поддерживающих видеорежимы с более высоким, по сравнению с VGA, геометрическим и цветовым разрешением. Коды и характеристики режимов существенно различались и программы, рассчитанные на работу с одной видеокартой, не могли работать с другими или, в лучшем случае, требовали дополнительной настройки. Разумеется, что программисты нашли выход и в системных библиотеках появились модули для определения типа установленной на компьютере видеокарты и настройки программы на ее параметры, но это были полумеры, требовалось радикальное решение.
При этом следует отметить, что отсутствие стандартов имело и свою положительную сторону. Именно в это время разработчиками видеооборудования был накоплен практический опыт использования различных видеорежимов. Трудно себе представить специалистов, которые могли бы предусмотреть все возможные случаи, не опираясь на существующий опыт. Намного проще обобщить достигнутые результаты, оставить главное и отбросить ненужное.
Необходимость стандартизации понимали не только программисты, но и производители видеооборудования. Благодаря объединению их усилий и была создана специализированная ассоциация VESA, которая до настоящего времени занимается вопросами стандартизации видеооборудования (не только для IBM PC). Все ведущие производители придерживаются этих стандартов и проблема несовместимости мониторов или видеокарт в наше время не столь актуальна, но технический прогресс вынуждает, время от времени, вновь возвращаться к проблеме стандартизации, уже на более высоком уровне работы с графикой.
Подведем итог сказанному в данном разделе. В современных видеокартах используется различная элементная база, поэтому они могут существенно различаться по своим техническим характеристикам. Но при использовании функций BIOS все они без исключения совместимы на программном уровне видеорежимами VGA IBM и VESA. Сказанное распространяется не только а обычные видеокарты, но и на акселераторы.
Свой первый персональный компьютер американская фирма IBM (International Business Machines) выпустила в 1981 году. В то время основным производителем персональных компьютеров была другая американская фирма DEC (Digital Equipment Corporation). IBM не входила даже в тройку лидеров, но за короткий отрезок времени она стала "законодателем мод" в сфере производства ПК. Этому, в немалой степени, способствовало то, что IBM публиковала подробную информацию о своих новых разработках, и ее могли использовать другие фирмы, занимающиеся производством компьютеров, совместимых с IBM PC, дополнительных плат различного назначения и разработкой программного обеспечения.
Впервые за всю историю существования IBM применила в своем изделии компоненты, изготовленные другими фирмами. В частности, в IBM PC использовался микропроцессор Intel 8086. С тех пор все семейство IBM PC базируется на микропроцессорах фирмы Intel. Кроме них могут применяться совместимые микропроцессоры фирм AMD и Cyrix.
Первая модель ПК выпускалась недолго, на смену ей пришел компьютер второго поколения PC хт, аббревиатура хт расшифровывается как extended Technology (расширенная технология). В нем по-прежнему использовался микропроцессор Intel 8086, но пространство оперативной памяти было увеличено до 640 Кбайт. Кроме того, были разработаны новые видеокарты, предназначенные для работы с монитором CGA и позволявшие отобразить 8 цветов. Объем видеопамяти у них достигал 4 Кбайт, а разрешающая способность составляла 320x200 точек.
Следующая модель компьютера была создана на базе микропроцессора Intel 80286, она называлась PC AT, аббревиатура AT расшифровывается как Advanced Technology (прогрессивная технология). При разработке PC AT в качестве стандарта был принят монитор EGA. На видеокартах появился новый разъем для подключения монитора. Количество разных цветов точки возросло до 16-ти, а объем видеопамяти до 64 Кбайт. Такой объем видеопамяти и разрешающая способность монитора позволяли создавать на экране изображение размером 640x350 точек.
К этому времени производство видеокарт и мониторов освоили разные фирмы, и их продукция существенно различалась по техническим характеристикам. Выпускались карты, которые позволяли использовать 64 цвета и имели объем видеопамяти больше, чем 64 Кбайт. Однако для использования таких карт требовалось описание способов их программирования, которое в большинстве случаев недоступно для программистов.
Для стандартов CGA и EGA характерна сложная организация видеопамяти. Простая запись кода точки в видеопамять или чтение кода из нее невозможны, для этого требуется около десятка команд и приходится работать с портами видеокарты.
Стандарт на монитор VGA был опубликован при выпуске новой серии IBM PS (персональные системы) на базе процессора Intel 80386. Эта серия компьютеров не получила широкого распространения. Стандарт был принят, но в стремлении вырваться вперед IBM выпустила недоработанный продукт, чем и не замедлили воспользоваться конкуренты.
Стандарт VGA предусматривал новый трехрядный 15-контактный разъем для подключения монитора. Пожалуй, это наиболее продуманная часть стандарта. В разъеме были оставлены свободные (зарезервированные) контакты для будущих расширений. Поэтому его форма не изменилась до настоящего времени. При подключении современных SVGA-мониторов используются некоторые из ранее зарезервированных контактов разъема. Следует отметить, что такой стандарт разъема распространяется только на видеокарты, предназначенные для семейства IBM PC.
Стандарт VGA был шагом вперед по количеству цветов, которое возросло с 16 до 256. Объем видеопамяти увеличился до 256 Кбайт, и упростилась ее организация. В отличие от стандартов CGA и EGA, запись и чтение кода точки теперь производились одной командой, как при работе с обычной (оперативной) памятью компьютера. Однако стандарт VGA имел следующий существенный недостаток. Видеопамять, как и обычная память, делится на сегменты размером по 64 Кбайт. Стандарт VGA не предусматривал механизм переключения сегментов, поэтому на экране можно было отобразить содержимое только одного из них. Соответственно размер максимально возможного изображения составлял 320x200 точек (320x200 = 64 000, что немного меньше, чем 64 Кбайт).
Для программирования конкретной видеокарты надо знать назначение ее внутренних регистров, их размерность (байты, слова и пр.), способ записи или чтения данных и расположение величин в разрядах регистров. Первый стандарт IBM регламентировал назначение, состав и способы работы с внутренними регистрами, что исключало несовместимость видеокарт. Но скоро стало очевидно, что это плохой способ решения проблемы совместимости и в стандартах EGA и VGA указанные требования распространялись только на основную часть регистров.
Для решения проблемы совместимости были стандартизированы функции BIOS. IBM выпустила описание базового набора, содержащее перечень основных функций, способ их вызова, назначение и размещение входных и выходных параметров. Так появилась группа функций BIOS с названием "video Services". Образующие ее подпрограммы и данные не входят в основную часть BIOS, они хранятся в специальной микросхеме, расположенной на видеокарте. Поэтому, устанавливая новую видеокарту, вы одновременно устанавливаете новую реализацию функций указанной группы. У современных моделей видеокарт эта группа может занимать полный сегмент памяти, т. е. 64 Кбайт. Это свидетельствует о сложности и разнообразии выполняемых действий и о большом объеме используемых при этом данных.
Именно благодаря наличию функций "video Services" вы можете быть уверены в том, что после смены видеокарты ваш ПК сохранит свою работоспособность. Программисты получили существенное упрощение структуры прикладных задач и их независимость от моделей видеокарт. А разработчики получили возможность изменять программную реализацию функций BIOS для учета конкретных особенностей видеокарты.
Перед выводом на экран монитора текста или графических изображений должен быть установлен соответствующий видеорежим. В частности, при первоначальной загрузке ПК BIOS устанавливает текстовый режим работы, при котором на экране можно расположить 25 строк, каждая из которых содержит не более чем 80 символов. DOS обычно не изменяет этот режим, а прикладные задачи могут выполняться в текстовых или в графических режимах.
В группу "video services" обязательно входит функция, выполняющая установку видеорежима. При ее вызове указывается код видеорежима, а данные, необходимые для его установки, хранятся в области BIOS. IBM ввела стандартные значения кодов для 20-ти видеорежимов, значения которых изменяются от о до I3h (буква h — признак шестнадцатеричного числа). Разработчики видеокарт могут вводить новые режимы по своему усмотрению, чем они обычно и пользуются.
Код режима | Количество точек в строке | Количество строк по вертикали | Размер точки в битах | Размер строки в байтах | Количество цветов |
VESA редакции 1.0 и 1.1 | |||||
100h | 640 | 400 | 8 | 640 | 256 |
101h | 640 | 480 | 8 | 640 | 256 |
102h | 800 | 600 | 4 | 100 | 16 |
103h | 800 | 600 | 8 | 800 | 256 |
104h | 1024 | 768 | 4 | 128 | 16 |
105h | 1024 | 768 | 8 | 1024 | 256 |
106h | 1280 | 1024 | 4 | 160 | 16 |
107h | 1280 | 1024 | 8 | 1280 | 256 |
108h | 80 | 60 | — | 160 | 16 |
109h | 132 | 25 | - | 264 | 16 |
10Ah | 132 | 43 | - | 264 | 16 |
10Bh | 132 | 50 | — | 264 | 16 |
10Ch | 132 | 60 | - | 264 | 16 |
VESA peдакции 1.2 | |||||
10Dh | 320 | 200 | 15 | 640 | 32К |
1QEh | 320 | 200 | 16 | 640 | 64К |
10Fh | 320 | 200 | 32/24 | 1280/960 | 16М |
110h | 640 | 480 | 15 | 1280 | 32К |
111h | 640 | 480 | 16 | 1280 | 64К |
11 2h | 640 | 480 | 32/24 | 2560/1920 | 16М |
113h | 800 | 600 | 15 | 1600 | 32К |
114h | 800 | 600 | 16 | 1600 | 64К |
115h | 800 | 600 | 32/24 | 3200/2400 | 16М |
116h | 1024 | 768 | 15 | 2048 | 32К |
117h | 1024 | 768 | 16 | 2048 | 64К |
118h | 1024 | 768 | 32/24 | 4096/3072 | 16М |
119h | 1280 | 1024 | 15 | 2560 | 32К |
11Ah | 1280 | 1024 | 16 | 2560 | 64К |
11Bh | 1280 | 1024 | 32/24 | 5120/3840 | 16М |
В первом столбце табл. 1.1 перечислены коды видеорежимов. Это шестнад-цатеричные числа, поэтому в их записи могут встречаться не только цифры, но и латинские буквы от А до F, а в конце кода обязательно указывается латинская буква n (lOOh = 256). В остальных столбцах таблицы приведены десятичные числа. В последнем столбце, для сокращения записи количества цветов, использованы буквы к и м. Они обозначают степени числа два, наиболее близкие по значению к тысяче (к=1024) и миллиону (м=Ю4857б). Соответственно, количество цветов может быть следующим:
32К = 32768, 64К = 65536, 16М = 16777216.
В графических режимах размер строки указан в точках, а в текстовых — в виде количества символов. В текстовых режимах ширина символов постоянна и составляет 8 точек, а высота — 8 или 16 точек.
Классификация режимов. Перечисленные в табл. 1.1 видеорежимы делятся на следующие пять групп:
Этим группам соответствуют 4 разные модели видеопамяти, поскольку разновидности режимов direct color используют одну модель. В описании стандарта понятие "модель видеопамяти" четко не объясняется, но речь идет
о том, как видеоконтроллер интерпретирует содержимое байтов видеопамяти. Для программиста важно знать не модель, а способ доступа к видеопамяти и что при этом записывается в ее байты.
Три из четырех моделей допускают непосредственную работу с видеопамятью, т. е. запись и чтение содержимого ее байтов и слов с помощью обычных команд ассемблера. Исключением являются режимы EGA, в этом случае для чтения или записи необходима работа с внутренними регистрами видеокарты. Эти режимы морально устарели, кроме того, они хорошо описаны в литературе, поэтому в данной книге не рассматриваются.
Текстовые режимы VESA просто расширяют возможности аналогичных режимов IBM и позволяют использовать стандартные процедуры BIOS, предназначенные для работы с текстом. Программирование в текстовых режимах описано в первой части главы 5.
Режимы packed pixel graphics отличаются от режима VGA IBM тем, что введено сегментирование видеопамяти, все пространство которой делится на окна размером по 64 Кбайт. Своевременное переключение окон позволяет работать с большим пространством видеопамяти, которое требуется для поддержки видеорежимов с высоким разрешением. Напомним, что в режиме VGA IBM разрешение составляет 320x200 точек (сравните с табл. 1.1).
В режимах packed pixel graphics между кодами точки и цвета нет однозначного соответствия, поскольку они расположены в разных устройствах видеокарты. Коды точек хранятся в видеопамяти, а коды их цветов — в специальных регистрах видеокарты. Изменяя содержимое этих регистров, можно изменить все цвета, использованные в изображении, без обращения к видеопамяти, т. е. не изменяя кодов точек образа рисунка. Количество регистров (256) определяет размер кодов точек — 1 байт (8 разрядов). В главе 3 описано программирование рисования и построения графических объектов в этих режимах, а в главе 4 — работа с цветом.
В режимах direct color базовые цвета расположены непосредственно в коде точки, который может содержать 2, 3 или 4 байта. На момент написания данной книги трехбайтовый код был обнаружен только у одного семейства акселераторов фирмы ATI. Размещение базовых цветов в коде точки значительно расширяет возможности работы с цветом и позволяет создавать различные спецэффекты, которые широко распространены в современной графике. Программирование в режимах direct color описано в главе 7. В этих режимах увеличивается размер кода точки, а следовательно, и пространство видеопамяти, необходимое для хранения содержимого рабочей области экрана. Например, 1 Мбайт видеопамяти достаточно для работы во всех режимах packed pixel graphics, кроме I07h, с разрешением 1280x1024 точки.
В то время как для поддержки режима H2h с разрешением всего 640x480 точек требуется 2 Мбайт, а для режима 11Bh с разрешением 1280x1024 необходимо 6 Мбайт видеопамяти. Поэтому возможность использования режимов с высоким разрешением зависит не только от монитора, но и от объема видеопамяти.
Выбор конкретного режима зависит от программиста и от особенностей задачи, которую ему предстоит решать. В режимах packed pixel graphics достигается максимальная производительность видеосистемы, но ограничены возможности манипуляций с цветом. В режимах direct color увеличиваются затраты оперативной и видеопамяти и замедляется процесс построения графических объектов, но существенно расширяются возможности работы с цветом. Вариантов много, есть из чего выбирать.
Адрес поля |
Размер поля |
Что хранится в поле |
Режим 101h |
Режим 110h |
Режим 112h |
00 |
2 |
Атрибуты режима |
009ВН |
OOQBh |
009Bh |
02 |
1 |
Атрибуты окна А |
07 |
||
03 |
1 |
Атрибуты окна в |
00 |
||
04 |
2 |
Размер ячейки окна в Кбайт |
0040И |
||
06 |
2 |
Размер окна в Кбайт |
0040h |
||
08 |
2 |
Код видеосегмента окна А |
AOOOh |
AOOOh |
AOOOh |
OAh |
2 |
Код видеосегмента окна в |
AOOOh |
AOOOh |
AOOOh |
OCh |
4 |
Адрес подпрограммы BIOS |
556Ch COOOh |
||
10h |
2 |
Размер строки в байтах |
0280h |
OSOOh |
OAOOh |
12h |
2 |
Размер строки в точках |
0280h |
0280h |
0280h |
14h |
2 |
Количество строк на экране |
01EOh |
01EOh |
01EOh |
16h |
1 |
Ширина символа (текст)- |
08 |
08 |
08 |
17h |
1 |
Высота символа (текст) |
10h |
10h |
10h |
18h |
1 |
Количество планов памяти |
01 |
01 |
01 |
19h |
1 |
Количество бит на точку |
08 |
OFh |
20h |
1Ah |
1 |
Количество банков видеопамяти |
01 |
01 |
01 |
1Bh |
1 |
Модель видеопамяти |
04 |
06 |
06 |
1Ch | 1 | Размер банка в Кбайт | 00 | 00 | 00 |
Адрес поля |
Размер поля |
Что хранится в поле |
Режим 101h |
Режим 110h |
Режим 112h |
1Dh |
1 |
Номер последнем страницы |
0Bh |
05 |
02 |
1ЕЬ |
1 |
Резервный байт |
01 |
01 |
01 |
IFh |
1 |
Размер маски красного цвета |
00 |
05 |
08 |
20h |
1 |
Позиция маски красного цвета |
00 |
0Ah |
10h |
21h |
1 |
Размер маски зеленого цвета |
00 |
05 |
08 |
22h |
1 |
Позиция маски зеленого цвета |
00 |
05 |
08 |
23h |
1 |
Размер маски синего цвета |
00 |
05 |
08 |
24h |
1 |
Позиция маски синего цвета |
00 |
00 |
00 |
25h |
1 |
Размер резервного поля |
00 |
01 |
08 |
26h |
1 |
Позиция резервного поля |
00 |
0Fh |
18 |
27h |
1 |
Флаги для режимов direct color |
00 |
00 |
00 |
Следующие 3 поля заполняются при VBE 2.0 и выше | |||||
28h |
4 |
Адрес начала видеопамяти |
0000 F800 |
||
2Ch |
4 |
Адрес свободного пространства |
00 |
00 |
00 |
30h |
2 |
Размер свободного пространства |
00 |
00 |
00 |
Часть величин, перечисленных в табл. 1.2, уже обсуждалась выше, назначение остальных будет описано по мере изложения материала, в тех случаях, когда они используются при программировании. Здесь мы ограничимся одним замечанием и опишем байт атрибутов режима.
Замечание 1
Замечание 1
Начиная с версии VBE 1.2, появились поля, в которых указываются количество банков видеопамяти (lAh) и размер банка в килобайтах (ich). У всех исследованных видеокарт указан один банк, а его размер равен нулю. Непонятно о каких банках идет речь, тем более, если их размер, выраженный в килобайтах, помещается в пределах одного байта. Поэтому эти поля лучше не использовать до выяснения их назначения.
Бит |
Код |
Что обозначает установка разряда |
0 |
1 |
Видеокарта поддерживает режим |
1 |
|
Резервный разряд (состояние безразлично) |
2 |
1 |
BIOS поддерживает вывод на экран |
3 |
1 |
Используется цветной монитор |
4 |
1 |
Режим графический |
5 |
1 |
Режим не совместим с VGA |
6 |
1 |
Невозможна работа с окнами видеопамяти |
7 |
1 |
Доступно все пространство видеопамяти (VBE 2 . 0) |
При программировании для защищенного режима надо проверять состояние бита 7. Если он установлен, то возможна непосредственная работа со всем пространством видеопамяти без переключения окон (см. раздел).
Как известно, на холсте или листе бумаги можно нарисовать только плоское изображение, а для придания ему эффекта объемности применяются специальные приемы рисования. То, что мы видим в ре-зультате, является оптической иллюзией (обманом зрения), основанной на нашем жизненном опыте восприятия окружающего мира.
Для получения изображения куба его грани надо расположить под определенными углами. У шара граней нет, поэтому для придания кругу эффекта объемности используется распределение света и тени, создаваемое с помощью штриховки или раскрашивания. Для получения эффекта расположения в пространстве нескольких объектов их размеры уменьшаются по мере удаления от точки наблюдения.
Перечисленные приемы основаны не только на нашем субъективном восприятии окружающего мира, но и на вполне объективных законах оптики, Существует возможность формального описания способов преобразования трехмерных объектов в двухмерные и программной реализации алгоритмов вычислений. Такие алгоритмы не учитывают субъективный фактор, но это второстепенный вопрос.
В отличие от листа бумаги экран монитора является плоской дискретной поверхностью, поэтому компьютерная графика имеет дело с дискретными объектами. Для их аналитического описания нужны специальные методы аппроксимации. При описании плоских (двухмерных) объектов обычно Применяются методы линейной и векторной графики, а при описании трехмерных объектов — полигональной графики (слово polygon переводится как многоугольник). Чаще всего в качестве многоугольников используются треугольники.
В разработку методов трехмерной графики основной вклад внесла Reality b 3D, подразделение ныне не существующей компании Rendermorphis.
Результаты ее работы легли в основу графической библиотеки Direct3D, продукт фирмы Microsoft. Они же были использованы при создании языка VRML, предназначенного для описания трехмерных сценариев. Его создала Cosmo Software, а адаптировала российская фирма ParaGraph.
Процесс построения трехмерного изображения можно разделить на два этапа:
геометрические вычисления; визуализация полученных результатов.Геометрические вычисления сводятся к манипуляциям с векторами и матрицами, в результате которых получается совокупность треугольников, аппроксимирующая поверхность трехмерного объекта. Эти вычисления полностью зависят от конкретных свойств графических объектов, поэтому их выполняет центральный процессор ПК.
Процесс визуализации (rendering) заключается в том, что полученные треугольники отображаются на дискретную плоскость (поверхность экрана). При этом некоторые из них могут превратиться в линии или точки, а часть окажется на невидимой стороне объекта. Треугольники раскрашиваются по заданным образцам (их называют текстурами), а для получения эффекта объемности учитывается распределение уровней освещенности. Кроме того, могут понадобиться вычисления, связанные с изменением размеров всего изображения или отдельных его частей, коррекцией перспективы, созданием эффекта тумана и др.
Все эти действия не столь сложны, как геометрические построения, но их количество огромно, оно во много раз больше, чем количество точек на экране монитора или в рисунке, если он занимает не весь экран. Выполнение визуализации процессором ПК существенно замедляет работу с графикой.
Одним из распространенных приемов ускорения является перенос несложных, но многократно повторяемых вычислений на аппаратный уровень. Для этого создаются специальные процессоры, выполняющие нужные вычисления по микропрограммам, работающим намного быстрее программ аналогичного назначения.
Любая графическая задача устанавливает тот видеорежим, на работу с которым она рассчитана. Для этой цели в состав VBE включена специальная функция. Перед ее вызовом целесообразно выполнить функцию 4F01H и проверить возможность работы в выбранном вами режиме, способ проверки описан в главе 2.
Функция 4F02h Set SuperVGA video Mode устанавливает видеорежим VESA, его код перед вызовом функции помещается в регистр bх. Обычно при установке режимов видеопамять очищается и экран оказывается черным. Если в регистре bх установить старший (15-й) разряд, то видеопамять не очищается. Сохранение содержимого видеопамяти может быть полезным (и применяется) в некоторых специальных случаях, но не забывайте, что при смене видеорежима картинка на экране изменяется до неузнаваемости.
Начиная с VBE 2.0, используется 14-й разряд регистра bx. Он должен быть очишен, если задача выполняется в реальном (16-разрядном) режиме работы микропроцессора, и установлен, если задача выполняется в защищенном (32-разрядном) режиме. При установке 14-го разряда возможность работы с окнами обычно исключается, поскольку доступно все пространство видеопамяти. Подробнее об этом сказано в разделе далее.
Функция универсальна в том отношении, что позволяет устанавливать не только режимы VESA, но и режимы IBM. Коды режимов VESA имеют значения от lOOh и выше. Обнаружив в регистре bx код с меньшим значением, функция 4F02h вызывает стандартную процедуру BIOS, предназначенную для установки режимов IBM (обычно ее использует функция 0 прерывания int 10h).
Как уже говорилось ранее, при установке режима код VESA заменяется кодом OEM, который записывается в байт с абсолютным адресом 449h, относящимся к области данных BIOS.
Специальная функции для завершения работы в режиме VESA не предусмотрена — она просто не нужна. В этом случае либо устанавливается новый видеорежим (стандартный или VESA), либо завершается выполнение задачи и DOS выполняет все необходимые действия по восстановлению исходного (текстового) видеорежима.
Функция 4F03h Get Current video Mode позволяет определить код установ-ленного (текущего) режима VESA, который возвращается в регистре bx. Функция введена по той причине, что в байте 44вь хранится код OEM. Исполнение запроса 4F03 сводится к чтению кода OEM из указанного байта и преобразованию его в код VESA по таблице соответствия, которая обязательно входит в состав BIOS.
Видеокарты воспринимают цифровую информацию, поступающую от ЦП, и вырабатывают сигналы, управляющие работой монитора. Ядром видеокарты является специализированный микропроцессор, выполняющий все необходимые функции. От него зависят такие технические характеристики, как производительность (или быстродействие), предельно допустимый объем памяти, конкретные особенности программирования. Кроме того, на любой видеокарте расположена оперативная память (RAM), предназначенная для хранения цифрового образа, выводимого на экран изображения. Наконец, на видеокарте находится постоянная память (ком), содержащая фрагмент базовой системы ввода-вывода (BIOS).
Разработчикам стандарта VESA предстояло, в первую очередь, ограничить разнообразие применявшихся на практике видеорежимов, связав с каждым из них конкретный код и набор характеристик. В двух первых версиях стандарта было описано 8 графических режимов packed pixel graphics и 5 текстовых режимов высокого разрешения. Графические видеорежимы direct color были введены в третьей версии стандарта. Коды режимов VESA и их характеристики перечислены в табл. 1.1.
Компьютерный рынок динамичен по своей природе, он постоянно предлагает новое, все более совершенное видеооборудование для ПК. Пользователи, напротив, склонны соразмерять свои потребности и реальные финансовые возможности. Поэтому в эксплуатации находится множество видеосистем, изготовленных разными фирмами и в разное время. Они существенно различаются по конструктивному исполнению, техническим характеристикам и, конечно же, по особенностям программирования. На их примере можно проследить всю историю развития технических средств персональных компьютеров от первых IBM PC XT до новейших моделей на базе процессоров семейства Pentium.
На любой видеокарте имеется микросхема пассивной, т. е. доступной только для чтения (ROM), памяти, в которой хранится фрагмент BIOS, содержащий структуры данных и подпрограммы, предназначенные для поддержки работы видеосистемы. В частности, к ним относятся функции, обращение к которым (вызов которых) происходит через прерывание int 10h (Video Services).
В состав группы video Services обязательно входит набор функций для поддержки стандартных режимов IBM. Он необходим, по крайней мере, для нормального выполнения процесса загрузки ПК. Дополнением к нему являются функции VBE, описанные в данном и двух следующих разделах. Перед обращением к BIOS код запрашиваемой функции помещается в регистр ах. Он состоит из кодов группы и функции в группе. Код группы VBE равен 4Fh, он указывается в старшем байте регистра ах. Код функции для версии VBE 1.2 может изменяться от 0 до 8, он указывается в младшем байте регистра ах. Таким образом, содержимое регистра ах при вызове функций VBE 1.2 МОЖет ИЗМенЯТЬСЯ ОТ 4F00h ДО 4F08h.
Функции могут иметь входные и выходные параметры, которые передаются в регистрах общего назначения или в сегментных регистрах. Входные параметры нужны для нормального выполнения конкретной функции, а выходные содержат ее возвращаемый результат. Назначение и размещение параметров в регистрах будет описано для каждой функции. Если запрошенная задачей функция поддерживается BIOS, то в регистр ai возвращается код 4Fh.
Важно Это признак того, что функция могла быть выполнена. При успешном выполнении дополнительно очищается байт ah. В противном случае он содержит код ошибки. Таким образом, код 4Fh в регистре ах является признаком успешного выполнения запроса.
Несмотря на небольшое число функций (11), их состав оказался вполне достаточным. Авторы версии VBE 3.0, которая опубликована в сентябре 1998 года, не ввели ни одной новой функции, а только расширили возможности существующих с учетом новейших достижений разработчиков видеокарт.
В разные годы ассоциация VESA выпустила несколько небольших документов с описанием функций специального назначения. Формально они не относятся к VBE и их описание отсутствует во всех стандартах. Даже авторы VBE 3.0 ограничились их перечислением и весьма лаконичным комментарием. Вот перечень этих функций без комментариев:
4FiOh — Power Management Extension (PM) для стандарта DPMS; 4Fiih — Flat Panel Interface Extension (FP); 4Fi3h — Audio Interface Extension (AI); 4Fi4h — OEM Extension, вводимые по усмотрению разработчиков; 4Fi5h — Display Data Channel (DDC).Функции с кодами 10h, I4h и ish были обнаружены автором при исследовании зюз видеокарт с версиями VEE 1.2 и VBE 2.0. Если вы умеете восстанавливать исходные тексты BIOS, то можно проверить, какие функции поддерживает интересующая вас видеокарта.
Общая характеристика стандарта VBE закончена и мы переходим к описанию программирования работы с графикой в иидеорежимах VESA.
Для получения общих данных о видеокарте предназначена функция 4FOOh Get SuperVGA information. Входным параметром является адрес массива размером 256 байтов, при исполнении запроса в него записываются данные о видеокарте. Полный адрес этого массива указывается в регистрах es:di. Форма записи es:di общепринята, она означает, что в регистре es находится сегмент памяти, а в регистре di — расположение (смещение) массива в этом сегменте. При исполнении запроса только первые 20 байтов массива заполняются следующими данными:
00 4 байта — VESASignature; 04 2 байта — VESAVersion; 0б 4 байта — OEMStringptr; 0Ah 4 байта — Capabilities; 0Eh 4 байта — VideoModePtr; 12h 2 байта — TotalMemory.В первом столбце приведенного списка указаны смещения полей относительно начала массива, адрес которого находится в регистрах es:di. Поле VESASignature содержит ASCII-КОДЫ четырех букв, образующих слово "VESA". Вот ЭТИ КОДЫ — 56h, 45h, 53h, 41h.
Поле VESAVersion занимает 2 байта, содержащих номер версии и ее редакцию, например 0102 для VBE 1.2 или 0200 для VBE 2.0.
В поле OEMStringptr находится полный адрес (из области BIOS) начала строки текста, содержащей наименование изготовителя видеокарты. Коды символов соответствуют стандарту ASCII, а строка заканчивается пустым байтом (формат строки ASCIIZ). Полный адрес занимает два слова, в первом из них хранится смещение, а во втором — код сегмента памяти.
Поле Capabilities состоит из 32-х независимых разрядов (битов), в которых указываются специфические особенности видеокарты. Авторы стандарта явно перестарались, даже в новейшей версии VBE 3.0 описано назначение только пяти младших разрядов.
У рядовой видеокарты все 32 разряда поля capabilities очищены. Установка нулевого разряда означает возможность увеличения количества разрядов регистров DAC до 8 (см. описание функции 4F08h). Установка первого разряда означает, что видеоконтроллер не совместим с режимом IBM VGA.
Установка второго разряда означает необходимость синхронизации момента изменения содержимого регистров DAC с обратным ходом луча (см. описание фуНКЦИИ 4F09h).
Третий и четвертый разряды описаны только в VBE 3.0, они устанавливаются в тех случаях, когда видеоконтроллер или внешние устройства поддерживают работу со стереоскопическими сигналами.
В поле videoModePtr находится адрес начала списка видеорежимов, поддерживаемых картой. Первое слово поля содержит смещение, а второе сегмент. Список расположен в области BIOS, код каждого режима занимает одно слово. Признаком конца списка является код OFFFFh.
В поле TotalMemory указан установленный на видеокарте объем памяти, выраженный в блоках размером 64 Кбайт. 1 Мбайт соответствует 16 блокам (10h). На устаревших моделях видеокарт это поле может быть очищено. В каких случаях полезна описанная функция? Например, если графическая задача рассчитана на выполнение в защищенном (32-разрядном) режиме работы микропроцессора, то обязательно надо проверять номер версии VBE. Видеокарта может работать в таком режиме, если на ней реализованы функции VBE 2 . О, НО НС VBE 1. 2.
Анализ состояния разрядов поля capabilities и списка поддерживаемых режимов при выполнении задачи едва ли целесообразен. Поддержка выбранного режима обязательно проверяется в задаче, но делается это более надежным способом, чем просмотр списка, поскольку присутствие режима в списке еще не означает его поддержку. Подробно это обсуждается в главе 2.
Целесообразно составить простую программу, которая сохраняет в файле или распечатывает результаты выполнения запроса 4FOOh, включая список видеорежимов, и учитывать эти результаты при программировании. Запрос характеристик видеорежимов. В начале выполнения любой графической задачи обязательно вызывается другая информационная функция, которая возвращает данные, необходимые для настройки на работу с конкретной видеокартой. Здесь описана структура информационного блока, а процессу настройки посвящена специальная глава 2.
Функция 4F01h Get SuperVGA Mode Information позволяет получить информацию о любом из поддерживаемых видеорежимов независимо от того, установлен он или нет. Ее целесообразно вызывать до попытки установить режим, т. к. полученные данные позволяют определить, поддерживает видеокарта работу в нужном режиме или нет, и выполнить ряд подготовительных действий.
Перед вызовом в регистрах es:di указывается адрес массива размером в 256 байтов (как и для функции 4FOOh), а в регистр сх помещается код инте- ресующего вас режима. Если видеокарта содержит VBE, то при возврате в регистре ах записан код 4Fh. Если режим не поддерживается, то признак ошибки не вырабатывается, просто очищаются все поля табл. 1.2.
Результатом исполнения запроса является структура данных, приведенная в табл. 1.2. В первом столбце таблицы указаны смещения полей от начала массива, адрес которого находится в регистрах es:di. Второй столбец содержит размеры полей в байтах. Для примера в трех последних столбцах показаны значения величин, формируемых видеокартой VIRGE /DX /GS семейства S3 при запросе характеристик режимов 10lh (640x480, 256 цветов), 11оh (640x480, 32К) и H2h (640x480, 16М цветов). В шести случаях последние столбцы слиты в один это означает, что данные в соответствующих полях зависят не от режима, а от характеристик видеокарты.
В примерах, приводимых в данной и последующих главах книги, многократно повторяются имена переменных, подпрограмм для работы с окнами видеопамяти и макроопределений, предназначенных для записи в стек или выталкивания из него содержимого регистров. Для того чтобы каждый раз не объяснять назначение и способы описания и определения этих имен, мы сделаем это в данном разделе.
Подпрограммы для работы с окнами. Текст подпрограмм, выполняющих установку текущего (setwin), следующего (Nxtwin) и предыдущего (prevwin) окна, приведен в примере 2.8. Здесь мы обратим ваше внимание на следующие особенности, связанные с их использованием в примерах.
Прежде всего, обычно Макроассемблер не различает заглавные и строчные буквы в именах, независимо от их назначения. Поэтому, например, имена setwin, setwin и setwin для него тождественны. Буквы разного размера используются для того, чтобы человек мог визуально различить отдельные части составных имен. Если же вы принудительно заставите Макроассемблер различать строчные и заглавные буквы, что вполне возможно, то придется использовать только одну форму записи имен.
Подпрограммы должны быть включены в текст основной программы, для того чтобы Макроассемблер мог опознать их имена и сформировать команды вызова. Возможны разные способы оформления текстов подпрограмм и их размещения в теле задачи, подробно эти вопросы рассмотрены в приложении В. В примерах основной части книги описаны подпрограммы, расположенные в кодовом сегменте, т. е. там, где находятся команды, образующие тело задачи. Обычно в исходном тексте он имеет имя Code. Место расположения подпрограмм в пределах кодового сегмента выбирается по усмотрению разработчика.
Специфической особенностью семейства IBM PC является ограничение пространства доступных адресов размером 1 Мбайт.
Оно делится на сегменты, предельный размер которых составляет 64 Кбайт (1 Кбайт равен 1024 байтам). Всего в пространстве адресов помещается 16 сегментов предельного размера, 10 из них занимает оперативная память ПК.
Для доступа к видеопамяти выделяется один сегмент размером в 64 Кбайт, который чаще всего имеет адрес доооь для графических и взооь для текстовых режимов. Только при указании этих сегментов графическая или текстовая информация будет направлена в видеопамять или считана из нее, поэтому их значения ни при каких условиях не должны изменяться задачей. Размер видеосегмента значительно меньше реального объема видеопамяти. Для того чтобы задачи могли работать со всей памятью, в современных видеоконтроллерах реализован следующий механизм.
Все пространство видеопамяти делится на сегменты размером по 64 Кбайт, которые пронумерованы начиная с нуля. По принятой терминологии такие сегменты называют окнами или видеоокнами. В специальном регистре видеоконтроллера хранится номер текущего окна. При обращениях к видеопамяти контроллер добавляет его к 16-разрядному адресу, указанному задачей, и получает абсолютный адрес. Количество разрядов в абсолютном адресе зависит от предельного объема памяти, которая может быть установлена на видеокарте. Если на видеокарте может быть установлено 2 Мбайт памяти, то адрес занимает 21 разряд, если установлено 4 Мбайт — то 22 разряда и т. д.
Таким образом, полный адрес видеопамяти складывается из двух частей. Младшая часть является относительным адресом (смещением в сегменте), а старшая часть — номером текущего окна, хранящимся в одном из регистров видеоконтроллера. Задача может изменять текущее окно с помощью функции 4F05h.
Прямое обращение к BIOS. Видеокарты различаются не только адресами регистров, в которых хранится номер текущего окна, но и тем, в каких разрядах этих регистров он располагается. Поэтому при описании функции 4F05H в документации специально оговорено, что номер окна выражается в единицах приращения его значений, дословно in granularity unit. В примере 2.6 показан способ вычисления единицы приращения, она хранится в переменной GrUnit. Обычно она равна 1, только для одной видеокарты получилось другое значение, но если есть исключения, то надо следовать рекомендациям VESA и использовать переменную GrUnit.
Для установки окна с помощью функции 4F05h его номер помещается в регистр dx, а регистр bx очищается. После этого в регистр ах записывается код функции 4F05h и выполняется команда int lOh. В примере 2.7 показан способ прямого обращения к BIOS для установки окна. В нем и во всех последующих примерах текущий номер окна, выраженный в единицах GrUnit, выбирается из переменной cur_win. Она имеет размер слова и располагается в разделе данных задачи (см. пример 2.11).
Страничная организация памяти влияет олько на способы определения адресов точек графических объектов. Поэтому желательно выбрать такое расположение страниц, при котором изменение работы с адресами точек будет минимально возможным. Иначе говоря, нас интересует такое расположение страниц в видеопамяти, при котором основные процедуры рисования, построения или преобразования графических объектов почти не изменяются.
Перед построением или изменением графического объекта обычно выбирается некая опорная точка, относительно которой вычисляются адреса всех остатьных точек объекта. Чаще всего это левый верхний угол прямоугольной области экрана, в которой располагается или будет расположен объект. Координаты опорной точки обычно задают в виде номера строки и столбца, на пересечении которых она находится. Но для дальнейшей работы их надо преобразовать в адрес видеопамяти.
Если видеопамять не разделена на страницы, то начало ее рабочего пространства находится на пересечении нулевой строки и нулевого столбца. В таком случае существует простая связь между координатами произвольной точки и ее адресом в видеопамяти. Адрес вычисляется как сумма двух произведений: номера строки на размер строки в байтах и номера столбца на размер кода точки в байтах. Здесь имеется в виду размер строки, отображаемой на экране (scan Line). Способы вычисления адресов описаны в разделе (режимы packed pixel graphics) разделе и (режимы direct color), a соответствующие подпрограммы приведены в примерах 3.4 и 7.3.
При делении видеопамяти на страницы вычисленное адреса точек становятся относительными. Для получения абсолютных значений адресов в этом случае при вычислениях надо учитывать адрес начала страницы или ее координаты (номера нулевой строки и нулевого столбца). Следовательно, при введении страничной организации памяти придется изменить подпрограммы, выполняющие вычисление адресов точек по их координатам. Но это не все.
В процессе работы с графическим объектом приходится вычислять адреса начала его строк. В примерах, приведенных в данной книге, для этой цели использовалась "константа переадресации", значением которой является разность между размером отображаемой на экране строки (scan Line) и шириной графического объекта, выраженной в байтах. Обе величины зависят от установленного задачей видеорежима, способ учета этой зависимости описан в разделе , а варианты соответствующих подпрограмм приведены в примере 7.3. При их составлении предполагалось, что отображаемые на экране строки начинаются с нулевого столбца.
Следовательно, если страницы расположить так, чтобы каждая из них начиналась с начала одной из строк видеопамяти, то значение константы переадресации строк не будет зависеть от номера страницы. При таком расположении страниц изменяются только подпрограммы, вычисляющие адреса точек по их координатам.
Размер кода режимов VESA превышает размер байта, поэтому при установке этих режимов в байт 0000:0449 записывается так называемый код OEM, т. е. код, выбираемый по усмотрению разработчиков видеокарты. В ROM BIOS имеются две таблицы соответствия для преобразования кодов видеорежимов из VESA в OEM и обратно. Никаких соглашений относительно значений кодов OEM не существует, кроме того, что его размер не превышает семи разрядов, а значения отличаются от кодов видеорежимов IBM. Функция VBE 4F03h читает код текущего видеорежима из байта 0000:0449 и преобразует его в код режима VESA по таблице соответствия, хранящейся в ROM BIOS. Если в указанном байте находился код одного из видеорежимов IBM, то его значение не изменяется. В этом отношении функция 4F03h более универсальна, чем функция OFh.
Стандартная функция установки видеорежимов IBM (функция оо прерывания int 10h) установит режим VESA, если при обращении к ней в регистре al указать соответствующий код OEM. Так что коды OEM не совсем бесполезны, они, например, используются в драйверах для Windows.
Если вы владеете техникой дисассемблирования, то можно сравнительно просто найти в ROM BIOS таблицы соответствия кодов режимов VESA и OEM.
Эти таблицы используются функции VBE 4F02h И 4F03h.
Установке требуемого видеоре-жнма могут препятствовать три причины:
режимы VESA вообще не поддерживаются; не поддерживается конкретный видеорежим; недостаточно видеопамяти для работы в этом режиме.Возникновение первой причины может означать, что на компьютере установлена очень старая видеокарта. Кроме того, если задача была вызвана не из DOS, а из Windows З.Х, 9Х, ME или 2000, то при определенных условиях она может не получить доступ к функциям BIOS. Если же задача была вызвана из DOS и качество видеокарты не вызывает сомнений, то имеет смысл проверить состояние системного программного обеспечения, установленного на компьютере.
Возникновение второй причины означает, что предельно допустимый объем видеопамяти, поддерживаемый видеокартой, не позволяет работать в выбранном видеорежиме.
Возникновение третьей причины означает, что установленный на видеокарте объем памяти меньше предельно допустимого и того, который нужен для работы в запрашиваемом видеорежиме.
Объем видеопамяти, необходимый для работы в конкретном режиме, можно подсчитать на основании данных, приведенных в табл. 1.1. Если окажется, что видеокарта имеет достаточный объем памяти, то имеет смысл проверить состояние системного программного обеспечения, установленного на компьютере. Разумеется, вы должны быть уверены в корректности самой задачи.
В данном разделе ничего не говорилось о контроле ошибок при установке или изменении окон видеопамяти. Это объясняется несколькими причинами. Прежде всего, признак ошибки функция 4F05h возвращает только в том случае, если содержимое регистра ьь больше единицы, т. е. если неправильно указано запрашиваемое действие. Контроль правильности указанного значения окна не производится, поскольку его не так просто осуществить.
Если задача проверила поддержку установленного видеорежима, т. к. это рекомендовалось в предыдущем разделе, то при корректно организованной работе с видеопамятью значение устанавливаемого окна не может выходить за допустимые пределы. При организации любого контроля над ходом вычислительного процесса надо, прежде всего, руководствоваться соображениями здравого смысла, иначе можно дойти до проверки результатов каждого выполняемого действия.
В рассматриваемом случае возможен единственный способ контроля — проверка соответствия номера окна реально существующему объему видеопамяти. Но выполнять такую проверку при каждом изменении номера окна едва ли целесообразно. В крайнем случае, ее можно временно на период отладки задачи включить в подпрограмму setwin и убрать после отладки. Но лучше тщательно продумать алгоритм работы задачи и проверить правильность его воплощения.
Если вы умеете работать с отладчиками и восстанавливать по кодам команд исходный текст на языке ассемблера, то имеет смысл разобраться в том, как воплощена процедура VMC в BIOS. Это позволит вам узнать, как выполняется чтение или установка номера окна на уровне работы с портами ввода-вывода.
При программировании графики надо знать количество-точек на экране по горизонтали и вертикали, способ кодирования цвета, расположение базовых цветов, способ переключения окон видеопамяти, значение сегмента для доступа к видеопамяти и некоторые другие данные. Одни из этих величин зависят от видеорежима, другие — от особенностей видеокарты, третьи — от установленного на компьютере оборудования и программного обеспечения. Поэтому есть только один способ сделать задачу переносимой и заключается он в максимальном использовании данных, приведенных в табл. 1.2. После выполнения примеров 2.1—2.3 эти данные расположены в массиве info. Значение сегмента массива находится в регистре fs, а адрес начала массива (смещение от начала сегмента) равен нулю и расположен в регистре di.
Для сохранения и последующего использования служебной информации в разделе данных задачи надо зарезервировать переменные требуемого размера и присвоить им соответствующие имена. Например, размер рабочего поля экрана по вертикали мы будем хранить в переменной versize, а по горизонтали — в переменной Horsize. Подробнее о резервировании и назначении переменных сказано в разделе данной главы. Описанные там и в примерах всей главы имена переменных будут неоднократно использоваться в тексте книги.
Значения Vbuff, Horsize и Versize. В примере 2.5 показано, как можно сохранять данные из массива info. Предполагается, что он выполняется сразу после примера 2.4, о чем свидетельствует метка succ.
Переменная — это последовательность байтов оперативной памяти, которым присвоено уникальное имя и в которых хранятся величины, применяемые при вычислениях. В командах обычно используются переменные, содержащие 1, 2 или 4 байта. Переменные, содержащие большее количество байтов, принято называть строками, массивами или таблицами.
Переменные обычно хранятся в отдельном сегменте оперативной памяти, который принято называть сегментом данных. В исходных текстах программ его наиболее распространенным именем является Data. Для большей наглядности рекомендуется располагать сегмент данных перед кодовым сегментом. В примере 2.11 показан вариант оформления сегмента данных и описания в нем основных переменных, используемых в последующих примерах. Способ определения (формирования значений) большинства из них был описан в данной главе (см. примеры 2.1—2.6).
Стандарт VESA создавался для того, чтобы графические задачи могли самостоятельно, или при минимальном вмешательстве оператора, настроиться на работу с установленной на ПК видеокартой. В этой главе описано, как производится такая настройка.
Любой стандарт оставляет некоторую свободу действий производителям оборудования, поэтому существуют модели видеокарт, которые формально соответствуют требованиям VESA, а фактически их программирование все же имеет специфические особенности. Тем не менее, возможна единая схема, в которую укладывается работа с большинством наиболее распространенных видеокарт. Мы рассмотрим элементы этой схемы работы с видеокартами, а обнаруженные автором отклонения от нее будут специально оговариваться.
Независимо от видеорежима VESA, который используется в задаче, перед началом работы с графикой должны быть выполнены определенные действия, обеспечивающие в дальнейшем се корректную работу, универсальность и независимость от модели видеокарты. Вот перечень этих действий:
проверить, поддерживает BIOS требуемый видеорежим или нет; проверить, достаточно видеопамяти для выбранного режима или нет; получить и сохранить в области данных характеристики режима; прочитать и сохранить исходный видеорежим (не обязательно); установить требуемый видеорежим VESA; вычислить константу для коррекции номеров окон видеопамяти; настроить подпрограммы для работы с видеоокнами; определить размер и расположение полей базовых цветов.При выполнения перечисленных действий используются функции VBE, описанные в предыдущей главе.
Макровызовы или макрокоманды — это вставка текста макроопределения в нужных местах программы с подстановкой конкретных параметров, если они имеются. Для макроопределений примера 2.12 макровызов состоит из имени PushReg или PopReg и списка регистров, заключенного в угловые скобки. Угловые скобки позволяют использовать запятые между именами регистров, входящих в список.
Обнаружив макровызов, Макроассемблер находит и обрабатывает соответствующее определение, в результате чего формируются обычные команды, которые сразу компилируются и полученный код вставляется в тело задачи.
В частности, при вызове макроопределений примера 2.12 директива irp повторяется столько раз, сколько имен содержит список reg. Название каждого имени выбирается из списка и подставляется в команду push или pop вместо параметра r.
В макроопределениях примера 2.12 разнообразие имен не ограничено явно, допустимо указание любых величин, которые могут быть операндами команд push и pop. Ими могут быть не только имена 16- и 32-разрядных регистров, но и имена переменных, кроме того, команда push может сохранять в стеке значения констант (непосредственное указание сохраняемой величины). Все эти величины можно использовать в макровызове. Например, во многих подпрограммах будет использоваться такой вариант макровызовов:
PushReg <fs, gs, Cur_win> И PopReg <Cur_win, gs, fs>
Обратите внимание, имена в списках PushReg и PopReg расположены в обратном порядке, поскольку работа со стеком организована по принципу "последнее записанное — первое считанное".
Альтернативным способом сохранения группы регистров в стеке и восстановления их оттуда одной командой являются операции pusha и рора, не имеющие параметров. Их исполняют все модели микропроцессоров, кроме Intel 8086. Эти операции сохраняют в стеке или восстанавливают из него регистры в таком порядке:
PUSHA -> ах, ex, dx, bx, sp, bp, si, di.
РОРА -> di, si, bp, sp, bx, dx, ex, ax.
Начиная с модели Intel 80386, микропроцессоры поддерживают операции pushad и popad, также не имеющие параметров. Они сохраняют в стеке или восстанавливают из него 32-разрядные регистры в таком порядке:
PUSHAD-> eax, ecx, edx, ebx, esp, ebp, esi, edi.
POPAD -> edi, esi, ebp, esp, ebx, edx, ecx, eax.
В отличие от макрокоманд, четыре описанные операции сокращают не только исходный текст программы, но и размер задачи и время выполнения операции, поскольку вместо восьми команд используется одна. Их недостаток в том, что обязательно сохраняются все восемь указанных регистров, но не сохраняются сегментные регистры. Поэтому в каждом конкретном случае надо решать, что лучше использовать — указанные операции или макрокоманды.
При программировании большинства графических алгоритмов удобнее иметь дело с одним окном видеопамяти, независимо от того, сколько их есть на самом деле. Для этого надо изменить описанное в примере 2.8 гнездо подпрограмм так, чтобы при их вызове всегда изменялось столько окон видеопамяти, сколько их поддерживает видеокарта.
Изменения, которые надо внести только в подпрограмму setwin, поскольку только она обращается к процедуре VMC, заключаются в следующем. Окно А существует в любом случае, поэтому подпрограмма, в первую очередь, должна установить его номер. После этого надо проверить существование второго окна. Если его нет, то установка завершена. Если окно в существует, то ему надо присвоить тот же номер, который был присвоен окну А.
Напомним, что в примере 2.6 значение байта, отражающего состояние второго окна, копируется в переменную wine. При описании примера 2.6 было сказано, что содержимое winB может иметь только 4 значения: 0, 3, 5 или 7. Если оно равно нулю, то второго окна не существует. Три остальных значения указывают, для каких действий доступно второе окно. В данном случае для нас важен сам факт существования окна, а не то, для чего оно предназначено. Поэтому надо просто проверить состояние нулевого разряда байта winB, если он очищен, то окно не существует, а если установлен, то существует.
Подпрограмма SetWin для двух окон. Текст измененной подпрограммы Setwin приведен в примере 2.9. Его надо включить в пример 2.8 вместо описанного там варианта Setwin.
test_1:push es | ; сохранение содержимого es |
mov es, Info | ; значение сегмента буфера Info |
xor di, di | ; адрес начала буфера |
mov ax, 4F00h | ; код запрашиваемой функции |
int 10h | ; обращение к BIOS |
cmp ax, 4Fh | ; стандарт VESA поддерживается ? |
jz test 2 | ; -> да, продолжение проверок ; нет, выполнение задачи не возможно |
pop es | ; вывод аварийного сообщения |
Выполнение примера 2.1 начинается с сохранения в стеке содержимого регистра es (если он еще не использовался и его содержимое не имеет значения, то команды push es и pop es можно исключить из текста примера). Далее в регистр es записывается значение сегмента, содержащего буфер info, a di очищается для размещения данных с начала буфера. После этого в регистр ах записывается код запрашиваемой функции и происходит обращение к прерыванию BIOS int 10h. Если видеокарта соответствует стандарту VESA, то при возврате из BIOS в ах находится код 4Fh, это и проверяет команда cmp ax, 4Fh. Если результат проверки положительный, то следующая команда jz выполнит переход на метку test_2, которая обозначает начало примера 2.2.
Если результат проверки отрицательный (код отличается от 4Fh), то дальнейшее выполнение задачи не возможно. На экран надо вывести аварийное сообщение типа "Видеокарта не поддерживает режимы VESA" И Прекратить выполнение задачи. Как можно подготовить текст сообщения и вывести его на экран, описано в главе 5, посвященной работе с текстом.
В случае успешного выполнения запроса, в буфере info находится общая информация о видеокарте (см. главу 1). Как правило, ее использует только Windows при выборе драйверов для работы с конкретной картой. Нам драйверы подбирать не надо, поэтому интерес может представлять только объем видеопамяти, указанный в слове массива info со смещением 12h. Эта величина выражена в блоках размером по 64 Кбайт, поэтому 1 Мбайт соответствует код 10h. Объем видеопамяти в случае его использования при выполнении задачи надо сохранить в области данных, т. к. уже на следующем шаге содержимое массива info изменится.
Проверка поддержки видеорежима. Если видеокарта соответствует стандарту VESA, то надо проверить, поддерживается выбранный вами видеорежим или нет, и одновременно прочитать в массив info информацию о нем. В примере 2.2 показано, как можно выполнить эти действия.
test 2:mov ax, 4F01h | ; код запрашиваемой функции |
mov ex, NewMode ; | ; код нужного видеорежима |
int 10h | ; обращение к BIOS |
pop es | ; восстановление содержимого es |
cmp ax, 4Fh | ; нужный режим поддерживается? |
jz test_3 | ; -> да, продолжение проверок |
Предполагается, что пример 2.2 выполняется после примера 2.1, поэтому пара регистров es:di содержит адрес буфера info для записи информации о режиме и в стеке сохранено содержимое регистра es. Выполнение примера начинается с указания кодов запрашиваемой функции и нужного видеорежима, после чего происходит обращение к BIOS для выполнения запроса. После возвращения в задачу восстанавливается сохраненное в стеке исходное содержимое регистра es. Если видеокарта рассчитана на поддержку выбранного видеорежима, то в ах будет находиться код 4Fh, это и проверяет предпоследняя команда примера. Если условие выполнено, то произойдет переход на метку test_3 (начало примера 2.3) для продолжения проверок.
Если код в регистре ах отличается от 4Fh, то видеокарта не поддерживает требуемый видеорежим. В зависимости от конкретных особенностей задачи, ее выполнение может быть либо прервано, либо предпринята попытка перейти на работу в другом режиме, требующем меньший объем видеопамяти. Если вы уверены в корректности задачи и в том, что видеокарта поддерживает нужный режим, то проверьте системное программное обеспечение, установленное на компьютере. В практике автора был случай, когда драйвер манипулятора "мышь" реагировал на выполнение функции 4FOih, к которой °н не имел никакого отношения. В остальном работа этого драйвера не вызывала никаких нареканий. Ошибка была выявлена при работе задачи, в которой проверялось исполнение запросов функций VESA, и устранена путем замены драйвера.
test_3: mov fs, Info | ; сегмент с данными о режиме |
test byte ptr fs:[di], 1 | ; объем видеопамяти достаточен ? |
jne stmd | ; -> да, конец проверок |
; Недостаточно видеопамяти для поддержки нужного режима |
В команде test запись byte ptr явно указывает, что операндом является байт. Тип операнда указывается в тех случаях, когда Макроассемблер не может его определить по записи команды. Если нулевой разряд байта установлен, то объем памяти достаточен и команда jne передаст управление на метку stmd, указанную перед первой командой примера 2.4 .
Если нулевой разряд очищен, то объем видеопамяти не достаточен для поддержки выбранного режима. Что делать в подобных случаях, говорилось при описании примера 2.2.
Установка видеорежима. После успешного выполнения трех описанных проверок можно либо сразу установить рабочий видеорежим, а затем продолжить подготовительные действия, либо, наоборот, сначала завершить всю подготовку и лишь после этого устанавливать рабочий видеорежим. Только из соображений удобства описания, мы сначала рассмотрим установку видеорежима, а затем вернемся к подготовительным действиям.
Установку режимов VESA осуществляет функция 4F02h. Если при завершении задачи должен быть восстановлен исходный видеорежим, то его значение сохраняется в разделе данных. Следующий фрагмент программы иллюстрирует способ сохранения исходного и установку нового видеорежима.
stmd: mov ax,OFOOh | код функции "Чтение видеорежима" |
int lOh BIOS | читает текущий видеорежим |
mov OldMode, al | сохранение кода видеорежима |
mov bx, NewMode | ; код одного из режимов VESA |
mov ax, 4F02h | ; код запрашиваемой функции BIOS |
int lOh BIOS | ; исполняет запрос |
cmp ax, 4Fh | ; режим установлен ? |
jz succ -> | ; да, на продолжение программы |
; Ошибка при установке видеорежима |
В тексте примера 2.4 использованы имена OldMode и NewMode. Первое из них может быть только именем байта, расположенного в области данных программы. Как создаются такие имена, описано в последнем разделе данной главы. NewMode может быть именем расположенного в области данных слова, или константы, которой заранее присвоено конкретное значение, скажем, NewMode = noh. Кроме того, код режима может быть указан в команде явно, например, mov bx, noh. Если при выполнении задачи видеорежим устанавливается только один раз, то выбор способа указания NewMode произволен. Тем не менее использование переменных является более универсальным и предпочтительным приемом.
Первые три команды примера 2.4 считывают текущий видеорежим и сохраняют его в байте OldMode. Следующие три команды устанавливают новый режим, в котором будет работать задача. После второго возврата из BIOS анализируется содержимое регистра ах. Если в нем записан код 4Fh, то нужный режим установлен и происходит переход на начало примера 2.5 (метка succ). Отличие кода от 4Fh означает чрезвычайную ситуацию, поскольку предварительно были выполнены проверки, показавшие, что видеокарта поддерживает нужный режим. Если ваша задача заведомо корректна, то остается только проверять общее состояние компьютера и программного обеспечения.
В примере 2.4 для определения значения исходного видеорежима издается запрос OFh прерывания int 10h. При его исполнении BIOS просто считывает в регистр al содержимое байта 0000:0449, относящегося к области данных BIOS. Выполнить эти действия можно непосредственно в задаче без обращения к BIOS. В таком случае исключаются примерно 30 команд, которые BIOS выполняет при расшифровке запроса, сохранении и восстановлении содержимого регистров.
succ: mov ax, fs:[di+08] | ; читаем значение видеосегмента |
mov Vbuff, ax | ; и сохраняем его в Vbuff |
mov ax, fs:[di+12h] | ; читаем количество точек в строке |
mov horsize, ax | ; и сохраняем его в horsize |
mov ax, fs:[di+14h] | ; читаем количество строк на экране |
mov versize, ax | ; и сохраняем его в versize |
Команда пересылки не может перемешать данные из одного места памяти в другое, в подобных случаях нужны две команды и регистр, используемый в качестве посредника. В примере 2.5 посредником является регистр ах, но можно применять любой из регистров общего назначения. При чтении в регистр ах использован индексный способ адресации второго операнда с указанием смещения в виде числа. Макроассемблер включает смещение в код формируемой команды. Конкретное значение адреса операнда вычисляет микропроцессор при выполнении команды. Для этого он суммирует текущее содержимое регистра di и указанное в коде команды смещение.
Напомним, что в слове массива info со смещением 10h находится размер отображаемой на экране строки (scan Line) в байтах. Если эта величина будет использоваться в задаче, то ее также надо сохранить. В примере 2.11 для этого выделена переменная Bperiine.
Переменные для работы с окнами. Массив info содержит три величины, значения которых используются при работе с окнами видеопамяти. В примере 2.6, который является продолжением примера 2.5, показан способ сохранения этих величин и вычисления единицы приращения значения окна.
mov al, fs:[di+3] | ; читаем состояние окна В |
mov winB, al | ; и сохраняем его в байте winB |
mov cl, es:[di+4] | ; читаем в cl window granularity |
mov ax, 64 | ; помещаем в ах число 64 |
div cl | ; деление ах = ax/cl |
mov GrUnit, ax | ; сохраняем результат деления |
mov eax, es:[di+OCh] | ; читаем в еах адрес подпрограммы |
mov VMC, eax | ; и сохраняем его в VMC |
Две первые команды примера 2.6 сохраняют в памяти содержимое байта состояния окна в. Эта величина нужна, если видеокарта поддерживает работу с двумя окнами. Она может принимать одно из четырех значений:
0 - второе окно не существует; 3 - окно используется только при чтении из видеопамяти; 5 — окно используется только при записи в видеопамять; 7 — окно используется при записи и при чтении.Далее в примере 2.6 команды 3—6 вычисляют единицу для приращения (увеличения или уменьшения) значений окон видеопамяти делением числа 64 на содержимое байта массива info со смещением 4. Такой способ вычисления GrUnit рекомендован стандартом VESA.
Две последние команды примера 2.6 сохраняют в области данных задачи адрес подпрограммы BIOS, выполняющей работу с видеоокнами. Адрес состоит из сегмента и смещения, поэтому при его пересылке в качестве посредника используется 32-разрядный регистр еах.
Имена GrUnit и VMC заимствованы из описания стандарта VESA, их расшифровка приводится в следующем разделе данной главы.
В байте массива info, имеющем смещение 6, хранится размер окна, выраженный в килобайтах. Его стандартным значением является код 40h, соответствующий 64 Кбайт. Для семейства IBM PC стандартным значением сегмента оперативной памяти является именно 64 Кбайт. Возможно, по этой причине автор не встречал видеокарт, имеющих окна аномального размера.
В данном разделе мы показали способ получения и сохранения только той информации о видеокарте, которая будет многократно использоваться в примерах, приведенных в книге. Об использовании других величин, содержащихся в массиве info, речь пойдет по мере изложения материала.
Советуем вам составить программу, которая позволяет ввести код видеорежима, прочитать в память массив info и вывести на экран или печать содержимое его первых 20 слов в шестнадцатеричном коде. Затраченные на ее составление и отладку усилия окупятся при дальнейшей работе.
mov dx, Cur_win | ; запись в dx значения окна |
xor bx, bx | ; признак установки окна |
mov ax, 4F05h | ; код функции BIOS |
int 10h | ; обращение к BIOS |
Ускорение работы с окнами. В описании стандарта VESA не рекомендуется использовать прямое обращение к BIOS для установки окна. Причина простая — при обращениях к BIOS с использованием прерываний, например int lOh, выполняется много вспомогательных действий, связанных с сохранением регистров и расшифровкой кода запроса. Специфической особенностью прерывания int 10h является то, что на обращения к нему реагируют компоненты DOS и некоторые резидентные (постоянно находящиеся в оперативной памяти) задачи, например русификатор KEYRUS. В результате количество дополнительных и ненужных в данном конкретном случае действий оказывается значительно больше количества полезных действий, выполняющих запись или чтение окна. По этой причине разработчики стандарта VESA рекомендуют использовать только процедуру BIOS, которая выполняет чтение или запись окна и состоит всего из 10—15 команд. В описании стандарта процедура называется video Memory control (VMC). Способ сохранения ее адреса показан в примере 2.6.
При вызове процедуры VMC для чтения или установки окна регистры bx и dx заполняются так же, как и при обращении к функции 4F05h. Прежде чем рассматривать конкретные примеры, мы обсудим несколько вопросов, имеющих отношения к работе с окнами.
Описание гнезда подпрограмм. Установка или изменение текущего окна производится в задачах при построении, перемещении или копировании рисунков, управлении курсором, выводе и редактировании текста, т. е. при любых манипуляциях с графическими объектами. Поэтому вызов процедуры VMC целесообразно оформить в виде подпрограмм, расположенных в теле основной задачи.
Опыт показывает, что при работе с графикой номер нового окна чаще всего отличается от текущего значения на единицу. Поэтому целесообразно составить гнездо подпрограмм, которые производят установку не только указанного, но и следующего или предыдущего окна.
Номер текущего окна бывает нужен сравнительно часто, поэтому его надо хранить в рабочей области памяти в переменной cur_win, имеющей размер слова. Конкретное значение этой переменной зависит от выполняемых задачей действий. Обычно в текущем окне располагаются графический курсор, обрабатываемая часть рисунка или редактируемый текст. В некоторых случаях текущее окно может соответствовать области видеопамяти, не отображаемой в данный момент на экране.
В примере 2.8 описаны три подпрограммы, предназначенные для установки значения окна путем вызова процедуры VMC. Подпрограммы оформлены как внутренние, поэтому они должны быть расположены в том же сегменте памяти, в котором находится основная программа. Обращения к ним производятся с помощью команды call, в которой указано имя (метка) подпрограммы. Входным параметром для всех трех подпрограмм является переменная Cur win, исходное значение которой изменяется при установке следующего или предшествующего окна. Так и должно быть, поскольку новое окно становится текущим. Подпрограммы не изменяют содержимое регистров общего назначения, с которыми они работают.
Подпрограммы SetWin, NxtWin и PrevWin. Ядром примера 2.8 является подпрограмма setwin (вызов call Setwin). Она помещает текущее окно в регистр dx, очищает регистр bx и вызывает процедуру VMC для установки указанного окна. Прерывание int lOh не используется, что соответствует рекомендациям стандарта VESA.
Подпрограмма Nxtwin (обращение call Nxtwin) устанавливает следующее окно. При ее выполнении текущее значение переменной Cur__win увеличивается на единицу приращения (crunit), а затем выполняется setwin.
Подпрограмма PrevWin (обращение call PrevWin) устанавливает предыдущее окно. При ее выполнении текущее значение переменной Cur_win уменьшается на единицу приращения (GrUnit), а затем вызывается setwin.
; Установка следующего окна | |
NxtWin: push ax | ; сохраняем содержимое ах |
mov ax, GrUnit | ; читаем единицу приращения окна |
add Cur win, ax | ; увеличиваем номер окна |
pop ax | ; восстанавливаем содержимое ах |
; Установка окна, указанного в Cur win | |
SetWin: PushP,eg <ax,bx,dx> | ; сохранение содержимого регистров |
xor bx, bx | ; признак установки окна |
mov dx, Cur win | ; номер устанавливаемого окна |
call [VMC] | ; обращение к подпрограмме BIOS |
PopReg <dx,bx,ax> | ; восстановление содержимого регистров |
ret | ; возврат из подпрограммы |
; Установка предыдущего окна | |
PrevWin push ax | ; сохранение содержимого ах |
mov ax, GrUnit | ; читаем единицу приращения окна |
sub Cur win, ax | ; уменьшаем номер окна |
pop ax | ; восстанавливаем содержимое ах |
jmp SHORT SetWin | ; переходим на установку окна |
Процедура VMC расположена в "удаленном" сегменте, т. е. в пространстве адресов, не принадлежащих задаче. В таких случаях для обращения к подпрограммам используется команда call, у которой операнд является двойным словом, содержащим полный адрес (сегмент и смещение). При компиляции Макроассемблер формирует специальный код, указывающий процессору, что переход производится на удаленный адрес. В примере 2.8 операндом команды call является переменная VMC, описанная как двойное слово. Заключение имени переменной в квадратные скобки указывает на то, что адресом процедуры является не VMC, а хранящееся в ней значение, которое было установлено при выполнении команд примера 2.6.
Указание типа short в команде jmp заставит Макроассемблер сформировать короткую команду (для перехода не более чем на 128 байтов), код которой занимает два байта. pushReg и PopReg — это макросы (макровызовы). Первый эквивалентен трем командам push ax, push bx и push dx, а второй — трем командам pop dx, pop bx, pop ax. В реальной программе вы должны либо заменить макросы указанными командами, либо поместить в начале текста программы соответствующие им макроопределения, текст которых приведен в примере 2.12. Сохранение исходного содержимого регистров в стеке и восстановление при выходе делается для того, чтобы находящиеся там данные не изменялись в результате выполнения подпрограммы.
Чтение текущего окна. Еще раз вернемся к текущему окну. При корректной работе задачи с окнами значение переменной Cur_win всегда соответствует номеру, находящемуся в регистре видеоконтроллера. Если по каким-то причинам такое соответствие нарушено, то текущее значение окна можно восстановить, прочитав его из видеоконтроллера. Оформлять чтение текущего окна в виде самостоятельной процедуры не целесообразно, поскольку регулярных обращений к ней не должно быть.
Для чтения окна с помощью функции 4F05h надо выполнить следующие три команды:
mov bx, 10Oh | ; признак чтения окна |
mov ax, 4F05h | ; код запрашиваемой функции |
int 10h | ; обращение к BIOS |
Другой способ чтения текущего значения окна заключается в прямом обращении к процедуре BIOS, а именно:
mov bx, lOOh | ; признак чтения окна |
call [VMC] | ; обращение к процедуре BIOS |
В обоих случаях номер текущего окна возвращается в регистре dx. Каким из двух способов лучше воспользоваться, решает программист. Мы привели эти примеры для того, чтобы еще раз проиллюстрировать различие между прерыванием и прямым обращением к процедуре.
SetWin: PushReg <ax,bx,dx> | ; сохранение используемых регистров |
хог bx, bx | ; признак установки окна А |
mov dx, Cur__win | ; номер устанавливаемого окна |
call [VMC] | ; установка окна А |
test winB, 01 | ; окно В существует ? |
je stw | ; -> нет, переход на метку stw |
mov bx , | ; 01 признак установки окна В |
call [VMC] | ; установка окна В |
stw: PopReg <dx,bx,ax> | ; восстановление регистров |
ret | ; возврат из подпрограммы |
В дальнейшем, при описании примеров работы с графикой мы будем считать, что выполнение подпрограмм Setwin, Nxtwin и Prevwin не зависит от количества окон видеопамяти, поддерживаемых видеокартой, а при наличии двух окон их номера изменяются одновременно.
хог bx, bx ; признак смены страницы
mov ex, BaseCol ; номер точки в исходной строке
mov dx, BaseRow ; номер исходной строки
mov ax, 4F07h ; код запрашиваемой функции
int 10h ; обращение к BIOS
В примере 2.10 значения координат начала рабочей области выбираются из переменных BaseCol и BaseRow, которые должны быть описаны в сегменте данных программы. Для превращения этого примера в подпрограмму переключения активных страниц к нему надо добавить вычисление значений указанных переменных по номеру страницы. Способ вычисления выбирается по усмотрению программиста, а зависит он от выбранного расположения страниц в видеопамяти.
При работе в режимах packed pixel graphics, использующих регистры палитры, перед переключением страниц может понадобиться сохранение в оперативной памяти текущей палитры и установка новой. Способы работы г палитрой цветов описаны в главе 4. При работе в режимах Hi-color и Truecoior палитра не используется, т. к. цвет каждой точки указан в ее коде.
Data | SEGMENT | ; директива указывает начало сегмента |
OldMode | db 0 | ; исходный видеорежим |
NevMode | dw 101h | ; видеорежим VESA 101h |
Vbuff | dw 0А000h | ; адрес видеобуфера |
Horsize | dw 640 | ; количество точек в строке |
Bperline | dw 640 | ; количество байтов в строке |
Versize | dw 480 | ; количество строк на экране |
Cur win | dw 0 | ; номер текущего окна |
Cur pos | dw 0 | ; адрес (смещение) в текущем окне |
GrUnit | dw 0 | ; единица приращения номера окна |
VMC | dd 0 | ; адрес процедуры BIOS |
winB | db 0 | ; параметры окна В |
; Далее до конца сегмента располагаются другие описания | ||
Data | ENDS | ; директива указывает конец сегмента |
Описание сегмента открывает директива SEGMENT, а закрывает директива ENDS. Перед обеими директивами указывается одно и то же имя, в данном случае Data. Назначение и способы оформления сегментов описаны в приложении Б данной книги.
В примере 2.11 описаны 11 основных переменных. Каждая из них имеет уникальное имя. После имени расположены директивы db, dw или dd, указывающие тип переменной, т. е. ее размер в байтах: db — байт (8 разрядов), dw — слово (16 разрядов), dd — двойное слово (32 разряда).
После директивы указывается значение, которое Макроассемблер присваивает переменной. В примере 2.11 переменным сразу присвоены конкретные значения, но на практике они формируются в процессе выполнения задачи. Например, код сегмента видеобуфера (значение переменной vbuff) обычно АОООЬ, но возможны исключения, поэтому его значение надо выбирать из массива info.
В конце каждой строки примера 2.11 расположен комментарий, поясняющий назначение переменных и директив. Признаком начала комментария является символ "точка с запятой". Обнаружив его, Макроассемблер просто пропускает весь текст до конца строки.
Обратите внимание, в тексте примера 2.11 после всех имен отсутствует символ "двоеточие". Существует простое правило: двоеточие должно указываться только после имен меток, расположенных перед командами.
Следует отметить, что после директив описания типа может указываться не одно, а несколько значений, при этом Макроассемблер размещает эти значения в последовательно расположенных байтах, словах или двойных словах. Имя переменной в таком случае относится только к первому байту, слову или двойному слову. В отдельных случаях оно может вообще отсутствовать (см. пример 6.3). Существует специальный оператор повторения, который позволяет связать с именем переменной требуемое количество байтов, например:
Buffpal dd 256 DUP (0) ; резервирование и очистка памяти
В этом примере имя buff pal соответствует буферу размером в 256 двойных слов, содержимое которых принудительно очищается.
И последнее замечание, директива db может использоваться для описания текстовых строк, предназначенных для вывода на экран подсказок, предупреждений, аварийных и других сообщений. В этом случае расположенный после директивы текст заключается в одиночные или двойные кавычки. Вот пример оформления спецификации файла:
filspc db 'c:\tmp\current.pal', 00; описание спецификации файла
В этом примере описана спецификация файла current.pal, который находится на диске с в каталоге ТМР. Спецификация предназначена для процедуры BIOS, выполняющей открытие файла для чтения или записи, поэтому ее текст заканчивается пустым байтом.
Макросы PushReg и PopReg. Подпрограммы во время выполнения не должны изменять чужие или исходные данные. Для этого перед началом основных действий в стеке сохраняется содержимое используемых регистров или переменных, а перед выходом из подпрограммы восстанавливаются исходные значения сохраненных величин. Сохранение в стеке одной величины выполняет одна команда, это же относится и к восстановлению. Существует специальное средство, позволяющее сократить запись повторяющихся однотипных действий и сделать ее более наглядной. Таким средством являются макросы. Подчеркнем, что сокращается только исходный текст, а не количество команд в теле задачи.
Понятие макрос (macro) распространяется на макроопределение (macro definition) и макроподстановку или макровызов (macro substitution). Макроопределение описывает некую заготовку текста программы, а макровызов — способ ее использования. Макроопределение существует только в исходном тексте, оно модифицируется в зависимости от указанных при вызове параметров и в измененном виде включается в тело задачи на месте каждого макровызова.
Ниже приводится пример двух простых макроопределений (пример 2.12). Их вызовы уже использовались в примерах 2.8 и 2.9, и будут неоднократно встречаться во многих примерах. Первое из них PushReg предназначено для сохранения в стеке содержимого регистров, a PopReg — для восстановления из стека ранее сохраненных регистров.
; Сохранение в стеке регистров, перечисленных в списке гeg | ||
PushReg | macro reg | ; заголовок макроопределения |
irp r,<reg> | ; начало оператора повторения | |
push r | заготовка повторяемой команды | |
endm | конец оператора повторения | |
endm | конец макроопределения | |
BO( | :становление из | стека регистров, перечисленных в списке reg. |
PopReg | macro reg | заголовок макроопределения |
irp r,<reg> | оператор повторения | |
pop r | заготовка повторяемой команды | |
endm | конец оператора повторения | |
endni | конец макроопределения | |
Макросы примера 2.12 различаются только заготовкой повторяемой команды. В одном случае это запись в стек, а в другом — выталкивание из него.
При оформлении макросов используются специальные директивы. Текст любого макроопределения начинается директивой macro, перед ней указывается имя макроса, а после нее, в той же строке, список аргументов, если таковые имеются, в данном примере это reg.
Другая, часто используемая директива — endm. В зависимости от контекста она указывает конец макроопределения или оператора, что и показано в примере 2.12.
Тела макроопределений примера 2.12 состоят из директивы повторения irp. После нее, в той же строке, указываются параметр г и имя списка аргументов, заключенное в угловые скобки. Оно должно совпадать с именем, указанным в директиве macro. В следующей строке записывается повторяемая команда, один из операндов которой г соответствует параметру директивы irp.
В общем случае тело директивы irp может состоять из нескольких команд или содержать другие директивы, поэтому нужен признак конца директивы endm.
Обнаружив в тексте программы макроопределение, Макроассемблер проверяет его синтаксис и запоминает имя и текст, не включая его в тело задачи. Исполнение макроопределения (вставка команд в тело задачи) будет производиться при каждом макровызове.
На видеокарте обязательно расположена оперативная память, которую принято называть видеопамятью (video memory). Видеоконтроллер непрерывно выводит содержимое части видеопамяти на экран монитора, причем размер этой части зависит от установленного видеорежима. На современных видеокартах базовый объем памяти составляет не менее 1 Мбайт (1 Мбайт равен 1 048 576 байтам) и может быть расширен, по крайней мере, до 4 Мбайт при установке на видеокарту дополнительных микросхем. Напомним, что для работы в режиме VESA ивь четырех мегабайтов недостаточно. У акселераторов объем видеопамяти существенно больше (до 64 Мбайт).
Для нормального выполнения любой прикладной задачи должны быть созданы соответствующие условия. Поэтому при разработке задач, как правило, предусматриваются вспомогательные действия, направленные на проверку и создание таких условий. В этом отношении графические задачи не являются исключением.
Предварительные действия, выполняемые в графических задачах, можно разделить на две категории по признаку их зависимости от видеорежима. В первую очередь обычно выполняются те из них, которые не зависят от видеорежима, используемого в задаче. К ним относятся проверки операционной среды (версии DOS), наличие необходимого пространства оперативной памяти, изменение значений векторов прерываний, формирование многократно используемых величин и т. п.
От номера версии DOS зависит набор функций, выполняемых по запросам прикладных задач. Базовый набор функций, предназначенных для работы с файловой системой, был сформирован в версии 3.0 и с тех пор существенно не изменялся. На большинстве современных компьютеров используются версии не ниже 6.0, поэтому вопрос о необходимости проверки версии DOS решает программист с учетом особенностей создаваемой задачи.
Графические задачи обычно нуждаются в большом пространстве оперативной памяти, соизмеримым с объемом видеопамяти, необходимым для поддержания используемого видеорежима. Поэтому в процессе подготовительных действий обязательно производится определение доступного для задачи пространства оперативной памяти, его резервирование и распределение для внутреннего использования. Как это делается, описано в приложении Б данной книги.
В процессе выполнения задачи могут использоваться специальные таблицы. Если это общесистемные таблицы, то надо определить их расположение в оперативной памяти или в области BIOS. Если же таблицы являются собственностью задачи, то их надо разместить в доступном пространстве памяти. Вспомогательные действия, выполняемые при работе с таблицами шрифтов, содержащими изображения букв, цифр и других символов, описаны в главе 5.
В тех случаях, когда задача должна реагировать на прерывания от внешних устройств, при выполнении подготовительных действий надо создать условия для вызова прерывающих подпрограмм. В главах 5 и 6 описана настройка подпрограмм, реагирующих на прерывания, поступающие от системного таймера и манипулятора "мышь".
В процессе настройки могут быть выявлены условия, препятствующие выполнению задачи или требующие вмешательства оператора, например не соответствующая версия DOS, недостаточный объем оперативной памяти и пр. В таком случае на экран выводятся аварийные сообщения или поддерживается диалог с оператором, если он может что-то изменить. Вывод сообщений проще программировать в текстовом режиме, который установлен DOS перед вызовом задачи. Поэтому переход в графический видеорежим целесообразно производить после всех описанных проверок.
В примере 2.1 приведен текст фрагмента программы, проверяющего соответствие видеокарты стандарту VESA.
Большинство исследованных автором видеокарт поддерживало работу только с одним окном А. Исключением явился акселератор тасЬб4 фирмы ATI Technologies Inc., у которого для доступа к видеопамяти используется два окна. Запись данных в видеопамять осуществляется через окно А, а чтение — через окно в. Оба окна отображены на один видеосегмент АОООЬ. При обращениях к видеопамяти видеоконтроллер самостоятельно выбирает нужное окно, в зависимости от того записываются данные или считываются. Программисту остается "только" следить за тем, какое из двух окон надо переключать при достижении границы сегмента. Но это "только" может стать серьезным камнем преткновения для неискушенного программиста.
Стандартом VESA предусмотрена возможность работы с двумя окнами А и В, чем говорилось при описании функции BIOS 4F05h. Для работы с окном А (его установки или чтения) регистры (младший байт регистра bx) должен быть очищен, а для работы с окном в в него записывается единица. Если существует только одно окно, то оно имеет имя А и доступно для записи и чтения. Для проверки количества окон достаточно проанализировать состояние окна в, т. к. А заведомо существует. Если окно в доступно только для чтения, то А будет доступно лишь для записи или наоборот. Оба окна не могут быть доступны для выполнения одной и той же операции с видеопамятью, поскольку в таком случае придется вводить специальный механизм выбора нужного окна, а он стандартом VESA не предусмотрен.
При поддержке видеокартой двух окон возможны два способа работы с ними. Первый способ (в описании стандарта VESA он называется overlapping windows) основан на одновременном (синхронном) изменении номеров обоих окон. Второй (в описании стандарта VESA он называется distinguished windows) — на независимом изменении номеров окон (различение окон).
Если размер страниц равен размеру отображаемой на экране (рабочей) части видеопамяти, то каждая из них будет начинаться с новой строки. При таких размерах страницы располагаются в видеопамяти подряд друг за другом.
Если предполагается использовать N страниц, то в оперативной памяти надо выделить массив размером 2N слов. При выполнении подготовительных действий и эти слова задача должна поместить номера окон, в которых начинаются страницы и адреса нулевых строк страниц в этих окнах. Эти величины нужны для подпрограммы, выполняющей преобразование значений координат в адреса точек. Учитывая, что реальное количество страниц невелико, такой массив можно хранить в разделе данных задачи.
Оиеним возможное количество страниц. Предположим, что установленный на видеокарте объем памяти составляет 4 Мбайт и используется видеорежим с разрешением 640x480 точек. Если это режим packed pixel graphics, то в видеопамяти помещается 13 страниц, в режиме Hi-color их количество сократится в 2 раза, а в режиме True Color — в 4 раза. При более высоком геометрическом разрешении количество страниц сокращается. Поэтому можно считать, что оно не больше десяти.
Для определения допустимого количества страниц при выполнении задачи надо прочитать байт массива info со смещением 10h. В нем хранится номер последней страницы, которую можно установить в конкретном видеорежиме при имеющемся объеме видеопамяти. Страницы нумеруют начиная с нуля, поэтому их количество на 1 больше числа, хранящегося в байте 10b.
Кроме указанного массива, в разделе данных задачи надо выделить две переменные, например, Base_win и Base_addr для хранения исходного окна и адреса текущей страницы, с которой работает задача. В исходном состоянии эти переменные очищены, поскольку обычно работа начинается с нулевой страницы. В дальнейшем задача изменяет их значения в зависимости от номера используемой страницы.
В данной главе приведены первые примеры программ, поэтому имеет смысл поговорить о принятом в книге оформлении текстов примеров, об описании используемых в них переменных и о некоторых общих вопросах, связанных с оформлением программ на языке ассемблера. Если вы владеете ассемблером и имеете опыт программирования на этом языке, то данный раздел можно пропустить без ущерба для понимания излагаемого в последующих главах материала. Начинающим рекомендуем обратиться к специальной литературе, а при наличии доступа к Internet посетите сайт www.assembier.ru. Данная книга не является руководством по программированию на языке ассемблера, она содержит лишь минимум сведений, необходимый для понимания действий, выполняемых в примерах.
Ассемблер — это язык команд семейства компьютеров, в котором коды инструкций и операндов заменены мнемоническими обозначениями, т. е. именами. Используемые в ассемблерных программах имена делятся на две категории — зарезервированные и выбираемые по усмотрению разработчика.
К зарезервированным относятся имена регистров, операций, директив, операторов и некоторые другие. Их нельзя изменять или использовать не по назначению, в противном случае при компиляции программы будет выдано сообщение об ошибке.
По усмотрению программиста выбираются имена меток, констант, переменных, структур данных, макросов, сегментов программ и некоторые другие. Желательно, чтобы имена несли смысловую нагрузку, т. е. указывали назначение величин, к которым они относятся, и легко читались.
В именах можно использовать заглавные и строчные буквы латинского алфавита, цифры и те символы, которые не имеют специального назначения. К последним относятся: пробел, запятая, двоеточие, точка с запятой, все виды скобок и знаки арифметических операций. По умолчанию, т. е. если явно не указано обратное, ассемблер не различает в именах заглавные и строчные буквы. Поэтому не имеет значения заглавными или строчными буквами набрано имя, или в нем чередуются те и другие.
Для большей наглядности текста отдельные части составных имен могут выделяться заглавными буквами, например oidMode, или отделяться друг от друга нижней чертой, например cur_win. He следует увлекаться слишком длинными именами, поскольку теряется наглядность и повышается вероятность ошибки при их наборе.
Команда — это элементарная единица любой ассемблерной программы, исполняемая процессором ПК. Нас интересует язык ассемблера для микропроцессоров Intel, поскольку на их основе собираются компьютеры семейства IBM PC. В записи на этом языке команда состоит из условного обозначения операции и операндов, количество которых изменяется от 0 до 3 (чаще всего 1 или 2). Операнды отделяются от операции пробелами, а друг от друга запятой и пробелами. Наличие запятой обязательно, а количество пробелов не ограничивается, поэтому вы можете оформлять текст своей программы так, как сочтете нужным.
Перед командой может находиться метка, имя которой заканчивается символом "двоеточие". Метки являются операндами команд передачи управления — условных и безусловных переходов и обращений к подпрограммам.
После команды может записываться комментарий, который отделяется от нее символом "точка с запятой". Обнаружив этот символ в текущей строке. Макроассемблер просто пропускает весь текст до конца строки. Поэтому его можно использовать только как признак последующего комментария, а в комментарии допустимы любые символы.
Вернемся к командам. Операции имеют зарезервированные имена и их изменения недопустимы. Ассемблер формирует код машинной инструкции исходя из имени операции и результатов анализа операндов. Поэтому одной операции может соответствовать несколько разных кодов машинных инструкций. Например, имя mov обозначает операцию пересылки, которую выполняют 8 разных инструкций микропроцессора Intel 80386.
В качестве операндов могут использоваться имена регистров, констант, переменных, меток и выражения, составленные из перечисленных величин.
Регистры являются внутренними устройствами микропроцессора, т. е. они входят в его состав, поэтому обращение к ним происходит быстрее, чем к оперативной памяти. Начиная с Intel 8086, все модели микропроцессоров содержат 16-разрядные регистры, имеющие следующие имена:
/1/АХ, ВХ, СХ, DX, DI, SI, ВР, SP, CS, DS, ES, SS.
Первые четыре имени списка /1/ относятся к регистрам общего назначения. При выполнении задач в них обычно находятся сами операнды, а не их адреса. Исключением является регистр вх, в котором может храниться адрес операнда. Каждый из регистров общего назначения делится на два независимых байта. Старшим байтам соответствуют имена АН, вн, сн, он, а младшим байтам — AL, BL, CL, DL.
Имена DI, SI, BP, SP относятся к регистрам-указателям. Обычно они содержат адреса оперативной памяти, в которых хранятся операнды. В таких случаях при записи команды имя регистра заключается в квадратные скобки. Регистры-указатели не делятся на байты, поскольку адрес не может быть 8-разрядным. DI и SI обычно используются при работе с данными, поэтому по умолчанию в качестве сегментного регистра процессор выбирает DS. Специально для работы со стеком предназначены ВР и SP, поэтому по умолчанию в качестве сегментного регистра используется ss.
Существует специальный регистр, содержащий адрес очередной выполняемой команды. В русскоязычной литературе его называют счетчиком команд, а в англоязычной — указателем инструкций (IP). В явном виде он не указывается ни в одной команде, поэтому его имя отсутствует в списке /1/. Тем Не менее, все команды передачи управления изменяют содержимое IP.
Последняя четверка имен списка /1/ соответствует сегментным регистрам. Они предназначены для хранения старшей части адресов операндов или команд. Содержимое сз (сегмент кодов) процессор использует при выборке очередной команды. Содержимое DS (сегмент данных) — при чтении и записи операндов. В ss хранится сегмент оперативной памяти, отведенный для стека. Регистр ES используется строковыми командами при записи результата, в остальных случаях программист может распоряжаться им по своему усмотрению. Во всех примерах, приводимых в данной книге, регистр ES используется при обращениях к видеопамяти, поэтому в нем должно находиться значение сегмента видеопамяти (видеосегмента).
В записях операндов имя сегментного регистра предшествует адресу и обязательно заканчивается символом "двоеточие". Адресами, чаще всего, являются имена меток или переменных и индексные выражения, но допустимо и явное указание адреса в виде конкретного числа.
Начиная с модели Intel 386, регистры общего назначения и указатели расширены до 32-х разрядов. Размеры сегментных регистров не изменились, но добавились два новых. Новые имена перечислены в списке /2/.
/2/
ЕАХ, ЕВХ, ЕСХ, EDX, EDI, ESI, EBP, ESP, FS, GS.
Расширение регистров и введение новых имен никак рне отразилось на назначении и возможности использования старых. Просто 16-разрядные регистры стали младшими словами 32-разрядных регистров. Однако старшие слова 32-разрядных регистров самостоятельно не существуют и поэтому не имеют собственных имен.
Одновременно с введением новых регистров был расширен набор способов адресации операндов. Адрес может находиться в любом из 32-разрядных регистров, а ЕАХ, ЕВХ, ЕСХ и EDX могут использоваться в индексных выражениях.
Поскольку полный адрес помещается в 32-разрядных регистрах, то сегментные регистры просто не нужны, именно поэтому их разрядность не была увеличена. Таким образом, сегментные регистры являются атрибутом вычислений с использованием 16-разрядных адресов операндов.
Кроме регистров, перечисленных в списках /1/ и /2/, существуют еще специальные регистры, используемые в системных задачах, программирование которых не рассматривается в данной книге.
Константы — это постоянные величины, которым присвоены определенные имена. Они описываются (или определяются) с помощью операторов присваивания, имеющих следующую структуру:
<имя константы> = арифметическое или логическое выражение>
Простейший пример такого оператора CR = 0Dh. Если его включить в текст программы, то при компиляции все имена CR будут заменены на код ooh (аббревиатура CR расшифровывается как "возврат каретки" и ей соответствует код 0Dh). В более сложных случаях в выражениях кроме чисел могут использоваться имена констант и переменных, символы, обозначающие арифметические или логические операции, и круглые скобки для указания порядка выполнения вычислений.
При программировании на ассемблере константы могут быть только целыми числами, поэтому операторы типа PI = 3.14 вызовут сообщение об ошибке. Не все операции допускают использование констант, например, константу нельзя записать в сегментный регистр с помощью операции пересылки (mov).
Более общей формой описания констант является использование директивы EQU вместо знака равенства. Если справа (после директивы) указано арифметическое или логическое выражение, то обе директивы (EQU и =) равноценны. Однако после EQU можно записать символьное выражение, которое Макроассемблер будет подставлять вместо имени константы при каждом ее использовании в программе. Кроме того, после EQU можно указать произвольный текст, заключенный в угловые скобки. В таком случае имя константы будет соответствовать указанному тексту (за исключением угловых скобок).
Ассемблер не выделяет специального места в теле задачи для хранения констант. При каждом обнаружении имени константы он просто подставляет в формируемую команду соответствующее значение, символьное выражение или строку текста. Этим константы отличаются от переменных.
Раздельная (независимая) работа с двумя окнами применяется в редких случаях. Если в вашей практике возникнет такой случай, то следует поступить так:
выяснить, какое окно доступно для записи, а какое для чтения; ввести две разные переменные для хранения текущих значений окон; использовать два разных указателя адреса в текущем окне; составить независимые подпрограммы для работы с двумя окнами; Обсудим эти вопросы более подробно.При составлении конкретных программ или подпрограмм надо знать, какое окно доступно для записи, а какое для чтения. Например, у акселератора mach64 фирмы ATI Technologies Inc. для записи доступно окно А, а для чтения — в. Но это не общее правило. Стандарт VESA не распространяется на назначение окон, он только требует, чтобы оно было отражено в байте состояния окна. Поэтому наиболее простой способ выяснения назначения окон заключается в визуальной проверке кодов байтов состояния обоих окон, делать это программно намного сложнее.
В процессе выполнения задачи текущие значения окон изменяются независимо, поэтому их надо хранить в разных переменных. Учитывая, что для изменения номеров окон будут использоваться разные подпрограммы, можно выделить в разделе данных задачи две переменные cur_winA и Cur_winB. В таком случае при вызове подпрограмм не надо будет затрачивать лишние действия на передачу параметров.
Раздельная работа с окнами подразумевает и раздельную работу с адресами расположенных в них точек. При программировании циклов построения или преобразования графических объектов адреса точек хранятся в указателях адресов, обычно это индексные регистры. Количество используемых указателей адресов зависит от реализуемого алгоритма. Если текущие значения адресов в обоих окнах совпадают, то достаточно одного указателя адреса, в противном случае их должно быть два, что неизбежно усложнит структуру подпрограммы, выполняющей нужные действия.
Два комплекта подпрограмм. Мы не будем приводить подпрограммы, а просто опишем, что необходимо сделать.
Прежде всего, надо решить вопрос об именах подпрограмм Setwin, Nxtwin и Prevwin и имени переменной Cur_win. Проще всего добавить в конце каждого из этих имен буквы див, например, cur_winA и cur_winB.
Для получения двух комплектов подпрограмм сделайте копию примера 2.8 с тем вариантом Setwin, который в нем описан. В тексте оригинала добавьте в конце имен точек входа и в конце имени cur_win букву А.
В тексте копии добавьте в конце имен точек входа и в конце имени Cur_win букву в. Кроме того, в тексте копии надо заменить в подпрограмме setwinB команду хог bx, bx командой mov bx, 01. Напомним, что наличие в регистре bx кода 1 является признаком работы с окном в. Это все, что надо сделать для получения двух комплектов подпрограмм.
Таковы основные особенности работы с двумя независимыми окнами. Еще раз напомним, что в практике автора только акселератор mach64 имел два разных окна для записи и чтения. Учитывая, что карта выпущена в 1997 году, можно допустить, что появятся и другие видеокарты с раздельными окнами для записи и чтения в видеопамять.
Рассмотрим, как производится проверка возможности работы в выбранном видеорежиме. Для проверки соответствия видеокарты стандарту VESA и получения общей информации о ней предназначена функция 4FOOh прерывания int l0h (см. главу 1). Перед ее вызовом в паре регистров es:di надо указать адрес буфера размером в 256 байтов для размещения полученных данных. Указание буфера обязательно, независимо от того, будет задача использовать эти данные или нет.
Буфер такого же размера потребуется и при следующей проверке для размещения основных данных, используемых в процессе настройки задачи. Выделять специально для него постоянное место в памяти, например, в разделе данных задачи, не целесообразно, поскольку полученная информация нужна только при выполнении подготовительных действий. В задаче наверняка должен быть предусмотрен буфер большего размера для чтения файлов, содержащих рисунки. В приложении Б данной книги описано, как создается такой буфер. При выполнении подготовительных действий этот буфер свободен и в его начало можно поместить служебную информацию. Предположим, что буфер существует и сегмент, в котором он расположен, хранится в переменной info, описанной в разделе данных задачи.
При компиляции программы (при ее преобразовании в объектный код) Макроассемблер преобразует имена переменных в относительные адреса. Следовательно, должна существовать некая точка отсчета, адрес которой можно принять за нуль. Такой точкой является начало сегмента. В памяти ПК сегмент — это произвольно выбранный участок адресов, размер которого не меньше чем 16 и не больше чем 65 536 байтов. Наименьшее значение объясняется принятым на IBM PC способом вычисления полного адреса, а наибольшее соответствует предельному значению числа, которое может быть записано в 16-разрядный регистр.
Формально в тексте программы должен быть описан хотя бы один сегмент, в противном случае вычисление адресов будет невозможно и Макроассемблер выдаст сообщение об ошибке. Существует специальный класс задач, при программировании которых можно ограничиться одним сегментом. Это так называемые резидентные задачи, которые постоянно находятся в памяти К. Типичным примером являются загружаемые драйверы. Обычно при работе в ПК загружено несколько резидентных задач. Поэтому, чем меньше места в памяти занимает такая задача, тем лучше.
В задачах среднего размера, обычно используется три сегмента для размещения данных, команд и стека. Они ассоциируются с тремя сегментными регистрами DS, CS и SS, используемыми процессором при выполнении команд. Выделение нескольких сегментов в структуре программы преследует две основные цели. Во-первых, ее текст становится более удобочитаемым. Во-вторых — это один из способов увеличения пространства памяти, отведенного для задачи. Вопросы сегментирования программ и распределения памяти обсуждаются в приложении Б данной книги. Здесь мы попробуем разобраться с тем, как устанавливается связь между сегментами программы и соответствующими регистрами процессора.
Любая задача должна иметь явно описанную точку входа, в противном случае DOS не сможет начать ее выполнение. Точкой входа является метка первой выполняемой команды задачи, кроме того, ее имя указывается после директивы END, которой заканчивается текст программы. По этому имени Макроассемблер находит сегмент, в котором описана метка, и значение этого сегмента будет записано в регистр cs перед пуском задачи. Отметим, что определить точные значения сегментов может только DOS при загрузке задачи для выполнения, поскольку именно в этот момент известно распределение памяти ПК и ее доступное пространство. Таким образом, сегмент, содержащий команды, опознается Макроассемблером независимо от присвоенного ему в программе имени.
Для ассоциирования других сегментов с соответствующими регистрами процессора надо либо использовать специальные директивы при их описании, либо загружать значения сегментов в регистры в процессе выполнения задачи. Покажем, как записать в регистр данных значение сегмента, описанного в примере 2.11.
; точка входа в задачу, имя start надо указать после последнего end.
start: mov ax, data ; запись значения сегмента в ах
mov ds, ax ; копирование ах в ds
; продолжение программы
Первая команда этого фрагмента записывает значение указанного сегмента в регистр ах, а вторая переписывает содержимое ах в ds. Промежуточный регистр ах нужен потому, что имя сегмента (data) является константой и его нельзя записать в сегментный регистр с помощью операции пересылки. После выполнения этих двух команд процессор "знает", где находятся операнды команд. Таким способом можно записать в любой сегментный регистр значение нужного сегмента.
Вне сегментов, т. е. в начале текста программы располагаются директивы, которые используются только при компиляции. К ним относятся описание констант, о котором говорилось выше, и макроопределения.
Макроопределения и макровызовы объединяет одно общее понятие "Макросы". Это средство для сокращения исходных текстов программ (но не задач), придания ему большей наглядности и упрощения процесса программирования. В комплект поставки Макроассемблера входит несколько специальных файлов, содержащих различные полезные макроопределения. Эти файлы обычно имеют тип (расширение) inc. Если при установке ком-, пилятора создается каталог include, то они располагаются в нем.
В данном разделе автор попытался ответить на часть вопросов, которые могут возникнуть у читателя при изучении приведенных в книге примеров. По мере изложения материала будут описаны особенности выполнения некоторых команд, использованных в примерах. Многие из приведенных в книге примеров оформлены в виде подпрограмм. Поэтому приложение В специально посвящено вопросам, связанным с разработкой и использованием подпрограмм.
Как уже говорилось, активной является та страница, содержимое которой в данный момент отображается на экране монитора. Специальный механизм переключения страниц отсутствует, но существует функция VBE с кодом 4F07h (см. раздел), которая позволяет переместить начало рабочей области (Display start) в любую точку видеопамяти. Есть только одно ограничение на ее применение — от выбранной точки до конца видеопамяти должно оставаться пространство, достаточное для размещения рабочей области. Координаты выбранной точки указываются в виде строки и столбца.
Перед вызовом этой функции в регистр dx помещается порядковый номер первой отображаемой строки, а в регистр сх — номер ее первой точки. Напомним, что все номера начинаются с нуля. Кроме этого регистр bx очищается, что является признаком изменения начала отображаемой области, а в регистр ах помещается код функции 4F07h. После чего производится обращение к BIOS. Описанные действия выполняет группа команд примера 2.10.
Особый интерес представляет случай, когда каждая страница начинается с нового окна видеопамяти. При этом базовые адреса равны нулю, и в оперативной памяти надо хранить только номера окон, с которых начинаются страницы. Кроме того, в этом случае значения абсолютных и относительных адресов отличаются только на номер окна. Поэтому после вычисления относительного адреса его надо просто увеличить на номер окна. Как это делается, показано в примерах 3.4 и 7.3.
Рекомендованные стандартом VESA видеорежимы перечислены в табл. 1.1.
Если исключить из рассмотрения текстовые и 16-цветные, то остается О режимов, которые по геометрической разрешающей способности делятся на 5 групп. В каждую группу входит 4 режима, различающихся по количеству цветов.
При работе с тремя группами видеорежимов начала страниц можно совместить с началом окон видеопамяти. В табл. 2.1 приведены размеры страниц этих групп режимов.
Одним из традиционных приемов при работе с видеопамятью является ее деление на страницы. Из нескольких страниц только одна отображается на экране монитора, а остальные не видны. Видимую (отображаемую на экране) страницу называют активной, а невидимые — пассивными. Изменение содержимого невидимых страниц никак не отражается на экране монитора. Поэтому можно заранее подготовить и расположить на пассивной странице нужное изображение, а затем "мгновенно" изменить картинку на экране, сделав эту страницу активной.
Возможность деления видеопамяти на страницы основана на том, что при взаимодействии с монитором видеоконтроллер отображает только ту ее часть, которая нужна для заполнения рабочей области экрана. Остальное пространство видеопамяти просто не используется. Размер рабочего пространства видеопамяти зависит от установленного видеорежима и изменяется в достаточно больших пределах (см. табл. 1.1). Соответственно изменяется и размер свободного пространства. В одних случаях оно может быть намного больше рабочей части видеопамяти, а в других его может просто не быть. Поэтому возможность и целесообразность деления видеопамяти на страницы решается с учетом ее реального объема и используемого в задаче видеорежима.
Стандарт VESA не распространяется на страничную организацию видеопамяти и, вообще, его авторы предпочитают говорить не о страницах, а о разных изображениях, расположенных в видеопамяти. Поэтому все практические вопросы решаются по усмотрению программиста. Именно он выбирает способ переключения и расположение страниц в видеопамяти.
Разрешающая способность в точках | Количество окон (к = 1, 2, 4) | Количество строк |
640x480 | 5*k | 512 |
1024x768 | 12*k | 768 |
1280x1024 | 20 *k | 1024 |
Во втором столбце табл. 2.1 буква k указывает количество байтов, которое занимает код точки. Это косвенная характеристика цветовой .палитры. 1 байт — 256 цветов (packed pixel graphics), 2 байта — 32К или 64К цветов (Hi-Color), 4 байта — 16М цветов (True Color).
При разрешении 1024x768 и 1280x1024 точки размер страницы совпадает с размером рабочей области видеопамяти. При разрешении 640x480 точек размер страницы на 32 строки больше размера рабочей области, т. е. по сравнению с последовательным расположением страниц теряется некоторое пространство видеопамяти, но это не имеет принципиального значения.
Из двух групп, не попавших в табл. 2.1, практический интерес представляют видеорежимы с разрешением 800x600 точек. Целое количество строк, содержащих 800 точек, укладывается в 25 окнах, что почти в три раза превышает размер рабочей области видеопамяти. В этом случае при совмещении начала страниц с началом окон будет потеряно большое пространство видеопамяти. Поэтому при разрешении 800x600 точек страницы лучше располагать в видеопамяти последовательно друг за другом.
Заключение. Использование страниц видеопамяти расширяет возможности работы с графикой только при решении определенного класса задач. Например, страницы видеопамяти применяются всеми текстовыми и графическими редакторами. В некоторых источниках встречаются указания об использовании переключения страниц для получения спецэффектов, основанных на быстром изменении картинки на экране. Однако не надо забывать, что требуется определенное время на создание нужного изображения на пассивных страницах. Поэтому вопрос о целесообразности введения страниц видеопамяти и способах работы с ними надо решать, учитывая особенности конкретной задачи.
Если в регистре ах находится код 4Fh, то остается последний штрих — проверить достаточность реально существующего (а не предельно допустимого) объема видеопамяти для поддержки вндеоре-жима. При сборе информации о запрошенном режиме BIOS выполняет нужные для проверки вычисления и сравнения, результат которых помещается в нулевой разряд нулевого байта массива info. Задаче остается только проверить состояние этого разряда.
После выполнения примера 2.2 было восстановлено исходное содержимое регистра es и доступ к массиву info с использованием этого регистра уже невозможен. Для дальнейшей работы с данными о режиме надо выделить другой сегментный регистр, например, fs или gs, который не используется функциями BIOS.
Фрагмент программы, выполняющий заключительную проверку, приведен в примере 2.3. Предполагается, что он выполняется после примеров 2.1 и 2.2, поэтому регистр di очищен и указывает начало буфера, содержащего данные о режиме. Для доступа к этим данным используется сегментный регистр fs, поэтому в него записывается значение переменной info.