Google
 

Trailing-Edge - PDP-10 Archives - bb-bt99q-bb - kniser.x23
There is 1 other file named kniser.x23 in the archive. Click here to see a list.
	TITLE	KNISER - KLNI DEVICE DRIVER	V114
	SUBTTL	WILLIAM C. DAVENPORT/WXD	30-MAY-89

	SEARCH	F,S,DEVPRM,ETHPRM,MACSYM
	T20SYM
	$RELOC
	$HIGH

;THIS MODULE IMPLEMENTS THE KLNI INTERFACE DRIVER FOR ETHERNET

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;  OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1985,1986,1988.
;ALL RIGHTS RESERVED.

.CPYRT<1985,1988>

	XP	VKNISR,114	;KNISER VERSION NUMBER

IFNDEF FTKNILDR,<FTKNILDR==0>	;-1 TO SUPPORT OLD KNILDR & KNIBT. UUO

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........................................  65
;         5.6   EBUS PARITY ERROR.................................  66
;         5.7   DATA PATH ERROR...................................  67
;         5.8   KLNI ERROR STOP PROCESSING........................  68
;         5.9   SPEAR ERROR LOGGING...............................  69
;    6. KLNI MAINTENANCE SERVICE
;         6.1   DIAG. UUO.........................................  71
;         6.2   KNIBT. UUO........................................  77
;         6.3   SHUT DOWN KLNI MICROCODE..........................  84
;         6.4   STOP KLNI MICROCODE...............................  85
;         6.5   PROCESS KLNI QUEUE................................  86
;         6.6   START KLNI MICROCODE..............................  87
;         6.7   DISABLE KLNI MICROCODE............................  89
;         6.8   ENABLE KLNI MICROCODE.............................  90
;         6.9   INITIALIZE KLNI MICROCODE.........................  91
;         6.10  READ LATCHED ADDRESS REGISTER.....................  97
;         6.11  READ CRAM CONTENTS................................  98
;         6.12  WRITE CRAM CONTENTS...............................  99
;         6.13  REQUEST MICROCODE RELOAD.......................... 100
;    7. QUEUE MANIPULATION
;         7.1   INITIALIZE QUEUE HEADER........................... 101
;         7.2   FIX A QUEUE....................................... 102
;         7.3   REMOVE AN ENTRY FROM QUEUE........................ 103
;         7.4   INSERT AN ENTRY INTO QUEUE........................ 104
;    8. BYTE POINTERS............................................. 105
;    9. THE END................................................... 106
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  MICROCODE VALUES


;MICROCODE ADDRESSES

UCODSA==0			;MICROCODE STARTING ADDRESS
UCODVR==137			;MICROCODE VERSION ADDRESS


;MICROCODE VERSION RESTRICTIONS

MINUVR==100,,167		;MINIMUM ALLOWABLE MICROCODE VERSION


;MICROCODE TABLE SIZES

MAXPTT==17			;SIZE OF PTT TABLE
MAXMCT==17			;SIZE OF MCAT TABLE
MAXKCB==54			;SIZE OF COUNTERS DATA BUFFER
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  QUEUE BLOCKS


;QUEUE HEADER BLOCK

	PHASE	0		;THESE ARE OFFSETS

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.QHIWD:! BLOCK	1		;INTERLOCK WORD
.QHFLI:! BLOCK	1		;FORWARD LINK WORD (FLINK)
.QHBLI:! BLOCK	1		;BACKWARD LINK WORD (BLINK)
.QHELN:! BLOCK	1		;QUEUE ENTRY LENGTH

.QHLEN:!			;LENGTH OF QUEUE HEADER

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

	DEPHASE


;QUEUE ENTRY BLOCK

	PHASE	0		;THESE ARE OFFSETS

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.QEFLI:! BLOCK	1		;FORWARD LINK WORD (FLINK)
.QEBLI:! BLOCK	1		;BACKWARD LINK WORD (BLINK)
.QERS0:! BLOCK	1		;RESERVED FOR SOFTWARE

.QELEN:!			;LENGTH OF QUEUE ENTRY HEADER

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

.QEVAD==.QERS0			;VIRTUAL ADDRESS OF QUEUE ENTRY

	DEPHASE
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  BUFFER SEGMENT DESCRIPTOR BLOCK


;A BUFFER SEGMENT DESCRIPTOR DESCRIBES A PHYSICALLY CONTIGUOUS
;SECTION OF MEMORY.  SEVERAL BUFFER SEGMENT DESCRIPTORS MAY BE
;LINKED TOGETHER TO DESCRIBE A BUFFER BEING SENT OR RECEIVED.

	PHASE	0		;THESE ARE OFFSETS

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.BSSBA:! BLOCK	1		;PHYSICAL BASE ADDRESS OF SEGMENT
	 BSASBA==000077,,777777		;SEGMENT BASE ADDRESS

.BSNXT:! BLOCK	1		;PHYSICAL ADDRESS OF NEXT BSD
	 BSNNXT==000077,,777777		;ADDRESS OF NEXT BSD

.BSSGL:! BLOCK	1		;BUFFER SEGMENT LENGTH
	 BSSSGL==000000,,177777		;SEGMENT LENGTH

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

.BSVNB:! BLOCK	1		;VIRTUAL ADDRESS OF NEXT BSD

.BSRWB:! BLOCK	1		;RE-ALIGNED WORD BYTE OFFSET
.BSRWA:! BLOCK	1		;RE-ALIGNED WORD ADDRESS
.BSRWD:! BLOCK	1		;RE-ALIGNED WORD DATA

.BSLEN:!			;LENGTH OF BSD

	DEPHASE
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  COMMAND BLOCK


;KLNI COMMAND BLOCK

	PHASE	0		;THESE ARE OFFSETS

.CMPUB:! BLOCK	1		;ADDRESS OF PROTOCOL USER BLOCK
.CMCBA:! BLOCK	1		;CALLBACK ROUTINE ADDRESS
.CMEAB:! BLOCK	1		;ADDRESS OF EA BLOCK

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.CMCMD:!			;START OF KLNI COMMAND

.CMQUE:! BLOCK	.QELEN		;QUEUE ENTRY WORDS

.CMCSW:! BLOCK	1		;KLNI COMMAND STATUS WORD
	 CMCSTS==776000,,000000		;COMMAND STATUS
		 CMSSND==100			;SEND DATAGRAM FLAG
		 CMSETY==076			;ERROR TYPE
			 CMEEXC==00			;EXCESSIVE COLLISIONS
			 CMECCF==01			;CARRIER CHECK FAILED
			 CMECDF==02			;COLLISION DETECT FAILED
			 CMESHT==03			;SHORT CIRCUIT
			 CMEOPN==04			;OPEN CIRCUIT
			 CMEFTL==05			;FRAME TOO LONG
			 CMERFD==06			;REMOTE FAILURE TO DEFER
			 CMEBCE==07			;BLOCK CHECK ERROR
			 CMEFRE==10			;FRAMING ERROR
			 CMEDOV==11			;DATA OVERRUN
			 CMEUPT==12			;UNRECOGNIZED PROTOCOL TYPE
			 CMEFTS==13			;FRAME TOO SHORT
			 CMESCE==27			;SPURIOUS CHANNEL ERROR
			 CMEWNZ==30			;CHANNEL WORD COUNT NOT ZERO
			 CMEQLV==31			;QUEUE LENGTH VIOLATION
			 CMEIPF==32			;ILLEGAL PLI FUNCTION
			 CMEUCM==33			;UNRECOGNIZED COMMAND
			 CMEBLV==34			;BUFFER LENGTH VIOLATION
			 CMEXBP==36			;TRANSMIT BUFFER PARITY ERROR
			 CMEINT==37			;INTERNAL ERROR
		 CMSERR==001			;ERROR FLAG

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

	 CMCFLG==001774,,000000		;COMMAND FLAGS
		 CMFCRC==100			;INTERNALLY GENERATED CRC
		 CMFPAD==040			;PAD DATAGRAM
		 CMFBSD==010			;BSD STYLE BUFFERS
		 CMFCLR==002			;CLEAR COUNTERS AFTER READ
		 CMFRSP==001			;COMMAND RESPONSE REQUIRED
	 CMCCMD==000003,,770000		;COMMAND OPCODE
		 CMOXDG==1			;SEND DATAGRAM
		 CMOLDM==2			;LOAD MCAT TABLE
		 CMOLDP==3			;LOAD PTT TABLE
		 CMORCC==4			;READ AND CLEAR COUNTERS
		 CMORDG==5			;RECEIVE DATAGRAM
		 CMOWPL==6			;WRITE PLI
		 CMORPL==7			;READ PLI
		 CMORSA==10			;READ NI STATION ADDRESS
		 CMOWSA==11			;WRITE NI STATION ADDRESS
		 CMOMAX==CMOWSA			;MAXIMUM DEFINED COMMAND OPCODE
	 CMCTDR==000000,,001777		;TIME DOMAIN REFLECTOMETRY VALUE ON ERROR

.CMHLN:!			;LENGTH OF COMMON COMMAND HEADER
.CMLNX==.			;CALCULATE LENGTH OF COMMAND BLOCK

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

	DEPHASE
;SNDDG (1) - SEND DATAGRAM COMMAND

	PHASE	.CMHLN		;FOLLOWING FIXED COMMAND HEADER

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.CMXDL:! BLOCK	1		;LENGTH OF TEXT DATA
	 CMXXDL==000000,,177777		;TRANSMIT DATAGRAM LENGTH

.CMXPT:! BLOCK	1		;PROTOCOL TYPE CODE
	 CMXXPT==000003,,777760		;TRANSMIT PROTOCOL TYPE

.CMXFQ:! BLOCK	1		;PHYSICAL ADDRESS OF FREE QUEUE HEADER
.CMXDA:! BLOCK	2		;DESTINATION ETHERNET ADDRESS
.CMXBA:! BLOCK	1		;PHYSICAL ADDRESS OF BSD CHAIN

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

.CMXVB:! BLOCK	1		;VIRTUAL ADDRESS OF BSD CHAIN

IFG <.-.CMLNX>,<.CMLNX==.>	;COMPUTE MAXIMUM LENGTH OF COMMAND BLOCK

	DEPHASE


;DGRCV (5) - DATAGRAM RECEIVED COMMAND

	PHASE	.CMHLN		;FOLLOWING FIXED COMMAND HEADER

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.CMRDL:! BLOCK	1		;LENGTH OF TEXT DATA
	 CMRRDL==000000,,177777		;RECEIVE DATAGRAM LENGTH

.CMRDA:! BLOCK	2		;DESTINATION ETHERNET ADDRESS
.CMRSA:! BLOCK	2		;SOURCE ETHERNET ADDRESS

.CMRPT:! BLOCK	1		;PROTOCOL TYPE CODE
	 CMRRPT==000003,,777760		;RECEIVE PROTOCOL TYPE

.CMRBA:! BLOCK	1		;PHYSICAL ADDRESS OF BSD CHAIN

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

.CMRVB:! BLOCK	1		;VIRTUAL ADDRESS OF BSD CHAIN

IFG <.-.CMLNX>,<.CMLNX==.>	;COMPUTE MAXIMUM LENGTH OF COMMAND BLOCK

	DEPHASE
;RDNSA (10) - READ NI STATION ADDRESS
;WRTNSA (11) - WRITE NI STATION ADDRESS

	PHASE	.CMHLN		;FOLLOWING FIXED COMMAND HEADER

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.CMNEA:! BLOCK	2		;ETHERNET ADDRESS

.CMNSM:! BLOCK	1		;SOFTWARE MODE WORD
	 CMMACE==000000,,000010		;ACCEPT PACKETS WITH CRC ERRORS
	 CMMAAM==000000,,000004		;ACCEPT ALL MULTI-CAST PACKETS
	 CMMH4K==000000,,000002		;H4000 MODE TRANSCEIVER
	 CMMPRM==000000,,000001		;PROMISCUOUS MODE

.CMNSW:! BLOCK	1		;SOFTWARE WORD
				;RDNSA COMMAND
	 CMWUCV==000003,,770000		;KLNI MICROCODE VERSION
	 CMWLMC==000000,,007700		;LENGTH OF MCAT TABLE
	 CMWLPT==000000,,000077		;LENGTH OF PTT TABLE
				;WRTNSA COMMAND
	 CMWERC==000000,,007777		;ERROR RETRY COUNT

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

IFG <.-.CMLNX>,<.CMLNX==.>	;COMPUTE MAXIMUM LENGTH OF COMMAND BLOCK

	DEPHASE


;DEFINE COMMAND BLOCK LENGTH AS LONGEST COMMAND

	PHASE	.CMLNX
.CMLEN:!			;LENGTH OF COMMAND BLOCK
	DEPHASE
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  PORT CONTROL BLOCK


;THE PORT CONTROL BLOCK (PCB) IS THE BASIC MEANS OF COMMUNICATION
;BETWEEN THE KLNI AND THE KLNI DEVICE DRIVER.  IT CONTAINS ALL
;ADDRESSES NEEDED BY THE KLNI AND HEADERS OF SEVERAL QUEUES
;USED IN COMMUNICATIONS BETWEEN THE KLNI AND ITS DRIVER.

	PHASE	IPKSIZ		;THESE ARE OFFSETS

.PBSTS:! BLOCK	1		;KLNI STATUS FLAGS
	 PBSRUN==400000,,000000		;KLNI IS RUNNING
	 PBSONL==200000,,000000		;KLNI IS ONLINE
	 PBSMAI==100000,,000000		;KLNI IS IN MAINTENANCE MODE
	 PBSPRM==040000,,000000		;KLNI IS IN PROMISCUOUS RECEIVE MODE
	 PBSPMM==020000,,000000		;KLNI IS IN PROMISCUOUS MULTI-CAST MODE
	 PBSADD==010000,,000000		;KLNI AUTO-DUMP DISABLED
	 PBSARD==004000,,000000		;KLNI AUTO-RELOAD DISABLED
	 PBSMOF==002000,,000000		;SET MEMORY OFFLINE IS IN PROGRESS
	 PBSINI==001000,,000000		;INITIALIZE KLNI AFTER MEMORY OFFLINE
	 PBSDMP==000400,,000000		;KLNI DUMP REQUESTED
	 PBSRLD==000200,,000000		;KLNI RELOAD REQUESTED
	 PBSQRR==000020,,000000		;QUEUED RELOAD REQUEST FLAG
	 PBSQIO==000010,,000000		;QUEUED I/O REQUEST FLAG
	 PBSCPU==000007,,000000		;CPU NUMBER OF KLNI
	 PBSMJB==000000,,777000		;JOB NUMBER OF MAINTENANCE JOB
	 PBSRJB==000000,,000777		;JOB NUMBER OF KNILDR

.PBUVR:! BLOCK	1		;KLNI MICROCODE VERSION
.PBKSA:! BLOCK	1		;KLNI MICROCODE START ADDRESS
.PBLGO:! BLOCK	1		;ADDRESS OF RH20 CHANNEL LOGOUT AREA
.PBCLI:! BLOCK	1		;CONI AT LAST INTERRUPT
.PBCDB:! BLOCK	1		;ADDRESS OF CHANNEL DATA BLOCK
.PBEKB:! BLOCK	1		;ADDRESS OF ETHSER'S KONTROLLER BLOCK

.PBHEA:! BLOCK	2		;HARDWARE ETHERNET ADDRESS
.PBEAD:! BLOCK	2		;CURRENT ETHERNET ADDRESS

.PBLPT:! BLOCK	1		;LENGTH OF PTT
.PBCPT:! BLOCK	1		;COUNT OF ENTRIES USED IN PTT
.PBVPT:! BLOCK	1		;VIRTUAL ADDRESS OF PTT

.PBLMC:! BLOCK	1		;LENGTH OF MCAT
.PBCMC:! BLOCK	1		;COUNT OF ENTRIES USED IN MCAT
.PBVMC:! BLOCK	1		;VIRTUAL ADDRESS OF MCAT

.PBLCD:! BLOCK	1		;LENGTH OF COUNTERS DATA BUFFER
.PBVCD:! BLOCK	1		;VIRTUAL ADDRESS OF KLNI COUNTERS BUFFER
.PBVCT:! BLOCK	1		;VIRTUAL ADDRESS OF KLNI COUNTERS AREA

.PBOLD:! BLOCK	1		;NON-ZERO IF ALREADY CONFIGURED
.PBULB:! BLOCK	.ULLEN		;MICROCODE LOADER PARAMETER BLOCK
.PBMPP:! BLOCK	1		;MAX PACKETS PROCESSED PER INTERRUPT
.PBNPP:! BLOCK	1		;NUMBER OF PACKETS PROCESSED THIS INTERRUPT

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

.PBBIT:! BLOCK	1		;ADDRESS OF CONI BITS TO TEST ON INTERRUPT
.PBKSC:! BLOCK	1		;COUNT OF TIMES KLNI STARTED
.PBKST:! BLOCK	1		;SYSTEM UPTIME WHEN KLNI LAST STARTED
.PBKAC:! BLOCK	1		;COUNT OF TIMES KLNI KEEP ALIVE FAILED
.PBKAT:! BLOCK	1		;SYSTEM UPTIME OF LAST KLNI KEEP ALIVE FAILURE
.PBKEC:! BLOCK	1		;COUNT OF KLNI ERRORS
.PBKET:! BLOCK	1		;SYSTEM UPTIME OF LAST KLNI ERROR
.PBKHC:! BLOCK	1		;COUNT OF TIMES KLNI HALTED
.PBKHT:! BLOCK	1		;SYSTEM UPTIME WHEN KLNI LAST HALTED
.PBKLC:! BLOCK	1		;COUNT OF TIMES KLNI RELOAD REQUESTED
.PBKQC:! BLOCK	1		;COUNT OF QUEUED I/O COMMANDS QUEUED
.PBKQT:! BLOCK	1		;SYSTEM UPTIME OF LAST QUEUED I/O COMMAND
.PBKFC:! BLOCK	1		;COUNT OF PROTOCOL FREE QUEUE COMMANDS QUEUED
.PBKFT:! BLOCK	1		;SYSTEM UPTIME OF LAST FREE QUEUE COMMAND
.PBKCC:! BLOCK	1		;COUNT OF KLNI COMMANDS QUEUED
.PBKCT:! BLOCK	1		;CPU UPTIME OF LAST KLNI COMMAND
.PBKRC:! BLOCK	1		;COUNT OF KLNI RESPONSES PROCESSED
.PBKRT:! BLOCK	1		;CPU UPTIME OF LAST KLNI RESPONSE

;ERROR INFORMATION

.PBMCE:! BLOCK	1		;COUNT OF MULTICAST CHECKSUM ERRORS
.PBFQE:! BLOCK	1		;COUNT OF FREE QUEUE EMPTY ERRORS
.PBCIB:! BLOCK	1		;COUNT OF INTERLOCKS BROKEN
.PBCLE:! BLOCK	1		;CONI ON LAST ERROR INTERRUPT
.PBCRA:! BLOCK	1		;CRAM ADDRESS FROM LAST ERROR
.PBCRC:! BLOCK	2		;CRAM CONTENTS FROM LAST ERROR
.PBLG0:! BLOCK	1		;CHANNEL LOGOUT WORD 0 FROM LAST ERROR
.PBLG1:! BLOCK	1		;CHANNEL LOGOUT WORD 1 FROM LAST ERROR
.PBLG2:! BLOCK	1		;CHANNEL LOGOUT WORD 2 FROM LAST ERROR
.PBECW:! BLOCK	1		;PORT'S CCW FROM LAST ERROR

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.PBPCB:!

.PBCMQ:! BLOCK	.QHLEN		;COMMAND QUEUE
.PBRSQ:! BLOCK	.QHLEN		;RESPONSE QUEUE
.PBUPQ:! BLOCK	.QHLEN		;UNKNOWN PROTOCOL TYPE QUEUE

.PBRS0:! BLOCK	1		;RESERVED FOR SOFTWARE

.PBPTT:! BLOCK	1		;PHYSICAL ADDRESS OF PTT
.PBMCT:! BLOCK	1		;PHYSICAL ADDRESS OF MCAT

.PBRS1:! BLOCK	1		;RESERVED FOR SOFTWARE

.PBER0:! BLOCK	1		;ERROR WORD 0
	 PBECMD==1B0			;ERROR WHILE READING A COMMAND
	 PBERES==1B3			;ERROR WHILE BUILDING RESPONSE
	 PBEFLI==77777777B35		;FLINK OF QUEUE IN ERROR
.PBER1:! BLOCK	1		;ERROR WORD 1 (API FUNCTION WORD)
.PBER2:! BLOCK	1		;ERROR WORD 2 (CHANNEL LOGOUT WORD 1)
.PBER3:! BLOCK	1		;ERROR WORD 3 (CHANNEL LOGOUT WORD 2)

.PBPBA:! BLOCK	1		;PHYSICAL ADDRESS OF PCB
.PBPIA:! BLOCK	1		;PRIORITY INTERRUPT ASSIGNMENT
.PBRP0:! BLOCK	1		;RESERVED TO KLNI MICROCODE
.PBCCW:! BLOCK	1		;CHANNEL COMMAND WORD

.PBKCB:! BLOCK	1		;PHYSICAL ADDRESS OF KLNI COUNTERS BUFFER

.PBPCL==.-.PBPCB		;LENGTH OF PCB AREA DEFINED BY KLNI MICROCODE

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

.PBLEN:!			;LENGTH OF PORT CONTROL BLOCK

	DEPHASE
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  PROTOCOL TYPE TABLE


;THE PROTOCOL TYPE TABLE SPECIFIES A SET OF PROTOCOL TYPES, WITH
;THEIR ASSOCIATED FREE QUEUE POINTERS THAT ARE CONSIDERED ENABLED
;BY THE PORT.
;
;THE PROTOCOL TYPES IN THIS TABLE COME INTO EFFECT ONLY AFTER
;THE EXECUTION OF A LOAD PTT COMMAND.

	PHASE	0		;THESE ARE OFFSETS

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.PTPTY:! BLOCK	1		;PROTOCOL TYPE WORD
	 PTTENA==400000,,000000		;PROTOCOL ENABLED
	 PTTPTY==000003,,777760		;PROTOCOL TYPE CODE

.PTFRQ:! BLOCK	1		;PHYSICAL ADDRESS OF FREE QUEUE HEADER

.PTRS0:! BLOCK	1		;RESERVED TO SOFTWARE

.PTLEN:!			;LENGTH OF PTT ENTRY

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

.PTPUB==.PTRS0			;ADDRESS OF PROTOCOL USER BLOCK

	DEPHASE
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  MULTI-CAST ADDRESS TABLE


;THE MCAT IS THE MULTI-CAST ADDRESS TABLE.  THIS TABLE SPECIFIES
;WHICH MULTI-CAST ADDRESSES ARE TO BE ACCEPTED BY THE KLNI
;ADDRESS FILTER.
;
;THE ADDRESSES IN THIS TABLE COME INTO EFFECT ONLY AFTER THE
;EXECUTION OF A LOAD MCAT COMMAND.

	PHASE	0		;THESE ARE OFFSETS

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.MCHAD:! BLOCK	1		;HIGH ORDER MULTI-CAST ADDRESS
	 MCTHAD==777777,,777760		;HIGH ORDER MULTI-CAST ADDRESS

.MCLAD:! BLOCK	1		;LOW ORDER MULTI-CAST ADDRESS
	 MCTLAD==777774,,000000		;LOW ORDER MULTI-CAST ADDRESS
	 MCTENA==000000,,000001		;MULTI-CAST ADDRESS ENABLED

.MCLEN:!			;LENGTH OF MCAT ENTRY

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

	DEPHASE
	SUBTTL	KLNI HARDWARE DEFINITIONS  --  COUNTERS BLOCK


;THE COUNTERS BLOCK CONTAINS ERROR COUNTERS ACCUMULATED BY THE KLNI.
;IT IS UPDATED UPON EXECUTION OF A READ/CLEAR COUNTERS COMMAND.

	PHASE	0		;THESE ARE OFFSETS

;***** START OF WORDS DEFINED BY THE KLNI MICROCODE *****

.KCBYR:! BLOCK	1		;BYTES RECEIVED
.KCBYX:! BLOCK	1		;BYTES TRANSMITTED
.KCDGR:! BLOCK	1		;DATAGRAMS RECEIVED
.KCDGX:! BLOCK	1		;DATAGRAMS TRANSMITTED
.KCMBR:! BLOCK	1		;MULTI-CAST BYTES RECEIVED
.KCMDR:! BLOCK	1		;MULTI-CAST DATAGRAMS RECEIVED
.KCDXD:! BLOCK	1		;DATAGRAMS TRANSMITTED, INITIALLY DEFERRED
.KCDX1:! BLOCK	1		;DATAGRAMS TRANSMITTED, SINGLE COLLISION
.KCDXM:! BLOCK	1		;DATAGRAMS TRANSMITTED, MULTIPLE COLLISIONS
.KCXMF:! BLOCK	1		;TRANSMIT FAILURES
.KCXFM:! BLOCK	1		;TRANSMIT FAILURE BIT MASK
	 CXFCRL==004000			;CARRIER LOST
	 CXFBPE==002000			;BUFFER PARITY ERROR
	 CXFRFD==001000			;REMOTE FAILURE TO DEFER
	 CXFFTL==000400			;FRAME TOO LONG
	 CXFOPN==000200			;OPEN CIRCUIT
	 CXFSHT==000100			;SHORT CIRCUIT
	 CXFCDF==000040			;COLLISION DETECT CHECK FAILED
	 CXFEXC==000020			;EXCESSIVE COLLISIONS
.KCCDF:! BLOCK	1		;COLLISION DETECT CHECK FAILURES
.KCRCF:! BLOCK	1		;RECEIVE FAILURES
.KCRFM:! BLOCK	1		;RECEIVE FAILURE BIT MASK
	 CRFFPE==000400			;FREE LIST PARITY ERROR
	 CRFNFB==000200			;NO FREE BUFFERS
	 CRFFTL==000100			;FRAME TOO LONG
	 CRFFRE==000040			;FRAMING ERROR
	 CRFBCE==000020			;BLOCK CHECK ERROR
