Google
 

Trailing-Edge - PDP-10 Archives - BB-F493Z-DD_1986 - 10,7/d8rint.mac
There are 4 other files named d8rint.mac in the archive. Click here to see a list.
	TITLE	D8RINT - SERVICE FOR DMR11 V016
	SUBTTL	T. LITT		24 SEP 85

	;From D8KINT V026

	SEARCH	F, S ,NETPRM, D36PAR
	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1981,1986>
;COPYRIGHT (C) 1981,1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.
;
;
XP VD8RINT,016

                     Comment @ Loose ends.

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

                         End Comment @

D8RINT::ENTRY	D8RINT

		Comment	@

D8RINT is the protocol translator between NETSER and DMRINT.  NETSER
calls us at D8RDSP (thru the FEK), and we call NETSER at FEKINT.

		End comment @
	SUBTTL	FEK -- FEK INTERFACE FOR D8RINT

;D8RINT FEK INTERFACE.  ENTRIES ARE:
;
;	D8RONC			;INITIALIZATION FOR THE FEK (Crank up DMR)
;	D8RSEC			;ONCE A SECOND CODE.
;	D8RRDD			;SET UP A READ REQUEST.
;	D8RWRT			;QUEUE A BUFFER FOR OUTPUT.
;	D8RCRS			;SHUT DOWN A LINE
;	D8RDWN			;NOT USED
;	D8RUP			;NOT USED
;	D8RSTC			;CALLED WITH  "U" := STC MESSAGE TO SEND
;
;FEKINT IN NETSER IS CALLED WHENEVER
;
;	A DMR GOES OFFLINE (FF.DWN INTERRUPT)
;	A MESSAGE IS RECEIVED (FF.IN INTERRUPT)
;	AN XMIT MESSAGE IS ACKED (FF.OUT INTERRUPT)

D8RDSP::CAIL	T1,FF.ONC	;RANGE CHECK THE FUNCTION
	CAILE	T1,FF.CPW	;  CODE AND STOP IF BAD
	PUSHJ	P,NTDSTP##	;++ ERROR: BAD FUNCTION CODE TO FEK
	JRST	@.+1(T1)	;DISPATCH TO APPROPRIATE ROUTINE
	 JRST	NTFONC##	;ONCE ONLY CODE (USE NETSER'S DEFAULT)
	 JRST	NTFSEC##	;ONCE/SECOND CODE - NETSER's DEFAULT IS FINE
	 JRST	D8RRDD		;SET UP A READ REQUEST
	 JRST	D8RWRT		;SET UP A WRITE REQUEST (MESSY)
	 JRST	D8RCRS		;CRASH THE FEK (CPU WENT DOWN?)
	 JRST	CPOPJ##		;FEK DOWN (NOT USED)
	 JRST	CPOPJ##		;FEK UP (NOT USED)
	 JRST	D8RSTC		;CALLED WITH "U := STC MESSAGE TO SEND"
	 JRST	D8RCRS		;SYSTEM SLEEPING AND
	 JRST	D8RCRS		;  WAKING IS TOO COMPLEX TO THINK ABOUT NOW


D8RCRS: PJSP	T1,DMRERR##	;THIS SHOULD NEVER EVER HAPPEN.  WE
				;  DO BETTER TIMING THAN NETSER.  IF
				;  IT DOES HAPPEN, SOMETHING IS HORRIBLY
				;  WRONG.
D8RRDD:	AOS	FEKBSI(J)	;SET FLAG SAYING WE HAVE INPUT BUFFER
	POPJ	P,		;Regrettably, we can do nothing now
D8RWRT:	NETOFF			;First, disable interrupts
	SKIPG	FEKOCT(J)	;If no messages,
	 JRST	NTONPJ##	;Done
	HRRZ	T3,FEKOAD(J)	;GET THE ADDRESS OF THE NEXT MSG
	HRRZ	T1,PCBBLK(T3)	;GET THE ADDRESS OF THE ONE AFTER IT
	HRRZM	T1,FEKOAD(J)	;MAKE THE SECOND THE NEW FIRST
	SKIPN	T3		;IF WE DIDN'T GET A MESSAGE,
	 PUSHJ	P,NTDSTP##	;THE OUTPUT QUEUE IS MESSED UP
	SOS	FEKOCT(J)	;COUNT DOWN ONE LESS MESSAGE
	NETON			;RE-ENABLE THE INTERRUPTS
	SETZM	MB.FMS(T3)	;MAKE SURE THIS LOOKS LIKE AN ANF MSG
	MOVEI	T1,DC.FQB	;GET THE QUEUE DATA FUNCTION
	MOVE	T2,FEKUNI(J)	;GET THE DMR BLOCK ADDRESS
	PUSH	P,T3		;SAVE THE MSG POINTER
	PUSHJ	P,DMADSP##	;QUEUE THE MESSAGE
	 JRST	[MOVEI	T1,DC.IOF ;GIVE THE NOTIFICATION OF
		 MOVE	T2,J	  ; THE OUTPUT NOT DONE
		 POP	P,T3	  ;RESTORE MSG POINTER
		 PUSHJ	P,D8RK2U  ;DO WHATEVER NEEDED
		 JRST	D8RWRT]   ;CHECK FOR MORE
	POP	P,T3		;RESTORE MSG POINTER
	JRST	D8RWRT		;AND GO CHECK FOR MORE
;D8RSTC	ROUTINE TO HANDLE THE "FF.STC" FEK ENTRY.
;CALL	U := POINTER TO STC BLOCK
;RETURN	CPOPJ			;IF FEK IS "UP".  IMPLIES A STC-REJECT
;	CPOPJ1			;STC MESSAGE IS QUEUED.
D8RSTC:	SKIPGE	T1,FEKBLK(J)	;GET THE FEK'S FLAGS
	 POPJ	P,		;IF RUNNING, THEN DON'T TAKE THE LINE DOWN
	TLNE	T1,FK.MAI	;IF WE'RE IN MAINT MODE
	 JRST	D8RST1		;  THEN WE CAN JUST QUEUE THE MESSAGE
	MOVEI	T1,DC.FSM	;IF NOT IN MAINT MODE, GET FUNCTION
	MOVE	T2,FEKUNI(J)	;  TO PUT IN MAINT MODE, AND
	PUSHJ	P,DMADSP##	;  CALL THE DRIVER.
	 POPJ	P,		;COULDN'T DO ANYTHING, WE DON'T OWN LINE
	MOVSI	T1,FK.MAI	;GET THE MAINT BIT
	IORM	T1,FEKBLK(J)	;  AND SAY WE'RE IN MAINT MODE
D8RST1:	MOVEI	T1,DC.FQB	;IF IN MAINT MODE, GET "QUEUE OUTPUT"
	MOVE	T2,FEKUNI(J)	;  FUNCTION, AND PASS THE
	MOVE	T3,U		;  BUFFER OFF TO THE DRIVER
	SETZM	MB.FMS(T1)	;  MAKE SURE IT LOOKS LIKE AN ANF-10 MSG
	PUSHJ	P,DMADSP##	;CALL DMRINT TO SEND THE MESSAGE
	 JRST	[MOVEI	T1,DC.IOF ;GIVE THE NOTIFICATION OF
		 MOVE	T2,J	  ; THE OUTPUT NOT DONE
		 MOVE	T3,U      ;SET UP MSG POINTER
		 PUSHJ	P,D8RK2U  ;DO WHATEVER NEEDED
		 JRST	CPOPJ1]   ;GIVE GOOD RETURN
	RETSKP			; AND GIVE A GOOD RETURN
	SUBTTL	DMRINT - INTERFACE FROM DMR DRIVER

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

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

D8RKKD:	IFIW	D8PUP		;(00) PROTOCOL UP (START/STACK COMPLETE)
	IFIW	D8PDN		;(01) PROTOCOL DOWN (ALL BUFFERS RETURNED)
	IFIW	D8MAI		;(02) MAINT MSG RECEIVED (IMPLIES PDN)
	IFIW	D8STR		;(03) START RECEIVED (LEAVE MAINT PROTOCOL)
	IFIW	D8ODN		;(04) OUTPUT DONE (MESSAGE SUCCESSFULY SENT)
	IFIW	D8OND		;(05) OUTPUT NOT DONE (LINE IS GOING DOWN)
	IFIW	D8IDN		;(06) INPUT DONE (T3 := BUFFER ADDRESS)
	IFIW	D8RQI		;(07) REQUEST INPUT BUFFER (T3 := BYTE COUNT)
	IFIW	NTDSTP##	;(10) NEW LINE CREATION
	IFIW	NTDSTP##	;(11) OLD LINE DISSOLUTION

;D8PUP	HERE WHEN PROTOCOL IS ESTABLISHED (START/STACK/ACK COMPLETE)
;	MAKE SURE KONTROLLER IS HONEST AND THEN SET THE FEK ONLINE

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

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

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

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

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

D8STR:	;The DMR is incapable of this.
	PUSHJ	P,NTDSTP
	XLIST		;D8RINT code in case DMR is ever updated
REPEAT	0,<
	MOVEI	T1,DC.FIL	;NOW TELL THE KONTROLLER
	MOVE	T2,FEKUNI(J)	; TO INITIALIZE ITSELF IN "NORMAL" MODE
	PUSHJ	P,DMADSP##	;DO IT
	 POPJ	P,		;WE NO LONGER OWN THE LINE, NOTHING MATTERS
	MOVSI	T1,FK.MAI	;GET AND CLEAR THE MAINT
	ANDCAM	T1,FEKBLK(J)	; MODE BIT
	POPJ	P,		;RESTORE AC'S AND LEAVE
>;Repeat 0
	LIST

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

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

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

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

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


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

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

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


;CALNET	- ROUTINE TO CALL FEKINT SAVING AND RESTORING W & F

CALNET:	PUSH	P,W		;SAVE DMR POINTER
	PUSH	P,F		;SAVE DMR POINTER
	PUSH	P,T4		;SAVE THE BDL POINTER
	PUSHJ	P,FEKINT##	;CALL NETSER
	POP	P,T4		;RESTORE BDL POINTER
	POP	P,F		;RESTORE LINE
	POP	P,W		;RESTORE DMR
	POPJ	P,		;RETURN.

	PRGEND
	TITLE	DMRSER - USER INTERFACE FOR DMR CONTROL V002
	SUBTTL	T. LITT 	21 NOV 81

		;From D8KINT V026

	SEARCH	F, S, NETPRM,  D36PAR
	$RELOC
	$HIGH

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

                     Comment @ Loose ends.

                         End Comment @

DMRSER::ENTRY	DMRSER

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


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

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

	%%%OFF==DEVLLD		;INITIALIZE THE OFFSET

	X	DEVDMR		;XWD DMR-ADDRESS, INPUT MSG QUEUE
	X	DEVOBC		;COUNT OF OUTPUT BUFFERS OUTSTANDING
	X	DMRDBL,0	;LENGTH OF A DMR DDB

	PURGE	X		;FLUSH THE MACRO


;NOW LAY OUT THE PROTOTYPE DDB

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

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

;DISPATCH TABLE (FROM UUOCON)

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

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

;NOW CHECK THE NAME
	LDB	T1,DRNDMR	;GET THE CONTROLLER NUMBER
	SUBI	T1,'0'		;Make it binary
	SKIPGE	T1		;IF LEGAL
	 POPJ	P,		;BAD
	CAIG	T1,M.DMRN##-1	; AND MAKE SURE IT'S BOTH LEGAL
	 SKIPN	W,DMRTBL##(T1)	; AND THAT IT EXISTS
	POPJ	P,		;IF IT DOESN'T EXIST, USER CAN'T OPEN IT
	SKIPE	DMRDDP(W)	;SEE IF SOMEONE ELSE OWN'S THE DDB
	JRST	CLRFPJ		; IF SO, ZERO F AND RETURN

;NOW BUILD A DDB
	MOVEI	T2,DMRDBL	;GET THE LENGTH OF A DMR DDB
	PUSHJ	P,GETWDS##	; AND ALLOCATE THE SPACE
	  JRST	CLRFPJ		; IF NOT ENOUGH, GIVE THE FAIL RETURN
	MOVSI	T2,DMRDDB	;GET THE ADDRESS OF THE PROTOTYPE
	HRR	T2,T1		; AND A COPY OF THE ADDRESS OF THE NEW ONE
	DDBSRL			;LOCK THE DDB CHAIN
	BLT	T2,DMRDBL-1(T1)	;INITIALIZE THE NEW ONE
	HRLM	T1,DEVSER+DMRDDB ; AND SET UP THE LINKS
	DDBSRU			;DDB IS IN PLACE, UNLOCK THE DDB CHAIN
	HRLM	F,DEVDMR(T1)	;MAKE THE DDB POINT TO THE DMR BLOCK
	EXCH	T1,DMRDDP(W)	;MAKE THE DMR BLOCK POINT TO THE DDB
	SKIPE	T1		;JUST A PARANOID CHECK.
	 PUSHJ	P,NTDSTP##	; I DON'T THINK THIS IS POSSIBLE
	MOVE	F,DMRDDP(W)	;GET THE ADDRESS OF THE DDB
	MOVEM	P1,DEVNAM(F)	;SET THE DEVICE'S NAME
	RETSKP			; AND GIVE A GOOD RETURN TO DDBSRC

CLRFPJ:	SETZ	F,		;CLEAR "F" IF AN ERROR
	POPJ	P,		; AND RETURN
;DMRREL	ROUTINE TO PROCESS THE "RELEASE" FUNCTION OF A DMR
;CALL	F := ADDRESS OF THE DDB
;RETURN	CPOPJ			;ALWAYS

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

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

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

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

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

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

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

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

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

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


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


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


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

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


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

