Google
 

Trailing-Edge - PDP-10 Archives - bb-d868a-bm - 3-sources/qsrsch.mac
There are 48 other files named qsrsch.mac in the archive. Click here to see a list.
TITLE	QSRSCH  --  Scheduler and Queue Dependent Functions
SUBTTL	Chuck O'Toole  12 JUL 77 (+JBS 13-MAY-77)




;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1974, 1975, 1976, 1977, 1978 BY
;	DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

	SEARCH	QSRMAC		;PARAMETER FILE

	PROLOGUE(QSRSCH)	;GENERATE THE NECESSARY SYMBOLS

COMMENT\

	Entry points found in QSRSCH

S$INIT	Initialization Entry Point
S$SCHD	Entry called to attempt a scheduling pass for any available PSB
S$RUSE	Notification of Removal of a job from the USE queue
S$RELE	Extension of Q$RELEASE for queue dependent functions


	Base Entry Vectors For Processing Queues

S$AFTR	The AFTER Queue
S$INPT	The Input Queue
S$LPT	The Line Printer Queue
S$PTP	The Paper Tape Punch Queue
S$CDP	The Card Punch Queue (EQUAL TO S$IBM AND S$JOB FOR DN60)
S$PLT	The Plotter Queue
S$RDE	The RDE Queue

	STOPCDs found in QSRSCH

IAO	ILLEGAL AFTER QUEUE OPERATION

\
SUBTTL	Some Useful Definitions

;MACRO TO FILL IN THE DEFAULTS FOR AN ENTRY

DEFINE	FILLIN(AC,LOCN,FIELD,DEFALT,%DUMMY),<
	LOAD	(AC,LOCN,FIELD)
	XLIST
	JUMPN	AC,%DUMMY
	MOVX	(AC,DEFALT)
	STORE	(AC,LOCN,FIELD)
%DUMMY:
	LIST
	SALL
>  ;END OF DEFINE FILLIN
SUBTTL	LOCAL STORAGE CELLS


SCHZER:				;FIRST LOCATION TO BE ZERO'ED AT INITIALIZATION

KSYSTM:	BLOCK	1		;SECONDS UNTIL KSYS TIME

FRMTYP:	BLOCK	FRMNUM		;TABLE FOR FORM NAMES (MASKED WITH PSYUNI)
FRMCNT:	BLOCK	FRMNUM		;PAGES REQUEST FOR THAT TYPE
FRMADR:	BLOCK	FRMNUM		;FIRST ENTRY THAT REQUESTED THAT TYPE

INPNJP:	BLOCK	1		;NUMBER OF JOBS CURRENTLY RUNNING
INPITN:	BLOCK	INPNUM		;TABLE OF BATCH JOB ITN'S
INPUNI:	BLOCK	INPNUM		;UNIQUENESS AND OTHER PARAMETERS
	IN.UNI==1B0		;INDICATES /UNIQUE:YES


ZERLAS==.-1			;LAST LOCATION TO BE ZERO'ED AT INITIALIZATION
SUBTTL	Initialization Entry Point

;CALLED BY QUASAR AT STARTUP TO INITIALIZE THE SCHEDULER DATA BASE

	INTERN	S$INIT

S$INIT:	ZERO	SCHZER			;CLEAR THE DATA BASE
	MOVE	S1,[SCHZER,,SCHZER+1]	;STANDARD BLT
	BLT	S1,ZERLAS		;GET IT ALL
	POPJ	P,			;AND RETURN
SUBTTL	Scheduler Entry Point

	INTERN	S$SCHD

S$SCHD:	PUSHJ	P,.SAVE4##		;SAVE P1-P4
	PUSHJ	P,I$KSYS##		;GET TIME TO KSYS
	MOVEM	S1,KSYSTM		;REMEMBER FOR LATER CHECKS
	SKIPE	S1			;IS IT SET TO HAPPEN
	  PUSHJ	P,RETMIN		;YES, SET UP TO WATCH IT EVERY MINUTE
SCHD.0:	LOAD	P4,HDRPSB##+.QHLNK,QH.PTF  ;FIND THE FIRST PSB
SCHD.1:	PJUMPE	P4,.POPJ##		;RETURN IF END OF PSB LIST
	MOVX	T1,PSYSCH		;THE AVAILABLE FOR SCHEDULING BIT
	TDNN	T1,PSBSTS(P4)		;IS THIS PROGRAM READY
	  JRST	SCHD.2			;NO, TRY THE NEXT
	MOVX	T1,PSYRDE		;GET RDE TEST
	TDNN	T1,PSBSTS(P4)		;CAN PROGRAM HANDLE RDE JOBS
	JRST	SCHD.5			;NO, CONTINUE ON
	MOVEI	H,HDRRDE##		;YES, LOAD THE HEADER
	PUSHJ	P,RDESCH		;SCHEDULE AN RDE JOB
	JUMPN	AP,SCHD.6		;AND JUMP IF WE GOT ONE
SCHD.5:	LOAD	H,PSBSTS(P4),PSYHDR	;FIND THE QUEUE HEADER FOR THIS PSB
	LOAD	T1,.QHPAG(H),QH.SCH	;GET THE ADDRESS OF THE SCHEDULING ROUTINE
	PUSHJ	P,SCHSCH(T1)		;ATTEMPT A SCHEDULING PASS
	JUMPE	AP,SCHD.2		;JUMP IF NOTHING WAS SCHEDULED
SCHD.6:	INCR	PSBSTS(P4),PSYNJP	;ACCOUNT FOR THE NEW JOB
	LOAD	P3,.QELNK(P4),QE.PTP	;REMEMBER MY PREVIOUS
	PUSH	P,H			;SAVE THE HEADER AROUND Q$NEXT
	PUSHJ	P,NXTRCK		;COMPUTE TIME OF NEXT CHECKPOINT
	STORE	S1,PSBRCK(P4)		;SAVE FOR LATER LOOPS
	MOVEI	T1,(P4)			;PSB ADDRESS INTO T1
	PUSHJ	P,Q$NEXTJOB##		;SEND A NEXTJOB
	POP	P,H			;RESTORE H
	LOAD	P2,.QHLNK(H),QH.PTF	;IN CASE P4 IS THE FIRST PSB
	SKIPE	P3			;DID IT HAVE A PREVIOUS
	  LOAD	P2,.QELNK(P3),QE.PTN	;YES, FIND ITS NEXT
	CAIE	P2,(P4)			;IN CASE COMPONENT DISAPPEARED
	  JRST	SCHD.0			;IT DID, START AT THE TOP
	JRST	SCHD.1			;NO, FIND ANOTHER JOB FOR IT (MAYBE)
SCHD.2:	LOAD	S1,PSBSTS(P4),PSYNJP	;THIS COMPONENT PROCESSING JOBS
	JUMPE	S1,SCHD.4		;JUMP IF NO NEED FOR CHECKPOINTS
	MOVE	S1,G$NOW##		;CURRENT TIME
	MOVE	S2,PSBRCK(P4)		;TIME TO ASK FOR CHECKPOINT
	CAMGE	S1,S2			;TIME YET
	  JRST	SCHD.3			;NO, SET UP SLEEP TIMERS
	PUSHJ	P,RETMIN		;COME BACK IN 1 MINUTE
	PUSHJ	P,NXTRCK		;COMPUTE TIME OF NEXT ONE
	STORE	S1,PSBRCK(P4)		;SAVE FOR LATER CHECKS
	MOVE	T1,PSBPID(P4)		;COMPONENTS PID
	LOAD	P4,.QELNK(P4),QE.PTN	;FIND THE NEXT NOW AS THE SEND MAY FAIL
	PUSHJ	P,A$RCK##		;REQUEST THE CHECKPOINT (SEE IF STILL ALIVE)
	JRST	SCHD.1			;AND TRY THE NEXT PSB
SCHD.3:	PUSHJ	P,I$AGE##		;COMPUTE TIME TILL NEXT CHECKPOINT REQUEST
	PUSHJ	P,I$SVAL##		;SET UP FOR SLEEP
SCHD.4:	LOAD	P4,.QELNK(P4),QE.PTN	;FIND THE NEXT PSB
	JRST	SCHD.1			;AND TRY SCHEDULING FOR IT
SUBTTL	Scheduler Functions

;THESE ROUTINES ARE POINTED TO BY THE QUEUE HEADER (QH.SCH)

