Google
 

Trailing-Edge - PDP-10 Archives - BB-F492Z-DD_1986 - 10,7/703mon/d8kint.mac
There are 3 other files named d8kint.mac in the archive. Click here to see a list.
TITLE	D8KINT - SERVICE FOR KMC-11/DUP-11 V100
SUBTTL	W. E. MATSON/TARL/TL	29 OCT 85
	SEARCH	F, S, NETPRM, D36PAR
	$RELOC
	$HIGH

	.DIRECT	FLBLST

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1978,1986>
;COPYRIGHT (C) 1978,1979,1980,1982,1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.
;
;
;
XP VD8KINT,100
                     Comment @ Loose ends.

    o	Worry about 2 lines to same node.  Also worry about
	"connected to self".  (ie. lines connected together.)

                         End Comment @

D8KINT::ENTRY	D8KINT
	SUBTTL	FEK -- FEK INTERFACE FOR D8KINT

;D8KINT FEK INTERFACE.  ENTRIES ARE:
;
;	D8KONC			;INITIALIZATION FOR FEK (ZERO KDL PAGE).
;	D8KSEC			;ONCE A SECOND CODE.
;	D8KRDD			;SET UP A READ REQUEST (NO-OP).
;	D8KWRT			;QUEUE A BUFFER FOR OUTPUT.
;	D8KCRS			;SHUT DOWN A LINE
;	D8KDWN			;NOT USED
;	D8KUP			;NOT USED
;	D8KSTC			;CALLED WITH  "U" := STC MESSAGE TO SEND
;
;FEKINT IN NETSER IS CALLED WHENEVER
;
;	A KDL GOES OFFLINE (FF.DWN INTERRUPT)
;	A MESSAGE IS RECEIVED (FF.IN INTERRUPT)
;	AN XMIT MESSAGE IS ACKED (FF.OUT INTERRUPT)

D8KDSP::CAIL	T1,FF.ONC	;RANGE CHECK FUNCTION
	CAILE	T1,FF.CPW	;  CODE AND STOP IF BAD
	PUSHJ	P,NTDSTP##	;++ ERROR: BAD FUNCTION CODE TO FEK
	JRST	@.+1(T1)	;DISPATCH TO APPROPRIATE ROUTINE
	JRST	NTFONC##	;ONCE ONLY CODE (USE NETSER'S DEFAULT)
	JRST	NTFSEC##	;USE NETSER DEFAULT (FOR NODE ID. ETC)
	JRST	D8KRDD		;SET UP A READ REQUEST
	JRST	D8KWRT		;SET UP A WRITE REQUEST
	JRST	D8KCRS		;CRASH THE FEK (CPU WENT (WILL BE) DOWN?)
	JRST	CPOPJ##		;FEK DOWN (NOT USED)
	JRST	CPOPJ##		;FEK UP (NOT USED)
	JRST	D8KSTC		;CALLED WITH "U := STC MESSAGE TO SEND"
	JRST	D8KCRS		;SYSTEM SLEEPING AND
	JRST	D8KCRS		;  WAKING IS TOO COMPLEX TO THINK ABOUT NOW


D8KCRS: PUSHJ	P,NTDSTP##	;THIS SHOULD NEVER EVER HAPPEN.  WE
	POPJ	P,		;  DO BETTER TIMING THAN NETSER.  IF
				;  IT DOES HAPPEN, SOMETHING IS HORRIBLY
				;  WRONG.

D8KRDD:	AOS	FEKBSI(J)	;SET FLAG SAYING WE HAVE AN INPUT BUFFER
	POPJ	P,		;KDPINT CALLS US WHEN DATA ARRIVES
D8KWRT:	NETOFF			;FIRST DISABLE INTERRUPTS
	SKIPG	FEKOCT(J)	;IF THERE ARE NO MORE MESSAGES,
	JRST	NTONPJ##	;THEN WE ARE DONE
	HRRZ	T3,FEKOAD(J)	;GET THE ADDRESS OF THE NEXT MSG
	HRRZ	T1,PCBBLK(T3)	;GET THE ADDRESS OF THE ONE AFTER IT
	HRRZM	T1,FEKOAD(J)	;MAKE THE SECOND THE NEW FIRST
	SKIPN	T3		;IF WE DIDN'T GET A MESSAGE,
	PUSHJ	P,NTDSTP##	;THE OUTPUT QUEUE IS MESSED UP
	SOS	FEKOCT(J)	;COUNT DOWN ONE LESS MESSAGE
	NETON			;RE-ENABLE THE INTERRUPTS
	SETZM	MB.FMS(T3)	;MAKE SURE THIS LOOKS LIKE AN ANF MSG
	MOVEI	T1,DC.FQB	;GET THE QUEUE DATA FUNCTION
	MOVE	T2,FEKUNI(J)	;GET THE KDL PAGE ADDRESS
	PUSH	P,T3		;SAVE THE MSG POINTER
	PUSHJ	P,KDADSP##	;QUEUE THE MESSAGE
	 JRST	[MOVEI	T1,DC.IOF ;GIVE THE NOTIFICATION OF
		 MOVE	T2,J	  ; THE OUTPUT NOT DONE
		 POP	P,T3	  ;RESTORE MSG POINTER
		 PUSHJ	P,D8KK2D  ;DO WHATEVER NEEDED
		 JRST	D8KWRT]   ;CHECK FOR MORE
	POP	P,T3		;RESTORE MSG POINTER
	JRST	D8KWRT		;AND GO CHECK FOR MORE

D8KSTC:	SKIPGE	T1,FEKBLK(J)	;GET THE FEK'S FLAGS
	POPJ	P,		;IF RUNNING, THEN DON'T TAKE THE LINE DOWN
	TLNE	T1,FK.MAI	;IF WE'RE IN MAINT MODE
	JRST	D8KST1		;  THEN WE CAN JUST QUEUE THE MESSAGE
	MOVEI	T1,DC.FSM	;IF NOT IN MAINT MODE, GET FUNCTION
	MOVE	T2,FEKUNI(J)	;  TO PUT IN MAINT MODE, AND
	PUSHJ	P,KDADSP##	;  CALL THE DRIVER.
	 POPJ	P,		;COULDN'T DO ANYTHING, WE DON'T OWN LINE
	MOVSI	T1,FK.MAI	;GET THE MAINT BIT
	IORM	T1,FEKBLK(J)	;  AND SAY WE'RE IN MAINT MODE
D8KST1:	MOVEI	T1,DC.FQB	;IF IN MAINT MODE, GET "QUEUE OUTPUT"
	MOVE	T2,FEKUNI(J)	;  FUNCTION, AND PASS THE
	MOVE	T3,U		;  BUFFER OFF TO THE DRIVER
	SETZM	MB.FMS(T1)	;  MAKE SURE IT LOOKS LIKE AN ANF-10 MSG
	PUSHJ	P,KDADSP##	;CALL KDPINT TO SEND THE MESSAGE
	 JRST	[MOVEI	T1,DC.IOF ;GIVE THE NOTIFICATION OF
		 MOVE	T2,J	  ; THE OUTPUT NOT DONE
		 MOVE	T3,U      ;SET UP MSG POINTER
		 PUSHJ	P,D8KK2D  ;DO WHATEVER NEEDED
		 JRST	CPOPJ1]   ;GIVE GOOD RETURN
	RETSKP			; AND GIVE A GOOD RETURN
	SUBTTL	KDPINT - INTERFACE FROM KMC/DUP DRIVER

;D8KK2D	HERE ON DISPATCH ENTRYS FROM THE KMC-11/DUP-11 KONTROLLER
;CALL	T1 := FUNCTION CODE (BELOW)
;	T2 := FEK/LINE-BLOCK ADDRESS
;	T3 := BUFFER ADDRESS OR BYTE COUNT

D8KK2D::CAIL	T1,DC.IPU	;FIRST RANGE CHECK THE
	CAILE	T1,DC.ICC	; FUNCTION CODE
	PUSHJ	P,NTDSTP##	;IF FUNCTION OUT OF RANGE, DUMP IT.
	PUSH	P,U		;SAVE AN AC
	PUSH	P,J		;AND ANOTHER AC
	MOVE	J,T2		;USE J AS THE FEK ADDRESS
	PUSHJ	P,@D8KKKD(T1)	;DISPATCH BASED ON FUNCTION
	POP	P,J		;RESTORE ANOTHER AC
	POP	P,U		;RESTORE AN AC
	POPJ	P,		;BACK TO WHOMEVER

D8KKKD:	IFIW	D8PUP		;(00) PROTOCOL UP (START/STACK COMPLETE)
	IFIW	D8PDN		;(01) PROTOCOL DOWN (ALL BUFFERS RETURNED)
	IFIW	D8MAI		;(02) MAINT MSG RECEIVED (IMPLIES PDN)
	IFIW	D8STR		;(03) START RECEIVED (LEAVE MAINT PROTOCOL)
	IFIW	D8ODN		;(04) OUTPUT DONE (MESSAGE SUCCESSFULY SENT)
	IFIW	D8OND		;(05) OUTPUT NOT DONE (LINE IS GOING DOWN)
	IFIW	D8IDN		;(06) INPUT DONE (T3 := BUFFER ADDRESS)
	IFIW	D8RQI		;(07) REQUEST INPUT BUFFER (T3 := BYTE COUNT)
	IFIW	NTDSTP##	;(10) NEW LINE CREATION
	IFIW	NTDSTP##	;(11) OLD LINE DISSOLUTION
;D8PUP	HERE WHEN PROTOCOL IS ESTABLISHED (START/STACK/ACK COMPLETE)
;	MAKE SURE KONTROLLER IS HONEST AND THEN SET THE FEK ONLINE

D8PUP:	SKIPGE	FEKBLK(J)	;MAKE SURE THE FEK IS OFF-LINE
	PUSHJ	P,NTDSTP##	;IF IT'S ONLINE, KONTROLLER IS BUGGY
	MOVSI	T1,FK.MAI	;GET BIT AND CLEAR
	ANDCAM	T1,FEKBLK(J)	; MAINTENANCE MODE
	MOVSI	T1,FK.ONL	;GET BIT AND SET
	IORM	T1,FEKBLK(J)	; FEK-IS-ONLINE (WILL SOON QUEUE NODE-ID)
	POPJ	P,		;ALL DONE

;D8PDN	HERE WHEN PROTOCOL IS TERMINATED (TIME-OUT OR WHATEVER)
;	MAKE SURE WE ARE IN PROTOCOL, THEN CLEAR FEK-ONLINE

D8PDN:	SKIPL	FEKBLK(J)	;IF WE THINK THE FEK IS DOWN NOW,
	POPJ	P,		;THEN NO NEED TO CALL NETSER
	MOVEI	T1,FI.DWN	;GET THE "FEK CRASHED" FUNCTION CODE
	PJRST	CALNET		;AND TELL NETSER THE BAD NEWS
;D8MAI	HERE WHEN WE RECEIVE A MAINTENANCE MESSAGE IN NORMAL PROTOCOL
;	AT THIS POINT ALL OUTPUT BUFFERS HAVE BEEN RETURNED

D8MAI:	SKIPGE	FEKBLK(J)	;MAKE SURE WE DON'T THINK THAT
	PUSHJ	P,NTDSTP##	;  WE ARE UP AND RUNNING.
	MOVSI	T1,FK.MAI	;GET AND SET THE
	IORM	T1,FEKBLK(J)	; MAINT-MODE BIT (SO WE ALLOC STC BLKS)
	POPJ	P,		;DONE.  RESTORE AC'S AND LEAVE

;D8STR	HERE WHEN WE RECEIVE A START MESSAGE WHILE IN MAINTENANCE PROTOCOL
;	AT THIS POINT ALL OUTPUT BUFFERS HAVE BEEN RETURNED.

D8STR:	MOVEI	T1,DC.FIL	;NOW TELL THE KONTROLLER
	MOVE	T2,FEKUNI(J)	; TO INITIALIZE ITSELF IN "NORMAL" MODE
	PUSHJ	P,KDADSP##	;DO IT
	 POPJ	P,		;WE NO LONGER OWN THE LINE, NOTHING MATTERS
	MOVSI	T1,FK.MAI	;GET AND CLEAR THE MAINT
	ANDCAM	T1,FEKBLK(J)	; MODE BIT
	POPJ	P,		;RESTORE AC'S AND LEAVE


;D8ODN	HERE WHEN A BUFFER HAS BEEN SUCCESSFULLY OUTPUT
;D8OND	HERE WHEN A BUFFER COULD NOT BE SENT (LINE DOWN ETC.)
;	FIGURE OUT IF WE ARE IN MAINT/NORMAL MODE AND EITHER
;	DESTROY OR RETURN THE BUFFER TO NETSER

D8ODN:
D8OND:	MOVE	T1,FEKBLK(J)	;GET THE FEK STATUS BITS
	TLNE	T1,FK.MAI	; AND IF WE ARE IN MAINT MODE
	JRST	D8MOD		; THEN GO FREE THE USLESS BUFFER
	MOVEI	T1,FI.ODN	;GET THE FEK CODE FOR OUTPUT DONE
	HRRZM	T3,FEKODN(J)	;PUT ADDRESS WHERE NETSER LIKES IT
	PJRST	CALNET		; AND RETURN THE BUFFER TO NETSER

D8MOD:	MOVE	J,T3		;PUT ADDRESS OF STD IN "J"
	PJRST	GIVSTC##	; AND TELL NETSER TO FREE THE STORAGE
;D8IDN	HERE WHEN A MESSAGE HAS BEEN RECEIVED (T3 := MESSAGE)
;	DETERMINE WHICH MODE WE ARE IN (NORMAL/MAINT) AND FORWARD
;	THE MESSAGE TO NETSER APPROPRIATLY

D8IDN:	MOVE	T1,FEKBLK(J)	;GET THE FEK'S STATUS BITS
	TLNE	T1,FK.MAI	;IF WE'RE IN MAINT MODE,
	JRST	D8IDNM		;THEN HANDLE MAINT MSGS
	SOS	FEKBSI(J)	;IN NORMAL MODE, SAY WE ARE NO LONGER BUSY
	CAME	T3,FEKIAD(J)	;MAKE SURE WE GOT THE RIGHT MESSAGE BACK
	PUSHJ	P,NTDSTP##	;WRONG MESSAGE RETURNED!
	MOVEI	T1,FI.RDD	;FUNCTION CODE IS READ-DONE
	PJRST	CALNET		;TELL NETSER

D8IDNM:	MOVEI	T1,FI.STC	;IF IN MAINT MODE, SAY IT'S MAINT DATA
	MOVE	U,T3		;COPY THE POINTER INTO NETSER'S FAVORITE
	PJRST	CALNET		; REGISTER, AND CALL NETSER


;D8RQI	HERE WHEN THE KONTROLLER WANTS US TO ALLOCATE SPACE FOR
;	AN INCOMING MESSAGE.  (SIZE OF MESSAGE IN T3)

D8RQI:	MOVE	T1,FEKBLK(J)	;GET THE FEK STATUS BITS, AND
	TLNE	T1,FK.MAI	; IF WE ARE IN MAINT MODE, THEN
	JRST	D8RQM		; GIVE THE KONTROLLER MAINT BUFFERS
	MOVE	T1,FEKIAD(J)	;GET THE BUFFER (BUT IT MAY NOT BE THERE)
	SKIPGE	FEKBSI(J)	;MAKE SURE THERE WAS A BUFFER THERE
	TDCA	T1,T1		;IF THERE WASN'T, SAY WE DIDN'T HAVE ONE
	SETZM	MB.FMS(T1)	;MAKE SURE IT LOOKS LIKE AN ANF-10 MSG
	POPJ	P,		;CLEAN UP AND RETURN

D8RQM:	MOVE	T1,T3		;COPY THE REQUESTED LENGTH
	PUSHJ	P,GETSTC##	;IF MAINT MODE, GET AN STC BLOCK
	 SETZ	J,		; IF NONE AVAILABLE, RETURN ZERO
	SKIPE	T1,J		;PUT POINTER IN T1 (OR A ZERO IF NO BUFFER)
	SETZM	MB.FMS(T1)	;MAKE SURE IT LOOKS LIKE AN ANF-10 MSG
	POPJ	P,		; AND RETURN
	SUBTTL	UTILITY ROUTINES.

;CALNET	ROUTINE TO CALL NETSER. PRESERVES REGISTERS THAT NETSER
;	LIKES TO CLOBBER
;CALL	PUSHJ	P,CALNET
;RETURN	POPJ	P,

CALNET:	PUSH	P,W		;W (NDB)
	PUSH	P,F		; F (DDB)
	PUSH	P,T4		;  AND T4 (???)
	PUSHJ	P,FEKINT##	;CALL NETSER
	POP	P,T4		;AND THEN
	POP	P,F		;  RESTORE
	POP	P,W		;  EVERYTHING
	POPJ	P,		;AND RETURN


	PRGEND			;OF D8KINT.MAC
TITLE	KDPSER - USER INTERFACE FOR KDP CONTROL V001
SUBTTL	W. E. MATSON	12 FEB 80
	SEARCH	F, S, NETPRM,  D36PAR
	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1981 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VKDPSER,001

                     Comment @ Loose ends.

                         End Comment @

KDPSER::ENTRY	KDPSER

	KDPMXQ==^D10		;MAXIMUM NUMBER OF INPUT OR OUTPUT MSGS
				; ALLOWED FOR A USER OF THE KDP: DEVICE
	IOSMAI==400		;IF SET, LINE SHOULD BE USED IN MAINT MODE
	IOSSRM==200		;IF SET, START WAS RECEIVED IN MAINT MODE
	IOSMRN==100		;IF SET, NORMAL MSG WAS RECEIVED IN MAINT MODE
	DALBUG==1		;IF TRUE, THEN WASTE LOTS OF TIME WORKING
				; AROUND LEWINE'S MICRO-CODE BUG


;ERROR BITS RETURNED IN DEVIOS
;	IOIMPM	LINE NOW "OWNED" BY THE KDP DEVICE (ANF/DECNET CONTROLS IT)
;	IODTER	LINE NOT "STARTED" YET (ONLY HAPPENS IN "NORMAL" MODE)
;	IODERR	LINE WENT DOWN (USUALLY IOSSRM OR IOSMRN ARE SET AS WELL)
;	IOBKTL	BLOCK TO LARGE -- ONE WAY OR THE OTHER
	SUBTTL	KDPDDB - PROTOTYPE DDB FOR THE KDP DEVICE

DEFINE	X(NAME<%%%OFF>,SIZE<1>),<NAME==:<%%%OFF==%%%OFF+SIZE>-SIZE>

	%%%OFF==DEVLLD		;INITIALIZE THE OFFSET

	X	DEVKDL		;XWD KDL-ADDRESS, INPUT MSG QUEUE
	X	DEVOBC		;COUNT OF OUTPUT BUFFERS OUTSTANDING
	X	KDPDBL,0	;LENGTH OF A KDP DDB

	PURGE	X		;FLUSH THE MACRO


;NOW LAY OUT THE PROTOTYPE DDB

DEFINE X(OFFSET,EXPR)<		;MACRO TO SET SELECTED LOCATIONS IN THE BLOCK
	RELOC KDPDDB+OFFSET	;GO TO THE RIGHT WORD
	EXPR			;ASSEMBLE IN THE EXPRESSION
>
	$LOW			;WE NEED TO LINK THIS IN
KDPDDB::X	DEVCHR,<XWD <6*HUNGST>+DVC2IO,KDLMMS+1> ;BUFFER SIZE
	X	DEVSER,<XWD 0,KDPUDS> ;DEFINE NETWORK DISPATCH VECTOR
	X	DEVMOD,<XWD DVIN!DVOUT,1_BYTMOD>
	X	DEVSTA,<XWD DEPLEN!.TYKDP,DEPEVM> ;VARIABLE BUFFERS, NO EVM
;	X	DEVCPU,<EXP 707B8> ;SET DEYPCL SO WILL RUN ON ANY CPU.
	X	NETLEN,0	;RESERVE ENOUGH SPACE FOR ENTIRE DDB

	$HIGH			;BACK TO PURE CODE
	PURGE	X		;FLUSH THE MACRO
	SUBTTL	KDPUUO -- UUO ENTRY TO KDPSRV

;DISPATCH TABLE (FROM UUOCON)

	JRST	CPOPJ##		;(-5) DEVICE OFF LINE
	JRST	CPOPJ##		;(-4) SPECIAL ERROR STATUS
	JRST	REGSIZ##	;(-3) LENGTH CAN BE GOTTEN FROM DDB
	JRST	CPOPJ##		;(-2) INITIALIZE (WE DO IT IN TSTKDP)
	JRST	CPOPJ##		;(-1) HUNG DEVICE
