【问题标题】:FLD floating-point instructionFLD 浮点指令
【发布时间】:2013-05-12 03:32:32
【问题描述】:

根据 http://cs.smith.edu/~thiebaut/ArtOfAssembly/CH14/CH14-4.html#HEADING4-5

14.4.4.1 FLD 指令
fld mem_32
fld mem_64[bx]

我的目标是将常量 10 存储到我的 fPU 堆栈中。 为什么我不能这样做?

__asm
{
  move bx, 0x0004;
  fld dword ptr[bx] or fld bx;


  //-------
  fld 0x004; //Since it is 32 bits?
  fild 0x004;     
}

【问题讨论】:

  • 什么是你“不能做”的?您使用的是什么操作系统/编译器?地址 4 是什么,它实际上可以访问吗?大多数操作系统会阻止您使用地址 4,因为它被保护为“NULL 访问检测”。

标签: c++ assembly inline-assembly fpu x87


【解决方案1】:

这里至少会出现三件事。一是汇编器的语法。二是指令集架构。第三个是内存模型(16 位 vs 32 位,分段 vs 平面)。我怀疑提供的示例是针对 16 位分段架构的,因为 8087 是从那个时代开始的,但是 c++ 编译器主要是在 386+ 保护模式之后出现的。

8087 FPU 不支持在通用寄存器 (GPR) 和浮点堆栈之间移动数据的指令。理由是浮点寄存器使用 32、64 或 80 位,而 GPR 只有 16 位宽。取而代之的是间接地从内存中移动数据。

示例fld myRealVar 假定已提供标签(带有宽度):

 .data
 myRealVar:    .real8  1.113134241241
 myFloat:      .real4  1.1131313
 myBigVar:     .real10 1.1234567890123456
 myInt:        .word   10
 myInt2:       .word   0
 myBytes:      .byte   10 dup (0)   ;// initializes 10 bytes of memory with zeros
      
 .text        
 fld      myRealVar;  // accesses 8 bytes of memory
 fild     myInt;      // access the memory as 16 bits
 fild     myBytes;    // ## ERROR ##   can't load 8-bits of data
 fild     dword ptr myBytes;  // Should work, as the proper width is provided

首先请注意,这些示例假设数据属于一个段 .data,并且已经用

 mov  ax, segment data;  //
 mov  ds, ax

只有在此之后,内存位置0x0004 才可能包含常量 10。我强烈怀疑该模型不适用于您的内联 c++ 系统。同样在这里,汇编器必须足够聪明,以将每个标签与提供的宽度相关联并在指令中对其进行编码。

将整数加载到 FPU 的一种方法是使用堆栈:

 push bp                 // save bp
 mov  ax, 10
 push ax
 mov  bp, sp             // use bp to point to stack
 fild  word ptr [bp]
 pop  ax                 // clean the stack and restore bp
 pop  bp
 .. or ..
 mov  bx, 10
 push bx
 mov  bx, sp
 fild  word ptr ss:[bx]  // notice the segment override prefix ss
 pop  ax                 // clean the constant 10

在 32 位架构中,可以直接使用esp 指向栈顶,你的 c++ 编译器可能就是这种情况:

 sub  esp, 4
 mov  dword ptr [esp], 10  // store the integer 10 into stack
 fild  dword ptr [esp]     // access the memory
 add  esp, 4               // undo the "push" operation

一些内联汇编器可能能够使用局部变量并自动将标签替换为 ebp/esp 寄存器和正确的偏移量:

 int f1 = 10;
 void myfunc(float f2) {
     double f = 10.0;
     __asm {
        fild f1   // encoded as fild dword ptr [xxx]
        fld f     // encoded as fld qword ptr [esp + xxx]
        fld f2    // encoded as fld dword ptr [esp + xxx]
     }
 }

【讨论】:

    【解决方案2】:

    为了清楚起见,请注意: BX 确实是一个 16 位寄存器。它仍然存在,但 FLD 指令不支持它。 第一两行的语法应该是:

    mov ebx, 4

    fld ebx

    在 C/C++ 中内联时,编译器可能支持 __asm 语法。 上面的答案假定使用单独的汇编器编译了单独的 ASM 文件。

    【讨论】:

    • fld 不接受整数寄存器操作数,只接受内存或 x87 st 寄存器(复制它)。也许你的意思是fld qword ptr [ebx]?但是你为什么要使用 EBX 而不是 fld qword ptr [4]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-16
    • 2021-09-26
    • 1970-01-01
    相关资源
    最近更新 更多