DMRONL:	PUSHJ	P,DMRCKO	;FIRST SEE IF WE OWN THE LINE
	  JRST	K.IMPM		; IF NOT, THEN IT'S IMPROPER MODE
	HLRZ	T1,DEVDMR(F)	;GET THE DMR BLOCK ADDRESS
	EXCH	W,T1		;PUT ADDRESS IN RIGHT PLACE
	LDB	T2,PDRSTS##	;GET CURRENT STATE
	EXCH	W,T1		;THUS
	CAIGE	T2,DR%MAI	;MUST BE IN AT LEAST MAINT STATE
	 JRST	K.DERR		;IF NOT, SAY DEVICE ERROR
	MOVE	S,DEVIOS(F)	;FIRST GET THE DEVICE STATUS
	TRNE	S,IOSSRM!IOSMRN	; AND IF EITHER ERROR IS STILL LIT
	 JRST	K.DERR		; RETURN "DEVICE ERROR"
	TRNE	S,IODERR!IODTER!IOIMPM!IOBKTL ;IF ANY "STANDARD" ERROR
	 POPJ	P,		; FORCE THE USER TO CLEARR IT
	TRNE	S,IOSMAI	;IF WE ARE TRYING MAINT MODE,
	JRST	DMRON1		; GO CHECK ONLINE DIFFERENTLY
	CAIGE	T2,DR%WT1	;IF SUPPOSED TO BE NORMAL, BUT NOT
	JRST	K.DERR		; THEN WE MUST HAVE SCREWED UP
	CAIE	T2,DR%RUN	;IF WE'RE RUNNING
	 CAIN	T2,DR%WT1	;OR IF WE NEED A MESSAGE TO START
	CAIA			;WE ARE OK
	JRST	K.DTER		; ELSE IT'S A "SOFT" ERROR. (WAIT FOR ONLINE)
	RETSKP			;IF IN RUN, THEN ALL'S OK

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

DMRCKM:	PUSHJ	P,DMRCKO	;SEE IF WE ARE THE LINE'S OWNER
	  POPJ	P,		; IF NOT, DON'T DO ANYTHING
	PUSH	P,W		;FIRST PRESERVE THE DDB POINTER
	HLRZ	W,DEVDMR(F)	;FROM THAT GET THE DMR BLOCK ADDRESS
	LDB	T1,PDRSTS##	;FROM THAT, GET THE STATE
	POP	P,W		;NOW RESTORE THE DDB ADDRESS
	MOVE	S,DEVIOS(F)	;RELOAD THE IO STATUS
	TRNE	S,IOSMAI	;IF WE'RE SUPPOSED TO BE IN MAINT
	JRST	DMRCK1		; THEN GO CHECK DIFFERENTLY
	CAIL	T1,DR%WT1	;IF WE'RE TRYING TO START OR BETTER
	 POPJ	P,		; THEN ALL'S OK
	MOVEI	T1,DC.FIL	;WE WANT TO "INITIALIZE" THE LINE
	JRST	DMRCK2		; SO GO TO COMMON CODE TO DO SO

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


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

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

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


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

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

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


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


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

	DMRHDL==4+MD.LEN	;LENGTH OF DMR HEADER IN WORDS
	DMRMSD==4		;OFFSET TO MSD FROM BEG OF BLK

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

DRNDMR:	POINT	6,P1,18+6-1	;KONTROLLER NUMBER






DRSEND::PRGEND
	TITLE	DMRINT - SERVICE FOR DMR11 V002
	SUBTTL	T. LITT		21 NOV 81

	;From D8KINT V026

	SEARCH	F, S ,NETPRM, D36PAR
	$RELOC
	$HIGH

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

DMRINT::ENTRY	DMRINT

	PURGE	NETOFF,NETON

                           COMMENT @

                 EHPL MI RTPADE NIISED A DP1P1
                                
                        END OF COMMENT @

		    Comment @ DMRINT Functional Description

DMRINT is the actual physical DMR driver.  It interfaces to the rest of the
monitor thru the standard DECnet ROUTER/DLL interface.  Other users, such as
ANF, UUOCON, or even IBMCOMM translate their private protocols to ours thru
the interface modules (D8RINT, DMRSER, D6RINT).

Requests from users are vectored thru DMRDSP, and use function codes of
the form DC.F??.  Actually, DMRDSP (DMADSP, DMODSP, DMIDSP) respectively.


Responses from DMRINT to the user are vectored thru the CALUSR routine,
and use function codes of the form DC.I??.

Two state variables are important; the line's protocol status (PDRSTS), and
the line's user code (DMRUSR).  Because the DMR won't tell us when a line
comes up unless we give it something to do, there are a couple of startup
states where we accept one message to xmit, then continuously return
Output-not-done to further requests until we get an interrupt that implies
that the line came up.  DMRUSR tells us who is using the line.

One more kludge; to avoid copying ANF messages into DECnet format buffers,
there is a kludge (not unique to DMR service) we will accept either DECnet
style message blocks (MB????) or ANF style PCB/STC blocks.  We determine
the difference by noting that MB.FMS is zero in the ANF format.  ANF style
compression is also dealt with here, simply because D8Kxxx chose to.

		End comment @
	SUBTTL	DMRPRM -- PARAMETERS FOR THE DMR

	;Define use of the DMR communications region (DMRPPL pages)

	;By convention, F will point to the first word of the comm region

	;-----------------------------------------------;\
;TBASAD	;DMR base table (128 bytes), used by HW	|waste	; \
	;-----------------------------------------------;  \
;TBFIN0	;DR%NBF Recieve buffers	(EBFSIZ bytes) |waste	;   \
	;-----------------------------------------------;    \
;TBFOU0	;DR%NBF Transmit buffers(EBFSIZ bytes) |waste	;  COMSIZ words
	;-----------------------------------------------;  allocated on a
							;\ page boundary in
							; \COMMON.

	;Symbols beginning with T are in units of PDP-10 words
	;Symbols beginning with E are in units of PDP-11 bytes

	;To make life easy, make all 11 addresses on a 10 word boundary
	;The code implicitly counts on this, thus the "waste"

	;Inputs:
	;	NETPRM - DMRPPL	Number of pages to allocate for comm region
	;	NETPRM - DR%NBF Number of buffers to allocate per channel

	COMSIZ==DMRPPL*1000	;10 Words in com region
	TBASAD==0		;10 Address of Base Table
	EBASAD==0		;11 Address of Base Table
	TBFIN0==TBASAD+<^D128+3>/4 ;10 Address of Input Buffer 0
	EBFIN0==TBFIN0*4	;11 Address of Input Buffer 0
	EBFSIZ==<<COMSIZ*4-EBFIN0>/<4*DR%NBF*2>>*4 ;Bytes/buffer
		;Bytes/reg - base-tbl-siz / #buf/chn*#chn rounded to 10 wd /4*4
	TBFSIZ==EBFSIZ/4	;10 Words/buffer
	IFN	<TBFSIZ-DMRMMS>,<Printx	?DMRMMS wrong, update NETPRM>
	EBFOU0==EBFIN0+<DR%NBF*EBFSIZ>	;11 Address of Output Buffer 0
	TBFOU0==TBFIN0+<DR%NBF*TBFSIZ>	;10 Address of Output Buffer 0
	;The following symbols are for information/debugging only
	;The code should work for N page comm regions, and M buffers
	EBFIN1==EBFIN0+EBFSIZ	;11 Address of Input Buffer 1
	TBFIN1==TBFIN0+TBFSIZ	;10 Address of Input Buffer 1
	EBFOU1==EBFOU0+EBFSIZ	;11 Address of Output Buffer 1
	TBFOU1==TBFOU0+TBFSIZ	;10 Address of Output Buffer 1

	;Compute wasted space (Due to rounding (word boundary))
	WASTE==COMSIZ-<TBFOU0+<DR%NBF*TBFSIZ>> ;10 Words wasted at end of Comm region
	IFL	WASTE,<Printx	? DMR11 Communications Region too BIG!>

	;Compute total wasted space 
	WASTE==COMSIZ*4-<^D128 + <2*DR%NBF*EBFSIZ>> ;Total wasted bytes
	SUBTTL	DEFINITIONS -- DMR11

	;DMR-11 BIT DEFINITIONS

	DMRSL0==0		;SEL0
	DMRBS0==0		;BSEL0
	 DMRTBI==0		;TRANSMIT BUFFER IN COMMAND
	 DMRCTI==1		;CONTROL IN COMMAND
	 DMRHLT==2		;HALT PROTOCOL COMMAND
	 DMRBSI==3		;BASE IN COMMAND
	 DMRRBI==4		;RECEIVE BUFFER IN COMMAND
	 DMRRQI==40		;REQUEST INPUT PERMISSION
	 DMRIEI==100		;INTERRUPT A ENABLE ON RDI
	 DMRRDI==200		;DMR IS READY TO INPUT COMMAND
	 DMRSSU==400		;STEP DMR 1 MICROINSTRUCTION
	 DMRRMI==1000		;ROM IN - DMR WILL XCT NEXT INSTR FROM SEL6
	 DMRRMO==2000		;ROM OUT - DMR WILL PRESENT NEXT CROM LOC OR
				;	   MICROINSTR IN SEL6
	 DMRLUL==4000		;TTL LOOP LINE UNIT SERIAL DATA
	 DMRSLU==10000		;STEP LINE UNIT 0 RCV SHIFT 1 XMT SHIFT
	 DMRIMD==20000		;INHIBIT MICRODIAGNOSTICS (COMPLEMENT SWITCH)
	 DMRMRC==40000		;MASTER CLEAR DMR (SELF-CLEARING)
	 DMRRUN==100000		;RUN
	DMRBS1==1		;BSEL1
	DMRSL2==2		;SEL2
	DMRBS2==2		;BSEL2
	 DMRTBO==0		;TRANSMIT BUFFER OUT
	 DMRRBO==4		;RECEIVE BUFFER OUT
	 DMRCTO==1		;CONTROL OUT
	 DMRIEO==100		;INTERRUPT ENABLE ON RDO
	 DMRRDO==200		;DMR HAS OUTPUT A NEW COMMAND
	DMRBS3==3		;BSEL3
	 DMRMTF==1		;MICROPROCESSOR BOARD TEST FAILED
	 DMRLTF==2		;LINE UNIT BOARD TEST FAILED
	 DMRMDI==100		;MICRODIAGNOSTICS WERE INHIBITED
	 DMRMDR==200		;MICRODIAGNOSTICS RAN
	DMRSL4==4		;SEL4
	DMRBS4==4		;BSEL4
	 ;MODEM STATUS BITS -- VALID WHEN RDI HAS SET
	 DMRMCD==1		;MODEM CARRIER DETECT/RCVR ACTIVE
	 DMRMSB==2		;RS449 STANBY LEAD
	 DMRMCS==4		;CLEAR TO SEND
	 DMRMSR==10		;DATA SET READY
	 DMRMHD==20		;LINE UNIT IN HDX MODE
	 DMRMRS==40		;REQUEST TO SEND
	 DMRMTR==100		;DATA TERMINAL READY
	 DMRMRG==200		;RING
	 DMRMTM==2000		;MODEM IN TEST MODE
	 DMRMSQ==40000		;SIGNAL QUALITY (CARRIER DETECT LOOKALIKE)
	 DMRMSR==100000		;SIGNAL RATE
	DMRBS5==5		;BSEL5
	DMRSL6==6		;SEL6
	DMRBS6==6		;BSEL6
	 ;MORE MODEM STATUS BITS
	 DMRMRH==1		;RTS HOLD - IN FDX, LT 1MBAUD DMR HOLDS RTS WHEN
				; LINE IDLE, EXCEPT ERROR RECOVERY
	 DMRMHX==20		;LINE UNIT IN HDX MODE
	 DMRMTX==100		;DATA TERMINAL READY
	 ;CONTROL-OUT STATUS BITS
	 DMRONK==1		;NAK THRESHOLD EXCEEDED
	 DMROTO==2		;REP TIMEOUT (7 CONSEC REPS XMITTED)
	 DMRONB==4		;7 NAKS RECEIVED DUE TO NO BUFFER
	 DMROMM==10		;MAINT MESSAGE RECEIVED (FATAL)
	 DMROLM==20		;MESSAGE LONGER THAN BUFFER RECEIVED (FATAL)
	 DMROHU==100		;DSR DROPPED (OTHER END HUNG UP)
	 DMROSR==200		;START RECEIVED WHILE RUNNING (FATAL)
	 DMRONX==400		;NXM WHILE XMT/RCV/BASE TABLE UPDATE (FATAL)
	 DMROHC==1000		;HALT COMPLETE
	;CONTROL-IN COMMAND BITS
	 DMRILS==4000		;Use long start timer
	 DMRIHD==2000		;Use half duplex mode
	 DMRIMT==400		;Use DDCMP maintenance mode
	DMRBS7==7		;BSEL7
	 ;MODEM STATUS BITS - VALID ONLY AFTER BASE TABLE ASSIGNED
	 DMRMPS==1		;PROGRAM SELECTED - MAINT BIT
	 DMRMIM==10		;INTEGRAL MODEM SELECTED
	 DMRM35==20		;V.35 MODEM SELECTED
	 DMRM32==100		;RS232-C OR RS423-A MODEM SELECTED
	 DMRM22==200		;RS422-A MODEM SELECTED
	SUBTTL	DMRONC  -- ONCE ONLY ROUTINE FOR DMR

;THIS ROUTINE IS CALLED BY SYSINI.  IT VERIFYS THAT ALL DMR11
;UNITS SPECIFIED BY MONGEN ACTUALLY EXIST.  


DMRONC::PUSHJ	P,SAVE4##	;HERE FROM ONCE.  WE USE 4 P'S

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

DMROCP:	CAIL	P1,M.DMRN##	;LOOP OVER ALL DMRs
	 POPJ	P,		;IF WE'VE DONE THEM ALL, RETURN

	SKIPN	W,DMRTBL##(P1)	;GET THE ADDRESS OF THE NEXT DMR BLOCK
	 AOJA	P1,DMROCP	;  IF IT'S NOT THERE, TRY THE NEXT ONE
	MOVE	T1,DMRCSR(W)	;GET THE ADDRESS OF THE DMR11
	PUSHJ	P,UBGOOD##	;SEE IF IT EXISTS (UNIBUS TRAP/DOESN'T TRAP)
	  JRST	DMRNXP		;NON-EXISTANT DMR

	PUSHJ	P,DMRINI	;Restart the DMR(In it's MONGEN'd mode)
	 JFCL			;If we lost, we lost
	AOJA	P1,DMROCP	;Try for next
;DMRNXP	- ROUTINE TO DECLARE A DMR11 NON-EXISTANT.  UNIT IS MARKED
;	NON-EXISTANT BY PUTTING A ZERO IN IT'S "DMRTBL" ENTRY.

DMRNXP:	SETZM	DMRTBL##(P1)	;CLEAR THE DMR TABLE ENTRY SO IT'S IGNORED
	MOVE	U,OPRLDB##	;GET THE ADDRESS OF THE OPR'S TTY
	PUSHJ	P,INLMES##	;TELL HIM FIRST PART OF BAD NEWS.
	ASCIZ	/
? Can't find /
	PUSHJ	P,PRDMR		;TELL HIM WHICH DMR (FROM "W")
	PUSHJ	P,INLMES##	;FINISH THE MESSAGE
	ASCIZ	/.
/
	AOJA	P1,DMROCP	;GO DO NEXT DMR

PDRSTS::POINT	3,DMRSTS(W),2	;Pointer to state byte in status word
	SUBTTL	DMRSEC  -- ONCE/SECOND ROUTINE FOR DMR

;THIS ROUTINE IS CALLED BY CLOCK1.  IT CHECKS ALL DMRS FOR RECEIVE
;BUFFER STARVATION, AND IF STARVED, TRYS TO DELIVER MESSAGE AGAIN.


DMRSEC::PUSHJ	P,SAVE4##	;HERE FROM CLOCK1.  WE USE 4 P'S
	PUSHJ	P,SAVEFW	;SAVE F & W AS WELL

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

DMRSC0:	CAIL	P1,M.DMRN##	;LOOP OVER ALL DMRs
	 POPJ	P,		;IF WE'VE DONE THEM ALL, RETURN

	SKIPN	W,DMRTBL##(P1)	;GET THE ADDRESS OF THE NEXT DMR BLOCK
	 AOJA	P1,DMRSC0	;  IF IT'S NOT THERE, TRY THE NEXT ONE
	MOVE	F,DMRTAD(W)	;GET ADDRESS OF COMM PAGE
	AOS	DMRZTM(W)	;Another second of uptime
	PUSHJ	P,FREMAI	;If in maint mode, free xmitted msgs
	MOVEI	T1,DMRSTV	;Get the starvation bit
	LDB	T2,PDRSTS	;Get current state
	CAIL	T2,DR%MAI	;If not at least in maint mode, or
	 TDNN	T1,DMRSTS(W)	;If this one isn't starving
	AOJA	P1,DMRSC0	;Leave the poor thing alone
	DMROFF			;Prevent races
	;Can the fact that we check the starvation bit in RCVBUF
	;prevent us from ever having all the buffers out?
	;Perhaps above test should be removed...
	;Or starvation should be a counter??
	SKIPL	T1,DMRRBC(W)	;Get count of receive buffers DMR has
	 CAILE	T1,DR%NBF	;Too big?
	PUSHJ	P,NTDSTP##	;++ Ridiculous outstanding RBF count
	CAIGE	T1,DR%NBF	;Less than max?
	 PUSHJ	P,RCVMSG	;Pass buffer to USER
	DMRON			;Allow interrupts again
				;Note that we don't try for many msgs
				;on the theory that we were recently core poor
	AOJA	P1,DMRSC0	;Try for next
	SUBTTL	DMRDSP - ENTRY TO DMRINT

;DMRDSP	- THIS ROUTINE IS DECnet'S ENTRY INTO DMRINT.
;CALL	MOVX	T1,FUNCTION-CODE (DC.F??)
;	MOVX	T2,DMR-BLOCK ADDRESS
;	MOVX	T3,BUFFER ADDRESS OR PARAMETER # (ONLY FOR DC.FQB/NTMAN)
;	MOVX	T4,PARAMETERVALUE (ONLY DC.CSC)
;	PUSHJ	P,DMRDSP/DMADSP/DMODSP/DMIDSP
;RETURN	CPOPJ			;WHEN WE ARE CALLED BY THE WRONG USER
;	CPOPJ1			;ON SUCCESS

;For DECnet
DMRDSP::SKIPL	T1		;FIRST RANGE CHECK THE
	 CAILE	T1,DC.FAL	;  FUNCTION CODE [DC.FMX]
	PUSHJ	P,NTDSTP##	;IF OUT OF RANGE, STOP
	PUSHJ	P,SAVEFW	;WE USE F := COMM, W := DMR
	CAIN	T1,DC.FAL	;ASSIGN LINE?
	 JRST	KF.AL		;YES
	MOVE	W,T2		;Point to the DMR block
	HRRZ	T2,DMRUSR(W)	;Get the line user
	CAIE	T2,DD.DEC	;If not DECnet,
	 POPJ	P,		;Die -- shouldn't use DMRDSP!
	MOVE	F,DMRTAD(W)	;Get address of Comm region
	PUSHJ	P,@DMRDST(T1)	;DISPATCH ON THE FUNCTION CODE
	 POPJ	P,		;ORDINARY RETURN
	PJRST	CPOPJ1##	;AND GIVE THE GOOD RETURN

;For ANF-10
DMADSP::SKIPL	T1		;FIRST RANGE CHECK THE
	 CAILE	T1,DC.FQB	;  FUNCTION CODE [DC.FMX]
	PUSHJ	P,NTDSTP##	;IF OUT OF RANGE, STOP
	PUSHJ	P,SAVEFW	;WE USE F := COMM, W := DMR
	CAIN	T1,DC.FAL	;ASSIGN LINE?
	 JRST	KF.AL		;YES
	MOVE	W,T2		;Point to the DMR block
	HRRZ	T2,DMRUSR(W)	;Get the line user
	CAIE	T2,DD.ANF	;If not ANF-10,
	 POPJ	P,		;Die -- shouldn't use DMRDSP!
	MOVE	F,DMRTAD(W)	;Get address of Comm region
	PUSHJ	P,@DMRDST(T1)	;DISPATCH ON THE FUNCTION CODE
	 POPJ	P,		;ORDINARY RETURN
	PJRST	CPOPJ1##	;AND GIVE THE GOOD RETURN

;For Program mode
DMODSP::SKIPL	T1		;FIRST RANGE CHECK THE
	 CAILE	T1,DC.FQB	;  FUNCTION CODE [DC.FMX]
	PUSHJ	P,NTDSTP##	;IF OUT OF RANGE, STOP
	PUSHJ	P,SAVEFW	;WE USE F := COMM, W := DMR
	CAIN	T1,DC.FAL	;ASSIGN LINE?
	 JRST	KF.AL		;YES
	MOVE	W,T2		;Point to the DMR block
	HRRZ	T2,DMRUSR(W)	;Get the line user
	CAIE	T2,DD.PRO	;If not Program mode
	 POPJ	P,		;Die -- shouldn't use DMRDSP!
	MOVE	F,DMRTAD(W)	;Get address of Comm region
	PUSHJ	P,@DMRDST(T1)	;DISPATCH ON THE FUNCTION CODE
	 POPJ	P,		;ORDINARY RETURN
	PJRST	CPOPJ1##	;AND GIVE THE GOOD RETURN

;For IBMcomm
DMIDSP::SKIPL	T1		;FIRST RANGE CHECK THE
	 CAILE	T1,DC.FQB	;FUNCTION CODE [DC.FMX]
	PUSHJ	P,NTDSTP##	;IF OUT OF RANGE, STOP
	PUSHJ	P,SAVEFW	;WE USE F := COMM, W := DMR
	CAIN	T1,DC.FAL	;ASSIGN LINE?
	 JRST	KF.AL		;YES
	MOVE	W,T2		;Point to the DMR block
	HRRZ	T2,DMRUSR(W)	;Get the line user
	CAIE	T2,DD.IBM	;If not IBMcomm,
	 POPJ	P,		;Die -- shouldn't use DMRDSP!
	MOVE	F,DMRTAD(W)	;Get address of Comm region
	PUSHJ	P,@DMRDST(T1)	;DISPATCH ON THE FUNCTION CODE
	 POPJ	P,		;ORDINARY RETURN
	PJRST	CPOPJ1##	;AND GIVE THE GOOD RETURN

DMRDST:	IFIW	KF.HA		;0 = HALT ALL PROTOCOLS
	IFIW	KF.IN		;1 = INITIALIZE NORMAL PROTOCOL (DDCMP)
	IFIW	KF.MA		;2 = INITIALIZE MAINT PROTOCOL (BOOTING)
	IFIW	KF.QO		;3 = QUEUE OUTPUT BUFFERS (MAINT OR NORMAL)
	IFIW	NTDSTP##	;4 = ASSIGN -- SEE SPECIAL DECODE ABOVE
	IFIW	KF.SE		;5 = SET  \
	IFIW	KF.RE		;6 = READ  ) NETWORK MANAGEMENT
	IFIW	KF.CL		;7 = CLEAR/
;KF.AL	- ROUTINE TO ASSIGN A LINE
;CALL	MOVX	T2,DECNET LINE ID

KF.AL:	LDB	W,[POINT 9,T2,17] ;YES, CONVERT LINE ID (LIKON)
	 CAIGE	W,M.DMRN##	;BETTER BE IN RANGE
	SKIPN	W,DMRTBL##(W)	;POINT TO THE DMR BLOCK
	 POPJ	P,		;PATCHED OUT
	HRRZ	T2,DMRUSR(W)	;GET LINE OWNER
	CAIE	T2,DD.DEC	;HACKING?
	 POPJ	P,		;NOT ME, YOU DON'T
	MOVE	T1,DMRCSR(W)	;GET CSR ADDRESS
	PUSHJ	P,UBGOOD##	;MAKE SURE IT'S THERE
	 POPJ	P,		;IT'S NOT, CAN'T ASSIGN
	MOVEM	T3,DMRLBK(W)	;SAVE ADDRESS OF DECNET'S BLOCK
	MOVE	T1,W		;RETURN ADDRESS OF OURS
	JRST	CPOPJ1##	;SUCCESS
;KF.HA	- ROUTINE TO SHUT-DOWN PROTOCOL ON A DMR-LINE.
;CALL	MOVX	W,DMR-BLOCK ADDRESS
;	PUSHJ	P,KF.HA		;FROM EITHER CLOCK OR UUO LEVEL WITH THE
;				;  INTERRUPTS ON.
;RETURN	CPOPJ1			;ALWAYS - HALTS DMR IF ERROR

KF.HA:	LDB	T1,PDRSTS	;FIRST GET OUR STATE,
	CAIGE	T1,DR%FLS	;IF WE ARE ALREADY "HALTED"
	 RETSKP			;RETURN WITH OUT TOUCHING ANYTHING
	PUSHJ	P,DMRKL0	;SILENTLY KILL THE DMR
	PUSHJ	P,DMRPDN	;TELL OUR DRIVER THAT WE ARE DOWN
	MOVEI	T1,DR%HLT	;NOW GO
	DPB	T1,PDRSTS	;TO "INITIALIZED" STATE
	RETSKP			;AND THE "HALT" COMMAND IS DONE
;KF.IN	- ROUTINE TO INITIALIZE THE DMR AND PUT IT IN NORMAL PROTOCOL
;CALL	MOVX	W,DMR-BLOCK ADDRESS
;	PUSHJ	P,KF.IN
;RETURN	CPOPJ1			;ALWAYS

KF.IN:	PUSHJ	P,DMRPD0	;CLEAN OUT QUEUES
	MOVEI	T1,DMRSMT	;The start in maintenance mode bit
	ANDCAM	T1,DMRSTS(W)	;We don't wanna
	PUSHJ	P,DMRINI	;Crank it up
	 JFCL
	RETSKP			;AND WE'RE DONE
;KF.MA	- ROUTINE TO INITIALIZE MAINT PROTOCOL ON THE DMR-LINE
;CALL	MOVX	W,DMR-BLOCK
;	PUSHJ	P,KF.MA
;RETURN	CPOPJ1			;ALWAYS

KF.MA:	LDB	T1,PDRSTS	;FIRST MAKE GET OUR STATE, AND
	CAIN	T1,DR%MAI	;  IF WE ARE ALREADY IN MAINT STATE,
	 RETSKP			;  THEN DON'T DO ANYTHING MORE
	CAIL	T1,DR%WT1	;IF WE ARE RUNNING (OR TRYING TO START)
	 PUSHJ	P,KF.HA		;  THEN FLUSH ALL BUFFERS
	JFCL			;IGNORE ERROR RETURN
	MOVEI	T1,DR%MAI	;GET AND SET THIS LINE TO
	DPB	T1,PDRSTS	;  MAINT STATE
	MOVEI	T1,DMRSMT	;Start in maint bit
	IORM	T1,DMRSTS(W)	;Insist on it
	PUSHJ	P,DMRINI	;Crank it up again
	 JFCL
	RETSKP			;AND WE'RE DONE
;KF.QO	- ROUTINE TO QUEUE OUTPUT MESSAGES
;CALL	MOVX	W,DMR-BLOCK ADDRESS
;	MOVX	T3,MESSAGE BUFFER ADDRESS
;	PUSHJ	P,KF.QO
;RETURN	CPOPJ1			;ALWAYS

KF.QO:	HLLZS	MB.NXT(T3)	;ZERO THE FORWARD POINTER
	SETOM	MB.MSN(T3)	;-1 IN NUMBER FIELD MEANS NO NUMBER ASSIGNED
	DMROFF			;PREPARE TO MESS WITH THE QUEUES
	LDB	T1,PDRSTS	;GET STATE
	CAIE	T1,DR%RUN	;ARE WE IN STILL RUN STATE?
	 CAIN	T1,DR%MAI	;IT'S OK TO SEND DATA IN MAINT MODE TOO
	JRST	KF.QO0		;GOODNESS, ITS OK
	CAIN	T1,DR%WT1	;If in this state,
	 JRST	[MOVEI	T1,DR%WTD ;Allow this, but reject following
		 DPB	T1,PDRSTS ;By advancing a state
		 JRST	KF.QO0]
	DMRON			;NO, PASS PTR TO MSG IN T3
	MOVEI T1,DC.IOF		; GET THE "OUTPUT NOT DONE" FUNCTION
	PUSHJ	P,CALUSR	; AND TELL OUR DRIVER THE NEWS
	RETSKP			;This is still "success"