;CALL	Arguments in Specified AC's

;	LOAD	xx,<HDRyyy+.QHPAG>,QH.SCH	;GET THE ADDRESS
;	PUSHJ	P,disp(xx)

;WHERE disp = SCHLNK TO LINK ENTRY 'AP' INTO QUEUE 'H'
;			CALLED BY EVERYBODY

;	    = SCHSCH TO SCHEDULE FOR QUEUE 'H' FOR KNOWN COMPONENT WHOSE PSB IS IN 'P4'
;			RETURNS 'AP' AS ENTRY SELECTED (0 IF NONE)
;			CALLED BY S$SCHD

;	    = SCHDEF TO FILL IN THE DEFAULTS IN CREATE MESSAGE 'M'
;			CALLED BY Q$CREATE

;	    = SCHMOD TO DO QUEUE DEPENDENT MODIFY FOR GROUP 'S1' ON EXTERNAL ENTRY 'AP'
;			CALLED BY Q$MODIFY

;SCHSCH ENTRY IS CALLED BY S$SCHD AND HAS FULL USE OF ALL REGISTERS

;S1,S2, AND TEMP ARE ALWAYS SCRATCH AC'S
	INTERN	S$LPT,S$INPT,S$AFTR,S$CDP,S$PTP,S$PLT,S$RDE
	INTERN	S$IBM,S$JOB

S$LPT:	JRST	OUTLNK			;LINK IN A NEW ENTRY
	JRST	OUTSCH			;SCHEDULE AN ENTRY
	JRST	LPTDEF			;FILL IN LINE PRINTER DEFAULTS
	JRST	OUTMOD			;DO GROUP 1 MODIFY

S$INPT:	JRST	INPLNK			;LINK IN A NEW ENTRY
	JRST	INPSCH			;SCHEDULE A NEW JOB
	JRST	INPDEF			;FILL IN THE DEFAULTS
	JRST	INPMOD			;DO GROUP 1 MODIFY

S$AFTR:	JRST	AFTLNK			;LINK IN ORDER OF EXPIRATION
	JFCL				;FALL INTO THE STOPCD
	JFCL				;FALL INTO THE STOPCD
	STOPCD	(IAO,FATAL)		;++ILLEGAL AFTER QUEUE OPERATION

S$IBM:
S$JOB:
S$CDP:	JRST	OUTLNK			;LINK IN A NEW ENTRY
	JRST	OUTSCH			;SCHEDULE AN ENTRY
	JRST	CDPDEF			;FILL IN CARD PUNCH DEFAULTS
	JRST	OUTMOD			;DO GROUP 1 MODIFY

S$PTP:	JRST	OUTLNK			;LINK IN A NEW ENTRY
	JRST	OUTSCH			;SCHEDULE AN ENTRY
	JRST	PTPDEF			;FILL IN PAPER TAPE PUNCH DEFAULTS
	JRST	OUTMOD			;DO GROUP 1 MODIFY

S$PLT:	JRST	OUTLNK			;LINK IN A NEW ENTRY
	JRST	OUTSCH			;SCHEDULE AN ENTRY
	JRST	PLTDEF			;FILL IN PLOTTER DEFAULTS
	JRST	OUTMOD			;DO GROUP 1 MODIFY

S$RDE:	JRST	M$ELNK##		;LINK IN AT THE END
	JFCL				;FALL INTO STOPCD
	JFCL				;FALL INTO STOPCD
	STOPCD	(IRO,FATAL)		;++ILLEGAL RDE QUEUE OPERATION
; THE LINKIN FUNCTION FOR THE AFTER QUEUE

AFTLNK:	SAVE	E			;SAVE CALLERS ACCUMULATOR
	LOAD	S1,.QECRE(AP)		;GET THIS ENTRIES AFTER PARAMETER
	LOAD	E,.QHLNK(H),QH.PTF	;FIND THE FIRST ENTRY IN THIS QUEUE
AFTR.1:	PJUMPE	E,M$ELNK##		;END OF THE QUEUE, MAKE THIS LAST
	CAMGE	S1,.QECRE(E)		;AFTER PARAMETER OF THAT ENTRY
	  PJRST	M$LINK##		;MINE WILL EXPIRE FIRST, E IS THE SUCCESSOR
	LOAD	E,.QELNK(E),QE.PTN	;FOLLOW THE CHAIN
	JRST	AFTR.1			;AND KEEP LOOKING

; EXIT ROUTINE FOR THE SCHEDULING MODULES, RETURNS AP = 0

RETTMR:	PUSHJ	P,RETMIN		;RETURN IN 1 MINUTE
RETZER:	ZERO	AP			;INDICATE NO JOB SELECTED
	POPJ	P,			;RETURN
RETMIN:	MOVEI	S1,^D60			;SET WAKEUP FOR 1 MINUTE
	PJRST	I$SVAL##		;TO SEE IF CONDITIONS CHANGE
;THE OUTPUT SCHEDULING ROUTINE CALLED BY S$SCHD ENTRY

OUTSCH:	LOAD	AP,.QHLNK(H),QH.PTF	;GET POINTER TO FIRST JOB
	PJUMPE	AP,.POPJ##		;NO JOBS, JUST RETURN EMPTY HANDED
	LOAD	P1,PSBSTS(P4),PSYNJP	;NUMBER CURRENTLY BEING PROCESSED
	JUMPN	P1,RETZER		;CAN ONLY PROCESS 1 AT A TIME
	LOAD	T1,PSBIN2(P4),PSYPGS	;GET MLIMIT
	LOAD	T2,PSBSTS(P4)		;GET THE STATUS
	PUSHJ	P,I$OPER##		;FIND OUT IF AN OPERATOR ON DUTY
	JUMPN	S1,OUTS.0		;GO IF THERE IS ONE PRESENT
	TXON	T2,PSYFRZ		;NO, NOBODY TO CHANGE FROMS
	  PUSHJ	P,RETMIN		;NOT FROZEN ALREADY, COME BACK LATER
OUTS.0:	SETO	P3,			;CLEAR FORMS CHANGE CODE
	LOAD	S1,PSBIN2(P4),PSYNXT	;GET 'NEXT' JOB REQUESTED BY OPERATOR
	JUMPE	S1,OUTS.2		;NONE, BEGIN SCHEDULING LOOP
	MOVE	P2,AP			;SAVE CURRENT POINTER
	PUSHJ	P,FNDNXT		;FIND REQUESTED JOB IN THE QUEUE
	JUMPE	AP,OUTS.1		;DIDN'T FIND IT, RESUME NORMAL PATH
	PUSHJ	P,OUTREJ		;DO REJECTION TESTS NOW
	JUMPE	S1,OUTS.1		;JUMP IF REJECTED
	ZERO	PSBIN2(P4),PSYNXT	;CLEAR NEXT IF FOUND
	LOAD	S1,.QELM1(AP)		;GET FORMS TYPE FOR THIS ENTRY
	ANDX	S1,FRMSK1		;MAKE IT UNIQUE
	STORE	S1,PSBIN1(P4)		;THAT IS THE CURRENTLY MOUNTED FORMS
	POPJ	P,			;RETURN WITH THAT JOB
OUTS.1:	MOVE	AP,P2			;GET ORIGINAL AP BACK
OUTS.2:	LOAD	P1,.QELM2(AP),QE.PGS	;GET THIS JOBS PAGE REQUEST
	CAILE	P1,(T1)			;TOO LARGE
	  JRST	OUTS.3			;YES, IT'LL HAVE TO WAIT
	PUSHJ	P,OUTREJ		;DO SOME SIMPLE REJECTION TESTS
	JUMPE	S1,OUTS.3		;JUMP IF REJECTED
	MOVE	P1,.QELM1(AP)		;GET FORMS TYPE FOR THIS ENTRY
	ANDX	P1,FRMSK1		;MAKE IT UNIQUE
	CAMN	P1,PSBIN1(P4)		;SAME AS CURRENTLY MOUNTED
	  POPJ	P,			;YES, TAKE THIS ONE
	SKIPLE	S1,KSYSTM		;IS KSYS SET?
	CAIG	S1,FRMKSY*^D60		;YES, TO CLOSE FOR COMFORT?
	JUMPN	S1,OUTS.3		;YES, DON'T CHANGE FORMS
	TXNN	T2,PSYFRZ		;NO, ARE FORMS CURRENTLY FROZEN
	  PUSHJ	P,OUTFRM		;ACCUMULATE FORMS CHANGE PARAMETERS
