linux nasm程序集打印从零到100的所有数字

时间:2012-10-27 06:38:37

标签: linux assembly x86 nasm

我正在编写一个程序来打印从0到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

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

提前致谢,

RileyH

更新:

已修改为包含“打印”标签

3 个答案:

答案 0 :(得分:2)

这是我在stdout上打印数字的功能。它在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

干杯!

答案 1 :(得分:0)

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

add BYTE[ecx], NUL

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

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

首先:内核函数'输出'东西,假设ecx包含字符串的地址。在任何地方都没有分配字符串。如果字符串恰好适合保留在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.
    

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

答案 2 :(得分:0)

您需要将数字转换为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之类的东西,而不是硬编码的数字。使代码更自我记录。