【问题标题】:linux nasm assembly print all numbers from zero to 100linux nasm 程序集打印从零到 100 的所有数字
【发布时间】:2012-10-27 06:38:36
【问题描述】:

我正在编写一个程序来打印从零到 100 的所有数字。我这样做的唯一原因是为了测试打印出多个数字。

我遇到的问题是我的程序只打印出数字 1 和 2。我不知道为什么。我的编译器编译得很好,没有错误,也没有链接器错误。

这是我的代码:

SECTION .data
len EQU 32
NUL EQU 0
countlen EQU 8

SECTION .bss
counter resb countlen
strlen resb countlen

SECTION .text
GLOBAL _start
_start:
    mov BYTE[counter], 1              ; set counter to 1
    mov BYTE[strlen], 1               ; set string length counter to 1
    mov ecx, counter                  ; move the counter to ecx
    add BYTE[ecx], NUL                ; add null terminator to ecx
    mov esi, 9                        ; move 9 to esi

Length: 
    cmp [counter], esi                ; compare counter to esi
    jle Set                           ; if equal, goto set
    inc BYTE[strlen]                  ; increment the string size
    mov eax, 10                       ; move 10 to eax
    mov ebx, esi                      ; move esi to ebx
    mul ebx                           ; multiply ebx by esi
    add eax, 9                        ; add nine to the result
    mov esi, eax                      ; move the result to esi
    jmp Length                        ; jump to Length 

Set:
    mov esi, 9                        ; reset checker

Check:
    cmp BYTE[strlen], 1               ; is it one digit?
    je Single                         ; if yes, jump to single
    cmp BYTE[strlen], 3               ; is it 100?
    je Exit                           ; if yes, jump to Exit

Print:                                 ; this section deals with multi-digit numbers                                   
    cmp BYTE[ecx], NUL                ; check if end of string
    je Exit                           ; if equal goto exit 
    mov eax, 4
    mov ebx, 1
    mov edx, 1
    int 80h                           ; print number

    inc ecx                           ; point to next digit in number
    jmp Print                         ; jump to Print

Single:                                   ; this section deals with single digit numbers         add BYTE[counter], '0'            ; convert to ASCII
    mov eax, 4                       
    mov ebx, 1
    mov ecx, counter 
    mov edx, countlen 
    int 80h                           ; print the digit
    jmp Length                        ; go back

Exit:                                     ; Exit section
    mov eax, 1                        ; sys_exit
    mov ebx, 0                        ; return 0
    int 80h                           ; syscall

为什么要这样做?另外,我需要更改什么才能使其按预期工作?

提前致谢,

莱利H

更新:

已编辑以包含“打印”标签

【问题讨论】:

  • Print 标签在哪里定义?

标签: linux assembly x86 nasm


【解决方案1】:

这是我在标准输出上打印数字的功能。对不起,在 AT&T ;)

movl <your decimal here>, %eax
xor %ecx, %ecx  # the counter
movl $10, %ebx  

loop:
xor %edx, %edx
div %ebx        # isolate the last digit, remainder in edx
add $48, %dx    # '0' is 48 in ascii, result is the ascii equivalent
shl $8, %dx     # move the ascii byte to %dh
pushw %dx       # puch ascii code on the stack
inc %esp        # point to the ascii byte! (discard %dl)
inc %ecx        # count the digits
cmp $0, %eax
jnz loop

movl $4, %eax     # write()
movl $1, %ebx     # stdout
movl %ecx, %edx   # now edx holds the number of digits
movl %esp, %ecx   # load the address of string array
int $0x80         # the string array is on top of the stack

干杯!

【讨论】:

    【解决方案2】:

    您需要将数字转换为 ASCII 数字才能打印到终端。现在我不会给你我的 dwtoa,这会让学习失去乐趣,但你可以这样做:

    sys_exit        equ     1
    sys_write       equ     4
    stdout          equ     1
    
    SECTION .bss
    lpBuffer    resb    4
    
    SECTION .text
    GLOBAL _start
    _start:
        xor     esi, esi
    
    .NextNum:
        call    PrintNum
        inc     esi
        cmp     esi, 100
        jna     .NextNum
    
    .Exit:                                 
        mov     eax, sys_exit                
        xor     ebx, ebx                      
        int     80h                           
    
    ;~ #####################################################################
    PrintNum:   
        push    lpBuffer
        push    esi 
        call    dwtoa
    
        mov     edi, lpBuffer
        call    GetStrlen
        inc     edx
        mov     ecx, lpBuffer
    
        mov     eax, sys_write
        mov     ebx, stdout
        int     80H     
        ret     
    
    ;~ #####################################################################    
    GetStrlen:
        push    ebx
        xor     ecx, ecx
        not     ecx
        xor     eax, eax
        cld
        repne   scasb
        mov     byte [edi - 1], 10
        not     ecx
        pop     ebx
        lea     edx, [ecx - 1]
        ret
    

    注意,我使用诸如 sys_exit、sys_write、stdout 之类的东西,而不是硬编码数字。使代码更加自我记录。

    【讨论】:

    • 我猜这是一个函数名 - dword 到 ascii。
    • 我在哪里可以得到这个dwtoa函数?
    【解决方案3】:

    编辑:这本身不是“错误”,而只是误导了普通读者将计数器和 strlen 作为单字节变量访问,并在其他地方将内容与 32 位变量进行比较...

    add BYTE[ecx], NUL

    这可能添加 NUL 终止符到ecx,但我想它应该追加终止符。 这可能发生在[ecx+1]

    无论如何,变量和指针的处理在你的代码中是非常规的......

    首先:'输出'内容的内核函数,假设 ecx 包含字符串的地址。任何地方都没有字符串allocated。如果字符串恰好适合在counter resb 8 处为计数器保留的八个字节,并且计数器将包含以下字符:'1','3','\0',那么该方法将起作用。这揭示了第二件事:printf 处理字符串,将单个数字 0-9 编码为值 48-57。空间在这个 ASCII 系统中编码为 32(十进制),而 \NUL 是 ascii 零。

    那么,需要什么:

    • 选项 1
      将您的 counter 初始化为字符串

      counter db '0','0','0','0','0','0','0','1'  
      length  dq 1
      

      Ascii 零不需要终止字符串,因为据我了解,它是 赋予打印功能

      然后可以将 real 指向字符串的指针作为

      lea ecx, counter     // get the address of counter string
      add ecx, 7           // this is the last character
      

      还可以将计数器作为字符串一次增加一位:

      loop:  
      mov al,[ecx]   // assuming ecx still points to last character  
      inc al  
      mov [ecx],al  
      cmp al, '9'  
      jle string ok  
      mov al, '0'  
      mov [ecx],al  
      dec ecx  
      jmp loop  
      ok:     // here the counter has been increased correctly  
      
    • 选项2

      将计数器增加为 32 位整数 使用以下算法将整数一次转换为一个数字:

      digits = 0;  
      string_ptr = &my_string[32];  // move barely outside the string  
      do {  
        last_digit = a % 10 + '0';      // calculate the last digit and convert to ASCII
        a = a / 10;  
        *--string_ptr = last_digit;     // write the last digit
        digits++;                       // count the number of digits  
      } while (a);  
      // because we predecrement string_ptr, that value also contains the exact  
      // start of the first character in the printable string. And digits contains the length.
      

    要产生一些好看的结果,仍然必须添加 换行。这可以单独处理,或者只是附加到原始字符串——并确保它们永远不会被覆盖,因此它们可以在所有情况下使用。

    【讨论】:

    • mul ebx;不将 esi 与 ebx 相乘。 MUL 的隐式操作数是 eax,结果传给 eax。 jle 也不比较相等性,而是比较 LESS 或 EQUAL,这是 OTOH 的一件好事,因为否则我看不到快速退出循环的方法。
    • 次要问题:mul ebx 的结果进入edx:eax - 不要指望edx 保持不变!
    猜你喜欢
    • 1970-01-01
    • 2017-05-27
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2013-06-30
    • 2011-10-17
    • 1970-01-01
    • 2016-02-24
    相关资源
    最近更新 更多