home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 09 / intterm.lst < prev    next >
File List  |  1991-08-21  |  9KB  |  207 lines

  1. _STRUCTURED PROGRAMMING COLUMN_
  2. by Jeff Duntemann
  3.  
  4. [LISTING ONE]
  5.  
  6. {------------------------------------------------------------------------}
  7. {                           INTTERM                                      }
  8. {                             by Jeff Duntemann                          }
  9. {                             Turbo Pascal V5.0 or later                 }
  10. {                             Last update 6/2/91                         }
  11. { This is an interrupt-driven "dumb terminal" program for the PC. It     }
  12. { the use of Turbo Pascal's INTERRUPT procedures, and in a lesser fashion}
  13. { the use of serial port hardware. It can be set to use either COM1: or  }
  14. { COM2: by setting the COMPORT constant to 1 (for COM1) or 2 (for COM2)  }
  15. { as appropriate and recompiling.                                        }
  16. {------------------------------------------------------------------------}
  17.  
  18. PROGRAM INTTERM;
  19.  
  20. USES DOS,CRT;
  21.  
  22. CONST
  23.   COMPORT  = 2;                     {  1 = COM1:  2 = COM2: }
  24.   COMINT   = 13-COMPORT;            { 12 = COM1: (IRQ4) 11 = COM2: (IRQ3) }
  25.   COMBASE  = $2F8;
  26.   PORTBASE = COMBASE OR (COMPORT SHL 8);  { $3F8 for COM1: $2F8 for COM2: }
  27.  
  28.   { 8250 control registers, masks, etc. }
  29.   RBR      = PORTBASE;        { 8250 Receive Buffer Register     }
  30.   THR      = PORTBASE;        { 8250 Transmit Holding Register   }
  31.   LCR      = PORTBASE + 3;    { 8250 Line Control Register       }
  32.   IER      = PORTBASE + 1;    { 8250 Interrupt Enable Register   }
  33.   MCR      = PORTBASE + 4;    { 8250 Modem Control Register      }
  34.   LSR      = PORTBASE + 5;    { 8250 Line Status Register        }
  35.   DLL      = PORTBASE;        { 8250 Divisor Latch LSB           }
  36.   DLM      = PORTBASE + 1;    { 8250 Divisor Latch MSB           }
  37.   DLAB     = $80;             { 8250 Divisor Latch Access Bit    }
  38.  
  39.   BAUD300  = 384;     { Value for 300 baud operation        }
  40.   BAUD1200 = 96;      { Value for 1200 baud operation       }
  41.   NOPARITY = 0;       { Comm format value for no parity     }
  42.   BITS8    = $03;     { Comm format value for 8 bits        }
  43.   DTR      = $01;     { Value for Data Terminal Ready       }
  44.   RTS      = $02;     { value for Ready To Send             }
  45.   OUT2     = $08;     { Bit that enables adapter interrupts }
  46.   BUFFSIZE = 1023;
  47.  
  48.   { 8259 control registers, masks, etc. }
  49.   OCW1     = $21;     { 8259 Operation Control Word 1  }
  50.   OCW2     = $20;     { 8259 Operation Control Word 2  }
  51.                       { The 8259 mask bit is calculated depending on }
  52.                       { which serial port is used...   }
  53.                       { $10 for COM1: (IRQ4); $08 for COM2: (IRQ3): }
  54.   IRQBIT   = $20 SHR COMPORT;
  55.  
  56. TYPE
  57.   CircularBuffer = ARRAY[0..BUFFSIZE] OF Char;  { Input buffer }
  58. VAR
  59.   Quit       : Boolean;           { Flag for exiting the program }
  60.   HiBaud     : Boolean;           { True if 1200 baud is being used }
  61.   KeyChar    : Char;              { Character from keyboard }
  62.   CommChar   : Char;              { Character from the comm port }
  63.   Divisor    : Word;              { Divisor value for setting baud rate }
  64.   Clearit    : Byte;              { Dummy variable }
  65.   Buffer     : CircularBuffer;    { Our incoming character buffer }
  66.   LastRead,                       { Index of the last character read }
  67.   LastSaved  : Integer;           { Index of the last character stored }
  68.   NoShow     : SET OF Char;       { Don't show characters set }
  69.   OldVector  : Pointer;           { Global storage slot for the old }
  70.                                   { interrupt vector }
  71. PROCEDURE EnableInterrupts;
  72. INLINE($FB);
  73. {->>>>Incoming (Interrupt Service Routine)<<<<--------------------------------}
  74. { This is the ISR (interrupt Service Routine) for comm ports. DO NOT call this}
  75. { routine directly; you'll crash hard. The only way Incoming takes control is }
  76. { when a character coming in from modem triggers a hardware interrupt from    }
  77. { serial port chip, the 8250 UART. Note that the register pseudo-parameters   }
  78. { are not needed here, and you could omit them. However, omitting them doesn't}
  79. { really get you any more speed or reliability.                               }
  80. {-----------------------------------------------------------------------------}
  81.  
  82. PROCEDURE Incoming(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : Word);
  83. INTERRUPT;
  84. BEGIN
  85.   { First we have to enable interrupts *at the CPU* during the ISR: }
  86.   EnableInterrupts;
  87.   { The first "real work" we do is either wrap or increment the index of the }
  88.   { last character saved. If index is "topped out" at buffer size (here,     }
  89.   { 1023) we force it to zero. This makes the 1024-byte buffer "circular,"   }
  90.   { in that  once the index hits the end, it rolls over to beginning again.  }
  91.   IF LastSaved >= BUFFSIZE THEN LastSaved := 0 ELSE Inc(LastSaved);
  92.  
  93.   { Next, we read the actual incoming character from the serial port's}
  94.   { one-byte holding buffer: }
  95.   Buffer[LastSaved] := Char(Port[RBR]);
  96.  
  97.   { Finally, we must send a control byte to the 8259 interrupt  }
  98.   { controller, telling it that the interrupt is finished:      }
  99.   Port[OCW2] := $20;                    { Send EOI byte to 8259 }
  100. END;
  101.  
  102. {$F+}
  103. PROCEDURE IntTermExitProc;
  104. BEGIN
  105.   Port[IER] := 0;                      { Disable interrupts at 8250 }
  106.   Port[OCW1] := Port[OCW1] OR IRQBIT;    { Disable comm int at 8259 }
  107.   Port[MCR] := 0;                       { Bring the comm line down  }
  108.   SetIntVec(COMINT,OldVector);    { Restore previously saved vector }
  109. END;
  110. {$F-}
  111.  
  112. PROCEDURE SetupSerialPort;
  113. BEGIN
  114.   LastRead  := 0;  { Initialize the circular buffer pointers }
  115.   LastSaved := 0;
  116.  
  117.   Port[IER] := 0;  { Disable 8250 interrupts while setting them up }
  118.  
  119.   GetIntVec(ComInt,OldVector);             { Save old interrupt vector }
  120.   ExitProc := @IntTermExitProc;            { Hook exit proc into chain }
  121.   SetIntVec(ComInt,@Incoming);     { Put ISR address into vector table }
  122.  
  123.   Port[LCR] := Port[LCR] OR DLAB;     { Set up 8250 to set baud rate   }
  124.   Port[DLL] := Lo(Divisor);           { Set baud rate divisor          }
  125.   Port[DLM] := Hi(Divisor);
  126.   Port[LCR] := BITS8 OR NOPARITY;         { Set word length and parity }
  127.   Port[MCR] := DTR OR RTS OR OUT2;        { Enable adapter, DTR, & RTS }
  128.   Port[OCW1] := Port[OCW1] AND (NOT IRQBIT);  { Turn on 8259 comm ints }
  129.   Clearit := Port[RBR];                   { Clear any garbage from RBR }
  130.   Clearit := Port[LSR];                   { Clear any garbage from LSR }
  131.   Port[IER] := $01;      { Enable 8250 interrupt on received character }
  132. END;
  133.  
  134. FUNCTION InStat : Boolean;
  135. BEGIN
  136.   IF LastSaved <> LastRead THEN InStat := True
  137.     ELSE InStat := False;
  138. END;
  139.  
  140. FUNCTION InChar : Char;    { Bring in the next character }
  141. BEGIN
  142.   IF LastRead >= BUFFSIZE THEN LastRead := 0
  143.     ELSE Inc(LastRead);
  144.   InChar := Buffer[LastRead];
  145. END;
  146.  
  147. PROCEDURE OutChar(Ch : Char);   { Send a character to the comm port }
  148. BEGIN
  149.   Port[THR] := Byte(Ch)     { Put character ito Transmit Holding Register }
  150. END;
  151.  
  152. PROCEDURE ShowHelp;
  153. BEGIN
  154.   Writeln('>>>IntTerm by Jeff Duntemann >>>>>>>>>>>>>>>>>>>>>>>>>>>');
  155.   Writeln('   Defaults to 1200 Baud; to run at 300 Baud');
  156.   Writeln('   invoke with "300" after "INTTERM" on the command line.');
  157.   Writeln;
  158.   Writeln('   Commands:');
  159.   Writeln('   ALT-X:  Exits to DOS');
  160.   Writeln('   CTRL-Z: Clears the screen');
  161.   Writeln('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<');
  162. END;
  163.  
  164. {>>>>>INTTERM MAIN PROGRAM<<<<<}
  165. BEGIN
  166.   HiBaud := True;               { Defaults to 1200 baud; if "300" is }
  167.   Divisor := BAUD1200;          { entered after "INTTERM" on the command }
  168.   IF ParamCount > 0 THEN        { line, then 300 baud is used instead.   }
  169.     IF ParamStr(1) = '300' THEN
  170.       BEGIN
  171.         HiBaud := False;
  172.         Divisor := BAUD300
  173.       END;
  174.   DirectVideo := True;
  175.   NoShow := [#0,#127];          { Don't display NUL or RUBOUT }
  176.   SetupSerialPort;              { Set up serial port & turn on interrupts }
  177.   ClrScr;
  178.   Writeln('>>>INTTERM by Jeff Duntemann');
  179.   Quit := False;        { Exit INTTERM when Quit goes to True }
  180.   REPEAT
  181.     IF InStat THEN      { If a character comes in from the modem }
  182.       BEGIN
  183.         CommChar := InChar;                        { Go get character  }
  184.         CommChar := Char(Byte(CommChar) AND $7F);  { Mask off high bit }
  185.         IF NOT (CommChar IN NoShow) THEN           { If we can show it,}
  186.           Write(CommChar)                          {  then show it! }
  187.       END;
  188.     IF KeyPressed THEN  { If a character is typed at the keyboard }
  189.       BEGIN
  190.         KeyChar := ReadKey;       { First, read the keystroke }
  191.         IF KeyChar = Chr(0) THEN  { We have an extended scan code here }
  192.           BEGIN
  193.             KeyChar := ReadKey;   { Read second half of extended code  }
  194.             CASE Ord(KeyChar) OF
  195.              59 : ShowHelp;       { F1 : Display help screen }
  196.              45 : Quit := True;   { Alt-X: Exit IntTerm      }
  197.             END { CASE }
  198.           END
  199.         ELSE
  200.         CASE Ord(KeyChar) OF
  201.          26 : ClrScr;             { Ctrl-Z: Clear the screen }
  202.          ELSE OutChar(KeyChar)
  203.         END;  { CASE }
  204.       END
  205.   UNTIL Quit
  206. END.
  207.