8259 PIC: Difference between revisions

2,745 bytes added ,  26 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
(further explanation)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(19 intermediate revisions by 16 users not shown)
Line 4:
 
==What does the 8259 PIC do?==
The 8259 PIC controls the CPU's interrupt mechanism, by accepting several interrupt requests and feeding them to the processor in order. For instance, when a keyboard registers a keyhit, it sends a pulse along it'sits interrupt line ([[IRQ]] 1) to the PIC chip, which then translates the IRQ into a system interrupt, and sends a message to interrupt the CPU from whatever it is doing. Part of the kernel's job is to either handle these IRQs and perform the necessary procedures (poll the keyboard for the scancode) or alert a userspace program to the interrupt (send a message to the keyboard driver).
 
Without a PIC, you would have to poll all the devices in the system to see if they want to do anything (signal an event), but with a PIC, your system can run along nicely until such time that a device wants to signal an event, which means you don't waste time going to the devices, you let the devices come to you when they are ready.
Line 14:
The IBM PC/AT extended the PC architecture by adding a second 8259 PIC chip. This was possible due to the 8259A's ability to cascade interrupts, that is, have them flow through one chip and into another. This gives a total of 15 interrupts. Why 15 and not 16? That's because when you cascade chips, the PIC needs to use one of the interrupt lines to signal the other chip.
 
Thus, in an AT, IRQ line 2 is used to signal the second chip... ButBecause toof confuse things morethis, IRQ 92 is redirectednot available for use by hardware devices, which got wired to IRQ 29 on the slave PIC instead. SoThe whenreal youmode getBIOS used to set up an interrupt handler for IRQ 9, that redirects to the signalIRQ is2 redirectedhandler. toThis way DOS drivers who used IRQ 2 continued to work. This two-chip architecture is still used and available in modern systems, and hasn't changed (except for the advent of the above-mentioned APIC architecture).
 