KDPUDS:	JRST	KDPREL		;(0)  RELEASE (MAKE SURE KDL DOESN'T POINT)
	JRST	KDPCLO		;(1)  CLOSE OUTPUT
	JRST	KDPOUT		;(2)  OUTPUT
	JRST	KDPIN		;(3)  INPUT
;TSTKDP	ROUTINE CALLED FROM UUOCON DURING A PHYSICAL DEVICE SEARCH
;CALL	T1 := DEVICE NAME
;RETURN	CPOPJ	NOT KDP, NOT IN OPEN UUO, ALREADY ASSIGNED OR NO PRIVS
;	CPOPJ1	F := DDB ADDRESS

TSTKDP::HLRZ	T2,T1		;GET THE RH OF THE NAME,
	CAIE	T2,'KDP'	; AND IF IT ISN'T "KDP"
	POPJ	P,		; THEN GIVE THE ERROR RETURN RIGHT AWAY
	LDB	T2,[POINT 9,M,8] ;GET THE OP-CODE OF THE UUO THAT GOT HERE
	CAIE	T2,<OPEN>_<-33>	;DON'T ALLOW RANDOM COMMANDS TO BUILD DDB'S
	POPJ	P,		;IF NOT AN OPEN UUO, THEN DEVICE NOT THERE
	PUSHJ	P,SAVT##	;SAVE THE TEAS
	PUSHJ	P,SAVE1##	; AND A PEA
	MOVE	P1,T1		;STORE THE DEVICE NAME IN P1 FOR THE DURATION
	MOVSI	T1,JP.POK	;ONE LAST QUICK CHECK, DON'T ALLOW
	PUSHJ	P,PRVBIT##	; JUST ANYONE TO USE THE DEVICE.
	  CAIA			; ONLY OPR AND POKE ALLOWED.
	POPJ	P,		;STUPID BASS AKWARDS UUO

;NOW CHECK THE NAME
	LDB	T1,KDNMPA	;GET THE MULTI-POINT ADDRESS
	JUMPN	T1,CPOPJ##	;THERE BETTER NOT BE ANY. WE DON'T SUPPORT IT
	LDB	T1,KDNKDP	;GET THE CONTROLLER NUMBER
	CAIG	T1,KDPMAX##	; AND MAKE SURE IT'S BOTH LEAGAL
	SKIPN	W,KDPTBL##(T1)	; AND THAT IT EXISTS
	POPJ	P,		;IF IT DOESN'T EXIST, USER CAN'T OPEN IT
	LDB	T1,KDNKDL	;GET THE LINE NUMBER ON THE KONTROLLER
	CAML	T1,KDPDPN(W)	; MAKE SURE THAT THE DUP NUMBER IS
	POPJ	P,		; IN RANGE. IF NOT, FAIL
	MOVEI	F,KDPKDL(W)	;NOW CALCULATE THE ADDRESS OF THE
	ADD	F,T1		; POINTER TO THE KDL PAGE
	SKIPN	F,(F)		;GET THE POINTER,
	POPJ	P,		;IF NO POINTER, THEN DUP DOESN'T EXIST
	SKIPE	KDLDDB(F)	;SEE IF SOMEONE ELSE OWN'S THE DDB
	JRST	CLRFPJ		; IF SO, ZERO F AND RETURN

;NOW BUILD A DDB
	MOVEI	T2,KDPDBL	;GET THE LENGTH OF A KDP DDB
	PUSHJ	P,GETWDS##	; AND ALLOCATE THE SPACE
	  JRST	CLRFPJ		; IF NOT ENOUGH, GIVE THE FAIL RETURN
	MOVSI	T2,KDPDDB	;GET THE ADDRESS OF THE PROTOTYPE
	HRR	T2,T1		; AND A COPY OF THE ADDRESS OF THE NEW ONE
	DDBSRL			;LOCK THE DDB CHAIN
	BLT	T2,KDPDBL-1(T1)	;INITIALIZE THE NEW ONE
	HRLM	T1,DEVSER+KDPDDB ; AND SET UP THE LINKS
	DDBSRU			;DDB IS IN PLACE, UNLOCK THE DDB CHAIN
	HRLM	F,DEVKDL(T1)	;MAKE THE DDB POINT TO THE KDL BLOCK
	EXCH	T1,KDLDDB(F)	;MAKE THE KDL BLOCK POINT TO THE DDB
	SKIPE	T1		;JUST A PARANOID CHECK.
	PUSHJ	P,NTDSTP##	; I DON'T THINK THIS IS POSSIBLE
	MOVE	F,KDLDDB(F)	;GET THE ADDRESS OF THE DDB
	MOVEM	P1,DEVNAM(F)	;SET THE DEVICE'S NAME
	RETSKP			; AND GIVE A GOOD RETURN TO DDBSRC
CLRFPJ:	SETZ	F,		;CLEAR "F" IF AN ERROR
	POPJ	P,		; AND RETURN
;KDPREL	ROUTINE TO PROCESS THE "RELEASE" FUNCTION OF A KDP
;CALL	F := ADDRESS OF THE DDB
;RETURN	CPOPJ			;ALWAYS

KDPREL:	PUSHJ	P,KDPCKO	;SEE IF WE ARE THE LINE'S "OWNER"
	  JRST	KDPRL1		;IF NOT, DON'T MESS WITH THE KONTROLLER
	MOVEI	T1,DC.FHL	;GET THE "HALT" FUNCTION CODE
	HLRZ	T2,DEVKDL(F)	;GET THE ADDRESS OF THE KDL PAGE
	PUSHJ	P,KDODSP##	;CALL THE KONTROLLER. TELL HIM TO HALE
	 JFCL			;IF WE DON'T OWN THE LINE WE DON'T CARE
KDPRL1:	SKIPE	DEVOBC(F)	;ALL BUFFERS SHOULD BE RETURNED BY NOW
	PUSHJ	P,NTDSTP##	;SINCE THEY ARE RETURNED BY THE "HALT" CALL
	HLRZ	T1,DEVKDL(F)	;CLEAR THE KDL'S POINTER TO THE DDB
	SETZM	KDLDDB(T1)	; SO WE DON'T GET ANY MORE INTERRUPTS

;NOW FREE ANY MSGS ON THE INPUT QUEUE
KDPRL2:	KDPOFF			;PROBABLY DON'T NEED TO, BUT...
	HRRZ	T1,DEVKDL(F)	;GET THE NEXT INPUT MESSAGE
	JUMPE	T1,KDPRL3	;IF NONE, THEN WE ARE DONE
	HRRZ	T2,MB.NXT(T1)	;GET THE MESSAGE AFTER THAT ONE
	HRRM	T2,DEVKDL(F)	; AND MAKE SURE WE GET IT NEXT
	KDPON			;QUEUE IS CONSISTANT AGAIN
	PUSHJ	P,GIVKDB	;RETURN THE BUFFER
	JRST	KDPRL2		; AND GO GET THE NEXT ONE

KDPRL3:	KDPON			;ALL MESSAGES HAVE BEEN FREED
	POPJ	P,		; AND WE'RE DONE
;ZAPKDP	ROUTINE TO DESTROY A KDP DDB
;CALL	F := DDB ADDRESS
;RETURN	CPOPJ
ZAPKDP::PUSHJ	P,KDPREL	;FIRST "RELEASE" IT (IN CASE OF SWAP READ ERR)
	MOVEI	T2,KDPDDB	;GET THE STARTING DDB
ZAPKD1:	MOVE	T1,T2		;FOLLOW THE DDB CHAIN
	HLRZ	T2,DEVSER(T1)	;NEXT DDB
	SKIPN	T2		;MAKE SURE THAT THE
	PUSHJ	P,NTDSTP##	;++ DDB WENT AWAY?
	CAIE	T2,(F)		;IS THIS THE ONE
	JRST	ZAPKD1		;NO CONTINUE
	DDBSRL			;LOCK THE DDB CHAIN
	HLRZ	T2,DEVSER(F)	;GET THE NEXT DDB
	HRLM	T2,DEVSER(T1)	;REMOVE THE DDB LINKS
	DDBSRU			;UNLOCK THE CHAIN
	HRRZ	T2,F		;GET THE ADDRESS OF THE DDB
	MOVEI	T1,KDPDBL	; AND IT'S LENGTH
	PUSHJ	P,GIVWDS##	;FREE THE STORAGE
	POPJ	P,		;AND WE'RE DONE.
;KDPIN	ROUTINE TO PROCESS THE IN UUO FOR THE KDP DEVICE
;CALL	F := DDB ADDRESS
;RETURN	CPOPJ			;ALWAYS
	T5==T4+1		;T5 FOR THE EXTEND INSTRUCTION
	T6==T5+1		;T6 FOR INDEXED MSD PTRS (DECNET)
IFN M-T6,<PRINTX ?KDPSER assumes that ACs M and T6 are the same
	  PRINTX ?Because it knows that M is OK to trash>

KDPIN:	PUSHJ	P,SAVE2##	;P1 = DEVIAD, P2 = INPUT MESSAGE
	MOVSI	S,IOBEG!IO	;CLEAR IOBEG SET "INPUT"
	ANDCAB	S,DEVIOS(F)	; FOR NO PARTICULARLY GOOD REASON
	PUSHJ	P,KDPCKM	;MAKE SURE WE'RE IN THE CORRECT MODE

KILOOP:	PUSHJ	P,KDPONL	;MAKE SURE THE KMC IS UP
	  POPJ	P,		; IF NOT, RETURN WITH ERROR BITS SET
	HRRZ	P1,DEVIAD(F)	;GET THE ADDRESS OF THE INPUT BUFFER
	SKIPE	T1,P1		;MAKE SURE THAT THERE IS ONE,
	EXCTUX	<SKIPGE (T1)>	; AND THAT IT IS EMPTY
	POPJ	P,		;IF NO EMPTY BUFFER, THEN RETURN
	PUSHJ	P,BRNGE##	;MAKE SURE THE BUFFER IS ADDRESSABLE
	HRRZ	P2,DEVKDL(F)	;GET THE ADDRESS OF THE INPUT MESSAGE
	JUMPE	P2,KIWAIT	;IF NO INPUT, GO WAIT FOR SOME

;NOW SET UP TO COPY THE DATA
	EXCTUX	<HLRZ T4,(P1)>	;GET THE LENGTH OF THE INPUT BUFFER (+1)
	SUBI	T4,1		;GET ACTUAL LENGTH IN WORDS
	LSH	T4,2		; AND CONVERT THAT TO BYTES
	MOVSI	T5,(POINT 8)	;MAKE A BYTE POINTER TO THE
	HRRI	T5,2(P1)	; USER'S INPUT BUFFER
	MOVE	T3,MB.FMS(P2)	;GET THE ADDRESS OF THE SEGMENT DESCRIPTOR
	MOVE	T1,MD.BYT(T3)	;FROM THE MSC, GET THE BYTE COUNT
	MOVE	T6,MD.ALA(T3)	;BYTE POINTER IS INDEXED BY T6
	MOVE	T2,MD.PTR(T3)	; AND THE BYTE POINTER
	CAMGE	T4,T1		;MAKE SURE THAT THE DATA WILL FIT,
	  JRST	K.BKTL		; IF NOT, THEN GIVE "BLOCK TOO LARGE"
	MOVEI	T4,3(T1)	;MAKE THE "DST" LENGTH BE THE SOURCE
	TRZ	T4,3		; LENGTH ROUNDED UP SO AS TO ZERO FILL LAST WD
	EXCTUU	<HRRM T1,1(P1)>	;STORE THE BYTE COUNT FOR THE USER
IFE DALBUG,<			;D.A.LEWINE
	PXCT	1,[EXTEND T1,[EXP MOVSLJ,0]] ;AND COPY THE DATA
	 PUSHJ	P,NTDSTP##	;WE CHECKED... THIS SHOULDN'T HAPPEN
>
IFN DALBUG,<			;UNTIL THEY GET THE MICRO-CODE RIGHT...
	JRST	.+3		;SKIP INTO THE MIDDLE OF THE LOOP
	ILDB	T4,T2		;GET THE NEXT BYTE
	EXCTUU	<IDPB T4,T5>	;STORE THE NEXT BYTE
	SOJGE	T1,.-2		;LOOP TILL ALL STORED
	SETZ	T3,		;CLEAR THE "TO COUNT"
>
;NOW DEQUEUE THE INPUT MESSAGE AND ADVANCE THE USER'S BUFFER
	KDPOFF			;KEEP INTERRUPT LEVEL OUT
	HRRZ	T1,MB.NXT(P2)	;GET THE ADDRESS OF THE NEXT MESSAGE
	HRRM	T1,DEVKDL(F)	; AND MAKE THAT ONE BE THE FIRST
	KDPON			;RE-ENABLE INTERRUPTS
	MOVE	T1,P2		;GET THE KDB ADDR BACK
	PUSHJ	P,GIVKDB	;FREE THE MESSAGE BLOCK
	PUSHJ	P,ADVBFF##	;ADVANCE THE USER'S INPUT BUFFER
	 POPJ	P,		;IF WE'RE SUPPOSED TO STOP, RETURN TO UUOCON
	JRST	KILOOP		;OTHERWISE TRY TO DO MORE INPUT
;KDPOUT	ROUTINE TO PROCESS THE OUTPUT UUO FOR THE KDP DEVICE
;CALL	F := DDB ADDRESS
;RETURN	CPOPJ			;ALWAYS
KDPCLO:
KDPOUT:	PUSHJ	P,SAVE2##	;P1 := DEVOAD, P2 := DATA BUFFER ADDRESS
	MOVSI	S,IOBEG		;CLEAR IOBEG
	ANDCAB	S,DEVIOS(F)	; FOR NO PARTICULAR GOOD REASON
	MOVSI	S,IO		;GET AND SET
	IORB	S,DEVIOS(F)	; "OUTPUT" SO IOSETC WORKS RIGHT
	PUSHJ	P,KDPCKM	;CHECK THE MODE

KOLOOP:	PUSHJ	P,KDPONL	;MAKE SURE THAT THE DEVICE IS ONLINE
	  POPJ	P,		;IF NOT, RETURN (WITH ERROR BITS SET)
	HRRZ	P1,DEVOAD(F)	;GET THE ADDRESS OF THE OUTPUT BUFFER
	SKIPE	T1,P1		;MAKE SURE THAT THERE IS ONE, AND
	EXCTUX	<SKIPL (T1)>	; THAT IT HAS DATA IN IT
	POPJ	P,		;IF NO FULL BUFFER, THEN EXIT
	PUSHJ	P,BRNGE##	;MAKE SURE THE BUFFER IS ADDRESSABLE

	AOS	T1,DEVOBC(F)	;COUNT UP THE NUMBER OF BUFFERS OUTSTANDING
	CAILE	T1,KDPMXQ	;IF TOO MANY, THEN
	JRST	[SOS DEVOBC(F)	; TAKE BACK WHAT WE JUST SAID
		 JRST KOWAIT]	; AND WAIT FOR SOME TO GET SENT

;NOW ALLOCATE A KDP DATA BLOCK TO HOLD THE DATA
KOLOO1:	EXCTUX	<HRRZ T1,1(P1)>	;GET THE NUMBER OF USER BYTES
	CAILE	T1,KDLMMS*4	;MAKE SURE THAT THE NUMBER IS REALISTIC
	JRST	[SOS DEVOBC(F)	;IF TOO MANY, TAKE BACK THE COUNT
		 JRST K.BKTL]	; AND TELL THE USER "BLOCK TOO LARGE"
	PUSHJ	P,GETKDB	;GET A BUFFER FOR THE MESSAGE
	  JRST	[SOS DEVOBC(F)	;SINCE ^C CAN HAPPEN HERE...
		 MOVEI T1,2	;IF NO CORE AVAILABLE,
		 PUSHJ P,SLEEPF## ;SLEEP FOR 2 SECONDS AND
		 JRST KOLOOP]	; TRY AGAIN
	MOVE	P2,T1		;REMEMBER THE ADDRESS OF THE MESSAGE BLOCK
;NOW COPY THE DATA INTO THE DATA BLOCK
	EXCTUX	<HRRZ T1,1(P1)>	;GET THE USER'S BYTE COUNT BACK AGAIN
	MOVSI	T2,(POINT 8)	;BUILD A BYTE POINTER TO THE
	HRRI	T2,2(P1)	;  USER'S DATA
	MOVE	T3,MB.FMS(P2)	;GET THE ADDRESS OF THE SEGMENT DESCRIPTOR
	MOVE	T4,T1		;GET THE LENGTH
	MOVEM	T1,MD.BYT(T3)	;STORE THE NUMBER OF BYTES WE'RE GOING TO COPY
	MOVE	T5,MD.AUX(T3)	;GET THE ADDRESS OF THE FIRST BYTE
	MOVE	T6,MD.ALA(T3)	;MD.AUX IS INDEXED BY T6
IFE DALBUG,<			;D.A.LEWINE
	PXCT	2,[EXTEND T1,[EXP MOVSLJ,0]] ;COPY THE DATA
	  PUSHJ	P,NTDSTP##	;CAN'T HAPPEN
>
IFN DALBUG,<			;UNTIL THEY GET THE MICRO-CODE RIGHT...
	JRST	.+3		;SKIP INTO THE MIDDLE OF THE LOOP
	EXCTUX	<ILDB T4,T2>	;LOAD THE NEXT BYTE
	IDPB	T4,T5		; AND STORE IT IN THE MONITOR BUFFER
	SOJGE	T1,.-2		;LOOP TILL ALL STORED
	SETZ	T3,		;CLEAR THE "TO COUNT"
>
	MOVEI	T1,DC.FQB	;FUNCTION = QUEUE OUTPUT DATA
	HLRZ	T2,DEVKDL(F)	; TO THIS KDL
	MOVE	T3,P2		; AND THIS IS THE MESSAGE BLOCK
	PUSHJ	P,KDODSP##	;CALL THE KONTROLLER
	 JRST	[MOVEI	T1,DC.IOF ;SIGNAL OUPUT NOT DONE
		 MOVE	T2,F	  ;POINT TO DDB
		 MOVE	T3,P2	  ;POINT TO MSG BLOCK
		 PUSHJ	P,KDPIDS  ;GIVE THE INTERRUPT
		 JRST .+1]	  ; AND CONTINUE
	PUSHJ	P,ADVBFE##	;ADVANCE THE USER'S OUTPUT BUFFER
	 POPJ	P,		;IF NO MORE OUTPUT, RETURN TO UUOCON
	JRST	KOLOOP		;OTHERWISE TRY TO SEND MORE

	PURGE	T5		;DONE WITH EXTEND INSTRUCTIONS
;KIWAIT	ROUTINE TO WAIT FOR INPUT
KIWAIT:	MOVE	T2,DEVAIO(F)	;GET THE ASYNCH IO BITS
	HRRZ	T1,DEVBUF(F)	;GET THE ADDRESS OF THE BUFFER CONTROL BLOCK
	JUMPE	T1,CPOPJ##	;IF NO BUFFERS SETUP ??
	EXCTUX	<HRRZ T1,(T1)>	;GET THE ADDRESS OF THE NEXT USER'S BUFFER
	EXCTUX	<SKIPL (T1)>	;IF HE HAS INPUT TO READ,
	TRNE	T2,DEPAIO	; OF IF THIS IS ASYNCH IO, THEN
	POPJ	P,		; RETURN TO THE USER
	MOVEI	T1,EV.KDP	;OTHERWISE, GO INTO
	PUSHJ	P,ESLEEP##	; EVENT WAIT AND THEN
	JRST	KILOOP		; CHECK FOR MORE INPUT


;KOWAIT WAIT FOR OUTPUT TO COMPLETE
KOWAIT:	MOVE	T1,DEVAIO(F)	;GET THE WORD WITH THE ASYNCH IO BITS
	TRNE	T1,DEPAIO	;SEE IF WE ARE DOING ASYNCH IO
	POPJ	P,		;IF ASYNCH IO, THEN RETURN TO THE USER
	MOVEI	T1,EV.KDP	;OTHERWISE, GO INTO
	PUSHJ	P,ESLEEP##	; EVENT WAIT, AND
	JRST	KOLOOP		; THEN SEE IF WE CAN DO OUTPUT


;KDPIAV	ROUTINE TO SIGNAL THAT KDP INPUT IS AVAILABLE
KDPIAV:	MOVE	T1,DEVAIO(F)	;GET THE WORD WITH THE ASYNCH IO BITS
	TRNN	T1,DEPAIO	; AND IF THIS IS NOT ASYNCH IO,
	JRST	[LDB T1,PJOBN##	; ASSUME THAT THE GUY IS IN EVENT WAIT
		 PJRST EWAKE##]	; AND GO WAKE HIM
	PUSH	P,DEVIOS(F)	;IF ASYNCH IO, THEN MAKE
	MOVSI	S,IO		; SURE THAT THE "IO" BIT IS
	ANDCAB	S,DEVIOS(F)	; CLEAR.  THIS INSURES THAT SETIOD
	PUSHJ	P,SETIOD##	; WILL GENERATE AN INPUT DONE INTERRUPT
	POP	P,DEVIOS(F)	;RESTORE THE STATE OF DEVIOS
	POPJ	P,		; AND RETURN.


;KDPOAV	ROUTINE TO SIGNAL THAT OUTPUT HAS BEEN COMPLETED
KDPOAV:	MOVE	T1,DEVAIO(F)	;GET THE ASYNCH IO BITS
	TRNN	T1,DEPAIO	;IF WE ARE NOT DOING ASYNCH IO
	PJRST	[LDB T1,PJOBN##	; THEN ASSUME THAT THE GUY IS IN
		 JRST EWAKE##]	; EVENT WAIT AND WAKE HIM
	PUSH	P,DEVIOS(F)	;IF ASYNCH IO, THEN MAKE SURE THAT
	MOVSI	S,IO		; THE "IO" BIT SAYS OUTPUT.  THIS
	IORB	S,DEVIOS(F)	; WILL CAUSE SETIOD TO GENERATE
	PUSHJ	P,SETIOD##	; AN "OUTPUT DONE" INTERRUPT.
	POP	P,DEVIOS(F)	;RESTORE DEVIOS AND
	POPJ	P,		; RETURN
;KDPCKO	ROUTINE TO CHECK OWNERSHIP OF THE KDL BLOCK.
;CALL	F := DDB ADDRESS
;RETURN	CPOPJ			;KDL IS NOT IN "PROGRAM" MODE
;	CPOPJ1			;KDL IS IN "PROGRAM" MODE

KDPCKO:	HLRZ	T1,DEVKDL(F)	;GET THE KDL PAGE ADDRESS
	HRRZ	T1,KDLUSR(T1)	;GET THE USER CODE
	CAIN	T1,DD.PRO	;IF IT IS "PROGRAM"
	AOS	(P)		; THEN GIVE A GOOD
	POPJ	P,		; RETURN


;KDPONL	ROUTINE TO CHECK TO SEE IF THE KDP IN "ON LINE"
;CALL	F := DDB ADDRESS
;RETURN	CPOPJ			;NOT OWNED OR NOT ONLINE (ERROR BITS SET)
;	CPOPJ1			;KDP APPEARS TO BE READY FOR I/O

KDPONL:	PUSHJ	P,KDPCKO	;FIRST SEE IF WE OWN THE LINE
	  JRST	K.IMPM		; IF NOT, THEN IT'S IMPROPER MODE
	HLRZ	T1,DEVKDL(F)	;GET THE KDL PAGE ADDRESS
	HRRZ	T1,KDLKDP(T1)	; AND FROM THAT GET THE KDP ADDRESS
	JUMPE	T1,K.DERR	;IF NO KDP YET (NOT INITED) THEN NOT UP
	MOVE	T1,KDPSTS(T1)	; AND FROM THAT GET THE KDP STATUS
	TLNN	T1,(KDPSRU)	;SEE IF WE'RE RUNNING
	JRST	K.DERR		;IF NOT, SAY DEVICE ERROR
	MOVE	S,DEVIOS(F)	;FIRST GET THE DEVICE STATUS
	TRNE	S,IOSSRM!IOSMRN	; AND IF EITHER ERROR IS STILL LIT
	JRST	K.DERR		; RETURN "DEVICE ERROR"
	TRNE	S,IODERR!IODTER!IOIMPM!IOBKTL ;IF ANY "STANDARD" ERROR
	POPJ	P,		; FORCE THE USER TO CLEARR IT
	PUSH	P,F		;PRESERVE THE DDB ADDRESS
	HLRZ	F,DEVKDL(F)	;GET THE KDL PAGE ADDRESS
	LDB	T1,KDLSTA##	; AND GET THE STATE
	POP	P,F		;RESTORE THE DDB ADDRESS
	TRNE	S,IOSMAI	;IF WE ARE TRYING MAINT MODE,
	JRST	KDPON1		; GO CHECK ONLINE DIFFERENTLY
	CAIGE	T1,KD%STR	;IF SUPPOSED TO BE NORMAL, BUT NOT
	JRST	K.DERR		; THEN WE MUST HAVE SCREWED UP
	CAIE	T1,KD%RUN	;IF WE'RE NOT RUNNING, THEN
	JRST	K.DTER		; IT'S A "SOFT" AREA. (WAIT FOR ONLINE)
	RETSKP			;IF IN RUN, THEN ALL'S OK

KDPON1:	CAIE	T1,KD%MAI	;IF WE'RE NOT IN MAINT MODE, THEN
	JRST	K.DERR		; IT'S AN ERROR
	RETSKP			;OTHERWISE WE'RE "ONLINE"
;KDPCKM	ROUTINE TO CHECK/SET THE MAINT/NORMAL MODE OF THE LINE
;CALL	F := DDB
;RETURN	CPOPJ			;ALWAYS (STARTS THE LINE IN MAINT/NORMAL)

KDPCKM:	PUSHJ	P,KDPCKO	;SEE IF WE ARE THE LINE'S OWNER
	  POPJ	P,		; IF NOT, DON'T DO ANYTHING
	PUSH	P,F		;FIRST PRESERVE THE DDB POINTER
	HLRZ	F,DEVKDL(F)	;FROM THAT GET THE KDL PAGE ADDRESS
	LDB	T1,KDLSTA##	;FROM THAT, GET THE STATE
	POP	P,F		;NOW RESTORE THE DDB ADDRESS
	MOVE	S,DEVIOS(F)	;RELOAD THE IO STATUS
	TRNE	S,IOSMAI	;IF WE'RE SUPPOSED TO BE IN MAINT
	JRST	KDPCK1		; THEN DGO CHECK DIFFERENTLY
	CAIL	T1,KD%STR	;IF WE'RE TRYING TO START OR BETTER
	POPJ	P,		; THEN ALL'S OK
	MOVEI	T1,DC.FIL	;WE WANT TO "INITIALIZE" THE LINE
	JRST	KDPCK2		; SO GO TO COMMON CODE TO DO SO

KDPCK1:	CAIN	T1,KD%MAI	;IF WE'RE IN MAINT MODE
	POPJ	P,		; THEN IT'S OK
	MOVEI	T1,DC.FSM	;WE WANT TO PUT IN MAINT STATE
KDPCK2:	PUSH	P,T1		;SAVE THE "DESTINATION STATE"
	MOVEI	T1,DC.FHL	;FIRST WE MUST "HALT" THE LINE
	HLRZ	T2,DEVKDL(F)	; GET THE LINE ID
	PUSHJ	P,KDODSP##	; AND CALL THE KONTROLLER
	 JFCL			;CAN'T BE, WE ALREADY CHECK THAT WE ARE OWNER
	POP	P,T1		;NOW GET THE "MODE" BACK
	HLRZ	T2,DEVKDL(F)	; AND THE LINE ID
	PUSHJ	P,KDODSP##	;CHANGE THE STATE
	 JFCL			;CAN'T BE, WE ALREADY CHECK THAT WE ARE OWNER
	POPJ	P,		; AND RETURN


;ROUTINES TO SET VARIOUS ERROR BITS
K.IMPM:	MOVEI	S,IOIMPM	;GET THE IMPROPER MODE BIT
	JRST	K.SET		; AND GO SET IT
K.DTER:	MOVEI	S,IODTER	;GET "SOFT ERROR"
	JRST	K.SET		; AND SET IT
K.DERR:	MOVEI	S,IODERR	;GET THE DEVICE ERROR BIT
	JRST	K.SET		;AND SET IT
K.BKTL:	MOVEI	S,IOBKTL	;GET THE BLOCK TOO LARGE BIT
K.SET:	IORB	S,DEVIOS(F)	;SET THE APPROPRIATE BIT
	POPJ	P,		; AND RETURN
;KDPIDS	INTERRUPT LEVEL DISPATCH FROM KMC-11/DUP-11 KONTROLLER

KDPIDS::CAIL	T1,DC.IPU	;RANGE CHECK THE FUNCTION CODE
	CAILE	T1,DC.ICC	; BEFORE BLINDLY DISPATCHING ON IT
	PUSHJ	P,NTDSTP##	;++ KONTROLLER GAVE BAD FUNCTION?
	PUSH	P,F		;SAVE THE KDL PAGE ADDRESS (OR WHAT EVER)
	MOVE	F,T2		;SET F TO POINT TO THE DDB
	PUSHJ	P,@KDPID1(T1)	;CALL THE APPROPRIATE INTERRUPT ROUTINE
	POP	P,F		;RESTORE "F"
	POPJ	P,		; AND RETURN TO THE KONTROLLER

KDPID1:	IFIW	KD.PUP		;(00) PRIMARY PROTOCOL UP
	IFIW	KD.PDN		;(01) PRIMARY PROTOCOL DOWN
	IFIW	KD.MAI		;(02) MAINT MSG RECEIVED IN NORMAL PROTOCOL
	IFIW	KD.STR		;(03) START MSG RECEIVED IN MAINT PROTOCOL
	IFIW	KD.ODN		;(04) OUTPUT DONE
	IFIW	KD.OND		;(05) OUTPUT NOT DONE
	IFIW	KD.IDN		;(06) INPUT DONE
	IFIW	KD.RQI		;(07) REQUEST INPUT BUFFER
	IFIW	NTDSTP##	;(10) LINE CREATION
	IFIW	NTDSTP##	;(11) LINE DISSOLUTION


;PROTOCOL UP
KD.PUP:	MOVEI	S,IODERR	;GET THE IO DEVICE ERROR BIT
	ANDCAB	S,DEVIOS(F)	; AND CLEAR IT
	PUSHJ	P,PSIONL##	;SIGNAL THE DEVICE IS ONLINE
	JRST	KD.WAK		; AND WAKE UP ANY SERVICE ROUTINES

;PROTOCOL DOWN
KD.PDN:	PUSHJ	P,K.DERR	;SIGNAL AN IO DEVICE ERROR
	PUSHJ	P,PSIDWN##	; AND GIVE AN OFFLINE INTERRUPT
KD.WAK:	PUSHJ	P,KDPIAV	;NOW MAKE SURE THAT THE SERVICE ROUTINE
	PUSHJ	P,KDPOAV	; WAKE UP SO THAT THE SEE THE
	LDB	T1,PJOBN##	;GET THE JOB NUMBER
	PUSHJ	P,WAKPST##	; AND WAKE HIM FROM A POSSIBLE HIBER
	POPJ	P,		; PROBLEM

;MAINT OR START RECEIVED IN THE WRONG MODE
KD.MAI:				;IF EITHER ONE OF THE "WRONG TYPE" OF
KD.STR:	PUSHJ	P,K.IMPM	; MESSAGES ARRIVES, SAY "IMPROPER MODE"
	PJRST	KD.PDN		; AND TREAT IT AS "PROTOCOL DOWN"
;KD.IDN	ROUTINE TO PROCESS INPUT MESSAGES FROM THE KDP
KD.IDN:	KDPOFF			;NO INTERRUPTS WHILE HACKING QUEUE
	HRRZ	T2,DEVKDL(F)	;GET THE ADDRESS OF INPUT MESSAGE QUEUE
	JUMPE	T2,[HRRM T3,DEVKDL(F) ;	IF NO QUEUE, START ONE AND
		    JRST KD.ID1] ; RETURN
	JRST	.+2		;SKIP INTO THE MIDDLE OF THE LOOP
	MOVE	T2,T1		;ADVANCE TO THE NEXT ENTRY IN THE LIST
	HRRZ	T1,MB.NXT(T2)	;GET THE ADDRESS OF THE ENTRY AFTER THAT
	JUMPN	T1,.-2		;LOOP IF THE END IS STILL NOT IN SIGHT
	HRRM	T3,MB.NXT(T2)	;MAKE OUR INPUT MSG BE THE LAST ONE
KD.ID1:	KDPON			;RE-ENABLE INTERRUPTS
	PUSHJ	P,KDPIAV	;SIGNAL THAT INPUT IS AVAILABLE
	POPJ	P,		; AND RETURN


;KD.ODN	KD.OND ROUTINES TO PROCESS RETURNED OUTPUT MESSAGES
KD.ODN:
KD.OND:	MOVE	T1,T3		;GET THE ADDRESS OF THE SPENT MSG
	PUSHJ	P,GIVKDB	; AND FREE IT
	SOS	DEVOBC(F)	; DECREMENT THE COUNT OF OUTSTANDING MSGS
	PJRST	KDPOAV		; AND WAKE UP THE DRIVER


;KD.RQI	ROUTINE TO PROCESS A KONTROLLER'S REQUEST FOR AN INPUT BUFFER
;CALL	T3 := NUMBER OF BYTES REQUESTED
;RETURN	T1 := 0 IF NO BUFFER AVAILABLE, T1 := ADDRESS OF BUFFER OTHERWISE
KD.RQI:	MOVE	T2,DEVIOS(F)	;GET DEVICE STATUS
	TRNE	T2,IOIMPM	;IN WRONG PROTOCOL?
	 JRST	KD.RQ1		;YES, DON'T ACCEPT MESSAGE
	KDPOFF			;LOCK THE INPUT CHAIN WHILE WE COUNT BUFFERS
	HRRZ	T2,DEVKDL(F)	;GET THE ADDRESS OF THE FIRST MESSAGE
	MOVEI	T1,KDPMXQ	; AND GET THE QUOTA OF INPUT BUFFERS
	JUMPN	T2,[HRRZ T2,MB.NXT(T2) ;KEEP LOOKING FOR THE END
		    SOJN T1,.	; AND COUNTING DOWN THE QUOTA
		    KDPON	; IF THE QUOTA IS EXHAUSTED
		    POPJ P,]	; THEN RETURN (T1 := 0)
	KDPON			;THE CHAIN IS CONSISTANT AGAIN
	MOVE	T1,T3		;GET THE BYTE COUNT
	PUSHJ	P,GETKDB	; AND ALLOCATE A MESSAGE BUFFER
KD.RQ1:	  SETZ	T1,		;IF NONE, TELL THE KONTROLLER
	POPJ	P,		;RETURN
;ROUTINES TO GET AND FREE KDP BUFFERS.
;FORMAT OF THE BUFFERS IS:
;  0	MB.NXT (POINTER TO NEXT MESSAGE BLOCK)
;  1	MB.FMS (POINTS TO SEGMENT DESCRIPTOR)
;  2	MB.MSN (DDCMP MSG NUMBER)
;  3	CONTAINS SIXBIT /KMCDUP/
;  4	\
;  5	 \ MESSAGE SEGMENT
;  6	 / DESCRIPTOR GOES HERE
;  7	/
;  8	\
;  .	 \ DATA
;  .	 / DATA
;  N-1	/
;  N	CONTAINS SIXBIT /DUPKMC/ (CHECK WORD)

	KDPHDL==4+MD.LEN	;LENGTH OF KDP HEADER IN WORDS
	KDPMSD==4		;OFFSET TO MSD FROM BEG OF BLK

GETKDB::PUSH	P,T1		;SAVE BYTE COUNT
	MOVEI	T2,<KDPHDL*4>+3+4(T1) ;ALLOW FOR OVERHEAD AND ROUND UP
	LSH	T2,-2		;CONVERT TO WORD COUNT
	PUSHJ	P,GETWDS##	; AND TRY TO ALLOCATE SPACE
	 JRST	TPOPJ##		;IF NO CORE, GIVE ERROR RETURN
	POP	P,T2		;GET BYTE COUNT BACK
	SETZM	MB.NXT(T1)	;NO POINTERS PLEASE
	SETZM	KDPMSD+MD.NXT(T1)      ;MAKE SURE THERE IS ONLY 1 MSD
	MOVEM	T2,KDPMSD+MD.BYT(T1)   ; AND STORE THAT FOR USER
	MOVEM	T2,KDPMSD+MD.ALA(T1)   ; & PUT IT IN "ALLOC INFO" AREA
	MOVEM	T2,KDPMSD+MD.ALL(T1)	; ...
	MOVSI	T3,(POINT 8)	       ;MAKE A BYTE POINTER TO DATA
	HRRI	T3,KDPMSD+MD.LEN(T1)  ; AREA AFTER HEADER
	MOVEM	T3,KDPMSD+MD.PTR(T1)   ; AND STORE THAT
	MOVEM	T3,KDPMSD+MD.AUX(T1)   ;STORE OUTPUT BYTE POINTER ALSO
	MOVEI	T3,KDPMSD(T1)	;GET ADDRESS OF MSD
	MOVEM	T3,MB.FMS(T1)	; AND STORE IT IN MESSAGE BLOCK
	MOVEI	T2,<KDPHDL*4>+3+4(T2) ;GET BYTE COUNT AGAIN
	LSH	T2,-2		; AND CONVERT TO LENGTH OF BLOCK
	ADD	T2,T1		;RELOCATE BY ADDRESS OF BLOCK
	MOVE	T3,[SIXBIT /KMCDUP/] ;GET CHECK DATA
	MOVEM	T3,3(T1)	;STORE THAT IN FIRST PART
	MOVSM	T3,-1(T2)	; AND SWAP OF IT IN LAST CHECK WD
	RETSKP			;RETURN
;ROUTINE TO RETURN MEMORY
;CALL	T1 := ADDRESS OF THE KDP BUFFER
;RETURN	CPOPJ			;ALWAYS
GIVKDB::MOVE	T2,KDPMSD+MD.ALL(T1) ;RECOVER ORIGINAL BYTE COUNT
	MOVEI	T2,<KDPHDL*4>+3+4(T2) ;GET LENGTH (IN BYTES)
	LSH	T2,-2		; AND CONVERT THAT TO WORDS
	MOVE	T3,T2		;COPY LENGTH
	ADD	T3,T1		; AND RELOCATE BY BLOCK ADDRESS
	MOVE	T4,[SIXBIT /KMCDUP/] ;GET CHECK WORD
	CAME	T4,3(T1)	;CHECK FIRST ONE
	 PUSHJ	P,NTDSTP##	;CHECK WORD CLOBBERED
	MOVS	T4,T4		;GET VALUE FOR LAST CHECK WORD
	CAME	T4,-1(T3)	; AND COMPARE THAT
	 PUSHJ	P,NTDSTP##	;CHECK WORD CLOBBERED
	EXCH	T1,T2		;IDIOTIC AC CONVENTIONS
	PUSHJ	P,GIVWDS##	;RETURN STORAGE
	POPJ	P,		; AND WE'RE DONE
;THESE ARE THE BYTE POINTERS INTO THE VARIOUS FIELDS OF THE
;DEVICE NAME (USED BY TSTKDP)

KDNMPA:	POINT	10,P1,35	;LOW 10 BITS ARE MULTI POINT ADDR
KDNKDL:	POINT	4,P1,25		;NEXT 4 BITS ARE LINE NUMBER
KDNKDP:	POINT	4,P1,21		;NEXT 4 BITS ARE KONTROLLER NUMBER



	XLIST
	$LIT			;DON'T LIST LITERALS
	LIST
KDSEND::PRGEND
TITLE	KDPINT - SERVICE FOR KMC-11/DUP-11 V002
SUBTTL	W. E. MATSON/TL	21-Oct-84
	SEARCH	F, S, NETPRM,  D36PAR
	$RELOC
	$HIGH

	.DIRECT	FLBLST

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1978,1979,1980 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VKDPINT,001

                     Comment @ Loose ends.

                         End Comment @

KDPINT::ENTRY	KDPINT


                           COMMENT @

                 EHPL MI RTPADE NIISED A DP1P1

                        END OF COMMENT @



	OPDEF	BLTBU	[716B8]		;FAST BYTE TO UNIBUS
	OPDEF	BLTUB	[717B8]		;FAST UNIBUS TO BYTE
	SUBTTL	KDPPRM -- PARAMETERS FOR THE KMC/DUP

	REPMAX==^D20		;NUMBER OF UNANSWERED REPS IMPLIES DEAD LINE
	STRSEC==^D5		;NUMBER OF SECONDS BETWEEN STARTS
	STKSEC==^D2		;NUMBER OF SECONDS BETWEEN STACKS
	REPSEC==^D3		;NUMBER OF SECONDS BETWEEN REPS
	MAXOUT==^D10		;MAXIMUM NUMBER OF OUTSTANDING UN-ACKED MSGS

	T6==T4+2		;T6 FOR INDEXED MSD PTRS (DECNET)

	PURGE	NETOFF, NETON	;GET RID OF THE OLD ONES


	;COMPUTE CODE FOR "TIMER STOPPED" BASED ON FIELD SIZE
	KD%MXT==-1_-^D<36-<<KD%TIM&^O<007700,,0>>_-24>>
	SUBTTL	DEFINITIONS -- DDCMP

	SYN==226
	ENQ==005	;1ST CHAR IN UNNUMBERED MESSAGES
	DLE==220	;1ST CHAR IN BOOTSTRAP MESSAGES
	SOH==201	;1ST CHAR IN NUMBERED MESSAGES

;NUMBERED MESSAGES
;	SOH CC1 (CC2 QSYNC SELECT) R N A0 BCC1 DATA BCC2
;	CC1&CC2 ARE TOTAL LENGTH OF DATA(BCC1 THRU BCC2 EXCLUSIVE)
;	CC1 IS LOWORDER 8BITS OF LENGTH
;	CC2 IS HIGH ORDER 6 BITS OF LENGTH
	QSYNC==100		;QSYNC BIT
	SELECT==200		;SELECT BIT
;	R IS # OF LAST GOOD MESSAGE RECEIVED
;	N IS THIS MESSAGE NUMBER
;	A0			;A0 IS THE DESTINATION STATION ADR(ALWAYS 1)
;	BCC1 IS THE 16 BIT CRC ON SOH THROUGH A0 INCLUSIVE
;	DATA IS PASSED TO NCL
;	BCC2 IS THE 16BIT CRC OF DATA
;UNNUMBERED MESSAGES

;ACK:	ENQ  ACK  (QSYNC  SELECT FILL)  MSG#  FILL  A0  BCC1  BCC2
;	MSG# IS LAST GOOD MSG RECEIVED
%ACK==1

;NAK:	ENQ  NAK  (QSYNC  SELECT RNAK)  MSG#  FILL  A0  BCC1  BCC2
;	RNAK IS NAK REASON
;	MSG# IS LAST GOOD MSG RECEIVED
%NAK==2

;REP:	ENQ  REP  (QSYNC  SELECT FILL)  FILL  N  A0  BCC1  BCC2
;	N IS THE LAST MESSAGE# SENT
%REP==3

;START:	ENQ  STRT  (QSYNC  SELECT  FILL)  FILL  N  A0  BCC1  BCC2
;	N IS NEXT NUMBERED MESSAGE TO BE SENT
%START==6

;STACK:	ENQ  STACK  (QSYNC  SELECT  FILL)  R  N  A0  BCC1  BCC2
;	R IS NEXT EXPECTED MESSAGE # FOR RECPTION
;	N IS NEXT MESSAGE # FOR TRANSMISSION
%STACK==7

;NAK REASONS
	RSNHCK==^D1		;HEADER BCC ERROR
	RSNDCK==^D2		;DATA BCC ERROR
	RSNREP==^D3		;REP RESPONSE
	RSNBTU==^D8		;BUFFER TEMPORARILY UNAVAILABLE
	RSNOVR==^D9		;RECEIVER OVERRUN
	RSNM2L==^D16		;MESSAGE TOO LONG (FATAL)
	RSNHFA==^D17		;MESSAGE HEADER FORMAT ERROR.
	SUBTTL	DEFINITIONS -- DUP11

	DUPADR==3760300		;ADDRESS OF 1ST DUP11
	DUPUBN==3		;UNIBUS ADAPTER NUMBER FOR DUP11

	DPRCSR==0		;RECEIVER CSR
	  DPDTR==000002		;DATA TERMINAL READY
	DPRDBF==2		;(RO)RECEIVER DATA BUFFER
	DPPCSR==2		;(WO)PARAMETER CONTROL AND STATUS REGISTER
	DPTCSR==4		;TRANSMIT CONTROL AND STATUS REGISTER
	  DPCBLP==010000	;EXTERNAL MAINTENCE MODE (CABLE LOOPBACK)
	  DPCNLP==004000	;SYSTEMS TEST MODE (CONTROLLER LOOPBACK)
	  DPMAIN==014000	;MAINTAINENCE MODE BITS
	DPTDBF==6		;TRANSMIT DATA BUFFER
	SUBTTL	DEFINITIONS -- KMC11

SEL0==0
BSEL1==1
	KMCRUN==100000		;RUN FLOP
	KMCMCL==040000		;MASTER CLEAR
	KMCCWR==020000		;CRAM WRITE
	KMCSLU==010000		;STEP LINE UNIT
	KMCLUL==004000		;LINE UNIT LOOP
	KMCRMO==002000		;ROM OUTPUT
	KMCRMI==001000		;ROM INPUT
	KMCSUP==000400		;STEP u-PROCESSOR
	KMCRQI==000200		;REQUEST INPUT
	KMCIEO==000020		;INTERRUPT ENABLE OUTPUT
	KMCIEI==000001		;INTERRUPT ENABLE INPUT
SEL2==2
BSEL3==3			;CONTAINS LINE NUMBER
	KMCOVR==100000		;KMC BUFFER OVER-RUN
	KMCRDO==000200		;READY FOR OUTPUT
	KMCRDI==000020		;READY FOR INPUT
	KMCIOT==000004		;SET FOR RECEIVE CLEARED FOR TRANSMIT
	KMCTYP==000003		;COMMAND TYPE
	  BASEIN==000003	;BASE IN
	  CNTLIN==000001	;CONTROL IN
	  BFADIN==000000	;BUFFER ADDRESS IN
	  CNTLOU==000001	;CONTROL OUT
	  BFADOU==000000	;BUFFER ADDRESS OUT
SEL4==4
BSEL5==5
				;BUFFER DESCRIPTOR LIST ADDRESS
				; (BUFFER ADR IN & OUT & CONTROL OUT)
SEL6==6
BSEL7==7
				;140000	;ADR BITS 17 & 16
				; (BUFFER ADR IN & OUT & CONTROL OUT)
	BFREOM==010000		;END OF MESSAGE (BUFFER ADR OUT)
	BFRENB==020000		;BUFFER ENABLE (BUFFER ADR IN)
	BFRKIL==010000		;BUFFER KILL (BUFFER ADR IN)
	CSRMSK==017770		;MASK FOR DUP11 CSR ADR (BASE IN)
	CDDCMP==100000		;FLAG THIS A DDCMP LINE (CONTROL IN)
	CHALFD==020000		;FLAG THIS IS HALF DUPLEX (CONTROL IN)
				;010000	;ENABLE SECONDARY STATION (CONTROL IN)
				;001000	;CRC INHIBIT (CONTROL IN)
	CENABL==000400		;FLAG TO ENABLE LINE (CONTROL IN)
	COUERR==000377		;ERROR CODE (CONTROL OUT)

CRAMSZ==2000			;SIZE OF KMC11 CRAM
DRAMSZ==2000			;SIZE OF KMC11 DRAM
;BUFFER DESCRIPTOR LISTS ARE STRINGS OF 3 16 BIT WORDS
; 1ST WORD 16 BITS OF BUFFER ADDRESS
; 2ND WORD 16 BIT BYTE COUNT
; 3RD WORD
	BDLLDS==100000		;LAST DESCRIPTOR
	BDLRSY==010000		;RESYNC TRANSMITTER
	BDLXAD==006000		;BUFFER ADDRESS  BITS 17 & 16
	BDLEOM==001000		;END OF MESSAGE
	BDLSOM==000400		;START OF MESSAGE


;MESSAGES TO THE KMC11
; BASEIN:	BSEL2/	<LINE #>*400+3
;		BSEL6/	<DUP11 ADR>&017770
; CONTROL IN:	BSEL2/	<LINE #>*400+1
;		BSEL6/	FLAGS
; BF AD IN:	BSEL2/	<LINE NU>*400+0+<4 IF INPUT>
;		BSEL4/	BUFFER DESCRIPTOR LIST ADR
;		BSEL6/	FLAGS
; BF AD OUT:	BSEL2/	<LINE NU>*400+0+<4 IF RECEIVE>
;		BSEL4/	BUFFER DESCRIPTOR LIST ADR
;		BSEL6/	FLAGS
; CONTROL OUT:	BSEL2/	<LINE NU>*400+1+<4 IF RECEIVE>
;		BSEL4/	BUFFER DESCRIPTOR LIST ADR
;		BSEL6/	ERROR CODE


;CONTROL OUT BIT MASK DEFINITIONS.  THE ROUTINE "CTOTLY" TAKES A
;  CONTROL OUT CODE, TALLYS THE CONTROL OUT IN THE KDL BLOCK, AND
;  PRODUCES A BIT VECTOR OF ONE BIT REPRESENTING THE CODE.  THIS
;  BIT VECTOR REPRESENTATION IS USED TO FACILITATE PARALLEL TESTING
;  FOR VARIOUS CONDITIONS.  BIT DEFINITIONS ARE:

	CTLO06==040000		;ABORT.  SHOULD NEVER HAPPEN
	CTLO10==020000		;HEADER CRC ERROR.
	CTLO12==010000		;DATA CRC ERROR.
	CTLO14==004000		;NO RECEIVE BUFFER ASSIGNED
	CTLO16==002000		;DATA SET READY TRANSITION
	CTLO20==001000		;KMC-11 GOT A NXM ON A UNIBUS ADDRESS
	CTLO22==000400		;TRANSMIT UNDERRUN.
	CTLO24==000200		;RECEIVE OVERRUN
	CTLO26==000100		;BUFFER KILL COMPLETE
	SUBTTL	KDPONC  -- ONCE ONLY ROUTINE FOR KMC/DUP

;THIS ROUTINE IS CALLED BY SYSINI.  IT VERIFYS THAT ALL KMC-11 AND
;  DUP-11 UNITS SPECIFIED BY MONGEN ACTUALLY EXIST.


KDPONC::PUSHJ	P,SAVE2##	;HERE FROM ONCE.  WE USE 2 P'S
	MOVSI	P1,(DF.RQK)	;JUST TO GET THINGS STARTED,
	IORM	P1,DEBUGF##	; REQUEST KDPLDR TO RUN NEXT (FIRST) TICK

	SETZ	P1,		;P1 IS CURRENT KDP INDEX, START WITH KDP ZERO

KDPOCP:	CAILE	P1,KDPMAX##	;LOOP OVER ALL KDP'S
	POPJ	P,		;IF WE'VE DONE THEM ALL, RETURN

	SKIPN	W,KDPTBL##(P1)	;GET THE ADDRESS OF THE NEXT KDP BLOCK
	AOJA	P1,KDPOCP	;  IF IT'S NOT THERE, TRY THE NEXT ONE
	MOVE	T1,KDPCSR(W)	;GET THE ADDRESS OF THE KMC-11/KDP
	PUSHJ	P,UBGOOD##	;SEE IF IT EXISTS (UNIBUS TRAP/DOESN'T TRAP)
	  JRST	KDPNXP		;NON-EXISTANT KMC/KDP

;NOW MAKE SURE IT'S NOT RUNNING (OTHERWISE IT WILL INTERRUPT BEFORE
;  THE VECTORS ARE SET UP)

	MOVEI	T1,KMCMCL	;GET THE MASTER CLEAR BIT.
	WRIO	T1,@KDPCSR(W)	;STOP IT GOOD.
;NOW CHECK ALL LINES ON THE KDP

	SETZ	P2,		;START WITH LINE ZERO

KDPOCL:	CAML	P2,KDPDPN(W)	;LOOP OVER ALL LINES
	AOJA	P1,KDPOCP	;WHEN DONE ALL LINES, DO NEXT KDP

	MOVEI	F,KDPKDL(W)	;GET THE ADDRESS OF THE KDL TABLE
	ADDI	F,(P2)		;RELOCATE TO THIS LINE
	SKIPN	F,(F)		;GET ADDRESS OF KDL PAGE (SKIP IF VALID)
	AOJA	P2,KDPOCL	;DUP-11 PATCHED OUT.  DO NEXT ONE.

	PUSH	P,KDLUSR(F)	;PRESERVE THE MONGEN LINE USER
	HRLI	T1,(F)		;GET FROM ADDRESS
	HRRI	T1,1(F)		;GET TO ADDRESS
	SETZM	(F)		;ZERO FIRST WORD AND
	BLT	T1,KDLLEN-1(F)	; BLT THE ENTIRE PAGE TO ZERO
	POP	P,KDLUSR(F)	;RESTORE LINE USER SO WE AUTO START

	MOVEI	T1,(P2)		;GET A COPY OF THE LINE NUMBER
	LSH	T1,3		;  * 8 TO GET DUP-11 CSR OFFSET
	ADD	T1,KDP1DP(W)	;RELOCATE TO THE ADDRESS OF KDP'S 1ST DUP
	PUSHJ	P,UBGOOD##	;SEE IF THE DUP-11 EXISTS.
	  JRST	KDPNXL		;NON-EXISTANT DUP-11
	AOJA	P2,KDPOCL	;GO CHECK NEXT LINE.
;KDPNXP	- ROUTINE TO DECLARE A KMC-11 NON-EXISTANT.  UNIT IS MARKED
;	NON-EXISTANT BY PUTTING A ZERO IN IT'S "KDPTBL" ENTRY.

KDPNXP:	SETZM	KDPTBL##(P1)	;CLEAR THE KDP TABLE ENTRY SO IT'S IGNORED
	MOVE	U,OPRLDB##	;GET THE ADDRESS OF THE OPR'S TTY
	PUSHJ	P,INLMES##	;TELL HIM FIRST PART OF BAD NEWS.
	ASCIZ	/? No KMC-11 for /
	PUSHJ	P,PRKDP		;TELL HIM WHICH KDP (FROM "W")
	PUSHJ	P,INLMES##	;FINISH THE MESSAGE
	ASCIZ	/.
/
	AOJA	P1,KDPOCP	;GO DO NEXT KDP


;KDPNXL	- ROUTINE TO DECLARE A DUP-11 NON-EXISTANT.  LINE IS MARKED
;	NON-EXISTANT BY PUTTING A ZERO IN IT'S "KDPKDL" ENTRY.

KDPNXL:	MOVEI	T1,KDPKDL(W)	;GET THE ADDRESS OF THE KDL TABLE
	ADDI	T1,(P2)		;GET ADDRESS OF ENTRY FOR THIS LINE
	SETZM	(T1)		;CLEAR IT SO DUP-11 WON'T BE USED
	MOVEM	P2,KDLINE(F)	;SET LINE NUMBER IN PAGE FOR "PRKDL"
	MOVE	U,OPRLDB##	;GET THE ADDRESS OF THE OPRS TERMINAL
	PUSHJ	P,INLMES##	;TELL HIM THE FIRST PART OF THE BAD NEWS
	ASCIZ	/? No DUP-11 for /
	PUSHJ	P,PRKDL		;TELL HIM WHICH LINE
	PUSHJ	P,INLMES##	;FINISH THE LINE
	ASCIZ	/.
/
	AOJA	P2,KDPOCL	;STEP TO THE NEXT LINE
;KDPLDR	ROUTINE TO START KDPLDR ON "FRCLIN"
;CALL	PUSHJ	P,KDPLDR##	;CALLED FROM CLOCK1
;RETURN	POPJ			;ALWAYS

KDPLDR::SKIPGE	DEBUGF##	;DON'T RUN IF DEBUGGING
	POPJ	P,		;THINGS ARE CONFUSING ENOUGH AS IT IS
	PUSHJ	P,FRCSET##	;SETUP TO TYPE ON FRCLIN
	PUSHJ	P,INLMES##	;TYPE THE COMMAND
	ASCIZ	/KDPLDR/
	PJRST	CRLF##		;TYPE THE CLOSING CRLF AND WE'RE DONE
	SUBTTL	KDPSEC - ONCE/SECOND PROCESSING

;KDPSEC	THIS ROUTINE PERFORMS THE TIMING FUNCTIONS REQUIRED BY DDCMP
;	IN PARTICULAR, IT SENDS
;	    STARTS	WITH PERIOD "STRSEC"
;	    STACKS	WITH PERIOD "STKSEC"
;	    REPS	WITH PERIOD "REPSEC"
;	IT ALSO COUNTS THE NUMBER OF "UN-ANSWERED" REPS, AND DECLARES THE
;	LINE DOWN IF THIS COUNT EXCEEDS "REPMAX".
;	IN THE EVENT THAT A LINE-DOWN EVENT DOES OCCUR, THE
;	OPERATOR IS INFORMED THAT THE LINE "TIMED OUT"

KDPSEC::PUSHJ	P,SAVEFW	;PRESERVE W & F
	PUSHJ	P,SAVE2##	;P1 := KDP COUNT, P1 := KDL
	SETZ	P1,		;START AT THE FIRST (0 TH) KDP
KDPSEP:	CAILE	P1,KDPMAX##	;IF WE HAVE DONE ALL KMC-11'S
	POPJ	P,		;  THEN RETURN
	SETZ	P2,		;START AT LINE ZERO ON EACH KDP
	SKIPE	W,KDPTBL##(P1)	;IF THE KDP EXISTS, THEN
	PUSHJ	P,KDPSEL	;  GO PROCESS IT'S LINES
	AOJA	P1,KDPSEP	;COUNT THE KDP AND TRY THE NEXT

KDPSEL:	CAML	P2,KDPDPN(W)	;IF WE HAVE PROCESSED ALL LINES ON THIS
	POPJ	P,		;  KDP, THEN RETURN
	MOVEI	F,KDPKDL(W)	;GET ADDRESS OF THIS KDP'S KDL TABLE
	ADD	F,P2		;  AND CALCULATE ADDRESS OF CURRENT LINE
	SKIPE	F,(F)		;IF THE LINE EXISTS, THEN
	PUSHJ	P,KDPSE0	;  GO DO 1/SECOND PROCESSING ON IT
	AOJA	P2,KDPSEL	;LOOP OVER ALL LINES ON THE KDL
KDPSE0:	AOS	KDLZTM(F)	;INDICATE THAT WE'VE BEEN UP A SECOND LONGER
	PUSHJ	P,FREMAI	;FREE ANY STALE MAINTENANCE MESSAGES

	KDPOFF			;WE BETTER BE CAREFULL WHEN IT'S RUNNING

	LDB	T1,KDLSTA##	;GET OUR STATE
	SUBI	T1,KD%STR	;ADJUST SO THAT "SENDING STARTS" IS STATE 0
	JUMPL	T1,KDPSE2	;  ANY STATE BEFORE DOESN'T GET TIMED

	CAILE	T1,KD%RUN-KD%STR ;[DBUG] JUST MAKE SURE IT'S A
	PUSHJ	P,NTDSTP##	;[DBUG] REASONABLE STATE

	LDB	T2,KDLTIM##	;GET THE TIMER
	CAIN	T2,KD%MXT	;STOPPED?
	 JRST	KDPSE2		;YES, IGNORE IT
	AOS	T2		;COUNT UP ONE MORE SECOND
	DPB	T2,KDLTIM##	;PUT THE UPDATED TIME BACK
	CAMGE	T2,[EXP STRSEC,STKSEC,REPSEC](T1) ;HAS IT OVERFLOWED
	JRST	KDPSE2		;  IF NOT, JUST CALL NETSER
	SETO	T2,		;IF IT'S OVERFLOWED, STOP IT
	DPB	T2,KDLTIM##	;  (NEXT EVENT WILL RESTART IT IF NECESSARY)
	MOVE	T2,[EXP KDSSTR,KDSSTK,KDSREP](T1) ;GET "REQUEST BIT" TO SET.
	IORM	T2,KDLSTS(F)	;REQUEST THE APROPRIATE CONTROL MESSAGE
	TLNN	T2,(KDSREP)	;WAS THIS A "REP"
	JRST	KDPSE1		;  IF NOT, SEND MESSAGE AND EXIT.

	LDB	T1,KDLRPC##	;IF IT'S A REP, GET THE REPCOUNT
	AOS	T1		;  AND ACCOUNT FOR THE COMING REP.
	DPB	T1,KDLRPC##	;STORE THE UPDATED COUNT BACK
	CAIG	T1,REPMAX	;HAVE WE "REP TIMED-OUT"?
	JRST	KDPSE1		;  IF NOT, JUST SEND THE REP AND EXIT

;FALL THROUGH TO DECLARE THE LINE DOWN
;(FROM ABOVE) LINE HAS TIMED OUT.   RESTART IT AND TELL THE OPERATOR.

	MOVEI	T1,KD%STR	;GET THE "SENDING START'S STATE"
	DPB	T1,KDLSTA##	;  AND PUT THIS LINE IN THAT STATE
	MOVSI	T1,(XMTBTS)	;GET MASK OF "MESSAGE QUEUED" BITS
	ANDCAM	T1,KDLSTS(F)	;  AND CLEAR ALL PENDING REQUESTS
	MOVSI	T1,(KDSSTR)	;GET THE "SEND A START" REQUEST BIT
	IORM	T1,KDLSTS(F)	;  AND REQUEST A START

	PUSHJ	P,KDLPDN	;TELL OWNER THAT THIS LINE IS RESTARTING.

	KDPON			;ALLOW INTERRUPTS WHILE CALLING SCNSER

	MOVE	U,OPRLDB##	;WE WANT TO TELL THE OPR, GET HIS LDB ADDR
	PUSHJ	P,INLMES##	;SEND FIRST PART OF MESSAGE
	ASCIZ	/%% Synch line /
	PUSHJ	P,PRKDL		;PRINT THE FULL KDP#, KDL# MESSAGE
	PUSHJ	P,INLMES##	;NOW FINISH THE MESSAGE WITH .CRLF
	ASCIZ	/ timed out.
/

	KDPOFF			;NO INTERRUPTS FOR XMT/RCV BUF
KDPSE1:	PUSHJ	P,RCVBUF	;MAKE SURE WE'VE POSTED RECEIVE BUFFERS
	PUSHJ	P,XMTBUF	;TRY TO SEND THE MESSAGE
KDPSE2:	KDPON			;INT'S OK NOW
	POPJ	P,		;  AND WE'RE DONE
	SUBTTL	KDP. -- UUO INTERFACE TO THE KMC/DUP LINE UNIT.
;KDP.
;
;FUNCTIONS THAT AFFECT THE KMC AND/OR ALL DUP-11'S CONNECTED:
;
;	KDP.KN			;RETURN THE NUMBER OF KMC-11'S ON SYS
;	KDP.DN			;RETURN THE NUMBER OF DUP-11'S ON A KMC
;	KDP.HA			;HALT THE KMC.  ARG1 = KMC #
;	KDP.MC			;MASTER CLEAR.  ARG1 = KMC #
;	KDP.ST			;START THE KMC.  ARG1 = KMC #
;	KDP.RE			;READ CRAM.  ARG1 = KMC #,
;				;	     ARG2 = CRAM ADDRESS,
;				;	     ARG3 GETS CONTENTS OF CRAM
;	KDP.WR			;WRITE CRAM.  ARGS AS IN KDP.RE.
;	KDP.KS			;GET THE STATUS OF THE KMC.
;
;FUNCTIONS THAT AFFECT JUST A SINGLE DUP-11:
;
;	KDL.RS			;READ DUP-11'S STATUS REGISTERS
;	KDL.HA			;HALT DDCMP ON THE LINE
;	KDL.ST			;START DDCMP ON THE LINE
;	KDL.SU			;SET THE LINE'S USER
;
;ERRORS

	KE%ILF==ECOD1##		;ILLEGAL FUNCTION CODE
	KE%ILK==ECOD2##		;ILLEGAL KDP NUMBER
	KE%ALS==ECOD3##		;ARG LIST TOO SHORT
	KE%IWR==ECOD4##		;ILLEGAL WHEN KMC-11 RUNNING
	KE%ICA==ECOD5##		;ILLEGAL CRAM ADDRESS (READ OR WRITE)
	KE%ILL==ECOD6##		;ILLEGAL LINE NUMBER
	KE%KNR==ECOD7##		;KMC-11 NOT RUNNING (KDL TYPE OP'S)
	KE%LNS==ECOD10##	;KDL LINE NOT STARTED
	KE%LAS==ECOD11##	;KDL LINE ALREADY STARTED
	KE%UNP==ECOD12##	;USER NOT PRIVILEGED
	KE%IUN==ECOD13##	;ILLEGAL LINE USER NAME (SET LINE USER FCN)
	KE%NDL==ECOD14##	;NO DDB FOR LINE WHILE TRYING TO START PROTOCOL
KDP.::	PUSHJ	P,SAVE2##	;SAVE A COUPLE OF P'S
	HLRZ	P2,T1		;SAVE THE LENGTH OF THE ARG LIST
	CAIGE	P2,2		;MAKE SURE THAT THERE ARE AT LEAST 2 ARGS
	PJRST	KE%ALS		;ILLEGAL ARGUMENT LIST
	HRR	M,T1		;SET RH(M) := ADDRESS OF ARG LIST
	PUSHJ	P,GETWDU##	;GET THE FUNCTION CODE
	JUMPLE	T1,KE%ILF	;ILLEGAL FUNCTION
	CAILE	T1,KDOPMX	;RANGE CHECK FOR KDP FUNCTIONS
	JRST	[CAILE T1,KLOPST;IF NOT KDP, SEE IF KDL FUNCTION
		 CAILE T1,KLOPMX;  WHICH IS BETWEEN THESE TWO VALUES
		 PJRST KE%ILF	;ILLEGAL FUNCTION
		 JRST .+1]	;LEGAL FUNCTION. CONTINUE WITH MAIN FLOW
	MOVEI	P1,(T1)		;REMEMBER THE FUNCTION CODE FOR LATER
	CAIG	P1,1		;IF IT'S A KDPOP THAT DOESN'T SPECIFY A KDP
	JRST	KDPOP.		;  GO TO THE DISPATCH NOW
	PUSHJ	P,GETWD1##	;GET THE KMC-11 NUMBER
	SKIPL	T1		;MAKE SURE THAT IT'S A
	CAILE	T1,KDPMAX##	;  LEGAL KMC-11 NUMBER
	PJRST	KE%ILK		;EXIT IF ILLEGAL KMC-11 NUMBER
	SKIPN	W,KDPTBL##(T1)	;LOAD KDP POINTER
	PJRST	KE%ILK		;KMC MUST NOT EXIST (ONCE DIDN'T FIND IT)

;HERE IF THIS IS A KMC-11 (KDP) ORIENTED FUNCTION
;  AT THIS POINT:
;
;	P1 := KDP. FUNCTION CODE.
;	P2 := LENGTH OF ARG-LIST.

KDPOP.:	CAILE	P1,KDOPMX	;IS THIS A KDP OR A KDL STYLE OP?
	JRST	KDLOP.		;THIS IS A KDL OP. USE DIFFERENT DISPATCH
	JRST	@.(P1)		;DISPATCH ON THE FUNCTION CODE
	JRST	KDPKN		; 1 = RETURN NUMBER OF KMC-11'S
	JRST	KDPDN		; 2 = RETURN NUMBER OF DUP=11'S
	JRST	KDPSS		; 3 = READ STATUS
	JRST	KDPHA		; 4 = HALT KMC-11
	JRST	KDPMC		; 5 = MASTER CLEAR THE KMC (DECLARE LINES DOWN)
	JRST	KDPST		; 6 = START THE KMC-11
	JRST	KDPRE		; 7 = READ A KMC-11 CRAM LOCATION
	JRST	KDPWR		; 8 = WRITE A KMC-11 CRAM LOCATION
	KDOPMX==8		;MAXIMUM KDP FUNCTION CODE
;HERE IF THIS IS A DUP-11 (KDL) ORIENTED FUNCTION
;  AT THIS POINT:
;
;	P1 := KDP. FUNCTION CODE.
;	P2 := LENGTH OF ARG-LIST.


KDLOP.:	CAIGE	P2,3		;MAKE SURE THERE IS A THIRD ARG (LINE #)
	PJRST	KE%ALS		;  IF NOT, GIVE ARG LIST TOO SHORT ERROR
	PUSHJ	P,GETWD1##	;GET THE DUP-11 NUMBER
	CAML	T1,KDPDPN(W)	;MAKE SURE IT'S IN RANGE
	PJRST	KE%ILL		;  IF NOT, GIVE "ILLEGAL LINE NUMBER" ERROR

	MOVEI	F,KDPKDL(W)	;GET ADDRESS OF POINTERS TO KDL BLOCKS
	ADDI	F,(T1)		;ADD OFFSET TO PROPER LINE
	SKIPN	F,(F)		;LOAD ADDRESS OF KDL BLOCK
	PJRST	KE%ILL		;DUP-11 DOES NOT EXIST.  (ONCE DIDN'T FIND IT)

KLOPTB:	JRST	@.-KLOPST(P1)	;DISPATCH ON THE FUNCTION CODE
	KLOPST==100		;LINE OP'S START AT "100"
	JRST	KDLRS		; 1 = READ LINE'S STATUS
	JRST	KDLHA		; 2 = HALT THE LINE (STOP DDCMP)
	JRST	KDLST		; 3 = START THE LINE (CONTROL OUT) START DDCMP
	JRST	KDLSU		; 4 = SET LINE'S USER (ANF, DECnet, PROGRAM)
	JRST	KDLRU		; 5 = READ LINE'S USER IN SIXBIT
	KLOPMX==KLOPST+.-KLOPTB	;MAX OP CODE ALLOWED
	SUBTTL	KDP. SUB-FUNCTIONS (KDPOP.)

;HERE ARE THE VARIOUS KMC-11 (KDP) ORIENTED FUNCTION
;  AT THIS POINT:
;
;	P1 := KDP. FUNCTION CODE.
;	P2 := LENGTH OF ARG-LIST.

; #1 -- RETURN THE NUMBER OF KDP'S ON THE SYSTEM
KDPKN:	MOVEI	T1,KDPMAX##	;GET NUMBER OF THE LAST ONE
	ADDI	T1,1		;INCREMENT IT TO GET A COUNT
	PUSHJ	P,PUTWD1##	;RETURN THE NUMBER IN ARG #2
	RETSKP			;GIVE GOOD RETURN


; #2 -- RETURN THE NUMBER OF DUP-11'S ON A PARTICULAR KDP
KDPDN:	MOVE	T1,KDPDPN(W)	;GET THE NUMBER OF DUP-11'S ON THIS KDP
	PUSHJ	P,PUTWD1##	;RETURN THE NUMBER IN ARG #3
	RETSKP			;GIVE GOOD RETURN


; #3 -- RETURN THE STATUS WORD OF KDP BLOCK
KDPSS:	MOVE	T1,KDPSTS(W)	;GET THE STATUS WORD
	PUSHJ	P,PUTWD1##	;RETURN STATUS WORD
	RETSKP			;GIVE GOOD RETURN


; #4 -- HALT THE KMC-11. THIS DOES NOT DECLARE DUP-11 LINES DOWN SINCE
;	SETTING THE RUN BIT WOULD, IN THEORY, ALLOW THE KMC-11 TO CONTINUE.
KDPHA:	PUSHJ	P,NTDPRV##	;THIS IS A PRIVILEGED FUNCTION
	  PJRST	KE%UNP		;IF NO PRIVILEGES, GIVE ERROR RETURN
	MOVEI	T1,0		;GET THE KMC-11 RUN BIT
	WRIO	T1,@KDPCSR(W)	;CLEAR THE RUN/MAINT BITS
	MOVSI	T1,(KDPSRU)	;GET THE "MICROCODE RUNNING" BIT
	ANDCAM	T1,KDPSTS(W)	;  AND CLEAR THAT
	RETSKP			;GIVE GOOD RETURN


; #5 -- MASTER CLEAR THE KMC-11.  THIS DECLARES THE DUP-11 LINES DOWN SINCE
;	THERE IS NOW NO WAY TO CONTINUE DDCMP ON THEM.
KDPMC:	PUSHJ	P,NTDPRV##	;THIS IS A PRIVILEGED FUNCTION
	  PJRST	KE%UNP		;IF NO PRIVILEGES, GIVE ERROR RETURN
	PUSHJ	P,KDPHLT	;LET "HALT" DO THE WORK
	RETSKP			;GIVE GOOD RETURN
; #6 -- START THE KMC-11.  THIS FIRST STARTS THE KMC-11, AND THEN
;	SETS UP THE INTERRUPT VECTORS. ONCE THE VECTORS ARE SET UP,
;	IT SETS THE TWO KMC-11 INTERRUPT ENABLES, AND DECLARES THE KDP RUNNING.
KDPST:	PUSHJ	P,NTDPRV##	;THIS IS A PRIVILEGED FUNCTION
	  PJRST	KE%UNP		;IF NO PRIVILEGES, GIVE ERROR RETURN
	MOVEI	T1,KMCRUN	;GET THE RUN BIT
	TIOE	T1,@KDPCSR(W)	;MAKE SURE THE KMC-11 ISN'T RUNNING ALREADY
	PJRST	KE%IWR		;IF IT IS, GIVE "ILLEGAL WHEN RUNNING" ERROR
	WRIO	T1,@KDPCSR(W)	;SET RUN NOW, WILL SET IEI & IEO SOON
				;  (MUST GIVE KMC TIME TO INIT)

	MOVE	T1,KDPVEC(W)	;GET THE ADDRESS OF THE VECTOR
	LSH	T1,-2		;CONVERT BYTE OFFSET INTO WORD OFFSET
	MOVSI	T2,(XPCW)	;GET BODY OF A "XPCW" INSTRUCTION

	HRRI	T2,KDPIVA(W)	;GET ADDRESS OF VECTOR "A" INTERRUPT SERVICE
	MOVEM	T2,VECTB3##(T1)	;STORE ADDRESS OF ROUTINE TO FORCE JSR TO
	HRRI	T2,KDPIVB(W)	;GET ADDRESS OF "B" SERVICE ROUTINE
	MOVEM	T2,VECTB3##+1(T1);SET UP VECTOR "B" INTERRUPT SERVICE

	MOVEI	T1,KMCRUN!KMCIEI!KMCIEO	;START THE KMC-11 AND ENABLE INTS
	WRIO	T1,@KDPCSR(W)	;START THE KMC-11

	MOVSI	T1,(KDPSRU)	;GET AND SET THE "RUNNING"
	IORB	T1,KDPSTS(W)	;  BIT SO IT AT LEAST LOOKS LIKE WE'RE UP.

	RETSKP			;ALL DONE. GIVE THE USER A GOOD RETURN
; #7 -- READ A KMC-11 CRAM LOCATION.  KMC-11 MUST BE HALTED
KDPRE:	CAIGE	P2,4		;MAKE SURE THE ARG LIST IS LONG ENOUGH
	PJRST	KE%ALS		;IF ARG LIST IS TO SHORT, GIVE AN ERROR
	MOVE	P1,KDPCSR(W)	;GET THE ADDRESS OF THE CSR FOR THE KMC-11
	MOVEI	T1,KMCRUN	;GET THE RUN BIT
	TIOE	T1,SEL0(P1)	;SEE IF THE KMC-11 IS RUNNING
	PJRST	KE%IWR		;IF IT IS RUNNING, GIVE AN ERROR
	MOVEI	T1,KMCRMO	;GET THE "READ CRAM" BIT
	WRIO	T1,SEL0(P1)	;ENABLE THE KMC-11 FOR MAINT CRAM READ
	PUSHJ	P,GETWD1##	;GET THE ARG WHICH IS THE CRAM ADDRESS
	CAIL	T1,0		;RANGE CHECK THE CRAM ADDRESS
	CAIL	T1,CRAMSZ	;  TO BE SURE IT FITS IN THE CRAM
	PJRST	KE%ICA		;GIVE AN ILLEGAL CRAM ADDR IF IT'S OUT
	WRIO	T1,SEL4(P1)	;STORE THE ADDRESS IN MAINT CRAM ADDR REG
	RDIO	T1,SEL6(P1)	;READ THE CRAM LOCATION
	PUSHJ	P,PUTWD1##	;STORE THE RESULT IN ARG #4
	PJRST	KDPHA		;CLEAR KDPSRU AND EXIT


; #8 -- WRITE A KMC-11 CRAM LOCATION.  JUST LIKE READ (ALMOST)
KDPWR:	CAIGE	P2,4		;MAKE SURE THE ARG LIST IS LONG ENOUGH
	PJRST	KE%ALS		;IF ARG LIST IS TO SHORT, GIVE AN ERROR
	MOVE	P1,KDPCSR(W)	;GET THE ADDRESS OF THE CSR FOR THE KMC-11
	MOVEI	T1,KMCRUN	;GET THE RUN BIT
	TIOE	T1,SEL0(P1)	;SEE IF THE KMC-11 IS RUNNING
	PJRST	KE%IWR		;IF IT IS RUNNING, GIVE AN ERROR
	MOVEI	T1,KMCRMO	;GET THE "READ CRAM" BIT
	WRIO	T1,SEL0(P1)	;ENABLE THE KMC-11 FOR MAINT CRAM READ
	PUSHJ	P,GETWD1##	;GET THE ARG WHICH IS THE CRAM ADDRESS
	CAIL	T1,0		;RANGE CHECK THE CRAM ADDRESS
	CAIL	T1,CRAMSZ	;  TO BE SURE IT FITS IN THE CRAM
	PJRST	KE%ICA		;GIVE AN ILLEGAL CRAM ADDR IF IT'S OUT
	WRIO	T1,SEL4(P1)	;STORE THE ADDRESS IN MAINT CRAM ADDR REG
	PUSHJ	P,GETWD1##	;GET THE VALUE TO WRITE
	WRIO	T1,SEL6(P1)	;PUT IT IN THE CRAM MEMORY BUFFER REGISTER
	MOVEI	T1,KMCCWR	;GET THE CRAM WRITE BIT
	BSIO	T1,SEL0(P1)	;CLOCK THE DATA INTO THE CRAM
	PJRST	KDPHA		;CLEAR KDPSRU AND EXIT
	SUBTTL	KDLOP. SUB-FUNCTIONS.

;THESE ARE THE DUP-11 (KDL) ORIENTED KDP. SUB-FUNCTIONS.
;  AT THIS POINT:
;
;	T1 := THE DUP-11'S LINE NUMBER (NEEDED FOR "START")
;	P1 := KDP. FUNCTION CODE.
;	P2 := LENGTH OF THE ARGLIST

; #101 	RETURN THE LINE STATUS BLOCK.  RETURNS ALL COUNTERS ETC...

KDLRS:	CAIGE	P2,4		;SKIP IF ARG LIST CONTAINS "XWD LEN,ADDR"
	PJRST	KE%ALS		;ARG LIST TOO SHORT IF NOT THERE
	PUSHJ	P,GETWD1##	;GET "XWD LENGTH,ADDRESS" OF RETURN VAL AREA
	MOVE	P2,T1		;COPY IT TO A SAFER PLACE
	HLRZ	T2,P2		;GET THE LENGTH OF THE RETURN VALUE AREA
	CAIGE	T2,<KDLEST-KDLSTS>+1 ;SEE IF IT WILL HOLD THE DATA
	PJRST	KE%ALS		;GIVE ARGLIST TOO SHORT ERROR IF WON'T FIT
	MOVEI	T1,(P2)		;GET USER ADDRESS OF RETURN AREA
	ADDI	T2,-1(T1)	;GET ADDRESS OF LAST WORD IN AREA
	PUSHJ	P,TRNGE##	;MAKE SURE IT'S ALL IN CORE.
	MOVEI	T1,(P2)		;GET ADDRESS OF RETURN AREA BACK
	MOVEI	T2,<KDLEST-KDLSTS>+1 ;GET LENGTH OF STATUS BLOCK
	ADDI	T2,-1(T1)	;GET ADDRESS OF LAST WORD TO WRITE
	HRLI	T1,KDLSTS(F)	;GET ADDRESS OF FIRST STATUS WORD TO SEND
	EXCTXU	<BLT T1,(T2)>	;COPY THE BLOCK INTO USER MEMORY
	RETSKP			;GIVE GOOD RETURN
; #102	HALT A SINGLE DUP-11 LINE.  TERMINATES DDCMP AND DECLARES THE
;	LINE DOWN.
KDLHA:	MOVE	T1,KDPSTS(W)	;GET THE KMC-11 STATUS
	TLNN	T1,(KDPSRU)	;IF IT'S NOT RUNNING
	PJRST	KE%KNR		;  GIVE A "KDP NOT RUNNING" ERROR

	PUSHJ	P,NTDPRV##	;THIS IS A PRIVILEGED FUNCTION
	  PJRST	KE%UNP		;IF NO PRIVILEGES, GIVE ERROR RETURN
	LDB	T1,KDLSTA##	;GET THE LINES STATE
	CAIG	T1,KD%FLS	;MAKE SURE WE THINK THAT THE LINE'S UP
	PJRST	KE%LNS		;  IF NOT, GIVE "LINE NOT STARTED" ERROR
	PJRST	KF.HA		;HALT THE LINE
; #103	ROUTINE TO START DDCMP ON A SPECIFIED DUP-11 LINE.
;NOTE:	T1 STILL HAS THE LINE'S NUMBER IN IT!!!
KDLST:	MOVE	T1,KDPSTS(W)	;GET THE KMC-11 STATUS
	TLNN	T1,(KDPSRU)	;IF IT'S NOT RUNNING
	PJRST	KE%KNR		;  GIVE A "KDP NOT RUNNING" ERROR

	PUSHJ	P,NTDPRV##	;THIS IS A PRIVILEGED FUNCTION
	  PJRST	KE%UNP		;IF NO PRIVILEGES, GIVE ERROR RETURN
	LDB	T2,KDLSTA##	;GET OUR CURRENT STATE
	CAIL	T2,KD%FLS	;SEE IF WE ARE IN A STATE TO RESTART
	PJRST	KE%LAS		;  IF RUNNING, SAY "LINE ALREADY STARTED"

	HRRZ	T2,KDLUSR(F)	;GET THE LINE'S USER'S ID
	CAIN	T2,DD.PRO	; AND IF IT'S NOT "PROGRAM"
	SKIPE	KDLDDB(F)	; OR IF HE HAS A DDB,
	CAIA			; THEN HE'S OK
	PJRST	KE%NDL		;NO DDB FOR LINE IN PROGRAM MODE

	LDB	T1,KDLSTA##	;GET THE STATE,
	CAIL	T1,KD%INI	; AND IF WE'VE BEEN INITIALIZED
	JRST	KDLST1		;DON'T DO IT AGAIN
	PUSHJ	P,KDLINI	;INITIALIZE THE LINE
	PUSHJ	P,ZRCTRS	;ZERO THE STATISTIC COUNTERS

;NOW SEE WHAT MODE THE USER WANT'S TO START THE LINE IN
KDLST1:	SETZ	T1,		;ASSUME NORMAL MODE
	CAIL	P2,4		;IF THE USER SPECIFIED A FORTH ARG,
	PUSHJ	P,GETWD1##	; THEN GO FETCH THE TYPE
	MOVEI	T2,KD%MAI	;ASSUME HE MENT TO START MAINT MODE
	JUMPN	T1,KDLST2	; AND IF SO, GO DO IT
	MOVSI	T1,(XMTBTS)	;MAKE SURE WE LEAVE NO CONTROL MESSAGES
	ANDCAM	T1,KDLSTS(F)	;PENDING FROM LAST TIME
	MOVSI	T1,(KDSSTR)	;GET THE "NEED TO SEND START" BIT
	IORM	T1,KDLSTS(F)	;  AND SET IT SO WE SEND ONE SOON
	PUSHJ	P,CLCTRS	;MAKE SURE WE START CORRECTLY
	MOVEI	T2,KD%STR	;GET THE "SENDING STARTS" STATE
KDLST2:	DPB	T2,KDLSTA##	;SET THIS LINE'S STATE
	KDPOFF			;NO INT'S IN RCV/XMT-BUF
	PUSHJ	P,RCVBUF	;SET UP A RECEIVE REQUEST
	PUSHJ	P,XMTBUF	;TRY TO SEND THE START
	KDPON			;INT'S OK NOW
	RETSKP			;GIVE GOOD RETURN
; #104	ROUTINE TO SET THE LINE'S USER
KDLSU:	PUSHJ	P,NTDPRV##	;MUST BE PRIVILEGED TO TO THIS
	  PJRST	KE%UNP		;IF HE'S NOT, THEN GIVE AN ERROR
	CAIGE	P2,3		;MAKE SURE THAT THE ARG LIST IS LONG ENOUGH
	PJRST	KE%ALS		;IF NOT, SAY "ARG LIST TOO SHORT"
	LDB	T1,KDLSTA##	;GET THE LINE'S CURRENT STATE
	CAIL	T1,KD%FLS	;THE LINE MUST BE HALTED TO SWITCH USERS
	PJRST	KE%LAS		;SAY "LINE STARTED" IF IT IS RUNNING
	PUSHJ	P,GETWD1##	;GET THE "NAME" OF THE USER TO SWITCH TO
KDLSU1:	PUSHJ	P,KONUSN##	;IDENTIFY USER NAME
	  JRST	KE%IUN		; THEN SAY ILLEGAL USER NAME
	HRRZM	T2,KDLUSR(F)	;IF IT MATCHES, SET THE LINE TYPE
	CAIN	T2,DD.DEC	;IS THE USER DECNET?
	JRST	KDLSUD		;YES, START UP ROUTER
	CAIN	T2,DD.PRO	;NO, IS IT PROGRAM?
	HRLM	J,KDLUSR(F)	;YES, SAVE THE JOB NUMBER
	RETSKP			;NO, RETURN SUCCESS

;Now that we know we are setting the user to DECNET, we'll set the
;line's state to on, so that when the protocol comes up RTR will be
;ready.  Note that if the definitions of the LINE ID change,
;this crock will have to change.

KDLSUD:	SETZ	T4,		;START OFF WITH A CLEAN LINE ID
	MOVE	T2,KDPNUM(W)	;GET THE KMC NUMBER (USUALLY ZERO)
	DPB	T2,[POINT 9,T4,17] ;SAVE IT AS KONTROLLER NUMBER (LI.KON)
	MOVE	T2,KDPDPN(W)	;GET THE NUMBER OF KDPS ON THIS KMC
	MOVEI	T3,KDPKDL(W)	;POINT TO KDL TABLE
	SOJGE	T2,[CAMN F,(T3)		;DO WE HAVE A MATCH
		    JRST KDLSD1		;YES, THAT MEANS WE FOUND KDP NUMBER
		    AOJA T3,.]		;NO, ADVANCE TO NEXT AND CHECK IT OUT
	PUSHJ	P,NTDSTP	;OOPS, SEVERE PROBLEM

KDLSD1:	SUBI	T3,KDPKDL(W)	;FIND THE KDP NUMBER
	DPB	T3,[POINT 9,T4,26] ;SAVE IT AS UNIT NUMBER (LI.UNI)
	MOVEI	T2,LD.KDP	;THIS MEANS THE KDP DEVICE TO RTR
	DPB	T2,[POINT 9,T4,8] ;SAVE AS DEVICE TYPE (LI.DEV)
	MOVEI	T1,DC.IOC	;INTERRUPT TO OPEN A CIRCUIT
	TLO	T4,(LILXC)	;SET THE BIT TO MAKE IT A LINE ID
	MOVE	T3,T4		;GET LINE ID IN PROPER AC
	PUSHJ	P,CALDVR	;CALL THE DRIVER
	RETSKP			;GIVE A GOOD RETURN

; #105	ROUTINE TO READ THE LINE'S USER
KDLRU:	CAIGE	P2,3		;MAKE SURE ARG LIST IS LONG ENOUGH
	PJRST	KE%ALS		;IF NOT, SAY "ARG LIST TOO SHORT"
	HRRZ	T1,KDLUSR(F)	;GET THE USER OF THE LINE
	MOVE	T1,DTNAME##(T1)	;GET THE SIXBIT NAME
	PUSHJ	P,PUTWD1##	;PLACE THE VALUE IN USER BLOCK
	HLRZ	T1,KDLUSR(F)	;GET THE JOB NUMBER OF THE OWNER
				; (ZERO IF NOT DD.PRO)
	PUSHJ	P,PUTWD1##	;PLACE IT IN NEXT WORD OF USER BLOCK
	RETSKP			; AND GIVE A GOOD RETURN
	SUBTTL	KDPDSP - ENTRY TO KDPINT

;KDPDSP	- THIS ROUTINE IS THE OUTSIDE WORLD'S ENTRY INTO KDPINT.
;CALL	MOVX	T1,FUNCTION-CODE (DC.F??)
;	MOVX	T2,KDL-BLOCK ADDRESS
;	T3, T4  ARE ARGUMENTS FOR FUNCTION
;PUSHJ	P,KDPDSP
;RETURN	CPOPJ			;IF CALLED WHEN OWNED BY SOMEONE ELSE
;	CPOPJ1			;ON SUCCESS

;FOR DECNET
KDPDSP::SKIPL	T1		;RANGE CHECK
	CAILE	T1,DC.FAL	; THE FUNCTION CODE [DC.FMX]
	PUSHJ	P,NTDSTP##	;IF OUT OF RANGE, STOP
	PUSHJ	P,SAVEFW	;WE USE F := KDL, W := KDP
	CAIN	T1,DC.FAL	;ASSIGN LINE?
	 JRST	KF.AL		;YES
	MOVE	F,T2		;SET UP OUR KDL BLOCK POINTER
	HRRZ	T2,KDLUSR(F)	;GET THE USER OF THE LINE
	 SKIPE	KDLLBK(F)	;BETTER HAVE A DECNET BLOCK
	CAIE	T2,DD.DEC	;DECNET?
	 POPJ	P,		;NOPE, GIVE ERROR RETURN.
	HRRZ	W,KDLKDP(F)	;SET UP THE KDP ADDRESS AS WELL
	PUSHJ	P,@KDPDST(T1)	;DISPATCH ON THE FUNCTION CODE
	POPJ	P,		;FAILURE
	PJRST	CPOPJ1##	;GIVE GOOD RETURN

;FOR ANF
KDADSP::SKIPL	T1		;RANGE CHECK
	CAILE	T1,DC.FQB	; THE FUNCTION CODE [DC.FMX]
	PUSHJ	P,NTDSTP##	;IF OUT OF RANGE, STOP
	PUSHJ	P,SAVEFW	;WE USE F := KDL, W := KDP
	CAIN	T1,DC.FAL	;ASSIGN LINE?
	 JRST	KF.AL		;YES
	MOVE	F,T2		;SET UP OUR KDL BLOCK POINTER
	HRRZ	T2,KDLUSR(F)	;GET THE USER OF THE LINE
	CAIE	T2,DD.ANF	;ANF?
	POPJ	P,		;NOPE, GIVE ERROR RETURN.
	HRRZ	W,KDLKDP(F)	;SET UP THE KDP ADDRESS AS WELL
	PUSHJ	P,@KDPDST(T1)	;DISPATCH ON THE FUNCTION CODE
	POPJ	P,		;FAILURE
	PJRST	CPOPJ1##	;GIVE GOOD RETURN

;FOR PROGRAM
KDODSP::SKIPL	T1		;RANGE CHECK
	CAILE	T1,DC.FQB	; THE FUNCTION CODE [DC.FMX]
	PUSHJ	P,NTDSTP##	;IF OUT OF RANGE, STOP
	PUSHJ	P,SAVEFW	;WE USE F := KDL, W := KDP
	CAIN	T1,DC.FAL	;ASSIGN LINE?
	 JRST	KF.AL		;YES
	MOVE	F,T2		;SET UP OUR KDL BLOCK POINTER
	HRRZ	T2,KDLUSR(F)	;GET THE USER OF THE LINE
	CAIE	T2,DD.PRO	;DECNET?
	POPJ	P,		;NOPE, GIVE ERROR RETURN.
	HRRZ	W,KDLKDP(F)	;SET UP THE KDP ADDRESS AS WELL
	PUSHJ	P,@KDPDST(T1)	;DISPATCH ON THE FUNCTION CODE
	POPJ	P,		;FAILURE
	PJRST	CPOPJ1##	;GIVE GOOD RETURN
KDPDST:	IFIW	KF.HA		;0 = HALT ALL PROTOCOLS
	IFIW	KF.IN		;1 = INITIALIZE NORMAL PROTOCOL (DDCMP)
	IFIW	KF.MA		;2 = INITIALIZE MAINT PROTOCOL (BOOTING)
	IFIW	KF.QO		;3 = QUEUE OUTPUT BUFFERS (MAINT OR NORMAL)
	IFIW	NTDSTP##	;4 = ASSIGN -- SEE SPECIAL DECODE ABOVE
;KF.AL	- ROUTINE TO ASSIGN A LINE
;CALL	MOVX	T2,DECNET LINE ID

KF.AL:	LDB	W,[POINT 9,T2,17] ;YES, CONVERT LINE ID (LIKON)
	CAILE	W,KDPMAX##	;BETTER BE IN RANGE
	 POPJ   P,		;SIGH
	SKIPN	W,KDPTBL##(W)	;POINT TO THE KDP BLOCK
	 POPJ	P,		;PATCHED OUT
	MOVE	T1,KDPCSR(W)	;GET UNIBUS ADDRESS
	PUSHJ	P,UBGOOD##	;BE SURE WE'RE THERE
	 POPJ	P,		;WE'RE NOT
	LDB	F,[POINT 9,T2,26] ;GET UNIT (DUP) NUMBER (LIUNI)
	CAML	F,KDPDPN(W)	;LEGAL NUMBER
	 POPJ	P,		;NO
	ADDI	F,KDPKDL(W)	;POINT TO KDL POINTER
	SKIPN	F,0(F)		;SEE IF PRESENT
	 POPJ	P,		;PATCHED OUT
	LDB	T1,[POINT 9,T2,26] ;GET UNIT (DUP) NUMBER (LIUNI) AGAIN
	LSH	T1,3		;  * 8 TO GET DUP-11 CSR OFFSET
	ADD	T1,KDP1DP(W)	;RELOCATE TO THE ADDRESS OF KDP'S 1ST DUP
	PUSHJ	P,UBGOOD##	;SEE IF THE DUP-11 EXISTS.
	 POPJ	P,		;IT DOESN'T, DON'T INIT
	HRRZ	T2,KDLUSR(F)	;GET LINE OWNER
	CAIE	T2,DD.DEC	;HACKING?
	 POPJ	P,		;NOT ME, YOU DON'T
	MOVEM	T3,KDLLBK(F)	;SAVE ADDRESS OF DECNET'S BLOCK
	MOVE	T1,F		;RETURN ADDRESS OF OURS
	JRST	CPOPJ1##	;SUCCESS
;KF.HA	- ROUTINE TO SHUT-DOWN PROTOCOL ON A KDL-LINE.
;CALL	MOVX	F,KDL-BLOCK ADDRESS
;	PUSHJ	P,KF.HA		;FROM EITHER CLOCK OR UUO LEVEL WITH THE
;				;  INTERRUPTS ON.
;RETURN	POPJ	P,		;ALWAYS - HALTS KMC IF ERROR

KF.HA:	LDB	T1,KDLSTA##	;FIRST GET OUR STATE,
	CAIGE	T1,KD%FLS	;IF WE ARE ALREADY "HALTED"
	RETSKP    		;RETURN WITH OUT TOUCHING ANYTHING
;	PUSHJ	P,KDLFLS	;MUST DO A BUFFER FLUSH TO QUIESCE THE KMC
	MOVEI	T1,KD%INI	;FLUSH COMPLETE, NOW GO
	DPB	T1,KDLSTA##	;TO "INITIALIZED" STATE
	PUSHJ	P,KDLPDN	;TELL OUR DRIVER THAT WE ARE DOWN
	RETSKP	  		;AND THE "HALT" COMMAND IS DONE
;KF.IN	- ROUTINE TO INITIALIZE THE KDL AND PUT IT IN NORMAL PROTOCOL
;CALL	MOVX	F,KDL-BLOCK ADDRESS
;	PUSHJ	P,KF.IN
;RETURN	CPOPJ			;ALWAYS

KF.IN:	PUSHJ	P,FREMAI	;FREE BUFFERS IF IN MAINT STATE
	LDB	T1,KDLSTA##	;GET THE LINE'S STATE
	CAIGE	T1,KD%INI	;IF THE LINE NEEDS { BASE ! CONTROL } IN
	PUSHJ	P,KDLINI	;THEN INITIALIZE THE LINE

	PUSHJ	P,KDLPD0	;CLEAN OUT QUEUES.
	MOVEI	T1,KD%STR	;GET THE "SENDING STARTS" STATE
	DPB	T1,KDLSTA##	;  AND SET THIS LINE'S STATE TO IT
	MOVSI	T1,(XMTBTS)	;MAKE SURE WE LEAVE NO CONTROL MESSAGES
	ANDCAM	T1,KDLSTS(F)	;PENDING FROM LAST TIME
	MOVSI	T1,(KDSSTR)	;GET THE "NEED TO SEND START" BIT
	IORM	T1,KDLSTS(F)	;  AND SET IT SO WE SEND ONE SOON
	PUSHJ	P,CLCTRS	;CLEAR THE MESSAGE NUMBERS
	KDPOFF			;NO INT'S IN RCV/XMT-BUF
	PUSHJ	P,RCVBUF	;SET UP A RECEIVE REQUEST
	PUSHJ	P,XMTBUF	;TRY TO SEND THE START
	KDPON			;INT'S OK NOW
	RETSKP    		;AND WE'RE DONE
;KF.MA	- ROUTINE TO INITIALIZE MAINT PROTOCOL ON THE KDL-LINE
;CALL	MOVX	F,KDL-BLOCK
;	PUSHJ	P,KF.MA
;RETURN	POPJ	P,		;ALWAYS

KF.MA:	LDB	T1,KDLSTA##	;FIRST MAKE GET OUR STATE, AND
	CAIN	T1,KD%MAI	;  IF WE ARE ALREADY IN MAINT STATE,
	RETSKP	  		;  THEN DON'T DO ANYTHING MORE
	CAILE	T1,KD%MAI	;IF WE ARE RUNNING (OR TRYING TO START)
	PUSHJ	P,KF.HA		;  THEN FLUSH ALL BUFFERS
	JFCL			;IGNORE ERROR RETURN
	LDB	T1,KDLSTA##	;GET OUR STATE AGAIN
	CAIGE	T1,KD%INI	;IF THE LINE NEEDS INITIALIZATION,
	PUSHJ	P,KDLINI	;THEN RE-SETUP THE KDL-BLOCK
	MOVEI	T1,KD%MAI	;GET AND SET THIS LINE TO
	DPB	T1,KDLSTA##	;  MAINT STATE
	KDPOFF			;DISABLE INTERRUPTS WHILE WE
	PUSHJ	P,RCVBUF	;SET UP RECEIVE AND
	PUSHJ	P,XMTBUF	;  TRANSMIT REQUESTS
	KDPON			;INTERUPTS ON AGAIN
	RETSKP	  		;AND WE'RE DONE
;KF.QO	- ROUTINE TO QUEUE OUTPUT MESSAGES
;CALL	MOVX	F,KDL-BLOCK ADDRESS
;	MOVX	T3,MESSAGE BUFFER ADDRESS
;	PUSHJ	P,KF.QO
;RETURN	CPOPJ			;ALWAYS

KF.QO:	HLLZS	MB.NXT(T3)	;ZERO THE FORWARD POINTER
	SETOM	MB.MSN(T3)	;-1 IN NUMBER FIELD MEANS NO NUMBER ASSIGNED
	KDPOFF			;PREPARE TO MESS WITH THE QUEUES
	LDB	T1,KDLSTA##	;GET STATE
	CAIE	T1,KD%RUN	;ARE WE IN STILL RUN STATE?
	 CAIN	T1,KD%MAI	;IT'S OK TO SEND DATA IN MAINT MODE TOO
	CAIA			;GOODNESS, IT'S OK
	 JRST   [KDPON		;NO, PASS PTR TO MSG IN T3
		MOVEI T1,DC.IOF	; GET THE "OUTPUT NOT DONE" FUNCTION
		PUSHJ P,CALDVR  ; AND TELL OUR DRIVER THE NEWS
		RETSKP]		;AND RETURN NO ERROR
	HRRZ	T1,KDLWTO(F)	;GET THE FIRST MESSAGE ON THE "WAIT OUTPUT" Q
	JUMPE	T1,[HRRZM T3,KDLWTO(F)	;IF NONE, THEN WE'RE FIRST
		    JRST KF.QO2]	;GO TRY TO SEND IT

KF.QO1:	HRRZ	T2,MB.NXT(T1)	;GET THE ADDRESS OF THE NEXT MESSAGE
	JUMPE	T2,[HRRM T3,MB.NXT(T1)	;IF NONE, APPEND TO THE END
		    JRST KF.QO2]	;AND FIRE UP THE XMITTER
	MOVE	T1,T2		;STEP TO NEXT MSG IN QUEUE
	JRST	KF.QO1		;AND SEE IF IT'S THE LAST

KF.QO2:	PUSHJ	P,RCVBUF	;MAKE SURE A RCV BUFFER IS QUEUED
	PUSHJ	P,XMTBUF	;TRY TO SEND THE MESSAGE
	KDPON			;DONE MESSING
	PUSHJ	P,FREMAI	;TRY TO FREE STALE MAINT MSG
	RETSKP			;DONE
	SUBTTL	INTERRUPTS -- INTERRUPT LEVEL INTERFACE TO THE KMC-11

                           Comment @

Each KMC-11 has two interrupt vector addresses.

    "A"	This interrupt is taken when RDYI (KMCRDI) comes up.  In this
	state the KMC-11 is ready for an input transaction.  All
	input transactions for the KMC-11 are queued in the KDP block.
	This is necessary because the following situation would otherwise
	cause a deadlock.
	   1)	The KMC-11 sets RDYO and gives a BUFFER-OUT transaction.
	   2)	At interrupt level, we want to do a BUFFER-IN.
	   3)	If, in the meantime, the KMC-11 has set RDYO again,
		we will not be able to get RDYI until we process another
		output transaction.
	The solution to this is to queue all input transactions.  This does
	mean that we have to take an interrupt on each transaction, but it
	does circumvent the above problem.

    "B"	This interrupt is taken when RDYO (KMCRDO) comes up.  In this
	state the KMC-11 is wants to perform an output transaction.
	It is these output transactions that drive almost all of the
	interrupt level KMC/DUP processing.

    The vector instructions are set up to be JSR's to the locations "KDPAIV",
and "KDPBIV" in the KDP block for the KMC-11.  These locations contain the
5 or 6 instructions necessary to save the AC's, load "W" with the address
of the particular KDP block, and dispatch to either of the two interrupt
routines "KDPAIV" or "KDPBIV".

                         End comment @
	SUBTTL	KDPAIV -- KMC-11 INTERRUPT VECTOR "A" PROCESSING.

;KDPAIV -- ROUTINE TO HANDLE KMC-11 INTERRUPT VECTOR "A" (INPUT)
;CALL	MOVE	W,[EXP KDP-BLOCK-ADDRESS]
;	PUSHJ	P,KDPAIV	;CALLED FROM KDPIVA IN THE KDP BLOCK
;RETURN	POPJ	P,		;TO DISMISS THE INTERRUPT.
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAY)
;
;ON MULTI-PROCESSOR SYSTEMS, THIS CODE WILL NEED TO AOSE THE KDP INTERLOCK
;
KDPAIV::AOS	KDPACT(W)	;COUNT THE INTERRUPT
	MOVE	T1,KDPSTS(W)	;GET THE STATUS
	TLNN	T1,(KDPSRU)	;  AND MAKE SURE WE THINK WE'RE ON LINE
	PJSP	T1,KDPERR	;  IF WE DON'T, CLEAR "RUN" SO INTS WILL STOP

	MOVE	U,KDPCSR(W)	;GET THE UNIBUS ADDRESS OF THE KMC-11
	MOVEI	T1,KMCRDI	;GET THE "RDYI" FLAG
	TION	T1,SEL2(U)	;MAKE SURE "RDYI" IS UP
	PJSP	T1,KDPERR	;  IF IT'S NOT, THEN ITS AN ILLEGAL INTERRUPT
	MOVE	T3,KDPIQT(W)	;GET NUMBER OF NEXT QUEUED TRANSACTION
	CAMN	T3,KDPIQP(W)	;MAKE SURE THAT IT'S DIFFERENT NOT THE "PUTTER"
	PJSP	T1,KDPERR	;  IF IT IS, THEN WE'RE GETTINT UNSOLICITED
				;  INTERRUPTS.  DECLARE KDP ILL.
	LSH	T3,1		;MAKE IT AN OFFSET INTO THE QUEUE
	ADDI	T3,KDPINQ(W)	;RELOCATE BY THE ADDRESS OF THE QUEUE
	MOVE	T1,0(T3)	;GET SEL2 DATA
	MOVE	T2,1(T3)	;GET SEL4, SEL6 DATA
	AOS	T3,KDPIQT(W)	;ADVANCE QUEUE TAKER
	CAIL	T3,KDPQLN	;IF ITS TIME TO WRAP AROUND, THEN
	SETZB	T3,KDPIQT(W)	;  WRAP AROUND TO THE FIRST ENTRY
	MOVEI	T4,KMCRQI	;GET THE REQUEST INPUT INTERRUPT BIT
	CAMN	T3,KDPIQP(W)	;IF QUEUE EMPTY (PUTTER = TAKER) THEN
	BCIO	T4,SEL0(U)	;  CLEAR RQI TO STOP THE INTERRUPTS

	WRIO	T2,SEL6(U)	;STORE TOP WORD
	MOVSS	T2,T2		;GET SEL4 DATA
	WRIO	T2,SEL4(U)	;  AND STORE THAT
	TRZ	T1,KMCRDI	;MAKE SURE RDYI CLEAR (SO KMC CAN RUN)
	WRIO	T1,SEL2(U)	;GIVE REST OF DATA TO KMC
	POPJ	P,		;ALL DONE
;KDPBIV -- ROUTINE TO HANDLE KMC-11 INTERRUPT VECTOR "B" (OUTPUT)
;CALL	MOVE	W,[EXP KDP-BLOCK-ADDRESS]
;	PUSHJ	P,KDPBIV	;CALLED FROM KDPIVB IN THE KDP BLOCK
;RETURN	POPJ	P,		;TO DISMISS THE INTERRUPT
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAYS)
;
;ON MULTI-PROCESSOR SYSTEMS THIS CODE WILL NEED TO AOSE THE KDP INTERLOCK.
;
KDPBIV::AOS	KDPBCT(W)	;COUNT THE INTERRUPT
	MOVE	T1,KDPSTS(W)	;GET OUR PERCEIVED STATUS
	TLNN	T1,(KDPSRU)	;  AND MAKE SURE WE THINK WE'RE RUNNING
	PJSP	T1,KDPERR	;  IF WE'RE NOT, CLEAR RUN AND RETURN

	MOVE	U,KDPCSR(W)	;GET THE UNIBUS ADDRESS OF THE KMC
	RDIO	P1,SEL2(U)	;READ STATUS BITS
	TRNN	P1,KMCRDO	;BETTER WANT AN OUTPUT TRANSACTION
	PJSP	T1,KDPERR	;ILLEGAL INTERRUPT. CRASH KMC
	RDIO	P2,SEL4(U)	;READ BDL ADDRESS
	RDIO	P3,SEL6(U)	;READ ERROR, STATUS OR WHAT EVER

	MOVEI	T1,KMCRDO	;GET THE RDYO BIT
	BCIO	T1,SEL2(U)	;CLEAR IT TO LET THE KMC CONTINUE
	TRNE	P1,2!KMCOVR	;MAKE SURE TYPE IS RIGHT, AND NO OVERRUN
	PJSP	T1,KDPERR	;  WE'RE CONFUSED. GIVE UP.

	LDB	F,[POINT 7,P1,27] ;GET 7 BIT LINE #.
	CAML	F,KDPDPN(W)	;IS THIS A LEGAL LINE FOR THIS KDP
	PJSP	T1,KDPERR	;  IF NOT, BETTER STOP NOW...
	ADDI	F,KDPKDL(W)	;MAKE IT AN OFFSET INTO THE KDP BLOCK
	HRRZ	F,(F)		;LOAD APPRIOATE KDL BLOCK ADDRESS

	SETZ	T1,		;GET A ZERO REGISTER
	TRNE	P1,KMCIOT	;IF INPUT, THEN
	TRO	T1,1		;  SET LOW ORDER BIT
	TRNE	P1,1		;IF CONTROL, THEN
	TRO	T1,2		;  SET NEXT TO LOW ORDER BIT
	JRST	@[JRST KDPBOO	;BUFFER OUT (OUTPUT)
		  JRST KDPBOI	;BUFFER OUT (INPUT)
		  JRST KDPCOO	;CONTROL OUT (OUTPUT)
		  JRST KDPCOI](T1);CONTROL OUT (INPUT)
				;ROUTINE DISPATCHED TO WILL RETURN.
	SUBTTL	KDPBOO -- KMC-11 BUFFER-OUT(OUTPUT) TRANSACTION PROCESSING.

