//==========================================================================
//
//  Communication package: high-level functions (low-level in comirq.asm)
//
//  E. Franssen                 (C) n.v. dZine
//
//==========================================================================

#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <string.h>
#include "com.h"
#include "version.h"

/*-------------------------- External functions (comirq.asm) ---------------*/
extern void  interrupt far com_ihandler(void);
extern uint  com_addhandler(COM far *com);
extern uint  com_removehandler(COM far *com);
extern void  com_txempty(uint i8250);
extern uchar com_enable_pic(uint irq);
extern void  com_disable_pic(uint irq);

/*-------------------------- Local functions -------------------------------*/
static void com_debloq(COM *com);
#ifdef DIGICOM
static void digicom_get_format_baudrate(int *pformat, int *pbaudrate, COM *com);
static uint digicom_set_format_baudrate(int format, int baudrate, COM *com);
static void digicom_set_m_ctrl(COM *com);
static void digicom_try_tx(COM *com);
#endif

/*-------------------------- Constant definitions --------------------------*/
#define IRQ0_BASE   8                   // Convert hardware irq to soft irq
#define IRQ8_BASE   0x70

/* Offset 8250 registers */
#define COM_RX      0                   // Receive buffer register
#define COM_TX      0                   // Transmit holding register
#define COM_IER     1                   // Interrupt enable register
#define COM_IIR     2                   // Interrupt ident. register
#define COM_L_CTRL  3                   // Line control register
#define COM_M_CTRL  4                   // Modem control register
#define COM_L_STAT  5                   // Line status register
#define COM_M_STAT  6                   // Modem status register

// calc_flowsize(): Returns 87.5 % of buffersize (see also comirq.asm)
#define calc_flowsize(com)              (com->r_end - com->r_begin - \
                                         ((com->r_end - com->r_begin) / 8))

// changed from 16 to 24 : to support 2 digiboards, 16 ports.
#define isDIGI(com)                     (com->i8250 < 24)

/*-------------------------- Local variables -------------------------------*/
static uint com_cnv_baudrate[] =
{                                       // Baudrate conversion table
    2304,                               // COM_B50
    1536,                               // COM_B75
    1047,                               // COM_B110
    768,                                // COM_B150
    384,                                // COM_B300
    192,                                // COM_B600
    96,                                 // COM_B1200
    48,                                 // COM_B2400
    24,                                 // COM_B4800
/*	17,									 COM_B6776    */
    16,                                 // COM_B7200
    12,                                 // COM_B9600
    10,                                 // COM_B11520
    9,                                  // COM_B12800
    8,                                  // COM_B14400
    6,                                  // COM_B19200
    3,                                  // COM_B38400
    2,                                  // COM_B57600
    1                                   // COM_B115200
}  ;

static uchar com_cnv_format[] =
{                                       // format conversion table
    0x02,                               // F7BNP1S
    0x06,                               // F7BNP2S
    0x1a,                               // F7BEP1S
    0x1e,                               // F7BEP2S
    0x0a,                               // F7BOP1S
    0x0e,                               // F7BOP2S
    0x03,                               // F8BNP1S
    0x07,                               // F8BNP2S
    0x1b,                               // F8BEP1S
    0x0b,                               // F8BOP1S
    0x1f,                               // F8BEP2S
    0x0f                                // F8BOP2S
};
#ifdef DIGICOM
// Digiboard: bh (parity), bl (stop bits), ch (character length)
static struct { int regbx; char regch; } digicom_cnv_format[] =
{                                       // format conversion table
    0x0000, 2,                          // F7BNP1S
    0x0001, 2,                          // F7BNP2S
    0x0200, 2,                          // F7BEP1S
    0x0201, 2,                          // F7BEP2S
    0x0100, 2,                          // F7BOP1S
    0x0101, 2,                          // F7BOP2S
    0x0000, 3,                          // F8BNP1S
    0x0001, 3,                          // F8BNP2S
    0x0200, 3,                          // F8BEP1S
    0x0100, 3,                          // F8BOP1S
    0x0201, 3,                          // F8BEP2S
    0x0101, 3                           // F8BOP2S
};

// Digiboard: cl = baudrate but other values of our COM 
static uint digicom_cnv_baudrate[] =
{                                       // Baudrate conversion table
    0x0d,                               // COM_B50
    0x0e,                               // COM_B75
    0x00,                               // COM_B110
    0x01,                               // COM_B150
    0x02,                               // COM_B300
    0x03,                               // COM_B600
    0x04,                               // COM_B1200
    0x05,                               // COM_B2400
    0x06,                               // COM_B4800
/*    0xff,								 COM_B6776: Not available*/
    0xff,                               // COM_B7200: Not available
    0x07,                               // COM_B9600
    0xff,                               // COM_B11520: Not available
    0xff,                               // COM_B12800: Not available
    0xff,                               // COM_B14400: Not available
    0x08,                               // COM_B19200
    0x09,                               // COM_B38400
    0x0a,                               // COM_B57600
    0x0c                                // COM_B115200
};
#endif

typedef struct
{
    long num_baud;                      // baudrate in decimal (e.g. 300)
    int  com_baud;                      // corresponding COM_Bxxx constant
} COM_NUM_BAUDRATE;

static COM_NUM_BAUDRATE com_num_baudrates[] =
{
    50,     COM_B50,
    75,     COM_B75,
    110,    COM_B110,
    150,    COM_B150,
    300,    COM_B300,
    600,    COM_B600,
    1200,   COM_B1200,
    2400,   COM_B2400,
    4800,   COM_B4800,
/*    6776,   COM_B6776,  */
    7200,   COM_B7200,
    9600,   COM_B9600,
    11520,  COM_B11520,
    12800,  COM_B12800,
    14400,  COM_B14400,
    19200,  COM_B19200,
    38400,  COM_B38400,
    57600,  COM_B57600,
    115200, COM_B115200,
    0,        0
};

/*--------------------------- Source code ----------------------------------*/
#pragma warning(disable : 4704)         // inline asm precludes global optim.'s

