【问题标题】:Can I get a int from my EAX/RAX to a register of the FPU like st0?我可以从我的 EAX/RAX 获取一个 int 到 FPU 的寄存器,比如 st0 吗?
【发布时间】:2018-05-08 19:26:40
【问题描述】:

我目前正在大学从事一个小型汇编程序项目。现在我的问题是,是否有可能从我的 EAX/RAX 寄存器到我的 FPU 寄存器之一(如 st0)获得用户给出的乘法(int)的 skalar?我正在使用 NASM 语法。

谢谢

【问题讨论】:

  • 是的。使用旧的 x87 代码 push rax fild qword [rsp] add rsp,8,但如果 CPU 确实支持 64b,它必须(?)也有某种 SSE,所以使用 x87 很奇怪......为什么?
  • SEE 是什么意思? :D
  • @J.B. SSE 是自 Pentium III 以来更新的浮点单元。这是您通常在 64 位代码中使用的内容。
  • 关于 SSE 和类似 CPU 扩展的维基:en.wikipedia.org/wiki/Streaming_SIMD_Extensions
  • 使用 SSE2 cvtsi2sd xmm0, rax 并忘记 x87,除非您需要 80 位 FP(64 位尾数)。

标签: assembly x86 nasm fpu


【解决方案1】:

没有办法直接将整数寄存器的内容传送到x87浮点寄存器,必须经过内存。典型代码如下所示:

PUSH RAX         ; push RAX on the stack
FILD QWORD [RSP] ; load eight byte integer onto FP stack
ADD RSP,8        ; release storage from stack

您通常可以通过在函数开始时在堆栈帧中为这种传输分配一些存储空间来避免摆弄堆栈指针。

【讨论】:

  • 如果我在主堆栈上有一个 64 位双精度并且我想将此值获取到 st0,是否允许在 RAX 中弹出 64 位值以通过此方法在 st0 中获取它?还是有另一种“直接”的方式?
  • 或者我可以使用 pop st0 吗?
  • @J.B.好吧,我回答的第一步是将来自rax 的值存储在堆栈上,所以我不确定你试图通过将一个值从堆栈移动到rax 然后大概回到堆栈来实现什么。您可以执行fld qword [rsp][rsp] 处的双精度加载到x87 堆栈上。有关可用指令的完整列表,请参阅英特尔手册。
  • @J.B.没有这样的指示; pop 仅作为 pop r64pop r16pop segrpop r/m64 存在。在询问您是否可以做某事之前,请尝试检查手册是否存在您询问的说明。 Stack Overflow 确实不能替代您阅读手册。
  • @J.B.地址是整数,根据经验,您希望将它们保存在通用寄存器中,如raxrbx 等。假设您要加载的数据的地址在rax,您只需写@ 987654334@ 从该地址加载双精度。在开始对 x87 FPU 进行编程之前,熟悉 x86 汇编的基础知识可能是个好主意。
【解决方案2】:

在 fuz 答案下的 cmets 中计算的示例(适用于 64b linux 和 NASM):

; file: x87test.asm
section .data
    some_value  dq 1234.5678    ; double value

section .bss
    result      resq    1       ; reserve memory for result double
    result2     resq    1       ; reserve memory for second result (code variant 2)

