Google
 

Trailing-Edge - PDP-10 Archives - bb-m780d-sm - monitor-sources/phykni.mac
There are 19 other files named phykni.mac in the archive. Click here to see a list.
; UPD ID= 8606, RIP:<7.MONITOR>PHYKNI.MAC.6,  11-Feb-88 17:12:38 by RASPUZZI
;TCO 7.1221 - Teach PHYKNI that where the major version of the NI ucode
;	      really is. When we start the KLNI, if there is bas NI ucode,
;	      start the KLNI anyway and BUGCHK.
; UPD ID= 8586, RIP:<7.MONITOR>PHYKNI.MAC.5,  11-Feb-88 16:54:57 by GSCOTT
;TCO 7.1218 - Update copyright date.
; UPD ID= 8433, RIP:<7.MONITOR>PHYKNI.MAC.4,   4-Feb-88 15:00:20 by GSCOTT
;TCO 7.1210 - Set KNIADR, KNICAE, KNICCF, KNICDF, KNICFF, KNICPE, KNIDM1,
; KNIDOV, KNIDPE, KNIEPE, KNIERE, KNIFBE, KNIFST, KNIFTL, KNIGCE, KNIHED,
; KNIINF, KNIIPE, KNIPER, KNIPIE, KNIRFD, KNIRIT, KNIRLF, KNISCE, KNISTA,
; KNISTP, KNISWC, KNIUBE, KNIUPE, KNIVER, and KNIXPE normally not dumpable.
; Also update TOC.
; UPD ID= 8, RIP:<7.MONITOR>PHYKNI.MAC.3,  28-May-87 16:10:22 by RASPUZZI
;TCO 7.1002 - Implement PMOVE/M instructions.
; *** Edit 7326 to PHYKNI.MAC by MCCOLLUM on 23-Jun-86
; Store the new physical address in UNCAR instead of UNDAD in NISCA 
; *** Edit 7286 to PHYKNI.MAC by MCCOLLUM on 14-Apr-86
; Store the ethernet address in the UN block before issuing the callbacks in
; NISCA 
; Edit 7174 to PHYKNI.MAC by PALMIERI on 28-Oct-85 (TCO 6.1.1544)
; Fix invalid KNIIEC BUGHLTs. 
; Edit 7134 to PHYKNI.MAC by GRANT on 15-Aug-85 (TCO 6-1-1522)
; Turn off the NI at BUGHLT and PAR>SHUT time 
; UPD ID= 2295, SNARK:<6.1.MONITOR>PHYKNI.MAC.117,  15-Jul-85 15:14:06 by PALMIERI
;TCO 6.1.1482  Add routine KNIRSC to reset all channels if requested by
; PHYSIO (usually after a power failure).
; UPD ID= 2292, SNARK:<6.1.MONITOR>PHYKNI.MAC.116,   9-Jul-85 14:00:33 by PALMIERI
;Change call to BUGHLT to a RET at KNIDSP reset channel function from PHYSIO.
;It was assumed this call would never be made.  This is temporary until we
;decide if reloading the KLNI is the appropriate thing to do.
; UPD ID= 2285, SNARK:<6.1.MONITOR>PHYKNI.MAC.115,  28-Jun-85 11:15:19 by MCCOLLUM
;TCO 6.1.1238 - Fix more BUG. documentation
; UPD ID= 2224, SNARK:<6.1.MONITOR>PHYKNI.MAC.114,  17-Jun-85 11:10:28 by GROSSMAN
;TCO 6.1.1452 - Fix symbols for SYSERR related stuff.
;TCO 6.1.1453 - Fix keep-alive stuff to be more sensitive to messed up command
;queues.
; UPD ID= 2162, SNARK:<6.1.MONITOR>PHYKNI.MAC.113,   5-Jun-85 10:42:54 by GROSSMAN
;TCO 6.1.1425 - Make KNIIPF a BUGHLT, start removing TOPS-10 conditionals.
;TCO 6.1.1424 - Use seperate BUGINFs for each planned CRAM parity error.
; UPD ID= 2027, SNARK:<6.1.MONITOR>PHYKNI.MAC.111,  28-May-85 16:59:22 by GROSSMAN
;TCO 6.1.1413 - Fix NIRPI to return multicasts correctly.
; UPD ID= 2004, SNARK:<6.1.MONITOR>PHYKNI.MAC.110,  24-May-85 12:02:23 by MCCOLLUM
;Fix KNIPPE Bug.
; UPD ID= 1999, SNARK:<6.1.MONITOR>PHYKNI.MAC.109,  23-May-85 14:29:27 by MCCOLLUM
;TCO 6.1.1238 - Fix more BUG. documentation
; UPD ID= 1955, SNARK:<6.1.MONITOR>PHYKNI.MAC.108,  10-May-85 16:47:32 by GROSSMAN
;TCO 6.1.1376 - Make NIINI non global.
; UPD ID= 1951, SNARK:<6.1.MONITOR>PHYKNI.MAC.107,   9-May-85 17:39:35 by MCCOLLUM
;TCO 6.1.1238 - Fix more BUG. documentation
; UPD ID= 1940, SNARK:<6.1.MONITOR>PHYKNI.MAC.106,   9-May-85 16:51:20 by GROSSMAN
;TCO 6.1.1372 - Fix ERRDSP dispatch table to not jump into section 0 when
;an unknown error code is received from the port.
; UPD ID= 1920, SNARK:<6.1.MONITOR>PHYKNI.MAC.105,   7-May-85 21:11:35 by MCCOLLUM
;TCO 6.1.1238 - Fix more BUG. documentation
; UPD ID= 1914, SNARK:<6.1.MONITOR>PHYKNI.MAC.104,   6-May-85 20:11:50 by GROSSMAN
;TCO 6.1.1364 - Check for existance of KLNI before playing with it.
; UPD ID= 1863, SNARK:<6.1.MONITOR>PHYKNI.MAC.103,   2-May-85 17:15:51 by GROSSMAN
;TCO 6.1.1355 - Prevent KNIIPT BUGHLTs by preventing NIDPT from giving
;error returns.
; UPD ID= 1843, SNARK:<6.1.MONITOR>PHYKNI.MAC.102,  30-Apr-85 11:55:45 by MCCOLLUM
;TCO 6.1.1238 - Fix more BUG. documentation
; UPD ID= 1743, SNARK:<6.1.MONITOR>PHYKNI.MAC.101,   9-Apr-85 14:03:52 by MCCOLLUM
;More of TCO 6.1.1238 - Fix BUG. documentation
; UPD ID= 1742, SNARK:<6.1.MONITOR>PHYKNI.MAC.100,   9-Apr-85 14:01:08 by MCCOLLUM
;TCO 6.1.1238 - Fix BUG. documentation
; UPD ID= 1678, SNARK:<6.1.MONITOR>PHYKNI.MAC.99,  25-Mar-85 10:58:08 by GROSSMAN
;TCO 6.1.1285 - Fix KNIRTO.  Remove code under FTCKM, and fix NF.RPI.
; UPD ID= 1667, SNARK:<6.1.MONITOR>PHYKNI.MAC.98,  22-Mar-85 11:27:54 by GROSSMAN
;More TCO 6.1.1283 - Change various BUGINF's to BUGCHK's and vice versa.  Also,
;fix text for KNICFF.
; UPD ID= 1662, SNARK:<6.1.MONITOR>PHYKNI.MAC.97,  21-Mar-85 23:22:15 by GROSSMAN
;TCO 6.1.1283 - Fix KNISTP.  Make KNIRFD a BUGINF, KNIUOP a BUGHLT.  Fix
;some bugs in code under FTHIST.
; UPD ID= 1561, SNARK:<6.1.MONITOR>PHYKNI.MAC.96,  22-Feb-85 17:17:25 by GROSSMAN
;TCO 6.1.1217 - Don't set Ethernet address to 0 if it hasn't been initialized.
; UPD ID= 1541, SNARK:<6.1.MONITOR>PHYKNI.MAC.95,  20-Feb-85 10:00:55 by GROSSMAN
;More TCO 6.1.1204 - Correct TCO number in edit 1529.
; UPD ID= 1534, SNARK:<6.1.MONITOR>PHYKNI.MAC.94,  19-Feb-85 12:00:45 by GROSSMAN
;TCO 6.1.1209 - Clean up the response queue in KNIERR so that we get RQA
;interrupts when we restart the port.
; UPD ID= 1530, SNARK:<6.1.MONITOR>PHYKNI.MAC.93,  18-Feb-85 23:36:07 by GROSSMAN
;TCO 6.1.1205 - Fix wrong code at NIDPT + a few.
; UPD ID= 1529, SNARK:<6.1.MONITOR>PHYKNI.MAC.92,  18-Feb-85 23:13:17 by GROSSMAN
;TCO 6.1.1204 - Fix detection of Planned CRAM Parity Errors.
; UPD ID= 1508, SNARK:<6.1.MONITOR>PHYKNI.MAC.91,  12-Feb-85 14:45:53 by GROSSMAN
;TCO 6.1.1181 - Add new version number format support for the KLNI.
; UPD ID= 1465, SNARK:<6.1.MONITOR>PHYKNI.MAC.90,   2-Feb-85 11:38:59 by GROSSMAN
;TCO 6.1.1170 - Call NISTP before doing a KNISTP so that we get the micro PC.
; UPD ID= 1463, SNARK:<6.1.MONITOR>PHYKNI.MAC.89,   2-Feb-85 11:28:20 by GROSSMAN
;TCO 6.1.1169 - Create a CDB and dispatch table so that DIAG% can manipulate
;the KLNI's channel logout area.  In addition, make ENBKLP call NISTP when
;initialization fails.
; UPD ID= 1424, SNARK:<6.1.MONITOR>PHYKNI.MAC.88,  29-Jan-85 14:36:19 by GROSSMAN
;TCO 6.1.1162 - Add state UNS.RR - Request Reload.  Setting this state causes
;STRLD to be called (which eventually makes KNILDR run and reload the KLNI).
; UPD ID= 1408, SNARK:<6.1.MONITOR>PHYKNI.MAC.87,  28-Jan-85 09:53:51 by GROSSMAN
;Do previous edit in a better way.
; UPD ID= 1396, SNARK:<6.1.MONITOR>PHYKNI.MAC.86,  22-Jan-85 23:59:53 by PAETZOLD
;TCO 6.1.1152 - Make sure buffers coming out of NIDPT2 have a clear address.
; UPD ID= 1348, SNARK:<6.1.MONITOR>PHYKNI.MAC.85,  16-Jan-85 13:41:44 by GROSSMAN
;More TCO 6.1.1122 - Work around KNISV for RDTIME bug in non-zero sections.
; UPD ID= 1341, SNARK:<6.1.MONITOR>PHYKNI.MAC.84,  15-Jan-85 23:59:31 by GROSSMAN
;More TCO 6.1.1137 - Call RUNDII in the correct section.  Make sure all
;addresses passed to RUNDII are global.
; UPD ID= 1339, SNARK:<6.1.MONITOR>PHYKNI.MAC.83,  15-Jan-85 14:24:27 by GROSSMAN
;TCO 6.1.1137 - Move KNIJB0 to section 6 so it can live harmoniously with the
;rest of NISRV.
;TCO 6.1.1136 - Remove all references to KNIN.
; UPD ID= 1310, SNARK:<6.1.MONITOR>PHYKNI.MAC.82,  11-Jan-85 14:06:29 by PAETZOLD
;Do not use KNIN for assembly control since it is a global symbol from STG.
; UPD ID= 1291, SNARK:<6.1.MONITOR>PHYKNI.MAC.81,   9-Jan-85 11:05:37 by GROSSMAN
;TCO 6.1.1122 - Move to section 6.  Fix NIRSI to prevent KNISTPs.  Don't call
;SETSTA with bogus PS from KNIJB0.  Fix GETCOR so that 4 word alignment is no
;longer done.
; UPD ID= 1275, SNARK:<6.1.MONITOR>PHYKNI.MAC.80,   6-Jan-85 15:20:32 by GROSSMAN
;TCO 6.1.1113 - Put KNIFQE, KNIDMD/DM1 under control of NIBUGX.
; UPD ID= 1267, SNARK:<6.1.MONITOR>PHYKNI.MAC.79,   4-Jan-85 09:06:26 by GROSSMAN
;TCO 6.1.1109 - Rewrite NIDRA & implement a multicast address use count.  Re-do
;NIWSI & friends so that they won't smash the promiscuous & promiscuous
;multicast bits.  Accumulate total time at interrupt level in TOTINT.  Add a
;memory request cache for xmits & receives.  Get rid of all code under DEBUG.
;Remove extra RESCDs.  Get rid of various NF. functions and routines.  Zero
;shadow counters during KLNI restarts.  Increase KNIRTO timeout value.  Re-do
;NIEPT and FNDPT to make protocol types -2 & -3 work (and work nicely).  Make
;NIDPT also disable all multicasts associated with the portal.
; UPD ID= 1187, SNARK:<6.1.MONITOR>PHYKNI.MAC.78,  11-Dec-84 14:56:28 by GROSSMAN
;More TCO 6.1.1072 - Detect length errors at the correct place, and fix error
;code UNRFD%.
; UPD ID= 1149, SNARK:<6.1.MONITOR>PHYKNI.MAC.77,   5-Dec-84 00:37:50 by GROSSMAN
;TCO 6.1.1072 - Remove NIRES and friends.  Add error recovery for response
;errors.
; UPD ID= 1106, SNARK:<6.1.MONITOR>PHYKNI.MAC.76,  20-Nov-84 01:01:28 by GROSSMAN
;TCO 6.1.1061 - Rewrite SETSTA.  Make sure external state properly reflects
;internal state.
; UPD ID= 1084, SNARK:<6.1.MONITOR>PHYKNI.MAC.75,  16-Nov-84 15:18:32 by GROSSMAN
;More of TCO 6.1.1051 - Don't start timing until job 0 runs.
; UPD ID= 1068, SNARK:<6.1.MONITOR>PHYKNI.MAC.74,  13-Nov-84 18:00:55 by GROSSMAN
;TCO 6.1.1051 - Implement timeout for KNILDR.  If KNILDR doesn't complete in
; 15. seconds, give a KNIRTO BUGCHK and put port in UNS.CR state.
; UPD ID= 1048, SNARK:<6.1.MONITOR>PHYKNI.MAC.73,  13-Nov-84 00:10:12 by GROSSMAN
; TCO 6.1.1047 - Many fixes in preparation for NI% JSYS.
; - Implement command abort for dead ports.
; - Make receive abort code (NIDPT) use MSGAVA.
; - Add KNICRS as mousetrap for spurious KNISTP's.
; - Remove NISCH (and CALL in LV8CHK).
; - KNISTP now calls NISTP to really stop the port.
; - Change XCTUU to XCTU in FIXBSD for user mode address case.
; - Make sure that RCRCC zeroes Receive Failure Bit Mask if Receive Failure
;    Counter is 0.
; - Fix race in NISTP by disabling PSCQA before stopping port.
; UPD ID= 784, SNARK:<6.1.MONITOR>PHYKNI.MAC.69,   4-Sep-84 14:04:26 by GROSSMAN
; Fix NSARD for the case when no address is ever set.
; UPD ID= 781, SNARK:<6.1.MONITOR>PHYKNI.MAC.68,   4-Sep-84 00:30:26 by GROSSMAN
; Clear the KLNI at the beginning of NIINIA.  Don't do anything if user is
; setting KLNI state to it's current value.
; UPD ID= 767, SNARK:<6.1.MONITOR>PHYKNI.MAC.67,  30-Aug-84 22:28:29 by GROSSMAN
; Fix management of port variables.  Make port restart code work right.
; UPD ID= 763, SNARK:<6.1.MONITOR>PHYKNI.MAC.66,  27-Aug-84 15:30:09 by GROSSMAN
; Various and sundry fixes for port restart.
; UPD ID= 749, SNARK:<6.1.MONITOR>PHYKNI.MAC.65,  23-Aug-84 09:30:59 by GROSSMAN
; Start putting in auto reload code for KLNI.
; Fix a possible buffer corruption bug in FIXBSD.
; Implement NISCS (Set Channel State).
; Remove some junk code.
; UPD ID= 737, SNARK:<6.1.MONITOR>PHYKNI.MAC.64,  10-Aug-84 17:33:04 by GROSSMAN
; Fix reading of the ROM address, and re-do some of the keep-alive code.  Also,
; don't check state anymore in QUECMD - PSCQA will change to do the correct
; thing based upon the current state of the channel.
; UPD ID= 720, SNARK:<6.1.MONITOR>PHYKNI.MAC.63,  28-Jul-84 14:30:51 by GROSSMAN
; Fix initialization bug by not setting CQA and ENA simultaneously in ENBKLP.
; UPD ID= 715, SNARK:<6.1.MONITOR>PHYKNI.MAC.62,  27-Jul-84 00:19:14 by GROSSMAN
; Implement BSD receives and new un-scrambled address format.  Start
; implementing state machine.  Many minor bug fixes.
; UPD ID= 557, SNARK:<6.1.MONITOR>PHYKNI.MAC.61,  24-May-84 14:03:23 by GROSSMAN
; Add history buffer maintenance code.
; Fix bug in NIDPT.  Don't use protocol type as a monitor address.  Clear T3
; on return from read counters.
; UPD ID= 521, SNARK:<6.1.MONITOR>PHYKNI.MAC.60,  14-May-84 10:05:25 by GROSSMAN
; Fix non-contiguous transmit buffers.  Fix race in .NION and .NIOFF.
; UPD ID= 466, SNARK:<6.1.MONITOR>PHYKNI.MAC.59,  27-Apr-84 13:41:32 by GROSSMAN
; Implement NISRV.MEM.
; UPD ID= 368, SNARK:<6.1.MONITOR>PHYKNI.MAC.58,   2-Mar-84 02:27:21 by GROSSMAN
; Fix counters.  Expand counters block for extra reserved words required by
; KLNI error detection/recovery.
; Speed up posting of receive buffers by remembering free-q header address in
; PRFQA instead of searching the PTT.
; UPD ID= 336, SNARK:<6.1.MONITOR>PHYKNI.MAC.57,  17-Feb-84 00:50:59 by GROSSMAN
; Fix moby race caused by allowing scheduler to run while NIPIA channel is off!
; Copy counters from Read Counters block to the user's buffer.
; UPD ID= 330, SNARK:<6.1.MONITOR>PHYKNI.MAC.56,  10-Feb-84 16:29:42 by GROSSMAN
; NISRB - Check receive buffers for physical contiguity.
; UPD ID= 318, SNARK:<6.1.MONITOR>PHYKNI.MAC.55,   8-Feb-84 17:39:26 by GROSSMAN
; Many numerous and sundry fixes to avoid race due to NP blocks.  NP blocks
; go away.  New AC conventions.  Considerable speedups in transmit and
; receive paths.  Interrupts cleaned up.  Some support for error recovery.
; Read ucode version before starting the port, and make it more accessible to
; humans by storing it into a full word field.  Also, add code to distinguish
; between the ROM address and a software address.  PSHAD & PSLAD now contain
; the soft address, PSHRA & PSLRA now contain the ROM address.
; UPD ID= 235, SNARK:<6.1.MONITOR>PHYKNI.MAC.54,   7-Nov-83 21:42:25 by GROSSMAN
; Add new dispatch to KNIDSP for PORT RELEASE function.
; UPD ID= 229, SNARK:<6.1.MONITOR>PHYKNI.MAC.53,  28-Oct-83 14:52:21 by GROSSMAN
; Make GETCOR a little smarter about BNC's.  Have it first call GETCOR to
; get a new block, then call RELCOR to release the bad block.
; UPD ID= 227, SNARK:<6.1.MONITOR>PHYKNI.MAC.52,  27-Oct-83 14:08:08 by GROSSMAN
; Make GETCOR try again when it gets a BNC.
; UPD ID= 223, SNARK:<6.1.MONITOR>PHYKNI.MAC.51,  26-Oct-83 10:36:30 by GROSSMAN
; Start writing new chunk manager.  This is under the FTCKM conditional.
; Fix hung port problem by making CHKBUF a little smarter.  It didn't handle
; the case where physical memory ran backwards w/respect to virtual memory.
;
;WORK:<GROSSMAN.BUILD>PHYKNI.MAC.69 24-Oct-83 14:26:40, Edit by GROSSMAN
; Only report line state change once when in maintenance mode.
; Make CHKBUF smarter about page boundaries.
; Take remainder into account when converting buffer size into words.
; Add more info to KNISTP.  Print out the LAR, and the CONI.
; UPD ID= 192, SNARK:<6.1.MONITOR>PHYKNI.MAC.50,   9-Aug-83 10:55:01 by GROSSMAN
; Convert buffer size to words when queueing up receive buffers (NISRB).
; Don't report errors twice, only report them at interrupt level.
; UPD ID= 172, SNARK:<6.1.MONITOR>PHYKNI.MAC.49,  22-Jul-83 10:23:33 by GROSSMAN
; Include CO.BTS when doing the CONO to clear FQE.
; UPD ID= 171, SNARK:<6.1.MONITOR>PHYKNI.MAC.48,  22-Jul-83 01:49:05 by GROSSMAN
; Use different symbol for ucode word in read counters response block.
; Handle Free Queue Errors differently.
; UPD ID= 169, SNARK:<6.1.MONITOR>PHYKNI.MAC.47,  21-Jul-83 19:51:57 by GROSSMAN
; Add comment to 168
; UPD ID= 168, SNARK:<6.1.MONITOR>PHYKNI.MAC.46,  21-Jul-83 19:45:13 by GROSSMAN
; Add one word to the Read Counters response block.
; UPD ID= 159, SNARK:<6.1.MONITOR>PHYKNI.MAC.45,  11-Jul-83 15:04:42 by RIZZOLO
; Move the NI.xxx symbols from PHYKNI into D36PAR so NIDLL can use them.
; UPD ID= 157, SNARK:<6.1.MONITOR>PHYKNI.MAC.44,   1-Jul-83 10:42:05 by LEAPLINE
; UPD ID= 156, SNARK:<6.1.MONITOR>PHYKNI.MAC.43,   1-Jul-83 10:30:44 by LEAPLINE
;Do some general clean up.
; UPD ID= 148, SNARK:<6.1.MONITOR>PHYKNI.MAC.41,  15-Jun-83 14:50:35 by LEAPLINE
;Change code at NIDRA to get the address from the correct place (NPDID)
;Make checks at all entry points for channel being a KLNI port.
; UPD ID= 128, SNARK:<6.1.MONITOR>PHYKNI.MAC.35,  10-May-83 12:13:54 by LEAPLINE
;Remove read counters test code.
; UPD ID= 125, SNARK:<6.1.MONITOR>PHYKNI.MAC.33,   9-May-83 11:43:00 by LEAPLINE
;Make use of CDB's.
;Remove channel independant code. IOEXE is REPEAT 0'ED OUT.
;SNARK:<6.1.MONITOR>PHYKNI.MAC.10, 7-APR-83 10:16:11 by RIZZOLO
;Fix Protocol type problems at SETFQH+some.
;Fixup REDIT changes.
;SNARK:<6.1.MONITOR>PHYKNI.MAC.10, 16-Mar-83 14:14:36 by RIZZOLO
;Add code to support the new DLL to port driver interfaces.
; UPD ID= 54, SNARK:<6.1.MONITOR>PHYKNI.MAC.9,  22-Feb-83 12:16:45 by LEAPLINE
;Make DSPTBL all illegal functions.
; UPD ID= 52, SNARK:<6.1.MONITOR>PHYKNI.MAC.8,  19-Feb-83 14:04:41 by LEAPLINE
;Remove NISFLG and return to SCHED if queue empty instead of BUG.
; UPD ID= 51, SNARK:<6.1.MONITOR>PHYKNI.MAC.6,  18-Feb-83 18:48:51 by LEAPLINE
;If no KLNI, set level 2 clock checks very high.
; UPD ID= 50, SNARK:<6.1.MONITOR>PHYKNI.MAC.5,  18-Feb-83 18:31:55 by LEAPLINE
;Fix bug in KNICHK.
; UPD ID= 49, SNARK:<6.1.MONITOR>PHYKNI.MAC.4,  18-Feb-83 11:31:28 by LEAPLINE
;Add routine KNICHK for periodic checks.
;Fix REMQUE to do standard +1 and +2 returns. Also, make IOEXE require the
; instruction to execute in T1 instead of P.
; UPD ID= 21, SNARK:<6.1.MONITOR>PHYKNI.MAC.3,  11-Feb-83 07:15:43 by LEAPLINE
;Take out save of AC's in PUTQUE, REMQUE. Routine adjusts P for returns.
; UPD ID= 30, SNARK:<6.DECNET>PHYKNI.MAC.9,  30-Jan-83 12:49:14 by LEAPLINE
;Do total clean up of all queues on RESET.
;Change interrupt processing to process selected entries from the RESPONSE Q
; UPD ID= 18, SNARK:<6.DECNET>PHYKNI.MAC.7,  17-Jan-83 17:39:10 by LEAPLINE
;Change interrupt processing to only check for errors and set a scheduler
;flag to finish the processing of the response queue.
;Changed definition of bit 33 in station register. WSENA --> WSPMC
; UPD ID= 10, SNARK:<6.DECNET>PHYKNI.MAC.6,  15-Dec-82 20:20:33 by LEAPLINE
;Use DNGWDZ and DNFWDS instead of ASGRES and RELRES.
; UPD ID= 9, SNARK:<6.DECNET>PHYKNI.MAC.5,  13-Dec-82 09:32:57 by LEAPLINE
;Correct interrupt handling code.
; UPD ID= 8, SNARK:<6.DECNET>PHYKNI.MAC.4,  10-Dec-82 13:03:22 by LEAPLINE
;Fix bug in IOEXE routine.
; UPD ID= 7, SNARK:<6.DECNET>PHYKNI.MAC.3,  10-Dec-82 10:30:09 by LEAPLINE
;Make CMDDSP 0 a temp dispatch for datagram received. Also, put in counter
;for LONG WORD COUNT ERRORS. Microcode can cause these.
; UPD ID= 6, SNARK:<6.DECNET>PHYKNI.MAC.2,   9-Dec-82 10:53:12 by LEAPLINE
;TCO 6.1.XXXX	More KLNI support
; UPD ID= 4, SNARK:<6.DECNET>PHYKNI.MAC.1,   7-Dec-82 15:32:24 by LEAPLINE

;	COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1984, 1988.
;	ALL RIGHTS RESERVED.
;
;	THIS SOFTWARE IS FURNISHED UNDER A  LICENSE AND MAY BE USED AND  COPIED
;	ONLY IN  ACCORDANCE  WITH  THE  TERMS OF  SUCH  LICENSE  AND  WITH  THE
;	INCLUSION OF THE ABOVE  COPYRIGHT NOTICE.  THIS  SOFTWARE OR ANY  OTHER
;	COPIES THEREOF MAY NOT BE PROVIDED  OR OTHERWISE MADE AVAILABLE TO  ANY
;	OTHER PERSON.  NO  TITLE TO  AND OWNERSHIP  OF THE  SOFTWARE IS  HEREBY
;	TRANSFERRED.
;
;	THE INFORMATION IN THIS  SOFTWARE IS SUBJECT  TO CHANGE WITHOUT  NOTICE
;	AND SHOULD  NOT  BE CONSTRUED  AS  A COMMITMENT  BY  DIGITAL  EQUIPMENT
;	CORPORATION.
;
;	DIGITAL ASSUMES NO  RESPONSIBILITY FOR  THE USE OR  RELIABILITY OF  ITS
;	SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
	SUBTTL S. LEAPLINE 27-SEP-82
	SUBTTL PHYKNI - DEVICE DEPENDENT CODE FOR KLNI PORT

	SEARCH	PROLOG		;SYSTEM PARAMETERS
	SEARCH	PHYPAR,SERCOD

;Special AC's

	DEFAC PS,Q3		; Points to PS (port storage) block
	DEFAC CM,P4		; Points to current command block
	Subttl	Table of Contents

;		     Table of Contents for PHYKNI
;
;				  Section		      Page
;
;
;    1. Known Bugs And Deficiencies  . . . . . . . . . . . . .   5
;    2. Special macros . . . . . . . . . . . . . . . . . . . .   6
;    3. CONx Bit Definitions . . . . . . . . . . . . . . . . .   7
;    4. Error Definitions  . . . . . . . . . . . . . . . . . .   8
;    5. Structure Definitions For The PCB  . . . . . . . . . .   9
;    6. PTT And MTT Definitions  . . . . . . . . . . . . . . .  11
;    7. Command Definitions  . . . . . . . . . . . . . . . . .  12
;    8. Send Datagram Definitions  . . . . . . . . . . . . . .  13
;    9. Port Storage Definitions . . . . . . . . . . . . . . .  14
;   10. Queue Structure Definitions  . . . . . . . . . . . . .  15
;   11. NI Station Info Format . . . . . . . . . . . . . . . .  16
;   12. Receive Datagram Definitions . . . . . . . . . . . . .  17
;   13. Misc Definitions . . . . . . . . . . . . . . . . . . .  18
;   14. Local Storage  . . . . . . . . . . . . . . . . . . . .  19
;   15. Command Queue Dispatch Table . . . . . . . . . . . . .  20
;   16. NIDSP - Primary interface between NISRV and PHYKNI . .  21
;   17. NIINI - Initialize The KLNI Port . . . . . . . . . . .  22
;   18. KLIINI - Initialize a KLNI the first time  . . . . . .  23
;   19. KNIRSC - Reset all channels  . . . . . . . . . . . . .  25
;   20. SETSTA - Set channel state . . . . . . . . . . . . . .  26
;   21. KNISV - Unvectored interrupt service for KLNI's  . . .  27
;   22. INTPRO - Determine what caused the interrupt, and hand  28
;   23. RSPERR - Handle errors generated by a response . . . .  29
;   24. Transmit errors  . . . . . . . . . . . . . . . . . . .  30
;   25. Here to handle receive errors  . . . . . . . . . . . .  31
;   26. KNIERR - Handle KLNI hardware errors . . . . . . . . .  32
;   27. KNIPPE - Planned CRAM Parity Errors  . . . . . . . . .  33
;   28. Individual Planned CRAM Parity Errors  . . . . . . . .  34
;   29. KNICHK - Periodic Checks . . . . . . . . . . . . . . .  40
;   30. Good Stuff - KLNI dump and reload logic description  .  41
;   31. KNIJB0 - Run KNILDR if necessary . . . . . . . . . . .  42
;   32. Read/Write CRAM  . . . . . . . . . . . . . . . . . . .  43
;   33. NISND - Send Datagram  . . . . . . . . . . . . . . . .  44
;   34. Byte pointer evaluation tables . . . . . . . . . . . .  45
;   35. CNTFRG - Count BSDs  . . . . . . . . . . . . . . . . .  50
;   36. BLDBSD - Build BSDs  . . . . . . . . . . . . . . . . .  51
;   37. FIXBSD - Correct buffer for physical contituity  . . .  54
;   38. SETSND - Setup Send Datagram Entry . . . . . . . . . .  55
;   39. NISRB - Specify Receive Buffer . . . . . . . . . . . .  58
;   40. Read/Clear Performance Counters  . . . . . . . . . . .  60
;   41. Read/Read Clear Counters Response  . . . . . . . . . .  61
;   42. Complete Reading Of Channel Counters . . . . . . . . .  62
;   43. Complete Reading Of Portal Counters  . . . . . . . . .  63
;   44. CMPSLZ - Compute Seconds Since Last Zeroed . . . . . .  64
;   45. NISCS - Set Channel State  . . . . . . . . . . . . . .  65
;   46. NIRPI - Read Portal Information  . . . . . . . . . . .  66
;   47. QUECMD - Queue A Command . . . . . . . . . . . . . . .  67
	Subttl	Table of Contents (page 2)

