Google
 

Trailing-Edge - PDP-10 Archives - bb-jr93e-bb - 7,6/ap017/klpser.x17
There is 1 other file named klpser.x17 in the archive. Click here to see a list.
TITLE	KLPSER - SERVICE ROUTINES FOR CI20 (KLIPA)	V45
SUBTTL	JOSEPH A. DZIEDZIC/JAD	24 JUN 86


	SEARCH	F,S,KLPPRM,ICHPRM,SCAPRM
	SEARCH	MSCPAR,MACSYM
	$RELOC
	$HIGH


	PURGEACS		;PURGE DEFAULT (TOPS-10) AC NAMES
	T20SYM			;SWITCH TO TOPS-20 AC NAMES
				;(NEEDED FOR INTERFACE TO SCAMPI)

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1984,1986>
;COPYRIGHT (C) 1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.


XP	VKLPSR,45		;VERSION NUMBER FOR GLOB AND MAP


KLPSER:!ENTRY	KLPSER		;LOAD IF LIBRARY SEARCH
	SUBTTL	PARAMETERS


;CI OPERATION CODES

OP.SDG==1			;SEND DATAGRAM
OP.SMS==2			;SEND MESSAGE
OP.RCF==3			;CONFIRM RECEIVED
OP.MCR==4			;MAINTENANCE CONFIRM RECEIVED
OP.RID==5			;REQUEST ID
OP.RRS==6			;RESET REMOTE SYSTEM
OP.SRS==7			;START REMOTE SYSTEM
OP.RD0==10			;REQUEST DATA ON QUEUE 0
OP.RD1==11			;REQUEST DATA 1
OP.RD2==12			;REQUEST DATA 2
OP.IDR==13			;ID RECEIVED
OP.LPB==15			;SEND/RECEIVE LOOPBACK
OP.RMD==16			;REQUEST MAINTENANCE DATA
OP.SDT==20			;SEND DATA
OP.RDT==21			;RETURN DATA (DATREC)
OP.SMD==22			;SEND MAINTENANCE DATA
OP.MDR==23			;MAINTENANCE DATA RECEIVED
OP.CKT==200			;SET VIRTUAL CIRCUIT
OP.SPT==201			;SET STATISTICS COUNTER
OP.RCT==202			;READ STATISTICS COUNTER
OP.RRG==203			;READ REGISTER
OP.WRG==204			;WRITE REGISTER
OP.CLB==205			;CLOSE BUFFER
OP.RMT==40			;ON IN REMOTELY GENERATED RESPONSES

INCXID==1B31			;VALUE TO INCREMENT TRANSACTION ID BY

MINUVR==100,,711		;MINIMUM KLIPA MICROCODE VERSION

MAXNOR==77			;NUMBER OF TIME OUTS BEFORE WE DECLARE THE OTHER PORT SICK

RIDTIM==^D2			;TIMEOUT FOR REQUEST-ID (SECONDS)
STSTIM==^D10			;TIMEOUT FOR START SEQUENCE (SECONDS)
CTRTIM==^D60*^D60		;TIMEOUT FOR PERIODIC READ-COUNTERS (SECONDS)

TIMOUT==^D1000			;VALUE OF INTERLOCK WORD AT WHICH WE DECLARE THE KLIPA DEAD
RSTIME==^D60*^D3		;LEAVE KLIPA STOPPED IF 2 ERRORS IN THIS INTERVAL

KAFTIM==^D10*^D1000		;KEEP ALIVE TIMEOUT (MILLISECONDS)
IDLTIM==^D1*^D1000		;IDLE TIME (MILLISECONDS)

CO.BTS==CO.ENA+CO.MRN		;BITS WHICH MUST BE ON IN ALL CONOS
;GLOBAL SYMBOLS FOR AUTCON

KLPPCB==:BYTE (9) .PCLEN, .PCPCB, .PCPCL, 0 ;BYTE (9) LENGTH OF PCB, OFFSET TO
				; PHYSICALLY CONTIGUOUS AREA, LENGTH OF
				; PHYSICALLY CONTIGUOUS AREA

KLPBTS==CI.CPE!CI.MER!CI.EPE!CI.FQE!CI.RQA ;BITS TO TEST FOR ON INTERRUPT


KLPX1==602547			;KLIPA TOPS-20 ERROR CODES
KLPX2==602050
KLPX5==602027
KLPX7==602031
KLPX9==602033
KLPX10==602734
KLPX11==602735
KLPX13==602052
KLPX14==602054
	SUBTTL	BHD/BSD INITIALIZATION


;ROUTINE TO GET SPACE FOR BHD'S AND BSD'S. CALLED DURING ONCE BEFORE
;THE CALL TO PPDINX SO BHDADR IS VALID.  NOTE ALL KLIPAS IN THE
;SYSTEM SHARE THE SAME POOL OF BHD'S AND BSD'S.
;CALL:
;	PUSHJ	P,BHDINI
;RETURN:
;	CPOPJ ALWAYS

BHDINI::SKIPE	BHDIPT		;ALREADY HAVE A BUFFER DESCRIPTOR TABLE SET UP?
	POPJ	P,		;YES, NOTHING ELSE TO DO
	SE1ENT			;ENTER NON-ZERO SECTION
	MOVEI	T1,C%BHDN*.BHSIZ ;NUMBER OF WORDS FOR BUFFER HEADER DESCRIPTORS
	IDIVI	T1,PGSIZ	;DETERMINE NUMBER OF PAGES
	SKIPE	T2		;IF A REMAINDER,
	AOS	T1		; ROUND UP
	PUSH	P,T1		;SAVE THE COUNT FOR LATER
	PUSHJ	P,PGRSKD	;GET THE RESIDENT SPACE
	  JRST	TPOPJ##		;NOT AVAILABLE?!
	MOVEM	T1,BHDIPT	;STORE INITIAL POINTER FOR BHD SEARCHES
	POP	P,T2		;RESTORE NUMBER OF PAGES REQUESTED
	MOVEM	T2,BHDPGS	;STORE FOR MEMORY OFFLINE CODE
	LSH	T2,P2WLSH##	;HOW MANY WORDS WERE ALLOCATED
	MOVE	T3,T2		;SAVE A COPY FOR LATER
	ADD	T2,T1		;COMPUTE LAST ADDRESS IN BUFFER DESCRIPTOR TABLE
	SUBI	T2,1		;...
	MOVEM	T2,BHDEND	;STORE FOR BHD SEARCHES
	IDIVI	T3,.BHSIZ	;COMPUTE HOW MANY BHD'S WILL FIT IN SPACE ALLOCATED
	MOVEM	T3,BHDNUM	;STORE FOR POSSIBLE FUTURE BHD RE-INIT
	PUSHJ	P,BHDCLR	;CLEAR ALL BHD'S

	MOVEI	T1,C%BSDN*.BSSIZ ;NUMBER OF WORDS FOR BUFFER SEGMENT DESCRIPTORS
	IDIVI	T1,PGSIZ	;DETERMINE NUMBER OF PAGES
	SKIPE	T2		;IF A REMAINDER,
	AOS	T1		; ROUND UP
	PUSH	P,T1		;SAVE THE COUNT FOR LATER
	PUSHJ	P,PGRSKD	;GET THE RESIDENT SPACE
	  JRST	TPOPJ##		;NOT AVAILABLE?!
	MOVEM	T1,BSDVRT	;SAVE VIRTUAL ADDRESS OF START OF BSD CHAIN
	POP	P,T2		;RESTORE NUMBER OF PAGES REQUESTED
	MOVEM	T2,BSDPGS	;SAVE FOR MEMORY OFFLINE CODE
	LSH	T2,P2WLSH##	;HOW MANY WORDS WERE ALLOCATED
	IDIVI	T2,.BSSIZ	;COMPUTE HOW MANY BSD'S WILL FIT IN SPACE ALLOCATED
	MOVEM	T2,BSDNUM	;STORE FOR POSSIBLE FUTURE BSD RE-INIT
	PJRST	BSDLNK		;LINK ALL THE BSD'S AND RETURN
;ROUTINE TO CLEAR ALL BHD'S.  CALLED BY BHDINI OR SET MEMORY OFFLINE
;CODE AFTER MEMORY HAS BEEN DIDDLED AS NECESSARY.
;CALL:
;	PUSHJ	P,BHDCLR
;RETURN:
;	CPOPJ ALWAYS

BHDCLR:	MOVE	T1,BHDIPT	;START OF BUFFER DESCRIPTOR TABLE
	MOVEM	T1,BHDCPT	;RESET CURRENT POINTER FOR BHD SEARCHES
	MAP	T2,0(T1)	;GET THE PHYSICAL ADDRESS FOR THE KLIPA
	TXZ	T2,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVEM	T2,BHDADR	;SAVE PHYSICAL ADDRESS OF BDT FOR PCBINI
	MOVE	T2,BHDNUM	;NUMBER OF BHD'S IN BDT
BHDCL1:	SETZM	.BHKEY(T1)	;ZERO VALID WORD
	SETZM	.BHBSA(T1)	;DITTO FOR POINTER TO FIRST BSD
	ADDI	T1,.BHSIZ	;ADVANCE POINTER
	SOJG	T2,BHDCL1	;INITIALIZE ALL BHD'S
	POPJ	P,		;RETURN


;ROUTINE TO LINK ALL BSD'S.  CALLED BY BHDINI OR SET MEMORY OFFLINE
;CODE AFTER MEMORY HAS BEEN DIDDLED AS NECESSARY.
;CALL:
;	PUSHJ	P,BSDLNK
;RETURN:
;	CPOPJ ALWAYS

BSDLNK:	MOVE	T1,BSDVRT	;GET VIRTUAL ADDRESS OF START OF BSD CHAIN
	MAP	T2,(T1)		;GET THE PHYSICAL ADDRESS
	TXZ	T2,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVEM	T2,BSDADR	;SAVE PHYSICAL ADDRESS OF START OF BSD CHAIN
	MOVEM	T2,BSDLOC	;ALSO SAVE AS START OF FREE BSD CHAIN
	MOVE	T2,BSDNUM	;NUMBER OF BSD'S WHICH WERE ALLOCATED
	SUBI	T2,1		;MINUS ONE
BHDIN2:	MAP	T3,.BSSIZ(T1)	;GET PHYSICAL ADDRESS OF NEXT BSD
	TXZ	T3,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVEM	T3,.BSNXT(T1)	;LINK NEXT TO THIS
	ADDI	T1,.BSSIZ	;GET ADDRESS OF NEXT BSD
	SOJG	T2,BHDIN2	;LOOP FOR ALL BSD'S
	SETZM	.BSNXT(T1)	;CLEAR LINK TO NEXT IN LAST BSD
	POPJ	P,		;RETURN
	SUBTTL	BHD/BSD MANIPULATION


;ROUTINE TO GET A BUFFER HEADER DESCRIPTOR.
;CALL:
;	PUSHJ	P,PPDGBH
;RETURN:
;	CPOPJ IF NO FREE BHDS
;	CPOPJ1 WITH:
;	T1/ INDEX INTO BUFFER DESCRIPTOR TABLE
;	T2/ VIRTUAL ADDRESS OF BHD

PPDGBH::SKIPN	BSDLOC		;ANY FREE BSDS?
	RETBAD	(KLPX2)		;NO, NO SENSE IN TRYING
	CIOFF			;PREVENT RACES
	MOVE	T1,BHDCPT	;GET CURRENT POINTER FOR BHD SEARCHES
PPDGH1:	SKIPN	.BHKEY(T1)	;THIS BHD FREE?
	JRST	PPDGH2		;YES
	XMOVEI	T1,.BHSIZ(T1)	;ADDRESS OF NEXT BHD
	CAML	T1,BHDEND	;PAST THE END OF THE TABLE?
	MOVE	T1,BHDIPT	;YES, RESET TO BEGINNING
	CAME	T1,BHDCPT	;HAVE WE LOOPED OVER THE ENTIRE TABLE?
	JRST	PPDGH1		;NO, KEEP LOOKING
	RETBAD	(KLPX1,<CION>)	;YOU LOSE

PPDGH2:	MOVX	T2,1B0		;GET A TEMPORARY FLAG
	IORM	T2,.BHKEY(T1)	;(OVERWRITTEN WITH KEY LATER)
	XMOVEI	T2,.BHSIZ(T1)	;ADDRESS OF NEXT BHD
	CAML	T2,BHDEND	;PAST THE END OF THE TABLE?
	MOVE	T2,BHDIPT	;YES, RESET TO BEGINNING
	MOVEM	T2,BHDCPT	;WHERE TO RESUME SEARCHING
	CION			;OK TO INTERRUPT
	MOVE	T2,T1		;COPY THE BHD ADDRESS
	SUB	T1,BHDIPT	;COMPUTE INDEX INTO TABLE
	JRST	CPOPJ1##	;SKIP RETURN


;ROUTINE TO RETURN A BUFFER HEADER DESCRIPTOR.
;CALL:
;	T1/ BUFFER NAME
;	PUSHJ	P,PPDRBH
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T2/ ADDRESS OF FIRST BSD (IF ANY)
;	T4/ CONTENTS OF .BHKEY WORD

PPDRBH::LDB	T1,[POINT BHSIDX,T1,BHPIDX] ;GET INDEX INTO TABLE
	ADD	T1,BHDIPT	;COMPUTE VIRTUAL BHD ADDRESS
	MOVE	T2,.BHBSA(T1)	;SAVE POINTER TO ANY BSDS
	MOVE	T4,.BHKEY(T1)	;RETURN THE KEY WORD FOR ERROR CHECKING
	SETZM	.BHKEY(T1)	;MARK THIS BHD AS FREE
	POPJ	P,		;RETURN
;ROUTINE TO GET A BUFFER SEGMENT DESCRIPTOR.
;CALL:
;	PUSHJ	P,PPDGBD
;RETURN:
;	CPOPJ IF NO FREE BSDS
;	CPOPJ1 WITH:
;	T1/ PHYSICAL ADDRESS OF BSD
;	T2/ VIRTUAL ADDRESS OF BSD

PPDGBD::CIOFF			;PREVENT RACES
	SKIPN	T1,BSDLOC	;GET HEAD OF FREE LIST
	RETBAD	(KLPX2,<CION>)	;NONE FREE, RETURN ERROR
	MOVE	T2,T1		;COPY ADDRESS
	ADDI	T2,.BSNXT	;OFFSET TO WORD WE WANT
	PUSHJ	P,PHMOV##	;FETCH THE LINK WORD
;***
;SUPPOSED TO RESERVE 1 BHD/BSD FOR DIAGNOSTIC FOLKS' USE
;***
	JUMPE	T2,[JFCL	;DIAGNOSTIC?
		    JRST .+1	;YES, GO AHEAD AND USE THIS BSD
		    RETBAD (KLPX2,<CION>)] ;NO, DON'T USE LAST BSD
	MOVEM	T2,BSDLOC	;NEW HEAD OF FREE LIST
	MOVE	T2,T1		;COPY PHYSICAL BSD ADDRESS
	SUB	T2,BSDADR	;CALCULATE OFFSET FROM START OF AREA
	ADD	T2,BSDVRT	;MAKE THIS A VIRTUAL ADDRESS
	PJRST	CINPJ1##	;INTERRUPTS BACK ON AND SKIP RETURN


;ROUTINE TO RETURN A BUFFER SEGMENT DESCRIPTOR.
;CALL:
;	T1/ PHYSICAL ADDRESS OF BSD
;	PUSHJ	P,PPDRBD
;RETURN:
;	CPOPJ ALWAYS

PPDRBD::CIOFF			;PREVENT RACES
	MOVE	T3,BSDLOC	;GET HEAD OF FREE LIST
	MOVE	T2,T1		;COPY ADDRESS
	ADDI	T2,.BSNXT	;OFFSET TO WORD WE WANT
	PUSHJ	P,PHMOVM##	;LINK THE FREE LIST TO THIS BSD
	MOVEM	T1,BSDLOC	;NEW HEAD OF THE FREE LIST
	PJRST	CIONPJ##	;INTERRUPTS BACK ON AND RETURN
;ROUTINE TO RETURN A BUFFER HEADER DESCRIPTOR AND ANY BUFFER SEGMENT
;DESCRIPTORS LINKED TO THE BHD.
;CALL:
;	T1/ BUFFER NAME
;	PUSHJ	P,PPDRHD
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T4/ CONTENTS OF .BHKEY WORD FROM BHD

PPDRHD::SE1ENT			;ENSURE WE RUN IN NZS
	PUSHJ	P,PPDRBH	;RETURN THE BHD
	PUSH	P,T4		;SAVE THE KEY WORD FOR RETURN TO CALLER
	SKIPA	T4,T2		;COPY ADDRESS OF FIRST BSD TO T4 AND SKIP
PPDRH1:	PUSHJ	P,PPDRBD	;RETURN THE BSD
	SKIPN	T1,T4		;IS THERE A NEXT BSD?
	JRST	T4POPJ##	;NO, DONE
	MOVE	T2,T1		;COPY ADDRESS
	ADDI	T2,.BSNXT	;OFFSET TO WORD WE WANT
	PUSHJ	P,PHMOV##	;FETCH THE LINK
	MOVE	T4,T2		;COPY NEXT BSD ADDRESS TO T4
	JRST	PPDRH1		;RETURN THIS BSD
	SUBTTL	KLIPA CONFIGURATION


;ROUTINE CALLED FROM AUTCON WHEN A KLIPA IS DETECTED.
;CALL:
;	T1/ PCB ADDRESS
;	P1/ CHANNEL DATA BLOCK ADDRESS
;	PUSHJ	P,KLPCFG
;RETURN:
;	CPOPJ ALWAYS

KLPCFG::PUSHJ	P,SAVQ##	;SAVE THE "Q" REGISTERS
	SE1ENT			;ENTER NON-ZERO SECTION
	MOVE	Q3,T1		;COPY PCB ADDRESS TO STANDARD REGISTER
	MOVEM	P1,.PCCDB(Q3)	;SAVE CHANNEL DATA BLOCK ADDRESS IN PCB
IFN FTMP,<
	MOVE	T1,.CPCPN##	;GET OUR CPU NUMBER
	MOVEM	T1,.PCCPU(Q3)	;SAVE FOR QUEUED I/O TESTS IN KLPSND
>; END IFN FTMP
	MOVEI	T1,DSKCHN##	;GET PI CHANNEL KLIPA WILL RESIDE ON
	MOVEM	T1,.PCPIA(Q3)	;STORE IN PCB
	MOVX	T1,C%MGSZ	;GET MAXIMUM MESSAGE SIZE
	MOVEM	T1,.PCMQE(Q3)	;STORE FOR THE KLIPA
	MOVX	T1,C%DGSZ	;GET MAXIMUM DATAGRAM SIZE
	MOVEM	T1,.PCDQE(Q3)	;STORE FOR THE KLIPA
;	MOVX	T1,C%RGSZ	;GET MAXIMUM RESERVED SIZE
;	MOVEM	T1,.PCRQE(Q3)	;STORE FOR THE KLIPA
	MOVEI	T1,.ULLEN	;NUMBER OF WORDS IN MICROLOADER PARAMETER BLOCK
	XMOVEI	T2,KLPULB	;ADDRESS OF PROTOTYPE BLOCK
	XMOVEI	T3,.PCULB(Q3)	;ADDRESS OF DESTINATION BLOCK IN PCB
	EXTEND	T1,[XBLT]	;COPY PROTOTYPE TO OUR PCB
	MOVE	T1,[KLPBTS]	;BITS TO TEST FOR ON INTERRUPT
	MOVEM	T1,CHNBTS##(P1)	;STORE IN CHANNEL DATA BLOCK
	PJRST	PCBINI		;INITIALIZE QUEUE STRUCTURES IN PCB AND RETURN
	SUBTTL	KLIPA INITIALIZATION


;ROUTINE TO INITIALIZE THE KLIPA ON THIS CPU.  MUST BE CALLED
;AFTER DSKCHN PI CHANNEL AND PI SYSTEM ARE TURNED ON.
;CALL:
;	PUSHJ	P,PPDINX
;RETURN:
;	CPOPJ ALWAYS

PPDINX::PUSHJ	P,SAVPQ##	;SAVE LOTS OF AC'S
	SE1ENT			;ENTER NON-ZERO SECTION
	SKIPE	P1,.CPCHN##+KLPICH ;GET ADDRESS OF CHANNEL DATA BLOCK
	SKIPN	Q3,CHNPCB##(P1)	;GET ADDRESS OF PORT CONTROL BLOCK
	POPJ	P,		;NO KLIPA ON THIS CPU
	SKIPE	.CPPCB##	;RESTART?
	JRST	[PUSHJ P,PCBINI	;YES, INITIALIZE THE PCB
		 JRST  PPDIN3]	;JOIN UP LATER ON
	MOVEM	Q3,.CPPCB##	;STORE PCB ADDRESS IN CPU DATA BLOCK
	PUSHJ	P,PCBINI	;INITIALIZE THE PCB

;STOCK THE DATAGRAM FREE QUEUE

PPDIN3:	PUSHJ	P,STKDFQ	;DO IT

;ENABLE THE KLIPA BUT DON'T GIVE IT ITS PI ASSIGNMENT YET

	SETZM	.PCFQE(Q3)	;CLEAR COUNT OF FREE QUEUE ERRORS
	SETOM	.PCONN(Q3)	;WE DON'T KNOW OUR NODE NUMBER
	MOVE	T1,.CPCPN##	;GET OUR CPU NUMBER
	MOVE	T1,BITTBL##(T1)	;GET THAT BIT
	TDNN	T1,IPAMSK##	;WANT TO SKIP STARTING THE CI?
	PUSHJ	P,RLDKLI	;LOAD AND START KLIPA MICROCODE
	  POPJ	P,		;FAILED, SKIP REST OF INITIALIZATION

;EMPTY THE RESPONSE QUEUE AND RETURN THE PACKET(S) TO THE FREE QUEUE(S)

PPDIN4:	PUSHJ	P,CLEANQ	;CLEAN OUT SOME RESPONSES
	  SKIPA			;NONE FOUND, DONE
	JRST	PPDIN4		;FOUND SOME, LOOK FOR MORE
;ENABLE INTERRUPTS AND CLEAN UP ANY RESPONSES THAT COULD HAVE COME IN
;AFTER THE QUEUE WAS CLEANED UP

	CONO	PI,PI.OFF	;STOP INTERRUPTS
	PUSHJ	P,PIAKLP	;GIVE THE KLIPA A PI ASSIGNMENT
	CONSZ	KLP,CI.RQA	;IS A RESPONSE AVAILABLE?
	JRST	[CONO KLP,CO.BTS+CO.RQA ;YES, CLEAR PI AND "RESPONSE AVAILABLE"
		 CONO PI,PI.ON	;ENABLE INTERRUPTS
		 JRST PPDIN4]	;GO TOSS THE RESPONSES
	CONO	PI,PI.ON	;ENABLE INTERRUPTS

;FINISH INITIALIZATION STUFF USING COMMON ROUTINE

	PJRST	PPDCSS		;DO COMMON STARTUP STUFF
;ROUTINE TO REMOVE PACKETS FROM THE RESPONSE QUEUE AND RETURN
;THEM TO THE APPROPRIATE FREE QUEUE.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CLEANQ
;RETURN:
;	CPOPJ IF DIDN'T FIND ANY
;	CPOPJ1 IF FOUND SOME

CLEANQ:	STKVAR	<QEMPTY>	;ALLOCATE A WORD OF STACK STORAGE
	SETZM	QEMPTY		;AND ZERO IT
CLENQ1:	CONO	KLP,CO.BTS!CO.RQA!CO.FQE ;CLEAR RESPONSE QUEUE AVAILABLE, FREE QUEUE ERR
CLENQ2:	XMOVEI	T1,.PCRSQ(Q3)	;RESPONSE QUEUE
	PUSHJ	P,REMQUE	;REMOVE THE TOP PACKET
	  JRST	CLENQ3		;EMPTY, WE'RE DONE
	AOS	QEMPTY		;FOUND ONE, COUNT IT
	PUSHJ	P,RMASAG	;BYTE SWAP THE PPD BYTE
	PUSHJ	P,TOSSIT	;RETURN IT TO THE PROPER FREE QUEUE
	JRST	CLENQ2		;LOOP FOR MORE

CLENQ3:	CONSZ	KLP,CI.RQA	;SOMETHING SHOW UP WHILE WE WERE WORKING?
	JRST	CLENQ1		;YES, GO GET IT
	SKIPE	QEMPTY		;DID WE GET ANY RESPONSES?
	AOS	(P)		;YES, SET FOR SKIP RETURN
	POPJ	P,		;RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE


;ROUTINE TO RETURN A PACKET TO THE PROPER FREE QUEUE.
;CALL:
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,TOSSIT
;RETURN:
;	CPOPJ ALWAYS

TOSSIT:	MOVX	T1,PK.SRB	;CLEAR ALL SOFTWARE RESPONSE BITS
	ANDCAM	T1,.PKVRT(Q2)	;...
	LDB	T1,PKYOP	;OP CODE
	TXZ	T1,OP.RMT	;MINUS REMOTE BIT
	CAIE	T1,OP.SMS	;IS IT A MESSAGE?
	PJRST	RETDG		;NO, RETURN THE DATAGRAM
	PJRST	RETMSG		;RETURN THE MESSAGE
	SUBTTL	PCB INITIALIZATION


;ROUTINE TO INITIALIZE A PCB.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,PCBINI
;RETURN:
;	CPOPJ ALWAYS
;
;NOTE:  PCBINI SHOULD INITIALIZE ANY INFORMATION IN THE PCB WHICH
;MIGHT CHANGE IF THE PCB IS MOVED IN PHYSICAL MEMORY.  CONSTANTS
;(SUCH AS MAXIMUM DG/MSG SIZE) SHOULD BE SET AT PPDINX.

PCBINI:	SKIPN	T1,BHDADR	;GET PHYSICAL ADDRESS OF BUFFER DESCRIPTOR TABLE
	BUG.	(HLT,KLPNBD,KLPSER,SOFT,<No buffer descriptor table>,,)
	MOVEM	T1,.PCBDT(Q3)	;STORE FOR THE KLIPA
	MAP	T1,0(Q3)	;DETERMINE PHYSICAL ADDRESS OF PCB
	TXZ	T1,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVEM	T1,.PCPBA(Q3)	;STORE PHYSICAL PCB ADDRESS FOR THE KLIPA
	MOVE	T2,Q3		;GET VIRTUAL ADDRESS OF PCB
	SUBM	T2,T1		;COMPUTE PHYSICAL-VIRTUAL OFFSET
	MOVEM	T1,.PCVPO(Q3)	;SAVE VIRTUAL-PHYSICAL OFFSET
	MOVX	T1,ST.DED	;INITIALLY IT IS ASSUMED TO BE DEAD
	IORM	T1,.PCSTS(Q3)	; WILL GET CLEARED WHEN RESTARTED
	MOVE	T1,.CPEPT##	;GET ADDRESS OF EPT
	MAP	T1,KLPICH*4+.CSCLP(T1) ;GET PHYSICAL ADDRESS OF CHANNEL LOGOUT WORD 1
	TXZ	T1,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVEM	T1,.PCAL1(Q3)	;STORE IT FOR THE KLIPA
	PJRST	RSTQS		;RESET PCB QUEUES AND RETURN
	SUBTTL	INTERRUPT SERVICE


;DISPATCH HERE FROM THE CONSO SKIP CHAIN ON A KLIPA INTERRUPT.
;THE SKIP CHAIN CODE HAS ALREADY SAVED ALL ACS, AND LOADED T1
;WITH THE CONI STATUS BITS AND T2 WITH THE ADDRESS OF THE PORT
;CONTROL BLOCK.