OUTS.3:	LOAD	AP,.QELNK(AP),QE.PTN	;FIND THE NEXT ENTRY
	JUMPN	AP,OUTS.2		;AND TRY TO SELECT IT
	PJUMPL	P3,.POPJ##		;RETURN 0 IF NO FORMS CHANGE


;FALL ONTO THE NEXT PAGE FOR AUTOMATIC FORMS CHANGE
;HERE TO SELECT THE BEST FORMS OF THE GROUP FOR AUTOMATIC CHANGE

	MOVE	P2,P3			;SAVE THE ORIGINAL COUNT
OUTS.4:	MOVE	S2,FRMCNT(P3)		;NOW FIND THE LARGEST FORMS LIMIT
	MOVEI	T1,(P3)			;S2 = PAGES, T1 = INDEX INTO FORMS TABLES
OUTS.5:	SOJL	P3,OUTS.6		;LOOKED AT THEM ALL YET
	CAMLE	S2,FRMCNT(P3)		;NO, IS THIS ONE BETTER
	  JRST	OUTS.5			;NO, KEEP LOOKING
	JRST	OUTS.4			;YES, TAKE THIS ONE
OUTS.6:	CAIGE	S2,FRMTHR		;LARGEST COUNT BELOW THE THRESHOLD
	  JRST	RETZER			;YES, NOT WORTH AN AUTOMATIC CHANGE
	MOVE	S2,FRMTYP(T1)		;GET THE NEW TYPE
	MOVX	S1,FRMNOR		;GET NAME OF "NORMAL" FORMS
	ANDX	S1,FRMSK1		;DOWN TO UNIQUE NAME
	CAME	S1,S2			;CHANGING BACK TO STANDARD
	  PUSHJ	P,OUTOTP		;NO, LOOK FOR OTHERS AT THIS STATION
	JUMPE	S1,OUTS.7		;SKIP THIS FORM CHANGE (TRY ANOTHER)
	MOVEM	S2,PSBIN1(P4)		;CHANGE THE FORMS
	MOVE	AP,FRMADR(T1)		;FIRST OF THIS NEW FORM
	POPJ	P,			;IS THE ONE TO SCHEDULE
OUTS.7:	MOVE	P3,P2			;RESTORE LOOP COUNT
	SETZM	FRMCNT(T1)		;SET REQUEST = 0 PAGES
	JRST	OUTS.4			;AND TRY ANOTHER FORM TYPE


;THE RDE QUEUE SCHEDULER

RDESCH:	LOAD	P1,PSBSTS(P4),PSYNJP	;GET NUMBER OF JOBS IN PROGRESS
	JUMPN	P1,RETZER		;RETURN ZERO IF BUSY
	LOAD	AP,.QHLNK(H),QH.PTF	;GET FIRST ITEM IN THE QUEUE

RDES.1:	PJUMPE	AP,.POPJ##		;RETURN IF NO JOBS
	LOAD	S1,PSBIN1(P4)		;GET FORMS TYPE
	MOVEM	S1,.QELM1(AP)		;SAVE IN REQUEST
	PUSHJ	P,SCHSTR		;CHECK THE STRUCTURE
	PJUMPN	S1,.POPJ##		;RETURN IF WE'VE WON
	LOAD	AP,.QELNK(AP),QE.PTN	;ELSE, GET THE NEXT JOB
	JRST	RDES.1			;AND LOOP
;THE INPUT SCHEDULING ROUTINE

INPSCH:	LOAD	AP,.QHLNK(H),QH.PTF	;GET FIRST JOB IN QUEUE
	PJUMPE	AP,.POPJ##		;NO JOBS, RETURN
	PUSHJ	P,I$LOGN##		;OK TO RUN JOBS
	JUMPE	S1,RETTMR		;NO, RETURN ZERO AND COME BACK IN A MIN.
	LOAD	P1,PSBSTS(P4),PSYNJP	;GET NUMBER CURRENTLY BEING PROCESSED
	LOAD	P2,PSBIN2(P4),PSYMJB	;GET MJOB VALUE
	CAIL	P1,(P2)			;ALREADY ENOUGH JOBS RUNNING
	  JRST	RETZER			;YES, RETURN A ZERO
	MOVSI	P1,-INPNUM		;FIND A SLOT IN THE TABLES
	SKIPE	INPITN(P1)		;EMPTY IS ITN = 0
	  AOBJN	P1,.-1			;KEEP GOING TILL FOUND
	JUMPGE	P1,RETZER		;MUST BE RUNNING ENOUGH JOBS
	LOAD	S1,PSBIN2(P4),PSYNXT	;GET OPERATOR SELECTION
	JUMPE	S1,INPS.2		;RESUME NORMAL PATH IF NONE
	MOVE	P2,AP			;COPY ORIGINAL AP
	PUSHJ	P,FNDNXT		;FIND THE ONE SELECTED
	JUMPE	AP,INPS.1		;NOT FOUND, GO NORMAL ROUTE
	PUSHJ	P,INPREJ		;DO SOME SIMPLE REJECTION TESTS
	JUMPE	S1,INPS.1		;JUMP IF REJECTED
	ZERO	PSBIN2(P4),PSYNXT	;CLEAR NEXT WHEN FOUND
	MOVEI	S1,-1			;GET A "LARGE" NUMBER
	MOVEM	S1,INPS.A		;SET SINGLE JOB TIME LIMIT
	MOVEM	S1,INPS.B		;SET TOTAL BATCH TIME LIMIT
	MOVEM	S1,INPS.C		;SET SINGLE JOB CORE LIMIT
	MOVEM	S1,INPS.D		;SET TOTAL BATCH CORE LIMIT
	JRST	INPS.4			;TO ENSURE THIS JOB FITS

;MORE OF THE INPUT SCHEDULING ROUTINE ON THE NEXT PAGE
;CONTINUE WITH INPUT JOB SELECTION

INPS.1:	MOVE	AP,P2			;RESTORE ORIGINAL AP
INPS.2:	LOAD	S1,PSBIN1(P4),PSYSJT	;VALUE OF MTIME
	MOVEM	S1,INPS.A		;STORE FOR LOOP
	LOAD	S1,PSBIN1(P4),PSYTBT	;AND TIME OPERATOR COMMANDS
	MOVEM	S1,INPS.B		;...
	LOAD	S1,PSBIN3(P4),PSYSJC	;SINGLE JOB CORE LIMIT
	CAMGE	S1,G$MCOR##		;SMALLER THAN SYSTEM MINIMUM
	  MOVE	S1,G$MCOR##		;YES, USE THAT INSTEAD
	MOVEM	S1,INPS.C		;...
	LOAD	S1,PSBIN3(P4),PSYTBC	;AND TOTAL BATCH CORE
	CAMGE	S1,G$MCOR##		;...
	  MOVE	S1,G$MCOR##		;...
	MOVEM	S1,INPS.D		;...
	MOVE	S1,KSYSTM		;GET TIME UNTIL KSYS
	JUMPL	S1,RETZER		;OVER, AND THIS IS NOT 'NEXT', DONT RUN
	JUMPE	S1,INPS.3		;NOT SCHEDULED, USE OPERATOR VALUES
	CAMG	S1,INPS.B		;USE THE SMALLEST OF THAT OR OPERATORS VALUE
	  MOVEM	S1,INPS.B		;USE KSYS TIMER
INPS.3:	PUSHJ	P,INPREJ		;DO SOME SIMPLE REJECTION TESTS
	JUMPE	S1,INPS.5		;JUMP IF REJECTED
