2013-01-08 74 views
1

我目前在學Simply FPU tutorial。所以作爲我自己的練習,我想了解如何在彙編中劃分浮點。假設我將以17.1分48.6分。這是代碼。X86組件中的浮點除法給出奇怪的結果

format PE console 4.0 
entry main 
include 'win32a.inc' 

section '.data' data readable writeable 
num1 dq 48.6 
num2 dq 17.1 
result dq ? 
fmt db "%g", 10 
szBuff db 32 dup (0) 

section '.code' code readable executable 
main: 
fld qword [num1] 
fld qword [num2] 
fdivp 
fstp qword [result] 
invoke printf, fmt, result 
invoke ExitProcess, 0 


section '.idata' import data readable 
library kernel32,'kernel32.dll', msvcrt,'msvcrt.dll' 
import kernel32, ExitProcess,'ExitProcess' 
import msvcrt, printf, 'printf' 

代碼的輸出是

7.62883e + 265

錯在這裏?

如小丑的建議,我檢查使用OllyDbg的

ollydbg screenshot

我猜的結果是正確的代碼,但不知何故,它是由printf的一塌糊塗?

回答

4

給予好評的使用教程,這是非常好的:)

一對夫婦的問題有:

  • 你的價值觀會不會st(0)st(7)他們將st(1)st(0)。寄存器編號是固定的,它始終是頂部的st(0),但桶轉了。當你加載的東西將是st(0)。如果之後再加載其他東西,桶會旋轉,並且您以前的內容將移至st(1),當前值將放入st(0)
  • 確保您的彙編程序會生成適當大小的指令, 如用於fldfst
  • 確保您的invoke宏觀知道如何通過浮點 參數printf
  • 你不清理FPU堆棧(這裏不影響操作, 它只是一個普遍的問題)

我建議你使用調試器來單步執行的代碼,所以你可以看到發生了什麼,而你沒有偶數n因爲試圖使用printf而陷入混亂。


更新:在Linux上使用gdb的一個工作代碼(編輯爲清楚起見)樣品會話:

$ cat > div.s 
.intel_syntax noprefix 
.globl main 

.data 
num1: .double 48.6 
num2: .double 17.1 
fmt: .string "%g\n" 

.text 
main: 
    sub esp, 16 
    fld qword ptr [num1] # st(0)=48.6 
    fld qword ptr [num2] # st(0)=17.1, st(1)=48.6 
    fdivp     # st(0)=st(1)/st(0), one item popped 
    fstp qword ptr [esp+4] # store as argument and pop 
    mov dword ptr [esp], offset fmt 
    call printf 
    add esp, 16 
    ret 
$ gcc -masm=intel -m32 -g div.s -o div 
$ ./div 
2.84211 
$ gdb ./div 
GNU gdb (GDB) 7.3.50.20111117-cvs-debian 
(gdb) br main 
Breakpoint 1 at 0x80483c4: file div.s, line 11. 
(gdb) r 
Starting program: div 
Breakpoint 1, main() at div.s:11 
11   sub esp, 16 
(gdb) n 
main() at div.s:12 
12   fld qword ptr [num1] # st(0)=48.6 
(gdb) 
13   fld qword ptr [num2] # st(0)=17.1, st(1)=48.6 
(gdb) info float 
=>R7: Valid 0x4004c266666666666800 +48.60000000000000142  
    R6: Empty 0x00000000000000000000 
    R5: Empty 0x00000000000000000000 
    R4: Empty 0x00000000000000000000 
    R3: Empty 0x00000000000000000000 
    R2: Empty 0x00000000000000000000 
    R1: Empty 0x00000000000000000000 
    R0: Empty 0x00000000000000000000 
(gdb) n 
14   fdivp     # st(0)=st(1)/st(0), one item popped 
(gdb) info float 
    R7: Valid 0x4004c266666666666800 +48.60000000000000142  
=>R6: Valid 0x400388ccccccccccd000 +17.10000000000000142  
    R5: Empty 0x00000000000000000000 
    R4: Empty 0x00000000000000000000 
    R3: Empty 0x00000000000000000000 
    R2: Empty 0x00000000000000000000 
    R1: Empty 0x00000000000000000000 
    R0: Empty 0x00000000000000000000 
(gdb) n 
15   fstp qword ptr [esp+4] # store as argument and pop 
(gdb) info float 
=>R7: Valid 0x4000b5e50d79435e4e16 +2.842105263157894584  
    R6: Empty 0x400388ccccccccccd000 
    R5: Empty 0x00000000000000000000 
    R4: Empty 0x00000000000000000000 
    R3: Empty 0x00000000000000000000 
    R2: Empty 0x00000000000000000000 
    R1: Empty 0x00000000000000000000 
    R0: Empty 0x00000000000000000000 
(gdb) n 
16   mov dword ptr [esp], offset fmt 
(gdb) info float 
    R7: Empty 0x4000b5e50d79435e4e16 
    R6: Empty 0x400388ccccccccccd000 
    R5: Empty 0x00000000000000000000 
    R4: Empty 0x00000000000000000000 
    R3: Empty 0x00000000000000000000 
    R2: Empty 0x00000000000000000000 
    R1: Empty 0x00000000000000000000 
=>R0: Empty 0x00000000000000000000 

注意gdb打印這會是下一個執行的指令。 FPU堆棧頂部用箭頭標記,根據定義,始終爲st(0)。如果需要的話,隨後按順序遞增幷包裝。第一個轉儲顯示48.6正在加載到st(0)中,因爲它由箭頭標記,其他位置爲空。然後,17.1正在被加載到st(0),因爲箭頭已經移動(桶旋轉)。 48.6現在是st(1)FDIVP執行除法並從堆棧中刪除一個項目,因此我們以st(0)結果,其餘爲空。FSTP然後將st(0)作爲參數存儲爲printf並將其從堆棧中刪除,因此所有寄存器現在都是空的。

+0

嗨小丑,謝謝你的提示。現在我得到了正確的結果,但不知何故它沒有正確打印。我將不得不再次研究我的代碼。 – anta40

+1

正如你的反彙編所看到的那樣,'invoke printf,fmt,result'宏調用將'result'的地址放在堆棧上,而不是'printf'所期望的'result'本身。 – Jester

+0

是的,你是對的。這部分:「調用printf,fmt,result」應該替換爲「cinvoke printf,fmt,double [result]。FASM有一個叫做cinvoke的宏用於調用C函數,double是qword的C等價物。正確的版本在這裏:http://pastebin.com/11hXKwdF,所以人們可以看到區別:) – anta40