KF.QO0:	HRRZ	T1,DMRWTO(W)	;GET THE FIRST MESSAGE ON THE "WAIT OUTPUT" Q
	JUMPE	T1,[HRRZM T3,DMRWTO(W)	;IF NONE, THEN WE'RE FIRST
		    JRST KF.QO2]	;GO TRY TO SEND IT

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

KF.QO2:	PUSHJ	P,RCVBUF	;MAKE SURE A RCV BUFFER IS QUEUED
	PUSHJ	P,XMTBUF	;TRY TO SEND THE MESSAGE
	DMRON			;DONE MESSING
	RETSKP			;DONE
	Subttl	NTMAN -- Interface to NTMAN for DECnet

;KF.SE - SET FUNCTIONS FOR NTMAN
;

KF.SE:	JUMPN	T3,KF.SE1	;IS THIS PARAMETER 0 (STATE?)
	CAIE	T4,NCK.ON	;ARE WE SETTING STATE ON?
	JRST	KF.S01		;NO
	LDB	T1,PDRSTS	;GET CURRENT STATE
	CAIE	T1,DR%RUN	;IS IT CURRENTLY RUNNING
	CAIN	T1,DR%MAI	;OR IN MAINTENANCE STATE?
	JRST    KFERRR		;WE SHOULDN'T BE DOING THIS
	PJRST	KF.IN		;INITIALIZE THE LINE

KF.S01:	CAIE	T4,NCK.OF	;ARE WE SETTING STATE OFF?
	JRST	KF.S02		;NOPE
	PJRST	KF.HA		;HALT THE LINE

KF.S02:	;CAIE	T4,NCK.SR	;ARE WE SETTING STATE TO SERVICE?
	; JFCL			;NO
	JRST	KFERRR		;YES, I DON'T KNOW WHAT TO DO ABOUT IT.

KF.SE1:	CAIE	T3,^D1111	;DUPLEX?
	 JRST	KF.SE2		;NO
	LDB	T1,PDRSTS	;GET LINE STATE
	CAIE	T1,DR%HLT	;HALTED?
	 JRST	[MOVNI	T1,^D11	;NO, IN WRONG STATE
		 POPJ	P,]	;DIE
	MOVEI	T1,DMRSHD	;HALF DUPLEX BIT
	TDNN	T1,DMRSTS(W)	;SET?
	 TDZA	T2,T2		;NO
	MOVEI	T2,1		;YES, HALF NOW
	CAIE	T2,(T4)		;ALREADY CORRECT?
	 XORM	T1,DMRSTS(W)	;NO, RESET TO NEW STATE
	RETSKP			;SUCCESS

KF.SE2:				;CHECK FOR OTHER PARAMETERS HERE
KFERRR:	SETO	T1,		;INDICATE ERROR
	POPJ	P,		;AND RETURN

KFNULL:	SETZ	T1,		;INDICATE NO ERROR
	POPJ	P,		;AND RETURN (TO SET NXNIL IN NTMAN)
;KF.RE - READ PARAMETERS FOR NETWORK MANAGEMENT
KF.RE:	JUMPN	T3,KF.RE1	;IF NOT READING STATE, PROCEED
	LDB	T1,PDRSTS	;GET DMR STATE
	MOVE T1,[NCK.OF		;DR%HLT : OFF
		NCK.OF		;DR%FLS : OFF -- HALTING
		NCK.ON		;DR%MAI : ON -- MAINT
		NCK.ON		;DR%WT1 : ON -- STARTING
		NCK.ON		;DR%WTD : ON -- SYNCHRONIZING
		NCK.ON](T1)	;DR%RUN : ON
	RETSKP			;AND RETURN WITH STATE

KF.RE1:	CAIE	T3,1		;SUBSTATE?
	JRST	KF.RE2		;NO, PROCEED
	LDB	T1,PDRSTS	;GET DMR STATE
	SKIPL T1,[EXP -1       	;DR%HLT : OFF
		EXP -1		;DR%FLS : OFF -- HALTING
		EXP -1		;DR%MAI : SERVICE
		NCB.ST		;DR%WT1 : ON -- STARTING
		NCB.SN		;DR%WTD : ON -- SYNCHRONIZING
		EXP -1](T1)	;DR%RUN : ON
	RETSKP			;WE GOT A SUBSTATE, RETURN IT
	SETZ	T1,		;NOTHING, INDICATE NXNIL
	POPJ	P,

KF.RE2:	CAIE	T3,^D1112	;IS THIS ASKING FOR PROTOCOL?
	 JRST	KF.RE3		;NO
	SETZ	T1,		;VALUE OF ZERO IS DDCMP POINT TO POINT
	RETSKP

KF.RE3:	CAIE	T3,^D1111	;ASKING FOR DUPLEX?
	 JRST	KFNULL		;NO
	MOVEI	T1,DMRSHD	;HALF DUPLEX BIT
	TDNN	T1,DMRSTS(W)	;SET?
	 TDZA	T1,T1		;NO, FULL
	MOVEI	T1,1		;YES, HALF
	RETSKP			;THUS
;KF.CL - Clear parameters

KF.CL:	JRST	KFERRR		;DO NOTHING ABOUT THIS JUST YET
	SUBTTL	INTERRUPTS -- INTERRUPT LEVEL INTERFACE TO THE DMR11

                           Comment @

Each DMR11 has two interrupt vector addresses.

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

    "B"	This interrupt is taken when RDYO (DMRRDO) comes up.  In this
	state the DMR11 is wants to perform an output transaction.
	It is these output transactions that drive almost all of the
	interrupt level DMR processing.

    The vector instructions are set up to be JSR's to the locations "DMRIVA",
and "DMRIVB" in the DMR block for the DMR11.  These locations contain the
10 instructions necessary to save the AC's, load "W" with the address
of the particular DMR block, load "F" with the address of the comm region and
dispatch to either of the two interrupt routines "DMRAIV" or "DMRBIV".

                         End comment @
	SUBTTL	DMRAIV -- DMR11 INTERRUPT VECTOR "A" PROCESSING.

;DMRAIV -- ROUTINE TO HANDLE DMR11 INTERRUPT VECTOR "A" (INPUT)
;CALL	MOVE	W,[EXP DMR-BLOCK-ADDRESS]
;	MOVE	F,[EXP DMR-COMM-REGION-ADDRESS]
;	PUSHJ	P,DMRAIV	;CALLED FROM DMRIVA IN THE DMR BLOCK
;RETURN	POPJ	P,		;TO DISMISS THE INTERRUPT.
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAY)
;
;ON MULTI-PROCESSOR SYSTEMS, THIS CODE WILL NEED TO AOSE THE DMR INTERLOCK
;
DMRAIV::AOS	DMRACT(W)	;COUNT THE INTERRUPT
	LDB	T1,PDRSTS	;Get line status
	CAIN	T1,DR%HLT	;If we're stopped
	 PJSP	T1,DMRERR	;  CLEAR "RUN" SO INTS WILL STOP

	MOVE	U,DMRCSR(W)	;GET THE UNIBUS ADDRESS OF THE DMR11
	MOVEI	T1,DMRRDI	;GET THE "RDYI" FLAG
	TION	T1,DMRSL0(U)	;MAKE SURE "RDYI" IS UP
	 PJSP	T1,DMRERR	;  IF IT'S NOT, THEN ITS AN ILLEGAL INTERRUPT
	RDIO	T1,DMRSL4(U)	;Read low-order modem status bits
	RDIO	T2,DMRSL6(U)	;Read high-order bits
	HRL	T2,T1		;Low word,,high word
	MOVEM	T2,DMRMST(W)	;Save last known status
	MOVE	T3,DMRIQT(W)	;GET NUMBER OF NEXT QUEUED TRANSACTION
	CAMN	T3,DMRIQP(W)	;MAKE SURE THAT IT'S DIFFERENT NOT THE "PUTTER"
	 PJSP	T1,DMRERR	;  IF IT IS, THEN WE'RE GETTING UNSOLICITED
				;  INTERRUPTS.  DECLARE DMR ILL.
	ASH	T3,1		;MAKE IT AN OFFSET INTO THE QUEUE
	ADDI	T3,DMRINQ(W)	;RELOCATE BY THE ADDRESS OF THE QUEUE
	MOVE	T1,0(T3)	;GET SEL0 DATA
	ANDI	T1,17		;Only command code
	MOVE	T2,1(T3)	;GET SEL4, SEL6 DATA
	AOS	T3,DMRIQT(W)	;ADVANCE QUEUE TAKER
	CAIL	T3,DMRIQN	;IF ITS TIME TO WRAP AROUND, THEN
	SETZB	T3,DMRIQT(W)	;  WRAP AROUND TO THE FIRST ENTRY
	WRIO	T2,DMRSL6(U)	;STORE TOP WORD
	MOVSS	T2,T2		;GET SEL4 DATA
	WRIO	T2,DMRSL4(U)	;  AND STORE THAT
	MOVEI	T2,17		;COMMAND CODE FIELD
	BCIO	T2,DMRSL0(U)	;CLEAR IT OF OLD VALUE
	BSIO	T1,DMRSL0(U)	;SET NEW COMMAND INTO PLACE
	MOVEI	T1,DMRRQI	;Request bit
	BCIO	T1,DMRSL0(U)	;Clear it to say command ready
	MOVEI	T2,DMRRDI	;DMR's response bit
	MOVEI	T4,2000		;Wait a while
	TIOE	T2,DMRSL0(U)	; for DMR to respond
	 SOJG	T4,.-1		;Not accepted yet, wait a while
	SKIPG	T4		;Well...
	 PJSP	T1,DMRERR	;Sigh, timed out - DMR must be ill
	CAME	T3,DMRIQP(W)	;Is queue empty now?
	 BSIO	T1,DMRSL0(U)	;No, request another interrupt
		;Note that we can't wait since DMR may already be doing RDO
	POPJ	P,		;ALL DONE
	SUBTTL	DMRBIV -- DMR11 Interrupt vector "B" processing.

;DMRBIV -- ROUTINE TO HANDLE DMR11 INTERRUPT VECTOR "B" (OUTPUT)
;CALL	MOVE	W,[EXP DMR-BLOCK-ADDRESS]
;	MOVE	F,[EXP DMR-COMM-REGION-ADDRESS]
;	PUSHJ	P,DMRBIV	;CALLED FROM DMRIVB IN THE DMR BLOCK
;RETURN	POPJ	P,		;TO DISMISS THE INTERRUPT
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAY)
;
;ON MULTI-PROCESSOR SYSTEMS THIS CODE WILL NEED TO AOSE THE DMR INTERLOCK.
;
DMRBIV::AOS	DMRBCT(W)	;COUNT THE INTERRUPT
	LDB	T1,PDRSTS	;Get line status
	CAIN	T1,DR%HLT	;See if we're OK
	 PJSP	T1,DMRERR	;  IF WE'RE NOT, CLEAR RUN AND RETURN

	MOVE	U,DMRCSR(W)	;GET THE UNIBUS ADDRESS OF THE DMR
	RDIO	P1,DMRSL2(U)	;READ STATUS BITS
	TRNN	P1,DMRRDO	;BETTER WANT AN OUTPUT TRANSACTION
	 PJSP	T1,DMRERR	;ILLEGAL INTERRUPT. CRASH DMR
	RDIO	P2,DMRSL4(U)	;Read first data port
	RDIO	P3,DMRSL6(U)	;Read second data port

	MOVEI	T1,DMRRDO	;GET THE RDYO BIT
	BCIO	T1,DMRSL2(U)	;CLEAR IT TO LET THE DMR CONTINUE
	ANDI	P1,7		;Get output command code
	CAIG	P1,4		;Legal?
	 XCT	[JRST	D8RTXC	;(0) Xmit complete (TBA/CCO)
		 JRST	D8RCTO	;(1) Control out (Status from DMR)
		 JSP	T1,DMRERR ;(2) Reserved code
		 JSP	T1,DMRERR ;(3) Reserved code
		 JRST	D8RRCV](P1) ;(4) Receive complete (RBA/CCO)
				;ROUTINE DISPATCHED TO WILL RETURN.
	JSP	T1,DMRERR	;Output command out of range
	SUBTTL	D8RTXC -- DMR11 TRANSMIT COMPLETE TRANSACTION PROCESSING.

;    This routine frees output buffers when a TRANSMIT COMPLETE transaction
;declares that the DMR has output all data in the buffer. It:
;	   1)	Returns the transmitted (and DDCMP ACK'd) message to NETSER
;	   2)	Performs all necessary book-keeping
;	   3)	Tries to fill the freed output buffer
;
;called with:
;	F, W 	    := COMM and DMR pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the DMR11
;
D8RTXC:	PUSHJ	P,GETBAD	;GET T4 SET UP TO POINT TO Buffer
	TLNE	T4,3		;MAKE SURE THAT BUFFER-OUT STARTS ON EVEN BYTE
	 PJSP	T1,DMRERR	;  IF ODD BYTE, THEN DMR11 SCREWED US

	MOVSI	T2,(1B0)	;GET THE BUFFER IN-USE MARK BIT
	TDNN	T2,0(T4)	;  AND MAKE SURE THAT IT'S SET
	 PUSHJ	P,NTDSTP##	;WE'VE SCREWED UP THE BUFFERS [DBUG] 
	ANDCAM	T2,0(T4)	;CLEAR THE USE BIT

	AOS	DMRXMC(W)	;Another message transmitted
	SOSGE	DMRCTA(W)	;One less buffer awaiting ACK
	 PJSP	T1,DMRERR	;++ COUNT NEGATIVE(or wrong buffer returned).
	HRRZ	T3,DMRWTA(W)	;Get address of first message out
	SKIPN	T3		;Anything?
	 PJSP	T1,DMRERR	;++ TBA/CCO with no message sent
	HRRZ	T2,MB.NXT(T3)	;Get address of next message waiting ack
	HRRZM	T2,DMRWTA(W)	;It's now first in q
	HLLZS	MB.NXT(T3)	;Let's not confuse qs
	LDB	T2,PDRSTS	;Get current status
	CAIN	T2,DR%MAI	;If in Maint mode
	 JRST	D8RTXM		;Must defer posting done (NETSER core alloc..)
	MOVEI	T1,DR%RUN	;Just in case we're starting up
	CAIE	T2,DR%WT1	;See if waiting for first msg (SNH!)
	 CAIN	T2,DR%WTD	;More likely, waiting for done from first msg
	DPB	T1,PDRSTS	;If so, mark the line as really up!
	MOVEI	T1,DC.IOD	;What we did
	PUSHJ	P,CALUSR	; -- Tell user it was sent
	PUSHJ	P,XMTBUF	;NOW GO TRY TO FILL THE JUST FREED BUFFER
	POPJ	P,		;All set, dismiss interrupt

