【发布时间】:2012-04-26 16:43:01
【问题描述】:
我在一个程序中有这条指令:
FSTENV (28-BYTE) PTR SS:[ESP-1C]
它有什么作用?
它使用和更新了哪些寄存器?
谢谢!
【问题讨论】:
-
假设您已经 google 过,在大量的文档结果中您有什么不明白的?
-
还是你不懂的操作数语法?
我在一个程序中有这条指令:
FSTENV (28-BYTE) PTR SS:[ESP-1C]
它有什么作用?
它使用和更新了哪些寄存器?
谢谢!
【问题讨论】:
Jerry Coffins 答案是正确的。
如果您想知道 (28-BYTE) PTR SS:[ESP-1C]:
这是存储 FP 环境的有效地址,它指定 28 字节版本的命令并指向堆栈段中堆栈指针下方的 28 (0x1c) 字节。
我只是添加了英特尔的官方描述,这是我使用搜索引擎找到的。
说明
在内存位置保存当前的 FPU 运行环境 用目标操作数指定,然后屏蔽所有 浮点异常。 FPU操作环境包括 FPU 控制字、状态字、标签字、指令指针、数据 指针和最后一个操作码。 IA-32 中的图 7-13 至 7-16 英特尔® 架构软件开发人员手册,第 1 卷,展示了 存储环境的内存布局,取决于操作 处理器的模式(受保护的或真实的)和当前操作数大小 属性(16 位或 32 位)。在虚拟 8086 模式下,实模式 使用布局。
FSTENV 指令检查并处理任何待处理的未屏蔽 存储 FPU 环境之前的浮点异常;这 FNSTENV 指令没有。保存的图像反映了状态 在 FPU 之前的所有浮点指令之后 指令流中的FSTENV/FNSTENV指令已被 执行。
这些指令经常被异常处理程序使用,因为它们 提供对 FPU 指令和数据指针的访问。这 环境通常保存在堆栈中。屏蔽所有异常 保存环境后防止浮点异常 中断异常处理程序。英特尔® 架构兼容性
在 MS-DOS* 中运行 Pentium® 或 Intel486™ 处理器时 系统兼容模式下,有可能(异常情况下 情况)为一个 FNSTENV 指令被中断之前 正在执行以处理挂起的 FPU 异常。见章节 标题为“No-Wait FPU Instructions Can Get FPU Interrupt in Window” IA-32 英特尔® 架构软件开发人员的附录 D 手册,第 1 卷,用于描述这些情况。一个 FNSTENV 在 Pentium Pro 上不能以这种方式中断指令 处理器。
操作
DEST[FPUControlWord)
DEST[FPUStatusWord)
DEST[FPUTagWord)
DEST[FPUDataPointer)
DEST[FPUInstructionPointer)
DEST[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) - 如果启用了对齐检查并且进行了未对齐的内存引用。
【讨论】:
它存储浮点环境。其中包括:当前控制字、状态字、标签字、指令指针和操作数指针。这些存储在内存中的结构中。在 16 位模式下,该结构为 14 个字节。在 32 位模式下,它是 28 个字节。我完全不确定它在 64 位模式下是否可用(64 位模式主要使用 SSE 代替)[编辑:显然在 32 位和 64 位模式下操作相同。]
我不相信它会改变协处理器的任何当前状态[编辑:哎呀——确实如此,它掩盖了 FP 异常,但大多数人从不从一开始就取消屏蔽它们,所以...]——但是当你使用fldenv 时,它会将状态恢复到你使用fstenv 存储它时的状态。
【讨论】:
为了完整起见,这里是FSTENV/FNSTENV 指令产生的内存布局。在 x86-64 "Long 64-bit Mode" 和 x86 "32 bit compatibility mode" 中是相同的(除非您在 x86-64 中添加前缀 66h。)
引用this unwieldy英特尔文档,这是布局:
(顺便说一句,上面的图像也应该被命名为“长模式”。)
因此,如果我们在长 64 位模式下运行实际测试:
并在 FNSTENV 指令之后立即中断,并具有以下上下文状态:
它返回的内存布局如下:
我不会隐瞒,这很奇怪。 (从截断的 RIP 寄存器到看起来很奇怪的操作码的任何内容。)但我猜出于遗留原因,它仍然受支持。
好消息是,在对现代 x64 代码进行编码时,这些古老的 x87 FPU 指令现在已经没有多少用途了。在这种情况下,您绝对应该坚持使用 XMM0 到 XMM15 寄存器及其相关指令以满足您的浮点需求。
【讨论】:
我想用不同的方法来回答这个问题。
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 都会指向导致异常的指令。
有很多这样的“特殊”说明。
例如,您可能会惊讶于much NOP's there are in x86,
他们通常给其他指令起别名。
Intel XED 可以在您的斗争中派上用场,原因如下:
Go asm 有一个名为 x86.csv 的东西,它列出了 x86 指令
采用英特尔 SDM 方式,但有时带有附加信息。
如果您将其 grep 为“FSTENV”,您将看到与之关联的“伪”标签。
请注意,x86.v0.2.csv 可能会遗漏一些说明,尤其是来自较新扩展的说明(不过,这将在 v0.3 中修复)。
【讨论】: