//MXPOW.BCPL

//Microprocessor power on/off overlay

get "mx.d"


//Power on/off is carried out by MicGo and MicStop.  Each of these creates
//a subsidiary menu with the items "processor", "memory", "both",
//"test-port-&-processor", and "test-memory-module."
//Port power is turned on when either memory or processor is turned on
//because neither one is operable with port power off.  If the memory was
//on or is turned on, a two or four quadrant configuration maximizing
//available memory is setup.  A string describing its actions is displayed.

//CONFIG in LDR controls memory configuration.  The right-most four
//nine-bit bytes in CONFIG represent quadrants 0-3 with the right-most eight
//bits of each byte representing the 8 possible 32-K memory modules in
//that quadrant.  A one in the bit enables that cabinet in that quadrant.
//The right-most bit is for module 0.  This arrangement of CONFIG was chosen
//so that the configuration is apparent when CONFIG is examined with Midas.

//MicGo and MicStop begin by creating actions for each of the four menu
//items and an abort item, and they destroy the actions before returning.

static [ ProcA; MemA; BothA; TestPA; TestMA; Module; PPVal
	ErrCA; SingleFA; ParityFA; DisFERA; DisZC; MemOnA
	ZCDisabled=0; SingleFatal=0; ParityFatal=0; ErrC=0
	FERDisabled=4000B
]

let MicGo(Flag) = valof
[	ProcA = CreateAction("Processor",lv ProcOn,0)
	MemA = CreateAction("Memory",lv MemOn,2)
	BothA = CreateAction("Both",lv MemOn,3)
	SingleFA = CreateAction("Make-SE-fatal",lv SinFatal,0)
	ParityFA = CreateAction("Make-PE-fatal",lv ParFatal,0)
	DisFERA = CreateAction("Disable-FER",lv DisFER,0)
	DisZC = CreateAction("No-core-zeroing",lv DisZeroCore,0)
	ErrCA = CreateAction("Disable-correction",lv ErrCOff,0)
	MemOnA = CreateAction("Do-it",lv MemOnNow,0)
	QuitF = -2
	resultis PowerTestActs(17B)

]


and MicStop() = valof
[	ProcA = CreateAction("Processor",lv PowOff,2)
	MemA = CreateAction("Memory",lv PowOff,3)
	BothA = CreateAction("Both",lv PowOff,0)
	QuitF = -1
	resultis PowerTestActs(0)
]


and PowerTestActs(Val) = valof
[	TestPA = CreateAction("Test-port-&-processor",lv PPowerTest,Val)
	TestMA = CreateAction("Test-memory-module",lv MPowerTest,Val)
	QUITact = CreateAction("Abort",lv PowAbort,0,0,$C-100B)
	resultis PowerMenu
]


and PowerMenu(S,Nix,MarkS) be
[	WsMarkA(QUITact)
	if (QuitF < 0) & (QuitF ge -2) do
	[ WsMarkA(ProcA); WsMarkA(MemA); WsMarkA(BothA)
	  WsMarkA(TestPA); WsMarkA(TestMA)
	]
	if QuitF eq -3 do
	[ WsMarkA(SingleFA); WsMarkA(ParityFA); WsMarkA(DisZC)
	  WsMarkA(DisFERA); WsMarkA(ErrCA); WsMarkA(MemOnA)
	]
]


and PowerExit(str) be
[	ReadAllRegs(); WssCSS(str)
	QuitCmdOverlay()
]


and PowAbort(S,garb,Buttons,arg) be
[	Resets(CmdCommentStream)
	test QuitF ge 0
	ifso [ RemoveFromEveryTimeList(QuitF); PowerExit("End of power test") ]
	ifnot [ PowerExit("XXX") ]
]


//Clear interrupt enables, dismiss interrupt in progress (if any), and
//clear F-register.  Used after power up.
and ResetM() be
[	XctMic(CLRARM); XctMic(INTRETN)
	XctL36(LDQ,table [ -1; -1; 170000B ] ); XctMic(LDSMF)
	XctL36(CLRFLQ,SMFORF); XctMic(LDSMF); XctL36(LDQ,Q)
]

and ZeroMem(MemX) be
[	let AVec = vec 1; AVec!0 = 0
	for I = 0 to MEMLEN!MemX do
	[ AVec!1 = I; PutMemData(MemX,AVec,table [ 0; 0; 0; 0; 0 ] ) ]
]


and ZeroLM() be
[	Wait(1); for I = 2 to 5 do ZeroMem(I)	//Zero IM, SM, DM, MP
	ZeroMem(10); ZeroMem(11)	//DM1 and DM2
]


//Delay is the maximum wait in multiple of 4 msec
and PPStatus(Delay) = valof
[	let StartTime = vec 2; Timer(StartTime); DoubleNeg(StartTime)
	while true do
	[ let TimedOut = (GetTime(StartTime)-Delay) ge 0
	  @ADREG = PPSTAT; let Status = @INREG & 3
	  if Status eq 3 then resultis 1
	  if TimedOut then switchon Status into
	  [
case 0:	WssCSS("Port and processor both off"); resultis 0
case 1:	WssCSS("Port power off"); resultis 0
case 2:	WssCSS("Port on, processor off "); resultis -1
	  ]
	]
]


//Turn on processor and port only (Port must be on to use processor)
and ProcOn(S,garb,Buttons,arg) be
[	@ADREG = PPPWR; @OUTREG = 3 
	test PPStatus(2500) le 0
	ifso PowerExit("")
	ifnot [ ResetM(); ZeroLM(); PowerExit("Port & processor on AOK") ]
]


//Turn on power supplies and configure memory.  PP is 3 for "both", 2 for
//"memory"
and MemOn(S,garb,Buttons,PP) be
[	PPVal = PP
	SingleFatal = 0		//Normal: single errors not fatal
	ParityFatal = 0		//Normal: parity errors not fatal
	ErrC = 0		//Normal: error correction enabled
	FERDisabled = 4000B	//Normal: FER given for fatal errors
	ZCDisabled = false	//Normal: Zero core
	QuitF = -3
	FormMenu(CmdMDFS,FormCmdmenuText)
]

and SinFatal(S,garb,Buttons,Zot) be SingleFatal = 252B
and ParFatal(S,garb,Buttons,Zot) be ParityFatal = 125B
and ErrCOff(S,garb,Buttons,Zot) be ErrC = 400B
and DisFER(S,garb,Buttons,Zot) be FERDisabled = 0
and DisZeroCore(S,garb,Buttons,Zot) be ZCDisabled = true

