Google
 

Trailing-Edge - PDP-10 Archives - AP-D483B-SB_1978 - qsrque.mac
There are 44 other files named qsrque.mac in the archive. Click here to see a list.
TITLE	QSRQUE  --  Batch Queue Message Handlers for QUASAR
SUBTTL	Larry Samberg  Chuck O'Toole /CER 6 Jan 77

;***Copyright (C) 1974, 1975, 1976, 1977, Digital Equipment Corp., Maynard, MA.***

	SEARCH	QSRMAC		;PARAMETER FILE

	PROLOGUE(QSRQUE)	;GENERATE THE NECESSARY SYMBOLS


COMMENT\

	STOPCDs found in QSRQUE

BDN	BAD DEVICE NAME INTERNALLY GENERATED
CRD	CREATE REJECTED DEFER DATA
CRL	CREATE REJECTED LOGOUT DATA
CRM	CREATE REJECTED MODIFY
CRS	CREATE REJECTED SPOOLING DATA
NBR	NEXTJOB'ING BAD REQUEST

\
SUBTTL	Queue Headers and Module Storage

	INTERN	TBLHDR
	INTERN	NQUEUE

;BUILD THE QUEUE HEADERS

TBLHDR:	QUEHDR
	NQUEUE==<.-TBLHDR>/QHSIZE

LSTITN:	BLOCK	1			;LAST ITN ASSIGNED
LISSIZ:	BLOCK	1			;NUMBER OF PAGES NEEDED FOR LAST
					; COMPLETE QUEUE LISTING
THSPSB:	BLOCK	1			;ADDRESS OF THE PSB OF THE
					;SENDER OF THE CURRENT MESSAGE
					;FILLED IN BY VALMSG
MSGPDB:	BLOCK	IPCHSZ			;PDB SPACE FOR "NEXT", "ABORT",
					; AND "LISTANSWER"

MSGMSG:	BLOCK	ABO.SZ			;RESERVE THE SPACE FOR THE MESSAGES
SUBTTL	Queue Database Initialization

Q$INIT::PUSHJ	P,I$NOW##		;GET NOW
	MOVEM	S1,LSTITN		;AND SAVE IT AS LAST ITN
	ZERO	LISSIZ			;START WITH A CLEAN SLATE
	POPJ	P,			;RETURN
SUBTTL	Batch and Spooling Message Handlers

;THE MESSAGE HANDLERS ARE TOP LEVEL ROUTINES WHICH PROCESS THE
;	VARIOUS MESSAGES THAT ARE SENT TO QUASAR.  THEY ARE
;	CALLED DIRECTLY OUT OF THE MAIN PROCESSING LOOP WITH
;	ACCUMULATOR "M" POINTING TO THE FIRST WORD OF THE MESSAGE.
;	THE MESSAGE HANDLERS HAVE FULL USE OF ALL ACCUMULATORS
;	EXCEPTING "M" AND THE "P" REGS.

	INTERN	Q$RELEASE	;FUNCTION 2  --  RELEASE
	INTERN	Q$CHECKPOINT	;FUNCTION 3  --  CHECKPOINT
	INTERN	Q$REQUEUE	;FUNCTION 4  --  REQUEUE
	INTERN	Q$NEXTJOB	;FUNCTION 5  --  NEXTJOB(*)
	INTERN	Q$ABORT		;FUNCTION 6  --  ABORT(*)
	INTERN	Q$CREATE	;FUNCTION 7  --  CREATE
	INTERN	Q$LIST		;FUNCTION 10 --  LIST
	INTERN	Q$MODIFY	;FUNCTION 11 --  MODIFY
	INTERN	Q$KILL		;FUNCTION 12 --  KILL
	INTERN	Q$LANSWER	;FUNCTION 13 --  LISTANSWER(*)
				;FUNCTION 14 --  TEXT(*)
	INTERN	Q$DEFER		;FUNCTION 16 --  DEFER

;SOME IPCC MESSAGES ARE SENT TO QUASAR FROM THE MONITOR EXEC PROCESS.
;	THEY ARE TREATED IN THE SAME MANNER AS USER GENERATED
;	CALLS (I.E. ACCUMULATOR "M" POINTS TO THE MESSAGE)

	INTERN	Q$SPOOL		;SPOOLING FILE REQUEST -- FUNCTION .IPCSU (26)
	INTERN	Q$LOGOUT	;LOGOUT OF A JOB       -- FUNCTION .IPCSL (27)

;(*) NEXTJOB, ABORT, LISTANSWER, AND TEXT ARE SENT BY QUASAR
;	THESE ARE NOT CALLED FROM THE MAIN LOOP BUT RATHER:
;
;	NEXTJOB   IS CALLED FROM THE SCHEDULING ROUTINES
;	ABORT	  IS CALLED FROM THE KILL FUNCTION
;	LANSWER	  IS CALLED FROM THE LIST FUNCTION
;	TEXT	  IS HANDLED BY THE MAIN LOOP ITSELF
;
;IF .QIFNC IS SET IN THE OPERATION FIELD OF A MESSAGE, THE CALL IS
;	CONSIDERED INTERNAL, AND SPECIAL HANDLING OR INTERPRETATION
;	OF THE VARIOUS FIELDS IN THE MESSAGE, (PARTICULARLY THE
;	OTHER 17 BITS OF THE TYPE FIELD) MAY OCCUR.  ANY SPECIAL
;	HANDLING OF THIS FORM WILL BE DESCRIBED IN THE ROUTINE
;	HEADER COMMENTS.  IF .QIFNC IS SET THE REST OF THE TYPE FIELD
;	DOES NOT HAVE TO REFLECT THE MESSAGE TYPE SINCE IF Q$CREATE
;	RECEIVES AN INTERNAL CALL, THE MESSAGE IS OBVIOUSLY A CREATE
;	MESSAGE.
SUBTTL	RELEASE  --  Function 2

;THE RELEASE MESSAGE IS SENT TO QUASAR BY ONE OF THE KNOWN SYSTEM
;	COMPONENTS TO RELEASE A JOB FROM THE QUEUE.

Q$RELEASE:
	MOVEI	T1,REL.SZ		;LOAD MINIMUM SIZE
	PUSHJ	P,VALMSG		;VALIDATE THE MESSAGE
	SKIPE	G$ERR##			;DID VALMSG SET THE ERROR FLAGS
	  POPJ	P,			;YES, REJECT THIS MESSAGE
	MOVE	T1,REL.IT(M)		;GET THE TASKS ITN
	PUSHJ	P,SRHUSE		;SEARCH THE USE QUEUE
	PJUMPE	T1,E$SNY##		;NOT FOUND
	MOVE	AP,T1			;COPY ENTRY ADR INTO AP
	MOVE	T1,G$SND##		;GET PID OF SENDER
	CAME	T1,.QEPID(AP)		;DOES HE REALLY OWN THE REQ?
	  PJRST	E$SNY##			;NO, GIVE "NOT YOURS" ERROR
	MOVE	T1,THSPSB		;GET ADDRESS OF HIS PSB
	PUSHJ	P,S$RUSE##		;TELL SCHED ABOUT REMOVAL
	PUSH	P,AP			;SAVE AP FIRST
	PUSHJ	P,S$RELE##		;RELEASE THE REQUEST
	POP	P,AP			;RESTORE REQUEST ADDRESS
	LOAD	S1,.QESTN(AP),QE.DPA	;GET THE DISK ADDRESS
	PUSHJ	P,F$RLRQ##		;AND RELS SPACE
	$COUNT	(MREL)
	MOVEI	H,HDRUSE		;LOAD ADR OF USE HEADER
	PJRST	M$RFRE##		;AND RETURN THE ENTRY
SUBTTL	CHECKPOINT  --  Function 3

;A CHECKPOINT MESSAGE IS SENT PERIODICALLY BY THE VARIOUS
;	KNOWN PROCESSORS.  THE CHECKPOINT OPERATIONS CONSISTS
;	OF UPDATING THE INCORE AND DISK ENTRIES WITH THE
;	CHECKPOINT INFORMATION PROVIDED.

Q$CHECKPOINT:
	MOVEI	T1,CHE.SZ		;LOAD MINIMUM MESSAGE SIZE
	PUSHJ	P,VALMSG		;VALIDATE THE MESSAGE
	SKIPE	G$ERR##			;DID VALMSG SET THE ERROR FLAGS
	  POPJ	P,			;YES, REJECT THIS MESSAGE
	MOVE	T1,CHE.IT(M)		;GET THE SPECIFIED ITN
	PUSHJ	P,SRHUSE		;SEARCH THE USE QUEUE
	PJUMPE	T1,E$SNY##		;NOT THERE!!
	MOVE	T2,G$SND##		;GET PID OF SENDER
	CAME	T2,.QEPID(T1)		;DOES HE OWN IT?
	  PJRST	E$SNY##			;NO, GIVE AN ERROR
	LOAD	S1,.QESTN(T1),QE.DPA	;GET THE DPA
	PUSHJ	P,F$RDRQ##		;READ THE REQUEST
	MOVE	AP,S1			;SAVE PAGE ADR IN AP
	HRRI	S2,.EQCHK(S1)		;AND PLACE TO PUT IT
	HRLI	S2,CHE.IN(M)		;FIRST OF THE INFORMATION WORDS
	BLT	S2,.EQCHK+<EQCKSZ-1>(S1)  ;AND BLT IT
	PUSHJ	P,F$WRRQ##		;WRITE IT OUT
	LOAD	S2,.QESTN(T1),QE.DPA	;GET PREVIOUS DPA
	STORE	S1,.QESTN(T1),QE.DPA	;STORE NEW DPA
	MOVE	S1,S2			;GET OLD DPA INTO S1
	PUSHJ	P,F$RLRQ##		;AND RELEASE IT
	$COUNT	(MCHK)
	ADR2PG	AP			;MAKE A PAGE NUMBER
	PJRST	M$RELP##		;RELEASE THE PAGE AND RETURN
SUBTTL	REQUEUE  --  Function 4

;THE REQUEUE FUNCTION IS SENT BY A KNOWN COMPONENT USUALLY
;	DUE TO OPERATOR REQUEST.  THE REQUEUE MESSAGE CONTAINS
;	THE SAME CHECKPOINT INFORMATION BUT HAS AN AFTER PARAMETER.
;
;REQUEUE PERFORMS THE FOLLOWING FUNCTIONS:
;	1)  CHECKPOINT THE ENTRY
;	2)  MOVE THE ENTRY FROM THE USE QUEUE TO THE 
;	    APPROPRIATE PROCESSING QUEUE
;	3)  RELEASE THE JOB FROM THE PSB

Q$REQUEUE:
	MOVEI	T1,REQ.SZ		;MINIMUM SIZE OF THE MESSAGE
	PUSHJ	P,VALMSG		;VALIDATE THE SIZE OF IT
	SKIPE	G$ERR##			;IS IT OK
	  POPJ	P,			;NO, RETURN NOW
	PUSHJ	P,Q$CHECKPOINT		;DO A CHECKPOINT FIRST
	SKIPE	G$ERR##			;DID CHECKPOINT REJECT THE REQUEST
	  POPJ	P,			;YES, SO WILL I
	MOVE	T1,REQ.IT(M)		;GET THE ITN OF THE TASK
	PUSHJ	P,SRHUSE		;LOCATE IN THE USE QUEUE
	MOVE	AP,T1			;SAVE FOR EVENTUAL MOVE
	ZERO	.QEPID(AP)		;CLEAR THE INTERLOCK
	LOAD	H,.QESTN(AP),QE.RQP	;GET RELATIVE QUEUE POINTER
	ADDI	H,TBLHDR		;AND MAKE ADDRESS OF QUE HDR
	SKIPN	S1,REQ.AF(M)		;IS THERE A /AFTER PARAMETER?
	JRST	REQU.1			;NO, CONTINUE NORMALLY
	MOVEI	H,HDRAFT		;YES, LOAD ADDRESS OF AFTER HEADER
	PUSHJ	P,I$AFT##		;GET AFTER TIME
	MOVEM	S1,.QECRE(AP)		;AND STORE IN ENTRY

REQU.1:	MOVE	S1,H			;PUT DESTINATION QUE IN S1
	MOVEI	H,HDRUSE		;LOAD H WITH SOURCE QUEUE
	PUSHJ	P,M$MOVE##		;MOVE THE ENTRY
	$COUNT	(MREQ)
	MOVE	T1,THSPSB		;LOAD THE PSB ADDRESS
	PJRST	S$RUSE##		;AND NOTIFY THE SCHEDULER
SUBTTL	NEXTJOB  --  Function 5

;NEXTJOB IS CALLED BY THE SCHEDULING ROUTINES IN QSRSCH WHEN
;	AN ENTRY IS READY TO BE SCHEDULED FOR A KNOWN SYSTEM
;	COMPONENT.
;
;NEXTJOB IS CALLED AS FOLLOWS:
;	AP = ADDRESS OF THE ENTRY IN THE PROCESSING QUEUE (EG LPT QUEUE)
;	T1 = ADDRESS OF THE PSB TO SCHEDULE IT FOR
;	H  = ADDRESS OF THE QUEUE HEADER OF THE PROCESSING QUEUE
;
;NEXTJOB PERFORMS THE FOLLOWING FUNCTIONS:
;	1)  MOVE ENTRY FROM PROCESSING QUEUE TO "USE" QUEUE
;	2)  READ THE QUEUE REQUEST FROM DISK
;	3)  INCLUDE THE CHANGABLE DATA
;	4)  SEND THE REQUEST TO THE PROCESSOR

Q$NEXTJOB:
	$COUNT	(MNXT)			;COUNT NEXTJOBS
	MOVEI	S1,HDRUSE		;LOAD DESTINATION QUEUE HDR
	PUSHJ	P,M$MOVE##		;AND MOVE THE ENTRY
	MOVE	S1,PSBPID(T1)		;PID OF THE PROCESSOR
	MOVEM	S1,.QEPID(AP)		;STORE THE INUSE NAME
	MOVE	S1,PSBPDV(T1)		;GET THE PROCESSING DEVICE
	MOVEM	S1,.QEDEV(AP)		;AND INCLUDE THE DEVICE

