Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Последние темы форума

Показать новые сообщения »

Почтовая рассылка

Подписчиков: 11878
Последний выпуск: 19.06.2015

Видеоадаптер: как выйти за предел 256 Кбайт

Рассмотрен метод программирования VGA- и SVGA- графических плат в режиме 640x480 и 800x600 точек при 256 цветах.

Поразительно, что, ограничив память своих VGA-адаптеров 256 Кбайт, корпорация IBM предусмотрела возможность доступа к вдвое большему объему, а это значит, что, пользуясь рассмотренным в упомянутой статье методом, мы можем получить в свое распоряжение режимы 640x480x256 и 800x600x256. Вообще говоря, можно запрограммировать и другие режимы, 830x631 к примеру, но это связано с глубоким перепрограммированием видеоадаптера, не совсем безопасно для дисплея, и, самое главное, не очень понятно, для чего бы это могло понадобиться, поэтому мы такую экзотику рассматривать не будем. Более высокое разрешение, чем 800x600, в DOS обычно не используется, по крайней мере будем надеяться, что для небольших коллективов и отдельных программистов, которые в основном и испытывают трудности из-за обилия несовместимых стандартов на видеоадаптеры, указанных режимов будет достаточно.

   Как правило, объем видеопамяти более 512 Кбайт нужен для того, чтобы увеличить разрядность числа, хранящего информацию о цвете (глубину цвета), до 16 или даже 24 разрядов (около 65 тыс. и 16 млн. цветовых оттенков соответственно), а не для увеличения пространственного разрешения. Кстати, адаптер VGA не поддерживает глубину цвета более 8 разрядов, более того, создается впечатление, что IBM и не предполагала серьезное использование 256 цветов и добавила единственный многоцветный режим скорее в рекламных целях.

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

   До IBM PC все персональные компьютеры (да-да, существовали и такие, причем не так уж мало, и в отличие от отечественных БК0010, РК86 и "Микрош" они были не игрушками, а благодаря операционной системе CP/M и разнообразному ПО вполне работоспособными устройствами) могли адресовать не более 64 Кбайт памяти. Один мегабайт адресного пространства процессора i8086 (i8088) в то время казался таким же неисчерпаемым, как и 64 терабайта 386-го процессора сегодня, а сам компьютер IBM PC имел только 64 Кбайт ОЗУ, как и большинство персоналок того времени.

   IBM щедро отвела десять 64-Кбайт сегментов адресного пространства (десятикратный запас!) под оперативную память, два - под видеопамять и оставшиеся четыре - под ROM BIOS. При этом BIOS компьютера располагалась в последнем сегменте, а три предшествующих сегмента использовались платами расширения, на которых были расположены части BIOS (драйверы соответствующих факультативных устройств). В настоящее время таким образом подключается только видеоадаптер, а для остальных устройств имеются драйверы, загружаемые в оперативную память. Кстати, первая версия MS-DOS не предусматривала возможности использования загружаемых драйверов, и подключать нестандартное оборудование можно было лишь с использованием микросхем ПЗУ на платах расширения. Кроме того, в BIOS IBM PC и IBM PC XT не были заложены возможности работы с жестким диском, и на его контроллер приходилось также ставить микросхемы ПЗУ - так что отведение пятой части всего адресного пространства на эти нужды отнюдь не выглядит неоправданной роскошью.

   В те далекие времена, когда большинство IBM-совместимых компьютеров были еще не "-совместимыми", а просто IBM, не существовало видеоадаптера хорошего во всех отношениях, зато имелись два специализированных адаптера - MDA (монохромный), предназначенный для работы с текстом, со знакоместом 9514 точек и без графических возможностей, и CGA, поддерживающий четырехцветную графику, но зато обладающий знакоместом 858 точек, что делало работу с текстом очень утомительной. Таким образом, на самом деле существовал не один компьютер IBM PC, а два, - они различались видеоадаптерами, а следовательно, и областью применения. Для удобной работы с текстом и графикой в компьютер можно было установить оба видеоадаптера, при этом приходилось пользоваться двумя дисплеями. Чтобы избежать конфликта адресов при одновременном использовании, видеоадаптерам были выделены различные области памяти. MDA при этом достался диапазон адресов B0000 - B7FFFh, который запрещалось использовать CGA. И хотя MDA оказался тупиковой ветвью эволюции, по традиции все наследники CGA, т. е. EGA, VGA и SVGA, также не используют эту область памяти ни в одном из стандартных режимов. Аппаратно никаких ограничений на это нет, именно этим мы и собираемся воспользоваться.

   Появление процессора 386 с его блоком подкачки страниц (Paging Unit) позволило заполнить не используемые ROM BIOS области верхней памяти "перенесенными" туда блоками оперативной памяти для размещения частей операционной системы и программ либо при работе в многозадачной ОС для DOS-сессии сымитировать работу в первом мегабайте. В некоторых случаях программы, осуществляющие подобные манипуляции, исходят из того, что область монохромного адаптера свободна и ее можно использовать. Поэтому необходимо проверять, не использует ли ваш диспетчер верхней памяти (EMM386, QEMM) область монохромного дисплея. Не следует также запускать программы, обращающиеся к видеосегменту B000h, из Windows. В общем-то ничего необычного в этих требованиях нет, многие игры, использующие видеопамять нестандартным способом, отказываются работать в Windows и требуют либо приведения файлов config.sys и autoexec.bat в соответствие со своими требованиями, либо вообще загрузки со специальной дискеты. Ничего не поделаешь - это плата за скорость вывода на экран.

   Программа, демонстрирующая установку и использование режима 800x600x256, приведена в листинге. Для режима 640x480x256 необходимо заменить номер видеорежима на 101h и изменить ряд констант в процедурах PutPixel и Main. Подпрограмма PutPixel, приведенная в листинге, служит лишь для демонстрации способа доступа к видеопамяти. Если вы собираетесь ее использовать, необходимо провести ассемблерную оптимизацию. Два варианта оптимизированной подпрограммы приведены в этом же листинге под именами PutPixel1 и PutPixel2. Время десятикратного заполнения экрана с помощью различных подпрограмм, а также фрагмента кода, помещенного непосредственно в программу, выраженное в единицах отсчета системного таймера, приведено в таблице для двух аппаратных конфигураций компьютера в реальном и защищенном режимах работы процессора.

Время десятикратного заполнения экрана


Конфигурация Процессор/ Видеоадаптер/ Режим процессора 
Время в зависимости от способа вывода на экран (единиц таймера) 
PutPixel 
PutPixel1 
PutPixel2 
Непосредственно 
486-100 / Tseng PCI/Реальный 
257 
154 
112 
46 
486-100/Tseng PCI/Защищенный 
426 
294 
264 
51 
Pentium-100 /Trident PCI/Реальный 
163 
108 
54 
31 
Pentium-100 /Trident PCI/Защищенный 
255 
183 
136 
31 
   Следует отметить, что результаты, приведенные в таблице, справедливы в случае заполнения экрана по столбцам, что оптимально для PutPixel2. Если же экран заполнять по строкам, то PutPixel2 будет немного проигрывать в скорости PutPixel1.

   Как видно из таблицы, наибольшего увеличения скорости вывода удается достичь только при отказе от вызова подпрограммы и размещении операторов вывода непосредственно в теле программы. Особенно это заметно при работе в защищенном режиме процессора. Вообще же, на мой взгляд, процедуры рисования точки могут использоваться только в демонстрационных программах, а при реальном программировании следует применять подпрограммы, оперирующие более крупными объектами, чем точка. Если вас заинтересовал приведенный метод, но видеоадаптер вашего компьютера не поддерживает стандарт VESA или ваша программа должна работать на максимально возможном числе различных компьютеров, следует воспользоваться рекомендациями, изложенными в нашей статье ("Мир ПК", # 5/97, с. 70).

program TestHiResMode;
uses crt;
const
        SeqP = $3c4;    { Базовый номер порта } 
                                        { контроллера синхронизации }
        CrtP = $3d4;    { Базовый номер порта контроллера ЭЛТ }
        GraP = $3ce;    { Базовый номер порта }
                                        { графического контроллера }
        SegA000=$a000;  { Сегмент видеопамяти }
        SegB000=$b000;  { Сегмент видеопамяти }
        Seg0040=$0040;  { Сегмент области данных BIOS }

var
	P                       - Базовый адрес регистра
	New_v   	- новое значение,которое нужно записать в регистр,
	Mask           	- маска,
	Number  	- индекс регистра}

Procedure SetVgaReg(P:word;New_V,Mask,Number:byte);
 Begin
  Inline($0FA);{ Cli - запрещаем прерывания}
  Port[P] := Number;
  Port[P+1] := (Port[P+1] and (not Mask))or (New_V and Mask);
  Inline($0FB);{ Sti - разрешаем прерывания}
End;

{Ожидаем вертикальный обратный ход луча}
Procedure WaitRetrace;
 Begin
  While(Port[$3DA] and $08)=0 do;
 End;

{Устанавливаем текстовый видеорежим 3h}
Procedure SetTextMode;
 Begin
	asm
	mov ax,3
	int $10       {установка видеорежима}
 end;
End;

{Читаем текущее показание системных часов}
function clock:longint;
 begin
  clock:=MemL[Seg0040:$6c];
 end;

