Search code examples
linuxassemblyinputnasmtty

How to 'silently' get user input from a terminal on Linux?


I need to get user input skipping every second consonant in it. So every second consonant should not be printed out (not just deleted in some process after hitting Enter).

I've got a task from my teacher in assembler language. Currently I'm using NASM on Linux and would like to stick to it.

The problem is that I need to 'overload' user input interrupt and in it I should be able to process that input (skip every second consonant). I've searched the Net and didn't find any suiting answers. Not how to 'catch' user input before buffering and printing it out, nor the way to read a single character without printing it in terminal.

I would like to see an example of replacing (or modifying) the standard input interrupt system call. That would be the ideal case.

The second option is to make your own type of 'handler' getting each char one by one without echoing it and than handling things like Backspace and Enter myself. Just like getch() in Windows C does. (or 0x16 BIOS interrupt).


Solution

  • Here is an example of what I use to retrieve a single keystroke. Keep in mind, functions keys, arrows and others like HOME, PGDN etc return more than one byte and for that reason I read up to 8 bytes so what's left in the input buffer won't become an artifact of the next write. This snippet was designed as a response system to things like;

    Continue [Y/N]

    Calling process would read character returned in AL. If it's 0x1b (27 dec / ESC) then we know it's one of the extended keys. As an example, F1 would return 0x504f1b in EAX.

    USE64
    
          sys_read      equ      0
          sys_write     equ      1
          sys_ioctl     equ     16
    
          %define      ICANON      2
          %define        ECHO      8
          %define      TCGETS      0x5401
          %define      TCPUTS      0X5402
    
                section    .text
        ; =============================================================================
        ; Accept a single key press from operator and return the result that may be
        ; up to 5 bytes in length.
    
        ;    LEAVE: RAX = Byte[s] returned by SYS_READ
        ; -----------------------------------------------------------------------------
    
          %define   c_lflag     rdx + 12
          %define      keys     rbp +  8
          %define      MASK     ICANON | ECHO
    
          STK_SIZE  equ 56              ; Room for 36 byte termios structure
    
          QueryKey:
    
                xor     eax, eax
                push    rax             ; This is where result will be stored.
    
                push    rbp
                mov     rbp, rsp
                sub     rsp, STK_SIZE
    
                push    r11             ; Modified by SYSCALL
                push    rbx
                push    rdx
                push    rcx
                push    rdi
                push    rsi             ; With size of 56, stack is now QWORD aligned
    
                mov     edi, eax            ; Equivalent to setting EDI to STDIN
                mov     esi, TCGETS
                lea     rdx, [rbp-STK_SIZE] ; Points to TERMIOS buffer on stack
                mov      al, sys_ioctl
                syscall
    
                lea     rbx, [c_lflag]
                and     byte [rbx], ~(MASK)
                inc     esi                 ; RSI = TCPUTS
                push    rsi
                mov      al, sys_ioctl
                push    rax
                syscall
    
           ; Wait for keypress from operator.
    
                lea     rsi, [keys]         ; Set buffer for input
                push    rdx
                mov     edx, 8              ; Read QWORD bytes max
                mov      al, sys_read
                syscall
    
    NOTE: The code you need could go here
    
                pop     rdx                 ; Points back to TERMIOS
                pop     rax
                pop     rsi                 ; TCPUTS again
                or      byte [rbx], MASK
                syscall
    
                pop     rsi
                pop     rdi
                pop     rcx
                pop     rdx
                pop     rbx
                pop     r11
    
                leave
                pop        rax              ; Return up to 8 characters
                ret