计算机组成原理笔记-第四章-指令系统

基于里昂学长的计算机组成原理课程

笔记目录

第一章-计算机系统概述
第二章-数据的表示和运算
第三章-存储系统
第四章-指令系统
第五章-中央处理器
第六章-总线
第七章-输入输出系统

第四章 指令系统

4.1 指令系统概述

4.1.1 指令的基本格式

基本格式

一条指令就是机器语言的一部分,一条机器语言指令通常包含操作码字段地址码字段两部分:

操作码指出指令应该执行什么操作,比如指出是做加法还是减法。

地址码给出被操作的信息(指令或数据)的地址,包括参加运算的操作数的地址、运算结果的保存地址、程序的转移地址等等。

image-20250623160342795

指令字长是指一条指令所包含的二进制代码的位数,其取决于操作码的长度、地址码的长度和地址码的个数。

指令字长和机器字长没有固定的关系,一般把指令长度等于机器字长的指令称为单字长指令,指令长度等于半个机器字长的指令称为半字长指令,指令长度等于两个机器字长的指令称为双字长指令

指令长度的不同会导致指令时间开销的不同。

单字长指令只需要访存一次就能将指令完整取出;双字长指令则需访存两次才能完整取出,耗费两个存取周期。

在一个指令系统中,如果所有指令的长度都是相等的,则称为定长指令字结构,其特点为执行速度快,控制简单。如果各种指令的长度随指令功能而异,则称为变长指令字结构

主存一般按字节编制,所以指令字长通常为字节的整数倍

指令格式的分类(了解)

根据指令中操作数、地址码数目的不同,可以将指令分为几种格式。

  • 零地址指令

只给出操作码OP,没有显式地址。有两种可能:

  1. 不需要操作数的指令。比如空操作、停机。
  2. 零地址的运算类指令(仅用在堆栈计算机中)。通常参与运算的两个操作数隐含地从栈顶和次栈顶弹出,送到运算器进行运算,运算结果再隐含地压入堆栈(隐含寻址)。

image-20250623162019231

  • 一地址指令

有一个OP操作码和$A_1$地址。

有两种常见的可能:

  1. 只有目的操作数的单操作数指令,按$A_1$地址读取操作数,进行OP操作后,结果存回原地址。例如操作码含义为加1,减1,求反,求补等等。
    指令含义:OP($A_1$)->$A_1$
  2. 隐含约定目的地址的双操作数指令,按指令地址$A_1$可读取源操作数,指令可隐含约定另一个操作数,由ACC(累加器)提供,运算结果也存放在ACC中。
    指令含义:(ACC)OP($A_1$)->ACC
  • 二地址指令

有一个OP操作数和两个地址$A_1$和$A_2$。

指令含义:($A_1$)OP($A_2$)->$A_1$

对于常用的算术和逻辑指令,往往要求使用两个操作数,需分别给出目的操作数和源操作数的地址。其中目的操作数地址还用于保存本次的运算结果。

  • 三地址指令

有一个OP操作数和三个地址$A_1$、$A_2$、$A_3$($A_3$用于保存结果)。

指令含义:($A_1$)OP($A_2$)->$A_3$

  • 四地址指令

有一个OP操作数和四个地址$A_1$、$A_2$、$A_3$、$A_4$($A_3$用于保存结果,$A_4$用于保存下址)。

指令含义:($A_1$)OP($A_2$)->$A_3$,$A_4$=下一条将要执行指令的地址。

4.1.2 指令操作码

操作码字段表示具体进行什么运算操作,不同功能的指令其操作码的编码不同。比如可以用0001表示加法操作,0010表示减法操作。

操作码有定长操作码变长操作码两种。

定长操作码指令格式

定长操作码指令在指令字的最高位部分分配固定的定长位表示操作码。一般n位的操作码字段的指令系统最大能表示$2^n$条指令。

定长操作码对于简化硬件设计、提高指令译码和识别速度很有利。当计算机字长位32位或更长时,这是常规用法。

扩展操作码指令格式(重点)

为了在指令字长有限的前提下增加指令数量,可采取可变长度操作码,即全部指令的操作码位数不固定,且分散地放在指令字的不同位置上。

这将增加指令译码和分析的难度,使控制器的设计复杂化。常见的变长操作码是扩展操作码。它使操作码的长度随地址码的减少而增加。

image-20250623164730677

设计扩展操作码指令格式时要注意:

  1. 各指令的操作码不能重复。
  2. 各操作码是前缀编码,任何操作码不能是其他编码的前缀。

通常情况下,对使用频率较高的指令分配较短的编码,反之亦然,尽可能减少指令译码和分析的时间。

