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, скомпилированный с отдельным ассемблером.