D8RTXM:	AOSG	DMRMAC(W)	;Count one more message on queue
	HRRZ	T2,DMRMAI(W)	;Get pointer to current first message
	HRRM	T2,MB.NXT(T3)	;String it after this one
	HRRZM	T3,DMRMAI(W)	;And put this one at the head of the queue
	PUSHJ	P,XMTBUF	;If possible, give the DMR more to do...
	POPJ	P,		;Done
	SUBTTL	D8RRCV -- ROUTINE TO HANDLE RECEIVE COMPLETE TRANSACTIONS.

;    This routine handles RECEIVE COMPLETE transactions.  These
;transactions consist of input messages coming in over the synchronous
;line.  This routine:
;	   1)	Frees the receive buffer. (No worry about races, this
;		code runs under the DMR interlock on multiprocessor
;		systems.  On a single processor system, it runs at
;		interrupt level.  Hence no one will try to allocate
;		the buffer between when it is freed and when it is
;		processed
;	Because the DMR may have received a message before NETSER has assigned
;	a receive PCB, and because the DMR has already DDCMP ACK'd the message,
;	we maintain a queue of ACK'd but unposted messages.  When the buffer is
;	finally posted, we dequeue the buffer in D8RRDD.
;	   2)    If no PCB available, queue received buffer
;	   	 Else pass buffer to NETSER
;
;called with:
;	F, W 	    := COMM and DMR pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the DMR11
;
D8RRCV:	PUSHJ	P,FREBOI	;FREE THE INPUT BUFFER, (SET UP T3/T4)
	  PJSP	T1,DMRERR	;?? DMR11 GAVE BAD BDL ADDRESS ??
	SOS	DMRRBC(W)	;One less buffer outstanding
	MOVE	T1,DMRRBG(W)	;Get GIVE pointer
	SUB	T1,DMRRBC(W)	;Next expected in(=Last given - # left in DMR)
	SKIPGE	T1		;But if time to wrap around,
	 ADDI	T1,DR%NBF	;Do so now
	MOVE	T2,T1		;Get a copy of the buffer number
	ADDI	T1,DMRRCC(W)	;Index into byte count table
	MOVEM	T3,(T1)		;Save byte count of buffer
	IMULI	T2,TBFSIZ	;Offset in buffer space
	ADDI	T2,TBFIN0(F)	;Make 10 CBP (Buffers start on word boundary)
	CAME	T2,T4		;Make sure that predicted matches actual
	 PUSHJ	P,NTDSTP##	;++ DMR returned wrong buffer
	PJRST	RCVMSG		;Now see if we can get a buffer to put it in
	SUBTTL	D8RCTO -- Routine to handle CONTROL-OUT (status) transactions

;This routine handles CONTROL-OUT (status) transactions.  These transactions
;are all errors detected by the DMR.  Fatal errors (noted below) halt the
;DMR.  Non-fatal errors indicate that something may or may not be wrong
;with the link.
;This code:
;	1)	Converts the bit mask returned by the DMR into a dense
;		code for counting
;	2)	If the error was fatal, restarts the DMR and the line,
;		notifys NETSER
;	3)	If the error was non-fatal, ignores it for the time being.
;		Version 3 should check for a threshold for each error type,
;		and restart the line if exceeded.
;	For now, DSR drop is considered FATAL only if it happened on a
;	switched line.  This is to allow for glitches on short haul modems.
;	A case can be made that it should always be fatal.  But until someone
;	convinces me, the crok remains...
;
;Called with:
;	F,W		:= COMM and DMR pointers
;	P1,P2,P3	:= SEL2, SEL4, and SEL6 at interrupt time

D8RCTO:	MOVEI	T1,DMRSSW	;Get switched line bit
	TDNE	T1,DMRSTS(W)	;Is this one?
	 TRNN	P3,DMROHU	;Yes, DSR drop? (Other end hung up)
	TRNE	P3,DMROMM!DMROLM!DMROSR!DMRONX!DMROHC ;Fatal error?
	 TLOA	P3,400K		;Yes, set composite error
	TLZ	P3,400K		;No, Unnecessary, but clear anyhow
	TRNE	P3,DMRONK	;Too many NAKs?
	 AOS	DMRCTT+00(W)	;Yes, count event
	TRNE	P3,DMROTO	;Too many REPs?
	 AOS	DMRCTT+01(W)	;Yes, count event
	TRNE	P3,DMRONB	;Too many NO BUFFER NAKs?
	 AOS	DMRCTT+02(W)	;Yes, count event
	TRNE	P3,DMROMM	;Maint message received?
	 PUSHJ	P,[AOS	DMRCTT+03(W)	;Yes
		   PJRST  RCVMAI] ;Go see what to do
	TRNE	P3,DMROLM	;Received message too long?
	 AOS	DMRCTT+04(W)	;Yes
	TRNE	P3,DMROHU	;DSR drop (Other end hung up)?
	 AOS	DMRCTT+05(W)	;Yes
	TRNE	P3,DMROSR	;Start received while running?
	 JRST	[AOS	DMRCTT+06(W) ;Yes
		 JRST	RCVSTR]	;Process start received
	TRNE	P3,DMRONX	;NXM detected by DMR?
	 AOS	DMRCTT+07(W)	;Yes
	TRNE	P3,DMROHC	;Halt complete?
	 AOS	DMRCTT+10(W)	;Yes
	JUMPGE	P3,CPOPJ##	;Return if no action required
	PJRST	DMRKIL		;For now, just restart DMR
	SUBTTL	RCVMSG -- ROUTINE TO DISPATCH ON THE DDCMP MESSAGE TYPE.

;Called from D8RRCV and D8RRDD with:
;	F, W 	    := com and DMR pointers
;
RCVMSG:	AOS	T4,DMRRBT(W)	;Get index of next buffer from DMR
	CAIL	T4,DR%NBF	;Too big?
	 SETZB	T4,DMRRBT(W)	;Yes, wrap around
	MOVE	T3,T4		;Get a copy of buffer number
	ADDI	T3,DMRRCC(W)	;Index into count table
	MOVE	T3,(T3)		;Get saved count
	IMULI	T4,TBFSIZ	;Offset by 10 size of buffer
	ADDI	T4,TBFIN0(F)	;Get address of received data
	LDB	T1,PDRSTS	;Get line status
	CAIL	T1,DR%WT1	;Running?
	 JRST	RCVDAT		;Yes, data message
	CAIN	T1,DR%MAI	;Maintenance mode?
	 JRST	RCVMA1		;Yes, maintenance message
	PJSP	T1,DMRERR	;++ Message received but DMR not running!

;Here if can't get core for a received message.
;Deadvance the RCC queue, so we don't skip a message later...

RCVABT:	MOVEI	T4,DR%NBF-1	;Get the maximum queue entry
	SOSGE	DMRRBT(W)	;Back up the queue
	 MOVEM	T4,DMRRBT(W)	;If we went thru zero, wrap properly
	POPJ	P,		;We'll get this one eventually at DMRSEC
	SUBTTL	RCVMAI -- ROUTINE TO PROCESS INCOMING MAINTENANCE MESSAGES.


RCVMAI:	LDB	T1,PDRSTS	;Get line's state
	CAIG	T1,DR%MAI	;If not in some normal state, leave OPR alone
	 JRST	RCVMA0		;We were halted or halting
	CAIL	T1,DR%WT1	;If user thought we were running
	 PUSHJ	P,DMRPDN	;Tell him protocol down
	MOVE	U,OPRLDB##	;LINE JUST CRASHED.  TELL OPERATOR
	PUSHJ	P,INLMES##	;  ABOUT THIS
	ASCIZ	/%% Maintenance message received on /
	PUSHJ	P,PRDMR		;PRINT THE LINE NUMBER
	PUSHJ	P,INLMES##	;WE ALSO WANT TO TIME-STAMP THIS
	ASCIZ	/ at /
	PUSHJ	P,PRDTIM##	;SO PRINT THE TIME OF DAY.
RCVMA0:	LDB	T1,PDRSTS	;Get status now
	MOVEI	T2,DR%MAI	;GET THE MAINTENANCE STATE
	DPB	T2,PDRSTS	;  AND SET THIS LINE TO IT.
	CAILE	T1,DR%MAI	;If now running (or starting)
	 PUSHJ	P,[DPB	T1,PDRSTS ;Restore current status
		   PUSHJ P,DMRPDN ;Free our buffers, tell user
		   MOVEI T2,DR%MAI ;Now, assume user will cooperate
		   DPB	T2,PDRSTS  ; ...
		   MOVEI T1,DC.IMR ;MM received code
		   PJRST CALUSR] ;Tell user
	LDB	T1,PDRSTS	;Get (possibly changed) state
	CAIE	T1,DR%MAI	;And if user didn't leave us in Maint mode
	 PJRST	RCVMAR		;well, then, toss the message (Must restart)

	MOVEI	T1,DMRSMT	;The start in maintenance bit
	IORM	T1,DMRSTS(W)	;Where to set it
RCVMAR:	PUSHJ	P,DMRINI	;Restart the DMR in the new mode
	 JFCL			;(It halted itself)
	POPJ	P,		;And it tossed the message
;Called from RCVMSG with:
;	F, W 	    := COMM and DMR pointers
;	T3	    := character count of received buffer
;	T4	    := a CBP to the first byte of the BDL (set up by FREBOI)
;

RCVMA1:	JUMPE	T3,RCVBUF	;IF EMPTY MSG, DON'T GIVE IT TO NETSER
	PUSHJ	P,GETBUF	;Get a receive buffer
	JUMPE	T1,RCVABT	;If refused, forget it
	AOS	DMRRMC(W)	;Another message received
	MOVE	T2,MB.FMS(T1)	;Get pointer to MSD
	JUMPE	T2,[MOVSI T2,(POINT 8,0)	;If ANF style
		    HRRI  T2,STCDAT(T1)		;make a byte pointer
		    JRST  RCVMA2] 	;And go copy
	CAMLE	T3,MD.BYT(T2)	;Make sure we got a nice, big buffer
	 PUSHJ	P,NTDSTP##	;++ User gave too small a buffer
	MOVEM	T3,MD.BYT(T2)	;Store the actual byte count
	MOVE	T6,MD.ALA(T2)	;Byte pointer is indexed by T6
	MOVE	T2,MD.PTR(T2)	;Fetch pointer to first byte
RCVMA2:	SOJGE	T3,[PUSHJ P,ILDCB ;Get a byte from DMR buffer
		    IDPB  S,T2	;Copy into user's buffer
		    JRST  RCVMA2];And loop till done
	MOVE	T3,T1		;Copy address of buffer
	MOVEI	T1,DC.IIC	;Input complete
	PUSHJ	P,CALUSR	;Call user
	PJRST	RCVBUF		;Go fetch another buffer
	SUBTTL	RCVSTR	-- START received control out from DMR

;Here when START received control out from DMR

RCVSTR:	MOVE	U,OPRLDB##	;WE WANT TO TELL THE OPR, GET HIS LDB ADDR
	PUSHJ	P,INLMES##	;SEND FIRST PART OF MESSAGE
	ASCIZ	/%% Unexpected restart on /
	PUSHJ	P,PRDMR		;PRINT THE FULL DMR#
	PUSHJ	P,INLMES##	;FINISH MESSAGE OFF WITH TIME OF DAY
	ASCIZ	/ at /
	PUSHJ	P,PRDTIM##	;PRINT TIME AND CRLF
	PJRST	DMRKIL		;Restart the line
	SUBTTL	RCVDAT -- ROUTINE TO PROCESS INCOMING DATA MESSAGES.

;Called from RCVMSG with:
;	F, W 	    := COMM and DMR pointers
;	T4	    := a CBP to the first byte of the BDL (set up by FREBOI)
;	T3	    := Byte count of message
;
RCVDAT:	LDB	T1,PDRSTS	;Get current state
	CAIGE	T1,DR%MAI	;If not in some sort of protocol
	 PJSP	T1,DMRKIL	;Initiate a halt sequence
	MOVEI	T2,DR%RUN	;Just in case...
	CAIE	T1,DR%WT1	;If in a start sequence 
	 CAIN	T1,DR%WTD	; (this is one too...)
	DPB	T2,PDRSTS	;Then we now know that the line is up
				;(And our previous lie was harmless...)
	JUMPE	T3,RCVBUF	;If message is null, ignore it
	PUSHJ	P,GETBUF	;Get a buffer (arg in , yes, T3)
	JUMPE	T1,RCVABT	;If can't get one, simply starve the DMR
				;(If it takes long enf, it will NAK some
				;other message till we give it a new one...)
	AOS	DMRRMC(W)	;Another message received
	PUSH	P,T1		;Save our pointer to the buffer