4.1.3 操作码类型

操作码类型有很多种,可以按照功能分为:数据传送算术逻辑运算移位转移等几大类。

  • 数据传送

传送指令通常有寄存器之间的传送(MOV)、从内存单元读取数据到CPU寄存器(LOAD)、从CPU寄存器写数据到内存单元(STORE)、进栈操作(PUSH)、出栈操作(POP)等。

  • 算术逻辑操作

算术逻辑操作包括算术运算逻辑运算两种。

  1. 算术运算:加(ADD)、减(SUB)、乘(MUL)、除(DIV)、自增1(INC)、自减1(DEC)等。
  2. 逻辑运算:与(AND)、或(OR)、求反(NOT)、异或(XOR)等。
  • 移位操作

包括算术移位和逻辑移位,可以通过左移或右移实现操作数的放大和缩小。由于移位操作实现的乘除法比乘除操作要更快,因此在实际中常用移位取代简单的乘除运算来提升代码的运行效率。

  • 转移操作

计算程序的顺序执行是通过程序计数器PC的自增实现的,要改变顺序执行就需要强行改变PC的值,能够实现该操作的指令被称为转移类指令。一般包括无条件转移指令(JMP)条件转移指令(BRANCH)调用指令(CALL)返回指令(RET)陷阱指令(TRAP)等。

  • 输入输出

这类指令专门完成CPU和外围设备传输数据、状态信息和控制命令的指令。

  • 其他

其他指令包括特权指令(操作系统)、空操作指令(不做其他操作)、向量指令(用来完成向量操作的指令)等。

4.1.4 ISA

指令集体系结构(ISA,Instruction Set Architecture)定义了计算机系统中指令的集合以及这些指令的规范,完整定义了软硬件之间的接口(相当于一个标准)。

一台计算机的所有指令集合构成该计算机的指令系统,称为指令集,是ISA最核心的部分。

ISA规定的主要内容:

  1. 指令格式、指令寻址方式、操作类型,以及每种操作对应的操作数的相应规定。
  2. 操作数的类型,操作数寻址方式,以及是按大端还是小端方式存放。
  3. 程序可访问的寄存器编号、个数和位数,存储空间的大小和编址方式。
  4. 指令执行过程的控制方式等,包括程序计数器、条件码定义等。

ISA规定了机器级程序的格式,机器语言或汇编语言程序员必须对机器的ISA非常熟悉。

4.2 指令的寻址方式

4.2.1 指令寻址与数据寻址

寻址就是寻找操作数的地址或下一条将要执行的指令的指令地址。寻址方式分为指令寻址数据寻址两大类。

寻找下一条将要执行的指令地址称为指令寻址,寻找本条指令的数据地址称为数据寻址

编址与寻址

需要编址的设备主要有CPU中的通用寄存器主存储器输入输出设备三种,必须对他们编址后才能访问。通常指令中的地址码字段指出操作数的来源和去向,操作数存放在相应的存储设备中。

  • 存储设备是CPU中的通用寄存器,在指令字中应该给出寄存器编号。
  • 是主存的一个存储单元,指令字中应该给出主存单元地址。
  • 是输入输出设备的一个寄存器,指令字中应该给出设备编号或设备端口地址或设备映像地址。
指令寻址

指令寻址可以分为顺序寻址跳跃寻址

顺序寻址可以通过程序计数器+1自动形成下一条指令的地址。

跳跃寻址则需要通过程序转移类指令实现。

跳跃寻址的转移地址形成方式有3种:直接寻址相对寻址以及间接寻址

这三种寻址方式寻找到的是转移的有效地址

PC自增的大小和编址方式、指令字长都有关。现代计算机通常按字节编址,假设指令字长16位,则PC自增为+2;假设指令字长为32位,则PC自增+4。

数据寻址

数据寻址种类较多,最终目的都是寻找所需要的操作数

通常在指令字中设置一个寻址特征字段,用来指明属于哪种寻址方式。

image-20250624190046397

指令中的地址A不代表操作数的真是地址,这个地址称为形式地址(A)

形式地址结合寻址方式可以计算出操作数的真实地址,这个地址称为有效地址(EA)

从形式地址生成有效地址的方式称为寻址方式

一般每个形式地址都需要一个寻址特征字段,不同的CPU支持的寻址方式的数目是不同的,这个数目会决定寻址特征的位数。每个计算机的指令系统都有自己的一套寻址方式,不同计算机的寻址方式的名称和含义不统一。

4.2.2 常见的数据寻址方式

立即寻址

立即寻址又称立即数寻址,是将操作数本身放至指令字中的地址码字段,寻址特征常常用#来表示。数据就在指令中,取出指令也就取出了操作数,这样的数称为立即数

