組み立てからできますが、簡単ではありません。 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
ソース。