home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of Shareware - Software Farm 2
/
wosw_2.zip
/
wosw_2
/
PASCAL
/
MADTRB21.ZIP
/
ITERM.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1985-06-06
|
11KB
|
283 lines
{--------------------------------------------------------------}
{ ITERM }
{ by Jeff Duntemann }
{ }
{ Interrupt-driven terminal program testbed }
{ }
{ V2.01 CP/M Turbo Pascal V2.0 }
{ Last Update 12/6/84 }
{ }
{--------------------------------------------------------------}
PROGRAM ITERM;
CONST BAUD_PORT = $00; { SIO Baud rate control port on 820-II }
CTRL_PORT = $06; { SIO control port on 820-II }
DATA_PORT = $04; { SIO data port on 820-II }
INT_LOC = $F800; { Address of SIO interrupt routine }
INT_BASE = $FF00; { Base of mode 2 interrupt vector table }
{ RING BUFFER INTERRUPT SERVICE ROUTINE }
{ This routine is an interrupt routine for incoming serial port data. }
{ This routine executes each time the SIO chip fills up with a complete }
{ data character from the RS232 line. The character is put in a ring }
{ buffer and a buffer pointer incremented. The buffer and pointer are }
{ absolute variables that were previously defined at a particular place }
{ in high memory. }
ROUTINE : ARRAY[0..29] OF BYTE =
($F5, { PUSH AF Save accumulator }
$E5, { PUSH HL Save HL register }
$F3, { DI Disable interrupts }
$2A,$19,$F8, { LD HL,(LAST_SAVED) Get current count }
$DB,$04, { IN A,(04H) Get the incoming character }
$77, { LD HL,A Store it in the buffer }
$23, { INC HL Bump insertion pointer }
$CB,$64, { BIT 4,H Make ring }
$28,$03, { JR Z,SIOINTL Relative jump 3 forward }
$21,$00,$C3, { LD HL,$C300 over reload of buffer head }
$22,$19,$F8, { LD (LAST_SAVED),HL SIOINTL: Save counter }
$E1, { POP HL Restore HL register }
$F1, { POP AL Restore accumulator }
$FB, { EI Re-enable interrupts }
$ED,$4D, { RETI Return from routine }
$00,$C3, { DW $C300 LAST_SAVED }
$00,$C3, { DW $C300 LAST_READ }
$00);
TYPE
STRING80 = STRING[80];
CODE_BLOCK = ARRAY[0..63] OF BYTE;
VECT_ARRAY = ARRAY[0..7] OF INTEGER;
VAR I,J,K : INTEGER;
CH : CHAR;
NOSHOW : SET OF BYTE;
PARITY : INTEGER; { 0=no parity; 1=odd parity; 2=even parity }
PARITAG : ARRAY[0..2] OF STRING[8]; { Holds parity tags }
OK : BOOLEAN;
HIBAUD : BOOLEAN; { TRUE = using 1200 baud, else 300 baud }
QUIT : BOOLEAN; { Flag for exiting the terminal loop }
DUMMY : STRING80;
{ The following variables all support the interrupt-driven ring buffer: }
INT_CODE : CODE_BLOCK ABSOLUTE INT_LOC; { Holds ring buffer serv. routine }
INT_VECT : INTEGER ABSOLUTE $FF02;
LAST_READ : INTEGER ABSOLUTE $F81B; { Offset of last char. read }
LAST_SAVED : INTEGER ABSOLUTE $F819; { Offset of last char. saved }
RINGPTR : ^CHAR ABSOLUTE $F81B; { ON TOP OF LAST_READ! }
VECT_TBL : VECT_ARRAY ABSOLUTE $FF00; { SIO interrupt jump tbl }
{<<<INCHAR>>>}
{ This function is called AFTER function INSTAT has determined that a char }
{ is ready to be read from the ring buffer. The char at LAST_READ/RINGPTR }
{ (the two are the same) is assigned to INCHAR's function value. Then the }
{ value of LAST_READ is bumped by one via SUCC. If the value of LAST_READ }
{ is found to have gone over the high ring buffer boundary of $CFFF to $D000 }
{ then LAST_READ is "rolled over" to become $C300 (the low boundary of the }
{ buffer) again. When LAST_READ "catches up to" LAST_SAVED (by being =) the }
{ ring buffer is considered empty. }
FUNCTION INCHAR : CHAR;
BEGIN
INCHAR := RINGPTR^; { Grab a character from the ring buffer }
LAST_READ := SUCC(LAST_READ); { Increment the pointer; check bounds: }
IF LAST_READ >= $D000 THEN LAST_READ := $C300 { Correct if it hits $D000 }
END;
{<<<INSTAT>>>}
{ This function determines if there is a new character to be read from the }
{ ring buffer. There are two pointers into the ring buffer: LAST_SAVED, }
{ and LAST_READ. LAST_SAVED is the address of the last character placed }
{ into the buffer by the SIO interrupt service routine. LAST_READ is the }
{ address of the last character read from the ring buffer. When the two are }
{ equal, the last character read was the last character saved, so we know we }
{ have read all the characters that have been placed into the buffer. Only }
{ when LAST_SAVED gets "ahead" of LAST_READ must we read characters from the }
{ ring buffer again. These two pointers chase each other around and around }
{ the ring. As the ring buffer is just a hair over 3300 bytes long, }
{ LAST_SAVED can get WAAAAY ahead of LAST_READ before there's trouble in }
{ River City. On the other hand, if this ever happens, there will be no }
{ warning. Just trouble. }
FUNCTION INSTAT : BOOLEAN;
BEGIN
IF LAST_SAVED <> LAST_READ THEN INSTAT := TRUE
ELSE INSTAT := FALSE
END;
PROCEDURE OUTCHR(CH : CHAR);
BEGIN { Loop until TBMT goes high }
REPEAT I := PORT[CTRL_PORT] UNTIL (I AND $04) <> 0;
PORT[DATA_PORT]:=ORD(CH) { Then send char out the port }
END;
PROCEDURE SET_7_BITS;
BEGIN
PORT[CTRL_PORT]:=$13; { Select write register 3 }
PORT[CTRL_PORT]:=$41; { 7 bits per RX char, enable RX}
PORT[CTRL_PORT]:=$15; { Select write register 5 }
PORT[CTRL_PORT]:=$AA { 7 bits per TX char, enable TX}
END;
PROCEDURE SET_8_BITS;
BEGIN
PORT[CTRL_PORT]:=$13; { Select write register 3 }
PORT[CTRL_PORT]:=$C1; { 8 bits per RX char, enable RX}
PORT[CTRL_PORT]:=$15; { Select write register 5 }
PORT[CTRL_PORT]:=$EA { 8 bits per TX char, enable TX}
END;
PROCEDURE SET_PARITY(PARITY : INTEGER);
BEGIN
PORT[CTRL_PORT]:=$14; { Select SIO Register 4 }
CASE PARITY OF { All 3: 16X clock, 1 stop }
0 : PORT[CTRL_PORT]:=$44; { 0=No parity }
1 : PORT[CTRL_PORT]:=$45; { 1=Odd parity }
2 : PORT[CTRL_PORT]:=$47; { 2=Even parity }
ELSE PORT[CTRL_PORT]:=$47; { Defaults to even parity }
END; { CASE }
END;
PROCEDURE INT_ENABLE;
BEGIN
PORT[CTRL_PORT] := $11; { Select write register 1 }
PORT[CTRL_PORT] := $18 { and turn interrupts on }
END;
PROCEDURE INT_DISABLE;
BEGIN
PORT[CTRL_PORT] := $01; { Select write register 1 }
PORT[CTRL_PORT] := $00 { and disable interrupts }
END;
{<<<INT_SETUP>>>}
PROCEDURE INT_SETUP;
BEGIN
FILLCHAR(INT_CODE,SIZEOF(INT_CODE),CHR(0)); { Zero array to hold routine }
FOR I := 0 TO 29 DO { Move the routine out of the }
INT_CODE[I] := ROUTINE[I]; { constant into the array. }
FOR I := 0 TO 7 DO VECT_TBL[I] := ADDR(INT_CODE);
INT_ENABLE; { Finally, enable SIO interrupts. }
END;
{>>>>INITSIO<<<<<}
PROCEDURE INITSIO(HIBAUD : BOOLEAN; PARITY : INTEGER);
BEGIN
SET_PARITY(PARITY); { Set parity }
SET_7_BITS; { Set SIO to 7 bits RX/TX }
IF HIBAUD THEN { Set baud rate: }
PORT[BAUD_PORT]:=$07 { 1200 baud code to baud port }
ELSE PORT[BAUD_PORT]:=$05; { 300 baud code to baud port }
WRITE('<Changing to ');
IF HIBAUD THEN WRITELN('1200 baud>') ELSE WRITELN('300 baud>')
END; { INITSIO }
FUNCTION GET_KEY : CHAR;
BEGIN
GET_KEY := CHR(BDOS(6,255))
END;
{>>>>CLEAR_BIT<<<<<<}
PROCEDURE CLEAR_BIT(VAR CH : CHAR; BIT : INTEGER);
VAR I,J : INTEGER;
BEGIN
I := NOT(1 SHL BIT); { Create a bit mask }
J := ORD(CH) AND I;
CH := CHR(J)
END;
{>>>>INIT_ITERM<<<<}
PROCEDURE INIT_ITERM;
BEGIN
NOSHOW:=[0,127]; { Don't display these! }
PARITY:=2; { Defaults to even parity }
PARITAG[0]:='No'; PARITAG[1]:='Odd'; PARITAG[2]:='Even';
HIBAUD := TRUE; { Defaults to 1200 baud }
INITSIO(HIBAUD,PARITY); { Do init on serial port A }
INT_SETUP { Init interrupt system }
END; { INIT_TERM }
BEGIN {**** ITERM MAIN ****}
LOWVIDEO;
INIT_ITERM; { Do inits on variables & hardware }
CLRSCR; { Clear screen }
QUIT:=FALSE; { Init flag for terminal exit }
REPEAT { Can only be exited by CTRL/E }
IF INSTAT THEN { If a char has come }
BEGIN { from the serial port }
CH := INCHAR; { Go get it from the port; }
CLEAR_BIT(CH,7); { Scuttle the parity bit; }
IF NOT (ORD(CH) IN NOSHOW) THEN WRITE(CH); { Write CH to the CRT }
END; { Incoming character handling }
CH:=GET_KEY; { See if a char was typed }
IF ORD(CH)<>0 THEN { If non-zero, char was typed }
CASE ORD(CH) OF { Parse the typed character }
5 : QUIT:=TRUE; { CTRL-E: Raise flag to exit }
17 : BEGIN { CTRL-Q: Step through parity }
IF PARITY=2 THEN PARITY:=0 ELSE PARITY:=PARITY+1;
INITSIO(HIBAUD,PARITY);
WRITELN('<NOW USING ',PARITAG[PARITY],' PARITY>')
END;
18 : BEGIN { CTRL-R: Toggle baud rate }
HIBAUD:=NOT HIBAUD;
INITSIO(HIBAUD,PARITY)
END;
26 : CLRSCR; { CTRL-Z: Clear CRT }
ELSE OUTCHR(CH); { Send all others to modem, }
END { CASE }
UNTIL QUIT;
INT_DISABLE; { Turn off SIO interrupts... }
END. { ITERM } { ...and blow this crazyhouse...}