9.2 中断向量表的结构
Posted doctorx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9.2 中断向量表的结构相关的知识,希望对你有一定的参考价值。
计算机组成
9 中断和异常
9.2 中断向量表的结构
我现在已经知道了,在运算的时候 一旦遇到了异常情况,就翻到第一页的第一行开始写的这些操作的指示,开始往下执行。开始往下执行,这就能解决问题了。但是问题在于这段操作,解决的是我那个运算结果在空格里填不下的问题。可是我遇到新的问题应该怎么办呢?我们可能还会遇到很多的其他的问题。这个时候,这个手册的制造者实际上就需要做一些改进了。
手册的第一页不能只写一种解决情况。他要画一个表格,在这个表格里分了一二三四五条,填上不同的情况。我们遇到了不认识的操作符号,请看第一页的第二条;如果这个地方粘了一个虫子,然后你这个数字看不清楚了,请见第一页的第六条。这样我们就能解决不同的情况了。
那我们先来回顾UNIVAC对异常处理的方式。当算术运算溢出的时候,UNIVAC就转向地址0去取出指令。在那里会执行两条修复指令。这个处理方式已经是很灵活的了。只不过还有一些值得改进的地方。例如在存储器当中,如果从地址0开始只预留了两条指令的空间用于修复指令。那如果后来要改变修复的方式,需要再增加几条指令,这里预留的空间是否就有可能不够了。另外当需要处理的异常情况变多的时候,那就需要根据异常情况的不同,去执行不同的异常处理指令。如果遇到异常都只能转向地址0,就没有办法处理多种不同的异常了。因此,在这两个方面都需要进行改进。
那么就来看一看后来,8086是怎么做的?
8086是一个16位的CPU。它内部有四个16位的通用寄存器,对外则有16根数据线。但是它的地址线要更多一些,一共有20根。这样可以寻址的内存空间就是2的20次方,也就是一兆个Byte。
由于它内部的寄存器与运算器都是16位宽的,要生成20位宽的地址,就得用一定的转换方法。8086采用的是段加偏移的方式。
然后对于这一兆的空间不都是可以任意使用的,有两个区域保留作专门的用途。在这1M字节的内存空间中,最低的1K个字节保留作中断向量表区,而最高的十六个字节保留为初始化程序区。
我们结合图示来进行说明,这里用填充了颜色的方块用来表示这一兆字节的存储器。8086CPU复位之后,它第一次取指令的操作发出的地址是四个F一个0(FFFF0H)。这个地址就是在这一兆内存空间的最高的16个字节的这个地方。这个区域实际上是很小的,只能放很少的几条指令。通常放在这里的是一条无条件的转移指令,转移到内存空间当中的另一个地方。在那个地方存放着后续的系统程序。
那CPU复位之后,为什么不从全0(00000H)的地址开始取址呢?那样看起来岂不是更自然一些。那因为从0开始的地址空间也已经被占用了。这个1K(0000H~003FFH)的字节被用作中断向量表区,它一共存放了 256个中断向量,每个中断向量占四个字节。这样正好就是1K(256*4=(2^{10}=1K))个字节。除了这两块专用的区域,其他区域就可以用来存储一般的程序指令和数据。在这块区域(0000H~003FFH),还有那些用于进行中断处理的程序,这些程序就被称为中断服务程序。而这些程序代码起始地址则被称为中断服务程序的入口地址。
这就是中断向量的定义。
现在的CPU一般都能够处理多种不同的中断类型。
每个中断类型就对应一个中断向量,一共4个字节。在这四个字节当中,前两个字节(06H和30H)用于存放中断服务程序入口的偏移量,而且是低字节(06H)在前,高字节(30H)在后。因此,对于这个中断向量,这两个字节(06H和30H)就会被存放到指令指针寄存器(IP)当中去,而且前一个字节(06H)是寄存器当中的低字节,后一个字节(30H)是寄存器当中的高字节。那么中断向量当中的后两个字节(00H和40H),则对应了中断服务程序入口地址的段基值,用来存放到代码段寄存器,也就是CS寄存器。那么同样,前面那个字节(00H)对应了寄存器当中的低字节,后面的字节(40H)对应了寄存器当中的高字节。在8086当中或者是后来X86处理器的实模式下,就需要用CS和IP这一对寄存器来指定一个内存的地址。
这个地址的产生方式就叫做段加偏移。CS寄存器就是一个段寄存器,它是16位的。而刚才的IP寄存器则对应了偏移量,也是一个16位。这两个16位的地址就构成了逻辑地址。通常的表示方式就是用一个冒号分隔开这两个16位数(CS:IP)。那在CPU产生地址时,会将段寄存器(CS)当中的数左移4位,然后加上偏移量,这样加法运算的结就是20位的物理地址。这就是逻辑地址生成物理地址的方式。实际上是段基值乘以16加上偏移量,对于二进制来说左移四位就相当于乘以16了(二进制左移四位等价于十六进制左移一位)。
那基于这样的方式,每个中断向量都由两个段基值和两个向量的偏移地址组成。因为每一个中断向量占四个字节,在整个中断向量表中一共有256个中断向量,分别命名为0号、1号、一直到255号中断。这个中断向量表要在系统里面启动时进行初始化。假设1号向量的初始值是这样的,当cpu接收到中断时,如果发现时1号中断。因为各个中断向量放置的地址是固定的,那cpu不需要通过执行指令,直接通过硬件电路的设置,就可以发出内存访问来读取这四个字节的内容。然后将其中高两个字节送到CS寄存器当中去,低两个字节送到IP寄存器当中去。
对于8086来说,这两个寄存器(CS和IP)的功能就相当于我们在之前介绍处理器内部结构时提到的PC寄存器。所以,这两个寄存器的值一旦发生改变,下一个周期cpu就会从这个新的地址(CS:IP)开始取下一条指令,根据段加偏移的计算方法,cpu发出的地址就是43006。因此,也就是说在遇到1号中断时,cpu就会转到43006这个地址开始执行程序。当然,需要事先把1号中断的服务程序存放在这里(存储器内)。
与此类似,我们还会把0号中断的服务程序放在存储器的另一个地方,然后将0号中断程序的起始地址分解成段基值和偏移地址,存放在0号中断向量所在的位置。当cpu遇到中断时,如果发现是0号中断,则会将0号中断向量对应的内容取出,分别填到CS和IP寄存器当中去。这样cpu就会从0号中断服务程序的起始地址开始取出指令进行执行。
我们注意到,这些中断服务程序在内存当中的存放顺序并没有要求。并不需要按照中断类型的顺序,先放0号中断服务程序,再放1号中断服务程序,而是可以随意放置。只需要把它的起始地址存放在中断向量表的对应位置就可以了。这样做就比UNIVAC的方式要灵活的多,一来中断服务程序就可以可长可短,不用担心在从0开始的地址到底要预留多少的空间才够;二来中断服务程序的存放位置如果发生改变,也没有关系,不需要修改cpu的硬件设计,而只需要修改中断向量表中对应的中断向量就可以了。这样只用初始化好中断向量表,并在存储器当中准备好对应的中断服务程序,cpu在遇到中断时就可以自动的跳到对应的中断服务程序进行处理。
关于中断向量的相关的计算,我们来看几个简单的练习。
第一,如果中断类型码,也就叫做中断类型号为20H。那中断向量起始的逻辑地址应该是什么呢?首先,我们知道中断向量表是从地址0开始的,一共256个中断向量顺序存放,而每个中断向量占四个字节。所以,中断向量存放的位置就是它的中断类型号乘以4,那这个中断地址就是0000:0080。如果这个中断向量中四个字节的内容分别是10H、20H、30H和40H,那中断服务程序的入口地址应该是什么呢?
按我们刚才介绍的情况。这四个字节单元低两个字节对应了IP寄存器,高两个字节对应了CS寄存器。而且地址较低的这个字节是放在寄存器当中较低的位置。所以,这中断服务程序的入口地址应该是4030:2010。这两个练习就是说明了cpu在遇到了中断之后,硬件完成的工作。如果cpu现在遇到的中断类型号是20H,则会通过硬件进行乘4的操作,从而得到这个逻辑地址(0000:0080)。然后将这个逻辑地址发到存储器中,读回了这四个字节(10H、20H、30H 和 40H)的内容。然后,按照我们刚才规定的原则拼接出了这样两个16位数(4030:2010),并且把这两个16位数分别存放到CS和IP寄存器当中去。这样在下一个时钟周期就会将新的地址发送到存储器去取下一条指令了。
然后我们再来看另一个练习。
如果我们现在要为17H号中断新写了一段中断服务程序,而且把这段中断服务程序放到了存储器的某一个地方,地址是2340H:7890H。
那现在我们就需要去更新中断向量表。现在问题就是,我们要更新中断向量表中的哪四个字节?而且更新成什么样的内容?那我们可以一起来算一下。因为中断类型号是17H,那对应中断向量的地址就应该是它乘以4(17*4 = 5C (mod 16))。由这个地址(0000:005C)开始,向高地址增长,一共4个字节。所以,这四个字节的逻辑地址是这样的(第一个空)。
这四个字节单元当中分别应该填写什么样的内容呢?那么还是要记住这个原则,如果把这个逻辑地址(2340:7890H)从右往左看。最右边的这个字节(90H)放在最低的地址,而最左边的这个字节(23H)放在最高的地址。这四个字节依次排放。所以,地址由低到高四个字节内容分别应该是90、78、40和23。
那这两个练习就分别展示了cpu硬件查找中断向量表的过程,和准备好中断服务程序去初始化或者修改中断向量表的过程。
对于8086的中断向量表cpu已经固定使用了前五个类型的中断,具体的功能我们后面会再介绍。之后的27个中断(5~31)也是保留给后续的cpu使用的。而除了前20个中断,之后的224个的中断则是交给使用cpu的用户自行定义。
我们现在知道了,原来我们的第一页制造者给我们列了一张表格。一共有256条。那我们在遇到异常情况的时候,就可以根据事先的约定对应不同的情况,去找对应的那一条表项。那条表项实际上是指一个页码。比如说第四条说翻到第十二页,这第十二页翻过来以后,写了具体的操作,我们应该做什么事情,操作还挺复杂,写了好几页,我们都放在一页纸是放不下的。所以,这就是我们现在要怎么处理异常情况的方法。
随着我们要做的运算任务的增加,这个表的内容可能还需要进一步的扩展。接下来我们就来看一看它是怎么扩展的。
以上是关于9.2 中断向量表的结构的主要内容,如果未能解决你的问题,请参考以下文章