第六章指令系统,寻址方式

用高级语言或者是汇编语言编写的程序,如果要在计算机上执行,必须要利用编译程序或者是汇编程序把高级语言编写的程序,指令,或者是汇编指令变成由0,1代码组成的机器指令,才能够在计算机中由计算机的硬件按序进行执行。

机器指令

机器指令;计算机系统的CPU能够直接识别并且执行的操作命令。一个处理器能够执行的所有的机器指令构成的集合,我们称之为指令集。指令集就是计算机系统软件和硬件的交界面。 软件通过指令系统告诉计算机硬件做什么操作,计算机硬件通过指令系统把执行结果和硬件状态返回给软件。

机器指令如何进行设计?系统当中要有哪些类型的指令、每种类型的指令具体要做什么操作,这些问题都是计算机体系结构设计人员要考虑的内容,而计算机组成设计人员要做的事情就是将计算机系统结构设计师设计的硬件指令用逻辑的方法进行实现。为了讲解控制器如何实现指令,首先要了解指令的属性。

一、指令

(1)指令的格式

操作码:指令具体做的是什么操作。操作码不仅指出要做什么操作,有很多机器的指令集中,操作码还要指令要对什么样的数据做操作,操作数的寻址方式。操作码分为两种:

长度固定的操作码:用于指令字长较长的情况,为了译码过程方便,译码电路简单。

长度可变的操作码:操作码分散在指令字的不同字段中。上图中,操作码字段内容看似是放在一起的,实际上这只是一种逻辑表示方式,在实际中操作码字段可以分开,放在不同的位置进行表示。为了支持操作码长度可变,我们要采用扩展操作码技术来扩展操作码的长度。

(2)扩展操作码技术

这里讲解通过保留操作码一个编码的码点作为扩展的标志,对操作码进行扩展。

要增加操作码的长度,如果指令的长度是不变的,那么实际上要减少地址码的位数。
首先假设指令格式如图所示:

OP:表示操作码部分。
A1,A2,A3:地址码部分

假设操作码和每一个地址码字段长度均为4位。如果不采用扩展操作码的方式,从上图中可知,OP为四位,那这台机器最多只能由16条指令,16条指令无法满足计算机编写复杂程序的要求。操作码的个数就是指令的个数。如果给这样的指令采用扩展操作码的方式进行编码,需要通过减少地址码的个数来增加指令的条数。

具体的扩展方式如下:

首先四位操作码编码:从全0到全1共16个点,这16个点,其中从0000 – 1110的15个点作为操作码长度为四位的操作码指令的编码。剩下的1111作为扩展标志。

8位操作码: 如果前四位编码为1111,表明这条指令操作码部分长度至少为8为,1111就是标志,我们减少了一个地址码字段,把这个地址码字段变成了操作码字段,同样编码过程还是从0000 – 1110,共15个点,保留一个点:1111 1111 作为扩展字段继续进行扩展。此时地址只剩下了A2和A3两段。

12位操作码:继续减少地址码个数,如果地址指令前面是1111 1111 说明这种编码方式中,指令的操作码至少是12位。

16位操作码:把所有地址码部分都变成操作码,即16位操作码。

对于这种保留码点的扩展方式,要让计算机能够识别出现在读取的指令他的操作码到底是多少位,为了让计算机能够识别操作码的位数,那么在操作码的扩展过程中,必须要遵守短操作码一定不能是长操作码的前缀。如果短操作码是长操作码的前缀,计算机在指令译码的时候,就很难识别这两条指令。

在整个扩展过程中,可以看出如下规律:

在指令执行的过程中,经常出现的高频指令采用短操作码表示,不经常出现的指令用长操作码表示。

二、指令格式

这里对指令格式进行进一步细致划分:

(1)四地址指令

四地址指令格式如下图所示:

实际的操作是A1和A2进行操作,把结果保存在A3中,执行完当前指令后,通过A4去取下一条指令。上图中的指令做的操作可表示如下:

如果地址字段均指示主存的地址,则这个操作共需访问4次存储器(取指令一次,取两个操作数两次,存放结果一次)。

