7. Регистры видеоадаптеров EGA и VGA

В этой главе мы опишем все основные регистры видеоадаптеров EGA и VGA, которые могут быть полезны при написании программ. Необходимо указать, что хотя программирование видеоадаптеров на уровне регистров позволяет увеличить скорость работы программ и решить некоторые задачи, которые нельзя решить при помоши функций BIOS, это может вызвать ряд проблем при переносе ваших программ на другие машины. Дело в том, что не все адаптеры совместимы на уровне регистров. Например, оригинальный видеоадаптер CGA создан на основе микросхемы Motorola 6845, а видеоадаптеры EGA и VGA используют более совершенный аналог этой микросхемы. Хотя EGA и VGA имеют регистры, соответствующие регистрам CGA, некоторые из них располагаются по другим адресам и могут выполнять какие-либо дополнительные функции. Кроме того, в каждом новом видеоадаптере расширяется набор используемых регистров.

Существуют модели видеоадаптеров EGA, VGA, Super VGA поддерживающие режим совместимости, когда они эмулируют адаптеры более низкого уровня (например MDA, Hercules, CGA). При этом эмуляция обычно происходит на уровне регистров, что гарантирует полную их совместимость. Режимы совместимости, если они поддерживаются, описаны в руководстве этого видеоадаптера.

На практике, для управления видеоадаптерами, рекомендуется преимущественно использовать функции BIOS. Это избавит вас от неприятных минут, когда ваша работающая программа, при переносе на другую машину перестанет правильно выполняться.

7.1. Краткий обзор

Видеоадаптеры EGA и VGA содержат большое количество регистров. EGA имеет около шестидесяти регистров, а VGA и Super VGA еще больше. Большая часть регистров EGA доступна только для записи, что создает определенные проблемы, особенно для мультизадачных систем. Из-за этого некоторые фирмы-изготовители видеоадаптеров выпускают совместимые с EGA платы видеоадаптеров, для регистров которых разрешена также и операция чтения.

В новых адаптерах VGA и Super VGA практически все регистры доступны как для записи так и для чтения.

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

Доступ к большинству регистров видеоадаптеров осуществляется в два этапа: через один порт ввода/вывода выбирается номер интересующего вас регистра, а затем через другой порт ввода/вывода осуществляется обмен данными. Это позволяет сэкономить большое число портов процессора.

Заметим, что в различных режимах работы видеоадаптера, его регистры могут иметь различные адреса. В таблицах 8.1 и 8.2 приведен список адресов регистров в монохромных и цветных режимах.

Адрес Регистр
3C2 регистр определения различных режимов работы (Miscellaneous Output Register - MOR);
  регистр состояния 0 (Input Status Register 0 - ISR0)
3BA регистр управления дополнительным устройством (Feature Control Register)
  регистр состояния 1 (Input Status Register 1 - ISR1)