;		     Table of Contents for PHYKNI
;
;				  Section		      Page
;
;
;   48. FNDPT - Find The PTT Entry For A Protocol Type . . . .  68
;   49. MSGAVA - Response Message Available  . . . . . . . . .  69
;   50. DGSNT - Response to Datagram Sent  . . . . . . . . . .  70
;   51. NIRSI - Read KLNI Station Information  . . . . . . . .  71
;   52. NIRCI - Read Channel Information . . . . . . . . . . .  72
;   53. NICLO - Flush All Commands From A Portal . . . . . . .  73
;   54. CLOSED - Command Flush Completion  . . . . . . . . . .  74
;   55. NISCA - Set Channel Address  . . . . . . . . . . . . .  75
;   56. PMOVE - Physical Mover . . . . . . . . . . . . . . . .  76
;   57. PUTQUE - Put An Entry On The Queue . . . . . . . . . .  77
;   58. REMQUE - Remove An Entry From A Queue  . . . . . . . .  78
;   59. GETCOR/RELCOR - Core Manager . . . . . . . . . . . . .  79
;   60. GXRCOR - Get Core For Transmits And Receives . . . . .  80
;   61. CHKBUF - Check Buffer Physical Range . . . . . . . . .  81
;   62. NISTP - Stop The KLNI (Freeze) . . . . . . . . . . . .  82
;   63. INICOM - Setup Initial KLNI Commands . . . . . . . . .  83
;   64. ENBKLP - Enable KLIPA NI Port  . . . . . . . . . . . .  84
;   65. PCBINI - Initialize The Port Control Block . . . . . .  85
;   66. ASGPRO - Assign Protocol Type  . . . . . . . . . . . .  88
;   67. NIDPT - Disable A Protocol Type  . . . . . . . . . . .  89
;   68. NISRA - Specify Receive Address  . . . . . . . . . . .  90
;   69. NIDRA - Disable A Multicast Address  . . . . . . . . .  91
;   70. FNDMT - Find Enabled MTT Entry For A Given Multicast A  92
;   71. LDMCT - Build Load Tables Commands . . . . . . . . . .  93
;   72. LDPTT - Load Protocol Type Table . . . . . . . . . . .  94
;   73. NIWSI - Write Station Information  . . . . . . . . . .  95
;   74. LOGERR - Generate ERROR.SYS Entries  . . . . . . . . .  96
;   75. Common BUGs  . . . . . . . . . . . . . . . . . . . . .  97
;   76. Miscellaneous Routines . . . . . . . . . . . . . . . .  98
;   77. History Buffer Routines  . . . . . . . . . . . . . . .  99
;   78. INIUNB - Initialize The History Buffer . . . . . . . . 100
;   79. FNDCHN - Find Channel Block Associated With Channel #  101
;   80. End of PHYKLP  . . . . . . . . . . . . . . . . . . . . 102
	SUBTTL Known Bugs And Deficiencies

COMMENT/

Stu	9 may 84 - Fixed 5-Dec-84
	Error recovery needs to be added. Currently, a BUGxxx is given
	for the errors detected.

Stu	9 May 84 - Fixed 6-Jan-85
	A mechanism for returning FREE QUEUE ERROR to the user must be
	invented.

Stu	9 May 84 - Fixed
	CM blocks don't get returned when a command gets an error.

Stu	9 May 84 - Fixed 4-Jan-85
	Need new memory mangler to prevent KNIBNCs.

Stu	9 May 84 - Fixed
	MSD style sends don't handle non-physically contiguous buffer
	segments.

Stu	9 May 84 - Fixed
	UNSTA not updated as per spec.

Stu	9 May 84 - Fixed 24 May 84
	Open of information portals fails for no good reason.

Stu	24 May 84 - Fixed
	Initialization failures not handled correctly.  ENBKLP and
	friends need to be made smarter.

Stu	23 July 84
	MAPs need to check results.

Stu	27 August 84 - Fixed
	NIWSI, LDPTT, LDMCT should be done first after a port restart.

Stu	15 October 84
	NOINT's need to be placed around all instances of GETCOR.

Stu	11 November 84 - Fixed 4-Jan-85
	DLLCLO should disable any multicast addresses associated with the
	portal.

Stu	11 December 84
	Things that acquire interlocks should leave some info laying around.

Stu	11 December 84
	When restarting the port, reset all the interlock words.
/
	SUBTTL Special macros

	DEFINE $SAVE(syms)<
	IRP syms,<.$SAV1(syms,\...STK)>
	.$SAV1($RESTORE,\...STK)
	DEFINE $RESTORE<.$RES1($RESTORE,\...STK)
				.$RES2(syms)>
	>
	SUBTTL CONx Bit Definitions

RH0==540				;DEVICE CODE FOR RH0

;Channel Command Word definitions (taken directly from PHYH2).  These are
;needed here because KLNI initialization involves setting up a CCW to the
;PCB, and then starting the KLIPA which then transfers some information at
;the specified address that indicates the PCB address, PI level of the
;device, and some other stuff.

CHXFR==1B0			;INDICATE XFER WORD
CHJMP==2B2			;INDICATE CHANNEL JUMP
CHLST==1B1			;INDICATE LAST XFER
CHREV==1B2			;INDICATE REVERSE
CHCNT==MASKB(3,13)		;MASK FOR WORD COUNT IN CCW
CHADR==MASKB(14,35)		;MASK FOR PHYSICAL ADDRESS IN CCW

;CONI BITS - LEFT (defined in the IPA20-L spec)

CI.PPT==1B0			;PORT PRESENT
CI.DCC==1B2			;DIAG CSR CHANGE
CI.CPE==1B6			;CRAM PARITY ERROR
CI.MBE==1B7			;MBUS ERROR
CI.IDL==1B11			;IDLE
CI.DCP==1B12			;DISABLE COMPLETE
CI.ECP==1B13			;ENABLE COMPLETE

;CONI/CONO BITS - RIGHT

CO.CPT==1B18			;CLEAR PORT
CO.LAR==1B21			;SELECT LAR
CI.EPE==1B24			;EBUS PARITY ERROR
CI.FQE==1B25			;FREE QUEUE ERROR
CI.DME==1B26			;DATA MOVER ERROR
CO.CQA==1B27			;COMMAND QUEUE AVAILABLE
CI.RQA==1B28			;RESPONSE QUEUE AVAILABLE
CO.DIS==1B30			;DISABLE
CO.ENA==1B31			;ENABLE
CO.MRN==1B32			;MICRO-PROCESSOR RUN
CO.BTS==CO.ENA!CO.MRN!NIPIA	;BITS WHICH NEED TO BE ON IN ALL CONOS
CI.ERR==CI.CPE!CI.MBE!CI.EPE!CI.DME ;HARDWARE ERROR BITS
CI.INT==CI.FQE!CI.RQA		;NORMAL INTERRUPT BITS

DO.LRA==1B0			;BIT ON IN DATAO TO LOAD RAM ADDRESS REGISTER

DEFSTR MICPC,,12,12		;UPROC PC FIELD WHEN READ VIA A DATAI

DEFSTR UCMAJ,,23,4		;[7.1221] Major version #
DEFSTR UCMIN,,29,4		; Minor version #
DEFSTR UCEDT,,29,10		; Edit #

MICVER==136			; Microcode major and minor version #s
MICEDT==137			; Microcode edit #

MINCPE==7750			;LOWEST PLANNED CRAM PARITY ERROR
MAXCPE==7777			;HIGHEST PLANNED CRAM PARITRY ERROR
	SUBTTL Error Definitions

; These codes are returned somewhere in the CMERR field of each completed
; command.  These codes are only valid if the low order bit is 1.

	NI.EXC==0		; Excessive collisions
	NI.CCF==1		; Carrier check failed
	NI.CDF==2		; Collision detect check failed
	NI.SCI==3		; Short circuit
	NI.OCI==4		; Open circuit
	NI.FTL==5		; Frame too long
	NI.RFD==6		; Remote failure to defer
	NI.BCE==7		; Block check error (CRC error)
	NI.FER==10		; Framing error
	NI.DOV==11		; Data overrun
	NI.UPT==12		; Unrecognized protocol type?!?
	NI.FTS==13		; Frame too short

	NI.SCE==27		; Spurious channel error
	NI.CER==30		; Channel error (WC <> 0)
	NI.QLV==31		; Queue length violation
	NI.IPL==32		; Illegal PLI function
	NI.URC==33		; Unrecognized command
	NI.BLV==34		; Buffer length violation
	NI.RSV==35		; Reserved
	NI.TBP==36		; Xmit buffer parity error
	NI.INT==37		; Internal error
	SUBTTL Structure Definitions For The PCB
COMMENT / THE FOLLOWING IS THE PORT CONTROL BLOCK FORMAT

	+-------------------------------------------------------+
PBCQI	!                Command Queue Interlock                !
	+-------------------------------------------------------+
PBCQF	!                  Command Queue FLINK                  !
	+-------------------------------------------------------+
PBCQB	!                  Command Queue BLINK                  !
	+-------------------------------------------------------+
PBRS0	!                       RESERVED                        !
	+-------------------------------------------------------+
PBRQI	!               Response Queue Interlock                !
	+-------------------------------------------------------+
PBRQF	!                 Response Queue FLINK                  !
	+-------------------------------------------------------+
PBRQB	!                 Response Queue BLINK                  !
	+-------------------------------------------------------+
PBRS1	!                       RESERVED                        !
	+-------------------------------------------------------+
PBUQI	!         Unknown Protocol Type Queue Interlock         !
	+-------------------------------------------------------+
PBUQF	!              Unknown Protocol Type FLINK              !
	+-------------------------------------------------------+
PBUQB	!              Unknown Protocol Type BLINK              !
	+-------------------------------------------------------+
PBUQL	!          Unknown Protocol Queue Entry Length          !
	+-------------------------------------------------------+
PBRS2	!                       RESERVED                        !
	+-------------------------------------------------------+
PBPTT	!         Protocol Type Table starting address          !
	+-------------------------------------------------------+
PBMTT	!       Multicast Address Table starting address        !
	+-------------------------------------------------------+
PBRS3	!                       RESERVED                        !
	+-------------------------------------------------------+
PBER0	!                    Error Logout 0                     !
	+-------------------------------------------------------+
PBER1	!                    Error Logout 1                     !
	+-------------------------------------------------------+
PBLAD	!           Address of Channel Logout Word 1            !
	+-------------------------------------------------------+
PBCLO	!          Contents of Channel Logout Word 1            !
	+-------------------------------------------------------+
PBPBA	!            Port Control Block base address            !
	+-------------------------------------------------------+
PBPIA	!                  PI Level Assignment                  !
	+-------------------------------------------------------+
PBIVA	!              Interrupt Vector Assignment              !
	+-------------------------------------------------------+
PBCCW	!                 Channel Command Word                  !
	+-------------------------------------------------------+
PBRS4	!		   RESERVED FOR PORT			!
	+-------------------------------------------------------+
/
BEGSTR PB
	WORD CQI		;COMMAND QUEUE INTERLOCK
	WORD CQF		;COMMAND QUEUE FLINK
	WORD CQB		;COMMAND QUEUE BLINK
	WORD RS0		;RESERVED FOR SOFTWARE
	WORD RQI		;RESPONSE QUEUE INTERLOCK
	WORD RQF		;RESPONSE QUEUE FLINK
	WORD RQB		;RESPONSE QUEUE BLINK
	WORD RS1		;RESERVED
	WORD UQI		;UNKNOWN PROTOCOL TYPE QUEUE INTERLOCK
	WORD UQF		;UNKNOWN PROTOCOL TYPE QUEUE FLINK
	WORD UQB		;UNKNOWN PROTOCOL TYPE QUEUE BLINK
	WORD UQL		;UNKNOWN PROTOCOL TYPE QUEUE LENGTH
	WORD RS2		;RESERVED
	WORD PTT		;PROTOCOL TYPE TABLE STARTING ADDRESS
	WORD MTT		;MULTICAST ADDRESS TABLE STARTING ADDRESS
	WORD RS3		;RESERVED
	WORD ER0		;KLNI ERROR LOGOUT 0
	WORD ER1		;KLNI ERROR LOGOUT 1
	WORD LAD		;ADDRESS OF CHANNEL LOGOUT WORD 1
	WORD CLO		;CONTENTS OF CHANNEL LOGOUT WORD 1
	WORD PBA		;PORT CONTROL BLOCK BASE ADDRESS
	WORD PIA		;PI LEVEL ASSIGNMENT
	WORD IVA		;INTERRUPT VECTOR ASSIGNMENT
	WORD CCW		;CHANNEL COMMAND WORD
	WORD RCB		;POINTER TO READ COUNTERS BUFFER
ENDSTR
	SUBTTL PTT And MTT Definitions

;PROTOCOL TYPE TABLE. NOTHING MAY BE ADDED WITHOUT AFFECTING THE UCODE.

BEGSTR PT
	FIELD ENA,1		; Protocol is enabled
	FILLER 15		; MBZ
	FIELD TYP,16		; Protocol type
	FIELD FRE,1,35		; 1 means entry is free
	WORD FRQ		; Free queue header address
	WORD VIR		; Virtual address of free queue header
ENDSTR
		
;MULTI-CAST ADDRESS TABLE. NOTHING MAY BE ADDED WITHOUT AFFECTING THE UCODE.

BEGSTR MT
	WORD HAD		; High order address
	FIELD LAD,36,35		; Low order address
	MSKSTR MTUSE,NMTT*MT.LST,-1 ; Usage count for this multicast
	MSKSTR MTENA,MT.LAD,1	; Enable bit
ENDSTR
	
	SUBTTL Command Definitions

;ALL COMMANDS HAVE THE FOLLOWING FORMAT. NOTHING CAN BE ADDED TO THE CM DEFINE.

BEGSTR CM
	WORD FLI		;FORWARD LINK
	MSKSTR CMERC,CM.FLI,CMFLI ;NISRV ERROR CODE DURING COMMAND PROCESSING
	WORD BLI		;BACKWARD LINK
	WORD VAD		;VIRTUAL ADDRESS OF ENTRY

;STATUS FIELD

	FIELD SRI,1,1		;SEND/RECEIVE INDICATOR
	FIELD ERR,6		;Error code including error bit at bottom

;FLAGS FIELD

	FIELD FLG,8		;FLAGS FIELD
	  BIT PAC		;PACKING MODE FOR NON-BSD
	  BIT CRC		;CRC INCLUDED
	  BIT PAD		;UNUSED
	  BIT B03		;UNUSED
	  BIT BSD		;BUFFER SEGMENT DESCRIPTOR FORMAT
	  BIT B05		;UNUSED
	  BIT CLR		;CLEAR COUNTERS
	  BIT RSP		;RESPONSE NEEDED

;OPCODE

	FIELD OPC,8		;OPCODE

;OPERATION CODE DEFINITIONS
	  OP.FLS==0		;FLUSH COMMANDS -- MUST BE ILLEGAL OPCODE
	  OP.SND==1		;SEND DATAGRAM
	  OP.LDM==2		;LOAD MUTICAST ADDRESS TABLE
	  OP.LDP==3		;LOAD PROTOCOL TYPE TABLE
	  OP.RCC==4		;READ AND CLEAR COUNTERS
	  OP.RCV==5		;DATAGRAM RECEIVED
	  OP.WPL==6		;WRITE PLI
	  OP.RPL==7		;READ PLI
	  OP.RSI==8		;READ STATION INFORMATION
	  OP.WSI==9		;WRITE STATION INFORMATION

;TDR

	FIELD TDR,^D10,^D35	;Time domain reflectometry value
ENDSTR
	SUBTTL Send Datagram Definitions

;NON-BSD DEFINITIONS. NOTHING MAY BE ADDED WITHOUT AFFECTING THE UCODE.

BEGSTR SN,CM.LST
	FIELD TXL,16,35		;TEXT LENGTH (BYTES)
	FIELD PTY,16,31		;PROTOCOL TYPE
	WORD FRQ		;FREE QUEUE HEADER ADDRESS
	WORD HAD		;HIGH ORDER ADDRESS
	WORD LAD		;LOW ORDER
ENDSTR

;BSD STYLE DATAGRAMS

BEGSTR SB,SN.LST
	WORD BBA		; Physical BSD base address
				;  Anything may be added from here on ****
	WORD PID		; Portal ID
	WORD MSD		; MSD pointer
	WORD RID		; Request ID
	WORD BFA,2		; Buffer address
	WORD RES,2		; Pad out to 4 word boundary
ENDSTR

;BSD DEFINITIONS

BEGSTR BD
	FIELD PAC,2,7		;PACKING MODE
	FIELD SBA,24,35		;PHYSICAL SEGMENT BASE ADDRESS
	WORD NXA		;PHYSICAL NEXT BSD ADDRESS
	WORD SLN		;SEGMENT LENGTH
	WORD RES		;RESERVED FOR SOFTWARE
				;**** ADDITION DEFINITIONS MAY BE ADDED HERE
ENDSTR
	SUBTTL Port Storage Definitions

;SOFTWARE DEFINED ONLY.

BEGSTR PS
	WORD NXT		; Pointer to next channel block
	WORD PCB		;PORT CONTROL BLOCK BASE ADDRESS (VIRTUAL)
	WORD PBA		;PORT CONTROL BLOCK PHYSICAL BASE ADDRESS
	WORD PTT		;VIRTUAL ADDRESS OF PROTOCOL TYPE TABLE
	WORD MTT		;VIRTUAL ADDRESS OF MULTICAST ADDRESS TABLE
	WORD INT		;INTERRUPT LEVEL CONTROL BUFFER
	WORD NON		;NON-INTERRUPT LEVEL CONTROL BUFFER
	WORD LPT		;LOAD PTT TABLE BUFFER ADDRESS
	WORD LMT		;LOAD MULTICAST ADDRESS TABLE BUFFER ADDRESS
	WORD WSI		;WRITE STATION INFO BUFFER ADDRESS
	WORD RSI		;READ STATION INFO BUFFER ADDRESS
	WORD UNK,PT.LEN		;PSEUDO PTT FOR UNKNOWN PROTOCOL TYPE QUEUE
	FIELD FLG,18		;FLAGS WORD
	 BIT SLS		;1=LINE STATE NEEDS REPORTED
	 BIT WUL		;1=WAITING FOR UCODE TO BE LOADED
	 BIT STP		;1=WAITING FOR PORT RESTART
	 BIT BIG		;1=KNISTP BUGINF REPORTED
	 BIT LSI		;1=NEED TO WRITE STATION INFORMATION
	 BIT LMC		;1=NEED TO DO LOAD MULTICAST TABLE COMMAND
	 BIT LPP		;1=NEED TO DO LOAD PROTOCOL TABLE COMMAND
	 BIT VAD		; 1=PSHAD/LAD is valid
	WORD STA		;LINE STATE
	 DEFSTR (PSRUN,$PSSTA,0,1) ;  Channel is running, should be 1b0
	 DEFSTR (PSSST,$PSSTA,26,9) ;  Channel substate
	 DEFSTR (PSEXS,$PSSTA,35,9) ;  Channel external state
	WORD HAD		;STORED HIGH ORDER STATION ADDRESS
	WORD LAD		;STORED LOW ORDER STATION ADDRESS
	WORD SAD,2		;Shadowed address
	WORD HRA		;STORED HIGH ORDER ROM ADDRESS
	WORD LRA		;STORED LOW ORDER ROM ADDRESS
	FIELD VAR,4		;STORED VARIBLES
	 ;--IMPORTANT--  THE FOLLOWING 4 BITS MUST BE IN SAME ORDER AS WSVAR
	  BIT CRC		;ALLOW RECEIPT OF FRAMES WITH CRC ERRORS
	  BIT PMC		;STATION IS IN PROMISCIOUS MULTICAST MODE
	  BIT H40		;H4000 MODE IF 1
	  BIT PRM		;PROMISCIOUS MODE IF 1
	FIELD SVA,4		; Shadowed variables (what the port contains)
	FIELD VBT,4		; Valid bits in PSVAR (same order as PSVAR)
	  BIT VCR		;   PSCRC is valid
	  BIT VPM		;   PSPMC is valid
	  BIT VH4		;   PSH40 is valid
	  BIT VPR		;   PSPRM is valid
	FIELD RSP,8		;MAXIMUM NUMBER OF ENTRIES ON THE RESPONSE QUE
	FIELD CHN,4		; Logical channel number
	FIELD CBA,3		; CBUS address
	WORD CHK		; Check word, contains magic value
	WORD TLR		; Time of last response
	WORD CNO		; CONO KNI,(T1)
	WORD CNI		; CONI KNI,T1
	WORD DTO		; DATAO KNI,T1
	WORD DTI		; DATAI KNI,T1
	WORD CQA		; CONO KNI,CO.BTS+CO.CQA or NOP
	WORD MXT		; # of Multicasts transmitted
	HWORD UMA		; Major version #
	HWORD UMI		; Minor version #
	WORD UED		; Edit #
	WORD TPC		;UDT OF PORT CRASH
	WORD LAR		;LAR AT TIME OF UCODE CRASH
	WORD CRL		;LEFT HAND CRAM BITS AT TIME OF CRASH
	WORD CRR		;RIGHT HAND CRAM BITS AT TIME OF CRASH
	WORD TLZ		;TIME AT WHICH PORT COUNTERS WERE ZEROED
	WORD SHC		;ADDRESS OF SHADOW COUNTERS BLOCK
ENDSTR
	SUBTTL Queue Structure Definitions

;SOFTWARE AND UCODE DEFINED.

BEGSTR QH			;QUEUE HEADER DEFINITION
	WORD IWD		;INTERLOCK WORD
	WORD FLI		;FORWARD LINK
	WORD BLI		;BACKWARD LINK
	WORD LEN		;LENGTH OF QUEUE ENTRIES
ENDSTR

BEGSTR QE			;QUEUE ENTRY
	WORD FLI		;FORWARD LINK
	WORD BLI		;BACKWARD LINK
	WORD VIR		;VIRTUAL ADDRESS OF ENTRY
	WORD OPC		;QUEUE ENTRY OPERATION CODE
ENDSTR
	SUBTTL NI Station Info Format

;WRITE STATION INFO FORMAT

BEGSTR WS,CM.LST
	WORD HAD		;HIGH ORDER ETHERNET ADDRESS
	WORD LAD		;LOW ORDER ETHERNET ADDRESS
	FILLER 32		;NOT USED
	FIELD VAR,4		;VARIBLES
	  BIT CRC		;ALLOW RECEIPT OF FRAMES WITH CRC ERRORS
	  BIT PMC		;PROMISCIOUS MULTICAST MODE
	  BIT H40		;H4000 MODE
	  BIT PRM		;PROMISCIOUS MODE
	NXTWRD
	FILLER 24		;NOT USED
	FIELD RTY,12		;ERROR RETRY VALUE
				;**** ADDITIONS MAY BE ADDED HERE
	HWORD FNC		;FUNCTION WHICH INVOKED THIS COMMAND
ENDSTR

;READ STATION INFORMATION FORMAT. UCODE DEFINED ONLY.

BEGSTR RS,CM.LST
	WORD HAD		;HIGH ORDER ETHERNET ADDRESS
	WORD LAD		;LOW ORDER ETHERNET ADDRESS
	FILLER 32		;NOT USED
	FIELD VAR,4		;VARIBLES
	  BIT NOP		;RECEIVE MOP MODE (UNUSED)
	  BIT PMC		;PROMISCIOUS MULTICAST MODE
	  BIT H40		;H4000 MODE
	  BIT PRM		;PROMISCIOUS MODE
	NXTWRD
	FILLER 16		;UNUSED
	FIELD UCV,8		;UCODE VERSION
	FIELD NMC,6		;NUMBER OF MULTICAST ADDRESSES ALLOWED
	FIELD NPT,6		;NUMBER OF PROTOCOL TYPES ALLOWED
ENDSTR
	SUBTTL Receive Datagram Definitions

BEGSTR RD,CM.LST
	FIELD SIZ,16,35		; Text length + CRC (bytes)
	WORD DA1,2		; Let program align the bytes
	WORD SA1,2
;	FIELD DA1,32		; High order destination address
;	FIELD DA2,16,31		; Low order destination address
;	FIELD SA1,32		; High order source address
;	FIELD SA2,16,31		; Low order source address
	FIELD PTY,16,31		; Protocol type
	FIELD PBA,22,35		; Physical address of receive buffer
				; **** Additions may be added here
	WORD VBA,2		; Virtual address of receive buffer
	WORD PID		; Portal ID
	WORD RID		; Request ID
ENDSTR

;READ/READ CLEAR COUNTERS

BEGSTR C1,CM.LST
	WORD RID		; Request ID
	WORD PID		; Process ID
	WORD BFA		; Buffer address
	WORD SPI		; Secondary portal ID
	FIELD ZRO,1		; Indicates counters should be zeroed
	HWORD FNC		; Function code
ENDSTR

; Structure for read counters block

BEGSTR RC
	WORD BR			;BYTES RECEIVED
	WORD BX			;BYTES TRANSMITTED
	WORD FR			;FRAMES RECEIVED
	WORD FX			;FRAMES TRANSMITTED
	WORD MCB		;MULTICAST BYTES RECEIVED
	WORD MCF		;MULTICAST FRAMES RECEIVED
	WORD FXD		;FRAMES XMITTED, INITIALLY DEFERRED
	WORD FXS		;FRAMES XMITTED, SINGLE COLLISION
	WORD FXM		;FRAMES XMITTED, MULTIPLE COLLISIONS
	WORD XF			;TRANSMIT FAILURES
	WORD XFM		;TRANSMIT FAILURE BIT MASK
	  RCLOC==1B24		;  LOSS OF CARRIER
	  RCXBP==1B25		;  XMIT BUFFER PARITY ERROR
	  RCRFD==1B26		;  REMOTE FAILURE TO DEFER
	  RCXFL==1B27		;  XMITTED FRAME TOO LONG
	  RCOC==1B28		;  OPEN CIRCUIT
	  RCSC==1B29		;  SHORT CIRCUIT
	  RCCCF==1B30		;  COLLISION DETECT CHECK FAILED
	  RCEXC==1B31		;  EXCESSIVE COLLISIONS
	WORD CDF		;CARRIER DETECT CHECK FAILED
	WORD RF			;RECEIVE FAILURES
	WORD RFM		;RECEIVE FAILURE BIT MASK
	  RCFLE==1B27		;  FREE LIST PARITY ERROR
	  RCNFB==1B28		;  NO FREE BUFFERS
	  RCFTL==1B29		;  FRAME TOO LONG
	  RCFER==1B30		;  FRAMING ERROR
	  RCBCE==1B31		;  BLOCK CHECK ERROR
	WORD DUN		;DISCARDED UNKNOWN
	WORD D01		;DISCARDED POSITION 1
	WORD D02		;DISCARDED POSITION 2
	WORD D03		;DISCARDED POSITION 3
	WORD D04		;DISCARDED POSITION 4
	WORD D05		;DISCARDED POSITION 5
	WORD D06		;DISCARDED POSITION 6
	WORD D07		;DISCARDED POSITION 7
	WORD D08		;DISCARDED POSITION 8
	WORD D09		;DISCARDED POSITION 9
	WORD D10		;DISCARDED POSITION 10
	WORD D11		;DISCARDED POSITION 11
	WORD D12		;DISCARDED POSITION 12
	WORD D13		;DISCARDED POSITION 13
	WORD D14		;DISCARDED POSITION 14
	WORD D15		;DISCARDED POSITION 15
	WORD D16		;DISCARDED POSITION 16
	WORD UFD		;UNRECOGNIZED FRAME DEST
	WORD DOV		;DATA OVERRUN
	WORD SBU		;SYSTEM BUFFER UNAVAILABLE
	WORD UBU		;USER BUFFER UNAVAILABLE
	WORD RS0		;PLI REG RD PAR ERROR,,PLI PARITY ERROR
	WORD RS1		;MOVER PARITY ERROR,,CBUS PARITY ERROR
	WORD RS2		;EBUS PARITY ERROR,,EBUS QUE PARITY ERROR
	WORD RS3		;CHANNEL ERROR,,SPUR CHANNEL ERROR
	WORD RS4		;SPUR XMIT ATTN ERROR,,CBUS REQ TIMOUT ERROR
	WORD RS5		;EBUS REQ TIMEOUT ERROR,,CSR GRNT TIMEOUT ERROR
	WORD RS6		;USED BUFF PARITY ERROR,,XMIT BUFF PARITY ERROR
	WORD RS7		;RESERVED FOR UCODE
	WORD RS8		;RESERVED FOR UCODE
ENDSTR

;FLUSH COMMAND QUEUE DEFINITIONS

BEGSTR FL,CM.LST
	WORD CHK		;CHECK WORD
	WORD PID		;PORTAL ID
