home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / COMM_TP4.ZIP / COMM_TP4.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1989-02-02  |  55.2 KB  |  506 lines

  1. PROGRAM Comm_TP4;                                                               {-This is the comment column }
  2.                                                                                 { which will be used to tell }
  3. { Written by:  Kevin R. Bulgrien           Version 1.01 completed 01/31/89    } { what the program is doing  }
  4. {                                                                             } { where its not self-evident }
  5. { Contact at:  LeTourneau College          LeTourneau College BBS             } {                            }
  6. {              Microcomputer Services      2400/1200/300 Baud                 } { Yes, it is wider than 80   }
  7. {              P.O. Box 7001               (214) 237-2742                     } { columns so you will need   }
  8. {              Longview, TX  75607                                            } { to use compressed print to }
  9. {                                                                             } { print it out.              }
  10. { This program works with Turbo Pascal 4.0 and 5.0.  See Comm_TP4.DOC for the } {                            }
  11. { instructions.  Comm_TP3, by the same author, works under Turbo Pascal 3.0,  } { Don't complain too much as }
  12. { and Comm_TC2 works with Turbo C.                                            } { code documentation is far  }
  13. {                                                                             } { easier to read when it is  }
  14. { Version History                                                             } { done like this...          }
  15. {                                                                             } {                            }
  16. {  1.00, 11/88 Original code uploaded to GEnie's Borland Roundtable           } {                            }
  17. {  1.01, 02/89 Bug found & fixed in Procedure RemoveInt. Port [$21] was ANDed } {                            }
  18. {              with $08 or $10 instead of ORed with those values.             } {                            }
  19.                                                                                 {                            }
  20. USES DOS, CRT;                                                                  {                            }
  21.                                                                                 {                            }
  22. CONST                                                                           {                            }
  23.   MaxSize = 511;                                                                {-Maximum input buffer size  }
  24.                                                                                 {                            }
  25. TYPE                                                                            {-8250 Communications Chip   }
  26.   INS8250 = RECORD                                                              { -------------------------- }
  27.               THR : INTEGER;                                                    { Transmit Holding Register  }
  28.               RHR : INTEGER;                                                    { Receive Holding Register   }
  29.               DLL : INTEGER;                                                    { Divisor Latch Register LSB }
  30.               IER : INTEGER;                                                    { Interrupt Enable Register  }
  31.               DLM : INTEGER;                                                    { Divisor Latch Register MSB }
  32.               IIR : INTEGER;                                                    { Interrupt ID Register      }
  33.               LCR : INTEGER;                                                    { Line Control Register      }
  34.               MCR : INTEGER;                                                    { Modem Control Register     }
  35.               LSR : INTEGER;                                                    { Line Status Register       }
  36.               MSR : INTEGER;                                                    { Modem Status Register      }
  37.             END;                                                                {                            }
  38.                                                                                 {                            }
  39.   ComSettingsRecord = RECORD                                                    {-Used to hold the current   }
  40.                        Baud : BYTE;                                             { settings of COM1 or COM2   }
  41.                        Parity : BYTE;                                           {                            }
  42.                        Stop : BYTE;                                             {                            }
  43.                        Bits : BYTE;                                             {                            }
  44.                      END;                                                       {                            }
  45.                                                                                 {                            }
  46.   ComSettingsType = ARRAY [1..2] OF ComSettingsRecord;                          {-COM1 & COM2 Settings       }
  47.                                                                                 {                            }
  48.   BaudType = (B110,B150,B300,B600,B1200,B2400,B4800,B9600,B19200,B38400);       {-Baud rates supported       }
  49.                                                                                 {                            }
  50.   ParityType = (None, Odd, Null, Even, MarkOff, Mark, SpaceOff, Space);         {-Parity types supported     }
  51.                                                                                 {                            }
  52.   ComBuffersType = ARRAY [1..2, 0..MaxSize] OF BYTE;                            {-The input buffers for COM1 }
  53.                                                                                 { and COM2                   }
  54.   OutBuffer = STRING [255];                                                     {                            }
  55.                                                                                 {                            }
  56. CONST                                                                           {                            }
  57.   RS232 : ARRAY [1..2] OF INS8250 = ( ( THR:$3F8; RHR:$3F8; DLL:$3F8;           {-COM1 addresses of the 8250 }
  58.                                         IER:$3F9; DLM:$3F9; IIR:$3FA;           { registers so that they may }
  59.                                         LCR:$3FB; MCR:$3FC; LSR:$3FD;           { be accessed by name.       }
  60.                                         MSR:$3FE ),                             {                            }
  61.                                       ( THR:$2F8; RHR:$2F8; DLL:$2F8;           {-COM2 addresses of the 8250 }
  62.                                         IER:$2F9; DLM:$2F9; IIR:$2FA;           { registers so that they may }
  63.                                         LCR:$2FB; MCR:$2FC; LSR:$2FD;           { be accessed by name        }
  64.                                         MSR:$2FE ) );                           {                            }
  65.                                                                                 {                            }
  66. VAR                                                                             {                            }
  67.   IntInstalled : ARRAY [1..2] OF BOOLEAN;                                       {-TRUE if interrupt in place }
  68.   OldIntVector : ARRAY [1..2] OF POINTER;                                       {-Original COMx int. vectors }
  69.   InHead, InTail : ARRAY [1..2] OF WORD;                                        {-Input buffer pointers      }
  70.   Carrier : ARRAY [1..2] OF BOOLEAN;                                            {-TRUE if Carrier Detected   }
  71.   ComSettings : ComSettingsType;                                                {-COM1 & COM2 line settings  }
  72.   InBuffer : ComBuffersType;                                                    {-Input circular queue buffer}
  73.   ExitSave : POINTER;                                                           {-Saves original ExitProc    }
  74.   MaxPorts : WORD;                                                              {-Number of usable COM ports }
  75.   Regs : REGISTERS;                                                             {-8088 CPU Registers         }
  76.   CurrentCom : BYTE;                                                            {-COM port currently logged  }
  77.                                                                                 {                            }
  78. PROCEDURE DisableInts; INLINE ($FA);                                            {-Disable hardware interrupts}
  79.                                                                                 {                            }
  80. PROCEDURE EnableInts; INLINE ($FB);                                             {-Enable hardware interrupts }
  81.                                                                                 {                            }
  82. { This procedure sets up the selected COM port to the specified parameters.  The Com parameter specifies the }
  83. { port to set up.  It must be in the range 1 to 2, and is checked for errors.  The Baud parameter is must be }
  84. { in the range 0 to 9, and is not range checked.  The TYPE BaudTable is included only to document the baud   }
  85. { rates supported in BaudTable.  I.E. BaudTable [1] refers to 150 Baud.  It is unneccessary for any other    }
  86. { purpose.  In the same manner, ParityType is provide to document the parity settings allowed.  Use ORD() to }
  87. { get the correct BYTE value to pass:  ORD(B110) returns the BYTE that selects 110 baud and ORD(None) gives  }
  88. { the BYTE that selects no parity. (Use global declarations of these TYPEs for these examples to work.)  1.5 }
  89. { stop bits are used when StopBits = 2 AND DataBits = 5, but otherwise StopBits will set the correct number  }
  90. { of stop bits in the range 1 to 2.  DataBits may be set with 5 to 8 for the number of data bits to use.     }
  91. {                                                                                                            }
  92. { Mark parity means that parity is enabled and the parity bit is always set to 0.  Space parity means that   }
  93. { parity is enabled and the parrity bit is always set to 1.  MarkOff and SpaceOff indicate that Mark or Space}
  94. { parity is chosen but parity is disabled.  Functionally they are equivalent to NONE - as is NULL.           }
  95.                                                                                 {                            }
  96. PROCEDURE SetupRS232 (Com, Baud, Parity, StopBits, DataBits : BYTE);            {                            }
  97. CONST                                                                           {-These values set the baud  }
  98.   BaudTable : ARRAY [0..9] OF WORD = ($0417, $0300, $0180, $00C0, $0060,        { rate of the 8250 when they }
  99.                                       $0030, $0018, $000C, $0006, $0003);       { are written to DLL & DMM.  }
  100. TYPE                                                                            {                            }
  101.   BaudType = (B110,B150,B300,B600,B1200,B2400,B4800,B9600,B19200,B38400);       {-Baud rates supported       }
  102.   ParityType = (None,Odd,Null,Even,MarkOff,Mark,SpaceOff,Space);                {-Parity settings supported  }
  103.                                                                                 {                            }
  104. VAR                                                                             {-Temporary variable to hold }
  105.   Parameters : BYTE;                                                            { correct LCR register value }
  106. BEGIN                                                                           {                            }
  107.   IF (Com <= MaxPorts)                                                          {-Check validity of Com      }
  108.     THEN BEGIN                                                                  {                            }
  109.            DisableInts;                                                         {-Always when writing to 8250}
  110.            PORT [RS232 [Com].MCR] := $00;                                       {-DTR & RTS off while setting}
  111.            PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] OR $80;             {-Allow access to DLL & DLM  }
  112.            PORT [RS232 [Com].DLL] := LO (BaudTable [Baud]);                     {-Set the baud rate          }
  113.            PORT [RS232 [Com].DLM] := HI (BaudTable [Baud]);                     {                            }
  114.            Parameters := (DataBits - 5) AND $03;                                {-Build the value to write   }
  115.            Parameters := Parameters OR (((StopBits - 1) SHL 2) AND $04);        { to Line Control Register.  }
  116.            Parameters := Parameters OR ((Parity SHL 3) AND $38);                {                            }
  117.            PORT [RS232 [Com].LCR] := Parameters;                                {-Set parity, data/stop bits }
  118.            PORT [RS232 [Com].MCR] := $0B;                                       {-DTR & RTS back on          }
  119.            EnableInts;                                                          {-Done writing to 8250 regs. }
  120.          END                                                                    {                            }
  121.     ELSE BEGIN                                                                  {                            }
  122.            WRITELN (#13,#10, 'Error!  COM', Com, ' not available', #10);        {-Mostly here for debugging  }
  123.          END;                                                                   { purposes                   }
  124. END;                                                                            {                            }
  125.                                                                                 {                            }
  126. { This procedure handles all interrupts from the 8250 communications chip.  All interrupt types are provided }
  127. { for even though I only implemented the receive data interrupt and crudely used the modem status interrupt. }
  128. { The skeleton is there though, so you can write your own implementations for the interrupts.  Incoming data }
  129. { is stored in InBuffer if the buffer is not full - otherwise it is ignored. The buffer is full when (InTail }
  130. { [IntCom] + 1) MOD (MaxSize + 1) = InHead [IntCom].  The buffer is empty when InTail [IntCom] = InHead      }
  131. { [IntCom].  InTail [IntCom] is incremented so that it always points to where the next item will be put.     }
  132. { Modem (port) status is monitored for Carrier Detect.  The global BOOLEAN variable 'Carrier' always shows   }
  133. { the status of each COM port if its interrupt handler is active.  TRUE = Carrier Detected and FALSE = No    }
  134. { Carrier Detected.  This is done for programs that will use a modem for input.  NOTE:  Stack checking must  }
  135. { always be turned off in interrupt handlers ($S-).                                                          }
  136.                                                                                 {                            }
  137. {$F+}                                                                           {-Interrupt handlers MUST be }
  138. PROCEDURE IntHandler; INTERRUPT;                                                { FAR calls.                 }
  139. {$F-}                                                                           {                            }
  140. VAR                                                                             {-The COMx port which is to  }
  141.   IntCom : BYTE;                                                                { be used.                   }
  142. BEGIN                                                                           {                            }
  143.   PORT [$20] := $0B;                                                            {-Allow access to 8259 ISR   }
  144.   IntCom := 3 - ((PORT [$20] AND $18) SHR 3);                                   {-Detect interrupting port   }
  145.   CASE PORT [RS232 [IntCom].IIR] AND $06 OF                                     {                            }
  146.     0 : BEGIN                                                                   {-Modem Status Change Int.   }
  147.           Carrier [IntCom] := ($80 AND PORT [RS232 [IntCom].MSR] = $80);        {-Save status of Carrier     }
  148.         END;                                                                    {                            }
  149.     2 : BEGIN                                                                   {-Transmit Register Empty    }
  150.         END;                                                                    {                            }
  151.     4 : BEGIN                                                                   {-Receive Register Full      }
  152.           PORT [RS232 [IntCom].LCR] := PORT [RS232 [IntCom].LCR] AND $7F;       {-Allow THR,RBR & IER access }
  153.           IF (InTail [IntCom] + 1) MOD (MaxSize + 1) <> InHead [IntCom]         {                            }
  154.             THEN BEGIN                                                          {-If the buffer is not full, }
  155.                    InBuffer [IntCom,InTail[IntCom]] := PORT [RS232[IntCom].RHR];{ add the character and set  }
  156.                    InTail [IntCom] := (InTail [IntCom] + 1) MOD (MaxSize + 1);  { the queue buffer pointer   }
  157.                  END                                                            {                            }
  158.             ELSE BEGIN                                                          {-If the buffer is full, the }
  159.                    IF (PORT [RS232 [IntCom].RHR] = $00) THEN { DO Nothing };    { data is read & not stored  }
  160.                  END;                                                           {                            }
  161.         END;                                                                    {                            }
  162.     6 : BEGIN                                                                   {-Line Status change & Error }
  163.         END;                                                                    {                            }
  164.   END;                                                                          {                            }
  165.   PORT [$20] := $20;                                                            {-Notify 8259 that interrupt }
  166. END;                                                                            { has been completed.        }
  167.                                                                                 {                            }
  168. { This procedure installs and enables the specified serial port interrupt.  It also forces the appropriate   }
  169. { input buffer to the empty state.  Carrier is initialized to the current state of the Carrier Detect line   }
  170. { on the port.  The old serial port interrupt vector is saved so that it can be reinstalled when we remove   }
  171. { our serial port interrupt.  DTR and RTS are forced to the ready state and the 8250 interrupts are enabled  }
  172. { by writing $0B to the MCR.  To enable all four types of 8250 interrupts, write $0F to the IER.  (I enabled }
  173. { the Modem Status Change and Receive Buffer Full interrupts by writing $09 to the IER). ORing $EF with PORT }
  174. { [$21] enables IRQ4 (COM1), while ORing $F7 enables IRQ3 (COM2).  Hardware interrupts should be disabled    }
  175. { during the installation process since the 8250 & 8259 ports are being accessed.                            }
  176.                                                                                 {                            }
  177. PROCEDURE InstallInt (Com : BYTE);                                              {                            }
  178. BEGIN                                                                           {                            }
  179.   IF NOT IntInstalled [Com] AND (Com <= MaxPorts)                               {-Don't install the handler  }
  180.     THEN BEGIN                                                                  { twice or if nonexistant.   }
  181.            DisableInts;                                                         {                            }
  182.            InTail [Com] := 0;                                                   {-Set input buffer to empty  }
  183.            InHead [Com] := 0;                                                   {                            }
  184.            Carrier [Com] := ($80 AND PORT [RS232 [Com].MSR] = $80);             {-Read Carrier Detect status }
  185.            PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F;            {-Allow THR,RBR & IER access }
  186.            PORT [RS232 [Com].IER] := $00;                                       {-Disable 8250 interrupts    }
  187.            IF PORT [RS232 [Com].LSR] <> 0 THEN { Nothing };                     {-Reset interrupts that were }
  188.            IF PORT [RS232 [Com].RHR] <> 0 THEN { Nothing };                     { waiting to be processed    }
  189.            GETINTVEC ($0D - Com, OldIntVector [Com]);                           {-Save old interrupt vector  }
  190.            SETINTVEC ($0D - Com, @IntHandler);                                  {-Load new interrupt vector  }
  191.            IntInstalled [Com] := TRUE;                                          {-The interrupt is installed }
  192.            CASE Com OF                                                          {                            }
  193.              1 : PORT [$21] := PORT [$21] AND $EF;                              {-Enable 8259 IRQ4 handling  }
  194.              2 : PORT [$21] := PORT [$21] AND $F7;                              {-Enable 8259 IRQ3 handling  }
  195.            END;                                                                 {                            }
  196.            PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F;            {-Allow THR,RBR & IER access }
  197.            PORT [RS232 [Com].IER] := $01;                                       {-Enable 8250 interrupts     }
  198.            PORT [RS232 [Com].MCR] := $0B;                                       {-Set DTR & RTS so the other }
  199.            EnableInts;                                                          { device knows we are ready  }
  200.          END                                                                    { to recieve data            }
  201.     ELSE BEGIN                                                                  {                            }
  202.            WRITE (#13, #10, 'Error!  COM', Com, ' ');                           {                            }
  203.            IF IntInstalled [Com]                                                {                            }
  204.              THEN WRITELN ('interrupt already installed',#10)                   {-Mostly here for debugging  }
  205.              ELSE WRITELN ('not available',#10)                                 { purposes.  Remove in your  }
  206.          END;                                                                   { final program if you want. }
  207. END;                                                                            {                            }
  208.                                                                                 {                            }
  209. { This procedure removes the specified serial port interrupt and reinstalls the original interrupt vectors.  }
  210. { DTR & RTS are set OFF and 8250 interrupts are disabled by writing $00 to the MCR. All 8250 interrupt types }
  211. { are disabled by writing $00 to the IER.  ORing $10 with PORT [$21] disables IRQ4 (COM1), while ORing $08   }
  212. { disables IRQ3 (COM2).  Hardware interrupts must be disabled during this process.                           }
  213.                                                                                 {                            }
  214. PROCEDURE RemoveInt (Com : BYTE);                                               {                            }
  215. BEGIN                                                                           {                            }
  216.   IF IntInstalled [Com]                                                         {-Don't remove if interrupt  }
  217.     THEN BEGIN                                                                  { has not been installed     }
  218.            DisableInts;                                                         {                            }
  219.            CASE Com OF                                                          {                            }
  220.              1 : PORT [$21] := PORT [$21] OR $10;                               {-Disable 8259 IRQ4 handling }
  221.              2 : PORT [$21] := PORT [$21] OR $08;                               {-Disable 8259 IRQ3 handling }
  222.            END;                                                                 {                            }
  223.            PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F;            {-Allow THR,RBR & IER access }
  224.            PORT [RS232 [Com].IER] := $00;                                       {-Disable 8250 interrupts    }
  225.            PORT [RS232 [Com].MCR] := $00;                                       {-Set DTR & RTS off.  Remove }
  226.            SETINTVEC ($0D - Com, OldIntVector [Com]);                           { if modem shouldn't hang up }
  227.            IntInstalled [Com] := FALSE;                                         { when you use RemoveInt.    }
  228.            EnableInts;                                                          {-The original interrupt is  }
  229.          END                                                                    { restored by SETINTVEC.     }
  230.     ELSE BEGIN                                                                  {-Mostly here for debugging  }
  231.            WRITE (#13, #10, 'Error!  COM', Com, ' ');                           { purposes.  Remove in your  }
  232.            WRITELN ('interrupt is not installed', #10);                         { program if you wish.       }
  233.          END;                                                                   {                            }
  234. END;                                                                            {                            }
  235.                                                                                 {                            }
  236. { This procedure writes character or string data to the serial port.  It does this by directly reading and   }
  237. { writing to the 8250 communications chip.  This is an example that may be modified to suit your purposes.   }
  238. { As is, it pauses the program while it sends the data.  If it cannot send a character after 65535 tries, it }
  239. { aborts the sending process.  This could easily be converted to a FUNCTION that returns the BOOLEAN value   }
  240. { TimeOut.  The statement: (PORT [RS232 [Com].LSR] AND $20) <> $20 indicates when the THR is ready for a new }
  241. { character to send.  CTS and DSR are not checked, but if you want to check for them (PORT [RS232 [Com].MSR] }
  242. { AND $30) must equal $30.  Interrupts must be disabled while using the 8250 port registers.                 }
  243.                                                                                 {                            }
  244. PROCEDURE WriteCOM (Com : BYTE; Data : OutBuffer);                              {                            }
  245. VAR                                                                             {                            }
  246.  LoopVar,                                                                       {-Pointer to output char     }
  247.  TimeLoop : WORD;                                                               {-Timeout counter variable   }
  248.  TimeOut : BOOLEAN;                                                             {-True if unable to send     }
  249. BEGIN                                                                           {                            }
  250.   LoopVar := 0;                                                                 {                            }
  251.   TimeOut := FALSE;                                                             {                            }
  252.   WHILE (LoopVar < LENGTH (Data)) AND NOT TimeOut DO                            {-Send the data one char at  }
  253.     BEGIN                                                                       { a time unless the port was }
  254.       TimeLoop := 0;                                                            { timed out.                 }
  255.       LoopVar := LoopVar + 1;                                                   {                            }
  256.       WHILE (TimeLoop < 65535) AND ((PORT [RS232 [Com].LSR] AND $20) <> $20) DO {-Do not try to send data if }
  257.         TimeLoop := TimeLoop + 1;                                               { the THR is not empty yet.  }
  258.       IF TimeLoop <> 65535                                                      {                            }
  259.         THEN BEGIN                                                              {                            }
  260.                DisableInts;                                                     {                            }
  261.                PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F;        {-Allow THR,RBR & IER access }
  262.                PORT [RS232 [Com].THR] := ORD (Data [LoopVar]);                  {-Put the data to send in    }
  263.                EnableInts;                                                      { the THR                    }
  264.              END                                                                {                            }
  265.         ELSE BEGIN                                                              {                            }
  266.                TimeOut := TRUE;                                                 {-WriteCOM aborts if the THR }
  267.                WRITELN (#13,#10, 'Timeout on COM', Com);                        { takes too long to become   }
  268.              END;                                                               { empty so you can send more }
  269.     END;                                                                        { data                       }
  270. END;                                                                            {                            }
  271.                                                                                 {                            }
  272. { This function is an example of how to get a character from the serial port. As is, if the buffer is empty, }
  273. { it waits until a character arrives, so this will not work for the TTY emulation. The interrupts are always }
  274. { disabled when the buffer pointers are checked or modified.  Beware!  Do not completely disable interrupts  }
  275. { in the wait loop or else you never will get a character if there is not one there already.                 }
  276.                                                                                 {                            }
  277. FUNCTION ReadCOM (Com : BYTE) : CHAR;                                           {                            }
  278. VAR                                                                             {                            }
  279.   CharReady : BOOLEAN;                                                          {-TRUE if there is data in   }
  280. BEGIN                                                                           { the input buffer           }
  281.   CharReady := FALSE;                                                           {                            }
  282.   REPEAT                                                                        {-Wait for data to arrive    }
  283.     DisableInts;                                                                {                            }
  284.     CharReady := InTail [Com] <> InHead [Com];                                  {-Check to see if buffer is  }
  285.     EnableInts;                                                                 { empty                      }
  286.   UNTIL CharReady;                                                              {                            }
  287.   DisableInts;                                                                  {                            }
  288.   ReadCOM := CHR(InBuffer [Com, InHead [Com]]);                                 {-Read a character of data   }
  289.   InHead [Com] := (InHead [Com] + 1) MOD (MaxSize + 1);                         {-Update the buffer pointer  }
  290.   EnableInts;                                                                   {                            }
  291. END;                                                                            {                            }
  292.                                                                                 {                            }
  293. { End of RS-232 handler routines ----------- Start of TTY emulation routines }  { Example TTY program starts }
  294.                                                                                 {                            }
  295. { A crude but effective procedure to allow the user to change settings of a COM port in use.  CurrentCom and }
  296. { ComSettings determine how the port is currently set up. As the parameters are changed, ComSettings is also }
  297. { updated.  Once again, keep in mind that the object of this program is not to provide a glamorous terminal  }
  298. { program.  Rather it serves as a simple model for those wanting to incorporate serial routines in their own }
  299. { programs.                                                                                                  }
  300.                                                                                 {                            }
  301. PROCEDURE SetUpPort (Com : BYTE);                                               {                            }
  302. VAR                                                                             {                            }
  303.   ResetPort : BOOLEAN;                                                          {-TRUE when settings changed }
  304.   InkeyChr : CHAR;                                                              {-Keyboard input variable    }
  305. BEGIN                                                                           {                            }
  306.   WRITELN;                                                                      {                            }
  307.   ResetPort := FALSE;                                                           {                            }
  308.   WRITELN ('COM', Com, ' Setup', #10);                                          {-Select a baud rate         }
  309.   WRITELN ('0)  110           5)  2400');                                       {                            }
  310.   WRITELN ('1)  150           6)  4800');                                       {-Note that defaults are     }
  311.   WRITELN ('2)  300           7)  9600');                                       { allowed if you press <CR>  }
  312.   WRITELN ('3)  600           8) 19200');                                       { at any of the prompts. The }
  313.   WRITELN ('4) 1200           9) 38400', #10);                                  { port is not reset unless   }
  314.   WRITE ('Select a baud rate [', ComSettings [Com] . Baud, ']: ');              { the defaults are changed.  }
  315.   REPEAT                                                                        {                            }
  316.     InkeyChr := READKEY;                                                        {                            }
  317.   UNTIL (InkeyChr IN ['0'..'9', #13]);                                          {                            }
  318.   WRITELN (InkeyChr, #10);                                                      {                            }
  319.   IF (InkeyChr <> #13)                                                          {                            }
  320.     THEN BEGIN                                                                  {                            }
  321.            ComSettings [Com] . Baud := ORD (InkeyChr) - 48;                     {                            }
  322.            ResetPort := TRUE;                                                   {                            }
  323.          END;                                                                   {                            }
  324.   WRITELN ('0) None           2) None');                                        {-Select a parity setting    }
  325.   WRITELN ('1) Odd            3) Even', #10);                                   {                            }
  326.   WRITE ('Select a parity type [', ComSettings [Com] . Parity, ']: ');          {                            }
  327.   REPEAT                                                                        {                            }
  328.     InkeyChr := READKEY;                                                        {                            }
  329.   UNTIL (InkeyChr IN ['0'..'3', #13]);                                          {                            }
  330.   WRITELN (InkeyChr, #10);                                                      {                            }
  331.   IF (InkeyChr <> #13)                                                          {                            }
  332.     THEN BEGIN                                                                  {                            }
  333.            ComSettings [Com] . Parity := ORD(InkeyChr) - 48;                    {                            }
  334.            ResetPort := TRUE;                                                   {                            }
  335.          END;                                                                   {                            }
  336.   WRITE ('Select number of stop bits [', ComSettings [Com] . Stop, ']: ');      {-Select number of stop bits }
  337.   REPEAT                                                                        {                            }
  338.     InkeyChr := READKEY;                                                        {                            }
  339.   UNTIL (InkeyChr IN ['1'..'2', #13]);                                          {                            }
  340.   WRITELN (InkeyChr, #10);                                                      {                            }
  341.   IF (InkeyChr <> #13)                                                          {                            }
  342.     THEN BEGIN                                                                  {                            }
  343.            ComSettings [Com] . Stop := ORD(InkeyChr) - 48;                      {                            }
  344.            ResetPort := TRUE;                                                   {                            }
  345.          END;                                                                   {                            }
  346.   WRITE ('Select number of data bits [', ComSettings [Com] . Bits, ']: ');      {-Select number of data bits }
  347.   REPEAT                                                                        {                            }
  348.     InkeyChr := READKEY;                                                        {                            }
  349.   UNTIL (InkeyChr IN ['5'..'8', #13]);                                          {                            }
  350.   WRITELN (InkeyChr, #10);                                                      {                            }
  351.   IF (InkeyChr <> #13)                                                          {                            }
  352.     THEN BEGIN                                                                  {                            }
  353.            ComSettings [Com] . Bits := ORD(InkeyChr) - 48;                      {                            }
  354.            ResetPort := TRUE;                                                   {                            }
  355.          END;                                                                   {                            }
  356.   IF ResetPort                                                                  {-If the settings changed,   }
  357.     THEN SetupRS232 (Com, ComSettings [Com] . Baud,                             { reset the port             }
  358.                           ComSettings [Com] . Parity,                           {                            }
  359.                           ComSettings [Com] . Stop,                             {                            }
  360.                           ComSettings [Com] . Bits);                            {                            }
  361. END;                                                                            {                            }
  362.                                                                                 {                            }
  363. { This provides a simple terminal emulation that might be used to prove that these routines really work, and }
  364. { that they are not hard to use.  I got to playing, and perhaps it got a bit more complex than necessary...  }
  365. { but then again, who said it had to be quick and dirty.  The LocalEcho parameter determines if characters   }
  366. { typed on the keyboard should be echoed to the screen.                                                      }
  367.                                                                                 {                            }
  368. PROCEDURE TTY (LocalEcho : BOOLEAN);                                            {                            }
  369. VAR                                                                             {                            }
  370.   ExitTTY,                                                                      {-TRUE when ready to quit    }
  371.   DataReady : BOOLEAN;                                                          {-TRUE if buffer not empty   }
  372.   OldCarrier : ARRAY [1..2] OF BOOLEAN;                                         {-Helps detect carrier change}
  373.   Buffer : CHAR;                                                                {-A character buffer         }
  374. BEGIN                                                                           {                            }
  375.   OldCarrier [1] := NOT Carrier [1];                                            {-Make Carrier Detect Status }
  376.   OldCarrier [2] := NOT Carrier [2];                                            { so it will be displayed    }
  377.   DataReady := FALSE;                                                           {-Initialize everything      }
  378.   ExitTTY := FALSE;                                                             {                            }
  379.   Buffer := #0;                                                                 {                            }
  380.   CLRSCR;                                                                       {                            }
  381.   WRITELN ('Terminal emulator commands', #10);                                  {-Brief summary of command   }
  382.   WRITELN ('<ALT C>  Toggle Port in use COM1/COM2');                            { keys that can be used      }
  383.   WRITELN ('<Alt E>  Toggle Local Echo On/Off');                                {                            }
  384.   WRITELN ('<Alt P>  Change Port Parameters');                                  {                            }
  385.   WRITELN ('<Alt X>  Exit');                                                    {                            }
  386.   REPEAT                                                                        {-Terminal emulation starts  }
  387.     DisableInts;                                                                {                            }
  388.     DataReady := (InTail [CurrentCom] <> InHead [CurrentCom]);                  {-If data has been received, }
  389.     EnableInts;                                                                 { print one character        }
  390.     IF DataReady                                                                {                            }
  391.       THEN BEGIN                                                                { CHR(12) is interpreted as  }
  392.              DisableInts;                                                       { a FormFeed, and so clears  }
  393.              Buffer := CHR(InBuffer [CurrentCom, InHead [CurrentCom]]);         { the screen                 }
  394.              InHead [CurrentCom] := (InHead [CurrentCom] + 1) MOD (MaxSize + 1);{                            }
  395.              EnableInts;                                                        { Input buffer is updated    }
  396.              CASE Buffer OF                                                     {                            }
  397.                #12 : CLRSCR;                                                    {                            }
  398.                ELSE  WRITE (Buffer);                                            {                            }
  399.              END;                                                               {                            }
  400.            END;                                                                 {                            }
  401.     IF (OldCarrier [CurrentCom] <> Carrier [CurrentCom])                        {-If a change in carrier     }
  402.       THEN BEGIN                                                                { detect occurs, notify the  }
  403.              WRITELN;                                                           { user of the new status     }
  404.              IF Carrier [CurrentCom]                                            {                            }
  405.                THEN WRITELN ('CARRIER DETECTED (COM', CurrentCom, ')')          {                            }
  406.                ELSE WRITELN ('NO CARRIER (COM', CurrentCom, ')');               {                            }
  407.              OldCarrier [CurrentCom] := Carrier [CurrentCom];                   {                            }
  408.            END;                                                                 {-If a key has been pressed, }
  409.     IF KEYPRESSED                                                               { process it                 }
  410.       THEN BEGIN                                                                {                            }
  411.              Buffer := READKEY;                                                 {                            }
  412.              IF (Buffer = #00) AND KEYPRESSED                                   {-Extended key codes require }
  413.                THEN BEGIN                                                       { another read               }
  414.                       Buffer := READKEY;                                        {                            }
  415.                       CASE Buffer OF                                            {                            }
  416.                         #46 : IF (1 + ORD (CurrentCom = 1)) <= MaxPorts         {-<ALT C> lets you toggle    }
  417.                                 THEN BEGIN                                      { between ports if the new   }
  418.                                        CurrentCom := 1 + ORD (CurrentCom = 1);  { port exists                }
  419.                                        WRITELN (#13,#10, 'COM', CurrentCom);    {                            }
  420.                                      END                                        {                            }
  421.                                 ELSE BEGIN                                      {                            }
  422.                                        WRITE (#13,#10, 'COM');                  {                            }
  423.                                        WRITE (1 + ORD (CurrentCom = 1));        {                            }
  424.                                        WRITELN (' not available');              {                            }
  425.                                      END;                                       {                            }
  426.                         #18 : LocalEcho := NOT LocalEcho;                       {-<ALT E> toggles Local Echo }
  427.                         #25 : SetupPort (CurrentCom);                           {-<ALT P> allows port setup  }
  428.                         #45 : ExitTTY := TRUE;                                  {-<ALT X> exits the program  }
  429.                         ELSE  WriteCOM (CurrentCom, CHR(27) + Buffer);          {-Other extended key codes   }
  430.                       END;                                                      { are sent to the port       }
  431.                     END                                                         {                            }
  432.                ELSE BEGIN                                                       {-Normal key codes are sent  }
  433.                       CASE Buffer OF                                            { or translated and sent     }
  434.                         #12 : BEGIN                                             {                            }
  435.                                 WriteCOM (CurrentCom, Buffer);                  {-FormFeed clears screen if  }
  436.                                 IF LocalEcho THEN CLRSCR;                       { local echo is on           }
  437.                               END;                                              {                            }
  438.                         #13 : BEGIN                                             {-A carriage return also     }
  439.                                 WriteCOM (CurrentCom, Buffer + CHR(10));        { sends a line feed          }
  440.                                 IF LocalEcho THEN WRITELN;                      {                            }
  441.                               END;                                              {                            }
  442.                         ELSE  BEGIN                                             {-All other characters are   }
  443.                                 WriteCOM (CurrentCom, Buffer);                  { sent as typed              }
  444.                                 IF LocalEcho THEN WRITE (Buffer);               {                            }
  445.                               END;                                              {                            }
  446.                       END;                                                      {                            }
  447.                     END;                                                        {                            }
  448.            END;                                                                 {                            }
  449.   UNTIL ExitTTY;                                                                {-Continue emulation until   }
  450. END;                                                                            { <ALT X> is pressed.        }
  451.                                                                                 {                            }
  452. FUNCTION Equipment : WORD;                                                      {-This function returns what }
  453. BEGIN                                                                           { equipment is present on    }
  454.   INTR ($11, Regs);                                                             { the machine it is running  }
  455.   Equipment := Regs.AX;                                                         { on.                        }
  456. END;                                                                            {                            }
  457.                                                                                 {                            }
  458. {$F+}                                                                           {-VERY IMPORTANT!  When the  }
  459. PROCEDURE RemoveIntOnExit;                                                      { program quits normally or  }
  460. {$F-}                                                                           { abnormally, the interrupt  }
  461. BEGIN                                                                           { handlers are uninstalled   }
  462.   IF IntInstalled [1]                                                           { if they are still set up.  }
  463.     THEN RemoveInt (1);                                                         {                            }
  464.   IF IntInstalled [2]                                                           {                            }
  465.     THEN RemoveInt (2);                                                         {                            }
  466.   ExitProc := ExitSave;                                                         {-Return control to the      }
  467. END;                                                                            { original exit procedure    }
  468.                                                                                 {                            }
  469. BEGIN                                                                           {                            }
  470.   ExitSave := ExitProc;                                                         {-VERY IMPORTANT!  This lets }
  471.   ExitProc := @RemoveIntOnExit;                                                 { the program halt safely.   }
  472.   MaxPorts := (Equipment AND $0E00) SHR 9;                                      {-Find # of system COM ports }
  473.   IntInstalled [1] := FALSE;                                                    {-No interrupt handlers are  }
  474.   IntInstalled [2] := FALSE;                                                    { installed on start up      }
  475.   ComSettings [1] . Baud := ORD (B9600);                                        {-Define COM1 default setup  }
  476.   ComSettings [1] . Parity := ORD (None);                                       {                            }
  477.   ComSettings [1] . Stop := 1;                                                  {                            }
  478.   ComSettings [1] . Bits := 8;                                                  {                            }
  479.   ComSettings [2] . Baud := ORD (B2400);                                        {-Define COM2 default setup  }
  480.   ComSettings [2] . Parity := ORD (None);                                       {                            }
  481.   ComSettings [2] . Stop := 1;                                                  {                            }
  482.   ComSettings [2] . Bits := 8;                                                  {                            }
  483.   IF (MaxPorts >= 1)                                                            {                            }
  484.     THEN BEGIN                                                                  {                            }
  485.            SetupRS232 (1, ComSettings [1] . Baud,                               {-Initialize COM1 to the     }
  486.                           ComSettings [1] . Parity,                             { default setup              }
  487.                           ComSettings [1] . Stop,                               {                            }
  488.                           ComSettings [1] . Bits);                              {                            }
  489.            InstallInt (1);                                                      {-Set up the COM1 interrupt  }
  490.          END                                                                    { if the computer has a port }
  491.     ELSE WRITELN ('Error!  No serial ports installed in this computer');        {                            }
  492.   IF (MaxPorts >= 2)                                                            {                            }
  493.     THEN BEGIN                                                                  {                            }
  494.            SetupRS232 (2, ComSettings [2] . Baud,                               {                            }
  495.                           ComSettings [2] . Parity,                             {-Initialize COM2 to the     }
  496.                           ComSettings [2] . Stop,                               { default setup              }
  497.                           ComSettings [2] . Bits);                              {                            }
  498.            InstallInt (2);                                                      {-Set up the COM2 interrupt  }
  499.          END;                                                                   {                            }
  500.   CurrentCom := 1;                                                              {-Set COM1 as logged port    }
  501.   TTY (FALSE);                                                                  {-TTY with local echo off    }
  502.                                                                                 {                            }
  503.   { IMPORTANT:  RemoveIntOnExit is always called when the program terminates! } {-RemoveIntOnExit invoked by }
  504.                                                                                 { Turbo.  Don't quit without }
  505. END.                                                                            { removing interrupts!       }
  506.