==How does the 8259 PIC chip work?==
Line 27:
|-
! Chip - Purpose
! [[I/O Portports|I/O port]]
|-
| Master PIC - Command
Line 72:
===Common Definitions===
This is just a set of definitions common to the rest of this section. For the outb(), inb() and io_wait() functions, see [[Inline_Assembly/Examples#I/O_access|this page]].
<sourcesyntaxhighlight lang="C">
#define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */
Line 79:
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
</syntaxhighlight>
</source>
 
===End of Interrupt===
Perhaps the most common command issued to the PIC chips is the ''end of interrupt'' (EOI) command (code 0x20). This is issued to the PIC chips at the end of an IRQ-based interrupt routine. If the IRQ came from the Master PIC, it is sufficient to issue this command only to the Master PIC; however if the IRQ came from the Slave PIC, it is necessary to issue the command to both PIC chips.
<sourcesyntaxhighlight lang="C">
#define PIC_EOI 0x20 /* End-of-interrupt command code */
 
void PIC_sendEOI(unsigned charuint8_t irq)
{
if(irq >= 8)
Line 93:
outb(PIC1_COMMAND,PIC_EOI);
}
</syntaxhighlight>
</source>
 
===Initialisation===
Line 101:
* Gives additional information about the environment. (ICW4)
 
<sourcesyntaxhighlight lang="C">
/* reinitialize the PIC controllers, giving them specified vector offsets
rather than 88h and 7070h, as configured by default */
 
#define ICW1_ICW4 0x01 /* Indicates that ICW4 (not)will be neededpresent */
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
Line 125:
void PIC_remap(int offset1, int offset2)
{
unsigned charuint8_t a1, a2;
a1 = inb(PIC1_DATA); // save masks
a2 = inb(PIC2_DATA);
outb(PIC1_COMMAND, ICW1_INIT+ | ICW1_ICW4); // starts the initialization sequence (in cascade mode)
io_wait();
outb(PIC2_COMMAND, ICW1_INIT+ | ICW1_ICW4);
io_wait();
outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset
Line 143:
io_wait();
outb(PIC1_DATA, ICW4_8086); // ICW4: have the PICs use 8086 mode (and not 8080 mode)
io_wait();
outb(PIC2_DATA, ICW4_8086);
Line 151:
outb(PIC2_DATA, a2);
}
</syntaxhighlight>
</source>
 
''Note the presence of io_wait() calls, on older machines its necessary to give the PIC some time to react to commands as they might not be processed quickly''
 
== Disabling ==
If you are going to use the processor local APIC and the IOAPIC, you must first disable the PIC. This is done via:by masking every single interrupt.
<sourcesyntaxhighlight lang=asm"C">mov al, 0xff
void pic_disable(void) {
out 0xa1, al
outb(PIC1_DATA, 0xff);
out 0x21, al
outb(PIC2_DATA, 0xff);
</source>
}
</syntaxhighlight>
 
== Masking ==
The PIC has an internal register called the IMR, or the Interrupt Mask Register. It is 8 bits wide. This register is a bitmap of the request lines going into the PIC. When a bit is set, the PIC ignores the request and continues normal operation. Note that setting the mask on a higher request line will not affect a lower line. HereMasking isIRQ2 anwill examplecause ofthe howSlave PIC to dostop raising this:IRQs.
 
<source lang="C">
Here is an example of how to mask an IRQ:
void IRQ_set_mask(unsigned char IRQline) {
<sourcesyntaxhighlight lang="C">
void IRQ_set_mask(unsigned charuint8_t IRQline) {
uint16_t port;
uint8_t value;
Line 179 ⟶ 183:
}
 
void IRQ_clear_mask(unsigned charuint8_t IRQline) {
uint16_t port;
uint8_t value;
Line 192 ⟶ 196:
outb(port, value);
}
</syntaxhighlight>
</source>
 
==ISR and IRR==
The PIC chip has two interrupt status registers: the In-Service Register (ISR) and the Interrupt Request Register (IRR). The ISR tells us which interrupts are being serviced, meaning IRQs sent to the CPU. The IRR tells us which interrupts have been raised. Based on the interrupt mask (IMR), the PIC will send interrupts from the IRR to the CPU, at which point they are marked in the ISR.
 
The ISR and IRR can be read via the OCW3 command word. This is a command sent to one of the command ports (0x20 or 0xa0) with bit 3 set. To read the ISR or IRR, write the appropriate command to the command port, and then read the command port (not the data port). To read the IRR, write 0x0a. To read the ISR, write 0x0b.
 
The ISR and IRR are each 8 bits. Here is an example of how to read 16 bits worth of ISR and IRR data from two cascaded PICs:
<syntaxhighlight lang="C">
#define PIC_READ_IRR 0x0a /* OCW3 irq ready next CMD read */
#define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */
 
/* Helper func */
static uint16_t __pic_get_irq_reg(int ocw3)
{
/* OCW3 to PIC CMD to get the register values. PIC2 is chained, and
* represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */
outb(PIC1_COMMAND, ocw3);
outb(PIC2_COMMAND, ocw3);
return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND);
}
 
/* Returns the combined value of the cascaded PICs irq request register */
uint16_t pic_get_irr(void)
{
return __pic_get_irq_reg(PIC_READ_IRR);
}
 
/* Returns the combined value of the cascaded PICs in-service register */
uint16_t pic_get_isr(void)
{
return __pic_get_irq_reg(PIC_READ_ISR);
}
</syntaxhighlight>
 
Note that these functions will show bit 2 (0x0004) as on whenever any of the PIC2 bits are set, due to the chained nature of the PICs. Also note that it is not necessary to reset the OCW3 command every time you want to read. Once you set it for either the IRR or the ISR, future reads of the CMD port will return the appropriate register. The chip remembers what OCW3 setting you used. (Disclaimer: I have not tested this last part, but that's what the spec says.)
 
==Spurious IRQs==
Line 210 ⟶ 249:
The correct way to handle an IRQ 15 is similar, but a little trickier due to the interaction between the slave PIC and the master PIC. First check the slave PIC chip's ISR to see if the IRQ is a spurious IRQ or a real IRQ. If it is a real IRQ then it is treated the same as any other real IRQ. If it's a spurious IRQ then don't send the EOI to the slave PIC; however you will still need to send the EOI to the master PIC because the master PIC itself won't know that it was a spurious IRQ from the slave.
 
Also note that some operating systemskernels (e.g. Linux) keep track of the number of spurious IRQs that have occurred (e.g. by incrementing a counter when a spurious IRQ occurs). This can be useful for detecting problems in software (e.g. sending EOIs at the wrong time) and detecting problems in hardware (e.g. line noise).
 
 
Line 223 ⟶ 262:
=== External Links ===
* [http://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf Intel Datasheet]
* http://www.brokenthorn.com/Resources/OSDevPic.html
* http://helppc.netcore2k.net/hardware/8259
 
 
[[Category:Interrupts]]
[[Category:X86]]
[[de:Programmable Interrupt Controller]]