Say hello to x86_64 Assembly [part 8]

title: Say hello to x86_64 Assembly [part 8]

date: 2020-01-11 23:50:39

tags:

  • x86

  • x64

  • 汇编

  • assembly


翻译原文地址

Say hello to x86_64 Assembly [part 8]

这是Say hello to x86_64 Assembly的第八部分,也是最后一部分,下面我们将介绍如何在汇编程序中使用非整数。使用浮点数据有两种方法:

  • fpu

  • sse

It is eight and final part of Say hello to x86_64 Assembly and here we will take a look on how to work with non-integer numbers in assembler. There are a couple of ways how to work with floating point data:

  • fpu

  • sse

首先让我们看看浮点数是如何存储在内存中的。有三种浮点数据类型:

  • -单精度

  • -双精度

  • -双扩展精度

First of all let’s look how floating point number stored in memory. There are three floating point data types:

  • single-precision

  • double-precision

  • double-extended precision

如英特尔64-ia-32-architecture-software-developer-vol-1-手册所述:

As Intel’s 64-ia-32-architecture-software-developer-vol-1-manual described:

这些数据类型的数据格式直接对应于IEEE标准754中为二进制浮点运算指定的格式。
The data formats for these data types correspond directly to formats specified in the IEEE Standard 754 for Binary Floating-Point Arithmetic.
| sign  | exponent | mantissa
|-------|----------|-------------------------
| 0     | 00001111 | 110000000000000000000000
value = mantissa * 2^-112
value = (-1)^sign * (1 + mantissa / 2 ^ 52) * 2 ^ exponent - 1023)
section .data
    x dw 1.0
​
fld dword [x]
;;
;; adds st0 value to st3 and saves it in st0
;;
fadd st0, st3
​
;;
;; adds x and y and saves it in st0
;;
fld dword [x]
fld dword [y]
fadd
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]
    fld qword [radius]
    fmul
​
    fldpi
    fmul
    fstp qword [result]
​
    mov rax, 0
    movq xmm0, [result]
    call printResult
​
    mov rax, SYS_EXIT
    mov rdi, EXIT_CODE
    syscall
#include <stdio.h>
​
extern int printResult(double result);
​
int printResult(double result) {
  printf("Circle radius is - %f\n", result);
  return 0;
}
build:
  gcc  -g -c circle_fpu_87c.c -o c.o
  nasm -f elf64 circle_fpu_87.asm -o circle_fpu_87.o
  ld   -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc circle_fpu_87.o  c.o -o testFloat1
​
clean:
  rm -rf *.o
  rm -rf testFloat1

And run:

运行:

We can build it with:

我们可以用下面命令构建:

Let’s try to understand how it works: First of all there is data section with predefined radius data and result which we will use for storing result. After this 2 constants for calling exit system call. Next we see entry point of program - _start. There we stores radius value in st0 and st1 with fld instruction and multiply this two values with fmul instruction. After this operations we will have result of radius on radius multiplication in st0 register. Next we load The number π with fldpi instruction to the st0 register, and after it radius * radius value will be in st1 register. After this execute multiplication with fmul on st0 (pi) and st1 (value of radius * radius), result will be in st0 register. Ok, now we have circle square in st0 register and can extract it with fstp instruction to the result. Next point is to pass result to the C function and call it. Remember we call C function from assembly code in previous blog post. We need to know x86_64 calling convention. In usual way we pass function parameters through registers rdi (arg1), rsi (arg2) and etc…, but here is floating point data. There is special registers: xmm0 - xmm15 provided by sse. First of all we need to put number of xmmN register to rax register (0 for our case), and put result to xmm0 register. Now we can call C function for printing result:

让我们试着理解它是如何工作的:首先,有一个带有预定义的radius数据和结果的数据部分,我们将使用它来存储结果。这2个常量用于调用退出系统调用。接下来我们看到程序的入口点-\u开始。在那里,我们用fld指令将radius值存储在st0和st1中,并用fmul指令将这两个值相乘。在这个操作之后,我们将在st0寄存器中得到半径乘法的结果。接下来,我们用fldpi指令将数字π加载到st0寄存器,然后radiusradius值将在st1寄存器中。在st0(pi)和st1(radiusradius的值)上执行fmul乘法后,结果将在st0寄存器中。好的,现在我们在st0寄存器中有了圆平方,可以用fstp指令将其提取出来。下一点是将结果传递给C函数并调用它。请记住,我们在上一篇博客文章中从程序集代码调用C函数。我们需要知道x86_64调用约定。通常我们通过寄存器rdi(arg1)、rsi(arg2)等传递函数参数,但这里是浮点数据。sse提供特殊寄存器:xmm0-xmm15。首先,我们需要将xmmN寄存器的个数放入rax寄存器(对于我们的情况是0),并将结果放入xmm0寄存器。现在我们可以调用C函数来打印结果:

Let’s look on simple example. We will have circle radius and calculate circle square and print it:

让我们看一个简单的例子。我们将得到圆半径,计算圆平方并打印出来:

pushes value of x to this stack. Operator can be 32bit, 64bit or 80bit. It works as usual stack, if we push another value with fld, x value will be in ST(1) and new value will be in ST(0). FPU instructions can use these registers, for example:

将x的值推送到这个堆栈。运算符可以是32位、64位或80位。它和通常的堆栈一样工作,如果我们使用fld推送另一个值,x值将在ST(1)中,新值将在ST(0)中。FPU指令可以使用这些寄存器,例如:

For example:

例子:

and etc… FPU has eight 10 byte registers organized in a ring stack. Top of the stack - register ST(0), other registers are ST(1), ST(2) … ST(7). We usually uses it when we are working with floating point data.

  • FADD - add floating point

  • FIADD - add integer to floating point

  • FSUB - subtract floating point

  • FISUB - subtract integer from floating point

  • FABS - get absolute value

  • FIMUL - multiply integer and floating point

  • FIDIV - device integer and floating point

Arithmetic instructions:

等等…FPU有8个10字节的寄存器组织在一个环形堆栈中。堆栈顶部-寄存器ST(0),其他寄存器是ST(1),ST(2)…ST(7)。我们通常在处理浮点数据时使用它。

  • FADD-添加浮点

  • FIADD-将整数添加到浮点

  • FSUB-减去浮点

  • FISUB-从浮点减去整数

  • FABS-获取绝对值

  • FIMUL-整数与浮点相乘

  • FIDIV-设备整数和浮点

算术指令:

  • FDL - load floating point

  • FST - store floating point (in ST(0) register)

  • FSTP - store floating point and pop (in ST(0) register)

Of course we will not see all instructions here provided by x87, for additional information see 64-ia-32-architecture-software-developer-vol-1-manual Chapter 8. There are a couple of data transfer instructions:

  • FDL - 加载浮点

  • FST - 存储浮点(在ST(0)寄存器中)

  • FSTP - 存储浮点和pop(在ST(0)寄存器中)

当然,我们在这里不会看到x87提供的所有说明,有关更多信息,请参阅64-ia-32-architecture-software-developer-vol-1-manual第8章。有一些数据传输指令:

  • Data transfer instructions

  • Basic arithmetic instructions

  • Comparison instructions

  • Transcendental instructions

  • Load constant instructions

  • x87 FPU control instructions

The x87 Floating-Point Unit (FPU) provides high-performance floating-point processing. It supports the floating-point, integer, and packed BCD integer data types and the floating-point processing algorithms. x87 provides following instructions set:

  • -数据传输指令

  • -基本算术指令

  • -比较说明

  • -超越指令

  • -加载常数指令

  • -x87 FPU控制指令

x87浮点单元(FPU)提供高性能浮点处理。它支持浮点、整数和压缩BCD整数数据类型以及浮点处理算法。x87提供以下指令集:

x87 FPU

Read more about it - here. Let’s look at simple example.

阅读更多信息-这里。让我们看一个简单的例子。

  • sign - 1 bit

  • exponent - 15 bit

  • mantissa - 112 bit

Extended precision is 80 bit numbers where:

  • 符号-1位

  • 指数-15位

  • 尾数-112位

扩展精度是80位数字,其中:

Result number we can get by:

结果编号:

  • sign - 1 bit

  • exponent - 11 bit

  • mantissa - 52 bit

Double precision number is 64 bit of memory where:

  • 符号-1位

  • 指数-11位

  • 尾数-52位

双精度数是64位内存,其中:

Exponent is either an 8 bit signed integer from −128 to 127 or an 8 bit unsigned integer from 0 to 255. Sign bit is zero, so we have positive number. Exponent is 00001111b or 15 in decimal. For single-precision displacement is 127, it means that we need to calculate exponent - 127 or 15 - 127 = -112. Since the normalized binary integer part of the mantissa is always equal to one, then in the mantissa is recorded only its fractional part, so mantissa or our number is 1,110000000000000000000000. Result value will be:

指数可以是-128到127之间的8位有符号整数,也可以是0到255之间的8位无符号整数。符号位是零,所以我们有正数。指数为0000111b或15(十进制)。对于单精度位移是127,这意味着我们需要计算指数-127或15-127=-112。由于尾数的标准化二进制整数部分始终等于1,因此尾数中只记录其小数部分,因此尾数或我们的数字是1110000000000000000000000。结果值为:

So for example if we have following number:

例如,如果我们有以下数字:

  • sign - 1 bit

  • exponent - 8 bits

  • mantissa - 23 bits

Single-precision floating-point float point data presented in memory:

  • 符号-1位

  • 指数-8位

  • 尾数-23位

内存中显示的单精度浮点数据:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值