// Power.bcpl -- Turn on/off Maxc1 power supplies


external
[
// Outgoing procedures
Block

// Incoming procedures
InitNovaAlto; InitializeInterrupt; InitializeZone; GetFixed; Zero
Ws; Wl; PutTemplate; CallSwat
SetupReadParam; ReadParam
ExIO; ExSkip
SetTimer; TimerHasExpired; Dismiss

// Incoming statics
dsp
]

manifest
[
//Nova i/o opcodes
nioc = #060200; nios = #060100; niop = #060300
dia = #060400; diac = #060600; dias = #060500; diap = #060700
dib = #061400; dibc = #061600; dibs = #061500; dibp = #061700
dic = #062400; dicc = #062600; dics = #062500; dicp = #062700
doa = #061000; doac = #061200; doas = #061100; doap = #061300
dob = #062000; dobc = #062200; dobs = #062100; dobp = #062300
doc = #063000; docc = #063200; docs = #063100; docp = #063300
skpbn = #063400; skpbz = #063500; skpdn = #063600; skpdz = #063700

//Nova device codes
maint = #60	// Maintenance interface
mem = #34	// Memory interface
mem2 = #35	// Second device code for memory interface

// Commands for Maxc memory
memRead = #60000
memWrite = #120000
memSetBit = #160000

// Bits controlling power supplies
procPlus5 = #1
portPlus5 = #2
memPlus5 = #1
memPlus16 = #2
memPlus4 = #4
memMinus5 = #10
]

static
[
procPowerOut; procPowerIn; memPowerOut; memPowerIn
zone
]

