2012-07-03 3 views
1

초기 문자열을 저장하는 버퍼를 사용하여 소문자 문자열을 대문자로 변환하는 프로그램을 작성하려고합니다. 내가 겪고있는 문제는 내 프로그램이 내가 준 문자열과 닮아야하는 무한 루프의 문자를 출력한다는 것이다.문자열을 입력하여 대문자로 출력하십시오.

  • 일부 서브 루틴 호출의 끝에서 ret을 사용하여 다음과 같이 내가 코드에 존재하는 생각

    다른 문제입니다. 문제가있는 문제는 입니다.이 서브 루틴의에는 실제로 ret이 필요하지 않으며 jmp과 함께 사용하는 것이 더 좋습니다. 솔직히 말해서, 저는이 두 가지 의미에 대해 약간 혼란스러워합니다. 예를 들어 ja이라는 서브 루틴은 통화가 끝나면 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 
+0

당신은 그 동작은 당신이 무엇을 기대에서 발산 곳을 찾기 위해, 당신의 프로그램을 통해 단계별로 디버거를 사용합니다. –

+0

디버깅을 많이했습니다. 방금 x86 어셈블리를 배우기 시작했습니다. 레지스터와 주소를 확인하고 있었는지 확인했습니다. 잘 아십니까? – zeboidlund

+0

코드를 단계별로 살펴본 결과 예상/의도하지 않은 특정 행이 하나 이상 발견되었을 것입니다. –

답변

4

귀하의 문제를 직접합니다 (read, 내가 기억하는 것과 당신이 그것을 처리 완료되면 당신은 결코 당신의 문자열 버퍼의 끝에 NUL 종료를 추가 없다는 사실에 기인한다 syscall은 널 (NULL)을 다시 읽지 않습니다).

불행하게도이 '음, 당신은 아마 당신이 buf 용량을 초과하지 않았는지 확인해야하지만 1킬로바이트으로, 나는 당신을 의심합니다 (홀수 제어 흐름으로 인해 할 조금 더 열심히, 그러나 SetupBuf을 변경하면 트릭을 할해야 d)는 학습 프로그램에 대한 걱정해야

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

그냥 당신이 적절하게 나타났습니다 코드(), 당신의 이상한 제어 흐름을 괴롭히는 것 같다 다른 이슈에

을 확인합니다. 희망이 당신의 방법에 더 적은을 spagetti 코드 당신을 도울 수 있다는 : 그래서 간단한 지침 (안 규칙, 단지 가이드 라인 주) :

  • JMP을 (그리고 조건부 점프가)는 동일한 절차에 lables로 이동하는 데 사용되어야한다 그렇지 않으면 다시 풀 수 없기 때문에 바인딩에 들어가기 시작합니다. 당신이 점프를 사용할 수있는 유일한 다른 시간은 꼬리 호출이지만,이 단계에서는 더 이상 혼란스러워 할 필요가 없습니다. 다른 절차에 갈 때

  • 항상 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 
+0

+1 좋은 답변입니다. – Rob

관련 문제