/*      RS-232.C contains code written by Bill Bytheway - AA6ED
*       that allows the developer to write a communications
*       interface for controlling up to 8 COM ports at the same
*       time using IRQ 1-15. It is written for reuse and is the
*       basis for many communications applications written by the
*       author. 
*
*       It has a 'main' section used to test the echo of data 
*       from one port to another, along with keyboard access.
*
*       Permission granted for non-commercial copying and use, 
*       provided this notice is retained.  Copyright 1999 
*       William H. Bytheway AA6ED, All Rights Reserved.
*/

#include <conio.h>
#include <bios.h>
#include <dos.h>

/* Define the communications port absolute numbers */
#define COM1 0
#define COM2 1
#define COM3 2
#define COM4 3
#define COM5 4
#define COM6 5
#define COM7 6
#define COM8 7

#define buff_size 0xFF

/* Define the interrupt mask structure for every IRQ interrupt */
struct
{  unsigned int  port_address;
   unsigned char int_id;
   unsigned char base_is_set;
   unsigned char datardy;
   unsigned char ringbuf[buff_size+1];
   unsigned int  readptr;
   unsigned int  writptr;
} COM[8] = { { 0x3F8,  4, 0, 0, 0, 0, 0, },   /* COM 1 */
	     { 0x2F8,  3, 0, 0, 0, 0, 0, },   /* COM 2 */
	     { 0x3E8, 10, 0, 0, 0, 0, 0, },   /* COM 3 */
	     { 0x3E0, 11, 0, 0, 0, 0, 0, },   /* COM 4 */
	     { 0x2F0, 12, 0, 0, 0, 0, 0, },   /* COM 5 */
	     { 0x2E8, 15, 0, 0, 0, 0, 0, },   /* COM 6 */
	     { 0x2E0,  0, 0, 0, 0, 0, 0, },   /* COM 7 */
	     { 0x260,  0, 0, 0, 0, 0, 0, }};  /* COM 8 */

/* Define the IRQ interrupt addresses and masks */
struct
{
  unsigned char int_id;
  unsigned char PIC1_mask;
  unsigned char PIC2_mask;
  unsigned char old_PIC1_mask;
  unsigned char old_PIC2_mask;
} IRQ[16] = { { 0x08, 0xFE, 0xFF, 0, 0},   /* IRQ00 tick counter */
	      { 0x09, 0xFD, 0xFF, 0, 0},   /* IRQ01 Keyboard     */
	      { 0x0A, 0xFB, 0xFF, 0, 0},   /* IRQ02 PIC2         */
	      { 0x0B, 0xF7, 0xFF, 0, 0},   /* IRQ03 COM2         */
	      { 0x0C, 0xEF, 0xFF, 0, 0},   /* IRQ04 COM1         */
	      { 0x0D, 0xDF, 0xFF, 0, 0},   /* IRQ05 Mouse        */
	      { 0x0E, 0xBF, 0xFF, 0, 0},   /* IRQ06 Floppy Drive */
	      { 0x0F, 0x7F, 0xFF, 0, 0},   /* IRQ07 Printer      */
	      { 0x70, 0xFB, 0xFE, 0, 0},   /* IRQ08 Clock        */
	      { 0x71, 0xFB, 0xFD, 0, 0},   /* IRQ09 Redirect IRQ2*/
	      { 0x72, 0xFB, 0xFB, 0, 0},   /* IRQ10 COM3         */
	      { 0x73, 0xFB, 0xF7, 0, 0},   /* IRQ11 COM4         */
	      { 0x74, 0xFB, 0xEF, 0, 0},   /* IRQ12 COM5         */
	      { 0x75, 0xFB, 0xDF, 0, 0},   /* IRQ13 System Int   */
	      { 0x76, 0xFB, 0xBF, 0, 0},   /* IRQ14 Fixed Disk   */
	      { 0x77, 0xFB, 0x7F, 0, 0}};  /* IRQ15 COM6         */

/* Define address adders for the various Async card registers  */
#define RBR 0x00   /* Transmitter Holding Register        */
#define IER 0x01   /* Interrupt Enable Register           */
#define IIR 0x02   /* Interrupt Identification Register   */
#define LCR 0x03   /* Line Control Register               */
#define MCR 0x04   /* Modem Control Register              */
#define LSR 0x05   /* Line Status Register                */
#define MSR 0x06   /* Modem Status Register               */
#define DLL 0x00   /* Divisor Latch Least Significant     */
#define DLM 0x01   /* Divisor Latch Most  Significant     */

/* Define port addresses for the 8259 interrupt controller   */
#define PICCMD1 0x20  /* Primary Controller   */
#define PICMSK1 0x21
#define PICCMD2 0xA0  /* Secondary Controller */
#define PICMSK2 0xA1
#define PICEOI  0x20  /* End of interrupt     */

/* Define UART byte settings   */
#define pSpace 0x38
#define pOdd   0x08
#define pMark  0x28
#define pEven  0x18
#define pNone  0x00

/* Define UART number of data bits   */
#define d5 0x00
#define d6 0x01
#define d7 0x02
#define d8 0x03

/* define UART number of stop bits   */
#define s1 0x00
#define s2 0x04

#define true  1
#define false 0

/* Store the pointers to the interrupt address  */
void interrupt (*Com1)();
void interrupt (*Com2)();
void interrupt (*Com3)();
void interrupt (*Com4)();
void interrupt (*Com5)();
void interrupt (*Com6)();
void interrupt (*Com7)();
void interrupt (*Com8)();

/* Async Interrupt routine   */
void Asynch_COM(int irq_id)
{
  int baseid;
  for (baseid = 0; baseid <= 7; baseid++)
  {
    if ((COM[baseid].int_id == irq_id) && COM[baseid].base_is_set)
    {
      int ComBase = COM[baseid].port_address;
      if (inportb(ComBase+IIR) == 0x04)     
      {
	COM[baseid].ringbuf[COM[baseid].writptr] = inportb(ComBase + RBR);
	COM[baseid].writptr = (COM[baseid].writptr + 1) & buff_size;
	COM[baseid].datardy = true;
      }
      if (irq_id > 7) outportb(PICCMD2, PICEOI);
      outportb(PICCMD1,PICEOI);
     }   /* test for matching interrupt and base is set     */
  }      /* loop through 8 COM ports and test for baseid    */
}        /* end of the interrupt at this point and return   */

/* Pass the correct interrupt to the Async_COM(interrupt id)   */
void interrupt IRQ0_Interrupt()  { Asynch_COM(0);   }
void interrupt IRQ1_Interrupt()  { Asynch_COM(1);   }
void interrupt IRQ2_Interrupt()  { Asynch_COM(2);   }
void interrupt IRQ3_Interrupt()  { Asynch_COM(3);   }
void interrupt IRQ4_Interrupt()  { Asynch_COM(4);   }
void interrupt IRQ5_Interrupt()  { Asynch_COM(5);   }
void interrupt IRQ6_Interrupt()  { Asynch_COM(6);   }
void interrupt IRQ7_Interrupt()  { Asynch_COM(7);   }
void interrupt IRQ8_Interrupt()  { Asynch_COM(8);   }
void interrupt IRQ9_Interrupt()  { Asynch_COM(9);   }
void interrupt IRQ10_Interrupt() { Asynch_COM(10);  }
void interrupt IRQ11_Interrupt() { Asynch_COM(11);  }
void interrupt IRQ12_Interrupt() { Asynch_COM(12);  }
void interrupt IRQ13_Interrupt() { Asynch_COM(13);  }
void interrupt IRQ14_Interrupt() { Asynch_COM(14);  }
void interrupt IRQ15_Interrupt() { Asynch_COM(15);  }

/* Open the COM port     */
void OpenCom(int ComPort,
	     int baud,
	     int parity,
	     int databits,
	     int stopbits)
{
  int rate;
  int LCRreg;
  int interrupt_id;
  int interrupt_no;
  int ComBase;

  COM[ComPort].base_is_set = true;
  interrupt_no = COM[ComPort].int_id;
  interrupt_id = IRQ[interrupt_no].int_id;
  switch (ComPort)
  {
    case 0: Com1 = getvect(interrupt_id); break;
    case 1: Com2 = getvect(interrupt_id); break;
    case 2: Com3 = getvect(interrupt_id); break;
    case 3: Com4 = getvect(interrupt_id); break;
    case 4: Com5 = getvect(interrupt_id); break;
    case 5: Com6 = getvect(interrupt_id); break;
    case 6: Com7 = getvect(interrupt_id); break;
    case 7: Com8 = getvect(interrupt_id); break;
  }
  disable();
  switch (interrupt_no)
  {
    case  0: setvect(interrupt_id,IRQ0_Interrupt);  break;
    case  1: setvect(interrupt_id,IRQ1_Interrupt);  break;
    case  2: setvect(interrupt_id,IRQ2_Interrupt);  break;
    case  3: setvect(interrupt_id,IRQ3_Interrupt);  break;
    case  4: setvect(interrupt_id,IRQ4_Interrupt);  break;
    case  5: setvect(interrupt_id,IRQ5_Interrupt);  break;
    case  6: setvect(interrupt_id,IRQ6_Interrupt);  break;
    case  7: setvect(interrupt_id,IRQ7_Interrupt);  break;
    case  8: setvect(interrupt_id,IRQ8_Interrupt);  break;
    case  9: setvect(interrupt_id,IRQ9_Interrupt);  break;
    case 10: setvect(interrupt_id,IRQ10_Interrupt); break;
    case 11: setvect(interrupt_id,IRQ11_Interrupt); break;
    case 12: setvect(interrupt_id,IRQ12_Interrupt); break;
    case 13: setvect(interrupt_id,IRQ13_Interrupt); break;
    case 14: setvect(interrupt_id,IRQ14_Interrupt); break;
    case 15: setvect(interrupt_id,IRQ15_Interrupt); break;
  }
  IRQ[interrupt_no].old_PIC2_mask = inportb(PICMSK2);
  IRQ[interrupt_no].old_PIC1_mask = inportb(PICMSK1);
  outportb(PICMSK2, (IRQ[interrupt_no].old_PIC2_mask & IRQ[interrupt_no].PIC2_mask));
  outportb(PICMSK1, (IRQ[interrupt_no].old_PIC1_mask & IRQ[interrupt_no].PIC1_mask));
  enable();
  outportb(PICCMD2,PICEOI);
  outportb(PICCMD1,PICEOI);

  ComBase = COM[ComPort].port_address;
  outportb(IER+ComBase, 0x01);
  outportb(MCR+ComBase, 0x0B);
  LCRreg = 0x80;
  LCRreg = LCRreg | parity;
  LCRreg = LCRreg | databits;
  LCRreg = LCRreg | stopbits;
  outportb(LCR+ComBase, LCRreg);
  rate = 3840 / (baud /30);
  outportb(DLM+ComBase, (rate/256 & 0xFF));
  outportb(DLL+ComBase, (rate     & 0xFF));
  outportb(LCR+ComBase, LCRreg & 0x7F);

  /* final flush to clear both interrupt controllers */
  Asynch_COM(COM[COMPORT].int_id);
}