;NOW, READ IN THE PAGE AND SEND IT TO THE CORRECT PROCESSOR

	LOAD	S1,.QESTN(AP),QE.DPA	;GET THE DPA
	PUSHJ	P,F$RDRQ##		;READ THE REQUEST
	SKIPN	0(S1)			;DO A SMALL VALIDITY CHECK
	  STOPCD(NBR,FATAL)		;++NEXTJOB'ING BAD REQUEST
	MOVEI	T1,.QONEX		;GET NEXTJOB FUNCTION
	STORE	T1,.MSTYP(S1),MS.TYP	;AND STORE IT IN THE MESSAGE
	LOAD	T1,.QEITN(AP)		;NOW MAKE SURE ITN EXISTS
	STORE	T1,.EQITN(S1)		;STORE IT
	LOAD	T1,.QECRE(AP)		;CREATION TIME
	STORE	T1,.EQAFT(S1)		;STORE THAT AS WELL
	LOAD	T1,.QESEQ(AP),QE.SEQ	;THE SEQUENCE NUMBER
	STORE	T1,.EQSEQ(S1),EQ.SEQ	;AND THAT
	LOAD	T1,.QEPRT(AP),DV.STN	;GET STATION NUMBER FOR REQUEST
	STORE	T1,.EQSEQ(S1),EQ.DSN	;SET PROPERLY FOR BATCON
	HRLI	T1,.QELM1(AP)		;MOVE LIMIT WORDS FROM INTERNAL
	HRRI	T1,.EQLM1(S1)		;TO EXTERNAL REQUEST
	BLT	T1,.EQLM5(S1)		;FOR EXTRA DEFAULTED VALUES
	MOVX	T1,IP.CFV		;GET PAGE-MODE BIT
	MOVEM	T1,MSGPDB+.IPCFL	;PUT IT IN THE FLAG WORD
	MOVE	T1,.QEPID(AP)		;GET PROCESSORS PID
	MOVEM	T1,MSGPDB+.IPCFR	;STORE RECEIVERS PID
	ADR2PG	S1			;MAKE A PAGE NUMBER
	HRLI	S1,1000			;AND THE SIZE
	MOVEM	S1,MSGPDB+.IPCFP	;SAVE IT
	MOVEI	AP,MSGPDB		;ADDRESS OF PDB
	PJRST	C$SEND##		;SHIP THE MESSAGE AND RETURN
SUBTTL	ABORT  --  Function 6

;ABORT IS CALLED BY THE "KILL" HANDLER WHEN THE JOB IN QUESTION IS
;	IN THE USE QUEUE.  CALL "ABORT" WITH T1 POINTING TO THE
;	USE QUEUE ENTRY.  ABORT SIMPLY SENDS AN ABORT MESSAGE
;	TO THE PROCESSOR INTERLOCKING THE JOB.

Q$ABORT:
	MOVEI	AP,MSGPDB		;LOAD ADR OF THE PDB
	MOVEI	T3,MSGMSG		;LOAD ADR OF THE MESSAGE
	ZERO	.IPCFL(AP)		;NO FLAGS
	LOAD	T4,.QEPID(T1)		;GET THE PROCESSOR'S PID
	MOVEM	T4,.IPCFR(AP)		;STORE AS RECEIVER
	MOVE	T4,[ABO.SZ,,MSGMSG]	;LOAD SIZE,,ADR
	MOVEM	T4,.IPCFP(AP)		;AND STORE IT
	MOVE	T4,[ABO.SZ,,.QOABO]	;LOAD THE MESSAGE HDR
	STORE	T4,.MSTYP(T3)		;AND STORE IT
	LOAD	T4,.QEITN(T1)		;GET THE ITN
	STORE	T4,ABO.IT(T3)		;STORE IN ABORT MESSAGE
	MOVX	T4,ABOUSR		;"ABORTED BY USER"
	STORE	T4,ABO.CD(T3)		;STORE THE CODE
	LOAD	T4,G$SID##		;GET ID OF SENDER
	STORE	T4,ABO.ID(T3)		;AND STORE IN THE MESSAGE
	PJRST	C$SEND##		;AND SEND IT
SUBTTL	CREATE  --  Function 7

;THE CREATE MESSAGE IS SENT TO QUASAR BY ANY UNKNOWN COMPONENT
;	TO PLACE SOMETHING IN ONE OF THE PROCESSING QUEUES.
;IF THIS IS AN INTERNAL CALL (I.E. .QIFNC IS SET) THEN THE LOW-ORDER
;	17 BITS OF THE FUNCTION FIELD ARE TAKEN TO BE THE DPA IF THE
;	REQUEST IS ALREADY FAILSOFT, AND IF THIS FIELD IS NON-ZERO,
;	THE REQUEST IS NOT WRITTEN OUT.
Q$CREATE:
	PUSHJ	P,.SAVE3##		;SAVE P1 THRU P3
	LOAD	T1,.MSTYP(M),MS.CNT	;GET LENGTH OF MESSAGE
	CAIGE	T1,EQHSIZ		;MUST BE AT LEAST EQHSIZ
	  PJRST	E$MTS##			;INDICATE MESSAGE TOO SHORT
	LOAD	T1,.EQLEN(M),EQ.LOH	;MUST CHECK HEADER SIZE AS WELL
	CAIGE	T1,EQHSIZ		;THAT IS ALSO THE MINIMUM VALUE
	  PJRST	E$MTS##			;TOO BAD, GIVE MESSAGE TOO SHORT
	LOAD	S1,.EQRDV(M)		;GET SIXBIT DEVICE QUEUED TO
	LOAD	S2,.EQSEQ(M),EQ.DSN	;CALLERS STATION NUMBER
	PUSHJ	P,I$MIDS##		;CREATE INTERNAL DEVICE SPEC
	PJUMPE	S1,E$IFD##		;JUMP IF ILLEGALLY FORMED
	MOVE	P1,S1			;SAVE FOR LATER COPY
	PUSHJ	P,Q$FHDR		;FIND THE QUEUE HEADER
	SKIPN	H,S1			;LOAD INTO H AND SKIP
	PJRST	E$UQS##			;UNKNOWN QUEUE SPECIFIED
	LOAD	P3,.MSTYP(M),MS.TYP	;GET THE TYPE FIELD
	TXNE	P3,.QIFNC		;IS IT INTERNAL?
	 SKIPN	T1,.EQITN(M)		;YES, LOAD OLD ITN IF NON-ZERO
	  AOS	T1,LSTITN		;GET A NEW ITN
	STORE	T1,.EQITN(M)		;AND STORE IN MSG FOR LATER
	LOAD	T1,.QHPAG(H),QH.SCH	;BASE OF SCHEDULING ENTRIES
	PUSHJ	P,SCHDEF(T1)		;GO FILL THE DEFAULTS FOR THIS QUEUE
	SKIPE	G$ERR##			;DID IT GET PAST THE CHECKS
	  POPJ	P,			;NO, SCHDEF REJECTED IT!!
CREA.1:	PUSH	P,H			;SAVE "REAL" QUEUE HEADER
	MOVE	S1,.EQAFT(M)		;GET THE AFTER PARAMETER
	TXNE	P3,.QIFNC		;IS IT INTERNAL?
	  JRST	CREA.2			;YES, JUST USE WHAT WE'VE GOT
	CAMGE	S1,G$NOW##		;NO, IS IT BEFORE NOW?
	  MOVE	S1,G$NOW##		;YES, USE NOW.  THIS DISALLOWS
	MOVEM	S1,.EQAFT(M)		;/AFT:YESTERDAY TO GIVE HI-PRIO
CREA.2:	CAMLE	S1,G$NOW##		;IS IT IN THE FUTURE?
	  MOVEI	H,HDRAFT		;YES, PUT ENTRY IN AFTER QUEUE
	PUSHJ	P,M$GFRE##		;GET A FREE CELL

				;"CREATE" IS CONTINUED ON NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

	MOVE	S1,P3			;COPY THE MESSAGE TYPE
	TXZE	S1,.QIFNC		;IS IT INTERNAL?
	  JUMPN	S1,CREA.3		;YES, IF DPA EXISTS, DON'T FAILSOFT
	MOVE	S1,M			;NO, GET THE ADDRESS
	PUSHJ	P,F$WRRQ##		;AND FAILSOFT IT
CREA.3:	STORE	S1,.QESTN(AP),QE.DPA	;STORE THE DISK ADDRESS
	POP	P,S1			;GET "REAL" QUEUE HEADER
	SUBI	S1,TBLHDR		;MAKE AN RQP
	STORE	S1,.QESTN(AP),QE.RQP	;AND SAVE RQP IN ENTRY
	LOAD	P2,.EQSPC(M),EQ.NUM	;GET NUMBER OF FILES IN THE REQUEST
	LOAD	T1,.EQITN(M)		;GET THE ITN
	STORE	T1,.QEITN(AP)		;AND STORE IT
	LOAD	T1,.EQJOB(M)		;GET JOB NAME
	STORE	T1,.QEJOB(AP)		;STORE IT
	MOVE	S1,M			;POINT S1 TO THE EQ
	PUSHJ	P,I$EQQE##		;MOVE INFO FROM EQ TO QE
	LOAD	T1,.EQSEQ(M),EQ.SEQ	;GET EXT SEQ NUMBER
	STORE	T1,.QESEQ(AP),QE.SEQ	;STORE IT
	LOAD	T1,.EQSEQ(M),EQ.PRI	;GET THE PRIORITY FIELD
	STORE	T1,.QESEQ(AP),QE.PRI	;STORE IT
	LOAD	T1,.EQSEQ(M),EQ.RDE	;GET RDE FLAG
	STORE	T1,.QESEQ(AP),QE.RDE	;AND MOVE IT
	LOAD	T1,.EQSEQ(M),EQ.SPL	;GET SPL BIT
	STORE	T1,.QESEQ(AP),QE.SPL	;AND STORE IT IN THE QE
	LOAD	T1,.EQSPC(M),EQ.PRO	;GET THE PROTECTION FIELD
	STORE 	T1,.QEPRT(AP),QE.PRO	;STORE IT
	LOAD	T1,.EQDED(M)		;GET DEADLINE
	STORE	T1,.QEDED(AP)		;STORE IT
	LOAD	T1,P1,DV.DMD		;GET DEVICE MODIFIERS FROM I$MIDS
	STORE	T1,.QEPRT(AP),QE.DMD	;SAVE FOR SCHEDULER
	MOVSI	T1,.EQLM1(M)		;SOURCE OF 5 LIMIT WORDS
	HRRI	T1,.QELM1(AP)		;DESTINATION OF 5 LIMIT WORDS
	BLT	T1,.QELM5(AP)		;BLT THEM ACROSS
	LOAD	T1,.EQAFT(M)		;GET CREATE OR AFTER TIME
	STORE	T1,.QECRE(AP)		;AND STORE IT
	ZERO	.QEPID(AP)		;ZERO THE PID
	LOAD	S1,.EQLEN(M),EQ.LOH	;NOW FIND THE FIRST FILE IN THE REQUEST
	ADDI	S1,(M)			;FIND FIRST FP AREA
	LOAD	S2,.FPSIZ(S1),FP.FHD	;LENGTH OF THE FIRST FP
	ADDI	S1,(S2)			;S1 = FIRST FD AREA
	PUSHJ	P,I$FSTR##		;EXTRACT THE FILE STRUCTURE FROM THE FD
	STORE	S1,.QESTR(AP)		;STORE FOR SCHEDULER
	LOAD	T1,.QESEQ(AP),QE.RDE	;GET THE JOBS RDE BIT
	JUMPE	T1,CREA.4		;NOT RDE, CONTINUE
	MOVEI	H,HDRRDE-TBLHDR		;GET RQP FOR RDE QUEUE
	STORE	H,.QESTN(AP),QE.RQP	;STORE IT
	ADDI	H,TBLHDR		;POINT TO RDE QUEUE


				;"CREATE" IS CONTINUED ON THE NEXT PAGE
				;CONTINUED FROM THE PREVIOUS PAGE

CREA.4:	LOAD	T1,.QHPAG(H),QH.SCH	;GET BASE OF SCHED VECTOR
	PUSHJ	P,SCHLNK(T1)		;AND LINK IN THE REQUEST
	$COUNT	(SCRE)			;NUMBER OF SUCCESSFUL CREATES
	SKIPN	G$ACK##			;DOES CALLER WANT ACKNOWLEDGEMENT
	  POPJ	P,			;NO, ALL DONE
	MOVE	S1,P1			;GET IDS OF REQUEST
	PUSHJ	P,I$MSDN##		;MAKE PRINTABLE SIXBIT DEVICE
	PUSHJ	P,G$CSIX##		;PUT IT INTO THE STRING
	MOVEI	S1,":"			;ISOLATE THE QUEUE NAME
	PUSHJ	P,G$CCHR##		;NOW HAVE DEV:
	MOVE	S1,.QEJOB(AP)		;THE JOB NAME
	PUSHJ	P,G$CSIX##		;ADD THAT TO THE DEVICE
	MOVEI	S1,[ASCIZ\=/Seq:\]
	PUSHJ	P,G$CSTG##		;ADD SOME MORE TEXT
	LOAD	S1,.QESEQ(AP),QE.SEQ	;GET THE SEQUENCE NUMBER
	PUSHJ	P,G$CDEC##		;THAT IS A DECIMAL NUMBER
	LOAD	S1,.QESTN(AP),QE.RQP	;GET RELATIVE QUEUE POINTER
	LOAD	S1,TBLHDR+.QHTYP(S1),QH.TYP	;GET QUEUE TYPE
	CAIN	S1,.QHTIP		;INPUT QUEUE
	  JRST	CREA.5			;YES, GIVE ANOTHER MESSAGE
	MOVEI	S1,[ASCIZ\/Limit:\]	;TELL REQUEST LIMIT
	PUSHJ	P,G$CSTG##		;APPEND
	LOAD	S1,.QELM2(AP),QE.PGS	;GET REQUEST LIMIT IN UNITS
	PUSHJ	P,G$CDEC##		;PROBABLY OUGHT TO TRY /FEET/PAGES
	MOVEI	S1,[ASCIZ/, /]		;ALIGNMENT
	PUSHJ	P,G$CSTG##		;ADD THAT
	MOVE	S1,P2			;GET THE NUMBER OF FILES IN THE REQUEST
	MOVEI	S2,[ASCIZ/ File/]	;STRING TO PLURALIZE
	PUSHJ	P,TYPPLR		;DO THE RIGHT THING
	JRST	CREA.6			;SEND THE ACK AND RETURN


				;"CREATE" IS CONTINUED ON THE NEXT PAGE
				;CONTINUED FROM THE PREVIOUS PAGE


CREA.5:	MOVEI	S1,[ASCIZ\/Time:\]	;TIME LIMIT
	PUSHJ	P,G$CSTG##		;APPEND
	LOAD	T1,.QELM2(AP),QE.TIM	;NUMBER OF SECONDS REQUESTED
	IDIVI	T1,^D3600		;HOURS IN T1
	IDIVI	T2,^D60			;MINUTES IN T2, SECONDS IN T3
	MOVE	S1,T1			;GET HOURS
	PUSHJ	P,G$CDEC##		;OUTPUT THAT
	MOVEI	S1,":"			;TIME ALIGNMENT
	PUSHJ	P,G$CCHR##		;ISOLATE PARTS
	MOVEI	S1,"0"			;IN CASE .LT.10
	CAIGE	T2,^D10			;NEED THE LEADING ZERO
	  PUSHJ	P,G$CCHR##		;YES, MAKE THE OUTPUT PRETTY
	MOVE	S1,T2			;GET MINUTES
	PUSHJ	P,G$CDEC##		;OUTPUT IT
	MOVEI	S1,":"			;TIME ALIGNMENT
	PUSHJ	P,G$CCHR##		;ISOLATE PARTS
	MOVEI	S1,"0"			;IN CASE .LT.10
	CAIGE	T3,^D10			;NEED THE LEADING ZERO
	  PUSHJ	P,G$CCHR##		;YES, MAKE THE OUTPUT PRETTY
	MOVE	S1,T3			;GET SECONDS
	PUSHJ	P,G$CDEC##		;AND ADD THAT

IFN INPCOR,<
	MOVEI	S1,[ASCIZ\/Core:\]	;NOW TYPE CORE VALUE
	PUSHJ	P,G$CSTG##		;APPEND
	LOAD	S1,.QELM2(AP),QE.COR	;GET THE VALUE
	PUSHJ	P,G$CDEC##		;OUTPUT THE NUMBER
	MOVEI	S1,"P"			;THAT NUMBER WAS IN PAGES
	PUSHJ	P,G$CCHR##		;SINCE LEAVING THAT OFF MEANS "K"
>  ;END IFN INPCOR

CREA.6:	MOVX	S1,TX.MOR		;INFORMATIONAL, MORE TO COME
	PJRST	G$MSND##		;SEND THE "ACK" AND RETURN
SUBTTL	LIST  --  Function 10

;THE PRIMARY PURPOSE OF THIS ROUTINE IS TO VALIDATE A LIST
;	REQUEST.  ONCE THIS IS DONE, THE LIST-ANSWER ROUTINE
;	(Q$LANSWER) DOES THE REST.

Q$LIST:
	LOAD	T1,.MSTYP(M),MS.CNT	;GET THE MESSAGE SIZE
	CAIGE	T1,LIS.SZ		;BIG ENOUGH?
	  PJRST	E$MTS##			;INDICATE MESSAGE TOO SHORT
	$COUNT	(MLST)			;NUMBER OF LIST MESSAGES
	LOAD	T1,LIS.QN(M)		;GET THE QUEUE NAME
	JUMPE	T1,Q$LANSWER		;ZERO INDICATES ALL QUEUES
	MOVE	S1,T1			;COPY FOR CONVERSION
	ZERO	S2			;DON'T CARE ABOUT STATION
	PUSHJ	P,I$MIDS##		;CONVERT TO INTERNAL FORM
	PJUMPE	S1,E$IFD##		;GIVE ERROR IF MALFORMED
	PUSHJ	P,Q$FHDR		;FIND THE HEADER
	SKIPN	T1,S1			;COPY HDR ADR INTO T1
	PJRST	E$UQS##			;AND LOSE IF UNKNOWN QUEUE
	LOAD	T2,.QHTYP(S1),QH.LST	;GET "LISTABLE" BIT
	PJUMPE	T2,E$CLQ##		;LOSE IF NOT SET
	JRST	Q$LANSWER		;AND GIVE HIM THE LISTING
SUBTTL	MODIFY  --  Function 11

;THE MODIFY MESSAGE IS SENT TO QUASAR BY ANY UNKNOWN COMPONENT TO CHANGE
;	THE PARAMETERS OF A REQUEST IN A PROCESSING OR AFTER QUEUE
;MODIFY PREFORMS THE FOLLOWING FUNCTIONS:
;	1 ) VALIDATE THE ARGUMENTS
;	2 ) BUILD A TEMPORARY QUEUE OF REQUESTS THAT MATCH
;	3 ) PERFORM THE MODIFY REQUEST ON EACH ELEMENT OF THE TEMPORARY QUEUE
;	4 ) CALL Q$CREATE FOR THE RESULTANT MODIFIED REQUEST
Q$MODIFY:
	PUSHJ	P,.SAVE4##		;SAVE P1-P4
	$COUNT	(MMOD)			;NUMBER OF MODIFY MESSAGES
	LOAD	P1,.MSTYP(M),MS.CNT	;GET THE COUNT FIELD
	CAIGE	P1,MOD.SZ		;TOO SMALL
	  PJRST	E$MTS##			;INDICATE MESSAGE TOO SHORT
	LOAD	S1,MOD.QN(M)		;GET THE QUEUE NAME
	ZERO	S2			;DON'T CARE ABOUT STATION
	PUSHJ	P,I$MIDS##		;CONVERT TO INTERNAL FORM
	PJUMPE	S1,E$IFD##		;GIVE ERROR IF MALFORMED
	PUSHJ	P,Q$FHDR		;FIND THE QUEUE HEADER
	SKIPN	H,S1			;PUT INTO H
	PJRST	E$UQS##			;NONE FOUND, FORGET IT

;BEFORE ACTUAL PROCESSING, CHECK THE REQUEST FOR INVALID LENGTHS AND GROUPS

	ADDI	P1,(M)			;P1 = FIRST WORD NOT IN THE MESSAGE
	MOVEI	P2,MOD.FG(M)		;P2 = THE FIRST GROUP HEADER
MODI.1:	CAIG	P1,MOD.GN(P2)		;OFF THE END OF THE MESSAGE
	  JRST	MODI.2			;YES, NOW BEGIN PROCESSING
	LOAD	P3,MOD.GN(P2),MODGPN	;GET THE GROUP NUMBER
	LOAD	P4,MOD.GN(P2),MODGLN	;AND NUMBER OF GROUP ELEMENTS
	PJUMPE	P4,E$BMG##		;CANNOT BE ZERO
	ADDI	P4,(P2)			;P4 = NEXT GROUP HEADER
	CAIG	P3,NGROUP		;INVALID GROUP NUMBER
	 CAIGE	P1,(P4)			;OR INVALID LENGTH
	  PJRST	E$BMG##			;YES, BAD MESSAGE FORMAT
	MOVEI	P2,(P4)			;POINT TO THE NEXT GROUP
	JRST	MODI.1			;CONTINUE FOR ALL PROVIDED GROUPS

;HAVING VERIFIED THE LENGTHS, BEGIN FINDING THE SPECIFIED REQUESTS

MODI.2:	PUSH	P,G$ACK##		;SAVE CALLERS "ACK" STATUS
	ZERO	G$ACK##			;SO DON'T GET MESSAGES FROM CREATE
	ZERO	MODI.C+0		;ZERO NUMBER OF REQUESTS MODIFIED
	SETOM	MODI.C+1		;INITIALLY, DON'T PRINT NUMBER OF FILES
	ZERO	MODI.C+2		;NOT CANCELLING JOBS
	ZERO	MODHDR+.QHLNK		;CLEAR LINKS OF FAKE HEADER
	PUSHJ	P,MODSET		;SET UP ARGUMENTS FOR FNDREQ
	PUSHJ	P,FNDREQ		;FIND REQUESTS IN REQUESTED QUEUE
	MOVEM	S1,MODI.C+3		;START COUNTING PROTECTION FAILURES
	PUSHJ	P,MODSET		;RESET THE ARGUMENTS
	MOVEI	H,HDRAFT		;BUT THIS TIME USE THE AFTER QUEUE
	PUSHJ	P,FNDREQ		;FIND ANY THERE
	ADDM	S1,MODI.C+3		;MORE PROTECTION FAILURES
	MOVEM	M,MODI.A		;SAVE ORIGINAL MESSAGE ADDRESS
				;"MODIFY" IS CONTINUED ON THE NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

;NOW, FLUSH THE TEMPORARY QUEUE, MODIFYING, RE-CREATING AS WE GO

MODI.3:	LOAD	AP,MODHDR+.QHLNK,QH.PTF	;GET THE TOP OF THE TEMPORARY QUEUE
	JUMPE	AP,MODI.6		;PROCESSED THEM ALL (OR NONE)
	LOAD	H,.QESTN(AP),QE.RQP	;GET THE RELATIVE QUEUE POINTER
	ADDI	H,TBLHDR		;MAKE A REAL POINTER OUT OF IT
	LOAD	S1,.QESTN(AP),QE.DPA	;GET THE RETREIVAL POINTER
	PUSHJ	P,F$RDRQ##		;READ IN THE ORIGINAL REQUEST
	MOVEM	S1,MODI.B		;SAVE FOR LATER RELEASE
	MOVE	AP,S1			;ARGUMENT FOR MODIFIER ROUTINES
	MOVE	M,MODI.A		;GET MODIFY MESSAGE BACK
	LOAD	P1,.MSTYP(M),MS.CNT	;NOW LOOP FOR ALL GROUP HEADERS
	ADDI	P1,(M)			;P1 = FIRST WORD NOT IN THE MESSAGE
	MOVEI	P2,MOD.FG(M)		;P2 = THE FIRST GROUP
MODI.4:	CAIG	P1,MOD.GN(P2)		;OFF THE END OF THE MESSAGE
	  JRST	MODI.5			;YES, DONE WITH THIS MATCH
	LOAD	P3,MOD.GN(P2),MODGPN	;GET THE GROUP NUMBER
	MOVEI	S1,(P2)			;ARGUMENT FOR MODIFIERS
	PUSHJ	P,@GRPDIS(P3)		;CALL THE PROPER MODIFIER FOR THIS GROUP
	LOAD	P3,MOD.GN(P2),MODGLN	;ADJUST FOR THE NEXT GROUP
	ADDI	P2,(P3)			;P2 = THE NEXT GROUP
	JRST	MODI.4			;CONTINUE FOR ALL GROUPS PROVIDED

;HERE AFTER ALL GROUP MODIFIES HAVE BEEN DONE, LINK IN THE NEW REQUEST

MODI.5:	MOVE	M,MODI.B		;ARGUMENT FOR CREATE = MODIFIED REQUEST
	MOVX	T1,.QIFNC		;THIS IS AN INTERNAL CALL
	STORE	T1,.MSTYP(M),MS.TYP	;SET FOR Q$CREATE
	PUSHJ	P,Q$CREATE		;PUT MODIFIED REQUEST BACK IN PLACE
	SKIPE	G$ERR##			;DID THE CREATE WORK
	  STOPCD(CRM,FATAL)		;++CREATE REJECTED MODIFY
	LOAD	AP,MODHDR+.QHLNK,QH.PTF	;GET OLD QUEUE ENTRY BACK
	LOAD	S1,.QESTN(AP),QE.DPA	;THE RETREIVAL POINTER
	PUSHJ	P,F$RLRQ##		;RELEASE OLD FAILSOFT COPY
	MOVEI	H,MODHDR		;POINT TO THE TEMPORARY QUEUE
	PUSHJ	P,M$DLNK##		;REMOVE ENTRY ALREADY MODIFIED
	LOAD	H,.QEPID(AP)		;QUEUE THIS ENTRY CAME FROM (PROC, OF AFTER)
	PUSHJ	P,M$PFRE##		;RETURN TO PROPER FREE LIST
	MOVE	AP,MODI.B		;ADDRESS OF OLD FAILSOFT COPY
	ADR2PG	AP			;MAKE A PAGE NUMBER
	PUSHJ	P,M$RELP##		;NO LONGER NEED IT
	JRST	MODI.3			;GET ANY OTHER MATCHING REQUESTS

;NOW, GENERATE "ACK" AND RETURN TO THE CALLER

MODI.6:	POP	P,G$ACK##		;RESTORE "ACK" STATUS
	MOVEI	T1,MODI.C		;ARGUMENT BLOCK FOR BLDKMS
	MOVEI	T2,[ASCIZ/ Modified/]	;TEXT TO APPEND
	PJRST	BLDKMS			;BUILD KILL/MODIFY STRING AND RETURN
; SUBROUTINES USED BY MODIFY

;ROUTINE CALL BE MODIFY LOOP TO SET UP ARGUMENTS FOR FNDREQ

MODSET:	MOVNI	T1,1			;INDICATE MODIFY REQUEST
	MOVEI	T2,MOD.RQ(M)		;RDB FOR KILL/MODIFY
	MOVE	T3,H			;NEED AN RQP
	SUBI	T3,TBLHDR		;SO GENERATE ONE FOR THIS QUEUE
	MOVEI	T4,MODS.1		;SUBROUTINE TO CALL FOR MATCHES
	POPJ	P,			;RETURN FOR CALL TO FNDREQ

;HERE WHEN FNDREQ FINDS A MATCH FOR THE MODIFY BLOCK, ACCUMULATE IN THE TEMP QUEUE

MODS.1:	STORE	H,.QEPID(AP)		;SAVE QUEUE THAT CONTAINED THIS ENTRY
	PUSHJ	P,M$DLNK##		;REMOVE FROM PROCESSING OR AFTER QUEUE
	MOVEI	H,MODHDR		;POINT TO THE TEMPORARY QUEUE
	PUSHJ	P,M$ELNK##		;LINK IN AT THE END
	AOS	MODI.C+0		;INCREMENT COUNT OF MODIFIED REQS
	POPJ	P,			;RETURN FOR NEXT MATCH


;THE DISPATCH TABLE FOR GROUP MODIFIES

GRPDIS:	EXP	MAJMOD			;GROUP 0 = MAJOR QUEUE PARAMETERS
	EXP	GRPMOD			;GROUP 1 = QUEUE DEPENDENT PARAMETERS
	EXP	MODFIL			;GROUP 2 = FILE-SPECIFIC PARAMETERS
NGROUP==<.-GRPDIS>-1			;HIGHEST KNOWN GROUP NUMBER

GRPMOD:	LOAD	P3,.QHPAG(H),QH.SCH	;GET SCHEDULING BASE
	PJRST	SCHMOD(P3)		;DO QUEUE DEPENDENT MODIFY
MODFIL:	SKIPGE	MODI.C+1		;ALREADY HERE ONCE
	  SETZM	MODI.C+1		;NO, START COUNTING FILES MODIFIED
	PUSHJ	P,FILMOD		;DO THE MODIFY
	ADDM	S1,MODI.C+1		;ADD NUMBER OF FILES THAT MATCHED
	POPJ	P,			;GET ANOTHER GROUP

MODHDR:	BLOCK	QHSIZE			;HEADER FOR THE TEMPORARY QUEUE

MODI.A:	BLOCK	1			;ORIGINAL 'M'
MODI.B:	BLOCK	1			;WHERE REQUEST HAS BEEN READ BY FAILSOFT
MODI.C:	BLOCK	4			;ARGUMENT BLOCK FOR BLDKMS
SUBTTL	KILL  --  Function 12

;THE KILL MESSAGE IS SENT TO QUASAR BY ANY UNKNOWN COMPONENT TO
;	DELETE AN ENTRY FROM A QUEUE.
;KILL PERFORMS THE FOLLOWING FUNCTIONS:
;	1) VALIDATE THE ARGUMENTS
;	2) LOOP THROUGH THE USE QUEUE ABORTING MATCHES.
;	3) LOOP THROUGH THE PROCESSING QUEUE DELETING MATCHES.
;	4) LOOP THROUGH THE AFTER QUEUE DELETING MATCHES.

