第63部分- Linux x86 64位彙編 FPU之概要
X87浮點單元FPU,提供高性能浮點處理。從80486就開始支援了。
Intel x87 FPU專門用于執行标量浮點計算,對單精度浮點(32位)、雙精度浮點(64位)以及擴充雙精度浮點(80位)進行計算,并順從IEEE754标準。
FPU的資料寄存器的個數都一樣,隻有8個。對x87 FPU的資料寄存器的通路方式與一般的寄存器有所不同,是棧式通路。通過FLD指令把外部資料搬到x87 FPU的資料寄存器中時,那麼x87 FPU會根據所搬資料的長度(32位、64位、80位)将輸入資料分别對待為單精度浮點、雙精度浮點和擴充雙精度浮點方式,然後統一轉為雙精度擴充模式放到資料棧頂。是以x87 FPU的資料寄存器長度為80位,并且後續的浮點計算都是基于擴充雙精度進行的。
在棧頂的資料寄存器索引為0,那麼它下面一個就是1, 然後是2, 以此類推,到棧底則為7。
資料輸出到存儲器時使用FST或FSTP指令,前者僅僅是根據目标存儲器的長度(32位、64位、80位)将擴充雙精度類型分别轉為單精度、雙精度、擴充雙精度類型,然後輸出到指定的存儲器位置中。FSTP指令,除了将資料搬到外部外,還會執行推出堆棧的操作。
FLD和FST以及FSTP還能搬移FPU内部寄存器的資料,FLD的作用是将指定的FPU資料寄存器位置的資料搬移到棧頂寄存器中,而FST則是将棧頂寄存器的資料搬移到指定的FPU資料寄存器的位置。
由于80位(10個位元組)并不是32位(4個位元組)的整數倍,是以資料加載或存儲雙精度擴充浮點往往是用96位(12個位元組)為機關進行,是以“tbyte”所指的就是“twelve bytes”
Intel彙編格式的位元組、字寬指定修飾詞:byte(位元組,8位),word(字,16位),dword(雙字,32位),qword(四字,64位),tbyte(十二位元組,96位),mmword(64位,但隻能用于MMX指令集中),xmmword(128位,用于SSE指令集),ymmword(256位,用于最新的AVX指令集)。
FPU有以環形堆棧組織的八個10位元組寄存器。 堆棧頂部-寄存器ST(0),其他寄存器為ST(1),ST(2)…ST(7)。 在處理浮點資料時,通常會使用它。
資料傳輸指令
FDL-加載浮點。
FST-存儲浮點(在ST(0)寄存器中)。
FSTP-存儲浮點數(在ST(0)寄存器中)并彈出。
FLD類似于PUSH指令
FSTP類似于POP指令
FADD類似于ADD指令
算術指令:
FADD-浮點加法;// ST(0)與 ST(1)相加,結果暫存在 ST(1)。然後 ST(0) 彈出堆棧,把加法結果保留在棧頂。
FIADD-将整數添加到浮點
FSUB-減去浮點
FISUB-從浮點數減去整數
FABS-獲得絕對值
FIMUL-整數和浮點數相乘
FIDIV-裝置整數和浮點
示例
定義printResult.c檔案如下:
#include <stdio.h>
extern int printResult(double result);
int printResult(double result) {
printf("Circle radius is - %f\n", result);
return 0;
}
-
-
-
Nasm文法
-
-
定義實作的彙編檔案printResult.asm如下:
extern printResult
section .data
radius dq 1.7
result dq 0
SYS_EXIT equ 60
EXIT_CODE equ 0
global _start
section .text
_start:
fld qword [radius];//儲存到st(0)
fld qword [radius] ;//儲存到st(1)
fmul;//将 ST(O) 與 ST(1) 相乘,乘積暫存于ST(1)。然後 ST(0) 彈出堆棧,将乘積則繼續留在棧頂。
fldpi;//裝入浮點的pi,就是裝入st(1)
fmul;//将ST(O) 與 ST(1) 相乘,儲存到st(0)
fstp qword [result];//将結果儲存到[result]
mov rax, 0;// 表示xmmN寄存器的數量放入rax寄存器
movq xmm0, [result];//将參數也就是結果放到xmm0.
call printResult
mov rax, SYS_EXIT;退出系統調用的号
mov rdi, EXIT_CODE
syscall
其中DQ作為彙編語言中的僞操作指令,用來定義操作數占用的位元組數,占有4個字,即8個位元組(64位),可用來存放雙精度浮點數。
浮點參數的傳參有特殊的寄存器:sse提供的xmm0-xmm15(而不是通常的rdi,rsi……)。需要将xmmN寄存器的數量放入rax寄存器(本例中為0),然後将結果放入xmm0寄存器。
編譯如下:
gcc -g -c printResult.c -o c.o
nasm -f elf64 printResult.asm -o printResult.o
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc printResult.o c.o -o printResult
gas文法
定義實作的彙編檔案如下:
printResult.s
.extern printResult
.section .data
radius: .double 1.7
result: .double 0
.equ SYS_EXIT , 60
.equ EXIT_CODE, 0
.global _start
.section .text
_start:
leaq radius,%rbx
fldl (%rbx)
fldl (%rbx)
fmulp
fldpi
fmulp
fstl result
movq $0,%rax
movq result, %xmm0
call printResult
movq $SYS_EXIT,%rax
movq $EXIT_CODE,%rdi
syscall
編譯如下:
gcc -g -c printResult.c -o c.o
as -g -o printResult.o printResult.s
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc printResult.o c.o -o printResult
tips:在gdb中可以時候用print $st0來列印寄存器中 值。