.KCFQU:! BLOCK	1		;FREE QUEUE ERRORS, UNKNOWN PROTOCOL QUEUE
.KCFQP:! BLOCK	^D16		;FREE QUEUE ERRORS, INDIVIDUAL PROTOCOL TYPES

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

.KCUFD:! BLOCK	1		;UNRECOGNIZED FRAME DESTINATION
.KCDOV:! BLOCK	1		;DATA OVERRUN ERRORS
.KCSBU:! BLOCK	1		;UNUSED
.KCFQE:! BLOCK	1		;FREE QUEUE ERRORS, ALL QUEUES
.KCHE1:! BLOCK	1		;HARDWARE ERROR COUNTS
	 CH1PRP==777777,,000000		;PLI REGISTER READ PARITY ERRORS
	 CH1PPE==000000,,777777		;PLI PARITY ERRORS
.KCHE2:! BLOCK	1		;HARDWARE ERROR COUNTS
	 CH2MPE==777777,,000000		;DATA MOVER PARITY ERRORS
	 CH2CPE==000000,,777777		;CBUS PARITY ERRORS
.KCHE3:! BLOCK	1		;HARDWARE ERROR COUNTS
	 CH3EPE==777777,,000000		;EBUS PARITY ERRORS
	 CH3EQP==000000,,777777		;EBUS QUEUE PARITY ERRORS
.KCHE4:! BLOCK	1		;HARDWARE ERROR COUNTS
	 CH4CHN==777777,,000000		;CHANNEL ERRORS
	 CH4SCE==000000,,777777		;SPURIOUS CHANNEL ERRORS
.KCHE5:! BLOCK	1		;HARDWARE ERROR COUNTS
	 CH5SXA==777777,,000000		;SPURIOUS TRANSMIT ATTENTION ERRORS
	 CH5CRT==000000,,777777		;CBUS REQUEST TIMEOUT ERRORS
.KCHE6:! BLOCK	1		;HARDWARE ERROR COUNTS
	 CH6UBP==777777,,000000		;USER BUFFER PARITY ERRORS
	 CH6XBP==000000,,777777		;TRANSMIT BUFFER PARITY ERRORS
.KCHEX:! BLOCK	3		;RESERVED

.KCLEN:!			;LENGTH OF COUNTERS DATA BUFFER

IF1,<IFN <.KCLEN-MAXKCB>,<PRINTX ?KLNI counters definition is in error>>

;***** END OF WORDS DEFINED BY THE KLNI MICROCODE *****

	DEPHASE
	SUBTTL	KLNI SOFTWARE DEFINITIONS  --  TIMER VALUES


;TIMEOUTS

TIMOUT==^D5000			;INTERLOCK WORD TIMEOUT VALUE
IDLTIM==^D1*^D1000		;KLNI IDLE TIMER (MILLISECONDS)
KAFTIM==^D35*^D1000		;KLNI KEEP ALIVE TIMER (MILLISECONDS)
UPTTIM==^D5*^D60*^D1000		;KLNI UPTIME TIMER (MILLISECONDS)
	SUBTTL	KLNI SOFTWARE DEFINITIONS  --  PROTOCOL USER BLOCK


;KNISER MAINTAINS A PROTOCOL USER BLOCK FOR EACH PROTOCOL ENABLED
;ON THE KLNI.  THIS BLOCK CONTAINS INFORMATION NEEDED TO PROPERLY
;PROCESS INCOMING DATAGRAMS AND TO INTERFACE WITH ETHSER.

	PHASE	0		;THESE ARE OFFSETS

.PUPTY:! BLOCK	1		;PROTOCOL TYPE CODE (KLNI FORMAT)

.PUSTS:! BLOCK	1		;PROTOCOL STATUS WORD
	 PUSPAD==400000,,000000		;PROTOCOL USES PADDING
	 PUSPTT==000000,,000077		;PTT TABLE INDEX OF THIS PROTOCOL

.PUEPB:! BLOCK	1		;ADDRESS OF ETHSER'S PORTAL BLOCK

.PUFQH:! BLOCK	.QHLEN		;FREE QUEUE HEADER FOR THIS PROTOCOL

.PULEN:!			;LENGTH OF PROTOCOL USER BLOCK

	DEPHASE
	SUBTTL	KLNI SOFTWARE DEFINITIONS  --  DIAG. UUO



;DIAG. ERROR CODES

DIANP%==1			;INSUFFICIENT PRIVILEGES
DIAIA%==2			;ILLEGAL NUMBER OF ARGUMENTS
DIAIC%==3			;ILLEGAL CONTROLLER NUMBER
DIAIU%==4			;ILLEGAL UNIT NUMBER
DIAAA%==5			;SOME UNITS ALREADY ASSIGNED
DIADM%==6			;UNIT NOT IN DIAGNOSTIC MODE
DIAAJ%==7			;UNIT ASSIGNED TO ANOTHER JOB
DIAFC%==10			;NOT ENOUGH FREE CORE
DIAAU%==11			;NO ASSIGNED UNITS
DIACP%==12			;IOWD CROSSES PAGE BOUNDARY
DIAIF%==13			;ILLEGAL FUNCTION
DIAVC%==14			;JOB MUST NOT BE VIRTUAL
DIANC%==15			;NO SUCH CPU
DIANR%==16			;CPU NOT RUNNING
DIABA%==17			;BAD ARGUMENT LIST
DIACI%==20			;NO CI PORT ON SPECIFIED CPU
DIATO%==21			;READ PORT COUNTERS FUNCTION TIMED OUT
DIANK%==22			;NO KLNI PRESENT ON CPU
	SUBTTL	KLNI SOFTWARE DEFINITIONS  --  KNIBT. UUO
IFN FTKNILDR,<

;KNIBT. FUNCTION CODES

.KBSTS==1			;CHECK KLNI STATUS
.KBSRJ==2			;SET KLNI RELOAD JOB
.KBSTP==3			;STOP KLNI
.KBSTA==4			;START KLNI
.KBRED==5			;READ KLNI CRAM
.KBWRT==6			;WRITE KLNI CRAM


;KNIBT. FUNCTION .KBSTS STATUS BITS

KS.RUN==400000,,000000		;KLNI IS RUNNING
KS.MAI==200000,,000000		;KLNI IS IN MAINTENANCE MODE
KS.RLD==100000,,000000		;KLNI NEEDS RELOADING
KS.ARD==040000,,000000		;KLNI AUTO-RELOAD IS DISABLED
KS.RRQ==020000,,000000		;KLNI RELOAD REQUESTED BY SYSTEM
KS.DRQ==010000,,000000		;KLNI DUMP REQUESTED BY SYSTEM
KS.ADD==004000,,000000		;KLNI AUTO-DUMP IS DISABLED
KS.RJB==000000,,777777		;JOB NUMBER OF KNILDR


;KNIBT. ERROR CODES

KBPRV%==1			;INSUFFICIENT PRIVILEGES
KBADC%==2			;ADDRESS CHECK
KBIAL%==3			;INVALID ARGUMENT LIST
KBILF%==4			;ILLEGAL FUNCTION
KBICS%==5			;ILLEGAL CPU SPECIFICATION
KBCNA%==6			;CPU NOT AVAILABLE
KBKDE%==7			;KLNI DOESN'T EXIST
KBKMM%==10			;KLNI IS IN MAINTENANCE MODE
KBDNS%==11			;KLNI DID NOT START
KBDNI%==12			;KLNI DID NOT INITIALIZE
KBICA%==13			;INVALID CRAM ADDRESS
KBCRE%==14			;CRAM READ ERROR
KBCWE%==15			;CRAM WRITE ERROR
KBNRJ%==16			;NOT THE RELOAD JOB
>;END IFN FTKNILDR
	SUBTTL	KLNI DEVICE SERVICE  --  AUTCON PARAMETERS


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