;    This routine frees output buffers when a BUFFER-OUT(OUTPUT) transaction
;declares that the KMC/DUP has output all data in the buffer. It:
;	   1)	Makes sure that this is the last buffer in the current
;		Buffer Description List.  (Data messages consist of two
;		buffers.  The DDCMP header buffer, and the data buffer.
;		These two buffers are combined in a single BDL)  If
;		the current buffer is not the last in the list, nothing
;		more is done, and the interrupt is exited.  (We will
;		get another interrupt when the next buffer in the BDL
;		finishes.)
;	   2)	The count of buffers outstanding is decremented, and a
;		check is made to ensure that this buffer is really the
;		next one we expected to finish.
;	   3)	XMTBUF is called in an attempt to fill the just freed output
;		buffer.
;
;called with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPBOO:	PUSHJ	P,GETBDL	;GET T4 SET UP TO POINT TO BDL
	TLNE	T4,1		;MAKE SURE THAT BUFFER-OUT STARTS ON EVEN BYTE
	PJSP	T1,KDPERR	;  IF ODD BYTE, THEN KMC-11 SCREWED US
	MOVE	T1,1(T4)	;GET WORD WITH LAST BD HALFWORD
	TLNN	T4,2		;IF WE WANT FIRST HALFWORD,
	MOVS	T1,T1		;  THEN SWAP IT TO THE LOW HALF
	TRNN	T1,BDLLDS	;IS THIS THE LAST BUFFER IN THE BDL
	POPJ	P,		;IF NOT, IGNORE THIS INTERRUPT.

;WE ARE AT END OF MESSAGE.  MAKE SURE WE GOT THE RIGHT ONE BACK

	MOVEI	S,KDSNXB	;STATUS BIT THAT SAYS WHICH BUFFER IS NEXT OUT
	MOVEI	T1,KDLXD1(F)	;ASSUME THAT BUFFER #1 IS FIRST BACK
	TDNE	S,KDLSTS(F)	;  BUT IF IT SHOULD BE BUFFER #2, THEN
	MOVEI	T1,KDLXD2(F)	;  THEN CHANGE OUR MIND.

	MOVSI	T2,(1B0)	;GET THE BDL IN-USE MARK BIT
	TDNN	T2,0(T1)	;  AND MAKE SURE THAT IT'S SET
;[DBUG]	PJSP	T1,KDLERR	;IF NOT, TRY TO CRASH THE LINE
	PUSHJ	P,NTDSTP##	;WE'VE SCREWED UP THE BUFFERS [DBUG]
	ANDCAM	T2,0(T1)	;CLEAR THE USE BIT

	HLRZ	T3,1(T1)	;GET THE HALFWORD CONTAINING THE 3RD WORD
				;  OF THE FIRST BD IN THE BDL
	TRNN	T3,BDLLDS	;IF IT'S NOT THE LAST BD, THEN
	ADD	T1,[XWD 2,1]	;  ADVANCE THE CBP IN T1 3 WORDS (6 BYTES)
	CAMN	T1,T4		;MAKE SURE IT'S THE BDL ADDRESS THE KMC GAVE US
	SOSGE	KDLXBC(F)	;DECREMENT THE NUMBER OF ACTIVE OUTPUT BUFFERS
	PJSP	T1,KDPERR	;++ EITHER BAD BUFFER, OF COUNT WENT NEGATIVE.
	XORB	S,KDLSTS(F)	;SIGNAL THAT OTHER BUFFER IS NEXT BUFFER OUT
	PJRST	XMTBUF		;NOW GO TRY TO FILL THE JUST FREED BUFFER
	SUBTTL	KDPBOI -- ROUTINE TO HANDLE BUFFER-OUT(INPUT) TRANSACTIONS.