and MemOnNow(S,garb,Buttons,Zot) be
[	let LPC,CABSINUSE,Configuration = nil,0,0

//Unpack the memory configuration stuff from CONFIG into TMP
//Since CONFIG is an 80-bit memory word, have to work on the right 36 bits
	TMP!0 = (MEMCFG!2 lshift 5)+(MEMCFG!3 rshift 11)	//CONFIG[0,8] = J
	TMP!1 = MEMCFG!3 rshift 2				//CONFIG[9,17] = K
	TMP!2 = (MEMCFG!3 lshift 7)+(MEMCFG!4 rshift 9)	//CONFIG[18,26] = L
	TMP!3 = MEMCFG!4					//CONFIG[27,35] = M
//Setup the vector TMP1 with a count of the number of cabinets available
//in each quadrant in r.h. and the quadrant number in l.h.
	for I = 0 to 3 do
	[ LPC = (TMP!I)<<rh; CABSINUSE = CABSINUSE % LPC
	  TMP1!I = Countbits(LPC) + (I lshift 8)
	]

//Get largest number of good modules in TMP1!3 smallest in TMP1!0
	for I = 0 to 2 do
	[ for J = 0 to 2 do
	  [ if (TMP1!J)<<rh gr (TMP1!(J+1))<<rh do
	    [ LPC = TMP1!(J+1); TMP1!(J+1) = TMP1!J; TMP1!J = LPC ]
	  ]
	]

//Turn on the port, leaving the processor on if it's already on
	@ADREG = PPSTAT; LPC = @INREG
	@ADREG = PPPWR; @OUTREG = PPVal % LPC

//Turn on the memory cabinets in the configuration--first turn on the
//+5, -5, and +20 supplies.  Then when these are on turn on the +16 supply.
//CabinetsOn leaves everything off if anything goes wrong.
//Below 5000*4 msec = 20 sec max. wait for power up
	Resets(CmdCommentStream)
//Had trouble with relying on status to be correct so added this double
//check code.
	let PowOn = CabinetsOn(CABSINUSE,15B,17B,5000)
	if PowOn then
	[ Wait(1); PowOn = CabinetsOn(CABSINUSE,15B,17B,1000) ]
	if PowOn then PowOn = CabinetsOn(CABSINUSE,17B,17B,5000)
	if PowOn then
	[ Wait(1); PowOn = CabinetsOn(CABSINUSE,17B,17B,1000) ]
	if (not PowOn) % (PPStatus(2000) eq 0) then PowerExit("")
	@ADREG = RESR; @OUTREG = 0	//Catastrophic reset

//MAINsize will wind up holding the upper bound on the value of MADDRH
//for main memory references (i.e., the largest value of the top four bits
//of a main memory address
//In 4Q mode, use 4*TMP1!0 modules; in 2Q mode, use 2*TMP1!2 modules
	let Mem2Q = (TMP1!2)<<rh - 1
	if Mem2Q eq -1 then PowerExit("Insufficient memory in CONFIG to run")
	let Mem4Q = (TMP1>>rh lshift 1) - 1
	test Mem4Q ge Mem2Q
	ifso
	[ LPC = table [ 4; 0; 6; 2; 5; 1; 7; 3 ]
	  Puts(CmdCommentStream,$4)
	  MAINsize = Mem4Q
	]
	ifnot
	[ MAINsize = Mem2Q
	  Puts(CmdCommentStream,$2)
	  LPC = table [ 4; 5; 0; 1; 6; 7; 2; 3 ]
	  Configuration = table [
	    7;	//JJ (impossible)
	    1;	//JK
	    2;	//JL
	    3;	//JM
	    1;	//KJ
	    7;	//KK (impossible)
	    4;	//KL
	    5;	//KM
	    2;	//LJ
	    4;	//LK
	    7;	//LL (impossible)
	    6;	//LM
	    3;	//MJ
	    5;	//MK
	    6;	//ML
	    7;	//MM (impossible)
	  ] ! (((TMP1!2 rshift 6) & 14B) + (TMP1!3)<<lh)
	]
	WssCSS(" quadrants ")
	WssCSS(selecton MAINsize into
	[ case 0: "64K"
	  case 1: "128K"
	  case 2: "192K"
	  case 3: "256K"
	  case 4: "320K"
	  case 5: "384K"
	  case 6: "448K"
	  case 7: "512K"
	  case 9: "640K"
	  case 11: "768K"
	  case 13: "896K"
	  case 15: "1024K"
	  default: "???K"
	] )
	@ADREG = CONFR
	@OUTREG = FERDisabled % (Configuration lshift 8)
		% SingleFatal % ParityFatal
	@ADREG = 0
	for I = 0 to 3 do		//for all quadrants
	[ let GOODMOD = TMP!I; let TOP = 7; let BOTTOM = 0;
	  let LOGMOD = nil;
	  for N = 0 to 7 do		//for all cabinets
	  [ test(GOODMOD & 1) eq 0
	    ifso
//Module not in configuration--assign high address
	    [ LOGMOD = LPC!TOP; TOP = TOP-1 ]
	    ifnot
	    [ LOGMOD = LPC!BOTTOM; BOTTOM = BOTTOM+1 ]
	    GOODMOD = GOODMOD rshift 1
//Reverse quadrant bits for hardware
	    LOGMOD = ((I&1) lshift 1) + ((I&2) rshift 1) + (N lshift 2) + (LOGMOD lshift 5) + ErrC
//Note that @ADREG must be quickly zeroed below
	    @ADREG = PLMR; @OUTREGP = LOGMOD; @ADREG = 0
	  ]
	]
	if PPVal eq 3 then ZeroLM()
	if not ZCDisabled do
	[ let AVec = vec 1; AVec!0 = 0; AVec!1 = 0
	  until AVec!0 rshift 12 > MAINsize do
	  if ZeroCore(AVec,40000B) ne 0 do
	  [ @ADREG = RESR; @OUTREG = 125B; @ADREG = 0
	    PowerExit("Memory errors during core-zeroing")
	  ]
	]
	SwitchWss(" EC ",ErrC,"on","off")
	SwitchWss(" FER ",FERDisabled,"off","on")
	SwitchWss(" SE ",SingleFatal,"non-fatal","fatal")
	SwitchWss(" PE ",ParityFatal,"non-fatal","fatal")
	SwitchWss("",ZCDisabled," Memory 0'ed","")
	PowerExit("")
]


