指令格式和寻址方式

高优先级

组成原理说实话就 两大块:cache 和 虚拟存储器,这两个要放在一起。指令系统 和 CPU,这两个也要放一起。两大块内部的知识点都是相互耦合的,需要综合理解。

指令格式

指令的功能就是 对某些数据 进行 某种操作

所以指令中主要包含两个部分:操作码(opcode)以及 地址(address)。

  • 操作码(opcode)就是决定了指令的类型:

    • 这个指令是干嘛的?进行哪种操作?
  • 地址是一个通用含义,指的是操作的对象:

    • 可以是一个 内存地址<addr>
    • 也可以是 CPU 中的一个寄存器编号<reg>
    • 也可以是一个 立即数<imm>

指令类型

根据操作码分类

指令根据其 操作码(opcode)的不同可以分为以下类别:

  1. 数据传输指令
    • MOV:将数据从一个位置传输到另一个位置,可以是寄存器到寄存器、内存到寄存器、寄存器到内存等。
    • PUSH:将数据(通常是寄存器中的值)推入堆栈。
    • POP:从堆栈中弹出数据并存储到寄存器中。
  2. 算术和逻辑运算指令
    • ADDSUBIMULIDIV:执行算术运算,如加法、减法、乘法和除法。
    • MULDIV 是无符号数运算
    • ANDORXORNOT:执行逻辑运算,如按位与、按位或、按位异或和按位取反。
    • INCDEC:递增和递减操作数的值。
    • CMP:用于比较两个值,并根据结果设置标志寄存器的状态。
  3. 控制转移指令
    • JMP:用于无条件跳转到指定的目标地址。
    • Jxx:条件跳转指令,根据特定的条件(如零标志、进位标志等)来决定是否跳转。
    • CALL:调用子程序或函数。
    • RET:从子程序返回。
  4. 输入/输出指令
    • IN:从外部设备或端口读取数据。
    • OUT:向外部设备或端口发送数据。
  5. 字符串操作指令(String Instructions):
    • MOVSLODSSTOSCMPS:用于在内存中执行字符串操作,如移动、加载、存储、比较。
  6. 陷阱指令(Trap Instructions):
    • INT:用于引发中断,通常用于与操作系统进行通信。
  7. 协处理器指令(Coprocessor Instructions):
    • CLISTI:用于清除和设置 CPU 的中断标志,通常只能在内核模式下执行。

根据地址个数分类

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

定长和变长指令

