Google
 

Trailing-Edge - PDP-10 Archives - decuslib10-08 - 43,50512/ttyio.b36
There are no other files named ttyio.b36 in the archive.
MODULE TTYIO=
BEGIN
!Module to do terminal I/O on TOPS-10
!
!Conditionals
!
COMPILETIME FTNONBLOCK=%VARIANT AND 1;
!On for non-blocking TTY I/O (use PSI)
COMPILETIME FTNETSPL=(%VARIANT AND 2) NEQ 0;
!On to support the LOG command in NETSPL
!Also forces on FTLOG and FTNONBLOCK
!COMPILETIME FTLOG=(%VARIANT AND 16) NEQ 0;
!On for LOG file support (set from library file)
!FTDET is also set in the library file
%IF FTNETSPL %THEN %(%ASSIGN(FTLOG,1))% %ASSIGN(FTNONBLOCK,1) %FI

COMPILETIME CHECKPOINTBUG=1;
!On if the FILOP. function .FOURB forces out the current block & does
!not back up after updating everything, resulting in 1 line of output
!+ a whole lotta nulls per disk block.  We get around this by using
!a CLOSE followed by an OPEN for APPEND (.FOCLS & .FOAPD)

!
! Require & Library files
!
%IF FTNONBLOCK
	%THEN REQUIRE 'INTR.REQ'
	%ELSE LIBRARY 'TBL'
	%FI;

!
!Table of Contents
!
FORWARD ROUTINE
TTYIN,
TTYINC,
LOGC,	!Log a character into the LOG file
LOGS, !Log an ASCIZ string
LOG;	!The LOG command (open a log file)
%IF FTNETSPL %THEN
FORWARD ROUTINE
TTYINI,	!Initialize TTY interrupts
LOGHANDLE;
%FI
%IF FTDET %THEN
FORWARD ROUTINE
DETACH,
TYPCHR,
TYPSTR,
TRMOP,
DET_TTY,
ATTACH_TTY,
UDX,
TTYCHK;
%FI

!
!Tell about conditionals
!
%IF FTNETSPL %THEN %INFORM('TTYIO for NETSPL')
%ELSE	%IF FTLOG %THEN %INFORM('TTYIO with LOG support') %FI
	%IF FTNONBLOCK %THEN %INFORM('TTYIO with PSI support') %FI
	%FI
%IF %SWITCHES(DEBUG) %THEN
	%WARN('TTYIO WITH /DEBUG')
	%FI

THIS_IS [TTYI] VERSION [1] EDIT [10]	DATE [30,OCT,79]

%(***	R E V I S I O N   H I S T O R Y

[10]	FRCUUO also does ^C on 7.00, so use ATTACH UUO
[7]	Only 1 buffer each way for LOG file, fix error messages for LOG cmd.
[6]	Make the first block get timestamped by initializing LFFLG nonzero
[5]	Make CHECKPOINTBUG write-around free I/O buffers instead of losing them
[4]	Put in DETACH support
[3]	Put in logging
[2]	Put in conditional for non-blocking
[1]	The Beginning

END	R E V I S I O N   H I S T O R Y		)%

!
! OWN storage
!
OWN LFFLG;

!
!Externals
!
EXTERNAL ROUTINE
	BOUT,
	WRITE,
	OUTPUT;
%IF FTNETSPL %THEN
EXTERNAL ROUTINE
	UDT,
	TSIGNL,
	TSICAN,
	TSTAMP,	!Time stamper
	COPY,
	RELEASE,
	TFB,
	FBINI,
	FPARSE,
	BUFFREE,
	EATSPACES;
%FI
%IF FTDET %THEN
EXTERNAL ROUTINE INTINI;
%FI

%IF FTNONBLOCK %THEN
EXTERNAL	RUN: REF PROCESS_BLOCK;	!Current process
%FI

