天天看点

第63部分- Linux x86 64位汇编 FPU之概要 第63部分- Linux x86 64位汇编 FPU之概要Nasm语法gas语法

第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;
}
           
      1. 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来打印寄存器中 值。

继续阅读