图形模式

VGA/SVGA卡通常都有两种模式:文本和图形。在视频游戏编程中,我们所感兴趣的是它们的图形显示模式。

  1)VGA的对应方式

  VGA又称视频图形阵列,是CGA、EGA的后继产品,所以尽管其从特征上说与前辈们完全不同,但出于向下兼容的考虑,VGA存储器的大部分存取模式与CGA、EGA类似。

  因显示模式的不同,VGA视频内存的起始位置会有所不同,可分为B8000H和A0000H两类,而且不同的模式有不同的对应方式:a)交错式对应, b)颜色平面对应,c)线性对应。不错,VGA卡是够复杂的,同时它是一个寄存器级别的可编程图形卡,如果你愿意,尽管可以通过编写内部寄存器程序来取得你所喜爱的图形模式和分辨率。这听来相当不错,但并不常用,因为我们在多数情况下宁可遵循标准的BIOS视频模式。

  交错式对应主要是为了同CGA兼容而设置的。何谓交错,是指视频内存被一分为二,一块用于存放屏幕偶数行的扫描线数据,一块用于存放屏幕奇数行的扫描线数据,它的起始位置为B8000H,起始地址相差02X000。VGA的4、5、6号模式都属于以上对应方式,但我们现在基本上已不再使用了。

  模式 12H是大家经常会遇到的:支持640×480,视频内存起始地址为A0000H,和其他D、E、10H三种模式一样同为16色,只是分辨率有所不同。它们的对应方式称为平面式对应:在视频内存地址上,每一个字节代表了8个像素,视频内存与屏幕是按顺序一一对应的(并非交错),但每一位仅能表示出2种颜色,所以我们要将数据分别写到四个颜色平面中对应的位上,才能表示出所有的16色。通过控制VGA的寄存器组(一般一组寄存器共用一个I/O口,另用一个专门的索引寄存器来指明),可获得4种颜色平面的写入模式及2种颜色平面的读取模式。日本人对这种16色的高分辨图形模式情有独钟(以至于 WATCOM C的函数集中竟也提供了PC98的16色图形接口),但它对美工要求相对较高,不然,你获取的效果也许只和4色的“打飞机”差不多!平面式对应中余下的两种模式11H和F仅能显示黑白两色,其中F就是以前常说的VGA单色模式,640×350,2色。

  单色、4色、16色……我们不要忘了VGA也给我们提供了256色的特殊模式,而且所说的256色可是能从256K色调色板中自由选取的!这就是VGA的13H(320×200,支持256色),大量的游戏都采用了这种显示模式就足以说明它的奇妙之处。

  13H是VGA卡中唯一可同显256色的标准BIOS视频模式,它采用的是线性对应的方法。线性对应是指屏幕上每一点与视频内存地址为一对一的关系,即一个点对应一个字节,这样做的好处是:这种对应方式比颜色平面对应方式简单多了,因为我们只需直接对视频内存地址做读写即可,而不必理会颜色平面与内存地址的对应关系(线性对应的实质是将四个颜色平面的内存串联在一起,成为一种直线的排列)。

  2)0x13初始化

   VGA卡的0x13模式提供了320×200的分辨率(屏幕上每行有320个像素,一共有200行),并支持屏幕上同显256色。这里所说的256色仅仅是因为受到VGA调色板的功能限制,而VGA所能支持的颜色数远远要超过它,实际上共有256K种的颜色可供用户选择,这样的一个颜色集合我想大多数情况下已经够用了!

  在VGA卡以前(包括VGA卡的其它模式),因为它们对视频缓冲是运用了一个多层的结构概念,如前一节中前两种对应方式所述,使得许多初学者、包括熟练的程序员不得不整日陷于各式各样的位移动、位旋转等等烦人的操作切换,而0x13模式却是相当简便又容易编程的,因为它是采用线性内存组织方式来存取,这也是它为什么被广泛应用于游戏编程的第一个原因。

  现在,我们来看看这个吸引力究竟有多大。何谓视频内存?它又称视频缓冲区,是所有CGA、EGA、VGA、SVGA等等图形模式用以显示屏幕上位图内容的内存块。模式0x13的视频内存起始地址为 A000:0000,这是什么意思呢?该地址的第一个部分A000叫做起始段或视频段,冒号后的第二部分0000称为内存偏移段。0x13模式视频内存的结束地址为A000:F9FF,我们不妨来进行计算一下(转换十六进制),这段内存区域的总和正好是64000(字节)=320×200,这样320× 200的分辨率允许我们在视频内存的每个字节中存储一个像素,这就是俗称的大平面概念。

  那么,我们如何对这个视频存储器实现写入操作呢?在C、C++中,我们可以创建一个远程far指针(Far Pointer),使它指向起始地址A000:0000h,如果我们需要改变某一点像素,那么移动这个指针使它在视频内存中偏移一段距离,落到那个像素点上去。

  我们知道,由于0x13模式的内存空间是线性的,屏幕上每一行像素点都是按照顺序排列的,所以内存中实际存储的字节值亦代表了屏幕上像素点的颜色。视频内存的每个字节可存储一个0到255之间的值,这个值也就是像素的颜色(为256色之一),可在颜色查寻表中找到像素颜色的索引,再根据表中的R、G、B 值合成屏幕上像素点的颜色。有一点请注意,这里所说的是指调色板中的索引表,而不是真实意义上的颜色,我们将留待稍后再解析其原理。不错,0x13模式的分辨率320×200显得偏低,但在今天这种分辨率仍被业界定义为游戏的一个标准,可能要归功于它的支持256色。由于我们的视频游戏中包括大量的色彩、动画,加上美工的技巧,都会在一定程度上掩盖分辨率的缺陷,想想看,属于320×200的《仙剑奇侠传》、《阿狗阿猫》等的画面及汉堂《炎龙骑士团》系列的战斗动画,都易使玩家忽略了较低的解析度,甚至让人产生以为同时采用了不止256种颜色的错觉,这就是0x13模式为游戏编程乐于采用的第二点原因,它的支持256色特征。

  那么,它的第三点原因是什么呢?那就是快。这种模式下的分辨率比不上SVGA精细,但我们若采用了640× 480或更高的解析度,同时也将向视频内存中写入更多的像素,这将大大增加刷新新画面所需的时间。这在当今的奔腾机加高速显示卡配置下已不成问题,但在以前的386、486低档机上要想让游戏有效地运行,这些都是致命的难题。如果当时的DOOM采用了SVGA,我想没有几个人的机器能跑得起来,现在支持高分辨的游戏中仍含有320×200这一栏,同样是出于这个历史的原因(相当长时期内视频游戏的标准),以让出品的游戏尽可能地满足玩家不同的机器配置。

  设置显示模式是十分简单的第一步,如果你从来没有设置过任何图形模式或者仅仅是调用过几个C语言版本自带的图形函数库,我想从现在起你应该有这方面的经验,同时,你会发现这是多么的简单。

  设置显示模式有多种方法,一个最具有挑战性的就是手工编制所有的VGA寄存器,但这有时会给我们带来麻烦,即在一种显示卡上可行的工作在另一种卡上并不可行,为了避免它,我们往往采用BIOS(基本输入、输出系统)来完成这个工作。

  在这里,我们需要调用的是BIOS的10号中断。中断是什么?简单地说它是一种"请求",PC机同时支持硬件中断(如键盘发出的申请)和软件中断(可用来调用操作系统资源),而软件中断由程序来执行。10号中断是一种视频BIOS中断,地址040~043H,我们可以用汇编指令INT 10H 完成,当处理器发现这种指令时,就停下当前的进程,跳转或根据转向向量来完成这个中断程序。下面,我们来看看一小段用汇编写成的例程:

 set_0x13mode 设置0x13模式

