?? tsr1.txt
字號:
以下是我手中的一些資料,供你參考.祝順利!
TSR程序中"保存","恢復"技術的應用
TSR編程常常要遇到DOS重入,屏幕保護等許多煩人的事情.但只要采用適當的
方法,這些事情可以變得很輕松.
我的經驗就是:保存!--盡情破壞--恢復!!
只要把一切可能被TSR破壞的東西(數據,硬件狀態等)保存好,TSR內部就不必
再顧慮重重了.到TSR退出激活態時,把所有東西一一復原,被中斷的程序就可以
安全地運行下去.
保存CPU的寄存器就不必說了,因為用interrupt關鍵字說明的函數會自動保存
所有寄存器.下面主要給出保存屏幕,鼠標,DOS數據等的例子.
以下的一些程序片斷,僅供分析借鑒,如有不妥或需要補充的,敬請提出. :)
>>-=============================程序實例:BEGIN=============================-<<
/* 程序在Borland C++ 3.1下編譯通過,由于涉及行內匯編,其他編譯器產
生的結果可能有所不同,還望注意檢查. */
/* 由于程序涉及較多未公開的DOS調用和內部數據結構,以及較多VGA寄存
器操作,對運行環境可能有一定的要求. */
#include
#include
#include
#include
/**************************下面是本程序的一些數據定義************************/
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<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==============================-<<
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -