Программирование видеоадаптеров CGA, EGA и VGA© Александр Фролов, Григорий ФроловТом 3, М.: Диалог-МИФИ, 1992, 287 стр. 4.2. Видеопамять в графических режимах CGAРаспределение видеопамяти в графических режимах работы видеоадаптера CGA отличается от распределения видеопамяти в текстовых режимах. Это вызвано тем, что в графических режимах необходимо хранить информацию о каждом пикселе изображения. Видеоадаптер CGA поддерживает три графических режима - режимы 4, 5 и 6. В следующих двух разделах подробно рассмотрена структура видеопамяти для этих режимов. Режимы 4 и 5Это режимы низкого разрешения (320х200), используются четыре цвета. Поддерживаются видеоадаптерами CGA, EGA и VGA. Режим 5 является режимом с подавлением цвета. Тоесть в этом режиме вместо цвета выводится сигнал яркости серого. Напомним, что как и в режимах 0 и 2, цвет подавляется только на композитном выходе адаптера. Из-за особенностей микросхемы Motorolla 6845, используемой видеоадаптером CGA, отображение видеопамяти на экран не является непрерывным: первая половина видеопамяти (начальный адрес B800:0000) содержит данные относительно всех нечетных линий экрана, а вторая половина (начальный адрес B800:2000) - относительно всех четных линий. Каждому пикселу изображения соответствуют два бита видеопамяти. За верхний левый пиксел экрана отвечают биты D7 и D6 нулевого байта видеопамяти. На рисунке 5.1 изображено соответствие видеопамяти пикселам экрана.
Рисунок 5.1 Структура видеопамяти для режимов 4 и 5. Если вы хотите выводить информацию на экран дисплея непосредственно через видеопамять, то необходимо уметь определять биты, которые управляют каждым пикселом изображения. В общем случае по координатам пиксела нужно вычислить адрес байта видеопамяти и номера битов в нем, управляющие данным пикселом. Следующие формулы позволяют определить смещение байта от начала станицы видеопамяти и номера битов в нем, управляющие пикселом с координатами (x,y): Если y четное число, то смещение байта = 50h*(y/2)+(x/4) Если y нечетное число, то смещение байта = 2000h+50h*((y-1)/2)+(x/4) Номер первого бита = 7-mod(x/4)*2 В этом выражении функция mod(x/y) возвращает остаток от деления x на y. Ниже представлена таблица соответствия значений битов, определяющих пиксел цвету пиксела: Значение битов Стандартный Альтернативный пиксела цвет цвет 00 черный черный 01 светло-синий зеленый 10 малиновый красный 11 ярко-белый коричневый Таблица 5.1 В режимах 4 и 5 имеются два набора цветов - стандартный и альтернативный. Для выбора используемого набора цветов можно воспользоваться функцией 0Bh прерывания INT 10h. Теперь мы приведем программу, отображающую пикселы на экране через непосредственный доступ к видеопамяти. Стержнем этой программы является функция Pixel_Offs_4_5. Первые два ее параметра определяют координаты пиксела. Третий параметр - это указатель на смещение байта, определяющего пиксел в видеопамяти. Четвертый параметр определяет номер младшего из битов, который отвечает за данный пиксел. Заметим, что так как каждый байт в этом режиме определяет четыре пиксела, то при изменении одного пиксела надо позаботится о сохранении остальных трех пикселов. #include "sysp.h" Pixel_Offs_4_5(unsigned x, unsigned y, unsigned *offset, unsigned char *shift) { unsigned char bit_shift; unsigned byte_offset; _asm { ; записываем в bx x-координату пиксела mov bx,x ; запоминаем в регистре cl младший байт переменной x mov cl,bl ; записываем в ax y-координату пиксела mov ax,y ; вычисляем смещение байта, определяющего пиксел с ; координатами (x,y) ; ax = 100h * y xchg ah,al ; в бит D7, регистра al, записываем младший бит ; переменной y; этот бит равен единице, если значение ; переменной y нечетное, и равен нулю, если оно четное ; al = 80h * (y&1) ; ax = ax/2 = 80h * y shr ax,1 ; bx = x + 8000h*(y&1) add bh,al ; ax = 100h*(y/2) xor al,al ; bx = x + 8000h*(y&1) + 100h*(y/2) add bx,ax ; ax = 40h*(y/2) shr ax,1 shr ax,1 ; bx = x + 8000h*(y&1) + 100h*(y/2) + 40h*(y/2) = ; = x + 8000h*(y&1) + 140h*(y/2) add bx,ax ; bx = x/4 + 2000h*(y&1) + 50h*(y/2) shr bx,1 shr bx,1 ; теперь bx содержит смещение байта, определяющего ; пиксел с координатами (x,y) относительно начала ; видеопамяти (страницы видеопамяти) mov byte_offset,bx ; определяем биты, отвечающие за пиксел (x,y) ; записываем в регистр cl два последних бита переменной ; x mov ah,3 and cl,ah ; cl = 3 - (x & 3) xor cl,ah ; cl = cl * 2 shl cl,1 ; регистр задает число бит для сдвига влево mov bit_shift,cl } *shift = bit_shift; *offset = byte_offset; } // // главная функция // void main(void) { unsigned offset,i; unsigned char mask,shift,color; unsigned char far *ptr; unsigned char current, w_pixel; // переводим видеоадаптер в режим 4 _asm { xor ax,ax mov al,4 int 10h } // отображаем на экране несколько точек // непосредственно через видеопамять for(i=0;i<198;i++){ // изменяем цвет пиксела color = i % 4; // определяем смещение (offset) и сдвиг (shift) // для пиксела с координатами x и y Pixel_Offs_4_5(i,i,&offset,&shift); // получаем указатель на байт, определяющий // нужный нам пиксел ptr = (unsigned char far*) (FP_MAKE(0xB800, offset)); // получаем старое значение байта, определяющего // пиксел current = *ptr; // записываем в видеопамять старое значение, // но с измененными битами, отвечающими за наш // пиксел w_pixel = (unsigned char) color << shift; *ptr = current | w_pixel; } getch(); } Режим 6Режим 6 (640х200) является режимом наибольшего разрешения для видеоадаптера CGA. На рисунке 5.2 отображено соответствие видеопамяти и пикселов экрана. Как и в режимах 4 и 5, первая половина видеопамяти содержит данные относительно всех нечетных линий экрана, а вторая половина - относительно всех четных линий. В данном режиме на один пиксел отводится один бит видеопамяти. Таким образом каждый байт видеопамяти управляет восьмью пикселами. Если значение бита видеопамяти, отвечающего за данный пиксел, равно нулю, то пиксел имеет черный цвет, если единице - белый. За верхний левый пиксел экрана отвечает бит D7 в нулевом байте видеопамяти, то есть самый старший его бит.
Рисунок 5.2 Структура видеопамяти в режиме 6. При непосредственном доступе к видеопамяти вы можете воспользоваться следующими формулами: Если y четное число, то смещение байта = 50h*(y/2)+(x/8) Если y нечетное число, то смещение байта = 2000h+50h*((y-1)/2)+(x/8) Номер бита = 7-mod(x/8) Эти формулы позволяют определить для пиксела, имеющего координаты (x,y), смещение от начала станицы видеопамяти байта и номер бита в нем, управляющего данным пикселом. Теперь мы приведем программу, отображающую пикселы на экране через непосредственный доступ к видеопамяти. Стержнем этой программы является функция Pixel_Offs_6. Первые два ее параметра определяют координаты пиксела. Третий параметр - это указатель на смещение байта, определяющего пиксел в видеопамяти. Четвертый параметр определяет номер младшего из битов, который отвечает за данный пиксел. Заметим, что так как каждый байт в этом режиме определяет восемь пикселов, то при изменении одного пиксела надо позаботится о сохранении остальных семи пикселов. #include "sysp.h" void Pixel_Offs_6(unsigned x, unsigned y, unsigned *offset, unsigned char *shift) { unsigned char bit_shift; unsigned byte_offset; _asm { ; записываем в bx x-координату пиксела mov bx,x ; запоминаем в регистре cl младший байт переменной x mov cl,bl ; записываем в ax y-координату пиксела mov ax,y ; вычисляем смещение байта, определяющего пиксел с ; координатами (x,y) ; ax = 100h * y xchg ah,al ; в бит D7, регистра al, записываем младший бит ; переменной y; этот бит равен единице, если значение ; переменной y нечетное, и равен нулю, если оно четное ; al = 80h * (y&1) ; ax = ax/2 = 80h * y shr ax,1 ; bx = x/2 shr bx,1 ; bx = x/2 + 8000h*(y&1) add bh,al ; ax = 100h*(y/2) xor al,al ; bx = x/2 + 8000h*(y&1) + 100h*(y/2) add bx,ax ; ax = 40h*(y/2) shr ax,1 shr ax,1 ; bx = x/2 + 8000h*(y&1) + 140h*(y/2) add bx,ax ; bx = x/8 + 2000h*(y&1) + 50h*(y/2) shr bx,1 shr bx,1 ; bx = byte offset in video buffer mov byte_offset,bx ; cl = x & 7 and cl,7 ; cl = number of bits to shift left xor cl,7 mov bit_shift,cl } *shift = bit_shift; *offset = byte_offset; } // // главная функция // void main(void) { unsigned offset,i; unsigned char mask,shift; unsigned char far *ptr; unsigned char current,w_pixel,color; // устанавливаем режим 6 _asm { xor ax,ax mov al,6 int 10h } // отображаем на экране несколько точек // непосредственно через видеопамять for(i=0;i<199;i++){ color = i % 2; // определяем смещение (offset) и сдвиг (shift) // для пиксела с координатами x и y Pixel_Offs_6(i,i,&offset,&shift); // получаем указатель на байт, определяющий // нужный нам пиксел ptr = (unsigned char far*) (FP_MAKE(0xB800, offset)); // получаем старое значение байта, определяющего // пиксел current = *ptr; // записываем в видеопамять старое значение, // но с измененными битами, отвечающими за наш // пиксел w_pixel = (unsigned char) color << shift; *ptr = current | w_pixel; } getch(); } Организация видеопамяти адаптера HerculesДля полноты картины мы рассмотрим структуру видеоадаптера Hercules. В графических режимах видеоадаптер Hercules использует один бит на пиксел. Разрешающая способность составляет 720 пикселов по горизонтали и 348 пикселов по вертикали. Hercules создан на основе микросхемы 6845 контроллера ЭЛТ и имеет еще более сложную организацию памяти, чем CGA. Видеопамять разделена на четыре части. Структура памяти приведена на рисунке 5.3.
Рисунок 5.3 Структура памяти видеоадаптера Hercules в графических режимах. Формулы приведенные ниже позволяют определить смещение байта от начала станицы видеопамяти и номер бита в нем, управляющего пикселом с координатами (x,y): Если [y/4]=0, то смещение байта = 5Ah*(y/4)+(x/8) Если [y/4]=1, то смещение байта = 2000h+5Ah*((y-1)/4)+(x/8) Если [y/4]=2, то смещение байта = 4000h+5Ah*((y-2)/4)+(x/8) Если [y/4]=3, то смещение байта = 6000h+5Ah*((y-3)/4)+(x/8) Номер бита = 7-mod(x/8) |