Единственно верный алгоритм руссификации адаптеров VGA/EGA

Дата публикации 16 июн 2002

Единственно верный алгоритм руссификации адаптеров VGA/EGA — Архив WASM.RU

  Актуальная, совсем недавно, проблемма руссификации видеоадаптеров EGA/VGA перестала быть жизненно важным моментом. И хотя, сами руссификаторы продолжают использоваться (например, для руссификации DOS сессий в Windows NT/2000), интерес, на наш взгляд, к принципам работы с видеоадаптерами должен сохраняться и поныне. Этому должно способствовать повальное увлечение современных любителей ассемблера к написанию собственных операционных систем. Именно поэтому решено было сделать римэйк, написанной некогда (1992 год) статьи, об идеальном алгоритме руссификации видеоадаптеров.

  Алгоритм руссификации разрабатывался и совершенствовался в течении четырех лет. Испытывался и отлаживался на огромном количестве, как фирменных видеоадаптеров, так и на их жутких "клонах". Работали над алгоритмом все это время два программиста Гайко Александр и Бордачев Андрей. Различные советы и идеи им давали так же их коллеги - Сысоев А.П., Гутников А.Е. Выражаем им свою благодарность.

  Исходный текст драйвера NVGA можно скачать здесь.

  При написании руссификатора нам хотелось добиться следующего:

  • корректной руссификации всех стандартных видеорежимов EGA и VGA адаптеров даже тех модификаций, которые имеют явные и грубые ошибки VIDEOBIOS;
  • полной руссификации видеорежимов портативных машин класса LapTop и NoteBook;
  • полное обслуживание всех нестандартных видеорежимов любых видеоадаптеров (в том числе SuperVga);
  • полной номенклатуры обслуживаемых шрифтов. Обработки не только основных шрифтов (8x8, 8x14, 8x16), но и альтернативных (9x14, 9x16), а также нестандартных, которые могут встречаться в VIDEOBIOS расширенных VGA-видеоадаптерах (8x11, 8x15);
  • руссификации 35/50-строчных режимов;
  • возможности частичной корректировки шрифтов (работа не со всеми символами кодовой таблицы, а только с теми которые подвергаются изменению). Это позволяет заметно снизить объем занимаемой руссификатором памяти. Это возможно для текстовых режимов видеоадаптеров;
  • возможности динамической смены шрифтов без перезагрузки драйвера. Это позволит менять не только начертание букв, но и динамически загружать нестандартные знаки.

  Итак. Для того, чтобы руссифицировать программным способом адаптер EGA или VGA, драйвер руссификатор должен:

  • перехватить прерывание 10h VIDEOBIOS. Это прерывание обслуживает все видеофункции персонального компьютера (включение текстовых и графических видеорежимов, программирование знакогенератора, цветовой палитры и т.д.).
  • отслеживать моменты переключения видеорежимов;
  • при включении текстового режима загружать в программируемый знакогенератор один или два шрифта требуемого размера. При включении графического режима установить вектор прерывания 43h на начало русского шрифта нужного размера;
  • отслеживать внешние запросы на информацию о шрифтах. Если запрашивается, например, местонахождение шрифта 8x16, возвратить в вызвавшую программу адрес шрифта;
  • отслеживать команды загрузки в знакогенератор адаптера шрифтов различной высоты. Если требуется загрузить шрифт из VIDEOBIOS, то вместо него загрузить руссифицированный шрифт.

  Рассмотрим более подробно, как работает перехватчик прерывания 10h. В первую очередь надо обратить внимание на то, что в теле обработчика содержится таблица всех шрифтов, которые необходимо обслуживать. Она располагается в самом конце обработчика (метка FonTable), имеет переменную длинну и может содержать до 16 описаний различных шрифтов. Для каждого шрифта зарезервированно 7 байтов. Каждое 7-байтовое описание имеет структуру, определяемую в начале исходного текста (см. структуру FontDef).

  • 1-й байт - высота шрифта FontSize (например, 14 или 16).
  • 2-й байт - вызов характеристик шрифта через BIOS FontCall.
    Например, чтобы узнать адрес шрифта 8x16, надо выполнить подфункцию 30h функции 11h (информация о знакогенераторе); при этом в регистре BH должно находиться значение 6 (узнать адрес шрифта 8x16). То значение, которое должно содержаться в регистре BH, и находится во втором байте структуры, описывающей шрифт.
  • 3-й байт - дополнительные признаки шрифта FontFlags.
    По умолчанию драйвер-обработчик 10-го прерывания работает с полными шрифтами. Например, полный шрифт 8x16 содержит 256 матриц символов с кодовыми позициями от 0 до 255. Однако драйвер может работать не только с полными таблицами шрифтов, но и с их фрагментами. Это позволяет не держать в памяти все 4096 байтов шрифта, когда не требуется руссификация графического режима.
    Если в адаптер EGA или VGA надо загрузить весь шрифт, то младшие три бита рассматриваемого байта - нулевые. Всего возможны три варианта частичного обслуживания знакогенератора.
    Все три варианта отображаются в младших трех битах байта FontFLags. В четвертом бите содержится признак того, что данный шрифт не основной, а альтернативный (9x14 или 9x16). Альтернативные шрифты имеют особый формат (см. ниже) и должны загружаться в знакогенератор отдельной процедурой. В пятом бите содержится признак того, что в памяти находится только часть шрифта
  • 4, 5-й байты - смещение шрифта в памяти (младшая часть адреса).
  • 6, 7-й байты - сегмент шрифта в памяти (старшая часть адреса).

  Таблица шрифтов FontTable может содержать от одного до 16-ти шрифтов в зависимости от конфигурации драйвера.

  Теперь рассмотрим, что происходит после того, как в компьютере возникает 10-е прерывание.

  Запомним значение регистров AX и BX (номера функции и подфункции прерывания 10h). Это может понадобиться для получения необходимой нам информации от VIDEOBIOS.

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

  Бросается в глаза некоторая нелогичность подхода. Сначало отрабатывает VIDEOBIOS, а затем наш драйвер. Двойная работа? Нет! Довольно сложно обеспечить совметимость со ВСЕМИ VIDEOBIOS. Ведь кроме того, что они разные в адаптерах разных фирм, у них меняется и железо и BIOS. А мы при этом сокращаем код своего обработчика и обеспечиваем его универсальность. Скорость же отработки драйвера такова, что никак не сказывается на работе прикладных программ.
  Итак, после отработки 10-го прерывания, управление получает наш обработчик. Мы должны проанализировать, что происходило и сделать соответствующие "телодвижения".

  Для нас важны следующие моменты:

  1. запрос информации о шрифтах;
    Если в переменной SubFunction находится значение 1130h, то, производился запрос адреса того или иного шрифта. Следовательно мы должны скорректировать значения в регистрах ES:BP таким образом, чтобы в них оказалась ссылка на руссифицированный шрифт.
      Делается это следующим образом: зная, какое число находилось в регистре BH, мы последовательно просматриваем всю таблицу обслуживаемых шрифтов. В описании каждого шрифта смотрим на байт FontCall и проверяем, не совпадает ли это значение с числом, которое передавалось в регистре BH. Если такого числа нет, то значения регистров ES:BP остаются неизменными. Если такой шрифт представлен необходимо проверить полностью ли или частично он представлен в памяти. Если шрифт представлен полностью регистры ES:BP корректируются так, чтобы указывать на соответствующий руссифицированный шрифт. При этом правильно обрабатывается ситуация частичной загрузки руссифицированных шрифтов.
  2. присутствие драйвера в памяти;
    Если в регистре BX передается значение 0FFAAh, а такое значение не используется в стандартных VIDEOBIOS, то мы считаем, что драйвер уже загружен. При этом в регистре AX возвращается число, получающееся из 0FFAAh после применения к нему операции исключающее ИЛИ, а в регистре ES - сегмент расположения драйвера в памяти. Это позволит нам в дельнейшем выполнять реинициализацию.
  3. активизация русских шрифтов при переключении видеорежимов.
    Если содержимое регистра AX не удовлетворяет требованиям пунктов 1 и 2, управление передается на метку PushAX. Регистр AX запоминается в стеке для того, чтобы обеспечить при возврате в систему его исходное значение, чем самым достигается прозрачность работы драйвера.

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

  • 0h - установка нового режима монитора:
  • 11h - работа с функциями знакогенератора. При этом в регистре AL не должно находиться одно из следующих значений:
    • 0h - загрузка знакогенератора пользователя;
    • 3h - установка блоков знакогенератора;
    • 10h - загрузка знакогенератора пользователя;
    • 20h, 21h- установка знакогенератора пользователя в графическом режиме.

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

  Как же нам отличить текстовый режим от графического? Чем они отличаются друг от друга? Для решения этой задачи приходится использовать несколько предположений. Рассмотрим этот вопрос поподробнее.

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

  Ура! Получилось. Но оказалось, что честно себя ведут далеко не все VIDEOBIOS. Раз не работает корректно VIDEOBIOS надо попробовать получить информацию о состоянии адаптера через какие-либо порты видеоадаптера.

  Находим в описании, что в порте 3CFh в регистре 6 в младшем бите содержится информация о том, текстовый или графический режим включен. Второй раз ура!

  И второй раз рано радовались. Оказалось, как и в первом случае, хотя и существенно реже, есть видеоадаптеры которые можно заставить выдавать чушь вместо нормальной информации. Примером такой программы оказалась DOS SHELL 5.0. К тому же для адаптеров EGA (1992 году было еще актуально) портов с такой информацией не было. Пришлось поднапрячься и сделать такое наблюдение. В области данных BIOS по адресу 0400h:004Ch расположенно слово, в котором содержится размер видео памяти, необходимый для организации текущего видеорежима (RegenSize). В текстовом режиме каждый символ на экране занимает 2 байта видеопамяти. В одном находится код символа, в другом его атрибут(цвет, мерцание). Поэтому объем памяти, требуемый для этого видеорежима определяется по формуле:

Код (Text):
  1.  
  2. П=2*К*С, где К - количество колонок, С - количество строк.
  3.  

  Например, для текстового режима 3 - 80x25 необходимо 2*80*25=4000 байтов памяти. Эта величина, округленная до числа равного степени двойки (4096), и записана в слове RegenSize. Объем памяти для графического режима можно расчитать по формуле:

Код (Text):
  1.  
  2. П=Ф*К*С,
  3.  

  где Ф - размерность матрицы, описывающий символ в данном режиме(высота в пикселах), К - количество колонок, С - количество строк.

  Например, для графического режима 6 (640x200) для вывода текста используется шрифт размером 8x8, и считается, что на экране умещается 80 колонок, и 25 строк. Тогда необходимый объем памяти равен 8*25*80=16000. Округлив до степени двойки получаем 16386.

  Отсюда получаем алгоритм определения типа режима:

  • определив количество строк и колонок данного режима;
  • вычислить необходимый объем памяти. умножить его на 4;
  • сравнить полученное значение со значением в переменной RegenSize.

  Если результат превышает RegenSize - режим текстовый.

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

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

Код (Text):
  1.  
  2. 0...3,7 - текстовые режимы;
  3. 4...6,8,0Dh...13h - графические режимы.
  4.  

  Если включается какой-либо из расширенных режимов - приводится в действие одна из вышеописанных процедур, которая и определяет в каком режиме мы находимся.

  Теперь, когда тип режима определен можно провести активизацию шрифтов. Проще всего это сделать в графическом режиме. Для этого надо установить вектор 43h на начало одного из шрифтов, находящегося в теле драйвера.

  В драйвере это происходит передачей управления на метку GraphMode. Там мы выясняем загружен ли в память требуемый шрифт и, в зависимости от состояния регистров BH(размер шрифта), и BL(основной или альтернативный) соответствующим образом настраиваем ES:BP. В данной реализации это делается непосредственной установкой регистров, так как функция VIDEOBIOS 11h с подфункцией 21h работает не на всех адаптерах.

  Для текстового режима все несколько сложней. Типичный текстовый режим 3 характерезуется параметрами 80 колонок, 25 строк, 16 цветов. Символы на экране формируются аппаратным образом, матрицы символов - программируемым знакогенератором. Знакогенератор находится во второй битовой плоскости видеоадаптера. Объем памяти отводимой этой плоскости таков, что в нем могут одновременно храниться до восьми знакогенераторов одновременно. Для каждого символа зарезервированно 32 байта. Максимальный размер шрифта при этом - 8x32. Количество знакогенераторов, которые могут одновременно отображаться на экране зависит от количества цветов. Если адаптер использует все 16 цветов возможна работа только с одним знакогенератором, если же пожертвовать половиной цветов, то можно использовать два знакогенератора, а это 512 символов одновременно. По такому пути пошли разработчики Laptop и Notebook. На ЖКИ экранах сложно (1992 год!) получить 16 градаций яркости, поэтому используют 8 градаций и 512 символов. Кроме этого горизонтальное разрешение экрана в этом режиме не 640 точек, 720. Таким образом, на символ отводится не 8 пикселей, а 9. Это приводит к тому, что дополнительно появляются еще два часто используемых шрифта (9x14, 9x16). Причем эти шрифты содержат не все символы, а только широкие. Алтернативные шрифты имеют структуру отличающуюся от структуры стандартных шрифтов. Первый байт - код символа; далее байты описывающие саму матрицу символа; затем повторение для следующего символа и т.д., пока байт кода символа не станет равным нулю.

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

  Описываемый драйвер полностью реализует данный алгоритм и поддержку альтернативных шрифтов.

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

  Для того, чтоб справиться с этой проблемой было принято решение загружать знакогенератор во вторую видеоплоскость адаптера напрямую через соответствующий порт. Так еще раз (определение типа режима см. выше) подтвердился парадокс IBM PC: совместимость драйверов и BIOS выше на уровне программирования портов, чем на уровне программирования посредством функций прерывания BIOS.

  Рассмотрим кратко как происходит этот процесс.

  Битовая плоскость видеоадаптера становится доступной после соответствующего программирования через определенные порты. Последовательность действия следующая:

  • программируем порт 3C4h
    • в регистр 0 заносим 1 (синхронный сброс);
    • в регистр 2 заносим 4 (CPU пишет только плоскость 2);
    • в регистр 4 заносим 7 (включение последовательной адресации);
    • в регистр 0 заносим 3 (очистка синхронного сброса).
  • программируем графический контроллер (3CEh)
    • в регистр 4 заносим 2 (чтение плоскости 2);
    • в регистр 5 заносим 0 (отмена черезстрочной адресации);
    • в регистр 6 заносим 0 (начало плоскости с 0A000h:0000).

  После этого ищем нужный шрифт и загружаем его процедурой SetFontAddrESBP. Формирование знакогенератора осуществляется процедурой LoadCharSet. Эта процедура может вызываться не один раз в зависимости от того , весь шрифт или только его часть необходимо корректировать. После загрузки основного шрифта с помощью чтения порта 3C4h выясняем надо ли догружать альтернативные шрифты. Если соответствующий признак есть догружаем необходимые символы из шрифтов 9x14 и 9x16.

  После загрузки нужно закрыть битовую плоскость 2. Делается это процедурой BitPlaneClose.

  Особым образом приходится руссифицировать 35/50 строчные режимы. Для этого приходится перехватывать процесс загрузки пользовательского шрифта с помощью подфункции 10h функции 11h. Управление передается на метку Set10Flag. В регистре AL устанавливается признак того, что при записи символов необходимо дополнять сверху и снизу пустыми строками. Далее вызывается процедура LoadCharSet, которая корректирует русские символы уже загруженного шрифта 8x10.

  В заключение надо сказать, что обработчик 10-го прерывания - это основная часть руссификатора, но не единственная. Обработчик прерывания выполнен перемещаемым, что позволяет при формировании резидентной части размещать его в более младших адресах, что позволяет экономить память за счет префикса программного сегмента(PSP). Реализованна также возможность перемещать этот обработчик в HMA(High Memory Area). В этом случае драйвер занимает в памяти 160 байт. Перед тем, как стать резидентом драйвер тестирует адаптер VGA, автоматически определяет какие шрифты требуют обслуживания, составляет таблицу шрифтов FontTable и обеспечивает ПОЛНОЕ обслуживание подавляющего большинства модификаций видеоадаптеров. При этом пользователь может отказаться от автоматической диагностики и директивно сообщать драйверу через командную строку какие шрифты можно обслуживать. (Для WinNT нужно указывать полный путь доступа к шрифтам!) При этом возможна существенная экономия памяти.

  Дистрибутив находится в прилагаемом архиве. Там же есть клавиатурный драйвер NKD(не менее идеальный ;-). А так же великолепная утилитка VACULA, которая не только расскажет вам все об видеоадаптере, но и позволит поиграться с его режимами, оценив его возможности.

  Литература:
  Фролов А.В., Фролов Г.В. Программирование видеоадаптеров CGA, EGA, VGA. Москва, Диалог-МИФИ, 1992 г.
  Programmers Guide to PC & PS/2 Video Systems. Richard Wilton. MicroSoft Press, 1987. © Андрей Бордачев & Александр Гайко


0 1.539
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532