/*
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 <windows.h>
#endif
#include <dos.h>
#include <comm.h>
#if (defined(MSC) || defined(__POWERC) || defined(__WATCOMC__) || defined(_INTELC32_) || defined(__HIGHC__))
#include <conio.h>
#endif
#if defined(DOSX286)
#include <pl286.h>
#elif defined(DOSX386)
#include <pl386.h>
#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);
}