1

私は現在、SimplyFPUチュートリアルを勉強しています。ですから、私自身の練習として、アセンブリでフローティングポイントを分割する方法を学びたいと思います。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.6ですst(1)FDIVP除算を実行し、スタックから1つのアイテムを削除するため、結果は結果にst(0)なり、残りは空になります。次に、 as引数をFSTP格納し、スタックから削除するため、すべてのレジスタが空になります。st(0)printf

于 2013-01-08T14:56:08.980 に答える