/**************************************************************************
*
* Define a communication handler for a specific com port
*
* !!! Notes for DigiBOARD support:
*       - the rxbuffer and txbuffer is used for intermediate buffering
*         (thus to avoid software interrupt calls for each character)
*       - com_close does not restore the old speed & format controls
*       - no support for handshaking and flow control yet
*
* port        COM_1, COM_2, ..., COM_PCOUNT -> use a 8250 (16550) COM port
*             DIGICOM_1..DIGICOM_PCOUNT -> Use a specific DigiBOARD port
* irq         Interrupt nr, unnecessary (0) for COM1, COM2 and DIGICOM_xxx
* addr        Address of 8250, unnecessary (0) for COM1, COM2 and DIGICOM_xxx
* rxbuffer    Ptr to Receive buffer
* rxsize      Size of Receive buffer (advisory 2 Kb.)
* txbuffer    Ptr to Transmit buffer
* txsize      Size of Transmit buffer (advisory 512 b.)
*
* Returns:    1 if ok, 0 if error (e.g. no com port found, ...)
*
*/
uint com_define(uint port, uint irq, uint addr, uchar _far *rxbuffer, uint rxsize,
                uchar _far *txbuffer, uint txsize, COM *com)
{  
    INSERT_VERSION
    uint ret = 0;

    if (!rxbuffer || !rxsize || !txbuffer || !txsize)
        return 0;
        
    memset(com, 0, sizeof(COM));        // Init structure
    if (port == COM_1)                  // If com1,2 --> Fixed irq, i8250
    {
    	com->irq = (irq == 0) ? 4 : (uchar)irq;
    	com->i8250 = (addr == 0) ? 0x3F8 : addr;
    }
    else if (port == COM_2)
    {
    	com->irq = (irq == 0) ? 3 : (uchar)irq;
    	com->i8250 = (addr == 0) ? 0x2F8 : addr;
    }
    else if (port >= COM_3 && port < COM_PCOUNT)
    {  
        com->irq = (uchar)irq;          // Save irq
        com->i8250 = addr;              // Save address 8250
    }  
    #ifdef DIGICOM
    else if (port >= DIGICOM_1 && port < DIGICOM_1 + DIGICOM_PCOUNT)
        com->i8250 = port - DIGICOM_1 + 5;  // Digiboard: adjust port nr to digi range
    #endif
    else
        return 0;                       // Invalid port: refuse it...
            
    com->r_begin = rxbuffer;
    com->r_end = rxbuffer + rxsize;
    com->t_begin = txbuffer;
    com->t_end = txbuffer + txsize;
    com->f_rstart = 0x11;               // XON/XOFF characters flow control
    com->f_rstop = 0x13;

    ret = com_check(com->i8250);    // See if a com port found at address
    if (ret && !isDIGI(com))
        ret = com_addhandler(com);  // Add handler in ASM-driver
    
    if (!ret)
        com->i8250 = 0;                 // Remember com_define() failed

    return ret;
}  

/**************************************************************************
*
* Init com port:
*      Save old 8250 registers
*      Set baudrate, format, dtr, rts to given values
*          Note: if a parameter is COM_CURRENT, then par. is not modified,
*      Init 8250, enable irq (remember: out 2 lo)
*      Initialize soft modem status, line status
*
* see functions com_baudrate(), com_format(), com_dtr() and
*               com_rts() for parameter values
*
* Return:  1 if ok, 0 if wrong parameters
*
*/
uint com_open(int baudrate, int format, int dtr, int rts, COM *com)
{  
    uint irq;
    #ifdef DIGICOM
    uint port;
    uchar rbl;
    #endif

    if (com->oldhandler != NULL || com->i8250 == 0)
        return 0;                       // This function must be called once

    /* Prepare stuff before communication really starts (thus, BEFORE
       RTS and DTR is set
    */
    com->unget = -1;                    // Clear unget-buffer
    com->t_idle = 1;                    // Say Tx-state idle (no irq Tx now)
    com->r_put = com->r_get = com->r_begin; /* Init get/put pointers */
    com->t_put = com->t_get = com->t_begin;
    com->r_count = com->t_count = com->r_error = 0;

    if (!isDIGI(com))
    {
        /* Map hardware irq to software irq */
        /* BUG FIX Stefan De Laere */
        /* irq = com->irq + ((com->irq < 8) ? IRQ0_BASE : IRQ8_BASE);*/
        irq = com->irq + ((com->irq < 8) ? IRQ0_BASE : IRQ8_BASE - 8);
        
        
        com->oldhandler = _dos_getvect(irq);/* Save current interrupt vector */


        /* Init soft modem and line control/status registers */
        com->old_ier = (uchar)inp(com->i8250 + COM_IER);
        com->old_l_ctrl = (uchar)inp(com->i8250 + COM_L_CTRL);
        com->old_m_ctrl = (uchar)inp(com->i8250 + COM_M_CTRL);
    
        com->l_ctrl = com->old_l_ctrl & (uchar)(~0x80); // Be sure no DLAB
        com->m_ctrl = com->old_m_ctrl;

        com->m_ctrl |= 0x08;            // out2 lo - modem control reg. */
        _disable();                     // CLI
        _dos_setvect(irq, com_ihandler);// Set vector to our irq-handler
        _enable();                      // STI
        com_debloq(com);                // Debloq' 8250 (GVR)
    }
    #ifdef DIGICOM
    else
    {
        _FP_OFF(com->oldhandler) = 1;   // indicate as initialized...
        port = com->i8250;
        _asm
        {
            mov     dx, port
            mov     ax, 0x0500          // Ext. Comm. Port Control: Read modem control reg.
            int     0x14
            mov     rbl, bl
        }
        com->m_ctrl = com->old_m_ctrl = rbl;
    }
    #endif

    if (dtr != COM_CURRENT)
    {                               // Set DTR if necessary
        com->m_ctrl = com->m_ctrl & (uchar)(~0x01);
        if (dtr)
          com->m_ctrl |= 1;
    }  
    if (rts != COM_CURRENT)
    {                               // Set RTS if necessary
        com->m_ctrl = com->m_ctrl & (uchar)(~0x02);
        if (rts)
          com->m_ctrl |= 2;
    }

    com->old_baudrate = com_baudrate(COM_CURRENT, com);  /* Read current speed*/
    com_format(format, com);            // Set format and baudrate
    com_baudrate(baudrate, com);
    
    if (!isDIGI(com))
    {
        com->old_pic_state = com_enable_pic(com->irq);  // Enable int controller
        _disable();                         // CLI
        outp(com->i8250 + COM_IER, 0x09);   // Int.enable on Rx + modem status
        /*** When really ready for receiving, set DTR/RTS ***/
        outp(com->i8250 + COM_M_CTRL, com->m_ctrl);  // Really program in 8250
        _enable();                          // STI

        com->l_stat = (uchar)inp(com->i8250 + COM_L_STAT);
        com->m_stat = (uchar)inp(com->i8250 + COM_M_STAT);
    }
    #ifdef DIGICOM
    else
    {
        rbl = com->m_ctrl;
        port = com->i8250;
        _asm
        {
            mov     dx, port
            mov     ax, 0x0501          // Ext. Comm. Port Control: Write modem control reg.
            mov     bl, rbl
            int     0x14
        }
    }
    #endif
    return 1;
}  

