DOS has int 21h / AH=08H: Console input without echo.
Is there something similar for Linux? If I need to process the entered value before it is displayed in the terminal.
DOS has int 21h / AH=08H: Console input without echo.
Is there something similar for Linux? If I need to process the entered value before it is displayed in the terminal.
Under Linux, it is the tty that buffers the typed chars before "sending" them to the requesting program.
This is controlled through the terminal mode: raw (no buffering) or cooked (also known respectively as non-canonical and canonical mode).
These modes are actually attributes of the tty, which can be controlled with tcgetattr and tcsetattr.
The code to set the terminal in non-canonical mode without echo can be found, for example, here (more info on the VTIME and VMIN control chars can be found here).
That's C, so we need to translate it into assembly.
From the source of tcgetattr we can see that the tty attributes are retrieved through an IOCTL to stdin with the command TCGETS (value 0x5401) and, similarly, they are set with an IOCTL with the command TCSETS (value 0x5402).
The structure read is not the struct termios  but struct __kernel_termios which is basically a shortened version of the former.
The IOCTL must be sent to the stdin file (file descriptor STDIN_FILENO of value 0).
Knowing how to implement tcgetattr and tcsetattr we only need to get the value of the constants (like ICANON and similar).
I advise using a compiler (e.g. here) to find the values of the public constants and to check the structure's offsets.
For non-public constants (not visible outside their translation units) we must resort to reading the source (this is not particularly hard, but care must be taken to find the right source).
Below a 64-bit program that invokes the IOCTLs to get-modify-set the TTY attribute in order to enable the raw mode.
Then the program waits for a single char and displays it incremented (e.g. a -> b).
Note that this program has been tested under Linux (5.x) and, as the various constants change values across different clones of Unix, it is not portable.
I used NASM and defined a structure for struct __kernel_termios, I also used a lot of symbolic constants to make the code more readable. I don't really like using structures in assembly but NASM ones are just a thin macro layer (it's better to get used to them if you aren't already).
Finally, I assume familiarity with 64-bit Linux assembly programming.
BITS 64
GLOBAL _start
;
; System calls numbers
;
%define SYS_READ    0
%define SYS_WRITE   1
%define SYS_IOCTL   16
%define SYS_EXIT    60
;
; __kernel_termios structure
;
%define KERNEL_NCC 19
struc termios
    .c_iflag:   resd    1       ;input mode flags
    .c_oflag:   resd    1       ;output mode flags
    .c_cflag:   resd    1               ;control mode flags
    .c_lflag:   resd    1               ;local mode flags
    .c_line:    resb    1               ;line discipline
    .c_cc:      resb    KERNEL_NCC      ;control characters
endstruc
;
; IOCTL commands
;
%define TCGETS      0x5401
%define TCSETS      0x5402
;
; TTY local flags
;
%define ECHO        8
%define ICANON      2
;
; TTY control chars
;
%define VMIN        6
%define VTIME       5
;
; Standard file descriptors
;
%define STDIN_FILENO    0
%define STDOUT_FILENO   1
SECTION .bss
    ;The char read (reserve a DWORD to make termios_data be aligned on DWORDs boundary)
    data        resd 1
    
    ;The TTY attributes
    termios_data    resb termios_size
    
SECTION .text
_start:
    ;
    ;Get the terminal settings by sending the TCGETS IOCTL to stdin
    ;
    mov edi, STDIN_FILENO       ;Send IOCTL to stdin (Less efficient but more readable)
    mov esi, TCGETS         ;The TCGETS command
    lea rdx, [REL termios_data] ;The arg, the buffer where to store the TTY attribs
    
    mov eax, SYS_IOCTL      ;Do the syscall     
    syscall
    ;
    ;Set the raw mode by clearing ECHO and ICANON and setting VMIN = 1, VTIME = 0
    ;
    and DWORD [REL termios_data + termios.c_lflag], ~(ICANON | ECHO)    ;Clear ECHO and ICANON
    mov BYTE [REL termios_data + termios.c_cc + VMIN], 1
    mov BYTE [REL termios_data + termios.c_cc + VTIME], 0
    
    ;
    ;Set the terminal settings
    ;
    mov edi, STDIN_FILENO       ;Send to stdin (Less efficient but more readable)
    mov esi, TCSETS         ;Use TCSETS as the command
    lea rdx, [REL termios_data] ;Use the same data read (and altered) before
    
    mov eax, SYS_IOCTL      ;Do the syscall
    syscall
    ;
    ;Read a char
    ;
    mov edi, STDIN_FILENO       ;Read from stdin (Less efficient but more readable)
    lea rsi, [REL data]     ;Read into data
    mov edx, 1          ;Read only 1 char
    
    mov eax, SYS_READ       ;Do the syscall (Less efficient but more readable)
    syscall
    
    ;
    ;Increment the char (as an example)
    ;
    inc BYTE [REL data]
    
    ;
    ;Print the char
    ;
    mov edi, STDOUT_FILENO      ;Write to stdout
    lea rsi, [REL data]     ;Write the altered char
    mov edx, 1          ;Only 1 char to write
    
    mov eax, SYS_WRITE      ;Do the syscall
    syscall
    
    ;
    ;Restore the terminal settins (similar to the code above)
    ;
    mov edi, STDIN_FILENO
    mov esi, TCGETS
    lea rdx, [REL termios_data]
    mov eax, SYS_IOCTL
    syscall
    ;Set ECHO and ICANON
    or DWORD [REL termios_data + termios.c_lflag], ICANON | ECHO
    mov edi, STDIN_FILENO   
    mov esi, TCSETS
    lea rdx, [REL termios_data]
    mov eax, SYS_IOCTL
    syscall 
    
    ;
    ;Exit
    ;
    xor edi, edi
    mov eax, SYS_EXIT
    syscall