ENDSTR
	SUBTTL Misc Definitions

	KNI==564		;DEVICE CODE FOR KLNI

	TIMOUT==^D5000		;LOOP FOR INTERLOCK WAIT

	ADRMSK==MASKB (0,13)	;ADDRESS MASK

	NRETRY==20		;NUMBER OF RETRYS

	KNIRH2==5		;NORMAL RH SLOT FOR KLNI

	NPTT==^D16		;NUMBER OF SUPPORTED PROTOCOL TYPES
				; THIS WILL BE SUPPLIED BY THE MICROCODE LATER

	NMTT==^D16		;NUMBER OF SUPPORTED MULTICAST ADDRESSES
				; THIS ALSO WILL BE SUPPLIED BY THE MICROCODE

	NIPIA==DLSCHN		;PRIORITY INTERRUPT ASSIGNMENT FOR KLNI

	KNICLK==^D5000		;SECONDS FOR LOCAL TIMER

	STSCLK==^D59		;TIME FOR STATS

	CHNLGO==KIEPT+KNIRH2*4	;ADDRESS OF FIRST WORD OF KLNI CHANNEL LOGOUT

	TIM==20			;Timer device

	OPDEF RDTIME [DATAI TIM,] ; Instruction for reading the time base
	SUBTTL Local Storage

RSI	(CHNBAS,0)		;BASE OF CHANNEL BLOCK LIST
RS	NIHERE,1		;[7134] NI IN USE
RS	(TIMCNT)		;COUNT FOR BROKEN KLIPA
RS	(PRTSTG,PS.LST)		;PORT STORAGE
RS	(PCBSTG,PB.LST)		;PCB
RS	(CNTRL,UN.LEN*2+QE.LEN*2+WS.LST+RS.LST) ;CONTROL BUFFERS
RS	(PTTADR,NPTT*PT.LST)	;PROTOCOL TYPE TABLE ADDRESS
RS	(MCTADR,NMTT*MT.LST)	;MULTICAST TABLE ADDRESS
RS	(MCTUSE,NMTT*MT.LST)	;MCAT USE TABLE (MUST FOLLOW
				; MCTADR & BE SAME LENGTH)
RS	(FRQADR,QH.LST*NPTT)	;FREE QUEUE HEADERS
RS	(RCBADR,RC.LEN)		;READ COUNTERS BLOCK
RS	(ENTINT,2)		;TIME AT WHICH WE ENTERED INTERRUPT LEVEL
RSI	(TOTINT,<0,0>)		;TOTAL TIME SPENT AT INTERRUPT LEVEL
RSI	(XRCQUE,0)		; Transmit/receive command core pool head
RSI	(CORTIM,0)		; Time at which next CORCHK should occur
RS	(JB0STF)		;Non-zero means run KNILDR under job 0
	SUBTTL Command Queue Dispatch Table

CMDDSP:	IFIW CLOSED		;FLUSH COMMANDS
	IFIW DGSNT		;DATAGRAM SENT
	IFIW LMCRES		;LOAD MULTICAST RESPONSE
	IFIW LPTRES		;LOAD PROTOCOL TYPE RESPONSE
	IFIW RCRES		;READ AND CLEAR COUNTERS RESPONSE
	IFIW MSGAVA		;DGRCV RESPONSE
	IFIW UNKRES		;WRITE PLI RESPONSE
	IFIW UNKRES		;READ PLI RESPONSE
	IFIW NSARD		;READ STATION REGISTER
	IFIW WRTNSA		;WRITE STATION REGISTER

DSPEND==.-CMDDSP			;END OF DISPATCH TABLE
	SUBTTL NIDSP - Primary interface between NISRV and PHYKNI

; Call:	MOVX T1,NF.xyz		; Get function code
;	MOVX UN,UN_block	; User's argument block
;	MOVX PR,PR_block	; Portal data base
;	CALL NIDSP
;	 <+1 with error code in T1>
;	<+2 Success>

NIDSP:	SAVEAC <PS,CM>
	SKIPLE T1		; Less than zero?
	CAXLE T1,NF.MAX		; Or larger than the max?
	 BUG. (HLT,KNIBFC,PHYKNI,SOFT,<PHYKNI - Illegal NI function code>,<<T1,FUNC>>,<

Cause:	NISRV called PHYKNI with a bad function code.  The code is in T1.

Data:	FUNC - Illegal function code
>)
	MOVE T1,DSPTBL(T1)	; Get dispatch table entry
	TXNE T1,NOCHAN		; Channel block required?
	 JRST (T1)		;  Nope, skip the check
	LOAD PS,PRCHN,(PR)	; Get channel block address
	JUMPLE PS,NIDSP1	; Jump if it's .LE. 0
	OPSTR <CAME PS,>,PSCHK,(PS) ; Does the check word look Kosher?
NIDSP1:	 BUG. (HLT,KNIICA,PHYKNI,SOFT,<PHYKNI - Illegal channel block address>,<<PS,PS>,<PR,PR>>,<

Cause:	The channel block address for this portal is invalid.

Data:	PS - Bad channel block address
	PR - Bad portal block address
>)
	JRST (T1)		; Call the routine

	NOCHAN==1B2		; No channel block required

DSPTBL:	TABBEG NF.ILL,NF.MAX,<FOO>
	  TABENT NF.ILL,<IFIW ILLFNC>	;ILLEGAL FUNCTION
	  TABENT NF.XMT,<IFIW NISND>	;TRANSMIT DATAGRAM
	  TABENT NF.RCV,<IFIW NISRB>	;SPECIFY RECEIVE BUFFER
	  TABENT NF.LSC,<IFIW ILLFNC>	;LINE STATE CHANGE
	  TABENT NF.RDC,<IFIW!NOCHAN!NIRDC> ;READ CHANNEL COUNTERS
	  TABENT NF.DPT,<IFIW NIDPT>	;DISABLE PROTOCOL TYPE
	  TABENT NF.EMA,<IFIW NISRA>	;ENABLE MULTICAST ADDRESS
	  TABENT NF.DMA,<IFIW NIDRA>	;DISABLE MULTICAST ADDRESS
	  TABENT NF.ASP,<IFIW!NOCHAN!ASGPRO> ;ASSIGN PROTOCOL
	  TABENT NF.RCI,<IFIW!NOCHAN!NIRCI> ;READ CHANNEL INFO
	  TABENT NF.CLO,<IFIW!NOCHAN!NICLO> ;CLOSE PORTAL (FLUSH COMMANDS)
	  TABENT NF.SCA,<IFIW!NOCHAN!NISCA> ;SET CHANNEL ADDRESS
	  TABENT NF.RPC,<IFIW!NOCHAN!NIRPC> ;READ PORTAL COUNTERS
	  TABENT NF.SCS,<IFIW!NOCHAN!NISCS> ;SET CHANNEL STATE
	  TABENT NF.RPI,<IFIW!NIRPI>	;READ PORTAL INFO
	TABEND
	SUBTTL NIINI - Initialize The KLNI Port

;+
; THIS ROUTINE IS CALLED AT SYSTEM STARTUP AND ONLY AT SYSTEM STARTUP
; TO GET THE KLNI GOING.
;
;	CALL	NIINI	;PORT IS ON RH#5
;	 P1 = CDB ADDRESS
;
; RETURN +1: ALWAYS
;
;-

IFN FTHINI,RESCD

NIINI:

IFN FTHINI,<
	JRST @[NIINI1]		; Make the big leap
	XRESCD			; Change PSECTs
NIINI1:
>

; Check for a KLIPA in slot 5

	CONI KNI,T1		; Read the CSR
	JE CI.PPT,T1,RTN	; Just return if not a KLIPA

; Create a CDB so that diagnostics will work

	SETOM NIHERE		;[7134] WE HAVE AN NI
	SAVEAC <P1,P2,P3>
	MOVX T1,CDBLEN		; Get length of CDB
	CALL PHYALC		; Allocate some storage
	 RET			;  Failure, quit initialization
	MOVEI P1,-CDBINT(T1)	; CDB base address to P1
	MOVX T1,5		; Get actual channel address
	MOVEM P1,CHNTAB(T1)	; Save CDB in channel table
	HRRZM T1,CDBADR(P1)	; Store CHNTAB index
	EXCH P1,P3		; Get CDB in P3
	MOVEI T1,.BTCDB		; Mark as CDB
	DPB T1,USYBKT		;  ...
	EXCH P1,P3		; Restore
	MOVEI T1,.CTNI		; Set type
	DPB T1,CSYTYP		;  as NI
	MOVX T1,CS.NIP		; This is an
	IORM T1,CDBSTS(P1)	;  NI channel

	MOVX T1,KIEPT+0+5*4	; Address of channel ICP area
				;  = ept+ICP offset+(rh slot #*logout len)
	MOVEM T1,CDBICP(P1)	; Stash in CDB

	MOVEI T1,KNIDSP		; Get bogus dispatch table address
	MOVEM T1,CDBDSP(P1)	; Setup dispatch table address

	MOVEI T1,CDBUDB(P1)	; Get start of UDB table
	MOVEM T1,CDBIUN(P1)	; Setup bogus AOBJN word for unit table

	MOVX T1,5		; Get CBUS channel number 5
	CALL KLIINI		; Initialize it
	 NOP			;  Just ignore failure return for now
	RET

	RESCD			; Dispatch table must live in section 0/1

; Bogus dispatch vector for NI channel

KNIDSP::JRST NIINI		;0 - INITIALIZATION
	JRST DSPBUG		;1 - STACK SECOND CHANNEL COMMAND
	JRST DSPBUG		;2 - START I/O
	JRST DSPBUG		;3 - POSITION REQUEST
	JRST DSPBUG		;4 - RETURN BEST XFER
	JRST DSPBUG		;5 - INTERRUPT PROCESSING
	JRST DSPBUG		;6 - MAKE CHANNEL XFER WORD
	JRST DSPBUG		;7 - TRANSFER HUNG
	JRST KNIRSC		;10 - RESET CHANNEL
	RET			;11 - PERIODIC CHECK
	JRST DSPBUG		;12 - CHECK UNIT EXISTANCE
	JRST DSPBUG		;13 - EXTRACT ADDRESS FROM CCW WORD

DSPBUG:	BUG. (HLT,KNIIPF,PHYKNI,SOFT,<PHYKNI - Illegal channel dispatch>,,<

Cause:	The KLNI driver was called to perform a PHYSIO function it is not
	capable of doing.
>)
	RET

	XRESCD			; Put us back in section 6
	SUBTTL KLIINI - Initialize a KLNI the first time

; Initialize a specified KLNI channel.  Create the all the databases and
; start the channel.
;
; Call:	MOVX T1,CBUS channel #
;	CALL KLIINI
;	 <+1 Return for failure>
;	<+2 Success>
;

KLIINI:	SAVEAC <PS,CM,UN,P1,P2,P3>
	SKIPA PS,CHNBAS		; Get base of channel list
KLIIN1:	 LOAD PS,PSNXT,(PS)	; Get next channel block
	JUMPE PS,KLIIN2		; No such channel
	OPSTR <CAMN T1,>,PSCBA,(PS) ; Does the CBUS address match?
	 JRST NIINIA		;  Yes, just init the channel
	JRST KLIIN1		; Loop over all channels

; Here to create a PS block for this channel and fill it in with constants

KLIIN2:	MOVE P1,T1		; Save the CBUS channel #
	XMOVEI PS,PRTSTG	; For now just use pre-allocated blocks

; Now setup all the constants

	STOR PS,PSCHK,(PS)	; Set up the check word
	STOR P1,PSCBA,(PS)	; Save the CBUS address
	MOVE T1,P1		; Get CBUS address
	LSH T1,^D26		; Move it into device code field
	MOVX T2,<CONO RH0,(T1)>	; Get standard CONO for CBUS addr # 0
	ADD T2,T1		; Compute the CONO for this KLNI
	STOR T2,PSCNO,(PS)	; Save it away
	MOVX T2,<CONI RH0,T1>	; Get standard CONI for CBUS addr # 0
	ADD T2,T1		; Compute the CONI for this KLNI
	STOR T2,PSCNI,(PS)	; Save it away
	MOVX T2,<DATAO RH0,T1>	; Get standard DATAO for CBUS addr # 0
	ADD T2,T1		; Compute DATAO for this KLNI
	STOR T2,PSDTO,(PS)	; Save it away
	MOVX T2,<DATAI RH0,T1>	; Get standard DATAI for CBUS addr # 0
	ADD T2,T1		; Compute DATAI for this KLNI
	STOR T2,PSDTI,(PS)	; Save it away

	MOVX T2,<NOP>		; Get a no-op
	STOR T2,PSCQA,(PS)	; Save that as the command initiator
	SETZRO PSFLG,(PS)	; Clear all the flags
	MOVEI T1,UNS.VG		; The channel is now in a virginal state
	CALL SETSTA		; Tell the others about it

	XMOVEI P2,PCBSTG	; PCB address
	STOR P2,PSPCB,(PS)	; Save the PCB address in KLNI storage
	MAP T1,(P2)		; Convert to physical address
	TXZ T1,ADRMSK		; Clear junk
	STOR T1,PBPBA,(P2)	; Save the PCB base physical address
	STOR T1,PSPBA,(PS)	; Save it in the channel block also
	MOVEI T2,NIPIA		; Get PI assignment
	STOR T2,PBPIA,(P2)	; Save the PIA
	MOVE T1,P1		; Get CBUS address
	LSH T1,2		; Multiply it by 4
	ADDI T1,KIEPT+1		; Compute address of channel logout area
	MAP T1,(T1)		; Make it physical
	TXZ T1,ADRMSK		; Clear junk bits
	STOR T1,PBLAD,(P2)	; Tell the port about it

	;...
	;...

	XMOVEI T1,CNTRL		; Control buffers address
	STOR T1,PSINT,(PS)	; Save interrupt level buffer
	ADDI T1,UN.LEN		; Advance to the next buffer
	STOR T1,PSNON,(PS)	; Save it
	ADDI T1,UN.LEN		; And the last, 3 buffers in all
	STOR T1,PSLPT,(PS)	; Save protocol type command buffer address
	ADDI T1,QE.LEN		; Advance to next
	STOR T1,PSLMT,(PS)	; And the multicast address command buffer
	ADDI T1,QE.LEN		; Advance to next
	STOR T1,PSWSI,(PS)	; Save the write station info command buffer
	ADDI T1,WS.LST		; Advance to next
	STOR T1,PSRSI,(PS)	; Save the read station info command buffer

	CALL PCBINI		; Initialize the PCB and queue headers
	MOVX T1,<.RESP1,,RC.LEN> ; Allocate resident memory for
	MOVX T2,.RESGP		;  counters shadow block
	CALL ASGRES		; Get the memory
	 JRST NIINI9		;  Couldn't get it...
	STOR T1,PSSHC,(PS)	; Setup pointer to shadow counters
	MOVE T1,CHNBAS		; Get base of channel list
	STOR T1,PSNXT,(PS)	; Make current block point to 1st on chain
	MOVEM PS,CHNBAS		; Make current block be the first
	CALL STRLD		; Initiate a reload
	RETSKP

NIINIA:	MOVE T1,CHNTAB+5	; Get our CDB address
	MOVX T2,CS.MAI		; Get the maint mode bit
	ANDCAM T2,CDBSTS(T1)	; Clear the maintenance mode bit

	MOVX T1,UNS.IN		; New state is "initializing"
	CALL SETSTA		; Set the state
	CALL ZERSHD		; Zero the shadow counters
	CALL INICOM		; Setup the initial KLNI commands
	 JRST NIINI9		;
	CALL ENBKLP		; Enable the KLNI
	 JRST NIINI9		;  Reset line state and exit
	RETSKP			; And return success

NIINI9:	CALL NISTP		; Stop the port
	MOVEI T1,UNS.BK		; Set state to broken
	CALLRET SETSTA		; Tell users about it
	SUBTTL  KNIRSC - Reset all channels

; Expects nothing
;	CALL KNIRSC
;	<+1 Return only>

IFN FTHINI, RESCD

KNIRSC:

IFN FTHINI,<
	JRST @[KNIRS1]
	XRESCD
KNIRS1:
>
	SKIPN CHNBAS		; Any channels initialized
	 RET			; No
	SAVEAC <PS>
	CALL ALLCHN		; Co-routine to loop through all channels
	CALL NISTP		; Stop this channel
	CALLRET STRLD		;  and request that KNILDR reload it
	SUBTTL SETSTA - Set channel state

; Expects PS to be setup.
;
; Call:	MOVX T1,new-state
;	CALL SETSTA
;	<+1 Return only>

SETSTA:	MOVE T1,STAMAP-UNS.VG(T1) ; Get new state word

	OPSTR <CAMN T1,>,PSSTA,(PS) ; Is the state changing???
	 RET			;  Nope, don't bother anybody
	STOR T1,PSSTA,(PS)	; Yes, save it
	OPSTR <SKIPN UN,>,PSNON,(PS) ; Get control buffer
	 RET			;  Can't report state
	LOAD T1,PSCHN,(PS)	; Get the channel number
	STOR T1,UNCHN,(UN)	; Stuff it in the UN block
	MOVEI T1,NF.LSC		; Function to dll (LINE STATE CHANGE)
	CALLRET DLLCBK		; Send it

;Table for mapping between substate and state word

	DEFINE XX (state)<
	IFE state-UNS.RN,<UNRUN+FLD(state,UNEXS)+FLD(.-STAMAP+UNS.VG,UNSST)>
	IFN state-UNS.RN,<FLD(state,UNEXS)+FLD(.-STAMAP+UNS.VG,UNSST)>>

STAMAP:	TABBEG UNS.VG,UNS.MX,FOO
	  TABENT UNS.VG,XX UNS.OF	; Virgin
	  TABENT UNS.RE,XX UNS.RN	; Reload
	  TABENT UNS.CR,XX UNS.OF	; Can't Reload
	  TABENT UNS.IN,XX UNS.RN	; Init
	  TABENT UNS.RN,XX UNS.RN	; Run
	  TABENT UNS.DP,XX UNS.RN	; Dump
	  TABENT UNS.DR,XX UNS.RN	; Dump & Reload
	  TABENT UNS.BK,XX UNS.OF	; Broken
	  TABENT UNS.OF,XX UNS.OF	; Off
	  TABENT UNS.RR,0		; Request Reload - impossible
	TABEND
	SUBTTL KNISV - Unvectored interrupt service for KLNI's

;
; This routine is called from STG when an unvectored interrupt on the KLNI
; channel occurs.  If there are no interrupt bits set, we just return so that
; other devices on this level can be serviced.  Otherwise, go into the real
; interrupt handler for the KLNI.
;

IFN FTHINI,RESCD

KNISV::	RDTIME ENTINT		; Remember when we entered this interrupt level
	EA.ENT			; Must run extended
IFN FTHINI,<
	JRST @[KNISV2]		; Jump to the skyseg
	XRESCD			; Change PSECTs
KNISV2:
>

	MOVX T2,CHNBAS-$PSNXT	; Get base of channel tables
KNISV1:	OPSTR <SKIPN T2,>,PSNXT,(T2) ;  Get address of next channel block
	 RET			;  Return if no more channels to check
	OPSTR <XCT>,PSCNI,(T2)	; Read KLNI status
	TXNN T1,CI.INT!CI.ERR	; Any reason for interrupt?
	 JRST KNISV1		;  Nope, check next KLNI

	CALL INTPRO		; Go process interrupt
	 NOP			;  In case of skips
	DMOVE T1,ENTINT		; Get the time at which we entered
	RDTIME ENTINT		; Get current runtime
	DSUB T1,ENTINT		; Compute neg amount of time we spent here
	DMOVN T1,T1		; Make it positive
	DADD T1,TOTINT		; Accumulate total time spent here
	DMOVEM T1,TOTINT	; Put it in the right place
	UNBRK KNI		; Dismiss the interrupt

repeat 0,<
	BUG. (HLT,KNIUEI,PHYKNI,HARD,<PHYKNI - Unexpected interrupt>,<<T1,CONI>,<T2,CDBSTS>>,<

Cause:	The KLNI has interrupted the processor, however the monitor does not
	beleive that there is a KLNI in this channel slot.  Since the monitor
	determined that this is NOT a KLNI (at system startup time via
	KNITST), this interrupt should NOT have happened.

Action:	Call field service.

Data:	CONI - CONI KNI,...
	CDBSTS - Contents of CDBSTS word of channel data block.
>)
> ; End of repeat 0
	SUBTTL INTPRO - Determine what caused the interrupt, and handle it

; Call:	T1/ CONI bits
;	T2/ channel block address
;	CALL INTPRO
;	 <+1 Return - Ignore>
;	<+2 Return>

INTPRO:	SAVEAC <P1,PS,PR,CM,UN>
	MOVE PS,T2		; Setup pointer to port storage
	TXNE T1,CI.ERR		; MBUS or parity error?
	 JRST KNIERR		;  Yes, do special handling

	TXNN T1,CI.FQE		; Free queue error?
	IFSKP.
	  MOVE T2,T1		; Save CONI bits for a minute
	  MOVX T1,CI.FQE+CO.BTS	; Get bits for CONO
	  OPSTR <XCT>,PSCNO,(PS); Clear the condition
	  SKIPE NIBUGX		; Skip this if we aren't interested
	  BUG. (INF,KNIFQE,PHYKNI,SOFT,<PHYKNI - Free Queue Error>,,<

Cause:	  The KLNI received a packet for a protocol, and there were no free
	  packets available for that protocol type.

Action:	  Determine which protocol type ran out of packets, and fix the driver
	  for that protocol type.
>)
	  TXNN T2,CI.RQA	; Is there anything on the response queue?
	   RET			;  No, just return
	ENDIF.

	MOVX T1,CI.RQA+CO.BTS	;Get bits for CONO
	OPSTR <XCT>,PSCNO,(PS)	;Clear "response queue available"

; Here to process the response queue.  Note that since we may come through
; here during error recovery, CONO's or CONI's to the KLNI should not be
; done after this point.

INTRQA:	STKVAR <RSPCNT>		;Temp storage for count of entrys on queue
	SETZM RSPCNT		;Zero it

	LOAD T1,PSPCB,(PS)	;Get virtual address of the PCB
	XMOVEI T1,PB.RQI+QH.IWD(T1) ;Get response queue header interlock addr
	LOAD P1,PSPBA,(PS)	;Get physical address of the PCB
	ADDX P1,PB.RQI+QH.FLI	;Compute phys addr of resp Q flink
	MOVE T3,P1		;Get another copy of the address

; No need for NIOFF/NION because we only come here at interrupt level, or
; when device is not running.

NIINT2:	OPSTR <AOSN T2,>,QHIWD,(T1) ;Wait for interlock
	 JRST NIINT3		; Got it
	CAXG T2,TIMOUT		;Waited long enough?
	 JRST NIINT2		;No, try again
	BUG. (CHK,KNIRIT,PHYKNI,SOFT,<PHYKNI - Response queue interlock timed out>,,<

Cause:	PHYKNI did not succeed in getting the response queue interlock after
	5000. tries.

>,RTN,<DB%NND>)			;[7.1210]

NIINT3:	MOVE T2,P1		;Save physical q-header address for a moment
	OPSTR <EXCH P1,>,QHFLI,(T1) ;Get forward link, make q's point to itself
	OPSTR <EXCH T3,>,QHBLI,(T1) ;Do same to backward link
	SETONE QHIWD,(T1)	;Release the interlock

	CAMN P1,T2		;Does the q-header point to itself
	 JRST QEMPTY		; Yes, shouldn't have gotten here...

	MOVE T2,T3		;Get address of last item on the queue
	SETZ T3,		;Get a zero
	PMOVEM T3,T2		;[7.1002] Make last entry on queue point to 0

NIINT5:	MOVE T2,P1		;Get physical address of q-entry
	ADDI T2,QE.VIR		;Compute phys addr of virt addr of q-entry
	PMOVE T2,T2		;[7.1002] Fetch virtual address of this q-entry
	MOVE CM,T2		;CM <= virtual address of q-entry
IFN FTHIST,<
	CALL RECCMB		;Record the contents of the command block
>; End of FTHIST
	LOAD P1,CMFLI,(CM)	;Get pointer to next entry

	AOS RSPCNT		;Count one more

	LOAD T1,CMERR,(CM)	;Get the error code
	LOAD T4,CMOPC,(CM)	;Get the command opcode
	CAXL T4,DSPEND		;Legal opcode ??
	 JRST UNKRES		; BUG. if not
	SKIPE T1		;Got an error?
	 CALL RSPERR		; Yes, process the error
	STOR T1,CMERC,(CM)	;Save the error code
	SKIPL T1		;Skip this if command was aborted
	CALL @CMDDSP(T4)	;Call the routine for completion
	 NOP
	JUMPN P1,NIINT5		;Process the next response if ptr non-zero

	MOVE T1,RSPCNT		;Get the current count
	OPSTR <CAMLE T1,>,PSRSP,(PS) ;Current greater than max ?
	 STOR T1,PSRSP,(PS)	; Yes, make it the new max
	RET			;And return from the interrupt

	ENDSV.
	SUBTTL RSPERR - Handle errors generated by a response

; This routine will process an error detected in a response.  The error
; will be dispatched to a command specific routine, and then validated.
; An error code will be returned in T1.

	EC.VXM==1B0		; This error is valid for transmit
	EC.VRC==1B1		; This error is valid for receive
	EC.VCL==1B2		; This error is valid for close

	DEFINE XX(code,arg)<TABENT code,<IFIW!arg>>

ERRDSP:	TABBEG 0,37,IFIW!ILLERR
	  XX NI.EXC,XMTERR	; Excessive collisions
	  XX NI.CCF,XMTERR	; Carrier check failed
	  XX NI.CDF,XMTERR	; Collision detect check failed
	  XX NI.FTL,XRERR	; [7174] Frame too long
	  XX NI.RFD,XMTERR	; Remote failure to defer
	  XX NI.BCE,RCVERR	; Block check error (CRC error)
	  XX NI.FER,RCVERR	; Framing error
	  XX NI.DOV,RCVERR	; Data overrun
	  XX NI.FTS,XMTERR	; Frame too short
	  XX NI.QLV,RCVERR	; Queue length violation
	  XX NI.URC,CLOERR	; Unrecognized command
	  XX NI.BLV,BLVERR	; Buffer length violation
	TABEND

RSPERR:	LSH T1,-1		; Right justify the error code
	JRST @ERRDSP(T1)	; Process the error

; Here for errors which can only be caused by a transmit

XMTERR:	CAIE T4,OP.SND		; Is it a transmit?
	 JRST ILLERR		;  Nope, die
	CAIN T1,NI.EXC		; Excessive collisions?
	 JRST EXCERR		;  Yes, go handle it
	CAIN T1,NI.CCF		; Carrier check failed?
	 JRST CCFERR		;  Yes, go handle it
	CAIN T1,NI.CDF		; Collision detect failed?
	 JRST CDFERR		;  Yes, go handle collision detect failure
	CAIN T1,NI.RFD		; Remote failure to defer?
	 JRST RFDERR		;  Yes, handle it
	CAIN T1,NI.FTS		; Frame too short?
	 JRST FTSERR		;  Yes, handle it
	JRST ILLERR		; None of the above, die

; Here for errors that can occur during a receive

RCVERR:	CAIE T4,OP.RCV		; Is this a receive?
	 JRST ILLERR		;  Nope, die
	CAIN T1,NI.BCE		; Block check error?
	 JRST ILLERR		;  Yes, go handle it
	CAIN T1,NI.FER		; Framing error?
	 JRST ILLERR		;  Yes, process the framing error
	CAIN T1,NI.DOV		; Data overrun?
	 JRST DOVERR		;  Yes, process it
	CAIN T1,NI.QLV		; Queue length violation?
	 JRST QLVERR		;  Yes, process it
	JRST ILLERR		; Didn't find it, die

; Here for errors that can occur during transmit or receive
XRERR:	CAIN T1,NI.FTL		; [7174] Frame too long?
	 JRST FTLERR		; [7174]  Yes, go process the error
	JRST ILLERR		; [7174] No, thats all we expect, so die

; Here on errors that can occur during a close

CLOERR:	CAIE T4,OP.FLS		; Is this a flush?
	 JRST ILLERR		;  Nope, die
	CAIN T1,NI.URC		; Unrecognised command?
	 JRST URCERR		;  Yes, go process it
	JRST ILLERR

; Here when we get an unrecognised command during a close

URCCOD==1234			; Magic code that tells CLOSED that we got here

URCERR:	MOVX T1,URCCOD		; Get the magic code
	RET

; Here when we get a buffer length violation.  This happens when the BSD chain
; contains inconsistent length information with respect to the command block
; or the free queue.

BLVERR:	BUG. (HLT,KNIBLV,PHYKNI,SOFT,<PHYKNI - Buffer length violation>,,<

Cause:	The BSD chain contained inconsistent length information for 
	the transmit or receive command that caused it.
>)

ILLERR:	BUG. (HLT,KNIIEC,PHYKNI,HARD,<PHYKNI - Illegal port error code>,<<T1,CODE>,<T4,CMD>>,<

Cause:	The port generated a response which contained:

	a. An unknown error code
	b. An inappropriate error code for the command

Data:	CODE - Error Code
	CMD- Command
>)
	SUBTTL Transmit errors

; Here for excessive collisions

EXCERR:	MOVX T1,UNEXC%		; Get error code
	RET			; And pass it on

CCFERR:	LOAD T1,CMTDR,(CM)	; Get the TDR value
	BUG. (INF,KNICCF,PHYKNI,HARD,<PHYKNI - Carrier check failed>,<<T1,TDR>>,<

Cause:	The NIA module did not detect it's own carrier while it was
	transmitting.

Action:	Make sure that both ends of the transceiver cable are securely
	fastened.  Also check Ethernet cable to see if it is properly
	terminated.

Data:	TDR - TDR value
>,,<DB%NND>)			;[7.1210] 
	MOVX T1,UNCCF%		; Get the error code
	RET

; Here on Collision Detect Check failures

CDFERR:	BUG. (CHK,KNICDF,PHYKNI,HARD,<PHYKNI - Collision detect check failed>,,<

Cause:	The H4000 did not assert the collision detect signal shortly after
	completion of a transmission.  (This signal is also known as the
	"Heartbeat" of the H4000).

Action:	Check the transceiver cable and make sure both ends are securely
	fastened (to the H4000 and the NIA box).  Check the H4000.
>,,<DB%NND>)			;[7.1210] 
	SETZ T1,		; Indicate successful transmission
	RET

; Here when we get a frame too long error

FTLERR:	BUG. (CHK,KNIFTL,PHYKNI,HARD,<PHYKNI - Frame too long>,,<

Cause:	The NIA module detected that it was transmitting a frame longer
	than 1536. bytes.

Action:	Check the NIA module.
>,,<DB%NND>)			;[7.1210] 
	SETZ T1,		; Just return success
	RET

; Here when a remote station fails to defer to us (ie: a late collision).

RFDERR:	LOAD T1,CMTDR,(CM)	; Get the TDR
	BUG. (INF,KNIRFD,PHYKNI,HARD,<PHYKNI - Remote failure to defer>,<<T1,TDR>>,<

Cause:	A collision was detected after the NIA had "acquired" control of the
	Ethernet cable.  This is also known as a "late collision".

	A collision may only occur during the transmission of the preamble
	of a frame.  This problem occurs when the collision is detected
	after the preamble has been transmitted.

Action:	Check the Ethernet cable.  The maximum distance between any two
	stations on the cable may not exceed 1500. meters.  A longer cable
	may result in late collisions of this sort.  This problem may also
	be caused by a malfunctioning Ethernet station.  Check the other
	Ethernet stations on the cable to see if they are having similar
	problems.

Data:	TDR - TDR value
>,,<DB%NND>)			;[7.1210] 
	MOVX T1,UNRFD%		; Get error code for Remote Failure to Defer
	RET

; Here when we attempt to transmit a frame with less than 46. bytes of data
; and we didn't specify padding.

FTSERR:	BUG. (HLT,KNIFTS,PHYKNI,SOFT,<PHYKNI - Frame too short>,,<

Cause:	The port was told to transmit a frame with less than 46. bytes of
	user data and the pad flag (CMPAD) was not set.  This should have
	been detected by NISND.
>)
	SUBTTL Here to handle receive errors

; Here when we get a NIA receive buffer overrun

DOVERR:	BUG. (CHK,KNIDOV,PHYKNI,HARD,<PHYKNI - NIA buffer overrun>,,<

Cause:	The NIA module did not have enough free space to store an
	incoming datagram.
>,,<DB%NND>)			;[7.1210] 
	LOAD PR,RDPID,(CM)	; Get the portal ID
	LOAD T1,PRFQA,(PR)	; Get PTT entry address
	LOAD T1,PTVIR,(T1)	; Get virt address of free queue header
	CALL PUTQUE		; Put this buffer back on the free list
	SETO T1,		; Indicate to NIINT that command has been
				;  blown away
	RET

; Here when the datagram won't fit into the user's buffer

QLVERR:	MOVX T1,UNRDL%		; Get code for Received Datagram too Long
	LOAD PR,RDPID,(CM)	; Get the portal block
	JE PRPAD,(PR),RTN	; Just return if not padding
	MOVX T1,UNLER%		; Make it a Length Error if padding
	RET
	SUBTTL KNIERR - Handle KLNI hardware errors

; Here when any CONI error bits are set.  If we got a KLIPA CRAM parity
; error, see what the PC is.  If the PC is between MINCPE and MAXCPE inclusive
; we have a planned CRAM parity error (ie: a ucode stopcode).  Otherwise we
; have an uplanned CRAM parity error which is a CRAM hardware problem.  If
; we get an EBUS parity error, Data Mover parity error, or an MBUS error,
; we just log the problem, and don't do any more analysis.

KNIERR:	SAVEAC P1
	MOVE P1,T1		; Save original CONI

	PUSH P,P1		; Save P1 for a moment
	TXNE P1,CI.RQA		; Response queue available?
	 CALL INTRQA		;  Yes, clean up the response queue
	POP P,P1		; Restore P1

	CALL NISTP		; Prevent the port from being restarted

	CALL LGTAD		; Read the time and date
	STOR T1,PSTPC,(PS)	; Save it for posterity

	CALL RDLAR		; Read the LAR (PC at this point)
	 SETO T1,		;  Port still running!?!?  Use a funny PC...
	STOR T1,PSLAR,(PS)	; Save the CRAM address

	TXNN P1,CI.CPE		; CRAM parity error?
	 JRST HRDERR		;  No, treat MBUS, EBUS, DATA MVR errors special

	CAXL T1,MINCPE		; Before the first planned CRAM parity error?
	 JRST KNIPPE		;  No, must be a micro-stopcode

; Here on an unplanned CRAM parity error

PARERR:	SAVEAC P2
	MOVE P2,T1		; Save the PC
	CALL RDCRAM		; Read the CRAM location
	 SETO T1,		; !?! Port running!?!
	STOR T1,PSCRL,(PS)	; Save the MSB in the PSB
	STOR T2,PSCRR,(PS)	; Save the LSB in the PSB
	BUG. (CHK,KNIPER,PHYKNI,HARD,<PHYKNI - CRAM parity error>,<<P1,CONI>,<P2,ADDR>,<T1,LOCMSB>,<T2,LOCLSB>>,<

Cause:	The KLNI has detected a parity error in it's Control RAM.  This is
	a hardware problem.  

Action:	Reload the ucode.  If the problem persists call field service.

Data:	CONI - CONI
	ADDR - Address of parity error
	LOCMSB & LOCLSB - Contents of memory location
>,,<DB%NND>)			;[7.1210] 
	CALL STRLD		; Set state to RELOAD and start KNILDR
	CALLRET LOGERR		; Log the problem and return

HRDERR:	BUG. (CHK,KNIHED,PHYKNI,HARD,<PHYKNI - Hard error detected>,<<P1,CONI>,<T1,PC>>,<

Cause:	MBUS ERROR, or EBUS PARITY ERROR was detected.  This is a KLNI
	hardware problem.  The address (ADDR) and it's contents (LOCMSB 
	and LOCLSB) are printed out.

Action:	Call field service.

Data:	CONI - CONI KNI,
	PC - PC (Microcode PC at time of problem)
>,,<DB%NND>)			;[7.1210] 
	MOVX T1,UNS.BK		; Port is now busted
	CALL SETSTA		; Tell everyone
	CALLRET LOGERR		; Log the error and return
	SUBTTL KNIPPE - Planned CRAM Parity Errors

; We come here when the KLNI gets a CRAM parity error whose address is between
; MINCPE and MAXCPE (inclusive).  In this case, the address is a special code
; which indicates a unique problem with the KLNI.  This is rather analogous
; to a stopcode or BUGHLT for the KLNI microcode.

KNIPPE:	LOAD T3,PSCBA,(PS)		; Get CBUS address
	LSH T3,2			; * 4 (length of channel logout area
	DMOVE T3,KIEPT+1(T3)		; Get channel logout words 1&2
	CALL @DIETAB-MINCPE(T1)		; Dispatch to the appropriate routine
	CALL LOGERR			; Go log the problem
	CALLRET STDUMP			; And dump the KLNI

DIETAB:	TABBEG MINCPE,MAXCPE,FOO
	  TABENT 7750,<IFIW DIEIPE>	; INTERNAL.ERROR
	  TABENT 7751,<IFIW DIEFST>	; FAIL.SELF_TEST
	  TABENT 7752,<IFIW DIEEPE>	; FAIL.EBUS
	  TABENT 7753,<IFIW DIEEPE>	; FAIL.EBUS.QUE
	  TABENT 7754,<IFIW DIEPIE>	; FAIL.PLIPE
	  TABENT 7755,<IFIW DIECPE>	; FAIL.CBUSPE
	  TABENT 7756,<IFIW DIEDPE>	; FAIL.PAR_PRE
	  TABENT 7757,<IFIW DIECAE>	; FAIL.CBUS.AVAIL
	  TABENT 7760,<IFIW DIEERE>	; FAIL.EBUSRQ
	  TABENT 7761,<IFIW DIEGCE>	; FAIL.GRANT.CSR
	  TABENT 7762,<IFIW DIESWC>	; FAIL.CHANNEL
	  TABENT 7763,<IFIW DIESCE>	; SPUR.CHAN.ERR
	  TABENT 7764,<IFIW DIESTA>	; FAIL.XMT.ATTN
	  TABENT 7765,<IFIW DIEUBE>	; USED.BUFF.PE
	  TABENT 7766,<IFIW DIEFBE>	; FREE.BUFFER.PE
	  TABENT 7767,<IFIW DIEXPE>	; XMIT.BUFFER.PE
	  TABENT 7770,<IFIW DIEUPE>	; ERR020
	  TABENT 7771,<IFIW DIEUPE>	; ERR021
	  TABENT 7772,<IFIW DIEUPE>	; ERR022
	  TABENT 7773,<IFIW DIEUPE>	; ERR023
	  TABENT 7774,<IFIW DIEUPE>	; ERR024
	  TABENT 7775,<IFIW DIEUPE>	; ERR025
	  TABENT 7776,<IFIW PARERR>	; Regular CRAM parity error
	  TABENT 7777,<IFIW DIEUPE>	; ERR026
	TABEND
	SUBTTL Individual Planned CRAM Parity Errors

DIEUPE:	BUG. (INF,KNIUPE,PHYKNI,HARD,<PHYKNI - NIA20 unknown planned CRAM parity error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA20 got a CRAM parity error in the range of 7750 to 7777.  This
	particular error falls into this range, but is not known to TOPS-20.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIEIPE:	BUG. (INF,KNIIPE,PHYKNI,HARD,<PHYKNI - Internal port error>,<<P1,CSR>,<P2,VERSION>,<T1,ADDR>>,<

Cause:	The NIA20 detected an inconsistency with an operation it was
	performing.  The inconsistency can be caused by any number of things,
	but the end result is that the function did not occur correctly or was
	not logical.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	VERSION - Version number of the NIA20 microcode
	ADDR - Address of parity error
>,,<DB%NND>)			;[7.1210] 
	RET

DIEFST:	BUG. (INF,KNIFST,PHYKNI,HARD,<PHYKNI - Failed self test>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	When the NIA20 is idle it performs a self test to check out various
	pieces of logic (such as the ALU, the microsequencer, and the data
	mover/formatter).  It also performs a self test when it is first
	started.  In one of those cases, the self test failed.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET
DIEEPE:	BUG. (INF,KNIEPE,PHYKNI,HARD,<PHYKNI - EBUS parity error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA20 received a word with bad parity from the EBUS.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIEPIE:	BUG. (INF,KNIPIE,PHYKNI,HARD,<PHYKNI - PLI parity error>,,<

Cause:	More than five parity errors occurred when reading data over the PLI
	interface.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.
>,,<DB%NND>)			;[7.1210] 
	RET

DIECPE:	BUG. (INF,KNICPE,PHYKNI,HARD,<PHYKNI - CBUS parity error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA20 detected bad parity for data that was read over the CBUS.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET
DIEDPE:	BUG. (INF,KNIDPE,PHYKNI,HARD,<PHYKNI - NIA20 data path error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The threshold (5) for data mover parity errors was exceeded.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIECAE:	BUG. (INF,KNICAE,PHYKNI,HARD,<PHYKNI - CBUS available timeout>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA20 was unable to acquire control of the CBUS within 50
	microseconds from the start of a CBUS request.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIEERE:	BUG. (INF,KNIERE,PHYKNI,HARD,<PHYKNI - EBUS request timeout>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA20 was unable to get control of the EBUS within 20 milliseconds
	after making a PI request.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET
DIEGCE:	BUG. (INF,KNIGCE,PHYKNI,HARD,<PHYKNI - Grant CSR timeout>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA20 was unable to acquire control of the CSR (CONI word) within
	10 milliseconds after requesting it.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIESWC:	BUG. (INF,KNISWC,PHYKNI,HARD,<PHYKNI - Channel short word count>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	When the NIA20 completed a CBUS transfer, the channel had a short word
	count error.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIESCE:	BUG. (INF,KNISCE,PHYKNI,HARD,<PHYKNI - Spurious channel error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	A spurious channel error occurs whenever the channel raises the error
	signal, but no error bits are present in the channel logout area.  This
	error occurs after the threshold (5) of spurious channel errors has
	been exceeded.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET
DIESTA:	BUG. (INF,KNISTA,PHYKNI,HARD,<PHYKNI - NIA20 spurious transmit attention>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA module set the PLI transmit attention bit, but there was no
	transmit pending according to the microcode.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIEUBE:	BUG. (INF,KNIUBE,PHYKNI,HARD,<PHYKNI - NIA20 used buffer list parity error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The port received a PLI parity error while reading the NIA module's
	user buffer list.  This error is only reported after a threshold (5)
	for this type of error has been exceeded.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET
DIEFBE:	BUG. (INF,KNIFBE,PHYKNI,HARD,<PHYKNI - NIA20 free buffer list parity error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA receive status indicated that there was a free buffer list
	parity error.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET

DIEXPE:	BUG. (INF,KNIXPE,PHYKNI,HARD,<PHYKNI - NIA20 transmit buffer parity error>,<<P1,CSR>,<T1,ADDR>,<T3,LOGOU1>,<T4,LOGOU2>>,<

Cause:	The NIA transmit status indicated a transmit buffer parity error.  This
	error is not reported until a threshold (5) of this type of error has
	been exceeded.

	The NIA20 will be dumped and restarted by KNILDR.

Action:	Contact Field Service.

Data:	CSR - CONI KNI,
	ADDR - Address of parity error
	LOGOU1 - Channel logout word 1
	LOGOU2 - Channel logout word 2
>,,<DB%NND>)			;[7.1210] 
	RET
	SUBTTL KNICHK - Periodic Checks

IFN FTHINI,RESCD

KNICHK::EA.ENT

IFN FTHINI,<
	JRST @[KNICH2]		; Jump to the skyseg
	XRESCD			; Change PSECTs
KNICH2:
>

	SAVEPQ			; Save some ACs
	MOVEI T1,KNICLK		; Get timer value
	MOVEM T1,KNITIM		; Reset the timer

	MOVE T1,TODCLK		; Get current time
	CAML T1,CORTIM		; Is it time to check for stale blocks?
	 CALL CORCHK		;  Yes, release the stale blocks

	SKIPN CHNBAS		; Get base of channel blocks
	 JRST LNGTIM		;  No channels, sleep for a long time
	CALL ALLCHN		; The following code must run for all channels

; See if the channel is in the RUN state.  If so, do normal timer stuff, if
; not, just ignore it.

	LOAD T1,PSSST,(PS)	; Get the channel's internal state
	CAXE T1,UNS.RN		; Are we running?
	 RET			;  Nope, just quit

	CALL NIRSI		; Give the KLNI something to do
	 TRNA			;  Hmmm, command still pending, kill the KLNI
	RET			; Everything's fine, just return

; There has been an outstanding read station info command for longer than
; the timeout period.  BUGCHK and return.

	OPSTR <XCT>,PSCNI,(PS)	; Read the status
	MOVE P1,T1		; Put it in a safe place
	CALL NISTP		; Stop the channel gracefully
	CALL RDLAR		; Read the LAR
	 SETO T1,		; Port is still running!  Use a funny PC...
	BUG. (CHK,KNISTP,PHYKNI,SOFT,<PHYKNI - KLNI stopped>,<<P1,CONI>,<T1,LAR>>,<

Cause:	No response from KLNI after 5 seconds.

Data:	CONI - CONI KNI,
	LAR - Latched Address Register
>,,<DB%NND>)			;[7.1210] 
	CALLRET STDUMP		; Take a dump and restart the channel

; Here when no KLNI's are present.  Sleep for a long time

LNGTIM:	HRLOI T1,377777		; Get the largest positive integer
	MOVEM T1,KNITIM		; Save it in the clock table
	RET			; ...
	SUBTTL Good Stuff - KLNI dump and reload logic description
comment ~

The primary philosophy behind this code is that the KLNI may be dumped or
reloaded in two different ways.  The first way is manually, by someone
explicitly running KNILDR.  The second way is automatically, by getting the
monitor to run the appropriate program.

There are two goals to this design.
1) Keep the monitor code as simple as possible.
2) Put as much brains as possible into KNILDR.

A monitor initiated dump/reload works as follows:
1) Some routine decides that the KLNI needs to be dumped/reloaded.
2) This routine calls STDUMP or STRLD depending upon the type of service
   desired.
3) STDUMP puts sets the channel state to "DUMP".  Goto step 5.
4) STRLD puts sets the channel state to "RELOAD".
5) Job 0 is told to run KNILDR.
6) Sometime later... KNILDR actually runs.  It scans through all the available
   channels examining their channel states.  If a channel's state is not "DUMP"
   or "RELOAD", it is ignored.  The following steps are executed for each
   channel in the DUMP or RELOAD state:
      a) The channel is stopped by setting it's state to DUMP or RELOAD.
      b) The channel is dumped or reloaded by doing I/O instructions.
      c) The channel is restarted by setting it's state to RUN.

A user initiated dump/reload must follow these steps:
1) The channel is stopped by setting it's state to DUMP or RELOAD (whichever is
   appropriate).
2) The channel is dumped or reloaded by doing I/O instructions.
3) The channel is restarted by setting it's state to RUN.

Notice the similarity between these steps, and steps 6abc in preceding section.
This is intentional.  Both the monitor and the program will always exhibit
similar behavior independant of where the dump or reload request came from.  
~
	SUBTTL KNIJB0 - Run KNILDR if necessary

; Here to startup job 0.  May be called at any time.

STDUMP:	SKIPA T1,[UNS.DP]	; Here to start a KLNI dump
STRLD:	 MOVX T1,UNS.RE		;  Here to start a KLNI reload
	CALL SETSTA		; Set the new state
	MOVE T1,TODCLK		; Get current time
	STOR T1,PSTLR,(PS)	; Start timing KNILDR
	AOS JB0STF		; Set our flag
	AOS JB0FLG		; Tell the monitor to run job 0
	RET			; All done

	SWAPCD			; This should be swappable

; Here whenever job 0 runs.  See if somebody requested that KNILDR be run.
; If so, run it and wait for completion.  Otherwise, quit immedaitely.

KNIJB0::SKIPN JB0STF		; Do we have anything to do??
	 RET			;  Nope, quit while we're ahead

IFN FTHINI,<
	JRST @[KNIJBH]		; Yes, enter the skyseg
	XSWAPCD			; This should be swappable
KNIJBH:
>
	SETZM JB0STF		; Yes, say that we've been here...

; Here when dumping/reloading the KLNI.  Run a job 0 fork with KNILDR in it.

	MOVX T2,OWGP. (7,XADDR.([ASCIZ /SYSTEM:KNILDR.EXE/]))
	MOVX T1,<GJ%SHT+GJ%OLD>	; Use short form GTJFN, file must exist
	MOVEI T3,1		; Use CCL entry point
	SETZ T4,		; Don't care about fork number
	CALLX (MSEC1,RUNDII)	; Run the program to completion
	IFNSK.
	  BUG. (CHK,KNICFF,PHYKNI,SOFT,<PHYKNI - Cannot reload the KLNI>,<<T1,ERROR>>,<

Cause:	The monitor was unable to find SYSTEM:KNILDR.EXE when it attempted to
	reload or dump the port.

Action:	Make sure that KNILDR.EXE is installed in SYSTEM:.

Data:	ERROR - Error code from RUNDII (Probably a JSYS error).
>,,<DB%NND>)			;[7.1210] 
	  CALL ALLCHN		; Loop through all channels

	  LOAD T1,PSSST,(PS)	; Get the substate
	  CAIE T1,UNS.DP	; Dump?
	   CAIN T1,UNS.RE	;  Or Reload?
	    JRST KNIJB1		;   Yes, funeral for a KLNI
	  CAIE T1,UNS.DR	; Dump/Reload?
	   RET			;  Nope, just ignore the KLNI
KNIJB1:	  MOVX T1,UNS.CR	; Get new state - Can't reload
	  CALLRET SETSTA	; Tell everyone about it
	ENDIF.

; Here after the successful completion of KNILDR

	CALL ALLCHN		; Check the status of all channels

	LOAD T1,PSSST,(PS)	; Get the substate
	CAXN T1,UNS.RN		; Is the KLNI now running?
	 RET			;  Yes, all done!

	BUG. (CHK,KNIRLF,PHYKNI,SOFT,<PHYKNI - KLNI Reload Failed>,<<T1,STATE>>,<

Cause:	KNILDR ran, but failed to reload the KLNI for some reason.

Data:	STATE - State of the KLNI
>,,<DB%NND>)			;[7.1210] 
	JRST KNIJB1		; Put KLNI into a terminal state

IFE FTHINI,<
	RESCD>
IFN FTHINI,<
	XRESCD>			; Put us in the skyseg
	SUBTTL Read/Write CRAM

;ROUTINE TO READ MICROCODE VERSION NUMBER
;CALL:	CALL RDVER	; NO ARGUMENTS
;	 <+1 COULDN'T READ VERSION BECAUSE UCODE WAS RUNNING>
;	<+2 Major version # in T1, minor version # in T2, edit # in T3>
;

RDVER:	SAVEAC P1
	MOVX T1,MICVER		; Read the version word
	CALL RDCRAM		; Read it
	 RET			;  Can't read CRAM while port is running...
	MOVE P1,T1		; Save the version # for a moment
	MOVX T1,MICEDT		; Read the edit word
	CALL RDCRAM		; Read it
	 RET			;  ...
	LOAD T3,UCEDT,T1	; Isolate the edit #
	LOAD T1,UCMAJ,P1	; Isolate major version number
	LOAD T2,UCMIN,P1	; Isolate the minor version number
	RETSKP			; And return success

;ROUTINE TO READ THE LAR (LATCHED ADDRESS REGISTER).
;CALL:	CALL RDLAR	; NO ARGUMENTS
;	 <+1 COULDN'T READ LAR BECAUSE UCODE WAS RUNNING>
;	<+2 LAR IN T1>
;
;ONLY USES T1

RDLAR:	OPSTR <XCT>,PSCNI,(PS)	;READ THE CSR
	TXNE T1,CO.MRN		;IS THE PORT RUNNING?
	 RET			; YES, QUIT NOW
	TLZ T1,-1		;PREVENT FURTHER INDIRECTION
	PUSH P,T1		;SAVE IT FOR A MINUTE
	MOVX T1,CO.LAR		;GET BITS FOR CONI
	OPSTR <XCT>,PSCNO,(PS)	;TELL PORT WE WANT TO READ IT'S LAR
	DATAI KNI,T1		;READ THE LAR
	LOAD T2,MICPC,T1	;RIGHT JUSTIFY ADDR
	POP P,T1		;RESTORE ORIGINAL CONI BITS
	OPSTR <XCT>,PSCNO,(PS)	;RESTORE THE STATUS
	MOVE T1,T2		;GET LAR INTO RIGHT PLACE
	RETSKP			;AND RETURN SUCCESS

;ROUTINE TO READ A CRAM LOCATION
;CALL:	MOVx T1,cram-address
;	CALL RDCRAM
;	 <+1 COULDN'T READ CRAM BECAUSE UCODE WAS RUNNING>
;	<+2 T1 6-35 CONTAINS CRAM MSB'S, T2 6-35 CONTAINS CRAM LSB'S>

RDCRAM:	MOVE T2,T1		;SAVE ADDRESS FOR A MOMENT
	OPSTR <XCT>,PSCNI,(PS)	;GET STATUS
	TXNE T1,CO.MRN		;IS THE PORT RUNNING?
	 RET			; YES, QUIT NOW
	TLZ T1,-1		;CLEAR THE LEFT HALF TO PREVENT INDIRECTION
	PUSH P,T1		;SAVE IT FOR A MINUTE
	SETZ T1,		;SETUP BITS FOR CONO
	OPSTR <XCT>,PSCNO,(PS)	;CLEAR CO.LAR
	STOR T2,MICPC,T2	;PUT THE PC IN THE RIGHT PLACE
	TXO T2,DO.LRA!1B13	;SET UP TO READ THE MSB'S
	DATAO KNI,T2		;TELL THE HARDWARE ABOUT IT
	DATAI KNI,T1		;GET CONTENTS OF HIGH ORDER PART OF CRAM
	TXZ T1,77B5		;CLEAR OUT 0-5 SINCE THEY ARE MEANINGLESS
	TXZ T2,1B13		;SET UP TO READ THE LSB'S
	DATAO KNI,T2		;TELL THE HARDWARE ABOUT IT
	DATAI KNI,T2		;GET CONTENTS OF LOW ORDER PART OF CRAM
	TXZ T2,77B5		;CLEAR OUT 0-5 SINCE THEY ARE MEANINGLESS
	MOVE T3,T1		;SAVE T1 FOR A MOMENT
	POP P,T1		;RESTORE CONI BITS
	OPSTR <XCT>,PSCNO,(PS)	;RESTORE STATUS
	MOVE T1,T3		;FIX T1
	RETSKP
	SUBTTL NISND - Send Datagram

;+
; THIS ROUTINE IS DISPATCHED TO THROUGH THE DLL DISPATCH TABLE.
; SEE NIDSP FOR REGISTER USAGE.
;
;-

NISND:	TRVAR <TXTSIZ,NBSDS>
	JE UNBSZ,(UN),NISNDA	; Jump if MSD style buffer(s)

	CALL NISND5		; Do non-MSD style xmit
	 JRST NISND8		;  Error return, free up CM block
	JRST NISND9		; All is well, proceed normally

; MSD style sends come here

NISNDA:	CALL CNTFRG		; Count number of BSD's needed
	 RET			;  Pass the error upwards
	SETZ CM,		; Clear CM for error recovery
	CALL BLDBSD		; Build the BSD's
	 JRST NISND8		;  Give bad return
;
; NON-MSD CODE REJOINS HERE
;

NISND9:	CALL SETSND		; Setup the send datagram command entry
	 JRST NISND8		;  Got an error, clean un and return
	CALL QUECMD		; Queue the command and exit
	INCR PROXM,(PR)		; Update the outstanding xmit count
	RETSKP			; Give good return

NISND8:	JUMPE CM,RTN		;Just return if no CM block
	EXCH CM,T1		;Save error code for a moment, get command addr
	CALL RXRCOR		;Release the command
	MOVE T1,CM		;Get the error code back
	RET			;And return
	SUBTTL Byte pointer evaluation tables

	BEGSTR BY		; Byte pointer info
	 FIELD OFF,1		; Offset of 0 or 1
	 FIELD CHK,2		; How to check this byte pointer's size
	 FIELD LSH,6		; LSH factor for word alignment
	 FIELD NUM,3		; Number of bytes remaining in word
	ENDSTR

; Values for BYCHK

	.BYILL==0		; This byte pointer is illegal
	.BYOK==1		; This byte pointer is OK
	.BYCSZ==2		; Check the size manually

DEFINE X(pfield,check<3>)
	<..Y==0				;;Initial word offset of 0
	 ..P==pfield			;;Initial position
	 IFL pfield-10,<..Y==1		;;Next word
			..P==^D36>	;;All 36 bits
	 <..Y>B0+<check>B2+<^D36-..P>B8+<..P/8>B11+pfield
	>

DEFINE XX(size)
	<..P==^D36
	REPEAT ^D36/size+1,<
		IFN size-10,FLD(.BYILL,BYCHK)
		IFE size-10,<X(..P,.BYOK)
			..P==..P-size>
		>
	>
	PFIELD==0

BYTAB:
REPEAT 45,<
	X(PFIELD,.BYCSZ)
	PFIELD==PFIELD+1
	>
	XX(6)
	XX(8)
	XX(7)
	XX(9)
	XX(^D18)
	XX(^D37)
; Non-MSD style send.  Send data pointed at by UNBFA.  This takes care of
; byte alignment, page boundaries, etc...

NISND5:	SAVEAC <P1,P2>
	MOVEI T1,3*BD.LST+SB.LST; Get core for BSD for non-MSD send
	SETZ CM,		; Clear CM in case of allocation failure
	CALL GXRCOR		; Allocate it
	 ERROR UNRES%		;  No resources
	MOVE CM,T1		; Set up KLNI command pointer
	XMOVEI P1,SB.LST(CM)	; Point to first BSD
	MAP T1,(P1)		; Make allocated address physical
	TXZ T1,ADRMSK		; Clear junk bits
	STOR T1,SBBBA,(CM)	; Save as BSD base address for KLNI

	OPSTR <DMOVE T1,>,UNBFA,(UN) ; Get byte pointer to buffer
	OPSTR <DMOVEM T1,>,SBBFA,(CM) ; Save it for callback
	LOAD T4,UNBSZ,(UN)	; Get the data length
	LDB T2,[POINT 6,T1,5]	; Get it's P field
	MOVE P2,BYTAB(T2)	; Get the byte pointer descriptor
	LOAD T3,BYCHK,+P2	; Get the size checking field
	JRST @.+1(T3)		; Dispatch to the correct checking routine
	IFIW ERET (UNIBP%)	; .BYILL - Illegal buffer pointer
	IFIW NISND7		; .BYOK - Size is OK
	IFIW NISND6		; .BYCHK - Check the size manually
	IFIW KNIBT1		; Bad BYTAB entry
; Manually check byte size, indexing or indirection, and extract E field

NISND6:	LDB T3,[POINT 6,T1,11]	; Get the S field from the byte pointer
	CAIE T3,8		; 8 bit bytes?
	 ERROR UNIBP%		;  Illegal byte size

; If T1 contains one-word byte pointer, set IFIW to fool next chunk of code.
; If T1 contains a two-word byte pointer, get second word (which is an
; indirect word of some sort).

	TXNN T1,1B12		; Two word byte pointer?
	 TXOA T1,<IFIW>		;  No, make it look like local format indirect
	  LOAD T1,UNBFA,+1(UN)	;   Yes, get 2nd word

; T1 now contains an indirect word.  If extended format indirect and no
; indexing/indirection, we are done.

	TXCN T1,77B5		; IFIW=0 & I=0 & X=0 ???
	 JRST NISND7		;  Yes, global addr, proceed

; T1 contains indirect word (IFIW complemented).  If complemented IFIW and
; no indexing/indirection, we are ok.

	TXNE T1,<IFIW @(17)>	; Indexing or indirection?
	 ERROR UNIBP%		;  Yes to either, no good

; Here for one-word globals, byte size is ok.

	TLZA T1,-1		; Through here for local indirect words