/**************************************************************************
*
* Close com port
*    Set baudrate, format, dtr, rts to given values
*           Note: if a parameter is COM_CURRENT, then par. is not modified
*                 if a parameter is COM_RESTORE, then original value from
*                                                com_open() is restored
*    Restore irq
*    Restore irq vector
*
* !!! Note: for a Digiboard COM port, baudrate and format are not restored !!!
*
* see functions com_baudrate(), com_format(), com_dtr() and
*               com_rts() for parameter values
*
* Return: 1 if ok, 0 if error
*
*/
uint com_close(int baudrate, int format, int dtr, int rts, COM *com)
{  
    uint irq;

    if (com->oldhandler == NULL || com->i8250 == 0)
      return 0;                         /* This function must be called once */

    if (!isDIGI(com))
    {
        /* Map hardware irq to soft.irq */
        irq = com->irq + ((com->irq < 8) ? IRQ0_BASE : IRQ8_BASE - 8);
    
        /*** Set or Restore data format ***/
        if (format == COM_RESTORE)
            outp(com->i8250 + COM_L_CTRL, com->old_l_ctrl);
        else if (format != COM_CURRENT)
            com_format(format, com);
    
        /*** Set or Restore baudrate ***/
        if (baudrate == COM_RESTORE)
          baudrate = com->old_baudrate;
        com_baudrate(baudrate, com);

        _disable();
    
        /*** Set or Restore DTR ***/
        if (dtr == COM_CURRENT)
            dtr = com_dtr(COM_CURRENT, com);
        if (dtr != COM_RESTORE)
            com->old_m_ctrl = (com->old_m_ctrl & (uchar)(~0x01)) |
                              ((dtr) ? (uchar)0x01 : (uchar)0);
        /*** Set or Restore RTS ***/
        if (rts == COM_CURRENT)
            rts = com_rts(COM_CURRENT, com);
        if (rts != COM_RESTORE)
            com->old_m_ctrl = (com->old_m_ctrl & (uchar)(~0x02)) |
                              ((rts) ? (uchar)0x02 : (uchar)0);
        /*** DTR, RTS + Out2 now physically to 8250 ***/
        outp(com->i8250 + COM_M_CTRL, com->old_m_ctrl);
    
        /*** Restore interrupt controller ***/
        if (com->old_pic_state == 0)
            com_disable_pic(com->irq);      /* Disable interrupt controller */
        /*** Restore irq-state on 8250 ***/
        outp(com->i8250 + COM_IER, com->old_ier);
        com_debloq(com);                    /* Soft reset 8250 */
    
        /*** Restore DOS-vector ***/
        _dos_setvect(irq, com->oldhandler);
        _enable();
        com->oldhandler = NULL;             /* Indicate com_close() done */
    }
    #ifdef DIGICOM
    else
    {
        int port = com->i8250;
        
        /*** Set parameters (restore not supported!) ***/
        com_format(format, com);        // com_format() checks for valid params
        com_baudrate(baudrate, com);
        if (dtr == COM_RESTORE)
            com->m_ctrl = (com->m_ctrl & (uchar) ~0x01) |
                          (com->old_m_ctrl & 0x01);
        if (rts == COM_RESTORE)
            com->m_ctrl = (com->m_ctrl & (uchar) ~0x02) |
                          (com->old_m_ctrl & 0x02);
        com_dtr(dtr, com);
        com_rts(rts, com);
        _asm
        {
            mov     ah, 0x09            // Clear buffers
            mov     dx, port
            int     0x14
        }
        com->oldhandler = NULL;         // Indicate com_close() done
    }
    #endif
    return 1;
}  

/**************************************************************************
*
* Release com port definition
*
* Return: 1 if ok, 0 if unknown
*
*/
uint com_undefine(COM *com)
{  
    uint ret = 0;

    // free in com_table
    if (com->i8250 != 0)                    // If handler added in com_define()...
    {
        if (!isDIGI(com))
            ret = com_removehandler(com);   // then remove handler from com_table
        memset(com, 0, sizeof(COM));    // If ok, clear structure...
    }
    return ret;
}  

/**************************************************************************
*
* Reset com port
*
*	Closes the com port.
*	Opens the com port again.
*
*/
void com_reset(COM *com)
{
    /* Close the port : keep the current settings. */
	com_close(COM_CURRENT, COM_CURRENT, COM_CURRENT, COM_CURRENT, com);
    /* Open the port again : keep the current settings */
	com_open(COM_CURRENT, COM_CURRENT, COM_CURRENT, COM_CURRENT, com);
}


/**************************************************************************
*
* Get character from Rx buffer
*
* Return:  0..0xff     character
*          -1          if no character available
*
*/
int com_rx(COM *com)
{  
    int ch;

    #ifdef DIGICOM
    if (isDIGI(com) && !com->r_count)
    {
        int port = com->i8250;
        int pseg, poff, len, available_len;
        
        // A Digiboard COM port and rxbuf empty -> read data 
        // But since read data blocks -> check whether data is present...
        if ((available_len=com_rx_available(com)) > 0)
        {
            // Free rx buffer space, but watch out with wrapping
            len = com->r_end - com->r_begin - com->r_count;
            if (len > (com->r_end - com->r_put))
                len = com->r_end - com->r_put;
            if (len > available_len)
                len = available_len;
            pseg = _FP_SEG(com->r_put);
            poff = _FP_OFF(com->r_put);
            _asm
            {
                push    es
                mov     ax, pseg
                mov     es, ax
                mov     bx, poff
                mov     cx, len
    
                mov     ah, 0x0F        // receive a string
                mov     dx, port
                int     0x14
    
                mov     len, ax         // # of chars read in buffer
                pop  es
                
            }
            com->r_count = len;
            com->r_put += len;
            if (com->r_put == com->r_end)
                com->r_put = com->r_begin;
        }
    }
    #endif
    if (com->r_count)
    {  
        if (!isDIGI(com) && (com->handshake & COM_RTS) && !(com->m_ctrl & 2))
        {                               /* RTS handshaking */
            /*** If r_count < 12.5 % buffersize, RTS on again ***/
            if (com->r_count < (uint)((com->r_end - com->r_begin) / 8))
              outp(com->i8250 +4, com->m_ctrl |=2); /* RTS On again */
        }  
        if (!isDIGI(com) && com->f_status == 0x81)
        {  
            /*** If r_count < 12.5 % buffersize, send XON ***/
            if (com->r_count < (uint)((com->r_end - com->r_begin) / 8))
              if (com_tx(com->f_rstart, com) != -1)
                com->f_status = 0x80;   /* clr b0 if XON sent */
        }  
        _disable();                     /* Watch out: r_count is also */
        com->r_count--;                 /* modified in irq routine */
        _enable();
        if (com->unget >= 0)
        {  
            ch = com->unget;
            com->unget = -1;
        }  
        else
        {  
            ch = *com->r_get;   /* get one character from receive buffer */
            if (++com->r_get == com->r_end)
              com->r_get = com->r_begin;
        }  
        return ch;
    }  
    else
        return -1;
}  