push ebx : 保存寄存器值

push ecx

push edx

move ax, 0013h : 注1

int 10h : 调用BIOS 10号中断

pop edx : 恢复寄存器值

pop ecx

pop ebx

ret

注1:寄存器ax含有函数代码和BIOS 10号中断调用,其中ax的高位部分ah=0,它告诉BIOS中断我们要设置视频模式,所以称为函数代码;ax的低位部分al=13h,这就是我们所要设置的视频模式0x13。

  当然,我们也很容易用C去编写,下面我们来看看C语言函数库int86()的格式(WATCOM C中为int386()):

 int86 ( int intnum, union REGS *inregs, union REGS *outregs );

// intnum 为中断数

// inregs 为寄存器入口值

// outregs 为寄存器出口值

void set_0x13mode() // 设置0x13模式

{

union REGS in,out;

in.h.ah=0;

out.h.al=0x13;

int86 ( 0x10,&in,&out );

}

  在读懂了汇编例程后相信这个C编码很容易理解,汇编总是可以清楚地表示我们所做的一切。

  另外,我们还需要一个能够在任何时候,将程序从图形模式切换回DOS文本模式的函数:

 void set_textmode() // 设置0x03模式

{

union REGS in,out;

in.h.ah=0;

out.h.al=0x03; // 转换为标准80*25彩色文本模式

int86 ( 0x10,&in,&out );

}

  这个函数在实际运用中很有用,当程序在图形模式下发生错误时,我们希望它显示错误信息并退回到DOS文本初始状态下,下面一段代码表明了一个典型调用方法:

 bz = back(); // bz为一个函数返回值

