【问题标题】:What is the correct way to access Tag Word in FPU?在 FPU 中访问 Tag Word 的正确方法是什么?
【发布时间】:2017-08-24 00:56:46
【问题描述】:

Assembly 是我正在学习的第一门编程语言,并且 我已经阅读了 Intel IA-32 手册和与 FPU 相关的http://www.ray.masmcode.com/tutorial/fpuchap3.htm#fstenv 部分。我找到了 32 位的内存布局:

根据上面的这张图表,标签字在地址 8 上占用 16 位。 不幸的是,英特尔指南和 ray 指南都使用了语法示例。

我的问题是,当我在第二个循环期间将fld $Tvalueinc[ecx] 加载到 st(0) 时,我 WAS 得到 1#IND。

为了弄清楚我为什么会得到 #1INF,我四处寻找,发现您可以使用 TAG WORD 寄存器 (http://www.ray.masmcode.com/tutorial/fpuchap1.htm) 查看 FPU 状态。

使用这部分代码:

fstenv [ebx-16]
fwait
fldenv [ebx-16]
mov    eax, [ebx-16]

如果代码有效,则数据应更改每个循环,但保持不变。

我在尝试访问 FPU 时做错了什么? 您不必担心我的代码的意义,以及为什么我无法访问 TAG WORD。

    .386
.model flat, stdcall
option casemap :none  

includelib \masm32\lib\msvcrt.lib
sprintf proto C :vararg
includelib \masm32\lib\user32.lib 
MessageBoxA proto :ptr,:ptr,:ptr,:DWORD
includelib \masm32\lib\kernel32.lib
ExitProcess proto :dword 

.data
   _title db "Result",13,10,0
   $interm db "%0.4f","+","%0.5f",13,10,0
   Aval REAL8 1.000
   Bval REAL8 -2.000
   Cval REAL8 19.000
   _fourval REAL8 4.000
   $Tvalueinc REAL4 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0
   $sampleval real10 4478784.0
   squareroot dq ?
   $prevCW dw ?
   $Tagword dd ?




.code
main PROC
LOCAL szBuf[9]:byte
fstcw $prevCW
fwait
fld Bval ;  [loads first instance of b]]
fmul Bval ; [b*b = b^2]
fld Aval ;[Load a (a*c)]
fmul Cval ;(a*c)
fmul _fourval ;[4*a*c]
fsubp;[b^2-4*a*c]
ffree st(7)
ftst ;compare ST(0) with 0.0
fstsw ax ;[store camparison results in ax]
fwait;wait
sahf ;transfer flags from AH register
mov ecx, 04h
fld $sampleval

jb _negative ;jump if <0
fsqrt ;sqrt(b^2-4*a*c)
jmp Finished



_negative:


$repeat:
mov ax, $prevCW


push eax
fld $Tvalueinc[ecx]
fstenv [ebx-16]
fwait
fldenv [ebx-16]
mov eax,[ebx-16]
fdiv
fldcw [esp]

fstsw ax

FRNDINT
fldcw $prevCW
pop eax


jne $repeat






Finished:  

   fstp squareroot
   mov eax, dword ptr squareroot
   mov edx, dword ptr squareroot[4h]
   invoke sprintf, addr szBuf, offset $interm, eax, edx
   invoke MessageBoxA, 0, addr szBuf, offset _title, 0
   invoke ExitProcess, 0



main ENDP
END main

注意:我使用 Visual Studio 2017,调试器出于某种原因一直显示弹出的值。

【问题讨论】:

  • 如果您刚开始学习 asm,您可能不知道这一点,但是 x87 浮点已经过时了。 64 位代码和大多数 32 位代码使用 xmm 寄存器中的 SSE/SSE2 指令进行数学运算。例如,movss xmm0, [var1]/mulss xmm0, [var2]/movss [result], xmm0。或者更好的是,将所有内容保存在 xmm 寄存器中,因为它是一个平面寄存器集,而不是 x87 之类的堆栈。有关更多信息,请参阅x86 tag wiki。这并不是一个坏问题,但值得指出。
  • AFAIK,在现代 CPU 上你永远不需要fwait(至少从最初的 Pentium 开始)。所以你可以只使用fnstenv 而不是fstenv(实际上是fwait + fnstenv)。

标签: assembly x86 masm masm32 x87


【解决方案1】:

根据您链接的图表,控制字和状态字存储在标记字之前。 (但请注意,这是实模式布局。您在 32 位保护模式下运行。(实际上,如果您在 64 位操作系统下运行 32 位代码,则在兼容模式下,但它本质上与 32- 32 位操作系统下的位保护模式)。mov eax, [ebx-16] 将加载控制字,加上高 16 位中的任何内容。(IDK 是否保证为零。)

尝试movzx eax, word ptr [ebx - 8] 加载从ebx-16 计数的第三个四字节双字的低半部分。

fstenv    [ebx-16]
;fwait                  ; you don't need this
;fldenv    [ebx-16]     ; you don't need to do this.
mov eax,  [ebx-16]      ; loads the x87 control word into eax, plus padding

试试这个:

fnstenv     [ebx-16]
movzx       eax, word ptr [ebx-16 + 8]  ; should be the tag word if that's the right diagram

FP 异常(如异常或无效结果)在进程启动时默认全部被屏蔽,因此它们仅在 x87 状态字中设置位,而不是在您执行除零之类的操作时实际跳转到异常处理程序。由于fstenv只是通过屏蔽所有异常来修改FP环境,所以不需要用fldenv恢复。


Assembly 是我学习的第一门编程语言

我强烈建议您学习一些 C 语言,这样您就可以轻松查看 C 编译器的 asm 输出,了解编译器如何执行您在 C 中理解的操作。(例如在 http://gcc.godbolt.org/ 上)

另外,我建议不要在 x87 FP 上花费太多时间,因为它很奇怪而且已经过时。 x86 中没有其他东西使用带有标签的寄存器堆栈来说明哪些是有效的。现代 x86 代码使用 SSE2 指令进行 FP 数学运算。

请参阅 标签 wiki 以获取许多好的链接。

我使用 Visual Studio 2017,调试器出于某种原因一直显示弹出的值。

它应该向您显示寄存器是标记为“免费”还是使用中的某种指示。

将寄存器标记为空闲与更改其中的值是分开的。 x87 堆栈使用标签来跟踪哪个底层寄存器实际上是当前堆栈的顶部,以及哪些是空闲的。修改这些标签并不会清除数据,我猜fstenv 仍然会将值存储到调试器可以获取的内存中。

我的问题是,当我在第二次循环中将 fld $Tvalueinc[ecx] 加载到 st(0) 时,我得到了 1#IND。

IIRC,当 x87 堆栈已满时会发生这种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 2014-08-16
    • 2022-01-25
    相关资源
    最近更新 更多