5

だから私はNASM構文でx86 Linuxアセンブリを学んでいます(ああ、これはもうありません、あなたは皆考えています)。EAX の値を標準出力に単純に出力するサブルーチンを作成しようとしています。コードは実行され、エラーなしで終了しますが、何も出力されません。理由がわかりません。まず、私が作業しているファイルは次のとおりです。

segment .bss
    to_print:   resd 1

segment .text
    global print_eax_val

print_eax_val:                  ;       (top)
    push    dword ebx           ;Stack:  edx
    push    dword ecx           ;        ecx
    push    dword edx           ;        ebx
                                ;       (bot)

    mov     ecx,eax             ;ecx = eax

    mov     [to_print],ecx      ;to_print = ecx

    mov     eax, 4              ;sys_write
    mov     ebx, 1              ;to stdout
    add     ecx, 47             ;add 47 for ASCII numbers
    mov     edx, 2              ;double word = 2 bytes
    int     0x80

    mov     eax, [to_print]     ;eax = original val
    pop     edx                 ;pop the registers back from the stack
    pop     ecx
    pop     ebx                 ;Stack: empty

    ret

これは私のメイン ファイルから呼び出され、次のようになります (何か抜本的なものがない限り、これはおそらく無関係です)。

segment .data
        hello   db      "Hello world!", 0
        newline db      0xA
        len     equ $ - hello
        len2    equ $ - newline

segment .text
        extern print_nl
        extern print_eax_val
        global main

main:
        enter   0,0

        call    print_nl

        mov     eax, 1

        call    print_eax_val

        mov     ebx, 0          ;exit code = 0 (normal)
        mov     eax, 1          ;exit command
        int     0x80            ;ask kernel to quit

print_nl改行を定義して出力する別のサブルーチンです。これは正常に実行され、期待どおりに新しい行が出力されます。

問題は私のsys_write呼び出しの長さパラメータに関係していますか? のサイズである 2 を与えています。これは、 で予約したレジスタとラベルdwordの両方のサイズです。必死になって長さを1、4、8、16、32に変更してみました...何もうまくいきませんでした。EAXto_printresd 1

編集:疑問に思っている人のために、コードを修正した方法を次に示します:(変更した行にアスタリスクを付けます):

segment .bss
    to_print:   resd 1

segment .text
        global print_eax_val

print_eax_val:                      ;       (top)
        push    dword ebx           ;Stack:  edx
        push    dword ecx           ;        ecx
        push    dword edx           ;        ebx
                                    ;       (bot)

        mov     ecx,eax             ;ecx = eax

        mov     [to_print],ecx        ;to_print = ecx

****    add     dword [to_print], 48

        mov     eax, 4              ;sys_write
        mov     ebx, 1              ;to stdout
****    mov     ecx, to_print
        mov     edx, 2
        int     0x80

****    sub     dword [to_print], 48
        mov     eax, [to_print]     ;eax = original val
        pop     edx                 ;pop the registers back from the stack
        pop     ecx
        pop     ebx                 ;Stack: empty

        ret

基本的に、値自体ではなく、印刷するブロックのアドレスecxを含める必要があります。選択した回答で指摘されているように、これはeax が 0-9 の範囲にある場合にのみ機能します。

編集 2 : sys_write の 2 番目のパラメーター (に格納されているものedx) について少し混乱しました。バイト数を指しているだけだと思います。したがって、dword私が使用していた a の場合、ダブルワードは 4 バイトまたは 32 ビットであるため、そこで 4 を使用するのが適切です。x86 はリトルエンディアンなので、うまくいったと思います。したがって、メモリ内では、 の 16 進値は次のto_printようになります。

90 00 00 00

長さ 2 を指定すると、sys_write は次のようになります。

90 00

したがって、幸いにも値が破損することはありません。

to_print後で代わりにバイトとして保存するようにコードを変更し、代わりに... を使用resb 1してアクセスし、 9 を超える値を与えるつもりはないので、ここではバイトで問題ありません。bytedwordto_print

4

2 に答える 2

4

私はアセンブラーに非常に慣れていませんが、書き込まれるバッファーのアドレスが含まれている必要があるときに、ECX に値 48 が含まれているようです。

あなたが意図したのprint_eax_valは、EAXのバイナリ値を取得し、ASCIIオフセットを数字0(47ではなく48である必要があります)に追加してから、その1文字を出力することだと思います。これを行うには、値を に格納する前に 48 を追加し、ECXto_printに のアドレスを入力to_printし、長さ (EDX) を 1 に設定します。これは、1 文字しか記述していないためです。

これは、0x0000 から 0x0009 の間の EAX 値に対してのみ機能することに注意してください。9 を超えると、他の ASCII 文字が表示されます。

EAX の任意のバイナリ値を取得し、それを出力可能な 10 進文字列に変換する方法を説明することは、SO の範囲をはるかに超えています。

于 2013-07-02T22:32:20.247 に答える
3

writeシステム コールは、 で%ecx指定された長さのでポイントされた、出力する文字のバッファを取得し%edxます。%eax一方、 のようなレジスタには 32 ビット整数が含まれます。

したがって、本当に を出力したい場合は%eax、まずその整数を一連の文字に変換する必要があります。ASCII 10 進数、16 進数、またはその他の好きな基数に変換できますが、文字にする必要があります。

于 2013-07-02T22:33:45.983 に答える