Google
 

Trailing-Edge - PDP-10 Archives - BB-F492Z-DD_1986 - 10,7/703mon/kniser.mac
There are 8 other files named kniser.mac in the archive. Click here to see a list.
	TITLE	KNISER - KLNI DEVICE DRIVER	V41
	SUBTTL	WILLIAM C. DAVENPORT/WXD	7 JAN 86


	SEARCH	F,S,ETHPRM,KNIPRM,ICHPRM,MACSYM
	T20SYM
	$RELOC
	$HIGH

;THIS MODULE IMPLEMENTS THE KLNI INTERFACE DRIVER FOR ETHERNET

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.

.CPYRT<1985,1986>
;COPYRIGHT (C) 1985,1986 BY
;DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
;ALL RIGHTS RESERVED.


	XP	VKNISR,41	;KNISER VERSION NUMBER


KNISER::!ENTRY	KNISER		;LOAD IF LIBRARY SEARCH
	SUBTTL	TABLE OF CONTENTS


;               TABLE OF CONTENTS FOR KNISER
;
;
;                        SECTION                                   PAGE
;    1. TABLE OF CONTENTS.........................................   2
;    2. KLNI DEVICE SERVICE
;         2.1   AUTCON PARAMETERS.................................   3
;         2.2   KLNI CONFIGURATION................................   4
;         2.3   KLNI INITIALIZATION...............................   5
;         2.4   ONCE A TICK CODE..................................   6
;         2.5   ONCE A SECOND CODE................................   7
;         2.6   SET MEMORY OFFLINE SUPPORT........................   9
;         2.7   REMOVE CPU SUPPORT................................  11
;         2.8   CPU OFFLINE SUPPORT...............................  12
;         2.9   RESET UUO.........................................  13
;    3. KLNI USER SERVICE
;         3.1   ETHSER FUNCTION DISPATCH..........................  14
;         3.2   SET KONTROLLER ETHERNET ADDRESS...................  16
;         3.3   READ AND CLEAR KONTROLLER COUNTERS................  18
;         3.4   READ AND CLEAR PORTAL COUNTERS....................  22
;         3.5   ENABLE PROTOCOL...................................  25
;         3.6   DISABLE PROTOCOL..................................  28
;         3.7   ENABLE MULTI-CAST ADDRESS.........................  31
;         3.8   DISABLE MULTI-CAST ADDRESS........................  33
;         3.9   RECEIVE DATAGRAM..................................  35
;         3.10  TRANSMIT DATAGRAM.................................  37
;         3.11  INTERRUPT ETHSER..................................  39
;         3.12  GENERATE KLNI COMMAND BUFFER......................  40
;    4. KLNI DEVICE SERVICE
;         4.1   PORT CONTROL BLOCK INITIALIZATION.................  41
;         4.2   PORT CONTROL BLOCK RESET..........................  42
;         4.3   ADD PROTOCOL TYPE TO PTT TABLE....................  43
;         4.4   DELETE PROTOCOL TYPE FROM PTT TABLE...............  44
;         4.5   ADD MULTI-CAST ADDRESS TO MCAT TABLE..............  45
;         4.6   DELETE MULTI-CAST ADDRESS FROM MCAT TABLE.........  46
;         4.7   UPDATE KLNI COUNTERS AREA.........................  47
;         4.8   GENERATE BSD CHAIN................................  48
;         4.9   RELEASE A BSD CHAIN...............................  52
;         4.10  ALLOCATE KLNI COMMAND BUFFER......................  53
;         4.11  RELEASE KLNI COMMAND BUFFER.......................  54
;         4.12  QUEUE KLNI COMMAND TO FREE QUEUE..................  55
;         4.13  QUEUE KLNI COMMAND TO COMMAND QUEUE...............  56
;         4.14  MISCELLANEOUS.....................................  57
;    5. KLNI INTERRUPT SERVICE
;         5.1   INTERRUPT DISPATCH................................  58
;         5.2   PROCESS RESPONSE QUEUE............................  59
;         5.3   FREE QUEUE ERROR..................................  60
;         5.4   CRAM PARITY ERROR.................................  61
;         5.5   MBUS ERROR........................................  64
;         5.6   EBUS PARITY ERROR.................................  65
;         5.7   DATA PATH ERROR...................................  66
;         5.8   KLNI ERROR STOP PROCESSING........................  67
;         5.9   SPEAR ERROR LOGGING...............................  68
;    6. KLNI MAINTENANCE SERVICE
;         6.1   DIAG. UUO.........................................  70
;         6.2   KNIBT. UUO........................................  76
;         6.3   SHUT DOWN KLNI MICROCODE..........................  83
;         6.4   STOP KLNI MICROCODE...............................  84
;         6.5   PROCESS KLNI QUEUE................................  85
;         6.6   START KLNI MICROCODE..............................  86
;         6.7   DISABLE KLNI MICROCODE............................  88
;         6.8   ENABLE KLNI MICROCODE.............................  89
;         6.9   INITIALIZE KLNI MICROCODE.........................  90
;         6.10  READ LATCHED ADDRESS REGISTER.....................  96
;         6.11  READ CRAM CONTENTS................................  97
;         6.12  WRITE CRAM CONTENTS...............................  98
;         6.13  REQUEST MICROCODE RELOAD..........................  99
;    7. QUEUE MANIPULATION
;         7.1   INITIALIZE QUEUE HEADER........................... 100
;         7.2   FIX A QUEUE....................................... 101
;         7.3   REMOVE AN ENTRY FROM QUEUE........................ 102
;         7.4   INSERT AN ENTRY INTO QUEUE........................ 103
;    8. BYTE POINTERS............................................. 104
;    9. THE END................................................... 105
	SUBTTL	KLNI DEVICE SERVICE  --  AUTCON PARAMETERS


KNIPCB==:BYTE (9) .PBLEN, .PBPCB, .PBPCL, 0 ;BYTE (9) LENGTH OF PCB, OFFSET TO
				; PHYSICALLY CONTIGUOUS AREA, LENGTH OF
				; PHYSICALLY CONTIGUOUS AREA
KNIBTS==CI.CPE!CI.MER!CI.EPE!CI.FQE!CI.RQA ;BITS TO TEST FOR ON INTERRUPT

CO.BTS==CO.MRN			;BITS WHICH MUST BE ON FOR ALL CONOS
	SUBTTL	KLNI DEVICE SERVICE  --  KLNI CONFIGURATION


;ROUTINE CALLED WHEN AUTCON DETECTS A KLNI ON THIS CPU
;LINKAGE:
;	T1/ PORT CONTROL BLOCK ADDRESS
;	P1/ CHANNEL DATA BLOCK ADDRESS
;	PUSHJ	P,KNICFG
;RETURNS:
;	CPOPJ ALWAYS

KNICFG::PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,T1		;GET PORT CONTROL BLOCK ADDRESS
	MOVEM	P1,.PBCDB(Q3)	;SAVE ADDRESS OF CHANNEL DATA BLOCK IN PCB
	MOVE	T1,[KNIBTS]	;BITS TO TEST FOR ON INTERRUPT
	MOVEM	T1,CHNBTS##(P1)	;SET IN CHANNEL DATA BLOCK
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  KLNI INITIALIZATION


;ROUTINE CALLED TO INITIALIZE THE KLNI ON THIS CPU
;LINKAGE:
;	PUSHJ	P,KNIINI
;RETURNS:
;	CPOPJ ALWAYS

KNIINI::PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	PUSHJ	P,SAVE1##	;SAVE P1
	SE1ENT			;RUN IN SECTION 1
	SETZB	Q1,Q2		;START CLEAN
	SKIPE	P1,.CPCHN##+KNIICH ;GET ADDRESS OF CHANNEL DATA BLOCK
	SKIPN	Q3,CHNPCB##(P1)	;GET THE PORT CONTROL BLOCK ADDRESS
	POPJ	P,		;NO KLNI ON THIS CPU
	SKIPE	.CPNPB##	;RESTART?
	JRST	[PUSHJ	P,SHTKNI ;YES, SHUT DOWN KLNI
		 PUSHJ	P,PCBINI ;RESET THE PORT CONTROL BLOCK
		 JRST	RLDKNI]	;REQUEST KLNI RELOAD AND RETURN
	PUSHJ	P,PCBINI	;INITIALIZE THE PCB
	MOVE	T1,Q3		;GET ADDRESS OF PORT CONTROL BLOCK
	XMOVEI	T2,KNUSER	;AND ADDRESS OF OUR DISPATCH ROUTINE
	MOVSI	T3,.KTKNI	;GET KONTROLLER TYPE AND NUMBER
	PUSHJ	P,ETKINI##	;CREATE AN ETHERNET KONTROLLER BLOCK
	  STOPCD CPOPJ##,DEBUG,KNICCK ;++CAN'T CREATE ETHERNET KONTROLLER BLOCK
	MOVEM	T1,.PBEKB(Q3)	;LINK KONTROLLER BLOCK TO PORT CONTROL BLOCK
	MOVEM	Q3,.CPNPB##	;SAVE PCB ADDRESS IN CDB

	PJRST	RLDKNI		;REQUEST RELOAD OF KLNI AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  ONCE A TICK CODE


;ROUTINE CALLED ONCE A TICK ON ALL CPUS
;LINKAGE:
;	PUSHJ	P,KNITIC
;RETURNS:
;	CPOPJ ALWAYS

KNITIC::SKIPN	.CPNPB##	;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN
	PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,.CPNPB##	;GET ADDRESS OF PORT CONTROL BLOCK
	MOVE	T1,.PBSTS(Q3)	;GET CURRENT KLNI STATUS
	TXNE	T1,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	POPJ	P,		;YES, RETURN NOW
	TXNE	T1,PBSRUN	;IS KLNI RUNNING?
	TXNN	T1,PBSQIO	;YES, ANY QUEUED I/O REQUESTS?
	POPJ	P,		;NO, RETURN
	MOVX	T1,PBSQIO	;CLEAR QUEUED I/O REQUEST FLAG
	ANDCAM	T1,.PBSTS(Q3)	;...
	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	CONO	KNI,CO.CQA!CO.BTS(T1) ;SET COMMAND QUEUE AVAILABLE FLAG
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  ONCE A SECOND CODE


;ROUTINE CALLED ONCE A SECOND ON ALL CPUS
;LINKAGE:
;	PUSHJ	P,KNISEC
;RETURNS:
;	CPOPJ ALWAYS

KNISEC::SKIPN	.CPNPB##	;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN
	PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,.CPNPB##	;GET ADDRESS OF PORT CONTROL BLOCK
	MOVE	T1,.PBSTS(Q3)	;GET CURRENT KLNI STATUS
	TXNE	T1,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	POPJ	P,		;YES, RETURN NOW
	TXNE	T1,PBSRUN	;KLNI RUNNING?
	JRST	KNISE1		;YES, GO PERFORM KEEP ALIVE CHECKS
	TXNE	T1,PBSINI	;NEED TO INITIALIZE KLNI?
	JRST	[MOVX	T1,PBSINI ;YES, CLEAR INITIALIZE KLNI FLAG
		 ANDCAM	T1,.PBSTS(Q3) ;...
		 PUSHJ	P,PCBINI ;INITIALIZE PORT CONTROL BLOCK
		 PJRST	RLDKNI]	;REQUEST KLNI RELOAD AND RETURN
	TXNN	T1,PBSRLD	;IS A RELOAD PENDING?
	POPJ	P,		;NO, RETURN
	LDB	T1,PBPRJB	;GET JOB NUMBER OF RELOAD JOB
	JUMPN	T1,CPOPJ##	;RETURN IF KNILDR IS RUNNING
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	SUB	T1,.PBKLT(Q3)	;COMPUTE TIME SINCE RELOAD REQUESTED
	IMULI	T1,^D1000	;CONVERT TO MILLISECONDS
	IDIV	T1,TICSEC##	;...
	CAIL	T1,RLDTIM	;TOO LONG SINCE RELOAD REQUESTED?
	PJRST	RLDKNI		;YES, REQUEST KLNI RELOAD AGAIN AND RETURN
	POPJ	P,		;RETURN

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

KNISE1:	MOVX	T1,PBSMOF	;IS MEMORY BEING SET OFFLINE?
	TDNE	T1,.PBSTS(Q3)	;...
	PJRST	SHTKNI		;YES, SHUT KLNI DOWN AND RETURN
	MOVX	T1,PBSONL	;IS KLNI ONLINE?
	TDNN	T1,.PBSTS(Q3)	;...
	POPJ	P,		;NO, RETURN
	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	SUB	T1,.PBKRT(Q3)	;CALCULATE TIME SINCE LAST KLNI RESPONSE
	IMULI	T1,^D1000	;CONVERT TO MILLISECONDS
	IDIV	T1,TICSEC##	;...
	CAIGE	T1,KAFTIM	;TOO LONG SINCE LAST RESPONSE?
	JRST	KNISE2		;NO, GO CHECK KLNI IDLE TIMER
	CONI	KNI,T1		;GET CURRENT CONI STATUS
	CONO	KNI,CO.CPT	;MAKE SURE KLNI IS REALLY STOPPED
	STOPCD	.+1,INFO,KNIKAF	;++KLNI KEEP ALIVE FAILED
	MOVEM	T1,.PBCLI(Q3)	;SAVE CONI FOR ERROR LOGGING
	AOS	.PBKAC(Q3)	;UPDATE COUNT OF KLNI KEEP ALIVE FAILURES
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PBKAT(Q3)	;REMEMBER TIME OF LAST KEEP ALIVE FAILURE
	PJRST	KNESTP		;DO KLNI ERROR STOP PROCESSING AND RETURN

KNISE2:	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	SUB	T1,.PBKCT(Q3)	;CALCULATE TIME SINCE LAST COMMAND QUEUED
	IMULI	T1,^D1000	;CONVERT TO MILLISECONDS
	IDIV	T1,TICSEC##	;...
	CAIGE	T1,IDLTIM	;TOO LONG SINCE LAST COMMAND?
	POPJ	P,		;NO, ALL DONE
	MOVEI	T1,CMORSA	;READ NI STATION ADDRESS COMMAND FUNCTION
	PUSHJ	P,GETCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, RETURN NOW
	XMOVEI	T1,GIVCMD	;GET ADDRESS OF CALLBACK ROUTINE
	PJRST	KNICMD		;QUEUE COMMAND AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  SET MEMORY OFFLINE SUPPORT


;ROUTINE CALLED ON CPU0 WHEN MEMORY IS ABOUT TO BE SET OFFLINE
;LINKAGE:
;	PUSHJ	P,KNIMOF
;RETURNS:
;	CPOPJ ALWAYS

KNIMOF::MOVEI	T1,KNIMF1	;GET ADDRESS OF ROUTINE
	PJRST	CPUAPP##	;CALL ROUTINE FOR ALL CPUS

;ROUTINE CALLED FOR ALL CPUS

KNIMF1:	SKIPN	.CPNPB##-.CPCDB##(P1) ;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN NOW
	PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,.CPNPB##-.CPCDB##(P1) ;GET ADDRESS OF PORT CONTROL BLOCK
	MOVX	T1,PBSMOF	;SET MEMORY OFFLINE FLAG
	IORM	T1,.PBSTS(Q3)	;...
	POPJ	P,		;AND RETURN
;ROUTINE CALLED ON CPU0 WHEN SET MEMORY OFFLINE HAS COMPLETED
;LINKAGE:
;	PUSHJ	P,KNIMON
;RETURNS:
;	CPOPJ ALWAYS

KNIMON::MOVEI	T1,KNIMN1	;GET ADDRESS OF ROUTINE
	PJRST	CPUAPP##	;CALL ROUTINE FOR ALL CPUS

;ROUTINE CALLED FOR ALL CPUS

KNIMN1:	SKIPN	.CPNPB##-.CPCDB##(P1) ;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN NOW
	PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,.CPNPB##-.CPCDB##(P1) ;GET ADDRESS OF PORT CONTROL BLOCK
	MOVX	T1,PBSMOF	;GET MEMORY OFFLINE FLAG
	TDNN	T1,.PBSTS(Q3)	;KLNI STOPPED BECAUSE OF SET MEMORY OFFLINE?
	POPJ	P,		;NO, RETURN NOW
	ANDCAM	T1,.PBSTS(Q3)	;YES, CLEAR MEMORY OFFLINE FLAG
	MOVX	T1,PBSINI	;SET INITIALIZE KLNI FLAG
	IORM	T1,.PBSTS(Q3)	;...
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  REMOVE CPU SUPPORT


;ROUTINE CALLED WHEN A CPU IS REMOVED
;LINKAGE:
;	PUSHJ	P,KNIRMV
;RETURNS:
;	CPOPJ ALWAYS
;ALL ACS PRESERVED

KNIRMV::PUSHJ	P,SAVT##	;SAVE T1-T4
	SKIPN	.CPNPB##	;DO WE HAVE A KLNI?
	POPJ	P,		;NO, RETURN
	PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,.CPNPB##	;GET ADDRESS OF PORT CONTROL BLOCK
	PJRST	SHTKNI		;SHUT DOWN KLNI AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  CPU OFFLINE SUPPORT


;ROUTINE CALLED WHEN A CPU IS DECLARED DEAD
;LINKAGE:
;	T1/ CPU NUMBER OF DEAD CPU
;	PUSHJ	P,KNIDED
;RETURNS:
;	CPOPJ ALWAYS
;ALL ACS PRESERVED

KNIDED::PUSHJ	P,SAVT##	;SAVE T1-T4
	LSH	T1,.CPSOF##	;COMPUTE OFFSET FROM CPU0'S CDB
	SKIPN	.C0NPB##(T1)	;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN
	PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,.C0NPB##(T1)	;GET ADDRESS OF PORT CONTROL BLOCK
	PJRST	STPKNX		;DECLARE KLNI DEAD AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  RESET UUO


;ROUTINE CALLED WHEN A JOB DOES A RESET UUO
;LINKAGE:
;	PUSHJ	P,KNIRST
;RETURNS:
;	CPOPJ ALWAYS

KNIRST::MOVEI	T1,KNIRS1	;GET ADDRESS OF ROUTINE
	PJRST	CPUAPP##	;CALL ROUTINE FOR ALL CPUS

;ROUTINE CALLED FOR ALL CPUS

KNIRS1:	SKIPN	.CPNPB##-.CPCDB##(P1) ;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN NOW
	PUSHJ	P,SAVQ##	;SAVE Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	Q3,.CPNPB##-.CPCDB##(P1) ;GET ADDRESS OF PORT CONTROL BLOCK
	SETZ	T1,		;GET A ZERO
	LDB	T2,PBPRJB	;GET JOB NUMBER OF RELOAD JOB
	CAMN	T2,.CPJOB##	;SAME AS JOB DOING RESET?
	DPB	T1,PBPRJB	;YES, CLEAR RELOAD JOB NUMBER
	LDB	T2,PBPMJB	;GET JOB NUMBER OF MAINTENANCE JOB
	CAMN	T2,.CPJOB##	;SAME AS JOB DOING RESET?
	DPB	T1,PBPMJB	;YES, CLEAR MAINTENANCE JOB NUMBER
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI USER SERVICE  --  ETHSER FUNCTION DISPATCH


;ROUTINE CALLED BY ETHSER FOR DEVICE SPECIFIC FUNCTIONS
;LINKAGE:
;	T1/ ADDRESS OF EA BLOCK
;	T2/ ADDRESS OF PORT CONTROL BLOCK
;	T3/ ADDRESS OF PROTOCOL USER BLOCK (OR ZERO)
;	PUSHJ	P,KNUSER
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNUSER::PUSHJ	P,SAVE1##	;SAVE P1
	PUSHJ	P,SAVQ##	;AND Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVE	P1,T1		;SAVE EA BLOCK ADDRESS
	MOVE	Q3,T2		;AND ADDRESS OF PORT CONTROL BLOCK
	MOVE	Q2,T3		;AND ADDRESS OF PROTOCOL USER BLOCK
	MOVE	T1,.EAFCN(P1)	;GET KONTROLLER FUNCTION CODE
	SKIPLE	T1		;RANGE CHECK FUNCTION CODE
	CAILE	T1,EK.MAX	;...
KNUIFC:!STOPCD	[ERRRET	(UNIFC%)],DEBUG,KNIIFC ;++ILLEGAL FUNCTION CODE
	MOVE	T1,KNUDSP(T1)	;GET ADDRESS OF FUNCTION SPECIFIC ROUTINE
	PJRST	(T1)		;DISPATCH

;GENERIC ERROR RETURN WHICH RELEASES KLNI COMMAND BUFFER

KNXSER:	PUSH	P,T1		;SAVE ERROR CODE
KNXSR1:	PUSHJ	P,GIVCMD	;RELEASE KLNI COMMAND BUFFER
	PJRST	TPOPJ##		;RESTORE ERROR CODE AND RETURN
;KLNI FUNCTION DISPATCH TABLES

