在FPU中访问标记字的正确方法是什么?

时间:2017-08-24 00:56:47

标签: assembly x86 masm masm32 x87

汇编是我学习的第一种编程语言 我已经阅读了与FPU相关的英特尔IA-32手册和http://www.ray.masmcode.com/tutorial/fpuchap3.htm#fstenv部分。我找到了32位的内存布局:Here

根据上面的图表,标记字在地址8上占用16位。 不幸的是,英特尔指南和光线指南使用了一个语法示例。

我的问题是,当我在第二次循环中加载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,调试器会出于某种原因显示弹出的值。

1 个答案:

答案 0 :(得分:2)

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

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

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恢复它。

  

汇编是我正在学习的第一种编程语言

我强烈建议学习一些C,这样你就可以轻松查看C编译器asm输出,看看编译器如何做你在C中理解的东西。(例如在http://gcc.godbolt.org/上)

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

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

  

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

它应该向您显示寄存器是否被标记的某种指示&#34; free&#34;或在使用中。

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

  

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

IIRC,当x87堆栈已满时发生。