// MemBash.bcpl -- Maxc2 memory contention diagnostic
//	Assumes a memory diagnostic (e.g., DGMR) running on Maxc

//	Last modified June 4, 1978  5:51 PM

get "streams.d"

external
[
Ws; Wss; Gets; Puts; Endofs; Resets; Noop; MoveBlock; Zero
CreateDisplayStream; ShowDisplayStream; StartIO
MemRead; MemWrite; MemRMW
InputSMI; OutputSMI; OutputSMIPulse
InputSMIErrors; OutputSMITest; ExecuteMicroInstruction; LoadBus; ReadBus
LoadRam; SetBLV; USetBLV

RamImage; dsp; keys; lvUserFinishProc; adrMTBS
]

static
[
AltoOperation; NextAdr
altoNumber; maxcNumber; numberTyped; altoCode; testAdr
displayEnabled = true
startAdr = #21
savedUserFinishProc
fatalSE = false; fatalPBF = false
adrMTBS  //external wanted by AltIOUtila.asm
]


manifest
[
//Maxc processor states (software convention)
stateRunning = 0	//processor running
stateBreakpoint = 1	//at micro-breakpoint
stateLocalMemPE = 2	//local memory parity error
stateMainBusPE = 3	//main memory bus parity error
stateHalted = 4		//halted for other reason

//SMI input addresses
smiRUN = #200	//processor run status
smiB0 = #203	//processor bus
smiB1 = #202
smiB2 = #201
smiJKERRS = #43	//memory errors for quadrants J and K
smiLMERRS = #44	//memory errors for quadrants L and M

//SMI output addresses
smiBR0 = #213	//processor bus
smiBR1 = #212
smiBR2 = #211
smiPIR0 = #217
smiPIR1 = #216	//pseudo-instruction register
smiPIR2 = #215
smiPIR3 = #214
smiCR = #210	//control register
smiPLMR = #40	//physical/logical memory module register
smiCONFR = #41	//memory configuration register
smiRESR = #42	//memory error reset register
]

structure RUN:	//processor run status
   [
   blank bit 8
   running bit		//processor is running
   notBreakpoint bit	//not at micro breakpoint
   localPE bit		//local memory parity error
   mainPE bit		//main memory data bus parity error
   ]

structure QErr↑0,3 byte	//memory error register (2 words)
manifest		//layout assuming byte right-justified
   [
   qeTimeout = #40
   qeDIP = #20
   qeAPE = #10
   qePBF = #4
   qeDE = #2
   qeSE = #1
   ]

manifest	//processor control register
[
crEIC = #100000	//enable instruction controlled changes
crEB = #40000	//enable changes to branch conditions
crEIMA = #20000	//enable changes to IMA
crENPC = #10000	//enable changes to NPC
crNOTSS = #4000	//don't single step
crSetRun = #2000 //set the run flipflop
crReset = #1000	//reset processor-memory interface
crEPIR = #400	//execute instructions from PIR rather than IM
crRegToB = #200	//put BR register on processor bus
crIntOn = #100	//enable micro-interrupts
crStrobe = #40	//generate interprocessor signal

//normal value sent to CR (e.g., when setting Strobe)
crNormal = crEIC+crEB+crEIMA+crENPC+crNOTSS+crIntOn
]

structure PLMR:	//physical/logical module register
   [
   blank bit 7
   disableCorrection bit
   logicalModule bit 3
   physicalModule bit 3
   quadrant bit 2
   ]

structure CONFR:  //configuration register
   [
   blank bit 4
   eFER bit	//enable reporting of fatal errors
   configuration bit 3  //address/quadrant map
   jSEF bit	//quadrant J single errors fatal
   jPBFF bit	//quadrant J parity bit failures fatal
   kSEF bit
   kPBFF bit
   lSEF bit
   lPBFF bit
   mSEF bit
   mPBFF bit
   ]
manifest allSEF = #252
manifest allPBFF = #125
manifest enableFER = #4000

structure RESR:  //error reset register (all bits complemented)
   [
   blank bit 8
   jResetErrors bit  //reset errors in quadrant J
   jResetQuadrant bit  //completely reset quadrant J
   kResetErrors bit
   kResetQuadrant bit
   lResetErrors bit
   lResetQuadrant bit
   mResetErrors bit
   mResetQuadrant bit
   ]
manifest allResetErrors = #125
manifest allResetQuadrant = #252

structure SMIErr:  //SMI error register
   [
   blank bit 11
   comb bit	//communication strobe B (unused)
   coma bit	//communication strobe A
   nfer bit	//non-fatal error
   fer bit	//fatal error
   blank bit
   ]
manifest smiErrorMask = #12  //bits that we care about (coma, fer)



