Для нормального выполнения любой прикладной задачи должны быть созданы соответствующие условия. Поэтому при разработке задач, как правило, предусматриваются вспомогательные действия, направленные на проверку и создание таких условий. В этом отношении графические задачи не являются исключением.
Предварительные действия, выполняемые в графических задачах, можно разделить на две категории по признаку их зависимости от видеорежима. В первую очередь обычно выполняются те из них, которые не зависят от видеорежима, используемого в задаче. К ним относятся проверки операционной среды (версии DOS), наличие необходимого пространства оперативной памяти, изменение значений векторов прерываний, формирование многократно используемых величин и т. п.
От номера версии DOS зависит набор функций, выполняемых по запросам прикладных задач. Базовый набор функций, предназначенных для работы с файловой системой, был сформирован в версии 3.0 и с тех пор существенно не изменялся. На большинстве современных компьютеров используются версии не ниже 6.0, поэтому вопрос о необходимости проверки версии DOS решает программист с учетом особенностей создаваемой задачи.
Графические задачи обычно нуждаются в большом пространстве оперативной памяти, соизмеримым с объемом видеопамяти, необходимым для поддержания используемого видеорежима. Поэтому в процессе подготовительных действий обязательно производится определение доступного для задачи пространства оперативной памяти, его резервирование и распределение для внутреннего использования. Как это делается, описано в приложении Б данной книги.
В процессе выполнения задачи могут использоваться специальные таблицы. Если это общесистемные таблицы, то надо определить их расположение в оперативной памяти или в области BIOS. Если же таблицы являются собственностью задачи, то их надо разместить в доступном пространстве памяти. Вспомогательные действия, выполняемые при работе с таблицами шрифтов, содержащими изображения букв, цифр и других символов, описаны в главе 5.
В тех случаях, когда задача должна реагировать на прерывания от внешних устройств, при выполнении подготовительных действий надо создать условия для вызова прерывающих подпрограмм. В главах 5 и 6 описана настройка подпрограмм, реагирующих на прерывания, поступающие от системного таймера и манипулятора "мышь".
В процессе настройки могут быть выявлены условия, препятствующие выполнению задачи или требующие вмешательства оператора, например не соответствующая версия DOS, недостаточный объем оперативной памяти и пр. В таком случае на экран выводятся аварийные сообщения или поддерживается диалог с оператором, если он может что-то изменить. Вывод сообщений проще программировать в текстовом режиме, который установлен DOS перед вызовом задачи. Поэтому переход в графический видеорежим целесообразно производить после всех описанных проверок.
Когда невозможна установка видеорежима?
Установке требуемого видеоре-жнма могут препятствовать три причины:
Возникновение первой причины может означать, что на компьютере установлена очень старая видеокарта. Кроме того, если задача была вызвана не из DOS, а из Windows З.Х, 9Х, ME или 2000, то при определенных условиях она может не получить доступ к функциям BIOS. Если же задача была вызвана из DOS и качество видеокарты не вызывает сомнений, то имеет смысл проверить состояние системного программного обеспечения, установленного на компьютере.
Возникновение второй причины означает, что предельно допустимый объем видеопамяти, поддерживаемый видеокартой, не позволяет работать в выбранном видеорежиме.
Возникновение третьей причины означает, что установленный на видеокарте объем памяти меньше предельно допустимого и того, который нужен для работы в запрашиваемом видеорежиме.
Объем видеопамяти, необходимый для работы в конкретном режиме, можно подсчитать на основании данных, приведенных в табл. 1.1. Если окажется, что видеокарта имеет достаточный объем памяти, то имеет смысл проверить состояние системного программного обеспечения, установленного на компьютере. Разумеется, вы должны быть уверены в корректности самой задачи.
Рассмотрим, как производится проверка возможности работы в выбранном видеорежиме. Для проверки соответствия видеокарты стандарту VESA и получения общей информации о ней предназначена функция 4FOOh прерывания int l0h (см. главу 1). Перед ее вызовом в паре регистров es:di надо указать адрес буфера размером в 256 байтов для размещения полученных данных. Указание буфера обязательно, независимо от того, будет задача использовать эти данные или нет.
Буфер такого же размера потребуется и при следующей проверке для размещения основных данных, используемых в процессе настройки задачи. Выделять специально для него постоянное место в памяти, например, в разделе данных задачи, не целесообразно, поскольку полученная информация нужна только при выполнении подготовительных действий. В задаче наверняка должен быть предусмотрен буфер большего размера для чтения файлов, содержащих рисунки. В приложении Б данной книги описано, как создается такой буфер. При выполнении подготовительных действий этот буфер свободен и в его начало можно поместить служебную информацию. Предположим, что буфер существует и сегмент, в котором он расположен, хранится в переменной info, описанной в разделе данных задачи.
Проверка существования VBE
В примере 2.1 приведен текст фрагмента программы, проверяющего соответствие видеокарты стандарту VESA.
Пример 2.1. Проверка поддержки стандарта VESA
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 показано, как можно выполнить эти действия.
Пример 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, и устранена путем замены драйвера.
Заключительная проверка
Если в регистре ах находится код 4Fh, то остается последний штрих — проверить достаточность реально существующего (а не предельно допустимого) объема видеопамяти для поддержки вндеоре-жима. При сборе информации о запрошенном режиме BIOS выполняет нужные для проверки вычисления и сравнения, результат которых помещается в нулевой разряд нулевого байта массива info. Задаче остается только проверить состояние этого разряда.
После выполнения примера 2.2 было восстановлено исходное содержимое регистра es и доступ к массиву info с использованием этого регистра уже невозможен. Для дальнейшей работы с данными о режиме надо выделить другой сегментный регистр, например, fs или gs, который не используется функциями BIOS.
Фрагмент программы, выполняющий заключительную проверку, приведен в примере 2.3. Предполагается, что он выполняется после примеров 2.1 и 2.2, поэтому регистр di очищен и указывает начало буфера, содержащего данные о режиме. Для доступа к этим данным используется сегментный регистр fs, поэтому в него записывается значение переменной info.
Пример 2.3. Заключительная проверка поддержки видеорежима
test_3: mov fs, Info | ; сегмент с данными о режиме |
test byte ptr fs:[di], 1 | ; объем видеопамяти достаточен ? |
jne stmd | ; -> да, конец проверок |
; Недостаточно видеопамяти для поддержки нужного режима |
В команде test запись byte ptr явно указывает, что операндом является байт. Тип операнда указывается в тех случаях, когда Макроассемблер не может его определить по записи команды. Если нулевой разряд байта установлен, то объем памяти достаточен и команда jne передаст управление на метку stmd, указанную перед первой командой примера 2.4 .
Если нулевой разряд очищен, то объем видеопамяти не достаточен для поддержки выбранного режима. Что делать в подобных случаях, говорилось при описании примера 2.2.
Установка видеорежима. После успешного выполнения трех описанных проверок можно либо сразу установить рабочий видеорежим, а затем продолжить подготовительные действия, либо, наоборот, сначала завершить всю подготовку и лишь после этого устанавливать рабочий видеорежим. Только из соображений удобства описания, мы сначала рассмотрим установку видеорежима, а затем вернемся к подготовительным действиям.
Установку режимов VESA осуществляет функция 4F02h. Если при завершении задачи должен быть восстановлен исходный видеорежим, то его значение сохраняется в разделе данных. Следующий фрагмент программы иллюстрирует способ сохранения исходного и установку нового видеорежима.
Пример 2.4. Сохранение исходного видеорежима и установка нового
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 выполняет
при расшифровке запроса, сохранении и восстановлении содержимого регистров.
Коды VESA и OEM
Размер кода режимов 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.