分享
 
 
 

阿贝的例程学校 第1部分:mode 13h

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

[声明]:我对阿贝的例程学校(Abe’s Demoschool)和阿贝的智囊(Abe’s bag of tricks)的翻译得到了原作者的同意。任何人如果想使用阿贝的例程学校(Abe’s Demoschool)和阿贝的智囊(Abe’s bag of tricks)做学习交流之外的事情,请与原作者联系:Albert.Veli@systemdesign.af.se。

Abe's Demoschool Part 1 : mode 13h

阿贝的例程学校 第1部分:mode 13h

欢迎来到例程学校的第一部分内容。在例程学校中,我将带你领略各种各样的图形特效,包括palette rotation(滚动调色板)、plasma、火焰、sprites(精灵)、3D图形、阴影、卷屏、mode-X、soundblaster等等所有可以展示的效果。例程学校中的大部分内容都是基于mode 13h或称MCGA-mode。这就立刻引出了第一个问题:

[什么是mode 13h?]

好吧,答案很简单:mode 13h是PC机的一种图形模式,大部分游戏和图形演示都基于mode 13h完成。mode 13h的分辨率是水平方向320个像素,垂直方向200个像素。这使得屏幕上一共出现320×200=64000个像素。屏幕上的每个像素可以有256种不同的颜色(当然一个像素一次只能具有一种颜色)。你肯定知道,一个byte(8bit)可以取256种不同的值。这意味着mode 13h中的每一个像素都可以用一个byte来表示,而事实也正是如此。

如上所述,mode 13h拥有320×200=64000个像素即64000bytes。Mode 13h的screen-memory从地址A000:0000h开始,到A000:FFFFh结束。这总共有65535bytes,从而使屏幕底部还包含了4个不可见的行。

要往屏幕上写一个像素,须先把段寄存器ES设为A0000h,再计算出该像素的偏移量放入DI,然后向ES:DI写入一个colorbyte(即表示颜色的byte)。偏移量用下式计算:

偏移量 = 320 × y + x

其中x是x坐标值,y是y坐标值。坐标(0,0)位于屏幕的左上角,坐标(319,319)位于屏幕的右下角。下面列出了各个坐标及其相应的偏移量:

(0,0) (1,0) (2,0) (3,0) … … … (319,0)

0 0 1 2 3 … … … 319 ------x轴-----à

1 320 321 322 323 … … … 639 (319,1)

2 640 641 642 643 … … … 959 (319,2)

. . . . . … … … .

. . . . . … … … .

. . . . . … … … .

199 63680 63681 63682 63683 … … … 63999

(0,199) (1,199) (2,199) (3,199) … … … (319,199)

y轴

mode 13h属于一种线性存储模型(linear memory model),即byte一个接一个的顺序排列。

要在坐标(3,2)处用颜色1(缺省值是蓝色)写入一个像素,你应该向A000:(320×2+3)=A000:643处写入值1。试试吧,一定行。

下面是用C写的一个未优化的例子:

void putpixel(int x,int y,char color)

{

int offs;

offs=320*y + x; //使用公式计算偏移量

asm mov ax,0a000h //你不能把一个值直接放到ES中

asm mov es,ax //把屏幕地址放入ES

asm mov di,offs //把计算出来的偏移量放入DI

asm mov al,color //把colorbyte放入AL

asm mov [es:di],al //最后将像素写入屏幕

}

在向屏幕写像素之前,你必须已经处于MCGA-mode 13h模式。可以用下面的方法切换到这个mode 13h:

mov ax,13h ;把模式代号放入AX

int 10h ;使用videointerrupt 10h进入改模式

可以用下面的方法切换回我们常用的textmode 3:

mov ax,3 ;mode 3是DOS的标准80×25 16色文本模式

int 10h ;进入textmode 3

你可以用C编写一个切换屏幕模式的函数:

void setmode(int mode)

{

asm mov ax,mode

asm int 10h

}

这样,你就可以在main程序开头用setmode(0x0013);切换到mode 13h,再用setmode(3);切换回文本模式。

要优化putpixel函数,你可以将很慢的乘法替换为位运算和加法。你肯定也知道,向左移1位等同于乘以2,向左移2位等同于乘以4,等等。所使用的技巧是,将320分为几个以2为底的幂之和。320即(256+64);y×320即(y左移8位)+(y左移6位)。可以参看使用inline-assembler的优化过的putpixel实现之C代码。

一个像素具有什么颜色取决于两个因素:你向屏幕写入的颜色值(0至255)以及该值在调色板中所代表的颜色。调色板共有256个入口(0到255)。调色板中的每一种颜色由3个byte表示,分别代表红、绿、蓝三种颜色。每一个byte值都在0到63之间。值的大小表示了颜色的强度:0表示没有强度,63表示最大强度。这样就有了64的3次方种可能的颜色。