指令长度的设计可以分为两类:

  • 定长指令集:所有指令的长度完全相同
    → 典型架构:ARM(RISC
    → 优点:解码简单、高效
    → 缺点:指令中可能出现浪费空间的无效字段

  • 变长指令集:不同指令具有不同长度
    → 典型架构:x86(CISC
    → 优点:编码更紧凑,能支持更复杂操作
    → 缺点:解码过程复杂,需要准确识别指令边界

注意

这两种设计思路分别带来了两个常见问题,在试题中也经常以变形方式考查:

  1. 在 定长指令集 中,所有指令长度固定,而不同指令所需的 地址字段 个数不一样,如何统一表示?
  2. 在 变长指令集 中,指令长度不固定,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 ~ 1110A1A2A3
二地址指令(12 条)1111 0000 ~ 1111 1011A2A3
一地址指令(63 条)1111 1100 0000 ~ 1111 1111 1110A3
零地址指令(16 条)1111 1111 1111 0000 ~ 1111 1111 1111 1111

注意

操作码扩展编码虽然属于计算机组成原理,但它所采用的设计思想,在计算机网络 中同样十分常见。

例如,变长子网划分 也是按照前缀不断扩展的方式划分地址空间:每向下一层扩展 1 位,地址空间便减半;最终,每个子网都对应二叉树上的一个叶子节点。

从二叉树的角度来看,两者都是不断将一个大的编码空间递归划分为更小的子空间:

        根节点
          │
   扩展一位(二选一)
       /       \
     0           1
   /  \        /  \
  …   …      …    …

因此,无论是 CPU 的操作码设计,还是 IP 地址的 VLSM 划分,本质上都是利用**前缀编码(Prefix Code)**来对有限的编码空间进行高效划分。只不过,一个划分的是 指令编码空间,另一个划分的是 IP 地址空间

例题

寻址方式

计算机中的 寻址方式(Addressing Modes)是指 确定指令或操作数有效地址 的方法,包括确定下一条待执行指令的地址以及本条指令所需操作数的地址,寻址方式可以分为 指令寻址数据寻址两大类

指令寻址

顺序寻址

通过 程序计数器(PC) 加上当前指令的字节长度,自动形成下一条指令地址

跳跃寻址

通过转移类指令实现。是否跳跃通常由状态寄存器中的条件码决定,转移目标地址由指令给出。转移方式分为:

  1. 绝对转移,直接由 地址码 给出目标地址
  2. 相对转移,地址码给出相对于当前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(基址指针)定位栈中元素,通过栈顶向下或向上偏移来读取或写入局部变量、返回地址等。通常与 PUSHPOPCALLRET 等指令结合使用。

📌 示例应用

应用示例说明
函数调用和返回CALL FUNCRET —— 使用栈存储返回地址
保存和恢复寄存器值PUSH AXPOP AX

Quote

优点:无

缺点:无

总结

寻址对比

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

程序的机器级代码表示

何为 目的操作数 何为 源操作数

目的操作数 (Destination Operand)

目的操作数是指指令执行完成后,存储计算结果的地方。

  • 特点:它的旧值会被指令产生的新值覆盖

  • 位置:通常是一个寄存器或一个内存单元。

源操作数 (Source Operand)

源操作数是指在指令执行过程中提供原始数据的操作数。

  • 特点:它的值在指令执行过程中通常不会改变(除非它是那种自增/自减指令)。

  • 来源:可以是寄存器中的值、内存地址里的数据,或者是指令中直接给出的立即数(常数)。

注意

一般来说 不管是什么格式,最终计算的表达式都是:

统考中通常使用 intel 格式

具体指令

剩下的就看大题了

指令集种类

指令体系结构

低优先级

指令体系结构 偶尔在选择题中考查,概念看一看,留个印象,基本就没问题。

指令体系结构 (Instruction Set Architecture,简称 ISA) 是计算机体系结构中定义处理器 指令集 的规范。它是硬件与软件之间的接口,规定了处理器能够执行的 指令集合指令格式寻址方式寄存器 组织、内存访问方式 以及 数据类型 等。

具体来说,ISA 包括以下关键内容:

  1. 指令集 :处理器支持的所有操作(如加法、跳转、加载/存储等)。
  2. 指令格式 :每条指令的编码结构,如操作码、操作数等。
  3. 寄存器 :可用的寄存器数量、类型和用途(如通用寄存器、程序计数器等)。
  4. 寻址方式 :如何指定操作数的存储位置(如立即数、寄存器寻址、内存寻址等)。
  5. 内存模型 :内存的组织方式和访问规则。
  6. 中断和异常处理 :如何处理外部事件或错误。

ISA 决定了软件如何与硬件交互,是 编译器操作系统 和 应用程序 开发的基础。常见的 ISA 包括 x86ARMRISC‑V 等,每种 ISA 在 性能功耗 和 应用场景 上各有特点。例如,RISC (精简指令集计算机)强调 简单高效 的指令,而 CISC (复杂指令集计算机)提供更 复杂 的指令以减少代码量。

简而言之,ISA 是计算机的核心“语言”,定义了处理器能做什么以及如何做。

复杂和精简指令集

低优先级

复杂和精简指令集也是在选择题偶尔考查,了解两者的差异,看到选项能选出来就行。

CISC (Complex Instruction Set Computer)和 RISC (Reduced Instruction Set Computer)是两种不同的计算机体系结构设计哲学,它们在 指令集架构 和 执行方式 上有显著的差异。

CISC

  1. 指令集复杂 :CISC 指令集包含大量 复杂的指令,其中一条指令可以执行多种操作,包括 内存访问算术运算逻辑运算 等。

  2. 指令不定长 :CISC 支持多种长度的指令。

  3. 多寻址模式 :CISC 指令通常支持多种 寻址模式,允许直接访问内存,因此可以在一条指令中执行复杂的操作。

  4. 微程序控制 :CISC 计算机通常使用 微程序控制单元,指令解码和执行过程相对复杂。

  5. 复杂硬件 :CISC 处理器通常包括大量的硬件单元,用于支持 复杂的指令集,这使得 CISC 芯片相对较大。

    CISC 的典型芯片就是 x86 系列,如 Intel 的 Core i 系列处理器和 AMD 的 Ryzen 系列处理器。

RISC

  1. 指令集精简 :RISC 计算机的指令集更加精简,通常包含较少、更 简单的指令。每条指令只执行一种操作。

  2. 指令定长 :RISC 指令集中所有指令长度相同。

  3. 固定寻址模式 :RISC 指令通常只支持一种或者很少种 寻址模式,鼓励将数据加载到寄存器中后再执行操作。

  4. 硬布线控制 :RISC 计算机使用 硬布线控制单元,指令解码和执行过程较为简单。

  5. 精简硬件 :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 的主要区别如下表所示:

特性CISCRISC
指令集复杂,指令数量多精简,指令数量少
指令长度不定长固定长度
寻址模式多种寻址模式较少寻址模式
控制方式微程序控制硬布线控制
硬件复杂度复杂精简
优点功能强大,一条指令可完成复杂操作性能高,功耗低
缺点硬件复杂,指令执行效率相对较低功能相对简单,复杂操作需要多条指令完成

与CISC相比,RISC的优势主要体现在以下几个方面:

  1. 更高效利用 芯片面积。CISC采用微程序控制,其控制存储器占CPU芯片面积的 50%以上,而RISC采用硬布线控制,逻辑电路仅占约10%,节省了宝贵的硅片资源。

  2. 更高的 运算速度。RISC指令数量少、格式统一、寻址方式简单,配合大量通用寄存器和流水线技术,使大多数指令可在单周期内完成,显著提升执行效率。

  3. 更易设计与维护。RISC指令系统结构简单,设计周期短;其逻辑清晰,出错概率低,且便于调试和验证,从而提高了 系统可靠 性。

  4. 更利于 编译优化。由于指令类型和寻址方式有限,编译器更容易选择最优指令序列、调整指令调度,生成高效的目标代码。

补充 CISC 的 指令功能 追求强大,所以会牺牲部分指令的格式而让其更高效 (RISC无)

指令操作码

高优先级

本节要和 CPU 中的 控制器 放在一起理解,也是后续 CPU 的基础。当然,其中协处理器和字符串操作指令不大重要,可以不作为重点,其他的操作码都要 深入理解

不同计算机架构的指令操作码不相同,但是其中涉及到的功能却大同小异。本节以 x86 平台的指令为例,说明一下指令操作码的主要分类和功能。

数据传输指令

数据传送指令在 x86 中就是 MOV,将第二个操作数(寄存器的内容、内存中的内容或常数值)复制到第一个操作数(寄存器内存),可以实现 寄存器内存之间的 数据传送