INPS.4:	LOAD	T4,.QELM2(AP),QE.TIM	;GET THIS JOBS /TIME: VALUE
	LOAD	T3,PSBSCH(P4),PSYSMT	;TIME ALREADY COMMITTED
	ADD	T3,T4			;ACCUMULATE THIS JOB
	CAMG	T4,INPS.A		;OVER THE SINGLE JOB LIMIT
	 CAMLE	T3,INPS.B		;OR WILL IT TAKE US OVER THE TOTAL
	  JRST	INPS.5			;YES, TRY ANOTHER JOB
	LOAD	S1,.QELM2(AP),QE.COR	;GET REQUESTED CORE
	LOAD	T4,PSBSCH(P4),PSYSMC	;AMOUNT ALREADY COMMITTED
	ADD	T4,S1			;INCLUDE THIS JOB
	CAMG	S1,INPS.C		;OVER THE SINGLE JOB LIMIT
	 CAMLE	T4,INPS.D		;OR WILL IT TAKE US OVER THE TOTAL
	  JRST	INPS.5			;YES, TRY ANOTHER JOB
	STORE	T1,INPUNI(P1),IN.UNI	;STORE THIS JOBS /UNIQUE VALUE
	STORE	T3,PSBSCH(P4),PSYSMT	;ACCUMULATED TOTAL COMMITTED
	STORE	T4,PSBSCH(P4),PSYSMC	;AND TOTAL FOR CORE
	MOVE	S1,P1			;COPY STREAM NUMBER INTO S1
	PUSHJ	P,I$UQST##		;AND SET STREAM DIRECTORY
	LOAD	T1,.QEITN(AP)		;THE ITN
	STORE	T1,INPITN(P1)		;STORE FOR RELEASE
	INCR	INPNJP			;ADD ANOTHER JOB
	POPJ	P,			;AND TAKE THIS ONE
INPS.5:	LOAD	AP,.QELNK(AP),QE.PTN	;FIND THE NEXT JOB
	JUMPN	AP,INPS.3		;TRY IT IF THERE IS ONE
	POPJ	P,			;ELSE, RETURN 0 SELECTION

INPS.A:	BLOCK	1			;SINGLE JOB TIME LIMIT
INPS.B:	BLOCK	1			;TOTAL BATCH TIME LIMIT
INPS.C:	BLOCK	1			;SINGLE JOB CORE LIMIT
INPS.D:	BLOCK	1			;TOTAL BATCH CORE LIMIT
;SUBROUTINES TO DO LINKIN FOR INPUT AND OUTPUT QUEUES

;CALLED WITH AP = THE ENTRY TO BE INCLUDED IN THE QUEUE
;	     H  = THE QUEUE

OUTLNK:	PUSHJ	P,.SAVE3##		;SAVE P1-P3
	SAVE	E			;...
	LOAD	P2,.QELM2(AP),QE.PGS	;GET THE PAGE LIMIT FOR THIS REQUEST
	MOVEI	P3,^D60			;FACTOR FOR AGEING IN PRICMP
	JRST	LNKPRI			;AND LINK IN PRIORITY ORDER

INPLNK:	PUSHJ	P,.SAVE3##		;SAVE A FEW REGS
	SAVE	E			;...
	LOAD	P2,.QELM2(AP),QE.TIM	;GET BASE FOR PRIORITY
	MOVEI	P3,^D10			;FACTOR FOR PRICMP
	ZERO	P1			;CLEAR ENTRANCE PRIORITY
	LOAD	S1,.QELM1(AP),QE.DEP	;GET VALUE OF DEPENDENCY
	JUMPN	S1,LNKP.1		;IF SET, PUT IN AGE ORDER
LNKPRI:	HRLOI	P1,1			;GET 1,,777777 INTO P1
	IDIVI	P1,(P2)			;FACTOR THE LIMIT REQUESTED
	LOAD	S1,.QESEQ(AP),QE.PRI	;GET THE EXTERNAL PRIORITY
	SUBI	S1,1			;DECREMENT IT
	IMULI	P1,(S1)			;BUMP BY THE SPECIFIED VALUE
	LSH	P1,-1			;BUT NOT BY TOO MUCH
LNKP.1:	MOVEM	P1,.QEIPR(AP)		;SAVE AS THE ENTRANCE PRIORITY
	MOVEI	E,(AP)			;COPY THE CURRENT ENTRY POINTER
	PUSHJ	P,PRICMP		;COMPUTE THE FULL PRIORITY
	MOVE	P1,S1			;SAVE IT IN P1
	LOAD	E,.QHLNK(H),QH.PTF	;GET THE FIRST IN THIS QUEUE
LNKP.2:	PJUMPE	E,M$ELNK##		;IF AT THE END, MAKE THIS THE LAST
	PUSHJ	P,PRICMP		;COMPUTE FULL PRIORITY OF THIS ONE
	CAMLE	P1,S1			;IS THE NEW ONE HIGHER
	  PJRST	M$LINK##		;YES, E IS THE SUCCESSOR
	CAME	P1,S1			;SPECIAL CASE IF EQUAL
	  JRST	LNKP.3			;NO, FOLLOW THE CHAIN
	MOVE	S1,.QECRE(E)		;NEEDED IF Q$CDIN IS RETURNING
	CAMLE	S1,.QECRE(AP)		;AN ENTRY TO THE PROCESSING QUEUE
	  PJRST	M$LINK##		;PUT EQUAL PRIO IN AGE ORDER
LNKP.3:	LOAD	E,.QELNK(E),QE.PTN	;NO, FOLLOW THE CHAIN
	JRST	LNKP.2			;TILL WE FIND OUR POSITION

;ROUTINE TO INCLUDE AGE FACTOR FOR QUEUES

PRICMP:	MOVE	S2,.QECRE(E)		;THIS ENTRIES CREATION TIME
	MOVE	S1,G$NOW##		;GET THE CURRENT TIME
	PUSHJ	P,I$AGE##		;GET THE AGE OF IT IN SECONDS
	IDIVI	S1,(P3)			;FACTOR BY DESIRED VALUE
	ADD	S1,.QEIPR(E)		;INCLUDE THE ENTRANCE PRIORITY
	POPJ	P,			;AND RETURN WITH PRI IN S1
; HERE WHEN CALLED BY Q$CREATE TO FILL IN DEFAULTS FOR A MESSAGE "M"

; FOR THE INPUT QUEUE

INPDEF:	FILLIN	S1,.EQLM1(M),EQ.OUT,%EQOLG  ;SET DEFAULT /OUTPUT VALUE
	FILLIN	S1,.EQLM2(M),EQ.TIM,INPTIM  ;SET DEFAULT TIME VALUE
	FILLIN	S1,.EQLM3(M),EQ.LPT,INPPGS  ;SET DEFAULT PAGE VALUE
	FILLIN	S1,.EQLM3(M),EQ.CDP,INPCDS  ;SET DEFAULT CARD VALUE
	FILLIN	S1,.EQLM4(M),EQ.PTP,INPPTP  ;SET DEFAULT PAPER TAPE VALUE
	FILLIN	S1,.EQLM4(M),EQ.PLT,INPPLT  ;SET DEFAULT PLOTTER VALUE
	FILLIN	S1,.EQLM2(M),EQ.COR,INPCOR  ;SET DEFAULT CORE VALUE
	MOVE	S2,G$MCOR##		;GET THE SYSTEM MINIMUM
	CAMGE	S1,S2			;VALUE ABOVE THE MINIMUM
	  STORE	S2,.EQLM2(M),EQ.COR	;NO, SET TO SYSTEM MINIMUM
	LOAD	S1,.EQSPC(M),EQ.NUM	;GET NUMBER OF FILES IN REQUEST
	CAIE	S1,2			;BETTER BE 2 FOR INPUT (.CTL, .LOG)
	  PJRST	E$INF##			;ILLEGAL NUMBER OF FILES
	PJRST	CLRCHK			;CLEAR/FILLIN ADDITIONAL INFORMATION
; FOR THE OUTPUT QUEUES

PLTDEF:	MOVX	S1,INPPLT		;USE SAME AS INPUT PLOT LIMIT
	MOVX	S2,PLTFCT		;GET PLOTTER FACTOR
	JRST	OUTDEF			;ENTER COMMON CODE
PTPDEF:	MOVX	S1,INPPTP		;USE SAME AS INPUT TAPE LIMIT
	MOVX	S2,PTPFCT		;GET PAPER TAPE FACTOR
	JRST	OUTDEF			;ENTER COMMON CODE
CDPDEF:	MOVX	S1,INPCDS		;USE SAME AS INPUT CARD LIMIT
	MOVX	S2,CDPFCT		;GET CARD PUNCH FACTOR
	JRST	OUTDEF			;ENTER COMMON CODE
LPTDEF:	MOVX	S1,INPPGS		;USE SAME DEFAULT AS INPUT PAGE LIMIT
	MOVX	S2,LPTFCT		;GET LINE PRINTER FACTOR