;Figure out what sort of message we got...

	MOVE	T2,MB.FMS(T1)	;Get the MSD pointer
	JUMPE	T2,RCVDA1	;If none, we are filling a PCB

	;Message is DECnet

	CAMLE	T3,MD.ALL(T2)	;Make sure the data will fit
	 PUSHJ	P,NTDSTP##	;User gave us too small a buffer
	MOVEM	T3,MD.BYT(T2)	;Store the byte count
	MOVE	T6,MD.ALA(T2)	;Byte pointer is indexed by T6
	MOVE	T2,MD.PTR(T2)	;Fetch the byte pointer
	JRST	RCVDA2		;Now go copy the data

	;Message is ANF

RCVDA1:	MOVE	T2,PCBCTR(T1)	;GET THE LENGTH OF THE INPUT PCB
	CAILE	T3,(T2)		;MAKE SURE THIS MSG WILL FIT
	 PJSP	T1,DMRERR	;DMR should have NAK'd
	MOVEM	T3,PCBCTR(T1)	;RETURN THE LENGTH TO NETSER
	MOVE	T2,PCBPTR(T1)	;GET THE BYTE POINTER TO FILL THE PCB WITH
;	JRST	RCVDA2		;Now, go copy the data
;AT THIS POINT
;	T2	:= A BYTE POINTER INTO THE PCB
;	T3	:= THE NUMBER OF BYTES TO MOVE
;	T4	:= A CBP INTO THE MESSAGE TO COPY.
;FOR EFFICIENCY, COPY AS MANY SETS OF FOUR BYTES AS POSSIBLE.

RCVDA2:	MOVEM	T3,T1		;GET A COPY OF THE COUNT
	LSH	T3,-2		;GET THE NUMBER 4 BYTE SETS TO COPY

RCVDA3:	SOJL	T3,RCVDA4	;COUNT OFF 4 MORE BYTES
	MOVS	S,(T4)		;GET BYTES #1 AND #0
	IDPB	S,T2		;STORE #0
	LSH	S,-^D8		;SHIFT #1 DOWN
	IDPB	S,T2		;  AND STORE THAT
	LSH	S,-^D<8+2>	;GET BYTES #3 AND #2
	IDPB	S,T2		;STORE #2
	LSH	S,-^D8		;SHIFT #3 DOWN
	IDPB	S,T2		;  AND STORE THAT
	AOJA	T4,RCVDA3	;ADVANCE THE CBP 4 BYTES AND GO DO NEXT SET

;HERE TO COPY THE LAST 3 OR FEWER BYTES
RCVDA4:	ANDI	T1,3		;GET THE COUNT MODULO 4
	PUSHJ	P,DLDCB		;PRE-DECREMENT THE CBP SINCE AOJA IS 1 TOO FAR
RCVDA5:	SOJL	T1,RCVDA6	;COUNT DOWN THE NEXT BYTE
	PUSHJ	P,ILDCB		;LOAD A CANNONICAL BYTE
	IDPB	S,T2		;  AND STORE IT
	JRST	RCVDA5		;LOOP OVER THE LAST FEW

	;Copy complete.  Give up the DMR's buffer NOW because:
	; - We're done with it
	; - D8RRDD would try to dequeue it again from FEKINT
	;Then tell NETSER that a message has arrived
RCVDA6:	PUSHJ	P,RCVBUF	;Release the DMR's Buffer
	POP	P,T3		;Get buffer address
	MOVEI	T1,DC.IIC	;GET INPUT DONE FUNCTION CODE
	PJRST	CALUSR		;CALL USER
	SUBTTL	RCVBUF -- POST AN INPUT BUFFER RECEIVE REQUEST

;    This routine:
;	   1)	Checks for a free input buffer.  If none, it returns.
;	   2)	Allocates the input buffer and marks it as "in use".
;	   3)	Queue's a RBA/CCI transaction for the DMR11
;
;Called from "many places" with:
;	F, W 	    := COMM and DMR pointers
;
RCVBUF:	LDB	T1,PDRSTS	;GET OUR STATE
	MOVEI	T2,DMRSTV	;If we're starving this line now,
	TDNN	T2,DMRSTS(W)	; then we don't feed the DMR
	 CAIGE	T1,DR%MAI	;Running?
	POPJ	P,		;  IF WE CAN'T, JUST EXIT NOW
	AOSG	T1,DMRRBC(W)	;INCREMENT COUNT OF RCV BUFFERS QUEUED
	 PUSHJ	P,NTDSTP##	;++ SHOULD NEVER BE NEGATIVE
	CAILE	T1,DR%NBF	;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
	JRST	[SOS DMRRBC(W)	;CORRECT THE COUNT
		 POPJ P,]	;  AND RETURN

	PUSHJ	P,SAVE4##	;Save ACs in case coming through FEKser
	AOS	P4,DMRRBG(W)	;Get next buffer to give
	CAIL	P4,DR%NBF	;Time to wrap?
	 SETZB	P4,DMRRBG(W)	;Yes, do so now
	MOVE	T1,P4		;Save buffer number for 11 address
	IMULI	P4,TBFSIZ	;Compute buffer offset
	ADDI	P4,TBFIN0(F)	;Make 10 address

	IMULI	T1,EBFSIZ	;11 Buffer offset
	ADDI	T1,EBFIN0	;Relocate to buffer space
	ADD	T1,DMREAD(W)	;Make 11 physical address

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

;DROP TRHOUGH TO CONSTRUCT THE RBA/CCI Command
;FROM ABOVE.  HERE TO CONSTRUCT AND QUEUE THE BUFFER-IN TRANSACTION

	LSHC	T1,-^D16	;Put low 16 address bits in SEL 4
	LSH	T2,-2		;Skip bits 0 & 1
	HRRI	T2,EBFSIZ	;Put CC in SEL6
	DPB	T1,[POINT 2,T2,21] ;Insert high address bits
	MOVEI	T1,DMRRBI	;RBA/CCI command

;NOW QUEUE THE BUFFER IN TRANSACTION

	PUSHJ	P,DMRINP	;QUEUE TRANSACTION
	 POPJ	P,		;AND RETURN
	POPJ	P,		;(IGNORE SKIP RETURN)
	SUBTTL	XMTBUF -- ROUTINE TO TRANSMIT DDCMP MESSAGES.

;Called from "most anywhere" with interrupts off and:
;	F, W 	    := COMM and DMR pointers
;
XMTBUF:	PUSHJ	P,SAVE4##	;THIS CODE CLOBBERS THEM ALL
	LDB	P1,PDRSTS	;GET OUR STATE
	CAIGE	P1,DR%MAI	;If not running,
	POPJ	P,
	SKIPN	DMRWTO(W)	; we need a message to send
	 POPJ	P,		;and we don't have one
	AOSG	T1,DMRCTA(W)	;INCREMENT COUNT OF XMIT BUFFERS QUEUED
	 PUSHJ	P,NTDSTP##	;++ SHOULD NEVER BE NEGATIVE
	CAILE	T1,DR%NBF	;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
	 JRST	[SOS DMRCTA(W)	;CORRECT THE COUNT
		 POPJ P,]	;  AND RETURN

	AOS	P4,DMRXBG(W)	;Get number of next buffer to send
	CAIL	P4,DR%NBF	;Wrap around time?
	 SETZB	P4,DMRXBG(W)	;Yes, do so
	IMULI	P4,TBFSIZ	;Make into buffer offset
	ADDI	P4,TBFOU0(F)	;10 virtual address

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

;   XMTDAT sends DDCMP data messages. It:
;	    1)	Requeues the PCB of the message it is outputing
;		to the "awaiting ACK" (DMRWTA) queue.  (We are
;		interlocked, so there is no fear of races.)
;	    2)	Copies the "header" portion of the message to the
;		correct output buffer in the DMR page.
;	    3)	Copies, with compression, the "data"
;		portion of the PCB.  (Ie. The PCBPT2 data.)
;	    4)	It queues a TBA/CCI transaction for the DMR11
;
;Called from XMTBUF with:
;	F, W 	    := COMM and DMR pointers
;	P4	    := The address of the BUFFER to use for the message.
;
;Register usage.
;	S     =   The character we are currently hacking
;	T1    =   Count of chars left in pcb
;	T2    =   Byte pointer into the pcb
;	T3    =   Total number of chars put in the output message buffer
;	T4    =   A "canonical" byte pointer to the output message buffer
;
;	P1-P3 =   "Scratch" registers for the conversion routines.
;	P4    =   Pointer to TBFOU?.  Ie. the beginning of the buffer
;
;	U     =   Pointer to the buffer
;	F     =   Pointer to Comm area
;	W     =   Pointer to DMR block
;
XMTDAT:	PUSH	P,U		;Save U for the duration
	HRRZ	U,DMRWTO(W)	;GET THE NEXT PCB AWAITING OUTPUT
	HRRZ	T1,MB.NXT(U)	;GET THE ADDRESS OF THE ONE AFTER THAT
	HRRZM	T1,DMRWTO(W)	;  AND MAKE THAT THE "NEXT" AWAITING OUTPUT

;FIRST PUT THE BUFFER ON THE END OF THE "AWAIT ACK" QUEUE

	HLLZS	MB.NXT(U)	;MAKE SURE THERE ARE NO STRAY POINTERS
	HRRZ	T1,DMRWTA(W)	;GET THE FIRST PCB IN THE AWAIT ACK QUEUE
	JUMPE	T1,[HRRZM U,DMRWTA(W) ;IF THERE AREN'T ANY, MAKE THIS THE FIRST
		    JRST XMTDA2] ;  AND CONTINUE WITH MAIN CODE.
XMTDA1:	HRRZ	T2,MB.NXT(T1)	;T2 := THE "NEXT" PCB
	JUMPE	T2,[HRRM U,MB.NXT(T1) ;IF THERE ISN'T ONE, MAKE "U" THE LAST
		    JRST XMTDA2] ;  AND CONTINUE WITH MAIN CODE.
	MOVEI	T1,(T2)		;If not at end of queue, go to next buffer
	JRST	XMTDA1		;LOOP UNTIL WE FIND THE END OF THE QUEUE.
;  FROM HERE ON, THE FOLLOWING CONVENTIONS GENERALLY HOLD
;	T3 := COUNT OF THE NUMBER OF BYTES SO FAR
;	T4 := A BYTE POINTER (PDP-10 STYLE) TO THE OUTPUT BUFFER.
;  WHEN WE ARE ALL DONE COPYING THE DATA, WE WILL SWAP THE BYTES
;  ALL AT ONCE.

XMTDA2:	MOVE	T2,MB.FMS(U)	;Get pointer to first MSD
	JUMPE	T2,XMTANF	;If none, must be an ANF-10 message
	MOVSI	T4,(POINT 8,)	;Start off with T4 being a byte pointer
	HRRI	T4,0(P4)	; to the output buffer
	PUSH	P,T0		; Needed for extend (really U)
	PUSH	P,[EXP	0]	;Save total bytes in message
XMTDEC:	MOVE	T0,MD.BYT(T2)	;GET THE LENGTH OF THIS SEGMENT
	MOVE	T6,MD.ALL(T2)	;GET ALLOCATED LENGTH
	CAMLE	T0,T6		;IS THE MESSAGE OVERLOADED?
	 PUSHJ	P,NTDSTP##	;++ DECNET MESSAGE SEGMENT OVERLOADED
	MOVE	T6,MD.ALA(T2)	;BYTE POINTER IS INDEXED BY T6
	MOVE	T1,MD.AUX(T2)	; AND IT'S BYTE ADDRESS
	MOVE	T3,T0		;COPY THE LENGTH FOR THE DESTINATION
	ADDM	T3,(P)		; AND ACCUMULATE THE TOTAL COPIED
	EXTEND	T0,[MOVSLJ]	;COPY THE SEGMENT
	 PUSHJ	P,NTDSTP##	;++ HARDWARE BROKEN
	HRRZ	T2,MD.NXT(T2)	;STEP TO THE NEXT SEGMENT
	JUMPN	T2,XMTDEC	; AND IF THERE IS ONE, COPY THAT TOO
	POP	P,T3		;T3 := THE TOTAL LENGTH
	POP	P,T0		;RESTORE "U"
	CAILE	T3,DMRMMS*4	;MAKE SURE THE LENGTH WAS REASONABLE
	 PUSHJ	P,NTDSTP##	;++ MESSAGE WAS TOO BIG
	JRST	XMTXIT		;GO SWAP THE BYTES AND SEND THE MESSAGE
;HERE FOR ANF-10 STYLE BUFFERS
XMTANF:	LDB	T1,PDRSTS	;FIRST GET THE STATE, AND
	CAIN	T1,DR%MAI	;IF THIS IS A MAINT MESSAGE
	 JRST	XMTANM		;  GO PROCESS THE STC-BLOCK FORMAT


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

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

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

	SOS	T1		;ROUND DOWN, AND SHIFT TO GET THE
	LSH	T1,-2		;  NUMBER OF WORDS TO BLT (LESS 1)
	MOVSI	T2,(1B0)	;Get buffer in use bit
	TDNN	T2,0(P4)	;Make sure its set
	 PUSHJ	P,NTDSTP##	;++ Failed to interlock buffer before sending

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

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

	LDB	T1,PCBPCV##	;GET THE CONVERSION CODE
	CAIL	T1,PCV.NC	;RANGE CHECK
	CAILE	T1,PCV.BN	;  THE CONVERSION CODE
	PUSHJ	P,NTDSTP##	;++ NETSER GAVE GARBAGE
	SKIPE	PCBCT2(U)	;IF NO SECOND BUFFER, DONT TRY TO SEND IT
	 PUSHJ	P,@[EXP C.NC,C.LP,C.BN](T1) ;CALL CONVERSION ROUTINE
	JRST	XMTXIT		;Go swap bytes and send it
;Here to send an ANF-10 maint message (STC block)

XMTANM:	HLRZ	T3,STCBLK(U)	;Get byte count where XMTXIT needs it
	CAILE	T3,DMRMMS*4	;If unreasonable, should have been caught
	 PUSHJ	P,NTDSTP##	;++ MM too long! (by NETSER...)
	MOVEI	T1,0(P4)	;Point to the buffer we chose
	HRLI	T1,STCDAT(U)	;Address of data to go
	MOVEI	T2,-1(T3)	;Len (bytes) Rnd up (+3) - 1 wd (-4)
	LSH	T2,-2		;Convert to words
	ADDI	T2,(T1)		;End address for BLT
	BLT	T1,(T2)		;Copy data into buffer