/***************************************************************************
*
* Get a character buffer from Rx buffer
*
* ptr   pointer to a buffer where the character will be put
* len   max. number of characters that may be put in buffer
*
* Returns:   count of characters read
*
*/
uint com_rx_buf(char __far *ptr, uint len, COM *com)
{
    uint ret = 0;
    int ch;
    
    //TBD: can be optimized (but watch interrupts...)
    while (len-- && (ch=com_rx(com)) >= 0)
    {
        *ptr++ = ch;
        ret++;
    }  

    return ret;
}

/**************************************************************************
*
* Put character in Tx buffer
*
* !Note: when using a port from a Digiboard, the user must call digicom_bg()
*        on a regular basis. This bg() will transmit a buffer from time to
*        time to the Digiboard and will eventually perform utx_handler() calls.
*
* ch       character to put
*
* Return:  0..0xff     character
*          -1          if buffer full (ch NOT in output buffer)
*
*/
int com_tx(uchar c, COM *com)
{  
    if (com->t_count < (uint)(com->t_end - com->t_begin))
    {  
        #ifdef DIGICOM        
        if (isDIGI(com))
        {
            *com->t_put = c;
            if (++com->t_put == com->t_end)
                com->t_put = com->t_begin;
            com->t_count++;
        }
        else
        #endif
        {
            _disable();
            if (com->t_idle && (com->t_count == 0) &&
                (!(com->handshake & COM_CTS) || (com->m_stat & 0x10)))
            {  
                /* !! If handshake CTS, and no CTS present -> to outputbuffer */
                com_start_tx(c, com);   // Tx c,enable interrupt, set idle=0
            }  
            else
            {  
                *com->t_put = c;
                if (++com->t_put == com->t_end)
                    com->t_put = com->t_begin;
                com->t_count++;
            }  
            _enable();
        }
        return c;
    }  
    return -1;
}  

/**************************************************************************
*
* Put a memory buffer in the Tx ring-buffer
*
* pbuf:     pointer to the data to put
* buflen:   count bytes that must be put
*
* Return:   >= 0    ok (count of bytes written, thus equal to buflen)
*           -1      not enough space in Tx buffer
*
*/
int com_tx_buf(char *pbuf, int buflen, COM *com)
{
    int ret = -1;
    
    if ((uint) buflen <= com_tx_free(com))
    {
        ret = 0;
        while (buflen--)
        {
            if (com_tx(*pbuf++, com) == -1)
                break;                  // Can normally not appear
            ret++;
        }  
    }
    return ret;
}

/**************************************************************************
*
* Put a C string (null terminated) in the Tx ring-buffer
* WARNING: the terminating null character (eos) is NOT written in the Tx buffer
*
* s:        pointer to the C-string to put
*
* Return:   >= 0    ok (count of bytes written, thus equal to strlen(s))
*           -1      not enough space in Tx buffer
*
*/
int com_tx_string(char *s, COM *com)
{
    return com_tx_buf(s, strlen(s), com);
}

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

/**************************************************************************
*
* Return:  Count of available char.'s in Rx-buffer
*
*/
uint com_rx_available(COM *com)
{  
    #ifdef DIGICOM
    if (isDIGI(com) && com->oldhandler)
    {
        uint digi_count = 0;
    
        // only check if the port is open
        int port = com->i8250;
    
        _asm
        {
            mov     ah, 0x0A            // get characters available
            mov     dx, port
            int     0x14
    
            mov     digi_count, ax
        }
        return com->r_count + digi_count;
    }
    #endif
    return com->r_count;
}  

/**************************************************************************
*
* Return: Count of free bytes (that still can be put) in Tx-buffer
*
*/
uint com_tx_free(COM *com)
{
    uint digi_free = 0;
    #ifdef DIGICOM
    if (isDIGI(com) && com->oldhandler)
    {
        int port = com->i8250;

        _asm
        {
            mov     ah, 0x12            // get TX buffer free space
            mov     dx, port
            int     0x14

            mov     digi_free, ax
        }
    }
    #endif
    return com->t_end - com->t_begin - com->t_count + digi_free;
}

#pragma check_stack(off)                // Called by CDMP during interrupt
/**************************************************************************
*
* Return: 1 if Tx busy (Tx-buffer not empty, Hold or Shift reg. not empty)
*         0    Tx idle
*
*/
uint com_tx_busy(COM *com)
{
    if (com->t_count)
        return 1;
        
    #ifdef DIGICOM
    if (isDIGI(com))    
    {
        // only check if the port is open
        if (com->oldhandler)
        {
            int port = com->i8250;
            uint txbufsize = 0, free = 0;

            _asm
            {
                mov     al, 0x00        // tx buffer
    
                mov     ah,0x1B         // get Tx buffer size
                mov     dx, port
                int     0x14
                mov     txbufsize, bx   // save it

                mov     ah, 0x12        // get TX buffer free space
                mov     dx, port
                int     0x14    
                mov     free, ax
            }
    
            if (free < txbufsize)
                return 1;               // Transmit buffer not empty: busy !
        }
        return 0;
    }
    #endif
    return ((inp(com->i8250 + COM_L_STAT) & 0x60) != 0x60) ?
           1 : 0;
}
#pragma check_stack()

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

