802-中断向量表的结构
# 802-中断向量表的结构
那好,我现在已经知道了,我在运算的时候 一旦遇到了异常情况,就翻到第一页的第一行开始写的这些操作的指示,开始往下执行。 开始往下执行,这就能解决问题了。 但是问题在于这段操作,解决的是我那个运算结果在空格里填不下的问题。
可是我遇到新的问题应该怎么办呢,我们可能还会遇到很多的其他的问题。 这个时候呢,这个手册的制造者实际上就需要这些改进了。 它需要把第一页不能只写一种解决情况, 他要画一个表格。在这个表格里分了一二三四五条,填上不同的情况。我们遇到了不认识的操作符号。 请看第一页的第二条,如果这个地方粘了一个虫子, 然后你这个数字看不清楚了,请见第一页的第六条。 这样我们就能解决不同的情况了。
那我们先来回顾 UNIVAC 对异常处理的方式。 当算术运算溢出的时候,UNIVAC 就转向地址 0 去取出指令。在哪里会执行两条修复指令。这个处理方式已经是很灵活的了。只不过还有一些值得改进的地方。
例如在存储器当中,如果从地址 0 开始只预留了两条指令的空间用于修复指令。那如果后来要改变修复的方式,需要再增加几条指令。 那这里预留的空间是否就有可能不够了。另外当需要处理的异常情况变多的时候, 那就需要根据异常情况的不同区执行不同的异常处理指令。那如果遇到异常都只能转向地址 0,就没有办法处理多种不同的异常了。
因此,在这两个方面都需要进行改进。 那么就来看一看后来,8086 是怎么做的? 8086 是一个十六位的 cpu。它内部有四个 16 位的通用寄存器。 对外则有 16 根数据线,但是它的地址显要更多一些,一共有 20 根。 这样可以寻址的内存空间就是 2 的 20 次方,也就是一 M 个 byte。 那由于它内部的寄存器与运算器都是 16 位宽的。 要生成 20 位宽的地址,就得用一定的转换方法。8086 采用的是“段加偏移”的方式。
然后对于这一兆的空间不都是可以任意使用的,有两个区域保留作专门的用途。在这一兆字节的内存空间中, 最低的 1K 个字节,保留作中断向量表区。 而最高的十六个字节保留为初始化程序区。
我们结合图示来进行说明,这里用填充了颜色的方块用来表示这一兆字节的存储器。 用 8086cpu 复位之后,它第一次取指令的操作发出的地址是四个 F 一个 0。 这个地址就是在这一兆内存空间的最高的 16 个字节的这个地方。 那这个区域实际上是很小的,只能放很少的几条指令。 通常放在这里的是一条无条件的转移指令。 转移到内存空间当中的另一个地方,在那个地方存放着后续的系统程序。
那 CPU 复位之后,为什么不从全 0 的地址开始取址呢?那样看起来岂不是更自然一些。 那因为从 0 开始的地址空间也已经被占用了。 这个 1K 的字节被用作中断向量表区,它一共存放了 256 个中断向量。每个中断向量占四个字节。这样正好就是 1K 个字节。
那除了这两块专用的区域,其他区域就可以用来存储一般的程序指令和数据。
那在这块区域,还有那些用于进行中断处理的程序。 这些程序就被称为中断服务程序。 而这些程序代码起始地址则被称为中断服务程序的入口地址。 这就是中断程序向量的定义。
现在的 CPU 一般都能够处理多种不同的中断类型。 那么每个中断类型就对应一个中断向量,一共 4 个字节。 这四个字节当中,前两个字节用于存放中断服务程序入口的偏移量。 而且是低字节在前,高字节在后。因此对于这个中断向量, 这两个字节就会被存放到而且前一个字节是寄存器当中的低字节,后一个字节是寄存器当中的高字节。 那么中断向量当中的后两个字节,则对应了终端服务器入口的地址的段基址。 用来存放到代码段计算器,也就是 CS 计算器。
那么同样,前面那个字节对应了寄存器当中的低字节, 后面的字节对应了寄存器当中的高字节。 那么在 8086 当中或者是后来 X86 处理器的实模式下,就需要用 CS 和 IP 这一对寄存器,来指定一个内存的地址。 这个地址的产生方式就是叫做段加偏移
那刚才 CS 寄存器就是一个段寄存器。它是 16 位的, 而刚才的 IP 寄存器则对应了偏移量,也是一个 16 位。 那这两个 16 位的地址就构成了逻辑地址。那通常的表示方式就是用一个冒号分隔开这两个 16 位数。 在 cpu 产生地址是,会将段寄存器当中的数左移 4 位, 然后加上偏移量,这样加法运算的结就是 20 位的物理地址。 这就是逻辑地址生成物理地址的方式,实际上是段基值乘以十六加上偏移量。 那对于二进制来说左移四位就相当于乘以 16 了。
那基于这样的方式,每个中断向量都由两个段基值和两个向量的偏移地址组成,还因为每一个中断向量占四个字节。 在整个中断向量表中,一共有 256 个中断向量。 分别命名为 0 号、1 号、一直到 255 号中断。 那这个中断向量表要在系统里面启动时进行初始化。 那么假设 1 号向量的初始值是这样的,那当 cpu 接收到中断时, 如果发现是 1 号中断, 那因为各个中断向量放置的地址是固定的,那 cpu 不需要通过执行指令, 直接通过硬件电路的设置,就可以发出内存访问来读取这四个字节的内容,然后将其中高两个字节送到 CS 寄存器当中去, 低两个字节送到 IP 寄存器当中去。
那对于 8086 来说,这两个寄存器的功能就相当于我们在之前介绍处理器内部结构时,提到的 PC 寄存器。 所以这两个寄存器的值一旦发生改变, 下一个周期 cpu 就会从那个新的地址开始取下一条指令。 那根据段加偏移的计算方法,cpu 发出的地址均是 43006,。 因此也就是说,在遇到一号中断时, cpu 就会转到 43006 这个地址开始执行程序。 那当然,需要实现把一号中断的服务程序存放在这里。
与此类似,我们还会把 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 的操作,从而得到这个逻辑地址。然后将这个逻辑地址发到存储器中,读回了这四个字节的内容。 然后按照我们刚才规定的原则拼接出了这样两个 16 位数。 并且把这两个 16 位数分别存放到 CS 和 IP 计算器当中去。 这样在下一个实施周期就会将新的地址发送到存储器去下一条指令了。
然后我们再来看另一条练习。 如果我们现在要为 17H 号中断新写了一段中断服务程序。 而且把这段中断服务程序放到了存储器的某一个地方。 地址是 2340H:7890H。那现在我们就需要去更新中断向量表。 那现在问题就是,我们要更新中断向量表中的哪四个字节?而且更新成什么样的内容? 那我们可以一起来算一下。因为中断类型号是 17H, 那对应中断向量的地址就应该是它乘以 4。由这个地址开始, 想高地址增长,一共 4 个字节。所以这四个字节的逻辑地址是这样的。 那这四个字节单元当中分别应该填写什么样的内容呢? 那么还是要记住这个原则,如果把这个逻辑地址从右往左看。 最右边的这个字节放在最低的地址,而最左边的这个地址放在最高的地址。 这四个字节依次排放。 所以地址由低到高四个字节内容分别应该是 90、78、40 和 23。
那这两个练习就分别展示了 cpu 硬件查找中断向量表的过程,和准备好中断服务程序去初始化或者修改中断向量表的过程。
那对于 8086 的中断向量表 cpu 已经固定使用了前五个类型的中断。 那具体的功能我们后面会再介绍, 那之后的 27 个中断也是保留给后续的 cpu 使用的。 而除了前 32 个中断,之后的 224 个的中断则是交给使用 cpu 的用户自行定义
那好,我们现在知道了原来我们在第一页呢, 制造者给我们列了一张表格。 一共有 256 条。那我们在遇到异常情况的时候呢就可以根据事先的约定对应不同的情况, 去找对应的那一条表项。表项呢实际上是指一个页码。 它比如说第四条说翻到第十二页,这第十二而翻过来以后呢。 写了具体的操作,我们应该做什么情况。这一项还挺复杂,写了好几页,对吧,都放在第一页是放不下的。所以这就是我们现在要怎么处理异常情况的方法。 那么随着我们要做的运算任务的增加呢,这个表的内容可能还需要进一步的扩展。 那么我们后来就来看一看它是怎么扩展的。
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02