Itu bisa dilakukan dari perakitan, tetapi itu tidak mudah. Anda tidak dapat menggunakan int 21h, itu adalah panggilan sistem DOS dan tidak tersedia di Linux.
Untuk mendapatkan karakter dari terminal di bawah sistem operasi mirip UNIX (seperti Linux), Anda membaca dari STDIN (file nomor 0). Biasanya, panggilan sistem baca akan diblokir hingga pengguna menekan enter. Ini disebut mode kanonik. Untuk membaca satu karakter tanpa menunggu pengguna menekan enter, Anda harus menonaktifkan mode kanonis terlebih dahulu. Tentu saja, Anda harus mengaktifkannya kembali jika menginginkan input baris nanti, dan sebelum program Anda ditutup.
Untuk menonaktifkan mode kanonik di Linux, Anda mengirim IOCTL (IO ControL) ke STDIN, menggunakan ioctl syscall. Saya menganggap Anda tahu cara melakukan panggilan sistem Linux dari assembler.
Syscall ioctl memiliki tiga parameter. Yang pertama adalah file untuk mengirim perintah ke (STDIN), yang kedua adalah nomor IOCTL, dan yang ketiga biasanya berupa penunjuk ke struktur data. ioctl mengembalikan 0 jika berhasil, atau kode kesalahan negatif jika gagal.
IOCTL pertama yang Anda butuhkan adalah TCGETS (nomor 0x5401) yang mendapatkan parameter terminal saat ini dalam struktur termios. Parameter ketiga adalah penunjuk ke struktur termios. Dari sumber kernel, struktur termios didefinisikan sebagai:
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 */
};
di mana tcflag_t panjangnya 32 bit, cc_t panjangnya satu byte, dan NCCS saat ini didefinisikan sebagai 19. Lihat manual NASM untuk cara mudah menentukan dan mencadangkan ruang untuk struktur seperti ini.
Jadi, setelah Anda mendapatkan termios saat ini, Anda perlu menghapus bendera kanonis. Bendera ini ada di bidang c_lflag, dengan topeng ICANON (0x00000002). Untuk menghapusnya, hitung c_lflag AND (BUKAN ICANON). dan simpan hasilnya kembali ke bidang c_lflag.
Sekarang Anda perlu memberi tahu kernel tentang perubahan Anda pada struktur termios. Gunakan TCSETS (nomor 0x5402) ioctl, dengan parameter ketiga atur alamat struktur termios Anda.
Jika semuanya berjalan dengan baik, terminal sekarang dalam mode non-kanonik. Anda dapat memulihkan mode kanonis dengan menyetel bendera kanonis (dengan ORing c_lflag dengan ICANON) dan memanggil ioctl TCSETS lagi. selalu pulihkan mode kanonis sebelum Anda keluar
Seperti yang saya katakan, itu tidak mudah.
Saya perlu melakukan ini baru-baru ini, dan terinspirasi oleh jawaban Callum yang luar biasa, saya menulis yang berikut (NASM untuk 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
(Catatan editor:jangan gunakan int 0x80
dalam kode 64-bit:Apa yang terjadi jika Anda menggunakan ABI Linux 0x80 int 32-bit dalam kode 64-bit? - itu akan merusak PIE yang dapat dieksekusi (di mana alamat statis tidak dalam 32 bit rendah), atau dengan buffer termios di tumpukan. Ini benar-benar berfungsi dalam executable non-PIE tradisional, dan versi ini dapat dengan mudah dipindahkan ke mode 32-bit.)
Anda kemudian dapat melakukan:
call canonical_off
Jika Anda membaca sebaris teks, Anda mungkin juga ingin melakukan:
call echo_off
sehingga setiap karakter tidak digaungkan saat diketik.
Mungkin ada cara yang lebih baik untuk melakukan ini, tetapi ini bekerja untuk saya pada instalasi Fedora 64-bit.
Informasi lebih lanjut dapat ditemukan di halaman manual untuk termios(3)
, atau di termbits.h
sumber.