/**************************************************************************
*
* Reprogram baudrate
*
* baudrate:   COM_B... for new setting, COM_CURRENT = unchanged
*
* Return:  COM_BCOUNT   invalid baudrate
*          COM_Bxxx     baudrate value
*          Note: to obtain current baudrate, use:
*                      baudrate = com_baudrate(COM_CURRENT);
*
*/
uint com_baudrate(int baudrate, COM *com)
{  
    uint i;

    if (baudrate >= COM_CURRENT && baudrate < COM_BCOUNT)
    {  
        #ifdef DIGICOM
        if (isDIGI(com))
        {
            int format;
            digicom_get_format_baudrate(&format, (int *) &i, com);
            if (baudrate == COM_CURRENT)
                baudrate = (int) i;
            else
            {
                /*** We always must set the baudrate and format at the same time.
                     Therefore, we first retrieve the format and then set both
                     at one.
                     However, someone could set the baudrate and afterwards 
                     initialise a valid format. Therefore, if we can't
                     retrieve a valid format (supported by the COM lib.), we'll
                     set a default format 8BNP1S and we'll hope they'll set the
                     format afterwards.
                ***/
                if (format < 0 || format >= COM_FCOUNT)
                    format = COM_F8BNP1S;
                
                if (!digicom_set_format_baudrate(format, baudrate, com))
                    baudrate = COM_BCOUNT;  // return error if failed
            }
        }
        else        
        #endif
        {
            com_txempty(com->i8250);          /* Wait till current Tx done... */
            _disable();
            /* Set Divisor Latch Access Bit */
            outp(com->i8250 + COM_L_CTRL, com->l_ctrl | 0x80);
            if (baudrate == COM_CURRENT)
            {  
                /* Re-read current baudrate, and re-convert to COM_B... value */
                i = (inp(com->i8250 + 1) << 8) | inp(com->i8250 + 0);
                for (baudrate = 0; baudrate < COM_BCOUNT; baudrate++)
                    if (i == com_cnv_baudrate[baudrate])
                        break;
            }  
            else
            {           /* Re-program Hi-byte and Lo-byte */
                outp(com->i8250 + 1, com_cnv_baudrate[baudrate] >> 8);
                outp(com->i8250 + 0, com_cnv_baudrate[baudrate] & 0xff);
            }  
            /* Re-set Divisor Latch Access Bit to 0 */
            outp(com->i8250 + COM_L_CTRL, com->l_ctrl);
            com_debloq(com);
            _enable();
        }
    }  
    return baudrate;
}  

/**************************************************************************
*
* Reprogram data format
*
* format:     COM_F... for new setting, COM_CURRENT = unchanged
*
* Return:  COM_FCOUNT   invalid format
*          COM_Fxxx     format value
*
*          Note: to obtain current format, use:
*                      format = com_format(COM_CURRENT);
*
*/
uint com_format(int format, COM *com)
{  
    uint i;
     
    if (format >= COM_CURRENT && format < COM_FCOUNT)
    {  
        #ifdef DIGICOM
        if (isDIGI(com))
        {
            int baudrate;
            digicom_get_format_baudrate((int *) &i, &baudrate, com);
            if (format == COM_CURRENT)
                format = (int) i;
            else
            {
                /*** We always must set the baudrate and format at the same time.
                     Therefore, we first retrieve the baudrate and then set both
                     at one.
                     However, someone could set the format and afterwards 
                     initialise a valid baudrate. Therefore, if we can't
                     retrieve a valid baudrate (supported by the COM lib.), we'll
                     set a default baudrate 19200 and we'll hope they'll set the
                     baudrate afterwards.
                ***/
                if (baudrate < 0 || baudrate >= COM_BCOUNT)
                    baudrate = COM_B19200;  
                
                if (!digicom_set_format_baudrate(format, baudrate, com))
                    format = COM_FCOUNT;    // return error if failed
            }
        }
        else        
        #endif
        {
            com_txempty(com->i8250);    // Wait till current Tx done...
            _disable();
            if (format == COM_CURRENT)
            {  
                /* Re-read current format, and re-convert to COM_F... value */
                i = (inp(com->i8250 + COM_L_CTRL)) & 0x1f;
                for (format = 0; format < COM_FCOUNT; format++)
                    if (i == com_cnv_format[format])
                        break;
            }  
            else
                outp(com->i8250 + COM_L_CTRL, com->l_ctrl = com_cnv_format[format]);
            com_debloq(com);
            _enable();
        }
    }  
    return format;
}  

#pragma check_stack(off)                // Called by CDMP during interrupt
/**************************************************************************
*
* Set/Clear or Get DTR
*
* dtr:     0 = OFF, 1 = ON, COM_CURRENT keep unchanged
*
* Return:  1 if dtr set, 0 if dtr not set
*
*/
uint com_dtr(int dtr, COM *com)
{
    if (dtr != COM_CURRENT)
    {
        #ifdef DIGICOM
        if (isDIGI(com))
        {
            com->m_ctrl = (com->m_ctrl & (uchar)(~0x01)) |
                          ((dtr) ? (uchar)0x01 : (uchar)0);
            digicom_set_m_ctrl(com);
        }
        else
        #endif
        {
            _asm pushf
            _asm cli
            com->m_ctrl = (com->m_ctrl & (uchar)(~0x01)) |
                          ((dtr) ? (uchar)0x01 : (uchar)0);
            outp(com->i8250 + COM_M_CTRL, com->m_ctrl);
            _asm popf
        }
    }
    return (com->m_ctrl & 0x01) ? 1 : 0;
}

/**************************************************************************
*
* Set/Clear or Get RTS
*
* rts:     0 = OFF, 1 = ON, COM_CURRENT keep unchanged
*          (see also note in com_handshake())
* Return:  1 if rts set, 0 if rts not set
*
*/
uint com_rts(int rts, COM *com)
{
    if (rts != COM_CURRENT && !(com->handshake & COM_RTS))
    {
        #ifdef DIGICOM
        if (isDIGI(com))
        {
            com->m_ctrl = (com->m_ctrl & (uchar)(~0x02)) |
                          ((rts) ? (uchar)0x02 : (uchar)0);
            digicom_set_m_ctrl(com);
        }
        else
        #endif
        {
            _asm pushf
            _asm cli
            com->m_ctrl = (com->m_ctrl & (uchar)(~0x02)) |
                          ((rts) ? (uchar)0x02 : (uchar)0);
            outp(com->i8250 + COM_M_CTRL, com->m_ctrl);
            _asm popf
        }
    }
    return (com->m_ctrl & 0x02) ? 1 : 0;
}
#pragma check_stack()

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

