关于我

指令系统

第四章_指令系统

4.1 指令系统

指令系统是 指令集体系结构(ISA) 中最核心的部分,ISA 完整的定义了软件和硬件之间的接口,是机器语言或汇编语言程序员必须要掌握的基础。

ISA 规定的内容主要包括:

  1. 指令格式、寻址方式、操作类型,以及各操作所需操作数的个数、类型和寻址约束。
  2. 操作数的数据类型,以及是按大端还是小端方式存放。
  3. 程序可访问的寄存器编号、数量和位数,存储空间的大小及编址方式。
  4. 程序员可见的控制状态,如程序计数器、条件码的定义与行为等。

4.1.1 指令的基本格式

一条指令是机器语言的一个语句,基本格式由 操作码字段 和 地址码字段 组成。 根据指令中 操作数地址码数目的不同,可将指令分为如下几种格式: 操作数地址码数目的不同 根据指令中 指令长度的不同,可将指令分为: 指令长度的不同 根据指令中 操作码长度的不同,可将指令分为: 操作码长度的不同 根据指令中 操作类型的不同,可将指令分为: 操作类型的不同

4.1.2 扩展操作码指令格式

扩展操作码指令格式 即 指令长度不变 + 操作码长度可变,在设计扩展操作码指令格式的时候,需注意一下两点:

  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 跳跃寻址

通过转移类指令实现,分为两种:

  1. 绝对转移: 比如汇编语言中的 JMP 指令,地址码直接给出目标地址。
  2. 相对转移: 地址码给出相对于当前 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 = 变址寄存器 + 偏移量 通过改变变址寄存器的值,依次访问 数组中的不同元素 程序员自己决定从哪里作为 “起点”

值得注意的是:

  1. 这些偏移量一般用有符号数(补码)来表示,也能用无符号数表示,可能涉及到扩展以及偏移范围的问题。具体是用什么表示题目会具体说明。
  2. 若采用基址寻址和变址寻址,地址字段需要用一些位数来表明寄存器编号,指出使用哪个基址寄存器/变址寄存器。具体用几位题目会说明,比如说总共 16 个寄存器那地址字段就要拆 4 位出来等。

基址寻址 如下图所示: 基址寻址 关于基址寻址的作用,如下图所示: 基址寻址的作用


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


相对寻址 如下图所示:

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


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

4.2.2.8 堆栈寻址

堆栈寻址,其读 / 写单元的地址由堆栈指针(SP)的特定寄存器给出。堆栈可分为两类:

  1. 高速寄存器构成(硬堆栈)。
  2. 主存中化出一部分区域实现(软堆栈)。

堆栈寻址如下图所示: 堆栈寻址

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

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

4.3 程序的机器级代码表示

在考研中并不会考察将更高级语言翻译成汇编语言,只需结合 C 语言能够看懂关键汇编语言的代码即可,比如能够看懂常见指令、选择结构、循环结构、函数调用等。

默认考察对象为 X86 汇编指令,MIPS 指令通常会在试题中附带功能说明。

4.3.1 高级语言和机器级代码之间的关系

本小节总览如下: 小节总览 关于 X86 架构的寄存器有哪些如下图所示,注意为了兼容早期 8位、16位的架构,通用寄存器能够进一步拆分。 X86 架构的寄存器 再来看一些常见的汇编指令的例子就不会感到陌生了: 常见的汇编指令的例子

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 系统下运行。

两种格式的写法对比如下(咸鱼说掌握这些就可以了): AT&T 格式 VS Intel 格式

4.3.4 选择语句的机器级表示

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

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

4.3.5 循环语句的机器级表示

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

4.3.6 函数调用的机器级表示

本节知识总览如下图所示: 知识总览


call 和 ret 这两个指令通常用于函数调用,一看便知,如下图所示: 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 旧值,那么:

  1. 汇编语言怎么传递调用函数,返回值?
  2. 如何访问栈帧里的数据?
  3. 如何切换栈帧?

访问栈帧里的数据: 访问栈帧里的数据都两种方法,如下图所示,注意,一个函数就对应这样一个栈,下图里栈中不同的色块表示不同的函数。 访问栈帧里的数据 切换栈帧: 除了 main 函数,其他的函数都是被调用的函数,每个函数的 Enter 和 Leave 指令,都可以 看做是被调用函数的模版!

调用 call 指令(压 PC 旧值) 之后,紧接着调用 Enter 指令(压上一层函数基址),在 ret 指令之前,调用 Leave 指令,两个指令的具体作用如下图所示: 切换栈帧 传递参数和返回值: 首先我们需要知道一个 栈帧包含了什么内容,如下图所示: 栈帧包含了什么内容 那 如何进行的参数传递和返回值 的呢,下面这个汇编代码,较为全面的进行了说明: 如何进行的参数传递和返回值 值得注意的是:一般考研都是和图中一样的模型,只需记住,[ebx + 一个数] 表示访问本函数变量。[ebx - 一个数] 表示访问上层函数变量。

4.4 CISC 和 RISC 基本概念

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

总是在探索未知