0

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

なぜこれを行うのですか?また、期待どおりに動作させるには何を変更する必要がありますか?

前もって感謝します、

ライリーH

アップデート:

「印刷」ラベルを含めるように編集

4

3 に答える 3

2

これは、標準出力に数字を出力する私の機能です。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

乾杯!

于 2013-01-01T00:02:32.353 に答える
0

編集:それ自体は「エラー」ではありませんが、カジュアルな読者がカウンターと strlen を 1 バイト変数としてアクセスし、他の場所で内容を 32 ビット変数と比較するという誤った指示です...

add BYTE[ecx], NUL

これはおそらくにターミネータを追加しますが、ターミネータを 追加する必要があると思います。それは場所で起こる可能性があります。NULecx[ecx+1]

とにかく、変数とポインターの処理は、コードでは非常に型破りです...

最初に、何かを「出力」するカーネル関数は、ecx に文字列のアドレスが含まれていると想定します。どこにも割り当てられた文字列はありません。カウンタ at に予約されている 8 バイトに文字列がちょうど収まり、カウンタに文字,counter resb 8が含まれる場合、このアプローチは機能します。そして、これは 2 番目のことを明らかにします: printf は、1 桁の 0 から 9 を値 48 から 57 にエンコードする文字列を処理します。たとえば、この ASCII システムのスペースは 32 (10 進数) にエンコードされますが、\NUL は ASCII ゼロです。'1''3''\0'

だから、何が必要です:

  • オプション 1文字列に
    初期化しますcounter

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

    文字列を終了するためにアスキーゼロは必要ありません.

    次に、文字列への実際のポインターを次のように指定できます。

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

    また、一度に 1 桁ずつ文字列としてカウンターを増やすこともできます。

    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 ビット整数として増やします。次のアルゴリズムを使用して、一度に 1 桁ずつ整数を文字列に変換します。

    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.
    

見栄えの良い結果を得るには、改行を追加する必要があります。これは個別に処理することも、元の文字列に追加することもできます。上書きされないようにすることで、あらゆる場合に使用できます。

于 2012-10-27T07:57:46.927 に答える
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 などを使用していることに注意してください。コードをもう少し自己文書化します。

于 2012-10-27T15:23:39.560 に答える