section .text
    global _start
    _start:
        ; initializations of example
        finit                       ; initialize FPU
        ; store "factor" into the stack
        mov     rax,__float64__(51.6)
        push    rax
        ; "value" is already in memory at address `some_value`

        ; load the FPU-stack with factor and value
        fld     qword [some_value]  ; st0 = value
        fld     qword [rsp]         ; st0 = factor, st1 = value
        add     rsp, 8              ; release the CPU stack space occupied by factor (by "push rax")

        ; Do the calculation with st0 and st1
        fmulp   st1                 ; st0 = st0 * st1 with "pop" (the FP stack holds only "st0")
            ; "fmul" without "p" would keep the "st1" intact (value) and st0 = product
        fstp    qword [result]      ; "pop" st0 into memory at "result" address

        ;--------------------------------------------------------------------------------------------
        ; other variant, skipping the load of second value, as the FMUL can use memory argument too
        ; store "factor" into the stack
        mov     rax,__float64__(7.89)
        push    rax

        ; load the FPU-stack with value
        fld     qword [some_value]  ; st0 = value
        fmul    qword [rsp]         ; st0 = value * factor
        add     rsp, 8              ; release the CPU stack space occupied by factor (by "push rax")
        fstp    qword [result2]     ; "pop" st0 into memory at "result2" address

        ;--------------------------------------------------------------------------------------------
        ; exit back to linux
        mov     eax, 60
        xor     edi, edi
        syscall

构建和执行:

nasm -f elf64 x87test.asm -l x87test.lst -w+all
ld -b elf64-x86-64 -o x87test x87test.o
./x87test

不应该发生输入/输出,只是干净退出。使用调试器检查,单步执行每条指令,并观察堆栈(rsp 指向)内存区域、x87 FPU“堆栈”(st0 .. st7 值)和result 地址处的内存。


编辑:

我的理解是每个浮点运算都必须由 FPU 完成。

绝对不,如果你这么认为,你仍然缺少计算机的整个原理。计算机中的所有内容都被编码为位序列(值 0 或 1)。所以你的陈述当翻译成这个基本前提时是“我这里有一个位模式,那里有另一个位模式,一个定义明确的操作,描述了某个操作应该产生哪个第三位模式,但如果我没有,我就做不到FPU”——这听起来合乎逻辑吗?

手动将两个 IEEE-754 “双精度”值相乘需要大量工作(数十条 x86 指令),您需要提取这些值的指数和尾数部分,分别将尾数和指数相乘,然后归一化/钳位值并将有效的 IEEE-754 "double" 类型结果组合回 64 位,但在没有 FPU 的情况下绝对可行,这就是 x87 的软件仿真在 80486DX 和 Pentium CPU 使硬件 FPU 通用(80486SX 和前辈)之前一直所做的80286 和 80386 没有内置 x87,它作为单独的昂贵协处理器芯片出售)。在 386 时代,大多数人确实使用 x87 的 SW 仿真器来运行需要 FPU 的专用软件。

问题是,如果您了解某事物(输入信息)如何以位编码,以及您想要什么作为输出信息(以位编码),您就可以描述一些位操作运算的算法来转换输入值转换为输出值,然后您可以通过任何符合图灵的 CPU 实现这种算法(尽管对于一些非常有限的系统,如 8 位 CPU,创建 IEEE-754 双 * 双计算可能是主要的 PITA,因为它可能需要数百条指令,或者如果内存太有限而无法同时容纳这么多位,您甚至可能会耗尽资源)。

x87 FPU 只是浮点运算的硬件加速解决方案,并不是唯一可能的计算方式。

【讨论】:

  • 谢谢!这很有帮助;)我的陈述有点错误。我只是想说,没有其他简单的方法可以将两个浮点数相乘
  • @J.B.好吧,您可以使用 SSE 浮点单元。使用起来稍微简单一些。
  • @J.B.另外,它还取决于您所说的“浮点”数是什么意思,如果您指的是 IEEE-754 编码,那么是的,x87 和 SSE 是 x86-64 上最简单的方法。但是您可以对浮点值进行不同的编码,例如以“定点”方式,或者使用一些自定义打包方案作为单独的数字(通常由 SQL 或任意精度数学库中的“十进制”类型使用),然后是代码没有 x87 或 SSE 可能会更简单,例如“定点”方式是专门为没有 HW FPU 的 CPU 设计的,或者当您只需要具有固定粒度的特定值范围时。
猜你喜欢
  • 1970-01-01
  • 2019-01-11
  • 1970-01-01
  • 2014-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-13
相关资源
最近更新 更多