7

ubuntuでnasmを使用しています。ちなみに、ユーザーのキーボードから単一の入力文字を取得する必要があるため (プログラムが y/n を要求する場合など)、キーを押して Enter キーを押さずに、入力した文字を読み取る必要があります。私はそれをたくさんグーグルで検索しましたが、私が見つけたものはすべてint 21h、「セグメンテーション違反」を引き起こすこの行 ( ) に何らかの形で関連していました。単一の文字を取得する方法、またはこのセグメンテーション違反を克服する方法を理解するのを手伝ってください.

4

3 に答える 3

16

組み立てからできますが、簡単ではありません。int 21h は使用できません。これは DOS システム コールであり、Linux では使用できません。

UNIX ライクなオペレーティング システム (Linux など) で端末から文字を取得するには、STDIN (ファイル番号 0) から読み取ります。通常、read システム コールは、ユーザーが Enter キーを押すまでブロックされます。これは正規モードと呼ばれます。ユーザーが Enter キーを押すのを待たずに 1 文字を読み取るには、最初に標準モードを無効にする必要があります。もちろん、後で行入力が必要な場合は、プログラムを終了する前に再度有効にする必要があります。

Linux で標準モードを無効にするには、ioctl syscall を使用して IOCTL (IO ControL) を STDIN に送信します。アセンブラから Linux システム コールを行う方法を知っていると思います。

ioctl syscall には 3 つのパラメーターがあります。1 つ目はコマンドを送信するファイル (STDIN)、2 つ目は IOCTL 番号、3 つ目は通常、データ構造へのポインターです。ioctl は、成功すると 0 を返し、失敗すると負のエラー コードを返します。

最初に必要な IOCTL は TCGETS (番号 0x5401) であり、termios 構造体で現在の端末パラメーターを取得します。3 番目のパラメーターは、termios 構造体へのポインターです。カーネル ソースから、termios 構造は次のように定義されます。

struct termios {
    tcflag_t c_iflag;               /* input mode flags */
    tcflag_t c_oflag;               /* output mode flags */
    tcflag_t c_cflag;               /* control mode flags */
    tcflag_t c_lflag;               /* local mode flags */
    cc_t c_line;                    /* line discipline */
    cc_t c_cc[NCCS];                /* control characters */
};

ここで、tcflag_t は 32 ビット長、cc_t は 1 バイト長、NCCS は現在 19 として定義されています。このような構造のスペースを簡単に定義および予約する方法については、NASM マニュアルを参照してください。

そのため、現在の termios を取得したら、canonical フラグをクリアする必要があります。このフラグは c_lflag フィールドにあり、マスク ICANON (0x00000002) を使用します。クリアするには、c_lflag AND (NOT ICANON) を計算します。結果を c_lflag フィールドに格納します。

ここで、termios 構造への変更をカーネルに通知する必要があります。TCSETS (番号 0x5402) ioctl を使用し、3 番目のパラメーターを termios 構造体のアドレスに設定します。

すべてがうまくいけば、端末は非標準モードになります。正規フラグを設定して (c_lflag と ICANON の OR をとることにより)、TCSETS ioctl を再度呼び出すことにより、正規モードを復元できます。終了する前に常に標準モードを復元します

私が言ったように、それは簡単ではありません。

于 2010-08-01T01:32:40.673 に答える
7

私は最近これを行う必要があり、Callumの優れた回答に触発されて、次のように書きました(x86-64のNASM):

DEFAULT REL

section .bss
termios:        resb 36

stdin_fd:       equ 0           ; STDIN_FILENO
ICANON:         equ 1<<1
ECHO:           equ 1<<3

section .text
canonical_off:
        call read_stdin_termios

        ; clear canonical bit in local mode flags
        and dword [termios+12], ~ICANON

        call write_stdin_termios
        ret

echo_off:
        call read_stdin_termios

        ; clear echo bit in local mode flags
        and dword [termios+12], ~ECHO

        call write_stdin_termios
        ret

canonical_on:
        call read_stdin_termios

        ; set canonical bit in local mode flags
        or dword [termios+12], ICANON

        call write_stdin_termios
        ret

echo_on:
        call read_stdin_termios

        ; set echo bit in local mode flags
        or dword [termios+12], ECHO

        call write_stdin_termios
        ret

; clobbers RAX, RCX, RDX, R8..11 (by int 0x80 in 64-bit mode)
; allowed by x86-64 System V calling convention    
read_stdin_termios:
        push rbx

        mov eax, 36h
        mov ebx, stdin_fd
        mov ecx, 5401h
        mov edx, termios
        int 80h            ; ioctl(0, 0x5401, termios)

        pop rbx
        ret

write_stdin_termios:
        push rbx

        mov eax, 36h
        mov ebx, stdin_fd
        mov ecx, 5402h
        mov edx, termios
        int 80h            ; ioctl(0, 0x5402, termios)

        pop rbx
        ret

(編集者注: 64 ビット コードでは使用しないでください: int 0x8064 ビット コードで 32 ビット int 0x80 Linux ABI を使用するとどうなりますか? - PIE 実行可能ファイル (静的アドレスが下位 32 ビット)、またはスタック上の termios バッファーを使用する. 実際には、従来の非 PIE 実行可能ファイルで動作し、このバージョンは 32 ビット モードに簡単に移植できます.)

その後、次のことができます。

call canonical_off

テキスト行を読んでいる場合は、おそらく次のことも実行したいと思うでしょう。

call echo_off

入力時に各文字がエコーされないようにします。

これを行うにはもっと良い方法があるかもしれませんが、64 ビットの Fedora インストールでうまくいきます。

詳細については、 のマニュアル ページtermios(3)またはtermbits.hソースを参照してください。

于 2010-10-30T15:52:31.847 に答える
-2

簡単な方法:テキストモードプログラムの場合、libncursesを使用してキーボードにアクセスします。グラフィカルプログラムの場合は、Gtk+を使用します。

難しい方法:テキストモードプログラムを想定すると、カーネルに1文字の入力が必要であることを伝えてから、多くの簿記とデコードを行う必要があります。本当に複雑です。古き良きDOSgetch()ルーチンに相当するものはありません。ここでそれを行う方法について学び始めることができます:ターミナルI/O。グラフィカルプログラムはさらに複雑です。そのための最低レベルのAPIはXlibです。

いずれにせよ、これがアセンブリにあるものは何でも、あなたは狂ったようにコーディングするでしょう。代わりにCを使用してください。

于 2010-07-22T01:02:35.753 に答える