【计算机组成原理】指令系统

  • ~4.50K 字
  1. 1. 指令基本格式
    1. 1.1. 分类
      1. 1.1.1. 按操作数
      2. 1.1.2. 按指令长度
      3. 1.1.3. 按操作码长度
      4. 1.1.4. 按操作类型
    2. 1.2. 扩展操作码
  2. 2. 寻址
    1. 2.1. 指令寻址
      1. 2.1.1. 按字编址
      2. 2.1.2. 按字节编址
      3. 2.1.3. 变长指令字结构
      4. 2.1.4. 跳跃寻址
    2. 2.2. 数据寻址
      1. 2.2.1. 直接寻址
      2. 2.2.2. 间接寻址
      3. 2.2.3. 寄存器寻址
      4. 2.2.4. 寄存器间接寻址
      5. 2.2.5. 隐含寻址
      6. 2.2.6. 立即寻址
      7. 2.2.7. 偏移寻址
        1. 2.2.7.1. 基址寻址
        2. 2.2.7.2. 变址寻址
        3. 2.2.7.3. 相对寻址
      8. 2.2.8. 堆栈寻址
  3. 3. 汇编语言
    1. 3.1. 指令格式
    2. 3.2. x86的常用寄存器
    3. 3.3. 常用x86汇编指令
      1. 3.3.1. 算术运算
      2. 3.3.2. 逻辑运算
    4. 3.4. AT&T与Intel格式
    5. 3.5. 选择语句
      1. 3.5.1. 无条件转移指令
      2. 3.5.2. 条件转移指令
    6. 3.6. 循环语句
      1. 3.6.1. 条件转移指令实现
      2. 3.6.2. loop指令实现
    7. 3.7. 函数调用
      1. 3.7.1. call与ret
      2. 3.7.2. 访问栈帧
      3. 3.7.3. 切换栈帧
      4. 3.7.4. 传参与返回值
  4. 4. CISC和RISC

指令基本格式

通常包括操作码字段和地址码字段两个部分

分类

按操作数

零地址指令:

  • 不需要操作数,如空操作,停机和关中断等
  • 堆栈计算机,两个操作数隐含在栈顶和次栈顶,计算结果压入栈顶

一地址指令:

  • 只要单操作数,如++,–,取反,求补等

    格式:,即取中的内容操作后放回

    三次访存:取指、读A1、写A1

  • 需要俩操作数,但其中一个隐含在某个寄存器中(如ACC)

    格式:

​ 两次访存:取指、读A1

二地址指令:

  • 用于两个操作数的算术运算和逻辑运算等

    格式:

    四次访存:取指、读A1、读A2、写A1

三地址指令:

  • 用于两个操作数的算术和逻辑运算等

    格式:

    四次访存:取指、读A1、读A2、写A3

四地址指令:

  • 格式:是吓一跳将要执行指令的地址

    四次访存:取指、读A1、读A2、写A3

也就是执行指令后,PC的值修改为A4所指地址

按指令长度

指令字长:指令的总长度

  • 定长指令字结构:指令系统中所有指令长度都相等
  • 变长指令字结构:指令系统中所有指令长度不等

机器字长:CPU一次整数运算所能处理的二进制数据的位数(和ALU相关)

存储字长:一个存储单元中的二进制代码位数(和MDR位数相同)

分为半字长、单字长、双字长指令,会影响取指令所需时间

按操作码长度

定长操作码:所有操作码长度都相同(若有n为则最多条指令)

可变长操作码:各指令操作码长度可变

按操作类型

数据传送:如LOAD,STORE

算术逻辑操作

移位操作

转移操作:如JMP,JZ1

输入输出操作

扩展操作码

定长指令字结构+可变长操作码即为扩展操作码

设计扩展操作码时,要注意:

  • 不允许短码是长码的前缀
  • 各指令操作码一定不能重复

对使用频率较高的指令分配较短的操作码,较低的分配较长的

例:指令字长固定16位,地址码需要四位,需要满足

  • 15条三地址指令
  • 12条二地址指令
  • 62条一地址指令
  • 32条零地址指令

则三地址指令的指令码设为0000-1110

二地址开头4bit为1111,后四位设为0000-1011,剩余的开头11xx的留给下一位地址

