Search code examples
nasmstdincpu-registers

Assembly read byte from STDIN directly into register


In x64 NASM, if I wanted to read one character from STDIN into an 8-bit register, the only method I know is this:

xor rax, rax
xor rdi, rdi
mov rsi, buffer
mov rdx, 1
syscall
mov al, byte [buffer]

Would it be possible to read the character directly into the register, without using a buffer? (I am using Ubuntu 18.04)


Solution

  • One thing you want to keep in mind is that a keypress does not necessarily equate to one byte. As an example, function keys can return up to 5 bytes. This method doesn't require a specially allocated buffer, but conceptually, space on stack could be considered a buffer.

    See comment below edi, 8 should be edx, 8

        mov     edi, 8                ; Size of one QWORD
        push    rax                   ; Could be any register, we just need 8 bytes
        mov     rsi, rsp
        xor     edi, edi              ; Essentially STDIN
        mov     eax, edi              ; SYS_READ
        syscall
        pop     rax                   ; Bytes
    

    If you pressed one of the 96 keys from space (20H) to tilde (7FH) then a your result will be returned in AL. However if AL = 27 (1BH) then the remaining bits (08-3F) will have the other relevant data.

    I use this procedure to accept a single keystroke without echo and the need to press return to accept entry.

    ; =============================================================================
    ; 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 STDOUT
            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
    
            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