1

バッファを使用して最初の文字列を格納し、小文字の文字列を大文字に変換するプログラムを作成しようとしています。私が経験している問題は、私のプログラムが、指定した文字列に類似している必要がある文字の無限ループを出力することです。

コードに存在すると私が信じる他の問題は次のとおりです。

  • 一部のサブルーチンretは、呼び出しの最後に使用します。私が問題を抱えている問題は、これらのサブルーチンのどれが実際にを必要としないかを理解することであり、。retでよりよく使用されjmpます。正直なところ、私はここで2つのセマンティクスの間で少し混乱しています。たとえば、で呼び出されたサブルーチンは、呼び出しの最後に'edjaする必要がありますか?ret

  • また、値の変換に使用されるループの各反復内で発生する反復数を出力しようとしています。なんらかの理由で、私はincカウンターを作成し、ルーチンでそれを印刷することを決心しますが、PrintNumIter残念ながら、それは何もしません。

完全なプログラムは次のとおりです。

Codez

bits 32

[section .bss]

        buf: resb 1024                  ;allocate 1024 bytes of memory to buf

[section .data]

        ;*************
        ;* CONSTANTS *
        ;*************

        ;ASCII comparison/conversion

        LowercaseA:     equ 0x61
        LowercaseZ:     equ 0x7A
        SubToUppercase: equ 0x20

        ;IO specifiers/descriptors

        EOF:            equ 0x0

        sys_read:       equ 0x3
        sys_write:      equ 0x4

        stdin:          equ 0x0
        stdout:         equ 0x1
        stderr:         equ 0x2

        ;Kernel Commands/Program Directives

        _exit:          equ 0x1
        exit_success:   equ 0x0
        execute_cmd:    equ 0x80

        ;Memory Usage

        buflen:         equ 0x400   ;1KB of memory


        ;*****************
        ;* NON-CONSTANTS *
        ;*****************

        iteration_count:    db 0
        query :             db "Please enter a string of lowercase characters, and I will output them for you in uppercase ^.^: ", 10   
        querylen :          equ $-query

[section .text]

    global _start
;===========================================
;             Entry Point
;===========================================

_start:
        nop                                         ;keep GDB from complaining
        call    AskUser 
        call    Read
        call    SetupBuf
        call    Scan
        call    Write
        jmp     Exit

;===========================================
;           IO Instructions
;===========================================

Read:
        mov     eax, sys_read                       ;we're going to read in something
        mov     ebx, stdin                          ;where we obtain this is from stdin
        mov     ecx, buf                            ;read data into buf
        mov     edx, buflen                         ;amount of data to read

        int     execute_cmd                         ;invoke kernel to do its bidding
        ret

Write:
        mov     eax, sys_write                      ;we're going to write something
        mov     ebx, stdout                         ;where we output this is going to be in stdout
        mov     ecx, buf                            ;buf goes into ecx; thus, whatever is in ecx gets written out to
        mov     edx, buflen                         ;write the entire buf

        int     execute_cmd                         ;invoke kernel to do its bidding
        ret

AskUser:
        mov     eax, sys_write
        mov     ebx, stdout
        mov     ecx, query
        mov     edx, querylen   

        int     execute_cmd
        ret

PrintNumIter:
        mov     eax, sys_write
        mov     ebx, stdout
        push    ecx                                 ;save ecx's address
        mov     ecx, iteration_count                ;print the value of iteration_count
        mov     edx, 4                              ;print 4 bytes of data

        int     execute_cmd
        pop     ecx                                 ;grab the value back in
        ret
;===========================================
;           Program Preperation
;===========================================

SetupBuf:
        mov     ecx, esi                        ;place the number of bytes read into ecx
        mov     ebp, buf                        ;place the address of buf into ebp
        dec     ebp                             ;decrement buf by 1 to prevent "off by one" error
        ret                                         

;===========================================
;           Conversion Routines     
;===========================================

ToUpper:
        sub     dword [ebp + ecx], SubToLowercase   ;grab the address of buf and sub its value to create uppercase character


Scan:
        call    PrintNumIter                        ;print the current iteration within the loop

        cmp     dword [ebp + ecx], LowercaseA       ;Test input char against lowercase 'a'
        jb      ToUpper                             ;If below 'a' in ASCII, then is not lowercase - goto ToLower

        cmp     dword [ebp + ecx], LowercaseZ       ;Test input char against lowercase 'z'
        ja      ToUpper                             ;If above 'z' in ASCII, then is not lowercase - goto ToLower

        dec     ecx                                 ;decrement ecx by one, so we can get the next character
        inc     byte [iteration_count]              ;increment the __value__ in iteration count by 1
        jnz     Scan                                ;if ecx != 0, then continue the process
        ret

;===========================================

;Next:
;       dec     ecx                             ;decrement ecx by one
;       jnz     Scan                            ;if ecx != 0 scan
;       ret

;===========================================

Exit:
        mov     eax, _exit
        mov     ebx, exit_success

        int     execute_cmd
4

1 に答える 1

4

あなたの問題は、処理が完了した後、文字列バッファの最後にnulターミネータを追加しないという事実に直接起因しています(私が覚えていることから、readsyscallはnullを読み返しません)。

残念ながら、これは奇妙な制御フローのために行うのが少し難しいですが、変更することでうまくいくSetupBufはずです(注:オーバーフローしていないことを確認する必要がありますbufが、1KBの場合、心配する必要はないと思います学習プログラム):

SetupBuf:
        mov     ecx, esi                        
        mov     ebp, buf
        mov     [ebp+ecx],0  ;make sure the string is nul terminated            
        dec     ebp                             
        ret

注意してください

あなたのコード(あなたが適切に気づいた)を悩ませているように見える別の問題に、あなたの奇妙な制御フロー。とてもシンプルなガイドライン(注:ルールではなく、単なるガイドライン)で、スパゲッティコードを減らすのに役立つことを願っています:

  • JMP(および条件付きジャンプ)は、同じ手順でラベルに移動するためにのみ使用する必要があります。そうしないと、元に戻すことができないため、バインドを開始します。ジャンプを使用できる他の唯一の時間は末尾呼び出しですが、この段階ではそれについて心配する必要はありません。さらに混乱します。

  • CALL別の手順に進むときは常に使用してください。これにより、 RETN/RET命令を使用して呼び出しサイトに正しく戻ることができ、制御フローがより論理的になります。

簡単な例:

print_num: ;PROC: num to print in ecx, ecx is caller preserved
    push ecx
    push num_format ; "%d\n" 
    call _printf
    sub esp,8 ;cleanup for printf
    retn

print_loop_count: ;PROC: takes no args
    mov ecx,0x10 ;loop 16 times

do_loop: ;LABEL: used as a jump target for the loop
         ;good idea to prefix jump lables with "." to differentiate them
   push ecx ;save ecx
   call print_num ;value to print is already in ecx
   pop ecx ;restore ecx
   dec ecx
   jnz do_loop ;again?

   retn
于 2012-07-03T07:54:12.060 に答える