设指令字长为32位,操作码固定为8位,并且每个地址码的长度相同,则每个地址码的长度均为6位,如下图:

得到寻址范围如下 = 2^6 = 64b。这个寻址范围非常小,可访问的内存空间非常小。如果访问寄存器还可以,如果访问内存,这样的指令几乎是不可用的。

如何通过减少地址码的个数来增加地址码的长度,从而增加寻址范围,在这条指令中,A4指明了下一条指令的地址,实际在现代计算机中,我们采用PC寄存器表示下一条要执行的指令的地址。如果我们采用PC来代表A4,地址码就可以减少一个,指令就由四地址指令变成了三地址指令。

(2)三地址指令

格式如下:

如果指令长度32位保持不变,那每个地址码的长度就会增加,如下:

而这条指令所表示的操作为:A1和A2中的操作数做相应的运算,将结果保存在A3中。

这种操作同样需要四次访存操作:取指令一次,取两个操作数两次,存放结果一次。

由于地址码个数的减少,每个地址码长度增加:寻址范围为:2^8 = 256b。

通过上面,我们使用PC寄存器代替了地址码A4,减少了地址码的个数,增加地址码长度,从而增加寻址范围,我们可以继续减少地址码的个数来增加地址码的长度,如:我们使用A1或者A2来代替A3。把运算结果不保存在A3存储单元,而是保存到某一个参加运算的原操作数地址单元中,这样就可以再次减少一个地址码。地址码由三地址变成了两地址。

(3)二地址指令

两地址指令格式如下:

他表示,内存单元A1地址中的内容和A2地址中的内容,做OP规定的操作,把结果保存在A1中,或者把结果保存在A2中,如下:

 

上面这种操作的访存次数依然是4次:取指令一次,取两个操作数两次,存放结果一次。

地址码由三个变成了两个,每个地址码字段的长度增加,寻址范围 = 2^12 = 4K

如果我们把操作结果不是保存在A1或者A2中,而是将其保存在一个指定的寄存器中,我们就可以进一步减少访存次数,如将操作结果保存在ACC,这样就只有3次访存。

如果我们把某一个操作数以隐含指定的方式就保存在ACC中,要进行某个操作,一个操作数必须在ACC中,另外一个操作数在内存中,ACC内容和内存内容做操作,结果保存在ACC中,这样就可以再一次减少地址码个数,从两地址变成单地址。这个地址只需要指明参加运算的出了ACC之外的另一个操作数在内存单元中的地址即可。

(4)一地址指令

一地址指令格式如下:

 

他的操作为,A1内存单元中的内容和ACC寄存器中的内存做OP指定的操作,结果保存在ACC中。操作表示如下:

这种操作需要两次访存:取指令,取A1操作数。

地址码减少为1个:寻址范围 = 2^24 = 16M

(5)零地址指令

即无地址码。如:对ACC中数据进行清零,取反,对ACC中某一位数据进行特定的操作,或者判断ACC中的数据是否为0,是否全1等。还有堆栈类的指令,如在堆栈型计算机中,加法操作只需要操作码即可,不需要操作数,因为ADD表示栈顶的两个数做加法,把结果再一次保存在栈顶。

对前面的分析,可以看出当用一些硬件资源代替指令字中的地址码字段后:
1、可扩大指令的寻址范围
2、可缩短指令字长
3、可减少访存次数

(6)当指令的地址字段为寄存器时

 

上面的R1,R2,R3是寄存器的编码,因为寄存器的个数在计算机中是非常有限的,如,如果有16个寄存器,那么只需要四位长度的二进制数就足够了,这样:
1、可缩短指令字长
2、指令执行阶段不访存

三、指令字长

根据前面的分析,可以很容易看出指令字长取决于:

 

指令字长固定的情况下:指令字长 = 存储字长

指令字长可变:指令字长将会按字节整数倍变化

操作数类型和操作类型

(一)操作数的类型

操作数:指令要处理的数据。主要分为以下四种类型:
1、地址:在跳转指令中,操作数部分就是地址或者是相对地址,如果是绝对地址,这个地址就是一个无符号数。如果是相对寻址,他就是一个有符号数。
2、数字:定点数,浮点数,十进制数。
3、字符:ASCII
4、逻辑数:逻辑操作。

