导读:
wince并不是完全意义上的RTOS(实时嵌入式操作系统),其启动方式主要有两种,bootload和loadcepc+DOS,而bootload里也分为bios+bootload和bootload两种方式,这里不作讨论,本人主要负责的是loadcepc+dos这种方式的启动定制,这里主要介绍如何在用loadcepc启动内核的时候加入公司LOGO动画的过程。
整个过程主要有几个过程,一,在DOS模式下显示图片
二,在显示图片的基础上显示连续的图片,产生动画
三,把产生动画的程序嵌入到loadcepc的读取内核的过程中。
一.首先要实现的是在DOS下显示图片,通常来说,就是打点。由于DOS是16位操作系统,工作在实模式下,所以其图片显示方式不同与普通的windows程序,这里可以参考几本书,《DOS编程大全》,《DOS技术内幕》作者是大名顶顶的一代旗帜性人物求伯君,详细介绍了DOS下16位程序的开发。现在来说明如何显示图片,在DOS下我们可以访问的内存只有1MB空间(这就是DOS的局限性所在),地址从00000H到FFFFFH,这段内存根据用途又分为不同的块,系统分配给图形缓冲区(显示存储器)的地址在A0000H到BFFFFH之间,大小为128KB,其中,VGA占用了A0000H到AFFFFH段,共64KB,这段地址是内存映射地址,供我们访问显示存储器用。在VGA 13H图形模式下,显示内存使用A0000H到AF9FFH的一段线性内存空间,每个字节表示一个点,对应屏幕上的一个像点,320*200的屏幕分辨率共需要64000个字节,刚好64KB,因为一个字节可以表示的最大整数值为256,所以每个像点就可以表示256种颜色,下面我们开始图片的具体显示过程。
1.我们先设置视频模式,画图以前必须使屏幕工作在图形方式,这就要设置屏幕的视频模式。设置视频模式有许多种方法,其中调用视频BIOS功能是最简单的一种,通过调用BIOS中断0x10的服务程序,可以很方便地设置屏幕模式。调用方法是将值0放入ah寄存器,显示模式放入al寄存器中,然后调用int86()函数。
设置视频模式的函数:
void SetVideoMode(int mode)
{
union REGS r;
r.h.ah=0;
r.h.al=mode;
int86(0x10,&r,&r);
}
其中mode是视频模式。
2.在屏幕上画点,由于显示存储器是线性排列的,每个像点用一个字节来表示,所以对像点寻址非常容易,像点在显示内存中的偏移地址可由这个公式确定:y*320+x,其中,y是像点在垂直方向的坐标,x是水平方向的坐标,320是屏幕的宽度。有了像点的偏移地址,然后加上显示内存的首地址即可得到像点在显示内存中的绝对地址。只要将表示点的颜色值放到这个地址处,就可以在屏幕上画点了。首先建立一个指针VideoBufferPtr,使它指向显示内存的首地址:
char far *VideoBufferPtr=( char far *)0xa0000000;
将这个指针加上像点的偏移地址,像点的最终地址就确定了,它等于:
VideoBufferPtr+y*320+x;
把颜色值color写到这个地址:
*(VideoBufferPtr+y*320+x)=color;
画点的函数:
void DrawPoint(int x,int y,unsigned char color)
{
*(VideoBufferPtr+y*320+x)=color;
}
3.设置颜色寄存器,我们知道VGA显示卡具有显示256种颜色的能力,每种颜色能够用一个0-255之间的数值来表示,那么这些数值与我们在屏幕上实际见到的颜色之间有什么关系呢?其实这些数值只是VGA显示卡上的颜色寄存器的索引值,颜色寄存器里才保存了屏幕上颜色的真实值。VGA显示卡上有一个包含256个单元的颜色寄存器(又称为调色板),每个单元由三部份组成,这三部份分别代表颜色中的红、绿、蓝三种成份(显示器就是用这三种成份来组成任何我们所看到的颜色),用三个字节表示,颜色寄存器一共有768个字节(3*256=768)。当我们要在屏幕上显示某种颜色时,显示卡硬件就根据颜色的索引值在颜色寄存器中查找,找到后再从相应单元中取出颜色值显示在屏幕上,这个过程与画家使用调色板相似,颜色寄存器相当于调色板,颜色寄存器中的单元相当于调色板上的色格,在色格中装有预先调好的颜色,当画家需要用某种颜色作画时,就从装有那种颜色的色格中把颜色取出来。例如,我们要显示颜色索引值为30的颜色,显示卡硬件就去查找颜色寄存器的第30单元,30单元位于距颜色寄存器首址3*30=90处(因为每个单元有三个字节),然后取出90处记录有红、绿、蓝三种成份的三个字节作为在屏幕上显示的色彩信号。但是实际上每个字节只用了六位来表示颜色,其它两位没用,这六位表示的数的值域为0-63,所以每种颜色(红、绿、蓝)成份具有64种亮度的表现能力,三种颜色成份组合共可以产生64*64*64=262,144种颜色(VGA 13H模式从这262,144种颜色中取出256种在同一屏幕上显示)。我们可以通过事先设置颜色寄存器的值来使用我们自己的颜色。
设置颜色寄存器有多种方法,如调用BIOS功能,但是这种方法速度比较慢,游戏设计中通常采用直接访问VGA显示卡的I/O端口的方法来快速设置颜色寄存器,我们只需访问四个I/O端口就可以完成设置颜色寄存器的工作。这四个端口分别是: 0x3c6、0x3c7、0x3c8和0x3c9。
端口0x3c6称为调色板屏蔽寄存器,用来屏蔽所要求的调色板寄存器的位,如果你在这个寄存器中放入0xff,你就可以通过调色板索引寄存器0x3c7和0x3c8(一个用于读,一个用于写)访问任何你希望访问的颜色寄存器,端口0x3c9称为调色板数据寄存器,红、绿、蓝三种成份就是通过它进行读写(颜色值要读或写三次)。
我们定义一个结构来方便处理颜色寄存器:
typedef struct RGB_COLOR
{
unsigned char red;
unsigned char green;
unsigned char blue;
}RGBColor,*RGBColorPtr;
结构中的red、green和blue变量用来保存颜色的红、绿、蓝三种成份。
设置颜色寄存器值的函数:
void SetPaletteRegister(int index,RGBColorPtr color)
{
outportb(0x3c6,0xff);
outportb(0x3c8,index);
outportb(0x3c9,color->red);
outportb(0x3c9,color->green);
outportb(0x3c9,color->blue);
}
获取颜色寄存器值的函数:
void GetPaletteRegister(int index,RGBColorPtr color)
{
outportb(0x3c6,0xff);
outportb(0x3c7,index);
color->red=inportb(0x3c9);
color->green=inportb(0x3c9);
color->blue=inportb(0x3c9);
}
4.在屏幕上画位图,计算机绘制图像通常采用一种称为位映射图(BITMAP)的图形处理方法进行,位映射图是一个矩形的点阵结构(二维矩阵),显示在屏幕上时,对应屏幕上一个矩形区域,组成位图的数据储存在内存中一段连续的区间。我们比较常见的位图文件有:BMP、PCX、GIF、JPG等。位图通常存储在外部文件中,使用以前必须将其从磁盘文件调入内存。我在项目中使用的是BMP图,由于BMP图体积较大,可以使用PCX等压缩图片,当然需要解压,这步需要在程序里完成,PCX是DOS下比较通用的压缩方式,这里主要介绍BMP位图:
if((fp=fopen(buff,"rb"))==NULL) //读取位图
{
printf("Can't open file: %s",buff);
return;
}
seek(fp,28,SEEK_SET);
fread(&d,2,1,fp);
if(d!=8) /*检查是否为256色位图*/
{
puts("Not a 256 color bitmap!");
fclose(fp);
return;
}
fseek(fp,18,SEEK_SET);
fread(&width,4,1,fp); //读取图片宽度
fseek(fp,22,SEEK_SET);
fread(&length,4,1,fp); //读取图片长度
fseek(fp,54,SEEK_SET);
for(c=0;c<256;c++) /*按照该图片的DAC色表设置色彩寄存器*/
{
b=fgetc(fp);
g=fgetc(fp);
r=fgetc(fp); /*获取R、G、B分量*/
_outp(0x3c8,c);
_outp(0x3c9,r>>2); /*右移是要转化为VGA的6位寄存器形式*/
_outp(0x3c9,g>>2);
_outp(0x3c9,b>>2);
fgetc(fp);
}
fseek(fp,10*sizeof(char),SEEK_SET);
fread(&offset,4,1,fp);
fseek(fp,offset*sizeof(char),SEEK_SET); //这里把指针移动到存放图像数据的位置
fread(buffer,320,1,fp); //开始读取数据
有一点要注意的是,位图存放数据的格式是用图片的最后一行开始,所以要注意数据的显示位置。
通常,为了显示流畅,必须考虑到速度问题,这就要把图片数据放到内存中,可以在DOS下开辟一块内存,专门存放数据。
buffer=malloc(320*200sizeof(char));
然后直接对内存进行操作。
5.换页函数
有一点你一定发现了,一个屏幕肯定不止64K个象素,那么一个800×600的屏幕怎么实现每个点的控制呢,在DOS中,有一个段值的概念,即一个屏幕被分成了N段,每一段对应一个64K的区域,这样我们只要找到相应的段,然后在段中找到位置就可以了。
void selectpage(register char page) /*换页函数*/
{
union REGS r;
r.x.ax=0x4f05;
r.x.bx=0;
r.x.dx=page; /*选择页面*/
int86(0x10,&r,&r);
}
这样图片的显示基本完成。
这里对显示部分代码进行优化,如:
unsigned char far *video_buffer = (char far*)0xA0000000L
unsigned int far *video_buffer_w = (unsigned int far*)video_buffer
原来我们是通过一个字,一个字来拷贝数据,优化后我们使用一个字节,一个字节来拷贝数据,速度可以提高一倍。需要指出一点的是,以上的开发环境我用的是BC3.1,强大的16位编译工具,如果你需要在windows上开发,你还要一个测试环境,就需要安装一个DOS模拟器,我用的是DOSBOX。
二.显示动画
图片显示好了,注意,如果只需要显示公司LOGO的单张图片,就可以直接进入定制LOADCEPC的部分了,这里主要是讲述如何显示连续的图片,产生动画的过程。
其实过程很简单,就是通过类似的方法,把20张图片(比方说要连续显示20张)全部载入内存,然后按顺序在屏幕上打点,不是就可以了吗?可是,如果是windows,就好办了,前面说了,DOS的局限性在于应用程序不能大于1M,一般用户在程序中使用的内存不能大于640k,而我们一张图片要malloc的内存是64k,这样最多开辟10张图片的内存,而实际使用时最多载入8张图片,那怎么办呢,早期的程序员也遇到过同样的问题,解决的方法有很多,(1).使用扩展内存。(2).使用扩充内存。(3)进入32位的保护模式进行内存调用,然后返回16位的实模式。我使用的是扩展内存(有些书中叫扩充内存)XMS,主要比较容易实现,而且细心的人在看PB中的loadcepc的源码的时候,发现在载入nk.bin的时候用到的也是XMS,连微软都用了,没有理由不用吧,那么下面介绍怎么用。
1.首先,你的DOS下必须要有设备驱动程序HIMEM.SYS,这样才能调用XMS,查询系统是否有HIMEM.SYS的代码:
unsigned char himem_inf() //取得HIMEM.SYS的安装状态
{
__asm
{
mov ax,4300h
int 2fh
xor ah,ah
cmp al,80h
jne done
}
return 1;
done: return 0; //若AL=80H: HIMEM.SYS 已加载!
} //若AL=00H: HIMEM.SYS 未加载!
2.取得HIMEM.SYS功能调用的入口地址
int (far * XMSControll)() = 0;
void get_himem_entry()
{
__asm
{
mov ax,4310h
int 2fh
mov WORD PTR [XMSControll], bx ;Save entry point
mov WORD PTR [XMSControll+2], es
mov ah, 05h
call [XMSControll]
clc
rcr al, 1
}
}
3.请求分配扩展存储块
int get_emb(int size,int *emb_handler)
{
int a=size,b; //emb_handler为句柄
__asm
{
mov dx,a //size为块长度(单位:KB)
mov ah, 9h
call [XMSControll]
push si
mov si, emb_handler
mov WORD PTR [si], dx
pop si
mov b,ax
}
return b;
}
4.请求分配扩展存储块,大小为size(KB)
int allocemb(int size)
{
if(!himem_inf()) //如果HIMEM.SYS没有安装,您可使用全部的XMS!
{
printf("Himem.sys isn't load!/n");
return 1;
}
else
{
get_himem_entry(); //取得HIMEM.SYS的入口地址!
if(!get_emb(size,&emb_handler)) //请求分配扩展存储块emb_handler
{
printf("Cann't get %d KB in EMB !/n",size); return 1;
}
}
return 0;
}
5.释放句柄为emb_handler存储块
int free_emb(int emb_handler)
{
__asm
{
mov dx, emb_handler
mov ah, 0ah
call [XMSControll]
}
return 1;
}
6.这样,就可以开始调用扩展内存了,下面是一个sample
struct move
{
unsigned long move_l;
int s_handler;
unsigned long s_offset;
int d_handler;
unsigned long d_offset;
} hm;
unsigned int mem_himem()
{
int state;
__asm
{
cli
mov ah, 0bh
push si
lea si, hm
//lds si,dword ptr fptr
call [XMSControll]
//__asm call dword ptr h_entry
pop si
mov state, ax
sti
}
return state;
}
for(p = 0;p <20;p ++)
{
for(i = 0;i <200;i ++)
{
fread(buffer,320,1,fp); //将读取的数据放入扩展内存
hm.move_l=320l;
hm.d_handler=emb_handler;
hm.s_handler=0;
hm.s_offset=(unsigned long)(char far*)buffer;
hm.d_offset=(unsigned long)p*200*320+i*320;
if(!mem_himem())
{
printf("MOVE HIMEM NO!/n");
}
}
}
for(p = 0;p <20;p ++)
{
for(i = 0;i <200;i ++)
{
fread(buffer,320,1,fp); //将数据从扩展内存中取出来
hm.move_l=320l;
hm.d_handler=0;
hm.s_handler=emb_handler;
hm.d_offset=(unsigned long)(char far*)buffer;
hm.s_offset=(unsigned long)p*200*320+i*320;
if(!mem_himem())
{
printf("MOVE HIMEM NO!/n");
}
}
}
这样,显示动画的技术实现了,只要把代码嵌入loadcepc中就可以实现我们的功能了。
三.修改loadcepc源码
PB中有Loadcepc的源码包,但有一点要注意,由于是16位程序,你要用VC1.5才能成功编译,打开源码包,大致看下所有的文件,然后找到main.c,这是主程序文件,在头上加入#include "sbmp.c",sbmp.c就是我们写的显示动画的函数的文件,然后找到载入内核的那部分,注意,loadcepc载入内核也使用同我们类似的XMS的方法,我们只需在内核载入的循环中,按进度显示图片即可,找到DownloadRead()函数,这个是系统载入内核的函数,后面加入我们的显示图片函数,当然,具体怎么显示,就需要具体的需求来控制了,我们也可以把载入内核时那个进度条换成我们自己画的进度条,找到函数DrawPercent(),这个函数在PPFSTOOL.C中实现,仔细看源码,你会发现方法和图片如出一辙,只需控制好在屏幕上显示的位置即可,这样,整个定制基本完成。
在DOS中做一个批处理,这样每次开机,就会自动运行定制出来的LOADCEPC了,开发的过程就是一个学习的过程,好好享受这个过程吧。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1934553
本文转自
http://blog.csdn.net/dthxman/archive/2007/12/14/1934553.aspx
wince并不是完全意义上的RTOS(实时嵌入式操作系统),其启动方式主要有两种,bootload和loadcepc+DOS,而bootload里也分为bios+bootload和bootload两种方式,这里不作讨论,本人主要负责的是loadcepc+dos这种方式的启动定制,这里主要介绍如何在用loadcepc启动内核的时候加入公司LOGO动画的过程。
整个过程主要有几个过程,一,在DOS模式下显示图片
二,在显示图片的基础上显示连续的图片,产生动画
三,把产生动画的程序嵌入到loadcepc的读取内核的过程中。
一.首先要实现的是在DOS下显示图片,通常来说,就是打点。由于DOS是16位操作系统,工作在实模式下,所以其图片显示方式不同与普通的windows程序,这里可以参考几本书,《DOS编程大全》,《DOS技术内幕》作者是大名顶顶的一代旗帜性人物求伯君,详细介绍了DOS下16位程序的开发。现在来说明如何显示图片,在DOS下我们可以访问的内存只有1MB空间(这就是DOS的局限性所在),地址从00000H到FFFFFH,这段内存根据用途又分为不同的块,系统分配给图形缓冲区(显示存储器)的地址在A0000H到BFFFFH之间,大小为128KB,其中,VGA占用了A0000H到AFFFFH段,共64KB,这段地址是内存映射地址,供我们访问显示存储器用。在VGA 13H图形模式下,显示内存使用A0000H到AF9FFH的一段线性内存空间,每个字节表示一个点,对应屏幕上的一个像点,320*200的屏幕分辨率共需要64000个字节,刚好64KB,因为一个字节可以表示的最大整数值为256,所以每个像点就可以表示256种颜色,下面我们开始图片的具体显示过程。
1.我们先设置视频模式,画图以前必须使屏幕工作在图形方式,这就要设置屏幕的视频模式。设置视频模式有许多种方法,其中调用视频BIOS功能是最简单的一种,通过调用BIOS中断0x10的服务程序,可以很方便地设置屏幕模式。调用方法是将值0放入ah寄存器,显示模式放入al寄存器中,然后调用int86()函数。
设置视频模式的函数:
void SetVideoMode(int mode)
{
union REGS r;
r.h.ah=0;
r.h.al=mode;
int86(0x10,&r,&r);
}
其中mode是视频模式。
2.在屏幕上画点,由于显示存储器是线性排列的,每个像点用一个字节来表示,所以对像点寻址非常容易,像点在显示内存中的偏移地址可由这个公式确定:y*320+x,其中,y是像点在垂直方向的坐标,x是水平方向的坐标,320是屏幕的宽度。有了像点的偏移地址,然后加上显示内存的首地址即可得到像点在显示内存中的绝对地址。只要将表示点的颜色值放到这个地址处,就可以在屏幕上画点了。首先建立一个指针VideoBufferPtr,使它指向显示内存的首地址:
char far *VideoBufferPtr=( char far *)0xa0000000;
将这个指针加上像点的偏移地址,像点的最终地址就确定了,它等于:
VideoBufferPtr+y*320+x;
把颜色值color写到这个地址:
*(VideoBufferPtr+y*320+x)=color;
画点的函数:
void DrawPoint(int x,int y,unsigned char color)
{
*(VideoBufferPtr+y*320+x)=color;
}
3.设置颜色寄存器,我们知道VGA显示卡具有显示256种颜色的能力,每种颜色能够用一个0-255之间的数值来表示,那么这些数值与我们在屏幕上实际见到的颜色之间有什么关系呢?其实这些数值只是VGA显示卡上的颜色寄存器的索引值,颜色寄存器里才保存了屏幕上颜色的真实值。VGA显示卡上有一个包含256个单元的颜色寄存器(又称为调色板),每个单元由三部份组成,这三部份分别代表颜色中的红、绿、蓝三种成份(显示器就是用这三种成份来组成任何我们所看到的颜色),用三个字节表示,颜色寄存器一共有768个字节(3*256=768)。当我们要在屏幕上显示某种颜色时,显示卡硬件就根据颜色的索引值在颜色寄存器中查找,找到后再从相应单元中取出颜色值显示在屏幕上,这个过程与画家使用调色板相似,颜色寄存器相当于调色板,颜色寄存器中的单元相当于调色板上的色格,在色格中装有预先调好的颜色,当画家需要用某种颜色作画时,就从装有那种颜色的色格中把颜色取出来。例如,我们要显示颜色索引值为30的颜色,显示卡硬件就去查找颜色寄存器的第30单元,30单元位于距颜色寄存器首址3*30=90处(因为每个单元有三个字节),然后取出90处记录有红、绿、蓝三种成份的三个字节作为在屏幕上显示的色彩信号。但是实际上每个字节只用了六位来表示颜色,其它两位没用,这六位表示的数的值域为0-63,所以每种颜色(红、绿、蓝)成份具有64种亮度的表现能力,三种颜色成份组合共可以产生64*64*64=262,144种颜色(VGA 13H模式从这262,144种颜色中取出256种在同一屏幕上显示)。我们可以通过事先设置颜色寄存器的值来使用我们自己的颜色。
设置颜色寄存器有多种方法,如调用BIOS功能,但是这种方法速度比较慢,游戏设计中通常采用直接访问VGA显示卡的I/O端口的方法来快速设置颜色寄存器,我们只需访问四个I/O端口就可以完成设置颜色寄存器的工作。这四个端口分别是: 0x3c6、0x3c7、0x3c8和0x3c9。
端口0x3c6称为调色板屏蔽寄存器,用来屏蔽所要求的调色板寄存器的位,如果你在这个寄存器中放入0xff,你就可以通过调色板索引寄存器0x3c7和0x3c8(一个用于读,一个用于写)访问任何你希望访问的颜色寄存器,端口0x3c9称为调色板数据寄存器,红、绿、蓝三种成份就是通过它进行读写(颜色值要读或写三次)。
我们定义一个结构来方便处理颜色寄存器:
typedef struct RGB_COLOR
{
unsigned char red;
unsigned char green;
unsigned char blue;
}RGBColor,*RGBColorPtr;
结构中的red、green和blue变量用来保存颜色的红、绿、蓝三种成份。
设置颜色寄存器值的函数:
void SetPaletteRegister(int index,RGBColorPtr color)
{
outportb(0x3c6,0xff);
outportb(0x3c8,index);
outportb(0x3c9,color->red);
outportb(0x3c9,color->green);
outportb(0x3c9,color->blue);
}
获取颜色寄存器值的函数:
void GetPaletteRegister(int index,RGBColorPtr color)
{
outportb(0x3c6,0xff);
outportb(0x3c7,index);
color->red=inportb(0x3c9);
color->green=inportb(0x3c9);
color->blue=inportb(0x3c9);
}
4.在屏幕上画位图,计算机绘制图像通常采用一种称为位映射图(BITMAP)的图形处理方法进行,位映射图是一个矩形的点阵结构(二维矩阵),显示在屏幕上时,对应屏幕上一个矩形区域,组成位图的数据储存在内存中一段连续的区间。我们比较常见的位图文件有:BMP、PCX、GIF、JPG等。位图通常存储在外部文件中,使用以前必须将其从磁盘文件调入内存。我在项目中使用的是BMP图,由于BMP图体积较大,可以使用PCX等压缩图片,当然需要解压,这步需要在程序里完成,PCX是DOS下比较通用的压缩方式,这里主要介绍BMP位图:
if((fp=fopen(buff,"rb"))==NULL) //读取位图
{
printf("Can't open file: %s",buff);
return;
}
seek(fp,28,SEEK_SET);
fread(&d,2,1,fp);
if(d!=8) /*检查是否为256色位图*/
{
puts("Not a 256 color bitmap!");
fclose(fp);
return;
}
fseek(fp,18,SEEK_SET);
fread(&width,4,1,fp); //读取图片宽度
fseek(fp,22,SEEK_SET);
fread(&length,4,1,fp); //读取图片长度
fseek(fp,54,SEEK_SET);
for(c=0;c<256;c++) /*按照该图片的DAC色表设置色彩寄存器*/
{
b=fgetc(fp);
g=fgetc(fp);
r=fgetc(fp); /*获取R、G、B分量*/
_outp(0x3c8,c);
_outp(0x3c9,r>>2); /*右移是要转化为VGA的6位寄存器形式*/
_outp(0x3c9,g>>2);
_outp(0x3c9,b>>2);
fgetc(fp);
}
fseek(fp,10*sizeof(char),SEEK_SET);
fread(&offset,4,1,fp);
fseek(fp,offset*sizeof(char),SEEK_SET); //这里把指针移动到存放图像数据的位置
fread(buffer,320,1,fp); //开始读取数据
有一点要注意的是,位图存放数据的格式是用图片的最后一行开始,所以要注意数据的显示位置。
通常,为了显示流畅,必须考虑到速度问题,这就要把图片数据放到内存中,可以在DOS下开辟一块内存,专门存放数据。
buffer=malloc(320*200sizeof(char));
然后直接对内存进行操作。
5.换页函数
有一点你一定发现了,一个屏幕肯定不止64K个象素,那么一个800×600的屏幕怎么实现每个点的控制呢,在DOS中,有一个段值的概念,即一个屏幕被分成了N段,每一段对应一个64K的区域,这样我们只要找到相应的段,然后在段中找到位置就可以了。
void selectpage(register char page) /*换页函数*/
{
union REGS r;
r.x.ax=0x4f05;
r.x.bx=0;
r.x.dx=page; /*选择页面*/
int86(0x10,&r,&r);
}
这样图片的显示基本完成。
这里对显示部分代码进行优化,如:
unsigned char far *video_buffer = (char far*)0xA0000000L
unsigned int far *video_buffer_w = (unsigned int far*)video_buffer
原来我们是通过一个字,一个字来拷贝数据,优化后我们使用一个字节,一个字节来拷贝数据,速度可以提高一倍。需要指出一点的是,以上的开发环境我用的是BC3.1,强大的16位编译工具,如果你需要在windows上开发,你还要一个测试环境,就需要安装一个DOS模拟器,我用的是DOSBOX。
二.显示动画
图片显示好了,注意,如果只需要显示公司LOGO的单张图片,就可以直接进入定制LOADCEPC的部分了,这里主要是讲述如何显示连续的图片,产生动画的过程。
其实过程很简单,就是通过类似的方法,把20张图片(比方说要连续显示20张)全部载入内存,然后按顺序在屏幕上打点,不是就可以了吗?可是,如果是windows,就好办了,前面说了,DOS的局限性在于应用程序不能大于1M,一般用户在程序中使用的内存不能大于640k,而我们一张图片要malloc的内存是64k,这样最多开辟10张图片的内存,而实际使用时最多载入8张图片,那怎么办呢,早期的程序员也遇到过同样的问题,解决的方法有很多,(1).使用扩展内存。(2).使用扩充内存。(3)进入32位的保护模式进行内存调用,然后返回16位的实模式。我使用的是扩展内存(有些书中叫扩充内存)XMS,主要比较容易实现,而且细心的人在看PB中的loadcepc的源码的时候,发现在载入nk.bin的时候用到的也是XMS,连微软都用了,没有理由不用吧,那么下面介绍怎么用。
1.首先,你的DOS下必须要有设备驱动程序HIMEM.SYS,这样才能调用XMS,查询系统是否有HIMEM.SYS的代码:
unsigned char himem_inf() //取得HIMEM.SYS的安装状态
{
__asm
{
mov ax,4300h
int 2fh
xor ah,ah
cmp al,80h
jne done
}
return 1;
done: return 0; //若AL=80H: HIMEM.SYS 已加载!
} //若AL=00H: HIMEM.SYS 未加载!
2.取得HIMEM.SYS功能调用的入口地址
int (far * XMSControll)() = 0;
void get_himem_entry()
{
__asm
{
mov ax,4310h
int 2fh
mov WORD PTR [XMSControll], bx ;Save entry point
mov WORD PTR [XMSControll+2], es
mov ah, 05h
call [XMSControll]
clc
rcr al, 1
}
}
3.请求分配扩展存储块
int get_emb(int size,int *emb_handler)
{
int a=size,b; //emb_handler为句柄
__asm
{
mov dx,a //size为块长度(单位:KB)
mov ah, 9h
call [XMSControll]
push si
mov si, emb_handler
mov WORD PTR [si], dx
pop si
mov b,ax
}
return b;
}
4.请求分配扩展存储块,大小为size(KB)
int allocemb(int size)
{
if(!himem_inf()) //如果HIMEM.SYS没有安装,您可使用全部的XMS!
{
printf("Himem.sys isn't load!/n");
return 1;
}
else
{
get_himem_entry(); //取得HIMEM.SYS的入口地址!
if(!get_emb(size,&emb_handler)) //请求分配扩展存储块emb_handler
{
printf("Cann't get %d KB in EMB !/n",size); return 1;
}
}
return 0;
}
5.释放句柄为emb_handler存储块
int free_emb(int emb_handler)
{
__asm
{
mov dx, emb_handler
mov ah, 0ah
call [XMSControll]
}
return 1;
}
6.这样,就可以开始调用扩展内存了,下面是一个sample
struct move
{
unsigned long move_l;
int s_handler;
unsigned long s_offset;
int d_handler;
unsigned long d_offset;
} hm;
unsigned int mem_himem()
{
int state;
__asm
{
cli
mov ah, 0bh
push si
lea si, hm
//lds si,dword ptr fptr
call [XMSControll]
//__asm call dword ptr h_entry
pop si
mov state, ax
sti
}
return state;
}
for(p = 0;p <20;p ++)
{
for(i = 0;i <200;i ++)
{
fread(buffer,320,1,fp); //将读取的数据放入扩展内存
hm.move_l=320l;
hm.d_handler=emb_handler;
hm.s_handler=0;
hm.s_offset=(unsigned long)(char far*)buffer;
hm.d_offset=(unsigned long)p*200*320+i*320;
if(!mem_himem())
{
printf("MOVE HIMEM NO!/n");
}
}
}
for(p = 0;p <20;p ++)
{
for(i = 0;i <200;i ++)
{
fread(buffer,320,1,fp); //将数据从扩展内存中取出来
hm.move_l=320l;
hm.d_handler=0;
hm.s_handler=emb_handler;
hm.d_offset=(unsigned long)(char far*)buffer;
hm.s_offset=(unsigned long)p*200*320+i*320;
if(!mem_himem())
{
printf("MOVE HIMEM NO!/n");
}
}
}
这样,显示动画的技术实现了,只要把代码嵌入loadcepc中就可以实现我们的功能了。
三.修改loadcepc源码
PB中有Loadcepc的源码包,但有一点要注意,由于是16位程序,你要用VC1.5才能成功编译,打开源码包,大致看下所有的文件,然后找到main.c,这是主程序文件,在头上加入#include "sbmp.c",sbmp.c就是我们写的显示动画的函数的文件,然后找到载入内核的那部分,注意,loadcepc载入内核也使用同我们类似的XMS的方法,我们只需在内核载入的循环中,按进度显示图片即可,找到DownloadRead()函数,这个是系统载入内核的函数,后面加入我们的显示图片函数,当然,具体怎么显示,就需要具体的需求来控制了,我们也可以把载入内核时那个进度条换成我们自己画的进度条,找到函数DrawPercent(),这个函数在PPFSTOOL.C中实现,仔细看源码,你会发现方法和图片如出一辙,只需控制好在屏幕上显示的位置即可,这样,整个定制基本完成。
在DOS中做一个批处理,这样每次开机,就会自动运行定制出来的LOADCEPC了,开发的过程就是一个学习的过程,好好享受这个过程吧。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1934553
本文转自
http://blog.csdn.net/dthxman/archive/2007/12/14/1934553.aspx