首页  编辑  

TSR程序中"保存","恢复"技术的应用

Tags: /计算机文档/DOS应用技巧/   Date Created:

                     TSR程序中"保存","恢复"技术的应用

   TSR编程常常要遇到DOS重入,屏幕保护等许多烦人的事情.但只要采用适当的

 方法,这些事情可以变得很轻松.

   我的经验就是:保存!--尽情破坏--恢复!!

   只要把一切可能被TSR破坏的东西(数据,硬件状态等)保存好,TSR内部就不必

 再顾虑重重了.到TSR退出激活态时,把所有东西一一复原,被中断的程序就可以

 安全地运行下去.

   保存CPU的寄存器就不必说了,因为用interrupt关键字说明的函数会自动保存

 所有寄存器.下面主要给出保存屏幕,鼠标,DOS数据等的例子.

   以下的一些程序片断,仅供分析借鉴,如有不妥或需要补充的,敬请提出. :)

>>-=============================程序实例:BEGIN=============================-<<

/*  程序在Borland C++ 3.1下编译通过,由于涉及行内汇编,其他编译器产

   生的结果可能有所不同,还望注意检查.                             */

/*  由于程序涉及较多未公开的DOS调用和内部数据结构,以及较多VGA寄存

   器操作,对运行环境可能有一定的要求.                             */

#include <dos.h>

#include <bios.h>

#include <stdio.h>

#include <string.h>

/**************************下面是本程序的一些数据定义************************/

const unsigned PSP_ENV=0x2c;  //PSP中保存环境段段址的偏移

const unsigned PSP_DTA=0x80;  //PSP中用于磁盘传输区DTA的内存区首址

const unsigned long VGA_VRAMG=0xa0000000L;  //图形模式显存地址首址

const unsigned long VGA_VRAMT=0xb8000000L;  //文本模式显存地址首址

static char vga_buf1[2048];    //保存VGA状态数据

static char vga_buf2[0x3000];  //保存显存数据2K+2K+8K

stactic chat mouse_buf[1024];  //保存鼠标驱动程序状态数据

static char sda_buf[2048];     //保存DOS的SDA数据

 //以上预先分配了一些缓冲区,其大小是经验值

static void far* sda_addr;     //DOS的SDA数据区首址

static unsigned sda_size;      //DOS的SDA数据区大小

static void far* indos_addr;   //DOS的INDOS标志地址

 //以上是DOS的一些参数,必须由main()在驻留前取得

/**************************上面是本程序的一些数据定义************************/

/************************下面是几个有关VGA操作的辅助函数*********************/

inline void vga_set_mode(unsigned modenum)  //把VGA显示模式设置为modenum

{

_AX=modenum;

_AH=0x0;

geninterrupt(0x10);          //AH=0,AL=modenum,调用BIOS INT 10H

}

inline void vga_rplane_sel(char planenum)  //选中VGA页面planenum读取

{

_AH=planenum;           //AH=planenum将被送到I/O地址0x3cf

_AL=0x4;                //AL=4送0x3ce,选中"读页面选择寄存器"

outport(0x3ce,_AX);     //AL先送0x3ce,AH后送0x3cf

}

inline void vga_wplane_sel(char planenum)  //选中VGA页面planenum写入

{

_AX=0x0102;             //AL=2送0x3c4,选中"彩色页面写允许寄存器"

_AH<<=planenum;         //AH=(0x01<<planenum)将被送到I/O地址0x3c5

outport(0x3c4,_AX);     //AL先送0x3c4,AH后送0x3c5

}

/***********************上面是几个有关VGA操作的辅助函数**********************/

/************************下面是几个保存和恢复屏幕的函数**********************/

/* 由于使用了VESA调用,对Super VGA的模式也可以成功地保存和恢复.

  但相应地,显示卡也必须支持有关VESA调用                       */

void vga_save(char far* buffer1,char far* buffer2)

 //保存VGA状态到buffer1,保存被文本模式03H破坏的显存到buffer2,并切换到模式03H

{

_ES=FP_SEG(buffer1);

_BX=FP_OFF(buffer1);    //ES:BX=buffer1,缓冲区的首址

_AX=0x4f04;             //BIOS INT 10H的4F04号VESA功能调用

_CX=0xffff;             //表示要保存所有的状态数据

_DL=0x1;                //子功能1,保存VGA状态到ES:BX

geninterrupt(0x10);

vga_set_mode(0x92);     //切换到模式12H,0x92的最高位是1表示保留显存数据

vga_rplane_sel(0x2);    //选择页面2读取

_fmemcpy(buffer2,(void far*)VGA_VRAMG,0x2000);

 //页面2的开头8K将被模式03H的字模覆盖,故保存到buffer2

vga_set_mode(0x83);     //切换到模式03H,0x82的最高位是1表示保留显存数据

_fmemcpy(buffer2+0x2000,(void far*)VGA_VRAMT,0x1000);

 //显存头上的4K(页面0的2K是字符,页面1的2K是属性,CPU地址交替)

 //将被模式03H的屏幕数据覆盖,故保存到buffer2+0x2000

}