and SwitchWss(Str1,Stat,StrZ,StrNZ) be
[	Puts(CmdCommentStream,$ ); WssCSS(Str1)
	WssCSS(Stat eq 0 ? StrZ,StrNZ)
]


and ReportCab(Status) be
[	if (Status & 1) ne 0 do WssCSS(" +5")
	if (Status & 2) ne 0 do WssCSS(" +16")
	if (Status & 4) ne 0 do WssCSS(" +20")
	if (Status & 8) ne 0 do WssCSS(" -5")
]

//CabMask is a mask of the cabinets being turned on with B[15] being
//cabinet 0 and B[8] being cabinet 7.  SupNow is a mask of the supplies
//being turned on now, and SupFinally a mask of the supplies ultimately
//being turned on.  In each cabinet being turned on, if SupFinally are
//already on, they are left on, else only SupNow are put on.
//Error reports are made and power is turned off on any cabinets whose
//supplies fail to turn on within the timeout.  Delay*4 msec is the max.
//wait for supplies to reach new status.

and CabinetsOn(CabMask,SupNow,SupFinally,Delay) = valof
[	let J,On,EverythingOn = vec 8,nil,true
//The vector J winds up with a mask of the supplies which should be on
//for each cabinet
	for I = 0 to 7 do
	[ J!I = -1	//-1 indicates don't check
	  if (CabMask & 1) ne 0 do
	  [ @ADREG = CABST+I+I; On = @INREG & 17B; J!I = On
	    if (On ne SupNow) & (On ne SupFinally) do
	    [ EverythingOn = false
	      J!I = ((On % SupNow) eq SupFinally) ? SupFinally,SupNow
	      @ADREG = CABPWR+I+I
	      @OUTREG = J!I
	    ]
	  ]
	  CabMask = CabMask rshift 1
	]
	@ADREG = 0; if EverythingOn then resultis true
	let StartTime = vec 2; Timer(StartTime); DoubleNeg(StartTime)
	while true do
	[ let TimedOut = (GetTime(StartTime)-Delay) ge 0
	  EverythingOn = true
	  for I = 0 to 7 do
	  [ let ShouldBeOn = J!I; if ShouldBeOn ne -1 do
	    [ @ADREG = CABST+I+I; On = @INREG & 17B
	      ShouldBeOn = ShouldBeOn & not On
	      if ShouldBeOn ne 0 do
	      [ test TimedOut
	        ifso
	        [ WssCSS((EverythingOn ? "Power off on C",", C"))
	          EverythingOn = false; @ADREG = CABPWR+I+I; @OUTREG = 0
	          Puts(CmdCommentStream,60B+I); ReportCab(ShouldBeOn)
	        ]
	        ifnot [ EverythingOn = false; break ]
	      ]
	    ]
	  ]
	  if EverythingOn % TimedOut do
	  [ @ADREG = 0; resultis EverythingOn ]
	]
]


