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

时间:2010-07-22 00:50:49

标签: assembly ubuntu character nasm

我在ubuntu下使用nasm。顺便说一下,我需要从用户的键盘上获取单个输入字符(比如一个程序要求你输入y / n?)所以当按下键并且没有按下输入时我需要读取输入的字符。我google了很多,但我发现的所有内容都与这一行(int 21h)有某种关系,导致“分段错误”。请帮我弄清楚如何获得单个字符或如何克服这个分段错误。

3 个答案:

答案 0 :(得分:11)

可以从装配完成,但这并不容易。你不能使用int 21h,这是一个DOS系统调用,它在Linux下不可用。

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

要在Linux上禁用规范模式,请使用ioctl系统调用将IOCTL(IO控制)发送到STDIN。我假设您知道如何从汇编程序进行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为一个字节长,NCCS当前定义为19.请参阅NASM手册,了解如何方便地为此类结构定义和保留空间。

因此,一旦你拥有了当前的termios,就需要清除规范标志。该标志位于c_lflag字段中,带有掩码ICANON(0x00000002)。要清除它,请计算c_lflag AND(NOT ICANON)。并将结果存储回c_lflag字段。

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

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

之前始终恢复规范模式

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

答案 1 :(得分:5)

我最近需要这样做,并且受到Callum的excellent answer的启发,我写了以下内容:

termios:        times 36 db 0
stdin:          equ 0
ICANON:         equ 1<<1
ECHO:           equ 1<<3

canonical_off:
        call read_stdin_termios

        ; clear canonical bit in local mode flags
        push rax
        mov eax, ICANON
        not eax
        and [termios+12], eax
        pop rax

        call write_stdin_termios
        ret

echo_off:
        call read_stdin_termios

        ; clear echo bit in local mode flags
        push rax
        mov eax, ECHO
        not eax
        and [termios+12], eax
        pop rax

        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

read_stdin_termios:
        push rax
        push rbx
        push rcx
        push rdx

        mov eax, 36h
        mov ebx, stdin
        mov ecx, 5401h
        mov edx, termios
        int 80h

        pop rdx
        pop rcx
        pop rbx
        pop rax
        ret

write_stdin_termios:
        push rax
        push rbx
        push rcx
        push rdx

        mov eax, 36h
        mov ebx, stdin
        mov ecx, 5402h
        mov edx, termios
        int 80h

        pop rdx
        pop rcx
        pop rbx
        pop rax
        ret

然后你可以这样做:

call canonical_off

如果您正在阅读一行文字,您可能也想这样做:

call echo_off

这样每个字符都不会在输入时回显。

可能有更好的方法可以做到这一点,但它适用于64位Fedora安装。

可以在termios(3)termbits.h source的手册页中找到更多信息。

答案 2 :(得分:0)

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

困难的方法:假设一个文本模式程序,你必须告诉内核你想要单字符输入,然后你必须做大量的簿记和解码。这真的很复杂。没有相当于好的旧DOS getch()例程。您可以开始了解如何在此处进行操作:Terminal I/O。图形程序更加复杂;最低级别的API是Xlib

无论哪种方式,你都会疯狂地编码装配中的任何东西;用C代替。