Dá se to udělat montáží, ale není to jednoduché. Nemůžete použít int 21h, to je systémové volání DOS a není dostupné pod Linuxem.
Chcete-li získat znaky z terminálu v operačních systémech podobných UNIX (jako je Linux), čtete ze STDIN (číslo souboru 0). Normálně se čtené systémové volání zablokuje, dokud uživatel nestiskne enter. Toto se nazývá kanonický režim. Chcete-li přečíst jeden znak bez čekání, až uživatel stiskne klávesu enter, musíte nejprve deaktivovat kanonický režim. Samozřejmě jej budete muset znovu povolit, pokud budete chtít řádkový vstup později a před ukončením programu.
Chcete-li zakázat kanonický režim na Linuxu, odešlete IOCTL (IO Control) do STDIN pomocí syscall ioctl. Předpokládám, že víte, jak provádět systémová volání Linuxu z assembleru.
Systémové volání ioctl má tři parametry. První je soubor, do kterého se má příkaz odeslat (STDIN), druhý je číslo IOCTL a třetí je obvykle ukazatel na datovou strukturu. ioctl vrátí 0 při úspěchu nebo záporný chybový kód při selhání.
První IOCTL, který potřebujete, je TCGETS (číslo 0x5401), který získá aktuální parametry terminálu ve struktuře termios. Třetí parametr je ukazatel na strukturu termios. Ze zdrojového kódu jádra je struktura termios definována jako:
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 */
};
kde tcflag_t je 32 bitů, cc_t je jeden bajt a NCCS je aktuálně definováno jako 19. Jak můžete pohodlně definovat a rezervovat prostor pro struktury, jako je tato, najdete v příručce NASM.
Takže jakmile budete mít aktuální termios, musíte vymazat kanonickou vlajku. Tento příznak je v poli c_flag s maskou ICANON (0x00000002). Chcete-li jej vymazat, vypočítejte c_lflag AND (NE ICANON). a výsledek uložte zpět do pole c_flag.
Nyní musíte oznámit jádru změny ve struktuře termios. Použijte TCSETS (číslo 0x5402) ioctl, s třetím parametrem nastavte adresu vaší termiosové struktury.
Pokud vše půjde dobře, terminál je nyní v nekanonickém režimu. Kanonický režim můžete obnovit nastavením kanonického příznaku (spojením ORing c_lflag s ICANON) a opětovným voláním TCSETS ioctl. Před ukončením vždy obnovte kanonický režim
Jak jsem řekl, není to snadné.
Potřeboval jsem to udělat nedávno a inspirován Callumovou vynikající odpovědí jsem napsal následující (NASM pro 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
(Poznámka redakce:nepoužívejte int 0x80
v 64bitovém kódu:Co se stane, když použijete 32bitové int 0x80 Linux ABI v 64bitovém kódu? - zlomil by se ve spustitelném souboru PIE (kde statické adresy nejsou v nízkých 32 bitech) nebo s vyrovnávací pamětí termiosů na zásobníku. Ve skutečnosti funguje v tradičním spustitelném souboru bez PIE a tuto verzi lze snadno přenést do 32bitového režimu.)
Poté můžete:
call canonical_off
Pokud čtete řádek textu, pravděpodobně budete chtít také:
call echo_off
aby se každý znak při psaní neozýval.
Možná existují lepší způsoby, jak to udělat, ale mně to funguje na 64bitové instalaci Fedory.
Více informací lze nalézt v manuálové stránce pro termios(3)
nebo v termbits.h
zdroj.