home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / FIBMCM.ZIP / COM_MGR.PAS next >
Encoding:
Pascal/Delphi Source File  |  1989-05-09  |  26.8 KB  |  680 lines

  1. unit COM_MGR;
  2.   (* Manager of the COMx ports, rewritten from the illcoded ScenicSoft
  3.   DEVICEIO units and, more usefully, from IBMCOM, a freeware program by
  4.   Wayne E. Conrad (CompuServe 71631,210 or at (602) 484-9356 @ 2400 baud).
  5.   
  6.      The main differences from Conrad's approach are
  7.   
  8.        (1) Instead of separate calls to set the speed, parity, and so
  9.      on, setup parameters here are all pre-loaded into a record, then
  10.      set into the UART during initialization.
  11.      
  12.        (2) This routine will comply with various uses of the DSR and
  13.      XOFF/XON protocols.  In particular,
  14.      
  15.             If NEED_DSR is true, then this routine will hang until
  16.           the attached device raises the DSR line.
  17.             If USING_DTR is true, then this routine will lower DTR
  18.           whenever its own buffers are getting full.
  19.             If USING_XOFF is true, then this routine will refrain from
  20.           transmissions between receiving an XOFF and receiving an XON,
  21.           and it will send XOFF & XON as its own buffers become full and
  22.           empty, respectively.  The implementation allows for a degree of
  23.           paranoia; if XOFF/XON characters are getting lost, with consequent
  24.           buffer overflow or deadlock, then simple changes can allow each
  25.           XOFF and/or XON to be sent more than once.  Also, one may cause
  26.           long XOFF waits to be announced on the screen, and then evaded
  27.           with the entry of a carriage return.
  28.        
  29.        (3) The COM_WRITE_STRING here makes use of a fixed external buffer
  30.      that is provided by the much larger application package in which this
  31.      unit is utilized.
  32.      
  33.        (4) Various monitoring records (count of characters dropped, count
  34.      of high water mark in input buffer, etc.) are retained for optional
  35.      display at termination.
  36.        
  37.        (5) Because port addresses and interrupt assignments are not well
  38.      established beyond COM2, only COM1 and COM2 are implemented here;
  39.      the implementation allows obvious extension for additional ports.
  40.   *)
  41.  
  42.   interface
  43.     const
  44.       COM_VERSION = 1017;
  45.  
  46.       (* eject *)
  47.     type
  48.       parity_option = (EVEN_PARITY, MARK_PARITY, NO_PARITY, ODD_PARITY,
  49.                          SPACE_PARITY);
  50.       port_option = (COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8);
  51.       stop_bits = (ONE_STOP_BIT, MORE_STOP_BITS);
  52.  
  53.       com_config_record = record
  54.         FILED_VERSION   (* version control for possible file *): integer;
  55.         NEED_DSR        (* if false, charge on regardless    *),
  56.         USING_DTR       (* hardware handshake                *),
  57.         USING_XOFF      (* software handshake                *): boolean;
  58.         THESE_DATA_BITS (* parity & data bits interdependent *): byte;
  59.         THIS_BAUD       (* 2 <= THIS_BAUD <= 115200          *): longint;
  60.         THIS_PARITY     (* parity & data-bits interdependent *): parity_option;
  61.         THIS_PORT       (* most of these not implemented     *): port_option;
  62.         THESE_STOP_BITS (* interpretation baud-dependent     *): stop_bits
  63.         end (* com_config_record *);
  64.  
  65.     var
  66.       COM_CONFIGURATION  (*  from HW_COLD_START         *): com_config_record;
  67.       COM_INPUT_WAITING  (** # of char in input buffer  *),
  68.       COM_OUTPUT_WAITING (** # of char in output buffer *): integer;
  69.  
  70.     function COM_CARRIER_PRESENT: boolean;
  71.     procedure COM_LOWER_DTR;
  72.     procedure COM_RAISE_DTR;
  73.     function COM_READ: char;
  74.     function COM_TX_READY: boolean;
  75.     procedure COM_WRITE_CH(CH: char);
  76.     procedure COM_WRITE_STRING;
  77.     procedure INIT_COM_MGR;
  78.     procedure TERM_COM_MGR;
  79.  
  80.   implementation
  81.     uses
  82.       DOS      (* GetIntVec, Intr, registers, SetIntVec *),
  83.       CRT      (* ClrEol, GotoXY, KeyPressed, WhereY *),
  84.       SCRN_MGR (* DEBUG, ERROR_EXIT, LOUD_WRITE, PAUSE *),
  85.       IOERROR  (* CHARS_WAITING, INIT_IOERROR, WAITING_CHAR *);
  86.  
  87.       (* eject *)
  88.     const
  89.       CR    = #13;
  90.       X_OFF = #19;
  91.       X_ON  = #17;
  92.  
  93.       DB_TRIGGER = 67;
  94.  
  95.       BAUD_RATE_DIVIDEND = 115200;
  96.  
  97.       RCV_BUFFER_SIZE = 1024;
  98.       LAST_RCV_CHAR   = 1023 (* derivative *);
  99.       GETTING_FULL    =  800 (* loosely derivative *);
  100.       GETTING_EMPTY   =  200 (* loosely derivative *);
  101.       TX_BUFFER_SIZE  =  256;
  102.       LAST_TX_CHAR    =  255 (* derivative *);
  103.  
  104.       CLI = $FA;
  105.       STI = $FB;
  106.  
  107.       IRQ_BASE = 8;
  108.  
  109.       (* Interrupt enable register:
  110.            bits 7-4  0000
  111.            bit 3     1 = enable change-in-modem-status interrupt
  112.            bit 2     1 = enable line-status interrupt
  113.            bit 1     1 = enable transmit-register-empty interrupt
  114.            bit 0     1 = enable data-available interrupt *)
  115.       IER_ENB_TX_EMPTY = 2;
  116.       IER_ENB_RCV_CHAR = 1;
  117.  
  118.       (* Line control register
  119.                bit 7:  0  normal
  120.                        1  address baud rate divisor registers
  121.                bit 6:  0  break disabled
  122.                        1  break enabled
  123.             bits 5-3: 000 no parity
  124.                       001 odd parity
  125.                       010 no parity
  126.                       011 even parity
  127.                       100 no parity
  128.                       101 parity always 1
  129.                       110 no parity
  130.                       111 parity always 0
  131.                bit 2:  0  1 stop bit
  132.                        1  1.5 stop bits if 5 bits/character or
  133.                           2 stop bits if 6-8 bits/character
  134.             bits 1-0: 00  5 bits/character
  135.                       01  6 bits/character
  136.                       10  7 bits/character
  137.                       11  8 bits/character *)
  138.       LCR_SET_BAUD = $80;
  139.       LCR_PARITY_BITS: array[EVEN_PARITY .. SPACE_PARITY] of byte =
  140.                                                      ($18, $28, 0, 8, $38);
  141.       LCR_STOP_BITS: array[ONE_STOP_BIT .. MORE_STOP_BITS] of byte = (0, 4);
  142.       LCR_DATA_BITS: array[5 .. 8] of byte = (0, 1, 2, 3);
  143.       (* eject *)
  144.       (* Modem control register
  145.             bits 7-5: 000
  146.                bit 4:  0 normal
  147.                        1 loop back test
  148.                bit 3:  1 interrupts to system bus, user-designated output: OUT2
  149.                bit 2:    user-designated output, OUT1
  150.                bit 1:  1 activate rts
  151.                bit 0:  1 activate dtr *)
  152.       MCR_DTR = 1;
  153.       MCR_OUT2 = 8;
  154.       MCR_RTS = 2;
  155.       
  156.       (* Modem status register
  157.            bit 7:  1 receive line signal detect
  158.            bit 6:  1 ring indicator (all PCs except PCjr)
  159.            bit 5:  1 dsr
  160.            bit 4:  1 cts
  161.            bit 3:  1 receive line signal detect has changed state
  162.            bit 2:  1 ring indicator has changed state (all PCs except PCjr)
  163.            bit 1:  1 dsr has changed state
  164.            bit 0:  1 cts has changed state *)
  165.       MSR_CARRIER = $80;
  166.       MSR_DSR = $20;
  167.  
  168.       PIC_EOI_PORT = $20;
  169.       PIC_IE_PORT  = $21;
  170.  
  171.     type
  172.       rcv_record = record
  173.         NEXT_IN  (** next slot for an incoming character *),
  174.         NEXT_OUT (*  next source for COM_READ            *): integer;
  175.         BUFFER: array[0 .. LAST_RCV_CHAR] of byte
  176.         end (* rcv_record *);
  177.  
  178.       tx_record = record
  179.         NEXT_IN  (*  next sink for COM_WRITE_CH *),
  180.         NEXT_OUT (** next source for port       *): integer;
  181.         BUFFER: array[0 .. LAST_TX_CHAR] of byte
  182.         end (* tx_record *);
  183.  
  184.         (* eject *)
  185.     var                     (** variables changed by interrupt *)
  186.       EVER_ACTIVE           (*  if false, omit buff-use report *),
  187.       INITIALIZED           (*  worth running termination code *),
  188.       XOFF_RECEIVED         (** XOFF rec'd from modem/printer  *),
  189.       XOFF_SENT             (** XOFF sent to modem/printer     *): boolean;
  190.       IRQ_USED              (*  int used by THIS_PORT          *),
  191.       I8259_BIT             (*  8259 int mask of THIS_PORT     *),
  192.       OLD_IER               (*  int enb reg before init        *),
  193.       OLD_I8259_MASK        (*  8259 mask before initializing  *),
  194.       OLD_MCR               (*  modem ctl reg before init      *),
  195.       STACKED_XOFFS         (** next chars to be sent          *),
  196.       STACKED_XONS          (** next chars to be sent          *): byte;
  197.       DROPPED               (** dropped input chars            *),
  198.       INPUT_ZENITH          (** max ever in input buffer       *): integer;
  199.       WAITING_FOR_DSR       (*  cycles waiting to write        *),
  200.       WAITING_FOR_XON       (*  cycles waiting for receiver    *): longint;
  201.       OLD_IRQ_INT_PROC      (*  previous int handler for port  *),
  202.       SAVE_EXIT             (*  for exit chain                 *): pointer;
  203.       RECEIVE_BUFFER        (*  complex once on heap           *): rcv_record;
  204.       TRANSMIT_BUFFER       (*  complex once on heap           *): tx_record;
  205.       DATA_REGISTER         (*  subscript of PORT array        *),
  206.       INT_ENABLE_REGISTER   (*  subscript of PORT array        *),
  207.       INT_ID_REGISTER       (*  subscript of PORT array        *),
  208.       LINE_CTL_REGISTER     (*  subscript of PORT array        *),
  209.       MODEM_CTL_REGISTER    (*  subscript of PORT array        *),
  210.       MODEM_STATUS_REGISTER (*  subscript of PORT array        *),
  211.       LINE_STATUS_REGISTER  (*  subscript of PORT array        *): word;
  212.  
  213.     procedure DISABLE_INTERRUPTS; inline(CLI);
  214.     procedure ENABLE_INTERRUPTS;  inline(STI);
  215.  
  216.     function COM_CARRIER_PRESENT: boolean;
  217.       (* Return true iff carrier is present. *)
  218.     
  219.       begin (* COM_CARRIER *)
  220.         COM_CARRIER_PRESENT :=
  221.                        ((port[MODEM_STATUS_REGISTER] and MSR_CARRIER) > 0)
  222.       end   (* COM_CARRIER *);
  223.       
  224.     procedure COM_INT_HANDLER; interrupt;
  225.       (* Field interrupts from THIS_PORT.  Operation of this ISR is
  226.       crucially dependent upon contents received from INT_ID_REGISTER.
  227.       In the case of a transmit-register-empty interrupt, we do a
  228.       test of the line-status register to see if the transmit
  229.       holding register is truly empty.  Some UARTS seem to cause transmit
  230.       interrupts when the holding register isn't empty, causing transmitted
  231.       characters to be lost.
  232.         If our transmit buffer is empty, then disable the transmitter to
  233.       prevent any more transmit interrupts.  This entails resetting the
  234.       appropriate bit of the INT_ENABLE_REGISTER, as described above.
  235.       
  236.         If the modem is not ready for another character (not DSR), then
  237.       hang; this is dependent upon the format of the MODEM_STATUS_REGISTER.
  238.       Otherwise, send the character.
  239.       *)
  240.       (* eject *)
  241.       const
  242.          (* Line status register
  243.               bit 7  0
  244.               bit 6  1 ($40) = transmit shift register is empty
  245.               bit 5  1 ($20) = transmit hold register is empty
  246.               bit 4  1 ($10) = break received
  247.               bit 3  1 ($08) = framing error received
  248.               bit 2  1 ($04) = parity error received
  249.               bit 1  1 ($02) = overrun error received
  250.               bit 0  1 ($01) = data received *)
  251.         LSR_TX_EMPTY = $20;
  252.         
  253.          (* Interrupt ID register
  254.               bits 7-3  00000
  255.               bits 2-1  00 = change-in-modem-status  (lowest priority)
  256.               bits 2-1  01 = transmit-register-empty (low priority)
  257.               bits 2-1  10 = data available          (high priority)
  258.               bits 2-1  11 = line status             (highest priority)
  259.               bit 0      1 = no interrupt pending
  260.               bit 0      0 = interrupt pending *)
  261.         IID_CH_RECD = 4;
  262.         IID_LINE_CHANGED = 6;
  263.         IID_MODEM_CHANGED = 0;
  264.         IID_TX_EMPTY = 2;
  265.       
  266.         PIC_EOI_PORT = $20;
  267.         PIC_EOI_ARMED = $20;
  268.  
  269.       var
  270.         PORT_QUIET    (* bit 0 of INT_ID_REGISTER is 1 *): boolean;
  271.         SCRATCH       (* trash                         *): byte;
  272.  
  273.         (* eject *)
  274.       procedure CHAR_RECEIVED;
  275.         (* Kludge to reduce the size of COM_INT_HANDLER *)
  276.         
  277.         var
  278.           RECEIVED_CHAR (* could be X_ON or X_OFF *): char;
  279.         
  280.         begin (* CHAR_RECEIVED *)
  281.           with COM_CONFIGURATION, RECEIVE_BUFFER do
  282.             begin (* received character *)
  283.               RECEIVED_CHAR := chr(port[DATA_REGISTER]);
  284.             
  285.               if (RECEIVED_CHAR = X_OFF) and USING_XOFF then
  286.                 XOFF_RECEIVED := true
  287.               else if (RECEIVED_CHAR = X_ON) and USING_XOFF then
  288.                 begin (* restart output *)
  289.                   XOFF_RECEIVED := false;
  290.                   if COM_OUTPUT_WAITING > 0 then
  291.                     port[INT_ENABLE_REGISTER] := port[INT_ENABLE_REGISTER] or
  292.                                                               IER_ENB_TX_EMPTY
  293.                 end   (* restart output *)
  294.               else if COM_INPUT_WAITING >= RCV_BUFFER_SIZE then
  295.                 inc(DROPPED)
  296.               else
  297.                 begin (* found room *)
  298.                   BUFFER[NEXT_IN] := ord(RECEIVED_CHAR);
  299.                   inc(NEXT_IN);
  300.                   if NEXT_IN >= RCV_BUFFER_SIZE then
  301.                     NEXT_IN := 0;
  302.                   inc(COM_INPUT_WAITING);
  303.                   if COM_INPUT_WAITING > INPUT_ZENITH then
  304.                     INPUT_ZENITH := COM_INPUT_WAITING;
  305.                   if COM_INPUT_WAITING >= GETTING_FULL then
  306.                     if USING_XOFF then
  307.                       begin (* stack X_OFF *)
  308.                         STACKED_XOFFS := 1;
  309.                         STACKED_XONS := 0;
  310.                         port[INT_ENABLE_REGISTER] :=
  311.                           port[INT_ENABLE_REGISTER] or IER_ENB_TX_EMPTY;
  312.                         XOFF_SENT := true
  313.                       end   (* stack X_OFF *)
  314.                     else if USING_DTR then
  315.                       port[MODEM_CTL_REGISTER] := port[MODEM_CTL_REGISTER] and
  316.                                                                   not MCR_DTR
  317.                 end   (* found room *)
  318.             end   (* received character *);
  319.         end   (* CHAR_RECEIVED *);
  320.         
  321.         (* eject *)
  322.       procedure TX_EMPTY;
  323.         (* Kludge to reduce the size of COM_INT_HANDLER *)
  324.         
  325.         begin (* TX_EMPTY *)
  326.           if port[LINE_STATUS_REGISTER] and LSR_TX_EMPTY = 0 then
  327.             (* ignore bogus interrupt *)
  328.           else
  329.             with TRANSMIT_BUFFER do
  330.               begin (* probably will transmit something *)
  331.                 if COM_CONFIGURATION.NEED_DSR then
  332.                   repeat (* need data-set-ready *)
  333.                     inc(WAITING_FOR_DSR)
  334.                   until  (* need data-set-ready *)
  335.                               (port[MODEM_STATUS_REGISTER] and MSR_DSR) <> 0;
  336.  
  337.                 if STACKED_XOFFS > 0 then
  338.                   begin (* send X_OFF *)
  339.                     port[DATA_REGISTER] := ord(X_OFF);
  340.                     dec(STACKED_XOFFS)
  341.                   end   (* send X_OFF *)
  342.                   
  343.                 else if STACKED_XONS > 0 then
  344.                   begin (* send X_ON *)
  345.                     port[DATA_REGISTER] := ord(X_ON);
  346.                     dec(STACKED_XONS)
  347.                   end   (* send X_ON *)
  348.  
  349.                 else if XOFF_RECEIVED or (COM_OUTPUT_WAITING = 0) then
  350.                   port[INT_ENABLE_REGISTER] := port[INT_ENABLE_REGISTER] and
  351.                                                       not IER_ENB_TX_EMPTY
  352.                 else
  353.                   with TRANSMIT_BUFFER do
  354.                     begin (* send character from buffer *)
  355.                       port[DATA_REGISTER] := BUFFER[NEXT_OUT];
  356.                       inc(NEXT_OUT);
  357.                       if NEXT_OUT >= TX_BUFFER_SIZE then
  358.                         NEXT_OUT := 0;
  359.                       dec(COM_OUTPUT_WAITING)
  360.                     end   (* send character from buffer *)
  361.               end   (* probably will transmit something *)
  362.         end   (* TX_EMPTY *);
  363.         
  364.         (* eject *)
  365.       begin (* COM_INT_HANDLER *)
  366.         PORT_QUIET := false;
  367.  
  368.         repeat (* check interrupt bit *)
  369.           case port[INT_ID_REGISTER] of
  370.                   IID_CH_RECD: CHAR_RECEIVED;
  371.                             
  372.             IID_MODEM_CHANGED: SCRATCH := port[MODEM_STATUS_REGISTER];
  373.               
  374.                  IID_TX_EMPTY: TX_EMPTY;
  375.                  
  376.              IID_LINE_CHANGED: SCRATCH := port[LINE_STATUS_REGISTER]
  377.             
  378.                           else PORT_QUIET := true
  379.             end (* case on INT_ID_REGISTER *)
  380.         until  (* check interrupt bit *) PORT_QUIET;
  381.  
  382.         port[PIC_EOI_PORT] := PIC_EOI_ARMED
  383.       end   (* COM_INT_HANDLER *);
  384.       
  385.     procedure COM_LOWER_DTR;
  386.       (* Lower (deactivate) the DTR line.  Causes most modems to hang up. *)
  387.     
  388.       begin (* COM_LOWER_DTR *)
  389.         DISABLE_INTERRUPTS;
  390.           port[MODEM_CTL_REGISTER] := port[MODEM_CTL_REGISTER] and not MCR_DTR;
  391.         ENABLE_INTERRUPTS
  392.       end   (* COM_LOWER_DTR *);
  393.       
  394.     procedure COM_RAISE_DTR;
  395.       (* Raise (activate) the DTR line. *)
  396.     
  397.       begin (* COM_RAISE_DTR *)
  398.         DISABLE_INTERRUPTS;
  399.           port[MODEM_CTL_REGISTER] := port[MODEM_CTL_REGISTER] or MCR_DTR;
  400.         ENABLE_INTERRUPTS
  401.       end   (* COM_RAISE_DTR *);
  402.     
  403.       (* eject *)
  404.     function COM_READ: char;
  405.       (* Wait for a character of input, then return it *)
  406.       
  407.       begin (* COM_READ *)
  408.         while COM_INPUT_WAITING = 0 do
  409.           (* spin *);
  410.  
  411.         with RECEIVE_BUFFER do
  412.           begin (* use RECEIVE complex *)
  413.             COM_READ := chr(BUFFER[NEXT_OUT]);
  414.             inc(NEXT_OUT);
  415.             if NEXT_OUT >= RCV_BUFFER_SIZE then
  416.               NEXT_OUT := 0
  417.           end   (* use RECEIVE complex *);
  418.  
  419.         DISABLE_INTERRUPTS;
  420.           dec(COM_INPUT_WAITING);
  421.         ENABLE_INTERRUPTS;
  422.         
  423.         if COM_INPUT_WAITING <= GETTING_EMPTY then
  424.           begin (* try to awaken sender *)
  425.             DISABLE_INTERRUPTS;
  426.               if XOFF_SENT then
  427.                 begin (* software handshake *)
  428.                   STACKED_XOFFS := 0;
  429.                   STACKED_XONS := 1;
  430.                   port[INT_ENABLE_REGISTER] := port[INT_ENABLE_REGISTER] or
  431.                                                            IER_ENB_TX_EMPTY;
  432.                   XOFF_SENT := false
  433.                 end   (* software handshake *)
  434.               else if COM_CONFIGURATION.USING_DTR then
  435.                 port[MODEM_CTL_REGISTER] := port[MODEM_CTL_REGISTER] or
  436.                                                                  MCR_DTR;
  437.             ENABLE_INTERRUPTS
  438.           end   (* try to awaken sender *)
  439.       end   (* COM_READ *);
  440.  
  441.     function COM_TX_READY: boolean;
  442.       (* installed and DSR line raised by modem *)
  443.       
  444.       begin (* COM_TX_READY *)
  445.         COM_TX_READY := INITIALIZED and
  446.                         ((not COM_CONFIGURATION.NEED_DSR) or
  447.                          ((port[MODEM_STATUS_REGISTER] and MSR_DSR) <> 0))
  448.       end   (* COM_TX_READY *);
  449.       
  450.       (* eject *)
  451.     procedure COM_WRITE_CH(CH: char);
  452.       (* Move CH to buffer for output *)
  453.  
  454.       var
  455.         THIS_CALL_XOFF: longint;
  456.  
  457.       begin (* COM_WRITE_CH *)
  458.         THIS_CALL_XOFF := 0;
  459.         while XOFF_RECEIVED do
  460.           begin (* avoid deadlock *)
  461.             inc(WAITING_FOR_XON);
  462.             inc(THIS_CALL_XOFF);
  463.             if THIS_CALL_XOFF >= 100000 then
  464.               begin (* may need intervention *)
  465.                 if DEBUG mod DB_TRIGGER = 1 then
  466.                   if THIS_CALL_XOFF = 100000 then
  467.                     LOUD_WRITE('X_OFF');
  468.                 if KeyPressed then
  469.                   if ReadKey = CR then
  470.                     XOFF_RECEIVED := false
  471.               end   (* may need intervention *)
  472.           end   (* avoid deadlock *);
  473.         
  474.         while COM_OUTPUT_WAITING = TX_BUFFER_SIZE do
  475.           inc(WAITING_FOR_DSR);
  476.  
  477.         with TRANSMIT_BUFFER do
  478.           begin (* with *)
  479.             BUFFER[NEXT_IN] := ord(CH);
  480.             inc(NEXT_IN);
  481.             if NEXT_IN >= TX_BUFFER_SIZE then
  482.               NEXT_IN := 0;
  483.             
  484.             DISABLE_INTERRUPTS;
  485.               inc(COM_OUTPUT_WAITING);
  486.               port[INT_ENABLE_REGISTER] := port[INT_ENABLE_REGISTER] or
  487.                                                          IER_ENB_TX_EMPTY;
  488.             ENABLE_INTERRUPTS
  489.           end   (* with *)
  490.       end   (* COM_WRITE_CH *);
  491.  
  492.     procedure COM_WRITE_STRING;
  493.       (* Run WAITING_CHAR[0 .. CHARS_WAITING-1] through COM_WRITE_CH *)
  494.  
  495.       var
  496.         I: integer;
  497.  
  498.       begin (* COM_WRITE_STRING *)
  499.         for I := 0 to CHARS_WAITING-1 do
  500.           COM_WRITE_CH(WAITING_CHAR[I]);
  501.         CHARS_WAITING := 0
  502.       end   (* COM_WRITE_STRING *);
  503.  
  504.       (* eject *)
  505.     {$F+}
  506.     procedure EPILOGUE;
  507.     {$F-}
  508.       (* Part of the great exit chain *)
  509.       
  510.       begin (* EPILOGUE *)
  511.         COM_OUTPUT_WAITING := 0;
  512.         TERM_COM_MGR;
  513.         if DEBUG mod DB_TRIGGER = 1 then
  514.           if EVER_ACTIVE then
  515.             begin (* report experience *)
  516.               writeln;
  517.               write(INPUT_ZENITH:10, ' character');
  518.               if INPUT_ZENITH <> 1 then
  519.                 write('s');
  520.               writeln(' in COMx input buffer at peak.');
  521.               write(DROPPED:10, ' COMx input character');
  522.               if DROPPED <> 1 then
  523.                 write('s');
  524.               writeln(' dropped.');
  525.               writeln(WAITING_FOR_DSR:10, ' cycles waiting for DSR.');
  526.               writeln(WAITING_FOR_XON:10, ' cycles waiting for XON.');
  527.               PAUSE
  528.             end   (* report experience *);
  529.         ExitProc := SAVE_EXIT
  530.       end   (* EPILOGUE *);
  531.  
  532.     procedure INIT_COM_MGR;
  533.       (* Called from PSTSCRPT, only after COM_CONFIGURATION set by
  534.       HW_COLD_START and use of real printer has been determined as
  535.       part of HW_BEGIN_DOCUMENT *)
  536.       
  537.       procedure SET_SPEED_AND_PARITY;
  538.         (* Kludge to shorten INIT_COM_MGR *)
  539.         
  540.         begin (* SET_SPEED_AND_PARITY *)
  541.           with COM_CONFIGURATION do
  542.             begin (* with *)
  543.               if THIS_BAUD < 2 then
  544.                 THIS_BAUD := 2;
  545.               
  546.               DISABLE_INTERRUPTS;
  547.                 port[LINE_CTL_REGISTER] := LCR_SET_BAUD;
  548.                 portw[DATA_REGISTER] := 115200 div THIS_BAUD;
  549.                 port[LINE_CTL_REGISTER] := LCR_PARITY_BITS[THIS_PARITY] or
  550.                                            LCR_STOP_BITS[THESE_STOP_BITS] or
  551.                                            LCR_DATA_BITS[THESE_DATA_BITS];
  552.               ENABLE_INTERRUPTS
  553.             end   (* with *)
  554.         end   (* SET_SPEED_AND_PARITY *);
  555.             
  556.         (* eject *)
  557.       procedure SET_VARIABLES;
  558.         (* Kludge to shorten INIT_COM_MGR *)
  559.         
  560.         begin (* SET_VARIABLES *)
  561.           if COM_CONFIGURATION.FILED_VERSION <> COM_VERSION then
  562.             ERROR_EXIT('COM_MGR configuration data in obsolete format.');
  563.           COM_INPUT_WAITING := 0;
  564.           COM_OUTPUT_WAITING := 0;
  565.           EVER_ACTIVE := true;
  566.           RECEIVE_BUFFER.NEXT_IN := 0;
  567.           RECEIVE_BUFFER.NEXT_OUT := 0;
  568.           STACKED_XOFFS := 0;
  569.           STACKED_XONS := 0;
  570.           TRANSMIT_BUFFER.NEXT_IN := 0;
  571.           TRANSMIT_BUFFER.NEXT_OUT := 0;
  572.           XOFF_RECEIVED := false;
  573.           XOFF_SENT := false
  574.         end   (* SET_VARIABLES *);
  575.       
  576.         (* eject *)
  577.       begin (* INIT_COM_MGR *)
  578.         if not INITIALIZED then
  579.           begin (* nondegenerate case *)
  580.             INIT_IOERROR;
  581.             SET_VARIABLES;
  582.         
  583.             case COM_CONFIGURATION.THIS_PORT of
  584.               COM1: begin (* COM1 *)
  585.                       DATA_REGISTER := $3F8;
  586.                       IRQ_USED := 4
  587.                     end   (* COM1 *);
  588.               COM2: begin (* COM2 *)
  589.                       DATA_REGISTER := $2F8;
  590.                       IRQ_USED := 3
  591.                     end   (* COM2 *)
  592.                else ERROR_EXIT('Ports beyond COM2 not implemented.')
  593.               end  (* case on THIS_PORT *);
  594.               
  595.             INT_ENABLE_REGISTER   := succ(DATA_REGISTER);
  596.             INT_ID_REGISTER       := succ(INT_ENABLE_REGISTER);
  597.             LINE_CTL_REGISTER     := succ(INT_ID_REGISTER);
  598.             MODEM_CTL_REGISTER    := succ(LINE_CTL_REGISTER);
  599.             LINE_STATUS_REGISTER  := succ(MODEM_CTL_REGISTER);
  600.             MODEM_STATUS_REGISTER := succ(LINE_STATUS_REGISTER);
  601.             
  602.             I8259_BIT := 1 shl IRQ_USED;
  603.             
  604.             OLD_IER := port[INT_ENABLE_REGISTER];
  605.             port[INT_ENABLE_REGISTER] := 0;
  606.             if port[INT_ENABLE_REGISTER] <> 0 then
  607.               ERROR_EXIT('Port hardware not responding.');
  608.             
  609.             DISABLE_INTERRUPTS;
  610.               OLD_I8259_MASK := port[PIC_IE_PORT];
  611.               port[PIC_IE_PORT] := OLD_I8259_MASK or I8259_BIT;
  612.             ENABLE_INTERRUPTS;
  613.             
  614.             GetIntVec(IRQ_BASE+IRQ_USED, OLD_IRQ_INT_PROC);
  615.             SetIntVec(IRQ_BASE+IRQ_USED, @COM_INT_HANDLER);
  616.             INITIALIZED := true;
  617.             port[LINE_CTL_REGISTER] := LCR_PARITY_BITS[NO_PARITY];
  618.  
  619.             DISABLE_INTERRUPTS;
  620.               OLD_MCR := port[MODEM_CTL_REGISTER];
  621.               port[MODEM_CTL_REGISTER] := MCR_OUT2 or MCR_RTS;
  622.             ENABLE_INTERRUPTS;
  623.  
  624.             port[INT_ENABLE_REGISTER] := IER_ENB_RCV_CHAR;
  625.  
  626.             DISABLE_INTERRUPTS;
  627.               port[PIC_IE_PORT] := port[PIC_IE_PORT] and not I8259_BIT;
  628.             ENABLE_INTERRUPTS;
  629.  
  630.             SET_SPEED_AND_PARITY
  631.           end   (* nondegenerate case *)
  632.       end   (* INIT_COM_MGR *);
  633.       (* eject *)
  634.     procedure TERM_COM_MGR;
  635.       (* Necessary if only to flush buffers before they go away *)
  636.  
  637.       begin (* TERM_COM_MGR *)
  638.         if INITIALIZED then
  639.           begin (* nondegenerate case *)
  640.             while COM_OUTPUT_WAITING > 0 do
  641.               begin (* drain *)
  642.                 if XOFF_RECEIVED then
  643.                   inc(WAITING_FOR_XON)
  644.                 else
  645.                   inc(WAITING_FOR_DSR);
  646.                 write('Draining buffer [', COM_OUTPUT_WAITING, ']   ');
  647.                 GotoXY(1, WhereY);
  648.               end   (* drain *);
  649.             ClrEol;
  650.             port[MODEM_CTL_REGISTER] := OLD_MCR;
  651.             port[INT_ENABLE_REGISTER] := OLD_IER;
  652.  
  653.             DISABLE_INTERRUPTS;
  654.               port[PIC_IE_PORT] := port[PIC_IE_PORT] and not I8259_BIT or
  655.                                    OLD_I8259_MASK and I8259_BIT;
  656.             ENABLE_INTERRUPTS;
  657.  
  658.             SetIntVec(IRQ_BASE+IRQ_USED, OLD_IRQ_INT_PROC);
  659.             INITIALIZED := false
  660.           end   (* nondegenerate case *)
  661.       end   (* TERM_COM_MGR *);
  662.  
  663.     begin (* COM_MGR *)
  664.       DROPPED := 0;
  665.       EVER_ACTIVE := false;
  666.       INITIALIZED := false;
  667.       INPUT_ZENITH := 0;
  668.       SAVE_EXIT := ExitProc;
  669.       WAITING_FOR_DSR := 0;
  670.       WAITING_FOR_XON := 0;
  671.       
  672.       ExitProc := @EPILOGUE
  673.     end   (* COM_MGR *).
  674.     
  675. END
  676. T-------T-------T-------T-------T-------T-------T-------T-------T-------T-------T
  677. $cursor=27342,20;$tag=23770,16;$last=24293,24;a=7948,17;z=26098,12;
  678. FTL0R79P5.F0B7
  679.  
  680.