/* cintr.c -- code for interrupt management */ /***************************************************************************** * Change Log * Date | Change *-----------+----------------------------------------------------------------- * 31-Dec-85 | Created changelog * 31-Dec-85 | Add c:\ to include directives * 31-Dec-85 | Save actual interrupt vector data; don't use constants * | (different on PC/AT and PC/XT) * 31-Dec-85 | Added intr_init * 31-Dec-85 | Handle secondary interrupt port for AT * 26-May-86 | Use cmdline.c to get debug switch * 6-Aug-86 | Modified for Lattice 3.0 -- use "void" to type some routines *****************************************************************************/ /* * I think this came from Dale Amon, but we had some problems * getting it to work with Lattice C and hacked away to get * something to do the job. */ #include "stdio.h" #include "dos.h" /* note: dos.h typedefs byte to unsigned char */ /* cext.h #defines byte to unsigned char */ #include "cext.h" #include "cmdline.h" #include "cintr.h" #include "atxt.h" /* * IBM-XT and IBM-AT interrupt information */ #define NIRQS 16 /* Maximum number of IRQ levels */ short defpc[NIRQS]; /* restore the vector to this PC */ short defseg[NIRQS]; /* and this segment */ #define VECTOR(irq) (0x20 + 4 * (irq)) /* Maps interrupt request number to the address of the vector */ /* CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! The above "vector" computation DOES NOT WORK for a PC/AT for values higher than 7. IRQ8..15 are vectored starting at 70. The proper AT computation, should higher IRQ values be required, is (0x20 + ((irq) < 8 ? 0x20 + 4*(irq) : 0x70 + 4 * ((irq) - 8))) but that is much too complicated to want to do in the restricted case of having only an IRQ2 device. But you have now been warned. Note also that devices which on the PC or XT interrupt on IRQ2 will interrupt on the AT at IRQ9, but the software in the BIOS redirects this interrupt (PC/AT Tech Ref page 5-71) via the IRQ2 vector. *** HOWEVER *** If the program masks off interrupts by twiddling the mask bit for IRQ2 it will also mask off the realtime clock interrupt, coprocessor interrupt, and *FIXED DISK INTERRUPT*. Therefore, any attempt to manipulate the interrupt masks must be done to the secondary 8259 controller channel chip, located at locations 0xA0 and 0xA1. Mask bits in primary mask register: Bit XT* AT** 7 Printer Parallel Port 1 6 Diskette Diskette controller 5 Fixed Disk Parallel Port 2 4 Serial Port 1 Serial Port 1 3 Serial Port 2 Serial Port 2 2 IRQ2 IRQ2 (OR of IRQ8..IRQ15) 1 Keyboard Keyboard 0 Timer Timer Mask bits in secondary mask register (AT only) 7 IRQ15 6 Fixed disk controller 5 Coprocessor 4 IRQ12 3 IRQ11 2 IRQ10 1 IRQ9 (Bus IRQ2) 0 Realtime clock Source: * PC/XT Technical Ref page 1-9 ** PC/AT Technical Ref page 1-11 */ #define INT_0_CONTROL_PORT 0x20 /* Interrupt chip I/0 port (PC, XT)*/ #define INT_1_CONTROL_PORT 0x21 /* " " " " " */ #define INT2_0_CONTROL_PORT 0xA0 /* Interrupt chip 2 I/O port (AT) */ #define INT2_1_CONTROL_PORT 0xA1 /* " " " " " */ #define EOI(irq) (0x60 | (irq)) /* End of interrupt command to send to PORT1 */ #define ndsw 2 private char *dsw[ndsw] = { "-d", "-debug" }; int enabled; /* flag to tell if interrupts are on */ int debug_intr; /* set for debugging */ int IsAT; /* AT or XT?, initialized here, read-only to others */ /**************************************************************************** * Routines local to this module ****************************************************************************/ private void print_intr(); /**************************************************************************** * intr_init * Effect: * Initializes the interrupt routines ****************************************************************************/ void intr_init() { int i; debug_intr = (cl_nswitch(dsw, ndsw) != NULL); IsAT = (ATXT() == ISAT); if (debug_intr) printf("IsAT is %d\n", IsAT); for (i = 0; i < NIRQS; i++) defpc[i] = defseg[i] = 0; } /**************************************************************************** * intr_enable * Inputs: * int irq: Interrupt level to enable * Effect: * Enables the interrupt in the control port, sets enabled flag true ****************************************************************************/ void intr_enable(irq) int irq; { int pv; if (IsAT && irq == 2) { /* AT on irq2 */ pv = inp(INT2_1_CONTROL_PORT); if (debug_intr) printf("intr_enable/AT: (before) %02x = %02x\n", INT2_1_CONTROL_PORT, pv); pv &= ~(1<<1); outp(INT2_1_CONTROL_PORT, pv); if (debug_intr) { /* report */ pv = inp(INT2_1_CONTROL_PORT); printf("intr_enable/AT: (after) %02x = %02x\n", INT2_1_CONTROL_PORT, pv); } /* report */ } /* AT on irq2 */ else { /* PC or XT or AT not IRQ2 */ pv = inp(INT_1_CONTROL_PORT); pv &= ~(1<