版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、567第11章 數(shù)學(xué)協(xié)處理器第11章 數(shù)學(xué)協(xié)處理器內(nèi)核目錄kernel/math目錄中包含數(shù)學(xué)協(xié)處理器仿真處理代碼文件,共包含9個C語言程序,見表11-1。本章內(nèi)容與具體硬件結(jié)構(gòu)關(guān)系非常密切,因此需要讀者具備較深的有關(guān)Intel CPU和協(xié)處理器指令代碼結(jié)構(gòu)的知識。但好在這些內(nèi)容與內(nèi)核實現(xiàn)關(guān)系不大,因此跳過本章內(nèi)容并不會妨礙讀者對內(nèi)核實現(xiàn)方法的完整理解。不過若能理解本章內(nèi)容,那么對于實現(xiàn)系統(tǒng)級應(yīng)用程序(例如匯編和反匯編等程序)和編制協(xié)處理器浮點處理程序?qū)⒂泻艽髱椭?。?1-1 linux/kernel/math目錄名稱大小/B最后修改時間名稱大小/B最后修改時間 Makefile 337719
2、91-12-31 12:26:48 ea.c 1807 1991-12-31 11:57:05 add.c 1999 1992-01-01 16:42:02 error.c 234 1991-12-28 12:42:09 compare.c 904 1992-01-01 17:15:34 get_put.c 5145 1992-01-01 01:38:13 convert.c 4348 1992-01-01 19:07:43 math_emulate.c 11540 1992-01-07 21:12:05 div.c 2099 1992-01-01 01:41:43 mul.c 1517 19
3、92-01-01 01:42:33 11.1 總體功能描述在計算機上執(zhí)行計算量較大的運算通常可以使用三種方法來完成。一種是直接使用CPU普通指令執(zhí)行計算。由于CPU指令是一類通用指令,因此使用這些指令進行復(fù)雜和大量的運算工作需要編制復(fù)雜的計算子程序,并且一般只有通曉數(shù)學(xué)和計算機的專業(yè)人員才能編制出這些子程序。另一種方法是為CPU配置一個數(shù)學(xué)協(xié)處理器芯片。使用協(xié)處理器芯片可以極大地簡化數(shù)學(xué)處理編程難度,并且運算速度和效率也會成倍提高,但需要另外增加硬件投入。還有一種方法是在系統(tǒng)內(nèi)核級使用仿真程序來模擬協(xié)處理器的運算功能。這種方法可能是運算速度和效率最低的一種,但與使用了協(xié)處理器一樣可以方便程序員
4、編制計算程序,并且能夠在對程序不加任何改動的情況下把所編程序運行在具有協(xié)處理器的機器上。在Linux 0.1x甚至Linux 0.9x內(nèi)核開發(fā)初期,數(shù)學(xué)協(xié)處理器芯片80387(或其兼容芯片)價格不菲,并且一直是普通PC中的奢侈品。因此除非在科學(xué)計算量很大的場合或特別需要之處,一般PC中不會安裝80387芯片。雖然現(xiàn)在的Intel 處理器中都內(nèi)置了數(shù)學(xué)協(xié)處理器功能部件,從而現(xiàn)在的操作系統(tǒng)中已經(jīng)無須包含協(xié)處理器仿真程序代碼,但是因為80387仿真程序完全建立在模擬80387芯片處理結(jié)構(gòu)和分析指令代碼結(jié)構(gòu)基礎(chǔ)上,因此學(xué)習(xí)本章內(nèi)容后讀者不僅能夠了解80387協(xié)處理器編程方法,而且對編寫匯編和反匯編處理
5、程序也有很大幫助。如果80386 PC中沒有包括80387數(shù)學(xué)協(xié)處理器芯片,那么當(dāng)CPU執(zhí)行到一條協(xié)處理器指令時就會引發(fā)“設(shè)備不存在”異常中斷7。該異常過程的處理代碼在sys_call.s第158行開始處。如果操作系統(tǒng)在初始化時已經(jīng)設(shè)置了CPU控制寄存器CR0的EM位,那么此時就會調(diào)用math_emulate.c程序中的math_emulate()函數(shù)來用軟件“解釋”執(zhí)行每一條協(xié)處理器指令。Linux 0.12內(nèi)核中的數(shù)學(xué)協(xié)處理器仿真程序math_emulate.c完全模擬了80387芯片執(zhí)行協(xié)處理器指令的方式。在處理一條協(xié)處理器指令之前,該程序會首先使用數(shù)據(jù)結(jié)構(gòu)等類型在內(nèi)存中建立起一個“軟”
6、80387環(huán)境,包括模仿所有80387內(nèi)部棧式累加器組ST、控制字寄存器CWD、狀態(tài)字寄存器SWD和特征字TWD(TAG word)寄存器,然后分析引起異常的當(dāng)前協(xié)處理器指令操作碼,并根據(jù)具體操作碼執(zhí)行相應(yīng)的數(shù)學(xué)模擬運算。因此在描述math_emulate.c程序的處理過程之前,有必要先介紹一下80387的內(nèi)部結(jié)構(gòu)和基本工作原理。11.1.1 浮點數(shù)據(jù)類型本節(jié)主要介紹協(xié)處理器使用的浮點數(shù)據(jù)類型。首先簡單回顧一下整型數(shù)的幾種表示方式,然后說明浮點數(shù)的幾種標(biāo)準(zhǔn)表示方式以及在80387中運算時使用的臨時實數(shù)表示方法。1整型數(shù)據(jù)類型對于Intel 32位CPU來講,有三種基本無符號數(shù)據(jù)類型:字節(jié)(by
7、te)、字(word)和雙字(double word),分別有8、16和32位。無符號數(shù)的表示方式很簡單,字節(jié)中的每個位都代表一個二進制數(shù),并且根據(jù)其所處位置具有不同的權(quán)值。例如一個無符號二進制數(shù)0b10001011可表示為:U = 0b10001011 = 127 + 026 + 025 + 024 + 123 + 022 + 121 + 120 = 139它對應(yīng)十進制數(shù)139。其中權(quán)值最小的一位(20)通常被稱為最低有效位(LSB,Least Significant Bit),而權(quán)值最大的位(27)被稱為最高有效位(MSB,Most Significant Bit)。而計算機中具有負數(shù)值的
8、整型數(shù)據(jù)表示方法通常也有三種:2的補碼(Twos complement)、符號數(shù)(Sign magnitude)和偏置數(shù)(biased number)表示方式。表11-2給出了這三種形式表示的一些數(shù)值。表11-2 整型數(shù)的幾種表示形式十進制數(shù)2的補碼表示法偏置表示法(127)符號數(shù)表示法128無法表示0b11111111無法表示1270b011111110b111111100b011111111260b011111100b111111010b0111111020b000000100b100000010b0000001010b000000010b100000000b0000000100b0000
9、00000b011111110b00000000-0無法表示無法表示0b10000000-10b111111110b011111100b10000001-20b111111100b011111010b10000010-1260b100000100b000000010b11111110-1270b100000010b000000000b11111111-1280b10000000無法表示無法表示2的補碼(二進制補碼)表示法是目前大多數(shù)計算機CPU使用的整數(shù)表示方法,因為CPU的無符號數(shù)的簡單加法也適用于這種格式的數(shù)據(jù)運算。使用這種表示法,一個數(shù)的負數(shù)就是該數(shù)每位取反后再加1。MSB位就是該數(shù)的符
10、號位。MSB= 0表示一個正數(shù);MSB = 1表示負數(shù)。80386 CPU具有8位(1字節(jié))、16位(1字)和32位(雙字)2的補碼數(shù)據(jù)類型,分別可以表示的數(shù)據(jù)范圍是:-128127、-3276832767、-21474836482146473647。另外,在80387仿真程序中使用了一種稱為臨時整數(shù)類型的格式,如圖11-1所示。它的長度為10字節(jié),可表示64位整型數(shù)據(jù)類型。其中低8字節(jié)最大可表示63位無符號數(shù),而最高2字節(jié)僅使用了最高有效位來表示數(shù)值的正負。對于32位整型值則使用低4字節(jié)來表示,16位整型值則使用低2字節(jié)表示。數(shù)的偏置表示法通常用于表示浮點數(shù)格式中的指數(shù)字段值。把一個數(shù)加上指
11、定的偏置值就是該數(shù)的偏置數(shù)表示的值。從表11-1可以看出,這種表示方法的數(shù)值具有無符號數(shù)的大小順序。因此這種表示方法易于比較數(shù)值大小。即大數(shù)值的偏置表示值總是無符號值的一個大數(shù),而其他兩種表示方式卻并非如此。符號數(shù)表示法有一個位專門用于表示符號(0表示正數(shù),1表示負數(shù)),而其他位則與無符號整數(shù)表示的數(shù)值相同。浮點數(shù)的有效數(shù)(尾數(shù))部分使用的就是這種表示方法,而符號位代表整個浮點數(shù)的正負符號。圖11-1 仿真程序支持的臨時整數(shù)格式2BCD碼數(shù)據(jù)類型BCD(Binary Coded Decimal)碼數(shù)值是二進制編碼的十進制數(shù)值,對于壓縮的BCD編碼,每個字節(jié)可表示兩位十進制數(shù),其中每4位表示一位
12、09的數(shù)。例如,十進制數(shù)59的壓縮BCD碼表示是0x01011001。對于非壓縮的BCD碼,每個字節(jié)只使用低4位表示1位十進制數(shù)。80387協(xié)處理器支持10字節(jié)壓縮BCD碼的表示和運算,可表示18位十進制數(shù),如圖11-2所示。與臨時整數(shù)格式類似,其中最高字節(jié)僅使用了符號位(最高有效位)來表示數(shù)值的正負,其余位均不用。若BCD碼數(shù)據(jù)是負數(shù),則會使用最高地址處1字節(jié)的最高有效位置1來表示負值。否則最高字節(jié)所有位均是0。圖11-2 80387支持的BCD碼數(shù)據(jù)類型3浮點數(shù)據(jù)類型具有整數(shù)部分和小數(shù)(尾數(shù))部分的數(shù)稱為實數(shù)或浮點數(shù)。實際上整型數(shù)是小數(shù)部分為0的實數(shù),是實數(shù)集的一個子集。由于計算機使用固定
13、長度位來表示一個數(shù),因此并不能精確地表示所有實數(shù)。由于計算機表示實數(shù)時為了在固定長度位內(nèi)能表示盡量精確的實數(shù)值,分配給表示小數(shù)部分的位個數(shù)并不是固定的,即小數(shù)點是可以“浮動”的,因此計算機表示的實數(shù)數(shù)據(jù)類型也稱為浮點數(shù)。為了便于程序移植,目前計算機中都使用IEEE標(biāo)準(zhǔn)754指定的浮點數(shù)表示方式來表示實數(shù)。這種實數(shù)表示方式的一般格式如圖11-3所示。它由有效數(shù)(Significant)部分、指數(shù)(Exponent)部分和符號位(Sign)組成。80387協(xié)處理器支持三種實數(shù)類型,它們每個部分使用的位數(shù)如圖11-4所示。圖11-3 浮點數(shù)一般格式圖11-4 80387協(xié)處理器使用的實數(shù)格式其中S是
14、一個位的符號位。S=1表示是負實數(shù);S=0表示是正實數(shù)。有效數(shù)(Significant)給出了實數(shù)數(shù)值的有效位數(shù)或尾數(shù)。當(dāng)使用指數(shù)時,一個實數(shù)可以表示成多種形式。例如十進制數(shù)字10.34可以表示成1034.010-2、10.34100、1.034101或0.1034102等。為了使計算能夠得到最大精度值,我們總是對實數(shù)進行規(guī)格化(Normalize)處理,即調(diào)整實數(shù)的指數(shù)值,使得二進制最高有效數(shù)值總是1,并且小數(shù)點就位于其右側(cè)。因此,上述例子正確的規(guī)格化處理結(jié)果就是1.034101。對于二進制數(shù)來說就是1.XXXXX2N(其中X是1或0)。如果我們總是使用這種形式來表示一個實數(shù),那么小數(shù)點左邊
15、肯定是1。所以在80387的短實數(shù)(單精度)和長實數(shù)(雙精度)格式中,這個“1”就沒有必要明確地表示出來。因此在短實數(shù)或長實數(shù)的二進制有效數(shù)中,0x0111.010實際上就是0x1.0111.010。格式中的指數(shù)字段含有把一個數(shù)表示成規(guī)格化形式時所需要的2的冪次值。正如前面提到的,為了便于數(shù)字大小的比較,80387使用偏置數(shù)形式來存儲指數(shù)值。短實數(shù)、長實數(shù)和臨時實數(shù)的偏置基量分別是127、1023和16383。因此一個短實數(shù)指數(shù)值0b10000000實際表示21(0b01111111 + 0b00000001)。另外,臨時實數(shù)是80387內(nèi)部運算時表示數(shù)的格式。它的最高有效數(shù)1被明確地放置在位
16、63處,并且無論你給出的數(shù)是什么數(shù)據(jù)類型的(例如,整型數(shù)、短實數(shù)或BCD碼數(shù)等),80387都會把它轉(zhuǎn)換成臨時實數(shù)格式。80387這樣做的目的是為了使得精度最大化并且盡量減少運算過程中的溢出異常。顯式地把1表示出來是因為80387在運算過程中確實需要該位(用于表示極小的數(shù)值)。當(dāng)輸入到80387中的短型或長型實數(shù)被轉(zhuǎn)換成臨時實數(shù)格式時,就會明確地在位63處放置一個1。4特殊實數(shù)與上面表中格式某些值無法表示的情況類似,使用實數(shù)格式表示的某些值也有其特殊含義。對于80位長度格式的臨時實數(shù),80387并沒有使用其可表示的所有范圍數(shù)值。表11-3是80387使用中的臨時實數(shù)所能表示的所有可能的數(shù)值,其
17、中有效數(shù)一欄虛線左側(cè)1位表示臨時實數(shù)位63,即明確表示數(shù)值1的位。短實數(shù)和長實數(shù)沒有此位,因此也沒有表中的偽非規(guī)格化類別。下面說明其中的一些特殊值:零值、無窮值、非規(guī)格化值、偽非規(guī)格化值以及信號NaN(Not a Number)和安靜NaN。表11-3 80387臨時實數(shù)所能表示的數(shù)值類型和范圍負號偏置型指數(shù)有效數(shù)類別0/111.11111.11安靜NaNs QNaNs(Quiet NaNs)0/111.111.0/111.11110.00不確定值(Indefinite)0/111.11101.11信號NaNs SNaNs(Signalling NaNs)0/111.111.0/111.111
18、00.010/111.11100.00無窮數(shù)(Infinite)0/111.10111.11規(guī)格化數(shù)(正常數(shù))(Normals)0/1.1.0/100.01100.000/100.00111.11偽非規(guī)格化數(shù)(Pseudo-Denormals)0/100.001.0/100.00100.000/100.00011.11非規(guī)格化數(shù)(Denormals)0/100.000.0/100.00000.010/100.00000.00零(Zero)零是指數(shù)和有效數(shù)均為0的值,其余指數(shù)為0的值作保留,即指數(shù)是0的值不能表示一個正常實數(shù)值。無窮值是指數(shù)值為全1、有效數(shù)值為全零的值,而且指數(shù)值為0x11.11
19、的所有其余值也作保留使用。非規(guī)格化數(shù)(Denormals)是一種用于表示非常小數(shù)值的特殊類值。它可以表示漸進下溢或漸進精度丟失情況。通常要求數(shù)值表示成規(guī)格化數(shù)(左移直到有效數(shù)的最高有效位是位1)。然而非規(guī)格化數(shù)的有效數(shù)最高有效位不是1。此時偏置型指數(shù)0x00.00分別是值為2-126、2-1022、2-16382的短實數(shù)、長實數(shù)和臨時實數(shù)指數(shù)值的特殊表示方式。這種表示比較特殊,因為偏置型指數(shù)0x00.01對三種實數(shù)類型也分別表示相同的指數(shù)值2-126、2-1022、2-16382。偽非規(guī)格化類數(shù)值(Pseudo-denormals)是有效數(shù)最高有效位為1的值,而非規(guī)格化類數(shù)值的該位是0。偽非規(guī)
20、格化數(shù)很少見,它們可以用規(guī)格化類數(shù)來表示卻沒有這么做。因為上面已經(jīng)說明特殊的偏置指數(shù)0x00.00與規(guī)格化數(shù)的指數(shù)0x00.01具有相同的值。因此偽非規(guī)格化類數(shù)可以表示成規(guī)格化類數(shù)值。另一種特殊情況是NaN。NaN是指“不是一個數(shù)”(Not a Number)。NaN有兩種形式:會產(chǎn)生信號(Signaling)的和不會產(chǎn)生信號的或稱為安靜的(Quiet)。當(dāng)一個產(chǎn)生信號的NaN(SNaN)被用于操作時就會引發(fā)一個無效操作異常,而一個安靜的NaN(QNaN)則不會。SnaN是一類會引發(fā)無效操作異常的數(shù)值。使用的方法就是程序先把變量都初始化為SNaN值,在實際使用這個變量時還需要對其進行真正的賦值
21、。這樣若操作過程中使用了一個未被初始化的值就會引發(fā)異常。當(dāng)然,NaN類數(shù)值也可以用來存儲其他信息。80387自身不會產(chǎn)生SNaN類的值,但會產(chǎn)生QNaN類的值。當(dāng)發(fā)生無效操作異常時80387就會產(chǎn)生一個QNaN類值,并且操作的結(jié)果將是不確定值(Indefinite)。不確定值是一種特殊的QNaN類值。每種數(shù)據(jù)類型都有一個表示不確定值的數(shù)。對于整型數(shù)則是用其最大負數(shù)來表示其不確定值。另外還有一些80387不支持的臨時實數(shù)值,即那些沒有在上表中列出的數(shù)值范圍。若80387遇到這些數(shù)值,就會引發(fā)無效操作異常。11.1.2 數(shù)學(xué)協(xié)處理器功能和結(jié)構(gòu)80386雖然是一個通用微處理器,但其指令并不是非常適用
22、于數(shù)學(xué)計算。因此若使用80386來執(zhí)行數(shù)學(xué)計算,那么就需要編制非常復(fù)雜的程序,而且執(zhí)行效率也相對較低。80387作為80386的輔助處理芯片,極大地擴展了程序員的編程范圍。以前程序員不太可能做到的事,使用協(xié)處理器后就可以很容易地,并且快速而精確地完成。80387具有一組特別的寄存器。這組寄存器可以讓80387直接操作比80386所能處理的大或小幾個數(shù)量級的數(shù)值。80386使用2進制補數(shù)方式表示一個數(shù)。這種方法不適合用來表示小數(shù)。而80387并不使用2的補數(shù)方法來表示數(shù)值,它使用了IEEE標(biāo)準(zhǔn)754規(guī)定的80位(10個字節(jié))格式。這種格式不僅具有廣泛的兼容性,而且能夠使用二進制表示極大(或極?。?/p>
23、的數(shù)值。例如,它能表示大到1.21104932數(shù)值,也能處理小到3.310-4932的數(shù)。80387并不保持固定小數(shù)點的位置,如果數(shù)值小的話就多使用一些小數(shù)位,如果數(shù)值大的話就少用幾位小數(shù)位。因此小數(shù)點的位置是可以“浮動”的。這也是術(shù)語“浮點”數(shù)的由來。為支持浮點運算,80387中包含三組寄存器,如圖11-5所示。 8個80位長的數(shù)據(jù)寄存器(累加器),可用于臨時存放8個浮點操作數(shù),并且這些累加器可以執(zhí)行棧式操作; 3個16位狀態(tài)和控制寄存器:一個狀態(tài)字寄存器SWD、一個控制字寄存器CWD和一個特征(TAG)寄存器; 4個32位出錯指針寄存器(FIP、FCS、FOO和FOS)用于確定導(dǎo)致8038
24、7內(nèi)部異常的指令和內(nèi)存操作數(shù)。圖11-5 80387的寄存器1棧式浮點累加器在浮點指令執(zhí)行過程中,8個80位長度的物理寄存器組被作為棧式累加器使用。雖然每個80位寄存器有固定的物理順序位置(即左邊的07),但當(dāng)前棧頂則由ST(即ST(0))來指明。ST之下的其余累加器使用名稱ST(i)來指明(i = 17)。至于哪個80位物理寄存器是當(dāng)前棧頂ST,則由具體操作過程指定。在狀態(tài)字寄存器中名稱為TOP的3位字段含有當(dāng)前棧頂ST對應(yīng)的80位物理寄存器的絕對位置。一個入棧(Push)操作將會把TOP字段值遞減1,并把新值存儲于新的ST中。在入棧操作之后,原來的ST變成了ST(1),而原來的ST(7)變
25、成了現(xiàn)在的ST。即所有累加器的名稱都從原來的ST(i)變成了ST(i+1)&0x7)。一個出棧(Pop)操作將會讀出當(dāng)前ST對應(yīng)的80位寄存器的值,并且把TOP字段值遞增1。因此在出棧操作之后,原來的ST(即ST(0))變成了ST(7),原來的ST(1)成為新的ST。即所有累加器的名稱都從原來的ST(i)變成ST(i-1)& 0x7)。ST的作用如同一個累加器是因為它被作為所有浮點指令的一個隱含操作數(shù)。若有另一個操作數(shù),那么該第2個操作數(shù)可以是任何其余累加器之一ST(i),或者是一個內(nèi)存操作數(shù)。棧中的每個累加器為一個實數(shù)提供了使用臨時實數(shù)格式存儲的80位空間,其最高位(s)是符號位,位7864
26、是15位的指數(shù)字段,位630是64位的有效數(shù)字段。浮點指令被設(shè)計成能充分利用這個累加器棧模式。浮點加載指令(FLD等)會從內(nèi)存中讀取一個操作數(shù)并壓入棧中,而浮點存儲指令則會從當(dāng)前棧頂取得一個值并寫到內(nèi)存中。若棧中該值不再需要時還可以同時執(zhí)行出棧操作。加和乘之類的操作會把當(dāng)前ST寄存器內(nèi)容作為一個操作數(shù),而另一個取自其他寄存器或內(nèi)存中,并且在計算完后即把結(jié)果保存在ST中。還有一類“操作并彈出”操作形式用于在ST和ST(1)兩者之間進行運算。這種操作形式會執(zhí)行一次彈出操作,然后把結(jié)果放入新的ST中。2狀態(tài)與控制寄存器三個16位的寄存器(TAG字、控制字和狀態(tài)字)控制著浮點指令的操作并且為其提供狀態(tài)
27、信息。它們的具體格式如圖11-6所示。下面逐一對它們進行說明。(1)控制字控制字(Control Word)可用于程序設(shè)置各種處理選項來控制80387的操作。其中可分為三個部分。位1110的RC(Rounding Control)是舍入控制字段,用于對計算結(jié)果進行舍入操作。位98的PC(Precision Control)是精度控制字段,用于在保存到指定存儲單元之前對計算結(jié)果進行精度調(diào)整。所有其他操作使用臨時實數(shù)格式精度,或者使用指令指定的精度。位50是異常屏蔽位,用于控制協(xié)處理器異常處理。這6位對應(yīng)80387可能發(fā)生的6種異常情況。其中每一種異常都可以單獨屏蔽掉。如果發(fā)生某個特定異常并且其對
28、應(yīng)屏蔽位沒有置位,那么80387就會向CPU通報這個異常,并且會讓CPU產(chǎn)生異常中斷int 16。然而如果設(shè)置了對應(yīng)屏蔽位,那么80387就會自己處理并糾正發(fā)生的異常問題而不會通知CPU。這個寄存器隨時可以讀寫,其中各位的具體含義參見圖11-6。(2)狀態(tài)字在運行期間,80387會設(shè)置狀態(tài)字(Status Word)中的位,用于程序檢測特定的條件。當(dāng)發(fā)生異常時,它可讓CPU確定發(fā)生異常的原因。因為所有6個協(xié)處理器異常都會讓CPU產(chǎn)生異常中斷int16。(3)特征字特征字(Tag Word)寄存器含有8個2位的Tag字段,分別對應(yīng)8個物理浮點數(shù)據(jù)寄存器。這些特征字段分別指明相應(yīng)的物理寄存器含有有
29、效、零、特殊浮點數(shù)值,或者是空的。特殊數(shù)值是指那些無限值、非數(shù)值、非規(guī)格化或不支持格式的數(shù)值。特征字段Tag可用于檢測累加器堆棧上下溢出情況。如果入棧(Push)操作遞減TOP指向了一個非空寄存器,就會發(fā)生棧上溢出。如果出棧(Pop)操作企圖去讀取或彈出空寄存器,就會造成棧下溢出(Underflow)。棧的上下溢出都將引發(fā)無效操作異常。圖11-6 控制和狀態(tài)寄存器格式3出錯指針寄存器出錯指針寄存器(Error-Pointer Register)是4個32位的80387寄存器,其中含有80387最后執(zhí)行指令和所用數(shù)據(jù)的指針,參見圖11-6。前兩個寄存器FIP和FCS中是最后執(zhí)行指令中2個操作碼的
30、指針(忽略前綴碼)。FCS是段選擇符和操作碼,F(xiàn)IP是段內(nèi)偏移值。后兩個寄存器FOO和FOS是最后執(zhí)行指令內(nèi)存操作數(shù)的指針。FOS中是段選擇符,F(xiàn)OO中是段內(nèi)偏移值。如果最后執(zhí)行的協(xié)處理器指令不含內(nèi)存操作數(shù),則后兩個寄存器值無用。指令FLDENV、FSTENV、FNSTENV、FRSTOR、FSAVE和FNSAVE用于加載和保存這4個寄存器的內(nèi)容。前3條指令共加載或保存28字節(jié)內(nèi)容:控制字、狀態(tài)字和特征字以及4個出錯指針寄存器??刂谱帧顟B(tài)字和特征字都以32位操作,高16位為0。后3條指令用于加載或保存協(xié)處理器所有108字節(jié)的寄存器內(nèi)容。4浮點指令格式對協(xié)處理器進行仿真就是解析具體的浮點指令操
31、作碼和操作數(shù),根據(jù)每一條指令的結(jié)構(gòu)使用80386的普通指令來執(zhí)行相應(yīng)的仿真操作。數(shù)學(xué)協(xié)處理器80387共有七十多條指令,共分5類,見表11-4。每條指令的操作碼都有2個字節(jié),其中第一個字節(jié)高5位都是二進制11011。這5位的數(shù)值(0x1b或十進制27)正好是字符ESC(轉(zhuǎn)義)的ASCII代碼值,因此所有數(shù)學(xué)協(xié)處理器指令都被形象地稱為ESC轉(zhuǎn)義指令。在仿真浮點指令時可忽略相同的ESC位,只要判斷低11位的值即可。表11-4 浮點指令類型第1字節(jié)第2字節(jié)可選字段11 1 0 1 1OPA1MOD1OPBR/MSIBDISP21 1 0 1 1MFOPAMODOPBR/MSIBDISP31 1 0
32、1 1dPOPA11OPBST(i)41 1 0 1 1001111OP51 1 0 1 1011111OP151110987654 3 2 1 0表中各個字段的含義如下(有關(guān)這些字段的具體含義和詳細說明請參考80x86處理器手冊):1)OP(Operation opcode)是指令操作碼,在有些指令中它被分成了OPA和OPB兩部分。2)MF(Memory Format)是內(nèi)存格式。00:32位實數(shù);01:32位整數(shù);10:64位實數(shù);11:64位整數(shù)。3)P(Pop)指明在操作后是否要執(zhí)行一次出棧處理。0:不需要;1:操作后彈出棧。4)d(destination)指明保存操作結(jié)果的累加器。0
33、:ST(0);1:ST(i)。5)MOD(Mode)和R/M(Register/Memory)是操作方式字段和操作數(shù)位置字段。6)SIB(Scale Index Base)和DISP(Displacement)是具有MOD和R/M字段指令的可選后續(xù)字段。另外,所有浮點指令的匯編語言助記符都以字母F開頭,例如:FADD、FLD等。還有如下一些標(biāo)準(zhǔn)表示方法:1)FI所有操作整型數(shù)據(jù)的指令都以FI開頭,例如FIADD、FILD等。2)FB所有操作BCD類型數(shù)據(jù)的指令都以FB開頭,例如FBLD、FBST等。3)FxxP所有會執(zhí)行一次出棧操作的指令均以字母P結(jié)尾,例如FSTP、FADDP等。4)FxxP
34、P所有會執(zhí)行二次出棧操作的指令均以字母PP結(jié)尾,例如FCOMPP、FUCOMPP等。5)FNxx除了以FN開頭的指令,所有指令在執(zhí)行前都會先檢測未屏蔽的運算異常。而以FN開頭的指令不檢測運算異常情況,例如FNINIT、FNSAVE等。11.2 math_emulate.c程序11.2.1 功能描述math_emulate.c程序中的所有函數(shù)可分為3部分:第一類是設(shè)備不存在異常處理程序接口函數(shù)math_emulate(),只有這一個函數(shù);第二類是浮點指令仿真處理主函數(shù)do_emu(),也只有一個函數(shù);另外所有函數(shù)都是仿真運算輔助類函數(shù),包括其余幾個C語言程序中的函數(shù)。在一臺不包含80387協(xié)處理
35、器芯片的PC中,如果內(nèi)核初始化時在CR0中設(shè)置了仿真標(biāo)志EM = 1,那么當(dāng)CPU遇到一條浮點指令時就會引起CPU產(chǎn)生異常中斷int 7,并且在該中斷處理過程中調(diào)用本程序中第476行處的math_emulate(long _false)函數(shù)。在math_emulate()函數(shù)中,若判斷出當(dāng)前進程還沒有使用過仿真的協(xié)處理運算時就會對仿真的80387控制字、狀態(tài)字和特征字(Tag Word)進行初始化操作,設(shè)置控制字中所有6種協(xié)處理器異常屏蔽位并復(fù)位狀態(tài)字和特征字。然后調(diào)用仿真處理主函數(shù)do_emu()。使用的參數(shù)是作為如下info結(jié)構(gòu)的中斷處理過程中調(diào)用math_emulate()函數(shù)的返回地址
36、指針。info結(jié)構(gòu)實際上就是棧中自從CPU產(chǎn)生中斷int7后逐漸入棧的一些數(shù)據(jù)構(gòu)成的一個結(jié)構(gòu),因此它與系統(tǒng)調(diào)用時內(nèi)核棧中數(shù)據(jù)的分布情況基本相同。參見include/linux/math_emu.h文件第11行和kernel/sys_call.s開始部分。 11 struct info 12 long _math_ret; / math_emulate()調(diào)用者(int7)返回地址。 13 long _orig_eip; / 臨時保存原EIP的地方。 14 long _edi; / 異常中斷int7處理過程入棧的寄存器。 15 long _esi; 16 long _ebp; 17 long _
37、sys_call_ret; / 中斷7返回時將去執(zhí)行系統(tǒng)調(diào)用的返回處理代碼。 18 long _eax; / 以下部分(18-30行)與系統(tǒng)調(diào)用時棧中結(jié)構(gòu)相同。 19 long _ebx; 20 long _ecx; 21 long _edx; 22 long _orig_eax; / 如不是系統(tǒng)調(diào)用而是其他中斷時,該值為-1。 23 long _fs; 24 long _es; 25 long _ds; 26 long _eip; / 26 - 30行 由CPU自動入棧。 27 long _cs; 28 long _eflags; 29 long _esp; 30 long _ss; 31
38、;do_emu()函數(shù)(第52行)首先根據(jù)狀態(tài)字來判斷有沒有發(fā)生仿真的協(xié)處理器內(nèi)部異常。若有則設(shè)置狀態(tài)字的忙位B(位15),否則就復(fù)位忙位B。然后從上述info結(jié)構(gòu)中EIP字段處取得產(chǎn)生協(xié)處理器異常的二字節(jié)浮點指令代碼code,并在屏蔽掉每條浮點指令碼中都相同的ESC碼(二進制11011)位部分后,根據(jù)此時的code值對具體的浮點指令進行軟件仿真運算處理。為便于處理,該函數(shù)按5種類型浮點指令碼分別使用了五個switch語句進行處理。例如,第一個switch語句(第75行)用于處理那些不涉及尋址內(nèi)存操作數(shù)的浮點指令。而最后兩個switch語句(第419、432行)則專門用來處理操作數(shù)與內(nèi)存相關(guān)的
39、指令。對于后一種類型的指令,其處理過程的基本流程是首先根據(jù)指令代碼中的尋址模式字節(jié)取得內(nèi)存操作數(shù)的有效地址,然后從該有效地址處讀取相應(yīng)的數(shù)據(jù)(整型數(shù)、實數(shù)或BCD碼數(shù)值)。接著把讀取的值轉(zhuǎn)換成80387內(nèi)部處理使用的臨時實數(shù)格式。在計算完畢后,再把臨時實數(shù)格式的數(shù)值轉(zhuǎn)換為原數(shù)據(jù)類型,最后保存到用戶數(shù)據(jù)區(qū)中。另外,在具體仿真一條浮點指令時,若發(fā)現(xiàn)浮點指令無效,則程序會立刻調(diào)用放棄執(zhí)行函數(shù)_math_abort()。該函數(shù)會向當(dāng)前執(zhí)行進程發(fā)送指定的信號,同時修改棧指針esp指向中斷過程中調(diào)用math_emulate()函數(shù)的返回地址(_math_ret),并立刻返回到中斷處理過程中去。11.2.2
40、 代碼注釋程序11-1 linux/kernel/math/math_emulate.c 1 /* 2 * linux/kernel/math/math_emulate.c 3 * 4 * (C) 1991 Linus Torvalds 5 */ 6 7 /* 8 * Limited emulation 27.12.91 - mostly loads/stores, which gcc wants 9 * even for soft-float, unless you use bruce evans patches. The patches 10 * are great, but they h
41、ave to be re-applied for every version, and the 11 * library is different for soft-float and 80387. So emulation is more 12 * practical, even though its slower. 13 * 14 * 28.12.91 - loads/stores work, even BCD. Ill have to start thinking 15 * about add/sub/mul/div. Urgel. I should find some good sou
42、rce, but Ill 16 * just fake up something. 17 * 18 * 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really 19 * test every possible combination. 20 */ /* * 仿真范圍有限的程序 91.12.27 - 絕大多數(shù)是一些加載/存儲指令。除非你使用了Bruce Evans的 * 補丁程序,否則即使使用軟件執(zhí)行浮點運算,gcc也需要這些指令。Bruce的補丁程序非常好,但每 * 次更換gcc版本你都得用這個補丁程序。而且對于軟
43、件浮點實現(xiàn)和80387,所使用的庫是不同的。因 * 此使用仿真是更為實際的方法,盡管仿真方法更慢。 * * 91.12.28 - 加載/存儲協(xié)處理器指令可以用了,即使是BCD碼的也能使用。我將開始考慮實現(xiàn)add/ * sub/mul/div 指令。唉,我應(yīng)該找一些好的資料,不過現(xiàn)在我會先仿造一些操作。 * * 91.12.30 - add/sub/mul/div/com 這些指令好像大多數(shù)都可以使用了。我真應(yīng)該測試每種指令 * 可能的組合操作。 */ 21 22 /* 23 * This file is full of ugly macros etc: one problem was that
44、 gcc simply 24 * didnt want to make the structures as they should be: it has to try to 25 * align them. Sickening code, but at least Ive hidden the ugly things 26 * in this one file: the other files dont need to know about these things. 27 * 28 * The other files also dont care about ST(x) etc - they
45、 just get addresses 29 * to 80-bit temporary reals, and do with them as they please. I wanted to 30 * hide most of the 387-specific things here. 31 */ /* * 這個程序中到處都是些別扭的宏:問題之一是gcc就是不想把結(jié)構(gòu)建立成其應(yīng)該成為的樣子:gcc 企 * 圖對結(jié)構(gòu)進行對齊處理。真是討厭,不過我起碼已經(jīng)把所有蹩腳的代碼都隱藏在這么一個文件中了: * 其他程序文件不需要了解這些信息。 * * 其他的程序也不需要知道ST(x)等80387內(nèi)部結(jié)構(gòu)
46、 - 它們只需要得到80位臨時實數(shù)的地址就可以 * 隨意操作。我想盡可能在這里隱藏所有387專有信息。 */ 32 33 #include / 信號頭文件。定義信號符號,信號結(jié)構(gòu)及信號操作函數(shù)原型。 34 35 #define _ALIGNED_TEMP_REAL 1 36 #include / 協(xié)處理器頭文件。定義臨時實數(shù)結(jié)構(gòu)和387寄存器操作宏等。 37 #include / 內(nèi)核頭文件。含有一些內(nèi)核常用函數(shù)的原形定義。 38 #include / 段操作頭文件。定義了有關(guān)段寄存器操作的嵌入式匯編函數(shù)。 39 40 #define bswapw(x) _asm_(xchgb %al,%ah
47、:=a (x): (short)x) / 交換2字節(jié)位置。 41 #define ST(x) (*_st(x) / 取仿真的ST(x)累加器值。 42 #define PST(x) (const temp_real *) _st(x)/ 取仿真的ST(x)累加器的指針。 43 44 /* 45 * We dont want these inlined - it gets too messy in the machine-code. 46 */ /* * 我們不想讓這些成為嵌入的語句 - 因為這會使得到的機器碼太混亂。 */ / 以下這些是相同名稱浮點指令的仿真函數(shù)。 47 static voi
48、d fpop(void); 48 static void fpush(void); 49 static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b); 50 static temp_real_unaligned * _st(int i); 51 / 執(zhí)行浮點指令仿真。 / 該函數(shù)首先檢測仿真的I387結(jié)構(gòu)狀態(tài)字寄存器中是否有未屏蔽的異常標(biāo)志置位。若有則對狀態(tài)字中 / 忙標(biāo)志B進行設(shè)置。然后把指令指針保存起來,并取出代碼指針EIP處的2字節(jié)浮點指令代碼code。 / 接著分析代碼code,并根據(jù)其含義進行處理。針對不同代
49、碼類型值,Linus使用了幾個不同的 / switch程序塊進行仿真處理。 / 參數(shù)是info結(jié)構(gòu)的指針。 52 static void do_emu(struct info * info) 53 54 unsigned short code; 55 temp_real tmp; 56 char * address; 57 / 該函數(shù)首先檢測仿真的I387結(jié)構(gòu)狀態(tài)字寄存器中是否有未屏蔽的異常標(biāo)志置位。若有就設(shè)置狀態(tài)字 / 中的忙標(biāo)志B(位15),否則復(fù)位B標(biāo)志。然后我們把指令指針保存起來。再看看執(zhí)行本函數(shù)的代 / 碼是不是用戶代碼。如果不是,即調(diào)用者的代碼段選擇符不等于 0x0f,則說明內(nèi)核中
50、有代碼使用了 / 浮點指令。于是在顯示出浮點指令出的CS、EIP值和信息“內(nèi)核中需要數(shù)學(xué)仿真”后停機。 58 if (I387.cwd & I387.swd & 0x3f) 59 I387.swd |= 0x8000; / 設(shè)置忙標(biāo)志B。 60 else 61 I387.swd &= 0x7fff; / 清忙標(biāo)志B。 62 ORIG_EIP = EIP; / 保存浮點指令指針。 63 /* 0x0007 means user code space */ 64 if (CS != 0x000F) / 不是用戶代碼則停機。 65 printk(math_emulate: %04x:%08xnr,CS,EIP); 66 panic(Math emulation needed in kernel); 67 / 然后我們?nèi)〕龃a指針EIP處的2字節(jié)浮點指令代碼code。由于Intel CPU存儲數(shù)據(jù)時是“小頭” / (Little
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年委托加工合同經(jīng)典版(三篇)
- 2024年工程審計工作總結(jié)參考樣本(三篇)
- 2024年學(xué)校教研活動總結(jié)標(biāo)準(zhǔn)版本(四篇)
- 生 物2024-2025學(xué)年北師大版生物八年級上冊復(fù)習(xí)題(第15-18章)
- 2024年學(xué)校辦公室工作總結(jié)經(jīng)典版(三篇)
- 2024年小學(xué)教師個人工作總結(jié)范本(三篇)
- 2024年四年級班主任計劃范例(四篇)
- 2024年家具制造公司勞動合同(四篇)
- 2024年小挖掘機租賃合同標(biāo)準(zhǔn)范文(二篇)
- 2024年小學(xué)少先隊輔導(dǎo)員學(xué)期工作計劃樣本(三篇)
- 溢流面混凝土施工方案選擇及施工方法淺談
- (完整word版)化學(xué)實驗室儀器藥品清單
- 家庭農(nóng)場項目建設(shè)方案3篇
- 醫(yī)院非產(chǎn)科孕情管理和三病檢測工作方案
- 物品出入庫明細表格
- 柔性制造技術(shù)的五個類型
- 基于stm32的低頻數(shù)字相位測量儀
- 第四章 造紙化學(xué)
- 湖南某糧食倉儲擴建項目拱板屋面施工方案(附示意圖)
- 五年級上冊數(shù)學(xué)應(yīng)用題精選150道
- 蘇泊爾電磁爐線路圖(上)
評論
0/150
提交評論