;    This routine handles BUFFER-OUT(INPUT) transactions.  These
;transactions consist of input messages coming in over the synchronous
;line.  This routine:
;	   1)	Frees the receive buffer. (No worry about races, this
;		code runs under the KMC interlock on multiprocessor
;		systems.  On a single processor system, it runs at
;		interrupt level.  Hence no one will try to allocate
;		the buffer between when it is freed and when it is
;		processed
;	   2)	Calls RCVMSG to process the incoming message
;
;called with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPBOI:	PUSHJ	P,FREBOI	;FREE THE INPUT BUFFER, (SET UP T4)
	  PJSP	T1,KDPERR	;?? KMC-11 GAVE BAD BDL ADDRESS ??
	PJRST	RCVMSG		;GO PROCESS MESSAGE (T4 POINTS TO BDL)
	SUBTTL	KDPCOO -- PROCESS A CONTROL-OUT(OUTPUT) TRANSACTION

;    This routine processes CONTROL-OUT(OUTPUT) transactions.  These
;consist primarily of various errors detected by the KMC-11.  This routine:
;	   1)	Counts the control out transaction code (CTOTLY)
;	   2)	Verifys that the error is legal/recoverable.  If not,
;		it crashes the KMC-11.
;	   3)	If the control out frees an output BDL, it assumes that
;		an output message has been clobbered, queues a REP message
;		to speed a recovery NAK, and calls "KDPBOO" to free the BDL.
;
;called with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPCOO:	PUSHJ	P,SAVE1##	;WE USE P1 FOR A "BIT MASK"
	PUSHJ	P,CTOTLY	;TALLY THE CONTROL OUT CODE AND
				;  PUT "BIT MASK" IN P1
	  PJSP	T1,KDPERR	;IF ILLEGAL CODE, KMC-11 IS SICK...

	TLNE	P1,^-<CTLO14!CTLO16!CTLO22!CTLO26> ;THESE ARE LEGAL OUTPUT
	  PJSP	T1,KDPERR	;  IF IT'S NOT ONE OF THEM, CRASH KMC-11

	TLNE	P1,CTLO14	;IS THIS A BUFFER TEMP UNAVAILABLE?
	JRST	[MOVEI T1,RSNBTU ;IF SO, GET THAT NAK CODE
		 PJRST RCVXNK]	;SEND THE NAK AND RETURN

	TLNE	P1,CTLO26	;IF THIS A "FLUSH DONE" TRANSACTION
	JRST	KDPFLO		;  IF FLUSH DONE, GO UPDATE KDLSTS
	TLNN	P1,CTLO22	;DOES THIS TRANSACTION FREE A BDL?
	POPJ	P,		;  IF NOT, RETURN (DON'T CALL XMTBUF...)

	MOVSI	T1,(KDSREP)	;ASSUME MESSAGE WAS CLOBBERED, SO
	IORM	T1,KDLSTS(F)	;  SO REQUEST A REP TO SPEED RECOVERY

	PJRST	KDPBOO		;PROCESS REST JUST LIKE ANY OTHER BUFFER-OUT
	SUBTTL	KDPCOI -- PROCESS CONTROL-OUT(INPUT) TRANSACTIONS

;    This routine processes the CONTROL-OUT(INPUT) transactions.  These
;transactions are primarily errors noticed by the KMC-11.  In particular,
;BCC (checksum) errors are processed here.  This routine:
;	   1)	Tallys the CONTROL-OUT transaction
;	   2)	Sees if it is a legal/recoverable error.  If not, it
;		crashes the KMC-11
;	   3)	If the transaction frees an input buffer, that is done.
;	   4)	If the transaction implies that a message was lost,
;		an approiate NAK is queued.
;
;called with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPCOI:	PUSHJ	P,SAVE1##	;P1 WILL HAVE THE "BIT MASK" IN IT.
	PUSHJ	P,CTOTLY	;TALLY THE CONTROL OUT TYPE
				;  RETURNS "BIT MASK" IN P1
	  PJSP	T1,KDPERR	;UNKNOWN CODE. KMC-11 IS SICK...
	TLNE	P1,^-<CTLO10!CTLO12!CTLO24!CTLO26> ;IS THIS LEGAL FOR INPUT
	PJSP	T1,KDLERR	;UN-RECOVERABLE ERROR.  BETTER START OVER

	TLNE	P1,CTLO26	;IS THIS AN "INPUT FLUSH" DONE TRANSACTION
	JRST	KDPFLI		;  IF INPUT FLUSH, GO UPDATE KDLSTS
	TLNE	P1,<CTLO10!CTLO12!CTLO24> ;DOES THIS FREE A BDL?
	PUSHJ	P,[PUSHJ P,FREBOI ;FREE THE INPUT BDL
		     PJSP T1,KDLERR ;BAD ADDRESS RETURNED BY KMC-11. DIE
		   POPJ P,]	;RETURN TO MAIN FLOW

	TLNN	P1,<CTLO10!CTLO12!CTLO24> ;DOES THIS REQUIRE A NAK?
	PJRST	RCVBUF		;IF NO NAK REQUIRED, WE ARE DONE
				;  ATTEMPT TO REQUEUE A BUFFER AND RETURN
	MOVEI	T1,RSNHCK	;ASSUME THAT ERROR WAS HEADER CHECKSUM
	TLNE	P1,CTLO12	;  BUT IF IT WAS A DATA CHECKSUM
	MOVEI	T1,RSNDCK	;  THEN CHANGE OUR MINDS AND USE THAT
	TLNE	P1,CTLO24	;  UNLESS IT WAS AN OVER-RUN, IN WHICH
	MOVEI	T1,RSNOVR	;  CASE WE SHOULD USE THIS.
	PJRST	RCVXNK		;QUEUE NAK AND REQUEUE BUFFERS IF POSSIBLE
;KDPFLO	ROUTINE TO CLEAN UP WHEN OUTPUT BUFFER FLUSH IS COMPLETED.
;  CLEAR "XMIT FLUSH IN PROGRESS" AND SAY NO OUTPUT BUFFERS QUEUED.

KDPFLO:	LDB	T1,KDLSTA##	;GET THE LINE'S STATE (JUST TO MAKE SURE)
	MOVEI	T2,KDSXFL	;GET THE "XMIT FLUSH" IN PROGRESS BIT
	CAIN	T1,KD%FLS	;IF WE'RE NOT IN "BUFFER FLUSH STATE", OR
	TDNN	T2,KDLSTS(F)	;  WE'RE NOT FLUSHING XMIT BUFFERS
	PJSP	T1,KDPERR	;  THEN ASSUME THE KMC-11 SCREWED UP

	SETZM	KDLXBC(F)	;SAY "NO OUTPUT BUFFERS ACTIVE"
	MOVEI	T1,KDSNXB!KDSXFL;  ALSO SAY THAT NEXT BUFFER IS #0, AND
	ANDCAB	T1,KDLSTS(F)	;  WE'RE NOT FLUSHING ANY MORE
	JRST	KDPFLX		;SEE IF WE CAN LEAVE "FLUSH" STATE


;KDPFLI	ROUTINE TO CLEAN UP WHEN INPUT BUFFER FLUSH IS COMPLETE

KDPFLI:	LDB	T1,KDLSTA##	;GET LINE'S STATE
	MOVEI	T2,KDSRFL	;"RECEIVE FLUSH IN PROGRESS" BIT
	CAIN	T1,KD%FLS	;MAKE SURE WE'RE FLUSHING
	TDNN	T2,KDLSTS(F)	;MAKE SURE IT'S INPUT
	PJSP	T1,KDPERR	;IF NOT INPUT FLUSH, THEN KMC GAVE BAD CODE

	SETZM	KDLRBC(F)	;ZERO COUNT OF RECEIVE BUFFERS POSTED
	MOVEI	T1,KDSNRB!KDSRFL;SAY BUFFER #0 NEXT, AND
	ANDCAB	T1,KDLSTS(F)	;  INPUT FLUSH COMPLETE

KDPFLX:	MOVEI	T2,KD%INI	;ASSUME THAT WE HAVE CLEARED BOTH FLUSH BITS
	TRNN	T1,KDSRFL!KDSXFL;  AND IF WE HAVE,
	DPB	T2,KDLSTA##	;  THEN WE'RE IN "INITED" STATE
	POPJ	P,		;DONE WITH THE INTERRUPT
	SUBTTL	RCVMSG -- ROUTINE TO DISPATCH ON THE DDCMP MESSAGE TYPE.

;Called from KDPBOI with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;	T4	    := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVMSG:	ADDI	T4,3		;POINT TO BUFFER 3 PDP-10 WORDS AFTER BDL
	HRLI	T4,2		;DDCMP HEADER STARTS IN RH SO DATA IS ON A WORD
	LDB	T1,BYTABL+2	;GET FIRST CHAR FROM MESSAGE
	CAIN	T1,SOH		;SOH ==: DATA MESSAGE
	JRST	RCVDAT		;  IF DATA, GO PROCESS IT
	CAIN	T1,ENQ		;ENQ ==: CONTROL MESSAGE
	JRST	RCVCTL		;  IF CONTROL GO PROCESS IT
	CAIN	T1,DLE		;DLE ==: MAINTENANCE MESSAGE.
	JRST	RCVMAI		;  IF MAINT, GO PROCESS THAT

	PJSP	T1,KDPERR	;IF WE DON'T RECOGNIZE IT, ASSUME KMC IS ILL.
	SUBTTL	RCVMAI -- ROUTINE TO PROCESS INCOMING MAINTENANCE MESSAGES.
;Called from RCVMSG with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;	T4	    := a CBP to the first byte of the BDL (set up by FREBOI)
;

RCVMAI:	AOS	KDLMAR(F)	;COUNT THE MAINTENANCE MESSAGES RECEIVED
	LDB	T1,KDLSTA##	;GET THE LINE'S STATE
	CAIG	T1,KD%MAI	;  AND SEE IF WERE IN MAINT STATE ALREADY
	JRST	RCVMA1		;IF NOT UP-AND-RUNNING, DON'T TELL OPERATOR
	CAIL	T1,KD%RUN	;IF THIS LINE IS UP, THEN
	PUSHJ	P,KDLPDN	;  TELL OUR USER THAT IT'S NOT!

	MOVE	U,OPRLDB##	;LINE JUST CRASHED.  TELL OPERATOR
	PUSHJ	P,INLMES##	;  ABOUT THIS
	ASCIZ	/%% Maintenance message received on /
	PUSHJ	P,PRKDL		;PRINT THE LINE NUMBER
	PUSHJ	P,INLMES##	;WE ALSO WANT TO TIME-STAMP THIS
	ASCIZ	/ at /
	PUSHJ	P,PRDTIM##	;SO PRINT THE TIME OF DAY.
RCVMA1:	LDB	T1,KDLSTA##	;GET OUR STATE, AND
	MOVEI	T2,KD%MAI	;GET THE MAINTENANCE STATE
	DPB	T2,KDLSTA##	;  AND SET THIS LINE TO IT.
	CAILE	T1,KD%MAI	;IF WE ARE TRYING TO START OR RUNNING PROTOCOL
	PUSHJ	P,[PUSH	P,T4	;SAVE CURRENT BDL POINTER
		   PUSHJ P,KDLPDN ;FREE OUR BUFFERS, TELL USER
		   MOVEI T1,DC.IMR ;THEN TELL OUR DRIVER THAT
		   PUSHJ P,CALDVR
		   POP	P,T4	;RESTORE THE BDL POINTER
		   POPJ	P,]	;AND RETURN
	LDB	T1,KDLSTA##	;GET OUR (POSSIBLY CHANGED!!) STATE
	CAIE	T1,KD%MAI	;AND IF OUR DRIVER HASN'T LEFT US IN MAINT
	PJRST	RCVBUF		;  STATE - DON'T READ THE MESSAGE

	TRNN	P3,BFREOM	;DOES THIS BUFFER HOLD THE ENTIRE MESSAGE
	PJSP	T1,KDLERR	;  IF NOT, WE CAN'T HANDLE IT.  DECLARE ERROR.

	LDB	T3,BYTABL+3	;GET THE LOW 8 BITS OF THE LENGTH
	LDB	T2,BYTABL+4	;GET THE HIGH 6 BITS
	DPB	T2,[POINT 6,T3,27] ;MAKE A 12 BIT NUMBER FROM THEM
	JUMPE	T3,RCVBUF	;IF EMPTY MESSAGE, IGNORE IT
	PUSH	P,T3		;SAVE THE COUNT
	PUSH	P,T4		;  AND THE CBP TO THE MESSAGE
	MOVEI	T1,DC.IGB	;REQUEST AN INPUT BUFFER
	PUSHJ	P,CALDVR	;CALL THE DRIVER FOR A BUFFER
	POP	P,T4		;RESTORE THE CBP
	POP	P,T3		; AND THE LENGTH OF THE MESSAGE
	JUMPE	T1,RCVBUF	;IF NO FREE BUFFERS, IGNORE THE MESSAGE
	ADD	T4,[XWD 1,1]	;ADVANCE THE INPUT BYTE POINTER TO START OF MSG
				;(HEADER IS 6 BYTES, WE WANT PRE-DEC CBP)
	MOVE	T2,MB.FMS(T1)	;GET THE POINTER TO THE MSD (IF DECNET)
	JUMPE	T2,[MOVSI T2,(POINT 8) ;IF ANF STYLE, BUILD A BYTE
		    HRRI T2,STCDAT(T1) ; POINTER TO THE DATA AREA
		    JRST RCVMA2]       ; AND GO COPY THE DATA
	CAMLE	T3,MD.BYT(T2)	;FIRST MAKE SURE THE WE GOT A BIG ENOUGH BUFFER
	PUSHJ	P,NTDSTP##	;++ DRIVER GAVE US A BAD BUFFER
	MOVEM	T3,MD.BYT(T2)	;STORE THE BYTE COUNT
	MOVE	T6,MD.ALA(T2)	;BYTE POINTER IS INDEXED BY T6
	MOVE	T2,MD.PTR(T2)	;FETCH THE BYTE POINTER TO THE FIRST BYTE
RCVMA2:	IBP	T2		;POINT TO WORD CONTAINING FIRST BYTE
	MOVEI	T2,@T2		;RESOLVE ANY INDEXING, ETC
	HRLI	T2,1(T4)	;POINT TO FIRST DATA WORD OF MESSAGE
	SOS	T3		;ROUND BYTE COUNT AND CONVERT TO WDS (+3 -4)
	LSH	T3,-2		;BYTES - WORDS
	ADDI	T3,(T2)		;END ADDRESS
	BLTUB	T2,(T3)		;MOVE MESSAGE TRANSLATED. (MABYE FEW EXTRA)

	MOVE	T3,T1		;COPY THE ADDRESS OF THE BUFFER
	MOVEI	T1,DC.IIC	;GET THE "INPUT DONE" FUNCTION
	PUSHJ	P,CALDVR	;TELL THE DRIVER THAT THE BUFFER'S FULL
	PJRST	RCVBUF		; AND WE'RE DONE
	SUBTTL	RCVCTL -- ROUTINE TO PROCESS INCOMING CONTROL MESSAGES.

;    This routine dispatches on the type field of incoming DDCMP
;control messages.
;
;Called from RCVMSG with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;	T4	    := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVCTL:	TRNN	P3,BFREOM	;WAS THIS MESSAGE LONGER THAN A SINGLE BUFFER
	PJSP	T1,KDPERR	;  IF IT WAS, THEN THE KMC-11 SCREWED UP...
	LDB	T3,KDLSTA##	;GET THE LINES STATE
	CAIG	T3,KD%FLS	;IF LINE IS NOT CURRENTLY ACTIVE,
	POPJ	P,		;  THEN RETURN WITH OUT RE-QUEUEING A RCV RQST

	LDB	T1,BYTABL+3	;GET THE SECOND BYTE (CONTROL TYPE)
;	CAIL	T1,%ACK		;RANGE CHECK IT
	CAILE	T1,%STACK	;  TO BE SURE THAT IT'S A LEGAL MESSAGE
	PJSP	T1,KDPERR	;THE KMC FED US GARBAGE...
	MOVEI	T2,KDLCTR-1(F)	;GET A POINTER TO THE CONTROL MESSAGE COUNTS
	ADDI	T2,(T1)		;  OFFSET BY THE MESSAGE TYPE
	AOS	(T2)		;  AND COUNT THIS RECEIVED CONTROL MESSAGE
	JRST	.(T1)		;DISPATCH TO APPROIATE MESSAGE PROCESSOR
	JRST	RCVACK		; 1 = ACK
	JRST	RCVNAK		; 2 = NAK
	JRST	RCVREP		; 3 = REP
	JSP	T1,KDPERR	; 4 = RESET (VESTIGIAL MESSAGE, NOW ILLEGAL)
	JSP	T1,KDPERR	; 5 = RESET (VESTIGIAL MESSAGE, NOW ILLEGAL)
	JRST	RCVSTR		; 6 = START
	JRST	RCVSTK		; 7 = STACK
RCVACK:	PUSHJ	P,CHKACK	;PROCESS ACK FIELD (MAY CALL DRIVER)
	PJRST	RCVBUF		;TRY TO SET UP ANOTHER RECEIVE REQUEST
				;  AND EXIT THE INTERRUPT.


RCVNAK:	LDB	T1,BYTABL+4	;GET THE NAK REASON
	ANDI	T1,77		;  AND JUST THE REASON
	PUSHJ	P,NAKTRN	;CONVERT THE NAK CODE INTO A TABLE INDEX
	ADDI	T1,KDLNKR(F)	;RELOCATE TO RECEIVED NAK TABLE
	AOS	(T1)		;COUNT THE RECEIVED NAK

	LDB	T1,KDLSTA##	;GET OUR STATE, AND
	CAIE	T1,KD%RUN	;  IF WE AREN'T RUNNING
	PJRST	RCVBUF		;DON'T PROCESS ANY NAK'S
	PUSHJ	P,CHKACK	;PROCESS THE ACK FIELD
	PUSHJ	P,STPTMR	;STOP THE TIMER
	HRRZ	T2,KDLWTA(F)	;GET THE HEAD OF THE LIST OF PCB'S AWAITING
				;  AN ACK
	JUMPE	T2,RCVNK2	;IF NONE, SKIP OVER CODE THAT RESENDS THEM

RCVNK1:	MOVEI	T1,(T2)		;COPY THE ADDRESS OF THE "NEXT" PCB
	HRRZ	T2,MB.NXT(T2)	;GET THE POINTER TO THE REST OF THE LIST
	JUMPN	T2,RCVNK1	;KEEP LOOPING IF NOT AT END OF LIST.
				;  AT EXIT, T1 := POINTER TO LAST PCB TO
				;  RE-TRANSMIT
	HRRZ	T2,KDLWTO(F)	;GET THE HEAD OF THE OUTPUT WAIT QUEUE
	HRRM	T2,PCBBLK(T1)	;AND MAKE IT THE "REST" OF THE WAIT ACK LIST
	SETZ	T1,		;GET A "ZERO" AND USE IT TO
	EXCH	T1,KDLWTA(F)	;  CLEAR THE "WAIT FOR ACK" QUEUE.
	MOVEM	T1,KDLWTO(F)	;NOW MAKE THE OLD ACK QUEUE THE WAIT FOR OUTPUT
				;  QUEUE
	SETZM	KDLCTA(F)	;SAY THAT THERE ARE NO MESSAGES AWAITING ACK.

RCVNK2:	PUSHJ	P,XMTBUF	;SEE IF THE TRANSMITTER NEEDS POKING
				;  (IT WILL IF OUTPUT QUEUE WAS EMPTY)
	PJRST	RCVBUF		;GO SET UP A RECEIVE REQUEST AND EXIT.


RCVREP:	LDB	T1,KDLSTA##	;GET LINE STATE
	CAIE	T1,KD%RUN	;MUST IGNORE UNLESS RUNNING
	 PJRST	RCVBUF		;IGNORE, REQUEUE RECEIVE BUFFER
	LDB	T1,KDLRMN##	;GET THE LAST RECIEVED MESSAGE NUMBER
	LDB	T2,BYTABL+6	;GET THE "N" FIELD OF THE REP MESSAGE
	CAIE	T1,(T2)		;COMPARE MESSAGE NUMBERS. SKIP IF ALL OK
	JRST	[MOVEI T1,RSNREP ;IF WE MISSED SOME, GET CODE "REP RESPONSE"
		 PJRST	RCVXNK]	;AND GO SEND A NAK
	MOVSI	S,(KDSACK)	;ASSUME RANDOM REP (RESPOND WITH ACK)
	IORM	S,KDLSTS(F)	;SET THE NEED TO SEND "?" MESSAGE FLAG
	MOVSI	S,(KDSNAK)	;CLEAR ANY PENDING NAK
	ANDCAM	S,KDLSTS(F)	;BECAUSE IT WOULD BE POINTLESS NOW
	PJRST	RCVNK2
RCVSTR:	CAIE	T3,KD%MAI	;IF NOT IN MAINT MODE
	JRST	RCVST1		;SEE IF WE'RE IN "RUN"
	MOVEI	T1,DC.ISR	;IF IN MAINT MODE, SAY WE'VE RECEIVED
	PUSHJ	P,CALDVR	;  A START MESSAGE
	JRST	RCVST2		;AND GO SEE IF WE CAN PROCESS IT

RCVST1:	CAIGE	T3,KD%RUN	;ARE WE RUNNING NOW?
	JRST	RCVST2		;  IF NOT, DON'T WORRY ABOUT TELLING NETSER

	PUSHJ	P,KDLPDN	;TELL NETSER THAT THIS LINE IS RESTARTING.

	MOVE	U,OPRLDB##	;WE WANT TO TELL THE OPR, GET HIS LDB ADDR
	PUSHJ	P,INLMES##	;SEND FIRST PART OF MESSAGE
	ASCIZ	/%% Unexpected restart on /
	PUSHJ	P,PRKDL		;PRINT THE FULL KDP#, KDL# MESSAGE
	PUSHJ	P,INLMES##	;FINISH MESSAGE OFF WITH TIME OF DAY
	ASCIZ	/ at /
	PUSHJ	P,PRDTIM##	;PRINT TIME AND CRLF

	;DDCMP SAYS WE SHOULD JUST HALT.  BUT WE WANT TO TRY AND KEEP THE
	;LINE UP.  SO WE ENTER SENDING STARTS STATE.  WE CAN NOT SKIP TO
	;SENDING STACKS, AS ANF PDP-11S GET VERY CONFUSED.

	MOVEI	T1,KD%STR	;SEND STARTS
	DPB	T1,KDLSTA##	;STARTING NOW
	MOVSI	T1,(XMTBTS)	;MAKE SURE PAST QUEUED REQUESTS ARE CANCELED
	ANDCAM	T1,KDLSTS(F)	;AS WE'D ONLY GET CONFUSED LATER
	MOVSI	T1,(KDSSTR)	;SEND A START
	IORM	T1,KDLSTS(F)	;WHEN WE GET A CHANGE
	PJRST	RCVNK2		;CRANK UP THE KMC

RCVST2:	LDB	T1,KDLSTA##	;GET OUR STATE, AND
	CAIE	T1,KD%STR	;  IF WE'RE NOT SENDING STARTS,
	 CAIN	T1,KD%STK	; OR SENDING STACKS
	CAIA			;OK
	 PJRST	RCVBUF		;THEN DON'T PROCESS THE START
	MOVEI	T1,KD%STK	;GET THE "SENDING STACK'S STATE"
	DPB	T1,KDLSTA##	;  AND PUT THIS LINE IN THAT STATE
	MOVSI	T1,(XMTBTS)	;GET MASK OF "MESSAGE QUEUED" BITS
	ANDCAM	T1,KDLSTS(F)	;  AND CLEAR ALL PENDING REQUESTS
	MOVSI	T1,(KDSSTK)	;GET THE "SEND A STACK" REQUEST BIT
	IORM	T1,KDLSTS(F)	;  AND REQUEST A STACK
	PJRST	RCVNK2		;CRANK US UP
RCVSTK:	CAIE	T3,KD%RUN	;IF NOT RUNNING
	 JRST	RCVSK1		;PROCEED
	MOVSI	T1,(KDSACK)	;RUNNING, SEND AN ACK
	IORM	T1,KDLSTS(F)	;TO KEEP OTHER END HAPPY
	MOVSI	T1,(KDSNAK)	;DON'T ALLOW NAK
	ANDCAM	T1,KDLSTS(F)	;WHICH WOULD KEEP THE ACK FROM GOING OUT
	JRST	RCVNK2		;CRANK THINGS UP

RCVSK1:	CAIE	T3,KD%STR	;IF WE AREN'T SENDING STARTS, THEN
	 CAIN	T3,KD%STK	; OK IF SENDING STACKS TOO
	CAIA
	PJRST	RCVBUF		;  IGNORE ALL STACKS
	PUSHJ	P,STPTMR	;STOP THE TIMER
	MOVEI	T1,KD%RUN	;GET THE "RUN" STATE
	DPB	T1,KDLSTA##	;  AND PUT THIS LINE IN "RUN"
	MOVSI	T1,(XMTBTS)	;GET THE BIT MASK OF MSG REQUESTS
	ANDCAM	T1,KDLSTS(F)	;  AND CLEAR ALL QUEUED REQUESTS
	MOVSI	T1,(KDSACK)	;GET THE "REQUEST ACK" BIT,
	IORM	T1,KDLSTS(F)	;  AND QUEUE AN ACK TO GET THINGS STARTED

	PUSHJ	P,KDLPUP	;TELL NETSER THAT THIS KDL IS ONLINE
	JRST	RCVNK2		;CRANK THINGS UP
	SUBTTL	RCVDAT -- ROUTINE TO PROCESS INCOMING DATA MESSAGES.

;Called from RCVMSG with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11
;	T4	    := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVDAT:	AOS	KDLDTR(F)	;COUNT THIS RECEIVED DATA MESSAGE
	TRNN	P3,BFREOM	;DOES THIS BUFFER CONTAIN THE ENTIRE MESSAGE
	PJSP	T1,KDLERR	;IF NOT, WE'RE NOT GOING TO RECOVER
				;SHOULD GIVE A "MESSAGE TO BIG" NAK BUT
				;  THE SECOND HALF OF THE MESSAGE WOULD
				;  BE INTERPRETED AS A COMPLETE NEW MESSAGE.
	PUSH	P,T4		;PRESERVE OUR BDL POINTER
	PUSHJ	P,CHKACK	;PROCESS THE IMPLICIT ACK IN THE MESSAGE
	POP	P,T4		;GET OUR BUFFER POINTER BACK
	LDB	T1,KDLSTA##	;FETCH OUR STATE
	CAIE	T1,KD%RUN	;IF WE ARE NOT RUNNINT,
	PJRST	RCVBUF		;THEN DON'T RECEIVE ANY DATA

	LDB	T1,BYTABL+6	;GET THIS MESSAGES NUMBER
	LDB	T2,KDLRMN##	;GET THE NUMBER OF THE LAST MESSAGE RECEIVED
	AOS	T2		;  PLUS ONE
	ANDI	T2,377		;MASK TO 8 BITS
	CAIE	T1,(T2)		;IS THIS THE ONE WE'RE EXPECTING?
	PJRST	RCVBUF		;IF NOT, JUST POST ANOTHER RECEIVE BUFFER

	LDB	T3,BYTABL+3	;FIRST GET THE LOW 8 BITS
	LDB	T1,BYTABL+4	;  AND THEN THE UPPER 6
	DPB	T1,[POINT 6,T3,27] ;ASSEMBLE THEM INTO THE MESSAGE LENGTH
	JUMPE	T3,RCVBUF	;IF ZERO LENGTH MESSAGE, IGNORE

	PUSH	P,T2		;REMEMBER MESSAGE NUMBER
	PUSH	P,T3		;SAVE THE MESSAGE LENGTH
	PUSH	P,T4		;  AND OUR POINTER INTO THE INPUT BUFFER
	MOVEI	T1,DC.IGB	;GET THE "REQUEST INPUT BUFFER" FUNCTION
	PUSHJ	P,CALDVR	;  AND CALL OUR DRIVER
	POP	P,T4		;RESTORE BHT CBP
	POP	P,T3		;  AND THE MESSAGE LENGTH
	POP	P,T2		;RESTORE OUR MESSAGE NUMBER
	JUMPE	T1,[MOVEI T1,RSNBTU ;IF WE DIDN'T GET A BUFFER
		    PJRST RCVXNK] ;  SEND A "NO ROOM" NAK
	DPB	T2,KDLRMN##	;FINALLY.  NOW WE CAN INCREMENT THE "RMN"