OUTDEF:	LOAD	TEMP,.EQLM2(M),EQ.PGS	;GET LIMIT SPECIFIED
	JUMPN	TEMP,OUTD.3		;OK IF ONE IS SPECIFIED
	LOAD	TEMP,.EQLM2(M),EQ.NBL	;NONE, GET NUMBER OF BLOCKS REQUESTED
	JUMPE	TEMP,OUTD.2		;NONE, USE INPUT SIDE AS DEFAULT
	MOVE	S1,TEMP			;GET INTO BETTER REG
	JUMPGE	S2,OUTD.1		;JUMP IF FACTOR IS A MULTIPLIER
	MOVMS	S2			;A DIVISOR, GET THE MAGNITUDE
	IDIVI	S1,(S2)			;FACTOR THE BLOCKS
	AOJA	S1,OUTD.2		;ROUND UP AND JUMP
OUTD.1:	IMULI	S1,(S2)			;FACTOR THE BLOCKS
OUTD.2:	MOVE	T1,S1			;SAVE PARTIAL LIMIT
	PUSHJ	P,CLRCHK		;CLEAR/FILLIN ADDITIONAL STUFF
	IMULI	S1,SPLLIM		;BIAS*SUM(ALL COPIES)
	ADD	S1,T1			;ADD IN THE LIMIT THUS FAR
	STORE	S1,.EQLM2(M),EQ.PGS	;STORE THE TOTAL LIMIT
	SKIPA				;AND SKIP ALTERNATE CALL
OUTD.3:	PUSHJ	P,CLRCHK		;CLEAR/FILLIN ADDITIONAL INFORMATION
	MOVX	S1,FRMNOR		;GET NAME OF NORMAL FORMS
	SKIPN	.EQLM1(M)		;FORMS SPECIFIED
	  MOVEM	S1,.EQLM1(M)		;NO, SET = "NORMAL"
	POPJ	P,			;AND RETURN
;SUBROUTINE TO FILL IN THE COMMON DEFAULTS FOR THE SCHDEF ENTRY
;
;RETURNS THE SUM OF THE COPIES OF ALL FILES IN S1

CLRCHK:	PUSHJ	P,.SAVET##		;SAVE T1-T4
	PUSHJ	P,.SAVE1##		;SAVE P1 AS WELL
	ZERO	CLRC.A			;CLEAR COPIES COUNTER
	LOAD	T4,.MSTYP(M),.QIFNC	;GET INTERNAL CREATE BIT FOR CHECKS
	JUMPN	T4,CLRC.2		;SKIP SOME TESTS IF THIS IS ONE
	ZERO	.EQSEQ(M),EQ.RDE!EQ.SPL	;CLEAR INTERNAL BITS
	LOAD	S1,.EQLEN(M),EQ.VRS	;GET REQUEST VERSION NUMBER
	CAIE	S1,%%.QSR		;BETTER BE THE SAME AS QSRMAC
	  PJRST	E$WVN##			;ISN'T, GIVE WRONG VERSION ERROR
	MOVE	S1,M			;POINT TO THE EQ
	PUSHJ	P,I$DFEQ##		;DEFAULT OS-DEP STUFF
	PJUMPE	S1,E$ICM##		;JUMP IF AN ILLEGAL CREATE
CLRC.1:	PUSHJ	P,I$WHEEL##		;FIND OUT IF CALLER IS PRIVILEGED
	STORE	S1,.EQSEQ(M),EQ.PRV	;STORE WHEEL STATUS FOR SPOOLERS
CLRC.2:	LOAD	S1,.EQSPC(M),EQ.NUM	;GET NUMBER OF FILES
	PJUMPE	S1,E$INF##		;BAD FILE COUNT
	MOVE	P1,S1			;P1 = NUMBER OF 'REAL' FILES
	LOAD	S2,.EQLEN(M),EQ.LOH	;LETS FIND THE FIRST FILE
	ADDI	S2,(M)			;S2 = POINTER INTO '.FPxxx'
	LOAD	T3,.MSTYP(M),MS.CNT	;GET THE MESSAGE COUNT
	ADDI	T3,(M)			;T3 = FIRST ADDR NOT IN MESSAGE
CLRC.3:	CAIL	S2,.FPINF(T3)		;WILL I TOUCH AN ILLEGAL ADDRESS
	  PJRST	E$ICM##			;YES, INVALID MESSAGE
	MOVX	T2,FP.SPL		;GET THE SPOOLED FILE BIT
	SKIPN	T4			;SKIP IF INTERNAL CREATE
	  ANDCAM T2,.FPINF(S2)		;CAN ONLY BE SET ON INTERNAL REQUESTS
	LOAD	T1,.FPINF(S2),FP.FCY	;GET NUMBER OF COPIES
	ADDM	T1,CLRC.A		;AND ADD TO THE SUM
	LOAD	T2,.FPINF(S2),FP.IGN	;GET THE /REMOVE FLAG
	SKIPE	T2			;IS THIS A 'REAL' FILE
	  SOS	P1			;NO, DECREMENT THE COUNT
	LOAD	T1,.FPSIZ(S2),FP.FHD	;SIZE OF FP AREA
	LOAD	T2,.FPSIZ(S2),FP.FFS	;SIZE OF FD AREA
	CAIL	T1,FPMSIZ		;LESS THAN THE MINIMUM FP SIZE
	 CAIGE	T2,FDMSIZ		;OR LESS THAN THE MINIMUM FD SIZE
	  PJRST	E$ICM##			;YES, AN INVALID MESSAGE
	CAIG	T1,FPXSIZ		;GREATER THAN MAXIMUM FP SIZE
	 CAILE	T2,FDXSIZ		;OR GREATER THAN THE MAXIMUM FD SIZE
	  PJRST	E$ICM##			;YES, AN ILLEGAL MESSAGE
	ADDI	S2,(T1)			;BUMP TO NEXT FILE PARAMETER AREA
	ADDI	S2,(T2)			;...
	SOJG	S1,CLRC.3		;CONTINUE FOR ALL FILES
	CAIE	S2,(T3)			;THIS BETTER COME OUT EVEN
	  PJRST	E$ICM##			;DIDN'T, AN INVALID MESSAGE
	JUMPN	P1,CLRC.4		;JUMP IF THERE ARE SOME FILES LEFT
	MOVX	P1,EQ.RDE		;GET THE 'DOESNT EXIST' BIT
	IORM	P1,.EQSEQ(M)		;AND MARK THE REQUEST


;CONTINUE WITH THE DEFAULT FILLING ON THE NEXT PAGE
;HERE FOR MORE DEFAULT FILLING

CLRC.4:	LOAD	S1,.EQSEQ(M),EQ.SEQ	;GET THE SEQUENCE NUMBER
	JUMPN	S1,CLRC.5		;GOT ONE, SKIP THIS
	HRRZ	S1,.EQITN(M)		;GET THE RH OF THE ITN
	ANDI	S1,^D8191		;NICE LOOKING MASK
	AOS	S1			;DON'T WANT A ZERO
	STORE	S1,.EQSEQ(M),EQ.SEQ	;THAT'S A NICE DEFAULT SEQUENCE
CLRC.5:	LOAD	S1,.EQJOB(M)		;GET THE JOB NAME
	JUMPN	S1,CLRC.6		;IS ONE, SKIP THE DEFAULT
	MOVE	S1,[SIXBIT\NONAME\]	;WOULD YOU BELIEVE '.BAS'
	MOVEM	S1,.EQJOB(M)		;MUST HAVE A JOB NAME SO LIST WILL WORK
CLRC.6:	JUMPN	T4,CLRC.7		;SKIP THIS IF INTERNAL CREATE
	ZERO	.EQCHK(M)		;CREATE CAN'T HAVE CHECKPOINT/REQUEUE INFO
	HRRI	S1,.EQCHK+1(M)		;SO LETS ZERO IT NOW
	HRLI	S1,.EQCHK(M)		;BUILD THE BLT
	BLT	S1,.EQCHK+4(M)		;CLEAR THE INFORMATION
