代码之家  ›  专栏  ›  技术社区  ›  Klei

使用x86 32位Linux sys_write(NASM)打印整数

  •  1
  • Klei  · 技术社区  · 10 年前

    我是这个论坛的新手。 我有一点高级语言的经验(真的很少)。将近一个月前,我想看看汇编是如何工作的一个好主意。在选择了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]
    

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

    1 回复  |  直到 10 年前
        1
  •  3
  •   Michael    6 年前

    你的数字将很快增长到一位数以上。你应该做的是在 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 .