Google
 

Trailing-Edge - PDP-10 Archives - BB-4170H-SM - sources/iman22.mac
There is 1 other file named iman22.mac in the archive. Click here to see a list.
;<4.MONITOR>IMAN22.MAC.71, 10-Jan-80 09:45:23, EDIT BY STOCKDALE
;ADD "AND T2,[-1,,177777] & AND T4,..." to ensure bits 18,19 are cleared - this
; fixes problem where UBA puts buffer address into buffer (18 bit address) on a
; left byte transfer which then messes up IMAN22 which expects to see only 16 
; bit bytes.
;<4.MONITOR>IMAN22.MAC.70,  3-Jan-80 08:08:53, EDIT BY R.ACE
;UPDATE COPYRIGHT DATE
;[SRI-KL]<LCAMPBELL>IMAN22.MAC.72  7-Dec-79 09:27:30, Edit by LCAMPBELL
;[SRI-KL]<LCAMPBELL>IMAN22.MAC.71  6-Dec-79 09:59:54, Edit by LCAMPBELL
;[SRI-KL]<LCAMPBELL>IMAN22.MAC.70  5-Dec-79 10:04:27, Edit by LCAMPBELL
;[SRI-KL]<LCAMPBELL>IMAN22.MAC.69  4-Dec-79 12:14:17, Edit by LCAMPBELL
;[SRI-KL]<LCAMPBELL>IMAN22.MAC.68 28-Nov-79 17:47:16, Edit by LCAMPBELL
;[SRI-KL]<LCAMPBELL>IMAN22.MAC.67 15-Nov-79 19:58:27, Edit by LCAMPBELL
; Fix special queues
;<4.MONITOR>IMAN22.MAC.66, 17-Sep-79 12:26:35, Edit by LCAMPBELL
; Document BUGs
;<OSMAN.MON>IMAN22.MAC.1, 10-Sep-79 15:32:57, EDIT BY OSMAN
;TCO 4.2412 - Move definition of BUGHLTs, BUGCHKs, and BUGINFs to BUGS.MAC
;<4.MONITOR>IMAN22.MAC.64,  6-Sep-79 14:43:29, Edit by LCAMPBELL
; BUGHLT if input started without an input buffer available
;<4.MONITOR>IMAN22.MAC.63, 18-Jul-79 16:50:12, Edit by LCAMPBELL
; Fix byte count for 36-bit mode output and special queues output
;<4.MONITOR>IMAN22.MAC.62, 17-Jul-79 11:47:18, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.61, 17-Jul-79 10:41:05, Edit by LCAMPBELL
; Fix PK3236, it also wasn't quite right
;<4.MONITOR>IMAN22.MAC.60, 16-Jul-79 21:34:44, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.59, 16-Jul-79 20:01:10, Edit by LCAMPBELL
; Overhaul UP3632 -- it was only half right
;<4.MONITOR>IMAN22.MAC.58, 16-Jul-79 13:04:00, Edit by LCAMPBELL
; Type out address of caller of AN2BUG
;<4.MONITOR>IMAN22.MAC.57, 11-Jul-79 17:34:46, Edit by LCAMPBELL
; Pare down number of RDIOs and WRIOs for better performance
;<4.MONITOR>IMAN22.MAC.56, 11-Jul-79 16:52:55, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.55, 10-Jul-79 16:41:04, Edit by LCAMPBELL
; Random fixes
;<4.MONITOR>IMAN22.MAC.54,  5-Jul-79 17:48:52, Edit by LCAMPBELL
; Get packing decisions right at AN2RC7
;<4.MONITOR>IMAN22.MAC.53, 29-Jun-79 15:29:41, Edit by LCAMPBELL
; Invalidate cache before touching input buffer
;<4.MONITOR>IMAN22.MAC.52, 28-Jun-79 17:28:42, Edit by LCAMPBELL
; When starting input (AN2STI), insure no input in progress
;<4.MONITOR>IMAN22.MAC.51, 27-Jun-79 19:14:54, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.50, 27-Jun-79 16:32:55, Edit by LCAMPBELL
; Minor fixes
;<4.MONITOR>IMAN22.MAC.49, 22-Jun-79 00:17:10, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.48, 21-Jun-79 14:10:57, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.47, 19-Jun-79 13:58:35, Edit by LCAMPBELL
; Ignore leftover data available interrupts if input in progress
;<4.MONITOR>IMAN22.MAC.46, 18-Jun-79 14:55:08, Edit by LCAMPBELL
; Fix race in input service
;<4.MONITOR>IMAN22.MAC.45, 18-Jun-79 11:56:01, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.44, 18-Jun-79 11:53:26, Edit by LCAMPBELL
; Reboot AN22 when bringing net back up
;<4.MONITOR>IMAN22.MAC.43, 14-Jun-79 19:54:40, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.42, 13-Jun-79 11:52:05, Edit by LCAMPBELL
; Handle case of data arriving when handling input interrupt
;  and no buffers are available (flag interrupt for later)
;<4.MONITOR>IMAN22.MAC.41, 12-Jun-79 12:34:37, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.40,  8-Jun-79 15:19:44, Edit by LCAMPBELL
; Save a RDIOB, they're damn expensive
;<4.MONITOR>IMAN22.MAC.39,  8-Jun-79 15:14:34, Edit by LCAMPBELL
; Keep history of recent commands in ring buffer for debugging
;<4.MONITOR>IMAN22.MAC.38,  6-Jun-79 14:50:09, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.37,  4-Jun-79 18:03:33, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.36,  4-Jun-79 18:00:18, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.35, 31-May-79 10:23:46, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.34, 28-May-79 16:04:58, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.33, 28-May-79 16:02:32, Edit by LCAMPBELL
; Make AN2BUG a BUGCHK, get word count right at AN2LDR
;<4.MONITOR>IMAN22.MAC.32, 25-May-79 13:41:08, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.31, 25-May-79 13:12:08, Edit by LCAMPBELL
; Bug fixes, code cleanup
;<4.MONITOR>IMAN22.MAC.30, 24-May-79 18:04:57, Edit by LCAMPBELL
; Check to see if xmit done at xmit interrupt routines
;<4.MONITOR>IMAN22.MAC.29, 23-May-79 11:42:47, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.28, 22-May-79 11:11:49, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.27, 20-May-79 18:56:15, Edit by LCAMPBELL
; Allocate buffers at link time, not runtime
;<4.MONITOR>IMAN22.MAC.26, 16-May-79 17:04:27, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.25, 15-May-79 14:24:02, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.24, 14-May-79 18:03:18, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.23, 14-May-79 14:43:24, Edit by LCAMPBELL
; Fix handling of buffer pointers
;<4.MONITOR>IMAN22.MAC.22, 14-May-79 10:51:08, Edit by LCAMPBELL
; Add code to verify microcode and follow std KMC conventions more closely
;<4.MONITOR>IMAN22.MAC.21, 14-May-79 10:12:01, Edit by LCAMPBELL
; Fix AC usage at call to packing routine
;<4.MONITOR>IMAN22.MAC.20, 11-May-79 17:26:04, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.19, 11-May-79 16:15:18, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.18, 11-May-79 16:10:07, Edit by LCAMPBELL
; Random bug fixes
;<4.MONITOR>IMAN22.MAC.17,  4-May-79 11:52:29, Edit by LCAMPBELL
; Fix AC usage at BUTKMC
;<4.MONITOR>IMAN22.MAC.16, 19-Apr-79 09:43:44, Edit by LCAMPBELL
; Set AN2XIE before telling AN22 to transmit
;<4.MONITOR>IMAN22.MAC.15, 18-Apr-79 15:49:00, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.14, 18-Apr-79 15:39:48, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.13, 18-Apr-79 13:58:23, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.12, 18-Apr-79 12:32:08, Edit by LCAMPBELL
; Minor code reordering and listing cleanup
;<4.MONITOR>IMAN22.MAC.11, 18-Apr-79 09:52:40, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.10, 17-Apr-79 15:32:40, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.9, 17-Apr-79 15:29:04, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.8, 17-Apr-79 15:26:07, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.7, 17-Apr-79 15:15:23, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.6, 17-Apr-79 15:13:47, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.5, 17-Apr-79 15:11:40, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.4, 17-Apr-79 15:10:47, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.3, 17-Apr-79 15:09:56, Edit by LCAMPBELL
;<4.MONITOR>IMAN22.MAC.2, 17-Apr-79 14:34:25, Edit by LCAMPBELL
; Fix random assembly errors
;<LCAMPBELL.ARPANET>IMAN22.MAC.62, 17-Apr-79 14:33:51, Edit by LCAMPBELL
; Creation