!
!Literals
!
LITERAL TTYTMO_INTERVAL=1;	!check every time we wake up (1/3 sec)
LITERAL CNOPR=XWD(%O'13',%O'11'); !GETTAB for OPR terminal
LITERAL DV_TTY=%O'10000000';	!DEVCHR: device is a TTY
LITERAL _TOOUC=5;		!TRMOP: output a character
LITERAL _TOOUS=7;		!TRMOP: output an ASCIZ string
LITERAL _TOHPS=%O'1011';	!TRMOP: horizontal position of carriage
LITERAL LF=%O'12';
!
!Macros
!
%IF CHECKPOINTBUG %THEN
UNDECLARE %QUOTE CHECKPOINT;
MACRO CHECKPOINT(FB)=
	BEGIN	!The FILOP. function .FOURB sets the pointer to the next block
	EXTERNAL ROUTINE BUFFREE;
	CLOSE(FB);	!but .FOCLS followed by .FOAPD works correctly
	BUFFREE(.FB[FILE$O_BRH]);	!Free the I/O buffers
	BUFFREE(.FB[FILE$I_BRH]);
	OPEN_A(FB)
	END%;
%FI

!
!Global Data
!
%IF FTNONBLOCK %THEN
GLOBAL TTYBLK: FILE_BLOCK;
GLOBAL TTYINTBLK: INT_BLOCK;	!Interrupt block for TTY
%FI
%IF FTLOG %THEN
GLOBAL LOGBLK: FILE_BLOCK;
%FI
%IF FTDET %THEN
GLOBAL PRIOUT;	!Our terminal output device
GLOBAL TERMINAL; !Our terminal
GLOBAL DETINTBLK: INT_BLOCK;	!Interrupt block for ATTACH/DETACH
%FI

!
!Routines
!

GLOBAL ROUTINE TTYIN(PTR,MAXLEN)=
!Routine to input a string from TTY (or AUTO file)
! and log it on LOG file if any
BEGIN
LOCAL C;	!Last character
DECR LEN FROM .MAXLEN TO 1 DO
	SELECT (C=TTYINC()) OF SET
		[%O'15']:			TTYINC(); !Get trailing LF
		[%O'15',%O'12',%O'33',%O'7']:	(CH$WCHAR_A(0,PTR);RETURN .C);
		[OTHERWISE]:			CH$WCHAR_A(.C,PTR)
		TES;
END;

GLOBAL ROUTINE TTYINC=
!Routine to get a character from TTY
!Returns: character (right justified)
BEGIN
MACRO TTCALL(FUN,ADDR)=(BUILTIN MACHSKIP;MACHSKIP(%O'051',FUN,ADDR))%;
LOCAL C;	!Character

%IF FTNONBLOCK %THEN
	BEGIN
	LOCAL	TTYTMO: INT_BLOCK,	!Wake us up in case PSISER doesn't
		TTO;			!Handle for timer
	CLEARV(TTYTMO);			!Empty block means just wake us up
	WHILE TTCALL(5,C) EQL 0 DO
		BEGIN
		TTO=TSIGNL(TTYTMO,UDT()+TTYTMO_INTERVAL); !Set timer
		WAIT(TTYINTBLK);	!INCHSL
		TSICAN(.TTO)		!Remove timer request
		END;
	END;

%ELSE TTCALL(4,C);			!INCHWL
%FI

%IF FTLOG %THEN LOGC(.C) ; %FI

.C					!Return the character
END;
GLOBAL ROUTINE LOGC(CHAR)=
!Write a character to the LOG file (if any)
!CHAR: Character to write
!Returns: Character that was passed to it
BEGIN
%IF FTLOG %THEN			!Just return character if NOT FTLOG

IF .LOGBLK[FILE$NAME] NEQ 0 THEN
	BEGIN
	IF .LFFLG NEQ 0 THEN
		BEGIN
		LFFLG=0;
		TSTAMP();
		END;
	BOUT(LOGBLK,.CHAR);	!Write out the character
	IF .CHAR EQL LF THEN
		BEGIN
		LFFLG=1;
		CHECKPOINT(LOGBLK) !Update rib if end of line
		END
	END;
%FI

.CHAR	!Returned value
END;	!LOGC
GLOBAL ROUTINE LOGS(STR)=
!Log an ASCIZ string in the LOG file (if any)
!STR: address of ASCIZ string (if LH=0) or byte pointer (if LH nonzero)
BEGIN

