2016-11-01 2 views
2

미리 설정된 문자열을 대문자에서 소문자로 변환하려고합니다. 현재 주소에있는 레지스터를 8 비트 레지스터로 옮기고 ASCII 값을 테스트하여 매우 대문자인지 확인하는 방법을 매우 조잡한 방법으로 수행하고 있습니다. 그것에 대해 더 깨끗한 방법이 있습니까?어셈블리 문제에서 대문자를 Lowecase로 변환

지금은 ASCII 값에서 65를 빼서 25로 비교합니다. 대문자는 ASCII (dec) 65-90이므로 대문자는 0-25가됩니다. SUB 다음 부호 비교

.DATA 
string DB "ATest This String?.,/[}", '$' 
strSize DD 23 
.CODE 
strToLower PROC 
     LEA  EAX, string 
     PUSH EAX 
     CALL toLower2 ; write toLower2 
     POP EAX 
     LEA EAX, string  ; return char* to C++ 
     RET 
strToLower ENDP 

;--------------------------------------------- 
;Procedure: Convert to LowerCase 
;Input: Address in EBX 
;  unsigned in AL for each letter 
;Output: EAX will contain new string 
;--------------------------------------------- 

toLower2 PROC ;65-90 is upper, 97-122 is lower (XOR 32?) 
      LEA EBX, string 
      MOVE ECX, strSize 
      PUSH AL  ; PUSH AL before manipulating it 
loop1:  MOV AL, [EBX] ; Put char into AL to manipulate 
      XOR BL, BL   ;????????????? 
      MOV BL, AL   ;Set condition here??? 
      SUB BL, 65   ;????????????? 
      CMP BL, 25   ;if(i > 64 && < 91) i += 32; 
      JA NoCap   ; 
      ADD AL, 32   ;Adds 32 to ASCII value, making lower 
NoCap:  MOV [EBX], AL 
      INC EBX 
      LOOP loop1 
      POP AL  ;Replace/POP AL 
      LEA EAX, string 
toLower2 ENDP 
      END 
+0

char에'0x20'을 추가하십시오. '0x61 <= char <= 0x7a'이면 저장하십시오. 다른 문자로 이동 –

+2

이 코드는 유효하지 않으며 첫 번째 문자 뒤에 다른 메모리를 덮어 씁니다. 디버거에서 확인 했습니까? "?????" 'xor bl, bl'이후의 코멘트는 유망 해 보이지 않습니다. "sub bl, 65"대신에'sub bl, 'A''을 사용할 수도 있습니다 (또는 문자 리터럴과 작동하지 않는 경우 컴파일러를 변경하십시오). 그래서 0x20을 추가하면 "if below" a "skip write"및 "above"'z "skip write"else "새로운 값을 문자열에 다시 쓰십시오". 이 ASCII 문자를 소스로 숫자로 쓸 필요가 없습니다. – Ped7g

+0

@ Ped7g : 하나의 분기 (sub/cmp/unsigned-jcc를 사용)가있는 범위의 양쪽을 검사하는 것은 실제로 좋은 기술이며 잘 작동합니다. –

답변

2

는 단 하나의 조건 분기를 사용하여 특정 범위 내에서 되 입력을 확인하는 대신 >= 'A'<= 'Z'에 대해 별도의 비교 및-가지의 좋은 방법이다.

가능하면 컴파일러가이 트릭을 사용합니다. 효율적인 asm 작성에 대한 자세한 내용은 Agner Fog's Optimizing Assembly guide 태그 위키의 다른 링크를 참조하십시오.

한 브랜치가있는 영문자 (대문자 또는 소문자)를 감지하는데도 사용할 수 있습니다. OR은 0x20과 함께 대문자를 소문자로 만들지 만 영문자가 아닌 문자는 알파벳이 아닙니다. 그러면 unsigned-compare 트릭을 사용하여 소문자 범위에 있는지 확인하십시오. (또는 그 비트를 지우려면 ~0x20과 AND로 시작하여 대문자를 입력해야합니다.) an answer on flipping the case of alphabetic characters while leaving other characters alone에서이 트릭을 사용했습니다.

