指令系统
第四章_指令系统
4.1 指令系统
指令系统是 指令集体系结构(ISA) 中最核心的部分,ISA 完整的定义了软件和硬件之间的接口,是机器语言或汇编语言程序员必须要掌握的基础。
ISA 规定的内容主要包括:
- 指令格式、寻址方式、操作类型,以及各操作所需操作数的个数、类型和寻址约束。
- 操作数的数据类型,以及是按大端还是小端方式存放。
- 程序可访问的寄存器编号、数量和位数,存储空间的大小及编址方式。
- 程序员可见的控制状态,如程序计数器、条件码的定义与行为等。
4.1.1 指令的基本格式
一条指令是机器语言的一个语句,基本格式由 操作码字段 和 地址码字段 组成。
根据指令中 操作数地址码数目的不同,可将指令分为如下几种格式:
根据指令中 指令长度的不同,可将指令分为:
根据指令中 操作码长度的不同,可将指令分为:
根据指令中 操作类型的不同,可将指令分为:

4.1.2 扩展操作码指令格式
扩展操作码指令格式 即 指令长度不变 + 操作码长度可变,在设计扩展操作码指令格式的时候,需注意一下两点:
- 不允许短码是长码的前缀,即短操作码不能与长操作码的前面的部分相同。
- 各指令的操作码一定不能重复。
一种扩展操作码的指令格式如下图所示:
看不明白没关系,随便做几道题就懂了。
4.2 寻址方式
若一台计算机支持不同的寻址方式(通常题目中会给出有几种寻址方式),指令结构就还需要考虑 特征位,指令结构变为: $$ \boxed{ \begin{array}{c|c|c} \text{操作码} & \text{特征位} & \text{形式地址} \end{array} } $$
特征位用于计算机判断采用哪种寻址方式。
4.2.1 指令寻址
指令寻址有两种方式:顺序寻址 和 跳跃寻址。
4.2.1.1 顺序寻址
众所周知,程序计数器(PC,不要被名字迷惑了,并不是计数)存放下一条将要执行的指令地址。有时候也叫做 IP (Instruction Pointer 指令指针)。
值得注意的是:PC 在取指周期完成之后就会 +1,并非执行完当前指令才变化。
我们常说的 PC + 1 ,这个 1 其实应该有一个引号。为什么呢,因为这个 1 实际指的是一条指令占据多少个存储单元,比如计算机按照字节编址,而这条指令占据2 个字节,那么这个 PC 应该是十进制的 +2。具体判断应该结合 编址方式和指令长度 来理解。
顺序寻址如下图所示:
定长指令格式:
变长指令格式:

4.2.1.2 跳跃寻址
通过转移类指令实现,分为两种:
- 绝对转移: 比如汇编语言中的 JMP 指令,地址码直接给出目标地址。
- 相对转移: 地址码给出相对于当前 PC 值的偏移量。
其实不论哪种方式,转移指令的执行结果都是去修改 PC 的值。
4.2.2 数据寻址
4.2.2.1 直接寻址
直接寻址就是指指令中的形式地址 A 就是操作数的真实地址 EA,如图所示:

4.2.2.2 间接寻址
间接寻址如图所示:

4.2.2.3 寄存器寻址
寄存器寻址如图所示:
指令的地址段给的是操作数所在寄存器的编号。

4.2.2.4 寄存器间接寻址
寄存器间接寻址结合了间接寻址和寄存器寻址的特点,指令中所指的寄存器存放的不是一个操作数,而是操作数所在的主存单元的地址。如下图所示:

4.2.2.5 隐含寻址
隐含寻址是指指令中不显示的给出操作数的地址,而是将操作数地址隐含在特定寄存器中,如下图所示:

4.2.2.6 立即寻址
不要和直接寻址因为名字混淆了,立即寻址指的是指令的形式地址段并不表示操作数的地址,而是直接存放操作数的本身,通常用补码表示,如下图所示:

4.2.2.7 偏移寻址
偏移寻址分为三种:
| 偏移寻址 | 有效地址 EA | 主要用途 | 记忆 |
|---|---|---|---|
| 相对寻址 | EA = PC + 偏移量 |
访问当前位置附近的数据/指令,常用于 转移指令 | 以程序计数器 PC 所指地址作为 “起点” |
| 基址寻址 | EA = 基址寄存器 + 偏移量 |
程序整体移动后,仍能访问其中的数据,常用于 程序重定位、访问数组/结构 | 以程序存放的地址作为 “起点” |
| 变址寻址 | EA = 变址寄存器 + 偏移量 |
通过改变变址寄存器的值,依次访问 数组中的不同元素 | 程序员自己决定从哪里作为 “起点” |
值得注意的是:
- 这些偏移量一般用有符号数(补码)来表示,也能用无符号数表示,可能涉及到扩展以及偏移范围的问题。具体是用什么表示题目会具体说明。
- 若采用基址寻址和变址寻址,地址字段需要用一些位数来表明寄存器编号,指出使用哪个基址寄存器/变址寄存器。具体用几位题目会说明,比如说总共 16 个寄存器那地址字段就要拆 4 位出来等。
基址寻址 如下图所示:
关于基址寻址的作用,如下图所示:

