GNU/Linux >> Znalost Linux >  >> Linux

Jak načtu jednoznakový vstup z klávesnice pomocí nasm (sestavení) pod ubuntu?

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.


Linux
  1. Jak odinstalovat rbenv z Ubuntu

  2. Jak odinstalovat aria2 z Ubuntu

  3. Jak vložit z vyrovnávací paměti v ex režimu vim?

  1. Linux – Jak číst z /proc/$pid/mem pod Linuxem?

  2. Ubuntu – Jak zabránit Grub v používání/spouštění jakéhokoli grafického uživatelského rozhraní?

  3. Jak mohu číst z /proc/$pid/mem pod Linuxem?

  1. Jak přidat IP adresu na Ubuntu 18.04 pomocí netplanu?

  2. Jak odinstalovat neovim z Ubuntu

  3. Jak odinstalovat pluginy rhythmbox z Ubuntu