8086-6-中断

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

8086-6-中断
中断(8086)
中断就是打断处理器当前的执⾏流程,去执⾏⼀些和当前⼯作不相⼲的指令,执⾏完之后,还可以返回到原来的程序流程继续执⾏。

就好⽐你在打游戏突然⽼板来电话了,你不得不先停⽌打游戏然后来处理这件更为重要的事件,然后打完电话之后继续打游戏。

中断的⼀些概念:
中断号:
由于CPU需要通过对不同类型的中断进⾏不同处理,所以每种类型的中断都被统⼀编号,这称为中断类型号、中断向量或者中断号。

Intel
处理器允许256 个中断,中断号的范围是0~255
中断源:
中断信号的来源,或者说产⽣中断的设备,被称为中断源。

中断嵌套:
当⼀个中断事件正在处理时,如果来了⼀个优先级更⾼的中断事件时,允许暂时中⽌当前的中断处理,先为优先级较⾼的中断事件服务,这称为中断嵌套。

实模式下的中断向量表(Interrupt Vector Table,IVT):
所谓中断处理,其实就是处理器要执⾏⼀段与该中断有关的程序(指令)你也可以将其当作⼀个函数。

处理器可以识别256 个中断,那么理论上就需要256 段代码。

这些代码实际存放的位置并不重要,重要的是,在实模式下,处理器要求将它们的⼊⼝点也就是起始地址集中存放到内存中从物理地址0x00000 开始,到0x003ff 结束,共1KB 的空间内,这就是所谓的中断向量表。

每个中断的⼊⼝点地址在中断向量表中占2 个字,分别是中断处理代码的偏移地址和段地址。

中断0的⼊⼝点位于物理地址0x00000 处,也就是逻辑地址0x0000:0x0000;中断1 的⼊⼝点位于物理地址0x00004 处,即逻辑地址0x0000:0x0004;其他中断⼊⼝点地址以此类推。

中断分类:
中断⼤致上可以分为硬件中断和软件中断(简称为软中断)。

顾名思义,硬件中断由硬件来提供,⽐如说:CPU,⿏标键盘等。

⽽软件键盘由内部的代码来定义。

硬件中断:
硬件中断还可以分为外部硬件中断和内部硬件中断,外部硬件中断是指除CPU以外的硬件对应的中断,⽽内部硬件中断是内部CPU对应的中断。

外部硬件中断:
外部硬件中断,就是除处理器以外的外部设备中来的中断信号。

当外部设备发⽣错误,或者有数据要传送(⽐如,从⽹络中接收到⼀个针对当前主机的数据包),或者处理器交给它的事情处理完了(⽐如,打印已经完成),它们都会告诉CPU先停下⼯作,来临时处理⼀下。

外部硬件中断⼜可以根据中断是否紧急⽽细分:因为有⼀些严重的事情,⽐如说电池没电了,这个如果不处理马上就会关机了,⽽有⼀些不是很重要,⽐如说键盘输⼊,有时候卡死了为了防⽌外部硬件继续倒腾导致系统直接崩溃,可以选择把这种不是很重要的中断进⾏屏蔽掉。

所以外部硬件中断⼜分为可屏蔽中断和不可屏蔽中断。

外部硬件中断是通过两个信号线引⼊处理器内部的。

从8086 处理器开始,这两根线的名字就叫NMI 和INTR。

(这是⼀个简化的⽰意图,不是真正的设备连接图。


当⼀个外部硬件中断发⽣时,处理器将会从中断引脚NMI或INTR中得到通知,其中不可屏蔽中断采⽤NMI引脚,⽽可屏蔽中断采⽤INTR引脚。

不可屏蔽中断(Non Maskable Interrupt,NMI):
不可屏蔽中断是针对⼀些硬件的很严重的事件进⾏处理。

所有的严重事件都必须⽆条件地加以处理,这种类型的中断是不会被阻断和屏蔽的,称为⾮屏蔽中断(Non Maskable Interrupt,NMI)。

在传统的兼容模式下,NMI 的中断源通过⼀个与⾮门连接到处理器。

处理器的NMI 引脚是⾼电平有效的,⽽中断信号是低电平有效。

当不存在中断的时候,与⾮门的所有输⼊都为⾼电平也就是所有中断源的都没有发出中断信息,因此与⾮门的结果就是低电平所以处理器的NMI 引脚为低电平,这CPU没有从NMI引脚获得中断信息。

当⾄少有⼀个不可屏蔽中断发⽣时都会导致与⾮门的输出结果为⾼电平,也就会导致NMI引脚为⾼电平,⽽CPU就会接受到NMI,然后进⾏对应的处理。

(与⾮门:)
由于不可屏蔽中断的⾮常特殊性,⼏乎所有触发NMI 的中断事件对处理器来说都是致命的,甚⾄是不可纠正的。

所以在这种情况下,努⼒去搞清楚发⽣了什么,通常没有太⼤的意义,这样的事最好留到事后,让专业维修⼈员来做。

因此在实模式下,NMI 被赋予了统⼀的中断号2,不再进⾏细分。

⼀旦发⽣2号中断,处理器和软件系统通常会放弃继续正常⼯作,也不会纠正已经发⽣的问题和错误,很可能只是由软件系统给出⼀个提⽰信息。

可屏蔽中断(Interrupt Request)INTR:
这⾥的INTR就不是可屏蔽中断的简称了,⽽是⼀个Interrupt request的简称。

这类中断有两个特点,第⼀是数量很多,毕竟有很多外部设备;第⼆是它们可以被屏蔽,这样处理器就不对它们进⾏处理。

所以,这类硬件中断称为可屏蔽中断。

可屏蔽中断是通过INTR 引脚进⼊处理器内部的,像NMI⼀样,不可能为每⼀个中断源都提供⼀个引脚。

⽽且,处理器每次只能处理⼀个中断。

在这种情况下,需要⼀个代理,来接受外部设备发出的中断信号。

还有,多个设备同时发出中断请求的⼏率也是很⾼的,所以该代理的还包括对它们抉择到底让它们中的哪⼀个优先向处理器提出服务请求。

在个⼈计算机中,⽤得最多的中断代理就是8259芯⽚,这类硬件就是通常所说的中断控制器( Interrupt Controller,IC)),从8086 处理器开始,它就⼀直提供这种服务。

即使是现在,在绝⼤多数单处理器的计算机中,也依然有它的存在。

8559芯⽚
Intel 处理器允许256 个中断,中断号的范围是0~255,8259 提供其中的15 个,但中断号并不固定。

之所以不固定,是因为设计时,允许软件根据⾃⼰的需要灵活设置中断号,以防⽌发⽣冲突。

该中断控制器芯⽚有⾃⼰的端⼝号,可以像访问其他外部设备⼀样⽤in 和 out 指令来改变它的状态,包括各引脚的中断号。

正是因为这样,它⼜叫可编程中断控制器(Programmable Interrupt Controller,PIC)。

每⽚8259 只有8 个中断输⼊引脚,在个⼈计算机上使⽤它,需要两块。

如图所⽰,第⼀块8259 芯⽚的代理输出的INT 直接送到处理器的INTR引脚,这是主⽚(Master);第⼆块 8259 芯⽚的INT输出送到第⼀块的引脚2 上,是从⽚(Slave),两块芯⽚之间形成级联(Cascade)关系。

所以两块8259 芯⽚可以向处理器提供15 个中断信号。

根据需要,这些中断引脚可以被各种设备使⽤。

8259 的主⽚引脚0(IR0)接的是系统定时器/计数器芯⽚;从⽚的引脚0(IR0)接的是实时时钟芯⽚RTC这两块芯⽚的固定连接即使是在硬件更新换代⾮常频繁的今天,也没有改变。

在8259 芯⽚内部,有中断屏蔽寄存器(Interrupt Mask Register, IMR),这是个8 位寄存器,对应着该芯⽚的8 个中断输⼊引脚,对应的位是0 还是1,决定了从该引脚来的中断信号是否能够通过8259 送往处理器(0 表⽰允许,1 表⽰阻断)。

当外部设备通过某个引脚送来⼀个中断请求信号时,如果它没有被IMR 阻断,那么,它可以被送往处理器。

