reaches the end of its time slice after writing the segment but before writing the offset to the
Exception Vector. If the next process to gain the use of the CPU generates the software
exception with the inconsistent address in the vector table, the handler will not execute. In
addition, if a system includes pipelining, interrupts can occur between any two stages of the fetch
execute cycle. This situation increases the chance that an interrupt could occur when the
Exception Vector is in an inconsistent state. For these reasons, disabling interrupts with the CLI
instruction before altering the Exception Vector is always a good idea. If the service routine is
for a hardware device, disabling interrupts before beginning to write to the Exception Vector is
absolutely necessary. If the device for which you are installing a handler's address generates an
interrupt before the installation is complete, the results will be unpredictable at best and perhaps
even catastrophic at worst. Of course, equally important is remembering to reenable interrupts
with the STI instruction as soon as the installation of the address is finished.
Because many I/O devices perform multiple functions and therefore require different kinds of
services from the CPU depending on the reason for the interrupt, another common scenario with
interrupt service routines is for the handler to be a dispatch routine. This means that the main
task of the handler is to determine what the device needs and then to call a subroutine to perform
the service. Sometimes, the most efficient implementation for this is a jump table. Other times,
the interrupt service routine will need to check several conditions and call an appropriate
subroutine based on the results of those checks. Although the 8253 timer is not a good example
of this type of device, since it really only performs one function, the ACIA discussed in
Peripheral Devices and I/O is. Typically, when a device can generate an interrupt for more than
one reason, it includes a status register. This is one of the ports on the device. The service routine
begins by reading this status register. Depending on the value of bits in this register, the handler
calls the proper subroutine to service the particular request. The meaning of the bits of the status
register of a device is available in the data sheet for the device.
The handler for our timer example (using the timer to generate an interrupt at the end of each
time slice) would need to perform a context switch between the currently running process and
the next process in line waiting for the CPU. Although other tasks would also be necessary, the
major job of our interrupt service routine would be to save the values of all the system registers
and then to write the previously saved values into the registers for the next process in line.
Finally, it is important to ensure that the device is disabled until the installation of the service
routine is complete. Many devices include an enable/disable bit in the control register, that
allows a programmer to disable a device through a software instruction. This is particularly
desirable when changing the service routine for a device that is already installed. If the device
does not include this capability, then the service routine installation must be complete before
connecting the device. This is the case with the 8253 timer.
Programming Devices
The data sheet for each peripheral device will include its programming requirements.
Programming a device generally involves writing one or more values to its control register, but
may also require writing values to other ports on the device as well. The 8253 timer is fairly
representative and has the advantage of also being quite simple. The first consideration for
programming virtually any device is to determine the operation required and the meaning of the
bits in the control register. The following figure shows the control register for the 8253 PIT.