void vga_restore(char far* buffer1,char far* buffer2)

 //用buffer2的数据恢复显存,用buffer1的数据恢复VGA状态

{

_fmemcpy((void far*)VGA_VRAMT,buffer2+0x2000,0x1000);

 //恢复显存头上的4K(页面0开头的2K,页面1开头的2K)

vga_set_mode(0x92);

vga_wplane_sel(0x2);

_fmemcpy((void far*)VGA_VRAMG,buffer2,0x2000);

 //恢复显存页面2的开头8K

_ES=FP_SEG(buffer1);

_BX=FP_OFF(buffer1);

_AX=0x4f04;

_CX=0xffff;

_DL=0x2;                //子功能2,用ES:BX的数据恢复VGA状态

geninterrupt(0x10);

}

/************************上面是几个保存和恢复屏幕的函数**********************/

/************************下面是几个保存和恢复鼠标的函数**********************/

void mouse_save(char far* buffer)  //保存鼠标驱动程序状态到buffer

{                       //Warning:Inline keyword will cause an error

_ES=FP_SEG(buffer);

_DX=FP_OFF(buffer);

_AX=0x0016;             //子功能0x16,保存保存鼠标驱动程序状态到ES:DX

geninterrupt(0x33);     //鼠标驱动程序INT 33H服务

}

void mouse_restore(char far* buffer) //用buffer数据恢复鼠标驱动程序状态

{

_AX=0x0000;

geninterrupt(0x33);     //先调子功能0x00,RESET鼠标驱动程序

_ES=FP_SEG(buffer);

_DX=FP_OFF(buffer);

_AX=0x0017;             //子功能0x17,用ES:DX数据恢复鼠标驱动程序状态

geninterrupt(0x33);     //鼠标驱动程序INT 33H服务

}

/************************上面是几个保存和恢复鼠标的函数**********************/

/**************************下面是有关设置DOS数据的函数***********************/

inline void set_psp(unsigned newpsp) //把DOS当前进程的PSP强行设置为newpsp

{

_BX=newpsp;                //未公开的DOS调用0x50

_AH=0x50;

geninterrupt(0x21);

}

void set_dta(void far* newdta)       //用newdta作为DOS磁盘传输区(DTA)

{

asm{

 push ds;

 lds dx,newdta;           //DS:DX=newdta

 mov ah,0x1a;             //INT 21H的1A号功能,把DTA指向DS:DX

 int 0x21;

 pop ds;

 }

}

/**************************上面是有关设置DOS数据的函数***********************/

/*****************下面是一个TSR激活时保存和恢复各种数据的实例****************/

void activate_tsr()

{

_fmemcpy(sda_buf,sda_addr,sda_size);    //保存DOS的SDA数据区.

 //SDA就是DOS的"数据段",包含了几乎所有DOS的内部数据,包括三个

 //  内部堆栈,当前进程的PSP,INDOS标志,关键出错标志......

 //所以用保存和恢复SDA的办法就完全不必担心DOS重入了,在tsr_body()

 //  里可以随意进行DOS调用

mouse_save(mouse_buf);            //保存鼠标

vga_save(vga_buf1,vga_buf2);      //保存屏幕

set_psp(_psp);                    //把TSR自己的PSP设为DOS当前进程

set_dta(MK_FP(_psp,PSP_DTA));     //把TSR自己的DTA设为DOS当前DTA

*(char far*)indos_addr=0;  //强制把INDOS标志清0

*(char far*)sda_addr=0;    //强制把DOS关键出错标志(恰在SDA的偏移0处)清0

tsr_body();                //做你想做的 :DD

vga_restore(vga_buf1,vga_buf2);   //恢复屏幕

mouse_restore(mouse_buf);         //恢复鼠标

_fmemcpy(sda_addr,sda_buf,sda_size);  //恢复SDA

}

main()   //仅仅是个简易版,用来说明怎样获取sda_size,sda_addr,indos_addr

{

//这里省略了一些重要事务...

asm push ds;

asm mov ax,0x5d06;  

asm int 0x21;        //INT 21H的功能5D06H,返回SDA的地址送DS:SI,大小送CX

asm pop ds;

sda_size=_CX;        

sda_addr=MK_FP(_BX,_SI);

                    //结果存入sda_addr和sda_size

asm mov ah,0x34;

asm int 0x21;        //INT 21H的功能34H,返回INDOS标志的地址送ES:BX

indos_addr=MK_FP(_ES,_BX);

                    //结果存入indos_addr

_ES=*(unsigned far*)MK_FP(_psp,PSP_ENV);

asm mov ah,0x49;

asm int 0x21;        //释放环境段所占的内存

//这里省略了一些重要事务...

}

/*****************上面是一个TSR激活时保存和恢复各种数据的实例****************/

>>-==============================程序实例:END==============================-<<

                                              Wei Min Luo

                                                  整理于1997.9