NISND7:  TXZ T1,77B5		;  Remove all but Y field

	TXNE P2,BYOFF		; Does the address need to be incremented?
	 ADDI T1,1		;  Yes, point to next word

	LOAD T3,BYLSH,+P2	; Get the shift factor
	JUMPE T3,NISND3		; Jump if word aligned
; Here when transmitting non-word aligned data.  Fetch the first word and
; word align it.  Save it in the BSD.  Adjust the total length to account
; for the bytes we just took care of.

	OPSTR <MAP T2,>,BDRES,(P1) ; Get addr of reserved word of BSD
	STOR T2,BDSBA,(P1)	; Save that as the segment address
	LOAD T2,UNADS,(UN)	; Get address space
	XCT [MOVE T2,(T1)	;  UNA.EV - Get word from Exec virtual
	     XCTU [MOVE T2,(T1)] ; UNA.UV - Get word from User virtual
	     JRST VMCBUG](T2)	;  UNA.PH - Get word from physical
	LSH T2,(T3)		; Word align the first word
	STOR T2,BDRES,(P1)	; Put the data into the BSD
	ADDI T1,1		; Increment the data address
	LOAD T2,BYNUM,+P2	; Get number of bytes we just took care of
	SUB T4,T2		; Compute number remaining
	STOR T2,BDSLN,(P1)	; Save length in the BSD (possibly wrong)
	MAP T3,BD.LST(P1)	; Get physical address of next BSD
	TXZ T3,ADRMSK		; Clear the junk bits
	STOR T3,BDNXA,(P1)	; Point to next BSD
	XMOVEI P1,BD.LST(P1)	; Point to next BSD
	JUMPG T4,NISND3		; Jump if no more to do

; The first word of data was the only word of data.  Correct the BSD length
; and zap pointer to next BSD and finish up.

	ADD T4,T2		; T4 now contains correct BSD length
	STOR T4,BDSLN,-BD.LST(P1) ; Install it
	SETZRO BDNXA,-BD.LST(P1) ; Clear pointer to next BSD
	JRST NISND2		; We are all done
; At this point T1 points to word aligned data.  T4 is the length in bytes.
; P1 points to a new BSD.  For the moment, assume that buffer does not
; cross a page boundary.

NISND3:	STOR T1,BDRES,(P1)	; Save virtual address of buffer
	LOAD T2,UNADS,(UN)	; Get address space <<< (Match angle brackets)
	XCT [MAP T2,(T1)	;  UNA.EV - Exec virtual => physical
	     XCTU [MAP T2,(T1)]	;  UNA.UV - User virtual => physical
	     TRN](T2)		;  UNA.PH - physical => physical

; Make sure the page is locked down

	LOAD T3,PHCPNO,+T2	; Get just the page #
	MOVE T3,@CST1X+T3	; Get the lock status of this page
	TXNN T3,<-PLKV>		; Is the page locked down?
	 ERROR UNIFB%		;  Nope, bad buffer

	STOR T2,BDSBA,(P1)	; Save the physical address of the data
	STOR T4,BDSLN,(P1)	; Save the data length

	MOVE T1,P1		; Get address of BSD
	LOAD T2,UNADS,(UN)	; Get address space descriptor
	CALL FIXBSD		; Account for virtual page boundaries and such
	 RET			;  Pass the error upwards

; Here after building BSD chain.

NISND2:	SETZRO SBMSD,(CM)	; Make sure MSD is zero
	LOAD T1,UNBSZ,(UN)	; Get the data length
	STOR T1,SNTXL,(CM)	; Save text length
	RETSKP			; And return success
	SUBTTL CNTFRG - Count BSDs

;
;This routine calculates how many BSD's will be needed to transmit
;an MSD style message.
;

CNTFRG:	SETZM NBSDS		; Where total number of BSD's goes
	SETZM TXTSIZ		; Total number of bytes for all MSD's
	OPSTR <SKIPA T2,>,UNBFA,(UN) ; Get the address of the buffer
CNTFR1:	 LOAD T2,MDNXT,(T2)	;  Get the next MSD if any
	JUMPE T2,RSKP		; Return if we're all done
	LOAD T1,MDBYT,(T2)	; Get the byte count
	JUMPE T1,CNTFR1		; Skip this MSD if it's empty
	ADDM T1,TXTSIZ		; Add it in
	MOVEI T1,2		; Allocate 2 BSD's in case buffer crosses
	ADDM T1,NBSDS		;  non-physically contiguous page boundary
	LDB T1,[POINT 6,$MDAUX(T2),5] ; Get the P field from this byte pointer
	MOVE T1,BYTAB(T1)	; Get descriptor for this P field
	TXNE T1,BYLSH		; Is the LSH factor non-zero?
	 AOS NBSDS		;  Increment again if not word aligned
	JRST CNTFR1		; Loop back for rest of MSD's
	SUBTTL BLDBSD - Build BSDs

;+
; THIS ROUTINE WILL ALLOCATE AND BUILD BSD FOR A SEND DATAGRAM FOR MSD STYLE
; BUFFERS.
;
; CALL WITH:
;	NBSDS = NUMBER OF BSDS NEEDED
;
; RETURNS +1: ERROR
;	  +2: BSD'S BUILT
;-

BLDBSD:	SAVEAC <P1,P2,P3>
	STKVAR <BYTES>
	MOVE T1,NBSDS		;GET NUMBER OF BSDS NEEDED
	IMULI T1,BD.LST		;NUMBER OF FRAGMENTS*BSD LENGTH
	ADDI T1,SB.LST		;PLUS ENTRY SIZE
	CALL GXRCOR		;TRY TO ALLOCATE IT
	 RET			;NOT ENOUGH LEFT
	MOVE CM,T1		;Make CM point to command block
	MOVE T2,TXTSIZ		;GET TOTAL TEXT SIZE
	STOR T2,SNTXL,(CM)	;SAVE IT
	XMOVEI P1,SB.LST(CM)	;Point to BSD start
	MAP T1,(P1)		;MAKE IT PHYSICAL
	TXZ T1,ADRMSK		;CLEAR JUNK
	STOR T1,SBBBA,(CM)	;SAVE THE BSD BASE ADDRESS
	LOAD P3,UNBFA,(UN)	;GET TOP MSD ADDRESS
	STOR P3,SBMSD,(CM)	;SAVE MSD ADDRESS
	SETZRO SBBFA,(CM)	;MAKE SURE NONMSD BUFFER IS ZERO

BLDBS1:	LOAD T1,MDBYT,(P3)	;Get the byte count
	JUMPE T1,BLDBS6		;Skip this MSD if it is blank
	STOR T1,BDSLN,(P1)	;Save it
	SETZRO BDPAC,(P1)	;Packing mode is always zero
	LOAD T1,MDAUX,(P3)	;Get the byte pointer
	LDB P2,[POINT 6,T1,5]	;Get it's P field
	MOVE P2,BYTAB(P2)	;Load up the descriptor for this P
	LOAD T2,BYCHK,+P2	;See what type of checking we have to do
	JRST @.+1(T2)		;Dispatch based on type of check

	IFIW RTN		;.BYILL - Illegal byte size, non-skip return
	IFIW BLDBS3		;.BYOK  - Byte size is fine, proceed normally
	IFIW BLDBS2		;.BYCSZ - Check the size manually
	IFIW KNIBT1		;Illegal BYTAB entry

KNIBT1:	BUG. (HLT,KNIBTB,PHYKNI,SOFT,<PHYKNI - Bad BYTAB entry>,<<P2,ENTRY>,<T1,BYTPTR>>,<
	
Cause:	BYTAB has been corrupted.  

Data:	ENTRY - The corrupted entry
	BYTPTR - The byte pointer used to fetch this entry.

>,RTN)
;Here when byte pointer is not one word global.  Check byte size manually

BLDBS2:	LDB T2,[POINT 6,T1,11]	;Get the S field
	CAIE T2,8		;8 bit bytes?
	 RET			; Nope, illegal byte size, non-skip return

BLDBS3:	HRRZ T2,T1		;Get Y field from byte pointer
	OPSTR <ADD T2,>,MDALA,(P3) ;Point to data
	TXNE P2,BYOFF		;Do we need to increment the address?
	 ADDI T2,1		; Yes, an IBP would cross a word boundary
	TXNE P2,BYLSH		;Do we need to move some bytes around???
	 JRST BLDBS4		; Yes, do special stuff

;Here when we have word aligned data.  This is the simplest case.

	STOR T2,BDRES,(P1)	;Save virtual address of the data
	LOAD T1,MDVMC,(P3)	;Get address space <<< (Match angle brackets)
	XCT [MAP T2,(T2)	;Exec virtual => physical
	     XCTUU [MAP T2,(T2)];User virtual => physical
	     NOP](T1)		;Physical => physical
	STOR T2,BDSBA,(P1)	;Save the segment address - physical

	MOVE T1,P1		;Get address of BSD
	LOAD T2,MDVMC,(P3)	;Get the address space descriptor
	CALL FIXBSD		;Correct for page boundarys
	 RET			; Pass the error upwards
	MOVE P1,T1		;Update BSD pointer

;See if any more MSD's.  If so, set up next BSD and start over.

BLDBS5:	LOAD P3,MDNXT,(P3)	;Get next segment address
	JUMPE P3,RSKP		;Jump if all done
	MAP T1,BD.LST(P1)	;Get physical address of next BSD
	TXZ T1,ADRMSK		;Clear junk
	STOR T1,BDNXA,(P1)	;Save as next BSD
	XMOVEI P1,BD.LST(P1)	;Point to next BSD
	JRST BLDBS1		;Do next

;Here when skipping zero length MSD's

BLDBS6:	LOAD P3,MDNXT,(P3)	;Get next segment address
	JUMPN P3,BLDBS1		;Jump if more to look at
	RETSKP
;Here when bytes are not word aligned.

;Build a seperate BSD for the first word's worth of bytes.  Left justify the
;bytes and stuff them into the reserved word of the BSD.  If there are any
;bytes left over, build another BSD for the rest of them (only one more BSD
;is required at this point because the succeeding words will all contain left
;justified bytes).

BLDBS4:	OPSTR <MAP T1,>,BDRES,(P1) ;Get phys addr of reserved word of BSD
	STOR T1,BDSBA,(P1)	;Save that as the data segment address

	LOAD T1,MDVMC,(P3)	;Get the address space
	XCT [MOVE T1,(T2)	;Get 1st word from exec virtual
	     XCTU [MOVE T1,(T2)];Get 1st word from user virtual
	     JRST VMCBUG](T1)	;Get 1st word from physical... too hard for now
	LOAD T3,BYLSH,+P2	;Get the LSH factor
	LSH T1,(T3)		;Left justify the bytes
	STOR T1,BDRES,(P1)	;Put them into the BSD
	LOAD T1,BYNUM,+P2	;Get the potential number of bytes remaining
	LOAD T3,MDBYT,(P3)	;Get actual number of bytes remaining
	CAIL T1,(T3)		;Did this BSD satisfy this MSD?
	 JRST BLDBS5		; Yes, we are all done

;At this point, we are building the second BSD for the word aligned bytes
;of this MSD.

	STOR T1,BDSLN,(P1)	;No, save the actual segment size
	SUB T3,T1		;T3 = # of bytes remaining in this MSD

	MAP T1,BD.LST(P1)	;Get physical address of next BSD
	STOR T1,BDNXA,(P1)	;Save pointer to next BSD
	XMOVEI P1,BD.LST(P1)	;Point to next BSD

	STOR T3,BDSLN,(P1)	;Save length of this segment
	STOR T2,BDRES,(P1)	;Save virtual buffer address
	LOAD T1,MDVMC,(P3)	;Get address space <<< (Match angle brackets)
	XCT [MAP T1,1(T2)	;Exec virtual => physical
	     XCTUU [MAP T1,1(T2)];User virtual => physical
	     ADDI T1,1](T1)	;Physical => physical
	STOR T1,BDSBA,(P1)	;Save it
	SETZRO BDPAC,(P1)	;Always use packing mode of 0

	MOVE T1,P1		;Get address of BSD
	LOAD T2,MDVMC,(P3)	;Get address space descriptor
	CALL FIXBSD		;Check for physical contiguity of buffer
	 RET			; Pass error upwards
	MOVE P1,T1		;Update BSD pointer

	JRST BLDBS5		;All done!
	SUBTTL FIXBSD - Correct buffer for physical contituity

; FIXBSD will see if the addressed BSD points to non-physically contiguous
; memory.  If it does, then:
;	1) The BSD is shortened down to the page boundary
;	2) A new BSD is created which will handle all the bytes beyond the
;	   page boundary.
;	3) The BSD's are linked together.
;	4) The new BSD is returned in T1.
;
; This routine expects that BDSLN and BDSBA are set up as per the hardware
; description.  BDRES should contain the 30 bit virtual address of the
; first word of the buffer.  BDNXA may be destroyed.
;
; Call:	MOVX T1,BSD-address
;	MOVX T2,address-space-descriptor
;	CALL FIXBSD
;	 <Return +1, error code in T1>
;	<Return +2, success, (possible) new BSD address in T1>

;See if buffer segment crosses a page boundary

FIXBSD:	SAVEAC <P1,P2>		; Use P1 as BSD pointer
	DMOVE P1,T1		; Make P1 point to BSD, P2 contains ASD
	LOAD T1,BDSLN,(P1)	; Get the length of the buffer (in bytes)
	MOVEI T1,3-4(T1)	; +3 to round up to word boundary, -4 to
				;  subtract one word
	LSH T1,-2		; Convert it to words
	OPSTR <ADD T1,>,BDRES,(P1) ; Compute last word of buffer
	MOVE T2,T1		; Save virtual end of buffer for a moment
	OPSTR <XOR T1,>,BDRES,(P1) ; Generate bit mask of differences
	TXNN T1,VPGNO		; Page numbers the same?
	 JRST FIXBS1		;  Yes, return success

;Buffer segment crosses a page boundary.  See if pages are physically
; contiguous.

				; Generate physical end of buffer <<< (M.A.B.)
	XCT [MAP T1,(T2)	; Exec virtual => physical
	     XCTU [MAP T1,(T2)]; User virtual => physical
	     NOP](P2)		; Physical => physical
	TXZ T1,ADRMSK!777	; Clear junk bits from last page
	LOAD T2,BDSBA,(P1)	; Get physical address of start of buffer
	SUB T1,T2		; See how many pages we span
	SKIPL T1		; Skip if pages not consecutive
	CAXLE T1,PGSIZ		; Skip if pages are consecutive
	 TRNA			;  Pages aren't consecutive
	  JRST FIXBS1		;   Physically contiguous, all done!

;Buffer occupies non-physically contiguous memory.

	ADD T1,T2		; Restore end address
	STOR T1,BDSBA,+BD.LST(P1); Save segment address in next BSD

	MOVN T1,T2		; T1(27-35) contains # words to page boundary
	ANDI T1,777		; T1 now has # words in 1st BSD
	LSH T1,2		; Convert to bytes
	LOAD T2,BDSLN,(P1)	; Get total buffer length
	STOR T1,BDSLN,(P1)	; Setup new BSD length
	SUB T2,T1		; Compute amount remaining for second BSD
	STOR T2,BDSLN,+BD.LST(P1) ; Install final BSD's length

	MAP T1,BD.LST(P1)	; Get physical address of next BSD
	TXZ T1,ADRMSK		; Clear out junk bits
	STOR T1,BDNXA,(P1)	; Make current BSD point to next BSD
	XMOVEI P1,BD.LST(P1)	; Make P1 point to new BSD

	SETZRO BDPAC,(P1)	; Always use industry compatible packing mode

; Here on good return

FIXBS1:	MOVE T1,P1		; Make T1 point to last BSD
	RETSKP			; And return success
	SUBTTL SETSND - Setup Send Datagram Entry

;+
; THIS ROUTINE SETS UP ALL COMMON FIELDS FOR A SEND DATAGRAM BOTH BSD
; AND NON-BSD.
;
; CALL WITH:
;	CM/ Address of command block
;	UN/ ADDRESS OF NIDLL TO PORT DRIVER INTERFACE BLOCK
;
; RETURNS +1: ALWAYS
;-

SETSND:	LOAD T1,PRPAD,(PR)	; Get the pad flag
	LOAD T2,SNTXL,(CM)	; Get the size
	CAMGE T2,[DEC 46	; Less than the minimum?
		  DEC 0](T1)	;
	 ERROR UNIBS%		;  Yes, Illegal Buffer Size
	CAMLE T2,[DEC 1500	; Greater than the max?
		  DEC 1498](T1)
	 ERROR UNIBS%		;  No, Illegal Buffer Size
	MOVEI T1,OP.SND		; Opcode for send datagram
	STOR T1,CMOPC,(CM)	; Save it
	CALL FETADR		; Fetch address from UN block
	TXNE T1,1B7		; Transmitting a multicast address?
	 OPSTRM <AOS>,PSMXT,(PS);  Yes, count it
	STOR T1,SNHAD,(CM)	; Store the high order address
	STOR T2,SNLAD,(CM)	; Store the low order address
	LOAD T1,PRPRO,(PR)	; Get protocol type
	STOR T1,SNPTY,(CM)	; Store protocol type
	SETONE CMBSD,(CM)	; Set BSD format
	STOR PR,SBPID,(CM)	; Store the portal ID
	LOAD T1,UNRID,(UN)	; Get the request ID
	STOR T1,SBRID,(CM)	; Save that with this command
	LOAD T1,PRPAD,(PR)	; Get the PAD bit
	STOR T1,CMPAD,(CM)	; Tell the KLNI about it
	RETSKP			; Return to caller

	ENDSV.
	ENDTV.
;table to convert P,S field of OWGBP to P or S
;access table using the structures, OWGSZ & OWGPS, with index = P,S field
;or the routines GETSIZ & GETPOS
OWGBTB:	6,,44
	6,,36
	6,,30
	6,,22
	6,,14
	6,,6
	6,,0
	10,,44
	10,,34
	10,,24
	10,,14
	10,,4
	7,,44
	7,,35
	7,,26
	7,,17
	7,,10
	7,,1
	11,,44
	11,,33
	11,,22
	11,,11
	11,,0
	22,,44
	22,,22
	22,,0
	77,,77			;illegal

DEFSTR OWGSZ,OWGBTB-^O45,17,18	;size field
DEFSTR OWGPS,OWGBTB-^O45,35,18	;position field
;get size field from a byte pointer, global or local
;CALL GETSIZ
;T1/ byte pointer
;returns +1 on with T1/ size
;preserves all other ACs

GETSIZ:	SAVEAC P1
	MOVE P1,T1		;save pointer
	LDB T1,[POINT 6,T1,5]
	CAIGE T1,45		;OWGBP ?
	IFSKP.
	  LOAD T1,OWGSZ,(T1)	;yes. convert.
	ELSE.
	  LDB T1,[POINT 6,P1,11] ;no. get size field
	ENDIF.
	RET

;get position field from a byte pointer, global or local
;CALL GETPOS
;T1/ byte pointer
;returns +1 with T1/ position
;preserves all other ACs

GETPOS:	LDB T1,[POINT 6,T1,5]
	CAIL T1,45		;OWGBP ?
	LOAD T1,OWGPS,(T1)	;yes. convert.
	RET

;Get the Y field from the byte pointer pointed at by T1.  This routine handles
;all cases (ie: one word locals, one word globals, two word globals).

IFN FTHINI,RESCD

BP2ADR::LDB T3,[POINT 6,T1,5]	; Get the byte pointer's P field
	CAILE T3,44		; One word global?
	 JRST GETBPO		;  Yes, handle it differently
	TXNE T1,1B12		; Two word byte pointer?
	 JRST GET2WG		;  Yes, another special case
	TLZ T1,-1		; One word local, 18 bit Y field
	RET			; All done

GET2WG:	LDB T1,[POINT 30,T2,35]	; Extract 30 bit address
	TXNE T2,<IFIW>		; Extended format address?
	 TLZ T1,-1		;  No, use 18 bit Y field
	RET

GETBPO:	TXZ T1,77B5		; Zero out the P field
	RET			; And return

IFN FTHINI,XRESCD
	SUBTTL NISRB - Specify Receive Buffer

;+
; THIS ROUTINE IS DISPATCHED TO THROUGH THE DLL DISPATCH TABLE TO ADD
; A BUFFER TO THE LIST FOR RECEIVES.
;
; WITH...
;	STANDARD SETUP
;
; RETURNS +1: ERROR
;	  +2: BUFFER ACCEPTED.
;-

NISRB:	SAVEAC P1
	OPSTR <SKIPN P1,>,PRFQA,(PR) ; Get the PTT entry address
	 ERROR UNNPE%		;  No protocol type enabled

; Allocate a KLNI command block

	MOVEI T1,RD.LST+BD.LEN*2; Get KLNI command buffer
	CALL GXRCOR		; Get it
	 ERROR UNRES%		;  No memory
	MOVE CM,T1		; Setup standard pointer
	XMOVEI T1,RD.LST(T1)	; Get address of first BSD
	MAP T1,(T1)		; Make it physical
	STOR T1,RDPBA,(CM)	; Make RDPBA point to 1st BSD

; Check buffer size.  Make sure size is same as previous buffers.

	LOAD T1,UNBSZ,(UN)	; Get buffer size
	JUMPLE T1,ERET UNIBS%	; Jump if illegal buffer size
	OPSTR <CAMN T1,>,PRBSZ,(PR) ; Does it match previous sizes?
	 JRST NISRB5		;  Yes, continue
	JN PRBSZ,(PR),ERET UNIBS% ; No, jump if size was already set
	LOAD T4,PTVIR,(P1)	; Get address of free-Q header
	STOR T1,QHLEN,(T4)	; Install size of q entries
; Check the buffer pointer.  Make sure it specifies 8 bit bytes, no indexing,
; no indirection, word aligned, physically contiguous, locked pages.
; Also, extract Y field from byte pointer and make it physical.

;Check byte pointer for 8 bit bytes and word alignment

NISRB5:	LOAD T1,UNBFA,(UN)	; Get byte pointer to start of buffer
	LDB T2,[POINT 6,T1,5]	; Get it's P field
	MOVE T2,BYTAB(T2)	; Get the byte pointer descriptor
	LOAD T3,BYCHK,+T2	; Get the size checking field
	JRST @.+1(T3)		; Dispatch to the correct checking routine
	IFIW ERET (UNIBP%)	; .BYILL - Illegal buffer pointer
	IFIW NISRB7		; .BYOK - Size is OK
	IFIW NISRB6		; .BYCHK - Check the size manually
	IFIW KNIBT1		; Bad BYTAB entry

; Manually check byte size, indexing or indirection, and extract E field

NISRB6:	LDB T3,[POINT 6,T1,11]	; Get the S field from the byte pointer
	CAIE T3,8		; 8 bit bytes?
	 ERROR UNIBP%		;  Illegal byte size

; If T1 contains one-word byte pointer, set IFIW to fool next chunk of code.
; If T1 contains a two-word byte pointer, get second word (which is an
; indirect word of some sort).

	TXNN T1,1B12		; Two word byte pointer?
	 TXOA T1,<IFIW>		;  No, make it look like local format indirect
	  LOAD T1,UNBFA,+1(UN)	;   Yes, get 2nd word

; T1 now contains an indirect word.  If extended format indirect and no
; indexing/indirection, we are done.

	TXCN T1,77B5		; IFIW=0 & I=0 & X=0 ???
	 JRST NISRB7		;  Yes, global addr, proceed

; T1 contains indirect word (IFIW complemented).  If complemented IFIW and
; no indexing/indirection, we are ok.

	TXNE T1,<IFIW @(17)>	; Indexing or indirection?
	 ERROR UNIBP%		;  Yes to either, no good

; Here for one-word globals, byte size is ok.

	TLZA T1,-1		; Through here for local indirect words
NISRB7:  TXZ T1,77B5		;  Remove all but Y field

	LOAD T3,BYLSH,+T2	; Get the shift factor
	JUMPN T3,ERET UNIFB%	; Jump if not word aligned

	TXNE T2,BYOFF		; Does the address need to be incremented?
	 ADDI T1,1		;  Yes, point to next word

	STOR T1,BDRES,+RD.LST(CM) ; Save virtual address of this buffer segment
	LOAD T2,UNADS,(UN)	; Get address space indicator <<<(M.A.B.)
	XCT [MAP T2,(T1)	; UNA.EV - Exec virtual => physical
	     XCTU [MAP T2,(T1)]	; UNA.UV - User virtual => physical
	     TRN](T2)		; UNA.PH - Physical => physical

; Make sure the page is locked down

	LOAD T3,PHCPNO,+T2	; Get just the page #
	MOVE T3,@CST1X+T3	; Get the lock status of this page
	TXNN T3,<-PLKV>		; Is the page locked down?
	 ERROR UNIFB%		;  Nope, bad buffer

	STOR T2,BDSBA,+RD.LST(CM) ; Make BSD point to this segment
	LOAD T4,QHLEN,(T4)	; Get theoretical size of this segment
	STOR T4,BDSLN,+RD.LST(CM) ; Setup size of this segment

	XMOVEI T1,RD.LST(CM)	; Setup pointer to BSD
	LOAD T2,UNADS,(UN)	; Setup Address Space Descriptor
	CALL FIXBSD		; Account for virtual page boundaries and such
	 RET			;  Pass the error upwards

	STOR PR,RDPID,(CM)	; Save portal ID in receive buffer
	OPSTR <DMOVE T1,>,UNBFA,(UN) ; Get the buffer address
	OPSTR <DMOVEM T1,>,RDVBA,(CM) ; Save given address in return word
	LOAD T1,UNRID,(UN)	; Get request ID
	STOR T1,RDRID,(CM)	; Save that too

	LOAD T1,PTVIR,(P1)	; Get virtual address of q-interlock
	CALL PUTQUE		; Put it on the queue
	 NOP			;  It was empty
	INCR PRORC,(PR)		; Increment the number of outstanding receives
	RETSKP			; Give good return
	SUBTTL Read/Clear Performance Counters

;+
; THIS ROUTINE IS CALLED TO QUEUE A READ/READ CLEAR COUNTERS COMMAND.
;
; RETURNS +1: ERROR, CODE IN T1
;	  +2: COMMAND QUEUED
;
;-

NIRPC:	LOAD T1,UNBSZ,(UN)	; Get length of buffer
	MOVX T2,PC.LEN		; Get minimum buffer length
	STOR T2,UNBSZ,(UN)	; Save it in case of error return
	JUMPLE T1,ERET UNIBS%	; Jump if it's negative
	CAXGE T1,PC.LEN		; Is it big enough ??
	 ERROR UNIBS%		;  Nope, Illegal buffer size
	LOAD T1,UNSPI,(UN)	; Get source portal ID
	LOAD PS,PRCHN,(T1)	; Get channel associated with that portal
	JUMPLE PS,NIDSP1	; BUGHLT if channel ptr .LE. 0
	OPSTR <CAME PS,>,PSCHK,(PS) ; Is the check word Kosher?
	 JRST NIDSP1		;  Nope, BUGHLT
	MOVX T1,NF.RPC		; Indicate function is read portal counters
	CALLRET RDCTR		; Read the actual counters

NIRDC:	LOAD T1,UNCHN,(UN)	; Get the channel #
	CALL FNDCHN		; Look for it's status block
	 ERROR UNNSC%		;  Error - No Such Channel
	MOVE PS,T1		; Setup the pointer
	LOAD T1,UNBSZ,(UN)	; Get length of buffer
	MOVX T2,CC.LEN		; Get minimum buffer length
	STOR T2,UNBSZ,(UN)	; Save it in case of error return
	JUMPLE T1,ERET UNIBS%	; Jump if it's negative
	CAXGE T1,CC.LEN		; Is it big enough ??
	 ERROR UNIBS%		;  Nope, Illegal buffer size
	MOVX T1,NF.RDC		; Indicate function is read channel counters
;	CALLRET RDCTR		; Read the actual counters

RDCTR:	MOVE CM,T1		; Save invoking command for a moment
	MOVX T1,C1.LST		; Get length of read counters command
	CALL GETCOR		; Allocate the command block
	 ERROR UNRES%		;  No resources
	EXCH CM,T1		; CM <= command block addr, T1 <= function code
	STOR T1,C1FNC,(CM)	; Save the function code
	LOAD T1,UNBFA,(UN)	; Get user's buffer address
	STOR T1,C1BFA,(CM)	; Save it in the command block
	STOR PR,C1PID,(CM)	; Save the portal ID for the callback
	LOAD T1,UNRID,(UN)	; Get request ID
	STOR T1,C1RID,(CM)	; Save it for later
	LOAD T1,UNSPI,(UN)	; Get secondary portal ID
	STOR T1,C1SPI,(CM)	; Save it for later
	MOVEI T1,OP.RCC		; Opcode
	STOR T1,CMOPC,(CM)	; Save the opcode
	LOAD T1,UNZRO,(UN)	; Get the "zero counters" bit
	STOR T1,C1ZRO,(CM)	; Indicate we are zeroing the counters
	SETONE CMCLR,(CM)	; Always clear the port counters
	CALL QUECMD		; Queue the command
	RETSKP			; Give good return
	SUBTTL Read/Read Clear Counters Response

RCRES:	XMOVEI T1,RCBADR	; Get hardware counter block address
	LOAD T2,PSSHC,(PS)	; Get address of shadow counter block
	MOVX T3,RC.LEN		; Get length of counter block

RCRES2:	MOVE T4,(T1)		; Get an entry from the hardware block
	ADDM T4,(T2)		; Add it to the shadow block
	XMOVEI T1,1(T1)		; Increment the source pointer
	XMOVEI T2,1(T2)		; And the destination pointer
	SOJG T3,RCRES2		; Loop through the whole block