;NOW FIGURE OUT WHAT SORT OF INPUT BUFFER WE ARE COPYING INTO

	PUSH	P,T1		;SAVE OUR POINTER TO THE BUFFER
	MOVE	T2,MB.FMS(T1)	;GET THE POINTER TO THE MSD (IF DECNET)
	JUMPE	T2,RCVDA1	;IF ANY, GO GET POINTERS FROM PCB
;	CAMLE	T3,MD.ALL(T2)	;MAKE SURE THAT THE DATA WILL FIT
;	PUSHJ	P,NTDSTP##	;++ DRIVER GAVE US TOO SMALL A BUFFER
	MOVEM	T3,MD.BYT(T2)	;STORE THE BYTE COUNT
	MOVE	T6,MD.ALA(T2)	;BYTE POINTER IS INDEXED BY T6
	MOVE	T2,MD.PTR(T2)	; AND FETCH THE BYTE POINTER
	JRST	RCVDA2		;NOW GO COPY THE DATA

;HERE FOR ANF-10 STYLE BUFFERS
RCVDA1:	MOVE	T2,PCBCTR(T1)	;GET THE SIZE OF THE PCB
	CAMLE	T3,T2		;  AND MAKE A QUICK SIZE CHECK
	PUSHJ	P,NTDSTP##	;STOP IF DRIVER GAVE TOO SMALL A BUFFER
	MOVEM	T3,PCBCTR(T1)	;STORE THE ACTUAL LENGTH FOR NETSER
	MOVE	T2,PCBPTR(T1)	;GET THE INPUT BYTE POINTER
;	JRST	RCVDA2		;FALL THROUGH TO THE COPY ROUTINES
;AT THIS POINT
;	T2	:= A BYTE POINTER INTO THE PCB
;	T3	:= THE NUMBER OF BYTES TO MOVE
;	T4	:= A CBP INTO THE MESSAGE TO COPY.
;FOR EFFICIENCY, COPY AS MANY SETS OF FOUR BYTES AS POSSIBLE.

RCVDA2:	ADD	T4,[XWD 1,1]	;ADVANCE THE CBP TO THE FIRST DATA BYTE
				;(DDCMP HEADER IS 6 BYTES, WANT PRE DEC CBP)
	IBP	T2		;POINT TO WORD CONTAINING FIRST BYTE
	MOVEI	T2,@T2		;RESOLVE ANY INDEXING, ETC
	HRLI	T2,1(T4)	;POINT TO FIRST DATA WORD OF MESSAGE
	SOS	T3		;ROUND BYTE COUNT AND CONVERT TO WDS (+3 -4)
	LSH	T3,-2		;BYTES - WORDS
	ADDI	T3,(T2)		;END ADDRESS
	BLTUB	T2,(T3)		;MOVE MESSAGE TRANSLATED. (MAYBE FEW EXTRA)

RCVDA6:	POP	P,T3		;GET OUR BUFFER ADDRESS BACK
	MOVEI	T1,DC.IIC	;GET THE "INPUT DONE" FUNCTION CODE
	PUSHJ	P,CALDVR	;CALL THE DRIVER TO RETURN THE FILLED BUFFER
	PUSHJ	P,RCVBUF	;TRY TO QUEUE ANOTHER RECEIVE BUFFER
	MOVSI	T1,(KDSNAK)	;NO POINT ANY MORE
	ANDCAM	T1,KDLSTS(F)	;...
	MOVSI	T1,(KDSACK)	;SEND AN ACK WHEN XMITTER IS IDLE
	IORM	T1,KDLSTS(F)	;TO ALLOW REMOTE TO FREEE BUFFERS & FILL PIPE
	PJRST	XMTBUF		;ANY DATA MESSAGE 'IMPLIED ACK' WILL CANCEL
	SUBTTL	RCVBUF -- POST AN INPUT BUFFER RECEIVE REQUEST

;    This routine:
;	   1)	Checks for a free input buffer.  If none, it returns.
;	   2)	Allocates the input buffer and marks it as "in use".
;	   3)	Constructs a BDL for the buffer.
;	   4)	Queue's a BUFFER-INPUT(INPUT) transaction for the KMC-11
;
;Called from "many places" with:
;	F, W 	    := KDL and KDP pointers
;
RCVBUF:	LDB	T1,KDLSTA##	;GET OUR STATE
	CAIG	T1,KD%FLS	;SKIP IF WE CAN QUEUE ANOTHER RECEIVE BUFFER
	POPJ	P,		;  IF WE CAN'T, JUST EXIT NOW
	AOSG	T1,KDLRBC(F)	;INCREMENT COUNT OF RCV BUFFERS QUEUED
	PUSHJ	P,NTDSTP##	;++ SHOULD NEVER BE NEGATIVE
	CAILE	T1,2		;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
	JRST	[SOS KDLRBC(F)	;CORRECT THE COUNT
		 POPJ P,]	;  AND RETURN

	PUSHJ	P,SAVP4##	;BEFORE WE DO REAL WORK, PRESERVE PERM AC
	SOS	T1		;T1 NOW IS EITHER 0 ! 1
	MOVE	T2,KDLSTS(F)	;GET THE STATUS (CONTAINS NEXT BUFFER OUT BIT)
	TRNE	T2,KDSNRB	;IF NEXT BUFFER OUT ISN'T 0
	XORI	T1,1		;THEN NEXT BUFFER IN IS DIFFERENT
	MOVEI	P4,@[CAI KDLRD1(F) ;LOAD PROPER BDL ADDRESS
		     CAI KDLRD2(F)](T1) ;FROM T1

	MOVSI	T1,(1B0)	;GET A SIGN BIT AND USE IT TO MAKE SURE
	TDNE	T1,(P4)		;  THAT THIS BDL IS REALLY FREE
	PUSHJ	P,NTDSTP	;++ SOMETHING SCREWED UP...
;	IORM	T1,(P4)		;SET BDL IN USE CHECK BIT (WE SET IT LATER)

;DROP TRHOUGH TO CONSTRUCT THE BDL
;FROM ABOVE.  HERE TO CONSTRUCT THE BDL AND QUEUE THE BUFFER-IN TRANSACTION

	MOVEI	T1,3(P4)	;GET START OF DATA MESSAGE AREA
	SUBI	T1,(F)		;CONVERT TO OFFSET ON PAGE
	LSH	T1,2		;CONVERT FROM WORDS TO BYTES
	ADDI	T1,2		;STARTS AT 3+1/2 WDS SO USER DATA IS WORD
				;ALIGNED & CAN UBABLT
	ADD	T1,KDLADR(F)	;CONVERT PAGE OFFSET TO UNIBUS ADDR
	MOVEI	T2,-16(T1)	;SAVE ADDR OF BDL FOR LATER
;BDL1.1
	HRLZM	T1,0(P4)	;STORE LOW 16 BITS OF ADDR IN THE BDL
;BDL1.3
	LSH	T1,-6		;SHIFT BITS 16 & 17 DOWN TO SCREWY POSITION
	ANDI	T1,BDLXAD	;MASK TO GET JUST THE EXTENDED ADDR BITS
	TRO	T1,BDLLDS	;SAY THAT THIS IS THE LAST BD
	HRLZM	T1,1(P4)	;STORE THIRD BDL WORD
;BDL1.2
	MOVEI	T1,KDLMMS*4	;GET MAX MESSAGE SIZE
	HRRM	T1,0(P4)	;AND STORE THAT IN THE LENGTH FIELD

	MOVSI	T1,(1B0)	;GET THE BDL IN USE BIT
	IORM	T1,0(P4)	;  AND MARK BDL AS IN USE

;NOW QUEUE THE BUFFER IN TRANSACTION

	MOVE	W,KDLKDP(F)	;JUST MAKE SURE "W" IS SET UP
	MOVE	T1,KDLINE(F)	;GET OUR LINE NUMBER
	LSH	T1,10		;SHIFT INTO THE HIGH BYTE
	TRO	T1,KMCIOT	;SAY THAT WE ARE QUEUEING AN INPUT BUFFER
	HRLZ	T3,T2		;COPY THE BDL ADDRESS
	LSH	T2,-2		;SHIFT TWO HIGH ORDER BITS DOWN
	IOR	T2,T3		;COMBINE SEL4 AND SEL6
	AND	T2,[XWD 177777,140000] ;MASK OUT TRASH
	PUSHJ	P,KMCINP	;QUEUE TRANSACTION
	POPJ	P,		;AND RETURN
	POPJ	P,		;(IGNORE SKIP RETURN)
	SUBTTL	XMTBUF -- ROUTINE TO TRANSMIT DDCMP CONTROL AND DATA MESSAGES.

;Called from "most anywhere" with interrupts off and:
;	F, W 	    := KDL and KDP pointers
;
XMTBUF:	PUSHJ	P,SAVE4##	;THIS CODE CLOBBERS THEM ALL
	MOVE	T1,KDLSTS(F)	;GET THE WORD WITH THE MESSAGE FLAGS
	AND	T1,[XMTBTS]	;  AND GET JUST THE FLAGS
	LDB	T2,KDLSTA##	;GET THE STATE
	CAIG	T2,KD%FLS	;  AND IF WE'RE FLUSHING,
	POPJ	P,		;  THEN DON'T DO ANYTHING
	CAIN	T2,KD%MAI	;IF WE ARE IN MAINT STATE, THEN
	SETZ	T1,		;  WE CAN ONLY SEND DATA (MAINT) MSGS
	SKIPE	KDLWTO(F)	;IF THERE IS DATA TO GO,
	TLO	T1,(KDSDAT)	;  THEN SET THE BIT SAYING SO.
	JFFO	T1,XMTBF1	;JUMP IF THERE ARE ANY MSGS TO SEND
	POPJ	P,		;  IF NO MSGS TO SEND, EXIT

XMTBF1:	AOSG	T1,KDLXBC(F)	;INCREMENT COUNT OF XMIT BUFFERS QUEUED
	PUSHJ	P,NTDSTP##	;++ SHOULD NEVER BE NEGATIVE
	CAILE	T1,2		;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
	JRST	[SOS KDLXBC(F)	;CORRECT THE COUNT
		 POPJ P,]	;  AND RETURN

	SOS	T1		;T1 NOW IS EITHER 0 ! 1
	MOVE	T3,KDLSTS(F)	;GET THE STATUS (CONTAINS NEXT BUFFER OUT BIT)
	TRNE	T3,KDSNXB	;IF NEXT BUFFER OUT ISN'T 0
	XORI	T1,1		;THEN NEXT BUFFER IN IS DIFFERENT
	MOVEI	P4,@[CAI KDLXD1(F) ;LOAD PROPER BDL ADDRESS
		     CAI KDLXD2(F)](T1) ;FROM T1

	MOVSI	T1,(1B0)	;GET A SIGN BIT AND USE IT TO MAKE SURE
	TDNE	T1,(P4)		;  THAT THIS BDL IS REALLY FREE
	PUSHJ	P,NTDSTP	;++ SOMETHING SCREWED UP...
	IORM	T1,(P4)		;SET BDL IN USE CHECK BIT (WE SET IT LATER)

;NOW P4 := BDL ADDRESS, TIME TO DISPATCH TO PROPER ROUTINE.

	PUSHJ	P,@[		;DISPATCH ON JFFO CODE
		    JRST XMTSTR	;SEND START
		    JRST XMTSTK	;SEND STACK
		    JRST XMTNAK	;SEND NAK
		    JRST XMTREP	;SEND REP
		    JRST XMTDAT	;SEND DATA
		    JRST XMTACK	;SEND ACK
		   ](T2)	;MUST REMEMBER NOT TO CLOBBER "T2" ABOVE...
	  JFCL			;IGNORE IT IF KMC-11 CRASHES
	POPJ	P,		;BACK TO CALLER
XMTSTR:	MOVEI	T1,%START	;GET THE START MESSAGE TYPE
	MOVSI	T2,(KDSSTR)	;GET THE "BIT TO CLEAR"
	DPB	T2,KDLTIM##	;START THE TIMER
	PUSHJ	P,XMTSET	;SET UP BDL, T1, T2, T4 ETC...
	PJRST	KMCINP		;  QUEUE TRANSACTION, CLEAR THE BIT AND RETURN

XMTSTK:	MOVEI	T1,%STACK	;GET THE STACK MESSAGE TYPE
	MOVSI	T2,(KDSSTK)	;GET THE "BIT TO CLEAR"
	DPB	T2,KDLTIM##	;START THE TIMER
	PUSHJ	P,XMTSET	;SET UP BDL, T1, T2, T4 ETC...
	PJRST	KMCINP		;  QUEUE TRANSACTION, CLEAR THE BIT AND RETURN

XMTREP:	MOVEI	T1,%REP		;GET THE REP MESSAGE TYPE
	MOVSI	T2,(KDSREP)	;GET THE "BIT TO CLEAR"
	DPB	T2,KDLTIM##	;START THE TIMER
	PUSHJ	P,XMTSET	;SET UP BDL, T1, T2, T4 ETC...
	LDB	T3,KDLLMX##	;GET LAST TRANSMIT MSG. NUMBER ASSIGNED
	DPB	T3,BYTABL+4	;  AND SEND THAT AS THE MSG NUMBER
	PJRST	KMCINP		;  QUEUE TRANSACTION, CLEAR THE BIT AND RETURN

XMTNAK:	MOVSI	T1,(KDSACK)	;IF ACK QUEUE'D TOO
	TDNE	T1,KDLSTS(F)	;I'M VERY CONFUSED
	 PUSHJ	P,NTDSTP##	;SO I'D BETTER QUIT
	MOVEI	T1,%NAK		;GET THE NAK MESSAGE TYPE
	MOVSI	T2,(KDSNAK)	;GET THE "BIT TO CLEAR"
	PUSHJ	P,XMTSET	;SET UP BDL, T1, T2, T4 ETC...
	PUSH	P,T1		;SAVE T1
	PUSH	P,T2		;  AND T2 (NAKTRN GET'S EM)
	LDB	T1,KDLXNK##	;GET THE NAK CODE.
	IORI	T1,QSYNC!SELECT	;PUT ON THE TWO "REQUIRED" BITS
	DPB	T1,BYTABL+2	;  AND STORE THAT IN THE MESSAGE
	ANDI	T1,77		;GET JUST THE NAK CODE BACK AGAIN,
	PUSHJ	P,NAKTRN	;  CONVERT IT INTO A TABLE INDEX
	ADDI	T1,KDLNKX(F)	;  RELOCATE TO OUR XMIT NAK COUNTER TABLE
	AOS	(T1)		;  AND INCREMENT THE ENTRY.
	POP	P,T2		;GET T2 AND
	POP	P,T1		;  BACK
	LDB	T3,KDLRMN##	;GET LAST MSG. RECEIVED
	DPB	T3,BYTABL+3	;  SEND THAT AS IMPLICIT ACK
	PJRST	KMCINP		;QUEUE TRANSACTION, CLEAR THE BIT AND RETURN

XMTACK:	MOVEI	T1,%ACK		;GET THE ACK MESSAGE TYPE
	MOVSI	T2,(KDSACK)	;GET THE "BIT TO CLEAR"
	PUSHJ	P,XMTSET	;SET UP BDL, T1, T2, T4 ETC...
	LDB	T3,KDLRMN##	;GET LAST MSG. RECEIVED
	DPB	T3,BYTABL+3	;  SEND THAT AS IMPLICIT ACK
	PJRST	KMCINP		;QUEUE TRANSACTION, CLEAR THE BIT AND RETURN
;    XMTSET is a utility subroutine used in constructing the various
;DDCMP control messages.  It performs the functions of:
;	   1)	Clearing the message request bit in KDLSTS.
;	   2)	Constructing a BDL for the control message
;	   3)	Filling in selected portions of the control message.
;		Specifically, the header (ENQ), type, control bits and
;		station address (= 1).
;	   4)	Sets things up to make it convenient to call KMCINP
;
;Called from XMT??? with:
;	F, W 	    := KDL and KDP pointers
;	T1	    := Control message type
;	T2	    := Message request bit in KDLSTS to clear.
;	P4	    := Address of the BDL to use for the message
;
;Returns "CPOPJ" with:
;	T1, T2	    := Set up with SEL2, SEL4 and SEL6 for KMCINP.
;	T4	    := A CBP to the first byte of the message (The ENQ)
;
XMTSET:	PUSH	P,T1		;SAVE MESSAGE TYPE FOR A BIT
	ADDI	T1,KDLCTX-1(F)	;MAKE MESSAGE TYPE A POINTER INTO THE XMITTED
	AOS	(T1)		;  COUNTS TABLE, AND COUNT THIS CONTROL MSG

	ANDCAM	T2,KDLSTS(F)	;CLEAR THE REQUEST BIT.

	MOVEI	T4,4(P4)	;GET ADDRESS OF THE DATA AREA

	MOVEI	T2,(T4)		;GET ADDRESS OF DATA
	SUBI	T2,(F)		;CONVERT TO OFFSET WITHIN PAGE
	LSH	T2,2		;GET PAGE OFFSET IN BYTES
	ADD	T2,KDLADR(F)	;MAKE IT A UNIBUS ADDRESS
;BDL1.1
	MOVEI	T1,400000(T2)	;MAKE SURE WE SET THE IN-USE MARK BIT
	HRLZM	T1,0(P4)	;STORE -11 ADDRESS OF DDCMP HEADER
;BDL1.2
	MOVEI	T1,6		;GET THE LENGTH OF THE DDCMP HEADER
	HRRM	T1,0(P4)	;WRITE THE LENGTH
;BDL1.3
	MOVEI	T1,(T2)		;GET ADDRESS OF THE HEADER AGAIN
	LSH	T1,-6		;SHIFT TO GET TWO HIGH BITS OF ADDRESS
	ANDI	T1,BDLXAD	;MASK TO JUST 2 BITS (6000)
	TRO	T1,BDLLDS!BDLRSY!BDLSOM!BDLEOM ;RESYNC, START, END OF MESSAGE
	HRLZM	T1,1(P4)	;WRITE THIRD WORD OF HEADER

;FALL THROUGH TO WRITE THE CONTROL MESSAGE
;FROM ABOVE.  HERE TO WRITE THE CONTROL MESSAGE

	MOVE	W,KDLKDP(F)	;MAKE SURE "W" IS SET UP
	MOVE	T1,KDLINE(F)	;GET THE LINE #
	LSH	T1,10		;PUT LINE NUMBER IN THE HIGH BYTE

	ADDI	T2,-20		;CORRECT TO GET ADDRESS OF THE BDL
	HRLZ	T3,T2		;COPY THE BDL'S -11 ADDRESS
	LSH	T2,-2		;SHIFT HIGH TWO BITS DOWN
	IOR	T2,T3		;COMBINE
	AND	T2,[XWD 177777,140000] ;MASK OUT TRASH

	MOVEI	T3,ENQ		;GET THE CONTROL MESSAGE HEADER
	DPB	T3,BYTABL+0	;  AND SET THAT
	POP	P,T3		;GET THE CONTROL MESSAGE TYPE
	DPB	T3,BYTABL+1	;  AND SET THAT
	MOVEI	T3,QSYNC!SELECT	;THESE TWO BITS ARE REQUIRED BY DDCMP
	DPB	T3,BYTABL+2	;  SO PUT THEM IN THE MESSAGE

	SETZ	T3,		;ZERO THE MESSAGE NUMBER FIELDS
	DPB	T3,BYTABL+3	;  ACK FIELD
	DPB	T3,BYTABL+4	;  NUMBER FIELD
	MOVEI	T3,1		;GET THE POINT-TO-POINT STATION ADDR = 1
	DPB	T3,BYTABL+5	;SET STATION ADDRESS
	POPJ	P,		;AND THAT'S ALL
	SUBTTL	XMTDAT -- ROUTINE TO SEND A DATA MESSAGE.

;   XMTDAT constructs DDCMP data messages. It:
;	    1)	Requeues the BFR of the message it is outputing
;		to the "awaiting ACK" (KDLWTA) queue.  (We are
;		interlocked, so there is no fear of races.)
;	    2)	Copies the "header" portion of the message to the
;		appropriate output buffer in the KDL page.
;	    3)	Copies, with the appropriate compression, the "data"
;		portion of the BFR.  (Ie. The PCBPT2 data.)
;	    4)	It the fills in the DDCMP header, constructs a BDL
;		consisting of two buffer descriptors. (One for the
;		DDCMP header, and one for the data portion)
;	    5)	It queues BUFFER-OUT(OUTPUT) transaction for the KMC-11
;
;Called from XMTBUF with:
;	F, W 	    := KDL and KDP pointers
;	P4	    := The address of the BDL to use for the message.
;Returns
;	CPOPJ	If KMC died.
;	CPOPJ1	With message queued if all went OK.
;
;Register usage.
;	S     =   The character we are currently hacking
;	T1    =   Count of chars left in BFR
;	T2    =   Byte pointer into the BFR
;	T3    =   Total number of chars put in the output message buffer
;	T4    =   A "canonical" byte pointer to the output message buffer
;
;	P1-P3 =   "Scratch" registers for the conversion routines.
;	P4    =   Pointer to KDLXD?.  Ie. the approiate buffer descriptor.
;
;	U     =   Pointer to the BFR
;	F     =   Pointer to kdl line block table
;	W     =   Pointer to kdp table
;
;FIRST MAKE SURE THAT WE DON'T HAVE TOO MANY MSGS IN THE "PIPE"

XMTDAT:	AOS	T1,KDLCTA(F)	;COUNT UP THE MESSAGES AWAITING ACK
	CAIL	T1,MAXOUT	;  BUT IF THERE ARE TOO MANY,
	JRST	[SOS KDLCTA(F)	;  CORRECT THE ACK AND
		 SOS KDLXBC(F)	;  NUMBER OF BUFFERS QUEUED COUNTS
		 SETZM (P4)	;  MARK THE BDL AS "FREE"
		 POPJ P,]	;  AND DON'T DO ANYTHING.

;NOW REQUEUE THE BFR FROM THE WTO QUEUE TO THE WTA.
	PUSH	P,U		;SAVE "U" FOR THE DURATION
	HRRZ	U,KDLWTO(F)	;GET THE FIRST ENTRY ON THE WTO QUEUE
	HRRZ	T1,MB.NXT(U)	;GET THE SECOND ENTRY ...
	HRRZM	T1,KDLWTO(F)	;NOW MAKE THE SECOND THE NEW FIRST

	HLLZS	MB.NXT(U)	;MAKE SURE THERE ARE NO STRAY POINTERS
	HRRZ	T1,KDLWTA(F)	;GET ADDR OF FIRST ON WTA QUEUE
	JUMPE	T1,[HRRZM U,KDLWTA(F)	;IF NONE, MAKE THIS THE FIRST
		    JRST XMTDA2]	;  AND GO PROCESS IT

XMTDA1:	HRRZ	T2,MB.NXT(T1)	;SEE IF WE'VE REACHED THE END OF THE WTQ Q
	JUMPE	T2,[HRRM U,MB.NXT(T1)	;IF SO, APPEND THIS ONE TO THE END
		    JRST XMTDA2]	;  AND TO PROCESS IT
	MOVE	T1,T2		;IF NOT AT END OF QUEUE, GO TO NEXT BFR
	JRST	XMTDA1		;  AND SEE IF IT IS THE END OF THE QUEUE


;NOW SEE IF THE MESSAGE HAS BEEN ASSIGNED A NUMBER.  IF NOT, THEN
;  ASSIGN A NEW ONE
XMTDA2:	LDB	T1,KDLSTA##	;GET OUR STATE, AND
	CAIN	T1,KD%MAI	;  IF WE ARE IN MAINT STATE
	JRST	XMTDA3		;  THEN DON'T ASSIGN A MESSAGE NUMBER
	MOVE	T1,MB.MSN(U)	;(MB.MSNN) = -1 MEANS NO NUMBER ASSIGNED
	JUMPGE	T1,XMTDA3	;IF RESENDING MSG, DON'T ASSIGN A NUMBER
	LDB	T1,KDLLMX##	;GET LAST NUMBER ASSIGNED
	ADDI	T1,1		;INCREMENT IT TO THE NEXT
	ANDI	T1,^O377	;MASK TO 8 BITS
	DPB	T1,KDLLMX##	;REMEMBER LAST MESSAGE ASSIGNED
	MOVEM	T1,MB.MSN(U)	;  AND ASSIGN IT TO THIS MESSAGE
;  FROM HERE ON, THE FOLLOWING CONVENTIONS GENERALLY HOLDS
;	T3 := COUNT OF THE NUMBER OF BYTES SO FAR
;	T4 := A BYTE POINTER (PDP-10 STYLE) TO THE OUTPUT BUFFER.
;  WHEN WE ARE ALL DONE COPYING THE DATA, WE WILL SWAP THE BYTES
;  ALL AT ONCE.

XMTDA3:	MOVE	T2,MB.FMS(U)	;NOW SEE IF THIS IS ANF-10 OR DECNET FORMAT
	JUMPE	T2,XMTANF	;  IF IT'S ANF, GO HANDLE FUNNY BUFFERS
	MOVSI	T4,(POINT 8)	;START OFF WITH T4 BEING A BYTE POINTER
	HRRI	T4,6(P4)	; TO THE OUTPUT BUFFER
	PUSH	P,[EXP 0]	;INITIALIZE THE "TOTAL" COUNT
XMTDEC:	;ADD CHECK FOR BEING OVERLOADED
	MOVE	T6,MD.ALA(T2)	;BYTE POINTER IS INDEXED BY T6
	MOVE	T1,MD.AUX(T2)	; AND IT'S BYTE ADDRESS
	MOVE	T3,MD.BYT(T2)	;COPY THE LENGTH FOR THE DESTINATION
	ADDM	T3,(P)		; AND ACCUMULATE THE TOTAL COPIED
XMTDE0:	SOJL	T3,XMTDE1	;COUNT DOWN NUMBER OF BYTES
	ILDB	T5,T1		;GET A BYTE
	IDPB	T5,T4		;PUT A BYTE
	JRST	XMTDE0		;AND LOOP

XMTDE1:	HRRZ	T2,MD.NXT(T2)	;STEP TO THE NEXT SEGMENT
	JUMPN	T2,XMTDEC	; AND IF THERE IS ONE, COPY THAT TOO
	POP	P,T3		;T3 := THE TOTAL LENGTH
	CAILE	T3,KDLMMS*4	;MAKE SURE THE LENGTH WAS REASONABLE
	PUSHJ	P,NTDSTP##	;++ MESSAGE WAS TOO BIG
	JRST	XMTXIT		;GO SWAP THE BYTES AND SEND THE MESSAGE
;HERE FOR ANF-10 STYLE BUFFERS
XMTANF:	LDB	T1,KDLSTA##	;FIRST GET THE STATE, AND
	CAIN	T1,KD%MAI	;IF THIS IS A MAINT MESSAGE
	JRST	XMTANM		;  GO PROCESS THE STC-BLOCK FORMAT