CLRC.7:	SKIPN	S1,.EQAFT(M)		;IS THERE CREATION OR AFTER
	  MOVE	S1,G$NOW##		;NO, USE CURRENT TIME
	MOVEM	S1,.EQAFT(M)		;AS DEFAULT CREATION TIME
	LOAD	S1,.EQSPC(M),EQ.PRO	;GET PROTECTION FIELD
	SKIPN	S1			;WAS IT 0?
	LOAD	S1,G$SPRT##		;YES, DEF IT WITH SPOOL PROT
	STORE	S1,.EQSPC(M),EQ.PRO	;AND STORE IT
	FILLIN	S1,.EQSEQ(M),EQ.PRI,SPLPRI ;DEFAULT THE EXTERNAL PRIO
	MOVE	S1,CLRC.A		;LOAD RETURN ANSWER
	POPJ	P,			;AND RETURN FOR LINKIN


CLRC.A:	BLOCK	1			;SUM OF ALL COPIES FIELD
;FUNCTIONS TO DO QUEUE DEPENDENT GROUP 1 MODIFIES

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

OUTMOD:	PUSHJ	P,.SAVE3##		;SAVE A FEW REGS FIRST
	LOAD	P1,MOD.GN(S1),MODGLN	;NUMBER OF GROUP 1 ELEMENTS
	SOJLE	P1,.POPJ##		;0 IS ACCEPTABLE, ADJUST FOR THE LOOP
	CAILE	P1,NOUTPM		;MORE THAN CURRENTLY IMPLEMENTED
	  MOVEI	P1,NOUTPM		;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
OUTM.1:	MOVE	P3,0(P2)		;GET AN ELEMENT
	CAME	P3,[-1]			;DID IT CHANGE
	  XCT	OUTMTB(P1)		;YES, STORE NEW VALUE
	INCR	P2			;TO NEXT ELEMENT
	AOBJN	P1,OUTM.1		;GET THEM ALL
	POPJ	P,			;RETURN TO Q$MODIFY FOR NEXT GROUP

OUTMTB:	STORE	P3,.EQLM1(AP)		; 0 = /FORMS
	STORE	P3,.EQLM2(AP),EQ.PGS	; 1 = /LIMIT
	STORE	P3,.EQLM3(AP)		; 2 = /NOTE (1ST HALF)
	STORE	P3,.EQLM4(AP)		; 3 = /NOTE (2ND HALF)

NOUTPM==<.-OUTMTB>			;NUMBER CURRENTLY IMPLEMENTED
;ROUTINE CORRESPONDS TO OUTMOD EXCEPT FOR THE INPUT QUEUE

;CALLED BY Q$MODIFY WITH THE SAME ARGUMENTS AS OUTMOD ENTRY

INPMOD:	PUSHJ	P,.SAVE4##		;SAVE A FEW REGS FIRST
	LOAD	P1,MOD.GN(S1),MODGLN	;NUMBER OF GROUP 1 ELEMENTS
	SOJLE	P1,.POPJ##		;0 IS ACCEPTABLE, ADJUST FOR THE LOOP
	CAILE	P1,NINPPM		;MORE THAN CURRENTLY IMPLEMENTED
	  MOVEI	P1,NINPPM		;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
INPM.1:	MOVE	P3,0(P2)		;GET AN ELEMENT
	CAME	P3,[-1]			;DID IT CHANGE
	  XCT	INPMTB(P1)		;YES, STORE NEW VALUE
	INCR	P2			;TO NEXT ELEMENT
	AOBJN	P1,INPM.1		;GET THEM ALL
	POPJ	P,			;RETURN TO Q$MODIFY FOR NEXT GROUP

INPMTB:	STORE	P3,.EQLM2(AP),EQ.COR	; 0 = /CORE
	STORE	P3,.EQLM2(AP),EQ.TIM	; 1 = /TIME
	STORE	P3,.EQLM3(AP),EQ.LPT	; 2 = /PAGES
	STORE	P3,.EQLM3(AP),EQ.CDP	; 3 = /CARDS
	STORE	P3,.EQLM4(AP),EQ.PTP	; 4 = /FEET (/METERS)
	STORE	P3,.EQLM4(AP),EQ.PLT	; 5 = /TPLOT
	PUSHJ	P,MODDEP		; 6 = /DEPENDENCY
	STORE	P3,.EQLM1(AP),EQ.UNI	; 7 = /UNIQUE
	STORE	P3,.EQLM1(AP),EQ.NRS	; 8 = /RESTART
	STORE	P3,.EQLM1(AP),EQ.OUT	; 9 = /OUTPUT

NINPPM==<.-INPMTB>			;NUMBER CURRENTLY IMPLEMENTED

MODDEP:	HLRZ	P4,P3			;GET CHANGE TYPE
	HRRZS	P3			;CLEAR THE CHANGE TYPE
	CAIN	P4,.MODAB		;ABSOLUTE CHANGE
	  JRST	MODD.2			;YES, GO STORE IT
	CAIN	P4,.MODPL		;ADDITIVE
	  JRST	MODD.1			;YES, GO ADD THEM TOGETHER
	CAIE	P4,.MODMI		;SUBTRACTIVE
	  POPJ	P,			;NO, DON'T STORE FOR UNKNOWN TYPE
	MOVNS	P3			;SUBTRACTING, NEGATE THE VALUE
MODD.1:	LOAD	P4,.EQLM1(AP),EQ.DEP	;GET OLD VALUE
	ADDB	P3,P4			;ADD (OR SUBTRACT) THEM
	SKIPGE	P3			;DON'T LET IT GO NEGATIVE
	  ZERO	P3			;IT DID, MAKE IT ZERO
	CAILE	P3,177777		;OR DON'T LET IT GET TOO BIG
	  MOVEI	P3,177777		;IT DID, SET TO MAXIMUM
MODD.2:	STORE	P3,.EQLM1(AP),EQ.DEP	;STORE NEW (OR ADJUSTED) VALUE
	POPJ	P,			;RETURN FOR NEXT
SUBTTL	Other Entry Points

;ROUTINE CALLED WHENEVER AN ENTRY IS REMOVED FROM THE USE QUEUE

;CALLED BY  Q$RELEASE, Q$REQUEUE, KILPSB
;WITH
;	AP = THE ENTRY BEING REMOVED
;	T1 = THE PSB WHO USED TO BE PROCESSING THIS ENTRY (0 IF UNKNOWN)

	INTERN	S$RUSE

S$RUSE:	LOAD	S1,.QESTN(AP),QE.RQP	;GET THE RELATIVE QUEUE POINTER
	ADDI	S1,TBLHDR##		;MAKE A REAL QUEUE POINTER
	LOAD	S1,.QHTYP(S1),QH.TYP	;GET THE QUEUE TYPE
	CAIE	S1,.QHTIP		;IS IT THE INPUT QUEUE
	  JRST	RUSE.1			;NO, DECREMENT NJP FOR PSB
	MOVSI	S1,-INPNUM		;SET UP FOR ITN SEARCH FOR BATCH JOBS
	MOVE	S2,.QEITN(AP)		;GET THE ITN OF THE REQUEST
	CAME	S2,INPITN(S1)		;LOOK FOR IT IN THE BATCH TABLES
	  AOBJN	S1,.-1			;CONTINUE UNTIL FOUND
	JUMPGE	S1,RUSE.1		;WHAT!!!!!
	ZERO	INPITN(S1)		;REMOVE FROM THE TABLES
	ZERO	INPUNI(S1)		;AND THE UNIQUE STUFF TOO
	PUSHJ	P,I$UQCL##		;CLEAR THE DIRECTORY
	DECR	INPNJP			;ONE LESS JOB RUNNING
	JUMPE	T1,.POPJ##		;RETURN NOW IF NO PSB
	LOAD	S1,PSBSCH(T1),PSYSMT	;TIME COMMITTED TO THIS PROCESS
	LOAD	S2,.QELM2(AP),QE.TIM	;THIS JOBS TIME LIMIT
	SUB	S1,S2			;ACCOUNT FOR JOB REMOVAL
	STORE	S1,PSBSCH(T1),PSYSMT	;STORE ADJUSTED VALUE
	LOAD	S1,PSBSCH(T1),PSYSMC	;CORE COMMITTED TO THIS PROCESS
	LOAD	S2,.QELM2(AP),QE.COR	;THIS JOBS CORE LIMIT
	SUB	S1,S2			;REMOVE THIS JOB
	STORE	S1,PSBSCH(T1),PSYSMC	;STORE ADJUSTED VALUE
