Google
 

Trailing-Edge - PDP-10 Archives - BB-J092C-SB_1982 - cpnser.mac
There are 11 other files named cpnser.mac in the archive. Click here to see a list.
TITLE	CPNSER - SECOND PROCESSOR CONTROL V1317
SUBTTL	M. CHURCH - LNS/MIT     P. HURLEY - DEC/JMF/TW/CDO/GMU    01 JUL 80
	SEARCH	F,S

	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1973,1974,1975,1976,1977,1978,1979,1982 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VCP1SR,1317	;VERSION NUMBER FOR LOADER STORAGE MAP



CPNSER::ENTRY	CPNSER		;LOAD IF SPECIFIED BY COMMON




;ROUTINE TO SETUP THE SUBQUEUE SCHEDULING SCAN TABLE FOR CPU1
; CALLED ONCE EVERY SCDINT TICKS BY SCHED1

SCDSS1::ILDB	T1,SSPNT1##	;GET NEXT PRIMARY SUBQUEUE
	MOVEI	T4,SSSCN1##	;ADDRESS OF SCHEDULING SCAN TABLE
	PJRST	SCDSST##	;LET SCHED1 DO THE WORK

;SUBROUTINE TO INSURE THAT A JOB IS ON CPU0
;CALLING SEQUENCE:
;	PUSHJ	P,ONCPU0
;ALWAYS RETURNS CPOPJ ON CPU0

ONCPU0::PUSH	P,T1		;SAVE CALLERS T1
	PUSH	P,T2		;SAVE ANOTHER
	MOVE	T2,.CPJOB##	;GET CURRENT JOB
	MOVSI	T1,(SP.ROP)	;RUN ONLY ON POLICY CPU
	IORM	T1,JBTSPS##(T2)	;LIGHT FOR BECOM0 FIXUPS
	MOVE	T1,BOOTCP##	;GET CURRENT POLICY CPU
	PUSHJ	P,ONCPUN	;GET ON CORRECT CPU, NOT RUNNABLE  ANYWHERE ELSE
	JRST	TTPOPJ##	;RETURN WHEN ON CPUN

;ROUTINE TO GET ON CPU OWNING A TERMINAL
;U POINTS TO LDB

ONCPUL::LDB	T1,LDPCPU##	;GET THE NUMBER OF THE OWNING CPU
	PJRST	ONCPUN		;GET ON THAT CPU
;ROUTINE TO GET ON CPUN BUT FAIL IF CPU IS NOT RUNNING
;CALLED FROM UUO'S LIKE PERF. AND DTE.
;	T1 = CPU NUMBER
;	PUSHJ P,ONCPUS
;	  HERE IF CPU IS NOT RUNNING ( OR GOING AWAY )
;	HERE WHEN ON CPUN

ONCPUS::PUSH	P,T1		;SAVE CPU NUMBER
	LSH	T1,.CPSOF##	;OFFSET FROM CPU0 CDB
	SKIPL	.C0RUN##(T1)	;CPU MARKED TO GO AWAY
	SKIPLE	.C0OK##(T1)	;OR ALREADY DEAD
	JRST	TPOPJ##		;YES, FAIL RETURN
	POP	P,T1		;RESTORE CPU NUMBER
	AOS	(P)		;GIVE GOOD RETURN

;FALL INTO ONCPUN

;CALLED TO REQUEUE CURRENT JOB TO CPUN (UUO LEVEL CALL ONLY)
;T1=NUMBER OF DESIRED CPU

