home *** CD-ROM | disk | FTP | other *** search
-
-
- {--------------------------------------------------------------}
- { 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...}