Linux 如何在ubuntu下使用nasm(汇编)从键盘读取单字符输入?

Linux 如何在ubuntu下使用nasm(汇编)从键盘读取单字符输入?,linux,assembly,character,nasm,tty,Linux,Assembly,Character,Nasm,Tty,我在ubuntu下使用nasm。顺便说一句,我需要从用户的键盘上获取单个输入字符(比如当一个程序要求您输入y/n时?),所以当按下键时,不按enter键,我需要读取输入的字符。我在谷歌上搜索了很多次,但我发现的都与这一行有关(int21h),这导致了“分段错误”。请帮助我找出如何获得单个字符或如何克服此分割错误。简单的方法:对于文本模式程序,使用访问键盘;对于图形程序,请使用 困难的方法是:假设一个文本模式的程序,你必须告诉内核你想要单字符输入,然后你必须做大量的簿记和解码。这真的很复杂。没有与

我在ubuntu下使用nasm。顺便说一句,我需要从用户的键盘上获取单个输入字符(比如当一个程序要求您输入y/n时?),所以当按下键时,不按enter键,我需要读取输入的字符。我在谷歌上搜索了很多次,但我发现的都与这一行有关(
int21h
),这导致了“分段错误”。请帮助我找出如何获得单个字符或如何克服此分割错误。

简单的方法:对于文本模式程序,使用访问键盘;对于图形程序,请使用

困难的方法是:假设一个文本模式的程序,你必须告诉内核你想要单字符输入,然后你必须做大量的簿记和解码。这真的很复杂。没有与旧的DOS
getch()
例程等价的程序。您可以在此处开始学习如何操作:。图形程序更为复杂;这方面的最低级别API是


无论哪种方式,你都会疯狂地编写汇编中的任何代码;用C代替

可以通过组装完成,但并不容易。不能使用int21h,这是DOS系统调用,在Linux下不可用

要在类UNIX操作系统(如Linux)下从终端获取字符,请读取STDIN(文件号0)。通常,读取系统调用将被阻止,直到用户按enter键。这称为规范模式。要在不等待用户按enter键的情况下读取单个字符,必须首先禁用规范模式。当然,如果希望稍后在程序退出之前进行行输入,则必须重新启用它

要在Linux上禁用规范模式,可以使用IOCTL系统调用向STDIN发送IOCTL(IO控件)。我假设您知道如何从汇编程序进行Linux系统调用

ioctl系统调用有三个参数。第一个是发送命令的文件(STDIN),第二个是IOCTL编号,第三个通常是指向数据结构的指针。ioctl在成功时返回0,或在失败时返回负错误代码

您需要的第一个IOCTL是TCGETS(编号0x5401),它获取termios结构中的当前终端参数。第三个参数是指向termios结构的指针。从内核源代码中,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 */
};
其中,tcflag_t为32位长,cc_t为一个字节长,NCC当前定义为19。请参阅NASM手册,了解如何方便地为此类结构定义和保留空间

因此,一旦获得了当前术语,就需要清除规范标志。此标志位于c_lflag字段中,掩码为ICANON(0x00000002)。要清除它,请计算c_lflag和(而不是ICANON)。并将结果存储回c_lflag字段

现在您需要通知内核您对termios结构的更改。使用TCSETS(编号0x5402)ioctl,第三个参数设置termios结构的地址

如果一切顺利,终端现在处于非规范模式。您可以通过设置规范标志(使用ICANON对c_lflag进行ORing)并再次调用TCSETS ioctl来恢复规范模式退出前始终恢复规范模式


正如我所说,这并不容易。

我最近需要这样做,受Callum的启发,我写了以下内容(NASM for x86-64):

如果您正在阅读一行文字,您可能还想:

call echo_off
这样每个字符就不会在键入时产生回声

也许有更好的方法可以做到这一点,但在64位Fedora安装上它对我很有效


更多信息可以在
termios(3)
的手册页或中找到。

虽然您所说的一切对于C来说都是正确的,但如果OP试图学习汇编语言,这并不是一个真正相关的答案。这是因为OP不应该用汇编语言编程。用汇编语言手工编写代码的唯一好理由是,它是一个性能关键的计算子例程,或者是操作系统内核中极少数不能以任何其他方式进行编码的低级部分之一。用户交互不合格。OP试图在Unix下做的甚至不是一个很好的学习练习。话虽如此,OP编写调用libncurses的汇编语言并没有什么可以阻止的,尽管这似乎是一种极大的时间浪费,我也明白这一点(但它不会像手工进行Unix终端I/O的汇编语言那样糟糕)。我不认为这是一个很好的Unix编程或汇编学习练习,但它确实是一个很好的学习练习。虽然对于绝大多数应用程序来说,汇编并不是一个实际的选择,但我不认为学习如何在汇编中通常不会做的事情是浪费时间。它为您提供了一个有趣的视角,让您了解在什么抽象级别上可能发生什么,以及您的机器和操作系统如何协同工作。-不要这样做,它将与低32之外的地址断开,例如堆栈空间。当然,
mov-edx,termios
——这只适用于x86-64 Linux上的非饼图可执行文件,否则您需要
lea-rdx,[rel-termios]
。(参见其中也包括NASM)
call canonical_off
call echo_off