/**************************************************************************
*
* Switch hardware handshaking on/off
* Note: When using COM_RTS handshaking (independent of COM_CTS setting),
*       the function com_rts() can only be used to obtain the current
*       rts setting.
* Example: com_handshake(0)                    Sets handshake off
*          com_handshake(COM_RTS | COM_CTS)    Full RTS/CTS handshaking
*
* !!! Note: not supported for digicom !!!
*
* mode:   this argument is created by ORing together one or more
*         of the following constants:
*             Constant    Meaning
*             --------    -------
*             COM_RTS     RTS is switched OFF (inactive) when the
*                         receive buffer gets almost (87.5 %) filled.
*                         RTS is switched ON (active) again, when the 
*                         receive buffer is almost empty (12.5 % occupied).
*             COM_CTS     Data is only sent when CTS is ON(active)
*
* Return:  1 if mode set, 0 if invalid mode specified
*
*/
uint com_handshake(uint mode, COM *com)
{  
    int i;

    if (isDIGI(com) || (mode & ~(COM_RTS | COM_CTS)) != 0)
      return 0;                         /* Invalid mode specified */
    if (mode | COM_RTS)
    {                                   /* If RTS handshaking, set new state */
        if (com->r_count > (uint)calc_flowsize(com))
          com_rts(0, com);               /* Buffer almost full, so drop RTS */
        else
          com_rts(1, com);               /* No problems, so set RTS... */
    }  
    /*** When handshake was CTS, and now set off, re-enable Tx-interrupt
         if characters waiting in outputbuffer
    ***/
    if (!(mode & COM_CTS) && com->t_idle && com->t_count)
    {  
        _disable();
        com->t_idle = 0;
        com->t_count--;
        outp(com->i8250, *com->t_get);  /* Transmit first char. */
        if (++com->t_get == com->t_end)
          com->t_get = com->t_begin;
        i = inp(com->i8250 +1);
        outp(com->i8250 +1, i | 2);      /* Enable Tx interrupt */
        _enable();
    }  
    com->handshake = (uchar)mode;       /* Save handshake mode */
    return 1;
}  
 
#ifdef FULL_FLOWCONTROL_CODED
/**************************************************************************
*
* Switch flow control on/off (XON/XOFF)
*
* mode:   0 (off)     No flow control assumed
*         1 (rxbuf)   XOFF is transmitted when the receive buffer gets 
*                     almost (87.5 %) filled.
*                     XON is transmitted again, when the receive buffer 
*                     is almost empty (12.5 % occupied).
* !TBD!   2 (txbuf)   When XOFF is received, the COM stops transmitting
*                     When XON is received, the COM restarts transmitting
* !TBD!   3           both rx- and txbuf flow control.
* 
**/
void com_flowcontrol(uint mode, COM *com)
{  
    if (mode == 0 && com->f_status == 0x81)
    {                                   /* If flow ctrl was on, & now off, */
        com_tx(com->f_rstart, com);      /* then at least try to tx XON */
    }  
    /* Save new flow control state */
    com->f_status = (mode) ? (uchar)0x80 : (uchar)0;
}  
#endif

/**************************************************************************
*
* Gets the status of the RS232 input lines
*
* Return: integer value, which is a bit field, obtained by ORing together one
*                        or more of the following constants:
*         Constant    Meaning
*         --------    -------
*         COM_CTS     CTS ON (active)
*         COM_DSR     DSR ON (active)
*         COM_RI      Ring Indicator ON (active)
*         COM_DCD     DCD ON (active)
*
*/
uint com_status(COM *com)
{  
    #ifdef DIGICOM
    if (isDIGI(com))
    {
        int port = com->i8250;
        uchar ral;
        _asm
        {
            mov     ah, 3
            mov     dx, port
            int     0x14                // Get Status
            mov     ral, al
        }
        com->m_stat = ral;
    }
    #endif
    return com->m_stat & (COM_CTS | COM_DSR | COM_RI | COM_DCD);
}  

/**************************************************************************
*
* Check if a 8250 found at given address
*
* i8250   address of 8250, e.g. 0x3f8
*
* Return:  1 if ok, 0 if no 8250 found
*
*/
uint com_check(uint i8250)
{
    uint ret = 0;                       /* Suppose no 8250 */

    #ifdef DIGICOM
    // changed from 16 to 24 to support 16 ports (2 digiboards)
    if(i8250 < 24)
    {
        uint drvver, nchannels, lowest_channel;
        
        _asm
        {
            mov     ax, 0x0601          // Get driver information
            sub     bx, bx              // board 0
            mov     dx, i8250
            int     0x14

            mov     drvver, bx          // save driver version
            mov     nchannels, ax
            mov     lowest_channel, dx
        }

        // check if driver at least version 4.0.5, and if channel in range
        if (drvver >= 0x405 && i8250 >= lowest_channel && 
            i8250 < (lowest_channel + nchannels))
            ret = 1;
    }    
    else
    #endif
    {
        /*** Changed:
               (inp(i8250 + COM_IIR) & 0xf8) == 0 &&       IIR : 00000xxx 
              16550 uses b7, b6 and b3 for FIFO stuff...
        ***/
        if ((inp(i8250 + COM_IER) & 0xf0) == 0 &&       /* IER : 0000xxxx       */
            (inp(i8250 + COM_IIR) & 0x30) == 0 &&       /* IIR : xx00xxxx       */
            (inp(i8250 + COM_M_CTRL) & 0xe0) == 0)      /* MCT : 000xxxxx       */
        {
            /*** Probably a 8250, so try scratch pad...
                 However, there are still old (very very old) PC's (XT), where
                 the 8250 does NOT have the scratch pad, so don't use it anymore
            outp(i8250 + 7,0x5a);
            if (inp(i8250 + 7) == 0x5a)
            ***/
            ret = 1;                    /* The lucky guy has found one... */
            if ((inp(i8250 + COM_IIR) & 0xc0) != 0)
            {
                /* IIR, b6 & b7 '1' -> this is a 16550, with FIFO's enabled
                   Since we do not support FIFO functionality -> disable FIFO
                   For this: Set b0 of FIFO Control Register to 0. The FIFO Control
                             reg. is at location of IIR (8250: reg. is read-only)
                */
                _outp(i8250 + COM_IIR, 0);  // Clear all (can't read and mask)
            }
        }
    }

    return ret;
}

/***************************************************************************
*
* Convert a decimal number baudrate to the corresponding COM_Bxxx value
*
* num_baudrate:     a baudrate in a number, e.g. 300, 19200, ...
*
* Return:           COM_Bxxx    corresponding COM baudrate value
*                   -1          error: unknown baudrate
*
*/
int com_num_to_baudrate(long num_baudrate)
{
    int i, ret = -1;                    // suppose we won't find it

    for (i = 0; com_num_baudrates[i].num_baud != 0; i++)
    {
        if (com_num_baudrates[i].num_baud == num_baudrate)
        {
            ret = com_num_baudrates[i].com_baud;
            break;                  /* Found it, so successfull */
        }
    }
    return ret;
}

/***************************************************************************
*
* Convert a COM_Bxxx baudrate to the corresponding decimal number baudrate
*
* baudrate:         a COM baudrate (COM_Bxxx constant)
*
* Return:           >= 75       corresponding baudrate number (e.g. 300, 19200)
*                   -1          error: unknown baudrate
*
*/
long com_baudrate_to_num(int baudrate)
{
    int i;
    long ret = -1;                      // suppose we won't find it

    for (i = 0; com_num_baudrates[i].num_baud != 0; i++)
    {
        if (com_num_baudrates[i].com_baud == baudrate)
        {
            ret = com_num_baudrates[i].num_baud;
            break;                  /* Found it, so successfull */
        }
    }
    return ret;
}

