Initialization Command Word
(ICW);
这种命令字被用作对8259A芯片的初始化。
Operation Command Word
(OCW):
这种命令被用来向8259A发布命令,以对其进行控制。OCW可以在8259A被初始化之后的任何时候被使用。
下表的内容是Master
8259A的I/O端口地址,以及通过它们所能操作的寄存器。AddressRead/WriteFunction0x20WriteInitialization Command Word 1
(ICW1)WriteOperation Command Word 2
(OCW2)WriteOperation Command Word 3
(OCW3)ReadInterrupt Request Register
(IRR)ReadIn-Service Register (ISR)0x21WriteInitialization Command Word 2
(ICW2)WriteInitialization Command Word 3
(ICW3)WriteInitialization Command Word 4
(ICW4)Read/WriteInterrupt Mask Register
(IMR)Addresses/Registers for Master 8259A下表的内容是Slave
8259A的I/O端口地址,以及通过它们所能操作的寄存器。AddressRead/WriteFunction0xA0WriteInitialization Command Word 1
(ICW1)WriteOperation Command Word 2
(OCW2)WriteOperation Command Word 3
(OCW3)ReadInterrupt Request Register
(IRR)ReadIn-Service Register (ISR)0xA1WriteInitialization Command Word 2
(ICW2)WriteInitialization Command Word 3
(ICW3)WriteInitialization Command Word 4
(ICW4)Read/WriteInterrupt Mask Register
(IMR)Addresses/Registers for Slave
8259A由于8259A芯片不仅能够用于IBM
PC/X86,也可以被用作MCS-80/85,对于这两者,在操作模式上有一些不一样,对于某些寄存器的设置也有所不同。我们后面仅仅讨论X86模式相关的内容。2.5.5
Initialization当
主机Power-on或Reset之后,必须对两个8259A都进行初始化。事实上,BIOS已经这么做了。但不幸的是,BIOS对其进行的初始化的结果
并非我们所需要。比如,我们要开发Protected
Mode下OS,我们要设置自己的IDT,那么我们就不能使用BIOS设置的IVT,而在对8259A初始化操作中,我们需要告诉8259A,其相关中断
请求的起始向量号,而我们对IDT的中断向量布局和BIOS设置的IVT的中断向量布局可以是不一样的。这样,我们也需要对两个8259A进行初始化。任
何时候,只要向某一个8259A的第一个端口(0x20 for Master, and
0xA0 for
Slave)写入的命令的bit-4(从0算起)为1,那么这个8259A就认为这是一个ICW1;而一旦一个8259A收到一个ICW1,它就认为一个
初始化序列开始了。你可以通过对照上边的表和后面的表,第一端口可写的有ICW1,OCW2和OCW3。而ICW1的bit-4要求必须是1,但OCW2
和OCW3的bit-4要求必须是0。8259A的初始化流程协议如下图所示,程序员对其进行初始化时必须遵守此协议:ICW1Bit(s)Function7:5Interrupt Vector Addresses
for MCS-80/85 Mode.4Must be set to 1 for
ICW131Level Triggered
Interrupts0Edge Triggered Interrupts21Call Address Interval of
40Call Address Interval of
81 (SINGL)1Single PIC0Cascaded PICs0 (IC4)1Will be Sending ICW40Don't need
ICW4Initialization Command Word 1 (ICW1) 对于X86,bit-0必须被设置为1;由于当今的IBM
PC上都有两个级连的8259A,所以bit-1应该被设置为0;由于bit-2是为MCS-80/85服务的,我们将其设置为0;bit-3也设置为0;bit-4被要求必须设置为1;bit5:7是为MCS-80/85服务的,对于X86,应将全部将其设为0。所以,在X86系统上,ICW1应该被设置为二进制00010001 =
0x11。ICW2Bit80x86
Mode7I76I65I54I43I32010 00 Initialization Command Word 2 (ICW2) ICW2被用作指定本8259A中的中断请求的起始中断向量,bit0:3必须被设为0;所以,其起始中断向量必须是8的倍数。比如,我们的OS的设计讲来自于Master
8259A的8个中断请求放在IDT的第32 (从0开始计)个位置到第39个位置,则我们应该将ICW2设为0x20。这样,当将来此8259A上接收到一个IRQ时,其低3位会被自动填充为IRQ号。比如,其收到一个IRQ6,将6自动填充到后3位,则生成的向量号为0x26。8259A会在收到CPU发来的第二个INTA信号之后,将生成的向量号放到Data
Bus上。ICW3Master 8259A和Slave
8259A有不同的ICW3格式。BitFunction7IR7 is connected to a
Slave6IR6 is connected to a
Slave5IR5 is connected to a
Slave4IR4 is connected to a
Slave3IR3 is connected to a
Slave2IR2 is connected to a
Slave1IR1 is connected to a
Slave0IR0 is connected to a
SlaveInitialization Command Word 3 for Master 8259A (ICW3)
Slave 8259A被接在Master
8259A的那个IRQ上,则相应的位就被设置为1,其余的位都被设置为0。在IBM PC上,Slave 8259A被接在Master
8259A的IRQ2上,则此ICW3的值应该被设置为二进制00000100 = 0x04。Bit(s)Function7Reserved. Set to
06Reserved. Set to
05Reserved. Set to
04Reserved. Set to
03Reserved. Set to
02:0Slave ID000Slave 0001Slave 1010Slave 2011Slave 3100Slave 4101Slave 5110Slave 6111Slave
7Initialization Command Word 3 for Slaves
(ICW3) Slave
8259A的ICW3的bit3:7被保留,必须被设为0;而bit0:2被设置为此Slave 8259A被接在Master
8259A的哪个IRQ上。比如,在IBM PC上,Slave 8259A被接在Master
8259A的IRQ2上,则此ICW3应被设为0x02。ICW4Bit(s)Function7Reserved. Set to
06Reserved. Set to
05Reserved. Set to
041Special Fully Nested Mode0Not Special Fully Nested Mode
3:20xNon - Buffered Mode10Buffered Mode - Slave 11Buffered Mode - Master11Auto EOI0Normal EOI018086/8080 Mode0MCS-80/85
Initialization Command Word 4 (ICW4)
在80x86模式下,我们不需要使用8259A的特殊功能,因此我们将bit1:4都设为0,这意味使用默认的Full Nested
Mode,不使用Buffer,以及手动EOI模式;我们只需要将bit-0设为1,这也正是我们ICW0处提到的我们为什么必须要ICW4的原因。所以ICW4的值应该被设为0x01。所以我们可以用下列代码初始化2个8259A芯片:inline void
init_8259a(void)
{
/* icw1
*/
outb( 0x11, 0x20 ); /* master port A
*/
outb( 0x11, 0xA0 ); /* slave port A
*/ /* icw2
*/
outb( 0x20, 0x21 ); /* master offset of 0x20 in
the IDT */
outb( 0x28, 0xA1 ); /* slave offset
of 0x28 in the IDT */ /* icw3
*/
outb( 0x04, 0x21 ); /* slaves attached to IR line
2 */
outb( 0x02, 0xA1 ); /* this slave in IR line 2
of master */
/* icw4 */
outb( 0x01, 0x21 ); /* set as master
*/
outb( 0x01, 0xA1 ); /*set as slave
*/
}2.5.6
Operation一旦按照初始化协议初始化完成之后,程序员就可以在任何时候,以任何顺序向8259A发送操作控制字OCW了。OCW1BitPIC 2PIC 17Mask IRQ15Mask IRQ76Mask IRQ14Mask IRQ65Mask IRQ13Mask IRQ54Mask IRQ12Mask IRQ43Mask IRQ11Mask IRQ32Mask IRQ10Mask IRQ21Mask IRQ9Mask IRQ10Mask IRQ8Mask
IRQ0Operation Control Word 1 (OCW1) OCW1是用来做中断请求屏蔽用的操作控制字。如果你想屏蔽那个IRQ,只需要对照上表将相应的Bit置为1,然后发送给相应的8259A就可以了。比如我想屏蔽IRQ10,我只需要将0x0A写到端口0xA1。对应代码如下:outb(0x0A, 0xA1);OCW2Bit(s)Function7:5000Rotate in Auto EOI Mode
(Clear)001Non Specific EOI010Reserved 011Specific EOI100Rotate in Auto EOI Mode
(Set)101Rotate on Non-Specific
EOI110Set Priority Command (Use Bits 2:0)
111Rotate on Specific EOI (Use Bits
2:0)4Must be set to
03Must be set to
02:0000Act on IRQ 0 or 8001Act on IRQ 1 or 9010Act on IRQ 2 or 10011Act on IRQ 3 or 11100Act on IRQ 4 or 12101Act on IRQ 5 or 13110Act on IRQ 6 or 14111Act on IRQ 7 or
15Operation
Control Word 2 (OCW2) 通过将bit3:4设置为0,以说明这是一个OCW2。如果bit-6被设为1,则bit0:2有效,其操作则是面向某个IRQ的;否则将bit0:2设为0,其操作是面向整个8259A的所有IRQ的。我们一般只会用到No
Specific EOI——因为我们在初始化8259A时,制定的EOI
Mode为手动模式,所以当每次对应某个8259A芯片的IRQ的中断服务程序ISR执行结束后,都需要向8259A发送一个EOI,其对应的OCW2的值为0x20。需要注意的是,由于IBM
PC有2个级连的8259A,所以我们每次必须分别给两个都发一个。比如下面示例代码用来向两个8259A芯片发送EOI,它需要在针对来自于两个8259A芯片的中断的服务程序ISR末尾处被调用:inline void
send_eoi(void)
{
/* Send EOI to both master and
slave */
outb( 0x20, 0x20 ); /* master PIC
*/
outb( 0x20, 0xA0 ); /* slave PIC */
}OCW3Bit(s)Function7Must be set to
06:500Reserved01Reserved10Reset Special Mask 11Set Special Mask4Must be set to
03Must be set to
121Poll Command0No Poll Command1:000Reserved01Reserved10Next Read Returns Interrupt Request
Register 11Next Read Returns In-Service
RegisterOperation Control Word 3 (OCW3) 通过将Bit-3设为1,Bit-4设为0,以让8259A知道这是一个OCW3。OCW3中对我们最有意义的位是bit0:1,我们可以通过将bit-1设为1来通知8259A,下一个读端口的动作将要读取IRR或ISR寄存器的内容。比如下面示例C++代码用来读取Master
8259A的IRR寄存器内容到__irr变量中:void read_irr(unsigned char&
__irr)
{
outb(0x02, 0x20);
inb(&__irr, 0x20);
}2.5.7
Full Nested Mode为了让我们更加理解8259A的中断控制机理,我们需要说明一下Full Nested
Mode。在我们初始化时,只需要将ICW4的bit-4设为0,我们就选择了Full Nested
Mode。Full
Nested
Mode其实就是实现按照中断请求的优先级别进行抢断处理的机制——如果当前一个IRQ正在被CPU处理,也就是说,当前CPU正在调用其中断服务程序
ISR;这时8259A又接到了新的IRQ,如果此IRQ的优先级大于正在处理的IRQ,那么,此IRQ就会被提交给CPU以优先处理;否则此IRQ则被
放置在IRR中,直到所有的高优先级中断被处理结束为止。其处理过程大致如下:在
ISR寄存器中有一个8-bit的字节,范围为bit[0,7];每一个bit对应一个IRQ(IRQ0-IRQ7对应bit[0,7])。当一个IRQ
被提交给CPU之后(收到来自于CPU的第一个INTA信号之后),其对应的bit会被设置为1。比如IRQ6被提交给CPU之后,IS
Register的bit-6会被设置为1。当此8259A收到一个EOI之后(对于手动模式,这意味着一个优先级别最高的中断请求被处理结束),会将
IS
Register中被设置的最高优先级IRQ的对应的bit清为0。比如在收到一个EOI时,发现IS Register的bit-3, bit-5,
bit-6被设置,那么被清除的则是bit-3(越小优先级别越高)。在清除优先级最高的bit之后,8259A会到IRR中察看是否有优先级别高于当前
正在处理的IRQ中优先级别最高的IRQ,如果有,则将此IRQ提交给CPU处理,同时设置相应的bit。还以上面的例子为例,当bit-3被清除之后,
如果发现在IRR中有一个IRQ4等待被处理,则将其提交给CPU,在收到来自于CPU的第一个INTA信号之后,则将IS
Register的bit-4置为1。在此过程中,如果8259A接到更高优先级别的IRQ,则将其立即提交给CPU。比如,当前正在处理的IRQ为IRQ3,IRQ5,那么IS
Register中被设置的bit为bit-3,bit-5;如果此时接到一个IRQ1,则立即将其提交给CPU,在收到来自于CPU的第一个INTA信号之后,则将IS
Register的bit-1置为1。由
此过程我们也可以看出,为了实现这种优先级机制,必须将EOI设为手动模式,也就是说必须将ICW4的bit-1设为0。因为,对于自动EOI模
式,8259A会在收到来自于CPU的第2个INTA信号之后,就自动将IS
Register中此IRQ对应的bit清0,而事实上,这个时候此IRQ对应的中断服务程序还没有被CPU调用,也就是说此IRQ还没有被处理结束,而
由于此IRQ对应的bit已经被清除,如果此IRQ是一个优先级很高的话,那么此IRQ的处理完全可以被一个优先级别更低的IRQ所中断。这不是我们所需
要的。