(二)数据在存储器中的存放方式

如,12345678H在计算机中的存放方式。

大端方式:

小端方式:

下面看看,按照字节编址,数据在计算机中是如何进行存放的,这里假设计算机存储字长64位,机器字长32位。

存储字长64位,说明CPU在访存过程中,一次访存最多可以拿到一个长度为64位的数据。若机器字长32位,那64位的数据就是一个双字。

(1)从任意位置开始

存放情况如下图所示:

 

半字 :一个字长为32位4字节,半字指的是16位双字节。

对于这种存放方式:

优点:对内存空间的利用非常好,不会浪费内存资源。

缺点:除了访问一个字节之外,访问其它任何类型的数据, 都可能花费两个存储周期的时间,才能将数据读出来或者写进去。读写控制比较复杂,因为要判断数据的长度,数据是不是跨存储字进行存取。计算机中内存是计算机运算速度的瓶颈,如果我们采用这种方式进行存储数据,访存的速度不会太快,那如何保证任何类型的数据在一个存储周期都能将其取出来呢。下面看第二种方式。

(2)从一个存储字的起始位置开始访问

每次存取数据不管是什么类型的,都从一个存储字的开始位置进行访问。如果是这样,则存放如下:

这样存储,任何一种类型的数据,都能保证在一个内存周期完成读写操作,并且读写控制非常简单。但是,浪费了宝贵的存储资源,图中的黑色部分都是被浪费的内存资源。

那有没有一种方式,即能够减少资源的浪费,有能够保证任何一种类型的数据(长度都要小于存储字长),在一个主存周期中都能够访问。下面看第三种方式。

(3)边界对准方式。

从地址的整数倍位置开始进行访问,地址的整数倍开始进行访问,具体如下图所示:

如上图中,一个字节进行存储的时候,可以存储在任何一个内存单元中。如果是一个双字,我们假设的是双字是8个字节,也就是占用了8个编址单位,对他进行存储的时候,起始地址从地址编码是8的倍数的存储单元开始,在上图的内存结构中,就是每次从一个存储字的起始地址进行存放。如果是一个半字,半字的字长为两个字节,那存储的时候,把他放在地址是偶数的起始地址开始存放。如果是一个字,长度为四个字节,那就从地址的地址编码为4的整数倍的位置开始存放。

这种方式,能够保证任何一种类型的数据(长度都要小于存储字长),进行访问的话,一个存储周期就能够得到或者是写入要访问的数据。这种方式是前两种方式的折中,数据存放的起始位置是数据长度的整数倍。这个数据长度用编址单位进行计算,如上图中的编址单位是字节,那就看存取的数据长度为多少个字节,那他的起始地址就是字节数的整数倍。这种方式既保证了在一个存取周期中能够完成数据的访问,也减少了存储空间的浪费。

(三)操作类型

(1)数据传送

在不同的存储介质中进行数据的传送。比如源和目的之间:

(2)算数逻辑操作

常见操作如下:

8086中常见的指令如下:

(3)移位操作

算术移位,逻辑移位,循环移位(带进位和不带进位)

(4)转移

1、无条件转移

JMP,执行到这样的指令,直接转移到目标地址。

2、条件转移

在一些计算机中称之为分支指令,如:

3、调用和返回

1、遇到调用指令,调用子程序1,程序的控制流就会发生转移,在子程序1上进行指令。
2、在子程序1上执行过程中,遇到调用指令,调用子程序2,程序的控制流发生跳转,执行子程序2。
3、开始执行子程序2
4、遇到return指令返回。在哪里就行的调用就会返回到什么地方。
5、返回到子程序1继续执行。
6、执行过程中遇到调用指令,调用子程序2,程序控制流跳转到子程序2.
7、开始执行子程序2
8、遇到return指令返回。
9、返回开始执行子程序1
10、子程序1执行结束以后,返回到主程序
11、主程序继续执行
12、主程序执行结束

4、陷阱(Trap)与陷阱指令

通常情况下陷指的是意外事故的中断。