(注,8259 芯⽚是可编程的,主⽚的端⼝号是 0x20 和0x21,从⽚的端⼝号是0xa0 和0xa1,可以通过这些端⼝访问 8259 芯⽚,设置它的⼯作⽅式,包括IMR 的内容。


中断能否被处理,除了要看8259 芯⽚的选择外,还需要由处理器来决定。

在处理器内部,标志寄存器有⼀个标志位IF,这就是中断标
志(Interrupt Flag)。

当IF 为0 时,所有从处理器INTR 引脚来的中断信号都被忽略掉;当其为1 时,处理器可以接受和响应中断。

IF 标志位可以通过两条指令cli 和sti 来改变。

这两条指令都没有操作数,cli(CLear Interrupt flag)⽤于清除IF 标志位,sti(SeT Interrupt flag)⽤于置位IF 标志为1。

在计算机内部,中断发⽣得⾮常频繁,当⼀个中断正在处理时,其他中断也有可能会响应,甚⾄会有多个中断同时发⽣的情况。

这就需要中断控制器了,8259 芯⽚会记住它们,并按⼀定的策略决定先为谁服务。

总体上来说,中断的优先级和引脚是相关的,主⽚的IR0引脚优先级最⾼,IR7引脚最低,从⽚也是如此。

当然,还要考虑到从⽚是级联在主⽚的IR2引脚上。

⼩结外部硬件中断:
外部硬件中断分为可屏蔽和不可屏蔽,对应NMI和INTR两个引脚,NMI由于很严重只要发⽣了就直接停下就好了,⽽不可屏蔽中断由于功能很多需要选择所以就需要⼀个中断控制器来处理,中断控制器⾥可以选择是否响应中断,⽽CPU⾥也有IF标志位来选择是否响应中断。

内部硬件中断:
内部硬件中断发⽣在处理器内部,是由执⾏的指令引起的。

⽐如,当处理器检测到div 或者idiv 指令的除数为零时,或者除法的结果溢出时,将产⽣中断0(0 号中断),这就是除法错误中断。

内部中断不受标志寄存器IF 位的影响,它们的中断类型是固定的,可以⽴即转⼊相应的处理过程。

软中断
软中断与硬件中断⽆关,是写好的存放在计算机⾥⾯的中断,可以类⽐为⼀个API函数。

软中断是由int 指令引起的中断处理。

中断号在指令中给出,int 指令的格式如下:
int3 //int3 是断点中断指令
int ⽴即数
into
注意,int3 和int 3是不⼀样的,int3是⼀个特殊的断点指令,⽽int 3是中断向量表中的第四个中断程序。

into 是溢出中断指令,机器码为0xCE,也是单字节指令。

当处理器执⾏这条指令时,如果标志寄存器的OF 位是1,那么,将产⽣4 号中断。

否则,这条指令什么也不做。

最有名的软中断是BIOS 中断,之所以称为BIOS 中断,是因为这些中断功能是在计算机加电之后,由BIOS 程序执⾏期间建⽴起来的。

CPU调⽤中断过程:
拿外部硬件中断来进⾏举例。

当中断发⽣时,如果从外部硬件到处理器之间的道路都是畅通的,那么,处理器在执⾏完当前的指令后,会⽴即为硬件服务。

它⾸先会响应中断,告诉8259 芯⽚(中断管理器)准备着⼿处理该中断。

接着要求 8259 芯⽚把中断号送过来。

8259 芯⽚它会把对应的中断号告诉处理器,处理器接受到中断号后就开始处理:
①保护断点的现场。

⾸先要将标志寄存器FLAGS 压栈,然后清除它的IF 位和TF 位。

接着,再将当前的代码段寄存器CS 和指令指针寄存器IP 压栈。

②执⾏中断处理程序。

由于处理器已经拿到了中断号,它将该号码乘以4(因为每个中断地址在中断向量表中占4 字节),就得到了该中断⼊⼝点在中断向量表中的偏移地址。

接着,从表中依次取出中断程序的偏移地址和段地址,并分别传送到IP 和CS,然后,处理器就开始执⾏中断处理程序了。

(由于IF 标志被清除,在中断处理过程中,处理器将不再响应硬件中断。

如果希望更⾼优先级的中断嵌套,可以在编写中断处理程序时,适时⽤sti 指令开放中断。


③返回到断点处接着执⾏。

所有中断处理程序的最后⼀条指令必须是中断返回指令iret。

这将导致处理器依次从栈中弹出(恢复)IP、CS 和FLAGS 的原始内容,于是转到主程序接着执⾏。

;这⾥的iret指令⽤汇编来描述就是
pop ip
pop cs
popf
其它的中断调⽤过程可以参考外部硬件调⽤过程,只是少了⼀个中断管理器。

为什么进⾏中断处理时,需要先将IF、TF都设置为0?
将IF设置为0的原因主要是因为8086CPU的设计者认为中断处理程序⼀般是不需要对其它中断做出响应的。

因此默认的将IF设置为0,禁⽌CPU处理可屏蔽中断。

当然,如果有的中断处理程序确实需要处理可屏蔽中断,也可以在中断处理程序
中开中断,将IF重新设置为1。

指令sti,设置IF=1;指令cti,设置IF=0。

将TF单步调试功能关闭的原因则是为了避免出现单步中断的死循环。

试想如果开启了单步调试功能,那么在进⼊中断处理程序,并执⾏完第⼀条指令后,便会引发单步中断,进⽽跳转到单步中断处理程序中。

⽽在执⾏了单步中断处理程序的第⼀条指令后,⼜会再度引发单步中断,这成为了⼀个死循环。

因此,在进⼊中断处理程序之前,必须⾸先把TF置为0,关闭单步调试功能,以避免上述情况产⽣。

⼿动编写软中断:
⾸先明⽩如何写中断。

1:编写中断程序
2:将中断程序保存在内存中的某⼀个位置
3:将中断程序的⼊⼝点保存到中断向量表⾥
4:调⽤中断程序。

1 编写中断程序:
这⾥我采⽤了很简单的代码,修改显卡的第⼀个字符的内容和颜⾊:
show:
mov ax,0xb800
mov es,ax
mov byte es:[0],0x48
mov byte es:[1],0x04
iret
2 将中断程序保存在内存中:
8086CPU的内存分布:
我们可以将我们的中断程序保存到DRAM内存条⾥,我们需要找⼀块空的内存地址就可以。

这⾥我查看了0x10000物理地址往上的地址空间:
发现是空的,我就采⽤这块地址空间了。

然后我们需要把中断程序的硬编码保存进去。

mov ax,0
mov ds,ax
mov ax,0x1000
mov es,ax
mov si,show
mov di,0
mov cx,showend-show
cld
rep movsb
3 将中断程序的⼊⼝点保存到中断向量表⾥
⾸先⽤bochs虚拟机查看中断向量表的空余中断地址:
我选择了这⾥物理地址为0x180的地⽅,然后我们将⼊⼝点的段地址和偏移地址赋值进去。

mov ax,0x0
mov es,ax
mov es:[0x180],word 0x0
mov es:[0x182],word 0x1000
;//先是偏移地址,再是段地址
4 调⽤中断程序
调⽤软中断很简单,只需要int n就⾏了,n是你的中断地址相对于起始中断地址的⼀个数组偏移值。

这⾥ n = ⾏数-1 * 4+对于⾏的起始地址的偏移量 =24*4+0=96
所以就直接调⽤指令就可以了:
int 96
验证中断调⽤是否成功:
整个汇编程序的源代码:
segment myInterrupt vstart=0x7c00
;将机器码赋值给0x1000:0的内存地址空间
start:
mov ax,0
mov ds,ax
mov ax,0x1000
mov es,ax
mov si,show
mov di,0
mov cx,showend-show
cld
rep movsb
;将中断⼊⼝点地址保存到中断向量表
mov ax,0x0
mov es,ax
mov es:[0x180],word 0x0
mov es:[0x182],word 0x1000
int 96
jmp start
show:
mov ax,0xb800
mov es,ax
mov byte es:[0],0x48
mov byte es:[1],0x04
iret
showend:
nop
times 510-($-$$) db 0
db 0x55,0xaa
将其编译后写⼊硬盘的主引导扇区就可以加载了。

采⽤bochs 调试虚拟机来调试,⾸先将程序运⾏到主引导扇区的地⽅:
然后查看我们的汇编指令,并给int 96指令打⼀个断点:
然后运⾏到断点处并查看内存是否写⼊:
可以看到已经成功写⼊内存地址为0x10000的地址空间了。

然后查看中断向量表是否成功写⼊:
可以看到内存地址 0x180的内容已经更改为 0x10000000,因为intel是采⽤的⼩端字节序,所以我们肯定更改成功了。

然后我们给int 96中断处理程序的⼊⼝地址打⼀个断点,并运⾏查看是否允运⾏到了我们的中断处理程序:
可以看到已经运⾏到我们的中断处理程序处了,⽽且此时的物理地址也改为了0x10000(可能有⼈想问前⾯的if不是置零了吗,int3断点中断也是中断啊,为啥这⾥就可以中断嵌套呢,其实很简单,那个if是针对外部中断设备的可屏蔽中断的,我们这个是软中断,和它⽆关。

)
然后我们将这个中断处理程序运⾏完,并查看显⽰器的内容是否修改:
可以看到完美实现。

所以我们的代码逻辑肯定没有问题,以上就是⼀个简单的软中断实现。

⼩结:
中断是CPU的⼀个重要概念,需要掌握。

当然,这次是讲述的8086中断,对于其它CPU虽然不吻合,但是具有很⼤的参考意义。

相关文档
最新文档