8259 PIC: Difference between revisions

From OSDev Wiki
Jump to navigation Jump to search
Line 69: Line 69:


==Code Examples==
==Code Examples==
(削除) {{In Progress}} (削除ここまで)


===Common Definitions===
===Common Definitions===
Line 97: Line 96:


===Initialisation===
===Initialisation===
(削除) there (削除ここまで)'(削除) s also (削除ここまで)the (削除) "initialize" (削除ここまで)command (code 0x11)(削除) , which (削除ここまで)makes the PIC wait for 3 extra "(削除) initialization (削除ここまで)words" on the data port. (削除) Those data (削除ここまで)bytes (削除) gives (削除ここまで)the PIC
(追記) When you enter protected mode (or even before hand, if you (追記ここまで)'(追記) re not using [[GRUB]]) (追記ここまで)the (追記) first command you will need to give the two PICs is the initialise (追記ここまで)command (code 0x11)(追記) . This command (追記ここまで)makes the PIC wait for 3 extra "(追記) initialisation (追記ここまで)words" on the data port. (追記) These (追記ここまで)bytes (追記) give (追記ここまで)the PIC(追記) : (追記ここまで)
* (削除) its (削除ここまで)vector offset (ICW2)(削除) , (削除ここまで)
* (追記) Its (追記ここまで)vector offset(追記) . (追記ここまで)(ICW2)
* (削除) tell (削除ここまで)it how it is wired to master/slaves (ICW3)
* (追記) Tell (追記ここまで)it how it is wired to master/slaves(追記) . (追記ここまで)(ICW3)
* (削除) gives (削除ここまで)additional (削除) infos (削除ここまで)about the environment (ICW4)
* (追記) Gives (追記ここまで)additional (追記) information (追記ここまで)about the environment(追記) . (追記ここまで)(ICW4)


<pre>
<pre>
Line 106: Line 105:
rather than 8 and 70, as configured by default */
rather than 8 and 70, as configured by default */


#define ICW1_ICW4 (削除) (削除ここまで)0x01 (削除) (削除ここまで)/* ICW4 (not) needed */
#define ICW1_ICW4 0x01 (追記) (追記ここまで)/* ICW4 (not) needed */
#define ICW1_SINGLE (削除) (削除ここまで)0x02 (削除) (削除ここまで)/* Single (cascade) mode */
#define ICW1_SINGLE 0x02 (追記) (追記ここまで)/* Single (cascade) mode */
#define ICW1_INTERVAL4 (削除) (削除ここまで)0x04 (削除) (削除ここまで)/* Call address interval 4 (8) */
#define ICW1_INTERVAL4 0x04 (追記) (追記ここまで)/* Call address interval 4 (8) */
#define ICW1_LEVEL (削除) (削除ここまで)0x08 (削除) (削除ここまで)/* Level triggered (edge) mode */
#define ICW1_LEVEL 0x08 (追記) (追記ここまで)/* Level triggered (edge) mode */
#define ICW1_INIT (削除) (削除ここまで)0x10 (削除) (削除ここまで)/* Initialization - required! */
#define ICW1_INIT 0x10 (追記) (追記ここまで)/* Initialization - required! */


#define ICW4_8086 (削除) (削除ここまで)0x01 (削除) (削除ここまで)/* 8086/88 (MCS-80/85) mode */
#define ICW4_8086 0x01 (追記) (追記ここまで)/* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO (削除) (削除ここまで)0x02 (削除) (削除ここまで)/* Auto (normal) EOI */
#define ICW4_AUTO 0x02 (追記) (追記ここまで)/* Auto (normal) EOI */
#define ICW4_BUF_SLAVE (削除) (削除ここまで)0x08 (削除) (削除ここまで)/* Buffered mode/slave */
#define ICW4_BUF_SLAVE 0x08 (追記) (追記ここまで)/* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C (削除) (削除ここまで)/* Buffered mode/master */
#define ICW4_BUF_MASTER 0x0C (追記) (追記ここまで)/* Buffered mode/master */
#define ICW4_SFNM (削除) (削除ここまで)0x10 (削除) (削除ここまで)/* Special fully nested (not) */
#define ICW4_SFNM 0x10 (追記) (追記ここまで)/* Special fully nested (not) */


/*
/*
(削除) (削除ここまで)arguments:
arguments:
(削除) (削除ここまで)offset1 - vector offset for master PIC
(追記) (追記ここまで)offset1 - vector offset for master PIC
(削除) (削除ここまで)vectors on the master become offset1..offset1+7
(追記) (追記ここまで)vectors on the master become offset1..offset1+7
(削除) (削除ここまで)offset2 - same for slave PIC: offset2..offset2+7
(追記) (追記ここまで)offset2 - same for slave PIC: offset2..offset2+7
(削除) (削除ここまで)*/
*/
void (削除) remap_pics (削除ここまで)(int offset1, int offset2)
void (追記) PIC_remap (追記ここまで)(int offset1, int offset2)
{
{
(削除) UCHAR (削除ここまで)a1, a2;
(追記) unsigned char (追記ここまで)a1, a2;
(追記) (追記ここまで)
(削除) (削除ここまで)a1=inb(PIC1_DATA); // save masks
(追記) (追記ここまで)a1 = inb(PIC1_DATA); // save masks
(削除) (削除ここまで)a2=inb(PIC2_DATA);
(追記) (追記ここまで)a2 = inb(PIC2_DATA);
(追記) (追記ここまで)
(削除) (削除ここまで)outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); // starts the initialization sequence
(追記) (追記ここまで)outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); // starts the initialization sequence
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(削除) (削除ここまで)outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
(追記) (追記ここまで)outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(削除) (削除ここまで)outb(PIC1_DATA, offset1); // define the PIC vectors
(追記) (追記ここまで)outb(PIC1_DATA, offset1); // define the PIC vectors
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(削除) (削除ここまで)outb(PIC2_DATA, offset2);
(追記) (追記ここまで)outb(PIC2_DATA, offset2);
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(削除) (削除ここまで)outb(PIC1_DATA, 4); // continue initialization sequence
(追記) (追記ここまで)outb(PIC1_DATA, 4); // continue initialization sequence
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(削除) (削除ここまで)outb(PIC2_DATA, 2);
(追記) (追記ここまで)outb(PIC2_DATA, 2);
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(追記) (追記ここまで)
(削除) (削除ここまで)outb(PIC1_DATA, ICW4_8086);
(追記) (追記ここまで)outb(PIC1_DATA, ICW4_8086);
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(削除) (削除ここまで)outb(PIC2_DATA, ICW4_8086);
(追記) (追記ここまで)outb(PIC2_DATA, ICW4_8086);
(削除) (削除ここまで)io_wait();
(追記) (追記ここまで)io_wait();
(追記) (追記ここまで)
(削除) (削除ここまで)outb(PIC1_DATA, a1); // restore saved masks.
(追記) (追記ここまで)outb(PIC1_DATA, a1); // restore saved masks.
(削除) (削除ここまで)outb(PIC2_DATA, a2);
(追記) (追記ここまで)outb(PIC2_DATA, a2);
}
}
</pre>
</pre>

