Metropoli BBS
VIEWER: interupt.c MODE: TEXT (ASCII)
/*
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);
}
[ RETURN TO DIRECTORY ]