# 指令系统 指令和数据是应用上的概念,同一个数据可以被解释为数据也可以被解释为指令。同一串二进制代码,可以是指令,也可以是数据,这决定于我们的程序设计。如$1000100111011000$被应用为数据时,它等于$89D8H$,$H$表示是十六进制。当被应用为指令时,它指的是$MOV\,AX,\,BX$。数据和指令的区分方式在第一章已经说明。 ## 指令格式 指如何使用二进制代码表示指令。 操作码+寻址特征码(有多少种寻址方式)+地址码。 ### 指令定义 + 指令(又称机器指令):是指示计算机执行某种操作的命令,是计算机运行的最小功能单位。 + 指令系统是计算机软硬件的界面。指令系统指的是计算机执行的机器指令的集合;这里要注意的是微指令是微程序级命令,属于硬件范畴;伪指令是由若干的机器指令组成的指令序列,属于软件范畴;然而机器指令介于两者之间,处于软硬件的交界面;而机器指令集又称为指令系统;所以回答是指令系统。 + 一台计算机的所有指令的集合构成该机的指令系统,也称为指令集。一台计算机只能执行自己指令系统中的指令,不能执行其他系统的指令。 + 一条指令通常要包括操作码字段($OP$)和地址码字段($A$)两部分。 + 操作码指出该指令要执行什么操作,地址码指出指令要操作的数据的地址。 + 指令地址由$PC$给出。 + 定长指令字结构:所有指令长度相等。执行速度块,控制简单。 + 变长指令字结构:指令长度随指令功能而异。 ### 指令字长 + 固定:指令字长=存储字长。 + 可变:按字节的倍数变化。 + 指令字长取决于: 1. 操作码长度。 2. 操作数地址长度。 3. 操作数地址个数。 ### 地址码 + 一条指令的执行分为三步,如果是两个操作数则四次访存: 1. 取指令。 2. 取两个操作数。 3. 放回结果。 + 访存是指访问内存,$ACC$在运算器中,访问$ACC$不是访问内存。如果数据在$Cache$中则不用访存。 指令类型: + 四地址指令:操作码+操作数$A_1$地址+操作数$A_2$地址+结果地址$A_3$+下指令地址$A_4$。 + $(A_1)OP(A_2)\rightarrow A_3$。 + 三地址指令:操作码+操作数$A_1$地址+操作数$A_2$地址+结果地址$A_3$。 + 下一条指令的寻址靠程序计数器$PC$完成。 + 一共访存四次。 + $(A_1)OP(A_2)\rightarrow A_3$。 + 二地址指令:操作码+操作数$A_1$地址+操作数$A_2$地址。 + 将结果存到操作数$A_1$地址或操作数$A_2$地址中。取指令,取数$A_1$和$A_2$,存放到$A_1$或$A_2$。一共访存四次。$(A_1)OP(A_2)\rightarrow A_1$。 + 若使用累加器$ACC$暂存结果则只用访存三次,取指令,取数$A_1$和$A_2$,暂存到$ACC$。$(A_1)OP(A_2)\rightarrow ACC$。 + 一地址指令(单地址指令):操作码+操作数地址: + 只需要一个操作数的指令操作,如加一、减一、取反、求补等。取出指令、取出数、操作、放回,只需要三次访存。$OP(A)\rightarrow A$。 + 隐含约定的目的地址的双操作数指令,如目的地址为累加器$ACC$的地址。取指令和取数,只需要两次访存。$(ACC)OP(A)\rightarrow ACC$。 + 零地址指令:只有操作码: + 不需要操作数。如空操作、停机、关中断等。 + 堆栈计算机,两个操作数隐含存放在栈顶和次栈顶,计算结果压回栈顶。 + 用硬件资源减少地址码字段的优势: + 扩大指令寻址范围。 + 缩短指令字长。 + 减少访存次数。 ### 操作码 #### 定长操作码 + 指令字的最高位部分分配固定的若干位表示操作码。 + 若有$n$位操作码,则有$2^n$条指令。 + 优点:能简化计算机硬件设计,提高指令译码和识别速度。 + 缺点:指令数量增加时会占用更多固定位,留给表示操作数地址的位数有限。 + 一般这种操作码用于指令字长较长的情况。 #### 拓展操作码 + 拓展操作码是变成操作码实现的一种,让操作码长度随地址码减少而增加。 + 操作码的位数至少为当种指令总条数的二对数。如指令总长度为$32$位,二地址指令有$27$条,则二地址指令的操作码至少有$27=2^4+11=5$位,否则不能操作这么多地址。 + 优点:在指令字长有限的前提下能保持比较丰富的指令种类。 + 缺点:增加了指令译码和分析的难度,使控制器的设计复杂化。 操作码数|OP|A1|A2|A3 :------:|:-:|:-:|:-:|:-: 4|0000|A1|A2|A3 15条|...|...|...|... 4|1110|A1|A2|A3 8|1111|0000|A2|A3 15条|...|...|...|... 8|1111|1110|A2|A3 12|1111|1111|0000|A3 15条|...|...|...|... 12|1111|1111|1110|A3 16|1111|1111|1111|0000 16条|...|...|...|... 16|1111|1111|1111|1111 + 假设指令字长为$16$位,前$4$位为基本操作码字段$OP$,另有$3$个$4$位长的地址字段$A_1$、$A_2$和$A_3$。$4$位基本操作码若全部用于三地址指令,则有$16$条。 + 但至少须将当前位的$1111$留作扩展操作码之用,即三地址指今为$15$条。所以目前就留下来了前$4$位操作数为$1111$的指令,这是只有四位操作码。 + 然后将留下来的指令的操作码位拓展为八位,将$A_1$的位也用作操作码,即将$1111\,0000\,A_2\,A_3$到$1111\,1110\,A_2\,A_3$作为二地址指令,二地址指也为$15$条。同样将$1111\,1111$的指令作为下一个拓展操作码备用。 + 同样将留下来的指令的操作码拓展为十二位,将$A_1A_2$的位用作操作码,即将$1111\,1111\,0000\,A_3$到$1111\,1111\,1110\,A_3$作为一地址指令,一地址指令也为$15$条。同样将$1111\,1111\,1111$的指令作为下一个拓展操作码备用。 + 最后将操作码拓展为十六位,将$A_1A_2A_3$全部作为操作码,即将$1111\,1111\,1111\,0000$到$1111\,1111\,1111\,1111$作为零地址指令,零地址指令为$16$条。 + 在设计扩展操作码指令格式时,必须注意以下两点: 1. 不允许短码是长码的前缀,即短操作码不能与长操个码的前面部分的代码相同,不然无法区分长码和短码。 2. 各指令的操作码一定不能重复。 + 通常情况下,对使用频率较高的指令,分配较短的操作码,对使用频率较低的指令,分配较长的操作码,从而尽可能减少指令译码和分析的时间。 假设地址长度为$n$,上一层留出$m$种状态,则下一层可以拓展出$m\times2^n$种状态。所以也可以形成任意种不同拓展方法。 ### 操作类型 1. 数据传输: 1. $MOV$:寄存器之间的传送。 2. $LOAD$:把存储器的数据放到$CPU$寄存器中。 3. $STORE$:把$CPU$寄存器的数据放到存储器中。 2. 算术逻辑: 1. 算术:加$ADD$、减$SUB$、比较$CMP$、乘$MUL$、除$DIV$、自加一$INC$、自减一$DEC$、求补、浮点运算、十进制运算。 2. 逻辑:与$AND$、或$OR$、非$NOT$、异或$XOR$、位操作、位测试、位清除、位求反。 3. 移位操作: 1. 算术移位。 2. 逻辑移位。 3. 循环移位(带进位和不带进位)。 4. 转移操作:调用指令必须保存下一条指令的地址,当子程序结束时要返回主程序继续执行,而转移指令不用返回执行。 1. 无条件转移:$JMP$。将地址码送入$PC$。 2. 条件转移:$BRANCH$,如$JZ$:结果为$0$;$JO$:结果溢出;$JC$:结果进位。 3. 调用$CALL$和返回$RET$。 4. 陷阱$TRAP$和陷阱指令。(意外事故的中断) 5. 输入输出操作。 ## 指令寻址方式 指令地址码不代表真实地址,这只是个形式地址$A$,只有结合寻址方式寻址才能得到真实地址$EA$。如$EA=(A)$指有效地址是形式地址$A$中保存的数值。 目的是:缩短指令字长、扩大寻址空间、提高编程灵活性。 寻址方式包括指令寻址和数据寻址。 ### 操作数类型与存放方式 #### 操作数类型 + 地址:无符号整数。 + 数字:定点数、浮点数、十进制数。 + 字符:$ASCII$。 + 逻辑数:逻辑运算。 #### 存放方式 + 若一个操作数有多个内存地址对应,则存放地址为: + 大端方式:指令中给出的地址是操作数最高有效字节($MSB$)所在的地址。字地址为高字节地址。如$012345$。 + 小端方式:指令中给出的地址是操作数最低有效字节($LSB$)所在的地址。字地址为低字节地址。如$452301$。 + 按字节地址寻址:给出个字节地址,可以取出长度为一个字节的数据。 + 按字地址寻址:给出个字地址,可以取出长度为一个字的数据。 + 按字节编址:每个字节存储单元都有一个地址编号。每个字中最小的字节地址就是字地址。 + 按字编址:每个字存储单元都有一个地址编号。但是按字编址就无法直接取出字节地址。所以一般只按字节编址。 + 三个字长: 1. 机器字长:$CPU$一次能处理的二进制数据的位数。一般等于内部寄存器的位数。 2. 指令字长:一个指令字中包含二进制代码的位数。若是单字长指令则指令字长等于机器字长,若是半字长指令等则不相等。 3. 存储字长:一个存储单元存储二进制代码的长度。 + 从任意位置开始存储: + 优点:不浪费存储资源。 + 缺点:除了访问一个字节之外,访阿其它任何类型的数据,都可能花费两个存储周期的时间。读写控制比较复杂。 + 从一个存储字的起始位置开始访问,其余位置置空或填充: + 优点:无论访问何种类型的数据,在一个周期内均可完成,读写控制简单。 + 缺点:浪费了宝贵的存储资源。 + 边界对准方式,从地址的整数倍位置开始访问: + 由于不同的机器数据字长不同,每台机器处理的数据字长也不统一,为了便于硬件实现,通常要求多字节的数据在存储器的存放方式能满足“边界对准”的要求。 + 数据存放的起始地址是数据长度(按照编址单位进行计算)的整数倍。 + 本方案是前两个方案的折衷,在一个周期内可以完成存储访问,空间浪费也不太严重。 ### 指令寻址 用于确定要执行的下一条指令的地址。始终由程序计数器$PC$给出。 + 顺序寻址:由$PC$自动加上某个数寻址下一个要操作的指令,注意:不是单纯加一。如果是按字编址就是直接加一,如果是按字节编址则是加一个指令字长。 + 跳跃寻址:由转移指令指出。如执行到操作码为$JMP$,则跳跃到保存的地址码所指向的指令地址进行执行。跳跃地址分为绝对地址(标记符直接得到)和相对地址(相对当前地址的偏移量)。跳跃的结构是当前指令修改$PC$值,所以下一条指令仍通过$PC$给出。可实现程序的条件或无条件转移。注意:程序跳跃后,按新的指令地址开始顺序执行。因此,程序计数器的内容也必须相应改变,以便及时跟踪新的指令地址。 ### 数据寻址 + 用于确定本条指令的数据地址。 + 具体的数据寻址分为: + 主存寻址:数据都存储在主存中 + 隐含寻址。 + 立即寻址。 + 直接寻址。 + 间接寻址。 + 寄存器寻址:数据都存储在寄存器中 + 寄存器寻址。 + 寄存器间接寻址。 + 偏移寻址:都需要加一个偏移量 + 相对寻址。 + 基址寻址。 + 变址寻址。 + 堆栈寻址。 + 此时指令就需要由:操作码$OP$+寻址特征(寻址方式)+形式地址$A$组成。形式地址不再是实际的地址,而是指令字中的地址。 + 有效地址$EA$就是通过寻址特征和形式地址进行运算得到的。 + 无论是有多少个地址,都必须给出成对的寻址特征和形式地址。 + 主存地址不能为负。 由于访存慢于访问寄存器,所以寄存器访问快于直接访问。 如果采用变长指令码格式,由于要表示一定范围的立即数,立即数的指令通常需要较多的二进制位,取指时,可能需要不止一次的读内存来完成取指,因此采用变长指令码格式的时候,寄存器寻址方式的执行速度更快。但是如果采用定长指令码格式,那就是立即寻址更快了。 #### 隐含寻址 + 不是明显地给出操作数的地址,而是在指令中隐含着操作数的地址。 + 如单地址指令,隐含约定另一个操作数的目的地址为累加器$ACC$的地址。 + 优点:有利于缩短指令字长。 + 缺点:需增加存储操作数或隐含地址的硬件。 #### 立即寻址 + 形式地址指出的不是数据地址,而是数据本身,称为立即数,采用补码形式存储。 + 地址形式为:操作码$OP+\#+$立即数。$\#$即表示立即寻址特征。 + 立即寻址的指令执行: 1. 取指令,访存$1$次。 2. 执行指令,直接取指令操作数,访存$0$次。 3. 暂不考虑如何存结果。 4. 共访存$1$次。 + 优点:指令执行阶段不访问主存,指令执行时间最短。 + 缺点: + 形式地址$A$的位数限制了立即数的范围。 + 如$A$的位数为$n$,且立即数采用补码时,可表示的数据范围为$[-2^{n-1},2^{n-1}-1]$。 #### 直接寻址 + 指令字中的形式地址$A$就是操作数的真实地址$EA$,即$EA=A$。 + 直接寻址的指令执行: 1. 取指令,访存$1$次。 2. 执行指令,取操作数,访存$1$次。 3. 暂不考虑如何存结果。 4. 共访存$2$次。 + 优点:简单,指令执行阶段仅访问一次主存,不需专门计算操作数的地址。 + 缺点: + 形式地址$A$的位数决定了该指令操作数的寻址范围。 + 操作数的地址不易修改。 #### 间接寻址 + 指令的地址字段给出的形式地址不是操作数的真正地址,而是操作数有效地址所在的存储单元的地址,也就是操作数地址的地址,即$EA=(A)$。 + 间接寻址的指令执行: 1. 取指令,访存$1$次。 2. 执行指令,取操作数地址,取操作数,访存$2$次。 3. 暂不考虑如何存结果。 4. 共访存$3$次。 + 若间址次数为$n$,则访问时间为$n+2$。 + 优点: + 可扩大寻址范围(有效地址$EA$的位数大于形式地址$A$的位数)。 + 便于编制程序(用间接寻址可以方便地完成子程序返回)。 + 缺点:指令在执行阶段要多次访存(一次间址需两次访存,多次寻址需根据存储字的最高位确定几次访存)。由于访问速度过慢,所以一般都不使用。 #### 寄存器寻址 + 在指令字中直接给出操作数所在的寄存器编号,即$EA=R_i$,其操作数在由$R_i$所指的寄存器内。 + 类似于直接寻址,只是数据在主存中变为在寄存器中。寄存器编号就是形式地址。 + 寄存器寻址的指令执行: 1. 取指令,访存$1$次。 2. 执行指令,由于操作数在寄存器中,寄存器集成在$CPU$中,所以不需要访问主存,访存$0$次。 3. 暂不考虑如何存结果。 4. 共访存$1$次。 + 优点: + 指令在执行阶段不访问主存,只访问寄存器,指令字短且执行速度快。 + 支持向量/矩阵运算。 + 缺点: + 寄存器价格昂贵。 + 计算机中寄存器个数有限。 #### 寄存器间接寻址 + 寄存器$R_i$中给出的不是一个操作数,而是操作数所在主存单元的地址,即$EA=(R_i)$。 + 寄存器间接寻址的指令执行: 1. 取指令,访存$1$次。 2. 执行指令,先访问寄存器获得地址,再根据地址在主存中访问,访存$1$次。 3. 暂不考虑如何存结果。 4. 共访存$2$次。 + 优点:与一般间接寻址相比速度更快。 + 缺点:指令的执行阶段需要访问主存(因为操作数在主存中)。 #### 相对寻址 + 把程序计数器$PC$的内容加上指令格式中的形式地址$A$而形成操作数的有效地址,即$EA=(PC)+A$,其中$A$是相对于当前指令地址的位移量,可正可负,补码表示。 + 由于取指时$PC$会默认加上一个指令字长($(PC)+1\rightarrow PC$),所以相对寻址所提供的相对地址是以下条指令在内存中首地址为基准位置的偏移量。 + 优点: + 操作数的地址不是固定的,它随着$PC$值的变化而变化,并且与指令地址之间总是相差一个固定值,因此便于程序浮动。 + 相对寻址广泛应用于转移指令。 + 所以转移指令基本上使用相对寻址方式。无条件转移指令会在指令周期中对$PC$进行两次修改操作,一起是取指周期后自动加一,一个是执行周期后转移修改。有条件指令如果条件满足则修改两次,条件不满足则只用修改一次。 #### 基址寻址 + 将$CPU$中基址寄存器($BR$)的内容加上指令格式中的形式地址$A$,而形成操作数的有效地址,即$EA=(BR)+A$。 + 分为隐式基址寄存器(专用寄存器$BR$)和显式基址寄存器(需要指定某通用寄存器为基址寄存器)。 + 基址寄存器是面向操作系统的,其内容由操作系统或管理程序确定,用户不可改变。在程序执行过程中,基址寄存器的内容不变(作为基地址),形式地址$A$可变(作为偏移量,使用补码表示)。 + 当采用通用寄存器作为基址寄存器时,可由用户决定哪个寄存器作为基址寄存器,但其内容仍由操作系统确定。 + 优点: + 可扩大寻址范围(基址寄存器的位数大于形式地址$A$的位数)。 + 用户不必考虑自己的程序存于主存的哪一空间区域,故有利于多道程序设计,以及可用于编制浮动程序。 #### 变址寻址 + 有效地址$EA$等于指令字中的形式地址$A$与变址寄存器$IX$的内容相加之和,即$EA=(IX)+A$。 + 其中$IX$为变址寄存器(专用),也可用通用寄存器作为变址寄存器。 + 与基址寄存器不同的是,变址寄存器是面向用户的,在程序执行过程中,变址寄存器的内容可由用户改变(作为偏移量),形式地址$A$不变(作为基地址)。 + 优点: + 可扩大寻址范围(变址寄存器的位数大于形式地址$A$的位数)。 + 在数组处理过程中,可设定$A$为数组的首地址,不断改变变址寄存器$IX$的内容,便可很容易形成数组中任一数据的地址,特别适合编制循环程序。 + 变址寻址与基址寻址配合使用:$EA=A+(BR)+(IX)$。 + 变址寻址与间接寻址配合使用: + 先变址后间址,$EA=(A+(IX))$。 + 先间址后变址,$EA=(A)+(IX)$。 #### 堆栈寻址 + 操作数存放在堆栈中,隐含使用堆栈指针($SP$)作为操作数地址。 + 堆栈是存储器(或专用寄存器组)中一块特定的按“后进先出($LIFO$)”原则管理的存储区,该存储区中被读/写单元的地址是用一个特定的寄存器给出的,该寄存器称为堆栈指针($SP$)。 + 寄存器做堆栈就是硬堆栈,成本高,主存做堆栈就是软堆栈。 默认任何寻址方式都要加上一次取指访存一次: 寻址方式|有效地址|访存次数 :------:|:------:|:------: 隐含寻址|程序指定|0 立即寻址|A即是操作数|0 直接寻址|EA=A|1 一次间接寻址|EA=(A)|2 寄存器寻址|EA=R|0 寄存器间接一次寻址|EA=(R)|1 相对寻址|EA=(PC)+A|1 基址寻址|EA=(BR)+A|1 变址寻址|EA=(IX)+A|1 + 立即寻址操作数获取便捷,通常用于给寄存器赋初值。 + 直接寻址相对于立即寻址,缩短了指令长度。 + 间接寻址扩大了寻址范围,便于编制程序,易于完成子程序返回。 + 寄存器寻址的指令字较短,指令执行速度较快。 + 寄存器间接寻址扩大了寻址范围。 + 基址寻址扩大了操作数寻址范围,适用于多道程序设计,常用于为程序或数据分配存储空间。 + 变址寻址主要用于处理数组问题,适合编制循环程序。 + 相对寻址用于控制程序的执行顺序、转移等。 + 基址寻址和变址寻址的区别: + 两种方式有效地址的形成都是寄存器内容+偏移地址。 + 在基址寻址中,程序员操作的是偏移地址,基址寄存器的内容由操作系统控制,在执行过程中是动态调整的。偏移量较短。 + 在变址寻址中,程序员操作的是变址寄存器,偏移地址是固定不变的。偏移量较大。 ## 指令集计算机 ### 复杂指令集计算机 + 即$Complex\,Instruction\,Set\,Computer$,$CISC$。 + 一条指令完成一个复杂的基本功能。 + 代表:$x86$架构,主要用于笔记本和台式机。 + $80-20$规律:典型程序中$80\%$的语句仅仅使用处理机$20\%$的指令。 ### 精简指令集计算机 + 即$Reduced\,Innstruction\,Set\,Computer$,$RISC$。 + 一条指令完成一个基本动作,多条指令组合完成一个复杂的基本功能。 + 代表:$ARM$架构,主要用于手机和平板。 对比项目\类别|CISC|RISC :----------:|:---:|:--: 指令系统|复杂,庞大|简单,精简 指令数目|一般大于200条|一般小于100条 指令字长|不固定|定长 可访存指令|不加限制|只有Load/Store指令 各种指令执行时间|相差较大|绝大多数在一个周期内完成 各种指令使用频度|相差很大|大都比较常用 通用寄存器数量|较少|多 目标代码|难以用优化编译生成高效的目标代码程序|采用优化的编译程序,生成代码较为高效 控制方式|微程序控制|组合逻辑(硬布线)控制 利用率|低|高 指令流水线|可以通过一定方式实现|必须实现 兼容性|强|弱 速度|慢|快