Revision as of 05:43, 23 November 2007

The Programmable Interrupt Controller (PIC) is one of the most important chips making up the x86 architecture. Without it, the x86 architecture would not be an interrupt driven architecture. The function of the chipset (made up mostly of a set of 8259As) is to manage hardware interrupts and send them to the appropriate system interrupt. This allows the system to respond to devices needs without loss of time (from polling the device, for instance).

It is important to note that APIC has replaced PIC in more modern systems, especially those with multiple cores/processors.

What does the PIC do?

The 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's 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 the PIC interface, you would have to poll all the devices in the system to see if they want to do anything (signal an event), but with the 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.

The IBM/PC PIC Architecture

In the beginning (IBM PC and XT), only a single PIC chip was used, which provided 8 IRQs to the system. These were traditionally mapped by the BIOS to interrupts 8 to 15 (0x08 to 0x0F). It is unlikely that any of these single-PIC machines will be encountered these days.

The IBM/AT PIC Architecture

The IBM AT extended the PC architecture by adding a second 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... But to confuse things more, IRQ 9 is redirected to IRQ 2. So when you get an IRQ 9, the signal is redirected to IRQ 2. 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 PIC chip work?

Each of the two PICs in modern systems have 8 inputs. When any of the inputs is raised, the PIC sets a bit internally telling one of the inputs needs servicing. It then checks whether that channel is masked or not, and whether there's an interrupt already pending. If the channel is unmasked and there's no interrupt pending, the PIC will raise the interrupt line. On the slave, this feeds IRQ 2 to the master, and the master is connected to the processor interrupt line.

When the processor accepts the interrupt, the master checks which of the two PICs is responsible for answering, then either supplies the interrupt number to the processor, or asks the slave to do so. The PIC that answers looks up the "vector offset" variable stored internally and adds the input line to form the requested interrupt number. After that the processor will look up the interrupt address and act accordingly (see Interrupts for more details).

Programming with the PIC Chips