DEFINE	FNC,<

	XALL				;;LIST GENERATED TABLE

	DISP	0,	KNUIFC		;;ILLEGAL (PLACE HOLDER)
	DISP	EK.SEA, KNUSEA		;;SET ETHERNET ADDRESS
	DISP	EK.RKC,	KNURKC		;;READ AND CLEAR KONTROLLER COUNTERS
	DISP	EK.RPC,	KNURPC		;;READ AND CLEAR PORTAL COUNTERS
	DISP	EK.EPT,	KNUEPT		;;ENABLE PROTOCOL
	DISP	EK.DPT,	KNUDPT		;;DISABLE PROTOCOL
	DISP	EK.EMA,	KNUEMA		;;ENABLE MULTI-CAST ADDRESS
	DISP	EK.DMA,	KNUDMA		;;DISABLE MULTI-CAST ADDRESS
	DISP	EK.RDG,	KNURDG		;;RECEIVE DATAGRAM
	DISP	EK.XDG,	KNUXDG		;;TRANSMIT DATAGRAM

	SALL				;;TURN LISTING BACK OFF

>; END DEFINE FNC

;GENERATE FUNCTION DISPATCH TABLE

DEFINE	DISP(CODE,ADDR),<
IF1,<IFN <CODE-<.-KNUDSP>>,<PRINTX ?Table KNUDSP entry CODE is out of order>>
	IFIW	ADDR		;CODE
>; END DEFINE DISP

KNUDSP:	FNC			;GENERATE FUNCTION DISPATCH TABLE
IF1,<IFN <EK.MAX-<.-KNUDSP-1>>,<PRINTX ?Table KNUDSP is missing entries>>
	SUBTTL	KLNI USER SERVICE  --  SET KONTROLLER ETHERNET ADDRESS


;HERE TO PROCESS A SET KONTROLLER ETHERNET ADDRESS CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNUSEA
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNUSEA:	MOVEI	T1,CMOWSA	;WRITE NI STATION ADDRESS COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	AOS	(P)		;PRESET SUCCESS RETURN
	DMOVE	T1,.EAEAD(P1)	;GET NEW ETHERNET ADDRESS
	DMOVEM	T1,.PBEAD(Q3)	;STORE INTO PORT CONTROL BLOCK
	LDB	T1,PBPPRM	;GET PROMISCUOUS RECEIVER FLAG
	DPB	T1,CMPPRM	;SET AS APPROPRIATE IN COMMAND
	LDB	T1,PBPPMM	;GET PROMISCUOUS MULTI-CAST FLAG
	DPB	T1,CMPAAM	;SET AS APPROPRIATE IN COMMAND
	DMOVE	T1,.PBEAD(Q3)	;GET NEW ETHERNET ADDRESS
	DMOVEM	T1,.CMNEA(Q1)	;STORE INTO COMMAND BUFFER
	XMOVEI	T1,KNCSEA	;GET CALLBACK ROUTINE ADDRESS
	MOVX	T2,PBSONL	;IS KLNI ONLINE?
	TDNN	T2,.PBSTS(Q3)	;...
	PJRST	(T1)		;NO, CALL CALLBACK ROUTINE NOW
	PJRST	KNICMD		;YES, QUEUE KLNI COMMAND
;HERE TO PROCESS WRITE ETHERNET ADDRESS CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCSEA
;RETURNS:
;	CPOPJ ALWAYS

KNCSEA:	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  READ AND CLEAR KONTROLLER COUNTERS


;HERE TO PROCESS AN READ AND CLEAR KONTROLLER COUNTERS CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNURKC
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNURKC:	MOVEI	T1,CMORCC	;READ/CLEAR COUNTERS COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	LDB	T1,CMPFLG	;GET CURRENT FLAGS
	IORX	T1,CMFCLR	;SET CLEAR COUNTERS FLAG
	DPB	T1,CMPFLG	;...
	AOS	(P)		;PRESET SUCCESS RETURN
	XMOVEI	T1,KNCRKC	;GET CALLBACK ROUTINE ADDRESS
	MOVX	T2,PBSONL	;IS KLNI ONLINE?
	TDNN	T2,.PBSTS(Q3)	;...
	PJRST	(T1)		;NO, CALL CALLBACK ROUTINE NOW
	PJRST	KNICMD		;YES, QUEUE KLNI COMMAND
;HERE TO PROCESS READ/CLEAR COUNTERS CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCRKC
;RETURNS:
;	CPOPJ ALWAYS

KNCRKC:	PUSHJ	P,SAVP2##	;SAVE P2
	PUSHJ	P,UPDCTR	;UPDATE KLNI COUNTERS AREA
	MOVE	P2,.PBVCT(Q3)	;GET ADDRESS OF KLNI COUNTERS AREA
	MOVSI	T4,-RKCTLN	;GET AOJBN POINTER TO COUNTERS TABLE
KNCRK1:	HRRZ	T3,RKCTAB(T4)	;GET KONTROLLER COUNTER NUMBER
	CAML	T3,.EACBS(P1)	;BUFFER LARGE ENOUGH FOR THIS COUNTER?
	JRST	KNCRK2		;NO, SKIP IT
	ADD	T3,.EACBA(P1)	;CALCULATE ADDRESS WHERE COUNTER STORED
	SETZ	T1,		;ZERO FOR RESETING COUNTER
	XCT	RKCTAB+1(T4)	;FETCH AND ZERO NEXT COUNTER
	MOVEM	T1,(T3)		;STORE COUNTER IN USER BUFFER
KNCRK2:	AOBJN	T4,.+1		;SKIP OVER COUNTER NUMBER LOCATION
	AOBJN	T4,KNCRK1	;LOOP BACK TO PROCESS ENTIRE TABLE
	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
;TABLE OF INSTRUCTIONS FOR FETCHING AND ZEROING KONTROLLER COUNTERS

DEFINE	CTR(CNTR,INST),<
	EXP	CNTR		;;COUNTER NUMBER
	EXP	INST		;;INSTRUCTION TO FETCH AND ZERO COUNTER
>; END DEFINE CTR

RKCTAB:	CTR	KC.BYR,<EXCH T1,.KCBYR(P2)> ;BYTES RECEIVED
	CTR	KC.BYX,<EXCH T1,.KCBYX(P2)> ;BYTES TRANSMITTED
	CTR	KC.DGR,<EXCH T1,.KCDGR(P2)> ;DATAGRAMS RECEIVED
	CTR	KC.DGX,<EXCH T1,.KCDGX(P2)> ;DATAGRAMS TRANSMITTED
	CTR	KC.MBR,<EXCH T1,.KCMBR(P2)> ;MULTI-CAST BYTES RECEIVED
	CTR	KC.MDR,<EXCH T1,.KCMDR(P2)> ;MULTI-CAST DATAGRAMS RECEIVED
	CTR	KC.DXD,<EXCH T1,.KCDXD(P2)> ;DATAGRAMS TRANSMITTED, INITIALLY DEFERRED
	CTR	KC.DX1,<EXCH T1,.KCDX1(P2)> ;DATAGRAMS TRANSMITTED, SINGLE COLLISION
	CTR	KC.DXM,<EXCH T1,.KCDXM(P2)> ;DATAGRAMS TRANSMITTED, MULTIPLE COLLISIONS
	CTR	KC.XMF,<EXCH T1,.KCXMF(P2)> ;TRANSMIT FAILURES
	CTR	KC.XFM,<PUSHJ P,RKCXFM>	    ;TRANSMIT FAILURE BIT MASK
	CTR	KC.RCF,<EXCH T1,.KCRCF(P2)> ;RECEIVE FAILURES
	CTR	KC.RFM,<PUSHJ P,RKCRFM>	    ;RECEIVE FAILURE BIT MASK
	CTR	KC.UFD,<EXCH T1,.KCUFD(P2)> ;UNRECOGNIZED FRAME DESTINATION
	CTR	KC.DOV,<EXCH T1,.KCDOV(P2)> ;DATA OVERRUN
	CTR	KC.SBU,<EXCH T1,.KCSBU(P2)> ;SYSTEM BUFFER UNAVAILABLE
	CTR	KC.UBU,<EXCH T1,.KCFQE(P2)> ;USER DATAGRAM BUFFER UNAVAILABLE
RKCTLN==.-RKCTAB		;LENGTH OF TABLE

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

;ROUTINE TO COMPUTE TRANSMIT FAILURE BIT MASK

RKCXFM:	EXCH	T1,.KCXFM(P2)	;FETCH BIT MASK AND ZERO
	LSH	T1,-4		;POSITION ERROR BITS
	POPJ	P,		;AND RETURN

;ROUTINE TO COMPUTE RECEIVE FAILURE BIT MASK

RKCRFM:	EXCH	T1,.KCRFM(P2)	;FETCH BIT MASK AND ZERO
	LSH	T1,-4		;POSITION ERROR BITS
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI USER SERVICE  --  READ AND CLEAR PORTAL COUNTERS


;HERE TO PROCESS AN READ AND CLEAR PORTAL COUNTERS CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNURPC
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNURPC:	MOVEI	T1,CMORCC	;READ/CLEAR COUNTERS COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	LDB	T1,CMPFLG	;GET CURRENT FLAGS
	IORX	T1,CMFCLR	;SET CLEAR COUNTERS FLAG
	DPB	T1,CMPFLG	;...
	AOS	(P)		;PRESET SUCCESS RETURN
	XMOVEI	T1,KNCRPC	;GET CALLBACK ROUTINE ADDRESS
	MOVX	T2,PBSONL	;IS KLNI ONLINE?
	TDNN	T2,.PBSTS(Q3)	;...
	PJRST	(T1)		;NO, CALL CALLBACK ROUTINE NOW
	PJRST	KNICMD		;YES, QUEUE KLNI COMMAND
;HERE TO PROCESS READ/CLEAR COUNTERS CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCRPC
;RETURNS:
;	CPOPJ ALWAYS

KNCRPC:	PUSHJ	P,SAVP2##	;SAVE P2
	PUSHJ	P,UPDCTR	;UPDATE KLNI COUNTERS AREA
	MOVE	P2,.PBVCT(Q3)	;GET ADDRESS OF KLNI COUNTERS AREA
	MOVSI	T4,-RPCTLN	;GET AOJBN POINTER TO COUNTERS TABLE
KNCRP1:	MOVE	T3,RPCTAB(T4)	;GET PORTAL COUNTER NUMBER
	CAML	T3,.EACBS(P1)	;BUFFER LARGE ENOUGH FOR THIS COUNTER?
	JRST	KNCRP2		;NO, SKIP IT
	ADD	T3,.EACBA(P1)	;CALCULATE ADDRESS WHERE COUNTER STORED
	SETZ	T1,		;ZERO FOR RESETING COUNTER
	XCT	RPCTAB+1(T4)	;FETCH AND ZERO NEXT COUNTER
	ADDM	T1,(T3)		;UPDATE COUNTERS AREA
KNCRP2:	AOBJN	T4,.+1		;SKIP OVER COUNTER NUMBER LOCATION
	AOBJN	T4,KNCRP1	;LOOP BACK TO PROCESS ENTIRE TABLE
	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
;TABLE OF INSTRUCTIONS FOR FETCHING AND ZEROING PORTAL COUNTERS

DEFINE	CTR(CNTR,INST),<
	EXP	CNTR		;;COUNTER NUMBER
	EXP	INST		;;INSTRUCTION TO FETCH AND ZERO COUNTER
>; END DEFINE CTR

RPCTAB:	CTR	PC.UBU,<PUSHJ P,RPCUBU> ;USER DATAGRAM BUFFER UNAVAILABLE
RPCTLN==.-RPCTAB		;LENGTH OF TABLE

;ROUTINE TO COMPUTE USER BUFFER UNAVAILABLE COUNTER

RPCUBU:	LDB	T2,PUPPTT	;GET PTT TABLE INDEX
	ADD	T2,P2		;ADD STARTING ADDRESS OF COUNTERS AREA
	EXCH	T1,.KCFQP(T2)	;FETCH AND ZERO FREE QUEUE EMPTY COUNTER
	POPJ	P,		;RETURN
	SUBTTL	KLNI USER SERVICE  --  ENABLE PROTOCOL


;HERE TO PROCESS AN ENABLE PROTOCOL CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNUEPT
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNUEPT:	MOVEI	T2,.PULEN	;GET LENGTH OF PROTOCOL USER BLOCK
	PUSHJ	P,GETNWZ##	;ALLOCATE CORE FOR PROTOCOL USER BLOCK
	  ERRRET (UNRES%)	;ERROR, NO RESOURCES
	MOVE	Q2,T1		;SAVE ADDRESS OF PROTOCOL USER BLOCK
	XMOVEI	T1,.PUFQH(Q2)	;INITIALIZE PROTOCOL'S FREE QUEUE HEADER
	PUSHJ	P,INIQUE	;...
	MOVE	T1,.EAPPB(P1)	;GET ADDRESS OF ETHSER'S PORTAL BLOCK
	MOVEM	T1,.PUEPB(Q2)	;SAVE IN PROTOCOL USER BLOCK
	MOVE	T1,.EAPAD(P1)	;GET PROTOCOL PADDING FLAG
	DPB	T1,PUPPAD	;STORE IN PROTOCOL USER BLOCK
	MOVE	T1,.EAPTY(P1)	;GET PROTOCOL TYPE CODE
	JUMPL	T1,KNUEPP	;IF NEGATIVE, GO ENABLE PSEUDO PROTOCOL TYPE
	CAILE	T1,MAXPTY	;RANGE CHECK PROTOCOL TYPE CODE
	ERRRET	(UNIVP%,KNXEP2)	;ERROR, INVALID PROTOCOL TYPE
	PUSHJ	P,SWAB		;SWAP HIGH AND LOW ORDER BYTES FOR KLNI
	MOVEM	T1,.PUPTY(Q2)	;STORE IN PROTOCOL USER BLOCK
	MOVEI	T1,CMOLDP	;GET LOAD PTT TABLE COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  PJRST	KNXEP2		;ERROR, ERROR CODE IN T1
	PUSHJ	P,ADDPTT	;ADD PROTOCOL TO PTT TABLE
	  PJRST	KNXEP1		;ERROR, ERROR CODE IN T1
	AOS	(P)		;PRESET SUCCESS RETURN
	XMOVEI	T1,KNCEPT	;GET ADDRESS OF CALLBACK ROUTINE
	MOVX	T2,PBSONL	;IS KLNI ONLINE?
	TDNN	T2,.PBSTS(Q3)	;...
	PJRST	(T1)		;NO, CALL CALLBACK ROUTINE NOW
	PJRST	KNICMD		;YES, QUEUE KLNI COMMAND

KNXEP1:	PUSH	P,T1		;SAVE ERROR CODE
	PUSHJ	P,GIVCMD	;RELEASE KLNI COMMAND BUFFER
	SKIPA			;AND CONTINUE
KNXEP2:	PUSH	P,T1		;SAVE ERROR CODE
	MOVEI	T1,.PULEN	;GET LENGTH OF PROTOCOL USER BLOCK
	MOVE	T2,Q2		;GET ADDRESS OF PROTOCOL USER BLOCK
	PUSHJ	P,GIVNWS##	;RELEASE THE CORE
	PJRST	TPOPJ##		;RESTORE ERROR CODE AND RETURN
;HERE TO ENABLE A PSEUDO-PROTOCOL TYPE

KNUEPP:	CAXGE	T1,MINPTY	;RANGE CHECK PSEUDO PROTOCOL TYPE
	ERRRET	(UNIVP%,KNXEP2)	;ERROR, INVALID PROTOCOL TYPE
	MOVEM	T1,.PUPTY(Q2)	;STORE PROTOCOL TYPE IN PROTOCOL USER BLOCK
	ERRRET	(UNIVP%,KNXEP2)	;ERROR, NOT YET IMPLEMENTED
;HERE TO PROCESS LOAD PROTOCOL TYPE TABLE CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCEPT
;RETURNS:
;	CPOPJ ALWAYS

KNCEPT:	MOVEM	Q2,.EAPPB(P1)	;SAVE ADDRESS OF PROTOCOL USER BLOCK
	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  DISABLE PROTOCOL


;HERE TO PROCESS A DISABLE PROTOCOL CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNUDPT
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNUDPT:	MOVE	T1,.PUPTY(Q2)	;GET PROTOCOL TYPE
	JUMPL	T1,KNUDPP	;IF NEGATIVE, GO DISABLE PSEUDO PROTOCOL TYPE
	MOVEI	T1,CMOLDP	;GET LOAD PTT TABLE COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	PUSHJ	P,DELPTT	;REMOVE PROTOCOL FROM PTT TABLE
	  PJRST	KNXSER		;ERROR, ERROR CODE IN T1
	AOS	(P)		;PRESET SUCCESS RETURN
	XMOVEI	T1,KNCDPT	;GET ADDRESS OF CALLBACK ROUTINE
	MOVX	T2,PBSONL	;IS KLNI ONLINE?
	TDNN	T2,.PBSTS(Q3)	;...
	PJRST	(T1)		;NO, CALL CALLBACK ROUTINE NOW
	PJRST	KNICMD		;YES, QUEUE KLNI COMMAND
				;CONTINUED FROM PREVIOUS PAGE

;HERE TO DISABLE A PSEUDO-PROTOCOL TYPE

KNUDPP:	CAXN	T1,PT%INF	;INFORMATION ONLY PROTOCOL TYPE?
	PJRST	KNCDPT		;YES, GIVE DISABLE CALLBACK NOW AND RETURN
	ERRRET	(UNIVP%)	;ERROR, INVALID PROTOCOL TYPE
;HERE TO PROCESS LOAD PROTOCOL TYPE TABLE CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCDPT
;RETURNS:
;	CPOPJ ALWAYS

KNCDPT:	PUSH	P,P1		;SAVE ADDRESS OF EA BLOCK
	PUSH	P,Q1		;AND ADDRESS OF COMMAND BLOCK
KNCDP1:	XMOVEI	T1,.PUFQH(Q2)	;GET ADDRESS OF PROTOCOL'S FREE QUEUE HEADER
	PUSHJ	P,REMQUE	;REMOVE NEXT ENTRY
	  JRST	KNCDP2		;NO MORE ENTRIES IN QUEUE
	XMOVEI	Q1,-.CMQUE(T2)	;GET ADDRESS OF KLNI COMMAND BUFFER
	MOVE	P1,.CMEAB(Q1)	;AND ADDRESS OF EA BLOCK
	MOVEI	T1,UNRAB%	;GET RECEIVE ABORTED STATUS CODE
	PUSHJ	P,KNCRDX	;DO RECEIVE DATAGRAM CALLBACK PROCESSING
	JRST	KNCDP1		;LOOP BACK TO EMPTY ENTIRE QUEUE
KNCDP2:	POP	P,Q1		;RESTORE COMMAND BLOCK ADDRESS
	POP	P,P1		;RESTORE ADDRESS OF EA BLOCK
	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	MOVEI	T1,.PULEN	;GET LENGTH OF PROTOCOL USER BLOCK
	MOVE	T2,Q2		;GET ADDRESS OF PROTOCOL USER BLOCK
;$	PUSHJ	P,GIVNWS##	;RELEASE THE CORE
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  ENABLE MULTI-CAST ADDRESS


;HERE TO PROCESS AN ENABLE MULTI-CAST ADDRESS CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNUEMA
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNUEMA:	MOVEI	T1,CMOLDM	;LOAD MCAT TABLE COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	DMOVE	T1,.EAMCA(P1)	;GET MULTI-CAST ADDRESS
	TXNN	T1,<BYTE (8) 1,0,0,0> ;IS MULTI-CAST BIT SET?
	ERRRET	(UNIMA%,KNXSER)	;NO, INVALID MULTI-CAST ADDRESS
	TXNN	T1,^-MCTHAD	;ANY EXTRANEOUS BITS SET?
	TXNE	T1,^-MCTLAD	;...
	ERRRET	(UNIMA%,KNXSER)	;YES, INVALID MULTI-CAST ADDRESS
	PUSHJ	P,ADDMCA	;ADD ADDRESS TO MCAT TABLE
	  PJRST	KNXSER		;ERROR, ERROR CODE IN T1
	AOS	(P)		;PRESET SUCCESS RETURN
	XMOVEI	T1,KNCEMA	;AND ADDRESS OF CALLBACK ROUTINE
	MOVX	T2,PBSONL	;IS KLNI ONLINE?
	TDNN	T2,.PBSTS(Q3)	;...
	PJRST	(T1)		;NO, CALL CALLBACK ROUTINE NOW
	PJRST	KNICMD		;YES, QUEUE KLNI COMMAND
;HERE TO PROCESS LOAD MULTI-CAST ADDRESS TABLE CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCEMA
;RETURNS:
;	CPOPJ ALWAYS

KNCEMA:	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  DISABLE MULTI-CAST ADDRESS


;HERE TO PROCESS A DISABLE MULTI-CAST ADDRESS CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNUDMA
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNUDMA:	MOVEI	T1,CMOLDM	;LOAD MCAT TABLE COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	DMOVE	T1,.EAMCA(P1)	;GET MULTI-CAST ADDRESS
	TXNN	T1,<BYTE (8) 1,0,0,0> ;IS MULTI-CAST BIT SET?
	ERRRET	(UNIMA%,KNXSER)	;NO, INVALID MULTI-CAST ADDRESS
	TXNN	T1,^-MCTHAD	;ANY EXTRANEOUS BITS SET?
	TXNE	T2,^-MCTLAD	;...
	ERRRET	(UNIMA%,KNXSER)	;YES, INVALID MULTI-CAST ADDRESS
	PUSHJ	P,DELMCA	;DELETE ADDRESS FROM MCAT TABLE
	  PJRST	KNXSER		;ERROR, ERROR CODE IN T1
	AOS	(P)		;PRESET SUCCESS RETURN
	XMOVEI	T1,KNCDMA	;AND ADDRESS OF CALLBACK ROUTINE
	MOVX	T2,PBSONL	;IS KLNI ONLINE?
	TDNN	T2,.PBSTS(Q3)	;...
	PJRST	(T1)		;NO, CALL CALLBACK ROUTINE NOW
	PJRST	KNICMD		;YES, QUEUE KLNI COMMAND
