ECE 371 Lab 7 -- 8259 Interrupt Experiment


This is an alternate lab from the one in the current Lab Manual.  Students in my (William Jones) sections will be doing the following lab.  Students in other lab sections will need to discuss this with their TAs further.

OBJECTIVE:  This assignment involves the 8259 Interrupt controller which resides in the PC.  You are to interface a debounced SPDT switch so that the inputs to the 8259's IRQ3 and/or IRQ4 interrupt request lines can be switched. (Note: IRQ3 and IRQ4 are included on the signal connections provided by the lab breadboard system.)  You will write service routines to handle interrupt requests from IRQ3 and IRQ4.  These service routines will involve displaying a 3 (for IRQ3) and a 4 (IRQ4) on the computer screen for a period of 10 (or less depending) seconds.  First, you will investigate what happens when a single request is made from either IRQ3 and IRQ4.  Then you will observe the priority interrupt handling capability of the 8259 by activating simultaneous requests to both IRQ3 and IRQ4.

EQUIPMENT NECESSARY:
Breadboard
SPDT switch
DIP Switches
Basic logic gates

PRE-LAB:
1.  Look over the debounced SPDT switch as shown here.  This switch configuration will permit signal level changes generated by the SPDT switch to be presented through the DIP switches, to either IRQ3 and/or IRQ4 via the breadboard connections.  The 8259 had already been initialized by the OS to be in the "edge triggered" mode.  Therefore, the signal that you generate using the SPDT switch should look like:
____________________________       ________________________________
                                                            |___|
2.  Write the software to adjust the initialization of the 8259 and to handle the interrupts requested via IRQ3 and IRQ4.  Since the 8259 in the PC is initialized by the OS, you should avoid disturbing its parameters as much as possible.  This is because there are other system resources that depend on its functionality.  The only thing that you should change are two bits in the mask register:  your program should unmask the 8259's IRQ3 and IRQ4 request lines, without disturbing the mask bits for the other six request lines (see HINTS section below).  Each of the 2 interrupt service routines should do the following: display the corresponding interrupt line number on the screen (for 10 seconds), then clear it from the screen.  For example, if an IRQ3 interrupt occurs, the character 3 should be displayed for 10 seconds, and if an interrupt from IRQ4 occurs then the character 4 should be displayed for 10 seconds.  In order to control the screen directly, you can use the following code fragment to display the characters:
-------------------------------
// To display a 3
pokeb(0xB800, 0x860, 0x33);
pokeb(0xB800, 0x861, 0x1F);

// To clear a 3
pokeb(0xB800, 0x860, 0x20);
pokeb(0xB800, 0x861, 0x1F);

// To display a 4
pokeb(0xB800, 0x910, 0x34);
pokeb(0xB800, 0x910, 0x1F);

// To clear a 4
pokeb(0xB800, 0x910, 0x20);
pokeb(0xB800, 0x910, 0x1F);
-------------------------------

The first two parameters in each pokeb command above specify a position on the screen. There are actually two address for each position on the screen: an even value that is specified along with the ASCII code for the character to be displayed (e.g. 0x33 = 3, 0x34 = 4, 0x20 = <space>), and the next highest odd value that is grouped with a control code that specifies how the character is to be displayed (e.g. 0x1F = <white character on a blue background>).  The space character is used for blanking a previously displayed digit after 10 seconds.  Note: The 3 will be displayed at a different position in the screen than the 4.

Each service routine should should re-enable interrupts using "enable()" so is can be interrupted by IRQ3 and IRQ4.

At the end of each service routine, a non-specific end-of-interrupt (EOI) should be sent to the 8259 (using OCW2).

Your background program, which runs when there are no interrupts being serviced, should display a 4 digit HEX counter on the screen.  Your counter should stop when a key is pressed on the PCs keyboard.

Please refer to the "skeleton" program below.  You may use it as a framework upon which to develop your software.

Note:  at the beginning of your program, you should read the current values of the interrupt vectors associated with IRQ3 and IRQ4 and save them.  This will allow you to restore them after your program terminates due to a key being pressed on the keyboard.

DURING LAB:
1.  Set up the circuit with the switches, as shown in the in the diagram.
2.  Run your program. Verify and show your TA that your system can handle the following:

POST-LAB:
1.  Write a report describing what you observed in the bulleted items above.
2.  Include a well documented listing of your program.
3.  Include a schematic.
4.  Discuss other possible applications of a 8259.
5.  Discuss what the purpose of the debouncing circuit is, and why we needed it.

SKELETON PROGRAM:
-----------------------------------------------------------
#include <stdio.h>
#include <dos.h>

#define MASK_PORT 0x21                /* 8259 Mask Register Port */
#define MASK_BYTE ??                     /* Byte used to unmask interrupts */
#define IVT_INDEX3  ??                     /* IBM PC interrupt number for IRQ3 */
#define IVT_INDEX4  ??                    /* IBM PC interrupt number for IRQ3 */

/* Storage for old IVT entries */
void (interrupt *old_isr3) (void);
void (interrupt *old_isr4) (void);
 

void interrupt new_isr3(void)
{
    /* Your interrupt service routine goes here */
    /* Remember no DOS calls are allowed in this region */

    outportb(0x20, 0x20);                    /* send a non-specific EOI (end of interrupt) */
}

void interrupt new_isr3(void)
{
    /* Your interrupt service routine goes here */
    /* Remember no DOS calls are allowed in this region */

    outportb(0x20, 0x20);                    /* send a non-specific EOI (end of interrupt) */
}

void main(void)
{

/* Declare variables */

/* Disable interrupts and save old IVTs */
    disable();
    old_isr3=getvect(IVT_INDEX3);
    old_isr4=getvect(IVT_INDEX4);

/* Place new vectors into table */
    setvect(IVT_INDEX3, new_isr3);
    setvect(IVT_INDEX4, new_isr4);

/* Get the mask register */
    mask=inportb(MASK_PORT);
/* Set correct bits to INTS=0, rest=1 */
    outportb(MASK_PORT, ?? );
/* Re-enable interrupts */
    enable();

        ........
/* Your background code will go here */
        ........
 

/* Restore old interrupts */
    disable();
    setvect(IVT_INDEX3, old_isr3);
    setvect(IVT_INDEX4, old_isr4);
    outportb(MASK_PORT, mask);
    enable();
}

---------------------------------------------------------

HINTS:
To set certain bits in a particular byte, just remember that you can do bit-wise operations. For example, if you want to set bit 4 and 7 to 1 without disturbing the other bits then you could do this in a generic example:
(pseudo-code)

byte = get_byte();
MASK = 0b10010000
newbyte = byte | MASK

So what you would have is:
value operator bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
byte x x x x x x x x
MASK 1 0 0 1 0 0 0 0
bit-wise OR
newbyte 1 x x 1 x x x x
( x represents 'don't care' )

What this would do is force bits 7 and 4 to be 1 and leave the other bits the way they were before.