指令系统
第四章_指令系统
4.1 指令系统
指令系统是 指令集体系结构(ISA) 中最核心的部分,ISA 完整的定义了软件和硬件之间的接口,是机器语言或汇编语言程序员必须要掌握的基础。
4.1.1 指令的基本格式
一条指令是机器语言的一个语句,基本格式由 操作码字段 和 地址码字段 组成。
根据指令中 操作数地址码数目的不同,可将指令分为如下几种格式:
根据指令中 指令长度的不同,可将指令分为:
根据指令中 操作码长度的不同,可将指令分为:
根据指令中 操作类型的不同,可将指令分为:

4.1.2 扩展操作码指令格式
扩展操作码指令格式 即 指令长度不变 + 操作码长度可变,在设计扩展操作码指令格式的时候,需注意一下两点:
- 不允许短码是长码的前缀,即短操作码不能与长操作码的前面的部分相同。
- 各指令的操作码一定不能重复。
一种扩展操作码的指令格式如下图所示:

4.2 寻址方式
4.2.1 指令寻址
指令寻址有两种方式:顺序寻址 和 跳跃寻址。
4.2.1.1 顺序寻址
众所周知,程序计数器(PC,不要被名字迷惑了,并不是计数)存放下一条将要执行的指令地址。有时候也叫做 IP (Instruction Pointer 指令指针)。
而我们常说的 PC + 1 ,这个 1 其实应该有一个引号。为什么呢,因为 主存的编址方式 可能会不同,如下图所示:
定长指令格式:
变长指令格式:

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 偏移寻址
偏移寻址分为三种:
- 基址寻址:以程序存放的地址作为 “起点”。
- 变址寻址:程序员自己决定从哪里作为 “起点”。
- 相对寻址:以程序计数器 PC 所指地址作为 “起点”。
基址寻址 如下图所示:
关于基址寻址的作用,如下图所示:

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

相对寻址如下图所示:
相对寻址的作用如图所示:

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

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 和 ret 指令只有改变 PC 寄存器地址的效果,那么:
- 汇编语言怎么传递调用函数,返回值?
- 如何访问栈帧里的数据?
- 如何切换栈帧?
访问栈帧里的数据:
访问栈帧里的数据都两种方法,如下图所示,注意,一个函数就对应这样一个栈,下图里栈中不同的色块表示不同的函数。
切换栈帧:
除了 main 函数,其他的函数都是被调用的函数,每个函数的 Enter 和 Leave 指令,都可以看做是被调用函数的模版!
传递参数和返回值:
首先我们需要知道一个栈帧包含了什么内容,如下图所示:
那如何进行的参数传递和返回值的呢,下面这个汇编代码,较为全面的进行了说明:

4.4 CISC 和 RISC 基本概念
CISC 即复杂指令系统计算机,RISC 即精简指令系统计算机。
基本概念和比较如下图所示:
