汇编之浮点数处理(CrackMe003前置知识)
文章目錄
- 浮點(diǎn)數(shù)的二進(jìn)制表示
- IEEE二進(jìn)制浮點(diǎn)數(shù)的表示
- 1.符號(hào)位
- 2.有效數(shù)字
- 3.有效數(shù)字的精度
- 階碼
- 規(guī)格化二進(jìn)制浮點(diǎn)數(shù)
- 新建IEEE表示
- 實(shí)數(shù)編碼
- 單精度數(shù)轉(zhuǎn)換為十進(jìn)制
- 浮點(diǎn)單元
- FPU寄存器棧
- FPU寄存器
- 專用寄存器
- 舍入
- FPU控制字
- 浮點(diǎn)數(shù)異常
- 浮點(diǎn)數(shù)指令集
- 1.初始化(FINIT)
- 2.浮點(diǎn)數(shù)據(jù)類型
- 3.加載浮點(diǎn)數(shù)值 FLD
- FILD
- 加載常數(shù)
- 保存浮點(diǎn)數(shù)值(FST FSTP FIST)
- 算術(shù)運(yùn)算指令
- FCHS和FABS
- FADD FADDP FIADD
- FSUB FSUBP FISUB
- FMUL FMULP FIMUL
- FDIV FDIVP FIDIV
- 比較浮點(diǎn)數(shù)值
- FCOM FCOMP FCOMPP
- 條件碼
- P6處理器的改進(jìn)
- 讀寫浮點(diǎn)數(shù)值
- 異常同步
浮點(diǎn)數(shù)的二進(jìn)制表示
十進(jìn)制浮點(diǎn)數(shù)有三個(gè)部分組成:符號(hào),有效數(shù)字和階碼。比如,在-1.23154*105中,符號(hào)為負(fù),有效數(shù)字為1.23154,階碼為5
IEEE二進(jìn)制浮點(diǎn)數(shù)的表示
x86處理器使用的三種浮點(diǎn)數(shù)二進(jìn)制存儲(chǔ)格式都是由IEEE標(biāo)準(zhǔn)754-1985——二進(jìn)制浮點(diǎn)運(yùn)算一一所制定。下表列出了他們的特點(diǎn)
| 單精度 | 32位:1位符號(hào)位,8位階碼,23位為有效數(shù)字的小數(shù)部分。大致的規(guī)格化范圍:2-126—2127 也被稱為短實(shí)數(shù) |
| 雙精度 | 64位:1位符號(hào)位,11位階碼,52位為有效數(shù)字的小數(shù)部分。大致的規(guī)格化范圍為:2-1022—21023 也被稱為長(zhǎng)實(shí)數(shù) |
| 擴(kuò)展雙精度 | 80位:1位符號(hào)位,15位階碼,1位為整數(shù)部分,63位為有效數(shù)字的小數(shù)部分。大致的規(guī)格化范圍:2-16382—216383 也被稱為擴(kuò)展實(shí)數(shù) |
由于三種格式比較相似,因此本節(jié)將重點(diǎn)關(guān)注單精度格式。
1.符號(hào)位
如果符號(hào)位為1,則該數(shù)為負(fù);如果符號(hào)位為0,則該數(shù)為正。
2.有效數(shù)字
浮點(diǎn)數(shù)的有效數(shù)字由小數(shù)點(diǎn)的左右的十進(jìn)制數(shù)字構(gòu)成。十進(jìn)制的數(shù)123.154用加權(quán)位計(jì)數(shù)法可以表示為下面的累加和形式
123.154=(1x102)+(2x101)+(3x100)+(1x10-1)+(5x10-2)+(4x10-3)
小數(shù)點(diǎn)左邊的數(shù)字的階碼都位正,右邊的數(shù)字階碼都為負(fù)
小數(shù)點(diǎn)右邊數(shù)字還有一種表達(dá)方式,即把他們列為分?jǐn)?shù)之和,其中分母為2的冪,例如:
.1011=1/2+0/4+1/8+1/16=11/16
3.有效數(shù)字的精度
用有限位數(shù)表示的任何浮點(diǎn)數(shù)都無(wú)法表示完整的連續(xù)的實(shí)數(shù)。例如:假設(shè)一個(gè)簡(jiǎn)單的浮點(diǎn)數(shù)格式有5位有效數(shù)字,那么將無(wú)法表示范圍在1.1111-10.000之間的二進(jìn)制數(shù)。比如,二進(jìn)制數(shù)1.11111就需要更精確的有效數(shù)字。將這個(gè)思想擴(kuò)展到IEEE雙精度格式,就會(huì)發(fā)現(xiàn)53位有效數(shù)字無(wú)法表示需要54位或更多二進(jìn)制數(shù)值。
階碼
單精度數(shù)用8位無(wú)符號(hào)整數(shù)存放階碼,引入的偏差為127,因此必須在數(shù)的實(shí)際階碼上再加上127
規(guī)格化二進(jìn)制浮點(diǎn)數(shù)
大多數(shù)二進(jìn)制浮點(diǎn)數(shù)都以規(guī)格化格式存放,以便將有效數(shù)字的精度最大化。給定任意二進(jìn)制浮點(diǎn)數(shù),都可以進(jìn)行規(guī)格化,方法是將小數(shù)點(diǎn)移位,直到小數(shù)點(diǎn)左邊只有一個(gè)1。階碼表示的是二進(jìn)制小數(shù)點(diǎn)向左或向右移動(dòng)的位數(shù)。示例如下:
| 1110.1 | 1.1101x23 |
| 000101 | 1.01x2-4 |
| 1010001 | 1.010001x2-6 |
反規(guī)格化數(shù):規(guī)格化操作的逆操作是將二進(jìn)制浮點(diǎn)數(shù)反規(guī)格化。移動(dòng)二進(jìn)制小數(shù)點(diǎn),直到階碼為0。如果階碼為正數(shù),則將小數(shù)點(diǎn)右移,如果階碼為負(fù)數(shù),則將二進(jìn)制小數(shù)點(diǎn)左移,并在需要的位置前填充導(dǎo)數(shù)0。
新建IEEE表示
實(shí)數(shù)編碼
一旦符號(hào)位 階碼和有效數(shù)字字段完成格式化和編碼后,生成一個(gè)完整的二進(jìn)制IEEE段實(shí)數(shù)就很容易了。首先設(shè)置符號(hào)位,然后是階碼字段,最后是有效數(shù)字部分。例如:下面表示的是二進(jìn)制1.101x20
- 符號(hào)位:0
- 階碼:01111111
- 小數(shù)部分:10100000000000000000000
偏移碼(01111111)是十進(jìn)制數(shù)127的二進(jìn)制形式。所有規(guī)格化有效數(shù)字在二進(jìn)制小數(shù)點(diǎn)的左邊都有個(gè)1,因此,不需要對(duì)這一位進(jìn)行顯示編碼。
? 單精度數(shù)位編碼示例
| -1.11 | 127 | 1 01111111 11000000000000000000000 |
| 1101.101 | 130 | 0 10000010 10110100000000000000000 |
IEEE規(guī)范包含了多鐘實(shí)數(shù)和非數(shù)字編碼
- 正零和負(fù)零
- 非規(guī)格化有限數(shù)
- 規(guī)格化有限數(shù)
- 正無(wú)窮和負(fù)無(wú)窮
- 非數(shù)字
- 不定數(shù)
規(guī)格化和非規(guī)格化: 規(guī)格化有限數(shù)是指所有非零有限值,這些數(shù)能被編碼為零到無(wú)窮之間的規(guī)格化實(shí)數(shù)。盡管看上去全部有限非零浮點(diǎn)數(shù)都應(yīng)被規(guī)格化,但若數(shù)值接近于零,則無(wú)法規(guī)格化,當(dāng)階碼范圍造成的限制使得FPU不能將二進(jìn)制小數(shù)點(diǎn)移動(dòng)到規(guī)格化位置時(shí),就會(huì)發(fā)生這種情況。假設(shè)FPU計(jì)算結(jié)果為1.0101111x2-129,其階碼太小,無(wú)法用單精度數(shù)形式存放。此時(shí)產(chǎn)生一個(gè)下溢異常,數(shù)值則每次將二進(jìn)制小數(shù)點(diǎn)左移一位逐步進(jìn)行非規(guī)格化,直到階碼達(dá)到有效范圍
正無(wú)窮和負(fù)無(wú)窮:正無(wú)窮表示最大正實(shí)數(shù),負(fù)無(wú)窮表示最大負(fù)實(shí)數(shù)。無(wú)窮可以和其他數(shù)值比較。負(fù)無(wú)窮小于正無(wú)窮,負(fù)無(wú)窮小于任意有限實(shí)數(shù)。任一無(wú)窮都可以表示浮點(diǎn)溢出條件。運(yùn)算結(jié)果不能格式化的原因是,結(jié)果的階碼太大而無(wú)法用有效階碼的位數(shù)來(lái)表示。
NaN:NaN是不表示任何有效實(shí)數(shù)的位模式
特定編碼:在浮點(diǎn)運(yùn)算中,常常會(huì)出現(xiàn)一些特定的數(shù)值編碼
單精度數(shù)轉(zhuǎn)換為十進(jìn)制
IEEE單精度數(shù)轉(zhuǎn)換為十進(jìn)制時(shí),建議步驟如下:
示例IEEE(0 10000010 01011000000000000000000)轉(zhuǎn)換為十進(jìn)制:
浮點(diǎn)單元
Inter8086處理器設(shè)計(jì)使之只能處理整數(shù)運(yùn)算。這對(duì)于使用浮點(diǎn)運(yùn)算的圖形和計(jì)算密集型軟件來(lái)說(shuō)就變成了麻煩。盡管也可以純粹地通過(guò)軟件來(lái)模擬浮點(diǎn)運(yùn)算,但這樣會(huì)帶來(lái)嚴(yán)重的性能損失
FPU寄存器棧
FPU不使用通用寄存器,反之,它有自己的一組寄存器,稱為寄存器棧。數(shù)值從內(nèi)存加載到寄存器棧,然后執(zhí)行計(jì)算,再將堆棧數(shù)值保存到內(nèi)存。FPU指令用后綴形式計(jì)算算術(shù)表達(dá)式,這和惠普計(jì)算器的方法大致相同。比如,現(xiàn)有一個(gè)中綴表達(dá)式:(5*6)+4,其后綴表達(dá)式為:5 6 *4 +
中綴表達(dá)式(A+B)*C要用括號(hào)來(lái)覆蓋默認(rèn)的優(yōu)先規(guī)則,與之等效的后綴表達(dá)式則不需要括號(hào):A B + C *
? 中綴轉(zhuǎn)為后綴的例子
| A+B | AB+ | (A+B)*(C+D) | AB+CD+* |
| (A-B)/D | AB-D/ | ((A+B)/C)*(E-F) | AB+C/EF-* |
表達(dá)式堆棧:在計(jì)算后綴表達(dá)式的過(guò)程中,用堆棧來(lái)保存中間結(jié)果
FPU寄存器
FPU有8個(gè)獨(dú)立的 可尋址的80位數(shù)據(jù)寄存器R0-R7,這些寄存器合稱為寄存器棧。FPU狀態(tài)字中名為TOP的一個(gè)3位字段給出了當(dāng)前處于棧頂?shù)募拇嫫骶幪?hào)。例如 當(dāng)TOP=011時(shí) 表示棧頂為R3。在編寫浮點(diǎn)指令時(shí),這個(gè)位置也稱為ST(0)。最后一個(gè)寄存器為ST(7)
如同想的一樣,入棧操作將top-1,并把操作數(shù)復(fù)制到標(biāo)識(shí)為ST(0)的寄存器中,如果在入棧之前,TOP等于0,那么TOP就回繞到寄存器R7。出棧操作把ST(0)的數(shù)據(jù)復(fù)制到操作數(shù),再將TOP+1。如果在出棧之前TOP=7,則TOP就回繞到寄存器R0。如果加載到堆棧的數(shù)值覆蓋了寄存器棧內(nèi)的原有數(shù)據(jù),就會(huì)產(chǎn)生一個(gè)浮點(diǎn)異常
盡管理解FPU如何利用一組有限數(shù)量的寄存器實(shí)現(xiàn)堆棧很有意思,但這里只需要關(guān)注ST(n),其中ST(0)總是表示棧頂。從這里開始,引用棧寄存器時(shí)將使用ST(0) ST(1),以此類推。指令操作數(shù)不能直接引用寄存器編號(hào)
寄存器中浮點(diǎn)數(shù)使用的是IEEE10字節(jié)擴(kuò)展實(shí)數(shù)格式,也被稱為臨時(shí)實(shí)數(shù)。當(dāng)FPU把算術(shù)運(yùn)算結(jié)果存入內(nèi)存時(shí),它會(huì)把結(jié)果轉(zhuǎn)換成如下格式之一:整數(shù) 長(zhǎng)整數(shù) 單精度 雙精度 或者壓縮二進(jìn)制編碼的十進(jìn)制數(shù)
專用寄存器
FPU有6個(gè)專用寄存器
- 操作碼寄存器:保存最后執(zhí)行的非控制指令的操作碼
- 控制寄存器:執(zhí)行運(yùn)算時(shí),控制精度以及FPU使用的舍入方法,還可以用這個(gè)寄存器來(lái)屏蔽單個(gè)浮點(diǎn)異常
- 狀態(tài)寄存器:包含棧頂指針 條件碼和異常警告
- 標(biāo)識(shí)寄存器:指明FPU數(shù)據(jù)寄存器棧內(nèi)每個(gè)寄存器的內(nèi)容。其中每個(gè)寄存器都用兩位來(lái)表示該寄存器包含的是一個(gè)有效數(shù) 零 特殊數(shù)值還是為空
- 最后指令指針寄存器:保存指向最后執(zhí)行的非控制指令的指針
- 最后數(shù)據(jù)(操作數(shù))指針寄存器:保存指向數(shù)據(jù)操作數(shù)的指針,如果存在那么該數(shù)被最后執(zhí)行的指令所使用
舍入
FPU嘗試從浮點(diǎn)運(yùn)算中產(chǎn)生非常精確的運(yùn)算結(jié)果,但是在很多情況下這是不可能的,因?yàn)槟繕?biāo)操作數(shù)可能無(wú)法精確表示計(jì)算結(jié)果。FPU可以在四種舍入方法中進(jìn)行選擇
FPU控制字
FPU控制字用兩位指明使用的舍入方法,這兩位被稱為RC字段。字段數(shù)值如下:
- 00:舍入到最接近的偶數(shù)(默認(rèn))
- 01:向負(fù)無(wú)窮舍入
- 10:向正無(wú)窮舍入
- 11:向0舍入
浮點(diǎn)數(shù)異常
每個(gè)程序都可能出錯(cuò),而FPU就需要處理這些結(jié)果。因而,它要識(shí)別并檢測(cè)6種類型的異常條件:無(wú)效操作 除零 非規(guī)格化操作數(shù) 數(shù)字上溢 數(shù)字下溢以及模糊精度。前三個(gè)在全部運(yùn)算操作發(fā)生前進(jìn)行檢測(cè),后三個(gè)在操作發(fā)生后進(jìn)行檢測(cè)。
每種異常都有對(duì)應(yīng)的標(biāo)志位和屏蔽位。當(dāng)檢測(cè)到浮點(diǎn)異常時(shí),處理器將與之匹配的標(biāo)志位置1。每個(gè)被處理器標(biāo)志的異常都有兩種可能的操作:
- 如果相應(yīng)的屏蔽位置1 那么處理器自動(dòng)處理異常并繼續(xù)執(zhí)行程序
- 如果相應(yīng)的屏蔽位清0,那么處理器將調(diào)用軟件異常處理程序
大多數(shù)程序普遍都可以接受處理器的屏蔽響應(yīng)。如果應(yīng)用程序需要特殊響應(yīng),那么可以使用自定義異常處理程序,一條指令能觸發(fā)多個(gè)異常,因此處理器要持續(xù)保存自上一次異常清零后所發(fā)生的全部異常。完成一系列計(jì)算后,可以檢測(cè)是否發(fā)生了異常。
浮點(diǎn)數(shù)指令集
FPU指令集有些復(fù)雜,因此本節(jié)嘗試對(duì)齊功能進(jìn)行概述,并用具體例子給出編譯器通常會(huì)生成的代碼。此外,本節(jié)還將看到如何通過(guò)改變舍入模式來(lái)控制FPU。指令集包括如下基本指令類型:
- 數(shù)據(jù)傳送
- 基本算術(shù)運(yùn)算
- 比較
- 超越函數(shù)
- 常數(shù)加載
- x87FPU控制
- x87FPU和SIMD狀態(tài)管理
浮點(diǎn)指令名用字母F開頭,以區(qū)別CPU指令,指令助記符的第二個(gè)字母(通常為B或I)指明如何解釋內(nèi)存操作數(shù):B表示BCD操作數(shù),I表示二進(jìn)制整數(shù)操作數(shù)。如果這兩個(gè)字母都沒(méi)有使用,則內(nèi)存操作數(shù)被認(rèn)為是實(shí)數(shù)。比如,FBLD操作對(duì)象為BCD數(shù)值,FILD操作對(duì)象為整數(shù),而FLD操作對(duì)象為實(shí)數(shù)
操作數(shù):浮點(diǎn)指令可以包含零操作數(shù) 單操作數(shù)和雙操作數(shù)。如果是雙操作數(shù),那么其中一個(gè)必然為浮點(diǎn)寄存器。指令中沒(méi)有立即操作數(shù),但是某些預(yù)定義常數(shù)可以加載到堆棧。通用寄存器EAX EBX…不能作為操作數(shù)。
整數(shù)操作數(shù)從內(nèi)存加載到FPU,并自動(dòng)轉(zhuǎn)換為浮點(diǎn)格式。同樣,將浮點(diǎn)數(shù)保存到整數(shù)內(nèi)存操作數(shù)時(shí),該數(shù)值也會(huì)被自動(dòng)截?cái)嗷蛏崛霝檎麛?shù)。
1.初始化(FINIT)
FINIT指令對(duì)FPU進(jìn)行初始化。將FPU控制字設(shè)置為037Fh,即屏蔽了所有浮點(diǎn)異常,舍入模式設(shè)置為最近偶數(shù),計(jì)算精度設(shè)置為64位。建議在程序開始時(shí)調(diào)用FINIT,這樣就可以了解處理器的其實(shí)狀態(tài)
2.浮點(diǎn)數(shù)據(jù)類型
MASM支持的浮點(diǎn)類型有:
- QWORD 64位整數(shù)
- TBYTE 80位整數(shù)
- REAL4 32位IEEE短實(shí)數(shù)
- REAL8 64位IEEE長(zhǎng)實(shí)數(shù)
- REAL10 80位IEEE擴(kuò)展實(shí)數(shù)
3.加載浮點(diǎn)數(shù)值 FLD
FLD指令將浮點(diǎn)操作數(shù)復(fù)制到FPU堆棧棧頂(ST(0))。操作數(shù)可以是32位 64位 80位的內(nèi)存操作數(shù)或另一個(gè)FPU寄存器。FLD支持的內(nèi)存操作數(shù)類型與MOV指令一樣
FILD
FILD指令將16位 32位或者64位有符號(hào)整數(shù)源操作數(shù)轉(zhuǎn)換為雙精度浮點(diǎn)數(shù),并加載到ST(0)。源操作數(shù)符號(hào)保留。FILD支持的內(nèi)存操作數(shù)類型和MOV一致
加載常數(shù)
下面的指令將特定常數(shù)加載到堆棧,這些指令沒(méi)有操作數(shù)
- FLD1指令將1.0壓入寄存器堆棧
- FLDL2T指令將log210壓入寄存器堆棧
- FLDL2E指令將log2e壓入寄存器堆棧
- FLDPI指令將π壓入寄存器堆棧
- FLDLG2指令將log102壓入寄存器堆棧
- FLDLN2指令將loge2壓入寄存器堆棧
- FLDZ(加載零)指令將0.0壓入FPU堆棧
保存浮點(diǎn)數(shù)值(FST FSTP FIST)
FST指令將浮點(diǎn)操作數(shù)從FPU棧頂復(fù)制到內(nèi)存。FST支持的內(nèi)存操作數(shù)類型和FLD一致。操作數(shù)可以為32位 64位 80位內(nèi)存操作數(shù)或另外一個(gè)FPU寄存器
FSTP(保存浮點(diǎn)值并將其出棧)指令將ST(0)的值復(fù)制到內(nèi)存并將ST(0)彈出堆棧
FIST(保存整數(shù))指令將ST(0)的值轉(zhuǎn)換為有符號(hào)整數(shù),并把結(jié)果保存到目標(biāo)操作數(shù)。保存的值可以為字或者雙字。FIST支持的內(nèi)存操作數(shù)類型與FST一致
算術(shù)運(yùn)算指令
下表列出了基本算術(shù)運(yùn)算操作。所有算術(shù)運(yùn)算指令支持的內(nèi)存操作數(shù)類型與FLD(加載)和FST(保存)一致,因此操作數(shù)可以是間接操作數(shù) 變址操作數(shù)和基址變址操作數(shù)等等
| FCHS | 修改符號(hào) |
| FADD | 源操作數(shù)與目的操作數(shù)相加 |
| FSUB | 從目的操作數(shù)中減去源操作數(shù) |
| FSUBR | 從源操作數(shù)中減去目的操作數(shù) |
| FMUL | 源操作數(shù)和目的操作數(shù)相乘 |
| FDIV | 目的操作數(shù)除以源操作數(shù) |
| FDIVR | 源操作數(shù)除以目的操作數(shù) |
FCHS和FABS
FCHS(修改符號(hào))指令將ST(0)中的浮點(diǎn)值的符號(hào)取反。FABS(絕對(duì)值)指令清除ST(0)中數(shù)值的符號(hào),以得到它的絕對(duì)值,這兩條指令都沒(méi)有操作數(shù)
FADD FADDP FIADD
FADD(加法),如果FADD沒(méi)有操作數(shù),則ST(0)與ST(1)相加,結(jié)果暫存在ST(1)。然后ST(0)彈出堆棧,把加法結(jié)果保留在棧頂。如果是寄存器操作數(shù),從同樣的棧開始,將ST(0)加到ST(1)。如果是內(nèi)存操作數(shù),FADD將操作數(shù)與ST(0)相加
FADDP(相加并出棧)指令先執(zhí)行加法操作,再將ST(0)彈出堆棧
FIADD(整數(shù)加法)指令先將源操作數(shù)轉(zhuǎn)換為擴(kuò)展雙精度浮點(diǎn)數(shù),再與ST(0)相加
FSUB FSUBP FISUB
FUSB指令從目的操作數(shù)中減去源操作數(shù),并把結(jié)果保存到目的操作數(shù)。目的操作數(shù)總是一個(gè)FPU寄存器,源操作數(shù)可以是FPU寄存器或內(nèi)存操作數(shù)。該指令操作數(shù)類型和FADD指令一致。
FUSB的操作與FADD相似,只不過(guò)它進(jìn)行的是減法而不是加法。比如,無(wú)參數(shù)FUSB實(shí)現(xiàn)ST(1)-ST(0),結(jié)果暫存與ST(1)。然后ST(0)彈出堆棧,將減法結(jié)果留在棧頂。若FSUB使用內(nèi)存操作數(shù),則從ST(0)中減去內(nèi)存操作數(shù),且不再?gòu)棾龆褩?/p>
FSUBP(相減并出棧)指令先執(zhí)行減法,再將ST(0)彈出堆棧
FISUB(整數(shù)減法)指令先把源操作數(shù)轉(zhuǎn)為擴(kuò)展雙精度浮點(diǎn)數(shù),再?gòu)腟T(0)中減去該操作數(shù)
FMUL FMULP FIMUL
FMUL指令將源操作數(shù)與目的操作數(shù)相乘,乘積保存在目的操作數(shù)中。目的操作數(shù)總是一個(gè)FPU寄存器,源操作數(shù)可以為寄存器或者內(nèi)存操作數(shù)。除了執(zhí)行的是乘法不是加法外,FMUL的操作與FADD相同。比如,無(wú)參數(shù)FMUL將ST(0)與ST(1)相乘,乘積暫存于ST(1),然后將ST(0)彈出堆棧,將乘積留在棧頂。
FMULP(相乘并出棧)指令先執(zhí)行乘法,再將ST(0)彈出堆棧
FIMUL與FIADD相同,只是它執(zhí)行的是乘法不是加法
FDIV FDIVP FIDIV
FDIV指令執(zhí)行目的操作數(shù)除以源操作數(shù),被除數(shù)保存在目的操作數(shù)中。目的操作數(shù)總是一個(gè)寄存器,源操作數(shù)可以為寄存器或者內(nèi)存操作數(shù)。其語(yǔ)法與FADD和FSUB相同。
除了執(zhí)行的是除法不是加法外,FDIV的操作和FADD相同。比如,無(wú)參數(shù)FDIV執(zhí)行ST(1)除以ST(0)。然后ST(0)彈出堆棧,將被除數(shù)留在棧頂。使用內(nèi)存操作數(shù)的FDIV將ST(0)除以內(nèi)存操作數(shù)。
若操作數(shù)為零 則產(chǎn)生除零異常。若源操作數(shù)為正 負(fù)無(wú)窮 零 或者NaN,則使用一些特殊情況
FIDIV指令先將整數(shù)源操作數(shù)轉(zhuǎn)換為擴(kuò)展雙精度浮點(diǎn)數(shù),再執(zhí)行與ST(0)的除法
比較浮點(diǎn)數(shù)值
浮點(diǎn)數(shù)不能使用CMP進(jìn)行比較,因?yàn)镃MP是通過(guò)整數(shù)減法來(lái)執(zhí)行比較的。取而代之,必須使用FCOM指令,執(zhí)行FCOM。執(zhí)行FCOM指令后,還需要采取特殊步驟,然后再使用JCC跳轉(zhuǎn)指令。由于所有的浮點(diǎn)數(shù)都為隱含的有符號(hào)數(shù),因此FCOM執(zhí)行的是有符號(hào)的比較。
FCOM FCOMP FCOMPP
FCOM(比較浮點(diǎn)數(shù))指令將源操作數(shù)與ST(0)進(jìn)行比較。源操作數(shù)可以為內(nèi)存操作數(shù)或者FPU寄存器
FCOMP指令的操作數(shù)類型和執(zhí)行的操作與FCOM指令相同,但是它要將ST(0)彈出堆棧
FCOMPP指令與FCOMP相同,但是它有兩次出棧操作
條件碼
FPU條件碼標(biāo)識(shí)有三個(gè):C3 C2和C0,用以說(shuō)明浮點(diǎn)數(shù)的比較結(jié)果。C3 C2和C0的功能分別與零標(biāo)志位(ZF) 奇偶標(biāo)志位(PF)和進(jìn)位標(biāo)志位(CF)相同。
在比較了兩個(gè)數(shù)值并設(shè)置了FPU條件碼之后,遇到的主要挑戰(zhàn)就是怎樣根據(jù)條件分支到相應(yīng)標(biāo)號(hào)。這包括兩個(gè)步驟
- 用FNSTSW指令把FPU狀態(tài)字送入AX
- 用SAHF指令把AH復(fù)制到EFLAGS寄存器
條件碼送入EFLAGS之后,就可以根據(jù)ZF CF和PF進(jìn)行條件跳轉(zhuǎn)
P6處理器的改進(jìn)
浮點(diǎn)數(shù)比較的運(yùn)行時(shí)開銷大于整數(shù)比較。考慮到這一點(diǎn),InterP6系列引入了FCOMI指令。該指令比較浮點(diǎn)數(shù)值,并直接設(shè)置ZF PF CF
讀寫浮點(diǎn)數(shù)值
- ReadFloat:從鍵盤讀取一個(gè)浮點(diǎn)數(shù),并將其壓入浮點(diǎn)堆棧
- WriteFloat:將ST(0)中的浮點(diǎn)數(shù)以階碼形式寫到控制臺(tái)窗口
異常同步
整數(shù)(CPU)和FPU是相互獨(dú)立的單元,因此,在執(zhí)行整數(shù)和系統(tǒng)指令的同時(shí)可以執(zhí)行浮點(diǎn)指令。這個(gè)功能被稱為并行性,當(dāng)發(fā)生未屏蔽的浮點(diǎn)異常時(shí),它可能是一個(gè)潛在的問(wèn)題。反之,已屏蔽異常則不成問(wèn)題,因?yàn)镕PU總是可以完成當(dāng)前操作并保存結(jié)果。
發(fā)生未屏蔽異常時(shí),中斷當(dāng)前的浮點(diǎn)指令,FPU發(fā)異常事件信號(hào)。當(dāng)下一條浮點(diǎn)指令或者FWAIT指令將要被執(zhí)行時(shí),FPU檢查待處理的異常。如果發(fā)現(xiàn)有這樣的異常,FPU就調(diào)用浮點(diǎn)異常處理程序
如果引發(fā)異常的浮點(diǎn)指令后面跟的是整數(shù)或系統(tǒng)指令,則指令不會(huì)檢查待處理異常——它們會(huì)立即執(zhí)行
總結(jié)
以上是生活随笔為你收集整理的汇编之浮点数处理(CrackMe003前置知识)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 160个CrackMe002
- 下一篇: 160个Crackme003之4C大法详