在Linux上使用汇编输出整数

10

我需要在纯汇编中完成这个任务(即没有使用库或调用C函数)。

我理解问题的本质:需要将整数除以10,将一位数字余数转换为ASCII,输出并重复这个过程直到商为0。

但出于某种原因,它就是不起作用。 我正在使用x86上的NASM。

以下是我现在拥有的代码(不输出任何内容,但也不会抛出任何汇编器错误):

; integer to output is stored in eax
mov ecx, 10   ; for base 10

loop:
div ecx  ;EAX contains the quotient, EDX the remainder

; Do something to EDX to convert it to ASCII, not sure if this is correct
add edx, '0'

push eax    ;We'll be playing with EAX to output EDX, save EAX to the stack

mov eax, 4              ; sys_write
mov ebx, 1              ; to STDOUT
mov ecx, edx
mov edx, 1
int 0x80

pop eax  ;restore EAX

cmp eax, 0   ;If EAX is 0, our job is done
jnz loop

有很多类似的问题(比如这个这个), 但我在实现时遇到了困难。对于DOS系统,这个问题也是有帮助的,但我仍然感到困惑。

我一定是漏掉了什么。有什么想法吗?


我在这段代码中没有看到常量10,这对于打印十进制数字是必要的。(您需要重复使用divmod10,存储数字,反转它们并输出。或者从“大端”开始,先除以100,000,000,然后除以10,000,000等等——但这可能不比存储和反转方法简单。) - sarnold
@sarnold 忘记在顶部加一行了。我编辑了我的问题来添加它。 - David Chouinard
可能是重复的问题:NASM Linux汇编打印整数 - Ciro Santilli OurBigBook.com
相关内容:如何在汇编级别编程中打印整数,而不使用来自C库的printf?已经有一个适用于x86-64 Linux的工作实现,很容易移植到32位int 0x80系统调用。 - Peter Cordes
基本上是 如何在 Linux x86 NASM 中打印字符? 的重复,或者说它是这个问题的重复。无论哪种方式,都存在将 ASCII 字符值传递而不是指向存储在内存中的 ASCII 字符的指针的相同问题。 - Peter Cordes
2个回答

5

除了@sarnold提到的对ecx的破坏外,至少还有两个问题:

  1. div ecx将64位值edx:eax除以ecx,因此您需要确保在除法运算之前将edx设置为0。

  2. write系统调用的第二个参数(在ecx中)应该是指向包含要打印的字符的缓冲区的指针,而不是字符本身。

解决第二个问题的一种方法是将包含要打印的字符的寄存器推送到堆栈上,然后将堆栈指针esp赋值给ecx(堆栈指针指向最近推送的项,x86按小端格式存储值,因此第一个字节是低8位)。例如:

push edx         ; save value on stack
mov  eax, 4      ; sys_write
mov  ebx, 1      ; to STDOUT
mov  ecx, esp    ; first byte on stack
mov  edx, 1      ; length = one byte
int  0x80
pop  edx         ; remove what we pushed (or "add esp, 4" would do just as well here;
                 ;                        we don't need the actual value again)

这应该足以获得一些输出...

(但此时,您可能会注意到算法的一个“特点”,并想重新考虑如何存储除法产生的数字!)


这是一个很好的解释,感谢您澄清。正如您所暗示的那样,我知道为什么该算法会倒序打印整数。对此有什么修复的想法吗? - David Chouinard
2
为数字保留一些空间(最大的无符号32位整数是4294967295,因此10个字节足够),可以在数据段中(例如使用resb指令)或堆栈上(只需从esp中减去,但确保减去4的倍数以保持堆栈正确对齐)。然后,将数字存储到缓冲区中,向前移动,然后循环遍历缓冲区并反向打印每个字符;或者(更好?)从缓冲区的最后一个字节开始向后工作,然后您可以使用单个write系统调用写入所有数字。 - Matthew Slattery
@DavidChouinard:在汇编级别编程中,如何打印整数而不使用c库中的printf函数?将数字放入缓冲区(从末尾开始),并在完成后进行一次打印系统调用。这样更有效率。 - Peter Cordes

2

您在程序顶部正确地将ecx设置为10,但是后来覆盖了ecx

mov eax, 4              ; sys_write
mov ebx, 1              ; to STDOUT
mov ecx, edx ;;; oops -- lost the 10
mov edx, 1
int 0x80

尝试将loop向上移动一行,这样每次通过循环重新初始化ecx10


好的,已经修复了。但我仍然没有得到任何输出,我的猜测是 add edx, '0' 没有正确地将单个数字转换为十进制。 - David Chouinard

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