Each chip (master and slave) has a command port and a data port (given in the table below). When no command is issued, the data port allows us to access the interrupt mask of the PIC.

Chip - Purpose I/O Port
Master PIC - Command 0x0020
Master PIC - Data 0x0021
Slave PIC - Command 0x00A0
Slave PIC - Data 0x00A1
  • Each PIC vector offset must be divisible by 8, as the 8259A uses the lower 3 bits for the interrupt number of a particular interrupt (0..7).
  • The only way to change the vector offsets used by the PIC is to re-initialize it, which explains why the code is "so long" and plenty of things that have apparently no reasons to be here.
  • If you plan to return to real mode from protected mode (for any purpose), you really must restore the PIC to its former configuration.

Real Mode

Chip Interrupt numbers (IRQ) Vector offset Interrupt Numbers
Master PIC 0 to 7 0x08 0x08 to 0x0F
Slave PIC 8 to 15 0x70 0x70 to 0x77

These default BIOS values suit real mode programming quite well; they do not conflict with any CPU exceptions like they do in protected mode.

Protected Mode

In protected mode, the IRQs 0 to 7 conflict with the CPU exception which are reserved by Intel up until 0x1F. Consequently it is difficult to tell the difference between an IRQ or an software error. It is thus recommended to change the PIC's offsets (also known as remapping the PIC) so that IRQs use non-reserved vectors. A common choice is to move them to the beginning of the available range (IRQs 0..0xF -> INT 0x20..0x2F). For that, we need to set the master PIC's offset to 0x20 and the slave's to 0x28. For code examples, see below.

Code Examples

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 this page.

#define PIC1		0x20		/* IO base address for master PIC */
#define PIC2		0xA0		/* IO base address for slave PIC */
#define PIC1_COMMAND	PIC1
#define PIC1_DATA	(PIC1+1)
#define PIC2_COMMAND	PIC2
#define PIC2_DATA	(PIC2+1)

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.

#define PIC_EOI		0x20		/* End-of-interrupt command code */
void PIC_sendEOI(unsigned char irq)
{
	if(irq >= 8)
		outb(PIC2_COMMAND,PIC_EOI);
	
	outb(PIC1_COMMAND,PIC_EOI);
}

Initialisation

When you enter protected mode (or even before hand, if you're not using GRUB) the first command you will need to give the two PICs is the initialise command (code 0x11). This command makes the PIC wait for 3 extra "initialisation words" on the data port. These bytes give the PIC:

  • Its vector offset. (ICW2)
  • Tell it how it is wired to master/slaves. (ICW3)
  • Gives additional information about the environment. (ICW4)
/* reinitialize the PIC controllers, giving them specified vector offsets
 rather than 8 and 70, as configured by default */
#define ICW1_ICW4	0x01		/* ICW4 (not) needed */
#define ICW1_SINGLE	0x02		/* Single (cascade) mode */
#define ICW1_INTERVAL4	0x04		/* Call address interval 4 (8) */
#define ICW1_LEVEL	0x08		/* Level triggered (edge) mode */
#define ICW1_INIT	0x10		/* Initialization - required! */
#define ICW4_8086	0x01		/* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO	0x02		/* Auto (normal) EOI */
#define ICW4_BUF_SLAVE	0x08		/* Buffered mode/slave */
#define ICW4_BUF_MASTER	0x0C		/* Buffered mode/master */
#define ICW4_SFNM	0x10		/* Special fully nested (not) */
/*
arguments:
	offset1 - vector offset for master PIC
		vectors on the master become offset1..offset1+7
	offset2 - same for slave PIC: offset2..offset2+7
*/
void PIC_remap(int offset1, int offset2)
{
	unsigned char a1, a2;
	
	a1 = inb(PIC1_DATA); // save masks
	a2 = inb(PIC2_DATA);
	
	outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); // starts the initialization sequence
	io_wait();
	outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
	io_wait();
	outb(PIC1_DATA, offset1); // define the PIC vectors
	io_wait();
	outb(PIC2_DATA, offset2);
	io_wait();
	outb(PIC1_DATA, 4); // continue initialization sequence
	io_wait();
	outb(PIC2_DATA, 2);
	io_wait();
	
	outb(PIC1_DATA, ICW4_8086);
	io_wait();
	outb(PIC2_DATA, ICW4_8086);
	io_wait();
	
	outb(PIC1_DATA, a1); // restore saved masks.
	outb(PIC2_DATA, a2);
}

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

External Links

Retrieved from "https://wiki.osdev.org/index.php?title=8259_PIC&oldid=4920"