2015-02-11 97 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