;HERE TO PROCESS LOAD MULTI-CAST ADDRESS TABLE CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCDMA
;RETURNS:
;	CPOPJ ALWAYS

KNCDMA:	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  RECEIVE DATAGRAM


;HERE TO PROCESS A RECEIVE DATAGRAM CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNURDG
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNURDG:	MOVX	T1,PBSONL	;IS KLNI ONLINE?
	TDNN	T1,.PBSTS(Q3)	;...
	ERRRET	(UNRAB%)	;NO, RECEIVE ABORTED
	MOVEI	T1,CMORDG	;RECEIVE DATAGRAM COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	LDB	T1,CMPFLG	;GET CURRENT FLAGS
	IORX	T1,CMFBSD	;SET BSD STYLE COMMAND FLAG
	LDB	T2,PUPPAD	;GET PROTOCOL PADDING FLAG
	JUMPE	T2,KNURD1	;JUMP IF DATAGRAM NOT PADDED
	IORX	T1,CMFPAD	;SET PADDING FLAG
KNURD1:	DPB	T1,CMPFLG	;STORE UPDATED FLAGS
	XMOVEI	T1,.EAMSD(P1)	;GET ADDRESS OF FIRST MSD
	SETZ	T2,		;$ DON'T RECEIVE PADDING SEPERATELY
	PUSHJ	P,GENBSC	;GENERATE BSD CHAIN
	  PJRST	KNXSER		;ERROR, ERROR CODE IN T1
	DPB	T1,CMPRDL	;STORE SIZE OF DATAGRAM IN COMMAND BUFFER
	MOVEM	T2,.CMRVB(Q1)	;SAVE ADDRESS OF BSD CHAIN
	MAP	T2,0(T2)	;GET PHYSICAL ADDRESS OF BSD CHAIN
	TXZ	T2,NADBTS	;...
	MOVEM	T2,.CMRBA(Q1)	;STORE IN COMMAND BUFFER
	LDB	T1,CMPRDL	;GET SIZE OF DATAGRAM
	SKIPN	.PUFQH+.QHELN(Q2) ;HAVE WE ALREADY SET UP QUEUE ENTRY LENGTH?
	MOVEM	T1,.PUFQH+.QHELN(Q2) ;NO, DO SO NOW
	SKIPLE	T1		;VALID DATAGRAM LENGTH?
	CAME	T1,.PUFQH+.QHELN(Q2) ;YES, SAME SIZE AS PREVIOUS DATAGRAMS?
	ERRRET	(UNIBS%,KNURDX)	;NO, INVALID DATAGRAM BUFFER SIZE
	XMOVEI	T1,KNCRDG	;GET ADDRESS OF CALLBACK ROUTINE
	PUSHJ	P,KNICMF	;QUEUE KLNI COMMAND TO PROTOCOL FREE QUEUE
	PJRST	CPOPJ1##	;AND RETURN

KNURDX:	PUSH	P,T1		;SAVE ERROR CODE
	MOVE	T1,.CMRVB(Q1)	;GET ADDRESS OF BSD CHAIN
	PUSHJ	P,GIVBSC	;RELEASE THE CORE
	PUSHJ	P,GIVCMD	;RELEASE THE KLNI COMMAND
	PJRST	TPOPJ##		;RESTORE ERROR CODE AND RETURN
;HERE TO PROCESS RECEIVE DATAGRAM CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCRDG
;RETURNS:
;	CPOPJ ALWAYS

KNCRDG:	DMOVE	T1,.CMRDA(Q1)	;GET DESTINATION ADDRESS OF DATAGRAM
	DMOVEM	T1,.EADDA(P1)	;STORE IN EA BLOCK
	DMOVE	T1,.CMRSA(Q1)	;GET SOURCE ETHERNET ADDRESS
	DMOVEM	T1,.EADSA(P1)	;STORE IN EA BLOCK
	LDB	T1,CMPRPT	;GET PROTOCOL TYPE OF DATAGRAM
	PUSHJ	P,SWAB		;SWAP HIGH AND LOW ORDER BYTES
	MOVEM	T1,.EADPT(P1)	;STORE IN EA BLOCK
	LDB	T1,CMPRDL	;GET SIZE OF DATAGRAM
	SUBI	T1,4		;SUBTRACT OFF CRC OVERHEAD BYTES
	MOVEM	T1,.EADSZ(P1)	;STORE IN EA BLOCK
	LDB	T1,PUPPAD	;GET PROTOCOL PADDING FLAG
	JUMPE	T1,KNCRD1	;JUMP IF DATAGRAM NOT PADDED
	ILDB	T1,.EAFCD+1(P1)	;$ CROCK
	ILDB	T2,.EAFCD+1(P1)	;$ DITTO
	LSH	T2,^D8		;$
	IOR	T1,T2		;$
;$	MOVE	T2,.CMRVB(Q1)	;YES, GET ADDRESS OF FIRST BSD
;$	MOVE	T1,.BSRWD(T2)	;GET FIRST TWO BYTES OF DATAGRAM
;$	LSH	T1,-<^D36-^D16>	;RIGHT JUSTIFY
;$	PUSHJ	P,SWAB		;SWAP BYTES
	MOVEM	T1,.EADSZ(P1)	;STORE CORRECT DATAGRAM SIZE
KNCRD1:	SETZ	T1,		;ASSUME DATAGRAM STATUS IS OK
	LDB	T2,CMPSTS	;GET DATAGRAM STATUS
	TXNN	T2,CMSERR	;ANY ERRORS?
	JRST	KNCRDX		;NO, GO GIVE CALLBACK
	LDB	T1,[POINTR (T2,CMSETY)] ;GET SPECIFIC ERROR TYPE
	PUSHJ	P,CVTETY	;CONVERT INTO ETHSER STATUS CODE
KNCRDX:	MOVEM	T1,.EADST(P1)	;STORE DATAGRAM STATUS IN EA BLOCK
	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	MOVE	T1,.CMRVB(Q1)	;GET ADDRESS OF BSD CHAIN
	PUSHJ	P,GIVBSC	;RELEASE THE CORE
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  TRANSMIT DATAGRAM


;HERE TO PROCESS A TRANSMIT DATAGRAM CALL TO KNISER
;LINKAGE:
;	P1/ ADDRESS OF EA BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNUXDG
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

KNUXDG:	MOVX	T1,PBSONL	;IS KLNI ONLINE?
	TDNN	T1,.PBSTS(Q3)	;...
	ERRRET	(UNDNS%)	;NO, DATAGRAM NOT SENT
	MOVEI	T1,CMOXDG	;TRANSMIT DATAGRAM COMMAND FUNCTION
	PUSHJ	P,GENCMD	;GENERATE KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	LDB	T1,CMPFLG	;GET CURRENT FLAGS
	IORX	T1,CMFBSD	;SET BSD STYLE COMMAND FLAG
	LDB	T2,PUPPAD	;GET PROTOCOL PADDING FLAG
	JUMPE	T2,KNUXD1	;JUMP IF DATAGRAM NOT PADDED
	IORX	T1,CMFPAD	;SET PADDING FLAG
KNUXD1:	DPB	T1,CMPFLG	;STORE UPDATED FLAGS
	MOVE	T1,.PUPTY(Q2)	;GET PROTOCOL TYPE CODE
	DPB	T1,CMPXPT	;STORE IN COMMAND BUFFER
	DMOVE	T1,.EADDA(P1)	;GET DESTINATION ADDRESS OF DATAGRAM
	TXNE	T1,<BYTE (8) 1,0,0,0> ;MULTI-CAST BIT SET?
	AOS	.PBMCE(Q3)	;YES, COUNT FOR ADJUSTMENT OF KLNI COUNTERS
	DMOVEM	T1,.CMXDA(Q1)	;STORE IN COMMAND BUFFER
	XMOVEI	T1,.EAMSD(P1)	;GET ADDRESS OF FIRST MSD
	SETZ	T2,		;NO SEPERATE RECEIVE PADDING BYTES
	PUSHJ	P,GENBSC	;GENERATE BSD CHAIN
	  PJRST	KNXSER		;ERROR, ERROR CODE IN T1
	SKIPG	T1		;VALID DATAGRAM SIZE?
	ERRRET	(UNIBS%,KNUXDX)	;NO, INVALID DATAGRAM BUFFER SIZE
	DPB	T1,CMPRDL	;STORE SIZE OF DATAGRAM IN COMMAND BUFFER
	MOVEM	T2,.CMXVB(Q1)	;SAVE ADDRESS OF BSD CHAIN
	MAP	T2,0(T2)	;GET PHYSICAL ADDRESS OF BSD CHAIN
	TXZ	T2,NADBTS	;...
	MOVEM	T2,.CMXBA(Q1)	;STORE IN COMMAND BUFFER
	XMOVEI	T1,KNCXDG	;ADDRESS OF CALLBACK ROUTINE
	PUSHJ	P,KNICMD	;QUEUE KLNI COMMAND
	PJRST	CPOPJ1##	;AND RETURN

KNUXDX:	PUSH	P,T1		;SAVE ERROR CODE
	MOVE	T1,.CMXVB(Q1)	;GET ADDRESS OF BSD CHAIN
	PUSHJ	P,GIVBSC	;RELEASE THE CORE
	PUSHJ	P,GIVCMD	;RELEASE THE KLNI COMMAND
	PJRST	TPOPJ##		;RESTORE ERROR CODE AND RETURN
;HERE TO PROCESS TRANSMIT DATAGRAM CALLBACK
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q1/ ADDRESS OF COMMAND BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNCXDG
;RETURNS:
;	CPOPJ ALWAYS

KNCXDG:	DMOVE	T1,.CMXDA(Q1)	;GET DESTINATION ADDRESS OF DATAGRAM
	DMOVEM	T1,.EADDA(P1)	;STORE IN EA BLOCK
	DMOVE	T1,.PBEAD(Q3)	;GET SOURCE ETHERNET ADDRESS
	DMOVEM	T1,.EADSA(P1)	;STORE IN EA BLOCK
	LDB	T1,CMPXPT	;GET PROTOCOL TYPE OF DATAGRAM
	PUSHJ	P,SWAB		;SWAP HIGH AND LOW ORDER BYTES
	MOVEM	T1,.EADPT(P1)	;STORE IN EA BLOCK
	LDB	T1,CMPXDL	;GET SIZE OF DATAGRAM
	MOVEM	T1,.EADSZ(P1)	;STORE IN EA BLOCK
	SETZ	T1,		;ASSUME DATAGRAM STATUS IS OK
	LDB	T2,CMPSTS	;GET DATAGRAM STATUS
	TXNN	T2,CMSERR	;ANY ERRORS?
	JRST	KNCXDX		;NO, GO GIVE CALLBACK
	LDB	T1,[POINTR (T2,CMSETY)]	;GET SPECIFIC ERROR TYPE
	PUSHJ	P,CVTETY	;CONVERT INTO ETHSER STATUS CODE
KNCXDX:	MOVEM	T1,.EADST(P1)	;STORE DATAGRAM STATUS IN EA BLOCK
	PUSHJ	P,CALETH	;INTERRUPT ETHSER
	MOVE	T1,.CMXVB(Q1)	;GET ADDRESS OF BSD CHAIN
	PUSHJ	P,GIVBSC	;RELEASE THE CORE
	PJRST	GIVCMD		;RELEASE COMMAND BUFFER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  INTERRUPT ETHSER


;ROUTINE TO INTERRUPT ETHSER
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	P1/ ADDRESS OF EA BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK (OR ZERO)
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,CALETH
;RETURNS:
;	CPOPJ ALWAYS

CALETH:	MOVE	T1,P1		;GET ADDRESS OF EA BLOCK
	MOVE	T2,.PBEKB(Q3)	;AND ADDRESS OF ETHERNET KONTROLLER BLOCK
	SKIPE	T3,Q2		;HAVE A PROTOCOL USER BLOCK?
	MOVE	T3,.PUEPB(Q2)	;YES, GET ADDRESS OF ETHERNET PORTAL BLOCK
	PJRST	ETKINT##	;INTERRUPT ETHSER AND RETURN
	SUBTTL	KLNI USER SERVICE  --  GENERATE KLNI COMMAND BUFFER


;ROUTINE CALLED TO GENERATE A KLNI COMMAND BUFFER
;LINKAGE:
;	T1/ KLNI COMMAND OPCODE
;	P1/ ADDRESS OF EA BLOCK
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	PUSHJ	P,GENCMD
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS WITH:
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER

GENCMD:	PUSHJ	P,GETCMD	;GET A KLNI COMMAND BUFFER
	  POPJ	P,		;ERROR, ERROR CODE IN T1
	MOVEM	Q2,.CMPUB(Q1)	;STORE ADDRESS OF PROTOCOL USER BLOCK
	MOVEM	P1,.CMEAB(Q1)	;AND ADDRESS OF EA BLOCK
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  PORT CONTROL BLOCK INITIALIZATION


;ROUTINE TO INITIALIZE A PORT CONTROL BLOCK
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,PCBINI
;RETURNS:
;	CPOPJ ALWAYS

PCBINI:	MOVE	T1,.CPCPN##	;GET OUR CPU NUMBER
	DPB	T1,PBPCPU	;STORE IN PORT CONTROL BLOCK
	MOVEI	T1,KNICHN##	;GET PRIORITY INTERRUPT ASSIGNMENT
	MOVEM	T1,.PBPIA(Q3)	;STORE IN PCB
	MOVE	T1,.CPEPT##	;GET ADDRESS OF EPT
	ADDI	T1,KNIICH*4	;COMPUTE ADDRESS OF CHANNEL LOGOUT AREA
	MOVEM	T1,.PBLGO(Q3)	;STORE IN PORT CONTROL BLOCK
	MAP	T1,.PBPCB(Q3)	;DETERMINE PHYSICAL ADDRESS OF PCB
	TXZ	T1,NADBTS	;CLEAR NON-ADDRESS BITS
	MOVEM	T1,.PBPBA(Q3)	;STORE PHYSICAL PCB ADDRESS FOR THE KLNI
	MOVE	T1,.PBLGO(Q3)	;GET ADDRESS OF RH20 LOGOUT AREA
	MAP	T1,1(T1)	;GET PHYSICAL ADDRESS OF SECOND WORD
	TXZ	T1,NADBTS	;...
	MOVEM	T1,.PBER2(Q3)	;SAVE IN PCB FOR KLNI
	XMOVEI	T1,.PBCMQ(Q3)	;GET VIRTUAL ADDRESS OF COMMAND QUEUE
	PUSHJ	P,INIQUE	;INITIALIZE QUEUE HEADER
	XMOVEI	T1,.PBRSQ(Q3)	;GET VIRTUAL ADDRESS OF RESPONSE QUEUE
	PUSHJ	P,INIQUE	;INITIALIZE QUEUE HEADER
	XMOVEI	T1,.PBUPQ(Q3)	;GET VIRTUAL ADDRESS OF UNKNOWN PROTOCOL QUEUE
	PUSHJ	P,INIQUE	;INITIALIZE QUEUE HEADER
	SETZM	.PBPTT(Q3)	;CLEAR ADDRESS OF PTT TABLE
	SETZM	.PBMCT(Q3)	;AND ADDRESS OF MCAT TABLE
	SETZM	.PBKCB(Q3)	;AND ADDRESS OF COUNTERS BUFFER
	MOVEI	T1,1		;$ SET PROMISCUOUS MULTI-CAST FLAG
	DPB	T1,PBPPMM	;$ ...
	POPJ	P,		;RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  PORT CONTROL BLOCK RESET


;ROUTINE CALLED TO RESET A PORT CONTROL BLOCK
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,PCBRST
;RETURNS:
;	CPOPJ ALWAYS

PCBRST:	SETOM	.PBCMQ+.QHIWD(Q3) ;RESET COMMAND QUEUE INTERLOCK
	SETOM	.PBRSQ+.QHIWD(Q3) ;RESET RESPONSE QUEUE INTERLOCK
	SETOM	.PBUPQ+.QHIWD(Q3) ;RESET UNKNOWN PROTOCOL TYPE QUEUE INTERLOCK
	MOVE	T3,.PBLPT(Q3)	;GET LENGTH OF PROTOCOL TYPE TABLE
	MOVE	T4,.PBVPT(Q3)	;AND ADDRESS OF PTT
PCBRS1:	JUMPE	T3,PCBRS3	;CONTINUE IF NO MORE PTT ENTRIES
	LDB	T1,PTPENA	;IS THIS PROTOCOL ENABLED?
	JUMPE	T1,PCBRS2	;JUMP IF NOT ENABLED
	MOVE	T2,.PTPUB(T4)	;GET ADDRESS OF PROTOCOL USER BLOCK
	SETOM	.PUFQH+.QHIWD(T2) ;CLEAR PROTOCOL'S FREE QUEUE INTERLOCK
PCBRS2:	ADDI	T4,.PTLEN	;BUMP PTT TABLE POINTER TO NEXT ENTRY
	SOJA	T3,PCBRS1	;LOOP BACK FOR ENTIRE PTT TABLE

PCBRS3:	SETZM	.PBER0(Q3)	;RESET ERROR WORDS
	SETZM	.PBER1(Q3)	;...
	SETZM	.PBER3(Q3)	;...
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  ADD PROTOCOL TYPE TO PTT TABLE


;ROUTINE TO ADD A PROTOCOL TO THE PTT TABLE
;LINKAGE:
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,ADDPTT
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

ADDPTT:	PUSHJ	P,SAVE2##	;SAVE P1-P2
	ETHLOK			;INTERLOCK AGAINST SMP RACES
	SETZ	T2,		;USED TO REMEMBER FIRST FREE ENTRY IN PTT
	MOVE	P1,.PBLPT(Q3)	;GET LENGTH OF PTT TABLE
	MOVE	P2,.PBVPT(Q3)	;GET VIRTUAL ADDRESS OF PTT TABLE
ADDPT1:	JUMPE	P1,ADDPT4	;EXIT LOOP IF NO MORE ENTRIES
	LDB	T1,PTPENA	;GET PROTOCOL ENTRY ENABLED BIT
	JUMPN	T1,ADDPT2	;IF ENABLED, GO CHECK FOR DUPLICATE
	SKIPN	T2		;ALREADY HAVE POINTER TO FREE ENTRY?
	MOVE	T2,P2		;NO, REMEMBER LOCATION OF FIRST FREE ENTRY
	JRST	ADDPT3		;AND CONTINUE CHECK REMAINDER OF TABLE
