// AltIOMaxc2.bcpl -- code implementing communication with Maxc
//			and error monitoring

//	Last modified April 17, 1978  2:57 PM

get "altio.decl"
get "altiomaxc2.decl"


external
[
//outgoing procedures
MaxcWatcher; ClockInterrupt; SignalMaxc; AltIOReset
StartMaxc; StopMaxc; StopMaxcForcefully; ResetMaxc
TakeDLSOutput; GiveDLSInput; ConfigureMemory; ResetMemory
MaxcState; PrintMaxcState; StartEmulator
ReadLM; ReadSM

//incoming procedures
Block; Allocate; Free; MoveBlock; SetBlock; CallSwat; Timer
DoubleIncrement; DoubleDifference; Puts; Ws
SetTimer; TimerHasExpired; PupReset; ImpReset
InputSMI; OutputSMI; OutputSMIPulse; InputSMIErrors; OutputSMITest
ExecuteMicroInstruction; LoadBus; ReadBus; DisplaceMaxcAdr
FindZeroMaxcBit; SetOneMaxcBit; IncrementMaxcWord
StartIntervalTimer; DisableInterrupts; EnableInterrupts
MemReadAbsolute; MemWriteAbsolute; MemRMWAbsolute
MemReadRelative; MemWriteRelative; MemRMWRelative
RMWBitAbsolute; RMWBitRelative; MemWriteBlock40A

//outgoing statics
adrMTBS; adrDLSBS; maxcRunning; resetExecuted; dlsOutputChar
maxcClock; lastTime; interruptInterval; clockMask
fatalError; stdMemConf

//incoming statics
dsp; updateDisplayValid; sysZone
pupInputRequest; pupOutputRequest; impInputRequest; impOutputRequest
]


static
[
adrMTBS; adrDLSBS
dlsOutputChar
maxcRunning = false
resetExecuted = false
smiErr = 0
fatalSE = false
fatalPBF = false
fatalError = false
clockUpdateEnabled = false
interruptInterval = 17
maxcClock; lastTime; interruptTimer; clockMask
]