KLPINT::SE1ENT			;NEED TO RUN IN NZS
	MOVE	Q3,T2		;COPY PCB ADDRESS TO "STANDARD" REGISTER
	MOVEM	T1,.PCCSR(Q3)	;SAVE CONI STATUS FOR LATER EXAMINATION
	MOVX	T2,ST.MAI	;SEE IF IN MAINTENANCE MODE
	TDNE	T2,.PCSTS(Q3)	;...
	POPJ	P,		;YES, DON'T GET IN THE WAY
	TXNN	T1,CI.CPE!CI.MER ;CRAM PARITY ERROR OR MBUS ERROR?
	JRST	KLPIN1		;NO
	PUSHJ	P,REPORT	;MAKE ERROR.SYS ENTRY
	MOVX	T1,CI.CPE	;GET CRAM PARITY ERROR BIT
	TDNE	T1,.PCCSR(Q3)	;CRAM PARITY ERROR?
	PUSHJ	P,KLECPE	;OUTPUT DESCRIPTIVE TEXT
	PUSHJ	P,NONODS	;TELL SCA ABOUT NODES GOING AWAY
	PUSHJ	P,RSTLKS	;RESET THE PCB QUEUE INTERLOCKS
	PUSHJ	P,RSTRID	;RESET REQUEST-ID DATA
	MOVX	T1,CI.CPE	;GET CRAM PARITY ERROR BIT
	TDNE	T1,.PCCSR(Q3)	;CRAM PARITY ERROR?
	PUSHJ	P,KLPCPD	;YES (KLPCPD MAY CALL KLPRQC)
	MOVX	T1,CI.MER	;GET MBUS ERROR BIT
	TDNE	T1,.PCCSR(Q3)	;MBUS ERROR?
	PUSHJ	P,KLPMBD	;YES (KLPRQC WON'T BE CALLED)
	POPJ	P,		;DISMISS THE INTERRUPT

;NEITHER A CRAM PARITY ERROR OR AN MBUS ERROR

KLPIN1:	TXNE	T1,CI.RQA	;RESPONSE QUEUE AVAILABLE?
	PUSHJ	P,KLPRQA	;YES, PROCESS THE PACKETS
	MOVX	T1,CI.FQE	;GET FREE QUEUE ERROR BIT
	TDNE	T1,.PCCSR(Q3)	;FREE QUEUE ERROR?
	PUSHJ	P,KLPFQE	;YES, FIND OUT WHICH ONE
	POPJ	P,		;DISMISS THE INTERRUPT
;ROUTINE CALLED WHEN RESPONSE QUEUE IS AVAILABLE.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,KLPRQA
;RETURN:
;	CPOPJ ALWAYS

;COMMON USE OF THE ACS IN KLPRQA:
;	Q1/ CI NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	P1/ PCB ADDRESS OFFSET BY CI NODE NUMBER
;	P2/ OPCODE FROM PACKET
;	P3/ -1 IF LOCALLY GENERATED, 0 IF REMOTELY GENERATED
;	P4/ SBK ADDRESS
;	P5/ PBK ADDRESS
;	P6/ PPD BYTE

KLPRQC:	CONO	KLP,CO.EPE!CO.RQA ;CLEAR RESPONSE AVAILABLE, EBUS PARITY
	JRST	KLPRQ1		;JOIN COMMON CODE

KLPRQA:	MOVE	T1,.PCPIA(Q3)	;GET KLIPA PI ASSIGNMENT
	CONO	KLP,CO.EPE!CO.RQA!CO.BTS(T1) ;CLEAR RESPONSE AVAILABLE, EBUS PARITY
KLPRQ1:	XMOVEI	T1,.PCRSQ(Q3)	;WHICH QUEUE
	PUSHJ	P,REMQUE	;REMOVE FIRST PACKET FROM QUEUE
	  POPJ	P,		;QUEUE IS EMPTY, DISMISS INTERRUPT AND RETURN

	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	MOVEM	T1,.PCKRT(Q3)	;REMEMBER WHEN LAST PACKET WAS RECEIVED
	PUSHJ	P,RMASAG	;SWAP THE PPD BYTE AND ADJUST LENGTH
	LDB	Q1,PKYNOD	;GET CI NODE NUMBER
	CAIL	Q1,0		;A LEGAL CI
	CAIL	Q1,MAXNDS	; NODE NUMBER?
	JRST	KLPRQ6		;NODE NUMBER OUT OF RANGE
	MOVE	P1,Q3		;GET PCB ADDRESS
	ADD	P1,Q1		; OFFSET FOR THIS NODE
	LDB	P2,PKYOP	;GET OP CODE
	TXZE	P2,OP.RMT	;REMOTELY GENERATED?
	TDZA	P3,P3		;YES, CLEAR P3 (LOCALLY GENERATED FLAG) AND SKIP
	SETO	P3,		;NO, SAY LOCALLY GENERATED
	MOVE	P4,.PCSBK(P1)	;GET THE SYSTEM BLOCK ADDRESS
	MOVE	P5,.PCPBK(P1)	;GET THE PATH BLOCK ADDRESS
;HERE TO DISPATCH THE PACKET BASED ON OP CODE (IN P2)

	MOVSI	T2,-KLPOPL	;GET LENGTH OF TABLE FOR OPCODE SEARCH
KLPRQ2:	CAMN	P2,KLPOPS(T2)	;THIS THE ONE?
	JRST	KLPRQ3		;YES, GO TO IT
	AOBJN	T2,KLPRQ2	;NO, TRY NEXT ONE
	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	BUG.	(INF,KLPOPC,KLPSER,SOFT,<Packet with bad opcode>,<<P2,OPCODE>,<T1,STATUS>,<T2,FLAGS>,<Q1,NODE>>,)
	PUSHJ	P,RETMSG	;RETURN PACKET TO MESSAGE FREE QUEUE
	JRST	KLPRQ1		;TRY FOR ANOTHER

;ALL ACS ARE SET UP AT THIS POINT EXCEPT FOR THE PPD BYTE.
;T2 CONTAINS THE DISPATCH INDEX.

KLPRQ3:	SETO	P6,		;NO PPD BYTE
	MOVE	T1,.PKSTS(Q2)	;GET PACKET STATUS WORD
	TXNN	T1,PS.ERR!PS.CLO ;ERRORS OR PATH CLOSED?
	JRST	KLPRQ5		;NO
	TXNN	T1,PS.ERR	;YES, ERROR?
	JRST	KLPRQ4		;NO, PATH CLOSED
	PUSHJ	P,@KLPERB(T2)	;DO ERROR PROCESSING
	JRST	KLPRQ1		;GO FOR NEXT PACKET

KLPRQ4:	PUSH	P,T2		;SAVE INDEX INTO DISPATCH TABLES
	PUSHJ	P,CLOPTH	;PATH CLOSED, UPDATE PATH INFO
	POP	P,T2		;RESTORE DISPATCH INDEX
	MOVX	T1,PK.SRB	;WERE WE EXPECTING A RESPONSE?
	TDNE	T1,.PKVRT(Q2)	;...
	JRST	KLPRQ5		;YES
	PUSHJ	P,@KLPRET(T2)	;RETURN THE PACKET
	JRST	KLPRQ1		;GO FOR NEXT PACKET

KLPRQ5:	PUSHJ	P,@KLPROU(T2)	;PROCESS THE PACKET
	JRST	KLPRQ1		;GO FOR NEXT PACKET


;THE CI NODE NUMBER IN THE RECEIVED PACKET IS BAD

KLPRQ6:	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	LDB	T3,PKYOP	;GET OPCODE
	BUG.	(INF,KLPNDE,KLPSER,SOFT,<Packet with bad node number>,<<Q1,NODE>,<T1,STATUS>,<T2,FLAGS>,<T3,OPCODE>>,)
	PUSHJ	P,TOSSIT	;RETURN TO APPROPRIATE FREE QUEUE
	JRST	KLPRQ1		;TRY FOR ANOTHER RESPONSE
;ROUTINE TO PROCESS A PACKET RECEIVED WITH THE PATH CLOSED BIT SET.
;CALL:
;	T1/ PACKET STATUS WORD
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	P1/ PCB ADDRESS OFFSET BY CI NODE NUMBER

CLOPTH:	PUSHJ	P,SAVQ##	;SAVE REGISTERS WHICH WILL GET STEPPED ON
	STKVAR	<AFLAG>		;ALLOCATE A WORD OF STACK STORAGE
	SETZM	AFLAG		;ASSUME ACKED ON PATH A
	TXNE	T1,PS.AKA	;ACKED ON PATH A?
	SETOM	AFLAG		;NO, REMEMBER THAT
	MOVX	T1,RI.PBO	;ASSUME ACKED ON PATH A (I.E., PATH B CLOSED)
	SKIPE	AFLAG		;RIGHT GUESS?
	MOVX	T1,RI.PAO	;NO, SO PATH A WAS CLOSED
	ANDCAM	T1,.PCRIS(P1)	;CLEAR THE BIT
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;CAN'T, MUST WAIT FOR POLLER
	MOVX	T3,PF.PT0	;ASSUME PATH A WAS CLOSED
	SKIPE	AFLAG		;WAS IT?
	MOVX	T3,PF.PT1	;NO, PATH B
	PJRST	KLPRID		;SEND A REQUEST-ID AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
;MACRO TO DEFINE OPCODE AND CORRESPONDING DISPATCH ROUTINE.
;ALSO INCLUDES DISPATCH FOR PACKETS WITH ERRORS AND THE
;PATH CLOSED BIT ON.

DEFINE	OPS,<

DISP	OP.SMS,GIVSCA,GIVERR,RETMSG	;SEND MESSAGE
DISP	OP.SDG,INTDG,DGERR,RETDG	;SEND DATAGRAM
DISP	OP.RCF,INTNBF,NBFERR,RETMSG	;DATA CONFIRM RECEIVED
DISP	OP.RDT,INTNBF,NBFERR,RETMSG	;RETURN DATA (DATREC)
DISP	OP.RID,INTRID,RIDERR,RETDG	;REQUEST-ID
DISP	OP.IDR,INTIDR,RETDG,RETDG	;ID RECEIVED
DISP	OP.LPB,INTLPB,LPBERR,RETDG	;SEND/RECEIVE LOOPBACK
DISP	OP.RD1,INTRD1,RD1ERR,RETMSG	;REQUEST DATA ON COMMAND QUEUE 1
DISP	OP.CKT,INTCKT,CKTERR,RETDG	;SET VIRTUAL CIRCUIT
DISP	OP.RCT,INTRCT,RCTERR,RETDG	;READ STATISTICS COUNTERS
DISP	OP.MCR,INTMCR,MCRERR,RETDG	;MAINTENANCE CONFIRM RECEIVED
DISP	OP.MDR,INTMDR,MDRERR,RETDG	;MAINTENANCE DATA RECEIVED
DISP	OP.RRG,INTRRG,RRGERR,RETDG	;READ REGISTER
DISP	OP.RD0,INTRD0,RD0ERR,RETMSG	;REQUEST DATA ON COMMAND QUEUE 0
DISP	OP.RD2,INTRD2,RD2ERR,RETMSG	;REQUEST DATA ON COMMAND QUEUE 2
DISP	OP.SDT,INTSDT,SDTERR,RETMSG	;SEND DATA
DISP	OP.RRS,RETDG,RETDG,RETDG	;RESET REMOTE SYSTEM
DISP	OP.SRS,RETDG,RETDG,RETDG	;START REMOTE SYSTEM
DISP	OP.RMD,RETDG,RETDG,RETDG	;REQUEST MAINTENANCE DATA
DISP	OP.SMD,RETDG,RETDG,RETDG	;SEND MAINTENANCE DATA
DISP	OP.SPT,RETDG,RETDG,RETDG	;SET STATISTICS COUNTERS
DISP	OP.WRG,RETDG,RETDG,RETDG	;WRITE REGISTER
DISP	OP.CLB,INTCLB,CLBERR,RETDG	;CLOSE BUFFER

>; END DEFINE OPS
;GENERATE OPCODE LOOKUP TABLE

DEFINE	DISP(CODE,ROU,ERB,RET),<
	CODE
>; END DEFINE DISP

KLPOPS:	OPS			;GENERATE OPCODE LOOKUP TABLE
KLPOPL==.-KLPOPS		;LENGTH OF TABLE

;GENERATE PARALLEL DISPATCH TABLE

DEFINE	DISP(CODE,ROU,ERB,RET),<
	IFIW	ROU
>; END DEFINE DISP

KLPROU:	OPS			;GENERATE DISPATCH TABLE

;GENERATE PARALLEL DISPATCH TABLE FOR PACKETS WITH ERROR BIT ON

DEFINE	DISP(CODE,ROU,ERB,RET),<
	IFIW	ERB
>; END DEFINE DISP

KLPERB:	OPS

;GENERATE PARALLEL DISPATCH TABLE FOR PACKETS WITH ERROR BIT OFF
;BUT THE CLOSED PATH BIT ON AND NO RESPONSE WAS REQUESTED

DEFINE	DISP(CODE,ROU,ERB,RET),<
	IFIW	RET
>; END DEFINE DISP

KLPRET:	OPS
;RECEIVED EITHER AN APPLICATION DATAGRAM OR AN APPLICATION MESSAGE

GIVSCA:	JUMPN	P5,GIVSC1	;PROCEED IF HAVE A PATH BLOCK
	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	BUG.	(INF,KLPIPA,KLPSER,SOFT,<Invalid packet arrived>,<<T1,STATUS>,<T2,FLAGS>,<P2,OPCODE>,<Q1,NODE>>,)
	CAIN	P2,OP.SDG	;A DATAGRAM?
	PJRST	RETDG		;YES, RETURN TO DATAGRAM FREE QUEUE
	PJRST	RETMSG		;NO, RETURN TO MESSAGE FREE QUEUE

GIVSC1:	JUMPN	P3,GV2SCA	;JUMP IF LOCALLY GENERATED
	LOAD	T1,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	CAIE	T1,VC.STR	;START-RECEIVED?
	JRST	GV2SCA		;NO
	SETZM	.PBSST(P5)	;TURN OFF START SEQUENCE TIMER
	PUSHJ	P,OPNSCA	;TELL SCA ABOUT NEW NODE
;FALL INTO GV2SCA
;HERE TO ACTUALLY HAND THE PACKET TO SCA.
;CALL:
;	Q2/ PACKET ADDRESS
;	P3/ FLAGS
;	P5/ PBK ADDRESS

GV2SCA:	MOVX	T1,PK.SCA	;SCA REQUEST A RESPONSE?
	TDNN	T1,.PKVRT(Q2)	;...
	JRST	GV2SC1		;NO
	SKIPL	P3		;YES, THIS SHOULD BE LOCALLY GENERATED, THEN
	BUG.	(INF,KLPIRP,KLPSER,HARD,<Software response bit on in remotely-generated packet>,<<Q1,NODE>,<T1,STATUS>>,)
	MOVX	P3,F.RSP	;TELL SCA THIS IS A RETURNED BUFFER
	MOVX	T1,PK.SRB	;CLEAR ALL SOFTWARE RESPONSE BITS
	ANDCAM	T1,.PKVRT(Q2)	;...
	JRST	GV2SC2		;PROCEED

GV2SC1:	JUMPE	P3,GV2SC2	;NO RESPONSE REQUESTED, THIS SHOULD BE REMOTE THEN

;THE PORT SHOULDN'T HAVE GIVEN US THIS BUFFER.  SCASER DOESN'T
;WANT IT, SO PUT IT BACK ON THE FREE QUEUE.

	BUG.	(INF,KLPILP,KLPSER,HARD,<Software response bit off in locally-generated packet>,<<Q1,NODE>,<T1,STATUS>>,)
	CAIN	P2,OP.SDG	;IS THIS A DATAGRAM?
	PJRST	RETDG		;YES, RETURN TO DATAGRAM FREE QUEUE
	PJRST	RETMSG		;NO, RETURN TO MESSAGE FREE QUEUE

GV2SC2:	MOVE	T1,.PKSTS(Q2)	;GET STATUS FLAGS
	LDB	T2,PKYLEN	;GET THE PACKET LENGTH
	TXNN	T1,PF.FMT	;HIGH DENSITY?
	JRST	GV2SC3		;NO
	TXO	P3,F.SPM	;YES, SET FLAG
	MOVEI	T2,3(T2)	;ROUND UP BYTE COUNT IN CASE OF PARTIAL WORD
	IMULI	T2,2		;CONVERT BYTE COUNT TO WORD COUNT
	IDIVI	T2,^D9		; (BYTE COUNT/4.5) = WORD COUNT
GV2SC3:	LOAD	T1,PBPBI,(P5)	;GET THE PATH BLOCK INDEX
	BLCAL.	(SC.INT##,<T1,Q2,T2,P3>) ;TELL SCA
	POPJ	P,		;RETURN
;RECEIVED A PACKET FOR SCA WITH THE ERROR FLAG SET

GIVERR:	JUMPE	P3,GIVER1	;IF REMOTELY-GENERATED, WE'RE DONE
	MOVX	T1,PK.SCA	;WAS A RESPONSE REQUESTED?
	TDNE	T1,.PKVRT(Q2)	;...
	JRST	GIVER2		;NO

;ERROR IN A REMOTELY-GENERATED PACKET, OR IN A LOCALLY-GENERATED
;PACKET AND NO RESPONSE WAS REQUESTED

GIVER1:	CAIN	P2,OP.SDG	;IS IT A DATAGRAM?
	PJRST	RETDG		;YES, RETURN IT TO FREE QUEUE
	PUSHJ	P,SCAERR	;NO, A MESSAGE, REPORT THE FAILURE
	PJRST	RETMSG		;RETURN IT TO FREE QUEUE

;ERROR IN A LOCALLY-GENERATED PACKET AND A RESPONSE WAS REQUESTED

GIVER2:	CAIN	P2,OP.SMS	;IS IT A MESSAGE?
	PUSHJ	P,SCAERR	;YES, SPEAR ENTRY, TELL SCA
	PJRST	GV2SCA		;GIVE THE PACKET TO SCA
;WE GOT A PACKET FOR SCA WITH AN ERROR AND IT WILL CLOSE AN OPEN VC.
;CALL:
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	P5/ PBK ADDRESS

SCAERR:	LOAD	T1,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	CAIE	T1,VC.OPN	;OPEN?
	POPJ	P,		;NO, DON'T NEED TO REPORT ANYTHING
	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	LDB	T3,PKYOP	;GET OPCODE
	BUG.	(INF,KLPERR,KLPSER,SOFT,<Received packet with error>,<<T1,STATUS>,<T2,FLAGS>,<T3,OPCODE>,<Q1,NODE>>,)
	SAVEAC	(Q2)		;PRESERVE EXISTING PACKET ADDRESS
	SETZB	Q2,P4		;WE NEED A BUFFER TO DO THE SET CIRCUIT
	PJRST	CLOSV1		;CLOSE THE VC, TELL SCA, AND RETURN
;RECEIVED A DATAGRAM

INTDG:	PUSHJ	P,CHKPPD	;CHECK THE PPD BYTE
	  PJRST	RETDG		;BAD PPD, RETURN PACKET TO FREE QUEUE AND RETURN
	PJRST	@DGDSP(P6)	;PROCESS THE PACKET AND RETURN

DGDSP:	IFIW	INTSTR		;START
	IFIW	INTSTK		;STACK
	IFIW	INTACK		;ACK
	IFIW	GIVSCA		;APPLICATION DATAGRAM
	IFIW	CHKPP1		;APPLICATION MESSAGE (OPCODE/PPD BYTE MISMATCH)
	IFIW	INTERP		;ERROR PACKET
	IFIW	INTSHT		;SHUTDOWN


;RECEIVED A DATAGRAM WITH AN ERROR

DGERR:	PUSHJ	P,CHKPPD	;CHECK THE PPD BYTE
	  PJRST	RETDG		;BAD PPD, RETURN PACKET TO FREE QUEUE AND RETURN
	PJRST	@DGEDSP(P6)	;PROCESS THE PACKET AND RETURN

DGEDSP:	IFIW	RETDG		;START
	IFIW	RETDG		;STACK
	IFIW	RETDG		;ACK
	IFIW	GIVERR		;APPLICATION DATAGRAM
	IFIW	CHKPP1		;APPLICATION MESSAGE (OPCODE/PPD BYTE MISMATCH)
	IFIW	ERPERR		;ERROR PACKET
	IFIW	RETDG		;SHUTDOWN WHICH FAILED
;HERE ON RECEIPT OF A START DATAGRAM

INTSTR:	JUMPN	P5,INTSR1	;MUST HAVE BOTH A SYSTEM BLOCK AND PATH BLOCK
				; TO HANDLE THE START
	PUSHJ	P,CHKIDT	;REQUEST-ID TIMER RUNNING?
	  PJRST	RETDG		;YES, ONE OUTSTANDING IS ENOUGH
	PUSHJ	P,GTCPTH	;GET CURRENT PATH
	PJRST	KLPRID		;SEND A REQUEST-ID IN THIS PACKET AND RETURN

INTSR1:	PUSHJ	P,FILLSB	;FILL IN THE SYSTEM BLOCK
	LOAD	T1,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	PJRST	@STRDSP(T1)	;GO DO THE WORK

STRDSP:	IFIW	RETDG		;CLOSED
	IFIW	STRSSN		;START-SENT
	IFIW	STRSTR		;START-RECEIVED
	IFIW	STROPN		;OPEN


;ROUTINE TO COPY DATA FROM THE START PACKET TO THE SYSTEM BLOCK.

FILLSB:	MOVEI	T1,.SBBLE-.SBDSA ;AMOUNT OF DATA TO MOVE
	XMOVEI	T2,.SRSSY(Q2)	;FROM THE PACKET
	XMOVEI	T3,.SBDSA(P4)	; TO THE SYSTEM BLOCK
	EXTEND	T1,[XBLT]	;FILL IN THE DATA
	MOVE	T1,.SBMMS(P4)	;GET MAXIMUM MESSAGE SIZE
	PUSHJ	P,REVFUL	;REVERSE THE BYTES TO MAKE THEM MEANINGFUL
	MOVEM	T1,.SBMMS(P4)	;PUT IT BACK
	POPJ	P,		;RETURN
;RECEIVED A START WHEN IN EITHER START-SENT OR START-RECEIVED STATE.
;RESET THE TIMER, NEW STATE IS START-RECEIVED, AND SEND A STACK.

STRSSN:	PUSHJ	P,KLPOPN	;TELL PORT TO OPEN ITS CIRCUIT
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	STRST1		;NO BIG DEAL, JUST LIKE THE STACK GETTING LOST
STRSTR:	MOVEI	T1,PP.STK	;GET STACK CODE
	PUSHJ	P,STRTDT	;MAKE A STACK
	PUSHJ	P,KLPSDG	;SEND IT
STRST1:	MOVEI	T1,VC.STR	;GET START-RECEIVED CODE
	STOR	T1,PBVCST,(P5)	;UPDATE STATE
	PJRST	STSST		;SET TIMER AND RETURN


;RECEIVED A START WHEN IN OPEN STATE.
;CLOSE VC AND TELL SCA OF THE ERROR.

STROPN:	BUG.	(INF,KLPSWO,KLPSER,SOFT,<Received a START when VC was open>,<<Q1,NODE>>,)
	SETZ	P4,		;WE NEED TO SEND A SETCKT
	PJRST	CLOSV1		;CLOSE THE VC AND TELL SCA
;RECEIVED A STACK

INTSTK:	PUSHJ	P,FILLSB	;FILL IN THE SYSTEM BLOCK
	LOAD	T1,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	PJRST	@STKDSP(T1)	;GO DO THE WORK

STKDSP:	IFIW	RETDG		;CLOSED
	IFIW	STKSTS		;START-SENT
	IFIW	STKSTR		;START-RECEIVED
	IFIW	STKOPN		;OPEN


;RECEIVED A STACK WHEN IN START-SENT OR START-RECEIVED STATE.
;STOP TIMER, SEND ACK, NEW STATE IS OPEN.

STKSTS:	PUSHJ	P,KLPOPN	;TELL PORT TO OPEN ITS CIRCUIT
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	STKST1		;NO SWEAT, JUST LIKE ACK GETTING LOST
STKSTR:	MOVEI	T1,PP.ACK	;GET ACK CODE
	DPB	T1,PKYPPD	;STORE PPD BYTE
	PUSHJ	P,KLPSDG	;SEND THE DATAGRAM
STKST1:	SETZM	.PBSST(P5)	;TURN OFF THE TIMER
	PJRST	OPNSCA		;TELL SCA ABOUT THE NEW PATH


;RECEIVED A STACK WHEN IN OPEN STATE.  SEND AN ACK.

STKOPN:	MOVEI	T1,PP.ACK	;GET ACK CODE
	DPB	T1,PKYPPD	;PUT IT IN
	PJRST	KLPSDG		;SEND IT AND RETURN
;RECEIVED AN ACK

INTACK:	LOAD	T1,PBVCST,(P5)	;GET STATE OF VC
	PJRST	@ACKDSP(T1)	;GO DO THE WORK

ACKDSP:	IFIW	RETDG		;CLOSED
	IFIW	RETDG		;START-SENT
	IFIW	ACKSTR		;START-RECEIVED
	IFIW	RETDG		;OPEN

;RECEIVED AN ACK WHEN IN START-RECEIVED STATE.  STOP TIMER AND
;SET NEW STATE TO OPEN.

ACKSTR:	SETZM	.PBSST(P5)	;TURN OFF THE START SEQUENCE TIMER
	PJRST	OPNSCA		;TELL SCA ABOUT THE NEW PATH
;RECEIVED AN ERROR LOG PACKET

INTERP:	LDB	P4,PKYLEN	;GET LENGTH OF PACKET IN BYTES
	ADDI	P4,3		;ROUND ODD BYTES TO AN EVEN NUMBER OF WORDS
	LSH	P4,-2		;...
	ADDI	P4,KE%LEN	;INCLUDE THE ADDITIONAL WORD(S) WE SUPPLY
	MOVE	T1,P4		;COPY LENGTH REQUIRED TO T1
	PUSHJ	P,ALCSEB##	;ALLOCATE A SYSTEM ERROR BLOCK
	  PJRST	RETDG		;NOT AVAILABLE, JUST TOSS PACKET
	MOVEI	T2,SEC%KE	;GET THE BLOCK TYPE
	DPB	T2,[POINT 9,.EBTYP(T1),8] ;STORE IN HEADER
	MOVE	T2,Q1		;COPY THE SOURCE NODE NUMBER
	TXO	T2,FLD(KLPICH,KE%CHN) ;INCLUDE INTERNAL CHANNEL NUMBER
	MOVEM	T2,.EBHDR+KE%SRC(T1) ;STORE IN BODY
	MOVEI	T2,-KE%ELG(P4)	;NUMBER OF WORDS TO MOVE (EXCLUDING HEADER)
	XMOVEI	T3,.PKLEN+1(Q2)	;SOURCE
	MOVEI	T4,.EBHDR+KE%ELG(T1) ;DESTINATION
	EXTEND	T2,[XBLT]	;MOVE THE DATA
	PUSHJ	P,QUESEB##	;QUEUE THE ERROR BLOCK
	PJRST	RETDG		;RETURN THE PACKET UNTIL WE KNOW WHAT TO DO


;RECEIVED AN ERROR LOG PACKET WITH AN ERROR

ERPERR:	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	BUG.	(CHK,KLPEPB,KLPSER,SOFT,<Received bad error logging packet>,<<T2,STATUS>,<T3,FLAGS>,<P2,OPCODE>,<Q1,NODE>>,)
	PJRST	RETDG		;RETURN PACKET TO FREE QUEUE


;RECEIVED A SHUTDOWN

INTSHT:	BUG.	(INF,KLPRSH,KLPSER,SOFT,<Received shutdown message>,<<Q1,NODE>>,)
	SETZ	P4,		;WE NEED TO SEND A SETCKT
	PJRST	CLOSV1		;CLOSE THE VC, TELL SCA, AND RETURN
;ROUTINE TO TELL SCA A PATH HAS COME ON LINE (A VC IS NOW OPEN).
;CALL:
;	Q1/ NODE NUMBER
;	Q3/ PCB ADDRESS
;	P5/ PBK ADDRESS
;	PUSHJ	P,OPNSCA
;RETURN:
;	CPOPJ ALWAYS

OPNSCA:	MOVEI	T1,VC.OPN	;GET OPEN CODE
	STOR	T1,PBVCST,(P5)	;SET NEW VC STATE
	MOVX	T1,PB.OKO	;NO LONGER OK TO OPEN THE VC
	ANDCAM	T1,.PBFLG(P5)	;...
	LOAD	T1,PBPBI,(P5)	;GET PATH BLOCK INDEX
	BLCAL.	(SC.ONL##,<T1>)	;TELL SCA ABOUT NEW PATH
	POPJ	P,		;RETURN
;ROUTINE THE CHECK THE PPD BYTE FROM A RECEIVED PACKET.
;CALL:
;	Q2/ PACKET ADDRESS
;	PUSHJ	P,CHKPPD
;RETURN:
;	CPOPJ IF BAD PPD BYTE
;	CPOPJ1 IF GOOD PPD BYTE
;
;PPD BYTE IS RETURNED IN P6.

CHKPPD:	LDB	P6,PKYPPD	;GET THE PPD BYTE
	CAIL	P6,PP.STA	;LEGAL
	CAILE	P6,PP.MAX	; PPD BYTE?
	JRST	CHKPP1		;NO
	JRST	CPOPJ1##	;YES


;SPECIAL ENTRY POINT FOR OPCODE/PPD BYTE MISMATCH.

CHKPP1:	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYOP	;GET OPCODE
	LDB	T3,PKYNOD	;GET NODE
	BUG.	(INF,KLPPPD,KLPSER,SOFT,<Packet with bad PPD byte>,<<T1,STATUS>,<T2,OPCODE>,<T3,NODE>,<P6,PPD>>,)
	POPJ	P,		;RETURN
;RECEIVED A CONFIRM OR A DATA RECEIVED

INTNBF:	CAMN	P3,[-1]		;LOCALLY GENERATED?
	PJRST	RETMSG		;YES, GIVE PACKET BACK TO FREE QUEUE
	DMOVE	T1,.PKXID(Q2)	;T1 = BUFFER NAME, T2 = CID
	BLCAL.	(SC.DMA##,<T1,T2>) ;TELL SCA, GIVE BUFFER NAME AND CID
	MOVE	T1,Q2		;POSITION BUFFER ADDRESS
	PJRST	SC.RBF##	;RETURN THE BUFFER AND RETURN
;RECEIVED A REQUEST-ID

;NOTE:  WE ONLY GET HERE IF THE REQUEST-ID WE SENT WAS RETURNED DUE
;       TO SOME KIND OF ERROR, AS THE KLIPA RESPONDS TO IDREQ PACKETS
;       FROM OTHER NODES WITHOUT OUR INTERVENTION.

INTRID:	MOVX	T1,PK.SRB	;CLEAR ALL SOFTWARE RESPONSE BITS
	ANDCAM	T1,.PKVRT(Q2)	;...
	MOVX	T1,RI.WFR	;NO LONGER WAITING FOR RESPONSE
	ANDCAM	T1,.PCRIS(P1)	; (IN CASE IT WAS SECOND TRY)
	PJRST	RETDG		;RETURN THE PACKET TO FREE QUEUE AND RETURN


;RECEIVED A REQUEST-ID WITH AN ERROR

RIDERR:	PUSH	P,.PKSTS(Q2)	;SAVE THE ERROR BITS
	LDB	T1,PKYSTS	;GET THE STATUS FIELD
	PUSH	P,T1		;SAVE THAT TOO
	PUSHJ	P,INTRID	;CLEAR BITS AND RETURN BUFFER
	POP	P,T2		;RESTORE THE STATUS FIELD
	POP	P,T1		;GET THE ERROR BITS BACK
	SETZM	.PCRIT(P1)	;TURN OFF TIMER
	TXNN	T1,PF.PT0	;RECEIVED ON PATH A??
	JRST	RIDER2		;NO
	CAIE	T2,PS.NRA	;WAS IT A NO RESPONSE?
	JRST	RIDER1		;NO
	MOVX	T1,RI.NRA	;YES, UPDATE STATUS
	IORB	T1,.PCRIS(P1)	;...
	PJRST	RIDERX		;GO SEE IF WE SHOULD DO SOMETHING

RIDER1:	MOVX	T1,RI.NRA	;NO LONGER NO-RESPONSE
	ANDCAM	T1,.PCRIS(P1)	;...
	POPJ	P,		;RETURN

RIDER2:	CAIE	T2,PS.NRB	;WAS IT A NO RESPONSE?
	JRST	RIDER3		;NO
	MOVX	T1,RI.NRB	;YES, UPDATE STATUS
	IORB	T1,.PCRIS(P1)	;...
	PJRST	RIDERX		;GO SEE IF WE SHOULD DO SOMETHING

RIDER3:	MOVX	T1,RI.NRB	;NO LONGER NO-RESPONSE
	ANDCAM	T1,.PCRIS(P1)	;...
	POPJ	P,		;RETURN
;HERE WHEN WE'VE RECEIVED A NO RESPONSE.  IF WE'VE RECEIVED ONE ON
;BOTH PATHS WE'LL ASSUME THE LISTENER HAS DIED AND CLOSE THE VC.

RIDERX:	JUMPE	P5,CPOPJ##	;MAY NOT HAVE A PATH BLOCK (NON-EXISTANT NODE)
	TXC	T1,RI.NRA!RI.NRB ;COMPLEMENT THE BITS
	TXCE	T1,RI.NRA!RI.NRB ;BOTH ON?
	POPJ	P,		;NO, RETURN
	LOAD	T1,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	CAIE	T1,VC.OPN	;OPEN?
	POPJ	P,		;NO, DON'T NEED TO DO ANYTHING
	BUG.	(INF,KLPNRS,KLPSER,SOFT,<Closing VC due to No Response>,<<Q1,NODE>>,)
	SETZB	Q2,P4		;WE NEED A BUFFER TO DO THE SET CIRCUIT
	PJRST	CLOSV1		;CLOSE THE VC, TELL SCA, AND RETURN
;RECEIVED AN IDREC

INTIDR:	STKVAR	<WFIFL>		;ALLOCATE SOME STACK STORAGE
	SETZM	WFIFL		;ASSUME NOT WAITING FOR IDREC
	SETZM	.PCRIT(P1)	;TURN OFF REQUEST-ID TIMER FOR THIS NODE
	MOVE	T1,.PKSTS(Q2)	;GET THE PACKET STATUS
	TXNN	T1,PF.PT0	;DID IT ARRIVE ON PATH A?
	JRST	INTID3		;NO, PATH B
	PUSHJ	P,CHKVCO	;DO WE HAVE AN OPEN VC?
	  JRST	INTID2		;NO, ONWARD
	MOVX	T1,RI.PAO	;IS PATH A ALREADY OPEN?
	TDNE	T1,.PCRIS(P1)	;...
	JRST	INTID2		;YES
	PUSH	P,Q2		;SAVE ADDRESS OF IDREC PACKET
	PUSHJ	P,KLPGDB	;GET A BUFFER
	  JRST	INTID1		;NOT AVAILABLE
	MOVX	T1,CK.LPT!CK.PAA!CK.PBA ;TELL PORT TO USE BOTH PATHS AGAIN
	MOVEM	T1,.PKCKT(Q2)	;...
	PUSHJ	P,KLPSCK	;SEND THE SET-CIRCUIT
	MOVX	T1,RI.PAO!RI.PBO ;BOTH PATHS ARE NOW OPEN
	IORM	T1,.PCRIS(P1)	;...
INTID1:	POP	P,Q2		;RESTORE ADDRESS OF IDREC PACKET
INTID2:	MOVX	T1,RI.NRA	;CLEAR NO-RESPONSE FLAG
	ANDCAM	T1,.PCRIS(P1)	;...
	JRST	INTID6		;PROCEED

INTID3:	PUSHJ	P,CHKVCO	;DO WE HAVE AN OPEN VC?
	  JRST	INTID5		;NO, ONWARD
	MOVX	T1,RI.PBO	;IS PATH B ALREADY OPEN?
	TDNE	T1,.PCRIS(P1)	;...
	JRST	INTID5		;YES
	PUSH	P,Q2		;SAVE ADDRESS OF IDREC PACKET
	PUSHJ	P,KLPGDB	;GET A BUFFER
	  JRST	INTID4		;NOT AVAILABLE
	MOVX	T1,CK.LPT!CK.PAA!CK.PBA ;TELL PORT TO USE BOTH PATHS AGAIN
	MOVEM	T1,.PKCKT(Q2)	;...
	PUSHJ	P,KLPSCK	;SEND THE SET-CIRCUIT
	MOVX	T1,RI.PAO!RI.PBO ;BOTH PATHS ARE NOW OPEN
	IORM	T1,.PCRIS(P1)	;...
INTID4:	POP	P,Q2		;RESTORE ADDRESS OF IDREC PACKET
INTID5:	MOVX	T1,RI.NRB	;CLEAR NO-RESPONSE FLAG
	ANDCAM	T1,.PCRIS(P1)	;...
INTID6:	JUMPE	P5,INTID7	;JUMP IF NO PATH BLOCK (BUILD ONE)
	MOVE	T1,.PKPST(Q2)	;GET THE PORT STATE
	MOVEM	T1,.PBDPS(P5)	;STORE IT IN THE PBK
	LOAD	T1,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	CAIE	T1,VC.CLO	;CLOSED?
	PJRST	RETDG		;NO, THAT'S ALL WE HAVE TO DO
	MOVX	T1,PB.WFI	;NO LONGER WAITING FOR AN IDREC
	ANDCAB	T1,.PBFLG(P5)	;CLEAR FLAGS AND COPY TO T1
	SETOM	WFIFL		;SAY WE TURNED IT OFF
	TXNE	T1,PB.NTC	;STILL NEED TO CLOSE VC?
	PJRST	RETDG		;YES, CAN'T DO ANYTHING ELSE NOW
	TXNN	T1,PB.OKO	;IS IT OK TO OPEN THIS VC?
	PJRST	RETDG		;NO, DONE
INTID7:	LDB	T1,PKYPST	;GET OTHER PORT'S STATE
	CAIE	T1,PS.ENB	;ENABLED?
	PJRST	RETDG		;NO, DON'T TRY TO SEND NOW

	JUMPN	P4,INTID8	;JUMP IF WE HAVE A SYSTEM BLOCK FOR THIS NODE
	PUSHJ	P,BLDSBK	;BUILD A SYSTEM BLOCK
	  PJRST	RETDG		;CAN'T, RETURN THE DATAGRAM AND TRY AGAIN LATER
INTID8:	JUMPN	P5,INTID9	;JUMP IF WE HAVE A PATH BLOCK FOR THIS NODE
	PUSHJ	P,BLDPBK	;BUILD A PATH BLOCK
	  PJRST	RETDG		;CAN'T, RETURN THE DATAGRAM AND TRY AGAIN LATER
INTID9:	PJRST	SNDSTA		;SEND A START AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
;ROUTINE TO CHECK FOR AN OPEN VC.
;CALL:
;	P5/ PBK ADDRESS
;	PUSHJ	P,CHKVCO
;RETURN:
;	CPOPJ IF VC NOT OPEN (OR NO PB)
;	CPOPJ1 IF VC OPEN

CHKVCO:	JUMPE	P5,CPOPJ##	;RETURN IF NO PATH BLOCK
	LOAD	T1,PBVCST,(P5)	;GET VC STATE
	CAIN	T1,VC.OPN	;VC OPEN?
	AOS	(P)		;YES, SET FOR SKIP
	POPJ	P,		;RETURN
;ROUTINE TO BUILD A SYSTEM BLOCK.
;CALL:
;	Q1/ CI NODE NUMBER
;	Q3/ PCB ADDRESS
;	PUSHJ	P,BLDSBK
;RETURN:
;	CPOPJ ON FAILURE
;	CPOPJ1 ON SUCCESS WITH:
;	P4/ SBK ADDRESS

BLDSBK:	CIOFF			;PREVENT RACES
	MOVSI	T1,-C%SBLL	;-VE LENGTH OF SYSTEM BLOCK LIST
BLDSB1:	SKIPN	P4,SBLIST##(T1)	;GET AN ENTRY
	JRST	BLDSB2		;NONE, TRY NEXT
	LOAD	T2,SBDPN,(P4)	;GET DESTINATION PORT NUMBER
	CAMN	T2,Q1		;A MATCH?
	JRST	BLDSB3		;YES, USE THIS ONE
BLDSB2:	AOBJN	T1,BLDSB1	;LOOP FOR ANY OTHER SYSTEM BLOCKS

	MOVEI	T2,.SBLEN	;NUMBER OF WORDS REQUIRED
	PUSHJ	P,GETCOR	;GET THE SPACE
	  PJRST	CIONPJ##	;NOT AVAILABLE, INTERRUPTS BACK ON AND RETURN
	MOVE	P4,T1		;COPY ADDRESS TO PROPER AC
	MOVSI	T1,-C%SBLL	;-VE LENGTH OF SYSTEM BLOCK LIST
	SKIPE	SBLIST##(T1)	;LOOK FOR A FREE SLOT
	AOBJN	T1,.-1		;...
	JUMPGE	T1,BLDSBE	;ERROR RETURN IF NO FREE SLOTS
	MOVEM	P4,SBLIST##(T1)	;CLAIM THIS SLOT
	ADDI	T1,1		;MAKE SYSTEM BLOCK INDEX 1-BASED TO COMPLY WITH SCA SPEC
	STOR	T1,SBSBI,(P4)	;SAVE SYSTEM BLOCK INDEX
	STOR	Q1,SBDPN,(P4)	;STORE DESTINATION PORT NUMBER (CI NODE NUMBER)
BLDSB3:	MOVE	T1,Q3		;GET PCB ADDRESS
	ADD	T1,Q1		; OFFSET FOR THIS NODE
	MOVEM	P4,.PCSBK(T1)	;STORE SYSTEM BLOCK ADDRESS IN PCB
	PJRST	CINPJ1##	;INTERRUPTS BACK ON AND SKIP RETURN

BLDSBE:	CION			;OK TO INTERRUPT AGAIN
	MOVEI	T1,.SBLEN	;LENGTH OF BLOCK
	MOVE	T2,P4		;COPY SYSTEM BLOCK ADDRESS
	PUSHJ	P,GIVSWS##	;RETURN THE SPACE
	SETZ	P4,		;GET A ZERO
	POPJ	P,		;TAKE THE ERROR RETURN
;ROUTINE TO BUILD A PATH BLOCK.
;CALL:
;	Q1/ NODE NUMBER
;	Q2/ ADDRESS OF IDREC PACKET
;	Q3/ PCB ADDRESS
;	P4/ SBK ADDRESS
;	PUSHJ	P,BLDPBK
;RETURN:
;	CPOPJ ON FAILURE
;	CPOPJ1 ON SUCCESS WITH:
;	P5/ PBK ADDRESS

BLDPBK:	MOVEI	T2,.PBLEN	;NUMBER OF WORDS REQUIRED
	PUSHJ	P,GETCOR	;GET THE SPACE
	  POPJ	P,		;NOT AVAILABLE
	MOVE	P5,T1		;COPY ADDRESS TO PROPER AC
	CIOFF			;PREVENT RACES
	MOVSI	T1,-C%PBLL	;-VE LENGTH OF PATH BLOCK LIST
	SKIPE	PBLIST##(T1)	;LOOK FOR A FREE SLOT
	AOBJN	T1,.-1		;...
	JUMPGE	T1,BLDPBE	;ERROR RETURN IF NO FREE SLOTS
	MOVEM	P5,PBLIST##(T1)	;CLAIM THIS SLOT
	ADDI	T1,1		;MAKE PATH BLOCK INDEX 1-BASED TO COMPLY WITH SCA SPEC
	STOR	T1,PBPBI,(P5)	;SAVE PATH BLOCK INDEX
	MOVE	T1,Q3		;GET PB ADDRESS
	ADD	T1,Q1		; OFFSET FOR THIS NODE
	MOVEM	P5,.PCPBK(T1)	;STORE PATH BLOCK ADDRESS IN PCB
	LOAD	T1,SBPBI,(P4)	;GET INDEX OF FIRST PATH BLOCK
	STOR	T1,PBNPI,(P5)	;LINK THAT PATH BLOCK TO THIS ONE
	LOAD	T1,PBPBI,(P5)	;GET OUR PATH BLOCK INDEX
	STOR	T1,SBPBI,(P4)	;INSERT THIS PATH BLOCK AS HEAD OF LIST
	INCR	SBDPC,(P4)	;INCREMENT DESTINATION PORT COUNT
	LOAD	T1,SBSBI,(P4)	;GET SYSTEM BLOCK INDEX
	STOR	T1,PBSBI,(P5)	;STORE IN PATH BLOCK
	STOR	Q1,PBDPN,(P5)	;STORE DESTINATION PORT NUMBER (CI NODE NUMBER)
	CION			;ALLOW INTERRUPTS AGAIN
	XMOVEI	T1,.PBFCB(P5)	;POINTER TO FIRST CONNECTION BLOCK
	MOVEM	T1,.PBLCB(P5)	;STORE IN BLINK
	SETZM	.PBFCB(P5)	;CLEAR FLINK
	XMOVEI	T1,.PBTWQ(P5)	;POINTER TO TOP OF WORK QUEUE
	MOVEM	T1,.PBBWQ(P5)	;STORE IN BLINK
	SETZM	.PBTWQ(P5)	;CLEAR FLINK
	SETZM	.PBOBB(P5)	;ZERO THE SCA OUTBOUND MESSAGE WORD
	MOVE	T1,.PKMID(Q2)	;TYPE OF NODE
	MOVEM	T1,.PBDPC(P5)	;SAVE IT IN SYSTEM BLOCK
	MOVE	T1,.PKCOD(Q2)	;GET PORT CODE REVISION
	MOVEM	T1,.PBDCR(P5)	;SAVE IN SYSTEM BLOCK
	MOVE	T1,.PKFUN(Q2)	;PORT FUNCTIONALITY
	MOVEM	T1,.PBDPF(P5)	;SAVE IN SYSTEM BLOCK
	MOVE	T1,.PKPST(Q2)	;GET PORT STATE
	MOVEM	T1,.PBDPS(P5)	;SAVE IN SYSTEM BLOCK
	MOVEM	Q3,.PBPCB(P5)	;STORE ADDRESS OF PORT CONTROL BLOCK
IFN FTMP,<
	MOVE	T1,.CPCPN##	;OUR CPU NUMBER
	MOVEM	T1,.PBCPU(P5)	;STORE
>; END IFN FTMP
	JRST	CPOPJ1##	;SKIP RETURN

BLDPBE:	CION			;OK TO INTERRUPT
	MOVEI	T1,.PBLEN	;LENGTH OF BLOCK
	MOVE	T2,P5		;COPY PATH BLOCK ADDRESS
	PUSHJ	P,GIVSWS##	;RETURN THE SPACE
	SETZ	P5,		;GET A ZERO
	POPJ	P,		;TAKE THE ERROR RETURN
;ROUTINE TO SEND A START.
;CALL:
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	P5/ PBK ADDRESS
;	PUSHJ	P,SNDSTA
;RETURN:
;	CPOPJ ALWAYS

SNDSTA:	MOVEI	T1,VC.STS	;GET START-SENT CODE
	STOR	T1,PBVCST,(P5)	;SET INITIAL VC STATE
	MOVEI	T1,PP.STA	;GET START CODE
	PUSHJ	P,STRTDT	;MAKE A START PACKET
	PUSHJ	P,KLPSDG	;SEND THE DATAGRAM
	PJRST	STSST		;SET THE TIMER AND RETURN
;ROUTINE TO PUT START DATA INTO A BUFFER.
;CALL:
;	T1/ FUNCTION (START OR STACK)
;	Q2/ PACKET ADDRESS
;	PUSHJ	P,SRTRDT
;RETURN:
;	CPOPJ ALWAYS

STRTDT:	DPB	T1,PKYPPD	;OPCODE = SCA PPD FIELD
	SETZM	.SRSSY(Q2)	;CLEAR SENDING SYSTEM
	SETZM	.SRRSV(Q2)	;CLEAR RESERVED WORD
	MOVEI	T1,1		;CI PROTOCOL VERSION
	STOR	T1,SRVRS,(Q2)	;SAVE IN PACKET
	MOVE	T1,.SRRSV(Q2)	;GET THE WORD
	PUSHJ	P,REVFUL	;REVERSE IT
	MOVEM	T1,.SRRSV(Q2)	;PUT IT BACK
	MOVE	T1,[C%MXMP_2,,C%MXDP_4] ;MAX MESSAGE, DG SIZES
	PUSHJ	P,REVFUL	;REVERSE THEM FOR THE HSC
	MOVEM	T1,.SRMMS(Q2)	;SAVE IN PACKET
	MOVE	T1,[BYTE (8) "T","-","1","0"] ;"T-10" IS SOFTWARE TYPE
	MOVEM	T1,.SRSWT(Q2)	;SAVE IN PACKET
	MOVE	T1,[POINT 3,.JBVER##] ;POINTER TO RELEASE NUMBER
	MOVEI	T2,.SRSWV(Q2)	;WHERE IN PACKET TO STORE IT
	MOVEI	T3,4		;4 BYTES
	PUSHJ	P,STORNO	;SAVE SW VERSION
	MOVE	T1,[BYTE (8) " "," "," "," "] ;1ST 4 BYTES OF INCARNATION ARE BLANK
	MOVEM	T1,.SRSWI(Q2)	;...
	MOVE	T1,[POINT 3,.JBVER##,23] ;POINTER TO VERSION
	MOVEI	T2,.SRSWI+1(Q2)	;WHERE IN PACKET TO STORE
	MOVEI	T3,4		;4 BYTES
	PUSHJ	P,STORNO	;SAVE SW INCARNATION
	MOVE	T1,[BYTE (8) "K","L","1","0"] ;"KL10"
	MOVEM	T1,.SRHWT(Q2)	;IS HARDWARE TYPE
	PUSHJ	P,SAVE1##	;FREE UP P1
	APRID	P1		;READ MICROCODE VERSION (APRID)
	ANDX	P1,ID.UVN	;KEEP JUST VERSION
	MOVE	T1,[POINT 3,P1,5] ;POINT TO START OF VERSION
	MOVEI	T2,.SRHWV(Q2)	;WHERE TO STORE IT
	MOVEI	T3,4		;4 BYTES
	PUSHJ	P,STORNO	;SAVE MICROCODE VERSION
	MOVE	T1,[BYTE (8) " "," "," "," "] ;PAD LAST 2 WORDS OF HARDWARE VERSION
	MOVEM	T1,.SRHWV+1(Q2)	;...
	MOVEM	T1,.SRHWV+2(Q2)	;...
	MOVE	T1,[BYTE (8)"K","L","-"] ;NODE NAME
	MOVEM	T1,.SRNNM(Q2)
	SETZM	.SRNNM+1(Q2)
	MOVE	T3,Q2
	ADDI	T3,.SRNNM
	MOVSI	CX,(POINT 8,(T3),23)
	MOVE	T1,.CPASN##
	PUSHJ	P,STRDEC
	PUSHJ	P,VAXTOD	;COMPUTE VAX-STYLE TIME OF DAY
	DMOVEM	T1,.SRTOD(Q2)	;SAVE
	POPJ	P,		;RETURN
;ROUTINE TO STORE AN OCTAL NUMBER AS 8-BIT ASCII BYTES IN A PACKET.
;CALL:
;	T1/ POINTER TO DATA
;	T2/ ADDRESS OF WHERE TO STORE
;	T3/ COUNT OF BYTES TO STORE
;	Q2/ PACKET ADDRESS
;	PUSHJ	P,STORNO
;RETURN:
;	CPOPJ ALWAYS

STORNO:	MOVSI	CX,(POINT 8,0(T2)) ;SET CX AS A BYTE POINTER
	HLL	T2,Q2		;TURN ON RIGHT SECTION BITS
STORN1:	ILDB	T4,T1		;GET A BYTE
	ADDI	T4,"0"		;CONVERT TO ASCII
	IDPB	T4,CX		;SAVE IN PACKET
	SOJG	T3,STORN1	;FOR ALL CHARACTERS
	POPJ	P,		;RETURN


;ROUTINE TO STORE A DECIMAL NUMBER AS 8-BIT ASCII BYTES IN A PACKET.
;CALL:
;	T1/ NUMBER
;	T3/ CX POINTER WHERE TO STORE
;	PUSHJ	P,STRDEC
;RETURN:
;	CPOPJ ALWAYS

STRDEC:	IDIVI	T1,^D10		;BASE 10
	PUSH	P,T2
	SKIPE	T1
	PUSHJ	P,STRDEC
	POP	P,T2
	ADDI	T2,"0"
	IDPB	T2,CX
	POPJ	P,
;ROUTINE TO COMPUTE VAX-STYLE TIME OF DAY.
;CALL:
;	PUSHJ	P,VAXTOD
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T1 & T2/ 64-BIT VAX-STYLE TIME OF DAY

VAXTOD::SKIPN	DATE##		;HAS UNIVERSAL DATE/TIME BEEN SET UP?
	PUSHJ	P,SUDATE##	;NO, VERY NICE OF US TO DO SO
	MOVE	T1,DATE##	;GET UNIVERSAL DATE/TIME
	MUL	T1,VAXTIM	;CONVERT TIME TO VAX'S REPRESENTATION
	LSH	T2,1		;KILL OFF THE SIGN BIT SO WE HAVE A 70-BIT
				; QUANTITY IN T1 AND T2
	LSHC	T1,-5		;RIGHT JUSTIFY LOW ORDER TIME
	MOVE	T3,T2		;COPY THE LOW ORDER PART
	LSH	T3,4		;POSITION IT
	LSHC	T1,-^D28	;GET HIGH ORDER PART IN T2
	MOVE	T1,T2		;BACK TO T1
	TRZ	T1,17		;CLEAR THE JUNK BITS
	PUSHJ	P,REVFUL	;REVERSE THE BYTES
	PUSH	P,T1		;SAVE THE HIGH ORDER PART
	MOVE	T1,T3		;GET THE LOW ORDER PART
	PUSHJ	P,REVFUL	;REVERSE THE BYTES
	PJRST	T2POPJ##	;RESTORE HIGH ORDER PART TO T2, LOW ORDER PART
				; ALREADY IN T1

VAXTIM:	DEC	52734375	;MAGIC CONSTANT TO CONVERT UDT TO VAX TIME BASE
;RECEIVED A REQUEST DATA ON QUEUE 0 (WE NEVER USE 0)
;RECEIVED A REQUEST DATA ON QUEUE 1 (WE ALWAYS USE 1)
;RECEIVED A REQUEST DATA ON QUEUE 2 (WE NEVER USE 2)
;RECEIVED A SEND-DATA (WE NEVER SET THE RESPONSE BIT)

INTRD0:	JRST	INTSDT		;USE COMMON ROUTINE
INTRD1:	JRST	INTSDT		;USE COMMON ROUTINE
INTRD2:	JRST	INTSDT		;USE COMMON ROUTINE
INTSDT:	JUMPE	P3,INTSD1	;JUMP IF REMOTELY-GENERATED
	MOVE	T1,Q2		;POSITION BUFFER ADDRESS
	PJRST	SC.RBF##	;GIVE BUFFER BACK TO SCA POOL

;REMOTELY-GENERATED PACKET

INTSD1:	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	BUG.	(INF,KLPIRD,KLPSER,SOFT,<Invalid remotely-generated data request>,<<T1,STATUS>,<T2,FLAGS>,<P2,OPCODE>,<Q1,NODE>>,)
	PJRST	RETMSG		;RETURN PACKET TO MESSAGE FREE QUEUE
;RECEIVE ONE OF THE FOLLOWING WITH AN ERROR:
;REQUEST DATA ON QUEUE 0 (WE NEVER USE 0)
;REQUEST DATA ON QUEUE 1 (WE ALWAYS USE 1)
;REQUEST DATA ON QUEUE 2 (WE NEVER USE 2)
;SEND-DATA (WE NEVER SET THE RESPONSE BIT)

NBFERR:	JRST	SDTERR		;USE COMMON ROUTINE
RD0ERR:	JRST	SDTERR		;USE COMMON ROUTINE
RD1ERR:	JRST	SDTERR		;USE COMMON ROUTINE
RD2ERR:	JRST	SDTERR		;USE COMMON ROUTINE
SDTERR:	PUSHJ	P,SCAERR	;TELL SCA
	JUMPE	P3,RETMSG	;IF NOT LOCALLY-GENERATED, RETURN PACKET AND RETURN
	MOVE	T1,Q2		;GET THE BUFFER ADDRESS
	PJRST	SC.RBF##	;RETURN THE BUFFER TO SCA POOL AND RETURN
;RECEIVED A LOOP-BACK

INTLPB:	MOVE	T1,.PKSTS(Q2)	;GET PACKET STATUS WORD
	TXNN	T1,PF.PT0	;DID IT ARRIVE ON PATH A?
	JRST	INTLP1		;NO
	MOVX	T1,ST.WAB	;YES, IS WIRE A FLAGGED AS BAD?
	TDNN	T1,.PCSTS(Q3)	;...
	PJRST	RETDG		;NO, RETURN THE DATAGRAM AND RETURN
	BUG.	(INF,KLPWAG,KLPSER,HARD,<CI wire A has gone from bad to good>,,)
	ANDCAM	T1,.PCSTS(Q3)	;CLEAR THE FLAG
	MOVEI	T2,KS%ABG	;GET READ-COUNTERS REASON CODE
	PUSHJ	P,KLPRPT	;DO A READ-COUNTERS
	JRST	INTLP2		;CONTINUE

INTLP1:	MOVX	T1,ST.WBB	;IS WIRE B FLAGGED AS BAD?
	TDNN	T1,.PCSTS(Q3)	;...
	PJRST	RETDG		;NO, RETURN THE DATAGRAM AND RETURN
	BUG.	(INF,KLPWBG,KLPSER,HARD,<CI wire B has gone from bad to good>,,)
	ANDCAM	T1,.PCSTS(Q3)	;CLEAR THE FLAG
	MOVEI	T2,KS%BBG	;GET READ-COUNTERS REASON CODE
	PUSHJ	P,KLPRPT	;DO A READ-COUNTERS

INTLP2:	SETZ	Q1,		;START WITH NODE ZERO
INTLP3:	MOVE	P1,Q3		;GET PCB ADDRESS
	ADD	P1,Q1		; OFFSET BY CI NODE NUMBER
	SKIPN	P5,.PCPBK(P1)	;IS THERE A PATH BLOCK?
	JRST	INTLP4		;NO
	LOAD	T1,PBVCST,(P5)	;GET VC STATE
	CAIE	T1,VC.OPN	;IS VC OPEN?
	JRST	INTLP4		;NO
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;CAN'T, NEXT REQID WILL DO THIS
	MOVX	T1,CK.LPT!CK.PAA!CK.PBA ;BOTH PATHS ALLOWED
	MOVEM	T1,.PKCKT(Q2)	;...
	PUSHJ	P,KLPSCK	;SEND A SET-CIRCUIT
	MOVX	T1,RI.PAO!RI.PBO ;MARK BOTH PATHS AS OPEN
	IORM	T1,.PCRIS(P1)	;...
INTLP4:	CAIL	Q1,MAXNDS-1	;DONE THEM ALL?
	POPJ	P,		;YES, RETURN
	AOJA	Q1,INTLP3	;NO, LOOP TO NEXT ONE
;RECEIVED A LOOP-BACK WITH AN ERROR

LPBERR:	MOVE	T1,.PKSTS(Q2)	;GET STATUS FLAGS
	TXNE	T1,PS.PAE!PS.PBE ;PATH ERROR?
	JRST	LPBER1		;YES
	LDB	T2,PKYSTS	;GET STATUS
	LDB	T3,PKYFLG	;GET FLAGS
	MOVE	T4,.PCCSR(Q3)	;GET CSR
	BUG.	(INF,KLPLBF,KLPSER,HARD,<Loopback failed>,<<T2,STATUS>,<T3,FLAGS>,<P2,OPCODE>,<T4,CSR>>,)
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN

;PATH ERROR

LPBER1:	TXNN	T1,PS.PAE	;PATH A ERROR?
	JRST	LPBER2		;NO, MUST BE PATH B
	MOVX	T1,ST.WAB	;IS WIRE A FLAGGED AS BAD?
	TDNE	T1,.PCSTS(Q3)	;...
	PJRST	RETDG		;YES, RETURN THE DATAGRAM AND RETURN
	BUG.	(INF,KLPWAB,KLPSER,HARD,<CI wire A has gone from good to bad>,,)
	IORM	T1,.PCSTS(Q3)	;SET THE FLAG
	MOVEI	T2,KS%AGB	;GET READ-COUNTERS REASON CODE
	PJRST	KLPRPT		;DO A READ-COUNTERS AND RETURN

LPBER2:	MOVX	T1,ST.WBB	;IS WIRE B FLAGGED AS BAD?
	TDNE	T1,.PCSTS(Q3)	;...
	PJRST	RETDG		;YES, RETURN THE DATAGRAM AND RETURN
	BUG.	(INF,KLPWBB,KLPSER,HARD,<CI wire B has gone from good to bad>,,)
	IORM	T1,.PCSTS(Q3)	;SET THE FLAG
	MOVEI	T2,KS%BGB	;GET READ-COUNTERS REASON CODE
	PJRST	KLPRPT		;DO A READ-COUNTERS AND RETURN
;RECEIVED A SET-CIRCUIT

INTCKT:	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	BUG.	(CHK,KLPSCR,KLPSER,HARD,<SET-CIRCUIT command received>,<<T1,STATUS>,<T2,FLAGS>,<P2,OPCODE>>,)
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN


;RECEIVED A SET-CIRCUIT WITH AN ERROR

CKTERR:	LDB	T1,PKYSTS	;GET STATUS
	LDB	T2,PKYFLG	;GET FLAGS
	BUG.	(CHK,KLPCKE,KLPSER,HARD,<SET-CIRCUIT command error>,<<T1,STATUS>,<T2,FLAGS>,<P2,OPCODE>>,)
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN
;RECEIVED A READ STATISTICS COUNTERS

INTRCT:	MOVE	T1,.PKSTS(Q2)	;GET PACKET STATUS
	TXNE	T1,PF.CPE	;PORT GENERATE PACKET AFTER A CRAM PARITY ERROR?
	SKIPA	Q1,[KS%PCP]	;YES, GET SPECIAL REASON CODE AND SKIP
	HRRZ	Q1,.PKXID(Q2)	;NO, GET REASON CODE FROM PACKET
	SKIPL	Q1		;CHECK FOR VALIDITY
	CAILE	Q1,RCTMAX	;...
	MOVEI	Q1,RCTMAX+1	;OUT OF RANGE, SET ERROR DISPATCH
	PJRST	@RCTDSP(Q1)	;GO PROCESS THE PACKET

RCTDSP:	IFIW	RCTBUG		;       ILLEGAL
	IFIW	RCTSPR		;KS%PCP PORT CRAM PARITY ERROR
	IFIW	RCTSPR		;KS%AGB PORT A WENT FROM GOOD TO BAD
	IFIW	RCTSPR		;KS%ABG PORT A WENT FROM BAD TO GOOD
	IFIW	RCTSPR		;KS%BGB PORT B WENT FROM GOOD TO BAD
	IFIW	RCTSPR		;KS%BBG PORT B WENT FROM BAD TO GOOD
	IFIW	RCTSPR		;KS%PER PERIODIC READ
	IFIW	RCTBUG		;KS%UCD GET MICROCODE VERSION NUMBER (NOT USED)
	IFIW	RCTDIA		;KS%DIA DIAG. UUO REQUEST
RCTMAX==.-RCTDSP-1		;MAXIMUM REASON CODE
	IFIW	RCTBUG		;OUT OF RANGE


;HERE IF TRANSACTION ID IN PACKET WAS OUT OF RANGE

RCTBUG:	BUG.	(INF,KLPBRC,KLPSER,HARD,<Bad READ-COUNTERS packet>,,)
	PJRST	RETDG		;RETURN THE PACKET


;RECEIVED A READ-STATISTICS-COUNTERS WITH AN ERROR

RCTERR:	BUG.	(CHK,KLPRCE,KLPSER,HARD,<READ-COUNTERS command failed>,,)
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN
;HERE TO MAKE A SPEAR ENTRY (TYPE 241)

RCTSPR:	MOVEI	T1,KS%LEN	;LENGTH OF ERROR BLOCK
	PUSHJ	P,ALCSEB##	;ALLOCATE SYSTEM ERROR BLOCK
	  PJRST	RETDG		;ERROR, JUST TOSS PACKET AND RETURN
	MOVEI	T2,SEC%KS	;GET THE ERROR CODE
	DPB	T2,[POINT 9,.EBTYP(T1),8] ;STORE THE TYPE IN THE HEADER
	MOVEI	T2,2		;OFFSET TO DATA
	MOVEM	T2,.EBHDR+KS%OFF(T1) ;STORE IT
	MOVE	T2,.PKUCD(Q2)	;MICROCODE VERSION
	MOVE	T3,.PKXID(Q2)	;GET TRANSACTION ID (REASON FOR READ COUNTERS)
	DPB	T3,[POINTR T2,KS%RSN] ;STORE IT
	TXO	T2,FLD(KLPICH,KS%CHN) ;SET INTERNAL CHANNEL NUMBER
	MOVEM	T2,.EBHDR+KS%VER(T1) ;STORE IT
	MOVEI	T2,NOSTCT-<.PKUCD-.PKPDA+1> ;NUMBER OF WORDS TO MOVE
	XMOVEI	T3,.PKUCD+1(Q2)	;SOURCE ADDRESS
	MOVEI	T4,.EBHDR+KS%VER+1(T1) ;DESTINATION
	EXTEND	T2,[XBLT]	;MOVE THE REMAINDER
	PUSHJ	P,QUESEB##	;QUEUE THE BLOCK
	PJRST	RETDG		;RETURN THE PACKET AND RETURN


;HERE WHEN DIAG. UUO ASKED TO READ THE COUNTERS

RCTDIA:	MOVE	T1,DATE##	;GET NOW
	MOVEM	T1,.PCCTR(Q3)	;SAVE AS FIRST WORD OF BLOCK
	MOVEI	T1,NOSTCT	;NUMBER OF COUNTERS TO STORE
	XMOVEI	T2,.PKPDA(Q2)	;SOURCE
	XMOVEI	T3,.PCCTR+1(Q3)	;DESTINATION
	EXTEND	T1,[XBLT]	;MOVE THE DATA
	HLRZ	T1,.PKXID(Q2)	;GET JOB WHICH READ COUNTERS
	S0PSHJ	WAKJOB##	;WAKE THEM UP
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN
;RECEIVED A READ-REGISTER

INTRRG:	LDB	T1,PKYREG	;GET THE REGISTER NUMBER
	CAIE	T1,.RGNOD	;RIGHT ONE?
	BUG.	(HLT,KLPKRW,KLPSER,HARD,<CI20 read the wrong register>,<<T1,REG>>,)
	LDB	T1,PKYDTA	;GET THE DATA (OUR NODE NUMBER)
	MOVEM	T1,.PCONN(Q3)	;STUFF IN PCB
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN


;RECEIVED A READ-REGISTER WITH AN ERROR

RRGERR:	BUG.	(CHK,KLPCRR,KLPSER,HARD,<READ-REGISTER command failed>,,)
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN
;RECEIVED A MAINTENANCE CONFIRM
;RECEIVED A MAINTENANCE DATA RECEIVED

INTMDR:
INTMCR:	PUSHJ	P,CHKMAI	;ARE WE EXPECTING THIS?
	  PJRST	RETDG		;NO, RETURN THE DATAGRAM AND RETURN
	SETZM	.PCMFL(Q3)	;YES, THERE IS NO ERROR
	PUSHJ	P,WAKMAI	;WAKE THE WAITING JOB
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN


;RECEIVED A MAINTENANCE CONFIRM WITH AN ERROR
;RECEIVED A MAINTENANCE DATA RECEIVED WITH AN ERROR

MDRERR:
MCRERR:	PUSHJ	P,CHKMAI	;ARE WE EXPECTING THIS?
	  PJRST	RETDG		;NO, RETURN THE DATAGRAM AND RETURN
	BUG.	(INF,KLPMCE,KLPSER,HARD,<Received an MCNF or an MDATREC with an error>,<<T1,NODE>,<T2,STATUS>>,)
	MOVEI	T1,1		;GET ERROR INDICATOR
	MOVEM	T1,.PCMFL(Q3)	;SET THE ERROR FLAG
	PUSHJ	P,WAKMAI	;WAKE THE WAITING JOB
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN


;ROUTINE TO CHECK FOR MAINTENANCE TYPE MESSAGES EXPECTED.
;CALL:
;	Q1/ NODE NUMBER
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKMAI
;RETURN:
;	CPOPJ IF NOT EXPECTED
;	CPOPJ1 IF EXPECTED

CHKMAI:	SKIPE	.PCMJB(Q3)	;IS A MAINTENANCE TYPE MESSAGE EXPECTED?
	JRST	CPOPJ1##	;YES
	BUG.	(CHK,KLPMCR,KLPSER,HARD,<Received an unexpected MCNF or MDATREC>,<<Q1,NODE>>,)
	POPJ	P,		;RETURN
;RECEIVED A CLOSE BUFFER FINISHED INTERRUPT

INTCLB:	SKIPN	.PCMCN(Q3)	;IS THE DIAG. UUO EXPECTING THIS?
	PJRST	RETDG		;NO, RETURN THE DATAGRAM AND RETURN
	MOVE	T1,.PKBNM(Q2)	;YES, GET THE BUFFER NAME
	CAME	T1,.PCMCN(Q3)	;IS THIS THE ONE THE DIAG. EXPECTS?
	PJRST	RETDG		;NO, RETURN THE DATAGRAM AND RETURN
	AOS	.PCMCF(Q3)	;YES, SET THE FLAG
	PUSHJ	P,WAKMAI	;WAKE THE WAITING JOB
	PJRST	RETDG		;RETURN THE DATAGRAM AND RETURN


;RECEIVED A CLOSE-BUFFER WITH AN ERROR

CLBERR:	LDB	T2,PKYSFD	;GET STATUS FIELD LESS ERROR BIT
	CAIE	T2,PS.IBN	;IS IT AN INVALID BUFFER NAME ERROR?
	BUG.	(INF,KLPCLB,KLPSER,HARD,<Close buffer function failed>,<<T1,STATUS>>,)
	PJRST	INTCLB		;FINISH UP LIKE IT SUCCEEDED


;ROUTINE TO WAKE THE JOB WAITING FOR A MAINTENANCE OPERATION TO COMPLETE.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,WAKMAI
;RETURN:
;	CPOPJ ALWAYS

WAKMAI:	MOVE	T1,.PCMJB(Q3)	;GET THE JOB
	S0JRST	WAKJOB##	;WAKE THE JOB AND RETURN
;ROUTINE TO PUT A PACKET ON THE DATAGRAM FREE QUEUE.
;CALL:
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,RETDG
;RETURN:
;	CPOPJ ALWAYS

RETDG:	XMOVEI	T1,.PCDFQ(Q3)	;POINT AT THE PROPER QUEUE
	PJRST	PUTQUE		;RETURN THE PACKET AND RETURN


;ROUTINE TO PUT A PACKET ON THE MESSAGE FREE QUEUE.
;CALL:
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,RETMSG
;RETURN:
;	CPOPJ ALWAYS

RETMSG:	XMOVEI	T1,.PCMFQ(Q3)	;POINT AT THE PROPER QUEUE
	PJRST	PUTQUE		;RETURN THE PACKET AND RETURN
	SUBTTL	INTERRUPT LEVEL ERROR PROCESSING


;ROUTINE CALLED TO PRINT AN ERROR TEXT ON A CRAM PARITY ERROR.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,KLECPE
;RETURN:
;	CPOPJ ALWAYS

KLECPE:	MOVE	T1,.PCCRA(Q3)	;GET THE CRAM ADDRESS
	CAIL	T1,PPEFST	;IS THIS A PLANNED CRAM PARITY ERROR?
	CAILE	T1,PPELST	;...
	STOPCD	CPOPJ,INFO,KLPCPE,CPETYP ;++KLIPA CRAM PARITY ERROR
	STOPCD	CPOPJ,INFO,KLPHLT,HLTTYP ;++KLIPA MICROPROCESSOR HALT

;ROUTINE CALLED FROM DIE ON AN UNPLANNED CRAM PARITY ERROR

CPETYP:	PUSHJ	P,INLMES##	;PRINT TEXT
	  ASCIZ	/CI20 CRAM parity error
CRAM location /
	MOVE	T1,.PCCRA(Q3)	;GET THE CRAM ADDRESS
	PUSHJ	P,PRTDI8##	;PRINT IN OCTAL
	PUSHJ	P,INLMES##	;TYPE OUT CRAM CONTENTS
	  ASCIZ	/, CRAM contents /
	MOVE	T1,.PCCDL(Q3)	;GET FIRST CRAM HALFWORD
	PUSHJ	P,HWDPNT##	;PRINT AS HALFWORDS
	PUSHJ	P,PRSPC##	;SPACE OVER
	MOVE	T1,.PCCDR(Q3)	;GET SECOND CRAM HALFWORD
	PJRST	HWDPNT##	;PRINT AS HALFWORDS AND RETURN (DIE ADDS CRLF)

;ROUTINE CALLED FROM DIE ON A PLANNED CRAM PARITY ERROR

HLTTYP:	PUSHJ	P,INLMES##	;PRINT TEXT
	  ASCIZ	/CI20 microprocessor halted - /
	MOVE	T1,.PCCRA(Q3)	;GET THE CRAM ADDRESS
	HRRZ	T1,CPETXT-PPEFST(T1) ;GET ERROR TEXT ADDRESS
	PJRST	CONMES##	;PRINT AND RETURN (DIE ADDS CRLF)
;ROUTINE TO FINISH UP PROCESSING OF A CRAM PARITY ERROR.
;MUST BE CALLED AFTER REPORT AS IT DEPENDS ON SOME
;OF THE DATA WHICH REPORT STORES IN THE PCB.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,KLPCPD
;RETURN:
;	CPOPJ ALWAYS

KLPCPD:	MOVE	T1,.PCCRA(Q3)	;GET THE LAR
	SUBI	T1,PPEFST	;OFFSET TO FIRST PLANNED CRAM PARITY ERROR
	SKIPGE	T1		;PLANNED CRAM PARITY ERROR?
	MOVEI	T1,CPEUNP	;NO, SET DISPATCH FOR UNPLANNED CRAM PARITY ERROR
	PUSHJ	P,@CPEDSP(T1)	;CALL THE APPROPRIATE ROUTINE
	PJRST	RSTEWD		;ZERO THE PCB ERROR WORDS AND RETURN
;MACRO TO DEFINE CLEAN-UP ROUTINE AND TEXT STRING FOR CRAM PARITY ERRORS.

DEFINE	ERRS,<

	XALL			;;LIST GENERATED TABLE

	CPE	7750, CPEDON, <Internal port error>
	CPE	7751, CPEDON, <Self test failed>
	CPE	7752, CPEDON, <EBUS parity error>
	CPE	7753, EPEDON, <EBUS parity error>
	CPE	7754, CPEDON, <PLI parity error>
	CPE	7755, CPEDON, <CBUS parity error>
	CPE	7756, CPEDON, <Data path error>
	CPE	7757, CPEDON, <CBUS request error>
	CPE	7760, CPEDON, <EBUS request error>
	CPE	7761, CPEDON, <Grant CSR error>
	CPE	7762, CPEDON, <Short word count>
	CPE	7763, CPEDON, <Spurious channel error>
	CPE	7764, CPEDON, <Spurious transmit attention error>
	CPE	7765, CPEDON, <Spurious receive attention error>
	CPE	7766, CPEDON, <Transmitter timeout>
	CPE	7767, CPEDON, <Unknown halt code 7767>
	CPE	7770, CPEDON, <Unknown halt code 7770>
	CPE	7771, CPEDON, <Unknown halt code 7771>
	CPE	7772, CPEDON, <Unknown halt code 7772>
	CPE	7773, CPEDON, <Unknown halt code 7773>
	CPE	7774, CPEDON, <Unknown halt code 7774>
	CPE	7775, CPEDON, <Unknown halt code 7775>
	CPE	7776, CPEDON, <Unknown halt code 7776>
	CPE	7777, CPEDON, <Unknown halt code 7777>

	SALL

>; END DEFINE ERRS
;GENERATE CLEAN-UP ROUTINE TABLE

DEFINE	CPE(LOC,ADDR,TEXT),<
IF1,<IFN <<LOC-PPEFST>-<.-CPEDSP>>,<PRINTX ?Table CPEDSP entry LOC is out of order>>
	IFIW	ADDR		;'LOC' 'TEXT
>; END DEFINE CPE

CPEDSP:	ERRS			;GENERATE CLEAN-UP ROUTINE ADDRESS
CPEUNP==.-CPEDSP		;OFFSET FOR UNPLANNED CRAM PARITY ERROR
	IFIW	KLPUNP		;XXXX UNPLANNED CRAM PARITY ERROR
;GENERATE ERROR TEXT TABLE

DEFINE	CPE(LOC,ADDR,TEXT),<
IF1,<IFN <<LOC-PPEFST>-<.-CPETXT>>,<PRINTX ?Table CPETXT entry LOC is out of order>>
	[ASCIZ	|TEXT|]		;'LOC'
>; END DEFINE CPE

CPETXT:	ERRS			;GENERATE ERROR TEXT TABLE
;HERE WHEN CRAM PARITY ERROR PROCESSING IS DONE

CPEDON:	PUSHJ	P,CLNKLP	;CLEAN UP THE QUEUES
CPEDN1:	PUSHJ	P,STRKLP	;RESTART THE KLIPA
	  PJRST	RLDKLP		;FAILED, RELOAD IT
	POPJ	P,		;RETURN


;EBUS PARITY ERROR (QUEUES MESSED UP)

EPEDON:	MOVX	T1,E0.RES	;RESPONSE QUEUE HAVE ERROR?
	TDNN	T1,.PCER0(Q3)	;...
	JRST	EPEDN1		;NO
	XMOVEI	T1,.PCRSQ(Q3)	;YES, GET RESPONSE QUEUE INTERLOCK WORD
	XMOVEI	T2,.PQFLI(T1)	;GET THE FLINK
	SUB	T2,.PCVPO(Q3)	;WE NEED THE PHYSICAL ADDRESS
	MOVEM	T2,.PQFLI(T1)	;MAKE FLINK POINT AT ITSELF
	MOVEM	T2,.PQBLI(T1)	;MAKE BLINK POINT AT FLINK
	JRST	EPEDN2		;CONTINUE

EPEDN1:	MOVX	T1,CI.RQA	;IS A RESPONSE QUEUE AVAILABLE?
	TDNE	T1,.PCCSR(Q3)	;...
	PUSHJ	P,KLPRQC	;YES, PROCESS THE RESPONSES
EPEDN2:	MOVX	T1,E0.CMD	;COMMAND QUEUE ERROR?
	TDNN	T1,.PCER0(Q3)	;...
	PJRST	CPEDN1		;NO, DONE
	LOAD	T2,E0QUE,(Q3)	;YES, GET THE COMMAND QUEUE NUMBER
	IMULI	T2,.PQLEN	;TIMES LENGTH OF AN ENTRY
	XMOVEI	T1,.PCCQ0(Q3)	;ADDRESS OF COMMAND QUEUE 0 INTERLOCK WORD
	SUB	T1,T2		;MOVE TO THE CORRECT INTERLOCK WORD
	PUSHJ	P,CLENCQ	;CLEAN ALL QUEUES EXCEPT THE BAD ONE
	PJRST	CPEDN1		;RESTART THE KLIPA
;UNPLANNED CRAM PARITY ERROR (0000-7747)

KLPUNP:	XMOVEI	T1,.PCQBG(Q3)	;START OF THE QUEUES
	XMOVEI	T4,.PCQND(Q3)	;END OF THE QUEUES
KLPUN1:	XMOVEI	T2,.PQFLI(T1)	;GET FLINK
	SUB	T2,.PCVPO(Q3)	;WE NEED THE PHYSICAL ADDRESS
	MOVEM	T2,.PQFLI(T1)	;MAKE FLINK POINT AT ITSELF
	MOVEM	T2,.PQBLI(T1)	;MAKE BLINK POINT AT FLINK
	ADDI	T1,.PQLEN	;ADVANCE TO NEXT QUEUE
	CAMGE	T1,T4		;DONE THEM ALL?
	JRST	KLPUN1		;NO
	PUSHJ	P,STKDFQ	;RE-STOCK THE DATAGRAM FREE QUEUE
	PJRST	RLDKLP		;RELOAD THE KLIPA AND RETURN
;ROUTINE TO CLEAN ALL QUEUES AND PROCESS ANY RESPONSES.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,CLNKLP
;RETURN:
;	CPOPJ ALWAYS

CLNKLP:	XMOVEI	T1,.PCRSQ(Q3)	;GET ADDRESS OF RESPONSE QUEUE
	PUSHJ	P,CHKMPT	;IS RESPONSE QUEUE EMPTY?
	  SKIPA			;YES
	PUSHJ	P,KLPRQC	;NO, CLEAN THE RESPONSE QUEUE
	SETZ	T1,		;FLAG WE SHOULD CLEAN ALL QUEUES
	PJRST	CLENCQ		;CLEAN THE COMMAND QUEUES AND RETURN


;ROUTINE TO CLEAN THE COMMAND QUEUES
;CALL:
;	T1/ 0 OR ADDRESS OF QUEUE WHICH IS BAD
;	Q3/ PCB
;	PUSHJ	P,CLENCQ
;RETURN:
;	CPOPJ ALWAYS

CLENCQ:	STKVAR	<IWORD,BADQI>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,BADQI	;SAVE ADDRESS OF .PQIWD FOR BAD QUEUE
	XMOVEI	T1,.PCCQB(Q3)	;GET ADDRESS OF FIRST COMMAND QUEUE
CLENC1:	MOVEM	T1,IWORD	;SAVE ACROSS CALLS TO CLNCOM
	SETOM	.PQIWD(T1)	;RESET THE INTERLOCK
	CAME	T1,BADQI	;IS THIS THE BAD ONE?
	JRST	CLENC2		;NO
	XMOVEI	T2,.PQFLI(T1)	;YES, GET ADDRESS OF FLINK
	SUB	T2,.PCVPO(Q3)	;GET PHYSICAL ADDRESS
	MOVEM	T2,.PQFLI(T1)	;MAKE FLINK POINT AT ITSELF
	MOVEM	T2,.PQBLI(T1)	;MAKE BLINK POINT AT FLINK
	JRST	CLENC3		;CONTINUE

CLENC2:	PUSHJ	P,CLNCOM	;CLEAN THE COMMAND QUEUE
CLENC3:	MOVE	T1,IWORD	;WHERE WE WERE
	ADDI	T1,.PQLEN	;STEP TO NEXT QUEUE ENTRY
	XMOVEI	T2,.PCCQE(Q3)	;END OF THE COMMAND QUEUES
	CAMGE	T1,T2		;GONE PAST THE END?
	JRST	CLENC1		;NO, DO NEXT COMMAND QUEUE
	POPJ	P,		;RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
;ROUTINE TO CLEAN AN INDIVIDUAL COMMAND QUEUE
;CALL:
;	T1/ ADDRESS OF QUEUE'S INTERLOCK WORD
;	Q3/ PCB
;	PUSHJ	P,CLNCOM
;RETURN:
;	CPOPJ ALWAYS

CLNCOM:	STKVAR	<IWORD>		;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	T1,IWORD	;STASH ADDRESS OF INTERLOCK WORD
CLNCM1:	PUSHJ	P,REMQUE	;GET A PACKET FROM THE QUEUE
	  POPJ	P,		;QUEUE EMPTY, DONE
	MOVX	T1,PK.SCA	;IS PACKET TO BE RETURNED TO SCA?
	TDNE	T1,.PKVRT(Q2)	;...
	JRST	CLNCM2		;YES
	PUSHJ	P,TOSSIT	;NO, RETURN IT TO PROPER FREE QUEUE
	JRST	CLNCM3		;CONTINUE

CLNCM2:	SETO	P3,		;SAY PACKET WAS LOCALLY GENERATED
	MOVE	P5,Q3		;GET PCB ADDRESS
	LDB	T1,PKYNOD	; OFFSET FOR THIS NODE
	ADD	P5,T1		;...
	MOVE	P5,.PCPBK(P5)	;GET PBK ADDRESS
	CIOFF			;PREVENT INTERRUPTS WHILE IN SCA LAND
	PUSHJ	P,GV2SCA	;GIVE THE PACKET TO SCA
	CION			;OK TO INTERRUPT
CLNCM3:	MOVE	T1,IWORD	;RESTORE ADDRESS OF INTERLOCK WORD
	JRST	CLNCM1		;GO GET NEXT PACKET

	ENDSV.			;END OF STACK VARIABLE RANGE
;ROUTINE TO STOP, RESET, AND RESTART THE KLIPA
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,URDEAD
;RETURN:
;	CPOPJ ALWAYS

URDEAD:	CIOFF			;PREVENT RACES
	PUSHJ	P,STPKLP	;STOP THE KLIPA
	PUSHJ	P,CIGONE	;RESET ALL CI ACTIVITY
	PUSHJ	P,RLDKLP	;RELOAD THE KLIPA
	PJRST	CIONPJ##	;TURN INTERRUPTS BACK ON AND RETURN


;ROUTINE TO RESET ALL CI ACTIVITY.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CIGONE
;RETURN:
;	CPOPJ ALWAYS

CIGONE:	PUSHJ	P,NONODS	;INFORM SCA OF DEPARTED NODES
	PUSHJ	P,RSTRID	;RESET REQUEST-ID STATUS
	PUSHJ	P,RSTLKS	;RESET QUEUE INTERLOCKS IN PCB
	PJRST	CLNKLP		;CLEAN THE QUEUES AND RETURN


;ROUTINE TO TELL SCA ABOUT THE DISAPPEARANCE OF EACH NODE
;WITH AN OPEN VC WHEN THE KLIPA CROAKS.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,NONODS
;RETURN:
;	CPOPJ ALWAYS

NONODS:	SAVEAC	<P1,P2,P4,P5>	;SAVE THE AC'S WE NEED
	MOVE	P1,Q3		;GET PCB ADDRESS
	MOVEI	P2,MAXNDS	;NUMBER OF ENTRIES
NONOD1:	SKIPN	P5,.PCPBK(P1)	;GET A PATH BLOCK ADDRESS
	JRST	NONOD2		;NONE?
	SETZ	Q2,		;GET YOUR OWN PACKET
	SETO	P4,		;WE NEED A SHUTDOWN PACKET
	PUSHJ	P,CLOSV1	;CLOSE THE VC AND TELL SCA
NONOD2:	SOJLE	P2,CPOPJ##	;RETURN IF NO MORE NODES LEFT
	AOJA	P1,NONOD1	;YES, OFFSET TO NEXT NODE AND LOOP
;CLEAR QUEUE INTERLOCKS
;CALL WITH:
;	Q3/ PCB
;	PUSHJ	P,RSTLKS
;RETURN:
;	CPOPJ ALWAYS

RSTLKS:	SETOM	.PCCQ3+.PQIWD(Q3) ;COMMAND QUEUE 3
	SETOM	.PCCQ2+.PQIWD(Q3) ;COMMAND QUEUE 2
	SETOM	.PCCQ1+.PQIWD(Q3) ;COMMAND QUEUE 1
	SETOM	.PCCQ0+.PQIWD(Q3) ;COMMAND QUEUE 0
	SETOM	.PCRSQ+.PQIWD(Q3) ;RESPONSE QUEUE
	SETOM	.PCMFQ+.PQIWD(Q3) ;MESSAGE FREE QUEUE
	SETOM	.PCDFQ+.PQIWD(Q3) ;DATAGRAM FREE QUEUE
	SETOM	.PCRFQ+.PQIWD(Q3) ;RESERVED FREE QUEUE
	POPJ	P,		;RETURN


;CLEAR THE ERROR WORDS
;CALL:
;	Q3/ PCB
;	PUSHJ	P,RSTEWD
;RETURN:
;	CPOPJ ALWAYS

RSTEWD:	SETZM	.PCER0(Q3)	;ERROR WORD 0
	SETZM	.PCER1(Q3)	;ERROR WORD 1
	SETZM	.PCER2(Q3)	;ERROR WORD 2
	SETZM	.PCER3(Q3)	;ERROR WORD 3
	SETZM	.PCER4(Q3)	;ERROR WORD 4
	POPJ	P,		;RETURN
;ROUTINE TO RESET THE PCB QUEUES.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,RSTQS
;RETURN:
;	CPOPJ ALWAYS

RSTQS:	XMOVEI	T1,.PCQBG(Q3)	;POINT AT FIRST QUEUE ENTRY TRIPLET
	XMOVEI	T4,.PCQND(Q3)	;POINT AT END OF QUEUE ENTRY TRIPLETS
RSTQS1:	SETOM	.PQIWD(T1)	;SET THE INTERLOCK WORD
	XMOVEI	T2,.PQFLI(T1)	;GET VIRTUAL ADDRESS OF FORWARD LINK
	SUB	T2,.PCVPO(Q3)	;MAKE IT A PHYSICAL ADDRESS
	MOVEM	T2,.PQFLI(T1)	;MAKE FLINK POINT AT ITSELF
	MOVEM	T2,.PQBLI(T1)	;MAKE BLINK POINT AT FLINK
	ADDI	T1,.PQLEN	;ADD LENGTH OF THE QUEUE ENTRY TRIPLET
	CAMGE	T1,T4		;PAST THE END?
	JRST	RSTQS1		;NO, LOOP FOR OTHER QUEUE ENTRY TRIPLETS
	POPJ	P,		;RETURN


;ROUTINE TO STOCK THE DATAGRAM FREE QUEUE.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,STKDFQ
;RETURN:
;	CPOPJ ONLY IF SUCCEEDED

STKDFQ:	MOVNI	T1,2*MAXNDS	;GET TWO DATAGRAM BUFFERS FOR EACH NODE
				; (INDICATE WE WANT TO OVER-RIDE THRESHOLD CHECK)
	PUSHJ	P,SC.ALD##	;GET THEM FROM SCA
	  BUG.	(HLT,KLPNOD,KLPSER,SOFT,<Can't stock datagram free queue>,,)
	MOVE	Q2,T1		;POSITION THE FIRST BUFFER ADDRESS
	XMOVEI	T1,.PCDFQ(Q3)	;QUEUE ON WHICH TO INSERT PACKET
	PJRST	LNKQUE		;LINK THE PACKETS ON THE FREE QUEUE AND RETURN
;ROUTINE CALLED TO PROCESS AN MBUS ERROR.
;MUST BE CALLED AFTER REPORT AS IT DEPENDS ON SOME
;OF THE DATA WHICH REPORT STORES IN THE PCB.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,KLPMBD
;RETURN:
;	CPOPJ ALWAYS

KLPMBD:	MOVE	T1,.PCCSR(Q3)	;GET CONI
	MOVE	T2,.PCCRA(Q3)	;GET LAR
	DMOVE	T3,.PCCDL(Q3)	;GET TWO DATA WORDS
	BUG.	(INF,KLPMBE,KLPSER,HARD,<MBUS error>,<<T1,CSR>,<T2,LAR>,<T3,CRAM1>,<T4,CRAM2>>,)
	PUSHJ	P,STRKLP	;RESTART THE KLIPA
	  PJRST	RLDKLP		;FAILED, RELOAD THE MICROCODE
	POPJ	P,		;RETURN
;ROUTINE CALLED TO PROCESS A FREE QUEUE ERROR.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,KLPFQE
;RETURN:
;	CPOPJ ALWAYS

KLPFQE:	MOVE	T1,.PCPIA(Q3)	;GET PI ASSIGNMENT
	CONO	KLP,CO.EPE+CO.FQE+CO.BTS(T1) ;CLEAR FREE QUEUE ERROR, EBUS PARITY
	XMOVEI	T1,.PCDFQ(Q3)	;ADDRESS OF QUEUE
	PUSHJ	P,CHKMPT	;IS QUEUE EMPTY?
	  SKIPA			;YES
	JRST	KLPFQ2		;NO
	BUG.	(INF,KLPDFQ,KLPSER,SOFT,<Datagram free queue empty>,,)
	MOVEI	T1,1		;GET A DATAGRAM BUFFER
	PUSHJ	P,SC.ALD##	;ASK SCA
	  JRST	KLPFQ1		;COULDN'T
	MOVE	Q2,T1		;POSITION THE BUFFER ADDRESS
	PUSHJ	P,RETDG		;PUT IT ON THE DATAGRAM FREE QUEUE
KLPFQ1:	MOVSI	T1,1		;COUNT A DATAGRAM FREE QUEUE ERROR
	ADDM	T1,.PCFQE(Q3)	;...
	POPJ	P,		;RETURN

KLPFQ2:	XMOVEI	T1,.PCMFQ(Q3)	;ADDRESS OF QUEUE
	PUSHJ	P,CHKMPT	;IS QUEUE EMPTY?
	  SKIPA			;YES
	JRST	KLPFQ4		;NO
	BUG.	(INF,KLPMFQ,KLPSER,SOFT,<Message free queue empty>,,)
	MOVEI	T1,1		;GET A MESSAGE BUFFER
	PUSHJ	P,SC.ABF##	;ASK SCA
	  JRST	KLPFQ3		;COULDN'T
	MOVE	Q2,T1		;POSITION THE BUFFER ADDRESS
	PUSHJ	P,RETMSG	;PUT IT ON THE MESSAGE FREE QUEUE
KLPFQ3:	MOVEI	T1,1		;COUNT A MESSAGE FREE QUEUE ERROR
	ADDM	T1,.PCFQE(Q3)	;...
	POPJ	P,		;RETURN

KLPFQ4:	BUG.	(INF,KLPSFQ,KLPSER,SOFT,<Spurious free queue error>,,)
	POPJ	P,		;RETURN
;ROUTINE TO RECORD PORT ERROR INFORMATION IN THE PCB AND
;MAKE A SPEAR ENTRY.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,REPORT
;RETURN:
;	CPOPJ ALWAYS

REPORT:	PUSHJ	P,SAVE1##	;SAVE P1 FOR A BIT
	MOVE	P1,DATE##	;GET NOW
	SUB	P1,.PCLKE(Q3)	;HOW LONG SINCE LAST ERROR
	CAIGE	P1,RSTIME	;HAS IT SCREWED UP LATELY?
	TDZA	P1,P1		;NO, WE'LL RESTART IT
	MOVEI	P1,1		;YES, WE'LL LEAVE IT STOPPED
	MOVE	T1,DATE##	;GET NOW
	MOVEM	T1,.PCLKE(Q3)	;UPDATE TIME OF LAST ERROR
	CONO	KLP,CO.LAR	;SET THE LAR BIT
	DATAI	KLP,T1		;READ THE LAR
	LDB	T1,[POINTR T1,DT.LAR] ;GET THE ADDRESS PORTION
	MOVEM	T1,.PCCRA(Q3)	;SAVE IT
	CONO	KLP,0		;CLEAR THE LAR BIT
	TXO	T1,.DOLRA!DO.LHW ;SET TO READ LH DATA (INCLUDE CRAM ADDRESS)
	DATAO	KLP,T1		;TELL THE KLIPA WHAT ADDRESS TO READ
	DATAI	KLP,T1		;READ THE DATA
	LDB	T1,[POINTR T1,DT.CRM] ;GET THE USEFUL PART
	MOVEM	T1,.PCCDL(Q3)	;SAVE IT
	MOVE	T1,.PCCRA(Q3)	;GET LAR BACK
	TXO	T1,.DOLRA	;SET TO READ RH DATA
	DATAO	KLP,T1		;TELL THE KLIPA WHAT ADDRESS TO READ
	DATAI	KLP,T1		;READ THE DATA
	LDB	T1,[POINTR T1,DT.CRM] ;GET THE USEFUL PART
	MOVEM	T1,.PCCDR(Q3)	;SAVE IT
	MOVE	T1,.CPEPT##	;GET OUR EPT ADDRESS
	DMOVE	T2,KLPICH*4(T1)	;GET FIRST TWO WORDS OF CHANNEL LOGOUT AREA
	DMOVEM	T2,.PCLG0(Q3)	;SAVE THEM
	MOVE	T2,KLPICH*4+2(T1) ;GET THIRD WORD
	MOVEM	T2,.PCLG2(Q3)	;SAVE IT
	MOVE	T1,.PCCCW(Q3)	;GET PORT'S CCW
	MOVEM	T1,.PCECW(Q3)	;SAVE IT

;WITH THE NECESSARY DATA GATHERED TRY TO MAKE A SPEAR ENTRY
	MOVEI	T1,KP%LEN	;LENGTH OF ERROR BLOCK
	PUSHJ	P,ALCSEB##	;ALLOCATE SYSTEM ERROR BLOCK
	  POPJ	P,		;SORRY, WE TRIED
	MOVEI	T2,SEC%KP	;GET THE ERROR CODE
	DPB	T2,[POINT 9,.EBTYP(T1),8] ;STORE THE TYPE IN THE HEADER
	MOVE	T2,.PCCSR(Q3)	;GET CONI AT INTERRUPT
	MOVEM	T2,.EBHDR+KP%CSR(T1) ;STORE IT
	HRRZ	T2,.PCULB+.ULVER(Q3) ;GET KLIPA MICROCODE VERSION
	TXO	T2,FLD(KLPICH,KP%CHN) ;SET INTERNAL CHANNEL NUMBER
	MOVEM	T2,.EBHDR+KP%VER(T1) ;STORE IT
;	MOVEI	T2,2(P1)	;GET DISPOSITION CODE
	SETZ	T2,		;STORE A ZERO LIKE TOPS-20 DOES
	MOVEM	T2,.EBHDR+KP%DSP(T1) ;STORE IT
	MOVE	T2,.PCCRA(Q3)	;GET CRAM ADDRESS
	MOVEM	T2,.EBHDR+KP%CRA(T1) ;STORE IT
	DMOVE	T2,.PCCDL(Q3)	;GET CRAM DATA
	DMOVEM	T2,.EBHDR+KP%CRD(T1) ;STORE IT
	DMOVE	T2,.PCLG0(Q3)	;GET FIRST TWO LOGOUT WORDS
	DMOVEM	T2,.EBHDR+KP%LG0(T1) ;STORE THEM
	MOVE	T2,.PCLG2(Q3)	;GET THIRD LOGOUT WORD
	MOVEM	T2,.EBHDR+KP%LG2(T1) ;STORE IT
	MOVE	T2,.PCECW(Q3)	;GET PORT'S CCW AT ERROR
	MOVEM	T2,.EBHDR+KP%ECW(T1) ;STORE IT
	DMOVE	T2,.PCER0(Q3)	;GET ERROR WORDS
	DMOVEM	T2,.EBHDR+KP%PE0(T1) ;STORE THEM
	PJRST	QUESEB##	;QUEUE THE BLOCK AND RETURN
	SUBTTL	MISCELLANEOUS KLIPA ROUTINES


;ROUTINE TO START THE KLIPA.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,STRKLP
;RETURN:
;	CPOPJ IF COULDN'T START KLIPA
;	CPOPJ1 IF KLIPA STARTED

STRKLP:	PUSHJ	P,RSTEWD	;RESET THE PCB ERROR WORDS
	PUSHJ	P,KIKKLP	;KICK THE KLIPA
	  JRST	STRKL1		;IT FAILED
	PUSHJ	P,PIAKLP	;GIVE THE KLIPA A PI ASSIGNMENT
	BUG.	(INF,KLPSTR,KLPSER,HARD,<CI20 restarted>,,)
	MOVX	T1,ST.RDY	;KLIPA NEEDS TO SEND REQUEST-IDS
	ANDCAM	T1,.PCSTS(Q3)	;CLEAR FLAG SO PPDSEC KNOWS
	JRST	CPOPJ1##	;SKIP RETURN

STRKL1:	MOVX	T1,ST.DED	;IS THE SECOND TIME WE'VE TRIED TO START THE KLIPA?
	TDNE	T1,.PCSTS(Q3)	;...
	JRST	STRKL2		;YES, IT MUST BE DEAD
	IORM	T1,.PCSTS(Q3)	;NO, SET THE FLAG FOR NEXT TIME
	BUG.	(INF,KLPRSF,KLPSER,HARD,<CI20 restart failed>,,)
	POPJ	P,		;RETURN

STRKL2:	BUG.	(INF,KLPDED,KLPSER,HARD,<CI20 is dead>,,)
	POPJ	P,		;RETURN


;ROUTINE TO STOP THE KLIPA.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,STPKLP
;RETURN:
;	CPOPJ ALWAYS

STPKLP:	PUSHJ	P,DISKLP	;PUT KLIPA IN DISABLED STATE
	  JFCL			;DON'T REALLY CARE
	CONO	KLP,CO.CPT	;STEP ON IT
	MOVX	T1,ST.DED	;SAY IT'S DEAD
	IORM	T1,.PCSTS(Q3)	;...
	POPJ	P,		;RETURN
;ROUTINE TO RELOAD THE KLIPA MICROCODE AND START THE KLIPA.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,RLDKLP
;RETURN:
;	CPOPJ ALWAYS


;ROUTINE TO RELOAD THE KLIPA MICROCODE AND START THE KLIPA.
;SIMILAR TO RLDKLP BUT TAKES SKIP RETURN IF RELOAD SUCCEEDED.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,RLDKLI
;RETURN:
;	CPOPJ IF RELOAD FAILED
;	CPOPJ1 IF RELOAD SUCCEEDED

RLDKLP:	TDZA	T1,T1		;INDICATE RLDKLP ENTRY
RLDKLI:	MOVEI	T1,1		;INDICATE RLDKLI ENTRY
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	P1,T1		;SAVE ENTRY INDICATOR
	PUSHJ	P,KLPLOD	;LOAD THE KLIPA MICROCODE
	JUMPE	T1,RLDKL3	;NO VERSION MEANS NO UCODE LOADED
	PUSHJ	P,KIKKLP	;KICK THE KLIPA
	  JRST	RLDKL2		;FAILED
	JUMPN	P1,CPOPJ1##	;SKIP PI ASSIGNMENT, ETC., IF INITIALIZATION
	PJRST	PIAKLP		;GIVE THE KLIPA A PI ASSIGNMENT AND RETURN

RLDKL2:	BUG.	(INF,KLPRLF,KLPSER,HARD,<CI20 microcode reload failed>,,)

RLDKL3:	MOVX	T1,ST.DED	;SAY IT'S DEAD
	IORM	T1,.PCSTS(Q3)	;...
	POPJ	P,		;RETURN
;ROUTINE TO GET THE KLIPA READY TO RUN.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,KIKKLP
;RETURN:
;	CPOPJ IF COULDN'T GET IT READ
;	CPOPJ1 IF KLIPA READY TO RUN
;
;CLEARS ST.DED IF KLIPA ENTERED ENABLED STATE.

KIKKLP:	PUSHJ	P,RSTRID	;RESET REQUEST-ID INFO
	MOVE	T1,.CPEPT##	;GET ADDRESS OF EPT
	MOVE	T2,.PCPBA(Q3)	;GET PHYSICAL ADDRESS OF PCB
	ADD	T2,[FLD(.CCFTH,CC.OPC)!FLD(3,CC.WDC)!FLD(.PCPBA,CC.ADR)]
				; SET INITIAL CCW TO TRANSFER .PCPBA, .PCPIA,
				; AND .PCAL1 TO THE KLIPA
	MOVEM	T2,KLPICH*4+.CSICW(T1) ;STORE IN THE CHANNEL LOGOUT AREA
	DATAO	KLP,[.DOLRA+0]	;SET START ADDRESS TO 0
	PUSHJ	P,DISKLX	;PUT KLIPA IN DISABLED STATE
	  POPJ	P,		;WE COULDN'T MAKE IT
	MOVE	T1,.CPEPT##	;GET ADDRESS OF EPT AGAIN
	MOVE	T2,.PCPBA(Q3)	;GET PHYSICAL ADDRESS OF PCB
	ADD	T2,[FLD(.CCJMP,CC.OPC)!FLD(.PCCCW,CC.ADR)] ;GET A JUMP CCW
				; FOR THE KLIPA TO USE
	MOVEM	T2,KLPICH*4+.CSICW(T1) ;STORE IN THE CHANNEL LOGOUT AREA
	PUSHJ	P,ENAKLP	;PUT KLIPA IN ENABLED STATE
	  POPJ	P,		;WE COULND'T MAKE IT
	MOVE	T1,.CPUPT##	;RESET KEEP-ALIVE TIMER
	MOVEM	T1,.PCKRT(Q3)	; TO PREVENT SPURIOUS KLPKAF
	MOVX	T1,ST.DED	;KLIPA ISN'T DEAD
	ANDCAM	T1,.PCSTS(Q3)	;...
	JRST	CPOPJ1##	;SUCCESS


;ROUTINE TO GIVE THE KLIPA ITS PI ASSIGNMENT.
;CALL:
;	Q3/ PCB
;	PUSHJ	P,PIAKLP
;RETURN:
;	CPOPJ ALWAYS

PIAKLP:	MOVE	T1,.PCPIA(Q3)	;GET PI ASSIGNMENT
	CONO	KLP,CO.BTS(T1)	;GIVE IT ITS PI ASSIGNMENT
	POPJ	P,		;RETURN
;ROUTINE TO MAKE THE KLIPA ENTER THE DISABLED STATE.
;CALL:
;	PUSHJ	P,DISKLP
;RETURN:
;	CPOPJ IF DIDN'T ENTER DISABLED STATE
;	CPOPJ1 IF KLIPA IN DISABLED STATE

DISKLP:	CONSO	KLP,CI.MRN	;MICROCODE RUNNING?
	JRST	CPOPJ1##	;NO, THEN DON'T NEED TO STOP IT
DISKLX:	CONO	KLP,CO.DIS+CO.MRN ;GO FROM UNINITIALIZED TO DISABLED
	MOVEI	T1,5000		;LOOP COUNT
DISKL1:	CONI	KLP,T2		;GET STATUS
	TXNE	T2,CI.DCP	;DISABLE COMPLETE?
	AOSA	(P)		;YES, SET FOR SKIP RETURN
	SOJG	T1,DISKL1	;NO, WAIT A BIT
	POPJ	P,		;DIDN'T MAKE IT


;ROUTINE TO MAKE THE KLIPA ENTER THE ENABLED STATE.
;CALL:
;	PUSHJ	P,ENAKLP
;RETURN:
;	CPOPJ IF DIDN'T ENTER ENABLED STATE
;	CPOPJ1 IF KLIPA ENABLED

ENAKLP:	CONO	KLP,CO.RQA!CO.BTS ;GO FROM DISABLED TO ENABLED
	MOVEI	T1,5000		;LOOP COUNT
ENAKL1:	CONI	KLP,T2		;GET STATUS
	TXNE	T2,CI.ECP	;ENABLE COMPLETE?
	AOSA	(P)		;YES, SET FOR SKIP RETURN
	SOJG	T1,ENAKL1	;NO, WAIT A BIT
	POPJ	P,		;DIDN'T MAKE IT
	SUBTTL	LOAD KLIPA MICROCODE


;ROUTINE TO LOAD THE KLIPA MICROCODE.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,KLPLOD
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T1/ MICROCODE VERSION LOADED

KLPLOD:	CONO	KLP,CO.CPT	;RESET THE KLIPA
	PUSHJ	P,SAVE4##	;SAVE P1-P4
	XMOVEI	P1,.PCULB(Q3)	;POINT TO MICROCODE LOADER BLOCK
	MOVE	T1,P1		;COPY ADDRESS
	PUSHJ	P,BTUCOD##	;FIND THE MICROCODE
	  SKIPA	T1,P1		;NOT AVAILABLE, POINT TO MICROCODE LOADER BLOCK
	JRST	KLPLD1		;GOT IT
	PUSHJ	P,BTURPT##	;REPORT LOAD FAILURE
	SETZ	T1,		;DON'T RETURN A MICROCODE VERSION
	POPJ	P,		;RETURN

KLPLD1:	SETZ	P2,		;CRAM ADDR
	MOVSI	P3,(<POINT 12,,>!1B12) ;MAKE A TWO WORD BYTE POINTER
	MOVE	P4,.ULADR(P1)	;30-BIT ADDRESS IN SECOND WORD

KLPLD2:	MOVEI	T1,5		;5 BYTES PER WORD OF CRAM
KLPLD3:	ILDB	T4,P3		;GET A BYTE
	LSHC	T2,^D12		;APPEND IT TO THE WORD
	ADD	T3,T4		;...
	SOJN	T1,KLPLD3	;GET ALL 5 BYTES
	LSHC	T2,^D12		;GET RID OF NOISE BITS
	MOVE	T4,P2		;CRAM ADDR
	LSH	T4,^D23		;...
	TLO	T4,(.DOLRA+DO.LHW) ;LEFT HALF
	DATAO	KLP,T4		;LOAD THE ADDR REGISTER
	LSHC	T1,^D30		;GET LH
	DATAO	KLP,T1		;WRITE LH DATA
	LSHC	T1,-^D30	;GET RH
	LSHC	T2,^D30		;...
	LSHC	T1,^D30		;...
	TLZ	T4,(DO.LHW)	;CLEAR LH WORD FLAG
	DATAO	KLP,T4		;LOAD THE ADDR REGISTER
	DATAO	KLP,T1		;WRITE RH DATA
	CAIE	P2,7777		;HIT END?
	AOJA	P2,KLPLD2	;NO, LOOP
	MOVE	T1,P1		;POINT TO MICROCODE LOADER BLOCK
	PUSHJ	P,BTURPT##	;REPORT A SUCESSFUL LOAD
	MOVE	T1,.ULVER(P1)	;GET VERSION NUMBER
	POPJ	P,		;RETURN
	SUBTTL	SET MEMORY OFFLINE SUPPORT


;ROUTINE CALLED WHEN MONITOR MEMORY WILL BE SET OFFLINE.
;MARK EACH KLIPA TO BE SHUT DOWN BY THE ONCE/SECOND CODE.
;CALL:
;	PUSHJ	P,PPDMFL
;RETURN:
;	CPOPJ ALWAYS

PPDMFL::PUSHJ	P,SAVQ##	;SAVE SOME PRESERVED REGISTERS
	MOVEI	Q1,.C0CDB##	;FIRST CPU DATA BLOCK
PPDMF1:	SKIPN	Q3,.CPPCB##-.CPCDB##(Q1) ;THIS CPU HAVE A KLIPA?
	JRST	PPDMF2		;NO
	MOVX	T1,ST.MFL	;GET THE APPROPRIATE FLAG
	IORM	T1,.PCSTS(Q3)	;REMEMBER TO SHUTDOWN THE KLIPA
PPDMF2:	HLRZ	Q1,.CPCDB##-.CPCDB##(Q1) ;GET LINK TO NEXT CDB
	JUMPN	Q1,PPDMF1	;LOOP FOR OTHER CPUS
	POPJ	P,		;RETURN
;ROUTINE CALLED WHEN MONITOR MEMORY HAS BEEN SET OFFLINE AND IT
;IS SAFE TO RESTORE THE STATE OF THE WORLD.
;CALL:
;	PUSHJ	P,PPDMON
;RETURN:
;	CPOPJ ALWAYS

PPDMON::MOVE	T1,.CPCPN##	;GET OUR CPU NUMBER
	MOVE	T1,BITTBL##(T1)	;GET THAT BIT
	TDNE	T1,IPAMSK##	;WANT TO SKIP STARTING THE CI?
	POPJ	P,		;DO NOTHING
	PUSHJ	P,BHDCLR	;CLEAR THE BHD AREA
	PUSHJ	P,BSDLNK	;RE-LINK THE BSD AREA
	PUSHJ	P,SAVQ##	;SAVE SOME PRESERVED REGISTERS
	MOVEI	Q1,.C0CDB##	;FIRST CPU DATA BLOCK
PPDMO1:	SKIPN	Q3,.CPPCB##-.CPCDB##(Q1) ;THIS CPU HAVE A KLIPA?
	JRST	PPDMO2		;NO
	MOVX	T1,ST.MFL	;GET THE MEMORY OFFLINE FLAG
	TDNN	T1,.PCSTS(Q3)	;WAS THE KLIPA SHUT DOWN?
	JRST	PPDMO2		;NO
	MOVX	T2,ST.RES	;FLAG WE CAN RESTART THE KLIPA NOW
	IORM	T2,.PCSTS(Q3)	;...
	ANDCAM	T1,.PCSTS(Q3)	;CLEAR THE MEMORY OFFLINE FLAG
PPDMO2:	HLRZ	Q1,.CPCDB##-.CPCDB##(Q1) ;GET LINK TO NEXT CDB
	JUMPN	Q1,PPDMO1	;LOOP FOR OTHER CPUS
	POPJ	P,		;RETURN
	SUBTTL	REMOVE A CPU


;ROUTINE CALLED WHEN REMOVING A CPU.
;CALL:
;	PUSHJ	P,PPDRMV
;RETURN:
;	CPOPJ ALWAYS

PPDRMV::SE1ENT			;RUN IN NZS
	PUSHJ	P,SAVQ##	;SAVE THE "Q" REGISTERS
	SKIPN	Q3,.CPPCB##	;GET PCB ADDRESS
	POPJ	P,		;NO KLIPA ON THIS CPU
	MOVX	T1,ST.MAI	;SEE IF IN MAINTENANCE MODE
	TDNE	T1,.PCSTS(Q3)	;...
	POPJ	P,		;DON'T CAUSE OTHER PROBLEMS
	PJRST	PPDS10		;SHUT DOWN THE KLIPA, TELL SCA, ETC, AND RETURN
	SUBTTL	KLIPA ONCE PER TICK CODE


;ONCE A TICK CODE FOR THE KLIPA.
;CALL:
;	PUSHJ	P,PPDTIC
;RETURN:
;	CPOPJ ALWAYS

PPDTIC::PUSHJ	P,SAVQ##	;SAVE THE "Q" REGISTERS
	SE1ENT			;RUN IN NZS
	SKIPN	Q3,.CPPCB##	;GET PCB ADDRESS
	POPJ	P,		;NO KLIPA ON THIS CPU
	MOVE	T1,.PCSTS(Q3)	;GET STATUS
	TXNN	T1,ST.DED!ST.MAI ;SKIP IF DEAD OR IN MAINTENANCE MODE
	TXNN	T1,ST.CQA	;DO WE NEED TO KICK THE KLIPA?
	POPJ	P,		;RETURN
	MOVX	T1,ST.CQA	;GET THE "KICK KLIPA" BIT
	ANDCAM	T1,.PCSTS(Q3)	;CLEAR THE BIT
	MOVE	T1,.PCPIA(Q3)	;GET PI ASSIGNMENT
	CONO	KLP,CO.CQA+CO.BTS(T1) ;KICK THE PORT
	POPJ	P,		;NOT MUCH ELSE TO DO
	SUBTTL	KLIPA ONCE PER SECOND CODE


;ONCE A SECOND CODE FOR THE KLIPA.
;CALL:
;	PUSHJ	P,PPDSEC
;RETURN:
;	CPOPJ ALWAYS

PPDSEC::PUSHJ	P,SAVPQ##	;SAVE LOTS OF AC'S
	SE1ENT			;RUN IN NZS
	SKIPN	Q3,.CPPCB##	;GET PCB ADDRESS
	POPJ	P,		;NO KLIPA ON THIS CPU?

;FIRST CHECK FOR UNUSUAL CONDITIONS

	MOVE	T1,.PCSTS(Q3)	;GET THE IMPORTANT FLAGS
	TXNE	T1,ST.MAI	;IS KLIPA IN MAINTENANCE MODE?
	POPJ	P,		;YES, GO AWAY
	TXNE	T1,ST.MFL	;MEMORY GOING OFFLINE?
	JRST	PPDS10		;YES
	TXNE	T1,ST.RES	;TIME TO RESTART KLIPA?
	JRST	PPDS20		;YES

;NOTHING UNUSUAL, SEE IF KLIPA IS READY TO ROLL

	TXNE	T1,ST.DED	;KLIPA DEAD?
	POPJ	P,		;YES, QUIT
	CONI	KLP,T1		;GET KLIPA STATUS
	TXNN	T1,CI.ECP	;ENABLE COMPLETE?
	POPJ	P,		;NO
	TXNE	T1,CI.ENA	;YES, ENABLED?
	JRST	PPDSC1		;YES
	BUG.	(CHK,KLPNEN,KLPSER,SOFT,<CI20 not enabled>,,)
	PJRST	URDEAD		;START OVER

;KLIPA IS RUNNING, DO ALL THE CHECKS

PPDSC1:	MOVX	T1,ST.RDY	;IS KLIPA READY TO ROLL?
	TDNN	T1,.PCSTS(Q3)	;...
	PJRST	PPDCSS		;NO, MUST DO COMMON START-UP STUFF
	PUSHJ	P,CHKKAF	;CHECK FOR KEEP ALIVE FAILURE
	  POPJ	P,		;SKIP REST OF CHECKS THIS SECOND
	PUSHJ	P,CHKPCB	;CHECK THE PCB
	PUSHJ	P,CHKWIR	;CHECK CI WIRES
	PUSHJ	P,CHKRTO	;CHECK FOR REQUEST-ID TIMEOUTS
	PUSHJ	P,CHKSTO	;CHECK FOR START SEQUENCE TIMEOUTS
	PUSHJ	P,CHKOPC	;CHECK FOR DEFERRED OPENS AND CLOSES
	PUSHJ	P,CHKPOL	;POLL THE NEXT NODE WITH A REQUEST-ID
	PUSHJ	P,CHKCTR	;CHECK FOR PERIODIC READ-COUNTERS
	POPJ	P,		;RETURN
;HERE IF JUST (RE)STARTED THE KLIPA - MUST ASK FOR IT'S CI NODE
;NUMBER AND SEND REQUEST-ID PACKETS TO EACH NODE ON THE NETWORK.

PPDCSS:	SKIPL	.PCONN(Q3)	;OUR NODE NUMBER KNOWN?
	JRST	PPDCS2		;YES, DO REQUEST-ID POLLING
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;TRY AGAIN LATER
	MOVEI	T1,.RGNOD	;REGISTER TO READ
	PUSHJ	P,KLPRRG	;SEND A READ REGISTER PACKET
	MOVEI	T1,100000	;GET A SPIN COUNT
PPDCS1:	SKIPL	.PCONN(Q3)	;OUR NODE NUMBER RETURNED YET?
	JRST	PPDCS2		;YES, PROCEED
	SOJG	T1,PPDCS1	;NO, WAIT FOR A WHILE
	POPJ	P,		;SORRY

PPDCS2:	PUSHJ	P,RSTRID	;ZERO TIMERS AND STATUS
	SETZ	Q1,		;START WITH FIRST NODE NUMBER
PPDCS3:	CAMN	Q1,.PCONN(Q3)	;SAME AS OUR NODE NUMBER?
	JRST	PPDCS4		;YES, DON'T ASK
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	PPDCS5		;NONE AVAILABLE, LET PPDSEC DO IT
	SETZ	T3,		;LET THE KLIPA SELECT A PATH, NO RESPONSE NEEDED
	PUSHJ	P,KLPRID	;SEND A REQUEST-ID

PPDCS4:	CAIGE	Q1,MAXNDS-1	;TRIED ALL NODES?
	AOJA	Q1,PPDCS3	;NO, LOOP FOR REMAINDER

PPDCS5:	MOVEI	T1,CTRTIM	;SECONDS UNTIL FIRST PERIODIC READ-COUNTERS
	MOVEM	T1,.PCCTM(Q3)	;START IT OFF RIGHT
	MOVX	T1,ST.RDY	;SHOW KLIPA IS READY FOR USE
	IORM	T1,.PCSTS(Q3)	;...
	POPJ	P,		;RETURN
;ROUTINE TO CHECK FOR KLIPA KEEP ALIVE FAILURE.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKKAF
;RETURN:
;	CPOPJ IF KLIPA RESTARTED/RELOADED
;	CPOPJ1 IF KLIPA STILL APPEARS TO BE RUNNING

CHKKAF:	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	SUB	T1,.PCKRT(Q3)	;CALCULATE TIME SINCE LAST KLIPA RESPONSE
	IMULI	T1,^D1000	;CONVERT TO MILLISECONDS
	IDIV	T1,TICSEC##	;...
	CAIGE	T1,KAFTIM	;TOO LONG SINCE LAST RESPONSE?
	JRST	CHKKF1		;NO, GO CHECK KLIPA IDLE TIMER
	CONI	KLP,.PCKCI(Q3)	;SAVE CONI FOR LATER SNOOPING
	CONO	KLP,CO.CPT	;MAKE SURE KLIPA IS REALLY STOPPED
	STOPCD	.+1,INFO,KLPKAF	;++KLIPA KEEP ALIVE FAILED
	AOS	.PCKAC(Q3)	;UPDATE COUNT OF KLIPA KEEP ALIVE FAILURES
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PCKAT(Q3)	;REMEMBER TIME OF LAST KEEP ALIVE FAILURE
	PJRST	URDEAD		;DO KLIPA ERROR STOP PROCESSING AND RETURN

CHKKF1:	AOS	(P)		;SET FOR SKIP RETURN, LOOKS LIKE KLIPA IS RUNNING
	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	SUB	T1,.PCKCT(Q3)	;CALCULATE TIME SINCE LAST COMMAND QUEUED
	IMULI	T1,^D1000	;CONVERT TO MILLISECONDS
	IDIV	T1,TICSEC##	;...
	CAIGE	T1,IDLTIM	;TOO LONG SINCE LAST COMMAND?
	POPJ	P,		;NO, ALL DONE
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;WE TRIED, TRY AGAIN NEXT SECOND
	MOVEI	T1,.RGNOD	;WHICH REGISTER TO READ
	PJRST	KLPRRG		;SEND THE READ-REGISTER PACKET AND RETURN
;ROUTINE TO CHECK SOME LOCATIONS IN THE PCB.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKPCB
;RETURN:
;	CPOPJ ALWAYS

CHKPCB:	MAP	T1,0(Q3)	;GET PHYSICAL ADDRESS
	TXZ	T1,MP.NAD	;CLEAR NON-ADDRESS BITS
	CAME	T1,.PCPBA(Q3)	;HAS IT CHANGED WITHOUT US KNOWING?
CHKPC1:	BUG.	(HLT,KLPPIC,KLPSER,SOFT,<PCB is corrupted>,,)
	MOVE	T1,.PCMQE(Q3)	;GET MESSAGE SIZE
	CAIE	T1,C%MGSZ	;PCB STILL CORRECT?
	JRST	CHKPC1		;NO
	CONI	KLP,T1		;READ CSR
	ANDI	T1,CI.PIA	;KEEP JUST PI ASSIGNMENT
	CAMN	T1,.PCPIA(Q3)	;SAME AS WHAT PCB SAYS IT IS?
	POPJ	P,		;YES, OK
	BUG.	(INF,KLPPIA,KLPSER,HARD,<CI20 has lost its PI assignment>,,)
	PJRST	URDEAD		;TRY TO START OVER
;ROUTINE TO CHECK THE CI WIRES BY SENDING LOOPBACK PACKETS.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKWIR
;RETURN:
;	CPOPJ ALWAYS

CHKWIR:	MOVE	Q1,.PCONN(Q3)	;GET OUR NODE NUMBER
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;DON'T SWEAT IT
	MOVE	T2,LPBCRC(Q1)	;GET THE PROPER CRC
	MOVEM	T2,LPBLEN-1(Q2)	;STASH THE CRC
	MOVEI	T1,OP.LPB	;OPCODE
	MOVEI	T2,KLPDRG	;PRIORITY
	MOVX	T3,ST.PTH	;WHICH PATH WAS LAST LOOPBACK SENT ON?
	TDNN	T3,.PCSTS(Q3)	;...
	JRST	CHKWI1		;PATH A, USE PATH B THIS TIME
	ANDCAM	T3,.PCSTS(Q3)	;CLEAR FLAG
	MOVX	T3,PF.PT0	;GET THE PATH SELECT BIT
	JRST	CHKWI2		;SEND THE PACKET

CHKWI1:	IORM	T3,.PCSTS(Q3)	;SET FLAG
	MOVX	T3,PF.PT1	;GET THE PATH SELECT BIT
CHKWI2:	SETZM	.PKLEN(Q2)	;NO LENGTH
	PJRST	DRVSND		;SEND THE PACKET AND RETURN
;ROUTINE TO CHECK FOR REQUEST-ID TIMEOUTS.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKRTO
;RETURN:
;	CPOPJ ALWAYS

CHKRTO:	SETZ	Q1,		;START WITH NODE NUMBER ZERO
CHKRT1:	MOVE	P1,Q3		;GET PCB ADDRESS
	ADD	P1,Q1		; OFFSET BY CI NODE NUMBER
	SKIPE	T1,.PCRIT(P1)	;TIMER ON?
	CAMLE	T1,DATE##	;YES, TIMED OUT?
	JRST	CHKRT6		;NO, NEXT NODE
	MOVE	T1,.PCRIS(P1)	;GET REQUEST-ID STATUS
	TXNN	T1,RI.TRY	;WAS THIS THE FIRST TIME?
	JRST	CHKRT5		;YES, TRY A SECOND TIME
	TXNN	T1,RI.WFR	;STILL WAITING FOR PORT TO RESPOND?
	JRST	CHKRT2		;NO
	BUG.	(INF,KLPHNG,KLPSER,HARD,<CI20 is hung>,,)
	PJRST	URDEAD		;TRY TO START OVER

;LOOKS LIKE THE REMOTE PORT IS SICK

CHKRT2:	LOAD	T1,IDNOR,(P1)	;GET NUMBER OF NO RESPONSES
	CAIGE	T1,MAXNOR	;HAVE WE TRIED ENOUGH?
	JRST	CHKRT3		;NO, TRY AGAIN
	BUG.	(INF,KLPNOR,KLPSER,SOFT,<Remote port is sick>,<<Q1,NODE>>,)
	SETZRO	IDNOR,(P1)	;START OVER
	JRST	CHKRT4		;...

CHKRT3:	ADDI	T1,1		;INCREMENT COUNT
	STOR	T1,IDNOR,(P1)	;...
CHKRT4:	MOVX	T1,RI.TRY!RI.WFR ;CLEAR SECOND TRY FLAG, WAITING FOR RESPONSE
	ANDCAM	T1,.PCRIS(P1)	;...
	POPJ	P,		;WAIT FOR MORE HAVOC
;THE FIRST REQUEST-ID TRY HAS TIMED OUT

CHKRT5:	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;NONE AVAILABLE, TRY AGAIN LATER
	MOVX	T1,RI.TRY!RI.WFR ;SECOND TRY, WAITING FOR RESPONSE
	IORB	T1,.PCRIS(P1)	;SET THE FLAGS
	MOVX	T3,PF.RSP	;SAY WE WANT A RESPONSE
	TXNN	T1,RI.PTH	;WHICH PATH SHOULD WE SEND IT ON?
	TXOA	T3,PF.PT0	;PATH A
	TXO	T3,PF.PT1	;PATH B
	PUSHJ	P,KLPRID	;SEND THE REQUEST-ID
	PUSHJ	P,NXTRID	;MOVE POLLER TO NEXT NODE
CHKRT6:	CAIL	Q1,MAXNDS-1	;WAS THIS THE LAST NODE?
	POPJ	P,		;YES, RETURN
	AOJA	Q1,CHKRT1	;CHECK NEXT NODE
;ROUTINE TO CHECK START SEQUENCE TIMERS.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKSTO
;RETURN:
;	CPOPJ ALWAYS

CHKSTO:	SETZ	Q1,		;START WITH NODE NUMBER ZERO
CHKST1:	MOVE	P1,Q3		;GET PCB ADDRESS
	ADD	P1,Q1		; OFFSET BY CI NODE NUMBER
	SKIPN	P5,.PCPBK(P1)	;GET A PATH BLOCK ADDRESS
	JRST	CHKST2		;NOTHING THERE
	SKIPE	T1,.PBSST(P5)	;GET EXPIRATION TIME, SKIP IF NOT RUNNING
	CAMLE	T1,DATE##	;TIMER EXPIRED?
	JRST	CHKST2		;NO, TRY NEXT ONE
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	CHKST2		;NONE AVAILABLE, TRY AGAIN NEXT SECOND
	LOAD	T1,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	CAIE	T1,VC.STS	;START-SENT?
	SKIPA	T1,[PP.STK]	;NO, GET STACK CODE
	MOVEI	T1,PP.STA	;YES, GET START CODE
	PUSHJ	P,STRTDT	;MAKE THE PACKET
	PUSHJ	P,KLPSDG	;SEND IT
	PUSHJ	P,STSST		;START THE TIMER
CHKST2:	CAIL	Q1,MAXNDS-1	;WAS THIS THE LAST NODE?
	POPJ	P,		;YES, RETURN
	AOJA	Q1,CHKST1	;CHECK NEXT NODE
;ROUTINE TO PERFORM REGULAR REQUEST-ID POLLING.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKPOL
;RETURN:
;	CPOPJ ALWAYS

CHKPOL:	MOVE	Q1,.PCRIN(Q3)	;GET POLLER'S NEXT NODE
	PUSHJ	P,CHKIDT	;IS REQUEST-ID TIMER ALREADY RUNNING?
	  PJRST	INCRID		;YES, MOVE POLLER TO NEXT NODE
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;NONE AVAILABLE, TRY AGAIN LATER
	PUSHJ	P,GTNPTH	;GET NEXT PATH FOR REQUEST-ID
	PUSHJ	P,KLPRID	;SEND THE REQUEST-ID
	PJRST	INCRID		;MOVE POLLER TO NEXT NODE AND RETURN


;ROUTINE TO PERFORM PERIODIC READ-COUNTERS COMMAND.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKCTR
;RETURN:
;	CPOPJ ALWAYS

CHKCTR:	SOSLE	.PCCTM(Q3)	;TIME TO SEND THE NEXT ONE?
	POPJ	P,		;NOT YET
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;NONE AVAILABLE, TRY AGAIN NEXT SECOND
	MOVEI	T1,CTRTIM	;RESET TIMEOUT
	MOVEM	T1,.PCCTM(Q3)	;...
	MOVX	T2,KS%PER	;REASON FOR READ-COUNTERS
	PJRST	KLPRPT		;SEND THE PACKET AND RETURN
;ROUTINE TO CHECK FOR DEFERRED CLOSES AND OPENS.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKOPC
;RETURN:
;	CPOPJ ALWAYS

CHKOPC:	SETZ	Q1,		;START WITH NODE NUMBER ZERO
CHKOP1:	MOVE	P1,Q3		;GET PCB ADDRESS
	ADD	P1,Q1		; OFFSET BY CI NODE NUMBER
	SKIPN	P5,.PCPBK(P1)	;IS THERE PATH TO THIS NODE?
	JRST	CHKOP3		;NO
	MOVX	T1,PB.NTC	;DOES IT NEED TO BE CLOSED?
	TDNN	T1,.PBFLG(P5)	;...
	JRST	CHKOP2		;NO
	PUSHJ	P,KLPGDB	;YES, GET A DATAGRAM BUFFER
	  POPJ	P,		;NONE AVAILABLE, TRY AGAIN LATER
	PUSHJ	P,KLPCLO	;TELL PORT TO CLOSE THE VC
	MOVX	T1,PB.NTC	;NO LONGER TRYING TO CLOSE
	ANDCAM	T1,.PBFLG(P5)	;...
	MOVX	T1,PB.WFI	;FLAG WAITING FOR IDREC
	IORM	T1,.PBFLG(P5)	;...

CHKOP2:	MOVE	T1,.PBFLG(P5)	;GET FLAGS BACK
	TXNE	T1,PB.OKO	;OK TO OPEN?
	TXNN	T1,PB.WFI	;YES, STILL WAITING FOR A NEW IDREC?
	JRST	CHKOP3		;NOT OK TO OPEN OR NOT WAITING FOR AN IDREC
	PUSHJ	P,CHKIDT	;ALREADY HAVE A REQUEST-ID OUTSTANDING?
	  JRST	CHKOP3		;YES, DON'T SEND ANOTHER
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;NONE AVAILABLE, TRY AGAIN LATER
	PUSHJ	P,GTNPTH	;GET NEXT PATH FOR REQUEST-ID
	PUSHJ	P,KLPRID	;SEND THE REQUEST-ID
CHKOP3:	CAIL	Q1,MAXNDS-1	;WAS THIS THE LAST NODE?
	POPJ	P,		;YES, RETURN
	AOJA	Q1,CHKOP1	;CHECK NEXT NODE
;MEMORY IS GOING TO BE SET OFFLINE WHICH WILL AFFECT THE KLIPA.
;SHUT IT DOWN IN AN ORDERLY FASHION SO IT CAN BE RESTARTED LATER.

PPDS10:	MOVX	T1,ST.DED	;HAVE WE ALREADY DONE THIS?
	TDNE	T1,.PCSTS(Q3)	;...
	POPJ	P,		;YES, NOTHING MORE TO DO
	CIOFF			;PREVENT RACES
	PUSHJ	P,STPKLP	;STOP THE KLIPA
	PUSHJ	P,CIGONE	;RESET ALL CI ACTIVITY
	CION			;ALLOW INTERRUPTS AGAIN
	SETZM	.PCFQC(Q3)	;INITIALIZE THE COUNTER
PPDS12:	XMOVEI	T1,.PCDFQ(Q3)	;POINT AT DATAGRAM FREE QUEUE
	PUSHJ	P,REMQUE	;GET AN ENTRY FROM THE QUEUE
	  JRST	PPDS13		;EMPTY
	MOVE	T1,Q2		;COPY PACKET ADDRESS
	PUSHJ	P,SC.RLD##	;GIVE IT BACK TO SCA
	MOVSI	T1,1		;COUNT ANOTHER ONE
	ADDM	T1,.PCFQC(Q3)	;...
	JRST	PPDS12		;LOOP FOR MORE

PPDS13:	XMOVEI	T1,.PCMFQ(Q3)	;POINT AT MESSAGE FREE QUEUE
	PUSHJ	P,REMQUE	;GET AN ENTRY FROM THE QUEUE
	  JRST	PPDS14		;EMPTY
	MOVE	T1,Q2		;COPY PACKET ADDRESS
	PUSHJ	P,SC.RBF##	;GIVE IT BACK TO SCA
	MOVEI	T1,1		;COUNT ANOTHER ONE
	ADDM	T1,.PCFQC(Q3)	;...
	JRST	PPDS13		;LOOP FOR MORE

PPDS14:
;	XMOVEI	T1,.PCRFQ(Q3)	;POINT AT RESERVED FREE QUEUE
;	PUSHJ	P,REMQUE	;GET AN ENTRY FROM THE QUEUE
;	  JRST	PPDS15		;EMPTY
;	MOVE	T1,Q2		;COPY PACKET ADDRESS
;	PUSHJ	P,SC.RRD##	;GIVE IT BACK TO SCA
;	MOVEI	T1,1		;COUNT ANOTHER ONE
;	ADDM	T1,.PCXXX(Q3)	;...
;	JRST	PPDS14		;LOOP FOR MORE

;PPDS15:
	POPJ	P,		;RETURN
;MEMORY IS STABLE AFTER BEING SET OFFLINE.  RESTART THE KLIPA
;AFTER POSSIBLY RE-LINKING THE FREE QUEUES.

PPDS20:	PUSHJ	P,PCBINI	;INITIALIZE THE PCB
	HLRZ	T1,.PCFQC(Q3)	;GET RESTOCK COUNT
	JUMPE	T1,PPDS21	;JUMP IF NOTHING THERE ORIGINALLY
	PUSHJ	P,SC.ALD##	;ASK SCA FOR THE DATAGRAMS BACK
	  JRST	PPDS21		;PROBABLY SHOULD STOPCD HERE
	MOVE	Q2,T1		;COPY ADDRESS OF FIRST PACKET
	XMOVEI	T1,.PCDFQ(Q3)	;POINT AT DATAGRAM FREE QUEUE
	PUSHJ	P,LNKQUE	;PUT THE PACKETS BACK ON THE FREE QUEUE
PPDS21:	HRRZ	T1,.PCFQC(Q3)	;GET RESTOCK COUNT
	JUMPE	T1,PPDS22	;JUMP IF NOTHING THERE ORIGINALLY
	PUSHJ	P,SC.ABF##	;ASK SCA FOR THE MESSAGES BACK
	  JRST	PPDS22		;PROBABLY SHOULD STOPCD HERE
	MOVE	Q2,T1		;COPY ADDRESS OF FIRST PACKET
	XMOVEI	T1,.PCMFQ(Q3)	;POINT AT MESSAGE FREE QUEUE
	PUSHJ	P,LNKQUE	;PUT THE PACKETS BACK ON THE FREE QUEUE
PPDS22:
;GET COUNT, GET DATAGRAMS HERE
;	XMOVEI	T1,.PCRFQ(Q3)	;POINT AT RESERVED FREE QUEUE
;	PUSHJ	P,LNKQUE	;PUT THE PACKETS BACK ON THE FREE QUEUE
PPDS23:	SETZM	.PCFQC(Q3)	;ZAP OUT RESTOCK COUNT
	MOVX	T1,ST.RES	;GET THE RESTART FLAG
	ANDCAM	T1,.PCSTS(Q3)	;KLIPA NO LONGER NEEDS RESTARTING
	PJRST	RLDKLP		;RELOAD THE KLIPA AND GET IT GOING
;ROUTINE TO UPDATE REQUEST-ID POLLER'S NEXT NODE.
;CALL:
;	Q1/ NODE NUMBER
;	Q3/ PCB ADDRESS
;	PUSHJ	P,NXTRID
;RETURN:
;	CPOPJ ALWAYS

NXTRID:	CAMN	Q1,.PCRIN(Q3)	;IS THIS THE NEXT NODE TO POLL?
	PJRST	INCRID		;YES, INCREMENT AND RETURN
	POPJ	P,		;RETURN


;ROUTINE TO INCREMENT THE POLLER'S NEXT NODE NUMBER.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,INCRID
;RETURN:
;	CPOPJ ALWAYS

INCRID:	AOS	T1,.PCRIN(Q3)	;BUMP THE NEXT NODE NUMBER
	CAILE	T1,MAXNDS-1	;TOO HIGH?
INCRI1:	SETZB	T1,.PCRIN(Q3)	;YES, WRAP TO ZERO
	CAMN	T1,.PCONN(Q3)	;IS IT OUR NODE?
	JRST	INCRID		;YES, DON'T TALK TO OURSELVES
	POPJ	P,		;RETURN


;ROUTINE TO RESET THE REQUEST-ID DATA IN THE PCB.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,RSTRID
;RETURN:
;	CPOPJ ALWAYS

RSTRID:	MOVEI	T1,MAXNDS-1	;NUMBER OF WORDS TO ZERO
	XMOVEI	T2,.PCRIS(Q3)	;POINT AT REQUEST-ID STATUS WORDS
	XMOVEI	T3,.PCRIS+1(Q3)	;POINT AT SECOND WORD OF IT
	SETZM	(T2)		;ZERO FIRST WORD
	EXTEND	T1,[XBLT]	;ZERO THE REMAINDER
	MOVEI	T1,MAXNDS-1	;NUMBER OF WORDS TO ZERO
	XMOVEI	T2,.PCRIT(Q3)	;POINT AT REQUEST-ID TIMER WORDS
	XMOVEI	T3,.PCRIT+1(Q3)	;POINT AT SECOND WORD OF IT
	SETZM	(T2)		;ZERO FIRST WORD
	EXTEND	T1,[XBLT]	;ZERO THE REMAINDER
	PJRST	INCRI1		;RESET POLLER'S NEXT NODE TO ZERO AND RETURN
;GET CURRENT PATH FOR REQUEST-ID.
;CALL:
;	T1/ PACKET STATUS WORD
;	Q1/ NODE NUMBER
;	Q3/ PCB ADDRESS
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T3/ FLAGS FIELD SET WITH PATH SELECT

GTCPTH:	MOVE	T2,Q3		;GET PCB ADDRESS
	ADD	T2,Q1		; OFFSET FOR THIS NODE
	TXNN	T1,PF.PT0	;DID PACKET ARRIVE ON PATH A?
	JRST	GTCPT1		;NO
	MOVX	T1,RI.PTH	;INDICATE PATH A USED (CLEAR FLAG)
	ANDCAM	T1,.PCRIS(T2)	;...
	MOVX	T3,PF.PT0	;USE PATH A FOR NEXT REQUEST-ID
	POPJ	P,		;RETURN

GTCPT1:	MOVX	T1,RI.PTH	;INDICATE PATH B USED (SET FLAG)
	IORM	T1,.PCRIS(T2)	;...
	MOVX	T3,PF.PT1	;USE PATH B FOR NEXT REQUEST-ID
	POPJ	P,		;RETURN


;GET NEXT PATH FOR REQUEST-ID.
;CALL:
;	Q1/ NODE NUMBER
;	Q3/ PCB ADDRESS
;	PUSHJ	P,GTNPTH
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T3/ FLAGS FIELD SET WITH PATH SELECT

GTNPTH:	MOVE	T1,Q3		;GET PCB ADDRESS
	ADD	T1,Q1		; OFFSET FOR THIS NODE
	MOVX	T2,RI.PTH	;WAS LAST REQUEST-ID ON PATH A?
	TDNN	T2,.PCRIS(T1)	;...
	JRST	GTNPT1		;YES, USE PATH B THIS TIME
	ANDCAM	T2,.PCRIS(T1)	;CLEAR FLAG
	MOVX	T3,PF.PT0	;GET PATH SELECT BIT
	POPJ	P,		;RETURN

GTNPT1:	IORM	T2,.PCRIS(T1)	;SET FLAG
	MOVX	T3,PF.PT1	;GET PATH SELECT BIT
	POPJ	P,		;RETURN
;ROUTINE TO CHECK IF THE REQUEST-ID TIMER IS RUNNING FOR A NODE.
;CALL:
;	Q1/ NODE NUMBER
;	Q3/ PCB ADDRESS
;	PUSHJ	P,CHKRIT
;RETURN:
;	CPOPJ IF TIMER IS RUNNING
;	CPOPJ1 IF TIMER IS NOT RUNNING

CHKIDT:	MOVE	T1,Q3		;GET PCB ADDRESS
	ADD	T1,Q1		; OFFSET FOR THIS NODE
	SKIPN	.PCRIT(T1)	;TIMER RUNNING?
	AOS	(P)		;NO, SET FOR SKIP RETURN
	POPJ	P,		;RETURN


;ROUTINE TO SET START SEQUENCE TIMER.
;CALL:
;	P5/ PBK ADDRESS
;	PUSHJ	P,STSST
;RETURN:
;	CPOPJ ALWAYS

STSST:	MOVEI	T1,STSTIM*3	;CONVERT TIMER LENGTH TO UDT INCREMENTS
	ADD	T1,DATE##	;WHEN TIMER WILL EXPIRE
	MOVEM	T1,.PBSST(P5)	;SET THE EXPIRATION TIME
	POPJ	P,		;RETURN
	SUBTTL	SCS-PPD INTERFACE - SEND DATAGRAM/MESSAGE


;ROUTINE TO SEND A DATAGRAM.
;CALL:
;	BLCAL.	(PPDSDG,<SS.PBI,SS.PKT,SS.LEN,SS.FLG,SS.PRI,SS.PTH>)
;
;WHERE:
;	SS.PBI - DESTINATION PATH BLOCK INDEX
;	SS.PKT - ADDRESS OF PACKET
;	SS.LEN - LENGTH OF PACKET
;	SS.FLG - FLAGS (F.XXX)
;	SS.PRI - COMMAND QUEUE PRIORITY
;	SS.PTH - PATH SELECT (0 = AUTO, 1 = A, 2 = B)
;
;RETURN:
;	CPOPJ IF ERRORS
;	CPOPJ1 IF SUCCESS

PPDSDG::BLSUB.	(<SS.PBI,SS.PKT,SS.LEN,SS.FLG,SS.PRI,SS.PTH>)
	MOVEI	T1,OP.SDG	;OPCODE = SEND DATAGRAM
	JRST	SNDPKT		;JOIN COMMON CODE


;ROUTINE TO SEND A MESSAGE.
;CALL:
;	BLCAL.	(PPDSMS,<SS.PBI,SS.PKT,SS.LEN,SS.FLG,SS.PRI,SS.PTH>)
;
;WHERE:
;	SS.PBI - DESTINATION PATH BLOCK INDEX
;	SS.PKT - ADDRESS OF PACKET
;	SS.LEN - LENGTH OF PACKET
;	SS.FLG - FLAGS (F.XXX)
;	SS.PRI - COMMAND QUEUE PRIORITY
;	SS.PTH - PATH SELECT (0 = AUTO, 1 = A, 2 = B)
;
;RETURN:
;	CPOPJ IF ERRORS
;	CPOPJ1 IF SUCCESS

PPDSMS::BLSUB.	(<SS.PBI,SS.PKT,SS.LEN,SS.FLG,SS.PRI,SS.PTH>)
	MOVEI	T1,OP.SMS	;OPCODE = SEND MESSAGE
;	JRST	SNDPKT		;JOIN COMMON CODE
;HERE TO SEND THE PACKET - OPCODE IN T1

SNDPKT:	PUSHJ	P,SAVPQ##	;MIND YOUR P'S AND Q'S
	MOVE	P4,T1		;SAVE THE OPCODE IN P4
	SKIPLE	P5,SS.PBI	;GET PATH BLOCK INDEX
	CAIL	P5,C%PBLL	;LEGAL?
	POPJ	P,		;NO, ERROR
	SKIPN	P5,PBLIST##-1(P5) ;GET PATH BLOCK ADDRESS
	POPJ	P,		;NONE?  AN ERROR
	MOVE	Q2,SS.PKT	;GET THE PACKET ADDRESS
	MOVE	Q3,SS.FLG	;GET THE FLAGS
	MOVE	T4,SS.LEN	;GET THE PACKET LENGTH
	TXNN	Q3,F.SPM	;IS THIS HIGH DENSITY, I.E., LENGTH IN WORDS?
	JRST	SNDPK1		;NO
	MOVEI	T1,1(T4)	;YES, GET A COPY, ROUNDED UP FOR LATER DIVIDE
	IMULI	T4,4		;CONVERT WORD COUNT TO BYTE COUNT
	IDIVI	T1,2		;GET THE NUMBER OF EXTRA BYTES LEFT OVER
	ADD	T4,T1		;ADD FRACTIONAL PART TO WHOLE TO GET TOTAL
SNDPK1:	DPB	T4,PKYLEN	;STORE LENGTH IN PACKET
	MOVEI	T4,PP.DG-OP.SDG(P4) ;COMPUTE PPD BYTE
	DPB	T4,PKYPPD	;SAVE IT IN THE PACKET
	PUSHJ	P,MASAGE	;SWAP THE PPD BYTE AND ADJUST PACKET LENGTH
	MOVE	T1,P4		;GET OPCODE IN T1
	MOVE	T2,SS.PRI	;PRIORITY
	SETZ	T3,		;CLEAR FLAGS REGISTER
	TXNE	Q3,F.RTB	;WANT A RESPONSE?
	TXO	T3,PF.RSP	;YES
	TXNE	Q3,F.SPM	;WANT HIGH DENSITY MODE?
	TXO	T3,PF.FMT	;YES
	MOVE	T4,SS.PTH	;GET PATH SELECT INFO
	CAIN	T4,1		;WANT PATH A?
	TXO	T3,PF.PT0	;YES
	CAIN	T4,2		;WANT PATH B?
	TXO	T3,PF.PT1	;YES
	PJRST	SENDVC		;SEND THE PACKET UNDER VIRTUAL CIRCUIT CONTROL
				;NON-SKIP RETURN IF ERROR, SKIP RETURN IF SUCCESS

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	SCS-PPD INTERFACE - BLOCK DATA SERVICE


;ROUTINE TO SEND DATA.
;CALL:
;	BLCAL.	(PPDSND,<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.CID>)
;
;WHERE:
;	SS.SNM - SENDER'S BUFFER NAME
;	SS.RNM - RECEIVER'S BUFFER NAME
;	SS.SOF - SENDER'S BUFFER OFFSET
;	SS.ROF - RECEIVER'S BUFFER OFFSET
;	SS.CID - CONNECTION-ID
;
;RETURN:
;	CPOPJ IF ERRORS
;	CPOPJ1 IF SUCCESS

PPDSND::BLSUB.	(<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.CID>)
	MOVEI	T1,OP.SDT	;OPCODE FOR SEND DATA
	JRST	SNDREC		;JOIN COMMON CODE


;ROUTINE TO REQUEST DATA.
;CALL:
;	BLCAL.	(PPDREQ,<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.CID>)
;
;WHERE:
;	SS.SNM - SENDER'S BUFFER NAME
;	SS.RNM - RECEIVER'S BUFFER NAME
;	SS.SOF - SENDER'S BUFFER OFFSET
;	SS.ROF - RECEIVER'S BUFFER OFFSET
;	SS.CID - CONNECTION-ID
;
;RETURN:
;	CPOPJ IF ERRORS
;	CPOPJ1 IF SUCCESS

PPDREQ::BLSUB.	(<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.CID>)
	MOVEI	T1,OP.RD1	;OPCODE FOR READ DATA
;	JRST	SNDREC		;JOIN COMMON CODE
;HERE TO FILL IN THE PACKET AND SEND IT OFF

SNDREC:	PUSHJ	P,SAVPQ##	;SAVE LOTS OF ACS
	MOVE	Q2,T1		;SAVE OPCODE
	MOVEI	T1,1		;GET ONE BUFFER
	PUSHJ	P,SC.ABF##	; FROM SCA
	  RETBAD ()		;YOU LOST
	EXCH	T1,Q2		;BUFFER ADDRESS IN Q2, OPCODE IN T1
	MOVE	T2,SS.SNM	;SENDER NAME
	MOVE	T3,T2		;IF DOING A SEND, THIS IS OUR BUFFER NAME
	MOVEM	T2,.PKSNM(Q2)	;SAVE WHERE PORT WANTS SENDER NAME
	MOVE	T2,SS.RNM	;RECEIVER'S NAME
	CAIN	T1,OP.RD1	;REQUEST DATA?
	MOVE	T3,T2		;YES, THIS IS OUR BUFFER NAME
	MOVEM	T2,.PKRNM(Q2)	;SAVE WHERE PORT WANTS RECEIVER NAME
	MOVE	T2,SS.SOF	;SENDER OFFSET
	MOVEM	T2,.PKSOF(Q2)	;INTO PACKET
	MOVE	T2,SS.ROF	;RECEIVER OFFSET
	MOVEM	T2,.PKROF(Q2)	;INTO PACKET
	MOVEM	T3,.PKXID(Q2)	;SENDER OR RECEIVER BUFFER NAME INTO XID
	MOVE	T2,SS.CID	;CID INTO OTHER XID
	MOVEM	T2,.PKXID+1(Q2)	;SO WE CAN TELL SCA ON THE INTERRUPT
	$LDCID	T2,T2		;GET CONNECTION BLOCK ADDRESS
	MOVE	P5,.CBPBK(T2)	;PATH BLOCK ADDRESS
	LDB	T2,[POINT BHSIDX,T3,BHPIDX] ;BHD INDEX FROM BUFFER NAME
	ADD	T2,BHDIPT	;POINT AT BHD TABLE ENTRY
	MOVE	T2,.BHLEN(T2)	;GET NIBBLE COUNT FROM BHD
	LSH	T2,-1+4		;CONVERT TO BYTE COUNT AND THEN SHIFT IT
	MOVEM	T2,.PKXLN(Q2)	;SAVE AS TRANSACTION LENGTH
	MOVEI	T2,KLPMED	;PRIORITY
	MOVX	T3,PF.SIZ	;INDICATE 576-BYTE PACKETS
	PUSHJ	P,SENDVC	;SEND UNDER VIRTUAL CIRCUIT CONTROL
	  SKIPA			;IT FAILED
	JRST	CPOPJ1##	;IT SUCCEEDED
	EXCH	T1,Q2		;SAVE ERROR CODE, GET BUFFER ADDRESS BACK
	PUSHJ	P,SC.RBF##	;RETURN BUFFER TO SCA
	MOVE	T1,Q2		;RESTORE ERROR CODE
	RETBAD	()		;PASS ALONG THE ERROR

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	MAINTENANCE INTERFACE - SEND/REQUEST DATA


;ROUTINE TO SEND MAINTENANCE DATA.
;CALL:
;	BLCAL.	(PPDSMD,<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.NOD>)
;
;WHERE:
;	SS.SNM - SENDER'S BUFFER NAME
;	SS.RNM - RECEIVER'S BUFFER NAME
;	SS.SOF - SENDER'S BUFFER OFFSET
;	SS.ROF - RECEIVER'S BUFFER OFFSET
;	SS.NOD - NODE TO SEND PACKET TO
;
;RETURN:
;	CPOPJ IF ERRORS
;	CPOPJ1 IF SUCCESS

PPDSMD::BLSUB.	(<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.NOD>)
	MOVEI	T1,OP.SMD	;OPCODE FOR SEND MAINTENANCE DATA
	JRST	SNDMAI		;JOIN COMMON CODE


;ROUTINE TO REQUEST MAINTENANCE DATA.
;CALL:
;	BLCAL.	(PPDRMD,<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.NOD>)
;
;WHERE:
;	SS.SNM - SENDER'S BUFFER NAME
;	SS.RNM - RECEIVER'S BUFFER NAME
;	SS.SOF - SENDER'S BUFFER OFFSET
;	SS.ROF - RECEIVER'S BUFFER OFFSET
;	SS.NOD - NODE TO SEND PACKET TO
;
;RETURN:
;	CPOPJ IF ERRORS
;	CPOPJ1 IF SUCCESS

PPDRMD::BLSUB.	(<SS.SNM,SS.RNM,SS.SOF,SS.ROF,SS.NOD>)
	MOVEI	T1,OP.RMD	;OPCODE FOR SEND MAINTENANCE DATA
;	JRST	SNDMAI		;JOIN COMMON CODE
;HERE TO FILL IN THE PACKET AND SEND IT OFF

SNDMAI:	PUSHJ	P,SAVPQ##	;SAVE LOTS OF ACS
	MOVE	Q2,T1		;SAVE OPCODE
	SKIPN	Q3,.CPPCB##	;GET PCB ADDRESS
	RETBAD	()		;ERROR
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  RETBAD ()		;YOU LOST
	EXCH	T1,Q2		;BUFFER ADDRESS IN Q2, OPCODE IN T1
	MOVE	T2,SS.SNM	;SENDER NAME
	MOVE	T3,T2		;IF DOING A SEND, THIS IS OUR BUFFER NAME
	MOVEM	T2,.PKSNM(Q2)	;SAVE WHERE PORT WANTS SENDER NAME
	MOVE	T2,SS.RNM	;RECEIVER'S NAME
	CAIN	T1,OP.RD1	;REQUEST DATA?
	MOVE	T3,T2		;YES, THIS IS OUR BUFFER NAME
	MOVEM	T2,.PKRNM(Q2)	;SAVE WHERE PORT WANTS RECEIVER NAME
	MOVE	T2,SS.SOF	;SENDER OFFSET
	MOVEM	T2,.PKSOF(Q2)	;INTO PACKET
	MOVE	T2,SS.ROF	;RECEIVER OFFSET
	MOVEM	T2,.PKROF(Q2)	;INTO PACKET
	MOVEM	T3,.PKXID(Q2)	;SENDER OR RECEIVER BUFFER NAME INTO XID
	LDB	T2,[POINT BHSIDX,T3,BHPIDX] ;BHD INDEX FROM BUFFER NAME
	ADD	T2,BHDIPT	;POINT AT BHD TABLE ENTRY
	MOVE	T2,.BHLEN(T2)	;GET NIBBLE COUNT FROM BHD
	LSH	T2,-1+4		;CONVERT TO BYTE COUNT AND THEN SHIFT IT
	MOVEM	T2,.PKXLN(Q2)	;SAVE AS TRANSACTION LENGTH
	SETZ	T3,		;NO FLAGS
	MOVE	Q1,SS.NOD	;GET THE NODE NUMBER
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	DIASND		;SEND THE DATAGRAM AND RETURN

	ENDBS.			;END OF BLSUB. RANGE
;ROUTINE TO SEND A CLOSE BUFFER COMMAND.
;CALL:
;	BLCAL.	(PPDCLB,<SS.BNM,SS.PKT>)
;
;WHERE:
;	SS.BNM - BUFFER NAME
;	SS.PKT - PACKET ADDRESS
;
;RETURN:
;	CPOPJ ALWAYS

PPDCLB::BLSUB.	(<SS.BNM,SS.PKT>)
	PUSHJ	P,SAVQ##	;SAVE THE Q REGISTERS
	SKIPN	Q3,.CPPCB##	;GET PCB ADDRESS
	POPJ	P,		;NO PCB?
	MOVE	Q2,SS.PKT	;GET THE PACKET ADDRESS
	MOVE	T1,SS.BNM	;GET THE BUFFER NAME
	MOVEM	T1,.PKBNM(Q2)	;STORE THE BUFFER NAME
	MOVEI	T1,OP.CLB	;GET OPCODE
	MOVEI	T2,KLPMED	;GET PRIORITY
	SETZ	Q1,		;CLEAR NODE NUMBER
	MOVX	T3,PF.RSP	;RESPONSE IS REQUESTED
	PJRST	DRVSND		;SEND THE PACKET AND RETURN

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	SCS-PPD INTERFACE - DEQUEUE BUFFERS


;ROUTINE TO DEQUEUE A DATAGRAM/MESSAGE BUFFER FROM THE PCB FREE QUEUE.
;CALL:
;	BLCAL.	(PPDD?B,<SS.PBI>)
;
;WHERE:
;	SS.PBI - PATH BLOCK INDEX
;
;RETURN:
;	CPOPJ IF ERRORS
;	CPOPJ1 IF SUCCESS WITH:
;	T1/ ADDRESS OF BUFFER

PPDDDB::BLSUB.	(<SS.PBI>)
	MOVEI	T2,.PCDFQ	;THE DATAGRAM FREE QUEUE
	JRST	PPDDD1		;JOIN COMMON ROUTINE

PPDDMB::BLSUB.	(<SS.PBI>)
	MOVEI	T2,.PCMFQ	;THE MESSAGE FREE QUEUE
PPDDD1:	SKIPLE	T1,SS.PBI	;CHECK FOR VALID PBI
	CAIL	T1,C%PBLL	;...
	POPJ	P,		;ERROR, INVALID PBI
	PUSHJ	P,SAVQ##	;SAVE THE Q REGISTERS
	MOVE	T1,PBLIST##-1(T1) ;GET THE PATH BLOCK ADDRESS IN T1
	MOVE	Q3,.PBPCB(T1)	;GET THE PCB ADDRESS FROM THE PATH BLOCK
	MOVX	T1,ST.MFL	;MEMORY OFFLINE IN PROGRESS?
	TDNE	T1,.PCSTS(Q3)	;...
	JRST	PPDDD2		;YES, WE HAVE TO PLAY SOME GAMES TO MAKE SCA
				; HAPPY SINCE WE'VE ALREADY GIVEN EVERYTHING
				; ON OUR FREE QUEUE BACK TO SCA
	MOVE	T1,Q3		;GET A COPY IN T1
	ADD	T1,T2		;OFFSET TO PROPER QUEUE
	PUSHJ	P,REMQUE	;REMOVE THE FIRST PACKET
	  JRST	PPDDD3		;EMPTY
	MOVE	T1,Q2		;COPY PACKET ADDRESS
	JRST	CPOPJ1##	;SKIP RETURN

PPDDD2:	CAIE	T2,.PCDFQ	;LOOKING FOR A DATAGRAM?
	JRST	PPDDD6		;NO, GET A MESSAGE
	JRST	PPDDD4		;YES, GET A DATAGRAM

PPDDD3:	XMOVEI	T2,.PCDFQ(Q3)	;GET ADDRESS OF DATAGRAM FREE QUEUE
	CAME	T1,T2		;IS THAT THE QUEUE WE TRIED?
	JRST	PPDDD5		;NO, IT WAS THE MESSAGE FREE QUEUE
	BUG.	(INF,KLPNDB,KLPSER,SOFT,<No datagram buffer>,,)
PPDDD4:	MOVEI	T1,1		;GET A DATAGRAM BUFFER
	PUSHJ	P,SC.ALD##	; FROM SCA'S POOL
	  RETBAD (KLPX11)	;ERROR, RETURN AN ERROR CODE
	JRST	CPOPJ1##	;SUCCESS

PPDDD5:	BUG.	(INF,KLPNMG,KLPSER,SOFT,<No message buffer>,,)
PPDDD6:	MOVEI	T1,1		;GET A MESSAGE BUFFER
	PUSHJ	P,SC.ABF##	; FROM SCA'S POOL
	  RETBAD (KLPX11)	;ERROR, RETURN AN ERROR CODE
	JRST	CPOPJ1##	;SUCCESS

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	SCS-PPD INTERFACE - QUEUE BUFFERS


;ROUTINE TO QUEUE (A) DATAGRAM/MESSAGE BUFFER(S) TO THE PCB FREE QUEUE.
;CALL:
;	BLCAL.	(PPDQ?B,<SS.PBI,SS.PKT>)
;
;WHERE:
;	SS.PBI - PATH BLOCK INDEX
;	SS.PKT - ADDRESS OF FIRST PACKET
;
;RETURN:
;	CPOPJ ALWAYS

PPDQDB::BLSUB.	(<SS.PBI,SS.PKT>)
	MOVEI	T3,.PCDFQ	;THE DATAGRAM FREE QUEUE
	JRST	PPDQD1		;JOIN COMMON ROUTINE

PPDQMB::BLSUB.	(<SS.PBI,SS.PKT>)
	MOVEI	T3,.PCMFQ	;THE MESSAGE FREE QUEUE
PPDQD1:	SKIPLE	T1,SS.PBI	;CHECK FOR VALID PBI
	CAIL	T1,C%PBLL	;...
	PUSHJ	P,KLPFOO	;ERROR, INVALID PBI
	PUSHJ	P,SAVQ##	;SAVE THE Q REGISTERS
	MOVE	T1,PBLIST##-1(T1) ;GET THE PATH BLOCK ADDRESS IN T1
	MOVE	Q3,.PBPCB(T1)	;GET THE PCB ADDRESS FROM THE PATH BLOCK
	MOVX	T1,ST.MFL	;MEMORY OFFLINE IN PROGRESS?
	TDNE	T1,.PCSTS(Q3)	;...
	JRST	PPDQD2		;YES
	MOVE	T1,Q3		;GET A COPY IN T1
	ADD	T1,T3		;OFFSET TO PROPER QUEUE
	MOVE	Q2,SS.PKT	;COPY ADDRESS OF FIRST PACKET
	PJRST	LNKQUE		;LINK THE PACKETS ONTO THE FREE QUEUE AND RETURN

;HERE IF SETTING MEMORY OFFLINE - PROBABLY CALLED FROM SCATMO GIVING BACK
;THE 2 FLOW CONTROL BUFFERS IT QUEUED.  COUNT THE NUMBER OF BUFFERS AND
;REMEMBER TO ASK SCA FOR THAT MANY MORE WHEN RESTARTING.

PPDQD2:	MOVE	T1,SS.PKT	;GET ADDRESS OF FIRST PACKET
	MOVEI	T2,1		;INIT COUNT OF PACKETS
	SKIPE	T1,(T1)		;IS THERE ANOTHER PACKET?
	AOJA	T2,.-1		;YES, COUNT AND LOOP
	CAIN	T3,.PCDFQ	;DATAGRAMS?
	MOVSS	T2		;YES, MOVE COUNT TO LH
	ADDM	T2,.PCFQC(Q3)	;INCLUDE IN RESTOCK COUNT
	MOVE	T1,SS.PKT	;GET ADDRESS OF FIRST PACKET
	CAIE	T3,.PCDFQ	;DATAGRAMS?
	PJRST	SC.RBF##	;NO
	PJRST	SC.RLD##	;YES

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	SCS-PPD INTERFACE - OPEN VIRTUAL CIRCUIT


;ROUTINE TO REQUEST THE OPENING OF A VIRTUAL CIRCUIT.
;CALL:
;	BLCAL.	(PPDOVC,<SS.PBI>)
;
;WHERE:
;	SS.PBI - DESTINATION PATH BLOCK INDEX
;
;RETURN:
;	CPOPJ ALWAYS

PPDOVC::BLSUB.	(<SS.PBI>)
	SKIPLE	T1,SS.PBI	;CHECK FOR VALID PBI
	CAIL	T1,C%PBLL	;...
	PUSHJ	P,KLPFOO	;ERROR, INVALID PBI
	PUSHJ	P,SAVPQ##	;SAVE THE P & Q REGISTERS
	SKIPN	P5,PBLIST##-1(T1) ;GET THE PATH BLOCK ADDRESS
	BUG.	(HLT,KLPNPB,KLPSER,SOFT,<No path block at PPDOVC>,,)
	LOAD	T1,PBVCST,(P5)	;GET THE VIRTUAL CIRCUIT STATE
	CAIE	T1,VC.CLO	;SHOULD BE CLOSED
	BUG.	(HLT,KLPONC,KLPSER,SOFT,<Trying to open a virtual circuit which isn't closed>,,)
	MOVX	T1,PB.OKO	;SAY IT'S OK TO OPEN
	IORB	T1,.PBFLG(P5)	;...
	TXNE	T1,PB.NTC	;TRYING TO CLOSE THE VC?
	POPJ	P,		;YES, THAT'S IT FOR NOW, ONCE/SECOND CODE WILL
				; COMPLETE OUR WORK
	LOAD	Q1,PBDPN,(P5)	;GET DESTINATION PORT NUMBER
	MOVE	Q3,.PBPCB(P5)	;GET THE PCB ADDRESS
	PUSHJ	P,CHKIDT	;IS THE REQUEST-ID TIMER RUNNING?
	  POPJ	P,		;YES, DON'T BOTHER SENDING ANOTHER
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  POPJ	P,		;NONE AVAILABLE
	PUSHJ	P,GTNPTH	;GET NEXT PATH FOR REQUEST-ID
	PJRST	KLPRID		;SEND A REQUEST-ID AND RETURN

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	SCS-PPD INTERFACE - CLOSE VIRTUAL CIRCUIT


;ROUTINE TO REQUEST A VIRTUAL CIRCUIT BE CLOSED.
;CALL:
;	BLCAL.	(PPDCVC,<SS.PBI>)
;
;WHERE:
;	SS.PBI - DESTINATION PATH BLOCK INDEX
;
;RETURN:
;	CPOPJ ALWAYS

PPDCVC::BLSUB.	(<SS.PBI>)
	SKIPLE	T1,SS.PBI	;CHECK FOR VALID PBI
	CAIL	T1,C%PBLL	;...
	PUSHJ	P,KLPFOO	;ERROR, INVALID PBI
	PUSHJ	P,SAVPQ##	;SAVE THE P & Q REGISTERS
	SKIPN	P5,PBLIST##-1(T1) ;GET THE PATH BLOCK ADDRESS
	PUSHJ	P,KLPFOO	;ERROR, INVALID PBI
	SETZB	Q2,P4		;NEED TO GET A BUFFER AND DO A SET CIRCUIT
	SETZ	T2,		;FLAG THAT WE WANT TO SEND A SHUTDOWN MESSAGE
	JRST	CLOSV2		;JOIN COMMON CODE


;ROUTINE TO REQUEST A VIRTUAL CIRCUIT BE CLOSED.  CALLED INTERNALLY
;FROM WITHIN KLPSER AS OPPOSED TO PPDCVC.
;CALL:
;	Q2/ ADDRESS OF BUFFER FOR KLPCLO
;	    0 = NEED TO GET A BUFFER
;	P4/ 0 = DO A SET CIRCUIT
;	   -1 = DON'T NEED TO DO A SET CIRCUIT
;	P5/ PBK
;	PUSHJ	P,CLOSV1
;RETURN:
;	CPOPJ ALWAYS

CLOSV1:	SETO	T2,		;NO SHUTDOWN REQUIRED
CLOSV2:	STKVAR	<BUFADR>	;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	Q2,BUFADR	;SAVE ADDRESS OF BUFFER (IF ANY)
;AT THIS POINT, T2 = 0 IF SCA INITIATED THE CLOSING.  IN THAT CASE,
;WE'LL SEND A SHUTDOWN TO THE OTHER SIDE.  OTHERWISE, T2 = -1 AND THE
;OTHER SIDE PROBABLY KNOWS ABOUT THE FAILURE.

	CIOFF			;DON'T LET INTERRUPT LEVEL CHANGE VC STATE
	LOAD	T1,PBVCST,(P5)	;GET VC STATE
	CAIE	T1,VC.OPN	;OPEN?
	JRST	CLOSV7		;NO, DONE
	MOVEI	T1,VC.CLO	;GET CLOSED CODE
	STOR	T1,PBVCST,(P5)	;SET IT

;TRY TO SEND A SHUTDOWN IF SCA ASKED FOR THE CLOSING.  IF THERE'S NO
;BUFFER, WE CAN CONTINUE, AND THE OTHER SIDE WILL FIND OUT WHEN IT
;GETS A START PACKET SOMETIME LATER.

	LOAD	Q1,PBDPN,(P5)	;GET DESTINATION PORT NUMBER
	MOVE	Q3,.PBPCB(P5)	;GET THE PCB ADDRESS
	MOVE	P1,Q3		;GET PCB ADDRESS
	ADD	P1,Q1		; OFFSET BY CI NODE NUMBER
	JUMPN	T2,CLOSV3	;JUMP IF INTERNAL CALL (NO SHUTDOWN)
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFER
	  JRST	CLOSV3		;NO BUFFER, NOT A DISASTER
	MOVEI	T1,PP.SHT	;GET PPD BYTE FOR SHUTDOWN
	DPB	T1,PKYPPD	;STORE IN PACKET
	PUSHJ	P,KLPSDG	;SEND THE DATAGRAM
CLOSV3:	JUMPN	P4,CLOSV5	;JUMP IF WE DON'T NEED A SET CIRCUIT
	SKIPE	Q2,BUFADR	;DO WE HAVE A BUFFER FOR THE SETCKT?
	JRST	CLOSV4		;YES, GO DO IT
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  SKIPA			;COULDN'T
	JRST	CLOSV4		;OK
	MOVX	T1,PB.NTC	;FAILED, SAY WE STILL NEED TO CLOSE VC
	IORM	T1,.PBFLG(P5)	;...
	JRST	CLOSV6		;TELL SCA WE'RE CLOSED

CLOSV4:	PUSHJ	P,KLPCLO	;SEND THE PACKET
CLOSV5:	MOVX	T1,PB.WFI	;WAITING FOR A FRESH IDREC
	IORM	T1,.PBFLG(P5)	;...
CLOSV6:	LOAD	T1,PBPBI,(P5)	;GET THE PATH BLOCK INDEX
	BLCAL.	(SC.ERR##,<T1>)	;TELL SCA
CLOSV7:	PJRST	CIONPJ##	;INTERRUPTS BACK ON AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	SCS-PPD INTERFACE - MAP A BUFFER


;ROUTINE TO MAP A BUFFER FOR A SUBSEQUENT DMA/MAINTENANCE OPERATION.
;CALL:
;	BLCAL.	(PPDMAP,<SS.BDA>)
;
;WHERE:
;	SS.BDA - ADDRESS OF THE BUFFER DESCRIPTOR
;
;RETURN:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE
;	CPOPJ1 ON SUCCESS WITH:
;	T1/ BUFFER NAME

PPDMAP::BLSUB.	(<SS.BDA>)
	PUSHJ	P,SAVPQ##	;SAVE THE P & Q REGISTERS
	PUSHJ	P,PPDGBH	;GET A BHD
	  RETBAD ()		;NONE AVAILABLE, RETURN ERROR
	MOVE	P1,T2		;SAVE BHD ADDRESS IN P1
	HRRZ	P2,T1		;GET BHD INDEX
	LSH	P2,^D35-BHPIDX	;POSITION IT WHERE THE KLIPA WANTS IT
	PUSHJ	P,PPDGCN	;GET A COMMAND REFERENCE NUMBER
	LSH	T4,^D35-BHPKEY-4 ;POSITION IT WHERE THE PORT WANTS IT
	IOR	P2,T4		;SAVE BUFFER NAME IN P2
	MOVE	T1,SS.BDA	;GET ADDRESS OF BUFFER DESCRIPTOR LIST
	MOVE	T1,.MDFLG(T1)	;GET THE FLAGS WORD
	TXNE	T1,SQ%WRT	;ALLOW WRITE OF HOST MEMORY?
	TXO	T4,BH.WRT	;YES, SET THE WRITABLE BIT IN THE BHD
	TXNE	T1,SQ%CVD	;ALLOW CLEARING OF THE VALID BIT?
	TXO	T4,BH.PRE	;NO, SET THE DO NOT CLEAR VALID BIT IN THE BHD
	TXO	T4,BH.VAL	;SET THE VALID BIT
	MOVEM	T4,.BHKEY(P1)	;SAVE KEY/VALID BITS IN BHD
	SETZM	.BHLEN(P1)	;ZERO NIBBLE COUNT IN BHD
	XMOVEI	P3,.BHBSA-.BSNXT(P1) ;INIT FIRST BSD LINK WORD
	SETZM	.BHBSA(P3)	;ZERO THE POINTER IN CASE NO BSDS
	PUSHJ	P,PPDGBD	;GET A BSD
	  JRST	MAPBU8		;NONE AVAILABLE
	MOVEM	T1,.BHBSA(P3)	;POINT BHD AT FIRST BSD (PHYSICAL ADDRESS)
	MOVE	P3,T2		;SAVE THE POINTER TO THE CURRENT BSD
	MOVE	Q1,SS.BDA	;GET ADDRESS OF FIRST BUFFER DESCRIPTOR
	MOVE	P5,.MDNXT(Q1)	;SAVE THE POINTER TO THE NEXT DESCRIPTOR
	ADDI	Q1,.MDSSD	;SET Q1 TO START OF LENGTH/ADDRESS PAIRS
	JRST	MAPBU2		;ALREADY HAVE A BSD
MAPBU1:	SKIPN	.MDLEN(Q1)	;ANOTHER BSD NEEDED?
	JRST	MAPBU6		;NO, FINISH UP
	PUSHJ	P,PPDGBD	;GET A BSD
	  JRST	MAPBU8		;NONE AVAILABLE
	MOVEM	T1,.BSNXT(P3)	;POINT PREVIOUS BSD AT THIS ONE (PHYSICAL ADDRESS)
	MOVE	P3,T2		;SAVE THE POINTER TO THE CURRENT BSD
MAPBU2:	MOVE	T1,.MDLEN(Q1)	;GET LENGTH OF THIS SEGMENT
	MOVE	T3,SS.BDA	;GET DESCRIPTOR ADDRESS
	LOAD	T3,MD%DMD,.MDFLG(T3) ;GET THE MODE
	CAIE	T3,MD%DIC	;INDUSTRY COMPATIBLE?
	JRST	MAPBU3		;NO
	LSH	T1,1		;YES, LENGTH IN BYTES, CHANGE TO NIBBLES
	MOVX	T2,BS.ICM	;GET INDUSTRY COMPATIBLE MODE BIT
	JRST	MAPBU5		;CONTINUE

MAPBU3:	CAIE	T3,MD%DHD	;HIGH DENSITY?
	JRST	MAPBU4		;NO
	LSH	T1,1		;YES, LENGTH IN BYTES, CHANGE TO NIBBLES
	MOVX	T2,BS.HDM	;GET HIGH DENSITY MODE BIT
	JRST	MAPBU5		;CONTINUE

MAPBU4:	IMULI	T1,CDNPW	;CALCULATE NIBBLES/WORD FOR CORE DUMP
	MOVX	T2,BS.CDM	;GET CORE DUMP MODE BIT
MAPBU5:	MOVEM	T1,.BSLEN(P3)	;SAVE LENGTH OF THIS SEGMENT IN NIBBLES
	ADDM	T1,.BHLEN(P1)	;ACCUMULATE TOTAL LENGTH IN BHD
	IOR	T2,.MDADR(Q1)	;INCLUDE ADDRESS IN MODE BIT
	MOVEM	T2,.BSADR(P3)	;SET MODE AND BASE ADDRESS IN BSD
	SETZM	.BSNXT(P3)	;ASSUME WE'RE DONE
	ADDI	Q1,.MDLSD	;STEP TO NEXT DESCRIPTOR LENGTH PAIR
	JRST	MAPBU1		;HANDLE IT

MAPBU6:	SKIPN	Q1,P5		;IS THERE ANOTHER BUFFER DESCRIPTOR BLOCK?
	JRST	MAPBU7		;NO
	MOVE	P5,.MDNXT(Q1)	;YES, POINT TO ITS NEXT DESCRIPTOR BLOCK
	ADDI	Q1,.MDSSD	;POINT TO START OF THE DESCRIPTOR PAIRS
	JRST	MAPBU1		;GO SET UP THE BSDS

MAPBU7:	MOVE	T1,P2		;GET THE BUFFER NAME
	JRST	CPOPJ1##	;SKIP RETURN

MAPBU8:	MOVE	T1,P2		;GET THE BUFFER NAME
	PUSHJ	P,PPDRHD	;RETURN BHD AND BSD(S)
	RETBAD	(KLPX2)		;RETURN ERROR

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	SCS-PPD INTERFACE - UNMAP A BUFFER


;ROUTINE TO UNMAP (RETURN ASSOCIATED BHD AND BSD(S) FOR) A BUFFER.
;CALL:
;	BLCAL.	(PPDUMP,<SS.NAM>)
;
;WHERE:
;	SS.NAM - BUFFER NAME
;
;RETURN:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE
;	CPOPJ1 ON SUCCESS

PPDUMP::BLSUB.	(<SS.NAM>)
	MOVE	T1,SS.NAM	;GET THE BUFFER NAME
	PUSHJ	P,PPDRHD	;RETURN BUFFER HEADER AND SEGMENT DESCRIPTOR(S)
	TXNN	T4,BH.ERR	;WERE THERE ANY ERRORS?
	JRST	CPOPJ1##	;NO, SKIP RETURN
	RETBAD	(KLPX13)	;YES, RETURN BUFFER TRANSFER ERROR CODE

	ENDBS.			;END OF BLSUB. RANGE
	SUBTTL	DIAGNOSTIC UUO INTERFACE


;HERE ON DIAG. UUO FUNCTIONS FOR A CHANNEL WHICH DID NOT HAVE A KDB.
;IF IT IS THE KLIPA CHANNEL, AND THE PCB EXISTS, ALLOW THE USER ACCESS.
;CALL:
;	P1/ CHANNEL DATA BLOCK ADDRESS
;	P2/ FUNCTION CODE FOR DIAG. UUO
;	PUSHJ	P,PPDDIA
;RETURN:
;	CPOPJ ON ERROR (ERROR CODE STORED)
;	CPOPJ1 ON SUCCESS

PPDDIA::SE1ENT			;ENSURE WE RUN IN NZS
	PUSHJ	P,DIACHP	;SEE IF THERE IS A PCB
	  POPJ	P,		;NO, ERROR CODE ALREADY STORED
	CAIL	P2,17		;THSE FUNCTIONS DON'T REQUIRE MAINTENANCE MODE
	CAILE	P2,23		;...
	SKIPA			;NOT A SPECIAL FUNCTION
	PJRST	@DIAFNC(P2)	;YES, DISPATCH NOW
	MOVX	T1,ST.MAI	;IN MAINTENANCE MODE?
	TDNN	T1,.PCSTS(Q3)	;...
	JRST	UNNDMD##	;NO, RETURN AN ERROR
	PJRST	@DIAFNC(P2)	;DISPATCH BASED ON FUNCTION CODE

DIAFNC:	IFIW	CPOPJ##		;(0) DISPATCH ON ^C (DON'T GET HERE THIS WAY)
	IFIW	CPOPJ##		;(1) ASSIGN SINGLE UNIT
	IFIW	DIAAAU		;(2) ASSIGN ALL UNITS
	IFIW	DIARCU		;(3) RELEASE CHAN AND ALL UNITS
	IFIW	DIASCP		;(4) SPECIFY CHANNEL PROGRAM
	IFIW	DIARCP		;(5) RELEASE CHAN PROGRAM
	IFIW	DIACST		;(6) GET CHAN STATUS
	IFIW	CPOPJ##		;(7) GET KONTROLLER AND UNIT
	IFIW	CPOPJ##		;(10) ILLEGAL FOR KLIPA
	IFIW	CPOPJ##		;(11) ILLEGAL FOR KLIPA
	IFIW	CPOPJ##		;(12) SPECIFY CHAN PROGRAM FOR REVERSE
	IFIW	CPOPJ##		;(13) ILLEGAL FOR KLIPA
	IFIW	CPOPJ##		;(14) ILLEGAL FOR KLIPA
	IFIW	CPOPJ##		;(15) ILLEGAL FOR KLIPA
	IFIW	CPOPJ##		;(16) ILLEGAL FOR KLIPA
	IFIW	DIAELD		;(17) ENABLE MICROCODE LOADING
	IFIW	DIADLD		;(20) DISABLE MICROCODE LOADING
	IFIW	DIALOD		;(21) LOAD MICROCODE
	IFIW	DIAEMM		;(22) ENABLE MAINTENANCE MODE
	IFIW	DIADMM		;(23) DISABLE MAINTENANCE MODE
MXDIAG==:.-DIAFNC
;(2) ASSIGN "CHANNEL" AND ALL UNITS

DIAAAU:	PUSHJ	P,MAILOK	;ENSURE NO ONE ELSE GETS THE USE OF THE PCB
	  JRST	UNAAJB##	;SOMEONE ELSE IS ALREADY USING IT
	JRST	CPOPJ1##	;SKIP RETURN


;(3) RELEASE "CHANNEL" AND ALL UNITS

DIARCU:	PUSHJ	P,MAIULK	;LET GO OF EXCLUSIVE OWNERSHIP
	  JRST	UNAAJB##	;WE DON'T OWN INTERLOCK
	JRST	CPOPJ1##	;SKIP RETURN


;(4) SPECIFY CHANNEL PROGRAM

DIASCP:	CAME	J,.PCMJB(Q3)	;THIS JOB OWN MAINTENANCE INTERLOCK?
	JRST	UNAAJB##	;NO
	MOVE	P3,.PCCDB(Q3)	;GET CDB ADDRESS
	PUSHJ	P,DIARCP	;RETURN ANY IOWD
	S0PSHJ	GETWD1##	;GET IOWD
	HLRE	T2,T1		;LENGTH OF IOWD
	JUMPE	T2,IOWCPB##	;TOO BIG IF 0
	MOVEM	T1,.CHICW##(P3)	;UNRELOCATED IOWD
	MOVEI	T1,1(T1)	;START ADDRESS
	MOVNS	T2		;+LENGTH
	ADDI	T2,-1(T1)	;TOP ADDRESS
	S0PSHJ	ZRNGE##		;MAKE SURE THE PAGES ARE OK
	  JRST	[SETZM .CHICW##(P3) ;PAGE NOT THERE
		 JRST IOWCPB##]	;BOMB HIM OUT
	SETZB	P1,P4		;SAY FIRST CALL, NOT A DX10
	MOVE	T2,.CHICW##(P3)	;GET IOWD
	S0PSHJ	MAPIO##		;RELOCATE THE IOWD
	  JRST	[SETZM .CHICW##(P3)
		JRST DINEFC##]	;NO LOW-CORE BLOCKS
	MOVSI	T1,(CC.HLT)	;LIGHT HALT BIT IN LAST CCW
	IORM	T1,-1(P1)	;...
	SETZM	(P1)		;TERMINATE LIST
	MOVEM	P2,.CHICW##(P3)	;STORE ADDRESS OF CHANNEL PROGRAM
	TLO	P2,(FLD(.CCJMP,CC.OPC)) ;MAKE ICW BE A JUMP
	MOVE	T1,.CPEPT##	;GET EPT ADDRESS
	ADDI	T1,KLPICH*4	;OFFSET TO CHANNEL LOGOUT AREA
	MOVEM	P2,.CSICW(T1)	;POINT ICWA AT CORE-BLOCK
	SETZM	.CSCLP(T1)	;CLEAR OTHER WORDS
	SETZM	.CSDBA(T1)	;...
	PUSHJ	P,STOTAC##	;TELL USER ICWA
	JRST	CPOPJ1##	;AND TAKE GOOD RETURN
;(5) RELEASE CHANNEL PROGRAM

DIARCP:	CAME	J,.PCMJB(Q3)	;THIS JOB OWN MAINTENANCE INTERLOCK?
	JRST	UNAAJB##	;NO
	MOVE	P3,.PCCDB(Q3)	;GET CHANNEL DATA BLOCK ADDRESS
	SKIPN	T1,.CHICW##(P3)	;NOTHING TO DO IF NO IOWD
	POPJ	P,
	SETZM	.CHICW##(P3)	;FORGET WE HAD IT
	S0JRST	RTNIOW##	;RETURN THE SPACE AND RETURN


;(6) TELL USER FINAL CHANNEL STATS

DIACST:	CAME	J,.PCMJB(Q3)	;THIS JOB OWN MAINTENANCE INTERLOCK?
	JRST	UNAAJB##	;NO
	MOVE	P2,.CPEPT##	;GET EPT ADDRESS
	ADDI	P2,KLPICH*4+.CSICW ;OFFSET TO ICWA ADDRESS
	PJRST	DIAGCS##	;FINISH UP IN UUOCON
;(17/20) ENABLE/DISABLE MICROCODE LOADING

DIADLD:	TDZA	T2,T2		;DISABLE
DIAELD:	MOVEI	T2,1		;ENABLE
	XMOVEI	T1,.PCULB(Q3)	;ADDRESS OF MICRO LOADER BLOCK
	PUSHJ	P,BTUEDL##	;ENABLE OR DISABLE
	  JRST	DIAMNA##	;ERROR
	JRST	CPOPJ1##	;SUCCESS


;(21) LOAD MICROCODE

DIALOD:	PUSHJ	P,URDEAD	;STOP, RELOAD, AND RESTART THE KLIPA
	MOVX	T1,ST.DED	;DID IT SUCCEED?
	TDNN	T1,.PCSTS(Q3)	;...
	JRST	CPOPJ1##	;YES, SKIP RETURN
	JRST	DIAMRF##	;RETURN ERROR
;(22) ENABLE MAINTENANCE MODE

DIAEMM:	MOVX	T1,ST.MAI	;GET MAINTENANCE MODE FLAG
	TDNE	T1,.PCSTS(Q3)	;WAS IT ALREADY IN MAINTENANCE MODE?
	JRST	CPOPJ1##	;YES, SO NOTHING ELSE TO DO
	IORM	T1,.PCSTS(Q3)	;FLAG FOR NOSY ROUTINES
	PUSHJ	P,STPKLP	;STOP THE KLIPA
	PUSHJ	P,CIGONE	;RESET ALL CI ACTIVITY
	MOVE	P1,.PCCDB(Q3)	;P1 HAS BEEN STOMPED BY CIGONE (KLPRQC)
	SETZM	CHNBTS##(P1)	;ZAP BITS TO TEST FOR ON INTERRUPT
	JRST	CPOPJ1##	;SKIP RETURN

;(23) DISABLE MAINTENANCE MODE

DIADMM:	MOVX	T1,ST.MAI	;GET MAINTENANCE MODE FLAG
	TDNE	T1,.PCSTS(Q3)	;WAS IT IN MAINTENANCE MODE?
	JRST	DIADM1		;YES
	MOVX	T1,ST.DED	;NOT IN MAINTENANCE MODE, IS IT DEAD?
	TDNN	T1,.PCSTS(Q3)	;...
	JRST	CPOPJ1##	;NO, NOTHING ELSE TO DO
	SKIPA			;YES, TRY TO GET IT GOING AGAIN
DIADM1:	ANDCAM	T1,.PCSTS(Q3)	;CLEAR THE FLAG
	PUSHJ	P,STPKLP	;STOP THE KLIPA
	MOVE	T1,[KLPBTS]	;BITS TO TEST FOR ON INTERRUPT
	MOVEM	T1,CHNBTS##(P1)	;SET THEM IN CHANNEL DATA BLOCK
	PUSHJ	P,URDEAD	;DO ALL THE RESTART STUFF
	MOVX	T1,ST.DED	;DID IT SUCCEED?
	TDNN	T1,.PCSTS(Q3)	;...
	JRST	CPOPJ1##	;YES, SKIP RETURN
	JRST	DIAMRF##	;RETURN ERROR
;DIAG. UUO FUNCTION TO RESET A REMOTE NODE.
;CALL:
;	J/ CALLER'S JOB NUMBER
;	M/ POINTER TO USER ARGUMENT LIST
;	P1/ NUMBER OF ARGUMENTS IN LIST
;
;LIST:	CPU #,,105
;	CHAN,,NODE
;	FORCE FLAG (OPTIONAL)
;
;RETURN:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE
;	CPOPJ1 ON SUCCESS

DIARRS::PUSHJ	P,DIACHK	;SET UP FOR DIAG. FUNCTION, CHECK PCB, ETC.
	  POPJ	P,		;ERROR, ERROR CODE ALREADY STORED
	SE1ENT			;ENSURE WE RUN IN NZS
	CAIGE	P1,2		;AT LEAST 2 ARGUMENTS?
	JRST	WRONGN##	;NO
	MOVX	T1,ST.DED	;IS IT RUNNING?
	TDNE	T1,.PCSTS(Q3)	;...
	PJRST	DIAPNR##	;NO, ERROR
	SETZ	P2,		;ASSUME NO FLAGS
	CAIGE	P1,3		;INCLUDE THE OPTIONAL FORCE RESET FLAG?
	JRST	DIARS1		;NO
	S0PSHJ	GETWD1##	;GET THE FORCE FLAG
	SKIPE	T1		;WANT TO FORCE A RESET?
	TXO	P2,PF.FRC	;YES, SET THE FLAG
DIARS1:	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	DINEFC##	;NONE AVAILABLE, GIVE AN ERROR
	MOVEI	T1,OP.RRS	;OPCODE = RESET REMOTE SYSTEM
	MOVE	T3,P2		;MOVE THE FLAGS TO T3
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	DIASND		;SEND THE PACKET AND RETURN
;DIAG. UUO FUNCTION TO START A REMOTE NODE.
;CALL:
;	J/ CALLER'S JOB NUMBER
;	M/ POINTER TO USER ARGUMENT LIST
;	P1/ NUMBER OF ARGUMENTS IN LIST
;
;LIST:	CPU #,,106
;	CHAN,,NODE
;	STARTING ADDRESS (ZERO IMPLIES DEFAULT)
;
;RETURN:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE
;	CPOPJ1 ON SUCCESS

DIASRS::PUSHJ	P,DIACHK	;SET UP FOR DIAG. FUNCTION, CHECK PCB, ETC.
	  POPJ	P,		;ERROR, ERROR CODE ALREADY STORED
	SE1ENT			;ENSURE WE RUN IN NZS
	CAIGE	P1,2		;AT LEAST 2 ARGUMENTS?
	JRST	WRONGN##	;NO
	MOVX	T1,ST.DED	;IS IT RUNNING?
	TDNE	T1,.PCSTS(Q3)	;...
	PJRST	DIAPNR##	;NO, ERROR
	SETZ	P2,		;ASSUME DEFAULT STARTING ADDRESS
	CAIGE	P1,3		;INCLUDE THE OPTIONAL STARTING ADDRESS?
	JRST	DIASR1		;NO
	S0PSHJ	GETWD1##	;GET THE STARTING ADDRESS
	MOVE	P2,T1		;COPY TO P2
DIASR1:	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	DINEFC##	;NONE AVAILABLE, GIVE AN ERROR
	MOVEM	P2,.PKSAD(Q2)	;STORE STARTING ADDRESS IN PACKET
	MOVEI	T1,OP.RRS	;OPCODE = RESET REMOTE SYSTEM
	SETZ	T3,		;ASSUME NO FLAGS
	SKIPN	P2		;WAS THERE A NON-ZERO STARTING ADDRESS?
	TXO	T3,PF.DSA	;NO, ASK FOR DEFAULT STARTING ADDRESS
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	DIASND		;SEND THE PACKET AND RETURN
;DIAG. UUO FUNCTION TO MANIPULATE THE PORT COUNTERS.
;CALL:
;	J/ CALLER'S JOB NUMBER
;	M/ POINTER TO USER ARGUMENT LIST
;	P1/ NUMBER OF ARGUMENTS IN LIST
;
;LIST:	CPU #,,107
;	CHAN,,COUNTERS SUB-FUNCTION
;	SUB-FUNCTION SPECIFIC DATA
;
;RETURN:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE
;	CPOPJ1 ON SUCCESS

DIACTR::SKIPN	Q3,.CPPCB##	;IS THERE A CI PORT ON THIS CPU?
	JRST	DIANPC##	;NO, ERROR
	CAIGE	P1,2		;AT LEAST 2 ARGUMENTS?
	JRST	WRONGN##	;NO
	MOVX	T1,ST.DED	;IS IT RUNNING?
	TDNE	T1,.PCSTS(Q3)	;...
	PJRST	DIAPNR##	;NO, ERROR
	PUSHJ	P,GETWD1##	;GET FIRST ARGUMENT
	HRRE	Q1,T1		;SAVE SUB-FUNCTION CODE HERE FOR DISPATCH
	HLRZS	T1		;ISOLATE CHANNEL
	CAIE	T1,KLPICH	;THE KLIPA CHANNEL?
	JRST	DIANPC##	;NO
	MOVSI	T1,JP.POK	;PRIVILEGE BIT THEY NEED
	CAIE	Q1,CTRRCT	;THE UNPRIVILEGED FUNCTION?
	PUSHJ	P,PRVBIT##	;NO, MAKE SURE USER IS PRIVILEGED
	  SKIPA			;OK, UNPRIVILEGED FUNCTION OR PRIVILEGED USER
	JRST	DIANPV##	;GIVE AN ERROR
	SE1ENT			;ENSURE WE RUN IN NZS
	SKIPL	Q1		;NEGATIVE SUB-FUNCTIONS ARE ILLEGAL
	CAILE	Q1,CTRFLN	;LEGAL SUB-FUNCTION?
	JRST	DIABAL##	;NO, RETURN AN ERROR
	PJRST	@CTRFCN(Q1)	;DISPATCH BASED ON SUB-FUNCTION CODE

CTRFCN:	IFIW	CTRGET		;0 - GET COUNTERS
	IFIW	CTRGIV		;1 - RELEASE COUNTERS
	IFIW	CTRPNT		;2 - POINT COUNTERS
CTRRCT==.-CTRFCN		;THE "READ COUNTERS" SUB-FUNCTION
	IFIW	CTRRED		;3 - READ COUNTERS
CTRFLN==.-CTRFCN-1		;MAXIMUM LEGAL SUB-FUNCTION
;GET CONTROL OF THE COUNTERS

CTRGET:	SKIPE	T1,.PCCJB(Q3)	;DOES SOME JOB ALREADY OWN THE COUNTERS?
	CAMN	T1,J		;YES, IS IT THE CALLER'S JOB?
	SKIPA			;NOT OWNED, OR OWNED BY CALLER
	JRST	UNAAJB##	;SORRY, THEY'RE ALREADY IN USE
	MOVEM	J,.PCCJB(Q3)	;YOU'RE THE LUCKY OWNER
	JRST	CPOPJ1##	;SKIP RETURN


;RELINQUISH CONTROL OF THE COUNTERS

CTRGIV:	CAMN	J,.PCCJB(Q3)	;DOES CALLER'S JOB OWN THE COUNTERS?
	JRST	CTRGV1		;YES
	CAIGE	P1,3		;ROOM FOR THE FORCE FLAG?
	JRST	UNAAJB##	;NO, GIVE AN ERROR
	S0PSHJ	GETWD1##	;GET THE FORCE FLAG
	SKIPG	T1		;FORCE THE RELEASE?
	PJRST	UNAAJB##	;NO, GIVE AN ERROR
CTRGV1:	SETZM	.PCCJB(Q3)	;NO ONE OWNS THE COUNTERS ANY MORE
	JRST	CPOPJ1##	;SKIP RETURN


;POINT THE COUNTERS AT A PARTICULAR NODE

CTRPNT:	CAME	J,.PCCJB(Q3)	;DOES CALLER'S JOB OWN THE COUNTERS?
	JRST	UNAAJB##	;NO, GIVE AN ERROR
	CAIGE	P1,4		;NEED AT LEAST THIS MANY WORDS
	JRST	WRONGN##	;NOPE, WE'RE A BIT LATE AND A WORD SHORT
	S0PSHJ	GETWD1##	;GET THE MASK ARGUMENT
	MOVE	P2,T1		;SAVE A MOMENT
	S0PSHJ	GETWD1##	;GET THE NODE ARGUMENT
	MOVE	P3,T1		;SAVE A MOMENT
				;*** NO CALLS TO GETWD1 AFTER CALLING KLPGDB
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	DINEFC##	;NONE AVAILABLE, GIVE AN ERROR
	MOVEM	P2,.PKMSK(Q2)	;STORE THE MASK IN THE PACKET
	SETZM	.PKPND(Q2)	;CLEAR PORT WORD
	DPB	P3,PKYCND	;WHICH NODE TO MONITOR
	MOVEI	T1,OP.SPT	;OPERATION IS SET POINTERS
	SETZB	T3,Q1		;NO FLAGS, CLEAR NODE NUMBER
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	DIASND		;SEND THE PACKET AND RETURN
;READ THE COUNTERS

CTRRED:	PUSH	P,M		;WE WANT TO CALL PUTWD1 LATER
	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	[POP	P,M	;NONE AVAILABLE, CLEAN STACK
		 JRST	DINEFC##] ;GIVE AN ERROR
	SETZM	.PCCTR(Q3)	;ZERO DATE/TIME COUNTERS WERE LAST READ
	MOVX	T2,KS%DIA	;REASON FOR READING COUNTERS
	HRL	T2,J		;PLUG IN OUR JOB NUMBER
	PUSHJ	P,KLPRPT	;SEND A READ-COUNTERS
	POP	P,M		;RESTORE ADDRESS FOR PUTWD1

;NOW WAIT FOR A RESPONSE - WAIT UP TO 5 SECONDS

	MOVEI	T1,^D5		;HOW LONG TO WAIT
	S0PSHJ	SLEEPF##	;PUT THE JOB TO SLEEP FOR A WHILE
	SKIPN	.PCCTR(Q3)	;ANYTHING GET RETURNED?
	JRST	DIATMO##	;NO, ERROR

;SOMETHING HAS ARRIVED, ASSUME IT IS THE DATA WE REQUESTED

	SUBI	P1,2		;ACCOUNT FOR FUNCTION AND SUB-FUNCTION WORDS
	CAILE	P1,NOSTCT+1	;ASKING FOR MORE THAN WE HAVE?
	MOVEI	P1,NOSTCT+1	;YES, REDUCE THEIR REQUEST
	SOJLE	P1,CPOPJ1##	;RETURN IF ARGUMENT LIST EXHAUSTED
	MOVE	T1,.PCCJB(Q3)	;GET JOB WHICH OWNS THE COUNTERS
	PUSHJ	P,PUTWD1##	;STORE FOR USER

;THE BLOCK OF DATA RETURNED BY THE READ COUNTERS PACKET

	MOVE	P2,Q3		;COPY THE PCB ADDRESS TO P2
CTRRD2:	SOJLE	P1,CPOPJ1##	;RETURN WHEN ARGUMENT LIST EXHAUSTED
	MOVE	T1,.PCCTR+1(P2)	;GET A COUNTER ITEM (SKIP DATE/TIME)
	PUSHJ	P,PUTWD1##	;STORE FOR THE USER
	AOJA	P2,CTRRD2	;LOOP FOR MORE DATA
;DIAG. UUO FUNCTIONS TO REQUEST (READ) AND WRITE MAINTENANCE DATA.
;CALL:
;	J/ CALLER'S JOB NUMBER
;	M/ POINTER TO USER ARGUMENT LIST
;	P1/ NUMBER OF ARGUMENTS IN LIST
;
;LIST:	CPU #,,112 FOR WRITE, 113 FOR READ
;	CHAN,,NODE
;	LENGTH OF TRANSFER IN BYTES
;	RECEIVER'S BUFFER NAME
;	ADDRESS OF USER BUFFER
;
;RETURN:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE
;	CPOPJ1 ON SUCCESS

DIARMD::SE1ENT			;ENSURE WE RUN IN NZS
	STKVAR	<RWFLAG,RTDG>	;ALLOCATE SOME STACK STORAGE
	SETZM	RWFLAG		;FLAG THIS IS A READ FUNCTION
	JRST	DIARWM		;JOIN COMMON CODE

DIAWMD::SE1ENT			;ENSURE WE RUN IN NZS
	STKVAR	<RWFLAG,RTDG>	;ALLOCATE SOME STACK STORAGE
	SETOM	RWFLAG		;FLAG THIS IS A WRITE FUNCTION
DIARWM:	CAIGE	P1,5		;ALL THE ARGUMENTS THERE?
	JRST	WRONGN##	;NO, GIVE ERROR
	PUSHJ	P,DIACHK	;SET UP FOR DIAG. FUNCTION, CHECK PCB, ETC.
	  POPJ	P,		;ERROR, ERROR CODE ALREADY STORED
	MOVX	T1,ST.DED	;IS IT RUNNING?
	TDNE	T1,.PCSTS(Q3)	;...
	PJRST	DIAPNR##	;NO, ERROR
	CAIL	Q1,0		;LEGAL CI NODE NUMBER?
	CAIL	Q1,MAXNDS	;...
	JRST	DIABAL##	;NO
	MOVE	T1,Q3		;GET PCB ADDRESS
	ADD	T1,Q1		; OFFSET BY CI NODE NUMBER
	SKIPN	P5,.PCPBK(T1)	;GET PATH BLOCK ADDRESS
	JRST	DIABAL##	;ERROR
	MOVE	T1,.PBDPF(P5)	;GET DESTINATION PORT FUNCTIONALITY
	SKIPN	RWFLAG		;READ OR WRITE?
	SKIPA	T2,[TXNN T1,PK.RMD] ;READ, GET THE APPROPRIATE TEST
	MOVE	T2,[TXNN T1,PK.SMD] ;WRITE, GET THE APPROPRIATE TEST
	XCT	T2		;DOES IT SUPPORT THE DESIRED FUNCTION?
	JRST	DIABAL##	;NO
	LDB	T1,[POINT PKSRST,.PBDPS(P5),PKPRST] ;GET THE PORT STATE
	CAIE	T1,PS.UMS	;IS IT IN UNITIALIZED MAINTENANCE MODE?
	JRST	DIABAL##	;NO
	LDB	T1,[POINT PKSRND,.PBDPS(P5),PKPRND] ;GET THE RESETTING NODE
	CAME	T1,.PCONN(Q3)	;SAME AS OUR NODE NUMBER?
	JRST	DIABAL##	;NO
	S0PSHJ	GETWD1##	;GET THE NUMBER OF 8 BIT BYTES TO READ/WRITE
	CAILE	T1,0		;MUST BE GREATER THAN 0
	CAILE	T1,^D512	; AND LESS THAN 513
	JRST	DIABAL##	;ERROR
	MOVE	P2,T1		;SAVE COUNT IN P2

	S0PSHJ	GETWD1##	;GET THE DESTINATION NODE'S BUFFER NAME
	MOVE	P3,T1		;SAVE IN P3

	S0PSHJ	GETWD1##	;GET THE ADDRESS OF THE USER'S BUFFER
	MOVE	P4,T1		;SAVE IN P4

;AT THIS POINT THE CALLING ARGUMENTS LOOK GOOD.  NOW START THE REAL
;WORK.  AFTER THIS POINT, AC "M" IS NO LONGER USABLE, AS IT IS THE
;SAME AS AC "Q2".

	PUSHJ	P,MAILOK	;GAIN OWNERSHIP OF THE SEND/RECEIVE INTERLOCK
	  JRST	UNAAJB##	;SOMEONE ELSE IS ALREADY USING IT

	MOVEI	T1,1		;ASK FOR A BUFFER
	PUSHJ	P,SC.ALD##	;GET A DATAGRAM BUFFER
	  JRST	[HRLM T1,.PCMFL(Q3) ;ERROR, STORE ERROR CODE
		 JRST DIRWF4]	;FINISH UP
	MOVE	Q2,T1		;SAVE BUFFER ADDRESS
	MOVEM	T3,RTDG		;SAVE ADDRESS OF ROUTINE TO RETURN BUFFER

	SETZM	.MDNXT(Q2)	;NO NEXT DESCRIPTOR IN CHAIN
	SETZM	.MDFLG(Q2)	;START WITH A FRESH FLAGS WORD
	MOVX	T1,MD%DIC	;GET INDUSTRY COMPATIBLE MODE BITS
	STOR	T1,MD%DMD,.MDFLG(Q2) ;STORE IN BUFFER DESCRIPTOR BLOCK
	MOVX	T1,SQ%WRT	;GET THE WRITE FLAG
	SKIPN	RWFLAG		;READ OR WRITE?
	IORM	T1,.MDFLG(Q2)	;READ, ALLOW KLIPA TO WRITE HOST MEMORY
	MOVE	T1,P2		;GET SIZE (IN 8-BIT BYTES)
	MOVEM	T1,.MDSSD+.MDLEN(Q2) ;STORE IN BUFFER DESCRIPTOR BLOCK
	MAP	T1,.MDSSD+.MDLSD+1(Q2) ;GET PHYSICAL ADDRESS OF SEGMENT
	TXZ	T1,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVEM	T1,.MDSSD+.MDADR(Q2) ;STORE IN BUFFER DESCRIPTOR BLOCK
	SETZM	.MDSSD+.MDLSD(Q2) ;ZERO LAST WORD OF DESCRIPTOR BLOCK
	BLCAL.	(PPDMAP,<Q2>)	;SET UP BHD AND BSD(S), RETURN BUFFER NAME
	  JRST	[HRLM T1,.PCMFL(Q3) ;ERROR, STORE ERROR CODE
		 JRST DIRWF3]	;FINISH UP
	MOVE	P1,T1		;SAVE BUFFER NAME
	SKIPN	RWFLAG		;READ OR WRITE?
	JRST	DIARW1		;READ
	MOVEI	T1,3(P2)	;WRITE, GET NUMBER OF BYTES AND ROUND UP
	LSH	T1,-2		;CONVERT TO WORDS
	MOVE	T2,P4		;COPY USER'S BUFFER ADDRESS
	EXCTUX	<SKIP (T2)>	;IS USER ADDRESS VALID?
	  ERJMP	DIRWA1		;NO, GO CLEAN UP
	ADD	T2,T1		;GET ENDING USER ADDRESS
	EXCTUX	<SKIP (T2)>	;IS ENDING USER ADDRESS VALID?
	  ERJMP	DIRWA1		;NO, GO CLEAN UP
	MOVE	T2,P4		;GET SOURCE ADDRESS
	XMOVEI	T3,.MDSSD+.MDLSD+1(Q2) ;GET DESTINATION ADDRESS
	EXCTUX	<EXTEND T1,[XBLT]> ;TRANSFER THE DATA
DIARW1:	SETOM	.PCMFL(Q3)	;SET THE FLAG: -1 MEANS NOT COMPLETE, 0 MEANS
				; COMPLETED, 1 MEANS COMPLETED WITH ERROR
	LSH	P3,4		;POSITION THE BUFFER NAME FOR KLPSER
	SKIPN	RWFLAG		;READ OR WRITE?
	JRST	DIARW2		;READ
	BLCAL.	(PPDSMD,<P1,P3,[0],[0]>)
	  JRST	[HRLM T1,.PCMFL(Q3) ;ERROR, SAVE ERROR CODE
		 JRST DIRWF2]	;ERROR ONLY IF UNABLE TO GET A BUFFER
	JRST	DIARW3		;REJOIN COMMON CODE

DIARW2:	BLCAL.	(PPDRMD,<P3,P1,[0],[0]>)
	  JRST	[HRLM T1,.PCMFL(Q3) ;ERROR, SAVE ERROR CODE
		 JRST DIRWF2]	;ERROR ONLY IF UNABLE TO GET A BUFFER
DIARW3:	PUSHJ	P,MAIRWT	;WAIT FOR THE WRITE TO FINISH
	  JRST	[SKIPG .PCMFL(Q3) ;TIMED OUT OR FINISHED WITH ERROR?
		 SKIPA T1,[27]	;TIMED OUT
		 MOVEI T1,30	;FINISHED WITH ERROR
		 HRLM  T1,.PCMFL(Q3) ;SAVE ERROR CODE
		 JRST  .+1]	;CONTINUE ON
	SKIPE	RWFLAG		;READ OR WRITE?
	JRST	DIRWF1		;WRITE, FINISH UP
	MOVEI	T1,3(P2)	;READ, GET NUMBER OF BYTES AND ROUND UP
	LSH	T1,-2		;CONVERT TO WORDS
	XMOVEI	T2,.MDSSD+.MDLSD+1(Q2) ;GET SOURCE ADDRESS
	MOVE	T3,P4		;GET DESTINATION
	EXCTUU	<MOVES (T3)>	;IS USER ADDRESS VALID?
	  ERJMP	DIRWA2		;NO
	ADD	T3,T1		;GET ENDING ADDRESS
	EXCTUU	<MOVES (T3)>	;IS USER ADDRESS VALID?
	  ERJMP	DIRWA2		;NO
	MOVE	T3,P4		;GET DESTINATION AGAIN
	EXCTXU	<EXTEND T1,[XBLT]> ;TRANSFER THE DATA
	JRST	DIRWF1		;FINISH UP

DIRWA1:	MOVEI	T1,ECOD1##	;GET ILLEGAL ADDRESS ERROR CODE
	HRLM	T1,.PCMFL(Q3)	;STORE ERROR CODE
	JRST	DIRWF2		;GO CLEAN UP

DIRWA2:	MOVEI	T1,ECOD1##	;GET ILLEGAL ADDRESS ERROR CODE
	HRLM	T1,.PCMFL(Q3)	;STORE ERROR CODE
	JRST	DIRWF1		;GO CLEAN UP
;HERE TO FINISH UP FOLLOWING A READ/WRITE FUNCTION.

DIRWF1:	PUSHJ	P,MAICLB	;CLEAN UP
DIRWF2:	BLCAL.	(PPDUMP,<Q3>)	;CLEAN UP THE BHD AND BSD (Q3 HAS BUFFER NAME)
	  JRST	[HRLM T1,.PCMFL(Q3) ;ERROR, STORE ERROR CODE
		 JRST DIRWF4]	;FINISH UP
DIRWF3:	MOVE	T1,Q2		;GET THE ADDRESS OF THE BUFFER TO RELEASE
	PUSHJ	P,@RTDG		;RELEASE THE BUFFER
	SKIPE	.PCMFL(Q3)	;DID MAINTENANCE OPERATION COMPLETE SUCCESSFULLY?
	JRST	DIRWF4		;NO
	PUSHJ	P,MAIULK	;RELINQUISH OWNERSHIP OF THE INTERLOCK
	  JFCL			;SHOULDN'T HAPPEN
	JRST	CPOPJ1##	;SKIP RETURN

DIRWF4:	HLRZ	T1,.PCMFL(Q3)	;GET THE ERROR CODE
	PUSH	P,T1		;SAVE IT FOR A MOMENT
	PUSHJ	P,MAIULK	;RELINQUISH OWNERSHIP OF THE SEND/RECEIVE INTERLOCK
	  JFCL			;SHOULDN'T HAPPEN
	POP	P,T1		;RESTORE ERROR CODE
	JRST	STOTAC##	;STORE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	ENDSV.			;END OF STACK VARIABLE RANGE
;ROUTINE TO GAIN OWNERSHIP OF THE CI PORT MAINTENANCE
;SEND/RECEIVE INTERLOCK.
;CALL:
;	J/ CALLER'S JOB NUMBER
;	Q3/ PCB ADDRESS
;	PUSHJ	P,MAILOK
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

MAILOK:	CIOFF			;PREVENT RACES
	SKIPE	.PCMJB(Q3)	;IS SOMEONE ELSE DOING A MAINTENANCE SEND/RECEIVE?
	PJRST	CIONPJ##	;YES, GIVE UP INTERLOCK AND NON-SKIP RETURN
	MOVEM	J,.PCMJB(Q3)	;NO, THEN WE ARE THE LUCKY ONES
	PJRST	CINPJ1##	;INTERRUPS BACK ON AND SKIP RETURN


;ROUTINE TO RELINQUISH OWNERSHIP OF THE CI PORT MAINTENANCE
;SEND/RECEIVE INTERLOCK.
;CALL:
;	J/ CALLER'S JOB NUMBER
;	Q3/ PCB ADDRESS
;	PUSHJ	P,MAIULK
;RETURN:
;	CPOPJ IF DIDN'T OWN INTERLOCK
;	CPOPJ1 IF INTERLOCK RELEASED

MAIULK:	CAME	J,.PCMJB(Q3)	;THIS JOB OWN MAINTENANCE INTERLOCK?
	POPJ	P,		;NO
	SETZM	.PCMJB(Q3)	;QUITE EASY
	JRST	CPOPJ1##	;SKIP RETURN
;ROUTINE TO TELL THE KLIPA TO CLOSE THE BUFFER SO THE KLIPA WILL
;DELETE THE OPERATION FROM ITS QUEUES.
;CALL:
;	P1/ BUFFER NAME
;	PUSHJ	P,MAICLB
;RETURN:
;	CPOPJ ALWAYS

MAICLB:	PUSHJ	P,SAVQ##	;SAVE THE Q REGISTERS
MAICL1:	PUSHJ	P,KLPGDB	;GET A DATAGRAM BUFFER
	  JRST	MAICL2		;ERROR, WAIT A BIT AND TRY AGAIN
	SETZM	.PCMCF(Q3)	;ZERO THE FLAG
	MOVEM	P1,.PCMCN(Q3)	;SAVE THE BUFFER NAME
	BLCAL.	(PPDCLB,<P1,Q2>) ;DO THE CLOSE BUFFER COMMAND
	PJRST	MAIRWT		;WAIT FOR COMPLETION AND RETURN

MAICL2:	MOVEI	T1,1		;FAILED TO GET A BUFFER, SLEEP A SECOND
	S0PSHJ	SLEEPF##	;(SLEEP ZEROES F)
	JRST	MAICL1		;TRY AGAIN


;ROUTINE TO WAIT FOR A MAINTENANCE OPERATION TO COMPLETE.
;CALL:
;	Q3/ PCB ADDRESS
;	PUSHJ	P,MAIRWT
;RETURN:
;	CPOPJ ALWAYS

MAIRWT:	MOVEI	T1,^D5		;HOW LONG TO WAIT
	S0JRST	SLEEPF##	;KNOCK OFF FOR A WHILE
;ROUTINE TO SET UP FOR A DIAG. UUO FUNCTION.
;CALL:
;	M/ POINTER TO FUNCTION WORD OF USER ARGUMENT LIST
;	P1/ NUMBER OF ARGUMENTS
;	PUSHJ	P,DIACHK
;RETURN:
;	CPOPJ IF ERRORS (ERROR CODE ALREADY STORED)
;	CPOPJ1 IF SUCCESS WITH:
;	Q1/ RIGHT HALF OF FIRST ARGUMENT (PROBABLY NODE NUMBER)
;	Q3/ PCB ADDRESS
;
;NOTE:  ALL KLIPA DIAG. UUO FUNCTIONS HAVE THE CHANNEL ARGUMENT IN
;THE LEFT HALF OF THE FIRST ARGUMENT FOLLOWING THE FUNCTION.  IF A
;FUNCTION IS ADDED WHICH DOESN'T FOLLOW THIS CONVENTION, DIACHK AND
;ALL CALLERS WILL HAVE TO BE MODIFIED SO THE CHANNEL IS CHECKED BY
;THE CALLER INSTEAD OF DIACHK.

DIACHK:	PUSHJ	P,DIACHP	;CHECK FOR PRIVILEGES AND A PCB
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	CAIGE	P1,2		;AT LEAST 2 ARGUMENTS?
	JRST	WRONGN##	;NO
	PUSHJ	P,GETWD1##	;GET FIRST ARGUMENT
	HRRZ	Q1,T1		;RETURN AS ADVERTISED
	HLRZS	T1		;ISOLATE CHANNEL
	CAIE	T1,KLPICH	;THE KLIPA CHANNEL?
	JRST	DIANPC##	;NO
	JRST	CPOPJ1##	;YES, SKIP RETURN


;ROUTINE TO CHECK FOR EXISTANCE OF PCB AND PRIVILEGES.  LIKE
;DIACHK BUT IT DOESN'T CHECK THE CHAN,,NODE WORD.
;CALL:
;	M/ POINTER TO FUNCTION WORD OF ARGUMENT LIST
;	PUSHJ	P,DIACHP
;RETURN:
;	CPOPJ IF ERRORS (ERROR CODE ALREADY STORED)
;	CPOPJ1 IF SUCCESS WITH:
;	Q3/ PCB ADDRESS

DIACHP:	MOVSI	T1,JP.POK	;MUST HAVE PRIVILEGES
	PUSHJ	P,PRVBIT##	;DO THEY?
	  SKIPA	Q3,.CPPCB##	;YES, GET PCB ADDRESS AND SKIP
	JRST	DIANPV##	;NO
	JUMPE	Q3,DIANPC##	;IF NO CI PORT, GIVE AN ERROR
	JRST	CPOPJ1##	;TAKE SKIP RETURN
;HERE FROM UUOCON FROM DIACLR DISPATCH (^C, HALT, ETC., TYPED)
;CALL:
;	J/ JOB NUMBER
;	PUSHJ	P,PPDCLR
;RETURN:
;	CPOPJ ALWAYS

PPDCLR::MOVEI	T1,CLRJOB	;LOAD ADDRESS OF SUBROUTINE TO CLEAR JOB IN PCB
	PJRST	CPUAPP##	;DO IT ON ALL CPUS AND RETURN


;ROUTINE EXECUTED TO CLEAR JOB NUMBER FIELDS IN PCB FOR ALL CPUS.
;ZEROS .PCCJB AND .PCMJB IN THE PCB.
;CALL:
;	P1/ CDB ADDRESS
;	J/ JOB NUMBER
;	PUSHJ	P,CLRJOB
;RETURN:
;	CPOPJ ALWAYS

CLRJOB:	SE1ENT			;RUN IN NZS
	SKIPN	T1,.CPPCB##-.CPCDB##(P1) ;IS THERE A KLIPA ON THIS CPU?
	POPJ	P,		;NO, RETURN
	CAMN	J,.PCCJB(T1)	;DOES THIS JOB OWN THE COUNTERS?
	SETZM	.PCCJB(T1)	;YES, BUT NOT ANY LONGER
	CAMN	J,.PCMJB(T1)	;DOES THIS JOB OWN THE MAINTENANCE SEND/RECEIVE INTERLOCK?
	SETZM	.PCMJB(T1)	;YES, BUT NOT ANY LONGER
	POPJ	P,		;RETURN
	SUBTTL	ROUTINE TO SEND A PACKET UNDER VIRTUAL CIRCUIT CONTROL


;ROUTINE TO SEND A PACKET UNDER VIRTUAL CIRCUIT CONTROL.
;CALL:
;	T1/ OPCODE
;	T2/ PRIORITY
;	T3/ FLAGS
;	Q2/ PACKET ADDRESS
;	P5/ PBK ADDRESS
;	PUSHJ	P,SENDVC
;RETURN:
;	CPOPJ ON ERRORS WITH:
;	T1/ ERROR CODE
;	CPOPJ1 ON SUCCESS

SENDVC:	LOAD	T4,PBVCST,(P5)	;GET VC STATE
	CAIE	T4,VC.OPN	;IS IT OPEN?
	RETBAD	(KLPX9)		;NO
	LOAD	Q1,PBDPN,(P5)	;GET THE DESTINATION NODE NUMBER
	MOVE	Q3,.PBPCB(P5)	;GET THE PCB ADDRESS
	MOVX	T4,PK.SRB	;CLEAR ALL SOFTWARE RESPONSE BITS
	ANDCAM	T4,.PKVRT(Q2)	;...
	MOVX	T4,PK.SCA	;GET THE SCA SOFTWARE RESPONSE FLAG
	TXNE	T3,PF.RSP	;WANT A RESPONSE?
	IORM	T4,.PKVRT(Q2)	;YES, SET THE FLAG
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	KLPSND		;SEND THE PACKET AND RETURN
	SUBTTL	ROUTINES TO SEND DATAGRAMS


;ROUTINES TO SEND A SET CIRCUIT DATAGRAM.
;CALL:
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,KLPSCK/KLPOPN/KLPCLO
;RETURN:
;	CPOPJ ALWAYS

KLPCLO:	MOVX	T1,RI.PAO!RI.PBO ;NEITHER PATH NOW OPEN
	ANDCAM	T1,.PCRIS(P1)	;...
	MOVX	T1,CK.LST	;CLOSE VIRTUAL CIRCUIT
	JRST	KLPOCC		;JOIN COMMON CODE

KLPOPN:	MOVX	T1,RI.PAO!RI.PBO ;START WITH BOTH PATHS OPEN
	IORM	T1,.PCRIS(P1)	;...
	MOVX	T1,CK.LST!CK.CST!CK.LPT!CK.PAA!CK.PBA ;OPEN VIRTUAL CIRCUIT
KLPOCC:	MOVEM	T1,.PKCKT(Q2)	;ASSUME NR-NS=0 INITIALLY
KLPSCK:	MOVEI	T1,OP.CKT	;OPCODE = SET VIRTUAL CIRCUIT
	MOVEI	T2,INCXID	;GET NEW TRANSACTION ID
	ADDB	T2,KLPXID	;...
	MOVEM	T2,.PKXID(Q2)	;STORE IN PACKET
	MOVEM	T2,.PKXID+1(Q2)	; (SAME THING IN BOTH WORDS)
	JRST	KLPSX1		;JOIN COMMON CODE


;ROUTINE TO SEND A READ REGISTER DATAGRAM.
;CALL:
;	T1/ REGISTER TO READ
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,KLPRRG
;RETURN:
;	CPOPJ ALWAYS

KLPRRG:	SETZB	Q1,.PKREG(Q2)	;ZERO NODE NUMBER AND MUST BE ZERO FIELDS
	DPB	T1,PKYREG	;STORE THE REGISTER TO READ
	MOVEI	T1,OP.RRG	;OPCODE = READ REGISTER
	JRST	KLPSX1		;JOIN COMMON CODE
;ROUTINE TO SEND A START DATAGRAM.
;CALL:
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,KLPSDG
;RETURN:
;	CPOPJ ALWAYS

KLPSDG:	LDB	T1,PKYPPD	;GET PACKET TYPE
	MOVE	T2,STRLEN(T1)	;GET APPROPRIATE LENGTH
	DPB	T2,PKYLEN	;STORE THE PACKET LENGTH
	PUSHJ	P,MASAGE	;SWAP THE PPD BYTE AND ADJUST LENGTH
	MOVEI	T1,OP.SDG	;OPCODE = SEND DATAGRAM
KLPSX1:	MOVEI	T2,KLPDRG	;PRIORITY
	SETZ	T3,		;NO FLAGS
	PJRST	DRVSND		;SEND PACKET AND RETURN

;START PACKET LENGTH TABLE

STRLEN:	SR.LEN			;(PPD = 0) START LENGTH
	SR.LEN			;(PPD = 1) STACK LENGTH
	SR.ALN			;(PPD = 2) ACK LENGTH
	0			;(PPD = 3) APPLICATION DATAGRAM
	0			;(PPD = 4) APPLICATION MESSAGE
	0			;(PPD = 5) ERROR PACKET DOESN'T COME HERE
	SR.ALN			;(PPD = 6) SHUTDOWN
;ROUTINE TO SEND A REQUEST-ID DATAGRAM.
;CALL:
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB
;	T3/ PATH BIT
;	PUSHJ	P,KLPRID
;RETURN:
;	CPOPJ ALWAYS

KLPRID:	MOVE	T1,Q3		;GET PCB ADDRESS
	ADD	T1,Q1		; OFFSET FOR THIS NODE
	MOVEI	T2,RIDTIM*3	;CONVERT TIMER LENGTH TO UDT INCREMENTS
	ADD	T2,DATE##	;WHEN TIMER WILL EXPIRE
	MOVEM	T2,.PCRIT(T1)	;SET THE EXPIRATION TIME
	MOVEI	T1,OP.RID	;OPCODE = REQUEST ID
	MOVEI	T2,INCXID	;GET NEW TRANSACTION ID
	ADDB	T2,KLPXID	;...
	MOVEM	T2,.PKXID(Q2)	;STORE IN PACKET
	MOVEM	T2,.PKXID+1(Q2)	; (SAME THING IN BOTH WORDS)
	MOVEI	T2,KLPDRG	;PRIORITY
	PJRST	DRVSND		;SEND THE PACKET AND RETURN


;ROUTINE TO SEND A READ-COUNTERS PACKET.
;CALL:
;	T2/ REASON CODE
;	Q2/ PACKET ADDRESS
;	Q3/ PCB
;	PUSHJ	P,KLPRPT
;RETURN:
;	CPOPJ ALWAYS

KLPRPT:	MOVEM	T2,.PKXID(Q2)	;PUT REASON IN PACKET
	MOVEI	T1,OP.RCT	;OPERATION = READ COUNTERS
	MOVEI	T2,KLPDRG	;PRIORITY
	SETZB	T3,Q1		;NO FLAGS, CLEAR NODE NUMBER
	PJRST	DRVSND		;SEND THE PACKET AND RETURN
;ROUTINE TO SEND A DATAGRAM FOR A DIAGNOSTIC FUNCTION.
;CALL:
;	T1/ OPCODE
;	T3/ FLAGS
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,DIASND
;RETURN:
;	CPOPJ ALWAYS

DIASND:	MOVEI	T4,INCXID	;GET NEW TRANSACTION ID
	ADDB	T4,KLPXID	;...
	MOVEM	T4,.PKXID(Q2)	;SAVE FIRST WORD
	MOVEM	T4,.PKXID+1(Q2)	; AND SECOND WORD
	MOVEI	T2,KLPDRG	;PRIORITY
	PJRST	DRVSND		;FINISH UP VIA DRVSND


;ROUTINE TO SEND A DATAGRAM FROM THE DRIVER.
;CALL:
;	T1/ OPCODE
;	T2/ PRIORITY
;	T3/ FLAGS
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,DRVSND
;RETURN:
;	CPOPJ ALWAYS

DRVSND:	MOVX	T4,PK.SRB	;CLEAR ALL SOFTWARE RESPONSE BITS
	ANDCAM	T4,.PKVRT(Q2)	;...
	MOVX	T4,PK.DRV	;GET THE DRIVER RESPONSE BIT
	TXNE	T3,PF.RSP	;DOES THE DRIVER WANT THE BUFFER BACK?
	IORM	T4,.PKVRT(Q2)	;YES, SET THE FLAG
	PJRST	KLPSND		;SEND THE PACKET AND RETURN
;ROUTINE TO SEND A MESSAGE/DATAGRAM.
;CALL:
;	T1/ OPCODE
;	T2/ PRIORITY
;	T3/ FLAGS
;	Q1/ NODE NUMBER
;	Q2/ PACKET ADDRESS
;	Q3/ PCB ADDRESS
;	PUSHJ	P,KLPSND
;RETURN:
;	CPOPJ ALWAYS

KLPSND:	MOVEM	T3,.PKSTS(Q2)	;SAVE FLAGS AND INITIALIZE THIS WORD
	DPB	T1,PKYOP	;SAVE OPCODE
	DPB	Q1,PKYNOD	;SAVE NODE NUMBER
	MOVE	T1,T2		;GET PRIORITY (WHICH COMMAND QUEUE)
	CAILE	T1,MAXQUE	;TOO LOW?
	MOVEI	T1,MAXQUE	;YES, REDUCE IT TO LOWEST PRIORITY WE HAVE
	IMULI	T1,.PQLEN	;COMPUTE ADDRESS OF QUEUE
	ADDI	T1,.PCCQB	;OFFSET TO CORRECT QUEUE
	ADD	T1,Q3		;ADD IN PCB ADDRESS
	PUSHJ	P,PUTQUE	;INSERT THE PACKET ON QUEUE
IFN FTMP,<
	MOVE	T1,.PCCPU(Q3)	;GET CPU WHO OWNS PORT
	CAMN	T1,.CPCPN##	;ANOTHER CPU?
	JRST	KLPSN1		;NO, NO BOTHER
	MOVX	T1,ST.CQA	;GET THE BIT
	IORM	T1,.PCSTS(Q3)	;LET OTHER CPU KNOW AT ONCE/TICK LEVEL
	MOVE	T1,.CPQPC##	;GET THIS CPU'S QUEUED I/O FLAG
	IORM	T1,DOORBL##	;SET QUEUED I/O FLAG
	POPJ	P,		;RETURN

KLPSN1:	MOVX	T1,ST.CQA	;CLEAR THE BIT
	ANDCAM	T1,.PCSTS(Q3)	; IN CASE OTHER CPU SET IT
>; END IFN FTMP
	MOVE	T1,.CPUPT##	;GET CPU UPTIME WHEN PACKET QUEUED
	MOVEM	T1,.PCKCT(Q3)	;SAVE FOR KEEP ALIVE FAILURE CHECKS
	MOVE	T1,.PCPIA(Q3)	;GET KLIPA PI ASSIGNMENT
	CONO	KLP,CO.CQA+CO.BTS(T1) ;KICK THE PORT
	POPJ	P,		;RETURN TO CALLER
;ROUTINE TO GET A BUFFER FROM THE DATAGRAM FREE QUEUE
;CALL:
;	Q3/ PCB
;	PUSHJ	P,KLPGDB
;RETURN:
;	CPOPJ IF NONE AVAILABLE
;	CPOPJ1 IF OK WITH:
;	Q2/ ADDRESS OF BUFFER

KLPGDB:	XMOVEI	T1,.PCDFQ(Q3)	;WHICH QUEUE
	PUSHJ	P,REMQUE	;GET BUFFER
	  POPJ	P,		;EMPTY
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	LINK/DELINK PACKET ROUTINES


;ROUTINE TO LINK PACKETS ONTO A FREE QUEUE.
;CALL:
;	T1/ ADDRESS OF QUEUE
;	Q2/ ADDRESS OF FIRST PACKET
;	PUSHJ	P,LNKQUE
;RETURN:
;	CPOPJ ALWAYS

LNKQUE:	PUSH	P,(Q2)		;SAVE ADDRESS OF NEXT PACKET
	PUSHJ	P,PUTQUE	;STUFF THE PACKET ONTO THE FREE QUEUE
	POP	P,Q2		;GET ADDRESS OF NEXT PACKET BACK
	JUMPN	Q2,LNKQUE	;KEEP LOOPING UNTIL ALL ARE LINKED
	POPJ	P,		;RETURN
	SUBTTL	THREE-WORD QUEUE MANIPULATION ROUTINES


;ROUTINE TO REMOVE FIRST PACKET FROM A QUEUE.
;CALL:
;	T1/ ADDRESS OF QUEUE
;	PUSHJ	P,REMQUE
;RETURN:
;	CPOPJ IF QUEUE WAS EMPTY
;	CPOPJ1 WITH:
;	Q2/ PACKET ADDRESS

REMQUE:	CIOFF			;NO INTERRUPTS
	SETZ	T3,		;INITIALIZE COUNT
REMQU1:	SKIPGE	.PQIWD(T1)	;SKIP IF INTERLOCK NOT AVAILABLE
	AOSE	.PQIWD(T1)	;AVAILABLE, TRY TO GET IT
	AOSA	T3		;NOT AVAILABLE, INCREMENT COUNT AND SKIP
	JRST	REMQU2		;GOT IT
	CAIGE	T3,TIMOUT	;WAITED LONG ENOUGH?
	JRST	REMQU1		;NO, TRY AGAIN
	AOS	BRKCNT		;YES. DO IT ANYWAY. BUMP COUNTER
	BUG.	(INF,KLPRMQ,KLPSER,HARD,<Queue interlock timeout>,,)

REMQU2:	PUSHJ	P,CHKMPT	;IS QUEUE ALREADY EMPTY?
	  JRST	QRET		;EMPTY
	AOS	(P)		;NOT EMPTY. SET FOR SKIP/SKIP2 RETURN
	MOVE	T2,.PQFLI(T1)	;GET 1ST PACKET FROM QUEUE
	PUSHJ	P,CHKPAK	;GET ITS VIRTUAL ADDRESS
	  JRST	REMQU4		;VIRTUAL ADDR DOESN'T MATCH PHYSICAL!
	MOVE	Q2,T2		;VIRTUAL ADDR OF PACKET IN Q2
	DMOVE	T2,.PKFLI(Q2)	;GET FLINK, BLINK OF THIS PACKET
	CAME	T2,T3		;FLINK=BLINK?
	JRST	REMQU3		;NO, QUEUE IS STILL NON-EMPTY
	DMOVEM	T2,.PQFLI(T1)	;YES. QUEUE IS EMPTY. POINT HEADER AT ITSELF
QRET:	SETOM	.PQIWD(T1)	;RELEASE INTERLOCK
	PJRST	CIONPJ##	;INTERRUPTS BACK ON AND RETURN

REMQU3:	MOVEM	T2,.PQFLI(T1)	;POINT QUEUE HEADER AT NEXT PACKET
	ADDI	T2,.PKBLI	;POINT AT FLINK OF NEXT PACKET
	PUSHJ	P,PHMOVM##	;POINT NEXT PACKET BACK AT HEADER
	JRST	QRET		;NON-SKIP RETURN

;HERE IF VIRTUAL ADDR IN PACKET ISN'T WHAT IT SHOULD BE

REMQU4:	MOVE	T4,.PQFLI(T1)	;GET THE FLINK WORD
	BUG.	(CHK,KLPVIR,KLPSER,HARD,<Virtual address in packet is wrong>,<<T1,QUEUE>,<T2,VMA>,<T3,PMA>,<T4,FLINK>>,)
	MAP	T2,.PQFLI(T1)	;GET PHYSICAL ADDR OF QUEUE HEADER
	TXZ	T2,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVEM	T2,.PQFLI(T1)	;RESET QUEUE TO EMPTY
	MOVEM	T2,.PQBLI(T1)	;...
	SOS	(P)		;GIVE EMPTY-QUEUE RETURN
	JRST	QRET		;NON-SKIP RETURN
;ROUTINE TO INSERT A PACKET ONTO A QUEUE.
;CALL:
;	Q2/ ADDRESS OF PACKET
;	T1/ ADDRESS OF QUEUE
;	PUSHJ	P,PUTQUE
;RETURN:
;	CPOPJ ALWAYS

PUTQUE:	CIOFF			;NO INTERRUPTS
	SETZ	T3,		;INITIALIZE COUNT
PUTQU1:	SKIPGE	.PQIWD(T1)	;SKIP IF INTERLOCK NOT AVAILABLE
	AOSE	.PQIWD(T1)	;AVAILABLE, TRY TO GET IT
	AOSA	T3		;NOT AVAILABLE, INCREMENT COUNT AND SKIP
	JRST	PUTQU2		;GOT IT
	CAIGE	T3,TIMOUT	;WAITED LONG ENOUGH?
	JRST	PUTQU1		;NO, TRY AGAIN
	AOS	BRKCNT		;YES. DO IT ANYWAY. BUMP COUNTER
	BUG.	(INF,KLPPTQ,KLPSER,HARD,<Queue interlock timeout>,,)
PUTQU2:	STOR	Q2,PKVRT,(Q2)	;SAVE VIRTUAL ADDR IN PACKET
	MAP	T3,.PKFLI(Q2)	;PHYSICAL ADDR OF PACKET
	TXZ	T3,MP.NAD	;CLEAR NON-ADDRESS BITS
	MAP	T2,.PQFLI(T1)	;PHYSICAL ADDR OF QUEUE HEADER
	TXZ	T2,MP.NAD	;CLEAR NON-ADDRESS BITS
	CAME	T2,.PQFLI(T1)	;FLINK POINT AT ITSELF (EMPTY QUEUE)?
	JRST	PUTQU3		;NO
	MOVEM	T2,.PKFLI(Q2)	;YES. POINT PACKET BACK AT QUEUE HEADER
	MOVEM	T2,.PKBLI(Q2)
	MOVEM	T3,.PQBLI(T1)	;POINT PCB FLINK AND BLINK AT PACKET
	MOVEM	T3,.PQFLI(T1)
	JRST	QRET

PUTQU3:	MOVEM	T2,.PKFLI(Q2)	;POINT FLINK OF PACKET AT PCB
	MOVE	T2,.PQBLI(T1)	;GET FORMER END OF QUEUE
	MOVEM	T3,.PQBLI(T1)	;POINT QUEUE TAIL AT THIS PACKET
	MOVEM	T2,.PKBLI(Q2)	;POINT BLINK OF THIS PACKET AT FORMER END
	PUSHJ	P,PHMOVM##	;POINT BLINK OF FORMER END AT THIS PACKET
	JRST	QRET
;ROUTINE TO CHECK IF A QUEUE IS EMPTY.
;CALL:
;	T1/ ADDRESS OF QUEUE
;	PUSHJ	P,CHKMPT
;RETURN:
;	CPOPJ IF QUEUE IS EMPTY
;	CPOPJ1 IF QUEUE ISN'T EMPTY

CHKMPT:	MAP	T2,.PQFLI(T1)	;GET PHYSICAL ADDRESS OF QUEUE HEADER
	TXZ	T2,MP.NAD	;CLEAR NON-ADDRESS BITS
	CAME	T2,.PQFLI(T1)	;DOES QUEUE POINT AT ITSELF (EMPTY)?
	AOS	(P)		;NO, SET FOR SKIP
	POPJ	P,		;RETURN


;ROUTINE TO RETURN THE VIRTUAL ADDRESS OF A PACKET.
;CALL:
;	T2/ PACKET PHYSICAL ADDRESS
;	PUSHJ	P,CHKPAK
;RETURN:
;	CPOPJ IF PACKET IS BAD (VIRT ADDR IN PACKET DOESN'T MATCH PHYS ADDR)
;	CPOPJ1 IF PACKET OK, WITH:
;	T2/ PACKET VIRTUAL ADDRESS
;THIS ROUTINE MUST PRESERVE T1.

CHKPAK:	SAVEAC	(Q1)		;SAVE Q1
	MOVE	Q1,T2		;COPY PHYSICAL ADDRESS
	ADDI	T2,.PKVRT	;POINT AT VIRTUAL ADDRESS WORD WITHIN PACKET
	PUSHJ	P,PHMOV##	;FETCH THE CONTENTS
	TXZ	T2,PK.SRB	;CLEAR ALL SOFTWARE RESPONSE BITS
	MAP	T3,(T2)		;COMPUTE PHYSICAL ADDRESS OF THAT VIRTUAL ADDRESS
	TXZ	T3,MP.NAD	;CLEAR NON-ADDRESS BITS
	CAMN	T3,Q1		;PHYSICAL ADDRESS MATCH?
	AOS	(P)		;YES, SET FOR SKIP RETURN
	POPJ	P,		;RETURN TO CALLER
	SUBTTL	MISCELLANEOUS ROUTINES


;ROUTINE TO REVERSE THE FULL WORD IN T1, PRESERVING OTHER AC'S.
;CALL:
;	T1/ WORD TO REVERSE
;	PUSHJ	P,REVFUL
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T1/ WORD REVERSED
;PRESERVES OTHER AC'S

SRVFUL::!			;ANOTHER NAME FOR THIS ROUTINE
REVFUL::PUSHJ	P,SAVE3##	;SAVE P1-P3
	LDB	P1,PBYTE1	;PICK UP THE BYTES
	LDB	P2,PBYTE2	;...
	LDB	P3,PBYTE3	;...
	LSH	T1,^D24		;POSITION LEFT OVER BYTE
	DPB	P3,PBYTE2	;PUT THEM IN BACKWARDS
	DPB	P2,PBYTE3	;...
	DPB	P1,PBYTE4	;...
	POPJ	P,		;RETURN


;BYTE POINTERS USED BY REVFUL

PBYTE1:	POINT	8,T1,7
PBYTE3:	POINT	8,T1,23
PBYTE2:	POINT	8,T1,15
PBYTE4:	POINT	8,T1,31
;ROUTINE TO ACCOUNT FOR PPD BYTE WHICH PORT WILL ADD.
;WE MUST SWAP THE BYTES INTO ELEVEN FORMAT AND ACCOUNT
;FOR THE PPD FIELD IN THE TEXT LENGTH.
;CALL:
;	Q2/ PACKET ADDRESS
;	PUSHJ	P,MASAGE
;RETURN:
;	CPOPJ ALWAYS WITH:
;	Q2/ PACKET ADDRESS

MASAGE:	MOVEI	T1,C%PPDL	;GET # OF BYTES IN PPD FIELD
	ADDM	T1,.PKLEN(Q2)	;ACCOUNT FOR THE PPD BYTE
	LDB	T1,PKYPPM	;GET THE MOST SIGNIFICANT BYTE OF THE PPD
	LDB	T2,PKYPPL	; AND THE LEAST SIGNIFICANT
	DPB	T2,PKYPPM	;SWAP THE TWO BYTES
	DPB	T1,PKYPPL	; INTO THE CORRECT FORMAT
	POPJ	P,		;RETURN


;ROUTINE TO SWAP THE BYTES IN THE PPD FIELD OF A PACKET WHICH HAS COME
;OFF THE BUS.  WE CAN ALWAYS ASSUME THE PPD BYTE IS IN ELEVEN FORMAT
;SINCE THE HSC SENDS IT THAT WAY AND OUR PORT SWAPS IT THAT WAY ON A
;SEND OPERATION.
;CALL:
;	Q2/ PACKET ADDRESS
;	PUSHJ	P,RMASAG
;RETURN:
;	CPOPJ ALWAYS WITH:
;	Q2/ PACKET ADDRESS

RMASAG:	LDB	T1,PKYOP	;GET THE OPCODE
	TRZ	T1,OP.RMT	;MAKE SURE THE REMOTE BIT IS OFF
	CAIE	T1,OP.SMS	;IS THIS A MESSAGE?
	CAIN	T1,OP.SDG	; OR A DATAGRAM?
	SKIPA	T1,[-C%PPDL]	;YES, GET NEGATIVE LENGTH OF PPD FIELD AND SKIP
	POPJ	P,		;NOT A MESSAGE OR DATAGRAM, RETURN
	ADDM	T1,.PKLEN(Q2)	;ACCOUNT FOR THE PPD FIELD LENGTH
	LDB	T1,PKYPPM	;GET MOST SIGNIFICANT BYTE OF THE PPD FIELD
	LDB	T2,PKYPPL	; AND THE LEAST SIGNIFICANT
	DPB	T2,PKYPPM	;SWAP THE TWO BYTES
	DPB	T1,PKYPPL	; INTO TEN FORMAT
	POPJ	P,		;RETURN
;ROUTINE TO GET THE NEXT COMMAND REFERENCE NUMBER.
;CALL:
;	PUSHJ	P,PPDGCN
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T4/ COMMAND REFERENCE NUMBER
;PRESERVES ALL OTHER AC'S.

PPDGCN::AOS	T4,CRFNUM	;BUMP COMMAND REFERENCE NUMBER
	ANDI	T4,37777	;KEEP JUST 14 BITS WORTH
	JUMPE	T4,PPDGCN	;DON'T ALLOW COMMAND REFERENCE NUMBER OF ZERO
	LSH	T4,4		;POSITION FOR SCA LAND
	POPJ	P,		;RETURN
	SUBTTL	MEMORY ALLOCATION ROUTINE


;ROUTINE TO ALLOCATE NON-ZERO SECTION MEMORY FOR SCA DATA STRUCTURES.
;CALL:
;	T2/ NUMBER OF WORDS DESIRED
;	PUSHJ	P,GETCOR
;RETURN:
;	CPOPJ IF NOT AVAILABLE
;	CPOPJ1 IF SUCCESS WITH:
;	T1/ ADDRESS OF CHUNK ALLOCATED

GETCOR:	PUSHJ	P,SAVE3##	;SAVE XBLT ACS
	MOVEI	P1,-1(T2)	;SAVE COUNT MINUS ONE
	PUSHJ	P,GETSWS##	;GET THE SPACE
	  POPJ	P,		;SORRY
	MOVE	P2,T1		;START OF CHUNK
	XMOVEI	P3,1(P2)	;SECOND WORD OF CHUNK
	SETZM	(P2)		;ZERO FIRST WORD
	EXTEND	P1,[XBLT]	;AND THE REMAINDER
	JRST	CPOPJ1##	;SKIP RETURN
;SUBROUTINE LIKE ONCMAP BUT ALWAYS CREATES PAGES WRITABLE AND NON-CACHED
;(FOR INTERFACING TO TOPS-20 CODE).
;CALL:
;	T1/ NUMBER OF PAGES
;	PUSHJ	P,PGRSKD
;RETURN:
;	CPOPJ IF NOT ENOUGH FREE PAGES
;	CPOPJ1 WITH:
;	T1/ ADDRESS OF THE FIRST PAGE ALLOCATED

PGRSKD::MOVE	T2,T1		;COPY NUMBER OF PAGES REQUESTED
	LSH	T2,P2WLSH##	;CONVERT TO WORDS
	ADD	T2,SCAFRE##	;COMPUTE NEW FIRST FREE
	MOVE	T1,SCAFRE##	;WHERE TO START
	PUSHJ	P,NZSCGT##	;ALLOCATE THE MEMORY SPACE
	MOVEM	T2,SCAFRE##	;NEW FIRST FREE ADDRESS

;LINK THE PAGES TOGETHER VIA THE FIRST WORD OF THE PAGE.
;THE LINK WORD OF THE LAST PAGE IS ZERO.

	SUB	T2,T1		;COMPUTE NUMBER OF WORDS OBTAINED
	LSH	T2,W2PLSH##	;CONVERT TO THE NUMBER OF PAGES
	PUSH	P,T1		;SAVE STARTING ADDRESS
PGRSK1:	SETZM	(T1)		;ZERO LINK TO NEXT PAGE
	PUSH	P,T2		;SAVE COUNT
	MOVEI	T2,PGSIZ-1	;NUMBER OF WORDS TO ZERO
	MOVE	T3,T1		;FIRST WORD TO COPY
	XMOVEI	T4,1(T1)	;DESTINATION
	EXTEND	T2,[XBLT]	;ZERO THE PAGE
	POP	P,T2		;RESTORE COUNT
	SOJLE	T2,TPOPJ1##	;JUMP WHEN DONE
	XMOVEI	T3,PGSIZ(T1)	;ADDRESS OF NEXT PAGE
	MOVEM	T3,0(T1)	;LINK THAT PAGE TO THIS
	MOVE	T1,T3		;COPY NEXT PAGE ADDRESS
	JRST	PGRSK1		;LOOP FOR REMAINING PAGES
	SUBTTL	BYTE POINTERS


;POINTERS APPLICABLE TO ALL TYPES OF PACKETS

PKYSTS:	POINT PKSSTS,.PKSTS(Q2),PKPSTS ;STATUS
PKYSFD:	POINT PKSSTS-1,.PKSTS(Q2),PKPSTS ;STATUS MINUS ERROR BIT
PKYFLG:	POINT PKSFLG,.PKSTS(Q2),PKPFLG ;FLAGS
PKYOP:	POINT PKSOP,.PKSTS(Q2),PKPOP   ;OP CODE
PKYNOD:	POINT PKSNOD,.PKSTS(Q2),PKPNOD ;NODE TO SEND TO

;POINTERS SPECIFIC TO SPECIFIC PACKETS

PKYREG:	POINT PKSREG,.PKREG(Q2),PKPREG ;REGISTER TO READ
PKYDTA:	POINT PKSDTA,.PKREG(Q2),PKPDTA ;DATA FROM REGISTER READ
PKYCND:	POINT PKSCND,.PKPND(Q2),PKPCND ;NODE TO POINT PERFORMANCE COUNTER AT
PKYPST:	POINT PKSPST,.PKPST(Q2),PKPPST ;REMOTE PORT STATE
PKYRST:	POINT PKSRST,.PKPST(Q2),PKPRST	;REMOTE PORT STATE AND MAINTENANCE FIELD
PKYRND:	POINT PKSRND,.PKPST(Q2),PKPRND ;RESETTING NODE NUMBER

;PPD BYTE POINTERS

PKYPPM:	POINT 8,.PKLEN(Q2),7	       ;MOST SIG BYTE OF PPD FIELD IN 10 FORMAT
PKYPPL:	POINT 8,.PKLEN(Q2),15	       ;LEAST SIG BYTE OF PPD FIELD IN 10 FORMAT
PKYPPD:	POINT PKSPPD,.PKLEN(Q2),PKPPPD ;CI.PPD

;LENGTH BYTE

PKYLEN:	POINT PKSLEN,.PKLEN(Q2),PKPLEN	;LENGTH FOR DATAGRAMS AND MESSAGES
	SUBTTL	LOOPBACK CRC CODES


;THIS TABLE CONTAINS THE CRC CODES NEEDED FOR A LOOPBACK PACKET.
;THE TABLE IS INDEXED BY THE CI NODE NUMBER.

LPBLEN==6			;LENGTH OF LOOPBACK PACKET

LPBCRC:	365137,,27260		;NODE 0
	461317,,343360		;NODE 1
	777542,,171600		;NODE 2
	73762,,215700		;NODE 3
	142070,,514540		;NODE 4
	646250,,670440		;NODE 5
	550405,,442120		;NODE 6
	254625,,726020		;NODE 7
	731224,,250400		;NODE 8
	35004,,134500		;NODE 9
	323651,,306060		;NODE 10
	427471,,62160		;NODE 11
	516363,,763320		;NODE 12
	212143,,407220		;NODE 13
	104716,,635740		;NODE 14
	600536,,551640		;NODE 15
	SUBTTL	PROTOTYPE MICROLOADER PARAMETER BLOCK


;PROTOTYPE MICROCODE LOADER BLOCK

KLPULB:	EXP	.BTKLP##	;MICROCODE INDEX
	XWD	KLP,-1		;DEVICE CODE,,UNIT NUMBER
	SIXBIT	/KLIPA/		;INTERFACE NAME
	SIXBIT	/CI20/		;CHANNEL NAME
	EXP	MINUVR		;MINIMUM MICROCODE VERSION
	EXP	0		;DATE/TIME OF LOAD SUCCESS OR FAILURE
	EXP	0		;MICROCODE VERSION
	EXP	0		;POINTER TO MAGIC TABLE
	EXP	0		;MICROCODE LENGTH
	EXP	0		;MICROCODE ADDRESS
	CONI	0,T2		;CONI
	CONO	0,(T2)		;CONO
	DATAI	0,T2		;DATAI
	DATAO	0,T2		;DATAO
	SUBTTL	IMPURE STORAGE


	$LOW

BHDIPT:	EXP	0		;INITIAL POINTER FOR BHD SEARCHES
BHDCPT:	EXP	0		;CURRENT POINTER FOR BHD SEARCHES
BHDEND:	EXP	0		;LAST ADDRESS IN BUFFER DESCRIPTOR TABLE
BHDADR::EXP	0		;PHYSICAL ADDRESS OF BUFFER DESCRIPTOR TABLE
				; (USED TO SET UP .PCBDT IN THE PCB)
BHDPGS::EXP	0		;NUMBER OF PAGES IN BUFFER DESCRIPTOR TABLE
BHDNUM:	EXP	0		;NUMBER OF BUFFER HEADER DESCRIPTORS

BSDVRT:	EXP	0		;VIRTUAL ADDRESS OF START OF BSD AREA
BSDADR::EXP	0		;PHYSICAL ADDRESS OF START OF BSD AREA
BSDLOC:	EXP	0		;POINTER TO FREE BSD CHAIN
BSDPGS::EXP	0		;NUMBER OF PAGES FOR BUFFER SEGMENT DESCRIPTORS
BSDNUM:	EXP	0		;NUMBER OF BUFFER SEGMENT DESCRIPTORS

BRKCNT:	EXP	0		;NUMBER OF TIMES A PCB INTERLOCK WAS BROKEN

KLPXID:	EXP	0		;TRANSACTION ID COUNTER

CRFNUM:	EXP	0		;COMMAND REFERENCE NUMBER

	$HIGH
	SUBTTL	THE END


KLPFOO:	BUG.	(HLT,KLPOHF,KLPSER,SOFT,<Oh foo!>,,)

KLPEND::!END