问题
I want to make linux just take 1 keystroke from keyboard using sys_read, but sys_read just wait until i pressed enter. How to read 1 keystroke ? this is my code:
Mov EAX,3
Mov EBX,0
Mov ECX,Nada
Mov EDX,1
Int 80h
Cmp ECX,49
Je Do_C
Jmp Error
I already tried using BIOS interrupt but it's failed (Segmentation fault), I want capture number 1 to 8 input from keyboard. Thanks for the advance Sorry for bad english
回答1:
Syscalls in 64-bit linux
The tables from man syscall
provide a good overview here:
arch/ABI instruction syscall # retval Notes
──────────────────────────────────────────────────────────────────
i386 int $0x80 eax eax
x86_64 syscall rax rax See below
arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 Notes
──────────────────────────────────────────────────────────────────
i386 ebx ecx edx esi edi ebp -
x86_64 rdi rsi rdx r10 r8 r9 -
I have omitted the lines that are not relevant here. In 32-bit mode, the parameters were transferred in eax
, ecx
etc. and the syscall number is in eax
. In 64-bit mode it is a little different: All registers are now 64-bit wide and therefore have a different name. The syscall number is still in eax
, which now becomes rax
. But the parameters are now passed in rdi
, rsi
etc. In addition, the instruction syscall
is used here instead of int 0x80
to trigger a syscall.
The order of the parameters can also be read in the man pages, here man 2 ioctl
and man 2 read
:
int ioctl(int fd, unsigned long request, ...);
ssize_t read(int fd, void *buf, size_t count);
So here the value of int fd
is in rdi
, the second parameter in rsi
etc.
How to get rid of waiting for a newline
Firstly create a termios
structure in memory (in .bss
section):
termios:
c_iflag rd 1 ; input mode flags
c_oflag rd 1 ; output mode flags
c_cflag rd 1 ; control mode flags
c_lflag rd 1 ; local mode flags
c_line rb 1 ; line discipline
c_cc rb 19 ; control characters
Then get the current terminal settings and disable canonical mode:
; Get current settings
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5401 ; request: TCGETS
mov rdx, termios ; request data
syscall
; Modify flags
and byte [c_cflag], $FD ; Clear ICANON to disable canonical mode
; Write termios structure back
mov eax, 16 ; syscall number: SYS_ioctl
mov edi, 0 ; fd: STDIN_FILENO
mov esi, 0x5402 ; request: TCSETS
mov rdx, termios ; request data
syscall
Now you can use sys_read
to read in the keystroke:
mov eax, 0 ; syscall number: SYS_read
mov edi, 0 ; int fd: STDIN_FILENO
mov rsi, buf ; void* buf
mov rdx, len ; size_t count
syscall
Afterwards check the return value in rax
: It contains the number of characters read.
References:
- Why does the sys_read system call end when it detects a new line?
- How do i read single character input from keyboard using nasm (assembly) under ubuntu?.
- Using the raw keyboard mode under Linux (external site with example in 32-bit assembly)
来源:https://stackoverflow.com/questions/62937150/reading-a-single-key-input-on-linux-without-waiting-for-return-using-x86-64-sy