简体   繁体   English

如何在ubuntu下使用nasm(程序集)从键盘读取单个字符输入?

[英]How do i read single character input from keyboard using nasm (assembly) under ubuntu?

I'm using nasm under ubuntu.我在 ubuntu 下使用 nasm。 By the way i need to get single input character from user's keyboard (like when a program ask you for y/n ?) so as key pressed and without pressing enter i need to read the entered character.顺便说一下,我需要从用户的键盘上获取单个输入字符(比如当程序要求您输入 y/n 时?)以便按下键而不按 Enter 我需要读取输入的字符。 I googled it a lot but all what i found was somehow related to this line ( int 21h ) which result in "Segmentation Fault".我用谷歌搜索了很多,但我发现的所有内容都与这条线( int 21h )有关,这导致了“分段错误”。 Please help me to figure it out how to get single character or how to over come this segmentation fault.请帮我弄清楚如何获取单个字符或如何克服这个分段错误。

It can be done from assembly, but it isn't easy.它可以从组装中完成,但这并不容易。 You can't use int 21h, that's a DOS system call and it isn't available under Linux.你不能使用 int 21h,这是一个 DOS 系统调用,它在 Linux 下不可用。

To get characters from the terminal under UNIX-like operating systems (such as Linux), you read from STDIN (file number 0).要在类 UNIX 操作系统(例如 Linux)下从终端获取字符,您可以从 STDIN(文件编号 0)读取。 Normally, the read system call will block until the user presses enter.通常, read 系统调用将阻塞,直到用户按下 Enter 键。 This is called canonical mode.这称为规范模式。 To read a single character without waiting for the user to press enter, you must first disable canonical mode.要在不等待用户按 Enter 的情况下读取单个字符,您必须首先禁用规范模式。 Of course, you'll have to re-enable it if you want line input later on, and before your program exits.当然,如果您想稍后在程序退出之前进行行输入,则必须重新启用它。

To disable canonical mode on Linux, you send an IOCTL (IO ControL) to STDIN, using the ioctl syscall.要在 Linux 上禁用规范模式,请使用 ioctl 系统调用向 STDIN 发送 IOCTL (IO Control)。 I assume you know how to make Linux system calls from assembler.我假设您知道如何从汇编程序进行 Linux 系统调用。

The ioctl syscall has three parameters. ioctl 系统调用具有三个参数。 The first is the file to send the command to (STDIN), the second is the IOCTL number, and the third is typically a pointer to a data structure.第一个是将命令发送到的文件 (STDIN),第二个是 IOCTL 编号,第三个通常是指向数据结构的指针。 ioctl returns 0 on success, or a negative error code on fail. ioctl 成功时返回 0,失败时返回负错误代码。

The first IOCTL you need is TCGETS (number 0x5401) which gets the current terminal parameters in a termios structure.您需要的第一个 IOCTL 是 TCGETS(编号 0x5401),它获取 termios 结构中的当前终端参数。 The third parameter is a pointer to a termios structure.第三个参数是一个指向 termios 结构的指针。 From the kernel source, the termios structure is defined as:从内核源代码来看,termios 结构定义为:

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 */
};

where tcflag_t is 32 bits long, cc_t is one byte long, and NCCS is currently defined as 19. See the NASM manual for how you can conveniently define and reserve space for structures like this.其中 tcflag_t 是 32 位长,cc_t 是 1 字节长,NCCS 当前定义为 19。请参阅 NASM 手册了解如何方便地为此类结构定义和保留空间。

So once you've got the current termios, you need to clear the canonical flag.因此,一旦您获得了当前的 termios,就需要清除规范标志。 This flag is in the c_lflag field, with mask ICANON (0x00000002).此标志位于 c_lflag 字段中,带有掩码 ICANON (0x00000002)。 To clear it, compute c_lflag AND (NOT ICANON).要清除它,请计算 c_lflag AND (NOT ICANON)。 and store the result back into the c_lflag field.并将结果存储回 c_lflag 字段。

Now you need to notify the kernel of your changes to the termios structure.现在您需要将您对 termios 结构的更改通知内核。 Use the TCSETS (number 0x5402) ioctl, with the third parameter set the the address of your termios structure.使用 TCSETS(编号 0x5402)ioctl,第三个参数设置 termios 结构的地址。