一般不提供给用户直接使用的陷阱指令:
在执行一条指令过程中,这条指令本身执行过程出现了意外,就要执行陷进指令,比如操作码非法,操作数访问越界,或者除法操作中除数为0,都会引发陷进指令,所以通常情况下陷进指令不是直接提供供用户直接使用的。是在出现问题的时候,出现异常事故的时候,由CPU自动执行的,所以他并不是指令集当中的一些指令,它由硬件自动执行,这种指令称之为隐指令。

提供给用户使用的陷阱指令:
也有一些系统中提供用户使用的陷阱指令供用户进行使用,比如进行程序调试。

5、输入输出

并不是每一种指令集中都有输入输出指令,如果IO端口编址空间作为内存编址空间的一部分,那这个指令集就不需要输入输出指令,直接用访存内存的指令就可以对外部设备进行输入输出。如果外部设备或者端口有自己的独立地址空间单独编址,就需要单独的输入输出指令对外部设备进行访问。

输入:将外部设备端口中的内容传输到CPU的寄存器中:

输出:将CPU寄存器的内容输出到设备的端口中。

寻址方式

如何找到指令或者数据的地址:

从指令和数据角度吧寻址方式划分为两类:即指令的寻址和指令中数据的寻址。

(一)指令寻址

(1)顺序寻址

取完一条指令,顺序的取下一条指令,由于指令的地址是保存在PC中的,去完当前的指令,为了取下一条指令,就要把PC内容加1,然后送到PC中。

上面的加1,并不是在任何一个计算机或者指令集中都是加1,实际上1是比较复杂的。如果内存单元的编址单位是字节,每条指令长度是32位4字节,那么顺序寻址的时候,每次加的值就是4,如果指令的长度是64字节,那PC加8,如果指令是可变程度的,那么加1操作就更加复杂。

(2)跳跃寻址

由转移指令指出下一条指令的地址。

如上图中,程序由多条指令构成,假设指令的地址从0开始,在开始执行程序的时候,PC的值被置0,将LDA指令取出后,PC会自动加1,为取下一条指令做准备,这就是顺序寻址。地址为3的地址十一条无条件的跳转指令,他的目标地址是1,执行完这条指令之后,要找到下一条指令地址,采用的寻址方式就是跳跃寻址,跳到7取出STA指令,之后又是顺序寻址。

(二)指令中数据的寻址

数据寻址的方式比较多,下面是指令的格式:

假设给出的是单地址指令。

寻址特征:标识采用什么样的寻址方式能够找到操作数所在的地址。

地址字段中给出的是形式地址,形式地址就是在操作码中给出的地址,形式地址并不是要找到的数据所在的存储单元或者是寄存器的真实地址。要找到真实地址,需要使用寻址特征和形式地址进行一定的运算或者是转换才能得到真正的有效地址。

有效地址:操作数的真实地址。

为了讲解方便,简化案例,约定:指令字长 = 存储字长 = 机器字长。

(1)立即寻址

形式地址A就是操作数,这个数据直接参与操作码指定的运算。如果采用立即寻址,上面指令的格式就变成如下图所示:

#:立即寻址的特征,在译码过程中,如果发现寻址特征位是#,表示后面的操作数是立即数。立即数可以是正数,也可以是负数,用补码进行表示。

如果采用立即寻址,操作数在取指令过程中实际已经被取入到了CPU中,所以在指令执行过程中,不需要再次访存。那么A到底取多长或者A到底占用多少位二进制位比较合适和实际情况有关。

(2)直接寻址

即有效地址 = 形式地址,具体表示如下:

 

EA是有效地址。A给出的就是操作数所在的内存单元的地址,由形式地址直接给出。如:

上图中表示的是,用LDA指令将内存单元中的数据取到CPU,保存在ACC寄存器中,图中蓝色部分是寻址特征,表示后面的操作数采用的是直接寻址方式,直接给出的就是内存单元的物理地址,我们就可以通过这个给出的地址去访存这个内存单元,把操作数取出送入ACC寄存器。

如果假设地址码中只有一个地址字段,这样的指令在指令执行过程中,需要访问一次存储器。
A的位数决定了操作数的寻址范围,如果A过短的话寻址范围会非常小。

