x86アセンブリ言語におけるビット操作命令の詳解

論理演算命令

論理演算命令は、ビット単位でデータを処理するために使用されます。これらの命令を実行すると、通常 CF(キャリー)と OF(オーバーフロー)はクリア(0)され、結果に応じて SF(サイン)や ZF(ゼロ)が設定されます。

命令 動作内容 主な用途
AND ビットごとの論理積。両方が1の場合のみ1。 特定のビットを0にする(マスキング)、偶数・奇数の判定。
OR ビットごとの論理和。どちらかが1なら1。 特定のビットを1にする(セット)、フラグの結合。
XOR ビットごとの排他的論理和。異なる値なら1。 ビットの反転、レジスタのゼロクリア(MOVより高速)。
NOT 全ビットを反転。 1の補数を求める。フラグには影響を与えない。
; 論理演算の例
MOV BL, 0b11001100
AND BL, 0b11111011 ; 第2ビットを0にクリア
OR  BL, 0b00000001 ; 第0ビットを1にセット
XOR BL, 0b00001111 ; 下位4ビットを反転
NOT BL             ; 全ビットを反転

シフトおよび回転命令

ビットを左右にずらすことで、算術計算やデータの抽出を高速に行うことができます。

論理シフトと算術シフト

  • SHL / SAL: 左シフト。空いた下位ビットには0が入る。符号なし・符号付き共に2倍の演算として機能。
  • SHR: 論理右シフト。最上位ビットに0を挿入。符号なし数値の除算に利用。
  • SAR: 算術右シフト。符号ビット(最上位ビット)を維持したまま右へずらす。符号付き数値の除算に利用。

回転命令

  • ROL / ROR: 循環シフト。押し出されたビットが反対側から入り、同時に CF にも格納される。
  • RCL / RCR: キャリーを含む循環シフト。CF を一つのビットとして扱い、全体を回転させる。
; シフト演算の例
MOV EAX, 12        ; 1100b
SHL EAX, 1         ; 24 (11000b)
SHR EAX, 2         ; 6  (110b)

MOV CL, 2
MOV EDX, -16       ; 負数
SAR EDX, CL        ; 算術右シフトによる除算 (-4)

ビットテストとスキャン命令

特定のビットの状態を確認したり、特定の条件を満たすビットの位置を探したりするための命令群です。

命令 動作 詳細
TEST AND演算を行いフラグを更新 元の値を破壊せずに ZF などでビット状態を確認する。
BT 指定ビットを CF にコピー ビットの値を直接テストする。
BTS / BTR / BTC テストして変更 CF にコピー後、そのビットを1にする(S)、0にする(R)、反転する(C)。
BSF / BSR ビットスキャン 最初に見つかった「1」のインデックスを返す(BSFは下位から、BSRは上位から)。
; ビットテストの例
TEST AL, 0b00001000 ; 第3ビットが1か確認
JZ   BitIsZero      ; 0ならジャンプ

MOV EBX, 0b10100000
BSR ECX, EBX        ; ECX = 7 (上位からスキャンして最初に見つかった1の位置)
BTS EBX, 4          ; 第4ビットを確認して1にセット

実装上の留意点とテクニック

  1. ゼロクリアの最適化: MOV EAX, 0 よりも XOR EAX, EAX の方が命令サイズが小さく、CPUのパイプライン最適化により実行速度が向上します。
  2. シフト回数のマスク: x86環境では、シフト回数(count)は通常、レジスタ幅に合わせてマスクされます(32ビットなら count & 0x1F)。そのため、32シフトすると0シフトと同じ結果になります。
  3. NOT と NEG の違い: NOT はビット反転(1の補数)ですが、NEG は符号反転(2の補数:ビット反転して1を加算)です。
  4. スキャン命令の未定義動作: BSFBSR のソースオペランドが 0 の場合、デスティネーションレジスタの値は未定義となるため、事前に TEST 命令等で 0 でないことを確認するのが安全です。
  5. アトミック操作: マルチプロセッサ環境で BTSBTR をメモリに対して使用する場合、LOCK プリフィックスを付加することでアトミックな(中断されない)操作を保証できます。これはスピンロックなどの実装に不可欠です。

タグ: x86 Assembly bit-manipulation Low-Level

5月19日 12:11 投稿