VGA图形控制器直接编程技术.docx
《VGA图形控制器直接编程技术.docx》由会员分享,可在线阅读,更多相关《VGA图形控制器直接编程技术.docx(16页珍藏版)》请在冰豆网上搜索。
VGA图形控制器直接编程技术
VGA图形控制器直接编程技术
使用VGA图形控制器直接编程,可以突破C语言提供的图形函数只能实现16色的限制,还可以在同一屏幕中控制图像的灰度,做出许多特技效果。
学习目标
●VGA卡的结构原理
●调用BIOS中断设置图形方式
●灰度级别的应用
VGA卡的概念
VGA图形控制器位于CPU和VideoRAM(视频存储器)的通路之间,主要用来对CPU写入VideoRAM的数据进行各种操作和处理。
它由算术逻辑部件(ALU)、位平面读出暂存器及一些控制寄存器组成。
在初始状态下它是透明的。
即CPU向VideoRAM读出时它对数据不作任何修改。
当直接对图形控制器进行控制时,它能协助我们完成一些原来要由CPU执行的任务。
在图形模式下,它对于绘图算法的快速实现提供了一定的硬件支持。
特别是在对像素操作时,直接使用图形控制器是最快捷的方法。
在VGA卡上,是不能直接对VideoRAM的内容进行读写的,必须通过特殊硬件作界面。
VGA的VideoRAM被分成四个独立的位平面(Bitplane)的形式。
在图形方式下的位平面地址方式是水平的,各位平面具有相同的空间地址,其首地址入口为A000:
000H。
每个位平面的读写操作均可独立地进行。
通常,屏幕上第五个像素的颜色用4个二进制位表示,它的像素值被划分到4个位平面上,分别占用4个位平面的一位。
所以,当CPU要读写VideoRAM地址时,并不是只用到一个字节,而是同时用到4个字节,分别在每一个位平面上。
图形控制器有四个读出数据暂存器(Latch),从VideoRAM的4个位平面读出的数据都同时暂存在其中,直到下一次再更新它们的内容。
每一个Latch对应于一个位平面,当CPU要用到VideoRAM的地址时,读取操作会更新Latch中的值,写入操作会更新位平面的值。
由于VGA与CPU之间的数据通路是单字节宽,只有8个Bit,但内部却有32位的数据通路,因此,从CPU送来的一个字节,在一个写周期内,可以同时向4个位平面写入。
图形控制器的逻辑运算部件主要对CPU与入VideoRAM的数据执行与、或、异或、循环等操作,由于可以对像素的颜色进行逻辑运算,可以大大简化绘图操作,获得一些特殊的效果。
在VGA卡中,与图形控制器工作状态相关的另一个物理器件是操作定序控制器,它是VGA各种寄存器操作和数据传送操作的顺序控制器,起着控制VideoRAM或图形控制器到属性控制器之间的数据流动的作用。
在下面所涉及的各种位平面写入操作中,最终对哪些平面有效,还要受到操作定序控制器中的位平面屏蔽寄存储的影响。
图形控制寄存器访问
对图形控制器的访问必须通过寄存器的读写操作来完成。
图形控制器有9个可定址的寄存器,这些寄存器均为一个字节宽。
由于系统分配给显示器端口地址的数量有限,在访问寄存器时采用了间址选择的方法,即预先将这些寄存器编号(称之为索引号),并添加一个索引寄存器和一个数据寄存器。
它们的存取操作均须通过地址索引寄存器(输出地址为3CEH)和数据寄存器(输入/输出地址为3CFH),前者存放索引号,后者存放索SI号所指定的寄存器写入或读出的数据。
索引寄存器的内容起着控制数据寄存器的功能,所以可将地址为3CFH的数据寄存器看作9个不同的寄存器来使用。
为了访问图形控制器的功能,对图形控制器寄存器的存取操作均须分两步进行:
首先应当把索引号(即功能号)作为寄存器的内容输出到索引寄存器端口3CEH中,然后再对数据寄存器进行读写操作。
图形控制器寄存器的参数及功能
为了实现对图形控制器的直接编程,必须掌握各个寄存器的含义及其用法。
在VGABIDS中,这些寄存器都有其初值,当然,这些值都是可以修改的。
下表列出这9个寄存器的名称和系统初始值。
ROMBIOS中VGA图形控制器所设置的初始值
功能索引号
寄存器名称及作用
系统初值
0
Set/Reset(置位/复位寄存器)
0
1
EnableSet(置位/复位允许寄存器)
0
2
ColorCompare<颜色比较寄存器)
0
3
DataRotate(数据移位寄存OS)
0
4
ReadMapSelect(位平面选择寄存器)
0
5
Mode(模式寄存器)
D0-D3为0
6
Miscellaneous(辅助寄存器)
取决视频模式
7
ColorDon'tCare(颜色忽略寄存器)
0FH或01H
8
BitMask(位屏蔽寄存器)
FFH
下面对这9个图形控制器寄存器进行逐一介绍。
1.置位/复位寄存器(功能号0)
S/R寄存器用来定义区域填充时向显示RAM写入的颜色。
S/R寄存器的D0~D3位分别与4个位平面相对应。
若D2=l,则向位平面2中写入的数据就全为1。
若D1:
0,则向位平面1中写入的数据就全为00
2.置位/复位允许寄存器(功能号1)
通常与S/R寄存器配合使用,启动对应于该寄存器D0~D3位的位平面,用来决定置位/复位操作在哪几个位子面进行。
如果关闭某位平面,则不能修改被关闭平面的颜色,也不能改变由功能0选取的颜色。
通常将OFH输出到ESR寄存器后,便可对0,-3位平面进行操作。
注意:
功能0与功能1仅用于由功能5设定的模式0写操作
3.颜色比较寄存器(功能2)与颜色忽略寄存器(功能7)
这一功能使用寄存器的D0~D3位存储从位平面读入的数据,当CPU向图形控制器指定一个参考色之后,颜色比较器每次都把从4个位平面中同时读出的数据与参考颜色作比较,指出是否与参考颜色相同。
若相同,则结果寄存器相应位置1,否则置o。
颜色忽略寄存器则用来控制哪些位平面参加比较,不参加比较的位平面无论其内容如何,总认为与参考颜色的相应位相等。
此功能仅用于模式寄存器(功能5)中读模式为1时。
4.数据移位寄存器(功能3)
这个寄存器用来控制以下功能:
写入数据的循环位移的逻辑操作。
寄存器在功能5模式。
情况下,该寄存器的D0~D2位表示的数值为向右循环移位的数据位数目。
逻辑运算用来实现显示RAM的读一改一写操作,对于实现光标或图符在屏幕上的移动特别有用。
5.位平面读出选择寄存器(功能4)
用来选取位平面在读模式0时进行读取。
当CPU对VideoRAM进行读出操作时,4个位平面的数据总是同时读出到暂存器中,由于一次只能向CPU送出一个字节,通过设置RMS的D0~D2位的数值可选择哪一个位平面的读出数据字节被送到CPU中去。
例如,当RMS:
01H时,送到CPU去的是从位平面1中读出的一个字节。
6,模式寄存器(功能5)
这里仅介绍功能5的读写模式。
在图形控制器中,无论按字节方式处理还是按像素方式处理,都要选择写入模式或读取模式。
MODE寄存器的DO~D1位值按以下方式确定:
写入模式0:
这是最基本的一种方式。
在CPU执行写入动作时,结合了字节方式和像素方式的操作,CPU所写入的数据可以更新任何一个或是全部的位平面,也可以更新暂存器的2个像素或是其中的一个。
在此模式下,可使用功能3提供的数值对处理器数据进行循环移位和逻辑操作,可使用功能。
和功能1启动位平面并设置像素颜色,可使用功能8来设置所有的位平面对应位等。
写入模式1:
此时向VideoRAM写入的仅仅是从Latch读出的数据,而与CPU送来的数据无关。
这时的移位、逻辑运算、置位/复位等操作均不起作用。
在此模式下,可实现对VGA表面上的一个彩色区域从一个位置拷贝到屏幕的另一个位置。
写入模式2:
此模式可将由CPU送来的数据字节的D0~D3位的数值按位分别写入0~3个位平面的所有8位中去。
例如该字节第。
位为1,则位平面。
中则为全l字节。
写入模式3:
它把置位/复位寄存器中的数据写到相应的位平面中去,例如DO=O时,位平面0中写入为全1字节。
功能5的读取模式共有两种,分别由MODE寄存器的第三位设置读模式(o,1两种状态)。
读模式0:
将读取由功能4选取的位平面中之一的一个字节的内容送到CPU中去。
读模式1:
使CPU获得4个位平面在指定地址处像素颜色与参考颜色的比较结果。
7.辅助寄存器(功能6)
该寄存器的D1~D2用来决定VGA的VideoRAM在CPU可寻址的主存储空间的入口地址及范围。
它的值在系统确定显示模式时已设定,一般不再改变。
例如D3和D2的值分别为0时,这时显存地址被设定为A000~BFFFH之间。
8.位屏蔽寄存器(功能8)
位屏蔽寄存器用来控制写入VideoRAM去的数据字节中,哪些位应该修改,哪些位应该保持不变,从而实现对VideoRAM的读一改一写操作,这对于以像素为单位画图时特别有用。
VGA图形控制器编程实例
我们所讨论的只是VGA的图形控制器一些最基本的内容,要想熟练掌握和灵活使用好图形控制器是要付出许多努力的。
为帮助读者理解和掌握VGA图形控制器的直接编程方法,这里我们给出一个用TURBOC实现的程序实例,供读者参考。
#include"graphics.h"
#include"dos.h"
#include"stdio.h"
#defineENABLE0x0F
#defineINDEXREG0x3CE
/*定义索引寄存器端口地址*/
#defineVALREG0x3CF
/*定义数据寄存器端口地址*/
#defineVGABASE0xA0000000L
#defineWIDTH80L
#defineXMAX639
#defineYMAX479
#defineXMIX0
#defineYMIN0
#defineOUTINDEX(index,val)outport(INDEXREG,index);outport(VALREG,val);
charfar*vgabase;
voidputpoint(intx,inty,intcolor,inthow);
main()
{
intgmode=VGAHI,gdriver=VGA;
intcolor=1,x,y;
initgraph(&gdriver,&gmode,"");
/*绘制各种颜色的直线段*/
for(y=1;y<=479;++y){
for(x=1;x<=639;++x)
putpoint(x,y,color,0);
color++;}
getch();
/*用异或方式擦除各条直线*/
color=1;
for(y=1;y<=479;++y){
for(x=1;x<=639;++x)
putpoint(x,y,color,0x18);
color++;}
getch();
closegraph();
}
/*利用图形控制寄存器功能写像素函数*/
voidputpoint(intx,inty,intcolor,inthow)
{
registerunsignedcharmask=0x80;
registercharfar*base;
unsigneddummy;
vgabase=(charfar*)MK_FP(0xA000,0);
base=(charfar*)(vgabase+((long)y*
WIDTH+(long)x/8L));
mask>>=x%8;
dummy=*base;
OUTINDEX(0,color);/*设定额色*/
OUTINDEX(1,ENABLE);/*启动位平面*/
OUTINDEX(3,how);/*确定逻辑操作方式*/
OUTINDEX(8,mask);/*设置屏蔽位*/
*base=1;
OUTINDEX(0,0);/*寄存器复位*/
OUTINDEX(1,0);
OUTINDEX(3,0);
OUTINDEX(8,0xff);
}
TVGA显示原理
SuperVGA是目前微机上显示方式极为普遍的配置,但SuperVGA并无统一标准,各种高级语言不支持SuperVGA方式的显示。
要实现高分辨多色彩图形显示,必须清楚SuperVGA的显示原理。
Windows是目前受人们欢迎的操作系统,位图(.BMP)文件是它图形文件的标准格式,但用户在用TurboC开发图形图像程序时,不能直接取用.BMP位图文件,这不能不说是一件遗憾之事。
下面就针对以上问题提出解决的方法。
这里只讨论256色下的图形显示技术。
1.内存映射关系
TVGA8900卡有512KB和1024KB显示缓冲区1种(512KBVRAM不支持1034X768的256色模式)。
DOS环境从A0000H~BFYFFH共有128KB的存储空间留给显示器,分为64KB的A0000H—久1TFPH和128KB的页方式映射到TVGA的VRAM来实现显示的。
2.寻址技术
要实现SuperVGA的显示,首先是将图形缓冲区中数据送到CPURAM,然后映射到TVGAVRAM中实现。
对于256色模式来说,屏幕上的一点占8位(28=256)1字节,VRAM中的数据从0KBdl024KB,每个字节顺序在对应屏幕中按行逐点对应。
CPURAM只有64KB,而VRAM有1024KB,要使图形数据缓冲区中的数据通过CPURAM和TVGAVRAM建立一一对应关系实现显示,关键是CPURAM如何映射到VRAM。
分析TVGA结构可以得到,TVGA(1024KB)VRAM被分成16个段,每段64KB,通过访问3C4H定序器地址寄存器来实现A0000H~AFFFFH64KB对应于TVGAVRAM不同的段,这样就得到了屏幕上一点(x,y)与CPURAM和TVGAVRAM的寻址关系:
xy=x+y*MAXX(MAXX为每行的最大值)
其中,xy为点(x,y)在VRAM中的线性地址
SEG:
xy/10000H=xy/65536=xy>>16
其中,SEG为点(x,y)在VRAM中的段号。
OFF:
xy&FFFFH
OFF是点(x,y)在VRAM段中的偏移量,即CPURAM中的线性地址
3.设置显示模式
SupervGA的扩充显示模式不像标准vGA只需设置相应的视频方式,它还需要设置页映射方式以及切换视频内存。
(1)设置视频方式
MOVAH,0
MOVAI,MODE;MODE=显示模式
INTl0H
(2)设置页方式,如下为选择64KB方式
MOVDX,3C4H
MOVAL,OBH
OUTDX,AL
INAL,DX
MOVDX,3CEH
MOVAX,506H
OUTDX,AX
(3)切换视频内存
MOVDX,3C4H
MOVAL,0EH
OUTDX,AL
MOVDX,3C5H
INAL,DX
XORAL,2
OUTDX,AL
4.点(x,y)在屏幕上显示
依据前面的分析,可以得到点(x,y)在屏幕上显示过程:
设置显示模式——计算点的线性地址——计算在VRAM中的段号——计算在RAM中的地址及偏移量——建立CPURAM与点在VRAM段的映射——向CPURAM中地址写点(x,y)的颜色值。
TVGA方式下灰度的实现
1.位图BMP文件格式分析
Paintbrush的.BMP位图共有1位、4位、8位、24位(即单色、1
彩色)4种位映像,结构都基本相同,现仅以256色为例说明。
256色.BMP位图文件格式分为三部分:
文件头、调色板和图形数据
①文件头的结构分析
文件头占用位图的前54个字节
1~2字节文件类型标志
4~7字节文件的长度
8~21字节图形的宽度
22~25字节图形的高度
②调色板的结构分析
从55到1078的1024个字节为调色板值,每4个字节为一种颜色的RGB值,依次分别存放B,G,R(O<二B,G,R<二255)的值,
③图形数据结构分析
从1079起为图形数据,每字节存放屏幕上的一个点的颜色值,有两点与屏幕显示不同。
第一、屏幕上图形的第一行对应于位图存储时的最后一行数据,以此类推:
第二、实际存放的每行数据是按16倍数存储的,当图形列数不是16的倍数时,存储时会自动补充为16的倍数存储。
2.灰度在IVGA卡上的实现
有了前面的TVGA显示原理和位图的结构分析,就能实现位图的显示。
有兴趣的读者可以参照上一讲的相关内容去编程实现,这里就不多讲了。
下面给出一个在TVGA卡上实现灰度显示的程序实例。
#include"stdio.h"
#include"dos.h"
#include
#include
#include
#include
unsignedlongpre_cale_y2[480];
typedefunsignedcharbyte;
unionREGSreg;
structSREGSinreg;
typedefstruct
{
bytered;
bytegrn;
byteblu;
}
rgb;
typedefrgbpalette_Register[256];
voidset_palette(palette_Registerhue)
{
reg.x.ax=0x1012;
segread(&inreg);
inreg.es=inreg.ds;
reg.x.bx=0;
reg.x.cx=256;
reg.x.dx=(int)&hue[0];
int86x(0x10,®,®,&inreg);
}
voidinit_palette_2(palette_Registercolor)
{
inti;
for(i=0;i<36;i++)
{
color[i].red=0;
color[i].grn=0;
color[i].blu=(int)(1.8*i+0.5);
}
for(i=36;i<72;i++)
{
color[i].red=0;
color[i].grn=(int)(1.8*(i-36)+0.5);
color[i].blu=0;
}
for(i=72;i<108;i++)
{
color[i].red=0;
color[i].grn=(int)(1.8*(i-72)+0.5);
color[i].blu=(int)(1.8*(i-72)+0.5);
}
for(i=108;i<144;i++)
{
color[i].red=(int)(1.8*(i-108)+0.5);
color[i].grn=0;
color[i].blu=0;
}
for(i=144;i<180;i++)
{
color[i].red=(int)(1.8*(i-144)+0.5);
color[i].grn=0;
color[i].blu=(int)(1.8*(i-144)+0.5);
}
for(i=180;i<216;i++)
{
color[i].red=(int)(1.8*(i-180)+0.5);
color[i].grn=(int)(1.8*(i-180)+0.5);
color[i].blu=0;
}
for(i=216;i<252;i++)
{
color[i].red=(int)(1.8*(i-216)+0.5);
color[i].grn=(int)(1.8*(i-216)+0.5);
color[i].blu=(int)(1.8*(i-216)+0.5);
}
}
voidprecale()
{
unsignedintj;
for(j=0;j<480;j++)
{
pre_cale_y2[j]=640*j;
}
}
voidplot(intx,inty,charcolor)
{
longL_offset;
intoffset,page;
charfar*address;
precale();
if((x<640)&&(y<480))
{
L_offset=pre_cale_y2[y]+x;
page=(L_offset>>16);
offset=L_offset&65535;
outportb(0x3c4,0xe);
outportb(0x3c5,(page&0xf)^0x2);
address=(charfar*)(0xa0000000L+offset);
*address=color;
}
}
voidput_pix(intx,inty,charcolor,charinten)
{
charcol;
col=((35+1)*(color-1)+inten)&255;
plot(x,y,col);
}
main()
{
inti,j;
palette_Registercolor;
precale();
reg.h.ah=0;
reg.h.al=0x13;
int86(0x10,®,®);
init_palette_2(color);
set_palette(color);
for(i=0;i<300;i++)
for(j=0;j<35;j++)
put_pix(i,j,3,j);
getchar();
closegraph();
}
小结
这一讲首先对VGA卡的结构原理作了介绍,然后分析了BIOS中断调用来设置图形方式的方法,最后给出了一个完整的调整图像灰度的程序,这些知识在图像处理编程中是有很大作用的。
讲座到这里,我们对于程序外壳编写的讨论就要告一段落了,我们的后继课程将讨论一些编程技术,主要包括:
中断调用、底层编程技术、鼠标的使用、程序的内存驻留与释放、软件加密算法等。