/*------------------------ User functions ----------------------------------*/

/**************************************************************************
*
* Install a user defined interrupt Rx (called urx) handler.
* If a urx handler is installed, then the com library will call this
* function each time a character is received under interrupt.
* Note of course that the urx handler is part of the com interrupt handler.
* This means that the urx handler must deal with the following:
*    - return as fast as possible (no locking)
*    - the stack is set to the interrupt stack, not to the C-stack
*      (thus if using local pointers to data, use _far pointers,
*       initialized to global variables)
*    - ds is set to the C data segment on entry by the com library
*    - all registers are saved and restored by the com library
*    - function must be compiled without stack checking !!!
*
* The com library calls the urx handler with following syntax:
*      uint (*urx)(uchar c, void *uptr)
*      Param:  c                    character just received
*              uptr                 ptr, installed with com_set_uptr()
*      Return: COM_URX_NONE         normal
*              COM_URX_PROCESS | c  com library will process character 'c'
*                                   normally (put in rx-buffer)
*
* urx      != NULL  pointer to user urx handler to enable urx
*          NULL     disable urx handler
*
* Return:  old_urx  previous urx handler (can be used to restore at end)
*
*/
uint (far *com_set_urxhandler(uint (far *urx)(uchar c, void *uptr), COM *com))()
{  
    uint (far *old_urx)();

    _disable();
    old_urx = com->urx;
    com->urx = urx;
    _enable();

    return old_urx;
}  

/**************************************************************************
*
* Install a user defined interrupt Tx (called utx) handler.
* If a utx handler is installed, then the com library will call this
* function at each transmit interrupt.
* The function 'com_start_tx()' will have to be called to start the Tx
* interrupt. Only from that moment on, the utx handler is called at each
* Tx interrupt. When done, the utx handler will (through a return value)
* have to disable the Tx interrupt.
* Note of course that the utx handler is part of the com interrupt handler.
* This means that the utx handler must deal with the following:
*    - return as fast as possible (no locking)
*    - the stack is set to the interrupt stack, not to the C-stack
*      (thus if using local pointers to data, use _far pointers,
*       initialized to global variables)
*    - ds is set to the C data segment on entry by the com library
*    - all registers are saved and restored by the com library
*    - function must be compiled without stack checking !!!
*    - Flow Control and Handshaking are disabled !!! (if necessary,
*      perform CTS,... checking in utx handler)
*    - the com_tx() function may no longer be called
*
* The com library calls the utx handler with following syntax:
*      uint (*utx)(void *uptr)
*      Param:  uptr                 ptr, installed with com_set_uptr()
*      Return: COM_UTX_FINISHED     Tx done, so kill Tx interrupt
*              COM_UTX_PROCESS | c  com library will Tx character 'c'
*      Note: for Digicom, the utx handler must return a COM_UTX_LAST
*            along with the last character. The digicom_bg() will then
*            send the buffer to the digiboard. The next upcall to utx 
*            will only appear when the total buffer is sent.
*            This allows CDF/CDMP to work transparently for a normal port
*            and a digi-port (CDMP 'blocks' until the last character is sent).
*
* utx      != NULL  pointer to user utx handler to enable utx
*          NULL     disable utx handler
*
* Return:  old_utx     previous utx handler (can be used to restore at end)
*
*/
uint (_far *com_set_utxhandler(uint (_far *utx)(void *uptr), COM *com))()
{  
    uint (far *old_utx)();

    _disable();
    old_utx = com->utx;
    com->utx = utx;
    _enable();

    return old_utx;
}  

#pragma check_stack(off)                // Called by CDMP during interrupt
/**************************************************************************
*
* Tx first character immediately and enable Tx interrupt
*
* c       character to transmit
*
*/
void com_start_tx(uchar c, COM *com)
{
    int i;

    #ifdef DIGICOM
    if (isDIGI(com))
    {
        com_tx(c, com);
        /*** We don't use the t_idle flag the same manner as for the normal
             COM ports, since we don't Tx under interrupt.
             We'll use it to remember whether to call the utx_handler()
             or not in the digicom_bg().
             Furthermore, once the utx_handler returns COM_UTX_LAST, we'll
             set t_idle to 0xff. From then on, we'll wait until the 
             Tx buffer is completely sent to the Digiboard.
             
        ***/
        com->t_idle = 0;
        return;
    }
    #endif
    _asm pushf                          // disable irq (ext. user can call
    _asm cli                            // this func.)

    com->t_idle = 0;
    i = inp(com->i8250 +1);
    outp(com->i8250 +1, i | 2);          /* Enable Tx interrupt */
    outp(com->i8250, c);                /* Transmit first char. */
//  outp(com->i8250 +1, i | 2);          /* Enable Tx interrupt */

    /*** On a speedy machine (386, 50 MHz), it seems that, sometimes, the
         Tx stops. We saw it using the CDMP as master. The 0xff (first byte,
         transmitted in this function) gets out but the next bytes do not.
         It seems that we get a 'modem status' interrupt instead of a Tx irq,
         and the THRE bit is not set so that we can't Tx.
         After hours and hours debugging, we've found out that a small delay
         before the interrupt is serviced solves the problem.
         We do not understand......... !@!@>?# Grmbl $!<?@#$!?
    ***/
    inp(0x3e8 +7);                      // read scratch pad to delay a bit...
    inp(0x3e8 +7);
    inp(0x3e8 +7);
    _asm popf                           // restore previous irq state
}
#pragma check_stack()