// ---------------------------------------------------------------------------
let MemBash() be
// ---------------------------------------------------------------------------
[
Ws("MemBash of June 4, 1978")

//load the special Ram microcode
RamImage!0 = #077376  //run tasks 0, 10, 17 in Ram
let res = LoadRam(RamImage, true)  //load and boot
if res ls 0 then
   Ws("*nFailed to load Alto Ram -- Ram or Ethernet board broken")
if res gr 0 then Ws("*nMicrocode constants wrong, beware!")
savedUserFinishProc = @lvUserFinishProc
@lvUserFinishProc = MyFinishProc

Ws("*nAlto MI State  Address  Maxc State    MAR      KMAR     Memory State")

let ds = vec 15000
ds = CreateDisplayStream(30, ds, 15000)
ShowDisplayStream(ds)
dsp = ds

OutputSMITest(#16)
ConfigureMemory(0, 0)

let v = vec 2; maxcNumber = v
let v = vec 1; testAdr = v; Zero(testAdr, 2)
AltoOperation = MemRead
NextAdr = IncrementAdr
Command()
]


// ---------------------------------------------------------------------------
and Command() be
// ---------------------------------------------------------------------------
[
Ws("*n**")
let char = GetKeys()
switchon char into
   [
   case $G: case $g:
      [
      if Confirm("Go") then DoTest()
      endcase
      ]
   case $A: case $a:
      [
      Ws("Alto operation = ")
      switchon GetKeys() into
         [
         case $R: case $r:
            [ Ws("Read"); AltoOperation = MemRead; endcase ]
         case $W: case $w:
            [ Ws("Write"); AltoOperation = MemWrite; endcase ]
         case $M: case $m:
            [ Ws("Modify"); AltoOperation = MemRMW; endcase ]
         case $?:
            [ Ws("? Read, Write, Modify"); loop ]
         default:
            [ Oop(); loop ]
         ]
      Ws(", address sequence = ")
      switchon GetKeys() into
         [
         case $S: case $s:
            [ Ws("Sequential"); NextAdr = IncrementAdr; endcase ]
         case $R: case $r:
            [
            Ws("Repeat at address: ")
            test GetNumber() eq $*n & numberTyped
               ifso MoveBlock(testAdr, maxcNumber+1, 2)
               ifnot [ Oop(); loop ]
            NextAdr = Noop
            endcase
            ]
         case $?:
            [ Ws("? Sequential, Repeat"); loop ]
         default:
            [ Oop(); loop ]
         ]
      endcase
      ]
   case $M: case $m:
      [
      Ws("Maxc start address = ")
      test GetNumber() eq $*n & numberTyped
         ifso startAdr = altoNumber
         ifnot Oop()
      endcase
      ]
   case $D: case $d:
      [
      if Confirm(displayEnabled?
       "Don't display errors", "Display errors") then
         displayEnabled = not displayEnabled
      endcase
      ]
   case $Z: case $z:
      [
      if Confirm("Zap memory") then ResetMemory()
      endcase
      ]
   case $Q: case $q:
      [
      if Confirm("Quit") then finish
      endcase
      ]
   case $?:
      [
      Ws("? Alto operation, Maxc start adr, Go, Display, Zap, Quit")
      endcase
      ]
   case $*n: case $*s:
      endcase
   default:
      Oop(char)
   ]
] repeat


// ---------------------------------------------------------------------------
and DoTest() be
// ---------------------------------------------------------------------------
[
Ws("*nTest running, hit any key to stop")
StartMaxc(startAdr)
   [
   Zero(maxcNumber, 3)
   altoCode = AltoOperation(testAdr, maxcNumber)
   let smiErr = InputSMIErrors()
   if altoCode ne 0 % smiErr<<SMIErr.fer then
      [
      test displayEnabled
         ifso DisplayErrors()
         ifnot Puts(dsp, $?)
      if smiErr<<SMIErr.fer then StartMaxc(startAdr)
      ]
   NextAdr()
   ] repeatwhile Endofs(keys)
StopMaxcForcefully()
Gets(keys)
]


// ---------------------------------------------------------------------------
and IncrementAdr() be
// ---------------------------------------------------------------------------
[
testAdr!1 = (testAdr!1+#10000) & #170000
if testAdr!1 eq 0 then testAdr!0 = (testAdr!0+1) & #37777
]


// ---------------------------------------------------------------------------
and Display20Bit(str,adr) be PrintOctalField(str,adr,0,19)
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
and Display36Bit(str,adr) be
// ---------------------------------------------------------------------------
[
PrintOctalField(str,adr,0,17)
Puts(str,$*s)
PrintOctalField(str,adr,18,35)
]

// ---------------------------------------------------------------------------
and Display40Bit(str,adr) be
// ---------------------------------------------------------------------------
[
test (adr!2 & #7400) ne 0
   ifso PrintOctalField(str,adr,36,39)
   ifnot Wss(str,"  ")
Puts(str,$*s)
Display36Bit(str,adr)
]


// ---------------------------------------------------------------------------
and PrintOctalField(str,adr,firstBit,lastBit) be
// ---------------------------------------------------------------------------
[
let r = (lastBit-firstBit) rem 3
firstBit = firstBit+r
let mask = (2 lshift r)-1
   [
   let q,r = firstBit rshift 4, firstBit&#17
   Puts(str,$0 + ((adr!(q-1) lshift (1+r) +
    adr!q rshift (15-r)) & mask))
   firstBit = firstBit+3
   mask = 7
   ] repeatuntil firstBit gr lastBit
]


// ---------------------------------------------------------------------------
and DisplayErrors() be
// ---------------------------------------------------------------------------
[
Puts(dsp, $*n)
Ws(selecton altoCode into
   [
   case 0:  "             "
   case 1:  "Mem bus PE   "
   case 2:
   case 3:  "Timed out    "
   default: "Impossible   "
   ])
Ws("  ")
Display20Bit(dsp, testAdr)
Ws("  ")
let state = MaxcState()
Ws(selecton state into
   [
   case stateRunning:    "Running     "
   case stateBreakpoint: "Breakpoint  "
   case stateLocalMemPE: "Local mem PE"
   case stateMainBusPE:  "Mem bus PE  "
   case stateHalted:     "Halted      "
   ])
Ws("  ")
if state eq stateRunning then StopMaxcForcefully()
let maxcData = vec 2
ExecuteMicroInstruction( table  // B←MAR
   [ #040102; #077546; #120001; #000000; #003400 ])
ReadBus(maxcData)
Display20Bit(dsp, maxcData+1)
Ws("  ")
ExecuteMicroInstruction( table  // B←KMAR
   [ #040102; #077546; #160001; #000000; #003400 ])
ReadBus(maxcData)
Display20Bit(dsp, maxcData+1)
if state eq stateRunning then StartMaxc()
Puts(dsp, $*s)

let qErr = vec 1
qErr!0 = InputSMI(smiJKERRS)
qErr!1 = InputSMI(smiLMERRS)
OutputSMI(smiRESR, allResetErrors)
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 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 ])
   maxcData!2 = (maxcData!2+#10000) & #170000
   if maxcData!2 eq 0 then maxcData!1 = maxcData!1 + 1
   LoadBus(maxcData)
   ExecuteMicroInstruction(  // NPC←BR, ENPC, EIMA
    table [ #040102; #077546; #000000; #020000; #133600 ])
   ExecuteMicroInstruction(  // IRET, INHINT
    table [ #040102; #077546; #000152; #000000; #103400 ])
   ]
ExecuteMicroInstruction(  // WRESTART, KWRESTART
 table [ #040102; #077546; #000110; #160000; #103400 ])
ExecuteMicroInstruction(  // FRZBALUBC, EIC, EB, ENPC, EIMA, NOTSS, Strobe
 table [ #040102; #077546; #000155; #000000; #177140 ])
InputSMIErrors()
]


// ---------------------------------------------------------------------------
and StopMaxcForcefully() be
// ---------------------------------------------------------------------------
   OutputSMI(smiCR,not (crEIC+crEB+crEIMA+crENPC+crIntOn))


// ---------------------------------------------------------------------------
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 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 = #040142  //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 GetNumber() = valof
// ---------------------------------------------------------------------------
//inputs an octal number from the keyboard, ignoring spaces.
//returns a 16-bit result in altoNumber and a 40-bit result
//in maxcNumber.  numberTyped will be true iff a number was typed
[
altoNumber = 0
Zero(maxcNumber,3)
numberTyped = false
   [
   let char = GetKeys()
   if char eq $*s then [ Puts(dsp,char); loop ]
   if char ls $0 % char gr $7 then resultis char
   numberTyped = true
   Puts(dsp,char)
   altoNumber = altoNumber lshift 3 + char-$0
   let overflow = maxcNumber!0 rshift 13
   maxcNumber!0 = maxcNumber!0 lshift 3 + maxcNumber!1 rshift 13
   maxcNumber!1 = maxcNumber!1 lshift 3 + maxcNumber!2 rshift 13
   maxcNumber!2 = (maxcNumber!2 & #010400) lshift 3 +
    (char-$0) lshift 12 + overflow lshift 8
   ] repeat
]


// ---------------------------------------------------------------------------
and Confirm(prompt; numargs na) = valof
// ---------------------------------------------------------------------------
[
if na gr 0 then Ws(prompt)
Ws(" [confirm] ")
   [
   switchon GetKeys() into
      [
      case $Y: case $y: case $*n: case $.:
         [ Ws("Yes."); resultis true ]
      case $N: case $n: case $*177:
         [ Ws("No."); resultis false ]
      ]
   Oop()
   ] repeat
]


// ---------------------------------------------------------------------------
and Oop(char; numargs na) be
// ---------------------------------------------------------------------------
[
if na gr 0 then Puts(dsp,char)
Ws(" ? ")
Resets(keys)
]


// ---------------------------------------------------------------------------
and GetKeys() = Gets(keys)
// ---------------------------------------------------------------------------


// ---------------------------------------------------------------------------
and MyFinishProc() be
// ---------------------------------------------------------------------------
[
@lvUserFinishProc = savedUserFinishProc
USetBLV(#177776)
StartIO(#100000)
]


// ---------------------------------------------------------------------------
and Ws(string) be Wss(dsp, string)
// ---------------------------------------------------------------------------