home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / PROG / PASCAL / T301AS.ZIP / T301_ASY.INC < prev    next >
Encoding:
Text File  |  1989-01-04  |  21.8 KB  |  727 lines

  1. (* This is the primary asynchronous program.  It controls the 8250 and
  2.    16450 and allows interrupt driven sending and receving of char-
  3.    acters via ring buffers.  This allows for more effective background
  4.    processing by the 8250 and/or 16450 in your system.
  5.  
  6.    Please note that 16550 support has been added to these routines
  7. *)
  8.  
  9. Type
  10.      { ====================================================================
  11.        New Asynch only supports com1 and com2.  This is due to the fact
  12.        that I have a two port serial controller card and can only test
  13.        for my own configuration.  You should be able to add multiple ser-
  14.        ial ports, given the comments in the source code.
  15.        ==================================================================== }
  16.  
  17.      tComPort =  (Com1, Com2);
  18.  
  19.      { Other Baud Rates can be supported, however, I can't test them
  20.        on my current system configuration.
  21.      }
  22.  
  23.      tBaud = (b110, b150, b300, b600, b1200, b2400, b4800, b9600);
  24.  
  25.      { I support a number of different parity settings.  However,
  26.        none, and even seem to be the most common.  I have not tested
  27.        any of the other parity settings except in loop back mode.
  28.      }
  29.  
  30.      tParity = (pSpace, pOdd, pMark, pEven, pNone);
  31.  
  32.      { All data bit settings supported by the National Semiconductor
  33.        8250/16450/16550 UARTs are supported.
  34.      }
  35.  
  36.      tDatabits = (d5, d6, d7, d8);
  37.  
  38.      { The National Semi series only supports two stop bit settings.
  39.      }
  40.  
  41.      tStopbits = (s1, s2);
  42.  
  43.      tSaveVector = record     {  Saved Com interrupt vector          }
  44.        IP: integer;
  45.        CS: integer;
  46.      end;
  47.  
  48.      tBufferType = array [0..MaxInt] of Byte ;
  49.  
  50.      tBuffer    = Record
  51.                      Ring_Buffer : ^tBufferType ;
  52.                      Buffer_Len    : Integer ;
  53.                      Read_Ptr    : Integer ;
  54.                      Write_Ptr   : Integer ;
  55.                    End ;
  56.  
  57.      tregpak = record case integer of
  58.                 1: (AX, BX, CX, DX, BP, SI, DI, DS, ES, FLAGS : integer) ;
  59.                 2: (al,ah, bl,bh, cl,ch, dl,dh : byte) ;
  60.               end;
  61.  
  62.      BigString = string[255] ;
  63.  
  64. Const
  65.      ourDS: integer = -1;    {  Will be init to contents of our DS
  66.                                   for later use in Interrupt routine  }
  67.  
  68.                               {  ASynch Interrupt Masks              }
  69.      imlist: array[Com1..Com2] of integer = ($EF, $F7);
  70.  
  71.                               {  ASynch hardware interrupt addresses }
  72.      ivlist: array[Com1..Com2] of integer = ($000C, $000B);
  73.  
  74.      PICCMD = $20;           {  8259 Priority Interrupt Controller  }
  75.      PICMSK = $21;           {  8259 Priority Interrupt Controller  }
  76.      EOI    = $20 ;          { End of Interrupt command for 8259.   }
  77.                              {  Asynch base port addresses are
  78.                                 in the ROM BIOS data area           }
  79.      ComBaseAddr: array[Com1..Com2] of integer = ($03f8, $02f8) ;
  80.  
  81. var
  82.      BIOSComBaseAddr: array[Com1..Com2] of integer Absolute $0040:$0000;
  83.  
  84. {
  85.     Define a ring buffer for Asynch_Interrupt to write into
  86.     and ReadCom to read from.
  87. }
  88.      Input  : tBuffer ;
  89.  
  90.      OutPut : tBuffer ;
  91.  
  92.      LSRstat,                           {  Line Status Reg at interrupt    }
  93.      MSRstat       : byte ;             {  Modem Status Reg at interrupt.  }
  94.      ComSaveVec    : tSaveVector ;      {  saved Async Interrupt vector    }
  95.      ComBase       : integer ;          {  Opened Com port base address    }
  96.      ActiveComPort : tComPort ;         {  Opened Com                      }
  97.      imvalue       : integer ;          {  Interrupt Mask value in use     }
  98.  
  99.     { Define Equivalent Port Address Registers. }
  100.  
  101.      RBR_Port,
  102.      THR_Port,
  103.      IER_Port,
  104.      FCR_Port,  { FIFO Control Register *16550 UART ONLY* }
  105.      IIR_Port,
  106.      LCR_Port,
  107.      MCR_Port,
  108.      LSR_Port,
  109.      MSR_Port,
  110.      SCR_Port,  { Scratch Register *16450, 16550 UARTs ONLY* }
  111.      DLL_Port,
  112.      DLM_Port      : integer ;
  113.  
  114. {
  115.   These are interrupt type counters.  They are not used by the routine
  116.   explicitly, and can probably be deleted if desired.  I like to see
  117.   the statistics, hence I left 'em in:
  118. }
  119.      i0, i2, i4, i6    : integer ;
  120.      ir, iw, ikp       : integer ;
  121.  
  122.     { Define Variables needed by Asynch_Interrupts procedure. }
  123.  
  124.      IIRreg,
  125.      IType             : byte ;
  126.      Temp              : integer ;
  127.      OpenError         : boolean ;
  128.      t16550            : boolean ;
  129.      tUseCTS           : boolean ;
  130.  
  131.      { Define Line Status Flags }
  132.  
  133.      tCTSTimeOut,
  134.      tDataReady,
  135.      tOverrunError,
  136.      tParityError,
  137.      tFramingError,
  138.      tBreakInterrupt,
  139.      tTHREmpty,
  140.      tTransmitterEmpty,
  141.      tRCVRError         : byte ;
  142.  
  143.      { Define Modem Status Flags }
  144.  
  145.      tClearToSend,
  146.      tDataSetReady,
  147.      tRingIndicator,
  148.      tDataCarrierDetect : byte ;
  149.  
  150. Procedure InstallInterrupt(IntVect: integer;
  151.                         Var SaveVector: tSaveVector);
  152. Var
  153.     dosregs: tregpak ;
  154.  
  155. Begin
  156.   inline($FA);                        {  cli        disable interrupts       }
  157.  
  158.   With dosregs Do Begin
  159.     ds := SaveVector.CS;
  160.     dx := SaveVector.IP;
  161.     ah := $25 ;
  162.     al := IntVect ;
  163.     MsDos(dosregs);                   {  DOS function 25 - set vector        }
  164.   End;
  165.   inline($FB);                        {  sti        re-enable ints           }
  166. End;
  167.  
  168. { This procedure returns the line status of the UART.  The flags are
  169.   considered on (TRUE) if they are non-zero.
  170. }
  171.  
  172. Procedure GetLineStatus ;
  173.  
  174. begin
  175.   LSRstat := PORT[LSR_Port] and $1E;
  176.   tOverrunError   := LSRstat and $02 ;
  177.   tParityError    := LSRstat and $04 ;
  178.   tFramingError   := LSRstat and $08 ;
  179.   tBreakInterrupt := LSRstat and $10 ;
  180.   tRCVRError      := LSRstat and $20 ;
  181. end ;
  182.  
  183. { This procedure returns the modem status.  The flags are
  184.   considered on (TRUE) if they are non-zero.
  185. }
  186. Procedure GetModemStatus ;
  187.  
  188. begin
  189.   MSRstat := PORT[MSR_Port];
  190.   tClearToSend       := MSRstat and $10 ;
  191.   tDataSetReady      := MSRstat and $20 ;
  192.   tRingIndicator     := MSRstat and $40 ;
  193.   tDataCarrierDetect := MSRstat and $80 ;
  194. end ;
  195.  
  196. { This procedure is private to the Interrupt handler and should not be used
  197.   for general sending of raw data to the UART.
  198. }
  199.  
  200. Procedure SendCharacter ;
  201.  
  202. begin
  203. with Output do
  204. begin
  205.   if Write_Ptr = Read_Ptr then Port[IER_Port] := Port[IER_Port] and $FD
  206.   else
  207.     begin
  208.       if tUseCTS = TRUE then
  209.         begin
  210.           if (Port[MSR_Port] and $10) = $10 then
  211.             begin
  212.               PORT[THR_Port] := ORD(Ring_Buffer^[Write_Ptr]) ;
  213.               Write_Ptr := (Write_Ptr + 1) mod Buffer_Len ;
  214.               tCTSTimeOut := 0 ;
  215.             end
  216.           else
  217.             begin
  218.               Port[IER_Port] := Port[IER_Port] and $FD ;
  219.               tCTSTimeOut := 1 ;
  220.             end ;
  221.         end
  222.       else
  223.         begin
  224.           PORT[THR_Port] := ORD(Ring_Buffer^[Write_Ptr]) ;
  225.           Write_Ptr := (Write_Ptr + 1) mod Buffer_Len ;
  226.         end ;
  227.     end ;
  228. end { with }
  229. end ;
  230.  
  231. { This procedure is private to the Interrupt handler and should not be used
  232.   for general receiving of raw data to the UART.
  233. }
  234. Procedure GetReceivedChar ;
  235.  
  236. begin
  237. with Input do
  238. begin
  239.   Temp := (Write_Ptr + 1) mod Buffer_Len ;
  240.   If (LSRstat and $9F) = 0 then                {  If Line Status is OK  }
  241.     If Temp <> Read_Ptr then
  242.       Begin
  243.         Ring_Buffer^[Write_Ptr] := PORT[RBR_Port];
  244.         Write_Ptr := Temp ;
  245.       End
  246.     Else LSRstat := (LSRstat or $02);
  247. end
  248. End;
  249.  
  250. {********************************************************************}
  251. {                                                                    }
  252. {       This routine gets control upon an Asynch Interrupt           }
  253. {       We service all four interrupt types generated by the         }
  254. {       INS8250 chip:                                                }
  255. {                    1. Received character error or break.           }
  256. {                    2. Received data ready.                         }
  257. {                    3. Transmit Hold Register Empty.                }
  258. {                    4. Modem Status Change                          }
  259. {                                                                    }
  260. {       In addition, circular queues are used for transmitting       }
  261. {       and receiveing data from the COM1 port.  These queues        }
  262. {       can optionally be turned off if buffer overflow is           }
  263. {       detected.                                                    }
  264. {                                                                    }
  265. {********************************************************************}
  266.  
  267. Procedure Asynch_Interrupt;
  268.  
  269. Begin
  270.   inline($50/$53/$51/$52/$57/$56/$06);  {  push all registers }
  271.   inline($1E);                          {  push   ds }
  272.   inline($2E/$8E/$1E/ourDS);            {  mov   DS,CS:ourDS }
  273.   inline($FB) ;
  274.  
  275. {=============================================================================
  276.   We enter a service loop to handle all interrupts at this point in the code.
  277.   This is neccessary because the 8259 cannot handle another 8250 interrupt
  278.   while we service the last interrupt, hence we are polling the 8250 in this
  279.   routine until all interrupts are serviced.
  280.  =============================================================================}
  281.  
  282. repeat
  283.   IIRreg := PORT[IIR_Port] ;            {  Get Interrupt Identification  }
  284.   If (IIRreg and $01) = 0 then Begin    {  If interrupt pending then }
  285.     case (IIRreg and $06) of            {  determine cause of interrupt }
  286.  
  287.     { Received data available }
  288.  
  289.     $04: Begin
  290.            i4 := i4 + 1 ;
  291.            GetReceivedChar ;
  292.          End ;
  293.  
  294.     { Received character error interrupt }
  295.  
  296.     $06: begin
  297.            i6 := i6 + 1 ;
  298.            GetLineStatus ;
  299.          end ;
  300.  
  301.     { Transmit hold register empty }
  302.  
  303.     $02: begin
  304.            i2 := i2 + 1 ;
  305.            SendCharacter ;
  306.          end ;
  307.  
  308.     { Modem status change }
  309.  
  310.     $00: begin
  311.            i0 := i0 + 1 ;
  312.            GetModemStatus ;
  313.          end ;
  314.     else ;
  315.     end ; { Case }
  316.   end ;
  317.  
  318. until (IIRreg and $01) = 1 ;
  319.  
  320. { Turn off 8259 and restore all registers. }
  321.  
  322.   PORT[PICCMD] := EOI;                  {  Send End Of Interrupt to 8259 }
  323.   inline($1F);                          {  pop    ds }
  324.   inline($07/$5E/$5F/$5A/$59/$5B/$58);  {  pop rest of regs }
  325.   inline($8B/$E5);                      {  mov    sp,bp }
  326.   inline($5D) ;                         {  pop    bp }
  327.   inline($CF);                          {  iret }
  328. End;
  329.  
  330.  
  331. { Initalize the communications port.  In this version of T301_ASY.INC,
  332.   only one communcations port is supported at a time.  By creating two
  333.   Asynch_Interrupt routines, or decoding which hardware interrupt gen-
  334.   erated the call, both COM ports can be supported simultaneously.
  335.  
  336.   In the following procedure call, the parameters are as follows:
  337.  
  338.   ComPort   : An integer representing COM1 or COM2.  [1..2]
  339.  
  340.   InBuf     : Maximum Input Buffer Size [1..MaxInt]
  341.  
  342.   OutBuf    : Maximum Output Buffer Size [1..MaxInt]
  343.  
  344.   Unless you are running at 9600 baud, you can create small buffers
  345.   of around 128 bytes and be perfectly safe from overrunning the buffers.
  346.   At 9600 baud, you may want to create an Input Buffer of about
  347.   1024 to 4096 bytes to handle high speed burst transmissions.  The Out-
  348.   put Buffer is pretty immaterial.  16 to 128 bytes is really all you'll
  349.   need for most applications, although 256 to 512 bytes will help
  350.   marginally speed binary file transfers, IF there is little or no line
  351.   noise.
  352. }
  353.  
  354. Procedure InitComPort (ComPort, InBuf, OutBuf : Integer) ;
  355.  
  356. var
  357.   IIR_Port : Integer ;
  358.   temp_ptr : ^tBufferType ;
  359.  
  360. begin
  361.  
  362.   OpenError := FALSE ;
  363.                                   {  Init the Const "ourDS" for use by
  364.                                      the Async_Interrupt routine         }
  365.   ourDS := DSEG ;
  366.                                   {  Swap Com interrupt vector           }
  367.   With ComSaveVec Do Begin
  368.     CS := CSEG;
  369.     IP := OFS(Asynch_Interrupt);
  370.   End;
  371.  
  372.   case Comport of
  373.  
  374.     2 : ActiveComPort := Com2 ;
  375.     1 : ActiveComPort := Com1 ;
  376.     else begin
  377.            OpenError := TRUE ;
  378.            ActiveComPort := Com1 ;
  379.          end ;
  380.   end ;
  381.  
  382.   ComBase := BIOSComBaseAddr[ActiveComPort];  {  Select Input Port         }
  383.  
  384.   { We need to check to see if the requested COMx port exists.  If a 16550
  385.     is active at reset, then the BIOS may not "see" the UART.  We put the code
  386.     through some gyrations to determine what we have, and does it exist.
  387.   }
  388.  
  389.   if (ComBase = 0) then
  390.   begin
  391.     IIR_Port := ComBaseAddr[ActiveComPort] + $02 ;
  392.  
  393.     if ((Port[IIR_Port] and $F0) <> 0) then
  394.       begin
  395.         Port[IIR_Port] := 0 ;
  396.         ComBase := ComBaseAddr[ActiveComPort] ;
  397.       end
  398.     else OpenError := TRUE ;
  399.   end ;
  400.  
  401.   InstallInterrupt(ivlist[ActiveComPort], ComSaveVec);
  402.  
  403.   with Input do
  404.   begin
  405.     Buffer_Len := InBuf ;
  406.     GetMem (Temp_Ptr, Buffer_Len) ;
  407.     Ring_Buffer := Temp_Ptr ;
  408.   end ;
  409.  
  410.   with Output do
  411.   begin
  412.     Buffer_Len := OutBuf ;
  413.     GetMem (Temp_Ptr, Buffer_Len) ;
  414.     Ring_Buffer := Temp_Ptr ;
  415.   end ;
  416. end ;
  417.  
  418. {                     Open COM1 or COM2, a la Basic                  }
  419.  
  420. Procedure SetCom( Baud, Databits, Stopbits : integer ;
  421.                   Parity : char ) ;
  422.  
  423. Const
  424.  
  425. {  Define addresses for the various Async card registers.           }
  426.  
  427.      RBR = $00;         { xF8   Receive Buffer Register             }
  428.      THR = $00;         { xF8   Transmitter Holding Register        }
  429.      IER = $01;         { xF9   Interrupt Enable Register           }
  430.      FCR = $02;         { xFA   16550 FIFO Control Register         }
  431.      IIR = $02;         { xFA   Interrupt Identification Register   }
  432.      LCR = $03;         { xFB   Line Control Register               }
  433.      MCR = $04;         { xFC   Modem Control Register              }
  434.      LSR = $05;         { xFD   Line Status Register                }
  435.      MSR = $06;         { xFE   Modem Status Register               }
  436.      DLL = $00;         { xF8   Divisor Latch Least Significant     }
  437.      DLM = $01;         { xF9   Divisor Latch Most  Significant     }
  438.  
  439.       baudcode: array[b110..b9600] of integer =
  440.                            ($417, $300, $180, $C0, $60, $30, $18, $0C);
  441.       paritycode: array[pSpace..pNone] of byte =
  442.                                              ($38, $08, $28, $18, $00);
  443.       databitscode: array[d5..d8] of byte = ($00, $01, $02, $03);
  444.       stopbitscode: array[s1..s2] of byte = ($00, $04);
  445.  
  446. Var
  447.       LCRreg,
  448.       errclear    : byte ;
  449.       baudindex   : tbaud ;
  450. Begin
  451.  
  452.   OpenError := FALSE ;            {  Default no error on open.           }
  453.                                   {  Init the Const "ourDS" for use by
  454.                                      the Async_Interrupt routine         }
  455.   imvalue := imlist[ActiveComPort] ;      {  Select Interrupt Mask val }
  456.  
  457.   Input.Read_Ptr   := 0 ;                 {  Init buffer pointers      }
  458.   Input.Write_Ptr  := 0 ;
  459.   OutPut.Read_Ptr  := 0 ;
  460.   OutPut.Write_Ptr := 0 ;
  461.  
  462.   RBR_Port := RBR + ComBase ;
  463.   THR_Port := THR + ComBase ;
  464.   IER_POrt := IER + ComBase ;
  465.   IIR_Port := IIR + ComBase ;
  466.   LCR_Port := LCR + ComBase ;
  467.   MCR_Port := MCR + ComBase ;
  468.   LSR_Port := LSR + ComBase ;
  469.   MSR_Port := MSR + ComBase ;
  470.   DLL_Port := DLL + ComBase ;
  471.   DLM_Port := DLM + ComBase ;
  472.  
  473.   {
  474.      Reset any pending error conditions and turn off DLAB.
  475.   }
  476.   Port[LCR_Port] := Port[LCR_Port] and $7F ;
  477.   errclear := PORT[LSR_Port] ;
  478.   errclear := PORT[RBR_Port] ;
  479.   errclear := PORT[MSR_Port] ;
  480.   {
  481.           Set Baud Rate Divisor Registers and the Line Control Register
  482.   }
  483.   LCRreg := $80;               {  Set Divisor Latch Access Bit in LCR }
  484.  
  485.   case Parity of
  486.    'S' : LCRreg := LCRreg or paritycode[pSpace] ;
  487.    'O' : LCRreg := LCRreg or paritycode[pOdd]   ;
  488.    'M' : LCRreg := LCRreg or paritycode[pMark]  ;
  489.    'E' : LCRreg := LCRreg or paritycode[pEven]  ;
  490.    'N' : LCRreg := LCRreg or paritycode[pNone]  ;
  491.    else LCRreg  := LCRreg or paritycode[pNone]   ;
  492.   end ; { case }
  493.  
  494.   case databits of
  495.     5 : LCRreg := LCRreg or databitscode[d5] ;
  496.     6 : LCRreg := LCRreg or databitscode[d6] ;
  497.     7 : LCRreg := LCRreg or databitscode[d7] ;
  498.     8 : LCRreg := LCRreg or databitscode[d8] ;
  499.     else LCRreg := LCRreg or databitscode[d8] ;
  500.   end ; { case }
  501.  
  502.   case stopbits of
  503.     1 : LCRreg := LCRreg or stopbitscode[s1] ;
  504.     2 : LCRreg := LCRreg or stopbitscode[s2] ;
  505.     else LCRreg := LCRreg or stopbitscode[s1] ;
  506.   end ; { case }
  507.  
  508.   baudindex := b1200 ;
  509.   if baud = 110 then baudindex := b110 ;
  510.   if baud = 150 then baudindex := b150 ;
  511.   if baud = 300 then baudindex := b300 ;
  512.   if baud = 600 then baudindex := b600 ;
  513.   if baud = 1200 then baudindex := b1200 ;
  514.   if baud = 2400 then baudindex := b2400 ;
  515.   if baud = 4800 then baudindex := b4800 ;
  516.   if baud = 9600 then baudindex := b9600 ;
  517.  
  518.   inline ($FA) ;
  519.   PORT[LCR_Port] := LCRreg;                  {  Set Parity, Data and Stop Bits
  520.                                              and set DLAB                     }
  521.   PORT[DLM_Port] := Hi(baudcode[Baudindex]); {  Set Baud rate                 }
  522.   PORT[DLL_Port] := Lo(baudcode[Baudindex]); {  Set Baud rate                 }
  523.   PORT[LCR_Port] := LCRreg and $7F ;         {  Reset DLAB                    }
  524.  
  525.   PORT[PICMSK] := PORT[PICMSK] and imvalue;  {  Enable ASynch Int             }
  526.  
  527.   { Note: OUT2, despite documentation,  MUST be ON, to enable interrupts      }
  528.  
  529.   PORT[MCR_Port] := $0F;                     {  Set RTS, DTR, OUT1, OUT2      }
  530.   PORT[IER_Port] := $0D ;                    {  Enable some interrupts        }
  531.   inline ($FB) ;
  532.   {
  533.     Let's determine if a 16550 INS UART is installed.
  534.   }
  535.  
  536.   t16550 := FALSE ;
  537.   Port[FCR_Port] := $01 ;
  538.   if (Port[IIR_Port] and $C0) <> $00 then
  539.     begin
  540.       Port[FCR_Port] := $00 ;
  541.       t16550 := TRUE ;
  542.       Port[FCR_Port] := $C1 ;
  543.     end ;
  544.  
  545.   PORT[PICCMD] := EOI ;
  546.   tUseCTS := FALSE ;
  547.   GetLineStatus ;
  548.   GetModemStatus ;
  549.   i0 := 0 ;
  550.   i2 := 0 ;
  551.   i4 := 0 ;
  552.   i6 := 0 ;
  553.   iw := 0 ;
  554.   ir := 0 ;
  555.   ikp := 0 ;
  556. End;
  557.  
  558.  
  559. {                 Close any initialized COM                                 }
  560.  
  561. Procedure CloseCom;
  562. Begin
  563.     inline ($FA) ;
  564.     PORT[IER_Port] := 0 ;   {  Disable Data Avail interrupt        }
  565.     Port[MCR_Port] := 0 ;
  566.                             {  Disable Async interrupt             }
  567.     PORT[PICMSK] := PORT[PICMSK] or ($FF - imvalue);
  568.     inline ($FB) ;
  569.     if t16550 = TRUE then PORT[FCR_Port] := $00 ;
  570. End;
  571.  
  572. Procedure Break ;
  573.  
  574. var
  575.     LCRreg,
  576.     DropDTR   : byte ;
  577.  
  578. begin
  579.     LCRreg := Port[LCR_Port] or $40 ;
  580.     Port[LCR_Port] := LCRreg ;
  581.     Delay (600) ;
  582.     LCRreg := Port[LCR_Port] xor $40 ;
  583.     Port[LCR_Port] := LCRreg ;
  584. end ;
  585.  
  586. Procedure Purge ;
  587.  
  588. { Purpose : Purge all circular queues. }
  589.  
  590. begin
  591.     Input.Read_Ptr   := 0;                  {  Init buffer pointers      }
  592.     Input.Write_Ptr  := 0;
  593.     OutPut.Read_Ptr  := 0 ;
  594.     OutPut.Write_Ptr := 0 ;
  595.  
  596. { Reset 8250 Interrupt Enable Registers. }
  597.  
  598.   Port [IER_Port] := Port [IER_Port] and $0D ;
  599.   if t16550 = TRUE then PORT[FCR_Port] := PORT[FCR_Port] or $06 ;
  600. end ;
  601.  
  602. Function  ReadChar : char ;
  603.  
  604. {
  605.   This routine is intended to function as an AUX or USR logical character
  606.   input device driver.  Usage is AuxInPtr := ofs(Readchar) followed by
  607.   the actual read operation read(AUX, var).
  608. }
  609.  
  610. Begin
  611.   with Input do
  612.   begin
  613.     ir := ir + 1 ;
  614.     if Read_Ptr = Write_Ptr then ReadChar := chr(0)
  615.     else begin
  616.         ReadChar := CHAR(Ring_Buffer^[Read_Ptr]) ;
  617.         inline ($FA) ;
  618.         Read_Ptr := (Read_Ptr + 1) mod Buffer_Len ;
  619.         inline ($FB) ;
  620.     end ;
  621.   end
  622. End;
  623.  
  624. Function RS232ChrAvailable : boolean ;
  625.  
  626. {
  627.   This routine should be checked before reading a character from the RS232
  628.   port, otherwise garbage will be returned and the buffer count messed up.
  629. }
  630.  
  631. begin
  632.   with Input do
  633.   begin
  634.    If Read_Ptr = Write_Ptr then RS232ChrAvailable := FALSE
  635.       else RS232ChrAvailable := TRUE ;
  636.   end
  637. end ;
  638.  
  639. Procedure WriteChar (ch:char) ;
  640.  
  641. {
  642.   This is the corresponding AuxOutPtr routine for use with write (aux, var).
  643. }
  644.  
  645. begin
  646.   iw := iw + 1;
  647.   with Output do
  648.   begin
  649.     Ring_Buffer^[Read_Ptr] := ORD(ch) ;
  650.     Read_Ptr := (Read_Ptr + 1) mod Buffer_Len ;
  651.   end ; { with }
  652.   Port [IER_Port] := Port [IER_Port] or $02 ;
  653. end ;
  654.  
  655. Procedure WriteBlock (var Block; size: integer) ;
  656.  
  657. {
  658.   This routine fills the OutPut.Ring_Buffer^, and then enables THRE interrupt.
  659. }
  660.  
  661. Label
  662.     reload ;
  663.  
  664. type
  665.     Block_Ovly = array [1..MaxInt] of Byte ;
  666.  
  667. Var
  668.     Dummy_Block: Block_Ovly absolute Block ;
  669.     Dummy_Block_Count: Integer ;
  670.  
  671. begin
  672.  
  673.   Dummy_Block_Count := 1 ;
  674.  
  675.   with Output do
  676.   begin
  677.     reload:
  678.  
  679.     while ((((Read_Ptr + 1) mod Buffer_Len) <> Write_Ptr) and
  680.            (Dummy_Block_Count <= size))
  681.     begin
  682.          Ring_Buffer^[Read_Ptr] := Dummy_Block[Dummy_Block_Count] ;
  683.          Read_Ptr := (Read_Ptr + 1) mod Buffer_Len ;
  684.          Dummy_Block_Count := Dummy_Block_Count + 1
  685.     end
  686.   end ; { with }
  687.     Port [IER_Port] := Port [IER_Port] or $02 ;
  688.     if (Dummy_Block_Count < size) THEN goto reload ;
  689. end ;
  690.  
  691. Procedure WriteString (var Block: BigString ) ;
  692.  
  693. {
  694.   This routine fills the OutPut.Ring_Buffer^, and then enables THRE interrupt.
  695. }
  696.  
  697. Label reload ;
  698.  
  699. Var
  700.     Dummy_Block_Count: Integer ;
  701.  
  702. begin
  703.     Dummy_Block_Count := 1 ;
  704.  
  705.   with Output do
  706.   begin
  707.     reload:
  708.  
  709.     while ((((Read_Ptr + 1) mod Buffer_Len) <> Write_Ptr) and
  710.            (Dummy_Block_Count <= Length(Block)))
  711.     begin
  712.          Ring_Buffer^[Read_Ptr] := ORD(Block[Dummy_Block_Count]) ;
  713.          Read_Ptr := (Read_Ptr + 1) mod Buffer_Len ;
  714.          Dummy_Block_Count := Dummy_Block_Count + 1
  715.      end ;
  716.   end ;
  717.      Port [IER_Port] := Port [IER_Port] or $02 ;
  718.      if (Dummy_Block_Count < Length(Block)) THEN goto reload ;
  719. end ;
  720.  
  721. Procedure UseCTS ;
  722.  
  723. begin
  724.   tUseCTS := TRUE ;
  725.   tCTSTimeOut := 0 ;
  726. end ;
  727.