;DRIVER CHARARCTERISTICS
;	KNI	= KNICNF
;	KNI	= KLNI
;	M.CPU	= MAXIMUM DEVICES IN SYSTEM
;	.KTKNI	= KONTROLLER TYPE
;	0	= MAXIMUM DRIVES PER KONTROLLER
;	0	= HIGHEST DFRIVE NUMBER ON KONTROLLER
;	MDSEC0	= SECTION FOR KDB/UDB
;	MDSEC0	= SECTION FOR DDB
DRVCHR	(KNI,KNI,M.CPU##,.KTKNI,0,0,MDSEC0,MDSEC0,<DR.XAD!DR.NMC>)


	.ORG	IPKSIZ
	BLOCK	.PBLEN			;PCB
KNIKLN:!				;LENGTH OF KDB
	.ORG


KNIKDB:	KDBBEG	(KNI,KNIKLN)
	SETWRD	(KDBNAM,<SIXBIT/KNI/>)	;KONTROLLER NAME
	SETWRD	(KDBPCC,<.PBPCL,,.PBPCB>)  ;PHYSICALLY CONTIGUOUS CORE
	SETWRD	(.PBOLD,<EXP -1>)	;FULL CONFIGURE FIRST TIME
	SETWRD	(.PBMPP,<^D2000>)	;MAX PACKETS PROCESSED PER INTERRUPT
	KDBEND


EQUATE	(LOCAL,0,<KNICKT,KNIUDB,KNIULN>)

KNIULB==.PBULB		;OFFSET OF UCODE LOADER BLOCK
KNIICD==IPAICD##	;PROTOTYPE INTERRUPT CODE ADDRESS
KNIICL==IPAICL##	;PROTOTYPE INTERRUPT CODE LENGTH

KNIDSP:	DRVDSP	(KNI,KNICHN##,,,KNIDIA)

;DEFAULT MONGEN'ED DEVICE TABLE
DEFMDT:	MDKL10	(7,564,0,0,<MD.KON>)	;DEVICE CODE 564
	MDTERM				;TERMINATE TABLE

;PROTOTYPE MICROCODE PARAMETER BLOCK
KNIULP:	EXP	.BTKNI##	;MICROCODE INDEX
	XWD	000,0		;DEVICE CODE,,MASSBUS UNIT NUMBER
	SIXBIT	/KLNI/		;INTERFACE NAME
	SIXBIT	/NIA20/		;CHANNEL NAME
	EXP	MINUVR		;MINIMUM MICROCODE VERSION
	EXP	0		;DATE/TIME OF LOAD SUCCESS OR FAILURE
	EXP	0		;MICROCODE VERSION
	EXP	0		;POINTER TO MAGIC TABLE
	EXP	0		;MICROCODE LENGTH
	EXP	0		;MICROCODE ADDRESS
	SUBTTL	KLNI DEVICE SERVICE  --  KLNI CONFIGURATION


;ROUTINE CALLED WHEN AUTCON
;LINKAGE:
;	T1/ DEVICE CODE
;	T2/ CONI BITS
;	PUSHJ	P,KNICFG
;RETURNS:
;	CPOPJ TO STOP SCANNING DRIVERS
;	CPOPJ1 TO SCAN OTHER DRIVERS

KNICFG:	CAIL	T1,FSTICD/4	;AN RH20?
	CAILE	T1,LSTICD/4	;...
	JRST	CPOPJ1##	;WRONG DRIVER
	TLNN	T2,(CI.PPT)	;IPA CHANNEL?
	JRST	CPOPJ1##	;NO
	PUSHJ	P,TYIPA##	;TRY TO DETERMINE THE PORT TYPE
	  JRST	KNICF1		;CAN'T
	CAIN	T1,.CIKNI	;KLNI?
	JRST	KNICF2		;YES
	JRST	CPOPJ1##	;ON TO THE NEXT DRIVER

KNICF1:	XMOVEI	T1,KNIMDT##	;MONGEN'ED DEVICE TABLE
	XMOVEI	T2,DEFMDT	;DEFAULT TABLE
	MOVNI	T3,1		;NO MASSBUS UNIT OR DRIVE INFORMATION
	MOVEI	T4,MD.KON	;MATCH ON KONTROLLER DEFINITION
	PUSHJ	P,AUTMDT##	;SCAN THE TABLES
	  JRST	CPOPJ1##	;NO MATCHES

KNICF2:	MOVSI	T1,CP.KNI	;KLNI CHANNEL
	PUSHJ	P,AUTCHN##	;BUILD A CHANNEL DATA BLOCK
	  POPJ	P,		;NO CORE
	MOVNI	T1,1		;NOT MULTI-UNIT, BUT MUST DO ANYWAY
	PUSHJ	P,AUTKDB##	;BUILD A KDB
	  POPJ	P,		;GIVE UP IF NO CORE
	AOSE	.PBOLD(W)	;BEEN HERE BEFORE?
	POPJ	P,		;YES--DO NOTHING OR I/O WILL STOP

	MOVE	T1,.CPCHA##	;GET CHANNEL DATA BLOCK ADDRESS
	MOVEM	T1,.PBCDB(W)	;SAVE IN THE PCB

	HRRZ	T1,@KDBCSO(W)	;ADDRESS OF CONI BITS TO TEST IN SKIP CHAIN
	MOVEM	T1,.PBBIT(W)	;UPDATE
	MOVE	T1,[KNIBTS]	;BITS TO TEST FOR ON INTERRUPT
	MOVEM	T1,@.PBBIT(W)	;SET IN THE CONSO SKIP CHAIN CODE
	PUSHJ	P,IPAADB##	;ALLOCATE DRAM DUMP BUFFER
	PJRST	KNIINI		;INITIALIZE THE KLNI AND RETURN
	SUBTTL	KLNI DEVICE SERVICE  --  SYSINI INITIALIZATION


;ROUTINE TO DO ONCE-ONLY SYSINI INITIALIZATION.
;LINKAGE:
;	PUSHJ	P,KNISYS
;RETURNS:
;	CPOPJ ALWAYS

	$INIT

KNISYS::
IFE FTXMON,<
	MOVEI	T1,KNISIZ##	;SIZE OF KLNI FREECORE NEEDED
	PUSHJ	P,INICOR##	;ALLOCATE CORE
	MOVEM	T2,KNILOC##	;SAVE STARTING ADDRESS
>; END IFN FTXMON
IFN FTXMON,<
	MOVEI	T2,KNISIZ##	;SIZE OF FREECORE
	MOVEI	T1,(MS.DCN)	;SECTION NUMBER
	PUSHJ	P,GFWNZN##	;ASK ONCMOD FOR NON-ZERO SECTION CORE
	  HALT	.		;*** FOR NOW
	MOVEM	T1,KNILOC##	;SAVE AS LOWEST ADDRESS IN FREECORE
>; END IFN FTXMON
	POPJ	P,		;RETURN

	$HIGH
	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
	XMOVEI	P1,SYSCHN##-CHNSYS ;SET PREDECESSOR
	MOVEI	T1,CP.KNI	;KLNI CHANNEL BITS

KNIIN1:	MOVE	P1,CHNSYS(P1)	;GET CHANNEL ADDRESS
	HLRZS	P1		;ADDRESS IN LH QUANTITY
	JUMPE	P1,CPOPJ##	;THERE MUST BE ONE!
	HLRZ	T2,CHNTYP(P1)	;GET INTERESTING BITS
	CAIE	T1,(T2)		;A KLNI?
	JRST	KNIIN1		;TRY AGAIN
	LDB	T2,CHYCPU##	;GET CPU NUMBER
	CAME	T2,.CPCPN##	;OURS?
	JRST	KNIIN1		;NO
	MOVE	T1,CHNTBP(P1)	;FETCH THE KDB (PCB) TABLE POINTER
	SKIPN	Q3,(T1)		;AND GET THE PORT CONTROL BLOCK
	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	KNIIN2]	;POSSIBLY RELOAD KLNI
	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

KNIIN2:	MOVE	T1,.CPBIT##	;GET OUR CPU'S BIT
	TDNN	T1,IPAMSK##	;WANT TO SKIP STARTING THE NI?
	PJRST	RLDKNI		;REQUEST RELOAD OF KLNI AND RETURN
	POPJ	P,		;SKIP RELOAD 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
	TXO	T1,CO.CQA!CO.BTS ;PLUS COMMAND QUEUE AVAILABLE FLAG
	XCT	KDBCNO(Q3)	;TELL KLNI
	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
;	MOVSI	T1,(CR.ATO)	;BIT TO TEST
;	TDNE	T1,.CPRUN##	;WAITING FOR AUTCON TO RUN?
;	POPJ	P,		;THEN DO NOTHING
	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	KNISE2		;YES, GO PERFORM KEEP ALIVE CHECKS
	TXNN	T1,PBSINI	;NEED TO INITIALIZE KLNI?
	JRST	KNISE1		;NO
	MOVX	T1,PBSINI	;GET INIT BIT
	ANDCAM	T1,.PBSTS(Q3)	;CLEAR IT
	PUSHJ	P,PCBINI	;INITIALIZE PORT CONTROL BLOCK
	PJRST	RLDKNI		;REQUEST KLNI RELOAD AND RETURN
KNISE1:	TXNE	T1,PBSQRR	;QUEUED RELOAD REQUESTED?
	PJRST	KNILOD		;YES, RELOAD MICROCODE AND RETURN
	POPJ	P,		;RETURN

				;CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

;AT THIS POINT, WE ASSUME THE KLNI IS RUNNING BECAUSE NO FLAVOR OF
;RELOAD OR INITIALIZATION HAS BEEN REQUESTED
KNISE2:	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	KNISE3		;NO, GO CHECK KLNI IDLE TIMER
	XCT	KDBCNI(Q3)	;CONI DEV,T1
	PUSH	P,T1		;SAVE FOR A MOMENT
	MOVEI	T1,CO.CPT	;MAKE SURE THE KLNI IS REALLY STOPPED
	XCT	KDBCNO(Q3)	;CONO DEV,(T1)
	POP	P,T1		;GET CONI BITS BACK
	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

KNISE3:	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::XMOVEI	T1,KNIMF1	;GET ADDRESS OF ROUTINE
	PJRST	CPUAPP##	;CALL ROUTINE FOR ALL CPUS

;ROUTINE CALLED FOR ALL CPUS

KNIMF1:	MOVE	T1,.CPBIT##-.CPCDB##(P1) ;GET CPU'S BIT
	TDNN	T1,IPAMSK##	;WANT TO SKIP STARTING THE NI?
	SKIPN	T2,.CPNPB##-.CPCDB##(P1) ;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN NOW
	MOVX	T1,PBSMOF	;SET MEMORY OFFLINE FLAG
	IORM	T1,.PBSTS(T2)	;...
	POPJ	P,		;AND RETURN
;ROUTINE CALLED ON CPU0 WHEN SET MEMORY OFFLINE HAS COMPLETED
;LINKAGE:
;	PUSHJ	P,KNIMON
;RETURNS:
;	CPOPJ ALWAYS

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

;ROUTINE CALLED FOR ALL CPUS

KNIMN1:	MOVE	T1,.CPBIT##-.CPCDB##(P1) ;GET CPU'S BIT
	TDNN	T1,IPAMSK##	;WANT TO SKIP STARTING THE NI?
	SKIPN	T2,.CPNPB##-.CPCDB##(P1) ;THIS CPU HAVE A KLNI?
	POPJ	P,		;NO, RETURN NOW
	MOVX	T1,PBSMOF	;GET MEMORY OFFLINE FLAG
	TDNN	T1,.PBSTS(T2)	;KLNI STOPPED BECAUSE OF SET MEMORY OFFLINE?
	POPJ	P,		;NO, RETURN NOW
	ANDCAM	T1,.PBSTS(T2)	;YES, CLEAR MEMORY OFFLINE FLAG
	MOVX	T1,PBSINI	;SET INITIALIZE KLNI FLAG
	IORM	T1,.PBSTS(T2)	;...
	POPJ	P,		;AND RETURN
	SUBTTL	KLNI DEVICE SERVICE -- PAUSE SERVICE


;ROUTINE TO PAUSE SERVICE WHEN WE'RE IN DANGER OF KAF'ING
;LINKAGE:
;	PUSHJ	P,KNIMON
;RETURNS:
;	CPOPJ ALWAYS

KNIPAU:	STOPCD	.+1,INFO,KNIKSP, ;++KLNI SERVICE PAUSING
	XCT	KDBCNI(Q3)	;CONI DEV,T1
	TRZ	T1,7		;CLEAR PIA
	XCT	KDBCNO(Q3)	;CONO DEV,(T1)
	MOVE	T1,.CPCPN##	;GET OUR CPU
	MOVEI	T2,(T1)		;MAKE A COPY
	ROT	T2,-4		;POSITION FOR CLOCK1
	TLO	T2,(1B0)	;ONLY THIS CPU
	HRR	T2,Q3		;SECTION LOCAL ADDR FOR IDENTIFICATION
	MOVE	T3,TICSEC##	;ONE SECOND
	HRLI	T3,KNICON	;ADDR OF ROUTINE TO CALL
	SYSPIF			;INTERLOCK QUEUE
	IDPB	T3,CLOCK##	;STORE TIMEOUT VALUE & SUBR
	IDPB	T2,CLOCK##	;STORE CPU AND DATA
	SETZM	CLKMIN##(T1)	;FLAG CPU DEPENDENT CLOCK REQUEST
	SYSPIN			;LET GO OF QUEUE
	POPJ	P,		;DONE


;HERE TO CONTINUE SERVICE
KNICON:	PUSHJ	P,SAVW##	;SAVE W
	SE1ENT			;ENTER SECTION ONE
	XMOVEI	W,KDBTAB##+.TYKNI-KDBNXT ;SET PREDESSOR

KNICO1:	SKIPN	W,KDBNXT(W)	;GET NEXT KDB
	POPJ	P,		;THERE ISN'T ONE
	HRRZ	T2,W		;COPY SECTION LOCAL ADDRESS
	CAIE	T1,(T2)		;THE ONE WE WANT?
	JRST	KNICO1		;NO--TRY NEXT
	MOVE	T1,.CPUPT##	;GET CPU UPTIME
	MOVEM	T1,.PBKRT(W)	;AVOID KNIKAFS
	XCT	KDBCNI(W)	;CONI DEV,T1
	IOR	T1,.PBPIA(W)	;INCLUDE PI CHANNEL
	XCT	KDBCNO(W)	;GIVE IT A PIA
	POPJ	P,		;AND LETTERRIP
	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
	IMULI	T1,.CPLEN##	;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::XMOVEI	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
IFN FTKNILDR,<
	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
>;END IFN FTKNILDR
	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	T2,^-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,MP.NAD	;...
	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,MP.NAD	;...
	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
	LDB	T1,[POINT 3,KDBCNO(Q3),9] ;GET RH20 NUMBER
	LSH	T1,2		;COMPUTE EPT OFFSET TO CHANNEL LOGOUT AREA
	ADD	T1,.CPEPT##	;PLUS ADDRESS OF EPT
	MOVEM	T1,.PBLGO(Q3)	;STORE IN PORT CONTROL BLOCK
	MAP	T1,.PBPCB(Q3)	;DETERMINE PHYSICAL ADDRESS OF PCB
	TXZ	T1,MP.NAD	;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,MP.NAD	;...
	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,MP.NAD	;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,MP.NAD	;...
	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,MP.NAD	;...
	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,MP.NAD	;...
	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,MP.NAD	;...
	DPB	T1,BSPSBA	;STORE IN BSD
	MOVE	T2,T1		;CALCULATE NEXT PAGE BOUNDARY
	ADDI	T2,PAGSIZ	;...
	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,MP.NAD	;...
	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
	MOVX	T1,PBSQIO	;CLEAR QUEUED I/O FLAG IN PCB
	ANDCAM	T1,.PBSTS(Q3)	;...
	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	TXO	T1,CO.CQA!CO.BTS ;PLUS COMMAND QUEUE AVAILABLE FLAG
	XCT	KDBCNO(Q3)	;TELL KLNI
	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:	MOVX	T2,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	TDNE	T2,.PBSTS(W)	;...
	POPJ	P,		;YES, RETURN NOW
	MOVE	Q3,W		;COPY PCB ADDRESS TO STANDARD REGISTER
	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
	TRO	T1,CO.CQA!CO.BTS ;PLUS COMMAND QUEUE AVAILABLE FLAG
	XCT	KDBCNO(Q3)	;TELL KLNI
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
	SETZM	.PBNPP(Q3)	;RESET PACKET PROCESSED COUNT ON THIS INTERRUPT
KNIRQ1:	MOVE	T1,.PBPIA(Q3)	;GET KLNI PI ASSIGNMENT
	TXO	T1,CO.RQA!CO.BTS ;PLUS RESPONSE QUEUE AVAILABLE
	MOVX	T2,PBSRUN	;IS KLNI RUNNING?
	TDNE	T2,.PBSTS(Q3)	;...
	XCT	KDBCNO(Q3)	;TELL KLNI RESPONSE QUEUE IS 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
	AOS	T1,.PBNPP(Q3)	;COUNT THE PACKET PROCESSED
	CAML	T1,.PBMPP(Q3)	;LESS THAN LIMIT PER INTERRUPT?
	JRST	KNIPAU		;NO--STOP THE WORLD BEFORE WE KAF
	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
	TXO	T1,CO.FQE!CO.BTS ;PLUS FREE QUEUE ERROR
	XCT	KDBCNO(Q3)	;TELL KLNI
	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
	PUSH	P,W		;SAVE AC
	MOVE	W,Q3		;PUT KDB (PCB) ADDRESS IN PROPER PLACE
	PUSHJ	P,RDLAR##	;READ LATCHED ADDRESS REGISTER CONTENTS
	  SETZ	T1,		;ERROR?
	MOVEM	T1,.PBCRA(Q3)	;SAVE IT
	PUSHJ	P,RDIPA##	;READ THAT LOCATION'S CONTENTS
	  SETZB	T2,T3		;ERROR?
	POP	P,W		;RESTORE AC
	DMOVEM	T2,.PBCRC(Q3)	;SAVE IN PCB
	MOVE	T1,.PBLGO(Q3)	;ADDRESS OF CHANNEL LOGOUT AREA
	DMOVE	T2,0(T1)	;GET FIRST TWO WORDS OF CHANNEL LOGOUT AREA
	DMOVEM	T2,.PBLG0(Q3)	;SAVE THEM
	MOVE	T2,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
	PUSH	P,T1		;SAVE ADDRESS FOR A MOMENT
	LDB	T1,[POINT 3,KDBCNO(Q3),9] ;GET RH20 NUMBER
	MOVE	T2,.PBUVR(Q3)	;GET KLNI MICROCODE VERSION
	DPB	T1,[POINTR (T2,KP%CHN)] ;SET RH20 CHANNEL
	TXO	T2,KP%NI	;AND KLNI FLAG
	POP	P,T1		;RESTORE T1
	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/ NUMBER OF ARGUMENTS
;	P2/ DIAG. UUO FUNCTION CODE
;	P3/ SUBROUTINE (FROM KNIDIA)
;	W/  KDB (PCB) ADDRESS
;	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:	EXP	KNIPPR		;PREPROCESSOR ROUTINE
	DIAFNC	(AAU,DIAAAU,)	;ASSIGN ALL UNITS
	DIAFNC	(RAU,DIARAU,)	;RELEASE CHANNEL AND ALL UNITS
	DIAFNC	(SCP,DIASCP,)	;SPECIFY CHANNEL PROGRAM
	DIAFNC	(RCP,DIARCP,)	;RELEASE CHANNEL PROGRAM
	DIAFNC	(GCS,DIACST,)	;GET CHANNEL STATUS
	DIAFNC	(ELD,DIAELD,)	;ENABLE MICROCODE LOADING
	DIAFNC	(DLD,DIADLD,)	;DISABLE MICROCODE LOADING
	DIAFNC	(LOD,DIALOD,)	;LOAD MICROCODE
	DIAFNC	(ISM,DIAISM,)	;SET MAINTENANCE MODE
	DIAFNC	(ICM,DIAICM,)	;CLEAR MAINTENANCE MODE
	DIAFNC			;TERMINATE TABLE

;PREPROCESSOR ROUTINE
KNIPPR:	PUSHJ	P,SAVE4##	;SAVE P1-P4
	PUSHJ	P,SAVQ##	;AND Q1-Q3
	MOVSI	T1,JP.POK	;JOB HAVE SUFFICIENT PRIVILEGES?
	PUSHJ	P,PRVBIT##	;...
	  SKIPA	Q3,W		;GET PORT CONTROL BLOCK (KDB) ADDRESS
	PJRST	DIAPRV		;NO, GIVE ERROR RETURN
	CAIL	P2,.DIELD	;THESE FUNCTIONS DON'T REQUIRE MAINTENANCE MODE
	CAILE	P2,.DIICM	;...
	SKIPA			;NOT A SPECIAL FUNCTION
	PJRST	(P3)		;YES, DISPATCH NOW
	MOVX	T1,PBSMAI	;IN MAINTENANCE MODE?
	TDNN	T1,.PBSTS(Q3)	;...
	JRST	DIAADM##	;NO, RETURN AN ERROR
	PJRST	(P3)		;DISPATCH BASED ON FUNCTION CODE
;(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	DIAAAJ##	;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

DIARAU:	LDB	T1,PBPMJB	;GET MAINTENANCE JOB NUMBER
	CAME	T1,J		;SAME AS CALLER'S JOB?
	JRST	DIAAAJ##	;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	DIAAAJ##	;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,DIAACP##	;TOO BIG IF 0
	MOVEM	T1,CHNICW(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 CHNICW(P3) ;PAGE NOT THERE
		 JRST  DIAACP##] ;BOMB HIM OUT
	SETZB	P1,P4		;SAY FIRST CALL, NOT A DX10
	MOVE	T2,CHNICW(P3)	;GET IOWD
	S0PSHJ	MAPIO##		;RELOCATE THE IOWD
	  JRST	[SETZM CHNICW(P3)
		 JRST   DIAAFC##] ;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,CHNICW(P3)	;STORE ADDRESS OF CHANNEL PROGRAM
	TLO	P2,(FLD(.CCJMP,CC.OPC)) ;MAKE ICW BE A JUMP
	LDB	T1,[POINT 3,KDBCNO(Q3),9] ;GET RH20 NUMBER
	LSH	T1,2		;COMPUTE EPT OFFSET TO CHANNEL LOGOUT AREA
	ADD	T1,.CPEPT##	;PLUS ADDRESS OF EPT
	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	DIAAAJ##	;NO, RETURN ERROR
	MOVE	P3,.PBCDB(Q3)	;GET CHANNEL DATA BLOCK ADDRESS
	SKIPN	T1,CHNICW(P3)	;NOTHING TO DO IF NO IOWD
	POPJ	P,
	SETZM	CHNICW(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	DIAAAJ##	;NO, RETURN ERROR
	LDB	P2,[POINT 3,KDBCNO(Q3),9] ;GET RH20 NUMBER
	LSH	P2,2		;COMPUTE EPT OFFSET TO CHANNEL LOGOUT AREA
	ADDI	P2,.CSICW	;PLUS OFFSET TO ICWA WORD
	ADD	P2,.CPEPT##	;PLUS ADDRESS OF EPT
	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,DIAARF##	;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) SET MAINTENANCE MODE

DIAISM:	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	@.PBBIT(Q3)	;ZAP BITS TO TEST FOR ON INTERRUPT
	MOVE	T1,.CPBIT##	;GET OUR CPU'S BIT
	IORM	T1,IPAMSK##	;DON'T LET THIS CHANNEL START IF SUSPENDED
	PJRST	CPOPJ1##	;AND RETURN


;(23) CLEAR MAINTENANCE MODE

DIAICM:	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	;...
	MOVEI	T1,CO.CPT	;MAKE SURE KLNI IS STOPPED
	XCT	KDBCNO(Q3)	;...
	MOVE	T1,[KNIBTS]	;BITS TO TEST FOR ON INTERRUPT
	MOVEM	T1,@.PBBIT(Q3)	;STORE IN CONSO SKIP CHAIN CODE
	MOVE	T1,.CPBIT##	;GET OUR CPU'S BIT
	ANDCAM	T1,IPAMSK##	;LET THIS CHANNEL START IF SUSPENDED
	PUSHJ	P,RLDKNI	;INITIATE AUTO-RELOAD OF KLNI
	PJRST	CPOPJ1##	;AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  KNIBT. UUO

IFE FTKNILDR,<
UKNIBT::	POPJ	P,	;NO KNIBT. UUO
>;END IFE FTKNILDR
IFN FTKNILDR,<

;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

UKNIBT::PUSHJ	P,SAVE4##	;SAVE P1-P4
	PUSHJ	P,SAVQ##	;AND Q1-Q3
	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
	MOVSI	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,M.CPU##	;...
	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
	SKIPN	Q3,.CPNPB##	;GET ADDRESS OF PORT CONTROL BLOCK
	PJRST	KBTKDE		;KLNI DOESN'T EXIST
	LDB	T1,[POINT 3,KDBCNO(Q3),9] ;GET RH20 NUMBER
	HRL	T1,.CPCPN##	;MAKE IT CPU#,,RH20#
	CAME	T1,P3		;MATCH?
	PJRST	KBTKDE		;NO, 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
	MOVE	T2,.PBSTS(Q3)	;GET CURRENT KLNI STATUS
	TXNE	T2,PBSRUN	;IS KLNI RUNNING?
	TXOA	T1,KS.RUN	;YES, MARK AS RUNNING
	TXO	T1,KS.RLD	;NO, MARK AS NEEDING TO BE RELOADED
	TXNE	T2,PBSRLD	;RELOAD REQUESTED?
	TXO	T1,KS.RRQ	;YES, MARK RELOAD REQUESTED BY SYSTEM
	TXNE	T2,PBSDMP	;DUMP REQUESTED?
	TXO	T1,KS.DRQ	;YES, MARK DUMP REQUESTED BY SYSTEM
	TXNE	T2,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	TXO	T1,KS.MAI	;YES, MARK IN RETURNED STATUS
	TXNE	T2,PBSARD	;IS AUTO-RELOAD DISABLED?
	TXO	T1,KS.ARD	;YES, MARK IN RETURNED STATUS
	TXNE	T2,PBSADD	;IS AUTO-DUMP DISABLED?
	TXO	T1,KS.ADD	;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
	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
	PUSH	P,W		;SAVE AC
	MOVE	W,Q3		;PUT KDB (PCB) ADDRESS IN PROPER PLACE
	PUSHJ	P,RDIPA##	;READ KLNI CRAM LOCATION
	  JRST	[POP	P,W	;RESTORE AC
		 PJRST	KBTCRE]	;CRAM READ ERROR
	POP	P,W		;RESTORE AC
	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
	PUSH	P,W		;SAVE W
	MOVE	W,Q3		;PUT KDB (PCB) ADDRESS IN PROPER PLACE
	PUSHJ	P,WTIPA##	;WRITE CRAM LOCATION
	  JRST	[POP	P,W	;RESTORE W
		 PJRST	KBTCWE]	;CRAM WRITE ERROR
	POP	P,W		;RESTORE W
	PJRST	CPOPJ1##	;AND RETURN
>;END IFN FTKNILDR
	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:	MOVEI	T1,CO.CPT	;MAKE SURE THE KLNI IS STOPPED
	XCT	KDBCNO(Q3)	;...
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)	;...
	MOVEI	T1,CO.CPT	;RESET KLNI
	XCT	KDBCNO(Q3)	;...
	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
	PUSH	P,T2		;SAVE T2
	MOVE	T2,T1		;COPY
	XCT	KDBDTO(Q3)	;DATAO DEV,T2
	POP	P,T2		;RESTORE T2
	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,MP.NAD	;...
	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,MP.NAD	;...
	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,MP.NAD	;...
	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
	TXO	T1,CO.DIS!CO.BTS ;PLUS DISABLE STATE
	XCT	KDBCNO(Q3)	;TELL KLNI
	MOVEI	T2,5000		;LOOP COUNT
DISKN1:	XCT	KDBCNI(Q3)	;CONI DEV,T1
	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
	TXO	T1,CO.ENA!CO.BTS ;PLUS ENABLED STATE
	XCT	KDBCNO(Q3)	;TELL KLNI
	MOVEI	T2,5000		;LOOP COUNT
ENAKN1:	XCT	KDBCNI(Q3)	;CONI DEV,T1
	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
IFE FTKNILDR,<
	POPJ	P,		;RETURN
>;END IFE FTKNILDR
IFN FTKNILDR,<
	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
>;END IFN FTKNILDR
;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&377	;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,MP.NAD	;...
	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,MP.NAD	;...
	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,MP.NAD	;...
	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)	;...
IFN FTKNILDR,<
	LDB	T1,PBPRJB	;GET JOB NUMBER OF RELOAD JOB
	SKIPE	T1		;JOB NUMBER SET?
	PUSHJ	P,WAKJOB##	;YES, WAKE IT UP
>;END IFN FTKNILDR
	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  --  REQUEST MICROCODE DUMP/LOAD


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

DMPKNI:	SKIPA	T1,[PBSDMP!PBSRLD] ;GET DUMP AND RELOAD REQUEST FLAGS
LODKNI:	MOVX	T1,PBSRLD	;GET RELOAD REQUEST FLAG
	JRST	RLDKN1		;AND CONTINUE
RLDKND:	SKIPA	T1,[PBSDMP!PBSRLD] ;GET DUMP AND RELOAD REQUEST FLAGS
RLDKNI:	MOVX	T1,PBSRLD	;GET RELOAD REQUEST FLAG
	MOVE	T2,.PBSTS(Q3)	;GET KLNI STATUS WORD
	TXNE	T2,PBSMAI	;IS KLNI IN MAINTENANCE MODE?
	POPJ	P,		;YES, RETURN NOW
	TXNE	T2,PBSADD	;IS AUTO DUMP DISABLED?
	TXZ	T1,PBSDMP	;YES, CLEAR DUMP REQUEST FLAG
	TXNE	T2,PBSARD	;IS AUTO RELOAD DISABLED?
	TXZ	T1,PBSDMP!PBSRLD ;YES, CLEAR DUMP AND RELOAD REQUEST FLAGS
RLDKN1:	IORM	T1,.PBSTS(Q3)	;SET DUMP/RELOAD REQUEST FLAG(S)
	TXNN	T1,PBSRLD	;REALY WANT A RELOAD OR NOT NOW!?
	POPJ	P,		;NO - JUST FORGET IT.
	AOS	.PBKLC(Q3)	;UPDATE COUNT OF TIMES DUMP/RELOAD REQUESTED
	XMOVEI	T1,.PBULB(Q3)	;GET ADDRESS OF MICROCODE LOADER BLOCK
	PUSHJ	P,BTUCOD##	;LOCATE KLNI MICROCODE
	  JRST	[XMOVEI	T1,.PBULB(Q3)	;POINT TO MICROCODE LOADER BLOCK
		 PJRST	BTURPT##]	;GO REPORT THE ERROR
	PJRST	KNILOD		;LOAD KLNI MICROCODE AND RETURN
	SUBTTL	KLNI MAINTENANCE SERVICE  --  LOAD MICROCODE


;ROUTINE TO LOAD KLNI MICROCODE
;LINKAGE:
;	Q3/ ADDRESS OF PORT CONTROL BLOCK
;	PUSHJ	P,KNILOD
;RETURNS:
;	CPOPJ ALWAYS

KNILOD:	MOVX	T1,PBSQRR	;CLEAR QUEUED RELOAD REQUEST FLAG
	ANDCAM	T1,.PBSTS(Q3)	;...
	LDB	T2,PBPCPU	;GET CPU NUMBER OF KLNI
	CAMN	T2,.CPCPN##	;KLNI ON OUR CPU?
	PJRST	KNILO1		;YES, GO SERVICE RELOAD REQUEST
	IORM	T1,.PBSTS(Q3)	;SET QUEUED RELOAD REQUEST FLAG
	POPJ	P,		;AND RETURN

KNILO1:	PUSHJ	P,SAVE1##	;SAVE P1
	XMOVEI	P1,.PBULB(Q3)	;POINT TO MICROCODE LOADER BLOCK
	PUSHJ	P,SHTKNI	;SHUTDOWN KLNI
	PUSH	P,W		;SAVE W
	MOVE	W,Q3		;SET UP KDB FOR DMPIPA/LDIPA
	MOVX	T1,PBSDMP	;BIT TO TEST
	TDNE	T1,.PBSTS(Q3)	;WANT A DUMP?
	PUSHJ	P,DMPIPA##	;DUMP THE KLNI DRAM
	  JFCL			;FAILED, DON'T REALLY CARE
	MOVE	T1,.ULADR(P1)	;GET ADDRESS OF MICROCODE STORAGE
	PUSHJ	P,LDIPA##	;LOAD THE CRAM
	  JRST	KNILO3		;CHANNEL IS RUNNING
	MOVX	T1,UCODSA	;GET MICROCODE START ADDRESS
	MOVEM	T1,.PBKSA(Q3)	;SAVE IN KLNI PCB
	PUSHJ	P,STAKNI	;START THE MICROCODE
	  JRST	KNILO3		;ERROR STARTING MICROCODE
	PUSHJ	P,INIKNI	;BEGIN INITIALIZATION OF KLNI
	  JRST	KNILO3		;ERROR INITIALIZING KLNI
	AOS	-1(P)		;SKIP RETURN
	JRST	KNILO4		;GO FINISH UP

KNILO3:	MOVEI	T1,.UEMPC	;MICROPROCESSOR CHECK
	DPB	T1,ULBEBP##	;STORE

KNILO4:	POP	P,W		;RESTORE W
	MOVE	T1,P1		;COPY LOADER BLOCK ADDRESS
	PJRST	BTURPT##	;GO REPORT A SUCCESSFUL LOAD
	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,MP.NAD	;...
	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:	SKIPGE	.QHIWD(T1)	;SKIP IF INTERLOCK NOT AVAILABLE
	AOSE	.QHIWD(T1)	;AVAILABLE, TRY TO GET IT
	AOSA	T3		;NOT AVAILABLE, INCREMENT COUNT AND SKIP
	JRST	REMQU2		;GOT IT
	CAIGE	T3,TIMOUT	;WAITED LONG ENOUGH?
	JRST	REMQU1		;NO, TRY AGAIN
	AOS	.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,MP.NAD	;...
	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
	PMOVE	T2,T2		;...
	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
	PMOVEM	T3,T2		;...
	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:	SKIPGE	.QHIWD(T1)	;SKIP IF INTERLOCK NOT AVAILABLE
	AOSE	.QHIWD(T1)	;AVAILABLE, TRY TO GET IT
	AOSA	T3		;NOT AVAILABLE, INCREMENT COUNT AND SKIP
	JRST	PUTQU2		;GOT IT
	CAIGE	T3,TIMOUT	;WAITED LONG ENOUGH?
	JRST	PUTQU1		;NO, TRY AGAIN
	AOS	.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,MP.NAD	;...
	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
	PMOVEM	T3,T2		;...
	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,MP.NAD	;...
	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