If all goes well, the terminal is now in non-canonical mode.如果一切顺利,终端现在处于非规范模式。 You can restore canonical mode by setting the canonical flag (by ORing c_lflag with ICANON) and calling the TCSETS ioctl again.您可以通过设置规范标志(通过将 c_lflag 与 ICANON 进行 ORing)并再次调用 TCSETS ioctl 来恢复规范模式。 always restore canonical mode before you exit在退出之前始终恢复规范模式

As I said, it isn't easy.正如我所说,这并不容易。

I needed to do this recently, and inspired by Callum's excellent answer , I wrote the following (NASM for x86-64):我最近需要这样做,并受到 Callum 出色答案的启发,我写了以下内容(适用于 x86-64 的 NASM):

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

(Editor's note: don't use int 0x80 in 64-bit code: What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? - it would break in a PIE executable (where static addresses aren't in the low 32 bits), or with the termios buffer on the stack. It does actually work in a traditional non-PIE executable, and this version can be easily ported to 32-bit mode.) (编者注:不要在 64 位代码中使用int 0x80如果在 64 位代码中使用 32 位 int 0x80 Linux ABI 会发生什么? - 它会破坏 PIE 可执行文件(其中静态地址不是在低 32 位),或者在堆栈上使用 termios 缓冲区。它实际上可以在传统的非 PIE 可执行文件中工作,并且这个版本可以很容易地移植到 32 位模式。)

You can then do:然后你可以这样做:

call canonical_off

If you're reading a line of text, you probably also want to do:如果您正在阅读一行文本,您可能还想这样做:

call echo_off

so that each character isn't echoed as it's typed.这样每个字符在输入时都不会被回显。

There may be better ways of doing this, but it works for me on a 64-bit Fedora installation.可能有更好的方法来做到这一点,但它适用于 64 位 Fedora 安装。

More information can be found in the man page for termios(3) , or in the termbits.h source .更多信息可以在termios(3)的手册页或termbits.h源中找到

The easy way: For a text-mode program, use libncurses to access the keyboard;简单的方法:对于文本模式程序,使用libncurses访问键盘; for a graphical program, use Gtk+ .对于图形程序,请使用Gtk+

The hard way: Assuming a text-mode program, you have to tell the kernel that you want single-character input, and then you have to do a whole lot of bookkeeping and decoding.困难的方法:假设一个文本模式程序,你必须告诉内核你想要单字符输入,然后你必须做大量的簿记和解码。 It's really complicated.这真的很复杂。 There is no equivalent of the good old DOS getch() routine.没有等效的旧 DOS getch()例程。 You can start learning about how to do it here: Terminal I/O .您可以在此处开始学习如何操作:终端 I/O Graphical programs are even more complicated;图形程序更加复杂; the lowest-level API for that is Xlib .最低级别的 API 是Xlib

Either way, you're going to go mad coding whatever this is in assembly;无论哪种方式,你都会疯狂地编码汇编中的任何东西; use C instead.改用 C。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用NASM Assembly忽略输入中的换行符? - How do I ignore line breaks in input using NASM Assembly? 如何使用汇编语言从键盘读取输入字符串 - How to read input string from keyboard using assembly language 如何在Ubuntu11.04中使用NASM维护堆栈 - How do I maintain stack in NASM using in Ubuntu11.04 如何从汇编中的标准输入读取输入,逐个字符 - How to read input from stdin in assembly, character by character 如何使用NASM程序集(使用C ++打开和关闭文件)读取和修改文件的字节? - How can I read and modify a file's bytes using NASM assembly, with C++ for opening/closing the file? 如何在Ubuntu Jaunty下从HID设备读取事件? - How do I read events from a HID device under Ubuntu Jaunty? 我应该如何使用NASM Assembly中的动态大小输入? - How should I work with dynamically-sized input in NASM Assembly? 用户提供文件名时无法读取文件(使用 nasm 的 x86 汇编程序) - unable to read from file when user provides filename (x86 assembly program using nasm) 如何从字符串NASM程序集64位Linux中读取每个字符 - How to read each char from string NASM assembly 64bit linux 如何在NASM程序集中检查数字是否代表大写字符? - How to check if a number represents an uppercase character in NASM Assembly?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM