【问题标题】:Printing an integer with x86 32-bit Linux sys_write (NASM)使用 x86 32 位 Linux sys_write (NASM) 打印整数
【发布时间】:2014-07-31 16:44:12
【问题描述】:

我是这个论坛的新手。 我对高级语言有一点经验(真的很少)。大约一个月前,我认为在 linux 上选择 nasm (IA-32) 后,看看汇编是如何工作的会是个好主意。

现在,结束它之后,我尝试编写一个简单的程序,让计算机打印一个包含 100 个数字(1 2 4 8 16...)的列表,但我什至无法正确完成。 我得到这个输出:

1PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP(continues)...

程序是这样的:

section .text
    global main
main:
    mov word [num], '1'
    mov ecx, 100
doubl:
    push ecx ; to push the loop counter

    mov eax, 4
    mov ebx, 1
    mov ecx, num
    mov edx, 1
    int 0x80

    sub ecx, 30h
    add ecx, ecx   ; shl ecx, 1
    add ecx, 30h
    mov [num], ecx   ; deleting this line I get  11111111111111111...

    pop ecx  ; to pop the loop counter
    loop doubl
exit:
    mov eax, 1
    int 0x80    
section .bss
num resw 2

看起来错误出在将数字翻倍的部分或将其存储在变量“num”中的部分,但我不明白为什么会发生以及如何解决它。

顺便说一句,有人可以解释一下何时准确地使用方括号吗?有什么规定吗?本教程将其称为“有效地址”,看起来当我想移动(或做某事)变量的内容而不是对变量的地址进行操作时,我必须使用方括号。然而我对此很困惑。我看到它用于:

mov ebx, count
inc word [ebx]
mov esi, value
dec byte [esi]

但是想要增加寄存器的内容不是很明显吗(因为它没有地址(或者没有地址?)??

【问题讨论】:

  • sys_write 调用需要一个指向您要在ecx 中输出的字节的指针,并且您正试图将实际值保留在那里。此外,您的 add/sub 30h 技术仅适用于小于 10 的值。
  • 相关:一个 x86-64 Linux(使用 64 位 syscall 而不是 32 位 int 0x80)实现转换和打印,很像迈克尔在这里的回答:stackoverflow.com/questions/13166064/…跨度>

标签: linux assembly x86 nasm


【解决方案1】:

您的数字将迅速增长而不仅仅是一位数。你应该做的是在num 中有一个整数而不是一个字符,然后将该整数转换为一个可以用sys_write 打印的字符串。

这里有一种转换方法:重复除以 10,首先取最低位作为余数:

; Input:
; eax = integer value to convert
; esi = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; eax = pointer to the first character of the generated string
; ecx = length of the generated string
int_to_string:
  add esi,9
  mov byte [esi],0    ; String terminator

  mov ebx,10
.next_digit:
  xor edx,edx         ; Clear edx prior to dividing edx:eax by ebx
  div ebx             ; eax /= 10
  add dl,'0'          ; Convert the remainder to ASCII 
  dec esi             ; store characters in reverse order
  mov [esi],dl
  test eax,eax            
  jnz .next_digit     ; Repeat until eax==0

  ; return a pointer to the first digit (not necessarily the start of the provided buffer)
  mov eax,esi
  ret

你可以这样使用:

    mov    dword [num],1
    ...
    mov    eax,[num]       ; function args using our own private calling convention
    mov    esi,buffer
    call   int_to_string
; eax now holds the address that you pass to sys_write
    ...

section .bss
    num    resd 1
    buffer resb 10

您的倍数可以简化为shl dword [num],1。或者更好的是,当它仍在add eax,eax 的寄存器中时,在某个时候将其翻倍。

【讨论】:

  • 非常感谢。它真的很有帮助。我真的很喜欢将数字转换为字符串的方法(尽管我无法理解它是如何工作的,直到我自己用纸和笔尝试过)。只有一个问题:子例程 .next_digit 以点开头。它是所有子程序或特定类型的约定吗?
  • .next_digit 并不是真正的子程序。它只是一个标签,在这种情况下标志着循环的开始。开头的点使它成为local label。局部标签可以方便地在子例程中使用,因为这允许您在程序的不同位置拥有多个具有相同名称的标签(适用于具有通用名称的标签,这样您就不必使用愚蠢的命名方案,如else1:else2:else3: 等)。
  • 我原以为 'ret' 是 .next_digit 的一部分。我只是看到它属于 int_to_string 。再次感谢。
猜你喜欢
  • 2015-06-09
  • 2019-07-06
  • 2011-10-17
  • 2014-02-21
  • 2013-12-14
  • 2015-06-29
  • 2015-06-25
  • 1970-01-01
  • 2013-06-30
相关资源
最近更新 更多