2015-02-11 59 views
0

我在读this,我注意到他们的例子显示fld加载值到不同的位置(st0,st1,然后回到st0),而没有指定加载到哪里。我假设fild以类似的方式工作,因为它只是加载整数的手段(或者这是我的理解),但我可能是错的。所以我的问题是这样的:fld在哪里,更具体地说是fild加载值?有没有一个参数来指定要使用哪个fpu寄存器,还是只是循环使用8,还是有一些完全不同的方式,我错过了?NASM x87 FILD指令

我特别使用的代码试图将3个数字相乘。我假设一种方法是加载到st0,然后加载到st1,然后加载到st2,然后fmul st0和st1(结果st0),然后fmul st0和st2。的代码如下:

mov dword [ebp-8], 4 
mov dword [ebp-12], ecx 
fild dword [ebp-4] 
fild dword [ebp-8] 
fild dword [ebp-12] 
fmul st0, st1 
fmul st0, st2 
fistp dword [ebp-8] 
mov eax, dword [ebp-8] 

ECX = 5和[EBP-4] = 5

此代码崩溃,使用OllyDbg的我看到,有一个访问冲突在00000069但当前没有被包含在任何寄存器中。

所以,是否有一种方法来指定fild加载值的位置,有没有一种很好的方法来确定它们应该去哪里,如果我在循环中运行它,是否会改变任何东西?

- EDIT 3 - 大部分是固定的,一件大事就是fmul不会将值推送到st0,它只会覆盖st0中的任何值。新代码:

mov dword [ebp-8], ecx 
fild dword [ebp-4] 
fild dword [four] 
fild dword [ebp-8] 
fmul st1 
fmul st2 
fistp dword [ebp-12] 
mov eax, dword [ebp-12] 

这个循环和递减直到ecx == 2,然后试图fild 2和1给出了同样的bad -NAN FFFF C0000000 00000000正如前面。我不确定3与2或1有什么不同(除了变小),但是它开始给出不好的值。我应该注意到ERROR_MOD_NOT_FOUND被抛出,虽然我不确定是什么意思,因为所有的cpu和fpu寄存器都应该可以访问。

--edit 2-- 固定的流行的东西,帕勒姆Alvani表现出与文档的东西:

mov dword [ebp-8], ecx  ; moves eax (starts as 5) into local var (fild can't take a cpu register) 
fild dword [ebp-4]   ; starts as 5, moves down with outer loop 
fild dword [four]   ; the integer 4 
fild dword [ebp-8]   ; starts as 5, moves down with inner loop 
fmul st0, st1    ; 0 := 0 * i 
fmul st0, st3 
fistp dword [ebp-12]  ; move st0 to local var 
mov eax, dword [ebp-12]  ; move local var to eax 

这使得推动5,然后推bad -NAN FFFF C0000000 00000000两次。 fmul似乎没有做任何事情(可能是因为不好的价值)。有没有更好的方法来加载值?看起来我的做法与fild错误,但根据第一个链接中提供的示例,并且按照定义,here,fild只需将它推入st0即可。

--edit 1-- 作为小丑的建议,我现在突然离开FPU堆栈的每一个循环:

mov dword [ebp-8], 4 
mov dword [ebp-12], ecx 
fild dword [ebp-4] 
fild dword [ebp-8] 
fild dword [ebp-12] 
fmul st0, st1 
fmul st0, st2 
fstp st2 
fstp st1 
fistp dword [ebp-8] 
mov eax, dword [ebp-8] 

此代码仍然崩溃。访问冲突在00000009,st0-4是0,st5 = 100,st6 = 4,st7 = 4

+2

的x87 FPU是基于栈,'FILD'将一个新值顶端堆栈,它总是被称为'st0'。因此,前一个'st0'变成'st1',依此类推。特别是在循环中要小心,因为你只有8个寄存器。我不知道wikibook,但我可以推荐[简单FPU教程](http://www.website.masmforum.com/tutorials/fptute/fpuchap1.htm)。 – Jester 2015-02-11 01:57:09

+0

那么我该如何弹出?那是什么fistp呢? – dlkulp 2015-02-11 02:00:43

+0

是的,大多数指令中的'P'表示流行音乐。也有非流行的变体。 – Jester 2015-02-11 02:02:37

回答

1

您的原始代码是正确的。 fldfild将加载的值压入x87堆栈。这种推动总是将值放入st0,将st0的旧值移至st1,将旧的st1移至st2,依此类推。

fild dword [ebp-4] ; st0 = x 
fild dword [ebp-8] ; st1 = x, st2 = y 
fild dword [ebp-12] ; st2 = x, st1 = y, st0 = z 
fmul st0, st1  ; st2 = x, st1 = y, st0 = z * y 
fmul st0, st2  ; st2 = x, st1 = y, st0 = z * y * x 
fistp dword [ebp-8] ; st1 = x, st0 = y 

您的代码可能会崩溃,因为ebp指向一个不好的地方,或者因为错误在你的代码,而不是你贴一部分的另一部分。你不会说哪条指令崩溃。在发生崩溃时,程序计数器(pc)指向崩溃指令。

我把你的代码放在一个简短的程序中,然后在我的OpenBSD/amd64机器的gdb上成功运行它。

section .data 
    dd 0 
    dd 0 
    dd 5 
space: 

section .text 
global main 
main: 
    mov ebp, space 
    mov ecx, 5 
    mov dword [ebp-8], 4 
    mov dword [ebp-12], ecx 
    fild dword [ebp-4] 
    fild dword [ebp-8] 
    fild dword [ebp-12] 
    fmul st0, st1 
    fmul st0, st2 
    fistp dword [ebp-8] 
    mov eax, dword [ebp-8] 
    int 3 

为了组装和运行:

$ nasm -felf64 fmul3.s && gcc -nopie -o fmul3 fmul3.o 
$ gdb fmul3 
... 
(gdb) run 
... 
Program received signal SIGTRAP, Trace/breakpoint trap. 
... 
(gdb) x/3wd (char *)&space - 12 
0x601000 <__data_start>:  5  100  5 
(gdb) print (int)$rax 
$1 = 100 

程序成功乘以5 * 4 * 5 = 100。

+1

可能是一个好主意,提到通常的调用约定要求x87堆栈在调用/返回时为空(或者在st0中有一个返回值)。这段代码在x87堆栈中留下了一些值,所以重复运行会溢出它(如果我正确记得的话,会导致NaN)。我想OP已经知道了,因为使用'fstp st0'来更新循环内的FP堆栈。 – 2018-01-09 00:41:24