//MX.BCPL interface to Maxc2
get "mx.d"
//GetRegData and PutRegData accept a register index as follows:
// 0 X 8 bits
// 1 AC 4 bits
// 2 Y 9 bits
// 3 P 36 bits
// 4 Q 36 bits
// 5 F 36 bits
// 6 NPC 12 bits
// 7 MAR 20 bits
// 8 MDR 40 bits (MDRL is the right-most bits)
// 9 KMAR 20 bits
// 10 KMDR 40 bits (KMDRL is the right-most bits)
// 11 ARM 36 bits
// 12 IMA 12 bits
// 13 KUNIT 3 bits
// 14 EREG 36 bits
// 15 BPC 20 bits
// 16 P1 36 bits (P1 and P are written concurrently)
//The duplicate registers for X and Y and the bus and alu branch
//conditions are invisible.
//GetMemData and PutMemData accept a memory index as follows:
// 0 IM[0,35] 36 bits 4,096 words (for diag only)
// 1 IM[36,71] 36 bits 4,096 words (for diag only)
// 2 IM 72 bits 4,096 words
// 3 SM 36 bits 512 words
// 4 DM 36 bits 512 words
// 5 MP 18 bits 1,024 words
// 6 MAIN 40 bits 1,048,576 words
// 7 RM 36 bits 32 words
// 8 LM 36 bits 32 words
// 9 STK 12 bits 12 words
// 10 DM1 36 bits 512 words
// 11 DM2 36 bits 512 words
// 12 LDR 80 bits 100 words
// 13 KSTAT 36 bits 8 words (read-only--bad result if int-in-progress)
//These routines accept a pointer called DataVec to a vector
//of data packed left-justified for each microprocessor word.
//The trailing bits of the last Alto word used to hold the
//value for a microprocessor register are returned zero-filled
//by Get, but may contain garbage on Put.
//GetMemData and PutMemData also use a pointer AddrVec to a
//double precision memory address which is unpacked into two
//Alto words called MADDRH and MADDRL during execution of the routines.
//The interface to Maxc2 works with 36 bits at-a-time.
//When sending data to the microprocessor, BR0 receives
//bits 0-15, BR1 16-31, and BR2 32-35 in bits 8-11 of the Alto word.
//When reading data from the microprocessor, B0, B1, and B2
//are used in the same way. The data must be complemented on
//both input and output.
//The Get and Put routines above freely smash EREG, Q, IMA, NPC, and
//F in the course of reading or writing the register explicitly
//addressed. MGetRegData, MPutRegData, MGetMemData, and MPutMemData
//are identical to the above but restore incidentally clobbered
//registers afterwards.
//XctMic is used to execute microinstructions through the maintenance
//interface. It does this by loading the 64-bit microinstructions
//into PIR0, PIR1, PIR2, and PIR3 of the interface, and then
//loading CR with control bits for executing the microinstruction.
//All of these operations are carried out through "memory
//references" to ADREG (selecting one of the maintenance
//interface registers mentioned above) and then to either
//INREG or OUTREG.
//An assortment of suboutines move data to and from the maintenance
//interface's data register before and after microinstructions.
//DataVec's are universally used to hold values for registers and memories.
//The 80-bit microinstructions executed through the maintenance
//interface are assembled by Micro using a declaration file
//called DLANG2 nearly identical to LANG2 for ordinary microinstructions.
//Differences are: fields in microinstructions are in different
//locations; branch address is missing; and values for the control register
//CR must be assembled as well. LOADER.MC contains microinstructions needed
//by procedures in MX. The order of these instructions
//is declared by the manifest constants in MX.D, so it should
//not be changed.
//Read into standard statics all registers clobbered incidentally while
//reading or writing other registers.
let ReadAllRegs() be
[ XctR36(RDEREG,EREG);
for I = 0 to 11 do //Save the stack
[ XctR12(RDSTK,STK+I); XctMic(POPSTK)
]
XctR12(RDNPC,lv NPC)
XctR12(RNIMA,lv IMA); IMA = (IMA-20B)&177760B //NPC←IMA+1, then B←NPC
XctR8(RDX,lv X); XctR9(RDY,lv Y); XctR36(RDSMF,SMFORF)
XctR36(RDQ,Q); XctR36(RDF,F); RestoreSNI(); MPDSwitchPhase()
]
//Restore registers clobbered during STK changes
//Route the values to NPC to STK by a CALL
and RestoreSNI() be
[ for I = 11 to 0 by -1 do
[ XctL12(LDNPC,STK+I); XctMic(CALL) ]
RestoreMRegs(2)
]
and RestoreAfterLoad() be
[ RestoreMRegs(2); RestoreMRegs(4)
RestoreMRegs(7); ReadAllRegs()
]
//Note that TMP and TMP1 must point at even word boundaries
//Arg asmtbl is pointer to function for doing the memory operation
and SetupMAIN(asmtbl) be
[ let errs = (asmtbl(TMP,TMP1) & 7)
if errs eq 0 then return
@ADREG = RESR; @OUTREG = 125B; @ADREG = 0 //Reset error latches
if (errs & 4) ne 0 do
[ StartIO(20000B)
WssCSS("Zapped memory interface ")
]
if (errs & 2) ne 0 then WssCSS("NXM ")
if (errs & 1) ne 0 then WssCSS("Memory bus PE ")
]
//Move tag bits from pos 36-39 to pos 0-3
and RFix40(DataVec,MemVec) be
[ let T1,T2,T3 = MemVec!0,MemVec!1,(MemVec!2) & 177400B
DataVec!0 = (T1 rshift 4)+(T3 lshift 4)
DataVec!1 = (T2 rshift 4)+(T1 lshift 12)
DataVec!2 = ((T3 rshift 4)+(T2 lshift 12))&177400B
]
//Move tag bits from pos 0-3 (from text input) to pos 36-39 (for memory)
and WFix40(MemVec,DataVec) be
[ let T1,T2,T3 = DataVec!0,DataVec!1,(DataVec!2) & 177400B
MemVec!0 = (T1 lshift 4)+(T2 rshift 12)
MemVec!1 = (T2 lshift 4)+(T3 rshift 12)
MemVec!2 = ((T3 lshift 4)+(T1 rshift 4)) & 177400B
]
//Get register data
//Always read from the hardware. Also update standard statics to shorten
//the state saving required for memory read/write.
and GetRegData(RegX,DataVec) be
[ switchon RegX into [
case 0: XctR8(RDX,DataVec); X = DataVec!0; endcase
case 1: XctR4(RDAC,DataVec); endcase
case 2: XctR9(RDY,DataVec); Y = DataVec!0; endcase
case 3: XctR36(RDP,DataVec); endcase
case 4: XctR36(RDQ,DataVec); MoveBlock(Q,DataVec,3); endcase
case 5: XctR36(RDF,DataVec); MoveBlock(F,DataVec,3); endcase
case 6: XctR12(RDNPC,DataVec); NPC = DataVec!0; endcase
case 7: XctR20(RDMAR,DataVec); endcase
case 8: XctR36(RDMDR,DataVec); XctMic(RDMDRL); goto GetB40
case 9: XctR20(RDKMAR,DataVec); endcase
case 10: XctR36(RDKMDR,DataVec); XctMic(RDKMDRL); goto GetB40
case 11: XctR36(RDARM,DataVec); endcase
case 12: XctR12(RNIMA,lv IMA); IMA = (IMA-20B)&177760B
DataVec!0 = IMA; XctL12(LDNPC,lv NPC); endcase
case 13: XctR4(RDKUN,DataVec); DataVec!0 = DataVec!0 lshift 1; endcase
case 14: XctR36(RDEREG,DataVec); MoveBlock(EREG,DataVec,3); endcase
case 15: XctR20(RDBPC,DataVec); endcase
case 16: XctR36(RDP,TMP); XctR36(RDPP1,DataVec); XctL36(LDP,TMP); endcase
default: CallSwat(); return
]
@ADREG = 0; return
GetB40: @ADREG = B2; DataVec!2 = DataVec!2 + (not (@INREG lshift 4) & 7400B)
RFix40(DataVec,DataVec); @ADREG= 0
]
and MGetRegData(RegX,DataVec) be
[ GetRegData(RegX,DataVec); XctL36(LDEREG,EREG); @ADREG = 0
]
//Change register data
and PutRegData(RegX,DataVec) be
[ switchon RegX into [
case 0: XctL8(LDX,DataVec); X = DataVec!0; return
case 1: XctL4(LDAC,DataVec); return
case 2: XctL9(LDY,DataVec); Y = DataVec!0; return
case 16:
case 3: XctL36(LDP,DataVec); return
case 4: XctL36(LDQ,DataVec); MoveBlock(Q,DataVec,3); return
//Note: cannot read/load SMFORF directly because it is slow.
//Hence, following uses Q as intermediary between SMFORF and E-bus,
//setting F to the new value, and finally restoring Q and SMFORF.
case 5: XctL36(LDQ,DataVec); XctMic(LDSMF)
XctMic(SETFNQ); XctMic(LDSMF); XctL36(CLRFLQ,SMFORF)
XctMic(LDSMF); XctL36(LDQ,Q); MoveBlock(F,DataVec,3); return
case 6: XctL12(LDNPC,DataVec); NPC = DataVec!0; return
case 7: GetRegData(8,TMP); XctL20(LDMAR,DataVec); PutRegData(8,TMP); return
case 8: WFix40(TMP1,DataVec); XctL36(LDMDR,TMP1); XctL8(LDMDRL,TMP1+2); return
case 9: GetRegData(10,TMP); XctL20(LDKMAR,DataVec); PutRegData(10,TMP); return
case 10: WFix40(TMP1,DataVec); XctL36(LDKMDR,TMP1); XctL8(LDKMDRL,TMP1+2); return
case 11: XctL36(LDARM,DataVec); return
case 12: XctL12(LDNPC,DataVec); XctL12(BRNIMA,lv NPC); IMA = DataVec!0; return
case 13: TMP!0 = DataVec!0 rshift 1; XctL4(LDKUN,TMP); return
case 14: XctL36(LDEREG,DataVec); MoveBlock(EREG,DataVec,3); return
case 15: XctL20(LDBPC,DataVec); return
default: CallSwat(); return
]
]
and MPutRegData(RegX,DataVec) be
[ PutRegData(RegX,DataVec); XctL36(LDEREG,EREG); MPDSwitchPhase()
]
//Get memory data
and GetMemData(MemX,AddrVec,DataVec) = valof
[ switchon ConvertAV(AddrVec,MemX) into
[
case 0: XctMic(RDIMH); XctR36(RDEREG,DataVec); endcase
case 1: XctMic(RDIML); XctR36(RDEREG,DataVec); endcase
case 2: XctMic(RDIMH); XctR36(RDEREG,DataVec)
XctMic(RDIML); XctR36(RDEREG,TMP); SetIMRd(DataVec); endcase
case 3: XctR36(RDSM,DataVec); endcase
case 4: XctR36(RDDM,DataVec); endcase
case 5: XctR18(RDMAP,DataVec); endcase
case 6: SetupMAIN(table [ FETCH; RTN ] ); RFix40(DataVec,TMP1); endcase
case 7: XctR36(RDRM,DataVec); endcase //Very clever here--see LOADER.MC
case 8: XctR36(RDLM,DataVec); endcase
case 9: DataVec!0 = STK!MADDRL; endcase
case 10: XctR36(RDDM1,DataVec); endcase
case 11: XctR36(RDDM2,DataVec); endcase
case 12: MoveBlock(DataVec,LADDR,5); endcase
case 13: GetRegData(11,TMP) //Return -1 if int in progress
if TMP!0 < 0 do
[ MoveBlock(DataVec,table [ -1; -1; 170000B ] , 3); endcase ]
GetRegData(13,TMP) //Preserve KUNIT
MADDRL = MADDRL lshift 13; PutRegData(13,lv MADDRL)
XctR36(RDKSTAT,DataVec); PutRegData(13,TMP); endcase
default: resultis false
]
resultis true
]
and MGetMemData(MemX,AddrVec,DataVec) = valof
[ test GetMemData(MemX,AddrVec,DataVec)
ifso [ RestoreMRegs(MemX); resultis true ]
ifnot resultis false
]
//Change memory data (analogous to GetMemData)
and PutMemData(MemX,AddrVec,DataVec) = valof
[ switchon ConvertAV(AddrVec,MemX) into
[
//Have to load EREG first, then IM so that parity gets computed.
//Otherwise, could go direct from BR to IM
case 0: XctL36(LDEREG,DataVec); XctMic(LDIMH); endcase
case 1: XctL36(LDEREG,DataVec); XctMic(LDIML); endcase
case 2: SetIMLd(DataVec)
XctL36(LDEREG,DataVec); XctMic(LDIMH)
XctL36(LDEREG,TMP); XctMic(LDIML); endcase
case 3: XctL36(LDQ,DataVec); XctMic(LDSM)
if MADDRL eq 377B then MoveBlock(SMFORF,DataVec,3); endcase
case 4: XctL36(LDQ,DataVec); XctMic(LDDM); endcase
case 5: XctL18(LDQ,DataVec); XctMic(LDMAP); endcase
case 6: WFix40(TMP1,DataVec); SetupMAIN(table [ STORE; RTN ] ); endcase
case 7: XctL36(LDQ,DataVec); XctMic(LDRM); endcase
case 8: XctL36(LDQ,DataVec); XctMic(LDLM); endcase
case 9: STK!MADDRL = (DataVec!0) & 177760B
RestoreSNI(); ReadAllRegs(); endcase
case 10: XctL36(LDQ,DataVec); XctMic(LDDM1); endcase
case 11: XctL36(LDQ,DataVec); XctMic(LDDM2); endcase
case 12: MoveBlock(LADDR,DataVec,5); endcase
case 13: DisplayError("Read-only register")
default: resultis false
]
resultis true
]
and MPutMemData(MemX,AddrVec,DataVec) = valof
[ test PutMemData(MemX,AddrVec,DataVec)
ifso [ RestoreMRegs(MemX); MPDSwitchPhase(); resultis true ]
ifnot resultis false
]
and BreakIML(AddrVec,BPBit) be
[ MGetMemData(2,AddrVec,INSTST)
INSTST!3 = ((INSTST!3) & not 100000B)%BPBit
MPutMemData(2,AddrVec,INSTST)
]
//Insert break point at address (error if no address given)
and InsertBreak(Addr; numargs Zot) be
[ let Avec = vec 1; Avec!0 = 0; Avec!1 = Addr
test Zot eq 0
ifso [ WssCSS("No address typed"); Blink() ]
ifnot
[ BreakIML(Avec,100000B)
WssCSS("Inserted break at")
Wos(CmdCommentStream,Addr)
]
]
//Remove break point at address (or at IMA if no address given)
and RemoveBreak(Addr; numargs Zot) be
[ let Avec = vec 1; Avec!0 = 0; Avec!1 = Addr
if Zot eq 0 then Avec!1 = IMA rshift 4
BreakIML(Avec,0)
WssCSS("Removed break at")
Wos(CmdCommentStream,Avec!1)
]
//If Zot is 0 then return (step or start at current address)
//else load IMA with address, NPC with address+1, and clear interrupts.
and SetupNPCIMA(Addr,Zot) be
[ if Zot ne 0 do
[ IMA = Addr lshift 4; NPC = IMA+20B
RestoreMRegs(2); XctMic(INTRETN)
]
MADDRL = IMA rshift 4
]
//Single step microprocessor at address (continue at IMA if no address given)
and SingleStepM(Addr; numargs Zot) be
[ SetupNPCIMA(Addr,Zot)
WssCSS(Zot ne 0 ? "Step at","Next step from")
Wos(CmdCommentStream,MADDRL)
ResetMaxc(SINSTP); ReadAllRegs()
ShowIMSym(CmdCommentStream,", halt at ",IMA rshift 4)
]
and ShowIMSym(Stream,Str,Addr) be
[ Wss(Stream,Str)
let AVec = vec 1; AVec!0 = 0; AVec!1 = Addr
SearchBlocks(Stream,2,AVec)
]
and ResetMaxc(Inst) be
[ @ADREG = RESR; @OUTREG = 125B //Reset memory error latches
let Zot = @ERRINF //Reset Alto FER latch
XctMic(WKWRES) //Unhang KRMW and RMW
let ARMvec = vec 2; GetRegData(11,ARMvec)
ARMvec!0 = (ARMvec!0 & 16B) lshift 3
PutRegData(11,ARMvec); XctL36(LDEREG,EREG); XctMic(Inst); @ADREG = 0
]
//Start at address.
and StartM(Addr; numargs Zot) = valof
[ SetupNPCIMA(Addr,Zot)
ResetMaxc(MGO)
WssCSS(Zot eq 0 ? "Continue at","Go at")
Wos(CmdCommentStream,MADDRL)
QUITact = CreateAction("Abort",lv HaltMaxc,0,0,$C-100B)
QuitF = AddToEveryTimeList(HaltWait,0)
resultis HaltWaitMenu
]
and HaltWait(Nix) be
[ @ADREG = RUN; let Z = @INREG
if (Z & 200B) eq 0 do
[ Z = @INREG
if (Z & 200B) ne 0 do
[ WssCSS("RG "); UpdateDisplay(); return ]
WssCSS(", Halt ")
if (Z & 100B) eq 0 then WssCSS("Breakpoint ")
if (Z & 160B) ne 0 then ErrorProtect(lv ReportPE,Z)
MaxcStopped(true)
]
]
and HaltMaxc(Nix) be
[ XctMic(MSTOP); Resets(CmdCommentStream)
WssCSS("Halted by mouse"); MaxcStopped()
]
and MaxcStopped(ShowF) be
[ if QuitF ge 0 do
[ ReadAllRegs(); RemoveFromEveryTimeList(QuitF) ]
if ShowF then ShowIMSym(CmdCommentStream," at ",IMA rshift 4)
QuitCmdOverlay()
]
and HaltWaitMenu(S,Nix) be WsMarkA(QUITact)
//Wait for X seconds
and Wait(X) be
[ for I = 0 to X do
[ for J = 0 to 37777B do [ ]
]
return
]
and ProcOnTest() = valof
[ @ADREG = PPSTAT; let X = @INREG & 3
resultis X eq 3 ? true,false
]
and PrintIMA(Stream,X,DVec,AVec) be
[ ShowIMSym(Stream,"",DVec!0 rshift 4)
for N = 0 to 9 by 3 do
[ CharInputRoutine(GetField(N,3,DVec)+$0 ) ]
ClearInText()
]
and PrintDM(Stream,X,DVec,AVec) be
[ ShowIMSym(Stream,"",GetField(1,11,DVec))
ShowIMSym(Stream,",",GetField(12,12,DVec))
ShowIMSym(Stream,",",GetField(24,12,DVec))
if DVec!0 ge 0 do [ Wss(Stream," [immediate]") ]
]
and InitHardware() be
[ @ERRENB = 16B //Enable FER interrupt only
ReadAllRegs()
]
and FinishHardware() be StartIO(#100000)