image-20250625093036742

假如指令字长为16位,操作码6位,寻址特征2位,立即数采用补码的表示形式,则取值范围为-128-127。(形式地址的位数决定了操作数的范围)

优点:操作码和操作数同时取出,不必再次访问主存,提高指令执行速度。

缺点:操作数是指令的一部分,不能被修改。立即数的大小受到指令长度的限制,灵活性最差。通常用于给某一寄存器或主存单元赋初值提供一个常数

直接寻址(绝对寻址)

直接寻址方式时操作数在主存储器中,操作数地址由形式地址A直接给出,形式地址就是有效地址

image-20250625095021856

优点:不需要做寻址运算,简单直观,便于硬件实现,取操作数只要访存一次。

缺点:形式地址等于有效地址,寻址范围受形式地址A位数的影响。

寄存器寻址

寄存器寻址指令的地址码部分给出某一个通用寄存器的编号$R_i$,这个寄存器存放操作数。有效地址EA就是寄存器的编号$R_i$。

image-20250625095417282

优点:从寄存器中存取数据比主存中存取快得多。且由于寄存器数量较少,其地址码字段比主存单元地址短得多,可以缩短指令长度,提高指令执行速度。

缺点:寄存器造价昂贵且数量较少,不能为操作数提供大量的存储空间。

隐含寻址

隐含寻址的指令字中不直接给出操作数的地址,而是在指令中隐含操作数地址。例如只有一个操作数的形式地址A,另一个操作数则隐含在寄存器ACC中。由于减少了一个操作数的地址,需要额外增加的硬件实现隐含操作,好处则是可以缩短指令字长。

image-20250625095839480.png

间接寻址

间接寻址意味着指令中给出的形式地址A是存放操作数地址的主存单元的地址,即(EA)=A。分为一级间址多级间址

对于一级间址,先访问地址A的主存单元,该位置存放操作数的有效地址EA,再访问地址EA的主存单元得到操作数。一级间址取操作数需要访问两次主存。

image-20250625104712695.png

对于多级间址需要多次访问主存,找到操作数的有效地址EA后还需要访问一次主存取得操作数。访问的每个主存单元都应该设有一个间址标志位,一般放在主存单元的最高位。

image-20250625101147257

  • 间址标志位=1,说明仍然是间接地址,需要继续寻址。
  • 间址标志位=0,说明找到有效地址,再进行一次访存可以读出真正的操作数。

优点:与直接寻址相比扩大了操作数寻址范围

缺点:增加了访问主存的次数,导致指令执行时间变长。

image-20250625104712695

寄存器间接寻址

为了克服在间接寻址中多次访存的缺点,可以采用寄存器间接寻址,指令中的地址码给出某一通用寄存器的编号,在被指定的寄存器中存放操作数的有效地址,操作数则存放在主存中,即(EA)=$R_i$。

image-20250625101358411

这种寻址方式的指令较短,取指令后一次访存便可得到操作数,执行速度较间接寻址快,是一种使用广泛的寻址方式。

基址寻址

基址寻址是将基址寄存器BR的内容加上指令字形式地址A而形成操作数的有效地址。即EA=(BR)+A。

基址寄存器可采用专用寄存器,也可指定某个通用寄存器作为基址寄存器。

image-20250625101759599

基址寻址将用户的逻辑地址转换成主存的物理地址。基址寄存器是面向操作系统的。

使用基址寻址,编写程序时不必考虑该程序位于主存的具体位置。程序执行过程中,基址寄存器的内容不变(作为基地址),形式地址可变(作为偏移)。采用通用寄存器作为基址寄存器时,用户可以决定是哪个寄存器,但是内容还是操作系统决定。

基址寻址可以扩大寻址范围(基址寄存器的位数大于形式地址A的位数),用户不必考虑自己的程序位于主存的具体位置,但是偏移量的位数(即形式地址A)较短。有利于多道程序设计,并可用于编址浮动程序

变址寻址

变址寻址变址寄存器IX的内容指令中给出的形式地址A相加,形成操作数的有效地址,即EA=(IX)+A。IX的内容称为变址值

image-20250625112706656

变址寄存器是面向用户的。变址寄存器的内容可由用户改变(作为偏移量),形式地址A不变(作为基地址)。

优点:扩大寻址范围;在数组处理过程中,可设定A为数组的首地址,不断改变变址寄存器IX的内容,可以很容易形成数组任意一个数据的地址,特别适合编制循环程序。偏移量的位数足以表示整个存储空间。

变址寻址和基址寻址的有效地址形成过程相似,本质上两者有较大区别。

