3

我在一个程序中有这个指令:

FSTENV (28-BYTE) PTR SS:[ESP-1C]

它有什么作用?

它使用和更新哪些寄存器?

谢谢!

4

4 回答 4

4

它存储浮点环境。其中包括:当前控制字、状态字、标签字、指令指针和操作数指针。这些存储在内存中的结构中。在 16 位模式下,该结构为 14 个字节。在 32 位模式下,它是 28 个字节。我完全不确定它在 64 位模式下是否可用(64 位模式主要使用 SSE 代替)[编辑:显然在 32 位和 64 位模式下操作相同。]

我不相信它会改变协处理器的任何当前状态[编辑:哎呀——确实如此,它掩盖了 FP 异常,但大多数人从不从一开始就取消掩盖它们,所以...]——但是当你使用fldenv,这会将状态恢复到您用于fstenv存储它时的状态。

于 2012-04-26T16:51:46.397 回答
4

Jerry Coffins 答案是正确的。
如果您想知道(28-BYTE) PTR SS:[ESP-1C]:
这是存储 FP 环境的有效地址,它指定命令的 28 字节版本,并指向堆栈段中堆栈指针下方的 28 (0x1c) 字节。
我只是添加了英特尔的官方描述,这是我使用搜索引擎找到的。

描述

将当前 FPU 操作环境保存在目标操作数指定的内存位置,然后屏蔽所有浮点异常。FPU 操作环境由 FPU 控制字、状态字、标签字、指令指针、数据指针和最后操作码组成。IA-32 英特尔® 架构软件开发人员手册第 1 卷中的图 7-13 到 7-16 显示了存储环境在内存中的布局,具体取决于处理器的操作模式(受保护的或真实的)和当前操作数-size 属性(16 位或 32 位)。在虚拟 8086 模式下,使用实模式布局。

FSTENV 指令在存储 FPU 环境之前检查并处理任何未决的未屏蔽浮点异常;FNSTENV 指令没有。保存的图像反映了指令流中FSTENV/FNSTENV指令之前的所有浮点指令都已执行后FPU的状态。

这些指令经常被异常处理程序使用,因为它们提供对 FPU 指令和数据指针的访问。环境通常保存在堆栈中。保存环境后屏蔽所有异常可防止浮点异常中断异常处理程序。英特尔® 架构兼容性

在 MS-DOS* 操作系统兼容模式下运行 Pentium® 或 Intel486™ 处理器时,可能(在不寻常的情况下)在执行 FNSTENV 指令之前中断以处理未决的 FPU 异常。有关这些情况的描述,请参阅 IA-32 英特尔® 架构软件开发人员手册第 1 卷附录 D 中标题为“No-Wait FPU 指令可以在窗口中获取 FPU 中断”的部分。在 Pentium Pro 处理器上不能以这种方式中断 FNSTENV 指令。

手术

DEST[FPUControlWord) <- FPUControlWord;

DEST[FPUStatusWord) <- FPUStatusWord;

DEST[FPUTagWord) <- FPUTagWord;

DEST[FPUDataPointer) <- FPUDataPointer;

DEST[FPUInstructionPointer) <- FPUInstructionPointer;

DEST[FPULastInstructionOpcode) <- FPULastInstructionOpcode;

受影响的 FPU 标志

C0、C1、C2 和 C3 未定义。

浮点异常

没有任何。

保护模式异常

GP(0) - 如果目标位于不可写段中。如果内存操作数有效地址超出 CS、DS、ES、FS 或 GS​​ 段限制。如果 DS、ES、FS 或 GS​​ 寄存器用于访问内存并且它包含一个空段选择器。

SS(0) - 如果内存操作数有效地址超出 SS 段限制。

NM - CR0 中的 EM 或 TS 已设置。

PF(fault-code) - 如果发生页面错误。

AC(0) - 如果启用对齐检查并在当前特权级别为 3 时进行未对齐的内存引用。实地址模式异常

GP - 如果内存操作数有效地址超出 CS、DS、ES、FS 或 GS​​ 段限制。

SS - 如果内存操作数有效地址超出 SS 段限制。

NM - CR0 中的 EM 或 TS 已设置。虚拟 8086 模式异常

GP(0) - 如果内存操作数有效地址超出 CS、DS、ES、FS 或 GS​​ 段限制。

SS(0) - 如果内存操作数有效地址超出 SS 段限制。

NM - CR0 中的 EM 或 TS 已设置。

PF(fault-code) - 如果发生页面错误。

AC(0) - 如果启用对齐检查并进行未对齐的内存引用。

于 2012-04-26T17:06:03.367 回答
3

只是为了完整起见,这里是FSTENV/FNSTENV指令产生的内存布局。它在 x86-64"Long 64-bit Mode"和 x86中是相同的"32 bit compatibility mode"(除非您在 x86-64 中添加前缀66h。)

引用这个笨重的英特尔文档,这是布局:

在此处输入图像描述

(顺便说一句,上面的图像也应该被命名为“长模式”。)

因此,如果我们以长 64 位模式运行实际测试:

在此处输入图像描述

FNSTENV并在具有以下上下文状态的指令后立即中断:

在此处输入图像描述

它返回的内存布局如下:

在此处输入图像描述

我不会隐瞒,这很奇怪。(从截断的 RIP 寄存器到看起来很奇怪的操作码。)但我猜出于遗留原因,它仍然受支持。

好消息是,在编写现代 x64 代码时,这些古老的 x87 FPU 指令如今已没有多少用途。在这种情况下,您绝对应该坚持使用XMM0直通XMM15寄存器及其相关指令来满足您的浮点需求。

于 2018-05-22T05:34:31.130 回答
2

我想用不同的方法来回答这个问题。

FSTENV 不是“真正的”指令。
搜索“FNSTENV”操作码时可能会更幸运。

仔细查看编码(来自Intel SDM):

FSTENV   9B D9 /6  
FNSTENV  D9 /6

看到前面的“9B”了吗?
它实际上是“FNSTENV”前面的“FWAIT”。

因此,“FSTENV”与许多其他指令一样,只是大多数汇编程序和反汇编程序都可以理解的约定。

英特尔手册确实提到了这个特性,但你不应该期望它在 100% 的情况下是准确的,有时它可能会忽略这些细节:

FSTENV/FNSTENV — 存储 x87 FPU 环境(第 2A 卷 3-393)

汇编器为 FSTENV 指令发出两条指令(一条FWAIT指令后跟一条 FNSTENV 指令),处理器分别执行这些指令中的每一条。如果这些指令中的任何一条产生了异常,则保存 EIP 会指向导致异常的指令。

有很多这样的“特殊”指令。
例如,您可能会对x86 中有多少 NOP感到惊讶,它们通常是其他指令的别名。

奖金 - 有用的资源

英特尔 XED可以在您的斗争中派上用场,原因如下:

  1. 它可以用作编码器/解码器来验证其他工具。
  2. 它的数据文件是人类(和机器)可读的 ISA 信息的良好来源。您可能会找到解释数据文件结构的工程注释。

Go asm有一个名为x86.csv的东西,它以英特尔 SDM 方式列出 x86 指令,但有时还包含附加信息。
如果您将其 grep 为“FSTENV”,您将看到与之关联的“伪”标签。
请注意,x86.v0.2.csv 可能会遗漏一些说明,尤其是来自较新扩展的说明(不过,这将在 v0.3 中修复)。

于 2018-01-09T12:03:50.513 回答