这种方式的地址码字段非常难以修改,编程困难。比如我们要对一个数组的每个值都加1,用循环去做,这样很难通过这个命令去取出数组的每一个数,除非有另外一条指令,能够把LOAD指令从内存中取出,并且能够修改地址码部分,这样我们才能用循环完成数组每个值加1的操作。

(3)隐含寻址

把寻址方式隐藏到操作码中,或者是参与运算某一个数据由操作码直接给出。如下:

 

上图中的ADD指令,蓝色部分代表寻址特征,这个寻址特征值得是后面的地址码的寻址方式,这个加法指令需要两个操作数参与运算,并且要把加法结果进行保存,指令当中给出了一个操作数,另外一个操作采用的就是隐含寻址的方式,这个操作数就隐含在ACC寄存器中。假设寻址特征位给出的是直接寻址,由这个地址只接到指定的内存单元中取出参加操作的一个操作数,另外一个操作数保存在ACC中,ACC中的数据和内存中的数据做加法操作,把计算的结果放入一个暂存器中,再把结果送入ACC。

在8086中:

采用隐含寻址的方式,尽管是两个操作数参与运算,但是其中的一个操作数我们用隐含的方式给出,所以在指令中我们只需要给出一个操作数地址,这样减少一个地址码字段,可以缩短指令长度。

(4)间接寻址

指操作数的地址保存在某一个内存单元中,指令当中出现的是这个内存单元的地址,有效地址在给定的内存单元中进行保存。表示为:

 一次间址:

 

如上图中,蓝色部分依旧是寻址特征,这个寻址特征标示了后面的操作数采用的是间接寻址。

一次间址操作过程:

图中指令真正的操作数所在的地址,保存在形式地址A所代表的内存单元中,要想取得真正的操作数,需要先通过形式地址A取得真正的有效地址,然后利用有效地址再次访问内存得到的才是真正的操作数。

在执行指令阶段需要访问两次内存,第一次取出有效地址,第二次根据有效地址取出真正的操作数。

这种方式可以扩大寻址范围,尽管A的字段可能比较短,寻址范围比较小,但是我们可以把EA字段放的很长,从而扩大寻址范围。

方便编写程序,可以通过修改EA从而修改真正的操作数所在的地址,这样指令中的形式地址不需要任何变化,操作数的真正地址就会发生变化。

多次间址:

上图中,A依旧不是操作数的地址,表示的是能够找到操作数地址必须要经过的中间过程的地址。
操作过程如下:

先通过A找到地址A1,这个A1并不是我们要的操作数的地址,前面标识1指的是目前这个地址依然是一个间接地址,所以我们需要再次寻址,可能会经过很多次,一直到我们找到的地址前面的标识为0,这个单元中保存的才是有效地址,我们还要利用这个有效地址再次访存,把操作数取出来,参与指定的操作。

间接寻址举例:

上图中执行流程如下:

程序在执行过程中,需要调用子程序,我们用间接寻址来保存程序断点。子程序的最后一条指令是跳转指令,这条跳转指令的寻址方式采用的是简介寻址。

1、主程序在执行过程中,如果80是一个调用子程序的调用指令,那么81就是程序的断点,为了能够返回,程序的断点必须要进行保存,我们把81保存在A表示的地址单元中,这样A地址单元中保存的内容就是81.执行完调用指令,程序的控制流进入到子程序执行。

2、子程序开始执行。

3、子程序执行完,进行返回的时候执行JMP指令,并不是直接跳转到A这个地址,而是跳转到A这个地址单元中保存的81地址。

4、返回到程序断点81开始执行。

5、如果地址201依然是一个调用子程序的指令,断点就是202,我们把断点202保存在A的地址单元中,A是地址,这个地址中保存的是 202,程序控制流转入子程序执行。

6、执行到子程序的最后一条JMP指令,这条指令跳转采用的依旧是简介寻址,跳转并不是跳转到地址A,而是跳转到A地址单元中保存的数据202指示的地址。

(5)寄存器寻址

指令的有效地址就是寄存器的编号,表示为:

寄存器寻址过程如图所示:

指令中的寻址特征指出指令的寻址方式为寄存器寻址,Ri是寄存器编号,参加运算的数据保存在给定编号的寄存器中,因此CPU要在给定的编号的寄存器中取出数据,对数据做相应的操作。

在指令的执行阶段不需要访存,只访问寄存器,执行速度块。

寄存器个数有限,所以指令中的地址码较短,可以缩短指令的字长。

(6)寄存器间接寻址

在这种寻址方式中,操作数保存在内存单元中的,操作数的地址保存在寄存器中,表示为:

寻址过程如下:

图中蓝色部分表示为寄存器间接寻址特征。根据这个特征首先访问相应的寄存器获取在内存中真正的操作数的有效地址。再根据有效地址访问内存,从内存中取出真正的操作数。

这种方式,有效地址在寄存器中,操作数在存储器中,指令执行阶段需要一次访存。

这种方式非常便于循环程序的编写,我们需要修改操作数的地址,只需要直接修改寄存器中的内容就可以。

(7)基址寻址

在CPU中设置专用的寄存器作为基址寄存器

参加操作的操作数的有效地址,是基址寄存器中保存的内容加上形式地址。表示为:

基址寻址过程如下:

蓝色部分表示寻址特征为基址寻址,寻址过程需要利用一个加法器,BR的内容和A的内容相加,得到的地址是操作数在内存单元中的有效地址,用这个地址去访问内存单元就可以得到操作数。

利用这种方式可以扩大寻址范围,因为BR是基址,他给出了寻址的起点,A是一个偏移量,通过调整BR就可以扩大寻址范围。

有利用多道程序设计,多道程序分时进行执行的时候,程序的起始地址可以把他放在BR中,在执行过程中,动态形成操作数的地址,这种方式称之为程序的动态定位。

BR内容一般由操作系统或者管理程序确定,用户不能修改BR内容,如果要修改地址,只能修改形式地址。在多道程序设计中通过动态修改指令中的形式地址进行程序的装入或者是内存中的定位。

采用通用寄存器作为基址寄存器
寻址过程如下:

在寻址过程中,操作和代用专用的基址寄存器过程类似,依然需要加法器。

可以由用户指定使用那个寄存器作为基址寄存器,但是程序执行过程中,基址寄存器的内容依然是由操作系统或管理程序决定,用户不能修改指定的基址寄存器的内容,形式地址A是可以改变的。

(8)变址寻址

在变址寻址中,变址寄存器内容加上形式地址内容等于有效地址。表示如下:

寻址过程如下:

和基址寻址类似,在形成有效地址过程中,我们要利用一个加法器把A的内容和编址寄存器的内容相加,得到的结果就是操作数所在内存单元的有效地址。用这个有效地址访问内存单元就能够获得操作数。

这种方式可以扩大寻址范围。

IX内容可以修改,在程序执行过程中A的值是固定的,IX的值可以修改。

这种方式便于对数组的操作,如果我们对数组进行循环操作,A表示数组的起始地址,IX可以作为数组的下表来做循环操作。

举例:使用变址寻址对数组进行操作。如果数组首地址为 D,求 N 个数的平均值。

首先看采用直接寻址方式,过程如下:

由上面可以看出,采用直接寻址,共需要N + 2条指令

下面是变址寻址:

变址寻址方式只需要8条指令就能够完成这个功能,大大减少了指令在内存中的占用空间。

(9)相对寻址

相对寻址,相对的是当前的PC值,所以有效地址等于PC值加上形式地址,表示如下:

相对寻址过程如下:

 

 

当前指令的地址是1000,当前的PC值就是1000,如果这条指令的寻址特征为相对寻址,PC的值和A的值做加法,得到内存单元中操作数的有效地址。A的值就是操作数相对于PC值的偏移量。

A的位数决定的相对寻址的寻址范围。

这种有利于程序浮动,所谓程序浮动,指的是程序在内存单元中存储位置发生变化。

这种方式非常广泛的应用于转移指令。

相对寻址举例:这个例子是前面介绍过的利用变址寻址方式求数组各个值的平均值。

