Google
 

Trailing-Edge - PDP-10 Archives - bb-jr93k-bb - 10,7/mon/cpnser.mac
There are 11 other files named cpnser.mac in the archive. Click here to see a list.
TITLE	CPNSER - SECOND PROCESSOR CONTROL V513
SUBTTL	M. CHURCH - LNS/MIT     P. HURLEY - DEC/JMF/TW/CDO/GMU/TARL 15-AUG-89

	SEARCH	F,S,DEVPRM
	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;  OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION
; 1973,1974,1975,1976,1977,1978,1979,1982,1984,1986,1988.
;ALL RIGHTS RESERVED.

.CPYRT<1973,1988>


XP VCP1SR,513	;VERSION NUMBER FOR LOADER STORAGE MAP



CPNSER::ENTRY	CPNSER		;LOAD IF SPECIFIED BY COMMON
SUBTTL	SCHEDULER ROUTINES

;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
	CAIN	T1,PCLCP0##	;GENERIC BOOT CPU?
	JRST	ONCPU0		;YES, HANDLE DIFFERENTLY
	PJRST	ONCPUN		;NO, 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::CAIN	T1,PCLCP0##	;BOOT CPU?
	JRST	[AOS	(P)	;YES, SUCCEED
		 JRST	ONCPU0]	;AND GET THERE
	CAIL	T1,M.CPU##	;IN LEGAL RANGE?
	POPJ	P,		;NO, FAIL
	PUSH	P,T1		;SAVE CPU NUMBER
	IMULI	T1,.CPLEN##	;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::CAIN	T1,PCLCP0##	;BOOT CPU?
	JRST	ONCPU0		;YES, HANDLE DIFFERENTLY
	CAIL	T1,M.CPU##	;NO, ARGUMENT IN LEGAL RANGE?
	STOPCD	CPOPJ##,JOB,ONCPUX,	;++<X> OUT OF RANGE IN ONCPU<X>
	PUSHJ	P,SETCPN	;MAKE RUNNABLE ONLY ON THE RIGHT CPU
	CAMN	T1,.CPCPN##	;ARE WE ON THE RIGHT CPU?
	POPJ	P,		;YES, RETURN
	PUSHJ	P,UUOLVL##	;CALLED AT INTERRUPT LEVEL?
	 STOPCD .,STOP,ONCPUI	;++ONCPU CALLED AT INTERRUPT LEVEL
	PUSH	P,.USMUO	;SAVE FLAGS
	PUSH	P,.USMUP	;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
	LDB	T1,[POINT 5,.USUPF,17] ;SET PCS
	TLO	T1,(IC.UOU)	;LITE USRIOT
	XMOVEI	T2,ONCPU1
	DMOVEM	T1,.CPPC##
	JRST	CLKSPD##	;RESCHED TO RIGHT CPU

;HERE ON CPU N

ONCPU1:	POP	P,.USMUP	;RESTORE UUO PC
	POP	P,.USMUO	;AND FLAGS
	POPJ	P,		;AND CONTINUE, ON CORRECT CPU
SUBTTL	MULTIPROCESSING I/O CONTROL

;SUBROUTINE TO CHECK IF ON THE RIGHT CPU FOR IO
;T1 RETURNS MASK OF WHICH CPU'S CAN DO THE IO
;PRESERVES T2,T3
CHKCPI::LDB	T4,DEYCPF##	;GET NUMBER OF 1ST CPU OWNING DEVICE
	CAIE	T4,CPFBIT##	;USE BIT MASK?
	JRST	CHKCP2		;NO, DO IT THE OLD WAY
	LDB	T4,PDVTYP##	;GET DEVICE TYPE
	CAIN	T4,.TYMTA	;MAGTAPE?
	PJRST	TAPCPI##	;ASK TAPSER
	HRRZ	T4,DEVUNI##(F)	;YES, MUST BE A DISK
	JUMPE	T4,CHKCP7
	MOVE	T1,UDBCAM(T4)	;GET MASK FOR PRIME PORT
IFN FTDUAL,<
	SKIPE	T4,UNI2ND(T4)	;DUAL PORTED?
	IOR	T1,UDBCAM(T4)	;YES, ADD MASK FOR SECOND PORT
>
CHKCP8:	TDNE	T1,.CPBIT##	;CAN THIS CPU DO IT?
	AOS	(P)		;YES
	POPJ	P,		;NO

;HERE IF DEVUNI IS ZERO (CAN HAPPEN IF
;CONCATENATING THE UFDS FROM SEVERAL STRS)
CHKCP7:	MOVEI	T1,<1_M.CPU##>-1 ;LIE, SAY ALL CPUS
	JRST	CPOPJ1##

;HERE FOR AN OLD STYLE DEVICE THAT DOES NOT HAVE A BIT MASK
CHKCP2:	CAIN	T4,CPFBOO##	;DEVICE ON GENERIC BOOT CPU
	MOVE	T4,BOOTCP##	;YES, GET CPU NUMBER
	MOVEI	T1,1		;BUILD A MASK
	LSH	T1,(T4)
REPEAT 0,<			;UNTIL WE GET ANOTHER DEVICE THAT LIGHTS DEPCPS
	MOVE	T4,DEVCPU(F)	;DUAL PORTED DEVICE?
	TLNN	T4,DEPCPS
	JRST	CHKCP8		;NO
	PUSH	P,T3		;YES, SAVE AN AC
	LDB	T4,DEYCPS##	;GET 2ND CPU WHICH OWNS DEVICE
	MOVEI	T3,1		;BUILD A MASK
	LSH	T3,(T4)
	OR	T1,T3		;BOTH MASKS TOGETHER
	POP	P,T3
>	;END OF REPEAT 0
	JRST	CHKCP8

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


