8

これはちょっと変わった話ですが、今日、私は GNU アセンブラーをいじっていました (少なくとも構文を読めるようにしたいのです)。つまり、0から100まで行きたいだけで、その間ずっと数字を出力しています。だから数分後、私はこれを思いつきます:

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,   %ebx # The ending point.

_loop:
    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

これから得られるのは、3回何度も印刷されたものだけです。私が言ったように、ほんの少し不自然な例なので、あまり心配しないでください。生死の問題ではありません。

(フォーマットは少し乱れていますが、大きな問題はありません)。

4

6 に答える 6

12

呼び出されたプロシージャがレジスタに対して行うことを信頼することはできません。レジスタをスタックにプッシュし、printf を呼び出した後にポップして戻すか、インクリメント値とエンドポイント値をメモリに保持し、必要に応じてレジスタに読み書きします。

以下がうまくいくことを願っています。pushl には同等の popl があり、追加の数をスタックにプッシュできると想定しています。

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,       %ebx # The ending point.

_loop:
    # Remember your registers.
    pushl   %eax
    pushl   %ebx

    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # reinstate registers.
    popl   %ebx
    popl   %eax

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:
于 2008-08-21T07:47:00.007 に答える
6

私は _printf にあまり詳しくありませんが、eax を変更する可能性がありますか? Printf は、印刷された文字数を返す必要があります。この場合は、'0' と '\n' の 2 つです。これは eax で返されると思います。インクリメントすると 3 になり、これが印刷に進みます。カウンター用に別のレジスタを使用する方がよい場合があります。

于 2008-08-21T06:28:08.917 に答える
5

自分で保存しなくても、「callee-saved」のレジスタを安全に使用できます。x86 では、これらは edi、esi、および ebx です。他のアーキテクチャにはさらに多くの機能があります。

これらは ABI リファレンスに記載されています: http://math-atlas.sourceforge.net/devel/assembly/

于 2008-09-04T02:32:11.960 に答える
3

適切に作成された関数は通常、すべてのレジスタをスタックにプッシュし、終了時にそれらをポップして、関数中に変更されないようにします。例外は、戻り値を含む eax です。printf のようなライブラリ関数はこのように書かれている可能性が高いので、私は Wedge が提案するようにはしません:

他の変数についても同じことを行う必要があります。ローカル変数を格納するためにレジスタを使用することは、それをサポートするのに十分なレジスタを備えたアーキテクチャ (例: EPIC、amd64 など) に限定されています。

実際、私の知る限り、コンパイラは通常、この問題に正確に対処するために関数をそのようにコンパイルします。

@seanyboy、あなたの解決策はやり過ぎです。必要なのは、eax を ecx などの他のレジスターに置き換えることだけです。

于 2008-08-21T09:46:17.583 に答える
1

ネイサンは正しい軌道に乗っています。サブルーチンを呼び出した後、レジスタ値が変更されないと仮定することはできません。実際、それらが変更されると想定するのが最善です。そうしないと、サブルーチンがその作業を実行できなくなります (少なくとも x86 のようなレジスタ数の少ないアーキテクチャの場合)。値を保存したい場合は、それをメモリに保存する必要があります (たとえば、スタックにプッシュし、その場所を追跡します)。

他の変数についても同じことを行う必要があります。ローカル変数を格納するためにレジスタを使用することは、それをサポートするのに十分なレジスタを備えたアーキテクチャ (例: EPIC、amd64 など) に限定されています。

于 2008-08-21T08:00:47.960 に答える
-1

たとえば、変更する必要のないレジスタを使用するように書き換えることができます%ebp。最初にそれらをスタックにプッシュし、ルーチンの最後にそれらをポップするようにしてください。

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    push    %ecx
    push    %ebp
    movl    $0, %ecx # The starting point/current value.
    movl    $100,       %ebp # The ending point.

_loop:
    # Display the current value.
    pushl   %ecx
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %ecx, %ebp
    je    _end

    # Increment the current value.
    incl    %ecx
    jmp _loop   

_end:
    pop     %ebp
    pop     %ecx
于 2008-08-21T20:48:29.033 に答える