想象你是银行里的一位资深交易员,面前摆着一排排整齐的“现金抽屉”——这就是CPU里的寄存器(Register),每个抽屉能存放一个数字。银行给你发了一本《操作手册》,里面全是让你对抽屉里的钱做处理的命令:把两个抽屉的钱加起来(ADD)、从一个抽屉里减去另一个的钱(SUB)、把某个抽屉的钱翻倍再翻倍(SHL)、检查两个抽屉的钱是否一样(CMP)……这本手册,就是x86指令集里的“算术与逻辑指令体系”。 你可能会问:我只是个AI工程师,天天写Python,为什么要搞懂这些“底层账本”?因为无论是训练神经网络时累加梯度(ADD)、计算注意力得分(MUL),还是判断两个向量是否匹配(CMP后条件跳转),最终都会编译成这些基础指令在CPU上执行。当你理解了这些指令,你就能读懂性能剖析器的输出,明白为什么某段代码快、某段代码慢,甚至能写出对CPU缓存和流水线更友好的高效代码——这在优化大模型推理、数据处理管道时,价值无法估量。
掌握x86算术与逻辑指令,等于拿到了读懂CPU底层“每一笔账目”的钥匙。对AI从业者而言,这是调优高性能计算、理解编译器行为和定位系统瓶颈的必备内功。
可视化演示
展示ADD/SUB算术指令ALU执行与EFLAGS标志位设置、AND/OR/XOR位运算与SHL/SHR移位操作、MUL/DIV隐式操作数乘除法流程,揭示x86指令体系核心机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
ADD/SUB算术指令执行 ALU执行:操作数经ALU执行加减法运算输出结果 EFLAGS:CF/ZF/SF/OF标志位根据运算结果设置 ADC/SBB:带进位加减法利用CF实现多精度运算 result = op1 + op2; EFLAGS = compute_flags(result) ADD执行加法并更新标志位驱动条件跳转
加减法基础算术
交易员每天最频繁的操作是什么?清点现金、合并账户、支付款项。在银行柜台,加法和减法就是最基本的“入账”和“出账”。而在 CPU 的世界里,ADD 和 SUB 指令扮演着同样的角色——它们是构成一切复杂运算的地基。当你写 total = total + price 时,编译器最终会生成一条 ADD 指令;当你写 balance = balance - pay 时,背后就是一条 SUB。
从交易映射到技术,ADD 指令的运作逻辑极其直白:它从左边操作数(目标)和右边操作数(源)中取出数值,执行加法,然后把结果“回写”到目标操作数所在的位置。比如 ADD AX, BX 的意思就是:AX = AX + BX。SUB 同理,执行 AX = AX - BX。在汇编语言中,操作数可以是寄存器、内存单元或立即数(即直接写在指令里的常数)。需要注意的是,x86 指令集中绝不允许两个操作数都来自内存——交易员不能同时从两个远处仓库直接调货,必须有一个放到最近的抽屉(寄存器)里。
这些基础算术指令会自动更新标志寄存器 EFLAGS 中的几个关键位:如果结果为零,零标志位 ZF 被置 1;如果最高位发生了进位或借位,进位标志 CF 被置 1;如果结果超出了目标类型的取值范围,溢出标志 OF 被置 1。此外还有 INC(加一)和 DEC(减一)指令,它们是专为计数器优化的“快捷操作”——比如遍历数组时用 INC 递增索引、递减循环次数时用 DEC,它们在底层比 ADD AX, 1 更节省编码空间。
从交易员场景回看:ADD 和 SUB 之所以是基石,是因为现代 CPU 内部的一切复杂运算——乘法、除法、地址计算——最终都能分解为一系列加减法。而作为 AI 工程师,理解 ZF、CF 这些“账本标记”,是读懂后续条件跳转(JE、JNE)的前提。
- 目标回写指令执行后,结果存回第一个操作数(目标),类似于交易员把合并后的现金放回原抽屉。
- 零标志更新运算后 CPU 检查结果,若为 0,则将 ZF 标志置 1,等价于账本上标记“此账户已清零”。
- 进位标志更新若加法产生最高位进位(无符号溢出),CF 被置 1,如同收到了“超出预期”的额外一摞现金。
乘除法宽积商余
如果说加减法是日常进出账,那乘除法就是“批量结算”。交易员月底要按 6 周每周 7 单的节奏计算总订单量,直接做 6+7 显然不对,需要一张“乘法表”。CPU 的 MUL 和 DIV 就是内置的乘法表,但它们有一个非常独特的规则:结果比想象中宽。
从交易规则说起:MUL(无符号乘法)有一个“双倍宽度”的隐藏约定。当你执行 MUL BX 时,被乘数默认放在 AX 中,而乘积的高 16 位被强制存入 DX,低 16 位存入 AX。这就像交易员不仅把总额放回原抽屉,还发现结果太大、一个抽屉装不下,于是把多出来的部分放进了相邻的备用抽屉(DX)。如果是 MUL ECX(32 位),那么结果用 EDX:EAX 这对 64 位组合来存放。“双倍宽度”保证了乘法永远不会因为空间不足而溢出。
除法 DIV 正好是乘法的逆运算:它把 DX:AX 组成的宽值作为被除数,除以操作数,商放回 AX,余数放入 DX。比如 DIV BX 就表示 AX = (DX:AX) / BX、DX = (DX:AX) % BX。有符号版本的 IMUL 和 IDIV 与之一一对应,只不过数值按补码解释。接下来看核心公式:
回到交易员视角:为何要如此大费周章?因为 CPU 的寄存器宽度是固定的,如果乘法结果不扩大到双倍宽度,进位信息就会永久丢失而无法察觉。对 AI 工程师来说,这解释了为什么在 C/C++ 中做 32 位整数乘法时,编译器有时会生成 imul 指令而不是 mul——有符号版本更灵活,且允许两个操作数不同。理解 MUL/DIV 的“宽结果”约定,能帮你避免在底层编码时因为溢出而写出“账实不符”的 bug。
MUL 像一张能展开的巨型收据——一个抽屉装不下乘积时,自动扩展到 DX 和 AX 组合的“加长收据”;DIV 则像拆包裹:商放进主抽屉,余数放进副抽屉,各归其位。- 乘积双倍存放
MUL将乘积的高位存入 DX、低位存入 AX,保证不丢失信息。 - 商与余数的分配
DIV用 32 位(DX:AX)整体做除法,商放到 AX,余数放到 DX。
按位逻辑与或非
银行柜员每天要核验凭证:检查一张支票的某些位是否被盖章、把几张凭证的标记合并、或者核验某个开关是否处于“关”状态。计算机里也有类似的操作,不过对象变成了一片“二进制位”。这就是 AND、OR、XOR、NOT 四位“位级操作员”。
以交易场景来说,AND 像“保险箱双重锁校验”:只有当两把钥匙(两个操作数)在同一位置都为 1 时,该位置才被点亮(结果为 1)。这在编程中常用于“清零掩码”——比如只保留一个整数的低 8 位,可以用 AND AX, 0x00FF,高 8 位被全部屏蔽。OR 像“合并员工工牌权限”:任一操作数在某位为 1,结果该位即为 1,用于把多个开关标志合入一个状态字。XOR 像“核对账目是否一致”:两位相同时为 0,不同时为 1,因此 XOR AX, AX 可以快速把寄存器清零——这是编译器最喜欢的惯用法,比 MOV AX, 0 更短。NOT 则简单粗暴地翻转每一位,相当于把账本上的记录全部黑白颠倒。
回扣交易员视角:按位逻辑在 AI 领域到处都是——位掩码(bitmask)用来压缩存储多个布尔状态、二进制特征向量做 AND 就是特征的交集判断、用 XOR 比较两个哈希是否匹配(密码学中的 “异或校验”)。最重要的是,理解 XOR AX, AX 这种“清零惯用法”,能让你的汇编阅读能力上一个台阶。
- 与运算按位求解对 A 和 B 的每个二进制位分别执行“两者皆 1 才为 1”的规则。
- 异或运算按位求解对每个位执行“不同则为 1、相同则为 0”的规则。
移位乘除2幂
交易员发现一个快捷技巧:当需要对某个数字“乘以 10 的整数倍”时,不需要逐笔计算,只需要把账本上的数字往左平移、并在末尾补零就行。比如数字 3 在十进制的账本上左移一位变成 30,相当于乘以 10。计算机的二进制世界同样如此,而且更彻底——因为二进制的每一位都代表 2 的幂,所以左移一位等价于乘以 2、右移一位等价于除以 2。
SHL(Shift Logical Left)指令把目标操作数整体向左移动指定的位数,右边腾出的空位一律补 0。SHR(Shift Logical Right)则向右移,左边补 0。比如 SHL AX, 1 相当于 AX = AX * 2,SHL AX, 3 相当于 AX = AX * 8。这在性能上远比调用 MUL 指令快得多,因为移位是 CPU 的单周期操作,而乘法需要更多周期。SAL(Shift Arithmetic Left)与 SHL 在左移时完全等价;但 SAR(Shift Arithmetic Right)与 SHR 不同——算术右移会保留符号位,对负数右移时左边补的是 1 而不是 0,从而保证结果仍然是正确的负数。
在真实的 AI 工程中,编译器经常把 x * 2 优化成 SHL x, 1、把 x / 8 优化成 SAR x, 3(对于有符号正数场景)或 SHR x, 3(对无符号)。当你用 size_t index = base + offset 时,CPython 的 PyLong 对象内部也会大量使用移位来快速乘 2。理解了这一节,你就能在阅读编译产物时一眼认出这些“隐藏的快捷操作”。
1 变成 10、10 变成 100),总金额正好乘以 2^n;SAR 则像把每张钞票面额缩小 2^n 倍,但要保留它是“正钞票”还是“欠条”(符号位)的属性。- 逻辑左移补零所有位向左移动 n 位,右边空出的位全部填入 0。
- 算术右移保号所有位向右移动 n 位,左边空出的位填入原符号位(正数补 0、负数补 1)。
比较指令设标志
交易员最重要的职责之一:在付款前核对两个账目是否相等、某个账户余额是否充足。但请注意——他不需要真的把钱从抽屉里拿出来合并,只需要在账本上“比划一下”。CPU 的 CMP(Compare)和 TEST 指令正是这个“只比划不动钱”的动作。
CMP 指令执行的操作其实是减法:CMP AX, BX 在内部计算 AX - BX,但结果不会被写回到 AX——它只更新标志位。如果两者相等,结果为 0,于是 ZF=1;如果 AX < BX(无符号视角),减法会产生借位,CF=1;如果 AX ≥ BX,则 Z=0 且 CF=0。TEST 指令执行的是“内部相与”:TEST AX, BX 计算 AX & BX 但不回写,只用来检查某些位是否为零——如果结果全 0,ZF=1。它的经典用途是检测某个标志位是否被设置:TEST AL, 0x01 后,若 ZF=1 说明最低位为 0。
这些标志位驱动着后续的“条件跳转”指令(Conditional Jump):JE(相等则跳)检查 ZF、JNE(不等则跳)检查 ZF=0、JB 检查 CF=1、JA 检查 CF=0 且 ZF=0。在高级语言中,if (a == b) 编译后会变成 CMP + JE 的组合。
回扣比喻:CMP 是“比划一下”,TEST 是“检索一下”。在 AI 推理引擎中,条件跳转几乎无处不在——判断循环边界、检测内存对齐、验证缓存命中。读汇编时,看到 CMP 后紧跟 JE/JNE/JB/JA,你就能准确推断出源码里对应的 if/else 和循环条件。
\text{CMP}\ AX, BX:\quad \text{内部计算 } AX - BX,\ \text{仅更新} \\begin{cases}ZF=1, & AX = BX \\CF=1, & AX < BX\end{cases}- 执行隐藏减法CMP 在 CPU 内部执行减法,但目的操作数的值保持不变。
- 更新零标志若结果为 0,则 ZF 置 1,表示“相等”。
- 更新进位标志若结果为负(无符号视角的借位),则 CF 置 1,表示 AX < BX。
算术标志位详解
交易员每天都会在账本上留下各类“记号”(flag):账目是否清零(Z)、是否超出抽屉容量而多出进位(C)、数额是否因过大而算错符号(O)、当前是入账还是出账(S)。CPU 的 EFLAGS 寄存器正是这样一本“账本记号簿”,里面每一类记号都有精确的含义。
零标志 ZF(Zero Flag)是使用频率最高的一位:当算术或逻辑运算的结果为 0 时被置 1。比如 XOR AX, AX 会将 AX 清零并同时把 ZF 置 1。进位标志 CF(Carry Flag)记录无符号运算的“越界”:最高位产生的进位(加法)或借位(减法)会让 CF=1,这有点像交易员发现现金超过了抽屉的物理容量,溢出的那一摞要么转存进相邻抽屉(多精度算术),要么报告异常。溢出标志 OF(Overflow Flag)则记录有符号运算的“符号错乱”:当两个正数相加得到负数、或两个负数相加得到正数时,OF=1——这意味着结果从数学上讲已经不正确了。符号标志 SF(Sign Flag)最简单:直接反映结果的最高位(符号位),结果为负则 SF=1。
在实际的 AI 高性能优化中,这些标志位是底层循环优化的核心。比如在 SIMD 向量化的归约(reduction)代码里,靠检查 ZF 来判断累加和是否为零、提前终止循环;在自定义内存池中,用 CF 检测分配大小的越界。看懂了 EFLAGS 这四枚“印章”,你就学会了阅读 CPU 的“账本审阅报告”。
- 零标志判定运算结果的所有位都为 0 时,ZF 置 1,常用于判断相等或是否为零值。
- 进位标志判定无符号加法从最高位产生进位(或减法产生借位)时,CF 置 1。
- 溢出标志判定有符号加/减结果超出可表示范围时置 1,例如最大正数加 1 变负数。
- 符号标志判定结果最高位为 1 时置 1,等价于结果为负(有符号视角)。
AI 应用场景
向量化前检查数据对齐
阶段:数据预处理 / 性能优化
解决的问题:在 GPU 和 CPU 上做向量化计算前,需要确认数据在内存中的地址是否对齐(如 16 字节或 32 字节对齐)。传统方式用 % 取模或者多次判断地址,效率很低;用 TEST 指令一行就能完成:TEST EAX, 0xF 后检查 ZF 是否置 1,如果地址低 4 位全为 0,就是 16 字节对齐。
具体案例:某 AI 推理框架在加载权重文件时,需要把张量数据搬运到对齐的缓冲区中。优化师用 TEST 指令快速判断每个 chunk 的起始地址是否满足 AVX-512 的 64 字节对齐要求,不满足则用汇编写一个微小的对齐拷贝例程,整体加载速度提升了 18%。
快速哈希计算中的逻辑运算
阶段:特征工程 / 数据索引
解决的问题:在构建大规模向量索引或特征哈希表时,需要快速计算哈希值的散列位。传统 hash % n 的取模运算很慢,而用 AND 指令配合掩码 hash & (n-1)(要求 n 为 2 的幂)可以一步完成等价运算。
具体案例:某推荐系统团队实现用户特征哈希时,将哈希表长度定为 2 的 20 次方(1048576),用 AND 指令代替取模后,在线特征查询的 P99 延迟从 3.2ms 降到 2.1ms。底层正是 x86 的 AND 指令在高效执行位掩码运算。
循环计数器的 DEC 优化
阶段:模型推理 / 算子实现
解决的问题:深度学习中很多算子(如 LayerNorm、Softmax)需要遍历张量的每个元素,传统 for (i = 0; i < n; i++) 循环在汇编层面通常被优化成 DEC 递减计数器的形式,这样可以同时利用 ZF 标志位判断循环是否结束,减少一次 CMP 比较指令。
具体案例:某大模型推理框架的 Softmax 算子中,开发者观察到编译器生成的循环体使用了 DEC R8 加 JNZ loop 的模式,于是手动调整指针递增方向,让循环在反向遍历时命中这种优化模式,端到端推理吞吐提升了约 5%。
Python 演示
class X86ALU: """模拟 x86 算术逻辑单元核心行为的教学工具""" def __init__(self): self.regs = {"AX": 0, "BX": 0, "CX": 0, "DX": 0} self.flags = {"ZF": 0, "CF": 0, "OF": 0, "SF": 0} def _update_flags(self, value): v = value & 0xFFFF # 模拟 16 位寄存器回写 self.flags["ZF"] = 1 if v == 0 else 0 self.flags["SF"] = 1 if (v >> 15) & 1 else 0 return v def set_reg(self, name, value): self.regs[name] = value & 0xFFFF def add(self, dst, src): a, b = self.regs[dst], self.regs[src] s = a + b self.flags["CF"] = 1 if s > 0xFFFF else 0 self.regs[dst] = self._update_flags(s) def sub(self, dst, src): a, b = self.regs[dst], self.regs[src] r = a - b self.flags["CF"] = 1 if r < 0 else 0 self.regs[dst] = self._update_flags(r) def mul(self, src): prod = self.regs["AX"] * self.regs[src] self.regs["DX"] = (prod >> 16) & 0xFFFF # 高 16 位入 DX self.regs["AX"] = prod & 0xFFFF # 低 16 位入 AX def div(self, src): dividend = (self.regs["DX"] << 16) | self.regs["AX"] divisor = self.regs[src] self.regs["AX"] = dividend // divisor self.regs["DX"] = dividend % divisor def band(self, dst, src): self.regs[dst] = self._update_flags(self.regs[dst] & self.regs[src]) def bor(self, dst, src): self.regs[dst] = self._update_flags(self.regs[dst] | self.regs[src]) def bxor(self, dst, src): self.regs[dst] = self._update_flags(self.regs[dst] ^ self.regs[src]) def shl(self, dst, n): self.regs[dst] = self._update_flags(self.regs[dst] << n) def cmp(self, a, b): diff = self.regs[a] - self.regs[b] self.flags["ZF"] = 1 if diff == 0 else 0 self.flags["CF"] = 1 if diff < 0 else 0 self.flags["SF"] = 1 if (diff >> 15) & 1 else 0 def show(self, label): regs = " ".join(f"{k}={self.regs[k]}" for k in self.regs) flags = " ".join(f"{k}={v}" for k, v in self.flags.items()) print(f"{label:12s} | {regs} | {flags}") if __name__ == "__main__": alu = X86ALU() alu.set_reg("AX", 35) alu.set_reg("BX", 28) alu.show("初始状态") alu.add("AX", "BX") alu.show("ADD AX,BX") alu.mul("BX") alu.show("MUL BX") alu.sub("AX", "CX") alu.show("SUB AX,CX") alu.set_reg("AX", 0x00FF) alu.set_reg("BX", 0x0F0F) alu.band("AX", "BX") alu.show("AND AX,BX") alu.cmp("AX", "BX") alu.show("CMP AX,BX")
def _update_flags(self, value)统一的标志位更新逻辑:结果为零则置 ZF,最高位为 1 则置 SF,模拟 CPU 的自动“盖印章”。self.flags["CF"] = 1 if s > 0xFFFF else 0加法超过 16 位最大表示范围时置进位标志,对应真实 CPU 的 CF 行为。self.regs["DX"] = (prod >> 16) & 0xFFFFMUL 的高半部分存入 DX,低半部分存入 AX,复刻 x86 双倍宽度乘积约定。dividend = (self.regs["DX"] << 16) | self.regs["AX"]DIV 前把 DX:AX 拼接成 32 位被除数,对应真实除法指令的输入形态。def cmp(self, a, b)CMP 只更新标志位、不回写寄存器,模拟“只比划不动钱”的行为。
常见误区
误区我以为 `ADD AX, BX` 只是临时算一下 `AX + BX`,并不会改变 AX 的值,就像 C 语言里的 `a + b` 表达式不会修改 a 和 b 一样。
正解x86 的 ADD 指令是“两操作数”语法,第一个操作数既是输入也是输出,计算完成后结果会覆盖写回。这与高级语言中 a + b 不改变 a 的语义完全不同!更接近 a += b。如果不希望修改原值,需要先 MOV 复制一份,或使用 LEA 指令间接计算。
为何常见:初学者从 Python/C 的表达式求值模型迁移到汇编,很容易把“指令”当成“表达式”,而忽略了汇编指令是“就地修改”的。
误区我以为 `MUL BX` 和 `IMUL BX` 都只在 16 位内得到一个结果,溢出就直接丢弃了。
正解MUL 的单操作数形式会把乘积扩展到双倍宽度(AX×BX 的结果共 32 位,写入 DX:AX),因此永远不会因为溢出丢失高位。而 IMUL 有多种形式,双操作数形式 IMUL AX, BX 才是在 16 位内截断结果的。两指令的溢出处理逻辑不同,使用时必须区分。
为何常见:看到“乘法”就直觉认为结果和操作数同宽,忽略了 x86 为了不丢失进位信息而设计的扩展存储约定。
误区右移指令不就是把各位往右推、左边补 0 吗?SAR 和 SHR 应该没有差别。
正解对于无符号数两者确实等价;但对于有符号负数,SHR 在左边补 0 会把负数变成一个很大的正数,数学意义完全错误。SAR 会在左边补符号位,保证 -8 右移 1 位仍是 -4 而不是某个正数。编译器对 int x / 2 会生成 SAR,对 unsigned x / 2 才用 SHR。
为何常见:初学者做移位练习时通常用正数,没有接触负数场景,导致忽略了算术右移与逻辑右移的关键区别。
误区`CMP AX, BX` 既然做了减法,AX 的值应该会变成 `AX - BX` 吧?例如想保留原 AX 又想知道大小关系,就先备份 AX 再比较。
正解CMP 确实在内部执行减法,但结果只用于更新标志位(ZF、CF、SF、OF 等),不会写回任何通用寄存器。AX 和 BX 的值在执行前后完全不变。真正把减法结果写回的是 SUB;CMP 就是“为了比较而减、减完就扔”。
为何常见:名字里带“Compare“但没有明显的语义线索说明”内部减但不保存”,初学者容易套用 SUB 的行为模型。
面试题
Q1. 关于 x86 的 ADD AX, BX 指令,下列说法正确的是?
- A.计算 AX+BX 后,结果仅保存在临时寄存器
- B.计算 AX+BX 后,结果写回 AX,并更新标志位← 正确答案
- C.计算 AX+BX 后,结果写回 BX,并更新标志位
- D.该指令不更新任何标志位
答案:B。ADD 是两操作数指令,执行 AX = AX + BX,结果回写到目标操作数 AX,同时更新 ZF/CF/OF/SF 等标志位。A 错在“不保存”;C 错在写回目标;D 错在算术运算必然联动标志位。
Q2. 执行 SUB AX, BX 后,若 AX 与 BX 的值相等,则哪个标志位会被置 1?
- A.CF
- B.OF
- C.ZF← 正确答案
- D.SF
答案:C。两数相等时相减结果为 0,零标志 ZF 被置 1。CF 只在产生借位时置 1,OF 表示有符号溢出,SF 表示结果为负——A、B、D 均不符合相等相减为 0 的情形。
Q3. MUL BX 指令中,乘积的高 16 位存放在哪个寄存器?
- A.AX
- B.BX
- C.DX← 正确答案
- D.CX
答案:C。单操作数 MUL 使用隐含的 AX 作为被乘数,乘积为 32 位:低 16 位回写 AX,高 16 位强制写入 DX,形成 DX:AX 对。BX 是乘数之一,CX 不参与,AX 只存低半部分。
Q4. 哪条指令可以用来快速判断一个寄存器的值是否为 0,且不改变该寄存器?
- A.
XOR AX, AX - B.
CMP AX, 0← 正确答案 - C.
ADD AX, 0 - D.
SUB AX, AX
答案:B。CMP 做减法但不回写,用 CMP AX, 0 可以检测 AX 是否为 0 且不修改它。XOR AX, AX 会把 AX 清零,A 改变了寄存器;ADD AX, 0 不改变值但污染标志位且效率不如 CMP;SUB AX, AX 同样清零寄存器。
Q5. 在 x86 中,SAR AX, 1 与 SHR AX, 1 的区别是?
- A.SAR 向左移,SHR 向右移
- B.SAR 在左端补符号位,SHR 补 0← 正确答案
- C.SAR 在左端补 0,SHR 补符号位
- D.两者没有任何区别
答案:B。SAR(算术右移)在左端补入原符号位的值,以保持负数在右移后仍然为负数;SHR(逻辑右移)无条件补 0。A 方向描述错误;C 正好说反;D 对无符号数成立但对有符号数不成立。
Q6. 对于无符号整数 value,若想只保留其低 8 位,应该使用哪条指令?
- A.
AND value, 0xFF← 正确答案 - B.
OR value, 0xFF - C.
XOR value, 0xFF - D.
NOT value
答案:A。AND 配合掩码 0xFF 可将高 8 位清零、低 8 位原样保留。OR 是把低 8 位全置 1;XOR 会把低 8 位全取反;NOT 会将所有位取反,均无法实现“只保留低 8 位”。
Q7. TEST AX, 0x0001 指令的目的是?
- A.把 AX 的低 1 位清零
- B.测试 AX 的最低位是否为 1,且不修改 AX← 正确答案
- C.把 AX 的最低位置 1
- D.判断 AX 是否等于 1
答案:B。TEST 执行内部 AND 但不回写,仅更新标志位,用于测试 AX 与掩码按位与的结果是否为 0。若 AX 最低位为 1,则结果为非 0,ZF=0;为 0 则 ZF=1。它不修改 AX,也不做相等性判断,A/C/D 均错。
Q8. 执行 ADD AX, BX 后,CF=1 且 OF=1,关于这次运算说法正确的是?
- A.无符号结果超出 16 位,有符号结果也在合法范围内
- B.无符号结果未越界,但有符号结果发生了溢出
- C.无符号结果超出 16 位,同时有符号结果也发生了溢出← 正确答案
- D.CF 与 OF 不能同时为 1,该情况不可能出现
答案:C。CF=1 表示无符号加法发生了最高位进位(结果大于 0xFFFF);OF=1 表示有符号视角下结果符号错误(如正+正得负或负+负得正)。无符号“进位”和有符号“溢出”是两种独立的判定逻辑,完全可以同时触发。
Q9. 下列哪些指令不会将结果写回通用寄存器?
- A.
CMP AX, BX← 正确答案 - B.
TEST AX, 0xFF← 正确答案 - C.
SUB AX, BX - D.
MUL BX
答案:AB。CMP 和 TEST 只更新标志位不写回任何寄存器,是“只比划不动钱”的指令。SUB 将差值写回 AX,MUL 将乘积写回 DX:AX,二者都会修改寄存器。
Q10. 关于 DIV 指令,以下说法正确的是?
- A.商存入 AX,余数存入 DX← 正确答案
- B.被除数默认来自 DX:AX 组合← 正确答案
- C.若除数为 0 会导致除法错(CPU 异常)← 正确答案
- D.无符号除法的结果会受到 CF 标志位影响
答案:ABC。DIV 用 DX:AX 拼接的宽值除以操作数,商入 AX、余入 DX,除数为 0 时 CPU 触发 #DE 异常。CF 是运算后更新的标志位之一,不会作为输入影响除法行为,D 错。