{Устанавливаем видеорежим 800x600x256}
Procedure SetHiResMode;
var OutStatus:word;
 Begin
  SetVgaReg(SeqP,$20,$20,1);        { выключаем экран }
  asm
	mov ax,$4f02 {пытаемся установить режим 800x600x256}
	mov bx,$103  		{ для режима 640x480x256 - заменить на $101}
	int $10
	mov OutStatus,ax   	{ и узнаем, что из этого получилось}
  end;
  if Lo(OutStatus) <> $4f then begin 	{VESA не поддерживается}
    SetTextMode;
    writeln('Your card is not VESA-compartible');
    halt;             		{ завершаем работу программы}
  end;
  if Hi(OutStatus) <> 0 then begin 	{ видеорежим не поддерживается }
    SetTextMode;
    writeln('Videomode is not supported');
    halt;             		{завершаем работу программы}
  end;
  WaitRetrace;
  SetVgaReg(CrtP,$40,$40,$17); 	{ устанавливаем режим байтов контроллера ЭЛТ }
  SetVgaReg(CrtP,0,$40,$14);  	{ сбрасываем режим "двойное слово" контр.ЭЛТ}
  WaitRetrace;
  SetVgaReg(SeqP,0,$08,4);  	{ сбрасываем режим "цепочка 4"}
  SetVgaReg(SeqP,$0F,$0F,2); 	{ разрешаем все плоскости для записи }
  SetVgaReg(GraP,0,$0C,6); { диапазон адресов видеопамяти A000:0000-B000:FFFF}
  FillChar(mem[SegA000:0],64000,0);    	{ очистка экрана }
  SetVgaReg(SeqP,0,$20,1);      { включаем экран }
End;

{Рисуем точку на экране}
procedure PutPixel(X, Y : word; Color : byte);
 begin
 { Устанавливаем маску для выбора нужной плоскости }
 PortW[SeqP] := 2 + $100 shl (X and 3);
 { Вычисляем смещение (Y * 800 div 4 + X div 4) и рисуем точку }
 { Для режима 640x480x256 заменить в трех местах 200 на 160 }
 if (longint(Y) * 200 + X div 4) < $10000 then
   Mem[SegA000 : Y * 200 + X shr 2] := Color
 else
   Mem[SegB000 : word(longint(Y) * 200 + X shr 2-$10000)] := Color;
 end;

{Рисуем точку на экране с оптимизацией}
procedure PutPixel1(X, Y : word; Color : byte);assembler;
 asm
	mov dx,SeqP
	mov cx,X
	and cx,3
	mov ax,$100
	shl ax,cl
	add ax,2
	out dx,ax  {устанавливаем маску}
	mov ax,200
	mul Y
	mov bx,X
	shr bx,2   {ax = X div 2}
	add bx,ax  {bx - 16 младших битов смещения}
	adc dx,dx  { если dx не равен 0, то используем сегмент SegB000}
	mov ax,SegA000
	jz @l
	mov ax,SegB000
	@l: mov es,ax
	mov al,Color
	mov es:[bx],al
 end;

const b:byte = 255; {номер плоскости, к которой было последнее обращение}
{Рисуем точку на экране с оптимизацией (2-й вариант)}
procedure PutPixel2(X, Y : word; Color : byte);assembler;
 asm
	mov dx,SeqP
	mov cx,X
	and cx,3
	cmp cl,b
	jz @l1
	mov b,cl
	mov ax,$100
	shl ax,cl
	add ax,2
	out dx,ax  {устанавливаем маску}
	@l1: mov ax,200
	mul Y
	mov bx,X
	shr bx,2   {ax = X div 2}
	add bx,ax  {bx - 16 младших битов смещения}
	adc dx,dx  {если dx не равен 0, то используем сегмент SegB000}
	mov ax,SegA000
	jz @l2
	mov ax,SegB000
	@l2: mov es,ax
	mov al,Color1
	mov es:[bx],al
 end;

{Основная программа}
var i,j,k:integer;
    c1,c2,c3,c4,c5:longint;
begin
  SetHiResMode;
  {выводим наклонные линии всех 256 цветов}
  {для режима 640x480x256 соответственно уменьшить длину циклов}
  c1 := clock;
  for k := 0 to 9 do for i:=0 to 799 do
    for j:=0 to 599 do PutPixel(i,j,lo(i+j));{}
  c2 := clock;
  for k := 0 to 9 do for i:=0 to 799 do
    for j:=0 to 599 do PutPixel1(i,j,lo(i+j));{}
  c3 := clock;
  for k := 0 to 9 do for i:=0 to 799 do
    for j:=0 to 599 do PutPixel2(i,j,lo(i+j));{}
  c4 := clock;
  for k := 0 to 9 do
    for i:=0 to 799 do for j:=0 to 599 do
  asm   { рисование точки непосредственно из тела программы }
	mov dx,SeqP
	mov cx,i
	and cx,3
	cmp cl,b
	jz @l1
	mov b,cl
	mov ax,$100
	shl ax,cl
	add ax,2
	out dx,ax  {устанавливаем маску}
	@l1: mov ax,200
	mul j
	mov bx,i
	shr bx,2   {ax = X div 2}
	add bx,ax  {bx - 16 младших битов смещения}
	adc dx,dx  {если dx не равен 0, то используем сегмент SegB000}
	mov ax,SegA000
	jz @l2
	mov ax,SegB000
	@l2: mov es,ax
	mov ax,i
	add ax,j
	mov es:[bx],al
  end;
  c5 := clock;{}
  SetTextMode;
  writeln('Elapsed time - PutPixel:',c2-c1,' PutPixel1:',
          c3-c2,' PutPixel2:', c4-c3,' Direct:',c5-c4);
end.

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог