/*->c.mnpdrvr */
/*=====================================================================

                      The Microcom MNP Library
                                        (Microsoft C Version)


        11/4/87 - explicit data typing...gp
        8/27/87 - rcv_lt() modified to use lcb.lcl_credit..gp

-----------------------------------------------------------------------

        LDRIVER - MNP Link RS-232 Interrupt Service Routines

     This module implements asynchronous I/O for IBM PC-compatible
     machines and thus much of this code may not be portable to 
     other dissimilar environments.

     This code implements Class 2 of the MNP link protocol,
     i.e. it uses the stop/start byte-oriented framing technique.
     A Class 3 implementation is not possible in the IBM PC 
     environment since the communications hardware is not capable
     of switching to synchronous (bit-oriented) operation.
     Protocol messages and procedures, however, are independent
     of the framing technique.          

     Global routines:

        - ascode: service I/O interrupt 
        - lne_stat: check carrier status 
        - trigger_sf: initiate transmit interrupts

=====================================================================*/

/* Header files
*/


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>

#include "h.mnpdat"
#include "h.llvl"
#include "h.mnp"




#include "h.os"
#include "h.wimp"
#include "h.sprite"
#include "h.wimpt"
#include "h.bbc"
#include "h.akbd"
#include "h.werr"
#include "h.flex"




#include "h.wos"
#include "h.main"
#include "h.serial"
#include "h.vax"






/* Send framer state names
*/
#define SF_INIT         0
#define SF_DLE          1
#define SF_STX          2
#define SF_INFO         3
#define SF_DLEINFO      4
#define SF_ETX          5
#define SF_FCS1         6
#define SF_FCS2         7

/* Receive framer state names
*/
#define RF_INIT         0
#define RF_DLE          1
#define RF_STX          2
#define RF_HDLEN        3
#define RF_HEADER       4
#define RF_INFOL        5
#define RF_INFO         6
#define RF_FCS1         7
#define RF_FCS2         8

/* Frame octet definitions
*/
#define SYN             0x16
#define DLE             0x10
#define ETX             3
#define STX             2

/* Length of header buffer (maximum acceptable LPDU header)
*/
#define MAX_HDRLEN      100

/* Bit set, clear and test definitions
*/
#define SETBIT1(bit)    lcb.status_1 |= bit;       
#define SETBIT2(bit)    lcb.status_2 |= bit;
#define SETBIT3(bit)    lcb.status_3 |= bit;
#define CLRBIT1(bit)    lcb.status_1 &= ~(bit);        
#define CLRBIT2(bit)    lcb.status_2 &= ~(bit);
#define CLRBIT3(bit)    lcb.status_3 &= ~(bit);
#define BIT1SET(bit)    (lcb.status_1 & bit)         
#define BIT2SET(bit)    (lcb.status_2 & bit)
#define BIT3SET(bit)    (lcb.status_3 & bit)




/* Local data
*/
USIGN_8 *sf_ptr,
     *rf_hptr,
     *rf_dptr;
USIGN_8 snd_char,
     rcv_char;





typedef struct  {
        USIGN_8 low;
        USIGN_8 hi;
        }  fcs;




fcs             snd_fcs,                        /* send fcs */
                rcv_fcs,                        /* calculated receive fcs */
                rfcs;                   /* received fcs */

USIGN_16 rdle_flg;                      /* 'previous char DLE' flag */
USIGN_16 rf_hdrlen,                     /* receive header length  */
    hdrcnt,                     
    rf_datlen,
    sf_state,                           /* send framer state var */
    rf_state,                           /* receive framer state var */
    sf_len,
    sf_busy,                            /* 'send frame' flag */
    sf_lt,                              /* 'sending LT' flag */
    frame_snt,                          /* 'info field sent' flag */ 
    frame_rcvd,                 /* 'frame received' flag */
    frame_dne;                          /* 'frame sent' flag */
USIGN_8 *rf_tiptr;

struct MNP_CB *p_mnpcb;
struct BLST *dat_struct, *hdr_struct;

USIGN_16 modem_out_busy;        /* RS-232 hardware status flag
                                                  0=transmitter not active
                                                  1=transmitter active */





/****************************************************************************/



void sar(reg,car) unsigned char * reg,* car;
{
 int temp;
 temp=(* reg) & 0x80;
 *car=(*reg) & 0x1;
 *reg=((*reg)>>1)|temp;
}



void rcl(reg,car) unsigned char * reg,* car;
{
 int temp;
 temp=(* reg) & 0x80;
 *reg=((*reg)<<1)|(*car);
 *car=temp >> 7;
}



void rcr(reg,car) unsigned char * reg,*car;
{
 int temp;
 temp=(*reg)&0x1;
 *reg=((*reg)>>1)|((*car)<<7);
 *car=temp;
}



void fcscalc(fcs * rcv_fcsp,USIGN_8  rcv_char)
{
 unsigned char temp,al,bl,cf;
 int i;

 al=bl=temp=(rcv_fcsp->hi)^rcv_char;

 for(i=0;i<7;i++)
 {
  sar(&bl,&cf);
  al=al+bl;
 }

 al&=0x1;
 bl=temp;
 cf=0;

 sar(&al,&cf);
 rcr(&bl,&cf);
 rcr(&al,&cf);
 sar(&bl,&cf);
 rcl(&bl,&cf);

 bl=bl^temp;

 rcr(&bl,&cf);
 rcr(&al,&cf);
 sar(&bl,&cf);
 rcl(&bl,&cf);

 if(cf) al++;

 al=al^(rcv_fcsp->low);

 rcv_fcsp->low=bl;
 rcv_fcsp->hi=al;
}




/*GLOBAL***************************************************************
                      keep calling this routine

**********************************************************************/

void mnpzero(void)
{
 static here;

 if(here) return;
 else
 {
  here=1;
  rcv_framer();
  snd_framer();
  mod_stat();
  here=0;
 }
}


/*GLOBAL***************************************************************

        lne_stat - physical-connection status routine

**********************************************************************/

SIGN_16 lne_stat(void)
{
 /* If the carrier detect bit is 1, return success (physical-connection
 ** still active).  Otherwise, return with a failure indication. 
 */

 if(online) return(SUCCESS);
 else       return(1);
}




/*GLOBAL***************************************************************

        trigger_sf - initiate send framer

**********************************************************************/

void trigger_sf(void)
{

/* If the RS-232 transmitter is not already busy, then a byte must 
** be placed in the transmit buffer to begin interrupt-driven sending.
** This is the start of a frame and thus the first byte to be sent is
** the SYN which begins the frame start flag.  Also the send framer's
** state variable is initialed here.
*/ 
if (!modem_out_busy)
        {
        modem_out_busy = TRUE;
        sf_state = SF_DLE;                              
        outbytelo(SYN);
        modem_out_busy = FALSE;
        }
}

/*LOCAL----------------------------------------------------------------

        get_char - get byte from send buffer

---------------------------------------------------------------------*/

USIGN_8 get_char(void)
{

/* Return the byte at the current send framer pointer position.
** In the process, advance the point for next time and reduce the
** number of bytes remaining by one.
*/
sf_len--;                                       
return (*sf_ptr++);
}

/*LOCAL----------------------------------------------------------------

        mod_stat - modem status change interrupt service routine

---------------------------------------------------------------------*/

void mod_stat(void)
{
 /* If carrier has been lost, signal the mainline that the link
 ** also has been lost by lowering the 'link established' flag. 
 */

 if(!online) CLRBIT1(LINK_EST)   
}



/*LOCAL----------------------------------------------------------------

        put_char - store an LT data byte

---------------------------------------------------------------------*/

void put_char(void)
{

/* Ignore this byte if the amount of received data exceeds the
** negotiated maximum user data size for the LT LPDU.  This could occur
** if the frame is broken or if the correspondent is misbehaving. 
*/
if (rf_datlen >= lcb.max_data_sz)
        return;

/* Otherwise, accept this byte. 
*/
rf_datlen++;                            
fcscalc (&rcv_fcs, rcv_char);

/* Now store the data byte in the receive buffer using the temporary
** insert pointer.  Advance the pointer, checking for wrap.
*/
*rf_tiptr++ = rcv_char;                 
if (rf_tiptr >= rbuf + RBUF_LEN)
        rf_tiptr = rbuf;

}

/*LOCAL----------------------------------------------------------------

        put_hdr - store an LPDU header byte

---------------------------------------------------------------------*/

void put_hdr(void)
{

/* Store header byte in header buffer.  Increment count of bytes in
** the buffer and include the byte in the receive fcs.
*/
rf_hdrlen++;

*rf_hptr++ = rcv_char;                        

fcscalc (&rcv_fcs, rcv_char);
}

/*LOCAL----------------------------------------------------------------

        RCV_FRAMER - Receive Framer

---------------------------------------------------------------------*/


void rcv_framer(void)
{
 int gbyte;

 gbyte=getbytelo();
 if(gbyte==-1) return;
 else          rcv_char = gbyte;

/* Take action based on current state of receive finite state machine.
** 'rf_state' is the state variable.
*/


switch (rf_state)
        {

/* State RF_INIT - This is the initial state.  Search the received
** data stream for a SYN character which may be the beginning
** of the start flag sequence of a byte mode frame. 
*/
        case RF_INIT:
                if (rcv_char == SYN)
                        rf_state++;     
                break;

/* State RF_DLE - Search for the second byte in the start flag, a DLE
** byte.  Go back to the initial state (SYN search) if this byte is
** something other than a DLE.
*/
        case RF_DLE:
                if (rcv_char == DLE)
                         rf_state++; 
                else 
                        rf_state--;             
                break;

/* State RF_STX - If the received byte is an STX then a frame start flag
** has been found.      In this case, initialize to receive an LPDU.
** Otherwise, go back to looking for a SYN. 
*/
        case RF_STX:
                if (rcv_char == STX)
                  {
                   if(get_b(&rlkb,&hdr_struct))
                   {
                    rf_state = RF_INIT;
                   }
                   else
                   {
                    rf_hptr = hdr_struct->bptr;
                    hdrcnt=rf_hdrlen=rf_datlen=rdle_flg=0;
                    rcv_fcs.hi=rcv_fcs.low=NULL;
                    rf_state++;
                   }
                  }
                else                            
                        rf_state = RF_INIT;         
                break;

/* State RF_HDLEN - Get the first byte of the LPDU, the length
** indicator.  Wait for the next byte if this happens to be a DLE (in a
** good frame the DLE will be doubled).  If the value  received is
** greater than this implementation's maximum header length, it must be
** a protocol error or the value is broken.  In either case, ignore this
** LPDU. This is like a broken frame, so force the sender to ack. 
*/
        case RF_HDLEN:
                if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
                        {
                        rdle_flg = 0;           
                
                        if (rcv_char > MAX_HDRLEN)
                                {
                                ret_b(&rlkb, hdr_struct);       
                                SETBIT2(FORCE_ACK)
                                rf_state = RF_INIT;             
                                }
                        else
                                {

                                hdrcnt = rcv_char;
                                put_hdr();
                                rf_state++;
                                }
                        }
                else
                        rdle_flg = TRUE;
                break;

/* State RF_HEADER - Receive header bytes, assuming the length indicator
** was correct.  Any bytes following the header will temporarily be
** assumed to be data.
*/
        case RF_HEADER:
                if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg)) 
                        {
                        rdle_flg = FALSE;
                        put_hdr();
                        if (!(--hdrcnt))
                                rf_state++;
                        }
                else
                        rdle_flg = TRUE;
                break;

/* State RF_INFOL -  Receive first data byte.  Note that we must check
** for the stop flag just in case the length indicator byte was broken.  ** When the first data byte is received, the temporary insert pointer
** into the data receive buffer is initialized to the current location
** of the real pointer.
*/
        case RF_INFOL:
                if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
                        {
                        if ((rcv_char == ETX) && rdle_flg) 
                                {
                                fcscalc(&rcv_fcs, rcv_char);
                                rf_state = RF_FCS1;
                                }
                        else
                                {
                                rdle_flg = FALSE;
                        rf_tiptr = rb_iptr;             
                                put_char();
                                rf_state++;                
                                }
                        }
                else
                        rdle_flg = TRUE;
                break;

/* State RF_INFO - Receive data bytes until the stop flag is found.
*/
        case RF_INFO:
                if ((rcv_char != DLE) || ((rcv_char == DLE) && rdle_flg))
                        {
                        if ((rcv_char == ETX) && rdle_flg)
                                {
                                fcscalc(&rcv_fcs, rcv_char);
                                rf_state++;             
                                }
                        else
                                put_char();
                        rdle_flg = FALSE;
                        }
                else
                        rdle_flg = TRUE;
                break;

/* State RF_FCS1 - Receive first byte of fcs.
*/
        case RF_FCS1:
                rfcs.hi = rcv_char;
                rf_state++;
                break;

/* State RF_FCS2 - Receive second (low) byte of fcs and take end-of-
** frame action.  If the calculated fcs is not identical to that
** received, the received frame is "broken".  Signal this to the sender
** by setting the LPDU type to 0 and setting the FORCE_ACK flag.  If the
** FCS's match, take action based on the type of LPDU received. 
*/
        case RF_FCS2:
                if ((rcv_fcs.hi != rfcs.hi) || (rcv_fcs.low != rcv_char))
                        {
                        lcb.lpdu_type = 0;
                        SETBIT2(FORCE_ACK);
                        ret_b(&rlkb, hdr_struct);
                        }
                else
                        {

                        switch (lcb.lpdu_type = *(hdr_struct->bptr + PDU_TYPE))
                                {
                                case LT:
                                        rcv_lt();
                                        break;

                                case LA:
                                        rcv_la();
                                        break;

/* Link Request - If an LR arrives during what we think is the data
** phase, the other side may be retransmitting. In this case, just
** discard the LR.  Otherwise, save the LR for the mainline to process.
** In either case, signal the mainline to ack. 
*/
                                case LR:
                                        if (BIT1SET(LINK_EST))
                                                ret_b(&rlkb, hdr_struct);
                                        else
                                                rlkb.used_lst = hdr_struct;  
                                        SETBIT2(FORCE_ACK)
                                        break;

                                case LN:
                                        rcv_ln();
                                        break;

                                case LNA:
                                        rcv_lna();
                                        break;

/* Link Disconnect - Signal link termination to the mainline.  If the LD
** contains a user reason byte (signalled by parm 1 having a value of
** 255), save it in the LCB. 
*/
                                case LD:
                                        CLRBIT1(LINK_EST)
                                        if (*(hdr_struct->bptr + 4) == 255)
                                                {
                                                p_mnpcb->ld_source = 1;
                                                p_mnpcb->ld_reason = 
                                                        *(hdr_struct->bptr+7);
                                                }
                                        else
                                                p_mnpcb->ld_source = 0;
                                        break;
                                }
                        }

/* Signal the mainline that a frame has been received and reset the
** receive frame state machine for the next frame. 
*/
                        frame_rcvd = TRUE;                      
                        rf_state = RF_INIT;     
                        break;
                }

}

/*LOCAL----------------------------------------------------------------

        rcv_la - process a received LA LPDU

---------------------------------------------------------------------*/

void rcv_la(void)
{

register USIGN_8 *p;
USIGN_16 i;
USIGN_8 lt_ret_seq;

/* Ignore this LA LPDU if a destructive attention is in progress. 
*/
if (BIT3SET(LN_SENT))
        {
        ret_b(&rlkb,hdr_struct);
        return;
        }

/* For reasons of robustness, check the validity of the LA LPDU 
*/
p = hdr_struct->bptr + 2;                       /* point to parm 1 */
if ((*p++ != 1) || (*p != 1))
        {
        ret_b(&rlkb,hdr_struct);
        return;
        }
p += 2;
if ((*p++ != 2) || (*p != 1))
        {
        ret_b(&rlkb,hdr_struct);
        return;
        }

/* If the receive sequence number of this LA is the same as the last
** one received, it may be an implied negative acknowledgment.  Force
** the mainline to consider retransmission by setting the 'force
** retransmission' flag.  Otherwise, save the new number as the last
** acked and set the 'good ack received' flag. 
*/
p -= 2;
if (*p == lcb.ltssn_acked)
        SETBIT2(FORCE_RET)
else
        {
        lcb.ltssn_acked = *p;
        SETBIT1(LA_RECEIVED)
        }

/* Process the new credit information received. If it is zero, return.
*/
p += 3;
if ((lcb.rem_credit = *p) == 0)
        {
        ret_b(&rlkb,hdr_struct);
        return;
        }

/* If credit is not zero and this ack was a good one, take into account
** the LT LPDUs that may have been sent since the correspondent sent
** this LA.  That is, subtract from the credit the number of these not
** yet acked LTs. 
*/
if (BIT1SET(LA_RECEIVED) && tbcnt)
        {
        lt_ret_seq = *(ftb.used_lst->bptr + LT_SEQ);

        if (lt_ret_seq <= lcb.ltssn_acked)
                i = (lcb.ltssn_acked - lt_ret_seq) + 1;
        else
                i = (256 - lt_ret_seq) + lcb.ltssn_acked + 1;

        i = tbcnt - i;
        if (lcb.rem_credit >= i)
                {
                lcb.rem_credit -= i;
                if (lcb.rem_credit < 0)
                        lcb.rem_credit = 0;
                }
        else
                lcb.rem_credit = 0;
        }

/* Clean up 
*/
ret_b(&rlkb, hdr_struct);

}

/*LOCAL----------------------------------------------------------------

        rcv_ln - process a received LN LPDU

---------------------------------------------------------------------*/

void rcv_ln(void)
{

register USIGN_8 *p;

/* Check the sequence number of this LN LPDU.  If it is the next one
** in the sequence space, signal its arrival to the sender by
** setting the LN_RECEIVED flag.  Also remember the type of the LN LPDU
** by saving the type parameter value in the LCB. 
*/
p = hdr_struct->bptr + 4;
if (*p == (USIGN_8) ++lcb.ln_rsn)
        {
        lcb.ln_rtype = *(p + 3);
        SETBIT3(LN_RECEIVED)
        }

/* If this is not the next one, then decrement the receive sequence
** number which was incremented above and signal the need to send an
** LNA LPDU.
*/
else
        {
        lcb.ln_rsn--;
        SETBIT3(FORCE_LNA)
        }

ret_b(&rlkb, hdr_struct);
}

/*LOCAL----------------------------------------------------------------

        rcv_lna -  process a received LNA LPDU

---------------------------------------------------------------------*/

void rcv_lna(void)
{


/* If the LNA LPDU is an acknowledgment of an outstanding LN, its
** sequence number will match that of the last LN sent. In this
** case, signal the sender that the LN has been acknowledged.  
** Otherwise, just ignore this LPDU. 
*/
if (*(hdr_struct->bptr + 4) == lcb.ln_ssn)
        {
        lcb.ln_ret_cnt = NULL;
        CLRBIT3(LN_SENT)
        CLRBIT3(LN_TIMER)
        tmr.ln = NULL;  
        }
ret_b(&rlkb, hdr_struct);
}

/*LOCAL----------------------------------------------------------------

        rcv_lt - process a received LT LPDU

---------------------------------------------------------------------*/

void rcv_lt(void)
{

/* Ignore this LT LPDU if a destructive attention is in progress. 
*/
if (BIT3SET(LN_SENT))
        {
        ret_b(&rlkb, hdr_struct);
        return;
        }

/* Reset window timer, if window timer is enabled. 
*/
if (BIT2SET(WNDW_TIMER))
        tmr.fcw = lcb.window_tmr;

/* Accept this LT LPDU if it is the next one in the sequence space
** and there is room for it (i.e. there is a receive credit).
*/
if ((lcb.lcl_credit > 0)
         && (*(hdr_struct->bptr + LT_SEQ) == (USIGN_8) ++lcb.lt_rsn))
        { 
        --lcb.lcl_credit;
        CLRBIT3(DUP_IGNORED)
        rb_iptr = rf_tiptr;     
        rb_cnt = rb_cnt + rf_datlen;
        }

/* Otherwise, ignore the LT LPDU. In this case, set the FORCE_ACK flag
** to get the sender to ack as rapidly as possible.
*/
else
        {
        --lcb.lt_rsn;
        SETBIT2(FORCE_ACK)

/* If the LT LPDU is the same as the last one which arrived, i.e. it is
** an immediate duplicate, and this is the first occurrence of the
** immediate duplicate, then don't force the sender to ack this time. 
*/
        if ((*(hdr_struct->bptr + LT_SEQ) == lcb.lt_rsn)
                && !BIT3SET(DUP_IGNORED))
                {
                CLRBIT2(FORCE_ACK)
                SETBIT3(DUP_IGNORED)            
                }
        }

/* Clean up 
*/
ret_b(&rlkb, hdr_struct);
}

/*LOCAL----------------------------------------------------------------

        snd_framer - handle a transmit holding register empty
                        interrupt.  This subroutine sends a byte-mode frame.

---------------------------------------------------------------------*/

void snd_framer(void)
{

/* Take action based on the current state of the framer state machine. 
*/
switch (sf_state)
        {

/* State SF_INIT - initial state.  If the mainline has designated an
** LPDU to frame, the sf_busy flag will be true.  If this is the case,
** begin the start flag of the frame. 
*/
    case SF_INIT:
        if (sf_busy)
                {
                snd_char = SYN;
                sf_state++;
                break;
                }
        else
                {
                frame_dne = 1;
                modem_out_busy = FALSE;
                return;
                }

/* State SF_DLE - Send DLE of start flag. 
*/
        case SF_DLE:
                snd_fcs.hi = snd_fcs.low = 0;
                snd_char = DLE;
                sf_state++;                             
                break;

/* State SF_STX - Send STX of start flag. 
*/
        case SF_STX:
                snd_char = STX;
                sf_state++;
                break;

/* State SF_INFO -  As long as there are info field bytes, send a 
** byte and double DLE's as necessary.  At the end of the information
** field of the frame, start the end flag.   Signal the mainline code
** that it can enqueue another frame if it is already waiting on
** 'frame_snt'.  Clearing 'sf_busy' will cause the framer to stop after
** the end flag unless another frame has been enqueued. 
*/
        case SF_INFO:
                if (sf_len > 0)
                        {
                        if ((snd_char = get_char()) == DLE)
                        sf_state++;
                        fcscalc(&snd_fcs, snd_char);
                        }
                else
                        {
                        sf_busy = sf_lt = FALSE;
                        frame_snt = TRUE;
                        snd_char = DLE;
                        sf_state = SF_ETX;
                        }
                break;

/* State SF_DLEINFO - Double an info field DLE.
*/
        case SF_DLEINFO:
                snd_char = DLE;
                sf_state--;
                break;

/* State SF_ETX - Send ETX of end flag.
*/
        case SF_ETX:
                snd_char = ETX;
                fcscalc(&snd_fcs, snd_char);
                sf_state++;
                break;

/* State SF_FCS1 - Send first byte of frame check sequence.
*/
        case SF_FCS1:
                snd_char = snd_fcs.hi;
                sf_state++;
                break;

/* State SF_FCS2 - Send second byte of frame check sequence.
** After this byte is sent, the send framer goes back to its
** initial state.
*/
        case SF_FCS2:
                snd_char = snd_fcs.low;
                sf_state = SF_INIT;
                break;
        }

/* Send byte out RS-232 port 
*/
outbytelo(snd_char);

}


/*****************************************************************************/

int mnpactive;
int mmici;
int mtick;

void drvr_ins(void)
{
 mnpactive=1;
 mmici=0;
/* dprintf(1,"mnpactive=%d",mnpactive); */
 addzeroevent(MNPZERO);
}


void drvr_rem(void)
{
 mnpactive=0;
/* dprintf(1,"mnpactive=%d",mnpactive); */
 remzeroevent(MNPZERO);
}


int mnpgetbyte(void)
{
 struct link_stat_blk lstat;
 static USIGN_8 rcv_data[80];
 static int retcode;
 static int n;

 if(mnpactive)
 {
  if(n==retcode)
  {
   mnpstatus(&lstat);
   {
    if(!lstat.l_status)
    {
     drvr_rem();
     return(-1);
    }
   }

   if(lstat.r_count>0)
   {
    retcode=mnpreceive(rcv_data,79);
    n=0;
   }
  }

  if(trickle)
  {
   if(clock()!=mtick)
   {
    mtick=clock();
    mmici+=rxbyterate;        /* vmic[rxrate]; */
   }

   if(mmici>=100) mmici-=100;
   else           return(-1);
  }

  if(n<retcode) return(rcv_data[n++]);
  else          return(-1);
 }
 else return(getbytelo());
}



int mnpoutbyte(int byte)
{
 static USIGN_8 buff[64];
 static int     bsize;
 int count;

 if(mnpactive)
 {
  buff[bsize++]=byte;
  if(bsize>1 && bsize<64) return(0);

  while(mnpactive)
  {
   count=mnpsend(buff,bsize);

   if(count==bsize || count<0)
   {
    bsize=0;
    break;
   }
   else
   if(count==0) pollzt();
   else   /* count < bsize */
   {
    memmove(buff,buff+count,bsize-count);
    bsize-=count;
   }
  } 
 }
 else
  outbytelo(byte);

 return(0);
}



void mnplinkneg(void)
{
 int speed;

/* mnpconnect(rate,format,port,mode); */

 speed=min(rxbitrate,txbitrate);

 if(speed<=110)  speed=B_110;
 else
 if(speed<=300)  speed=B_300;
 else
 if(speed<=1200) speed=B_1200;
 else
 if(speed<=2400) speed=B_2400;

 mnpconnect(speed,0,2,answer); 
}

void mnplinktrash(void)
{
 if(mnpactive)
 {
  mnpdisconnect();
 }
}

