/*
==========================
c.language/listings #115, from barryn, 8544 chars, Sat Aug 29 17:17:28 1987
--------------------------
TITLE: Communications routines in Turbo C
       Converted to Microsoft C 5.1 by Ron Merts

   The following code shows how to take advantage of some of
the TurboC extensions to the C language to do asynchronous
communications (without having to write supporting assembly-
language routines).  The code was supplied by Peter Ibbotson of
Borland.

   First, here's Serial.C:
*/

/*       SERIAL.C       */

#include        <dos.h>
#include        <stdio.h>
#include        <conio.h>
#include        <bios.h>
#include        <process.h>
#include        "serial.h"

#define MK_FP(seg,ofs)  ((void far *) \
               (((unsigned long)(seg) << 16) | (unsigned)(ofs)))

int SError;
int portbase=0;
void far *oldvects[3];
char ccbuf[SBUFSIZ];
int startbuf=0;
int endbuf=0;

/*      this does the hard work (handling interrupts)           */

void interrupt far com_int (void)

{ _disable ();
  if ((inp (portbase+IIR) & RX_MASK)==RX_ID)
    { if (((endbuf+1) & 0x1fff)==startbuf)
      SError=BUFOVFL;
      ccbuf[endbuf++]=inp (portbase+RX);
      endbuf&=0x1fff;
    };
  outp (ICR,EOI);
  _enable ();
}

/* this routine returns the currently value in the buffer */

int getccb(void)

{ int res;
  if (endbuf==startbuf) return (-1);
  res=(int) ccbuf[startbuf];
  startbuf=(startbuf+1) % SBUFSIZ;
  return (res);
}

void setvects (void)

{ oldvects[0]=_dos_getvect(0x0b);
  oldvects[1]=_dos_getvect(0x0c);
  oldvects[2]=_dos_getvect(0x21);
  _dos_setvect(0x0b,com_int);
  _dos_setvect(0x0c,com_int);
/*  _dos_setvect(0x21,com_int); */       /* Grab the MS-DOS Interrupt vector */
}

void resvects (void)

{ _dos_setvect(0x0b,oldvects[0]);
  _dos_setvect(0x0c,oldvects[1]);
/*  _dos_setvect(0x21,oldvects[2]);*/ /* Restore the MS-DOS Interrupt vector */
}

void i_enable (int pnum)

{ int c;
  _disable();
  c = inp (portbase+MCR) | MC_INT;
  outp (portbase+MCR,c);
  outp (portbase+IER,RX_INT);
  c = inp (IMR) & (pnum==2 ? IRQ3 : IRQ4);
  outp (IMR,c);
  _enable();
}

void i_disable (void)

{ int c;
  _disable ();
  c=inp (IMR) | ~IRQ3 | ~IRQ4;
  outp (IMR,c);
  outp (portbase + IER,0);
  c=inp (portbase+MCR) & ~MC_INT;
  outp (portbase+MCR,c);
  _enable ();
}

void comon (void)

{  int c,pnum;
   pnum = portbase == COM1BASE ? 1 : 2;
   i_enable (pnum);
   c=inp (portbase + MCR) | DTR | RTS;
   outp (portbase + MCR,c);
}

void initserial (void)

{ endbuf=startbuf=0;
  setvects();
  comon();
};

void comoff (void)

{ i_disable ();
  outp (portbase+MCR,0);
}

void closeserial(void)

{ comoff();
  resvects();
};

/* this outputs a serial character              */

int SerialOut (char x)

{ long timeout = 0x0000ffff;
  outp (portbase+MCR,OUT2|DTR|RTS);
  /* wait for clear to send     */
  while ((inp(portbase + MSR) & CTS)==0)
    if ((--timeout)==0) return (-1);
  timeout=0x0000ffff;
  /* wait for outp register to clear         */
  while ((inp(portbase+LSR) & DSR)==0)
    if ((--timeout)==0) return (-1);
  _disable ();
  outp (portbase+TX,x);
  _enable ();
  return (0);
}

/* this routine sets which port we are working with     */

int SetPort (int Port)

{
    int far *RS232_Addr;
    int Offset;

  switch (Port) {                       /* sort out the base address    */
      case 1 :                          /* only ports one & two allowed */
          Offset=0x0000;
          break;
      case 2 :
          Offset=0x0002;
          break;
      default :
          return (-1);
      }
  RS232_Addr=MK_FP(0x0040,Offset);      /* find out where the port is   */
  if (*RS232_Addr==0)
      return (-1);                      /* if it ain't there return (-1)*/
  portbase=*RS232_Addr;                 /* otherwise set portbase       */
  return (0);
}

/* this routine sets the speed; will accept funny baud rates   */

int SetSpeed (int Speed)

{ char c;
  int divisor;
  if (Speed==0) return (-1);            /* avoid divide by zero */
  else divisor=(int)(115200L/Speed);
  if (portbase==0) return (-11);
  _disable ();
  c=inp (portbase+LCR);
  outp (portbase+LCR,(c|0x80));                     /* set DLAB     */
  outp (portbase+DLL,(divisor & 0x00ff));   /* set divisor  */
  outp (portbase+DLH,((divisor>>8)&0x00ff));
  outp (portbase+LCR,c);
  _enable();
  return (0);
}

/*      This routine set the LCR                */

int SetOthers (int Parity,int Bits,int StopBit)

{ int temp;
  if (portbase==0) return (-1);
  if ((Parity<NO_PAR) || (Parity>OD_PAR)) return (-1);
  if ((Bits<5) || (Bits>8)) return (-1);
  if ((StopBit<1) || (StopBit>2)) return (-1);
  temp=Bits-5;
  temp|=((StopBit==1) ? 0x00 : 0x04);
  switch (Parity)
  { case NO_PAR : temp |= 0x00; break;
    case OD_PAR : temp |= 0x08; break;
    case EV_PAR : temp |= 0x18; break;
  }
  _disable();            /* turn off interrupts */
  outp (portbase+LCR,temp);
  _enable();             /* turn em back on */
  return (0);
}

/*      This routine sets the ports                     */

int setserial (int Port,int Speed,int Parity,int Bits,int StopBit)

{ if (SetPort(Port)==-1) return(-1);
  if (SetSpeed(Speed)==-1) return(-1);
  if (SetOthers(Parity,Bits,StopBit)==-1) return (-1);
  return (0);
}

/*      This routine check for Carrier Detect,
          returns 1 if CD present or
          returns 0 if CD not present
*/

int checkCD()
{
    int c;
    _disable();    /* Disable Interrupts */
    c = inp(portbase+MSR) & CD;
    if (c == 128)
        c = 1;
    _enable();     /* Enable Interrupts again */
    return(c);
}

/* short demo                                           */
/* this opens the ports and echos to screen until       */
/* ESCape received                                      */

main ()

{ int c,c_kb;

  if (setserial (COM2,2400,NO_PAR,8,1) ==-1) exit (1);
  initserial();
  printf("You're now in terminal mode.  Press ESC to quit the program.\n\n");

do {
  if (kbhit())
      {
      c_kb = getch();
      if (c_kb==27) break;
      SerialOut(c_kb);
      }

  if ((c=getccb())!=-1)
     putchar (c);

  } while (c_kb != 27);
  closeserial();

}