;	JRST	XMTXIT		;Swap bytes and send message
;NOW WE HAVE THE BUFFER FILLED, BUT WE MUST SWAP THE BYTES FOR THE
;  STUPID UBA...

XMTXIT:	MOVNI	T1,3(T3)	;GET MINUS THE NUMBER OF BYTES (ROUND UP)
	ASH	T1,-2		;CONVERT TO A WORD COUNT
	HRL	T4,T1		;SET UP THE LH OF THE AOBJN POINTER
	HRRI	T4,(P4)		;SET UP THE RH TO POINT TO THE BUFFER

XMTSWP:	MOVE	T1,(T4)		;GET THE NEXT SET OF 4 BYTES
	LSH	T1,-4		;POSITION THE "LAST" BYTE
	DPB	T1,BYTABL+3	;  AND STORE IT
	LSH	T1,-^D8		;POSITION THE NEXT ONE
	DPB	T1,BYTABL+2	;  AND STORE THAT
	LSH	T1,-^D8		;GET THE NEXT TO FIRST
	DPB	T1,BYTABL+1	;  ..
	LSH	T1,-^D8		;GET THE FIRST
	DPB	T1,BYTABL+0	;  ..
REPEAT 0,<	;[DBUG] Waste CPU time and clear 10 only bits
	MOVE	T1,[600K,,600K]	;This makes looking at xmit buffers
	ANDCAM	T1,(T4)		;Much easier when debugging
>;End REPEAT 1
	AOBJN	T4,XMTSWP	;LOOP OVER THEM ALL

;Now, figure out what to tell the DMR

	MOVSI	T1,(1B0)	;First, reset the buffer in use bit
	IORM	T1,0(P4)	;Since the conversion code (BLT) stomped on it
	MOVEI	T1,(T3)		;COPY THE MESSAGE LENGTH
	POP	P,U		;Restore U
	PUSHJ	P,XMTBDH	;WRITE HEADER, BDL AND QUEUE OUTPUT TO DMR
	 PJSP	T1,DMRERR	;Error, reload dmr
	POPJ	P,		;SUCCESS
	SUBTTL	XMTBDH -- Build and Queue DMR transaction for output

;XMTBDH Routine to Build and Queue DMR transaction for output
;CALL	T1 := LENGTH OF MESSAGE IN BYTES
;	F, W, P4 := SET UP AS FOR CALL TO XMT???
;RETURN	CPOPJ	IF DMR11 IS DEAD
;	CPOPJ1	IF BUFFER-OUT TRANSACTION SUCCESSFULY QUEUED

XMTBDH:	HRRZ	T2,T1		;Get Low-order bits of count
	CAIG	T1,EBFSIZ	;Message too big?
	 TRZE	T2,740K		;DDCMP max is 14 bits
	PUSHJ	P,NTDSTP	;Attempt to send ridiculous message
	MOVEI	T1,(P4)		;Data address
	SUBI	T1,(F)		;In-page address
	LSH	T1,2		; in 11 bytes
	ADD	T1,DMREAD(W)	;Make into 11 address
	DPB	T1,[POINT 16,T2,17] ;Low address for SEL 4
	LSH	T1,-^D16	;Get high address
	DPB	T1,[POINT 2,T2,21] ;Put in high 2 bits of SEL6
	MOVEI	T1,DMRTBI	;Command we want executed
	PJRST	DMRINP		;WILL SKIP IF DMR STILL RUNNING
	SUBTTL	ANF-10 specific conversion/compression routines

;C.NC	COPY. NO CONVERSION

	T0==T1-1		;REALLY IS "U" (WE NEED 5 AC'S FOR EXTEND)

C.NC:	PUSH	P,T0		;SAVE OUR TEMP
	MOVE	T1,PCBPT2(U)	;GET THE BYTE POINTER FOR IT
	MOVE	T0,PCBCT2(U)	;GET THE LENGTH OF THE STRING TO MOVE
	ADD	T3,T0		;UPDATE T3 TO REFLECT IMPENDING COPY
	PUSH	P,T3		;SAVE IT FOR A BIT
	MOVE	T3,T0		;MAKE SOURCE AND DEST LENGTHS THE SAME
	EXTEND	T0,[MOVSLJ]	;MOVE THE SLUDGE
	PUSHJ	P,NTDSTP##	;++ HARDWARE IS BROKEN?
	POP	P,T3		;UPDATE THE COUNT OF BYTES IN THE BUFFER
	POP	P,T0		;RESTORE OUR "TEMP"
	POPJ	P,		;  AND WE'RE DONE (WASN'T THAT EASY...)
;C.BN	COPY. BINARY CONVERSION

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

;ROUTINE TO DO A "DECREMENT AND LOAD BYTE"

C.DLB:	MOVE	S,T4		;COPY THE BYTE POINTER
	SETO	T4,		;GET A -1
	ADJBP	T4,S		;BACK IT UP
	LDB	S,T4		;GET THE BYTE
	POPJ	P,		;  AND RETURN
	SUBTTL	GETBAD -- ROUTINE TO FIND A BUFFER GIVEN IT'S UNIBUS ADDRESS.

;Called from D8R?O? with:
;	F, W 	    := COMM and DMR pointers
;	P1, P2, P3  := SEL2, SEL4 and SEL6 of the DMR11 (Which contain
;		       the unibus address of a BUFFER)
;Returns
;	CPOPJ	With T4 := CBP pointing to the BUFFER described in P2 & P3.
;		     T3 := Byte count of message
;
GETBAD:	MOVE	T1,P2		;16 Bit address
	HRLZ	T4,P3		;High bits and count
	SETZ	T3,		;Clear hi bits
	LSHC	T3,4		;Get 2 high bits (skip 2 10-only bits)
	LSH	T3,^D16		;Shift to end of word (B16 & B17)
	IORI	T1,(T3)		;18 bit unibus address
	SUB	T1,DMREAD(W)	;Convert to page offset
	DPB	T1,[POINT 2,T1,17-2] ;Byte in 10 word to LH
	LSH	T1,-2		;Now should have CBP
	HRRZ	T3,T1		;Get page offset
	CAIGE	T3,DMRPPL*1K	;If greater than end of region
	 TLNE	T1,^-3		; or if bits other than byte # set
	PUSHJ	P,NTDSTP##	;++ Miscomputed byte pointer
	ADDI	T1,(F)		;Relocate CBP to 10 virtual address
	SETZ	T3,		;Clear count
	LSHC	T3,^D14		;Put received count in T3
	MOVE	T4,T1		;Copy CBP into T4
	POPJ	P,		;Done
	Subttl	DMRINI -- Routine to Crank up a DMR11

;Here at once-only time and for most fatal errors

DMRINI:	PUSHJ	P,SAVE2##	;Save our working ACs
	AOS	DMRICT(W)	;Count attempted restarts
	MOVE	T1,DMRCSR(W)	;Get Unibus address
	PUSHJ	P,UBGOOD##	;Webe good?
	 POPJ	P,		;No, don't even try
	MOVEI	T1,DMRMRC	;Get Mister Clear
	WRIO	T1,@DMRCSR(W)	;Start over
	MOVEI	T2,4000		;Snooze time
	MOVEI	T1,DMRRUN	;The run bit
DMRILP:	TION	T1,@DMRCSR(W)	;Set yet?
	 SOJG	T2,DMRILP	;No, wait for it (Spec says 6.4 ms)
	MOVE	P2,DMRCSR(W)	;Get CSR address
	RDIOB	T1,DMRBS3(P2)	;Read diagnostic results
	CAIE	T1,DMRMDI	;If inhibited
	 CAIN	T1,DMRMDR	;or ran successfully
	JUMPG	T2,DMRSTR	;Start the DMR
	;The DMR timed out or failed microdiagnostics
	SETZ	T1,		;Clear run, IEI
	WRIO	T1,DMRSL0(P2)	; ...
	WRIOB	T1,DMRBS2(P2)	;and IEO
	POPJ	P,		;Error return

	;The DMR is running and needs BASEIN/CONTROLIN

DMRSTR:	SKIPN	F,DMRTAD(W)	;Get 10 address
	 PUSHJ	P,NTDSTP##	;DMR block was smashed
	DMROFF			;Prevent various races
	HRLZI	T1,(F)		;Clear out comm region
	HRRI	T1,1(F)		;...
	SETZM	(F)		;
	BLT	T1,COMSIZ-1(F)	;So we start fresh
	MOVE	T1,DMRFEK(W)	;Get FEK address
	HRRM	W,FEKUNI(T1)	;Be sure FEK points to DMR
	MOVEI	T1,M.DN60##	;See if IBMcom loaded
	JUMPE	T1,DMRST1	;If not, skip this
	HRRZ	T1,DMRLIN(W)	;Get this DMR number
	MOVE	T1,DMRBAS##(T1)	;Find the DLX block for this line
DMRST1:	MOVEM	T1,DMRDLX(W)	;Point to it
	PUSHJ	P,DEFCIR	;Define a DECnet circuit if necessary
	MOVE	T1,DMRVEC(W)	;Get vector address for this DMR
	LSH	T1,-2		;Make into 10 word offset
	HLRZ	T2,DMRCSR(W)	;Get UBA number
	ADD	T1,.EPVIT##-1(T2) ;Compute address of vector table entry

	MOVSI	T2,(XPCW)	;INTERRUPT INSTRUCTION
	HRRI	T2,DMRIVA(W)	;Vector code
	MOVEM	T2,0(T1)	;'A' Vector instruction
	HRRI	T2,DMRIVB(W)	;Vector code
	MOVEM	T2,1(T1)	;'B' Vector instruction
	MOVSI	T1,DMRCLR(W)	;Clear out DMR dynamic area
	HRRI	T1,DMRCLR+1(W)	;...
	SETZM	-1(T1)		;...
	BLT	T1,DMRLEN-1(W)	;So counters, etc are reset
	MOVEI	T1,DR%WT1	;Get waiting for first message state
	DPB	T1,PDRSTS	;Save
	MOVEI	T2,DMRPPL	;Get pages/line
	MOVE	T3,DMRMAP(W)	;Get address of the map
	MOVEI	T4,(F)		;Virtual address of com region
DMRST2:	MAP	T1,(T4)		;Find physical address of this page
	TLZ	T1,^-17		;Clear nonexistant bits
	LSH	T1,W2PLSH##	;Make page number
	IORI	T1,UNBVBT!UNBD18 ;Valid mapping, 16 bit mode
	WRIO	T1,(T3)		;Make it accessible to the DMR
	ADDI	T4,PAGSIZ##	;Advance to the next page
	AOJ	T3,		;And the next mapping register
	SOJG	T2,DMRST2	;Loop for all pages of com region
	MOVEI	T1,DMRIEI	;Enable input ready interrupts
	BSIOB	T1,DMRBS0(P2)	;From DMR
	MOVEI	T1,DMRIEO	;And output ready
	BSIOB	T1,DMRBS2(P2)	;Prior to BASE IN
	MOVEI	T1,DMRBSI	;BASE IN
	HRRZ	T2,DMREAD(W)	;Get 11 address
IFN EBASAD,	ADDI	T2,EBASAD	;Offset by base table start
	HRLS	T3,T2		;Low in SEL4, high in SEL6
	LSH	T3,-2		;Move bit 17 into bit 15
	HRR	T2,T3		;Copy B14!B15 (other bits not valid)
	AND	T2,[177777,,B14!B15] ;Mask off unused bits
	PUSHJ	P,DMRINP	;Add to queue
	 PUSHJ	P,NTDSTP##	;Can't be full!
	MOVEI	T1,DMRCTI	;CONTROL IN
	SETZ	T2,		;Default is zero
	MOVE	T3,DMRSTS(W)	;Get status bits for this DMR
	TRNE	T3,DMRSLS	;Select long start timer?
	 TRO	T2,DMRILS	;Yes, set in SEL6
	TRNE	T3,DMRSHD	;Select HDX line?
	 TROA	T2,DMRIHD	;Yes, set in SEL6
	TRZ	T2,DMRILS	;Not HDX, don't set long start
	TRNE	T3,DMRSMT	;Select maint mode?
	 JRST	[TRO	T2,DMRIMT ;Yes, tell DMR
		 MOVEI	T3,DR%MAI ;Get maint state
		 DPB	T3,PDRSTS ;and put line into it
		 JRST	.+1]
	PUSHJ	P,DMRINP	;Add to Q
	 PUSHJ	P,NTDSTP##	;Can't be full yet!
INILP2:	MOVE	T1,DMRRBC(W)	;Get current count of RBFs q'd
	CAIL	T1,DR%NBF	;Q'd all of them yet?
	 JRST	INILP3		;Yes, stop
	PUSHJ	P,RCVBUF	;No, queue one more
	JRST	INILP2		;And see if done

INILP3:	DMRON			;Allow interrupts again
	PUSHJ	P,DMRPUP	;Tell user protocol is up (even if it isn't)
			;DMR hides when link goes up, so we lie a lot
	JRST	CPOPJ1##	;Done
	SUBTTL	DEFCIR -- DEFINE A DECNET CIRCUIT FOR A DMR

;This code requires that the call to DMRONC in SYSINI follow
;the call to D36INI at D36STR.  Otherwise core for CB/Buffers unavailable.


DEFCIR:	MOVEI	T1,M.DECN##	;SEE IF DECNET IS LOADED
	JUMPE	T1,CPOPJ##	;IT IS NOT, WE CAN DO NIL
;	HRRZ	T1,DMRUSR(W)	;GET CURRENT LINE USER
;	CAIE	T1,DD.DEC	;IF NOT DECNET (IT ISN'T NECESSARY TO CHECK)
;	 POPJ	P,		;DON'T PESTER ROUTER (WE MIGHT GET IT LATER)
	MOVEI	T1,DC.IOC	;INTERRUPT DRIVER TO CREATE CIRCUIT
	MOVE	T3,W		;GET POINTER TO KONTROLLER LINE BLOCK
	HRRZ	T4,DMRNUM(W)	;GET OUR DMR NUMBER
	MOVSI	T4,<LD.DMR_^D9(1B0)>(T4) ;KONTROLLER TYPE + KON #
	DNCALL	DMIPPI##	;TELL ROUTER ABOUT THIS LINE
	SKIPN	T1		;WHERE WE SUPPLIED A CIRCUIT BY ROUTER?
	 PUSHJ	P,NTDSTP##	;NOPE, WE'LL HAVE TO DIE
	POPJ	P,		;DONE
	SUBTTL	DMRINP -- ROUTINE TO QUEUE TRANSACTIONS FOR THE DMR11

;Called with:
;	W 	    := DMR pointer
;	T1	    := XWD 0,SEL0
;	T2	    := XWD SEL4,SEL6
;Returns
;	CPOPJ	DMR11 not running
;	CPOPJ1	Transaction described by T1 & T2 has been queued.
;		RQI has been set to request an interrupt on vector "A".
;		DMRAIV will then process the top transaction on the queue.
;
DMRINU:	DMROFF			;ENTRY TO TURN INTERRUPTS OFF FIRST
	PUSHJ	P,DMRINP	;QUEUE THE TRANSACTION
	SKIPA			;HANDLE SKIP RETURNS PROPERLY
	AOS	(P)		;GIVE SKIP
DMRONJ::DMRON			;RE-ENABLE INTERRUPTS
	POPJ	P,		;ALL DONE



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

	MOVEI	T3,DMRRQI	;GET RQI AND SET IT IN BSEL0
	BSIO	T3,@DMRCSR(W)	;  THIS WILL CAUSE A VECTOR "A" INTERRUPT
	RETSKP			;GOOD RETURN
	SUBTTL	FREBOI - Free an input buffer

;FREBOI	FREE AN INPUT BUFFER
;  RETURN CANONICAL BYTE POINTER TO MESSAGE IN T4
;  Returns byte count of message in T3

FREBOI:	PUSHJ	P,GETBAD	;GET T4 SET UP FROM P2, P3
	TLNE	T4,3		;BUFFER DESCRIPTOR BETTER START ON EVEN -10 WD
	 POPJ	P,		;  IF NOT EVEN -10 ADDRESS, THEN DMR SCREWED UP
	SKIPGE	T1,0(T4)	;  AND MAKE SURE THAT IT'S CLRd by DMR.
	 PUSHJ	P,NTDSTP##	;WE'VE SCREWED UP THE BDL POINTERS [DBUG]
	RETSKP
	SUBTTL	FREMAI - Free maint messages that have been output

;FREMAI	ROUTINE TO FREE ANY MAINT MESSAGES
;  MAINT MESSAGES MUST BE FREED OUTSIDE THE DMROFF/DMRON INTERLOCK WHICH
;  IS NOT POSSIBLE IN D8RTXC.
;CALL	W := DMR POINTER
;RETURN	CPOPJ

FREMAI:	LDB	T1,PDRSTS	;If we're not in maint mode,
	CAIE	T1,DR%MAI	; ...
	 POPJ	P,		;Don't confuse poor users (esp NETSER...)

FREMA1:	DMROFF			;INTERLOCK WRT D8RTXC
	HRRZ	T3,DMRMAI(W)	;GET THE NEXT STC MSG TO FREE
	JUMPE	T3,FREMA2	;If none, finish up
	HRRZ	T2,MB.NXT(T3)	;GET THE ADDRESS OF THE NEXT message TO FREE
	HRRZM	T2,DMRMAI(W)	;  AND REMEMBER IT FOR NEXT TIME
	SOSGE	DMRMAC(W)	;Count down one fewer message
	 PUSHJ	P,NTDSTP##	;++ Counts are wrong
	DMRON			;GIVE BACK THE INTERLOCK
	MOVEI	T1,DC.IOD	;Say output done
	PUSHJ	P,CALUSR	; to the user
	JRST	FREMA1		;Keep going

;Here with all messages freed

FREMA2:	SKIPE	DMRMAC(W)	;Better be none left
	 PUSHJ	P,NTDSTP##	;++ Queue length didn't match count
	DMRON			;Re-enable interrupts
	POPJ	P,		;Done at last
	SUBTTL	ERROR ROUTINES

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

DMRERR::MOVEM	T1,DMRCPC(W)	;SAVE THE CALLER'S PC
	MOVE	U,OPRLDB##	;WE HAD BETTER TELL SOMEONE ABOUT THIS...
	PUSHJ	P,INLMES##	;SEND THE OPERATOR A MESSAGE
	ASCIZ	/%% Fatal error on /
	PUSHJ	P,PRDMR		;TELL HIM WHICH DMR IT WAS.
	PUSHJ	P,PRSPC##	;Where we died...
	MOVE	T2,DMRCPC(W)	;GET THE CALLER'S PC BACK
	SOS	T2		;  BACK IT UP 1 LOCATION
	PUSHJ	P,PCP##		;  AND PRINT IT AS A "PC"
	PUSHJ	P,INLMES##	;FINISH OFF THE MESSAGE
	ASCIZ	/. Line restarting
/

;	PJRST	DMRKIL		;STOP IT (CLEAR RUN)
;DMRKIL - ROUTINE TO CRASH A DMR11 (DMR) WHEN SOMETHING IS DRASTICALLY
;	WRONG.

DMRKIL:	PUSHJ	P,DMRKL0	;First kill the silly thing
	PUSHJ	P,DMRINI	;Then, restart it
	 JFCL			;Since it will probably recover
	POPJ	P,		;Done

DMRKL0:	PUSHJ	P,SAVE1##	;WE USE 1 P
	MOVEI	T1,DMRRUN	;GET THE RUN BIT
	BCIO	T1,@DMRCSR(W)	;Clear RUN.  Don't master clear (would restart)

	MOVEI	T1,DMRSTV	;Get the starvation bit
	ANDCAM	T1,DMRSTS(W)	;And save a wasted call
	MOVE	T2,DMRCSR(W)	;GET ADDRESS OF THE DMR11 CSR'S
	MOVE	T3,[POINT 18,DMRCRG(W)] ;GET A BYTE POINTER TO "CRASH REGS"
	MOVEI	T1,4		;GET COUNT OF REGISTERS TO READ

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

	MOVE	T1,DMRMAP(W)	;GET THE INITIAL MAP REG FOR THIS DMR
	SETZ	T2,		;GET A ZERO,
	MOVEI	T3,DMRPPL	;Pages/line
	SOJGE	T3,[WRIO T2,(T1);Clear a mapping register
		    AOJA T1,.]	;Loop over all of em

	LDB	T1,PDRSTS	;GET THE STATE OF THIS LINE
	CAIE	T1,DR%HLT	;WAS LINE "ALIVE"
	 PUSHJ	P,DMRPDN	;Tell user protocol down
	MOVEI	T2,DR%HLT	;GET THE "DOWN" STATE
	DPB	T2,PDRSTS	;  AND MARK THIS LINE AS HOPELESS
	POPJ	P,		;Done
	SUBTTL	Protocol Control Subroutines

;DMRPUP - Routine to tell user protocol is up

DMRPUP:	AOS	DMRLUP(W)	;Count line up event
	PUSH	P,T4		;Save T4 for caller
	MOVEI	T1,DC.IPU	;Function for user
	PUSHJ	P,CALUSR	;Bring happiness and good cheer
	POP	P,T4		;Restore T4
	POPJ	P,		;Done

;DMRPDN - Routine to tell user protocol is down.  
;DMRPD0 - Routine to flush the queues, returning output-not-done

DMRPDN:	AOS	DMRLDN(W)	;Count line down event
	PUSHJ	P,DMRPD0	;Flush the queues
	MOVEI	T1,DC.IPD	;Function for user
	PUSHJ	P,CALUSR	;Bring sadness and death
	POPJ	P,		;Done

DMRPD0:	DMROFF			;We're hacking the queues...

DMRPD1:	HRRZ	T3,DMRWTO(W)	;GET THE FIRST PCB ON THE "WAIT OUTPUT" QUEUE
	JUMPE	T3,DMRPD2	;  IF NO MSGS AWAITING OUTPUT, CHECK AWAIT ACK
	HRRZ	T1,MB.NXT(T3)	;GET THE ADDRESS OF THE "SECOND" MESSAGE
	HRRZM	T1,DMRWTO(W)	;  AND MAKE IT BE THE "FIRST"
	JRST	DMRPD3		;RELEASE THE MESSAGE

DMRPD2:	HRRZ	T3,DMRWTA(W)	;GET THE FIRST PCB ON THE "WAIT ACK" QUEUE
	JUMPE	T3,DMRPD4	;  IF NONE, GO TELL USER THAT LINE IS DOWN
	SOS	DMRCTA(W)	;COUNT OFF ONE LESS "AWATING ACK"
	HRRZ	T1,MB.NXT(T3)	;GET THE SECOND PCB ON THE WAIT ACK QUEUE
	HRRZM	T1,DMRWTA(W)	;  AND MAKE IT THE FIRST

DMRPD3:	DMRON			;Turn interrupts back on
	MOVEI	T1,DC.IOF	;Say output not done
	HLLZS	MB.NXT(T3)	;It's not polite to point
	PUSHJ	P,CALUSR	;Tell user bad news
	JRST	DMRPD0		;And keep flushing

DMRPD4:	SKIPE	DMRCTA(W)	;MAKE SURE WE DIDN'T LOSE ANY MSGS
	 PUSHJ	P,NTDSTP##	;++ ERROR:  ACK QUEUE WRONG LENGTH
	DMRON			;Interrupts OK now
	PJRST	FREMAI		;Done, see if MAINT msgs to free
	SUBTTL	GETBUF - Get a buffer for a message, or starve

;By the time we get around to asking for a buffer, the DMR has alread ACKd
;this message.  So we can't really toss it, without damage, especially to
;Phase II DECnet nodes.  We gave the DMR a comm page buffer on blind faith,
;which has been shattered.  So, we mark the DMR as needing attention at 
;once/sec level, and simply starve it.  If things are bad enough, in a 
;little while, it will start NAKing some other message, since it will have
;no buffers at all.  When we can get a buffer at clock level to put the
;current message into, we'll feed the DMR another buffer, and it will
;crank up the line again.   If things aren't bad at all, which is the
;typical ANF-10 case (NETSER just didn't get a chance to give us a buffer)
;we'll just continue happily along, and the other end will never know...

GETBUF:	MOVEI	T1,DMRSTV	;The I'm starving bit
	ANDCAM	T1,DMRSTS(W)	;Not yet, I'm not
	PUSH	P,T3		;Save length
	PUSH	P,T4		;and CBP to data
	MOVEI	T1,DC.IGB	;I need a buffer
	PUSHJ	P,CALUSR	;Please
	POP	P,T4
	POP	P,T3
	JUMPN	T1,CPOPJ##	;Got one, we lucked out
	MOVEI	T2,DMRSTV	;Failed, were starving
	IORM	T2,DMRSTS(W)	;Now
	POPJ	P,		;We'll try again later
	SUBTTL	CALUSR - Routine to dispatch to line's user


;CALUSR - Routine to call this line's user
;Call: W := Pointer to this DMR's DMR block
;      T1 := Interrupt function (KI.???), T3 := function data
;	PUSHJ P,CALUSR
;
;CALUSR sets up T2 with the Line block address (eg FEK, CB, DLX, DDB, ...)
;Users are not allowed to smash F/W!

CALUSR:	PUSHJ	P,SAVR##	;DECnet trashes R, so save it
	HRRZ	T2,DMRUSR(W)	;Get line's user code
	CAILE	T2,DD.MAX	;In range?
	 PUSHJ	P,NTDSTP##	;Die, die, die...!
	LSH	T2,1		;Convert to biword offset
	PJRST	CALDVN(T2)	;Go dispatch

	DEFINE	X(TYP,FT,BLK,DSP),<
IFN	<.-CALDVN-<2*DD.'TYP>>,<PRINTX ? CALUSR vector wrong for TYP>
IFE	FT,<
	PUSHJ	P,NTDSTP##	;;++ User not supported in this monitor!
	HALT	.-1>		;;Fill out the block
IFN	FT,<
	MOVE	T2,DMR'BLK(W)	;;Point to user's block
	PJRST	DSP>		;;And dispatch to user's entry vector
>;Define X

CALDVN:	X(NOBODY,1,USR,NOBDSP##)	;The null user
	X(ANF10,FTNET,FEK,D8RK2U##)	;Real networks
	X(DECNET,FTDECN,LBK,CALPPI)	;Imitation networks
	X(PROGRAM,1,DDP,DMRKTU##)	;UUOCON
	X(IBMCOMM,FTDN60,DLX,D6RK2U##)	;Non-networks
IFN <.-CALDVN-<2*<DD.MAX+1>>>,<PRINTX ? CALDVN user entry missing!>

IFN FTDECN,<
CALPPI:	DNCALL	DMIPPI##	;CALL DECNET IN DECNET CONTEXT
	  POPJ	P,		;RETURN
	PJRST	CPOPJ1##	;...
>; END IFN FTDECN
	SUBTTL	PRINT ROUTINES

;PRDMR	- ROUTINE TO PRINT OUT "DMR11 #?"

PRDMR:	PUSHJ	P,INLMES##	;PRINT OUT THE ASCII PART
	ASCIZ	/DMR11 #/
	HRRZ	T1,DMRNUM(W)	;GET THE DMR'S NUMBER
	PUSHJ	P,PRTDIG##	;  AND PRINT THAT
	PUSHJ	P,INLMES##	;Identify further
	ASCIZ	/(Synch line /
	HLRZ	T1,DMRLIN(W)	;Line number on this node
	PUSHJ	P,PRTDIG##	;Add it
	PUSHJ	P,INLMES##	;Finish up
	ASCIZ	/)/
	POPJ	P,


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

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

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

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

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


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

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

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

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