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