Q$KILL:	PUSHJ	P,.SAVE3##		;SAVE P1-P3
	$COUNT	(MKIL)			;NUMBER OF KILL MESSAGES
	LOAD	T1,.MSTYP(M),MS.CNT	;GET MESSAGE SIZE
	CAIGE	T1,KIL.SZ		;BIG ENOUGH?
	  PJRST	E$MTS##			;INDICATE MESSAGE TOO SHORT
	LOAD	S1,KIL.QN(M)		;GET THE QUEUE NAME
	ZERO	S2			;DON'T CARE ABOUT STATION
	PUSHJ	P,I$MIDS##		;CONVERT TO INTERNAL FORM
	PJUMPE	S1,E$IFD##		;GIVE ERROR IF MALFORMED
	PUSHJ	P,Q$FHDR		;AND FIND THE QUEUE HEADER
	SKIPN	H,S1			;LOAD HDR ADR INTO H
	PJRST	E$UQS##			;UNKNOWN QUEUE SPECIFIED
	MOVE	P3,S1			;GET ADR OF QUE HDR
	SUBI	P3,TBLHDR		;AND MAKE AN RQP FROM IT
	ZERO	P1			;INDICATE KILL REQUEST
	MOVEI	P2,KIL.RQ(M)		;REQUEST DESCR BLOCK FOR FNDREQ
	ZERO	KILL.A+0		;ZERO NUMBER KILLED
	SETOM	KILL.A+1		;DON'T COUNT FILES
	ZERO	KILL.A+2		;NUMBER CANCELLED

;NOW, SETUP AND LOOP THRU THE NECESSARY QUEUES

KILL.1:	PUSH	P,H			;SAVE PROCESSING QUEUE HDR
	MOVE	T4,[P1,,T1]		;MOVE PERMENANT ARGS TO REAL ARGS
	BLT	T4,T3			;COPY P1 - P3
	MOVEI	T4,KILL.9		;SUBROUTINE TO CALL
	MOVEI	H,HDRUSE		;LOAD ADR OF USE QUEUE HDR
	PUSHJ	P,FNDREQ		;FIND A MATCHING REQUEST
	MOVEM	S1,KILL.A+3		;START COUNTING PROT FAILURES
	MOVE	T4,[P1,,T1]		;NEED ARGUMENTS AGAIN
	BLT	T4,T3			;SO MOVE THEM
	MOVEI	T4,KILL.8		;NEW ROUTINE TO CALL
	POP	P,H			;GET ADR OF PROCESSING Q HDR
	PUSHJ	P,FNDREQ		;AND CHECK IT
	ADDM	S1,KILL.A+3		;COUNT PROTECTION FAILURES
	MOVEI	H,HDRAFT		;LOAD ADR OF AFT HEADER
	MOVE	T4,[P1,,T1]		;ONE MORE TIME
	BLT	T4,T3			;BUT NOW FNDREQ IS USABLE BY Q$MODIFY
	MOVEI	T4,KILL.8		;SAME PLACE FOR THE AFTER QUEUE
	PUSHJ	P,FNDREQ		;LOOP THRU THE AFTER QUEUE
	ADDM	S1,KILL.A+3		;MORE PROTECTION FAILURES
	MOVEI	T1,KILL.A		;ARGUMENT BLOCK FOR BLDKMS
	MOVEI	T2,[ASCIZ/ Killed/]	;TEXT TO APPEND
	PJRST	BLDKMS			;BUILD KILL/MODIFY STRING AND RETURN
; SUBROUTINES USED DURING KILL

;FOLLOWING ARE THE CO-ROUTINES (SORT OF) WHICH ARE CALLED BY FNDREQ
;
;ROUTINE TO ACTUALLY TAKE A REQUEST OUT OF A PROCESSING QUEUE OR THE
;	AFTER QUEUE.

KILL.8:	AOS	KILL.A+0		;INCREMENT KILL COUNT
	MOVE	T1,AP			;COPY CELL ADDRESS
	LOAD	T2,.QESEQ(AP),QE.SPL	;DOES REQ CONTAIN SPOOLED FILES?
	JUMPN	T2,KIL.8A		;YES, SET RDE AND RETURN
	LOAD	S1,.QESTN(AP),QE.DPA	;NO, GET DISK ADDRESS
	PUSHJ	P,F$RLRQ##		;RELEASE THE DISK REQUEST
	PUSHJ	P,M$RFRE##		;RELEASE THE QUEUE ENTRY
	POPJ	P,			;AND RETURN TO KILLUP

KIL.8A:	SAVE	H			;SAVE THE QUEUE HEADER
	PUSHJ	P,SETRDE		;SET THE RDE BIT IN QE AND EQ
	MOVEI	S1,HDRRDE		;GET THE RDE HEADER
	PJRST	M$MOVE##		;AND MOVE THE REQUEST

;ROUTINE TO CAUSE AN ABORT MESSAGE TO BE SENT TO A KNOWN COMPONENT
;	ON A KILL OF A JOB IN THE USE QUEUE.

KILL.9:	SAVE	AP			;SAVE CALLERS AP
	MOVE	T1,AP			;GET ADR OF QE AREA
	PUSHJ	P,SETRDE		;SET RDE IN BOTH .EQ AND .QE
	MOVE	T1,AP			;LOAD ARGUMENT FOR ABORT
	AOS	KILL.A+2		;INCREMENT ABORTED COUNT
	PJRST	Q$ABORT			;SEND THE ABORT AND RETURN


KILL.A:	BLOCK	4			;ARGUMENT BLOCK FOR BLDKMS
SUBTTL	LISTANSWER  --  Function 13

;LISTANSWER IS CALLED TO SEND AN ANSWER BACK ON A LIST REQUEST.
;	CALL WITH T1 CONTAINING ADDRESS OF QUEUE HEADER FOR THE
;	QUEUE TO BE LISTED, OR ZERO IF ALL QUEUES ARE TO BE LISTED.

Q$LANSWER:
	PUSHJ	P,.SAVE2##		;SAVE P1 AND P2
	PUSHJ	P,I$NOIN##		;DON'T ALLOW INTERRUPTS
	MOVE	S1,LISSIZ		;GET SIZE OF LAST COMPL LISTING
	SKIPE	P1,T1			;DOES HE JUST WANT A PARTIC. Q?
	LOAD	S1,.QHPAG(T1),QH.NTL	;YES, GET IT'S NUMBER
	ADDI	S1,1			;PAD IT A LITTLE
	PUSHJ	P,M$PCNT##		;SEE IF WE CAN GET IT
	PJUMPE	S1,E$CBL##		;NO, LOSE NOW!
	MOVX	S1,TX.NMS		;NO MESSAGE FLAG
	SKIPE	G$ACK##			;DOES USER WANT AN ACK?
	PUSHJ	P,G$MSND##		;YES, SEND IT
	MOVE	T2,[LANS.A,,LANS.A+1]	;SETUP A BLT POINTER
	CLEARM	LANS.A			;CLEAR PAGE SEQ NUMBER
	BLT	T2,LANS.Z		;AND CLEAR THE OTHER LOCALS
	PUSH	P,[EXP LANEND]		;PLACE TO GO BEFORE RETURNING
	JUMPE	P1,LANS.1		;JUMP IF HE WANTS ALL QUEUES

	PUSHJ	P,LANQUE		;DO THE LISTING
	MOVE	S1,LANS.A		;GET NUMBER OF PAGES NEEDED
	STORE	S1,.QHPAG(P1),QH.NTL	;SAVE IT FOR NEXT TIME
	POPJ	P,			;AND RETURN

LANS.1:	MOVEI	P2,0			;START AT QUEUE 0
LANS.2:	CAIL	P2,NQUEUE		;GOT THEM ALL?
	JRST	LANS.3			;YUP, FINISH UP AND RETURN
	MOVE	P1,P2			;NO, GET QUEUE NUMBER
	IMULI	P1,QHSIZE		;CONVERT TO OFFSET
	ADDI	P1,TBLHDR		;GET ADDRESS IN P1
	LOAD	T2,.QHTYP(P1),QH.LST	;GET "LISTABLE" BIT
	SKIPE	T2			;SKIP IF NOT SET
	PUSHJ	P,LANQUE		;ELSE LIST THE QUEUE
	AOJA	P2,LANS.2		;AND LOOP

LANS.3:	MOVE	S1,LANS.A		;GET NUMBER OF PAGES NEEDED
	MOVEM	S1,LISSIZ		;SAVE FOR NEXT TIME
	POPJ	P,			;AND RETURN
;HERE TO LIST QUEUE WHOSE HEADER ADDRESS IS IN P1
LANQUE:	PUSHJ	P,.SAVE2##		;SAVE P1 AND P2
	MOVE	P2,P1			;GET ADR OF Q HDR
	SUBI	P2,TBLHDR		;MAKE AN RQP
	MOVEI	H,HDRUSE		;START WITH USE QUEUE
	LOAD	T1,.QHLNK(H),QH.PTF	;GET ADDRESS OF FIRST
	JUMPE	T1,LANQ.3		;NONE, GO DO AFT QUEUE

LANQ.1:	LOAD	T2,.QESTN(T1),QE.RQP	;GET RQP FROM REQUEST
	CAME	P2,T2			;CORRECT QUEUE?
	JRST	LANQ.2			;NO, GET NEXT ENTRY
	LOAD	T2,.QESEQ(T1),QE.RDE	;GET THE RDE BIT
	JUMPN	T2,LANQ.2		;REQUEST DOESN'T EXIST!!
	PUSHJ	P,LANMSG		;YES, GET SPACE
	PUSHJ	P,LANMOV		;MOVE COMMON STUFF
	MOVE	T2,.QECRE(T1)		;GET CREATION TIME
	CAIN	H,HDRAFT		;IS THIS THE AFTER QUEUE?
	MOVEM	T2,LST.AF(AP)		;YES, STORE IT
	MOVE	T2,.QEDEV(T1)		;GET PROCESSING DEVICE
	CAIN	H,HDRUSE		;IS THIS THE USE QUEUE?
	MOVEM	T2,LST.PV(AP)		;YES, STORE IT
LANQ.2:	LOAD	T1,.QELNK(T1),QE.PTN	;GET PTR TO NEXT ENTRY
	JUMPN	T1,LANQ.1		;AND LOOP
LANQ.3:	CAIN	H,HDRAFT		;E-O-LIST, WERE WE DOING AFT?
	JRST	LANQ.4			;YES, GO DO THE EXTERNAL QUEUE
	MOVEI	H,HDRAFT		;NO, DO THE AFTER
	LOAD	T1,.QHLNK(H),QH.PTF	;GET PTR TO FIRST
	JUMPN	T1,LANQ.1		;GO TO IT

;HERE TO LIST THE EXTERNAL QUEUE
LANQ.4:	MOVE	H,P1			;GET ADDRESS OF HEADER
	LOAD	T1,.QHLNK(H),QH.PTF	;GET POINTER TO FIRST
	JUMPE	T1,.POPJ##		;DONE IF NONE

LANQ.5:	PUSHJ	P,LANMSG		;GET SOME SPACE
	PUSHJ	P,LANMOV		;MOVE COMMON STUFF
	LOAD	T1,.QELNK(T1),QE.PTN	;GET POINTER TO NEXT
	JUMPN	T1,LANQ.5		;AND LOOP OVER WHOLE QUEUE
	POPJ	P,			;WE ARE DONE!!


				;"LISTANSWER" IS CONTINUED ON NEXT PAGE
;HERE AT THE END OF LIST, TO TERMINATE THE LAST PAGE AND FORCE IT OUT
LANEND:	PUSHJ	P,LANMSG		;GET SOME SPACE (AVOID BOUNDRY PROBLEMS)
	MOVE	T1,LANS.B		;GET CURRENT PAGE NUMBER
	PG2ADR	T1			;MAKE IT AN ADDRESS
	MOVX	T2,1B0			;LOAD BIT ZERO
	IORM	T2,.MSTYP(T1)		;SET "LAST PAGE" FLAG
	PJRST	LANSND			;AND SEND THE LAST PAGE


;SUBROUTINE TO MOVE COMMON STUFF FROM ENTRY TO LISTANSWER.
;	T1=ENTRY	AP=ANSWER
LANMOV:	MOVE	T2,.QEJOB(T1)		;GET JOB NAME
	MOVEM	T2,LST.JB(AP)		;STORE IN ANSWER
	MOVE	T2,.QESTR(T1)		;GET STRUCTURE CONTAINING THE REQUEST
	MOVEM	T2,LST.ST(AP)		;STORE FOR LISTER
	LOAD	T2,.QESEQ(T1),QE.SEQ	;GET SEQUENCE FIELD
	STORE	T2,LST.SQ(AP),LS.SEQ	;AND STORE IN ANSWER
	LOAD	T2,.QEPRT(T1),QE.PRO	;GET PROTECTION OF REQUEST
	STORE	T2,LST.SQ(AP),LS.PRO	;AND STORE IT
	MOVE	T2,.QEOID(T1)		;THE OWNER ID
	MOVEM	T2,LST.OI(AP)		;AND STORE
	MOVSI	T2,.QELM1(T1)		;POINT TO LIMIT WORDS
	HRRI	T2,LST.LM(AP)		;AND MAKE A BLT POINTER
	BLT	T2,LST.LM+4(AP)		;AND BLT THE FIVE OF THEM
	LOAD	T2,.QHTYP(P1),QH.NAM	;GET THE QUEUE NAME
	LOAD	S1,.QEPRT(T1),QE.DMD	;GET DEVICE MODIFIERS FOR THIS REQUEST
	HRL	S1,T2			;INCLUDE DEVICE NAME (QUEUE NAME)
	PUSHJ	P,A$CSTN##		;CONVERT STATIONS IF NECESSARY
	PUSHJ	P,I$MSDN##		;TURN INTO A DEVICE NAME
	STORE	S1,LST.DV(AP)		;INTO CURRENT ANSWER
	LOAD	T2,.QESEQ(T1),QE.PRI	;GET EXTERNAL PRIORITY
	STORE	T2,LST.PI(AP),LS.PRI	;AND STORE IT
	MOVE	S1,T1			;POINT S1 TO THE QE
	PUSHJ	P,I$QELA##		;MOVE OS DEPENDENT STUFF
	POPJ	P,			;AND RETURN


				;"LISTANSWER" IS CONTINUED ON NEXT PAGE
;MORE SUBROUTINES FOR LISTANSWER

;HERE TO GET SPACE FOR A LISTANSWER ENTRY.  RETURN ADR IN AP.
LANMSG:	SOSLE	LANS.C			;ANY ROOM LEFT IN CURRENT PAGE?
	JRST	LANM.1			;YES, GO GET IT
	PUSHJ	P,.SAVET##		;NO, SAVE T1-T4
	SKIPE	LANS.B			;IT THIS THE FIRST TIME THRU?
	PUSHJ	P,LANSND		;NO, SEND CURRENT PAGE
	$COUNT	(NLAP)			;NUMBER OF LIST ANSWER PAGES
	PUSHJ	P,M$ACQP##		;GET A PAGE
	MOVEM	AP,LANS.B		;SAVE PAGE NUMBER
	PG2ADR	AP			;MAKE AN ADDRESS
	MOVE	S1,AP			;COPY ADDRESS TO S1
	PUSHJ	P,.ZPAGA##		;AND ZAP IT
	AOS	T1,LANS.A		;INCREMENT SEQ # AND LOAD IT
	HRLZS	T1			;GET SEQ#,,0
	HRRI	T1,.QOLAN		;GET SEQ#,,FUNCTION
	STORE	T1,.MSTYP(AP)		;AND STORE IN MESSAGE HEADER
	MOVEI	T1,LST.NU		;GET NUMBER OF ENTRIES/PAGE
	MOVEM	T1,LANS.C		;AND STORE COUNTER
	MOVEI	AP,LST.FT(AP)		;GET ADDRESS OF FIRST ENTRY
	MOVEM	AP,LANS.D		;SAVE IT
	POPJ	P,			;AND RETURN

