home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / turbopas / io5150.pas < prev    next >
Pascal/Delphi Source File  |  1994-03-05  |  8KB  |  265 lines

  1. {
  2.      Device driver for IBM 5150 PC's serial port (AUX).
  3.      By J. Eric Roskos
  4.      Public Domain, may not be sold for profit.
  5.  
  6.      This driver is initialized by calling AuxInit, and MUST be
  7.      deactivated by calling AuxOff before the program exits (or a sub-
  8.      sequent serial interrupt will cause the system to hang).
  9.      It uses serial interrupts, and thus works considerably better than
  10.      the standard AUX device driver supplied with the IBM PC.
  11.  
  12.      Once AuxInit is called, you can read and write to the predefined
  13.      Turbo Pascal file "Aux" to access the serial port.  The procedure
  14.      AuxSt returns True if a character is presently available at the
  15.      serial port, equivalent to the KeyPressed procedure for the console.
  16.      You don't have to (and shouldn't) call any of the other procedures
  17.      in this file other than AuxInit, AuxOff, and AuxSt.
  18.  
  19.      Presently, only input is done via interrupts; output is via
  20.      conventional polling, and a write to the serial port will cause
  21.      a busy-wait until any previous character is completely transmitted.
  22.      The input buffer's size is defined by the constant BUFSIZ below,
  23.      which must be a power of 2 (or the wrap-around algorithm won't work).
  24.      The serial port is presently initialized to 1200 baud; if you want
  25.      to set it via the MODE command of DOS instead, you can comment out
  26.      the statements indicated below.
  27.  
  28.      If the input buffer becomes full, a ^S is sent to the remote machine;
  29.      a ^Q is then sent when it empties sufficiently to resume.  This
  30.      is indeed used at 1200 baud if a number of escape sequences are
  31.      sent in succession.
  32.  
  33.      Only the top 24 lines of the screen are used, in order to comply with
  34.      most standard terminals.  However, for this to work you have to
  35.      compile the program with Turbo Pascal version 2.0 or later, and use
  36.      an IBM PC version of the compiler (not the generic MS-DOS version).
  37. }
  38.  
  39. (*
  40.  
  41. unit IO5150;
  42. interface
  43.      procedure AuxInit;     { Initialize the unit; must be called at start }
  44.      procedure AuxOff;      { Terminates the unit; must be called at exit  }
  45.      procedure AuxSt;       { Returns True if char immed. avail from Aux   }
  46.  
  47. implementation
  48.  
  49. *)
  50.  
  51. {$R-}
  52. {$C-}
  53.  
  54. const
  55.      BUFSIZ = 256;    { Buffer size -- must be a power of 2 }
  56.  
  57.      { Port addresses for COM1: you must change these to use COM2 }
  58.  
  59.      RX     = 1016;   { Receiver Buffer Register            }
  60.      TX     = 1016;   { Transmitter Buffer Register         }
  61.      IE     = 1017;   { Interrupt Enable Register           }
  62.      II     = 1018;   { Interrupt Identification Register   }
  63.      LC     = 1019;   { Line Control Register               }
  64.      MC     = 1020;   { Modem Control Register              }
  65.      LS     = 1021;   { Line Status Register                }
  66.      MS     = 1022;   { Modem Status Register               }
  67.      DLL    = 1016;   { Divisor Latch, Low Order Byte       }
  68.      DLH    = 1017;   { Divisor Latch, High Order Byte      }
  69.  
  70.  
  71. type
  72.      { The input buffer structure }
  73.  
  74.      buffer=record
  75.           buf: packed array[0..BUFSIZ] of char; { The character buffer       }
  76.           ip,op,cnt: integer; { Input pointer, output pointer, char count    }
  77.      end;
  78.  
  79.      { Turbo Pascal's DOS/BIOS call parameter block }
  80.  
  81.      regpack=record
  82.           ax,bx,cx,dx,bp,si,di,ds,es,flags: integer;
  83.      end;
  84.  
  85.  
  86. var
  87.      Buf:    buffer;                       { The input buffer                }
  88.      AuxOset, AuxSeg: integer;             { Saved orig. serial int vector   }
  89.      SvOset: integer absolute $0000:$0030; { The serial interrupt vector     }
  90.      SvSeg:  integer absolute $0000:$0032; {  "    "        "       "        }
  91.      SavDs:  integer absolute $0000:$002E; { Program's DS addr is saved here }
  92.      Run:    boolean;                      { True while emulator is to run   }
  93.      XedOff: boolean;                      { True if IntSer Xed off remote   }
  94.      c:      char;                         { Current input character         }
  95.  
  96.  
  97. {
  98.      Interrupt service routine.
  99.  
  100.      This routine saves all registers, and then sets up its Data Segment
  101.      from the value saved in 0:$2E by AuxInit (since, contrary to what the
  102.      Turbo manual tells you, the DS may be changed by the ROM BIOS and thus
  103.      may be incorrect on entry to the interrupt routine).  It then gets the
  104.      input character (if the interrupt was due to a received character) and
  105.      stores it in the buffer, handling XOFFs if necessary.
  106. }
  107.  
  108. procedure IntSer;
  109. var
  110.      i: integer;
  111.  
  112. begin
  113.      { Save all registers and set up our Data Segment }
  114.  
  115.      inline($50/$53/$51/$52/$57/$56/$06/$1e);
  116.      inline($06/$50/$B8/$00/$00/$8e/$C0/$26/$8E/$1E/$2E/$00/$58/$07);
  117.  
  118.      { Turn interrupts back on }
  119.  
  120.      inline($FB);
  121.      Port[$20] := $20;
  122.  
  123.      { Process the interrupt }
  124.  
  125.      case Port[II] of
  126.      0:   i := Port[MS];                           { Modem Status Intr }
  127.      1:   ;                                        { No Interrupt      }
  128.      2:   writeln('Error: THRE interrupt');        { Transmit Intr     }
  129.      4:                                            { Receive Intr      }
  130.           begin
  131.                Buf.buf[Buf.ip] := chr(Port[RX]);
  132.                Buf.ip := (Buf.ip + 1) and (BUFSIZ-1);
  133.                inline($FA); { CLI }
  134.                Buf.cnt := Buf.cnt + 1;
  135.                inline($FB); { STI }
  136.                if (Buf.cnt >= BUFSIZ-25) and not Xedoff then
  137.                begin
  138.                     Xedoff := true;
  139.                     Port[TX] := ord(^S);
  140.                end;
  141.           end;
  142.      6:   i := Port[LS];                           { Line Status Intr  }
  143.      end {case};
  144.  
  145.      { Restore saved registers and do IRET }
  146.  
  147.      inline($1F/$07/$5E/$5F/$5A/$59/$5B/$58/$8B/$E5/$5D/$CF);
  148. end;
  149.  
  150.  
  151. {
  152.      AUX port status.  Returns True if there is a character available
  153.      for reading, same as KeyPressed for console.
  154. }
  155.  
  156. function AuxSt: boolean;
  157. begin
  158.      AuxSt := (Buf.cnt > 0);
  159. end;
  160.  
  161.  
  162. {
  163.      AUX input routine.  Not called by the user: called by the Turbo
  164.      runtime system when you do a read from the Aux file.
  165. }
  166.  
  167. function AuxIn: char;
  168. begin
  169.      while Buf.cnt = 0 do ;
  170.  
  171.      AuxIn := Buf.buf[Buf.op];
  172.      Buf.op := (Buf.op + 1) and (BUFSIZ-1);
  173.  
  174.      inline($fa); { CLI }
  175.      Buf.cnt := Buf.cnt - 1;
  176.      inline($fb); { STI }
  177.  
  178.      if (Buf.cnt < 25) and Xedoff then
  179.      begin
  180.           Xedoff := false;
  181.           Port[TX] := ord(^Q);
  182.      end;
  183. end;
  184.  
  185.  
  186. {
  187.      AUX port output routine.  Not called by the user: called by the
  188.      Turbo runtime system when you do a write to the Aux file.
  189. }
  190.  
  191. procedure AuxOut(c:char);
  192. begin
  193.      while (Port[LS] and $20) = 0 do ; { Busy Wait }
  194.      Port[TX] := ord(c);
  195. end;
  196.  
  197.  
  198. {
  199.      AUX device driver initialization.  You must call this before accessing
  200.      the Aux file if you want to use these device drivers.
  201. }
  202.  
  203. procedure AuxInit;
  204. begin
  205.      inline($fa); { CLI }
  206.  
  207.      { Initialize interrupt vector and 8259A }
  208.  
  209.      SavDs     := Dseg;
  210.      AuxOset   := SvOset;
  211.      AuxSeg    := SvSeg;
  212.      SvOset    := ofs(IntSer);
  213.      SvSeg     := Cseg;
  214.      Port[$21] := $ac;   { enable ints 8, 9, C, E }
  215.  
  216.      { Initialize 8250 UART }
  217.      { Comment the starred lines out if you want to use MODE to init AUX: }
  218.  
  219.      Port[MC]  := $00;  { *** } { Negate DTR and RTS             }
  220.      Port[LC]  := $80;  { *** } { Set baud rate to 1200 baud     }
  221.      Port[DLL] := $60;  { *** } { Use $80 here for 300 baud      }
  222.      Port[DLH] := $00;  { *** } { Use $01 here for 300 baud      }
  223.      Port[LC]  := $2a;  { *** } { Set stick parity 7 data 1 stop }
  224.      Port[IE]  := $05;          { Turn on interrupts             }
  225.      Port[MC]  := $0b;          { Turn on int gate, DTR, and RTS }
  226.  
  227.      { Initialize buffer }
  228.  
  229.      Buf.ip  := 0;
  230.      Buf.op  := 0;
  231.      Buf.cnt := 0;
  232.      Xedoff  := false;
  233.  
  234.      { Initialize I/O system }
  235.  
  236.      AuxInPtr  := ofs(AuxIn);
  237.      AuxOutPtr := ofs(AuxOut);
  238.  
  239.      inline($fb); { STI }
  240. end;
  241.  
  242.  
  243. {
  244.      Reset the AUX port to non-interrupt mode.  You MUST call this routine
  245.      before you exit.
  246. }
  247.  
  248. procedure AuxOff;
  249. begin
  250.      inline($fa); { CLI }
  251.  
  252.      SvOset := AuxOset;{ Restore serial int vector to its original value  }
  253.      SvSeg  := AuxSeg;
  254.  
  255.      Port[IE]  := $00; { Turn off UART's interrupt enables                }
  256.      Port[MC]  := $00; { Negate RTS and CTS, and turn off interrupt gate  }
  257.      Port[$21] := $BC; { Set 8259 PIC back to disabling serial interrupts }
  258.      Port[$20] := $20; { Send EOI to PIC                                  }
  259.  
  260.      inline($fb); { STI }
  261. end;
  262.  
  263.  
  264.  
  265.