关于我

指令系统

第四章_指令系统

4.1 指令系统

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

4.1.1 指令的基本格式

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

4.1.2 扩展操作码指令格式

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

  1. 不允许短码是长码的前缀,即短操作码不能与长操作码的前面的部分相同。
  2. 各指令的操作码一定不能重复。

一种扩展操作码的指令格式如下图所示: 扩展操作码

4.2 寻址方式

4.2.1 指令寻址

指令寻址有两种方式:顺序寻址跳跃寻址

4.2.1.1 顺序寻址

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

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 偏移寻址

偏移寻址分为三种:

  • 基址寻址:以程序存放的地址作为 “起点”。
  • 变址寻址:程序员自己决定从哪里作为 “起点”。
  • 相对寻址:以程序计数器 PC 所指地址作为 “起点”。

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


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


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


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

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 和 ret 指令只有改变 PC 寄存器地址的效果,那么:

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

访问栈帧里的数据: 访问栈帧里的数据都两种方法,如下图所示,注意,一个函数就对应这样一个栈,下图里栈中不同的色块表示不同的函数。 访问栈帧里的数据 切换栈帧: 除了 main 函数,其他的函数都是被调用的函数,每个函数的 Enter 和 Leave 指令,都可以看做是被调用函数的模版切换栈帧 传递参数和返回值: 首先我们需要知道一个栈帧包含了什么内容,如下图所示: 栈帧包含了什么内容如何进行的参数传递和返回值的呢,下面这个汇编代码,较为全面的进行了说明: 如何进行的参数传递和返回值

4.4 CISC 和 RISC 基本概念

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

总是在探索未知