开发之路(1).wince开机动画及loadcepc的定制

    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了,开发的过程就是一个学习的过程,好好享受这个过程吧。
   



   
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Wince嵌入式开发程序入门.doc》是一篇关于Wince嵌入式开发程序入门的指南文档。该文档主要向初学者介绍了Wince嵌入式开发程序的基本概念、开发工具以及开发流程等内容。 首先,文档详细介绍了Wince嵌入式开发程序的基本概念,包括Wince系统的特点、嵌入式开发的目的以及嵌入式系统的组成等内容。这有助于初学者对Wince嵌入式开发程序有一个整体的认识。 其次,文档介绍了Wince嵌入式开发程序的开发工具,包括开发环境搭建、调试工具和软件开发工具等方面。对于初学者来说,了解和熟悉这些开发工具是非常重要的,有助于提高开发效率和开发质量。 文档还详细介绍了Wince嵌入式开发程序的开发流程。从设定开发目标、设计系统架构、编码调试、测试和发布等方面,对开发流程进行了详细的解读。这对于初学者来说非常有价值,可以帮助他们更好地规划和组织开发工作。 此外,文档还提供了一些常用的Wince嵌入式开发技巧和经验分享,如调试技巧、性能优化和错误处理等方面。这对于初学者来说是非常实用的,可以帮助他们更高效地开发和调试嵌入式程序。 总的来说,《Wince嵌入式开发程序入门.doc》是一份很有用的指南文档,它全面介绍了Wince嵌入式开发程序的相关知识和技术,对初学者来说是一个很好的学习资料和参考指南。读者可以通过阅读该文档了解和掌握Wince嵌入式开发程序的基本知识和技能,从而更好地进行嵌入式开发工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值