RUSE.1:	JUMPE	T1,.POPJ##		;RETURN IF NO PSB AVAILABLE
	DECR	PSBSTS(T1),PSYNJP	;RUNNING ONE LESS JOB
	POPJ	P,			;RETURN TO CALLER
;THIS ROUTINE PROCESSES THE QUEUE DEPENDENT FUNCTIONS AT THE RELEASE MESSAGE
; Q$RELEASE HAS ALREADY VALIDATED AND PROCESSED THE MESSAGE

;CALLED BY Q$RELEASE WITH:

;	M  = THE RELEASE MESSAGE
;	AP = ENTRY BEING RELEASED (.QExxx)

	INTERN	S$RELE

S$RELE:	PUSHJ	P,.SAVE1##		;SAVE P1
	LOAD	T2,.QESTN(AP),QE.RQP	;GET THE RELATIVE QUEUE POINTER
	ADDI	T2,TBLHDR##		;MAKE A REAL QUEUE POINTER
	LOAD	T2,.QHTYP(T2),QH.TYP	;NOW GET THE QUEUE TYPE
	CAIE	T2,.QHTIP		;THE INPUT QUEUE
	  POPJ	P,			;NO, DON'T BOTHER WITH ALL THE WORK
	LOAD	P1,.MSTYP(M),MS.CNT	;GET LENGTH OF RELEASE MESSAGE
	SUBI	P1,REL.SZ		;EXPECT A LONG ONE FROM BATCON
	JUMPLE	P1,.POPJ##		;NOT FROM BATCON (OR A BUG IN BATCON)
	STORE	AP,<BATLGO+CLM.JB>,CL.BQE  ;SAVE THE BATCH QUEUE ENTRY ADDRESS
	LOAD	T2,REL.BJ(M),RL.JOB	;GET BATCH JOB NUMBER
	STORE	T2,<BATLGO+CLM.JB>,CL.JOB ;SAVE FOR EVENTUAL FAKE LOGOUT
	SOJE	P1,BJLOGO		;ADJUST P1, JUMP IF WAS /OUTPUT:0
	CAIGE	P1,FDMSIZ		;MESSAGE TOO SMALL
	  JRST	BJLOGO			;ANOTHER BUG IN BATCON
	MOVEI	T1,BATSPL		;POINT TO 'MY' CANONICAL SPOOL MESSAGE
	STORE	T2,CSM.JB(T1),CS.JOB	;STORE JOB NUMBER, CLEAR THE REST
	LOAD	T2,.QEPRT(AP),DV.STN	;GET STATION OF THE INPUT REQUEST
	STORE	T2,CSM.JB(T1),CS.LOC	;MAKE SURE THE OUTPUT IS CORRECTLY ROUTED
	ZERO	CSM.JB(T1),CS.FLG	;NO FLAGS HERE
	LOAD	S1,.QEPRT(AP),QE.DMD	;GET DEVICE MODIFIERS
	HRLI	S1,'LPT'		;LOG GOES TO LPT THERE
	PUSHJ	P,I$MSDN##		;MAKE A SIXBIT DEVICE NAME FROM THAT
	STORE	S1,CSM.DV(T1)		;STORE FOR Q$FSPL
	MOVE	T2,.QEOID(AP)		;GET OWNER ID
	STORE	T2,CSM.OI(T1)		;SAVE THAT
	MOVEI	S1,REL.FD(M)		;POINT TO THE LOG FILE FD AREA
	STORE	S1,CSM.FD(T1),CS.FDA	;SAVE FOR Q$INCL
	PUSHJ	P,I$FSTR##		;EXTRACT THE STRUCTURE NAME
	STORE	S1,CSM.ST(T1)		;SAVE FOR Q$FSPL
	STORE	P1,CSM.FD(T1),CS.FDL	;FINALLY, THE LENGTH OF THE FD INVOLVED
	PUSHJ	P,Q$FSPL##		;FIND MATCHING SPOOL REQUEST


; CODE IS CONTINUED ON THE NEXT PAGE
;HERE WITH	M  = THE RELEASE MESSAGE
;		AP = CURRENT SPOOLING REQUEST (.EQxxx)
;		E  = SPL QUEUE ENTRY FOR THIS JOB
;		P1 = THE LENGTH OF THE LOG FILE FD
;		T1 = THE CANONICAL SPOOL MESSAGE

	MOVE	T2,BATFLG		;DEFAULT LOG FILE SETTINGS
	LOAD	S1,REL.BJ(M)		;GET RELEASE INFO WORD
	TXNE	S1,RL.DLG		;/DISP:DELETE
	  TXO	T2,FP.DEL		;YES, SET THE DELETE BIT
	TXNE	S1,RL.PRL		;PRINT THE LOG?
	  JRST	RELE.1			;YES, ALL IS WELL
	TXZ	T2,FP.FCY		;NO, ZERO COPIES
	LOAD	T3,.EQSPC(AP),EQ.NUM	;GET CURRENT NUMBER OF FILES
	JUMPN	T3,RELE.1		;SOME ALREADY, JUST ADD
	MOVX	T3,EQ.RDE		;NONE, GET NOT REAL BIT
	IORM	T3,.EQSEQ(AP)		;SO DOESN'T GET LISTED
RELE.1:	MOVEM	T2,CSM.FP(T1)		;STORE THE FLAG SETTINGS
	PUSHJ	P,Q$INCL##		;INCLUDE THE LOG FILE
	MOVE	S1,AP			;GET THE ADDRESS INTO S1
	ADR2PG	AP			;CONVERT TO A PAGE NUMBER
	PUSHJ	P,F$WRRQ##		;SAVE THE REQUEST
	LOAD	S2,SPLJOB(E),SPYDPA	;GET THE OLD DPA
	STORE	S1,SPLJOB(E),SPYDPA	;STORE NEW RETREIVAL POINTER
	SKIPE	S1,S2			;GET OLD DPA IF THERE IS ONE
	  PUSHJ	P,F$RLRQ##		;AND RELEASE OLD FAILSOFT COPY
	PUSHJ	P,M$RELP##		;RELEASE OLD COPY
BJLOGO:	MOVEI	M,BATLGO		;POINT TO THE LOGOUT BLOCK
	MOVX	S1,.QIFNC		;GET THE INTERNAL FLAG
	IORM	S1,.MSTYP(M)		;INDICATE BATCH CALL TO LOGOUT
	PJRST	Q$LOGOUT##		;FAKE A LOGOUT MESSAGE

BATSPL:	BLOCK	CSMSIZ			;ARGUMENT BLOCK FOR Q$FSPL, Q$INCL
BATLGO:	BLOCK	CLMSIZ			;LOGOUT BLOCK
BATFLG:	INSVL.(.FPFAS,FP.FFF)!INSVL.(1,FP.FSP)!FP.FLG!INSVL.(1,FP.FCY)
SUBTTL	Scheduler Utilities

;ROUTINE TO ACCUMULATE SIZES IF FORMS ARE NOT FROZEN

;	P3 = FORM COUNTER
;	P1 = NEW TYPE (MASKED)
;	AP = CURRENT ENTRY
;RETURNS WITH TABLES INCREMENTED

OUTFRM:	JUMPL	P3,OUTF.2		;JUMP IF THIS IS THE FIRST CALL
	ZERO	S1			;CLEAR SEARCH INDEX
OUTF.1:	CAILE	S1,(P3)			;END OF THE LIST
	  JRST	OUTF.2			;YES, APPEND TO THE END
	CAME	P1,FRMTYP(S1)		;FIND IN THE TABLE
	  AOJA	S1,OUTF.1		;NO, KEEP LOOKING
	LOAD	S2,.QELM2(AP),QE.PGS	;PAGE REQUEST FOR THIS ENTRY
	ADDM	S2,FRMCNT(S1)		;INCLUDE IN THE TOTAL
	POPJ	P,			;AND RETURN
OUTF.2:	CAIL	P3,FRMNUM-1		;TABLE FULL
	  POPJ	P,			;YES, DON'T INCLUDE THIS ONE
	AOS	P3			;BUMP COUNT
	MOVEM	P1,FRMTYP(P3)		;STORE FORM NAME
	MOVEM	AP,FRMADR(P3)		;AND FIRST FOR THIS TYPE
	LOAD	S1,.QELM2(AP),QE.PGS	;PAGE REQUEST
	MOVEM	S1,FRMCNT(P3)		;AS THE FIRST COUNT
	POPJ	P,			;NOW RETURN