所以一地址开头设为111111,后六位设为1地址指令,范围是110000-111101,剩余开头11111x的留给下一位地址

所以零地址开头设为11111111111,后五位正好0-31装32条,00000-11111

总结一下:若地址长度为n,上一层留出m种状态,下一层可以扩展出种状态

寻址

指令寻址

PC始终用于指向下一条要执行的指令的地址

若对一条2字节的指令寻址,但是如果是按字节编址,一条指令需要+2才能得到下一条,或者按变长指令字,PC不知道一条指令多长,无法按指定值递增,会出现问题

看两个例子:

按字编址

按字节编址

变长指令字结构

图中相同颜色的是一条指令,可以看出每条指令占的字节数都不同

跳跃寻址

可以由转移指令JMP给出指定地址码,将PC的值修改为对应地址,实现无条件转移

数据寻址

确定本条指令的地址码指明的真实地址

数据寻址有十种寻址方式,为了指定每次寻址的寻址特征,可以在每个形式地址前添加一个四位的寻址方式位,称为寻址特征

寻址特征+形式地址才是操作数的真实地址,称为有效地址(EA)

直接寻址

指令字中的形式地址A就是操作数的真实地址EA,即EA=A

需要访存两次:取指令、执行指令

优点:执行指令仅访问一次主存,不需要专门计算操作数地址

缺点:A的位数决定指令操作数的寻址范围,操作数地址不易修改

间接寻址

指令地址字段给出的是操作数有效地址所在的存储单元的地址,即地址的地址,EA=(A)

间接寻址可以间接若干次

访存三次:取指令一次,执行指令两次

优点:可以扩大寻址范围(有效地址EA的位数大于形式地址A的位数),便于编制程序

缺点:多次访存

寄存器寻址

指令字中直接给出操作数所在寄存器编号,即,操作数位于所指的寄存器内

访存一次:取指令一次

优点:执行阶段不访问主存,只访问寄存器,指令字短且速度快

缺点:价格昂贵,寄存器个数有限

寄存器间接寻址

寄存器给出的是操作数所在主存单元地址

访存两次:取指令一次,执行指令一次

特点:比一般间接寻址快

隐含寻址

指令中隐含着操作数的地址

优点:有利于缩短指令字长

缺点:需要增加存储操作数或硬件

立即寻址

形式地址A就是操作数本身,又称为立即数,一般采用补码,地址前用#表示

访存一次:取指令

偏移寻址

基址寻址

将CPU中基址寄存器(BR)的内容加上指令格式中的形式地址A,形成操作数的有效地址,即EA=(BR)+A

基址寻址

有的设备中会使用若干通用寄存器代替基址寄存器的功能,需要花个bit位指明用哪个寄存器作为基址寄存器

优点:便于程序浮动,方便多道程序并发运行

注意:基址寄存器是面向操作系统的,内容由操作系统或管理程序决定,程序执行时基址寄存器内容不变

采用通用寄存器作为基址寄存器时,用户只可以决定用哪个寄存器作为基址寄存器

变址寻址

有效地址EA等于指令字中的形式地址A与变址寄存器IX的内容相加之和,即EX=(IX)+A

同样可以用通用寄存器作为变址寄存器

注:变址寄存器是面向用户的,程序执行过程中,变址寄存器的内容可以由用户改变

例:

相对寻址

把程序计数器PC的内容加上指令格式中的形式地址A而形成操作数的有效地址,即EA=(PC)+A,A是相对于PC所指地址的偏移量,补码表示

例:

优点:代码在程序内浮动时不用更改跳转指令的地址码

堆栈寻址

指操作数放在堆栈中,隐含使用堆栈指针SP作为操作数地址

堆栈是存储器内一块特定按LIFO管理的存储区,该存储区中被读写单元的地址是用一个特定的寄存器给出的,称为堆栈指针SP

汇编语言

指令格式

以mov为例,格式为mov d,s

作用为将源操作数s复制到目的操作数d所指位置

  • mov eax, ebx:将寄存器ebx的值复制到eax
  • mov eax, 5:将立即数5复制到寄存器eax
  • mov eax, dword ptr [地址]:将指定内存地址所指的双字值复制到寄存器eax
  • mov byte ptr [地址], 5:将立即数5复制到指定内存地址所指的一字节中