[常见的颜色]

红 绿 蓝 颜色

______________________________

0 0 0 BLACK黑

63 0 0 RED红

20 0 0 DARK RED暗红

0 63 0 GREEN绿

0 0 63 BLUE蓝

63 63 0 YELLOW黄

63 32 0 ORANGE橙

63 0 63 PURPLE紫

0 63 63 CYAN青

63 63 63 WHITE白

32 32 32 GRAY灰

10 10 10 DARKGRAY暗灰

MCGA-mode 13h总是以相同的调色板开始(即0表示黑,1表示蓝,等等)。但要自己改变调色板是很容易的。通过向3c8h和8c9h端口写入适当信息,我们就可以改变调色板。这并没有看上去那么难。你只要向3c8h端口写入你希望改变的起始颜色的索引值,再向3c9h端口写入红、绿、蓝的值就可以了。向3c9h写入了这三个colorbyte后,索引会自动递增。

如果调色板被存放在一个数组pal(3×256bytes长),我们假设0代表黑,1代表蓝,2代表灰,那么pal将会像这样开始:0,0,0;0,0,63;32,32,32;……

下面是一个C函数,它根据数组pal的值来设置调色板:

void setpal(char*pal)

{

int i;

outp(0x3C8,0); //从颜色值0开始

for(i=0;i<256*3;i++)

outp(0x3C9,pal[i]); //将调色板数据送到3c9h端口

}

控制调色板最常用的方法就是在特定的程序里使用数组pal,而把调色板的信息存到磁盘的一个文件中。这个调色板文件很容易辨认,因为它的大小总是3×256=768bytes。在程序中你只需要从此磁盘加载调色板文件并将信息放入数组,再用上面讲到的setpal函数设置调色板。

在汇编语言中,你可以使用一个非常快的指令rep outsb来设置调色板。rep outsb 指令从DS:SI将byte传到DX值指示的端口,传送的byte数目由CX的值指定。每传一个byte,SI就增加1。

下面是设置整个调色板的汇编程序:

mov dx,03c8h ;将端口号放到DX

xor al,al ;将AX清零

out dx,al ;将0传到3c8h端口

lds si,pal ;让DS:SI指向pal

mov cx,3*256 ;将要传送的byte数目存放在CX

inc dx ;DX = 3c9h

rep outsb ;从pal向3c9h端口传送768bytes

一种常见的图形效果就是不断的循环改变数组pal并调用setpal(pal)以形成颜色的循环变化。你可以在其它图形特效里使用这种技术来制造静态plasma效果。首先设置数组pal,使其包含一些好看颜色之间平滑的循环变化;然后使用sin或者cos函数在屏幕上画出优美的图案;最后不断的滚动调色板,使其不停的循环即可。

画出的图案并不必须是plasma picture,你可以画任意图案上去,看看当调色板滚动的时候会产生什么效果。要使数组pal循环滚动,先将第一个颜色(即前三个byte)保存起来,然后将数组中所有的颜色值向前移动一个位置(即3个byte),最后再将尾部空出来的位置设为刚才保存的颜色。

滚动调色板的C代吗如下:

(实际上就是每次将调色板中第一个颜色滚动到最末尾的位置)

void rotpal(char*pal,int first, int last)

{

char r,g,b;

int i;

r=pal[first*3 + 0]; //将第一个颜色的三个值分别赋给r,g,b

g=pal[first*3 + 1];

b=pal[first*3 + 2];

for(i=first*3;i<(last+1)*3;i++) //将所有颜色向前移动一个位置

pal[i]=pal[i+3];

pal[last*3+0]=r; //将最末尾设置为保存的原第一个颜色

pal[last*3+1]=g;

pal[last*3+2]=b;

wtsync(); //等待电子束扫完当前帧

setpal(pal); //根据滚动过的pal设置调色板

}

你并不一定非要将第一个和最末个颜色作为函数参数。但这样做的确有些好处。在例程中,我使用了一个拥有多于256种颜色的输组,每次滚动整个调色板时,都没有改变值为0的颜色,我只设置前256个颜色。我不让值为0的颜色参与滚动是因为,我不希望让背景颜色也改变。你可以让其也参加滚动试一试,这样就能明白我的意思了。

在每次改变调色板之前总应该调用wtsync函数,以避免因为与电子束不同步而产生的所谓“雪花”效果。wtsync等待垂直回溯,即电子束扫完一帧后跳回初始位置以便开始扫下一帧的时刻。

也许你会说本次的例程只能算是半个plasma特效——好吧,在例程学校以后的部分里,我会给你演示真正的plasma特效。

就到此为止,去看看例程的C源代码吧。

[例程在http://www.mds.mdh.se/f%C3%B6reningar/small/abe/abedemo1.zip下载。]

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有