%IF FTLOG %THEN				!If NOT FTLOG routine is a no-op.
IF .LOGBLK[FILE$NAME] NEQ 0 THEN
	BEGIN
	REGISTER C;
	IF .STR<LH> EQL 0 THEN STR=CH$PTR(.STR); !Make into byte pointer if not
	WHILE (C=CH$RCHAR_A(STR)) NEQ 0 DO LOGC(.C);
	END;
%FI

END;	!LOGS
GLOBAL ROUTINE LOG(PTR,ARGS)=
!The "LOG" command: Open a new LOG file
!PTR: Address of byte pointer to command string (returned updated past filespec)
!ARGS: Additional arguments from caller (not used)
BEGIN
%IF FTLOG AND FTNETSPL %THEN

MACRO PREFIX='LOG'%;		!Prefix for MSG macro

LOCAL LOGSAV: FILE_BLOCK;	!Save old contents of LOGBLK

ESTABLISH (LOGHANDLE,LOGSAV);	!Set up handler

IF .LOGBLK[FILE$NAME] EQL 0
	THEN FBINI(LOGBLK) 		!Init if first time
	ELSE	BEGIN
		CHECKPOINT(LOGBLK);	!Update RIB first
		MSG('[','','  Previous log file was: ');
		TFB(LOGBLK);		!Say what we are replacing
		TYPE(']',CRLF);
		END;
COPY(LOGBLK,LOGSAV,FB_LEN);
EATSPACES(.PTR);

LOGBLK[FILE$EXTENSION]=(%SIXBIT'LOG')^-18;	!Set defaults
LOGBLK[FILE$NAME]=%SIXBIT'NETSPL';	
FPARSE(LOGBLK,.PTR);

	BEGIN
	IF .LOGSAV[FILE$NAME] NEQ 0
	 THEN	BEGIN
		CLOSE(LOGBLK); !Do a CLOSE
		BUFFREE(.LOGBLK[FILE$I_BRH]);	!Free the I/O buffers
		BUFFREE(.LOGBLK[FILE$O_BRH]);	!
		END;

	LOGBLK[FILE$MODE]=_IOASC;	!ASCII always
	LOGBLK[FILE$I_NBUFF]=(LOGBLK[FILE$O_NBUFF]=1); !1 buffer for each
	LOGBLK[FILE$GODLY]=1;		!As much access as possible
	OPEN_A(LOGBLK);	!Open for append
	LOGS(UPLIT(CRLF));			!Force timestamp
	END

%ELSE TYPE('%',PPREFIX,'LNS  LOG file not supported')
%FI
END;	!LOG
%IF FTNETSPL %THEN
GLOBAL ROUTINE TTYINI=
!Initialize TTY interrupts
BEGIN
EXTERNAL	INTTBL: VECTOR;

CLEARV(TTYBLK);		!Zero the TTY block first
			!This also gives us channel 0 which is reserved for TTY
PRIOUT=UDX(%SIXBIT'TTY');	!Set up primary output
TTYBLK[FILE$DEVICE]=%SIXBIT'TTY';
OPEN_U(TTYBLK);
TTYINTBLK[INT$WHAT]=.TTYBLK[FILE$CHANNEL];
TTYINTBLK[INT$REASONS]=%O'200000';	!Input done only
TTYINTBLK[INT$OFFSET]=0;	!The first slot is reserved for us
TTYINTBLK[INT$PROCESS]=.RUN;
INTTBL[0]=TTYINTBLK;		!Store interrupt block for handler
INTERRUPTS(ADD,TTYINTBLK);
!!!INTERRUPTS(CLEARDEV,TTYINTBLK);
%IF FTDET %THEN
	BEGIN
	IF .DETINTBLK[INT$OFFSET] EQL 0 THEN INTINI(DETINTBLK);
		!Initialize block if not done already
	DETINTBLK[INT$WHAT]=_PCDAT;	!ATTACH & DETACH interrupts
	DETINTBLK[INT$SIGNAL_ARGS]=4;	!Signal REATTA
	DETINTBLK[INT$S_ALWAYS]=1;
	DETINTBLK[INT$STSCODE]=REATTA;
	DETINTBLK[INT$SEVERITY]=SS$_WARN;
	INTERRUPTS(ADD,DETINTBLK);
	INTERRUPTS(CLEARDEV,DETINTBLK);
	END %FI;