// ---------------------------------------------------------------------------
let MaxcWatcher() be
// ---------------------------------------------------------------------------
//process that watches for Maxc i/o requests and errors
[
Block()
unless maxcRunning do
   [  //watch only for memory errors if Maxc not running
   if InputSMI(smiJKERRS) ne 0 % InputSMI(smiLMERRS) ne 0 then
      [
      let qErr = vec 2
      ReadMemErrors(qErr)
      if FatalMemError(qErr) then
         [
         ReportMemErrorsToDisplay(qErr)
         fatalError = true
         ]
      ]
   loop
   ]

//all places that read the SMI error register must update smiErr.
//this is because reading the register causes it to be cleared,
//and signals such as COMA may be lost if we aren't careful
smiErr = (smiErr % InputSMIErrors()) & smiErrorMask
if smiErr ne 0 then
   [
   if smiErr<<SMIErr.fer then  //fatal error?
      [
      maxcRunning = false
      if MaxcState() eq stateRunning then
         [
         StopMaxcForcefully()
         Ws("*nMaxc didn't stop on fatal error, PMAINT switch ok?")
         ]

      //read and reset all memory error latches
      let qErr = vec 2
      ReadMemErrors(qErr)

      //discover whether a fatal memory error occurred, and if so,
      //whether Maxc is enabled to handle it
      test CheckMemErrors(qErr)
         ifso
            [  //fatal memory error that Maxc was enabled for
            ReportMemErrorsToMaxc(qErr)
            StartMaxc()
            ]
         ifnot
            [  //not an enabled memory error.
               //It is possibly an I/O reset request that we
               //are slow in responding to.  If so, fall through
               //to the COMA code, else leave Maxc halted.
            let resetRequested = false
            if smiErr<<SMIErr.coma &
             MaxcState() eq stateBreakpoint then
               [
               let maxcData = vec 3
               MemReadAbsolute(aMAXNV, maxcData)
               resetRequested = (maxcData!(mnIORST/16) &
                #100000 rshift (mnIORST rem 16)) eq 0
               ]
            unless resetRequested do
               [
               Puts(dsp,$*n)
               PrintMaxcState()  //tell what happened
               fatalError = true
               loop  //ignore requests until Maxc restarted
               ]
            ]
      ]

// MaxcWatcher (cont'd)

   if smiErr<<SMIErr.coma then  //communication strobe?
      [
      smiErr = 0
      let maxNV = vec 3
      MemReadAbsolute(aMAXNV, maxNV)  //read MAXNV
         [
         //stay in this loop until all requests have been processed
         let bitNumber = FindZeroMaxcBit(maxNV)
         switchon bitNumber into
            [
            //process a single request.
            //if the processing code resets the request bit itself,
            //it must do so using the maxNV data vector, and
            //it should finish with "loop" rather than "endcase"
            case mnIORST:  //io reset request
               [
               Block() repeatuntil MaxcState() ne stateRunning
               ConfigureMemory(0, 0)  //normal memory configuration
               AltIOReset()
               resetExecuted = true
               updateDisplayValid = false
               StartMaxc()  //resume processor
               break  //we have completely reset MAXNV
               ]
            case mnPUPIBG:  //Pup begin input request
               [ pupInputRequest = true; endcase ]
            case mnPUPOBG:  //Pup begin output request
               [ pupOutputRequest = true; endcase ]
            case mnIMPIBG:  //Imp begin input request
               [ impInputRequest = true; endcase ]
            case mnIMPOBG:  //Imp begin output request
               [ impOutputRequest = true; endcase ]
            case mnDLSOBG:  //DLS begin output request
               [
               SetOneMaxcBit(maxNV, bitNumber) //reset request first
               MemRMWAbsolute(aMAXNV, maxNV)
               DLSOutputRequest()
               loop
               ]
            case mnCDVS:  //CDVS (miscellaneous requests)
               [
               let maxcData = vec 3
               MemReadRelative(xCCHS, maxcData)  //read the arg
               switchon maxcData!0 into
                  [
                  case cdvsSetClock:
                     [ SetClockInterval(maxcData!1); endcase ]
                  case cdvsSetConfiguration:
                     [
                     StopMaxc()
                     ConfigureMemory(maxcData!1, maxcData!2)
                     StartMaxc()
                     endcase
                     ]
                  default:
                     CallSwat("Undefined Maxc CDVS request")
                  ]
               endcase
               ]
            case -1:
               break
            default:
               CallSwat("Undefined Maxc request (MAXNV)")
            ]
         SetOneMaxcBit(maxNV, bitNumber)  //reset request bit
         MemRMWAbsolute(aMAXNV, maxNV)
         ] repeat
      ]
   ]
] repeat

// ---------------------------------------------------------------------------
and ReadMemErrors(qErr) be
// ---------------------------------------------------------------------------
//read memory error status into 2-word vector qErr, then reset
//error latches in memory system.
[
qErr!0 = InputSMI(smiJKERRS)
qErr!1 = InputSMI(smiLMERRS)
OutputSMI(smiRESR, allResetErrors)
]

// ---------------------------------------------------------------------------
and FatalMemError(qErr) = valof
// ---------------------------------------------------------------------------
//return true if a fatal memory error has occurred
[
let mask = qeTimeout+qeDIP+qeAPE+qeDE  //always-fatal errors
if fatalSE then mask = mask+qeSE
if fatalPBF then mask = mask+qePBF
mask = mask + mask lshift 8
resultis (qErr!0 & mask) ne 0 % (qErr!1 & mask) ne 0
]

// ---------------------------------------------------------------------------
and CheckMemErrors(qErr) = valof
// ---------------------------------------------------------------------------
//analyze memory errors indicated by qErr!0 and qErr!1.
//return true if a fatal memory error occurred and Maxc is
//enabled for it, false otherwise.  If Maxc is not enabled,
//display whatever errors we find.
[
unless FatalMemError(qErr) resultis false  //no fatal errors
let state = MaxcState()
let maxcData = vec 3
MemReadRelative(xPSCAN, maxcData)  //see if Maxc enabled for error
test maxcData!0 eq 0 &
 (state eq stateHalted % state eq stateMainBusPE)
   ifnot
      [  //not enabled, report all errors to display
      ReportMemErrorsToDisplay(qErr)
      resultis false
      ]
   ifso resultis true  //pass errors to Maxc
]

// ---------------------------------------------------------------------------
and ReportMemErrorsToDisplay(qErr) be
// ---------------------------------------------------------------------------
[
Ws("*nMain memory error:")
for i = 0 to 5 do
   [
   let mask = #401 lshift i
   if (qErr!0 & mask) ne 0 % (qErr!1 & mask) ne 0 then
      [
      Ws(selecton i into
         [
         case 0: " SE: "
         case 1: " DE: "
         case 2: " PBF: "
         case 3: " APE: "
         case 4: " DIP: "
         case 5: " Timeout: "
         ])
      for q = 0 to 3 do
         if (qErr>>QErr↑q & mask) ne 0 then Puts(dsp, $J+q)
      ]
   ]
]

// ---------------------------------------------------------------------------
and ReportMemErrorsToMaxc(qErr) be
// ---------------------------------------------------------------------------
[
//figure out which kind of error to hand Maxc
manifest timeMask = qeTimeout + qeTimeout lshift 8
let errBit = (qErr!0 & timeMask) ne 0 % (qErr!1 & timeMask) ne 0?
 nmNXM, nmPARERR
RMWBitAbsolute(aNVMAX, errBit)  //set NXM or PARERR

//hand over assorted other interesting information
MemWriteRelative(xMEMER, qErr)
let uInst = table
   [
   #040102; #077546; #120001; #000000; #003400  // B←MAR
   #040102; #077546; #100001; #000000; #003400  // B←MDR
   #040102; #077546; #110001; #000000; #003400  // B←MDRL
   #040102; #077546; #160001; #000000; #003400  // B←KMAR
   #040102; #077546; #140001; #000000; #003400  // B←KMDR
   #040102; #077546; #150001; #000000; #003400  // B←KMDRL
   ]
let errorData = vec 3*6
for i = 0 to 15 by 3 do
   [
   ExecuteMicroInstruction(uInst)
   uInst = uInst+5
   ReadBus(errorData+i)
   ]
let errorAdr = vec 1
DisplaceMaxcAdr(xMAR, errorAdr)
MemWriteBlock40A(errorAdr, errorData, 6)
MemWriteRelative(xPSCAN, table [ -1; -1; -1 ])
]

// ---------------------------------------------------------------------------
and DLSOutputRequest() be
// ---------------------------------------------------------------------------
[
let maxcData = vec 3
MemReadRelative(xDLSO, maxcData, adrDLSBS)  //read request bits
   [
   let line = FindZeroMaxcBit(maxcData)
   if line ls 0 return  //no lines requesting output
   if line ls numDLSLines then
      [  //found line requesting output, copy the character
      MemReadRelative(xDLSB+2*line+1, maxcData, adrDLSBS)
      dlsOutputChar!line = maxcData!0
      ]
   SetOneMaxcBit(maxcData, line)
   MemRMWRelative(xDLSO, maxcData, adrDLSBS)  //reset line request
   ] repeat
]


// ---------------------------------------------------------------------------
and TakeDLSOutput(line) = valof
// ---------------------------------------------------------------------------
//procedure called externally to wait for a DLS output request
//on line and return the character.
[
Block() repeatwhile dlsOutputChar!line eq -1
let char = dlsOutputChar!line
dlsOutputChar!line = -1  //no character pending now
RMWBitRelative(xDLSOD, line, adrDLSBS)  //set done bit for line
RMWBitAbsolute(aNVMAX, nmDLSDN)  //signal DLS done
SignalMaxc()
resultis char
]


// ---------------------------------------------------------------------------
and GiveDLSInput(line, char) be
// ---------------------------------------------------------------------------
//procedure called externally to send a DLS input character to Maxc
[
MemWriteRelative(xDLSB+2*line, lv char, adrDLSBS) //give char to Maxc
RMWBitRelative(xDLSIN, line, adrDLSBS)  //set done bit for line
RMWBitAbsolute(aNVMAX, nmDLSDN)  //signal DLS done
SignalMaxc()
]

// ---------------------------------------------------------------------------
and AltIOReset() be
// ---------------------------------------------------------------------------
//perform i/o reset operation
[
OutputSMITest(#16)  //put SMI in normal mode
OutputSMI(smiRESR, allResetErrors)  //reset memory error latches
let maxcData = vec 3
let zeroWord = table [ 0; 0; 0 ]
let oneWord = table [ #177777; #177777; #177400 ]

//read the addresses of the communication regions
MemReadAbsolute(aMTBS, maxcData)
MoveBlock(adrMTBS, maxcData+1, 2)
MemReadAbsolute(aDLSBS, maxcData)
MoveBlock(adrDLSBS, maxcData+1, 2)

//reset DLS state
SetBlock(dlsOutputChar, -1, numDLSLines)  //no output chars pending
MemWriteRelative(xDLSOD, zeroWord, adrDLSBS)  //reset output done
MemWriteRelative(xDLSO, oneWord, adrDLSBS)  //reset output requests
MemWriteRelative(xDLSIN, zeroWord, adrDLSBS)  //reset input done

//reset Maxc clock
SetClockInterval(0)

//reset the Pup and Imp processes
PupReset()
ImpReset()

//finally, reset NVMAX and MAXNV to their idle states
MemWriteAbsolute(aNVMAX, zeroWord)
MemWriteAbsolute(aMAXNV, oneWord)
]

// ---------------------------------------------------------------------------
and SignalMaxc() be
// ---------------------------------------------------------------------------
   if maxcRunning then OutputSMI(smiCR, not (crNormal+crStrobe))

// ---------------------------------------------------------------------------
and StartMaxc(microAdr; numargs na) be
// ---------------------------------------------------------------------------
//start Maxc running at the specified micro-address, or just
//resume it if microAdr is not specified
[
if na gr 0 then
   [
   let maxcData = vec 3
   maxcData!0 = 0
   maxcData!1 = microAdr rshift 4
   maxcData!2 = microAdr lshift 12
   LoadBus(maxcData)
   ExecuteMicroInstruction(  // NPC←BR, ENPC
    table [ #040102; #077546; #000000; #020000; #113600 ])
   DoubleIncrement(maxcData+1, #10000)
   LoadBus(maxcData)
   ExecuteMicroInstruction(  // NPC←BR, ENPC, EIMA
    table [ #040102; #077546; #000000; #020000; #133600 ])
   ExecuteMicroInstruction(  // IRET, INHINT
    table [ #040102; #077546; #000152; #000000; #103400 ])
   ResetArm()  //reset bipolar memory errors
   ]
ExecuteMicroInstruction(  // WRESTART, KWRESTART
 table [ #040102; #077546; #000110; #160000; #103400 ])
ExecuteMicroInstruction(  // FRZBALUBC, EIC, EB, ENPC, EIMA, NOTSS, Strobe
 table [ #040102; #077546; #000155; #000000; #177140 ])

//now that Maxc is running again, read and reset the SMI error
//register, ignoring FER (which will be on) but being careful
//not to lose other signals (COMA).
smiErr = (smiErr % InputSMIErrors()) &
 (smiErrorMask - #100000 rshift offset SMIErr.fer)
maxcRunning = true
]

// ---------------------------------------------------------------------------
and StopMaxc() be
// ---------------------------------------------------------------------------
[
if maxcRunning then
   [
   RMWBitAbsolute(aNVMAX, nmHALT)  //request console halt
   SignalMaxc()
   maxcRunning = false  //shut off MaxcWatcher process
   let timer, state = nil, nil
   SetTimer(lv timer, 5)  //wait 50 ms for request to take
      [
      Block()
      state = MaxcState()
      ] repeatuntil state ne stateRunning % TimerHasExpired(lv timer)
   unless state eq stateBreakpoint do
      [
      Ws("*nUnclean micro stop: ")
      test state eq stateRunning
         ifso  //Maxc didn't stop, halt it by force.
            [
            StopMaxcForcefully()
            Ws("Maxc didn't respond to halt request")
            ]
         ifnot PrintMaxcState(state)
      ]
   ]
]

// ---------------------------------------------------------------------------
and StopMaxcForcefully() be
// ---------------------------------------------------------------------------
   OutputSMI(smiCR, not (crEIC+crEB+crEIMA+crENPC+crIntOn))

// ---------------------------------------------------------------------------
and ResetMaxc() be
// ---------------------------------------------------------------------------
//do emulator reset in preparation for starting a program
[
StopMaxc()
StartMaxc(emulatorReset)
StopMaxc()  //wait for reset to complete
MemWriteAbsolute(aNVMAX, table [ 0; 0; 0 ])  //reset Alto requests
]


// ---------------------------------------------------------------------------
and MaxcState() = valof
// ---------------------------------------------------------------------------
[
let runReg = InputSMI(smiRUN)
if runReg<<RUN.running resultis stateRunning
unless runReg<<RUN.notBreakpoint resultis stateBreakpoint
if runReg<<RUN.localPE resultis stateLocalMemPE
if runReg<<RUN.mainPE resultis stateMainBusPE
resultis stateHalted
]


// ---------------------------------------------------------------------------
and PrintMaxcState(state; numargs na) be
// ---------------------------------------------------------------------------
[
if na eq 0 then state = MaxcState()
Ws(selecton state into
   [
   case stateRunning: "Maxc running"
   case stateBreakpoint: "Maxc halted at breakpoint"
   case stateLocalMemPE: "Maxc halted with bipolar memory error"
   case stateMainBusPE: "Maxc halted with memory bus parity error"
   case stateHalted: "Maxc halted"
   ])
]


// ---------------------------------------------------------------------------
and StartEmulator(maxcWord; numargs na) be
// ---------------------------------------------------------------------------
//start emulation of PDP-10 instructions at the specified address
//(given as a 36-bit maxcWord, not a maxcAdr).
[
StopMaxc()
if na gr 0 then MemWriteAbsolute(aSTADR,maxcWord)  //set starting address
StartMaxc(emulatorStart)
]

// ---------------------------------------------------------------------------
and ResetMemory() be
// ---------------------------------------------------------------------------
[
OutputSMI(smiRESR, 0)  //full reset
ConfigureMemory(0, 0)
]


// ---------------------------------------------------------------------------
and ConfigureMemory(map, fatality) be
// ---------------------------------------------------------------------------
//setup the memory system with the logical-physical mapping
//defined by "map", which is in the following form (see Maxc 11.6):
//	B0:	disable error correction for module 0
//	B1-3:	logical module assignment for module 0
//	B4:	disable error correction for module 1
//	etc.
//"fatality" determines whether SE and PBF are fatal:
//	B0:	SE fatal
//	B1:	PBF fatal
[
//set standard quadrant configuration (0) and fatality as specified
fatalSE = (fatality & #100000) ne 0
fatalPBF = (fatality & #40000) ne 0
OutputSMI(smiCONFR, enableFER+(allSEF&fatalSE)+(allPBFF&fatalPBF))

//set logical-physical map for each module in each quadrant
if map eq 0 then map = stdMemConf  //standard configuration
for m = 0 to 3 do for q = 0 to 3 do
   [
   let plmr = map rshift (7-4*m)  //position ec and log mod number
   plmr<<PLMR.physicalModule = m
   plmr<<PLMR.quadrant = q
   OutputSMIPulse(smiPLMR, plmr)
   ]
OutputSMI(smiRESR, allResetErrors)
]

// ---------------------------------------------------------------------------
and SetClockInterval(interval) be
// ---------------------------------------------------------------------------
//set the Maxc clock interrupt interval to "interval", and, if
//nonzero, enable the automatic updating of TODCLK
[
DisableInterrupts()  //lock out the clock interrupt
clockUpdateEnabled = interval ne 0
test clockUpdateEnabled
   ifso
      [  //get starting value of TODCLK that we are to update
      MemReadRelative(xTODCLK, maxcClock)
      interruptInterval = interval
      ]
   ifnot
      interruptInterval = 17
EnableInterrupts()
]


// ---------------------------------------------------------------------------
and ClockInterrupt() be
// ---------------------------------------------------------------------------
//this procedure services the interval timer interrupt, updates the
//clock in Maxc if required, and initiates Maxc clock interrupts.
//Note that although we are simulating a millisecond clock,
//it is too expensive to actually update the clock every
//millisecond.  The clock update interval is given by the
//parameter clockUpdateInterval, which is independent of the
//interrupt interval requested by Maxc.  Hence the Maxc interrupt
//interval is quantized to the nearest clockUpdateInterval unit.
[
let now = vec 1
Timer(now)
let dif = DoubleDifference(now, lastTime)
MoveBlock(lastTime, now, 2)
if clockUpdateEnabled then
   [  //update TODCLK cell in Maxc
   IncrementMaxcWord(maxcClock, dif)
   MemWriteRelative(xTODCLK, maxcClock)
   ]
interruptTimer = interruptTimer+dif
if interruptTimer ge interruptInterval then
   [  //generate Maxc clock interrupt
   RMWBitAbsolute(aNVMAX, nmCLOCK)
   SignalMaxc()
   interruptTimer = interruptTimer rem interruptInterval
   ]
StartIntervalTimer(clockUpdateInterval, clockMask)
]

// ---------------------------------------------------------------------------
and ReadLM(adr, maxcData) be
// ---------------------------------------------------------------------------
[
let state = SaveMaxcState()
LoadX(adr)
ExecuteMicroInstruction(  // Q←LX
 table [ #040062; #077046; #000001; #000000; #103400 ])
ReadQ(maxcData)
RestoreMaxcState(state)
]


// ---------------------------------------------------------------------------
and ReadSM(adr, maxcData) be
// ---------------------------------------------------------------------------
[
let state = SaveMaxcState()
LoadY(adr)
ExecuteMicroInstruction(  // B←SY
 table [ #040102; #077546; #060001; #000000; #103400 ])
ReadBus(maxcData)
RestoreMaxcState(state)
]


// ---------------------------------------------------------------------------
and SaveMaxcState() = valof
// ---------------------------------------------------------------------------
[
let state = Allocate(sysZone, 8)
ExecuteMicroInstruction(  // B←EREG
 table [ #040102; #077547; #020001; #000000; #003400 ])
ReadBus(state)
ReadQ(state+3)
state!6 = ReadX()
state!7 = ReadY()
resultis state
]


// ---------------------------------------------------------------------------
and RestoreMaxcState(state) be
// ---------------------------------------------------------------------------
[
LoadY(state!7)
LoadX(state!6)
LoadBus(state+3)
ExecuteMicroInstruction(  // Q←BR, EIC
 table [ #040102; #077746; #000001; #000000; #103600 ])
LoadBus(state)
ExecuteMicroInstruction(  // EREG←BR, EIC
 table [ #040102; #077546; #000001; #000000; #103600 ])
Free(sysZone, state)
]

// ---------------------------------------------------------------------------
and ReadQ(maxcData) be
// ---------------------------------------------------------------------------
[
ExecuteMicroInstruction(  // B←Q
 table [ #040102; #077547; #040001; #000000; #003400 ])
ReadBus(maxcData)
]

// ---------------------------------------------------------------------------
and ReadX() = Read16BitReg(  // B←X
// ---------------------------------------------------------------------------
 table [ #040102; #077546; #010001; #000000; #003400 ])

// ---------------------------------------------------------------------------
and ReadY() = Read16BitReg(  // B←Y
// ---------------------------------------------------------------------------
 table [ #040102; #077546; #020001; #000000; #003400 ])

// ---------------------------------------------------------------------------
and Read16BitReg(microInst) = valof
// ---------------------------------------------------------------------------
[
let data = vec 2
ExecuteMicroInstruction(microInst)
ReadBus(data)
resultis data!1 lshift 4 + data!2 rshift 12
]

// ---------------------------------------------------------------------------
and LoadX(value) be Load16BitReg(value,  // X←BR, EIC
// ---------------------------------------------------------------------------
 table [ #040102; #077546; #000201; #000000; #103600 ])

// ---------------------------------------------------------------------------
and LoadY(value) be Load16BitReg(value,  // Y←BR, EIC
// ---------------------------------------------------------------------------
 table [ #040102; #077546; #000401; #000000; #103600 ])

// ---------------------------------------------------------------------------
and Load16BitReg(value, microInst) be
// ---------------------------------------------------------------------------
[
let data = vec 2
data!1 = value rshift 4; data!2 = value lshift 12
LoadBus(data)
ExecuteMicroInstruction(microInst)
]

// ---------------------------------------------------------------------------
and ResetArm() be  //reset bipolar parity errors
// ---------------------------------------------------------------------------
[
let data = vec 2
ExecuteMicroInstruction(  // B←ARM
 table [ #040102; #077546; #000055; #000000; #003400 ])
ReadBus(data)
data!0 = (data!0 & #16) lshift 3
LoadBus(data)
ExecuteMicroInstruction(  // ARM←BR, EIC
 table [ #100102; #077546; #007745; #000000; #103600 ])
]