home *** CD-ROM | disk | FTP | other *** search
/ Hall of Fame / HallofFameCDROM.cdr / prpascal / asyturbo.lzh / ASYTURBO.PAS
Encoding:
Pascal/Delphi Source File  |  1986-05-28  |  22.5 KB  |  607 lines

  1. Program NewAsyInterrupt ;
  2.  
  3. (*****************************************************************************
  4.  
  5.    First Release : March 10, 1986 by Gene Harris
  6.  
  7.    This program is designed to allow your PC, XT, AT or jr to have access to
  8.    a full support RS-232 set of routines written in Turbo Pascal.  The down
  9.    load archive files should contain two versions of this program.  The first
  10.    program is NewAsyInterrupt (this routine).  This program is written com-
  11.    pletely in Turbo Pascal.  I suspect it will only handle baud rates of about
  12.    2400.  It has only been tested to 1200 baud.
  13.  
  14.    The second routine is AsyInlInterrupt.  The second program has parts of
  15.    the interrupt service routine coded in inline assembler code.  The source
  16.    code for the interrupt service routines is included as comments.  The second
  17.    version has been tested to 4800 baud and will probably handle 9600 baud.
  18.  
  19.    Both routines have been tested on an IBM PCjr equipped with an NEC V20,
  20.    an XT clone running at six and eight MHz and an 8 MHz PC AT clone
  21.    successfully.  The OPENCOM routine must be changed to run with a jr.
  22.    Appropriate changes and intructions for implementing these changes is
  23.    included as comments.
  24.  
  25.    This code is copyrighted by M. Eugene Harris, Jr.  It is released to the
  26.    public domain, and may not be used for commercial gain.  The program is
  27.    user supported, and updates may be obtained from Remark BBS, in Oklahoma
  28.    City 405 728-2463.
  29.  
  30.    A 3rd version of the program will be released in April which will
  31.    automatically support XON/XOFF from the interrupt routine, as well as
  32.    incorporate several enhanced string instructions.  The program will also
  33.    support the Intel 80188 and NEC-V20.
  34.  
  35.  *****************************************************************************
  36.  
  37.    Routines included in this code:
  38.  
  39.    OpenCom (icomport,ibaudrate,idatabits,istopbits:integer;
  40.                 iparity:char) ;
  41.  
  42.    icomport : 1 or 2 only, no other ports supported.
  43.  
  44.    ibaudrate : 110, 150, 300, 600, 1200, 2400, 4800, 9600 baud supported.
  45.  
  46.    idatabits : 5, 6, 7, 8 data bits supported.
  47.  
  48.    istopbits : 1 or 2 stopbits supported.
  49.  
  50.    iparity : 'E'ven, 'O'dd, 'N'one, 'M'ark, 'S'pace are supported.
  51.  
  52.    Purpose of the routine: User supplied communication port values are
  53.                            converted to the proper set values used by
  54.                            the asynch routines.  Sets are used internally
  55.                            to avoid var conflicts and improve speed.
  56.                            The routine returns an OpenError = TRUE if
  57.                            the specified COM port does not exist.  Therefore,
  58.                            the user code needs to check OpenError before 
  59.                            preceding to the next logical phase.
  60.  
  61.  *****************************************************************************
  62.  
  63.    Break - No Parameters.
  64.  
  65.    Purpose : drop DTR to effectively hang up the modem.
  66.  
  67.  *****************************************************************************
  68.  
  69.    Purge - No Parameters.
  70.  
  71.    Purpose : Purge the circular queues.
  72.              Reset the 8250 interrupt control register.
  73.  
  74.  *****************************************************************************
  75.  
  76.    RS232ChrAvailable : Boolean ;
  77.  
  78.    Purpose : Returns true if there are characters in the RS232 input queue
  79.              otherwise returns false.
  80.  
  81.  ****************************************************************************
  82.  
  83.    Function ReadChar : char ;
  84.  
  85.    Purpose : Returns the next character in the RS232 input buffer.  If no
  86.              characters are present, the routine returns a null byte.  The
  87.              RS232ChrAvailable routine should ALWAYS be checked before
  88.              trying to read a character, otherwise you will not be able
  89.              to tell if the null returned is correct.  The routine is
  90.              designed to be used in the following format:
  91.  
  92.              begin
  93.                 auxoutptr := ofs(readchar) ; { Install device driver }
  94.              ....
  95.              program body
  96.              ....
  97.                 if RS232ChrAvailable then read( aux, byte or char) ;
  98.              ....
  99.              rest of program
  100.  
  101.  ***************************************************************************
  102.  
  103.    Procedure WriteChar ;
  104.  
  105.    Purpose : Writes the next character into the RS232 character buffer.
  106.              The Asynch_Interrupt routine will output the characters in
  107.              the queue as THRE interrupts are generated by the 8250.
  108.              This routine is intended to be a device driver for the logical
  109.              devices USR or AUX.
  110.  
  111.              EXAMPLE :
  112.                     begin
  113.                        auxinptr := ofs(WriteChar) ; { Install Device Driver. }
  114.                        . . . .
  115.                        . . . .
  116.                        Write (aux, variable) ; { Put in RS232 output queue }
  117.  
  118.  ***************************************************************************
  119.  
  120.    Caveats : The Asynch_interrupt routine is written in pascal.  It is fast
  121.              enough to handle 1200 baud, and may handle 2400 baud.  You will
  122.              not get 4800 or 9600 baud support with this routine.  If you
  123.              need high speed support, use the companion routine ASYINL00.PAS
  124.              which handles 8250 interrupt processing with inline assembler
  125.              routines.
  126.  
  127.              The buffer input routine always checks that the variable
  128.              LSRstat is zero.  The LSRstat (Line Status Register) is utilized
  129.              to show buffer overflow on input.  Therefore, your program
  130.              must monitor LSRstat to determine if a buffer overflow has
  131.              occurred.  Bit 1 will be set if buffer overflow has occured.
  132.  
  133.              EXAMPLE :          if LSRstat <> 0 then begin
  134.                                    writeln('Line Status Error: ',LSRstat) ;
  135.  
  136.                                    . . . .
  137.                                    Handle LSR error condition. . .
  138.                                    . . . .
  139.  
  140.                                    { Reset LSRstat so that transmission can
  141.                                      begin again. Turn of bit 1. }
  142.  
  143.                                    LSRstat := LSRstat and $FD ;
  144.                                 end ;
  145.  
  146. *)
  147.  
  148. Type
  149.      tComPort =  (Com1, Com2);
  150.      tBaud = (b110, b150, b300, b600, b1200, b2400, b4800, b9600);
  151.      tParity = (pSpace, pOdd, pMark, pEven, pNone);
  152.      tDatabits = (d5, d6, d7, d8);
  153.      tStopbits = (s1, s2);
  154.  
  155.      tSaveVector = record     {  Saved Com interrupt vector          }
  156.        IP: integer;
  157.        CS: integer;
  158.      end;
  159.  
  160.      regpak = record case integer of
  161.                 1: (AX, BX, CX, DX, BP, SI, DI, DS, ES, FLAGS : integer) ;
  162.                 2: (al,ah, bl,bh, cl,ch, dl,dh : byte) ;
  163.               end;
  164.  
  165. Const
  166.      ourDS: integer = -1;    {  Will be init to contents of our DS
  167.                                   for later use in Interrupt routine  }
  168.  
  169.                               {  ASynch Interrupt Masks              }
  170.      imlist: array[Com1..Com2] of integer = ($EF, $F7);
  171.  
  172.                               {  ASynch hardware interrupt addresses }
  173.      ivlist: array[Com1..Com2] of integer = ($000C, $000B);
  174.  
  175.      PICCMD = $20;           {  8259 Priority Interrupt Controller  }
  176.      PICMSK = $21;           {  8259 Priority Interrupt Controller  }
  177.      EOI    = $20 ;          { End of Interrupt command for 8259.   }
  178.                              {  Asynch base port addresses are
  179.                                 in the ROM BIOS data area           }
  180.      Max_Input_Buffer = 2048 ;
  181.      Max_Output_Buffer = 1024 ;
  182.  
  183. var
  184.      ComBaseAddr: array[Com1..Com2] of integer Absolute $0040:$0000;
  185.  
  186. {
  187.     Define a ring buffer for Asynch_Interrupt to write into
  188.     and ReadCom to read from.
  189. }
  190.      Input_Ring_Buffer : array[0..Max_Input_Buffer] of char;
  191.      Input_Read_Ptr,             {  Index which ReadCom will next read from.   }
  192.      Input_Write_Ptr : integer ; {  Index which Asynch_Interrupt will next     }
  193.                                 {  write into. If readptr=writptr then        }
  194.                                 {  the buffer is empty.                       }
  195.  
  196.      OutPut_Ring_Buffer : array[0..Max_Output_Buffer] of char ;
  197.      OutPut_Read_Ptr,            {  Index which WriteCom will next input to.   }
  198.      OutPut_Write_Ptr : integer ;{  Index which Asynch_Interrupt will next     }
  199.                                 {  read from.  If readptr=writptr then the    }
  200.                                 {  buffer is full.                            }
  201.  
  202.      LSRstat,                           {  Line Status Reg at interrupt        }
  203.      MSRstat       : byte ;             {  Modem Status Reg at interrupt.      }
  204.      ComSaveVec    : tSaveVector ;      {  saved Async Interrupt vector        }
  205.      ComBase       : integer ;          {  Opened Com port base address        }
  206.      ActiveComPort : tComPort ;         {  Opened Com                          }
  207.      imvalue       : integer ;          {  Interrupt Mask value in use         }
  208.      IERMask       : byte ;
  209.  
  210.     { Define Equivalent Port Address Registers. }
  211.  
  212.      RBR_Port,
  213.      THR_Port,
  214.      IER_Port,
  215.      IIR_Port,
  216.      LCR_Port,
  217.      MCR_Port,
  218.      LSR_Port,
  219.      MSR_Port,
  220.      DLL_Port,
  221.      DLM_Port      : integer ;
  222.  
  223. {
  224.   These are interrupt type counters.  They are not used by the routine
  225.   explicitly, and can probably be deleted if desired.  I like to see
  226.   the statistics, hence I left 'em in:
  227. }
  228.      i0, i2, i4, i6    : integer ;
  229.  
  230.     { Define Variables needed by Asynch_Interrupts procedure. }
  231.  
  232.      IIRreg,
  233.      IType             : byte ;
  234.      Temp              : integer ;
  235.      OpenError         : boolean ;
  236.  
  237. Procedure InstallInterrupt(IntVect: integer;
  238.                         Var SaveVector: tSaveVector);
  239. Var
  240.     dosregs: regpak ;
  241.  
  242. Begin
  243.   inline($FA);                        {  cli        disable interrupts       }
  244.  
  245.   With dosregs Do Begin
  246.     ds := SaveVector.CS;
  247.     dx := SaveVector.IP;
  248.     ah := $25 ;
  249.     al := IntVect ;
  250.     MsDos(dosregs);                   {  DOS function 25 - set vector        }
  251.   End;
  252.   inline($FB);                        {  sti        re-enable ints           }
  253. End;
  254.  
  255. {********************************************************************}
  256. {                                                                    }
  257. {       This routine gets control upon an Asynch Interrupt           }
  258. {       We service all four interrupt types generated by the         }
  259. {       INS8250 chip:                                                }
  260. {                    1. Received character error or break.           }
  261. {                    2. Received data ready.                         }
  262. {                    3. Transmit Hold Register Empty.                }
  263. {                    4. Modem Status Change                          }
  264. {                                                                    }
  265. {       In addition, circular queues are used for transmitting       }
  266. {       and receiveing data from the COM1 port.  These queues        }
  267. {       can optionally be turned off if buffer overflow is           }
  268. {       detected.                                                    }
  269. {                                                                    }
  270. {********************************************************************}
  271.  
  272. Procedure Asynch_Interrupt;
  273.  
  274. Begin
  275.   inline($50/$53/$51/$52/$57/$56/$06);  {  push all registers }
  276.   inline($1E);                          {  push   ds }
  277.   inline($2E/$8E/$1E/ourDS);            {  mov   DS,CS:ourDS }
  278.  
  279.  
  280. {=============================================================================
  281.   We enter a service loop to handle all interrupts at this point in the code.
  282.   This is neccessary because the 8259 cannot handle another 8250 interrupt
  283.   while we service the last interrupt, hence we are polling the 8250 in this
  284.   routine until all interrupts are serviced.
  285.  =============================================================================}
  286.  
  287. repeat
  288.   IIRreg := PORT[IIR_Port] ;            {  Get Interrupt Identification  }
  289.   If (IIRreg and $01) = 0 then Begin    {  If interrupt pending then }
  290.     case (IIRreg and $06) of            {  determine cause of interrupt }
  291.  
  292.     { Received data available }
  293.  
  294.     $04: Begin
  295.            i4 := i4 + 1 ;
  296.            Temp := (Input_Write_Ptr + 1) mod Max_Input_Buffer ;
  297.            If LSRstat = 0 then Begin  {  If Line Status is OK  }
  298.              If Temp <> Input_Read_Ptr then Begin
  299.                Input_Ring_Buffer[Input_Write_Ptr] := CHR(PORT[RBR_Port]);
  300.                Input_Write_Ptr := Temp ;
  301.              End
  302.            Else LSRstat := (LSRstat or $02);
  303.            End;
  304.          End ;
  305.  
  306.     { Received character error interrupt }
  307.  
  308.     $06: begin
  309.            i6 := i6 + 1 ;
  310.            LSRstat := PORT[LSR_Port] and $1E;
  311.          end ;
  312.  
  313.     { Transmit hold register empty }
  314.  
  315.     $02: begin
  316.          i2 := i2 + 1 ;
  317.          if Output_Write_Ptr = Output_Read_Ptr then
  318.             Port[IER_Port] := Port[IER_Port] and $FD
  319.             else begin
  320.                PORT[THR_Port] := ORD(OutPut_Ring_Buffer[OutPut_Write_Ptr]) ;
  321.                OutPut_Write_Ptr := (OutPut_Write_Ptr + 1) mod Max_Output_Buffer ;
  322.             end
  323.          end ;
  324.  
  325.     { Modem status change }
  326.  
  327.     $00: begin
  328.            i0 := i0 + 1 ;
  329.            MSRstat := PORT[MSR_Port];
  330.          end ;
  331.     else ;
  332.     end ; { Case }
  333.   end ;
  334.  
  335. until (IIRreg and $01) = 1 ;
  336.  
  337. { Turn off 8259 and restore all registers. }
  338.  
  339.   PORT[PICCMD] := EOI;                  {  Send End Of Interrupt to 8259 }
  340.   inline($1F);                          {  pop    ds }
  341.   inline($07/$5E/$5F/$5A/$59/$5B/$58);  {  pop rest of regs }
  342.   inline($89/$EC);                      {  mov    sp,bp }
  343.   inline($5D/$FB) ;                     {  pop    bp }
  344.   inline($CF);                          {  iret }
  345. End;
  346.  
  347.  
  348. {                     Open COM1 or COM2, a la Basic                  }
  349.  
  350. Procedure OpenCom(ComPort, Baud, Databits, Stopbits : integer ;
  351.                   Parity : char ) ;
  352.  
  353. Const
  354.  
  355. {  Define addresses for the various Async card registers.           }
  356.  
  357.      RBR = $00;         { xF8   Receive Buffer Register             }
  358.      THR = $00;         { xF8   Transmitter Holding Register        }
  359.      IER = $01;         { xF9   Interrupt Enable Register           }
  360.      IIR = $02;         { xFA   Interrupt Identification Register   }
  361.      LCR = $03;         { xFB   Line Control Register               }
  362.      MCR = $04;         { xFC   Modem Control Register              }
  363.      LSR = $05;         { xFD   Line Status Register                }
  364.      MSR = $06;         { xFE   Modem Status Register               }
  365.      DLL = $00;         { xF8   Divisor Latch Least Significant     }
  366.      DLM = $01;         { xF9   Divisor Latch Most  Significant     }
  367.  
  368.       baudcode: array[b110..b9600] of integer =
  369.                            ($417, $300, $180, $C0, $60, $30, $18, $0C);
  370.       paritycode: array[pSpace..pNone] of byte =
  371.                                              ($38, $08, $28, $18, $00);
  372.       databitscode: array[d5..d8] of byte = ($00, $01, $02, $03);
  373.       stopbitscode: array[s1..s2] of byte = ($00, $04);
  374.  
  375. Var
  376.       LCRreg,
  377.       errclear    : byte ;
  378.       baudindex   : tbaud ;
  379. Begin
  380.  
  381.   OpenError := FALSE ;            {  Default no error on open.           }
  382.                                   {  Init the Const "ourDS" for use by
  383.                                      the Async_Interrupt routine         }
  384.   ourDS := DSEG ;
  385.                                   {  Swap Com interrupt vector           }
  386.   With ComSaveVec Do Begin
  387.     CS := CSEG;
  388.     IP := OFS(Asynch_Interrupt);
  389.   End;
  390.  
  391.   if Comport = 2 then ActiveComPort := Com2 else ActiveComPort := Com1 ;
  392.  
  393.   InstallInterrupt(ivlist[ActiveComPort], ComSaveVec);
  394.  
  395.   imvalue := imlist[ActiveComPort] ;      {  Select Interrupt Mask val }
  396.  
  397.   ComBase := ComBaseAddr[ActiveComPort];  {  Select Input Port         }
  398.   if ComBase = 0 then OpenError := TRUE ;
  399.  
  400.   Input_Read_Ptr   := 0 ;                 {  Init buffer pointers      }
  401.   Input_Write_Ptr  := 0 ;
  402.   OutPut_Read_Ptr  := 0 ;
  403.   OutPut_Write_Ptr := 0 ;
  404.  
  405.   RBR_Port := RBR + ComBase ;
  406.   THR_Port := THR + ComBase ;
  407.   IER_POrt := IER + ComBase ;
  408.   IIR_Port := IIR + ComBase ;
  409.   LCR_Port := LCR + ComBase ;
  410.   MCR_Port := MCR + ComBase ;
  411.   LSR_Port := LSR + ComBase ;
  412.   MSR_Port := MSR + ComBase ;
  413.   DLL_Port := DLL + ComBase ;
  414.   DLM_Port := DLM + ComBase ;
  415.  
  416.   inline ($FA) ;
  417.   {
  418.      Reset any pending error conditions and turn off DLAB.
  419.   }
  420.   Port[LCR_Port] := Port[LCR_Port] and $7F ;
  421.   PORT[PICMSK] := PORT[PICMSK] or ($FF - imvalue);
  422.   PORT[IER_Port] := 0 ;   {  Disable Data Avail interrupt        }
  423.   Port[MCR_Port] := 0 ;
  424.   errclear := PORT[LSR_Port] ;
  425.   errclear := PORT[RBR_Port] ;
  426.   errclear := PORT[MSR_Port] ;
  427.   {
  428.           Set Baud Rate Divisor Registers and the Line Control Register
  429.   }
  430.   LCRreg := $80;               {  Set Divisor Latch Access Bit in LCR }
  431.  
  432.   case Parity of
  433.    'S' : LCRreg := LCRreg or paritycode[pSpace] ;
  434.    'O' : LCRreg := LCRreg or paritycode[pOdd]   ;
  435.    'M' : LCRreg := LCRreg or paritycode[pMark]  ;
  436.    'E' : LCRreg := LCRreg or paritycode[pEven]  ;
  437.    'N' : LCRreg := LCRreg or paritycode[pNone]  ;
  438.    else LCRreg  := LCRreg or paritycode[pNone]   ;
  439.   end ; { case }
  440.  
  441.   case databits of
  442.     5 : LCRreg := LCRreg or databitscode[d5] ;
  443.     6 : LCRreg := LCRreg or databitscode[d6] ;
  444.     7 : LCRreg := LCRreg or databitscode[d7] ;
  445.     8 : LCRreg := LCRreg or databitscode[d8] ;
  446.     else LCRreg := LCRreg or databitscode[d8] ;
  447.   end ; { case }
  448.  
  449.   case stopbits of
  450.     1 : LCRreg := LCRreg or stopbitscode[s1] ;
  451.     2 : LCRreg := LCRreg or stopbitscode[s2] ;
  452.     else LCRreg := LCRreg or stopbitscode[s1] ;
  453.   end ; { case }
  454.  
  455.   PORT[LCR_Port] := LCRreg;     {  Set Parity, Data and Stop Bits
  456.                                    and set DLAB                          }
  457.   baudindex := b1200 ;
  458.   if baud = 110 then baudindex := b110 ;
  459.   if baud = 150 then baudindex := b150 ;
  460.   if baud = 300 then baudindex := b300 ;
  461.   if baud = 600 then baudindex := b600 ;
  462.   if baud = 1200 then baudindex := b1200 ;
  463.   if baud = 2400 then baudindex := b2400 ;
  464.   if baud = 4800 then baudindex := b4800 ;
  465.   if baud = 9600 then baudindex := b9600 ;
  466.  
  467.   PORT[DLM_Port] := Hi(baudcode[Baudindex]);   {  Set Baud rate               }
  468.   PORT[DLL_Port] := Lo(baudcode[Baudindex]);   {  Set Baud rate               }
  469.   PORT[LCR_Port] := LCRreg and $7F ;      {  Reset DLAB                  }
  470.  
  471.   PORT[PICMSK] := PORT[PICMSK] and imvalue;  {  Enable ASynch Int         }
  472.   PORT[IER_Port] := $0F ;               {  Enable some interrupts     }
  473.                                { Note: OUT2, despite documentation,
  474.                                  MUST be ON, to enable interrupts     }
  475.   PORT[MCR_Port] := $0F;                 {  Set RTS, DTR, OUT1, OUT2 }
  476.   inline ($FB) ;
  477.   PORT[PICCMD] := EOI ;
  478.   LSRstat := 0 ;                        {  Reset LSR status          }
  479.   MSRstat := 0 ;
  480.   i0 := 0 ;
  481.   i2 := 0 ;
  482.   i4 := 0 ;
  483.   i6 := 0 ;
  484. End;
  485.  
  486.  
  487. {                 Close any initialized COM                          }
  488.  
  489. Procedure CloseCom;
  490. Begin
  491.                               {  Disable Async interrupt             }
  492.   PORT[PICMSK] := PORT[PICMSK] or ($FF - imvalue);
  493.   PORT[IER_Port] := 0 ;   {  Disable Data Avail interrupt        }
  494.   Port[MCR_Port] := 0 ;
  495. End;
  496.  
  497. Procedure Break ;
  498.  
  499. var
  500.     LCRreg,
  501.     DropDTR   : byte ;
  502.  
  503. begin
  504.    LCRreg := Port[LCR_Port] ;
  505.    DropDTR := (LCRreg and $7F) or $40 ;
  506.    Port[LCR_Port] := DropDTR ;
  507.    Delay (600) ;
  508.    Port[LCR_Port] := LCRreg ;
  509. end ;
  510.  
  511. Procedure Purge ;
  512.  
  513. { Purpose : Purge all circular queues. }
  514.  
  515. begin
  516.   Input_Read_Ptr   := 0;                  {  Init buffer pointers      }
  517.   Input_Write_Ptr  := 0;
  518.   OutPut_Read_Ptr  := 0 ;
  519.   OutPut_Write_Ptr := 0 ;
  520.  
  521. { Reset 8250 Interrupt Enable Registers. }
  522.  
  523.   Port [IER_Port] := Port [IER_Port] and $0F ;
  524. end ;
  525.  
  526. Function  ReadChar : char ;
  527.  
  528. {
  529.   This routine is intended to function as an AUX or USR logical character
  530.   input device driver.  Usage is AuxInPtr := ofs(Readchar) followed by
  531.   the actual read operation read(AUX, var).
  532. }
  533.  
  534. Begin
  535.     if Input_Read_Ptr = Input_Write_Ptr then ReadChar := chr(0)
  536.     else begin
  537.       ReadChar := Input_Ring_Buffer[Input_Read_Ptr];
  538.       inline ($FA) ;
  539.       Input_Read_Ptr := (Input_Read_Ptr + 1) mod Max_Input_Buffer ;
  540.       inline ($FB) ;
  541.     end
  542. End;
  543.  
  544. Function RS232ChrAvailable : boolean ;
  545.  
  546. {
  547.   This routine should be checked before reading a character from the RS232
  548.   port, otherwise garbage will be returned and the buffer count messed up.
  549. }
  550.  
  551. begin
  552.   If Input_Read_Ptr = Input_Write_Ptr then RS232ChrAvailable := FALSE
  553.     else RS232ChrAvailable := TRUE ;
  554. end ;
  555.  
  556. Procedure WriteChar (ch:char) ;
  557.  
  558. {
  559.   This is the corresponding AuxOutPtr routine for use with write (aux, var).
  560. }
  561.  
  562. begin
  563.   OutPut_Ring_Buffer[OutPut_Read_Ptr] := ch ;
  564.   inline ($FA) ;
  565.   OutPut_Read_Ptr := (Output_Read_Ptr + 1) mod Max_Output_Buffer ;
  566.   inline ($FB) ;
  567.   if (IERMask and $02) = 0 then Port [IER_Port] := (Port [IER_Port] or $02) ;
  568. end ;
  569.  
  570. {=============================================================================
  571.   The following program is a VERY dumb glass CRT example program on how to use
  572.   the asynchronos routines.  I hope this routine is of some help.
  573.  =============================================================================}
  574.  
  575. label quit ;
  576.  
  577. var
  578.   ser_char,
  579.   key_char       :char ;
  580.  
  581. begin
  582.   AUXINPTR := ofs(Readchar) ;           { Assign aux input device driver      }
  583.   AUXOUTPTR := ofs(Writechar) ;         { Assign aux output device driver     }
  584.   OPENCOM (1,1200,8,1,'N') ;            { Open the communications port.       }
  585.   if OpenError then goto quit ;
  586.   writeln ('Port Opened') ;
  587.   key_char := #32 ;
  588.   repeat
  589.     if keypressed then begin            { Correct procedure for reading the   }
  590.       read (kbd, key_char) ;            { keyboard and sending a character    }
  591.       if key_char <> #27 then write( aux, key_char) ;
  592.       end ;
  593.     if RS232ChrAvailable then begin     { Correct procedure to read the RS232 }
  594.          read(aux, ser_char) ;          { communications port.                }
  595.          write (ser_char)
  596.        end ;
  597.     if LSRstat <> 0 then begin          { Check LSRstat byte for overflow.    }
  598.        writeln('Line Status Error: ',LSRstat) ;
  599.        LSRstat := 0 ;
  600.     end ;
  601.   until key_char = #27 ;
  602. quit :
  603.   Break ;
  604.   CloseCom ;
  605. {**  writeln (i0:6,i2:6,i4:6,i6:6,ir:6,iw:6) ; }
  606.  end.Rreg := LCRreg or databitscode[d8] ;
  607.     else LCRreg := LCRreg or databitscode[d8