265頁 講,Borland C/C++顯示出此程序可以使用longjmp(長跳轉)和其它一些功能來直接與 高層的程序發生交互作用。 列表11.2 /* handler.c Listing 11.2 of DOS Programmer's Reference*/ #include<stdlib.h> #include<stdio.h> #include<conio.h> #include<dos.h> #define CR 0x0D #define LF 0x0A int handler(void); void main() { int c; int i; if(getcbrk()==0) printf("BREAK checking is 0FF\n"); else printf("BREAK checking is ON\n"); ctrlbrk(handler); for(i=0; i<250; i++){ printf("%4.4d : Testing Ctrl-Break\n",i); } printf("\nCharacter input\n"); printf("press any key; test Ctrl-Break\n"); printf("if all else fails, press Esc to exit)\n\n"); while((c=getche())!=27) if(C==CR||c==LF) putchar(LF); } handler() { int c; printf("\nCtrl-Break handler\n"); printf("Do you want to quit?"); while((c=getch())!='y' && c!'Y'&& c!'n' && c!='N'); printf("\n"); return ((C=='Y'||c=='y':)?0:1)); } Handler.c 做了兩件事來允許你測試Ctrl-C處理: 1.在屏幕上,它顯示出可以用Ctrl-C或Ctrl-Break中斷的250行重複內容。 2.它接收鍵盤敲入的字元信息,這樣,在鍵盤輸入期間,可以使用ctrl-C或ctrl- Break。 雖然彙編語言函數簡單得多,但直接用彙編語言編碼的函數使用起來幾乎能使ctrl- brk()函數以假亂真。之所以必須使用彙編語言,是因為必須通過中斷返回(JRET)而不是 普通函數返回來完成處理程序。雖然可在此處理程序中來用直接插入碼(嵌入式彙編),只 是其效果沒有採用彙編語言那麼好。
266頁 優秀的彙編語言編程人員能直接編寫出中斷處理程序,但是大多數人可能不知道怎 樣去拼湊出中斷處理程序。要創建這種彙編語言程序,可從一道空的C程序開始,並把它 編譯成彙編語言源代碼: set_brk() { } brk() { handler(); } 通過Borland C/C++,我們就可以這樣來編譯此代碼: C>bcc.s set_brk.c 獲取彙編語言源碼的過程列於列表11.3。 列表11.3 ifndef ??version ?debug macro endm publicdll macro name public name endm $ comm macro name ,dist, size,conunt comm dist name:BYTE:count*size endm else $comm macro name,dist,size , count comm dist name :BYTE : count endm endif ?debug V 300h ?debug S "set_brk.c" ?debug C E9EC837A1A097365745F62726B2E63 TEXT segment byte public'CODE' _TEXT ends DGROUP group _DATA,_BSS assume cs:_TEXT,ds :DGROUP DATA segment word public 'DATA' d@ label byte d@w label word _DATA ends _BSS segment word public 'BSS' b@ label byte b@w label word _BSS ends _TEXT segment byte public 'CODE' ; ; set_brk() ; assume CS:_TEXT _set_brk proc near push bp nov bp,sp;
267頁 ; { ; } ; pop bp ret _set_brk endp , ; brk() ; assume cs :_TEXT _brk proc near push bp mov bp,sp ; ; { ; handler(); ; call nearptr_handler ; ; } ; pop bp ret _brk endp ?debug C E9 ?debug C FA00000000 _TEXT ends DATA segment word public 'DATA' s@ label byte _DATA ends TEXT segment byte public 'CODE' _TEXT ends extrn _handler:near public _brk public _set_prk _s@ equ s@ end這個空的C程序(有時叫做NULL程序)產生了一種空常式,用它可建立中斷處理程 序。我們可能需要跋涉過C編譯程序所插入的不重要信息(如調試信息),但是,如果對匯 編語言不熟悉,則這類信息會節約一點時間。一些彙編語言「高手」也許會嘲笑這種方式, 但專業化的程序員已多年利用這種方法來了解編譯程序產生代碼的方式,或者提供一種 方式,用彙編語言重新編碼某個功能,以便節省處理時間。 建立彙編語言常式 一本正經的彙編語言程序員可能害怕這種想法,但確實可以這樣準備一個 彙編語言常式:首先用高級語言如C編寫一個常式,然後用可以產生彙編語言 源代碼的選項來編譯該常式。列表11.3的實例中,開發時間已顯著減少,因為該 常式的骨架是從編譯程序產生的。 如果必須產生彙編語言程序或者如果想優化用高級語言編寫的程序,那麼 這項技術是不錯的。把程序轉換成彙編碼,並編輯所形成的文件,這可以在最小 量的時間內提供一個工作程序。 268頁 在Turbo Pascal 4.0及更高版本中,可以聲明帶有interrupt指令的程序為中斷處理程 序。編譯程序自動操縱寄存器和IRET指令。該程序可保存所有寄存器;如果只需要保存 幾個,那麼自己直接寫的彙編代碼可能更有效。 列表11.4 _TEXT segment byte public 'code' _TEXT ends DGROUP group _DATA,_BSS assume CS:_TEXT , ds : DGROUP DATA segment wordpublic'DATA' d@ label byte d@w label word _DATA ends _BSS segment word public 'BSS' b@ label byte b@w label word _BSS ends _TEXT segment byte public 'CODE' ; ; set_brk() ; assume cs :_TEXT _set_brk proc near ; ; { push bp ;save the registers push ds push di push si mov dx , CS mov ds , dX mov dx , offset_brk mov ah , 25h ;Set interrupt vector mov al , 23h ;Ctrl-C handler int 21h pop si ;Retrieve the registers pop di pop ds pop bp ret ; } ; _set_brk endp ; ; brk() ; assume cs :_TEXT _brk proc near push ax ; Save the registers push bx push cx push dx push di push si push bp ; ; { ; handler(); ; call near ptr_handler269頁; ; } ; pop bp ;Retrieve the registers pop si pop di pop dx pop cx pop bx pop aX iret _brk endp _TEXT ends DATA segment word public『 DATA』 s@ label byte _DATA ends TEXT segment byte public'CODE' TEXT ends extrn _handler:near public _brk public_set_brk _s@ equ s@ end注意除編譯程序提供的代碼以外,沒有清除別的代碼;不過,它移動了調試碼。一些附 加的技巧已經被去掉(彙編語言編程高手會做到這一點),但這並非必不可少。唯一的要求 是代碼能工作。它做得到這一點。 最後,修改的handler.c程序見列表11.5。 列表11.5/* handler2.c Listing 11.5 of DOS Programmer's Reference*/ #include<stdlib.h> #include<stdio.h> #include<conio.h> #include<dos.h> #define CR 0x0D #define LF 0x0A int handler(void); void main() { void set_brk(void); int c; int i; if(getcbrk()==0) printf("BREAK checking is OFF\n"); else printf("BREAK checking is ON\n"); set_brk(); for(i=0; i<250; i++){ printf("%4.4d:Testing Ctrl-Break\n", i) ; }