我正在学习Simply FPU tutorial。因此,对于我自己的锻炼,我想学习如何在装配中划分浮点数。假设我打算将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检查了代码
我猜结果是正确的,但不知怎的,它被printf搞砸了?
答案 0 :(得分:4)
Upvote使用该教程,非常好:)
那里有几个问题:
st(0)
,而st(7)
将是st(1)
和st(0)
。寄存器编号是固定的,它始终位于顶部st(0)
,但是桶旋转。加载某些内容时,它将为st(0)
。如果您之后加载其他内容,则桶会旋转,您之前拥有的内容将移至st(1)
,当前值将放入st(0)
。fld
和fst
invoke
宏知道如何传递浮点数
printf
我建议您使用调试器来单步执行代码,这样您就可以看到正在发生的事情,甚至不需要尝试使用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
并将其从堆栈中删除,因此所有寄存器现在都是空的。