Google
 

Trailing-Edge - PDP-10 Archives - BB-JR93L-BB_1990 - 10,7/galaxy/nebula/nebula.mac
There are 23 other files named nebula.mac in the archive. Click here to see a list.
TITLE	NEBULA - DECsystem-10 Network Queue Controller
SUBTTL	D. P. Mastrovito & Joseph A. Dziedzic /DPM/JAD


;
;
;	      COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION
;                               1986,1987.
;			ALL RIGHTS RESERVED.
;
;
;     THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED
;     AND COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE
;     AND WITH THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE.   THIS
;     SOFTWARE  OR ANY OTHER COPIES THEREOF MAY NOT BE PROVIDED OR
;     OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON.  NO  TITLE  TO
;     AND OWNERSHIP OF THE SOFTWARE IS HEREBY TRANSFERRED.
;
;     THE INFORMATION  IN  THIS  SOFTWARE  IS  SUBJECT  TO  CHANGE
;     WITHOUT  NOTICE  AND SHOULD NOT BE CONSTRUED AS A COMMITMENT
;     BY DIGITAL EQUIPMENT CORPORATION.
;
;     DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY
;     OF  ITS  SOFTWARE  ON  EQUIPMENT  WHICH  IS  NOT SUPPLIED BY
;     DIGITAL.


	SEARCH	NEBPRM
	MODULE	(NEBULA)
	.REQUE	CHRFRM		;GET CHARACTERISTICS/FORMS FILE HANDLER

	LOC	.JBVER		;VERSION
	EXP	%%.NEB		; NUMBER
	RELOC			;  ...

COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1983,1987. ALL RIGHTS RESERVED.
\;END OF COPYRIGHT MACRO
SUBTTL	Initialization


NEBULA::JFCL			;NO CCL
	RESET			;STOP I/O
	MOVE	P,[IOWD PDLMSZ,PDL] ;SET UP STACK
	MOVEI	S1,IB.SZ	;IB SIZE
	MOVEI	S2,IB		;IB ADDR
	PUSHJ	P,I%INIT##	;FIRE UP GLXLIB
	PUSHJ	P,INITIA	;INITIALIZE
	PUSHJ	P,I%ION		;TURN ON THE PSI SYSTEM
	$LOG	(<^T/G$NAM/ %^V/.JBVER/ starting>,,,<$WTFLG(WT.SJI)>)
	MOVEI	M,HELLO		;POINT TO HELLO HESSAGE
	PUSHJ	P,G$SQSR	;GREET QUASAR
SUBTTL	Main scheduling loop


MAIN:	PUSHJ	P,I%NOW		;GET "NOW"
	MOVEM	S1,G$NOW	;SAVE IT
	PUSHJ	P,IPCF		;PROCESS IPCF MESSAGES
	PUSHJ	P,RESEND	;RESEND ANY THAT DIDN'T MAKE IT
	AOSN	CLKTIC		;TIMER GONE OFF?
	PUSHJ	P,CLKCHK	;COUNTDOWN THE CLOCK REQUEST QUEUE
	AOSN	G$CLNC		;NEED TO GARBAGE COLLECT PAGES?
	PUSHJ	P,M%CLNC##	;YES, SHRINK DOWN IF WE CAN
	MOVSI	P1,-JOBN	;AOBJN POINTER
	SETZM	RUNCNT		;CLEAR COUNT OF JOBS RUN ON THIS PASS

MAIN1:	SKIPN	R,G$ADR(P1)	;HAVE A STREAM?
	JRST	RSCHED		;NO
	MOVEM	P1,CURJOB	;SAVE CURRENT STREAM
	MOVE	S1,.JBFLG(R)	;GET FLAGS
	TXNE	S1,JB.KIL	;KILL JOB?
	JRST	KILJOB		;YES
	TXNE	S1,JB.ABO	;ABORT JOB?
	JRST	ABOJOB		;YES
	TXNE	S1,JB.REQ	;REQEUE PENDING?
	JRST	REQJOB		;YES
	TXNE	S1,JB.PAU	;WANT TO PAUSE STREAM?
	PUSHJ	P,PAUJOB	;YES--TRY TO DO IT NOW
	.CREF	.WSRUN		;SHOW WHAT WE'RE TESTING
	SKIPE	.JBWSC(R)	;AND IS IT RUNNABLE?
	JRST	RSCHED		;NO
	HRLZI	0,.JBACS+1(R)	;SET UP BLT
	HRRI	0,1		;START WITH AC 1
	BLT	0,17		;LOAD ACS 1 THROUGH 17
	MOVE	0,.JBACS(R)	;LOAD AC 0
	AOS	RUNCNT		;REMEMBER WE RAN A JOB
	POPJ	P,		;RETURN TO INTERRUPTED PROCESS
SUBTTL	Scheduler -- Rescheduling and job context switching


SSCHED::PUSH	P,S1		;SAVE S1
	PUSHJ	P,I%IOFF	;TURN OFF PSI
	MOVE	S1,@-1(P)	;GET WORD FOLLOWING CALL
	TRC	S1,-1		;IF TIME IS -1
	TRCN	S1,-1		; THEN IGNORE IT
	MOVEM	S1,.JBTIM(R)	;SET TIMER
	HLRZS	S1		;ISOLATE WAIT STATE IN RH
	ANDI	S1,777		;STRIP OFF JUNK
	CAIE	S1,777		;IGNORE?
	MOVEM	S1,.JBWSC(R)	;SET NEW CODE
	PUSHJ	P,I%ION		;TURN ON PSI
	POP	P,S1		;RESTORE S1
	POPJ	P,		;RETURN

WSCHED::MOVEM	0,.JBACS(R)	;SAVE AC 0
	HRLZI	0,1		;START WITH AC 1
	HRRI	0,.JBACS+1(R)	;SET UP BLT
	BLT	0,.JBACS+17(R)	;SAVE THE ACS
	PUSHJ	P,UPDATE	;UPDATE STREAM STATUS IF REQUIRED
	SETZM	.JBFCT(R)	;RESET FAIRNESS COUNTER

XSCHED:	MOVE	P,[IOWD PDLMSZ,PDL] ;RESET PDL
	MOVE	P1,CURJOB	;GET CURRENT (NOW LAST) JOB POINTER

RSCHED:	AOBJN	P1,MAIN1	;LOOP IF MORE TO CHECK
	MOVEM	P1,CURJOB	;UPDATE
	MOVEI	S1,ZZTIME	;SNOOZE TIME
	SKIPN	RUNCNT		;RUN ANY JOB LAST TIME?
	PUSHJ	P,I%SLP		;ZZZZZZ
	JRST	MAIN		;LOOP BACK
SUBTTL	Scheduler -- Clock queue/Timer control


;TIMER INTERRUPT

TIMINT:	$BGINT	(1)		;SWITCH TO INTERRUPT CONTEXT
	SETOM	CLKTIC		;SET FLAG
	$DEBRK			;DISMISS INTERRUPT


;CLEAR A TIMER REQUEST FOR A STREAM
;CALL:	PUSHJ	P,TIMCLR

TIMCLR::MOVEI	S1,1		;TIME IS ONE SECOND
	SETZ	S2,		;NO SUBROUTINE


;SET A TIMER REQUEST TO RUN A SUBROUTINE IN THE JOB'S CONTEXT
;CALL:	MOVE	S1, TIME IN SECONDS
;	MOVE	S2, SUBROUTINE
;	PUSHJ	P,TIMJOB

TIMJOB::MOVEM	S1,.JBTIM(R)	;STORE TIME IN SECONDS
	MOVEM	S2,.JBTQS(R)	;STORE TIMER QUEUED SUBROUTINE ADDRESS
	MOVX	S1,JB.SPR	;SCHEDULER BIT
	ANDCAM	S1,.JBFLG(R)	;CLEAR IT
	JRST	TIMCHK		;SEE IF WE NEED TO RESET TIMER


;SET A STREAM TIMER TO RUN A SUBROUTINE IN THE SCHEDULER'S CONTEXT
;CALL:	MOVE	S1, TIME IN SECONDS
;	MOVE	S2, SUBROUTINE
;	PUSHJ	P,TIMSCD

TIMSCD::MOVEM	S1,.JBTIM(R)	;STORE TIME IN SECONDS
	MOVEM	S2,.JBTQS(R)	;STORE TIMER QUEUED SUBROUTINE ADDRESS
	MOVX	S1,JB.SPR	;SCHEDULER BIT
	IORM	S1,.JBFLG(R)	;SET IT


TIMCHK:	MOVE	S1,.JBTIM(R)	;GET TIME BACK
	CAMG	S1,CLKTIM	;NEED TO RESET TIMER?
	PUSHJ	P,CLKCHK	;YES
	POPJ	P,		;RETURN
;CHECK ALL STREAMS FOR EXPIRED TIMERS

CLKCHK:	PUSHJ	P,.SAVE2	;SAVE P1 AND P2
	PUSHJ	P,I%NOW		;GET CURRENT TIME
	MOVE	S2,S1		;COPY
	EXCH	S2,CLKTIM	;SWAP WITH TIME ON LAST SCAN
	SUB	S1,S2		;COMPUTE DIFFERENCE
	MULI	S1,250600	;MULTIPLY BY SECONDS PER DAY
	ASHC	S1,17		;POSITION RESULT IN SECONDS
	MOVSI	P1,-JOBN	;AOBJN POINTER
	MOVE	P2,S1		;COPY TIME

CLKCH1:	SKIPE	R,G$ADR(P1)	;HAVE A STREAM?
	SKIPE	.JBTIM(R)	;AND IS A TIMER RUNNING?
	JRST	CLKCH4		;NO
	MOVN	S1,P2		;GET TIME
	ADDB	S1,.JBTIM(R)	;COUNT IT DOWN
	JUMPG	S1,CLKCH4	;JUMP IF NOT TIMED OUT YET
	MOVX	S1,JB.SPR	;BIT TO TEST
	TDNE	S1,.JBFLG(R)	;SCHEDULER PROCESSED TIMER REQUEST?
	JRST	CLKCH2		;YES
	MOVE	S1,.JBACS+P(R)	;GET STREAM STACK
	SKIPE	.JBTQS(R)	;HAVE A TIMER QUEUED SUBROUTINE?
	PUSH	S1,.JBTQS(R)	;PUT IT ON THE STACK
	MOVEM	S1,.JBACS+P(R)	;UPDATE
	JRST	CLKCH3		;ONWARD

CLKCH2:	ANDCAM	S1,.JBFLG(R)	;CLEAR SCHEDULER BIT
	PUSHJ	P,@.JBTQS(R)	;CALL SUBROUTINE

CLKCH3:	SETZM	.JBTIM(R)	;CLEAR TIMER (MIGHT HAVE GONE NEGATIVE)
	SETZM	.JBTQS(R)	;CLEAR OUT TIMER QUEUED SUBROUTINE
	SETZ	S1,		;CLEAR TIME REMAINING

CLKCH4:	JUMPE	S1,CLKCH5	;IGNORE ZERO
	CAMGE	S1,CLKNEW	;SMALLER INTERVAL?
	MOVEM	S1,CLKNEW	;YES

CLKCH5:	AOBJN	P1,CLKCH1	;LOOP FOR ALL STREAMS
	MOVE	S1,CLKNEW	;GET NEW TIME
	SETOM	CLKTIC		;RESET FLAG
	PITMR.	S1,		;ENABLE TIMER INTERRUPT
	  JFCL			;???
	POPJ	P,
SUBTTL	Scheduler -- Update job status


UPDATE:	PUSHJ	P,I%NOW		;GET "NOW"
	SUB	S1,.JQLUT(R)	;GET TIME SINCE LAST UPDATE
	CAXGE	S1,UPDTIM	;TIME FOR AN UPDATE?
	POPJ	P,		;NO, RETURN
FSTATU:	MOVE	S1,.JQOBJ+OBJ.UN(R) ;GET STREAM NUMBER
	CAIL	S1,PRCN		;PROCESSING STREAM?
	POPJ	P,		;NO, NO UPDATE REQUIRED
	MOVX	S1,STU.MX	;SIZE OF MESSAGE
	MOVEI	S2,G$MSG	;SCRATCH SPACE
	PUSHJ	P,.ZCHNK	;ZERO IT
	MOVEI	M,G$MSG		;POINT AT THE MESSAGE
	MOVX	S1,STU.MX	;SIZE OF MESSAGE
	STORE	S1,.MSTYP(M),MS.CNT ;STUFF IT
	MOVX	S1,.QOSTU	;MESSAGE TYPE
	STORE	S1,.MSTYP(M),MS.TYP ;STUFF IT
	MOVSI	S1,.JQOBJ(R)	;OBJECT BLOCK ADDRESS
	HRRI	S1,STU.RB(M)	;WHERE IT GOES
	BLT	S1,STU.RB+OBJ.SZ-1(M) ;COPY IT
	MOVX	S1,%ACTIV	;ASSUME ACTIVE
	MOVE	S2,.JBWSC(R)	;GET STREAM WAIT STATE
	CAXN	S2,.WSIDL	;IDLE?
	MOVX	S1,%IDLE	;YES
	CAXE	S2,.WSWCC	;WAITING FOR A CONNECTION?
	CAXN	S2,.WSWRC	;...
	MOVX	S1,%CNECT	;YES
	CAIN	S2,.WSPAU	;STREAM PAUSED?
	MOVX	S1,%STOPD	;GET STOPPED CODE
	MOVE	S2,.JBFLG(R)	;GET STREAM FLAGS
	TXNE	S2,JB.REQ	;REQUEUE IN PROGRESS?
	MOVX	S1,%REQUE	;YES
	TXNE	S2,JB.ABO	;ABORT IN PROGRESS?
	MOVX	S1,%CNCLG	;YES
	MOVEM	S1,STU.CD(M)	;STORE STATE CODE
	MOVE	S1,.JBNOD(R)	;GET NODE NAME
	MOVEM	S1,STU.PR+.ONNOD(M) ;STORE IT
	PUSHJ	P,I%NOW		;GET NOW
	MOVEM	S1,.JQLUT(R)	;SAVE AS LAST UPDATE TIME
	SUB	S1,.JQTRS(R)	;SUBTRACT TIME STARTED
	IMULI	S1,^D60*^D60*^D24 ;CONVERT UDT FRACTION TO SECONDS
	HLRZM	S1,STU.PR+.ONCON(M) ;STORE CONNECT TIME (SECONDS)
	MOVE	S1,.JNIOV+.IONTY(R) ;GET NETWORK TYPE
	MOVEM	S1,STU.PR+.ONLNK(M) ;STORE IT
	MOVE	S1,.JQBYT(R)	;GET NUMBER OF BYTES TRANSFERRED
	MOVEM	S1,STU.PR+.ONBYT(M) ;STORE IT
	MOVEI	T1,[ITEXT (<, transferring file ^D/S1/ of ^D/S2/>)]
	SKIPN	S1,.JQRFN(R)	;GET THE RELATIVE FILE NUMBER
	MOVEI	T1,[ITEXT ()]	;NOTHING MORE TO SAY HERE
	LOAD	S2,.EQSPC(R),EQ.NUM ;GET NUMBER OF FILES IN REQUEST
	$TEXT	(<-1,,STU.ST(M)>,<Started at ^C/.JQTRS(R)/^I/0(T1)/^0>)
;	$TEXT	(<-1,,STU.ST(M)>,<Transferring file ^F/.JQCFD(R)/>)
	PJRST	G$SQSR		;SEND TO QUASAR AND RETURN
SUBTTL	Scheduler -- KILJOB - Kill a job


KILJOB:	SKIPE	.JNIOV+.IOABO(R) ;WE MAY NOT HAVE A DRIVER, SO CHECK FIRST
	PUSHJ	P,@.JNIOV+.IOABO(R) ;MAKE SURE LINK IS CLOSED (ABORTED)
	  JFCL			;IGNORE ERRORS
	HRRZ	S1,CURJOB	;JOB NUMBER
	MOVE	P,[IOWD PDLMSZ,PDL] ;RESET PDL
	PUSHJ	P,ZAPJOB	;DELETE DATA BASE
	JRST	XSCHED		;JUMP BACK INTO SCHEDULER LOOP
SUBTTL	Scheduler -- ABOJOB - Abort active job


