// 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 ])
]