perm filename IMPSER.MAC[IP,NET] blob sn#702374 filedate 1983-03-14 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00113 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00017 00002		TITLE IMPSER 	 IMP DATA TRANSFER LOGIC	V 1607/607	  VIMPSR  IMPSER
C00018 00003		MFE Revision History
C00027 00004	PARAMETERS, DEFINITIONS  TIMBUF
C00028 00005	NOWAITS<  IMPDDB DEVNAM DEVCHR DEVIOS DEVSER DEVMOD DEVLOG DEVBUF DEVIAD DEVOAD DEVSTS DEVSTA DEVXTR DEVEVM DEVPSI DEVESE DEVHCW DEVCPU DEVJOB DEVCTR
C00030 00006	\
C00032 00007		subttl	Host to IMP leader definition  $I$Old %First %Last %Len %OffSt %First im.pri im.trc im.oct HTISiz HTIWds HTIMax maxmes newfrm
C00039 00008		subttl	HDT - Host Data Table entry description  HS.Rfn HS.Max HS.Dwn
C00042 00009	INPUT INTERRUPT SERVICE
C00045 00010	\
C00047 00011	NOWAITS<  IMPISR
C00049 00012	INPUT INTERRUPT SERVICE  IMPIN IN0 IN1 IN2 IN3 INON IN5 in6 IN7
C00053 00013	HERE ON BLKI RUNOUT AT INTERRUPT LEVEL.  T POINTS TO THE LAST  IMPIND IND1 IND2 IND3 ind4
C00056 00014	HERE AT INTERRUPT LEVEL UPON RECEIPT OF THE END OF A MESSAGE.  T1  IMPEIM eim1 EIM4 EIM5 EIM6 EIM7
C00059 00015	mes10==q4nops	[96bit] must be defined before second pass  mes10 MESDSP MESDSN MES0 MES4 MES3 MES6
C00061 00016	HERE AT INTERRUPT LEVEL ON RECEIPT OF A BUFFERED DATA MESSAGE  MES00
C00065 00017	HERE IF BAD LINK NUMBER  MES07 mes07a mes08
C00066 00018	repeat 0,<	 try not using this  CHKBUF CHKBF1 CHKBF2
C00069 00019	HERE ON TYPE 1 MESSAGE			ERROR IN PREVIOUS LEADER  mes1 MES2 MES7 mes10 MES9
C00071 00020	HERE ON TYPE 5 MESSAGE...		RFNM  MES5 MES8 MesErr
C00072 00021	INPUT STREAM MANIPULATION 
C00074 00022	IFE FTIMP32,< Next 3 pages  INBYT0 InByt1 INBYT2 INBYT3 InBy36 INBYT4
C00077 00023	HERE TO GET 5TH BYTE  INBYT5 INBYT6 INBYT7 INBYT8 INBYT9
C00079 00024	SUBROUTINE TO SET UP NEXT BUFFER FOR INBYTE  INBYTG
C00080 00025		subttl	InByte  InBytL InByte InBytM INBYTC INBYTX
C00083 00026	SUBROUTINE TO GET ANOTHER BUFFER FOR INPUT UNPACKING ROUTINES  INBUFR
C00084 00027	SUBROUTINE TO ADVANCE POINTERS TO THE NEXT INPUT BUFFER AND RETURN  INPBFX INPBF1 inpbf2
C00086 00028	OUTPUT INTERRUPT SERVICE
C00090 00029		MESSAGE QUEUES:
C00092 00030	HERE AT INTERRUPT LEVEL WHEN END-OF-MESSAGE HAS BEEN SENT.  IMPEOM EOM1 Eom2
C00094 00031	HERE ON BLKO RUNOUT AT INTERRUPT LEVEL.  OLD BLKO POINTER IN AC T.  IMPOND OND1
C00096 00032	SUBROUTINE TO START UP OUTPUT  OUTGO1 OUTGO2 NOPiow MS.NOP HGDiow ms.hgd
C00098 00033	IMPOUT	...	OUTPUT CONTROL ROUTINES  IMPQLN IMPMES IMPMS1 IMPMS3
C00100 00034	\
C00101 00035	SUBROUTINE TO MAKE VARIOUS TESTS AND, IF THEY ARE  OTBYTE OTBErr
C00104 00036	SUBROUTINE TO APPEND AN 8 BIT BYTE TO THE OUTPUT STREAM.  OUBYT0 CpByte OUBYT2 OUBYT3 OuBy36 OUBYT4
C00106 00037	HERE TO PUT FIFTH BYTE  THE ONE THAT WRAPS AROUND THE  OUBYT5 OUBYT6 OUBYT7 OUBYT8 OUBYT9
C00108 00038	SUBROUTINE TO GET ANOTHER BUFFER FOR OUTPUT, SET UP POINTERS,  OUBYTG OUBYTH
C00110 00039	 routine to put one character into the monitor internal buffers while  OuBy2 OuBytD OuByte OuBy6
C00112 00040	SUBROUTINE TO SET UP AN OUTPUT BUFFER  OUBUFR OUBUF2 OuBuf3
C00114 00041	 subroutine to decide if there's enough buffer space to send a message
C00116 00042		subttl	ImpMak  ImpMak ImpMa1 ImpMa2 ImpMa3 ImpMa4 MakErr MakEr1
C00124 00043	 subroutine to queue a TCP message for output from interrupt level.
C00126 00044	SUBROUTINE TO APPEND AN ASSEMBLED BUFFER TO AN EXISTING STREAM.  OUTBFX
C00128 00045	CLOCK LEVEL STUFF  IMPSEC
C00132 00046	HERE TO TEST AN IMP  IMPCL2 ImpCl4 ImpCl5 ImpCl6
C00134 00047	NOWAITS<  ImpRem FstRem RemDsp LstRem RemBSL RemCNS RemCnO
C00136 00048	HERE IF IMP(OR INTERFACE) DEAD OR GOING DOWN  DEDIMP DWNIMP FLTIM0 FLTIMP FLTIM1 Q4NOPS Q4Nop0 Q4NOP1
C00139 00049	ROUTINES TO REQUEST IMP PROCESSING AT CLOCK LEVEL.  IMP I/O ROUTINES  RQIITI RQTOIO RQIITO RQTIIO IRQSET
C00141 00050	ROUTINE CALLED BY CLOCK1 ON ANY CLOCK TICK DURING WHICH IMPRQF IS  IMPTIK IMPTK1 IMPTK2 IMPTKA IMPTK3 imptkx IMPTK4 IMPTK5 IMPTKT
C00145 00051		subttl	initialization code  INI INI1 ZERTAB ZERTBN
C00147 00052	NOWAITS< Several pages of NOWAITS start here  OCLSE IMPDSP
C00149 00053	HERE FROM UUOCON ON AN INPUT UUO  INPT INPT01 INPT02
C00151 00054	HERE TO GET AN INPUT BYTE  INPT17 INPT18
C00153 00055	HERE WHEN STREAM EXHAUSTED BEFORE USER BUFFERS  INPT19 INPT20 INPT21 INPT09 INPT11 INP11A INPT13 Inpt14 INPT12 ADVIBF
C00157 00056	HERE ON OUTPUT UUO FROM UUOCON  OUTPT OUT01 OUT01A OUT02
C00159 00057	TEST FOR ALLOCATION  OUT022 OUT025 OUT026 OUT05 OUT051
C00161 00058	HERE WHEN ALL THROUGH  OUT055 OUT07
C00162 00059	HERE IF IMP OFF LINE OR SOME SUCH  OUT09 OUT06 OUT10 OUT102 OUTAIO Outai1
C00164 00060	ROUTINE TO WAIT FOR INTERRUPT ACTIVITY.  RETURNS WHEN WOKEN AT  IMPW60 IMPWAT IMPWA1
C00166 00061	Last in series of NOWAITS pages  IMPREL IMPAIO IMOR10 IMOR20 IMOR21 IMOR30 IMOR31 IMOR50
C00169 00062	 subroutine to tell the input code about new data.  ImpNew
C00170 00063	 subroutine to handle wake up after allocation has increased.  checks  AlcNew
C00171 00064	ROUTINE TO WAKE THE JOB AT INTERRUPT LEVEL  IMPWAK IMPWK1 IMPHNG IMPIOD
C00173 00065	SUBROUTINE TO DETERMINE IF A DDB IS THAT OF AN IMP AND WHETHER OR  IMPDEV IMPDV1 IMPRES IMPRS1
C00176 00066	TELNET	...	TELETYPE ROUTINES  TTYTST
C00179 00067	SUBROUTINE TO SET UP A CROSSPATCH BETWEEN AN IMP DDB AND A LOCAL  IMPTTY TTIDET IMPATT
C00182 00068	ROUTINE TO TRANSFER CHARACTERS FROM A TTY INPUT BUFFER TO AN IMP OUTPUT  IMPTTI TTIGO TTINCH TTIMOR TTISVD TTICMN
C00186 00069	HERE ON IMP OUTPUT ERROR  TTISV1 TTIDON TTIIAC TTICMD
C00187 00070	ROUTINE TO TRANSMIT CHARACTERS FROM THE IMP INPUT BUFFER TO THE TTY  IMPTTO TTO0
C00188 00071	HERE TO TRANSFER A CHARACTER.  TTO05 TTO2 TTO2A TTO3 TTO4 TTO5
C00191 00072	HERE FROM IMP CLOCK LEVEL WHEN A MESSAGE HAS BEEN RECEIVED  ITTYIN
C00192 00073	HERE TO TRANSFER A CHARACTER FROM THE IMP INPUT BUFFER TO  TTYIN0 TTYIN2 TTYI2A TTYIN1 TTYIN6 TTYIN3 TTYIN4 TTYIN5
C00196 00074	HERE WHEN NO MORE DATA FROM IMP CONNECTION.  (interrupts always off)  TTY4 TTY3 TTY4B TTY4c TTY4A
C00198 00075	ROUTINE TO TRANSMIT CHARACTERS FROM A TTY OUTPUT BUFFER TO AN IMP  IMPTYC
C00200 00076	HERE FROM SCNSER AT UUO AND CLOCK LEVEL TO START  IMPTYP IMPTY2 ImpTy0 ImpTy1 IMPTY7 IMPTY3 IMPTY5 ITYSTO
C00205 00077	HERE WHEN PROCESSING A TELNET CONTROL STREAM (2 OR 3 CHARS)  IMPTY8 IMPTY9 XMTQIT TTYRn0 TTYRNM TTYRN1
C00208 00078	SEE IF CROSSPATCHED LINE CAN TAKE MORE (MIC) (163)  MICIMP MICIM0
C00210 00079	TELNET CONTROL RECEIVER (FROM IMP)  TTYIAC IACTAB IACFST
C00213 00080	SOME TELNET COMMAND PROCESSING ROUTINES  IACIP IACAO IACAYT IACEC IACEL IACDM
C00216 00081	PTLNop:	pointr	TelWrd(f),OptChr	 pointer for telnet option character  PTLNop IAWWDD IACAOS IACNOP IACNIT
C00218 00082	THE FOLLOWING CODE HANDLES THE TELNET ECHO OPTION.  TUNECH TUYECH TSYECH TSNECH
C00221 00083	THE FOLLOWING CODE HANDLES THE TELNET SUPRESS GO-AHEAD OPTION.  WILSGA WNTSGA DOSGA DNTSGA
C00223 00084	ROUTINES TO PROVIDE THE PROPER 'WILL', 'WONT', 'DO', 'DONT' RESPONSES.  WILL WONT DO DONT TLNRSP TLNOCH IGNXPF IGNXPT CLRXPT
C00226 00085	TELNET CONTROL TRANSMITTER (FOR IMP)  IMPTOI ITOTAB ITORSP PTOFnc ITOTEL ITOQT1 ITOQIT ITOIAC
C00230 00086	ITOSDM:	HRLM	F,(P)		SAVE DDB ADDRESS OVER SCNSER CALL  ITOSDM ITOUDM ITOAO ITONEG
C00233 00087	THESE ARE THE ECHO NEGOTIATION ROUTINES:  OUDECH OUEECH
C00235 00088	THE STATE OF SERVER ECHO IS DETERMINED PRIMARILY BY THE STATE OF THE  OSWECH OSWEC1 OSXECH
C00239 00089	HERE ARE THE SGA NEGOTIATION ROUTINES. WE LOOK AT EACH ATTEMPT AT  IODSGA IOESGA IOWSGA IOXSGA ITOWIL ITOWNT ITODO ITODNT ITOXMS ITOWL1 ITOXM1
C00242 00090	ROUTINES TO BE CALLED TO CHECK WHETHER OR NOT A SGA NEGOTIATION  SETFSG CLRFSG SETLSG CLRLSG
C00244 00091	DATA FOR TELNET CONTROL AND SUBROUTINES  WWDDTB ITOWDB
C00246 00092	ASSEMBLE ALL THE DISPATCH TABLES FOR THE VARIOUS TELNET OPTIONS.  OPTNTB NTNOPS WILTBL WILTBO WNTTBL WNTTBO DOTBL DOTBO DNTTBL DNTTBO
C00248 00093	THIS IS THE MAPPING TABLE FROM TTY CHARS TO TELNET COMMANDS, USED BY  TELTAB TELLEN
C00250 00094	 subroutine to appropriately tweak everybody when an URG comes in  TTyUrg TTUUrg TTSUrg
C00252 00095	SUBROUTINE TO ALLOCATE A LINE NUMBER FOR IMPS CONNECTING  ITYGET ITYGE1 ITYGE2
C00254 00096	SUBROUTINE TO RELEASE AN ITY.  NORMALLY CALLED BY THE NCP AFTER  ITYREL ITYRE2
C00255 00097	HOST TABLE:
C00257 00098	HOST CONTROL AND QUEUEING SUBROUTINES  HstEOM EOM03 EOM05 EOM02
C00260 00099	SUBROUTINE TO ALLOW TRANSMISSION TO A HOST(PREVIOUS MESSAGE TO  GtRFNM
C00261 00100	SUBROUTINE TO INITIALIZE THE HOST TABLE.  HSTINI
C00262 00101	SUBROUTINE TO CHECK TO SEE THAT A HOST IS GOOD AND, IF SO,  Go1822 Gx1822
C00265 00102	repeat 0,<	 we may want to check for lost RFNMs, but for now i'm  HOSTCK HOSTC0 HOSTC5 HOSTC6
C00268 00103	SUBROUTINE TO FLAG A HOST AS BAD.   CALLED FROM INTERRUPT OR CLOCK  HOSTBD BadLp HostB1
C00270 00104	repeat 0,<	 not used yet  HSTRES
C00271 00105	repeat 0,<	 not used yet.  HSTCLR HSTCL1 HSTCLU HSTCL3 HSTCL4 HSTCL5 HSTCL6
C00275 00106	SUBROUTINE TO ENTER A HOST IN THE TABLE.  HSTNEW HSTNE0 HSTNE4 HSTNE1 HSTNE3 HSTNE5
C00278 00107	SUBROUTINE TO ENSURE THAT A HOST IS IN THE TABLES  HSTCHK HSTCK1 HSTNXT HSTNX0 HSTNX1
C00280 00108	SUBROUTINE TO DETERMINE WHETHER ANY IMP DDB REFERENCES A GIVEN HOST.  HSTUSE HSTUS1 HstUs2
C00282 00109	 routine to get the pseudo DDB for input, clear it out, and return  GetPDB
C00283 00110	 routine to take the host in T1 and make all entries in the retransmission  FixRTQ FixLup
C00285 00111	IFN DEBUG,<  INDERR IMPWCP
C00286 00112		SUBTTL	IMPSER DATA BASE  ZERO INHALT IBFHLT INBUFP MESSIZ impihd LEADER BUFADR FLTFLG STOPFL TESTHS OLINKP NowOut IMPQ IMPQTP IMPQTC IMPQPP IMPQPC IMPREQ CLKSEC DEDFLG IMPUP OKFLAG PSDDDB IMkDDB HSTLAS LASCHK HOSTS HSTCNT ZERON
C00290 00113	IMPLIT:	$LIT  IMPLIT IMPEND
C00291 ENDMK
C⊗;
	TITLE IMPSER 	 IMP DATA TRANSFER LOGIC	V 1607/607	 ; VIMPSR ;⊗ IMPSER
	SUBTTL R SUNDBERG/RLS/EAT/EW13	--	AUGUST '74

	SEARCH	F,S
	search	NetDef			; get standard IMP definitions
	search	MacTen			; make writing code easier.
					; (must be AFTER NetDef)

	$RELOC
	$HIGH


XP	VIMPSR,1000			; first IP/TCP supporting version


IMPSER:	ENTRY	IMPSER		;TO LOAD ON LIB SEARCH


	external NetSub		; make sure to load support routines
;	MFE Revision History

;	04 May 82, BCH
;	Changed EXCTUUs to EXCTUX or EXCTXU.
;
;	07 May 82, BCH
;	Made STOPFL global for NETCON.
;	Changed BAA stopcode to BAL to remove conflict with DTESER definition.

;	LOCAL REVISION HISTORY

;(147)	17-Apr-78, Jim McCool
;	Edit Type: Enhancment (in IMP code)
;	Modules Affected: IMPSER, SCNSER, COMCON, NETCON
;	Remove all support for old TELNET protocol.  Also add
;	a forced command invoked when an attempt is made to
;	perform an ICP on socket 1 which will tell the user that
;	we no longer support the old style TELNET.
;	Note that the forced command is only temporary until
;	a final announcement is made regarding the termination
;	of support for old style TELNET by DCA.

;(150)	17-Apr-78, Jim McCool
;	Edit Type: Enhancment
;	Modules Affected: IMPSER
;	Add support for non-blocking I/O and PSI signaling of
;	same to the ARPANET code.

;(157)	10-Jun-78, Bob Baker
;	Modules Affected: IMPSER
;	Edit Type: Bug Fix (in IMP code)
;	Symptom: TELNET INTERRUPT PROCESS (IP) code is ignored
;	when received from one of the AFSCNET sites.
;	Diagnosis: Those sites send IP within a TELNET SYNC
;	SEQUENCE, during which ordinary characters are ignored
;	but control codes like IP should take effect.  In IMPSER
;	the IP is processed and replaced with a CTRL-C, but then
;	a test is made for SYNC sequence in progress which causes
;	the CTRL-C to be thrown away.
;	Cure: If a char is to go to TTY as a result of a TELNET
;	control code coming in, send the char even if a SYNC
;	sequence is in progress.

;(162)	15-Jun-78, Jim McCool
;	Modules Affected: IMPSER
;	Edit Type: Enhancement (in IMP code)
;	Make SERVER TELNET handler know about TELNET ARE YOU THERE
;	command if the TTY isn't in RT COMPATIBLITY mode, send A
;	CONTROL-T to it on receipt of a TELNET AYT command.

;(163)	21-Jul-78, Jim McCool
;	Modules Affected: IMPSER, SCNSER
;	Edit Type: Enhancement
;	Symptom: MIC thinks that lines crosspatched to the network
;	can't take more input when in fact they almost always can.
;	Diagnosis: MIC functions in SCNSER know nothing about the ARPA
;	NET, specifically, the MIC STATUS function only indicates that
;	that a job can take more input if it is in a TI WAIT STATE.
;	Cure: In the MIC get status routine, TOPMGT, in SCNSER
;	add a check to see if the TTY line is crosspatched to the
;	ARPANET and if so, call a new routine, MICIMP, in IMPSER
;	to see if the connection can take more input.
;
;	Note:  logically, this modification should be placed in
;	the routine UJBSTX in PTYSER since this is the routine that
;	standard DEC SCNSER calls to determine if a line can take
;	more input.  It was decided that the modification should
;	go into SCNSER itself since PTYSER is as yet unmodified.

;(165)	12-MAR-78 JIM MCCOOL
;	MODULES AFFECTED:  S,IMPSER,CLOCK1,SCHED1
;	EDIT TYPE: ENHANCMENT
;	ADD AN IMP WAIT SATISFIED QUEUE, ISQ, AND A ROUTINE
;	IN CLOCK1, STIIOD, TO REQUE JOBS IN IWTQ TO THIS QUEUE.
;	THIS QUEUE IS ADDED SO A REQUEUE CAN BE FORCED ON A JOB
;	COMING OUT OF IW WAIT.
;	CHANGE IMPIOD IN IMPSER TO CALL STIIOD INSTEAD OF SETIOD.

;[96bit]7-Jul-80, Jim McCool
;	Modules effected: S, COMDEV, I, IMPINT, IMPSER, NETCON
;	Edit type: Enhancement
;	Add the CMU modifications to the ARPAnet code to support
;	96 bit leaders in the HOST-IMP protocol

;(234)	6-Jan-81, Jim McCool
;	Edit type: enhancement
;	Modules affected: IMPSER, NETCON
;	Description: Add code to count the occurences of the various types of
;	incomplete message transmission errors.

;(235)	8-Jan-81, provan
;	edit type: bug fix
;	modules affected: ImpSer
;	description: repair labels so a trival edit like (234) doesn't
;	require a careful search of the entire file: add a label MesErr
;	for common error handling code so no one jumps into Mes9 code.

;(236)	8-jan-81, provan
;	edit type: enhancement
;	modules affected: I, ImpSer, NetCon
;	symptom: imp code is clumsy to compile.
;	diagnosis: I.mac is not a universal file.
;	cure: bring the imp code into the 1970's by making i.mac a universal.

;(252)	8-jan-81, provan
;	module affected: ImpSer
;	symptom: PJ0 stopcd
;	diagnosis: FTPSRV job is getting killed manually (KSYS).
;		in the process, the controlling IMP is considered
;		the terminal, and not killed off.  however, since
;		FTPSRV is really handling terminal IO, the first
;		time something happens after FTPSRV is killed
;		off, the monitor tries to schedule the dead job.
;	cure: quick fix: at IMPDEV, use control of keyboard or
;		printer as criteria for "controlling a job"
;		rather than the ttyjob bit.
;	note: it doesn't look to me like FTPSRV really needs
;		an ITY at all.  it should be able to run detached.

;(270)	12-july-82, provan
;	modules affected: ImpInt, ImpSer, Common, ScnSer
;	description: insert some bug fixes discovered at
;	lll-mfe in the arpanet code.

;(271)	6-august-82, provan
;	modules affected: ScnSer, ImpSer
;	symptom:
;		monitor hangs in a tight loop around TopMt3.
;	diagnosis:
;		terminal is not unlocked at RECINT + N because
;		RECUNI takes an illegal skip return.
;	cure:
;		remove all skip returns from RECINU.
;		instead, allow LDBISR shut down data from
;		the IMP just like with any other terminal.
;	note:
;		this revealed some problems in the way incoming
;		traffic was being handled when buffers began to
;		run out.  the allocation was continually updated
;		(even though we want to make sure not to give out
;		any more allocation until the situation has been
;		handled) and input would not be started back up
;		after more buffer space was finally available.

; End of Local Revision History
SUBTTL PARAMETERS, DEFINITIONS ;⊗ TIMBUF

TIMBUF==↑D10		;AVERAGING PERIOD FOR BUFFER FREE COUNT(SECONDS)

NOWAITS< ;⊗ IMPDDB DEVNAM DEVCHR DEVIOS DEVSER DEVMOD DEVLOG DEVBUF DEVIAD DEVOAD DEVSTS DEVSTA DEVXTR DEVEVM DEVPSI DEVESE DEVHCW DEVCPU DEVJOB DEVCTR
	subttl	prototype IMPDDB

	$LOW
;PROTOTYPE IMP DDB
IMPDDB:: PHASE	0
DEVNAM:: SIXBIT	\IMP0\
DEVCHR:: EXP	UBUFL+1!DEPCBC!dvc2io	;[tcp] can do input and output
					;[tcp]  at the same time.
DEVIOS:: EXP	0
DEVSER:: EXP	IMPDSP
DEVMOD:: xwd	dvin!dvout!DVSWPW , <1←A>!<1←AL>!<1←PimMod>
DEVLOG:: SIXBIT	\\
DEVBUF:: EXP	0
DEVIAD:: R	,, 0
DEVOAD:: R	,, 0
DEVSTS:: EXP	0
DEVSTA:: .TYIMP ,, DEPEVM		;NO EXEC VIRTUAL MEMORY	DK/ET/EW	DEC 75
	
DEVXTR:: EXP	0		
DEVEVM:: EXP	0
DEVPSI:: EXP	0		;FOR PSI SYSTEM
DEVESE:: EXP	0		;(150)
DEVHCW:: EXP	0		;(150)
DEVCPU:: EXP	0		;FOR 7.01
DEVJOB:: EXP	0		;DUMMY FOR 6.02			DK/DEC 75
DEVCTR:: EXP	0
	EXP	0		;IMPIOS/IMPCLR			DK/MAR 75

	BLOCK	IMPDDS-.

	DEPHASE
	$HIGH
>;NOWAITS
IFWAITS<
EXTERN IMPDDB,DEVNAM,DEVCHR,DEVIOS,DEVSER,DEVMOD,DEVLOG,DEVBUF,DEVIAD,DEVOAD
;Other words in the TOPS-10 DDB shouldn't be used in WAITS.
>;IFWAITS
COMMENT \
MODE 2 IS 8-BIT BYTE MODE WITH BREAK ON ANY INPUT BYTE.
MODE 6 IS 8-BIT BYTE MODE WITH BREAK AFTER 32 BITS (FULL WORD) OR
END-OF-FILE ONLY.

THE DVSWPW BIT IN DEVMOD(F) IS A SIGNAL TO THE SCHEDULER THAT THE
JOB IS TO WAIT IN THE INDEFINITE SWAPPABLE WAIT STATE (IWT) RATHER
THAN THE NORMAL UNSWAPPABLE I/O WAIT STATE (IOW).  THIS DOES MEAN,
HOWEVER, THAT ALL DATA TRANSFER TO AND FROM USER BUFFERS MUST BE DONE
AT UUO LEVEL.

THE DEPCBC BIT IN DEVTYP(F) SIGNALS THE BYTE-COUNT ROUTINES IN UUOCON
THAT A BYTE POINTER IS PASSED IN THE LH OF THE THIRD WORD OF A USER
BUFFER FOR USE IN COMPUTING A CORRECT BYTE COUNT, RATHER THAN THE
TRADITIONAL NON-FEATURE OF COMPUTING THE NEXT HIGHER MULTIPLE OF THE
NUMBER OF BYTES PER WORD.
\
	subttl	Host to IMP leader definition ;⊗ $I$Old %First %Last %Len %OffSt %First im.pri im.trc im.oct HTISiz HTIWds HTIMax maxmes newfrm


; the following are byte definition for storing and retrieving
;  information from a Host to IMP (or IMP to host) leader, as
;  described in report 1822, "Specifications for the Interconnection
;  of a Host and an IMP.


; first, define a helpful macro which takes three arguments:
;	name of byte field
;	first bit of byte field (first bit of leader is 1) in decimal
;	last bit of byte field in decimal
IFE FTIMP32,<
define	IMPDef ($Nam,$First,$Last),
    <
	$I$Old==10			; get old radix
	radix	10			; go into decimal
	%First==<$First>-1		; get first bit, zero origined
	%Last==<$Last>-1		; and last bit, too
	%Len==<%Last-%First>+1		; compute length
	%OffSt==%First/ful.wd		; compute offset into leader
	%First==%First-<%OffSt*ful.wd>	; compute first bit of field in
					;  this pdp-10 word.

	ifg %Last-<<%OffSt+1>*ful.wd>,<	; is the last within the word?
		; print friendly message
printx	? byte '$Nam' in 1822 leader is not all in one pdp-10 word (IMPSER)
	   >
	ifle %Last-<<%OffSt+1>*ful.wd>,<; or does it all fit?
		; all fits.  define a byte pointer
		DefFd.	'$Nam',%OffSt,%First,%Len
	   >
	radix	$I$Old			; back to old radix
	purge	$I$Old,%First,%Last,%Len,%OffSt
   > ; end of IMPDef macro
>;IFE FTIMP32

IFN FTIMP32,<
define	IMPDef ($Nam,$First,$Last),
    <
	$I$Old==10			; get old radix
	radix	10			; go into decimal
	%First==<$First>-1		; get first bit, zero origined
	%Last==<$Last>-1		; and last bit, too
	%Len==<%Last-%First>+1		; compute length
	%OffSt==%First/net.wd		; compute offset into leader
	%First==%First-<%OffSt*net.wd>	; compute first bit of field in
					;  this pdp-10 word.

	ifg %Last-<<%OffSt+1>*net.wd>,<	; is the last within the word?
		; print friendly message
printx	? byte '$Nam' in 1822 leader is not all in one pdp-10 word (IMPSER)
	   >
	ifle %Last-<<%OffSt+1>*net.wd>,<; or does it all fit?
		; all fits.  define a byte pointer
		DefFd.	'$Nam',%OffSt,%First,%Len
	   >
	radix	$I$Old			; back to old radix
	purge	$I$Old,%First,%Last,%Len,%OffSt
   > ; end of IMPDef macro
>;IFN FTIMP32


; now define the fields
ImpDef	HTICon, 1,18	; information that is always constant on incoming
			;  messages (we take a halfword slice for convenience.
ImpDef	HTIIni, 1,36	; information which needs to be set the same on all
			;  outgoing messages (includes everything in first
			;  pdp-10 word).
ImpDef	HTITyp,25,32	; type of message
ImpDef	HTIAdr,41,64	; address of host
ImpDef	HTILnk,65,72	; link field (contains protocol for next level
ImpDef	HTISub,77,80	; message subtype
ImpDef	HTILen,81,96	; message length, in bits

IFE FTIMP32,<
	im.pri==1b32	; PRIORITY BIT (HOST-IMP MESSAGES)
>;IFE FTIMP32
;JJW - Since IM.PRI isn't used, we don't define it for IFN FTIMP32
	im.trc==1b20	; TRACE BIT
	im.oct==1b21	; OCTAL BIT

HTISiz==↑d96		; the length of the leader is 96.
IFE FTIMP32,<
HTIWds==<HTISiz+<ful.wd-1>>/ful.wd	; number of words in a H-T-I leader.
>;IFE FTIMP32
IFN FTIMP32,<
HTIWds==<HTISiz+<net.wd-1>>/net.wd	; number of words in a H-T-I leader.
>;IFN FTIMP32
HTIMax==↑d8159		; absolute maximum number of bits in an 1822 message.

; maximum message size for TCP message in bytes (ignores the possibility of
;	options).
maxmes==<<HTIMax/↑d8>-<HTISiz/↑d8>-<IpLen##*4>-<TCPLen##*4>>

; value of the first pdp-10 word of 1822 leaders.  this value is
;  constant for all messages except for the message type.  for
;  outgoing messages, this is always type 0 (since other types
;  are handled differently), so all of it is constant for outgoing.

	newfrm==byte(4)0,↑d15		; second 4 bit byte is decimal 15
					;  to flag new style ("96 bit")
					;  leader, all other bytes are zero.
	subttl	HDT - Host Data Table entry description ;⊗ HS.Rfn HS.Max HS.Dwn


; this is the description of an entry in in the host data table.  the host
;	data table contains one entry for each ARPANET host we are sending
;	or receiving packets from.  note that this is NOT necessarily the
;	host we are communicating with in IP level.  it could be a gateway,
;	for example.  each ARPANET host must have its own queue because we
;	must not send more than 8 messages in transit to one host at one time.

;;!------------------------------------|------------------------------------!
;;!	target host address (address of ARPAnet host, not IP address)	    !
;;!------------------------------------|------------------------------------!
;;!   last BIB is transmission queue   |  first BIB in transmission queue   !
;;!------------------------------------|------------------------------------!
;;!	   	not used	       |		flags		    !
;;!------------------------------------|------------------------------------!



BkIni.		; start block

	BkNxt.	HDTAdr			; address of ARPAnet host
					; (or negative to flag end of buffer)

	BkDef.	HDTNxt			; next buffer pointer or negative
					;  count of entries left this buffer
					;  if HDTAdr less than zero.
	BkDef.	HDTBfs			; but usually it's: buffer descriptor
	BkOff.	HDTBfO			; buffer word offset.
	BkNxt.	HDTLst,hlf.wd		;(LH) last BIB in queue
	BkNxt.	HDTFst,hlf.wd		;(RH) first BIB in queue

	BkDef.	HDTRNC			; entire next word is INCRed and
					;  DECRed so save energy.

	BkNxt.	HDTFlg,ful.wd		;(RH) flags
		HS.Rfn==17		; low bits are RFNM count,
					;  which may be INCRed and
					;  DECRed via HDTRNC.
		HS.Max==10		; most RFNMs we can have in
					;  transit is 8, so if this bit
					;  is on, we have sent 8 and
					;  can send no more.
		HS.Dwn==400000		; host is down bit.

BkEnd.	HDTLen				; get the length
SUBTTL INPUT INTERRUPT SERVICE

COMMENT \
	THIS SECTION CONTAINS THE INTERRUPT DRIVEN PORTION OF THE
INPUT PACKAGE.  LOGICAL TESTING OF MESSAGE CONTENT IS KEPT TO A
MINIMUM.
	WHEN INPUT BEGINS ON A MESSAGE, A BLKI POINTER IS NOT YET
SET UP AND CONTROL TRAPS TO ROUTINE IMPIN.  IF IT IS NOT A DATA MESSAGE, IMPONE
IS CALLED AS A SUBROUTINE AND THE REST OF THE MESSAGE IS IGNORED.
IF IT IS A DATA MESSAGE, A BUFFER
IS ALLOCATED, THE FIRST WORD OF DATA IS PLACED IN IT, AND AN IOWD
POINTER IS BUILT TO INPUT THE REST OF THE MESSAGE INTO THE NEW
BUFFER.
	IF THE BUFFER IS FILLED BEFORE THE END OF THE MESSAGE, CONTROL
TRANSFERS TO ROUTINE IMPIND AT INTERRUPT LEVEL.  IF THE MESSAGE IS
A NON-DATA MESSAGE OR A DATA MESSAGE WITH BIT COUNT TOO LARGE,
THE REST OF THE MESSAGE IS DISCARDED AND REGULAR E.O.M. PROCESSING
OCCURS (FOR NON-DATA MESSAGES).  IF THE
MESSAGE IS A DATA MESSAGE, A BIT COUNT IS COMPUTED AND PLACED IN
THE JUST FILLED BUFFER,  A NEW BUFFER IS ALLOCATED AND LINKED TO
THE CURRENT BUFFER, AND AN IOWD POINTER IS BUILT TO INPUT
INTO THE NEW BUFFER.
	WHEN THE END OF MESSAGE IS RECEIVED, CONTROL GOES TO
ROUTINE IMPEIM.  HERE, IF AN ERROR WAS DETECTED DURING THE MESSAGE,
THE ENTIRE MESSAGE IS DISCARDED AND THE BUFFER(S) FREED.  IF NO
ERRORS WERE DETECTED, THE MESSAGE TYPE IS CHECKED.  IF THIS IS A
NON-DATA TYPE, THE FIRST WORD OF THE MESSAGE IS SAVED, THE BUFFER
RELEASED, AND THE PROPER HANDLING ROUTINE CALLED.  IF THIS IS A
DATA MESSAGE, A WORD COUNT IS GENERATED AND PLACED IN THE BUFFER,
THE INTENDED RECIPIENT IS FOUND AND THE ENTIRE MESSAGE IS LINKED
INTO THE INPUT STREAM FOR THAT DDB.  THE USER(OR NCP) IS THEN
WAKENED AND THE INTERRUPT DISMISSED.
\
COMMENT \

THERE ARE MUCH BETTER WAYS TO ORGANIZE THIS STUFF.   SUGGEST:

1.	ALLOCATE A FIXED BUFFER FOR INPUT THEN, AND END-OF-MESSAGE
	INTERRUPT LEVEL, IF NECESSARY, TRANSFER THE DATA TO A LINKED
	BUFFER ELSEWHERE.   FOR NCP INPUT, TTY INPUT, THIS WONT BE
	NECESSARY, SINCE THE NCP EATS ALL INPUT IMMEDIATELY AND THE
	TTY HAS BUFFERS ELSEWHERE (ALLOCATION MUST BE SUCH THAT THESE
	BUFFERS ARE NEVER OVERRUN).

2.	RUN THE DATA INPUT (BLKI) AT A HIGH INTERRUPT LEVEL (CH 2),
	AND DO THE END-OF-MESSAGE STUFF AT A LOW (6) LEVEL.  THE
	BUFFER POINTER WOULD HAVE TO BE SET UP BEFORE INPUT WAS
	STARTED.

3.	WHEN A ONE WORD MESSAGE IS INPUT, THE CH 2 ROUTINE CAN BE RE-
	STARTED AS SOON AS THE LEADER IS IN AN AC(P1).  I ASSUME
	THAT THERE IS A REASONABLE PROBABILITY THAT RFNMS WILL BE
	FOLLOWED BY DATA.

4.	PERHAPS THE CLOCK SHOULD BE PUT ON CH 6, AND SOME LOW LEVEL
	TASKING BE DONE ON CH 7.   COULD DO ALL E-O-M STUFF HERE.
	THIS COULD BE A MAJOR CHANGE TO THE DEC SYSTEM.

\
NOWAITS< ;⊗ IMPISR
	SUBTTL	TABLE OF INTERRUPT SERVICE ROUTINES

;  THIS TABLE WAS PUT IN TO ACCOMODATE 5.07(A), POINTED TO BY
;  @LDBISR(U), WHERE LDBISR CONTAINS FOR IMP DEVICES IMPISR(T1),
;  SO THAT IMPISR IS A VECTOR OF FUNCTIONS, THE PARTICULAR ONE
;  BEING SELECTED BY (T1). IT IS INTENDED PRIMARILY FOR USE BY THE
;  DC76 ASYNCHRONOUS FRONT END. FUNCTIONS INTENDED ONLY FOR THE DC76
;  CHECK FIRST FOR A BIT IN LDBISR(U) SIGNALLING A "SMART" FRONT END;
;  NATURALLY THE IMP HAS BEEN STIGMATIZED AS DUMB. "SMART"
;  FUNCTIONS WILL SIMPLY RETURN WITHOUT COMMENT (6.02 REQUIRES IT).

;     IT IS INTENDED THAT THE STANDARD SCNSER/CLOCK ROUTINES WILL USE
;  THIS TABLE; WORK ON THAT WILL WAIT UNTIL THE CURRENT VERSION
;  HAS BEEN TESTED.
;						DK/FEB 75

IMPISR::JRST	IMPTYP		;OUTPUT DATA IN T3, DEVICE ADDR IN U
	POPJ	P,		;DATA SET CONTROL
	JRST	IMPSEC		;ONCE-A-SECOND CHECK
	POPJ	P,		;INIT INTERRUPT SERVICE ROUTINE
	POPJ	P,		;CHANGE HARDWARE PARAMETERS
	POPJ	P,		;LINE PARAMETER CONTROL
	POPJ	P,		;SET TTY ELEMENT
	jrst	ImpRem		;(7) do remote functions
	JRST	CPOPJ1##	;(10)SKIP IF FRONT END ON LINE
>;NOWAITS
SUBTTL INPUT INTERRUPT SERVICE ;⊗ IMPIN IN0 IN1 IN2 IN3 INON IN5 in6 IN7

;HERE AT INTERRUPT LEVEL ON FIRST INPUT INTERRUPT
IMPIN::	SKIPL	IMPUP		;WANT IT UP?
	JRST	IN7		;NO
	SKIPE	INHALT		;RESUMING INTERRUPTED INPUT?
	JRST	IN5		;YES
	SETZM	INBUFP		;CLEAR BUFFER POINTER
	load.	t2,HTICon,t1		; get constant field from T1
	caie	t2,(newfrm)	;[96bit] is it the new format?
	  jrst	in7		;[96bit] no: ignore it.
	load.	t2,HTITyp,t1		; get type of message
	JUMPN	T2,IN6			; data message?
	setzm	impihd			; yes.  remember for later
	PUSH	P,T1		;YES, SAVE LEADER
	ScnOff		;PREVENT RACES
	PUSHJ	P,BufGet##	;GET A BUFFER
	  JRST	IN2		;NONE!
	ScnOn
	POP	P,NBHLen(T1)	;[96bit] PUT LEADER IN FIRST DATA WORD
IN0:	HRLM	T1,INBUFP	;SAVE AS FIRST BUFFER IN MESSAGE
IN1:	HRRM	T1,INBUFP	;SET LAST BUFFER ADDRESS
	HRLI	T1,-ImpBfs##+NBHLen+1	;MAKE IOWD POINTER TO REMAINING SPACE
	AOJA	T1,CPOPJ

;HERE IF NO MORE INPUT BUFFERS
IN2:	POP	P,LEADER	;SAVE LEADER
	SETZM	T1		;FORCE IMPIN CALL ON NEXT INTERRUPT
IN3:	SETOM	INHALT		;FLAG FOR NEXT INPUT
	SETOM	IBFHLT		;FLAG FOR BUFFER CONTROL
	PUSHJ	P,IMPIOF##	;TURN OFF INPUT
	JRST	SOnppj##		; SCNSER interrupts back on and return

;HERE from NetSub WHEN A BUFFER IS AGAIN FREE.  ADDRESS IN T
INON::	HRRZM	T1,BUFADR	;SAVE ADDRESS
	SETZM	IBFHLT		;CLEAR FLAG
	JRST	IMPION##	;INPUT BACK ON

;HERE ON FIRST INPUT AFTER BUFFER AGAIN FREE.
IN5:	MOVE	T2,T1		;SAVE NEW DATA
	SETZM	INHALT		;CLEAR FLAG
	HRRZ	T1,BUFADR	;GET BUFFER ADDRESS
	SKIPN	T3,LEADER	;FIRST BUFFER OF MESSAGE?
	JRST	IND3		;NO
	MOVEM	T3,NBHLen(T1)	;[96bit] YES, PUT THE LEADER IN THE BUFFER
	MOVEM	T2,NBHLen+1(T1)	; and that makes this the second word
	PUSHJ	P,IN0		;DO IT
	AOBJN	T1,.+1		;BUMP POINTER PAST DATA WORD
	POPJ	P,		;RESUME INPUT

;[96bit] here if this is a message from the imp
in6:	movem	t1,impihd	;[96bit] save the leader's first word
	move	t1,[iowd HTIWds-1,impihd+1]
				;[96bit] get pointer for the rest
	popj	p,		;[96bit] and return that blki pointer

;[96bit] here to discard everything until an eom is seen
IN7:	MOVEI	T1,777777	;AND IGNORE THE REST
	setom	impihd		;[96bit] tell eom code to ignore message
	POPJ	P,
;HERE ON BLKI RUNOUT AT INTERRUPT LEVEL.  T POINTS TO THE LAST ;⊗ IMPIND IND1 IND2 IND3 ind4
; LOCATION FILLED.
IMPIND::
	PUSHJ	P,SAVE2##	;SAVE SOME ACS
	skipl	impup		;[96bit] should the imp be up?
	  jrst	in7		;[96bit] no: just discard
	skipe	p1,impihd	;[96bit] are we getting a leader?
	  jrst	ind4		;[96bit] yes: go take care of it
				;[96bit] no: this is a host-host message
	move	p2,inbufp	;[96bit] get the input buffer pointers
IFE FTIMP32,<
	movei	t1,NBfB36		; number of bytes in a 36 bit buffer
>;IFE FTIMP32
IFN FTIMP32,<
	MOVEI T1,NBHBYT			;Number of bytes in a 32-bit buffer
>;IFN FTIMP32
	stor.	t1,NBHCnt,(p2)		; store as length
;	PUSHJ	P,CHKBUF	;SET BUFFER SIZE, SKIP IF DATA
;	  JRST	IN7		; an error
IND1:	ScnOff
	PUSHJ	P,BufGet##	;GET ANOTHER BUFFER
	  JRST	IND2		;NONE!
	ScnOn
	stor.	T1,NBHNxt,(P2)		; link to old last.
	HRRM	T1,INBUFP	;NEW CURRENT BUFFER
	HRLI	T1,-ImpBfs##+NBHLen	;MAKE AN IOWD
	POPJ	P,

;HERE IF NO MORE BUFFERS
IND2:	SETZB	T1,LEADER	;NOT FIRST BUFFER
	JRST	IN3		;WAIT FOR A FREE BUFFER

;HERE WHEN INPUT RESUMES WITH NEW BUFFER ADDRESS IN T1. FIRST WORD OF
; DATA IS IN T2.
IND3:	MOVEM	T2,NBHLen(T1)	;[96bit] PUT THE DATA IN THE BUFFER
	HRRZ	T2,INBUFP	;LINK TO REST OF MESSAGE
	stor.	T1,NBHNxt,(T2)	; this is the next
	JRST	IN1		;BUMP OTHER POINTERS AND RETURN

;[96bit] here to deal with an imp message which was longer than it
;	 should have been.
ind4:	pushj	p,eim1		;[96bit] go process the message
	jrst	in7		;[96bit] ignore the left overs
;HERE AT INTERRUPT LEVEL UPON RECEIPT OF THE END OF A MESSAGE.  T1 ;⊗ IMPEIM eim1 EIM4 EIM5 EIM6 EIM7
; CONTAINS THE ADDRESS OF THE LAST BUFFER WORD FILLED IN THE RIGHT
; HALF AND ERROR INDICATIONS IN THE LEFT HALF(OK IF NEGATIVE OR ZERO).
IMPEIM::
	SETZM	STOPFLG		;ABORT IMP GOING DOWN IF GET ANYTHING
	SKIPL	IMPUP		;WANT IT?
	JRST	EIM7		;NO.   GOING DOWN.
	SKIPE	FLTFLG		;ERROR DETECTED?
	JRST	EIM5		;YES
	PUSHJ	P,IMPCHK##	;CHECK HARDWARE
	  JRST	EIM4		;DEAD!
	  JRST	EIM5		;SICK!
	PUSHJ	P,SAVE2##	;GET SOME ACS
	skipn	p1,impihd	;[96bit] is this an imp message?
	  jrst	mes00		;[96bit] no: it's data (host-host)
	jumpl	p1,cpopj	;[96bit] just throwing away data: junk.
eim1:	load.	t3,HTITyp,p1		; get type out of P1
	cail	t3,mesdsn	;[96bit] legal message?
	  jrst	eim6		;[96bit] no: count bad message
	AOS	MESTYP##(T3)	; COUNT IT
	jrst	@mesdsp(t3)	;[96bit] dispatch

;HERE IF IMP DEAD
EIM4:	PUSHJ	P,DEDIMP	;TELL ALL

;HERE IF IMP WAS DOWN DURING THIS INPUT
EIM5:	PUSHJ	P,FLTIMP	;TELL ALL
	SETZM	FLTFLG		;CLEAR ERROR FLAG
	AOSA	BADIMP##	;COUNT THE ERROR

;HERE IF BAD MESSAGE TYPE
EIM6:	AOS	BDMMES##	;COUNT IT

;HERE TO DISCARD THE MESSAGE
EIM7:	MOVS	T1,INBUFP	;GET MESSAGE POINTER
	skipn	impihd		;[96bit] does this have a buffer?
	  PJRST	RELBUF##	;RELEASE IT
	popj	p,		;[96bit] no, it was using the header
				;[96bit] area, so don't release buffer
mes10==q4nops	;[96bit] must be defined before second pass ;⊗ mes10 MESDSP MESDSN MES0 MES4 MES3 MES6

;DISPATCH TABLE FOR IMP MESSAGES

MESDSP: MES0	;[96bit] host-host message (should never get this far)
	MES1	;[96bit] error in leader
	MES2	;[96bit] IMP going down
	MES3	;[96bit] undefined
	MES4	;[96bit] no-op
	MES5	;[96bit] ready for next message (RFNM)
	MES6	;[96bit] dead host status (unused by this code)
	MES7	;[96bit] destination host or imp is dead or unknown
	mes8	;[96bit] error in data
	mes9	;[96bit] incomplete transmission
	mes10	;[96bit] interface reset

	MESDSN==.-MESDSP

ifg mesdsn - mesdln,<	;[96bit] make sure netcon has room to remember
			;	 the number of message types we may
			;	 have.
printx ? MESDLN (from NetDef.MAC) must be greater than or equal to MESDSN
>

MES0==cpopj##		;[96bit] 1 WORD DATA SHOULD NEVER HAPPEN
MES4==cpopj##		;[96bit] TYPE 4 IS A NO-OP

;UNUSED CODES
MES3==EIM6
MES6==EIM6
;HERE AT INTERRUPT LEVEL ON RECEIPT OF A BUFFERED DATA MESSAGE ;⊗ MES00
MES00:
	pushj	p,save4##		; save p1-p2
	aos	mestyp			;[96bit] count the type 0 message
	move	p2,inbufp		;[96bit] get the buffer pointer
	hrrzs	t1			; get just last word written
	subi	t1,(p2)			; subtract off start of buffer
					;  to get words written.
IFE FTIMP32,<
	imuli	t1,↑d9			; nine bytes...
	lsh	t1,-1			; ...for every two words.
>;IFE FTIMP32
IFN FTIMP32,<
	LSH T1,WD2BYT			;Convert to a byte count
>;IFN FTIMP32
	stor.	t1,NBHCnt,(p2)		; store the byte count for this buffer
	pushj	p,GetPDB		; get pseudo DDB for
					;  convenience while inputting.
	hlrz	p1,p2			;[96bit] get the first buffer location
	load.	t1,HTIAdr,NBHLen(p1)	; get source host
	movem	t1,NetAdr(f)		; where this message came from
					;  on our network.
IFN DEBUG,<
	SKIPE	TESTHS			;TESTING?
	 CAMN	T1,TESTHS		;YES.  JUST TALK TO ONE HOST
	  skipa				; not testing or host the one
					;  we want.
	   jrst	Mes08			; error
>
;	PUSHJ	P,CHKBUF	;GET BUFFER SIZE.
;	  JRST	MES08		;FOULUP -- TRY TO CLEAN UP
	load.	T1,HTILnk,NBHLen(p1)	;GET LINK NUMBER
	caie	t1,.lnkip		; IP's link?
	  jrst	Mes07			; no.  flush it.
	hlrz	t1,p2			; get first buffer.
	; figure max amount of data in this buffer.  this fails to spot
	;  premature end of message, but otherwise does no harm, since
	;  IP figures out how long the message is.
IFE FTIMP32,<
	movei	t2,<NBfB36-<HTISiz/↑d8>>
	movem	t2,IBfBC(f)		; number of bytes left, this buffer.
	add	t1,[point 8,3,23]	; fudge byte pointer for
					;  beginning of IP leader.
>;IFE FTIMP32
IFN FTIMP32,<
	MOVEI T2,<NBFBYT-<HTISIZ/↑D8>>
	movem	t2,IBfBC(f)		; number of bytes left, this buffer.
	ADD T1,[POINT 8,3,31]		;Point to last byte in H-T-I leader
>;IFN FTIMP32
	movem	t1,IBfPnt(f)		; save the new pointer
	hlrzm	p2,IBfThs(f)		; make this the current buffer
					;  (by the time InpBfx checks
					;  this, it will be time to
					;  move to the next buffer,
					;  hence set left half to 0)
IFE FTIMP32,<
	movei	p4,InBy36		; set co-routine for correct
					;  place in word for IP beginning.
>;IFE FTIMP32
IFN FTIMP32,<
	MOVEI P4,INBYTE
>;IFN FTIMP32
	pushj	p,IPIn##		; go read an IP message.  IP
					;  calls next protocol for
					;  futher processing until
					;  message in completely read.

	move	t1,IBfThs+PsdDDB	; get stream pointer of input stream
					;  from pseudo DDB (F may now
					;  points at a real DDB).
	pjrst	RelBuf##		; go release anything left of
					;  the stream and return.
;HERE IF BAD LINK NUMBER ;⊗ MES07 mes07a mes08
MES07:	AOS	BDMLNK##	;COUNT IT
mes07a:	HLRZ	T1,P2		;[96bit] BUFFER STREAM
	PJRST	RELBUF##	;THROW AWAY THE WHOLE THING

;[96bit] here to discard a message
mes08:	setzm	inbufp		;[96bit] zero the buffer pointers
				;	 (we have a copy in p2)
	jrst	mes07a		;[96bit] free the buffers and return
repeat 0,<	; try not using this ;⊗ CHKBUF CHKBF1 CHKBF2

;SUBROUTINE TO DO VARIOUS
;MANIPULATIONS ON BIT/BYTE/WORD COUNTS.
;CALL:
;	MOVE	P2,[ FIRST BUFFER ,, LAST BUFFER ]
;	MOVE	T1,[BLKI POINTER RETURNED BY IMPINT]
;	PUSHJ	P,CHKBUF
;	  ERROR RETURN	...NON-DATA MESSAGE
;	OK RETURN...	DATA PRESENT, BUFFER ADDRESS IN T1
CHKBUF:
;[96bit] these conditions cannot make it to this routine any longer
;[96bit]HLRE	T2,T1		;GET RESIDUAL WORD COUNT
;[96bit]JUMPG	T2,CPOPJ	;POSITIVE MEANS DATA WAS DISCARDED
;[96bit]LDB	T2,MESPT1	;GET MESSAGE TYPE
;[96bit]JUMPN	T2,CPOPJ	;EXIT IF NOT DATA
	ANDI	T1,777777	;ADDRESS ONLY
IFN DEBUG,<
	CAMGE	T1,IMPBUF##	;MAKE SURE IT'S A BUFFER ADDRESS
	STOPCD	CPOPJ,STOP,GBP,;++GARBAGE BLKI POINTER
	;[96bit] changed to STOP from DEBUG by cmu 4/9/80
>
	MOVSI	T2,MRKFLG
	TDNN	T2,(P2)		;FIRST BUFFER?
	JRST	CHKBF1		;NO
	LDB	T2,PBYTC2	;YES, GET BYTE COUNT IN T3
	LDB	T3,PBYTS2	;AND BYTE SIZE IN T2
	IMUL	T2,T3		;FORM BIT COUNT
	CAILE	T2,↑D8192	;IF TOO BIG,
	JRST	CHKBF2		;  DISCARD IT
	MOVEM	T2,MESSIZ	;AND SAVE IT
	JFFO	T2,.+2		;COUNT HIGH BIT POSITION
	MOVEI	T3,↑D36		;IF NONE SET
	MOVNI	T2,-↑D36(T3)	;ORDER OF MAGNITUDE [2]
	AOS	SIZHST##(T2)	;COUNT THIS MESSAGE SIZE
	SUBI	T1,.wdled	;[96bit] ALLOW FOR LEADER IN WORD COUNT
CHKBF1:	SUBI	T1,(P2)		;WORD COUNT (T1 HAS LAST DATA WORD, P2
				;  HAS LAST BUFFER ADDRESS).
	IMULI	T1,↑D36		;BIT COUNT
	CAMLE	T1,MESSIZ	;LARGER THAN TOTAL BITS?
	MOVE	T1,MESSIZ	;YES, USE SMALLER
	DPB	T1,PBTCN2	;BIT COUNT IN BUFFER
	MOVNS	T1		;DECREMENT REMAINING BIT COUNT
	ADDM	T1,MESSIZ
	JRST	CPOPJ1##	;SKIP RETURN

;HERE WHEN <BYTE COUNT>*<BYTE SIZE> WAS TOO LARGE
CHKBF2:	AOS	SIZERR##	;COUNT THE ERROR
	POPJ	P,		;DISCARD THE MESSAGE

> ; end of repeat 0
;HERE ON TYPE 1 MESSAGE			ERROR IN PREVIOUS LEADER ;⊗ mes1 MES2 MES7 mes10 MES9
mes1:	load.	t1,HTISub,ImpIHd	;[96bit] get the subtype field
	cail	t1,EPLmax##		;[96bit] too big?
	  movei	t1,EPLmax##		;[96bit] count "too big"
	aos	EPLcnt##(t1)		;[96bit] and count it.
	popj	p,			;[96bit] and return

;HERE ON TYPE 2 MESSAGE...		IMP GOING DOWN
MES2:	MOVEM	P1,STOPFLG
	POPJ	P,


;HERE ON TYPE 7 MESSAGE...		REMOTE HOST DOWN
MES7:
;(235)	PUSHJ	P,MES9		;FIRST TELL THE USER
	pushj	p,MesErr	;(235) take care of error handling
	load.	t1,HTIAdr,ImpIhd	; get host
	Pjrst	HOSTBD		;TELL HOST CONTROL

;HERE ON TYPE 10 MESSAGE (IMP INTERFACE RESET)
mes10==q4nops	;[96bit] just queue four no-ops and return.  this equate
		;	 is also done before the dispatch table to get
		;	 mes10 defined before the second pass.

;Here on type 9 Message (Incomplete transmission) (234)

MES9:	load.	t1,HTISub,ImpIhd	;(234) get the subtype field
	cail	t1,INCmax##		;(234) too big?
	  movei	t1,INCmax##		;(234) count "too big"
	aos	INCcnt##(t1)		;(234) and count it.
;(235)	jrst	mes8			;(234) Continue error processing
	jrst	MesErr			;(235) and then do error processing


;HERE ON TYPE 5 MESSAGE...		RFNM ;⊗ MES5 MES8 MesErr
MES5:
;HERE ON TYPE 8 MESSAGE (ERROR)
MES8:
;(235) here for common code for error handling
MesErr:	load.	T1,HTIAdr,ImpIhd	;HOST NUMBER
	ScnOff
	PUSHJ	P,GtRFNM		; deal with RFNM
	JRST	sonppj##		; scnser interrupts back on and return
SUBTTL INPUT STREAM MANIPULATION ;⊗

COMMENT \

	INPUT MESSAGES ARE BLOCKED AT INTERRUPT LEVEL INTO FIXED
LENGTH BUFFERS.  THE FIRST AND THE LAST BUFFER ARE NEVER
TOTALLY EMPTY.   THE BUFFER CONSISTS OF A HEADER WORD AND UP
TO 255 DATA WORDS.  THE HEADER CONTAINS A COUNT OF THE BITS
ACTUALLY FILLED BY THE INTERRUPT ROUTINES.  THE FORMAT OF THE
HEADER IS AS FOLLOWS:

BITS 0 - 17	BIT COUNT FOR THIS BUFFER
BITS 18 - 35	LINK TO NEXT BUFFER FOR THIS SOCKET(0 IF NONE)


	EACH DDB MUST HAVE SEVERAL STORAGE LOCATIONS FOR MANIPULATING
ITS INPUT STREAM.  THEY ARE AS FOLLOWS:
IBFTHS--	CURRENT BUFFER BEING EMPTIED.  CONTAINS -1 IN
		THE LEFT HALF IF THIS IS THE FIRST BUFFER IN THE
		INPUT STREAM(IF IBFTHS WAS 0).

IBfLst--	last buffer of stream waiting to be read by user input
IBFBC--		BYTE COUNT IN THE CURRENT BUFFER
IBFPNT--	POINTS TO THE CURRENT BYTE BEING INPUT
\
IFE FTIMP32,< ;Next 3 pages ;⊗ INBYT0 InByt1 INBYT2 INBYT3 InBy36 INBYT4
;SUBROUTINE TO GET 8 BIT BYTES FROM THE INPUT STREAM.
;  THIS SUBROUTINE IS STRUCTURED AS ONE HALF
;  OF A CO-ROUTINE PAIR.  LINKAGE IS STORED IN THE DDB.
;  IBFBC CONTAINS THE BYTE COUNT REMAINING IN THE BUFFER.
;  IBFPNT POINTS TO THE LAST BYTE TO BE FETCHED FROM THE BUFFER.
;  IBFMES COUNTS THE MESSAGES INPUT;  IBFBIT COUNTS THE BITS.
;  COSTS ABOUT 11 MEMORY REFERENCES PER BYTE PLUS ABOUT 80
;  PER BUFFER.
;CALL:
;	MOVE	P4,[INBYTE]	;ON FIRST CALL ONLY
;	MOVE	F,[ADDRESS OF DEVICE DATA BLOCK]
;	ScnOff
;	JSP	P4,(P4)
;	   ERROR RETURN	...  NO MORE
;	OK RETURN  ...	BYTE IN T1,  P4 SET FOR NEXT CALL.

INBYT0:	JSP	P4,1(P4)
InByt1:	; InByte is now a byte getter for getting bytes from 32 bit words.
	;  this method (from 36 bit words) is called at InBy36
	SOSGE	IBFBC(F)	;ANY MORE?
	JSP	T4,INBYTG	;NO, GET ANOTHER BUFFER
	  ILDB	T1,IBFPNT(F)	;GET THE BYTE
	JSP	P4,1(P4)	;RETURN
INBYT2:	SOSGE	IBFBC(F)	;COUNT
	JSP	T4,INBYTG	;NEXT BUFFER
	  ILDB	T1,IBFPNT(F)	;FETCH
	JSP	P4,1(P4)	;RETURN
INBYT3:	SOSGE	IBFBC(F)
	JSP	T4,INBYTG
	  ILDB	T1,IBFPNT(F)
	JSP	P4,1(P4)
InBy36:	; start here when reading a new stream to skip over 96 bit leader.
INBYT4:	SOSGE	IBFBC(F)
	JSP	T4,INBYTG
	  ILDB	T1,IBFPNT(F)
	JSP	P4,1(P4)

;FALL INTO FIFTH BYTE
;HERE TO GET 5TH BYTE ;⊗ INBYT5 INBYT6 INBYT7 INBYT8 INBYT9
INBYT5:	SOSGE	IBFBC(F)	;COUNT
	JSP	T4,INBYTG	;NEW BUFFER
	  AOS	T2,IBFPNT(F)	;BUMP POINTER TO NEXT WORD
	  MOVE	T1,-1(T2)	;GET LAST 4 BITS OF PREV. WORD
	  MOVE	T2,(T2)		;GET FIRST 4 BITS OF NEXT
	  LSHC	T1,4		;POSITION THEM
	  HRLI	T1,(POINT 8,,3)	;FIX POINTER
	  HLLM	T1,IBFPNT(F)
	  ANDI	T1,↑O377	;JUST 8 BITS
	JSP	P4,1(P4)	;RETURN
INBYT6:	SOSGE	IBFBC(F)	;BACK TO STANDARD FORMAT
	JSP	T4,INBYTG
	  ILDB	T1,IBFPNT(F)
	JSP	P4,1(P4)
INBYT7:	SOSGE	IBFBC(F)
	JSP	T4,INBYTG
	  ILDB	T1,IBFPNT(F)
	JSP	P4,1(P4)
INBYT8:	SOSGE	IBFBC(F)
	JSP	T4,INBYTG
	  ILDB	T1,IBFPNT(F)
	JSP	P4,1(P4)
INBYT9:	SOSGE	IBFBC(F)
	JSP	T4,INBYTG
	  ILDB	T1,IBFPNT(F)
	JRST	INBYT0		;LOOP FOR NEXT WORD PAIR
;SUBROUTINE TO SET UP NEXT BUFFER FOR INBYTE ;⊗ INBYTG
;  THIS ROUTINE EITHER JUMPS TO INBYTE if SUCCESSFUL OR TO
;  CALL - 2 if UNSUCCESSFUL WITH P4 DECREMENTED.  IT
;  IS INTENDED TO BE USED IN THE INBYTE CO-ROUTINE.
;  COSTS ABOUT 72 MEMORY REFERENCES PER BUFFER.
INBYTG:	PUSHJ	P,INBUFR	;SET UP INPUT BUFFER
	  SOJA	P4,-3(T4)	;DECREMENT P4 AND TAKE ERROR RETURN
	MOVEM	T2,IBFBC(F)		; let TCP decide when to stop.
	HRLI	T1,(POINT 8)	;MAKE BYTE POINTER
	MOVEM	T1,IBFPNT(F)	;SAVE POINTER
	JRST	INBYT1		;NO.
>;IFE FTIMP32
	subttl	InByte ;⊗ InBytL InByte InBytM INBYTC INBYTX

;++
; Functional description:
;
;	get the next byte from the given DDB's input byte stream.
;
;	may be called from IMP interrupt level OR from UUO level.
;
; Calling sequence:
;
;		ScnOff
;		move	f,DDB
;		move	p4,[InByte]	; first call only
;		jsp	p4,(p4)
;		  <nothing left in stream>
;		<next byte is in T1>
;
; Input parameters:
;
;	F - DDB for connection
;
; Output parameters:
;
;	T1 - next byte
;
; Implicit inputs:
;
;	P4, DDB, input stream
;
; Implicit outputs:
;
;	P4, DDB, input stream
;
; Routine value:
;
;	non-skip if no more bytes left.
;
; Side effects:
;
;	discards exhausted buffers, updates DDB.
;
;--

InBytL:	JSP	P4,1(P4)
InByte::
	SOSGE	IBFBC(F)		; byte for sale?
	  jsp	t4,InBytM		; no.  try for a new buffer
	ILDB	T1,IBFPNT(F)		; get byte
	jrst	InBytL			; loop

InBytM:	PUSHJ	P,INBUFR	;SET UP INPUT BUFFER
	  soja	p4,-3(t4)		; out of buffers.  take error return.
	MOVEM	T2,IBFBC(F)		; set that (let TCP decide when
					;  to stop on the last buffer.)
	HRLI	T1,(POINT 8)	;MAKE BYTE POINTER
	MOVEM	T1,IBFPNT(F)	;SAVE POINTER
	jrst	-2(t4)			; try again with good return


;SUBROUTINE TO VERIFY THAT THE STREAM IS EMPTY.
;CALL:
;	ScnOff
;	PUSHJ	P,INBYTC
;	  ERROR	...  DATA REMAINING
;	OK RETURN  ...	STREAM EMPTY

	AOJA	P4,CPOPJ1##	;CORRECT P4 AND TAKE SKIP RETURN
INBYTC:	SKIPG	IBFBC(F)	;ANY BYTES?
	  JSP	T4,INBYTM	;TRY FOR NEXT BUFFER
INBYTX:	POPJ	P,		;STRING NOT EMPTY
;SUBROUTINE TO GET ANOTHER BUFFER FOR INPUT UNPACKING ROUTINES ;⊗ INBUFR
;CALL:
;	PUSHJ	P,INBUFR
;	  ERROR RETURN	...  END OF STREAM
;	OK RETURN  ...	T1 HAS BUFFER ADDRESS,  T2 HAS SIZE (IN BITS)
IFWAITS<ENTRY INBUFR>
INBUFR:	SETZM	IBFBC(F)	;IN CASE
	PUSHJ	P,INPBFX	;GET IT
	  POPJ	P,		;NONE
	load.	t2,NBHCnt,(t1)		; get length from buffer.
	addi	t1,NBHLen		; point at first data word.
	JRST	CPOPJ1
;SUBROUTINE TO ADVANCE POINTERS TO THE NEXT INPUT BUFFER AND RETURN ;⊗ INPBFX INPBF1 inpbf2
; THE BUFFER ADDRESS IN T1.  IBFTHS IS ADVANCED TO THE NEXT
; BUFFER.  IF IBFTHS CONTAINED -1 IN THE LEFT HALF, THE FIRST BUFFER
; HAD NEVER BEEN REFERENCED AND THE POINTER IS NOT ADVANCED.  THE
; OLD BUFFER(IF ANY) WILL BE RELEASED.
;CALL:
;	MOVEI	F, DATA BLOCK ADDRESS
;	ScnOff
;	PUSHJ	P,INPBFX
;	  ERROR RETURN	...NO NEXT BUFFER
;	OK RETURN...	BUFFER ADDRESS IN T
INPBFX:	SKIPN	T1,IBFTHS(F)	;IS THERE A CURRENT BUFFER?
	  jrst	inpbf2			; no.  complete clearing stream
	TLZE	T1,-1		;YES.  HAS IT BEEN REFERENCED?
	  JRST	INPBF1		;NO
	load.	T2,NBHNxt,(T1)	;GET NEXT BUFFER
	HRLM	T2,(P)		;SAVE IT
	PUSHJ	P,BufRel##	;RELEASE LAST ONE
	HLRZ	T1,(P)		;GET NEXT BUFFER
INPBF1:	MOVEM	T1,IBFTHS(F)	;STORE NEW BUFFER ADDRESS
	JUMPE	T1,inpbf2	;IF END, ERROR RETURN
	aosa	(p)		;OK.  SKIP RETURN
inpbf2:	  setzm	IBfLst(f)	; mark buffers exhausted
	popj	p,			; return
SUBTTL OUTPUT INTERRUPT SERVICE

REPEAT 0,<
	THIS OUTPUT SECTION DOES NO LOGICAL TESTING ON THE DATA
OR SYSTEM STATE.  WHEN A BUFFER OF DATA IS SENT TO THIS SYSTEM,
IT CAN BE CONSIDERED GONE.  MESSAGES ARE CONTAINED IN LINKED
BUFFERS.  THE BUFFER FORMAT IS AS FOLLOWS:

	WORD 0 CONTAINS:
BIT 0		SET IF THIS IS THE FIRST BUFFER IN A MESSAGE
BITS 1-4	UNUSED
BITS 5-17	BUFFER WORD COUNT(N)	POINTER -- PWDCN.
BITS 18-35	POINTER TO WORD 0 OF THE NEXT BUFFER IN THIS OUTPUT
		STREAM.  0 IF THIS IS THE LAST BUFFER TO BE SENT.

	WORDS 1-N CONTAIN DATA(N IS THE WORD COUNT GIVEN IN BITS
		5-17 OF WORD 0).

	OUTPUT PROCEEDS AS FOLLOWS:
1.	A MESSAGE IS PLACED IN ONE OF THE OUTPUT QUEUES AND OUTPUT
IS ACTIVATED(BY DIRECT TRANSMISSION OF A NO-OP, IF NECESSARY).
2.	AT INTERRUPT LEVEL, AFTER SENDING THE END-OF-MESSAGE(EOM)
FOR THE PREVIOUS MESSAGE(SOMETIMES A NO-OP), THE QUEUES ARE SCANNED
FOR THE HIGHEST PRIORITY MESSAGE AWAITING TRANSMISSION.  THE ADDRESS
OF THE POINTER TO THE NEXT MESSAGE IS PLACED IN LOCATION OLINKP.
THE REFERENCED POINTER MUST HAVE, IN THE RIGHT HALF, THE ADDRESS
OF THE NEXT BUFFER TO BE OUTPUT FROM THAT STREAM.  THIS ADDRESS
WILL BE UPDATED BY THE INTERRUPT ROUTINES AS EACH BUFFER IS EMPTIED.
IF THERE ARE NO ENTRIES IN ANY OF THE QUEUES, THE OUTPUT IS
STOPPED AND FLAGGED AS INACTIVE.
3.	AN IOWD BLKO POINTER IS BUILT FROM THE WORD COUNT IN THE
FIRST WORD(WORD 0) OF THE INDICATED BUFFER AND OUTPUT BEGUN.
4.	WHEN THE BUFFER HAS BEEN EMPTIED, THE LINK TO THE NEXT BUFFER
IS SAVED AND THIS BUFFER IS RELEASED.  THUS, THE MESSAGE IS NOT
RETAINED AT INTERRUPT LEVEL FOR ERROR CHECKING.  ANY RETRANSMISSION
WILL HAVE TO TAKE PLACE AT USER LEVEL.
5.	THE NEXT BUFFER IS RETRIEVED AND THE BUFFER POINTER UPDATED.
6.	IF THERE WAS NO NEXT BUFFER, THE APPROPRIATE ROUTINE IS
CALLED TO CLEAR OUT QUEUE REGISTERS AND INTERLOCK WITH UUO OR
CLOCK LEVEL ACTIVITY.
7.	IF THERE WAS NO NEXT BUFFER OR THE NEXT BUFFER IS THE FIRST
OF A NEW MESSAGE, OUTPUT IS STOPPED(CONTROL WILL TRAP VIA A DEVICE
INTERRUPT TO EOM LEVEL AT STEP 2, ABOVE).
8.	IF THE NEXT BUFFER IS TRANSMITABLE, CONTROL LOOPS TO STEP 3,
ABOVE.
	MESSAGE QUEUES:

	THE NCP HOST QUEUES ARE HANDLED ENTIRELY WITHIN THE NETWORK
CONTROL PROGRAM.  THE IMP SERVICE INTERFACES TO THE NCP OUTPUT QUEUES
VIA TWO SUBROUTINES:
	NCPEOM    RETURNS, IN ACCUMULATOR T1, THE ADDRESS OF THE
POINTER TO THE NEXT MESSAGE TO BE OUTPUT.  A SKIP RETURN IS TAKEN IF
SUCCESSFUL.  A NON-SKIP RETURN IS TAKEN IF THE NCP QUEUES ARE EMPTY.
	NCPOND    IS CALLED WHEN A COMPLETE MESSAGE HAS BEEN SENT.
ACCUMULATOR T MUST CONTAIN THE ADDRESS OF THE POINTER BEING USED.

	THE REGULAR DATA MESSAGE QUEUE CONSISTS OF THE LOCATION
OQUEUE.  THE RIGHT HALF CONTAINS THE ADDRESS OF THE FIRST BUFFER
IN THE QUEUE AND THE LEFT HALF CONTAINS THE LAST BUFFER IN THE QUEUE.
  OQUEUE IS ZERO IF THE QUEUE IS EMPTY.
	TO PLACE A MESSAGE IN THE DATA QUEUE, DATOUT IS CALLED
WITH THE ADDRESS OF THE FIRST BUFFER IN ACCUMULATOR T AND THE LAST
BUFFER IN ACCUMULATOR T1.

	SUBROUTINE OUTGO1   ACTIVATES OUTPUT(BY SENDING A NO-OP
MESSAGE) AND SHOULD BE CALLED WHENEVER MESSAGE IS PLACED IN A
PREVIOUSLY EMPTY QUEUE.
>
;HERE AT INTERRUPT LEVEL WHEN END-OF-MESSAGE HAS BEEN SENT. ;⊗ IMPEOM EOM1 Eom2
IMPEOM::
	SKIPG	IMPREQ		;ANY IMP SUPER-PRIORITY MESSAGES?
	JRST	EOM2		;NO
	setzm	NowOut		; no buffer stream here.
	SOS	IMPREQ		;DECREMENT COUNT
	AOS	IMPQTP		;BUMP POINTER, COUNTER
	SOSLE	IMPQTC		;TEST COUNT
	JRST	EOM1		;OK, SEND IT
	MOVE	T2,[XWD IMPQLN,IMPQ];RESET QUEUE
	HLRZM	T2,IMPQTC
	HRRZM	T2,IMPQTP
EOM1:	move	t1,@impqtp	;[96bit] get the pointer
	jrst	cpopj1		;[96bit] and skip return

; check other protocols
Eom2:	pushj	p,HstEOM		; get next message to send
	  popj	p,			; nothing.  return

ifn debug,<	; code has bugs we need to check for?
	skipn	t1			; is there an entry?
	 stopcd	CPOPJ,DEBUG,ZER		;++ zero entry returned.
>
	movem	t1,OLinkP		; save the BIB pointer to this message
	load.	t1,BIBMes,(t1)		; get the actual buffer of the message
					;  from the BIB
	movem	t1,NowOut		; outputing this message
IFN DEBUG,<
	TRNN	T1,-1		; something there?
	STOPCD	CPOPJ,DEBUG,EQP,;++EMPTY QUEUE POINTER
>
	load.	t2,NBHCnt,(t1)		; get byte count (as in a 32
					;  bit buffer!)
	lsh	t2,byt2wd		; divide by 4 to get word count
	MOVNS	T2		;FORM IOWD
	HRL	T1,T2
	JRST	CPOPJ1		;SKIP RETURN
;HERE ON BLKO RUNOUT AT INTERRUPT LEVEL.  OLD BLKO POINTER IN AC T. ;⊗ IMPOND OND1
IMPOND::
	SKIPN	t2,NowOut	;ANYTHING GOING?
	  POPJ	P,		;NO.
	PUSH	P,T1		;SAVE OLD BLKO POINTER
	load.	T1,NBHNxt,(T2)	;NEXT BUFFER ADDRESS
	movem	T1,NowOut	;UPDATE POINTER
	JUMPE	T1,OND1		;JUMP IF NO NEXT BUFFER
	POP	P,T2		;CLEAR STACK
	load.	t2,NBHCnt,(t1)		; get word count times 4
	lsh	t2,byt2wd		; convert to word count

	MOVNS	T2		;MAKE IOWD AND RETURN
	HRL	T1,T2
	POPJ	P,

;HERE WHEN LAST BUFFER OF A MESSAGE IS TRANSMITTED.  T1 HAS
;  THE ADDRESS OF THE NEXT BUFFER (0 IF EMPTY QUEUE).
;  EMPTY, THEN THE QUEUE IS ALSO EMPTY.
OND1:	move	t1,OLinkP		; get BIB of this message again
	scnoff				; no interrupts while BIB futsing
ifn debug,<	; debugging
	pushj	p,BIBChk##		; consistency check
>
	incr.	t2,BIBTim,(t1),g	; start off timer.  was negative?
	  pushj	p,ARlBib##		; yes.  always release the BIB

	scnon
	JRST	TPOPJ##		;RESTORE OLD POINTER AND RETURN
;SUBROUTINE TO START UP OUTPUT ;⊗ OUTGO1 OUTGO2 NOPiow MS.NOP HGDiow ms.hgd
;CALL:
;	ScnOff
;	PUSHJ	P,OUTGO1
;	ALWAYS RETURN HERE

OUTGO1::SKIPN	OKFLAG		;HEALTHY?
	POPJ	P,		;NO, DON'T START ANY OUTPUT
	MOVE	T1,NOPIOW	;SEND NO-OP
OUTGO2:	PUSHJ	P,IMPOUT##	;YES, START OUTPUT IF NECESSARY
	  POPJ	P,		;WASNT NEEDED
	setzm	NowOut		; no buffer stream, just us.
	POPJ	P,

NOPiow:	iowd	HTIWds, MS.NOP	;[96bit] POINTS TO NO-OP
MS.NOP:	;[96bit] three word nop message
	newfrm ! <4 ← 4>	;[96bit] new format flag + type:no-op
	HdSite##		;[96bit] the site number
	0			; final word is zero
HGDiow:	iowd	HTIWds,ms.hgd	;[96bit] point to host going down mess.
ms.hgd:	;[96bit] three word host going down message
	newfrm ! <2 ← 4>	;[96bit] new format + host going down
	HdSite## ! 377		;[96bit] site, plus first 8 bits of
				;	 message id.  in this case, id
				;	 means "down time not known"
	byte(4) 16,15		;[96bit] last 4 bits of "unknown", plus
				;[96bit] sub-type field 15 octal:
				;	 "down for unspecified reason",
				;	 until implemented.
SUBTTL IMPOUT	...	OUTPUT CONTROL ROUTINES ;⊗ IMPQLN IMPMES IMPMS1 IMPMS3

IMPQLN==↑D10			;MAXIMUM NUMBER OF MESSAGES AT A TIME

;SUBROUTINE TO PUT A MESSAGE IN THE IMP HIGH PRIORITY OUTPUT QUEUE.
;USES T1, T2.
;[96bit] EACH ENTRY IS a pointer to the MESSAGE ITSELF.
;CALL:
; 	MOVE	T1,MESSAGE location.  ((234) can't be thrown away
; 					after queuing.)
;	ScnOff
;	PUSHJ	P,IMPMES
;	ERROR RETURN -- QUEUE IS FULL
;	OK RETURN.   MESSAGE IS ON ITS WAY
IMPMES:	AOS	T2,IMPREQ	;BUMP, TEST IMP REQUESTS
	CAILE	T2,IMPQLN-1	;WITHIN BOUNDS?
	JRST	IMPMS3		;NO
	AOS	IMPQPP		;ADVANCE POINTER
	SOSLE	IMPQPC		;AROUND END?
	JRST	IMPMS1		;NO.
	MOVE	T2,[XWD IMPQLN,IMPQ]
	HLRZM	T2,IMPQPC	;RESET POINTERS
	HRRZM	T2,IMPQPP
IMPMS1:	MOVEM	T1,@IMPQPP	;PUT DATA IN BUFFER
	MOVE	T1,NOPIOW	;GET NO-OP MESSAGE
	MOVE	T2,IMPREQ	;THIS THE ONLY MESSAGE?
	CAIG	T2,1
	PUSHJ	P,OUTGO2	;YES,  RESTART OUTPUT.
	JRST	CPOPJ1##	;SKIP RETURN

IMPMS3:	SOS	IMPREQ		;NO ROOM.  DECREMENT COUNT
	POPJ	P,
COMMENT \

OUTPUT MESSAGE ASSEMBLY REGISTERS IN THE DDB

	MESSAGE LINKAGE POINTERS

OBFFST	FIRST BUFFER IN A MESSAGE
OBFLST	LAST BUFFER IN A MESSAGE
ObfByt	byte COUNT IN A MESSAGE

	BYTE PACKING REGISTERS

OBFTHS	ADDRESS OF THE BUFFER CURRENTLY BEING FILLED
OBFBC	COUNT OF FREE BYTES LEFT IN THE BUFFER
OBFPNT	POINTER INTO THE BUFFER.  POINTS TO THE LAST BYTE
	DEPOSITED.
\
;SUBROUTINE TO MAKE VARIOUS TESTS AND, IF THEY ARE ;⊗ OTBYTE OTBErr
;  SATISFIED, TRANSMIT A CHARACTER.  ON
;  AN ERROR RETURN, T2 IS NEGATIVE IF AN ALLOCATION FAULT
;  OCCURRED, POSITIVE IF THE CHARACTER WOULD CAUSE THE
;  MESSAGE SIZE TO EXCEED ITS MAXIMUM, OR ZERO IF AN INTERNAL
;  BUFFER FAILURE OCCURRED.
;CALL:
;	MOVE	P4, [COROUTINE LINKAGE] ;FIRST CALL ONLY
;	MOVE	T1, [CHARACTER]
;	MOVE	F, [ADDRESS OF IMP DATA BLOCK]
;	ScnOff
;	PUSHJ	P,OTBYTE
;	  ERROR RETURN	...  ALLOCATION(-), BUFFER(0) OR SIZE(+) FAILURE
;	OK RETURN  ...	CHAR BUFFERED FOR TRANSMISSION

IFWAITS<ENTRY OTBYTE>
OTBYTE:
;HERE TO TEST THE ALLOCATION, MESSAGE SIZE, ETC. AND BUFFER THE BYTE
	move	T2,ObfByt(F)	; get the count
	skipe	SndMax(f)	; is there a maximum specified?
	 camge	t2,SndMax(f)	; are we about to exceed receiving TCP's
				;  maximum message size?
;[fudge]  CAIL	T2,MAXMES	; or too big for an IMP message?
	  cail	t2,MaxMes-5	;[fudge] cover for off by one (or more)
				;[fudge]  arithmetic somewhere.
				; (note: we are ignoring options here)
	   aoja	t2,cpopj##	; make sure T2 is positive as a flag
	seto	t2,		; prepare to flag "window full"
	sosl	SndWnd(f)	; window size is one less.  room here?
	 JSP	P4,(P4)		; yes.  STUFF THE DATA
	  jrst	OTBErr		;BUFFER ERROR.  (T2 ZEROed if from Co-routine)
	aos	OBfByt(f)	; count another byte output
	JRST	CPOPJ1##	;GOOD RETURN

OTBErr:	aos	SndWnd(f)	; restore window
	popj	p,
;SUBROUTINE TO APPEND AN 8 BIT BYTE TO THE OUTPUT STREAM. ;⊗ OUBYT0 CpByte OUBYT2 OUBYT3 OuBy36 OUBYT4
;  THIS ROUTINE IS ONE-HALF OF A CO-ROUTINE.
;  OBFBC HAS THE FREE BYTE COUNT IN THE CURRENT BUFFER.
;  OBFPNT CONTAINS A BYTE POINTER TO THE LAST BYTE REFERENCED.
;CALL:
;	MOVE	P4, [OUBYTE]	;FIRST CALL ONLY
;	MOVE	F, [ADDRESS OF DATA BLOCK]
;	MOVE	T1, [THE DATA BYTE]
;	ScnOff
;	JSP	P4,(P4)
;	ALWAYS RETURN HERE

OUBYT0:	JSP	P4,1(P4)
;	OUBYTE::
CpByte:
	SOSGE	OBFBC(F)	;DECREMENT BYTE COUNT
	JSP	T4,OUBYTG	;GET ANOTHER BUFFER
	IDPB	T1,OBFPNT(F)	;DEPOSIT THE BYTE
	JSP	P4,1(P4)	;AND RETURN
OUBYT2:	SOSGE	OBFBC(F)	;COUNT
	JSP	T4,OUBYTG	;NEW BUFFER
	IDPB	T1,OBFPNT(F)	;DEPOSIT
	JSP	P4,1(P4)	;RETURN
OUBYT3:	SOSGE	OBFBC(F)
	JSP	T4,OUBYTG
	IDPB	T1,OBFPNT(F)
	JSP	P4,1(P4)
OuBy36:	; where to start copying 36 bit buffers in after 1822 leader.
OUBYT4:	SOSGE	OBFBC(F)
	JSP	T4,OUBYTG
	IDPB	T1,OBFPNT(F)
	JSP	P4,1(P4)


;FALL INTO OUBYT5
;HERE TO PUT FIFTH BYTE;  THE ONE THAT WRAPS AROUND THE ;⊗ OUBYT5 OUBYT6 OUBYT7 OUBYT8 OUBYT9
;  WORD BOUNDARY.
OUBYT5:	SOSGE	OBFBC(F)	;COUNT THE BYTE
	JSP	T4,OUBYTG	;GET A NEW BUFFER
	HRR	T2,OBFPNT(F)	;GET THE POINTER ADDRESS
	HRLI	T2,(POINT 4,,35);POINT AT LAST 4 BITS
	ROT	T1,-4		;POSITION THE BITS
	DPB	T1,T2		;DEPOSIT THEM
	ROT	T1,4		;GET LOW 4 BITS
	IDPB	T1,T2		;DEPOSIT THEM IN NEXT WORD
	HRLI	T2,(POINT 8,,3)	;POINT AT NEXT BYTE
	MOVEM	T2,OBFPNT(F)
	JSP	P4,1(P4)	;RETURN
OUBYT6:	SOSGE	OBFBC(F)	;BACK TO OLD ALGORITH
	JSP	T4,OUBYTG
	IDPB	T1,OBFPNT(F)
	JSP	P4,1(P4)
OUBYT7:	SOSGE	OBFBC(F)
	JSP	T4,OUBYTG
	IDPB	T1,OBFPNT(F)
	JSP	P4,1(P4)
OUBYT8:	SOSGE	OBFBC(F)
	JSP	T4,OUBYTG
	IDPB	T1,OBFPNT(F)
	JSP	P4,1(P4)
OUBYT9:	SOSGE	OBFBC(F)
	JSP	T4,OUBYTG
	IDPB	T1,OBFPNT(F)
	JRST	OUBYT0		;LOOP FOR ANOTHER WORD PAIR
;SUBROUTINE TO GET ANOTHER BUFFER FOR OUTPUT, SET UP POINTERS, ;⊗ OUBYTG OUBYTH
;  COUNTERS, AND RETURN TO CALL - 2 IF
;  UNSUCCESSFUL, OR, IF SUCCESSFUL, RETURN TO CALL - 1.
;  ON SUCCESSFUL RETURN, T1 HAS IDPB POINTER TO THE BUFFER, T2
;  HAS FREE BYTE COUNT.
;CALL:
;	SOSGE	OBFBC(F)	;RETURN HERE FROM OUBYTG
;	JSP	T4,OUBYTG
;	  NEVER RETURN HERE (EXCEPT ON SOSGE)
;	  OR HERE
;	  OR HERE
;	RETURN HERE IF NO MORE BUFFERS, P4 DECREMENTED

OUBYTG:	PUSH	P,T1		;SAVE THE BYTE
	PUSHJ	P,OUBUFR	;SET UP BUFFER
	  SOJA	P4,OUBYTH	;ERROR.  RETURN TO CALL - 2.
	ASH	T2,-1		;COMPUTE (8 BIT) BYTE COUNT
	IMULI	T2,↑D9		;  FROM WORD COUNT
	MOVEM	T2,OBFBC(F)	;SET BYTE COUNT
	HRLI	T1,(POINT 8,,35);MAKE A BYTE POINTER
	MOVEM	T1,OBFPNT(F)
	POP	P,T1		;RESTORE DATA
	JRST	CpByte		;RETURN TO TOP OF COROUTINE

;HERE ON BUFFER ERROR
OUBYTH:	POP	P,T1		;CLEAR THE STACK
	JRST	-3(T4)		;AND TAKE ERROR RETURN
; routine to put one character into the monitor internal buffers while ;⊗ OuBy2 OuBytD OuByte OuBy6
;  doing a 1's complement 16 bit checksum into P3.

;GET HERE WHEN CAN DEPOSIT THE DATA
OuBy2:	IDPB	T1,OBFPNT(F)	;DEPOSIT THE BYTE
ifn FtChck,<	; if supporting checksumming
	pushj	p,CSmByt##		; add this byte to running
					;  checksum in P3.
>
OuBytD:	jsp	p4,1(p4)		; and return

;HERE ON ENTRY
IFWAITS<ENTRY	OUBYTE>
OuByte:	SOSL	OBFBC(F)	;ROOM?
	  JRST	OuBy2		;YES
	PUSH	P,T1
	PUSHJ	P,OUBUFR	;SET UP ANOTHER BUFFER
	  JRST	OuBy6		;ERROR
	lsh	t2,Wd2Byt		; words * 4 is bytes.
	MOVEM	T2,OBFBC(F)
	HRLI	T1,(POINT 8,,35)	;MAKE POINTER (start in 2nd
					;  word of the buffer.)
	MOVEM	T1,OBFPNT(F)
	POP	P,T1
	JRST	OuByte			; and try again

;HERE ON BUFFER ERROR
OuBy6:	POP	P,T1
	SOJA	P4,OuBytD	;ERROR RETURN
;SUBROUTINE TO SET UP AN OUTPUT BUFFER ;⊗ OUBUFR OUBUF2 OuBuf3
;  CALLED FROM OUBYTE, OUWORD.
OUBUFR:	SETZM	OBFBC(F)	;IN CASE
	PUSHJ	P,BufGet##	;GET THE SPACE
	  POPJ	P,		;UNAVAILABLE
	SKIPE	OBFTHS(F)	;FIRST?
	  JRST	OUBUF2		;NO
	SKIPe	OBFFST(F)	;IS THERE A FIRST BUFFER?
	  jrst	OuBuf3			; and finish up
	HRRZM	T1,OBFFST(F)	;NO, MAKE THIS BE IT
ifn FtChck,<	; take check sum
	setzb	p3,OBfCsm(f)		; clear both checksums
> ; ifn FtChck
	jrst	OuBuf3			; and finish up

;HERE TO APPEND TO A STREAM
OUBUF2:	HRLM	T1,(P)		;SAVE NEW ADDRESS
	PUSHJ	P,OUTBFX	;LINK LAST ONE
	HLRZ	T1,(P)		;RESTORE NEW ADDRESS
OuBuf3:	HRRZM	T1,OBFTHS(F)	;SET UP THIS ONE
	MOVEI	T2,ImpBfs##-NBHLen	;AVAILABLE SPACE in words
	JRST	CPOPJ1##	;POINT TO DATA AREA AND EXIT
; subroutine to decide if there's enough buffer space to send a message
OutPre::
	pushj	p,save2##		; get two Ps (P2 for the IDIVI)
	move	p1,OBfByt(f)		; get the byte count
	; add the number of bytes we should see in the leaders.
	addi	p1,<HTISiz/↑d8>+<IPLen##*4>+<TCPLen##*4>+NBfB36-1
	idivi	p1,NBfB36		; how many buffer's do we need?
	camg	p1,BufNum##		; do we have that many?
	  pjrst	cpopj1##		; yes!
	popj	p,			; no.

;SUBROUTINE TO LINK AN ENTIRE MESSAGE FOR TRANSMISSION.
; this routine assumes that enough buffers are available.  call
;  OutPre to check with SCNOFFed before calling this routine.
IFWAITS<ENTRY OUTBYT>
OUTBYT:
	seto	t2,			; use SndNxt as sequence number
OutBy0:	; entry to use sequence number in T2 for SndNxt
	push	p,t2			; save sequence number desired
	PUSHJ	P,OUTBFX		;LINK LAST BUFFER
	pop	p,t1			; retrieve sequence number wanted
	pjrst	TCPMak##		; send this to TCP for processing.
					;  TCP will send it to IP who
					;  will send it to 1822 (ImpMak),
					;  so it'll actually go onto
					;  the queues.
	subttl	ImpMak ;⊗ ImpMak ImpMa1 ImpMa2 ImpMa3 ImpMa4 MakErr MakEr1

;++
; Functional description:
;
;	prepare a message for output to the IMP by converting it to 36
;	bit buffers from 32 bit buffers and adding the 1822 leader to
;	it.  after it's all ready, we put it in the queues for transmission
;	and retransmission if necessary.  if there are any errors, this
;	routine STOPCDs.
;
; Important: enough buffers MUST be available for this routine before it
;	is called.  if it tries to get a buffer and can't, it STOPCDs.
;	use OutPre to check.
;
;
; Calling sequence:
;
;		move	f,DDB
;		move	t1,<1822 protocol number of calling protocol>
;		ScnOff
;		<insure that enough buffers are available by calling
;			OutPre>
;		pushj	p,ImpMak
;		<always returns here>
;
; Input parameters:
;
;	F - DDB
;	T1 - calling protocol number for 1822.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	none.
;
; Implicit outputs:
;
;	transmission and retranmission queues
;
; Routine value:
;
;	always returns non-skip, but may STOPCD if there are not enough
;	buffers for the conversion to 36 bits or if not enough free core
;	is available for a BIB.
;
; Side effects:
;
;	adds a message to the transmission queue for this DDB's host.
;	flushed buffer stream, but only if successfully queued in at
;	least the retranmission queue.
;--



ImpMak::
	pushj	p,save4##		; get all p's
	move	p4,t1			; save protocol calling us.
	pushj	p,BufGet##		; get a buffer
	 stopcd	CPOPJ##,DEBUG,GBM,	;++ guarenteed buffer missing.


	move	p1,ObfByt(F)		; get byte COUNT
	lsh	p1,3			; times 8 to get bit count
	stor.	p1,HTILen,NBHLen(t1)	; put bit count in imp leader
	movx	p1,newfrm		; set up the constant fields
					; (including message type of 0)
	stor.	p1,HTIIni,NBHLen(t1)	; init init area
	move	p1,NetAdr(f)		; get target host number
	stor.	p1,HTIAdr,NBHLen(t1)	; store in buffer.
	stor.	p4,HTILnk,NBHLen(t1)	; put calling protocol into message

	move	p1,t1			; get a copy of this buffer pointer
	add	t1,[point 8,3,23]	; fudge byte pointer for
					;  beginning of IP leader.
	movem	t1,OBfPnt+IMkDDB	; save the new pointer
	movei	t1,NBfByt		; get byte count for full buffer
	stor.	t1,NBHCnt,(p1)		; assume this first buffer will
					;  be full (if it isn't, we'll
					;  fill it in correctly later).
	movei	t2,<NBfB36-<HTISiz/↑d8>>; number of bytes available in
					;  the rest of this 36 bit buffer.
	movem	t2,OBfBC+IMkDDB		; number of byte left, this buffer.
	movei	p4,OuBy36		; set co-routine for correct
					;  place in word for IP beginning.
	setzm	OBfThs+IMkDDB		; no current buffer yet.
	movem	p1,OBfLst+IMkDDB	; and also as the last one.
	movem	p1,OBfFst+IMkDDB	; make this one the first, too.
					;  for CpByte.
	setz	p1,			; clear the first buffer pointer...
	exch	p1,OBfFst(f)		; ...and get the real first.
	push	p,f			; save our DDB
	movei	f,IMkDDB		; get the ersatz DDB

ImpMa1:	jumpe	p1,ImpMa4		; no more.  queue it up.
	load.	p2,NBHCnt,(p1)		; get byte count
	move	p3,[point 8,NBHLen(p1)]	; point at first byte

ImpMa2:	sojl	p2,ImpMa3		; if there's more, go get next byte

	ildb	t1,p3			; get byte
	jsp	p4,(p4)			; store byte
	  jrst	MakErr			; no buffers.  release what we've
					;  already allocated and return
	jrst	ImpMa2			; and loop

ImpMa3:	move	t1,p1			; get a copy
	load.	p1,NBHNxt,(p1)		; get next buffer
	pushj	p,BufRel##		; free the spent buffer.
	jrst	ImpMa1			; and deal with the new one

ImpMa4:	pushj	p,OutBfx		; link up any odd buffer
	movei	t1,NBfB36		; max bytes that could be here.
	sub	t1,OBfBC+IMkDDB		; minus bytes we didn't use.
	lsh	t1,1			; *2
	sosl	t1			; -1, and cover for 0 count
	  idivi	t1,↑d9			; /9, which gives the word count-1
	aos	t1			; get word count
	lsh	t1,wd2byt		; get word count times 4.
	skipe	t2,OBfLst+IMkDDB	; get last buffer
	  stor.	t1,NBHCnt,(t2)		; put word count into place

	pop	p,f			; get our own DDB back
	move	p1,OBfFst+IMkDDB	; get first buffer in the
					;  stream to put in the BIB
	pushj	p,MakBib##		; get a BIB and set it up as DDB
					;  wants it to be.
	  jrst	MakEr1			; couldn't get it.  pass the
					;  error back.
	pushj	p,Go1822		; put it in the host's output queue
	  jfcl				; just wait for it to be retranmitted.
MakOut:	SETZM	OBFFST(F)		; remember there's nothing more here
	SETZM	OBFLST(F)		;  now that we successfully got it
	setzm	OBfBC(f)		;  queued out there.
	setzm	OBfByt(f)		;  clear them all
	popj	p,			; and return.


MakErr:	pop	p,f			; remember to get back our DDB
	stopcd	.+1,DEBUG,MGB,		; missing guarenteed buffer
	move	t1,p1			; point at remains of stream we
					; were translating.
	pushj	p,RelBuf##		; flush it
	jrst	MakEr2			; and flush what we've created.

MakEr1:	stopcd	.+1,DEBUG,CGB,		;++ can't get BIB for ImpMak
MakEr2:	move	t1,OBfFst+IMkDDB	; get the first in the chain we
					;  were trying to build.
	pushj	p,RelBuf##		; flush it.
	jrst	MakOut			; make sure to clear everything
; subroutine to queue a TCP message for output from interrupt level.
; call with:
;		T2 - SEQ number to use, or -1 if SndNxt is OK.
SndMsg::
	skipe	OBfByt(f)		; any partial message?
	  pjrst	cpopj##			; yes.  it will send the things we
					;  want sent piggy-back.
	pushj	p,OutPre		; are there enough buffers to send it?
	  popj	p,			; no.  take error return.
	setzm	ObfThs(f)		; no current buffer
	setzm	ObfFst(f)		; and no first buffer
ifn FtChck,<	; if checksumming
	push	p,p3			; save P3
	setz	p3,			; fresh checksum
	pushj	p,OutBy0		; send message
	pop	p,p3			; get back P3
	pjrst	cpopj1##		; good return unless we DECRed for bad
>
ife FtChck,<	; to scared to checksum
	pjrst	OutBy0			; set up message (use T2 as sequence)
					;  and send it.
>
;SUBROUTINE TO APPEND AN ASSEMBLED BUFFER TO AN EXISTING STREAM. ;⊗ OUTBFX
;  THIS ROUTINE SHOULD BE CALLED ONLY WHEN THE BUFFER IS
;  FULL OR AT THE END OF THE MESSAGE.
;  OBFLST(F) POINTS TO THE LAST BUFFER IN THE OUTPUT STREAM(0
;  IF NONE) AND OBFFST(F) POINTS TO THE FIRST BUFFER IN THE
;  STREAM.
;CALL:
;	ScnOff
;	PUSHJ	P,OUTBFX
;	RETURN HERE
OUTBFX:	SKIPN	T1,OBFTHS(F)	;IS THERE A CURRENT BUFFER?
	  POPJ	P,		;NO
	movei	t2,NBfByt		; get max number of bytes in a buffer
	sub	t2,ObfBC(f)		; account for those we didn't use
	stor.	T2,NBHCnt,(t1)		;PLACE IN BUFFER
	SKIPN	T2,OBFLst(F)		;NEW STREAM?
	  movei	T2,OBFFST-NBHNof(F)	;YES. SET UP FIRST BUFFER ADDRESS
	stor.	T1,NBHNxt,(T2)		;LINK TO EXISTING STREAM
	movem	t1,OBfLst(f)		; make this the last now.
	SETZM	OBFTHS(F)	;NO CURRENT BUFFER
	SETZM	OBFPC(F)	;NEXT TIME START AT TOP
	POPJ	P,
SUBTTL CLOCK LEVEL STUFF ;⊗ IMPSEC

;NOTE:  THERE IS NO INTERLOCK IN THE IMP SERVICE TO PREVENT THIS CODE
;   FROM BEING EXECUTED DURING A CLOCK INTERRUPT OF UUO-LEVEL IMP
;   CODE INSIDE ScnOff...ScnOn (I.E. ScnOff DOES NOT
;   TURN OFF THE CLOCK).  THIS IS NOT NECESSARY AT PRESENT SINCE CLOCK
;   CODE IS INTERLOCKED WITH UUO-LEVEL CODE IN CLOCK1 (I.E. CLOCK
;   CODE IS NOT EXECUTED IF THE INTERRUPT PC IS IN EXEC MODE).  IF THIS
;   IS EVER CHANGED, THE REPEAT 0,< ... > CODE WILL HAVE TO BE ENABLED
;   (BELOW).


;HERE EVERY SECOND TO CHECK THINGS
IMPSEC::
IFN FTMP,<			;IF SMP SYSTEM, ONLY DO THIS ON CPU WITH IMP
	PUSH	P,F		;SAVE F 
	MOVEI	F,IMPDDB	;FIND OUT IF WE'RE ON RIGHT CPU
	LDB	T1,DEYCPF##	;GET CPU NUMBER FOR IMPS
	POP	P,F		;RESTORE F
	CAME	T1,.CPCPN##	;SAME?
	POPJ	P,		;NOPE, GET OUT WHILE WE CAN
>
	MOVEI	T1,IMPBFN##	;GET 10↑4 * FRACTIONAL BUFFER
	SUB	T1,BUFNUM##	;	UTILIZATION
	IMULI	T1,↑D10000
	IDIVI	T1,IMPBFN##
	MOVE	T2,BUFAVG##	;AND AVERAGE IT IN
	IMULI	T2,TIMBUF-1
	ADD	T1,T2
	IDIVI	T1,TIMBUF
	MOVEM	T1,BUFAVG##

REPEAT 0,<	;THIS CODE NOT NEEDED NOW BUT MIGHT BE SOMEDAY
	CONI	PI,T1		;GET STATUS OF PI SYSTEM
	ANDCAI	T1,177		;MASK COMPLEMENT OF ACTIVE CHANNELS
	TRNN	T1,IMPBTS##	;IMP DISABLED?
>		;END REPEAT 0
	AOSGE	CLKSEC		;NO, WAITING TO TAKE SYSTEM DOWN?
	POPJ	P,		;YES, RETURN IMMEDIATELY
	SETZM	CLKSEC		;NO
	SKIPGE	IMPUP		;WANT IT UP?
	PUSHJ	P,IMPCHK##	;YES,  HARDWARE OK?
	  JRST	DEDIMP		;NO!!!!
	  JRST	FLTIM0		;NO!!
	ScnOff			;LOCK OUT INTERRUPT STUFF

	setz	t1,			; set flag telling once a
					;  second code to do timeout
					;  checking, not unmerciful flushing.
	pushj	p,IPSec##		; do IP's once a second code
					;  (check FDB chain for timeouts).

	PUSHJ	P,OUTGO1	;SEND NO-OP EVERY SECOND
	SKIPe	OKFLAG		;IMP SYSTEM UP NOW?
	 pushj	p,sonpj1##	; yes. turn on interrupts and skip
	  PUSHJ	P,FLTIM1	;NO, MAKE IT BE UP (INTERRUPTS ON)
	SETOM	OKFLAG		;HARDWARE OK NOW
	MOVEI	F,IMPDDB	;SCAN THROUGH IMP DDBS
NOWAITS<
	PUSH	P,[IMPN]	;COUNT THE IMPS
>;NOWAITS
	; fall into loop
;HERE TO TEST AN IMP ;⊗ IMPCL2 ImpCl4 ImpCl5 ImpCl6
IMPCL2:	ScnOff			; shut down interrupts
	PUSHJ	P,TTYTST	;TTY CONNECTION?
	  jrst	ImpCl6			;YES, WITHOUT JOB CONTROL
	  jrst	ImpCl5			; yes, with job control
ImpCl4:	MOVEI	T1,IODATA	;SETUP DATA-IN FLAG
	SKIPE	IBFTHS(F)	;ANY INPUT DATA BUFFERED?
	  IORM	T1,DEVIOS(F)	;YES, SET FLAG IN CASE CLOBBERED BY RELEASE
	ScnOn				; interrupts ok for a while
	pushj	p,TcpChk##		; let TCP check this DDB out.
	HLRZ	F,DEVSER(F)	;GET NEXT
NOWAITS<
	SOSLE	(P)		;COUNT
>;NOWAITS
IFWAITS<
	CAIE F,IMP.NX##
>;IFWAITS
	JRST	IMPCL2		;LOOP FOR MORE
	pjrst	tpopj##			; clear entry off stack and return

; here for a tty connection without job control
ImpCl5:	SKIPE	ITTYC(F)	; tty with job control.  IMP INPUT BACKED UP?
	  PUSHJ	P,RQIITI	; yes, ATTEMPT TO RESTART INPUT
; here with job control.
ImpCl6:	pushj	p,TCPWUp##		; update window information
	jrst	ImpCl4			; back into the loop
NOWAITS< ;⊗ ImpRem FstRem RemDsp LstRem RemBSL RemCNS RemCnO
;(271) here to process a remote function for the "FE".

ImpRem:	cail	t3,FstRem		; is it less that the first function?
	 caile	t3,LstRem		; or more than the last one?
	  popj	p,			; yes to one: we don't do it.
	pjrst	@RemDsp(t3)		; dispatch to function

FstRem==1		; first function we handle is function 1
RemDsp==.-FstRem	; make a zero offset label for the start
	RemBSL		; buffer space (for receiving characters) is low
	RemCNS		; buffer space is gone (character not stored)
	RemCnO		; control-O: turn on or off discarding characters
LstRem==.-RemDsp+FstRem	; last function we handle


RemBSL==cpopj1##	; do NOTHING, including:
			; 1) not sending a ↑Q (non-skip would send one)
			; 2) not updating allocation (we want him to stop!)

RemCNS==cpopj##		; no-op
RemCnO==cpopj##		; no-op
>;NOWAITS
;HERE IF IMP(OR INTERFACE) DEAD OR GOING DOWN ;⊗ DEDIMP DWNIMP FLTIM0 FLTIMP FLTIM1 Q4NOPS Q4Nop0 Q4NOP1
; call with interrupts still on.
DEDIMP:	ScnOff			; turn off interrupts for protection
	SETZM	OKFLAG		;FLAG BAD
	SKIPLE	IMPUP		;JUST GOING DOWN?
	JRST	DWNIMP		;YES
	SKIPN	DEDFLG		;ALREADY DOWN?
	  JRST	sonppj##		;YES.  interrupts back on and return
	SETZM	DEDFLG		;NO.  SET FLAG
	SKIPL	IMPUP		;INTENTIONAL?
	PUSHJ	P,IMPDWN##	;YES, TURN OFF
	JRST	sonppj##		; interrupts back on and return.

;HERE WHEN ABOUT TO TAKE THE TEN OFF THE NETWORK
; here with interrupts already off (ScnOff).  ScnOn is called at exit.
DWNIMP:	MOVEI	T1,↑D30		;WAIT 30 SECONDS
	MOVNM	T1,CLKSEC
	SETZM	IMPUP		;DOWN NEXT
	move	t1,HGDiow	;[96bit] SEND 'HOST GOING DOWN'
	PUSHJ	P,IMPMES
	  JFCL
	JRST	sonppj##		; interrupts back on and return

;HERE WHEN IMP WAS DOWN BUT IS NOW UP
; call with interrupts still on
FLTIM0:	SETZM	OKFLAG		;FLAG IMP SYSTEM DOWN
FLTIMP:	SETOM	FLTFLG		;FLAG THE ERROR
	ScnOff			; call off interrupts.
; here with interrupts already off (ScnOff).  ScnOn called at exit.
FLTIM1:	SKIPE	DEDFLG		;COMING UP FROM DOWN?
	  JRST	sonppj##		;NO.   just return
	SETOM	DEDFLG
	skipn	IBfHlt		; waiting for a buffer to appear?
	  PUSHJ	P,IMPION##	; no.  TURN INPUT BACK ON
	jrst	Q4Nop0		; queue up 4 no-ops, interrupts already off.

;ROUTINE TO QUEUE 4 NOPS FOR OUTPUT TO THE IMP
Q4NOPS:	ScnOff			; turn off interrupts
Q4Nop0:	MOVEI	T3,4		;QUEUE UP SOME NO-OPS
Q4NOP1:	move	t1,NOPiow	;[96bit] point to no-op message
	PUSHJ	P,IMPMES	;[96bit] queue one
	  JFCL
	SOJG	T3,Q4NOP1
	JRST	sonppj##		; all done.  interrupts enabled
					;  and go.
;ROUTINES TO REQUEST IMP PROCESSING AT CLOCK LEVEL.  IMP I/O ROUTINES ;⊗ RQIITI RQTOIO RQIITO RQTIIO IRQSET
;  MAY NOT BE CALLED AT OTHER THAN IMP-INTERRUPT, CLOCK, AND UUO LEVEL
;  TO AVOID SERIOUS RACE CONDITIONS.
;	MOVE	F,[IMP DDB ADDRESS]
;	PUSHJ	P,RQWXYZ
;	ALWAYS RETURN HERE WITH ALL AC'S PROTECTED
;  WHERE
;	W AND Y = "I" FOR "IMP" OR "T" FOR "TTY"
;	X AND Z = "I" FOR "INPUT" OR "O" FOR "OUTPUT"

RQIITI::PUSHJ	P,IRQSET	;IMP INPUT TO TTY INPUT (IMP LINE)
RQTOIO::PUSHJ	P,IRQSET	;TTY OUTPUT TO IMP OUTPUT (IMP LINE)
RQIITO::PUSHJ	P,IRQSET	;IMP INPUT TO TTY OUTPUT (CROSSPATCHED TTY)
RQTIIO::PUSHJ	P,IRQSET	;TTY INPUT TO IMP OUTPUT (CROSSPATCHED TTY)

;COMMON ROUTINE TO SET IMP REQUEST BITS
IRQSET:	EXCH	T1,(P)		;SAVE T1 AND GET ROUTINE ADR +1
	SUBI	T1,RQIITI+1	;CONVERT TO OFFSET FFROM FIRST ROUTINE
	PUSH	P,T2		;SAVE ANOTHER AC
	MOVSI	T2,TTYRQF&<-TTYRQF> ;SETUP RIGHTMOST REQUEST BIT
	LSH	T2,(T1)		;SHIFT BIT TO CORRECT POSITION
	SYSPIF
	IORM	T2,TTYLIN(F)	;SET REQUEST BIT IN DDB
	SETOM	IMPRQF##	;SET MASTER IMP REQUEST FLAG FOR CLOCK1
	SYSPIN
	POP	P,T2		;RESTORE T2
	JRST	TPOPJ##		;RESTORE T1 AND RETURN
;ROUTINE CALLED BY CLOCK1 ON ANY CLOCK TICK DURING WHICH IMPRQF IS ;⊗ IMPTIK IMPTK1 IMPTK2 IMPTKA IMPTK3 imptkx IMPTK4 IMPTK5 IMPTKT
;  SET, TO PERFORM REQUESTED IMP PROCESSING.

IMPTIK::
REPEAT 0,<			;INTERLOCK NOT NEEDED AT PRESENT (SEE IMPSEC)
	CONI	PI,T1		;GET STATUS OF PI SYSTEM
	ANDCAI	T1,177		;MASK COMPLEMENT OF ACTIVE CHANNELS
	TRNE	T1,IMPBTS##	;IMP DISABLED?
	POPJ	P,		;YES, RETURN IMMEDIATELY (WAIT FOR NEXT TICK)
>				;END REPEAT 0
	MOVEI	F,IMPDDB	;GET DDB FOR FIRST IMP
IFN FTMP,<
	LDB	T1,DEYCPF##	;GET CPU NUMBER FOR IMPS
	CAME	T1,.CPCPN##	;SAME?
	POPJ	P,		;NOPE, GET OUT WHILE WE CAN
>
	SETZM	IMPRQF##	;CLEAR MASTER IMP REQUEST FLAG
NOWAITS<
	PUSHJ	P,SAVE1##	;SAVE ANOTHER AC
	MOVEI	P1,IMPN##	;START IMP COUNTER
>;NOWAITS
IMPTK1:	MOVSI	T1,TTYRQF	;ALL REQUEST BITS TO TEST
IMPTK2:	syspif			; guard against a race
	TDNE	T1,TTYLIN(F)	;ANY PROCESSING REQUEST IN FOR THIS IMP?
	JRST	IMPTK3		;YES, HANDLE IT
	syspin			; PI back on
	SKIPN	ITTYC(F)	;NO FLAGS, BUT BUFFERS COULD BE BACKED UP
	SKIPE	IBFTHS(F)	;SO CHECK THAT
	JRST	IMPTK4		;WAS SOMETHING THERE
IMPTKA:	HLRZ	F,DEVSER(F)	;NO, TRY NEXT
NOWAITS<
	SOJG	P1,IMPTK1	;BACK IF ANY IMP DDB'S REMAIN
>;NOWAITS
IFWAITS<
	CAME F,IMP.NX##
	 JRST IMPTK1
>;IFWAITS
	POPJ	P,		;RETURN TO CLOCK1

;HERE WHEN FOUND AN IMP DDB NEEDING SERVICING
IMPTK3:	AND	T1,TTYLIN(F)	;GET THE REQUEST BITS
	JFFO	T1,imptkx	;FIND POSITION OF FIRST ONE
	 stopcd	.,STOP,NRB	;++ no resuest bit set
imptkx:	MOVE	T1,IMPTKT-↑L<TTYRQF>+↑D18(T2) ;FETCH CORRESPONDING ENTRY
	HLLZ	U,T1		;CLEAR THE BIT WE JUST FOUND
	ANDCAB	U,TTYLIN(F)	;  AND SETUP U TO TTY LDB
	SYSPIN			;TURN THEM BACK ON
	HRLM	F,(P)		;SAVE F ACROSS TELETYPE ROUTINES
	PUSHJ	P,(T1)		;CALL THE SELECTED PROCESSING ROUTINE
	HLRZ	F,(P)		;RESTORE IMP DDB POINTER
	JRST	IMPTK1		;GO TEST THIS IMP AGAIN BEFORE CONTINUING

IMPTK4:	HRLM	F,(P)		;SAVE F ACROSS TTY ROUTINES
	PUSHJ	P,TTYTST	;CROSSPATCHED?
	JRST	[ PUSHJ	P,IMPTTO;YES, PROCESS THAT
		  JRST IMPTK5 ]
	JRST	[ PUSHJ	P,ITTYIN ;NO, MUST BE INCOMING IMP
		  JRST	IMPTK5 ]
IMPTK5:	HLRZ	F,(P)		;RESTORE F
	JRST	IMPTKA

;IMP CLOCK-LEVEL PROCESSING TABLE.  REQUEST BIT IN LH, ROUTINE IN RH
IMPTKT:	IRTIIO ,, IMPTTI	;CROSSPATCHED TTY INPUT TO IMP OUTPUT
	IRIITO ,, IMPTTO	;CROSSPATCHED IMP INPUT TO TTY OUTPUT
	IRTOIO ,, IMPTYC	;IMP LINE'S TTY OUTPUT TO IMP OUTPUT
	IRIITI ,, ITTYIN	;IMP INPUT TO TTY LINE INPUT
	subttl	initialization code ;⊗ INI INI1 ZERTAB ZERTBN

IFWAITS<ENTRY INI>
INI:
NOWAITS<
	PUSHJ	P,SAVE1##	;SAVE P1
>;NOWAITS
	PUSHJ	P,IMPDWN##	;TURN OFF HARDWARE

	MOVEI	F,IMPDDB	; start by clearing DDBS
NOWAITS<
	MOVEI	P1,IMPN
>;NOWAITS
INI1:	pushj	p,ImpWak		; wake job in case waiting for anything.
	PUSHJ	P,DDBFls##		;CLEAR OUT A DDB
	pushj	p,DDBRel##		; release IMPs from jobs.
	HLRZ	F,DEVSER(F)
NOWAITS<
	SOJG	P1,INI1		;LOOP ON DDBS
>;NOWAITS
IFWAITS<
	CAIE F,IMP.NX##
	 JRST INI1
>;IFWAITS

	seto	t1,			; tell IP once a second code to
					;  flush all FDBs.  show no mercy.
	pushj	p,IPSec##		; flush FDBs in IPSER.

	MOVSI	T1,-ZERTBN	;GET WIPE TABLE
	MOVE	T2,ZERTAB(T1)	;GET ONE WIPER
	SETZM	(T2)		;CLEAR A LOCATION
	AOBJN	T2,.-1		;LOOP
	AOBJN	T1,.-3		;GET NEXT TABLE ENTRY

	MOVEI	T1,IMPBFN##	;INIT BUFFER COUNTERS
	MOVEM	T1,BUFNUM##
	MOVEM	T1,BUFAVG##

	PJRST	HstIni			; initialize host tables

;TABLE OF TABLES TO WIPE
ZERTAB:	-ZERON	,,  ZERO	;DATA BASE IN IMPSER
	ImpDCn##,,  ImpDat##	; gettab data base in NetSub
	-ITYN##	,,  ITYTAB##	;PSEUDO TELETYPE 'ITY' LINKAGE TABLE
	-IMPB36##,,  IMPBFT##	;BUFFER ALLOCATION TABLE
	TCPDCn##,,  TCPDat##	; TCP data area.

	ZERTBN==.-ZERTAB
NOWAITS< ;Several pages of NOWAITS start here ;⊗ OCLSE IMPDSP
SUBTTL IMPUUO	...	UUO HANDLING ROUTINES AND INITIALIZATION

;HERE FROM UUOCON ON AN OUTPUT CLOSE UUO
OCLSE:	pushj	p,TCPCls##		; tell TCP about the close.
					;  close does a TCP push.
	PUSHJ	P,OUT##		;SEND LAST BUFFER(S)
	MOVSI	S,IO	;SET FLAG
	IORM	S,IMPIOS(F)
	IORB	S,DEVIOS(F)
	PJRST	IMPWK1		;CLEAR FLAGS

;DISPATCH TABLE

	JRST	REGSIZ##	;BUFFER SIZE CAN BE GOTTEN FROM DDB
	JRST	INI		;INITIALIZE
	JRST	IMPHNG		;HUNG DEVICE TIMEOUT
IMPDSP::JRST	IMPREL		;(150) RELEASE (CHANGED FROM IMPWK1)
	JRST	OCLSE		;OUTPUT CLOSE
	JRST	OUTPT		;OUTPUT
				;FALL INTO INPUT
;HERE FROM UUOCON ON AN INPUT UUO ;⊗ INPT INPT01 INPT02
INPT:	PUSHJ	P,SAVE4##	;SAVE SOME ACS
	MOVSI	S,ALLWAT!IOBRKF!IO!IOFST
	ANDCAM	S,IMPIOS(F)
	HRRI	S,IODATA
	ANDCAB	S,DEVIOS(F)	;CLEAR FLAGS
	TLNN	S,IOBEG		;FIRST TIME AROUND?
	JRST	INPT01		;NO
	SETZM	ISHREG(F)	;YES
	MOVSI	S,IOFST!IOBEG	;FIRST IO FLAG
	XORB	S,DEVIOS(F)
INPT01:	PUSHJ	P,SETBYT##	;SET BYTE SIZE
	HLLM	T1,DEVPTR(F)
	MOVSI	S,IDATWT
	IORM	S,IMPIOS(F)
	IORB	S,DEVIOS(F)	;SET IO WAIT FLAGS
	ScnOff			; avoid anarchy
	PUSHJ	P,INBYTC	;CALL CHECK ROUTINE
	  JRST	INPT02		;DATA!
	JRST	INPT11		;NO

INPT02:	ScnOn

	HRRZS	DEVIAD(F)	;CLEAR RELOCATION FACTOR
	HRR	P1,DEVIAD(F)	;GET USER'S INPUT BUFFER ADDRESS
	EXCTUX	<LDB J,[POINT 17,@DEVIAD(F),17]>	;DO THE ITMSET STUFF HERE
	SUBI	J,1
	PUSHJ	P,ITMCNT##	;GET BYTE COUNT
	JUMPLE	J,IMPWK1	;EXIT ON 1 WORD BUFFER
	MOVEM	J,DEVCTR(F)	;SAVE IT FOR COUNTING
	HLL	P1,DEVPTR(F)	;MAKE BYTE POINTER
	ADDI	P1,1		;POINT AT DATA AREA
	MOVEI	P4,0
	EXCH	P4,IBFPC(F)	;GET COROUTINE LINK, IF ANY.
	JUMPN	P4,INPT17	;PROCEED IF ALREADY SET
	MOVEI	P4,INBYTE	;ASSUME TEXT
				;FALL INTO TRANSFER LOOP
;HERE TO GET AN INPUT BYTE ;⊗ INPT17 INPT18
INPT17:	MOVSI	S,IDATWT	;SET WAIT FLAG
	IORM	S,IMPIOS(F)
	IORB	S,DEVIOS(F)
	ScnOff
	JSP	P4,(P4)		;GET A BYTE
	  JRST	INPT19		;NO MORE
	aos	IBfByt(f)		; count bytes read
	ScnOn
	MOVSI	S,IOFST		;CLEAR FIRST DATA FLG
	ANDCAB	S,DEVIOS(F)
	EXCTUU	<IDPB	T1,P1>	;STORE IT
	SOSLE	DEVCTR(F)	;COUNT
	JRST	INPT17		;AND LOOP

;HERE WHEN USER BUFFER EXHAUSTED
INPT18:	SKIPLE	IBFBC(F)	;ANY INPUT LEFT?
	  HRRZM	P4,IBFPC(F)	;YES, SAVE LINKAGE
	PUSHJ	P,ADVIBF	;ADVANCE BUFFER
	  JFCL			;NEXT BUFFER FULL
	ScnOff			; avoid confusion
	PUSHJ	P,InBytC	;MAKE SURE NO MORE
	  SKIPA
	JRST	INPT21		;EMPTY
	MOVEI	S,IODATA	;SET DATA FLAG
	IORB	S,DEVIOS(F)
	JRST	INPT09		;DONE: tell NCP and free interrupts
;HERE WHEN STREAM EXHAUSTED BEFORE USER BUFFERS ;⊗ INPT19 INPT20 INPT21 INPT09 INPT11 INP11A INPT13 Inpt14 INPT12 ADVIBF
INPT19:	MOVEI	S,IODATA	;CLEAR INPUT DATA FLAG
	ANDCAB	S,DEVIOS(F)

;HERE WHEN INPUT EXHAUSTED
INPT20:	PUSHJ	P,ADVIBF	;YES, ADVANCE BUFFERS
	  JFCL			;FULL
INPT21:	PUSHJ	P,TCPIFn##	;TEST FOR CLOSED
	  JRST	inpt14			; closed.  interrupts are on.
					;  tell user about EOF.

;HERE WHEN DONE
INPT09:	PUSHJ	P,TCPWUp##	; update window information
	ScnOn			; allow interrupts again
	PJRST	IMPWK1

;HERE IF NO DATA READY UPON ENTRY
INPT11:	PUSHJ	P,TCPICK##	;OPEN?
	  JRST	INPT13		;NO
	SKIPE	OKFLAG
	SKIPE	STOPFLG		;IMP OK?
	JRST	INPT12		;NO
	MOVEI	T1,DEPAIO	;(150) CHECK FOR NON-BLOCKING
	TDNN	T1,DEVAIO(F)	;(150) IS IT?
	JRST	INP11A		;(150) NO
	MOVEI	S,IODATA	;(150) YES, CLEAR FLAGS
	ANDCAM	S,DEVIOS(F)	;(150)
	MOVSI	S,IDATWT	;(150) LET THE REST OF IMPSER
	IORB	S,IMPIOS(F)	;(150) WE NEED A SIGNAL WHEN DONE
	pjrst	sonppj##		; interrupts back on and return
INP11A:				;(150) LABEL ADDED
	PUSHJ	P,IMPW60	;WAIT
	JRST	INPT01		;TRY FROM TOP

;HERE IF SOCKET NOT OPEN
INPT13:	ScnOn			; interrupts are safe for democracy.
Inpt14:	MOVSI	S,IOEND		;END OF FILE
	IORB	S,DEVIOS(F)
	SKIPN	IBFTHS(F)	;ANY DATA IN  BUFFERS?
	TLNN	S,IOFST		;NO.  WAS ANY INPUT?
	JRST	IMPWK1		;YES
	MOVEI	S,IOIMPM	;NO.  ERROR
	IORB	S,DEVIOS(F)
	PJRST	IMPWK1

;HERE IF IMP NOT OK
INPT12:	ScnOn
	PJRST	HNGSTP		;TYPE "OK?" MESSAGE


;ROUTINE TO PLACE WORD COUNT IN THE USER'S CURRENT INPUT BUFFER AND ADVANCE
;  TO NEXT.    P1 CONTAINS POINTER TO LAST DATUM PLACED:
;	  POINT XX,BUFF(F),XX
;	PUSHJ	P,ADVIBF
;	  NO MORE BUFFERS RETURN
;	NORMAL RETURN

ADVIBF:
IFE FTKI10!FTKL10,<
	MOVE	T1,DEVIAD(F)	;GET ADDRESS OF 2ND WORD OF BUFFER
>
IFN FTKI10!FTKL10,<						;DK/ET-MAR 75
	HRRZ	T1,DEVIAD(F)	;GET ADDRESS OF SECOND WORD O BUFFER
>
	AOS	T2,T1		;POINT AT 3RD WORD(WORD COUNT)
	SUBM	P1,T1		;WORD COUNT TO T1[RH]
	HLLM	P1,T1		;STORE LH OF BYTE PTR SO UUOCON CAN COMPUTE
	EXCTXU	<MOVEM	T1,@T2>	;  A CORRECT BYTE COUNT
	PJRST	ADVBFF##	;AND ADVANCE THE BUFFERS
;HERE ON OUTPUT UUO FROM UUOCON ;⊗ OUTPT OUT01 OUT01A OUT02
OUTPT:	PUSHJ	P,SAVE4##	;GET PERMANENTS ACS
OUT01:	SKIPE	OKFLAG		;ALL OK?
	SKIPE	STOPFL
	JRST	OUT09		;NO
 	SKIPLE	P2,IMPBYT(F)	;(150) RESIDUAL BYTES FROM LAST TIME?
	SKIPN	P1,IMPPTR(F)	;(150)
	JRST	OUT01A		;(150) NO, RECOMPUTE
	MOVEM	P2,DEVCTR(F)	;(150) USE OLD COUNTS AND POINTERS
	JRST	OUT02		;(150)
OUT01A:	SETZM	IMPBYT(F)	;(150) CLEAR ANY DEAD RESIDUAL
	SETZM	IMPPTR(F)	;(150) BYTE COUNT
	PUSHJ	P,SETBYT##	;SET BYTE SIZE
	HLLM	T1,DEVPTR(F)
	MOVE	P1,DEVOAD(F)	;USERS BUFFERS
	ADDI	P1,1
	EXCTUX	<MOVE	J,@P1>	;BYTE PTR LH ,, WORD COUNT
	PUSHJ	P,ITMCNT##	;COUNT THE BYTES
	MOVEM	J,DEVCTR(F)	;SAVE BYTE COUNT
OUT02:	PUSHJ	P,TCPOCK##	;OPEN FOR OUTPUT?
	  JRST	OUT102		;NO
	MOVEI	P4,OUBYTE	;ASSUME BYTE MODE
	MOVSI	S,IO
	IORB	S,DEVIOS(F)
	SKIPG	DEVCTR(F)	;ANY DATA?
	JRST	OUT051		;NO
;TEST FOR ALLOCATION ;⊗ OUT022 OUT025 OUT026 OUT05 OUT051
OUT022:
	MOVSI	S,AllcWt	;SET WAIT FLAG
	IORM	S,IMPIOS(F)
	IORB	S,DEVIOS(F)
	pushj	p,TCPTCk##		; is there enough window available?
	  jrst	Out07			; no window or not enough.  wait.

;OUTPUT LOOP
OUT025:	EXCTUX	<ILDB	T1,P1>	;GET A CHARACTER
	ScnOff
	PUSHJ	P,OtByte		;BUFFER AND COUNT IT
	  JRST	OUT06		;LOSE!!!
	ScnOn
OUT026:	SOSLE	DEVCTR(F)	;COUNT THE BYTE
	JRST	OUT025		;LOOP FOR MORE
	SKIPG	ObfByt(F)	;DID WE BUFFER ANYTHING?
	JRST	OUT051		;NO, SO DON'T SEND ANYTHING

;HERE WHEN TRANSFER TO MONITOR BUFFER STOPPED.
OUT05:	skipg	DevCtr(f)		; is this the last byte in the
					;  buffer?
	  pushj	p,TCPPsh##		; yes.  let TCP do push handling.
	ScnOff
	pushj	p,OutPre		; predict it we will have enough
					;  buffers for this message.
	  jrst	[			; not enough room.
		 ScnOn			; interrupts back on
		 jrst	Out10		; indicate error
		]
	PUSHJ	P,OUTBYT	; send it out
	ScnOn
	SKIPLE	DEVCTR(F)	;ANY BYTES LEFT?
	JRST	OUT02		;YES

;HERE ON GENERAL EXIT TO UUOCON
OUT051:	PUSHJ	P,ADVBFE##	;ADVANCE BUFFER
	  JRST	OUT055		;NEXT ONE EMPTY.
	PUSHJ	P,IMPWK1	;CLEAR FLAGS
	JRST	OUT01		;AND DO NEXT BUFFER
;HERE WHEN ALL THROUGH ;⊗ OUT055 OUT07
OUT055:
	SETZM	IMPBYT(F)	;(150) CLEAR RESIDUAL BYTES
	SETZM	IMPPTR(F)	;(150) AND POINTER TO THEM
	MOVSI	S,IOBEG
	SKIPE	IBFTHS(F)	;ANY INPUT DATA?
	TDNN	S,DEVIOS(F)	;AND STILL VIRGIN INPUT SIDE?
	JRST	IMPWK1		;NO
	MOVEI	S,IODATA	;YES
	IORB	S,DEVIOS(F)	;SET DATA BIT
	JRST	IMPWK1

;HERE TO WAIT
OUT07:
	MOVEI	T1,DEPAIO	;(150) DON'T WAIT IF ASYNCH. IO
	TDNE	T1,DEVAIO(F)	;(150)
	  PJRST	outai1		;(150)
	ScnOff			; interlock so IMPW60 doesn't damage some
				;  one else's lock.  (lock the lock)
	PUSHJ	P,IMPW60	;WAIT A MINUTE
	JRST	OUT02		;TRY AGAIN
;HERE IF IMP OFF LINE OR SOME SUCH ;⊗ OUT09 OUT06 OUT10 OUT102 OUTAIO Outai1
OUT09:	PUSHJ	P,HNGSTP##	;TYPE USER MESSAGE AND STOP
	JRST	OUT01		;TRY AGAIN IF HE CONTINUES

;HERE IF FAILED TO PACK BYTE INTO BUFFER.  T2 CONTAINS CERROR FLAG
OUT06:	ScnOn			; allow interrupts again
	LDB	T1,[POINT 6,P1,11];GET BYTE SIZE
	ROT	T1,-6
	ADD	P1,T1		;BACK UP BYTE POINTER
	JUMPN	T2,OUT05	;IF NON-ZERO, MESSAGE SIZE OR ALLOCATION

;HERE IF NO BUFFERS LEFT
OUT10:	MOVEI	S,IODERR	;DEVICE ERROR
	SKIPA
OUT102:	MOVEI	S,IOIMPM	;IMPROPER MODE
	IORB	S,DEVIOS(F)
	ScnOff
	SKIPE	T1,OBFFST(F)	;IS THER A STREAM?
	PUSHJ	P,RELBUF##	;YES,  CLEAR IT
	setzm	OBFFST(F)	; clear pointer to stream.
	ScnOn
	JRST	OUT051

;(150) HERE WHEN LEAVING BECAUSE ASYNCH OUTPUT FAILED

OUTAIO:	ScnOn			; interrupts ok now (enter Outai1 if on now)
Outai1:	MOVEI	S,IOACT		;(150) CLEAR IOACT
	ANDCAM	S,DEVIOS(F)	;(150)
	HRRZ	T1,DEVCTR(F)	;(150) SAVE RESIDUAL BYTES
	MOVEM	T1,IMPBYT(F)	;(150) SALT AWAY
	MOVEM	P1,IMPPTR(F)	;(150) SAVE POINTER
	LDB	T2,PJOBN##	;(150) GET JOB NUMBER FOR THIS DDB
	MOVSI	T1,(JS.NIO)	;(150) INDICATE NON-BLOCKING OUTPUT
	IORM	T1,JBTLCL##(T2)	;(150)
	POPJ	P,		;(150)
;ROUTINE TO WAIT FOR INTERRUPT ACTIVITY.  RETURNS WHEN WOKEN AT ;⊗ IMPW60 IMPWAT IMPWA1
;   INTERRUPT LEVEL OR WHEN WAIT TIMES OUT.
;	(SET SOME WAIT FLAG IN IMPIOS)
;	MOVE	T1,[TIMEOUT CODE] ;TIMEOUT=4*2↑N SEC, 1 .LE. N .LE. 7
;	PUSHJ	P,IMPWAT
;	RETURN WHEN I/O DONE OR TIMER TIMES OUT (TIMFLG SET)
;	  WITH INTERRUPTS TURNED BACK ON!

IMPW60::
	MOVEI	T1,4		;60 SECOND SLEEP
IMPWAT::
	MOVEI	T2,2(T1)	;MULT BY 4 (ADD 2 TO EXPONENT)
	CAILE	T2,7		;MORE THAN STANDARD TIMER CAN HANDLE?
	MOVEI	T2,7		;YES, SET TO MAX (64 SEC)
	DPB	T2,PDVTIM##	;SET TIMEOUT IN DDB
	HRREI	T2,-5(T1)	;SET OVERFLOW COUNTER
	ASH	T2,1		;  2 FOR CODE 6, 4 FOR CODE 7
	ScnOn			; TURN ON INTERRUPTS
	MOVEI	T3,TIMFLG	; CLEAR TIMEOUT FLAG
	ANDCAM	T3,IMPIOS(F)
	MOVSI	T4,ALLWAT	;TEST ALL WAIT FLAGS
IMPWA1:	MOVE	S,DEVIOS(F)	;PICK UP I/O STATUS WORD
	PUSHJ	P,SETACT##	;SET IOACT (CLOBBERS T1 ONLY)
	TDNE	T4,IMPIOS(F)	;WAIT FLAG(S) STILL SET?
	PUSHJ	P,WSYNC##	;YES, WAIT (NO AC'S CLOBBERED)
	SOJG	T2,IMPWA1	;BACK IF TIMEOUT AND LARGE CODE GIVEN
	TDNE	T4,IMPIOS(F)	;WAIT FLAG STILL SET?
	IORM	T3,IMPIOS(F)	;YES, SET TIMEOUT FLAG
	PJRST	IMPWK1		;ENSURE FLAGS ARE CLEAR AND RETURN
;Last in series of NOWAITS pages ;⊗ IMPREL IMPAIO IMOR10 IMOR20 IMOR21 IMOR30 IMOR31 IMOR50
;(150) ROUTINE CALLED ON RELEASE

IMPREL:	SETZM	IMPBYT(F)	;(150) CLEAR OLD POINTERS
	SETZM	IMPPTR(F)	;(150)
	PJRST	IMPWK1		;(150)

;(150) ROUTINE CALLED AT CLOCK LEVEL TO CONTINUE PROCESSING
;(150) NON-BLOCKING IMP OUTPUT.

IMPAIO::			;(150)
IFN FTMP,<			;(150)
	SKPCPU(0)		;(150) ONLY ON MASTER
	POPJ	P,
>

	PUSHJ	P,SAVE2##	;(150) GET SOME AC'S
	MOVEI	F,IMPDDB	;GET THE FIRST DDB
	MOVEI	P1,IMPN##	;AND THE NUMBER OF IMP DDB'S
	MOVSI	P2,(JS.NIO)	;SET TO CLEAR BIT (FOR ANDCAM LATER)
	MOVE	T1,.C0PC##	;GET PC
	TLNE	T1,USRMOD	;IN USER MODE?
	MOVEM	T1,JOBPD1##(R)	;YES, MAKE SURE JOBPD1 IS RIGHT
IMOR10:	LDB	T1,PJOBN##	;THIS DDB BELONG TO THIS JOB
	CAIN	T1,(J)		;
	SKIPN	IMPBYT(F)	;AND OUTPUT PENDING?
	JRST	IMOR20		;NO, TRY NEXT ONE
	MOVEI	T1,DEPAIO	;NON-BLOCKING I/O GOING ON?
	TDNN	T1,DEVAIO(F)	;
	JRST	IMOR20		;NO, LOOK AT NEXT ONE
	PUSHJ	P,IMOR30	;ALL O.K., FINISH UP OUTPUT
;	PJRST	IMOR20		;FALL THRU TO GET NEXT DDB

IMOR20:	SOJLE	P1,IMOR21	;ANY LEFT
	HLRZ	F,DEVSER(F)	;GET LINK TO NEXT ONE
	JRST	IMOR10		;CHECK IT
IMOR21:	ANDCAM	P2,JBTLCL(J)	;SET OR CLEAR THE BIT AS DETERMINED
	POPJ	P,		;BY IMOR30 AND LEAVE

IMOR30:
IFN FTVM,<
	HRRZ	T1,DEVOAD(F)	;SEE IF BUFFER IN CORE
	LDB	T2,PBUFSZ##	;CHECK THE WHOLE THING
	SOS	T1		;CHECK BUFFER-1 TOO
	ADD	T2,T1
	PUSHJ	P,ZRNGE##	;
	  JRST IMOR50		;NOT IN, GET IT IN
>
	PUSHJ	P,OUTPT		;CALL OUTPUT ROUTINE
	SKIPE	IMPBYT(F)	;EVERYTHING SENT?
	JRST	IMOR31		;APPARENTLY NOT.
	PUSHJ	P,PSIIOD##	;SIGNAL USER
	SKIPA
IMOR31:	MOVEI	P2,0		;NOT DONE, DON'T CLEAR BIT
	POPJ	P,

IFN FTVM,<
IMOR50:	MOVE	T3,.C0PC##	;JOB'S PC
	TLNN	T3,USRMOD	;IN USER MODE?
	POPJ	P,		;NO, TRY AGAIN LATER
	MOVE	T3,[EXP IC.UOU+TTYFLT##]
	MOVEM	T3,.C0PC##	;GO TO TTYFLT
	MOVEM	T1,.UPMP+.UPUPF	;STORE FAULT ADDRESS
	POPJ	P,
>
>;NOWAITS
; subroutine to tell the input code about new data. ;⊗ ImpNew
; call with F set to DDB.
ImpNew::
	PUSHJ	P,TTYTST	;TTY LINKAGE?
	  PJRST	RQIITO		;YES,  WITHOUT JOB CONTROL
	  PJRST	RQIITI		;YES,  WITH JOB CONTROL.
	MOVEI	S,IODATA	;SET DATA FLAG
	IORB	S,DEVIOS(F)
IFWAITS<
	MOVSI T1,INTINP##	;Give him an input interrupt
	PUSHJ P,INTCOM##
>;IFWAITS
	MOVSI	T1,IDATWT	;CHECK IO WAIT
	TDNN	T1,IMPIOS(F)
	  POPJ	P,		;NO
NOWAITS<
IFN FTPI,<			;(150)
	TLZ	S,IO		;(150) YES, FORCE TO INPUT
	PUSHJ	P,PSIIOD##	;(150) SIGNAL INPUT DONE
>
>;NOWAITS
	PJRST	IMPWAK		;YES
; subroutine to handle wake up after allocation has increased.  checks ;⊗ AlcNew
;	for TTY handling line and starts terminal up again.
AlcNew::
	pushj	p,ttytst		; what type of connection?
	  jrst	RQTIIO			; cross-patched out
	  jrst	RQTOIO			; cross-patched in
	movsi	t1,AllcWt		; waiting for allocation?
	TDNn	T1,IMPIOS(F)		; ?
	  popj	p,			; no.
IFN FTPI,<	; PSI?
	move	s,DevIOS(f)	; get device flgas
	TLo	S,IO		; FORCE TO output
	PUSHJ	P,PSIIOD##	; SIGNAL output DONE
>
	pjrst	ImpWak			; yes.  start up.
;ROUTINE TO WAKE THE JOB AT INTERRUPT LEVEL ;⊗ IMPWAK IMPWK1 IMPHNG IMPIOD
IMPWAK::
	PUSHJ	P,IMPIOD	;SET I/O DONE FOR JOB

;ROUTINE TO CLEAR ALL WAIT FLAGS AND RESET THE TIMEOUT COUNTER TO INFINITY
IMPWK1::MOVSI	S,ALLWAT!IOFST!IOW ;CLEAR THESE FLAGS
	DPB	S,PDVTIM##	;CLEAR TIMEOUT COUNTER
	ANDCAM	S,IMPIOS(F)	;CLEAR IN BOTH IOS WORDS
	ANDCAB	S,DEVIOS(F)
NOWAITS<
	PJRST	CLRACT##	;ENSURE IOACT IS OFF AND RETURN
>;NOWAITS
IFWAITS<
	PJRST CLRAC1##
>;IFWAITS

;HERE AT CLOCK LEVEL TO HANDLE HUNG DEVICE TIMEOUT
IMPHNG:	AOS	(P)		;PRESET SKIP RETURN TO BYPASS
				;HUNG DEVICE MSG

;ROUTINE TO SET I/O DONE FOR A JOB WAITING FOR AN IMP
IMPIOD:	MOVE	S,DEVIOS(F)	;PICK UP DEVICE STATUS WORD
	TLNE	S,IOW		;IS THE JOB WAITING FOR THIS IMP?
NOWAITS<
;(165)	PUSHJ	P,SETIOD##	;YES, REQUEUE JOB TO I/O DONE STATE
	PUSHJ	P,STIIOD##	;(165) YES, REQUE TO IW DONE STATE
	PJRST	CLRACT##	;CLEAR IOACT AND RETURN
>;NOWAITS
IFWAITS<
	PUSHJ P,STTIO1##	;JJW - is this right?
	PJRST CLRAC1##
>;IFWAITS
;SUBROUTINE TO DETERMINE IF A DDB IS THAT OF AN IMP AND WHETHER OR ;⊗ IMPDEV IMPDV1 IMPRES IMPRS1
;   NOT IT IS CONTROLLING A JOB THROUGH AN ITY.
;	MOVE	F,[DDB ADDRESS]
;	PUSHJ	P,IMPDEV
;	  RETURN--NOT AN IMP OR NO CONNECTIONS OPEN
;	  RETURN--IMP NOT CONTROLLING A JOB
;	RETURN--IMP CONTROLLING A JOB
;	DESTROYS NO ACS

IMPDEV::PUSH	P,T1		;SAVE REGISTER
	HLRZ	T1,DEVNAM(F)	;GET LH OF PHYSICAL NAME
	CAIE	T1,'IMP'	;IS IT AN IMP?
	JRST	TPOPJ		;NO
	skipn	State(f)		; state closed?
	  pjrst	TPOPJ##			;RETURN IF NOT
IMPDV1:
;(252)	SKIPGE	TTYLIN(F)	;IMP CONNECTION OPEN, CONTROL A JOB?
	move	t1,ttylin(f)		;(252) get bits
	tlne	t1,ttyptr!ttykbd	;(252) controlling job's tty?
	AOS	-1(P)		;YES, SKIP TWICE
	JRST	TPOPJ1##	;NO


;ROUTINE TO CLEAN UP ALL IMP DDBS (CLEAR IOS FLAGS, ETC.) ASSIGNED TO THIS JOB.
;   CALLED FROM UUOCON ON A RESET UUO.
;	MOVE	J,[JOB NUMBER]
;	PUSHJ	P,IMPRES
;	ALWAYS RETURN HERE

IMPRES::
NOWAITS<
	PUSHJ	P,SAVE1##	;GET ANOTHER AC
	MOVEI	P1,IMPN##	;COUNT THE IMPS
>;NOWAITS
	MOVEI	F,IMPDDB	;START AT FIRST ONE
IMPRS1:	LDB	T1,PJOBN##	;GET OWNER OF THE DDB
	CAIN	T1,(J)		;THIS JOB OWN IT?
	PUSHJ	P,IMPWK1	;YES, CLEAN IT UP
	HLRZ	F,DEVSER(F)	;ADVANCE TO NEXT DDB
NOWAITS<
	SOJG	P1,IMPRS1	;LOOP THRU ALL IMP DDBS
>;NOWAITS
IFWAITS<
	CAIE F,IMP.NX##
	 JRST IMPRS1
>;IFWAITS
	POPJ	P,		;RETURN TO UUOCON
SUBTTL TELNET	...	TELETYPE ROUTINES ;⊗ TTYTST

COMMENT \

	THE INTERFACE BETWEEN THE TELETYPE SERVICE AND THE IMP SERVICE
IS DELICATE.  STRICT INTERLOCKING IS NECESSARY DUE TO THE ODD (VERY
FAST) RESPONSE TIMES.   IN SOME CASES, THE ACTIVITY MUST BE ASSUMED
TO BE INSTANTANEOUS.  DEC CODE IS OFTEN TOO SLOPPY TO WORK.   IN PARTICULAR,
IOACT, TOIP, IOW AND OTHER SUCH FLAGS MUST BE USED WITH CARE.

	INTERRUPT CODE IS IN SEVERAL PLACES CALLED FROM UUO LEVEL
AND, IN A FEW PLACES, INTERRUPTS ARE SIMULATED AT UUO LEVEL IN ORDER TO
PACK DATA FOR SPACE EFFICIENCY.
\


;SUBROUTINE TO TEST FOR A TELETYPE CONNECTION.
;  IF SO, PUTS PERTINENT DATA IN T1 AND U
;CALL:
;	MOVE	F,[IMP DATA BLOCK ADDRESS]
;	PUSHJ	P,TTYTST
;	  HERE IF TTY CONNECTION WITHOUT JOB CONTROL
;	  HERE IF TTY CONNECTION WITH JOB CONTROL
;	RETURN HERE IF NO TTY CONNECTION
TTYTST::
	SKIPE	T1,TTYLIN(F)	;ANY TTY DATA?
	TLNN	T1,TTYPTR!TTYKBD ;YES,  IS A TERMINAL THERE?
	JRST	CPOPJ2##	;NO, DOUBLE SKIP
IFN DEBUG,<
	TRNN	T1,-1		;MAKE DARN SURE WE REALLY HAVE ONE
	STOPCD	CPOPJ2##,DEBUG,NTC, ;++NO TELETYPE CONNECTION
>
	HRRZ	U,T1		;YES, LOAD TTY LDB ADDRESS
	HLL	U,LDBDCH##(U)	;LOAD TTY CHARACTERISTICS
	JUMPL	T1,CPOPJ1	;SKIP RETURN IF JOB CONTROL
	POPJ	P,		;NON-SKIP IF TTY WITH NO JOB CONTROL
;SUBROUTINE TO SET UP A CROSSPATCH BETWEEN AN IMP DDB AND A LOCAL ;⊗ IMPTTY TTIDET IMPATT
; TELETYPE.  NORMALLY CALLED FROM THE NCP AT UUO LEVEL.
;CALL:
;	PUSHJ	P,IMPTTY
;	ALWAYS RETURN HERE

IMPTTY::
	PUSHJ	P,TTYIMP##	;GIVE SCNSER THE IMP DDB
	JUMPE	T1,CPOPJ	;QUIT IF FAILED TO CONNECT
	HRLI	T1,TTYPTR+TTYKBD ;KEYBOARD AND PRINTER ALWAYS ATTACHED
	MOVEM	T1,TTYLIN(F)	;LDB ADDRESS
	SKIPN	ITTYC(F)	;ANY INPUT ALREADY BACKED UP?
	SKIPE	IBFTHS(F)	;  OR SITTING IN INPUT BUFFERS?
	PJRST	RQIITO		;YES, START OUTPUT
	POPJ	P,


;ROUTINE TO DETACH A CROSSPATCHED TTY FROM ITS CONTROLLED IMP DDB.
;	MOVE	F,[IMP DDB ADDRESS]
;	PUSHJ	P,TTIDET
;	ALWAYS RETURNS HERE, T1 AND T2 CLOBBERED

TTIDET::PUSH	P,U		;SAVE U (NEEDED BY NCP)
	MOVE	U,TTYLIN(F)	;GET POINTER TO CONNECTED TTY LDB
	TLNN	U,TTYKBD!TTYPTR	;IS IT CROSSPATCHED TO IMP?
	JRST	UPOPJ##		;NO, FORGET IT
	SETZM	TTYLIN(F)	;YES, CLEAR CROSSPATCH MODE
	SETZM	LDBIMP##(U)	;CLEAR POINTER FROM LDB TO IMP DDB
	TLNE	U,TTYXWT	;WAS JOB WAITING FOR CROSSPATCH TO BREAK?
	PUSHJ	P,IMPIOD	;YES, WAKE UP THE JOB
	PUSHJ	P,TSETBI##	;CLEAR TTY INPUT BUFFER
	SETZM	OTTYC(F)	;CLEAR ANY SAVED CHARACTER
	JRST	UPOPJ##		;RESTORE U AND RETURN


;ROUTINE TO SET THE JOB NUMBER INTO THE RIGHT IMP DDB WHEN INITIATING
;  A JOB FROM AN IMP TTY OR WHEN AN IMP TTY ATTACHES TO AN EXISTING JOB.
;	MOVE	J,[JOB #]
;	MOVE	U,[LDB ADDRESS]
;	PUSHJ	P,IMPATT
;	ALWAYS RETURN HERE

IMPATT::PUSH	P,F		;SAVE TTY DDB ADDRESS FOR SCNSER
	LDB	T1,LDPLNO##	;GET LINE NUMBER
	MOVE	F,ITYOFS##(T1)	;INDEX INTO ITYTAB
	PUSHJ	P,SETDVL##	;STORE JOB NUMBER AND ADD TO LOGICAL TABLE	DK/MAR 75
	JRST	FPOPJ##		;RESTORE F AND RETURN
;ROUTINE TO TRANSFER CHARACTERS FROM A TTY INPUT BUFFER TO AN IMP OUTPUT ;⊗ IMPTTI TTIGO TTINCH TTIMOR TTISVD TTICMN
; BUFFER FOR A TTY CROSSPATCHED TO THE NETWORK.  CALLED ONLY AT CLOCK LEVEL.
; THE ROUTINE SETS UP AN OUTPUT STREAM AND, IF OUTPUT IS NOT
; ALREADY GOING(RFNM WAIT) AND THE ALLOCATION IS SUFFICIENT, THE
; MESSAGE IS SENT.
;CALL:
;	MOVE	U, [LDB ADDRESS]
;	MOVE	F,LDBIMP(U)
;	JUMPE	F,.+3
;	PUSHJ	P,IMPTTI
;	ALWAYS RETURNS HERE

IMPTTI:	PUSHJ	P,TTYTST	;TEST FOR TTY
	  JRST	TTIGO		;OK
	  POPJ	P,		;N.G.
	POPJ	P,		;N.G.

;HERE TO START TRANSFERRING DATA
TTIGO:	PUSHJ	P,SAVE4##	;SAVE THROUGH P4
	scnoff				; guard against wierdness
	pushj	p,TCPTCk##		; enough window here?
	  jrst	TTyRn1			; no.  go see if we should send
					;  what's lined up.
	scnon				; interrupts ok.
	HRLM	F,(P)		;SAVE THE IMP DDB POINTER
	MOVEI	T1,LDROSU##	;DON'T KNOW WHEN FOREIGN
	ANDCAM	T1,LDBDCH##(U)	;  RENABLES OUTPUT, SO WE HAVE TO FAKE IT
TTINCH:	; used to be NoInterrupt, but that seems unnecessary.
	SKIPN	P4,OBFPC(F)	;GET OUTPUT ROUTINE ADDRESS
	MOVEI	P4,OUBYTE	;  FROM THE TOP
ifn FtChck,<	; take checksum
	move	p3,OBfCsm(f)		; get checksum.
> ; ifn FtChck
	MOVEI	T1,0
	EXCH	T1,OTTYC(F)	;SOMETHING ALREADY THERE?
	JUMPN	T1,TTISVD	;YES
	PUSHJ	P,TTYTTI##	;GET A CHAR
	  JRST	TTIDON		;NORMAL OUT
	HLRZ	F,(P)		;RESTORE IMP DDB POINTER
	TRNN	T3,400		;IMAGE MODE?
	ANDI	T3,177		;YES, FLUSH REMOTE ECHO BIT AND EVERYTHING ELSE
	ANDI	T3,377		;IGNORE IMAGE
	MOVEI	T1,(T3)		;MOVE WHERE WE CAN USE
TTIMOR:	SKIPGE	T4,TELOWD(F)	;TELNET CONTROL PROCESSING?
	JRST	TTICMD		;YES, GO CONTINUE IT
	CAIN	T1,.TNIAC	;SHOULD WE START PROCESSING?
	JRST	TTIIAC		;YES, DO SO.
TTISVD:	ScnOff			; protect our ass
	PUSHJ	P,OTBYTE	;TRY SENDING IT
	  JRST	TTISV1		;SAVE JUST ONE
	ScnOn			; another ass protected successfully
	MOVSI	T1,(TELOMR)	;CHECK TO SEE IF TELNET ROUTINE HAS MORE TO SEND
	TDNE	T1,TELOWD(F)
	JRST	TTIMOR		;YES IT DOES, ASK IT
	HRRZM	P4,OBFPC(F)	;SAVE NEXT BYTE ADDRESS
ifn FtChck,<	; take check sum
	movem	p3,ObfCsm(f)		; save checksum
> ; ifn FtChck
TTICMN:	; used to be match Interrupt
	JRST	TTINCH		;LOOP FOR MORE
;HERE ON IMP OUTPUT ERROR ;⊗ TTISV1 TTIDON TTIIAC TTICMD
TTISV1:	ScnOn			; interrupts ok, the damage is done.
	ANDI	T1,377		;MAKE SURE ONLY 1 CHAR
	HRROM	T1,OTTYC(F)	;STORE FOR WHEN WE WAKE UP
TTIDON:	HLRZ	F,(P)		;RESTORE DDB POINTER
	JRST	TTYRN0		;GO TRANSMIT ASSEMBLED STREAM

;HERE FOR GOING OFF TO TELNET CONTROL PROCESSOR
TTIIAC:	MOVEI	T4,IMPTOI	;START UP TELNET CONTROL HACKER
TTICMD:	PUSHJ	P,(T4)		;OR CONTINUE WHERE IT LEFT OFF
	  SETZ	T1,		;NOTHING TO PRINT
	HRRM	T4,TELOWD(F)	;SAVE WHERE IT LEFT
	JUMPE	T1,TTICMN	;GET NEXT IF NOTHING TO PRINT
	JRST	TTISVD		;ELSE PRINT IT FIRST
;ROUTINE TO TRANSMIT CHARACTERS FROM THE IMP INPUT BUFFER TO THE TTY ;⊗ IMPTTO TTO0
;  OUTPUT BUFFER FOR A TTY CROSSPATCHED TO THE NETWORK.  CALLED ONLY
;  AT CLOCK LEVEL.
;CALL:
;	SKIPE	F,LDBIMP(U)
;	PUSHJ	P,IMPTTO
;	ALWAYS RETURN HERE
IMPTTO:	PUSHJ	P,TTYTST	;TEST FOR TTY
	  JRST	TTO0		;OK
	  POPJ	P,		;N.G.
	POPJ	P,		;N.G.

;HERE TO TRANSFER TEXT FROM THE IMP INPUT BUFFER
;  TO THE TELETYPE OUTPUT BUFFER.

TTO0:	PUSH	P,P4		;SAVE P4
	HRRZ	U,TTYLIN(F)	;GET LDB ADDRESS
	PUSH	P,F		;SAVE DDB
	; fall into loop on next page
;HERE TO TRANSFER A CHARACTER. ;⊗ TTO05 TTO2 TTO2A TTO3 TTO4 TTO5
TTO05:	SETZB	P4,T1
	EXCH	T1,ITTYC(F)	;WAS THERE A CHARACTER?
	JUMPN	T1,TTO2		;JUMP IF SOMETHING THERE
	EXCH	P4,IBFPC(F)	;GET COROUTINE LINKAGE
	SKIPN	P4
	  MOVEI	P4,INBYTE	;START FROM TOP
	ScnOff			; lock down control of byte routines
	JSP	P4,(P4)		;GET ANOTHER CHARACTER
	  JRST	TTY4		;NONE
	aos	IBfByt(f)		; count bytes read
	ScnOn			; release hold on next byte routines
	HRRZM	P4,IBFPC(F)	;SAVE INPUT ROUTINE PC
	SKIPGE	T4,TELWRD(F)	;TELNET COMMAND IN PROCESS?
	JRST	TTO5		;YES, PROCESS IT
	CAIN	T1,.TNIAC	;'INTERPRET AS COMMAND' CONTROL CODE?
	JRST	TTO4		;YES, BEGIN TELNET COMMAND
TTO2:	skipe	RcvUrg(f)		; currently trying to get to
					;   urgent data?
	  JRST	TTO3			; YES, FLUSH mere characters until
					;  we spot a data mark.
TTO2A:	MOVSI	T2,(SYNCLR)	;NO, MARK THAT SYNC MAY CLEAR OUTPUT
	IORM	T2,TELWRD(F)
	MOVE	T3,T1		;PUT CHAR IN RIGHT AC
	PUSHJ	P,TTYXMT##	;SEND THE CHAR
	  JRST	TTY3		;NO ROOM
TTO3:	MOVE	F,(P)		;RESTORE DDB
	JRST	TTO05		;GET ANOTHER CHARACTER

;HERE TO INTERPRET TELNET CONTROL COMMANDS
TTO4:	MOVEI	T4,TTYIAC	;BEGIN COMMAND SEQUENCE ON 'IAC'
TTO5:	PUSHJ	P,(T4)		;CORETURN TO COMMAND PROCESSOR
	  SETZ	T1,		;NONSKIP MEANS FLUSH CHARACTER
	HRRM	T4,TELWRD(F)	;SAVE COROUTINE PC
	JUMPE	T1,TTO3		;BACK FOR MORE IF NO CHARS TO PASS THROUGH
	JRST	TTO2A		;(157) GIVE CHAR TO TTY FIRST
;HERE FROM IMP CLOCK LEVEL WHEN A MESSAGE HAS BEEN RECEIVED ;⊗ ITTYIN
; FOR A LOCAL PROCESS.
ITTYIN:	PUSH	P,P4		;SAVE P4
	PUSH	P,F
	HRRZ	U,TTYLIN(F)	;LDB ADDRESS
	; fall into loop on next page
;HERE TO TRANSFER A CHARACTER FROM THE IMP INPUT BUFFER TO ;⊗ TTYIN0 TTYIN2 TTYI2A TTYIN1 TTYIN6 TTYIN3 TTYIN4 TTYIN5
;  THE TELETYPE INPUT BUFFER.
TTYIN0:	SETZB	P4,T1
	EXCH	T1,ITTYC(F)	;ANYTHING LEFT OVER?
	JUMPN	T1,TTYIN1	;JUMP IF SO
	EXCH	P4,IBFPC(F)	;GET INPUT ROUTINE ADDRESS
	SKIPN	P4
	  MOVEI	P4,INBYTE	;START FROM THE TOP
TTYIN2:	ScnOff			; don't let others use next byte routines
	JSP	P4,(P4)		;GET A CHARACTER
	  JRST	TTY4		;NONE.  DONT SAVE P4.
	aos	IBfByt(f)		; count bytes read
	ScnOn			; ok, the danger is past
	HRRZM	P4,IBFPC(F)	;SAVE INPUT COROUTINE LINKAGE
	SKIPGE	T4,TELWRD(F)	;TELNET COMMAND IN PROGRESS?
	JRST	TTYIN5		;YES, PROCESS IT
	CAIN	T1,.TNIAC	;'INTERPRET AS COMMAND' TELNET CONTROL?
	JRST	TTYIN4		;YES, BEGIN CONTROL SEQUENCE
TTYI2A:	TRNE	T1,400		;SPECIAL CHAR HANDLING?
	JRST	TTYIN1		;YES, DON'T DIDDLE CRLF FLAG
	MOVSI	T2,TTYCRL
	AND	T2,TTYLIN(F)	;GET/CLEAR LAST-CHAR-A-CR BIT.
	ANDCAM	T2,TTYLIN(F)
	CAIN	T1,.CHLF	;LINE FEED?
	JUMPN	T2,TTYIN2	;YES.  IGNORE IF PRECEDING CR.
	MOVSI	T2,TTYCRL
	CAIN	T1,.CHCR	;IS IT CR?
	IORM	T2,TTYLIN(F)	;YES. SET THE BIT
TTYIN1:	skipe	RcvUrg(f)		; trying to get to urgent data?
	  JRST	TTYIN3			; yes.  throw out normal
					;  characters until we see a
					;  data mark.
TTYIN6:	MOVE	T3,T1		;CHARACTER INTO CHREC(T3)
	PUSHJ	P,RECIMP##	;SCANNER INTERRUPT CODE
;(271) if the character was flushed, there's nothing we can do.  continue
;(271)  to scan (and flush) anything else we have.  if we try to save it,
;(271)  we'll end up with filled buffers and/or we won't be able to see
;(271)  an incoming ↑C.
;(271)	  JRST	TTY3		;NOT ENOUGH ROOM
TTYIN3:	MOVE	F,(P)		;GET DDB ADDRESS BACK
	JRST	TTYIN0		;LOOP FOR MORE.

;HERE TO HANDLE TELNET CONTROL SEQUENCES
TTYIN4:	MOVEI	T4,TTYIAC	;START CONTROL ROUTINE FROM TOP
TTYIN5:	PUSHJ	P,(T4)		;CORETURN TO TELNET COMMAND PROCESSOR
	  SETZ	T1,		;NO CHAR TO PASS ON
	HRRM	T4,TELWRD(F)	;SAVE CORETURN PC
	JUMPE	T1,TTYIN3	;LOOP FOR MORE
	JRST	TTYIN6		;(157) GIVE TO TTY FIRST
;HERE WHEN NO MORE DATA FROM IMP CONNECTION.  (interrupts always off) ;⊗ TTY4 TTY3 TTY4B TTY4c TTY4A
TTY4:	MOVEI	T1,IODATA	;CLEAR DATA-IN FLAG
	ANDCAM	T1,DEVIOS(F)
	jrst	TTY4c		; interrupts are already off here

;HERE AT ANY LEVEL WHEN NOT ENOUGH ROOM IN TTY BUFFERS.
;(271) here if there wasn't enough room in TTY output buffer.  we save
;(271)  the character, but DON'T reset the allocation, because we CAN'T
;(271)  handle any more incoming.
TTY3:
;(271)	MOVE	F,(P)		;RESTORE IMP DDB ADDRESS
	pop	p,f		;(271) get back good F
	HRROM	T3,ITTYC(F)	;SAVE THE CHAR
	setom	ImpRQF##	;(271) flag for action next tick
	pop	p,p4		;(271) restore P4
	popj	p,		;(271) and return

TTY4B:	ScnOff			; protect TCPIFn and TCPWUp
TTY4c:	PUSH	P,U		;SAVE LDB ADDRESS
	PUSHJ	P,TCPIFn##	;ENSURE STILL OPEN
	  jrst	TTy4a		; not.  clear up stack and return (interrupts
				;  already on.)
	PUSHJ	P,TCPWUp##	;HANDLE ALLOCATION
	scnon				; reenable interrupts
TTY4A:	POP	P,U		;RESTORE LDB ADDRESS
	POP	P,F		;GET DDB ADDRESS OFF STACK
	POP	P,P4		;RESTORE P4
	popj	p,			; return
;ROUTINE TO TRANSMIT CHARACTERS FROM A TTY OUTPUT BUFFER TO AN IMP ;⊗ IMPTYC
;  OUTPUT BUFFER FOR AN IMP-CONTROLLED TTY LINE.  CALLED ONLY AT
;  CLOCK LEVEL

IMPTYC:	ScnOff		;PREVENT RACES
	PUSHJ	P,TTYTST	;TELETYPE CONNECTION?
	  JRST	sonppj##	;YES BUT NO JOB CONTROL
	  SKIPA	T1,OTTYC(F)	;YES, GET SAVED CHAR IF ANY
	JRST	sonppj##		;NO
	JUMPE	T1,TTYRN1	;IF NONE, JUST FINISH ANY PREVIOUS OUTPUT
	SETZM	OTTYC(F)	;CLEAR SAVED CHARACTER
	PUSH	P,F		;SAVE F (POPPED LATER)
	PUSH	P,P4		;ALSO POPPED LATER
	SKIPN	P4,OBFPC(F)	;GET COOROUTINE ADDR
	MOVEI	P4,OUBYTE	;OR START ANEW
ifn FtChck,<	; take check sum
	push	p,p3			; save P3
	move	p3,ObfCsm(f)		; get checksum back
> ; end of ifn FtChck
	JRST	IMPTY1		;ENTER THE MIDDLE OF THE FRAY, AFTER TELNET CHECK
;HERE FROM SCNSER AT UUO AND CLOCK LEVEL TO START ;⊗ IMPTYP IMPTY2 ImpTy0 ImpTy1 IMPTY7 IMPTY3 IMPTY5 ITYSTO
;   TRANSMISSION OF A CHARACTER IN T3.  AT THE END OF PROCESSING,
;   IMPTYP LOOPS BACK TO SCNSER TO SIMULATE ANOTHER INTERRUPT.
;   THE LOOP IS BROKEN WHEN THE TELETYPE OUTPUT BUFFER IS EMPTY
;   AND SCNSER CALLS THE XMTQIT ROUTINE.

IMPTYP:
	PUSH	P,F		;SAVE SCANNER DDB
	LDB	T1,LDPLNO##	;GET LINE NUMBER
	MOVSI	T2,TTYPTR!TTYKBD;BIT TO TEST
	SKIPE	F,ITYOFS##(T1)	;IS THERE AN IMP DDB CONTROLLING THIS LINE?
	TDNN	T2,TTYLIN(F)	;YES, IS IT ACTUALLY CONNECTED?
	JRST	IMPTY3		;IGNORE CHAR IF NO DDB
	TRZN	T3,400		;IMAGE BIT SET?
	ANDI	T3,177		;NO, DON'T BELIEVE PARITY BIT
	MOVEI	T1,(T3)		;PUT IN USABLE REGISTER
	PUSH	P,P4		;SAVE AN AC
	SKIPN	P4,OBFPC(F)	;GET OUTPUT ROUTINE ADDRESS
	MOVEI	P4,OUBYTE	;BYTE OUTPUT ROUTINE
ifn FtChck,<	; take check sum
	push	p,p3			; save out checksuming AC
	move	p3,ObfCsm(f)		; get checksum
> ; end of ifn FtChck
IMPTY2:	SKIPGE	T4,TELOWD(F)	;PROCESSING TELNET CONTROL?
	JRST	IMPTY9		;YES, GO CONTINUE IT
	CAIN	T1,.TNIAC	;TELNET ESCAPE?
	JRST	IMPTY8		;YES, GO START TELNET CONTROL PROCESSOR
ImpTy0:	ScnOff			; protect ourselves
ImpTy1:	pushj	p,TCPTCk##	; any window, and are we in a correct state?
	  jrst	ImpTy5		; no.  try to send what's there.
	PUSHJ	P,OTBYTE	;TEST AND BUFFER IT
	  JRST	IMPTY5		;TOO MUCH (LH OF T2 TELLS WHY)
	ScnOn			; we're ok
	MOVSI	T1,(TELOMR)	;CHECK TO SEE IF TELNET ROUTINE HAS MORE TO SAY
	TDNE	T1,TELOWD(F)
	JRST	IMPTY2		;YES IT DOES, ASK IT
	HRRZM	P4,OBFPC(F)	;SAVE COROUTINE ADDRESS
ifn FtChck,<	; take check sum
	movem	p3,ObfCsm(f)		; save checksum
> ; end of ifn FtChck
IMPTY7:
ifn FtChck,<	; take check sum
	pop	p,p3			; get back this reg.
> ; end of ifn FtChck
	POP	P,P4		;RESTORE AC'S
	POP	P,F
	JRST	XMTIn1##	;BACK FOR NEXT SCANNER INTERRUPT, U set up.

;HERE IF NO IMP CONNECTED TO THE LINE.
IMPTY3:	POP	P,F		;CLEAR THE STACK
	PUSHJ	P,TSETBO##	;CLEAR OUTPUT BUFFER
	JRST	XMTIn1##	;AND THROW OUT THE CHAR, get next.

;HERE IF BIT OR BUFFER SHORTAGE
IMPTY5:	ANDI	T1,377		;ENSURE JUST 1 CHAR HERE
	HRL	T1,T2		;SET FLAG WITH CHARACTER (flag unused)
	TRO	T1,1B18		;CHARACTER NEVER NULL
	MOVEM	T1,OTTYC(F)	;SAVE THIS CHARACTER
	PUSHJ	P,TTYRNM	;START TRANSMISSION, TURN ON INTERRUPTS
ifn FtChck,<	; take check sum
	pop	p,p3			; get back this reg.
> ; end of ifn FtChck
	POP	P,P4		;RESTORE AC'S
	JRST	FPOPJ##		;RESTORE F, TERMINATE THE OUTPUT LOOP

;HERE FROM CLOCK TICK LEVEL TO START PROCESSING QUEUED OUTPUT TO ITY'S

ITYSTO::
	SKIPE	(T1)		;ANYTHING TO SEND?
	PUSHJ	P,TOTAKE##
	  POPJ	P,		;NO, LINE IS NO IDLE
	SKIPGE	LDBDCH##(U)	;LINE IDLE?
	PUSHJ	P,XMTCHR##	;OR HAVE A CHARACTER TO SEND?
	  POPJ	P,		; EITHER IDLE OR NO CHAR TO SEND
	TRNN	T3,400		; IMAGE MODE SET?
	ANDI	T3,177		; NO, ONLY SEND 7 BITS
	PJRST	IMPTYP		;SEND THE CHAR ON ITS WAY

;HERE WHEN PROCESSING A TELNET CONTROL STREAM (2 OR 3 CHARS) ;⊗ IMPTY8 IMPTY9 XMTQIT TTYRn0 TTYRNM TTYRN1
IMPTY8:	MOVEI	T4,IMPTOI	;SAW AN IAC, ENTER TELNET CONTROL PROCESSOR
IMPTY9:	PUSHJ	P,(T4)		;DO THIS PART
	  SETZ	T1,		;FLAG THAT NOTHING TO PRINT
	HRRM	T4,TELOWD(F)	;SAVE WHERE TO GO NEXT
	JUMPE	T1,IMPTY7	;DON'T PRINT, JUST GET NEXT CHAR
	JRST	impty0		;OR OUTPUT, THEN GET NEXT


;ROUTINE CALLED BY SCNSER TRANSMIT INTERRUPT CODE FOR AN IMP LINE
;   WHEN THERE ARE NO MORE CHARACTERS TO TRANSMIT (I.E. THE LINE BECOMES
;   'IDLE').
;	MOVE	U,[LDB ADDRESS]
;	PUSHJ	P,XMTQIT
;	ALWAYS RETURN HERE -- F CLOBBERED

XMTQIT::
	LDB	T1,LDPLNO##	;GET LINE NUMBER
	SKIPN	F,ITYOFS##(T1)	;GET CONTROLLING IMP DDB
	POPJ	P,		;NO CONNECTION OPEN--DISCARD
	ScnOff		;INTERLOCK
	PJRST	TTYRN1		;START TRANSMISSION OF TTY STREAM


;HERE TO TRANSMIT ANY ASSEMBLED TTY STREAM.  CALLED
;  FROM ANY LEVEL WITH INTERRUPTS OFF.
TTYRn0:	ScnOff			; start here if don't have interrupts off yet.
TTYRNM:	HRRZM	P4,OBFPC(F)	;SAVE PC
ifn FtChck,<	; take check sum
	movem	p3,ObfCsm(f)		; save checksum
> ; end of ifn FtChck
TTYRN1:	SKIPG	T1,ObfByt(F)		;ANY DATA?
	  JRST	sonppj##		;NO DATA
	pushj	p,OutPre		; are there enough buffers available
					;  to send this message?
	  pjrst	sonppj##		; no.  don't send it.
					; note: user must type something for
					;  this to revive.  should be fixed.
ifn FtChck,<	; take check sum
	push	p,p3			; save P3
	move	p3,ObfCsm(f)		; get checksum into the
					;  register where it's expected.
> ; end of ifn FtChck
	PUSHJ	P,OUTBYT	;LINK UP and send
ifn FtChck,<	; take check sum
	pop	p,p3			; get back P3.
> ; end of ifn FtChck
	JRST	sonppj##		;RETURN
SUBTTL	SEE IF CROSSPATCHED LINE CAN TAKE MORE (MIC) ;(163) ;⊗ MICIMP MICIM0

;(163) MICIMP CALLED BY SCNSER GET MIC STATUS CODE.
;(163)  T1 CONTAINS THE JOB STATUS WORD AS SUPPLIED BY
;(163)  ROUTINE UJBSTX.
;(163)  U CONTAINS THE ADDR OF THE LINE'S LDB
;(163)  IF THE IMP CAN TAKE MORE INPUT, BIT 1B4 IS SET TO 1,
;(163)  0 OTHERWISE (THIS IS THE BIT SCNSER CHECKS TO SEE IF THE
;(163)  JOB IS IN TTY WAIT).

MICIMP::PUSHJ	P,SAVE1##		;(163) GET SOME AC'S
	MOVE	P1,LDBIMP(U)		;(163) GET DDB OF CONNECTED IMP
	TLO	T1,(1B4)		;(163) ASSUME ALL IS O.K.
	SKIPG	SndWnd(P1)		;(163) OR BIT SPACE
MICIM0:	TLZ	T1,(1B4)		;(163) NO TO EITHER
	POPJ	P,			;(163) YES TO BOTH (T1 SET ABOVE)
SUBTTL	TELNET CONTROL RECEIVER (FROM IMP) ;⊗ TTYIAC IACTAB IACFST

;THE FOLLOWING IS THE RECIEVED TELNET CONTROL PROCESSING COROUTINE.  PROCESSING
;  BEGINS WHEN AN 'IAC' IS RECEIVED AND TTYIAC IS CALLED WITH A PUSHJ.
;  WHEN THE MATCHING POPJ IS EXECUTED, T4 SHOULD CONTAIN THE PC OF
;  THE NEXT SEGMENT OF THE PROCESSING ROUTINE.  TO TERMINATE CONTROL PROCESSING,
;  THE IACFLG BIT IN TELWRD(F) MUST BE EXPLICITLY CLEARED.

TTYIAC:	MOVSI	T4,(IACFLG)	;SIGNAL TELNET COMMAND IN PROGRESS
	IORM	T4,TELWRD(F)
	JSP	T4,CPOPJ##	;SET CORETURN PC AND RETURN

;CALL HERE WHEN THE FIRST CHARACTER AFTER 'IAC' IS RECEIVED
	CAIGE	T1,IACFST	;A COMMAND WE KNOW ABOUT?
	JRST	IACNOP		;NO, IGNORE IT
	SKIPGE	TTYLIN(F)	;YES, HOW IS IMP CONNECTED?
	SKIPA	T2,IACTAB-IACFST(T1) ;SERVER TELNET
	MOVS	T2,IACTAB-IACFST(T1) ;USER TELNET
	PJRST	(T2)		;DISPATCH ON TELNET COMMAND

;TELNET COMMAND PROCESSING TABLE.  COMMANDS RECEIVED BY USER TELNET IN LH,
;  BY SERVER TELNET IN RH.

IACTAB:	IACDM	,, IACDM	; 242 DATA MARK
	IACNOP	,, IACNOP	; 243 BREAK
	IACNOP	,, IACIP	; 244 INTERRUPT PROCESS
	IACNOP	,, IACAO	; 245 ABORT OUTPUT
	IACNOP	,, IACAYT	; (162) 246 ARE YOU THERE
	IACNOP	,, IACEC	; 247 ERASE CHARACTER
	IACNOP	,, IACEL	; 248 ERASE LINE
	IACNOP	,, IACNOP	; 249 GO AHEAD
	IACNOP	,, IACNOP	; 250 SUB-NEGOTIATE
	IAWWDD	,, IAWWDD	; 251 WILL
	IAWWDD	,, IAWWDD	; 252 WON'T
	IAWWDD	,, IAWWDD	; 253 DO
	IAWWDD	,, IAWWDD	; 254 DON'T
	IACNOP	,, IACNOP	; 255 IAC (TRANSPARENT)

	IACFST==↑D256-<.-IACTAB>	;FIRST COMMAND IN TABLE
;SOME TELNET COMMAND PROCESSING ROUTINES ;⊗ IACIP IACAO IACAYT IACEC IACEL IACDM

;HERE ON 'INTERRUPT PROCESS' COMMAND.  TURN IT INTO A CONTROL-C.
IACIP:	MOVEI	T1,"C"-100	;SETUP CONTROL-C
	JRST	IACAOS		;SEND IT AND END COMMAND PROCESSING

;HERE ON 'ABORT OUTPUT'.  SEND ANSWERING SYNCH AND FORCE A CONTROL-O.
IACAO:	PUSHJ	P,TSETBO##	;FOREIGN RITE WILL IGNORE ALL THAT ANYWAY
	MOVEI	T3,.TNIAC	;SEND AN IAC TO TTY BUFFERS
	PUSHJ	P,TLNOCH
	MOVEI	T3,.TNDM	;FOLLOWED BY A DATA MARK
	PUSHJ	P,TLNOCH	;OUTPUT CODE WILL SEND OUT INS FOR US AND DO ↑O
	JRST	IACNOP

;(162) HERE ON 'ARE YOU THERE'.  SUBSTITUTE CONTROL-T IF NOT IN
;(162) RT COMPATIBILITY MODE

IACAYT:	LDB	T1,LDPRTC##	;(162) GET RTCOMP BIT
	JUMPN	T1,IACNOP	;(162) IF ON, DON'T SEND CONTROL-T
	MOVEI	T1,"T"-100	;(162) NOT ON, SEND CONTROL-T THROUGH
	JRST	IACAOS		;(162)

;HERE ON 'ERASE CHARACTER'.  SUBSTITUTE RUBOUT.
IACEC:	MOVEI	T1,177		;SETUP RUBOUT
	JRST	IACAOS		;GIVE IT TO SCNSER AND END THE COMMAND

;HERE ON 'ERASE LINE'.  SUBSTITUTE CONTROL-U
IACEL:	MOVEI	T1,"U"-100	;SETUP CONTROL-U
	JRST	IACAOS		;GIVE IT TO SCNSER AND END THE COMMAND

;HERE ON 'DATA MARK'.  if we're past current urgent pointer, we can get
;  out of urgent mode.
IACDM:	move	t1,RcvRed(f)		; get sequence number of
					;  characters read last window update.
	add	t1,IbfByt(f)		; add characters read since then.
	caml	t1,RcvUrg(f)		; past the urgent pointer?
	  setzm	RcvUrg(f)		; yes.  no longer urgent.
	PJRST	IACNOP
PTLNop:	pointr	TelWrd(f),OptChr	; pointer for telnet option character ;⊗ PTLNop IAWWDD IACAOS IACNOP IACNIT

;ROUTINE TO HANDLE 'WILL', 'WONT', 'DO', 'DONT' COMMANDS.
IAWWDD:	DPB	T1,PTLNop	;REMEMBER WHICH COMMAND IT WAS
	JSP	T4,CPOPJ##	;CORETURN

;HERE WHEN NEXT CHARACTER ARRIVES
	PUSHJ	P,IACNOP	;END COMMAND INTERPRETATION
	LDB	T2,PTLNop	;GET BACK COMMAND
	DPB	T1,PTLNop	;REMEMBER WHICH OPTION
	MOVSI	T3,-NTNOPS	;SEARCH TELNET OPTION TABLE
	CAIE	T1,@OPTNTB(T3)
	AOBJN	T3,.-1
	JUMPGE	T3,IACNIT-.TNWIL(T2) ;REFUSE IF NOT IN TABLE
	SKIPGE	TTYLIN(F)	;HOW IS TTY CONNECTED?
	SKIPA	T3,@WWDDTB-.TNWIL(T2) ;SERVER TELNET
	MOVS	T3,@WWDDTB-.TNWIL(T2) ;USER TELNET
	PJRST	(T3)		;PERFORM THE OPTION NEGOTIATION

;HERE TO LEAVE 'IAC' MODE.
IACAOS:	AOS	(P)		;HERE TO SKIP RETURN TOO
IACNOP:	MOVSI	T4,(IACFLG)	;CLEAR TELNET COMMAND FLAG
	ANDCAM	T4,TELWRD(F)
	POPJ	P,		;RETURN FROM COROUTINE

;TABLE OF WHAT TO DO WHEN AN UNKNOWN OPTION ARRIVES.
IACNIT:	PJRST	DONT		;'WILL BLAH' -- REPLY 'DONT BLAH'
	POPJ	P,		;'WONT BLAH' -- IGNORE ('WONT' IS DEFAULT)
	PJRST	WONT		;'DO BLAH' -- REPLY 'WONT BLAH'
	POPJ	P,		;'DONT BLAH' -- IGNORE ('DONT' IS DEFAULT)
;THE FOLLOWING CODE HANDLES THE TELNET ECHO OPTION. ;⊗ TUNECH TUYECH TSYECH TSNECH

;HERE ON 'WILL ECHO' FROM SERVER TO USER
TUNECH:	MOVE	T1,LDBDCH##(U)	;GET TTY CHARACTERISTICS
	TLNE	T1,LDLLCP##	;IS IT A LOCAL COPY LINE?
	TRNE	T1,LDRIMP##	;  AND NOT AN IMP LINE?
	CAIA			;NO
	PJRST	DONT		;YES, CAN'T STOP ECHOING ON LOCAL-COPY
	MOVE	T1,TELOWD(F)	;SEE IF WE CAN LET HIM ECHO
	TLNE	T1,(IECHO)	;AS DETERMINED BY IECHO (SET AT XPATCH TIME)
	PJRST	DONT		;WE REALLY WANT TO ECHO, REFUSE
	PUSH	P,F		;SAVE DDB POINTER
	HRRZ	F,LDBDDB##(U)	;GET TTY DDB POINTER
	JUMPE	F,IGNXPF	;IGNORE IF DETACHED
	MOVEI	T1,IOSNEC##	;ECHOING ALREADY TURNED OFF?
	TDNE	T1,DEVIOS(F)
	PJRST	IGNXPF		;YES, IGNORE
	PUSHJ	P,SETNEC##	;NO, DISABLE USER TELNET ECHOING
	POP	P,F		;RESTORE IMP DDB
	PJRST	DO		;RESPOND 'DO ECHO'

;HERE ON 'WONT ECHO' FROM SERVER TO USER
TUYECH:	PUSH	P,F		;SAVE DDB POINTER
	HRRZ	F,LDBDDB##(U)	;GET TTY DDB POINTER
	JUMPE	F,IGNXPF	;IGNORE IF DETACHED
	MOVEI	T1,IOSNEC##	;ALREADY ECHOING LOCALLY?
	TDNN	T1,DEVIOS(F)
	PJRST	IGNXPF		;YES, IGNORE
	PUSHJ	P,SETECH##	;NO, ENABLE USER TELNET ECHOING
	POP	P,F		;RESTORE IMP DDB POINTER
	PJRST	DONT		;REPLY 'DONT ECHO'.

;HERE ON 'DO ECHO' FROM USER TO SERVER
TSYECH:	MOVSI	T1,LDLLCP##	;CLEAR LOCAL COPY BIT
	ANDCAM	T1,LDBDCH##(U)	;SO SERVER WILL ECHO
	SOSL	ECPEND(F)	;WAS THIS A REPLY TO SERVER'S REQUEST?
	PJRST	IGNXPT		;YES, NEVER REPLY TO A REPLY
	SETZM	ECPEND(F)	;NO, RESET SEMAPHORE AND
	PJRST	WILL		;REPLY 'WILL ECHO'

;HERE ON 'DONT ECHO' FROM USER TO SERVER
TSNECH:	MOVSI	T1,LDLLCP##	;SET LOCAL COPY BIT
	IORM	T1,LDBDCH##(U)	;SO SERVER WON'T ECHO
	SOSL	ECPEND(F)	;WAS THIS A REPLY TO SERVER'S REQUEST?
	PJRST	IGNXPT		;YES, NEVER REPLY TO A REPLY
	SETZM	ECPEND(F)	;NO, RESET SEMAPHORE AND
	PJRST	WONT		;REPLY 'WONT ECHO'
;THE FOLLOWING CODE HANDLES THE TELNET SUPRESS GO-AHEAD OPTION. ;⊗ WILSGA WNTSGA DOSGA DNTSGA
;  NOTE THAT WE ARE MERELY PROVIDING CORRECT RESPONSES.  'GO-AHEAD'
;  ITSELF IS NOT ACTUALLY IMPLEMENTED!

;HERE ON 'WILL SUPPRESS GA'
WILSGA:	PUSHJ	P,SETFSG	;IS HE ALREADY SUPPRESSING GA?
	  PJRST	DO		;REPLY 'DO SUPPRESS GA'
	PJRST	IGNXPT		;YES, IGNORE

;HERE ON 'WONT SUPPRESS GA'
WNTSGA:	PUSHJ	P,CLRFSG	;IS HE SUPPRESSING GA NOW?
	  PJRST	DONT		;REPLY 'DONT SUPPRESS GA'
	PJRST	IGNXPT		;NO, IGNORE

;HERE ON 'DO SUPPRESS GA'
DOSGA:	PUSHJ	P,SETLSG	;ARE WE ALREADY SUPPRESSING GA?
	  PJRST	WILL		;REPLY 'WILL SUPPRESS GA'
	PJRST	IGNXPT		;YES, IGNORE

;HERE ON 'DONT SUPPRESS GA'
DNTSGA:	PUSHJ	P,CLRLSG	;ARE WE SUPPRESSING GA NOW?
	  PJRST	WONT		;REPLY 'WONT SUPPRESS GA'
	PJRST	IGNXPT		;NO, IGNORE
;ROUTINES TO PROVIDE THE PROPER 'WILL', 'WONT', 'DO', 'DONT' RESPONSES. ;⊗ WILL WONT DO DONT TLNRSP TLNOCH IGNXPF IGNXPT CLRXPT
WILL:	JSP	T1,TLNRSP	;'WILL'
WONT:	JSP	T1,TLNRSP	;'WONT'
DO:	JSP	T1,TLNRSP	;'DO'
DONT:	JSP	T1,TLNRSP	;'DONT'
TLNRSP:	SUBI	T1,WILL+1-.TNWIL ;BUILD THE RESPONSE COMMAND CHARACTER
	HRLM	T1,(P)		;SAVE IT
	PUSHJ	P,CLRXPT	;CLEAR OUT THE RIGHT REPLY EXPECTED BIT
	  POPJ	P,		;WAS ON...IGNORE
	MOVEI	T3,.TNIAC	;SEND 'IAC'
	PUSHJ	P,TLNOCH
	SETZ	T3,		;SEND A NULL (IAC NULL TERMINATES PROCESSING, SO NEG MAKE IT OUT)
	PUSHJ	P,TLNOCH
	HLRZ	T3,(P)		;SEND COMMAND
	PUSHJ	P,TLNOCH
	LDB	T3,PTLNop	;SEND OPTION NAME

TLNOCH:	IORI	T3,400		;SET IMAGE BIT TO DISABLE FURTHER MANGLING
	SKIPGE	TTYLIN(F)	;HOW IS TTY CONNECTED?
	PJRST	CCTYO9##	;SERVER TELNET, SEND IT
	PUSH	P,F		;USER TELNET, FAKE USER TTY INPUT
	PUSHJ	P,RECIMP##
;(271)	  JFCL			;INPUT BUFFER FULL (SHOULDN'T HAPPEN)
	JRST	FPOPJ##		;RESTORE IMP DDB POINTER

;HERE WHEN RECIEVE A NEGOTIATION TO A MODE WE ARE IN ALREADY. SHOULD
;BE A REPLY (BUT NEVER CAN TELL)
IGNXPF:	POP	P,F		;FOR ECHO HACKERS
IGNXPT:	PUSHJ	P,CLRXPT	;TURN OFF THE RIGHT XPT BIT
	  POPJ	P,		;AND IGNORE PREVIOUS SETTING
	POPJ	P,

;HERE TO RECORD REPLY IN, AND GIVE A SKIP RETURN TO
;SAY WE WEREN'T EXPECTING THAT, BETTER REPLY OURSELVES.
CLRXPT:	MOVSI	T3,-NTNOPS	;SEARCH FOR COMMAND
	LDB	T2,PTLNop	;GET NAME
	CAIE	T2,@OPTNTB(T3)	;SEE IF WE HAVE IT
	AOBJN	T3,.-1
	JUMPGE	T3,CPOPJ1##	;SEND IF NOT FOUND
	HLLZ	T1,OPTNTB(T3)	;GET RIGHT REPLY EXPECTED BIT
	AND	T1,TELOWD(F)	;NEED TO KNOW IF OFF ALREADY
	JUMPE	T1,CPOPJ1##	;IN WHICH CASE, WE SEND IT
	ANDCAM	T1,TELOWD(F)	;OTHERWISE WE FORGET IT
	POPJ	P,		;LEAVE NEGOTIATION
SUBTTL	TELNET CONTROL TRANSMITTER (FOR IMP) ;⊗ IMPTOI ITOTAB ITORSP PTOFnc ITOTEL ITOQT1 ITOQIT ITOIAC

;THE FOLLOWING IS THE MIRROR IMAGE OF TTYIAC, AND PROCESSES TELNET CONTROL
;  DESTINED FOR THE NET. THERE ARE MANY SIMILARITES WITH TTYIAC, BUT THERE ARE
;  AS MANY DIFFERENCES IN DIRECTION AND MEANING. HERE WE USE TELOWD INSTEAD OF
;  TELWRD, BUT BIT DEFINITIONS ARE THE SAME.
IMPTOI:	MOVSI	T4,(IACFLG)	;MARK US IN TELNET ACTIVE
	IORM	T4,TELOWD(F)	;REMEMBER
	JSP	T4,CPOPJ##	;CAN'T OUTPUT TILL WE AT LEAST SEE WHAT NEXT CHAR IS
	TRNN	T1,377		;THIS STREAM IAC-NULL? (I.E. FROM TLNRSP)
	JRST	ITORSP		;YES, PREFACE WITH IAC AND IGNORE REST
	CAIGE	T1,IACFST	;ONE WE KNOW ABOUT?
	JRST	ITOTEL		;NOPE, PASS IT ON. WONDER WHAT IT DOES?
	SKIPGE	TTYLIN(F)	;WHICH DIRECTION?
	SKIPA	T2,ITOTAB-IACFST(T1);FROM SERVER
	HLRZ	T2,ITOTAB-IACFST(T1);FROM USER
	JRST	(T2)		;BRANCH TO NEVER-NEVER LAND

;	USER	,,  SERVER
ITOTAB:	ITOUDM	,,  ITOSDM	; 242 DATA MARK
	ITOTEL	,,  ITOTEL	; 243 BREAK
	ITOTEL	,,  ITOTEL	; 244 INTERRUPT PROCESS
	ITOAO	,,  ITOTEL	; 245 ABORT OUTPUT
	ITOTEL	,,  ITOTEL	; 246 ARE YOU THERE?
	ITOTEL	,,  ITOTEL	; 247 ERASE CHARACTER
	ITOQIT  ,,  ITOTEL	; 248 ERASE LINE
	ITOTEL	,,  ITOTEL	; 249 GO AHEAD  [!! DATELS !!]
	ITOTEL	,,  ITOTEL	; 250 SUB-NEGOTIATE
	ITONEG	,,  ITONEG	; 251 WILL
	ITONEG	,,  ITONEG	; 252 WON'T
	ITONEG	,,  ITONEG	; 253 DO
	ITONEG	,,  ITONEG	; 254 DON'T
	ITOTEL	,,  ITOTEL	; 255 IAC

ITORSP:	MOVEI	T1,.TNIAC	;SEND AN IAC, LET REST THRU AS IS
	JRST	ITOQT1


PTOFnc:	pointr	TelOWd(f),TelFnc	; pointer to place to save function
					;  while outputing leading IAC.

;HERE TO OUTPUT TLENET CONTROL IN T1. WE HAVE TO PREFACE WITH
;AN IAC, SO CONTROL WILL BE SAVED IN PTOFnc WHILE OUTPUTTING
ITOTEL:	DPB	T1,PTOFnc	;SAVE CONTROL PART
	JSP	T4,ITOIAC	;SEND IAC
	LDB	T1,PTOFnc	;GET CONTROL BACK
ITOQT1:	AOS	(P)		;RETURN WITH OUTPUTTING
ITOQIT:	MOVSI	T4,(IACFLG!TELOMR);PREPARE TO LEAVE IAC PROCESSING
	ANDCAM	T4,TELOWD(F)	;CLEAR FLAG
	POPJ	P,		;AND EXIT

;HERE TO SEND IAC, ENTER TELOMR MODE. T4 HAS ADDRESS TO OUTPUT REST
;(NORMALLY JSP T4,ITOIAC, BUT SEE ITOWIL)
ITOIAC:	MOVSI	T1,(TELOMR)	;WE'LL BE BACK TO SEND SOME MORE
	IORM	T1,TELOWD(F)	;SO TELL CALLER
	MOVEI	T1,.TNIAC	;AND SEND IAC FIRST
	JRST	CPOPJ1##	;SEND THAT, RETURN TO CALLER FOR MORE
ITOSDM:	HRLM	F,(P)		;SAVE DDB ADDRESS OVER SCNSER CALL ;⊗ ITOSDM ITOUDM ITOAO ITONEG
	MOVEI	T3,"O"-100	;ONLY WAY SERVER SHOULD BE SENDING A DATA MARK
	PUSHJ	P,RECIMP##	;IS FROM RECIEVING AN AO.  CAN'T DO ↑O
;(271)	  JFCL			;AT AO TIME, CAUSE WE'LL LOSE DM. SIGH.
	HLRZ	F,(P)		;GET DDB BACK

ITOUDM:	PUSHJ	P,SetUrg##		;DATA MARK.  set the urgent field
					; for the next outgoing message.
	HRRZ	U,TTYLIN(F)	;  FOREIGN SERVER TO MATCH WITH THE
	MOVEI	T1,.TNDM	;GET OUR DM CODE BACK
	JRST	ITOTEL		;  DATA MARK

ITOAO:	MOVEI	T1,LDROSU##	;FAKE A ↑O OPERATION FROM HERE,
	IORM	T1,LDBDCH##(U)	;TO FLUSH ANY DATA HERE OR IN THE NET
	PUSHJ	P,TSETBO##	;PUNT CURRENT BUFFER
	MOVEI	T1,.TNAO	;NEED TO SAY WHAT WE'RE  DOING
	JRST	ITOTEL		;FINISH UP

ITONEG:	DPB	T1,PTOFnc	;SAVE WILL, WON'T, ETC.
	JSP	T4,CPOPJ	;RETURN FOR OPTION
	LDB	T2,PTOFnc	;GET FUNCTION
	DPB	T1,PTOFnc	;SAVE OPTION
	MOVSI	T3,-NTNOPS	;PREPARE TO SEARCH FOR OPTION
	CAIE	T1,@OPTNTB(T3)	;THIS IT?
	AOBJN	T3,.-1		;NOPE, TRY NEXT
	JUMPGE	T3,ITOQIT	;NOT IN TABLE, IGNORE USER'S ATTEMPT
	SKIPGE	TTYLIN(F)	;DIRECTION?
	SKIPA	T3,@ITOWDB-.TNWIL(T2);FROM SERVER
	HLRZ	T3,@ITOWDB-.TNWIL(T2);FROM USER
	CAIE	T1,.TOECH	;IF WE AREN'T NEGGING ECHO,
	JRST	(T3)		;  WE WON'T NEED FOLLOWING
	HRRZ	T2,LDBDDB##(U)	;GET TTY ADDRESS
	JUMPE	T2,ITOQIT	;  CAN'T DO SINCE WE NEED DDB
	PUSH	P,T3		;SAVE, WE NEED ALL THE TEMPS WE HAVE
	MOVSI	T1,LDLLCP##	;FOR USE BY ECHO OPTION
	MOVEI	T3,IOSNEC##	;BIT TO CHECK IN DDB
	MOVSI	T4,(LLCPWN)	;AND LEAVE LCP ALONE ON WONT
	POPJ	P,		;GO TO ROUTINE (NO, NOT! RETURN!)
;THESE ARE THE ECHO NEGOTIATION ROUTINES: ;⊗ OUDECH OUEECH

;NEGOTIATION FROM USER END. THE IOSNEC BIT IS USED TO INDICATE WHAT
;WE ARE DOING.
;UNDER USER ECHO CONTROL:
;NOECHO OFF MEANS SERVER WON'T ECHO,
;NOECHO ON  MEANS SERVER WILL  ECHO.

;HERE WHEN USER TELNET SENDS DO ECHO
OUDECH:	TDNE	T3,DEVIOS(T2)	;ARE WE ALREADY NOT ECHOING?
	JRST	ITOQIT		;YES, DON'T SAY SO AGAIN
	HRLM	F,(P)		;SAVE IMP DDB ADDR
	PUSHJ	P,SETNEC##	;OKAY, STOP ECHO (AND MAYBE PASS DOWN)
	HLRZ	F,(P)		;GET IMPDDB BACK
	JRST	ITODO		;SEND THE DO

;HERE WHEN TRYING A DON'T ECHO
OUEECH:	TDNN	T3,DEVIOS(T2)	;ARE WE ALREADY ECHOING?
	JRST	ITOQIT		;YES, DON'T SAY SO AGAIN
	HRLM	F,(P)		;SAVE IMPDDB
	PUSHJ	P,SETECH##	;BACK TO ECHO MODE
	HLRZ	F,(P)		;BACK TO IMP
	JRST	ITODNT		;DEMAND HE STOP
;THE STATE OF SERVER ECHO IS DETERMINED PRIMARILY BY THE STATE OF THE ;⊗ OSWECH OSWEC1 OSXECH
;LCP BIT, WITH A LITTLE HELP FROM NOECHO.
;CURRENTLY, THE ONLY WAY TO SEND ECHO NEGOTIATIONS IS THROUGH THE
;SETECH/SETNEC ROUTINES IN SCNSER. NOTE THAT THE NORMAL, DESIRED
;STATE FOR AN ITY LINE IS NOECHO OFF, LCP ON.
;BY THE WAY, ITY PROCESSES CAN CONFUSE THE STATE OF ECHOING BY DIDDLING
;LCP VIA THE SETLCH UUO. THIS IS AN UNFRIENDLY ACT, AND IS FROWNED UPON.
;THE FOLLOWING TABLE IS WHAT WE DO IN ANY POSSIBLE
;CIRCUMSTANCE OF SERVER ECHO CONTROL:
;		SERVER SENDS WILL ECHO	    !	SERVER SENDS WON'T ECHO
;LCP:		OFF		ON	    !	OFF		ON
;NOECHO	OFF:	IGNORE		OFF,SEND    !	(2)		IGNORE
;	ON:	IGNORE(1)	OFF,SEND    !	SEND,ON		IGNORE
;FOOTNOTES:
;(1)	THIS REPRESENTS THE SERVER PROCESS SETTING NOECHO. WE SET LLCPWN
;	(LEAVE LCP ALONE ON WON'T) SO THAT WE'LL STAY IN SERVER-ECHO (LCP OFF)
;	MODE AFTER NOECHO IS CLEARED
;(2)	IF LLCPWN IS ON (THANKS TO (1)), THEN CLEAR, BUT DON'T SEND ANYTHING.
;	OTHERWISE, SENDS THE CONTROL AND CLEAR LLCPWN

;HERE WHEN SERVER SENDS A WILL ECHO
OSWECH:	TDNN	T1,LDBDCH(U)	;ARE WE ECHOING?
	JRST	OSWEC1		;YES, SEE IF NOECHO GOT SET AND REMEMBER THAT
	ANDCAM	T1,LDBDCH(U)	;LEAVE LCP MODE TO START ECHO
	AOS	ECPEND(F)	;INCREMENT COUNT OF PENDING REPLIES
	JRST	ITOWIL		;STORE IT AND DO IT

OSWEC1:	TDNE	T3,DEVIOS(T2)	;IS NOECHO ON?
	IORM	T4,TELOWD(F)	;YES, LET'S SAY WILL ECHO CAUSED BY IT
	JRST	ITOQIT		;  SO WE'LL STAY ECHOING WHEN IT'S CLEARED

;HERE WHEN SERVER SENDS A WON'T ECHO
OSXECH:	TDNE	T1,LDBDCH(U)	;ARE WE ECHOING?
	JRST	ITOQIT		;NOPE, NOTHING TO DO
	AND	T4,TELOWD(F)	;GET STATE OF LLCPWN
	ANDCAM	T4,TELOWD(F)	;AND ENSURE IT'S OFF
	TDNN	T3,DEVIOS(T2)	;IS NOECHO OFF TOO?
	JUMPN	T4,ITOQIT	;YES, SO IF THIS WAS ON, LEAVE LCP ALONE
	IORM	T1,LDBDCH(U)	;NOPE, STOP ECHOING
	AOS	ECPEND(F)	;INCREMENT COUNT OF PENDING REPLIES
	JRST	ITOWNT		;AND LET THEM KNOW
;HERE ARE THE SGA NEGOTIATION ROUTINES. WE LOOK AT EACH ATTEMPT AT ;⊗ IODSGA IOESGA IOWSGA IOXSGA ITOWIL ITOWNT ITODO ITODNT ITOXMS ITOWL1 ITOXM1
;  NEGOTIATION AND CHECK TO SEE WHETHER OR NOT WE ARE IN THAT MODE ALREADY
;  IF SO, WE IGNORE THE ATTEMPT, OTHERWISE WE PASS IT THROUGH

IODSGA:	PUSHJ	P,SETFSG	;"DO SGA" ... TRY TO SET FOREIGN SGA
	  PJRST	ITODO		;WASN'T, DO IT NOW
	PJRST	ITOQIT		;WAS, ABORT NEGOTIATION

IOESGA:	PUSHJ	P,CLRFSG	;"DON'T SGA" ... CLEAR FOREIGN BIT
	  PJRST	ITODNT
	PJRST	ITOQIT

IOWSGA:	PUSHJ	P,SETLSG	;"WILL SGA" ... SET LOCAL BIT
	  PJRST	ITOWIL
	PJRST	ITOQIT

IOXSGA:	PUSHJ	P,CLRLSG	;"WON'T SGA' ... CLEAR LOCAL BIT
	  PJRST	ITOWNT
	PJRST	ITOQIT

;HERE TO TRANSMIT TELNET CONTROLS ONE WAY OR ANOTHER
ITOWIL:	JSP	T4,ITOXMS	;SEND, SET REPLY-EXPECTED
ITOWNT:	JSP	T4,ITOXMS
ITODO:	JSP	T4,ITOXMS
ITODNT:	JSP	T4,ITOXMS

ITOXMS:	MOVEI	T4,ITOWL1-ITOWIL-1(T4);MAKE T4 BE ADDR TO REENTER AT
	JRST	ITOIAC		;SEND IAC, RETURN FOR NEGOTIATION

ITOWL1:	JSP	T1,ITOXM1
	JSP	T1,ITOXM1
	JSP	T1,ITOXM1
	JSP	T1,ITOXM1

ITOXM1:	SUBI	T1,ITOWL1+1-.TNWIL;GET NEGOTIATION TYPE
	JSP	T4,CPOPJ1##	;AND SEND THAT MUCH
	LDB	T1,PTOFnc	;GET OPTION
	MOVSI	T2,-NTNOPS	;LIGHT APPROPRIATE EXPECT BIT
	CAIE	T1,@OPTNTB(T2)	;THIS ONE?
	AOBJN	T2,.-1		;NOPE, GUESS AGAIN
	HLLZ	T2,OPTNTB(T2)	;FOUND (CAN'T LOSE!), GET BIT
	IORM	T2,TELOWD(F)	;SAY WE'RE EXPECTING A REPLY
	JRST	ITOQT1		;AND SEND!
;ROUTINES TO BE CALLED TO CHECK WHETHER OR NOT A SGA NEGOTIATION ;⊗ SETFSG CLRFSG SETLSG CLRLSG
;  MAY PROCEED. NORMAL RETURN IF YES, SKIP RETURN IF NO.

SETFSG:	MOVSI	T1,(FSGAFG)	;IS HE ALREADY SUPRESSING GA?
	TDNE	T1,TELWRD(F)
	AOSA	(P)		;YES, IGNORE
	IORM	T1,TELWRD(F)	;NO, NOW HE IS
	POPJ	P,		;AND SEND RIGHT CONTROL

CLRFSG:	MOVSI	T1,(FSGAFG)	;IS HE SUPPRESSING GA NOW?
	TDNN	T1,TELWRD(F)
	AOSA	(P)		;NO, IGNORE
	ANDCAM	T1,TELWRD(F)	;YES, NOW HE ISN'T
	POPJ	P,		;AND SEND RIGHT CONTROL

SETLSG:	MOVSI	T1,(LSGAFG)	;ARE WE ALREADY SUPPRESSING GA?
	TDNE	T1,TELWRD(F)
	AOSA	(P)		;YES, IGNORE
	IORM	T1,TELWRD(F)	;NO, NOW WE ARE
	POPJ	P,		;AND SEND RIGHT CONTROL

CLRLSG:	MOVSI	T1,(LSGAFG)	;ARE WE SUPPRESSING GA NOW?
	TDNN	T1,TELWRD(F)
	AOSA	(P)		;NO, IGNORE
	ANDCAM	T1,TELWRD(F)	;YES, NOW WE AREN'T
	POPJ	P,		;AND SEND RIGHT CONTROL
SUBTTL	DATA FOR TELNET CONTROL AND SUBROUTINES ;⊗ WWDDTB ITOWDB


;TABLE FOR SWITCHING ON 'WILL', 'WONT', 'DO', 'DONT' AND USER/SERVER TELNET
WWDDTB:	WILTBL(T3)		;'WILL'
	WNTTBL(T3)		;'WONT'
	DOTBL (T3)		;'DO'
	DNTTBL(T3)		;'DONT'

;SAME SORT OF TABLE, BUT FOR NEGOTIATION INITTED BY US
ITOWDB:	WILTBO(T3)		;'WILL'
	WNTTBO(T3)		;'WONT'
	DOTBO (T3)		;'DO'
	DNTTBO(T3)		;'DONT'


;THE FOLLOWING MACRO LISTS ALL THE IMPLEMENTED OPTIONS AND THEIR INTERPRETATION
;  CALL IS:
;	CD  OPTION NAME, USER-TELNET HANDLING, SERVER-TELNET HANDLING
;  WHERE 'HANDLING' CONSISTS OF 4 DISPATCH ADDRESSES IN THE ORDER
;  'WILL', 'WONT', 'DO', 'DONT'.

DEFINE TELCDS <
	CD ECH,TUNECH,TUYECH,WONT,CPOPJ##,DONT,CPOPJ##,TSYECH,TSNECH
	CD SGA,WILSGA,WNTSGA,DOSGA,DNTSGA,WILSGA,WNTSGA,DOSGA,DNTSGA
>

;AND ANOTHER ONE FOR NEGOTIATIONS FROM US
DEFINE TELCDO <
	CD	ECH,ITOQIT,ITOQIT,OUDECH,OUEECH,OSWECH,OSXECH,ITOQIT,ITOQIT
	CD	SGA,IOWSGA,IOXSGA,IODSGA,IOESGA,IOWSGA,IOXSGA,IODSGA,IOESGA
>
;ASSEMBLE ALL THE DISPATCH TABLES FOR THE VARIOUS TELNET OPTIONS. ;⊗ OPTNTB NTNOPS WILTBL WILTBO WNTTBL WNTTBO DOTBL DOTBO DNTTBL DNTTBO

DEFINE CD(COD,A,B,C,D,E,F,G,H) <	;;OPTION CODE TABLE
	XPT'COD	!  .TO'COD	;;REPLY EXPECTED BIT,,NEGOTIATION CHARACTER
>
OPTNTB:	PHASE	0		;TO DEFINE INDEXES RIGHT
	TELCDS
	DEPHASE

	NTNOPS==.-OPTNTB	;NUMBER OF IMPLEMENTED TELNET OPTIONS

DEFINE CD(COD,A,B,C,D,E,F,G,H) <	;;'WILL' DISPATCH TABLE
	A	,, E
>
WILTBL:	TELCDS
WILTBO:	TELCDO

DEFINE CD(COD,A,B,C,D,E,F,G,H) <	;;'WONT' DISPATCH TABLE
	B	,, F
>
WNTTBL:	TELCDS
WNTTBO:	TELCDO

DEFINE CD(COD,A,B,C,D,E,F,G,H) <	;;'DO' DISPATCH TABLE
	C	,, G
>
DOTBL:	TELCDS
DOTBO:	TELCDO

DEFINE CD(COD,A,B,C,D,E,F,G,H) <	;;'DONT' DISPATCH TABLE
	D	,, H
>
DNTTBL:	TELCDS
DNTTBO:	TELCDO
;THIS IS THE MAPPING TABLE FROM TTY CHARS TO TELNET COMMANDS, USED BY ;⊗ TELTAB TELLEN
;NETQUO IN SCNSER.

TELTAB::-TELLEN,,.+1
;	TELNET	,,  ASCII
	.TNNOP	,,  0		;NULL SENDS NOP
	.TNDM	,,  "S"		;S FOR SYNC
	.TNBRK	,,  "B"		;B FOR BREAK
	.TNIP	,,  "C"-100	;↑C TO INTERRUPT PROCESS
	.TNAO	,,  "O"-100	;↑O TO STOP OUTPUT
	.TNAYT	,,  "E"-100	;↑E IS WRU ON TTY, THIS IS CLOSE ENOUGH
	.TNEC	,,  177		;RUBOUT TO ERASE CHARACTER
	.TNEL	,,  "U"-100	;↑U TO ERASE LINE
	.TNGA	,,  "G"		;G TO TELL THE OTHER GUY TO GA
	.TNSB	,,  -1		;DISALLOW SUBNEGOTIATIONS FOR NOW
	.TNWIL	,,  "W"		;W FOR A WILL (FOLLOW BY QUOTED CHAR)
	.TNWNT	,,  "W"-100	;C-ONT-ROL ADDS THE ON'T PART
	.TNDO	,,  "D"		;DO FOR DO
	.TNDNT	,,  "D"-100	;AND ↑D FOR DON'T

TELLEN==.-TELTAB-1

IFN FTPATT,<BLOCK 2>		;FOR NEW CMDS THAT CAN'T WAIT
; subroutine to appropriately tweak everybody when an URG comes in ;⊗ TTyUrg TTUUrg TTSUrg
;  through TCP.  mainly, it clears out data which is already (or still)
;  in the monitors terminal buffers (all of which should be flushed),
;  and then makes sure everyone is aware that there is SOMETHING to be
;  looked at.
TTyUrg::
	pushj	p,TTyTst		; what kind of terminal?
	  jrst	TTUUrg			; user (TTy controled)
	  jrst	TTSUrg			; server (job controlled)
	popj	p,			; not a terminal at all

TTUUrg:	MOVSI	T1,(SYNCLR)	;HAS ANY DATA BEEN RECEIVED OVER THIS CONNECTION?
	TDNE	T1,TELWRD(F)	;  (IF NOT, DON'T CLEAR OUTPUT BUFFER)
	  PUSHJ	P,TSETBO##	;CLEAR USER OUTPUT BUFFER
	MOVEI	T1,LDROSU##	;ASSUME THIS IS FINISH OF AO,
	ANDCAM	T1,LDBDCH##(U)	;  SO WE MAY TURN OUTPUT ON AGAIN
	PJRST	RQIITO		;USER TELNET, WAKE IT UP

TTSUrg:	PUSHJ	P,TSETBI##	;CLEAR SERVER INPUT BUFFER
	PJRST	RQIITI		;SERVER TELNET, WAKE IT UP
;SUBROUTINE TO ALLOCATE A LINE NUMBER FOR IMPS CONNECTING ;⊗ ITYGET ITYGE1 ITYGE2
; TO A LOCAL PROCESS.  NORMALLY CALLED BY NCP IN RESPONSE TO
; REQUESTS TO A LOW SOCKET NUMBER.
;CALL:
;	MOVE	F,[ ADDRESS OF REQUESTING IMP ]
;	PUSHJ	P,ITYGET
;	  ERROR RETURN	..  .NONE LEFT
;	OK RETURN  ...	LDB ADDRESS IN DDB
ITYGET::
	PUSH	P,U		;SAVE AC
	MOVSI	U,-ITYN##	;FIND UNUSED ITY
ITYGE1:	SKIPE	ITYTAB(U)	;THIS ONE?
	AOBJN	U,.-1		;NO.   LOOP
	JUMPGE	U,upopj##	;ERROR RETURN IF NONE FREE
	PUSH	P,U		;SAVE IN CASE THIS IN USE
	HRRZM	F,ITYTAB##(U)	;FLAG IN USE WITH DDB ADDRESS
	ADDI	U,ITYFST##	;OFFSET TO A LINE NUMBER
	HRR	U,LINTAB##(U)	;GET LDB ADDRESS
	HRRZ	T1,LDBDDB(U)	;MAKE SURE ITY NOT IN USE BY A JOB
	JUMPE	T1,ITYGE2	;IT ISN'T, FINE
	POP	P,U		;GET OUR AOBJN WORD BACK
	SETZM	ITYTAB##(U)	;FORGET WE EVER SAW THIS
	AOBJN	U,ITYGE1	;AND TRY FOR ANOTHER
	JRST	upopj##		;OOPS...AREN'T ANY MORE!

ITYGE2:	HRLI	U,TTYJOB	;JOB CONTROLLING IMP
	MOVEM	U,TTYLIN(F)	;STORE IN DDB
	POP	P,U		;WE HAVE NO NEED FOR THIS ANY MORE
	POP	P,U		;RESTORE U
	JRST	CPOPJ1##	;AND TAKE NORMAL RETURN
;SUBROUTINE TO RELEASE AN ITY.  NORMALLY CALLED BY THE NCP AFTER ;⊗ ITYREL ITYRE2
; THE SOCKET CONNECTIONS HAVE BEEN BROKEN.
;CALL:
;	MOVE	F,ADDRESS OF IMP DDB
;	PUSHJ	P,ITYREL
;	ALWAYS RETURN HERE
ITYREL::
	SKIPL	T1,TTYLIN(F)	;PROCESS CONNECTION?
	POPJ	P,
	SETZM	TTYLIN(F)
	PUSH	P,F		;SAVE IMP DDB
	PUSH	P,U		;SAVE U
	HRRZ	U,T1		;LDB ADDRESS
	JUMPE	U,ITYRE2	;JUMP IF NULL LDB
	LDB	T1,LDPLNO##	;GET LINE NUMBER
	SETZM	ITYOFS##(T1)	;CLEAR OUT ITYTAB ENTRY
	PUSHJ	P,DscClr##	;SIGNAL DISCONNECT and clear down LDB
ITYRE2:	POP	P,U		;RESTORE U
	POP	P,F		;GET DDB ADDRESS BACK
	POPJ	P,
;HOST TABLE:

;ENTRY FORMAT:		;[96bit]

; EACH ENTRY CONSISTS OF three WORDS:   THE FIRST WORD CONTAINS THE
; host number, right justified.  THE SECOND WORD CONTAINS THE ADDRESSES
; OF THE LAST AND FIRST BUFFERS IN ITS data OUTPUT QUEUE, IN THE LEFT
; AND RIGHT HALVES, RESPECTIVELY.  THIS WORD IS ZERO IF
; THERE IS NO OUTPUT FOR THE HOST.   the third word contains pointer
; to the end of the ICMP queue in the left half and flags in
; the right half.  ICMP messages are queued in front of TCP and other
; protocol messages.

;END OF TABLE:		;[96bit]

; a host word (.hthst) of -1 is used to indicate a clerical entry.
; clerical entries have two forms, depending on the value of the
; flag word (.htflg): if the flag word is greater than 0, it is a
; pointer to another buffer where the list continues.  if it is
; negative or zero, it is the end of the list, and the flag word
; contains the negative number of empty entry slots in the current
; buffer.

; the offsets for an entry in the host table are defined above.
; they are of the form "HDT???".
SUBTTL HOST CONTROL AND QUEUEING SUBROUTINES ;⊗ HstEOM EOM03 EOM05 EOM02

;SUBROUTINE TO SEARCH THE HOST LIST FOR A QUEUE FROM WHICH TO
; TRANSMIT A MESSAGE.
;CALL:
;	PUSHJ	P,HstEOM
;	  ERROR RETURN	...ALL QUEUES EMPTY
;	NORMAL RETURN...	ADDRESS OF QUEUE POINTER IN T1
HstEOM:	scnoff				; protect ass
	SKIPG	T2,HSTLAS		;GET THE LAST HOST NUMBER CHECKED
EOM03:	MOVEI	T2,HOSTS-HDTLen		; TOP OF LIST
EOM05:	PUSHJ	P,HSTNXT		; GET NEXT
	  JRST	EOM03			;END OF LIST
	load.	t3,HDTFst,(T2)		; get first pointer
	cain	t3,HDTBfO-BIBTQO(t2)	; is it pointing at itself?
	  jrst	EOM02			; yes.  it's empty.
	load.	t3,HDTFlg,(t2)		; get flag/rfnm count
	txnn	T3,HS.Dwn		; crashed?
	 txne	t3,HS.Max		; or too many in transit now?
	  JRST	EOM02			;YES.  LET CLOCK STUFF HANDLE IT.
	incr.	t3,HDTRnc,(t2)		; one more RFNM out (INCRs HDTFlg)
	HRRZM	T2,HSTLAS		;READY TO GO
	load.	T1,HDTFst,(T2)		; BIB of first message
	load.	t3,BIBNTQ,(t1)		; get next BIB in queue
	stor.	t3,HDTFst,(t2)		; make that the first
	movei	t2,HDTBfO-BIBTQO(t2)	; get pointer to HDT buffer word
	stor.	t2,BIBLTQ,(t3)		; make sure that BIB knows it's first
	zero.	,BIBTQ,(t1)		; remember this isn't in
					;  transmission queue anymore.
	skip.	,BibTim,(t1),le		; supposed to save it?
	  zero.	,BibTim,(t1)		; yes.  clear timer so we don't start
					;  counting again until sent.
ifn debug,<	; debugging
	pushj	p,BIBChk##		; consistency check
	exch	t1,t3			; switch to new BIB
	pushj	p,BIBChk##		; check that
	exch	t1,t3			; return to normal
>
	pjrst	sonpj1##		; skip RETURN, T1 has pointer to the
					;  BIB to send.

;HERE IF THIS ENTRY CANT BE USED
EOM02:	CAMN	T2,HSTLAS	;MORE TO GO?
	  pjrst	sonppj##	;NO, NOTHING READY.
	SKIPG	HSTLAS		;YES.  WAS LAST HOST GIVEN?
	MOVEM	T2,HSTLAS	;NO, INITIALIZE
	JRST	EOM05		;AND LOOP
;SUBROUTINE TO ALLOW TRANSMISSION TO A HOST(PREVIOUS MESSAGE TO ;⊗ GtRFNM
; THE HOST WAS ACKNOWLEDGED)
;CALL:
;	MOVE	T1,HOST NUMBER
;	ScnOff
;	PUSHJ	P,GtRFNM
;	ALWAYS RETURN HERE
GtRFNM:	PUSHJ	P,HSTCHK	;IN THE TABLE?
	  POPJ	P,		;NO.  FORGET IT.
	load.	t3,HDTFlg,(t2)		; get RFNM count
	trnn	t3,HS.Rfn		; is the RFNM field zero?
	 AOSA	BdmRFM##		; yes, COUNT discarded RFNM.
	  decr.	t3,HDTRnc,(t2)		; yes.  one less to wait for.
	PJRST	OUTGO1		;WAKE OUTPUT if needed.
;SUBROUTINE TO INITIALIZE THE HOST TABLE. ;⊗ HSTINI
;CALL:
;	PUSHJ	P,HSTINI
;	ALWAYS RETURNS HERE

HSTINI:	MOVEI	T1,1			;INITIALIZE HOST COUNT
	MOVEM	T1,HSTCNT
	move	t1,MySite##		;[96bit] put our site in the table
	stor.	T1,HDTAdr,HOSTS		;[96bit]  AS THE ONLY ENTRY
	move	t1,[Hosts+HDTBfO,,Hosts+HDTBfO]	; point buffer stream at self
	stor.	t1,HDTBfs,Hosts		; ....
	zero.	t1,HDTFlg,Hosts		; clear flags.
	zero.	t1,HDTAdr,hosts+HDTLen	;[96bit] set end-of-table flag
	POPJ	P,
;SUBROUTINE TO CHECK TO SEE THAT A HOST IS GOOD AND, IF SO, ;⊗ Go1822 Gx1822
; SEND THE MESSAGE.
;CALL:
;	MOVE	T1, <pointer to BIB of message to be sent>
;	MOVE	f, [ADDRESS OF connection's DDB]
;	ScnOff
;	PUSHJ	P,Go1822
;	  ERROR RETURN...	SPACE PROBLEM OR HOST SICK
;	OK RETURN...	MESSAGE ON ITS WAY
Go1822::push	p,t1			; save t1
	move	t1,NetAdr(f)		; get target host address
IFN DEBUG,<
	JUMPn	T1,Gx1822		; skip bug report if no bug
	stopcd	.+1,DEBUG,NTA		;++ no target address
	setzm	UtTimr(f)		; have no patience with this one.
	jrst	tpopj##			; restore T1 and return
Gx1822:
>
	PUSHJ	P,HSTNEW	;MAKE SURE IN THE TABLES
	  pjrst	tpopj##		;NO ROOM
	load.	t1,HDTFlg,(t2)		; get flags
	txz	t1,HS.Dwn		; clear down flag to give this
					;  send a chance.
	stor.	t1,HDTFlg,(t2)		; put that back in place.
	pop	p,t1			; get back BIB pointer
	aos	(p)			; always good return now.
	load.	t3,HDTLst,(t2)		; get our tail
ifn debug,<	; another bug check
	cain	t3,(t1)			; same one?
	 stopcd	CPOPJ##,DEBUG,LMS	;++ linking message to self
>
	stor.	t1,BIBNTQ,(t3)		; point it at us.
	stor.	t3,BIBLTQ,(t1)		; and point us at him
	stor.	t1,HDTLst,(t2)		; make us the new last.
	movei	t3,HDTBfO-BIBTQO(t2)	; point us at the last area
	stor.	t3,BIBNTQ,(t1)		; by making it look like our next.
ifn debug,<	; debugging?
	pushj	p,BIBChk##		; make a consistency check
>
	JRST	OUTGO1			;YES, START IT UP
repeat 0,<	; we may want to check for lost RFNMs, but for now i'm ;⊗ HOSTCK HOSTC0 HOSTC5 HOSTC6
		;  going to ignore the problem, since the IMP is supposed
		;  to guarenty RFNMs.  we may lose RFNMs if our interface
		;  fails, but we still have 7 left before transmission to this
		;  host will fail.

;SUBROUTINE TO CHECK A HOST FOR lost RFNM at clock level.  TESTING TAKES PLACE
; ON ALL HOSTS IN THE TABLE.
;CALL:
;	PUSHJ	P,HOSTCK
;	ALWAYS RETURN HERE
HOSTCK:	PUSHJ	P,SAVE4##	;SAVE ALL THE ACS
	ScnOff			;LOCK OUT CONFLICTS
	SKIPG	T2,LASCHK
HOSTC0:	  MOVEI	T2,HOSTS-HDTLen	;[96bit] START AT TOP
	PUSHJ	P,HSTNXT	;GET NEXT HOST TO CHECK
	  JRST	HOSTC0		;END,   LOOP.
	MOVEM	T2,LASCHK	;REMEMBER WHICH WAS CHECKED
	load.	t1,HDTAdr,(t2)	;[96bit] get the host number
	JUMPE	T1,sonppj##	;DONT TEST IF EMPTY
	TRNN	T3,HS.NRM	;RFNM OUT?
	  jrst	sonppj##			; no.  reset interrupt and return
	MOVx	T3,HS.NRX	;RFNM TIMER FLAG
	tdne	t3,.htflg(t2)	;[96bit] set now?
	  JRST	HOSTC5		;YES, SIMULATE ONE
	iorb	t3,.htflg(t2)	;[96bit] no.  set it.
	jrst	sonppj##		; reset interrupts and return

;HERE TO SIMULATE A RFNM
HOSTC5:	AOS	NORFNM		;COUNT THE ERROR
	MOVSI	T3,HS.NRM!HS.NRX
	ANDCAB	T3,.htflg(T2)	;[96bit] TURN OFF RFNM FLAGS
HOSTC6:	PUSHJ	P,OUTGO1##	;WAKE UP TRANSMISSION
	JRST	sonppj##		;TURN ON INTERRUPTS AND RETURN

> ; end of REPEAT 0
;SUBROUTINE TO FLAG A HOST AS BAD.   CALLED FROM INTERRUPT OR CLOCK ;⊗ HOSTBD BadLp HostB1
; LEVEL.
;CALL:
;	MOVE	T1, [HOST NUMBER]
;	ScnOff
;	PUSHJ	P,HOSTBD
;	ALWAYS RETURN HERE
HOSTBD:	PUSHJ	P,HSTCHK	;YES,  IN TABLE?
	  POPJ	P,		;NO,  FORGET IT.
	camn	t1,MySite##	;[96bit] THIS SITE?
	  POPJ	P,		;YES
NOWAITS<
	pushj	p,save1##		; get p1
>;NOWAITS
	txo	t3,HS.Dwn		; remember not to transmit
	stor.	t3,HDTFlg,(t2)		; remember it's down
NOWAITS<
	movei	p1,impn			; set up count
>;NOWAITS
	movei	f,impddb		; start at the very beginning
BadLp:	came	t1,NetAdr(f)		; is this DDB sending to this site?
	  jrst	HostB1			; no.  try next
	movei	t2,TrgDwn		; get target down flag
	iorm	t2,ImpIOS(f)		; set bit
	pushj	p,DDBFls##		; flush all buffers attached here,
					;  make state closed.
	pushj	p,ImpWak		; wake up job if needed
HostB1:	hlrz	f,DevSer(f)		; get next in chain
NOWAITS<			;JJW - Shouldn't this be a SOJG?
	sojle	p1,BadLp		; loop until munged
>;NOWAITS
IFWAITS<
	CAIE F,IMP.NX##
	 JRST BADLP
>;IFWAITS
	popj	p,			; all done.
repeat 0,<	; not used yet ;⊗ HSTRES

;SUBROUTINE TO RESET(NOT WIPE) A HOST
HSTRES:	MOVSI	T3,HS.OK	;SET OK FLAG
	IORM	T3,.htflg(T2)
	HRLOI	T3,HS.NRM!HS.NRX!HS.XMT!HS.RST!HS.OK
	ANDB	T3,.htflg(T2)	;[96bit] CLEAR ALL BUT OUTPUT AND REPLY FLAGS
	JRST	HSTCLU		;CLEAR OUT USERS

> ; end of repeat 0
repeat 0,<	; not used yet. ;⊗ HSTCLR HSTCL1 HSTCLU HSTCL3 HSTCL4 HSTCL5 HSTCL6

;SUBROUTINE TO WIPE A HOST OUT OF ALL TABLES IN THE
;  NCP.  CALLED FROM HOSTBD.
;CALL:
;	MOVE	T1, [HOST NUMBER]
;	ScnOff
;	PUSHJ	P,HSTCLR
;	ALWAYS RETURN HERE
HSTCLR:	PUSHJ	P,HSTCHK	;IN TABLE?
	  POPJ	P,		;NO, FORGET IT

;HERE TO WIPE A HOST OUT OF EXISTENCE
HSTCL1:	MOVSI	T3,HS.BAD	;SET HOST BAD FLAG
	MOVEM	T3,.htflg(T2)	;[96bit] WIPE ALL ELSE
	zero.	t3,HSTHst,(t2)	;[96bit] forget the host
	SOSG	HSTCNT		;DECREMENT COUNT OF HOSTS
	AOS	HSTCNT		;BUT KEEP IT POSITIVE

;HERE TO REMOVE ALL USER REFERENCES TO A HOST AND
;  ABORT ALL PENDING MESSAGES TO THAT HOST.
HSTCLU:	PUSHJ	P,SAVE2##	;SAVE ACS
	move	P1,T1		;[96bit] HOST NUMBER
	PUSH	P,F		;SAVE DDB ADDRESS
	load.	T1,HDTFst,(T2)	;[96bit] GET ADDRESS OF BUFFER HEADER
	PUSHJ	P,OUTCHK##	;AND SEE IF IT'S BUSY OUTPUTING NOW
	  JRST	HSTCL3		;YES, CAN'T DELETE MESSAGES SINCE IMPSER WANTS TO
	MOVEI	T1,0		;CLEAR BUFFERS USED
croak
	EXCH	T1,.htbuf(T2)
	PUSHJ	P,RELBUF##	;RELEASE THE STRING OF BUFFERS
HSTCL3:
NOWAITS<
	PUSH	P,[IMPN]	;COUNT OF THE DDBS
>;NOWAITS
	MOVEI	F,IMPDDB	;FOR LOSING USERS
HSTCL4:
	LDB	T3,POHOST
	came	t3,p1		;[96bit] THIS HOST?
	JRST	HSTCL5
	hrlo	p2,f		;[96bit] save F in P2, and make P2
				; odd so NCPIOD (called from
				; CLSDNO) will know to close output
	PUSHJ	P,CLSDNO	;CLOSE IT
	hlrz	f,p2		;[96bit] restore F
HSTCL5:	LDB	T3,PIHOST
	came	t3,p1		;[96bit] SAME INPUT HOST?
	JRST	HSTCL6		;NO
	hrlz	p2,f		;[96bit] save F in P2, and make P2
				; even so NCPIOD (called from CLSDNI)
				; will know to close input.
	PUSHJ	P,CLSDNI	;CLOSE IT
	hlrz	f,p2		;[96bit] restore F
HSTCL6:	HLRZ	F,DEVSER(F)	;GET NEXT DDB IN CHAIN
NOWAITS<
	SOSLE	(P)		;COUNT THE DDBS DONE
>;NOWAITS
IFWAITS<
	CAIE F,IMP.NX##
>;IFWAITS
	JRST	HSTCL4		;LOOP FOR ANOTHER
	POP	P,F		;CLEAR COUNTER OFF STACK
	POP	P,F		;RESTORE DDB ADDRESS
	POPJ	P,

> ; end of repeat 0
;SUBROUTINE TO ENTER A HOST IN THE TABLE. ;⊗ HSTNEW HSTNE0 HSTNE4 HSTNE1 HSTNE3 HSTNE5
;CALL:
; 	move	T1, [HOST NUMBER]
;	ScnOff
;	PUSHJ	P,HSTNEW
;	  ERROR RETURN	...NO SPACE
;	OK RETURN...	T2 HAS ADDRESS OF NEW ENTRY
HSTNEW:	PUSHJ	P,HSTCHK	;ALREADY THERE?
	  JRST	HSTNE0		;NO
	JRST	CPOPJ1##	;SKIP RETURN

;HERE IF HOST NOT YET IN TABLE.
HSTNE0:	push	p,t1		;[96bit] SAVE HOST NUMBER
	MOVEI	T1,0
	PUSHJ	P,HSTCHK	;FIND EMPTY SLOT
	  JRST	HSTNE1		;NONE
	JRST	HSTNE3		;GOT ONE

;HERE WHEN BUFFER FULL, GET MORE CORE
HSTNE4:	PUSH	P,T2		;END OF BUFFER ADDRESS
	PUSHJ	P,BufGet##	;GET A BUFFER
	  JRST	HSTNE5		;NONE!
	POP	P,T3		;LINK TO LAST ONE
	stor.	t1,HDTNxt,(t3)	;[96bit] link up to last buffer
	movni	t3,<<ImpBfs##-2>/HDTLen> ;[96bit] get the entries per buffer.
	move	t2,t1		;[96bit] shuffle acs

;[96bit] HERE TO EXTEND THE TABLE.  t3 has the contents of the HDTFlg
;[96bit] word of the end of table entry.
HSTNE1:	AOJG	T3,HSTNE4	;JUMP IF NO ROOM LEFT
	zero.	,HDTAdr,HDTLen(t2)	;[96bit] advance end of tables
	stor.	T3,HDTNxt,HDTLen(T2)	;[96bit] NEW FREE ENTRY COUNT
HSTNE3:	pop	p,t1		;[96bit] RESTORE HOST NUMBER
	stor.	t1,HDTAdr,(t2)	;[96bit] store in table
	movei	t3,HDTBfO-BIBTQO(t2)	; point at buffer word for BIB access
	stor.	t3,HDTFst,(t2)		; make this the first
	stor.	t3,HDTLst,(t2)		; and make it the last, too.
	zero.	t3,HDTRnc,(T2)	;[96bit] flags and ICMP end buffer pointer
	AOS	HSTCNT		;BUMP HOST COUNT
	JRST	CPOPJ1##

;HERE IF NO ROOM FOR NEW HOST ENTRY
HSTNE5:	POP	P,T2
	jrst	tpopj##		;[96bit] restore T1 and return
;SUBROUTINE TO ENSURE THAT A HOST IS IN THE TABLES ;⊗ HSTCHK HSTCK1 HSTNXT HSTNX0 HSTNX1
;CALL:
; 	move	T1, [HOST NUMBER]
;	ScnOff
;	PUSHJ	P,HSTCHK
;	  ERROR RETURN	...HOST NOT THERE.  T2 POINTS AT FREE SLOT.
;	OK RETURN...	T2 HAS ENTRY ADDRESS, TEM HAS FLAGS. INTERRUPTS OFF
HSTCHK:	MOVEI	T2,HOSTS-HDTLen		; START AT TOP
HSTCK1:	PUSHJ	P,HSTNXT		; GET NEXT ENTRY
	  POPJ	P,			; NO NEXT ENTRY...EMPTY
	cam.	t1,HDTAdr,(t2),e	; is it this host?
	  JRST	HSTCK1			; NO
	JRST	CPOPJ1##		; GOOD RETURN

;SUBROUTINE WHICH, GIVEN THE LAST HOST REFERENCED, RETURNS THE
; NEXT.
;CALL:
;	MOVE	T2,ADDRESS OF LAST ENTRY USED
;	PUSHJ	P,HSTNXT
;	  ERROR RETURN	...END OF LIST
;	OK RETURN...	T2 HAS ADDRESS OF NEXT ENTRY
HSTNXT:
IFN DEBUG,<
	JUMPN T2,HSTNX0
	STOPCD	.+1,DEBUG,ZHE,	;++ZERO HOST ENTRY
	MOVEI	T2,HOSTS-HDTLen
>
HSTNX0:	ADDI	T2,HDTLen		; BUMP THE POINTER
HSTNX1:	skip.	,HDTAdr,(T2),LE		; is the host word negative?
	  jrst	[			; no.  this is a real entry
		 load.	t3,HDTFlg,(t2)	; get flag word for him
		 pjrst	cpopj1##	; and return
		]
	load.	t3,HDTNxt,(t2)		; get pointer to next buffer.
	JUMPLE	T3,CPOPJ##		; no next buffer: negative count of
					;  entries left in this buffer.
	MOVE	T2,T3			;LINK TO NEXT BUFFERFUL
	JRST	HSTNX1			; loop
;SUBROUTINE TO DETERMINE WHETHER ANY IMP DDB REFERENCES A GIVEN HOST. ;⊗ HSTUSE HSTUS1 HstUs2
;	MOVE	T1,[HOST NUMBER]
;	PUSHJ	P,HSTUSE
;	  HOST NOT REFERENCED BY ANYBODY
;	HOST REFERENCED BY AT LEAST 1 DDB
;NO AC'S CLOBBERED

NOWAITS<
HSTUSE:	PUSHJ	P,SAVE2##	;SAVE P1-P2
	PUSH	P,F		;SAVE DDB POINTER
	MOVEI	F,IMPDDB	;START SEARCH OF ALL DDB'S
	MOVEI	P1,IMPN##	;NUMBER OF DDBS
	jrst	HstUs2			; jump into the loop
HSTUS1:	HLRZ	F,DEVSER(F)	;ADVANCE TO NEXT DDB
HstUs2:	camn	t1,NetAdr(f)	;[96bit] HOST REFERENCED BY THE DDB?
	 AOSA	-1(P)		;YES, PRESET SKIP RETURN
	  SOJG	P1,HSTUS1	;NO, CHECK NEXT DDB
	POP	P,F		;RESTORE DDB POINTER
	POPJ	P,
>;NOWAITS

IFWAITS<
HSTUSE:	PUSH P,F		;SAVE DDB POINTER
	MOVEI F,IMPDDB		;START SEARCH OF ALL DDB'S
HSTUS1:	CAMN T1,NETADR(F)
	 JRST FPOPJ1##
	HLRZ F,DEVSER(F)
	CAIE F,IMP.NX##
	 JRST HSTUS1
	JRST FPOPJ##
>;IFWAITS
; routine to get the pseudo DDB for input, clear it out, and return ;⊗ GetPDB
;  a pointer to it in F.

GetPDB:
	setzm	PsdDDB+PDBTop		; zero first word of block
	move	f,[ xwd	PSDDDB+PDBTop,PSDDDB+PDBTop+1 ]	; BLT pointer
	blt	f,PSDDDB+PDBBot		; clear entire block
	movei	f,PSDDDB		; point at hypothetical start
	popj	p,			; and return
; routine to take the host in T1 and make all entries in the retransmission ;⊗ FixRTQ FixLup
;  queue use that host.
FixRTQ::
	pushj	p,save2##		; save some regs
	scnoff				; don't allow distractions
	move	p1,RetrnQ(f)		; get our retranmission queue
FixLup:	jumpe	p1,sonppj##		; no more in retranmission queue
	load.	p2,BIBMes,(p1)		; point at the first buffer in
					;  the message.
	skip.	,BIBTQ,(p1),n		; is this in the transmission queue?
	 skip.	,BIBTim,(p1),g		; or being transmitted?
	 skipa				; yes to one.  can't replace target.
	  stor.	t1,HTIAdr,NBHLen(p2)	; no. put this new target in place.
	load.	p1,BIBRTQ,(p1)		; get next in retranmission queue
	jrst	FixLup			; and loop through queue
IFN DEBUG,< ;⊗ INDERR IMPWCP
;ROUTINE TO HALT WITH THE "IND" ERROR (INTERRUPTS NOT DISABLED).
;   CALLED WITH A JSR FROM WITHIN THE CHKINT MACRO.

	$LOW
INDERR::0
	STOPCD	@INDERR,DEBUG,IND, ;++INTERRUPTS NOT DISABLED
IMPWCP::
	STOPCD .+,STOP,IN0	;++ IMP STUFF ON WRONG CPU
	$HIGH
>
	SUBTTL	IMPSER DATA BASE ;⊗ ZERO INHALT IBFHLT INBUFP MESSIZ impihd LEADER BUFADR FLTFLG STOPFL TESTHS OLINKP NowOut IMPQ IMPQTP IMPQTC IMPQPP IMPQPC IMPREQ CLKSEC DEDFLG IMPUP OKFLAG PSDDDB IMkDDB HSTLAS LASCHK HOSTS HSTCNT ZERON
	$LOW
	ZERO==.


INHALT:	0		;-1 IF INPUT INTERRUPTED FOR LACK OF BUFFER
IBFHLT::0		;-1 IF BUFFER STILL NEEDED
INBUFP:	0		;POINTS TO MESSAGE BEING INPUT(FIRST,,LAST BUFFER)
MESSIZ:	0		;HAS DATA MESSAGE SIZE IN BITS
impihd:	block	HTIWds	;[96bit] location to store imp to host leader
			;	 while it's coming in.  first word = 0
			;	 flags host-host message.
LEADER:	0		;TEMPORARY STORAGE OF LEADER
BUFADR:	0		;NEW BUFFER ADDRESS
FLTFLG:	0		;-1 IF HARDWARE ERROR
STOPFL::0		;-1 IF IMP GOING DOWN
IFN DEBUG,<
TESTHS:	0		;IF NON-ZERO, ONLY MESSAGES FROM
			;  SPECIFIED HOST ARE ACCEPTED.
>


OLINKP:	XWD 0,0			;ADDRESS OF OUTPUT data BUFFER POINTER.
NowOut:	0			; buffer now being output.

IMPQ:	BLOCK IMPQLN		;QUEUE FOR HIGH PRIORITY HOST-IMP
				;  MESSAGES.
IMPQTP:	0		;TAKE POINTER
IMPQTC:	0		;TAKE COUNTER
IMPQPP:	0		;PUT POINTER
IMPQPC:	0		;PUT COUNTER

IMPREQ:	0		;NUMBER OF MESSAGES TO BE SENT

CLKSEC:	0			;IF POSITIVE, COUNTS DOWN TO NEXT TEST
DEDFLG:	0			;-1 IF IMP IS(OR WAS JUST) DEAD
IMPUP::	0			;-1 IF WANT THE IMP SYSTEM UP
OKFLAG::0			;-1 IF IMP IS USEABLE


PSDDDB=:.-PDBTop		; define hypothetical start of our pseudo DDB
	block	PDBBot-PDBTop+1	; number of words we really use

IMkDDB=.-OBfTop			; hypothetic start of this DDB
	block	OBfBot-OBfTop+1	; allocate words needed

;TEMPORARY STORAGE
HSTLAS:	BLOCK	1	;TABLE ENTRY ADDRESS FOR LAST HOST TO TRANSMIT
LASCHK:	BLOCK	1	;TABLE ENTRY ADDRESS FOR LAST HOST TO BE CHECKED

HOSTS:	BLOCK	HDTLen+2;[96bit] start of HOST TABLE.
			;  CONTAINS FLAGS AND OUTPUT
			;  QUEUE FOR NCP LINK OUTPUT TO EACH HOST.
			;  TWO WORDS PER HOST.  THIRD WORD
			;  HAS POINTER TO NEXT SEGMENT OF THE LIST.
			;  SEE COMMENTS IN HOST TESTING SECTION FOR
			;  MORE DETAIL.
HSTCNT:	BLOCK	1	;COUNT OF HOSTS IN THE TABLE (ABOVE)


	ZERON==	.-ZERO		;NUMBER OF WORDS TO CLEAR
IMPLIT:	$LIT ;⊗ IMPLIT IMPEND
	VAR
IMPEND:	END