ONCPUN::PUSHJ	P,SETCPN	;MAKE RUNNABLE ONLY ON THE RIGHT CPU
	CAMN	T1,.CPCPN##	;ARE WE ON THE RIGHT CPU?
	POPJ	P,		;YES, RETURN
	PUSH	P,.UPMP+.UPMUP	;SAVE UUO PC
	MOVEM	17,.JDAT+JOBD17## ;SAVE ACS
	MOVEI	17,.JDAT+JOBDAC##
	BLT	17,.JDAT+JOBD16##
	MOVE	J,.CPJOB##	;SET SO WILL RESCHEDULE TO RIGHT CPU
	MOVE	T1,[XC.UOU##,,ONCPU1]
	MOVEM	T1,.CPPC##
	JRST	CLKSPD##	;RESCHED TO RIGHT CPU

;HERE ON CPU N

ONCPU1:	POP	P,.UPMP+.UPMUP	;RESTORE UUO PC
	POPJ	P,		;AND CONTINUE, ON CORRECT CPU
SUBTTL	MULTIPROCESSING I/O CONTROL

;SUBROUTINE TO CHECK IF ON THE RIGHT CPU FOR IO
;RETURNS DEYCPF-BYTE IN T1
;RETURNS T4 NEGATIVE IF NOT DUAL-PORTED
;PRESERVES T2,T3
CHKCP::
CHKCPI::LDB	T1,DEYCPF##	;GET NUMBER OF 1ST CPU OWNING DEVICE
	HLRO	T4,DEVCPU(F)	;DUAL PORTED DEVICE
	TRNE	T4,DEPCPS	; ON 2 CPUS?
	LDB	T4,DEYCPS##	;YES, GET 2ND CPU WHICH OWNS DEVICE
	CAIN	T1,7		;DEVICE ON GENERIC BOOT CPU
	MOVE	T1,BOOTCP##	;YES, GET CPU NUMBER
	CAME	T1,.CPCPN##	;ARE WE ON THE RIGHT CPU?
	CAMN	T4,.CPCPN##
	AOS	(P)		;YES, SKIP RETURN
	POPJ	P,		;TELL CALLER IF WE ARE OK

SETCPN::PUSHJ	P,SAVE1##	;SAVE P1
	PUSH	P,J		;SAVE J
	MOVE	J,.CPJOB##
	MOVEI	P1,SP.CP0	;RIGHTMOST BIT IS CPU0 (=1)
	LSH	P1,(T1)		;POSITION BIT FOR RIGHT CPU
	TRC	P1,SP.CPA	;NOT RUNNABLE FOR ALL OTHER CPUS
	DPB	P1,MPYNRN##	;NOT RUNNABLE ON ALL BUT CPU N
	JRST	JPOPJ##		;RETURN

;ROUTINES TO KEEP THE NUMBER OF BUFFERS SWEPT FOR, AND THOSE WE CAN DO, CORRECT
IFN FTKL10,<
;SUBROUTINE TO SAVE THE VITAL INFORMATION ABOUT BUFFERS IN THE DDB
;CALLED BY THE FILLER, CHECKED BY THE EMPTIER
STONBF::LDB	T1,PJOBN##	;JOB NUMBER
	MOVE	T1,JBTST3##(T1)	;CPU JOB LAST RAN ON
	LDB	T2,[POINT 1,S,^L<IO,,0>]	;IF A SWEEP IS IN PROGRESS
	TRNE	S,IOACT		; IT MIGHT BE PAST USERS BUFFER.  SINCE
	CONSO	APR,LP.CSB+LP.CSD ; WE DON"T SWEEP AGAIN IF IOACT IS ON,
	SETZ	T2,		; REQUIRE 1 MORE SWEEP FOR OUTPUT
	ADD	T2,.CPCSN##-.CPCDB##(T1)  ;SWEEP SERIAL NUMBER
	SYSPIF
	MOVEM	T2,DEVCSN(F)	;SAVE IN DDB
	MOVE	T2,.CPCPN##-.CPCDB##(T1) ;CPU JOB LAST RAN ON
	DPB	T2,DEYCPU##	;SAVE IN DDB
	JRST	ONPOPJ

;SUBROUTINE TO CHECK THAT WE CAN HAVE THE BUFFERS
;UPDATES LH(DEVNBF) TO REFLECT ALL THE BUFFERS WHICH HAVE BEEN SWEPT FOR
;PRESERVES T2
CKNBF::	PUSHJ	P,CHKCP		;IF WE ARE ON THE CPU WHICH OWNS THE DEVICE
	  JRST	CHKNB
	JUMPL	T4,ADDNBF	;OK IF NOT ON 2 CPUS
	TLNN	S,IO
	JRST	CHKNB
	LDB	T1,DEYPCL##	;CPU WHICH STARTED IO
	CAIN	T1,7		;7 IS A SUBSTITUTE FOR 0
	MOVEI	T1,0
	TRNE	S,IOACT
	CAMN	T1,.CPCPN##
	JRST	ADDNBF		; THEN ALL BUFFERS ARE USABLE

;PRESERVES T3,T2
CHKNB::	SYSPIF
	LDB	T1,DEYCPU##	;CPU JOB STARTED IO ON
	LDB	T4,PJOBN##	;IF NOT WHERE JOB NOW IS,
	MOVE	T4,JBTST3##(T4)
	CAME	T1,.CPCPN##-.CPCDB##(T4)
	JRST	ADDNB1		; WE HAVE SWEPT, SO ADD TO LH
	LSH	T1,.CPSOF##
	MOVE	T1,.C0CSN##(T1)	;CURRENT SWEEP SERIAL NUMBER ON THAT CPU
	CAMG	T1,DEVCSN(F)	;IF THEY ARE THE SAME
	JRST	ONPOPJ		; THAT CPU HASN'T SWEPT, CAN'T HAVE EXTRA BUFFERS
	JRST	ADDNB1

;SUBROUTINE TO UPDATE THE LH(DEVNBF) AND CLEAR THE RH
;PRESERVES T2,T3
ADDNBF:	SYSPIF
ADDNB1:	HRRZ	T1,DEVNBF(F)	;NUMBER OF BUFFERS TO UPDATE BY
	JUMPE	T1,ONPOPJ	;GO IF NOTHING TO ADD
	MOVN	T4,T1		;SET TO CLEAR RH
	HRLI	T4,-1(T1)	;OVERFLOW FROM RH WILL GET THE LAST ONE
	ADDM	T4,DEVNBF(F)	;UPDATE DEVNBF
	JRST	ONPOPJ		;AND RETURN
>;END FTKL10
;SUBROUTINE TO GET JOB ON RIGHT CPU FOR FILE OPERATION
;CALL WITH F = DDB
;PRESERVES T1,T4(I KNOW..)

SETCPF::PUSHJ	P,SAVT##	;SAVE CALLERS
	LDB	T1,DEYPCL##	;IF QUEUED PROTOCOL
	JUMPN	T1,CPOPJ##	; IT CAN RUN ON ANY CPU
SETCF1::PUSHJ	P,CHKCPI
	  CAIA
	MOVE	T1,.CPCPN##
	PUSHJ	P,ONCPUS	;GET ON CORRECT CPU
	  CAIA			;CPU NOT RUNNING
	POPJ	P,		;RETURN WHEN ON CPUN
	MOVSI	T1,DVOFLN	;NO, GET OFFLINE BIT
	IORM	T1,DEVCHR(F)	;LIGHT IN DDB
	PUSHJ	P,CPUOFS	;GET OFFSET TO CPU'S CDB
	MOVSI	T2,100000	;CPU DETACHED BIT
	TDNN	T2,.C0RUN##(T1)	;IS THIS CPU DETACHED
	JRST	[PUSHJ P,HNGSTP## ;COMPLAIN TO USER AND OPERATOR
		 JRST SETCF1]	;AND TRY ALL OVER AGAIN
	PUSHJ	P,RTNEVM##	;RETURN EVM
IFN FTPI,<
	MOVEI	T4,'IDC'	;"IO TO DETACHED CPU" CODE
	PUSHJ	P,PSIJBI##	;SEE IF USER WANTS THE TRAP
	  JRST	[TRO S,IOIMPM!IODERR!IODTER!IOBKTL ;YES, GET ERROR BITS
		 PUSHJ P,CLRACT## ;FOR ANY CORRECTION THE USER MAY WANT
		 JRST ERRGOU##]	;TAKE OFF WITH THE TRAP
> ;END FTPI
	JRST	CPUZAP		;USER DOESN'T WANT TRAP, ZAP HIM

;SUBROUTINE TO RING THE DOORBELL
SETQPB::PUSHJ	P,CPUOFS	;GET RIGHT CPU
	MOVE	T1,.C0QPC##(T1)	;DOORBELL BITS FOR ALL OTHER CPUS
	IORM	T1,DOORBL##	;RING RING
	POPJ	P,

;ROUTINE TO RETURN THE OFFSET FROM CPU0 CDB TO THE CURRENT CPU CDB
CPUOFS::LDB	T1,DEYCPF##	;CPU OWNING THE DEVICE
	LSH	T1,.CPSOF##	;MULTIPLY BY LENGTH OF CDB (OLY WORKS FOR POWER OF TWO)
	POPJ	P,		;RETURN
;CODE CALLED FROM USRXIT TO SEE IF MUST RESCHEDULE

UXITMP::SKIPGE	.CPRUN##	;CAN THIS CPU RUN JOBS
	PJRST	DPXST		;NO, MUST RESCHEDULE
	MOVEI	T1,SP.CR0	;BIT FOR CAN RUN ON CPU0
	LSH	T1,@.CPCPN##	;POSITION FOR CPU PROCESSING UUO
	TDNE	T1,JBTSPS##(J)	;CAN JOB CONTINUE TO RUN HERE
	AOS	(P)		;YES HE CAN, SKIP RETURN
				;FALL INTO "DPXST"

;HERE FROM COMCON, RUN UUO, AND TO CLEAR SP.NR0 AT END OF A UUO

DPXST::	MOVSI	T1,(SP.NRA!SP.ROP)
	ANDCAM	T1,JBTSPS##(J)
	POPJ	P,


;ROUTINE TO CHECK IF JOB CAN RUN ON EITHER OR BOTH CPUS
;CALL:	MOVE	J,JOB NUMBER
;	PUSHJ	P,CHKCPU
;	  JOB IS SET TO RUN ONLY CPU1
;	JOB WAS SET TO RUN ON ALL CPUS OR LOCKED ON A CPU - T1=CPU
;CALLED BY TRPSET AND RTTRP

CHKCPU::MOVE	T1,.CPCPN##
	PUSHJ	P,CPLCK		;ONLY ON THIS CPU
	  CAIA
	JRST	CPOPJ1##
	MOVE	T1,JBTSPS##(J)	;JOB CAN RUN BITS IN SECOND CPU STATUS
	TRNN	T1,SP.CR0	;CAN JOB RUN ON CPU0
	POPJ	P,		;NO, RETURN ON CPU1 ONLY
	MOVEI	T1,SP.CRA-SP.CR0 ;CLEAR CAN RUN ON CPU1
	ANDCAM	T1,JBTSPS##(J)	;SO WILL ONLY RUN ON CPU0
	SETZ	T1,
	JRST	CPOPJ1##	;RETURN ON CPU0 ONLY

;SUBROUTINE TO SEE IF A JOB IS LOCKED ON THE CURRENT CPU
CPLCK::	PUSHJ	P,SAVE1##
	MOVEI	P1,SP.CR0	;POSITION A BIT
	LSH	P1,@.CPCPN	; FOR THE CURRENT CPU
	TRC	P1,SP.CRA	;ALL BITS EXCEPT FOR OUR CPU
	TDNN	P1,JBTSPS##(J)	;IF ALL BITS ARE OFF
	AOS	(P)		; IT CAN RUN ONLY ON THIS CPU
	POPJ	P,
;HERE FROM COMCON TO TEST IF JOB ON SLAVE, BEFORE DOING CONTROL C


SPSTOP::PUSHJ	P,LOKSCD	;TURN ON INTERLOCK
	MOVSI	T2,(SP.CC1)	;SET THE CONTROL C BIT SO SLAVE CANNOT RUN JOB
	IORM	T2,JBTSPS##(J)	;AVOID SLAVE PICKING JOB UP BEFORE IT IS STOPPED
	MOVSI	T2,(SP.CJ0)	;TEST IF JOB IS CURRENT JOB ON SLAVE PROCESSOR
	LSH	T2,@BOOTCP##	;POSITION BIT FOR BOOT CPU
	TLC	T2,(SP.CJA)	;WANT TO ASK IF CURRENT JOB ON ANY OTHER PROCESSOR
	TDNN	T2,JBTSPS##(J)
	AOS	(P)		;NO, SKIP RETURN AND DONT DELAY COMMAND
	JRST	ULKSCD		;TURN OFF INTERLOCK

;HERE TO CLEAR MULTIPROCESSOR STATUS BITS

CLRJSP::PUSH	P,T2		;SAVE A WORKING AC
	MOVSI	T2,(SP.CC1)	;CLEAR LEFT HALF ERROR BITS
	ANDCAM	T2,JBTSPS##(J)
	JRST	T2POPJ##	;RESTORE T2 AND RETURN

;ROUTINE TO SET INITIAL CONDITIONS FOR CPU RUNNABILITY
;CALLED FROM CLOCK1 - CLRJBT

SETJSP::MOVEI	T1,SP.SCA+SP.CPA	;MAKE JOB RUNNABLE ON ALL CPU'S
	MOVEM	T1,JBTSPS##(J)
	POPJ	P,		;AND RETURN

;SUB TO MAKE JOB RUNNABLE ON ALL CPUS
;CLEARING BITS IN LH OF JBTSPS(CALLED FROM COMCON ON KJOB SO LOGOUT CAN RUN
; NO MATER WHAT CPU SPECIFICATION USER MAY HAVE CHANGED TO)

ALLJSP::SKIPA	T1,ALCPUS	;MAKE JOB RUNABLE ON ALL CPUS
				; SKIP INTO CRESET

;SUBROUTINE TO RETURN CPU SPECIFICATION TO THAT SET
;BY LAST "SET CPU" COMMAND.  CALLED FROM "RESET" IN UUOCON

CRESET::LDB	T1,[POINT 6,JBTSPS##(J),29] ;GET LAST COMMAND SET SPECIFICATION
	DPB	T1,[POINT 6,JBTSPS##(J),35] ;OVERRIDE LAST SETUUO
ALCPUS:	POPJ	P,<SP.CPA>B29+SP.CPA	;CAN RUN ON CPU0 AND 1

;ROUTINE TO CLEAR CONTROL C TYPED BIT
;CALLED FROM CLOCK1-WHEN JOB CAN BE STOPPED

CLRCCB::MOVSI	T1,(SP.CC1)	;THIS ROUTINE IS CALLED TO CLEAR SP.CC1 BIT
	ANDCAM	T1,JBTSPS##(J)	;CONTROL C TYPED, SO DON'T RUN ON CPU1.
	POPJ	P,		;AND RETURN
;HERE FROM ANYDEV TO MAKE IT LOOK LIKE JOB HAS ACTIVE DEVICES
;IF JOB IS CURRENTLY BEING RUN ON THE SLAVE PROCESSOR


ANYRUN::PUSHJ	P,SAVE2##	;SAVE A COUPLE OF WORKING REGS
	MOVEI	P1,.C0CDB##	;START WITH CPU0'S CDB
ANYRN:	PUSHJ	P,LOKSCD	;TURN ON INTERLOCK
ANYRN1:	CAMN	P1,.CPSLF##	;SKIP CALLING CPU
	JRST	ANYRN2		;STEP TO NEXT
IFN FTTRPSET,<
	CAMN	J,.CPSTS##-.CPCDB##(P1) ;STOPPING TIMESHARING FOR THIS JOB
	JRST	ULKSCD		;YES, GO ULKSCD AND POPJ RETURN
>
	MOVE	P2,.CPJOB##-.CPCDB##(P1) ;GET JOB CURRENTLY ON THIS CPU
	CAILE	J,JOBMAX##	;CALLER WANT JOB OR SEGMENT NUMBER
	HRRZ	P2,JBTSGN##(P2)	;GET HIGH SEGMENT NUMBER FOR THAT JOB
	CAIN	P2,(J)		;IS JOB RUNNING OR USING SAME SEGMENT
	JRST	ULKSCD		;YES, GIVE NO SWAP RETURN
ANYRN2:	HLRZ	P1,.CPCDB##-.CPCDB##(P1) ;STEP TO NEXT CDB IN SYSTEM
	JUMPN	P1,ANYRN1	;AND CONTINUE LOOKING
	AOS	(P)		;OK TO SWAP THIS JOB
	JRST	ULKSCD		;UNLOCK SCHEDULER AND RETURN
	SUBTTL	MULTIPROCESSING OPR/USER FAILING CPU INTERFACE

;HERE ON BOOT CPU FROM CLOCK1 ONCE A MINUTE TO CHECK IF OTHER CPU'S ARE RUNNING
;IF CPUN IS NOT RUNNING, TYPE OUT APPROPRIATE MESSAGES TO OPERATOR/USER

CP1CHK::PUSHJ	P,CPRMIN	;CHECK TO SEE IF ALL JOBS HAVE RUNNABLE SPECIFICATIONS
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,.C0CDB##	;ADDRESS OF CPU0'S CDB (START THERE)
CP1CH1:	SKIPLE	.CPOK##-.CPCDB##(P1) ;THIS CPU OK?
	SKIPGE	T1,.CPRUN##-.CPCDB##(P1) ;NO, SHOULD IT BE COUNTING ITS OK WORD?
	JRST	CP1CH6		;EITHER RUNNING OR SHOULDN'D BE, TRECK ON
	TLNE	T1,40000
	JRST	CP1CH6
	HRRZ	U,OPRLDB##	;ADDRESS OF THE OPR LDB
	MOVEI	T1,[ASCIZ /%
%Problem on /]			;TEXT FOR COMPLAINT
	PUSHJ	P,CONMES##	;GO TELL THE OPR THAT THE CPU DIED
	MOVE	T2,.CPLOG##-.CPCDB##(P1) ;LOGICAL NAME OF THE DEAD CPU
	PUSHJ	P,PRNAME##	;TELL THE OPERATOR THAT
	MOVEI	T1,[ASCIZ/ serial # /] ;TEXT
	PUSHJ	P,CONMES##	;EXPLAINATION OF THE NEXT NUMBER
	MOVE	T1,.CPASN##-.CPCDB##(P1) ;DEAD CPU'S SERIAL NUMBER
	PUSHJ	P,RADX10##	;TELL THE OPERATOR THAT SINCE IT MAY NOT BE OBVIOUS
	PUSHJ	P,PCRLF##	;COMPLETE THE LINE
IFN FTKL10,<
	MOVE	J,HIGHJB##	;HIGHEST JOB ON THE SYSTEM
CP1CH2:	PUSHJ	P,RWRTCH	;JOB RIGHT WITH RESPECT TO THE CACHE ON THE DEAD CPU?
	  JRST	CP1CH4		;YES, IT CAN STILL RUN
	CAMN	J,MMUSER##	;OWN THE MM RESOURCE?
	JRST	CP1CH3		;YES, THAT WOULD HANG THE SYSTEM SO WARN THE OPR
>;END IFN FTKL10
IFN FTKI10,<
	SKIPN	J,.CPJOB##-.CPCDB##(P1) ;JOB ON THE DEAD CPU
	JRST	CP1CH4		;OK IF NO JOB IS RUNNING ON THE DEAD CPU
	CAMN	J,MMUSER##	;DOES THE JOB OWN THE MM RESOURCE?
	JRST	CP1CH3		;YES, TELL THE OPR ABOUT THE PROBLEM
>;END IFN FTKI10
	PUSHJ	P,FLSDR##	;DOES THE JOB OWN A RESOURCE?
	  JRST	CP1CH4		;NO, JOB DOESN'T JEOPARDIZE THE SYSTEM
CP1CH3:	MOVEI	T1,[ASCIZ /(system will do no useful work until /]
	PUSHJ	P,CONMES##	;EXPLAIN THE PROBLEM TO THE OPERATOR
	MOVE	T2,.CPLOG##-.CPCDB##(P1) ;SICK CPU'S NAME
	PUSHJ	P,PRNAME##	;TELL THE OPR AGAIN
	MOVEI	T1,[ASCIZ /
is restarted at 400 or DETACHed)
/]
	PUSHJ	P,CONMES##	;TELL THE OPR HOW TO GET IT GOING AGAIN
IFN FTKL10,<
	JRST	CP1CH5		;OPR HAS BEEN TOLD, DON'T COMPLAIN AGAIN ABOUT THIS CPU
>;END IFN FTKL10
CP1CH4:
IFN FTKL10,<
	SOJG	J,CP1CH2	;LOOP OVER ALL CPUS
>;END IFN FTKL10
CP1CH5:	MOVE	T1,STATES##	;CURRENT STATES SETTING
	TRNE	T1,ST.NOP	;IS THERE AN OPERATOR IN ATTENDANCE?
	PUSHJ	P,[PUSHJ P,FRCSET## ;SETUP TO A TYPE A COMMAND ON FRCLIN
		   PUSHJ P,INLMES## ;DETACH CPUN
		   ASCIZ /Detach /
		   MOVE T2,.CPLOG##-.CPCDB##(P1) ;CPUN
		   PUSHJ P,PRNAME## ;ADD THAT TO THE COMMAND
		   PJRST PCRLF##] ;END THE COMMAND WITH A BREAK
CP1CH6:	HLRZ	P1,.CPCDB##-.CPCDB##(P1) ;NEXT CPU'S CDB ADDRESS
	JUMPN	P1,CP1CH1	;LOOP IF THERE IS YET ANOTHER CPU
	POPJ	P,		;RETURN

;SUBROUTINE TO CHECK IF THIS JOB IS RUNNABLE W.R.T. THE CACHE ON THE
; CPU WHOSE CDB IS IN P1
;CALLING SEQUENCE:
;	MOVEI	P1,CDB OF THE CPU IN QUESTION
;	MOVE	J,JOB NUMBER OF JOB IN QUESTION
;	PUSHJ	P,RWRTCH
;	  OK TO RUN ON OTHER CPUS
;	RUNNABLE ONLY W.R.T. P1 CPUS CACHE
;

IFN FTKL10,<
RWRTCH:	HRRZ	T3,JBTST3##(J)	;CDB OF LAST CPU JOB RAN ON
	SKIPE	JBTADR##(J)	;RUNNABLE ON ALL CPUS IF SWAPPED
	CAME	T3,P1		;DID JOB LAST RUN ON CPU IN QUESTION?
	POPJ	P,		;NO, JOB IS RUNNABLE W.R.T. P1 CPUS CACHE
	MOVE	T4,.CPCSN##-.CPCDB##(P1) ;CACHE SWEEP SERIAL NUMBER FOR P1 CPU
	CAMLE	T4,JBTCSN##(J)	;DID P1 CPU SWEEP SINCE THE LAST TIME IT RAN THIS JOB?
	CAMN	J,.CPJOB##-.CPCDB##(P1) ;CURRENT JOB ON DEAD CPU?
	AOS	(P)		;NO, ONLY RUNNABLE W.R.T. P1 CPUS CACHE
	POPJ	P,		;RETURN
>
;ROUTINE TO FIND NEXT STR WHICH CAN NO LONGER BE ACCESSED BECAUSE OF
; A DEAD CPU
;CALLING SEQUENCE:
;	MOVEI	P1,CDB OF THE DEAD CPU
;	PUSHJ	P,DEDSTR
;	  HERE IF NONE (LEFT)
;	HERE IF ONE FOUND, T1=STR NAME

DEDSTR:	MOVE	T4,STRAOB##	;POINTER TO TABSTR
DEDST1:	SKIPN	T2,TABSTR##(T4)	;NEXT STR DATA BLOCK
	JRST	DEDST4		;THAT ENTRY IS ZERO
	HLRZ	T2,STRUNI##(T2)	;FIRST UNIT IN THE FILE STR
DEDST2:	PUSHJ	P,DEDUN		;SEE IF UNIT CAN STILL BE ACCESSED
	  JRST	DEDST3		;YES, ALL IS WELL SO FAR
	MOVE	T1,@TABSTR##(T4);FILE STRUCTURE NAME
	JRST	CPOPJ1##	;FILE STRUCTURE CANNOT BE ACCESSED
DEDST3:	HLRZ	T2,UNISTR##(T2)	;NEXT UNIT
	JUMPN	T2,DEDST2	;CHECK IT IF THERE IS ONE
DEDST4:	AOBJN	T4,DEDST1	;NEXT FILE STRUCTURE
	POPJ	P,		;ALL HAVE BEEN REMOVED

;SUBROUTINE TO SEE IF THERE IS STILL A PATH TO A UNIT AFTER A CPU HAS
; DIED
;CALLING SEQUENCE:
;	MOVEI	P1,CDB OF THE CPU IN QUESTION
;	MOVE	T2,ADDRESS OF UDB FOR UNIT IN QUESTION
;	PUSHJ	P,DEDUNI
;	  RETURNS HERE IF UNIT CAN STILL BE ACCESSED
;	HERE IF NO PATH TO THE UNIT
;RETURNS T2 = UDB
;DEDUN PRESERVES T4

DEDUN:
DEDUNI:	MOVE	T1,UNIKON##(T2)	;KONTROLLER
	LDB	T1,KOXCPU##	;CPU THIS KONTROLLER IS ON
IFN FTDUAL,<
	SKIPE	T3,UNI2ND##(T2)	;SECOND PORT, IS THERE ONE?
	SKIPA	T3,UNIKON##(T3)	;YES, GET KDB
	SKIPA	T3,T1		;NO, GET CPU NUMBER FROM ONLY PORT
	LDB	T3,KOZCPU##	;SECOND PORT CPU NUMBER
	CAMN	T3,.CPCPN##-.CPCDB##(P1) ;STILL GET AT THE SECONDARY PORT?
>
	CAME	T1,.CPCPN##-.CPCDB##(P1) ;OR AT THE PRIMARY PORT?
	POPJ	P,		;UNIT CAN BE REACHED
	JRST	CPOPJ1##	;NO PATH TO THE UNIT
;HERE TO ATTACH A CPU

CPUATT::LSH	T1,.CPSOF##	;OFFSET TO CPU CDB
	ADDI	T1,.C0CDB##	;ADDRESS OF CDB
	MOVSI	T2,100000	;CPU'S DETACHED BIT
	TDNN	T2,.CPRUN##-.CPCDB##(T1) ;IS THE CPU DETACHED?
	JRST	COMERA##	;NO, ERROR
	TLO	T2,400000	;ALLOW JOBS
	ANDCAM	T2,.CPRUN##-.CPCDB##(T1) ;ALLOW THE CPU TO START
	HRLI	T1,.CSCAT	;CODE FOR ATTACH
	PJRST	CPUCSC		;LOG ATTACH AND RETURN
;HERE TO DETACH A CPU

CPUDET::MOVE	P1,T1		;CPU NUMBER TO P1
	LSH	P1,.CPSOF##	;CDB OFFSET
	ADDI	P1,.C0CDB##	;ADDRESS OF THE CDB
	SKIPG	.CPOK##-.CPCDB##(P1) ;IS THE CPU CURRENTLY RUNNING?
	JRST	COMERA##	;YES, CANNOT DETACH A RUNNING CPU
	MOVSI	T1,500000	;CPU'S DETACHED BIT
	IORM	T1,.CPRUN##-.CPCDB##(P1) ;INDICATE CPU IS DETACHED
	PUSH	P,J		;SAVE JOB NUMBER
	MOVEI	T1,(P1)		;CDB ADDRESS TO T1
	HRLI	T1,.CSCDT	;CODE FOR DETACH
	PUSHJ	P,CPUCSC	;LOG DETACH
IFN FTKL10,<
	MOVE	J,HIGHJB##	;HIGHEST JOB ON THE SYSTEM
CPUDE1:	PUSHJ	P,RWRTCH	;JOB RUNNABLE W.R.T. THE CACHE ON THE DEAD CPU?
	  CAIA			;YES
>
IFN FTKI10,<
	SKIPE	J,.CPJOB##-.CPCDB##(P1) ;CURRENT JOB ON THE DEAD CPU
>
	PUSHJ	P,CPUZAP	;NO, ZAP THE JOB
IFN FTKL10,<
	SOJG	J,CPUDE1	;LOOP OVER ALL JOBS
> ;END FTKL10
	POP	P,J		;RESTORE JOB NUMBER OF JOB DOING THE COMMAMD
	SETZM	.CPJOB##-.CPCDB##(P1) ;ZERO CURRENT JOB ON DEAD CPU
	PUSH	P,F		;SAVE TTY DDB
	HLRZ	F,HNGLST##	;START OF DEVICE CHAIN
CPUD1A:	LDB	T1,DEYCPF##	;CPU OWNING THIS DEVICE
	MOVE	S,DEVIOS(F)	;I/O STATUS
	CAMN	T1,.CPCPN##-.CPCDB##(P1) ;DEVICE ON CPU BEING DETACHED?
	TRNN	S,IOACT		;YES, WAS I/O ACTIVE WHEN CPU STOPPED?
	JRST	CPUD1B		;NO, LOOK AT NEXT DEVICE
	PUSHJ	P,DEVERR##	;CLEAR IOACT/SET TROUBLE
CPUD1B:	HLRZ	F,DEVSER(F)	;NEXT DEVICE
	JUMPN	F,CPUD1A	;LOOP IF THERE IS ONE
	POP	P,F		;RESTORE F
	MOVE	S,P1		;SAVE CDB ADDRESS
	JSP	T2,SAVCTD##	;MUST BE AT UUO LEVEL TO REMOVE FILE STRUCTURES
	MOVE	P1,S		;GET CDB ADDRESS BACK
;HERE TO REMOVE FILE STRUCTURES WHICH ARE INACCESSIBLE
CPUDE2:	PUSHJ	P,DEDSTR	;SEE IF THERE ARE ANY FILE STRS WHICH CANNOT BE ACCESSED
	  JRST	CPUDE3		;NONE OR FINISHED
	PUSHJ	P,RMVSTR##	;REMOVE THE INACCESSIBLE FILE STRUCTURE
	  JFCL			;MIGHT NOT SKIP
	JRST	CPUDE2		;NEXT STR
;HERE TO SEE THAT SWAPPING SPACE IS NOT ALLOCATED ON INACCESSIBLE UNITS
CPUDE3:	MOVEI	T4,0		;START AT FIRST ENTRY IN SWPTAB
	PUSHJ	P,SAVE4##	;DODELE CLOBBERS P ACS
CPUDE4:	MOVE	P1,-4(P)	;CPU NUMBER SINCE DODELE MAY HAVE BEEN CALLED
	SKIPE	T2,SWPTAB##(T4)	;NEXT UNIT IN THE ACTIVE SWAPPING LIST
	PUSHJ	P,DEDUN		;STILL ABLE TO GET AT THIS UNIT?
	  JRST	CPUDE5		;YES, LOOP ON
	MOVEI	P2,-1		;+ INFINITY
	MOVE	U,T2		;UNIT
	PUSH	P,T4		;DON'T COUNT ON DODELE PRESERRVING T4
	PUSHJ	P,[PUSHJ P,GGVMM ;OBTAIN MM INTERLOCK
		   JRST DODELE##] ;REMOVE DORMANT SEGMENTS FROM UNREACHABLE UNIT
	  JFCL			;SHOULD NEVER SKIP
	POP	P,T4
	MOVN	T1,UNIFKS##(T2)	;FREE SWAPPING SPACE ON THIS UNIT
	ADDM	T1,UNIFKS##(T2)	;ZAP, NO FREE SPACE
	ADDM	T1,VIRTAL##	;AND DECREMENT TOTAL AMOUNT OF FREE SPACE AVAILABLE
CPUDE5:	CAIGE	T4,SWPMAX##	;LOOKED AT ALL THE UNITS IN THE A.S.L.?
	AOJA	T4,CPUDE4	;NO, LOOK AT THE NEXT UNIT
	HRRZ	U,OPRLDB##	;OPR'S LDB
	MOVE	T2,.CPLOG##-.CPCDB##(P1) ;CPU'S NAME
	PUSHJ	P,PRNAME##	;TELL THE OPR WHAT CPU
	PJSP	T1,CONMES##	; WAS DETACHED
	ASCIZ	/ Detached
/
;SUBROUTINE TO ZAP THE CURRENT JOB ON A DEAD CPU, OR TO ZAP A JOB WHICH
; ATTEMPTS TO DO I/O TO A DEVICE ON A DEAD CPU, OR TO ZAP A JOB WHICH
; MIGHT HAVE DATA IN THE DEAD CPU'S CACHE
;CALLING SEQUENCE:
;	MOVE	J,JOB NUMBER
;	PUSHJ	P,CPUZAP
;	ALWAYS RETURN HERE

CPUZAP:	PUSHJ	P,SVEUB##	;MAKE SURE THE JOB IS ADDRESSABLE
	PUSHJ	P,GIVRSC##	;GIVE UP ANY RESOURCES THE JOB MAY OWN
	PUSHJ	P,ZAPUSC##	;RELEASE ALL DEVICES
	PUSHJ	P,CLRJOB##	;CLEAR JOBJDA
	MOVSI	T1,(SP.CJ0)	;CURRENT JOB ON CPU0 BIT
	LSH	T1,@.CPCPN##-.CPCDB##(P1) ;POSITION IT TO DEAD CPU
	TLO	T1,(SP.NRA!SP.ROP) ;CLEAR NOT RUNNABLE BITS TOO
	ANDCAM	T1,JBTSPS(J)	;CLEAR CURRENT JOB ON DEAD CPU BIT
IFN FTKL10,<
	PUSHJ	P,CLCSN		;CLEAR SWEEP SERIAL NUMBER SO JOB ISN'T STUCK
>
	JSP	T1,ERRPNT##	;INFORM THE USER
	ASCIZ	/CPU failed/
	PJRST	PCSTOP##	;TELL THE USER THE PC AND STOP THE JOB


;SUBROUTINE TO MAKE A DAEMON ENTRY FOR CPU ATTACH/DETACH.
;CALLING SEQUENCE:
;	MOVE	T1,[CODE,,CDB ADDRESS
;	PUSHJ	P,CPUCSC
;	ALWAYS	RETURN HERE

CPUCSC:	PUSH	P,U		;SAVE U
	PUSH	P,F		;  AND F
	MOVEI	F,0		;NO DDB ADDRESS FOR THIS ERROR
	MOVEI	U,.CPLOG##-.CPCDB##(T1) ;U POINTS AT CPU NAME
	HRRI	T1,.ERCSC	;ERROR CODE FOR DAEMON
	PUSHJ	P,DAEEIM##	;NOTIFY DAEMON
	JRST	FUPOPJ##	;RESTORE F AND U AND RETURN
SUBTTL	INTERLOCK ROUTINES

;INTERLOCK ROUTINES - LOKSCD, ULKSCD
;CALLED BY PUSHJ TO ENTER CODE WHICH MUST RUN TO COMPLETION
;ON THE CURRENT PROCESSOR BEFORE IT (OR ANY INTERLOCKED
;SECTIONS) IS ENTERED BY THE OTHER PROCESSOR.
;
;	PUSHJ P,LOKSCD
;	...INTERLOCKED CODE...
;	PUSHJ P,ULKSCD
;
;THERE IN FACT CAN EXIST SEVERAL LEVELS OF INTERLOCK, OF WHICH
;ONLY THE LOWEST LEVEL IS REAL, REST ARE VIRTUAL.  NECESSARY
;BECAUSE OF THE EXISTENCE OF ROUTINES WHICH MUST THEMSELVES
;BE INTERLOCKED BUT WHICH ARE CALLED BOTH BY INTERLOCKED CODE
;AND UN-INTERLOCKED CODE.  (EXAMPLE: QQXFER)
;THE LOCATIONS .C0SCD,.C1SCD SHOULD BE INITIALIZED TO -1
;AT SYSTEM STARTUP TIME.

LOKSCD::AOSE	.CPSCD##	;BUMP LEVEL OF INTERLOCK
	POPJ	P,		;JUST NESTING, STILL HAVE INTERLOCK
LOKSC1:	AOSE	SCDLOK##	;GET SYSTEM INTERLOCK
	AOSA	.CPLLC##	;COUNT INTERFERENCE
	AOSA	.CPRSI##	;GOT IT, FILL IN CPU ID
	JRST	LOKSC1		;TRY AGAIN
IFN FTKI10,<
	CONI	PAG,INOSCD##
>
IFN FTKL10,<
	APRID	INOSCD##
>
	POPJ	P,		;RETURN WITH INTERLOCK

;ROUTINE TO LOKSCD, DISPATCH, ULKSCD, AND RETURN.
; CALLED FROM ANY ROUTINE WHICH IS CALLED BY A PUSHJ
; AND EXPECTS TO RETURN WITH POPJ OR AOS (P), POPJ.

SBSCD::	PUSHJ	P,LOKSCD	;TURN ON INTERLOCK
	PUSHJ	P,@(P)		;DISPATCH TO ROUTINE
	  CAIA
	AOS	-1(P)		;PROVIDE FOR SKIP RETURN
	POP	P,(P)		;FIX PUSHDOWN TO RETURN TO ORIGINAL CALLER
ULKSCD::PUSH	P,T1		;SAVE AN AC
	SOSL	T1,.CPSCD##	;DECREMENT LEVEL
	JRST	TPOPJ##		;STILL NESTED ON THIS CPU, RETURN
	EXCH	T1,SCDLOK##	;RELEASE SYSTEM INTERLOCK
	SETOM	INOSCD##	;TELL THE WORLD WE DON'T OWN IT
	JUMPGE	T1,TPOPJ##	;ALL IS WELL IF INTERLOCK TAKEN
	STOPCD	TPOPJ##,DEBUG,SAU,	;++SCHEDULER ALREADY UNLOCKED

;ROUTINE TO ATTEMPT TO OBTAIN THE CORE ALLOCATION RESOURCE
; IN SCHEDULAR CONTEXT
;N.B. MUST OWN THE SCHEDULAR INTERLOCK WHEN CALLING SCDMM
;CALLING SEQUENCE:
;	PUSHJ	P,GETMM
;	  RETURNS HERE IF RESOURCE IS NOT CURRENTLY AVAILABLE
;	RETURNS HERE, CALLER OWNS THE RESOURCE
;NOTE, THE CALLER MUST TAKE RESPONSIBILITY FOR TRYING AGAIN
; LATER (DLYCOM, SWAPPER, ETC.) IF THE RESOURCE IS NOT CURRENTLY
; AVAILABLE. ANY ROUTINE WHICH OBTAINS THE LOCK BY CALLING GETMM
; MUST GIVE IT UP BY CALLING GIVMM

GETMM::	PUSHJ	P,SBSCD		;CALL COROUTINE TO GET THE SCHEDULER INTERLOCK
;HERE IF SCHEDULER INTERLOCK IS ALREADY OWNED
SCDMM::	AOSE	MMREQ##		;SEE IF THE RESOURCE IS AVAILABLE
	SKIPE	MMAVAL##	;OR AVAILABLE, DIDN'T GIVE IT TO A JOB
				; WAITING FOR IT BECAUSE IT WAS BEING SWAPPED, ETC.
	AOSA	(P)		;AVAILABLE, SET FOR RESOURCE AVAILABLE RETURN
	JRST	SCDMM1		;NOT NOW, UNDO THE AOS
	SETZM	MMAVAL##	;MAKE SURE THE SCHEDULAR DOESN'T GIVE IT
				; TO A UUO LEVEL WAITER UNTIL WE GIVE IT UP
				;MUST HAVE THE SCHEDULAR INTERLOCK TO DO THIS
IFN FTKL10,<
	PUSH	P,CORTAL##	;CURRENT VALUE OF CORTAL
	POP	P,SAVCTL##	;SAVE IT TO DETERMINE IF A SWEEP IS NECESSARY
				; WHEN THE MM IS GIVEN UP
	APRID	INTLMO##	;NOTE THAT THIS CPU IS THE OWNER
>
IFN FTKI10,<
	CONI	PAG,INTLMO##	;SERIAL NUMBER OF THE OWNING CPU
>
	POPJ	P,		;RETURN WITH THE LOCK
SCDMM1:	SOS	MMREQ##		;UNDO THE AOS
	POPJ	P,		;GIVE RESOURCE NOT AVAILABLE RETURN

;SUBROUTINE TO GIVE UP THE MM RESOURCE WHEN IT WAS OBTAINED BY
; CALLING GETMM
;CALLING SEQUENCE:
;	PUSHJ	P,GIVMM
;	ALWAYS RETURNS HERE

GIVMM::	SETOM	INTLMO##	;MARK THE FACT THAT THIS CPU NO LONGER OWNS IT
	PJRST	DWNMM		;RETURN THE RESOURCE AND RETURN
;SUBROUTINE TO SEE IF THE CURRENT JOB OWNS THE MM RESOURCE. CPOPJ IF NOT, CPOPJ1 IF SO
MMOWN::	PUSH	P,J		;SAVE J
	MOVE	J,INTLMO##	;OWNER OF THE MM RESOURCE IF OWNED BY A CPU
IFN FTKI10,<
	LSH	J,-^D26		;ISOLATE THE SERIAL NUMBER
>
IFN FTKL10,<
	ANDI	J,7777		;ISOLATE THE SERIAL NUMBER
>
	CAMN	J,.CPASN##	;OWN THE RESOURCE AT INTERRUPT LEVEL?
	JRST	JPOPJ1##	;YES, GIVE OWNED RETURN
	MOVE	J,.CPJOB##	;CURRENT JOB
	CAMN	J,MMUSER##	;OWN THE MEMORY MANAGEMENT RESOURCE?
	JRST	JPOPJ1##	;YES
	JRST	JPOPJ##		;NO
;SUBROUTINE TO SEE IF A JOB OWNS THE MM RESOURCE AND GIVE IT UP IF
; THIS ROUTINE REMEMBERS THAT IT WAS OWNED AND A MATCHING CALL TO
; UIFMM MUST FOLLOW IT
DIFMM::	PUSHJ	P,MMOWN		;OWN THE MM RESOURCE?
	  POPJ	P,		;NO
	PUSH	P,T1		;YES
	MOVSI	T1,(UP.MMO)	;JOB OWNED THE MM BIT
	IORM	T1,.UPMP+.UPBTS	;REMEMBER THAT
	PUSHJ	P,DWNMM		;GIVE IT UP
	JRST	TPOPJ##		;RESTORE T1 AND RETURN

;SUBROUTINE TO GET BACK THE MM RESOURCE IF A JOB OWNED IT WHEN DIFMM
; WAS CALLED
UIFMM::	PUSH	P,T1		;SAVE T1
	MOVSI	T1,(UP.MMO)	;JOB OWNED THE MM RESOURCE BIT
	TDNE	T1,.UPMP+.UPBTS	;DID THE JOB OWN IT?
	PUSHJ	P,UPMM		;YES, GET IT BACK
	ANDCAM	T1,.UPMP+.UPBTS	;CLEAR THE BIT
	JRST	TPOPJ##		;AND RETURN

;COROUTINE TO SAVE AND RESTORE THE STATE OF THE MM RESOURCE
REMMM::	PUSHJ	P,DIFMM		;SAVE STATE OF MM, FREE IT IF OWNED
	POP	P,(P)		;POP CALLER'S RETURN PC
	PUSHJ	P,@1(P)		;RETURN TO CALLER
	  CAIA			;NON- SKIP
	AOS	(P)		;SKIP
	JRST	UIFMM		;RESTORE IT IF OWNED

;COROUTINE TO GET THE MM RESOURCE AND GIVE IT UP ON RETURN
GGVMM::	PUSHJ	P,UPMM		;GET THE MM RESOURCE
	POP	P,(P)		;POP CALLER'S RETURN PC
	PUSHJ	P,@1(P)		;RETURN TO CALLER
	  CAIA			;NON-SKIP
	AOS	(P)		;SKIP
	PJRST	DWNMM		;GIVE UP THE MM RESOURCE
;SUBROUTINE TO GET THE MM RESOURCE AND WORRY ABOUT CACHE IF PAGES
; ARE DEALLOCATED
UPMM::	PUSH	P,F		;SAVE F
	SETZ	F,		;SO SRWAIT DOESN'T WORRY ABOUT EVM
	PUSHJ	P,MMWAIT##	;GET THE MM RESOURCE
IFN FTKL10,<
	MOVE	F,CORTAL##	;CURRENT NUMBER OF FREE OR FREEABLE PAGES
	MOVEM	F,SAVCTL##	;REMEMBER TO SEE IF CORE WAS DEALLOCATED
>
	JRST	FPOPJ##		;RESTORE F AND RETURN

;SUBROUTINE TO GIVE UP THE MM IF OWNED
TGVMM::PUSHJ	P,MMOWN		;OWN THE MM?
	 POPJ	P,		;NO, NOOP
;FALL INTO DWNMM

;SUBROUTINE TO GIVE UP THE MM RESOURCE AND SWEEP THE CACHE IF CORE
; HAS BEEN DEALLOCATED (NECESSARY SINCE WRITTEN BITS MAY BE ON IN DEALLOCATED PAGES)
DWNMM::	PUSH	P,F		;SAVE F
IFN FTKL10,<
	MOVE	F,CORTAL##	;CURRENT CORTAL
	PUSHJ	P,MLSCSH	;IS THE LOW SEGMENT UNCACHED
	  CAMG	F,SAVCTL##	;HAS CORTAL INCREASED SINCE CALL TO UPMM? (DEALLOCATION)
	JRST	DWNMM1		;NO, OR LAST CPU, DON'T BOTHER WITH SWEEP
	AOS	.CPSDA##	;YES, COUNT THE NUMBER OF SWEEPS REQUIRED BY DEALLOCATION
	PUSHJ	P,CSDMP##	;SWEEP - MUST DO IT WHILE STILL OWNING THE MM
DWNMM1:>
	SETZ	F,		;ZERO F (NO EVM)
	PUSHJ	P,MMFREE##	;RETURN THE MM
	JRST	FPOPJ##		;RESTORE F AND RETURN
;THIS ROUTINE IMPLEMENTS A SYSTEM WIDE INTERLOCK. IT IS EXACTLY
; SYNONUMOUS TO TURNING THE PI SYSTEM OFF ON A SINGLE PROCESSOR SYSTEM
; EXCEPT IT DOES NOT ALLOW NESTING, I.E., A CALL TO UNLSPI MUST BE
; EXECUTED BEFORE LOKSPI CAN BE CALLED AGAIN.

LOKSPI::CONO	PI,PI.OFF	;NO PIS
	CONI	PI,.CPPIS##	;READ CURRENT STATE OF CHANNEL ENABLES
	CONO	PI,PI.ON+SYSOFF## ;TURN THE PI SYSTEM ON,
				; LEAVING OFF CHANNELS WHICH OBTAIN THE INTERLOCK
	AOSE	INTRLK##	;INTERLOCK AVAILABLE?
	JRST	.-1		;NO, WAIT UNTIL IT IS
IFN FTKI10,<
	CONI	PAG,INTOLK##	;SERIAL NUMBER OF THE CPU WHICH OWNS THE INTERLOCK
>
IFN FTKL10,<
	APRID	INTOLK##	;SERIAL NUMBER OF OWNING CPU
>
	POPJ	P,		;RETURN WITH THE INTERLOCK

;THIS ROUTINE RETURNS THE SYSTEM WIDE INTERLOCK AND EXITS WITH THE PI
; SYSTEM TURNED OFF

UNLSPT::EXCH	T1,.CPPIS##	;SAVE T1, GET STATE OF THE PI WHEN INTERLOCK WAS OBTAINED
	ANDI	T1,177		;ISOLATE CHANNEL ENABLES
	TRO	T1,PI.OFF	;EXIT WITH PI OFF
	JRST	UNLSP1		;JOIN COMMON CODE

;COMMON EXIT ROUTINE TO RETURN SYSTEM WIDE INTERLOCK AND POPJ

ONPOPJ::			;FALL INTO UNLSPI
;	SYSPIN
;	POPJ	P,

;ROUTINE TO GIVE UP THE SYSTEM WIDE INTERLOCK, RESTORE THE CHANNELS WHICH
; WERE ENABLED WHEN THE INTERLOCK WAS OBTAINED, AND EXIT WITH THE PI IN
; THE STATE IT WAS IN WHEN THE INTERLOCK WAS OBTAINED - ON IF ON/OFF IF OFF

UNLSPI::EXCH	T1,.CPPIS##	;SAVE T1, GET STATE OF THE PI WHEN INTERLOCK WAS OBTAINED
	ANDI	T1,177		;ISOLATE CHANNEL ENABLES
UNLSP1:	TRO	T1,PI.TNP	;TURN ON SELECTED CHANNELS
	EXCH	T1,.CPPIS##	;RESTORE T1, SET TO RESTORE PI SYSTEM
	SETOM	INTRLK##	;RELINQUISH INTERLOCK
	SETOM	INTOLK##	;NO LONGER THE OWNER OF THE INTERLOCK
	CONO	PI,@.CPPIS##	;RESTORE THE PI SYSTEM
	POPJ	P,		;AND RETURN
;ROUTINE TO INTERLOCK ON A DEVICE TO ENSURE THAT THE USER PROGRAM USING
; THE DEVICE IS NOT RUNNING AT UUO LEVEL ON ONE PROCESSOR "PIOFF" WHILE AN
; INTERRUPT FROM THE DEVICE IS BEING SERVICED ON ANOTHER PROCESSOR.
; THIS CAN NOT HAPPEN ON A SINGLE CPU BECAUSE OF CONO PI,PI.OFF.
; THE INTERLOCK IS ONLY BETWEEN UUO LEVEL AND THE INTERRUPT LEVEL WHICH
; THE DEVICE INTERRUPTS TO. THE INTERLOCK IS ONLY ON THE CHANNEL THAT THE DEVICE
; INTERRUPTS ON, I.E., IF A PROCESSOR IS WAITING AT AN INTERUPT LEVEL
; FOR AN INTERLOCK OWNED BY SOME OTHER PROCESSOR AT UUO LEVEL, INTERRUPTS
; AT HIGHER PI LEVELS ON THE WAITING PROCESSOR CAN STILL BE SERVICED.

LOKDPI::CONO	PI,PI.OFF	;TURN THE PI SYSTEM OFF
	CONI	PI,.CPDPI##	;READ PI CHANNEL ENABLES
	CONO	PI,PI.ON+SYSOFF## ;TURN PI ON WITH DEVICE CHANNELS OFF
	AOSE	@DEVCPU(F)	;DDB INTERLOCK AVAILABLE?
	JRST	.-1		;NO, WAIT FOR IT
	PUSH	P,T1		;SAVE A TEMPORARY
	HRRZ	T1,DEVCPU(F)	;ADDRESS OF THIS DDB'S INTERLOCK WORD
	SUBI	T1,INTL0##	;OFFSET INTO INTERLOCKS
IFN FTKI10,<
	CONI	PAG,INTO0##(T1)	;OWNING PROCESSOR'S SERIAL NUMBER
>
IFN FTKL10,<
	APRID	INTO0##(T1)	;REMEMBER WHICH CPU OWNS THE INTERLOCK
>
	JRST	TPOPJ##		;RESTORE T1 AND RETURN

;COMMON EXIT ROUTINE TO RETURN DEVICE PI INTERLOCK
; AND RETURN WITH PI RESTORED

ONPOPD::			;FALL INTO UNLDPI
;	PION
;	POPJ	P,

;ROUTINE TO RETURN THE DEVICE INTERLOCK, MARK IT AS UNOWNED, AND
; RESTORE THE PI SYSTEM TO THE STATE IT WAS IN WHEN THE DVEICE WAS
; LOCKED

UNLDPI::PUSH	P,T1		;SAVE A WORKING AC
	MOVE	T1,.CPDPI##	;CHANNEL ENABLES WHEN INTERLOCK WAS OBTAINED
	ANDI	T1,177		;ONLY CHANNEL ENABLES
	TRO	T1,PI.TNP	;TURN ON SELECTED CHANNELS
	MOVEM	T1,.CPDPI##	;FOR CONO
	HRRZ	T1,DEVCPU(F)	;ADDRESS OF THIS DDB'S INTERLOCK WORD
	SUBI	T1,INTL0##	;OFFSET INTO INTERLOCK TABLES
	SETOM	INTL0##(T1)	;RETURN THE DDB INTERLOCK
	SETOM	INTO0##(T1)	;AND INDICATE NO LONGER OWNED BY THIS CPU
	CONO	PI,@.CPDPI##	;RESTORE SELECTED PI CHANNELS
	JRST	TPOPJ##		;RESTORE TEMPORARY AND RETURN
;THIS ROUTINE IMPLEMENTS AN INTERLOCK FOR BIT DIDDLERS (SETOS,CLRBTS).
; IT IS SIMILAR TO LOKSPI AND LOKDPI.

LOKBTI::CONO	PI,PI.OFF	;NO PIS
	CONI	PI,.CPBTI##	;READ CURRENT STATE OF CHANNEL ENABLES
	CONO	PI,PI.ON+SYSOFF## ;TURN THE PI SYSTEM ON
	AOSE	INTLBT##	;INTERLOCK AVAILABLE?
	JRST	.-1		;NO, WAIT UNTIL IT IS
IFN FTKI10,<
	CONI	PAG,INTOBT##	;SERIAL NUMBER OF THE CPU WHICH OWNS THE INTERLOCK
>
IFN FTKL10,<
	APRID	INTOBT##	;SERIAL NUMBER OF OWNING CPU
>
	POPJ	P,		;RETURN WITH THE INTERLOCK

;COMMON EXIT ROUTINE TO RETURN BIT DIDDLER INTERLOCK AND POPJ

ONPOPB::			;FALL INTO UNLBTI
;	BTSON
;	POPJ	P,

;ROUTINE TO GIVE UP THE BIT DIDDLER INTERLOCK AND RESTORE THE PI CHANNELS

UNLBTI::EXCH	T1,.CPBTI##	;SAVE T1, GET STATE OF THE PI WHEN INTERLOCK WAS OBTAINED
	ANDI	T1,177		;ISOLATE CHANNEL ENABLES
	TRO	T1,PI.TNP	;TURN ON SELECTED CHANNELS
	EXCH	T1,.CPBTI##	;RESTORE T1, SET TO RESTORE PI SYSTEM
	SETOM	INTLBT##	;RELINQUISH INTERLOCK
	SETOM	INTOBT##	;NO LONGER THE OWNER OF THE INTERLOCK
	CONO	PI,@.CPBTI##	;RESTORE THE PI SYSTEM
	POPJ	P,		;AND RETURN
;ROUTINES TO INTERLOCK/RELEASE SCNSER'S DATA

LOKSCI::CONO	PI,SCNPIF##
	AOSE	SCNLOK##
	JRST	.-1
IFN FTKL10,<APRID INOSCN##>
IFN FTKI10,<CONI PAG,INOSCN##>
	POPJ	P,

UNLSCI::SETOM	SCNLOK##
	SETOM	INOSCN##
	CONO	PI,SCNPIN##
	POPJ	P,


;ROUTINES TO INTERLOCK/RELEASE NETSER'S DATA

LOKNPI::CONO	PI,NETPIF##
	AOSE	NETLOK##
	JRST	.-1
IFN FTKL10,<APRID INONET##>
IFN FTKI10,<CONI PAG,INONET##>
	POPJ	P,

UNLNPI::SETOM	NETLOK##
	SETOM	INONET##
	CONO	PI,NETPIN##
	POPJ	P,
SUBTTL	MULTIPROCESSING SCHEDULING

;THIS ROUTINE IS CALLED AT THE BEGINNING OF SCHED (NXTJOB)
;TO PUT SCAN TABLE ADDRESS INTO AC DAT.  THE MASTER PROCESSOR
;IS MADE TO SCAN PQ1,PQ2,PQ3 WHILE THE SLAVE PROCESSOR IS MADE
;TO SCAN PQ3,PQ2,PQ1.  IN ADDITION, THIS ROUTINE IS USED 
;TO DETERMINE WHETHER TO DO REGULAR SCHEDULING AT THIS TIME
;OR TO RUN EITHER (1) A JOB WHICH HAS STOPPED TIME SHARING
;OR (2) A JOB WHICH WAS RUNNING ON THE SLAVE PROCESSOR THAT
;MUST DO A UUO (PROBABLY FOR I/O)

MSCHED::
IFN FTTRPSET,<
	SKIPE	J,.CPSTS##	;DID ANYONE STOP TIME SHARING?
	JRST	MRNBL1		;YES, SEE IF HE IS RUNNABLE
>

MSCALL:	PUSHJ	P,SCHEDJ##	;DISPATCH TO SCHED CODE
MSEL:	MOVSI	T1,(SP.CJ0)	;PICK UP RUN BIT FOR MASTER PROCESSOR
	LSH	T1,@.CPCPN##	;POSITION FOR THIS PROCESSOR
	MOVE	F,.CPJOB##	;GET LAST JOB NUMBER
	ANDCAM	T1,JBTSPS##(F)	;TURN OFF RUNBIT FOR OLD JOB
	IORM	T1,JBTSPS##(J)	;TURN ON RUNBIT FOR NEW JOB
	JUMPE	J,CPOPJ##	;RETURN IF NULL JOB
	MOVEI	T1,JS.SCN	;SET SCANNED BIT
	IORM	T1,JBTST2##(J)	; FOR THE JOB
	POPJ	P,		;AND RETURN

;TEST IF JOB WHICH STOPPED T/S IS RUNNABLE
MRNBL1:	MOVE	T1,NRNMSK	;GET NON RUNNABLE MASK
	TDNN	T1,JBTSTS##(J)	;IS JOB WAITING TO SHUFFLE,SWAP,EXPAND,LOCK,OR REQUE
	PUSHJ	P,DXRUN		;SECOND STATUS WORD BITS OK?
	  MOVEI	J,0		;JOB UNRUNNABLE, GO RUN NULL JOB
	JRST	MSEL		;JOB RUNNABLE, GO TO IT


IFN FTLOCK,<
NRNMSK:	XWD	SWP+SHF+JXPN+JRQ+WTMASK,LOK
>
IFE FTLOCK,<
NRNMSK:	XWD	SWP+SHF+JXPN+JRQ+WTMASK,0
>
;ROUTINE TO DETERMINE IF JOB IS RUNNABLE WITH RESPECT TO JBTSPS BITS
;CALLED FROM SCHED WHEN SCHED IS ENTERED BY ANY PROCESSOR
;SKIP RETURN IF JOB IS RUNNABLE

DXRUN::	PUSH	P,T1		;SAVE WORKING REG
	PUSH	P,T2		;AND ANOTHER
	MOVEI	T1,SP.CP0	;BIT FOR CPU0 ( ** ALSO SP.CR0 ** )
	LSH	T1,@.CPCPN##	;MASK FOR THIS CPU'S BITS
	MOVE	T2,T1		;COPY POSITIVE MASK
	TRC	T1,SP.CPA	;MAKE "ANYWHERE BUT THIS CPU" MASK
	LDB	F,MPYNRN##	;GET JOBS NOT RUNNABLE BITS
	CAIN	F,(T1)		;RUNNABLE ONLY HERE (CONTEXT SWITCHED TO THIS CPU)
	JRST	DXRUN1		;REALLY GOT TO RUN THIS JOB
	SKIPL	.CPRUN##	;CAN CPU RUN ANY JOBS (SET RUN NO CPUN)
	TDNN	T2,JBTSPS##(J)	;YES, DOES USER WANT THIS CPU (SET CPU COMMAND/UUO)
	JRST	TTPOPJ##	;NO, CANNOT RUN THIS JOB
DXRUN1:	LSH	T1,<^L<SP.CR0>-^L<SP.CJ0>>	;POSITION MASK
	TRNN	F,(T2)		;MARKED UNRUNNABLE HERE (SWITCHED AWAY)
	TDNE	T1,JBTSPS##(J)	; OR CURRENTLY ON SOME OTHER CPU
	JRST	TTPOPJ##	;YES, CANNOT RUN THIS JOB
	SKPCPU	(1)		;CPU CHECK
	JRST	DXRUN2		;BOOT, OK TO RUN THIS JOB
	TRC	T2,SP.CPA	;FLIP RUNNABLE MASK
	MOVSI	T1,(SP.CC1)	;CONTROL C BIT
	CAIE	F,(T2)		;MUST RUN UUO TO COMPLETION
	TDNN	T1,JBTSPS##(J)	;NO, WAS ^C TYPED
	CAIA			;LOOK SOME MORE
	JRST	TTPOPJ##	;DON'T RUN JOB
	CAMN	J,SW0JOB##	;WANT TO SWAP THIS JOB?
	JRST	DXRUN3		;YES, DONT RUN IT THEN
	SKIPG	F,JBTSGN##(J)	;GET HIGH SEG NO., SPY OR NONE?
	JRST	DXRUN2		;NO HIGH SEG OR SPY HI SEG - GIVE CAN RUN RETURN
	MOVE	F,JBTSTS##(F)	;YES, HIGH SEG STATUS WORD (OR JOB 0'S)
	TLNN	F,SWP!SHF!JXPN	;IS HIGH SEG BEING SHUFFLED, SWAPPED, OR EXPANDED?
DXRUN2:	AOSA	-2(P)		;JOB IS RUNNABLE ON THIS CPU
DXRUN3:	MOVEM	J,.CPPLT##	;FLAG POTENTIALLY LOST IF CAN'T RUN HIM
				; BECAUSE OF MONITOR BIND
	JRST	TTPOPJ##	;GIVE CANNOT RUN THIS JOB RETURN
;SUBROUTINE TO CHECK IF A JOB IS RUNNABLE ON CPU0 AND IF
; CPU0 IS RUNNING
; EXITS CPOPJ1 IF SO, CPOPJ IF NOT RUNNABLE ON CPU0 OR CPU0 NOT RUNNING
;PRESERVES ALL ACS EXCEPT T3

CP0RC::	MOVEI	T3,SP.CR0	;CAN RUN ON CPU0 BIT
	TDNE	T3,JBTSPS##(J)	;CAN THE JOB RUN ON CPU0?
	SKIPGE	.C0RUN##	;AND IS CPU0 RUNNING?
	POPJ	P,		;CAN'T RUN ON CPU0 OR CPU0 NOT RUNNING
	JRST	CPOPJ1##	;RETURN

IFN FTKI10,<
;SUBROUTINE TO SEE MI PROG DIS MUST BE OFF ON CPU1 FOR
; THIS JOB TO USE ADDRESS BREAK
;ENTER WITH T4=IP.MID
;EXIT POPJ IF ADDRESS BREAK NOT AVAILABLE, CPOPJ1 IF OK
;PRESERVES T1,T2

CP1AC::	MOVEI	T3,SP.CR1	;CAN RUN ON CPU1 BIT
	TDNE	T3,JBTSPS##(J)	;CAN THE JOB RUN ON CPU1?
	SKIPGE	.C1RUN##	;AND IS CPU1 RUNNING?
	JRST	CPOPJ1##	;NO, ADDRESS BREAK IS OK
	TDNN	T4,.C1APR##	;IS MI PROG DIS SET?
	AOS	(P)		;NO, OK
	POPJ	P,		;RETURN
>;END IFN FTKI10

;SUBROUTINE TO SEE IF JOB # IN J IS ON ANY CPU
;CALL WITH:
;	MOVEI	J,JOB-TO-TEST
;	PUSHJ	P,ANYCPU
;	  RETURN HERE IF ON A CPU
;	RETURN HERE IF NOT ON A CPU

ANYCPU::MOVSI	T2,(SP.CJA)	;CURRENT JOB ANYWHERE MASK
	TDNN	T2,JBTSPS##(J)	;SEE IF JOB IN J IS ON THE OTHER CPU
	AOS	(P)		;NOT--SKIP RETURN
	POPJ	P,

IFN FTLOCK,<
;SUBROUTINE TO COPY A PORTION OF THE USER'S MAP INTO
; ALL CPU'S MAPS
;CALLING SEQUENCE:
;	MOVE	T1,EXEC VIRTUAL PAGE NUMBER
;	MOVE	T2,NUMBER OF PAGES
;	MOVE	T3,USER VIRTUAL ADDRESS
;	PUSHJ	P,MAPUEC
;	ALWAYS RETURN HERE

MAPUEC::PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,.C0CDB##	;START AT CPU0'S CDB
MAPUE1:	CAME	P1,.CPSLF##	;CURRENT CPU? IF SO, THE MAP IS ALREADY SET
	PUSHJ	P,MAPCP		;COPY USER'S MAP TO CPU FOUND'S MAP
	HLRZ	P1,.CPCDB##-.CPCDB##(P1) ;NEXT CPU'S CDB
	JUMPN	P1,MAPUE1	;DONE IF ALL CDB HAVE BEEN LOOKED AT
	POPJ	P,		;RETURN

MAPCP:	PUSHJ	P,SAVT##	;SAVE T ACCUMULATORS SO CAN CONTINUE
	ADD	T1,.CPTOS##-.CPCDB##(P1) ;ADDRESS OF THE CURRENT CPUS MAP
	PJRST	MAPUEI##	;GO COPY MAP TO MAP
;SUBROUTINE TO MAP A PAGE OF A JOB BEING LOCKED INTO
; ALL CPU'S MAPS
;CALLING SEQUENCE:
;	MOVE	P1,BYTE POINTER TO CPU'S MAP(P3)
;	MOVE	T3,CONTENTS OF UPMP MAP SLOT
;	PUSHJ	P,MAPUC
;	ALWAYS RETURN HERE
;PRESERVES T1

MAPUC::	PUSHJ	P,SAVE4##	;SAVE P1-P4
	MOVEI	P4,.C0CDB##	;START AT CPU0'S CDB
MAPUC1:	MOVE	P3,.CPTOS##-.CPCDB##(P4) ;ADDRESS OF CURRENT CPU'S MAP
	DPB	T3,P1		;STORE MAP CONTENTS IN THAT CPUS EPT
	HLRZ	P4,.CPCDB##-.CPCDB##(P4) ;NEXT CPU
	JUMPN	P4,MAPUC1	;LOOP OVER ALL CPUS
	POPJ	P,		;AND RETURN
>
IFN FTMOFFL,<
;SUBROUTINE TO INSURE THAT ALL BUT THE BOOT CPU ARE IN THE ACS BEFORE
; CHANGING THE EXEC MAP.
;CALL:	PUSHJ	P,CP1STP
;	  RETURN HERE IF SOME OTHER PROCESSOR STILL RUNNING
;	RETURN HERE IF ALL OTHERS IN THE ACS
;PRESERVES ALL ACS

CP1STP::PUSHJ	P,SAVE2##	;SAVE P1-P2
	MOVEI	P1,.C0CDB##	;START AT CPU0'S CDB
	MOVEI	P2,SR.ACL	;GET "IN AC LOOP" BIT
CP1ST1:	CAME	P1,.CPSLF##	;POINTING AT OURSELF?
	SKIPL	.CPOK##-.CPCDB##(P1) ;NO, THIS CPU STILL RUNNING?
	JRST	CP1ST2		;NO, IT PASSES
	TDNN	P2,.CPSBR##-.CPCDB##(P1) ;THIS CPU IN THE AC'S?
	POPJ	P,		;NO
CP1ST2:	HLRZ	P1,.CPCDB##-.CPCDB##(P1) ;GET ADDRESS OF NEXT CDB
	JUMPN	P1,CP1ST1	;LOOP IF MORE
	PJRST	CPOPJ1##	;GIVE SKIP RETURN
>;END IFN FTMOFFL
SUBTTL MULTIPROCESSING KL10 CACHE CONTROL

IFN FTKL10,<



;ROUTINE FOR SCHEDULER TO CHECK THE STATE OF THE CACHE FOR A JOB
; SKIP RETURN MEANS OK, NON-SKIP MEANS JOB IS NOT RUNNABLE ON THIS
; CPU BECAUSE OF CACHE. CALL IS:
;
;	MOVE	J,<JOB NUMBER>
;	PUSHJ	P,SCDCSH
;	  <JOB NOT RUNNABLE W.R.T. CACHE>
;	<JOB RUNNALBE W.R.T. CACHE>
; IF ERROR RETURN WAS GIVEN, .CPCLN IS INCREMENTED (# TIMES SCHEDULER
; COULD NOT RUN A JOB BECAUSE OF CACHE) AND .CPCLF IS SET TO THE JOB'S 
; NUMBER (POTENTIALLY LOST TIME BECAUSE OF CACHE)


SCDCSH::PUSHJ	P,CHKCSH	;CHECK STATE OF CACHE
	  JRST	CPOPJ1##	;OK, RETURN
	AOS	.CPCLN##	;NOT OK, SCHEDULER HAS TO IGNORE AGAIN
	MOVEM	J,.CPCLF##	;IN CASE NOTHING ELSE IS RUNNABLE
	POPJ	P,		;RETURN AND SAY RUN SOMEONE ELSE


;ROUTINE TO SET UP JBTST3 AND JBTCSN WITH CDB ADDRESS AND CURRENT
; SWEEP SERIAL NUMBER USING CDB IN P4.
;	CALL:
;	MOVE	U,<OLD JOB NUMBER>
;	MOVE	J,<NEW JOB NUMBER>
;	MOVE	P4,<CDB ADDRESS>
;	PUSHJ	P,SETCSN
;	<ONLY RETURN>
;
;CALLED BY CLOCK1 AFTER COMPLETION OF CONTEXT SWITCHING FROM OLD JOB.
; NOT CALLED FOR CONTEXT SWITCH FROM NULL JOB
; MUST BE CALLED INSIDE CPU INTERLOCK
; ANY CACHE SWEEP DONE ON A CPU AFTER THIS ROUTINE IS CALLED ON
; THAT CPU WILL CAUSE JOB THAT THIS ROUTINE WAS CALLED FOR TO BE
; RUNNABLE W.R.T. CACHE ON ANOTHER CPU

SETCSN::MOVE	T1,.CPSLF##	;THIS CPUS CDB
	HRRM	T1,JBTST3##(U)	;SETUP CDB ADDRESS
	CONSO	APR,LP.CSB+LP.CSD ;SWEEP IN PROGRESS
	TDZA	T1,T1		;NO, NEXT ONE WILL MAKE JOB RUNNABLE
	MOVEI	T1,1		;YES, REQUIRE ONE MORE TO BE RUNNABLE
	ADD	T1,.CPCSN##	;COMPUTE SWEEP SERIAL #
	MOVEM	T1,JBTCSN##(U)	;SAVE FOR LATER USE BY SCHED1
	POPJ	P,		;RETURN
;ROUTINE TO CHECK THE STATE OF CACHE FOR A JOB, CALLED BY SWAPPER
; JUST BEFORE IT IS TO SWAP OUT A JOB OR SEGMENT.
; SKIP RETURN MEANS OK, NON-SKIP MEANS JOB IS NOT SWAPPABLE W.R.T. CACHE.
; CALL IS:
;
;	MOVE	J,<SEGMENT NUMBER>
;	PUSHJ	P,SWPCSH
;	  <JOB CANNOT BE SWAPPED BECAUSE OF CACHE>
;	<OK TO SWAP JOB>
;
; IF ERROR RETURN IS GIVEN, .CPCSD IS INCREMENTED, WHICH IS THE
; NUMBER OF TIMES THE SWAPPER WAS DELAYED FROM SWAPPING A JOB OUT
; BECAUSE OF THE CACHE.

SWPCSH::CAILE	J,JOBMAX##	;IS THIS A LOWSEG?
	JRST	CPOPJ1##
	HRRZ	T3,JBTST3##(J)	;CDB ADDRESS OF CPU JOB LAST RAN ON
	JUMPE	T3,CPOPJ1##	;CORE 0 IS ALWAYS OK
	CAMN	T3,.CPSLF##	;LAST RUN HERE
	PUSHJ	P,MLSCSH	;YES, DO WE NEED TO WORRY ABOUT THE CACHE
	  PUSHJ	P,CHKRCH	;DO THE REAL WORK
	  JRST	CPOPJ1##	;EVERY THING IS BEAUTIFUL
				; (IN ITS OWN WAY)
	AOS	.CPCSD##	;INCREMENT SWAPPER CACHE DELAY COUNT
	POPJ	P,		;BECAUSE SWAPPER HAS BAD NEWS COMING
;ROUTINE TO CHECK THE STATE OF THE CACHE FOR A JOB
; CALLED ONLY BY SCDCSH, SWPCSH
; CALL:
;	MOVE	J,<JOB NUMBER>
;	PUSHJ	P,CHKCSH
;	  <SUCCESSFUL RETURN>		;JOB IS RUNNABLE, SWAPPABLE W.R.T. CACHE
;	<ERROR RETURN>		;CACHE MUST BE SWEPT ON ANOTHER CPU FOR THIS JOB
;
; SMASHES T3,T4


CHKCSH::HRRZ	T3,JBTST3##(J)	;GET CDB ADDR OF LAST CPU JOB RAN ON
	JUMPE	T3,CPOPJ##	;ZERO MEANS SOMEONE SPECIFICALLY GAVE
				; THE OK NOT TO SWEEP CACHE
	CAMN	T3,.CPSLF##	;WAS LAST CPU SAME AS THIS ONE?
	POPJ	P,		;YES, ALL IS OK
CHKRCH:	MOVE	T4,.CPCSN##-.CPCDB##(T3)  ;NO, MUST CHECK CURRENT SWEEP NUMBER ON OLD CPU
	CAMLE	T4,JBTCSN##(J)	; AGAINST SWEEP NUMBER ON OLD CPU WHEN JOB LEFT IT
	POPJ	P,		;CURRENT SERIAL NUMBER IS LARGER, OLD CPU
				; HAS SWEPT SO JOB IS RUNNABLE, SWAPPABLE

;JOB CANNOT BE RUN OR SWAPPED, SO PUT IN A REQUEST TO THE OLD CPU
; TO SWEEP ITS CACHE.

	MOVEM	T4,.CPCSR##-.CPCDB##(T3)  ;.CPCSN CAN NEVER BE LESS THAN JBTCSN, SO
				; NOW WE KNOW THAT .CPCSN AND JBTCSN ARE EQUAL.
				; OTHER CPU WILL SWEEP IF REQUEST IS EQUAL
				; OR GREATER IN VALUE THAN CURRENT SWEEP NUMBER
				; ON THAT CPU
	JRST	CPOPJ1##	;INDICATE THAT JOB CANNOT BE DEALT
				; WITH NOW.


;ROUTINE TO CLEAR JOB'S CACHE SWEEP SERIAL NUMBER DATA, INDICATING
; THAT NO SWEEP NEED BE DONE BEFORE THE JOB SWITCHES CPUS
; CALLED FROM KLSER, COMCON WHEN JOB THROWS AWAY OLD CORE IMAGE.
;	CALL:
;	MOVE	J,<JOB NUMBER>
;	PUSHJ	P,CLCSN
;	<ONLY RETURN>
;
; RESPECTS ALL ACS

CLCSN::	CAIG	J,JOBMAX##	;CHECK FOR HIGH SEGMENT NUMBER
	HLLZS	JBTST3##(J)	;JUST CLEAR OUT CDB ADDRESS, MAKING
				; JBTCSN ENTRY INVALID
	POPJ	P,		;RETURN
;ROUTINE TO SERVICE CACHE SWEEP REQUEST. CALLED BY
; CLOCK1 BEFORE SCHEDULER IS CALLED SO THAT
; SWEEP REQUESTS CAN BE SERVICED BY ANOTHER CPU BEFORE IT
; ENTERS THE SCHEDULER.
; IF A SWEEP IS DONE, .CPCRN IS INCREMENTED

CSREQS::MOVE	T1,.CPCSR##	;GET REQUEST NUMBER
	CAMGE	T1,.CPCSN##	;IS IT LESS THAN CURRENT SWEEP NUMBER?
	POPJ	P,		;NO REQUESTED CACHE SWEEP THIS TICK
	AOS	.CPCRN##	;NO, EQUAL(HAPPENS) OR GREATER (NEVER HAPPENS)
				; INCREMENT COUNT OF SWEEPS DONE BY REQUEST
				;FALL INTO "CTXSWP"
	CLRPGT	(0)		;CLEAR HARDWARE PAGE TABLE AS WELL SINCE EXEC MAP
				; MIGHT HAVE CHANGED AS WELL


;ROUTINE TO ANTICIPATE THE NEED FOR A CACHE SWEEP FOR ANOTHER CPU
; CALLED IN CLOCK1 AFTER OLD JOB'S CONTEXT IS SAVED (IF APPLICABLE)
; AND AFTER SETCSN IS CALLED SO THAT IF A CACHE SWEEP IS DONE IT
; WILL MAKE THE JOB IN QUESTION RUNNABLE ON ANOTHER CPU.
; NOTE THAT IT IS CALLED INSIDE INTERLOCK SO THAT MASTER CPU
; WILL NEVER HAVE TO REQUEST A CACHE SWEEP FOR A JOB THAT DID A
; MASTER UUO ON SLAVE BY GETTING TO SCHEDULER BEFORE CSHUUO IS
; CALLED BY SLAVE.
; A SWEEP IS DONE IF LAST JOB RUN WAS ON
; SLAVE CPU AND DID A UUO WHICH MUST BE EXECUTED ON MASTER. HOPEFULLY,
; THIS SAVES CPU0 FROM WAITING FOR CPU1 TO RUN NULL JOB (COULD BE A WHILE)
; OR SERVICING SWEEP REQUEST (COULD TAKE SEVERAL TICKS, SINCE ITS DONE AT
; PI 7

CTXSWP::PUSHJ	P,MLSCSH
	  SWPUA
	POPJ	P,


;SUBROUTINE TO SEE IF MONITORS LOW SEGMENT IS CACHED MEANING WE ARE DOWN
;	TO ONE PROCESSOR.
;RETURNS:	CPOPJ IF NOT CACHED
;		CPOPJ1 IF CACHED

MLSCSH::PUSH	P,T1		;SAVE T1
	MOVSI	T1,(ST%LSC)	;LOW SEGMENT CACHED BIT
	TDNE	T1,CNFST2##	;IS IT
	AOS	-1(P)		;YES, GIVE SKIP RETURN
	JRST	TPOPJ##		;RESTORE T1 AND RETURN

;SUBROUTINE TO CALL SETCSN WHEN THE JOB NUMBER
; IS IN J AND THE CONTENTS OF U IS UNKNOWN

SETCSJ::PUSH	P,U		;SAVE U
	MOVE	U,J		;ARGUMENT FOR SETCSN
	PUSHJ	P,SETCSN	;MAKE JOB UNRUNNABLE ON THE OTHER
				; CPU UNTIL A SWEEP IS DONE
	PJRST	UPOPJ##		;RESTORE U AND RETURN
;SUBROUTINE TO CAUSE HIGH SEGMENT PART OF ALL MAPS TO GET CHANGED WHEN UWP CHANGES
;CALLING SEQUENCE:
;	MOVEI	T1,PREVIOUS STATE OF UWP (0 IF OFF, 1 IF ON)
;	PUSHJ	P,CLRCSH
;ALWAYS RETURN HERE (CACHE BITS CLEARED OR SET AS APPROPRIATE)

CLRCSH::MOVE	J,.CPJOB##	;CURRENT JOB
	SKIPLE	J,JBTSGN##(J)	;JOB HAVE A HIGH SEGMENT?
	TLNN	J,SHRSEG	; AND IS IT SHARABLE?
	POPJ	P,		;NO, NOTHING TO DO
	TLNN	J,UWPOFF	;IS THE HIGH SEGMENT WRITE PROTECTED?
	JRST	CLRCS2		;YES, SEE IF ANY OTHER JOB HAS IT WRITE ENABLED
	JUMPE	T1,CPOPJ##	;NOTHING TO DO IF NOTHING CHANGED
	MOVEI	T1,SETNCH	;SUBROUTINE TO SET NO CACHE BITS
	PUSHJ	P,HGHAPP##	;TURN ON NOCSH!REDOMP FOR ALL JOBS SHARING THIS SEGMENT
	PUSHJ	P,SAVE2##	;SAVE P1,P2 FOR ANYRN
CLRCS1:	MOVEI	P1,.C0CDB##	;FIRST CDB
CLRCS3:	MOVE	J,.CPJOB##	;CURRENT JOB
	HRRZ	J,JBTSGN##(J)	;JOB'S HIGH SEGMENT NUMBER
	PUSHJ	P,ANYRN		;SEE IF ANY OTHER RUNNING JOB IS USING THAT SEGMENT
	  CAIA			;YES, MUST WAIT
	POPJ	P,		;NO, EVERYTHING IS OK
	MOVE	T2,.CPJOB##-.CPCDB##(P1) ;JOB NUMBER USING IT
	MOVSI	T1,REDOMP	;HAS THE MAP BEEN REDONE FOR THE CPU1 JOB?
	TDNN	T1,JBTSGN##(T2)	; ...
	JRST	[HLRZ P1,.CPCDB##-.CPCDB##(P1) ;NEXT CDB
		 JUMPE P1,CPOPJ## ;ALL IS WELL
		 JRST CLRCS3]	;CHECK NEXT CPU AS WELL
	MOVEI	T1,1		;NO, SLEEP A TIC
	MOVE	J,.CPJOB##	;OUR JOB NUMBER
	PUSHJ	P,SLEEP##	;WAIT A WHILE
	JRST	CLRCS1		;AND TRY AGAIN
CLRCS2:	JUMPN	T1,CPOPJ##	;RETURN IF NOT CHANGING THE STATE OF UWP
	MOVEI	T1,CHKUWP	;SUBROUTINE TO CHECK IF ANY OTHER JOB HAS UWPOFF
	SETZ	T2,		;INITIALIZE FLAG SAYING NO JOBS FOUND
	PUSHJ	P,HGHAPP##	;ANY OTHER JOB HAVE UWP OFF FOR THIS SEGMENT?
	JUMPL	T2,CPOPJ##	;EXIT IF SOME JOB HAS UWP OFF
	MOVEI	T1,CLRNCH	;CLEAR NOCSH FOR ALL JOBS SHARING THIS SEGMENT
	PJRST	HGHAPP##	;CAUSE MAPS TO BE REDONE WITH CACHE ON

SETNCH:	MOVSI	T1,NOCSH!REDOMP	;DON'T CACHE THE HIGH SEG, CAUSE MAP TO BE REDONE
	IORM	T1,JBTSGN##(J)
	POPJ	P,

CHKUWP:	MOVSI	T1,UWPOFF	;IS USER WRITE PROTECT OFF?
	TDNE	T1,JBTSGN##(J)
	SETO	T2,		;YES, FLAG IT
	POPJ	P,

CLRNCH:	MOVSI	T1,NOCSH	;DON'T CACHE IT
	ANDCAM	T1,JBTSGN##(J)	;CLEAR NO CACHE BIT
	MOVSI	T1,REDOMP	;REDO THE MAP BIT
	IORM	T1,JBTSGN##(J)	;CAUSE MAP TO BE REDONE WITH CACHE BITS ON
	POPJ	P,
;SUBROUTINE TO CHECK TO SEE IF A JOB IS ATTACHING TO A SHARABLE
; WRITABLE HIGH SEGMENT

CHKSWS::MOVE	J,.CPJOB##	;CURRENT JOB
	SKIPLE	J,JBTSGN##(J)	;JOB HAVE A REAL HIGH SEGMENT?
	TLNN	J,SHRSEG	; AND IS IT SHARABLE?
	POPJ	P,		;NO, NOTHING TO DO
	MOVEI	T1,CHKUWP	;ROUTINE TO LOOK FOR UWPOFF
	SETZ	T2,		;ASSUME IT ISN'T
	PUSHJ	P,HGHAPP##	;SET IF ANY JOB HAS UWPOFF FOR THIS SEGMENT
	MOVE	J,.CPJOB##	;JOB NUMBER
	MOVSI	T1,NOCSH!REDOMP	;FORCE MAP TO BE REDONE UNCACHED
	SKIPE	T2		;SKIP IF NOT WRITABLE
	IORM	T1,JBTSGN##(J)	;UNCACHE HIGH SEGMENT IN THIS JOB'S MAP
	POPJ	P,		;AND RETURN
;SUBROUTINE TO UNCACHE A PAGE OF THE EXEC ADDRESS SPACE ON ALL CPU'S
; IN THE SYSTEM
;CALLING SEQUENCE:
;	MOVE	T2,BYTE POINTER TO EXEC MAP SLOT RELOCATED BY .CPTOS
;	LDB	T3,T2
;	TRZE	T3,PM.CSH	;VERIFIY PAGE IS CACHED
;	PUSHJ	P,UNCACH	;ITS CACHED, UNCACHE IT
;	ALWAYS RETURNS HERE, T3 INTACT
;

UNCACH::PUSHJ	P,SAVE4##	;SAVE WORKING ACS
	DMOVE	P2,T2		;SAVE ARGUMENTS
	MOVSI	P1,(ST%LSC)	;LOW SEGMENT IS CACHED BIT
	TDNE	P1,CNFST2##	;IS THE LOW SEGMENT CACHED? (ONLY ONE CPU RUNNING)
	JRST	[ANDI	P3,17777	;ISOLATE PAGE NUMBER
		 MOVSI	P1,TNCSHB	;TEMPORARY UNCACHED BIT
		 IORM	P1,PAGTAB(P3)	;LITE IN CASE CPU GETS "ADDED"
		 POPJ	P,]	;RETURN, LEAVING CACHE ON FOR NOW
	PUSH	P,T1		;SAVE T1
	PUSHJ	P,FIXOTB	;FIXUP OUCHE TABLE
	SUB	P2,.CPTOS##	;UNRELOCATE, RELOCATED DIFFERENTLY BELOW
	TLO	P2,P4		;BYTE POINTER INDEXED BY P4
	MOVEI	T1,UNCACC	;ROUTINE TO REALLY DO THE WORK
	PUSHJ	P,CPUAPP##	;UNCACHE THE PAGE IN EVERY CPU'S MAP
	MOVE	T3,P3		;RESTORE MAP CONTENTS
	JRST	TPOPJ##		;AND RETURN

;SUBROUTINE TO UNCACHE A PAGE IN EVERY CPU'S MAP

UNCACC:	MOVE	P4,.CPTOS##-.CPCDB##(P1) ;ADDRESS OF THIS CPU'S EXEC MAP
	DPB	P3,P2		;STORE MAPPING IN THIS CPU'S MAP (PM.CSH OFF)
	MOVE	P4,.CPCSN##-.CPCDB##(P1) ;CURRENT CACHE SWEEP SERIAL NUMBER ON THIS CPU
	ADDI	P4,2		;ONE MORE SWEEP AND IN CASE ONE COULD BE IN PROGRESS
	MOVEM	P4,.CPCSR##-.CPCDB##(P1) ;REQUEST THAT CPU TO SWEEP
UNCAC1:	SKIPGE	.CPOK##-.CPCDB##(P1) ;DON'T WORRY ABOUT CPUS THAT AREN'T RUNNING
	CAMGE	P4,.CPCSN##-.CPCDB##(P1) ;HAS HE SWEPT YET?
	POPJ	P,		;ITS MAP WILL BE FIXED WHEN IT GETS RESTARTED
	MOVEI	T1,0		;0 SLEEP TIME (1 TIC)
	PUSHJ	P,SLEEPF##	;WAIT FOR HIM TO SWEEP
	JRST	UNCAC1		;NO, WAIT SOME MORE

;SUBROUTINE TO FIXUP OUCHTB WHEN TURNING OFF THE CACHE FOR SOME PAGE
; SO THAT OUCHE ALWAYS REFERENCES THROUGH FOUR CACHED PAGES

FIXOTB:	PUSHJ	P,SAVE4##	;ONLY REASON THIS IS A SUBROUTINE
IFE MONORG&1000,<
	MOVE	P1,[POINT 18,.EPPM##-.EPMP##+<MONORG-400000>/<2*1000>]
>
IFN MONORG&1000,<
	MOVE	P1,[POINT 18,.EPPM##-.EPMP##+<MONORG-400000>/<2*1000>,17]
>
	ADD	P1,.CPTOS##	;POINT AT THIS CPU'S MAP
	MOVEI	P2,MONORG-PAGSIZ## ;STARTING VIRTUAL ADDRESS
	ANDI	P3,17777	;PAGE CACHE IS BEING TURNED OFF FOR
	MOVEI	P4,4		;NEED 4 CACHED PAGES
FIXOT1:	ADDI	P2,PAGSIZ##	;NEXT VIRTUAL ADDRESS
	CAML	P2,MONVFF##	;RUN OUT OF MONITOR PAGES?
	STOPCD	CPOPJ##,JOB,N4C,;++NOT 4 CACHED PAGES
	ILDB	T1,P1		;NEXT PAGE
	TRNN	T1,PM.CSH	;IS IT CACHED?
	JRST	FIXOT1		;NO, CAN'T USE IT
	ANDI	T1,17777	;JUST THE PHYSICAL PAGE NUMBER
	MOVE	T2,PAGTAB(T1)	;PAGTAB ENTRY
	TLNN	T2,TNCSHB	;DON'T USE THIS PAGE IF IT MIGHT GET UNCACHED
	CAIN	T1,(P3)		;IS IT THE PAGE THATS BEING UNCACHED?
	JRST	FIXOT1		;YES, CAN'T USE THAT PAGE EITHER
	HRRM	P2,OUCHTB##-1(P4) ;OUCHE CAN USE THIS VIRTUAL ADDRESS
	SOJG	P4,FIXOT1	;FIND A TOTAL OF 4 CACHED PAGES
	POPJ	P,		;ALL DONE, RETURN
;ROUTINE TO HANDLE SETUUO #40 (.STCSB)
; SET/CLEAR CACHE BITS FOR TEMPORARILY UNCACHED PAGED

SETCSB::PUSHJ	P,SAVE2##	;SAVE P1,P2
	MOVE	P1,NCPRUN##	;NAME OF RUNNABLE CPU'S
	SOJG	P1,CPOPJ##	;FAIL IF MORE THAN 1
	HRRZ	P1,.CPTOS##	;ADDRESS OF THE EPT
	MOVE	T4,[POINT 18,.EPLM##-.EPMP##(P1)]
	MOVE	P2,SYSSIZ##	;LAST ADDRESS
	LSH	P2,W2PLSH##	;P2=# OF PAGES TO DO
	PUSHJ	P,SETCS1	;SET/CLEAR CACHE FOR THE LOW SEGMENT
IFE .EPCDB&1000,<
	MOVE	T4,[POINT 18,.EPMP##-.EPMP##+<.EPCDB/<2*1000>>(P1)]
>
IFN .EPCDB&1000,<
	MOVE	T4,[POINT 18,.EPMP##-.EPMP##+<.EPCDB/<2*1000>>(P1),17]
>
	MOVE	P2,MONVFF##	;LAST LOCATION OF DATA BEYOND HIGH SEGMENT
	SUBI	P2,.EPCDB	;NUMBER OF WORDS ALLOCATED
	LSH	P2,W2PLSH##	;CONVERT TO PAGES OF MONITOR HIGHSEG
	PUSHJ	P,SETCS1	;CLEAR/SET CACHE BITS FOR LDB'S, ETC...
	CLRPGT	(0)		;LET HARDWARE SEE CHANGES
	MOVSI	T1,(ST%LSC)	;LOW SEGMENT IS CACHED BIT
	TRNN	T2,1		;SETTING CACHE ON OR OFF
	SKIPA	T2,[ANDCAM T1,CNFST2##]	;OFF, SET TO CLEAR BIT
	MOVE	T2,[IORM T1,CNFST2##]	;ON, SET TO LIGHT BIT
	PUSHJ	P,CSDMP##	;ALWAYS SWEEP THE CACHE
	XCT	T2		;SET/CLEAR BIT IN STATES
	PUSHJ	P,FIXOTB	;MAKE SURE "OUCHE" WORKS
	JRST	CPOPJ1##	;GIVE GOOD RETURN TO UUO

SETCS1:	ILDB	T3,T4		;GET MAP ENTRY
	JUMPE	T3,SETCS2	;IGNORE IF NOT MAPPED
	HRRZ	T1,T3		;GET PHYSICAL PAGE #
	ANDI	T1,17777	;ISOLATE PAGE NUMBER
	MOVE	T1,PAGTAB(T1)	;GET BITS FOR PAGE
	TLNN	T1,TNCSHB	;PAGE TEMPORARILY UNCACHED
	JRST	SETCS2		;NO, LEAVE IT ALONE
	TRNN	T2,1		;WANT CACHE ON OR OFF
	TRZA	T3,PM.CSH	;OFF, CLEAR BIT
	TRO	T3,PM.CSH	;ON, SET IT
	DPB	T3,T4		;PUT ENTRY BACK IN MAP
SETCS2:	SOJG	P2,SETCS1	;TRY NEXT PAGE
	POPJ	P,		;RETURN


>;END IFN FTKL10
SUBTTL	MULTIPROCESSING STOPCD PROCESSING

;ROUTINE TO WAIT FOR ANOTHER CPU TO FINISH PROCESSING A STOPCD.  CALLED
;FROM APR INTERRUPT LEVEL ON EACH PROCESSOR IF DIELOK GOES POSITIVE.
;WAITS FOR ONE OF THE FOLLOWING EVENTS:
;	1. DIELOK GOES NEGATIVE
;	2. CRSHWD GOES NON-ZERO (GO DIRECTLY TO REBOOT IN THIS CASE)
;	3. DIELOK STAYS POSITIVE TOO LONG (CPU DIED IN DIE)
;CALL:	PUSHJ	P,CHKDIE
;	<RETURN HERE ON EVENTS 1 AND 3>

CHKDIE::CONO	PI,PI.OFF	;TURN OFF THE PI SYSTEM
	JSR	.CPSVA##	;SAVE ALL AC SETS
	PUSHJ	P,SAVT##	;SAVE T1-T4
	PUSHJ	P,RCDSTB##	;READ CPU AND DEVICE STATUS BLOCKS
	HRROS	.CPSBR##	;INTERLOCK CALL AT REBOOT
IFN FTKL10,<
	PUSHJ	P,CSDMP##	;DUMP CACHE
	PUSHJ	P,CHKDI1	;CALL ROUTINE TO DO THE WORK
	JRST	CHKDI5		;AND EXIT
CHKDI1:	PUSHJ	P,SVPPC##	;SAVE PROTOCOL, ENTER SECONDARY
>
CHKDI2:	MOVE	T1,[^D25000000]	;WAIT THIS LONG FOR INTERLOCK TO CLEAR
				;(ABOUT 45 SECONDS ON A MODEL B KL)
CHKDI3:	SKIPE	CRSHWD##	;RELOADING SYSTEM BECAUSE OF STOPCD?
	PJRST	REBOOT##	;YES
	SKIPGE	DIELOK##	;CPU FINISHED WITH STOPCD YET?
	JRST	CHKDI4		;YES
	SOJG	T1,CHKDI3	;NO, COUNT DOWN AND LOOP
	SETOM	INODIE##	;TOO LONG, BREAK INTERLOCK
	SETOM	DIELOK##	;...
	JRST	CHKDI2		;INSURE THAT ANOTHER WASN'T WAITING
CHKDI4:	HRRZS	.CPSBR##	;LET STATUS BLOCKS BE READ AGAIN
IFN FTKL10,<
	POPJ	P,
CHKDI5:
>
	CONO	PI,PI.ON	;TURN ON PI SYSTEM AGAIN
	POPJ	P,		;AND RETURN
;ROUTINE TO SEE IF WE ARE THE LAST CPU RUNNING IN A MULTIPROCESSOR
;SYSTEM.
;CALL:	PUSHJ	P,LSTCPU
;	<RETURN HERE IF OTHERS RUNNING>
;	<RETURN HERE IF WE ARE THE ONLY ONE>
;PRESERVES ALL BUT T1

LSTCPU::MOVEI	T1,.C0CDB##	;START AT CPU0 CDB
LSTCP1:	CAMN	T1,.CPSLF##	;IS THIS ME?
	JRST	LSTCP2		;YES, DOESN'T COUNT
	SKIPGE	.CPOK##-.CPCDB##(T1) ;THIS CPU STILL RUNNING?
	POPJ	P,		;YES, AT LEAST ONE STILL RUNNING
LSTCP2:	HLRZ	T1,.CPCDB##-.CPCDB##(T1) ;GET ADDRESS OF NEXT CDB
	JUMPN	T1,LSTCP1	;LOOP IF MORE
	JRST	CPOPJ1##	;WE ARE THE ONLY ONE LEFT
	$LIT
SUBTTL	INITIALIZATION AND CRASH PROCEDURES FOR CPUN
;NON-BOOT PROCESSOR INITIALIZATION.  JRST HERE FROM 400 STARTUP.


	$LOW			;THIS CODE MUST BE IN THE LOW SEGMENT SINCE
				; THE HIGH SEGMENT ISN'T ADDRESSABLE UNTIL
				;CPU1'S MAP IS SETUP FROM CPU0'S MAP
SPRINI::CONO	PI,CLRPIS##	;CLEAR PI AND PARITY ERROR FLAG
	CONO	APR,AP1RST##	;RESET EVERYTHING AND ENABLE CLOCK
IFN FTKL10,<
	EXECAC			;MAKE SURE AC BLOCKS ARE OK
>
	MOVSI	P,300000	;"CPU IS DOWN" OR "DETACHED" BITS
	TDNE	P,.CPRUN##	;CAN THIS CPU RUN YET
	JRST	.-1		;NO, WAIT UNTIL IT CAN
	MOVE	P,.CPNPD##	;SET UP PUSH DOWN POINTER
IFN FTKL10,<
	MOVE	T1,.CPCPN##
	PUSHJ	P,DTEINI##
	PUSHJ	P,ENTSPC##	;ENTER SECONDARY PROTOCOL
>;END IFN FTKL10
	PUSHJ	P,PRTCPU	;TELL OPR WHO WE ARE
	MOVSI	T1,40000	;SUPPENDED BIT
	TDNE	T1,.CPRUN##	;RESTART AFTER REBOOT RATHER THAT START OR RESTART?
	JRST	SPRIN1		;YES, DON'T WAIT FOR A BOOT CPU TO COME TO LIFE
	MOVE	14,[XWD ACLOOP,2] ;LOAD AC'S WITH WAIT LOOP
	BLT	14,14		;CPU1 WILL WAIT UNTIL CPU0 IS GOING
	MOVE	14,[JRST SPRIN1] ;SET UP RETURN INSTRUCTION
	JRST	2		;ENTER LOOP

;INITIALIZE PD LIST
SPRIN1:	MOVE	P,.CPNPD##	;SET UP PUSH DOWN POINTER
	MOVE	T1,.CPTOS##	;SAVE FIRST SLOT IN CPU1'S MAP TEMPORARRLY
	PUSH	P,.E0PM##-.E0MP##(T1) ;MAPINI CLOBBERS THIS
	PUSHJ	P,MAPINI	;COPY THE BOOT CPU'S MAP INTO THE CURRENT CPU'S MAP
IFN FTKL10,<
	MOVEI	T1,LG.CSL!LG.CSW;CACHE STRATEGY LOOK, CACHE STRATEGY LOAD
	IORM	T1,.CPEBR##	;MAKE SURE CURRENT CPU'S CACHE IS ON
>
	JRSTF	@[EXP IC.UOU+.+1] ;TURN ON USER IOT SO EXECUTE PAGED WORKS
				; CORRECTLY AND PROCEED WITH INITIALIZATION
	CLRPGT	(1)
	CONO	PI,10000	;AND CLEAR PI SYSTEM
	MOVE	T1,.CPTOS##	;RESTORE CONTENTS OF CPU1 EXEC MAP SLOT 0
	POP	P,.E0PM##-.E0MP##(T1) ;FALL INTO SPRIN2
	MOVSI	T1,40000	;SUPPENDED BIT
	TDNE	T1,.CPRUN##	;RESTART AFTER REBOOT?
	JRST	SPRIN5		;YES, DISKS ARE INITIALIZED
SPRIN2:	MOVE	17,[ONCLOP,,1] ;WAIT IN ACS FOR CPU0 TO REQUEST IO
	BLT	17,16		; DO IT (AT SPRIN3) WHEN REQUESTED
	SJSP	0,3
SPRIN3:	MOVE	P,.CPNPD##	;SET UP A PDL
	SKIPN	T1,ONCCOM##	;WHAT CPU0 WANTS US TO DO
	JRST	SPRIN2		;NOTHING TO DO
	AOJE	T1,SPRIN2	;RESTARTED IF -1
	SOS	F,T1		;COMM WORD INTO F
	SOJE	T1,SPRIN5	;DONE IF +1
	TLNE	F,-1		;IF -1,,UDB
	JRST	SPRIN4		;DO CPY ROUTINE
	PUSHJ	P,SPRIO##	;0,,DDB - DO IO
	JRST	SPRIN2		;AND WAIT SOME MORE
SPRIN4:	HRRZ	U,F		;SET U=UDB
	PUSHJ	P,SPRCPY##	;CALL KONCPY
	JRST	SPRIN2		;AND WAIT SOME MORE
;HERE TO SET UP TO RUN NULL JOB, & SETUP PI
SPRIN5:	PUSHJ	P,MAPINI
	MOVEI	T1,CCTYO##	;GET ADDRESS OF TYPEOUT ROUTINE
	MOVEM	T1,.CPTOA##	; AND TELL SCNSER
	MOVEI	T1,COMTIV##	;GET ADDRESS OF COMMAND INPUT ROUTINES
	MOVEM	T1,.CPTIV##	; AND TELL SCNSER TO USE LDB'S NOW.
	SETZM	.CPTNT##	;JUST STARTING
	MOVE	J,.CPCPN##	; FOR THIS CPU
	SETZM	CLKMIN##(J)	;CLEAR MIN REQUEST
	SETOM	.CPSCD##	;CLEAR SCHED NESTING
	SETOM	.CPDWD##	;GIVE UP DIE RECURSION INTERLOCK
	SETZM	.CPNBI##	;CLEAR NUMBER OF BROKEN INTERLOCKS
	SETZM	.CPAEF##	;CLEAR CPU ERROR/SWEEP FLAGS
	HRRZS	.CPMPC##	;CLEAR SERIOUS MEMORY PARITY HALT FLAG
				; HALT AFTER PRINTING ON CPU0 CTY
	MOVE	T1,TIME##	;GET CURRENT TIME
	MOVEM	T1,.CPTML##	;STORE FOR TICKS GONE BY
	MOVE	T2,TICSEC##	;TICK PER SECOND
	LSH	T2,-1		;DIVIDE BY 2
	ADD	T1,T2		;AND ROUND
	IDIV	T1,TICMIN##	;T2 = PARTIAL MINUTE (IN TICKS)
	IDIV	T2,TICSEC##	;T2 = SECONDS, T3 = REMAINDER IN TICKS
	SUBI	T2,^D60		;COMPUTE TIME TO NEXT MINUTE
	MOVNM	T2,.CPSEC##	;STORE FOR ONCE-A-MINUTE UPDATE
	SUB	T3,TICSEC##	;TIME TO NEXT SECOND
	MOVNM	T3,.CPHTM##	;STORE FOR ONCE-A-SECOND UPDATE
	MOVEI	T1,SR.STS!SR.DIE!SR.ACL ;CLEAR THESE BITS WHEN WE
	ANDCAM	T1,.CPSBR##	; RESTART THE PROCESSOR
	MOVEI	J,JOBMAX##	;MAX JOB NO.
	MOVSI	T1,(SP.CJ0)	;JOB RUNNING ON CPU1 BIT
	LSH	T1,@.CPCPN##
SPRLP1:	ANDCAM	T1,JBTSPS##(J)	;CLEAR FOR ALL JOB IN SYSTEM
				; (IN CASE THIS IS A 450 RESTART ON CPU1)
	SOJGE	J,SPRLP1	;MORE?
IFN FTKL10,<
	APRID	T3		;PROCESSOR HARDWARE OPTIONS
	DMOVE	T1,[M.EBPS##	;ASSUME A MODEL A PROCESSOR
		M.MBPS##]
	TRNE	T3,ID.XKL	;IS IT A MODEL B PROCESSOR?
	DMOVE	T1,[^D30*M.EBPS##/^D25 ;YES, USE DIFFERENT EBOX/MBOX CALABRATION CONSTANTS
		^D30*M.MBPS##/^D25]
	MOVEM	T1,.CPEBS##	;STORE AS EBOX TICS/SEC
	MOVEM	T2,.CPMBS##	;STORE AS MBOX TICS/SEC
	IDIV	T1,TICSEC##	;AND NOW GET EBOX COUNTS/JIFFY
	IMULI	T1,.EBCPT	;MULTIPLY BY EBOX COUNTS/TICK
	MOVEM	T1,.CPEBJ##	;STORE IN CDB VARIABLE
	MOVE	T1,.CPMBS##	;GET MBOX TICKS/SECOND
	IDIV	T1,TICSEC##	;GET MBOX COUNTS/JIFFY
	IMULI	T1,.MBCPT	;MULTIPLY BY MBOX COUNTS/TICK
	MOVEM	T1,.CPMBJ##	;SAVE
IFN FTEMRT,<
	PUSHJ	P,ACCMON##	;START UP ACCOUNTING METERS
	PUSHJ	P,CLREMB##	;CLEAR OUT E AND MBOX ACCOUNTING METERS
>;END IFN FTEMRT
>;END IFN FTKL10
	PUSHJ	P,SETIME##	;TO INITILIZE 'GGTIME' CLOCK (IN SYSINI)
IFN FTKL10,<
	CONI	MTR,T1		;PRESERVE TIME BASE INFO
	CONO	MTR,AP1CHN##(T1);SETUP INTERVAL TIMER PI
	MOVSI	T1,40000	;SUPPENDED BIT
	TDNE	T1,.CPRUN##	;THERE MIGHT NOT BE A BOOT CPU AFTER A REBOOT
	JRST	SPRI5A		; SO DON'T WAIT ON IT
	MOVE	T1,BOOTCP##	;BOOT CPU
	LSH	T1,.CPSOF##	;OFFSET TO ITS CDB
	SKIPE	T2,.C0TMF##(T1)	;WAIT UNTIL CLOCK FLAG CLEARS, ZERO T2
	JRST	.-1
	SKIPN	.C0TMF##(T1)	;TIME FROM SET UNTIL SET
	JRST	.-1
	SKIPE	.C0TMF##(T1)
	AOJA	T2,.-1
	SKIPN	.C0TMF##(T1)
	AOJA	T2,.-1
	MOVE	T3,.CPCPN##	;OUR CPU NUMBER
	SUB	T3,BOOTCP##	; MINUS THE BOOT CPU NUMBER
	SKIPG	T3		;IF NEGATIVE, (NEVER EQUALS ZERO)
	ADDI	T3,CPUN##	; MODULO THE NUMBER OF CPU'S
	IMULI	T2,(T3)
	IDIVI	T2,CPUN##	;FRACTION OF A TIC TO WAIT
	SOJG	T2,.		; BEFORE STARTING THE CLOCK
SPRI5A:	MOVEI	T1,^D1666	;ASSUME 60HZ
	MOVE	T2,STATES##
	TLNE	T2,(ST.CYC)	;IS OUR ASSUMPTION CORRECT?
	MOVEI	T1,^D2000	;NO, 2000 IS INTERVAL
	CONO	TIM,TO.CTD!TO.CIT!TO.SIT(T1)	;START TIMER GOING
>
IFN FTKI10,<	CTY==120	;DEVICE CODE FOR CTY
	CONO	CTY,1200+SCNCHN##
>
	CONO	APR,AP1NUL##
	CONO	PI,52377	;TURN ON ALL PI, ENABLE MEMORY PARITY
IFN FTKL10,<
PPCPT1::PUSHJ	P,STAPPC##	;PATCH TO JFCL TO INHIBIT STARTING PRIMARY PROTOCOL
	  JFCL
IFN FTEDSK,<

	PUSHJ	P,RNXSAK##	;INSURE ALL RP20 DX20'S ARE RUNNING
>
>
	HLRZ	U,SYSUNI##	;FIRST UNIT IN THE SYSTEM
	MOVN	J,TICSEC##	;AVOID BRKLOK
	MOVEM	J,.CPOK##	;CALLS BY THE BOOT CPU
SPRIN6:	HRRZ	J,UNIKON##(U)	;KONTROLLER DATA BLOCK
	LDB	T1,KOYCPU##	;CPU THIS CONTROLLER LIVES ON
	CAME	T1,.CPCPN##	;CURRENT CPU?
	JRST	SPRIN7		;NO, ONWARD AND UPWARD
	PUSHJ	P,ATTCPD##	;YES, ATTACH THE UNIT
	  JFCL			;DON'T CARE
	  JFCL			;THIS ONE EITHER
SPRIN7:	HLRZ	U,UNISYS##(U)	;NEXT UNIT IN THE SYSTEM
	JUMPN	U,SPRIN6	;GO IF NOT THE LAST
	SETZB	S,P1		;ALWAYS CALL INI ROUTINE ONCE
	HLRZ	P3,HNGLST##	;LOOK ONLY AT REAL HARDWARE
SPRIN8:	MOVE	F,P3		;SETUP F FOR INI CODE
	MOVE	P3,DEVSER(P3)	;ADDRESS OF DEVICE DISPATCH VECTOR
	LDB	T1,DEYCPF##	;CPU OWNING THE DEVICE 
	CAMN	T1,.CPCPN##	;CURRENT CPU?
	CAIN	P1,(P3)		;YES, ALREADY CALLED INI ROUTINE?
	JRST	SPRIN9		;YES, SKIP ON
	MOVSI	T1,DVMTA	;SPECIAL CHECK FOR MAGTAPE
	TDNN	T1,DEVMOD(F)	;ALWAYS DO INITIALIZATION FOR MAGTAPE
	SKIPN	.CPAID##	; (SEE TPMINI) OR IF OTHER DEVICES
	CAIA			; HAVEN'T BEEN INITIALIZED YET
	JRST	SPRIN9		; OTHERWISE SKIP ON
	PUSHJ	P,DINI(P3)	;NO, CALL INI ROUTINE
	  HRRZ	P1,P3		;NEEDN'T CALL IT AGAIN
SPRIN9:	HLRZS	P3		;NEXT DDB
	JUMPN	P3,SPRIN8	;LOOP IF THERE IS ONE
	SETOM	.CPAID##	;INDICATE DEVICES HAVE ALREADY BEEN INITIALIZED
	MOVSI	T1,40000	;SUPPENDED BIT
IFN FTNET,<
	TDNE	T1,.CPRUN##	;IF RESTARTING,
	PUSHJ	P,FEKCPW##	; TELL THE FEK'S
>
	ANDCAM	T1,.CPRUN##	;CLEAR IT SINCE RESUMING
	SJSP	U,NULJB1##	;GO START NULL JOB
;SUBROUTINE TO INITIALISE CPU1'S MAP FROM CPU0'S MAP

MAPIND:	PUSHJ	P,SSEUB##
MAPINI:	MOVE	T4,.CPTOS##	;GET ADDRESS OF EPT WE ARE USING
	MOVE	T1,BOOTCP##	;CPU WHO'S MAP WE WANT TO COPY
	LSH	T1,P2WLSH##	;TO OFFSET FROM CPU0'S EPT
	MOVSI	T1,.EPPM##(T1)	;SOURCE = HIGH SEG MAP
	HRRI	T1,.EPPM##-.EPMP##(T4)		;DESTINATION = OUR HIGH SEG MAP
	BLT	T1,.EPPM##-.EPMP##+177(T4)	;MOVE IT ALL
IFN FTKL10,<
	MOVE	T1,BOOTCP##	;CPU WHO'S MAP WE WANT TO COPY
	LSH	T1,P2WLSH##	;TO OFFSET FROM CPU0'S EPT
	MOVSI	T1,.EPLM##(T1)	;SOURCE = LOW SEG MAP
	HRRI	T1,.EPLM##-.EPMP##(T4)		;DESTINATION = OUR LOW SEG MAP
	BLT	T1,.EPLM##-.EPMP##+157(T4)	;MOVE IT ALL
>
	JSP	T4,SYSTR1##	;REMAP OUR CDB
	  JFCL
	POPJ	P,		;RETURN


;ROUTINE TO TELL THE OPERATOR THE CURRENT CPUS NAME UPON STARTUP

PRTCPU:	MOVEI	T1,[ASCIZ/
[/]				;START ON A NEW LINE WITH OPEN SQUARE BRACKET
	PUSHJ	P,CTYTYP##	;TYPE THAT
	MOVE	T2,.CPLOG##	;CPU'S LOGICAL NAME
PRTCP1:	MOVEI	T1,0		;ZERO PREVIOUS CHARACTER
	LSHC	T1,6		;SHIFT IN NEW CHARACTER
	MOVEI	T3,40(T1)	;MAKE IT ASCIZ
	PUSHJ	P,CTYWAT##	;TYPE IT
	JUMPN	T2,PRTCP1	;JUMP IF YET ANOTHER SIXBIT CHARACTER
	MOVEI	T1,[ASCIZ/]
/]				;CLOSE OFF WITH CLOSING SQUARE BRACKET AND NEW LINE
	PJRST	CTYTYP##	;TYPE THAT
;SUBROUTINE TO ASSUME THE ROLE OF THE BOOT CPU
; CALLED WHEN THE BOOT CPU'S OK WORD FLIPPED FROM
; NEGATIVE TO ZERO (CALL IS INTERLOCKED BY BOOT CPU'S OK WORD)

BECOM0::PUSHJ	P,SAVT##	;SAVE THE T REGS
	MOVSI	T1,(1B0)	;GET BIT FOR BREAKPOINT ON CPU0
	MOVN	T2,BOOTCP##	;-BOOT CPU NUMBER
	LSH	T1,(T2)		;SHIFT TO BIT FOR BOOT CPU
	TDNE	T1,CPNDDT##	;BOOT CPU AT A BREAKPOINT?
	POPJ	P,		;YES, DON'T ASSUME THE ROLE
	PUSH	P,U		;SAVE U
	MOVEI	T1,LDRCTY##	;THIS IS THE CTY BIT
	MOVE	U,BOOTCT##	;LINE NUMBER OF THE CTY ON THE CPU THAT DIED
	HRRZ	U,LINTAB##(U)	;ADDRESS OF THE LDB
	ANDCAM	T1,LDBDCH##(U)	;ZAP, NO LONGER THE CTY
	HRRZ	T2,.CPCTN##	;OUR CTY LINE NUMBER
	MOVEM	T2,BOOTCT##	;MAKE IT THE "CTY"
	MOVE	T2,LINTAB##(T2)	;LDB FOR OUR CTY
	CAMN	U,OPRLDB##	;WAS "OPR" THE CTY ON THE CPU WHICH WENT AWAY?
	HRRZM	T2,OPRLDB##	;YES, MAKE "OPR" BE OUR CTY
	IORM	T1,LDBDCH##(T2)	;ZAP, WE'RE THE NEW CTY
	PUSH	P,T2		;SAVE NEW CTY LDB
	PUSHJ	P,TTYNAM##	;MAKE TTY NAME FOR OLD CTY
	HRRZ	U,LDBDDB##(U)	;FIND DDB ATTACHED
	SKIPE	U		;IS THERE ONE
	MOVEM	T2,DEVNAM(U)	;YES, CHANGE NAME
	MOVSI	T1,'CTY'	;NAME OF NEW CTY
	POP	P,T2		;NEW CTY LDB
	HRRZ	T2,LDBDDB##(T2)	;FIND ATTACHED DDB
	SKIPE	T2		;ANY DDB THERE
	MOVEM	T1,DEVNAM(T2)	;YES, CHANGE IT
IFN FTNET,<
	MOVE	U,OPRLDB##	;GET LOCAL OPR LDB
	PUSHJ	P,NETOPR##	;TELL NETSER IN CASE IT CHANGED
	  JFCL			;ALWAYS SKIPS
>
	POP	P,U		;RESTORE U
	PUSH	P,J		;SAVE J (NEED FOR MPYNRN)
	MOVE	J,HIGHJB##	;FOR EVERY JOB IN THE SYSTEM
	MOVEI	T1,SP.CP0	;BIT FOR CPU0 (1B35)
	LSH	T1,@.CPCPN##	;BIT FOR NEW POLICY CPU
	TRC	T1,SP.CPA	;NOT RUNNABLE ANYWHERE BUT...
	MOVSI	T2,(SP.ROP)	;FORCED TO POLICY CPU BIT
	TDNE	T2,JBTSPS##(J)	;JOB FORCED?
	DPB	T1,MPYNRN##	;YES, SET NEW NOT-RUNNABLE
	SOJG	J,.-2		;GET ALL THE JOBS
	POP	P,J		;RESTORE J
	SETOM	CLKNEW##	;RECOMP "SYSTEM" CLOCK QUEUE
	MOVE	T1,BOOTCP##	;OLD "CPU0"
	LSH	T1,.CPSOF##	;TO CDB OFFSET
	MOVEI	T1,.C0CDB##(T1)	;POINT TO CDB
	CAMN	T1,.CPSLF##	;ARE WE ALREADY "CPU0"
	POPJ	P,		;YES, SYSINI IS FIXING THE CTY
	PUSHJ	P,BRKLO1	;RETURN INTERLOCK
	MOVE	T1,DATE##	;GET CURRENT UNIVERSAL DATE/TIME
	MOVEM	T1,RSDTTM##	;SAVE FOR CRASH ANALYSIS
	PUSHJ	P,FRCSET##	;SETUP U TO POINT TO FRCLIN
	PUSHJ	P,INLMES##
	ASCIZ/SEND CTY /	;SEND MESSAGE TO NEW CTY
	MOVE	T2,.CPLOG##	;GET NAME OF THIS CPU
	PUSHJ	P,PRNAME##	;PUT INTO MESSAGE
	PUSHJ	P,INLMES##
	ASCIZ/ has assumed the role of policy CPU on / ;TELL WHAT HAPPENED
	PUSHJ	P,DAYTIM##	;ADD DATE/TIME 
BCOM0::	MOVE	T1,.CPCPN##	;OUR CPU NUMBER
	MOVEM	T1,BOOTCP##	;STORE AS NEW "CPU0"
	MOVE	T1,.CPASN##	;OUR SERIAL NUMBER
	MOVEM	T1,SERIAL##	;STORE IN OLD GETTAB
	MOVEI	T1,.C0CDB##	;START OF THE CDB'S
	MOVE	T2,.CPSLF##	;OUR OWN CDB
BECOM1:	MOVSI	T3,(CAI)	;"SKPCPU(0)" FOR THIS CPU
	MOVSI	T4,(CAIA)	;"SKPCPU(1)"
	CAMN	T2,.CPSLF##-.CPCDB##(T1) ;LOOKING AT OURSELF
	EXCH	T3,T4 		;YES, MAKE "SKPCPU(0)" WORK FOR US
	DMOVEM	T3,.CPSK0##-.CPCDB##(T1) ;AND FIX SKIP CPU WORDS
	MOVEI	T3,.CPOK##-.CPCDB##(T2)  ;OUR OK WORD'S ADDRESS
	MOVEM	T3,.CPOK1##-.CPCDB##(T1) ;POINT OK WORDS AT THIS CDB
	MOVEI	T3,SSCAN##	;POLICY CPU SCHEDULER SCAN TABLE
	CAME	T2,.CPSLF##-.CPCDB##(T1) ;LOOKING AT OURSELF
	MOVE	T3,SSCN##(T3)	;NO, USE SECONDARY (REVERSED) TABLE
	MOVEM	T3,.CPSCN##-.CPCDB##(T1) ;STORE SCAN TABLE ADDRESS
	HLRZ	T1,.CPCDB##-.CPCDB##(T1) ;STEP TO NEXT CDB
	JUMPN	T1,BECOM1	;AND FIX IT UP
	POPJ	P,

	XLIST			;DUMPED LITERALS
	LIT
	LIST
	$HIGH			;BACK TO THE HIGH SEGMENT
;HERE WHEN BOOT CPU COUNTS ANOTHERS OK WORD TO 1 SEC
; ASSUME THAT THE CPU IS DOWN AND BREAK INTERLOCKS
;CALL	T1=CDB OF THAT CPU

BRKLOK::PUSHJ	P,SAVT##	;SAVE THE T REGS
	MOVSI	T2,(1B0)	;GET BIT FOR CPU0 AT A BREAKPOINT
	MOVN	T3,.CPCPN##-.CPCDB##(T1) ;-CPU NUMBER FOR CPU IN QUESTION
	LSH	T2,(T3)		;SHIFT TO BIT FOR THAT CPU
	TDNE	T2,CPNDDT##	;CPU AT A BREAKPOINT?
	POPJ	P,		;YES, DON'T BREAK INTERLOCKS
BRKLO1:	PUSHJ	P,BRKSI1	;BREAK THE SOFTWARE INTERLOCKS
	MOVE	T2,INODIE##	;CPU OWNING DIE INTERLOCK
IFN FTKI10,<
	LSH	T2,-^D26	;ISOLATE SERIAL NUMBER
>
IFN FTKL10,<
	ANDI	T2,7777		;ISOLATE SERIAL NUMBER
>
	CAMN	T2,.CPASN##-.CPCDB##(T1) ;THIS CPU OWN THE DIE INTERLOCK?
	SETOM	DIELOK##	;YES, BREAK IT
IFN FTNET,<
	PUSHJ	P,BRKFEK##	;DECLARE THE FEKS DOWN
>
	POPJ	P,


;ROUTINE TO RETURN THE SOFTWARE INTERLOCKS OWNED BY THIS CPU.
;CALL	PUSHJ	P,GIVLOK
;	RETURN HERE ALWAYS

GIVLOK::HRRZ	T1,.CPSLF##	;GET ADDRESS OF OUR CDB
	PUSHJ	P,BRKSIW	;BREAK THE LOCKS
	SETZM	.CPNBI##	;WE KNOW WE JUST BROKE THEM
	POPJ	P,		;RETURN
;ROUTINE TO BREAK THE SOFTWARE INTERLOCKS FOR A CPU THAT HAS GONE
;DOWN OR FROM A JOB STOPCD.
;CALL	T1=CDB ADDRESS OF THE CPU

BRKSIW::PUSHJ	P,SAVT##	;SAVE T1-T4
BRKSI1:	MOVE	T2,.CPASN##-.CPCDB##(T1) ;GET APR ID OF DOWN CPU
	MOVEI	T3,INTRLK##	;FIRST SYSTEM INTERLOCK
BRKSI2:	SKIPGE	0(T3)		;INTERLOCK TAKEN
	JRST	BRKSI3		;NO, LOOK AT NEXT
	MOVE	T4,INTOLK##-INTRLK##(T3) ;OWNER OF THIS INTERLOCK
IFN FTKI10,<
	LSH	T4,-^D26	;ISOLATE SERIAL NUMBER
>
IFN FTKL10,<
	ANDI	T4,7777		;ISOLATE SERIAL NUMBER
>
	CAIE	T2,(T4)		;THAT CPU OWN THE INTERLOCK
	JRST	BRKSI3		;NO, CONTINUE
	AOS	.CPNBI##-.CPCDB##(T1) ;COUNT THE BROKEN INTERLOCK
	SETOM	0(T3)		;YES, FREE IT
BRKSI3:	CAIE	T3,INTOLK##-1	;DO LAST INTERLOCK
	AOJA	T3,BRKSI2	;NO, GET THEM ALL
	MOVE	T4,INTLMO##	;CPU OWNING "MM" RESOURCE
IFN FTKI10,<
	LSH	T4,-^D26	;ISOLATE SERIAL NUMBER
>
IFN FTKL10,<
	ANDI	T4,7777		;ISOLATE SERIAL NUMBER
>
	CAIN	T2,(T4)		;DOES IT OWN THE INTERLOCK
	PUSHJ	P,[AOS .CPNBI##-.CPCDB##(T1) ;COUNT THE BROKEN INTERLOCK
		   PJRST GIVMM]  ;AND LET THE SWAPPER BACK IN;
	MOVE	T2,.CPCPN##-.CPCDB##(T1) ;CPU NUMBER OF LOST CPU
	CAME	T2,SCNOWN##	;DOES IT OWN SWAPPER RESOURCE
	POPJ	P,		;NO, RETURN
	SETOM	SCNOWN##	;YES, ALLOW SWAPPING AGAIN
	SETOM	SCNCNT##	;...
	AOS	.CPNBI##-.CPCDB##(T1) ;COUNT THE BROKEN INTERLOCK
	POPJ	P,		;RETURN
;ROUTINE TO WAIT IN THE AC'S WHILE MEMORY IS BEING SET OFF-LINE
;BY THE BOOT CPU.
;CALL:	JRST	CP1MFL
;	RETURNS TO SAVPC2 IN CLOCK1

CP1MFL::CONO	PI,PI.OFF	;TURN OFF PI SYSTEM
	MOVEM	17,.CPCAC##+17	;SAVE 17
	MOVEI	17,.CPCAC##	;WHERE TO SAVE THE AC'S
	BLT	17,.CPCAC##+16	;SAVE THEM ALL
	MOVE	P,.CPNPD##	;SETUP POINTER TO NULPDL
IFN FTKL10,<
	PUSHJ	P,CP1MF1	;CALL ROUTINE TO WAIT
	JRST	CP1MF3		;AND JUMP AROUND CODE
CP1MF1:	PUSHJ	P,SVPPC##	;SAVE PROTOCOL, ENTER SECONDARY
	PUSHJ	P,CSDMP##	;INSURE IT'S ALL IN CORE
>
	MOVEM	P,.CPPWF##+P	;SAVE P BEFORE LOADING AC LOOP
	MOVE	14,[ACLOOP,,2]
	BLT	14,14		;LOAD THE AC LOOP
	DMOVE	12,[TDNE 17,.CPSBR##	; WE RELOADED IF BIT NO LONGER SET
		    JRST CP1MF2] 	;WHERE TO GO AT END OF LOOP
	MOVEI	17,SR.ACL	;TELL OTHER CPU'S
	IORM	17,.CPSBR##	;THAT WE'RE IN THE AC'S
	JRST	2		;AND GO THERE

	$LOW

CP1MF2:	MOVE	P,.CPPWF##+P	;RESTORE P
	PUSHJ	P,MAPIND	;MAP MAY HAVE CHANGED
IFN FTKL10,<
	POPJ	P,
CP1MF3:
>
	MOVEI	17,SR.ACL	;NO LONGER IN
	ANDCAM	17,.CPSBR##	;  THE AC'S
	MOVSI	17,.CPCAC##	;WHERE TO RESTORE THE AC'S FROM
	BLT	17,17		;RESTORE THEM ALL
	CONO	PI,PI.ON	;TURN PI SYSTEM BACK ON
	JRST	SAVPC2##	;RETURN

	$HIGH
;ROUTINE TO WAIT IN THE AC'S WHILE ANOTHER CPU IS AT A BREAKPOINT.
;CALL:	JRST	CPNBPT
;	RETURNS @.CPABK

CPNBPT::CONO	PI,PI.OFF	;TURN OFF PI SYSTEM
	JSR	.CPSVA##	;SAVE ALL AC SETS
	MOVE	P,.CPEPD##	;INSURE A PDL IS SETUP
IFN FTKL10,<
	PUSHJ	P,CPNBP1	;CALL ROUTINE TO DO THE WORK
	JRST	CPNBP3		;AND SKIP THE SUBROUTINE
CPNBP1:	PUSHJ	P,SVPPC##	;SAVE PROTOCOL, ENTER SECONDARY
	PUSHJ	P,CSDMP##	;MAKE SURE IT'S ALL IN CORE
>
	MOVE	6,[BPLOOP,,2]	;SETUP TO LOAD THE AC LOOP
	BLT	6,6		;MOVE IN THE AC LOOP
	MOVEI	17,SR.ACL	;GET BIT TO TELL OTHERS THAT WE ARE
	IORM	17,.CPSBR##	;LOOPING IN THE AC'S
	JRST	2		;AND GO WAIT

	$LOW

CPNBP2:	PUSHJ	P,MAPIND	;INSURE THE MAP IS SETUP CORRECTLY
IFN FTKL10,<
	POPJ	P,		;RETURN FROM SUBROUTINE IF KL10
CPNBP3:
>
	MOVEI	17,SR.ACL	;NO LONGER LOOPING
	ANDCAM	17,.CPSBR##	;  IN THE AC'S
	HRLZ	17,.CPACA##	;WHERE TO RESTORE THE AC'S FROM
	BLT	17,17		;RESTORE THEM ALL
	CONO	PI,PI.ON	;TURN ON PI SYSTEM
	JRSTF	@.CPABK##	;RETURN TO CALLER

	$HIGH
;HERE ON ALL CPU'S BUT THE BOOT CPU WITH THE AC'S AND MACHINE
;STATE SAVED TO LOOP IN THE AC'S WAITING FOR THE SYSTEM TO BE RELOADED
;OR FOR THE OPERATOR TO RESTART THE CPU.  IF SR.DIE IS SET IN .CPSBR,
;THE PROCESSOR WILL NOT RESTART (EVEN AFTER A RELOAD) UNLESS THE
;OPERATOR MANUALLY STARTS IT AT 400.


CP1CRS::
IFN FTKL10,<
	MOVE	P,.CPEPD##	;INSURE STACK IS SETUP
	PUSHJ	P,ENTSPC##	;ENTER SECONDARY PROTOCAL
>
	MOVEI	17,SR.ACL	;GET LOOPING IN THE AC'S BIT
	IORM	17,.CPSBR##	;  AND TELL OTHERS
	MOVEI	T1,SR.DIE	;BIT TELLING US NOT TO RESTART
	TDNE	T1,.CPSBR##	;SET FOR THIS CPU?
	PJRST	RMVCPU		;YES, JRST 1 FOREVER
	MOVE	14,[ACLOOP,,2]	;LOAD BLT POINTER TO AC LOOP
	BLT	14,14		;LOAD THE AC LOOP
	JRST	2		;JUMP INTO THE AC'S

	$LIT
	$LOW

;THE FOLLOWING LOOPS ARE LOADED INTO THE AC'S WHEN A CPU WANTS TO
;WAIT FOR AN EVENT.  ACLOOP AND BP LOOP ARE BOTH ENTERED WITH AC
;17 CONTAINING SR.ACL.

ACLOOP:	MOVEI	1,20		;2  INITIALIZE ITERATION COUNT
	MOVE	0,.CPOK##	;3  GET CURRENT .C1OK
	CAMN	0,.CPOK##	;4  SEE IF .C1OK HAS CHANGED
	JRST	4		;5  NO, WAIT UNTIL IT DOES CHANGE
	AOS	0		;6  COUNT OLD .C1OK BY ONE
	CAME	0,.CPOK##	;7  DID .C1OK INCREASE BY EXACTLY ONE
	JRST	2		;10 NO, START OVER AGAIN
	SOJG	1,4		;11 WAIT FOR 20 COUNTS TO BE SURE
	JFCL			;12 PATCHED
	JFCL			;13 PATCHED
	JRST	SYSDSP##	;14 GO RESTART SLAVE

BPLOOP:	SKIPE	CPNDDT##	;2 IS DDT FINISHED YET?
	JRST	2		;3 NO, LOOP
	TDNE	17,.CPSBR##	;4 IF NOT STILL SET, WE RELOADED
	JRST	CPNBP2		;5 RETURN TO CALLER
	JRST	SYSDSP##	;6 GO RESTART CPU

;NOTE THAT WE ENTER AT 3 THE FIRST TIME, WITH 0 NON-0
ONCLOP:	MOVE	0,.CPOK##	;1  RESYNCHRONIZE
	SKIPE	17		;2  DID BOOTS ZERO CORE?
	MOVE	17,[1]		;3  NO. THIS LITERAL SHOULD BE RIGHT IF SAME MONITOR
	CAMN	0,.CPOK##	;4  WAIT TILL .CPOK COUNTS UP BY 1
	JRST	2		;5  DIDNT COUNT, WAIT A WHILE
	AOS	0		;6  COUNTED. BY 1?
	CAME	0,.CPOK##	;7
	JRST	1		;10 NO, WAIT TILL IT COUNTS BY EXACTLY 1
	MOVE	0,[1]		;11 IS THE LITERAL STILL INTACT?
	SOJN	0,1		;12 NO, DIFFERENT MONITOR WAS LOADED
	JUMPE	17,SYSDSP##	;13 YES, 17=0 IF BOOTS RELOADED THE MONITOR
	SKIPE	ONCCOM##	;14 ALL SEEMS OK. SOMETHING FOR US TO DO?
	JRST	SPRIN3		;15 YES, GO DO WHAT CPU0 TELLS US
	JRST	1		;16 NOTHING TO DO, WAIT SOME MORE

	LIT
	$HIGH
SUBTTL	MULTIPROCESSING COMMANDS AND UUOS

;CODE TO IMPLEMENT THE SETUUO'S NUMBER 14 (.STCPU) 
;AND 15 (.STRUN)

SETCRN::MOVEI	T4,.C0CDB##	;ADDRESS OF FIRST CDB
SETCR1:	MOVE	T3,.CPRUN##-.CPCDB##(T4)  ;GET RUN BIT FOR CPU
	TRNE	T2,1		;CHECK BIT FOR CPU RUNNABILITY
	TLZA	T3,400000	;CLEAR SIGN BIT TO ALLOW CPU TO RUN
	TLO	T3,400000	;SET SIGNBIT TO TURN CPU OFF
	MOVEM	T3,.CPRUN##-.CPCDB##(T4)  ;PUT BACK IN CDB
	HLRZ	T4,.CPCDB##-.CPCDB##(T4)  ;GET ADDRESS OF NEXT CDB
	PJUMPE	T4,CPOPJ1##	;NO ERROR RETURNS
	LSH	T2,-1		;MOVE NEXT BIT TO 35
	JRST	SETCR1		;AND CHECK OUT THIS ONE

.ERNAR==0			;NONE ARE RUNNING ERROR CODE
.ERDHP==1			;DOESN'T HAVE PRIVILEGES ERROR
;ROUTINE TO HANDLE SETUUO #14 (.STCPU) USER SET
;CPU SPECIFICATION

SETCPU::MOVSI	T3,JP.CCC	;CAN CHANGE CPU SPECIFICATION BIT
	TDNE	T3,JBTPRV##(J)	;USER PRIVILEGED TO CHANGE?
	JRST	SETCP1		;YES, PROCEED
	PUSHJ	P,PRVJ##	;NO, IS HE [1,2] OR IS JACCT SET?
	  JRST	SETCP1		;YES, OK TO PROCEED
	MOVEI	T1,.ERDHP	;NO, GIVE ERROR RETURN
	PJRST	STOTAC##	;WITH DOESN'T HAVE PRIVILEGE CODE

SETCP1:	MOVE	T1,T2		;HOLD BITS IN T1
	MOVEI	T4,.C0CDB##	;GET ADDRESS OF CPU0 CDB
	TRZ	T2,777700	;EXTRACT THE BITS WE WANT
SETCP2:	TRNE	T2,1		;DO WE CARE ABOUT THIS CPU?
	SKIPGE	.CPRUN##-.CPCDB##(T4)  ;YES, IS IT RUNNING?
	JRST	SETCP3		;NO, CHECK NEXT
	DPB	T1,[POINT 6,JBTSPS##(J),35]	;YES, WE ARE SATISFIED.
	JRST	CPOPJ1##	;AND SKIP RETURN

SETCP3:	HLRZ	T4,.CPCDB##-.CPCDB##(T4)  ;GET ADDRESS OF NEXT CDB
	JUMPE	T4,SETCP4	;IF NO MORE, ERROR
	LSH	T2,-1		;MOVE NEXT BIT INTO POSITION
	JRST	SETCP2

SETCP4:	MOVEI	T1,.ERNAR	;GET NONE ARE RUNNING CODE
	PJRST	STOTAC##	;GIVE TO USER AND CPOPJ
;ROUTINE TO HANDLE SETUUO #37 (.STCDN)
; SET CPU UP/DOWN STATUS

SETCDN::HRRZ	T1,T2		;COPY CPU NUMBER
	TRZE	T1,1B19		;SUPPEND A CPU?
	JRST	SETCD3		;YES
	TRZN	T1,1B18		;ADD OR REMOVE A CPU
	JRST	SETCD1		;ADDING
	MOVE	T3,[SOS NCPRUN##] ;REMOVE, SET TO DECREMENT
	MOVE	T4,NCPRUN##	;HOW MANY CPUS ARE LEFT
	SOJLE	T4,CPOPJ##	;CAN'T REMOVE THE LAST
	JRST	SETCD2		;JOIN COMMON PART
SETCD1:	MOVE	T3,[AOS NCPRUN##] ;ADD, SET TO BUMP COUNT
	MOVSI	T4,(ST%LSC)	;LOW SEG CACHED BIT
	TDNN	T4,CNFST2##	;CACHE RIGHT FOR ADDING
SETCD2:	CAIL	T1,CPUN##	;RANGE CHECK CPU NUMBER
	POPJ	P,		;CACHE WRONG OR BAD CPU
	LSH	T1,.CPSOF##	; TO CDB OFFSET FROM CPU0'S
	SKIPL	T4,.C0RUN##(T1)	;GET RUN BITS FOR CPU
	TRNN	T2,1B18		;STILL RUNNABLE, TRYING TO ADD
	SKIPA			;YES, ALWAYS OK TO ADD A CPU
	POPJ	P,		;CPU STILL RUNNABLE, IGNORE
	TRNN	T2,1B18		;ADDING OR REMOVING IT
	TLZA	T4,200000	;ADD, CLEAR "GO AWAY" BIT
	TLO	T4,200000	;MARK FOR REMOVAL
	CAME	T4,.C0RUN##(T1)	;DID WE CHANGE STATES
	XCT	T3		;YES, ADJUST COUNT OF CPUS
	MOVEM	T4,.C0RUN##(T1)	;STORE NEW BITS
	JRST	CPOPJ1##	; AND ALL DONE
SETCD3:	CAIL	T1,CPUN##	;LEGAL CPU NUMBER?
	POPJ	P,		;NO, ERROR RETURN
	LSH	T1,.CPSOF##	;OFFSET INTO THE APPROPRIATE CDB
	MOVSI	T2,40000	;CPU IS TO BE SUPPENDED BIT
	IORM	T2,.C0RUN##(T1)	;LITE THE BIT
	JRST	CPOPJ1##	;TELL THE USER HE DID GOOD


;HERE FROM CLOCK1 WHEN ITS TIME TO REMOVE THIS CPU OR ON A CPU STOPCODE
RMVCPU::
IFN FTKL10,<
	MOVEI	T1,LG.CSL!LG.CSW ;TURN OFF CACHE BITS
	ANDCAM	T1,.CPEBR##	;FOR 400 RE-START
	MOVE	F,.CPCPN##	;CPU NUMBER
	MOVE	F,DTEMAS##(F)	;THE MASTER DTE
	PUSHJ	P,TTDRLD##	;CLEAR F.E. TTY STATUS
	PUSHJ	P,SVPPC##	;TURN OFF "KEEP ALIVE"
	PUSHJ	P,CSDMP##	;EMPTY CACHE
	AOS	.CPCSN##	;INSURE SWEEP GETS RECORDED
>
	CONO	PI,CLRPIS##	;DON'T MAINTAIN OK WORDS
	CONO	APR,AP1RST##	;BLAST THE PROCESSOR
	MOVE	1,.+1		;GET AN INFINITE LOOP
	JRST	1		;AND DISAPPEAR

CPUCMD::PUSHJ	P,SAVE1##	;SAVE P1
	MOVSI	P1,JP.CCC	;GET CAN CHANGE CPU SPECIFICATION BIT
	PUSHJ	P,SETLGL##	;IS THIS THE OPR OR A [1,2] JOB,
	  TDNE	P1,JBTPRV##(J)	; OR DOES THE JOB HAVE PRIVILEGES?
	JRST	CPUCM1		;YES, PROCEED

	PJSP	T1,ERRMES##	;NO, ERROR MESSAGE
ASCIZ	/No privileges to SET CPU
/

CPUCM1:	PUSHJ	P,CTEXT##	;GET NEXT ARGUMENT
	MOVE	P1,T2		;SAVE IN P1
	PUSHJ	P,CPUFND	;SEE IF IT IS CPXN
	  JRST	CPUCM2		;NO, GET REST OF COMMAND
	IORM	T3,JBTSPS##(J)	;WAS SET CPU CPXN, BITS IN T3
	JRST	CPUCM5		;AND RETURN

CPUCM2:	PUSHJ	P,CTEXT##	;GET NEXT ARGUMENT
	JUMPE	T2,CPUCM4	;NO MORE, MUST BE SET CPU ALL
	PUSHJ	P,CPUFND	;FIND CPU AND SET MASK BITS
	  PJRST	COMERR##		;NOT FOUND, ERROR
	CAME	P1,[SIXBIT /NO/]	;WAS THIRD ARGUMENT "NO"?
	JRST	CPUCM3		;NO, CHECK OTHERS
	ANDCAM	T3,JBTSPS##(J)	;YES, CHANGE SPECIFICATION
	JRST	CPUCM5		;AND RETURN

CPUCM3:	CAME	P1,[SIXBIT /ONLY/]	;WAS THIRD ARGUMENT ONLY?
	PJRST	COMERR##	;NO, ERROR
	DPB	T3,[POINT 12,JBTSPS##(J),35]	;CHANGE SPECIFICATION
	JRST	CPUCM5		;AND RETURN.

CPUCM4:	CAME	P1,[SIXBIT/ALL/];ARGUMENT #3 MUST BE "ALL"
	PJRST	COMERR##	;WASN'T, ERROR
	MOVEI	T3,7777		;GET BITS FOR ALL POSSIBLE CPU'S
	DPB	T3,[POINT 12,JBTSPS##(J),35]	;CHANGE SPECIFICATION
CPUCM5:	PUSHJ	P,CPRONE	;CHECK TO SEE IF NEW SPECIFICATION IS VALID
	PJRST	CPOPJ1##	;AND RETURN
;SUBROUTINE TO DETERMINE IF CPU DESIGNATOR IN SET CPU COMMAND
;IS LOGICAL OR PHYSICAL CPU NAME.  IF PHYSICAL, MAKE SURE IT EXISTS AND
;SET UP BIT MASK FOR JBTSPS.  CALLED WITH SIXBIT ARGUMENT IN T2,
;CPU NUMBER RETURNED IN T1, T2 RETURNED WITH ORIGINAL ARGUMENT, BIT MASK
;RETURNED IN AC T3.  T4 RESPECTED.

CPUFND::HLLZ	T1,T2		;GET LEFT MOST 3 CHARACTERS
	CAMN	T1,[SIXBIT /CPU/]	;IS ARGUMENT LOGICAL NAME?
	JRST	CPUFN2		;YES, DOESN'T MATTER IF IT EXISTS
	MOVEI	T3,.C0CDB##	;GET ADDRESS OF FIRST CDB
CPUFN1:	CAMN	T2,.CPPHY##-.CPCDB##(T3)  ;THIS CPU?
	JRST	CPUFN2		;YES, OK TO PROCEED
	HLRZ	T3,.CPCDB##-.CPCDB##(T3)  ;GET ADDRESS OF NEXT CDB
	JUMPE	T3,CPOPJ##	;NON-SKIP RETURN IF NON EXISTANT
	JRST	CPUFN1		;GO LOOK AT NEXT
CPUFN2:	HRRZ	T1,T2		;CPU NAME OK, MAKE A BIT MASK
	LSH	T1,-14		;GET SIXBIT NUMBER RIGHT JUSTIFIED
	SUBI	T1,20		;REDUCE TO BINARY
	CAIG	T1,5		;LEGAL CPU NUMBER?
	SKIPGE	T1		;
	POPJ	P,		;ERROR RETURN IF NON EXISTANT
	MOVEI	T3,101		;MASK FOR CPU0
CPUFN3:	LSH	T3,(T1)		;POSITION MASK
	JRST	CPOPJ1##	;AND GIVE FOUND RETURN

;SUBROUTINE CALLED BY SIMCHK IN CLOCK1 TO SEE IF JOB WITH EXEC
;MODE PC HAS A RUNNABLE CPU SPECIFICATION.  RETURN CPOPJ IF NONE, CPOPJ1 IF RUNNABLE


CPSCHK::PUSHJ	P,CPRBIT	;SET UP MASK OF RUNNING CPU'S
	TDNN	T2,JBTSPS##(J)	;SKIP IF JOB HAS A RUNNABLE CPU
	POPJ	P,		;NO RUNNABLE CPU'S, STOP JOB
	MOVE	T2,JBTSPS##(J)	;PICK UP SECOND PROCESSOR STATUS
	TLNE	T2,(SP.CC1)	;SKIP IF CONTROL C NOT ON CPU1
	TLNE	T2,(SP.NR1)	;WAS ON CPU1, SKIP IF NOT IN MIDDLE OF UUO
	JRST	CPOPJ1##	;IS RUNNABLE, CATCH AT UUO EXIT
	TRNN	T2,SP.CR0	;SINCE CAN'T RUN ON CPU1, SKIP IF RUNNABLE ONCPU0
	POPJ	P,		;IS NOT RUNNABLE, STOP JOB.
	JRST	CPOPJ1##	;JOB IS RUNNABLE, DEFER STOPPING.

;SUBROUTINE TO BUILD A BIT MASK FOR TESTING JBTSPS ENTRIES.  AN
;APPROPRIATE BIT IS SET FOR EACH RUNNING CPU.  RETURNS MASK IN T2.

CPRBIT:	MOVEI	T4,.C0CDB##	;GET ADDRESS OF FIRST CDB
	MOVEI	T3,1		;GET BIT INDICATING CPU0 IS RUNNING
	SETZ	T2,		;CLEAR T2
CPRBT1:	SKIPL	.CPRUN##-.CPCDB##(T4)  ;SKIP IF CPU NOT RUNNING
	ADD	T2,T3		;SET BIT IN MASK
	HLRZ	T4,.CPCDB##-.CPCDB##(T4)  ;GET ADDRESS OF NEXT CDB
	JUMPE	T4,CPOPJ##	;EXIT IF THROUGH
	LSH	T3,1		;MOVE BIT TO NEXT POSITION
	JRST	CPRBT1		;AND LOOK AT NEXT CPU

;SUBROUTINE TO CHECK IF A JOB IS RUNNABLE ON ANY PROCESSOR CALLED
;WITH MASK OF RUNNING PROCESSORS IN AC T2 AND JOB NUMBER TO CHECK IN J.
CPRONE:	PUSHJ	P,CPRBIT	;SET UP MASK OF RUNNING CPU'S

CPRCHK:	TDNE	T2,JBTSPS##(J)	;SKIP IF NO RUNNABLE CPU FOR THIS JOB
	POPJ	P,		;OK, RETURN
	SKIPE	U,TTYTAB##(J)	;GET TTY DDB
	HRRZ	U,DDBLDB##(U)	;LDB ADDRESS
	JUMPE	U,CPOPJ##	;NO MESSAGE IF NO TTY
	PJSP	T1,CONMES##	;PRINT MESSAGE TO USER AND CPOPJ
ASCIZ	/%No Running CPU's in specification
/


CPRMIN:	PUSHJ	P,CPRBIT	;GET MASK FOR TESTING JBTSPS
	SKIPLE	J,HIGHJB##	;GET HIGHEST USED JOB NUMBER
				; -1 IF NO JOBS HAVE LOGGED IN YET
	PUSHJ	P,CPRCHK	;CHECK TO SEE IF RUNNABLE ON SOME CPU
	SOJG	J,.-1		;NEXT?
	POPJ	P,		;NO, EXIT

	$LIT			;GENERATE LITERALS SO CAN CHECK ASCII OCTAL

CPNEND:	END