// ----------------------------------------------------------------
let Power() be
// ----------------------------------------------------------------
[
// SMI addresses for power control input and output registers
// The first entry in each table is the number of following entries
procPowerOut = table [ 1; #100 ]  //also controls port
procPowerIn =  table [ 1; #101 ]
memPowerOut = table [ 3; #20; #33; #105 ]
memPowerIn =  table [ 3; #21; #32; #104 ]

zone = GetFixed(2000)
InitializeZone(zone,2000)
InitNovaAlto(zone)

// Arm memory interface interrupt
InitializeInterrupt(mem,MaxcMemInt)

// Read command line to find out what to do
let stringVec,switchVec = vec 127,vec 127
SetupReadParam()
if ReadParam(0,0,stringVec,switchVec,true) eq -1 then Error()
let doProc,doMem = false,false
test switchVec!0 eq 0
   ifso  // No switches means do both processor and memory
      [ doProc,doMem = true,true ]
   ifnot for i = 1 to switchVec!0 do
      switchon switchVec!i into
         [
         case $P: case $p: [ doProc = true; endcase ]
         case $M: case $m: [ doMem = true; endcase ]
         default: Error()
         ]
if stringVec!0 eq 2 & stringVec!1 eq $O & stringVec!2 eq $N then
   [  // "ON"
   ProcPortOn(doProc? procPlus5+portPlus5, portPlus5)
   if doMem then MemoriesOn()
   ResetMemErrors()
   finish
   ]
if stringVec!0 eq 3 & stringVec!1 eq $O & stringVec!2 eq $F &
 stringVec!3 eq $F then
   [  // "OFF"
   if doProc then ProcPortOff(doMem? procPlus5+portPlus5,procPlus5)
   if doMem then MemoriesOff()
   finish
   ]
Error()
]

// ----------------------------------------------------------------
and Error() be
// ----------------------------------------------------------------
[
Wl("Commands are:")
Wl("  POWER ON    -- Turns on processor and memories")
Wl("  POWER OFF   -- Turns off processor and memories")
Wl("  POWER ON/P  -- Turns on processor only")
Wl("  POWER ON/M  -- Turns on memories only")
Wl("  POWER OFF/P -- Turns off processor only")
Wl("  POWER OFF/M -- Turns off memories only")
finish
]

// ----------------------------------------------------------------
and ProcPortOn(supply) be
// ----------------------------------------------------------------
[
let v = vec 1
v!0 = 1; v!1 = supply
PowerOn(procPowerOut,procPowerIn,v,ProcPortOnFail)
]

// ----------------------------------------------------------------
and MemoriesOn() be
// ----------------------------------------------------------------
[
PowerOn(memPowerOut,memPowerIn,
   table
      [
      4
      memPlus5	// +5v
      memMinus5	// -5v
      memPlus4	// +4v
      memPlus16	// +16v
      ],
   MemOnFail)
ConfigureMemory()
ZeroMemory()
]


// ----------------------------------------------------------------
and ProcPortOff(supply) be
// ----------------------------------------------------------------
[
OutputSMI(procPowerOut!1,InputSMI(procPowerIn!1) & not supply)
]


// ----------------------------------------------------------------
and MemoriesOff() be
// ----------------------------------------------------------------
[
for i = 1 to memPowerOut!0 do if InputSMI(memPowerIn!i) ne 0 then
   OutputSMI(memPowerOut!i,#15)  // +16 supply off first, rest on
Dismiss(100)  // Wait 1 second
for i = 1 to memPowerOut!0 do
   OutputSMI(memPowerOut!i,0)  // Now kill remaining supplies
]

// ----------------------------------------------------------------
and PowerOn(outputList,inputList,sequence,FailProc) = valof
// ----------------------------------------------------------------
// Turn on power supplies.
// outputList = number of cabinets followed by list of
//  output SMI addresses
// inputList = number of cabinets followed by list of
//  input SMI addresses
// sequence = number of supplies in cabinet, followed by
//  list of bit values required to turn on each supply
// FailProc(cab,supply) is called upon failure to turn on a
//  supply (cab and supply are indices into outputList and
//  sequence respectively).
// returns true if all cabinets turned on successfully.
[
let timer = nil
SetTimer(lv timer,3000)  // 30 seconds maximum
let allOn,timedOut = false,false
until allOn % timedOut do
   [
   timedOut = TimerHasExpired(lv timer)
   allOn = true
   for i = 1 to inputList!0 do
      [  // Look for first turned-off supply in each cabinet
      let state = InputSMI(inputList!i)  // get current state
      for j = 1 to sequence!0 do
       if (state & sequence!j) ne sequence!j then
         [  // Found one, turn it on
         allOn = false
         test timedOut
            ifnot OutputSMI(outputList!i,state % sequence!j)
            ifso FailProc(i,sequence!j)
         break
         ]
      ]
   ]
resultis allOn
]

// ----------------------------------------------------------------
and ProcPortOnFail(nil,supply) be
// ----------------------------------------------------------------
[
let badSupply = supply & not InputSMI(procPowerIn!1)
if (badSupply&procPlus5) ne 0 then
   [
   Wl("Failed to turn on processor 5v supply.")
   Wl("  (It might be that the fan is broken.)")
   ]
if (badSupply&portPlus5) ne 0 then
   [
   Wl("Failed to turn on port 5v supply.")
   ]
]

// ----------------------------------------------------------------
and MemOnFail(cab,supply) be
// ----------------------------------------------------------------
[
OutputSMI(memPowerOut!cab,0)  // turn off all supplies in cabinet
let voltage = selecton supply into
   [
   case memPlus5: 5
   case memMinus5: -5
   case memPlus4: 4
   case memPlus16: 16
   ]
PutTemplate(dsp,"Failed to turn on $Dv supply in memory cab $O.*n",
   voltage,cab-1)
]

// ----------------------------------------------------------------
and ConfigureMemory() be
// ----------------------------------------------------------------
[
OutputSMI(#25,0)  // Set normal quadrant configuration
OutputSMI(#24,0)  // Reset all quadrants
let moduleTable = table
   [  // Physical to logical module correspondence table
   4
   #200		// Physical 0 = logical 4
   #004		// Physical 1 = logical 0
   #310		// Physical 2 = logical 6
   #114		// Physical 3 = logical 2
   ]
for quad = 0 to 3 do for mod = 1 to moduleTable!0 do
   OutputSMIPulse(#22,moduleTable!mod+quad)
]

// ----------------------------------------------------------------
and ResetMemErrors() be
// ----------------------------------------------------------------
[
OutputSMI(#24,#125)
]


// ----------------------------------------------------------------
and ZeroMemory() be
// ----------------------------------------------------------------
[
let zeroPage = vec 512*3
Zero(zeroPage,512*3)
for cab = 1 to memPowerIn!0 do
   if InputSMI(memPowerIn!cab) eq #17 then  // All supplies on?
      for i = 0 to 255 do  // Zero all memory in this cabinet
         [
         let page = 256*(cab-1)+i
         unless BltMaxcMem(memWrite,page,0,zeroPage,512) do
            [
            PutTemplate(dsp,"Memory cabinet $O not responding*n",
               cab-1)
            break
            ]
         ]
]

// ----------------------------------------------------------------
and BltMaxcMem(command,maxcPage,maxcWord,novaAdr,count) = valof
// ----------------------------------------------------------------
[
ExIO(dob+mem,maxcPage)
ExIO(doc+mem,maxcWord)
ExIO(doa+mem2,novaAdr)
ExIO(doas+mem,command+512-count)
let timer = nil; SetTimer(lv timer,10)  //100 ms plenty long enough
while ExSkip(skpbn+mem) do
   if TimerHasExpired(lv timer) then
      [ ExIO(nioc+mem); ResetMemErrors(); resultis false ]
ExIO(nioc+mem)
resultis true
]


// ----------------------------------------------------------------
and MaxcMemInt() be
// ----------------------------------------------------------------
// Handle interrupt from memory interface
[
ExIO(nioc+mem)
]


// ----------------------------------------------------------------
and InputSMI(address) = valof
// ----------------------------------------------------------------
[
ExIO(dob+maint,address)
let res = ExIO(dia+maint)
ExIO(dob+maint,0)
resultis res
]


// ----------------------------------------------------------------
and OutputSMI(address,value) be
// ----------------------------------------------------------------
[
ExIO(dob+maint,address)
ExIO(doa+maint,value)
ExIO(dob+maint,0)
]


// ----------------------------------------------------------------
and OutputSMIPulse(address,value) be
// ----------------------------------------------------------------
[
ExIO(dob+maint,address)
ExIO(doap+maint,value)
ExIO(dob+maint,0)
]


// ----------------------------------------------------------------
and Block() be [ ]  // Dummy since not using context package
// ----------------------------------------------------------------