基址寻址面向系统,主要用于为多道程序或数据分配存储空间。因此基址寄存器的内容通常由操作系统或管理程序确定,在程序执行过程中其值不可变,而在指令字中的A是可变的。

变址寻址面向用户,主要用于处理数组问题,在变址寻址中,变址寄存器的内容由用户设定,在程序执行过程中其值可变,而指令字中的A是不可变的

相对寻址

相对寻址是基址寻址的一种变通,由程序计数器(PC)提供基准地址,指令中的形式地址A作为偏移量,两者相加后得到操作数的有效地址,即EA=(PC)+A。A可正可负,用补码表示。

image-20250625113536185

A的位数决定操作数的寻址范围。

优点:操作数的地址随着PC的变化而变化,且与指令地址总是相差一个固定的偏移量,因此便于程序浮动。相对寻址广泛应用于转移指令

基址寻址、变址寻址、相对寻址三种寻址方式非常类似,都将某个寄存器的内容和一个形式地址相加而生成操作数的有效地址,通常把这三种寻址方式称为偏移寻址

堆栈寻址

堆栈区通常是主存储器中指定的一个区域,也可以专门设置一个小而快的存储器作为堆栈区。在堆栈容量很小的情况下,还可以用一组寄存器来构成堆栈。

堆栈是存储器或寄存器组中一块特定的、按后进先出(LIFO)原则管理的存储区。

  • 寄存器堆栈

又称为硬堆栈。这种堆栈的栈顶是固定的,各寄存器是相互连接的,他们之间有对应位自动推移的功能。由于栈顶的位置是固定的,因此不必设置栈顶指针

image-20250625114844644

  • 存储器堆栈

又称为软堆栈。寄存器的成本比较高,不适合做大容量堆栈,从主存中划分出一段区域比较合算也比较常用。堆栈大小可变,栈底固定,栈顶浮动,需要一个专门的硬件寄存器作为栈顶指针,简称栈指针(SP)。栈指针指定的存储单元就是堆栈的栈顶。

存储器堆栈又分为自底向上生成堆栈自顶向下生成堆栈

自底向上生成堆栈即向低地址方向生成,栈底地址大于栈顶地址。进栈时,SP的内容自动-1,然后压入数据;出栈时,需要先将堆栈中的数据弹出,然后SP的内容+1。

image-20250625115454815

自定向下生成堆栈即向高地址方向生成,栈底地址小于栈顶地址。进栈时,SP的内容自动+1,然后压入数据;出栈时,需要先将堆栈中的数据弹出,然后SP的内容-1。

软堆栈的容量可以很大,而且可以在主存中浮动,但是速度比较慢。每访问一次堆栈就时访问一次主存。一些计算机系统希望堆栈容量大、速度快,可以将前两种堆栈组合起来实现软、硬结合的堆栈。

扩展变址方式(了解)

变址和间址两种寻址方式结合起来,就变成扩展变址方式,按寻址方式操作的先后顺序,有前变址后变址两种形式。

  • 先变址后间址(前变址方式)

先进行变址运算,其运算结果作为间接地址,间接地址指出的单元的内容才是有效地址,即EA=((IX)+A)。

image-20250625120432590

  • 先间址后变址(后变址方式)

将指令中的地址码先进行一次简介寻址,然后再与变址值进行计算,从而得到EA,即EA=(IX)+(A)。

image-20250625120551843

基址变址寻址

基址变址寻址是最灵活的一种寻址方式,有效地址是由基址寄存器中的值、变址寄存器中的值和偏移量三者相加求得的。

除了偏移量A在指令一旦确定后就不能再修改之外,基址和变址寄存器的内容都可以改变,即EA=(BR)+(IX)+A。

总结

image-20250625120749103

4.3 程序的机器级代码表示

4.3.1 x86寄存器基础

通用寄存器

x86处理器有8个通用寄存器,最早的x86架构机器是16位字长,包括AXBXSI等通用寄存器。以AX为例,其分为AHAL两部分,处理8位数据时,使用AL寄存器即可。若碰到16位数据,则需要使用AX。

image-20250625142942619

为了向后兼容32位字长的计算机,在16位寄存器的基础上对齐扩展。可以将AX扩展为EAX。x86系列机具有兼容性,即使是32位的机器仍然可以用AL来保存8位数据。AX、AH、AL都可以被当做寄存器独立使用。

image-20250625143144537

通用寄存器都可以用来存储操作数、运算结果或者指针,但是EBP一般只用来指示堆栈底,ESP只用来指示堆栈顶。

EAX扩展累加寄存器(accumulator),通常用于存储操作结果。

EBX扩展基地址寄存器(base),可以在内存寻址时存放基地址。(基地址寄存器)