ADDPT2:	LDB	T1,PTPPTY	;GET PROTOCOL TYPE OF THIS ENTRY
	CAMN	T1,.PUPTY(Q2)	;FOUND DUPLICATE PROTOCOL?
	ERRRET	(UNPIU%,UNLETH##) ;YES, PROTOCOL ALREADY IN USE
ADDPT3:	ADDI	P2,.PTLEN	;BUMP PTT TABLE POINTER TO NEXT ENTRY
	SOJA	P1,ADDPT1	;LOOP BACK TO CHECK ALL TABLE ENTRIES

ADDPT4:	JUMPE	T2,[ERRRET (UNNRE%,UNLETH##)] ;ERROR IF NO FREE ENTRIES
	MOVE	P2,T2		;GET ADDRESS OF FREE ENTRY
	MOVE	T1,.PUPTY(Q2)	;GET DESIRED PROTOCOL TYPE
	DPB	T1,PTPPTY	;STORE IN PTT ENTRY
	MAP	T1,.PUFQH+.QHFLI(Q2) ;GET PHYSICAL ADDRESS OF FREE QUEUE FLINK
	TXZ	T1,NADBTS	;CLEAR NON-ADDRESS BITS
	MOVEM	T1,.PTFRQ(P2)	;SAVE IN PTT ENTRY
	MOVEM	Q2,.PTPUB(P2)	;SAVE ADDRESS OF PUB IN PTT ENTRY
	MOVEI	T1,1		;SET THE PROTOCOL ENABLED FLAG
	DPB	T1,PTPENA	;...
	MOVE	T1,P2		;GET INDEX INTO PTT TABLE
	SUB	T1,.PBVPT(Q3)	;CALCULATE PTT TABLE ENTRY NUMBER
	IDIVI	T1,.PTLEN	;...
	DPB	T1,PUPPTT	;AND REMEMBER IN PROTOCOL USER BLOCK
	AOS	.PBCPT(Q3)	;ADJUST COUNT OF PTT ENTRIES
	ETHULK			;RELEASE SMP INTERLOCK
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  DELETE PROTOCOL TYPE FROM PTT TABLE


;ROUTINE TO DELETE A PROTOCOL FROM THE PTT TABLE
;LINKAGE:
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,DELPTT
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

DELPTT:	PUSHJ	P,SAVE2##	;SAVE P1-P2
	ETHLOK			;INTERLOCK AGAINST SMP RACES
	MOVE	P1,.PBLPT(Q3)	;GET LENGTH OF PTT TABLE
	MOVE	P2,.PBVPT(Q3)	;GET VIRTUAL ADDRESS OF PTT TABLE
DELPT1:	JUMPE	P1,[ERRRET (UNIVP%,UNLETH##)] ;ERROR IF NO MORE ENTRIES
	LDB	T1,PTPENA	;GET PROTOCOL ENTRY ENABLED BIT
	JUMPE	T1,DELPT2	;SKIP CHECK IF NOT ENABLED
	CAMN	Q2,.PTPUB(P2)	;FOUND SUBJECT PROTOCOL USER BLOCK?
	JRST	DELPT3		;YES, EXIT LOOP
DELPT2:	ADDI	P2,.PTLEN	;BUMP PTT TABLE POINTER TO NEXT ENTRY
	SOJA	P1,DELPT1	;LOOP BACK TO CHECK ALL TABLE ENTRIES

DELPT3:	SETZ	T1,		;CLEAR THE PROTOCOL ENABLED FLAG
	DPB	T1,PTPENA	;...
	SOS	.PBCPT(Q3)	;ADJUST COUNT OF PTT ENTRIES
	ETHULK			;RELEASE SMP INTERLOCK
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  ADD MULTI-CAST ADDRESS TO MCAT TABLE


;ROUTINE TO ADD A MULTI-CAST ADDRESS TO THE MCAT FOR A PROTOCOL
;LINKAGE:
;	T1-T2/ MULTI-CAST ADDRESS
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,ADDMCA
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

ADDMCA:	PUSHJ	P,SAVE4##	;SAVE P1-P4
	DMOVE	P3,T1		;SAVE MULTI-CAST ADDRESS IN P3,P4
	ETHLOK			;INTERLOCK AGAINST SMP RACES
	SETZ	T2,		;USED TO REMEMBER FIRST FREE ENTRY IN MCAT
	MOVE	P1,.PBLMC(Q3)	;GET LENGTH OF MCAT TABLE
	MOVE	P2,.PBVMC(Q3)	;GET VIRTUAL ADDRESS OF MCAT TABLE
ADDMC1:	JUMPE	P1,ADDMC4	;EXIT LOOP IF NO MORE ENTRIES
	LDB	T1,MCPENA	;GET MULTI-CAST ADDRESS ENABLED BIT
	JUMPN	T1,ADDMC2	;IF ENABLED, GO CHECK FOR DUPLICATE
	SKIPN	T2		;ALREADY HAVE POINTER TO FREE ENTRY?
	MOVE	T2,P2		;NO, REMEMBER LOCATION OF FIRST FREE ENTRY
	JRST	ADDMC3		;AND CONTINUE CHECK REMAINDER OF TABLE
ADDMC2:	MOVE	T1,.MCHAD(P2)	;GET HIGH ORDER MULTI-CAST ADDRESS
	ANDX	T1,MCTHAD	;...
	CAME	T1,P3		;MATCH SUBJECT ADDRESS?
	JRST	ADDMC3		;NO, CONTINUE CHECKING
	MOVE	T1,.MCLAD(P2)	;GET LOW ORDER MULTI-CAST ADDRESS
	ANDX	T1,MCTLAD	;...
	CAME	T1,P4		;MATCH SUBJECT ADDRESS?
	ERRRET	(UNIMA%,UNLETH##) ;YES, INVALID MULTI-CAST ADDRESS
ADDMC3:	ADDI	P2,.MCLEN	;BUMP MCAT TABLE POINTER TO NEXT ENTRY
	SOJA	P1,ADDMC1	;LOOP BACK TO CHECK ALL TABLE ENTRIES

ADDMC4:	MOVE	P2,T2		;GET ADDRESS OF FREE MCAT ENTRY
	JUMPE	P2,[ERRRET (UNNRE%,UNLETH##)] ;ERROR IF NO FREE ENTRIES
	DMOVEM	P3,.MCHAD(P2)	;STORE MULTI-CAST ADDRESS IN TABLE
	MOVEI	T1,1		;SET THE MULTI-CAST ADDRESS ENABLED FLAG
	DPB	T1,MCPENA	;...
	AOS	.PBCMC(Q3)	;ADJUST COUNT OF MCAT ENTRIES
	ETHULK			;RELEASE SMP INTERLOCK
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  DELETE MULTI-CAST ADDRESS FROM MCAT TABLE


;ROUTINE TO DELETE A MULTI-CAST ADDRESS FROM THE MCAT
;LINKAGE:
;	T1-T2/ MULTI-CAST ADDRESS
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,DELMCA
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS

DELMCA:	PUSHJ	P,SAVE4##	;SAVE P1-P4
	DMOVE	P3,T1		;SAVE MULTI-CAST ADDRESS IN P3,P4
	ETHLOK			;INTERLOCK AGAINST SMP RACES
	MOVE	P1,.PBLMC(Q3)	;GET LENGTH OF MCAT TABLE
	MOVE	P2,.PBVMC(Q3)	;GET VIRTUAL ADDRESS OF MCAT TABLE
DELMC1:	JUMPE	P1,[ERRRET (UNIMA%,UNLETH##)] ;ERROR IF NO MORE ENTRIES
	LDB	T1,MCPENA	;GET MULTI-CAST ADDRESS ENABLED BIT
	JUMPE	T1,DELMC2	;IF NOT ENABLED, SKIP CHECK
	MOVE	T1,.MCHAD(P2)	;GET MULTI-CAST ADDRESS
	ANDX	T1,MCTHAD	;...
	ANDX	T2,MCTLAD	;...
	CAMN	T1,P3		;MATCH SUBJECT ADDRESS?
	CAME	T2,P4		;...
	SKIPA			;NO, KEEP CHECKING
	JRST	DELMC3		;YES, EXIT LOOP
DELMC2:	ADDI	P2,.MCLEN	;BUMP MCAT TABLE POINTER TO NEXT ENTRY
	SOJA	P1,DELMC1	;LOOP BACK TO CHECK ALL TABLE ENTRIES

DELMC3:	SETZ	T1,		;CLEAR THE MULTI-CAST ADDRESS ENABLED FLAG
	DPB	T1,MCPENA	;...
	SOS	.PBCMC(Q3)	;ADJUST COUNT OF MCAT ENTRIES
	ETHULK			;RELEASE SMP INTERLOCK
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  UPDATE KLNI COUNTERS AREA


;ROUTINE CALLED TO UPDATE KLNI COUNTERS AREA (.PBCTR) FROM
;KLNI COUNTERS DATA BUFFER.
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,UPDCTR
;RETURNS:
;	CPOPJ ALWAYS

UPDCTR:	PUSHJ	P,SAVE3##	;SAVE P1-P3
	MOVE	P1,.PBLCD(Q3)	;GET LENGTH OF KLNI COUNTERS AREA
	MOVE	P2,.PBVCD(Q3)	;GET ADDRESS OF COUNTERS DATA BUFFER
	MOVE	P3,.PBVCT(Q3)	;AND ADDRESS OF KLNI COUNTERS AREA
	SETZ	T1,		;FETCH AND ZERO MULTICAST CHECKSUM ERROR COUNT
	EXCH	T1,.PBMCE(Q3)	;...
	MOVNS	T1		;NEGATE
	ADDB	T1,.KCRCF(P2)	;ADJUST COUNT OF RECEIVE FAILURES
	JUMPG	T1,UPDCT1	;CONTINUE IF COUNT POSITIVE
	SETZM	.KCRCF(P2)	;CLEAR RECEIVE FAILURE COUNT
	SETZM	.KCRFM(P2)	;AND RECEIVE FAILURE BIT MASK
UPDCT1:	MOVE	T1,(P2)		;GET COUNTER VALUE FROM DATA BUFFER
	ADDM	T1,(P3)		;UPDATE KLNI COUNTERS AREA
	AOJ	P2,		;UPDATE POINTER TO COUNTERS DATA BUFFER
	AOJ	P3,		;AND POINTER TO KLNI COUNTERS AREA
	SOJG	P1,UPDCT1	;LOOP BACK TO UPDATE ALL COUNTERS
	MOVE	P2,.PBVCD(Q3)	;GET ADDRESS OF COUNTERS DATA BUFFER
	MOVE	P3,.PBVCT(Q3)	;AND ADDRESS OF KLNI COUNTERS AREA
	MOVE	T1,.KCXFM(P3)	;GET TRANSMIT FAILURE BIT MASK
	SUB	T1,.KCXFM(P2)	;COMPENSATE FOR PREVIOUS LOOP
	IOR	T1,.KCXFM(P2)	;UPDATE TRANSMIT FAILURE BIT MASK
	MOVEM	T1,.KCXFM(P3)	;...
	MOVE	T1,.KCRFM(P3)	;GET RECEIVE FAILURE BIT MASK
	SUB	T1,.KCRFM(P2)	;COMPENSATE FOR PREVIOUS LOOP
	IOR	T1,.KCRFM(P2)	;UPDATE RECEIVE FAILURE BIT MASK
	MOVEM	T1,.KCRFM(P3)	;...
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  GENERATE BSD CHAIN


;ROUTINE TO GENERATE A BSD CHAIN FOR A DATAGRAM
;LINKAGE:
;	T1/ ADDRESS OF FIRST MSD
;	PUSHJ	P,GENBSC
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS WITH:
;	T1/ COMPUTED SIZE OF DATAGRAM
;	T2/ ADDRESS OF START OF BSD CHAIN

GENBSC:	PUSHJ	P,SAVE4##	;SAVE P1-P4
	SETZ	P1,		;START WITH COMPUTED DATAGRAM SIZE OF ZERO
	MOVE	P2,T1		;SAVE ADDRESS OF FIRST MSD
	SETZB	P3,P4		;ZERO POINTERS TO BSD CHAIN
	JUMPE	T2,GENBC1	;JUMP IF NOT DOING PADDING ON RECEIVE
	MOVEI	T2,.BSLEN	;GET SIZE OF BSD
	PUSHJ	P,GETNWZ##	;ALLOCATE ZEROED CORE FOR BSD
	  ERRRET (UNRES%)	;ERROR, NO RESOURCES
	MOVE	P3,T1		;SAVE START ADDRESS OF BSD CHAIN IN P3
	MOVE	P4,T1		;AND CURRENT BSD ADDRESS IN P4
	MOVEI	T1,2		;SET UP BSD FOR TWO LENGTH BYTES
	DPB	T1,BSPSGL	;...
	MAP	T1,.BSRWD(P4)	;GET PHYSICAL ADDRESS OF BSD DATA WORD
	TXZ	T1,NADBTS	;...
	DPB	T1,BSPSBA	;STORE AS BASE ADDRESS OF THIS SEGMENT
	ADDI	P1,2		;ADJUST COMPUTED DATAGRAM SIZE
GENBC1:	SKIPA			;ENTER MAIN LOOP
GENBC2:	LOAD	P2,MDNXT,(P2)	;GET ADDRESS OF NEXT MSD
	JUMPE	P2,GENBC5	;EXIT LOOP AT END OF BSD CHAIN
	LOAD	T1,MDBYT,(P2)	;GET BYTE COUNT
	JUMPE	T1,GENBC2	;LOOP BACK IF EMPTY MSD
	ADD	P1,T1		;ADJUST COMPUTED DATAGRAM SIZE
	LDB	T2,[POINT 6,MD.AUX(P2),11] ;GET "S" FIELD FROM BYTE POINTER
	CAIE	T2,^D8		;EIGHT BIT BYTES?
	ERRRET	(UNIBP%,GENBCX)	;NO, ILLEGAL BYTE POINTER
	LDB	T2,[POINT 6,MD.AUX(P2),5] ;GET "P" FIELD FROM BYTE POINTER
	IDIVI	T2,^D8		;COMPUTE BYTE OFFSET
	SUBI	T2,4		;...
	MOVMS	T2		;...
	CAIE	T3,4		;CORRECTLY ALIGNED BYTES?
	ERRRET	(UNIBP%,GENBCX)	;NO, ILLEGAL BYTE POINTER
	HRRZ	T3,MD.AUX(P2)	;GET ANY OFFSET FROM BYTE POINTER
	ADD	T3,MD.ALA(P2)	;COMPUTE ADDRESS OF DATA AREA
	TLNE	T3,777740	;ANY BITS EXCEPT FOR ADDRESS PRESENT?
	ERRRET	(UNIBP%,GENBCX)	;YES, ILLEGAL BYTE POINTER
	TRZE	T2,4		;WORD ALIGNED ON SECOND WORD OF DATA?
	ADDI	T3,1		;YES, BUMP ADDRESS

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

GENBC3:	PUSHJ	P,GENBSD	;GENERATE NEXT BSD FOR THIS SEGMENT
	  JRST	GENBCX		;ERROR, ERROR CODE IN T1
	JUMPE	P3,[MOVE   P3,T4 ;IF FIRST BSD, SET UP INITIAL POINTER
		    JRST   GENBC4] ;AND CONTINUE
	PUSH	P,T4		;SAVE ADDRESS OF NEW BSD
	MOVEM	T4,.BSVNB(P4)	;LINK NEW BSD TO END OF CURRENT BSD CHAIN
	MAP	T4,0(T4)	;GET PHYSICAL ADDRESS OF BSD
	TXZ	T4,NADBTS	;...
	DPB	T4,BSPNXT	;LINK TO PREVIOUS CHAIN
	POP	P,T4		;GET BACK BSD ADDRESS
GENBC4:	MOVE	P4,T4		;UPDATE POINTER TO CURRENT BSD
	JUMPN	T1,GENBC3	;LOOP BACK TO COMPLETE THIS DATA SEGMENT
	JRST	GENBC2		;THEN LOOP BACK FOR REMAINDER OF BSD CHAIN

GENBC5:	MOVE	T1,P1		;GET COMPUTED SIZE OF DATAGRAM
	MOVE	T2,P3		;AND ADDRESS OF BSD CHAIN
	PJRST	CPOPJ1##	;RETURN

GENBCX:	PJUMPE	P3,CPOPJ##	;JUST RETURN IF NO BSD CHAIN
	PUSH	P,T1		;SAVE ERROR CODE
	MOVE	T1,P3		;GET START OF BSD CHAIN
	PUSHJ	P,GIVBSC	;RELEASE BSD CHAIN
	PJRST	TPOPJ##		;RESTORE ERROR CODE AND RETURN
;ROUTINE CALLED TO BUILD A BUFFER SEGMENT DESCRIPTOR GIVEN
;A BYTE COUNT, BYTE OFFSET, AND ADDRESS OF A DATAGRAM SEGMENT
;LINKAGE:
;	T1/ BYTE COUNT OF DATAGRAM SEGMENT
;	T2/ BYTE OFFSET TO DATAGRAM SEGMENT
;	T3/ ADDRESS OF DATAGRAM SEGMENT
;	PUSHJ	P,GENBSD
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS WITH:
;	T1/ UPDATED BYTE COUNT OF DATAGRAM SEGMENT
;	T2/ UPDATED BYTE OFFSET TO DATAGRAM SEGMENT
;	T3/ UPDATED ADDRESS OF DATAGRAM SEGMENT
;	T4/ ADDRESS OF BUFFER SEGMENT DESCRIPTOR

GENBSD:	PUSHJ	P,SAVE4##	;SAVE P1-P4
	DMOVE	P1,T1		;SAVE BYTE COUNT AND BYTE OFFSET
	MOVE	P3,T3		;AND ADDRESS OF DATAGRAM SEGMENT
	JUMPE	P2,GENBS1	;JUMP IF WORD ALIGNED DATA
	MOVEI	T2,.BSLEN	;GET LENGTH OF BSD
	PUSHJ	P,GETNWZ##	;ALLOCATE ZEROED CORE FOR BSD
	  ERRRET (UNRES%)	;ERROR, NO RESOURCES
	MOVE	P4,T1		;SAVE ADDRESS OF BSD IN P4
	MOVEI	T1,4		;COMPUTE BYTE COUNT OF MIS-ALIGNED DATA
	SUB	T1,P2		;...
	CAMLE	T1,P1		;COMPARE TO SEGMENT'S BYTE COUNT
	MOVE	T1,P1		;USE SMALLER OF THE TWO
	DPB	T1,BSPSGL	;STORE BYTE COUNT INTO BSD
	SUB	P1,T1		;UPDATE SEGMENT BYTE COUNT
	MOVE	T1,P2		;GET BYTE OFFSET
	IMULI	T1,^D8		;COMPUTE SHIFT COUNT TO ALIGN DATA
	MOVE	T2,0(P3)	;FETCH FIRST DATA WORD
	LSH	T2,(T1)		;WORD ALIGN THE DATA
	MOVEM	P2,.BSRWB(P4)	;SAVE RE-ALIGNED WORD BYTE OFFSET
	MOVEM	P3,.BSRWA(P4)	;AND RE-ALIGNED WORD ADDRESS
	MOVEM	T2,.BSRWD(P4)	;AND RE-ALIGNED WORD DATA
	MAP	T1,.BSRWD(P4)	;GET PHYSICAL ADDRESS OF RE-ALIGNED DATA
	TXZ	T1,NADBTS	;...
	DPB	T1,BSPSBA	;STORE IN BSD
	SETZ	P2,		;ZERO BYTE OFFSET AS NOW WORD ALIGNED
	ADDI	P3,1		;UPDATE SEGMENT ADDRESS
	JRST	GENBS3		;AND RETURN

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

GENBS1:	MOVEI	T2,.BSLEN	;GET LENGTH OF BSD
	PUSHJ	P,GETNWZ##	;ALLOCATE ZEROED CORE FOR BSD
	  ERRRET (UNRES%)	;ERROR, NO RESOURCES
	MOVE	P4,T1		;SAVE BSD ADDRESS IN P4
	MAP	T1,0(P3)	;GET PHYSICAL ADDRESS OF DATA SEGMENT
	TXZ	T1,NADBTS	;...
	DPB	T1,BSPSBA	;STORE IN BSD
	XMOVEI	T2,PAGSIZ##(T1)	;CALCULATE NEXT PAGE BOUNDARY
	TRZ	T2,PG.BDY##	;...
	SUB	T2,T1		;CONVERT INTO WORD COUNT
GENBS2:	IMULI	T2,4		;CONVERT WORD COUNT INTO BYTE COUNT
	CAMLE	T2,P1		;COMPARE TO SEGMENT BYTE COUNT
	MOVE	T2,P1		;USE SMALLER OF THE TWO
	LDB	T3,BSPSGL	;GET BSD SEGMENT LENGTH
	ADD	T3,T2		;ADJUST COUNT
	DPB	T3,BSPSGL	;...
	SUB	P1,T2		;UPDATE DATAGRAM SEGMENT BYTE COUNT
	IDIVI	T2,4		;CONVERT BYTE COUNT INTO WORD COUNT
	ADD	P3,T2		;UPDATE SEGMENT DATA ADDRESS
	JUMPE	P1,GENBS3	;RETURN AT END OF DATAGRAM SEGMENT
	MAP	T2,0(P3)	;GET PHYSICAL ADDRESS OF NEXT DATA CHUNK
	TXZ	T2,NADBTS	;...
	SUB	T2,T1		;COMPUTE OFFSET FROM PREVIOUS PAGE
	SKIPL	T2		;PHYSICALLY CONTINGUOUS WITH PREVIOUS PAGE?
	CAILE	T2,PAGSIZ##	;...
	JRST	GENBS3		;NO, RETURN
	MOVEI	T2,PAGSIZ##	;GET SIZE OF NEXT POSSIBLE CHUNK
	MOVE	T1,T2		;GET BASE ADDRESS OF NEXT PAGE
	JRST	GENBS2		;LOOP BACK TO PROCESS SEGMENT
GENBS3:	DMOVE	T1,P1		;GET UPDATED BYTE COUNT AND BYTE OFFSET
	DMOVE	T3,P3		;AND UPDATED SEGMENT ADDRESS AND BSD ADDRESS
	PJRST	CPOPJ1##	;RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  RELEASE A BSD CHAIN


;ROUTINE CALLED TO RELEASE A BSD CHAIN
;LINKAGE:
;	T1/ ADDRESS OF BSD CHAIN
;	PUSHJ	P,GIVBSC
;RETURNS:
;	CPOPJ ALWAYS

GIVBSC:	PUSHJ	P,SAVE1##	;SAVE P1
	MOVE	P1,T1		;SAVE ADDRESS OF FIRST BSD
GIVBC1:	PUSH	P,.BSVNB(P1)	;SAVE ADDRESS OF NEXT BSD
	MOVEI	T1,.BSLEN	;GET LENGTH OF BSD
	MOVE	T2,P1		;AND ADDRESS OF CURRENT BSD
	PUSHJ	P,GIVNWS##	;RELEASE THE CORE
	POP	P,P1		;GET ADDRESS OF NEXT BSD
	JUMPN	P1,GIVBC1	;LOOP BACK TO RELEASE ENTIRE CHAIN
	POPJ	P,		;RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  ALLOCATE KLNI COMMAND BUFFER


;ROUTINE CALLED TO ALLOCATE A KLNI COMMAND BUFFER
;LINKAGE:
;	T1/ KLNI COMMAND OPCODE
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,GETCMD
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	T1/ ERROR CODE (UNXXX%)
;	CPOPJ1 ON SUCCESS WITH:
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER

GETCMD:	PUSH	P,T1		;SAVE KLNI COMMAND CODE
	MOVEI	T2,.CMLEN	;GET LENGTH OF COMMAND BUFFER
	PUSHJ	P,GETNWZ##	;ALLOCATE CORE FOR COMMAND BUFFER
	  PJRST	[POP	P,(P)	;ERROR, CLEAR STACK
		 ERRRET (UNRES%)] ;AND GIVE ERROR RETURN
	MOVE	Q1,T1		;GET ADDRESS OF COMMAND BUFFER
	POP	P,T1		;GET BACK KLNI COMMAND CODE
	DPB	T1,CMPCMD	;STORE OPCODE IN KLNI COMMAND BUFFER
	MOVX	T1,CMFRSP	;SET RESPONSE REQUIRED FLAG
	DPB	T1,CMPFLG	;...
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  RELEASE KLNI COMMAND BUFFER


;ROUTINE CALLED TO RELEASE A KLNI COMMAND BUFFER
;LINKAGE:
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	PUSHJ	P,GIVCMD
;RETURNS:
;	CPOPJ ALWAYS

GIVCMD:	PJUMPE	Q1,CPOPJ##	;RETURN IF NO BUFFER ALLOCATED
	MOVEI	T1,.CMLEN	;GET LENGTH OF COMMAND BUFFER
	MOVE	T2,Q1		;AND ADDRESS OF COMMAND BUFFER
	SETZ	Q1,		;NO KLNI COMMAND BUFFER
	PJRST	GIVNWS##	;RELEASE CORE AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  QUEUE KLNI COMMAND TO FREE QUEUE


;ROUTINE TO QUEUE A COMMAND BUFFER TO A PROTOCOL'S FREE QUEUE
;LINKAGE:
;	T1/ ADDRESS OF CALLBACK ROUTINE
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	Q2/ ADDRESS OF PROTOCOL USER BLOCK
;	PUSHJ	P,KNICMF
;RETURNS:
;	CPOPJ ALWAYS

KNICMF:	MOVEM	T1,.CMCBA(Q1)	;STORE CALLBACK ROUTINE ADDRESS
	AOS	.PBKFC(Q3)	;UPDATE COUNT OF FREE COMMANDS QUEUED
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PBKFT(Q3)	;REMEMBER WHEN LAST COMMAND QUEUED
	XMOVEI	T1,.PUFQH(Q2)	;GET ADDRESS OF PROTOCOL'S FREE QUEUE
	XMOVEI	T2,.CMQUE(Q1)	;AND ADDRESS OF QUEUE ENTRY
	PJRST	PUTQUE		;INSERT COMMAND INTO QUEUE AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  QUEUE KLNI COMMAND TO COMMAND QUEUE


;ROUTINE TO QUEUE A COMMAND BUFFER TO THE KLNI'S COMMAND QUEUE
;LINKAGE:
;	T1/ ADDRESS OF CALLBACK ROUTINE
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNICMD
;RETURNS:
;	CPOPJ ALWAYS

KNICMD:	MOVEM	T1,.CMCBA(Q1)	;STORE CALLBACK ROUTINE ADDRESS
	XMOVEI	T1,.PBCMQ(Q3)	;GET ADDRESS OF KLNI COMMAND QUEUE
	XMOVEI	T2,.CMQUE(Q1)	;AND ADDRESS OF QUEUE ENTRY
	PUSHJ	P,PUTQUE	;INSERT COMMAND INTO QUEUE
	LDB	T1,PBPCPU	;GET CPU NUMBER OF KLNI
	CAME	T1,.CPCPN##	;KLNI ON OUR CPU?
	JRST	KNICM1		;NO, GO DO QUEUED I/O
	AOS	.PBKCC(Q3)	;UPDATE COUNT OF KLNI COMMANDS QUEUED
	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	MOVEM	T1,.PBKCT(Q3)	;REMEMBER WHEN LAST COMMAND QUEUED
	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	CONO	KNI,CO.CQA!CO.BTS(T1) ;SET COMMAND QUEUE AVAILABLE FLAG
	POPJ	P,		;AND RETURN

KNICM1:	AOS	.PBKQC(Q3)	;UPDATE COUNT OF QUEUED I/O COMMANDS
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PBKQT(Q3)	;REMEMBER WHEN LAST COMMAND QUEUED
	MOVX	T1,PBSQIO	;SET QUEUED I/O FLAG IN PCB
	IORM	T1,.PBSTS(Q3)	;...
	MOVE	T1,.CPQPC##	;GET THIS CPU'S QUEUED I/O FLAG
	IORM	T1,DOORBL##	;SET QUEUED I/O FLAG
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  MISCELLANEOUS


;ROUTINE TO SWAP HIGH AND LOW ORDER BYTES OF A NUMBER
;LINKAGE:
;	T1/ NUMBER
;	PUSHJ	P,SWAB
;RETURNS:
;	CPOPJ ALWAYS WITH:
;	T1/ NUMBER

SWAB:	DPB	T1,[POINT 8,T1,19] ;REPOSITION LOW ORDER BYTE
	LSH	T1,-^D8		;JUSTIFY RESULT
	POPJ	P,		;RETURN


;ROUTINE TO CONVERT TRANSMIT/RECEIVE DATAGRAM ERROR CODE INTO
;APPROPRIATE ETHSER ERROR CODE
;LINKAGE:
;	T1/ KLNI ERROR CODE
;	PUSHJ	P,CVTETY
;RETURNS:
;	CPOPJ ALWAYS WITH:
;	T1/ ETHSER ERROR CODE (UNXXX%)

CVTETY:	MOVEI	T1,UNRAB%	;THESE HAVE TO BE DEFINED
	POPJ	P,		;RETURN
	SUBTTL	KLNI INTERRUPT SERVICE  --  INTERRUPT DISPATCH


;ROUTINE CALLED FROM CONSO SKIP CHAIN TO PROCESS KLNI INTERRUPT
;LINKAGE:
;	T1/ CONI STATUS WORD
;	T2/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNIINT
;RETURNS:
;	CPOPJ ALWAYS

KNIINT::SE1ENT			;NEED TO RUN IN NON-ZERO SECTION
	MOVE	Q3,T2		;COPY PCB ADDRESS TO STANDARD REGISTER
	MOVX	T2,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	TDNE	T2,.PBSTS(Q3)	;...
	POPJ	P,		;YES, RETURN NOW
	SETZB	Q1,Q2		;START CLEAN
	MOVEM	T1,.PBCLI(Q3)	;SAVE CONI IN PORT CONTROL BLOCK
	TXNE	T1,CI.CPE!CI.MER!CI.EPE!CI.DPE ;ANY BAD ERROR?
	JRST	KNIIT2		;YES
	MOVX	T1,PBSQIO	;ANY QUEUED I/O REQUESTS?
	TDNN	T1,.PBSTS(Q3)	;...
	JRST	KNIIT1		;NO, CONTINUE NORMALLY
	ANDCAM	T1,.PBSTS(Q3)	;YES, CLEAR QUEUED I/O REQUEST FLAG
	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	CONO	KNI,CO.CQA!CO.BTS(T1) ;SET COMMAND QUEUE AVAILABLE FLAG
KNIIT1:	MOVX	T1,CI.FQE	;FREE QUEUE ERROR?
	TDNE	T1,.PBCLI(Q3)	;...
	PUSHJ	P,KNEFQE	;YES, PROCESS
	PUSHJ	P,KNIRQA	;PROCESS ANY RESPONSES
	POPJ	P,		;DISMISS THE INTERRUPT

KNIIT2:	PUSHJ	P,KNESTP	;PERFORM KLNI ERROR STOP PROCESSING
	MOVX	T1,CI.CPE	;CRAM PARITY ERROR?
	TDNE	T1,.PBCLI(Q3)	;...
	PUSHJ	P,KNECPE	;YES
	MOVX	T1,CI.MER	;MBUS ERROR?
	TDNE	T1,.PBCLI(Q3)	;...
	PUSHJ	P,KNEMBE	;YES
	MOVX	T1,CI.EPE	;EBUS PARITY ERROR?
	TDNE	T1,.PBCLI(Q3)	;...
	PUSHJ	P,KNEEPE	;YES
	MOVX	T1,CI.DPE	;DATA PATH ERROR?
	TDNE	T1,.PBCLI(Q3)	;...
	PUSHJ	P,KNEDPE	;YES
	POPJ	P,		;DISMISS THE INTERRUPT
	SUBTTL	KLNI INTERRUPT SERVICE  --  PROCESS RESPONSE QUEUE


;ROUTINE CALLED TO PROCESS KLNI RESPONSE QUEUE
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNIRQA
;RETURNS:
;	CPOPJ ALWAYS

KNIRQA:	PUSHJ	P,SAVE1##	;SAVE P1
KNIRQ1:	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	MOVX	T2,PBSRUN	;IS KLNI RUNNING?
	TDNE	T2,.PBSTS(Q3)	;...
	CONO	KNI,CO.RQA!CO.BTS(T1) ;YES, CLEAR RESPONSE QUEUE AVAILABLE
	XMOVEI	T1,.PBRSQ(Q3)	;GET VIRTUAL ADDRESS OF QUEUE HEADER
	PUSHJ	P,REMQUE	;REMOVE NEXT ENTRY FROM RESPONSE QUEUE
	  POPJ	P,		;RESPONSE QUEUE EMPTY, RETURN
	XMOVEI	Q1,-.CMQUE(T2)	;GET ADDRESS OF KLNI COMMAND BUFFER
	LDB	T1,CMPCMD	;GET COMMAND OPCODE
	SKIPE	T1		;VALID OPCODE?
	CAILE	T1,CMOMAX	;...
	STOPCD	.+1,DEBUG,KNIICO ;++INVALID COMMAND OPCODE
	AOS	.PBKRC(Q3)	;UPDATE COUNT OF KLNI RESPONSES PROCESSED
	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	MOVEM	T1,.PBKRT(Q3)	;REMEMBER WHEN LAST RESPONSE PROCESSED
	MOVE	T1,.CMCBA(Q1)	;GET CALLBACK ROUTINE ADDRESS
	MOVE	Q2,.CMPUB(Q1)	;GET ADDRESS OF PROTOCOL USER BLOCK
	MOVE	P1,.CMEAB(Q1)	;AND ADDRESS OF EA BLOCK
	PUSHJ	P,(T1)		;CALL CALLBACK ROUTINE
	  JFCL			;...
	JRST	KNIRQ1		;LOOP BACK TO EMPTY RESPONSE QUEUE
	SUBTTL	KLNI INTERRUPT SERVICE  --  FREE QUEUE ERROR


;ROUTINE CALLED TO PROCESS A FREE QUEUE ERROR
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNEFQE
;RETURNS:
;	CPOPJ ALWAYS

KNEFQE:	AOS	.PBFQE(Q3)	;COUNT A FREE QUEUE ERROR
	MOVE	T1,.PBPIA(Q3)	;GET PI ASSIGNMENT
	CONO	KNI,CO.FQE!CO.BTS(T1) ;CLEAR FREE QUEUE ERROR
	POPJ	P,		;RETURN
	SUBTTL	KLNI INTERRUPT SERVICE  --  CRAM PARITY ERROR


;ROUTINE CALLED TO PROCESS A CRAM PARITY ERROR
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNECPE
;RETURNS:
;	CPOPJ ALWAYS
;NOTE:
;MUST BE CALLED AFTER REPORT AS IT DEPENDS ON SOME
;OF THE DATA RECORDED GATHERED BY REPORT

KNECPE:	MOVE	T1,.PBCRA(Q3)	;GET THE CRAM ADDRESS
	CAIL	T1,PPEFST	;IS THIS A PLANNED CRAM PARITY ERROR?
	CAILE	T1,PPELST	;...
	STOPCD	CPOPJ,INFO,KNICPE,CPETYP ;++KLNI CRAM PARITY ERROR
	STOPCD	CPOPJ,INFO,KNIHLT,HLTTYP ;++KLNI MICROPROCESSOR HALT

;ROUTINE CALLED FROM DIE ON AN UNPLANNED CRAM PARITY ERROR

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

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

;ROUTINE CALLED FROM DIE ON A PLANNED CRAM PARITY ERROR

HLTTYP:	PUSHJ	P,INLMES##	;PRINT TEXT
	  ASCIZ	/NIA20 microprocessor halted - /
	MOVE	T1,.PBCRA(Q3)	;GET THE CRAM ADDRESS
	HRRZ	T1,CPETXT-PPEFST(T1) ;GET ERROR TEXT ADDRESS
	PJRST	CONMES##	;PRINT AND RETURN (DIE ADDS CRLF)
;TABLE OF TEXT STRINGS BASED ON CRAM ADDRESS FOR PLANNED CRAM PARITY ERRORS

DEFINE	ERRS,<

	XALL			;;LIST GENERATED TABLE

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

	SALL			;;TURN LISTING BACK OFF

>; END DEFINE ERRS
;GENERATE ERROR TEXT TABLE

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

CPETXT:	ERRS			;GENERATE ERROR TEXT TABLE
IF1,<IFN <<PPELST-PPEFST>-<.-CPETXT-1>>,<PRINTX ?Table CPETXT is missing entries>>
	SUBTTL	KLNI INTERRUPT SERVICE  --  MBUS ERROR


;ROUTINE CALLED TO PROCESS AN MBUS ERROR
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNEMBE
;RETURNS:
;	CPOPJ ALWAYS
;NOTE:
;MUST BE CALLED AFTER REPORT AS IT DEPENDS ON SOME
;OF THE DATA WHICH REPORT STORES IN THE PCB

KNEMBE:	STOPCD	CPOPJ##,INFO,KNIMBE ;++KLNI MBUS ERROR
	SUBTTL	KLNI INTERRUPT SERVICE  --  EBUS PARITY ERROR


;ROUTINE CALLED TO PROCESS AN EBUS PARITY ERROR
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNEEPE
;RETURNS:
;	CPOPJ ALWAYS
;NOTE:
;MUST BE CALLED AFTER REPORT AS IT DEPENDS ON SOME
;OF THE DATA WHICH REPORT STORES IN THE PCB

KNEEPE:	STOPCD	CPOPJ##,INFO,KNIEPE ;++KLNI EBUS PARITY ERROR
	SUBTTL	KLNI INTERRUPT SERVICE  --  DATA PATH ERROR


;ROUTINE CALLED TO PROCESS AN DATA PATH ERROR
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNEDPE
;RETURNS:
;	CPOPJ ALWAYS
;NOTE:
;MUST BE CALLED AFTER REPORT AS IT DEPENDS ON SOME
;OF THE DATA WHICH REPORT STORES IN THE PCB

KNEDPE:	STOPCD	CPOPJ##,INFO,KNIDPE ;++KLNI DATA PATH ERROR
	SUBTTL	KLNI INTERRUPT SERVICE  --  KLNI ERROR STOP PROCESSING


;ROUTINE CALLED WHEN KLNI STOPS
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNESTP
;RETURNS:
;	CPOPJ ALWAYS

KNESTP:	PUSHJ	P,STPKNI	;STOP KLNI MICROCODE AND CLEAN UP
	PUSHJ	P,REPORT	;MAKE AN ERROR.SYS ENTRY
	MOVE	T1,.PBKHT(Q3)	;GET UPTIME WHEN KLNI HALTED
	SUB	T1,.PBKST(Q3)	;CALCULATE TIME KLNI WAS UP
	IMULI	T1,^D1000	;CONVERT TO MILLISECONDS
	IDIV	T1,TICSEC##	;...
	CAXL	T1,UPTTIM	;WAS KLNI UP MINIMUM LENGTH OF TIME?
	PJRST	RLDKND		;YES, REQUEST KLNI DUMP AND RELOAD
	STOPCD	CPOPJ##,INFO,KNIARD ;++KLNI AUTO-RELOAD DISABLED
	SUBTTL	KLNI INTERRUPT SERVICE  --  SPEAR ERROR LOGGING


;ROUTINE TO RECORD PORT ERROR INFORMATION AND MAKE A SPEAR ENTRY
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,REPORT
;RETURNS:
;	CPOPJ ALWAYS

REPORT:	AOS	.PBKEC(Q3)	;UPDATE COUNT OF KLNI ERRORS
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PBKET(Q3)	;REMEMBER TIME OF LAST KLNI ERROR
	MOVE	T1,.PBCLI(Q3)	;GET CONI FROM THIS INTERRUPT
	MOVEM	T1,.PBCLE(Q3)	;SAVE AS CONI FROM LAST ERROR
	PUSHJ	P,REDLAR	;READ LATCHED ADDRESS REGISTER CONTENTS
	  SETZ	T1,		;ERROR?
	MOVEM	T1,.PBCRA(Q3)	;SAVE IT
	PUSHJ	P,REDKNI	;READ THAT LOCATION'S CONTENTS
	  SETZB	T2,T3		;ERROR?
	DMOVEM	T2,.PBCRC(Q3)	;SAVE IN PCB
	MOVE	T1,.CPEPT##	;GET OUR EPT ADDRESS
	DMOVE	T2,KNIICH*4(T1)	;GET FIRST TWO WORDS OF CHANNEL LOGOUT AREA
	DMOVEM	T2,.PBLG0(Q3)	;SAVE THEM
	MOVE	T2,KNIICH*4+2(T1) ;GET THIRD WORD
	MOVEM	T2,.PBLG2(Q3)	;SAVE IT
	MOVE	T1,.PBCCW(Q3)	;GET PORT'S CCW
	MOVEM	T1,.PBECW(Q3)	;SAVE IT

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

	MOVEI	T1,KP%LEN	;LENGTH OF ERROR BLOCK
	PUSHJ	P,ALCSEB##	;ALLOCATE SYSTEM ERROR BLOCK
	  POPJ	P,		;SORRY, WE TRIED
	MOVEI	T2,SEC%KP	;GET THE ERROR CODE
	DPB	T2,[POINT 9,.EBTYP(T1),8] ;STORE THE TYPE IN THE HEADER
	MOVE	T2,.PBCLE(Q3)	;GET CONI ON ERROR
	MOVEM	T2,.EBHDR+KP%CSR(T1) ;STORE IT
	MOVE	T2,.PBUVR(Q3)	;GET KLNI MICROCODE VERSION
	TXO	T2,KP%NI!FLD(KNIICH,KP%CHN) ;SET KLNI FLAG AND RH20 CHANNEL
	MOVEM	T2,.EBHDR+KP%VER(T1) ;STORE IT
	MOVEI	T2,2(P1)	;GET DISPOSITION CODE
	MOVEM	T2,.EBHDR+KP%DSP(T1) ;STORE IT
	MOVE	T2,.PBCRA(Q3)	;GET CRAM ADDRESS
	MOVEM	T2,.EBHDR+KP%CRA(T1) ;STORE IT
	DMOVE	T2,.PBCRC(Q3)	;GET CRAM CONTENTS
	DMOVEM	T2,.EBHDR+KP%CRD(T1) ;STORE IT
	DMOVE	T2,.PBLG0(Q3)	;GET FIRST TWO LOGOUT WORDS
	DMOVEM	T2,.EBHDR+KP%LG0(T1) ;STORE THEM
	MOVE	T2,.PBLG2(Q3)	;GET THIRD LOGOUT WORD
	MOVEM	T2,.EBHDR+KP%LG2(T1) ;STORE IT
	MOVE	T2,.PBECW(Q3)	;GET PORT'S CCW AT ERROR
	MOVEM	T2,.EBHDR+KP%ECW(T1) ;STORE IT
	DMOVE	T2,.PBER0(Q3)	;GET ERROR WORDS
	DMOVEM	T2,.EBHDR+KP%PE0(T1) ;STORE THEM
	PJRST	QUESEB##	;QUEUE THE BLOCK AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  DIAG. UUO


;DIAG. UUO FUNCTIONS FOR DIAGNOSTIC CONTROL OF A KLNI
;LINKAGE: (CALL FROM DIAUUO ON CORRECT CPU)
;	P1/ ADDRESS OF CHANNEL DATA BLOCK
;	P2/ DIAG. UUO FUNCTION CODE
;	M/ ADDRESS OF USER ARGUMENT LIST
;	PUSHJ	P,KNIDIA
;RETURNS:
;	CPOPJ ON ERROR WITH:
;	USER AC/ ERROR CODE (DIXXX%)
;	CPOPJ1 ON SUCCESS

	ERCODX	DIAPRV,DIANP%	;INSUFFICIENT PRIVILEGES
	ERCODX	DIAIAL,DIAIA%	;INVALID ARGUMENT LIST LENGTH
	ERCODX	DIAICN,DIAIC%	;ILLEGAL CONTROLLER NUMBER
	ERCODX	DIAILF,DIAIF%	;ILLEGAL FUNCTION
	ERCODX	DIANKC,DIANK%	;NO KLNI PORT ON THIS CPU

KNIDIA::PUSHJ	P,SAVE4##	;SAVE P1-P4
	PUSHJ	P,SAVQ##	;AND Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	MOVX	T1,JP.POK	;JOB HAVE SUFFICIENT PRIVILEGES?
	PUSHJ	P,PRVBIT##	;...
	  SKIPA			;YES, CONTINUE
	PJRST	DIAPRV		;NO, GIVE ERROR RETURN
	MOVE	Q3,CHNPCB##(P1)	;GET THE PORT CONTROL BLOCK ADDRESS
	CAIL	P2,17		;THESE FUNCTIONS DON'T REQUIRE MAINTENANCE MODE
	CAILE	P2,23		;...
	SKIPA			;NOT A SPECIAL FUNCTION
	PJRST	@DIAFNC(P2)	;YES, DISPATCH NOW
	MOVX	T1,PBSMAI	;IN MAINTENANCE MODE?
	TDNN	T1,.PBSTS(Q3)	;...
	JRST	UNNDMD##	;NO, RETURN AN ERROR
	PJRST	@DIAFNC(P2)	;DISPATCH BASED ON FUNCTION CODE
;DIAG. UUO FUNCTION DISPATCH TABLE

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

DIAAAU:	ETHLOK			;INTERLOCK AGAINST SMP RACES
	LDB	T1,PBPMJB	;GET JOB NUMBER OF CURRENT MAINTENANCE JOB
	JUMPE	T1,DIAAA1	;JUMP IF NOT CURRENTLY OWNED
	ETHULK			;RELEASE SMP INTERLOCK
	JRST	UNAAJB##	;RETURN ERROR

DIAAA1:	MOVE	T1,.CPJOB##	;GET OUR JOB NUMBER
	DPB	T1,PBPMJB	;SET UP OUR JOB AS MAINTENANCE JOB
	ETHULK			;RELEASE SMP INTERLOCK
	JRST	CPOPJ1##	;AND RETURN


;(3) RELEASE "CHANNEL" AND ALL UNITS

DIARCU:	LDB	T1,PBPMJB	;GET MAINTENANCE JOB NUMBER
	CAME	T1,J		;SAME AS CALLER'S JOB?
	JRST	UNAAJB##	;NO, RETURN ERROR
	SETZ	T1,		;CLEAR MAINTENANCE JOB NUMBER
	DPB	T1,PBPMJB	;...
	JRST	CPOPJ1##	;AND RETURN


;(4) SPECIFY CHANNEL PROGRAM

DIASCP:	LDB	T1,PBPMJB	;GET MAINTENANCE JOB NUMBER
	CAME	T1,J		;SAME AS CALLER'S JOB?
	JRST	UNAAJB##	;NO, RETURN ERROR
	MOVE	P3,.PBCDB(Q3)	;GET CHANNEL DATA BLOCK ADDRESS
	PUSHJ	P,DIARCP	;RETURN ANY IOWD
	S0PSHJ	GETWD1##	;GET IOWD
	HLRE	T2,T1		;LENGTH OF IOWD
	JUMPE	T2,IOWCPB##	;TOO BIG IF 0
	MOVEM	T1,.CHICW##(P3)	;UNRELOCATED IOWD
	MOVEI	T1,1(T1)	;START ADDRESS
	MOVNS	T2		;+LENGTH
	ADDI	T2,-1(T1)	;TOP ADDRESS
	S0PSHJ	ZRNGE##		;MAKE SURE THE PAGES ARE OK
	  JRST	[SETZM .CHICW##(P3) ;PAGE NOT THERE
		 JRST IOWCPB##]	;BOMB HIM OUT
	SETZB	P1,P4		;SAY FIRST CALL, NOT A DX10
	MOVE	T2,.CHICW##(P3)	;GET IOWD
	S0PSHJ	MAPIO##		;RELOCATE THE IOWD
	  JRST	[SETZM .CHICW##(P3)
		JRST DINEFC##]	;NO LOW-CORE BLOCKS

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

	MOVSI	T1,(CC.HLT)	;LIGHT HALT BIT IN LAST CCW
	IORM	T1,-1(P1)	;...
	SETZM	(P1)		;TERMINATE LIST
	MOVEM	P2,.CHICW##(P3)	;STORE ADDRESS OF CHANNEL PROGRAM
	TLO	P2,(FLD(.CCJMP,CC.OPC)) ;MAKE ICW BE A JUMP
	MOVE	T1,.CPEPT##	;GET EPT ADDRESS
	ADDI	T1,KNIICH*4	;OFFSET TO CHANNEL LOGOUT AREA
	MOVEM	P2,.CSICW(T1)	;POINT ICWA AT CORE-BLOCK
	SETZM	.CSCLP(T1)	;CLEAR OTHER WORDS
	SETZM	.CSDBA(T1)	;...
	PUSHJ	P,STOTAC##	;TELL USER ICWA
	JRST	CPOPJ1##	;AND TAKE GOOD RETURN


;(5) RELEASE CHANNEL PROGRAM

DIARCP:	LDB	T1,PBPMJB	;GET MAINTENANCE JOB NUMBER
	CAME	T1,J		;SAME AS CALLER'S JOB?
	JRST	UNAAJB##	;NO, RETURN ERROR
	MOVE	P3,.PBCDB(Q3)	;GET CHANNEL DATA BLOCK ADDRESS
	SKIPN	T1,.CHICW##(P3)	;NOTHING TO DO IF NO IOWD
	POPJ	P,
	SETZM	.CHICW##(P3)	;FORGET WE HAD IT
	S0JRST	RTNIOW##	;RETURN THE SPACE AND RETURN


;(6) GET CHANNEL STATUS

DIACST:	LDB	T1,PBPMJB	;GET MAINTENANCE JOB NUMBER
	CAME	T1,J		;SAME AS CALLER'S JOB?
	JRST	UNAAJB##	;NO, RETURN ERROR
	MOVE	P2,.CPEPT##	;GET EPT ADDRESS
	ADDI	P2,KNIICH*4+.CSICW ;OFFSET TO ICWA ADDRESS
	PJRST	DIAGCS##	;FINISH UP IN UUOCON
;(17) ENABLE MICROCODE RELOAD

DIAELD:	SETZ	T1,		;CLEAR AUTO-RELOAD DISABLED FLAG
	DPB	T1,PBPARD	;...
	MOVX	T1,PBSRUN	;IS KLNI CURRENTLY RUNNING?
	TDNN	T1,.PBSTS(Q3)	;...
	PUSHJ	P,RLDKNI	;NO, INITIATE RELOAD OF KLNI
	PJRST	CPOPJ1##	;AND RETURN


;(20) DISABLE MICROCODE RELOAD

DIADLD:	MOVEI	T1,1		;SET AUTO-RELOAD DISABLED FLAG
	DPB	T1,PBPARD	;...
	PJRST	CPOPJ1##	;AND RETURN


;(21) RELOAD MICROCODE

DIALOD:	MOVX	T1,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	TDNE	T1,.PBSTS(Q3)	;...
	PJRST	DIAILF		;YES, RETURN AN ERROR
	PUSHJ	P,SHTKNI	;SHUT DOWN KLNI
	PUSHJ	P,LODKNI	;INITIATE RELOAD OF KLNI
	MOVEI	T2,10		;WAIT AT MOST TEN SECONDS FOR RELOAD
DIALO1:	MOVX	T1,PBSONL	;IS KLNI ONLINE?
	TDNE	T1,.PBSTS(Q3)	;...
	PJRST	CPOPJ1##	;YES, RETURN
	SOJL	T2,DIAMRF##	;ERROR IF KLNI DIDN'T RELOAD IN TIME
	MOVEI	T1,1		;SLEEP FOR A SECOND
	PUSHJ	P,SLEEPF##	;...
	JRST	DIALO1		;AND LOOP BACK TO CHECK AGAIN
;(22) ENABLE MAINTENANCE MODE

DIAEMM:	MOVX	T1,PBSMAI	;MAINTENANCE MODE ALREADY SET?
	TDNE	T1,.PBSTS(Q3)	;...
	PJRST	CPOPJ1##	;YES, RETURN NOW
	IORM	T1,.PBSTS(Q3)	;SET MAINTENANCE MODE
	PUSHJ	P,SHTKNI	;SHUT DOWN KLNI
	SETZM	CHNBTS##(P1)	;ZAP BITS TO TEST FOR ON INTERRUPT
	PJRST	CPOPJ1##	;AND RETURN


;(23) DISABLE MAINTENANCE MODE

DIADMM:	MOVX	T1,PBSMAI	;MAINTENANCE MODE ALREADY CLEAR?
	TDNN	T1,.PBSTS(Q3)	;...
	PJRST	CPOPJ1##	;YES, RETURN NOW
	ANDCAM	T1,.PBSTS(Q3)	;CLEAR MAINTENANCE MODE
	SETZ	T1,		;CLEAR MAINTENANCE JOB NUMBER
	DPB	T1,PBPMJB	;...
	CONO	KNI,CO.CPT	;MAKE SURE KLNI IS STOPPED
	MOVE	T1,[KNIBTS]	;BITS TO TEST FOR ON INTERRUPT
	MOVEM	T1,CHNBTS##(P1)	;STORE IN CHANNEL DATA BLOCK
	PUSHJ	P,RLDKNI	;INITIATE AUTO-RELOAD OF KLNI
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  KNIBT. UUO


;UUO FOR BOOTING/DUMPING A KLNI
;LINKAGE:
;	XMOVEI	AC,ADDR
;	KNIBT.	AC,
;	  ERROR RETURN, CODE IN AC
;	NORMAL RETURN
;
;ADDR:	FUNCTION CODE,,LENGTH
;	ARGUMENTS

	ERCODX	KBTPRV,KBPRV%	;INSUFFICIENT PRIVILEGES
	ERCODX	KBTADC,KBADC%	;ADDRESS CHECK
	ERCODX	KBTIAL,KBIAL%	;INVALID ARGUMENT LIST LENGTH
	ERCODX	KBTILF,KBILF%	;ILLEGAL FUNCTION
	ERCODX	KBTICS,KBICS%	;ILLEGAL CPU SPECIFICATION
	ERCODX	KBTCNA,KBCNA%	;CPU IS NOT AVAILABLE
	ERCODX	KBTKDE,KBKDE%	;KLNI DOESN'T EXIST
	ERCODX	KBTKMM,KBKMM%	;KLNI IS IN MAINTENANCE MODE
	ERCODX	KBTDNS,KBDNS%	;KLNI DID NOT START
	ERCODX	KBTDNI,KBDNI%	;KLNI DID NOT INITIALIZE
	ERCODX	KBTICA,KBICA%	;INVALID CRAM ADDRESS
	ERCODX	KBTCRE,KBCRE%	;CRAM READ ERROR
	ERCODX	KBTCWE,KBCWE%	;CRAM WRITE ERROR
	ERCODX	KBTNRJ,KBNRJ%	;NOT THE RELOAD JOB

KNIBT.::PUSHJ	P,SAVE4##	;SAVE P1-P4
	PUSHJ	P,SAVQ##	;AND Q1-Q3
	SE1ENT			;RUN IN SECTION 1
	PUSHJ	P,SSPCS##	;SAVE CURRENT PCS
	PUSHJ	P,SXPCS##	;SET UP PCS FOR USER ARGUMENT
	  PJRST	KBTADC		;ADDRESS CHECK
	MOVE	M,T1		;SET UP FOR GETEWD
	MOVX	T1,JP.POK	;JOB HAVE SUFFICIENT PRIVILEGES?
	PUSHJ	P,PRVBIT##	;...
	  SKIPA			;YES, CONTINUE
	PJRST	KBTPRV		;NO, GIVE ERROR RETURN
	PUSHJ	P,GETEWD##	;GET FUNCTION CODE AND LENGTH
	  PJRST	KBTADC		;ADDRESS CHECK
	HRRE	P1,T1		;SAVE LENGTH OF ARGUMENT BLOCK
	HLRE	P2,T1		;ISOLATE FUNCTION CODE
	SOJL	P1,KBTIAL	;VALIDITY CHECK ARGUMENT BLOCK LENGTH
	SKIPLE	P2		;RANGE CHECK FUNCTION CODE
	CAILE	P2,KBTMAX	;...
	  PJRST	KBTILF		;ILLEGAL FUNCTION CODE

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

	SOJL	P1,KBTIAL	;VALIDITY CHECK ARGUMENT BLOCK LENGTH
	PUSHJ	P,GETEW1##	;YES, GET CPU NUMBER OF KLNI
	  PJRST	KBTADC		;ADDRESS CHECK
	MOVE	P3,T1		;SAVE IN P3
	HLRE	T1,P3		;GET CPU NUMBER
	PJUMPL	T1,KBTICS	;RANGE CHECK CPU NUMBER
	CAIL	T1,CPUN##	;...
	PJRST	KBTICS		;ILLEGAL CPU SPECIFICATION
IFN FTMP,<
	PUSHJ	P,ONCPUS##	;SET TO RUN ON THAT CPU
	  PJRST	KBTCNA		;KLNI CPU IS NOT AVAILABLE
> ;END IFN FTMP
	HRRE	T1,P3		;GET RH20 CHANNEL NUMBER OF KLNI
	CAIE	T1,KNIICH	;CORRECT RH20 CHANNEL NUMBER
	PJRST	KBTKDE		;NO, KLNI DOESN'T EXIST
	SKIPN	Q3,.CPNPB##	;GET ADDRESS OF PORT CONTROL BLOCK
	PJRST	KBTKDE		;KLNI DOESN'T EXIST
	MOVX	T1,DF.IMM	;IS FUNCTION ILLEGAL IN MAINTENANCE MODE?
	TDNN	T1,KBTDSF(P2)	;...
	JRST	KNIBT1		;NO, CONTINUE
	MOVX	T1,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	TDNE	T1,.PBSTS(Q3)	;...
	PJRST	KBTKMM		;YES, KLNI IS IN MAINTENANCE MODE
KNIBT1:	MOVX	T1,DF.RJB	;NEED TO CHECK JOB NUMBER?
	TDNN	T1,KBTDSF(P2)	;...
	JRST	KNIBT2		;NO, JUST DISPATCH
	LDB	T1,PBPRJB	;GET JOB NUMBER OF KNILDR
	CAME	T1,.CPJOB##	;IS THIS JOB THE CORRECT JOB?
	PJUMPN	T1,KBTNRJ	;NO, ERROR IF RELOAD JOB IS SET
KNIBT2:	MOVE	T1,KBTDSP(P2)	;GET ADDRESS OF FUNCTION SPECIFIC ROUTINE
	PJRST	(T1)		;DISPATCH TO FUNCTION SPECIFIC ROUTINE
;KNIBT. UUO DISPATCH TABLE

DEFINE	FNC,<

	DISP	0,		0,	KBTILF	;;(0) ILLEGAL, PLACE HOLDER
	DISP	0,		.KBSTS,	KBTSTS	;;(1) GET KLNI STATUS
	DISP	DF.IMM,		.KBSRJ,	KBTSRJ	;;(2) SET RELOAD JOB
	DISP	DF.IMM!DF.RJB,	.KBSTP,	KBTSTP	;;(3) STOP KLNI
	DISP	DF.IMM!DF.RJB,	.KBSTA,	KBTSTA	;;(4) START KLNI
	DISP	DF.IMM!DF.RJB,	.KBRED,	KBTRED	;;(5) READ CRAM LOCATION
	DISP	DF.IMM!DF.RJB,	.KBWRT,	KBTWRT	;;(6) WRITE CRAM LOCATION

>; END DEFINE FNC

;GENERATE FUNCTION FLAGS TABLE

DF.IMM==400000,,000000		;FUNCTION ILLEGAL IN MAINTENANCE MODE
DF.RJB==200000,,000000		;REQUIRE JOB TO BE THE RELOAD JOB

DEFINE	DISP(FLAG,CODE,ADDR),<
	EXP	FLAG		;(CODE) ADDR
>; END DEFINE DISP

KBTDSF:	FNC			;GENERATE FUNCTION FLAGS TABLE
;GENERATE FUNCTION DISPATCH TABLE

DEFINE	DISP(FLAG,CODE,ADDR),<
IF1,<IFN <CODE-<.-KBTDSP>>,<PRINTX ?Table KBTDSP entry CODE is out of order>>
	IFIW	ADDR		;(CODE) ADDR
>; END DEFINE DISP

KBTDSP:	FNC			;GENERATE FUNCTION DISPATCH TABLE
KBTMAX==.-KBTDSP-1		;MAXIMUM KNIBT. UUO FUNCTION CODE
;KNIBT. UUO FUNCTION .KBSTS (RETURN KLNI STATUS)

KBTSTS:	SETZ	T1,		;START WITH ZERO
	MOVX	T2,PBSRUN	;IS KLNI RUNNING?
	TDNE	T2,.PBSTS(Q3)	;...
	TXOA	T1,KS.RUN	;YES, MARK AS RUNNING
	TXO	T1,KS.RLD	;NO, MARK AS NEEDING TO BE RELOADED
	MOVX	T2,PBSRLD	;RELOAD REQUESTED?
	TDNE	T2,.PBSTS(Q3)	;...
	TXO	T1,KS.RRQ	;YES, MARK RELOAD REQUESTED BY SYSTEM
	MOVX	T2,PBSDMP	;DUMP REQUESTED?
	TDNE	T2,.PBSTS(Q3)	;...
	TXO	T1,KS.DRQ	;YES, MARK DUMP REQUESTED BY SYSTEM
	MOVX	T2,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	TDNE	T2,.PBSTS(Q3)	;...
	TXO	T1,KS.MAI	;YES, MARK IN RETURNED STATUS
	MOVX	T2,PBSARD	;IS AUTO-RELOAD DISABLED?
	TDNE	T2,.PBSTS(Q3)	;...
	TXO	T1,KS.ARD	;YES, MARK IN RETURNED STATUS
	LDB	T2,PBPRJB	;GET JOB NUMBER OF KNILDR
	DPB	T2,[POINTR (T1,KS.RJB)] ;STORE IN RETURN AC
	MOVS	M,.USMUO	;RESTORE POINTER TO USER'S AC
	PJRST	STOTC1##	;STORE STATUS IN USER AC AND RETURN


;KNIBT. UUO FUNCTION .KBSRJ (SET RELOAD JOB)

KBTSRJ:	ETHLOK			;INTERLOCK AGAINST SMP RACES
	LDB	T1,PBPRJB	;GET JOB NUMBER OF KNILDR
	SKIPE	T1		;JOB NUMBER OF KNILDR ALREADY SET?
	CAMN	T1,.CPJOB##	;YES, SAME AS THIS JOB'S JOB NUMBER?
	SKIPA			;YES, CONTINUE
	PJRST	[ETHULK		;NO, RELEASE SMP INTERLOCK
		 PJRST	KBTNRJ]	;NOT THE RELOAD JOB
	MOVX	T1,PBSDMP!PBSRLD ;CLEAR DUMP AND RELOAD REQUEST FLAGS
	ANDCAM	T1,.PBSTS(Q3)	;...
	MOVE	T1,.CPJOB##	;GET JOB NUMBER OF THIS JOB
	DPB	T1,PBPRJB	;SET THE JOB NUMBER OF KNILDR
	ETHULK			;RELEASE SMP INTERLOCK
	PJRST	CPOPJ1##	;AND RETURN
;KNIBT. UUO FUNCTION .KBSTP (STOP KLNI)

KBTSTP:	PUSHJ	P,SHTKNI	;SHUT DOWN KLNI
	PJRST	CPOPJ1##	;AND RETURN


;KNIBT. UUO FUNCTION .KBSTA (START KLNI)

KBTSTA:	SOJL	P1,KBTIAL	;VALIDITY CHECK ARGUMENT BLOCK LENGTH
	PUSHJ	P,GETEW1##	;GET KLNI START ADDRESS
	  PJRST	KBTADC		;ADDRESS CHECK
	SKIPL	T1		;VALID CRAM ADDRESS?
	CAILE	T1,MAXCRA	;...
	PJRST	KBTICA		;NO, INVALID CRAM ADDRESS
	MOVEM	T1,.PBKSA(Q3)	;SAVE KLNI START ADDRESS IN PCB
	PUSHJ	P,STAKNI	;START THE KLNI
	  PJRST	KBTDNS		;KLNI DID NOT START
	PUSHJ	P,INIKNI	;BEGIN INITIALIZATION OF KLNI
	  PJRST	KBTDNI		;ERROR, KLNI DID NOT INITIALIZE
	MOVEI	T1,5		;WAIT AT MOST 5 SECONDS
	PUSHJ	P,SLEEPF##	;...
	SETZ	T1,		;CLEAR JOB NUMBER OF KNILDR
	DPB	T1,PBPRJB	;...
	MOVX	T1,PBSONL	;IS KLNI NOW ONLINE?
	TDNN	T1,.PBSTS(Q3)	;...
	PJRST	KBTDNI		;NO, KLNI DID NOT INITIALIZE
	PJRST	CPOPJ1##	;RETURN
;KNIBT. UUO FUNCTION .KBRED (READ CRAM LOCATION)

KBTRED:	SUBI	P1,3		;CHECK FOR ARGUMENT LENGTH ERROR
	PJUMPL	P1,KBTIAL	;...
	PUSHJ	P,GETEW1##	;GET CRAM LOCATION
	  PJRST	KBTADC		;ADDRESS CHECK
	SKIPL	T1		;VALID CRAM ADDRESS?
	CAILE	T1,MAXCRA	;...
	PJRST	KBTICA		;NO, INVALID CRAM ADDRESS
	PUSHJ	P,REDKNI	;READ KLNI CRAM LOCATION
	  PJRST	KBTCRE		;CRAM READ ERROR
	MOVE	P1,T3		;SAVE LOW-ORDER CRAM CONTENTS
	MOVE	T1,T2		;GET HIGH ORDER CRAM CONTENTS
	PUSHJ	P,PUTEW1##	;STORE INTO ARGUMENT BLOCK
	  PJRST	KBTADC		;ADDRESS CHECK
	MOVE	T1,P1		;GET LOW-ORDER CRAM CONTENTS
	PUSHJ	P,PUTEW1##	;STORE INTO ARGUMENT BLOCK
	  PJRST	KBTADC		;ADDRESS CHECK
	PJRST	CPOPJ1##	;AND RETURN


;KNIBT. UUO FUNCTION .KBWRT (WRITE CRAM LOCATION)

KBTWRT:	SUBI	P1,3		;CHECK FOR ARGUMENT LENGTH ERROR
	PJUMPL	P1,KBTIAL	;...
	PUSHJ	P,GETEW1##	;GET CRAM ADDRESS
	  PJRST	KBTADC		;ADDRESS CHECK
	SKIPL	T1		;VALID CRAM ADDRESS?
	CAILE	T1,MAXCRA	;...
	PJRST	KBTICA		;NO, INVALID CRAM ADDRESS
	MOVE	P2,T1		;SAVE FOR LATER
	PUSHJ	P,GETEW1##	;GET HI-ORDER CRAM CONTENTS
	  PJRST	KBTADC		;ADDRESS CHECK
	MOVE	P1,T1		;SAVE FOR LATER
	PUSHJ	P,GETEW1##	;GET LOW-ORDER CRAM CONTENTS
	  PJRST	KBTADC		;ADDRESS CHECK
	EXCH	T1,P2		;SAVE LOW-ORDER CONTENTS, GET CRAM ADDRESS
	DMOVE	T2,P1		;GET CRAM CONTENTS
	PUSHJ	P,WRTKNI	;WRITE CRAM LOCATION
	  PJRST	KBTCWE		;CRAM WRITE ERROR
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  SHUT DOWN KLNI MICROCODE


;ROUTINE CALLED TO CLEANLY SHUT DOWN THE KLNI
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,SHTKNI
;RETURNS:
;	CPOPJ ALWAYS

SHTKNI:	MOVX	T1,PBSRUN	;IS KLNI CURRENTLY RUNNING?
	TDNE	T1,.PBSTS(Q3)	;...
	PUSHJ	P,DISKNI	;YES, MAKE KLNI ENTER DISABLED STATE
	  JFCL			;DON'T CARE IF ERROR
	PJRST	STPKNI		;STOP KLNI MICROCODE AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  STOP KLNI MICROCODE


;ROUTINE CALLED TO STOP THE KLNI
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,STPKNI
;RETURNS:
;	CPOPJ ALWAYS

STPKNI:	CONO	KNI,CO.CPT	;NO, MAKE SURE THE KLNI IS STOPPED
STPKNX:	PUSHJ	P,SAVE2##	;SAVE P1-P2
	MOVX	T1,PBSRUN!PBSONL ;CLEAR KLNI RUN AND ONLINE FLAGS
	ANDCAM	T1,.PBSTS(Q3)	;...
	AOS	.PBKHC(Q3)	;UPDATE COUNT OF TIMES KLNI HALTED
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PBKHT(Q3)	;REMEMBER WHEN KLNI LAST HALTED
	XMOVEI	T1,.PBRSQ(Q3)	;GET ADDRESS OF RESPONSE QUEUE
	PUSHJ	P,FIXQUE	;FIX IT UP IF NEEDED
	PUSHJ	P,KNIRQA	;PROCESS THE RESPONSE QUEUE
	XMOVEI	T1,.PBCMQ(Q3)	;GET ADDRESS OF COMMAND QUEUE
	PUSHJ	P,FIXQUE	;FIX IT UP AS NEEDED
	XMOVEI	T1,.PBCMQ(Q3)	;GET ADDRESS OF COMMAND QUEUE
	PUSHJ	P,STPKNQ	;EMPTY THE QUEUE
	PUSH	P,Q2		;SAVE Q2
	MOVE	P1,.PBLPT(Q3)	;GET LENGTH OF PTT TABLE
	MOVE	P2,.PBVPT(Q3)	;AND ADDRESS OF PTT TABLE
STPKN1:	JUMPE	P1,STPKN3	;EXIT LOOP IF NO MORE ENTRIES
	LDB	T1,PTPENA	;GET PROTOCOL ENABLED BIT
	JUMPE	T1,STPKN2	;IF NOT ENABLED, SKIP PROTOCOL FREE QUEUE
	MOVE	Q2,.PTPUB(P2)	;GET ADDRESS OF PROTOCOL USER BLOCK
	XMOVEI	T1,.PUFQH(Q2)	;GET ADDRESS OF PROTOCOL'S FREE QUEUE
	PUSHJ	P,FIXQUE	;FIX IT UP AS NEEDED
	XMOVEI	T1,.PUFQH(Q2)	;GET ADDRESS OF PROTOCOL'S FREE QUEUE
	PUSHJ	P,STPKNQ	;EMPTY THE QUEUE
STPKN2:	ADDI	P2,.PTLEN	;BUMP PTT TABLE POINTER TO NEXT ENTRY
	SOJA	P1,STPKN1	;LOOP BACK TO PROCESS ENTIRE PTT TABLE
STPKN3:	POP	P,Q2		;RESTORE Q2
	MOVE	T1,.PBEKB(Q3)	;GET ADDRESS OF KONTROLLER BLOCK
	PJRST	ETKOFL##	;INFORM ETHSER OF KONTROLLER OFFLINE AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  PROCESS KLNI QUEUE


;ROUTINE CALLED WHEN KLNI STOPPED TO PROCESS A KLNI QUEUE
;LINKAGE:
;	T1/ ADDRESS OF QUEUE HEADER
;	PUSHJ	P,STPKNQ
;RETURNS:
;	CPOPJ ALWAYS

STPKNQ:	PUSHJ	P,SAVE2##	;SAVE P1-P2
	PUSHJ	P,SAVQ##	;AND Q1-Q3
	MOVE	P2,T1		;SAVE ADDRESS OF QUEUE HEADER
STPKQ1:	MOVE	T1,P2		;GET ADDRESS OF QUEUE HEADER
	PUSHJ	P,REMQUE	;REMOVE NEXT ENTRY FROM QUEUE
	  POPJ	P,		;QUEUE EMPTY, RETURN
	XMOVEI	Q1,-.CMQUE(T2)	;GET ADDRESS OF KLNI COMMAND BUFFER
	LDB	T1,CMPSTS	;GET COMMAND STATUS BYTE
	IORX	T1,CMSERR	;SET COMMAND ERROR FLAG
	DPB	T1,CMPSTS	;STORE BACK INTO COMMAND
	MOVE	T1,.CMCBA(Q1)	;GET CALLBACK ROUTINE ADDRESS
	MOVE	Q2,.CMPUB(Q1)	;GET ADDRESS OF PROTOCOL USER BLOCK
	MOVE	P1,.CMEAB(Q1)	;AND ADDRESS OF EA BLOCK
	PUSHJ	P,(T1)		;CALL CALLBACK ROUTINE
	  JFCL			;...
	JRST	STPKQ1		;LOOP BACK TO EMPTY QUEUE
	SUBTTL	KLNI MAINTENANCE SERVICE  --  START KLNI MICROCODE


;ROUTINE CALLED TO START THE KLNI MICROCODE
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,STAKNI
;RETURNS:
;	CPOPJ IF COULDN'T START KLNI
;	CPOPJ1 IF KLNI STARTED

STAKNI:	AOS	.PBKSC(Q3)	;UPDATE COUNT OF TIMES KLNI STARTED
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PBKST(Q3)	;REMEMBER WHEN KLNI LAST STARTED
	MOVX	T1,PBSDMP!PBSRLD ;CLEAR DUMP AND RELOAD REQUEST FLAGS
	ANDCAM	T1,.PBSTS(Q3)	;...
	CONO	KNI,CO.CPT	;RESET KLNI
	MOVE	T2,.PBLGO(Q3)	;GET ADDRESS OF RH20 LOGOUT AREA
	MOVE	T1,.PBPBA(Q3)	;GET PHYSICAL ADDRESS OF PCB
	ADD	T1,[FLD(.CCFTH,CC.OPC)!FLD(3,CC.WDC)!FLD(<.PBPBA-.PBPCB>,CC.ADR)]
				; SET INITIAL CCW TO TRANSFER .PBPBA, .PBPIA,
				; AND .PBRP0 TO THE KLNI
	MOVEM	T1,.CSICW(T2)	;STORE IN THE CHANNEL LOGOUT AREA
	MOVE	T1,.PBKSA(Q3)	;GET KLNI START ADDRESS
	IORX	T1,.DOLRA	;LOAD RAM ADDRESS REGISTER
	DATAO	KNI,T1		;...
	PUSHJ	P,DISKNI	;START THE KLNI IN DISABLED STATE
	  PJRST	STPKNI		;COULDN'T, STOP KLNI AND RETURN
	MOVX	T1,PBSRUN	;MARK KLNI AS RUNNING
	IORM	T1,.PBSTS(Q3)	;...

	SKIPE	T1,.PBVPT(Q3)	;PTT TABLE ALREADY ALLOCATED?
	JRST	STAKN1		;YES, GO RECOMPUTE PHYSICAL ADDRESS
	MOVEI	T2,MAXPTT	;GET COUNT OF PTT ENTRIES SUPPORTED
	MOVEM	T2,.PBLPT(Q3)	;SAVE IN PORT CONTROL BLOCK
	IMULI	T2,.PTLEN	;CALCULATE LENGTH OF PTT TABLE
	PUSHJ	P,GETNWZ##	;ALLOCATE ZEROED CORE FOR TABLE
	  STOPCD STPKNI,DEBUG,KNICAP ;++CAN'T ALLOCATE PTT TABLE
	MOVEM	T1,.PBVPT(Q3)	;SAVE ADDRESS OF TABLE
STAKN1:	MAP	T1,(T1)		;CALCULATE PHYSICAL ADDRESS OF TABLE
	TXZ	T1,NADBTS	;...
	MOVEM	T1,.PBPTT(Q3)	;AND SAVE FOR KLNI

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

	SKIPE	T1,.PBVMC(Q3)	;MCAT TABLE ALREADY ALLOCATED?
	JRST	STAKN2		;YES, GO RECOMPUTE PHYSICAL ADDRESS
	MOVEI	T2,MAXMCT	;GET COUNT OF MCAT ENTRIES SUPPORTED
	MOVEM	T2,.PBLMC(Q3)	;SAVE IN PORT CONTROL BLOCK
	IMULI	T2,.MCLEN	;CALCULATE LENGTH OF MCAT TABLE
	PUSHJ	P,GETNWZ##	;ALLOCATE ZEROED CORE FOR TABLE
	  STOPCD STPKNI,DEBUG,KNICAM ;++CAN'T ALLOCATE MCAT TABLE
	MOVEM	T1,.PBVMC(Q3)	;SAVE ADDRESS OF TABLE
STAKN2:	MAP	T1,(T1)		;CALCULATE PHYSICAL ADDRESS OF TABLE
	TXZ	T1,NADBTS	;...
	MOVEM	T1,.PBMCT(Q3)	;AND SAVE FOR KLNI

	SKIPE	T1,.PBVCD(Q3)	;COUNTERS BUFFER ALREADY ALLOCATED?
	JRST	STAKN3		;YES, GO RECOMPUTE PHYSICAL ADDRESS
	MOVEI	T2,MAXKCB	;GET NUMBER OF COUNTERS SUPPORTED
	MOVEM	T2,.PBLCD(Q3)	;SAVE IN PORT CONTROL BLOCK
	PUSHJ	P,GETNWZ##	;ALLOCATE ZEROED CORE FOR TABLE
	  STOPCD STPKNI,DEBUG,KNICAD ;++CAN'T ALLOCATE COUNTERS DATA BUFFER
	MOVEM	T1,.PBVCD(Q3)	;SAVE ADDRESS OF TABLE
STAKN3:	MAP	T1,(T1)		;CALCULATE PHYSICAL ADDRESS OF TABLE
	TXZ	T1,NADBTS	;...
	MOVEM	T1,.PBKCB(Q3)	;AND SAVE FOR KLNI

	SKIPE	T1,.PBVCT(Q3)	;KLNI COUNTERS AREA ALREADY ALLOCATED?
	JRST	STAKN4		;YES, GO ENABLE KLNI AND RETURN
	MOVE	T2,.PBLCD(Q3)	;GET SIZE OF COUNTERS DATA BUFFER
	PUSHJ	P,GETNWZ##	;ALLOCATE ZEROED CORE FOR TABLE
	  STOPCD STPKNI,DEBUG,KNICAC ;++CAN'T ALLOCATE KLNI COUNTERS AREA
	MOVEM	T1,.PBVCT(Q3)	;SAVE ADDRESS OF TABLE

STAKN4:	MOVE	T2,.PBLGO(Q3)	;GET ADDRESS OF RH20 LOGOUT AREA
	MOVE	T1,.PBPBA(Q3)	;GET PHYSICAL ADDRESS OF PCB
	ADD	T1,[FLD(.CCJMP,CC.OPC)!FLD(<.PBCCW-.PBPCB>,CC.ADR)] ;GET A JUMP
				; CCW FOR THE KLNI TO USE
	MOVEM	T1,.CSICW(T2)	;STORE IN THE CHANNEL LOGOUT AREA
	PUSHJ	P,ENAKNI	;PUT KLNI IN ENABLED STATE
	  PJRST	STPKNI		;COULDN'T, STOP KLNI AND RETURN
	PJRST	CPOPJ1##	;SUCCESS, RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  DISABLE KLNI MICROCODE


;ROUTINE TO MAKE THE KLNI ENTER THE DISABLED STATE
;LINKAGE:
;	PUSHJ	P,DISKNI
;RETURNS:
;	CPOPJ IF DIDN'T ENTER DISABLED STATE
;	CPOPJ1 IF KLNI IN DISABLED STATE

DISKNI:	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	CONO	KNI,CO.DIS!CO.BTS(T1) ;ENTER DISABLED STATE
	MOVEI	T2,5000		;LOOP COUNT
DISKN1:	CONI	KNI,T1		;GET STATUS
	TXNE	T1,CI.DCP	;DISABLE COMPLETE?
	AOSA	(P)		;YES, SET FOR SKIP RETURN
	SOJG	T2,DISKN1	;NO, WAIT A BIT
	POPJ	P,		;DIDN'T MAKE IT
	SUBTTL	KLNI MAINTENANCE SERVICE  --  ENABLE KLNI MICROCODE


;ROUTINE TO MAKE THE KLNI ENTER THE ENABLED STATE
;LINKAGE:
;	PUSHJ	P,ENAKNI
;RETURNS:
;	CPOPJ IF DIDN'T ENTER ENABLED STATE
;	CPOPJ1 IF KLNI ENABLED

ENAKNI:	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	CONO	KNI,CO.ENA!CO.BTS(T1) ;GO FROM DISABLED TO ENABLED
	MOVEI	T2,5000		;LOOP COUNT
ENAKN1:	CONI	KNI,T1		;GET STATUS
	TXNE	T1,CI.ECP	;ENABLE COMPLETE?
	AOSA	(P)		;YES, SET FOR SKIP RETURN
	SOJG	T2,ENAKN1	;NO, WAIT A BIT
	POPJ	P,		;DIDN'T MAKE IT
	SUBTTL	KLNI MAINTENANCE SERVICE  --  INITIALIZE KLNI MICROCODE


;ROUTINE CALLED TO INITIALIZE KLNI MICROCODE
;LINKAGE:
;	T1/ ADDRESS OF COMPLETION ROUTINE
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,INIKNI
;RETURNS:
;	CPOPJ ALWAYS

INIKNI:	PUSHJ	P,SAVQ1##	;AND Q1
	MOVEI	T1,CMORSA	;READ NI STATION ADDRESS COMMAND FUNCTION
	PUSHJ	P,GETCMD	;GENERATE KLNI COMMAND BUFFER
	  PJRST	INIKNX		;ERROR, GIVE COMPLETION NOW
	XMOVEI	T1,INIKN1	;GET ADDRESS OF CALLBACK ROUTINE
	PUSHJ	P,KNICMD	;QUEUE READ NI STATION ADDRESS COMMAND
	PJRST	CPOPJ1##	;AND RETURN

INIKNX:	PUSHJ	P,SHTKNI	;SHUT DOWN KLNI
	LDB	T1,PBPRJB	;GET JOB NUMBER OF KNILDR
	PJUMPE	T1,CPOPJ##	;RETURN NOW IF NO JOB NUMBER
	PJRST	WAKJOB##	;WAKE UP RELOAD JOB AND RETURN
;HERE ON COMPLETION OF READ NI STATION ADDRESS COMMAND FUNCTION
;DURING INITIALIZATION OF KLNI MICROCODE
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,INIKN1
;RETURNS:
;	CPOPJ ALWAYS

INIKN1:	PUSHJ	P,SAVQ1##	;AND Q1
	LDB	T1,CMPUCV	;GET KLNI MICROCODE VERSION
	CAIGE	T1,MINUVR	;WITHIN REASON?
	STOPCD	INIKNX,INFO,KNIWUV ;++WRONG MICROCODE VERSION
	MOVEM	T1,.PBUVR(Q3)	;SAVE IN PORT CONTROL BLOCK
	DMOVE	T1,.CMNEA(Q1)	;GET HARDWARE ETHERNET ADDRESS
	DMOVEM	T1,.PBHEA(Q3)	;SAVE IN PORT CONTROL BLOCK
	SKIPN	.PBEAD(Q3)	;DO WE HAVE ANY CURRENT ETHERNET ADDRESS?
	DMOVEM	T1,.PBEAD(Q3)	;NO, USE THE HARDWARE ETHERNET ADDRESS
	LDB	T1,CMPLPT	;GET SIZE OF PTT TABLE
	CAME	T1,.PBLPT(Q3)	;SAME SIZE AS CURRENT PTT TABLE?
	STOPCD	INIKNX,DEBUG,KNIPWS ;++KLNI PTT TABLE IS WRONG SIZE
	LDB	T1,CMPLMC	;GET SIZE OF MCAT TABLE
	CAME	T1,.PBLMC(Q3)	;SAME SIZE AS CURRENT MCAT TABLE?
	STOPCD	INIKNX,DEBUG,KNIMWS ;++KLNI MCAT TABLE IS WRONG SIZE
REPEAT 0,<			;NEED KLNI MICROCODE CHANGE FOR THIS
	LDB	T1,CMPLCB	;GET SIZE OF COUNTERS BUFFER
	CAME	T1,.PBLCD(Q3)	;SAME SIZE AS CURRENT BUFFER?
	STOPCD	INIKNX,DEBUG,KNICWS ;++KLNI COUNTERS BUFFER IS WRONG SIZE
>; END REPEAT 0
	PUSHJ	P,GIVCMD	;RELEASE PREVIOUS KLNI COMMAND
	MOVEI	T1,CMOWSA	;WRITE NI STATION ADDRESS COMMAND FUNCTION
	PUSHJ	P,GETCMD	;GENERATE KLNI COMMAND BUFFER
	  PJRST	INIKNX		;ERROR, GIVE CALLBACK NOW
	DMOVE	T1,.PBEAD(Q3)	;GET CURRENT ETHERNET ADDRESS
	DMOVEM	T1,.CMNEA(Q1)	;SET IN KLNI COMMAND BUFFER
	LDB	T1,PBPPRM	;GET PROMISCUOUS RECEIVER FLAG
	DPB	T1,CMPPRM	;SET AS APPROPRIATE IN COMMAND
	LDB	T1,PBPPMM	;GET PROMISCUOUS MULTI-CAST FLAG
	DPB	T1,CMPAAM	;SET AS APPROPRIATE IN COMMAND
	XMOVEI	T1,INIKN2	;GET ADDRESS OF CALLBACK ROUTINE
	PJRST	KNICMD		;QUEUE WRITE NI STATION ADDRESS AND RETURN
;HERE ON COMPLETION OF WRITE NI STATION ADDRESS COMMAND FUNCTION
;DURING INITIALIZATION OF KLNI MICROCODE
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,INIKN2
;RETURNS:
;	CPOPJ ALWAYS

INIKN2:	PUSHJ	P,GIVCMD	;RELEASE COMMAND BUFFER
	MOVEI	T1,CMOLDP	;LOAD PTT TABLE COMMAND FUNCTION
	PUSHJ	P,GETCMD	;GENERATE KLNI COMMAND BUFFER
	  PJRST	INIKNX		;ERROR, GIVE CALLBACK NOW
	MOVE	T1,.PBVPT(Q3)	;GET VIRTUAL ADDRESS OF PTT
	MAP	T1,0(T1)	;CALCULATE PHYSICAL ADDRESS OF PTT
	TXZ	T1,NADBTS	;...
	MOVEM	T1,.PBPTT(Q3)	;SAVE IN PORT CONTROL BLOCK
	XMOVEI	T1,INIKN3	;GET ADDRESS OF CALLBACK ROUTINE
	PJRST	KNICMD		;QUEUE LOAD PTT TABLE COMMAND AND RETURN
;HERE ON COMPLETION OF LOAD PTT TABLE COMMAND FUNCTION
;DURING INITIALIZATION OF KLNI MICROCODE
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,INIKN3
;RETURNS:
;	CPOPJ ALWAYS

INIKN3:	PUSHJ	P,GIVCMD	;RELEASE COMMAND BUFFER
	MOVEI	T1,CMOLDM	;LOAD MCAT TABLE COMMAND FUNCTION
	PUSHJ	P,GETCMD	;GENERATE KLNI COMMAND BUFFER
	  PJRST	INIKNX		;ERROR, GIVE CALLBACK NOW
	MOVE	T1,.PBVMC(Q3)	;GET VIRTUAL ADDRESS OF MCAT
	MAP	T1,0(T1)	;CALCULATE PHYSICAL ADDRESS OF MCAT
	TXZ	T1,NADBTS	;...
	MOVEM	T1,.PBMCT(Q3)	;SAVE IN PORT CONTROL BLOCK
	XMOVEI	T1,INIKN4	;GET ADDRESS OF CALLBACK ROUTINE
	PJRST	KNICMD		;QUEUE LOAD MCAT TABLE COMMAND AND RETURN
;HERE ON COMPLETION OF LOAD MCAT TABLE COMMAND FUNCTION
;DURING INITIALIZATION OF KLNI MICROCODE
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,INIKN4
;RETURNS:
;	CPOPJ ALWAYS

INIKN4:	PUSHJ	P,GIVCMD	;RELEASE COMMAND BUFFER
	MOVEI	T1,CMORCC	;READ AND CLEAR COUNTERS COMMAND FUNCTION
	PUSHJ	P,GETCMD	;GENERATE KLNI COMMAND BUFFER
	  PJRST	INIKNX		;ERROR, GIVE CALLBACK NOW
	MOVE	T1,.PBVCD(Q3)	;GET VIRTUAL ADDRESS OF COUNTERS BUFFER
	MAP	T1,0(T1)	;CALCULATE PHYSICAL ADDRESS OF BUFFER
	TXZ	T1,NADBTS	;...
	MOVEM	T1,.PBKCB(Q3)	;SAVE IN PORT CONTROL BLOCK
	LDB	T1,CMPFLG	;GET CURRENT FLAGS
	IORX	T1,CMFCLR	;SET CLEAR COUNTERS FLAG
	DPB	T1,CMPFLG	;...
	XMOVEI	T1,INIKN5	;GET ADDRESS OF CALLBACK ROUTINE
	PJRST	KNICMD		;QUEUE READ AND CLEAR COUNTERS AND RETURN
;HERE ON COMPLETION OF READ AND CLEAR COUNTERS COMMAND FUNCTION
;DURING INITIALIZATION OF KLNI MICROCODE
;LINKAGE: (CALLED AT INTERRUPT LEVEL)
;	Q1/ ADDRESS OF KLNI COMMAND BUFFER
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,INIKN5
;RETURNS:
;	CPOPJ ALWAYS

INIKN5:	PUSHJ	P,GIVCMD	;RELEASE COMMAND BUFFER
	MOVE	T1,.PBLCD(Q3)	;GET LENGTH OF COUNTERS BUFFER
	SUBI	T1,1		;ADJUST FOR XBLT TO ZERO COUNTERS
	MOVE	T2,.PBVCD(Q3)	;GET ADDRESS OF COUNTERS BUFFER
	XMOVEI	T3,1(T2)	;AND ADDRESS+1 OF COUNTERS BUFFER
	SETZM	(T2)		;ZERO FIRST WORD OF BUFFER
	EXTEND	T1,[XBLT]	;ZERO REMAINDER OF COUNTERS BUFFER
	MOVX	T1,PBSONL	;MARK KLNI AS ONLINE
	IORM	T1,.PBSTS(Q3)	;...
	LDB	T1,PBPRJB	;GET JOB NUMBER OF RELOAD JOB
	SKIPE	T1		;JOB NUMBER SET?
	PUSHJ	P,WAKJOB##	;YES, WAKE IT UP
	MOVE	T1,.PBEKB(Q3)	;GET ADDRESS OF ETHSER'S KONTROLLER BLOCK
	DMOVE	T2,.PBHEA(Q3)	;GET HARDWARE ETHERNET ADDRESS
	PJRST	ETKONL##	;INFORM ETHSER OF KONTROLLER ONLINE AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  READ LATCHED ADDRESS REGISTER


;ROUTINE CALLED TO READ CONTENTS OF LATCHED ADDRESS REGISTER
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,REDLAR
;RETURNS:
;	CPOPJ ON ERROR
;	CPOPJ1 ON SUCCESS WITH:
;	T1/ CRAM ADDRESS

REDLAR:	MOVX	T4,PBSRUN	;IS KLNI RUNNING?
	TDNE	T4,.PBSTS(Q3)	;...
	POPJ	P,		;YES, ERROR
	CONO	KNI,CO.LAR	;SET LATCHED ADDRESS REGISTER FLAG
	DATAI	KNI,T1		;READ CONTENTS OF REGISTER
	LDB	T1,[POINTR (T1,DT.LAR)] ;GET CORRECT FIELD
	CONO	KNI,0		;MAKE SURE CO.LAR IS CLEAR
	PJRST	CPOPJ1##	;RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  READ CRAM CONTENTS


;ROUTINE CALLED TO READ THE CONTENTS OF A CRAM LOCATION
;LINKAGE:
;	T1/ CRAM ADDRESS
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,REDKNI
;RETURNS:
;	CPOPJ ON ERROR
;	CPOPJ1 ON SUCCESS WITH:
;	T2-T3/ CRAM CONTENTS

REDKNI:	MOVX	T4,PBSRUN	;IS KLNI RUNNING?
	TDNE	T4,.PBSTS(Q3)	;...
	POPJ	P,		;YES, ERROR
	CONO	KNI,0		;MAKE SURE CO.LAR IS CLEAR
	DPB	T1,[POINTR (T1,DO.RAR)] ;PUT ADDRESS INTO CORRECT FIELD
	ANDX	T1,DO.RAR	;...
	IORX	T1,.DOLRA!DO.LHW ;SET UP TO READ LEFT HALF CRAM MICRO-WORD
	DATAO	KNI,T1		;...
	DATAI	KNI,T2		;READ LEFT HALF CRAM MICRO-WORD
	ANDX	T2,DT.CRM	;MASK OUT EXTRANEOUS BITS
	TXZ	T1,DO.LHW	;SET UP TO READ RIGHT HALF CRAM MICRO-WORD
	DATAO	KNI,T1		;...
	DATAI	KNI,T3		;READ LEFT HALF CRAM MICRO-WORD
	ANDX	T3,DT.CRM	;MASK OUT EXTRANEOUS BITS
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  WRITE CRAM CONTENTS


;ROUTINE CALLED TO WRITE THE CONTENTS OF A CRAM LOCATION
;LINKAGE:
;	T1/ CRAM ADDRESS
;	T2-T3/ CRAM CONTENTS
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;RETURNS:
;	CPOPJ ON ERROR
;	CPOPJ1 ON SUCCESS

WRTKNI:	MOVX	T4,PBSRUN	;IS KLNI RUNNING?
	TDNE	T4,.PBSTS(Q3)	;...
	POPJ	P,		;YES, ERROR
	CONO	KNI,0		;MAKE SURE CO.LAR IS CLEAR
	DPB	T1,[POINTR (T1,DO.RAR)] ;PUT ADDRESS INTO CORRECT FIELD
	ANDX	T1,DO.RAR	;...
	IORX	T1,.DOLRA!DO.LHW ;SET UP TO WRITE LEFT HALF CRAM MICRO-WORD
	DATAO	KNI,T1		;...
	ANDX	T2,DT.CRM	;MASK OUT EXTRANEOUS BITS
	DATAO	KNI,T2		;WRITE LEFT HALF CRAM MICRO-WORD
	TXZ	T1,DO.LHW	;SET UP TO WRITE RIGHT HALF CRAM MICRO-WORD
	DATAO	KNI,T1		;...
	ANDX	T3,DT.CRM	;MASK OUT EXTRANEOUS BITS
	DATAO	KNI,T3		;WRITE LEFT HALF CRAM MICRO-WORD
	PJRST	CPOPJ1##	;AND RETURN

	SUBTTL	KLNI MAINTENANCE SERVICE  --  REQUEST MICROCODE RELOAD


;ROUTINE TO REQUEST THE RELOAD OF KLNI MICROCODE
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,RLDKNI
;RETURNS:
;	CPOPJ ALWAYS

RLDKNI:	TDZA	T1,T1		;DON'T SET DUMP FLAG
RLDKND:	MOVX	T1,PBSDMP	;GET DUMP REQUESTED FLAG
	MOVX	T2,PBSMAI!PBSARD ;MAINTENANCE MODE OR AUTO-RELOAD DISABLED?
	TDNE	T2,.PBSTS(Q3)	;...
	POPJ	P,		;YES, RETURN NOW
	TXOA	T1,PBSRLD	;SET RELOAD REQUESTED FLAG AND SKIP
LODKNI:	MOVX	T1,PBSRLD	;GET RELOAD REQUESTED FLAG
	TXZ	T1,PBSDMP	;$ CLEAR DUMP REQUESTED FLAG
	IORM	T1,.PBSTS(Q3)	;SET DUMP/RELOAD REQUEST FLAG(S)
	AOS	.PBKLC(Q3)	;UPDATE COUNT OF TIMES KLNI RELOAD REQUESTED
	MOVE	T1,SYSUPT##	;GET SYSTEM UPTIME
	MOVEM	T1,.PBKLT(Q3)	;REMEMBER WHEN RELOAD REQUESTED
	SETZ	T1,		;CLEAR ANY RELOAD JOB
	DPB	T1,PBPRJB	;...
	MOVX	T1,DF.RQN	;RUN KNILDR NEXT CLOCK TICK
	IORM	T1,DEBUGF##	;...
	POPJ	P,		;RETURN


;ROUTINE CALLED AT CLOCK LEVEL TO INITIATE RUN OF KNILDR
;LINKAGE:
;	PUSHJ	P,KNILDR
;RETURNS:
;	CPOPJ ALWAYS

KNILDR::PUSHJ	P,FRCSET##	;SET UP TO TYPE ON FRCLIN
	PUSHJ	P,INLMES##	;RUN KNILDR
	  ASCIZ /KNILDR/
	PJRST	PCRLF##		;END LINE AND RETURN
	SUBTTL	QUEUE MANIPULATION  --  INITIALIZE QUEUE HEADER


;ROUTINE TO INITIALIZE A QUEUE HEADER
;LINKAGE:
;	T1/ ADDRESS OF QUEUE HEADER
;	PUSHJ	P,INIQUE
;RETURNS:
;	CPOPJ ALWAYS

INIQUE:	SETOM	.QHIWD(T1)	;RESET QUEUE INTERLOCK WORD
	MAP	T2,.QHFLI(T1)	;GET PHYSICAL ADDRESS OF FLINK
	TXZ	T2,NADBTS	;...
	MOVEM	T2,.QHFLI(T1)	;MAKE FLINK POINT TO ITSELF
	MOVEM	T2,.QHBLI(T1)	;AND BLINK POINT TO FLINK
	SETZM	.QHELN(T1)	;CLEAR QUEUE ENTRY LENGTH
	POPJ	P,		;AND RETURN
	SUBTTL	QUEUE MANIPULATION  --  FIX A QUEUE


;ROUTINE CALLED ON AN ERROR STOP TO FIX A QUEUE (IF POSSIBLE)
;LINKAGE:
;	T1/ ADDRESS OF QUEUE HEADER
;	PUSHJ	P,FIXQUE
;RETURNS:
;	CPOPJ ALWAYS

FIXQUE:	POPJ	P,
	SUBTTL	QUEUE MANIPULATION  --  REMOVE AN ENTRY FROM QUEUE


;ROUTINE TO REMOVE FIRST QUEUE ENTRY FROM A QUEUE
;LINKAGE:
;	T1/ ADDRESS OF QUEUE HEADER
;	PUSHJ	P,REMQUE
;RETURNS:
;	CPOPJ IF QUEUE WAS EMPTY
;	CPOPJ1 IF SUCCESS WITH:
;	T2/ ADDRESS OF QUEUE ENTRY

REMQUE:	SYSPIF			;NO INTERRUPTS
	SETZ	T3,		;INITIALIZE TIMEOUT COUNTER
REMQU1:	AOSN	.QHIWD(T1)	;WAIT FOR INTERLOCK
	JRST	REMQU2		;GOT IT
	CAIE	T3,TIMOUT	;WAITED LONG ENOUGH?
	AOJA	T3,REMQU1	;NO, INCREMENT COUNTER AND TRY AGAIN
	AOS	.PBCIB(Q3)	;INCREMENT COUNT OF BROKEN INTERLOCKS
	STOPCD	.+1,INFO,KNIRIT ;++REMQUE INTERLOCK TIMEOUT

REMQU2:	MAP	T3,.QHFLI(T1)	;GET PHYSICAL ADDRESS OF QUEUE HEADER
	TXZ	T3,NADBTS	;...
	CAMN	T3,.QHFLI(T1)	;IS QUEUE EMPTY?
	JRST	REMQU3		;YES, RETURN
	MOVE	T2,.QHFLI(T1)	;GET PHYSICAL ADDRESS OF FIRST QUEUE ENTRY
	ADDI	T2,.QEVAD	;GET VIRTUAL ADDRESS OF FIRST QUEUE ENTRY
	PUSHJ	P,PMOVE##	;...
	PUSH	P,T2		;SAVE ADDRESS OF QUEUE ENTRY
	MOVE	T2,.QEFLI(T2)	;GET ADDRESS OF NEXT QUEUE ENTRY
	MOVEM	T2,.QHFLI(T1)	;STORE AS NEW FIRST ENTRY IN QUEUE
	ADDI	T2,.QEBLI	;POINT NEW FIRST ENTRY BACK AT QUEUE HEADER
	PUSHJ	P,PMOVEM##	;...
	POP	P,T2		;GET BACK ADDRESS OF QUEUE ENTRY
	AOS	(P)		;SET FOR SKIP RETURN

REMQU3:	SETOM	.QHIWD(T1)	;RELEASE QUEUE INTERLOCK
	SYSPIN			;ALLOW INTERRUPTS AGAIN
	POPJ	P,		;AND RETURN
	SUBTTL	QUEUE MANIPULATION  --  INSERT AN ENTRY INTO QUEUE


;ROUTINE TO INSERT A QUEUE ENTRY ONTO A QUEUE
;LINKAGE:
;	T1/ ADDRESS OF QUEUE HEADER
;	T2/ ADDRESS OF QUEUE ENTRY
;	PUSHJ	P,PUTQUE
;RETURNS:
;	CPOPJ ALWAYS

PUTQUE:	SYSPIF			;NO INTERRUPTS
	SETZ	T3,		;INITIALIZE TIMEOUT COUNTER
PUTQU1:	AOSN	.QHIWD(T1)	;GET INTERLOCK
	JRST	PUTQU2		;GOT IT
	CAIE	T3,TIMOUT	;WAITED LONG ENOUGH?
	AOJA	T3,PUTQU1	;NO, INCREMENT COUNTER AND TRY AGAIN
	AOS	.PBCIB(Q3)	;INCREMENT COUNT OF BROKEN INTERLOCKS
	STOPCD	.+1,INFO,KNIPIT ;++PUTQUE INTERLOCK TIMEOUT

PUTQU2:	MOVEM	T2,.QEVAD(T2)	;SAVE VIRTUAL ADDRESS IN QUEUE ENTRY
	MAP	T3,.QEFLI(T2)	;GET PHYSICAL ADDRESS OF NEW QUEUE ENTRY
	TXZ	T3,NADBTS	;...
	PUSH	P,T2		;SAVE ADDRESS OF QUEUE ENTRY
	MOVE	T2,.QHBLI(T1)	;GET PHYSICAL ADDRESS OF LAST QUEUE ENTRY
	ADDI	T2,.QEFLI	;POINT IT AT NEW ENTRY
	PUSHJ	P,PMOVEM##	;...
	POP	P,T2		;GET BACK ADDRESS OF QUEUE ENTRY
	EXCH	T3,.QHBLI(T1)	;SET NEW LAST ENTRY ADDRESS, GET PREVIOUS
	MOVEM	T3,.QEBLI(T2)	;POINT LAST ENTRY BACK TO PREVIOUS ENTRY
	MAP	T3,.QHFLI(T1)	;GET PHYSICAL ADDRESS OF QUEUE HEADER
	TXZ	T3,NADBTS	;...
	MOVEM	T3,.QEFLI(T2)	;POINT NEW LAST ENTRY AT QUEUE HEADER

PUTQU3:	SETOM	.QHIWD(T1)	;RELEASE QUEUE INTERLOCK
	SYSPIN			;ALLOW INTERRUPTS AGAIN
	POPJ	P,		;AND RETURN
	SUBTTL	BYTE POINTERS


;BYTE POINTERS FOR PORT CONTROL BLOCK

PBPCPU:	POINTR	(.PBSTS(Q3),PBSCPU)	;CPU NUMBER OF KLNI
PBPMAI:	POINTR	(.PBSTS(Q3),PBSMAI)	;KLNI IS IN MAINTENANCE MODE
PBPPRM:	POINTR	(.PBSTS(Q3),PBSPRM)	;KLNI IS IN PROMISCUOUS RECEIVER MODE
PBPPMM:	POINTR	(.PBSTS(Q3),PBSPMM)	;KLNI IS IN PROMISCUOUS MULTI-CAST MODE
PBPARD:	POINTR	(.PBSTS(Q3),PBSARD)	;KLNI AUTO-RELOAD DISABLED
PBPMJB:	POINTR	(.PBSTS(Q3),PBSMJB)	;JOB NUMBER OF MAINTENANCE JOB
PBPRJB:	POINTR	(.PBSTS(Q3),PBSRJB)	;JOB NUMBER OF KNILDR


;BYTE POINTERS FOR PROTOCOL USER BLOCK

PUPPAD:	POINTR	(.PUSTS(Q2),PUSPAD)	;PROTOCOL USES PADDING
PUPPTT:	POINTR	(.PUSTS(Q2),PUSPTT)	;PTT TABLE INDEX OF THIS PROTOCOL


;BYTE POINTERS FOR PTT AND MCAT TABLES

PTPENA:	POINTR	(.PTPTY(P2),PTTENA)	;PROTOCOL ENABLED FLAG
PTPPTY:	POINTR	(.PTPTY(P2),PTTPTY)	;PROTOCOL TYPE CODE
MCPENA:	POINTR	(.MCLAD(P2),MCTENA)	;MULTI-CAST ADDRESS ENABLED FLAG


;BYTE POINTERS FOR KLNI COMMAND BUFFERS

CMPSTS:	POINTR	(.CMCSW(Q1),CMCSTS)	;COMMAND STATUS
CMPFLG:	POINTR	(.CMCSW(Q1),CMCFLG)	;COMMAND FLAGS
CMPCMD:	POINTR	(.CMCSW(Q1),CMCCMD)	;COMMAND OPCODE
CMPTDR:	POINTR	(.CMCSW(Q1),CMCTDR)	;TIME DOMAIN REFLECTOMETRY VALUE

CMPXDL:	POINTR	(.CMXDL(Q1),CMXXDL)	;TRANSMIT DATAGRAM LENGTH
CMPXPT:	POINTR	(.CMXPT(Q1),CMXXPT)	;TRANSMIT DATAGRAM PROTOCOL TYPE

CMPRDL:	POINTR	(.CMRDL(Q1),CMRRDL)	;RECEIVE DATAGRAM LENGTH
CMPRPT:	POINTR	(.CMRPT(Q1),CMRRPT)	;RECEIVE DATAGRAM PROTOCOL TYPE

CMPACE:	POINTR	(.CMNSM(Q1),CMMACE)	;ACCEPT PACKETS WITH CRC ERRORS
CMPAAM:	POINTR	(.CMNSM(Q1),CMMAAM)	;ACCEPT ALL MULTI-CAST PACKETS
CMPH4K:	POINTR	(.CMNSM(Q1),CMMH4K)	;H400 MODE TRANSCEIVER
CMPPRM:	POINTR	(.CMNSM(Q1),CMMPRM)	;PROMISCUOUS MODE

CMPUCV:	POINTR	(.CMNSW(Q1),CMWUCV)	;KLNI MICROCODE VERSION
CMPLMC:	POINTR	(.CMNSW(Q1),CMWLMC)	;LENGTH OF MCAT TABLE
CMPLPT:	POINTR	(.CMNSW(Q1),CMWLPT)	;LENGTH OF PTT TABLE
CMPERC:	POINTR	(.CMNSW(Q1),CMWERC)	;ERROR RETRY COUNT


;BYTE POINTERS FOR BUFFER SEGMENT DESCRIPTORS

BSPSBA:	POINTR	(.BSSBA(P4),BSASBA)	;SEGMENT BASE ADDRESS
BSPNXT:	POINTR	(.BSNXT(P4),BSNNXT)	;ADDRESS OF NEXT BSD
BSPSGL:	POINTR	(.BSSGL(P4),BSSSGL)	;SEGMENT LENGTH
	SUBTTL	THE END


KNILIT:!XLIST			;LITERALS
	LIT
	LIST

KNIEND:!END