【问题标题】:How do I print a new line after a number in a loop in x86 assembly?如何在 x86 程序集中循环中的数字后打印新行?
【发布时间】:2019-10-17 07:57:53
【问题描述】:

我有一个汇编程序,它在一行上打印出 1 到 9 的数字,如下所示:123456789。我想要的是在每一行上打印这些值;每行一个数字。

我尝试实现新行,但在我的代码中抛出错误分段错误(核心转储)。我在组装方面仍然很新鲜,所以我真的不知道如何找出为什么这不起作用。它只打印第一个数字 (1),然后抛出该错误。

我尝试换行的代码:

mov dl, 13
mov ah, 02h
int 21h
mov dl, 10
mov ah, 02h
int 21h

我的代码:

_start:
    mov ecx, 10
    mov eax, '1'

L1:
    mov [num], eax
    mov eax, 4
    mov ebx, 1
    push ecx

    mov ecx, num
    mov edx, 1
    int 0x80

    mov eax, [num]
    sub eax, '0'
    inc eax
    add eax, '0'
    pop ecx
    loop L1

    mov eax, 1
    int 0x80

section .bss
    num resb 1

【问题讨论】:

  • “我尝试实施新行” - 告诉我们你做了什么?
  • @DavidWohlferd 我编辑了帖子以明确我尝试了什么。
  • 为什么在“我尝试过的代码”部分与“我的代码”部分产生不同的中断?是 DOS 的“我尝试过的代码”和 Linux 的“我的代码”吗?
  • 另外,为什么你只在“我试过的代码”中加载 16 位 reg,而不是在“我的代码”中加载完整的 32 位 reg?
  • @我正在使用 Linux,很抱歉造成混乱。

标签: linux assembly x86 nasm


【解决方案1】:

您应该使用相同的系统调用来打印用于打印数字的换行符。

另外,Linux 中的换行符只是 LF (char 10,) 而不是 CR (char 13) 后跟 LF,就像 Windows/DOS 使用的那样。

如何在 Linux 上以 x86 程序集打印到标准输出

This answer 描述了每个参数对 Linux 打印系统调用的作用,这就是您通过引发 int 0x80 来调用的。

系统调用使用寄存器来传递它们的参数。从链接的答案来看,eax 是系统调用号(4 = 打印),ebx 是目标流(1 = 标准输出),ecx 是指向要打印的数据的指针,edx 是要打印的数据。

因此,在循环中实际执行打印的代码是:

mov [num], eax  ; Moves the character in eax to the buffer pointed to by num.
mov eax, 4      ; Moves 4 into eax, i.e. selects the print system call.
mov ebx, 1      ; Moves 1 into ebx, i.e. selects stdout as the destination.
mov ecx, num    ; Moves the address where your text is stored into ecx.
mov edx, 1      ; Moves the number of bytes to characters (1) into edx.
int 0x80        ; Executes a Linux system call using the above parameters.

要打印换行符,您只需要在 eax 中在此代码之前添加换行符(十进制字符 10),而不是数字字符。因此,例如,在此代码之前添加 mov eax, 10 将打印换行符而不是数字。

如何使用您现有的循环进行这项工作

“我的代码”部分中的num 是您存储要打印的数据的缓冲区。但是,此代码也使用此内存来跟踪它打印的最后一个数字。因此,有两种方法可以在循环之间丢失该信息:

选项 1:只需将缓冲区的大小从 1 个字节增加到 2 个字节,然后将换行符放在第二个字节中。然后,您可以将 2 移动到 edx 而不是 1 以告诉 Linux 您要打印 2 个字符,从而在每次循环迭代时打印数字和换行符。

选项 2:分配另一个单字节缓冲区来存储换行符。将换行符移到那里,然后在打印循环中的数字的系统调用之后进行第二次系统调用以打印换行符。例如,如果您的新缓冲区名为“lfbuffer”,那么您将在现有循环中的int 0x80 行之后添加此代码:

mov byte [lfbuffer], 10  ; Moves the a line feed to the buffer pointed to by lfbuffer.
mov eax, 4           ; Moves 4 into eax, i.e. selects the print system call.
mov ebx, 1           ; Moves 1 into ebx, i.e. selects stdout as the destination.
mov ecx, lfbuffer    ; Moves the address where your line feed is stored into ecx.
mov edx, 1           ; Moves the number of bytes to characters (1) into edx.
int 0x80             ; Executes a Linux system call using the above parameters.

【讨论】:

  • mov [lfbuffer], eax 是一个双字存储。您建议使用单字节缓冲区,因此之后这将踩到 3 个字节。此外,mov byte [lfbuffer], 10 有效。或者更好的是,静态初始化它而不是浪费指令。喜欢section .rodata / lfbuffer: db 10。但显然更有效的方法是将换行符附加到您已经传递给第一个 write 的字符串中,就像您首先建议的那样。
  • @PeterCordes 啊,对。这就是我在清晨进行组装所得到的。我只是在看原始代码,甚至没有想到它已经是一个 32 位存储。固定的。在这种情况下,原始代码已经占用了 3 个字节的内存,或者num 缓冲区已经有足够的空间来存储数字字符后的换行符。
  • 原始代码只保留了 1 个字节的 BSS 空间,但作为一个静态可执行文件,后面可能什么都没有,所以它确实有一整页。 (它没有可以与 BSS 共享页面的 .data 部分)
  • @PeterCordes 啊,是的,又一次。显然我在最初阅读这个问题时错过了整个部分。另一个迹象表明我可能不应该在清晨处理组装。 - 哈哈
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 2013-09-23
相关资源
最近更新 更多