ECX扩展计数寄存器(count),是某些指令的计数器。

EDX扩展数据寄存器(data),与EAX经常配合使用。

ESI/EDI扩展源/目标指针寄存器(source index/destination),其中ESI通常用于存储当前正在处理的源数据(source data)的索引或地址。(变址寄存器)

EBP扩展基址指针寄存器(base pointer),一般用来指示栈底。

ESP扩展堆栈指针寄存器(stack pointer),一般用来指示栈顶。

以上内容了解即可。

指令指针寄存器

EIP寄存器是一个32位的指令指针寄存器(即PC寄存器),它包含下一条欲执行指令的地址。

EFLAGS寄存器是一个32位的标志寄存器(即程序状态字寄存器PSW),它由多个标志位构成。通常当某标志位位1时,该标志位被称为置位,当某标志位为0时被称为清零

4.3.2 x86汇编指令

指令格式

指令由操作码以及操作数构成。

一条汇编指令包括:助记符 目的操作数 源操作数

助记符:指令操作性质的英文缩写。

操作数:指令的操作对象或操作对象存放的地址。

例:

MOV AX, 2000H
操作类型

一条汇编指令一般有0-3个操作数,类型可能是寄存器内存立即数。使用R[r]表示寄存器r的值,使用M[addr]表示内存地址为addr处的值。

image-20250625145143235

数据传输指令(Intel格式)

汇编通常可以分为数据传送指令算数和逻辑运算指令控制流指令

  • MOV指令

MOV 目的操作数, 源操作数; 将源操作数复制到目的操作数,源操作数内容不变。

  1. MOV <reg>, <reg>; 将一个寄存器的内容移动到另一个寄存器。
  2. MOV <mem>, <reg>; 将寄存器的内容移动到内存地址。
  3. MOV <reg>, <mem>; 将内存的内容移动到寄存器。
  4. MOV <mem>, <con>; 将一个常量移动到内存。
  5. MOV <reg>, <con>; 将一个常量移动到寄存器。

<reg>表示寄存器,如果后面带有数字则指定其位数。

<mem>表示内存内容,例如[eax][var+4]dword ptr[eax+ebx]

<con>表示8位、16位、32位常数。

PTR操作符放在内存操作数之前指明其位数。BYTE PTR=单字节;WORD PTR=字(双字节);DWORD PTR=双字(四字节)。

MOV指令的两个操作数不能同时是内存,不能使用一条MOV指令直接把数据从一个内存位置移动到另一个内存位置。如果需要这么做,应该先把源操作数复制到一个寄存器中,再将这个寄存器复制到目的操作数。

  • push指令

push指令将操作数压入堆栈。

对于32位的操作数,先把ESP-4,然后把32位的操作数复制到栈顶。

push 操作数; 操作数可以是寄存器、内存或立即数。

默认x86的堆栈是向低地址生长的满栈。满栈指堆栈指针指向最后一个已入栈元素的栈,空栈是指堆栈指针指向下一个要入栈元素的空位的栈。

image-20250625151644474

  • pop指令

pop指令把栈顶元素弹出至操作数。

对于32位的操作数,先将栈顶元素复制到操作数,然后再将ESP值+4。

pop 操作数; pop指令的操作数只能是内存或寄存器,不能是立即数

算数与逻辑运算指令
  • add/sub指令

加/减法指令,将目的操作数与源操作数相加并将结果保存在目的操作数中,源操作数保持不变。

add/sub 目的操作数, 源操作数;

add/sub指令可能影响进/借位标志、零标志、符号标志、溢出标志。

image-20250625152158888

  • inc/dec指令

inc/dec指令表示将操作数自加1/自减1。

  • idiv指令

有符号除法(integer division)指令。

操作数是除数,隐藏的被除数是edx:eax(其中高32位在edx中,低32位在eax中),运算结果的商传送至eax,余数传递至edx。操作数类型可以是寄存器或内存。

idiv 操作数;

  • imul指令

有符号整数乘法指令,有两种格式:

  1. 两个操作数:将两个操作数相乘,将结果保存在第一个操作数中,第一个操作数必须为寄存器
  2. 三个操作数:将第二个和第三个操作数相乘,将结果保存在第一个操作数中,第一个操作数必须为寄存器
  • and/or/xor指令

逻辑与(and),逻辑或(or)和逻辑异或(xor)。目的操作数与源操作数进行逻辑运算,结果保存在目的操作数中。

and/or/xor 目的操作数, 源操作数;

  • not指令

取反指令。将操作数的每一位取反并保存至原操作数。操作数类型可以是寄存器或内存。

not 操作数;

  • neg指令