;This software is furnished under a license and may only be used
;  or copied in accordance with the terms of such license.
;
;Copyright (C) 1979,1980 by Digital Equipment Corporation, Maynard, Mass.

	SEARCH MACSYM,MONSYM,IMPPAR,PROKS,PROLOG
	SALL
	.DIRECTIVE FLBLST

	TTITLE (IMPPHY,IMAN22,< - AN22 driver for 2020 - L. Campbell>)

;Nota bene:
;	Although this driver was written for a 2020, care has been taken
;	to write code that will run in nonzero sections, so that future
;	machines with extended addressing can use this driver.
;
; All interrupt routines save T1-T4, CX, CSR, and P, and dismiss through
; common exit code.
;
; Unlike IMPANX, the word count stored in NBBSZ on a receive is computed
; from HHSIZ and HHCNT in the H-H leader.  IMPANX computes it from the
; word count kept by the AN10.  These should agree, but watch out...
; (Note:  for special queues the word count supplied by the firmware
;  in the AN22 is believed, because there is no H-H leader)

	DEFAC (CSR,Q3)		; address of AN22 CSRs (control & status regs)
;Hardware definitions

AN2UBN==3			; UBA number AN22 sits on
AN2ADR==AN2UBN,,760110		; AN22 CSR address
AN2VEC==300			; AN22 vector A address

SEL0==0
BSEL0==0		; command handed to AN22
BSEL1==1		; maintenance register
				; contains microcode version number
KMCRUN==100000			; KMC run flop
KMCCLR==040000			; KMC master clear

SEL2==2			; BSEL2 and BSEL3 are occasionally referenced as a word
BSEL2==2		; bits
	AN2PDP==1		; PDP-11 mode (byte-swapping)
	AN2RDA==2		; receive data available
	AN2RDN==4		; receive done
	AN2XDN==10		; transmit done
	AN2HRD==20		; host ready
	AN2IRD==40		; IMP ready
	AN2ILX==100		; illegal command code
	AN2IWD==200		; IMP was down (ready line error)
BSEL3==3		; more bits, and fields
	AN2XIE==1		; transmit interrupt enable
	AN2RIE==2		; receive interrupt enable
	AN2XTC==34		; transmit termination code
MSKSTR XMTTM,,AN2XTC
	AN2RTC==340		; receive termination code
MSKSTR RCVTM,,AN2RTC
		TC.NEM==1		; not end of message
		TC.EOM==2		; end of message
		TC.NMP==3		; NXM fetching buffer pointer
		TC.NMB==4		; NXM accessing buffer
		TC.NNB==5		; non-negative byte count

SEL4==4			; SEL4 and SEL6 are addressed as words
SEL6==6
; AN22 command codes

