指令格式和寻址方式
高优先级
组成原理说实话就 两大块:cache 和 虚拟存储器,这两个要放在一起。指令系统 和 CPU,这两个也要放一起。两大块内部的知识点都是相互耦合的,需要综合理解。
指令格式
指令的功能就是 对某些数据 进行 某种操作。
所以指令中主要包含两个部分:操作码(opcode)以及 地址(address)。
-
操作码(opcode)就是决定了指令的类型:
- 这个指令是干嘛的?进行哪种操作?
-
地址是一个通用含义,指的是操作的对象:
- 可以是一个 内存地址(
<addr>) - 也可以是 CPU 中的一个寄存器编号(
<reg>) - 也可以是一个 立即数(
<imm>)
- 可以是一个 内存地址(

指令类型
根据操作码分类
指令根据其 操作码(opcode)的不同可以分为以下类别:
- 数据传输指令
MOV:将数据从一个位置传输到另一个位置,可以是寄存器到寄存器、内存到寄存器、寄存器到内存等。PUSH:将数据(通常是寄存器中的值)推入堆栈。POP:从堆栈中弹出数据并存储到寄存器中。
- 算术和逻辑运算指令
ADD、SUB、IMUL、IDIV:执行算术运算,如加法、减法、乘法和除法。MUL、DIV是无符号数运算AND、OR、XOR、NOT:执行逻辑运算,如按位与、按位或、按位异或和按位取反。INC、DEC:递增和递减操作数的值。CMP:用于比较两个值,并根据结果设置标志寄存器的状态。
- 控制转移指令
JMP:用于无条件跳转到指定的目标地址。Jxx:条件跳转指令,根据特定的条件(如零标志、进位标志等)来决定是否跳转。CALL:调用子程序或函数。RET:从子程序返回。
- 输入/输出指令
IN:从外部设备或端口读取数据。OUT:向外部设备或端口发送数据。
- 字符串操作指令(String Instructions):
MOVS、LODS、STOS、CMPS:用于在内存中执行字符串操作,如移动、加载、存储、比较。
- 陷阱指令(Trap Instructions):
INT:用于引发中断,通常用于与操作系统进行通信。
- 协处理器指令(Coprocessor Instructions):
CLI、STI:用于清除和设置 CPU 的中断标志,通常只能在内核模式下执行。

根据地址个数分类
根据指令中的 地址 个数,可以将指令划分为以下类型。
这些地址可以是 寄存器、内存地址,也可以是 立即数。

定长和变长指令

指令长度的设计可以分为两类:
-
定长指令集:所有指令的长度完全相同
→ 典型架构:ARM(RISC)
→ 优点:解码简单、高效
→ 缺点:指令中可能出现浪费空间的无效字段 -
变长指令集:不同指令具有不同长度
→ 典型架构:x86(CISC)
→ 优点:编码更紧凑,能支持更复杂操作
→ 缺点:解码过程复杂,需要准确识别指令边界

注意
这两种设计思路分别带来了两个常见问题,在试题中也经常以变形方式考查:
- 在 定长指令集 中,所有指令长度固定,而不同指令所需的 地址字段 个数不一样,如何统一表示?
- 在 变长指令集 中,指令长度不固定,CPU 又如何判断指令边界(即每条指令的起始和结束位置)?
解决思路
问题 1:定长指令中地址字段数量不统一怎么办?
解决方法是使用 无效字段填充。
即:当某条指令所需的 地址字段 不足以填满整个固定长度时,剩余部分使用 填充值(无效字段)占位。这些填充值不会影响指令执行,仅用于保证每条指令长度一致,简化硬件解码逻辑。

问题 2:变长指令中如何识别指令边界?
一种常用方案是引入 指令前缀。
即:在每条指令的开头,设置一个前缀字段,或在 操作码 的高位嵌入标志,用来标识该指令的长度或类型。CPU 在解码时先读取前缀位,就能判断指令长度,从而准确提取整条指令。
虽然这种方式增加了解码复杂度,但换来了更大的编码灵活性与指令集的扩展能力。
操作码扩展编码
为了在统一的指令格式中支持不同 地址数 的操作,同时避免指令之间产生歧义,指令系统通常采用:
可变长度操作码 + 定长指令字 的方式,
并要求这些 操作码 遵循 前缀码 设计原则。
🌟 先举个简单的例子:
假设我们设计如下 操作码长度规则:
- 三地址指令使用 4 位 操作码
- 二地址指令使用 6 位 操作码
- 一地址指令使用 8 位 操作码
为了防止解析冲突,必须满足 前缀码 的要求:
- 任意一个 4 位 操作码不能是任何 6 位 或 8 位 操作码的前缀
- 任意一个 6 位 操作码不能是任何 8 位 操作码的前缀
这样,每条指令的 操作码 就能唯一识别其类别与长度,避免歧义,并保持系统的可扩展性和解码自同步。
🌟 再举个复杂的例子:
假设某指令系统指令长 16 位,操作码字段为 4 位,地址码字段为 4 位,采用扩展 操作码 技术,形成 三地址指令 15 条、二地址指令 12 条、一地址指令 63 条、零地址指令 16 条。
那么 三地址指令 格式如下:

二地址指令 复用 三地址指令 的 A1 字段,一地址指令 复用 三地址指令 的 A1 和 A2 字段,零地址指令 复用 三地址指令 的 A1、A2 和 A3 字段。
可以通过树形扩展得到不同指令的 op 前缀:

沿着树的边一直走到叶子结点,可以得到如下格式的指令:
| 指令类型 | 操作码 | 地址码1 | 地址码2 | 地址码3 |
|---|---|---|---|---|
| 三地址指令(15 条) | 0000 ~ 1110 | A1 | A2 | A3 |
| 二地址指令(12 条) | 1111 0000 ~ 1111 1011 | A2 | A3 | |
| 一地址指令(63 条) | 1111 1100 0000 ~ 1111 1111 1110 | A3 | ||
| 零地址指令(16 条) | 1111 1111 1111 0000 ~ 1111 1111 1111 1111 |
注意
操作码扩展编码虽然属于计算机组成原理,但它所采用的设计思想,在计算机网络 中同样十分常见。
例如,变长子网划分 也是按照前缀不断扩展的方式划分地址空间:每向下一层扩展 1 位,地址空间便减半;最终,每个子网都对应二叉树上的一个叶子节点。
从二叉树的角度来看,两者都是不断将一个大的编码空间递归划分为更小的子空间:
根节点 │ 扩展一位(二选一) / \ 0 1 / \ / \ … … … …因此,无论是 CPU 的操作码设计,还是 IP 地址的 VLSM 划分,本质上都是利用**前缀编码(Prefix Code)**来对有限的编码空间进行高效划分。只不过,一个划分的是 指令编码空间,另一个划分的是 IP 地址空间。
寻址方式
计算机中的 寻址方式(Addressing Modes)是指 确定指令或操作数有效地址 的方法,包括确定下一条待执行指令的地址以及本条指令所需操作数的地址,寻址方式可以分为 指令寻址 和 数据寻址两大类:
指令寻址
顺序寻址
通过 程序计数器(PC) 加上当前指令的字节长度,自动形成下一条指令地址
跳跃寻址
通过转移类指令实现。是否跳跃通常由状态寄存器中的条件码决定,转移目标地址由指令给出。转移方式分为:
- 绝对转移,直接由 地址码 给出目标地址
- 相对转移,地址码给出相对于当前PC值的偏移量
无论何种方式,转移指令的执行结果都是修改 PC 的值,CPU 随后根据新的 PC 从主存取出下一条指令
数据寻址
立即数寻址
立即数寻址(Immediate Addressing)是一种将 常量值直接嵌入指令中 的寻址方式,常用于赋值、初始化、比较等基本操作。
在 立即数寻址 之中,操作数本身就是指令的一部分,而不是从寄存器或内存中取得。这种寻址方式不涉及额外的地址计算,执行效率较高。
举个实际例子,下图是指令 MOV AX, 4567H 存储结构和执行示意图,指令直接将 立即数 4567H 存储到寄存器 R1 中:

📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 加载常量 | MOV AX, 5 —— 将常数 5 加入 AX |
| 比较固定值 | CMP AL, 0 —— 判断 AL 是否为零 |
Quote
优点:只要取出指令,可立即获得操作数,这样就不用访问存储器
缺点:A 的位数限制了能表示的立即数的范围
直接寻址
直接寻址(Direct Addressing)是一种通过 在指令中显式给出操作数的内存地址 来访问数据的方式,适用于访问固定位置的数据。
在 直接寻址 中,指令中包含了操作数在内存中的确切地址。CPU 在执行指令时,会直接从该地址读取或写入数据,不依赖寄存器辅助寻址。
举个实际例子,下图是指令 MOV R1, [1000] 的执行示意图,以 立即数 1000 作为访存地址,指令从内存地址 1000 的单元读取数据并加载到寄存器 R1 中:

📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 访问固定内存 | MOV AX, [0x1234] —— 读取内存地址 0x1234 的内容 |
| 读取硬件端口 | IN AL, [0x60] —— 从端口地址读取键盘输入 |
| 设置显存颜色值 | MOV [0xB8000], AL —— 设置文本模式字符颜色 |
Quote
优点:寻找操作数简单,不需要专门计算操作数的地址,只访问一次主存
缺点:A 的位数限制了操作数的寻址范围,必须改 A 的值,才能修改操作数的地址
隐含寻址
指令字中不明显地给出操作数的地址,其操作数的地址隐含在操作码或某个寄存器中
如:一地址格式的加法指令只给出一个操作数的地址,另一个操作数隐含在累加器ACC中这样累加器ACC成了另一个数的地址

Quote
优点:指令字中少一个地址,能缩短指令字长
缺点:需增加 “存储操作数或隐含地址” 的硬件
间接寻址
间接寻址(Indirect Addressing)是一种通过 寄存器或内存中的地址来访问实际数据地址 的方式,适用于访问指针、链表等动态结构。
在 间接寻址 中,指令中提供的是一个地址的“指针”,实际的数据地址存储在寄存器或内存单元中。CPU 先访问该中间地址,再通过它获取最终的操作数地址。
举个实际例子,下图是指令 MOV R1, [R2] 的执行示意图,访存地址间接地存储在寄存器 R2 中,指令首先从 R2 中读取目标地址,然后在相应的地址中读取数据加载进入 R1 中:

间接寻址 包含多种类型,其中最常见的是 寄存器间接寻址:
操作数的地址保存在寄存器中,CPU 通过这个寄存器中存储的地址访问内存中的操作数。
当我们提到 间接寻址 时,大多数时候都是 寄存器间接寻址
📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 通过指针访问数据 | MOV AX, [BX] —— BX 存储了目标地址 |
Quote
优点:可扩大寻址范围 ( 内存单元 或 寄存器 位数 一定大于 形式地址 )
缺点:指令在执行阶段需要多次访存,浪费了很多时间
寄存器寻址
寄存器寻址(Register Addressing)是一种将操作数存储在寄存器中的寻址方式。在这种模式下,指令通过指定寄存器来访问操作数,寄存器本身就是操作数的存储位置。
举个实际例子,指令 MOV AX, BX 表示将寄存器 BX 中的值复制到寄存器 AX 中:

📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 拷贝寄存器内容 | MOV AX, BX —— 将 BX 内容拷贝到 AX |
| 比较寄存器 | CMP AX, BX —— 判断 AL 是否为零 |
Quote
优点:指令执行阶段无需访存,减少了执行时间
缺点:没啥缺点,就是贵,使用非常广泛
基址寻址
基址寻址(Base Addressing)是一种通过 基址寄存器与偏移值相加 来访问结构体字段或局部变量的方式,基址寄存器 是面向操作系统的,只能由 操作系统 或 管理程序 根据主存空间情况来确定,常见于函数调用过程中的栈帧操作。
分为隐式和显式两种
隐式:计算机内专门设有一个基址寄存器BR,使用时用户不必明显指出该寄存器
显式:一组通用寄存器里,用户明确指出哪个寄存器用做基址寄存器,存放基地址

📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 栈帧内访问局部变量或参数 | MOV AX, [BP - 2]、MOV AX, [BP + 6] |
Quote
优点:基址寻址扩大寻址范围,有利于多道程序设计,可用于编制浮动程序
缺点:无说明
基址寻址和变址寻址的区别
首先,两者具有共同点:基址寻址与变址寻址都类似于相对寻址,它们的有效地址 EA = 基址 + 指令字中形式地址 A。
1、基址寻址
计算公式:EA = (BR) + A
有效地址是将 CPU 中 基址寄存器 BR 的内容加上指令字中形式地址 A。BR 的内容由操作系统决定,在程序执行过程中 BR 的内容不可变,而形式地址是可变的。基址寻址 方式适合解决动态定位的问题。在多道程序的环境当中,操作系统根据内存空间的情况赋值给 BR,一旦赋值成功就不可更改,直至用户程序结束,使得用户不必关心实际的地址而只需要关心自己的地址空间即可。
2、变址寻址
计算公式:EA = (IX) + A
有效地址是将 CPU 中 变址寄存器 IX 的内容加上指令字中有效地址 A。其指令字的形式地址作为一个基准地址,内容不可变,而 CPU 中 变址寄存器 IX 在程序执行过程中根据使用情况发生改变。这样的寻址方式非常适合于循环问题,原因在于指令的“基址”(形式地址)保持不变,使得执行循环时,只需要改变 IX 的内容即可(比如迭代时,不断加 4)。假若使用 基址寻址 的方式,意味着循环过程中不断需要新的“基址”,也就是需要更多的指令字加以控制。而 变址寻址 只需要一条指令即可完成相关操作,可以大量缩短指令编码的长度,提高指令字的可用性。
两种寻址方式都是解决特定应用场景的问题,它们本质上是一样的,只是表现形式的不同而已。
变址寻址
变址寻址(Indexed Addressing)是一种通过 变址寄存器的值加上偏移量 来获取操作数地址的寻址方式,通常用于数组或表格中元素的访问。
变址寄存器 的内容由用户设定,程序执行过程中其值可变,而指令字中A不可变
变址寻址主要用于处理数组问题,可设定A为数组首地址,不断改变变址寄存器X的内容,很容易能形成数组中任一数据地址

📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 多维数组访问 | MOV AX, [BX + SI] —— 行列下标组合 |
| 结构体数组成员访问 | MOV AX, [DI + SI*4] —— 每个结构体占 4 字节 |
| 动态偏移数据结构遍历 | MOV AL, [BX + CX] —— 使用索引偏移访问 |
Quote
优点:适合编制循环程序
缺点:无说明
相对寻址
相对寻址(Relative Addressing)是一种根据 当前指令地址(PC)与偏移量 来确定跳转或访问目标位置的方式,广泛应用于控制流指令。
在 相对寻址 中,以当前程序计数器(PC)作为基准,通过加上一个有符号的偏移量来计算跳转目标地址。这种寻址方式便于编写可重定位代码。

📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 条件跳转(分支) | JZ LABEL —— 如果为零,跳转到相对偏移处 |
| 循环控制 | LOOP LOOP_START —— PC 相对跳转 |
| 实现函数局部跳转表 | JMP [PC + offset](某些架构中) |
Quote
优点:无说明
缺点:无说明
堆栈寻址
堆栈寻址(Stack Addressing)是一种通过 栈指针或基址指针 来访问 栈中数据 的方式,广泛应用于函数调用过程中的参数传递和返回值保存。
在 堆栈寻址 中,利用 SP(栈指针)或 BP(基址指针)定位栈中元素,通过栈顶向下或向上偏移来读取或写入局部变量、返回地址等。通常与 PUSH、POP、CALL、RET 等指令结合使用。

📌 示例应用
| 应用 | 示例说明 |
|---|---|
| 函数调用和返回 | CALL FUNC、RET —— 使用栈存储返回地址 |
| 保存和恢复寄存器值 | PUSH AX、POP AX |
Quote
优点:无
缺点:无
总结

寻址对比
下表给出了各个 寻址方式 的核心区别:

程序的机器级代码表示

何为 目的操作数 何为 源操作数
目的操作数 (Destination Operand)
目的操作数是指指令执行完成后,存储计算结果的地方。
-
特点:它的旧值会被指令产生的新值覆盖。
-
位置:通常是一个寄存器或一个内存单元。
源操作数 (Source Operand)
源操作数是指在指令执行过程中提供原始数据的操作数。
-
特点:它的值在指令执行过程中通常不会改变(除非它是那种自增/自减指令)。
-
来源:可以是寄存器中的值、内存地址里的数据,或者是指令中直接给出的立即数(常数)。
注意
一般来说 不管是什么格式,最终计算的表达式都是:
统考中通常使用 intel 格式
剩下的就看大题了
指令集种类
指令体系结构
低优先级
指令体系结构 偶尔在选择题中考查,概念看一看,留个印象,基本就没问题。
指令体系结构 (Instruction Set Architecture,简称 ISA) 是计算机体系结构中定义处理器 指令集 的规范。它是硬件与软件之间的接口,规定了处理器能够执行的 指令集合、指令格式、寻址方式、寄存器 组织、内存访问方式 以及 数据类型 等。
具体来说,ISA 包括以下关键内容:
- 指令集 :处理器支持的所有操作(如加法、跳转、加载/存储等)。
- 指令格式 :每条指令的编码结构,如操作码、操作数等。
- 寄存器 :可用的寄存器数量、类型和用途(如通用寄存器、程序计数器等)。
- 寻址方式 :如何指定操作数的存储位置(如立即数、寄存器寻址、内存寻址等)。
- 内存模型 :内存的组织方式和访问规则。
- 中断和异常处理 :如何处理外部事件或错误。
ISA 决定了软件如何与硬件交互,是 编译器、操作系统 和 应用程序 开发的基础。常见的 ISA 包括 x86、ARM、RISC‑V 等,每种 ISA 在 性能、功耗 和 应用场景 上各有特点。例如,RISC (精简指令集计算机)强调 简单高效 的指令,而 CISC (复杂指令集计算机)提供更 复杂 的指令以减少代码量。
简而言之,ISA 是计算机的核心“语言”,定义了处理器能做什么以及如何做。
复杂和精简指令集
低优先级
复杂和精简指令集也是在选择题偶尔考查,了解两者的差异,看到选项能选出来就行。
CISC (Complex Instruction Set Computer)和 RISC (Reduced Instruction Set Computer)是两种不同的计算机体系结构设计哲学,它们在 指令集架构 和 执行方式 上有显著的差异。

CISC
-
指令集复杂 :CISC 指令集包含大量 复杂的指令,其中一条指令可以执行多种操作,包括 内存访问、算术运算、逻辑运算 等。
-
指令不定长 :CISC 支持多种长度的指令。
-
多寻址模式 :CISC 指令通常支持多种 寻址模式,允许直接访问内存,因此可以在一条指令中执行复杂的操作。
-
微程序控制 :CISC 计算机通常使用 微程序控制单元,指令解码和执行过程相对复杂。
-
复杂硬件 :CISC 处理器通常包括大量的硬件单元,用于支持 复杂的指令集,这使得 CISC 芯片相对较大。
CISC 的典型芯片就是 x86 系列,如 Intel 的 Core i 系列处理器和 AMD 的 Ryzen 系列处理器。
RISC
-
指令集精简 :RISC 计算机的指令集更加精简,通常包含较少、更 简单的指令。每条指令只执行一种操作。
-
指令定长 :RISC 指令集中所有指令长度相同。
-
固定寻址模式 :RISC 指令通常只支持一种或者很少种 寻址模式,鼓励将数据加载到寄存器中后再执行操作。
-
硬布线控制 :RISC 计算机使用 硬布线控制单元,指令解码和执行过程较为简单。
-
精简硬件 :RISC 处理器通常采用更 精简的硬件,以提高 性能 和降低 成本。
RISC 的典型芯片就是 arm 系列,比如苹果的 A 系列和 M 系列处理器。

上图体现了 CISC 和 RISC 的差别:RISC 寄存器数量比 CISC 更多,CISC 的访存指令比 RISC 更加复杂(CISC 单条指令完成的工作 RISC 需要多条指令才能完成)。
程序示例
这一节举个实际的例子说明一下 CISC 和 RISC 上的汇编代码区别。
for (i = 0; i < 24; i++)
for (j = 0; j < 24; j++)
...
a[i][j] = 10;
...对于上述的循环代码段,编译器将其编译为如下的 intel x86 汇编代码(CISC)和 arm mips 汇编代码(RISC):


对比
CISC 和 RISC 的主要区别如下表所示:
| 特性 | CISC | RISC |
|---|---|---|
| 指令集 | 复杂,指令数量多 | 精简,指令数量少 |
| 指令长度 | 不定长 | 固定长度 |
| 寻址模式 | 多种寻址模式 | 较少寻址模式 |
| 控制方式 | 微程序控制 | 硬布线控制 |
| 硬件复杂度 | 复杂 | 精简 |
| 优点 | 功能强大,一条指令可完成复杂操作 | 性能高,功耗低 |
| 缺点 | 硬件复杂,指令执行效率相对较低 | 功能相对简单,复杂操作需要多条指令完成 |
与CISC相比,RISC的优势主要体现在以下几个方面:
-
更高效利用 芯片面积。CISC采用微程序控制,其控制存储器占CPU芯片面积的 50%以上,而RISC采用硬布线控制,逻辑电路仅占约10%,节省了宝贵的硅片资源。
-
更高的 运算速度。RISC指令数量少、格式统一、寻址方式简单,配合大量通用寄存器和流水线技术,使大多数指令可在单周期内完成,显著提升执行效率。
-
更易设计与维护。RISC指令系统结构简单,设计周期短;其逻辑清晰,出错概率低,且便于调试和验证,从而提高了 系统可靠 性。
-
更利于 编译优化。由于指令类型和寻址方式有限,编译器更容易选择最优指令序列、调整指令调度,生成高效的目标代码。
补充 CISC 的 指令功能 追求强大,所以会牺牲部分指令的格式而让其更高效 (RISC无)
指令操作码
高优先级
本节要和 CPU 中的 控制器 放在一起理解,也是后续 CPU 的基础。当然,其中协处理器和字符串操作指令不大重要,可以不作为重点,其他的操作码都要 深入理解。
不同计算机架构的指令操作码不相同,但是其中涉及到的功能却大同小异。本节以 x86 平台的指令为例,说明一下指令操作码的主要分类和功能。
数据传输指令
数据传送指令在 x86 中就是 MOV,将第二个操作数(寄存器的内容、内存中的内容或常数值)复制到第一个操作数(寄存器或内存),可以实现 寄存器、内存之间的 数据传送。