는 단 하나의 조건 분기를 사용하여 특정 범위 내에서 되 입력을 확인하는 대신 >= 'A'
및 <= 'Z'
에 대해 별도의 비교 및-가지의 좋은 방법이다.
가능하면 컴파일러가이 트릭을 사용합니다. 효율적인 asm 작성에 대한 자세한 내용은 Agner Fog's Optimizing Assembly guide 및 x86 태그 위키의 다른 링크를 참조하십시오.
한 브랜치가있는 영문자 (대문자 또는 소문자)를 감지하는데도 사용할 수 있습니다. 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
char에'0x20'을 추가하십시오. '0x61 <= char <= 0x7a'이면 저장하십시오. 다른 문자로 이동 –
이 코드는 유효하지 않으며 첫 번째 문자 뒤에 다른 메모리를 덮어 씁니다. 디버거에서 확인 했습니까? "?????" 'xor bl, bl'이후의 코멘트는 유망 해 보이지 않습니다. "sub bl, 65"대신에'sub bl, 'A''을 사용할 수도 있습니다 (또는 문자 리터럴과 작동하지 않는 경우 컴파일러를 변경하십시오). 그래서 0x20을 추가하면 "if below" a "skip write"및 "above"'z "skip write"else "새로운 값을 문자열에 다시 쓰십시오". 이 ASCII 문자를 소스로 숫자로 쓸 필요가 없습니다. – Ped7g
@ Ped7g : 하나의 분기 (sub/cmp/unsigned-jcc를 사용)가있는 범위의 양쪽을 검사하는 것은 실제로 좋은 기술이며 잘 작동합니다. –