8259A
在前面我们已经看到,在CPU处理的各种中断中,有很大一部分是来自外部硬件设备的中断,这些中断通过可编程控制器(PIC)控制。在IBMPC兼容机上该控制器为Intel 8259A芯片。
单个8259A芯片最多可以连接8个中断源,但由于可以最多将9个该芯片级连,因此,其最多可以接受64个中断源。在IBMPC机上采用2个8259A芯片级连,最多支持15个中断源。这两个芯片一个叫做Master,另一个叫做Slave。之所以这么称呼是因为,由于CPU只具有INTR这一个中断线,所以Slave必须连级到Master上,占用MasterPIC的IRQ2,将IRQ9重定向到IRQ2上。
8259A芯片处理中断的过程,主要是通过芯片内3个内部寄存器进行的。这三个寄存器分别为IMR,IRR和ISR其中,IMR用作过滤被屏蔽的中断,IRR用来存放被悬挂的中断并等待进一步处理,ISR用来保存CPU正在处理的中断。
另外8259A芯片还有一个叫做优先级仲裁的单元。该单元的作用是在8259A同时接受到多个中断时,根据各个中断的优先级,挑选具有最高优先级的中断传递给CPU处理。
在大致介绍这几个单元后,下面我们来看一些8259A在处理中断时的具体过程。
首先,外部中断请求(IR0到IR7)传输到IMR,IMR根据此中断请求是否被屏蔽,以决定是将其丢弃,还是放入IRR中等待进一步处理。当8259A等待到一个中断时机时,优先级仲裁单元会从所有放入IRR中的中断请求中挑出一个优先级最高的中断,传递给CPU处理。值得注意的是中断优先级是随着中断请求号降低而提高的。在CPU的INTR引脚接收到8259A发送过来的信号后,CPU会暂停执行下一条指令,并向8259A发送一个INTA信号。在 8259A接收到该信号后,就会将ISR中代表该中断的位置1,并将IRR中相应的位清零。以表示该中断正在被CPU处理。接着CPU会向8259A再发送一个INTA信号,向其请求中断向量号。这时,8259A会根据先前设置好的起始向量号再加上中断请求号计算出中断向量号,并将其放入数据总线中。这时候,如果8259A的EOI通知被设定为自动模式,那么8259A就会自动将ISR中刚才置1的位清零。在CPU获得该中断向量号后,就会转去调用该中断服务程序。在处理完该中断后如果8259A的EOI通知被设定为人工模式,则还要向8259A发送一个EOI。通常来讲,这一工作往往是在中断服务程序中完成。在8259A接收到该EOI通知后,就会将ISR中刚才置1的位清零。
以上就是8259A处理一个中断的整个过程的简述。由于中断请求存在着优先级,因此,如果在一个中断处理期间,8259A又收到了新的中断请求,则首先跟当前处理的优先级进行比较,如果新到的中断请求的优先级高于当前处理的中断请求,则马上处理新到的中断请求,否则则将新到的中断请求放入IRR。
对于8259A的操作,是通过端口进行的。其中, Master的端口地址为0x20, 0x21, Slave的端口地址位0xA0,0xA1。8259A具有两种命令,一种是ICW,其作用是用来初始化8259A芯片。另一个是OCW,其作用是用来向 8259A发送命令。虽然在系统启动后BIOS会自动初始化8259A,但这并不是我们所需要的。因为在进入保护模式后,我们要设置IDT,因此我们必须根据所设置的IDT去初始化8259A.
对8259A的操作有两类命令,其中一类是ICW,另一类是OCW。ICW用来对8259A进行初始化,而OCW则用来在初始化后对8259A发布命令。有意思的是,8259A的两个端口对于这两类命令的发布是有固定安排的。对于0x20和0xA0端口,你可以向它们写入ICW1,OCW2,OCW3,读取IRR和ISR。对于0x21和0xA1端口,你可以向它们写入ICW2,ICW3,ICW4, 并能够读写IMR寄存器。
下面我们分别来讨论这几个命令
ICW1:该命令作为初始化序列的第一条命令,一旦向端口送入该命令,8259A就认为初始化序列开始。
位 | 功能 |
7:5 | MCS-80/85模式下的中断向量地址 |
4 | 必须设置为1 |
3 | 0:Edge Triggered Interrupts 1:Level Triggered Interrupts |
2 | 0:Call Address Interval of 8 1:Call Address Interval of 4 |
1 | 0:Cascaded PICs 1:Single PIC |
0 | 0:Don't need ICW4 1:Will be Sending ICW4 |
在设置时,对于80x86的CPU,其应设置为(00010001),也就是0x11。
ICW2:该命令用来指定所初始化的8259A中断请求的起始向量。其中ICW2的低3位必须为0,其这么做的原因在于当该8259A接收到一个中断请求时,低3位会自动填充为所接受到的向量号。因此这也就决定了我们设置的起始中断向量,必须为8的倍数。
ICW3:Master PIC和Slave PIC对于ICW3命令具有不同的格式
对于Master PIC,Slave PIC被接到了Master PIC的哪个IRQ上,则ICW3中相应的位就置1。在8259A中,由于SlavePIC是级连在Master PIC的IRQ2上的,因此ICW3的值应该为(00000100),也就是0x04。而对于SlavePIC其高5位必须设置为零,低3位为该PIC被级连到哪个Master PIC的IRQ号,在8259A中,其SlavePIC的值为(00000010),即0x02。
ICW4:
位 | 功能 |
7:5 | 保留,设置为0 |
4 | 0:Not Special Fully Nested Mode 1:Special Fully Nested Mode |
3:2 | 0x:Non-Buffered Mode 10:Buffered Mode - Slave 11:Buffered Mode - Master |
1 | 0:Normal EOI 1:Auto EOI |
0 | 0:MCS-80/85 1:8086/8088 Mode |
在80x86模式下,我们采用默认的Full Nested Mode,将ICW4设置为(000000001),即0x01。
而我们之所以我们要采用NormalEOI,其原因在于我们要允许中断请求的按优先级抢占。如果我们将EOI通知设定为自动模式,那么在CPU发出第二个 INTA信号后,8259A中相应的ISR就会自动清零,而此时该中断服务程序还没有被调用。如果在该中断服务程序被调用的过程中,8259A收到了优先级比当前正在处理的中断优先级低的中断请求,由于正在处理的中断在ISR中相应的位已经清零,因此这个新的中断请求就完全可以抢占正在处理的优先级比它高的中断服务程序。
下面是初始化8259A的代码:
void init_8259A(byte master_vector,byte slave_vector) { outportb (PORT_8259A_M, 0x11); outportb (PORT_8259A_S, 0x11); outportb (PORT_8259A_M+1, master_vector); outportb (PORT_8259A_S+1, slave_vector); outportb (PORT_8259A_M+1, 1<<2); outportb (PORT_8259A_S+1, 2); outportb (PORT_8259A_M+1, 1); outportb (PORT_8259A_S+1, 1); } |
在介绍完初始化这几个命令后,我们开始介绍如何通过OCW对8259A进行操作。
OCW1:该命令用来屏蔽所设定的中断请求。其操作方式是,向你要屏蔽的中断请求所在的8259A发送一个操作控制字。需要屏蔽哪个中断请求就将该字上相应的位置1即可。
实例代码如下:
#define PORT_INT_MASK_M 0x21 #definePORT_INT_MASK_S 0xA1 void mask_IRQ(byte IRQ) { byte mask; if(IRQ > 15) return; if(IRQ < 8) { mask = inportb(PORT_INT_MASK_M); mask |= 1 << IRQ; outportb(PORT_INT_MASK_M, mask); } else { mask = inportb(PORT_INT_MASK_S); mask |= 1 << (IRQ-8); outportb(PORT_INT_MASK_S, mask); } } void unmask_IRQ(byte IRQ) { byte mask; if(IRQ > 15) return; if(IRQ < 8) { mask = inportb(PORT_INT_MASK_M); mask &= !(1 << IRQ); outportb(PORT_INT_MASK_M, mask); } else { mask = inportb(PORT_INT_MASK_S); mask &= !(1 << (IRQ-8)); outportb(PORT_INT_MASK_S, mask); } } |
OCW2:
位 | 功能 |
7:5 | |
4:3 | Must be set to 0 |
2:0 | |
如果OCW2中的bit6被设置为0,那么该命令将对整个8259A有效。否则,将针对bit2:0这3位所代表的IRQ进行操作。由于我们前面已经将8259A设置为手动EOI模式,所以在这里我们要将bit7:5设置为(001)
OCW3:
位 | 功能 |
7 | Must be set to 0 |
6:5 | 10:Reset Special Mask 11:Set Special Mask |
4 | Must be set to 0 |
3 | Must be set to 1 |
2 | 0:No Poll Command 1:Poll Command |
1:0 | 10:Next Read Returns IMR 11:Next Read Returns ISR |