END; !TTYINI
ROUTINE LOGHANDLE(SIGNAL_ARGS,MECH_ARGS,ENABLE_ARGS)=
!Condition handler for the LOG command
!Standard condition-handler arguments
!ENABLE_ARGS[1] is address of LOGSAV (old logfile spec)
BEGIN
MAP ENABLE_ARGS: REF VECTOR;
MAP MECH_ARGS: REF VECTOR;
MAP SIGNAL_ARGS: REF VECTOR;

LOCAL ERRSTRING;	!Will hold address of error message
MACRO PREFIX='COL'%;	!Prefix for MSG macro

BIND LOGSAV=.ENABLE_ARGS[1]: FILE_BLOCK;
EXTERNAL ROUTINE
	UNWIND,
	TFB,
	COPY,
	ERTEXT;

SELECT .$CODE OF SET
[FILERR TO FILOPN,FPAERR TO FPAERR+%O'17']:
	BEGIN
	BUFFREE(.LOGBLK[FILE$I_BRH]);	!Free the I/O buffers
	BUFFREE(.LOGBLK[FILE$O_BRH]);	!
	COPY(LOGSAV,LOGBLK,FB_LEN);	!Restore log file block
	IF .LOGBLK[FILE$NAME] NEQ 0 THEN
		BEGIN
		LOGSAV[FILE$NAME]=0;	!Prevent infinite recursion if open fails
		OPEN_A(LOGBLK);	!Try to re-open old one
		END
	ELSE	BEGIN
		LOGBLK[FILE$NAME]=0;
		RELEASE(LOGBLK);	!Give channel back
		END;
	WRN_NCRLF('Can''t Open LOG file (');
	ERRSTRING=ERTEXT(.$CODE);
	TSTR(.ERRSTRING);	!Type error message
	TYPE(')',CRLF);
	IF .LOGBLK[FILE$NAME] NEQ 0 THEN
		BEGIN
		MSG('[','','  Old log file: ');
		TFB(LOGBLK);	!Re-open old one
		TYPE(' re-opened]',CRLF);
		END;
	UNWIND(.MECH_ARGS[MA_DEPTH]+1)	!Unwind thru caller
	END;
[OTHERWISE]:	RETURN SS$_RESIGNAL;
TES;
END;	!LOGHANDLE
%FI
%IF FTDET %THEN
GLOBAL ROUTINE DETACH(PTR,ARGS)=
!The "DETACH" command.
!Abstract:	Detach the job from the terminal & continue operation
!		If an argument is given to the command, it is the
!		terminal to direct output to, otherwise direct it nowhere.
!		Note that "TTY" is a valid argument, directing output to
!		the terminal we are about to detach from.
!		Output is done via TRMOP. so the terminal is avaliable
!		for other use.
!		Control is not returned until the terminal is reattached
!		but the program may run anyway if interrupt-driven.
!Formals:
!PTR:	Address of byte pointer to command string
!ARGS:	Additional arguments (ignored)
!Returns: nothing
BEGIN
EXTERNAL ROUTINE
TTYINI,
WAIT,
INTINI,
INTFREE,
RDSIXA,
GETARG;
LOCAL OUTUDX;
LOCAL DETBLK: INT_BLOCK;

IF GETARG(.PTR) EQL 1
THEN	BEGIN
	OUTUDX=UDX(RDSIXA(.PTR));	!Get device name & convert to UDX
	IF TTYCHK(.OUTUDX)
		THEN PRIOUT=.OUTUDX	!Use if a valid TTY
		ELSE	BEGIN
			UNDECLARE %QUOTE PREFIX;
			MACRO PREFIX=%ASCIZ'DET'%;
			!Prefix for following messages
			WRN('"DETACH" argument is not a TTY');
				INFO('Job not detached');
			RETURN
			END
	END