变址寻址如下图所示:
关于变址寻址的作用,如下图所示:
在实际运用中往往需要多种复合方式进行混合使用,比如 基址寻址 + 变址寻址,如下图所示:

相对寻址 如下图所示:
注意:PC + “1” 是在取指结束之后就进行,所以实际是以 PC + “1” 的位置进行偏移。
相对寻址的作用如图所示:

了解了三种偏移寻址,我们可以来看看 硬件 是怎么实现 数的比较 的,如下图所示:

4.2.2.8 堆栈寻址
堆栈寻址,其读 / 写单元的地址由堆栈指针(SP)的特定寄存器给出。堆栈可分为两类:
- 高速寄存器构成(硬堆栈)。
- 主存中化出一部分区域实现(软堆栈)。
堆栈寻址如下图所示:

4.2.2.9 各种寻址方式访存次数比较

4.3 程序的机器级代码表示
在考研中并不会考察将更高级语言翻译成汇编语言,只需结合 C 语言能够看懂关键汇编语言的代码即可,比如能够看懂常见指令、选择结构、循环结构、函数调用等。
默认考察对象为 X86 汇编指令,MIPS 指令通常会在试题中附带功能说明。
4.3.1 高级语言和机器级代码之间的关系
本小节总览如下:
关于 X86 架构的寄存器有哪些如下图所示,注意为了兼容早期 8位、16位的架构,通用寄存器能够进一步拆分。
再来看一些常见的汇编指令的例子就不会感到陌生了:

4.3.2 常用的 X86 汇编指令
常见的 算术运算指令 如下:
常见的 逻辑运算指令 如下:
还有一些其他的指令,如:
- 用于实现分支结构、循环结构的指令:cmp、test、jmp、jxxx
- 用于实现函数调用的指令:push、pop、call、ret
- 用于实现数据转移的指令:mov
4.3.3 AT&T 格式 VS Intel 格式
X86 架构的汇编语言通常具有两种格式,即:
- Intel 格式:在 Windows 系统下运行(也是我们之前学的)。
- AT&T 格式:在 Unix/Linux 系统下运行。
两种格式的写法对比如下(咸鱼说掌握这些就可以了):

4.3.4 选择语句的机器级表示
要了解汇编层面上的选择语句,我们首先需要了解条件转移指令( jmp 是无条件转移指令!!),条件转移指令能够判断条件正确后直接跳转的某个地址,也能像 C 语言中的 goto 一样,通过设置一个标号来进行跳转,如下图所示:
我们可以通过一道真题来熟悉:

关于 cmp 指令的底层原理,之前提及过一二,具体如下图所示,条件转移指令就是通过 cmp 指令更新的 PSW 中的状态位来判断要不要进行跳转的:

4.3.5 循环语句的机器级表示
汇编语言中,条件转移指令实现循环语句如图所示:
汇编语言为了写循环语句方便点,也有一种 loop 指令来实现循环结构,如图所示:

4.3.6 函数调用的机器级表示
本节知识总览如下图所示:

call 和 ret 这两个指令通常用于函数调用,一看便知,如下图所示:
不难看出,上图的汇编语言其实对应的是下面这一段代码:
int caller(){
int temp1=125;
int temp2=80;
int sum=add(temp1,temp2);
return sum;
}
int add(int x, int y){
return x+y;
}
但是引出一个问题,C 语言在函数调用的时候传入了参数值,在函数返回的时候返回了值。可汇编语言中的 call 指令是将 PC 旧值压入栈中并将 PC 值修改为调用函数地址, ret 指令改变 PC 寄存器地址为 PC 旧值,那么:
- 汇编语言怎么传递调用函数,返回值?
- 如何访问栈帧里的数据?
- 如何切换栈帧?
访问栈帧里的数据:
访问栈帧里的数据都两种方法,如下图所示,注意,一个函数就对应这样一个栈,下图里栈中不同的色块表示不同的函数。
切换栈帧:
除了 main 函数,其他的函数都是被调用的函数,每个函数的 Enter 和 Leave 指令,都可以 看做是被调用函数的模版!
调用 call 指令(压 PC 旧值) 之后,紧接着调用 Enter 指令(压上一层函数基址),在 ret 指令之前,调用 Leave 指令,两个指令的具体作用如下图所示:
传递参数和返回值:
首先我们需要知道一个 栈帧包含了什么内容,如下图所示:
那 如何进行的参数传递和返回值 的呢,下面这个汇编代码,较为全面的进行了说明:
值得注意的是:一般考研都是和图中一样的模型,只需记住,[ebx + 一个数] 表示访问本函数变量。[ebx - 一个数] 表示访问上层函数变量。
4.4 CISC 和 RISC 基本概念
CISC 即 复杂指令系统计算机,RISC 即 精简指令系统计算机。
基本概念和比较如下图所示:
