第65部分- Linux x86 64位汇编 FPU之二浮点基本运算
FPU提供对浮点值执行基本数学功能的指令。
例如:
FADD source: 内存中32位或64位和ST0寄存器相加。
FADD %st(x),%st(0): st(x)和st(0)相加,结果存于st(0)
FADD %st(0),%st(x): st(x)和st(0)相加,结果存于st(x)
FADDP %st(0),%st(x): st(x)和st(0)相加,结果存于st(x),并弹出st(0)
在GNU汇编器中,指定内存中的值的指令的助记符要包含长度(s用于32位单精度浮点值,l用于双精度浮点值)
示例
我们来计算一个表达式:
((43.65/22+(76.34*3.1))/((12.43*6)-(140.2/94.21))
具体思路如下:
- 把43.65加载到st0
- st0除以22,结果保存到st0
- 把76.34加载st0(步骤2结果移动到st1)
- 把3.1记载到st0(76.34移动到st1,步骤2结果移动到st2)
- St0和st1相乘,存放于st0
- St0和st2相加,存放于st0。表达式的分子部分完成结果。
- 把12.43加载到st0,步骤六结果移动到st1
- St0乘以6,存放在st0
- 把140.2加载到st0,步骤8移动到st1,步骤6移动到st2
- 把94.21加载到st0,步骤8移动到st2,步骤6移动到st3
- St1/st0,弹出堆栈并把结果保存到st0(步骤8答案移到st1,步骤6移动到st2)
- St1-st0,保存到st0. 表达式的分目部分完成结果。
- St2除以st0,结果保存到st0中。完成。
.extern printf ;//调用外部的printf函数
.section .data
fmt: .ascii "result is: %f \n"
value1:
.float 43.65
value2:
.int 22
value3:
.float 76.34
value4:
.float 3.1
value5:
.float 12.43
value6:
.int 6
value7:
.float 140.2
value8:
.float 94.21
result: .double 0.0
.section .text
.globl _start
_start:
nop
finit;//初始化fpu
flds value1;//1.加载value1 43.65到st0
fidiv value2;// 2.st0除以22,结果保存到st0
flds value3;//3.把76.34加载st0(步骤2结果移动到st1)
flds value4;// 4.把3.1记载到st0(76.34移动到st1,步骤2结果移动到st2)
fmul %st(1), %st(0) ;//5. St0和st1相乘,存放于st0
fadd %st(2), %st(0) ;//6. St0和st2相加,存放于st0。表达式的分子部分完成结果。
flds value5;// 7.把12.43加载到st0,步骤六结果移动到st1
fimul value6;//8. St0乘以6,存放在st0
flds value7;//9. 把140.2加载到st0,步骤8移动到st1,步骤6移动到st2
flds value8;//10. 把94.21加载到st0,步骤8移动到st2,步骤6移动到st3
fdivrp;//11. 反向除法,St1/st0,弹出堆栈并把结果保存到st0(步骤8答案移到st1,步骤6移动到st2)
fsubr %st(1), %st(0) ;//12. 反向减法,St1-st0,保存到st0. 表达式的分目部分完成结果。
fdivr %st(2), %st(0) ;//13.反向触发,St2除以st0,结果保存到st0中。完成。
fstpl result ;//st0加载到result目标中
mov $fmt,%rdi
movq result,%xmm0
call printf
mov $60,%rax
syscall
FIDIV指令在执行除法之前将整数源操作数转换为双精度扩展浮点数格式。
FDIVR是反向除法。
编译连接:
as -g -o fpmath1.o fpmath1.s
ld -o fpmath1 fpmath1.o -lc -I /lib64/ld-linux-x86-64.so.2
对应的图示结果如下: