问题
According to http://cs.smith.edu/~thiebaut/ArtOfAssembly/CH14/CH14-4.html#HEADING4-5
14.4.4.1 The FLD Instruction
fld mem_32
fld mem_64[bx]
My objective is store a constant 10 into my fPU stack. Why I cant do this?
__asm
{
move bx, 0x0004;
fld dword ptr[bx] or fld bx;
//-------
fld 0x004; //Since it is 32 bits?
fild 0x004;
}
回答1:
At least three things can go wrong here. One is the syntax of the assembler. The second is instruction set architecture. The third is the memory model (16 bit vs 32 bit, segmented vs flat). I suspect that the examples provided are targeted at 16-bit segmented architecture as the 8087 is from those ages, but c++ compilers mainly arrived after 386+ protected mode.
The 8087 FPU does not support instructions that move data between general purpose registers (GPR) and floating point stack. The rationale is that floating point registers use 32, 64 or 80 bits, while the GPRs are only 16 bit wide. Instead on moves data indirectly from memory.
The example fld myRealVar
presupposes that a label (with a width) has been provided:
.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
Notice first that these examples assume that data belongs to a segment .data
and that one has initialized the segment with
mov ax, segment data; //
mov ds, ax
Only after that the memory location 0x0004
could possibly contain the constant 10. I strongly suspect that that model isn't available with your inline c++ system. Also here the assembler has to be smart enough to associate each label with the provided width and encode that in the instruction.
One way to load the integer into FPU is to use the stack:
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
In 32-bit architecture one can directly use esp
to point the top of stack, which is probably the case with your c++ compiler:
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
Some inline assemblers may be able to use local variables and automatically substitute the label with ebp/esp register and the correct offset:
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:
Note for clarity: BX is a 16 bit register indeed. It still exists, but the FLD instruction doesn't support it. The syntax for the 1st two lines should be:
mov ebx, 4
fld ebx
When inlined in C/C++ the __asm styntax may be supported by the compiler. The answer above assumes a separate ASM file compiled with a separate assembler.
来源:https://stackoverflow.com/questions/16503914/fld-floating-point-instruction