x86 彙編/浮點數
ALU 只能處理整數值。雖然整數對於某些應用程式來說已經足夠了,但通常需要使用小數。一個高度專業化的協處理器,是 FPU(浮點單元)的一部分,將允許您操作帶有小數部分的數字。
最初的 x86 家族成員擁有一個獨立的數學協處理器,用於處理浮點運算。原始的協處理器是 8087,從那時起,所有 FPU 都被稱為“x87”晶片。後來,變體將 FPU 整合到微處理器本身。能夠管理浮點數意味著幾件事
- 微處理器必須有空間來儲存浮點數。
- 微處理器必須有指令來操作浮點數。
即使 FPU 被整合到 x86 晶片中,它仍然被稱為“x87”部分。例如,該主題的文獻通常將 FPU 暫存器堆疊稱為“x87 堆疊”,並且 FPU 操作通常被稱為“x87 指令集”。
可以使用 cpuid 指令檢查整合 x87 FPU 的存在。
; after you have verified
; that the cpuid instruction is indeed available:
mov eax, 1 ; argument request feature report
cpuid
xor rax, rax ; wipe clean accumulator register
bt edx, rax ; CF ≔ edx[rax] retrieve bit 0
setc al ; al ≔ CF
FPU 有一個包含 *8 個* 暫存器的陣列,可以作為堆疊訪問。有一個 *top* 索引指示堆疊的當前頂部。將專案推入或彈出堆疊只會更改 *top* 索引,並分別儲存或擦除資料。
st(0) 或簡稱為 st 指的是當前位於堆疊頂部的暫存器。如果堆疊上儲存了 8 個值,則 st(7) 指的是堆疊上的最後一個元素(即底部)。
數字從 *記憶體中* 推入堆疊,並從堆疊彈出回 *記憶體中*。沒有指令允許直接將值傳輸到或從 ALU 暫存器。x87 堆疊只能透過 FPU 指令訪問 - 您不能編寫 mov eax, st(0) - 有必要將值儲存到記憶體中,例如,如果您想列印它們。
FPU 指令通常會從堆疊中彈出前兩個專案,對它們進行操作,並將答案推回堆疊頂部。
浮點數通常可以是 32 位長,即程式語言 C 中的 float 資料型別,或者 64 位長,即 C 中的 double。但是,為了減少舍入誤差,FPU 堆疊暫存器都 *80 位寬*。
大多數 呼叫約定 在 st(0) 暫存器中返回浮點值。
以下程式(使用 NASM 語法)計算 123.45 的平方根。
[org 0x7c00]
[bits 16]
global _start
section .data
val: dq 123.45 ; define quadword (double precision)
section .bss
res: resq 1 ; reserve 1 quadword for result
section .text
_start:
;initilizes the FPU, avoids inconsistent behavior
fninit
; load value into st(0)
fld qword [val] ; treat val as an address to a qword
; compute square root of st(0) and store the result in st(0)
fsqrt
; store st(0) at res, and pop it off the x87 stack
fstp qword [res]
; the FPU stack is now empty again
; end of program
本質上,使用 FPU 的程式使用 fld 及其變體將值載入到堆疊上,對這些值執行操作,然後使用 fst 的一種形式將它們儲存到記憶體中,最常見的是在您完成 x87 後使用 fstp,以根據大多數呼叫約定清理 x87 堆疊。
這是一個更復雜的示例,它評估 餘弦定律
;; c^2 = a^2 + b^2 - cos(C)*2*a*b
;; C is stored in ang
global _start
section .data
a: dq 4.56 ;length of side a
b: dq 7.89 ;length of side b
ang: dq 1.5 ;opposite angle to side c (around 85.94 degrees)
section .bss
c: resq 1 ;the result ‒ length of side c
section .text
_start:
fld qword [a] ;load a into st0
fmul st0, st0 ;st0 = a * a = a^2
fld qword [b] ;load b into st0 (pushing the a^2 result up to st1)
fmul st0, st0 ;st0 = b * b = b^2, st1 = a^2
faddp ;add and pop, leaving st0 = old_st0 + old_st1 = a^2 + b^2. (st1 is freed / empty now)
fld qword [ang] ;load angle into st0. (st1 = a^2 + b^2 which we'll leave alone until later)
fcos ;st0 = cos(ang)
fmul qword [a] ;st0 = cos(ang) * a
fmul qword [b] ;st0 = cos(ang) * a * b
fadd st0, st0 ;st0 = cos(ang) * a * b + cos(ang) * a * b = 2(cos(ang) * a * b)
fsubp st1, st0 ;st1 = st1 - st0 = (a^2 + b^2) - (2 * a * b * cos(ang))
;and pop st0
fsqrt ;take square root of st0 = c
fstp qword [c] ;store st0 in c and pop, leaving the x87 stack empty again ‒ and we're done!
; don't forget to make an exit system call for your OS,
; or execution will fall off the end and decode whatever garbage bytes are next.
mov eax, 1 ; __NR_exit
xor ebx, ebx
int 0x80 ; i386 Linux sys_exit(0)
;end program
您可能會注意到,以下某些指令在名稱上只差一個字母:在末尾附加一個 **P**。該字尾表示除了執行正常操作之外,它們還會在執行完成後 **P**op x87 堆疊。
FDISI, FENI, FLDENVW, FLDPI, FNCLEX, FNDISI, FNENI, FNINIT, FNSAVEW, FNSTENVW, FRSTORW, FSAVEW, FSTENVW
fld: 載入浮點數fild: 載入整數fbldfbstp- 在堆疊頂部載入一個常數
fld1:fldld2e:fldl2t:fldlg2:flln2:fldz: “正”
fst,fstpfist,fistp: 儲存整數fxch: 交換fisttp: 儲存截斷的整數
算術指令
[edit | edit source]fabs: 絕對值fchs: 改變符號fxtract: 拆分指數和有效數
fadd,faddp,fiadd: 加法fsub,fsubp,fisub: 減法fsubr,fsubrp,fisubr: 反向減法
fmul,fmulp,fimulfsqrt: 平方根fdiv,fdivp,fidiv: 除法(另見fdiv維基百科上的錯誤)fdivr,fdivrp,fidivrfprem: 部分餘數fptanfpatanfrndint: 四捨五入為整數fscale: 乘以/除以 2 的整數次冪f2xm1:fyl2x:fyl2xp1:
FPU 內部和其他指令
[edit | edit source]finit: 初始化 FPUfldcwflenvfrstorfsave,fnsavefstcw,fnstcwfstenv,fnstenvfstsw,fnstsw
finccstp和fdecstp: 增加或減少 topffree: 將暫存器標記為可用
ftst: 測試fcom,fcomp,fcompp: 比較浮點數ficom,ficomp: 與整數比較fxam: 檢查暫存器
fclex: 清除異常fnopfwait與wait相同。
在特定處理器中新增
[edit | edit source]隨 80287 新增
[edit | edit source]FSETPM
隨 80387 新增
[edit | edit source]FCOS, FLDENVD, FNSAVED, FNSTENVD, FPREM1, FRSTORD, FSAVED, FSIN, FSINCOS, FSTENVD, FUCOM, FUCOMP, FUCOMPP
隨奔騰 Pro 新增
[edit | edit source]FCMOVB, FCMOVBE, FCMOVE, FCMOVNB, FCMOVNBE, FCMOVNE, FCMOVNU, FCMOVU, FCOMI, FCOMIP, FUCOMI, FUCOMIP, FXRSTOR, FXSAVE
FXRSTOR,FXSAVE
這些指令也在沒有 SSE 支援的後期奔騰 II 中得到支援
FISTTP(x87 到整數轉換,無論狀態字如何都進行截斷)
ffreep: 執行ffree st(i)並彈出堆疊