/* Copyright (C) Magna Carta Software, Inc. 1990, 1991. All Rights Reserved INTERUPT.C -- Serial interrupt handling routines */ #define CCT_DEVELOPMENT #if (defined(CCTW) || defined(_WINDOWS)) #include #endif #include #include #if (defined(MSC) || defined(__POWERC) || defined(__WATCOMC__) || defined(_INTELC32_) || defined(__HIGHC__)) #include #endif #if defined(DOSX286) #include #elif defined(DOSX386) #include #endif #define PIC0 0X20 /* 8259 PROGRAMMABLE INTERRUPT CONTROLLER PORT */ #define PIC1 0XA0 /* 8259 PROGRAMMABLE INTERRUPT CONTROLLER PORT */ #if 0 /* AN ARRAY OF 8 MASK VALUES FOR THE EIGHT IRQ LINES OF THE 8259 */ WORD int_enable_mask[] = {0XFE, 0XFD, 0XFB, 0XF7, 0XEF, 0XDF, 0XBF, 0X7F}; #endif #if 0 /* ARRAY OF ISRs. ONE FOR EACH AVAILABLE IRQ LINE */ void (INTERRUPT_ *com_isr[MAX_ISRS])() = { (void (INTERRUPT_ *)()) com_isr0_, (void (INTERRUPT_ *)()) com_isr1_, (void (INTERRUPT_ *)()) com_isr2_, (void (INTERRUPT_ *)()) com_isr3_}; #endif void (FAR_ *com_isr[MAX_ISRS])() = { (void (FAR_ *)()) com_isr0_, (void (FAR_ *)()) com_isr1_, (void (FAR_ *)()) com_isr2_, (void (FAR_ *)()) com_isr3_}; /* ARRAY OF FAR POINTERS TO PORTS THAT HAVE ISRs */ COMM_PORT FAR_ * mc_isr[MAX_ISRS] = {NULL}; char mc_isr_used[MAX_ISRS] = {'\0'}; WORD cct_warpdrive; short deinstall_ipr(COMM_PORT *p, WORD itype) { return((*p->deinstall_ipr)(p, itype)); } /* DEINSTALL_ISR -- Disable interrupts for a specific IRQ. 1) Mask interrupts on the specified IRQ line; 2) Restore the old vector. 3) Set the COMM_PORT IRQ and interrupt number to 0; 4) Set the COMM_PORT ISR and old interrupt vector pointers to NULL; */ short deinstall_isr(COMM_PORT *p) { short i; if (p->old_isr0 != NULL) { /* IF DEFAULT ISR USED -- REMOVE THIS PORT FROM ARRAY */ for (i=0; i < MAX_ISRS; i++) { if (mc_isr[i] == (COMM_PORT FAR_ *) p) { mc_isr_used[i] = '\0'; mc_isr[i] = NULL; break; } } /* DISABLE INTERRUPTS AT THE PROGRAMMABLE INTERRUPT CONTROLLER */ disable_irq(p->irq); #if (!defined(DOSX286) && !defined(DOSX386)) set_vector(p->rxintnum, p->old_isr0); #else reset_pmrm_vector(p); #endif p->irq = p->rxintnum = 0; p->isr = p->old_isr0 = NULL; } return (0); } /* DISABLE_COMM_INT -- Disable interrupts at the communications chip. */ short disable_comm_int(COMM_PORT *p, WORD int_type) { return ((*p->disable_comm_int)(p, int_type)); } /* ENABLE_COMM_INT -- Enable interrupts at the communications chip. */ short enable_comm_int(COMM_PORT *p, WORD int_type) { return ((*p->enable_comm_int)(p, int_type)); } short install_ipr(COMM_PORT *p, WORD itype, void FAR_ *fn, BYTE FAR_ *buf, WORD len) { return((*p->install_ipr)(p, itype, fn, buf, len)); } /* INSTALL_ISR -- Enable interrupts for a specific IRQ. 1) Compute the interrupt number corresponding to the given IRQ; 2) Save the old vector. 3) Install the new vector. 4) Unmask interrupts on the specified IRQ line; Return Value: 0 -- success; EOF -- no available default ISRs); */ short install_isr(COMM_PORT *p, WORD irq, void FAR_ *isr) { short i; if (isr == NULL) { /* DEFAULT ISR REQUESTED -- SEARCH FOR A FREE ONE */ for (i=0; i < MAX_ISRS; i++) { if (mc_isr_used[i] == '\0') { mc_isr_used[i] = (char) 1; mc_isr[i] = (COMM_PORT FAR_ *) p; break; } } if (i == MAX_ISRS) return (EOF); } p->isr = (isr == NULL) ? (void FAR_ *) com_isr[i] : isr; p->irq = irq; p->rxintnum = (irq > 7) ? irq + 0X70 - 8 : irq + 8; p->pic = (irq > 7) ? PIC1 : PIC0; #if (!defined(DOSX286) && !defined(DOSX386)) p->old_isr0 = (void FAR_ *) get_vector(p->rxintnum); set_vector(p->rxintnum, p->isr); #else set_pmrm_vector(p, p->isr, NULL); #endif /* ENABLE INTERRUPTS AT THE PROGRAMMABLE INTERRUPT CONTROLLER */ enable_irq(p->irq); return (0); } /* IRQ8259_SET -- Enable or disable the communications interrupt request line (IRQ) at the 8259 Programmable Interrupt Controller (in the IBM architecture). */ short irq8259_set(WORD irq, WORD state) { BYTE cval; /* current value of 8259 PIC */ WORD pic; pic = (irq > 7) ? PIC1 : PIC0; cval = (BYTE) inp(pic+1); /* read the IMR */ /* CLEAR THE BIT CORRESPONDING TO THE INTERRUPT TO BE UNMASKED */ if (state) { outp(pic+1, cval & ~(1 << ((irq > 7) ? irq - 8 : irq))); } /* SET THE BIT CORRESPONDING TO THE INTERRUPT TO BE MASKED */ else { outp(pic+1, cval | (1 << ((irq > 7) ? irq - 8 : irq))); } return (0); } /* I_RXSTAT -- Check for characters ready to receive when running under interrupts. Note: There are no characters ready if the head and the tail ptr. are equal. */ short i_rxstat(COMM_PORT *p) { if (p->rxbufhead >= p->rxbuftail) return (p->rxbufhead - p->rxbuftail); else return (p->rx_bufsiz - (p->rxbuftail - p->rxbufhead)); /* return (p->rxbufhead != p->rxbuftail); */ } /* I_READ -- Get a byte from the receive buffer. Return it for use and update the pointer to the receive buffer tail. */ short i_read(COMM_PORT *p) { BYTE c; WORD rxdatabits = p->rxdatabits; c = *p->rxbuftail++; if (p->rxbuftail > p->rxbufend) p->rxbuftail = p->rxbuf; if (rxdatabits != 8) { if (rxdatabits == 7) c &= 0X7F; else if (rxdatabits == 6) c &= 0X3F; else if (rxdatabits == 5) c &= 0X1F; } return (c); } /* I_TXSTART -- Start transmit interrupts. Update the pointer to the transmit buffer head. Set the TX interrupt busy flag if necessary (cleared by the handler). */ void i_txstart(COMM_PORT *p) { _disable(); /* don't let TX int. occur yet */ if (!p->f_txbusy && !(p->inhold & RX) && p->txbufhead != p->txbuftail) { while ((*p->txstat_)(p) == FALSE); (*p->c_write_)(p, *p->txbuftail++); /* write one byte */ if (p->txbuftail > p->txbufend) p->txbuftail = p->txbuf; /* update tail */ /* NOTIFY PROGRAM THAT TX INTERRUPT IS BUSY */ if (p->txbuftail != p->txbufhead) p->f_txbusy |= TX_BUSY; } _enable(); /* now the CPU can see the int. */ } /* I_TXSTAT -- Return space in transmit buffer. Return Value: n >=0 -- free buffer space number (in bytes); */ short i_txstat(COMM_PORT *p) { #if 0 BYTE FAR_ *bptr = (BYTE FAR_ *) p->txbufhead + 1; if (bptr > p->txbufend) bptr = p->txbuf; /* IF TRANSMIT BUFFER IS FULL AND TX INTERRUPTS NOT BUSY, KICK-START TX */ if (bptr == p->txbuftail && !(p->f_txbusy & TX_BUSY)) { p->f_txbusy &= ~TX_WAIT; /* clear the wait flag */ i_txstart(p); return (TRUE); } else return (bptr != p->txbuftail); #endif if (p->txbuftail > p->txbufhead) return(p->txbuftail - p->txbufhead - 1); else return(p->tx_bufsiz - (p->txbufhead - p->txbuftail) - 1); } /* I_WRITE -- Send a byte to the transmit buffer. Update the pointer to the transmit buffer head. Set the TX interrupt busy flag if necessary (cleared by the handler). */ void i_write(COMM_PORT *p, WORD c) { *p->txbufhead++ = (BYTE) c; /* add byte to head */ if (p->txbufhead > p->txbufend) p->txbufhead = p->txbuf; if (istxrts(p)) i_txstart(p); } short set_warpdrive(WORD irq) { if (irq > 7) return (EOF); cct_warpdrive = irq; outp(0X20, (0X80 | 0X40 | ((irq == 0) ? 7 : irq-1))); return (0); }