/***************************************************************************
*
* Digiboard Bg loop:
*   - Transmit a data block to the Digiboard when possible
*   - uTx and uRx handler support
*
*/
void digicom_bg(COM *com)
{
    #ifdef DIGICOM
    uint ch;
    
    if (!isDIGI(com) || !com->oldhandler)
        return;                    // Only for 'opened' Digiboard ports

    /*** If !com->t_idle, we'll issue utx() upcalls until finished, so 
         that we can collect all Tx data in our tx-buffer before we give 
         it to the Digiboard.
    ***/
    if (com->utx)
    {
        while (!com->t_idle)
        {
            while (com->t_count < (uint)(com->t_end - com->t_begin))
            {
                // While we still have room in our tx buffer
                ch = com->utx(com->uptr);
                if ((ch & 0xFF00) == COM_UTX_FINISHED)
                {
                    com->t_idle = 1;        // Done...
                    break;
                }
                else
                {                                                          
                    com_tx((uchar) ch, com);
    
                    /* Last Character ???, if so send buffer now and wait 
                       for next upcall until data really sent.
                       (see com_start_tx() remark)
                    */
                    if ((ch & 0xFF00) == COM_UTX_LAST)
                    {
                        com->t_idle = 0xff;
                        break;
                    }
                }
            }
        }
    }

    // Transmit txbuf to digiboard if t_count...
    digicom_try_tx(com);

    if (com->utx && com->t_idle == 0xff && !com_tx_busy(com))
    {
        // All utx data is now really sent: call a last time utx()
        ch = com->utx(com->uptr);
        // We're sure they returned COM_UTX_FINISHED 
        // (if not: caller's bug)
        com->t_idle = 1;
    }

    // If urx handler, feed it
    // (but don't try to Rx continuously: slow irq's)
    /*** CDMP: Only call urx handler if CDMP saw COM_UTX_FINISHED.
         This simulates exactly the same behaviour for CDMP as on
         a COM port (thus: half duplex).
    ***/
//  if (com->urx)
    if (com->urx && com->t_idle == 1)
    {
        int i;
        
        // If rx data; call rx handler
        while ((i = com_rx( com)) >= 0)
            com->urx((uchar)i, com->uptr);
    }
    #endif
}

/**************************************************************************
*
* Set/Get User pointer uptr (see com_set_u?xhandler())
*
* uptr     void pointer to user data
*
* Return:  previous uptr (NULL after com_define())
*
*/
void *com_set_uptr(void *uptr, COM *com)
{  
    void *old_uptr;

    _disable();
    old_uptr = com->uptr;
    com->uptr = uptr;
    _enable();

    return old_uptr;
}  

/*------------------------ Local functions ---------------------------------*/

/**************************************************************************
*
* Function token from GVR: 'd‚bloque cette merde de 8250'
* Resets (by soft ways) 8250 chip
*
**/
static void com_debloq(COM *com)
{  
    inp(com->i8250 + COM_L_STAT);       /* Read line status - reset IIR */
    inp(com->i8250 + COM_RX);           /* Read receive buffer - reset IIR */
    inp(com->i8250 + COM_IIR);          /* Read interrupt ident. reg. */
    inp(com->i8250 + COM_M_STAT);       /* Read modem status */
}  


#ifdef DIGICOM
/**************************************************************************
*
* This function retrieves both the format and baudrate from a DigiBoard channel.
*
* pformat:  contains, upon return,
*               COM_FCOUNT   invalid format
*               COM_Fxxx     format value
* pbaudrate:contains, upon return,
*               COM_BCOUNT   invalid baudrate
*               COM_Bxxx     baudrate value
**/
static void digicom_get_format_baudrate(int *pformat, int *pbaudrate, COM *com)
{
    int regbx = 0xffff, port = com->i8250;
    uchar regch = 0xff, regcl = 0xff;

    *pformat = COM_FCOUNT;
    *pbaudrate = COM_BCOUNT;
    
    if (isDIGI(com))
    {        
        _asm                            // Function 0x0c: Get Channel Parameters
        {
            mov     ah, 0x0c
            mov     dx, port
            int     0x14                // Get channel parameters
            cmp     ah, 0xff
            je      digi_gfb9
            mov     regbx, bx           // format parity & # stop bits
            mov     regch, ch           // format # data bits
            mov     regcl, cl           // baudrate
        digi_gfb9:
        }

        for (*pformat = 0; *pformat < COM_FCOUNT; *pformat += 1)
        {
            if (regbx == digicom_cnv_format[*pformat].regbx &&
                regch == digicom_cnv_format[*pformat].regch)
                break;
        }
        for (*pbaudrate = 0; *pbaudrate < COM_BCOUNT; *pbaudrate += 1)
        {
            if (regcl == digicom_cnv_baudrate[*pbaudrate])
                break;
        }
    }
}

/**************************************************************************
*
* This function sets both the format and baudrate from a DigiBoard channel.
*
* format:  COM_Fxxx     format value
* baudrate:COM_Bxxx     baudrate value
*
* Returns: 1    ok
*          0    failed
*
**/
static uint digicom_set_format_baudrate(int format, int baudrate, COM *com)
{
    uint retv = 0;
    int regbx = 0xffff, port = com->i8250;
    uchar regch = 0xff, regcl = 0xff;

    if (isDIGI(com) && 
        format >= 0 && format < COM_FCOUNT && 
        baudrate >= 0 && baudrate < COM_BCOUNT)
    {        
        regcl = digicom_cnv_baudrate[baudrate];
        if (regcl != 0xff)
        {
            regbx = digicom_cnv_format[format].regbx;
            regch = digicom_cnv_format[format].regch;
            _asm
            {
                mov     al, 0           // no break
                mov     bx, regbx       // format parity & # stop bits
                mov     ch, regch       // format # data bits
                mov     cl, regcl       // baudrate
    
                mov     ah, 0x04        // initialise port
                mov     dx, port
                int     0x14
                cmp     ah, 0xff
                je      digi_sf9
                mov     retv, 1         // succeeded
            digi_sf9:
            }
        }
    }
    return retv;
}

/**************************************************************************
*
* This function transmits a buffer to the Digiboard if t_count != 0
*
**/
static void digicom_try_tx(COM *com)
{
    if (isDIGI(com) && com->oldhandler && com->t_count)
    {
        int port = com->i8250;
        int pseg, poff, len;

        // As much tx data as possible, but watch out with wrapping
        len = com->t_count;
        if (len > (com->t_end - com->t_get))
            len = com->t_end - com->t_get;

        pseg = _FP_SEG(com->t_get);
        poff = _FP_OFF(com->t_get);
        _asm
        {
            push    es
            mov     ax, pseg
            mov     es, ax
            mov     bx, poff
            mov     cx, len

            mov     ah, 0x0E            // Transmit a string
            mov     dx, port
            int     0x14

            mov     len, ax             // save number of characters sent
            pop     es
        }
        
        com->t_count -= len;            // Adjust pointers
        com->t_get += len;
        if (com->t_get == com->t_end)
            com->t_get = com->t_begin;
    }
}

/**************************************************************************
*
* Set the dtr and rts for a Digi com port: write current com->m_ctrl reg.
*
**/
static void digicom_set_m_ctrl(COM *com)
{
    uchar rbl = com->m_ctrl;
    int port = com->i8250;
    _asm
    {
        mov     dx, port
        mov     ax, 0x0501  // Ext. Comm. Port Control: Write modem control reg.
        mov     bl, rbl
        int     0x14
    }
}
#endif