ELSE	PRIOUT=0;			!No terminal output at all
DET_TTY();
INTERRUPTS(CLEARDEV,DETBLK);	!Don't enable until now or we get one right away
END; !DETACH
GLOBAL ROUTINE TYPCHR(CHR)=
!Routine to type a character on the output terminal
!CHR: character to type (right justified)
IF .PRIOUT NEQ 0 THEN
	TRMOP(_TOOUC,.PRIOUT,.CHR);


GLOBAL ROUTINE TYPSTR(STR)=
!Routine to type an ASCIZ string on the output terminal
!STR: address of ASCIZ string
IF .PRIOUT NEQ 0 THEN
	TRMOP(_TOOUS,.PRIOUT,.STR);

GLOBAL ROUTINE POS=
!Routine to return carriage position of output terminal
!Returns: horizontal position of carriage (or what monitor thinks it is)
IF .PRIOUT NEQ 0 THEN
	TRMOP(_TOHPS,.PRIOUT,0);

GLOBAL ROUTINE TELLOPR(STR)=
!Routine to type a string on OPR: device (may be remote OPR:)
BEGIN
LOCAL OPRDEV;
EXTERNAL ROUTINE GETTAB,UDX;

IF (OPRDEV=GETTAB(CNOPR)) NEQ 0
	THEN TRMOP(_TOOUS,UDX(.OPRDEV),.STR);
END; !TELLOPR

ROUTINE TRMOP(FUN,DESIGNATOR,ARG)=
!Routine to do a TRMOP. UUO
!FUN: Function code (1st argument to TRMOP.)
!DESIGNATOR: UDX of terminal (2nd argument to TRMOP.)
!ARG: Third argument to TRMOP.
BEGIN
REGISTER R;
LOCAL TRMOP_BLOCK: VECTOR[3];
TRMOP_BLOCK[0]=.FUN;
TRMOP_BLOCK[1]=.DESIGNATOR;
TRMOP_BLOCK[2]=.ARG;
R=TRMOP_BLOCK;	R<LH>=3;	!R <- len,,addr
CALLI(R,%O'116');		!TRMOP. UUO
END;
ROUTINE DET_TTY=
!Routine to detach the job from the terminal
BEGIN
REGISTER R;
LOCAL FRCBLK;

R=-1; CALLI(R,%O'115');	!TRMNO. of our terminal
IF .R EQL 0 THEN RETURN 0; !Already detached?
R=(.R<0,9>+%O'400000')^18;
CALLI(R,%O'104');	!ATTACH (our line to nothing!)
END; !DET_TTY

GLOBAL ROUTINE ATTACH_TTY(DESIGNATOR)=
!Routine to attach our job to the designated terminal (in user mode)
BEGIN
REGISTER R;
LITERAL AT_UUM=%O'200000000000';	!Force into user mode
LITERAL AT_SLF=%O'777777';		!Our job

R=AT_UUM+AT_SLF;			!Our job # + user mode
R<18,9>=.DESIGNATOR;			!Bottom 9 bits of UDX is unit #
CALLI(R,%O'104')			!ATTACH UUO
END; !ATTACH_TTY
GLOBAL ROUTINE UDX(NAME)=
!Routine to get the Universal Device Index of a device
!NAME: Sixbit name of device
!Returns: UDX of device
BEGIN
REGISTER R;
R=.NAME;
CALLI(R,%O'127');			!IONDX. UUO
.R					!Return the UDX as value
END; !UDX

ROUTINE TTYCHK(DESIGNATOR)=
!Make sure a device is a TTY
!DESIGNATOR: Device designator to check
!Returns: 1 if a TTY, 0 otherwise
BEGIN
LITERAL DV_TTY=%O'10000000';	!Bit returned from DEVCHR for TTY
REGISTER R;
R=.DESIGNATOR;
CALLI(R,4);	!DEVCHR UUO
(.R AND DV_TTY) NEQ 0	!Return true if bit is on
END;	!TTYCHK
%FI
END ELUDOM