Skip to content

位运算

汇编语言中的位运算指令非常丰富,它们直接在二进制位上进行操作,效率极高。在 x86 架构中,位运算指令主要可以分为以下五大类:


一、 逻辑运算指令 (Logical Instructions)

这类指令类似于高级语言中的 &, |, ^, ~ 运算符,按位执行布尔逻辑。

  1. AND (按位与):
    • 作用:只有两个对应位都为 1 时,结果才为 1。
    • 常见用途:掩码操作(清空特定位)。例如 and al, 0x0F 会保留低4位,清空高4位。
    • 标志位:清空 CF 和 OF,根据结果设置 ZF、SF、PF。
  2. OR (按位或):
    • 作用:只要对应位中有一个为 1,结果就为 1。
    • 常见用途:设置特定位。例如 or al, 0x80 会强制将最高位设为 1。
  3. XOR (按位异或):
    • 作用:对应位不同则为 1,相同则为 0。
    • 常见用途:取反特定位或寄存器清零。xor rax, rax 比 mov rax, 0 更快且指令更短。
  4. NOT (按位取反):
    • 作用:0 变 1,1 变 0(一元操作)。
    • 注意:不影响任何标志位。
  5. TEST (逻辑比较):
    • 作用:执行 AND 运算,但不保存结果,只更新标志位。
    • 常见用途:检查某一位是否为 1。例如 test al, 1 可以检查 AL 是否为奇数。

二、 移位指令 (Shift Instructions)

移位指令会将位向左或向右移动,空出的位置补 0 或补符号位。

  1. SHL / SAL (逻辑左移/算术左移):
    • 动作:所有位左移,最低位补 0,移出的最高位进入 CF。
    • 用途:快速计算 乘以 2 的 n 次方。
  2. SHR (逻辑右移):
    • 动作:所有位右移,最高位补 0,最低位进入 CF。
    • 用途:快速计算 无符号除以 2 的 n 次方。
  3. SAR (算术右移):
    • 动作:所有位右移,最高位补 原来的符号位(保持正负号不变)。
    • 用途:快速计算 有符号除以 2 的 n 次方。

三、 循环移位指令 (Rotate Instructions)

移出的位不会丢弃,而是绕回到另一端重新进入。

  1. ROL (左循环移位):最高位移出后,进入 CF 的同时回到最低位。
  2. ROR (右循环移位):最低位移出后,进入 CF 的同时回到最高位。
  3. RCL (带进位左循环移位):将 CF 也看作数据的一部分参与循环。
  4. RCR (带进位右循环移位):同上,右向循环。

四、 位测试与修改指令 (Bit Test & Modify)

这类指令允许你直接操作指定的某一个二进制位(通过索引)。

  1. BT (Bit Test):把指定的第 \(n\) 位复制到 CF 标志位。
  2. BTS (Bit Test and Set):把第 \(n\) 位存入 CF,然后将该位置 1。
  3. BTR (Bit Test and Reset):把第 \(n\) 位存入 CF,然后将该位置 0。
  4. BTC (Bit Test and Complement):把第 \(n\) 位存入 CF,然后将该位 翻转 (0\(\leftrightarrow\)1)。

五、 位扫描指令 (Bit Scan)

用于寻找寄存器中第一个出现的 "1"。

  1. BSF (Bit Scan Forward):从低位向高位扫描,返回第一个 1 的索引。
  2. BSR (Bit Scan Reverse):从高位向低位扫描,返回第一个 1 的索引。
    • 如果源操作数为 0,则 ZF 被设为 1。

六、 高级位操作指令 (BMI1 / BMI2 扩展)

现代 CPU (Intel Haswell / AMD Piledriver 以后) 增加了一些更强大的指令: * POPCNT:计算一个数字里总共有多少个 "1"(Hamming weight)。 * LZCNT:计算开头有多少个连续的 "0"。 * TZCNT:计算末尾有多少个连续的 "0"。

总结表格

操作类型 指令 核心特点
逻辑 AND, OR, XOR, NOT 处理位遮罩、反转、清零
移位 SHL, SHR, SAR 用于 2 的幂次乘除法运算
循环 ROL, ROR, RCL, RCR 数据重组、加密算法常用
单位 BT, BTS, BTR, BTC 精确控制某一个比特位
扫描 BSF, BSR, POPCNT 查找 1 的位置或统计数量

学习建议:位运算最常配合 TEST 和 Jcc 指令使用。比如你想判断 RAX 的第 3 位是否为 1,通常写成:

test rax, 8    ; 8 的二进制是 1000 (第3位是1,从0开始数)
jnz bit_is_set ; 如果结果不为0,说明第3位是1
你想试着写一个程序来判断一个数是偶数还是奇数吗?用 TEST 指令非常简单。