在NASM中每秒更新已经打印的字符串

Update the string that has already been printed every second in NASM

我对组装这件事完全陌生,用谷歌搜索了几个小时,在 SO 上搜索并没有解决问题,所以我来这里问了。

我想达到的目标:

[first second]: hello (stays on the screen for 1 second)

[second second]: world (hello disappeared and now we have `world` in place of it)

而且这个流程是无限循环的

换句话说,我希望我的终端的标准输出在 hello 和 world 之间闪烁(改变)而不附加任何换行符、写入字符串或任何其他东西,我只希望现有的、已经打印的文本被另一个替换文本。

我写了无限循环,它会打印 hello,然后等一秒钟,然后打印 world,再等一秒钟。我也把这段代码放在一个无限循环中。

这是我目前拥有的代码:

section .data
    hello db "hello",10,0
    world db "world",10,0
    delay dq 1,0

section .text
    global _start

_start:
    mov rax, 1
    mov rdi, 1
    mov rsi, hello
    mov rdx, 6
    syscall

    mov rax, 35
    mov rdi, delay
    mov rsi, 0
    syscall

    mov rax, 1
    mov rdi, 1
    mov rsi, world
    mov rdx, 6
    syscall

    mov rax, 35
    mov rdi, delay
    mov rsi, 0
    syscall

    call _start

请注意,我使用 elf64 asm 格式,非常感谢您提出该格式的解决方案。

打印一个 \r 回车 return(ASCII 代码 13)以将光标放在行的开头 而无需 滚动终端 \n 换行符 (ASCII 10) 可以。

然后你可以覆盖你最后写的东西。如果它更短,您可以在可见字符后打印空格以 "erase" 后面的字符仍然存在。

例如因为这两个词的长度相同,你可以这样做:

section .rodata
    hello db 13, "hello"        ; `\rhello`
    hello_len equ $ - hello
    world db 13, "world"
    world_len equ $ - world

请注意,您的数据中不需要 , 0,因为您将这些缓冲区传递给 write 而不是 printf,因此它们不需要 0 ] 终止的隐式长度 C 字符串。您也不需要硬编码 mov rdx, 6,您可以使用 mov rdx, hello_len 并让汇编程序为您计算。


对于休眠部分,您可以使用 sleep libc 函数,但对于原始系统调用,您必须使用 nanosleep。 (就像你已经在做的那样。)


对于循环,不要使用call _start;使用 jmp。您不想推送 return 地址;最终会堆栈溢出(大约 100 万秒后:8MiB 堆栈大小限制和调用推送一个 8 字节 return 地址。)

使用 Peter Cordes 的有用指导解决了这个问题。

工作代码如下所示:

section .data
    hello db "hello",13,0
    world db "world",13,0
    delay dq 1,0

section .text
    global _start

_start:
    mov rax, 1
    mov rdi, 1
    mov rsi, hello
    mov rdx, 6
    syscall

    mov rax, 35
    mov rdi, delay
    mov rsi, 0
    syscall

    mov rax, 1
    mov rdi, 1
    mov rsi, world
    mov rdx, 6
    syscall

    mov rax, 35
    mov rdi, delay
    mov rsi, 0
    syscall

    jmp _start