这个程序中,CPX指令判断加和次数是否达到了N,如果没有,就使用BNE指令跳转到M。而M的值会随着程序所在存储空间位置的不同而发生变化。但是当前这条BNE指令和M之间的相对位移是固定的,如果我们采用相对寻址的方式,*是相对寻址的特征位,我们把当前的PC值减3,我们就可以得到这条跳转指令如果跳转成功的话,那么下一条指令他的地址。所以可以把BNE M,指令直接改成BNE *-3,所以下一条指令的有效地址为:

在看一个按字节寻址的相对寻址例子:

图中,2000H位置的指令JMP *+8 采用的是相对寻址,如果指令的字长是两个字节。如果JMP指令执行完成,那么下一条指令所在地址就应该是当前的PC加8,结果就是2008H。但是机器实际执行的时候并非如此,在现在的计算机中,一条指令只要取完,在他还没有执行或者是还没有执行完,PC的值就已经发生了修改,也就是说2000H里面包含的这条JMP指令只要被取出,还没有执行,PC的值就已经被修改为2002H,因此要想跳转到正确的目标地址,我们必须对JMP中的偏移地址进行修改,修改为JMP * 06,即这条指令的位移量应该是06,如下图所示:

(10)堆栈寻址

堆栈的特点:

堆栈是一个先进后出的队列,堆栈有两种类型:
硬堆栈: 在堆栈型的计算机中,一般都采用硬堆栈方式,也就是说由多个寄存器作为栈顶,栈底部分在内存中。进行操作的话,如加法操作,将两个操作数均来自于栈顶,操作完成后将操作结果保存在栈顶。
软堆栈: 指定的一段内存空间。

栈顶位置由SP寄存器指出。通常情况下,堆栈的栈底是地址最高的,栈顶是低地址。如果我们进行进栈操作,SP – 1,栈顶向上升起,如果是出栈操作,SP + 1,栈顶进行下移。

入栈操作:
SP开始等于2000H,表示当前的堆栈的栈顶在2000H位置。
SP = SP – 1 = 1FFFH,前提是压入堆栈的数据正好占用了一个编址单位。

出栈操作:
SP开始等于1FFFH,表示当前的堆栈的栈顶在1FFFH位置。
SP = SP + 1 = 2000H。

SP的修改和主存编址方法有关。

1、按字编址:

2、按字节编址:

存储字长为16位时:

存储字长为32位时:

 

指令格式举例

(一) 设计指令格式应考虑各种因素

(1)指令系统的兼容性。

如计算机发展过程中,新设计的指令集,指令数量可能会比较少,用户利用这个指令集编写了大量程序完成一些功能,随着计算机发展要设计新的处理器新的指令集,但是在设计新的指令集的时候,必须要考虑在已有的计算机上已经跑起来的应用,为旧的机器设计的应用程序,最好不经过任何修改就能够在新的指令集机器上顺利执行。否则,用户购入新的机器需要重新设计应用编写程序,对用户来说需要付出很大的代价。对用户来说是不可接受的,对CPU设计人员来说,如果不能和旧的指令集兼容,则新的处理器就很难占领市场。

(2)其他因素

 

 

(二)IMB360指令格式

共有五中指令格式

(1)RR格式

两个寄存器参加操作,结果保存在其中的一个寄存器中,指令的长度是16位,操作码长度是8位,IBM360指令的操作码长度都是8位。

(2)RX格式

寄存器和存储器,其中X是编址寄存器, B是基址寄存器,从寄存器所占位数看公有16个寄存器,D是偏移地址,采用基址加编址方式进行寻址。

(3)RS格式

这种是三地址格式,用于寄存器和存储器之间的操作,比如,在寄存器和内存之间进行成组的数据传输,成组的PUSH操作,从R1R3中内容传送到内存中。

(4)SI格式

 

这种格式是立即数和内存的立即数寻址,地址长度32位。

(5)SS格式

这种是存储器和存储器之间的数据进行操作,用于内存之间的数据传输。

(三)Intel8086

从指令字长来说8086是典型的复杂指令集计算机,指令字长比较多,占用1-6个字节。

一个字节的指令:

6字节指令:

从地址格式来说,也有多种类型的地址格式:

零地址:

一地址:

二地址:


RISC技术

(一)RISC的产生和发展

RISC(Reduced Instruction Set Computer):精简指令集计算机,和其相对的是CISC(Complex Instruction Set Computer):复杂指令集计算机,早期计算机采用的是复杂指令集,其实最早的计算机他的指令集一定不是非常复杂的,但是在发展过程中,CPU的设计人员,计算机体系结构的设计人员,为了适应应用的发展,为了面向目标程序进行优化,面向高级语言进行优化,面向操作系统进行优化,把目标程序中经常用到的操作,把他变成计算机的指令,来加快应用,指令集逐渐复杂,甚至有些计算机的指令集多达300-400,条,但是太多的指令是没有必要的。在70年代末80年代初的时候,有人开始考虑计算机的指令集是不是越庞大越好,指令的条数是不是越多越好,指令的功能是不是越强越好,分析的结果是二八规律。即在典型的程序中,百分之80语句,仅仅使用了指令集中百分之20的指令,或者是在程序的执行过程中,百分之80被执行的语句只是百分之20的指令。那么,在指令集中加入的很多很复杂的指令导致计算机系统的控制器设计起来很复杂,甚至是只能用微程序的方式进行设计,导致了百分之20那部分功能比较简单的指令在复杂指令集的影响下,或者是强功能的指令的影响下,他的运算速度被拖慢。那能否在指令集中只包含百分之20的简单的指令,以及其他的必要的指令。复杂的指令在指令集中去除,不用硬件实现,涉及到复杂指令的功能,使用简单的百分之20的指令组合实现,这就是RISC技术。

(二)RISC主要特征

(1)指令集比较小,只选择使用频率较高的简单指令,复杂指令功能由简单指令组合实现。使得机器的控制设计简单。

(2)指令长度固定,指令格式种类少,寻址方式少。译码简单,甚至操作码位置固定,操作数位置固定,在译码时候可以取操作数,可以对操作数进行读操作。提供指令速度。

(3)只有 LOAD,STORE指令访存,其他指令都不能访存,其他指令的传输操作或者是算数运算,逻辑运算操作,只能在寄存器和寄存器之间执行,并且结果保存在寄存器中。这样CPU中有比较多的通用寄存器支持这些操作。

(4)指令比较简单,指令之间的关系简单,所以精简指令集的计算机都是采用流水线的方式来做的,而且特别关注提高流水线的效率,在一个时钟周期内完成一条指令。

(5)为了提高速度,所有的精简指令集的指令都是采用组合逻辑的方式,也就是用硬联方式实现控制器,使其速度更快。

(三)CISC主要特征

(1)系统指令复杂庞大,各种指令使用频度相差大。

(2)指令的长度不固定,指令格式多种多样,寻址方式也非常多,导致译码过程非常复杂,CPU硬件部分设计复杂。

(3)访存指令不受限制。

(4)CPU中设有专用寄存器

(5)大多数指令需要多个时钟周期完成,因为指令比较复杂,如果指令都采用硬联方式实现,硬件系统会非常庞大。

(6)在CPU设计时候,采用微程序控制器,指令被分成若干个微指令,微指令按顺序执行完成这条指令要求的功能,微指令组成的微程序保存在控制存储器中,执行指令的时候需要多次访问控制存储器,速度慢。

(四)RISC和CISC比较

(1)RISC指令简单,控制器在CPU芯片上占用的面积小,更能充分利用VLSI芯片面积。剩余的硬件可以做存储来提高CPU运行的速度。

(2)RISC更能够提高计算机的运算速度。指令数、指令格式、寻址方式少, 通用寄存器多,采用 组合逻辑 , 便于实现指令流水。

(3)RISC便于设计,可降低成本。硬件结构简单,提高可靠性。

(4)RISC不容易实现指令系统的兼容,他的指令格式设计的很紧凑,如果后期增加指令比较难。

 

转载来源https://blog.csdn.net/b_x_p/article/details/85223641

posted @ 2021-05-16 16:59  随手一只风  阅读(955)  评论(0编辑  收藏  举报