shr eax, 1Fh
は、eax
の最上位ビットを分離するために役立ちます。 16進数1Fh
を10進数31
に変換すると分かりやすいでしょう。今度は、eax
が31だけ右にシフトしていることがわかります。eax
は32ビットの値なので、ビットを31右にシフトすると、最上位ビットが分離されます。eax
には0または1が含まれます。元の値はビット31でした(0でビットに番号を付けることを前提としています)。
符号ビットを隔離するための一般的なトリックです。値が2の補数のマシンで符号付き整数として解釈されるとき、最上位ビットは符号ビットです。値が負の場合は設定され(== 1)、そうでない場合はクリア(== 0)されます。もちろん、値が符号なし整数として解釈される場合、最上位ビットはその値を格納するために使用されるもう1つのビットであるため、最上位ビットは任意の値を持ちます。
ここでは、分解を通じて行ずつ行くことは、コードが何をするかです:
mov eax, edx
明らかに、入力がEDX
にありました。この命令はEDX
の値をEAX
にコピーします。これにより、後続のコードでEAX
の値を操作することができます。元の値は失われません(EDX
)。
shr eax, 1Fh
右31個の場所によるシフトEAX
は、このように最上位ビットを単離します。入力値が符号付き整数であると仮定すると、これが符号ビットになります。
EAX
は、元の値が負の場合は1を、そうでない場合は0を含みます。
add eax, edx
はEAX
で私たちの一時的な値を元の値(EDX
)を追加します。元の値が負の値の場合は、1が加算されます。それ以外の場合は、0が加算されます。
sar eax, 1
1つの場所によってシフトEAX
権利を。ここで相違点は、の算術の右シフトであるのに対し、SHR
はの論理の右シフトであることです。論理的にシフトすると、新たに露出したビットが0で埋められます。算術シフトは、最上位ビット(符号ビット)を新たに露光されたビットにコピーする。
一緒にすべてを置く
、これは、負の値が正しく丸みを帯びていることを確認するために2によって署名された整数値を分割するための標準的なイディオムです。
符号なしの値を2で除算すると、単純なビットシフトがすべて必要です。したがって:
shr eax, 1
しかし、符号付きの値を分割する際、あなたは符号ビットに対処する必要があります。
unsigned Foo(unsigned value)
{
return (value/2);
}
は同等です。 sar eax, 1
を使用して符号付き整数の除算を2で実装できますが、結果の値は負の無限大に丸められます。これは、常にゼロに向かって丸めるDIV
/IDIV
命令の動作とは異なります。ゼロへ向かう振る舞いをエミュレートしたい場合は、特別な処理が必要です。これは、コードが持つものとまったく同じです。あなたは次の関数をコンパイルするときに実際には、GCC、クラン、MSVC、そしておそらく他のすべてのコンパイラは、すべて正確にこのコードを生成します。
int Foo(int value)
{
return (value/2);
}
これは非常に古いトリックです。 Michael Abrashは、アセンブリ言語ののZenでそれについて議論しました。 1990年(Here is the relevant section)の書籍に掲載されています。それ以前はアセンブリ言語の専門家の間では確かによく知っていました。