// This installs the Hearsay serialdev driver

int Device_Claim=0x1;
int Device_Channel=0x2;

int Device_TxRate=0x43AC1;
int Device_RxRate=0x43AC2;
int Device_ParityBits=0x43AC3;
int Device_DataBits=0x43AC4;
int Device_StopBits=0x43AC5;
int Device_Status=0x43AC6;
int Device_Flow=0x43AC7;
int Device_Select=0x43AC8;
int Device_Break=0x43AC9;
int Device_Get=0x43ACA;
int Device_Put=0x43ACB;
int Device_CountPurge=0x43ACC;
int OS_SerialOp=0x57;
int Device_Register=0x43ACD;

int SERIAL_RI =0x100000;
int SERIAL_DSR= 0x80000;
int SERIAL_DCD= 0x40000;
int SERIAL_DTR= 0x8;

int devblockmode;



int sdevnearest(int rate)
{
 int address;
 int nearest;
 int next;
 int xdiff;
 int ndiff;

 address=0x100;
 nearest=0;

 while(1)
 {
  next=serialdevread(address);

  if(!next) break;
  else
  if(!nearest) nearest=next;
  else
  {
   xdiff=nearest-rate;
   if(xdiff<0) xdiff=-xdiff;

   ndiff=next-rate;
   if(ndiff<0) ndiff=-ndiff;

   if(ndiff<xdiff) nearest=next;
  }
  address+=4;
 }

 return(nearest);
}


// map modem driver commands to Serial dev block

int devcon(int fn,int port,int p1,int p2)
{
 int code;
 int temp;

 if(fn==Device_Claim)
 {
  code=claimdevice(2,serialdevread(0xC8)|port);
 }
 else
 if(fn==Device_Channel)
 {
  if(devblockmode)
  {
   setchannel(TX,4,0,port);
   setchannel(RX,4,0,port);
  }
  else
  {
   setchannel(TX,2,0,port);     // use Serial dev block
   setchannel(RX,2,0,port);
  }
  code=0;
 }
 else
 if(fn==Device_TxRate)   // fn port speed 0(read)/1(write)
 {
  if(p2==1) code=serialdevcall(13,port,sdevnearest(p1),0); //fn,port,speed/-1
  else      code=serialdevcall(13,port,-1,0);
 }
 else
 if(fn==Device_RxRate)   // fn port speed 0(read)/1(write)
 {
  if(p2==1) code=serialdevcall(14,port,sdevnearest(p1),0); //fn, port, speed/-1
  else      code=serialdevcall(14,port,-1,0);
 }
 else
 if(fn==Device_ParityBits) // fn port NEOMS 0(read)/1(write)
 {
  code=serialdevcall(15,port,-1,0);
  if(p2==1)
  {
   code=code & ~0x38; // zero parity bits 3,4,5
   if(p1)
   {
    code|=0x8; // parity enabled
    if(p1==1) code|=0x10;
    else
    if(p1==3) code|=0x20;
    else
    if(p1==4) code|=0x30;
   }
   code=serialdevcall(15,port,code,0);
  }

  if(code & 0x8)
  {
   code&=0x30;
   if(code==0x0)  code=2;
   else
   if(code==0x10) code=1;
   else
   if(code==0x20) code=3;
   else
   if(code==0x30) code=4;
  }
  else code=0;

 }
 else
 if(fn==Device_DataBits)
 {
  code=serialdevcall(15,port,-1,0);
  if(p2==1)
  {
   code=code & ~0x3; // zero data bits 0,1
   if(p1==7) code|=0x1;
   else
   if(p1==6) code|=0x2;
   else
   if(p1==5) code|=0x3;
   code=serialdevcall(15,port,code,0);
  }

  code=code & 0x3;

  if(code==0) code=8;
  else
  if(code==1) code=7;
  else
  if(code==2) code=6;
  else
  if(code==3) code=5;
 }
 else
 if(fn==Device_StopBits)
 {
  code=serialdevcall(15,port,-1,0);
  if(p2==1)
  {
   code=code & ~0x4; // zero stop bits 2
   if(p1==2) code|=0x4;
   code=serialdevcall(15,port,code,0);
  }

  if(code & 0x4) code=2;
  else           code=1;
 }
 else
 if(fn==Device_Status) // fn port XOR mask AND mask
 {
  // we are here to either read control lines, or write DTR

  code=serialdevcall(8,port,-1,0);

  if(!(p2 & SERIAL_DTR) || (p1 & SERIAL_DTR))
  {
   p2=p2 & SERIAL_DTR;
   p2=p2 >> 3;
   p1=p1 & SERIAL_DTR;
   p1=p1 >> 3;

   code&=p2|0xFFFFFFFE;
   code^=p1;

   code=serialdevcall(8,port,code,0);
  }

  temp=(code & 1)<<3;    // generate DTR bit

  code=serialdevcall(9,port,0,0);

  if(code & 0x2) temp|=SERIAL_DSR;
  if(code & 0x4) temp|=SERIAL_RI;
  if(code & 0x8) temp|=SERIAL_DCD;

  code=temp;
 }
 else
 if(fn==Device_Flow)   // fn port method 0(read)/1(write)
 {
  if(p2==1) code=serialdevcall(16,port,p1,0);  // fn, port, method/-1
  else      code=serialdevcall(16,port,-1,0);
 }
 else
 if(fn==Device_Select) // fn port 1(activate)/0(deactivate)
 {
  if(p1)
  {
   code=serialdevcall(17,port,0,0);    // init
   code=serialdevcall(8,port,3,0);     // raise RTS
  }
  else
  {
   code=serialdevcall(8,port,0,0);     // drop RTS
   code=serialdevcall(18,port,0,0);    // close
  }
 }
 else
 if(fn==Device_Break)  // fn port time
 {
  code=serialdevcall(11,port,p1,0);
 }
 else
 if(fn==Device_CountPurge) // fn port RX/TX 0(purge)/1(bytes)/2(free)
 {
  if(p1==RX)
  {
   if(p2==1) code=serialdevcall(5,port,0,0); // Check RX buffer port
   else
   if(p2==0) code=serialdevcall(7,port,0,0); // Flush RX buffer port
   else      code=-1;
  }
  else
  if(p1==TX)
  {
   if(p2==2) code=serialdevcall(4,port,0,0); // Check TX buffer port
   else
   if(p2==0) code=serialdevcall(6,port,0,0); // Flush TX buffer port
   else      code=-1;
  }
 }

 return(code);
}


void device_poll(int port)
{
 serialdevcall(19,port,0,0);
}



void device_terminate(void)
{
 int code;

 code=swi13(Device_Register,0,0,0);
 if(!code) osclis("RMKill Hearsay");
}



int device_initiate(void)
{
 string s;
 //-----------------
 string c_port;
 string c_port_no;
 string title;
 int x;
 //-----------------
 
 s="RMEnsure UtilityModule 3.10 RMEnsure UtilityModule 3.00 RMEnsure Hearsay 1.00 Run <Hearsay$Resources>.Hearsay";

 if(!osclis(s))
 {
  s="RMEnsure UtilityModule 3.10 RMEnsure Hearsay 1.00 Run <Hearsay$Resources>.HearsayA5D";
  if(!osclis(s))
  {
   s="RMEnsure Hearsay       1.00 Run <Hearsay$Resources>.HearsayA5";

   if(!osclis(s))
   {
    swi13(Device_Register,1,0,0);

    serialdevinit(1);

    //-----------------------------------------------
    title="Hearsay$title";
    x=getenvs(title);
  
    if(x!=0)sethearsaytitle(title);
    c_port_no="Hearsay$com_port_num";
    x=getenvs(c_port_no);
    if(x=0)
    {
     errorbox("Could not read system variable <Hearsay$com_port_no>, check !Run file");
    }
    c_port="Hearsay$com_port";
    x=getenvs(c_port);
    if(x=0)
    {
     errorbox("Could not read system variable <Hearsay$com_port>, check !Run file.");
    }
    setserialdev(c_port,stoi(c_port_no));

    devblockmode=serialdevread(0xC4) & 0x40;

//  Uncomment the next line if your DSP card has bug in block read/writes
//  devblockmode=0;
    return(0);
   }
  }
 }
 errorbox(s);
 return(1);
}

