home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / utils / ziptv20.zip / INTRCOMM.INC next >
Text File  |  1989-08-29  |  15KB  |  571 lines

  1.  
  2. (*
  3.  * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
  4.  *
  5.  * This is a component of the ProDoor System.
  6.  * Do not distribute modified versions without my permission.
  7.  * Do not remove or alter this notice or any other copyright notice.
  8.  * If you use this in your own program you must distribute source code.
  9.  * Do not use any of this in a commercial product.
  10.  *
  11.  *)
  12.  
  13. (*
  14.  * intrcomm.inc - interrupt-based communication library for PCB ProDOOR
  15.  *
  16.  *)
  17.  
  18. {$R-,S-}
  19.  
  20.  
  21. (* ------------------------------------------------------------ *)
  22. procedure control_k;
  23.    (* process cancel-output command *)
  24. begin
  25.    txque.next_in := 1;
  26.    txque.next_out := 1;          (* throw away pending output *)
  27.    txque.count := 0;             
  28.  
  29.    linenum := 2000;              (* cancel current function *)
  30.    pending_keys[0] := #0;
  31. end;
  32.  
  33.  
  34. (* ------------------------------------------------------------ *)
  35. procedure INTR_service_MSR;
  36.   (* modem status change interrupt *)
  37. var
  38.    c: byte;
  39. begin
  40.    c := port[ port_base+MSR ];
  41.    io_delay;
  42. end;
  43.  
  44.  
  45. (* ------------------------------------------------------------ *)
  46. procedure INTR_service_LSR;
  47.    (* line status change interrupt *)
  48. var
  49.    c: byte;
  50. begin
  51.    c := port[ port_base+LSR ];
  52.    io_delay;
  53. end;
  54.  
  55.  
  56. (* ------------------------------------------------------------ *)
  57. procedure INTR_service_transmit;
  58.    (* low-level interrupt service for transmit, call only when transmit
  59.       holding register is empty *)
  60. var
  61.    c:       char;
  62. const
  63.    recur:  boolean = false;
  64.  
  65. begin
  66.  
  67. (* prevent recursion fb/bg *)
  68.    if recur then exit;
  69.    recur := true;
  70.  
  71. (* drop out if transmitter is busy *)
  72.    if (port[ port_base+LSR ] and LSR_THRE) = 0 then
  73.    begin
  74.       io_delay;
  75.       recur := false;
  76.       exit;
  77.    end;
  78.  
  79.    io_delay;
  80.  
  81.    (* stop transmitting when queue is empty, or XOFF is active
  82.       or it is not CLEAR-to-send to modem *)
  83.  
  84.    xmit_active := (txque.count <> 0) and (not xoff_active) and
  85.                   (disable_CTS_check or ((port[port_base+MSR] and MSR_CTS)>0));
  86.  
  87.    io_delay;
  88.  
  89.    (* start next byte transmitting *)
  90.    if xmit_active then
  91.    begin
  92.       c := txque.data[txque.next_out];
  93.       if txque.next_out < sizeof(txque.data) then
  94.          inc(txque.next_out)
  95.       else
  96.          txque.next_out := 1;
  97.       dec(txque.count);
  98.  
  99.       port[ port_base+THR ] := ord(c); io_delay;
  100.    end;
  101.  
  102.    recur := false;
  103. end;
  104.  
  105.  
  106. (* ------------------------------------------------------------ *)
  107. procedure INTR_service_receive;
  108.    (* low-level interrupt service for receive data,
  109.       call only when receive data is ready *)
  110. var
  111.    c: char;
  112.    o: byte;
  113.  
  114. begin
  115.    o := port[ port_base+LSR ];
  116.    io_delay;
  117.  
  118. (***
  119.    if (o and LSR_OERR) <> 0 then inc(LOERR_count);
  120.    if (o and LSR_PERR) <> 0 then inc(LPERR_count);
  121.    if (o and LSR_FERR) <> 0 then inc(LFERR_count);
  122.    if (o and LSR_BREAK)<> 0 then inc(LBREAK_count);
  123. ***)
  124.  
  125.    if (o and LSR_DAV) = 0 then
  126.       exit;
  127.  
  128.    c := chr( port[ port_base+RBR ] ); io_delay;
  129.  
  130.    if XOFF_active then           (* XOFF cancelled by any character *)
  131.       cancel_xoff
  132.    else
  133.  
  134.    if c = XOFF_char then         (* process XOFF/XON flow control *)
  135.       XOFF_active := true
  136.    else
  137.  
  138.    if (c = ^K) then              (* process cancel-output command *)
  139.       control_k
  140.    else
  141.  
  142.    if c = carrier_lost then      (* ignore this special character! *)
  143.    begin
  144.       {do nothing}
  145.    end
  146.    else
  147.  
  148.    if rxque.count < sizeof(rxque.data) then
  149.    begin
  150.       inc(rxque.count);
  151.       rxque.data[rxque.next_in] := c;
  152.       if rxque.next_in < sizeof(rxque.data) then
  153.          inc(rxque.next_in)
  154.       else
  155.          rxque.next_in := 1;
  156.    end;
  157. end;
  158.  
  159.  
  160. (* ------------------------------------------------------------ *)
  161. procedure INTR_poll_transmit;
  162.    (* recover from CTS or XOF handshake when needed *)
  163. begin
  164.    {no action if nothing to transmit}
  165.    if (txque.count = 0) or (com_chan < 0){local} then
  166.       exit;
  167.  
  168.    {check for XON if output suspended by XOFF}
  169.    INTR_service_receive;
  170.    INTR_service_transmit;
  171. end;
  172.  
  173.  
  174. (* ------------------------------------------------------------ *)
  175. procedure cancel_xoff;
  176. begin
  177.    XOFF_active := false;
  178.    INTR_poll_transmit;
  179. end;
  180.  
  181.  
  182. (* ------------------------------------------------------------ *)
  183. procedure INTR_check_interrupts;
  184.    (* check for and process any pending 8250 interrupts.
  185.       can be called from TPAS *)
  186. var
  187.    status:  integer;
  188.  
  189. begin
  190.  
  191. (* get the interrupt identification register *)
  192.    status := port[ port_base+IIR ]; io_delay;
  193.  
  194. (* repeatedly service interrupts until no more services possible *)
  195.    while (status and IIR_PENDING) = 0 do
  196.    begin
  197.       disable_int;
  198.  
  199.       case (status and IIR_MASK) of
  200.          IIR_MSR:   (* modem status change interrupt *)
  201.             INTR_service_MSR;
  202.  
  203.          IIR_THRE:  (* transmit holding register empty interrupt *)
  204.             INTR_service_transmit;
  205.  
  206.          IIR_DAV:   (* data available interrupt *)
  207.             INTR_service_receive;
  208.  
  209.          IIR_LSR:   (* line status change interrupt *)
  210.             INTR_service_MSR;
  211.       end;
  212.  
  213.       enable_int;
  214.  
  215.   (* get the interrupt identification register again *)
  216.       status := port[ port_base+IIR ];
  217.       io_delay;
  218.    end;
  219.  
  220. end;
  221.  
  222.  
  223. (* ------------------------------------------------------------ *)
  224. procedure INTR_interrupt_handler(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP: word);
  225. interrupt;
  226.    (* low-level interrupt service routine.  this procedure processes
  227.       all receive-ready and transmit-ready interrupts from the 8250 chip.
  228.       DO NOT call this proc from TPAS *)
  229.  
  230. begin
  231.  
  232. (* service interrupts until no more services possible *)
  233.    INTR_check_interrupts;
  234.  
  235. (* acknowledge the interrupt and return to foreground operation *)
  236.    port[ $20 ] := $20;   {non-specific EOI} io_delay;
  237.  
  238. end;
  239.  
  240.  
  241. (* ------------------------------------------------------------ *)
  242. function INTR_receive_ready: boolean;
  243.    (* see if any receive data is ready on the active com port *)
  244. begin
  245.    INTR_poll_transmit;
  246.    INTR_receive_ready := rxque.count > 0;
  247. end;
  248.  
  249.  
  250. (* ------------------------------------------------------------ *)
  251. procedure INTR_flush_com;
  252.    (* wait for all pending transmit data to be sent *)
  253. begin
  254.    enable_int;
  255.    while txque.count > 0 do
  256.    begin
  257.       INTR_poll_transmit;
  258.       give_up_time;             (* give up extra time *)
  259.    end;
  260. end;
  261.  
  262.  
  263. (* ------------------------------------------------------------ *)
  264. procedure verify_txque_space;
  265.    (* wait until there is enough space in the queue for this message *)
  266.    (* or until flow control is released *)
  267. begin
  268.    while txque.count > queue_low_water do
  269.    begin
  270.       INTR_poll_transmit;
  271.       give_up_time;             (* give up extra time *)
  272.    end;
  273. end;
  274.  
  275.  
  276. (* ------------------------------------------------------------ *)
  277. procedure INTR_lower_dtr;
  278.    (* lower DTR to inhibit modem answering *)
  279. var
  280.    o: byte;
  281. begin
  282.    if {local or} (com_chan < 0) then exit;
  283.  
  284.    o := port [ port_base+MCR ];                 io_delay;
  285.    port[ port_base+MCR ] := o and not MCR_DTR;  io_delay;
  286. end;
  287.  
  288.  
  289. (* ------------------------------------------------------------ *)
  290. procedure INTR_raise_dtr;
  291.    (* raise DTR to allow modem answering - not supported by BIOS *)
  292. var
  293.    o: byte;
  294. begin
  295.    if {local or} (com_chan < 0) then exit;
  296.  
  297.    o := port [ port_base+MCR ];                       io_delay;
  298.    port[ port_base+MCR ] := o or (MCR_DTR+MCR_RTS);   io_delay;
  299. end;
  300.  
  301.  
  302. (* ------------------------------------------------------------ *)
  303. procedure INTR_select_port(chan: integer);
  304.    (* lookup the port address for the specified com channel *)
  305. begin
  306.    com_current_chan := chan;
  307.    xmit_active := false;
  308.    XOFF_active := false;
  309.  
  310.    if (chan >= 0) and (chan <= 2) then
  311.    begin
  312.       port_base := atow(GetEnv('COMBASE'));
  313.       if port_base = 0 then
  314.          port_base := COM_BASE_TABLE[chan];
  315.       bios_bastab[chan] := port_base;
  316.  
  317.       port_irq := atow(GetEnv('COMIRQ'));
  318.       if port_irq = 0 then
  319.          port_irq := COM_IRQ_TABLE[chan];
  320.  
  321.       port_intr := IRQ_VECT_TABLE[port_irq];
  322.       intr_mask := IRQ_MASK_TABLE[port_irq];
  323.    end;
  324.  
  325. (**
  326. writeln('[chan=',chan,' port base=',port_base,' intr=',port_intr,' mask=',intr_mask,']');
  327. **)
  328.  
  329. (* initialize the receive and transmit queues *)
  330.    rxque.next_in := 1;
  331.    rxque.next_out := 1;
  332.    rxque.count := 0;
  333.  
  334.    txque.next_in := 1;
  335.    txque.next_out := 1;
  336.    txque.count := 0;
  337.  
  338.    INTR_raise_dtr;
  339. end;
  340.  
  341.  
  342. (* ------------------------------------------------------------ *)
  343. procedure INTR_init_com(chan: integer);
  344.    (* initialize communication handlers for operation with the specified
  345.       com port number.  must be called before any other services here *)
  346. var
  347.    o: byte;
  348. begin
  349.  
  350. (* initialize port numbers, receive and transmit queues *)
  351.    INTR_select_port(chan);
  352.  
  353.    if chan < 0 then exit;
  354.  
  355. (* save the old interrupt handler's vector *)
  356.    GetIntVec(port_intr, old_vector);
  357. {writeln('got old');}
  358.  
  359. (* install a vector to the new handler *)
  360.    SetIntVec(port_intr,@INTR_interrupt_handler);
  361. {writeln('new set');}
  362.  
  363. (* save original 8250 registers *)
  364.    disable_int;
  365.    prev_LCR := port[ port_base+LCR ];              io_delay;
  366.    prev_MCR := port[ port_base+MCR ];              io_delay;
  367.    prev_IER := port[ port_base+IER ];              io_delay;
  368.    prev_ICTL  := port[ ICTL ];                     io_delay;
  369.  
  370. (* clear divisor latch if needed *)
  371.    port[ port_base+LCR ] := prev_LCR and not LCR_ABDL;
  372.    io_delay;
  373.  
  374. (* initialize the 8250 for interrupts *)
  375.    o := port[ port_base+MCR ];                     io_delay;
  376.    port[ port_base+MCR ] := o or MCR_OUT2;         io_delay;
  377.    port[ port_base+IER ] := IER_DAV+IER_THRE;      io_delay;
  378.  
  379. (* enable the interrupt through the interrupt controller *)
  380.    o := port[ ICTL ];                              io_delay;
  381.    port[ ICTL ] := o and (not intr_mask);          io_delay;
  382.    enable_int;
  383.  
  384. (* initialize the receive queues in case of an initial garbage byte *)
  385.    disable_int;
  386.    rxque.next_in := 1;
  387.    rxque.next_out := 1;
  388.    rxque.count := 0;
  389.    enable_int;
  390.  
  391. {writeln('init done');}
  392.  
  393. end;
  394.  
  395.  
  396. (* ------------------------------------------------------------ *)
  397. procedure INTR_uninit_com;
  398.    (* remove interrupt handlers for the com port
  399.       must be called before exit to system *)
  400. var
  401.    o: byte;
  402. begin
  403.    if (port_base = -1) or (old_vector = nil) then
  404.       exit;
  405.  
  406. (* wait for the pending data to flush from the queue *)
  407.    INTR_flush_com;
  408.  
  409. (* attach the old handler to the interrupt vector *)
  410.    disable_int;
  411.  
  412.    SetIntVec(port_intr, old_vector);
  413.  
  414.    port[ port_base+LCR ] := prev_LCR;     io_delay;
  415.    port[ port_base+MCR ] := prev_MCR;     io_delay;
  416.    port[ port_base+IER ] := prev_IER;     io_delay;
  417.    o := port[ ICTL ];                     io_delay;
  418.    port[ ICTL ] := (o and not intr_mask) or (prev_ICTL and intr_mask);
  419.    io_delay;
  420.  
  421.    enable_int;
  422.  
  423. (***
  424. writeln('prev: LCR=',itoh(prev_LCR),
  425.              ' MCR=',itoh(prev_MCR),
  426.              ' IER=',itoh(prev_IER),
  427.              ' ICTL=',itoh(prev_ICTL));
  428. ****)
  429. (***
  430. writeln(' now: LCR=',itoh(port[ port_base+LCR ]),
  431.              ' MCR=',itoh(port[ port_base+MCR ]),
  432.              ' IER=',itoh(port[ port_base+IER ]),
  433.              ' ICTL=',itoh(port[ ICTL ]));
  434. ****)
  435. (***
  436. writeln('intr_mask=',itoh(intr_mask),
  437.              ' vector=',itoh(seg(old_vector)),':',itoh(ofs(old_vector)));
  438. ***)
  439.  
  440.    old_vector := nil;
  441. end;
  442.  
  443.  
  444. (* ------------------------------------------------------------ *)
  445. procedure INTR_set_baud_rate(speed: word);
  446. var
  447.    divisor: word;
  448.    o: byte;
  449. begin
  450.    if com_chan < 0 then exit;
  451.    {INTR_}flush_com;
  452.  
  453.    divisor := 115200 div speed;
  454.    disable_int;
  455.  
  456. (* enable address divisor latch *)
  457.    o := port[port_base+LCR];              io_delay;
  458.    port [port_base+LCR] := o or LCR_ABDL; io_delay;
  459.  
  460. (* set the divisor *)
  461.    portw[port_base+THR] := divisor;       io_delay;
  462.  
  463. (* set 8 bits, 1 stop, no parity, no break, disable divisor latch *)
  464.    prev_LCR := LCR_8BITS   or LCR_1STOP   or
  465.                LCR_NPARITY or LCR_NOBREAK;
  466.  
  467.    port[ port_base+LCR ] := prev_LCR;     io_delay;
  468.  
  469.    enable_int;
  470.  
  471. (****
  472. if setdebug then
  473. writeln(dbfd,'set baud: LCR=',itoh(port[ port_base+LCR ]),
  474.              ' MCR=',itoh(port[ port_base+MCR ]),
  475.              ' IER=',itoh(port[ port_base+IER ]),
  476.              ' ICTL=',itoh(port[ ICTL ]),
  477.              ' div=',divisor,
  478.              ' spd=',speed);
  479. ****)
  480. end;
  481.  
  482.  
  483. (* ------------------------------------------------------------ *)
  484. function INTR_receive_data:  char;
  485.    (* wait for and return 1 character from the active com port *)
  486.    (* returns carrier_lost if carrier is not present *)
  487. var
  488.    c: char;
  489.  
  490. begin
  491.    if com_chan < 0 then exit;
  492.  
  493.    repeat
  494.       io_delay;
  495.  
  496.       if INTR_receive_ready then
  497.       begin
  498.          disable_int;
  499.  
  500.          {deque from rxque}
  501.          c := rxque.data[rxque.next_out];
  502.          if rxque.next_out < sizeof(rxque.data) then
  503.             inc(rxque.next_out)
  504.          else
  505.             rxque.next_out := 1;
  506.          dec(rxque.count);
  507.  
  508.          enable_int;
  509.  
  510.          {strip parity in 7,E mode}
  511.          if even_parity then
  512.             c := chr( ord(c) and $7f );
  513.  
  514.          INTR_receive_data := c;
  515.          exit;
  516.       end;
  517.  
  518.       {give up time while waiting}
  519.       give_up_time;
  520.  
  521.       io_delay;
  522.    until not ((port[port_base+MSR] and MSR_RLSD)<>0);
  523.  
  524.    {carrier not present}
  525.    cancel_xoff;
  526.    INTR_receive_data := carrier_lost;
  527. end;
  528.  
  529.  
  530. (* ------------------------------------------------------------ *)
  531. procedure INTR_transmit_data(s:    longstring);
  532.    (* transmits a string of characters to the specified com port;
  533.       does not transmit when carrier is not present *)
  534. var
  535.    i:    integer;
  536.  
  537. begin
  538.    if com_chan < 0 then exit;
  539.  
  540. (* wait until there is enough space in the queue for this message *)
  541. (* or until flow control is released *)
  542.  
  543.    if txque.count > queue_high_water then
  544.       verify_txque_space;
  545.  
  546.  
  547. (* enque the string to be transmitted *)
  548.    for i := 1 to length(s) do
  549.    begin
  550.       disable_int;
  551.  
  552.       inc(txque.count);
  553.       txque.data[txque.next_in] := s[i];
  554.       if txque.next_in < sizeof(txque.data) then
  555.          inc(txque.next_in)
  556.       else
  557.          txque.next_in := 1;
  558.  
  559.       enable_int;
  560.    end;
  561.  
  562.  
  563. (* force an initial interrupt to get things rolling (in case there are
  564.    no more pending transmit-ready interrupts *)
  565.  
  566.    INTR_poll_transmit;
  567. end;
  568.  
  569. { $R+,S+}
  570.  
  571.