;HERE IF SPACE EXISTS IN CURRENT PAGE
LANM.1:	MOVEI	AP,LST.SZ		;GET SIZE OF AN ENTRY
	ADDB	AP,LANS.D		;GET ADR OF NEXT AND SAVE IT
	POPJ	P,			;AND RETURN


;HERE TO SEND THE CURRENT PAGE
LANSND:	MOVEI	AP,MSGPDB		;LOAD ARGUMENT FOR C$SEND
	MOVX	T1,IP.CFV		;GET PAGE-MODE BIT
	MOVEM	T1,.IPCFL(AP)		;STORE FLAG WORD
	MOVE	T1,G$SND##		;GET CURRENT SENDER'S PID
	MOVEM	T1,.IPCFR(AP)		;AND SET RECEIVE
	MOVSI	T1,1000			;GET PAGE SIZE,,0
	HRR	T1,LANS.B		;GET PAGE SIZE,,PAGE NUMBER
	MOVEM	T1,.IPCFP(AP)		;STORE IT
	TXO	AP,IPS.TF		;IGNORE LANSWER ORDER
	PJRST	C$SEND##		;SEND THE MESSAGE AND RETURN


LANS.A:	BLOCK	1			;SEQ NUMBER OF CURRENT PAGE
LANS.B:	BLOCK	1			;NUMBER OF CURRENT PAGE
LANS.C:	BLOCK	1			;# ENTRIES LEFT IN CURRENT PAGE
LANS.D:	BLOCK	1			;ADDRESS OF CURRENT ENTRY

	LANS.Z==.-1
SUBTTL	TEXT  --  Function 14

;THE TEXT MESSAGE IS ASSEMBLED AND SENT BY THE GLOBAL ROUTINES
;	G$CSTG, G$CSIX, G$CDEC, G$CCHR, AND G$MSND.

;SINCE THE TEXT MESSAGE IS SENT ONLY WHEN THE CALLER REQUESTS "MS.ACK"
;	AND THE MESSAGE IS DERIVED FROM THE GLOBAL ERROR CODE, THERE IS
;	NO CODE IN QSRQUE TO PROCESS OR SEND THE TEXT RESPONSE.

;IT IS THE RESPONSIBILITY OF THE MAIN PROCESSING LOOP IN MODULE QUASAR
SUBTTL	DEFER  --  Function 16

;THE DEFER MESSAGE IS SENT TO QUASAR BY ANY UNKNOWN COMPONENT TO 
;	RELEASE OR KILL DEFERED SPOOL REQUESTS
;
;DEFER PERFORMS THE FOLLOWING FUNCTIONS:
;	1)VALIDATE THE ARGUMENTS
;	2)LOOP THROUGH THE SPOOL QUEUE FINDING ENTRIES FOR THIS JOB
;	3)CALL Q$CREATE FOR MATCHES, SETTING EQ.RDE IF THIS IS A KILL
;	4)RETURN AN ACK IF WANTED SAYING HOW MANY JOBS, FILES WERE
;		RELEASED OR KILLED; ALSO HOW MANY "PROTECTION" FAILURES
;		I.E. JOB NUMBER MATCHES WITH WRONG DIRECTORY

Q$DEFER:
	PUSHJ	P,.SAVE4##		;SAVE P1-P4
	$COUNT	(MDEF)			;NUMBER OF DEFER MESSAGES
	LOAD	T1,.MSTYP(M),MS.CNT	;GET LENGTH OF MESSAGE
	CAIGE	T1,DFR.SZ		;IS IT ALL THERE
	  PJRST	E$MTS##			;NOT LONG ENOUGH, GIVE ERROR
	ZERO	DEFE.A+0		;CLEAR COUNT OF JOBS AFFECTED
	ZERO	DEFE.A+1		;AND OF FILES
	ZERO	DEFE.A+2		;NOT CANCELLING JOBS
	ZERO	DEFE.A+3		;AND OF PROTECTION FAILURES
	LOAD	P1,DFR.JB(M),DF.JOB	;GET JOB NUMBER FROM MESSAGE
	LOAD	P2,<HDRSPL+.QHLNK>,QH.PTF  ;GET THE FIRST ENTRY IN THE SPOOL QUEUE
	LOAD	T1,DFR.JB(M),DF.FNC	;GET THE FUNCTION REQUESTED
	ZERO	P3			;INDICATE RELEASE (FOR LOOP)
	CAIN	T1,.DFKIL		;IS IT KILL?
	  MOVEI	P3,1			;YES, INDICATE FOR LOOP
	LOAD	P4,DFR.QN(M)		;PICK UP DEVICE NAME IN MESSAGE
	JUMPE	P4,DEFE.1		;GO AHEAD IF ZERO
	MOVE	S1,P4			;COPY FOR MIDS
	ZERO	S2			;DON'T REALLY CARE ABOUT STATION
	PUSHJ	P,I$MIDS##		;ELSE CONVERT IT TO INTERNAL FORM
	PJUMPE	S1,E$IFD##		;BAD NAME IN MESSAGE
	LOAD	P4,S1,DV.GDN		;NEED ONLY THE GENERIC DEVICE NAME
DEFE.1:	JUMPE	P2,DEFE.4		;END OF SPOOL QUEUE, RETURN
	LOAD	T2,SPLJOB(P2),SPYJOB	;GET THE JOB # FROM THIS QUEUE ENTRY
	LOAD	T1,SPLQUE(P2),DV.GDN	;AND THE DEVICE NAME
	CAME	T1,P4			;IS DEVICE NAME OF ENTRY SAME AS
	  JUMPN	P4,DEFE.3		;NO, JUMP IF REQUESTED SPECIFIC QUEUE
	CAME	T2,P1			;IS THIS FOR THE RIGHT JOB
	  JRST	DEFE.3			;WRONG JOB OR WRONG QUEUE
	LOAD	T1,SPLOID(P2)		;GET OWNER ID OF THIS ENTRY
	LOAD	T2,G$SID##		;GET SENDER'S ID
	CAME	T1,T2			;IS IT THE SAME AS SENDER'S?
	  JRST	DEFE.2			;NO,PROTECTION FAILURE
	AOS	DEFE.A+0		;FOUND A MATCHING JOB, COUNT IT
	LOAD	S1,SPLJOB(P2),SPYDPA	;GET THE DPA
	PUSHJ	P,F$RDRQ##		;READ THE REQUEST
	LOAD	T1,.EQSPC(S1),EQ.NUM	;PICK UP THE # OF FILES IN THE REQUEST
	ADDM	T1,DEFE.A+1		;COUNT THEM FOR ACK
	PUSH	P,S1			;SAVE THE REQUEST ADDR
	MOVE	M,S1			;POINT TO IT

				;Q$DEFER IS CONTINUED ON THE NEXT PAGE
				;CONTINUED FROM THE PREVIOUS PAGE

	MOVX	S1,.QIFNC		;LOAD INTERNAL FUNCTION CODE
	STORE	S1,.MSTYP(M),MS.TYP	;AND SAVE IN REQUEST
	STORE	P3,.EQSEQ(M),EQ.RDE	;SET RDE BIT IF THIS WAS A KILL
	PUSHJ	P,Q$CREATE		;CALL CREATE
	SKIPE	G$ERR##			;ANY ERROR?
	  STOPCD(CRD,FATAL)		;++CREATE REJECTED DEFER DATA
	POP	P,AP			;AND ADDR OF REQUEST
	ADR2PG	AP			;MAKE IT A PAGE NUMBER
	PUSHJ	P,M$RELP##		;RELEASE IT
	LOAD	S1,SPLJOB(P2),SPYDPA	;GET THE POINTER TO THE DISK
	PUSHJ	P,F$RLRQ##		;RELEASE THE OLD REQUEST
	MOVEI	H,HDRSPL		;GET THE HEADER OF THE SPOOL QUEUE
	MOVE	AP,P2			;GET THE CURRENT ENTRY
	LOAD	P2,.QELNK(P2),QE.PTN	;STEP TO NEXT ENTRY
	PUSHJ	P,M$RFRE##		;FREE UP THIS ONE
	JRST	DEFE.1			;GO LOOP THROUGH QUEUE
DEFE.2:	AOS	DEFE.A+3		;COUNT PROTECTION FAILURES
DEFE.3:	LOAD	P2,.QELNK(P2),QE.PTN	;STEP TO NEXT ENTRY
	JRST	DEFE.1			;AND LOOP ON
DEFE.4:	MOVEI	T1,DEFE.A		;ARGUMENT BLOCK FOR BLDKMS
	MOVEI	T2,[ASCIZ/ Killed/]	;ASSUME WE KILLED THEM
	SKIPN	P3			;DID WE
	  MOVEI	T2,[ASCIZ/ Released/]	;NO, SAY RELEASED
	PJRST	BLDKMS			;SEND ACK IF REQUESTED

DEFE.A:	BLOCK	4			;ARGUMENT BLOCK FOR BLDKMS
SUBTTL	SPOOL   --  IPCC Function .IPCSU (26)

;THE SPOOL MESSAGE IS SENT TO QUASAR BY THE OPERATING SYSTEM UPON
;	THE CLOSE OF A SPOOLED FILE.

;AFTER CONVERTING THE ACTUAL SPOOL MESSAGE FROM [SYSTEM]IPCC INTO THE
;	CANONICAL FORM, ALL SPOOLING HANDLERS WILL USE THAT DATA STRUCTURE

Q$SPOOL:
	MOVEI	S1,(M)			;POINT TO IPCC'S MESSAGE
	PUSHJ	P,I$CSM##		;CONVERT TO STANDARD FORMAT
	MOVEI	T1,(S1)			;USE THAT FROM NOW ON
	MOVX	T2,CS.DFR		;CHECK FOR DEFERRED MODE SPOOLING
	TDNE	T2,CSM.JB(T1)		;DID THE USER REQUEST IT
	  JRST	SPOO.1			;YES, GO ADD TO THE SPL QUEUE
	$COUNT	(ISPL)			;NUMBER OF IMMEDIATE  SPOOLS
	PUSHJ	P,SPROTO		;BUILD EXTERNAL PROTOTYPE
	PUSHJ	P,Q$INCL		;INCLUDE THE NEW FILE (ONLY)
	PUSH	P,AP			;SAVE ADDR OF PROTOTYPE BUILT
	MOVE	M,AP			;MAKE IT THE MESSAGE
	PUSHJ	P,Q$CREATE		;CALL THE CREATE PROCESSOR
	SKIPE	G$ERR##			;WAS THERE AN ERROR?
	  STOPCD(CRS,FATAL)		;++CREATE REJECTED SPOOLING DATA
	POP	P,AP			;RESTORE ADDRESS
	ADR2PG	AP			;TO A PAGE NUMBER
	PJRST	M$RELP##		;RETURN THROUGH RELEASE PAGE

SPOO.1:	$COUNT	(DSPL)			;NUMBER OF DEFERED SPOOLS
	PUSHJ	P,Q$FSPL		;FIND THE MATCHING REQUEST
	PUSHJ	P,Q$INCL		;INCLUDE THIS FILE
	LOAD	S1,.MSTYP(AP),MS.CNT	;GET CURRENT REQUEST SIZE
	STORE	S1,SPLRQZ(E),SPYLEN	;SAVE IN SPOOL QUEUE
	MOVE	S1,AP			;GET REQUEST ADDRESS
	ADR2PG	AP			;CONVERT REQUEST TO PAGE NUMBER
	PUSHJ	P,F$WRRQ##		;WRITE IT OUT
	LOAD	S2,SPLJOB(E),SPYDPA	;LOAD THE OLD DPA INTO S2
	STORE	S1,SPLJOB(E),SPYDPA	;SAVE THE NEW RETRIEVAL POINTER
	SKIPE	S1,S2			;GET OLD DPA ( 0 IF INITIAL BUILD)
	  PUSHJ	P,F$RLRQ##		;RELEASE OLD COPY IF THERE IS ONE
	PJRST	M$RELP##		;RETURN THE PAGE AND EXIT
SUBTTL	LOGOUT  --  IPCC Function .IPCSL (27)

;THE LOGOUT MESSAGE IS SENT BY THE OPERATING SYSTEM UPON
;	EXECUTION OF A LOGOUT UUO OR LGOUT JSYS.

;IF .QIFNC IS SET INDICATING AN INTERNAL CALL, THEN COMING FROM S$RELEASE
;	AT THE END OF A BATCH JOB.  CL.BQE IN CLM.JB IS THE ADDRESS OF THE
;	.QExxx FORM OF THAT BATCH JOB

Q$LOGOUT:
	PUSHJ	P,.SAVE4##		;SAVE P1-P4
	ZERO	P4			;INDICATE NOT A BATCH CALL
	LOAD	T1,.MSTYP(M),.QIFNC	;GET INTERNAL CALL INDICATOR
	SKIPE	T1			;A QUICK CHECK FOR INTERNAL CALL
	  LOAD	P4,CLM.JB(M),CL.BQE	;IS, GET THE BATCH JOB QUEUE ENTRY
	MOVEI	S1,(M)			;GET ADDRESS OF SYSTEM LOGOUT MESSAGE
	JUMPN	T1,LOGO.1		;JUMP IF INTERNAL CALL NOW
	PUSHJ	P,I$CLM##		;CONVERT TO CANONICAL LOGOUT MESSAGE
	LOAD	T1,CLM.JB(S1),CL.BAT	;GET THE BATCH JOB BIT
	PJUMPN	T1,.POPJ##		;IGNORE IT HERE, BATCON WILL RELEASE IT
LOGO.1:	LOAD	P1,CLM.JB(S1),CL.JOB	;GET THE JOB NUMBER FOR THE LOOP
	LOAD	P2,<HDRSPL+.QHLNK>,QH.PTF  ;GET THE FIRST IN THE SPL QUEUE
	JUMPE	P4,LOGO.2		;JUMP IF NOT A BATCH JOB
	LOAD	S1,.QESTN(P4),QE.DPA	;ELSE GET THE DPA
	PUSHJ	P,F$RDRQ##		;READ THE INPUT REQUEST
	MOVE	P4,S1			;AND STORE ADDRESS IN P4
