如何在NASM汇编中忽略输入的换行符?

4
学习NASM汇编语言,我正在尝试编写一个程序,读取两个一位数的输入。
我在.bss中声明了两个变量:
num1 resb 1
num2 resb 1

接下来,我会要求用户按照以下方式输入数字:

; Get number 1
mov EAX,3
mov EBX,1
mov ECX,num1
mov EDX,1
int 0x80

; Get number 2
mov EAX,3
mov EBX,1
mov ECX,num2
mov EDX,1
int 0x80

由于我只对一位数字感兴趣,所以我将EDX设置为1。这样,无论用户输入什么,只有第一个字符会存储在我的变量中(对吗?)。

问题是,在第一个字符之后的所有内容都将用于未来的读取。如果您输入 5 然后按 ENTER,ASCII码53将被成功存储在 num1 中,但您通过按 ENTER 生成的换行符将传递到下一个读取指令中,该指令将存储在 num2 中。显然这不是我想要的。我希望用户输入一个数字,按ENTER,然后输入另一个数字,再按 ENTER

我不确定如何以最简单的方式解决这个问题。

最愚蠢的想法是在 num1num2 之间放置一个“虚拟”的读取指令,它将捕获换行符(并不做任何处理)。这显然不好。


如果您需要无缓冲输入,请参考此链接此链接。否则,您可以填充变量(使它们成为字,并在读取后清除上字节)。 - Michael
虽然“虚拟读取”听起来不太好,但这几乎就是你需要做的。你需要一个小的“获取数字”的例程,它从输入中读取字符并跳过任何不是数字的内容,直到看到数字并返回它。这是一种相当标准的方法,而且并不愚蠢。它非常简单和有效。 - lurker
如果用户输入了字母"a"而不是数字,您打算怎么做?此外,将ebx设置为1是否使用标准输出?您可能需要将其设置为0以使用标准输入。 - lurker
@mbratch:我明白了。虚拟读取现在听起来不错 - 似乎没有简单的方法来解决这个问题。至于“a”,我没有计划做任何事情 - 我只是对获取单个字符输入感兴趣。 - Saturn
3个回答

3

以下是一种非常基本的获取想要数字输入的方法,它将跳过除数字外的所有内容。如果此方法提供了所需功能,则其可行。如果您需要根据其他非数字输入需要不同的行为,则需要指定该行为。然后也可以编程该行为。

    ; Get number 1
    mov   ECX,num1
    call  GetNumber

    ; Get number 2
    mov   ECX,num2
    call  GetNumber
    ...

GetNumber:
    pusha              ; save regs
get:
    mov   EAX,3        ; system call for reading a character
    mov   EBX,0        ; 0 is standard input
    mov   EDX,1        ; number of characters to read
    int   0x80         ; ECX has the buffer, passed into GetNumber
    cmp   byte [ecx],0x30
    jl    get          ; Retry if the byte read is < '0'
    cmp   byte [ecx],0x39
    jg    get          ; Retry if the byte read is > '9'

    ; At this point, if you want to just return an actual number,
    ; you could subtract '0' (0x30) off of the value read
    popa               ; restore regs
    ret

3
干涉标准输入以禁用可能有效,但可能是“艰难的方法”。使用两个字节的缓冲区并执行mov edx, 2如果用户表现良好就可以工作——清除第二个字节,或者只是忽略它。
有时候,用户会表现得不好。处理“垃圾输入”或其他错误条件通常需要比“完成工作”需要更多的代码!要么处理它,要么满足于一个“通常”工作的程序。第二个选项对于初学者可能已经足够了。
顽皮的用户可能只是按下“回车”键而没有输入数字。在这种情况下,我们要么重新提示,要么打印“抱歉,您不喜欢我的程序”,然后退出。或者他/她可能在按下“enter”键之前输入了多个字符。这是潜在的危险!如果一个恶意用户输入“1rm -rf .”,你刚刚清空了整个系统!Unix是强大的,像任何强大的工具一样,在不熟练的用户手中可能很危险。
你可以尝试这样做(警告:未经测试的代码!)...
section .bss
    num1 resb 1
    num2 resb 1
    trashbin resb 1

section .text
re_prompt:
; prompt for your number
; ...
; get the number (character representing the number!)
    mov ecx, num1
reread:
    mov edx, 1
    mov ebx, 0 ; 1 will work, but 0 is stdin
    mov eax, 3 ; sys_read
    int 0x80
    cmp byte [ecx], 10 ; linefeed
    jz got_it
    mov ecx, trashbin
    jmp reread
got_it:
    cmp byte [num1], 10 ; user entered nothing?
    jz re_prompt ; or do something intelligent
; okay, we have  a character in num1
; may want to make sure it's a valid digit
; convert character to number now?
; carry on

您可能需要调整它以使其正常工作。我可能不应该发布未经测试的代码(这样可能会让您尴尬!)。 "类似这样" 对您来说可能比调整 termios 更容易。Michael 给您的第二个链接包含了我用于此目的的代码。我对它不是很满意(有些凌乱!),但它“勉强能用”。无论哪种方式,祝您玩得开心! :)


相关:相关:在Linux上读取单键输入(无需等待返回),使用x86_64 sys_call re: TCGETS / TCSETS(适用于64位模式)。 - Peter Cordes

0

你将需要处理规范禁用、原始键盘等问题。这就是Linux管理输入控制台密码的方式,例如在不显示密码的情况下。

这里很好地描述了实现此操作的汇编代码:

http://asm.sourceforge.net/articles/rawkb.html


1
stty -echostty -icanon不同。您可以拥有无回显输入,但仍然是“熟的”(不是原始的:退格键有效,输入在按下EOL或EOF之前不会“提交”)。例如,通过在一个终端中运行cat,并在另一个终端中使用stty -echo < /proc/$(pidof cat)/fd/0来干扰它正在运行的终端的终端设置来尝试它。(stty在其fd 0上执行终端ioctl,并将有关其输出打印到其fd 1,因此需要输入重定向。) - Peter Cordes
相关:在Linux上读取单键输入(无需等待返回),使用x86_64 sys_call,涉及TCGETS / TCSETS(适用于64位模式)。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接