取负指令。将操作数的相反数保存至原操作数。操作数类型可以是寄存器或内存。

neg 操作数;

  • shl/shr指令

逻辑移位指令,逻辑左移(shl,shift left)和逻辑右移(shr,shift right)。

将操作数1以操作数2的值进行逻辑移位,结果保存在操作数1中。操作数1可以是内存或寄存器,操作数2可以是8位立即数cl寄存器

shl/shr 操作数1, 操作数2;

控制流指令
  • cmp/test指令

这两个指令只设置条件码而不改变操作数。除此之外,cmpsub指令、testand指令相同。cmp比较两个操作数的值,与sub指令设置同样的状态字,但不改变第一个操作数的值。

image-20250625153408079

  • 无条件转移指令

代码跳转至label处执行,该标号由汇编器翻译成偏移地址。

jmp <label>;

image-20250625153514090

标号可以作为指令或数据位置标记的标识符,放置在一条指令之前的代码标号表示该指令的地址,放置在变量之前的数据标号代表该变量的地址。

image-20250625153636602

  • call/ret指令

分别用于实现子程序(过程、函数等)的调用及返回。

call

ret;

call用于调用其他程序,ret指令负责返回,是程序(函数)调用中最关键的两条指令。

image-20250625153842390

  • Jcondition系列指令

条件转移指令,条件满足则跳转至label处执行,否则顺序执行,一般与cmp指令配合使用,实现分支或循环结构。

Jcondition

image-20250625154040740

image-20250625154056131

x86指令总结

image-20250625184454207

4.3.3 常用语句的机器级表示

选择结构语句的机器级表示

常用的选择结构语句有if-else等。编译器通过条件码(标志位)和转移指令来实现程序中的选择结构语句,最常用的条件码有CF、ZF、SF和OF。

常见的算数逻辑运算指令在设置条件码的同时修改其他寄存器,cmptest指令只设置条件码而不改变其他寄存器。

if-else的通用形式如下:

if(test_expr)
    then_statement
else
    else_statement

这里的test_expr是一个整数表达式,两个分支语句只会执行一个。

t=test_expr;
if(!t)
    goto false;
then_statement
goto done;
false:
else_statement
done:

image-20250625160913424

示例代码:

mov eax, 7
mov ebx, 6
cmp eax, ebx
jg GREAT
mov ecx, ebx
jmp END
GREAT:
mov ecx, eax
END:
循环结构语句的机器级表示

常见的循环结构语句有whilefordo-while。汇编中没有相应指令存在,可以用条件测试和转移语句来实现循环的效果。在循环结构中通常用条件转移指令来判断循环的结束。大多数编译器将三种循环结构都转换为do-while循环。

do
    body_statement
    while(test_expr);

可以翻译为:

loop:
body_statement
t=test_expr;
if(t)
    goto loop;

每次循环程序都会执行循环体内的语句,body_statement至少会被执行一次,然后执行测试表达式,若测试为真则继续执行循环。

image-20250625162229520

示例代码:

mov eax, 0
mov edx, 1
cmp edx, 100
jg END
LOOP:
add eax, edx
inc edx
cmp edx, 100
jle LOOP
END:

for循环还能用loop指令实现。

image-20250625163034692

过程调用的机器级表示(较难)

假定过程P(调用者)调用过程Q(被调用者),调用的步骤如下:

  1. P将入口参数(实参)放到Q能访问到的地方。
  2. P将返回地址存到特定的地方,然后将控制转移到Q(call指令)。
  3. Q保存P的现场(通用寄存器的内容),并为自己的非静态局部变量分配空间。
  4. 执行过程Q。
  5. Q回复P的现场,将返回结果放到P能访问的地方。
  6. Q取出返回地址,将控制转移到P(ret指令)。

由于用户可见寄存器数量有限,调用者和被调用者需要共享寄存器,不能随意覆盖,因此有如下规范:

  1. 寄存器EAXECXEDX调用者保存寄存器,当P调用Q时,Q如果需要用到这些寄存器,则将这些寄存器的内容保存到栈中,并在返回后由P恢复他们的值。
  2. 寄存器EBXEXIEDI被调用者保存寄存器,当P调用Q时,Q必须先将这些寄存器的内容保存在栈中才能使用他们,并在返回P之前恢复他们的值。

递归函数在执行的过程中会使用来保存函数的状态。

image-20250625163642123

image-20250625163657434

image-20250625163705726

image-20250625163740948

用下面的C语言程序说明过程调用的机器级实现:

int add(int x, int y){
    return x+y;
}
int caller(){
    int temp1 = 125;
    int temp2 = 80;
    int sum = add(temp1,temp2);
    return sum;
}
int main(){
    caller();
    return 0;
}