; Now correct for transmit and receive failure bit masks

	XMOVEI T1,RCBADR	; Get address of read counters block
	LOAD T2,PSSHC,(PS)	; Get address of shadow block
	LOAD T3,RCXFM,(T2)	; Get transmit failure mask from shadow
	OPSTR <SUB T3,>,RCXFM,(T1) ; Correct for the addition we just performed
	OPSTR <IOR T3,>,RCXFM,(T1); And OR in the bit mask to make right result
	STOR T3,RCXFM,(T2)	; Save the result in the shadow block

	LOAD T3,RCRFM,(T2)	; Get receive failure mask from shadow
	OPSTR <SUB T3,>,RCRFM,(T1) ; Correct for the addition we just performed
	OPSTR <IOR T3,>,RCRFM,(T1); And OR in the bit mask to make right result
	STOR T3,RCRFM,(T2)	; Save the result in the shadow block

	OPSTR <SKIPN UN,>,PSINT,(PS) ; Get control buffer
	 JRST NOCTL		; None ??
	LOAD PR,C1PID,(CM)	; Get Portal ID
	LOAD T1,C1BFA,(CM)	; Get users buffer address
	STOR T1,UNBFA,(UN)	; Save buffer address
	LOAD T1,C1RID,(CM)	; Get the request ID
	STOR T1,UNRID,(UN)	; Save that too

	LOAD T1,C1FNC,(CM)	; Get the function code
	CAXN T1,NF.RDC		; Is it "read channel counters"
	 JRST RCRCC		;  Yes, process channel counters
	CAXN T1,NF.RPC		; Read portal counters?
	 JRST RCRPC		;  Yes, go to it
	BUG. (HLT,KNIICF,PHYKNI,SOFT,<PHYKNI - Illegal read counters function>,,<

Cause:	The read counters callback routine detected an illegal function code
	in the field C1FNC of the command block.
>)
	SUBTTL Complete Reading Of Channel Counters

	BEGSTR CT		; Define structure counter descriptors
	 FIELD SRC,9		; Source (RC block) offset
	 FIELD DST,9		; Destination block offset
	 HWORD CTR		; NM counter number
	ENDSTR

RCRCC:	SAVEAC P1

	MOVX T1,CC.LEN		; Get length of CC block
	STOR T1,UNBSZ,(UN)	; Save if for the user

; Now copy all data out of the main "read counters block" into the user's
; block.

	LOAD T1,PSTLZ,(PS)	; Get last time at which counters were zeroed
	CALL CMPSLZ		; Compute seconds since last zeroed

	LOAD T3,C1BFA,(CM)	; Get buffer address
	STOR T1,CCSLZ,(T3)	; Save seconds since last zeroed

	XMOVEI P1,CCTAB		; Get address of channel counters table
	TXO T3,<<T1>>B5		; Make an EFIW indexed by T1
	MOVX T4,CCTLEN		; Setup length of CC table

CCCOPY:	LOAD T1,CTSRC,(P1)	; Get offset of source counter
	OPSTR <ADD T1,>,PSSHC,(PS) ; Add in base of shadow counters
	MOVE T2,(T1)		; Fetch the counter
	LOAD T1,CTDST,(P1)	; Get offset of dest counter
	MOVEM T2,@T3		; Store the counter in the user's block
	LOADE T2,CTCTR,(P1)	; Get NM counter number
	JUMPL T2,CCCOP1		; Don't install counter # if negative
	SUBI T1,1		; Point to previous word
	MOVEM T2,@T3		; And stuff counter number into user's block
CCCOP1:	XMOVEI P1,1(P1)		; Increment the CCTAB pointer
	SOJG T4,CCCOPY		; Loop over the whole CC table

; Now correct the Bytes Received counter, and the Receive Failures counters
; to account for CRC's and port craziness.

	LOAD T1,C1BFA,(CM)	; Get user's buffer address
	LOAD T2,CCBYR,(T1)	; Get Bytes Received counter
	LOAD T3,CCDGR,(T1)	; Get the number of datagrams received
	LSH T3,2		; Multiply that by 4
	SUB T2,T3		; Sub 4 bytes (CRC length) for 
				;  each received datagram
	STOR T2,CCBYR,(T1)	; Save it
	LOAD T2,CCRF,(T1)	; Get the Receive Failures counter
	OPSTR <SUB T2,>,PSMXT,(PS) ; Subtract the # multicasts's we xmitted
	STOR T2,CCRF,(T1)	; Save it
	SKIPN T2		; Do we now have 0 Receive Failures?
	 SETZRO CCRFM,(T1)	;  Yes, the mask should be zero

	JE C1ZRO,(CM),RCRCC1	; Skip this if not zeroing counters
	CALL ZERSHD		; Zero the shadow counters

RCRCC1:	MOVEI T1,NF.RDC		; Function code
	LOAD T3,CMERC,(CM)	; Get the error code
	CALL DLLCBK		; Pass it upwards
	MOVE T1,CM		; Get address of command block
	CALLRET RELCOR		; Release it

; Reset seconds since last zeroed, and the shadow counters

ZERSHD:	MOVE T1,TODCLK		; Get current time
	STOR T1,PSTLZ,(PS)	; Indicate counters were just zeroed
	MOVX T1,RC.LEN		; Number of words to clear
	LOAD T2,PSSHC,(PS)	; Start at beginning of shadow counters block
	SETZM (T2)		; Clear first word of shadow counter block
	XMOVEI T3,1(T2)		; Get address +1
	EXTEND T1,[XBLT]	; Zapp the entire block
	SETZRO PSMXT,(PS)	; Reset the # of multicasts xmitted
	RET

	DEFINE XX (source,dest,ctr)<
	BYTE (9) RC.'source,CC.'dest (18) ctr
	>

CCTAB:	XX BR,BYR,0		; Bytes received
	XX BX,BYS,0		; Bytes sent
	XX FR,DGR,0		; Datagrams received
	XX FX,DGS,0		; Datagrams sent
	XX MCB,MBR,0		; Multicast bytes received
	XX MCF,MDR,0		; Multicast datagrams received
	XX FXD,DSD,0		; Datagrams sent, initially deferred
	XX FXS,DS1,0		; Datagrams sent, single collision
	XX FXM,DSM,0		; Datagrams sent, multiple collisions
	XX XF,SF,0		; Send failures
	XX XFM,SFM,-1		; Send failure bit mask
	XX RF,RF,0		; Receive failure
	XX RFM,RFM,-1		; Receive failure bit mask
	XX UFD,UFD,0		; Unrecognised frame destination
	XX DOV,DOV,0		; Data overrun
	XX SBU,SBU,0		; System buffer unavailable
	XX UBU,UBU,0		; User buffer unavailable
	CCTLEN==.-CCTAB
	SUBTTL Complete Reading Of Portal Counters

RCRPC:	SAVEAC P1

	LOAD P1,C1SPI,(CM)	; Get secondary portal ID
	STOR P1,UNSPI,(UN)	; Save it for the user
	LOAD T1,PRTLZ,(P1)	; Get time at which counters were zeroed
	CALL CMPSLZ		; Convert it to seconds since last zeroed

	LOAD T3,C1BFA,(CM)	; Get buffer address
	STOR T1,PCSLZ,(T3)	; Save seconds since last zeroed

	MOVX T1,PC.LEN		; Get length of CC block
	STOR T1,UNBSZ,(UN)	; Save if for the user

	LOAD T1,PRBYR,(P1)	; Bytes received
	STOR T1,PCBYR,(T3)
	LOAD T1,PRDGR,(P1)	; Datagrams received
	STOR T1,PCDGR,(T3)
	LOAD T1,PRBYS,(P1)	; Bytes sent
	STOR T1,PCBYS,(T3)
	LOAD T1,PRDGS,(P1)	; Datagrams sent
	STOR T1,PCDGS,(T3)

; Fetch the lost buffers count from the appropriate place in the read counters
; block.  **** Note that this code may not work for unknown protocol queue****

	LOAD T1,PRFQA,(P1)	; Get free queue address
	LOAD T2,PSPTT,(PS)	; Get start of PTT for this channel
	SUB T1,T2		; Compute offset from start of PTT
	IDIVI T1,PT.LEN		; Compute offset into read counters block
	OPSTR <ADD T1,>,PSSHC,(PS) ; Add start of shadow counters table
	LOAD T2,RCD01,(T1)	; Fetch the lost buffer count
	STOR T2,PCUBU,(T3)	; User buffer unavailable

	JE C1ZRO,(CM),RCRPC2	; Skip this if not zeroing counters
	MOVE T2,TODCLK		; Get current time
	STOR T2,PRTLZ,(P1)	; Indicate that we just zeroed the counters
	SETZRO RCD01,(T1)	; Reset the lost buffer count
	SETZRO PRBYR,(P1)	; Clear bytes received
	SETZRO PRDGR,(P1)	; Clear datagrams received
	SETZRO PRBYS,(P1)	; Clear bytes sent
	SETZRO PRDGS,(P1)	; Clear datagrams sent

RCRPC2:	MOVX T1,NF.RPC		; Tell the user that
	LOAD T3,CMERC,(CM)	; Get the error code
	CALL DLLCBK		;  we are done reading his counters
	MOVE T1,CM		; Get command block address
	CALLRET RELCOR		; Release the memory
	SUBTTL CMPSLZ - Compute Seconds Since Last Zeroed

; Call:	MOVX T1,TODCLK time at which counters were zeroed
;	CALL CMPSLZ
;	<Return +1 Always T1/ Seconds since last zeroed>

CMPSLZ:	SUB T1,TODCLK		; T1 <= -amount of time since last zeroing
	SUBI T1,^D500		; Add 1/2 a second for rounding
	IDIV T1,[-^D1000]	; Convert it to positive seconds
	RET			; And return it to the user
	SUBTTL NISCS - Set Channel State

NISCS:	LOAD T1,UNCHN,(UN)	; Get channel #
	CALL FNDCHN		; Look it up
	 ERROR UNNSC%		;  Error - No such channel
	MOVE PS,T1		; Make it official

	LOAD T1,UNSST,(UN)	; Get the new state
	LOAD T2,PSSST,(PS)	; Get the current state
	CAMN T1,T2		; Is the state changing?
	 RETSKP			;  No, we're all done

	CAXL T1,UNS.VG		; Is it too small?
	 CAXLE T1,UNS.MX	;  Or too big?
	  ERROR UNICS%		;   Yes to either, bomb him out
	JRST @.+1-UNS.VG(T1)	; Dispatch to the right routine

	TABBEG UNS.VG,UNS.MX,FOO
	  TABENT UNS.VG,<IFIW ERET UNICS%> ; Virgin - Can't set to virgin state
	  TABENT UNS.RE,<IFIW NISCS1>	   ; Reload - Reload the KLNI microcode
	  TABENT UNS.CR,<IFIW ERET UNICS%> ; Can't Reload - Can't set this
	  TABENT UNS.IN,<IFIW ERET UNICS%> ; Init - Can't set to INIT state
	  TABENT UNS.RN,<IFIW NIINIA>	   ; Run - Startup the KLNI
	  TABENT UNS.DP,<IFIW NISCS1>	   ; Dump - Just dump the KLNI
	  TABENT UNS.DR,<IFIW NISCS1>	   ; Dump & Reload - Dump the KLNI and restart it
	  TABENT UNS.BK,<IFIW ERET UNICS%> ; Broken - KLNI cannot be initialized
	  TABENT UNS.OF,<IFIW NISCS1>	   ; Off - Turn off the KLNI
	  TABENT UNS.RR,<IFIW NISCS2>	   ; Request Reload - Make KNILDR run
	TABEND

; Here to set the state to anything except RUN.  Call with T1/ new state

NISCS1:	SAVEAC P1
	MOVE P1,T1		; Save the new state in a safe place
	NIOFF
	CALL NISTP		; Stop the port
	MOVE T1,P1		; Get the new state back
	CALL SETSTA		; Tell everybody about the new state
	NION
	RETSKP			; And return success

; Here to force KNILDR to reload a KLNI.

NISCS2:	NIOFF			; Test and change of state must be interlocked
	CAXE T2,UNS.RN		; Are we running?
	IFSKP.
	  NION			;   Enable interrupts
	  ERROR UNICS%		;   Yes, don't allow this
	ENDIF.
	CALL STRLD		; Make CHKR run KNILDR
	NION
	RETSKP			; All done!
	SUBTTL NIRPI - Read Portal Information

NIRPI:	LOAD T1,PSCHN,(PS)	; Get the logical channel number
	STOR T1,UNCHN,(UN)	; Return the channel #

	OPSTR <SKIPG T1,>,UNBSZ,(UN) ; Does he want the multicasts?
	 RETSKP			;  No, return now

	SAVEAC <P1,P2,P3>
	LOAD P1,PSMTT,(PS)	; Get the multicast table base
	LOAD P2,UNBFA,(UN)	; Get the buffer address
	SETZM (P2)		; Zero out count word
	SUBI T1,1		; Account for header word
	ADDI P2,1		; Skip header word

	LOAD T2,PRMUL,(PR)	; Get the multicast bit mask
	SETZ P3,		; Reset total # of multicasts

NIRPI1:	JUMPE T2,NIRPI2		; Jump if no more to do
	JFFO T2,.+1		; Look for an address
	ADDI P3,1		; Increment number of multicasts
	LSH T2,2(T3)		; Remove the entry we just acquired
	ADD P1,T3		; Point to the MTT entry for this bit

	SUBI T1,MT.LEN		; Account for this entry
	JUMPL T1,NIRPI1		; Don't store multicast if no room in buffer

	OPSTR <DMOVE T3,>,MTHAD,(P1) ; Get the address
	ADDI P1,MT.LEN		; Point to next address
	TXZ T3,17		; Clear low 4 bits of high order address
	AND T4,[777774,,0]	; Clear all bit top 16 bits of low order
	DMOVEM T3,(P2)		; Store the address
	ADDI P2,MT.LEN		; Update the buffer pointer
	LOAD T3,UNBFA,(UN)	; Get the buffer address
	HRLM P3,(T3)		; Update the number of addresses returned
	JRST NIRPI1		; And do it again

NIRPI2:	LOAD T3,UNBFA,(UN)	; Get the buffer address again
	HRRM P3,(T3)		; Update the total multicast count
	RETSKP			; And return success
	SUBTTL QUECMD - Queue A Command

;+
; THIS ROUTINE WILL PUT A COMMAND ON THE COMMAND QUEUE AND NOTIFY THE
; KLIPA IF NEED BE.
;
;	CALL QUECMD
;
;	Q2/ ADDRESS OF QUEUE ENTRY
;	P2/ PRTTBL ADDRESS
;
; RETURNS +1: ALWAYS
;-

QUECMD:	SETONE CMRSP,(CM)	;MAKE SURE THE RESPONSE BIT IS SET
	LOAD T1,PSPCB,(PS)	;GET PCB BASE ADDRESS
;	XMOVEI T1,PB.CQI(T1)	;GET THE ADDRESS OF THE INTERLOCK WORD
IFN FTHIST,<
	CALL RECCMB		; Record the CM block
>; End of FTHIST
	CALL PUTQUE		;PUT IT ON THE QUEUE
	 NOP			; IGNORE SKIP RETURN
	OPSTR <XCT>,PSCQA,(PS)	;TELL KLIPA THAT COMMAND QUEUE IS AVAILABLE
	RET
	SUBTTL FNDPT - Find The PTT Entry For A Protocol Type

; This routine will find the PTT entry for a given protocol type.  If that
; protocol type is not found, it will return the address of the first free
; PTT slot.
;
; Call:	MOVX T1,protocol_type
;	CALL FNDPT
;	 <Return +1, protocol type not found,
;		     T1/ "fixed" protocol type,
;		     T2/ addr of 1st free entry or 0>
;	<Return +2, success T1/ "fixed" protocol type,
;			    T2/ address of PTT entry>

FNDPT:	CAME T1,[-2]		; Promiscuous?
	 CAMN T1,[-3]		;  Or Unknown?
	IFNSK.			; Yes to either
	  OPSTR <XMOVEI T2,>,PSUNK,(PS) ;   Get address of pseudo PTT
	  JE PTFRE,(T2),RSKP	;   Skip return if it's in use
	  RET			;   Otherwise, give error & return address
	ENDIF.

	CALL SWAB		; Swap the bytes
	MOVEI T3,NPTT		; Number of protocol types
	SETZ T4,		; No free slots yet
	OPSTR <SKIPA T2,>,PSPTT,(PS) ; Get the base address of the PTT
FNDPT2:  ADDI T2,PT.LEN		;  Advance to the next entry
	TMNN PTFRE,(T2)		; Skip if free
	IFSKP.
	  JUMPN T4,FNDPT1	; Skip this if we have an entry already
	  MOVE T4,T2		; Remember this address
	  JRST FNDPT1		; Go on to next PTT slot
	ENDIF.
	OPSTR <CAMN T1,>,PTTYP,(T2) ; This the one ?
	 RETSKP			;  Yes, skip return
FNDPT1:	SOJG T3,FNDPT2		; Back to the top
	MOVE T2,T4		; Get address of 1st free PTT slot
	RET			; None found
	SUBTTL MSGAVA - Response Message Available

;+
; THIS ROUTINE IS DISPATCHED TO THROUGH THE COMMAND DISPATCH TABLE
; WITH
;	Q2 = ENTRY ADDRESS
;	P2 = PRTTBL ADDRESS
;-

MSGAVA:	OPSTR <SKIPN UN,>,PSINT,(PS) ; Get control buffer for interrupt level
	 JRST NOCTL		; None ??
	LOAD PR,RDPID,(CM)	; Get portal ID

	OPSTR <DMOVE T1,>,RDDA1,(CM) ; Get high order part of address

	TXNN T1,1B7		; Is it a multicast address?
	 JRST MSGAV2		;  No, proceed normally

; We have a multicast frame.  Determine if this portal is enabled for this
; multicast address.

	TXO T2,MTENA		; Set enable bit so we get a match in the MTT

	SAVEAC P1		; Get a perm ac
	LOAD P1,PSMTT,(PS)	; Get address of MCAT
	LOAD T3,PRMUL,(PR)	; Get the multicast bit map
	JRST MULAV1		; Start the search

MULAVA:	LSH T3,2(T4)		; Remove leading 0's + 2 bits because each
				;  entry is two bits long
	ADD P1,T4		; Point to appropriate MCAT entry
	OPSTR <CAME T1,>,MTHAD,(P1) ; Does the high order match?
	 JRST MULAV2		;  Nope, try again
	OPSTR <CAMN T2,>,MTLAD,(P1) ; Does the low order address match?
	 JRST MSGAV2		;  Yes, accept the datagram
MULAV2:	ADDI P1,MT.LEN		; Skip over the entry we just checked
MULAV1:	JFFO T3,MULAVA		; Jump if there are some enabled addresses

; See if this is a promiscuous or unknown protocol type portal.  If so,
; pass on the datagram.

	LOADE T3,PRPRO,(PR)	; Get the protocol type
	JUMPL T3,MSGAV2		; Jump if it's promiscuous or unknown

;Here when a multicast is received for a portal that doesn't have it enabled

	SKIPN NIBUGX		; Don't print these if not interested
	IFSKP.
	  SAVEAC P2		; Need yet another AC

	  OPSTR <DMOVE P1,>,RDDA1,(CM) ; Get destination Ethernet address

	  OPSTR <DMOVE T3,>,RDSA1,(CM) ; Get source Ethernet address

	  LOAD T1,RDPTY,(CM)	; Get protocol type
	  CALL SWAB		; Normalize it

	  BUG. (INF,KNIDMD,NISRV,SOFT,<Portal not enabled for this multicast>,<<P1,HIDST>,<P2,LODST>,<T3,HISRC>,<T4,LOSRC>>,<

Cause:  A portal received a multicast frame on an address it wasn't enabled
	for.  The frame will be discarded, and the buffer will be re-used.

Data:	HIDST - High order destination address
	LODST - Low order destination address
	HISRC - High order source address
	LOSRC - Low order source address
>)
	  BUG. (INF,KNIDM1,NISRV,SOFT,<KNIDMD continued>,<<T1,PROTO>>,<

Cause:	Additional data for KNIDMD.

Data:	PROTO - Protocol type
>,,<DB%NND>)			;[7.1210] 
	ENDIF.

	LOAD T1,PRFQA,(PR)	; Get PTT entry address
	LOAD T1,PTVIR,(T1)	; Get virt address of free queue header
	CALLRET PUTQUE		; Put this buffer back on the free list

MSGAV2:	LOAD T1,RDRID,(CM)	; Get request ID
	STOR T1,UNRID,(UN)	; Set request ID

	OPSTR <DMOVE T1,>,RDVBA,(CM) ; Get the buffer pointer
	OPSTR <DMOVEM T1,>,UNBFA,(UN) ; Save it

	LOAD T1,RDPTY,(CM)	; Protocol type
	CALL SWAB		; Swap the bytes so humans can stand it
	STOR T1,UNPRO,(UN)	; Save it

	OPSTR <DMOVE T1,>,RDDA1,(CM) ; Get destination Ethernet address
	OPSTR <DMOVEM T1,>,UNDAD,(UN) ; Store the dest address

	OPSTR <DMOVE T1,>,RDSA1,(CM) ; Get source Ethernet address
	OPSTR <DMOVEM T1,>,UNSAD,(UN) ; Store it

	LOAD T1,RDSIZ,(CM)	; Get size
	SUBI T1,4		; Subtract length of CRC
	OPSTR <ADDM T1,>,PRBYR,(PR) ; Keep track of bytes received
	INCR PRDGR,(PR)		;  and datagrams received

	JE PRPAD,(PR),MSGAV4	; Jump if padding is not enabled

	OPSTR SKIPE,CMERC,(CM)	; Did we get an error?
	 JRST MSGAV4		;  Yes, just return it to the user

	LOAD T2,BDSBA,+RD.LST(CM) ; Get physical buffer address
	PMOVE T2,T2		;[7.1002] Get 1st word of buffer
	LSH T2,-^D20		; Right justify the data length field
	EXCH T1,T2		; T1 <= data length field, T2 <= frame length
	CALL SWAB		; Make data length field useable
	MOVX T3,UNLER%		; Prepare for a length error
	CAMLE T1,T2		; Data length field > frame length?
	 JRST MSGAV1		;  Yes, not Kosher
	OPSTR <IBP>,UNBFA,(UN)	; Increment the user's pointer
	OPSTR <IBP>,UNBFA,(UN)	;  twice (to skip the data length field).

MSGAV4:	LOAD T3,CMERC,(CM)	; Get the error code
MSGAV1:	STOR T1,UNBSZ,(UN)	; Save the data length
	DECR PRORC,(PR)		; Decrement the number of outstanding rcvs
	MOVEI T1,NF.RCV		; Data recieved
	CALL DLLCBK		; Call the portal back
	MOVE T1,CM		; Get the entry address
	CALL RXRCOR		; And return it
	RETSKP
	SUBTTL DGSNT - Response to Datagram Sent

;+
; THIS ROUTINE IS DISPATCHED TO THROUGH THE COMMAND DISPATCH TABLE
; WITH
;	Q2 = ENTRY ADDRESS
;	P2 = PRTTBL ADDRESS
;-

DGSNT:	OPSTR <SKIPN UN,>,PSINT,(PS) ; Get interrupt level control buffer
	 JRST NOCTL		; None
	JE CMBSD,(CM),DGSNT5	; Jump if non-BSD
	LOAD PR,SBPID,(CM)	; Get the portal ID
	LOAD T1,SBRID,(CM)	; Get request ID
	STOR T1,UNRID,(UN)	; Make it available to the user
	OPSTR <SKIPN T1,>,SBMSD,(CM) ; Get MSD base address
	 OPSTR <DMOVE T1,>,SBBFA,(CM) ; If not MSD, get buffer address
	OPSTR <DMOVEM T1,>,UNBFA,(UN) ; Save it
	LOAD T1,SNTXL,(CM)	; Get the length of the data
	STOR T1,UNBSZ,(UN)	; Stuff it
	OPSTR <ADDM T1,>,PRBYS,(PR) ; Keep track of bytes transmitted
	INCR PRDGS,(PR)		;  and datagrams transmitted
	DECR PROXM,(PR)		; Decrement the outstanding xmit count

	MOVEI T1,NF.XMT		; Get the function code
	LOAD T3,CMERC,(CM)	; Get the error code
	CALL DLLCBK		; Send to NISRV
	MOVE T1,CM		; Get address of queue entry
	CALL RXRCOR		; Return it
	RETSKP			; Give good return

;NON-BSD SENT

DGSNT5:	BUG. (HLT,KNINBS,PHYKNI,SOFT,<PHYKNI - Non-BSD datagram sent>,<<CM,BUFFER>>,<

Cause:	A NON-BSD style datagram was sent. The driver does not send this
	style.

Data:	BUFFER - Buffer address
>)				;+++debug+++
	RETSKP			;RETURN
	SUBTTL NIRSI - Read KLNI Station Information

; Read the KLNI's station info.  When the response happens do:
;
; 1) If the channel is in the initializing state:
;	a) Copy all variables from the response into the shadow area
;	b) Copy all variables that aren't valid from the response into the
;	   variables area.
;
; 2) If the channel is not in the initializing state, compare all the variables
;    in the response with those in the shadow area.  If they match, all is
;    well, otherwise shutdown the KLNI and complain.
;

NIRSI:	SETZ CM,		; First get a 0
	OPSTR <EXCH CM,>,PSRSI,(PS) ; Acquire command queue entry
	JUMPE CM,RTN		; Return if command is already active

	MOVEI T1,OP.RSI		; Get the opcode for this command
	STOR T1,CMOPC,(CM)	; Save it
	CALL QUECMD		; Queue the command
	RETSKP			; And give a skip return

; Read Station Information response

NSARD:	STOR CM,PSRSI,(PS)	; Give buffer back
	LOAD T1,RSVAR,(CM)	; Get the station varibles
	OPSTR <DMOVE T2,>,RSHAD,(CM) ; Get the current station address
	LOAD T4,PSSST,(PS)	; Get the current KLNI state
	CAXE T4,UNS.IN		; Is it initializing?
	IFSKP.

; Here upon completion of first KLNI command.  Save the ROM address, and
; setup the shadow variables.

	  OPSTR <DMOVEM T2,>,PSHRA,(PS) ; Save the ROM address
	  OPSTR <DMOVEM T2,>,PSSAD,(PS) ; Setup the shadow address
	  TMNN PSVAD,(PS)		; Has the Ethernet address been set?
	   OPSTR <DMOVEM T2,>,PSHAD,(PS) ;  No, put it into the variables area

	  STOR T1,PSSVA,(PS)	; Save shadow variables
	  LOAD T2,PSVBT,(PS)	; Get the mask of valid bits
	  LOAD T3,PSVAR,(PS)	; Get the bits that the user explicitly set
	  TDZ T1,T2		; Zero all the KLNI bits that we have set
	  AND T3,T2		; Zero all the bits the user hasn't set
	  OR T1,T3		; Combine the KLNI var with the user's vars
	  STOR T1,PSVAR,(PS)	; Save the resultant variables

	  CALL NIWSI		; Update the new set of variables

	  MOVX T1,UNS.RN	; Port is now running
	  CALL SETSTA		; Tell everyone about it
	ELSE.

; Here when KLNI is not initializing.  Compare returned values with expected
; values in the shadow variables.

	  OPSTR <CAME T1,>,PSSVA,(PS) ; Does the port and my database match?
	   JRST BADVAR		;  Nope, complain and shut down the port
	  OPSTR <CAMN T2,>,PSSAD,(PS) ; Does the high order address match?
	  OPSTR <CAME T3,>,PSSAD,+1(PS) ; And does the low order address match?
	   JRST BADADR		;  Nope, complain and shut down the port
	ENDIF.

	RETSKP			; Good return

BADVAR:	LOAD T2,PSVAR,(PS)	; Get the monitor's copy of the variables.

	BUG. (CHK,KNIVAR,PHYKNI,SOFT,<Monitor variables do not match KLNI variables>,<<T1,KLNI>,<T2,MON>>,<

Cause:	PHYKNI just read some status variables from the KLNI and found them 
	different from the shadow copies stored in the monitor.

Action:	The port will be shutdown.

Data:	KLNI - KLNI's version of the variables
	MON - Monitor's version of the variables
>)
BADVA1:	CALL NISTP		; Stop the KLNI
	CALL STDUMP		; Take a dump of the KLNI
	STOR CM,PSRSI,(PS)	; Give buffer back
	RET

BADADR:	LOAD T3,PSHAD,(PS)	; Get the monitor's copy of the address
	LOAD T4,PSLAD,(PS)	; "
	BUG. (CHK,KNIADR,PHYKNI,SOFT,<Monitor address does not match KLNI address>,<<T1,KLNHIO>,<T2,KLNLO>,<T3,MONHIO>,<T4,MONLO>>,<

Cause:	PHYKNI just read the Ethernet address from the KLNI and found it 
	different from the shadow copy stored in the monitor.

Action:	The port will be shutdown.

Data:	KLNHIO & KLNLO - KLNI's copy of the Ethernet address
	MONHIO & MONLO - Monitor's copy of the Ethernet address
>,,<DB%NND>)			;[7.1210] 
	JRST BADVA1		; Shutdown the port
	SUBTTL NIRCI - Read Channel Information

NIRCI:	LOAD T1,UNCHN,(UN)	; Get the channel # user supplied us
	CALL FNDCHN		; Lookup the channel block
	 ERROR UNNSC%		;  Error - No Such Channel
	MOVE PS,T1		; Put pointer into proper AC

	OPSTR <DMOVE T1,>,PSHRA,(PS) ; Get high order ROM address
	OPSTR <DMOVEM T1,>,UNHAD,(UN) ; Give it to caller

	OPSTR <DMOVE T1,>,PSHAD,(PS) ; Get current high order address
	OPSTR <DMOVEM T1,>,UNCAR,(UN) ; Return high order address

	LOAD T1,PSSTA,(PS)	; Get the channel status
	STOR T1,UNSTA,(UN)	; Make that the save it
	RETSKP			; And return success
	SUBTTL NICLO - Flush All Commands From A Portal

NICLO:	CALL ALLCHE		; Do this for all channels

	MOVX T1,CM.LEN+FL.LEN	; Just get a small command block
	CALL GETCOR		; Allocate the memory
	 ERROR UNRES%		;  No memory???
	MOVE CM,T1		; Put pointer in special AC
	MOVX T1,OP.FLS		; Get the flush opcode
	STOR T1,CMOPC,(CM)	; Install it
	MOVX T1,'FLUSH'		; Get special keyword
	STOR T1,FLCHK,(CM)	; Install it in the check word
	STOR PR,FLPID,(CM)	; Install the PID
	CALL QUECMD		; Queue it up