if (bz)

{

set_textmode();

printf ("Error ××× ; %d ", bz);

exit (1); // 跳出程序

}

  3)SVGA及VBE

  计算机芯片的运算速度及显示速度的提升,使得高分辨在游戏中成为可能。现在这许多三维贴图与真三维游戏甚至支持到1024×768、256色或更高,但即使是奔腾166也难承其荷,对一个电脑发烧友来说,也许游戏才是其真正的升级动力和升级压力。

  一般来说,SVGA卡都至少拥有512K以上的视频内存,以用来存储更高分辨率或更多颜色的数据,它的256色或真彩显示模式都是采用线性对应的方法,所需要的内存数量也各有差别:

 分辨率 颜色数 像素大小 最小内存值

320*200 256 1 64K

640*480 256 1 307K

640*480 16M 3 922K

800*600 256 1 480K

1024*768 256 1 768K

1024*768 32K/64K 2 >1M

  这就是为什么1M的真彩卡最大支持到1024×768、256色的原因。但不管如何,其所占用的视频内存地址仍只有64K,所以当发生以上内存地址不够使用的情况时,SVGA卡采用区域切换(Bank Switching)的方法来解决,即将整个内存划分成许多内存页,然后利用切换功能来把不同的内存页对应到视频内存地址A0000h上(或其它),这样就可以用有限的视频内存地址来存取所有的视频内存了。

  现在,SVGA卡的生产厂商很多,大家周围的SVGA卡可谓琳琅满目,无论出品厂家还是型号都不尽相同,这对于程式开发人员来说,不能不说是一个头痛的问题:各类SVGA 切换内存页的方法都不一样,而且有的显示卡上甚至有两个读、写切换开关。象大家熟悉的Trident卡在不同型号间就有新、旧两种存储页大小模式(128K与64K),它的存储页选择寄存器(Segment Select Refgister)地址在0x3c4;而另一类ET卡,它的存储页选择寄存器地址则为0x3cd,在ET3000上只用前6位来表示读写存储页的位置,对于ET4000W32X,则另外增加了一个扩展存储页寄存器,地址在0x3cb,用来表示读写存储页的4、5位(读写存储页的0~3位仍在0x3cd上不变)。对于这些混乱的状态,美国信息电子标准协会(VESA)提出了一套标准的SVGA卡技术规范,其中制定了许多SVGA卡的功能及可显示的扩展模式(Extended Mode),只要各厂家都遵循这个标准,那就会尽最大可能避免不兼容的情况发生。同时,VEGA也提出了一套软件的标准接口:VESA BIOS EXTENSION,即VBE,它扩展了VGA/SVGA BIOS原有的功能,而我们就可以利用这个统一的接口来进行显示控制(调用10号中断)。

  目前,新型的SVGA卡都内建有VBE的功能,它固化在卡上的BIOS内,对于较旧的SVGA卡可以用外挂式VBE程序来补充,我们在现在的许多SVGA游戏目录下经常会看到UNIVBE.EXE就是用以解决某些卡的不兼容问题的。利用VBE来控制SVGA卡的显示功能,不仅省去了许多复杂的工作,也基本保证了程序对各类SVGA卡的兼容性。

  VBE的功能大致有两种:a)制定了各种标准显示模式及分辨率的规范。我们知道,一般SVGA BIOS的模式编号用1个字节来表示即可,象0x13、0x03等,在VBE中为了区别开来,采用了2个字节的表达方式,高字节的第8位固定为1,例如0x0100、0x0101等,详见稍后的初始化例程附表,当然,这些模式不一定所有的SVGA卡都具备,只是在能力范围内尽量提供符合以上标准的显示模式;b)提供了一些特

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License