/*------------ Close any initialized COM -----------------------*/
void CloseCom(int ComPort)
{
  int interrupt_no, interrupt_id, ComBase;

  ComBase = COM[ComPort].port_address;
  COM[ComPort].base_is_set = false;
  interrupt_no = COM[ComPort].int_id;
  interrupt_id = IRQ[interrupt_no].int_id;
  outport(PICMSK2, (IRQ[interrupt_no].old_PIC2_mask));
  outport(PICMSK1, (IRQ[interrupt_no].old_PIC1_mask));
  switch (ComPort)
   {
      case 0: setvect(interrupt_id,Com1);  break;
      case 1: setvect(interrupt_id,Com2);  break;
      case 2: setvect(interrupt_id,Com3);  break;
      case 3: setvect(interrupt_id,Com4);  break;
      case 4: setvect(interrupt_id,Com5);  break;
      case 5: setvect(interrupt_id,Com6);  break;
      case 6: setvect(interrupt_id,Com7);  break;
      case 7: setvect(interrupt_id,Com8);  break;
   }
  outportb(IER+ComBase, 0x00);
  outportb(MCR+ComBase, 0x00);
}

/* Checks the datardy status for a given receive port and gets the char   */
/* It returns a -1 if there isn't anything, or the character if there is */
int  recvchar(int ComPort)
{
  int ch = -1;
  if (COM[ComPort].datardy)
  {
    ch = COM[ComPort].ringbuf[COM[ComPort].readptr];
    COM[ComPort].readptr = (COM[ComPort].readptr + 1) & buff_size;
    if (COM[ComPort].readptr == COM[ComPort].writptr)  COM[ComPort].datardy = false;
  }
  return(ch);
}

/* Transmits a character to the appropriate communications port   */
int xmitchar(int ComPort, int ch)
{

  int  LSRreg,i=-1;
  do {
      LSRreg = inportb(LSR+COM[ComPort].port_address) & 0x20;
      i++;
     }  while (LSRreg == 0);
  outportb(RBR+COM[ComPort].port_address,ch);
  return(i);
}

/* Returns a -1 if there is no DCD connect, a +1 if there is  */
int DCDconnect(int ComPort)
{
 int ComBase;
 int i = -1;
 ComBase = COM[ComPort].port_address;        /* Set ComBase Address   */
 if ((inportb(ComBase + MSR) & 0x80) == 0x80) i = 1;
 return (i);
}


/* Resets the input buffer on any given comport    */
void purgeline(int ComPort)
{

     COM[ComPort].readptr = COM[ComPort].writptr;
}

/* ----------------------------- MAIN PROGRAM ------------------ */
/*   THE CODE BELOW SHOWS A TYPICAL EXAMPLE OF HOW THIS IS USED  */
void main()
{
char ch = 0;
char ch1 = 0;
textmode(C4350);
setcbrk(0);
textcolor(WHITE);
clrscr();
OpenCom(COM5,2400,pNone,d8,s1);
OpenCom(COM2,4800,pNone,d8,s1);
do
{

  /*  First COMM Port   */   
  if (COM[COM5].datardy)
   {
      ch = recvchar(COM5) & 0x7F;
       cprintf("%c",ch);
      xmitchar(COM2,ch);
   }

  /*  Second COMM Port     */
  if (COM[COM2].datardy)
   {
      ch = recvchar(COM2) & 0x7F;
      cprintf("%c",ch);
      xmitchar(COM5,ch);
   }

   /*   Keyboard   */
  if (bioskey(1) !=0)
   {
      ch1 = bioskey(0);
      xmitchar(COM2,ch1);
   }

} while (ch1 !=27);

CloseCom(COM5);
CloseCom(COM2);
}