네가 눈치 챘 듯이 ASCII는 모든 글자의 대문자와 소문자의 차이가 1 비트를 뒤집을 수 있도록 설계되었습니다. 모든 소문자에는 0x20이 설정되고 대문자에는 지워집니다. (ADD/SUB와 비교하여)이 작업을 수행하는 경우 일반적으로 AND/OR/XOR을 사용하는 것이 좋습니다. 이는 한 가지 경우를 강제 할 때 초기 상태를 신경 쓰지 않기도하기 때 문입니다. PUSH AL도 푸시의 최소 크기 때문에/팝업 16 비트, 대부분의 조립과 조립하지 않습니다


코드는 몇 가지 이상한 물건이있다. AL을 저장/복원하는 것은 의미가 없습니다. 왜냐하면 루프 이후에 AL을 복원 한 직후에 EAX 전체를 clobber하기 때문입니다!

또한 MOV는 대상을 덮어 쓰므로 xor bl,bl이 필요하지 않습니다.

또한, 당신은 스크래치 레지스터로 BL을 사용하지만, EBX의 하위 바이트이다 (당신이 포인터로 사용!)

다음

는 난 단지 EAX, ECX와 EDX 그래서를 사용하여, 그것을 할 수있는 방법 레지스터를 저장/복원 할 필요가 없습니다. (대부분의 32 비트 및 64 비트 호출 규칙에서 저장/복원 기능이 필요한 EBX를 clavbers 함수). string이 정적으로 할당되지 않은 경우 여분의 레지스터가 필요합니다. 즉, 즉석 상수로 해당 주소를 사용하게 할 수 있습니다.

toLower2 PROC ;65-90 is upper, 97-122 is lower (XOR 32?) 
      mov edx, OFFSET string ; don't need LEA for this, and mov is slightly more efficient 
      add edx, strSize   ; This should really be an equ definition, not a load from memory. 

      ; edx starts at one-past-the-end, and we loop back to the start 
loop1: 
      dec edx 
      movzx eax, byte [edx]  ; mov al, [edx] leaving high garbage in EAX is ok, too, but this avoids a partial-register stall when doing the mov+sub in one instruction with LEA 
      lea ecx, [eax - 'A']  ; cl = al-'A', and we don't care about the rest of the register 

      cmp cl, 25    ;if(c >= 'A' && c <= 'Z') c |= 0x20; 
      ja NoCap 
      or  al, 0x20   ; tolower 
      mov [edx], al   ; since we're branching anyway, make the store conditional 
NoCap: 
      cmp edx, OFFSET string 
      ja loop1 

      mov eax, edx    
toLower2 ENDP 

The LOOP instruction is slow, and should be avoided. 그냥 존재하는 것을 잊어 버리고 편리한 루프 상태를 사용하십시오.

문자가 변경 될 때만 저장소를 수행하면 코드가 더 효율적이됩니다. 아무 것도하지 않으면 잠시 동안 변경되지 않은 메모리에서 사용할 때 캐시가 더럽지 않기 때문입니다.


ja NoCap 대신에 cmov로 분기없이 처리 할 수 ​​있습니다. 하지만 지금 우리는 LEA를 사용하여 플래그에 영향을주지 않고 0x20을 추가하여 레지스터를 저장하기 때문에 ADD/SUB 대신 AND/OR을 선호한다는 제안을 무시해야합니다.

loop1: 
      dec edx 
      movzx eax, byte [edx]  ; mov al, [edx] leaving high garbage in EAX is ok, too, but this avoids a partial-register stall when doing the mov+sub in one instruction with LEA 
      lea ecx, [eax - 'A']  ; cl = al-'A', and we don't care about the rest of the register 

      cmp cl, 25    ;if(c >= 'A' && c <= 'Z') c += 0x20; 
      lea ecx, [eax + 0x20] ; without affecting flags 
      cmovna eax, ecx   ; take the +0x20 version if it was in the uppercase range to start with 
      ; al = tolower(al) 

      mov [edx], al 
      cmp edx, OFFSET string 
      ja loop1 
관련 문제