wince开机动画及loadcepc的定制 - dthxman的专栏 - CSDNBlog

导读:
  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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值