は、唯一の条件分岐を使用して、一定の範囲内にある入力に対してチェック代わり>= 'A'
と<= 'Z'
ための別個の比較・アンド・ブランチのための良い方法です。
可能であれば、コンパイラはこのトリックを使用します。効率的なasmの作成についての詳細は、Agner Fog's Optimizing Assembly guideとx86タグ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
charに '0x20'を追加します。 '0x61 <= char <= 0x7a'ならば、保存してください。他の文字に移動する –
このコードは無効です。最初の文字の後に他のメモリを上書きします。デバッガでチェックしましたか、それは何ですか? "?????" 'xor bl、bl'の後のコメントは有望に見えません。また、 "sub bl、65"の代わりに 'sub bl、 'A''を使うこともできます(あるいは、文字リテラルで動作しない場合はコンパイラを変更します)... 0x20を追加した後、" if below "スキップ書き込み "と"上の場合は "z"スキップ書き込み "else"新しい値を文字列に書き戻す "。これらのASCII文字を数値としてソースに書き込む必要はありません。 – Ped7g
@ Ped7g:1つのブランチ(sub/cmp/unsigned-jccを使用)の範囲の両側を調べることは、実際には良いテクニックであり、うまく機能します。 –