home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1991
/
09
/
intterm.lst
< prev
next >
Wrap
File List
|
1991-08-21
|
9KB
|
207 lines
_STRUCTURED PROGRAMMING COLUMN_
by Jeff Duntemann
[LISTING ONE]
{------------------------------------------------------------------------}
{ INTTERM }
{ by Jeff Duntemann }
{ Turbo Pascal V5.0 or later }
{ Last update 6/2/91 }
{ This is an interrupt-driven "dumb terminal" program for the PC. It }
{ the use of Turbo Pascal's INTERRUPT procedures, and in a lesser fashion}
{ the use of serial port hardware. It can be set to use either COM1: or }
{ COM2: by setting the COMPORT constant to 1 (for COM1) or 2 (for COM2) }
{ as appropriate and recompiling. }
{------------------------------------------------------------------------}
PROGRAM INTTERM;
USES DOS,CRT;
CONST
COMPORT = 2; { 1 = COM1: 2 = COM2: }
COMINT = 13-COMPORT; { 12 = COM1: (IRQ4) 11 = COM2: (IRQ3) }
COMBASE = $2F8;
PORTBASE = COMBASE OR (COMPORT SHL 8); { $3F8 for COM1: $2F8 for COM2: }
{ 8250 control registers, masks, etc. }
RBR = PORTBASE; { 8250 Receive Buffer Register }
THR = PORTBASE; { 8250 Transmit Holding Register }
LCR = PORTBASE + 3; { 8250 Line Control Register }
IER = PORTBASE + 1; { 8250 Interrupt Enable Register }
MCR = PORTBASE + 4; { 8250 Modem Control Register }
LSR = PORTBASE + 5; { 8250 Line Status Register }
DLL = PORTBASE; { 8250 Divisor Latch LSB }
DLM = PORTBASE + 1; { 8250 Divisor Latch MSB }
DLAB = $80; { 8250 Divisor Latch Access Bit }
BAUD300 = 384; { Value for 300 baud operation }
BAUD1200 = 96; { Value for 1200 baud operation }
NOPARITY = 0; { Comm format value for no parity }
BITS8 = $03; { Comm format value for 8 bits }
DTR = $01; { Value for Data Terminal Ready }
RTS = $02; { value for Ready To Send }
OUT2 = $08; { Bit that enables adapter interrupts }
BUFFSIZE = 1023;
{ 8259 control registers, masks, etc. }
OCW1 = $21; { 8259 Operation Control Word 1 }
OCW2 = $20; { 8259 Operation Control Word 2 }
{ The 8259 mask bit is calculated depending on }
{ which serial port is used... }
{ $10 for COM1: (IRQ4); $08 for COM2: (IRQ3): }
IRQBIT = $20 SHR COMPORT;
TYPE
CircularBuffer = ARRAY[0..BUFFSIZE] OF Char; { Input buffer }
VAR
Quit : Boolean; { Flag for exiting the program }
HiBaud : Boolean; { True if 1200 baud is being used }
KeyChar : Char; { Character from keyboard }
CommChar : Char; { Character from the comm port }
Divisor : Word; { Divisor value for setting baud rate }
Clearit : Byte; { Dummy variable }
Buffer : CircularBuffer; { Our incoming character buffer }
LastRead, { Index of the last character read }
LastSaved : Integer; { Index of the last character stored }
NoShow : SET OF Char; { Don't show characters set }
OldVector : Pointer; { Global storage slot for the old }
{ interrupt vector }
PROCEDURE EnableInterrupts;
INLINE($FB);
{->>>>Incoming (Interrupt Service Routine)<<<<--------------------------------}
{ This is the ISR (interrupt Service Routine) for comm ports. DO NOT call this}
{ routine directly; you'll crash hard. The only way Incoming takes control is }
{ when a character coming in from modem triggers a hardware interrupt from }
{ serial port chip, the 8250 UART. Note that the register pseudo-parameters }
{ are not needed here, and you could omit them. However, omitting them doesn't}
{ really get you any more speed or reliability. }
{-----------------------------------------------------------------------------}
PROCEDURE Incoming(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP : Word);
INTERRUPT;
BEGIN
{ First we have to enable interrupts *at the CPU* during the ISR: }
EnableInterrupts;
{ The first "real work" we do is either wrap or increment the index of the }
{ last character saved. If index is "topped out" at buffer size (here, }
{ 1023) we force it to zero. This makes the 1024-byte buffer "circular," }
{ in that once the index hits the end, it rolls over to beginning again. }
IF LastSaved >= BUFFSIZE THEN LastSaved := 0 ELSE Inc(LastSaved);
{ Next, we read the actual incoming character from the serial port's}
{ one-byte holding buffer: }
Buffer[LastSaved] := Char(Port[RBR]);
{ Finally, we must send a control byte to the 8259 interrupt }
{ controller, telling it that the interrupt is finished: }
Port[OCW2] := $20; { Send EOI byte to 8259 }
END;
{$F+}
PROCEDURE IntTermExitProc;
BEGIN
Port[IER] := 0; { Disable interrupts at 8250 }
Port[OCW1] := Port[OCW1] OR IRQBIT; { Disable comm int at 8259 }
Port[MCR] := 0; { Bring the comm line down }
SetIntVec(COMINT,OldVector); { Restore previously saved vector }
END;
{$F-}
PROCEDURE SetupSerialPort;
BEGIN
LastRead := 0; { Initialize the circular buffer pointers }
LastSaved := 0;
Port[IER] := 0; { Disable 8250 interrupts while setting them up }
GetIntVec(ComInt,OldVector); { Save old interrupt vector }
ExitProc := @IntTermExitProc; { Hook exit proc into chain }
SetIntVec(ComInt,@Incoming); { Put ISR address into vector table }
Port[LCR] := Port[LCR] OR DLAB; { Set up 8250 to set baud rate }
Port[DLL] := Lo(Divisor); { Set baud rate divisor }
Port[DLM] := Hi(Divisor);
Port[LCR] := BITS8 OR NOPARITY; { Set word length and parity }
Port[MCR] := DTR OR RTS OR OUT2; { Enable adapter, DTR, & RTS }
Port[OCW1] := Port[OCW1] AND (NOT IRQBIT); { Turn on 8259 comm ints }
Clearit := Port[RBR]; { Clear any garbage from RBR }
Clearit := Port[LSR]; { Clear any garbage from LSR }
Port[IER] := $01; { Enable 8250 interrupt on received character }
END;
FUNCTION InStat : Boolean;
BEGIN
IF LastSaved <> LastRead THEN InStat := True
ELSE InStat := False;
END;
FUNCTION InChar : Char; { Bring in the next character }
BEGIN
IF LastRead >= BUFFSIZE THEN LastRead := 0
ELSE Inc(LastRead);
InChar := Buffer[LastRead];
END;
PROCEDURE OutChar(Ch : Char); { Send a character to the comm port }
BEGIN
Port[THR] := Byte(Ch) { Put character ito Transmit Holding Register }
END;
PROCEDURE ShowHelp;
BEGIN
Writeln('>>>IntTerm by Jeff Duntemann >>>>>>>>>>>>>>>>>>>>>>>>>>>');
Writeln(' Defaults to 1200 Baud; to run at 300 Baud');
Writeln(' invoke with "300" after "INTTERM" on the command line.');
Writeln;
Writeln(' Commands:');
Writeln(' ALT-X: Exits to DOS');
Writeln(' CTRL-Z: Clears the screen');
Writeln('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<');
END;
{>>>>>INTTERM MAIN PROGRAM<<<<<}
BEGIN
HiBaud := True; { Defaults to 1200 baud; if "300" is }
Divisor := BAUD1200; { entered after "INTTERM" on the command }
IF ParamCount > 0 THEN { line, then 300 baud is used instead. }
IF ParamStr(1) = '300' THEN
BEGIN
HiBaud := False;
Divisor := BAUD300
END;
DirectVideo := True;
NoShow := [#0,#127]; { Don't display NUL or RUBOUT }
SetupSerialPort; { Set up serial port & turn on interrupts }
ClrScr;
Writeln('>>>INTTERM by Jeff Duntemann');
Quit := False; { Exit INTTERM when Quit goes to True }
REPEAT
IF InStat THEN { If a character comes in from the modem }
BEGIN
CommChar := InChar; { Go get character }
CommChar := Char(Byte(CommChar) AND $7F); { Mask off high bit }
IF NOT (CommChar IN NoShow) THEN { If we can show it,}
Write(CommChar) { then show it! }
END;
IF KeyPressed THEN { If a character is typed at the keyboard }
BEGIN
KeyChar := ReadKey; { First, read the keystroke }
IF KeyChar = Chr(0) THEN { We have an extended scan code here }
BEGIN
KeyChar := ReadKey; { Read second half of extended code }
CASE Ord(KeyChar) OF
59 : ShowHelp; { F1 : Display help screen }
45 : Quit := True; { Alt-X: Exit IntTerm }
END { CASE }
END
ELSE
CASE Ord(KeyChar) OF
26 : ClrScr; { Ctrl-Z: Clear the screen }
ELSE OutChar(KeyChar)
END; { CASE }
END
UNTIL Quit
END.