3BB регистр очистки защелки светового пера (Clear Light Pen Latch Register- CLPLR)
3BC установка защелки светового пера (Set Light Pen Latch Register - SLPLR)
3C4, регистры синхронизатора
3C5 (Sequencer Register's - SR)
3B4, регистры контроллера ЭЛТ
3B5 (CRT Controller Register's - CRT_CR)
3CA, регистры графического контроллера
3CC, (Graphics Controller Register's - GCR)
3CE,  
3CF  
3C0 регистр контроллера атрибутов (Attribute Controller Register - ACR)
3C3 регистр разрешения работы системы VGA (VGA Enable Register - VGA_ER)
3C6, регистры цифро-аналогового преобразователя (ЦАП) VGA
3C7, (VGA Video DAC Register - VGA_DAC_R)
3C8,  
3C9  

Таблица 8.1 Карта портов ввода/вывода (монохромный режим).

Адрес Регистр
3C2 регистр определения различных режимов работы (Miscellaneous Output Register - MOR);
  регистр состояния 0 (Input Status Register 0 - ISR0).
3DA регистр управления дополнительным устройством Feature Control Register)
  регистр состояния 1 (Input Status Register 1 - ISR1)
3DB регистр очистки защелки светового пера (Clear Light Pen Latch Register- CLPLR)
3DC установка защелки светового пера (Set Light Pen Latch Register - SLPLR)
3C4, регистры синхронизатора
3C5 (Sequencer Register's - SR)
3D4, регистры контроллера ЭЛТ
3D5 (CRT Controller Register's - CRT_CR)
3CA, регистры графического контроллера
3CC, (Graphics Controller Register's - GCR)
3CE,  
3CF  
3C0 регистр контроллера атрибутов (Attribute Controller Register - ACR)
3C3 регистр разрешения работы системы VGA (VGA Enable Register - VGA_ER)
3C6, регистры ЦАП VGA
3C7, (VGA Video DAC Register - VGA_DAC_R)
3C8,  
3C9  

Таблица 8.2 Карта портов ввода/вывода (цветной режим).

Адресное пространство видеопамяти также различается в разных режимах работы видеоадаптера. В таблице 8.3 приведены адреса памяти в зависимости от режима работы.

Номер режима работы Адрес памяти
0,1,2,3,4,5,6 B000:8000-B000:FFFF
7 B000:0000-B000:7FFF
0Dh,0Eh,0Fh,10h,11h,12h,13h A000:0000-A000:FFFF

Таблица 8.3 Распределение видеопамяти в различных режимах.

Функция Get_Seg_Vmem, приведенная ниже, при помощи функции 0Fh прерывания INT 10h определяет текущий режим работы видеоадаптера и возвращает сегмент начала видеопамяти. Если видеоадаптер работает в нестандартном режиме, функция возвращает ноль.

#include "sysp.h"

unsigned Get_Seg_Vmem(void) {

   unsigned char   mode;
   unsigned seg_address;

   // определяем текущий режим видеоадаптера

   _asm {
      mov ah,0Fh
      int 10h
      mov mode,al
   }

   // если видеоадаптер находится в режимах 0, 1, 2, 3,
   // 4, 5, 6 то видеопамять начинается с сегмента B800h

   if((mode >= 0) && (mode <= 6))
      seg_address = 0xB800;

   // если видеоадаптер находится в режиме 7,
   // то видеопамять начинается с сегмента B000h

   else if(mode == 7)
      seg_address = 0xB000;

   // если видеоадаптер находится в режимах 0Dh - 13h,
   // то видеопамять начинается с сегмента A000h

   else if((mode >= 0x0D) && (mode <= 0x13))
      seg_address = 0xA000;

   // если видеоадаптер не находится в стандартном режиме
   // возвращаем ноль

   else seg_address = 0x0;

   return(seg_address);
}

Ниже подробно рассмотрены регистры видеоадаптеров EGA и VGA. Регистры сгруппированы по признаку выполняемых ими функций. Мы выделили следующие группы регистров:

7.2. Внешние регистры

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

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

Регистр определения различных режимов работы
(Miscellaneous Output Register - MOR)

В видеоадаптере EGA данный регистр доступен по адресу 3C2h только для записи. Видеоадаптер VGA позволяет также считать содержимое регистра, но уже по адресу 3CCh.

Ниже приведено описание отдельных битов регистра MOR:

Улучшенный цветной дисплей и аналоговый дисплей используют биты D7 и D6 для определения скорости сканирования. Таблица 8.4 содержит допустимые значения для этих битов.

D7   D6    EGA                  VGA
0    0     200 линий            не используется
0    1     350 линий            350 линий
1    0     не используется      400 линий
1    1     не используется      480 линий

Таблица 8.4 Соответствие полярности синхронизации и вертикальной разрешающей способности.

D3   D2    Режим
0    0     640 столбцов (или 320 столбцов)
0    1     720 столбцов
1    0     внешний генератор (доступен через разъем
           дополнительного устройства)
1    1     зарезервированно 

Таблица 8.5 Установка битов D3 и D2.

Данные, первоначально записываемые BIOS в этот регистр зависят от режима работы видеоадаптера:

Режим 0-6,0Dh,0Eh 7 Fh 10h
Содержимое регистра 23h 0A6h 0A2h 0A7h

Регистр управления дополнительным устройством
(Feature Control Register - FCR)

Для видеоадаптера EGA биты D1 и D0 данного регистра передают сигналы на разъем дополнительного устройства, соответственно, на линии FC0 (вывод 21) и FC1 (вывод 20) разъема. Этот регистр не используется для VGA, но бит D3 должен содержать ноль.

Адрес порта ввода/вывода данного регистра зависит от режима видеоадаптера. В монохромном режиме адрес порта 3BAh, а в цветном - 3DAh.

Регистр доступен только для записи, но VGA позволяет прочитать его содержимое через порт 3CAh.

Регистр состояния 0
(Input Status Register 0 - ISR0)

Данный регистр, имеющий адрес 3C2h, доступен только для чтения.

Регистр позволяет получить различную информацию от видеоадаптера EGA. Формат регистра приведен ниже:

3-D0   Не используются.
4   Состояние переключателей.
5   Бит 0 дополнительного устройства (FEAT0).
6   Бит 1 дополнительного устройства (FEAT1).
7   Бит прерывания от ЭЛТ.
Бит D3 Бит D2 Считываемый переключатель
0 0 переключатель 1
0 1 переключатель 2
1 0 переключатель 3
1 1 переключатель 4

Таблица 8.6 Установка битов D3 и D2.

Если бит D4 установлен в единицу, то переключатель, заданный битами D2 и D3 регистра определения различных режимов работы, находится в положении ON. Если бит сброшен в ноль, то переключатель находится в положении OFF.

Для EGA эти четыре переключателя определяют, какой дисплей используется с видеоадаптером и какой дополнительный видеоадаптер может быть подключен к компьютеру (cм. главу "Системы с двумя дисплеями").

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

      4 3 2 1
     _________   Первичный: EGA с цветным дисплеем (40х25).
ON   ¦Ё     Ё¦   
OFF  ¦  Ё Ё  ¦   Дополнительный: MDA.
     _________   


      4 3 2 1
     _________   Первичный: EGA с цветным дисплеем (80х25).
ON   ¦Ё      ¦
OFF  ¦  Ё Ё Ё¦   Дополнительный: MDA.
     _________


      4 3 2 1
     _________   Первичный: EGA с улучшенным цветным дисплеем.
ON   ¦  Ё Ё Ё¦
OFF  ¦Ё      ¦   Дополнительный: MDA.
     _________


      4 3 2 1
     _________   Первичный: EGA с улучшенным цветным дисплеем.
ON   ¦  Ё Ё  ¦
OFF  ¦Ё     Ё¦   Дополнительный: MDA.
     _________


      4 3 2 1
     _________   Первичный: EGA с монохромным дисплеем.
ON   ¦  Ё   Ё¦
OFF  ¦Ё   Ё  ¦   Дополнительный: CGA с цветным дисплеем
     _________             (40х25).


      4 3 2 1
     _________   Первичный: EGA с монохромным дисплеем.
ON   ¦  Ё    ¦
OFF  ¦Ё   Ё Ё¦   Дополнительный: CGA с цветным дисплеем
     _________             (80х25)


      4 3 2 1
     _________   Первичный: MDA.
ON   ¦Ё Ё Ё Ё¦
OFF  ¦       ¦   Дополнительный: EGA с цветным дисплеем
     _________             (40х25).


      4 3 2 1
     _________   Первичный: MDA.
ON   ¦Ё Ё Ё  ¦
OFF  ¦      Ё¦   Дополнительный  EGA с цветным дисплеем
     _________             (80х25).


      4 3 2 1
     _________   Первичный: MDA.
ON   ¦Ё Ё   Ё¦
OFF  ¦    Ё  ¦   Дополнительный: EGA с улучшенным цветным
     _________             дисплеем.


      4 3 2 1
     _________   Первичный: MDA.
ON   ¦Ё Ё    ¦
OFF  ¦    Ё Ё¦   Дополнительный: EGA с улучшенным цветным
     _________             дисплеем.


      4 3 2 1
     _________   Первичный:  CGA с цветным дисплеем 40х25
ON   ¦Ё   Ё Ё¦
OFF  ¦  Ё    ¦   Дополнительный: EGA с монохромным дисплеем.
     _________


      4 3 2 1
     _________   Первичный:  CGA с цветным дисплеем 80х25
ON   ¦Ё   Ё  ¦
OFF  ¦  Ё   Ё¦   Дополнительный: EGA с монохромным дисплеем.
     _________



VGA использует бит D4 для определения типа используемого дисплея (цветной или монохромный).

Особенно подчеркнем, что положение этих переключателей может быть считано также из байта памяти с адресом 0000:0488h:

Следующая программа считывает значение регистра состояния 0, выделяет биты D5 и D6, поступающие с разъема дополнительного устройства и отображает значения этих битв на экране диспля.

// чтение битов дополнительного устройства
// (битов D5 и D6 регистра состояния 0)

#include <stdio.h>
#include "sysp.h"
#include "sysgraph.h"

void main(void) {
   int            feature_bits;
   unsigned       crt_address;
   BIOS_VAR _far  *bios_var_ptr;

   // читаем содержимое регистра состояния 0 и выделяем 
   // биты D5 и D6

   feature_bits = (ReadReg(0x3C2) & 0x60) >> 5;

   printf("Биты дополнительного устройства: %x\n", feature_bits);
}

Функция ReadReg, используемая в программе, описана ранее.

Регистр состояния 1
(Input Status Register 1 - ISR0)

Регистр позволяет получить различную информацию о состоянии видеоадаптера.

Для видеоадаптера CGA Содержимое регистраможно считать через порт, имеющий адрес 3DAh. Для адаптеров EGA и VGA данный регистр, имеет адрес 3BAh в монохромных режимах и адрес 3DAh - в цветных. Регистр состояния 1 доступен только для чтения.

Приведем формат регистра состояния 1:

D0   Бит разрешения отображения.
D1   Бит тригера светового пера.
D2   Бит переключателя светового пера.
D3   Бит обратного вертикального хода 
D5-D4   Диагностические биты.
D7-D6   Биты не используются.

Регистр разрешения  Регистр состояния 1
цветового слоя          
D5  D4              D5                 D4
0   0               красный            синий
0   1               второй красный     второй зеленый
1   0               второй синий       зеленый
1   1               не используется    не используется

Таблица 8.7 Считывание диагностических битов EGA.

Таблица 8.8 показывает, как можно считать два из восьми цветовых сигналов вырабатываемых контроллером атрибутов видеоадаптера VGA:

Регистр разрешения  Регистр состояния 1
цветового слоя                  
D5  D4              D5                 D4
0   0               P2                 P0
0   1               P5                 P4
1   0               P3                 P1
1   1               P7                 P6

Таблица 8.8 Считывание диагностических битов VGA.

Фактически биты D5 и D4 представляют возможность прочитать содержимое регистров таблицы цветовой палитры для видеоадаптера EGA. Следует отметить, что некоторые адаптеры, совместимые с EGA, не поддерживают эти биты. По-этому их использование может наложить некоторые ограничения на работу программы.

Биты D2 и D1 относятся к управлению световым пером. Для видеоадаптеров VGA и Super VGA эти биты не используются, так как такие адаптеры не поддерживают световое перо.

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

Ниже представлена программа, использующая функцию WaitVert, для определения частоты кадров. Функция WaitVert задерживает выполнение программы до начала обратного вертикального хода луча.

// программа определения частоты кадров

#include   <time.h>
#include   <stdio.h>
#include   <bios.h>

#define   NUM   200

void WaitVert(void);

void main() {

   time_t   t_start, t_end;
   int      i;
   float   fr;

// определяем время запуска процесса

   t_start = clock();

// ожидаем когда процесс вертикальной разверти выполнится NUM раз

   for(i = 0; i < NUM; i++) WaitVert();

// определяем время завершения процесса

   t_end = clock();

// вычисляем частоту кадров

   fr = NUM/(((float)t_end - t_start) / CLK_TCK);
   printf( "\nЧастота кадров = %4.1f \n", (float)fr );
}


/**
*.Name   WaitVert
*
*.Title   Определение начала обратного вертикального хода луча.
*
*.Descr   Функция ожидает начало обратного вертикального хода луча.
*      Использование функции позволяет синхронизировать работу 
*      программы с периодами работы видеоадаптера.
*
*.Proto   void WaitVert(void)
*
*.Params   Отсутствуют.
*
*.Return   Не используются.
*
*.Sample   get_vert.c
**/
void WaitVert(void) {
   unsigned count;
   _asm {

; устанавливаем регистр es на сегмент с нулевым адресом

      mov   ax,0h
      mov   es,ax

; определяем адрес порта индексного регистра
;  контроллера ЭЛТ (3B4h/3D4h в 
; зависимости от режима работы видеоадаптера -
;  монохромный или цветной)

      mov   dx,es:[463h]

; вычисляем адрес порта регистра состояния 1 (ISR1);
; это достигается добавлением к адресу порта
; индексного регистра 6, так как адрес  порта регистра
; состояния 1 равен 3BAh или 3DAh в зависимости от режима
; работы видеоадаптера (монохромный или цветной)

      add   dl,6

; читаем содержимое порта регистра состояния 1

      in   al,dx

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

      test   al,8
      jz   wait_on

   wait_off:

      in   al,dx

; тестируем бит D3

      test   al,8

; ожидаем конец обратного вертикального хода луча

      jnz   wait_off

   wait_on:

      in   al,dx
      test   al,8

; ожидаем начало обратного вертикального хода луча

      jz   wait_on
; 
;  здесь могут распологаться операции, которые необходимо 
;  выполнить  во время обратного  вертикального хода луча 
;
   }
}

Регистр разрешения работы системы VGA
(VGA Enable Register - VGA_ER)

Данный регистр имеет адрес 3С3h. Регистр используется только для VGA. Биты D1-D7 зарезервированны, а бит D0 управляет работой VGA. Если он равен нулю, то запрещен доступ к видеопамяти и портам ввода/вывода видеоадаптера (кроме самого регистра VGA_ER).

Регистр сброса триггера-защелки светового пера (Light Pen Latch Reset Register - LPLRR)

Доступ к регистру производится через порт 3DBh для цветного режима и через 3BBh для монохромного. Регистр используется только видеоадаптерами CGA и EGA. Любая операция записи (OUT) в этот регистр сбрасывает триггер-защелку светового пера.

Регистр установки триггера-защелки светового пера (Light Pen Latch Set Register - LPLRR)

Доступ к регистру производится через порт 3DCh для цветного режима и через 3BCh для монохромного. Регистр используется только видеоадаптерами CGA и EGA. Любая операция записи (OUT) в этот регистр вызывает установку триггера-защелки светового пера.

7.3. Регистры контроллера ЭЛТ

Регистры контроллера ЭЛТ управляют сигналами синхронизации, необходимыми для формирования растра, определяют формат данных на экране, форму курсора, а также для видеоадаптеров CGA и EGA управляют световым пером.

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

Назначение и формат ряда регистров контроллера ЭЛТ различаются для видеоадаптеров EGA, VGA и для видеоадаптеров MDA, Hercules и CGA. Кроме того, они могут различаться и для видеоадаптерв одного типа, но разных фирм-производителей. В результате могут возникнуть проблем совместимости на компьютерах, оснащенных другим видеоадаптером. Если вы все-таки решитесь их перепрограммировать, то обращайтесь к описанию вашего адаптера.

Регистры контроллера ЭЛТ составляют самую многочисленную группу регистров видеоадаптеров. Для видеоадаптеров EGA и VGA группа содержит 24 регистра.

В таблице 8.9 приведен список всех регистров контроллера ЭЛТ и их индексы, используемые для доступа к ним.

Индекс Регистр контроллера ЭЛТ
0 общая длина линии горизонтальной развертки (Horizontal Total Register - HTR)
1 длина отображаемой части горизонтальной развертки (Horizontal Display Enable End Register - HDER)
2 начало импульса гашения луча горизонтальной развертки (Start Horizontal Blank Register - SHBR)
3 конец импульса гашения луча горизонтальной развертки (End Horizontal Blank Register - EHBR)
4 начало импульса горизонтального обратного хода луча (Start Horizontal Retrace Register - SHRR)
5 конец импульса горизонтального обратного хода луча (End Horizontal Retrace Register - EHRR)
6 число горизонтальных линий растра (Vertical Total Register - VTR)
7 дополнительный регистр (Overflow Register - OVR)
   
8 предварительная установка горизонтальной развертки (Preset Row Scan Register - PRSR)
9 высота символов текста (Max Scan Line Register - MSLR)
0Ah начальная линия курсора (Cursor Start Register - CSR)
0Bh конечная линия курсора (Cursor End Register - CER)
0Ch старший байт начального адреса (Start Address Register - SAR, high byte)
0Dh младший байт начального адреса (Start address Register - SAR, low byte)
0Eh старший байт позиции курсора (Cursor Location Register - CLR, high byte)
0Fh младший байт позиции курсора (Cursor Location Register - CLR, low byte)
10h начало обратного вертикального хода луча (Vertical Retrace Start Register - VRSR)
11h конец обратного вертикального хода луча (Vertical Retrace End Register - VRER)
10h старший байт адреса светового пера (Light Pen Address Register - LPAR, high byte)
11h младший байт адреса светового пера (Light Pen Address Register - LPAR, low byte)
12h начало гашения вертикальной развертки (Vertical Display End Register - VDER)
13h логическая ширина экрана (Offset Register - OFR)
14h положение подчеркивания символа (Underline Location Register - ULR)
15h начало импульса гашения вертикальной развертки (Start Vertical Blank Register - SVBR)
16h конец импульса гашения вертикальной развертки (End Vertical Blank Register - EVBR)
17h управление режимом (Mode Control Register - MCR)
18h регистр сравнения линий (Line Compare Register - LCR)

Таблица 8.9 Регистры EGA и VGA контроллера ЭЛТ.

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

У видеоадаптеров MDA и Hercules индексный порт имеет адрес 3B4h, а порт данных - адрес 3B5h. Для CGA адреса портов другие. Индексный порт имеет адрес 3D4h, а порт данных - адрес 3D5h. Адреса портов контроллера ЭЛТ для видеоадаптеров EGA и VGA зависят от режима работы видеоадаптера (монохромный или цветной). В монохромном режиме адрес порта индексного регистра равен 3B4h, а регистра данных - 3B5h. В цветном режиме адреса соответственно равны 3D4h и 3D5h:

Тип адаптера Адрес порта индексного регистра Адрес порта регистра данных
MDA и Hercules 3B4h 3B5h
CGA 3D4h 3D5h
EGA и VGA (монохромный режим) 3B4h 3B5h
EGA и VGA (цветной режим) 3D4h 3D5h

Таблица 8.10 Адреса портов индексного регистра и регистра данных.

Использование видеоадаптерами различного адресного пространства для портов контроллера ЭЛТ необходимо, чтобы создать возможность одновременного подключения к компьютеру двух видеоадаптеров.

Адрес порта индексного регистра записан в области переменных видеофункций BIOS, по адресу 0000:0463. Приведем фрагмент программы, определяющей адрес индексного порта контроллера ЭЛТ:

; устанавливаем es на нулевой сегмент

xor   ax,ax
mov   es,ax

; записываем в dx адрес порта индексного регистра контроллера ЭЛТ

mov   dx,es:[463]

Так как адреса портов индексного регистра и регистра данных контроллера ЭЛТ является смежными, то адрес порта регистра данных можно получить, прибавив единицу к адресу порта индексного регистра.

Общая длина линии горизонтальной развертки (Horizontal Total Register - HTR) (индекс 0)

Этот регистр относится к группе регистров контроллера ЭЛТ, которые управляют генерацией синхросигналов для дисплея. Необходимость модификации данной группы регистров возникает только при программировании нестандартных режимов работы видеоадаптера.

Кроме регистров, контроллер ЭЛТ использует еще два внутренних счетчика для управления синхросигналами. Счетчик числа горизонтальных знакомест экрана увеличивается при выводе очередного сивола на экран (регенерация экрана) и сбрасывается, когда его значение становится равным регистру общей длины линии горизонтальной развертки. Счетчик числа горизонтальных линий развертки увеличивается после окончания обратного горизонтального хода луча и сбрасывается, когда достигает величины, хранящейся в регистре числа горизонтальных линий растра.

Регистр HTR определяет число знакомест на одной линии сканирования, включая обратный ход луча и рамку экрана.

Для EGA число знакомест по горизонтали будет на два, а для VGA на пять больше, чем значение, хранящееся в регистре.

При установке режима работы видеоадаптера, BIOS загружает в регистр HTR следующие значения:

Режим 0,1,4,5,D 2,3,6,E 7, F 10
Содержимое регистра 37h 70h 60h 5Bh  

Если используется улучшенный цветной дисплей, то эти значения отличаются:

Режим 0,1 2,3
Содержимое регистра 2Dh 5Bh

Длина отображаемой части горизонтальной развертки (Horizontal Display Enable End Register - HDER) (индекс 1)

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

При установке режима работы видеоадаптера, BIOS загружает в регистр HDER следующие значения:

Режим 0,1,4,5,D 2,3,6,7,E,F,10
Содержимое регистра 27h 4Fh

Начало импульса гашения луча горизонтальной развертки (Start Horizontal Blank Register - SHBR) (индекс 2)

Регистр используется видеоадаптерами EGA и VGA для определения начала импульса гашения луча горизонтальной развертки.

При установке режима работы видеоадаптера, BIOS загружает в регистр SHBR следующие значения:

Режим 0,1,4,5,D 2,3 6,E 7,F 10
Содержимое регистра 2Dh 5Ch 59h 56h 53h

Если используется улучшенный цветной дисплей, то эти значения отличаются:

Режим 0,1 2,3
Содержимое регистра 2Bh 53h

Конец импульса гашения луча горизонтальной развертки (End Horizontal Blank Register - EHBR) (индекс 3)

При установке режима работы видеоадаптера, BIOS загружает в регистр EHBR следующие значения:

Режим 0,1,4,5,D 2,3 6,E 7 F 10
Содержимое регистра 37h 2Fh 2Dh 3Ah 1Fh 17h

Если используется улучшенный цветной дисплей, то эти значения отличаются:

Режим 0,1 2,3
Содержимое регистра 2Dh 37h

Начало импульса горизонтального обратного хода луча (Start Horizontal Retrace Register - SHRR) (индекс 4)

Для EGA и VGA регистр задает момент начала импульса горизонтального обратного хода луча.

При установке режима работы видеоадаптера, BIOS загружает в регистр SHRR следующие значения:

Режим 0,1 4,5,D 2,3 6,E 7 F,10
Содержимое регистра 31h 30h 5Fh 5Eh 51h 50h

Если используется улучшенный цветной дисплей, то эти значения отличаются:

Режим 0,1 2,3
Содержимое регистра 28h 51h

Конец импульса горизонтального обратного хода луча (End Horizontal Retrace Register - EHRR) (индекс 5)

Число горизонтальных линий растра (VTR) (индекс 6)

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

Для видеоадаптера EGA, регистр VTR содержит девять битов. Девятый бит находится в дополнительном регистре (OVR). Регистр VTR видеоадаптера VGA содержит десять битов. Десятый бит, также как и девятый, расположен в дополнительном регистре (OVR).

Дополнительный регистр (OVR) (индекс 7)

Многие регистры контроллера ЭЛТ, видеоадаптера EGA, имеют по девять, а для VGA и по десять битов. Дополнительный регистр содержит биты, которые не помещаются в восьмиразрядную сетку основных регистров.

Предварительная установка горизонтальной развертки
(Preset Row Scan Register - PRSR) (индекс 8)

Регистр позволяет производить плавную вертикальную прокрутку содержимого экрана дисплея в текстовых режимах (свертку). В графических режимах регистр должен содержать нулевое значение.

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

Рисунок 8.1 Плавная прокрутка экрана.

После установки режима работы видеоадаптера данный регистр содержит нулевое значение.

Высота сиволов текста
(Max Scan Line Register - MSLR) (индекс 9)

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

На следующих двух рисунках показано использование регистра высоты символов текста. На первом рисунке, при использовании шрифта 8х8 пикселов, регистр высоты символов содержит число семь. Для второго рисунка, значение регистра равно тринадцати (используется шрифт 8х14). Как виднно из рисунков, значение регистра на один меньше, чем высота символов отображаемых на экране.

Рисунок 8.2 Изменение высоты символов текста.

Рисунок 8.3 Изменение высоты символов текста.

Начальная линия курсора
(Cursor Start Register - CSR) (индекс 0Ah)

Этот регистр является доступным как для чтения, так и для записи.

Регистр задает линию сканирования символа, в которой начинается курсор. Вместе с регистром конечной линии курсора (CER) он определяет размер и форму курсора.

D4-D0   Начальная линия курсора. (см. рисунок 8.4).
D5   Бит гашения курсора. Реализован только на VGA. Если D5 содержит единицу, курсор гаснет.
D7-D6   Не используются.

При установке режима работы видеоадаптера, BIOS загружает в регистр CSR следующие значения:

Режим 0,1,2,3 4,5,6,D,E,F,10 7
Содержимое регистра 06 00 0Bh

Конечная линия курсора
(Cursor End Register - CER) (индекс 0Bh)

Этот регистр, также как и регистр CSR, является доступным для чтения и для записи.

Регистр задает последнюю линию сканирования символа, в которой кончается курсор.

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

Режим 0,1,2,3 4,5,6,D,E,F,10 7
Содержимое регистра 07 00 0Ch

Изменяя значение регистров начальной и конечной линии курсора, можно менять его положение и размер. На левой части рисунка, приведенного ниже, регистр начальной линии курсора равен 0Ah, регистр конечной линии курсора равен 0Bh. На правой части рисунка значение регистров изменено: регистр начальной линии равен 4, а регистр конечной линии - 5.

Рисунок 8.4 Изменение положения курсора.

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

#include <stdio.h>
#include "sysp.h"
#include "sysgraph.h"

void main(void) {

   char           top = 0, bottom = 0;
   unsigned       crt_address;
   BIOS_VAR _far  *bios_var_ptr;

   // получаем указатель на область переменных видеофункций BIOS

   bios_var_ptr = (BIOS_VAR _far *) FP_MAKE(0x0000, 0x0410);

   // определяем адрес порта индексного регистра контроллера ЭЛТ

   crt_address = bios_var_ptr->crt_address;

   // считываем значения регистров начальной и
   // конечной линии курсора

   // выбираем регистр начальной линии курсора

   WriteReg(crt_address++, 0x0A);

   // считываем значение регистра

   top = (unsigned char) ReadReg(crt_address--);

   // выбираем регистр конечной линии курсора

   WriteReg(crt_address++, 0x0B);

   // считываем значение регистра

   bottom = (unsigned char) ReadReg(crt_address);

   // отображаем на экране значение регистров

   printf("\nРегистр начальной линии курсора содержит %x\n"
          "\nРегистр конечной линии курсора содержит %x\n", 
          top, bottom);

   getch();
}

Регистры начального адреса

Это группа из двух регистров: регистр старшего байта начального адреса (Start Address Register - SAR, high byte) (индекс 0Ch) и регистр младшего байта начального адреса (Start Address Register - SAR, low byte) (индекс 0Dh).

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

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

На рисунке 8.5 показано, как происходит панарамирование экрана при изменении регистров начального адреса. В верхней части рисунка регистры начального адреса содержат ноль. В этом случае видеопамять отображается на экране с начала. В нижней части значение регистров начального адреса увеличено до 77. Как видно из рисунка, в этом случае видеопамять отображается на экране начиная с данных, имеющих смещение 77 от начала видеопамяти. При этом снизу экрана возникает изображение, ранее не помещавщееся на экране.

Рисунок 8.5 Процесс панорамирования экрана.

Регистры начального адреса доступны для чтения и для записи. При установке режима работы видеоадаптера регистры обнуляются.

Регистры, определяющие положение курсора

Два регистра - регистр старшего байта позиции курсора (Cursor Location Register - CLR_h, high byte) (индекс 0Eh) и регистр младшего байта позиции курсора (Cursor Location Register - CLR_l, low byte) (индекс 0Fh) определяют положение курсора на экране (cм. рисунок 8.6). Регистры доступны как для записи, так и для чтения.

Рисунок 8.6 Отображение курсора на экране.

Программа, приведенная ниже считывает значения из регистров позиции курсора (на момент запуска программы) и отображает их на экране дисплея:

// чтение регистра положения курсора

#include <stdio.h>
#include <graph.h>
#include "sysp.h"
#include "sysgraph.h"


void main(void) {

   int            crt_port;
   unsigned char  h_pos, l_pos;
   BIOS_VAR _far  *bios_var_ptr;


   // получаем указатель на область переменных видеофункций BIOS

   bios_var_ptr = (BIOS_VAR _far *) FP_MAKE(0x0000, 0x0410);

   // определяем адрес порта индексного регистра контроллера ЭЛТ

   crt_port = bios_var_ptr -> crt_address;

   // выбираем старший байт регистра положения курсора

   WriteReg(crt_port, 0x0E);

   // считываем значение старшего байта регистра положения курсора

   h_pos = ReadReg(crt_port + 1);

   // выбираем младший байт регистра положения курсора

   WriteReg(crt_port, 0x0F);

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

   l_pos = ReadReg(crt_port + 1);

   printf("\nТекущий адрес курсора %X:%X\n",
            (unsigned char) h_pos, (unsigned char) l_pos );
}

Начало обратного вертикального хода луча (VRSR) (индекс 10h)

Регистр определяет начало обратного вертикального хода луча. Для EGA этот регистр содержит 9, а для VGA - 10 битов. Дополнительные биты расположены в регистре OVR.

Данный регистр доступен только для записи.

Конец обратного вертикального хода луча (VRER) (индекс 11h)

Данный регистр видеоадаптеров EGA и VGA доступен только для записи.

Регистр устанавливает конец обратного вертикального хода луча, а также выполняет некоторые другие функции.

D3-D0   Конец обратного вертикального хода луча.
D4   Сброс вертикального прерывания.
D5   Разрешения вертикального прерывания.
D6   Изменение скорости регенирации экрана. (Только VGA.)
D7   Защита от записи. (Только VGA.)

Регистр адреса светового пера (LPAR)

Это 16-битовый регистр, который имеется в видеоадаптерах CGA и EGA, доступен только для чтения. Регистр LPAR дает возможность определить положение светового пера на экране. Регистр содержащий старший байт адреса светового пера имеет индекс 10h, а регистр содержащий младший байт - индекс 11h.

LPAR сохраняет адрес видеопамяти, которая регенерировалась в момент включения светового пера.

Завершение отображения вертикальной развертки (VDER) (индекс 12h)

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

Для видеоадаптера EGA регистр VDER содержит 9, а для VGA - 10 бит. Девятыи и десятый биты доступны через дополнительный регистр (OVR).

Логическая ширина экрана (OFR) (индекс 13h)

Регистр может быть использован для отображения большего, чем обычно, количества символов в строке. Например, для перевода видеоадаптера в режим отображения 132 симвов в строке.

В графических режимах регистр OFR задает логическую длину (в 16-битных словах или 32-битных двойных словах) горизонтальной линии развертки. Если линия num развертки начинается по адресу adr, то следующая линия развертки num+1 начинается по адресу adr+offset, где offset это значение регистра OFR.

В текстовых режимах регистр содержит смещение между соседними строками текста, заданное в 16-битных словах.

Использование регистраа OFR иллюстрируется рисунком 8.7. На верхней части рисунка видеоадаптер находится в стандартном текстовом режиме с разрешением 80 символов в строке. При этом значение регистра логической ширины экрана равно 40. На нижней части рисунка представлен результат увеличения содержимого регистра до 41. Логическая ширина экрана в этом случае увеличивается до 82 символов в строке. Последние два символа каждой строки не помещаются на экране. Для их отображения надо выполнить горизонтальную свертку экрана.

Рисунок 8.7 Логическая ширина экрана.

Положение подчеркивания символа (ULR) (индекс 14h)

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

Программа, приведенная ниже, использует регистры контроллера ЭЛТ для перевода видеоадаптеров EGA и VGA в нестандартный текстовый режим, имеющий разрешение по вертикали 43 строки. Данная программа переводит видеоадаптер в текстовый режим с разрешением по вертикали 43 символа и затем отображает на экране диспля 43 строки текста.

#include <graph.h>

void Set43Line(void);


void main(void) {

   int   i;

   // устанавливаем текстовый режим, принятый по умолчанию;
   // функция _setvideomode определена в графической библиотеке
   // трансляторов Microsoft Quick C 2.5 и C 6.0 

   _setvideomode(_DEFAULTMODE);

   // перепрограммируем видеоадаптер для отображения 43 строк

   Set43Line();

   // выводим на экран дисплея 43 строки текста

   for(i = 0; i < 43; i++)
      printf("text line number %d\n", i);

   getch();

   // восстанавливаем режим с 25 текстовыми строками   

   _setvideomode(_DEFAULTMODE);
}


/**
*.Name   Set43Line
*
*.Title   Установка текстового режима, использующего 43 строки.
*
*.Descr   Переводит адаптеры EGA и VGA в режим отображения 43 
*      строк текста.
*
*.Proto   void Set43Line(void)
*
*.Params   Отсутствуют.
*
*
*.Return   Не используется.
*
*.Sample   43line.c
**/
void Set43Line(void) {

   #define   CHAR_SIZE   8      // новая высота символов

   _asm {

      ; устанавливаем регистр es на начало оперативной памяти

      xor   ax,ax
      mov   es,ax

; получаем адрес порта индексного регистра контроллера ЭЛТ
; (3B4h/3D4h),
; в монохромных режимах для адресации к индексному регистру
; используется порт с адресом 3B4h, а в цветных - порт 3D4h

      mov   dx,es:[463h]

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

      mov   al,9
      out   dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ,
; в монохромных режимах для адресации к регистру данных
; используется порт с адресом 3B5h, а в цветных - порт 3D5h

      inc   dx

; биты D4-D0, регистра максимальной линии сканирования содержат
; число, меньшее на единицу, чем высота символов в пикселах

      mov   al,CHAR_SIZE-1
      out   dx,al

; вычисляем адрес порта индексного регистра контроллера ЭЛТ

      dec   dx

; выбираем для обмена регистр положения подчеркивания

      mov   al,14h
      out   dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ

      inc   dx

; определяем положение подчеркивания символов

      mov   al,CHAR_SIZE
      out   dx,al

; вычисляем адрес порта индексного регистра контроллера ЭЛТ

      dec   dx


; выбираем для обмена регистр завершения отображения
; вертикальной развертки

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

      mov   al,12h
      out   dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ

      inc   dx

; для отображения 43 текстовых символов, при высоте символов
; 8 пикселов, необходимо иметь 43 * 8 = 344 горизонтальных
; линий растра

; помещаем в регистр завершения отображения вертикальной
; развертки младшие 8 бит числа 344

      mov   al,(43 * CHAR_SIZE - 1 - 256)
      out   dx,al

; устанавливаем новые значения переменных BIOS

; число символов в строке равно восьмидесяти

      mov   WORD PTR es:[44Ah],80

; число строк текста минус один

      mov   BYTE PTR es:[484h],(350/CHAR_SIZE)-1

; высота символов в пикселах

      mov   WORD PTR es:[485h],CHAR_SIZE

; загружаем нулевую таблицу знакогенератора, расположенную во втором 
; цветовом слое, набором символов (размера 8х8 пикселов) из ПЗУ BIOS

; процесс загрузки шрифтов более подробно описан в разделе "Загрузка 
; таблиц знакогенератора" из главы "Дополнительные средства BIOS для 
; управлния EGA и VGA"

      mov   ax,1112h
      xor   bl,bl
      int   10h
   }
}

Начало импульса гашения вертикальной развертки (SVBR) (byltrc 15h)

Регистр определяет момент начала гашения луча в прцессе вертикальной развертки.

Для EGA регистр имеет 9, а для VGA - 10 битов. Девятый бит и для EGA и для VGA расположен в дополнительном регистре (OVR). Десятый бит для VGA расположен в регистре высоты символов текста (MSLR).

Конец импульса гашения вертикальной развертки (EVBR) (индекс 16h)

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

Управление режимом (MCR) (индекс 17h)

Регистр содержит набор битов, управляющих контроллером ЭЛТ.

D6 D5 00 01
  MA0 MA13 MA15
  MA1 MA0 MA0
  MA2 MA1 MA1
  MA3 MA2 MA2
  MA4 MA3 MA3
  MA5 MA4 MA4
  MA6 MA5 MA5
  MA7 MA6 MA6
  MA8 MA7 MA7
  MA9 MA8 MA8
  MA10 MA9 MA9
  MA11 MA10 MA10
  MA12 MA11 MA11
  MA13 MA12 MA12
  MA14 MA13 MA13
  MA15 MA14 MA14

Таблица 8.11 Преобразование адреса.

Бит D5 равен нулю только для видеоадаптеров EGA с объемом видеопамяти 64К байт. При этом происходит сцепление слоев в графических режимах с высоким разрешением.

Регистр сравнения линий (Line Compare Register - LCR) (индекс 18h)

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

Данный регистр используют совместно с регистром начального адреса. Когда счетчик горизонтальных линий сканирования достигает значения, записанного в регистре LCR, происходит сброс счетчика адреса регенерируемой видеопамяти в ноль. В результате экран разбивается на две части, где в верхней отображаются данные, на которые указывает регистр начального адреса, а в нижней - данные, находящиеся в начале видеопамяти. На рисунке 8.8 иллюстрируется процесс разделения экрана:

Рисунок 8.8 Процесс разделения экрана.

Содержимое верхнего окна можно перемещать, изменяя содержимое регистра начального адреса.

Заметим, что у видеоадаптера EGA регистр сравнения линий состоит из 9 бит. Доступ к девятому биту возможен через дополнительный регистр (OVR) контроллера ЭЛТ. Видеоадаптер VGA имеет дополнительно еще и десятый бит, расположенный в регистре MSLR.

При изменении режима работы видеоадаптера, BIOS записывает в регистр сравнения линий значение 0FFh.

Следующая программа выводит на экран дисплея 25 строк текста, а затем, после нажатия любой клавиши, разделяет экран на две части.

// пример разделения экрана дисплея на две части при помоши
// регистра сравнения линий контроллера ЭЛТ

#include <dos.h>
#include <stdio.h>

void Split(unsigned);


void main(void) {

     int   i;

   // выводим на экран дисплея 25 строк текста

     for(i = 0; i < 25; i++) 
      printf("Строка номер %d.",i);

   getch();

// разделяем экран по линии 200

   Split(200);

   getch();
// после завершения программы
// значения регистров не восстанавливаются!
}


/**
*.Name   Split
*
*.Title   Разделение экрана.
*
*.Descr   Функция разделяет экран на две части. Функция работает
*      только на EGA и VGA.
*
*.Proto   void Split(unsigned split_line)
*
*.Params   unsigned  split_line - линия горизонтальной развертки, 
*               в которой происходит разделение экрана 
               дисплея.
*
*.Return   Не используется.
*
*.Sample   split.c
**/
void Split(unsigned split_line) {

     _asm {

; устанавливаем регистр es на начало оперативной памяти

          xor   ax,ax
          mov   es,ax

; получаем адрес порта индексного регистра контроллера ЭЛТ
; (3B4h/3D4h),
; в монохромных режимах для адресации к индексному регистру
; используется порт с адресом 3B4h, а в цветных - порт 3D4h

          mov   dx,es:[463h]

; вычисляем адрес порта регистра состояния 1,
; в монохромных режимах для адресации к регистру состояния 1
; используется порт с адресом 3BAh, а в цветных - порт 3DAh

          add   dx,6


; ожидаем начало обратного вертикального хода луча

          in    al,dx
          nop

      ; если бит D3 равен единице, то происходит обратный
      ; вертикальный ход луча

          test  al,8
          jz    wait_on

     wait_off:

          in    al,dx

          ; задержка

          nop
          test  al,8
          jnz    wait_off

     wait_on:

          in    al,dx
          nop
          test  al,8
          jz    wait_on


; вычисляем адрес порта индексного регистра контроллера ЭЛТ
; (3B4h/3D4h)

          sub   dx,6

; выбираем для обмена регистр сравнения линий

          mov   al,18h
          out   dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ,
; в монохромных режимах для адресации к регистру данных
; используется порт с адресом 3B5h, а в цветных - порт 3D5h

          inc   dx


; определяем линию горизонтальной развертки, в которой происходит
; разделение экрана дисплея

          mov   ax,split_line

; у видеоадаптера EGA регистр сравнения линий состоит из 9 бит,
; доступ к девятому биту происходит через дополнительный регистр
; контроллера ЭЛТ;
; видеоадаптер VGA имеет еще и десятый бит, расположенный в регистре
; высоты символов текста

; записываем младшие 8 битов в регистр сравнения линий

          out   dx,al

; вычисляем адрес порта индексного регистра контроллера ЭЛТ

          dec   dx

; вызываем функцию чтения конфигурации видеосистемы,
; данная функция подробно описана в главе
; "Дополнительные средства BIOS для управлния EGA и VGA"

          mov   ax,1A00h
          int   10h

; если после вызова функции регистр al не равен 1Ah, то используемый
; видеодаптер не VGA

          cmp   al,1Ah
          je    vga
          jmp   ega

     vga:

; считываем в al состояние дополнительного регистра

          mov   al,7
          out   dx,al
          inc   dx
          in    al,dx

; модифицируем бит D4 дополнительного регистра

          mov   bx,split_line
          mov   cl,4
          shl   bh,cl
          and   bh,00010000b

          and   al,11101111b
          or    al,bh
          out   dx,al

; сбрасываем бит D6 регистра высоты символов текста

          dec   dx
          mov   al,9
          out   dx,al
          inc   dx
          in    al,dx
          and   al,10111111b
          out   dx,al
          jmp   quit_split

     ega:

          mov   bx,split_line
          mov   cl,4
          shl   bh,cl
          and   bh,00010000b

          cmp   dx,3B4h         ; монохромный режим?
          je    default_1Fh     ; если да, то переходим
                  ; на метку default_1Fh
; определяем наличие улучшенного цветного дисплея по положению
; переключателей на плате EGA

          mov   al,es:[488h]
          and   al,0Fh

; положение переключателей "off off on on" ?

          cmp   al,3
          je    default_1Fh

; положение переключателей "off on on off" ?

          cmp   al,9
          je    default_1Fh

; для видеосистем с монохромным или цветным дисплеями,
; дополнительный регистр по умолчанию содержит 11h

          or    bh,1
          jmp   set_overflow

; для видеосистем с улучненным цветным дисплеем или в монохромных
; текстовых режимах дополнительный регистр по умолчанию содержит 
; значение 1Fh

     default_1Fh:

          or    bh,0Fh

; устанавливаем дополнительный регистр

     set_overflow:

          mov   al,07h
          out   dx,al
          inc   dx
          mov   al,bl
          out   dx,al

     quit_split:

     }
}

7.4. Регистры синхронизатора

Синхронизатор управляет всеми временными параметрами видеоадаптера, а также разрешением и запрещением доступа к отдельным цветовым слоям. Синхронизатор имеет пять регистров, они перечислены в таблице 8.12. У видеоадаптера EGA все пять регистров доступны только для записи, а у VGA и Super VGA также и для чтения.

Индекс регистра Регистр синхронизатора
0 регистр сброса синхронизатора (Reset Register - RR)
1 регистр режима синхронизации (Clock Mode Register - CMR)
2 регистр разрешения записи цветового слоя (Color Plane Write Enable Register - CPWER)
3 регистр выбора знакогенератора (Character Generator Select Register - CGSR)
4 регистр определения структуры памяти (Memory Mode Register - MMR)

Таблица 8.12 Регистры синхронизатора.

Доступ к регистрам осуществляется через индексный порт с адресом 3C4h и через порт данных с адресом 3C5h.

Как и в случае описания регистров контроллера ЭЛТ мы будем подробно рассказывать только о безопасных и наиболее полезных регистрах синхронизатора.

Регистр сброса синхронизатора (Reset Register - RR) (индекс 0)

Если регистр переведен в состояние сброса, то все процессы видеоадаптера приостанавливаются. В результате могут быть разрушены данные в видеопамяти.

Для нормальной работы видеоадаптера биты D0 и D1 должны быть равны единице. Перед доступом к регистру режима синхронизации синхронизатор должен быть переведен в состояние синхронного сброса (бит D1 равен 0).

Регистр режима синхронизации (Clock Mode Register - CMR) (индекс 1)

Регистр управляет временными циклами синхронизатора. При изменении его содержимого необходимо соблюдать осторожность. Изменяя отдельные биты регистра, сохраняйте значения остальных битов.

Перед модификацией регистра CMR надо сбросить бит D1 в регистре сброса синхронизатора, переведя таким образом синхронизатор в режим сброса и остановки.

Ниже приведены значения регистра CMR, устанавливаемые BIOS при установке режима работы адаптера:

Режим 00,01,04,05,0Dh 02,03,06,0Eh 0Fh,10h
Содержимое регистра 0Bh 01 05

Регистр разрешения записи цветового слоя
(Color Plane Write Enable - CPWE) (индекс 2)

При помощи данного регистра можно запретить запись процессором данных в любые цветовые слои видеопамяти. На рисунке 8.9 показан механизм запрещения записи данных в отдельные слои видеопамяти. Рассмотрим процесс записи данных процессором в видеопамять:

Рисунок 8.9 Разрешение записи в цветовые слои.

Биты регистра разрешения записи цветового слоя имеют следующие значения:

При установке режима работы адаптера BIOS загружает в регистр CPWE следующие значения:

Режим 0,1,2,3,4,5,7 6 D,E,F,10
Содержимое регистра 03 01 0Fh

Регистр выбора знакогенератора (Character Generator Select Register - CGSR) (индекс 3)

Видеоадаптер EGA позволяет загрузить в память четыре, а VGA - восемь таблиц знакогенератора, каждая из которых опрделяет 256 символов.

Одновременно могут использоваться символы одной или двух таблиц знакогенератора. Это позволяет отображать до 512 различных символов.

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

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

Для выбора активных (используемых в данный момент для отображения символов) таблиц знакогенератора надо установить биты D0-D5 в соответствии со следующей таблицей:

D5 D3 D2 Номер таблицы знакогенератора, используемой если бит D3 байта атрибутов равен 1
D4 D1 D0 Номер таблицы знакогенератора, используемой если бит D3 байта атрибутов равен 0
0 0 0 первая таблица знакогенератора
0 0 1 вторая таблица знакогенератора
0 1 0 третья таблица знакогенератора
0 1 1 четвертая таблица знакогенератора
1 0 0 пятая таблица знакогенератора (VGA)
1 0 1 шестая таблица знакогенератора (VGA)
1 1 0 седьмая таблица знакогенератора (VGA)
1 1 1 восьмая таблица знакогенератора (VGA)

Таблица 8.13 Выбор таблиц знакогенератора.

Если нужна только одна активная таблица знакогенератора, то значеня битов D5 D3 D2 и битов D4 D1 D0 должны совпадать.

Для установки номеров активных таблиц знакогенератора рекомендуется использовать соответствующую функцию BIOS.

Регистр определения структуры памяти (Memory Mode Register - MMR) (индекс 4)

Регистр инициализируется BIOS при выполнении операции выбора режима работы видеоадаптера и определяет структуру видеопамяти в этом режиме.

При выборе режима работы адаптера, BIOS загружает в регистр CPWE следующие значения:

Режим 0,1,2,3,7 4,5 6,0Dh,0Eh 0Fh,10h
Содержимое регистра 03 02 06 00

7.5. Регистры графического контроллера

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

Графический контроллер содержит девять регистров. Обращение к ним происходит через индексный порт с адресом 3CEh и порт данных с адресом 3CFh. Список регистров приведен в таблице 8.14.

Адаптер EGA имеет еще два порта с адресами 3CAh и 3CCh, используемых при инициализации видеоадаптера.

Индекс регистра Регистр графического контроллера
0 регистр установки/сброса (Set/Reset Register - SRR)
1 регистр разрешения установки/сброса (Set/Reset Enable Register - SRER)
2 регистр сравнения цветов (Color Compare Register - CCR)
3 регистр циклического сдвига и выбора функции (Data Rotate & Function Select - DRFS)
4 регистр выбора читаемого слоя (Read Plane Select Register - RPSR)
5 регистр режима работы (Mode Register - MDR)
6 регистр смешанного назначения (Miscellaneous Register - MIR)
7 регистр маскирования цветовых слоев (Color Don't Care Register - CDCR)
8 регистр битовой маски (Bit Mask Register - BMR)

Таблица 8.14 Регистры графического контроллера.

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

Ниже, на рисунке 8.10, демонстрируются функции, выполняемые графическим контроллером. Рассмотрм функционирование графического адаптера.

Байт, записываемый процесором в видеопамять (11100001b), поступает в графический контроллер. В соответствии со значением регистра циклического сдвига и выбора функции, происходит циклический сдвиг на один бит содержимого записываемого в видеопамять байта. Затем результат складывается по логике ИЛИ с содержимым регистров-защелок. Какая булева функция используется - ИЛИ, И, ИСКЛЮЧАЮЩЕЕ ИЛИ также определяется регистром циклического сдвига и выбора функции.

Дальнейшие преобразования происходят в соответствии со значениями регистра разрешения установки/сброса и регистра установки/сброса:

Затем в соответствии с состоянем регистра битовой маски происходит запись данных в видеопамять:

Рисунок 8.10 Функции графического контроллера.

Теперь рассмотрим регистры графического контроллера более подробно.

Регистр установки/сброса
(Set/Reset Register - SRR) (индекс 0)

Использовав данный регистр совместно с регистром разрешения установки/сброса (Set/Reset Enable Register), можно определить данные, размещаемые в цветовых слоях видеопамяти при выполнении процессором записи любых данных в видеопамять. Данные процессора при этом игнорируются.

Приведем формат регистра установки/сброса:

По умолчанию во все биты регистра записываются нулевые значения.

Возможности использования данного регистра разъясняются при описании регистра разрешения установки/сброса.

Регистр разрешения установки/сброса
(Set/Reset Enable Register - SRER) (индекс 1)

Регистр разрешения установки/сброса позволяет при операции записи в видеопамять для одних цветовых слоев использовать данные от процессора, а для других - из регистра установки/сброса (см. рисунок 8.11).

Если биты регистра, управляющие цветовыми слоями, содержат единицы, то при выполнении операции записи, в соответствующие цветовые слои записывается информация из регистра установки/сброса. В остальные слои видеопамяти заносятся данные от процессора.

Теперь приведем формат регистра разрешения установки/сброса:

По умолчанию во все биты регистра записываются нулевые значения.

Заметим, что возможность использования регистров установки/сброса можно реализовать только в нулевом режиме записи. Режим записи устанавливается регистром режима работы графического контроллера (Mode Register, индекс 5). В нулевом режиме записи, если запрещено выполнение операции маскирования пикселов (регистр битовой маски содержит значение 0FFh), то каждый байт, записываемый процессором в видеопамять определяет параметры 8 пикселов в одном или нескольких цветовых слоях. Более подрбное описание регистра режима работы графического контроллера и регистра битовой маски приведено ниже.

Рассмотрим на рисунке 8.11 применение регистров установки/сброса. Пусть графический контроллер находится в нулевом режиме работы, и регистр битовой маски равен 0FFh (используются только данные от процессора). В этом случае можно использовать регистры установки/сброса.

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

Данные, непосредственно поступающие в видеопамять зависят от значения регистра разрешения установки/сброса. Биты D3 и D0, регистра разрешения установки/сброса равны нулю, поэтому в третий слой и нулевой слой видеопамяти данные заносятся без изменения. Биты D2 и D1 регистра разрешения установки/сброса равны единице, значит во второй и первый слои видеопамяти записываются данные в соответствии со значением битов D2 и D1 регистра установки/сброса. То есть во второй слой записывается байт 00000000b, а в первый - 11111111b.

Рисунок 8.11 Использование регистров установки/сброса.

Регистр сравнения цветов
(Color Compare Register - CCR) (индекс 2)

Регистр CCR используется программами, осуществляющими поиск на экране пикселов с определенным цветом. Без использования регистра CCR за один цикл чтения видеопамяти процессор может считать данные только из одного цветового слоя. Программирование данного регистра позволяет за один цикл чтения произвести чтение всех четырех цветовых слоев, сравнение считанных значений с искомыми и возвращение результата.

Ниже приведен формат регистра сравнения цветов:

После выполнения BIOS устаноки режима работы видеоадаптера все биты регистра содержат нулевые значения.

Операция поиска пикселов данного цвета иллюстрируется рисунком 8.12.

Рисунок 8.12 Использование регистра CCR.

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

Заметим, что перед использованием регистра CCR для операции поиска пикселов определенного цвета необходимо установить регистр режима работы (Mode Register - MDR) и регистр маскирования цветовых слоев (Color Don't Care Register - CDCR), описанные ниже.

Регистр циклического сдвига и выбора функции
(Data Rotate & Function Select - DRFS) (индекс 3)

Регистр DRFS выполняет две различные функции, отраженные в его названии:

После выполнения BIOS установки режима работы видеоадаптера биты регистра DRFS содержат нулевые значения.

При выполнении операции циклического сдвига биты счетчика сдвига могут иметь следующие значения. Чтобы разрешить операцию циклического сдвига надо использовать нулевой режим записи.

D2 D1 D0 Данные циклически сдвигаются вправо на следующее количество битов
0  0  0 ноль, сдвиг не происходит
0  0  1 один бит
0  1  0 два бита
0  1  1 три бита
1  0  0 четыре бита
1  0  1 пять битов
1  1  0 шесть битов
1  1  1 семь битов

Таблица 8.15 Циклический сдвиг.

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

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

D4 D3 Выполняемая логическая операция
0  0 запись немодифицированных данных
0  1 И
1  0 ИЛИ
1  1 ИСКЛЮЧАЮЩЕЕ ИЛИ

Таблица 8.16 Возможные логические операции.

Следует отметить, что операция циклического сдвига выполняется до выполнения логических функций.

Регистр DRFS можно использовать для быстрого копирования областей видеопамяти с возможной их модификацией.

Рисунок 8.13 иллюстрирует выполнение логической операции ИЛИ. Регистр циклического сдвига и выбора функции содержит число 00010000b. После операции чтения данные из всех четырех слоев записываются в регистрах-защелках. Затем выполняется операция записи. При этом данные процессора в соответствии с состоянием регистра циклического сдвига и выбора функции складываются по логике ИЛИ со значением регистров-защелок и помещаются в видеопамять.

Рисунок 8.13 Логические операции и операция циклического сдвига.

Заметим, что логические операции можно производить только в нулевом и втором режиме записи.

Регистр выбора читаемого слоя
(Read Plane Select Register - RPSR) (индекс 4)

Определяет номер цветового слоя видеопамяти, из которого процессор может читать данные. Заметим, что при выполнении операции сравнения цветов значение этого регистра безразлично.

Приведем формат регистра выбора читаемого слоя.

При установке режима работы видеоадаптера BIOS заполняет все биты регистра нулями, что соответствует нулевому цветовому слою.

Операция чтения видеопамяти процессором иллюстрируется на рисунке 8.14. На этом рисунке регистр выбора читаемого слоя разрешает чтение второго слоя видеопамяти. Таким образом при чтении процессором данных из видеопамяти, процессор считывает данные только из второго слоя. Остальные слои будут недоступны для чтения процессором, пока вы не измените состояние регистра читаемого слоя.

Рисунок 8.14 Операция чтения из видеопамяти.

Регистр режима работы
(Mode Register - MDR) (индекс 5)

Регистр управляет несколькими различными функциями графического контроллера. В частности он управляет режимом записи в видеопамять, а также разрешением режима сравнния цветов (см. регистр сравнения цветов графического контроллера).

D1-D0  Режим записи.
D2     Не используется.
D3     Разрешение режима сравнения цветов.
D4     Четный/нечетный режим.
D5     Режим регистра сдвига.
D6     Управление режимом VGA с 256 цветами.
D7     Не используется.

Ниже подробно рассмотрены отдельные биты регистра. Мы не рекомендуем изменять состояние битов D4-D7, так как это может привести к потере изображения на экране дисплея.

D1 D0 Номер режима Режим записи
0  0 0 режим непосредственной записи
0  1 1 использование для записи регистров-защелок
1  0 2 заполнение N-ого цветового слоя битом номер N из данных, записываемых процессором
1  1 - не используется

Процесс записи в видеопамять в каждом из перечисленных выше режимов показан на рисунке 8.14.

Рисунок 8.14 Различные режимы записи в видеопамять.

Остальные биты регистра модифицировать не рекомендуется.

При установке режима работы видеоадаптера BIOS загружает в регистр MDR следующие значения, в зависимости от режима работы видеоадаптера:

Режим 0,1,2,3,7,F,10 4,5 6,D,E
Содержимое регистра 10h 30h 00

Регистр смешанного назначения
(Miscellaneous Register - MIR) (индекс 6)

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

При установке режима работы видеоадаптера BIOS загружает в регистр MIR следующие значения, в зависимости от режима работы видеоадаптера:

Режим 3 7 F 10
Содержимое регистра Eh Ah 07 05

Регистр маскирования цветовых слоев
(Color Don't Care Register - CDCR) (индекс 7)

Регистр используется в режиме сравнения цветов (см. регистр сравнения цветов - CCR). Если какие-либо биты D3-D0 содержат ноль, то при операции сравнения цветов соответствующие цветовые слои в рассмотрение не принимаются.

При установке режима работы в регистр заносятся следующие значения:

Режим 3 7 F 10
Содержимое регистра 00 00 0Fh 0Fh

Регистр битовой маски
(Bit Mask Register - BMR) (индекс 8)

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

Рисунок 8.15 Использование регистра битовой маски.

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

По умолчанию, во всех режимах регистр хранит число 0FFh.

7.6. Регистры контроллера атрибутов

Контроллер атрибутов управляет цветовыми характеристиками изображений.

Контроллер атрибутов содержит двадцать один регистр. Регистры перечисленны в таблице 8.17. Доступ к ним осуществляется через один порт - 3C0h (в отличие от предыдущих рассмотренных нами регистров). Этот порт совмещает в себе функции индексного порта и порта данных, регулируемые внутренним триггером. Триггер переключается при каждой операции записи в порт и записываемые данные воспринимаются либо как индекс регистра, либо как данные для обмена с регистром.

Установить триггер в исходное состояние можно, выполнив чтение из порта 3BAh для монохромного режима или из порта 3DAh - для цветного режима работы видеоадаптера. После установки триггера данные, записываемые в регистр 3C0h, будут восприниматься, как индекс регистра.

Индекс Регистры контроллера атрибутов
0 - 0Fh регистры цветовой палитры (0-15) (Color Palette Register's - CPR)
10h регистр управления режимом (Mode Control Register - MCR)
11h регистр цвета рамки экрана (Screen Border Color Register - SBCR)
12h регистр разрешения цветового слоя (Color Plane Enable Register - CPER)
13h регистр горизонтального панорамирования (Horizontal Panning Register - HPR)
14h регистр выбора цвета (Color Select Register - CSR)

Таблица 8.17 Регистры контроллера атрибутов.

Регистры цветовой палитры (0-15)
(Color Palette Register's - CPR)

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

16 регистров цветовой палитры предоставляют возможность выбора любых 16 цветов из 64 возможных, которые будут использоваться видеоадаптером EGA в данный моент.

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

Другие дисплеи - цветной (CD) и монохромный (MD), имеют меньшее число линий управления и регистр цветовой палитры также имеет другой формат.

Форматы регистров цветовой палитры для различных дисплеев приведены ниже:

Улучшенный цветной дисплей.

Цветной дисплей.

Монохромный дисплей.

Дисплей VGA.

Регистр управления режимом
(Mode Control Register - MCR) (индекс 10h)

Регистр управления режимом управляет контроллером атрибутов.

По умолчанию регистр содержит следующие значения:

Режим 0,1,2,3 4,5,6,D,E,10 7 F
Содержимое регистра 08 01 0Eh 0Bh

Регистр цвета рамки экрана
(Screen Border Color Register - SBCR) (индекс 11)

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

К сожалению операция установки цвета рамки работает не правильно на большинстве адаптеров EGA.

Регистр разрешения цветового слоя
(Color Plane Enable Register - CPER) (индекс 12)

При установке режима работы видеоадаптера BIOS записывает в регистр следующие значения:

Режим 0,1,2,3,7,D,E 4,5 6 F,10  
Содержимое регистра 0Fh 03 03 01 05

Регистр горизонтального панорамирования
(Horizontal Panning Register - HPR) (индекс 13)

Регистр позволяет сдвигать в горизонтальном направлении содержимое экрана на один пиксел. При использовании регистра HPR совместно с регистром начального адреса контроллера атрибутов можно производить горизонтальный сдвиг экрана влево на любое число пикселов.

Формат регистра горизонтального панорамирования представлен ниже:

  Величина сдвига содержимого крана (в пикселах)
D3 D2 D1 D0 монохромный текстовый VGA, режим 13h остальные режимы
0  0  0  0 8 0 0
0  0  0  1 0 - 1
0  0  1  0 1 1 2
0  0  1  1 2 - 3
0  1  0  0 3 2 4
0  1  0  1 4 - 5
0  1  1  0 5 3 6
0  1  1  1 6 - 7
1  0  0  0 7 - -
1  0  0  1 - - -
1  0  1  0 - - -
1  0  1  1 - - -
1  1  0  0 - - -
1  1  0  1 - - -
1  1  1  0 - - -
1  1  1  1 - - -

Таблица 8.18 Величина горизонтального сдвига в различных режимах работы видеоадаптера.

По умолчанию для всех режимов работы видеоадаптера регистр горизонтального панорамирования содержит нулевые значения для всех битов.

На рисунке 8.16 иллюстрируется операция горизонтального сдвига содержимого экрана при различных значениях регистра HPR. На левой части рисунка регистр горизонтального панорамирования содержит ноль, а на правой части рисунка значение регистра увеличено до трех, при этом изображение на экране сдвигается на три пиксела влево.

Рисунок 8.16 Горизонтальный сдвиг экрана.

Следующая программа позволяет перемещать содержимое экрана по горизонтали и вертикали. Функции HorScroll и VerScroll реализуют, соответственно, горизонтальный и вертикальный сдвиг экрана.

// смещение содержимого экрана по горизонтали и вертикали

#include "sysgraph.h"
#include <stdio.h>
#include <stdlib.h>
#include <graph.h>
#include "sysp.h"
#include <dos.h>

void HorScroll(unsigned, unsigned);
void VerScroll(unsigned);
void SetColumn(unsigned char);

void main(void) {

   struct videoconfig vc;
   unsigned char   i,j;

   // выбираем текстовый режим работы видеоадаптера,
   // принятый по умолчанию

   _setvideomode(_DEFAULTMODE);

   // устанавливаем логическую ширину экрана в 100 символов

   SetColumn(100);

   // отображаем на экране дисплея 24 строки текста

   for(i = 0; i < 24; i++)
      printf("\ntext text text");

   // производим горизонтальное смещение экрана
   // на i пикселов при каждом нажатии на клавиатуру

   for(i = 0; i < 13; i++) {
      getch();
      HorScroll(i,8);
   }

   // производим вертикальное смещение экрана
   // на i пикселов при каждом нажатии на клавиатуру


   for(i = 0; i < 13; i++) {
      getch();
      VerScroll(i);
   }
   getch();

_setvideomode(_DEFAULTMODE);
}


/**
*.Name   HorScroll
*
*.Title   Горизонтальный сдвиг содержимого экрана.
*
*.Descr   Функция смещает содержимое экрана влево на определенное
*      число пикселов.
*
*.Proto   void HorScroll(unsigned offset, unsigned wide)
*
*.Params   unsigned  offset - величина смещения в пикселах,
*
*      unsigned  wide   - ширина символов (8 или 9).
*
*.Return   Не используется.
*
*.Sample   scroll.c
**/
void HorScroll(unsigned offset, unsigned wide) {

   unsigned start_addr_reg, panning_reg;
   div_t res;

   res = div(offset,wide);

   start_addr_reg = res.quot;
   panning_reg = (wide == 9) ? 
            ((res.rem == 0) ? 8 : res.rem - 1 ) : res.rem;

   _asm {
      push   ds

; вычисляем адрес регистра состояния 1 (3BAh/3DAh)

      xor   ax,ax
      mov   es,ax
      mov   dx,es:[463h]
      add   dx,6

; ожидаем начало обратного вертикального хода луча

      in   al,dx
      nop
      nop
      test   al,08h
      jz   wait_on

   wait_off:

      in   al,dx
      nop
      nop
      test   al,08h
      jnz   wait_off

   wait_on:

      in   al,dx
      nop
      nop
      test   al,08h
      jz   wait_on

; вычисляем адрес порта индексного регистра контроллера ЭЛТ

      sub   dx,6

; выбираем для доступа младший байт регистра начального адреса

      mov   al,0Dh
      out   dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ

      inc   dx

; устанавливаем новый начальный адрес

      mov   ax,start_addr_reg
      out   dx,al

; снова получаем адрес порта регистра состояния 1

      add   dx,5

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

      in   al,dx
      nop
      nop

; выбираем регистр горизонтального панорамирования

      mov   dx,3C0h
      mov   al,13h
      out   dx,al

      mov   ax,panning_reg
      out   dx,al

      pop   ds
   }
}



/**
*.Name   VerScroll
*
*.Title   Вертикальный сдвиг содержимого экрана.
*
*.Descr   Функция смещает содержимое экрана вверх на определенное
*      число пикселов.
*
*.Proto   void VerScroll(unsigned offset)
*
*.Params   unsigned  offset - величина смещения в пикслах.
*
*.Return   Не используется.
*
*.Sample   scroll.c
**/
void VerScroll(unsigned offset) {

   unsigned start_addr_reg, preset_row_reg;
   div_t res;
   BIOS_VAR _far  *bios_var_ptr;


   bios_var_ptr = (BIOS_VAR _far *) FP_MAKE(0x0000, 0x0410);

   res = div(offset,bios_var_ptr -> char_height);

   start_addr_reg = res.quot * (bios_var_ptr -> columns);
   preset_row_reg = res.rem;


   _asm {
      push   ds

; вычисляем адрес регистра состояния 1 (3BAh/3DAh)

      xor   ax,ax
      mov   es,ax
      mov   dx,es:[463h]
      add   dx,6

; ожидаем начало обратного вертикального хода луча

      in   al,dx
      nop
      nop
      test   al,08h
      jz   wait_on

   wait_off:

      in   al,dx
      nop
      nop
      test   al,08h
      jnz   wait_off

   wait_on:

      in   al,dx
      nop
      nop
      test   al,08h
      jz   wait_on

; вычисляем адрес порта индексного регистра контроллера ЭЛТ

      sub   dx,6

; модифицируем младший байт регистра начального адреса

      mov   al,0Dh
      out   dx,al
      inc   dx
      mov   ax,start_addr_reg
      out   dx,al

      dec   dx

; модифицируем старший байт регистра начального адреса

      mov   al,0Ch
      out   dx,al
      inc   dx
      mov   al,ah
      out   dx,al

; вычисляем адрес порта регистра состояния 1

      add   dx,5

; ожидаем вертикальный обратный ход луча

wait_next:

      in   al,dx
      nop
      nop
      test   al,08h
      jz   wait_next

; определяем адрес порта индексного регистра контроллера ЭЛТ

      sub   dx,6

; устанавливаем регистр предустановки линии сканирования

      mov   al,8
      out   dx,al
      mov   ax,preset_row_reg
      inc   dx
      out   dx,al

      pop   ds
   }
}


/**
*.Name   SetColumn
*
*.Title   Установка логической ширины экрана.
*
*.Descr   Функция устанавливает логическую ширину экрана.
*
*.Proto   void SetColumn(unsigned char col)
*
*.Params   unsigned char  col - логическая ширина экрана (в символах).
*
*.Return   Не используется.
*
*.Sample   scroll.c
**/
void SetColumn(unsigned char col) {

   _asm {
      xor   ax,ax
      mov   es,ax

; получаем адрес порта индексного регистра
; контроллера ЭЛТ (3B4h/3D4h)
      mov   dx,es:[463h]

; выбираем для обмена регистр логической ширины экрана

      mov   al,13h
      out   dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ (3B5h/3D5h)

      inc   dx

; модифицируем переменную BIOS, содержащую число символов в строке

      mov   al,col
      mov   es:[44Ah],al

; регистр логической ширины экрана в текстовых режимах содержит
; смещение между соседними строками текста в словах

      shr   al,1
      out   dx,al
   }
}

Регистр выбора цвета
(Color Select Register - CSR) (индекс 14)

Регистр используется только видеоадаптером VGA для управления цветом.

7.7. Регистры цифро-аналогового преобразователя VGA

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

Цветовая 8-битовая информация, поступающая от контроллера атрибутов (см. рисунок 8.17), преобразуется согласно таблице цветов в три 6-битовые сигнала для трех ЦАП. Такая схема позволяет одновременно отображать на экране 256 различных цветов, каждый из которых можно отдельно выбрать из 26+6+6 = 218 = 262144 возможных цветов.

Рисунок 8.17 Схема управления цветами (VGA).

Таблица цветов фактически является набором из 256 18-битовых регистров. Используя регистры ЦАП, можно получить доступ для чтения и для записи к каждому регистру таблицы цветов.

ЦАП видеоадаптера VGA управляется пятью регистрами, перечисленными в таблице 8.19.

Адрес Регистр
3C6h регистр маскирования пикселов (Pixel Mask Register - PMR)
3C7h регистр состояния ЦАП (для чтения) (DAC State Register - DAC_SR)
3C7h индекс читаемого регистра таблицы цветов (для записи) (Look-up Table Read Index Register - LTRIR)
3C8h индекс записываемого регистра таблицы цветов (Look-up Table Write Index Register - LTWIR)
3C9h регистр данных таблицы цветов (Look-up Table Data Register - LTDR)

Таблица 8.19 Регистры управления ЦАП.

Регистр маскирования пикселов
(Pixel Mask Register - PMR)

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

Регистр состояния ЦАП
(DAC State Register - DAC_SR)

Регистр адресуется при помоши порта с адресом 3C7h и доступен только для чтения. Прочитав данные из регистра, можно определить, доступны регистры цветовой таблицы для чтения или же они доступны для записи.

Индекс читаемого регистра таблицы цветов
(Look-up Table Read Index Register - LTRIR)

Это индексный регистр доступен через порт 3C7h только для записи. Запись в данный регистр индекса элемента цветовой таблицы позволяет прочитать его содержимое через регистр данных цветовой таблицы.

Данные из регистров таблицы цветов читаются через порт 3C9h, как три 6-битовых числа. После чтения третьего числа значение индексного регистра (LTRIR) автоматически увеличивается на единицу, что позволяет прочитать всю таблицу цветов, загрузив регистр индекса только один раз.

Особо подчеркнем, что во время операций чтения или записи таблицы цветов прерывания должны быть запрещены.

Индекс записываемого регистра таблицы цветов
(Look-up Table Write Index Register - LTWIR)

После записи в регистр LTWIR индекса регистра таблицы цветов можно записать в него новое значение через регистр данных таблицы цветов (см. ниже).

Данные записываются в регистры таблицы цветов через порт 3C9h, как три 6-битовых числа. После записи третьего числа значение индексного регистра (LTWIR) автоматически увеличивается на единицу, что позволяет прочитать таблицу цветов, загрузив регистр индекса только один раз.

Регистр данных таблицы цветов
(Look-up Table Data Register - LTDR)

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

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

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

// программа демонстрирует использование регистров таблицы цветов

#include <conio.h>
#include <stdio.h>
#include <graph.h>
#include "sysp.h"
#include "sysgraph.h"
#include <dos.h>


void SetVgaDAC(unsigned, unsigned);


viod main(void) {

   struct videoconfig vc;       // структура описана в graph.h
   RGB color_table[256];
   unsigned char   i, j;
   unsigned char   far *ptr;
   int error, x_num, y_num;
   unsigned seg_table,off_table;

// записываем в массив color_table новые значения для
// регистров таблицы цветов

   for(j = 0; j < 4; j++) {
      for(i = 0; i < 64; i++) {
         (color_table[i+j*64]).red = (j == 0) ? i : 0;
         (color_table[i+j*64]).green = (j == 1) ? i : (j == 3) ? i : 0;
         (color_table[i+j*64]).blue = (j == 2) ? i : (j == 3) ? i : 0;
      }
   }

// устанавливаем режим видеоадаптера номер 13h (256 цветов) 
// данный режим поддерживается только VGA и Super VGA

   error = _setvideomode( _MRES256COLOR );

// если режим не установлен, завершаем выполнение программы

   if(!error) exit(1);

   ptr = (unsigned char far*) &color_table[0];

// определяем сегмент и смешение массива color_table

   seg_table = FP_SEG(ptr);
   off_table = FP_OFF(ptr);

// загружаем новые значения в регистры таблицы цветов

   SetVgaDAC(seg_table,off_table);

// выводим на экран вертикальные линии различного цвета,
// процессор записывает данные непосредственно в видеопамять

// получаем в ptr указатель на начало видеопамяти

   ptr = (unsigned char far*) (FP_MAKE(0xA000, 0x0));

// записываем данные непосредственно в видеопамять

   for(y_num = 0; y_num < 200; y_num++) {
      for(x_num = 0; x_num < 320; x_num++) {
         *ptr = (unsigned char) x_num;
         ptr++;
      }
   }

// ожидаем нажатия на любую клавишу

   getch();

// устанавливаем режим видеодаптера, используемый по умолчанию
   _setvideomode( _DEFAULTMODE );
}



/**
*.Name   SetVgaDAC
*
*.Title   Запись регистров таблицы цветов
*
*.Descr   Функция устанавливает новые значения для всех регистров
*      таблицы цветов.
*
*
*.Proto   void SetVgaDAC(unsigned seg_table, unsigned off_table)
*
*.Params   unsigned  seg_table - сегмент таблицы, содержащей новые
*                        значения регистров таблицы цветов,
*
*      unsigned  off_table - смещение таблицы, содержащей новые
*                        значения регистров таблицы цветов.

*.Return   Не используется.
*
*.Sample   vga256.c
**/
void SetVgaDAC(unsigned seg_table, unsigned off_table) {

   _asm {

; сохраняем регистры ds и es      

      push  ds
      push  es

      ; устанавливаем регистр es на начало оперативной памяти

          xor   ax,ax
          mov   es,ax

   ; получаем адрес порта индексного регистра контроллера ЭЛТ
     ; (3B4h/3D4h),
   ; в монохромных режимах для адресации к индексному регистру
   ; используется порт с адресом 3B4h, а в цветных - порт 3D4h

          mov   dx,es:[463h]

   ; вычисляем адрес порта регистра состояния 1,
   ; в монохромных режимах для адресации к регистру состояния 1
   ; используется порт с адресом 3BAh, а в цветных - порт 3DAh

      add   dx,6

      pop   es


; ожидаем начало обратного вертикального хода луча

      in    al,dx
      nop
      nop

      ; если бит D3 равен единице, то происходит обратный
      ; вертикальный ход луча

      test  al,08h
      jz    wait_on

   wait_off:

      in    al,dx
      nop
      nop
      test  al,08h
      jnz   wait_off

   wait_on:

      in    al,dx
      nop
      nop
      test  al,08h
      jz    wait_on

; устанавливаем индекс первого записываемого регистра
; таблицы цветов

      mov   dx,3C8h

; начинаем модифицировать таблицу цветов с первого регистра

      mov   ax,1   
      out   dx,al

   ; задержка

      nop
      nop

; устанавливаем ds:si на массив данных, записываемых
; в регистры таблицы цветов

      mov   ax,seg_table
      mov   ds,ax
      mov   si,off_table

; загружаем 256 регистров (по 3 байта на регистр)

      mov   cx,(256 * 3)

; выбираем регистр данных таблицы цветов (порт 3C9h)

      mov   dx,3C9h

      cld

; загружаем все регистры таблицы цветов

   get_reg:

      lodsb
      out   dx,al
      nop         
      nop
      loop  get_reg

; восстанавливаем регистр ds

      pop   ds
   }
}

7.8. Нестандартные режимы видеоадаптера VGA

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

Мы рассмотрим два наиболее интересных с нашей точки зрения нестандартных режимов VGA: 320х400 и 360х480 пикселов при 256 цветах.

Эти режимы нельзя установить на обычных VGA адаптерах с помощью функций BIOS. С помощью BIOS можно установить только один режим с 256-цветной палитрой - 13h (320х200 пикселов, 256 цветов). Однако если вы воспользуетесь возможностью непосредственного программирования адаптера через регистры, то любой адаптер VGA можно перевести в эти режимы.

Программирование всех трех описанных ниже нестандартных режимов мы проведем в два этапа:

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

Организация видеопамяти

Режим 13h использует простую линейную организацию видеопамяти, в которой по каждому адресу в видеопамяти находится один байт управляющий одним пикселом. Такая организация видеопамяти хотя и облегчает программирование, но не позволяет увеличить разрешающую способность. Дело в том, что в режиме 13h адресное пространство видеопамяти ограничено 64K, которых хватает как раз для того, чтобы получить разрешающую способность 200х320 точек при 256 цветах (200*320 = 64000). Кроме того такая организация видеопамяти не позволяет использовать для копирования видеоданных регистры-защелки, что может существенно повысить скорость работы программ.

Исходя из вышесказонного для нашего нестандартного режима используется другая структура видеопамяти, более схожая со структурой видеопамяти режимов 10h и 12h. На следующем рисунке представлена структура видеопамяти используемая нами во всех описываемых нестандартных режимах:

Рисунок 8.18 Структура видеопамяти в нестандартных, 256-цветовых режимах.

Как видно из рисунка первый пиксел экрана, отображаемый в левом верхнем углу, определяется байт со смещением 0 из нулевого цветового слоя. Второй пиксел определяется байтом со смещением 0 из первого слоя, третий пиксел - байтом со смещением 0 из второго слоя, четвертый пиксел - байтом со смещением 0 из третьего слоя. Пятый пиксел определяется байтом со смещением 1 из нулевого слоя и так далее.

Таким образом для пиксела с координатами x и y байт, который определяет его цвет, расположен со смещением (x + y * PIXEL_PER_LINE) / 4, в цветовой плоскости (x + y * PIXEL_PER_LINE) mod 4. В этой формуле константа PIXEL_PER_LINE должна определять горизонтальную разрешающую способность экрана в данном режиме.

Такая организация видеопамяти, хотя и более неудобна для вычисления адреса пикселов, чем линейная организация памяти режима 13h, но дает другие неоспаримые преимущества.

Во первых, в режиме с разрешением 320х400 пикселов мы можем использовать две страницы видеопамяти, первая из них имеет нулевое смещение, а вторая смещение 8000h от начала видеопамяти. Режим с разрешением 360х480 пикселов позволяет иметь только одну страницу, но так как он использует только 172800 байт из 256 килобайт, то неиспользуемую память можно использовать для хранения пиктограмм и шрифтов.

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

Режим 320х400 пикселов, 256 цветов

Мы начнем рассмотрение нестандартных режимов с режима, имеющего разрешение 320х400 пикселов. Программирование этого режима является самым простым и безопасным, так как при его установке нам не придется изменять содержимое регистров контроллера ЭЛТ.

Как мы указывали при описании режимов видеоадаптеров, в режиме 13h используется двойное сканирование. То есть в этом режиме - 320х200 пикселов на самом деле отображается не 200, а 400 линий сканирования. Перепрограммировав несколько регистров адаптера можно перевести его в режим 320х400 пикселов.

Рассмотрим последовательность действий, необходимую для перевода видеоадаптера в нестандартный режим с разрешением 320х400 пикселов:

Теперь мы приведем программу, которая реализует изложенный алгоритм и переводит видеоадаптер в режим отображающий 256 цветов при разрешающей способности 320х400 пикселов.

/**
*      Включаемый файл vga_new.h
**/

// сегмент видеопамяти для режима 13h
#define VGA_SEGMENT     0a000h     


// регистр определения различных режимов работы
#define MOR  3c2h    

// адрес индексного порта синхронизатора
#define SC_INDEX   3c4h  

   // регистр разрешения записи цветового слоя
   #define CPWER   2    

   // регистр определения структуры памяти
   #define MMR     4    


// адрес индексного порта графического контроллера
#define GC_INDEX   3ceh  

   // регистр выбора читаемого слоя
   #define RPSR    4   

   // регистр режима работы
   #define MDR     5   

   // регистр смешанного назначения
   #define MIR     6   


// адрес индексного порта контроллера ЭЛТ (цветной режим)   
#define CRTC_INDEX      3d4h     

   // регистр высоты символов текста
   #define MSLR    9    

   // регистр начального адреса
   #define SAR_h   0ch  

   // регистр положения подчеркивания символа
   #define ULR     14h  

   // регистр управления режимом
   #define MCR     17h  


// режим 320х400 пикселов

// число пикселов по вертикали 
#define SCREEN_HEIGHT  400 

// число пикселов по горизонтали  
#define SCREEN_WIDTH   320 


// режим 360х480 пикселов

// число пикселов по вертикали 
#define SCREEN_HEIGHT_H 480 

// число пикселов по горизонтали 
#define SCREEN_WIDTH_H  360 
/**
*      Файл   e256mres.c
**/
#include "sysp.h"
#include "sysgraph.h"
#include <dos.h>

#include    <graph.h>
#include    "vga_new.h"

/**
*.Name    Set320x400Mode
*
*.Title   Установка режима 320х400 пикселов, 256 цветов.
*
*.Proto   void Set320x400Mode( void )
*
*.Params  Не используются.
*
*.Return  Не используетя.
*
*.Sample  e256mres.c
**/

void    Set320x400Mode( void ) {

   _asm  {

      // сохраняем регистр di
      push    di

      // устанавливаем стандартный режим 13h (320x200
      // пикселов, 256 цветов)
      mov     ax,0013h
      int     10h

      // выбираем регистр определенияя структуры памяти
      mov     dx,SC_INDEX
      mov     al,MMR
      out     dx,al

      // считываем значение регистра определения
      // структуры памяти
      inc     dx
      in      al,dx

      // сбрасываем бит D4
      and     al,11110111b

      // устанавливаем бит D3, при этом выключается
      // режим адресации по четным и нечетным адресам к
      // разным слоям памяти
      or      al,00000100b

      // записываем в регистр новое значение
      out     dx,al


      // после загрузки в этот регистр нового значения
      // структура видеопамяти соответствует режимам 10h
      // и 12h за исключением того, что каждому пикселу
      // соответствует один байт видеопамяти

      // выбираем регистр режима работы графического
      // контроллера

      mov     dx,GC_INDEX
      mov     al,MDR
      out     dx,al

      // считываем его значение
      inc     dx
      in      al,dx
      
      // выключаем доступ по четным адресам к четным
      // слоям, а по нечетным адресам к нечетным слоям
      and     al,11101111
      out     dx,al

      // выбираем регистр смешанного назначения
      // графического контроллера
      dec     dx
      mov     al,MIR
      out     dx,al

      // считываем его значение
      inc     dx
      in      al,dx

      // сбрасываем бит управляющий сцеплением четных и
      // нечетных слоев
      and     al,11111101b
      out     dx,al

      // разрешаем запись днных во все четыре цветовых
      // слоя, записывая число 0fh в регистр разрешения
      // записи цветового слоя

      mov     dx,SC_INDEX
      mov     al,CPWER
      out     dx,al

      inc     dx
      mov     al,00001111b
      out     dx,al

      // очищаем первую страницу видеопамяти, так как
      // установка ржима 13h очищает только первые 64K

      mov     ax,VGA_SEGMENT
      mov     es,ax

      xor     di,di
      mov     ax,di

      mov     cx,8000h
      cld
      rep     stosb

      // выбираем регистр высоты символов текста
      // контроллера ЭЛТ
      mov     dx,CRTC_INDEX
      mov     al,MSLR
      out     dx,al

      inc     dx
      in      al,dx

      // запрещаем двойное сканирование
      and     al,01100000b
      out     dx,al

      // выбираем регистр положения подчеркивания
      // символа
      dec     dx
      mov     al,ULR
      out     dx,al

      // выключаем режим адресации видеопамяти по
      // двойным словам
      inc     dx
      in      al,dx
      and     al,10111111b
      out     dx,al

      // выбираем регистр управления режимом
      dec     dx
      mov     al,MCR
      out     dx,al

      // включаем байтовый режим адресации
      inc     dx
      in      al,dx
      or      al,01000000b
      out     dx,al

      pop     di
   }
}




/**
*.Name    WritePixel
*
*.Title   Отображение пиксела.
*
*.Descr   Функция отображает на экране пиксел в заданных
*      координатах,
*         определенного цвета.
*
*.Proto   void WritePixel(unsigned x, unsigned y, unsigned
*                char color)
*
*.Params  x - x-координата пиксела (0-319),
*
*         y - y-координата пиксела (0-399),
*
*         color - цвет пиксела (0-255).
*
*.Return  Не используетя.
*
*.Sample  e256mres.c
**/

void  WritePixel(unsigned x, unsigned y,
           unsigned char color) {

   _asm  {

      push    di

      mov     cx,x
      mov     dx,y
      mov     bl,color

      mov     ax,VGA_SEGMENT
      mov     es,ax

      mov     ax,( SCREEN_WIDTH / 4 )
      mul     dx

      push    cx

      shr     cx,1
      shr     cx,1

      add     ax,cx
      mov     di,ax

      pop     cx

      and     cl,3
      mov     ah,1
      shl     ah,cl

      mov     dx,SC_INDEX
      mov     al,CPWER
      out     dx,ax

      mov     es:[di],bl

      pop     di
   }
}



/**
*.Name    ReadPixel
*
*.Title   Определение цвета пиксела.
*
*.Descr   Функция возвращает значение байта видеопамяти,
*      определяющего пиксел 
*         с заданными координатами.
*
*.Proto   unsigned char ReadPixel(unsigned x, unsigned y,
*                       unsigned char color)
*
*.Params  x - x-координата пиксела (0-319),
*
*         y - y-координата пиксела (0-399).
*
*.Return  цвет пиксела (0-255).
*
*.Sample  e256mres.c
**/

unsigned char   ReadPixel( unsigned x, unsigned y ) {

   unsigned char  color;

   _asm  {

      push    si

      mov     cx,x
      mov     dx,y

      mov     ax,VGA_SEGMENT
      mov     es,ax

      mov     ax,( SCREEN_WIDTH / 4 )
      mul     dx

      push    cx

      shr     cx,1
      shr     cx,1

      add     ax,cx
      mov     si,ax

      pop     ax

      and     al,3
      mov     ah,al

      mov     dx,GC_INDEX
      mov     al,RPSR
      out     dx,ax

      mov     al,es:[si]
      mov     color,al

      pop     si
   }

   return( color );
}


/**
*.Name    Full_Scr
*
*.Title   Закрашивает экран заданным цветом.
*
*.Proto   void Full_Scr( unsigned char color )
*
*.Params  color - цвет экрана (0-255).
*
*.Return  Не используетя.
*
*.Sample  e256mres.c
**/

void Full_Scr( unsigned char color ) {

   _asm {

      ;разрешаем запись данных во все четыре цветовых
      ;слоя

      push    di

      mov     dx,SC_INDEX
      mov     al,CPWER
      out     dx,al

      inc     dx
      mov     al,0fh
      out     dx,al


      mov     ax,VGA_SEGMENT
      mov     es,ax

      xor     di,di
      mov     al,color

      mov     cx,32000
      cld
      rep     stosb

      pop     di
   }
}



// функция LoadVGA256 загружает регистры таблицы цветов
// цифро-аналогового преобразователя новыми значениями

void LoadVGA256(void) {

   RGB color_table[256];
   unsigned char   i, j;
   unsigned char   far *ptr;
   unsigned seg_table,off_table;

// записываем в массив color_table новые значения для
// регистров таблицы цветов

   for(j = 0; j < 4; j++) {
      for(i = 0; i < 64; i++) {
         (color_table[i+j*64]).red = (j == 0) ? i : 0;

         (color_table[i+j*64]).green = 
               (j == 1) ? i : (j == 3) ? i : 0;

         (color_table[i+j*64]).blue = 
               (j == 2) ? i : (j == 3) ? i : 0;
      }
   }

   ptr = (unsigned char far*) &color_table[0];

// определяем сегмент и смешение массива color_table

   seg_table = FP_SEG(ptr);
   off_table = FP_OFF(ptr);

// загружаем новые значения в регистры таблицы цветов

   SetVgaDAC(seg_table,off_table);

// функция SetVgaDAC загружает регистры таблицы цветов 
// цифро-аналогового преобразователя
// исходный текст функции приведен при описании регистра
// данных таблицы цветов ЦАП VGA (файл vga256.c)
}


//
//   главная функция
//
void main( void ){

   unsigned i;
   char ch = 13;

   struct videoconfig vc;

   // заполняем поля структуры vc

   printf("\n   (C) Frolov G.V., 1992 \n\n");
   _getvideoconfig( &vc );

   // завершаем программу если нет VGA адаптера

   if(vc.adapter != _VGA) {
      printf("Для выполнения программы необходим"
           " адаптер VGA.\n");

      exit(0);
   }

   // устанавливаем режим 320х400 пикселов, 256 цветов

   Set320x400Mode();

   // загружаем регистры ЦАП VGA

   LoadVGA256();


   for(i = 0; i < 400; i++)
      WritePixel(160, (unsigned) i,
                   (unsigned char) (i % 256) );

   for(i = 0; i < 320; i++)
      WritePixel((unsigned) i, 200,
                   (unsigned char) (i % 256) );

   ch = getch();
   if( ch == 27 ) exit(1);

   for(i = 0; i < 320; i++)
      WritePixel((unsigned) i, (unsigned) i,
                   (unsigned char) (i % 256) );

   ch = getch();

   for(i = 0; ((i < 256) && (ch != 27)); i++) {
      Full_Scr( (unsigned char) i );
      ch = getch();
   }


   // возвращаемся в текстовый режим

   _setvideomode(_DEFAULTMODE);

   printf("Привет всем!!!\n");
}

Режим 360х480 пикселов, 256 цветов

Второй рассматриваемый нами нестандартный режим может отображать 256 цветов при разрешающей способности 360х480 пикселов. Программирование этого режима является менее простыми безопасным, чем предыдущего режима, так как нам придется изменять содержимое регистров контроллера ЭЛТ, отвечающих за временные характеристики видеоадаптера. В остальном программирование видеоадаптера осуществляется по тем же правилам, что и в режиме с разрешающей способностью 320х400 пикселов.

Ниже приведена программа, которая переводит видеоадаптер в нестрандартный режим с разрешением 360х480 пикселов:

/**
*      Файл    e256hres.c
**/
#include "sysp.h"
#include "sysgraph.h"
#include <dos.h>

#include    <graph.h>
#include    "vga_new.h"

/**
*.Name    Set360x480Mode
*
*.Title   Установка режима 360х480 пикселов, 256 цветов.
*
*.Proto   void Set360x480Mode( void )
*
*.Params  Не используются.
*
*.Return  Не используетя.
*
*.Sample  e256hres.c
**/

void    Set360x480Mode( void ) {

   _asm  {

   // устанавливаем режим 12h, чтобы очистить видеопамять
      mov     ax,12h
      int     10h

   // устанавливаем стандартный режим 13h 
      mov     ax,0013h
      int     10h

   // перепрограммируем регистр определения структуры
   //  памяти: запрещаем адресацию к разным слоям памяти в
   // зависимости от кратности адреса памяти четырем (бит
   // D4 - chain4)
      mov   dx,SC_INDEX
      mov   ax,0604h
      out   dx,ax

   // производим синхронный сброс и остановку
   // синхронизатора
      mov   ax,0100h
      out   dx,ax

   // адресуемся к регистру определения различных режимов
   // работы
      mov   dx,MOR

   // устанавливаем частоту кадров 60Кц
      mov   al,0e7h
      out   dx,al

   // запускаем синхронизатор
      mov   dx,SC_INDEX
      mov   ax,0300h
      out   dx,ax


   // выбираем регистр режима работы графического
   // контроллера
      mov     dx,GC_INDEX
      mov     al,MDR
      out     dx,al

   // считываем его значение
      inc     dx
      in      al,dx

   // выключаем доступ по четным адресам к четным слоям, а
   // по нечетным адресам к нечетным слоям
      and     al,11101111
      out     dx,al

   // выбираем регистр смешанного назначения графического
   // контроллера
      dec     dx
      mov     al,MIR
      out     dx,al

   // считываем его значение
      inc     dx
      in      al,dx

   // сбрасываем бит управляющий сцеплением четных и
   // нечетных слоев
      and     al,11111101b
      out     dx,al


   // выбираем регистр конца обратного вертикального хода
   // луча
      mov   dx,3d4h
      mov   al,11h
      out   dx,al

   // снимаем защиту от записи с регистров контроллера
   // ЭЛТ, имеющих индексы от 0 до 7
      inc   dx
      in    al,dx
      and   al,7fh
      out   dx,al

      dec   dx

   // программируем регистры контроллера ЭЛТ, втом числе
   // регистры, определяющие временные параметры режима

   // устанавливаем регистр общей длины линии
   // горизонтальной развертки
      mov   ax,06b00h
      out   dx,ax

   // устанавливаем регистр длины отображаемой части
   // горизонтальной развертки
      mov   ax,05901h
      out   dx,ax

   // устанавливаем регистр начала импульса гашения луча
   // горизонтальной развертки
      mov   ax,05a02h
      out   dx,ax

   // устанавливаем регистр конца импульса гашения луча
   // горизонтальной развертки
      mov   ax,08e03h
      out   dx,ax

   // устанавливаем регистр начала импульса
   // горизонтального обратного хода луча
      mov   ax,05e04h
      out   dx,ax

   // устанавливаем регистр конца импульса горизонтального
   // обратного хода луча
      mov   ax,08a05h
      out   dx,ax

   // устанавливаем регистр числа горизонтальных линий
   // растра
      mov   ax,0d06h
      out   dx,ax

   // устанавливаем дополнительный регистр
      mov   ax,03e07h
      out   dx,ax

   // устанавливаем регистр высоты символов текста
      mov   ax,04009h
      out   dx,ax

   // устанавливаем регистр начала обратного
   // вертикального хода луча
      mov   ax,0ea10h
      out   dx,ax

   // устанавливаем регистр конца обратного
   // вертикального хода луча
      mov   ax,0ac11h
      out   dx,ax

   // устанавливаем регистр начала гашения вертикальной
   // развертки
      mov   ax,0df12h
      out   dx,ax

   // устанавливаем регистр логической ширины экрана
      mov   ax,02d13h
      out   dx,ax

   // устанавливаем регистр положения подчеркивания
   // символа
      mov   ax,014h
      out   dx,ax

   // устанавливаем регистр начала импульса гашения
   // вертикальной развертки
      mov   ax,0e715h
      out   dx,ax

   // устанавливаем регистр конца импульса гашения
   // вертикальной развертки
      mov   ax,0616h
      out   dx,ax

   // устанавливаем регистр управления режимом
      mov   ax,0e317h
      out   dx,ax
   }
}


/**
*.Name    WritePixel_H
*
*.Title   Отображение пиксела.
*
*.Descr   Функция отображает на экране пиксел в заданных
*       координатах, определенного цвета.
*
*.Proto   void WritePixel_H(unsigned x, unsigned y, unsigned
*                  char color)
*
*.Params  x - x-координата пиксела (0-319),
*
*         y - y-координата пиксела (0-399),
*
*         color - цвет пиксела (0-255).
*
*.Return  Не используетя.
*
*.Sample  e256hres.c
**/

void  WritePixel_H(unsigned x, unsigned y, 
                   unsigned char color) {

   _asm  {

      push    di

      mov     cx,x
      mov     dx,y
      mov     bl,color


      mov     ax,VGA_SEGMENT
      mov     es,ax

      mov     ax,( SCREEN_WIDTH_H / 4 )
      mul     dx

      push    cx

      shr     cx,1
      shr     cx,1

      add     ax,cx
      mov     di,ax

      pop     cx

      and     cl,3
      mov     ah,1
      shl     ah,cl

      mov     dx,SC_INDEX
      mov     al,CPWER
      out     dx,ax

      mov     es:[di],bl

      pop     di
   }
}


/**
*.Name    ReadPixel_H
*
*.Title   Определение цвета пиксела.
*
*.Descr   Функция возвращает значение байта видеопамяти,
*      определяющего пиксел с заданными координатами.
*
*.Proto   unsigned char ReadPixel_H(unsigned x, unsigned y,
                      unsigned char color)
*
*.Params  x - x-координата пиксела (0-319),
*
*         y - y-координата пиксела (0-399).
*
*.Return  цвет пиксела (0-255).
*
*.Sample  e256hres.c
**/

unsigned char   ReadPixel_H( unsigned x, unsigned y ) {

   unsigned char  color;

   _asm  {

      push    si

      mov     cx,x
      mov     dx,y

      mov     ax,VGA_SEGMENT
      mov     es,ax

      mov     ax,( SCREEN_WIDTH_H / 4 )
      mul     dx

      push    cx

      shr     cx,1
      shr     cx,1

      add     ax,cx
      mov     si,ax

      pop     ax

      and     al,3
      mov     ah,al

      mov     dx,GC_INDEX
      mov     al,RPSR
      out     dx,ax

      mov     al,es:[si]
      mov     color,al

      pop     si
   }

   return( color );
}


/**
*.Name    Full_Scr_H
*
*.Title   Закрашивает экран заданным цветом.
*
*.Proto   void Full_Scr_H( unsigned char color )
*
*.Params  color - цвет экрана (0-255).
*
*.Return  Не используетя.
*
*.Sample  e256hres.c
**/

void Full_Scr_H( unsigned char color ) {

   _asm {

      push    di

      ;разрешаем запись данных во все четыре цветовых слоя

      mov     dx,SC_INDEX
      mov     al,CPWER
      out     dx,al

      mov     al,0fh
      out     dx,al


      mov     ax,VGA_SEGMENT
      mov     es,ax

      xor     di,di
      mov     al,color

      mov     cx,43200
      cld
      rep     stosb

      pop     di
   }
}


// функция LoadVGA256 загружает регистры таблицы цветов
// цифро-аналогового преобразователя новыми значениями

void LoadVGA256(void) {

   RGB color_table[256];
   unsigned char   i, j;
   unsigned char   far *ptr;
   unsigned seg_table,off_table;

// записываем в массив color_table новые значения для
// регистров таблицы цветов

   for(j = 0; j < 4; j++) {
      for(i = 0; i < 64; i++) {
         (color_table[i+j*64]).red = (j == 0) ? i : 0;

         (color_table[i+j*64]).green = 
               (j == 1) ? i : (j == 3) ? i : 0;

         (color_table[i+j*64]).blue = 
               (j == 2) ? i : (j == 3) ? i : 0;
      }
   }

   ptr = (unsigned char far*) &color_table[0];

// определяем сегмент и смешение массива color_table

   seg_table = FP_SEG(ptr);
   off_table = FP_OFF(ptr);

// загружаем новые значения в регистры таблицы цветов

   SetVgaDAC(seg_table,off_table);

// функция SetVgaDAC загружает регистры таблицы цветов 
// цифро-аналогового преобразователя
// исходный текст функции приведен при описании регистра
// данных таблицы цветов ЦАП VGA (файл vga256.c)
}

//
//   главная функция
//
void main( void ){

   unsigned i;
   char ch = 13;

   struct videoconfig vc;

   // заполняем поля структуры vc

   printf("\n   (C) Frolov G.V., 1992\n\n");
   _getvideoconfig( &vc );

   // завершаем программу если нет VGA адаптера

   if(vc.adapter != _VGA) {
      printf("Для выполнения программы нобходим"
            " адаптер VGA\n");
      exit(0);
   }


   // устанавливаем режим 360х480 пикселов, 256 цветов

   Set360x480Mode();

   // загружаем регистры ЦАП VGA

   LoadVGA256();

   for(i = 0; i < 480; i++)
      WritePixel_H(180, (unsigned) i,
                  (unsigned char) (i % 256) );

   for(i = 0; i < 360; i++)
      WritePixel_H((unsigned) i, 240,
                  (unsigned char) (i % 256) );

   ch = getch();
   if( ch == 27 ) exit(1);

   for(i = 0; i < 360; i++)
      WritePixel_H((unsigned) i, (unsigned) i,
               (unsigned char) (i % 256) );

   ch = getch();

   for(i = 0; ((i < 256) && (ch != 27)); i++) {
      Full_Scr_H( (unsigned char) i );
      ch = getch();
   }


   // возвращаемся в текстовый режим

   _setvideomode(_DEFAULTMODE);

   printf("Привет всем!!!\n");
}