



版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第一講C語言圖形方式下的編程學習目標?了解PC顯示系統(tǒng)的結(jié)構(gòu)?C語言圖形初始化的一般方法?C語言常用的圖形處理函數(shù)顯示系統(tǒng)簡介PC機顯示系統(tǒng)?般是由顯示器和顯示卡組成。顯示器(Monitor)是獨立于主機的ー種外部設備。顯示卡(Adapter)是插在Pc主機上的ー塊電路板。PC機對顯示屏幕的所有操作都是通過顯示卡來實現(xiàn)的。顯示系統(tǒng)的主要特性.顯示分辨率顯示分辨率是指屏幕上所能顯示的像素點數(shù),通常用列數(shù)和行數(shù)的乘積宋表示。為了獲得良好的顯示效果,要求顯示器的分辨率與對應分辨率的顯示卡相匹配。通常,高分辨率的顯示效果比低分辨率的效果好。但是,顯示分辨率的提高對顯示器與顯示卡的硬、軟件要求更高。特別是分辨率的提高在很大程度上受到顯示器的顯示尺寸和掃描頻率的限制,也受到顯示卡的顯存的限制。根據(jù)應用情況的不同,在不超過顯示器最高分辨率的條件下,可以通過對顯示卡的設置而使用不同的分辨率。.顯示速度顯示速度是指在屏幕上顯示圖形和字符的速度。顯示速度與顯示分辨率和顯示器的掃描頻率密切相關(guān)。顯示分辨率越高,整個屏幕上的像素點數(shù)就越多,顯示速度就越慢。在這種情況下,為了提高顯示速度,就需要提高掃描頻率。如果顯示器只有一種掃描頻率,則它只能與一種顯示卡相匹配使用。隨著顯示技術(shù)的發(fā)展,目前一般的顯示器可以適應具有多種分辨率與顯示速度的顯示卡。顏色與灰度是衡量顯示系統(tǒng)的重要參數(shù)。單色顯示器只有亮和暗兩種灰度;彩色顯示器的顏色和灰度要受顯示內(nèi)存的限制,分辨率越高,顏色越豐富,所需要的顯示內(nèi)存就越多。.圖形顯示能力圖形顯示能力是顯示系統(tǒng)對屏幕上的每一個像素點都可以設置成不同的值的能カ。通常,圖形顯示對硬件的要求比字符顯示要求高得多,同時,圖形顯示對顯示緩沖區(qū)的要求也比字符顯示時高得多。顯示卡的種類常用的顯示卡有以下兒種MDA卡MDA卡(MonochromeDisplayAdapter 單色字符顯示器適配卡)與單色字符顯示器配接,它只支持字符顯示功能,無圖形功能。HGC卡HGC卡(HerculesGraphicsCard單色顯示圖形卡)不僅支持字符顯示,而且支持單色圖形功能。在圖形方式下,其圖形顯示分辨率最高可達720?348點陣。通過軟件的設置,還可以支持640-400單色圖形顯示以及模擬CGA卡圖形方式。CGA卡CGA卡(ColorGraphicsAdapter--彩色圖形顯示卡)支持字符/圖形兩種方式。在字符方式下,它支持80歹リ、25行及奶列、25行的方式,顏色可選16種。但是字符的質(zhì)量比較差,只有8X8點陣。在圖形方式下,它支持最大為640-200分辨率,但只有黑、白兩種顏色。此外,它還支持320?200的分辨率,每個像素點可以有四種顏色。CGA卡有16K的顯示緩沖區(qū),用于顯示字符及屬性或圖形方式下的圖形數(shù)據(jù)。EGA卡EGA卡(EnhancedGraphicsAdapter增強型圖形顯示卡)的字符顯示能力和圖形顯示能力都比CGA卡有了較大的提高,顯示分辨率達到640?350,最高分辨率圖形方式的顏色達到了16種。EGA卡的顯示模式也比CGA卡豐富,并且兼容CGA卡及MDA卡的顯示模式。VGA卡VGA卡(VideoGraphicsArrayーー視頻圖形陣列)是ー種功能十分強大、顏色豐富的顯示卡。VGA的標準分辨率可達到640?480,并且具有16種顏色。ー些兼容的VGA卡(如TVGA卡、EVGA卡)的分辨率可達640-480(256種顏色)、800-600(16種顏色)或1024-768(16種顏色)。VGA卡兼容MDA卡、CGA卡、EGA卡的所有顯示模式。隨著顯示技術(shù)的發(fā)展,目前的顯示卡的分辨率一般都可以達到1024X768,顯示32位真彩(232種顏色)。視頻BIOS視頻BIOS(BasicI/OSystem基本輸入/輸出系統(tǒng))是與顯示卡配套的ー個重要組成部分。用戶通過調(diào)用它可以完成一些與顯示有關(guān)的控制功能。對于要用到顯示系統(tǒng)的一般程序設計都可以通過調(diào)用視頻BIOs或視頻函數(shù)庫來完成。但要想獲得更高性能的顯示程序,還需要對顯示卡的寄存器和顯示內(nèi)存直接進行編程。顯示模式顯示模式按功能可以分為字符模式和圖形模式兩大類。字符模式也稱為字母數(shù)字模式,即A/N模式(AlphaNumbermode)。在這種模式下,顯示緩沖區(qū)中存放的是顯示字符的代碼和屬性,而顯示屏幕被分為若干個字符顯示行和列。圖形模式(Graphicsmode)也稱為APA模式(A11PointsAddressablemode)〇在這種模式下,顯示緩沖區(qū)中存放的是顯示器屏幕上的每個像素點的顏色或灰度值,而顯示屏幕被劃分為像素行和像素列。由于顯示卡的種類很多,其中有些顯示模式在不同類型的顯示卡及不同廠家的顯示卡之間是通用的,這類顯示模式稱為標準模式。還有些模式是專用的,稱為非標準模式。通常,顯示模式號小于14H的是標準模式;其他則為非標準模式。圖形模式的初始化不同的顯示器適配器有不同的圖形分辨率。即使是同??顯示器適配器,在不同模式下也有不同分辨率。因此,在屏幕作圖之前,必須根據(jù)顯示器適配器的種類將顯示器設置成為某種圖形模式。在未設置圖形模式之前,微機系統(tǒng)默認屏幕為文本模式(80歹リ,25行字符模式),此時所有圖形函數(shù)均不能工作。設置屏幕為圖形模式,可用下列圖形初始化函數(shù):voidfarinitgraph(intfar*gdriver,intfar*gmode.char*path);其中g(shù)driver和gmode分別表示圖形驅(qū)動器和模式,path是指圖形驅(qū)動程序所在的目錄路徑。圖形驅(qū)動程序由TurboC出版商提供,文件擴展名為.BGL根據(jù)不同的圖形適配器有不同的圖形驅(qū)動程序。例如對于EGA、VGA圖形適配器的圖形驅(qū)動程序為EGAVGA.BGL有時編程者并不知道所用的圖形顯示器適配器種類,而且我們?yōu)榱藢⒕帉懙某绦蚩梢杂糜诓煌瑘D形驅(qū)動器,增強程序的通用性,我們通常不指定圖形顯示器適配器種類,而使用TurboC提供了一個自動檢測顯示器硬件的函數(shù),其調(diào)用格式為:voidfardetectgraph(int*gdriver,*gmode);其中g(shù)driver和gmode意義同上。例1:自動進行硬件測試后進行圖形初始化#include"graphics.h"main()(intgdriver,gmode;detectgraph(&gdriver,&gmode);/*自動測試硬件?/printf("driveris%d,modeis%d\n",gdriver,gmode);/?輸出結(jié)果?/getch();initgraph(&gdriver,&gmode,"");/?根據(jù)測試結(jié)果初始化圖形?/circle(320,240,50);circle(320,240,80);circle(320,240,110);getch();closegraph();}上例程序中先對圖形顯示器自動檢測,然后再用圖形初始化函數(shù)進行初始化設置。其中,closegraph()為退出圖形狀態(tài)的函數(shù),其調(diào)用格式為:voidfarclosegraph(void):調(diào)用該函數(shù)后可退出圖形狀態(tài)而進入文本方式,并釋放用于保存圖形驅(qū)動程序和字體的系統(tǒng)內(nèi)存。同時TurboC提供了一種更簡單的初始化圖形的方法,即用gdriver=DETECT語句后再跟initgraph。函數(shù)就行了。比如,上例可改為例2的樣子。例2:#include"graphics.h"main()(intgdriver=DETECT,gmode;initgraph(&gdriver,&gmode,"");circle(320,240,50);circle(320,240,80);circle(320,240,110);getch();closegraph();}屏幕顏色的設置對于圖形模式的屏幕顏色設置,同樣分為背景色的設置和前景色的設置。在Turboc中分別使用以下兩個函數(shù):voidfarsetbkcolor(intcolor);設置背景色voidfarsetcolor(intcolor):設置作圖色其中color為圖形方式下顏色的規(guī)定數(shù)值,符號常數(shù)及數(shù)值如下所示。符號常數(shù)數(shù)值含義字符或背景BLACK0黑兩者均可BLUE!蘭兩者均可GREEN2綠兩者均可CYAN3青兩者均可RED4紅兩者均可MAGENTA5洋紅兩者均可BROWN6棕兩者均可LIGHTGRAY7淡灰兩者均可DARKGRAY8深灰只用于字符LIGHTBLUE9淡蘭只用于字符LIGHTGREEN10淡綠只用于字符LIGHTCYAN1!淡青只用于字符LIGHTRED12淡紅只用于字符LIGHTMAGENTA13淡洋紅只用于字符YELLOW14黃只用于字符WHITE15白只用于字符BLINK128閃爍只用于字符清除圖形屏幕內(nèi)容使用清屏函數(shù),其調(diào)用格式如下voidfarcleardevice(void);有關(guān)顏色設置、清屏函數(shù)的使用請看例3。例3:#include"stdio.h"#include"graphics.h"mainO(intgdriver,gmode,i,j;gdriver=DETECT;initgraph(&gdriver,&gmode,"");/?圖形初始化?/setbkcolor(〇);/?設置圖形背景?/cleardevice();for(i=0;i<=15;i++)(setcolor⑴;/?設置不同作圖色?/circle(319,239,20+i*15);/?畫半徑不同的圓?/delay(3000);/?延遲3000毫秒?/}for(i=0;i<=15;i++)(setbkcolor(i);/?設置不同背景色?/cleardevice();for(j=0;j<=15;j++)(setcolor(j);/*設置不同作圖色?/circle(319,239,20+j*15);/?畫半徑不同的圓ッdelay(3000);1}getch();closegraph();}另外,TURBOC也提供了幾個獲得現(xiàn)行顏色設置情況的函數(shù)。intfargetbkcolor(void):返回現(xiàn)行背景顏色值。intfargetcolor(void);返回現(xiàn)行作圖顏色值。intfargetmaxcolor(void):返回最高可用的顏色值?;緢D形函數(shù)基本圖形函數(shù)包括畫點,線以及其它一些基本圖形的函數(shù)。本節(jié)對這些函數(shù)作ー全面的介紹。畫點.畫點函數(shù)voidfarputpixel(intx,inty,intcolor);該函數(shù)表示有指定的象元畫ー個按color所確定顏芯的點。對于顏色color的值可從上表中獲得而對x,y是指圖形象元的坐標。在圖形模式下,是按象元來定義坐標的。對VGA適配器,它的最高分辨率為640x480,其中640為整個屏幕從左到右所有象元的個數(shù),480為整個屏幕從上到下所有象元的個數(shù)。屏幕的左上角坐標為(0,0),右下角坐標為(639,479),水平方向從左到右為x軸正向,垂直方向從上到下為y軸正向。TURBOC的圖形函數(shù)都是相對于圖形屏幕坐標,即象元來說的。關(guān)于點的另外一個函數(shù)是:intfargetpixel(intx,inty);它獲得當前點(x,y)的顏色值。.有關(guān)坐標位置的函數(shù)intfargetmaxx(void):返回x軸的最大值。intfargetmaxy(void);返回y軸的最大值。intfargetx(void);返回游標在x軸的位置。voidfargety(void);返回游標有y軸的位置。voidfarmoveto(intx,inty);移動游標到(x,y)點,不是畫點,在移動過程中亦畫點。voidfarmoverel(intdx,intdy):移動游標從現(xiàn)行位置(x,y)移動到(x+dx,y+dy)的位置,移動過程中不畫點。畫線與線型設定.畫線函數(shù)TURBOC提供了一系列畫線函數(shù),下則分別敘述:voidfarline(intx0,intyO,intxl,intyl):畫一條從點(xO,y0)到(xl,yl)的直線。voidfarlineto(intx,inty);畫一作從現(xiàn)行游標到點(x,y)的直線。voidfarlinerel(intdx,intdy);畫一條從現(xiàn)行游標(x,y)到按相對增量確定的點(x+dx,y+dy)的直線。voidfarcircle(intx,inty,intradius);以(x,y)為圓心,radius為半徑,畫一個圓。voidfararc(intx,inty,intstangle,intendangle,intradius);以(x,y)為圓心,radius為半徑,從stangle開始到endangle結(jié)束(用度表示)畫一段圓弧線。在TURBOC中規(guī)定x軸正向為〇度,逆時針方向旋轉(zhuǎn)一周,依次為90,180,270和360度(其它有關(guān)函數(shù)也按此規(guī)定不再重述)。voidellipse(intx,inty,intstangle,intendangle,intxradius,intyradius);以(x,y)為中心,xradius,yradius為x軸和y軸半徑,從角stangle開始到endangle結(jié)束畫一段橢圓線,當stangle=O,endangle=360時,畫出,,個完整的橢圓。voidfarrectangle(intxl,intyl,intx2,inty2);以(xl,yl)為左上角?(x2,y2)為右下角畫一個矩形框。voidfardrawpoly(intnumpoints,intfar*polypoints);圓一個頂點數(shù)為numpoints,#頂點坐標由polypoints給出的多邊形。polypoints整型數(shù)組必須至少有2倍頂點數(shù)個無素。每ー個頂點的坐標都定義為x,y,并且x在前。值得注意的是當畫一個封閉的多邊形時,numpoints的值取實際多邊形的頂點數(shù)加一,并且數(shù)組polypoints中第一個和最后ー個點的坐標相同。.設定線型函數(shù)在沒有對線的特性進行設定之前,TURBOC用其默認值,即一點寬的實線,但TURBOC也提供了可以改變線型的函數(shù)。線型包括:寬度和形狀。其中寬度只有兩種選擇:一點寬和三點寬。而線的形狀則有五種。下面介紹有關(guān)線型的設置函數(shù)。voidfarsetlinestyle(intlinestyle,unsignedupattern,intthickness);該函數(shù)用來設置線的有關(guān)信息,其中l(wèi)inestyle是線形狀的規(guī)定,見下表:該程序給用戶三次輸入密碼的機會,如果正確則顯示OK!welcome,否則顯示Sorry!Youcannotlogin。DOS系統(tǒng)下的批處理程序的加密程序磁盤操作系統(tǒng)DOS的批處理程序是由后綴.BAT為擴展名的文本文件,該文件包括若干條DOS內(nèi)部命令、外部命令及用戶應用程序組成。當磁盤操作系統(tǒng)的命令解釋程序COMMAND.COM在命令所指定的路徑上找到欲執(zhí)行的批處理文件后,逐條解釋并執(zhí)行相應的有關(guān)命令,完畢后返回到DOS命令處理程序狀態(tài)。由于批處理程序是以文本文件的形式存儲在磁盤中,并能通過有關(guān)命令或文本編輯程序進行查閱、修改,所以批處理程序是完全透明的無保密性及安全性可言。為此,人們采取各種各樣的保密方法、手段對批處理文本文件進行處理,以防止批處理程序被篡改、暴靈。如何才能得到ー種更安全保密的批處理文件的保密方法?我們通過實踐找到了解決批處理程序進行加密的?種較好方法。用DEBUG或PCTOOLS工具軟件對批處理程序進行分析,可以發(fā)現(xiàn)每ー個命令均以ODH,0AH作為該命令的結(jié)束符,而整個批處理文件以ODH,OAH,IAH作為該處理程序的結(jié)束符。ODH,OAH,IAH分別是ASCI!碼的回車符,換行符、文件結(jié)束符,并且磁盤操作系統(tǒng)命令處理程序COMMAND.COM是以ODH作為批處理命令的結(jié)束符,以IAH作為批處理程序的結(jié)束符。通過以上分析,可以在不破壞原批處理程序文本文件組織結(jié)構(gòu)的前提下,對批處理文本進行預處理,使批處理程序的換行符OAH全部改為返冋00H,從而使經(jīng)過預處理的批處理程序的所有內(nèi)容每一行層層覆蓋,起到為批處理文件加密的作用。用此方法對批處理文本文件處理后,用TYPE,EDLIN,WS,DEBUG,PE,SK等命令或工具軟件都無法破譯批處理程序,最多只能破譯最后兩行的疊加結(jié)果,而不能確切得出最后兩行的內(nèi)容。以下給出對批處理文本程序進行預處理的加密程序。此程序不僅能解決批處理程序的安全保密,還能夠運用于文本文件形式的其他程序的安全保密,譬如BASIC解釋程序、dBASE及MFFOXPLAS源程序。#include<dos.h>#include<stdio.h>#include<string.h>/?開始修改為固定返回符。OH*/main()(charerrorkkj[35],filename[20],*ptr,*data_all;staticcharreaderrorロメ讀文件失敗ジ;FILE*stream;intlength;printf("請輸入需要加密的文本文件名:");gets(filename);stream=fopen(filename,”rd+”);/?打開二進制文本文件?/if(stream=NULL)(printf(“007”);printf(“007”);strcpy(errorkkj,readerror);strcat(errorkkj,filename);printf(”%s”,errorkkj);printf(”\007”);}else(fseek(stream,2,SEEK_END);length=ftell(stream);data_all=(char*)malloc((unsigned)length,sizeof(char));if(!data_all);(printf(”出現(xiàn)錯誤,請檢查后再運行!”);exit(l);}rewind(stream);while(!feof(stream))/?修改為返回符00H*/fscanf(stream,"[\x00]n,data_all);while(ptr=strrchr(data_all,,\n,))strnset(ptrAxOO*,1);}rewind(stream);fwrite(data_all,1,length,stream);fclose(stream);)數(shù)據(jù)文件的加密算法——替代加密法在C語言中對數(shù)據(jù)文件利用替代法進行加密。計算機軟件編程者往往對加密技術(shù)比較感興趣,其原因可能是加密是?種算法,它和編程一樣很具有吸引力。在許多場合下加密是ー種必須做的工作。常用的加密方法有三種:①替代加密法:是以另外一個字符替代原來的字符并保持數(shù)據(jù)次序不變。②換位加密法:根據(jù)某ー規(guī)則打亂原來的數(shù)據(jù)順序。③位操作加密法:按照某種算法改變數(shù)據(jù)在計算機內(nèi)的表示方法。替代加密法是最簡單的加密法,它是把文件中所有的字母都用另外一個字母代替而進行加密的,如:將字符串中所有的字符值都增加2,即:“abcde”將被加密為“cdefg”這種算法只是簡單地把原字符串的值偏移ー個固定的量,很容易被人識別。這是因為它最多只有26種可能偏移的情況,用不了多長時間就可以嘗試出來,改進的辦法就是每個字符的偏移情況各不相同,而使用多種偏移量。另外一個原因是它保留了各個單詞之間的空格,破譯者很容易將一個個單詞區(qū)分開,改進的辦法就是對空格也進行加密。綜合上述我們可采用隨機偏移的辦法進行加密,即對26個大小寫字母和?個空格這53個字符小集合中,給每個字符隨便指定一加密字符,這樣加密后的加密文件可能有271種情況,被破譯的機會相對小的多了。如在下例中我們將字符集zmb的加密密鑰指定為mm,這樣一來凡是在文件中出現(xiàn)a的地方都將用Q來替代,出現(xiàn)b的地方都將用A來替代,依次類推。其中這個密鑰變量mm的順序可隨機更改,但對加密的文件必須用相同的密鑰才能解密。使用方法有兩種:①passwordく文件名1><文件名2><E>:將文件1加密為文件2。②passwordく文件名1>〈文件名2><D>:將文件1解密到文件2。/*password.c*/#include<stdio.h>#include<ctype.h>char mm[54]="QAZWSXEDCRFVTGBYHNUJMIKOLPqwertyuiopasdfghjklzxcvbnm1';char zmb[54]=nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”;/*mm為加密字符串即密鑰,zmb為原字母對照表*/main(intargc,char*argv[]){FILE*fpl,*fp2;intch,flag=0;if((toupper(*argv[3])—E*)ll(toupper(*argv[3])—D*))flag=l;if((argc!=4)ll(flag!=!)){printf(n\007Error:參數(shù)錯誤!!\n“);printf(H格式:JM1inputoutputE/D'n");exit(O);)if((fpl=fopen(argv[l],nrM))==0){printf(n\007Error:不能打開輸入文件!\n");exit(O);)if((fp2=fopen(argv[2],nwn))==0){printf(M\OO7Error:不能創(chuàng)建輸出文件!\n”);exit(O);)do{ch=getc(fpl);if(ch==EOF)break;if(isalpha(ch)llch==,*)if(toupper(*argv[3])==E*)/*E:加密D:解密?/ch=mm[find(zmb,ch)];elsech=zmb[find(mm,ch)];putc(ch,fp2);}while(l);fclose(fpl);fclose(fp2);)find(char*s,intch){registerintt;for(t=0;t<54;t++)if(ch==s[tl)returnt;}該加密方法可以說是比較科學,但是所生成的文件還是不難破譯的,這是因為并不是所有字符出現(xiàn)的頻率是ー樣的,而是有的多有的少。根據(jù)字母的頻度表,比較容易破譯密碼文件,而且加密文件越長就越容易破譯。為解決這個問題我們可以用多個替代密鑰進行加密,即待加密文件中的每個字符不是始終用另ー個字母來代替,而是輪翻由多個其他字符代替。這里我們就不詳細講了。成批文件的簡易加密法下面這個程序可以使用通配符“,”和“?”,一次加密多個文件,不管是數(shù)據(jù)文件還是其他文件(包括各種可執(zhí)行文件)。程序采用替換加密法,對文件中所有字母數(shù)字和空格進行替換;即把串S!中每個字符用串S2中相應的字符替換,達到加密目的,帶參數(shù)E(encode)是加密過程,D(decode)是解密過程。例如:敲入CODEA*.*E就把當前目錄下所有以A開頭的文件加密;再敲入CODEA*.*E便把所有A*.?文件恢復。程序加密過程可反復運行多次,以達到多重替換的目的。解密時只需逆向運行相同的次數(shù)即可。/*CODE.c*/#include"dir.h"#include"ctype.h”#include"stdio.h"voidencode(char*);voiddecode(char*);intat(char,char*);chars[64]=uabcdefghijklmnopqrstuvwxyzl23456789\ABCDEFGHIJKLMNOPQRSTUVWXYZ0;chart[64]=nqwertopasdfghjkizxcvbnm\MIN2B3V4c5X6Z7L8K9JOHGFDSAPOIUYTREWQ”;voidmain(intargc,char*argv[]){structffblkptr;intdone;charpd;pd=toupper(*argv[2]);if((argc!=3)ll(strlen(argv[2])>l)ll(pd!=,E,&&pd!=D,)){printf("usageerrorAn*');exit();)done=findfirst(argv[1],&ptr,O);while(!done){if(pd==E){printf("Encodingfile:%s\n'\ptr.ff_name);encode(ptr.ff_name);}else{printf("Decodingfile%s\n",ptr.ff_name);decode(ptr.ff_name);}done=findnext(&ptr););)voidencode(char*filename)(FILE*fpl,*fp2;intch;if((fp1=fopen(filename,"rb"))==NULL){printf("cannotopenfile:%s\n",filename);exit();}if((fp2=fopen("is","wb"))==NULL){printf("cannotopenfile:is\n");exit();}while((ch=getc(fpl))!=EOF){if(isalnum(ch)llch=="")ch=t[at(ch,s)];putc(ch,fp2);)fclose(fpl);fclose(fp2);fpl=fopen("is","rd");fp2=fopen(filename,Mwbn);while((ch=getc(fp1))!=EOF){putc(ch,fp2);)fclose(fpl);fclose(fp2);}voiddecode(char*filename)(intch;FILE*fpl,*fp2;if((fp1=fopen(filename,nrdn))==NULL){printf(ucannotopenfile:%s\nn,filename);exit();}if((fp2=fopen(,,is,,;,wb',))==NULL){printf(Mcannotopenfile:is\nn);exit();}while((ch=getc(fpl))!=EOF){if(isalnum(ch)llch=MM)ch=s[at(ch,t)];putc(ch,fp2););fclose(fp1);fclose(fp2);fpl=fopen(',is,,,nrbn);fp1=fopen(nfilename,H,nwbn);while((ch=getc(fp1))!=EOF){putc(ch,fp2);)fclose(fpl);fclose(fp2);}intat(charch,char*ss)(registerinti;for(i=0;iv64;i++)if(ch==ss)returni;)限制軟件的使用次數(shù)限制軟件使用的方法有很多,關(guān)鍵是把次數(shù)這個值儲存在哪里,在這里我們介紹這么ー種方法,通過改變文件的時間來存儲試用次數(shù),從而限制軟件的試用。在MS-DOS中,文件的時間是通過ー個16位的值來保存的,位!5-1I用來保存小時數(shù),位10?5用來保存分鐘數(shù),位4?〇用來保存秒鐘數(shù)。這個16位的值正好可以被我們用來保存試用次數(shù)。例如,在下面的示例程序中,我們就使用了保存分鐘數(shù)的位來保存試用次數(shù)。運行一下示例程序,我們可以看到,只是文件時間的分鐘數(shù)加了1,一般人很難察覺,這種方法有比較好的隱蔽性。下面我們簡要介紹一下示例程序中所涉及的中斷。1,中斷21H的功能3DH用途:打開文件用于讀、寫或補充。調(diào)用寄存器:AH=3DH,AL-OOH,DS:DX=文件名;返回寄存器:CF復位一成功,AX:文件把柄。CF置位一出錯,AX=錯誤代碼。2,中斷21H的功能3EH用途:強迫清除所有緩沖區(qū),更新目錄,使文件把柄無效。調(diào)用寄存器:AH=3EH,BX二文件把柄;返回寄存器:CF復位一成功,AX被破壞,CF置位—出錯,AX:錯誤代碼。3,中斷21H的功能57H、子功能OOH用途:讀出文件被最后修改的日期和時間。調(diào)用寄存器:AX=57OOH,BX=文件把柄;返回寄存器:CF復位ー成功,CX=文件時間CF置位一出錯,AX:錯誤代碼。4,中斷21H的功能57H、子功能01HDX=文件日期用途:將文件最后被修改過的日期和時間信息變成指定值。調(diào)用寄存器:AX=5701H,BX=文件把柄,CX=新時間,DX=新日期返回寄存器:CF復位一成功,CF置位一出錯,AX:錯誤代碼下面我們就來看這個程序:#include<stdio.h>#include<dos.h>#include<conio.h>intcount()unionREGSinregs,outregs;structSREGSsegs;unsignedhandle;unsignedtimes;/?打開文件?/inregs.h.ah=0x3d;inregs.h.al=0;segread(&segs);inregs.x.dx=ntest.exen;intdosx(&inregs,&outregs,&segs);if(outregs.x.cflag)return(O);else/?讀取文件日期時間?/handle=outregs.x.ax;inregs.x.ax=0x5700;inregs.x.bx=handle;intdosx(&inregs,&outregs,&segs);times=(outregs.x.cx&Ox7eO)?5;if(times<40)/?試用次數(shù)可以通過改變此處的來實現(xiàn)ッ(inregs.x.ax=OxD701;inregs.x.bx=handle;inregs.x.cx=outregs.x.cx+0x20;/*試用次數(shù)計數(shù)?/inregs.x.dx=outregs.x.dx;intdosx(&inregs,&outregs,&segs);/?重置日期時間?/return(2);}elsereturn(l);/?關(guān)閉文件?/inregs.h.ah=0x3e;inregs.x.bx=handle;intdosx(&inregs,&outregs,&segs);})main()(intflag;flag=count();if(flag==O)printf("此軟件為試用版,請勿任意修改!\n");if(flag==1)printf("此軟件為試用版,您的試用次數(shù)已滿!W");if(flag=2)printf("此軟件為試用版,歡迎試用!、ガ);}小結(jié)這ー講我們簡要介紹了C語言中加密的應用,在D版軟件充斥市場的今天,我們有必要好好探討一下加密算法。雖然本講的內(nèi)容比較簡單,但是還是希望大家努力學好。第十二講C語言大作業(yè)設計思路今天,我們來做c語言高級程序設計講座的最后ー講,主要講??下C語言大作業(yè)的設計思路。當前,很多院校在C語言結(jié)課的時候都要求做?份大作業(yè),作為考核的一-部分,很多同學為此撓頭,在這里我們將一些設計的思路,希望能給大家一點提示。所謂大作業(yè),其實質(zhì)目的就是讓學生在制作過程中體驗這種設計思路,一般來說,思路有了,其他的就不在話下了。畢竟我們不是要求來做這種Windows之類的大型軟件,Microsoft的精英開發(fā)了好兒年,如果我們用C語言來做,恐怕也不是一朝一タ能完成的。我們要求學生能夠設計出ー個小型的游戲或者ー個小型的數(shù)據(jù)庫管理軟件就很好了。我們在設計之前,一般考慮以下幾點:1、問題定義我們首先要知道應該做什么。2、可行性分析然后確定這個方案能否實施,不要做那種自己不可能完成的程序,“只有想不到,沒有做不到”這句話以馬克思主義哲學的觀點來看是不成立的。3、需求分析如果這個方案可行,就要考慮這個程序需要實現(xiàn)什么功能。4、系統(tǒng)設計前面的都確定后,要設計出宏觀的框架,例如主界面。5、詳細設計詳細的設計每一項功能。6、編譯并測試一般來講,我們提倡每完成一項內(nèi)容都要編譯測試。7、運行維護自己用自己的軟件,用所有想到的錯誤操作來操作它,找出所有的內(nèi)在BUG。8、發(fā)布如果你自己沒有找到錯誤了,該軟件就差不多可以交上去了,結(jié)合開發(fā)過程我們就可以寫出論文來。下面我們來介紹ー個簡單的人工智能程序。初始化人機界面/*exper_l.c*/#include<stdio.h>#include<stdlib.h>#include<conio.h>#defineN11char*pre[]={3",“p2“Jp3”,“p4”,“p5”,"p67p7”,“p8“Jp9“JplO”Jplド);char*symptom[]={"咳嗽","流鼻涕","怕冷","打噴嚏","痰多","體溫高","血象偏高","泛酸","嘔吐","上腹隱痛","食欲不振",1;main()(inti,j;FILE*fp;charanswer;charmidpre[N][5];if((fp=fopen("c:\\bc\\bin\\pre.txt","wt"))=NULL)printf(℃annotopenflie");exit(O);)for(i=0;i<N;i++)(printf(n\n你%$嗎?\n”,symptom);answer=strlwr(getch());switch(answer)(casey:putchar(answer);fprintf(fp,"%sn,pre);fprintf(fp,n");break;case'n':putchar(answer);break;)}fclose(fp);printf("\n你的癥狀是:");if((fp=fopen("c:\\bc\\bin\\pre.txt","rt"))==NULL)(printf("Cannotopenflie");exit(O);}i=0;while(fscanf(fp,"%s",midpre)!=EOF)(for(j=0;j<N;j++)(if(!strcmp(midpre,pre[j1))printf("%s",symptom[j]);}1fclose(fp);printf("\n按任意鍵返回、n");getch();)具體的實例程序#include<stdlib.h>#include<stdio.h>#include<string.h>#include<process.h>#defineN10#defineM11structRL{intrnum;intrpsum;charpre[N][5];charres[5];);structRLzrul[N];char*pre[]={“pl",“p2”Jp3“,“p4”,“p5“Jp6”,“p7”,“p8“Jp9“JplO”Jplド);char*symptom[]={"咳嗽","流鼻涕","怕冷","打噴嚏","痰多","體溫高","血象偏高”,"泛酸","嘔吐","上腹隱痛","食欲不振",);char*result[]={"rl","r2","r3");char*diagnose[]={"上感","支氣管炎","胃炎");inthave()(FILE*fp;inti,ri=-l,rs=0;charmstr[5];chardstr[N][5];if((fp=fopen(nc:\\bc\\bin\\jingyan.txtn,,,rtn))==NULL)(printf("Cannotopenflie");exit(0);}while(fscanf(fp,n%sM,mstr)!=EOF)(if((!strstr(mstr,"p"))&&(!strstr(mstr,"r")))(ri++;zrul[ri].rnum=atoi(mstr);if(ri>0)zrulfri-1].rpsum=rs-1;for(i=0;i<zrul[ri-1].rpsum;i++)strcpy(zrul[ri-l].pre,dstr);strcpy(zrul[ri-1].res,dstr[rs-1]);}rs=O;}else(strcpy(dstr[rs],mstr);rs++;))zrul[ri].rpsum=rs-1;for(i=0;i<zrul[ri].rpsum;i++)strcpy(zrul[ri].pre,dstr);strcpy(zrul[ri].res,dstr[rs-l]);fclose(fp);retum(ri+l);}query(intn)(FILE*fp,*fpi;charlstr[N][5];charmstr[5];inti=O,j=O,k=O;intpsum=0;intagrees=0;intm;if((fp=fopen(nc:\\bc\\bin\\pre.txt,,,,,rtn))==NULL)(printf("Cannotopenflie");exit(O);}if((fpi=fopen(nc:\\bc\\bin\\reason.txtnwt0))==NULL)(printf("Cannotopenflie");exit(O);}while(fscanf(fp,"%sn,mstr)!=EOF)(strcpy(lstr,mstr);i++;}fclose(fp);psum=i;for(i=0;i<n;i++)(if(zrul.rpsum==psum)(for(j=0;j<psum;j+-b)(for(k=0;k<zrul.q?sum;k++)(if(!strcmp(lstr|j],zrul.pre(k]))agrees++;)}if(agrees==zrul.q)sum)fprintf(fpi,n%sM,zrul.res);agrees=0;for(m=0;m<l1;m++)(if(!strcmp(zrul.res,result[m]))printf(n\n對你的癥狀做出的診斷為:%s\n”,diagnose[m]);}})fclose(fpi);)main()(intn;system(Mc:\\bc\\bin\\exper_l.exen);n=have();query(n);)文本經(jīng)驗數(shù)據(jù)庫1plp2p3p4p5p6p7rl2plp5p6r23p8p9pl0pllr3大家可以通過對本例程的進ー步完善使之成為ー個專家系統(tǒng),讓自己的電腦成為家庭醫(yī)生。到這里,你是不是對C語言大作業(yè)的設計有了一定的思路了呢?我們這ー講就到這里了。小結(jié)這ー講首先對VGA卡的結(jié)構(gòu)原理作了介紹,然后分析了BIOS中斷調(diào)用來設置圖形方式的方法,最后給出了一個完整的調(diào)整圖像灰度的程序,這些知識在圖像處理編程中是有很大作用的。講座到這里,我們對于程序外殼編寫的討論就要告一段落了,我們的后繼課程將討論ー些編程技術(shù),主要包括:中斷調(diào)用、底層編程技術(shù)、鼠標的使用、程序的內(nèi)存駐留與釋放、軟件加密算法等。第七講中斷調(diào)用鵬程C語言教學網(wǎng)站慶祝建站一周年特別企劃本文技術(shù)支持網(wǎng)站:29從這ー講起我們將過渡到研究程序內(nèi)部的ー些編程方法,不同于前6講,接下來的內(nèi)容可能會比較枯燥,但是卻是C語言高級程序設計必不可少的部分,我盡量用比較簡短的語言來敘述,以避免大家一看頁數(shù)就頭暈。對于中斷調(diào)用,其實是很復雜的,這里講得或許只是ー?個皮毛,更深入的研究還要大家自己努力。學習目標?了解什么是中斷?了解兒個中斷函數(shù)?了解中斷服務程序的編寫什么是中斷所謂中斷,是指CPU在正常運行程序時,由于程序的預先安排或內(nèi)外部事件,引起CPU中斷正在運行的程序,而轉(zhuǎn)到為預先安排的事件或內(nèi)外部事件服務的程序中,這些引起程序中斷的事件稱為中斷源。預先安排的事件是指PC機的中斷指令,執(zhí)行到此,立即轉(zhuǎn)相應的服務程序去執(zhí)行。內(nèi)部事件是指系統(tǒng)板上出現(xiàn)的ー些事件信號,中斷指令也可看作內(nèi)部事件,外部事件是指某些接口設備所發(fā)出的請示中斷程序執(zhí)行的信號,這些信號稱為中斷請求信號。中斷請求信號何時發(fā)生是不能預知的,然而,它們一旦請求中斷,則會向CPU的接收中斷信號的引腳發(fā)出電信號,因此這些信號CPU是馬上可以知道的。這樣CPU就無需花大量的時間去查詢這些信號是否產(chǎn)生。因為中斷請示信號一旦產(chǎn)生,便會馬上通知CPU〇如鍵盤,何時有鍵按下,是隨機的。因而CPU可以對鍵盤不加理睬,而去執(zhí)行其他程序,一旦有鍵按下,鍵盤馬上產(chǎn)生中斷請求信號,CPU得知這信號后,便立即去執(zhí)行為鍵盤服務的中斷程序,服務完后,CPU又恢復執(zhí)行被中斷了的程序。中斷服務程序執(zhí)行完,返回原來執(zhí)行程序的中斷處(稱為斷點)繼續(xù)往下執(zhí)行,稱為中斷返回。有時中斷請求信號(即中斷源)可能有好兒個,因此CPU響應這些中斷就得有先后次序,這稱為中斷的優(yōu)先級。CPU首先響應優(yōu)先級高的中斷,優(yōu)先級低的中斷,暫不響應,稱為掛起。有些中斷源產(chǎn)生的中斷,可以用編程的辦法使CPU不予理睬,這叫中斷的屏蔽。CPU響應中斷,轉(zhuǎn)去執(zhí)行中斷服務程序前,需將被中斷程序的現(xiàn)場信息保存下來,以便執(zhí)行完中斷服務程序后,接著從被中斷程序的斷點處繼續(xù)往下執(zhí)行?,F(xiàn)場信息是指程序計數(shù)器的內(nèi)容、CPU的狀態(tài)信息、執(zhí)行指令后的結(jié)果特征和一些通用寄存器的內(nèi)容,有些信息的保存和程序計數(shù)器的內(nèi)容等由機器硬件預先安排完成,稱為中斷處理的隱操作。有些信息保存是在中斷服務程序中預先安排的。CPU響應中斷時,由中斷源提供地址信息,引導程序轉(zhuǎn)移到中斷程序中去執(zhí)行。這個地址信息稱為中斷向量,它?般是和中斷源相對應的,PC機采用類型號來標識中斷源。中斷方式以其執(zhí)行速度快,可實時處理,不占用CPU過多的時間等優(yōu)點,在ー些高級應用場合中較多地被采用。PC機中斷系統(tǒng)不僅具備一般中斷系統(tǒng)的特點,而且有所創(chuàng)新,比如,中斷不僅可由外部事件引起,也可由預先安排的事件,或稱為內(nèi)部的事件引起,這些內(nèi)部事件是指中斷指令和執(zhí)行…些指令引起的特殊事件等。兒個中斷函數(shù)在我們了解了什么是中斷后,我們通過實例來介紹幾個DOS中斷函數(shù)。int86()函數(shù)函數(shù)說明如下:intint86(intintr_num,unionREGS*inregs,unionREGS*ourregs);這個函數(shù)的三個參數(shù)如上述的BIOS功能調(diào)用ー樣,其中第一個參數(shù)mtr_num表示BIOS調(diào)用類型號,即中斷類型號,第二個參數(shù)表示是指向聯(lián)合類型REGS的指針,它用于接收調(diào)用的功能號及其他ー些指定的入口參數(shù),以便傳給相應的寄存器,第三個參數(shù)也是ー個指向聯(lián)合類型REGS的指針,它用于接收功能調(diào)用后的返回值,即出口參數(shù),如調(diào)用的結(jié)果,狀態(tài)信息,這些值從相關(guān)寄存器中得到。聯(lián)合類型PEGS在dos.h頭文件中定義,因而使用該函數(shù)時,應將dos.h文件包括在程序中,在dos.h中PEGS定義如下:structWORDREGS(unsignedintax,bx,cx,dx,si,di,cfiag,flags;1;structBYTEREGS(unsignedcharal,ah,bl,bh,cl,ch,dl,dh;)unionPEGS(structWORDREGSx;structBYTEREGSh;}它表示REGS是ー個聯(lián)合類型名。該聯(lián)合的成員由結(jié)構(gòu)類型的x和h組成,其中x代表16位寄存器變量,它的成員用8088CPU的相關(guān)寄存器名表示(注意!它們并不是真正的物理寄存器)。其中cilag代表標志寄存器的進位標志位,一般調(diào)用后,從該標志位是〇還是I可知調(diào)用成功還是失敗。結(jié)構(gòu)類型h代表8位寄存器變量,即長度為字節(jié)的結(jié)構(gòu)變量,它的成員與相關(guān)的8088的8位寄存器同名。unionREGS表示兩個成員x,h共用ー個內(nèi)存區(qū)域,即可以是16位長,也可以是8位長,我們可以用這個內(nèi)存區(qū)向有關(guān)的寄存器賦值,或取得有關(guān)寄存器的返回值,這樣就和BIOS調(diào)用格式一致起來了,如定義ー個ax變量并賦值為2,可寫作:unionREGSregs;regs.x.ax=2;它表示regs變量是ー個REGS聯(lián)合類型,取其16位的結(jié)構(gòu)成員x中的ax井賦值2。上述的定義與賦值過程,實際上就完成了對該變量代表的寄存器的賦值。下面示例INT10H,功能號為2的顯示中斷調(diào)用,它的功能是設置光標位置,其中DH為光標所在行號,DL為光標所在列號,BH為顯示光標的頁號,選0。出口參數(shù)無,下面的程序在屏幕35行10列出顯示出Hello字樣。#include<stdio.h>#include<conio.h>#include<dos.h>#defineVIDEO0x10voidmovetoxy(intx,inty)(unionREGSregs;regs.h.ah=2;regs.h.dh=y;regs.h.dl=x;regs.h.bh=O;int86(VIDEO,?s,?s);/*BIOS中斷調(diào)用?/)intmain(void)(clrscr();/?清屏幕?/movetoxy(35,l0);/?調(diào)用movetoxy函數(shù)?/printf("Hello\n");return0;)intdos()函數(shù)它的說明格式是:intintdos(unionREGS*inregs,unionREGS*outregs);該函數(shù)的第一個參數(shù)足表示相應功能號的入口參數(shù),第二個參數(shù)是出口參數(shù),調(diào)用后,返回值在outregs的AX寄存器中,并將設置標志寄存器flags,若進位標志器cilag被置位,則表示調(diào)用出錯,此時AX中為錯誤代碼,若為零,則表示成功。由于該函數(shù)的參數(shù)中沒有涉及到有關(guān)段寄存器,因而當進行要改變段的ー些調(diào)用時,該函數(shù)就不能用了。下面的示例程序是利用該函數(shù)進行DOS系統(tǒng)調(diào)用,刪除ー個當前目錄下的文件(不需要改變段值):#include<stdio.h>#include<dos.h>intdelete_file(charnear"filename)(unionREGSregs;intret;regs.h.ah=0x41;regs.x.dx=(unsigned)filename;ret=intdos(?s,?s);return(regs.x.cflag?ret:O);)intmain(void)(charnear*filename;interr;printf("Enterfilename:");gets(filename);err=delete_file(filename);if(!err)printf("Abletodelete",filename);elseprintf("NOTabletodelete,thisfilenotexist\n");return0;}通過上面兩個函數(shù)的分析,您是否已經(jīng)明白ー些了呢?下面我們來講一下中斷服務程序的編寫。中斷服務程序的編寫對于ー一般的C語言愛好者而言,就如何在C中使用中斷例程這ー問題應該已經(jīng)非常熟悉,例如,我們可以通過int86()函數(shù)調(diào)用13H號中斷直接對磁盤物理扇區(qū)進行操作,也可以通過int86()函數(shù)調(diào)用33H號中斷在屏幕上顯示鼠標光標等。其實,13H號也好,33H號也好,它們只不過就是ー些函數(shù),這些函數(shù)的參數(shù)通過CPU的寄存器傳遞。中斷號也只不過是間接地指向函數(shù)體的起始內(nèi)存單元,說它是間接的,也就是說,函數(shù)的起始段地址和偏移量是由中斷號通過ー種方法算得的(具體如何操作,下面會作解釋)。如此一來,程序員不必要用太多的時間去寫操作硬件的程序了,只要在自己的程序中設置好參數(shù),再調(diào)用BIOS或DOS提供的中斷服務程序就可以了,大大減小了程序開發(fā)難度,縮短了程序開發(fā)周期。那么中斷既然是函數(shù),就可以由用戶任意的調(diào)用、由用戶任意地編寫。計算機內(nèi)存的前!024個字節(jié)(偏移量00000H到003FFH)保存著256個中斷向量,每個中斷向量占4個字節(jié),前兩個字節(jié)保存著中斷服務程序的入口地址偏移量,后兩個字節(jié)保存著中斷程序的入口段地址,使用時,只要將它們分別調(diào)入寄存器IP及CS中,就可以轉(zhuǎn)入中斷服務程序?qū)崿F(xiàn)中斷調(diào)用。每當中斷發(fā)生時,CPU將中斷號乘以4,在中斷向量表中得到該中斷向量地址,進而獲得IP及CS值,從而轉(zhuǎn)到中斷服務程序的入口地址,調(diào)用中斷。這就是中斷服務程序通過中斷號調(diào)用的基本過程。在計算機啟動的時候,BIOS將基本的中斷填入中斷向量表,當DOS得到系統(tǒng)控制權(quán)后,它又要將一些中斷向量填入表中,還要修改ー部分BIOS的中斷向量。有一部分中斷向量是系統(tǒng)為用戶保留的,如60H到67H號中斷,用戶可以將自己的中斷服務程序?qū)懭脒@些中斷向量中。不僅如此,用戶還可以自己更改和完善系統(tǒng)已有的中斷向量。在C語言中,提供了一種新的函數(shù)類型interrupt,專門用來定義中斷服務程序,比如我們可以寫如下的中斷服務程序:voidinterruptint60()(puts(nThisisanexample");)該中斷的功能就是顯示ー個字符串,為什么不用printf()函數(shù)呢?這就牽涉到DOS的重入問題,后面將作一些介紹。ー個簡單的中斷服務程序?qū)懞昧?如何把它的函數(shù)入口地址填寫到中斷向量表中,以便在產(chǎn)生中斷的時候能轉(zhuǎn)入中斷服務程序去執(zhí)行呢?這里要用到setvect()和getvect()函數(shù)。setvect()有兩個參數(shù):中斷號和函數(shù)的入口地址,其功能是將指定的函數(shù)安裝到指定的中斷向量中,getvect()函數(shù)有一個參數(shù):中斷號,返回值是該中斷的入口地址。在安裝中斷以前,最好用disable()函數(shù)關(guān)閉中斷,以防止在安裝過程中又產(chǎn)生新的中斷而導致程序運行混亂,待安裝完成后,再用enableい函數(shù)開放中斷,使程序正常運行。現(xiàn)在我們可以把上面的例子再豐富一下,該程序的執(zhí)行結(jié)果是在屏幕上顯示"PengchengSoftware”。#include<dos.h>#include<stdio.h>#ifdef_cplusplus#define_ARGU...#else#define_ARGU#endifvoidinterruptint60(_ARGU)/?中斷服務函數(shù)?/(puts("PengchengSoftware");)voidinstall(voidinterrupt(*fadd)(_ARGU),intnum)/?安裝中斷?/(disable。;/*關(guān)閉中斷?/setvect(num,fadd);/?設置中斷?/enable。;/?開放中斷?/1voidmain。(install(int60,0x60);/?將int60函數(shù)安裝到0x60中斷?/geninterrupt(0x60);/?人為產(chǎn)生0x60號中斷?/}小結(jié)在這ー講中我們首先了解了中斷的概念,然后講了int86。和intdos。函數(shù)的用法,然后再講了中斷服務程序的編寫。本講的內(nèi)容比較枯燥,但是還請大家好好學習一下,這是很有用的。第七講程序內(nèi)存駐留程序(TSR)這ー講是上一講的延續(xù)。在C語言中,可以用keep()函數(shù)將程序駐留內(nèi)存。這個函數(shù)有兩個參數(shù):status和size。size為駐留內(nèi)存長度,可以用size=_SS+_SP/16-_psp得到,當然這也是一種估算的方法,并不是精確值。函數(shù)執(zhí)行完以后,出口狀態(tài)信息保存在status中。比如,對于上面的例子,將wgeninterrupt(0x60);"改寫成“keep(0,_SS+_SP/16-_psp);”后再執(zhí)行程序,這一段程序就被駐留,此后在其它的任何軟件或程序設計中,只要用到了60H號中斷,就會在屏幕上顯示“Thisisanexample!"的字樣。要恢復系統(tǒng)對60H號中斷的定義,只能重新啟動計算機。像上一講的例子其實還很不完善,它沒有考慮DOS系統(tǒng)環(huán)境的狀態(tài)、沒有考慮程序是否已經(jīng)駐留內(nèi)存、沒有考慮退出內(nèi)存駐留等問題。對于第二個問題還是很容易解決的:執(zhí)行程序?開始就讀取某ー函數(shù)中斷入口地址(如63H號中斷)判斷是否為空(NULL),如果為空就先將該地址置為非空再駐留內(nèi)存,若為非空則表示已經(jīng)駐留并退出程序。這ー步判斷非常重要,否則將會因為重復駐留占用過多內(nèi)存空間而最后造成系統(tǒng)崩潰。至于其它兩個問題,在此不多作說明,有興趣的讀者可以參考一些有關(guān)書籍。不僅如此,我們還可以通過在DOS下使用熱鍵(Hotkey)來調(diào)用內(nèi)存駐留程序。比如將《希望漢字系統(tǒng)》自帶的《希望詞典》駐留內(nèi)存后,在任意時刻按下Ctrl+Fll鍵,就能激活程序,出現(xiàn)詞典界面。微機的鍵盤中有一個微處理芯片,用來掃描和檢測每個按鍵的按下和釋放狀態(tài)。大多數(shù)按鍵都有一個掃描碼,告知CPU當前的狀態(tài),但ー些特殊的鍵如PrintScreen,Ctrl+Break等不會產(chǎn)生掃描碼,而直接產(chǎn)生中斷。正因為如此,我們可以將Ctrl+Break產(chǎn)生的中斷號指向我們自己寫好的程序入口地址,那么當按下Ctrl+Break后,系統(tǒng)就會調(diào)用我們自己的程序去執(zhí)行,這實際上也就是修改了Ctrl+Break的中斷向量。至于其它按鍵激活程序則可以利用9H號鍵盤中斷捕獲的掃描碼來實現(xiàn),在此不多作說明。例如,執(zhí)行下面的程序后,退回DOS系統(tǒng),在任意的時候按下Ctrl+Break后,屏幕的底色就會變成紅色。#include<dos.h>#include<conio.h>#ifdef_cplusplus#define_ARGU...#else#deflne_ARGU#endifvoidinterruptnewint(_ARGU);/?函數(shù)聲明?/voidinstall(voidinterrupt(*fadd)(_ARGU),intnum);intmain()(install(newint,0xlb);/*Ctrl+Break中斷號:1BH*/keep(O,_SS+(_SP/16)-_psp);/?駐留程序?/return0;1voidinterruptnewint(_ARGU)(textbackground(4);/?設置屏幕底色為紅色?/clrscr();/?清除屏幕?/}voidinstall(voidinterrupt(*fadd)(—ARGU),intnum)(disable();setvect(num,fadd);/*設置中斷?/enable();)由于!3H號中斷是BIOS提供的磁盤中斷服務程序,對于DOS下的應用程序,它們的存盤、讀盤功能都是通過調(diào)用這一中斷來實現(xiàn)的。有許多DOS下的病毒就喜歡修改I3H號中斷來破壞系統(tǒng),例如,修改13H號中斷服務程序,將其改成:voidinterruptnew13(—ARGU)(if(病毒發(fā)作條件成熟){修改入口參數(shù)指向病毒程序入口地址;執(zhí)行病毒代碼;}調(diào)用原來的I3H中斷;}只要當任一軟件(如EDIT.COM等)對磁盤有操作并且病毒發(fā)作條件成熟時,病毒就被激活。當然,這樣做會導致可用內(nèi)存空間減少,容易被用戶發(fā)現(xiàn)。ー些“聰明”的病毒又會去修改其它的中斷向量,使得系統(tǒng)報告的內(nèi)存大小和實際相符合。還有的病毒,當發(fā)現(xiàn)用戶通過ー些程序(如DEBUG.COM等)去跟蹤它時,它會悄悄地溜掉,其基本原理仍然與修改中斷有關(guān)。硬盤的〇面。柱1扇區(qū)(Side0Cylinder0Sector1)保存著重要的引導信息,一旦破壞,計算機將無法識別硬盤。我們可以寫ー個程序來防止任何軟件(包括病毒)對這ー?扇區(qū)執(zhí)行“寫”操作,一定程度上實現(xiàn)了“寫保護”的作用,它的基本原理就是修改13H號中斷向量并常駐內(nèi)存,監(jiān)視著軟件(包括病毒)對磁盤操作的每ー個細節(jié)。讀者請注意:本程序沒有考慮內(nèi)存駐留的退出,如果想恢復13H號中斷,請重新啟動計算機。#include<dos.h>#include<stdio.h>#include<stdlib.h>#defineSTSIZE8192#definePSP_ENV_PSP0x2c#definePARA(x)((FP_OFF(x)+15)?4)typedefstruct{unsignedbp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags;}INTERRUPT_PARAMETER;voidinstall(voidinterrupt(*faddress)(),intnum);voidinterruptnew13(INTERRUPT_PARAMETERp);intmain()unionREGSregs;structSREGSsregs;unsignedmem;unsignedfar"pointer;charfar"stack;printf(n\n?MasterBootSectorProtector?version1.0\n\nn);if((stack=malloc(STSIZE))==NULL)(printf(nNotenoughMemory!\nu);exit(l);)if(getvect(0x62)!=NULL)(printf("AlreadyInstalled!\nn);exit(l);)install(getvect(Ox13),0x62);install(new13,0x13);pointer=MK_FP(_psp,PSP_ENV_PSP);freemem(*pointer);segread(&sregs);mem=sregs.ds+PARA(stack)-_psp;setblock(_psp,mem);keep(0,mem);return0;}voidinstall(voidinterrupt("faddress)(),intnum)(disable();setvect(num,faddress);enable();)voidinterruptnew13(INTERRUPT_PARAMETERp){'p.ax=_AX;p.cx=_CX;p.dx=_DX;if(_AH==0x03&&_CH==0&&_CL==0x01&&_DH=0&&_DL==0x80)return;enable();geninterrupt(0x62);disable();_AX=p.ax;_CX=p.cx;_DX=p.dx;return;}說明:在使用本程序以前,請:①用殺毒軟件對計算機引導扇區(qū)、內(nèi)存和所有文件進行一次全面的掃描,確信計算機中沒有任何病毒;②有計算機匯編語言基礎的讀者可以自己寫ー個新的引導程序,先將本程序駐留內(nèi)存,再調(diào)用原來的引導程序,以便在病毒還沒有取得系統(tǒng)控制權(quán)以前開啟防護功能。最后簡要說明下DOS系統(tǒng)重入問題。DOS是單用戶單任務操作系統(tǒng)。如果程序在執(zhí)行的過程中被打斷,就有可能因為破壞了原來的程序運行環(huán)境而造成運行不正常,這是災難性的。當中斷產(chǎn)生后,CPU立即中止當前的程序去執(zhí)行中斷服務程序,如果在中斷服務程序中又有對DOS中斷的調(diào)用(如DOS的21H號中斷)時,這樣必定會重寫環(huán)境全局變量(例如PSP程序段前綴就會被改成正在執(zhí)行的中斷程序的PSP),這樣原來的環(huán)境被破壞,原來的程序也就無法正確執(zhí)行。當中斷調(diào)用完成并返回后,用戶得到的結(jié)果是出乎意料的。所以在編寫中斷服務程序時應該避免DOS系統(tǒng)功能調(diào)用,在C語言的中斷服務程序中不應該出現(xiàn)malloc()>printf()>sprintf()等函數(shù)。第九講底層編程技術(shù)學習目標?掌握輸入輸出函數(shù)的調(diào)用?了解揚聲器發(fā)生端口的原理I/O接口的輸入輸出函數(shù)接口輸入函數(shù)intinport(intprotid)intinprotb(intprotid)import函數(shù)從指定的接口地址protid中讀入?"個字(即16位ニ進制),而inportb則從指定的接口地址portid中讀入ー個字節(jié)(8位二進制數(shù))。接口輸出函數(shù)intoutport(intportid,intvalue)intoutportb(intportid,unsignedcharvalue)outport函數(shù)把ー個6位二進制數(shù)value發(fā)送到口地址為portid的接口中去,而outportb函數(shù)是將一個字節(jié)的數(shù)value送到ロ地址為portid的接口中去。定時與計數(shù)器8253端口應用舉例在PC機的系統(tǒng)板上裝有定時與計數(shù)器8253芯片,還有8255可編程并行接口芯片,由它們組成的硬件電路可用來產(chǎn)生PC機內(nèi)揚聲器的聲音,對于286、386、486、586等PC微機,由于采用了超大規(guī)模集成電路,因而看不到這些芯片,它們均集成在外圍電路芯片上了。當我們操作計算機時常常聽到的發(fā)聲,就是由軟件控制這些電路而產(chǎn)生的,聲音的長短,音調(diào)的高低,均可由程序進行控制。下面就是ー個揚聲器發(fā)音程序。系統(tǒng)板上控制發(fā)音的電路可用下圖表示,其中定時器電路產(chǎn)生一個方波信號,其頻率可以編程設定,它決定了揚聲器發(fā)音的頻率,當對定時器電路進行頻率設定時,首先要對其命令寄存器(ロ地址為0x43)寫命令字,如寫入0x66,則表示選擇該定時器的第二個通道,計數(shù)頻率先送低8位(二進制),后送高8位,這是用outportb(〇x43,0x66);實現(xiàn)的。接著用口地址0x42送頻率計數(shù)值,先送低8位,后送高8位,即用outportb(Ox42,count.c[OD outportb(Ox42,count.c[ll)來實現(xiàn)。該計數(shù)值減為一半時,定時器產(chǎn)生低電平,減為。時,又變高電平并又重新開始減計數(shù)(仍從最初設置的計數(shù)值開始),因而產(chǎn)生一系統(tǒng)方波信號,此信號是否能推動揚聲器發(fā)音,還要看由8255產(chǎn)生的門控信號和送數(shù)信號是否為k它們也可編程。其口地址為0x61,當8255的PBロ的。位和1位為1時,就表示允許發(fā)聲。為。時,便禁止發(fā)聲。因而用。utportb(0x61,bitsl3)來允許發(fā)聲,而用outportb(Ox61,bits&Oxfc)來禁止發(fā)聲,且不改變8255其他位原來的值。程序中由rand()函數(shù)產(chǎn)生一個〇?32767之間的偽隨機整數(shù),為使聲音柔和,在freq<=1000時,退出循環(huán)而調(diào)用sound發(fā)聲函數(shù),該函數(shù)由傳來的參數(shù)freq來設定嬴率計數(shù)值,count.divisor為119328/freq(占用兩個地址單元),由于采用了聯(lián)合結(jié)構(gòu),共用一個存儲區(qū),故聯(lián)合結(jié)構(gòu)成員countc[O]和countc[l]取該數(shù)的低8位和高8位(二進制位),用于初始化定時器的頻率計數(shù)值。運行該程序后,將會聽到美妙的聲音,當要停止時,可按任意鍵而由kbhit()函數(shù)控制do循環(huán)結(jié)束來實現(xiàn)。由于PC機主頻不同,發(fā)聲效果將不同,選取freq>1000,將會在386PC機上(主頻為33M)發(fā)出清脆的聲音,當主頻小于33M時,可適當增加此數(shù)。voidsound(unsignedintfreq);main()(unsignedintfreq;do(do(freq=rand();}while(freq>1000);sound(freq);}while(!kbhit());)voidsound(unsignedintfreq){unsignedi;unio
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- YY/T 1946-2024腫瘤組織基因突變檢測試劑盒(高通量測序法)
- 農(nóng)副產(chǎn)品購銷示范合同
- 簽訂的門面租賃合同條款解析
- 建筑項目施工合同管理人員聘用合同
- 炒股合作經(jīng)典合同案例
- 車輛采購合同細則
- 國際物流服務合同專業(yè)版詳解
- 農(nóng)村土地流轉(zhuǎn)授權(quán)合同書
- 城市房屋拆遷補償安置標準合同樣本
- 鋼材買賣合同(示范文本GF-0155)
- 骶髂關(guān)節(jié)損傷郭倩課件
- 內(nèi)科學疾病概要-支氣管擴張課件
- 2025陜西渭南光明電力集團限公司招聘39人易考易錯模擬試題(共500題)試卷后附參考答案
- 預防感冒和流感的方法
- 2024年黑龍江職業(yè)學院高職單招語文歷年參考題庫含答案解析
- 2024年南京旅游職業(yè)學院高職單招語文歷年參考題庫含答案解析
- 股指期貨基礎知識介紹培訓課件
- 2024年北京東城社區(qū)工作者招聘筆試真題
- xx學校培訓部工作職責
- T-GXAR 005-2024 制冷機房運行維護規(guī)程
- 開工第一課安全培訓總結(jié)精彩
評論
0/150
提交評論