2016-11-01 1 views
2

J'écris pour convertir une chaîne prédéfinie de majuscule en minuscule. Je suis en train de déplacer ce qui est à l'adresse d'un registre 8bit, puis de faire une façon très bâclée de tester la valeur ASCII pour voir si elle est en majuscules. Y a-t-il une façon plus propre de s'y prendre?Conversion de majuscules en valeurs Lowecase dans l'assemblage

En ce moment je soustrais 65 de la valeur ASCII et de la comparaison à 25. Puisque les majuscules sont ASCII (dec) 65-90, toutes les majuscules se traduiront par 0-25.

.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

Ajoutez '0x20' au caractère. si '0x61 <= char <= 0x7a', alors stocker. Sinon, passez au caractère suivant –

+2

Ce code n'est pas valide, il écrasera une autre mémoire après le premier caractère. Avez-vous vérifié dans le débogueur, que fait-il? Ayant "?????" commenter après 'xor bl, bl' ne semble pas prometteur. Vous pouvez également utiliser 'subbl, 'A'' au lieu de" sub bl, 65 "(ou changer de compilateur, si cela ne fonctionne pas avec les littéraux de caractères) ... donc après avoir ajouté 0x20, vous pouvez tester" if below " a'' skip write "et" if above ''z'' skip écrivent" else "réécrivent la nouvelle valeur dans la chaîne". Pas besoin d'écrire ces caractères ASCII dans la source sous forme de nombres. – Ped7g

+0

@ Ped7g: Vérifier les deux côtés d'une gamme avec une branche (en utilisant sub/cmp/unsigned-jcc), est en fait une bonne technique, et fonctionne bien. –

Répondre

2

SUB puis un unsigned comparer est un bon moyen de vérifier une entrée étant dans une certaine plage en utilisant une seule branche conditionnelle, au lieu de comparer-et-branches distinctes pour >= 'A' et <= 'Z'.

Les compilateurs utilisent cette astuce lorsque c'est possible. Voir aussi Agner Fog's Optimizing Assembly guide, et d'autres liens dans le wiki tag pour plus d'informations sur l'écriture d'asm efficace.

Vous pouvez même l'utiliser pour détecter des caractères alphabétiques (majuscules ou minuscules) avec une branche: OU avec 0x20 rendra n'importe quelle minuscule majuscule, mais ne rendra aucun caractère alphabétique non alphabétique. Faites-le, puis utilisez l'astuce non signée pour comparer les valeurs en minuscules. (Ou commencez par ET avec ~0x20 pour effacer ce bit, en forçant les majuscules). J'ai utilisé cette astuce dans an answer on flipping the case of alphabetic characters while leaving other characters alone.

Et oui, comme vous l'avez remarqué, ASCII est conçu de sorte que la différence entre les lettres majuscules/minuscules pour chaque lettre ne fait que retourner un bit. Chaque caractère minuscule a 0x20 défini, tandis que les majuscules l'ont effacé. ET/OU/XOR sont généralement préférables pour ce faire (par rapport à ADD/SUB), parce que vous pouvez parfois profiter de ne pas se soucier de l'état initial, en forçant à un cas.


Votre code a des trucs bizarres: PUSH AL n'assemblez même pas avec la plupart des assembleurs, puisque la taille minimale pour push/pop est 16 bits. Il n'y a pas non plus de raison de sauvegarder/restaurer AL, parce que vous vous débarrassez de toute EAX juste après avoir restauré AL après la boucle!

En outre, MOV écrase juste sa destination, il n'y a donc pas besoin de xor bl,bl.

En outre, vous utilisez BL comme un registre à gratter, mais il est faible octet de EBX (que vous utilisez comme un pointeur!)

Voici comment je pourrais le faire, en utilisant seulement EAX, ECX et EDX donc je Vous n'avez pas à sauvegarder/restaurer les registres. (Votre fonction clobbers EBX, dont la plupart des conventions d'appel 32 et 64 bits nécessitent des fonctions pour enregistrer/restaurer). J'aurais besoin d'un registre supplémentaire si string n'était pas alloué statiquement, me laissant utiliser son adresse comme constante immédiate.

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. Juste oubliez qu'il existe même et utilisez n'importe quelle condition de boucle est commode. Le fait de ne stocker le caractère que lorsque le personnage change rend le code plus efficace, car il ne salit pas le cache lorsqu'il est utilisé sur une mémoire qui n'a pas changé depuis un moment s'il n'y a rien à faire.


Au lieu de ja NoCap, vous pourriez faire branchlessly avec un cmov. Mais maintenant je dois ignorer ma suggestion de préférer ET/OU au lieu de ADD/SUB, parce que nous pouvons utiliser LEA pour ajouter 0x20 sans affecter les drapeaux, nous enregistrez un registre.

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