C.NOOP==200		; no-op
C.SHRD==201		; set host ready (AN2HRD)
C.CHRD==202		; clear host ready (AN2HRD)
C.SRIE==203		; set receive interrupt enable (AN2RIE)
C.CRIE==204		; clear receive interrupt enable (AN2RIE)
C.SXIE==205		; set transmit interrupt enable (AN2XIE)
C.CXIE==206		; clear transmit interrupt enable (AN2XIE)
C.XPAR==207		; transmit partial message
C.XEOM==210		; transmit end of message (set LHB when done)
C.RECV==211		; start a receive
C.SPDP==212		; set PDP-11 mode (don't reorder bytes)
C.CIWD==213		; clear IMP was down (AN2IWD)
C.RSET==214		; reset
;	215-220		; reserved


; Random parameters

STY%NP==0		; number of words of leader padding requested
AN2STN==30		; size of interrupt stack
MXWPM==400		; maximum words per message
CMRNGN==^D64		; length of command ring buffer

; Storage

RS(AN2AC1,2)		; interrupt ACs
RS(AN2AC3,2)		;  ..
RS(AN2CSR,1)		;  ..
RS(AN2CX,2)		; also gets P
RS(AN2STK,AN2STN)	; interrupt stack
RS(AN2NMR,1)		; number of UBA mapping registers used
RS(INBUB,1)		; Unibus address of input buffer
RS(OUTBUB,1)		; Unibus address of output buffer
RS(AN2UBM,1)		; address of first UBA map register
RS(AN2XPC,10)		; XPCW block for vectored interrupts (2)
RS(AN2NRD,1)		; no. of times we had cmd ready but AN22 wasn't
RS(AN2BAD,1)		; no. of junk messages received
RS(INPON,1)		; input interrupts are enabled
RS(IOWDIU,1)		; Unibus address of input IOWD
RS(IOWDOU,1)		; Unibus address of output IOWD
RS(UBWADR,0)		;----start of area windowed into Unibus space
RS(IOWDI,1)		; input IOWD
RS(IOWDO,1)		; output IOWD
RS(AN2IBF,MXWPM)	; input buffer
RS(AN2OBF,MXWPM)	; output buffer
RS(UBWEND,0)		;----end of Unibus window
RS(CMRNGB,1)		; pointer to command ring buffer
RS(RING,CMRNGN)		; the ring buffer
RS(CMRNGE,0)		; end of ring buffer

UCDVER:	EXP 215			; microcode version we understand
; Boot the AN22, set up XPCW block, allocate buffers, etc.

IMPINI::MOVE T1,[AN2ADR]	; address of AN22
	CALL UBGOOD		; check to see if installed
	 RET			; no AN22
	XMOVEI T1,UBWADR	; address of first thing windowed to Unibus
	LSH T1,-^D9		; starting page of window
	XMOVEI T2,UBWEND	; last page of window
	LSH T2,-^D9		; ending page of window
	SUB T2,T1		; pages needed
	ADDI T2,1		;  ..
	MOVEM T2,AN2NMR		; remember how many mapping regs used
	MOVEI T1,AN2UBN		; UBA unit number
	CALL ALUBWA		; get UBA mapping registers
	 BUG(AN2NUA)
	MOVEM T1,AN2UBM		; save address of UBA mapping registers
	XMOVEI T1,UBWADR	; get -10 address of buffer space
	ANDI T1,777		; word offset within page
	LSH T1,2		; byte offset within page
	ADD T2,T1		; compute Unibus address of buffer space
	LSH T2,-2		; form Unibus -10-word address
	MOVEM T2,IOWDIU		; save Unibus addr of input IOWD
	ADDI T2,1		; next Unibus addr
	MOVEM T2,IOWDOU		;  is Unibus addr of output IOWD
	ADDI T2,1		; next is first buffer
	MOVEM T2,INBUB		; save Unibus address of input buffer
	ADDI T2,MXWPM		; plus length gives Unibus addr of next buffer
	MOVEM T2,OUTBUB		; save its Unibus address
	; ..
	; ..
	MOVE T1,AN2UBM		; addr of UBA mapping registers
	MOVEI T2,AN2IBF		; get address of AN22 buffers
	LSH T2,-^D9		; convert to page number
	IORI T2,UNBVBT		; flag valid
	MOVE T3,AN2NMR		; no. of mapping registers
IMPIN1:	WRIO T2,@T1		; set mapping register
	ADDI T2,1		; next page
	ADDI T1,1		; next map register
	SOJG T3,IMPIN1		; loop for all pages in window

	MOVSI T1,(<XPCW>)	; interrupt instruction
	HRRI T1,AN2XPC		; XPCW addr for vector A (output) interrupts
	SETZM 2(T1)		; clear interrupt flags
	XMOVEI T3,AN2ERR	; right now, any interrupt would be an error
	MOVEM T3,3(T1)		; set interrupt routine addr
	MOVEI T2,AN2VEC/4	; offset for vector
	ADD T2,SMTEPT+AN2UBN	; get addr of vector table
	MOVEM T1,(T2)		; set vector
	ADDI T1,4		; XPCW addr for vector B (input) interrupts
	SETZM 2(T1)		; flags for vector B
	XMOVEI T3,AN2ERR	; any interrupts now are garbage
	MOVEM T3,3(T1)		; interrupt routine for vector B
	MOVEM T1,1(T2)		; set vector B
	MOVEI T1,RING-1		; init command ring buffer pointer
	MOVEM T1,CMRNGB		;  ..
	RET
; Boot KMC11 and start microcode

BUTKMC::ACVAR<JFN,CKS>		; JFN of ucode file, checksum
	MOVE CSR,[AN2ADR]	; address of AN22 Unibus registers
	MOVX T1,GJ%SHT
	HRROI T2,[ASCIZ /PS:<SYSTEM>AN22.MICROCODE/]
	GTJFN
	 ERJMP [MOVE T1,LSTERR
		BUG(AN2CFM,<<T1,LSTERR>>)
		RET]
	MOVX T2,OF%RD+<070000,,0>
	OPENF
	 ERJMP [MOVE T1,LSTERR
		BUG(AN2COM,<<T1,LSTERR>>)
		RET]
	MOVE JFN,T1

BUTKM1:	MOVE T1,JFN
	BIN			; get another byte
	JUMPE T2,[GTSTS			; see if EOF
		TXNN T2,GS%EOF		; is it?
		JRST BUTKM1		; no, ignore embedded nulls
		JRST BUTKM2]		; yes, finish up
	CAIN T2,";"		; comment line?
	JRST [	CALL SKPLIN		; yes, skip rest of line
		JRST BUTKM1]		; and go for more
	CAIN T2,"C"		; CRAM line?
	JRST BTCRAM		; yes, handle it
	CAIN T2,"D"		; DRAM line?
	JRST BTDRAM		; yes
BUTKMX:	BUG(AN2ULM)
	CALL SKPLIN		; ignore the line
	JRST BUTKM1

BUTKM2:	MOVE T1,JFN		; finish up
	CLOSF
	 JFCL
	RETSKP			; give good return
; Skip a line of microcode

SKPLIN:	MOVE T1,JFN
	BIN
	JUMPE T2,[GTSTS			; see if EOF
		TXNN T2,GS%EOF		; is it?
		JRST SKPLIN		; no, ignore imbedded nulls
		JRST BUTKM2]		; yes, finish up
	CAIE T2,12		; LF?
	JRST SKPLIN		; no, keep looking
	RET

; Get a word of data from ASCIIized microcode file

GETWD:	SETZ T4,		; init sum
	MOVE T1,JFN
GETWD1:	BIN			; get a character
	JUMPE T2,[GTSTS			; see if EOF
		TXNN T2,GS%EOF		; is it?
		JRST GETWD1		; no, ignore nulls
		JRST BUTKM2]		; yes, finish up
	CAIE T2,","		; comma?
	CAIN T2,15		; or carriage return?
	JRST [	MOVE T1,T4		; yes, return what we've got
		RET]
	LSH T4,6
	CAIL T2,100		; do funny conversion
	SUBI T2,100
	ADD T4,T2
	JRST GETWD1
; Boot a line of CRAM

BTCRAM:	MOVE T1,JFN		; next byte
	BIN
	CAIE T2," "		; should be space
	JRST BUTKMX		; catch-all error handler
	CALL GETWD		; get word count
	JUMPE T1,BTXFER		; zero means this is transfer block
	MOVE P1,T1		; save
	MOVE CKS,T1		; start running checksum
	CALL GETWD		; get load address
	MOVE P2,T1		; save
	ADD CKS,T1		; compute checksum

BTCRM1:	CALL GETWD		; next data word
	ADD CKS,T1		; keep running checksum
	MOVEI T2,4		; select CRAM
	WRIOB T2,BSEL1(CSR)	;  ..
	WRIO P2,SEL4(CSR)	; address into which to load it
	WRIO T1,SEL6(CSR)	; plug instruction into KMC register
	MOVEI T2,40		; toggle CRAM write
	BSIOB T2,BSEL1(CSR)	; ..
	SETZ T2,		; clear SEL0
	WRIO T2,SEL0(CSR)	;  ..
	WRIO T2,SEL4(CSR)	;  and 4 and 6
	WRIO T2,SEL6(CSR)	;  ..
	MOVEI T2,4		; set RAM O
	WRIOB T2,BSEL1(CSR)	;  ..
	WRIO P2,SEL4(CSR)	; set PC to read
	RDIO T2,SEL6(CSR)	; read the word
	CAME T1,T2		; does it match what we thought we loaded?
	BUG(AN2MCV,<<T1,DESIRD>,<T2,ACTUAL>>)
	SETZ T1,
	WRIO T1,SEL0(CSR)	; clear bits
	ADDI P2,1		; bump load address
	SOJG P1,BTCRM1		; do for all words in this frame

	CALL GETWD		; get line checksum
	ANDI CKS,177777		; keep only 16 bits of running checksum
	ADD CKS,T1		; see if checksum OK
	TRNE CKS,177777		;  ?
	BUG(AN2CKS)
	CALL SKPLIN		; skip end of this line
	JRST BUTKM1		; go do next line
; Boot line of KMC11 DRAM

BTDRAM:	BUG(AN2DNS)
	CALL SKPLIN
	JRST BUTKM1

; Transfer block

BTXFER:	CALL GETWD		; get start address
	MOVE P1,T1		; save it
	CALL GETWD		; get checksum
	MOVE CKS,P1		; see if it checks
	ADD CKS,T1		;  ..
	TRNE CKS,177777
	BUG(AN2CEX)
	MOVEI T1,KMCCLR		; KMC11 master clear
	WRIO T1,SEL0(CSR)	; clear it
	HRRZ T1,P1		; get start address
	ANDI T1,1400		; get high order two bits
	LSH T1,3		; position for strange KMC architecture
	HRRZ T2,P1		; get address again
	ANDI T2,377		; low-order 8 bits
	IOR T1,T2		; form address in split (KMC) format
	IORI T1,100400		; make an unconditional branch instruction
	WRIO T1,SEL6(CSR)	;  (See KMC11 Programmer's Manual for
	MOVEI T2,2		;  an explanation of this esoteric
	WRIOB T2,BSEL1(CSR)	;  procedure - page 2-13)
	MOVEI T2,3		; execute the instruction
	BSIOB T2,BSEL1(CSR)	;  ..
	MOVEI T2,7		; clear leftover bits
	BCIOB T2,BSEL1(CSR)	;  ..
	MOVEI T1,KMCRUN		; KMC11 run flop
	WRIO T1,SEL0(CSR)	; start the little bugger
	CALL SKPLIN		; eat the line feed
	JRST BUTKM1
; Clock level check routine

IMPCHK::MOVEI T1,^D1000		; call this every second
	MOVEM T1,IMPTM2
	SKIPN IMPRDY		; net on?
	RET			; no, quit now
	CALL IMPRLQ		; IMP is or was down?
	 SKIPL IMPRDT		;  and not already noticed?
	RET			; no, no need to do anything
	CALL IMIERR		; yes, get it noticed
	AOS IMPFLG		; cause NCP fork to run
	RET

; Start input

IMISRT::CALL IMPRLQ		; IMP up?
	 RET			; no, do nothing
	SOSL IMPNFI		; get next free input buffer
	SKIPN T1,IMPFRI		;  ..
	 BUG(IMPNII)
	LOAD T2,NBQUE,(T1)	; get address of next buffer
	MOVEM T2,IMPFRI		; point head of list at it
	SETZRO NBQUE,(T1)	; unlink this buffer
	MOVEM T1,IMIB		; remember this one
	SETZM IMPIOV		; clear overflow flag
	XMOVEI T1,AN2RC1	; interrupt dispatch address
	MOVEM T1,AN2XPC+7	; set it up
	MOVE CSR,[AN2ADR]	; address of AN22 registers
	RDIOB T1,BSEL2(CSR)	; see if data already available
	TXNE T1,AN2RDA		; any there?
	CALL AN2STI		; yes, start input already
	SKIPE INPON		; input interrupts already enabled?
	RET			; yes, save time
	MOVEI T1,C.SRIE		; set receive interrupt enable
	SETOM INPON		;  and return
	CALL AN2CMD		;  ..
	RET
; These magic numbers are stored in IMITYP and IMOTYP to steer packing
;  and unpacking decisions for input and output, respectively

.IINC6==0			; 36-bit NCP message
.IINC2==1			; 32-bit NCP message
.IITNT==2			; Telenet link
.IISPQ==3			; special queue
.IIINT==4			; Internet
.IIMLC==5			; MLC (PTIP) format

; Routines to call to pack a buffer read from AN22 into a net buffer

PACK:	IFIW!PK3236		; 36-bit NCP
	IFIW!PK3232		; 32-bit NCP
	IFIW!PAKBUG		; Telenet not supported by DEC
	IFIW!PKSPQ		; special queue
	IFIW!PAKBUG		; Internet not supported by DEC
	IFIW!PAKBUG		; PTIP not supported by DEC

; Routines to call to unpack a buffer from net buffer into AN22 output buffer

UNPACK:	IFIW!UP3632		; 36-bit NCP
	IFIW!UP3232		; 32-bit NCP
	IFIW!PAKBUG		; Telenet not supported by DEC
	IFIW!UP3232		; special queue
	IFIW!PAKBUG		; Internet not supported by DEC
	IFIW!PAKBUG		; PTIP not supported by DEC

PAKBUG:	BUG(AN2BPK)
	RET
;Input ready

AN2RC1:	DMOVEM T1,AN2AC1	; save ACs
	DMOVEM T3,AN2AC3
	DMOVEM CX,AN2CX
	MOVEM CSR,AN2CSR
	MOVE CSR,[AN2ADR]	; address of AN22
	MOVE P,[-AN2STN,,AN2STK]
	RDIOB T1,BSEL2(CSR)	; get byte with useful bits in it
	TXNN T1,AN2ILX		; illegal command interrupt?
	TXNN T1,AN2RDA		;  or receive data available not set?
	CALL AN2BUG		; yes, BUGCHK
	CALL AN2STI		; start input
	JRST AN2RCX		; restore regs and dismiss

; Here to actually start input

AN2STI:	SKIPN IMIB		; make sure input buffer is ready
	BUG(AN2BNR)
	RDIOB T1,BSEL2(CSR)	; get status bits
	TXNN T1,AN2RDN		; insure no input in progress
	CALL AN2BUG		; there is, gripe
	MOVSI T1,-^D17		; read I-H leader and H-H leader (136 bits)
	HRR T1,INBUB		; Unibus address of buffer
	MOVEM T1,IOWDI		; store the IOWD
	MOVE T1,IOWDIU		; Unibus addr of IOWD
	WRIO T1,SEL4(CSR)	; point AN22 at IOWD
	XMOVEI T1,AN2RC2	; next interrupt dispatch
	MOVEM T1,AN2XPC+7	; set it
	MOVEI T1,C.RECV		; receive command code
	CALLRET AN2CMD		; whomp it in there


AN2ERR:	DMOVEM T1,AN2AC1	; funny interrupt
	DMOVEM T3,AN2AC3
	DMOVEM CX,AN2CX
	MOVEM CSR,AN2CSR
	MOVE P,[-AN2STN,,AN2STK]
	MOVE CSR,[AN2ADR]
	CALL AN2BUG		; report error
	JRST AN2RCX		; restore regs and dismiss


; Here on random AN22 bugs

AN2BUG:	RDIOB T1,BSEL0(CSR)	; get bad command code
	RDIO T2,SEL2(CSR)	;  and interesting bits
	MOVE T3,0(P)		;  and return address
	BUG(AN2BG1,<<T1,BSEL0>,<T2,SEL2>,<T3,CALLER>>)
	RET
; Here when I-H leader and H-H leader (if present) have been read

AN2RC2:	DMOVEM T1,AN2AC1	; save standard ACs
	DMOVEM T3,AN2AC3
	DMOVEM CX,AN2CX
	MOVEM CSR,AN2CSR
	MOVE P,[-AN2STN,,AN2STK]
	MOVE CSR,[AN2ADR]
	RDIOB T1,BSEL2(CSR)	; get byte with interesting bits
	TXNE T1,AN2ILX		; illegal command interrupt?
	CALL AN2BUG		; yes, complain
	TXNN T1,AN2RDN		; receive done?
	JRST AN2RCX		; no, probably leftover data available int
				;  posted while we were in interrupt service
	TXNE T1,AN2IWD		; was IMP down?
	CALL IMIERR		; yes, note that
	MOVEI T1,AN2IBF		; physical address of AN22 input buffer
	LSH T1,-^D9		; page number
	CALL MONCLR		; invalidate cache for that page
	MOVEI T2,AN2IBF		; get address of buffer back
	ADDI T2,MXWPM		; compute ending address
	LSH T2,-^D9		; compute ending page
	EXCH T1,T2		; in case call to MONCLR needed
	CAME T1,T2		; buffer crosses page boundary?
	CALL MONCLR		; yes, invalidate second page
	MOVEI T1,AN2IBF		; pack from
	MOVE T2,IMIB		; to
	ADDI T2,.NBLD0		; first word of leader
	MOVEI T3,5		; 17 8-bit bytes fits in 5 32-bit words
	CALL PK3236		; pack the data
	MOVE T1,IMIB		; address of newly packed data
	LOAD T2,IHHST,(T1)	; get host and link
	LOAD T3,IHLNK,(T1)	;  ..
	CAIGE T2,FKHOST		; outside NCP range?
	CAILE T3,LLINK		;  ..
	JRST [	DMOVE T2,.NBLD2(T1)	; yes, no H-H leader then
		LSHC T2,^D20		; so get first 32 bits of data
		LSH T2,^D4		; justify
		DMOVEM T2,.NBHHL(T1)	; save it and the next 8 bits
		JRST AN2R2A]
	DMOVE T2,.NBLD2(T1)	; get H-H leader (crosses word boundary)
	LSHC T2,-^D8		; position to where IMPDV expects it
	STOR T2,IHPD1,(T1)	; high-order 4 bits here (.NBLD2)
	MOVEM T3,.NBHHL(T1)	;  and low 36 bits here
AN2R2A:	RDIOB T1,BSEL3(CSR)	; get byte with termination codes
	LOAD T1,RCVTM,T1	; get receive termination code
	CAIN T1,TC.EOM		; end of message?
	JRST AN2EOM		; yes, handle it
	CAIE T1,TC.NEM		; receive OK without EOM?
	CALL AN2BUG		; no, complain
	XMOVEI T1,AN2RC3	; next interrupt dispatch
	MOVEM T1,AN2XPC+7	; set it up
	MOVNI T1,MXWPM		; negative max word count
	LSH T1,2+^D18		; Unibus byte count in left half
	HRR T1,INBUB		; Unibus address of input buffer
	MOVEM T1,IOWDI		; save IOWD
	MOVE T1,IOWDIU		; Unibus addr of IOWD
	WRIO T1,SEL4(CSR)	; point AN22 at IOWD
	MOVEI T1,C.RECV		; receive command code
	CALL AN2CMD		; hand the command to the AN22
	JRST AN2RCX		; restore ACs and dismiss
;Here when user data has been completely read

AN2RC3:	DMOVEM T1,AN2AC1	;save ACs
	DMOVEM T3,AN2AC3
	DMOVEM CX,AN2CX
	MOVEM CSR,AN2CSR
	MOVE P,[-AN2STN,,AN2STK]
	MOVE CSR,[AN2ADR]	;address of AN22 registers
	RDIOB T1,BSEL2(CSR)	;get byte with illegal command bit
	TXNN T1,AN2ILX		;illegal command?
	TXNN T1,AN2RDN		; or receive done not set?
	CALL AN2BUG		;yes to either, die
	RDIOB T1,BSEL3(CSR)	;get byte with rcv termination code
	LOAD T1,RCVTM,T1	;get termination code
	CAIN T1,TC.NEM		;not end of message?
	JRST [	AOS IMPIOV		;yes, flag overflow
		MOVNI T1,MXWPM		; ..
		LSH T1,2+^D18		;Unibus byte count
		HRR T1,INBUB		;Unibus addr of input buffer
		MOVEM T1,IOWDI		; save IOWD
		MOVE T1,IOWDIU		; Unibus addr of IOWD
		WRIO T1,SEL4(CSR)	; ..
		MOVEI T1,C.RECV		;receive command code
		CALL AN2CMD		;hand it off
		JRST AN2RCX]		;dismiss the interrupt
;	JRST AN2EOM
AN2EOM:	CAIE T1,TC.EOM		; bad termination code?
	CALL AN2BUG		; yes, note the error
	MOVEI T2,AN2IWD		; was IMP down?
	TIOEB T2,BSEL2(CSR)	;  ??
	CALL IMIERR		; yes, note that
	AOSG IMPFLS		; flushing messages?
	JRST [	AOS AN2BAD		; yes, count bad messages
		JRST AN2RC5]		; discard it
	SKIPE IMPIOV		; overflow happened?
	JRST [	BUG(AN2OVF)
		AOS AN2BAD		; count discarded msgs
		JRST AN2RC5]		; gobble that goop
	MOVE T1,IMIB		; get buffer address
	LOAD T3,IHFTY,(T1)	; new format flag
	CAIE T3,ITY%LL		; long leader?
	JRST [	AOS AN2BAD		; no, count discards
		JRST AN2RC5]		; flush it
	LOAD T3,IHMTY,(T1)	; get message type
	CAIE T3,.IHIGD		; one without a message-id (link)?
	CAIN T3,.IHDHS		;  ..
	JRST AN2IRM		; yes, give leader to NCP (no user data)
	CAIN T3,.IHNOP		; link also meaningless on NOPs
	JRST AN2IRM		;  ..
	MOVEI T2,.IINC6		; assume NCP 36-bit packing
	MOVEM T2,IMITYP		;  ..
	; ..
	; ..

	LOAD T2,IHHST,(T1)	; see whether NCP irregular message
	LOAD T4,IHLNK,(T1)	; get host and link
	CAIGE T2,FKHOST		; from a fake host?
	CAILE T4,LLINK		;  or non-NCP link?
	JRST [	MOVEI T2,.IISPQ		; special queue formatting then
		MOVEM T2,IMITYP		; remember it
		JRST .+2]		;  and place onto NCPFRK queue
	JUMPN T3,AN2IRM		; non-zero message type is irregular
	MOVEI T1,AN2IBF		; address of AN22 input buffer
	LSH T1,-^D9		; page number
	CALL MONCLR		; invalidate cache for that page
	MOVEI T2,AN2IBF		; get address of buffer back
	ADDI T2,MXWPM		; compute ending address
	LSH T2,-^D9		; compute ending page
	EXCH T1,T2		; in case call to MONCLR needed
	CAME T1,T2		; buffer crosses page boundary?
	CALL MONCLR		; yes, invalidate second page
	MOVE T1,IMIB		; current net buffer for input
	LOAD T2,HHSIZ,(T1)	; get connection byte size
	MOVE T3,IMITYP		; special queues?
	CAIN T3,.IISPQ		;  ..
	JRST AN2RC7		; yes, don't change packing decision now
	MOVEI T3,.IINC2		; 32-bit NCP packing?
	CAIE T2,^D36		; (non-36-bit-bytes get 32-bit packing)
	MOVEM T3,IMITYP		; yes, remember that
AN2RC7:	HRRZ T3,IOWDI		; Get carcass of IOWD
	HRRZ T2,INBUB		; Unibus addr of input buffer
	SUBI T3,-1(T2)		; Compute no. of 32-bit words read
	MOVEI T2,.NBDW0(T1)	; where to put the data
	MOVEI T1,AN2IBF		; where raw input lives
	MOVE T4,IMITYP		; get buffer type
	CALL @PACK(T4)		; call appropriate packing routine
	MOVE T1,IMIB		; get address of NCP buffer
	MOVE T3,IMPIBI		; add to NCP fork's queue
	JUMPN T3,AN2RC4		; anything on it already?
	MOVEM T1,IMPIBO		; no, this is only entry then
	SKIPA			; don't chain it
AN2RC4:	HRLM T1,0(T3)		; add buffer to input queue
	MOVEM T1,IMPIBI
	MOVE T3,IMITYP		; special queue?
	CAIE T3,.IISPQ		;  ..
	JRST AN2RC8		; no, believe H-H leader then
	HRRZ T2,IOWDI		; yes, no H-H leader, so figure count
	HRRZ T3,INBUB		;  from hardware word count
	SUBI T2,(T3)		;  minus one because we put data into .NBHHL
	JRST AN2RC9		; pass this count on to NCP
AN2RC8:	LOAD T3,HHSIZ,(T1)	; get connection byte size
	LOAD T2,HHCNT,(T1)	;  and byte count for this msg
	IMULI T2,(T3)		; compute bits received
	MOVEI T4,^D36		; assume 36 bits per word
	CAIE T3,^D36		; is it really?
	MOVEI T4,^D32		; no, must be 32 then
	ADDI T2,-1(T4)		; round up
	IDIV T2,T4		; compute words in buffer
AN2RC9:	ADDI T2,.NBDW0+1	; add length of leaders and count word
	STOR T2,NBBSZ,(T1)	; save for IMPDV
	AOS IMPFLG		; nudge job zero
;	JRST AN2RC6
AN2RC6:	SETZM IMIB		; no current input buffer for NCP
	SKIPLE IMPNFI		; any more buffers available?
	JRST [	CALL IMISRT		; yes, start input
		JRST AN2RCX]		; and dismiss
	SETZM INPON		; remember that input is off
	MOVEI T1,C.CRIE		; clear receive interrupt enable
	CALL AN2CMD		;  ..
	XMOVEI T1,AN2RCY	; while disabled, just flush interrupts
	MOVEM T1,AN2XPC+7	;  (this is necessary because one might be
				;  pending now)

AN2RCX:	DMOVE T1,AN2AC1
	DMOVE T3,AN2AC3
	DMOVE CX,AN2CX
	MOVE CSR,AN2CSR
AN2RCY:	XJEN AN2XPC+4


; Here with irregular message

AN2IRM:	CALL IMP8XQ		; hand to the NCP
	AOS IMPFLG		; poke job zero
				; fall through

; Here to release buffer

AN2RC5:	MOVE T1,IMIB		; get current net buffer's address
	MOVE T2,T1		; copy to T2
	EXCH T1,IMPFRI		; cause to be first free
	HRLM T1,0(T2)		; chain onto old first free
	AOS IMPNFI		; count one more free buffer
	JRST AN2RC6		; start input and dismiss
; Here from PI level, or NCPFRK, to start output going out
; Call:
;	JSP T4,IMPXOU

IMPXOU::PIOFF
	SKIPN IMPOB		; any output in progress?
	 JRST AN2XO1		; no
	PION			; yes, quit
	JRST 0(T4)

AN2XO1:	SETOM IMPOB		; flag output in progress
	PION
	PUSH P,T4		; put return addr on stack
AN2IOU:	CALL IMPRLQ		; IMP up?
	 JRST [	SETZM IMPOB		; no, clear output-in-progress
		RET]			; and quit
	SKIPLE NOPCNT		; any NOP's still pending?
	JRST IOUNOP		; yes, send one
	MOVE T1,IMPHBO		; high priority message waiting?
	JUMPE T1,AN2IOL		; no, check for low priority
	HLRZ T2,0(T1)		; yes, does it have a successor?
	JUMPN T2,AN2IO1		; yes
	SETZM IMPHBI		; no, flag queue empty
	SKIPA			; keep IMPHBO zero
AN2IO1:	HRLI T2,ANBSEC		; section buffers are in
	MOVEM T2,IMPHBO		; dequeue this one
	JRST AN2IOC		; send it

AN2IOL:	SKIPN T1,IMPOBO		; any messages waiting?
	JRST [	SKIPE T2,HSTGDM		; any host-going-down msgs?
		JRST IOUHGD		; yes, send it
		XMOVEI T1,AN2DN2	; reset interrupt dispatch
		MOVEM T1,AN2XPC+3	;  ..
		SETZM IMPOB		; flag no output in progress
		RET]			; return
	HLRZ T2,0(T1)		; get successor
	JUMPN T2,AN2IO2		; is there one?
	SETZM IMPOBI		; no, empty the queue
	SKIPA			; keep tail ptr zero
AN2IO2:	HRLI T2,ANBSEC		; set section number
	MOVEM T2,IMPOBO		; dequeue this buffer
	; ..
	; ..

AN2IOC:	MOVEM T1,IMPOB		; remember this buffer
	SETZRO NBQUE,(T1)	; remove from old chain
	LOAD T2,IHHST,(T1)	; Check host and link for NCP range
	LOAD T3,IHLNK,(T1)	;  ..
	CAIGE T2,FKHOST		; Special to-imp group?
	CAILE T3,LLINK		; Or link out of range?
	JRST [	MOVEI T3,.IISPQ	; Yes, special queue formatting.
		JRST AN2IOD]
	LOAD T2,HHSIZ,(T1)	; NCP will have set up packing mode
	CAIE T2,^D36		; Is it 36 bit mode?
	SKIPA T3,[.IINC2]	; No, it is 8 or 32.
	MOVEI T3,.IINC6		; Select 36 bit mode
AN2IOD:	MOVEM T3,IMOTYP		; remember packing decision
	MOVEI T1,1(T1)		; first word of I-H leader
	MOVEI T2,AN2OBF		; addr of AN22 output buffer
	MOVEI T3,^D3		; just move I-H leader (3 36-bit words)
	CALL UP3632		; shuffle those bits!
	MOVE T1,IMPOB		; recover net buffer address
	MOVEI T2,AN2OBF		;  and output buffer address
	MOVE T3,.NBHHL(T1)	; get H-H leader (36 bits of it)
	LSH T3,-^D6		; position for output buffer
	HLLM T3,3(T2)		; do first two bytes
	LSH T3,-2		; make the 2-bit gap
	HRRM T3,3(T2)		; next two bytes
	SETZM 4(T2)		; last byte is always zero
	XMOVEI T1,AN2LDR	; where to go on next output interrupt
	MOVEM T1,AN2XPC+3	; set up interrupt dispatch
	MOVE CSR,[AN2ADR]	; AN22 Unibus address
	MOVSI T1,-^D17		; byte count
	MOVE T2,IMOTYP		; special queue?
	CAIN T2,.IISPQ		;  ..
	MOVSI T1,-^D12		; yes, no H-H leader to send, then
	HRR T1,OUTBUB		; Unibus address of output buffer
	MOVEM T1,IOWDO		; save it
	MOVE T1,IOWDOU		; Unibus addr of same
	WRIO T1,SEL6(CSR)	; hand off to AN22
	MOVE T2,IMPOB		; addr of net buffer
	LOAD T2,NBBSZ,(T2)	; get size of this message
	MOVEI T1,C.XPAR		; transmit partial message
	CAIG T2,.NBHHL		;  unless this is a short one
	MOVEI T1,C.XEOM		;  in which case turn on EOM bit at end
	CALL AN2CMD		; nudge the AN22 and return
	RET			; (don't change to CALLRET)
; Here when both leaders have been sent

AN2LDR:	DMOVEM T1,AN2AC1	; save ACs
	DMOVEM T3,AN2AC3
	DMOVEM CX,AN2CX
	MOVEM CSR,AN2CSR
	MOVE CSR,[AN2ADR]
	MOVE P,[-AN2STN,,AN2STK]
	RDIOB T1,BSEL2(CSR)	; fetch useful bits
	TXNN T1,AN2ILX		; illegal command?
	TXNN T1,AN2XDN		;  or transmit done not set?
	CALL AN2BUG		; yes, go die
	RDIOB T2,BSEL3(CSR)	; get byte with termination code
	LOAD T2,XMTTM,T2	; get termination code
	CAIN T2,TC.EOM		; end of message (short message)?
	JRST AN2DN1		; yes, go finish up
	CAIE T2,TC.NEM		; termination code OK?
	CALL AN2BUG		; no, complain
	MOVE T1,IMPOB		; addr of this net buffer
	MOVEI T2,AN2OBF		; -10 addr of AN22 output buffer
	LOAD T3,NBBSZ,(T1)	; get size of buffer
	SUBI T3,.NBHHL		; account for leader already sent
	XMOVEI T1,.NBDW0(T1)	; first word of user data
	MOVE T4,IMOTYP		; get packing type index
	CAIN T4,.IISPQ		; special queues?
	JRST [	SUBI T1,<.NBDW0-.NBHHL>	; yes, data starts one word earlier
		ADDI T3,<.NBDW0-.NBHHL>	;  hence have one more to send 
		JRST .+1]
	CALL @UNPACK(T4)	; shuffle those bits!
	MOVE T1,IMPOB		; addr of net buffer
	LOAD T2,NBBSZ,(T1)	; get word count
	MOVE T4,IMOTYP		; special queue?
	CAIN T4,.IISPQ		;  ..
	JRST AN2LD0		; yes, no adjustments necessary
	LOAD T3,HHSIZ,(T1)	; get byte size
	CAIN T3,^D36		; 36-bit mode?
	JRST [	IMULI T2,^D36		; yes, must adjust word count
		IDIVI T2,^D32		;  because of 32-bit packing
		JRST AN2LD0]		;  ..
AN2LD0:	LSH T2,2		; make byte count
	MOVN T2,T2		; negate
	ADDI T2,^D12		; account for I/H leader already sent
	CAIE T4,.IISPQ		; special queue?  if so, no H-H leader
	ADDI T2,^D5		; no, account for H-H leader already sent
	LSH T2,^D18		; move to left half
	HRR T2,OUTBUB		; Unibus addr of AN22 buffer
	MOVEM T2,IOWDO		; save it
	MOVE T2,IOWDOU		; Unibus addr of same
	WRIO T2,SEL6(CSR)	; tell AN22 where to find IOWD
	XMOVEI T1,AN2DN0	; where to go when done with this output
	MOVEM T1,AN2XPC+3	; set up interrupt dispatch
	MOVEI T1,C.XEOM		; transmit end of message
	CALL AN2CMD		;  ..
	DMOVE T1,AN2AC1		; restore ACs
	DMOVE T3,AN2AC3
	DMOVE CX,AN2CX
	MOVE CSR,AN2CSR
	XJEN AN2XPC		; dismiss interrupt
; Make a Host Going Down message

IOUHGD:	MOVE T1,H2IHGD		; get template for msg
	SETZ T3,		; build msg in T1-T3
	LSHC T2,-^D32
	LSH T2,2		; waste bits, UBA-style
	LSHC T2,^D16
	LSH T3,-2		; and again
	JRST IOUIRG		; common irregular msg sender

; Here to send NOP to IMP

IOUNOP:	SOS NOPCNT		; count NOPs
	DMOVE T1,H2INOP		; get template NOP
	MOVE T3,H2INOP+2	; fall through

; Send irregular message, message in T1-T3 in AN22 format

IOUIRG:	MOVEI T4,AN2OBF		; -10 addr of AN22 output buffer
	DMOVEM T1,0(T4)		; build message
	MOVEM T3,2(T4)		;  ..
	MOVE CSR,[AN2ADR]	; AN22 address
	MOVSI T1,-^D12		; 12 bytes in an irregular msg
	HRR T1,OUTBUB		; Unibus addr of AN22 output buffer
	MOVEM T1,IOWDO		; stuff it
	MOVE T1,IOWDOU		; Unibus addr of IOWD
	WRIO T1,SEL6(CSR)	; inform AN22 of this
	XMOVEI T1,AN2DN2	; output interrupt dispatch
	MOVEM T1,AN2XPC+3	; set it up
	MOVEI T1,C.XEOM		; transmit end of message
	CALL AN2CMD		;  ..
	RET			; and return (DON'T change to CALLRET)

;Templates of the only two Host-to-IMP irregular messages we ever send

H2INOP:				; the 2-bit fields are UBA-ignored bits
	BYTE (2)0(4)0,ITY%LL (8)0(2)0(8)0(8).IHNOP
	EXP 0
	EXP 0			; padding control is here, happens to be zero

H2IHGD:				; the 2-bit fields are UBA-ignored bits
	BYTE (2)0(4)0,ITY%LL (8)0(2)0(8)0(8).IHHGD
; Here on output done interrupt for a real buffer (regular message)

AN2DN0:	DMOVEM T1,AN2AC1	; save interrupt ACs
	DMOVEM T3,AN2AC3
	DMOVEM CX,AN2CX
	MOVEM CSR,AN2CSR
	MOVE CSR,[AN2ADR]
	MOVE P,[-AN2STN,,AN2STK]
	RDIOB T1,BSEL2(CSR)	; get byte with "illegal command" bit
	TXNN T1,AN2ILX		; was it one?
	TXNN T1,AN2XDN		;  or transmit done not set?
	CALL AN2BUG		; yes, BUGCHK
	RDIOB T2,BSEL3(CSR)	; get byte with termination code
	LOAD T2,XMTTM,T2	; get termination code
	CAIE T2,TC.EOM		; OK?
	CALL AN2BUG		; no, complain
	TXNE T1,AN2IWD		; ready line error?
	CALL IMIERR		; yes, note that
AN2DN1:	CALL IMODUN		; post completion and free buffer
	MOVE T1,IMPOB
	CALL IMULKB		; unlock buffer
AN2UBO:	SETZM IMPOB		; note no output in progress
	CALL AN2IOU		; start output if any ready to go
	DMOVE T1,AN2AC1		; restore ACs
	DMOVE T3,AN2AC3
	DMOVE CX,AN2CX
	MOVE CSR,AN2CSR
	XJEN AN2XPC		;  and dismiss

; Here on output done interrupt for irregular message (so no net buffer
;  to be released)

AN2DN2:	DMOVEM T1,AN2AC1
	DMOVEM T3,AN2AC3
	DMOVEM CX,AN2CX
	MOVEM CSR,AN2CSR
	MOVE P,[-AN2STN,,AN2STK]
	MOVE CSR,[AN2ADR]
	RDIOB T1,BSEL2(CSR)	; illegal command interrupt?
	TXNN T1,AN2ILX		;  ..
	TXNN T1,AN2XDN		;  or transmit done not set?
	CALL AN2BUG		; yes, BUGCHK
	TXNE T1,AN2IWD		; ready line error?
	CALL IMIERR		; yes, complain
	RDIOB T1,BSEL3(CSR)	; get byte with termination code
	LOAD T1,XMTTM,T1	; get termination code
	CAIE T1,TC.EOM		; end of message?
	CALL AN2BUG		; no, gripe
	JRST AN2UBO		; go start output if possible and dismiss
;Pack data stored UBA-style into 36-bit words
; Note that the AN22 reverses the bytes before storing into -10
; memory, to save the -10 the byte-swapping time
;Call:
;	T1/ source
;	T2/ dest
;	T3/ word count (32-bit words)

PK3236:	SAVEP
	DMOVE P1,T1		; copy args
	MOVE P3,T3
PK26B:	MOVE T2,(P1)		; get 32 bits
	AND T2,[-1,,177777]	; clear 2 high order bits which we expect 
	ADDI T2,(T2)		;    cleared anyway
	ADDI T2,(T2)
	LSH T2,2		; concatenate and left justify
	MOVEM T2,(P2)		; store 32 bits
	SOJLE P3,R		; count words fetched, quit if exhausted
	MOVSI T4,-^D8		; offset counter
PK26A:	ADDI P1,1		; next source word
	MOVE T2,(P1)		; next 32 bits
	AND T2,[-1,,177777]	; clear 2 high order bits which we expect 
	ADDI T2,(T2)		;    cleared anyway
	ADDI T2,(T2)
	LSH T2,2		; dump unused bits
	SETZ T1,		; clear unused bits
	LSHC T1,@SHIFT(T4)	; 4,8,12,16,20,24,28,32 bits
	IORM T1,(P2)		; fill out righthand end of dest word
	ADDI P2,1		; next dest word
	LSHC T1,^D36		; left-justify remainder
	MOVEM T1,(P2)		; save leftmost bits of next word
	SOJLE P3,R		; if done, quit
	AOBJN T4,PK26A		; do it 8 times
	ADDI P1,1		; next source word
	JRST PK26B		; loop

SHIFT:	EXP ^D4
	EXP ^D8
	EXP ^D12
	EXP ^D16
	EXP ^D20
	EXP ^D24
	EXP ^D28
	EXP ^D32		; amount to shift remainder of word
;Pack data stored UBA style into 32-bit words
; (i.e., concatenate and left-justify it)
;Call:
;	T1/ source
;	T2/ dest
;	T3/ word count (32-bit words)

PK3232:	MOVE T4,(T1)		;get source word
	AND T4,[-1,,177777]	; clear 2 high order bits which we expect 
	ADDI T4,(T4)		;    cleared anyway
	ADDI T4,(T4)		;concatenate and left justify
	LSH T4,2
	MOVEM T4,(T2)		;store
	ADDI T1,1		;next source word
	ADDI T2,1		;next dest word
	SOJG T3,PK3232
	RET


;Pack input for special queues.  First word (.NBDW0) already has
; one byte of data in it.
;
; Call:
;	T1/ source
;	T2/ dest
;	T3/ word count (32-bit words)

PKSPQ:	PUSH P,P1		; need this
	LSH T3,2		; compute 8-bit-byte count
	HRLI T2,(POINT 8,,7)	; net buffer already has one byte in it
	MOVSI P1,-4		; state counter
PKSPQ0:	LDB T4,UBYTE(P1)	; get ptr to this byte in Unibus data
	IDPB T4,T2		; pick 'em up and put 'em down
	SOJLE T3,PKSPQR		; count bytes
	AOBJN P1,PKSPQ0		; step state counter
	MOVSI P1,-4		; reset state counter
	ADDI T1,1		; and step to next word in Unibus buffer
	JRST PKSPQ0		; and keep going
PKSPQR:	POP P,P1		; return
	RET


;Table of pointers to bytes in Unibus format (T1 points at word)

UBYTE:	POINT 8,(T1),9
	POINT 8,(T1),17
	POINT 8,(T1),27
	POINT 8,(T1),35
;Unpack 36-bit words into UBA-style format
; Note that the AN22 reverses the bytes for us, to save
; the -10 from having to swap bytes (a KS is slow enough already!)
;Call:
;	T1/ source addr
;	T2/ dest addr
;	T3/ word count (36-bit words)
;Returns: +1 always

UP3632:	SAVEP
	DMOVE P1,T1		; copy args
	MOVE P3,T3
	MOVE T2,(P1)		; get word of source
	ADDI P1,1		; next source
	SETZ T1,
	LSHC T1,^D16		; form one -11 word
	LSH T1,2		; waste a couple of bits
	LSHC T1,^D16		; form next -11 word
	MOVEM T1,(P2)		; stuff into UBA buffer
	ADDI P2,1		; next dest

; T2 now has 4 bits left in it, left-justified.

UP62A:	MOVSI T3,-4		; loop counter
UP62B:	SETZ T1,
	LSHC T1,@[EXP ^D4	; get bits left in T2
		EXP ^D8
		EXP ^D12
		EXP ^D16](T3)
	MOVE T2,(P1)		; get more source
	ADDI P1,1		; next source addr
	SKIPG P3		; if source exhausted,
	SETZ T2,		;  then supply filler
	LSHC T1,@[EXP ^D12	; get bits needed to fill out a whole -11 word
		EXP ^D8
		EXP ^D4
		EXP ^D0](T3)
	LSH T1,2		; make a gap
	LSHC T1,^D16		; another whole -11 word
	MOVEM T1,(P2)		; stuff it
	ADDI P2,1		; next dest
	SOJLE P3,R		; if source gone, we're done
	AOBJN T3,UP62B		; do for all flavors of -11-word fragments

; at this point T2 has 20 bits left in it, left-justified

	LSHC T1,^D16		; get another PDP-11 word
	LSH T1,2		; make a gap

; T2 now has 4 bits left.  T1 has 16 bits and a gap.

	MOVSI T3,-4		; loop and offset counter
UP62C:	LSHC T1,@[EXP ^D4	; get remaining bits
		EXP ^D8
		EXP ^D12
		EXP ^D16](T3)
	MOVE T2,(P1)		; get 36 more bits
	ADDI P1,1		; bump pointer to source
	SKIPG P3		; if source exhausted,
	SETZ T2,		;  fill with zeroes
	LSHC T1,@[EXP ^D12	; get bits to make another -11 word
		EXP ^D8
		EXP ^D4
		EXP ^D0](T3)
	MOVEM T1,(P2)		; store it
	ADDI P2,1		; next dest word
	SOJLE P3,R		; if source exhausted, quit
	LSHC T1,^D16		; get next -11 word
	LSH T1,2		; make another gap
	AOBJN T3,UP62C		; go round again

; T1 now has 16 bits and a gap.  T2 has 20 bits.

	LSHC T1,^D16		; next -11 word
	MOVEM T1,(P2)		; stuff it
	ADDI P2,1		; bump dst addr
	JRST UP62A		; now T2 has 4 bits left -- reenter first loop
;Unpack 32-bit words left-justified into UBA-style buffer
;Call:
;	T1/ source addr
;	T2/ dest addr
;	T3/ word count
;Returns: +1 always

UP3232:	ACVAR<SRC>		; source pointer
	MOVE SRC,T1
	MOVE T4,T2		; need T2 for scratch
UP22A:	MOVE T2,(SRC)		; get some
	SETZ T1,		; insure unused bits stay zero
	LSHC T1,^D16		; form one -11 word
	LSH T1,2		; and a gap
	LSHC T1,^D16		; next -11 word
	MOVEM T1,(T4)		; stuff it
	ADDI SRC,1		; next source word
	ADDI T4,1		; next destination word
	SOJG T3,UP22A		; loop
	RET
;Here to give command to AN22.  Call with command code in T1.
; Saves history of commands in ring buffer pointed to by CMRNGB.

AN2CMD:	SKIPN DBUGSW		; debugging?
	JRST AN2CM1		; no, skip this expensive stuff
	PIOFF			; interrupt routines use this, too
	AOS T2,CMRNGB		; advance round command ring buffer
	CAILE T2,CMRNGE-1	; gone past end?
	SUBI T2,CMRNGN		; yes, wrap around
	HRLZ T3,T1		; put command in left half
	HRR T3,(P)		;  and caller's address in right half
	MOVEM T3,(T2)		; save it
	MOVEM T2,CMRNGB		;  and current pointer
	PION
AN2CM1:	MOVE T4,T1		; preserve command code
	JSP CX,AN2WAT		; wait for AN22 ready
	WRIOB T4,BSEL0(CSR)	; give the command
	PION			; interrupts back on
	RET			; and return


; Wait for AN22 to be ready to accept a command
; Call:	CSR/ address of AN22
;	JSP CX,AN2WAT
; Return: +1 always, with PI's off

AN2WAT:	MOVEI T3,^D5000		; how patient to be
	MOVEI T1,200		; magic bit (see AN22 functional spec)
AN2WT1:	PIOFF			; prevent confusion
	TIONB T1,BSEL0(CSR)	; ready yet?
	JRST 0(CX)		; yes, return
	PION			; no, don't tie up machine
	AOS AN2NRD		; count occurrences of this
	SOJG T3,AN2WT1		; keep trying for a while
	RDIOB T1,BSEL0(CSR)	; get offending command code
	BUG(AN2HNG,<<T1,BSEL0>>)
	SETZM NETON		; shut down ARPANET
	JRST 0(CX)		; and return
; Status check

IMPRLQ::SAVET
	MOVE T1,[AN2ADR]
	CALL UBGOOD		; AN22 exist?
	 RET			; no, return bad
	MOVE CSR,[AN2ADR]
	RDIOB T1,BSEL2(CSR)	; get byte with IMP READY bit
	TXNN T1,AN2IRD		; is IMP ready?
	RET			; no, fail
	RETSKP			; yes, ok


; Here from NCPFRK to shut down our side of IMP/Host interface

IDVKIL::SAVET
	MOVE CSR,[AN2ADR]
	MOVEI T1,C.RSET		; reset command code
	CALL AN2CMD		; (don't change to CALLRET, please)
	RET


; Called here from IMPIN0 on initialization of NCPFRK

IMPRSD::XMOVEI T1,AN2ERR	; reset interrupt dispatches
	MOVEM T1,AN2XPC+7	; input interrupts will cause BUGCHK
	XMOVEI T1,AN2DN2	; output interrupts will attempt more output
	MOVEM T1,AN2XPC+3	;  ..
	RET
; Called here from NCPFRK whenever net is down and wanted up

IMPRSS::CALL BUTKMC		; boot the AN22
	 RET			; something fell over
	RDIOB T1,BSEL1(CSR)	; get ucode version number
	MOVE T2,UCDVER		; see if it's what we expect
	CAME T1,T2		; is it?
	BUG(AN2UCV,<<T1,ACTUAL>,<T2,DESIRD>>)
	CALL IMPRLQ		; IMP there?
	 RET			; no, do nothing
	CALL IMPRSN		; reset variables
	SETZM IMPRDL		; clear count of IMP ready line flaps
	MOVE CSR,[AN2ADR]	; address of AN22
	MOVEI T1,C.RSET		; reset it
	CALL AN2CMD		;  ..
	MOVEI T1,C.SHRD		; set host ready
	CALL AN2CMD		;  ..
	MOVEI T1,C.SXIE		; set xmit interrupt enable
	CALL AN2CMD		; once now and forever
	SETZM INPON		; flag input off
	SETZM IMPDRQ		; forget any take-down requests
	MOVNI T1,2		; flush first 2 messages IMP sends
	MOVEM T1,IMPFLS		;  (they're NOPs, or should be)
	MOVEI T1,3		; send three NOPs
	MOVEM T1,NOPCNT
	MOVEI T1,^D500		; allow time for ready line to settle
	DISMS
	AOS NETTCH		; note network change of state
	AOS JB0FLG		; poke job zero
	GTAD			; get current time and date
	MOVEM T1,NCPUPT		; remember time net came up
	SKIPLE IMPNFI		; if input buffers available,
	CALL IMISRT		;  then start input
	SETOM IMPRDY		; note IMP ready
	SETOM IMPORD		; allow output
	MOVE T1,NLHOST		; send ourself an RRP
	CALL IMPRRP		;  ..
	RET

; Here whenever IMP ready line flaps

IMIERR:	SAVET
	SETOM IMPRDL		; Be sure NCP notices this
	MOVNI T1,2		; flush NOPs from IMP
	MOVEM T1,IMPFLS		;  ..
	MOVNM T1,NOPCNT		; and send some ourself
	MOVE CSR,[AN2ADR]	; addr of AN22
	RDIO T1,SEL2(CSR)	; get interesting bits
	BUG(AN2IER,<<T1,SEL2>>)
	MOVEI T1,C.CIWD		; clear ready line error
	CALL AN2CMD		; (don't change to CALLRET, please)
	RET


	END