GNU/Linux >> Linux の 問題 >  >> Linux

ubuntuでnasm(アセンブリ)を使用してキーボードから1文字の入力を読み取るにはどうすればよいですか?

組み立てからできますが、簡単ではありません。 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 を再度呼び出すことにより、正規モードを復元できます。 終了する前に常に正規モードに戻す

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


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

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

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

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

call canonical_off

1 行のテキストを読んでいる場合は、おそらく次の操作も実行する必要があります:

call echo_off

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

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

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


Linux
  1. netplanを使用してUbuntu18.04にIPアドレスを追加するにはどうすればよいですか?

  2. アセンブリNASMで数値を出力するには?

  3. シェルスクリプトで一文字だけ読む方法

  1. Linux –Linuxで/proc / $ pid / memから読み取る方法は?

  2. 標準入力からsedを読み取るにはどうすればよいですか?

  3. SSH 経由で接続しているときに、ホストのキーボードから入力を読み取るにはどうすればよいですか?

  1. Ubuntu – GrubがグラフィカルUIを使用/開始しないようにする方法は?

  2. Ubuntu から rbenv をアンインストールする方法

  3. Ubuntu から aria2 をアンインストールする方法