//Turn off processor.  Pstat is 0 for "both", 2 for "processor",
//3 for "memory"
and PowOff(S,garb,Buttons,Pstat) be
[	let DesiredPstat,NewPstat,Module,PowerOK = nil,nil,nil,true
	@ADREG = PPSTAT; DesiredPstat = @INREG & Pstat
	if Pstat ne 2 do
	[ for I = 0 to 7 do [ @ADREG = CABPWR+I+I; @OUTREG = 0 ] ]
	@ADREG = PPPWR; @OUTREG = DesiredPstat
	Wait(6)
	@ADREG = PPSTAT; NewPstat = @INREG & 3
	if NewPstat ne DesiredPstat do
	[ PowerOK = false
	  WssCSS(selecton (NewPstat & not DesiredPstat) into
	  [
case 1:	  "Processor stuck on"
case 2:	  "Port stuck on"
case 3:	  "Port & processor stuck on "
default: ""
	  ] )
	  WssCSS(selecton(DesiredPstat & not NewPstat) into
	  [
case 1:	  "Processor off erroneously"
case 2:	  "Port off erroneously"
case 3:	  "Port & processor off erroneously"
default: ""
	  ] )
	  UpdateDisplay()
	]
	if Pstat ne 2 do
	[ if not PowerOK do [ Wait(15); Resets(CmdCommentStream) ]
	  for I = 0 to 7 do
	  [ @ADREG = CABST+I+I; NewPstat = @INREG & 17B
	    if NewPstat ne 0 do
	    [ PowerOK = false; Puts(CmdCommentStream,$C)
	      Puts(CmdCommentStream,60B+I); ReportCab(NewPstat)
	      WssCSS(" stuck on ")
	    ]
	  ]
	]
	PowerExit((PowerOK ? "Power off AOK",""))
]

and PPowerTest(S,garb,Buttons,val) be
[	WssCSS("Repeating port & processor power ")
	WssCSS(selecton val into
	[
case 0:	"off"
case 17B: "on"
	] )
	QuitF = AddToEveryTimeList(PPTestLp,val)
	FormMenu(CmdMDFS,FormCmdmenuText)
]


and MPowerTest(S,garb,Buttons,val) be
[	WssCSS("Module number (0-7): ")
	UpdateDisplay()
	Module = Gets(keys)-60B
	if (Module ls 0)%(Module gr 7) do
	[ Resets(CmdCommentStream); PowerExit("XXX"); return
	]
	Resets(CmdCommentStream); WssCSS("Repeating power ")
	WssCSS(selecton val into
	[
case 0:	"off"
case 17B: "on"
	] )
	WssCSS(" on module ")
	Puts(CmdCommentStream,60B+Module)
	QuitF = AddToEveryTimeList(MPTestLp,val)
	FormMenu(CmdMDFS,FormCmdmenuText)
]


and PPTestLp(val) be
[	@ADREG = PPPWR; @OUTREG = val
]


and MPTestLp(val) be
[	@ADREG = CABPWR+Module+Module; @OUTREG = val
]


and Countbits(X) = valof
[	let N = 0
	while X ne 0 do [ if (X & 1) ne 0 then N = N+1; X = X rshift 1 ]
	resultis N
]


//Elapsed time returned as multiple of 4 msec
and GetTime(StartTime) = valof
[	let Now = vec 2; Timer(Now)
	DoubleAdd(Now,StartTime)
	resultis (Now!1 rshift 2)+(Now!0 lshift 14)+13
]