image-20250625164203985

GCC编译后caller代码如下:

caller:
push ebp
mov ebp, esp
sub esp, 24
mov [ebp-12], 125               #temp1=125
mov [ebp-8], 80                 #temp2=80
mov eax, dword ptr [ebp-8]    #R[eax]=temp2
mov [esp+4], eax                #temp2入栈
mov eax, dword ptr [ebp-12]   #R[eax]=temp1
mov [esp],eax                    #temp1入栈
call add                         #调用add,将返回值保存在eax中
mov [ebp-4], eax                #add返回值送给sum
mov eax, dword ptr [ebp-4]    #sum作为返回值
leave
ret

leave指令的作用:

mov esp, ebp #让esp指向栈帧的底部
pop ebp      #esp所指元素出栈,写入ebp

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

  1. gcc编译器将每个栈帧大小设置为16B的整数倍(当前函数的栈帧除外)
  2. 空闲未使用的区域
  3. 局部变量集中存储在栈帧底部区域
  4. 调用参数集中存储在栈帧顶部区域
  5. 栈帧最底部一定是上一层栈帧的基址(ebp旧值)
  6. 栈帧最顶部一定是返回地址(当前函数的栈帧除外)

一个过程对应的机器级代码通常有3个部分:准备阶段、过程体、结束阶段。

准备阶段:调整指针位置,设置上一层基址…

过程体:执行具体过程,返回值存入EAX中…

结束阶段:恢复值,恢复指针位置…

两种汇编指令格式

x86架构有两种主要的语法格式:AT&T格式和Intel格式。

image-20250625172647032

它们的区别主要体现如下:

  1. AT&T格式的指令只能用小写字母,Intel格式的指令对大小写不敏感。
  2. AT&T格式的指令,第一个为源操作数,第二个为目的操作数,方向从左到右。
    而Intel格式的知识,第一个为目的操作数,第二个为源操作数,方向从右到左。
  3. AT&T格式中,寄存器需要加前缀%,立即数需要加前缀$
    Intel格式中,寄存器和立即数都不需要加前缀。
  4. 内存寻址方面,AT&T格式使用(),Intel格式使用[]。
  5. 在处理复杂寻址方式时,例如AT&T格式的内存操作数disp(base,index,scale)分别表示偏移量、基址寄存器、变址寄存器和比例因子,如8(%edx,%eax,2)表示操作数为M[R[edx]+R[eax]*2+8],其对应的Intel格式的操作数为[edx+eax*2+8]
  6. 在指定数据长度方面,AT&T格式指令操作码后面紧跟一个字符,表示操作数大小。b表示字节,w表示字,l表示双字。Intel也有类似的语法。

4.3.4 MIPS汇编语言基础(了解)

MIPS是一种RISC处理器架构。

寄存器

MIPS提供了32个32位通用寄存器。寄存器以$开始,可以使用寄存器名称,也可以使用寄存器编号。

image-20250625190010630

指令格式和寻址方式

MIPS的存储按字节编址,只能通过Load/Store指令访存,采用大端方式存放数据,要求按字对齐

对于存储器数据,其操作数地址为32位,由一个32位寄存器的内容加上一个16位的偏移量得到,地址空间大小为4GB。

指令格式有3种,分别为R-型指令I-型指令J-型指令

  • R-型指令

image-20250625190459191

R-型指令的寻址方式只有寄存器寻址

操作码为000000,操作类型由func字段指定。

对于双目运算指令,rsrt分别是第一和第二操作数,操作结果送往rd

对于移位指令,则是将rt内容根据shamt字段进行移位,结果送往rd

  • I-型指令

image-20250625190717773

I-型指令的寻址方式有4种,有寄存器寻址立即数寻址相对寻址基址或变址寻址

对于双目运算类指令,将rs的内容和立即数分别作为第一和第二操作数,结果送往rt

对于访存指令,将rs的内容和立即数符号扩展后的内容相加的结果作为内存单元地址进行loadstore

对于条件转移指令,对rsrt的内容进行换算,根据结果决定程序是否跳转运行。转移的目标地址为PC的值加上立即数符号扩展之后的值。

  • J-型指令

image-20250625191218808

J-型指令主要是无条件跳转指令,其寻址方式只有直接寻址

将当前PC的高4位拼接26位直接地址,最后再补充两个0(定长指令要补足地址为4的倍数),得到的就是跳转的目标地址。

MIPS指令简介

MIPS属于RISC指令集,非访存指令不会进行内存相关操作,因此对MIPS指令含义描述中的寄存器不再使用R[]来表示取寄存器的值。MIPS使用#作为注释的起始符号。

  • 算数运算指令:add、sub

