Программирование видеоадаптеров CGA, EGA и VGA© Александр Фролов, Григорий ФроловТом 3, М.: Диалог-МИФИ, 1992, 287 стр. 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");
}
|