ABOJOB:	.CREF	.WSRUN		;STATE CODE
	SETZM	.JBWSC(R)	;MAKE STREAM RUNNABLE
	PUSHJ	P,FSTATU	;UPDATE QUASAR
	SKIPE	.JNIOV+.IOABO(R) ;WE MAY NOT HAVE A DRIVER, SO CHECK FIRST
	PUSHJ	P,@.JNIOV+.IOABO(R) ;MAKE SURE LINK IS CLOSED (ABORTED)
	  JFCL			;IGNORE ERRORS
	$WTOJ	(<Job Aborted>,<^R/.JQEQP+.EQJBB(R)/>,.JQOBJ(R))
	PUSHJ	P,RLSMSG	;SEND RELEASE MESSAGE
	MOVX	S1,JB.ABO!JB.KIL ;CLEAR ABORT PENDING (DON'T KILL STREAM)
	ANDCAM	S1,.JBFLG(R)	; SO CLEAR THE FLAG
	PJRST	IDLJOB		;IDLE THE STREAM
SUBTTL	Scheduler -- IDLJOB - Make a job idle


IDLJOB:	SKIPE	.JNIOV+.IOABO(R) ;WE MAY NOT HAVE A DRIVER, SO CHECK FIRST
	PUSHJ	P,@.JNIOV+.IOABO(R) ;MAKE SURE LINK IS CLOSED (ABORTED)
	  JFCL			;IGNORE ERRORS
	MOVX	S1,.WSIDL	;GET THE IDLE WAIT STATE
	MOVEM	S1,.JBWSC(R)	;STUFF IT
	MOVX	S1,-1-JB.INP	;GET MASK
	ANDCAM	S1,.JBFLG(R)	;CLEAR OUT JUNK
	JRST	XSCHED		;JUMP BACK INTO SCHEDULER LOOP
SUBTTL	Scheduler -- PAUJOB - Pause job


PAUJOB:	SKIPE	.JBWSC(R)	;JOB RUNNABLE NOW?
	POPJ	P,		;NOT YET
	MOVX	S1,.WSPAU	;GET STATE CODE
	MOVEM	S1,.JBWSC(R)	;STUFF IT
	PUSHJ	P,FSTATU	;UPDATE QUASAR
	$WTOJ	(<Stopped>,<^R/.JQEQP+.EQJBB(R)/>,.JQOBJ(R))
	MOVX	S1,JB.PAU	;GET THE MAGIC BIT
	ANDCAM	S1,.JBFLG(R)	;CLEAR STOP PENDING
	POPJ	P,		;RETURN
SUBTTL	Scheduler -- REQJOB - Requeue active job


REQJOB:	.CREF	.WSRUN		;STATE CODE
	SETZM	.JBWSC(R)	;MAKE STREAM RUNNABLE
	PUSHJ	P,FSTATU	;UPDATE QUASAR
	SKIPE	.JNIOV+.IOABO(R) ;WE MAY NOT HAVE A DRIVER, SO CHECK FIRST
	PUSHJ	P,@.JNIOV+.IOABO(R) ;MAKE SURE LINK IS CLOSED (ABORTED)
	  JFCL			;IGNORE ERRORS
	$WTOJ	(<Job Requeued>,<^R/.JQEQP+.EQJBB(R)/>,.JQOBJ(R))
	MOVX	S1,JB.REQ!JB.KIL ;CLEAR REQUEUE PENDING (DON'T KILL STREAM)
	ANDCAM	S1,.JBFLG(R)	; SO CLEAR THE FLAG
	MOVX	S1,JB.HLD	;WANT TO HOLD THE JOB
	IORM	S1,.JBFLG(R)	;SAVE FOR REQUEUE
	PUSHJ	P,REQMSG	;SEND REQUEUE MESSAGE
	PJRST	IDLJOB		;IDLE THE STREAM
SUBTTL	Scheduler -- ZAPJOB - Delete job data base


ZAPJOB:	PUSH	P,S1		;SAVE STREAM INDEX
	SKIPE	S1,.JBPSI(R)	;GET PSI VECTOR ADDRESS
	SETZM	(S1)		;RECYCLE BLOCK
	MOVE	S2,R		;COPY STREAM RELOCATION
	ADR2PG	S2		;CONVERT TO PAGE NUMBER
	POP	P,S1		;STREAM INDEX
	SETZB	R,G$ADR(S1)	;ERASE ALL MEMORY OF IT
	MOVEI	S1,.JPAGS	;PAGES IN DATA BASE
	PUSHJ	P,M%RLNP	;DEALLOCATE CORE
	SETOM	G$CLNC		;GARBAGE COLLECT NEXT PASS THROUGH MAIN
	POPJ	P,		;RETURN
SUBTTL	Scheduler -- TIMOUT - Link timeout


TIMOUT::PUSHJ	P,JOBIDN	;GENERATE IDENTIFYING TEXT
	$WTO	(<Link timeout error>,<^T/.JBIDN(R)/>,.JQOBJ(R))
	PUSHJ	P,ABORTJ	;QUEUE UP TO ABORT JOB
	HALT	.
SUBTTL	Scheduler -- Generate connect and disconnect messages


;GENERATE CONNECT MESSAGE
CONMSG:	MOVEI	T1,@.JNIOV+.IONAM(R) ;POINT TO NAME STRING
	MOVE	T2,.JBNOD(R)	;NODE NAME
	MOVEI	T3,[ITEXT ()]	;NULL ITEXT
IFN OLDDQS,<
	MOVX	S1,JB.OLD	;
	TDNE	S1,.JBFLG(R)	;TALKING TO THE OLD VERSION?
	MOVEI	T3,[ITEXT (< (old Distributed Job Manager)>)]
>
	$WTO	(<Connect>,<^T/(T1)/ link to ^N/T2/ established^I/(T3)/>,.JQOBJ(R))
	POPJ	P,		;RETURN


;GENERATE DISCONNECT MESSAGE
DISMSG:	MOVEI	T1,@.JNIOV+.IONAM(R) ;POINT TO NAME STRING
	MOVE	T2,.JBNOD(R)	;NODE NAME
	$WTO	(<Disconnect>,<^T/(T1)/ link to ^N/T2/ terminated>,.JQOBJ(R))
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- IPCF interrupt processing


IPCINT:	$BGINT	(1)		;SWITCH TO INTERRUPT CONTEXT
	PUSHJ	P,C%INTR	;TELL LIBRARY WE HAVE A MESSAGE
	$DEBRK			;DISMISS INTERRUPT
SUBTTL	IPCF/Operator/QUASAR interface -- IPCF message processing


IPCF:	PUSHJ	P,C%RECV	;TRY TO RECEIVE A MESSAGE
	JUMPF	.POPJ		;NONE THERE--RETURN
	PUSHJ	P,IPCSET	;SET UP ALL SORTS OF VARIABLES
	  JRST	IPCF.X		;ERROR OF SOME SORT
	LOAD	S1,.MSTYP(M),MS.TYP ;GET MESSAGE TYPE
	PUSH	P,S1		;SAVE IT
	MOVE	S1,[-NUMMSG,,MSGTAB] ;GET POINTER TO MESSAGE TABLE

IPCF.1:	HLRZ	S2,(S1)		;GET TYPE FROM TABLE
	CAME	S2,(P)		;A MATCH?
	AOBJN	S1,IPCF.1	;KEEP SEARCHING
	SKIPL	S1		;POINTER POSITIVE IF NO MATCH
	MOVEI	S1,MSGTAB	;UNKNOWN MESSAGE TYPE
	POP	P,(P)		;TRIM STACK
	HRRZ	S1,(S1)		;GET PROCESSOR ADDRESS
	PUSHJ	P,(S1)		;DISPATCH

IPCF.X:	PUSHJ	P,C%REL		;RELEASE MESSAGE
	JRST	IPCF		;TRY FOR ANOTHER PACKET


; Message dispatch table
MSGTAB:	XWD	000000,UNKMSG	;?????? UNKNOWN MESSAGES
	XWD	MT.TXT,ACK	;ACKS

	XWD	.OMPAU,PAUSE	;ORION PAUSE
	XWD	.OMCON,CONTIN	;ORION CONTINUE
	XWD	.OMREQ,REQUE	;ORION REQUEUE
	XWD	.OMCAN,CANCEL	;ORION CANCEL
	XWD	.OMSHQ,SHOWQ	;ORION SHOW QUEUES

	XWD	.QONEX,NXTJOB	;QUASAR CREATE REMOTE QUEUE ENTRY
	XWD	.QOABO,ABORT	;QUASAR ABORT JOB
	XWD	.QOLIS,LIST	;QUASAR LIST REMOTE
	XWD	.QOSUP,SETUP	;QUASAR SETUP
NUMMSG==.-MSGTAB		;LENGTH OF TABLE
;Routine to set up for IPCF message processing

IPCSET:	SETZM	G$ACK		;ASSUME NO ACK WANTED
	MOVE	S2,MDB.SP(S1)	;GET THE SENDERS PID
	MOVEM	S2,G$SND	;AND SAVE IT
	MOVE	S2,MDB.SD(S1)	;GET THE SENDERS ID
	MOVEM	S2,G$SID	;AND SAVE IT
	MOVE	S2,MDB.PV(S1)	;GET SENDERS CAPABILITIES
	MOVEM	S2,G$PRV	;SAVE THAT AS WELL
	MOVE	S2,MDB.SI(S1)	;GET THE SENDERS SPECIAL PID INDEX
	MOVEM	S2,G$IDX	;STORE IT
	MOVE	S2,MDB.FG(S1)	;GET FLAG WORD
	MOVEM	S2,G$FLG	;SAVE
	LOAD	M,MDB.MS(S1),MD.ADR ;POINT M AT INCOMMING PACKET
	MOVE	S1,.MSCOD(M)	;GET THE MESSAGE ACK CODE
	MOVEM	S1,G$COD	;AND SAVE IT
	MOVEI	S1,.OHDRS+ARG.HD(M);POINT TO FIRST BLOCK IN MESSAGE
	MOVEM	S1,MSGBLK	;SAVE
	MOVE	S1,.OARGC(M)	;GET ARGUMENT BLOCK COUNT
	MOVEM	S1,MSGCNT	;SAVE
	SETZM	G$ACK		;ASSUME NO ACK WANTED
	MOVX	S1,MF.ACK	;GET ACK BIT
	TDNE	S1,.MSFLG(M)	;IS IT SET?
	SETOM	G$ACK		;SENDER WANTS AN ACK
	LOAD	S1,G$IDX,SI.IDX	;GET THE SENDERS SPECIAL PID INDEX
	CAIE	S1,SP.QSR	;QUASAR?
	CAIN	S1,SP.OPR	;ORION?
	AOS	(P)		;YES
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- Process /REASON block


REASON:	PUSHJ	P,.SAVE3	;SAVE SOME ACS
	MOVEI	P1,<STSSIZ*5>-1	;GET MAX BYTE COUNT
	MOVEI	P2,.JBRSN(R)	;POINT TO REASON STORAGE
	HRLI	P2,(POINT 7,)	;MAKE A BYTE POINTER
	MOVE	P3,T3		;COPY BLOCK ADDRESS
	HRLI	P3,(POINT 7,)	;MAKE A BYTE POINTER

REAS.1:	ILDB	S1,P3		;GET A CHARACTER
	JUMPE	S1,REAS.2	;DONE?
	CAIE	S1,.CHCRT	;CR?
	CAIN	S1,.CHLFD	;LF?
	JRST	REAS.2		;ONLY PROCESS ONE LINE
	IDPB	S1,P2		;PUT A CHARACTER
	SOJG	P1,REAS.1	;LOOP BACK FOR MORE

REAS.2:	SETZ	S1,		;GET A NUL
	IDPB	S1,P2		;TERMINATE STRING
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- Resend messages


RESEND:	PUSHJ	P,.SAVE2	;SAVE P1 AND P2
	SETZB	P1,P2		;CLEAR PREVIOUS PID AND INDEX
	MOVE	S1,IPCQUE	;GET LINKED LIST FOR RESENDS
	PUSHJ	P,L%FIRS	;POSITION TO FIRST ENTRY
	JRST	RESE.2		;ENTER LOOP

RESE.1:	MOVE	S1,IPCQUE	;GET LINKED LIST FOR RESENDS
	PUSHJ	P,L%NEXT	;POSITION TO NEXT ENTRY

RESE.2:	JUMPF	.POPJ		;RETURN IF END OF LIST
	MOVSI	S1,(S2)		;POINT TO SAVED SAB
	HRRI	S1,G$SAB	;AND WORKING COPY
	BLT	S1,G$SAB+SAB.SZ-1 ;RETRIEVE FROM LIST
	MOVE	M,G$SAB+SAB.MS	;POINT TO THE MESSAGE
	MOVE	S1,G$SAB+SAB.LN	;GET MESSAGE LENGTH
	TRNN	M,PAGSIZ-1	;ON A PAGE BOUNDRY?
	CAIE	S1,PAGSIZ	;AND A PAGE IN LENGTH?
	MOVEI	M,SAB.SZ(S2)	;NO--POINT TO SAVED MESSAGE
	MOVEM	M,G$SAB+SAB.MS	;UPDATE
	CAMN	P1,G$SAB+SAB.PD	;THIS PID SAME AS LAST ONE?
	CAME	P2,G$SAB+SAB.SI	;INDEX THE SAME TOO?
	JRST	RESE.1		;YES--IGNORE SINCE LAST SEND FAILED
	MOVEI	S1,SAB.SZ	;SAB LENGTH
	MOVEI	S2,G$SAB	;SAB ADDRESS
	PUSHJ	P,C%SEND	;SEND MESSAGE
	JUMPT	RESE.3		;DELETE FROM QUEUE IF SUCESSFUL
	CAIE	S1,ERNSP$	;NO SUCH PID?
	CAIN	S1,ERPWA$	;PID WENT AWAY?
	JRST	RESE.3		;JUST REMOVE FROM QUEUE
	MOVE	P1,G$SAB+SAB.PD	;COPY PID
	MOVE	P2,G$SAB+SAB.SI	;AND INDEX
	JRST	RESE.1		;TRY ANOTHER TO RESEND TO ANOTHER PID

RESE.3:	SETZB	P1,P2		;CLEAR PREVIOUS PID AND INDEX
	MOVE	S1,IPCQUE	;ELSE MUST DELETE
	PUSHJ	P,L%DENT	;THE QUEUE ENTRY
	SOSE	RSENDC		;COUNT DOWN
	JRST	RESE.1		;GO TRY ANOTHER RESEND
	POPJ	P,		;QUEUE IS EMPTY
SUBTTL	IPCF/Operator/QUASAR interface -- Validate object block


; Validate object block in messages
; Call:	PUSHJ	P,VALOBJ
;	<NON-SKIP>		;ILLEGAL MESSAGE
;	<SKIP>			;MESSAGE OK
;
; On sucessful return, R:= stream relocation
;
VALOBJ:	PUSHJ	P,.SAVE1	;SAVE P1

VALO.1:	PUSHJ	P,G$BLK		;GET A MESSAGE BLOCK
	  JRST	VALO.E		;NO MORE - ERROR
	CAIE	T1,.OROBJ	;IS THIS THE OBJECT BLOCK???
	JRST	VALO.1		;NO - GET THE NEXT MSG BLOCK
	MOVE	S2,T3		;GET THE BLOCK DATA ADDRESS IN S1.
	MOVE	T1,.ROBTY(S2)	;GET OBJECT TYPE
	MOVE	T2,.ROBAT(S2)	;GET UNIT NUMBER
	MOVE	T3,.ROBND(S2)	;AND NODE NUMBER
	MOVSI	P1,-JOBN	;AOBJN POINTER

VALO.2:	SKIPN	R,G$ADR(P1)	;GET STREAM RELOCATION
	JRST	VALO.3		;NOT SETUP
	CAMN	T1,.JQOBJ+.ROBTY(R) ;COMPARE OBJECT TYPE
	CAME	T2,.JQOBJ+.ROBAT(R) ;COMPARE UNIT NUMBER
	JRST	VALO.3		;NO MATCH
	CAMN	T3,.JQOBJ+.ROBND(R) ;COMPARE NODE NUMBER
	JRST	.POPJ1		;MESSAGE SEEMS OK - RETURN

VALO.3:	AOBJN	P1,VALO.2	;NO--LOOP

VALO.E:	$WTO	(<NEBULA Error>,<Invalid object block: type = ^O/T1/, unit/stream = ^O/T2/, node = ^N/T3/>)
	POPJ	P,		;TAKE ERROR RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- Message block processing


; Get the next block of a message
; Call:	PUSHJ	P,G$BLK
;	<NON-SKIP>		;END OF MESSAGE
;	<SKIP>			;NEXT BLOCK FOUND
;
; On error return, T1, T2 and T3 left unchanged
; On sucessful return, T1= type, T2= length, T3= data address
;
; AC usage:	Destroys S1
;
G$BLK::	SOSGE	MSGCNT		;SUBTRACT 1 FROM THE BLOCK COUNT
	POPJ	P,		;ERROR RETURN IF NO MORE
	MOVE	S1,MSGBLK	;GET THE PREVIOUS BLOCK ADDRESS
	LOAD	T1,ARG.HD(S1),AR.TYP ;GET THE BLOCK TYPE
	LOAD	T2,ARG.HD(S1),AR.LEN ;GET THE BLOCK LENGTH
	MOVEI	T3,ARG.DA(S1)	;GET THE BLOCK DATA ADDRESS
	ADD	S1,T2		;POINT TO THE NEXT MESSAGE BLOCK
	MOVEM	S1,MSGBLK	;SAVE IT FOR THE NEXT CALL
	JRST	.POPJ1		;RETURN SUCESSFUL
SUBTTL	IPCF/Operator/QUASAR interface -- Unknown message


UNKMSG:	$WTO	(<^T/G$NAM/ Error>,<^I/UNKTXT/>)
	POPJ	P,		;RETURN


UNKTXT:	ITEXT	(<                  Unknown IPCF message
Sender: ^O12R0/G$SND/, ^U/G$SID/
Header: ^O12R0/.MSTYP(M)/, ^O12R0/.MSFLG(M)/, ^O12R0/.MSCOD(M)/>)
SUBTTL	IPCF/Operator/QUASAR interface -- ACK message #700000


ACK:	SKIPE	.OARGC(M)	;QUASAR SNIFFING AROUND?
	$WTO	(<^T/G$NAM/ error>,<^I/ACKTXT/>,,<$WTFLG(WT.SJI!WT.NFO)>)
	POPJ	P,		;RETURN


ACKTXT:	ITEXT	(<                  Unexpected ACK
Sender: ^O12R0/G$SND/, ^U/G$SID/
Header: ^O12R0/.MSTYP(M)/, ^O12R0/.MSFLG(M)/, ^O12R0/.MSCOD(M)/
^T/.OHDRS+ARG.DA(M)/>)
SUBTTL	IPCF/Operator/QUASAR interface -- QUASAR message #5 (NEXTJOB)


NXTJOB:	PUSHJ	P,FNDPRC	;FIND AN IDLE PROCESSING STREAM
	  JRST	NXTJ.1		;NONE?  QUASAR BLEW IT
	PUSHJ	P,COPMSG	;COPY THE REQUEST TO THE STREAM DATABASE
	MOVE	S1,.EQROB+.ROBND(R) ;GET DESTINATION NODE
	MOVEM	S1,.JBNOD(R)	;STORE FOR ASSORTED TEXT MESSAGES
	PUSHJ	P,SELDRV	;SELECT A NETWORK DRIVER
	  JRST	NXTJ.2		;NONE AVAILABLE, REQUEUE THE REQUEST
	MOVE	S1,.JBACS+P(R)	;GET STREAM STACK
	PUSH	S1,[IDLJOB]	;WHERE TO END UP
	PUSH	S1,[RCREAT]	;WHERE TO START JOB
	MOVEM	S1,.JBACS+P(R)	;UPDATE
	MOVE	S1,G$NOW	;GET "NOW"
	MOVEM	S1,.JQTRS(R)	;SAVE TIME REQUEST STARTED PROCESSING
	SETZM	.JQLUT(R)	;NO UPDATE SENT YET
	SETZM	.JQRFN(R)	;INITIALIZE RELATIVE FILE NUMBER TO ZERO
	SETZM	.JQCFD(R)	;NO CURRENT FILE
	SETZM	.JQBYT(R)	;NOTHING SENT YET
	MOVSI	S1,.JBRSN(R)	;POINT TO START OF REASON TEXT
	HRRI	S1,.JBRSN+1(R)	;MAKE A BLT POINTER
	SETZM	.JBRSN(R)	;CLEAR FIRST WORD
	BLT	S1,.JBRSN+STSSIZ-1(R) ;CLEAR STORAGE
	PUSHJ	P,INIOBF	;SET UP OUTPUT BUFFERS
	.CREF	.WSRUN		;SHOW NEW STATE
	SETZM	.JBWSC(R)	;MAKE THE STREAM RUNNABLE
	POPJ	P,		;RETURN

NXTJ.1:;STOPCD	(NFS,HALT,,<No free processing streams>) ;debug
	$WTO	(<^T/G$NAM/ error>,<No free processing streams>,,<$WTFLG(WT.SJI)>)
	PJRST	REQMSG		;BE NICE AND REQUEUE THE JOB

NXTJ.2:	$WTO	(<Node ^N/.EQROB+.ROBND(M)/ not accessible>,,.JQOBJ(R),<$WTFLG(WT.SJI)>)
	MOVX	S1,%RSUNA	;GET "NOT AVAILABLE" RESPONSE CODE
	SETZ	S2,		;NO ATTRIBUTES
	PJRST	RSETUP		;SEND RESPONSE TO SETUP MSG TO QUASAR
SUBTTL	IPCF/Operator/QUASAR interface -- QUASAR message #6 (ABORT)



ABORT:	MOVE	S1,ABO.IT(M)	;GET INTERNAL TASK NAME
	PUSHJ	P,FNDITN	;FIND THE OBJECT
	  POPJ	P,		;DUH?
	MOVX	S1,JB.ABO	;INDICATE ABORT IN PROGRESS
	IORM	S1,.JBFLG(R)	;LITE FOR SCHEDULER
	$WTOJ	(<Cancel request queued by user ^P/ABO.ID(M)/>,<^R/.JQEQP+.EQJBB(R)/>,.JQOBJ(R))
	$TEXT	(LOGCHR,<^I/NBABO/Job aborted by user ^P/ABO.ID(M)/>)
	$TEXT	(<-1,,.JBRSN(R)>,<Job cancelled by user ^P/ABO.ID(M)/^0>)
	POPJ	P,		;RETURN


;Here from when network driver decides to abort the job
ABORTJ::MOVX	S1,JB.ABO	;GET ABORT BIT
	IORM	S1,.JBFLG(R)	;LITE FOR SCHEDULER
	$TEXT	(LOGCHR,<^I/NBABO/Job aborted by NEBULA>)
	$TEXT	(<-1,,.JBRSN(R)>,<Job aborted by NEBULA^0>)
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- QUASAR message #10 (LIST)


LIST:	TDZA	S1,S1		;GET A ZERO AND SKIP
SHOWQ:	SETO	S1,		;GET -1
	PUSHJ	P,.SAVE1	;SAVE P1
	MOVE	P1,S1		;PRESERVE REQUEST TYPE INDICATION
	PUSHJ	P,FNDLST	;FIND AN IDLE LISTING STREAM
	  JRST	LIST.3		;NONE, CAN WE DEFER THE REQUEST?
	PUSH	P,P1		;SAVE REQUEST TYPE INDICATION
	MOVE	P1,S1		;COPY STREAM NUMBER
	PUSHJ	P,SETU.1	;DO BASIC STREAM SETUP
	POP	P,.JLTYP(R)	;SAVE REQUEST TYPE INDICATION
	PUSHJ	P,COPMSG	;COPY REQUEST MESSAGE TO STREAM DATABASE
	MOVE	S1,G$COD	;GET SENDER'S ACK CODE
	MOVEM	S1,.JLCOD(R)	;SAVE IT

LIST.1:	PUSHJ	P,G$BLK		;GET THE NEXT BLOCK FROM THE MESSAGE
	  JRST	LIST.2		;DONE
	MOVE	S1,0(T3)	;GET THE DATA ITEM
	CAIN	T1,.LSPID	;PID OF REQUESTOR?
	MOVEM	S1,.JLPID(R)	;YES
	CAIN	T1,.LSDND	;DESTINATION NODE?
	MOVEM	S1,.JBNOD(R)	;YES
	CAIN	T1,.ORNOD	;VIA OPR REQUEST?
	MOVEM	S1,.JBNOD(R)	;YES
;CHECK FOR OTHER USEFUL ONES HERE
	CAIE	T1,.LSQNM	;QUEUE NAME BLOCK?
	JRST	LIST.1		;NO, LOOK FOR MORE
	SUBI	T2,ARG.DA	;SUBTRACT OVERHEAD WORDS
	MOVEI	T1,.JLQNM(R)	;WHERE TO COPY THE QUEUE NAME STRING
	HRL	T1,T3		;SET SOURCE ADDRESS
	ADDI	T2,-1(T1)	;COMPUTE END ADDRESS
	BLT	T1,(T2)		;COPY THE QUEUE NAME OVER
	JRST	LIST.1		;LOOK FOR MORE

;Now check for consistancy

LIST.2:	SKIPE	.JBNOD(R)	;MUST HAVE A NODE
	SKIPN	.JLPID(R)	;MUST ALSO HAVE REQUESTOR'S PID
	JRST	LIST.5		;ERROR
	MOVE	S1,.JBNOD(R)	;GET REMOTE NODE
	PUSHJ	P,SELDRV	;SELECT A NETWORK DRIVER
	  JRST	LIST.4		;NONE ACCEPTABLE
	MOVE	S1,.JBACS+P(R)	;GET STREAM STACK
	PUSH	S1,[LLIST]	;WHERE TO START JOB
	MOVEM	S1,.JBACS+P(R)	;UPDATE
	POPJ	P,		;RETURN
LIST.3:	HALT	.		;SURE WOULD BE NICE TO DEFER THIS!

LIST.4:	MOVEI	S1,[ASCIZ / Remote Queue Listing /] ;HEADER TEXT
	PUSHJ	P,SETPAG	;SET UP A DISPLAY TEXT PAGE
	$TEXT	(DEPBYT,<Node ^N/.JBNOD(R)/ is not accessible>)
	PJRST	SENDIT		;SEND OFF THE ACK AND RETURN

LIST.5:	MOVEI	S1,[ASCIZ / Remote Queue Listing /] ;HEADER TEXT
	PUSHJ	P,SETPAG	;SET UP A DISPLAY TEXT PAGE
	$TEXT	(DEPBYT,<Invalid list request message>)
	PJRST	SENDIT		;SEND OFF THE ACK AND RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- QUASAR message #22 (SETUP)


SETUP:	PUSHJ	P,.SAVE1	;SAVE P1
	MOVX	S1,SUFSHT	;SEE IF A SHUTDOWN
	TDNE	S1,SUP.FL(M)	;...
	JRST	SHUTDN		;IF SO, SHUT IT DOWN
	MOVSI	P1,-PRCN	;CHECK FOR A FREE PROCESSING STREAM

	SKIPN	G$ADR(P1)	;A FREE STREAM?
	JRST	SETU.1		;YES
	AOBJN	P1,.-2		;KEEP LOOKING
	STOPCD	(TMS,HALT,,<Too many SETUP messages>)

;ENTERED HERE FOR LIST/SHOW QUEUE STREAM SETUP

SETU.1:	MOVEM	P1,CURJOB	;SAVE CURRENT STREAM
	MOVEI	S1,.JPAGS	;NUMBER OF PAGES
	PUSHJ	P,M%AQNP	;ALLOCATE CORE
	PG2ADR	S1		;CONVERT PAGE NUMBER TO ADDRESS
	MOVEM	S1,G$ADR(P1)	;SAVE IN TABLE
	MOVE	R,S1		;SET UP STREAM RELOCATION

;SET UP ACS

	MOVEM	R,.JBACS+R(R)	;SAVE RELOCATION
	MOVSI	S1,-PDLSSZ	;PDL LENGTH
	HRRI	S1,.JBPDL-1(R)	;ADDRESS
	PUSH	S1,[KILJOB]	;WHERE FINAL POPJ GOES
	MOVEM	S1,.JBACS+P(R)	;SAVE

;FILL IN OBJECT BLOCK

	MOVEI	S1,.OTNQC	;OBJECT TYPE
	MOVEM	S1,.JQOBJ+OBJ.TY(R)
	MOVE	S1,G$HNBR	;GET HOST NODE NUMBER
	MOVEM	S1,.JQOBJ+OBJ.ND(R) ;STUFF IN OBJECT BLOCK
	HRRZM	P1,.JQOBJ+OBJ.UN(R) ;STREAM NUMBER

;SET UP PSI INTERRUPT WORDS

	MOVE	S1,[$BGINT (1)]	;SWITCH TO INTERRUPT STACK
	MOVEM	S1,.JBPSR+0(R)
	MOVSI	S1,(MOVEI R,)	;WILL NEED RELOCATION
	HRR	S1,R		; TO PROCESS INTERRUPT
	MOVEM	S1,.JBPSR+1(R)
	HRLOI	S1,(JRST)	;AND A DISPATCH
	MOVEM	S1,.JBPSR+2(R)
;INITIALIZE BUFFER POINTERS

	PUSHJ	P,INIOBF	;INITIALIZE OUTPUT BUFFER STUFF

;ALL SETUP COMMON TO PROCESSING AND LISTING STREAMS MUST BE DONE
;BEFORE THIS POINT

	HRRZ	S1,P1		;ISOLATE STREAM NUMBER
	CAIL	S1,PRCN		;IS THIS A LISTING STREAM?
	POPJ	P,		;YES, THAT'S ALL SHE WROTE

;SET STATE TO "IDLE"

	MOVX	S1,.WSIDL	;GET THE STATE
	MOVEM	S1,.JBWSC(R)	;PREVENT SCHEUDLING UNTIL A NEXTJOB RECEIVED

;CHECK ATTRIBUTES

	MOVE	S1,SUP.CN(M)	;GET CONDITION WORD (WE STUFF ATTRIBUTES THERE)
	CAXE	S1,%NQINP	;INPUT STREAM?
	TDZA	S1,S1		;NO
	MOVX	S1,JB.INP	;YES
	IORM	S1,.JBFLG(R)	;SET THE FLAG

;FIRE UP STREAM IF INPUT

	.CREF	JB.INP		;BIT WE'RE TESTING
	SKIPGE	.JBFLG(R)	;INPUT STREAM?
;	PUSHJ	P,FIREUP	;YES
;	  MOVX	S1,%RSUNA	;NOT AVAILABLE
	JFCL

;SEND RESPONSE TO QUASAR'S SETUP MESSAGE

	MOVX	S1,%RSUOK	;TELL QUASAR ALL IS WELL
	MOVE	S2,SUP.CN(M)	;GET ATTRIBUTE
	PUSHJ	P,RSETUP	;SEND RESPONSE TO SETUP MESSAGE
	POPJ	P,		;ALL DONE
;The SETUP message was really a SHUTDOWN message

SHUTDN:	MOVE	S1,SUP.UN(M)	;GET UNIT NUMBER
	MOVE	S2,SUP.NO(M)	;GET NODE IDENTIFIER
	PUSHJ	P,FNDOBJ	;FIND THE OBJECT
	  POPJ	P,		;DUH?
	MOVX	S1,JB.KIL	;KILL
	IORM	S1,.JBFLG(R)	; JOB
	.CREF	.WSRUN		;SHOW NEW STATE
	SETZM	.JBWSC(R)	;MAKE RUNNABLE
	POPJ	P,		;RETURN
SUBTTL	SETUP message processing -- Response to SETUP

;	Call:	S1/	response code
;		S2/	attribute (%NQINP or %NQOUT)

RSETUP:	PUSH	P,S1		;SAVE RESPONSE CODE
	PUSH	P,S2		;AND ATTRIBUTE
	MOVX	S1,RSU.SZ	;MESSAGE LENGTH
	MOVEI	S2,G$MSG	;SCRATCH SPACE
	PUSHJ	P,.ZCHNK	;ZERO IT OUT
	MOVEI	M,G$MSG		;GET THE ADDRESS
	MOVX	S1,RSU.SZ	;MESSAGE LENGTH
	STORE	S1,.MSTYP(M),MS.CNT ;STORE IT
	MOVX	S1,.QORSU	;MESSAGE TYPE
	STORE	S1,.MSTYP(M),MS.TYP ;STORE IT
	MOVSI	S1,.JQOBJ(R)	;ADDRESS OF OBJECT BLOCK
	HRRI	S1,RSU.TY(M)	;WHERE IT GOES
	BLT	S1,RSU.TY+OBJ.SZ-1(M) ;COPY IT TO THE RESPONSE MESSAGE
	POP	P,S1		;GET ATTRIBUTE BACK
	STORE	S1,RSU.DA(M),RO.ATR ;PUT IN MSG
	POP	P,S1		;GET RESPONSE CODE BACK
	MOVEM	S1,RSU.CO(M)	;STORE IT
	PUSHJ	P,G$SQSR	;SEND RESPONSE TO QUASAR
	POPJ	P,		;RETURN
SUBTTL	Stream setup -- Copy request message to stream database


COPMSG:	MOVX	S1,PAGSIZ	;SIZE OF MESSAGE STORAGE
	MOVEI	S2,.JQEQP(R)	;ADDRESS OF IT
	PUSHJ	P,.ZCHNK	;CLEAR IT OUT
	MOVS	S1,M		;COPY MESSAGE ADDRESS
	HRRI	S1,.JQEQP(R)	;WHERE IT GOES
	LOAD	S2,.MSTYP(M),MS.CNT ;LENGTH OF THE MESSAGE
	ADDI	S2,.JQEQP(R)	;COMPUTE END OF BLT
	BLT	S1,-1(S2)	;COPY THE MESSAGE
	POPJ	P,		;RETURN


FNDOBJ:	$SAVE	<T1>		;SAVE AN AC
	MOVSI	T1,-PRCN	;NUMBER OF PROCESSING STREAMS

FNDO.1:	SKIPN	R,G$ADR(T1)	;GET THE DATABASE
	JRST	FNDO.2		;NONE
	CAMN	S1,.JQOBJ+OBJ.UN(R) ;CHECK UNIT NUMBER
	CAME	S2,.JQOBJ+OBJ.ND(R) ; AND NODE IDENTIFIER
FNDO.2:	AOBJN	T1,FNDO.1	;LOOP
	JUMPGE	T1,.POPJ	;DIDN'T FIND IT
	JRST	.POPJ1		;SKIP RETURN


FNDITN:	$SAVE	<T1>		;SAVE AN AC
	MOVSI	T1,-PRCN	;NUMBER OF PROCESSING STREAMS

FNDI.1:	SKIPN	R,G$ADR(T1)	;GET THE DATABASE
	JRST	FNDI.2		;NONE
	CAME	S1,.EQITN(R)	;INTERNAL TASK NAME MATCH?
FNDI.2:	AOBJN	T1,FNDI.1	;LOOP
	JUMPGE	T1,.POPJ	;DIDN'T FIND IT
	JRST	.POPJ1		;SKIP RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- ORION message #200004 (PAUSE)


SUBTTL	IPCF/Operator/QUASAR interface -- QUASAR message #6 (ABORT)


PAUSE:	PUSHJ	P,VALOBJ	;VALIDATE OBJECT BLOCK
	  POPJ	P,		;ERROR
	MOVNI	TF,1		;ASSUME SUCCESS
	SETZ	S1,		;CLEAR ERROR TEXT
	MOVE	S2,.JBFLG(R)	;GET STREAM FLAGS
	TXNE	S2,JB.PAU	;PAUSE REQUEST ALREADY QUEUED?
	MOVEI	S1,[ASCIZ /Stop in progress/]
	TXNE	S2,JB.REQ	;REQUEUE QUEUED?
	MOVEI	S1,[ASCIZ /Requeue in progress/]
	TXNE	S2,JB.ABO	;ABORT QUEUED?
	MOVEI	S1,[ASCIZ /Abort in progress/]
	SKIPE	S1		;HAVE ERROR TEXT?
	TDZA	TF,TF		;YES--FLAG IT
	MOVEI	S1,[ASCIZ /Stop request queued/]
	PUSH	P,TF		;SAVE FLAG
	$ACK	(<^T/(S1)/>,<^R/.EQJBB(R)/>,.JQOBJ(R),.MSCOD(M))
	POP	P,TF		;GET FLAG BACK
	MOVX	S1,JB.PAU	;GET PAUSED BIT
	SKIPE	TF		;ERROR?
	IORM	S1,.JBFLG(R)	;NO--TELL SCHEDULER TO STOP JOB
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- ORION message #200005 (CONTINUE)


CONTIN:	PUSHJ	P,VALOBJ	;VALIDATE OBJECT BLOCK
	  POPJ	P,		;ERROR
	MOVE	S1,.JBWSC(R)	;GET WAIT STATE CODE
	CAIN	S1,.WSPAU	;PAUSED?
	JRST	CONTI1		;YES
	$ACK	(<Not stopped>,,<.JQOBJ(R)>,<.MSCOD(M)>)
	JRST	CONTI2		;ONWARD

CONTI1:	$ACK	(<Continued>,<^R/.EQJBB(R)/>,.JQOBJ(R),.MSCOD(M))

CONTI2:	.CREF	.WSRUN		;SHOW NEW STATE
	SETZM	.JBWSC(R)	;MAKE THE STREAM RUNNABLE
	PUSHJ	P,FSTATU	;UPDATE QUASAR
	MOVX	S1,JB.PAU	;GET PAUSED BIT
	ANDCAM	S1,.JBFLG(R)	;LET JOB CONTINUE
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- ORION message #200025 (REQUEUE)


REQUE:	PUSHJ	P,VALOBJ	;VALIDATE OBJECT BLOCK
	  POPJ	P,		;ERROR
	MOVNI	TF,1		;ASSUME SUCCESS
	SETZ	S1,		;CLEAR ERROR TEXT
	MOVE	S2,.JBFLG(R)	;GET STREAM FLAGS
	TXNE	S2,JB.REQ	;PAUSE REQUEST ALREADY QUEUED?
	MOVEI	S1,[ASCIZ /Requeue in progress/]
	TXNE	S2,JB.PAU	;REQUEUE QUEUED?
	MOVEI	S1,[ASCIZ /Stop in progress/]
	TXNE	S2,JB.ABO	;ABORT QUEUED?
	MOVEI	S1,[ASCIZ /Abort in progress/]
	SKIPE	S1		;HAVE ERROR TEXT?
	TDZA	TF,TF		;YES--FLAG IT
	MOVEI	S1,[ASCIZ /Requeue request queued/]
	PUSH	P,TF		;SAVE FLAG
	$ACK	(<^T/(S1)/>,<^R/.EQJBB(R)/>,.JQOBJ(R),.MSCOD(M))
	POP	P,TF		;GET FLAG BACK
	JUMPE	TF,.POPJ	;RETURN ON ERRORS
	MOVX	S1,JB.REQ	;GET REQUE BIT
	IORM	S1,.JBFLG(R)	;TELL SCHEDULER TO REQUEUE JOB
	$TEXT	(LOGCHR,<^I/NBREQ/Job requeued by the operator>)
	POPJ	P,		;RETURN


;Here from when network driver decides to requeue the job
REQUEJ::MOVX	S1,JB.REQ	;GET REQUEUE BIT
	IORM	S1,.JBFLG(R)	;LITE FOR SCHEDULER
	$TEXT	(LOGCHR,<^I/NBREQ/Job requeued by NEBULA>)
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- ORION message #200026 (CANCEL)


CANCEL:	PUSHJ	P,VALOBJ	;VALIDATE OBJECT BLOCK
	  POPJ	P,		;ERROR
	MOVNI	TF,1		;ASSUME SUCCESS
	SETZ	S1,		;CLEAR ERROR TEXT
	MOVE	S2,.JBFLG(R)	;GET STREAM FLAGS
	TXNE	S2,JB.ABO	;ABORT REQUEST ALREADY QUEUED?
	MOVEI	S1,[ASCIZ /Abort in progress/]
	TXNE	S2,JB.REQ	;REQUEUE QUEUED?
	MOVEI	S1,[ASCIZ /Requeue in progress/]
	TXNE	S2,JB.PAU	;PAUSE QUEUED?
	MOVEI	S1,[ASCIZ /Stop in progress/]
	SKIPE	S1		;HAVE ERROR TEXT?
	TDZA	TF,TF		;YES--FLAG IT
	MOVEI	S1,[ASCIZ /Abort request queued/]
	PUSH	P,TF		;SAVE FLAG
	$ACK	(<^T/(S1)/>,<^R/.EQJBB(R)/>,.JQOBJ(R),.MSCOD(M))
	POP	P,TF		;GET FLAG BACK
	JUMPE	TF,.POPJ	;RETURN ON ERRORS
	MOVX	S1,JB.ABO	;GET ABORT BIT
	IORM	S1,.JBFLG(R)	;TELL SCHEDULER TO ABORT JOB
	$TEXT	(LOGCHR,<^I/NBABO/Job aborted by the operator>)
	$TEXT	(<-1,,.JBRSN(R)>,<Job aborted by the operator^0>)

CANC.1:	PUSHJ	P,G$BLK		;GET OPTIONAL SWITCH BLOCK
	  POPJ	P,		;JUNK MESSAGE??
	CAIE	T1,.ORREA	;/REASON?
	JRST	CANC.1		;NO--IGNORE IT
	PUSHJ	P,REASON	;PROCESS REASON BLOCK
	$TEXT	(LOGCHR,<^I/NBOPR/^T/.JBRSN(R)/>)
	POPJ	P,		;RETURN
SUBTTL	IPCF/Operator/QUASAR interface -- Send to OPR or QUASAR


G$SQSR::SKIPA	S2,[SI.FLG+SP.QSR] ;SEND TO [SYSTEM]QUASAR
G$SOPR::MOVE	S2,[SI.FLG+SP.OPR] ;SEND TO [SYSTEM]OPERATOR
	MOVEI	S1,0		;DON'T USE A REAL PID

G$SEND::MOVEM	S1,G$SAB+SAB.PD	;SAVE PID
	MOVEM	S2,G$SAB+SAB.SI	;SAVE SPECIAL PID INDEX WORD
	LOAD	S1,.MSTYP(M),MS.CNT ;GET LENGTH
	MOVEM	S1,G$SAB+SAB.LN	;SAVE
	MOVEM	M,G$SAB+SAB.MS	;SAVE MESSAGE ADDRESS
SEND.0:	PUSHJ	P,FNDPID	;FIND THE PID IN THE RESEND QUEUE
	  JRST	SEND.1		;ALREADY THERE
	MOVEI	S1,SAB.SZ	;SAB LENGTH
	MOVEI	S2,G$SAB	;SAB ADDRESS
	PUSHJ	P,C%SEND	;SEND MESSAGE
	JUMPT	.POPJ		;RETURN IF NO ERRORS
	CAIE	S1,ERNSP$	;NO SUCH PID?
	CAIN	S1,ERPWA$	;PID WENT AWAY?
	POPJ	P,		;JUST GIVE UP

SEND.1:	PUSHJ	P,.SAVE1	;SAVE P1
	MOVE	S1,IPCQUE	;GET RESEND QUEUE HANDLE
	PUSHJ	P,L%LAST	;POSITION TO END OF LIST
	MOVE	M,G$SAB+SAB.MS	;GET MESSAGE ADDRESS
	MOVE	S2,G$SAB+SAB.LN	;GET MESSAGE LENGTH
	TRNN	M,PAGSIZ-1	;MESSAGE ON A PAGE BOUNDRY?
	CAIE	S2,PAGSIZ	;AND A PAGE IN LENGTH?
	JRST	SEND.2		;NO--RANDOM PACKET
	SETZ	S2,		;ONLY SAVE THE SAB

SEND.2:	ADDI	S2,SAB.SZ	;PLUS THE SAB
	MOVE	P1,S2		;SAVE ENTRY SIZE
	MOVE	S1,IPCQUE	;GET LINKED LIST HANDLE AGAIN
	PUSHJ	P,L%CENT	;CREATE LIST ENTRY
	SKIPT			;DID IT WORK?
	STOPCD	(CCE,HALT,,<Can't create list entry>)
	MOVSI	S1,G$SAB	;POINT TO THE SAB
	HRRI	S1,(S2)		;AND TO THE LINKED LIST STORAGE
	BLT	S1,SAB.SZ-1(S2)	;COPY SAB
	CAIG	P1,SAB.SZ	;SAVING JUST THE SAB (PAGE MODE)?
	JRST	SEND.3		;YES
	MOVSI	S1,(M)		;POINT TO MESSAGE
	HRRI	S1,SAB.SZ(S2)	;POINT PAST THE SAB STORAGE
	ADD	S2,G$SAB+SAB.LN	;COMPUTE END BLT ADDRESS
	BLT	S1,SAB.SZ-1(S2)	;COPY MESSAGE INTO LIST

SEND.3:	AOS	RSENDC		;COUNT THE RESEND NEEDED LATER
	POPJ	P,		;RETURN
FNDPID:	PUSHJ	P,.SAVE2	;SAVE P1 AND P2
	MOVE	P1,G$SAB+SAB.PD	;GET PID
	MOVE	P2,G$SAB+SAB.SI	;GET SPECIAL INDEX WORD
	MOVE	S1,IPCQUE	;GET LINKED LIST FOR RESENDS
	PUSHJ	P,L%FIRS	;POSITION TO FIRST ENTRY
	JRST	FNDP.2		;ENTER LOOP

FNDP.1:	MOVE	S1,IPCQUE	;GET LINKED LIST FOR RESENDS
	PUSHJ	P,L%NEXT	;POSITION TO NEXT ENTRY

FNDP.2:	JUMPF	.POPJ1		;RETURN IF END OF LIST
	CAMN	P1,SAB.PD(S2)	;BOTH THE PID
	CAME	P2,SAB.SI(S2)	;AND THE INDEX MUST MATCH
	JRST	FNDP.1		;KEEP SEARCHING
	POPJ	P,		;RETURN
SUBTTL	Find an idle processing or listing stream


;This nonsense is required because a SETUP message does nothing
;really useful other than create the stream database.  When a
;NEXTJOB message is received, we have to find a idle processing
;stream to run the job in.
;Call:
;	PUSHJ	P,FNDPRC
;	  <Return if no free streams>
;	<Return if found a free stream>
;	R/ Address of stream database

FNDPRC:	MOVSI	S1,-PRCN	;NUMBER OF PROCESSING STREAMS
	MOVX	S2,.WSIDL	;WAIT STATE WE REQUIRE

	SKIPE	R,G$ADR(S1)	;IS THERE A STREAM THERE?
	CAME	S2,.JBWSC(R)	;YES, IT IT IDLE?
	AOBJN	S1,.-2		;NO, KEEP LOOKING
	JUMPGE	S1,.POPJ	;RETURN IF DIDN'T FIND IDLE STREAM (QUASAR ERROR?)
	JRST	.POPJ1		;SKIP RETURN


;Listing streams are somewhat easier.  We create them as
;needed as we receive LIST request messages.
;Call:
;	PUSHJ	P,FNDLST
;	  <Return if no free listing streams>
;	<Return if found a free stream>
;	S1/ Stream index

FNDLST:	MOVE	S1,[-LSTN,,PRCN] ;LISTING STREAMS FOLLOWING PROCESSING STREAMS
	SKIPN	R,G$ADR(S1)	;IS THIS ONE FREE?
	JRST	.POPJ1		;YES
	AOBJN	S1,.-2		;KEEP LOOKING
	POPJ	P,		;NO FREE LISTING STREAMS
SUBTTL	Select a network driver


;Routine to select a network driver to process a request.
;Call:
;	S1/ Node name
;	PUSHJ	P,SELDRV
;	  <Here if no driver can handle specified node>
;	<Here with I/O driver vector filled in>

SELDRV:	PUSHJ	P,.SAVE2	;SAVE P1 AND P2
	MOVE	P1,NETIOV	;GET START OF NETWORK I/O DRIVER CHAIN
	SKIPA	P2,S1		;COPY THE NODE NAME

SELD.1:	MOVE	S1,P2		;GET THE NODE NAME BACK
	PUSHJ	P,@.IOINF(P1)	;CAN THIS DRIVER HANDLE IT?
	  SKIPA	P1,.IOLNK(P1)	;NO, GET LINK TO NEXT
	JRST	SELD.2		;YES
	JUMPN	P1,SELD.1	;LOOP IF MORE DRIVERS
	POPJ	P,		;CAN'T HANDLE IT, NON-SKIP RETURN

SELD.2:	MOVSI	S1,(P1)		;POINT TO I/O DRIVER VECTOR
	HRRI	S1,.JNIOV(R)	;MAKE A BLT POINTER
	BLT	S1,.JNIOV+.IOLEN-1(R) ;COPY
	JRST	.POPJ1		;RETURN
SUBTTL	Initialization


INITIA:	MOVE	S1,[%%.MOD]	;GET OUR PROGRAM NAME
	MOVEM	S1,G$PGM	;SAVE
	MOVEI	S1,[ITEXT (<[SYSTEM]>)] ;PRODUCTION NAME
	SKIPE	DEBUGW		;DEBUGGING?
	MOVEI	S1,[ITEXT (<^U/DEBUGW/>)] ;YES
	LOAD	S2,PIB+PB.FLG,IP.SPF ;GET SYSTEM PID BIT
	CAIE	S2,1		;SYSTEM PID?
	MOVEI	S1,[ITEXT (<>)]	;NO
	$TEXT	(<-1,,G$NAM>,<^I/(S1)/^W/[%%.MOD]/^0>) ;GENERATE NAME
	PUSHJ	P,L%CLST	;CREATE A NEW ONE
	MOVEM	S1,IPCQUE	;SAVE HANDLE

INITI1:	MOVEI	S1,SP.QSR	;GET PID INDEX FOR [SYSTEM]QUASAR
	PUSHJ	P,C%RPRM	;ASK FOR THE PID
	JUMPF	INITI1		;WAIT FOR QUASAR
	MOVEM	S1,QSRPIB+PB.PID ;SAVE FOR IN BEHALF OF QUASAR SENDS

	PUSHJ	P,I%HOST	;GET NAME OF HOST NODE
	MOVEM	S1,G$HNAM	;SAVE IT
	MOVEM	S2,G$HNBR	;DITTO FOR NUMBER
	MOVE	S1,[%LDQUE]	;NEED THE QUEUE PPN
	GETTAB	S1,		;ASK MONITOR
	  MOVE	S1,[3,,3]	;DEFAULT
	SKIPE	DEBUGW		;DEBUGGING?
	MOVEI	S1,0		;YES--USE OUR PPN
	MOVEM	S1,G$QPPN	;SAVE
	MOVE	S1,[%LDSPP]	;NEED THE SPOOLED FILE PROTECTION
	GETTAB	S1,		;ASK MONITOR
	  MOVSI	S1,(<077>B8)	;DEFAULT TO <077>
	MOVEM	S1,G$QPRT	;SAVE
	MOVSI	S1,-5		;AOBJN POINTER

INITI2:	HRLZ	S2,S1		;GET OFFSET
	ADD	S2,[%CNFG0]	;FORM GETTAB ARGUMENT
	GETTAB	S2,		;FETCH WORD OF MONITOR NAME
	  SETZ	S2,		;???
	MOVEM	S2,CONFIG(S1)	;STUFF AWAY
	AOBJN	S1,INITI2	;LOOP

	MOVEI	S1,IPCINT	;IPCF INTERRUPT ROUTINE
	MOVEM	S1,IPCVEC+.PSVNP
	MOVEI	S1,TIMINT	;TIMER INTERRUPT ROUTINE
	MOVEM	S1,TIMVEC+.PSVNP
	HRREI	T1,.PCTMR	;CONDITION CODE
	MOVSI	T2,<TIMVEC-VECTOR> ;OFFSET,,0
	MOVSI	T3,TIMLVL	;INTERRUPT PRIORITY LEVEL
	MOVE	S1,[PS.FAC+T1]	;ADD CONDITION BIT + ADDRESS
	PISYS.	S1,		;PUT TIMER ON INTERRUPT SYSTEM
	  JSP	S2,UUOERR	;FAILED
	SKIPN	R,NETIOV	;POINT TO START OF NETWORK I/O VECTORS
	STOPCD	(NID,HALT,,<No I/O drivers included>)

INITI3:	PUSHJ	P,@.IOINI(R)	;INITIALIZE
	  $WTO	(<^T/G$NAM/ error>,<^T/@.IONAM(R)/ initialization failure>,,<$WTFLG(WT.SJI)>)
	SKIPE	R,.IOLNK(R)	;GET ADDRESS OF NEXT
	JRST	INITI3		;LOOP
	POPJ	P,		;RETURN
SUBTTL	PSI system control


;ASSIGN PSI VECTOR BLOCKS
;CALL:	PUSHJ	P,PSIAVG	;ASSIGN GLOBAL VECTOR
;	PUSHJ	P,PSIAVJ	;ASSIGN JOB VECTOR
;	  <ERROR>		;NONE AVAILABLE
;	<SKIP>			;S1 HAS ADDRESS OF ZEROED VECTOR BLOCK
;				;S2 HAS -OFFSET,,0

PSIAVG::SKIPA	S1,[-GLBPSV,,GLBVEC] ;POINTER TO GLOBAL VECTORS
PSIAVJ::MOVE	S1,[-JOBPSV,,JOBVEC] ;POINTER TO JOB VECTORS

PSIAV1:	SKIPN	.PSVNP(S1)	;VECTOR IN USE?
	JRST	PSIAV2		;NO
	ADDI	S1,3		;ACCOUNT FOR MULTI-WORD BLOCKS
	AOBJN	S1,PSIAV1	;LOOP
	POPJ	P,		;NONE AVAILABLE

PSIAV2:	HRRZS	S1		;STRIP OFF COUNT
	SETZM	.PSVOP(S1)	;CLEAR OLD PC
	SETZM	.PSVFL(S1)	;CLEAR FLAGS
	SETZM	.PSVIS(S1)	;CLEAR STATUS
	MOVSI	S2,-VECTOR(S1)	;GET OFFSET IN LH OF S2
	JRST	.POPJ1		;RETURN


;DEASSIGN PSI VECTOR BLOCKS
PSIDVJ::MOVE	S1,.JBPSI(R)	;GET PSI BLOCK ADDRESS
PSIDVG::SETZM	.PSVNP(S1)	;CLEAR NEW PC
	POPJ	P,		;RETURN
SUBTTL	Job processing -- Remote CREATE request


RCREAT:	PUSHJ	P,@.JNIOV+.IOOPN(R) ;OPEN A LINK TO REMOTE
	  JRST	RCRE.9		;OPEN FAILED, REQUEUE US
	PUSHJ	P,CONMSG	;TELL THE WORLD WE'RE CONNECTED
	$WTOJ	(Begin,<^R/.EQJBB(R)/>,.JQOBJ(R))
	PUSHJ	P,LOGINI	;INIT RUN LOG STUFF

;Steps in request creation:
;	Connect to remote DJM
;	Send create message
;	For each file,
;		Send FILESPEC message
;		Send FILEDATA message(s)
;		Send EOF message
;	Send EOR message
;	Receive ERROR or SUMMARY message
;	Send COMMIT message (no errors) or WITHDRAW message (errors)
;	Receive END message from remote DJM
;	Disconnect from remote DJM

;AC usage in this routine:
;
;	P1/ Number of files in this request
;	P2/ Address of current FP
;	P3/ Address of current FD

	MOVX	S1,.MTCRE	;TYPE = CREATE REQUEST
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER

;Job number

	MOVX	S1,.BTRJN	;TYPE = REQUESTOR JOB NUMBER
	MOVE	S2,.EQRID(R)	;USE REQUEST NUMBER
	PUSHJ	P,G$PWRD	;STUFF IT

;Job name

	$TEXT	<-1,,TMPBUF>,<^W/.EQJOB(R)/^0>
	MOVX	S1,.BTJNA	;TYPE = JOB NAME
	MOVE	S2,[POINT 7,TMPBUF]
	PUSHJ	P,G$PSTG	;STUFF IT
;Job owner

	$TEXT	<-1,,TMPBUF>,<^W/G$HNAM/::^W6/.EQOWN(R)/^W/.EQOWN+1(R)/^0>
	MOVX	S1,.BTJOW	;TYPE = JOB OWNER
	MOVE	S2,[POINT 7,TMPBUF]
	PUSHJ	P,G$PSTG	;STUFF IT

;Queue name

	$TEXT	<-1,,TMPBUF>,<^Q/QUENAM/^0>
	MOVX	S1,.BTQUE	;TYPE = QUEUE NAME
	MOVE	S2,[POINT 7,TMPBUF]
	PUSHJ	P,G$PSTG	;STUFF IT

;Priority

	MOVX	S1,.BTPRI	;TYPE = PRIORITY
	LOAD	S2,.EQSEQ(R),EQ.PRI ;GET EXTERNAL PRIORITY
	CAILE	S2,4		;IN RANGE FOR DQS?
	MOVEI	S2,4		;NO, USE THE MAXIMUM
	PUSHJ	P,G$PWRD	;STUFF IT

;Characteristics

	MOVEI	S1,.EQCHR(R)	;ADDRESS OF STRING
	SKIPN	(S1)		;CHARACTERISTICS SPECIFIED?
	JRST	RFORMS		;NO, DONT USE A DEFAULT
	PUSHJ	P,CNVCHR##	;CONVERT CHARACTERISTICS STRING TO BITMAP
	JUMPF	RCRE.A		;ERROR
	HRLI	S1,(POINT 8)	;SET BYTE POINTER
	MOVE	S2,S1		;WHERE IT BELONGS
	MOVE	S1,[.LNCHR,,.BTCHR] ;LENGTH = 16, TYPE = CHARACTERISTICS
	PUSHJ	P,G$PCST	;STUFF IT

;Forms code

RFORMS:	MOVEI	S1,.EQFRM(R)	;ADDRESS OF STRING
	SKIPN	(S1)		;FORMS SPECIFIED?
	MOVEI	S1,[ASCIZ /NORMAL/] ;NO, USE A DEFAULT
	PUSHJ	P,CNVFRM##	;CONVERT FORMS TYPE STRING
	JUMPF	RCRE.A		;ERROR
	MOVE	S2,S1		;WHERE G$PBYT WANTS IT
	MOVX	S1,.BTFRM	;TYPE = FORMS CODE
	PUSHJ	P,G$PBYT	;STUFF IT
;Notify action

	MOVX	S1,.BTNOA	;TYPE = NOTIFY ACTION
	MOVEI	S2,NA.CMP!NA.CHG ;NOTIFY ON COMPLETION OR CHANGE
	PUSHJ	P,G$PBYT	;STUFF IT

;Time queued

	$TEXT	<-1,,TMPBUF>,<^H9/.EQAFT(R)/^0> ;JUST DD-MMM-YY PART OF DATE/TIME
	MOVE	S2,[POINT 7,TMPBUF+1,13] ;NOW "VAX-IZE" THE YEAR
	ILDB	TF,S2		;GET FIRST CHARACTER
	ILDB	S1,S2		;GET SECOND CHARACTER
	MOVE	S2,[POINT 7,TMPBUF+1,13] ;BACK TO START
	MOVEI	T1,"1"		;THIS CENTURY
	IDPB	T1,S2
	MOVEI	T1,"9"
	IDPB	T1,S2
	IDPB	TF,S2		;NOW ADD IN 2 DIGITS OF YEAR
	IDPB	S1,S2
	MOVEM	S2,TMPPTR	;SAVE THE BYTE POINTER
	$TEXT	TMPDPB,< ^C/.EQAFT(R)/^0> ;NOW INCLUDE THE TIME
	MOVX	S1,.BTTMQ	;TYPE = AFTER TIME
	MOVE	S2,[POINT 7,TMPBUF]
	PUSHJ	P,G$PSTG	;STUFF IT

;Note string

	GETLIM	S1,.EQLIM(R),NOT1 ;GET THE FIRST HALF OF /NOTE:
	GETLIM	S2,.EQLIM(R),NOT2 ;GET THE SECOND HALF
	SKIPN	S1		;SKIP IF NOT NULL
	JUMPE	S2,RCRE.0	;IF NULL, IGNORE IT
	$TEXT	<-1,,TMPBUF>,<^W6/S1/^W/S2/^0>
	MOVX	S1,.BTNOT	;TYPE = NOTE
	MOVE	S2,[POINT 7,TMPBUF]
	PUSHJ	P,G$PSTG	;STUFF IT

RCRE.0:	PUSHJ	P,FINMSG	;FINISH MESSAGE
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND OUTPUT DATA
	  PJRST	ABORTJ		;ABORT THE JOB
;Now loop for each file, sending a FILESPEC message, one or more
;FILEDATA messages, and an EOF message.

	LOAD	P1,.EQSPC(R),EQ.NUM ;GET NUMBER OF FILES
	LOAD	P2,.EQLEN(R),EQ.LOH ;GET LENGTH OF HEADER
	ADD	P2,R		;POINT AT FIRST FP
	MOVE	P3,P2		;COPY IT
	LOAD	S1,.FPLEN(P2),FP.LEN ;GET LENGTH OF THE FP
	ADD	P3,S1		;POINT AT FIRST FD

RCRE.1:	PUSHJ	P,@.JNIOV+.IOSIA(R) ;HAS THE DJM OBJECTED TO SOMETHING IT ATE?
	  SKIPA			;NOPE, ALL IS WELL
	JSP	S1,RCRE.E	;YES, HANDLE DJM INDIGESTION
	AOS	.JQRFN(R)	;INCREMENT RELATIVE FILE NUMBER
	MOVEM	P3,.JQCFD(R)	;SAVE CURRENT FD ADDRESS
	PUSHJ	P,FSTATU	;TELL QUASAR OUR NEW STATUS
	PUSHJ	P,SNDFSP	;SEND FILESPEC MESSAGE
	PUSHJ	P,INPOPN	;OPEN THE NEXT FILE
	  JRST	RCRE.6		;ERROR
;Output the data here (similar to calling G$PSTG)

RCRE.2:	PUSHJ	P,@.JNIOV+.IOSIA(R) ;HAS THE DJM OBJECTED TO SOMETHING IT ATE?
	  SKIPA			;NOPE, ALL IS WELL
	JSP	S1,RCRE.E	;YES, HANDLE DJM INDIGESTION
	MOVX	S1,.MTFDT	;TYPE = FILEDATA
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	AOS	BLKCNT		;COUNT ANOTHER BLOCK
	MOVX	S1,.BTDAT	;TYPE = FILEDATA
	PUSHJ	P,PUTBYT	;STUFF THE BLOCK TYPE
	PUSH	P,.JIOBP(R)	;SAVE THE POINTER TO THE HIGH ORDER LENGTH BYTE
	SETZ	S1,		;GET A ZERO
	PUSHJ	P,PUTBYT	;STORE LENGTH OF ZERO TEMPORARILY
	PUSHJ	P,PUTBYT	;...
	PUSH	P,.JIOBC(R)	;SAVE OUTPUT BYTE COUNT
	MOVE	T1,.JIOBC(R)	;GET MAXIMUM NUMBER OF BYTES WE CAN SEND IN MSG
	SETZ	P4,		;ASSUME WE'LL HIT EOF THIS BUFFER FULL

RCRE.3:	MOVE	S1,.JFIFN(R)	;GET IFN
	PUSHJ	P,F%IBYT	;GET A BYTE
	JUMPF	RCRE.4		;END OF FILE PROBABLY
	SKIPN	S1,S2		;COPY THE CHARACTER
	JRST	RCRE.3		;NULL, THROW IT AWAY
	PUSHJ	P,PUTBYT	;STUFF THE BYTE
	SOJG	T1,RCRE.3	;LOOP FOR THE REST WE CAN DO
	SETO	P4,		;REMEMBER THERE'S MORE OUTPUT TO DO
RCRE.4:	POP	P,S1		;GET BYTE COUNT BEFORE STRING
	SUB	S1,.JIOBC(R)	;MINUS NUMBER LEFT GIVES NUMBER STORED
	ADDM	S1,.JQBYT(R)	;INCLUDE IN COUNT SENT
	POP	P,S2		;RETRIEVE BYTE POINTER TO LENGTH BYTE
	ROT	S1,-^D8		;SHIFT HIGH BYTE OVER
	IDPB	S1,S2		;STORE THE HIGH ORDER LENGTH BYTE
	ROT	S1,^D8		;SHIFT LOW BYTE BACK
	IDPB	S1,S2		;STORE THE LOW ORDER LENGTH BYTE

	PUSHJ	P,FINMSG	;FINISH MESSAGE
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND THE DATA
	  PJRST	ABORTJ		;ABORT THE JOB
	JUMPE	P4,RCRE.5	;JUMP IF NOTHING MORE TO DO
	AOS	S1,.JBFCT(R)	;BUMP FAIRNESS COUNTER
	CAXLE	S1,OUTFCT	;TIME TO DESCHED?
	PUSHJ	P,WSCHED	;GIVE OTHER STREAMS A CHANCE
	JRST	RCRE.2		;LOOP FOR MORE OUTPUT

RCRE.5:	MOVE	S1,.JFIFN(R)	;GET IFN
	PUSHJ	P,F%REL		;RELEASE THE FILE
	$TEXT	(LOGCHR,<^I/NBMSG/Finished file ^F/(P3)/>)
	JRST	RCRE.7		;DONE
;File LOOKUP error

RCRE.6:	$TEXT	(LOGCHR,<^I/NBERR/Can't access file ^F/0(P3)/, ^E/[-1]/^0>)
	$TEXT	(<-1,,TMPBUF>,<Can't access file ^F/0(P3)/, ^E/[-1]/^0>)
	MOVX	S1,.MTFDT	;TYPE = FILEDATA
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	MOVX	S1,.BTDAT	;TYPE = DATA TRANSFER
	MOVE	S2,[POINT 7,TMPBUF]
	PUSHJ	P,G$PSTG	;STUFF THE STRING
	PUSHJ	P,FINMSG	;FINISH UP MESSAGE
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND OUTPUT DATA
	  PJRST	ABORTJ		;ABORT THE JOB

RCRE.7:	PUSHJ	P,@.JNIOV+.IOSIA(R) ;HAS THE DJM OBJECTED TO SOMETHING IT ATE?
	  SKIPA			;NOPE, ALL IS WELL
	JSP	S1,RCRE.E	;YES, HANDLE DJM INDIGESTION
	MOVX	S1,.MTEOF	;TYPE = END OF FILE
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	PUSHJ	P,FINMSG	;FINISH UP MESSAGE (NO BODY)
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND OUTPUT DATA
	  PJRST	ABORTJ		;ABORT THE JOB

	SOJLE	P1,RCRE.8	;DONE IF NO MORE FILES
	LOAD	S1,.FDLEN(P3),FD.LEN ;GET LENGTH OF FD
	ADD	P3,S1		;POINT AT NEXT FP
	MOVE	P2,P3		;GET A COPY
	LOAD	S1,.FPLEN(P2),FP.LEN ;GET LENGTH OF THE FP
	ADD	P3,S1		;POINT TO NEXT FD
	JRST	RCRE.1		;GO PROCESS NEXT FILE

;All the files have been sent, send the END (COMMIT) message

RCRE.8:	PUSHJ	P,LOGSUM	;GENERATE RUN LOG SUMMARY
	MOVX	S1,.MTEOR	;TYPE = END OF RECORD
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	PUSHJ	P,FINMSG	;FINISH UP MESSAGE (NO BODY)
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND THE MESSAGE
	  PJRST	ABORTJ		;ABORT THE JOB
;Now wait for a response from the DJM

	PUSHJ	P,@.JNIOV+.IOINP(R) ;GET SOME INPUT
	  PJRST	ABORTJ		;ABORT THE JOB
	LDB	S1,P.ITYP	;GET MESSAGE TYPE
	CAIE	S1,.MTSUM	;SUMMARY MESSAGE?
	  PJRST	ABORTJ		;ABORT THE JOB
;MIGHT WANT TO PROPAGATE REMOTE REQUEST (JOB) NUMBER TO QUEUER?

	MOVX	S1,.MTCOM	;TYPE = COMMIT
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	PUSHJ	P,FINMSG	;FINISH UP MESSAGE (NO BODY)
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND THE MESSAGE
	  PJRST	ABORTJ		;ABORT THE JOB

	PUSHJ	P,@.JNIOV+.IOINP(R) ;GET SOME INPUT
	  PJRST	ABORTJ		;ABORT THE JOB
	LDB	S1,P.ITYP	;GET MESSAGE TYPE
	CAIE	S1,.MTEND	;END MESSAGE?
	  PJRST	ABORTJ		;ABORT THE JOB

	PUSHJ	P,FILDIS	;DISPOSE OF SPOOLED FILES
	$WTOJ	(End,<^R/.JQEQP+.EQJBB(R)/>,.JQOBJ(R))
	PUSHJ	P,RLSMSG	;SEND RELEASE MESSAGE

RCRE.X:	PUSHJ	P,@.JNIOV+.IOCLS(R) ;DO DISCONNECT
	  PJRST	ABORTJ		;ABORT JOB
	PJRST	DISMSG		;GENERATE DISCONNECT MESSAGE AND RETURN

;Remote node not accessible

RCRE.9:	$WTO	(<Job requeued>,<Node ^N/.JBNOD(R)/ not accessible^I/0(S1)/>,.JQOBJ(R))
	PJRST	REQMSG		;SEND REQUEUE MESSAGE TO QUASAR AND RETURN

QUENAM:	POINT	8, .EQQNM(R)	;WHERE QUASAR STUFFS THE QUEUE NAME FOR US
;CNFCHR or CNVFRM gave an error return; clunk the job

RCRE.A:	HRRZ	S1,CFERRT##(S1)	;GET THE ASCII ERROR TEXT
	$WTO	(<Job aborted>,<^T/0(S1)/>,.JQOBJ(R))
	JRST	RCRE.K		;KLUNK THE JOB


;DJM sent something back, probably an error message

RCRE.E:	PUSHJ	P,@.JNIOV+.IOINP(R) ;GET THE INPUT
	  PJRST	ABORTJ		;WHAT?!
	LDB	S1,P.ITYP	;GET THE MESSAGE TYPE
	CAXE	S1,.MTERR	;ERROR MESSAGE?
	JRST	RCRE.P		;NO, PROTOCOL ERROR
	PUSHJ	P,UNPERR	;UNPACK THE ERROR MESSAGE
	$WTO	(<^T/G$NAM/ error>,<Remote DJM rejected request^M^J^T/ERRBUF/>,.JQOBJ(R))
	MOVE	S1,ERRLVL	;GET ERROR LEVEL
	CAXE	S1,.ELFAT	;FATAL ERROR?
	JRST	RCRE.R		;NO, PROBABLY SAFE TO REQUEUE IT
RCRE.K:	$WTOJ	(Abort,<^R/.JQEQP+.EQJBB(R)/>,.JQOBJ(R))
	PUSHJ	P,RLSMSG	;FAKE LIKE WE FINISHED IT
	JRST	RCRE.X		;QUIT WHILE WE'RE AHEAD

RCRE.P:	$WTO	(<^T/G$NAM/ error>,<DQS protocol error>,.JQOBJ(R))
RCRE.R:	PUSHJ	P,REQMSG	;SEND REQUEUE MESSAGE
	JRST	RCRE.X		;QUIT WHILE WE'RE AHEAD
SUBTTL	Remote create routines -- Open input file


INPOPN:	MOVX	S1,FOB.SZ	;GET THE FOB SIZE
	MOVEI	S2,.JFFOB(R)	;AND THE FOR ADDRESS
	PUSHJ	P,.ZCHNK	;ZERO IT OUT
	MOVEM	P3,.JFFOB+FOB.FD(R) ;SAVE FD ADDRESS
	MOVEI	S1,7		;LOAD NORMAL BYTE SIZE
	STORE	S1,.JFFOB+FOB.CW(R),FB.BSZ ;AND SAVE THE BYTE SIZE
	SETZM	.JFFOB+FOB.US(R) ;DEFAULT TO NO ACCESS CHECKING
	SETZM	.JFFOB+FOB.CD(R) ;HERE ALSO
	LOAD	S1,.EQSEQ(R),EQ.PRV ;GET THE USERS PRIVILEGE BITS
	JUMPN	S1,INPO.1	;IF SET, AVOID ACCESS CHECK
	LOAD	S1,.FPINF(P2),FP.SPL ;LIKEWISE IF SPOOLED
	JUMPN	S1,INPO.1	; ...
	MOVE	S1,.EQOID(R)	;GET THE PPN
	MOVEM	S1,.JFFOB+FOB.US(R) ;AND SAVE IT

INPO.1:	MOVX	S1,FOB.SZ	;GET FOB SIZE
	MOVEI	S2,.JFFOB(R)	;AND ADDRESS
	PUSHJ	P,F%IOPN	;OPEN THE FILE
	JUMPF	INPO.2		;JUMP IF FAILED
	MOVEM	S1,.JFIFN(R)	;ELSE, SAVE THE IFN
	JRST	.POPJ1		;AND RETURN

INPO.2:	ZERO	.FPINF(P2),FP.DEL ;CLEAR THE 'DELETE FILE' BIT
	POPJ	P,		;RETURN
SUBTTL	Remote create routines -- Send FILESPEC message


SNDFSP:	MOVE	S1,.FPINF(P2)	;GET FLAGS FOR FILE
	TXNE	S1,FP.REN	;IS IT /DISPOSE:RENAME?
	JRST	SFSP.3		;YES, PROCESS THAT
	TXNN	S1,FP.SPL	;IS IT A SPOOLED FILE?
	JRST	SFSP.2		;NO, CONTINUE ON
	TXNN	S1,FP.FLG	;IS IT ALSO A LOG FILE?
	JRST	SFSP.1		;NO, JUST A PLAIN SPOOLED FILE
	CAIE	P2,LOGFP	;SPOOLED BATCH LOG?
	$TEXT	(<-1,,TMPBUF>,<Batch Log File^0>)
	CAIN	P2,LOGFP	;CHECK AGAIN
	$TEXT	(<-1,,TMPBUF>,<NEBULA Run Log^0>)
	JRST	SFSP.5		;CONTINUE

SFSP.1:	MOVE	S1,.JFIFN(R)	;GET THE FILE'S IFN
	MOVX	S2,FI.SPL	;GET THE SPOOL NAME INFO CODE
	$CALL	F%INFO		;GET THE SPOOLED NAME
	JUMPE	S1,SFSP.4	;NOTHING
	$TEXT	(<-1,,TMPBUF>,<^W/S1/^0>) ;GENERATE THE SPOOLED NAME
	JRST	SFSP.5		;CONTINUE

SFSP.2:	$TEXT	(<-1,,TMPBUF>,<^W/.FDNAM(P3)/.^W3/.FDEXT(P3)/^0>)
	JRST	SFSP.5		;CONTINUE

SFSP.3:	$TEXT	(<-1,,TMPBUF>,<^W/.FPONM(P2)/.^W3/.FPOXT(P2)/^0>)
	JRST	SFSP.5		;CONTINUE

SFSP.4:	$TEXT	(<-1,,TMPBUF>,<Spooled Printer File^0>)
SFSP.5:	MOVX	S1,.MTFSP	;TYPE = FILE SPEC
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER

;Filename string

	MOVX	S1,.BTFNM	;TYPE = FILENAME
	MOVE	S2,[POINT 7,TMPBUF]
	PUSHJ	P,G$PSTG	;STUFF IT

;Data type

	MOVX	S1,.BTFTY	;TYPE = DATA TYPE
	SETZ	S2,		;I DON'T KNOW
	PUSHJ	P,G$PBYT	;STUFF IT

;Copy count

	MOVX	S1,.BTFCC	;TYPE = COPY COUNT
	LOAD	S2,.FPINF(P2),FP.FCY ;GET THE COUNT
	CAILE	S2,1		;DON'T BOTHER IF JUST ONE COPY
	PUSHJ	P,G$PWRD	;STUFF IT
;Separator

	MOVX	S1,.BTFSE	;TYPE = SEPARATOR
	MOVE	S2,.FPINF(P2)	;GET FILE INFO BITS
	TXNE	S2,FP.NFH	;NO FILE HEADERS?
	TDZA	S2,S2		;YES, CLEAR S2 AND SKIP
	MOVX	S2,SE.HDR	;GET THE HEADER BIT
IFN FTFBST,<
	TRO	S2,SE.BST	;SET THE "BURST" FLAG
>
IFN FTFTRL,<
	TRO	S2,SE.TRL	;SET THE "TRAILER" FLAG
>
	PUSHJ	P,G$PBYT	;STUFF IT

;Blank lines

	MOVX	S1,.BTFBL	;TYPE = BLANK LINE COUNT
	LOAD	S2,.FPINF(P2),FP.FSP ;GET THE SPACING
	SUBI	S2,1		;CONVERT TO NUMBER OF BLANK LINES
	PUSHJ	P,G$PBYT	;STUFF IT

;Page options

	MOVX	S1,.BTFPO	;TYPE = PAGE OPTIONS
	SETZ	S2,		;NONE YET
	PUSHJ	P,G$PBYT	;STUFF IT

;Start page

	MOVX	S1,.BTFSP	;TYPE = START PAGE
	MOVE	S2,.FPFST(P2)	;GET STARTING PAGE
	CAILE	S2,1		;DON'T BOTHER IF STARTING AT BEGINNING
	PUSHJ	P,G$PWRD	;STUFF IT

;End of blocks

	PUSHJ	P,FINMSG	;FINISH MESSAGE
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;OUTPUT IT
	  PJRST	ABORTJ		;ABORT THE JOB
	CAIE	P2,LOGFP	;NEBULA RUN LOG?
	$TEXT	(LOGCHR,<^I/NBMSG/Starting file ^F/(P3)/>)
	POPJ	P,		;EVENTUALLY
SUBTTL	Remote create routines -- Dispose of spooled files


FILDIS:	LOAD	P3,.EQLEN(R),EQ.LOH ;GET THE HEADER LENGTH
	ADD	P3,R		;POINT TO FIRST FILE
	LOAD	T1,.EQSPC(R),EQ.NUM ;GET THE NUMBER OF FILES
FILD.1:	MOVE	T2,.FPINF(P3)	;GET THE FILE INFO BITS
	LOAD	S2,.FPLEN(P3),FP.LEN ;GET THE FILE INFO LENGTH
	ADD	P3,S2		;POINT TO FILE SPEC
	MOVEM	P3,.JFFOB+FOB.FD(R) ;SAVE THE FD ADDRESS IN THE FOB
	LOAD	S2,.FPLEN(P3),FD.LEN ;GET THE FD LENGTH
	ADD	P3,S2		;POINT P3 AT NEXT FILE
	SETZM	.JFFOB+FOB.US(R) ;DEFAULT TO NO ACCESS CHECKING
	SETZM	.JFFOB+FOB.CD(R) ;HERE ALSO
	LOAD	S1,.EQSEQ(R),EQ.PRV ;GET THE USERS PRIVILGE BITS
	JUMPN	S1,FILD.2	;IF SET, AVOID ACCESS CHECK
	TXNE	T2,FP.SPL	;WAS IT A SPOOLED FILE?
	JRST	FILD.2		;YES,,THEN NO ACCESS CHECK
	MOVE	S1,.EQOID(R)	;GET THE PPN
	MOVEM	S1,.JFFOB+FOB.US(R) ;AND SAVE IT

FILD.2:	MOVX	S1,FOB.SZ	;GET THE FOB LENGTH
	MOVEI	S2,.JFFOB(R)	;AND THE FOB ADDRESS
	TXNE	T2,FP.SPL	;SPOOL FILE?
	JRST	FILD.3		;YES, DELETE THE FILE IN ANY CASE
	TXNE	T2,FP.DEL	;/DELETE?
FILD.3:	$CALL	F%DEL		;YES, HERE TO DELETE
	SOJG	T1,FILD.1	;GO PROCESS THE NEXT FILE
	$RETT			;RETURN
SUBTTL	Remote create routines -- Send release message


RLSMSG:	MOVX	S1,REL.SZ	;MESSAGE LENGTH
	MOVEI	S2,G$MSG	;SCRATCH SPACE
	PUSHJ	P,.ZCHNK	;ZERO IT OUT
	MOVEI	M,G$MSG		;POINT AT SCRATCH SPACE
	MOVX	S1,REL.SZ	;SIZE OF MESSAGE
	STORE	S1,.MSTYP(M),MS.CNT ;STORE SIZE
	MOVX	S1,.QOREL	;TYPE OF MESSAGE
	STORE	S1,.MSTYP(M),MS.TYP ;STORE TYPE
	MOVE	S1,.EQITN(R)	;GET INTERNAL TASK NAME
	MOVEM	S1,REL.IT(M)	;STUFF IT
	MOVX	S1,JB.ABO	;BIT TO TEST
	MOVX	S2,RF.ABO	;GET FAILURE BIT
	TDNE	S1,.JBFLG(R)	;JOB IN ERROR?
	IORM	S2,REL.FL(M)	;LITE ERROR BIT (USED FOR USER ACK)
	MOVSI	S1,.JBRSN(R)	;POINT TO REASON TEXT
	HRRI	S1,REL.TX(M)	;AND TO MESSAGE STORAGE
	SKIPE	.JBRSN(R)	;HAVE ANY REASON TEXT?
	BLT	S1,REL.TX+STSSIZ-1(M) ;YES--COPY IT
	PUSHJ	P,G$SQSR	;TELL QUASAR
	SKIPE	S1,.JRLNK(R)	;HAVE A RUN LOG?
	PUSHJ	P,L%DLST	;DELETE IT
	SETZM	.JRLNK(R)	;INVALIDATE
	POPJ	P,		;RETURN
SUBTTL	Run Log routines -- LOGINI - Initialize at start of job


LOGINI:	POPJ	P,		;*** TEMP ***
	PUSHJ	P,L%CLST	;CREATE LINKED LIST
	JUMPF	.POPJ		;RETURN ON ERRORS
	MOVEM	S1,.JRLNK(R)	;SAVE HANDLE FOR POSTERITY
	$TEXT	(LOGCHR,<^T/LOGHDR/>)
	MOVE	T1,.JBVER	;OUR VERSION
	MOVE	T2,G$HNBR	;OUR NODE
	MOVEI	T3,CONFIG	;MONITOR NAME
	$TEXT	(LOGCHR,<^I/NBCFG/NEBULA %^V/T1/  ^N/T2/  ^T/(T3)/>)

	MOVEI	T1,@.JNIOV+.IONAM(R) ;POINT TO NAME STRING
	MOVE	T2,.JBNOD(R)	;NODE NAME
	MOVEI	T3,[ITEXT ()]	;NULL ITEXT
IFN OLDDQS,<
	MOVX	S1,JB.OLD	;
	TDNE	S1,.JBFLG(R)	;TALKING TO THE OLD VERSION?
	MOVEI	T3,[ITEXT (< (old Distributed Job Manager)>)]
>
	$TEXT	(LOGCHR,<^I/NBNET/Connected via ^T/(T1)/ to node ^N/T2/^I/(T3)/>)

	MOVE	T1,.JQEQP+.EQJOB(R) ;JOB NAME
	LOAD	T2,.JQEQP+.EQSEQ(R),EQ.SEQ ;SEQUENCE NUMBER
	MOVEI	T3,.JQOBJ(R)	;OBJECT BLOCK
	$TEXT	(LOGCHR,<^I/NBJOB/Job /^W/T1/ sequence #^D/T2/ running in ^B/@T3/>)
	MOVE	T1,.JQEQP+.EQAFT(R) ;REQUEST CREATION DATE/TIME
	MOVEI	T2,.JQEQP+.EQACT(R) ;ACCOUNT STRING
	$TEXT	(LOGCHR,<^I/NBJOB/Request created ^H/T1/  Account "^T/(T2)/">)

	MOVEI	T1,.JQEQP+.EQUSR(R) ;USER NAME
	HRLI	T1,(POINT 8,)	;8-BIT ASCII
	SKIPE	.JQEQP+.EQUSR(R) ;HAVE SOMETHING?
	$TEXT	(LOGCHR,<^I/NBJOB/Spooled for ^Q/T1/>)

	MOVEI	T1,.JQEQP+.EQBOX(R) ;DISTRIBUTION
	HRLI	T1,(POINT 8,)	;8-BIT ASCII
	SKIPE	.JQEQP+.EQBOX(R) ;HAVE SOMETHING?
	$TEXT	(LOGCHR,<^I/NBJOB/Distribution to ^Q/T1/>)
	POPJ	P,		;RETURN
SUBTTL	Run Log routines -- LOGCHR - Character output


LOGCHR::SKIPN	.JRLNK(R)	;WANT RUN LOG?
	$RETT			;NO
	SOSL	.JRCNT(R)	;COUNT CHARACTERS
	JRST	LOGCH1		;GO STORE
	PUSH	P,S1		;SAVE S1
	PUSH	P,S2		;SAVE S2
	MOVE	S1,.JRLNK(R)	;GET LINKED LIST HANDLE
	MOVEI	S2,RLGBSZ	;BUFFER SIZE
	PUSHJ	P,L%CENT	;CREATE AN ENTRY
	JUMPF	LOGCH2		;FAILED
	HRLI	S2,(POINT 7,)	;MAKE A BYTE POINTER
	MOVEM	S2,.JRPTR(R)	;SAVE
	MOVEI	S2,<RLGBSZ*5>-1	;BYTE COUNT
	MOVEM	S2,.JRCNT(R)	;SAVE
	POP	P,S2		;RESTORE S2
	POP	P,S1		;RESTORE S1
LOGCH1:	IDPB	S1,.JRPTR(R)	;STUFF CHARACTER AWAY
	$RETT			;RETURN
LOGCH2:	MOVX	S1,JB.RLE	;BIT TO SET
	IORM	S1,.JBFLG(R)	;REMBER FOR LATER
	SKIPE	S1,.JRLNK(R)	;GET LINKED LIST HANDLE IF ANY
	PUSHJ	P,L%DLST	;DELETE LIST
	SETZM	.JRLNK(R)	;INVALIDATE
	POP	P,S2		;RESTORE S2
	POP	P,S1		;RESTORE S1
	$RETT			;RETURN
SUBTTL	Run Log routines -- LOGSUM - Generate summary


LOGSUM:	SKIPN	.JRLNK(R)	;HAVE A RUN LOG?
	POPJ	P,		;NO
	MOVE	S1,.JQRFN(R)	;FILES TRANSFERED
	$TEXT	(LOGCHR,<^I/NBSUM/Summary:^D5/S1/ files transfered>)
	MOVEI	S1,0		;DISK BLOCKS READ
	$TEXT	(LOGCHR,<^I/NBSUM/        ^D5/S1/ disk blocks read>)
	$TEXT	(LOGCHR,<^I/NBQUE/Request queued for processing in "^Q/QUENAM/" queue>)
	AOS	.JQRFN(R)	;INCREMENT RELATIVE FILE NUMBER
	MOVEI	P2,LOGFP	;DUMMY FP
	MOVEI	P3,LOGFD	;DUMMY FD
	PUSHJ	P,SNDFSP	;SEND FILESPEC
	PUSHJ	P,@.JNIOV+.IOSIA(R) ;HAS THE DJM OBJECTED TO SOMETHING IT ATE?
	  SKIPA			;NOPE, ALL IS WELL
	JSP	S1,RCRE.E	;YES, HANDLE DJM INDIGESTION
	MOVX	S1,.MTFDT	;TYPE = FILEDATA
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	AOS	BLKCNT		;COUNT ANOTHER BLOCK
	MOVX	S1,.BTDAT	;TYPE = FILEDATA
	PUSHJ	P,PUTBYT	;STUFF THE BLOCK TYPE
	PUSH	P,.JIOBP(R)	;SAVE THE POINTER TO THE HIGH ORDER LENGTH BYTE
	SETZ	S1,		;GET A ZERO
	PUSHJ	P,PUTBYT	;STORE LENGTH OF ZERO TEMPORARILY
	PUSHJ	P,PUTBYT	;...
	MOVE	S1,.JRLNK(R)	;GET LINKED LIST HANDLE
	PUSHJ	P,L%FIRS	;POSITION TO FIRST ENTRY
	JUMPF	LOGSU6		;SHOULDN'T FAIL
	JRST	LOGSU2		;ONWARD

LOGSU1:	MOVE	S1,.JRLNK(R)	;GET LINKED LIST HANDLE
	PUSHJ	P,L%NEXT	;POSITION TO NEXT ENTRY
	JUMPF	LOGSU6		;DONE?

LOGSU2:	HRLI	S2,(POINT 7,)	;BYTE POINTER
	MOVEM	S2,.JRPTR(R)	;SAVE
	MOVEI	S2,<RLGBSZ*5>-1	;BYTE COUNT
	MOVEM	S2,.JRCNT(R)	;SAVE
	PUSH	P,.JIOBC(R)	;SAVE OUTPUT BYTE COUNT

LOGSU3:	MOVE	T1,.JIOBC(R)	;GET MAX NUMBER OF BYTES WE CAN SEND IN MSG
	SETZ	P4,		;ASSUME WE'LL HIT EOF

LOGSU4:	ILDB	S1,.JRPTR(R)	;GET A CHARACTER
	JUMPE	S1,LOGSU5	;RUN LOG BUFFER RUN OUT?
	PUSHJ	P,PUTBYT	;STUFF IN NETWORK BUFFER
	SOJG	T1,LOGSU4	;LOOP 'TIL NET BUFFER FULL
	SETO	P4,		;REMEMBER THERE'S MORE OUTPUT TO DO

LOGSU5:	POP	P,S1		;GET BYTE COUNT BEFORE STRING
	SUB	S1,.JIOBC(R)	;MINUS NUMBER LEFT GIVES NUMBER STORED
	POP	P,S2		;RETRIEVE BYTE POINTER TO LENGTH BYTE
	ROT	S1,-^D8		;SHIFT HIGH BYTE OVER
	IDPB	S1,S2		;STORE THE HIGH ORDER LENGTH BYTE
	ROT	S1,^D8		;SHIFT LOW BYTE BACK
	IDPB	S1,S2		;STORE THE LOW ORDER LENGTH BYTE
	PUSHJ	P,FINMSG	;FINISH MESSAGE
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND THE DATA
	  PJRST	ABORTJ		;ABORT THE JOB
	AOS	S1,.JBFCT(R)	;BUMP FAIRNESS COUNTER
	CAXLE	S1,OUTFCT	;TIME TO DESCHED?
	PUSHJ	P,WSCHED	;GIVE OTHER STREAMS A CHANCE
	JUMPE	P4,LOGSU1	;LOOP BACK FOR ANOTHER RUN LOG BUFFER
	JRST	LOGSU3		;ELSE CONTINE TO EMPTY CURRENT

LOGSU6:	PUSHJ	P,@.JNIOV+.IOSIA(R) ;HAS THE DJM OBJECTED TO SOMETHING IT ATE?
	  SKIPA			;NOPE, ALL IS WELL
	JSP	S1,RCRE.E	;YES, HANDLE DJM INDIGESTION
	MOVX	S1,.MTEOF	;TYPE = END OF FILE
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	PUSHJ	P,FINMSG	;FINISH UP MESSAGE (NO BODY)
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;SEND OUTPUT DATA
	  PJRST	ABORTJ		;ABORT THE JOB
	MOVE	S1,.JRLNK(R)	;GET LINKED LIST HANDLE
	PUSHJ	P,L%DLST	;DELETE RUN LOG BUFFERS
	SETZM	.JRLNK(R)	;INVALIDATE
	POPJ	P,		;RETURN
SUBTTL	Run Log routines -- Miscellaneous


LOGHDR:	ASCIZ	/
	* * *  N E B U L A  R u n  L o g  * * *
/


LOGFP:	$BUILD	(FPXSIZ)		;BLOCK SIZE
	  $SET	(.FPLEN,FP.LEN,FPXSIZ)	;LENGTH
	  $SET	(.FPINF,FP.FFF,.FPFAS)	;FILE FORMAT = ASCII
	  $SET	(.FPINF,FP.FPF,%FPLAR)	;PRINT FORMAT = ARROW
	  $SET	(.FPINF,FP.FSP,1)	;SPACING = 1
	  $SET	(.FPINF,FP.FLG,1)	;LOG FILE
	  $SET	(.FPINF,FP.NFH,1)	;NO FILE HEADERS
	  $SET	(.FPINF,FP.SPL,1)	;SPOOLED FILE
	  $SET	(.FPINF,FP.FCY,1)	;COPY COUNT = 1
	$EOB				;END OF BLOCK


LOGFD:	$BUILD	(FDXSIZ)		;BLOCK SIZE
	  $SET	(.FDLEN,FD.LEN,FDXSIZ)	;LENGTH
	  $SET	(.FDLEN,FD.TYP,.FDNAT)	;NATIVE MODE
	  $SET	(.FDSTR,,<'SPL   '>)	;DEVICE
	  $SET	(.FDNAM,,<'NEBULA'>)	;FILE NAME
	  $SET	(.FDEXT,,<'LOG   '>)	;EXTENSION
	  $SET	(.FDPPN,,0)		;DEFAULT FROM DEVICE
	$EOB				;END OF BLOCK


;TIME STAMPS
NBABO::	ITEXT	(<^C/[-1]/ NEBABO	? >)
NBCFG::	ITEXT	(<^C/[-1]/ NEBCFG	>)
NBDAT::	ITEXT	(<^C/[-1]/ NEBDAT	>)
NBERR::	ITEXT	(<^C/[-1]/ NEBERR	? >)
NBJOB::	ITEXT	(<^C/[-1]/ NEBJOB	>)
NBMSG::	ITEXT	(<^C/[-1]/ NEBMSG	>)
NBNET::	ITEXT	(<^C/[-1]/ NEBNET	>)
NBOPR::	ITEXT	(<^C/[-1]/ NEBOPR	>)
NBQUE::	ITEXT	(<^C/[-1]/ NEBQUE	>)
NBREQ::	ITEXT	(<^C/[-1]/ NEBREQ	>)
NBSUM::	ITEXT	(<^C/[-1]/ NEBSUM	>)
SUBTTL	Send requeue message


REQMSG:	MOVX	S1,REQ.SZ	;MESSAGE LENGTH
	MOVEI	S2,G$MSG	;SCRATCH SPACE
	PUSHJ	P,.ZCHNK	;ZERO IT OUT
	MOVEI	M,G$MSG		;POINT AT SCRATCH SPACE
	MOVX	S1,REQ.SZ	;SIZE OF MESSAGE
	STORE	S1,.MSTYP(M),MS.CNT ;STORE SIZE
	MOVX	S1,.QOREQ	;TYPE OF MESSAGE
	STORE	S1,.MSTYP(M),MS.TYP ;STORE TYPE
	MOVE	S1,.EQITN(R)	;GET INTERNAL TASK NAME
	MOVEM	S1,REQ.IT(M)	;STUFF IT
	MOVX	S1,REQTIM	;REQUEUE TIME
	MOVX	S2,JB.HLD	;BIT TO TEST
	TDNN	S2,.JBFLG(R)	;REQUEUE BY OPR COMMAND?
	STORE	S1,REQ.FL(M),RQ.TIM ;STORE IT FOR QUASAR
	MOVX	S1,RQ.HBO	;GET "HELD BY OPR" BIT
	TDNE	S2,.JBFLG(R)	;TEST AGAIN
	IORM	S1,REQ.FL(M)	;THIS TIME LITE "HOLD"
	PUSHJ	P,G$SQSR	;SEND TO QUASAR
	SKIPE	S1,.JRLNK(R)	;HAVE A RUN LOG?
	PUSHJ	P,L%DLST	;DELETE IT
	SETZM	.JRLNK(R)	;INVALIDATE
	POPJ	P,		;RETURN
SUBTTL	Job processing -- Local LIST request


LLIST:	SETZM	.JLMPT(R)	;ASSUME QUEUE IS EMPTY
	SETZM	.JLNRM(R)	;INITIALIZE LISTING CONTROL FLAGS
	SETZM	.JLBPT(R)
	SETOM	.JLNJL(R)
	SETZM	.JLNAJ(R)

	PUSHJ	P,@.JNIOV+.IOOPN(R) ;OPEN A LINK TO REMOTE
	  JRST	LLST.7		;OPEN FAILED
	PUSHJ	P,CONMSG	;TELL THE WORLD WE'RE CONNECTED

	MOVEI	S1,.MTDIS	;TYPE = DISPLAY QUEUES
	PUSHJ	P,SETMSG	;SET UP MESSAGE HEADER
	MOVEI	S1,.BTJOW	;TYPE = JOB OWNER
	MOVEI	S2,G$NAM	;OUR NAME
	PUSHJ	P,G$PSTG	;STORE THE ASCII STRING BLOCK
	PUSHJ	P,FINMSG	;FINISH UP MESSAGE, SET LENGTH, ETC.
	PUSHJ	P,@.JNIOV+.IOOUT(R) ;OUTPUT BUFFER
	  PJRST	ABORTJ		;ABORT JOB

LLST.1:	PUSHJ	P,@.JNIOV+.IOINP(R) ;READ A BUFFER
	  PJRST	ABORTJ		;ABORT JOB
	LDB	S1,P.ITYP	;GET MESSAGE TYPE BYTE
	CAIN	S1,.MTEND	;END?
	JRST	LLST.2		;YES
	CAIE	S1,.MTSUM	;SUMMARY?
	JRST	LLST.6		;NO, MIGHT BE ERROR, BUT BAD NEWS IN ANY CASE
	PUSHJ	P,LISTEM	;DECODE MESSAGE
	JRST	LLST.1		;LOOP BACK FOR MORE

LLST.2:	PUSHJ	P,LSTM.6	;PRINT SUMMARY LINE IF NEEDED
	SKIPE	.JLMPT(R)	;ARE THE QUEUES EMPTY?
	JRST	LLST.4		;NO
	SKIPN	.JLTYP(R)	;OPERATOR REQUEST?
	JRST	LLST.3		;NO
	$ACK	(<The remote queues are empty>,,,.JLCOD(R))
	JRST	LLST.5		;WIND DOWN THE STREAM

LLST.3:	MOVEI	S1,[ASCIZ / Remote Queue Listing /] ;FOR OPR
	PUSHJ	P,SETPAG	;SET UP THE OUTPUT PAGE
	$ASCII	(<The remote queues are empty>)
LLST.4:	PUSHJ	P,CRLF		;END WITH A CRLF
	PUSHJ	P,SENDIT	;SEND THE LAST PAGE
LLST.5:	PUSHJ	P,@.JNIOV+.IOCLS(R) ;DO DISCONNECT
	  PJRST	ABORTJ		;ABORT JOB
	PJRST	DISMSG		;GENERATE DISCONNECT MESSAGE AND RETURN
LLST.6:	$WTO	(<^T/G$NAM/ error>,<Unknown message type ^O/S1/ received in lister>,.JQOBJ(R))
	PJRST	ABORTJ		;STOMP THIS GUY

LLST.7:	PUSH	P,S1		;SAVE ERROR TEXT
	MOVEI	S1,[ASCIZ / Remote Queue Listing /] ;FOR OPR
	PUSHJ	P,SETPAG	;SET UP THE OUTPUT PAGE
	POP	P,S1		;RESTORE ERROR TEXT
	$TEXT	(DEPBYT,<^M^JConnect to node ^N/.JBNOD(R)/ failed^T/0(S1)/>)
	PJRST	SENDIT		;SEND THE LAST PAGE
LISTEM:	PUSHJ	P,GETBYT	;SKIP TYPE
	PUSHJ	P,GETBYT	;SKIP FLAGS
	PUSHJ	P,GETBYT	;SKIP CONTEXT
	PUSHJ	P,GETBYT	;GET BLOCK COUNT
	MOVE	P1,S1		;SAVE BLOCK COUNT
	PUSHJ	P,GETWRD	;GET MESSAGE LENGTH
	MOVEI	S2,BFSBYT-.HDSIZ ;SEE IF IT MATCHES
	SUB	S2,.JNARG+.NSAA1(R) ; WHAT WE RECEIVED
	CAME	S1,S2		;...
	JRST	LENERR		;LENGTH ERROR
	SETZM	LISBEG		;ZERO OUT TEMPORARIES
	MOVE	S1,[LISBEG,,LISBEG+1]
	BLT	S1,LISEND	;ALL THE WAY

;Remove the blocks from the message and store them temporarily,
;then do the real listing.

LSTM.1:	SOJL	P1,LSTM.4	;DONE IF NO MORE BLOCKS
	PUSHJ	P,GETBYT	;GET THE BLOCK TYPE
	MOVE	T1,S1		;SAVE IT
	PUSHJ	P,GETBYT	;SKIP FLAGS
	PUSHJ	P,GETBYT	;GET LENGTH
	MOVE	T2,S1		;SAVE IT

;See if we're interested in this block type, and store the data
;temporarily if so.  Otherwise just skip this block.

	MOVSI	S1,-LSTTBL	;LENGTH OF TABLE
LSTM.2:	HLRZ	S2,LSTTAB(S1)	;GET A BLOCK TYPE
	CAME	S2,T1		;MATCH THE ONE WE HAVE?
	AOBJN	S1,LSTM.2	;NO, LOOP
	JUMPGE	S1,LSTM.3	;IF NO MATCH, JUST SKIP THE BLOCK
	HRRZ	S1,LSTTAB(S1)	;GET THE PROCESSING ROUTINE
	PUSHJ	P,(S1)		;CALL IT
	JRST	LSTM.1		;LOOP

LSTM.3:	SOJL	T2,LSTM.1	;DONE WHEN ALL BYTES PROCESSED
	PUSHJ	P,GETBYT	;GET A BYTE
	JRST	LSTM.3		;ONWARD
;Now do the actual listing

LSTM.4:	SKIPN	LISJNB		;REAL JOB?
	JRST	LSTM.6		;NOPE, MUST BE START OF NEW QUEUE
	SKIPN	.JLQNM(R)	;DID THE REQUESTOR SPECIFY A QUEUE NAME?
	JRST	LSTM.5		;NO
	MOVEI	S1,.JLQNM(R)	;GET ADDRESS OF THE QUEUE NAME STRING
	HRLI	S1,(POINT 8)	;IT'S 8-BIT ASCII
	HRROI	S2,LISQNM	;THIS QUEUE NAME IS IN 7-BIT ASCII
	PUSHJ	P,S%SCMP	;COMPARE THE STRINGS
	JUMPN	S1,.POPJ	;RETURN IF THEY DIDN'T COMPARE
LSTM.5:	SKIPE	.JLNRM(R)	;RUNNING OUT OF LISTING PAGE SPACE?
	PUSHJ	P,PAGOVF	;YES, SEND IT OUT AND GET ANOTHER
	AOSG	.JLNJL(R)	;FIRST ONE?
	PUSHJ	P,LSTHDR	;YES, PUT OUT HEADER
	MOVE	S1,LISSTS	;GET STATUS
	CAIN	S1,.STEXC	;EXECUTING?
	SKIPA	S1,[ASCIZ /* /]	;YES
	SKIPA	S1,[ASCIZ /  /]	;NO
	AOS	.JLNAJ(R)	;COUNT AN ACTIVE JOB
	$TEXT	DEPBYT,<^5/S1/^T20/LISNAM/  ^D7R/LISJNB/  ^D6R/LISPGL/  ^T20/LISOWN/>
	SETOM	.JLMPT(R)	;REMEMBER SOME QUEUE WAS NON-EMPTY
	POPJ	P,		;DONE

LSTM.6:	AOSG	S1,.JLNJL(R)	;GET NUMBER ACTUALLY LISTED
	JRST	LSTM.7		;NONE THERE
	SKIPN	.JLNAJ(R)	;ANY ACTIVE JOBS?
	SKIPA	S2,[[ITEXT (<(none in progress)>)]]
	MOVEI	S2,[ITEXT (<(^D/.JLNAJ(R)/ in progress)>)]
	CAIN	S1,1		;JUST ONE?
	$TEXT	DEPBYT,<^M^JThere is 1 job in the queue ^I/0(S2)/>
	CAIE	S1,1		;MORE THAN ONE?
	$TEXT	DEPBYT,<^M^JThere are ^D/S1/ jobs in the queue ^I/0(S2)/>
LSTM.7:	SETOM	.JLNJL(R)	;START OVER WITH THE NEXT QUEUE
	SETZM	.JLNAJ(R)
	POPJ	P,		;RETURN

LSTHDR:	MOVEI	S1,[ASCIZ / Remote Queue Listing /] ;FOR OPR
	SKIPN	.JLBPT(R)	;ALREADY DONE SO?
	PUSHJ	P,SETPAG	;SET UP THE OUTPUT PAGE
	$TEXT	(DEPBYT,<^M^JRemote queue ^T/LISQNM/:^T/HEADER/^A>)
	POPJ	P,		;RETURN

HEADER:	ASCIZ	|
Job Name                 Req #    Limit    User
----------------------  -------  ------  --------------------
|
SUBTTL	Block type dispatch table


LSTTAB:	XWD	.BTJNB,JNB	;JOB NUMBER
	XWD	.BTJNA,JNA	;JOB NAME
	XWD	.BTJOW,JOW	;JOB OWNER
	XWD	.BTJBS,JBS	;STATE
	XWD	.BTPGL,JPL	;PAGE LIMIT
	XWD	.BTPRI,PRI	;PRIORITY
	XWD	.BTFRM,FRM	;FORMS TYPE
	XWD	.BTNOT,NOT	;NOTE STRING
	XWD	.BTQUE,QUE	;QUEUE NAME STRING
LSTTBL==.-LSTTAB		;LENGTH OF TABLE
;Here for job number block

JNB:	PUSHJ	P,GETITM	;GET IT
	MOVEM	S1,LISJNB	;SAVE IT
	$RETT

;Here for job name block

JNA:	MOVEI	S1,LISNAM	;POINT WHERE IT GOES
	PJRST	GETSTG		;GET IT AND RETURN

;Here for job owner block

JOW:	MOVEI	S1,LISOWN	;POINT WHERE IT GOES
	PJRST	GETSTG		;GET IT AND RETURN

;Here for job state block

JBS:	PUSHJ	P,GETITM	;GET IT
	MOVEM	S1,LISSTS	;SAVE IT
	$RETT

;Here for page limit block

JPL:	PUSHJ	P,GETITM	;GET IT
	MOVEM	S1,LISPGL	;SAVE IT
	$RETT

;Here for priority block

PRI:	PUSHJ	P,GETITM	;GET IT
	MOVEM	S1,LISPRI	;SAVE IT
	$RETT

;Here for forms type block

FRM:	PUSHJ	P,GETITM	;GET IT(?)
	MOVEM	S1,LISFRM	;SAVE IT
	$RETT

;Here for NOTE block

NOT:	MOVEI	S1,LISNOT	;POINT AT WHERE IT GOES
	PJRST	GETSTG		;GET IT

;Here for queue name block

QUE:	MOVEI	S1,LISQNM	;POINT AT WHERE IT GOES
	PJRST	GETSTG		;GET IT
SUBTTL	Error message unpacking routine

UNPERR:	PUSHJ	P,.SAVE1##	;FREE UP P1
	PUSHJ	P,GETBYT	;SKIP TYPE
	PUSHJ	P,GETBYT	;SKIP FLAGS
	PUSHJ	P,GETBYT	;SKIP CONTEXT
	PUSHJ	P,GETBYT	;GET BLOCK COUNT
	MOVE	P1,S1		;SAVE BLOCK COUNT
	PUSHJ	P,GETWRD	;GET MESSAGE LENGTH
	MOVEI	S2,BFSBYT-.HDSIZ ;SEE IF IT MATCHES
	SUB	S2,.JNARG+.NSAA1(R) ; WHAT WE RECEIVED
	CAME	S1,S2		;...
	JRST	LENERR		;LENGTH ERROR
	SETZM	ERRBEG		;ZERO OUT TEMPORARIES
	MOVE	S1,[ERRBEG,,ERRBEG+1]
	BLT	S1,ERREND	;ALL THE WAY

;Remove the blocks from the message and store them.

UNPE.1:	SOJL	P1,.POPJ	;DONE IF NO MORE BLOCKS
	PUSHJ	P,GETBYT	;GET THE BLOCK TYPE
	MOVE	T1,S1		;SAVE IT
	PUSHJ	P,GETBYT	;SKIP FLAGS
	PUSHJ	P,GETBYT	;GET LENGTH
	MOVE	T2,S1		;SAVE IT

;See if we're interested in this block type, and store the data
;temporarily if so.  Otherwise just skip this block.

	MOVSI	S1,-ERRTBL	;LENGTH OF TABLE
UNPE.2:	HLRZ	S2,ERRTAB(S1)	;GET A BLOCK TYPE
	CAME	S2,T1		;MATCH THE ONE WE HAVE?
	AOBJN	S1,UNPE.2	;NO, LOOP
	JUMPGE	S1,UNPE.3	;IF NO MATCH, JUST SKIP THE BLOCK
	HRRZ	S1,ERRTAB(S1)	;GET THE PROCESSING ROUTINE
	PUSHJ	P,(S1)		;CALL IT
	JRST	UNPE.1		;LOOP

UNPE.3:	SOJL	T2,UNPE.1	;DONE WHEN ALL BYTES PROCESSED
	PUSHJ	P,GETBYT	;GET A BYTE
	JRST	UNPE.3		;ONWARD
ERRTAB:	XWD	.BTEEL,LVL	;ERROR LEVEL
	XWD	.BTEEC,CLS	;ERROR CLASS
	XWD	.BTECD,COD	;ERROR CODE
	XWD	.BTETX,TXT	;ERROR TEXT

;Here on error level, class, or code

LVL:	PUSHJ	P,GETITM	;GET IT
	MOVEM	S1,ERRLVL	;SAVE IT
	$RETT			;RETURN

CLS:	PUSHJ	P,GETITM	;GET IT
	MOVEM	S1,ERRCLS	;SAVE IT
	$RETT			;RETURN

COD:	PUSHJ	P,GETITM	;GET IT
	MOVEM	S1,ERRCOD	;SAVE IT
	$RETT			;RETURN

;Here on error text

TXT:	MOVEI	S1,ERRBUF	;POINT AT WHERE IT GOES
	PJRST	GETSTG		;GET IT
SUBTTL	Message unpacking routines


;Get a byte from the input message

GETBYT:	SOSGE	.JIIBC(R)	;STILL ROOM?
	JRST	LENERR		;NOPE
	ILDB	S1,.JIIBP(R)	;GET IT
	POPJ	P,		;RETURN


;Get a word (byte swapped) from the input message

GETWRD:	PUSHJ	P,GETBYT	;GET FIRST BYTE (LOW ORDER)
	PUSH	P,S1		;SAVE IT
	PUSHJ	P,GETBYT	;GET SECOND BYTE (HIGH ORDER)
	LSH	S1,^D8		;POSITION IT
	IOR	S1,(P)		;INCLUDE LOW ORDER
	ADJSP	P,-1		;POP OFF THE JUNK
	POPJ	P,		;RETURN


;Get a longword (swapped) from the input message

GETLWD:	PUSHJ	P,GETWRD	;GET FIRST WORD (LOW ORDER)
	PUSH	P,S1		;SAVE IT
	PUSHJ	P,GETWRD	;GET SECOND WORD (HIGH ORDER)
	LSH	S1,^D16		;POSITION IT
	IOR	S1,(P)		;INCLUDE LOW ORDER
	ADJSP	P,-1		;POP OFF THE JUNK
	POPJ	P,		;RETURN


;Get a byte/word/longword (based on length in T2)

GETITM:	SETZ	S1,		;ASSUME NO MATCH
	CAIN	T2,1		;SINGLE BYTE?
	MOVEI	S1,GETBYT	;YES
	CAIN	T2,2		;WORD?
	MOVEI	S1,GETWRD	;YES
	CAIN	T2,4		;LONGWORD?
	MOVEI	S1,GETLWD	;YES
	JUMPN	S1,(S1)		;DO IT
	POPJ	P,		;ON SECOND THOUGHT, DON'T DO IT


;Get a string (length in T2) and store it in block pointed to by S1

GETSTG:	TLNN	S1,-1		;POINTER SUPPLIED?
	HRLI	S1,(POINT 7)	;NOPE
	MOVE	T1,S1		;SAVE POINTER
GETS.1:	SOJL	T2,.RETT	;WHEN DONE
	PUSHJ	P,GETBYT	;GET A BYTE
	IDPB	S1,T1		;STORE IT
	JRST	GETS.1		;LOOP
SUBTTL	Message packing routines


;SETMSG - set up message header.
;Call:
;	S1/ message type

SETMSG:	SETZM	BLKCNT		;NO BLOCKS IN THIS MESSAGE YET
	PUSHJ	P,PUTBYT	;STUFF THE TYPE
	SETZ	S1,		;GET A ZERO
	PUSHJ	P,PUTBYT	;NO FLAGS
	MOVE	S1,.JQOBJ+OBJ.UN(R) ;SENDER'S CONTEXT(?)
	PUSHJ	P,PUTBYT	;STUFF IT
	SETZ	S1,		;GET A ZERO
	PUSHJ	P,PUTBYT	;NO BLOCKS YET
	PUSHJ	P,PUTBYT	;NO BODY LENGTH
	PJRST	PUTBYT		;RETURN


;FINMSG - finish a message

FINMSG:	MOVX	S1,BFSBYT	;GET BUFFER SIZE
	SUB	S1,.JIOBC(R)	;SUBTRACT AMOUNT FREE TO GET LENGTH
	SUBI	S1,.HDSIZ	;LENGTH DOESN'T INCLUDE HEADER
	DPB	S1,P.OLNL	;STORE LOW BYTE OF LENGTH
	LSH	S1,-^D8		;SHIFT OFF THE LOW BYTE
	DPB	S1,P.OLNH	;STORE HIGH BYTE OF LENGTH
	MOVE	S1,BLKCNT	;GET NUMBER OF BLOCKS
	DPB	S1,P.OBLK	;STORE IN MESSAGE
	POPJ	P,		;RETURN
;G$PBYT - Put a byte
;G$PWRD - Put a word (2 bytes)
;G$PLWD - Put a longword (4 bytes)
;G$PSTG - Put a string
;G$PCST - Put a counted string
;Call:
;	S1/ block type
;	S2/ data item (or pointer if string)

G$PBYT:	AOS	BLKCNT		;COUNT ANOTHER BLOCK
	PUSHJ	P,PUTBYT	;STORE THE BLOCK TYPE
	SETZ	S1,		;GET A ZERO
	PUSHJ	P,PUTBYT	;STORE HIGH ORDER LENGTH BYTE
	MOVEI	S1,1		;LENGTH
	PUSHJ	P,PUTBYT	;STORE LOW ORDER LENGTH BYTE
	MOVE	S1,S2		;DATA ITEM
	PJRST	PUTBYT		;STORE IT AND RETURN

G$PWRD:	AOS	BLKCNT		;COUNT ANOTHER BLOCK
	PUSHJ	P,PUTBYT	;STORE THE BLOCK TYPE
	SETZ	S1,		;GET A ZERO
	PUSHJ	P,PUTBYT	;STORE HIGH ORDER LENGTH BYTE
	MOVEI	S1,2		;LENGTH
	PUSHJ	P,PUTBYT	;STORE LOW ORDER LENGTH BYTE
	MOVE	S1,S2		;COPY THE WORD TO STORE
	PUSHJ	P,PUTBYT	;STORE LOW ORDER BYTE OF WORD
	LSH	S1,-^D8		;SHIFT OVER HIGH ORDER BYTE
	PJRST	PUTBYT		;STORE IT AND RETURN

G$PLWD:	AOS	BLKCNT		;COUNT ANOTHER BLOCK
	PUSHJ	P,PUTBYT	;STORE THE BLOCK TYPE
	SETZ	S1,		;GET A ZERO
	PUSHJ	P,PUTBYT	;STORE HIGH ORDER LENGTH BYTE
	MOVEI	S1,4		;LENGTH
	PUSHJ	P,PUTBYT	;STORE LOW ORDER LENGTH BYTE
	MOVE	S1,S2		;COPY LONG WORD
	PUSHJ	P,PUTBYT	;STORE LOW ORDER BYTE
	LSH	S1,-^D8		;SHIFT NEXT BYTE INTO POSITION
	PUSHJ	P,PUTBYT	;STORE IT
	LSH	S1,-^D8		;SHIFT NEXT BYTE INTO POSITION
	PUSHJ	P,PUTBYT	;STORE IT
	LSH	S1,-^D8		;SHIFT HIGH ORDER BYTE INTO POSITION
	PJRST	PUTBYT		;STORE IT AND RETURN
G$PSTG:	AOS	BLKCNT		;COUNT ANOTHER BLOCK
	PUSHJ	P,PUTBYT	;STORE THE BLOCK TYPE
	SETZ	S1,		;GET A ZERO
	PUSH	P,.JIOBP(R)	;SAVE THE POINTER TO THE HIGH ORDER LENGTH BYTE
	PUSHJ	P,PUTBYT	;STORE LENGTH OF ZERO TEMPORARILY
	PUSHJ	P,PUTBYT	;...
	PUSH	P,.JIOBC(R)	;SAVE OUTPUT BYTE COUNT
	TLNN	S2,-1		;IS THERE A POINTER?
	HRLI	S2,(POINT 7)	;NO, ASSUME REGULAR ASCII
PSTG.1:	ILDB	S1,S2		;GET A BYTE
	JUMPE	S1,PSTG.2	;JUMP IF END
	PUSHJ	P,PUTBYT	;STORE IT
	JRST	PSTG.1		;LOOP

PSTG.2:	POP	P,S1		;GET BYTE COUNT BEFORE STRING
	SUB	S1,.JIOBC(R)	;MINUS NUMBER LEFT GIVES NUMBER STORED
	POP	P,S2		;RETRIEVE BYTE POINTER TO LENGTH BYTE
	ROT	S1,-^D8		;SHIFT HIGH BYTE OVER
	IDPB	S1,S2		;STORE THE HIGH ORDER LENGTH BYTE
	ROT	S1,^D8		;SHIFT LOW BYTE BACK
	IDPB	S1,S2		;STORE THE LOW ORDER LENGTH BYTE
	POPJ	P,		;RETURN

G$PCST:	AOS	BLKCNT		;COUNT ANOTHER BLOCK
	PUSHJ	P,PUTBYT	;STUFF THE TYPE
	HLRZS	S1		;ISOLATE LENGTH
	ROT	S1,-^D8		;SHIFT HIGH BYTE OVER
	PUSHJ	P,PUTBYT	;STUFF IT
	ROT	S1,^D8		;SHIFT LOW BYTE BACK
	PUSHJ	P,PUTBYT	;STUFF IT
	MOVE	TF,S1		;COPY COUNT

	ILDB	S1,S2		;GET A BYTE
	PUSHJ	P,PUTBYT	;STUFF IT
	SOJG	TF,.-2		;LOOP AS REQUIRED
	POPJ	P,		;RETURN
	POPJ	P,		;FOR NOW

PUTBYT:	SOSGE	.JIOBC(R)	;ROOM FOR THIS BYTE?
	JRST	LENERR		;NOPE
	IDPB	S1,.JIOBP(R)	;STORE IT
	POPJ	P,		;RETURN
SUBTTL	SETPAG - Routine to set up an IPCF ACK page

;CALL:	S1/ The Address of an ASCIZ Type Line String
;
;RET:	True Always

SETPAG:	MOVE	T3,S1		;SAVE THE HEADER ADDRESS
	PUSHJ	P,M%GPAG	;GET A PAGE FOR OUTPUT
	MOVEM	S1,.JLPAG(R)	;SAVE ITS ADDRESS
	MOVE	S2,[.OHDRS,,.OMACS] ;GET MSG TYPE PARMS
	MOVEM	S2,.MSTYP(S1)	;SAVE IT IN THE MSG
	MOVE	S2,.JLCOD(R)	;GET THE OPR ACK CODE
	MOVEM	S2,.MSCOD(S1)	;SAVE IT IN THE MSG
	MOVX	S2,WT.SJI+WT.NFO ;GET JOB INFO SUPPRESS BITS
	MOVEM	S2,.OFLAG(S1)	;SAVE IT IN THE MSG
	AOS	.OARGC(S1)	;ADD 1 TO THE ARGUMENT COUNT
	MOVEI	S1,.OHDRS(S1)	;POINT TO THE FIRST MESSAGE BLK
	SKIPE	T3		;SKIP IF NO HEADER WANTED
	PUSHJ	P,SETHDR	;ELSE GO PUT IT IN
	MOVEI	T4,.CMTXT	;GET THE TEXT BLOCK TYPE
	MOVEM	T4,ARG.HD(S1)	;SAVE IT IN THE MESSAGE
	MOVEI	T4,ARG.DA(S1)	;POINT TO DATA AREA
	MOVEM	T4,.JLDAA(R)	;SAVE THE START DATA ADDRESS
	MOVE	S1,.JLPAG(R)	;GET THE MESSAGE START ADDRESS
	SUB	S1,T4		;CALC NEG. NUMBER OF WORDS USED
	ADDI	S1,^D512-^D75	;CALC NUMBER OF WORDS LEFT
	IMULI	S1,5		;CALC NUMBER OF BYTES LEFT
	MOVEM	S1,.JLBCT(R)	;AND SAVE IT
	SETZM	.JLNRM(R)	;RESET NO MORE ROOM FLAG
	HRLI	T4,(POINT 7)	;GEN THE BYTE POINTER
	MOVEM	T4,.JLBPT(R)	;AND SAVE IT
	$RETT			;RETURN
SUBTTL 	SETHDR - Routine to insert the message header

;Here with
;	S1/	Adrs of free slot in message
;	T3/	Adrs of ASCIZ string
;Returns
;	display block into message
;	S1	points to new first free location in message

SETHDR:	$SAVE	<P1>		;PRESERVE A REG
	MOVE	S2,.JLPAG(R)	;GET THE MESSAGE ADDRESS
	AOS	.OARGC(S2)	;ALSO BUMP THE BLOCK COUNT BY 1
	MOVX	P1,.ORDSP	;GET BLOCK TYPE
	STORE	P1,ARG.HD(S1),AR.TYP ;SAVE IT IN THE MSG
	MOVE	P1,G$NOW	;GET THE TIME
	MOVEM	P1,ARG.DA(S1)	;SAVE TIME STAMP
	MOVEI	P1,ARG.DA+1(S1)	;POINT TO BLOCK DATA AREA
	HRLI	P1,(POINT 7)	;MAKE A BYTE POINTER OF IT
	MOVEM	P1,.JLBPT(R)	;SAVE FOR TEXT OUTPUT ROUTINE
	$TEXT	(DEPBYT,<^T/0(T3)/^A>) ;DUMP THE HEAD INTO THE MESSAGE
	HRRZ	P1,.JLBPT(R)	;GET LAST ADRS USED
	SUBI	P1,-1(S1)	;FIGURE LENGTH OF THIS BLOCK
	STORE	P1,ARG.HD(S1),AR.LEN ;MARK LENGTH OF THIS BLOCK
	ADDI	S1,0(P1)	;POINT TO NEXT SLOT AFTER THIS BLOCK
	MOVSS	P1		;LENGTH TO LEFT HALF
	ADDM	P1,.MSTYP(S2)	;UPDATE MESSAGE LENGTH, TOO
	$RETT
SUBTTL	SENDIT - End-of-message processing routine

SNDMSG:	MOVX	S1,WT.MOR	;GET THE MORE PAGES COMMING BIT
	MOVE	S2,.JLPAG(R)	;GET THE MESSAGE ADDRESS
	IORM	S1,.OFLAG(S2)	;LIGHT THE BIT

SENDIT:	HRRZ	S1,.JLBPT(R)	;GET FINAL MESSAGE ADDRESS
	SUB	S1,.JLDAA(R)	;SUBTRACT THE START ADDRESS
	ADDI	S1,2		;ADD THE HEADER LENGTH+1
	MOVSS	S1		;SHIFT RIGHT TO LEFT
	MOVE	S2,.JLDAA(R)	;GET THE BLOCK DATA START ADDRESS
	ADDM	S1,-1(S2)	;BUMP TEXT BLOCK LENGTH
	ADDM	S1,@.JLPAG(R)	;BUMP TOTAL MSG LENGTH
	MOVE	S1,.JLPID(R)	;GET SENDER'S PID
	MOVEM	S1,G$SAB+SAB.PD ;AND SAVE IT
	MOVEI	S1,QSRPIB	;POINT AT PIB FOR SENDING IN BEHALF OF QUASAR
	MOVEM	S1,G$SAB+SAB.PB	;TELL GLXIPC
	MOVX	S1,SF.PRV	;IN YOUR BEHALF OF SENDS REQUIRE PRIVILEGES
	MOVEM	S1,G$SAB+SAB.FL	; SO LET GLXIPC KNOW
	MOVX	S1,PAGSIZ	;LENGTH OF THE PACKET
	MOVEM	S1,G$SAB+SAB.LN	;SAVE IT
	MOVE	S1,.JLPAG(R)	;ADDRESS OF THE PACKET
	MOVEM	S1,G$SAB+SAB.MS	;SAVE IT
	SETZM	G$SAB+SAB.SI	;NO SPECIAL PID INDEX
	PUSHJ	P,SEND.0	;SEND IT OFF
	SETZM	G$SAB+SAB.MS	;ZERO THE SAB MSG ADDRESS
	SETZM	G$SAB+SAB.PB	;NOT IN QUASAR'S BEHALF ANY MORE
	SETZM	G$SAB+SAB.FL	;NO FLAGS EITHER
	$RETT			;RETURN
SUBTTL	Listing output routines


DEPBYT:	IDPB	S1,.JLBPT(R)	;PUT THE BYTE INTO THE MESSAGE
	SOSG	.JLBCT(R)	;CHECK THE BYTES REMAINING
	SETOM	.JLNRM(R)	;NO MORE ROOM,,TURN ON FLAG
	SETZM	.JLCRF(R)	;CLEAR THE CRLF FLAG
	$RETT			;RETURN


PAGOVF:	PUSHJ	P,SNDMSG	;SEND THE MESSAGE OFF
	SETZ	S1,		;INDICATE WE DONT HAVE ANY HEADER
	PUSHJ	P,SETPAG	;GO SET UP A NEW OUTPUT PAGE
	$RETT			;AND RETURN

CRLF:	MOVEI	S1,[BYTE(7) .CHCRT,.CHLFD,0] ;GET THE CRLF
	PUSHJ	P,ASCOUT	;DUMP IT OUT
	SETOM	.JLCRF(R)	;SAY LAST THING OUT WAS CRLF
	$RETT			;AND RETURN

ASCOUI:	PUSH	P,S1		;SAVE S1
	HRRZ	S1,@-1(P)	;GET THE ADRS OF THE MESSAGE
	AOS	-1(P)		;SKIP OVER THE ARG POINTER
	PUSHJ	P,ASCOUT	;DUMP IT OUT
	POP	P,S1		;RESTORE S1
	$RETT			;AND WIN

ASCOUT:	PUSHJ	P,.SAVE1	;SAVE P1
	MOVE	P1,S1		;SAVE THE INPUT ADDRESS
	HRLI	P1,(POINT 7)	;MAKE IT A BYTE POINTER
ASCO.1:	ILDB	S1,P1		;GET A BYTE
	JUMPE	S1,.RETT	;DONE,,RETURN
	PUSHJ	P,DEPBYT	;PUT IT OUT
	JRST	ASCO.1		;AND DO ANOTHER


CHKSPC:	ADD	S1,T3		;ADD FIELD LENGTH AND LAST BYTE ADDRESS
	CAMG	S1,.JLBCT(R)	;IS THERE ROOM FOR THE FIELD ???
	$RETT			;YES,,RETURN
	PUSHJ	P,CRLF		;INSERT A CRLF
	$ASCII	(<	>)	;INSERT A TAB
	SETOM	.JLCRF(R)	;INDICATE BEGINNING OF LINE
	MOVE	T3,.JLBCT(R)	;GET THE BYTE COUNT
	SUBI	T3,^D64		;GET NEW LINE END ADDRESS
	$RETT			;AND RETURN


CHKLIN:	MOVE	S1,.JLBCT(R)	;GET THE CURRENT BYTE COUNT FOR OUTPUT PAGE
	SUBI	S1,^D64		;SUBTRACT A "STANDARD" LINE
	SKIPG	S1		;MORE ROOM LEFT?
	PUSHJ	P,PAGOVF	;NO, GO SET UP NEXT PAGE
	$RET			;CONTINUE
SUBTTL	Error handling routines


;Error routines are branched to via a JSP TF,xxxERR so we can
;print the PC of the caller (as if that would help).  Device-
;dependent error code should have already been printed by the
;driver module.

LENERR:	MOVEI	S1,[ASCIZ |message length error|]
	JRST	ALLERR		;JOIN COMMON CODE

ALLERR:	$WTO	(<^T/G$NAM/ ^T/0(S1)/ at PC ^O/TF,RHMASK/>,,.JQOBJ(R))
	PJRST	ABORTJ		;ABORT THE JOB
SUBTTL	UUO error reporting


UUOERR::MOVEM	S1,ERRCOD	;SAVE ERROR CODE
	SUBI	S2,2		;POINT TO THE OFFENDING UUO
	HRRZM	S2,ERRPC	;SAVE ERROR PC
	SKIPE	S1,R		;STREAM RELOCATION SETUP?
	MOVE	S1,.JNIOV+.IOUUE(R) ;GET TRANSLATION TABLE
	MOVEM	S1,ERRTBL	;SAVE
	PUSHJ	P,JOBIDN	;GENERATE JOB ID TEXT
	MOVE	S1,@ERRPC	;GET UUO IN QUESTION
	TDZ	S1,[Z 17, @UU.PHY(17)] ;ZAP AC, INDIRECTION, INDEX, PHYSICAL
	MOVE	S2,[IFIW UUOTAB] ;TABLE POINTER

UUOER1:	SKIPN	0(S2)		;END OF TABLE?
	JRST	UUOER2		;YES
	CAMN	S1,0(S2)	;MATCH?
	JRST	UUOER3		;YES
	ADDI	S2,3		;ACCOUNT FOR MULTI-WORD ENTRIES
	JRST	UUOER1		;LOOP

UUOER2:	SETZ	S2,		;ZERO
	EXCH	S2,ERRTBL	;TRY TABLE FROM DRIVER
	JUMPN	S2,UUOER1	;LOOP BACK
	MOVE	S2,ERRPC	;GET PC
	$TEXT	(<-1,,ERRBUF>,<Unknown UUO ^O12R0/S1/ at PC ^O6R0/S2/^M^J^0>)
	JRST	UUOER6		;FINISH UP

UUOER3:	MOVE	S1,1(S2)	;GET SIXBIT NAME
	MOVEM	S1,ERRNAM	;SAVE
	MOVE	S2,2(S2)	;PICK UP UUO TRANSLATION TABLE
	MOVE	S1,ERRCOD	;AND THE ORIGINAL ERROR CODE

UUOER4:	HLRZ	TF,(S2)		;GET AN ERROR CODE
	CAIN	TF,(S1)		;MATCH?
	JRST	UUOER5		;YES
	AOBJN	S2,UUOER4	;LOOP THROUGH TABLE
	MOVEI	S2,[[ASCIZ /Unknown error code/]]

UUOER5:	MOVE	S1,ERRCOD	;GET ERROR CODE
	HRRZ	S2,(S2)		;GET ERROR TEXT
	$TEXT	(<-1,,ERRBUF>,<^I/UUOETX/^M^J^0>)

UUOER6:	MOVEI	S1,ERRBUF	;POINT TO RETURNED TEXT
	POPJ	P,		;RETURN


UUOETX:	ITEXT	(<^W/ERRNAM/ UUO error ^O/S1/ at PC ^O6R0/ERRPC/; ^T/(S2)/>)

UUOTAB:	UUO	(<PISYS.>,PISYSL,PISYST)
	UUO	(<PITMR.>,PITMRL,PITMRT)
UUOLEN==.-UUOTAB
;PISYS. UUO
PISYST:	PSTMA%,,[ASCIZ /Too many arguments/]
	PSNFS%,,[ASCIZ /No function supplied/]
	PSUKF%,,[ASCIZ /Unknown function requested/]
	PSOOF%,,[ASCIZ /On and off in same function/]
	PSUKC%,,[ASCIZ /Unknown condition or device requested/]
	PSDNO%,,[ASCIZ /Device not open/]
	PSPRV%,,[ASCIZ /Privilege failure/]
	PSIVO%,,[ASCIZ /Invalid vector offset/]
	PSUKR%,,[ASCIZ /Unknown reason enabled/]
	PSPTL%,,[ASCIZ /Priority too large/]
	PSNRW%,,[ASCIZ /Non-zero reserved word/]
	PSPND%,,[ASCIZ /PIINI. not done/]
	PSARF%,,[ASCIZ /Add and remove in same function/]
PISYSL==.-PISYST


;PITMR. UUO ERRORS
PITMRT:	PSTNE%,,[ASCIZ /Timer not enabled/]
	PSUFB%,,[ASCIZ /Unknown function bit/]
PITMRL==.-PITMRT
SUBTTL	Miscellaneous routines -- JOBIDN - Generate job identifier


JOBIDN::PUSHJ	P,.SAVET	;SAVE SOME ACS
	MOVEI	T1,@.JNIOV+.IONAM(R) ;NETWORK NAME
	MOVE	T2,.JBNOD(R)	;NODE NAME/NUMBER
	MOVE	T4,.JBWSC(R)	;GET WAIT STATE CODE
	HLLZ	T3,WSTABL(T4)	;GET ASSOCIATED CODE
	HRRZ	T4,WSTABL(T4)	;AND TEXT
	$TEXT	(<-1,,.JBIDN(R)>,<^T/(T1)/ node: ^N/T2/  Stream state: ^W/T3/ (^T/(T4)/)>)
	POPJ	P,		;RETURN


DEFINE	X	(NAM,TXT),<XWD	''NAM'',[ASCIZ |'TXT|]>
WSTABL:	WSTATE
SUBTTL	Miscellaneous routines -- SETxBF - Setup buffer pointers


;INPUT BUFFER
SETIBF::MOVSI	S1,(BF.VBR)		;VIRGIN BUFFER BIT
	HRRI	S1,.JIIBF+.BFHDR(R)	;ADDRESS OF BUFFER
	MOVEM	S1,.JIIBR+.BFADR(R)	;STORE IN RING HEADER
	MOVSI	S1,(POINT 8,)		;8 BIT BYTES
	HRRI	S1,.JIIBF+.BFCNT+1(R)	;ADDRESS OF FIRST DATA WORD
	MOVEM	S1,.JIIBR+.BFPTR(R)	;STORE IN RING HEADER
	MOVSI	S1,BFSWRD-2		;DATA WORDS IN A BUFFER
	HRRI	S1,.JIIBF+.BFHDR(R)	;ADDRESS OF BUFFER
	MOVEM	S1,.JIIBF+.BFHDR(R)	;RING LOOPS ON ITSELF
	SETZM	.JIIBF+.BFCTR(R)	;NOTHING AVAILABLE YET
	POPJ	P,			;RETURN


;OUTPUT BUFFER
SETOBF::MOVSI	S1,(BF.VBR)		;VIRGIN BUFFER BIT
	HRRI	S1,.JIOBF+.BFHDR(R)	;ADDRESS OF BUFFER
	MOVEM	S1,.JIOBR+.BFADR(R)	;STORE IN RING HEADER
	MOVSI	S1,(POINT 8,)		;8 BIT BYTES
	HRRI	S1,.JIOBF+.BFCNT+1(R)	;ADDRESS OF FIRST DATA WORD
	MOVEM	S1,.JIOBR+.BFPTR(R)	;STORE IN RING HEADER
	MOVSI	S1,BFSWRD-2		;DATA WORDS IN A BUFFER
	MOVSM	S1,.JIOBR+.BFCTR(R)	;STORE IN RING HEADER
	HRRI	S1,.JIOBF+.BFHDR(R)	;ADDRESS OF BUFFER
	MOVEM	S1,.JIOBF+.BFHDR(R)	;RING LOOPS ON ITSELF
	POPJ	P,			;RETURN


;RESET OUTPUT BUFFER POINTERS
INIOBF::MOVX	S1,BFSBYT	;INITIALIZE OUTPUT BYTE COUNT
	MOVEM	S1,.JIOBC(R)
	MOVEI	S1,.JIOBF(R)	;ADDRESS OF OUTPUT BUFFER
	HRLI	S1,(POINT 8)	;8-BIT BYTES
	MOVEM	S1,.JIOBP(R)	;INITIALIZE OUTPUT BYTE POINTER
	POPJ	P,		;RETURN
SUBTTL	Initialization blocks


;GLXLIB INITIALIZATION BLOCK
IB:	$BUILD	(IB.SZ)			;SIZE OF BLOCK
	  $SET	(IB.PRG,FWMASK,%%.MOD)	;PROGRAM NAME
	  $SET	(IB.FLG,IP.STP,0)	;SEND STOPCODES TO ORION
	  $SET	(IB.PIB,FWMASK,PIB)	;ADDRESS OF PIB
	  $SET	(IB.INT,FWMASK,VECTOR)	;ADDRESS OF PSI VECTORS
	$EOB				;END OF BLOCK


;PID INITIALIZATION BLOCK
PIB:	$BUILD	(PB.MNS)		;SIZE OF BLOCK
	  $SET	(PB.HDR,PB.LEN,PB.MNS)	;LENGTH OF THIS BLOCK
	  $SET	(PB.FLG,IP.PSI,1)	;USE PSI FOR IPCF
	  $SET	(PB.FLG,IP.RSE,1)	;RETURN ON SEND FAILURES
	  $SET	(PB.INT,IP.CHN,<IPCVEC-VECTOR>) ;OFFSET TO IPCF INTRUPT BLOCK
	  $SET	(PB.SYS,IP.SQT,^D511)	;INFINITE SEND QUOTA
	  $SET	(PB.SYS,IP.RQT,^D511)	;INFINITE RECEIVE QUOTA
	$EOB

;DUMMY PIB FOR SENDING VIA QUASAR'S PID
QSRPIB:	$BUILD	(PB.MNS)
	$EOB


;QUASAR INITIALIZATION BLOCK
HELLO:	$BUILD	HEL.SZ
	  $SET	(.MSTYP,MS.TYP,.QOHEL)	;MESSAGE TYPE
	  $SET	(.MSTYP,MS.CNT,HEL.SZ)	;MESSAGE LENGTH
	  $SET	(HEL.NM,,%%.MOD)	;PROGRAM NAME
	  $SET	(HEL.FL,HEFVER,%%.QSR)	;QUASAR VERSION
	  $SET	(HEL.NO,HENNOT,2)	;NUMBER OF OBJECT TYPES
	  $SET	(HEL.NO,HENMAX,PRCN)	;MAX NUMBER OF JOBS
	  $SET	(HEL.OB+0,HELOBJ,.OTNQC);NETWORK QUEUE CONTROLLER
	  $SET	(HEL.OB+0,HELATR,%NQINP);INPUT (FROM REMOTE)
	  $SET	(HEL.OB+1,HELOBJ,.OTNQC);NETWORK QUEUE CONTROLLER
	  $SET	(HEL.OB+1,HELATR,%NQOUT);OUTPUT (TO REMOTE)
	$EOB


	.LNKEN	IOLNK,NETIOV
NETIOV:	XWD	0,0		;NETWORK I/O DRIVER CHAIN
SUBTTL	Forms type file descriptor blocks


FTYFD:	$BUILD	(FDMSIZ)		;SHORT FILESPEC BLOCK
	  $SET	(.FDLEN,FD.LEN,FDMSIZ)	;LENGTH
	  $SET	(.FDSTR,,'SYS   ')	;DEVICE
	  $SET	(.FDNAM,,'FORMST')	;NAME
	  $SET	(.FDEXT,,'DAT   ')	;EXTENSION
	$EOB

CHRFD:	$BUILD	(FDMSIZ)		;SHORT FILESPEC BLOCK
	  $SET	(.FDLEN,FD.LEN,FDMSIZ)	;LENGTH
	  $SET	(.FDSTR,,'SYS   ')	;DEVICE
	  $SET	(.FDNAM,,'CHARTY')	;NAME
	  $SET	(.FDEXT,,'DAT   ')	;EXTENSION
	$EOB

FTYFOB:	$BUILD	(FOB.SZ)		;FILE OPEN BLOCK
	  $SET	(FOB.CW,FB.BSZ,7)	;BYTE SIZE (ASCII)
	$EOB
SUBTTL	Message byte pointers


;P.Ixxx are for incoming messages, P.Oxxx are for outgoing messages

P.ITYP:	BYTPNT	(.HDTYP,.JIIBF(R))	;TYPE
P.OTYP:	BYTPNT	(.HDTYP,.JIOBF(R))
P.IFLG:	BYTPNT	(.HDFLG,.JIIBF(R))	;FLAGS
P.OFLG:	BYTPNT	(.HDFLG,.JIOBF(R))
P.ICTX:	BYTPNT	(.HDCTX,.JIIBF(R))	;CONTEXT
P.OCTX:	BYTPNT	(.HDCTX,.JIOBF(R))
P.IBLK:	BYTPNT	(.HDBLK,.JIIBF(R))	;NUMBER OF BLOCKS
P.OBLK:	BYTPNT	(.HDBLK,.JIOBF(R))
P.ILNH:	BYTPNT	(.HDLNH,.JIIBF(R))	;HIGH BYTE OF MESSAGE LENGTH
P.OLNH:	BYTPNT	(.HDLNH,.JIOBF(R))
P.ILNL:	BYTPNT	(.HDLNL,.JIIBF(R))	;LOW BYTE OF MESSAGE LENGTH
P.OLNL:	BYTPNT	(.HDLNL,.JIOBF(R))
SUBTTL	Tempoary storage for listing


;Only used when listing one message - doesn't need to be in stream database

LISBEG:!
LISJNB:	BLOCK	1		;JOB NUMBER
LISNAM:	BLOCK	10		;JOB NAME
LISOWN:	BLOCK	10		;JOB OWNER
LISSTS:	BLOCK	1		;STATE
LISPGL:	BLOCK	1		;PAGE LIMIT
LISPRI:	BLOCK	1		;PRIORITY
LISFRM:	BLOCK	1		;FORMS NAME
LISNOT:	BLOCK	10		;NOTE
LISQNM:	BLOCK	QNMLEN		;QUEUE NAME
LISEND==.-1
SUBTTL	Global data storage


G$ADR::	BLOCK	JOBN		;JOB DATA BASE ADDRESS TABLE
G$PGM::	BLOCK	1		;OUR PROGRAM NAME
G$NAM::	BLOCK	20		;OUR PROCESS NAME
G$HNAM::BLOCK	1		;HOST NODE NAME
G$HNBR::BLOCK	1		;HOST NODE NUMBER
G$QPPN::BLOCK	1		;QUEUE PPN
G$QPRT::BLOCK	1		;SPOOLED FILE PROTECTION

G$SAB::	BLOCK	SAB.SZ		;IPCF SEND ARGUMENT BLOCK
G$MSG::	BLOCK	PAGSIZ+1	;IPCF MESSAGE STORAGE
G$SND:: BLOCK	1		;SENDER'S PID
G$SID:: BLOCK	1		;SENDER'S ID
G$PRV:: BLOCK	1		;SENDER'S PRIVS
G$FLG:: BLOCK	1		;IPCF RECEIVE FLAGS
G$IDX:: BLOCK	1		;SENDER'S SPECIAL PID INDEX
G$ACK:: BLOCK	1		;NON-ZERO IF SENDER WANTS AN ACK
G$COD:: BLOCK	1		;ACK CODE
G$NOW::	BLOCK	1		;"NOW"
G$CLNC::BLOCK	1		;-1 IF NEED TO CALL M%CLNC
SUBTTL	Local data storage


PDL:	BLOCK	PDLMSZ		;MAIN PUSH DOWN LIST
CONFIG:	BLOCK	5		;MONITOR NAME

VECTOR:!			;START OF PSI VECTORS
TIMVEC:	BLOCK	4		;TIMER TRAP VECTOR
IPCVEC:	BLOCK	4		;IPCF INTERRUPT VECTOR
GLBVEC:	BLOCK	4*GLBPSV	;SPARE GLOBAL PSI VECTORS
JOBVEC: BLOCK	JOBPSV*<4*JOBN>	;STREAM VECTORS

IPCQUE:	BLOCK	1		;IPCF RESEND QUEUE
RSENDC:	BLOCK	1		;COUNT OF RESENDS NEEDED
MSGLEN:	BLOCK	1		;REQUESTED MESSAGE LENGTH
MSGBLK:	BLOCK	1		;ADDRESS OF CURRENT BLOCK IN MESSAGE
MSGCNT:	BLOCK	1		;COUNT OF MESSAGE BLOCKS TO PROCESS

BLKCNT:	BLOCK	1		;NUMBER OF BLOCKS IN MESSAGE BEING BUILT

CLKTIC:	BLOCK	1		;TIMER HAS GONE OFF
CLKTIM:	BLOCK	1		;CURRENT DATE/TIME
CLKNEW:	BLOCK	1		;NEW TIME INTERVAL
CURJOB:	BLOCK	1		;AOBJN POINTER FOR CURRENT JOB
NEWJOB:	BLOCK	1		;AOBJN POINTER FOR NEW JOB
RUNCNT:	BLOCK	1		;COUNT OF JOBS RUN LAST SCHEDULER PASS

ERRBEG:!
ERRTBL:	BLOCK	1		;UUO ERROR FLAG FOR DRIVER UUOS
ERRPC:	BLOCK	1		;UUO ERROR PC
ERRCOD:	BLOCK	1		;UUO ERROR CODE
ERRLVL:	BLOCK	1		;ERROR LEVEL
ERRCLS:	BLOCK	1		;ERROR CLASS
ERRNAM:	BLOCK	1		;UUO OPCODE NAME
ERRBUF:	BLOCK	30		;UUO ERROR TEXT BUFFER
ERREND==.-1

TMPDPB:	IDPB	S1,TMPPTR	;STUFF THE BYTE
	$RETT
TMPPTR:	BLOCK	1		;BYTE POINTER INTO TEXT STRING BUFFER
TMPBUF:	BLOCK	30		;TEMPORARY BUFFER FOR BUILDING TEXT STRINGS

	END	NEBULA		;A GOOD PLACE TO START