NASM x87 FILD指令

时间:2015-02-11 01:52:22

标签: assembly nasm x87

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

我特别合作的代码试图将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加载值的位置,是否有一种很好的方法可以确定它们应该去哪里,如果我在循环中运行它会改变什么呢?

- 编辑3-- 大多数是固定的,排序。一件大事是fmul DOESN' T将值推到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寄存器都应该是可访问的。

- 编辑2-- 修正了Parham 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做错了,但是根据第一个链接中提供的示例,以及定义为herefild只需将st0推送到st0它

- 编辑1-- 正如Jester建议的那样,我现在每次循环都会弹出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&n; s,st5 = 100,st6 = 4,st7 = 4

1 个答案:

答案 0 :(得分: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。