x86_16 汇编中如何检查位字段的多个标志位是否全部置位?
原来的 AND、CMP、JNE 写法正确而直接:它判断指定标志位是否全部置位,同时忽略其他位。对于这个多位掩码,一条 TEST 配合 JZ 或 JNZ 无法完成相同的判断。
检查“全部置位”的条件
设属性值为 attributes,掩码为 mask,需要判断的条件是:
(attributes & mask) == mask
掩码 0b0000000010011001,即 0x0099,要求第 0、3、4、7 位全部为 1,位编号从 0 开始。其他位经过 AND 后都会变成 0,不影响比较。
原来的代码实现了这个条件,可以继续使用:
mov ax, word[vbe_mode.attributes]
and ax, 0b0000000010011001
cmp ax, 0b0000000010011001
jne .missing_flags
代码先读取属性值,用掩码清除无关位,再将结果与掩码比较。只要任意一个必需位为 0,就会跳转到 .missing_flags。这些指令只修改寄存器及相关标志,不会改写内存中的属性值。
TEST 可以忽略无关位,但判断条件不同
TEST 计算按位与并更新标志寄存器,但不保存结果。它也能通过掩码忽略不关心的位。
例如:
test word[vbe_mode.attributes], 0b0000000010011001
jz .none_flags
这里的 JZ 判断的是:
(attributes & mask) == 0
指定标志位全部为 0 时,代码会跳转。如果改为 JNZ,则会在其中至少一位置位时跳转。
这与判断“指定标志位全部为 1”不同。例如,属性值为 0x0001 时,TEST 的结果已经非零,但掩码要求的另外三个位仍然缺失。
检查单个位时,“至少一个指定位置位”和“所有指定位置位”才是同一个条件,此时可以直接使用 TEST 配合条件跳转。
当前掩码可以只检查低字节
这个掩码的高 8 位全部为 0,因此只读取并检查低字节,也能得到相同的分支结果:
mov al, byte[vbe_mode.attributes]
and al, 0b10011001
cmp al, 0b10011001
jne .missing_flags
x86 使用小端字节序,字属性所在地址的第一个字节就是低字节。对于这里的掩码,这种写法通常能减少指令编码长度,但指令条数仍然是四条。运行速度是否更快取决于目标处理器和执行环境。
使用前需要确认:
- 后续代码不依赖原写法留下的完整
AX值,因为字节版本不会修改AH。 - 掩码始终只涉及低 8 位;如果以后需要检查高字节中的标志,应恢复为字操作。
如果没有代码体积或实测性能方面的需求,保留原来的字操作实现即可。仅凭这段代码,无法认定存在一个在所有 x86 处理器上都更快的替代方案。
备注:内容仅供参考。