LOGO.2:	PJUMPE	P2,LOGO.5		;END OF THE QUEUE, RETURN
	LOAD	T1,SPLJOB(P2),SPYJOB	;JOB NUMBER OF THIS ENTRY
	CAME	T1,P1			;SAME JOB
	  JRST	LOGO.4			;NO, TRY THE NEXT
	LOAD	S1,SPLJOB(P2),SPYDPA	;GET THE DPA
	PUSHJ	P,F$RDRQ##		;READ THE REQUEST
	JUMPE	P4,LOGO.3		;JUMP IF THIS IS NOT A BATCH JOB
	ZERO	S2			;IN CASE WE DON'T FIND A MATCH
	LOAD	T1,SPLQUE(P2),DV.GDN	;GET THE QUEUE NAME
	CAIN	T1,'LPT'		;WATCH THIS BRUTE FORCE METHOD
	  LOAD	S2,.EQLM3(P4),EQ.LPT	;YES, GET PAGE REQUEST
	CAIN	T1,'CDP'		;LOOKING AT THE PUNCH QUEUE
	  LOAD	S2,.EQLM3(P4),EQ.CDP	;YES, GET CARD LIMIT
	CAIN	T1,'PTP'		;THE PAPER TAPE QUEUE
	  LOAD	S2,.EQLM4(P4),EQ.PTP	;YES, GET INPUT TAPE REQUEST
	CAIN	T1,'PLT'		;LAST CHANCE, THE PLOTTER
	  LOAD	S2,.EQLM4(P4),EQ.PLT	;YES, GET THAT
	SKIPE	S2			;FIND ONE ( OR DON'T LIMIT IT)
	  STORE	S2,.EQLM2(S1),EQ.PGS	;STORE SOMETHING AS JOB LIMIT
	LOAD	T1,.EQJOB(P4)		;GET BATCH JOB NAME
	STORE	T1,.EQJOB(S1)		;AS OUTPUT NAME
	LOAD	T1,.EQSEQ(P4),EQ.SEQ	;GET SEQUENCE NUMBER
	STORE	T1,.EQSEQ(S1),EQ.SEQ	;STORE IT
	LOAD	T1,.EQSEQ(P4),EQ.PRI	;GET EXT-PRIO FIELD
	STORE	T1,.EQSEQ(S1),EQ.PRI	;STORE IT
	LOAD	T1,.EQSPC(P4),EQ.PRO	;GET REQUEST PROTECTION
	STORE	T1,.EQSPC(S1),EQ.PRO	;STORE IT AWAY
	MOVSI	T1,EQISIZ(P4)		;BEGINING OF OS DEP DATA
	HRRI	T1,EQISIZ(S1)		;IN BOTH REQUESTS
	BLT	T1,EQHSIZ-1(S1)		;AND BLT IT


				;Q$LOGOUT IS CONTINUED ON THE NEXT PAGE
;  NOW, CREATE THE REQUEST POINTED TO BY 'S1'

LOGO.3:	PUSH	P,S1			;SAVE REQUEST ADDRESS
	MOVE	M,S1			;POINT TO IT
	MOVX	S1,.QIFNC		;LOAD INTERNAL FUNCTION CODE
	STORE	S1,.MSTYP(M),MS.TYP	;AND SAVE IN THE REQUEST
	PUSHJ	P,Q$CREATE		;CREATE THE REQUEST
	SKIPE	G$ERR##			;WAS THERE AN ERROR?
	  STOPCD(CRL,FATAL)		;++CREATE REJECTED LOGOUT DATA
	POP	P,AP			;GET THE ADR BACK
	ADR2PG	AP			;CONVERT TO PAGE NUMBER
	PUSHJ	P,M$RELP##		;RELEASE IT
	LOAD	S1,SPLJOB(P2),SPYDPA	;GET THE RETRIEVAL POINTER
	PUSHJ	P,F$RLRQ##		;AND DELETE THE OLD REQUEST
	MOVEI	H,HDRSPL		;GET A QUEUE HEADER
	MOVE	AP,P2			;CURRENT ENTRY
	LOAD	P2,.QELNK(P2),QE.PTN	;FIND THE NEXT
	PUSHJ	P,M$RFRE##		;RETURN SPL QUEUE CELL
	JRST	LOGO.2			;GET THE NEXT ENTRY
LOGO.4:	LOAD	P2,.QELNK(P2),QE.PTN	;FIND THE NEXT
	JRST	LOGO.2			;GET THE NEXT ENTRY

;HERE WHEN WE ARE ALL DONE
LOGO.5:	PJUMPE	P4,.POPJ##		;RETURN IF NOT A BATCH JOB
	ADR2PG	P4			;ELSE MAKE A PAGE NUMBER
	MOVE	AP,P4			;MOVE IT OVER
	PJRST	M$RELP			;AND RELEASE IT
SUBTTL	Global Subroutines

;ENTRY POINTS
	INTERN	Q$CKAF		;CHECK THE AFTER QUEUE
	INTERN	Q$FSPL		;FIND A MATCHING SPOOLING REQUEST
	INTERN	Q$INCL		;INCLUDE A FILE INTO A PROTOTYPE AREA
	INTERN	Q$FHDR		;FIND QUEUE HEADER
SUBTTL	Q$CKAF  -- Routine to check the AFTER queue

;Q$CKAF IS CALLED FROM THE MAIN PROCESSING LOOP TO CHECK THE
;	AFTER QUEUE FOR JOBS WHICH HAVE BECOME "OF AGE".  IF
;	ONE IS FOUND, IT IS MERGED INTO THE CORRECT SCHEDULING
;	QUEUE.
;
Q$CKAF:	MOVEI	H,HDRAFT		;AND POINT TO THE AFTER HEADER
	MOVE	S1,G$NOW##		;LOAD NOW TIME
	LOAD	AP,.QHLNK(H),QH.PTF	;GET TOP ENTRY IN THE QUEUE
	PJUMPE	AP,.POPJ##		;NOTHING THERE, RETURN
	CAMLE	S1,.QECRE(AP)		;HAS THE FIRST COME OF AGE?
	  JRST	CKAF.1			;YES, REMOVE IT
	MOVE	S2,.QECRE(AP)		;AND WHEN IT WILL
	PUSHJ	P,I$AGE##		;GET DIFFERENCE IN SECONDS
	PJRST	I$SVAL##		;SET SLEEP INTERVAL AND RETRUN

CKAF.1:	ZERO	.QEPID(AP)		;CLEAR THE INTERLOCK WORD
	MOVEM	S1,.QECRE(AP)		;AND SAVE THE CREATION TIME
	LOAD	S1,.QESTN(AP),QE.RQP	;GET REL QUEUE PTR
	ADDI	S1,TBLHDR		;AND MAKE A REAL QUEUE HEADER
	PUSHJ	P,M$MOVE##		;AND MOVE THE ENTRY
	JRST	Q$CKAF			;AND LOOP
SUBTTL	Q$FSPL  --  Find a SPL Queue entry

;Q$FSPL IS CALLED WITH T1 POINTING TO THE CANONICAL SPOOL MESSAGE.
;	IT WILL FIND THE APPROPRIATE REQUEST IN THE SPL QUEUE THAT
;	CAN ACCEPT ANOTHER FILE (Q$INCL) FOR THE CRITERIA DESCRIBED
;	IN THE DEFINITION OF THE CANONICAL SPOOL MESSAGE

;RETURN	E  = THE SPL QUEUE ENTRY (WILL MAKE ONE IF NONE)
;	AP = THE EXTERNAL QUEUE REQUEST READY FOR CREATE CALL

Q$FSPL:	LOAD	S1,CSM.DV(T1)		;S1 = THE REQUESTED DEVICE
	LOAD	S2,CSM.JB(T1),CS.LOC	;S2 = THE USERS DEFAULT STATION
	PUSHJ	P,I$MIDS##		;CREATE INTERNAL DEVICE SPECIFICATION
	SKIPN	S1			;DID THAT WORK
	  STOPCD(BDN,FATAL)		;++BAD DEVICE NAME INTERNALLY GENERATED
	MOVEM	S1,FSPL.A		;SAVE FOR LATER
	LOAD	S2,CSM.OI(T1)		;S2 = THE OWNER ID
	LOAD	T2,CSM.JB(T1),CS.JOB	;T2 = THE USERS JOB NUMBER
	LOAD	T3,CSM.ST(T1)		;T3 = STRUCTURE CONTAINING THE FILE
	LOAD	T4,CSM.FD(T1),CS.FDL	;T4 = THE LENGTH OF THE FD TO BE INCLUDED
	LOAD	E,<HDRSPL+.QHLNK>,QH.PTF  ;FIND FIRST IN SPL QUEUE
	JUMPE	E,FSPL.6		;EMPTY QUEUE, START IT
FSPL.1:	LOAD	TEMP,SPLOID(E)		;GET THE OWNER ID FOR THE REQUEST
	CAME	S2,TEMP			;FOR THE SAME USER
	  JRST	FSPL.5			;NO, TRY NEXT
	LOAD	TEMP,SPLJOB(E),SPYJOB	;GET THE JOB NUMBER
IFN FTSPLIT,<
	CAMN	T3,SPLSTR(E)		;FOR THE SAME STRUCTURE
>  ;END OF IFN FTSPLIT
	 CAME	TEMP,T2			;FOR THE SAME JOB
	  JRST	FSPL.5			;NO, TRY NEXT
	LOAD	TEMP,SPLRQZ(E),SPYLEN	;GET CURRENT SIZE
	ADDI	TEMP,FPMSIZ(T4)		;SIZE OF FILE TO BE INCLUDED
	CAILE	TEMP,1000		;REQUEST BECOME TOO BIG
	  JRST	FSPL.5			;YES, LOOK FOR ANOTHER
	MOVE	TEMP,SPLQUE(E)		;GET IDS FOR THE SPOOL REQUEST
	XOR	TEMP,S1			;FOR EASIER CHECKS
	JUMPE	TEMP,FSPL.4		;IF AN EXACT MATCH, INCLUDE THE FILE
	TXNE	TEMP,DV.GDN!DV.STN	;SAME QUEUE AND STATION
	  JRST	FSPL.5			;NO, TRY ANOTHER ENTRY
	TXNN	S1,DV.LLP!DV.LUP	;IS THIS REQUEST FOR LL: OR LU:
	  JRST	FSPL.2			;NO, DO DEVICE MATCHING
	TXNE	TEMP,DV.NUL		;YES, CURRENT GENERIC (XOR FLIPPED IT)
	  JRST	FSPL.5			;NO, CANNOT INCLUDE LL/LU WITH IT
	XOR	TEMP,S1			;RE-GET THE SPL QUEUE MODIFIERS
	TXNN	TEMP,DV.LLP!DV.LUP	;IS THIS REQUEST FOR LL/LU
	  JRST	FSPL.3			;NO, CHANGE IT TO LL/LU AND INCLUDE
	JRST	FSPL.5			;YES BUT WASN'T A COMPLETE MATCH

; Q$FSPL IS CONTINUED ON THE NEXT PAGE
; HERE TO DO DEVICE MATCHING AFTER LL:/LU: HAS BEEN TAKEN CARE OF

FSPL.2:	TXNE	S1,DV.NUL		;WANT GENERIC STATION
	  JRST	FSPL.4			;YES, THIS IS IT
	TXNE	TEMP,DV.NUL		;IF CURRENT REQUEST IS DEVICE SPECIFIC
	 TXNE	TEMP,DV.LLP!DV.LUP	; OR FOR LL: OR LU:
	   JRST	FSPL.5			;CAN'T MERGE SPECIFIC DEVICES
FSPL.3:	MOVEM	S1,SPLQUE(E)		;CONVERT OLD GENERIC TO NEW SPECIFIC
FSPL.4:	LOAD	S1,SPLJOB(E),SPYDPA	;GET THE DPA
	PUSHJ	P,F$RDRQ##		;READ THE REQUEST
	MOVE	AP,S1			;SAVE ADR IN AP
	LOAD	S1,SPLQUE(E),DV.STN	;GET CURRENT STATION
	STORE	S1,.EQSEQ(AP),EQ.DSN	;SET CURRENT DEFAULT STATION
	MOVE	S1,SPLQUE(E)		;NOW GET FULL IDS
	MOVE	T2,S1			;MAKE A COPY
	PUSHJ	P,I$MSDN##		;CONVERT TO SIXBIT DEVICE NAME
	TXNE	T2,DV.LLP		;NOW, WAS IT LL:
	  MOVSI	S1,'LL '		;YES, SET AS SUCH
	TXNE	T2,DV.LUP		;CHECK IF LU:
	  MOVSI	S1,'LU '		;THIS IS DONE FOR CORRECT VALUES
	STORE	S1,.EQRDV(AP)		;IN THE FAILSOFT DATA BASE
	POPJ	P,			;AND RETURN
FSPL.5:	LOAD	E,.QELNK(E),QE.PTN	;FIND THE NEXT
	JUMPN	E,FSPL.1		;LOOK AT IT IF THERE
FSPL.6:	MOVEI	H,HDRSPL		;GET A QUEUE HEADER
	PUSHJ	P,M$GFRE##		;GET A CELL
	MOVE	E,AP			;WANT IT IN E
	PUSHJ	P,M$ELNK##		;LINK IT IN AT THE END
	MOVE	S1,FSPL.A		;GET RESULT OF I$MIDS
	STORE	S1,SPLQUE(E)		;STORE INTO THE SPL QUEUE
	LOAD	S1,CSM.OI(T1)		;GET THE USERS ID
	STORE	S1,SPLOID(E)		;STORE THAT TOO
	LOAD	S1,CSM.JB(T1),CS.JOB	;GET THE JOB NUMBER
	STORE	S1,SPLJOB(E),SPYJOB	;SAVE THAT TOO
	ZERO	SPLJOB(E),SPYDPA	;NOT FAILSOFT YET
	LOAD	S1,CSM.ST(T1)		;GET THE STRUCTURE AGAIN
	MOVEM	S1,SPLSTR(E)		;SAVE IN SPL QUEUE
	MOVEI	S1,EQHSIZ		;ALL REQUEST START AT THIS SIZE
	STORE	S1,SPLRQZ(E),SPYLEN	;STORE THE INITIAL LENGTH
	PJRST	SPROTO			;BUILD THE PROTOTYPE AND RETURN

FSPL.A:	BLOCK	1			;SAVED IDS FROM I$MIDS
SUBTTL	Q$INCL  --  Append a File to a Request

;Q$INCL IS CALLED WITH:
;	AP = THE CURRENT SPOOL REQUEST
;	T1 = THE CANONICAL SPOOL MESSAGE FOR THE NEW FILE

;RETURNS WITH THE NEW FILE INCLUDED IN THE REQUEST AP

Q$INCL:	MOVE	S2,CSM.FP(T1)		;GET THE FLAG SETTINGS
	MOVEI	S1,1			;GET A BIT
	TXNE	S2,FP.SPL		;IS IT A SPOOLED FILE?
	  STORE	S1,.EQSEQ(AP),EQ.SPL	;YES, SET SPOOLING REQUEST FLAG
	LOAD	TEMP,.MSTYP(AP),MS.CNT	;GET LENGTH OF CURRENT MESSAGE
	MOVE	S2,TEMP			;MAKE A COPY
	LOAD	S1,CSM.FD(T1),CS.FDL	;S1 = LENGTH OF NEW FILE TO BE INCLUDED
	ADDI	TEMP,FPMSIZ(S1)		;TEMP = LENGTH OF NEW REQUEST
	STORE	TEMP,.MSTYP(AP),MS.CNT	;INCLUDE LENGTH FOR THIS FILE
	ADD	S2,AP			;AP + OLD LEN = NEXT FILE POINTER
	STORE	S1,.FPSIZ(S2),FP.FFS	;STORE LENGTH OF FD FOR NEW FILE
	MOVEI	TEMP,FPMSIZ		;LENGTH OF DEFAULT FP AREA
	STORE	TEMP,.FPSIZ(S2),FP.FHD	;STORE THAT AS WELL
	MOVE	TEMP,CSM.FP(T1)		;GET FLAGS REQUESTED BY THE CALLER
	STORE	TEMP,.FPINF(S2)		;AS FILE INFORMATION
	MOVEI	TEMP,1			;START AT LINE 1
	STORE	TEMP,.FPFST(S2)		;STORE STARTING POINT
	ZERO	.FPFR1(S2)		;/REPORT WORD 1
	ZERO	.FPFR2(S2)		; AND WORD 2
	MOVEI	S2,FPMSIZ(S2)		;BUMP TO FD AREA
	LOAD	TEMP,CSM.FD(T1),CS.FDA	;POINT TO THE FD TO INCLUDE
	HRLS	TEMP			;SET UP FOR BLT
	HRRI	TEMP,(S2)		;DESTINATION
	ADDI	S2,(S1)			;COMPUTE THE LAST LOCATION (S1 FROM ABOVE)
	BLT	TEMP,-1(S2)		;MOVE THE NEW FILE SPEC INTO THE REQUEST
	INCR	.EQSPC(AP),EQ.NUM	;BUMP NUMBER OF FILES IN REQUEST
	LOAD	S1,CSM.FS(T1)		;GET THE NUMBER OF BLOCKS IN THE FILE
	LOAD	S2,.EQLM2(AP),EQ.NBL	;GET BLOCKS IN REQUEST
	ADD	S1,S2			;INCLUDE THE NEW FILE
	STORE	S1,.EQLM2(AP),EQ.NBL	;UPDATE COUNTERS
	SKIPE	.EQJOB(AP)		;ANY JOB NAME YET
	  POPJ	P,			;YES, RETURN NOW
	LOAD	S1,CSM.EN(T1)		;GET THE 'ENTERED' NAME
	STORE	S1,.EQJOB(AP)		;MAKE THAT THE REQUEST NAME
	POPJ	P,			;AND RETURN
SUBTTL	Q$FHDR  --  Subroutine to find a Queue Header

;Q$FHDR IS CALLED WITH A IDS IN S1.  IT SCANS THE LIST OF EXTERNAL QUEUE
;	HEADERS FOR THE QUEUE REPRESENTING THE SPECIFIED DEVICE AND RETURNS
;	THE ADDRESS OF THE MATCHED QUEUE HEADER IN S1.  IF NO MATCH IS
;	FOUND, A "FALSE" INDICATION IS RETURNED.

Q$FHDR:	LOAD	T2,S1,DV.GDN		;GET GENERIC DEVICE NAME
	MOVEI	S1,TBLHDR		;LOAD ADDRESS OF FIRST QUEUE HDR
	HRLZI	T3,-NQUEUE		;MAKE AN AOBJN POINTER

FHDR.1:	LOAD	T4,.QHTYP(S1),QH.NAM	;GET THE QUEUE NAME
	CAMN	T2,T4			;GOT A MATCH?
	JRST	FHDR.2			;YES, MAKE SURE IT'S EXTERNAL
	ADDI	S1,QHSIZE		;MOVE UP TO THE NEXT ONE
	AOBJN	T3,FHDR.1		;AND LOOP
	PJRST	.FALSE##		;LOSE

FHDR.2:	LOAD	T2,.QHTYP(S1),QH.TYP	;GET THE QUEUE TYPE
	CAIE	T2,.QHTQS		;IS IT INTERNAL TO QUASAR?
	POPJ	P,			;NO, RETURN SUCCESS
	PJRST	.FALSE##		;YES, LOSE
SUBTTL	Subroutines

;	VALMSG		VALIDATE MESSAGES FROM KNOWN COMPONENTS
;	SRHUSE		SEARCH THE USE QUEUE FOR A GIVEN ITN
;	FNDREQ		UTILITY FOR KILL/MODIFY TO FIND ALL MATCHES IN A QUEUE
;	SPROTO		BUILD SPOOL TO QUEUE PROTOTYPE
;	MAJMOD		PERFORM MODIFY ON GLOBAL QUEUE PARAMETERS
;	FILMOD		PERFORM FILE-SPECIFIC MODIFIES
;	BLDKMS		BUILD THE KILL/MODIFY/DEFER ACKNOWLEDGEMENT STRING
;	TYPPLR		ROUTINE TO PLURALIZE A MESSAGE
;	SETRDE		SET THE EQ.RDE FLAG IN FAILSOFT REQUEST
SUBTTL	VALMSG  --  Routine to validate message from known component

;VALMSG IS CALLED WITH THE MINIMUM SIZE OF THE MESSAGE IN T1.  IF
;	THE MESSAGE IS INVALID, EXIT THROUGH QSR??% TO SET GLOBAL ERROR
;	CALLER MUST CHECK G$ERR TO DETERMINE IF OK TO PROCEED
;
;ON SUCCESS, LOCATION THSPSB IS FILLED WITH THE ADDRESS OF THE PSB
;	FOR THE SENDER

VALMSG:	LOAD	T2,.MSTYP(M),MS.CNT	;GET SIZE OF MESSAGE
	CAMGE	T2,T1			;GREATER OR EQUAL?
	  PJRST	E$MTS##			;INDICATE MESSAGE TOO SHORT
	MOVE	S1,G$SND##		;GET PID OF CURRENT SENDER
	PUSHJ	P,A$FPSB##		;FIND HIS PSB
	PJUMPE	S1,E$NKC##		;INDICATE NO PSB
	MOVEM	S1,THSPSB		;SAVE THE PSB
	POPJ	P,			;AND RETURN
SUBTTL	SRHUSE  --  Routine to search the USE queue

;CALL SRHUSE WITH T1 CONTAINING THE ITN.  IF THE ENTRY IS FOUND,
;	RETURN THE ADDRESS OF THE ENTRY IN T1, ELSE RETURN ZERO.

SRHUSE:	MOVEI	H,HDRUSE		;ADDRESS OF USE QUEUE HDR
	MOVE	T2,T1			;COPY THE ITN INTO T2
	LOAD	T1,.QHLNK(H),QH.PTF	;GET POINTER TO FIRST

SRHU.1:	JUMPE	T1,.POPJ##		;RETURN IN END OF CHAIN
	CAMN	T2,.QEITN(T1)		;COMPARE
	POPJ	P,			;MATCH!! RETURN
	LOAD	T1,.QELNK(T1),QE.PTN	;LOAD POINTER TO NEXT
	JRST	SRHU.1			;AND LOOP
SUBTTL	FNDREQ  --  Utility for KILL/MODIFY to find all matches

;FNDREQ IS CALLED WITH THE FOLLOWING ARGUMENTS
;	H  = THE QUEUE TO SEARCH
;	T1 = CODE (0 = KILL, -1 = MODIFY)
;	T2 = ADDRESS OF AN RDB TO MATCH AGAINST
;	T3 = RQP
;	T4 = SUBROUTINE TO CALL UPON FINDING A MATCH
;USES AP TO TRAVERSE THE QUEUE

;THE SUBROUTINE WHICH IS CALLED UPON FINDING A MATCHING REQUEST (@T4)
;	IS CALLED WITH THE ADDRESS OF THE REQUEST IN "AP", AND "H"
;	SETUP.  IT MAY USE T1-T4,AP, AND H.
;
;RETURNS NUMBER OF PROTECTION FAILURES IN S1

FNDREQ:	CLEARB	S1,FNDR.E		;CLEAR THE # PROT FAILS
	LOAD	AP,.QHLNK(H),QH.PTF	;GET FIRST IN QUEUE
	PJUMPE	AP,.POPJ##		;DON'T BOTHER IF EMPTY
	PUSHJ	P,.SAVE1##		;NEED ANOTHER REG
	MOVEM	T1,FNDR.A		;SAVE THE ORIGINAL ARGUMENTS
	MOVEM	T2,FNDR.B		;SO THAT THE CALLER CAN USE
	MOVEM	T3,FNDR.C		;THEM WHEN I CALL THE SUBROUTINE
	MOVEM	T4,FNDR.D		;UPON FINDING A MATCH

FNDR.1:	LOAD	T4,.QESTN(AP),QE.RQP	;GET RQP OF THIS ENTRY
	CAME	T4,FNDR.C		;FOR THE SAME QUEUE
	  JRST	FNDR.4			;NO, TRY THE NEXT IN THE QUEUE
	MOVE	S1,FNDR.B		;POINT TO THE MASK BLOCK FOR CHKMCH
	PUSHJ	P,I$RMCH##		;SEE IF THIS THE A MATCHING REQUEST
	JUMPE	S1,FNDR.4		;JUMP IF NOT A MATCH

;WITH A REQUEST IN AP THAT MATCHES, DO A LITTLE ACCESS CHECKING

	LOAD	S1,.QESEQ(AP),QE.RDE	;REQUEST ALREADY GONE?
	JUMPN	S1,FNDR.4		;YES, DON'T GET IT AGAIN
FNDR.2:	LOAD	S1,G$SID##		;GET MESSAGE SENDER ID
	LOAD	S2,.QEOID(AP)		;REQUEST OWNER ID
	CAMN	S2,S1			;DOING SOMETHING TO OWN REQUEST
	 SKIPL	FNDR.A			;YES, TRYING TO MODIFY IT
	  SKIPA				;NO, NEED TO DO ACCESS CHECKS
	   JRST	FNDR.3			;USER MAY ALWAYS MODIFY OWN REQUEST
	LOAD	S1,.QEPRT(AP),QE.PRO	;GET REQUEST PROTECTION
	HRLI	S1,ACC.KM		;CHECK ACCESS TYPE
	PUSHJ	P,I$CHAC##		;CHECK REQUESTORS RIGHTS
	JUMPN	S1,FNDR.3		;JUMP IF ACCESS ALLOWED
	AOS	FNDR.E			;COUNT PROTECTION FAILURES
	JRST	FNDR.4			;AND TRY THE NEXT


				;"FNDREQ" IS CONTINUED ON THE NEXT PAGE
				;CONTINUED FROM PREVIOUS PAGE

;SINCE ITS OK TO KILL/MODIFY THIS REQUEST, CALL THE CALLERS SUBROUTINE

FNDR.3:	PUSH	P,H			;MAKE US IMMUNE FROM IT
	LOAD	P1,.QELNK(AP),QE.PTN	;FIND NEXT IN CASE ITS REMOVED
	PUSHJ	P,@FNDR.D		;DO SOMETHING TO THIS REQUEST
	MOVE	AP,P1			;NOW POINT TO THE NEXT
	POP	P,H			;RESTORE ORIGINAL HEADER
	CAIN	H,HDRUSE		;A LITTLE SPECIAL CHECK
	  LOAD	AP,.QHLNK(H),QH.PTF	;THE USE QUEUE COULD BE CHANGED BY Q$CDIN
	SKIPA				;SKIP OVER THE LOAD, ALREADY HAVE AP
FNDR.4:	LOAD	AP,.QELNK(AP),QE.PTN	;FIND THE NEXT
	JUMPN	AP,FNDR.1		;CHECK FOR ALL ENTRIES
	MOVE	S1,FNDR.E		;LOAD NUMBER OF PROT FAILURES
	POPJ	P,			;QUEUE EXHAUSTED, RETURN

;LOCAL STORAGE

FNDR.A:	BLOCK	1			; KILL = 0, MODIFY = -1
FNDR.B:	BLOCK	1			;KILL/MODIFY REQ DESC BLOCK
FNDR.C:	BLOCK	1			;RQP OF REQUESTED QUEUE
FNDR.D:	BLOCK	1			;ADDRESS OF PROCESSING ROUTINE
FNDR.E:	BLOCK	1			;# OF PROTECTION FAILURES
SUBTTL	SPROTO  --  Build a CREATE Message Prototype

;SPROTO IS CALLED WITH T1 = THE CANONICAL SPOOL MESSAGE TO BUILD THE
;	PROTOTYPE EXTERNAL QUEUE ENTRY FOR THE FIRST FILE IN DEFERRED
;	MODE OR THE ONLY FILE IF IMMEDIATE MODE

;RETURN	AP = THE BUILT PROTOTYPE READY FOR Q$INCL

SPROTO:	PUSHJ	P,M$ACQP##		;GET A PAGE FOR THE PROTOTYPE
	PG2ADR	AP			;TO AN ADDRESS
	HRLI	T2,(AP)			;BUILD A BLT POINTER
	HRRI	T2,1(AP)		;NEED A CLEAN HEADER
	SETZM	(AP)			;CLEAR THE FIRST WORD
	BLT	T2,EQHSIZ-1(AP)		;GET IT ALL
	MOVX	T2,<INSVL.(EQHSIZ,MS.CNT)!.QIFNC>  ;GET SIZE AND INTERNAL CREATE
	STORE	T2,.MSTYP(AP)		;AS THE MESSAGE HEADER
	MOVX	T2,<%%.QSR,,EQHSIZ>	;VERSION,,HEADER SIZE
	STORE	T2,.EQLEN(AP)		;AS EXTERNAL QUEUE LENGTHS
	LOAD	T2,CSM.DV(T1)		;GET THE REQUESTED DEVICE
	STORE	T2,.EQRDV(AP)		;STORE FOR CREATE
	LOAD	T2,CSM.JB(T1),CS.LOC	;THE DEFAULT STATION NUMBER
	STORE	T2,.EQSEQ(AP),EQ.DSN	;SAVE FOR CREATE
	MOVE	T2,G$SPRT##		;GET SYSTEM PROTECTION OF SPOOLED FILES
	STORE	T2,.EQSPC(AP),EQ.PRO	;AS THE REQUEST PROTECTION
	MOVE	S1,T1			;POINT S1 TO CSM
	PJRST	I$SMEQ##		;AND MOVE SYSTEM DEPENDENT
					; STUFF AN RETURN
SUBTTL	MAJMOD  --  Perform MODIFY on Major Queue Items

;CALLED BY Q$MODIFY WITH
;	AP = THE ENTRY BEING MODIFIED (.EQxxx FORM)
;	S1 = GROUP 0 MODIFY BLOCK

MAJMOD:	PUSHJ	P,.SAVE3##		;SAVE A FEW REGS FIRST
	LOAD	P1,MOD.GN(S1),MODGLN	;NUMBER OF GROUP 0 ELEMENTS
	SOJLE	P1,.POPJ##		;0 IS ACCEPTABLE, ADJUST FOR THE LOOP
	CAILE	P1,NMAJPM		;MORE THAN CURRENTLY IMPLEMENTED
	  MOVEI	P1,NMAJPM		;YES, USE ONLY THE KNOWN VALUES
	MOVNS	P1			;NEGATE IT
	HRLZS	P1			;P1 = AOBJN POINTER
	MOVEI	P2,MOD.GE(S1)		;POINT TO FIRST GROUP ELEMENT
MAJM.1:	MOVE	P3,0(P2)		;GET AN ELEMENT
	CAME	P3,[-1]			;DID IT CHANGE
	  XCT	MAJMTB(P1)		;YES, STORE NEW VALUE
	INCR	P2			;TO NEXT ELEMENT
	AOBJN	P1,MAJM.1		;GET THEM ALL
	POPJ	P,			;RETURN TO Q$MODIFY FOR NEXT GROUP

MAJMTB:	STORE	P3,.EQAFT(AP)		; 0 = /AFTER
	STORE	P3,.EQSEQ(AP),EQ.PRI	; 1 = /PRIORITY
	STORE	P3,.EQDED(AP)		; 2 = /DEADLINE
	STORE	P3,.EQSPC(AP),EQ.PRO	; 3 = /PROTECTION

NMAJPM==<.-MAJMTB>			;NUMBER CURRENTLY IMPLEMENTED
SUBTTL	FILMOD  --  Perform File-Specific MODIFY

;CALLED BY Q$MODIFY WITH
;	AP = THE ENTRY BEING MODIFIED (.EQxxx FORM)
;	S1 = GROUP 2 MODIFY BLOCK
;RETURNS S1 = NUMBER OF FILES THAT MATCHED THE SPEC

FILMOD:	PUSHJ	P,.SAVET##		;SAVE T1-T4
	PUSHJ	P,.SAVE2##		;AND P1-P2
	ZERO	FILM.E			;CLEAR FILE COUNT FOR THIS PASS
	LOAD	T2,MOD.GN(S1),MODGLN	;NUMBER OF WORDS IN GROUP 2
	CAIG	T2,MOD.FD		;ENOUGH FOR THE INFO WORD
	  JRST	FILM.5			;NO, IGNORE IT
	LOAD	T1,MOD.FI(S1),MODFDL	;T1 = LENGTH OF AN FD IN MODIFY
	CAIL	T1,FDMSIZ		;RANGE CHECK FOR TOO SMALL
	 CAILE	T1,FDXSIZ		;OR TOO LARGE
	  JRST	FILM.5			;OUT OF RANGE, IGNORE IT
	MOVE	T3,T1			;NOW CHECK THE NUMBER OF ELEMENTS
	ADDI	T3,MOD.FD(T1)		;COMPUTE 2*FD + OFFSET
	SUB	T2,T3			;T2 = NUMBER OF REAL ELEMENTS
	JUMPLE	T2,FILM.5		;NONE OR BAD, IGNORE IT
	CAILE	T2,NFILPM		;MORE THAN I KNOW ABOUT
	  MOVEI	T2,NFILPM		;YES, USE ONLY WHATS THERE
	ADDI	T3,(S1)			;T3 = FIRST PARM
	MOVEM	T3,FILM.D		;SAVE FOR LOOP
	MOVEI	T3,MOD.FD(S1)		;FIRST FD
	MOVEM	T3,FILM.A		;SAVE FOR I$FMCH
	ADDI	T3,(T1)			;MASKS
	MOVEM	T3,FILM.C		;SAVE THAT TOO
	LOAD	T3,.EQSPC(AP),EQ.NUM	;T3 = NUMBER OF FILES IN REQUEST
	LOAD	T4,.EQLEN(AP),EQ.LOH	;NOW FIND FIRST FILE IN REQUEST
	ADDI	T4,(AP)			;T4 = FIRST FP AREA IN ORIGINAL

;NOW LOOP THROUGH ALL FILES IN ORIGINAL LOOKING TO MATCH MODIFY REQUEST
;DURING THE LOOP:
;	T1 = THE LENGTH OF FD
;	T2 = NUMBER OF ELEMENTS SPECIFIED IN MODIFY
;	T3 = NUMBER OF FILES IN ORIGINAL REQUEST
;	T4 = FP AREA IN ORIGINAL

FILM.1:	LOAD	S2,.FPSIZ(T4),FP.FFS	;GET SIZE OF THIS FD
	LOAD	P1,.FPSIZ(T4),FP.FHD	;GET SIZE OF THIS FP
	ADDI	P1,(T4)			;P1 = FD FOR THE ORIGINAL REQUEST
	CAIE	S2,(T1)			;FD LENGTHS MATCH
	  JRST	FILM.4			;NO, THEN MASKS WON'T EITHER
	MOVEM	P1,FILM.B		;SAVE ADDR OF 2ND FD FOR I$FMCH
	MOVEI	S1,FILM.A		;ADDRESS OF ARGUMENT BLOCK
	PUSHJ	P,I$FMCH##		;COMPARE THE FD AREAS
	JUMPE	S1,FILM.3		;JUMP IF THEY DIDN'T
	LOAD	S1,.FPINF(T4),FP.IGN	;DOES THIS FILE EXIST
	JUMPN	S1,FILM.3		;JUMP IF /REMOVE'D BEFORE


;FILMOD IF CONTINUED ON THE NEXT PAGE
;CONTINUE WITH FILMOD

	AOS	FILM.E			;ADD A MATCH
	MOVN	P2,T2			;GET NUMBER OF ELEMENTS
	HRLZS	P2			;P2 = AOBJN POINTER
	MOVE	S2,FILM.D		;POINT TO FIRST ELEMENT
FILM.2:	MOVE	S1,0(S2)		;GET AN ELEMENT
	CAME	S1,[-1]			;DID IT CHANGE
	  XCT	FILMTB(P2)		;YES, STORE NEW VALUE
	INCR	S2			;POINT TO NEXT
	AOBJN	P2,FILM.2		;GET THEM ALL
FILM.3:	MOVE	S2,T1			;COPY THE FD LENGTH
FILM.4:	MOVE	T4,P1			;FD AREA
	ADDI	T4,(S2)			;+FD LENGTH = NEXT FILE IN ORIGINAL
	SOJG	T3,FILM.1		;GET THE NEXT FILE
FILM.5:	MOVE	S1,FILM.E		;GET NUMBER OF FILES MODIFIED
	POPJ	P,			;ALL DONE WITH GROUP 2

FILMTB:	STORE	S1,.FPINF(T4),FP.IGN	; 0 = /REMOVE
	STORE	S1,.FPINF(T4),FP.NFH	; 1 = /HEADER
	STORE	S1,.FPINF(T4),FP.FSP	; 2 = /SPACING
	STORE	S1,.FPINF(T4),FP.FPF	; 3 = /PAPER
	STORE	S1,.FPINF(T4),FP.FFF	; 4 = /FILE
	STORE	S1,.FPINF(T4),FP.DEL	; 5 = /DELETE
	STORE	S1,.FPINF(T4),FP.FCY	; 6 = /COPIES
	STORE	S1,.FPFR1(T4)		; 7 = /REPORT (1ST HALF)
	STORE	S1,.FPFR2(T4)		; 8 = /REPORT (2ND HALF)
	STORE	S1,.FPFST(T4)		; 9 = /TAG OR /BEGIN

NFILPM==<.-FILMTB>			;NUMBER CURRENTLY IMPLEMENTED

FILM.A:	BLOCK	1			;ADDRESS OF 1ST FD (SPEC)
FILM.B:	BLOCK	1			;ADDRESS OF 2ND FD (ORIGINAL)
FILM.C:	BLOCK	1			;ADDRESS OF MASKS
FILM.D:	BLOCK	1			;ADDRESS OF FIRST GROUP ELEMENT
FILM.E:	BLOCK	1			;NUMBER OF FILES MODIFIED THIS PASS
SUBTTL	BLDKMS  --  Routine to build KILL/MODIFY/DEFER acknowledgement string

;BLDKMS IS CALLED WITH T1 POINTING TO AN ARGUMENT BLOCK CONTAINING:

;	+0 = NUMBER OF "THINGS" DONE (KILLED, MODIFIED, DEFERRED)
;	+1 = NUMBER OF FILES (MODIFY, DEFER)
;	+2 = NUMBER OF CANCEL MESSAGES SENT (KILL ONLY)
;	+3 = NUMBER OF PROTECTION FAILURES

; AND T2 = "THING" STRING ADDRESS (ASCIZ FORMAT)

;ASSEMBLES THE CORRECT MESSAGE (USES TYPPLR TO PLURALIZE THE PARTS) AND CALLS G$MSND

BLDKMS:	SKIPN	G$ACK##			;CALLER WANT THIS MESSAGE
	  POPJ	P,			;NO, NEVER MIND
	MOVE	S1,0(T1)		;GET "THINGS" DONE
	PUSHJ	P,TYPPLJ		;TYPE CORRECT "JOB(S)"
	SKIPL	T3,1(T1)		;DON'T TYPE FILES
	 SKIPN	0(T1)			;YES, BUT WERE THERE ANY JOBS
	  JRST	BLDK.1			;DONT BOTHER
	MOVEI	S1,[ASCIZ/ (/]		;ALIGN THE OUTPUT
	SKIPN	T3			;ANY FILES
	  MOVEI	S1,[ASCIZ/ (But /]	;NO, POINT THAT OUT
	PUSHJ	P,G$CSTG##		;INCLUDE THAT
	MOVE	S1,T3			;GET NUMBER OF FILES
	MOVEI	S2,[ASCIZ/ File/]	;SAY FILE(S) INSTEAD OF JOB(S)
	PUSHJ	P,TYPPLR		;OUTPUT CORRECT ENGLISH
	MOVEI	S1,[ASCIZ/)/]		;MAKE IT LOOK NICE
	PUSHJ	P,G$CSTG##		;BY ADDING THE CLOSURE
BLDK.1:	MOVE	S1,T2			;NOW FOR THE "THING" STRING
	PUSHJ	P,G$CSTG##		;APPEND THE STRING
	SKIPN	T2,2(T1)		;ANY JOBS CANCELLED
	  JRST	BLDK.2			;NO, AVOID THE OUTPUT
	MOVEI	S1,[ASCIZ/, /]		;PUNCTUATION
	PUSHJ	P,G$CSTG##		;ALIGN THE NEXT COMMENT
	MOVE	S1,T2			;GET NUMBER OF THEM DONE
	PUSHJ	P,TYPPLJ		;AND SAY "n JOB(S)"
	MOVEI	S1,[ASCIZ/ Cancelled/]	;WHAT WAS DONE
	PUSHJ	P,G$CSTG##		;INCLUDE THAT
BLDK.2:	SKIPN	T3,3(T1)		;ANY PROTECTION FAILURES
	  JRST	BLDK.3			;NO, AVOID THAT TOO
	MOVEI	S1,[ASCIZ/, /]		;PUNCTUATION
	PUSHJ	P,G$CSTG##		;ALIGN THE NEXT COMMENT
	MOVE	S1,T3			;GET THE COUNT OF PROTECTION FAILURES
	MOVEI	S2,[ASCIZ/ Protection Failure/]
	PUSHJ	P,TYPPLR		;TYPE NUMBER AND PLURALIZE THE MESSAGE
BLDK.3:	ZERO	S1			;THIS IS AN INFORMATIONAL MESSAGE
	PJRST	G$MSND##		;SEND "ACK" AND RETURN
SUBTTL	TYPPLR  --  Routine to pluralize a message

;TYPPLR IS CALLED WITH S1 CONTAINING A NUMBER "N" AND S2 CONTAINING
;	THE ADDRESS OF AN ASCIZ STRING "FOO".  IF N IS 0, TYPPLR
;	WILL SEND TO THE CURRENT TEXT MESSAGE "NO FOOS".  IF N IS 1,
;	"1 FOO" WILL BE SENT, OTHERWISE, "N FOOS" WILL BE SENT.
;
;ENTER AT "TYPPLJ" IF THE STRING IS "JOB".

TYPPLJ:	MOVEI	S2,[ASCIZ \ Job\]	;LOAD DEFAULT STRING
TYPPLR:	MOVEM	S2,TYPP.A		;SAVE ADDRESS OF THE STRING
	CAIE	S1,1			;ONE "THING" DONE
	  JRST	TYPP.1			;NO, TRY ANOTHER
	PUSHJ	P,G$CDEC##		;TYPE OUT THE ONE
	MOVE	S1,TYPP.A		;GET STRING
	PJRST	G$CSTG##		;TYPE IT AND RETURN

TYPP.1:	JUMPN	S1,TYPP.2		;JUMP IF IT WAS IT "SOME THINGS"
	MOVEI	S1,[ASCIZ /No/]		;GET STRING "NO" INSTEAD OF TYPING 0
	PUSHJ	P,G$CSTG##		;TYPE A STRING
	SKIPA				;SKIP ALTERNATE ENTRY
TYPP.2:	PUSHJ	P,G$CDEC##		;TYPE DECIMAL NUMBER
	MOVE	S1,TYPP.A		;GET STRING BACK
	PUSHJ	P,G$CSTG##		;AND TYPE IT
	MOVEI	S1,"s"			;AND PLURAL ENDING
	PJRST	G$CCHR##		;TYPE IT AND RETURN

TYPP.A:	BLOCK	1			;LOCAL STORAGE
SUBTTL	SETRDE  --  Routine to set "REQUEST DOESN'T EXIST" in a request

;SETRDE IS CALLED WITH THE ADDRESS OF A QUEUE ENTRY (QE) IN T1.  IT READS
;	IN THE FAILSOFT COPY, SETS EQ.RDE, SETS QE.RDE, WRITES IT OUT,
;	AND STORES THE NEW DPA BACK IN THE QUEUE ENTRY.

SETRDE:	SAVE	AP			;SAVE CALLERS REGISTER
	LOAD	S1,.QESTN(T1),QE.DPA	;GET THE DPA
	PUSHJ	P,F$RDRQ##		;AND READ THE REQUEST
	MOVEI	S2,1			;LOAD A BIT
	STORE	S2,.EQSEQ(S1),EQ.RDE	;SET RDE BIT
	STORE	S2,.QESEQ(T1),QE.RDE	;SET IN THE INTERNAL QUEUE AS WELL
	MOVE	AP,S1			;SAVE ADDRESS IN AP
	PUSHJ	P,F$WRRQ##		;WRITE OUT THE REQUEST
	LOAD	S2,.QESTN(T1),QE.DPA	;LOAD THE OLD DPA
	STORE	S1,.QESTN(T1),QE.DPA	;STORE THE NEW DPA
	MOVE	S1,S2			;COPY OVER THE OLD ONE INTO S1
	PUSHJ	P,F$RLRQ##		;RELEASE FAILSOFT SPACE
	ADR2PG	AP			;CONVERT ADDRESS TO A PAGE #
	PJRST	M$RELP##		;RELEASE THE PAGE AND RETURN
	END