;NOW COPY THE "NCL HEADER" PORTION OF THE MESSAGE.
;  (SNEAKILY MAKING USE OF THE FACT THAT PCB'S START ON WORDS)

	MOVE	T1,PCBCTR(U)	;GET THE LENGTH OF THE HEADER
	MOVE	T3,T1		;GET A "FINAL" COPY OF THE LENGTH
	MOVE	T4,T1		;  AND A COPY TO USE FOR ADJBP.

	HLRZ	T2,PCBPTR(U)	;GET THE PCB'S BYTE POINTER
	CAIN	T2,(POINT 8,0)	;  AND IF IT'S NOT A NEW 8 BIT POINTER
	SKIPG	T1		;  OR THE COUNT IS .LE. 0
	PUSHJ	P,NTDSTP##	;LET SOMEONE KNOW ABOUT IT.

	SOS	T1		;ROUND DOWN, AND SHIFT TO GET THE
	LSH	T1,-2		;  NUMBER OF WORDS TO BLT (LESS 1)

	HRLZ	T2,PCBPTR(U)	;GET THE ADDRESS TO START BLT'ING FROM
	HRRI	T2,6(P4)	;GET THE ADDRESS TO BLT TO
	ADDI	T1,(T2)		;ADDRESS OF LAST WORD TO BLT TO
	BLT	T2,(T1)		;TRANSFER THE ENTIRE HEADER (AND
				;  POSSIBLY A FEW EXTRA BYTES...)
	MOVSI	T1,(POINT 8)	;GET A SKELETON BYTE POINTER AND
	HRRI	T1,6(P4)	;  MAKE IT POINT TO THE OUTPUT BUFFER
	ADJBP	T4,T1		;ADVANCE IT TO ACCOUNT FOR THE DATA
				;  WE JUST PUT IN THE BUFFER.

;NOW THE "NCL HEADER" HAS BEEN COPIED, READ CONVERSION CODE AND COPY "DATA"

	LDB	T1,PCBPCV##	;GET THE CONVERSION CODE
	CAIL	T1,PCV.NC	;RANGE CHECK
	CAILE	T1,PCV.BN	;  THE CONVERSION CODE
	PUSHJ	P,NTDSTP##	;++ NETSER GAVE GARBAGE
	SKIPE	PCBCT2(U)	;IF NO SECOND BUFFER, DON'T TRY TO SEND IT
	PUSHJ	P,@[EXP C.NC,C.LP,C.BN](T1) ;CALL CONVERSION ROUTINE
	JRST	XMTXIT		;GO SWAP THE BYTES AND SEND IT
;XMTANM	ROUTINE TO SEND AN ANF-10 MAINT MESSAGE (STC BLOCK)
XMTANM:	HLRZ	T3,STCBLK(U)	;GET THE BYTE COUNT WHERE XMTXIT WANTS IT
	CAIL	T3,KDLMMS*4	;MAKE SURE THAT THE LENGTH IS REASONABLE
	PUSHJ	P,NTDSTP##	;IF NOT, TELL SOMEONE
	MOVEI	T1,6(P4)	;GET THE ADDRESS OF THE KDL BUFFER
	HRLI	T1,STCDAT(U)	;GET THE ADDRESS OF THE MAINT DATA
	MOVE	T2,T3		;GET THE LENGTH (IN BYTES)
	SOS	T2		;ROUND UP(+3), LESS 1 WD (-4)
	LSH	T2,-2		;CONVERT TO WORDS
	ADDI	T2,(T1)		;CALCULATE THE END OF THE "BLT"
	BLTBU	T1,(T2)		;COPY AND SWAP IN ONE SWELL FOOP
	JRST	XMTDON		;DONE

;NOW WE HAVE THE BUFFER FILLED, BUT WE MUST SWAP THE BYTES FOR THE
;	STUPID UBA...
;	T3 := THE NUMBER OF BYTES IN THE BUFFER
;
;DECNET MESSAGES CAN COME AS CHAINED MSDS OF ODD BYTE LENGTHS, AND
;ANF MESSAGES CAN HAVE CONVERSION.  SO UBABLT HAS TO WAIT TILL NOW, 
;WHEN THE DATA IS IN A FINAL FORM, AND THE START IS WORD-ALIGNED.

XMTXIT:	MOVEI	T1,3(T3)	;GET THE NUMBER OF BYTES, ROUND UP
	ASH	T1,-2		;CONVERT TO A -10 WORD COUNT
	ADDI	T1,6(P4)	;SKIP THE BUFFER DESCRIPTIOR, PT TO USER DATA
	MOVEI	T4,6(P4)	;T1 HAS THE END ADDR +1, T4 THE START
	HRLI	T4,0(T4)	;THIS BLT GOES IN PLACE (SET SOURCE)
	BLTBU	T4,-1(T1)	;DO THE SWAP
;HERE TO WRITE THE BUFFER DESCRIPTOR HEADER

XMTDON:	LDB	T1,KDLSTA##	;GET STATE (MAINT OR NOT) WE USE IT A LOT
	CAIN	T1,KD%MAI	;IF THIS IS A MAINT MSG, THEN
	AOSA	KDLMAX(F)	;  THEN COUNT IT.
	AOS	KDLDTX(F)	;OTHERWISE IT'S DATA
	MOVEI	T4,3(P4)	;BUILD A PRE-DECREMENTED CBP TO THE
	HRLI	T4,3		;  HEADER AREA

;HEADER
	MOVEI	S,SOH		;ASSUME THAT THIS IS A DATA MESSAGE
	CAIN	T1,KD%MAI	;  BUT IF IT TURNS OUT TO BE MAINT
	MOVEI	S,DLE		;  THEN CHANGE THE HEADER CHAR
	PUSHJ	P,IDPCB		;WRITE THE HEADER CHARACTER
;CC1
	MOVE	S,T3		;GET THE LOW 8 BITS OF THE COUNT
	ANDI	S,377		;  AND JUST THE LOW 8 BITS
	PUSHJ	P,IDPCB		;WRITE THE FIRST PART OF THE COUNT
;CC2
	MOVE	S,T3		;GET THE COUNT AGAIN
	LSH	S,-10		;  AND THIS TIME JUST THE TOP 6 BITS
	TRZE	S,777700	;MAKE SURE THE COUNT WAS REASONABLE
	PUSHJ	P,NTDSTP##	;  AND STOP IF BUFFER WAS TO BIG
M9312::	TRO	S,SELECT	;SET THE SELECT (POINT 2 POINT)
	CAIN	T1,KD%MAI	;AND IF WE ARE IN MAINT MODE,
	TRO	S,QSYNC		;SET THE QSYNC ALSO (MAKE THE DMR HAPPY)
	PUSHJ	P,IDPCB		;STORE THE UPPER-COUNT AND FLAGS
;RESP
	SETZ	S,		;ASSUME THIS IS MAINT, HENCE ZERO REPS
	CAIE	T1,KD%MAI	;  BUT IF WE ARE SENDING REAL DATA,
	LDB	S,KDLRMN##	;LOAD THE "IMPLIED ACK" NUMBER
	PUSHJ	P,IDPCB		;WRITE THE ACK FIELD
	MOVSI	S,(KDSACK)	;NO NEED TO ACK
	CAIE	T1,KD%MAI	;AT LEAST IF A DATA MESSAGE
	 ANDCAM	S,KDLSTS(F)	;BECAUSE THE RESP IS JUST AS GOOD
				;NOTE: RH OF S IS STILL ZERO
;NUM
	CAIE	T1,KD%MAI	;IF WE ARE SENDING DATA MESSAGES
	MOVE	S,MB.MSN(U)	;GET THE MESSAGE'S NUMBER
	PUSHJ	P,IDPCB		;WRITE THE MESSAGE NUMBER FIELD
;ADDR
	MOVEI	S,1		;GET OUR STATION ADDRESS (1 = P 2 P)
	PUSHJ	P,IDPCB		;  AND SEND IT
;TIMER
	SETZ	S,		;PREPARE TO START TIMER
	LDB	T1,KDLTIM##	;IF IT'S RUNNING
	CAIN	T1,KD%MXT	;WE DON'T CHANGE IT
	 DPB	S,KDLTIM##	;IT'S NOT, START IT
				;IT'S OK NOT TO CHECK MAINT - TIMER IS IGNORED
;BCC1
;BCC2
;FROM ABOVE.  AT THIS POINT, THE ENTIRE MESSAGE IS COMPLETE.
;  ALL THAT IS LEFT IS TO BUILD THE BUFFER DESCRIPTOR LIST AND
;  GIVE IT TO THE KMC.  P4 := ADDRESS OF THE BDL

	MOVEI	T4,(P4)		;GET ADDRESS OF THE BDL AREA
	TLO	T4,(POINT 18,0)	;MAKE A HALF WORD BYTE POINTER

	MOVEI	T2,(P4)		;GET ADDRESS OF THE DATA
	SUBI	T2,(F)		;GET JUST THE OFFSET WITHIN THE PAGE
	LSH	T2,2		;GET PAGE OFFSET IN BYTES
	ADD	T2,KDLADR(F)	;MAKE IT A UNIBUS ADDRESS
	MOVEI	S,20(T2)	;ADDR OF DDCMP HEADER IS 20 BYTES PAST BDL
;BDL1.1
	IORI	S,(1B0)		;MAKE SURE WE SET THE IN-USE MARK BIT
	IDPB	S,T4		;STORE -11 ADDRESS OF DDCMP HEADER
;BDL1.2
	MOVEI	S,6		;GET THE LENGTH OF THE DDCMP HEADER
	IDPB	S,T4		;WRITE THE LENGTH
;BDL1.3
	MOVEI	S,20(T2)	;GET ADDRESS OF THE HEADER AGAIN
	LSH	S,-6		;SHIFT TO GET TWO HIGH BITS OF ADDRESS
	ANDI	S,BDLXAD	;MASK TO JUST 2 BITS (6000)
	TRO	S,BDLRSY!BDLSOM!BDLEOM ;RESYNC, START AND END OF MESSAGE
	IDPB	S,T4		;WRITE THIRD WORD
;BDL2.1
	MOVEI	S,30(T2)	;GET ADDRESS AGAIN (OFFSET PAST DDCMP HEADER)
	IDPB	S,T4		;STORE LOW 16 BITS OF ADDR
;BDL2.2
	IDPB	T3,T4		;T3 STILL HAS THE LENGTH (REMEMBER...)
;BDL2.3
	MOVEI	S,30(T2)	;GET ADDR FOR NEARLY THE LAST TIME
	LSH	S,-6		;SHIFT TO GET HIGH ORDER 2 BITS
	ANDI	S,BDLXAD	;MASK TO 2 BITS (6000)
	TRO	S,BDLLDS!BDLSOM!BDLEOM ;LAST BDL, START AND END OF MESSAGE
	IDPB	S,T4		;STORE LAST DESCRIPTOR WORD

;BDL IS NOW COMPLETE.  TIME TO QUEUE IT TO KMCINP

	MOVE	W,KDLKDP(F)	;GET THE ADDRESS OF THE KDP BLOCK
	MOVE	T1,KDLINE(F)	;GET THE LINE #
	LSH	T1,10		;PUT LINE NUMBER IN THE HIGH BYTE

	HRLZ	T3,T2		;COPY THE BDL'S -11 ADDRESS
	LSH	T2,-2		;SHIFT HIGH TWO BITS DOWN
	IOR	T2,T3		;COMBINE
	AND	T2,[XWD 177777,140000] ;MASK OUT TRASH
	POP	P,U		;RESTORE THE REG USED TO HOLD BFR ADDR
	PJRST	KMCINP		;WILL SKIP IF KMC STILL RUNNING
	SUBTTL	XMTJNK - ANF-10 SPECIFIC CONVERSION/COMPRESSION ROUTINES

;C.NC	COPY. NO CONVERSION


C.NC:
	MOVE	T1,PCBPT2(U)	;GET THE BYTE POINTER FOR IT
	MOVE	T2,PCBCT2(U)	;GET THE LENGTH OF THE STRING TO MOVE
	ADD	T3,T2		;UPDATE T3 TO REFLECT IMPENDING COPY
C.NC0:	SOJL	T2,CPOPJ##	;COUNT DOWN BYTES TO COPY
	ILDB	T5,T1		;GET A BYTE
	IDPB	T5,T4		;PUT A BYTE
	JRST	C.NC0		;AND LOOP
;C.BN	COPY. BINARY CONVERSION

C.BN:	MOVE	T1,PCBCT2(U)	;GET COUNT OF SECONDARY BUFFER
	MOVE	T2,PCBPT2(U)	;GET ADDRESS OF SECONDARY BUFFER
C.BN1:	SOJL	T1,C.BN2	;LOOP OVER ALL OUTPUT BYTES
	ILDB	P1,T2		;GET FIRST 12 BIT BYTE
	LDB	S,[POINT 8,P1,31] ;GET FIRST 8 BITS (4 BITS LEFT)
	IDPB	S,T4		;  AND STORE THEM
	SOJL	T1,C.BNX	;IF WE COUNT OUT NOW, SEND LAST 4 BITS
	ILDB	P2,T2		;GET NEXT 12 BIT CHAR
	LDB	S,[POINT 4,P2,27] ;GET 4 LOW BITS FROM NEW 12 BIT BYTE
	DPB	P1,[POINT 4,S,31] ;PUT 4 HIGH BITS FROM OLD 12 BIT BYTE
	IDPB	S,T4		;  AND STORE THEM
	LDB	S,[POINT 8,P2,35] ;GET LAST 8 BITS FROM NEW BYTE
	IDPB	S,T4		;  AND GET RID OF THE LAST
	JRST	C.BN1		;LOOP OVER ALL 12 BIT BYTES

C.BNX:	DPB	P1,[POINT 4,S,31] ;HERE IF ODD NUMBER, GET LAST 4 BITS
	ANDI	S,360		;MASK TO ONLY 4 BITS
	IDPB	S,T4		;  AND WEVE MOVED ALL THE DATA

;NOW TO ADJUST "T3" TO ACCOUNT FOR THE BYTES JUST STORED

C.BN2:	MOVE	T1,PCBCT2(U)	;GET THE COUNT BACK
	IMULI	T1,^D12		;    (12 * PCBCTR) + 7
	ADDI	T1,7		;    -----------------  =  NUMBER OF 8 BIT BYTES
	LSH	T1,-3		;            8
	ADDI	T3,(T1)		;UPDATE THE COUNT
	POPJ	P,		;DONE, T4 CONTAINS UPDATED BYTE POINTER
;C.LP	COPY. LINE PRINTER CONVERSION (WITH COMPRESSION)
;
;DATA FOR LINE PRINTER IS COMPRESSED AS FOLLOWS
;	1CCCCCCC		;CCCCCCC IS A SINGLE CHARACTER
;	01XXXXXX		;XXXXXX  IS A COUNT OF BLANKS
;	001XXXXX		;XXXXX   IS A REPETITION COUNT FOR NEXT CHAR
;
;REGISTER USAGE
;	P1 := REPEAT COUNT
;	P2 := CHAR BEING REPEATED
;	P3 := NEXT CHAR (PUT HERE & COMPARED WITH P2)

C.LP:	MOVE	T1,PCBCT2(U)	;GET COUNT OF SECONDARY DATA
	MOVE	T2,PCBPT2(U)	;GET POINTER TO SECONDARY DATA

	PUSH	P,T4		;SAVE BYTE POSITION AND COUNT.  WE WILL
	PUSH	P,T3		;  HAVE TO "FIXUP" THE NCL HEADER TO ACCOUNT
				;  FOR THE LENGTH DIFFERENCE AFTER COMPRESSION.
;FROM ABOVE. NOW START COMPRESSING THE LINE-PRINTER DATA.

	SETZ	P1,		;INITIALIZE REPEAT COUNT
	SOJL	T1,C.LPX	;IF NO DATA, FIX UP MSG LENGTH ANYWAY
	ILDB	P2,T2		;PRIME THE LOOP WITH AN INITIAL CHAR

LPLOOP:	SOJL	T1,NMATCH	;HO CHARS DON'T MATCH ANY CHARS
	ILDB	P3,T2		;GET NEXT CHAR
	CAIN	P3,(P2)		;SAME AS LAST?
	AOJA	P1,LPLOOP	;IF SO, COUNT IT AND KEEP SCANNING

NMATCH:	JUMPE	P1,SINGLE	;JUMP IF THIS WAS AN UN-REPEATED CHAR
	AOJ	P1,		;FIXUP THE COUNT (WAS OFF BY 1)
	CAIN	P2," "		;WERE WE COMPRESSING SPACES?
	JRST	SPACES		;IF IT WAS SPACES, HANDLE DIFFERENTLY

CHARS:	CAIG	P1,37		;MORE CHARS THAN 1 BYTE CAN REPEAT?
	JRST	CHARX		;IF IT WILL FIT IN 1 BYTE, SEND NOW
	MOVEI	S,77		;MAXIMUM REPEAT COUNT & CHAR FLAG
	IDPB	S,T4		;WRITE THE REPEAT COUNT
	IDPB	P2,T4		;  AND NOW WRITE THE CHARACTER
	ADDI	T3,2		;ACCOUNT FOR THE TWO BYTES WRITTEN
	SUBI	P1,37		;ACCOUNT FOR 37 LESS CHARS IN REPEAT COUNT
	JRST	CHARS		;LOOP TILL REPEAT COUNT GETS SMALL ENOUGH

CHARX:	MOVEI	S,40(P1)	;GET REPEAT CHAR BYTE
	IDPB	S,T4		;STORE THE REPEAT COUNT
	TRO	P2,200		;TURN ON THE EXTEND BIT TO KEEP 8'S HAPPY
	IDPB	P2,T4		;  AND NOW THE CHARACTER
	ADDI	T3,2		;ACCOUNT FOR BOTH
	JRST	ADVNC1		;  AND GO BACK TO COMPARE LOOP

SPACES:	CAIG	P1,77		;SEE IF WE CAN FIT THIS ALL IN 1 REPEAT COUNT
	JRST	SPACEX		;JUMP IF 1 REPEAT COUNT BYTE IS SUFFICIENT
	MOVEI	S,177		;GET A "77" SPACES REPEAT BYTE
	IDPB	S,T4		;STORE THE SPACE REPEAT COUNT
	AOS	T3		;  AND ACCOUNT FOR IT
	SUBI	P1,77		;COUNT OFF THE 77 SPACES
	JRST	SPACES		;LOOP TILL COUNT GETS SMALL ENOUGH

SPACEX:	MOVEI	S,100(P1)	;GET "SPACE REPEAT" BIT AND COUNT
	JRST	ADVNCE		;WRITE THE BYTE AND GO BACK TO COMPARE LOOP

SINGLE:	MOVEI	S,200(P2)	;GET THE SINGLE CHAR BIT (AND THE CHAR)
ADVNCE:	IDPB	S,T4		;STORE THE BYTE
	AOS	T3		;  AND ACCOUNT FOR IT
ADVNC1:	MOVEI	P2,(P3)		;ADVANCE THE CHAR TO ATTEMPT A MATCH ON
	SETZ	P1,		;CLEAR THE COUNT
	JUMPGE	T1,LPLOOP	;GO BACK IF THERE ARE MORE CHARS TO DO
;	JRST	C.LPX		;IF DONE, GO FIXUP NCL HEADER (SIGH)
;C.LPX	At this point the line printer data has been compressed.
;	T3 contains the actual number of data bytes.  (P) contains
;	the number of bytes in the "NCL HEADER" portion of the
;	message. (This includes the "type" field)
;
C.LPX:	POP	P,T2		;GET LENGTH OF HEADER
	SUBM	T3,T2		;SUBTRACT TO GET LENGTH OF DATA
	ADDI	T2,1		;COUNT THE TYPE FIELD TWICE. (WE JUST
				;  SUBTRACTED IT OUT ONCE.)
	EXCH	T4,(P)		;GET A BYTE POINTER TO LAST "CNT" BYTE
	PUSHJ	P,C.DLB		;SKIP BACKWARDS OVER THE "TYPE" FIELD
	MOVEI	T1,0		;INITIALIZE LENGTH OF THE "CNT" FIELD

C.LPX1:	PUSHJ	P,C.DLB		;DECREMENT AND LOAD THE BYTE
	TRNE	S,200		;IS THE EXTENSIBLE BIT ON?
	AOJA	T1,C.LPX1	;IF STILL EXTENSIBLE, THEN STILL IN "CNT"

C.LPX2:	MOVEI	S,(T2)		;GET A COPY OF T2 (DATA BYTE COUNT)
	LSH	T2,-7		;SHIFT COUNT DOWN FOR NEXT TIME
	ANDI	S,177		;GET JUST 7 BITS WORTH
	SKIPE	T1		;BUT IF THIS ISN'T THE LAST BYTE
	TRO	S,200		;  THEN SET THE EXTENSIBLE BIT
	IDPB	S,T4		;STORE THE BYTE
	SOJGE	T1,C.LPX2	;LOOP OVER ALL OF THE "CNT" FIELD

	SKIPE	T2		;WE BETTER HAVE STORED IT ALL
	PUSHJ	P,NTDSTP##	;++ HORRIBLE BUG.

	POP	P,T4		;GET POINTER TO MESSAGE BACK AGAIN
	POPJ	P,		;DONE WITH LPT COMPRESSION
				;  T3 := LENGTH OF ENTIRE MESSAGE BUFFER

;ROUTINE TO DO A "DECREMENT AND LOAD BYTE"

C.DLB:	MOVE	S,T4		;COPY THE BYTE POINTER
	SETO	T4,		;GET A -1
	ADJBP	T4,S		;BACK IT UP
	LDB	S,T4		;GET THE BYTE
	POPJ	P,		;  AND RETURN
	SUBTTL	CHKACK -- PROCESS THE "ACK" FIELD OF CONTROL AND DATA MSGS.

;    CHKACK is a routine that scans the "awaiting ack" message queue for
;messages whose numbers are less than, or equal to the "ack" field of the
;message header pointed to by "T4".  Such messages are de-queued and returned
;to the driver via DC.IOD
;
;Called from RCV??? with:
;	F, W 	    := KDL and KDP pointers
;	T4	    := a CBP to the first byte of the message.
;		       (Set up by XMTSET)
;Returns
;	CPOPJ	ALWAYS
;
CHKACK:	LDB	T1,KDLSTA##	;GET THE DDCMP "STATE" VARIABLE
	CAIN	T1,KD%STK	;SEE IF WE ARE SENDING STACKS
	JRST	[MOVSI S,(KDSSTR!KDSSTK) ;IF WE ARE, CLEAR SEND STACK!START
		 ANDCAB S,KDLSTS(F) ;  SO WE WON'T CLOBBER NEWLY STARTED LINE
		 MOVEI	T1,KD%RUN ;GET THE "RUN" STATE VALUE
		 DPB T1,KDLSTA##;AND SET THE STATE TO "RUNNING"
		 PUSHJ P,KDLPUP	;TELL NETSER THE LINE'S UP
		 JRST CHKAC1]	;RETURN TO MAIN LINE CODE
	CAIE	T1,KD%RUN	;ARE WE "RUNNING"
	POPJ	P,		;IF NOT RUNNING, GIVE ERROR RETURN

;THE LINE IS UP.  FALL THROUGH TO PROCESS THE ACK
;FROM ABOVE

;NOW LOOP INCREMENTING "LAST MESSAGE ACKED" UNTIL IT EITHER
;  EQUALS "LAST MESSAGE SENT" OR THIS ACK'S VALUE.
;  MESSAGES THAT GET ACK'ED ARE GIVEN BACK TO NETSER

CHKAC1:	LDB	T1,BYTABL+5	;GET THE ACK FIELD OF THE MESSAGE
	LDB	T2,KDLLMA##	;GET THE LAST MESSAGE ACKED
	CAIN	T1,(T2)		;SEE IF THIS IS A "NEW" ACK
	JRST	CHKAC4		;IF WE'VE SEEN THIS ACK, CLEAR REP CNT AND EXIT
	SUBI	T1,(T2)		;GET THE DIFFERENCE BETWEEN MESSAGES
	SKIPG	T1		;CHECK FOR MESSAGE NUMBER WRAP-AROUND
	ADDI	T1,400		;IF WRAP-AROUND, CORRECT TO POSITIVE VALUE
	CAIL	T1,MAXOUT	;SEE IF THIS ACK IS IN RANGE
	POPJ	P,		;IF ACK IS OUT OF RANGE, RETURN WITH-OUT
				;  PROCESSING. (MIGHT WANT TO GIVE ERROR...)
	LDB	T1,KDLLMA##	;GET THE "LAST MESSAGE ACKED"
	ADDI	T1,1		;INCREMENT IT TO NEXT MESSAGE
	ANDI	T1,377		;MASK TO 8 BITS
	DPB	T1,KDLLMA##	;STORE THE NEW "LAST MESSAGE ACKED"
	HRRZ	T3,KDLWTA(F)	;GET ADDRESS OF NEXT MESSAGE AWAITING AN ACK
	JUMPE	T3,CHKAC2	;NOTHING ON THE WAIT ACK QUEUE, TRY OUTPUT
	HRRZ	T2,MB.NXT(T3)	;GET THE ADDRESS OF THE MESSAGE AFTER THAT
	HRRZM	T2,KDLWTA(F)	;MAKE THAT MESSAGE THE "NEXT" MESSAGE WAITING
	SOSGE	KDLCTA(F)	;SAY THAT THERE IS ONE LESS AWAITING ACK
	PUSHJ	P,NTDSTP##	;WE'VE SCREWED UP THE ACK COUNT?
	PJRST	CHKAC3		;GO RETURN THE MESSAGE TO NETSER


;HERE IF BFR IS NOT ON THE WAIT ACK QUEUE.  CHECK FOR IT ON OUTPUT WAIT

CHKAC2:	HRRZ	T3,KDLWTO(F)	;GET THE FIRST ENTRY FROM THE OUTPUT WAIT QUEUE
	SKIPN	T3		;IF THERE ISN'T ONE, THEN WE GOT AN ACK FOR
	PJSP	T1,KDLERR	;  A MSG WE NEVER SENT.  THIS IS A FATAL ERROR
	HRRZ	T2,MB.NXT(T3)	;GET THE NEXT MSG OFF THE QUEUE
	HRRZM	T2,KDLWTO(F)	;  AND MAKE IT THE FIRST AWAITING OUTPUT

;DROP THROUGH TO RETURN THE BFR
;HERE WE HAVE T3 := THE BFR THAT HAS BEEN ACKED.  FIRST MAKE SURE
;  THAT IT'S REASONABLE, THEN RETURN IT TO THE DRIVER.

CHKAC3:	MOVE	T2,MB.MSN(T3)	;GET THE DDCMP MESSAGE NUMBER
	SKIPLE	T3		;MAKE SURE WE GOT A BFR
	CAIE	T1,(T2)		;  AND IT BETTER BE THE "NEXT" ONE
	PUSHJ	P,NTDSTP##	;++ MESSAGES OUT OF ORDER.  KDPINT SCREWED UP.
	HLLZS	MB.NXT(T3)	;NO STRAY POINTERS PLEASE
	PUSH	P,T4		;PRESERVE THE POINTER TO THE MSG
	MOVEI	T1,DC.IOD	;GET THE "OUTPUT DONE" FUNCTION CODE
	PUSHJ	P,CALDVR	;  AND TELL THE DRIVER THE GOOD NEWS
	POP	P,T4		;RECOVER THE POINTER TO THE MESSAGE
	SETZ	T1,		;GET A ZERO AND USE IT TO CLEAR THE REP
	DPB	T1,KDLRPC##	;  COUNTER SINCE THINGS SEEM TO BE WORKING.
	DPB	T1,KDLTIM##	;RESTART REP TIMER
	JRST	CHKAC1		;LOOP INCREMENTING "LAST MESSAGE ACK'ED" UNTIL
				;  IT EQUALS THE "ACK" FIELD OF THE MESSAGE.


CHKAC4:	LDB	T1,KDLLMX##	;GET THE LAST MESSAGE SENT
;	LDB	T2,KDLLMA##	;(FROM ABOVE)
	CAIE	T1,(T2)		;SEE IF ANY MESSAGES ARE OUTSTANDING
	POPJ	P,		;IF SO, LEAVE REP COUNTER RUNNING
	SETZ	T1,		;GET A ZERO AND USE IT TO CLEAR THE REP
	DPB	T1,KDLRPC##	;  COUNTER SINCE NO MESSAGES OUTSTANDING
	PUSHJ	P,STPTMR	;STOP THE TIMER, THERE'S NOTHING OUTSTANDING
	POPJ	P,
	SUBTTL	GETBDL -- ROUTINE TO FIND A BDL GIVEN IT'S UNIBUS ADDRESS.

;Called from KDP?O? with:
;	F, W 	    := KDL and KDP pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the KMC-11 (Which contain
;		       the unibus address of a BDL)
;Returns
;	CPOPJ	With T4 := CBP pointing to the BDL described in P2 & P3.
;
GETBDL:	MOVE	T1,P2		;GET BUFFER DESCRIPTOR LIST ADDRESS (11 ADDR)
	MOVE	T4,P3		;GET 2 HIGH ORDER BITS
	LSH	T4,2		;POSITION THEM AS HIGH BITS IN 18 BIT ADDR
	ANDI	T1,177777	;GET LOW ORDER 16 BITS
	ANDI	T4,600000	;GET HIGH ORDER 2 BITS
	IORI	T4,(T1)		;"OR" THEM TO GET ENTIRE 18 BIT 11 ADDRESS

	SUB	T4,KDLADR(F)	;T1 NOW HAS BYTE OFFSET FROM START OF PAGE

	DPB	T4,[POINT 2,T4,15] ;MOVE THE TWO LOW ORDER BITS TO HIGH HALF
	LSH	T4,-2		;SHIFT WHOLE MESS INTO A CANONICAL BP

	TDNE	T4,[XWD 777774,^-<KDLLEN-1>] ;MAKE SURE IT MAPS TO A PAGE
	PUSHJ	P,NTDSTP##	;++ OUR MAP'S ARE SCREWED UP...

	ADDI	T4,(F)		;RELOCATE TO A PAGE ADDRESS
	POPJ	P,		;ALL DONE.  T4 := A CBP TO THE BDL
	SUBTTL	KMCINP -- ROUTINE TO QUEUE TRANSACTIONS FOR THE KMC-11

;Called with:
;	W 	    := KDP pointer
;	T1	    := XWD 0,SEL2
;	T2	    := XWD SEL4,SEL6
;Returns
;	CPOPJ	KMC-11 not running
;	CPOPJ1	Transaction described by T1 & T2 has been queued.
;		RQI has been set to request an interrupt on vector "A".
;		KDPAIV will then process the top transaction on the queue.
;
KMCINU:	KDPOFF			;ENTRY TO TURN INTERRUPTS OFF FIRST
	PUSHJ	P,KMCINP	;QUEUE THE TRANSACTION
	SKIPA			;HANDLE SKIP RETURNS PROPERLY
	AOS	(P)		;GIVE SKIP
KDPONJ:	KDPON			;RE-ENABLE INTERRUPTS
	POPJ	P,		;ALL DONE



KMCINP:	MOVE	T3,KDPIQP(W)	;GET INDEX OF NEXT ENTRY IN THE QUEUE
	LSH	T3,1		;MAKE IT AN OFFSET (ENTRYS ARE 2 WDS)
	ADDI	T3,KDPINQ(W)	;RELOCATE TO THE ADDRESS OF THE QUEUE
	MOVEM	T1,0(T3)	;STORE SEL2
	MOVEM	T2,1(T3)	;STORE XWD SEL4,SEL6
	AOS	T3,KDPIQP(W)	;ADVANCE THE "PUTTER"'S INDEX
	CAIL	T3,KDPQLN	;IF WE NEED TO WRAP AROUND, THEN
	SETZB	T3,KDPIQP(W)	;  THEN WRAP TO THE FIRST ENTRY
	CAMN	T3,KDPIQT(W)	;IS THE QUEUE FULL (PUTTER = TAKER)
	PJSP	T1,KDPERR	;  IF SO, KMC MUST BE DEAD.  CRASH IT.

	MOVEI	T3,KMCRQI	;GET RQI AND SET IT IN BSEL0
	BSIO	T3,@KDPCSR(W)	;  THIS WILL CAUSE A VECTOR "A" INTERRUPT
	RETSKP			;GOOD RETURN
;NAKTRN	- ROUTINE TO "TRANSLATE" NAKS.  SINCE NAK CODES ARE SPARSE, AND
;	WE WISH TO USE DENSE NAK COUNT TABLES, THIS ROUTINE MAPS FROM
;	NAK CODE TO TABLE INDEX
;
;CALL	MOVEI	T1,NAK-CODE
;	PUSHJ	P,NAKTRN
;RETURN	CPOPJ			;ALWAYS,  T1 := TRANSLATED NAK,
;				;  OR 0 IF UN-RECOGNIZED NAK (RANDOM NAK)
;
NAKTRN:	PUSHJ	P,SAVE1##	;WE NEED A TEMP.  SO WE USE P1
	MOVEI	T2,(T1)		;COPY THE NAK CODE
	MOVEI	T1,1		;GET FIRST GUESS AT TABLE INDEX
	MOVE	P1,[POINT 6,NAKTBL]	;GET A POINTER TO THE NAK XLATION TBL
NAKTR1:	ILDB	T3,P1		;GET THE NEXT NAK CODE
	CAIN	T3,(T2)		;DOES IT MATCH OUR NAK
	POPJ	P,		;IF IT MATCHES, RETURN. T1 HAS TABLE INDEX
	SKIPE	T3		;WAS THIS A ZERO BYTE?
	AOJA	T1,NAKTR1	;IF NOT AT END OF TABLE, KEEP LOOPING
	SETZ	T1,		;TABLE EXHAUSTED, RETURN NAK TYPE OF ZERO
	POPJ	P,		;ALL DONE

NAKTBL:	BYTE	(6)1,2,3,^D8,^D9,^D16,^D17,0	;TABLE OF NAKS
;CTOTLY	- ROUTINE TO "TALLY" CONTROL OUT TRANSACTIONS
;CALL	MOVE	P3,SEL6(U)	;CONTAINS THE ERROR CODE
;	PUSHJ	P,CTOTLY	;TALLY THE CONTROL OUT TRANSACTION
;	  ERROR			;UNKNOWN CONTROL OUT CODE
;	OK			;TRANSACTION TALLYED.
;				;  P1 := "BIT MAP"  WHICH HAS A SINGLE
;				;  BIT SET ACCORDING TO THE CODE.  THIS IS
;				;  USEFULL FOR KDPCOO AND KDPCOI.
;RETURN	"BIT MASK" IN P1

CTOTLY:	MOVEI	P1,(P3)		;COPY THE ERROR CODE FOR A WHILE
	ANDI	P1,377		;MAKE SURE WE HAVE JUST THE ERROR CODE
	CAIL	P1,6		;LOWEST ERROR CODE WE KNOW OF...
	CAILE	P1,26		;IS THIS ERROR CODE "IN RANGE"
	POPJ	P,		;  IF NOT IN RANGE, GIVE ERROR RETURN

	LSH	P1,-1		;MAKE CODES DENSE (NOT EVERY OTHER)
	MOVN	T1,P1		;COPY THE NEGATIVE OF THE CODE FOR LATER LSH
	ADDI	P1,KDLCTO-3(F)	;RELOCATE CODE TO COUNTER TABLE
	AOS	(P1)		;TALLY THE CONTROL OUT TRANSACTION

	MOVSI	P1,(1B0)	;GET A BIT SHIFTED 0 BITS
	LSH	P1,(T1)		;SHIFT IT BY "ERROR CODE" BITS
	RETSKP			;GOOD RETURN
;FREBOI	FREE AN INPUT BUFFER DESCRIPTOR LIST
;  RETURN CANONICAL BYTE POINTER TO MESSAGE IN T4
FREBOI:	PUSHJ	P,GETBDL	;GET T4 SET UP FROM P2, P3
	TLNE	T4,3		;BUFFER DESCRIPTOR BETTER START ON EVEN -10 WD
	POPJ	P,		;  IF NOT EVEN -10 ADDRESS, THEN KMC SCREWED UP
	MOVEI	S,KDSNRB	;BIT DESIGNATING NEXT RECEIVE BUFFER
	MOVEI	T1,KDLRD1(F)	;ASSUME THAT THIS SHOULD BE BUFFER #1
	TDNE	S,KDLSTS(F)	;  BUT IF IT SHOULD BE #2, THEN
	MOVEI	T1,KDLRD2(F)	;  CHANGE OUR MIND.
	CAMN	T1,T4		;MAKE SURE WE GOT THE CORRECT BUFFER BACK
	SOSGE	KDLRBC(F)	;DECREMENT THE NUMBER OF ACTIVE RECEIVE BUFFERS
	POPJ	P,		;++ EITHER WRONG BUFFER, OR TOO MANY. KILL LINE
	XORB	S,KDLSTS(F)	;TOGGLE THE NEXT BUFFER OUT BIT

	MOVSI	T1,(1B0)	;GET THE BDL IN-USE MARK BIT
	TDNN	T1,0(T4)	;  AND MAKE SURE THAT IT'S SET.
;[DBUG]	POPJ	P,		;WE'VE SCREWED UP THE BDL POINTERS?
	PUSHJ	P,NTDSTP##	;WE'VE SCREWED UP THE BDL POINTERS [DBUG]
	ANDCAM	T1,0(T4)	;CLEAR THE IN-USE BIT
	RETSKP


;RCVXNK - UTILITY ROUTINE TO SEND A NAK.  CODE IN T1
RCVXNK:	DPB	T1,KDLXNK##	;SAVE THE NAK CODE
	LDB	T1,KDLSTA##	;GET THE STATE, AND
	CAIE	T1,KD%RUN	;  IF WE ARE NOT RUNNING,
	JRST	RCVXN1		;  DON'T SEND A NAK
	MOVSI	T1,(KDSNAK)	;GET THE NEED TO NAK FLAG
	IORM	T1,KDLSTS(F)	;SET FLAG FOR XMTBUF TO SEE
	MOVSI	T1,(KDSACK)	;NO POINT IN ACKING...
	ANDCAM	T1,KDLSTS(F)	; WHEN WE'RE NAKING
RCVXN1:	PUSHJ	P,XMTBUF	;CALL XMTBUF TO TRY TO SEND THE NAK
	PJRST	RCVBUF		;TRY TO QUEUE ANOTHER RECEIVE REQUEST
;FREMAI	ROUTINE TO FREE MAINT MESSAGES
;  MAINT MESSAGES MUST BE FREED OUTSIDE THE KDPOFF/KDPON INTERLOCK WHICH
;  IS NOT POSSIBLE IN XMTMAI.
;CALL	F := KDL POINTER
;RETURN	CPOPJ

FREMAI:	LDB	T1,KDLSTA##	;GET OUR STATE, AND
	CAIE	T1,KD%MAI	;IF WE AREN'T IN MAINT STATE
	POPJ	P,		;DON'T SCREW AROUND WITH THE QUEUE!!
FREMA1:	KDPOFF			;INTERLOCK WRT XMTMAI
	HRRZ	T3,KDLWTA(F)	;GET THE NEXT MSG AWATING AN ACK
	JUMPE	T3,FREMA2	;  IF NONE, WE ARE DONE
	HRRZ	T2,MB.NXT(T3)	;GET THE "NEXT" MESSAGE
	HRRZM	T2,KDLWTA(F)	; AND MAKE IT THE NEW FIRST
	SOSGE	KDLCTA(F)	;COUNT DOWN ONE LESS MESSAGE
	PUSHJ	P,NTDSTP##	;SCREWED UP THE COUNTS...
	KDPON			;GIVE BACK THE INTERLOCK
	MOVEI	T1,DC.IOD	;SAY OUTPUT DONE
	PUSHJ	P,CALDVR	;  TO OUR FRIEND THE DRIVER
	JRST	FREMA1		;SEE IF THEY ARE ANY MORE LEFT

;HERE IF NO MORE MAINT MSGS TO FREE
FREMA2:	SKIPE	KDLCTA(F)	;MAKE SURE THE COUNT IS ZERO
	PUSHJ	P,NTDSTP##	;STOP IF COUNT IS WRONG W.R.T. QUEUES
	KDPON			;RE-ENABLE INTERRUPTS
	POPJ	P,		;  AND WE ARE DONE
	SUBTTL	ERROR ROUTINES

;KDPERR	- CALLED WHEN A KDP IS HOPELESSLY ILL.
;CALL	JSP	T1,KDPERR	;T1 CONTAINS CALLERS PC FOR DEBUGGING
;RETURN	CPOPJ			;  USUALLY TO CALLER'S CALLER

KDPERR:	MOVEM	T1,KDPCPC(W)	;SAVE THE CALLER'S PC
	MOVE	U,OPRLDB##	;WE HAD BETTER TELL SOMEONE ABOUT THIS...
	PUSHJ	P,INLMES##	;SEND THE OPERATOR A MESSAGE
	ASCIZ	/%% Fatal KMC-11 error on /
	PUSHJ	P,PRKDP		;TELL HIM WHICH KDP IT WAS.
	PUSHJ	P,INLMES##	;TELL HIM WHO NOTICED
	ASCIZ	/,  called/
	MOVE	T2,KDPCPC(W)	;GET THE CALLER'S PC BACK
	SOS	T2		;  BACK IT UP 1 LOCATION
	PUSHJ	P,PCP##		;  AND PRINT IT AS A "PC"
	PUSHJ	P,INLMES##	;FINISH OFF THE MESSAGE
	ASCIZ	/  KMC-11 Halted.
/

	PUSHJ	P,KDPHLT	;STOP IT (CLEAR RUN)
	MOVSI	T1,(DF.RQK)	;NOW REQUEST THAT KDPLDR
	IORM	T1,DEBUGF##	; ON THE NEXT CLOCK TICK
	POPJ	P,		;RETURN (LET SOME ONE ELSE CLEAN UP...)
;KDLERR	- ROUTINE CALLED WHEN AN EVENT FATAL TO ONLY ONE LINE OCCURS
;CALL	PJSP	T1,KDLERR	;T1 HAS CALLER'S PC
;RETURN	POPJ	P,		;USUALLY TO CALLER'S CALLER

KDLERR:	MOVEM	T1,KDLCPC(F)	;REMEMBER THE CALLERS PC
	MOVE	U,OPRLDB##	;GET THE OPER'S TTY POINTER
	PUSHJ	P,INLMES##	;TELL HIM THE BAD NEWS
	ASCIZ	/%% Fatal error on /
	PUSHJ	P,PRKDL		;TELL HIM WHICH LINE.
	PUSHJ	P,INLMES##
	ASCIZ	/,  called/
	MOVE	T2,KDLCPC(F)	;GET THE CALLER'S PC BACK
	SOS	T2		;BACK IT UP TO THE POINT OF CALL
	PUSHJ	P,PCP##		;PRINT THE PC
	PUSHJ	P,INLMES##	;FINISH OFF
	ASCIZ	/  Line shut down.
/

;NOW SHUT DOWN THE LINE

	PUSHJ	P,KDLFLS	;START THE BUFFERS FLUSHING

	MOVE	T1,KDLMAP(F)	;GET THE FIRST MAP LOCATION
	MOVEI	T2,KDLPPL	;PAGES PER LINE
	SETZ	T3,		;A ZERO TO ZAP THE MAP WITH
	SOJGE	T2,[WRIO T3,(T1);ZAP THE PAGE
		    AOJA T1,.]	;AND LOOP OVER THEM ALL

	PUSHJ	P,KDLPDN	;TELL THE DRIVER HIS LINE IS DOWN
	PJRST	CLBFRS		;CLEAR THE BUFFER USE BITS AND RETURN
;KDLFLS	ROUTINE TO SET THE "FLUSH IN PROGRESS" BITS, GO TO "FLUSH" STATE
;  AND QUEUE FLUSH TRANSACTIONS TO THE KMC-11
KDLFLS:	MOVEI	T1,KD%FLS	;GET THE "FLUSHING BUFFERS" STATE
	DPB	T1,KDLSTA##	;  AND USE SET OUR STATE TO THAT
	MOVEI	T1,KDSXFL!KDSRFL;GET THE "FLUSH IN PROGRESS BITS"
	IORM	T1,KDLSTS(F)	;  AND SET THEM IN THE STATUS WORD
	HRRZ	T1,KDLINE(F)	;GET OUR LINE NUMBER
	LSH	T1,10		;  AND PUT IT IN BSEL3
	MOVEI	T2,BFRKIL	;GET THE KILL BIT (LEAVE BDL ADDR = 0)
	MOVE	W,KDLKDP(F)	;JUST MAKE SURE "W" IS SET UP
	PUSHJ	P,KMCINU	;MASSACRE THE OUTPUT BUFFERS
	  PJSP T1,KDPERR	;?? KMC-11 CRASHED WHILE WE WEREN'T LOOKING ??
	IORI	T1,KMCIOT	;SET "INPUT" BUFFER BIT
	PUSHJ	P,KMCINU	;MURDER ALL THE INPUT BUFFERS AS WELL.
	  PJSP T1,KDPERR	;?? "" ??
	POPJ	P,		;ALL DONE
;KDPHLT - ROUTINE TO CRASH A KMC-11 (KDP) WHEN SOMETHING IS DRASTICALLY
;	WRONG.

KDPHLT:	PUSHJ	P,SAVE1##	;WE USE 1 P
	MOVEI	T1,KMCMCL	;GET THE MASTER CLEAR BIT
	WRIO	T1,@KDPCSR(W)	;CLEAR RUN, SET MASTER CLEAR. (DIE DIE DIE...)

	MOVSI	T1,(KDPSRU)	;GET MICROCODE RUNNING BIT
	ANDCAM	T1,KDPSTS(W)	;AND CLEAR IT SO WE WON'T TRY TO RESTART

	MOVE	T2,KDPCSR(W)	;GET ADDRESS OF THE KMC-11 CSR'S
	MOVE	T3,[POINT 18,KDPRGS(W)] ;GET A BYTE POINTER TO "CRASH REGS"
	MOVEI	T1,4		;GET COUNT OF REGISTERS TO READ

KDPHL1:	RDIO	S,(T2)		;GET KMC-11 REGISTER
	IDPB	S,T3		;  AND STORE IT IN THE KDP BLOCK FOR DEBUGGING
	ADDI	T2,2		;GO TO THE NEXT -11 WORD
	SOJG	T1,KDPHL1	;LOOP OVER ALL REGISTERS

;NOW CLEAR THE "VOLITILE" PART OF THE KDP BLOCK

	HRLI	T1,KDPZER(W)	;GET ADDR OF FIRST WORD TO ZERO
	HRRI	T1,KDPZER+1(W)	;GET ADDRESS OFF THE SECOND
	SETZM	KDPZER(W)	;CLEAR THE FIRST WORD
	BLT	T1,KDPLEN-1(W)	;CLEAR THE REST

;NOW DECLARE ALL LINES ASSOCIATED WITH THIS KDP AS CRASHED.

	MOVE	P1,KDPDPN(W)	;GET A COUNT OF THE NUMBER OF DUP-11 ATTACHED
KDPHL2:	SOJL	P1,CPOPJ##	;COUNT DOWN EACH DUP-11 PROCESSED

	MOVE	T1,P1		;GET OUR LINE NUMBER
	IMULI	T1,KDLPPL	; TIMES NUMBER OF PAGES PER LINE
	ADD	T1,KDPIMR(W)	; RELOCATE TO THE FIRST PAGE
	MOVEI	T2,KDLPPL	;GET THE NUMBER OF PAGES TO CLEAR
	SETZ	T3,		;GET A ZERO
	SOJGE	T2,[WRIO T3,(T1);ZERO THE NEXT WORD
		    AOJA T1,.]	;AND LOOP OVER ALL PAGES FOR THIS LINE

	MOVEI	F,(P1)		;COPY OFFSET INTO LINE TABLE
	ADDI	F,KDPKDL(W)	;RELOCATE OFFSET TO LINE TABLE
	SKIPN	F,(F)		;LOAD ADDRESS OF KDL BLOCK
	JRST	KDPHL2		;ONCE SAID THERE WASN'T ONE, LOOP OVER REST

	LDB	T1,KDLSTA##	;GET THE STATE OF THIS LINE
	MOVEI	T2,KD%DWN	;GET THE "DOWN" STATE
	DPB	T2,KDLSTA##	;  AND MARK THIS LINE AS HOPELESS
	CAILE	T1,KD%FLS	;WAS LINE "ALIVE"
	PUSHJ	P,KDLPDN	;  IF IT WAS UP, REQUEUE PCB'S AND CALL NETSER

	JRST	KDPHL2		;LOOP OVER ALL LINES
	SUBTTL	PROTOCOL CONTROL SUBROUTINES

;KDLPUP	- ROUTINE TO TELL OUR DRIVER THAT PROTOCOL IS UP

KDLPUP:	PUSH	P,T4		;CALLER EXPECTS US TO SAVE T4
	MOVEI	T1,DC.IPU	;GET THE PROTOCOL "UP" FUNCTION
	PUSHJ	P,CALDVR	;  AND TELL THE DRIVER THE GOOD NEWS
	POP	P,T4		;RESTORE CALLER'S T4
	POPJ	P,		;AND WE'RE DONE


;KDLPDN	- ROUTINE TO RETURN ALL UNSENT BFR'S AND THEN DECLARE THE
;	LINE DOWN
;	HERE EITHER FROM CLOCK LEVEL (TIMED OUT), OR FROM RECEIVE
;	INTERRUPT LEVEL (MAINT/START MSG), OR CONTROLLER REQUEST
;	FROM A DRIVER (ROUTER KF.IN OR KF.HA)

KDLPDN:	PUSHJ	P,KDLPD0	;EMPTY THE QUEUES FIRST
;HERE WHEN WE'VE RETURNED ALL THE MESSAGES.  DECLARE PROTOCOL DOWN
	MOVEI	T1,DC.IPD	;GET THE "PROTOCOL DOWN" FUNCTION
	PUSHJ	P,CALDVR	;TELL THE DRIVER THE BAD NEWS
	PUSHJ	P,CLCTRS	;CLEAR THE DDCMP MESSAGE NUMBER COUNTERS
	POPJ	P,		;AND WE'RE DONE


KDLPD0:	KDPOFF			;NO INTERRUPTS WHILE HACKING QUEUES
	HRRZ	T3,KDLWTA(F)	;GET THE FIRST BFR ON THE WTA QUEUE
	JUMPE	T3,KDLPD1	;IF QUEUE IS EMPTY, TRY WTO
	HRRZ	T2,MB.NXT(T3)	;GET THE "NEXT" BFR ON THE QUEUE
	HRRZM	T2,KDLWTA(F)	;  AND MAKE IT THE FIRST
	SOSGE	KDLCTA(F)	;COUNT DOWN ONE LESS WTA BFR
	PUSHJ	P,NTDSTP##	;IF IT WENT NEGATIVE, DUMP IT.
	JRST	KDLPD2		;GO RETURN THE BUFFER

;HERE IF THE WTA QUEUE IS EMPTY, TRY WTO
KDLPD1:	SKIPE	KDLCTA(F)	;MAKE SURE THE WTA QUEUE COUNTED OUT RIGHT
	PUSHJ	P,NTDSTP##	;STOP IF COUNT AND QUEUE DON'T AGREE
	HRRZ	T3,KDLWTO(F)	;GET THE FIRST BFR ON THE WTO QUEUE
	JUMPE	T3,KDLPD3	;IF NONE, WE'VE RETURNED ALL THE BFRS
	HRRZ	T2,MB.NXT(T3)	;GET THE "SECOND" BUFFER ON THE WTO QUEUE
	HRRZM	T2,KDLWTO(F)	;AND MAKE IT THE FIRST BUFFER

;HERE TO RETURN A MESSAGE AS "OUTPUT NOT DONE"
KDLPD2:	KDPON			;WE'VE GOT OUR BFR, RE-ENABLE INTERRUPTS
	MOVEI	T1,DC.IOF	;GET THE "OUTPUT NOT DONE" FUNCTION
	PUSHJ	P,CALDVR	;AND TELL OUR DRIVER THE NEWS
	JRST	KDLPD0		;LOOP TILL WE HAVE RETURNED ALL BFRS

KDLPD3:	KDPON			;RE-ENABLE INTERRUPTS
	POPJ	P,		;AND RETURN
;KDLINI	- ROUTINE TO INITIALIZE A KDL-PAGE
;CALL	MOVX	F,KDL-BLOCK ADDRESS
;	PUSHJ	P,KDLINI	;FROM CLOCK OR UUO LEVEL
;RETURN	POPJ	P,		;ALWAYS
;	THIS ROUTINE RE-BUILDS A KDL BLOCK. IN PARTICULAR IT:
;	   A)	SETS UP ALL IMPORTANT "STATIC" LOCATIONS IN THE KDL PAGE
;	   B)	MAPS THE KDL PAGE SO AS TO MAKE IT ACCESSIBLE TO THE KMC-11
;	   C)	QUEUES THE BASE IN AND CONTROL IN TRANSACTIONS NECESSARY
;		TO LET THE LINE SEND AND RECEIVE MSGS