;ROUTINE TO SEE IF ALL OF THE CPU'S ARE DEAD
;T1 PASSES BIT MASK FOR CPU'S TO BE TESTED (NOT PRESERVED)
;SKIP IF AT LEAST ONE CPU IS OK
;T1 RETURNS THE NUMBER OF THE CPU THAT IS KNOWN TO BE ALIVE
CPUOK::	TDNE	T1,.CPBIT##	;US?
	JRST	[MOVE	T1,.CPCPN## ;GET OUR CPU NUMBER
		 JRST	CPOPJ1##] ;RETURN WITH ANSWER IN T1
	PUSH	P,T2		;SAVE T2
	PUSH	P,T3		;SAVE T3
	PUSH	P,T4		;SAVE T4
	SETZ	T3,		;WILL COUNT BITS SO FAR
CPUOK1:	JFFO	T1,CPUOK2	;FIND A BIT
	JRST	CPUOK3		;NONE LEFT, ALL DEAD
CPUOK2:	LSH	T1,1(T2)	;SHIFT PAST THAT BIT
	ADDI	T3,1(T2)	;COUNT BITS THAT WERE SHIFTED
	MOVEI	T4,^D36		;FIND THAT CDB
	SUB	T4,T3
	IMULI	T4,.CPLEN##
	SKIPL	.C0OK##(T4)	;IS CPU OK?
	JRST	CPUOK1		;NO, KEEP LOOKING
	MOVE	T1,.C0CPN##(T4)	;GET CPU NUMBER
	AOS	-3(P)		;SKIP
CPUOK3:	POP	P,T4		;RESTORE T4
	POP	P,T3		;RESTORE T3
	POP	P,T2		;RESTORE T2
	POPJ	P,		;RETURN WITH ANSWER IN T1


;ROUTINE TO CONVERT A BIT MASK INTO A CPU NUMBER
;THIS ROUTINE WILL CHOOSE A RUNNING CPU IF POSSIBLE
;T1 PASSES BIT MASK
;T1 RETURNS THE NUMBER OF THE CPU 
;ALWAYS RETURNS CPOPJ
CAMCPU::PUSH	P,T1		;SAVE MASK
	PUSHJ	P,CPUOK		;TRY TO FIND A LIVE CPU
	  JRST	CAMCP0		;ALL DEAD
	POP	P,(P)		;PHASE STACK
	POPJ	P,		;RETURN WITH CPU NUMBER IN T1
CAMCP0:	MOVE	T1,T2		;COPY T2
	EXCH	T1,(P)		;SAVE AC AND GET MASK BACK
	JFFO	T1,CAMCP1	;FIND A BIT
	SKIPA	T2,.CPCPN##	;NONE AT ALL, TELL HIM THIS CPU
CAMCP1:	SUBI	T2,^D35		;CONVERT BIT NUMBER TO CPU NUMBER
	MOVM	T1,T2		;
	JRST	T2POPJ##	;RESTORE T2 AND RETURN W/ANSWER IN T1

;ROUTINE TO BUILD A COMBINED BIT MASK FOR A DUAL PORTED DRIVE
;T1 RETURNS THE MASK
GETCAM::MOVE	T1,UDBCAM(U)	;1ST MASK
IFN FTDUAL,<
	SKIPE	T2,UNI2ND(U)	;2ND UDB
	IOR	T1,UDBCAM(T2)	;2ND MASK
>
	POPJ	P,
;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
	NBFOFF
	MOVEM	T2,DEVCSN(F)	;SAVE IN DDB
	MOVE	T2,.CPCPN##-.CPCDB##(T1) ;CPU JOB LAST RAN ON
	DPB	T2,DEYCPU##	;SAVE IN DDB
	JRST	ONPOPN##

;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,CHKCPI	;IF WE ARE ON THE CPU WHICH OWNS THE DEVICE
	  JRST	CHKNB
	CAMN	T1,.CPBIT##	;ARE WE THE ONLY CPU?
	JRST	ADDNBF		;YES, IT'S OK
	TLNN	S,IO
	JRST	CHKNB
	LDB	T1,DEYPCL##	;CPU WHICH STARTED IO
	CAIN	T1,PCLCP0##	;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::	NBFOFF
	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
	IMULI	T1,.CPLEN##
	MOVE	T1,.C0CSN##(T1)	;CURRENT SWEEP SERIAL NUMBER ON THAT CPU
	CAMG	T1,DEVCSN(F)	;IF THEY ARE THE SAME
	JRST	ONPOPN##	; 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:	NBFOFF
ADDNB1:	HRRZ	T1,DEVNBF(F)	;NUMBER OF BUFFERS TO UPDATE BY
	JUMPE	T1,ONPOPN##	;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	ONPOPN##	;AND RETURN
;	STILL IN FTKL10 CONDITIONAL
;SUBROUTINE TO CHECK IF AN INPUT BUFFER IS OK WITH RESPECT TO CACHE
;ENTER T1=BUFFER ADDRESS
;CPOPJ EXIT IF BUFFER ISN'T RIGHT, CPOPJ1 IF IT IS
BUFSSN::PUSH	P,T1
	PUSHJ	P,MLSCSH	;IS LOW SEG CACHED?
	  SKIPA T3,.USJOB	;NO,GET JOB NUMBER
	JRST	TPOPJ1##	;YES,DOWN TO ONE CPU, DON'T WORRY
	POP	P,T1		;RESTORE ADDR OF BUFFER
	HRRZ	T3,JBTST3##(T3)	;GET CPU JOB LAST RAN ON (OR IS RUNNING ON)
	CAMN	T3,.CPSLF##	;DID IT RUN WHERE THE IO WILL HAPPEN?
	JRST	CPOPJ1##	;YES, CACHE IS RIGHT (OR WILL BE WHEN IO IS DONE)
	MOVE	T4,.CPCPN##-.CPCDB##(T3) ;NO. GET CPU NUMBER
	EXCTUX	<MOVS T2,-1(T1)> ;GET S-WORD OF BUFFER
	TRZE	T2,IO		;DID UUOCON LAST SAVE SWEEP NUMBER?
	CAIE	T4,(T2)		;YES. WAS IT ON THE CURRENT CPU?
	JRST	BUFSS1		;NO. THIS BUFFER IS OK TO FILL
	HLRZS	T2		;YES. ISOLATE SWEEP NUMBER
	HRRZ	T3,.CPCSN##-.CPCDB##(T3) ;GET CURRENT SWEEP NUMBER FOR THAT CPU
	CAME	T2,T3		;WAS CACHE SWEPT AFTER BUF BECAME AVAILABLE?
BUFSS1:	AOS	(P)		;YES. BUFFER IS USABLE
	SUBI	T1,1		;WE REFERENCED THAT CACHE LINE,
	PJRST	OUCHE##		; SO GET IT OUT OF CACHE 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
SETCF1:	PUSHJ	P,SETCF2
	  SKIPA	T1,[DVOFLN,,0]	;GET OFFLINE BIT
	POPJ	P,		;IT'S ALIVE
	IORM	T1,DEVCHR(F)	;LIGHT IN DDB
	PUSHJ	P,CPUOFS	;GET OFFSET TO CPU'S CDB
	MOVSI	T2,(CR.DET)	;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 EUM
	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
	JSP	T1,ERRPNT##	;USER NOT WANT TRAP, PRINT MESSAGE
	ASCIZ	/CPU failed for /
	PJRST	DEVEXC##	;AND STOP HIM
SETCPP::PUSHJ	P,SAVT##	;SAVE T1-T4
SETCF2:	LDB	T1,DEYPCL##	;IF QUEUED PROTOCOL
	JUMPN	T1,CPOPJ1##	; IT CAN RUN ON ANY CPU
	PUSHJ	P,CHKCPI
	  PUSHJ	P,GETCP1	;GET OWNING CPU
	MOVE	T1,.CPCPN##
	PUSHJ	P,ONCPUS	;GET ON CORRECT CPU
	  POPJ	P,		;CPU NOT RUNNING
	JRST	CPOPJ1##	;RETURN WHEN ON CPUN
;SUBROUTINE TO RING THE DOORBELL
SETQPB::PUSHJ	P,CPUOFS	;GET RIGHT CPU
SETQP1::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::PUSHJ	P,GETCPF	;CPU OWNING THE DEVICE
	IMULI	T1,.CPLEN##	;MULTIPLY BY LENGTH OF CDB
	POPJ	P,		;RETURN

;ROUTINE TO GET (THE ONE AND ONLY) CPU NUMBER
;T1 RETURNS THE CPU NUMBER
GETCP1:	AOS	(P)		;SKIP RETURN
GETCPF:	LDB	T1,DEYCPF##	;GET THE CPU NUMBER
	CAIE	T1,CPFBIT##	;IS THERE MORE THAN ONE?
	POPJ	P,		;ONLY ONE
	PUSHJ	P,CHKCPI	;GET THE BIT MASK AGAIN
	  TRNA			;NOT REACHABLE HERE
	SKIPA	T1,.CPCPN##	;WE CAN DO THE I/O, DON'T ASK ANYONE ELSE
	PUSHJ	P,CAMCPU	;FIND SOME CPU WHO CAN DO THE I/O
	POPJ	P,		;RETURN TARGET CPU NUMBER IN T1
;CODE CALLED FROM USRXIT TO SEE IF MUST RESCHEDULE

UXITMP::SKIPGE	.CPRUN##	;CAN THIS CPU RUN JOBS
	PJRST	DPXST		;NO, MUST RESCHEDULE
IFE SP.CR0-1,<
	MOVE	T1,.CPBIT##	;BIT POSITION FOR CURRENT CPU
>
IFN SP.CR0-1,<
	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
	  SKIPA	T1,JBTSPS##(J)	;JOB CAN RUN BITS IN SECOND CPU STATUS
	JRST	CPOPJ1##
	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##
IFE SP.CR0-1,<
	MOVE	P1,.CPBIT##	;BIT POSITION FOR CURRENT CPU
>
IFN SP.CR0-1,<
	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
;NOTE:  IF CALLED AT ANYRN WITH A HIGH SEG, ON EXIT P2 WILL CONTAIN
;THE ADDRESS OF THE HIGH SEG DATA BLOCK FOR THE JOB WHICH HAD THE
;SEGMENT IN QUESTION

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
	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
	HRRO	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
	TLZE	P2,-1		;CHECKING HIGH SEGS?
	TRNN	P2,-1		;YES, ARE THERE ANY?
	JRST	ANYRN2		;CHECKING JOBS OR NO HIGH SEGS
ANYR1A:	SKIPLE	.HBSGN(P2)	;SPY SEGMENT?
	CAIE	J,@.HBSGN(P2)	;NO, IS THIS THE SEGMENT IN QUESTION?
	CAIA			;SPY OR NO MATCH, CHECK NEXT HIGH SEG THIS JOB
	JRST	ULKSCD		;A MATCH, GIVE NO SWAP RETURN
	HRRZ	P2,.HBLNK(P2)	;POINT TO NEXT HIGH SEG
	JUMPN	P2,ANYR1A	;CONTINUE IF ANOTHER HIGH SEG
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,(CR.RMV!CR.DET!CR.SPD)
	JRST	CP1CH6
	MOVE	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
	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
	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
	JRST	CP1CH5		;OPR HAS BEEN TOLD, DON'T COMPLAIN AGAIN ABOUT THIS CPU
CP1CH4:	SOJG	J,CP1CH2	;LOOP OVER ALL CPUS
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
;
RWRTCH::MOVEI	T3,SP.CR0	;BIT FOR CPU0
	LSH	T3,@.CPCPN##-.CPCDB##(P1) ;MASK FOR THIS CPU'S BITS
	TRC	T3,SP.CPA	;MAKE "ANYWHERE BUT HERE" MASK
	LDB	T4,MPYNRN##	;JOB'S NOT RUNNABLE BITS
	CAIN	T3,(T4)		;RUNNABLE ONLY HERE?
	POPJ	P,		;YES, MUST ZAP IT

IFN FTKL10,<
	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,DEDUNI	;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
;PRESERVES T4

DEDUNI:	MOVE	T1,UDBCAM(T2)	;CPU THIS UNIT LIVES ON
	CAME	T1,.CPBIT##-.CPCDB##(P1);ONLY THIS CPU?
	POPJ	P,		;NO, OTHERS AS WELL
IFN FTDUAL,<
	SKIPN	T3,UNI2ND(T2)	;SECOND PORT, IS THERE ONE?
	JRST	CPOPJ1##	;NO
	MOVE	T3,UDBCAM(T3)	;SECOND PORT CPU
	CAME	T3,.CPBIT##-.CPCDB##(P1) ;STILL GET AT THE SECONDARY PORT?
	POPJ	P,		;YES
>
	JRST	CPOPJ1##	;NO PATH TO THE UNIT
;COMMAND LEVEL ENTRY POINT TO ATTACH A CPU - CPU NUMBER IN T1

CPUATT::IMULI	T1,.CPLEN##	;OFFSET TO CPU CDB
	ADDI	T1,.C0CDB##	;ADDRESS OF CDB
	SKIPG	.CPOK##-.CPCDB##(T1) ;THIS CPU OK?
	JRST	[MOVEI	T1,[ASCIZ /CPU is already running/]
		 PJRST	ERRCRL##] ;REPORT ERROR AND RETURN
	MOVE	S,.CPCPN##-.CPCDB##(T1) ;COPY CPU NUMBER
	JSP	T2,SAVCTX##	;START THE MONITOR JOB
	MOVE	T1,S		;GET CPU NUMBER BACK
	PUSHJ	P,CPUATU	;ATTACH VIA COMMON CODE
	  CAIA			;ERROR RETURN
	POPJ	P,		;GOOD RETURN - ALL DONE
	JSP	T1,ERRCRL##	;GIVE ERROR
	ASCIZ	/ATTACH CPU failed/

;UUO LEVEL ENTRY POINT TO ATTACH A CPU - CPU NUMBER IN T1

CPUATU:	PUSHJ	P,SAVE2##	;FREE UP AN AC OR TWO
	MOVE	P1,T1		;COPY CDB NUMBER SOMEWHERE SEMI-PERMANENT
	IMULI	P1,.CPLEN##	;OFFSET TO CPU CDB
	ADDI	P1,.C0CDB##	;ADDRESS OF CDB
	MOVSI	T1,(CR.RMV!CR.DET) ;REMOVED AND DETACHED BITS
	TDNN	T1,.CPRUN##-.CPCDB##(P1) ;IS THE CPU DETACHED?
	JRST	DIAAAF##	;NO--ATTACH FAILURE
	PUSHJ	P,RECROM##	;RUN ONLY ME
	  JRST	DIAAAF##	;ATTACH FAILURE
	PUSHJ	P,RCNIOW##	;WAIT FOR I/O TO STOP
	SETZ	T2,		;INDICATE TO UNCACHE
	PUSHJ	P,SETCSB	;CLEAR TEMPORARILY CACHED PAGES
	  JFCL			;IGNORE ERROR
	AOS	NCPRUN##	;INCREMENT NUMBER OF "RUNNABLE" CPUS
IFN FTKL10,<
	MOVE	T1,[JRST SYSTOP##] ;MAKE WARM RESTART
	MOVEM	T1,WRSINS	; DO A JUMP 407
> ;END IFN FTKL10
	MOVSI	T1,(CR.ATO)	;DRIVERS MUST WAIT FOR AUTCON TO RUN
	IORM	T1,.CPRUN##-.CPCDB##(P1)
	MOVSI	T1,(CR.RMV!CR.DET!CR.NRN) ;GET BITS WHICH PREVENT RUNNING
	ANDCAM	T1,.CPRUN##-.CPCDB##(P1) ;ALLOW THE CPU TO START
	MOVEI	P2,^D10		;DON'T LET THE SYSTEM IDLE FOREVER
CPUAT1:	MOVE	T1,.CPCPN##-.CPCDB##(P1) ;GET CPU NUMBER
	PUSHJ	P,ONCPUS	;TRY TO RESCHEDULE THIS JOB ON CPUN
	  JRST	[MOVEI T1,1	;FAILED, SLEEP FOR A SECOND
		 PUSHJ P,SLEEP##
		 SOJG  P2,CPUAT1 ;TRY AGAIN IF TIME IS LEFT
		 JRST  CPUAT2]	;CPUN DIDN'T START
IFN FTKL10,<
	MOVEI	T1,1		;WAIT FOR SETSPD TASK TO RUN AND
	PUSHJ	P,SLEEP##	; CFE TO SETTLE DOWN
>; END IFN FTKL10
	PUSHJ	P,AUTCPU##	;CONFIGURE DEVICES WHILE SYSTEM IS IDLE
	PUSHJ	P,RECCRM##	;CLEAR "RUN ONLY ME"
	XMOVEI	T2,CPATBL	;POINT TO TRANSFER TABLE
	AOS	(P)		;SKIP RETURN FOR SUCCESS
	PJRST	CPUCSC		;LOG ATTACH AND RETURN

;CPUN DIDN'T START IN THE ALLOTTED TIME.  "LOGICALLY" DETACH IT
;(UNDO THE WORK WE JUST DID) AND LET THE OPERATOR TRY AGAIN AT
;A MORE OPPORTUNE TIME.

CPUAT2:	PUSHJ	P,CPUDXX	;DETACH CPUN AND UNDO THE WORK WE DID
	PUSHJ	P,RECCRM##	;CLEAR "RUN ONLY ME"
	JRST	DIAAAF##	;NOTIFY CALLER OF FAILURE
;HERE FROM COMMAND TO DETACH A CPU
;CALL WITH CPU NUMBER IN T1

CPUDET::MOVE	S,T1		;SAVE CPU NUMBER
	JSP	T2,SAVCTD##	;MUST BE AT UUO LEVEL TO REMOVE FILE STRUCTURES
	MOVE	T1,S		;GET COU NUMBER BEING REMOVED BACK
	PUSHJ	P,CPUDTU	;CALL THE UUO CODE
	  CAIA			;ERROR RETURN
	POPJ	P,		;GOOD RETURN - ALL DONE
	JSP	T1,ERRCRL##	;GIVE ERROR
	ASCIZ	/DETACH CPU failed/

;UUO CODE TO HANDLE DETACHING A CPU
;CALL WITH CPU NUMBER TO DETACH IN T1
;IT IS THE CALLER'S RESPONSIBILITY TO ENSURE THAT THE CPU'S OK WORD IS POSITIVE

CPUDTU:	PUSHJ	P,SAVE4##	;DODELE CLOBBERS P ACS
	MOVE	P1,T1		;CPU NUMBER
	IMULI	P1,.CPLEN##	;CDB OFFSET
	ADDI	P1,.C0CDB##	;FORM ADDRESS OF THE CDB
	SETZ	T1,		;PICK A CPU, ANY CPU
CPUDT1:	CAME	T1,.CPCPN##-.CPCDB##(P1) ;THE CPU DEING REMOVED?
	PUSHJ	P,ONCPUS	;NO, TRY TO GET ON IT
	  CAIA			;NOT RUNNING - TRY ANOTHER
	JRST	CPUDT2		;GOT THERE OK - CONTINUE
	CAIGE	T1,M.CPU##-1	;TRIED THEM ALL?
	AOJA	T1,CPUDT1	;NO, TRY NEXT
	JRST	DIAADF##	;YES, WE TRIED

CPUDT2:	PUSHJ	P,RECROM##	;RUN ONLY ME
	  JRST	DIAADF##	;DETACH FAILURE
IFN FTXMON,<PUSHJ P,SSEC0##>	;RUN IN SECTION ZERO
	SKIPG	.CPOK##-.CPCDB##(P1) ;IS THE CPU CURRENTLY RUNNING?
	JRST	CPUDT5		;YES, DON'T WORRY ABOUT I/O HANGING
	PUSH	P,F		;SAVE TTY DDB
	HLRZ	F,DEVLST##	;START OF DEVICE CHAIN
CPUDT3:	LDB	T1,PDVTIM##	;GET HUNG TIMER
	JUMPE	T1,CPUDT4	;IGNORE DEVICES WHICH CAN'T HANG
	PUSHJ	P,CHKCPI	;GET CPU ACCESSABILITY MASK
	  TRNA			;OK IF WE CAN'T DO THE I/O
	JRST	CPUDT4		;WE CAN DO THE I/O--FORGET THIS ONE
	TDNN	T1,.CPBIT##-.CPCDB##(P1) ;ACCESSIBLE ON DEAD CPU?
	JRST	CPUDT4		;NO--LOOK AT NEXT DEVICE
	MOVE	S,DEVIOS(F)	;I/O STATUS
	PUSHJ	P,CPUOK		;YES--CAN A LIVE CPU DO ITS I/O?
	  TRNN	S,IOACT		;NO--WAS I/O ACTIVE WHEN CPU STOPPED?
	JRST	CPUDT4		;NO,LOOK AT NEXT DEVICE
	PUSHJ	P,DEVERR##	;CLEAR IOACT/SET TROUBLE
CPUDT4:	HLRZ	F,DEVSER(F)	;NEXT DEVICE
	JUMPN	F,CPUDT3	;LOOP IF THERE IS ONE
	POP	P,F		;RESTORE TTY DDB AND CONTINUE
CPUDT5:	PUSHJ	P,RCNIOW##	;WAIT FOR I/O TO STOP
;NOW REMOVE INACCESSIBLE STRUCTURES
CPUDT6:	PUSHJ	P,DEDSTR	;SEE IF ANY STRS WHICH CANNOT BE ACCESSED
	  JRST	CPUDT7		;NONE OR FINISHED
	PUSHJ	P,RMVSTR##	;REMOVE THE INACCESSIBLE FILE STRUCTURE
	  JFCL			;MIGHT NOT SKIP
	JRST	CPUDT6		;NEXT STR

;HERE TO SEE THAT SWAPPING SPACE IS NOT ALLOCATED ON INACCESSIBLE UNITS
CPUDT7:	MOVEI	T4,0		;START AT FIRST ENTRY IN SWPTAB
CPUDT8:	SKIPLE	T2,SWPTAB##(T4)	;NEXT UNIT IN THE ACTIVE SWAPPING LIST
	PUSHJ	P,DEDUNI	;STILL ABLE TO GET AT THIS UNIT?
	  JRST	CPUDT9		;YES, LOOP ON
	MOVEI	P2,-1		;+ INFINITY
	MOVE	U,T2		;UNIT
	PUSH	P,T4		;DON'T COUNT ON DODELE PRESERRVING T4
	PUSH	P,P1		;OR P1 EITHER
	PUSHJ	P,[PUSHJ P,GGVMM ;OBTAIN MM INTERLOCK
		   JRST DODELE##] ;REMOVE DORMANT SEGMENTS FROM UNREACHABLE UNIT
	  JFCL			;SHOULD NEVER SKIP
	POP	P,P1
	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
	SETOM	SWPTAB##(T4)	;FLAG UNIT DEAD
CPUDT9:	CAIGE	T4,SWPMAX##	;LOOKED AT ALL THE UNITS IN THE A.S.L.?
	AOJA	T4,CPUDT8	;NO, LOOK AT THE NEXT UNIT

;NOW DETACH ALL DISK PORTS CONNECTED TO THE CPU BEING REMOVED
; NECESSARY SO THAT PULSAR WILL KNOW THEY HAVE GONE AWAY
	MOVEI	J,KDBTAB##+.TYDSK-KDBNXT ;START AT FIRST KONTROLLER
CPUD10:	SKIPN	J,KDBNXT(J)	;NEXT KONTROLLER
	JRST	CPUD12		;GO IF DONE
	MOVE	T1,KDBCAM(J)	;CPU(S) OWNING THIS KONTROLLER
	CAME	T1,.CPBIT##-.CPCDB##(P1) ;SAME AS THE CPU BEING DETACHED?
	JRST	CPUD10		;NO, LOOK AT THE NEXT KONTROLLER
	MOVE	T1,KDBIUN(J)	;AOBJN POINTER TO UNIT TABLE
	SKIPN	U,(T1)		;A UNIT THERE?
CPUD11:	AOBJN	T1,.-1		;NO, LOOK AT THE NEXT SLOT
	JUMPGE	T1,CPUD10	;GO IF ALL UNITS ON THE KONTROLLER HAVE BEEN LOOKED AT
	PUSH	P,T1		;SAVE AOBJN POINTER
	SKIPGE	UNI2ND(U)	;IS THIS THE ALTERNATE PORT?
	TLO	U,400000	;YES, DETDSK WANTS U NEGATIVE TO INDICATE THAT
	PUSHJ	P,DETCPD##	;DETACH THE UNIT
	  JFCL			;WE TRIED
	  JFCL			;IGNORE ERROR
	POP	P,T1		;RESTORE AOBJN POINTER
	MOVE	J,UDBKDB(U)	;DETCPD MAY CLOBBER J
	JRST	CPUD11		;SEE IF ANY UNITS LEFT ON THIS KONTROLLER
CPUD12:	MOVE	T2,[CPUDCL,,1]	;WHERE TO GO,,IN 1 TICK
	SYSPIF			;'CAUSE 2-WORD ENTRIES
	IDPB	T2,CLOCK##	;STORE TIMER + ROUTINE
	IDPB	P1,CLOCK##	;AND CDB + STATUS
	SETOM	CLKNEW##	;NEW SYSTEM CLOCK QUEUE ENTRY
	SYSPIN			;ALLOW OTHERS AGAIN
	PUSHJ	P,CPUDXX	;DO THE WORK OF DETACHING THE CPU
	PUSHJ	P,RECCRM##	;CLEAR "RUN ONLY ME"
	XMOVEI	T2,CPDTBL	;POINT TO TRANSFER TABLE
	PUSHJ	P,CPUCSC	;LOG DETACH
	AOS	(P)		;SKIP FOR GOOD RETURN
	MOVE	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
/

;HERE TO LOGICALLY DETACH A CPU

CPUDXX:	MOVSI	T1,(CR.RMV!CR.DET!CR.NRN) ;GET BITS WHICH PREVENT RUNNING
	IORM	T1,.CPRUN##-.CPCDB##(P1) ;INDICATE CPU IS REMOVED
	MOVEI	T1,1		;SLEEP FOR A SECOND TO LET CPUN HIT RMVCPU
	PUSHJ	P,SLEEP##	;WAIT FOR DETACH TO HAPPEN
	SOS	NCPRUN##	;DECREMENT NUMBER OF "RUNNABLE" CPUS
	MOVEI	T2,1		;SET TO CACHE THE LOW SEG (IF DOWN TO 1 CPU)
	PUSHJ	P,SETCSB	;TWIDDLE CACHE BITS AS APPROPRIATE
	  JFCL			;IGNORE ERROR (MORE THAN 1 CPU LEFT RUNNING)
	POPJ	P,		;RETURN

;HERE TO DO CLOCK-LEVEL THINGS FOR DETACHING A CPU

CPUDCL:	MOVE	P1,T1		;GET SAVED CDB ADDRESS
	PUSH	P,J		;SAVE JOB NUMBER
	MOVE	J,HIGHJB##	;HIGHEST JOB ON THE SYSTEM
CPUDC1:	PUSHJ	P,RWRTCH	;JOB RUNNABLE W.R.T. THE CACHE ON THE DEAD CPU?
	  CAIA			;YES
	PUSHJ	P,CPUZAP	;NO, ZAP THE JOB
	SOJG	J,CPUDC1	;LOOP OVER ALL JOBS
	POP	P,J		;RESTORE JOB NUMBER
	SETZM	.CPJOB##-.CPCDB##(P1) ;ZERO CURRENT JOB ON DEAD CPU
	POPJ	P,		;RETURN - CPU REMOVED
;SUBROUTINE TO ZAP THE CURRENT JOB 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,ZAPZAP##	;BLOW AWAY HIS CORE IMAGE
	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
	MOVSI	T1,JERR		;DON'T ALLOW TRAPS
	IORM	T1,JBTSTS##(J)
	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:
;	XMOVEI	T1,TRANSFER TABLE
;	PUSHJ	P,CPUCSC
;	ALWAYS	RETURN HERE

CPUCSC:	SETZ	T1,		;CAUSE SEB ALLOCATION TO HAPPEN
	PUSHJ	P,XFRSEB##	;FILL AND QUEUE UP RECORD
	  JFCL			;NO CORE
	POPJ	P,		;RETURN


;CPU ONLINE TRANSFER TABLE FOR ERROR.SYS LOGGING
CPATBL:	SEBTBL	(.ERCSC,CPAEND,<EX.QUE!EX.AVL>)
	MOVE	.CPLOG##	;(R00) CPU NAME
	MOVE	.CPASN##	;(R01) ADDITIONAL INFO (SERIAL NUMBER)
	MOVSI	.CSCAT		;(R02) REASON CODE
CPAEND:!			;END OF TABLE

;CPU OFFLINE TRANSFER TABLE FOR ERROR.SYS LOGGING
CPDTBL:	SEBTBL	(.ERCSC,CPDEND,<EX.QUE!EX.AVL>)
	MOVE	.CPLOG##	;(R00) CPU NAME
	MOVE	.CPASN##	;(R01) ADDITIONAL INFO (SERIAL NUMBER)
	MOVSI	.CSCDT		;(R02) REASON CODE
CPDEND:!			;END OF TABLE
SUBTTL	INTERLOCK ROUTINES

	$CSUB			;MUST BE ADDRESSABLE BY ALL CODE SEGMENTS

;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::CONO	PI,SYSOFF##	;TURN PI SYSTEM OFF
	AOSE	.CPSCD##	;BUMP LEVEL OF INTERLOCK
	POPJ	P,		;JUST NESTING, STILL HAVE INTERLOCK
LOKSC1:	CONO	PI,SYSOFF##
	SKIPGE	SCDLOK##	;DON'T TRY IF NO CHANCE OF GETTING IT (TIES UP MEMORY)
	AOSE	SCDLOK##	;GET SYSTEM INTERLOCK
	AOSA	.CPLLC##	;COUNT INTERFERENCE
	AOSA	.CPRSI##	;GOT IT, FILL IN CPU ID
	JRST	[CONO	PI,SYSON##-PI.SC7 ;TURN PI'S ON EXCEPT FOR SCHEDULER
		 JRST	LOKSC1]	;TRY AGAIN
	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
	SETOM	INOSCD##	;TELL THE WORLD WE DON'T OWN IT
	EXCH	T1,SCDLOK##	;RELEASE SYSTEM INTERLOCK
	CONO	PI,SYSON##
	JUMPGE	T1,TPOPJ##	;ALL IS WELL IF INTERLOCK TAKEN
	STOPCD	TPOPJ##,DEBUG,SAU,	;++SCHEDULER ALREADY UNLOCKED
;ROUTINE TO CHECK OWNERSHIP OF THE SCHEDULER INTERLOCK
; RETURN CPOPJ IF OWNED, CPOPJ1 IF NOT
SCDOWN::PUSH	P,T1		;ITS TRADITIONAL NOT TO CLOBBER AC'S
	APRID	T1
	CAME	T1,INOSCD##	;OWN INTERLOCK?
	JRST	TPOPJ1##	;NO
	JRST	TPOPJ##		;YES
;ROUTINE TO OBTAIN THE DIE INTERLOCK

LOKDIE::AOSE	.CPDIE##	;OWN INTERLOCK?
	POPJ	P,		;JUST NESTING, RETURN
	SKIPGE	INTDIE##	;INTERLOCK AVAILABLE?
	AOSE	INTDIE##	;TRY TO GET IT
	JRST	.-2		;NOT AVAILABLE
	APRID	INODIE##	;REMEMBER WHO OWNS IT
	SETOM	DIEFLG##	;FLAG A STOPCODE IS HAPPENING
	POPJ	P,		;RETURN

;ROUTINE TO RELEASE THE DIE INTERLOCK

ULKDIE::PUSH	P,T1		;SAVE AN AC
	SOSL	T1,.CPDIE##	;COUNT DOWN A LEVEL OF NESTING
	JRST	TPOPJ##		;STILL NESTING, RETURN
	SETOM	INODIE##	;NO ONE OWNS IT NOW
	SETZM	DIEFLG##	;CLEAR STOPCODE IN PROGRESS FLAG
	EXCH	T1,INTDIE##	;RELEASE INTERLOCK
	JUMPGE	T1,TPOPJ##	;OK IF WAS LOCKED PREVIOUSLY
	STOPCD	TPOPJ##,DEBUG,DAU, ;++DIE ALREADY UNLOCKED

;ROUTINE TO BREAK THE DIE INTERLOCK

BRKDIE::SETOM	INODIE##	;NO ONE OWNS IT NOW
	SETZM	DIEFLG##	;CLEAR STOPCODE IN PROGRESS FLAG
	SETOM	INTDIE##	;RELEASE INTERLOCK
	POPJ	P,		;RETURN
;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
	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::	SKIPE	DINITF##	;ONCE-ONLY?
	JRST	CPOPJ1##	;YES, JOB OWNS THE MM
	PUSH	P,J		;SAVE J
	MOVE	J,INTLMO##	;OWNER OF THE MM RESOURCE IF OWNED BY A CPU
	ANDI	J,ID.PSN	;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 SO
; 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,.USBTS	;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,.USBTS	;DID THE JOB OWN IT?
	PUSHJ	P,UPMM		;YES, GET IT BACK
	ANDCAM	T1,.USBTS	;CLEAR THE BIT
	JRST	TPOPJ##		;AND RETURN

;COROUTINE TO SAVE AND RESTORE THE STATE OF THE MM RESOURCE
REMMM::	PUSHJ	P,MMOWN		;OWN MM?
	  POPJ	P,		;NO, NOTHING TO DO THEN
	PUSHJ	P,DWNMM		;GIVE UP MM NOW
	POP	P,(P)		;POP CALLER'S RETURN PC
	PUSHJ	P,@1(P)		;RETURN TO CALLER
	  CAIA			;NON- SKIP
	AOS	(P)		;SKIP
	JRST	UPMM		;RESTORE MM

;COROUTINE TO GET THE MM RESOURCE AND GIVE IT UP ON RETURN
GGVMM::	SKIPE	DINITF##	;ONCE-ONLY?
	POPJ	P,		;YES, AN ELABORATE NO-OP
	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

	$HIGH
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::SKIPE	J,.CPSTS##	;DID ANYONE STOP TIME SHARING?
	JRST	MRNBL1		;YES, SEE IF HE IS RUNNABLE
	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
IFE SP.CP0-1,<
	MOVE	T1,.CPBIT##	;BIT MASK FOR THIS CPU
>
IFN SP.CP0-1,<
	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
	HRRZ	F,JBTSGN##(J)	;GET HIGH SEG NO., SPY OR NONE?
	JUMPE	F,DXRUN2	;NO HIGH SEG OR SPY HI SEG - GIVE CAN RUN RETURN
	MOVSI	T2,SWP!SHF!JXPN	;BITS TO CHECK
DXRN1A:	SKIPLE	T1,.HBSGN(F)	;GET SEGMENT WORD, SPY SEG?
	TDNN	T2,JBTSTS##(T1)	;NO, HIGH SEG BEING MUNGED?
	CAIA			;SPY OR BEING MUNGED, CHECK NEXT HIGH SEG
	JRST	DXRUN3		;BEING MANGLED SOMEWHERE, FLAG LOST TIME
	HRRZ	F,.HBLNK(F)	;NEXT SEGMENT
	JUMPN	F,DXRN1A	;CHECK IF THERE IS ONE
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 CLEAR SP.CJn FOR JOB IN J (CURRENT CPU)

CLRSJ0::MOVSI	T1,(SP.CJ0)	;PICK UP RUN BIT FOR MASTER PROCESSOR
	LSH	T1,@.CPCPN##	;POSITION FOR THIS PROCESSOR
	ANDCAM	T1,JBTSPS##(J)	;TURN OFF RUNBIT FOR OLD JOB
	POPJ	P,

;SUBROUTINE TO SET SP.CJn FOR JOB IN .CPJOB (CURRENT CPU)

SETSJ0::MOVE	T2,.CPJOB##	;GET "CURRENT" JOB
	MOVSI	T1,(SP.CJ0)	;PICK UP RUN BIT FOR MASTER PROCESSOR
	LSH	T1,@.CPCPN##	;POSITION FOR THIS PROCESSOR
	IORM	T1,JBTSPS##(T2)	;TURN ON RUNBIT FOR "CURRENT" JOB
	POPJ	P,
;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
;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,.CPMAP##-.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,.CPMAP##-.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
>
;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
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


SBCCSH::PUSHJ	P,SBSCD		;GET SCHEDULAR INTERLOCK
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,.CPCSN##	;GET CURRENT SWEEP NUMBER
	PUSHJ	P,CHKNDS	;SEE IF A SWEEP IS NEEDED
	  POPJ	P,		;NO REQUESTED CACHE SWEEP THIS TICK
	CLRPGT			;CLEAR HARDWARE PAGE TABLE AS WELL SINCE EXEC
				; MAP MIGHT HAVE CHANGED AS WELL
	CAML	T1,.CPCSW##	;FORCED SWEEP BECAUSE MAPS CHANGED STILL NEED
				; TO BE DONE?
	SETZM	.CPCSW##	;NO, GET ANYONE WAITING FOR SWEEP TO GO AGAIN
	PUSHJ	P,CHKNDS	;STILL NEED A SWEEP?
	  POPJ	P,		;NO
	AOSA	.CPCRN##	;YES, EQUAL(HAPPENS) OR GREATER (NEVER HAPPENS)
				; INCREMENT COUNT OF SWEEPS DONE BY REQUEST
				;FALL INTO "CTXSWP", ALWAY SWEEP


;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 CHECK IF A SWEEP REQUESTED SWEEP MUST BE DONE. IF .CPCSW.NE.0,
; FORCES AT LEAST A CLRPGT. CALL WITH T1=CURRENT SWEEP NUMBER, SKIPS IF SWEEP
; MAY BE NEEDED

CHKNDS:	CAMLE	T1,.CPCSR##	;REQUESTED SWEEP NEEDED?
	SKIPE	.CPCSW##	;OR A FORCED SWEEP?
	AOS	(P)		;YES, SWEEP IS LIKELY TO BE NEEDED
	POPJ	P,		;RETURN
;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::PUSHJ	P,SBSCD		;INTERLOCK
	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)
;	MOVEI	T2,ADDR OF HIGH SEG DATA BLOCK FOR SEGMENT THAT CHANGED
;	PUSHJ	P,CLRCSH
;ALWAYS RETURN HERE (CACHE BITS CLEARED OR SET AS APPROPRIATE)

CLRCSH::SKIPLE	J,.HBSGN(T2)	;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	CLRCWL		;YES, SEE IF ANY OTHER JOB HAS IT WRITE ENABLED
CLRCWE:	JUMPE	T1,CPOPJ##	;NOTHING TO DO IF NOTHING CHANGED
	MOVEI	T1,SETNCH	;SUBROUTINE TO SET NO CACHE BITS
	HRRZS	J		;KEEP ONLY SEGMENT #
	PUSH	P,J		;SAVE J
	PUSHJ	P,HGHAPP##	;TURN ON NOCSH!REDOMP FOR ALL JOBS SHARING THIS SEGMENT
	POP	P,J		;RESTORE SEGMENT #
	PUSHJ	P,SAVE2##	;SAVE P1,P2 FOR ANYRN
CLRCS1:	MOVEI	P1,.C0CDB##	;FIRST CDB
CLRCS3:	PUSHJ	P,ANYRN		;SEE IF ANY OTHER RUNNING JOB IS USING THAT SEGMENT
	  SKIPA	T2,.CPJOB##-.CPCDB##(P1) ;YES, MUST WAIT; JOB NUMBER USING IT
	POPJ	P,		;NO, EVERYTHING IS OK
	MOVSI	T1,REDOMP	;HAS THE MAP BEEN REDONE FOR THE CPU1 JOB?
	TDNN	T1,.HBSGN(P2)	; ...
	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
	PUSH	P,J		;SAVE J
	MOVE	J,.CPJOB##	;OUR JOB NUMBER
	PUSHJ	P,SLEEP##	;WAIT A WHILE
	POP	P,J		;RESTORE J (SEGMENT #)
	JRST	CLRCS1		;AND TRY AGAIN

CLRCWL:	JUMPN	T1,CPOPJ##	;RETURN IF NOT CHANGING THE STATE OF UWP
	MOVEI	T1,CHKUWP	;SUBROUTINE TO CHECK IF ANY OTHER JOB HAS UWPOFF
	PUSH	P,J		;SAVE J
	PUSHJ	P,HGHAPP##	;ANY OTHER JOB HAVE UWP OFF FOR THIS SEGMENT?
	JUMPL	J,JPOPJ##	;EXIT IF SOME JOB HAS UWP OFF
	POP	P,J		;GET HISEG AGAIN
	MOVEI	T1,CLRNCH	;CLEAR NOCSH FOR ALL JOBS SHARING THIS SEGMENT
	PJRST	HGHAPP##	;CAUSE MAPS TO BE REDONE WITH CACHE ON

SETNCH:	MOVSI	T1,REDOMP!NOCSH	;DON'T CACHE THE HIGH SEG, CAUSE MAP TO BE REDONE
	IORM	T1,.HBSGN(P2)	;SET BITS IN SEGMENT DATA BLOCK
	TLZ	T1,NOCSH	;AND NOCSH BIT
	IORM	T1,JBTSGN##(J)
	POPJ	P,

CHKUWP:	MOVSI	T1,UWPOFF	;IS USER WRITE PROTECT OFF?
	TDNE	T1,.HBSGN(P2)
	SETZ	J,		;YES, FLAG IT AND STOP SCAN
	POPJ	P,

CLRNCH:	MOVSI	T1,NOCSH	;DON'T CACHE IT
	ANDCAM	T1,.HBSGN(P2)	;CLEAR NO CACHE BIT
	MOVSI	T1,REDOMP	;REDO THE MAP BIT
	IORM	T1,.HBSGN(P2)	;FLAG IT'S THIS SEGMENT
	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 (SEGMENT MUST HAVE BEEN JUST ATTACHED TO FOR JOB,
;I.E. FIRSTHIGH SEG DATA BLOCK IN CHAIN)

CHKSWS::MOVE	J,.CPJOB##	;CURRENT JOB
	HRRZ	T2,JBTSGN##(J)	;GET ADDR OF SEGMENT DATA BLOCK
	SKIPLE	J,.HBSGN(T2)	;GET SEGMENT #
	TLNN	J,SHRSEG	; AND IS IT SHARABLE?
	POPJ	P,		;NO, NOTHING TO DO
	MOVEI	T1,CHKUWP	;ROUTINE TO LOOK FOR UWPOFF
	PUSHJ	P,HGHAPP##	;SET IF ANY JOB HAS UWPOFF FOR THIS SEGMENT
	JUMPGE	J,CPOPJ##	;IF SCAN COMPLETED, NO JOB HAS UWP OFF
	MOVE	J,.CPJOB##	;JOB NUMBER IN QUESTION
	MOVSI	T1,REDOMP!NOCSH	;FORCE MAP TO BE REDONE UNCACHED
	IORM	T1,.HBSGN(T2)	;...
	TLZ	T1,NOCSH	;FORCE MAP WITH NO CACHE
	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 .CPMAP
;	LDB	T3,T2
;	TDZE	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
	SE1ENT			;MUST BE IN SECTION 1 TO REFERENCE PAGTAB
	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
		 SSX	P3,MS.MEM	;PAGTAB SECTION
		 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,.CPMAP##	;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 STORE A PAGE IN A MAP AND INSURE THAT ALL CPU'S SEE THE NEW MAPPING
STMPEC::SKIPA	P4,.CPEPT##-.CPCDB##(P1) ;ADDRESS OF THIS CPU'S EPT

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

UNCACC:	MOVE	P4,.CPMAP##-.CPCDB##(P1) ;ADDRESS OF THIS CPU'S EXEC MAP
	DPB	P3,P2		;STORE MAPPING IN THIS CPU'S MAP (PM.CSH OFF)
	SKIPLE	ONCCOM##	;IF IN ONCE ONLY
	PUSHJ	P,UUOLVL##	;OR AT UUO LEVEL,
	  POPJ	P,		;DON'T WAIT FOR OTHER CPUS
	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,.CPCSW##-.CPCDB##(P1) ;REQUEST THAT CPU TO SWEEP
	MOVSI	T1,-1		;MAKE HIM NON-CONTRL-CABLE
	ADDM	T1,JBTCCC##(J)	;..
UNCAC1:	SKIPGE	.CPOK##-.CPCDB##(P1) ;DON'T WORRY ABOUT CPUS THAT AREN'T RUNNING
	SKIPN	.CPCSW##-.CPCDB##(P1) ;HAS HE SWEPT YET?
	JRST	DECCCC##	;LET HIM ^C NOW
	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 EIGHT CACHED PAGES
;MUST BE CALLED IN SECTION 1 IN A KL PAGING MONITOR FOR THE REFERENCE
; TO PAGTAB

FIXOTB:	PUSHJ	P,SAVE4##	;ONLY REASON THIS IS A SUBROUTINE
	MOVE	P1,[POINT 36,MONORG##/PAGSIZ]

	ADD	P1,.CPMAP##	;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,10		;NEED 8 CACHED PAGES
FIXOT1:	ADDI	P2,PAGSIZ	;NEXT VIRTUAL ADDRESS
	CAML	P2,MONVFF##	;RUN OUT OF MONITOR PAGES?
	STOPCD	CPOPJ##,JOB,N8C,;++NOT 8 CACHED PAGES
	ILDB	T1,P1		;NEXT PAGE
	TDNN	T1,[PM.CSH]	;IS IT CACHED?
	JRST	FIXOT1		;NO, CAN'T USE IT
	ANDI	T1,17777	;JUST THE PHYSICAL PAGE NUMBER
	SSX	T1,MS.MEM	;PAGTAB SECTION
	MOVE	T2,PAGTAB(T1)	;PAGTAB ENTRY
	TLNN	T2,TNCSHB	;DON'T USE THIS PAGE IF IT MIGHT GET UNCACHED
	CAIN	P3,(T1)		;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 8 CACHED PAGES
	POPJ	P,		;ALL DONE, RETURN
;ROUTINE TO HANDLE SETUUO #40 (.STCSB)
; SET/CLEAR CACHE BITS FOR TEMPORARILY UNCACHED PAGED

SETCSB::PUSHJ	P,SAVE3##	;SAVE P1,P2,P3
	SE1ENT			;MUST BE IN SECTION 1 TO REFERENCE PAGTAB AND CALL FIXOTB
	MOVE	P1,NCPRUN##	;NAME OF RUNNABLE CPU'S
	SOJG	P1,ECOD0##	;FAIL IF MORE THAN 1
	HRRZ	P1,.CPMAP##	;ADDRESS OF THE SECTION ZERO MAP
	MOVE	T4,[POINT 36,(P1)] ;BYTE POINTER TO IT
	MOVE	P2,SYSSIZ##	;LAST ADDRESS
	LSH	P2,W2PLSH	;P2=# OF PAGES TO DO
	PUSHJ	P,SETCS1	;SET/CLEAR CACHE FOR THE LOW SEGMENT
	MOVE	T4,[POINT 36,MONORG/PAGSIZ(P1)]
	MOVE	P2,MONVFF##	;LAST LOCATION OF DATA BEYOND HIGH SEGMENT
	SUBI	P2,MONORG	;NUMBER OF WORDS ALLOCATED
	LSH	P2,W2PLSH	;CONVERT TO PAGES OF MONITOR HIGHSEG
	PUSHJ	P,SETCS1	;CLEAR/SET CACHE BITS FOR LDB'S, ETC...
	MOVE	T4,[POINT 36,.EVCDB##/PAGSIZ(P1)]
	MOVEI	P2,CDBPGS##	;NUMBER OF CDB PAGES
	PUSHJ	P,SETCS1	;SET/CLEAR CACHE BIT IN POINTERS MAPPING THE CDB
	MOVE	P3,.CPEPT##	;OUR EPT
	ADDI	P3,<(MS.FMD)>+1	;START AT SECTION 3 (SKIP EXTENDED HIGH SEGMENT)
SETCB1:	SKIPE	T1,SECTAB(P3)	;A SECTION MAP?
	TLNN	T1,(PM.CSH)	;YES, ALLOW FOR AN UNCACHED SECTION
	JRST	SETCB2		;SKIP THIS SECTION
	MOVE	T3,.CPMAP##	;MAKE MAP ADDRESSABLE
	MOVEM	T1,.EUPMP/PAGSIZ(T3)
	CLRPT	.EUPMP
	MOVE	T4,[POINT 36,.EUPMP]
	MOVEI	P2,^D512	;ALL THE PAGES IN THE SECTION
	MOVE	T1,.CPEPT##	;ADDRESS OF OUT EPT
	CAIN	P3,MXSECN(T1)	;SECTION 37?
	JRST	[ADDI T4,UMAPS/PAGSIZ ;YES, SECTION 37 IS SPECIAL
		 MOVEI P2,PAGSIZ-UMAPS/PAGSIZ ;NUMBER OF PAGES MAPPED
		 JRST .+1]	;IN SECTION 37
	PUSHJ	P,SETCS1	;SET/CLEAR BITS
SETCB2:	MOVE	T1,.CPEPT##
	CAIGE	P3,MXSECN(T1)	;LOOKED AT EVERY SECTION?
	AOJA	P3,SETCB1	;NO, LOOK ON
	CLRPGT			;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,SETCS3	;IGNORE IF NOT MAPPED
	LDB	T1,[POINT 3,T3,2] ;GET POINTER TYPE
	CAIN	T1,PM.ICD	;IF INDIRECT, JUST DO IT
	JRST	SETCS2		; ..
	HRRZ	T1,T3		;GET PHYSICAL PAGE #
	ANDI	T1,17777	;ISOLATE PAGE NUMBER
	SSX	T1,MS.MEM	;PAGTAB SECTION
	MOVE	T1,PAGTAB(T1)	;GET BITS FOR PAGE
	TLNN	T1,TNCSHB	;PAGE TEMPORARILY UNCACHED
	JRST	SETCS3		;NO, LEAVE IT ALONE
SETCS2:	TRNN	T2,1		;WANT CACHE ON OR OFF
	TDZA	T3,[PM.CSH]	;OFF, CLEAR BIT
	TDO	T3,[PM.CSH]	;ON, SET IT
	DPB	T3,T4		;PUT ENTRY BACK IN MAP
SETCS3:	SOJG	P2,SETCS1	;TRY NEXT PAGE
	POPJ	P,		;RETURN

>;END IFN FTKL10
;ROUTINE TO HANDLE SETUUO #52 (.STPCP)
; SET POLICY CPU

SETPCP::PUSHJ	P,PRVJ##	;PRIVILEGED?
	  SKIPA	T1,T2		;YES, PROCEDE - T1 = NEW POLICY CPU NUMBER
	JRST	SETCP0		;NO, ERROR RETURN
	CAMN	T2,BOOTCP##	;ALREADY THE BOOT CPU?
	JRST	CPOPJ1##	;OK, THAT'S ALL RIGHT
	CAIGE	T1,M.CPU##	;CPU NUMBER IN RANGE?
	PUSHJ	P,ONCPUS	;GET ON THAT CPU IF ITS RUNNING
	  JRST	SETCP4		;NOT A LEGAL CPU OR CPU ISN'T RUNNING
	SKIPE	MOFLPG##	;FLAG FREE?
	PUSHJ	P,DELAY1##	;NO, WAIT UNTIL IT IS
	HRROM	T1,MOFLPG##	;FLAG TO SAY CHANGING POLICY CPU TO US
	PUSHJ	P,CP1STP	;SEE IF ALL OTHER CPUS ARE IN THEIR ACS
	  PUSHJ	P,DELAY1##	;NO, GIVE THEM A CHANCE
	PUSHJ	P,BECOM0	;BECOME THE POLICY CPU
	SETZM	MOFLPG##	;LET EVERYONE ELSE RUN AGAIN
	JRST	CPOPJ1##	;TELL USER HE DID GOOD
SUBTTL	MULTIPROCESSING STOPCD PROCESSING

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

CHKDIE::SKIPGE	INTDIE##	;STILL LOCKED?
	POPJ	P,		;NO, IGNORE
	SKIPN	T1,DIEFLG##	;YES, DO WE KNOW WHY?
	JRST	CHKDIE		;NO, WAIT FOR REAL ENTRY OR EXIT FROM DIE
	JUMPG	T1,CPOPJ##	;YES, EXIT NOW IF MERELY AN EVENT
	CONO	PI,PI.OFF	;NO, STOPCD, 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
>; END IFN FTKL10
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	INTDIE##	;CPU FINISHED WITH STOPCD YET?
	JRST	CHKDI4		;YES
	SOJG	T1,CHKDI3	;NO, COUNT DOWN AND LOOP
	PUSHJ	P,BRKDIE	;TOO LONG, BREAK INTERLOCK
	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	INITIALIZE CPU'S MAP FROM BOOT CPU'S MAP

	$LOW

;SUBROUTINE TO INITIALIZE THIS CPU'S MAP FROM BOOT CPU'S MAP
;NOTE THAT MAPINI WILL NOT HANDLE THE CASE OF CHANGING THE PAGE
;USED FOR A CPU'S CDB, BUT ODDS ARE THE CPU WOULDN'T BE ABLE TO
;GET HERE IN THE FIRST PLACE SO WHO CARES?

MAPINI::SKPCPU	(1)		;WE THE BOOT CPU?
	POPJ	P,		;DO NOTHING
	PUSH	P,T1
	PUSH	P,T2
	PUSH	P,T3
	PUSH	P,T4

;FIRST COPY ALL DATA SECTION ENTRIES FROM THE BOOT CPU'S EPT.
;ALL DATA SECTIONS ARE IDENTICAL AMONG CPUS.
IFN FTXMON,<
	MOVE	T1,BOOTCP##	;CPU'S MAP TO COPY
	IMULI	T1,.CPLEN##	;OFFSET INTO ITS CDB
	MOVE	T1,.C0EPT##(T1)	;BOOT CPU'S EPT
	ADDI	T1,<(MS.FMD)>+1	;SKIP CODE SECTION POINTERS
	MOVE	T2,.CPEPT##	;OUR EPT
	ADDI	T2,<(MS.FMD)>+1	;SKIP CODE SECTION POINTERS
	MOVEI	T3,<(MS.FMD)>+1	;FIRST SECTION WE'LL COPY
MAPIN1:	SKIPE	T4,SECTAB(T1)	;SECTION POINTER FROM BOOT CPU'S MAP
	SKIPN	.CPKPM##	;MCA25 INSTALLED?
	TLZA	T4,(PM.KPM)	;NO, DON'T TURN ON "KEEP ME" IN SECTION POINTER
	TLO	T4,(PM.KPM)	;YES, TAKE ADVANTAGE OF IT
	MOVEM	T4,SECTAB(T2)	;STORE POINTER INTO OUR MAP
	AOS	T1		;NEXT SECTAB ENTRY
	AOS	T2		;...
	CAIGE	T3,MXSECN-1	;DONE EVERYTHING UP TO SECTION 37?
	AOJA	T3,MAPIN1	;NO, LOOP FOR REMAINDER

;FIXUP SECTION 37 MAPPING FOR PER-CPU CODE/DATA SECTIONS
	MOVE	T1,.CPEPT##	;POINT TO OUR EPT
	ADDI	T1,SECTAB	;OFFSET TO SECTION POINTERS
	HRLZS	T1		;PUT IN LH
	HRRI	T1,.CPMMA##	;MAKE A BLT POINTER
	BLT	T1,.CPMMA##+<(MS.FMD)> ;COPY POINTERS

;FIXUP SECTION 37 MAPPING FOR COMMON DATA SECTIONS
	MOVE	T1,BOOTCP##	;CPU'S MAP TO COPY
	IMULI	T1,.CPLEN##	;OFFSET INTO ITS CDB
	MOVSI	T2,.C0MMA##+<(MS.FMD)>+1(T1) ;BOOT CPU'S SECTION MAPPING WORDS
	HRRI	T2,.CPMMA##+<(MS.FMD)>+1 ;MAKE A BLT POINTER
	BLT	T2,.CPMMA##+MXSECN-1 ;COPY POINTERS

;SET UP MAPPING FOR CDB IN S2
	MOVE	T3,.CPEPT##	;GET ADDRESS OF EPT
	HRRZ	T3,SECTAB+<(MS.HGH)>(T3) ;GET PAGE NUMBER OF S2 MAP
	LSH	T3,P2WLSH	;MAKE IT AN ADDRESS
	MOVE	T4,.CPMAP##	;GET ADDRESS OF S0/S1 MAP
	MOVE	T1,.EVCDB##/PAGSIZ(T4) ;GET MAPPING FOR CDB
	HRLI	T3,-CDBPGS##	;NUMBER OF MAP ENTRIES TO COPY
	MOVEM	T1,.EVCDB##/PAGSIZ(T3) ;STORE IN S2 MAP
	AOBJP	T3,.+2		;QUIT WHEN ALL CDB MAPPING HAS BEEN COPIED
	AOJA	T1,.-2		;LOOP FOR ANOTHER
	CLRPGT			;MAKE NEW MAPPING VISIBLE
>; END IFN FTXMON

;NOW COPY MAPPING FOR NON-EVA CODE ADDRESSES FOR SECTIONS 0/1 AND 2
	MOVEI	T1,0		;SECTION MAP TO COPY
	PUSHJ	P,MAPINS	;DO IT
IFN FTXMON,<
	MOVEI	T1,(MS.HGH)	;SECTION MAP TO COPY
	PUSHJ	P,MAPINS	;DO IT
>; END IFN FTXMON

IFN FTXMON,<
> ;END IFN FTXMON
	POP	P,T4
	POP	P,T3
	POP	P,T2
	POP	P,T1
	POPJ	P,		;RETURN
;HELPER ROUTINE FOR MAPINI TO COPY A CODE SECTION MAP.
;CALLED WITH T1 CONTAINING THE SECTION NUMBER.

MAPINS:	MOVE	T3,.CPMAP##	;GET OUR MAP
	MOVE	T2,BOOTCP##	;BOOT CPU'S CPU NUMBER
	IMULI	T2,.CPLEN##	;OFFSET INTO ITS CDB
	MOVE	T2,.C0EPT##(T2)	;ADDRESS OF ITS EPT
	ADD	T2,T1		;OFFSET FOR DESIRED SECTION
	MOVE	T2,SECTAB(T2)	;GET SECTION TABLE ENTRY
	MOVEM	T2,.EUPMP/PAGSIZ(T3) ;BOOT CPU'S SECTION MAP ADDRESSABLE VIA .EUPMP
	CLRPT	.EUPMP		;MAKE NEW MAPPING VISIBLE
	ADD	T1,.CPEPT##	;OFFSET OUR EPT ADDRESS FOR DESIRED SECTION
	MOVE	T1,SECTAB(T1)	;GET SECTION TABLE ENTRY
	MOVEM	T1,.ECKSM/PAGSIZ(T3) ;OUR SECTION MAP ADDRESSABLE VIA .ECKSM
	CLRPT	.ECKSM		;MAKE NEW MAPPING VISIBLE
	MOVEI	T3,0		;STARTING AT THE BEGINNING OF THE MAP
MAPIS1:	MOVE	T1,.EUPMP(T3)	;GET ENTRY FROM BOOT CPU'S SECTION MAP
	LDB	T2,[POINT 3,T1,2] ;GET POINTER TYPE
	CAIE	T2,PM.NCD	;NO ACCESS OR
	CAIN	T2,PM.DCD	; DIRECT POINTER?
	JRST	MAPIS2		;YES, JUST STORE IT AS IS
	CAIE	T2,PM.ICD	;INDIRECT IS ONLY OTHER TYPE WE KNOW OF
	STOPCD	.,STOP,IPT,	;++ILLEGAL POINTER TYPE
	TRNN	T1,777770	;IS THIS AN SPTTAB INDEX?
	JRST	MAPIS2		;YES, NO ADJUSTMENT NEEDED
	MOVE	T2,.CPCPN##	;OUR CPU NUMBER
	HRRI	T1,SPTLOW##-SPTTAB##(T2) ;SPT INDEX
MAPIS2:	MOVEM	T1,.ECKSM(T3)	;STORE ENTRY IN OUR SECTION MAP
	CAIGE	T3,<HLCSAD##/PAGSIZ>-1 ;DONE ALL CODE ADDRESSES?
	AOJA	T3,MAPIS1	;NO, LOOP FOR NEXT ENTRY
	CLRPGT			;MAKE NEW MAPPING VISIBLE
	POPJ	P,		;RETURN
	SUBTTL	TELL OPERATOR CURRENT CPU'S NAME


;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
	SUBTTL	ASSUME ROLE OF THE BOOT CPU

;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
	SKIPL	DEBUGF##	;DEBUGGING?
	JRST	BECOM1		;NO
	MOVE	T1,DEBCPU##	;GET CPU WHICH MAY BECOME THE POLICY CPU
	CAME	T1,.CPCPN##	;SAME AS ME?
	POPJ	P,		;NOPE
BECOM1::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
	SE1ENT			;ENTER SECTION 1
	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
	MOVE	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?
	MOVEM	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"
	IMULI	T1,.CPLEN##	;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
	SKIPL	MOFLPG##	;DON'T BREAK LOCKS IF JUST CHANGING POLICY CPU
	PUSHJ	P,BRKLO2	;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,DATIME##	;ADD DATE/TIME
	PUSHJ	P,PCRLF##	;AND A CRLF
	TDZA	T1,T1		;CALLED AFTER STARTUP
BCOM0:: MOVEI	T1,1		;CALLED DURING STARTUP
	PUSH	P,T1		;REMEMBER HOW WE GOT HERE
	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
BECOM2:	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,BECOM2	;AND FIX IT UP
	POP	P,T1		;GET FLAG
	PJUMPE	T1,SETEDV##	;RESET EDV IF NOT SYSINI
	POPJ	P,		;RETURN
				;RESET EDV STUFF AND RETURN
	XLIST			;DUMPED LITERALS
	LIT
	LIST
	$HIGH			;BACK TO THE HIGH SEGMENT
	SUBTTL	BREAK SOFTWARE INTERLOCKS

;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
	SKIPL	DEBUGF##	;DEBUGGING?
	JRST	BRKLO1		;NO
	MOVE	T2,DEBCPU##	;GET ONLY CPU WHICH MAY BECOME POLICY
	CAME	T2,.CPCPN##	;IS IT ME?
	POPJ	P,		;NO
BRKLO1:	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
	SKIPN	MOFLPG##	;DON'T BREAK LOCKS IF SETTING MONITOR MEMORY OFF

	TDNE	T2,CPNDDT	;CPU AT A BREAKPOINT?
	POPJ	P,		;YES, DON'T BREAK INTERLOCKS
BRKLO2:	SETOM	.CPDRQ##-.CPCDB##(T1)	;INDICATE DISKS NEED RESCUING
	PUSHJ	P,BRKSI1	;BREAK THE SOFTWARE INTERLOCKS
	MOVE	T2,INODIE##	;CPU OWNING DIE INTERLOCK
	ANDI	T2,ID.PSN	;ISOLATE SERIAL NUMBER
	CAMN	T2,.CPASN##-.CPCDB##(T1) ;THIS CPU OWN THE DIE INTERLOCK?
	PUSHJ	P,BRKDIE	;YES, BREAK IT
IFN FTNET,<
	PUSHJ	P,BRKFEK##	;DECLARE THE FEKS DOWN
>
IFN FTKL10,<
	MOVE	T1,.CPCPN##-.CPCDB##(T1) ;GET NUMBER OF DYING CPU.
IFN FTENET,<
	PUSHJ	P,KNIDED##	;CLEAR KLNI ON DEAD CPU
>; END IFN FTENET
	S0PSHJ	KILDTE##	;CLEAR ALL DTE'S FOR THE DYING CPU
>; END IFN FTKL10

	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
	ANDI	T4,ID.PSN	;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
	ANDI	T4,ID.PSN	;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
	SUBTTL	WAIT WHILE MEMORY IF BEING SET OFF-LINE

;ROUTINE TO WAIT IN THE AC'S WHILE MEMORY IS BEING SET OFF-LINE
;BY THE BOOT CPU. ACS MUST BE EXEC ACS AND MUST BE USABLE WITHOUT SAVING THEM.
;CALL:	JRST	CP1MFL
;	RETURNS TO SAVPC2 IN CLOCK1

CP1MFL::CONO	PI,PI.OFF	;TURN OFF PI SYSTEM
IFN FTKL10,<
	PUSHJ	P,CP1MF1	;CALL ROUTINE TO WAIT
	JRST	CP1MF3		;AND JUMP AROUND CODE
CP1MF1:	SKIPL	MOFLPG##	;DON'T SAY DEC10 NO RUNNING IF JUST POLICY SWITCHING
	PUSHJ	P,SVPPC##	;SAVE PROTOCOL, ENTER SECONDARY
	PUSHJ	P,CSDMP##	;INSURE IT'S ALL IN CORE
>
	MOVEM	P,.CPCA1##+P	;SAVE P BEFORE LOADING AC LOOP
	MOVE	6,[MFLOOP,,2]
	BLT	6,6		;LOAD THE AC 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,.CPCA1##+P	;RESTORE P
	PUSHJ	P,MAPINI	;MAP MAY HAVE CHANGED
IFN FTKL10,<
	POPJ	P,
CP1MF3:
>
	MOVEI	17,SR.ACL	;NO LONGER IN
	ANDCAM	17,.CPSBR##	;  THE AC'S
	CONO	PI,PI.ON	;TURN PI SYSTEM BACK ON
	JRST	SAVPC2##	;RETURN

	$HIGH
	SUBTTL	WAIT WHILE A CPU IS AT A BREAKPOINT

;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,MAPINI	;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
	JRST	APRABK##	;RETURN TO APR INTERRUPT PROCESSING

	$HIGH
	SUBTTL	WAIT FOR SYSTEM RELOAD OR CPU RESTART

;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	12,[ACLOOP,,2]	;LOAD BLT POINTER TO AC LOOP
	BLT	12,12		;LOAD THE AC LOOP
	JRST	2		;JUMP INTO THE AC'S

	$LIT
	SUBTTL	WAIT LOOPS LOADED INTO AC'S

	$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
	JRST	SYSDSP##	;12 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

MFLOOP:	SKIPE	MOFLPG##	;2 MONITOR MEMORY SET OFF LINE YET?
	JRST	2		;3 NO, LOOP
	TDNE	17,.CPSBR##	;4 IF NOT STILL SET, WE RELOADED
	JRST	CP1MF2		;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	SPROIO##	;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 (.STCRN)

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,(CR.NRN)	;CLEAR SIGN BIT TO ALLOW CPU TO RUN
	TLO	T3,(CR.NRN)	;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
SETCP0:	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 T2,NCPRUN##] ;REMOVE, SET TO DECREMENT
	MOVE	T4,NCPRUN##	;HOW MANY CPUS ARE LEFT
	SOJLE	T4,ECOD0##	;CAN'T REMOVE THE LAST
	JRST	SETCD2		;JOIN COMMON PART
SETCD1:	MOVE	T3,[AOS T2,NCPRUN##] ;ADD, SET TO BUMP COUNT
	MOVSI	T4,(ST%LSC)	;LOW SEG CACHED BIT
	TDNN	T4,CNFST2##	;CACHE RIGHT FOR ADDING
SETCD2:	CAIL	T1,M.CPU##	;RANGE CHECK CPU NUMBER
	JRST	ECOD0##		;CACHE WRONG OR BAD CPU
	IMULI	T1,.CPLEN##	; 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
	JRST	ECOD0##		;CPU STILL RUNNABLE, IGNORE
	TRNN	T2,1B18		;ADDING OR REMOVING IT
	TLZA	T4,(CR.RMV)	;ADD, CLEAR "GO AWAY" BIT
	TLO	T4,(CR.RMV)	;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
IFN FTKL10,<
	CAIE	T2,1		;DOWN TO 1 CPU?
	SKIPA	T2,[JRST SYSTOP##] ;NO, HAVE WARM RESTART=J407
	MOVE	T2,[JRST APRWRS##] ;YES, WR SHOULD DO ALL
	MOVEM	T2,WRSINS	;STORE AS WHERE TO GO ON WR
>
	JRST	CPOPJ1##	; AND ALL DONE
SETCD3:	CAIL	T1,M.CPU##	;LEGAL CPU NUMBER?
	JRST	ECOD0##		;NO, ERROR RETURN
	IMULI	T1,.CPLEN##	;OFFSET INTO THE APPROPRIATE CDB
	MOVSI	T2,(CR.SPD)	;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

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
	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
	MOVE	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 CPUs 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
SUBTTL	DIAG. UUO INTERFACE


CPUDIA::EXP	MCSEC1+CPUPPR		;PREPROCESSOR ROUTINE
	DIAFNC	(SDS,CPUSDS,CPUSDS)	;SET DEVICE STATUS
	DIAFNC				;TERMINATE TABLE

CPUPPR:	JRST	(P3)		;GO PROCESS DIAG. UUO


;SET DEVICE STATUS
CPUSDS:	PUSHJ	P,GETWD1##	;GET NEXT ARGUMNET
	CAIL	T1,0		;RANGE
	CAILE	T1,SDSLEN	; CHECK
	JRST	DIAABA##	;BAD ARGUMENT LIST
	MOVE	T2,T1		;PUT IN A BETTER PLACE
	MOVEI	T1,(U)		;COPY CPU NUMBER
	PJRST	@SDSTAB(T2)	;GO SET STATUS

SDSTAB:	IFIW	DIAABA##	;SET IGNORE
	IFIW	DIAABA##	;CLEAR IGNORE
	IFIW	CPUDTU		;SET DETACHED
	IFIW	CPUATU		;SET ATTACHED
SDSLEN==.-SDSTAB		;LENGTH OF TABLE
	$LIT			;GENERATE LITERALS SO CAN CHECK ASCII OCTAL

CPNEND:	END