其中内存的读写长度:

  • dword ptr:双字,32bit
  • word ptr:单字,16bit
  • byte ptr:字节,8bit

x86的常用寄存器

寄存器
  • 以E开头的寄存器长为32bit
  • 前四个寄存器存储未知数据,结尾为X,称为通用寄存器
  • 下面两个寄存器 处理字符串等,结尾为I,称为变址寄存器
  • EBP为堆栈基指针
  • ESP为堆栈顶指针
  • 前四个若只想使用16bit,可以去掉前面的E
  • 前四个若只想使用8bit,对应的寄存器为AH, AL,BCD同理

常用x86汇编指令

算术运算

除法这里,s为除数,被除数被隐含寻址于edx:eax中

这里的意思是两个寄存器拼接,因为在进行除法操作时,被除数会扩展其位数到原来的两倍

逻辑运算

AT&T与Intel格式

选择语句

无条件转移指令

jmp 地址 将PC无条件的转移到对应地址,或寄存器指向的地址

jmp 标号无条件转移到对应标号指明位置,标号的格式名字:

条件转移指令

条件转移指令

一般要和cmp a,b一起使用,先比较,若上面的比较成立则执行下面的跳转指令

循环语句

条件转移指令实现

由四个部分组成:

  • 循环前初始化
  • 是否直接跳过循环
  • 循环主体
  • 是否继续循环

loop指令实现

loop默认用ecx作循环计数器

loop对应的还有某些loopx指令,如loopnz表示当ecx!=0且ZF==0时继续循环

函数调用

call与ret

函数调用:call 函数名

函数返回:ret

通常用函数名作为函数起始地址的标号

注:Intel里PC被叫做IP

  • 执行call后,会将IP内的值(call的下一条)压入栈顶
  • 然后进入调用的函数,将IP新值设为被调用函数第一条指令
  • 调用函数栈帧出栈,ret后将IP旧值写回

访问栈帧

在虚拟内存用户区中,用户栈的栈底位于高地址,栈顶位于低地址,所以一般表示栈是倒着画的,且x86系统中,默认以四字节为栈的操作单位

寄存器ebp指向当前栈帧的底部

寄存器esp指向当前栈帧的顶部

指令:

  • push:入栈操作,先让esp减4,再将数据压入
  • pop:出栈操作,栈顶元素出栈,写入指定寄存器,再让esp加4
  • mov:访问栈其他位置 ,结合ebp和esp指针访问数据,可以用sub/add指令修改栈顶指针esp的值

切换栈帧

当call一个新函数时,我们进入对应栈帧:

  • 首先call指令会将IP旧值(call的下一句)压栈保存,并设置IP新值为被调用函数的第一条指令
  • push ebp把上一层函数的ebp旧值压入栈,这个地址就是返回地址
  • 然后move ebp, esp,把ebp寄存器指向到esp的位置(新函数地址)
  • 接下来用mov等压入数据,执行操作
  • 前两个操作可以用一个enter指令代替
  • 函数返回后,新的栈帧消失,栈顶此时就是原来的ebp旧值和IP旧值
  • 首先执行mov esp, ebp,将esp指向当前栈帧底部

  • 然后执行pop ebp,将esp所指元素出栈,写入寄存器ebp,实现让ebp重新指回上一层函数栈帧基址

  • 上面两个命令等价于leave指令,即从函数栈帧顶部找到IP旧值,出栈并恢复IP寄存器

传参与返回值

一个栈帧内可能包含的内容:

  • 最底部:上一层栈帧基址(ebp、旧值)
  • 最顶部:返回地址(当前函数栈帧除外)
  • 靠近底部:用于存储局部变量,越靠前定义的越靠近栈顶
  • 靠近顶部:用于存储形式变量,参数列表中越靠前的参数越靠近栈顶
  • 中间:可能有未使用区域没因为栈帧大小要求16字节整数倍

CISC和RISC

CISC:复杂指令集,一条指令完成一个复杂的基本功能,用于x86架构

如果有复杂指令用纯硬件实现困难,可以采用存储程序的设计思想,由一个比较通用的电路配合存储部件完成一条指令,即形成微程序

RISC:精简指令集,一条指令完成一个基本动作,多条指令组合完成一个复杂的基本功能 ,用于arm架构

赞助喵
非常感谢您的喜欢!
赞助喵