KDLINI:	MOVEI	T1,KDPMAX##	;FIRST FIND THE KDP THAT OWNS THIS KDL
KDLIN1:	SKIPGE	T1		;IF WE COULDN'T FIND ONE
	PUSHJ	P,NTDSTP##	; THEN A DRIVER GAVE US A BAD KDL ADDRESS
	HRRZ	W,KDPTBL##(T1)	;ASSUME THAT THIS IS THE RIGHT KDP
	HRRZ	T2,KDPDPN(W)	;GET THE NUMBER OF DUPS ON THIS ONE
	MOVEI	T3,KDPKDL(W)	;GET THE ADDRESS OF THE DUP TABLE
KDLIN2:	SOJL	T2,[SOJA T1,KDLIN1] ;IF NO MORE DUPS, DO NEXT KDP
	HRRZ	T4,(T3)		;GET THE RH OF THE NEXT ENTRY
	CAIE	T4,(F)		;SEE IF THIS IS THE ONE
	AOJA	T3,KDLIN2	;IF NOT, KEEP LOOKING

	SETZM	KDLFZR(F)	;START CLEARING AFTER THE BLOCK POINTERS
	HRLI	T3,KDLFZR(F)	;FIRST HALF OF "BLT" POINTER
	HRRI	T3,KDLFZR+1(F)	;SECOND HALF OF "BLT" POINTER
	BLT	T3,KDLLEN-1(F)	;CLEAR THE BLOCK

	HRRZM	W,KDLKDP(F)	;STORE THE KDP ADDRESS

	MOVE	T1,KDPDPN(W)	;GET THE NUMBER OF DUPS ON THIS KMC
	SUBI	T1,1(T2)	;GET THE NUMBER OF THIS ONE
	MOVEM	T1,KDLINE(F)	;GET OUR LINE NUMBER
	IMULI	T1,KDLPPL	;OFFSET BY THE NUMBER OF PAGES PER LINE
	ADD	T1,KDPIMR(W)	;CALCULATE OUR MAP REGISTER'S ADDRESS
	MOVEM	T1,KDLMAP(F)	;STORE THE MAP REGISTER'S ADDRESS

	MOVEI	T1,KDPKDL(W)	;GET ADDR OF FEK TO KDL CORRISPONDENCE TBL
	ADD	T1,KDLINE(F)	; THAT LIVES IN THE KDP BLOCK
	HLRZ	T1,(T1)		;GET OUR FEK ADDRESS (IF RUNNING ANF)
	MOVEM	T1,KDLFEK(F)	; AND STORE THAT

	MOVEI	T1,M.DN60##	;SEE IF DN60 WAS LOADED
	JUMPE	T1,KDLIN3	; IF NOT, THEN THERE IS NO DLX FOR THIS LINE
	MOVE	T1,KDLINE(F)	;IF THERE IS A DLX, GET THE LINE NUMBER
	MOVE	T1,KDPBAS##(T1)	; AND FROM THAT, THE DLX TABLE ADDRESS
KDLIN3:	MOVEM	T1,KDLDLX(F)	;AND STORE A POINTER TO THE DLX BLOCK
	MOVE	T1,KDLINE(F)	;GET OUR LINE NUMBER BACK
	IMULI	T1,KDLPPL	;MULTIPLY BY THE NUMBER OF PAGES PER LINK
	LSH	T1,^D11		;CONVERT PAGE OFFSET TO BYTE OFFSET
	ADD	T1,KDPIEA(W)	;CALCULATE OUR PAGE ADDRESS
	MOVEM	T1,KDLADR(F)	;STORE OUR PAGE'S -11 ADDRESS

	MOVE	T1,KDLINE(F)	;GET OUR DUP-11'S NUMBER AGAIN
	LSH	T1,3		;MULTIPLY BY THE SIZE OF A DUP-11 (IE. 8)
	ADD	T1,KDP1DP(W)	;RELOCATE BY ADDRESS OF THE FIRST DUP-11
	MOVEM	T1,KDLCSR(F)	;STORE OUR CSR ADDRESS (FOR BASE IN)

;KDL PAGE NOW INITIALIZED.  TIME TO SET UP THE MAP.

	MAP	T1,(F)		;GET THE PHYSICAL ADDRESS OF THE KDL PAGE.
	AND	T1,[XWD 17,-1]	;GET JUST THE ADDRESS
	LSH	T1,W2PLSH##	;CHANGE ADDRESS TO PAGE NUMBER
	IORI	T1,UNBVBT!UNBD18;VALID & 18 BIT DISABLE
	MOVEI	T2,KDLPPL	;GET A COUNT OF THE NUMBER OF PAGES PER LINE
	MOVE	T3,KDLMAP(F)	;GET THE ADDRESS OF THE MAP
	SOJGE	T2,[WRIO T1,(T3);WRITE THE PAGE REGISTER
		    AOS T1	;GET READY TO WRITE THE NEXT ONE
		    AOJA T3,.]	;LOOP OVER ALL REGISTERS

	HRRZ	T1,KDLINE(F)	;GET LINE NUMBER FOR "BASE IN"
	LSH	T1,10		;SHIFT INTO UPPER BYTE
	IORI	T1,BASEIN	;SET THE FUNCTION CODE
	HRRZ	T2,KDLCSR(F)	;GET THE DUP-11'S CSR ADDRESS (-11 MEMORY)
	ANDI	T2,17777	;  BUT ONLY USE LOW 13 BITS OF IT.
	PUSHJ	P,KMCINU	;QUEUE BASE-IN TRANSACTION
	JSP	T1,KDPERR	;?? KMC-11 CRASHED ?? GIVE KMC NOT RUNNING ??

	TRZ	T1,2		;CHANGE BASE-IN FUNCTION TO CONTROL-IN (UGLY)
	MOVEI	T2,CDDCMP!CENABL;SET POLLING = 0, DDCMP MODE, AND ENABLE
	PUSHJ	P,KMCINU	;QUEUE THE CONTROL-IN TRANSACTION
	JSP	T1,KDPERR	;?? KMC-11 CRASHED ?? GIVE KMC NOT RUNNING ??
	POPJ	P,		;KDL HAS BEEN INITIALIZED
;CALDVR	ROUTINE TO CALL THIS LINE'S DRIVER
;CALL	F := POINTER TO A KDL
;	SETS UP T2 := DRIVER'S BLOCK ADDRESS
;	AND DISPATCHES TO THE DRIVER ROUTINE
;DON'T PRESERVE F/W SINCE OUR DRIVER SHOULDN'T CLOBBER THEM!

DEFINE	X(TYP,CON,DAT,ADR),<
	IFN	.-CALDV0-<2*DD.'TYP>,<
		PRINTX	? CALDVR vector dispatch phase error for TYP user>
	IFE	CON,<
		PUSHJ	P,NTDSTP##	;;DIE IF NO DRIVER SUPPORTED
		HALT	.>		;;FILLER FOR TWO-WORD VECTOR
	IFN	CON,<
		MOVE	T2,DAT(F)	;;GET "DRIVER" DATA BLOCK ADDRESS
		PJRST	ADR>		;;AND GO TO APPROPRIATE DRIVER
> ;END DEFINE X

CALDVR:	PUSHJ	P,SAVR##	;DECnet USES R AS A TRASH AC.
	HRRZ	T2,KDLUSR(F)	;GET THE USER CODE
	CAILE	T2,DD.MAX	; AND RANGE CHECK IT
	PUSHJ	P,NTDSTP##	;++ ILLEGAL USER CODE
	LSH	T2,1		;TRANSLATE INTO TWO-WORD BLOCK OFFSET
	PJRST	.+1(T2)		;DISPATCH BASED ON "USER" TYPE

CALDV0:	X	(NOBODY,1,0,NOBDSP##)
CALDV1:	X	(ANF10,FTNET,KDLFEK,D8KK2D##)
CALDV2:	X	(DECNET,FTDECN,KDLLBK,CALPPI)
CALDV3:	X	(PROGRAM,1,KDLDDB,KDPIDS##)
CALDV4:	X	(IBMCOMM,FTDN60,KDLDLX,D6KK2D##)

	IFN	.-CALDV0-<2*<DD.MAX+1>>,<
		PRINTX	? CALDVR vector dispatch entry missing>

IFN FTDECN,<
CALPPI:	DNCALL	KDIPPI##	;CALL DECNET IN DECNET CONTEXT
	  POPJ	P,		;AND RETURN
	PJRST	CPOPJ1##	;...
>; END IFN FTDECN
;CLCTRS	CLEAR THE VARIOUS COUNTERS ASSOCIATED WITH A LINE
CLCTRS:	SETZ	T1,		;GET A ZERO
	DPB	T1,KDLTIM##	;CLEAR THE TIMER
	DPB	T1,KDLRMN##	;CLEAR THE RECEIVE MESSAGE NUMBER
	DPB	T1,KDLLMX##	;CLEAR LAST MESSAGE SENT
	DPB	T1,KDLLMA##	;CLEAR LAST MESSAGE ACKED
	DPB	T1,KDLRPC##	;CLEAR THE REP COUNTER
	POPJ	P,


;CLBFRS	ROUTINE TO SET ALL BUFFERS TO "EMPTY".  USED WHEN GOING INTO
;  MAINT MODE.
CLBFRS:	MOVEI	T1,KDSNRB!KDSNXB!KDSRFL!KDSXFL ;STATUS BITS TO CLEAR
	ANDCAM	T1,KDLSTS(F)	;MAKE SURE NEXT BUFFER IS FIRST BUFFER.
	HRLI	T1,KDLXBC(F)	;START ZEROING HERE
	HRRI	T1,KDLXBC+1(F)	; ...
	SETZM	KDLXBC(F)	;WRITE THE "SEED" ZERO
	BLT	T1,KDLLEN-1(F)	;  AND ZAP THE REST OF THE BLOCK
	POPJ	P,		;RETURN WITH ALL BUFFERS AND USE BITS CLEAR
;STPTMR ROUTINE TO STOP THE DDCMP TIMER

STPTMR:	SETO	T1,		;STOP TIMER
	HRRZ	T2,KDLUSR(F)	;CHECK FOR ANF CROCK
	CAIN	T2,DD.ANF	;BECAUSE IT WANTS TO
	 SETZ	T1,		;IDLE REPS, SO WE JUST RESTART TIMER
	DPB	T1,KDLTIM##	;WHATEVER
	POPJ	P,


;ZRCTRS	- ROUTINE TO ZERO THE COUNTER SECTION OF THE KDL BLOCK
;CALL	MOVX	F,KDL-BLOCK ADDRESS
;	PUSHJ	P,ZRCTRS
;RETURN	CPOPJ			;ALWAYS

ZRCTRS:	HRLI	T1,KDLZER(F)	;NOW ZERO COUNTER SECTION OF THE KDL (RESTART)
	HRRI	T1,KDLZER+1(F)	;GET ADDRESS OF SECOND WORD ...
	SETZM	KDLZER(F)	;CLEAR THE FIRST ONE
	BLT	T1,KDLLEN-1(F)	;CLEAR THE REST
	POPJ	P,		;AND WE'RE DONE
	SUBTTL	PRINT ROUTINES

;PRKDP	- ROUTINE TO PRINT OUT "KDP #?"

PRKDP:	PUSHJ	P,INLMES##	;PRINT OUT THE ASCII PART
	ASCIZ	/KDP #/
	MOVE	T1,KDPNUM(W)	;GET THE KDP'S NUMBER
	PJRST	PRTDIG##	;  AND PRINT THAT

;PRKDL	- ROUTINE TO PRINT "KDP #?,  KDL #?"

PRKDL:	PUSHJ	P,PRKDP		;PRINT OUT THE FIRST PART
	PUSHJ	P,INLMES##	;PRINT OUT THE ",  KDL #"
	ASCIZ	/,  KDL #/
	MOVE	T1,KDLINE(F)	;GET OUR DUP-11 LINE NUMBER
	PJRST	PRTDIG##	;  AND PRINT THAT.





;ROUTINE TO SAVE F & W FOR THE KDP??? ROUTINES

SAVEFW:	EXCH	F,(P)		;SAVE F, GET PC
	PUSH	P,W		;SAVE W
	PUSH	P,S		;SAVE S TOO...
	MOVEM	F,1(P)		;PUT PC IN A "SAFE" PLACE
	MOVE	F,-2(P)		;GET F BACK
	PUSHJ	P,@1(P)		;CALL CALLER BACK.
	  SKIPA			;NON SKIP RETURN
	AOS	-3(P)		;PROPAGATE THE SKIP RETURN
	POP	P,S		;GET S BACK
	POP	P,W		;RESTORE W
	POP	P,F		;RESTORE F
	POPJ	P,		;RETURN
;ROUTINES TO DO "CANONICAL" BYTE POINTER MANIPULATION
;BECAUSE THE BYTES TO/FROM THE UBA ARE SWAPPED NORMAL PDP-10 BYTE
;  INSTRUCTIONS DON'T WORK.  HENCE THE "CANONICAL" -11 BYTE POINTER
;  AND THESE ROUTINES.
;THE FORMAT OF THE BYTE POINTER IS:
;
;	XWD	BYTE-POS,ADDR
;
;WHERE
;
;	BYTE-POS = {0, 1, 2, 3}	;THE POSITION OF THE BYTE IN THE -10 WORD
;	ADDR = THE 10 ADDRESS OF THE WHOLE WORD
;

;HERE IS A TABLE OF BYTE POINTERS TO THE FOUR -11 BYTES IN A -10 WORD

BYTABL:	POINT	8,(T4),17	;FIRST BYTE
	POINT	8,(T4),9	;SECOND BYTE
	POINT	8,(T4),35	;THIRD BYTE
	POINT	8,(T4),27	;FOURTH BYTE
	POINT	8,1(T4),17	;FIFTH BYTE
	POINT	8,1(T4),9	;SIXTH BYTE
	POINT	8,1(T4),35	;SEVENTH BYTE
	POINT	8,1(T4),27	;EIGHTH BYTE
;IDPCB	- ROUTINE TO INCREMENT AND DEPOSITE CANONICAL BYTE
;CALL	MOVEI	S,BYTE		;THE BYTE TO STORE
;	MOVE	T4,BP		;A CANONICAL BYTE POINTER AS ABOVE
;	PUSHJ	P,IDPCB		;DO THE DEPOSIT BYTE FUNCTION
;
;CLOBBERS NO REGISTERS

IDPCB:	PUSH	P,T1		;GET A TEMP
	HLRZ	T1,T4		;GET THE POSITION FIELD
	AOS	T1		;INCREMENT THE POSITION
	CAIL	T1,4		;SEE IF WE HAVE GONE TO NEXT WORD YET
	JRST	[AOS T4		;IF NEXT WORD, INCREMENT ADDRESS
		 SETZ T1,	;  AND SET BYTE POSITION TO ZERO
		 JRST .+1]	;BACK TO MAIN FLOW
	HRLM	T1,T4		;PUT THE UPDATED POSITION BACK
	DPB	S,BYTABL(T1)	;STORE THE BYTE
	POP	P,T1		;RESTORE T1
	POPJ	P,		;RETURN (NOT "JRST TPOPJ" TO SAVE TIME)


;ILDCB	- ROUTINE TO INCREMENT AND LOAD CANONICAL BYTE
;CALL	MOVE	T4,BP		;A CANONICAL BYTE POINTER AS ABOVE
;	PUSHJ	P,IDPCB		;DO THE DEPOSIT BYTE FUNCTION
;RETURN	CPOPJ			; S := REQUESTED BYTE
;CLOBBERS NO REGISTERS (EXCEPT S)

ILDCB:	PUSH	P,T1		;GET A TEMP
	HLRZ	T1,T4		;GET THE POSITION FIELD
	AOS	T1		;INCREMENT THE POSITION
	CAIL	T1,4		;SEE IF WE HAVE GONE TO NEXT WORD YET
	JRST	[AOS T4		;IF NEXT WORD, INCREMENT ADDRESS
		 SETZ T1,	;  AND SET BYTE POSITION TO ZERO
		 JRST .+1]	;BACK TO MAIN FLOW
	HRLM	T1,T4		;PUT THE UPDATED POSITION BACK
	LDB	S,BYTABL(T1)	;FETCH THE BYTE
	POP	P,T1		;RESTORE T1
	POPJ	P,		;RETURN (NOT "JRST TPOPJ" TO SAVE TIME)

;DLDCB	- ROUTINE TO DECREMENT AND LOAD CANONICAL BYTE
;CALL	MOVE	T4,BP		;A CANONICAL BYTE POINTER
;	PUSHJ	P,DLDCB		;DECREMENT AND LOAD BYTE
;RETURN	CPOPJ			;WITH S := THE BYTE
;
;CLOBBERS NO REGISTERS (EXCEPT "S" OF COURSE)

DLDCB:	HLRZ	S,T4		;GET THE BYTE "POSITION"
	SOJL	S,[SOS T4	;DECREMENT POSITION AND ADDR IF NECESSARY
		   MOVEI S,3	;IF BACKING UP, SET BP TO 4TH BYTE
		   JRST .+1]	;BACK TO MAIN STREAM
	HRLM	S,T4		;PUT THE "POSITION" BACK IN THE BP
	ADDI	S,BYTABL	;INDEXING DOESN'T WORK FOR "S"
	LDB	S,@S		;GET THE REQUESTED BYTE
	POPJ	P,		;RETURN
;	XLIST
;	$LIT			;DON'T LIST LITERALS
	LIST
KDPEND::END