;ROUTINE TO DETERMINE IF ANOTHER DEVICE AT THE SAME STATION IS USING
;	FORMS WE ARE ABOUT THE CHANGE TO.
;CALL	S2 = FORMS DESIRED
;	P4 = MY PSB
;RETURNS S1 = .FALSE. IF THERE ARE AND NOT TO DUPLICATE FORMS CHANGES
;	    = .TRUE. IF THERE ARE NONE AND OK TO CHANGE FORMS
;DESTROYS T2, T3

OUTOTP:	LOAD	S1,HDRPSB##+.QHLNK,QH.PTF  ;FIND THE FIRST IN THE PSB QUEUE
OUTO.1:	CAIN	S1,(P4)			;LOOKING AT MYSELF
	  JRST	OUTO.2			;YES, SKIP THE TESTS
	CAME	S2,PSBIN1(S1)		;USING SAME FORMS AS I WANT
	  JRST	OUTO.2			;NO, TRY THE NEXT
	LOAD	T2,PSBSDV(S1),DV.STN	;STATION NUMBER
	LOAD	T3,PSBSDV(P4),DV.STN	;AND MY LOCATION
	CAME	T2,T3			;SAME PLACE
	  JRST	OUTO.2			;NO, TRY THE NEXT
	LOAD	T2,PSBSTS(S1),PSYHDR	;QUEUE DRAWING FROM
	LOAD	T3,PSBSTS(P4),PSYHDR	;AND MY QUEUE
	CAMN	T2,T3			;SAME QUEUE
	  PJRST	.FALSE##		;YES, DON'T CHANGE FORMS
OUTO.2:	LOAD	S1,.QELNK(S1),QE.PTN	;LINK TO THE NEXT PSB
	JUMPN	S1,OUTO.1		;AND LOOK IF ONE THERE
	PJRST	.TRUE##			;GIVE OK TO CHANGE RETURN
;ROUTINE TO DO OUTPUT REJECTION TESTS

;CALL	AP = CURRENT ENTRY
;	P4 = CURRENT PSB
;RETURNS S1 = .TRUE. IF OK TO SCHEDULE
;	      .FALSE. IF REJECTED

OUTREJ:	LOAD	S1,.QEPRT(AP),QE.DMD	;GET REQUEST DEVICE MODIFIERS
	LOAD	S2,PSBSTS(P4),PSYLLP	;GET LOWER CASE PRINTER BIT
	TXNE	S1,DV.LLP		;REQUEST NEED LOWER CASE PRINTER
	  PJUMPE S2,.FALSE##		;YES, JUMP IF THIS ISN'T ONE
	TXNE	S1,DV.LUP		;NOW TRY UPPER CASE
	  PJUMPN S2,.FALSE##		;YES, REJECT IF PRINTER IS LOWER CASE
	PUSHJ	P,A$CSTN##		;CONVERT FOR RE-ROUTING
	LOAD	S2,PSBSDV(P4),PSYDMD	;GET DEVICE MODIFIERS OF SPOOLERS
	TXNN	S1,DV.NUL		;REQUEST FOR GENERIC STATION
	  JRST	OUTR.2			;NO, TRY FOR DEVICE MATCH
	XOR	S1,S2			;FOR MATCH CHECKS
	TXNN	S1,DV.STN		;STATION MATCH
	  PJRST	SCHSTR			;YES, NOW GO CHECK STRUCTURE
	PJRST	.FALSE##		;WRONG STATION
OUTR.2:	XOR	S1,S2			;FOR DEVICE CHECKS
	TXNN	S1,DV.STN!DV.UTN	;AN EXACT MATCH ON UNIT AND STATION
	 TXNE	S2,DV.NUL		;YES, BUT DOES SPOOLER WANT THOSE
	  PJRST	.FALSE##		;WANTS ONLY STATION OR WRONG UNIT
	PJRST	SCHSTR			;OK SO FAR, GO VERIFY STRUCTURE
;ROUTINE TO DO INPUT REJECTION TESTS

;CALL;	AP = CURRENT ENTRY
;RETURNS S1 = .TRUE. IF OK TO SELECT THIS JOB
;	      .FALSE. IF REJECTED
;	 T1 = .TRUE. OR .FALSE. FOR /UNIQUE :YES OR :NO IF S1 IS .TRUE.

INPREJ:	LOAD	S1,.QELM1(AP),QE.DEP	;GET /DEPENDENCY:n VALUE
	JUMPN	S1,.FALSE##		;DON'T RUN IF NON-ZERO
	ZERO	T1			;CLEAR THIS JOBS UNIQUENESS
	LOAD	S1,.QELM1(AP),QE.UNI	;GET /UNIQUE:value
	CAIE	S1,QE.UNO		;IS IT /UNIQUE:NO
	  MOVEI	T1,1			;NO, INDICATE /YES
	SKIPN	INPNJP			;ARE THERE ANY RUNNING NOW
	  PJRST	SCHSTR			;NO, GO CHECK STRUCTURE
	PUSHJ	P,.SAVE1##		;SAVE P1
	MOVSI	P1,-INPNUM		;SET UP FOR SEARCH
INPR.1:	HRRZ	S1,P1			;PUT STREAM NUMBER IN S1
	PUSHJ	P,I$UQCH##		;CHECK FOR UNIQNESS MATCH
	JUMPE	S1,INPR.2		;JUMP IF NO MATCH
	JUMPN	T1,.FALSE##		;YES, JUMP IF THIS IS /UNIQUE:YES
	LOAD	S2,INPUNI(P1),IN.UNI	;GET OTHER JOBS UNIQUE VALUE
	JUMPN	S2,.FALSE##		;IT TOO MUST BE /UNIQUE:NO
INPR.2:	AOBJN	P1,INPR.1		;CONTINUE FOR ALL JOBS
	PJRST	SCHSTR			;OK SO FAR, GO CHECK STRUCTURE
;ROUTINE TO VERIFY THAT A STRUCTURE NEEDED BY REQUEST 'AP' IN ONLINE

;RETURNS S1 = .TRUE. IF IT IS
;	      .FALSE. IF NOT

SCHSTR:	LOAD	S1,.QESTR(AP)		;GET STRUCTURE FOR THIS REQUEST
	PUSHJ	P,I$VSTR##		;VERIFY THAT IT IS ONLINE
	STORE	S2,.QESTR(AP)		;RE-STORE IN CASE VSTR CONVERTED IT
	JUMPN	S1,.POPJ##		;JUMP IF STR IS AVAILABLE
	PUSHJ	P,RETMIN		;TRY AGAIN IN 1 MINUTE
	PJRST	.FALSE##		;AND REJECT IT NOW

;ROUTINE TO FIND THE 'NEXT' REQUEST OF THE OPERATOR

;	S1 = THE SEQUENCE REQUESTED
;	AP = THE PLACE TO START LOOKING

;RETURNS AP = THE ONE OR ZERO
;DESTROYS S2

FNDNXT:	LOAD	S2,.QESEQ(AP),QE.SEQ	;GET THE SEQUENCE NUMBER OF THIS JOB
	CAMN	S2,S1			;THE SAME AS REQUESTED
	  POPJ	P,			;YES, THIS IS IT
	LOAD	AP,.QELNK(AP),QE.PTN	;FIND THE NEXT
	JUMPN	AP,FNDNXT		;JUMP IF THERE ARE MORE
	POPJ	P,			;RETURN AP = 0 IF NOT FOUND

;SUBROUTINE TO COMPUTE WHEN THE NEXT REQUEST FOR CHECKPOINT IS TO BE SENT OUT

;RETURNS S1 = THE TIME
;DESTROYS S2

NXTRCK:	SKIPG	S1,KSYSTM		;KSYS PENDING
	 SKIPA	S1,[SPLMBC]		;NO, GET NORMAL INTERVAL
	  IDIVI	S1,^D60			;TO EVEN MINUTES
	SKIPN	S1			;LESS THAN 1 MINUTE
	  MOVEI	S1,1			;YES, MAKE IT ONE
	PJRST	I$AFT##			;COMPUTE AND RETURN
SUBTTL	Constants and Other Things

	XLIST		;FORCED OUT LITERAL POOL HERE
	LIT
	LIST
	SALL


	END