![]() |
|
Operating Systems Development Series | |
This series is intended to demonstrate and teach operating system development from the ground up. ![]() 8259A PIC Microcontroller with all pins labled. IntroductionWelcome! :)This tutorial covers a very important topic: The Programmable Interrupt Controller. We will need to initialize this microcontroller by mapping it to our IRQ's. This will be needed when setting up interrupts, and handling interrupt requests. This is our first controller tutorial. All of these controller tutorials go very deep in each device, while building a workable interface to handling them. Remember that, as we are in protected mode, we have nothing to guide us. One wrong move can cause unpredictable results. As we have no helping hand, we have to communicate with each controller directly. Because of this, we have emphisized hardware programming concepts all througout this series so our readers have more experience and better understanding of hardware level programming. This tutorial puts everything we learned to the test. I will do my best to keep things simple. the 8259A Microcontroller, Also known as the Programmable Interrupt Controller (PIC). Ready? Get ReadyThis is our first of many microcontroller programming tutorials. We will cover nearly every asset of each microcontroller as we cover them. The main series will refrence these tutorials on an as needed bases to help cover what we need these controllers for.This tutorial is fairly complicated. We will cover the 8259A Microcontroller from both hardware and software perspectives, and understand exactally how it connects and enteracts with the PC. We will also cover every command, register, and part of this microcontroller. History*To do - We plan on adding this section soon*Because the 8259A PIC handles hardware interrupts, we should first have a basic understanding of what interrupts are, and how they work. InterruptsAn Interrupt is an external asynchronous signal requiring a need for attention by software or hardware. It allows a way of interrupting the current task so that we can execute something more important.Not to hard. Interrupts provide a way to help trap problems, such as divide by zeros. If the processor find a problem with the currently executing code, it provides the processor alternative code to execute to fix that problem. Other interrupts may be used to provide a way to service software as routines. These interrupts can be called by any software from within the system. This is used alot for System API's, which provide a way for ring 3 applications to execute ring 0 level routines. Interrupts provide alot of use, espically as a way of recieving information from hardware that may change its state at asynchronous times. Interrupt TypesThere are two types of interrupts: Software Interrupts and Hardware Interrupts.Software InterruptsSoftware Interrupts are interrupts implimented and triggered in software. Normally, the processor's instruction set will provide an instruction to service software interrupts. For the x86 architectures, these are normally INT imm, and INT 3. It alsu uses IRET and IRETD instructions.For example, here we generate an interrupt through a software instruction:
These instructions may be used to generate software interrupts and execute Interrupt Routines (IR)'s through software. We will not cover software interrupts here. The 8259A PIC Micrcontroller only services hardware interrupts. Software interrupts will be covered in another tutorial. Hardware InterruptsA hardware interrupt is an interrupt triggered by a hardware device. Normally, these are hardware devices that require attention. The hardware Interrupt handler will be required to service this hardware request.Spurious Interrupt This is a hardware interrupt generated by electrical interference in the interrupt line, or faulty hardware. We do NOT want this! Interrupt ModesThere are several modes and classes of interrupts that we will need to cover. In programming the PIC, we will need to choose a mode.Note: This section may require some knowedge of the 8259A PIC hardware pin layout. this is discussed in the next section. Level TriggeredA Level Triggered interrupt is determined to happen when the Interrupt Request (IR) line on the PIC has current (1). A device sends a signal (Setting this line to active), and keeps it at that state until the interrupt is serviced.Level Triggered interrupt lines may be shared by multiple interrupts if the circuit is designed to handle it. This mode is the preferred mode because of how the lines are shared. When an IR line is active, the CPU searches through all of the devices sharing the same line until it finds what device is activating the signal. After finding the device, the CPU rechecks all of the devices again to insure there are no other devices that also need service. A problem with this approch is, if there is an interrupt with higher priority that needs to be serviced, all other interrupts will be perminately blocked until the other interrupts are serviced. After all, only one line can be active at a given time. Edge TriggeredEdged Triggered interrupts are determnined to happen when the Interrupt Request (IR) line on the PIC has current (1). A device sends a signal (Setting this line to active) through a single pulse, and returns the line to its previous state.Edged Triggered interrupt lines may be shared by multiple interrupts if the circuit is designed to handle it. If the pulse is too short to be detected, then it will not be detected. As these are only pulses of current that signals interrupt requests, Edged triggered mode does not have the same problems that Level triggered does with shared IRQ lines. Of course, we still run into the possibility of an interrupt being missed, as it is just a single pulse of current being sent through the IRQ line. This has caused early computer lockups of the CPU. However, through recent times, these lockups have decreased through time. HybridBoth of these modes have their pros and cons. Alot of systems impliment a hybrid of both of them. More specifically, Most systems check for both Edge triggered and Level triggered interrupts on the Non Maskable Interrupt (NMI) pin on the CPU. The purpose of this is that the NMI pin is used to signal major problems with the system that can cause big problems, or entire system malfunctions, possibly hardware damage.The Non Maskable Interrupt is just that -- It cannot be disabled or masked off by any device. This insures, along with having a hybrid setup, that if the NMI pin is set, the system can die peacefully without big problems. Message SignaledThese types of hardware interrupts do not use a physical interrupt line. Instead, they rely on another medium, such as the system bus, to send messages over. These types of interrupts cause the device to only send a pulse of current over the medium, similar to edge triggered interrupts. These types of systems may use a special interrupt line on its control bus indicating a message signaled interrupt number. As these numbers are sent over the medium as a series of bits, they do not have the limitations of the other interrupt types, which are limited to a single interrupt line. As such, they can manage as much interrupts as the underlaying system allows. These types of interrupts also support sharing of interrupt vectors. PCI Express uses these types of interrupts alot.Thats allOkay, alot of info here ;) The 8259A only has support for Level triggered and Edge triggered interrupts. Because of this, Those should be your primary focus when working with the 8259A Microcontrollers.Interrupt Vector TableThe Interrupt Vector Table (IVT) is a list of Interrupt Vectors. There are 256 Interrupts in the IVT.
Interrupt Routines (IR)An Interrupt Routine (IR) is a special function used to handle an Interrupt Request (IRQ).When the processor executes an interrupt instruction, such as INT, it executes the Interrupt Routine (IR) at that location within the Interrupt Vector Table (IVT). This means, it simply executes a routine that we define. Not to hard, huh? This special routine determins the Interrupt Function to execute normally based off of the value in the AX register. This allows us to define multiple functions in an interrupt call. Such as, the DOS INT 21h function 0x4c00. Remember: Executing an interrupt simply executes an interrupt routine that you created. For example, the instruction INT 2 will execute the IR at index 2 in the IVT. Cool? IVT MapThe IVT is located in the first 1024 bytes of physical memory, from addresses 0x0 through 0x3FF. Each entry inside of the IVT is 4 bytes, in the following format:
Okay, Lets take a look at the IVT. The first few interrupts are reserved, and stay the same.
Not to hard. Each of these interrupts are located at a base address within the IVT. Interrupt Handling in Protected Mode (PMode)Protected Mode requires each IVT entry to point to an interrupt routine (IR) defined within an Interrupt Descriptor Table (IDT). The IDT will be explained further in another tutorial, as it is not directly related to this tutorial.The IDT is an array of Interrupt Descriptors, that describe the base address of the Interrupt Routine (IR) to execute, that contains extra information about it's protection level, segment information, etc. PMode uses a Global Descriptor Table (GDT) that defines the memory map that is being used. Most of the interrupt routines will be inside of a code descriptor, mapped by the GDT. This is why the IDT is required in PMode. Do not worry if you do not understand this right now. For now, just think of it as an array of 256 function pointers, mapped exactally like that of the IVT (It normally is, anyways.) Hardware InterruptsThere are two types of interrupts, those generated by software (Useually by an instruction, such as INT, INT 3, BOUND, INTO), and an interrupt generated by hardware.Hardware interrupts are very important for PC's. It allows other hardware devices to signal the CPU that something is about to happen. For example, a keystroke on the keyboard, or a single clock tick on the internal timer, for example. We will need to map what Interrupt Request (IRQ) to generate when these interrupts happen. This way, we have a way to track these hardware changes. Lets take a look at these hardware interrupts.
You do not need to worry to much about each device just yet. The 8259A Pins will be described in detail within the next section. The Interrupt Numbers listed in this table are the default DOS interrupt requests (IRQ) to execute when these events trigger. In most cases, we will need to recreate a new interrupt table. As such, most operating systems need to remap the interrupts the PIC's use to insure they call the proper IRQ within their IVT. This is done for us by the BIOS for the real mode IVT. We will cover how to do this later in this tutorial as well. 8259 Programmable Interrupt ControllerThe 8259 Microcontroller familiy is a set of Programmable Interrupt Controller (PIC) Integrated Circuits (ICs). Look back again at Tutorial 7... Under the Processor Architecture section, Notice that the processor has it's own internal PIC Microcontroller. This is very important to note.Do to limitations in the circuit design, a PIC only supports 8 IRQ's. This is a big limitation. As additional devices were created, IBM quickly realized that this limitation is very bad. Because of this, Most motherboards contain a secondary (Slave) PIC microcontroller to work with the primary PIC inside the processor. Today, this is very common. A single PIC can be "cascaded" (capable of working with) another PIC. This makes it possible to support more IRQ's with additional PICs. The More PIC's supported, the more IRQ's can be handled. They can be cascaded to support up to 64 IRQ's. Cool?
Remember: Most computers have 2 PIC's, 1 inside the processor, and 1 on the motherboard. Some systems may not have this. Not to hard :) 8259 HardwareUnderstanding how microcontrollers work at the hardware level will help in understanding how the software side of things work. Remember that the PIC's are only used during a hardware interrupt.8259A MicrocontrollerAt the top of this tutorial, there is an image of an actual 8259 Dual Inline Package (DIP), with all of the electronic pins labled. To make things more understandable, we are going to represent the controller using a simpler graphic. The only pins these graphics do not display that the 8259 has are GND (Ground) and Vcc (Input Voltage). You can see these pins labled in the picture on the top of this tutorial. Lets first look at what we are going to be programming:![]() Thats it--The 8259A Programmable Interrupt Controller. Each of the lines in the above image displays each of the controllers electronic pins. These electronic pins are the connections between the controller and the rest of the system. This is the chip that we will need to program in order to handle IRQ's within an operating system. Let's look at this closer at each pin. I bolded the important pins.
Remember that we can connect PIC's together. This allows us to provide support for up to 64 IR numbers. In other words--64 hardware interrupts. CAS0, CAS1, and CAS2 pins provide a way to send signals between these PIC's. Look at the INT and INTA pins. Remember from the Processor Perspective section that the processors' own INT and INTA pins connect to these pins on the PIC. Remember that, when about to execute an interrupt, the processor clears the Interrupt (IF) and Trap flags (TF) from the FLAGS register, which disables the INTR pin. The PIC's INT pin connects to the processors' INTR pin. This means that the processor, essentally, disables the PIC's INT pin when executing an interrupt. With this, the pins IR0-IR7 can be streamed to other PIC's. These 8 pins represent the 8 bit interrupt number to be executed. Notice that this, as being an 8 bit value, provides a way to allow up to 256 hardware interrupts. these lines provide a way to send the interrupt number to another PIC controller, so that controller could handle it instead. The important thing to note is that We can combine multiple PIC's to support more interrupt routine numbers. The IR lines connect to another PIC's data lines to transfer data over. As there are only 8 lines (8 bits), we can only connect up to 8 PIC's together, providing support for up to 64 interrupt numbers. Okay... Alot of stuff here, huh? We have described how the processor connects to the primary PIC, and how the PIC's can combine with other PIC's to create a chain of PIC's. This is great, but completely useless. How does an interrupt execute through hardware? What makes this controller "programmable"? How can we program the PIC to work for our needs? Programming the PIC revolves around the use of sending Command Bytes through the 8 bit data line that the PIC's have. This 8 bit command byte follows specific formats that describe what the PIC is to do. We will need to know these commands in order to program the PICs. We w8ill cover this later. Lets take a closer look at how the PIC works. This will help in better understanding of the 8259A pins, and how interrupt signals are sent. 8259A ConnectionsNote: This section may require some knowledge in Digital Logic Electronics. Okay... So far we have looked at the 8259A PIC pins. Lets try to look at these pins from another perspective, and see what it looks like within a typical computer.Connecting the PICs to the processorFirst, remember when I said that most computers today have 2 8259A PICs? This is only half true. Remember from the Processor Architceture that the primary PIC is integrated into the processor. There is a reason for this, as you will soon see. To make things simple, lets imagine the system we are on actually have 2 PIC controllers, both directly on the motherboard (None of them are integrated with the processor.) Looking at this graphically, this is what we might see:![]() How hardware interrupts executeOn the underside of all microprocessors contain connectors. These can be flat, or in the form of pins, that connects to the motherboard. Two of these pins are the INTR and NMI pins. With this, there is another pin for acknowledges the completion of the interrupt - INTA. Software interrupts are handled differently then hardware interrupts. Both of these types of interrupts are inside of the Interrupt Vector Table located at address 0 through 0x3ff in memory. Remember: Only hardware interrupts are handled through the Programmable Interrupt Controller. The interrupt is generatedWhen a device controller needs to generate an interrupt, it needs to signal the PIC somehow. Lets say, for purposes of discussion, that this device is the timer, which uses interrupt line 0.
The processor now knows that an interrupt has been fired. The processor acnowledges the interrupt
Now the processor has the IRQ number and the interrupt vector number to execute. Interruption
The Interrupt Service RoutineNow the ISR is executing to handle the hardware interrupt. It can perform whatever action needed to service the specific device. For example, reading or writing data to/from the device, reading status registers, sending commands, et al.During this time, all interrupts are masked out by the Interrupt Mask Register (IMR). In other words, this disables all hardware interrupts until a request has been made to end the interrupt. this requires an End of Interrupt (EOI) command to be sent to the PIC. After the EOI signal has been sent to the PIC through the Primary PIC's Command Register, The PIC cleares the approprate bit in the In Service Register (IRR), and is now ready to service new interrupts. the interrupt service routine then performs a IRETD instruction, popping EFLAGS, CS, and EIP registers, which were pushed by the processor when the interrupt was fired. This transfers control back to the initil task. 8259A RegistersThe 8259A has several internal registers, simular to the processor.
Command RegisterThis is a write only register that is used to send commands to the microcontroller. There are alot of different commands that you can send. Some commands are used to read from other registers, while other command are used to initialize and sending data, such as End of Interrupt (EOI). We will cover these commands later.Status registerThis is a read only register that can be accessed to determin the status of the PIC.Interrupt Request Register (IRR)This register specifies which interrupts are pending acknowledgment.Note: This register is internal, and cannot be accessed directly.
If a bit is set, the interrupt has been signaled by a device, and the PIC has signaled the CPU, but is awaiting acknowledgment from the CPU to go ahead with the interrupt. In-Sevice Register (ISR)This register specifies which interrupts have already been acknowledged, but are awaiting for the End of Interrupt (EOI) signal. The EOI signal is very important as it determins the end of an interrupt.Note: We will need to send the EOI signal upon completion of the interrupt to let the 8259A acknowledge the interrupt. Not doing so will result in undefined behavior or malfunction. More on this later. Note: This register is internal, and cannot be accessed directly.
If a bit is set, the current IRQ has been acknowledged by the CPU to go ahead and begin executing. The PIC uses this register to determin what IRQ is currently being executed. Interrupt Mask Register (IMR)This specifies what interrupts are to be ignored, and not acknowledged. this allows us to focus on executing certain, more important interrupts before executing the interrupts specified in this register.This is an 8 bit register, where each bit determins if an interrupt is disabled or not. If the bit is 0, it is enabled. If it is a 1, the interrupt device is disabled.
This is an important register, as it allows us to enable and disable interrupts from certain devices. Each of these IRQ's represent the device listed in the x86 Hardware Interrupts table shown above. For example, lets say we want to enable COM1 (Serial Port 1). Looking at the x86 Hardware Interrupt Table, we can see that this is mapped to IRQ 4. So, in order to enable COM1 interrupts, all we need to do is set the IRQ4 bit for the primary PIC's Interrupt Mask Register. This register is mapped to the software port number 0x21 (We will cover this later.) So, all we need to do is set the bit by writing to this port location. Too cool for school B) When a hardware interrupt occurs, The 8259A Masks out all other interrupts until it recieves an End of Interrupt (EOI) signal. We will need to send the EOI upon completion of the interrupt. We will look at this later. 8259A Software Port MappingsLike all hardware controllers, the BIOS POST maps each controller to use a specific region of software ports. Because of this, in order to communicate with the PIC controllers, we need to use software ports.
Notice the Primary PIC's Interrupt Mask Register is mapped to Port 0x21. We have seen this before, havn't we? The Command Register and Status Register are to different registers that share the same port number. The command register is write only, while the status register is read only. This is an important difference, as the PIC determins what register to access depending on weather the write or read lines are set. We will need to be able to write to these ports to communicate with individual device registers and control the PICs. Lets now take a look at the commands for the PIC. 8259A CommandsSetting up the PIC is farily complex. It is done through a series of Command Wowrds, which are a bit pattern that containes various of states used for initialization and operation. This might seem a little complex, but it is not to hard.Because of this, lets first look at how to initialize the PIC controllers for our use, followed by operating and controlling the PICs. Initialization Control Words (ICW)The purpose of initializing the PIC is to remap the PIC's IRQ numbers to our own. this insures the proper IRQ is generated when a hardware interrupt happens.In order to initialize the PIC, we must send a command byte (Known as an Initialization Control Word (ICW)) to the primary PIC Command Register. This is ICW 1. There can be up to 4 Initialization Control Words. These are not required, but are often needed. Lets take a look at them. Note: If there are multiple PICs in the system that are to be cascaded with each other, we must send the ICW's to both of the PICs! ICW 1This is the primary control word used to initialize the PIC. this is a 7 bit value that must be put in the primary PIC command register. This is the format:
As you can see, there is alot going on here. We have seen some of these before. This is not as hard as it seems, as most of these bits are not used on the x86 platform. To initialize the primary PIC, all we need to do is create the initil ICW and set the appropraite bits. So, lessee...
Because we have enabled cascading, we need to send ICW 3 to the controller as well. Also, because we have set bit 0, we must also send ICW 4. More on those later. For now, lets take a look at ICW 2. ICW 2This control word is used to map the base address of the IVT of which the PIC are to use. This is important!
During initialization, we need to send ICW 2 to the PICs to tell them where the base address of the IRQ's to use. If an ICW1 was sent to the PICs (With the initialization bit set), you must send ICW2 next. Not doing so can result in undefined results. Most likley the incorrect interrupt handler will be executed. Unlike ICW 1, which is placed into the PIC's data registers, ICW 2 is sent to the data Registers, as software ports 0x21 for the primary PIC, and port 0xA1 for the secondary PIC. (Please see the 8259A Software Port Map table for a complete listing of PIC software ports). Okay, so assuming we have just sent an ICW 1 to both PICs (Please see the above section), lets send an ICW 2 to both PICs. This will map a base IRQ address to both PICs. This is very simple, but we must be careful at where we map the PICs to. Remember that the first 31 interrupts (0x0-0x1F) are reserved (Please see the above x86 Interrupt Vector Table (IVT) table). As such, we have to insure we do not use any of these IRQ numbers. Instead, lets map them to IRQs 32-47, right after these reserved interrupts. the first 8 IRQ's are handled by the primary PIC, so we map the primary PIC to the base address of 0x20 (32 decimal), and the secondary PIC at 0x28 (40 decimal). Remember there are 8 IRQ's for each PIC. That is simple, huh? Onto the next one! ICW 3This is an important command word. It is used to let the PICs know what IRQ lines to use when communicating with each other.ICW 3 Command Word for Primary PIC
ICW 3 Command Word for Secondary PIC
We must send an ICW 3 whenever we enable cascading within ICW 1. this allows us to set which IRQ to use to communicate with each other. Remember that the 8259A Microcontroller relies on the IR0-IR7 pins to connect to other PIC devices. With this, it uses the CAS0-CAS2 pins to communicate with each other. We need to let each PIC know about each other and how they are connected. We do this by sending the ICW 3 to both PICs containing which IRQ line to use for both the master and associated PICs. Remember: The 80x86 architecture uses IRQ line 2 to connect the master PIC to the slave PIC. Knowing this, and remembering that we need to write this to the data registers for both PICs, we need to follow the formats shown above. Note that, in the ICW 3 for the primary PIC, Each bit represents an interrupt request. That is...
Notice that IRQ 2 is Bit 2 within ICW 3. So, in order to set IRQ 2, we need to set bit 2 (Which is at 0100 binary, or 0x4). Here is an example of sending ICW 3 to the primary PIC:
To send this to the secondary PIC, we must remember that we must send this in binary notation. Please refer to the table above. Note that only Bits 0...2 are used to represent the IRQ line. By using binary notation, we can refer to the 8 IRQ lines to choose from:
Simple enough. Notice that this just follows a binary<->decimal conversition in the above table. Because we are connected by IRQ line 2, we need to use bit 1 (Shown above). Here is a complete example, that sends a ICW 2 to both primary and secondary PIC controllers: Thats all there is to it ;) Okay, so now both PICs are connected to use IR line 2 to communicate with each other. We have also set a base interrupt number for both PICs to use. This is great, but we are not done yet. Remember that, when building up ICW 1, if bit 0 is set, the PIC will be expecting us to send it ICW 4. As such, we need to send ICW 4, the final ICW, to the PICs. ICW 4Yey! This is the final initialization control word. This controls how everything is to operate.
This is a pretty powerful function. Bits 5..7 are always 0, so lets focus on the other bits and peices (pun entended ;) ) The PIC was originally designed to be a generic microcontroller, even before the 80x86 existed. As such, it containes alot of different operation modes designed for different systems. one of these modes is the Special Fully Nested Mode. The x86 family does not support this mode, so you can saftley set bit 4 to 0. Bit 3 is used for buffered mode. For now, set this to 0. We will cover modes of operation later. Bit 2 is only used when bit 3 is set, so set this to 0. With this, Bit 1 is rairly used either. As such, we only need to set bit 0, which enables the PIC for 80x86 mode. Simple enough. So, to send ICW 4, all we need to do is this: This is probably the easiest code snipplet in this tutorial. Brace it while it lasts! :) Initializing the PIC - Putting it togetherBelieve it or not, but we have already went over this. In initilizing the PIC, all we need to do is send the correct ICW's to the PIC.Lets put everything from the previous section together to initialize the PIC for better understanding of how everything is put together: That was not that hard, was it? We covered everything in this code. Now the PIC is initialized. Whenever an hardware interrupt accors, it will call our interrupts 32 - 47 that we have previously defined somewhere within the Interrupt Vector Table (IVT). This allows us to track hardware interrupts. Cool, huh?
Operation Command Words (OCW)Yippee! Now that the ugly initialization stuff is out of the way, we can finally focus on standard controlling and operation of the PIC. This is done by writing and reading from various registers through Operation Control Words (OCW)'s.OCW 1OCW 1 represents the value inside of the Interrupt Mask register (IMR). To abtain the current OCW 1, all you need to do is read from the IMR.Remember that the IMR is mapped to the same port that the status register is at. Because the status register is read only, the PIC can determin what register to access based off if this is a read or write operation. We have looked at the IMR register above when we covered the PIC registers. OCW 2This is the primary control word used to control the PIC. Lets take a look...
Okay then! Bits 0-2 represents the interrupt level for the current interrupt. Bits 3-4 are reserved. Bits 5-7 are the interesting bits. Lets take a look at each combination for these bits.
Okay...This table, in its current state, is confusing, don't you think? Alot of the above commands are fairly advanced. Lets take a look at what we can do. Sending End of Interrupt (EOI) As you know, when a hardware interrupt triggers, all other interrupts are masked off inside of the Interrupt Mask Register until an EOI signal is sent to the primary controller. This means, we must send an EOI to insure all hardware interrupts are enabled at the end of our Interrupt Routine (IR). Looking at the above table, we can send a non specific EOI command to signal EOI to the controller. Because the EOI bit is bit 5 within the OCW 2, all we need to do is set bit 5 (100000 binary = 0x20):
ConclusionThe PIC is a complex microcontroller to program. We have covered alot in this tutorial, and it is only going to get harder! I hope I explained everything well. The OS Development Series primary tutorial series will put everything inside of this tutorial where it belongs. It will be the glue between setting up interrupts, interrupt handling, and hardware interrupts ;) I plan on expanding on this tutorial to provide more content, and to insure we describe every detail reguarding the 8259A microcontrollers. This is a side tutorial that the main series will use. As such, there is no demo for this tutorial. I might decide to make one, however, to being everything together. However, this will require us to cover the IDT, and interrupt handling in detail here, which is not relivant to the PIC. Not directly relivant, anyways. I hope this tutorial will help answer alot of questions one may have when programming the PIC, and understanding what is really going on behind the hood. Until next time,
~Mike |