2016-11-01 5 views
2

あらかじめ設定された文字列を大文字から小文字に変換するように書いています。私は現在、アドレスにあるものを8ビットレジスタに移動しています。次に、大文字かどうかを調べるためにASCII値をテストするという非常に厄介な方法を行っています。それについてもっとクリーンな方法がありますか?アセンブリの問題で大文字を小文字に変換する

今は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 "スキップ書き込み "と"上の場合は "z"スキップ書き込み "else"新しい値を文字列に書き戻す "。これらのASCII文字を数値としてソースに書き込む必要はありません。 – Ped7g

+0

@ Ped7g:1つのブランチ(sub/cmp/unsigned-jccを使用)の範囲の両側を調べることは、実際には良いテクニックであり、うまく機能します。 –

答えて

2

は、唯一の条件分岐を使用して、一定の範囲内にある入力に対してチェック代わり>= 'A'<= 'Z'ための別個の比較・アンド・ブランチのための良い方法です。

可能であれば、コンパイラはこのトリックを使用します。効率的なasmの作成についての詳細は、Agner Fog's Optimizing Assembly guideタグwikiの他のリンクも参照してください。

英数字(大文字または小文字)を1つのブランチで検出することもできます.0x20のORは大文字を小文字にしますが、アルファベット以外の英字はアルファベットにしません。それでは、unsigned-compareトリックを使用して、小文字の範囲にあるかどうかをチェックします。 (あるいは、そのビットをクリアするには、ANDで~0x20で始まり、大文字を強制する)。私はan answer on flipping the case of alphabetic characters while leaving other characters aloneでこのトリックを使用しました。

あなたが気づいたように、ASCIIはすべての文字の大文字/小文字の違いがちょうど1ビット反転するように設計されています。すべての小文字に0x20が設定され、大文字にはクリアされます。 1つのケースに強制するときに初期状態を気にしないことがあるので、これを実行するには通常はAND/OR/XORが適しています(対ADD/SUB)。


あなたのコードは、いくつかの奇妙なものがあります:プッシュ/ポップの最小サイズは16ビットであるので、PUSH ALでも、ほとんどのアセンブラでアセンブルしていません。 ALの保存/復元のポイントもありません。なぜなら、ループの後でALを復元した直後にEAX全体を壊すからです!

また、MOVは宛先を上書きするだけなので、xor bl,blは不要です。

また、あなたはスクラッチ・レジスタとしてBLを使用しますが、それはEBXの下位バイトだ(あなたはポインタとして使用!)

ここで私はこれだけEAX、ECXとEDXを使用して、私はそれを行う可能性がある方法ですレジスタをセーブ/リストアする必要はありません。 (あなたの関数は、ほとんどの32ビットと64ビットの呼び出し規約で保存/復元する関数を必要とするEBXをクローバーします)。 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 
関連する問題