FLD инструкция с плавающей точкой

Согласно http://cs.smith.edu/~thiebaut/ArtOfAssembly/CH14/CH14-4.html

14.4.4.1 Инструкция FLD
поле mem_32
поле 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;     
}

1 ответ

По крайней мере три вещи могут пойти не так, как надо здесь. Одним из них является синтаксис ассемблера. Второе - это архитектура набора команд. Третья модель памяти (16-битная или 32-битная, сегментированная и плоская). Я подозреваю, что представленные примеры нацелены на 16-битную сегментированную архитектуру, поскольку 8087 относится к тому же периоду, но компиляторы C++ в основном пришли после защищенного режима 386+.

FPU 8087 не поддерживает инструкции, которые перемещают данные между регистрами общего назначения (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
 fld  word ptr [bp]
 pop  ax                 // clean the stack and restore bp
 pop  bp
 .. or ..
 mov  bx, 10
 push bx
 mov  bx, sp
 fld  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]
     }
 }

Примечание для ясности: BX действительно 16-битный регистр. Это все еще существует, но инструкция FLD не поддерживает это. Синтаксис для первых двух строк должен быть:

Мов Эбкс, 4

поле ebx

Когда это встроено в C/C++, синтаксис __asm ​​может поддерживаться компилятором. Ответ выше предполагает отдельный файл ASM, скомпилированный с отдельным ассемблером.

Другие вопросы по тегам