; See if the channel is running.  If not, we have to manually search through
; the command queue, returning all commands queued up by this portal.

	NIOFF			; No interrupts please
	LOAD T1,PSSST,(PS)	; Get the internal state
	CAXN T1,UNS.RN		; Is the KLNI running?
	 JRST NICLO1		;  Yes, we're all done

	SAVEAC P1
NICLO3:	LOAD T1,PSPCB,(PS)	; Get address of command queue
	CALL REMQUE		; Get an item off the queue
	 JRST NICLO1		;  Error, queue should never be empty

	LOAD P1,CMOPC,(CM)	; Get the opcode
	XCT PIDFET(P1)		; Fetch the portal ID
	CAME PR,T1		; Is this the desired portal?
	 JRST NICLO2		;  Nope, requeue the command

	MOVX T1,UNCAB%		; Get the command abort error code
	STOR T1,CMERC,(CM)	; Put the error code into the command block
	CALL @CMDDSP(P1)	; Process the command
	 NOP			;  In case of skip returns
	CAXN P1,OP.FLS		; Did we just process a close?
	 JRST NICLO1		;  Yes, we're all done
	JRST NICLO3		; Try again

NICLO2:	CALL QUECMD		; Requeue the command
	JRST NICLO3		; And try again

NICLO1:	NION			; Allow interrupts
	RETSKP			; And return

; Table of instructions used for fetching portal IDs from various commands.

PIDFET:	TABBEG OP.FLS,OP.WSI,<FOO>
	  TABENT OP.FLS,<LOAD T1,FLPID,(CM)>	; Close command
	  TABENT OP.SND,<LOAD T1,SBPID,(CM)>	; Xmit command
	  TABENT OP.LDM,<SETZ T1,>		; Load multicast command
	  TABENT OP.LDP,<SETZ T1,>		; Load PTT command
	  TABENT OP.RCC,<LOAD T1,C1PID,(CM)>	; Read counters command
	  TABENT OP.RCV,<CALL UNKRES>		; Receive command
	  TABENT OP.WPL,<CALL UNKRES>		; Write PLI command
	  TABENT OP.RPL,<CALL UNKRES>		; Read PLI command
	  TABENT OP.RSI,<SETZ T1,>		; Read station info command
	  TABENT OP.WSI,<SETZ T1,>		; Write station info command
	TABEND
	SUBTTL CLOSED - Command Flush Completion

CLOSED:	LOAD T1,CMERC,(CM)	; Get the error code
	CAXE T1,URCCOD		; Is it the magic code?
	 CAXN T1,UNCAB%		;  Or a command abort?
	  TRNA			;   Yes to either, alls fine
	JRST BADFLS		; Nope, die
	OPSTR <SKIPN UN,>,PSINT,(PS) ; Get interrupt level control buffer
	 JRST NOCTL		; None
	LOAD T1,FLCHK,(CM)	; Get check word
	CAXE T1,'FLUSH'		; Is it the magic value?
	 JRST BADFLS		;  Nope, complain about it
	SETZRO FLCHK,(CM)	; Yes, zapp it
	LOAD PR,FLPID,(CM)	; Setup portal block pointer
	MOVX T1,NF.CLO		; Indicate that all commands are flushed
	CALL DLLCBK		; And inform the user
	MOVE T1,CM		; Get address of command block
	CALLRET RELCOR		; Release the command block

BADFLS:	BUG. (HLT,KNIIRC,PHYKNI,SOFT,<Illegal status on close>,<<T1,STATUS>>,<

Cause:	The status field contained an unexpected value upon return from the
	close function.

Data:	STATUS - Status
>)
	SUBTTL NISCA - Set Channel Address

NISCA:	LOAD T1,UNCHN,(UN)	; Get channel number
	CALL FNDCHN		; Look foor the channel block
	 ERROR UNNSC%		;  Error - No Such Channel
	MOVE PS,T1		; Setup channel block pointer

	CALL FETADR		; Get address from UN block
	TXNE T1,1B7		; Is it a multicast?
	 ERROR UNICA%		;  Yes, Illegal channel address

	NIOFF			; No interrupts
	OPSTRM <DMOVEM T1,>,PSHAD,(PS) ; Store the new address
	SETONE PSVAD,(PS)	; Indicate that the address is valid
	NION			; Enable the interrupts

	OPSTRM <DMOVEM T1,>,UNCAR,(UN)  ; [7326]Save the address we loaded
					; [7326]for those who get the callback
	CALL NIWSI		; Write the new station info
	MOVX T1,NF.SCA		; Get the callback code
	CALL DLLCBK		; Tell everyone about the address change
	RETSKP			; Return success
	SUBTTL PMOVE - Physical Mover
Repeat 0,<	;[7.1002] Remove obsolete code

;SUBROUTINE TO GET THE CONTENTS OF A GIVEN PHYSICAL ADDRESS
;CALL:	T2/DESIRED PHYSICA ADDRESS
;	RETURNS +1, WITH T2=CONTENTS
;PRESERVES T1

PMOVE:	SAVEAC <T1>		;SAVE T1
	LOAD T1,PGWD,T2		;WORD NUMBER
	LOAD T2,PHCPNO,T2	;PHYSICAL PAGE NUMBER
	CALL MOVRCA		;GET THE CONTENTS
	MOVE T2,T1		;RETURNS THE ANSWER IN T2
	JRST PMOVER		;RESTORE T1 AND RETURN

;ROUTINE TO STORE INTO A GIVEN PHYSICAL ADDRESS
;CALL:	T3/CONTENTS TO BE STORED
;	T2/ADDRESS TO STORE IT
;RETURNS +1 ALWAYS
;PRESERVES T1

PMOVEM:	SAVEAC <T1>		;SAVE T1
	LOAD T1,PGWD,T2		;WORD NUMBER
	LOAD T2,PHCPNO,T2	;PAGE NUMBER
	CALL STORCA		;STORE T3 THERE
PMOVER:	RET			;AND RETURN TO CALLER
>	;[7.1002] End obsolete code
	SUBTTL PUTQUE - Put An Entry On The Queue

;SUBROUTINE TO INSERT A PACKET ONTO A QUEUE
;CALL WITH Q2=ADDRESS OF PACKET, T1= ADDR OF INTERLOCK WORD OF QUEUE 
;RETURNS TO CALL+1 IF QUEUE WAS EMPTY
;RETURNS TO CALL+2 IF QUEUE WAS NON-EMPTY BEFORE INSERTION
;
;DESTROTS T1,T2,T3

PUTQUE:	NIOFF			;IF WE INTERRUPT OWNING AN INTERLOCK
				; WE HANG THE KLIPA
PUTQU1:	AOSN T2,QH.IWD(T1)	;GET INTERLOCK
	JRST PUTQU2		;GOT IT
	CAXG T2,TIMOUT		;WAITED LONG ENOUGH?
	JRST PUTQU1		;NO, TRY AGAIN
	AOS TIMCNT		;YES. DO IT ANYWAY. BUMP COUNTER
PUTQU2:	MOVEM CM,QE.VIR(CM)	;SAVE VIRTUAL ADDR IN PACKET BUFFER
	MAP T3,QE.FLI(CM)	;PHYSICAL ADDR OF PACKET
	TXZ T3,ADRMSK		;CLEAR JUNK
	MAP T2,QH.FLI(T1)	;PHYSICAL ADDR OF QUEUE HEADER
	TXZ T2,ADRMSK		;CLEAR JUNK
	CAME T2,QH.FLI(T1)	;FLINK POINT AT ITSELF (EMPTY QUEUE)?
	JRST PUTQU3		;NO
	MOVEM T2,QE.FLI(CM)	;YES. POINT PACKET BACK AT QUEUE HEADER
	MOVEM T2,QE.BLI(CM)
	MOVEM T3,QH.BLI(T1)	;POINT PCB FLINK AND BLINK AT PACKET
	MOVEM T3,QH.FLI(T1)
QRET:	SETOM QH.IWD(T1)	;RELEASE INTERLOCK
	NION			;TURN INTERRUPTS BACK ON
	RET			;RETURN TO CALLER
PUTQU3:	MOVEM T2,QE.FLI(CM)	;POINT FLINK OF PACKET AT PCB
	MOVE T2,QH.BLI(T1)	;GET FORMER END OF QUEUE
	MOVEM T3,QH.BLI(T1)	;POINT QUEUE TAIL AT THIS PACKET
	MOVEM T2,QE.BLI(CM)	;POINT BLINK OF THIS PACKET AT FORMER END
	PMOVEM T3,T2		;[7.1002] Point BLINK of former end at this packet
QRETS:	SETOM QH.IWD(T1)	;RESET THE INTERLOCK
	NION			;TURN INTERRUPTS BACK ON
	RETSKP			;SKIP RETURN
	SUBTTL REMQUE - Remove An Entry From A Queue

;SUBROUTINE TO REMOVE FIRST PACKET FROM A QUEUE
;CALL WITH T1=ADDRESS OF INTERLOCK WORD OF QUEUE
;RETURNS WITH Q2=ADDRESS OF 1ST PACKET ON QUEUE (WHICH IS NOW UNQUEUED)
;RETURNS TO CALL+1 IF QUEUE IS EMPTY BEFORE REMOVAL
;RETURNS TO CALL+2 WITH T2=-1 IF QUEUE IS STILL NOT EMPTY, ELSE 0
;
;DESTROYS T1,T2,T3

REMQUE:	NIOFF			;NO INTERRUPTS
REMQU1:	AOSN T2,QH.IWD(T1)	;WAIT FOR INTERLOCK
	JRST REMQU2		;GOT IT
	CAXG T2,TIMOUT		;WAITED LONG ENOUGH?
	JRST REMQU1		;NO, TRY AGAIN
	AOS TIMCNT
REMQU2:	CALL CHKMPT		;IS QUEUE ALREADY EMPTY?
	 JRST QRET		;EMPTY
	MOVE T2,QH.FLI(T1)	;GET 1ST PACKET FROM QUEUE
	ADDI T2,QE.VIR		;POINT AT VIRTUAL ADDRESS OF PACKET
	PMOVE T2,T2		;[7.1002] Get it
	MOVE CM,T2		;VIRTUAL ADDR OF PACKET IN CM
	DMOVE T2,QE.FLI(CM)	;GET FLINK, BLINK OF THIS PACKET
	CAME T2,T3		;FLINK=BLINK?
	JRST REMQU3		;NO, QUEUE IS STILL NON-EMPTY
	DMOVEM T2,QH.FLI(T1)	;YES. QUEUE IS EMPTY. POINT HEADER AT ITSELF
	SETZ T2,		;SET INDICATOR
	JRST QRETS		;AND SKIP - RETURN
REMQU3:	MOVEM T2,QH.FLI(T1)	;POINT QUEUE HEADER AT NEXT PACKET
	ADDI T2,QE.BLI		;POINT AT FLINK OF NEXT PACKET
	PMOVEM T3,T2		;[7.1002] POINT ENXT PACKET BACK AT HEADER
	SETO T2,		;SET INDICATOR
	JRST QRETS		;SKIP RETURN


;ROUTINE TO CHECK IF A QUEUE IS EMPTY
;CALL WITH T1=ADDRESS OF INTERLOCK WORD OF QUEUE
;RETURNS WITH T1 UNCHANGED
;NON-SKIP RETURN IF QUEUE IS EMPTY
;SKIP RETURN IF QUEUE IS NON-EMPTY

CHKMPT:	MAP T2,QH.FLI(T1)	;GET PHYSICAL ADDRESS OF QUEUE HEADER
	TXZ T2,ADRMSK		;CLEAR JUNK
	CAMN T2,QH.FLI(T1)	;DOES QUEUE POINT AT ITSELF (EMPTY) ?
	RET			;YES, NON-SKIP
	RETSKP			;NO, SKIP
	SUBTTL GETCOR/RELCOR - Core Manager

;+
; THIS ROUTINE IS CALLED TO ALLOCATE WORDS FROM THE RESIDENT FREE SPACE
; POOL. IT RETURNS ALL ALLOCATIONS ON A FOUR WORD BOUNDARY. THE ALLOCATED
; ADDRESS IS SAVED IN THE FOUR WORD BOUNDARY ADDRESS -1. THE ENTIRE BLOCK
; IS ZEROED.
;
; CALL WITH:
;	T1/ NUMBER OF WORDS NEEDED
;
; RETURNS +1: ALLOCATION FAILURE
;	  +2: ALLOCATION MADE. T1=BUFFER ADDRESS
;-

GETCOR:	STKVAR <WORDS,ADDRESS>	;PLACE TO SAVE NUMBER OF WORDS AND ADDRESS
	MOVEM T1,WORDS		;SAVE NUMBER OF WORDS
	HRLI T1,.RESP1		;HIGHEST PRIORITY
	MOVEI T2,.RESGP		;FROM THE GENERAL POOL
	CALL ASGRES		;GET WORDS
	 RET			;GIVE ERROR RETURN
	MOVEM T1,ADDRESS	;SAVE THE ADDRESS
	MOVE T2,WORDS		;GET NUMBER OF WORDS BACK
	CALL CHKBUF		;MAKE SURE IT IS CONTIGUOUS PHYSICALLY
	IFNSK.
	  MOVE T1,WORDS		;GET NUMBER OF WORDS BACK
	  CALL GETCOR		;TRY AGAIN
	   RET			; TRIED TWICE, GIVE UP
	  EXCH T1,ADDRESS	;SWAP NEW ADDRESS WITH OLD ADDRESS
	  CALL RELCOR		;RELEASE THE OLD BLOCK
	ENDIF.

	MOVE T1,ADDRESS		;GET ADDRESS BACK
	RETSKP			;AND GIVE GOOD RETURN

	ENDSV.

;+
; ROUTINE TO RELEASE ALLOCATED FREE CORE. ALLOCATED ADDRESS STORED IN
; ADDRESS -1.
;
; CALL WITH:
;	T1/ BUFFER ADDRESS. ADDRESS ALLOCATED BY GETCOR.
;
; RETURNS +1: ALWAYS
;-

RELCOR:	CALLRET RELRES		;FREE THEM AND RETURN
	SUBTTL GXRCOR - Get Core For Transmits And Receives

GXRCOR:	NIOFF			; Disable interrupts
	SKIPN T2,XRCQUE		; Any command blocks available?
	 JRST GXRCO3		;  Nope, get more from ASGRES
	XMOVEI T3,XRCQUE	; Setup previous address

GXRCO2:	CAMG T1,-1(T2)		; Is this block big enough?
	 JRST GXRCO1		;  Yes, give it away

	MOVE T3,T2		; Remember previous block
	MOVE T2,0(T2)		; Nope, point to next block
	JUMPN T2,GXRCO2		; And loop till done

GXRCO3:	NION			; Turn interrupts back on
	PUSH P,T1		; Save Size for a moment
	ADDI T1,1		; Account for size word
	CALL GETCOR		; Get the memory
	 JRST PA1		;  Adjust the stack and return
	POP P,(T1)		; Put the size into the block
	ADDI T1,1		; Point to the next word
	RETSKP			; And return success

GXRCO1:	MOVE T1,T2		; Get block address into T1
	MOVE T2,0(T2)		; Get next block address
	MOVEM T2,0(T3)		; Make previous block point to next block
	NION

; Zero the core we are returing

	SETZM (T1)		; Clear the first word
	MOVE T2,-1(T1)		; Get the length of the block
	MOVE T3,T1		; Get the address of the first word
	XMOVEI T4,1(T3)		; Get the address of the second word
	EXTEND T2,[XBLT]	; Clear the whole block
	RETSKP			; All done

RXRCOR:	SETZM 0(T1)		; Zero the next block pointer
	MOVE T2,TODCLK		; Get a timestamp
	MOVEM T2,1(T1)		; Stamp the block

	NIOFF			; No interrupts please
	MOVE T2,XRCQUE		; Get the first block on the queue
	MOVEM T2,0(T1)		; Make this block point to the first
	MOVEM T1,XRCQUE		; Make this block be the new first
ONRET:	NION			; Allow interrupts
	RET

; Here once a minute to release grubby blocks from the XRCQUE

CORCHK:	ADDI T1,^D60000		; Make it one more minute
	MOVEM T1,CORTIM		; Don't do this till a minute

	NIOFF			; No interrupts please
	SKIPN T2,XRCQUE		; Get the queue head
	 JRST ONRET		;  Q is empty, just return

	SAVEAC <P1,P2,P3>
	MOVE P1,T2		; Get q head setup
	XMOVEI P2,XRCQUE	; Get the queue head
	SUBI T1,^D120000	; Compute age of grubbiness
	MOVE P3,T1		; Put time in a safe place

CORCH2:	CAML P3,1(P1)		; Is this block too grubby?
	 JRST CORCH1		;  Yes, release it
	MOVE P2,P1		; Remember previous block
	MOVE P1,0(P1)		; Nope, point to next block
	JUMPN P1,CORCH2		; And loop over all blocks
	NION
	RET

CORCH1:	MOVE T1,P1		; Get block address into T1
	MOVE P1,0(P1)		; Get next block address
	MOVEM P1,0(P2)		; Make previous block point to next block
	SUBI T1,1		; Point to true start of block
	CALL RELCOR		; Release the memory
	JUMPN P1,CORCH2		; Loop over all blocks
	NION
	RET
	SUBTTL CHKBUF - Check Buffer Physical Range

;ROUTINE TO ADDRESS CHECK ANY BUFFER.  NOTE THAT THE BUFFER SHOULD BE .LE.
;1000 WORDS FOR THIS ROUTINE TO WORK CORRECTLY.
;CALL WITH
;	T1 = BUFFER ADDRESS
;	T2 = # OF WORDS LONG
;RETURNS
;	+1: ERROR
;	+2: BUFFER IS PHYSICALLY CONTIGUOUS
;-

CHKBUF:	ADD T2,T1		;ADD START ADDRESS TO LENGTH
	MAP T1,(T1)		;GET PHYSICAL ADDRESS OF START OF BUFFER
	MAP T2,-1(T2)		;GET PHYSICAL ADDRESS OF END OF BUFFER
	TXZ T1,ADRMSK+PGWD	;CLEAR ACCESS BITS AND WORD WITHIN PAGE
	TXZ T2,ADRMSK+PGWD	;CLEAR ACCESS BITS AND WORD WITHIN PAGE
	SUB T2,T1		;SEE HOW MANY PAGES WE SPAN
	JUMPL T2,RTN		;CATCH CASE OF PHYSICAL RUNNING BACKWARDS
	CAXG T2,1_PGSFT		;IS IT GREATER THAN ONE?
	 AOS (P)		; NO, WE ARE FINE
	RET			;YES, PUNT
	SUBTTL NISTP - Stop The KLNI (Freeze)

NISTP:	MOVX T1,NOP		; Get a No-op
	STOR T1,PSCQA,(PS)	; Prevent QUECMD from starting the port
	OPSTR <XCT>,PSCNI,(PS)	; Read the CSR
	TXNN T1,CO.MRN		; Is the port running?
	 JRST NISTP1		;  Nope, don't bother
	MOVX T1,CO.DIS+CO.MRN	; Setup bits for CONO
	OPSTR <XCT>,PSCNO,(PS)	; Disable the port
	MOVEI T2,TIMOUT		; Loop count
	DO.
	  OPSTR <XCT>,PSCNI,(PS); Read the status
	  TXNN T1,CI.DCP	; Disable complete?
	  SOJG T2,TOP.		; Continue checking
	ENDDO.

NISTP1:	SETZ T1,		; Get bits for CONO
	OPSTR <XCT>,PSCNO,(PS)	; Clear the run flop irregardless

	MOVE T1,CHNTAB+5	; Get our CDB
	MOVX T2,CS.MAI		; Get the maint bit
	IORM T2,CDBSTS(T1)	; Set it in the CDB
	RET			;  and return
	SUBTTL INICOM - Setup Initial KLNI Commands

; This routine will ensure that the first four commands on the command
; queue are Read Station Info, Write Station Info, Load PTT, Load MCAT.
; These commands must be done first to ensure that the KLNI is in a
; consistent state when it finally gets running.

INICOM:	OPSTR XCT,PSCNI,(PS)	; Get the status
	TXNE T1,CO.MRN		; Is the KLNI running?
	 RET			;  Yes, that's not good
	SAVEAC <P1,P2,P3>
	STKVAR (GODRET)
	SETZM GODRET		; Indicate that we want good return
	LOAD P3,PSPCB,(PS)	; Get address of PCB
	OPSTR <DMOVE P1,>,PBCQF,(P3) ; Get flink and blink of command queue
	OPSTR <MAP T2,>,PBCQF,(P3) ; Get physical address of command queue
	TXZ T2,ADRMSK		; Clear junk bits
	STOR T2,PBCQF,(P3)	; Make the flink point to itself
	STOR T2,PBCQB,(P3)	; Make the blink point to the flink

	CALL NIRSI		; Issue a Read station info as the 1st command.
	 NOP			;  Command is busy?!?
	CALL LDPTT		; Load the protocol type table
	CALL LDMCT		; Load the multicast table

	OPSTR <MAP T1,>,PBCQF,(P3) ; Get the physical address of the flink
	TXZ T1,ADRMSK		; Clear junk bits
	CAMN T1,P1		; Was the command queue empty?
	 JRST INICO1		;  Yes, quit now

	LOAD T2,PBCQB,(P3)	; Get address of last command in queue
	MOVE T3,P1		; Get address of 1st command in old queue
	PMOVEM T3,T2		;[7.1002] Make last command point to first old cmd

	MOVE T2,P1		; Get address of 1st command in old queue
	ADDX T2,QE.BLI		; Get address of it's blink
	LOAD T3,PBCQB,(P3)	; Get address of last command in queue
	PMOVEM T3,T2		;[7.1002] Make blink in 1st command in old queue
				;  point to last command in new queue

	MOVE T2,P2		; Get address of last command in old queue
	OPSTR <MAP T3,>,PBCQF,(P3) ; Get address of cmd q header
	TXZ T3,ADRMSK		; Clear junk bits
	PMOVEM T3,T2		;[7.1002] Make flink in last cmd in old queue point
				;  to cmd q header

	STOR P2,PBCQB,(P3)	; Make cmd q blink point to last command in
				;  old queue

INICO1:	SKIPE GODRET		; Do we want to give a good return?
	 RET			;  Nope, quit now
	RETSKP
	SUBTTL ENBKLP - Enable KLIPA NI Port
;+
; THIS ROUTINE IS CALLED BY NININI TO GET THE KLIPA NI PORT RUNNING.
;
; INPUTS:
;
;	Q3 = PCB ADDRESS
;	P1 = CDB ADDRESS
;	P2 = PORT STORAGE ADDRESS
;
; RETURNS:
;
;	+1 = CAN'T START THE KLIPA
;	+2 = KLIPA NI PORT IS NOW ENABLED AND RUNNING
;-

UCVEDT:	EXP 171			;[7.1221] Acceptable edit level

ENBKLP:	CALL RDVER		; Read the ucode version
	 RET			;  Port running!?!??
	STOR T1,PSUMA,(PS)	; Save major version #
	STOR T2,PSUMI,(PS)	; Save minor version #
	STOR T3,PSUED,(PS)	; Save the edit #
	CAMGE T3,UCVEDT		;[7.1221] Acceptable edit level?
	BUG. (CHK,KNIVER,PHYKNI,SOFT,<Bad KLNI microcode version>,<<T3,BADEDT>,<<XADDR. UCVEDT>,GODEDT>>,<

Cause:	PHYKNI has read the microcode edit number from the KLNI, and has
	determined that it is below the minimum revision level required for
	proper port/driver operation. The NI is started anyway.

Action:	Obtain the proper version of the KLNI microcode.  The minimum
	acceptable version is indicated by the second additional data item.

Data:	BADEDT - Edit number read from the KLNI
	GODEDT - Edit number we require
>,,<DB%NND>)			;[7.1210][7.1221] BUGCHK, but start the KLNI
	MOVX T1,CO.CPT		; Get ready to
	OPSTR XCT,PSCNO,(PS)	;  clear the port
	LOAD T1,PSPBA,(PS)	; Get physical addr of 1st word of PCB
	ADDX T1,<CHXFR!CHLST!FLD(3,CHCNT)+PB.PBA>; XFER 3 words and HALT
	LOAD T2,PSCBA,(PS)	; Get CBUS address
	LSH T2,2		; * 4
	MOVEM T1,KIEPT(T2)	; Save in channel area
	MOVX T1,DO.LRA		; Get bits for DATAO
	OPSTR <XCT>,PSDTO,(PS)	; Set start address to 0
	MOVX T1,CO.DIS+CO.MRN	; Get bits for CONO
	OPSTR <XCT>,PSCNO,(PS)	; Go from uninitialized to disabled
	MOVEI T3,TIMOUT		; Loop count
	DO.
	  OPSTR <XCT>,PSCNI,(PS); Read the status
	  TXNN T1,CI.DCP	; Disable complete?
	  SOJG T3,TOP.		; Continue checking
	ENDDO.
	JUMPE T3,INIERR		; Go away if timed out
	LOAD T1,PSPBA,(PS)	; Get physical base address of PCB
	ADDX T1,PB.CCW		; Address of channel command word
	TXO T1,CHJMP		; Make it a jump command
	MOVEM T1,KIEPT(T2)	; Save it in the channel jump word

	LOAD T1,PSCNO,(PS)	; Get the CONO for this channel
	TXC T1,<Z CO.BTS+CO.CQA(T1)> ; Clear index AC, set Command Queue Avail
	STOR T1,PSCQA,(PS)	; Allow QUECMD to kick the port
	MOVX T1,CO.BTS		; Get the enable bit, PIA and Uproc run
	OPSTR <XCT>,PSCNO,(PS)	; Enable the port for normal operation
	MOVEI T2,TIMOUT		; Get the loop count
	DO.
	  OPSTR <XCT>,PSCNI,(PS); Read the status
	  TXNN T1,CI.ECP	; Enable complete?
	  SOJG T2,TOP.		; Continue checking
	ENDDO.
	JUMPE T2,INIERR		; Go away if timed out
	OPSTR XCT,PSCQA,(PS)	; Start up the command queue
	RETSKP			; Indicate success

; Here when we timed out during port initialization.  See if any error bits
; are set in T1.

INIERR:	TXNE T1,CI.ERR		; Any error bits set?
	 JRST KNIERR		;  Yes, complain and log the problem

	BUG. (CHK,KNIINF,PHYKNI,HARD,<PHYKNI - KLNI initialization timed out>,<<T1,CONI>>,<

Cause:	The KLNI timed out during initialization.  Either "disable complete"
	didn't set or "enable complete" didn't set (the CONI will indicate
	which).  This is very likely a hardware problem, because the microcode
	version number was valid, and there was no specific error indication
	in the CONI.

Action:	Try reloading the KLNI microcode.  If the problem still persists call
	field service.

Data:	CONI - CONI KNI,
>,,<DB%NND>)			;[7.1210] 
	CALLRET NISTP			; Stop the KLNI gracefully
	SUBTTL PCBINI - Initialize The Port Control Block

;+
; THIS ROUTINE IS CALLED BY ENBKLP TO SETUP THE PCB BEFORE ENABLING THE
; KLIPA NI PORT.
;
; CALL
;
; RETURNS
;	+1 = ALWAYS
;-

; Do the command queue first

QUNLOK:	TDZA T1,T1		; Only unlock interlocks
PCBINI:	SETO T1,		; Reset queue headers
	SAVEAC <P1,P2>
	MOVEM T1,P2		; Remember the unlock only flag
	LOAD P1,PSPCB,(PS)	; Get PCB virt address
	OPSTR <XMOVEI T1,>,PBCQI,(P1) ; Get command queue header
	CALL INIQH		; Initialize the command queue header

; Now do the response queue

	OPSTR <XMOVEI T1,>,PBRQI,(P1) ; Get the response queue header
	CALL INIQH		; Initialize the response queue header

; The unknown protocol type free queue

	OPSTR <XMOVEI T1,>,PBUQI,(P1) ; Get the queue flink address
	CALL INIQH		; Initialize the UPT queue header

	;...
	;...

; Setup the protocol type table

	XMOVEI T1,PTTADR	; Protocol type table address
	STOR T1,PSPTT,(PS)	; Save in port storage
	MAP T1,(T1)		; Make it physical
	TXZ T1,ADRMSK		; Clear junk
	STOR T1,PBPTT,(P1)	; Save it in PCB

; Setup the free queues and their pointers in the PTT

	XMOVEI T1,FRQADR	; Get free queue headers address
	LOAD T3,PSPTT,(PS)	; Get virt address of PTT
	MOVEI T4,NPTT		; Setup loop count
SETFQ1:	STOR T1,PTVIR,(T3)	; Save the virtual address of header
	MOVX T2,PTFRE		; Get the PTT entry free bit
	SKIPE P2		; Skip if just unlocking interlocks
	 IORM T2,$PTFRE(T3)	;  Indicate entry is unused
	CALL INIQH		; Setup the free queue header
	SKIPE P2		; Skip if just unlocking interlocks
	 STOR T2,PTFRQ,(T3)	;  Save the physical address of the flink
	ADDI T1,QH.LEN		; Advance to next queue header
	ADDI T3,PT.LEN		; Advance to next protocol type entry
	SOJG T4,SETFQ1		; Loop for all

	JUMPE P2,RTN		; Just return now if unlocking interlocks

; Set up the fake PTT entry for the unknown protocol type queue

	OPSTR <XMOVEI T1,>,PBUQI,(P1) ; Get address of the unknown free queue
	OPSTR <XMOVEI T3,>,PSUNK,(PS) ; Get address of pseudo PTT
	STOR T1,PTVIR,(T3)	; Save the virtual address of the q header
	OPSTR <MAP T2,>,QHFLI,(T1) ; Make address physical
	TXZ T2,ADRMSK		; Clear access bits
	STOR T2,PTFRQ,(T3)	; Save the physical address of the flink
	SETONE PTFRE,(T3)	; Indicate entry is unused

; Setup the multicast table

	XMOVEI T1,MCTADR	; Multicast table address
	STOR T1,PSMTT,(PS)	; Save in ports storage
	MAP T1,(T1)		; Make physical
	TXZ T1,ADRMSK		; Clear junk
	STOR T1,PBMTT,(P1)	; Save in PCB

; Setup the counters block

	XMOVEI T1,RCBADR	; Get address of read counters block
	MAP T1,(T1)		; Make it physical
	TXZ T1,ADRMSK		; Clear out junk bits
	STOR T1,PBRCB,(P1)	; Tell the port about it
	RET