add $s1, $s2, $3 ($s2+$s3)

  • 存储访问指令:lw、sw

lw从内存取一个字到寄存器:lw $s1, 100($s2) ($s1<-M[$s2+100])

sw从寄存器存一个字到内存:sw $s1, 100($s2) (M[$s2+100]<-$s1)

  • 逻辑运算指令:and/nor/or

三个操作数都是寄存器。

and $s1, $s2, $s3 ($s1<-($s2&$s3))

  • 条件分支指令:beq/bne

比较寄存器的值,根据比较结果选择是否跳转。

beq:相等则跳转。

bne:不相等则跳转。

  • 无条件跳转指令:j/jal/jr

j Label将PC设为Label的地址。

jal Function将PC设为Function的地址,并将一条指令的地址存入$ra

jr $ra将PC设为$ra寄存器中的值,从而返回到子程序调用前的代码。

4.4 CISC和RISC的基本概念

CISC(复杂指令集计算机,Complex Instruction Set Computer)和RISC(精简指令集计算机,Reduced Instruction Set Computer)是两种不同的计算机CPU设计模式。

CISC旨在增强原有指令的功能,设置更为复杂的新指令实现功能,典型代表x86计算机;

RISC旨在减少指令种类和简化指令功能提高指令的执行速度,典型的有ARM、MIPS架构的计算机。

4.4.1 复杂指令系统计算机(CISC)

特点:

  1. 指令系统复杂庞大,指令数目一般为200条以上。
  2. 指令长度不固定,指令格式多,寻址方式多。
  3. 可以访存的指令不受限制
  4. 各种指令使用频度相差很大
  5. 各种指令执行速度相差很大,大多数指令需要几个时钟周期
  6. 控制器大多数采用微程序控制
  7. 难以编译优化。

庞大的系统对指令设计提出了极高的要求,研制周期变得很长。

人们发现一味追求指令系统的复杂和完备程度不是提高性能的唯一途径。

传统的CISC指令系统各种指令的使用频率相差悬殊,大约只有20%的简单指令被反复使用,约占整个程序的80%。而80%的复杂指令很少使用,约占整个程序的80%。

人们开始用最常用的20%简单指令重组实现不常用的80%的指令功能,RISC随之诞生。

4.4.2 精简指令系统计算机(RISC)

RISC的思想是要求指令系统简化,尽量使用寄存器-寄存器操作指令,指令格式力求一致。

特点:

  1. 选区使用频率最高的一些简单指令,复杂指令的功能由简单指令的组合来实现。
  2. 指令长度固定,指令格式种类少,寻址方式种类少。
  3. 只有LOAD/STORE指令访存,其余指令的操作都在寄存器之间进行。
  4. CPU中通用寄存器的数量相当多
  5. 采用指令流水线技术,大部分指令在一个时钟周期内完成
  6. 硬布线控制为主,不用或少用微程序控制。
  7. 特别重视编译优化工作,以减少程序执行时间。

从系统兼容性看,CISC大多能实现软件兼容,即高档机包含了低档机的全部指令,并可以加以扩充。

由于RISC简化了指令系统,指令条数少,格式也不同于老机器,所以大多数RISC机不能与老机器兼容。

由于RISC有更强的实用性,因此应该是未来处理器的发展方向。但事实上,当今Intel几乎一统江湖,且早期很多软件都是根据CISC设计的,单纯的RISC将无法兼容。

此外,现代CISC的结构的CPU已经融合了很多的RISC的成分,其性能差距已经越来越小。CISC可以提供更多的功能,这是程序设计所需要的。

4.4.3 CISC和RISC的比较

和CISC相比,RISC的优点主要体现在:

  1. CISC采用微程序控制,其控制存储器占CPU面积的50%以上,而RISC采用组合逻辑控制,其硬布线逻辑只占CPU面积的10%左右,更能利用芯片的面积
  2. RISC更能提高运算速度。RISC的指令数、寻址方式和指令格式种类少,又设有多个通用寄存器,采用流水线技术,所以运算速度回更快,大多指令在一个时钟周期内完成。
  3. RISC便于设计,可以降低成本、提高可靠性。
  4. RISC指令系统简单,因此机器设计周期短;其逻辑简单,出错概率低,有错也易发现,可靠性高。
  5. RISC有利于编译程序代码优化。RISC指令类型少,寻址方式少,使编译程序容易选择更有效的指令和寻址方式,并适当地调整指令顺序,使得代码执行更高效化。

image-20250625200142376

当前文章:计算机组成原理笔记-第四章-指令系统
作者:ikuyo
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