home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / progm / ibmcom.zip / IBMCOM.C < prev    next >
Text File  |  1987-11-11  |  31KB  |  988 lines

  1. /***************************************************
  2. *                                                  *
  3. *            I B M C O M                           *
  4. *            ===========                           *
  5. *                                                  *
  6. *  Comments: IBM RS232 interrupted driven                *
  7. *                communications routines. If you whish *
  8. *                 to see these routines continue to be    *
  9. *                 supported with ehancements,bug fixes, *
  10. *                 protocals, terminal emulators,etc.        *
  11. *                 Send $15 to:                                    *
  12. *                    Intrinsic Computers                        *
  13. *                    RR 4                                            *
  14. *                    Oak Ridge,NJ   07438                        *
  15. *                 Send $25 and we'll give you the latest*
  16. *                 version on a diskette.                        *
  17. *                                                                   *
  18. *  Programmer(s):  Al Morgan                       *
  19. *  Revision 1.0                                               *
  20. *  Revision Date:09/17/1987                                *
  21. *                     Converted to TurboC                    *
  22. *                                                                    *
  23. *  Description of Routines:                               *
  24. *     init_handl( int port_num)                                *
  25. *     Initializes port_num interrupt routines.         *
  26. *        Must be called after init_port(),but before     *
  27. *        using any other routines.                            *
  28. *                                                                    *
  29. *    init_port(port_num,baudrate,parity,data_bits,    *
  30. *        stopbit)                                                    *
  31. *        Must be called before init_handl(). It sets    *
  32. *        the port_num's baud rate,parity,etc..            *
  33. *                                                                    *
  34. *    check_port_hw(port_num)                                    *
  35. *        Used to check if a port error has occured or    *
  36. *        if no error it returns the number of char        *
  37. *        in the ports interrupt buffer.                    *
  38. *                                                                    *
  39. *    check_port_buf(port_num)                                *
  40. *        Returns port_num's buffer overflow status.    *
  41. *                                                                    *
  42. *    com_status(port_num)                                        *
  43. *        Returns port_num's port status.                    *
  44. *                                                                    *
  45. *    clear_char_from_port(port_num)                        *
  46. *        Reads a char from port_num. Used to clear        *
  47. *        a character from the port after a port error *
  48. *        detected.                                                *
  49. *                                                                    *
  50. *    reset_port(port_num)                                        *
  51. *        Resets port_num so that it will not interrupt*
  52. *        the IBM anymore. Used to halt communications *
  53. *        Should be called before exiting your programs*
  54. *        or interrupts will still be active and            *
  55. *        hang the machine (or worse).                        *
  56. *                                                                    *
  57. *  putc_com(port_num,character)                            *
  58. *        Outputs the indicated character to port_num    *
  59. *        Does not buffer the character. It uses a        *
  60. *        polling technique. Depending on user             *
  61. *        response I may make it interrupt driven in a *
  62. *        later version of these routines.                    *
  63. *                                                                    *
  64. *    puts_com(port_num,string,num_char)                    *
  65. *        Outputs the indicated num_char from string   *
  66. *        to port_num.                                            *
  67. *                                                                    *
  68. *    getc_com(port_num)                                        *
  69. *        Gets a char from port_num's buffer.    If the    *
  70. *        buffer is empty it returns 0xFF which could    *
  71. *        be a valid character. This routine should be    *
  72. *        used in conjunction with check_port_buf() to    *
  73. *        see if any characters are in the buffer         *
  74. *        before call this routine.                            *
  75. *                                                                    *
  76. *    gets_com(buffer,num_bytes,port_num)                    *
  77. *        Gets num_bytes from port_num and puts it         *
  78. *        into the string buffer.                                *
  79. *                                                                    *
  80. *    report_serial_stauts(error_code)                        *
  81. *        Will print out the port status if you pass    *
  82. *        it the port status (obtained from                 *
  83. *        com_status(). Note, not all status bits are    *
  84. *        errors, some are normal.  See this routines    *
  85. *        code for detailed explanation of what the     *
  86. *        bits mean.                                                *
  87. *                                                                    *
  88. *    Compilation Instructions:                                *
  89. *        Compile this file with DEBUG set to FALSE        *
  90. *        to use with your routines. Set DEBUG to TRUE    *
  91. *        to test these routines by themseleves.            *
  92. *                                                                    *
  93. *    Linking Instructions:                                    *
  94. *        Not a main file (unless you compile with        *
  95. *        DEBUG set to TRUE).  Must be linked with        *
  96. *        a main() and circ_buf.obj                            *
  97. *                                                                    *
  98. *    References Used:                                             *
  99. *        Interrupts and the IBM PC Parts 1&2,            *
  100. *        Nov/Dec 83,January 84,PC Tech Journal            *
  101. *                                                                    *          
  102. *                                                                     *
  103. *     8088 Assembly Lanquage Programming : The            *
  104. *     IBM PC,Willow D.,Krantz J.,Howard Sames & Co.,    *
  105. *     Indianapolis,Indiana                                    *
  106. *                                                                   *
  107. ***************************************************/
  108.  
  109. /* 
  110.  
  111.     This file plus circ_buf.c implements a simple set of interrupt
  112.     routines for ports 1 and 2 of the IBM PC/XT family and clones.    The
  113.     interrupt routines use circular buffers. These     routines have been
  114.     tested up 19.2kbaud.  Buffer sizes and other features can be changed
  115.     by changing defines in the following code.      
  116.  
  117.     The input side of the routines are interrupt driven using an
  118.     unconventional approach to implementing interrupts with TurboC (but
  119.     such is life).  They where written that way because our philosohy in
  120.     a interrupt routine is to do it fast, with minimum of overhead. 
  121.     Knowing that the interrupt itself imposes a high overhead (as
  122.     compared to a dedicated polling techique) we decided to bypass
  123.     Borlands interrupt implementation.  The output side of the routines
  124.     use a simple polled technique.  This will get you by, untill we can
  125.     write a interrupt output routine (we're working on it).  If you have
  126.     any comments lets us know via our compuserve number (75036,1602). Do
  127.     you want support for ports 3 and 4? Do you want interrupt output? 
  128.     Any bugs? Let us know.  
  129.                                     Thanks 
  130.                                     Al Morgan
  131.  
  132.         PS. This code is subject to the normal disclaimer. We specifically
  133.     disclaim all warranties, expressed or implied, including but not
  134.     limited to implied warranties of merchantability and fitness for a
  135.     particular purpose with respect to defects in the documentation and
  136.     with     respect to any particular application, use, or     purpose.  In
  137.     no event shall Intrinsic be liable for any loss of profit or any
  138.     other commercial damage, including but not limited to special,
  139.     incidental consequential or other damages.           
  140.                                                                                 */
  141.     #include <stdio.h>
  142.     #include <dos.h>
  143.  
  144.     #include "circ_buf.h"            /* contains circular buffer structure definitions */
  145.  
  146.     #define        FALSE                        0
  147.     #define        TRUE                        1
  148.  
  149.     /* Change the buffer size and recompile to your likeing, Max is 32k */
  150.  
  151.     #define        BUFFER_1_SIZE            1000        /* define size of buffer */
  152.     #define        BUFFER_2_SIZE            1000        /* define size of buffer */
  153.  
  154.     #define        PORT1_BASE_AD            0x3f8
  155.     #define        PORT1_INT_VECTOR_NUM    12
  156.     #define        PORT1_INT_MASK            0xEF
  157.  
  158.     #define        PORT2_BASE_AD            0x2f8
  159.     #define        PORT2_INT_VECTOR_NUM    11
  160.     #define        PORT2_INT_MASK            0xF7
  161.  
  162.     #define        PORT3_BASE_AD            0x2f8
  163.     #define        PORT3_INT_VECTOR_NUM    11
  164.     #define        PORT3_INT_MASK            0xF7
  165.  
  166.     #define        PORT4_BASE_AD            0x2f8
  167.     #define        PORT4_INT_VECTOR_NUM    11
  168.     #define        PORT4_INT_MASK            0xF7
  169.  
  170.     #define        DEBUG                        TRUE
  171.  
  172.     /* The follwing define allows you have the interrupt routines
  173.         set a flag (message_in_buffer) when the EOM_CHAR is recieved
  174.         by the interrupt routine.  This is particularly useful if all
  175.         your messages are terminated with a particular character such
  176.         as a carraige return.  You must define the character if you
  177.         want to use it, otherwise comment it out or delete it. If you 
  178.         do use it, it is up to your routines to reset the 
  179.         message_in_buffer flag.                */
  180.  
  181.     #define        EOM_CHAR                    '!'
  182.  
  183. #if DEBUG != TRUE
  184. #ifdef EOM_CHAR
  185.     extern int message_in_buffer; /* You must define it in your main file */
  186. #endif
  187. #endif
  188.  
  189.     static int serial_port_address[]={PORT1_BASE_AD,PORT2_BASE_AD};
  190.     static int vector_number[]={PORT1_INT_VECTOR_NUM,PORT2_INT_VECTOR_NUM};
  191.     static unsigned char interrupt_mask[]={PORT1_INT_MASK,PORT2_INT_MASK};
  192.  
  193.     static char buffer2[BUFFER_1_SIZE],buffer1[BUFFER_2_SIZE];
  194.     static circ_buffer_description cb1,cb2;
  195.     static circ_buffer_description *buffer_descript[]={&cb1,&cb2};
  196.  
  197. #ifdef EOM_CHAR
  198.     static char eom=EOM_CHAR;
  199. #endif
  200.  
  201. #if DEBUG == TRUE
  202.     int message_in_buffer=0;
  203.  
  204.     void init_handl(int),dump_buf(int,int);
  205.     void report_serial_status(int),putc_com(int,char),report_circ_status(int);
  206.     unsigned char com_status(int);
  207. main ()
  208. {
  209.     int pe,i,j,nc,port;
  210.     char tbuffer[100];
  211. /*
  212.     This main routine is mainly for testing the communications routines.
  213.     Its also shows how the routine can be used, and in what sequence.
  214.  
  215.                                                                                             */
  216.     printf("Testing %s\n",__FILE__);
  217.     printf("Version Date: %s, Time: %s\n",__DATE__,__TIME__);
  218.     port = 1;
  219.  
  220.     /* First initialize the ports baud rate, parity, etc */
  221.  
  222.     if( init_port(port,19200,'n',8,1) != TRUE)
  223.     {
  224.         puts("Error on initializing the baud rate,etc\n");
  225.         exit(0);
  226.     }
  227.  
  228.     /* Next initialize the interrupt drivers for the port */
  229.  
  230.     init_handl(port);
  231.  
  232.     /* Output a string through the port */
  233.  
  234.     puts_com(port,"Testing IBMCOM.C\n",17);
  235.  
  236. do
  237.     {
  238.         if( (pe=check_port_hw(port)) < 0 ) /* If < 0 then a port error */
  239.         {
  240.             printf("Port Error 0x%02x\n",-pe);
  241.             report_serial_status(-pe);    /* print out the error code in english */
  242.         }
  243. /*        report_circ_status(port); */    /* for debugging */
  244.         if(pe > 0 || check_port_buf(port))    /* If no port errors, see any chars in ports buffer */
  245.         {
  246.             while((nc = gets_com (tbuffer,sizeof(tbuffer),port)) != 0)
  247.             {
  248.                 for(i=0;i<nc;i++)
  249.                 {
  250.                     putch(tbuffer[i]);    /* display the characters on the console */
  251.                     putc_com(port,tbuffer[i]);    /* echo back characters */
  252.                 }
  253.             }
  254. /*            report_serial_status(com_status(port)); */    /* for debugging */
  255.         }
  256.         sleep(3);     /* delay for a while */ 
  257.     }
  258.     while(ci() != 27);
  259.     reset_port(port);        /* before we exit, turn off interrupts */
  260. }
  261. void report_circ_status(int port_num)
  262. {
  263. /* This routine used for debugging only */
  264.  
  265.     printf("\n**** Circular buffer status for Port %d\n",port_num);
  266.      printf("\tHead ptr   = %d\n",buffer_descript[port_num-1]->head);
  267.     printf("\tTail ptr   = %d\n",buffer_descript[port_num-1]->tail);
  268.     printf("\tBuf adrs   = %d\n",buffer_descript[port_num-1]->address);
  269.     printf("\tBuf end    = %d\n",buffer_descript[port_num-1]->end);
  270.     printf("\tBuf length = %d\n",buffer_descript[port_num-1]->length);
  271.     printf("\tChar Count = %d\n",buffer_descript[port_num-1]->char_count);
  272.     printf("\tOverflow flag = %d\n",buffer_descript[port_num-1]->overflow_flag);
  273. /*    printf("\tOverflow flag = %d\n",cb1.overflow_flag); */
  274.     dump_buf(port_num,buffer_descript[port_num-1]->length+3);
  275. }
  276. int ci(void)
  277. {
  278. /* This routine used for debugging only */
  279.  
  280.     int c;
  281.  
  282.     if (!bioskey(1))
  283.         return(0);
  284.     c = getch();
  285.     return(c);
  286. }
  287. vputc(char tchar)
  288. {
  289. /* This routine used for debugging only */
  290.  
  291.     union REGS regs;
  292.     regs.h.ah = 0x0a;
  293.     regs.h.al = tchar;
  294.     regs.h.bh = 0;    /* page 0 */
  295.     regs.h.cl =    1; /* number of times to write char */
  296.     regs.h.ch = 0;
  297.     int86(0x10,®s,®s);
  298.     printf("\tOverflow flag = %d\n",cb1.overflow_flag);
  299. }
  300. #endif
  301. /*****************************************
  302. **                                      **
  303. **  Init_handl                                     **
  304. **                                                     **
  305. *****************************************/
  306. void init_handl (int port_num)
  307. {
  308.  
  309.     int port_address;
  310.     char in_byte;
  311.     unsigned asynch_offset;    /* offset of Asynch routine */
  312.     void interrupt (*int_routine_pntr)();
  313.     int dummy_value;
  314.  
  315.     dummy_value=1;
  316.  
  317.     if(port_num<1 || port_num>2)
  318.         return;
  319.  
  320. /*
  321.         This routine initializes the hardware for the serial ports 1 & 2
  322.         of the IBM PC/XT to interrupt the processor when a character is
  323.         recieved.  The routine also sets up the proper vector locations
  324.         to point to a interupt handler for each port.  The interupt handler
  325.         are contained in this routine.  This routine sets aside a
  326.         buffer for storage of the incomming characters.  The buffer size is
  327.         definable by the user (from 0 to 32k) and is passed when the routine
  328.         is compiled.
  329.  
  330.         The routine should be called after the port parameters
  331.         (baudrate,parity,stop bits,etc) had been setup (by init_port(..).
  332.         and then never again (unless the port has been reset (by reset_port() ).
  333.  
  334.         The routine returns a FALSE if the port_num is not 1 or 2.
  335.                                                                                              */
  336.  
  337.  
  338.     if ( port_num == 1)
  339.     {
  340.         buffer_descript[0]->address  = buffer1;
  341.         buffer_descript[0]->length      = sizeof(buffer1);
  342. asm    mov        ax,offset asynch_int1    /* put this ports interrupt handler offset into table */
  343. asm    mov        asynch_offset,ax
  344.     }
  345.     if (port_num == 2)
  346.     {
  347.         buffer_descript[1]->address  = buffer2;
  348.         buffer_descript[1]->length      = sizeof(buffer2);
  349. asm    mov        ax,offset asynch_int2    /*put this ports interrupt handler offset into table */
  350. asm    mov        asynch_offset,ax
  351.     }
  352.  
  353.     buffer_descript[port_num-1]->end 
  354.         = buffer_descript[port_num-1]->address
  355.             + buffer_descript[port_num-1]->length-1;
  356.     reset_circ_buffer(buffer_descript[port_num-1]);
  357.  
  358.       
  359.     /*  Initialize the Handlers */
  360.  
  361.     port_address = serial_port_address[port_num-1];
  362.     reset_port(port_num); 
  363.     clear_char_from_port(port_num);
  364.  
  365.     int_routine_pntr = MK_FP(_CS,asynch_offset);
  366.     setvect(vector_number[port_num-1],int_routine_pntr);            /* Set interrupt vector to interrupt handler */
  367.     disable();                                    /* Disable interrupts */
  368.  
  369.     /*    Enable IRQ on 8259 interupt controller */
  370.  
  371.     in_byte = inportb(0x21);                /* get interrupt mask register */
  372.     in_byte = in_byte & interrupt_mask[port_num-1];    /* set up RS-232 interrupts */
  373.     outportb(0x21,in_byte);                     /* do it! */
  374.  
  375.     /*    The following logic sets up the data ready interupt */
  376.  
  377.     in_byte = inportb(port_address+3);    /* Get LCR for port */
  378.     in_byte = in_byte & 0x7F;                /* Set DLAB = 0 */
  379.     outportb(port_address+3,in_byte);    /* Send back LCR */
  380.     outportb(port_address+1,0x01);        /* Enable Data Ready Interrupts of IER */
  381.  
  382.     outportb(port_address+4,0x08);        /* Set up out2 on 8250 */
  383. asm    mov        cs:word data_ad,ds
  384.     enable();                                    /* Enable interrupts */
  385.  
  386.     if(dummy_value)
  387.         return;
  388.  
  389.     /*  Interrupt Handler's  */
  390.  
  391. /*    The following routines are the interupt handlers for port 1&2.  */
  392. /* There is one interrupt handler per port.  Each gets its incomming */
  393. /* character from the port and stuffs it into a circular buffer */
  394. /*    The routine will throw characters away once the buffer is full */
  395. /*    Other code must be quick enough to get the characters out of the */
  396. /* buffer before it overwrites them.  Also, this routine does not */
  397. /* detect hardware errors, other logic does this (ie check_port(..)). */
  398.  
  399.     /* Interrupt Handler 1 */
  400.  
  401. asm asynch_int1:
  402.  
  403. asm    sti                                    /* enable interupts */
  404. asm    push        bx                            /* Save regs used by this routine */
  405. asm    push        ax
  406. asm    push        dx
  407. asm    push        ds                            /* ds might not be this progs ds */
  408.  
  409.     /* This code gets the character from the port */
  410.  
  411. asm    mov        ds,cs:word data_ad    /* get ds that init_handl() saved */
  412. asm    mov        dx,03f8h                   /* set up dx to point to port addrs */
  413. asm    in         al,dx                        /* get char in al */
  414. asm   cli                                    /* inhibit interupts */
  415.  
  416.     /* This code checks for a buffer full condition,and ignores the  */
  417.     /* character if so and sets the buffer overflow flag */
  418.  
  419. asm    mov        dx,word cb1.char_count        /* first check if buffer overflow    */
  420. asm    cmp         dx,word cb1.length            /* is number of char < buffer size?    */
  421. asm    jl         int1_contin                        /* if so, continue on processing the char */
  422. asm    mov        ah,01H                            /* else,set up AH so we can .... */
  423. asm    mov        byte cb1.overflow_flag,ah    /* ...flag overflow conditon */
  424. asm    jmp        exit_int1                        /* exit without saveing char */
  425.  
  426. /* This code checks for a "End of Message" (EOM) character.  If the */
  427. /* incomming character is an EOM char it will set the message_in_buffer */
  428. /* flag to signal that an EOM has arrived, and places the EOM into the */
  429. /* buffer (ie. treats it like any other character) */
  430.  
  431.     int1_contin:
  432.  
  433. #ifdef EOM_CHAR
  434. asm    cmp        al,byte eom                    /* test for end of message */
  435. asm    jne        orcra    
  436. asm    mov        bx,01H                        /* if eom set flag */
  437. asm    mov        word message_in_buffer,bx
  438. #endif
  439.  
  440.     /* This code puts the character into the buffer */
  441.     /* Note: Buffer is a circular buffer and the code */
  442.     /* deals mostly with handling the "wrap around" condition. */
  443.  
  444. orcra:
  445.  
  446. asm    mov        bx,word cb1.head            /* get next char pointer */
  447. asm    mov        dx,word cb1.end            /* get end of buffer address */
  448. asm    mov        [bx],al                        /* save char in buffer */
  449. asm    inc        bx                                /* up bx to point to next byte to put */
  450.                                                     /*... a char into */
  451. asm    cmp        bx,dx                            /* have we crossed over the end of the */
  452.                                                     /*... buffer(next_char_ptr > end_buffer)? */
  453. asm    jbe        int1                            /* no,so use new pointer */
  454. asm    mov        bx,word cb1.address        /* yes,so reset bx to point to start of */
  455.                                                     /*... buffer */
  456. int1:
  457.  
  458. asm    mov        word cb1.head,bx        /* save new pointer address */
  459. asm    inc        word cb1.char_count    /* increment character counter */
  460.                                                 /*... ( other logic decrements it ) */
  461.  
  462.     /* This logic signals end of interupt */
  463.  
  464. exit_int1:
  465.  
  466. asm    mov        al,20H                        /* set up to do a EOI */
  467. asm    out        20H,al                        /* do it */
  468. asm    sti                                        /* enable interupts */
  469.  
  470. asm    pop        ds                                /* restore used registers */
  471. asm    pop        dx                                
  472. asm    pop        ax
  473. asm    pop        bx
  474.  
  475. asm    iret                      /* return from interrupt routine */
  476.  
  477. /******************************/
  478. /* END of Interrupt handler 1 */
  479. /******************************/
  480.  
  481. asm    data_ad    dw    0        /* storage in code segment for saving DS of interrupt buffers */
  482.  
  483.     /*  Interrupt Handler 2  */
  484.     /*************************/
  485.  
  486. asm    asynch_int2:
  487.  
  488. asm    sti                                            /* enable interupts */
  489. asm    push    bx
  490. asm    push    ax
  491. asm    push    dx
  492. asm    push    ds                  
  493.  
  494. asm    mov        ds,cs:word data_ad            /* get ds that init_handl() saved */
  495. asm    mov        dx,02f8H                           /* set up dx to point to port addrs */
  496. asm    in         al,dx                                /* get char in al */
  497. asm    cli                                            /* inhibit interupts */
  498.  
  499.     /* This code checks for a buffer full condition,and ignores the  */
  500.     /* character if so and sets the buffer overflow flag */
  501.  
  502. asm    mov        dx,word cb2.char_count        /* first check if buffer overflow    */
  503. asm    cmp         dx,word cb2.length            /* is number of char < buffer size?    */
  504. asm    jl         int2_contin                        /* if so, continue on processing the char */
  505. asm    mov        ah,01H                            /* set up AH so we can .... */
  506. asm    mov        byte cb2.overflow_flag,ah    /* ...flag overflow conditon */
  507. asm    jmp        exit_int2                        /* exit without saveing char */
  508.  
  509. /* This code checks for a "End of Message" (EOM) character.  If the */
  510. /* incomming character is an EOM char it will set the message_in_buffer */
  511. /* flag to signal that an EOM has arrived, and places the EOM into the */
  512. /* buffer (ie. treats it like any other character) */
  513.  
  514. int2_contin:
  515.  
  516. #ifdef EOM_CHAR
  517. asm    cmp        al,byte eom                        /* test for end of message */
  518. asm    jne        orcrb                    
  519. asm    mov        bx,01H                            /* if eom set flag */
  520. asm    mov        word message_in_buffer,bx
  521. #endif
  522.  
  523. orcrb:
  524.  
  525. asm    mov        bx,word cb2.head        /* get next char pointer */
  526. asm    mov        dx,word cb2.end        /* get end of buffer address */
  527. asm    mov        [bx],al                    /* save char in buffer */
  528.                                                 /*... to put a char in buffer */
  529. asm    inc        bx                            /* up bx to point to next byte to put */
  530.                                                 /*... a char into */
  531. asm    cmp        bx,dx                        /* have we crossed over the end of the */
  532.                                                 /*... buffer(next_char_ptr > end_buffer)? */
  533. asm    jbe        int2                        /* no,so use new pointer */
  534. asm    mov        bx,word cb2.address    /* yes,so reset bx to point to start of */
  535.                                                 /*... buffer */
  536. int2:
  537.  
  538. asm    mov        word cb2.head,bx        /* save new pointer address */
  539. asm    inc        word cb2.char_count    /* increment counter to flag another */
  540.                                                 /* char has arrived (other logic decrements */
  541.                                                 /* it) */
  542. exit_int2:
  543.  
  544. asm    mov        al,20H                    /* set up to do a EOI */
  545. asm    out        20H,al                    /* do it */
  546. asm    sti                                    /* enable interupts */
  547.  
  548. asm    pop        ds                            /* restore used registers */
  549. asm    pop        dx                
  550. asm    pop        ax
  551. asm    pop        bx
  552.  
  553. asm    iret                                    /* return from interrupt routine */
  554.  
  555. /* END of Interrupt handler 2 */
  556. /******************************/
  557. }
  558. /*****************************************
  559. **                                      **
  560. **  Init_port function                  **
  561. **                                                     **
  562. *****************************************/
  563.  
  564. int init_port( int port_num,int baudrate,char parity,int data_bits,int stopbit )
  565. {
  566.     int port_address;
  567.     char msb,lsb,t;
  568. /*
  569.  
  570.     This routine intializes one of the two IBM PC/XT ports
  571.     comunications parameter (baud,parity,data bits,stop bits).
  572.  
  573.                                                                                         */
  574.     if(port_num<1 || port_num>2)
  575.                 return (FALSE);
  576.     t = 0;
  577.     port_address = serial_port_address[port_num-1];
  578.  
  579.     switch (baudrate)
  580.     {
  581.         case 3:
  582.         case 300:
  583.             msb = 01;
  584.             lsb = 0x80;
  585.             break;
  586.  
  587.         case 6:
  588.         case 600:
  589.             msb = 00;
  590.             lsb = 0xc0;
  591.             break;
  592.  
  593.         case 12:
  594.         case 1200:
  595.             msb = 00;
  596.             lsb = 0x60;
  597.             break;
  598.  
  599.         case 24:
  600.         case 2400:
  601.             msb = 00;
  602.             lsb = 0x30;
  603.             break;
  604.  
  605.         case 48:
  606.         case 4800:
  607.             msb = 00;
  608.             lsb = 0x18;
  609.             break;
  610.  
  611.         case 96:
  612.         case 9600:
  613.             msb = 00;
  614.             lsb = 0x0c;
  615.             break;
  616.  
  617.         case 19200:
  618.         case 19:
  619.             msb = 00;
  620.             lsb = 0x06;
  621.             break;
  622.  
  623.         default:
  624. /*            printf ("Error: illegal baudrate\n");   */
  625.             return (FALSE);
  626.     }
  627.  
  628.     switch (parity)
  629.     {
  630.         case 'e':
  631.         case 'E':
  632.             t = t | 030;
  633.             break;
  634.         case 'n':
  635.         case 'N':
  636.             t = t | 000;
  637.             break;
  638.         case 'o':
  639.         case 'O':
  640.             t = t | 010;
  641.             break;
  642.         default:
  643. /*            printf ("Error: Illegal parity parameter\n");   */
  644.             return (FALSE);
  645.     }
  646.  
  647.     if (stopbit == 1)
  648.         ;
  649.         else
  650.             if (stopbit == 2 )
  651.                 t = t|04;
  652.                 else
  653.                 {
  654. /*                    printf ("Error: Illegal # of stop bits\n");  */
  655.                     return (FALSE);
  656.                 }
  657.  
  658.     switch (data_bits)
  659.     {
  660.         case 5:
  661.             break;
  662.         case 6:
  663.             t = t | 01;
  664.             break;
  665.         case 7:
  666.             t = t | 02;
  667.             break;
  668.         case 8:
  669.             t = t | 03;
  670.             break;
  671.         default:
  672. /*            printf ("Error: Illegal data bit length"); */
  673.             return (FALSE);
  674.         }
  675.  
  676. /*    printf ("t = %x,msb = %x,lsb = %x\n",t,msb,lsb);   */
  677.  
  678.     outportb(port_address+3,0x80);    /* Set bit 7 of LCR for init */
  679.     outportb(port_address  ,lsb);        /* Do lsb of baud rate */
  680.     outportb(port_address+1,msb);        /* Do msb of baud rate */
  681.     outportb(port_address+3,t  );        /* Do line control word */
  682.     return (TRUE);
  683. }
  684. /**************************
  685. *                                    *
  686. * Check the port             *
  687. *                                 *
  688. **************************/
  689. int check_port_hw(int port_num)
  690. {
  691. /*
  692.     This routine is used to check if an error on a port has
  693.     occured.  If so, it returns the negative of the line control
  694.     status register of the port.  If there is no error it returns
  695.     the number of characters in the ports buffer.
  696.                                                                             */
  697.     unsigned char in_byte;
  698.     int port_address;
  699.  
  700.     if(port_num<1 || port_num>2)
  701.         return (-1);
  702.     in_byte = com_status(port_num);
  703.     if(in_byte & 0x1E)            /* Test for any error status bits set */
  704.     {
  705.         clear_char_from_port(port_num);    /* read any bad char in port (if any) */
  706.         return(-((int)in_byte));            /* return the negative of status to indicate port error */
  707.     }
  708.     /* No errors , so return the number of chars in buffer (for partic port) */
  709.     return(check_circ_buffer_fill(buffer_descript[port_num-1]));    
  710. }
  711. /**************************
  712. *                                    *
  713. * Check the port buffer   *
  714. *                                 *
  715. **************************/
  716. int check_port_buf(int port_num)
  717. {
  718. /*
  719.     This routine is used to check if a buffer overflow has occured.
  720.     It returns the buffer overflow setting - 1 for overflow,0 for
  721.     no overflow. It returns -1 for an illegal port number.
  722.                                                                             */
  723.     if(port_num<1 || port_num>2)
  724.         return (-1);
  725.     return(check_circ_buffer_over(buffer_descript[port_num-1]));    
  726. }
  727. /**************************
  728. *                                    *
  729. * Com_status                 *
  730. *                                 *
  731. **************************/
  732. unsigned char com_status(int port_num)
  733. {
  734. /*
  735.     This routine is used to get the port status.  It returns the
  736.     port status or if the port number is illegal it returns zero.
  737.  
  738.                                                                             */
  739.     int port_address;
  740.     char in_byte;
  741.  
  742.     if(port_num<1 || port_num>2)
  743.         return (0);
  744.     port_address = serial_port_address[port_num-1];
  745.     in_byte = inportb(port_address+5);
  746.     return(in_byte);
  747. }
  748. /**************************
  749. *                                    *
  750. * Clear_char_from_port    *
  751. *                                 *
  752. **************************/
  753. clear_char_from_port(int port_num)
  754. {
  755. /*
  756.     This routine reads a byte from the port without checking to see
  757.     if a character is ready to be read. It is mainly used to flush out
  758.     "known" or "don't care if there is a" bad character at the port.
  759.     It returns TRUE if the port number is legal,otherwise it returns FALSE.
  760.  
  761.                                                                             */
  762.     int port_address;
  763.     char in_byte;
  764.  
  765.     if(port_num<1 || port_num>2)
  766.         return(FALSE);
  767.     port_address = serial_port_address[port_num-1];
  768.     inportb(port_address);    /* read in char from port to clear it */
  769.     return(TRUE);
  770. }
  771. /**************************
  772. *                                    *
  773. * Reset the port                *
  774. *                                *
  775. **************************/
  776. int reset_port(int port_num)
  777. {
  778. /*
  779.     This routine resets the port so that it will not interrupt the processor.
  780.     It should be used when the program is ready to finish, otherwise interrupts
  781.     will continue to be recieved and really screw up the processor.
  782.  
  783.                                                                     */
  784.     char in_byte;
  785.     int port_address;
  786.  
  787.     if(port_num<1 || port_num>2)
  788.         return(FALSE);
  789.     
  790.     port_address = serial_port_address[port_num-1];
  791.  
  792.     disable();    /* disable interrupts */
  793.  
  794.     /* Disable 8259 interrupts for this RS232 port's interrupts */
  795.  
  796.     in_byte = inportb(0x21);
  797.     in_byte = in_byte | ~interrupt_mask[port_num-1];
  798.     outportb(0x21,in_byte);
  799.  
  800.     /* Disable 8250 Data Ready interrupt */
  801.  
  802.     in_byte = inportb(port_address+3);            /* Get Line Control Register */
  803.     in_byte = in_byte & 0x7F;                        /* Reset Bit 7,but keep rest intact so that we can access the IER register */
  804.     outportb(port_address+3,in_byte);            /* Do it */
  805.     outportb(port_address+1,0);                    /* Now we can reset IER */
  806.  
  807.     outportb(port_address+4,0);                    /* Reset MCR */
  808.  
  809.     enable();                                            /* enable interrupts */
  810.  
  811. /*    puts("Returning from resetting the port");    */
  812.     return(TRUE);
  813. }
  814. /*****************************************
  815. **                                      **
  816. **  Putc_Output function                **
  817. **                                                    **
  818. *****************************************/
  819.  
  820. void putc_com (int port_num,char tchar)
  821. {
  822. /*
  823.     This routine output a character to the indicated port. 
  824.  
  825.                                                                     */
  826.     int port_address;
  827.  
  828.     while (!(com_status(port_num) & 0x20))    /* wait untill trans holding register ready */
  829.         ;
  830.     port_address = serial_port_address[port_num-1];
  831.     outportb(port_address,tchar);    /* read in char from port to clear it */
  832. }
  833. /*****************************************
  834. **                                      **
  835. **  Puts_com function                     **
  836. **                                                    **
  837. *****************************************/
  838.  
  839. int puts_com (int port,char *string,int num_char)
  840. {
  841.     void putc_com();
  842. /*
  843.     This routine outputs a string to the indicated port.  
  844.     It loops until all the characters are output.  It does 
  845.     not timeout.
  846.                                                                     */
  847.     while(num_char--)
  848.         putc_com(port,*string++);
  849.     return(TRUE);
  850. }
  851. /*****************************************
  852. **                                      **
  853. **  Dump Buffer                         **
  854. **                                                     **
  855. *****************************************/
  856. void dump_buf(int port_num,int num_char_to_dump)
  857. {
  858. /*
  859.     This routine dumps the buffer for port 1.  Starting at the
  860.     begining of the buffer (not the begining of the remaning characters
  861.     in the buffer).  This is used mainly to see what the buffer has in
  862.     it (for debugging).
  863.  
  864.                                                                 */
  865.     int i,j;
  866.      printf("Buffer for port %d contains....\n",port_num);
  867.     for ( i=0 ; i<200 ; i+= 40)
  868.     {
  869.         for ( j=0 ; j < 40 ; j++)
  870.         {
  871.             printf ("%c",*(buffer_descript[port_num-1]->address+j+i));
  872.             num_char_to_dump--;
  873.             if(!num_char_to_dump)
  874.             {
  875.                 printf("\n");
  876.                 return;
  877.             }
  878.         }
  879.     printf("\n");
  880.      }
  881. }
  882. /*************************************
  883. *                                                *
  884. * Get a character from port's buffer *
  885. *                                                  *
  886. *************************************/
  887. unsigned char getc_com(int port_num)
  888. {
  889. /*
  890.     This routine returns one character from the ports buffer.  If the
  891.     buffer is empty it returns an octal FF.  Note this routine must be defined
  892.     as short in the calling rotuine.
  893.                                                                     */
  894.     unsigned char t1;
  895.  
  896.     t1 = 255;
  897.     if(port_num<1 || port_num>2)
  898.         return(t1);
  899.     
  900.     if(!buffer_descript[port_num-1]->char_count)
  901.         return(255);
  902.     t1=*buffer_descript[port_num-1]->tail;
  903.     if(++buffer_descript[port_num-1]->tail>buffer_descript[port_num-1]->end)
  904.         buffer_descript[port_num-1]->tail = buffer_descript[port_num-1]->address;
  905.     disable();    /* disable interrupts while changing something the interrupt */
  906.                    /* handler could change if it occured now */
  907.     buffer_descript[port_num-1]->char_count--;
  908.     buffer_descript[port_num-1]->overflow_flag = 0; /* reset overflow condition flag */
  909.     enable();    /* ok, let any interrupts come through */
  910.     return(t1);
  911. }
  912.  
  913. /*****************************************
  914. **                                      **
  915. **     Get data from the Port function  **
  916. **                                                     **
  917. *****************************************/
  918.  
  919. int gets_com(char *buffer,int bytes_wanted,int port_num)
  920. /*
  921.     This routine will place a given number of bytes from the communciation buffer
  922.     into a user supplied buffer. It also returns the actual number transferred.
  923.     It will return a zero if bytes_wanted is negative. The string returned
  924.     is not null terminated (as a NULL is a valid character).
  925.  
  926.                             */
  927.  
  928. {
  929.     int tcount;
  930.  
  931.     if(bytes_wanted <= 0 ) 
  932.         return (0);     /* Illegal bytes_wanted arg */
  933.     if(port_num<1 || port_num>2)
  934.         return(0);
  935.     
  936.     tcount = gets_circ_buffer (buffer,bytes_wanted,buffer_descript[port_num-1]);
  937.     return(tcount);
  938. }
  939. /*****************************************
  940. **                                      **
  941. **     Report serial status             **
  942. **                                                     **
  943. *****************************************/
  944. void report_serial_status(int error_code)
  945. {
  946. /*
  947.     All status bits are harware status bits.
  948.  
  949.         Reference page 1-231 of IBM Technical Reference (1502237),
  950.                                         Personal Computer XT Hardware Reference
  951.                                         Library,April 1983 Edition.
  952.                                         
  953.     Bit 7 (hi order bit) =>  if set Unknown Error
  954.         6                 =>           if set Tx Shift Register Empty (TSRE)
  955.         5                 =>           if set Transmitter Holding Register Empty (THRE)
  956.         4                 =>           if set Break Interrupt (BI)
  957.         3                 =>           if set Framming Error (FE)
  958.         2                 =>           if set Parity Error (PE)
  959.         1                 =>           if set Overrun Error (OR)
  960.         0                 =>           if set Data Ready (DR)
  961.                                                                               */
  962.  
  963.     if (error_code & 0x80)
  964.        puts("Unknown Error (Bit 8)");
  965.  
  966.     if (error_code & 0x40)
  967.        puts("Tx Shift Register Empty (TSRE)");
  968.  
  969.     if (error_code & 0x20)
  970.        puts("Transmitter Holding Register Empty (THRE)");
  971.  
  972.     if (error_code & 0x10)
  973.        puts ("Break Interrupt (BI)");
  974.  
  975.     if (error_code & 0x08)
  976.        puts ("Framming Error (FE)");
  977.  
  978.     if (error_code & 0x04)
  979.        puts("Parity Error (PE)");
  980.  
  981.     if (error_code & 0x02)
  982.        puts("Overrun Error (OR)");
  983.  
  984.     if (error_code & 0x01)
  985.        puts("Data Ready (DR)");
  986. }
  987.  
  988.