1

我目前正在学习Simply FPU 教程。所以作为自己的练习,我想学习如何在汇编中划分浮点数。假设我要将 48.6 除以 17.1。这是代码。

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

这里出了什么问题?

正如 Jester 所建议的,我使用 OllyDbg 检查了代码

ollydbg 截图

我猜结果是正确的,但不知何故它被 printf 搞砸了?

4

1 回答 1

4

赞成使用该教程,它非常好:)

那里有几个问题:

  • 你的价值观不会st(0)st(7)他们会st(1)st(0)。寄存器编号是固定的,它总是st(0)在顶部,但桶会转动。当您加载某些内容时,它将是st(0). 如果你之后加载其他东西,桶会旋转,你之前拥有的东西将移动到st(1)当前值将被放入st(0)
  • 确保您的汇编程序生成适当大小的指令,例如用于fldfst
  • 确保您的invoke宏知道如何将浮点参数传递给printf
  • 你没有清理FPU栈(这里不影响操作,只是一般问题)

我建议您使用调试器单步执行代码,这样您就可以看到正在发生的事情,您甚至不需要尝试使用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.6st(1)FDIVP执行除法并从堆栈中删除一项,因此我们最终得到结果,st(0)其余为空。FSTP然后存储st(0)asprintf参数并将其从堆栈中删除,因此所有寄存器现在都是空的。

于 2013-01-08T14:56:08.980 回答