; Initialize a KLNI queue header
; Call:	movx T1,virtual address of queue header
;	movx P2,flag, if 0 just reset interlock
;	CALL INIQH
;	<Return +1 always, T1 unchanged, T2/ phys addr of flink of Q header>
; All other ACs are unchanged

INIQH:	SETONE QHIWD,(T1)	; Initilize the interlock word
	JUMPE P2,RTN		; Jump if just resetting interlocks
	OPSTR <MAP T2,>,QHFLI,(T1) ; Get physical addr of command queue flink
	TXZ T2,ADRMSK		; Clear junk
	STOR T2,QHFLI,(T1)	; Make flink point to itself
	STOR T2,QHBLI,(T1)	; Make blink point to flink
	SETZRO QHLEN,(T1)	; Clear the length/reserved word
	RET
	SUBTTL ASGPRO - Assign Protocol Type

; This routine assigns the protocol type in UNPRO on the channel in UNCHN
; in an interlocked manner.

ASGPRO:	LOAD T1,UNCHN,(UN)	; Get the channel number from the UN block
	CALL FNDCHN		; Lookup that channel
	 ERROR UNNSC%		;  No such channel
	STOR T1,PRCHN,(PR)	; Save the channel block address in portal
	MOVE PS,T1		; Setup channel block pointer

	NIOFF			; Disable interrupts for a while
	CALL NIEPT		; Try to enable the protocol type
	 JRST ONRET		;  Got an error, NION & return failure
ONRSKP:	NION			; Enable the interrupts
	RETSKP			; And return success

NIEPT:	LOADE T1,UNPRO,(UN)	; Get the protocol type
	CAML T1,[-3]		; Less than the smallest legal protocol type
	 CAILE T1,177777	;  Or greater than the largest legal?
	  ERROR UNIVP%		;   Yes, invalid protocol type
	CALL FNDPT		; Try to find it
	 SKIPA			;  Not there, good
	  ERROR UNPIU%		;   Protocol already in use

	STOR T1,PRPRO,(PR)	; Store the protocol type
	STOR T2,PRFQA,(PR)	; Save free q address for NISRB

	STOR T1,PTTYP,(T2)	; Put the protocol type into the PTT
	SETZRO PTFRE,(T2)	; Entry is no longer free
	SETONE PTENA,(T2)	; Enable this entry

	CAMN T1,[-3]		; Unknown mode?
	 RETSKP			;  Yes, all done

	CAME T1,[-2]		; Promiscuous mode?
	IFSKP.			; Yes...
	  SETONE <PSPMC,PSPRM>,(PS) ;   Set promiscuous mode & all multicast
	  SETONE <PSVPM,PSVPR>,(PS) ;   Indicate these bits are valid
	  CALL NIWSI		;   Tell the KLNI about them
	  RETSKP		;   All done!
	ENDIF.

	CALL LDPTT		; Tell the channel about it
	RETSKP			; And return successful
	SUBTTL NIDPT - Disable A Protocol Type

;+
; THIS ROUTINE IS CALLED BY THE DLL TO DISABLE A PROTOCOL TYPE
;
; RETURNS +1: ERROR
;	  +2: PROTOCOL DISABLED
;-

NIDPT:	NIOFF			; No interrupts
	SAVEAC <P1,P2>
	LOAD P1,PRFQA,(PR)	; Get the PTT entry address
	JUMPLE P1,ILLPTY	; Error if .LE. 0
	JE PTENA,(P1),ILLPTY	; Error if protocol type wasn't enabled
	LOADE P2,PRPRO,(PR)	; Get the protocol type from portal block
	SKIPGE P2		; Is it one of the funny ones?
	IFSKP.			; No, perform a sanity check
	  LOAD T2,PTTYP,(P1)	;   Get protocol type from PTT
	  CAME P2,T2		;   Do they match?
	   JRST ILLPTY		;    No, error
	ENDIF.

	SETZRO PTENA,(P1)	; Disable the protocol type
	LOAD T2,PTVIR,(P1)	; Get virtual address of free buffer list
	SETZRO QHLEN,(T2)	; Zero the length
	SKIPL P2		; Unknown or promiscuous?
	 CALL LDPTT		;  No, load a new PTT

	CAME P2,[-2]		; Promiscuous?
	IFSKP.			; Yes, disable it in the KLNI
	  SETZRO <PSPMC,PSPRM>,(PS) ;   Clear promiscuous mode & all multicast
	  CALL NIWSI		;   Tell the KLNI about them
	ENDIF.

; Free up all receive buffers

NIDPT2:	LOAD T1,PTVIR,(P1)	; Get address of free queue header interlock
	CALL REMQUE		; Get something off the queue
	 JRST NIDPT1		;  Nothing on the queue
	MOVX T1,UNRAB%		; Get the receive abort error code
	STOR T1,CMERC,(CM)	; Put it into the response
	CALL MSGAV2		; Return the message to it's owner
	 NOP
	JRST NIDPT2		; Look for more

NIDPT1:	SETONE PTFRE,(P1)	; Indicate that the entry is free

; Disable all multicasts associated with this portal

NIDPT3:	OPSTR <SKIPN T2,>,PRMUL,(PR) ; Get the enabled multicast bit table
	 JRST ONRSKP		;  Nothing enabled, just return
	JFFO T2,.+1		; Find the MTT table offset for this bit
	OPSTR <ADD T3,>,PSMTT,(PS) ; Point to the MTT entry
	CALL NIDRA2		; Disable this address
	 BUG. (HLT,KNIADE,PHYKNI,SOFT,<PHYKNI - Multicast address disable error>,,<

Cause:	NIDPT got an error from NIDRA when attempting to disable a multicast
	address that was supposedly enabled.
>)
	JRST NIDPT3		; Go look for more addresses
	SUBTTL NISRA - Specify Receive Address

;+
; THIS ROUTINE IS CALLED BY THE DLL TO SPECIFY A MULTICAST ADDRESS.
; STANDARD SETUP.
;
; RETURNS +1: ERROR
;	  +2: ADDRESS ASSIGNED
;
;-

NISRA:	SAVEAC P1
	CALL FETADR		; Fetch the multicast address from UNDAD
	TXNN T1,1B7		; Is it really a multicast address?
	 ERROR UNIMA%		;  Nope, quit now

	NIOFF			; Interlock search and set of multicast table
	CALL FNDMT		; Is it already enabled?
	IFNSK.			;  Nope, set it and enable it

; Not already enabled, setup the MTT slot and tell the KLNI about it

	  JUMPE T3,[NION	;   Jump if no free slots
		    ERROR UNNRE%] ;   No room for entry

; Found a free entry, set the multicast address

	  OPSTR <DMOVEM T1,>,MTHAD,(T3) ;   Install the multicast address
	  SETONE MTENA,(T3)	;   Set the enable bit

	  MOVE P1,T3		;   Save address of MCAT entry
	  MOVX T1,NF.EMA	;   Indicate we are enabling multicast address
	  CALL LDMCT		;   Tell KLNI about the change
	  MOVE T3,P1		;   Restore the address of the MCAT entry
	ENDIF.

	MOVE T2,T3		; Get a copy of the base address
	OPSTR <SUB T2,>,PSMTT,(PS) ; Subtract base address of table
	MOVNS T2		; Shifting right
	MOVX T1,1B0		; Get a single bit
	LSH T1,(T2)		; Shift it into place
	OPSTR <TDNE T1,>,PRMUL,(PR) ; Does he have this enabled?
	 JRST ONRSKP		;  Yes, don't do it again
	OPSTR <IORM T1,>,PRMUL,(PR) ; Add this address to the table
	INCR MTUSE,(T3)		; Increment the use count for this address
	NION			; Enable the interrupts
	RETSKP			; Return to caller
	SUBTTL NIDRA - Disable A Multicast Address

;+
; THIS ROUTINE IS CALLED TO DISABLE A MULTICAST ADDRESS
;
;-

NIDRA:	CALL FETADR		; Fetch the Ethernet address
	TXNN T1,1B7		; Is it really a multicast address?
	 ERROR UNIMA%		;  Nope, quit now

	NIOFF			; Disable interrupts
	CALL NIDRA1		; Do the work
	 JRST ONRET		;  Enable interrupts & pass error upwards
	JRST ONRSKP		; Enable interrupts & Skip return

NIDRA1:	CALL FNDMT		; Try to find it
	 ERROR UNANE%		;  Address not enabled

; The address is in the MTT, see if this portal has the address enabled

NIDRA2:				; Here from NIDPT
	MOVE T1,T3		; Get a copy of the MTT entry address
	OPSTR <SUB T1,>,PSMTT,(PS) ; Subtract base address of MTT
	MOVNS T1		; Make offset negative (for right shift)
	MOVX T2,1B0		; Get a single bit
	LSH T2,(T1)		; Shift it into place
	OPSTR <TDNN T2,>,PRMUL,(PR) ; Is this protocol type enabled?
	 ERROR UNANE%		;  Address not enabled
	OPSTR <ANDCAM T2,>,PRMUL,(PR) ; Turn off the bit in his table
	DECR MTUSE,(T3)		; Decrement the use count
	JN MTUSE,(T3),RSKP	; Jump if use count is non-zero

; Here when we have to disable the multicast address

	SETZRO MTENA,(T3)	; Disable the address
	CALL LDMCT		; Tell KLNI about the change
	RETSKP			; And return success
	SUBTTL FNDMT - Find Enabled MTT Entry For A Given Multicast Address

; Find the MTT entry associated with a given mulitcast address.  If the
; address is not found, return the address of the first free MTT slot, or
; 0 if none are available.
;
; Call:	MOVX T1,high_order_addr
;	MOVX T2,low_order_addr
;	CALL FNDMT
;	 <Return +1, address not found,
;		     T1 & T2/ untouched, T3/ addr of 1st free MTT slot or 0>
;	<Return +2, success,
;		    T1 & T2/ untouched, T3/ addr of MTT slot containing mcast>
;

FNDMT:	SAVEAC <P1,P2,P3>
	DMOVE P1,T1		; Put the address in a safe place
	SETZ P3,		; Clear the free MTT entry indicator
	LOAD T3,PSMTT,(PS)	; Get the MTT base address
	MOVX T4,NMTT		; Number of supported multicast addresses
	DO.			; Loop over the whole table
	  TMNN MTENA,(T3)	;   Is this entry enabled?
	  IFSKP.		;   Yes, see if it matches
	    OPSTR <DMOVE T1,>,MTHAD,(T3) ;     Get the address
	    TRZ T1,17		;     Mask it down
	    TXZ T2,3777777	;     Mask the low order
	    CAMN T1,P1		;     High order match?
	     CAME T2,P2		;      Low order match?
	      TRNA		;       No to either
	       JRST FNDMT1	;        Yes, quit the loop
	  ELSE.			;   Entry not enabled
	    SKIPN P3		;     Do we have a free entry yet?
	     MOVE P3,T3		;      No, use this one
	  ENDIF.
	ADDI T3,MT.LST		; Advance to next
	SOJG T4,TOP.		; Loop for all
	ENDDO.

	MOVE T3,P3		; Get the first free entry
	DMOVE T1,P1		; Get the mulitcast address back
	RET			; Return failure

FNDMT1:	DMOVE T1,P1		; Restore the address
	RETSKP			; Return success
	SUBTTL LDMCT - Build Load Tables Commands

;+
; THESE ROUTINES ARE CALLED TO TELL THE KLIPA THAT EITHER THE MULTICAST
; ADDRESS TABLE OR THE PROTOCOL TYPE TABLE HAS CHANGED.
;
; RETURNS +1: ALWAYS
;-

LDMCT:	SETZ CM,		; First get a 0
	NIOFF			; No interrupts please
	OPSTR <EXCH CM,>,PSLMT,(PS) ; Acquire command queue entry
	SKIPE CM		; Is the buffer in use?
	IFSKP.			; Yes, set indicator for later
	  SETONE PSLMC,(PS)	;   Set flag to do later
	  NION			;   OK to interrupt again
	  RET			;   Give good return
	ENDIF.
	NION			; Enable interrupts
	MOVEI T1,OP.LDM		; Opcode for load multicast address table
	STOR T1,CMOPC,(CM)	; Save it
	CALLRET QUECMD		; Queue the command

; Response to load multicast table command

LMCRES:	STOR CM,PSLMT,(PS)	; Give buffer back
	TMNN PSLMC,(PS)		; Do we have a deferred one?
	 RETSKP			;  Nope, all done
	SETZRO PSLMC,(PS)	; Yes, clear the deferral flag
	CALLRET LDMCT		; Go load the table again
	SUBTTL LDPTT - Load Protocol Type Table

; Setup the protocols that the KLNI should be sensitive to.

LDPTT:	SETZ CM,		; First get a 0
	NIOFF			; No interrupts please
	OPSTR <EXCH CM,>,PSLPT,(PS) ; Acquire command queue entry
	SKIPE CM		; Is the buffer in use?
	IFSKP.			; Yes, set indicator for later
	  SETONE PSLPP,(PS)	;   Set flag to do later
	  NION			;   OK to interrupt again
	  RET			;   Give good return
	ENDIF.
	NION			; Enable interrupts
	MOVEI T1,OP.LDP		; Opcode for loading protocol type table
	STOR T1,CMOPC,(CM)	; Save it
	CALLRET QUECMD		; Queue the command

; Here for response from load PTT command

LPTRES:	STOR CM,PSLPT,(PS)	; Give buffer back
	TMNN PSLPP,(PS)		; Do we have a deferred one?
	 RETSKP			;  Nope, all done
	SETZRO PSLPP,(PS)	; Yes, clear the deferral flag
	CALLRET LDPTT		; Go load the table again
	SUBTTL NIWSI - Write Station Information

; This routine is called to transfer a new set of KLNI variables into the
; KLNI.  The variables transferred are:
;
;	PSHAD/LAD	-	Channel Ethernet address
;	PSCRC		-	Allow CRC errors
;	PSPMC		-	Promiscuous multicast mode
;	PSH40		-	H4000 mode
;	PSPRM		-	Promiscuous mode
;	PSRTY		-	# of retries for various operations


NIWSI:	SETZ CM,		; First get a 0
	NIOFF			; No interrupts please
	OPSTR <EXCH CM,>,PSWSI,(PS) ; Acquire command q entry for WSI
	SKIPE CM		; Is the buffer in use?
	IFSKP.			; Yes, set indicator for later
	  SETONE PSLSI,(PS)	;   Set flag to do it later
	  NION			;   OK to interrupt again
	  RET			;   Give good return
	ENDIF.
	NION			; Enable interrupts

	LOAD T2,PSHAD,(PS)	; Get channel's Ethernet address to set
	STOR T2,WSHAD,(CM)	; Store it in the command
	LOAD T2,PSLAD,(PS)	; Get low order
	STOR T2,WSLAD,(CM)	; Save it

	MOVEI T1,OP.WSI		; Get the command opcode
	STOR T1,CMOPC,(CM)	; Store the opcode

	LOAD T1,PSVAR,(PS)	; Get current mode bits
	STOR T1,WSVAR,(CM)	; Save them in the command

	MOVX T1,NRETRY		; Get the number of retrys allowed
	STOR T1,WSRTY,(CM)	; And save it

	CALLRET QUECMD		; Queue the command

; Here when done with Write Station Info command.  Copy the variables we just
; set in the KLNI into the monitor's shadow variables.

WRTNSA:	OPSTR <DMOVE T1,>,WSHAD,(CM) ; Get the station address
	OPSTR <DMOVEM T1,>,PSSAD,(PS) ; Put it into the shadow area

	LOAD T1,WSVAR,(CM)	; Get the variables
	STOR T1,PSSVA,(PS)	; Put them in the shadow area

	STOR CM,PSWSI,(PS)	; Give the WSI command back
	TMNN PSLSI,(PS)		; Do we have a deferred command?
	 RETSKP			;  Nope, all done
	SETZRO PSLSI,(PS)	; Yes, clear the deferral flag
	CALLRET NIWSI		; Go load the variables again
	SUBTTL LOGERR - Generate ERROR.SYS Entries

LOGERR:	SAVEAC <P2,P3,P5>	; Save a P
	MOVX T1,KP%LEN		; Size of KLIPA entry
	MOVX T2,KP%SIZ		; Size of strings
	CALL ALCSEB		; Get the SEB
	 RET			;  No memory, give up for now
	LOAD P5,PSCBA,(PS)	; Get CBUS address
	LSH P5,2		; * 4 (Length of channel logout area)
	MOVE P3,T1		; Put SEB pointer into a safe place
	LOAD P2,PSPCB,(PS)	; Get PCB address into a safe place
	MOVE T2,[-SEBTSZ,,SEBTAB] ; Get arg for SEBCPY
	CALL SEBCPY		; Copy the easy stuff
	 JFCL			;  Really shouldn't happen
	LOAD T1,PSUED,(PS)	; Get microcode version
	IORX T1,KP%NI		; Indicate this is an NI
	MOVEM T1,SEBDAT+KP%VER(P3) ; Save the version info
	MOVE T1,P3		; Restore SEB address
	CALLRET QUESEB		; And queue up the SEB

SEBTAB:	;SEBPTR offset,type(SBT{WD,STR,EVC,FNA}),data
	SEBPTR	    0,SBTEVC,SEC%KP	; KLIPA event
	SEBPTR KP%CSR,SBTWD,P1		; CONI
;	SEBPTR KP%VER,SBTWD,PS.UCV(PS)	; Microcode version
	SEBPTR KP%DSP,SBTWD,PS.TPC(PS)	; Error disposition
	SEBPTR KP%CRA,SBTWD,PS.LAR(PS)	; CRAM address
	SEBPTR KP%CRD,SBTWD,PS.CRL(PS)	; CRAM data left (1'st 30 bits)
	SEBPTR KP%CRD+1,SBTWD,PS.CRR(PS); CRAM data right (next 30 bits)
	SEBPTR KP%LG0,SBTWD,KIEPT+0(P5)	; Channel logout word 0
	SEBPTR KP%LG1,SBTWD,KIEPT+1(P5)	; Channel logout word 1
	SEBPTR KP%LG2,SBTWD,KIEPT+2(P5)	; Channel logout word 2
	SEBPTR KP%ECW,SBTWD,PB.CCW(P2)	; CCW at time of error
	SEBPTR KP%PE0,SBTWD,PB.ER0(P2)	; First PCB error logout word
	SEBPTR KP%PE1,SBTWD,PB.ER1(P2)	; Second PCB error logout word
	SEBTSZ==.-SEBTAB
	SUBTTL Common BUGs

ILLPTY:	BUG. (HLT,KNIIPT,PHYKNI,SOFT,<PHYKNI - Illegal protocol type on close>,<<T1,PTYPE>>,<

Cause:	A protocol type was specified on the close that was NOT enabled.

Data:	PTYPE - The specified protocol type.
>)
	RET

QEMPTY:	BUG. (CHK,KNIQUE,PHYKNI,SOFT,<PHYKNI - Queue empty on entry>,<<T1,QUE>>,<

Cause:	A queue was empty when the routine REMQUE was called.

Data:	QUE - The queue header address.
>)
	RET


UNKRES:	BUG. (HLT,KNIUOP,PHYKNI,HARD,<PHYKNI - Unknown response>,<<T1,RESP>>,<

Cause:	The port gave us a response we don't know about.

Data:	RESP - Response
>)
	RET

ILLFNC:	BUG. (CHK,KNIIFD,PHYKNI,SOFT,<PHYKNI - Illegal function from DLL>,<<T1,PASED>,<T2,BLKADR>,<T3,FNC>>,<

Cause:	The NIDLL called the driver with a function we don't handle yet.

Data:	BLKADR -The function block address.
	FNC - The function code
>)
	RET

NOCTL:	BUG. (HLT,KNINIB,PHYKNI,SOFT,<PHYKNI - No control buffer at interrupt level>,,<

Cause:	The Port Storage (PS) block was not set up with the address of a UN
	block to be used at interrupt level.
>)
	RET

VMCBUG:	BUG. (CHK,KNIIAM,PHYKNI,SOFT,<PHYKNI - Illegal addressing mode>,<<T2,ADR>>,<

Cause:	An illegal addressing mode was specified.

Data:	ADR - The mode specfied.
>)
	RET
	SUBTTL Miscellaneous Routines

; Swap the right justified bytes in T1

SWAB:	DPB T1,[POINT 8,T1,19]		; Move the bottom byte to the top
	LSH T1,-8			; Right justify the whole mess
	RET

SWAP6B:	MOVE T2,(T1)			; Get 1st word of bytes
	LSHC T2,-^D12			; Put 1st byte at top of T3
	DPB T2,[POINT 8,T3,15]		; Put 2nd byte next
	DPB T2,[POINT 16,T3,31]		; Put 3rd byte next
	LSH T2,-^D16			; Put last byte into position
	DPB T2,[POINT 8,T3,31]		; Put in 4th byte

	MOVE T1,1(T1)			; Get 2nd word of bytes
	LSHC T1,-^D12			; Isolate 5th and 6th bytes
	DPB T1,[POINT 8,T2,15]		; Put 6th byte into place
	MOVE T1,T3			; Get 1 thru 4 into T1
	TRZ T1,17			; Clear unwanted bits in high address
	TDZ T2,[000003,,-1]		; Clear unwanted bits in low address

	RET				; And return (that wasn't so bad!)

; Fetch an Ethernet address from a UN block.

FETADR:	JN UNPTR,(UN),FETAD1		; Jump if user supplied a byte pointer

; Immediate address

	OPSTR <DMOVE T1,>,UNDAD,(UN)	; Get the address
	TXZ T1,17
	TXZ T2,3777777
	RET

FETAD1:	OPSTR <DMOVE T3,>,UNDAD,(UN)	; Fetch byte pointer
	ILDB T1,T3			; Byte 0
	LSHC T1,-8			; Put it into place
	ILDB T1,T3			; Byte 1
	LSHC T1,-8			; Shift it in
	ILDB T1,T3			; Byte 2
	LSHC T1,-8			; Shift it in
	ILDB T1,T3			; Byte 3
	LSHC T1,-12			; Shift it in & right justify 0-3
	ILDB T1,T3			; Byte 4
	ILDB T3,T3			; Byte 5
	DPB T3,[POINT 8,T1,19]		; Install byte 5
	LSH T1,-8			; Right justify 4 & 5 into place
	EXCH T1,T2			; Correct the order	
	RET

RSI (NIPIFG,-1)			; Indicates NI interrupts are off

.NIOFF:	CONSO PI,PIPIIP		; Are we at interrupt level?
	 NOSKED			;  No, disable the scheduler
	CHNOFF NIPIA		; Disable the KLNI channel
	AOS NIPIFG		; Indicate another nested interlock
	RET			; And return to caller

.NION:	SOSGE NIPIFG		; Should we enable the interrupts?
	 CHNON NIPIA		;  Enable the KLNI interrupt channel
	CONSO PI,PIPIIP		; Are we at interrupt level?
	 OKSKED			;  No, don't forget to enable the scheduler
	RET
	SUBTTL History Buffer Routines

IFN FTHIST,<

	..FOO==.
	PHASE 0
.HBFCN:!	BLOCK 1			; Function code
.HBUNA:!	BLOCK 1			; UN block address
.HBPC:!		BLOCK 1			; PC of the caller
.HBCPI:!	BLOCK 1			; CONI PI,
.HBTIM:!	BLOCK 2			; Two word RDTIME value
.HBUNB:!	BLOCK UN.SPI		; UN block
.HBLEN:!	BLOCK 0			; Total length
	DEPHASE
	RELOC ..FOO

RSI UNBUFF,0			; Pointer to start of recording buffer
RSI UNNEXT,0			; Pointer to next useable HB block
RSI UNLAST,0			; Pointer to last useable HB block
RSI NUMUNB,0			; Number of HB blocks to allocate

; Record UN block.  Arguments: T1/ fcn code, T2/ UN block pointer
; Uses no AC's.

RECUNB:	SKIPN UNBUFF			; Buffer initialized?
	 CALL INIUNB			;  Nope, go init the buffer
	PUSH P,T1			; Save our work AC's
	PUSH P,T2
	PUSH P,T3

	PIOFF				; Absolutely no interrupts, please
	MOVE T3,UNNEXT			; Get pointer to next free slot
	CAMLE T3,UNLAST			; Are we beyond the end?
	 MOVE T3,UNBUFF			;  Yes, reset to the beginning
	MOVEM T3,UNNEXT			; Save the pointer

	DMOVEM T1,.HBFCN(T3)		; Save function code & un block address
	MOVEI T1,UN.SPI			; Setup length of transfer
	ADDI T3,.HBUNB			; Point to dest address
	EXTEND T1,[XBLT]		; Copy the UN block into the buffer

	MOVE T3,UNNEXT			; Get address buffer (again)
	RDTIME .HBTIM(T3)		; Save the current time
	MOVE T1,-3(P)			; Get callers PC
	MOVEM T1,.HBPC(T3)		; Save it in the UN block
	CONI PI,.HBCPI(T3)		; Get the PI's too
	ADDI T3,.HBLEN			; Point to next block
	MOVEM T3,UNNEXT			; Save it for later
	PION				; Enable the interrupts
	POP P,T3
	POP P,T2
	POP P,T1
	RET

; Record CM block.  Arguments: CM/ Pointer to CM block
; Uses no AC's.

RECCMB:	SKIPN UNBUFF			; Buffer initialized?
	 CALL INIUNB			;  Nope, go init the buffer
	PUSH P,T1			; Save our work AC's
	PUSH P,T2
	PUSH P,T3

	PIOFF				; Absolutely no interrupts, please
	MOVE T3,UNNEXT			; Get pointer to next free slot
	CAMLE T3,UNLAST			; Are we beyond the end?
	 MOVE T3,UNBUFF			;  Yes, reset to the beginning
	MOVEM T3,UNNEXT			; Save the pointer

	SETOM .HBFCN(T3)		; Make function code look invalid
	MOVEM CM,.HBUNA(T3)		; Save command block address
	MOVEI T1,UN.SPI			; Setup length of transfer
	MOVE T2,CM			; Get address of command block
	ADDI T3,.HBUNB			; Point to dest address
	EXTEND T1,[XBLT]		; Copy the CM block into the buffer

	MOVE T3,UNNEXT			; Get address buffer (again)
	RDTIME .HBTIM(T3)		; Save the time in the buffer
	MOVE T1,-3(P)			; Get callers PC
	MOVEM T1,.HBPC(T3)		; Save it in the UN block
	CONI PI,.HBCPI(T3)		; Get the PI's too
	ADDI T3,.HBLEN			; Point to next block
	MOVEM T3,UNNEXT			; Save it for later
	PION				; Enable the interrupts
	POP P,T3
	POP P,T2
	POP P,T1
	RET
	SUBTTL INIUNB -  Initialize The History Buffer

; Still under IFN FTHIST

INIUNB:	SKIPN NUMUNB			; Want to record UN blocks?
	 JRST [ADJSP P,-1		; Pop call off the stack
	       RET]			; And return
	SAVET				; Preserve all T's
	MOVE T1,NUMUNB			; Get number of UN block desired
	IMULI T1,.HBLEN			; Convert to words
	HRLI T1,.RESP1			; Priority 1
	MOVX T2,.RESGP			; From the general pool
	CALL ASGRES			; Get the memory
	 RET
	PIOFF
	MOVEM T1,UNBUFF			; Install pointer to UN buffer
	MOVEM T1,UNNEXT			; Install pointer to next block
	MOVE T2,NUMUNB			; Get number of UN blocks
	IMULI T2,.HBLEN			; Convert it to words
	ADD T1,T2			; Point to end of buffer
	SUBI T1,.HBLEN			; Point to last HB block
	MOVEM T1,UNLAST			; Save it
	PION
	RET
>; End of IFN FTHIST
	SUBTTL FNDCHN - Find Channel Block Associated With Channel # in UNCHN

; Call:	MOVX T1,channel #
;	CALL FNDCHN
;	 <Error return, invalid channel #>
;	<Good return, PS block addr in T1>

FNDCHN:	SKIPA T2,CHNBAS		; Get channel list base address
FNDCH1:	 LOAD T2,PSNXT,(T2)	;  Get next channel block
	JUMPE T2,RTN		; Jump if we reached the end
	OPSTR <CAME T1,>,PSCHN,(T2) ; Do the channel numbers match?
	 JRST FNDCH1		;  No, try again
	MOVE T1,T2		; Put block address into T1
	RETSKP			; Yes, return success

; ALLCHN is a co-routine that will loop through all channels in existence.
; Skip and non-skip returns are treated the same.

ALLCHN:	SKIPA PS,CHNBAS		; Get base of channel list
ALLCH1:	 LOAD PS,PSNXT,(PS)	;  Get next channel block	
	JUMPE PS,ALLCH2		; Jump when we reach end of list
	CALL @(P)		; Call back the caller
	 NOP			;  Ignore skip/non-skip
	JRST ALLCH1		; Loop for the rest

ALLCH2:	ADJSP P,-1		; Pop the caller off the stack
	RET			;  and return to his caller

; ALLCHE is the same as ALLCHN except that it will terminate on errors
; (non-skip returns) and pass skip returns back to it's callers.

ALLCHE:	SKIPA PS,CHNBAS		; Get base of channel list
ALLCE1:	 LOAD PS,PSNXT,(PS)	;  Get next channel block	
	JUMPE PS,ALLCE2		; Jump when we reach end of list
	CALL @(P)		; Call back the caller
	 TRNA			;  Got an error, propagate it upwards
	JRST ALLCE1		; Loop for the rest

	ADJSP P,-1		; Pop the caller off the stack
	RET			;  and return error to the caller

ALLCE2:	ADJSP P,-1		; Pop the caller off the stack
	RETSKP			;  and return success to his caller

; The following instruction is used by the routine REDLIS in NISRV for
; fetching the channel number from a PS block.

CHNFET:	LOAD T1,PSCHN,(PR)

;XCKNI - STOP THE NI PORT
;	CALL XCKNI		;(/)
; RETURNS +1

	RESCD

XCKNI::	SKIPE NIHERE		;[7134] NI IN USE?
	CONO KNI,CO.CPT		;[7134] YES, SHOOT IT
	RET			;[7134]
	SUBTTL End of PHYKLP

	TNXEND
	END