<> <> DIRECTORY Boot USING [ReadMDS], BootSwap USING [InLoad, OutLoad, Teledebug], CPSwapDefs USING [ BBArray, BBHandle, CallDP, DebugParameter, ExternalStateVector, SwapInfo, StartDP, SwapReason, UBBPointer, UserBreakBlock, VersionID], DebuggerSwap USING [canSwap, parameters], DeviceCleanup USING [Item, Linkage, Perform, Reason], Environment USING [Byte, maxPagesInMDS, PageCount, PageNumber], Frame USING [ GetReturnFrame, GetReturnLink, MyLocalFrame, SetReturnFrame, SetReturnLink], Inline USING [HighHalf, LowHalf], KeyboardFace USING [keyboard], Keys USING [DownUp, KeyBits], Mopcodes USING [zDUP, zRFS], PilotMP USING [cCantSwap, cCleanup, cClient, cHang, Code], PilotSwitches USING [switches--.h, .i, .r--], PrincOps USING [ ControlLink, FieldDescriptor, FrameHandle, GlobalFrameHandle, NullFrame, NullLink, Port, StateVector, SVPointer], Process USING [GetPriority, Priority, SetPriority, SetTimeout, Ticks], ProcessInternal USING [DisableInterrupts, EnableInterrupts], ProcessOperations USING [HandleToIndex, IndexToHandle, ReadPSB, ReadPTC, ReadWDC, WritePSB, WritePTC, WriteWDC], ProcessPriorities USING [priorityRealTime], ProcessorFace USING [BootButton, GetClockPulses, microsecondsPerHundredPulses, SetMP], PSB USING [PDA], Runtime USING [Interrupt], RuntimeInternal USING [], RuntimePrograms USING [], SDDefs USING [ sAlternateBreak, sBreak, sBreakBlock, sBreakBlockSize, sCallDebugger, sCoreSwap, SD, sInterrupt, sProcessBreakpoint, sUncaughtSignal, sWorryCallDebugger, sXferTrap], XferTrap USING [ReadXTS, Status, WriteXTS]; PilotNub: MONITOR -- for CheckInterrupt, DeviceCleanup.Install IMPORTS Boot, BootSwap, DebuggerSwap, DeviceCleanup, Frame, Inline, KeyboardFace, PilotSwitches, Process, ProcessInternal, ProcessOperations, ProcessorFace, Runtime, XferTrap EXPORTS DeviceCleanup, RuntimeInternal, RuntimePrograms SHARES DeviceCleanup = BEGIN <> loadStatePage: PUBLIC Environment.PageNumber; -- exported to RuntimeInternal CAbort: PUBLIC SIGNAL = CODE; CantSwap: PUBLIC SIGNAL = CODE; KillThisTurkey: SIGNAL = CODE; Quit: SIGNAL = CODE; <<>> <> numberBlocks: CARDINAL = 5; -- number of break blocks BreakBlocks: TYPE = MACHINE DEPENDENT RECORD [ header(0): CPSwapDefs.BBArray, -- length plus (zero-length) array of blocks body(1): ARRAY [0..numberBlocks) OF CPSwapDefs.UserBreakBlock]; breakBlocks: BreakBlocks; InitBreakBlocks: PROC = BEGIN OPEN SDDefs; SD[sBreakBlock] _ Inline.LowHalf[LONG[@breakBlocks]]; <> SD[sBreakBlockSize] _ SIZE[BreakBlocks]; breakBlocks.header.length _ 0; END; Break: PROC = -- executed by (non-worry) BRK instruction BEGIN state: RECORD [ padding: ARRAY [0..2) OF UNSPECIFIED, v: PrincOps.StateVector]; state.v _ STATE; state.v.dest _ Frame.GetReturnLink[]; state.v.source _ PrincOps.NullLink; ProcessBreakpoint[@state.v]; -- isn't this supposed to go through SD[sProcessBreakpoint]? IF XferTrap.ReadXTS[] = on THEN XferTrap.WriteXTS[skip1]; RETURN WITH state.v; END; ProcessBreakpoint: PROC [s: PrincOps.SVPointer] = BEGIN inst: Environment.Byte; swap: BOOLEAN; [inst, swap] _ DoBreakpoint[s]; IF swap THEN CoreSwap[breakpoint, s] ELSE s.instbyte _ inst -- replant the instruction and go on END; <> DoBreakpoint: PROC [s: PrincOps.SVPointer] RETURNS [Environment.Byte, BOOLEAN] = BEGIN bba: CPSwapDefs.BBHandle = SDDefs.SD[SDDefs.sBreakBlock]; l: PrincOps.FrameHandle _ LOOPHOLE[s.frame]; FOR i: CARDINAL IN [0..bba.length) DO ubb: CPSwapDefs.UBBPointer = @bba.blocks[i]; IF ubb.frame = l.accesslink AND ubb.pc = CARDINAL[l.pc] THEN IF TrueCondition[ubb, l, s] THEN EXIT ELSE RETURN[ubb.inst, FALSE] ENDLOOP; RETURN[0, TRUE] END; <> TrueCondition: PROC [ ubb: CPSwapDefs.UBBPointer, base: POINTER, s: PrincOps.SVPointer] RETURNS [BOOLEAN] = INLINE BEGIN ReadField: PROC [POINTER, PrincOps.FieldDescriptor] RETURNS [UNSPECIFIED] = MACHINE CODE BEGIN Mopcodes.zRFS END; fd: PrincOps.FieldDescriptor; locL, locR: POINTER; left, right: UNSPECIFIED; IF ubb.counterL THEN RETURN[(ubb.ptrL _ ubb.ptrL + 1) = ubb.ptrR]; locL _ SELECT TRUE FROM ubb.stackRelative => @s.stk[LOOPHOLE[ubb.ptrL, CARDINAL]], ubb.localL => base + LOOPHOLE[ubb.ptrL, CARDINAL], ENDCASE => ubb.ptrL; fd _ [offset: 0, posn: ubb.posnL, size: ubb.sizeL]; left _ ReadField[locL, fd]; IF ~ubb.immediateR THEN BEGIN fd _ [offset: 0, posn: ubb.posnR, size: ubb.sizeR]; locR _ IF ubb.localR THEN base + LOOPHOLE[ubb.ptrR, CARDINAL] ELSE ubb.ptrR; right _ ReadField[locR, fd] END ELSE right _ ubb.ptrR; RETURN[ SELECT ubb.relation FROM lt => left < right, gt => left > right, eq => left = right, ne => left # right, le => left <= right, ge => left >= right, ENDCASE => FALSE] END; <> WorryBreaker: PROC RETURNS [PrincOps.FrameHandle] = BEGIN OPEN PrincOps; state: RECORD [padding: ARRAY [0..2) OF UNSPECIFIED, v: StateVector]; state.v.instbyte _ 0; state.v.stkptr _ 1; state.v.stk[0] _ Frame.MyLocalFrame[]; state.v.dest _ Frame.GetReturnLink[]; state.v.source _ PrincOps.NullLink; ProcessInternal.DisableInterrupts[]; DO IF XferTrap.ReadXTS[] = on THEN XferTrap.WriteXTS[skip1]; ProcessInternal.EnableInterrupts[]; TRANSFER WITH state.v; ProcessInternal.DisableInterrupts[]; state.v _ STATE; state.v.dest _ Frame.GetReturnLink[]; state.v.source _ PrincOps.NullLink; swapInfo.state _ @state.v; swapInfo.reason _ worrybreak; -- set mds too ToDebugger[@swapInfo]; ENDLOOP; END; <<>> <> Catcher: PROC [msg, signal: UNSPECIFIED, frame: PrincOps.FrameHandle] = BEGIN Punt: PROC [c: PilotMP.Code] = INLINE {ProcessorFace.SetMP[c]; DO ENDLOOP}; SignallerGF: PrincOps.GlobalFrameHandle; state: PrincOps.StateVector; f: PrincOps.FrameHandle; state.stk[0] _ msg; state.stk[1] _ signal; state.stkptr _ 0; <> f _ Frame.GetReturnFrame[]; SignallerGF _ f.accesslink; f _ LOOPHOLE[state.dest _ f.returnlink, PrincOps.FrameHandle]; IF f.accesslink = SignallerGF THEN state.dest _ f.returnlink; IF signal = CantSwap THEN Punt[PilotMP.cCantSwap]; CoreSwap[uncaughtsignal, @state ! CAbort => GOTO abort]; EXITS abort => IF signal = ABORTED THEN {BackStop[frame]; ERROR KillThisTurkey} ELSE ERROR ABORTED; END; BackStop: PROC [root: PrincOps.FrameHandle] = BEGIN endProcess: PrincOps.ControlLink = root.returnlink; Caller: PROC = LOOPHOLE[Frame.GetReturnLink[]]; root.returnlink _ [frame[Frame.MyLocalFrame[]]]; Frame.SetReturnFrame[PrincOps.NullFrame]; Caller[ ! KillThisTurkey => CONTINUE]; Frame.SetReturnLink[endProcess]; END; <<~~~~~~~~~ Interrupts (e.g. CTRL-SWAT) ~~~~~~~~~>> wakeup: CONDITION; ticksPerWakeup: Process.Ticks = 1; CheckInterrupt: ENTRY PROC = -- default CTRL-SWAT watcher BEGIN interruptState: Keys.DownUp _ up; pKeys: LONG POINTER TO Keys.KeyBits = LOOPHOLE[KeyboardFace.keyboard]; DO --FOREVER-- ENABLE ABORTED => CONTINUE; WAIT wakeup; IF pKeys[Spare3]=down AND pKeys[Ctrl]=down AND pKeys[LeftShift]=up AND interruptState = up AND LOOPHOLE[PSB.PDA.available, CPSwapDefs.SwapInfo] .externalStateVector ~= NIL THEN { interruptState _ down; Runtime.Interrupt[] } ELSE interruptState _ up; ENDLOOP; END; Interrupt: PROC = -- implementation of Runtime.Interrupt BEGIN state: RECORD [a, b: UNSPECIFIED, v: PrincOps.StateVector]; state.v _ STATE; state.v.dest _ LOOPHOLE[Frame.MyLocalFrame[], PrincOps.ControlLink]; CoreSwap[interrupt, @state.v] END; InitializeInterrupt: PUBLIC PROC = <> <> BEGIN IF PilotSwitches.switches.i = down THEN BEGIN throwAway: PROCESS; priorityPrev: Process.Priority = Process.GetPriority[]; Process.SetTimeout[@wakeup, ticksPerWakeup]; Process.SetPriority[ProcessPriorities.priorityRealTime]; throwAway _ FORK CheckInterrupt[]; -- (no profit in Detaching) Process.SetPriority[priorityPrev]; END; END; <<>> <> CallDebugger: PROC [s: STRING] = <> BEGIN state: RECORD [a, b: UNSPECIFIED, v: PrincOps.StateVector]; state.v _ STATE; state.v.stk[0] _ s; state.v.stkptr _ 1; state.v.dest _ Frame.GetReturnLink[]; CoreSwap[explicitcall, @state.v] END; worryCallIndirect: PORT; WorryCallDebugger: PROC RETURNS [PrincOps.FrameHandle] = BEGIN OPEN PrincOps; state: RECORD [padding: ARRAY [0..2) OF UNSPECIFIED, v: StateVector]; state.v.instbyte _ 0; state.v.stkptr _ 1; <> <> <> LOOPHOLE[worryCallIndirect, PrincOps.Port].frame _ Frame.MyLocalFrame[]; state.v.stk[0] _ @worryCallIndirect; state.v.dest _ Frame.GetReturnLink[]; state.v.source _ PrincOps.NullLink; ProcessInternal.DisableInterrupts[]; DO --FOREVER-- IF XferTrap.ReadXTS[] = on THEN XferTrap.WriteXTS[skip1]; ProcessInternal.EnableInterrupts[]; TRANSFER WITH state.v; ProcessInternal.DisableInterrupts[]; state.v _ STATE; state.v.dest _ state.v.stk[state.v.stkptr + 1]; Frame.SetReturnLink[state.v.dest]; state.v.source _ PrincOps.NullLink; swapInfo.state _ @state.v; swapInfo.reason _ worrycall; ToDebugger[@swapInfo]; ENDLOOP END; CleanMapLog: PUBLIC PROC = BEGIN state: RECORD [a, b: UNSPECIFIED, v: PrincOps.StateVector]; state.v _ STATE; state.v.stkptr _ 0; state.v.dest _ Frame.GetReturnLink[]; CoreSwap[cleanmaplog, @state.v] END; <> swapInfo: CPSwapDefs.ExternalStateVector; -- nub-debugger communication area parmstring: STRING = [40]; GetMDS: PROC RETURNS [Environment.PageNumber] = BEGIN RETURN[Boot.ReadMDS[]*Environment.maxPagesInMDS] END; CoreSwap: PROC [why: CPSwapDefs.SwapReason, sp: PrincOps.SVPointer] = BEGIN DP: CPSwapDefs.DebugParameter; decode: PROC RETURNS [proceed: BOOLEAN] = -- decode the SwapReason BEGIN f: PrincOps.GlobalFrameHandle; lsv: PrincOps.StateVector; SELECT swapInfo.reason FROM proceed, resume => RETURN[TRUE]; call => BEGIN lsv _ LOOPHOLE[swapInfo.parameter, CPSwapDefs.CallDP].sv; lsv.source _ LOOPHOLE[Frame.MyLocalFrame[], PrincOps.ControlLink]; TRANSFER WITH lsv; lsv _ STATE; LOOPHOLE[swapInfo.parameter, CPSwapDefs.CallDP].sv _ lsv; why _ return END; start => BEGIN f _ LOOPHOLE[swapInfo.parameter, CPSwapDefs.StartDP].frame; IF ~f.started THEN START LOOPHOLE[f, PROGRAM] ELSE RESTART f; why _ return END; quit => SIGNAL Quit; ENDCASE => RETURN[TRUE]; RETURN[FALSE] END; --decode-- <> <> swapInfo.state _ sp; DP.string _ parmstring; swapInfo.parameter _ @DP; <> <> swapInfo.mds _ GetMDS[]; DO swapInfo.reason _ why; ProcessInternal.DisableInterrupts[]; ToDebugger[@swapInfo]; ProcessInternal.EnableInterrupts[]; BEGIN IF decode[ ! CAbort => IF swapInfo.level > 0 THEN {why _ return; CONTINUE}; Quit => GOTO abort] THEN EXIT EXITS abort => SIGNAL CAbort END ENDLOOP END; <> level: INTEGER _ -1; ToDebugger: PORT [POINTER TO CPSwapDefs.ExternalStateVector]; -- formerly WBPort FromPilot: PORT RETURNS [POINTER TO CPSwapDefs.ExternalStateVector]; -- formerly CSPort pulsesPerTwentySeconds: LONG CARDINAL; MemorySwap: PROC [pESV: POINTER TO CPSwapDefs.ExternalStateVector] = BEGIN PKeys: PROC RETURNS [LONG POINTER TO Keys.KeyBits] = INLINE {RETURN[LOOPHOLE[KeyboardFace.keyboard]]}; SwapIt: PROC = INLINE BEGIN savewdc, saveptc: UNSPECIFIED; xferTrapStatus: XferTrap.Status = XferTrap.ReadXTS[]; xferTrapHandler: UNSPECIFIED = SDDefs.SD[SDDefs.sXferTrap]; pESV.level _ level; pESV.loadstatepage _ loadStatePage; -- (possibly updated by loader) pESV.mds _ Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]]; -- ("1" since <<0 collides with NIL.)>> pESV.psb _ ProcessOperations.HandleToIndex[ProcessOperations.ReadPSB[]]; SDDefs.SD[SDDefs.sXferTrap] _ Frame.MyLocalFrame[]; -- in case we are restarted in trap mode XferTrap.WriteXTS[off]; <> saveptc _ ProcessOperations.ReadPTC[]; savewdc _ ProcessOperations.ReadWDC[]; <> <> PSB.PDA.block[pESV.psb].context.frame _ Frame.MyLocalFrame[]; DeviceCleanup.Perform[turnOff]; -- turn all devices off IF PilotSwitches.switches.h = down THEN BEGIN AddToStack: PROC [BOOLEAN] = MACHINE CODE BEGIN END; GetTOS: PROC RETURNS [BOOLEAN] = MACHINE CODE BEGIN Mopcodes.zDUP; END; RemoveFromStack: PROC RETURNS [BOOLEAN] = MACHINE CODE BEGIN END; AddToStack[TRUE]; ProcessorFace.SetMP[PilotMP.cHang]; WHILE GetTOS[] DO ENDLOOP; [] _ RemoveFromStack[]; END ELSE IF ~DebuggerSwap.canSwap OR PilotSwitches.switches.r = down THEN BEGIN ProcessorFace.SetMP[PilotMP.cCantSwap]; BootSwap.Teledebug[@DebuggerSwap.parameters.locDebugger]; END <> ELSE IF BootSwap.OutLoad[@DebuggerSwap.parameters.locDebuggee, restore] = outLoaded THEN BEGIN OPEN DebuggerSwap, parameters; <> IF pMicrocodeCopy ~= NIL THEN DeviceCleanup.Perform[kill]; BootSwap.InLoad[pMicrocodeCopy, pGermCopy, nGerm, @locDebugger] <> END; <> ProcessOperations.WriteWDC[savewdc]; ProcessOperations.WritePTC[saveptc]; <> ProcessOperations.WritePSB[ProcessOperations.IndexToHandle[pESV.psb]]; DeviceCleanup.Perform[turnOn]; -- turn devices back on level _ pESV.level; XferTrap.WriteXTS[xferTrapStatus]; SDDefs.SD[SDDefs.sXferTrap] _ xferTrapHandler; ProcessorFace.SetMP[PilotMP.cClient]; -- announce our return END; DO pESV _ FromPilot[]; <> Frame.SetReturnLink[LOOPHOLE[FromPilot, PrincOps.Port].dest]; DO SwapIt[]; SELECT pESV.reason FROM kill => ProcessorFace.BootButton[]; showscreen => BEGIN pulsesThen: LONG CARDINAL = ProcessorFace.GetClockPulses[]; prevSpare3: Keys.DownUp _ PKeys[][Spare3]; DO IF (ProcessorFace.GetClockPulses[] - pulsesThen) > pulsesPerTwentySeconds THEN EXIT; IF prevSpare3=down THEN prevSpare3 _ PKeys[][Spare3] ELSE IF PKeys[][Spare3]=down THEN {WHILE PKeys[][Spare3]=down DO --snoore-- ENDLOOP; EXIT} ELSE NULL -- both up ENDLOOP; END; ENDCASE => EXIT; pESV.reason _ return; ENDLOOP ENDLOOP END; <<>> <> linkage: PUBLIC DeviceCleanup.Linkage; InitializeDeviceCleanup: PROC = BEGIN reason: DeviceCleanup.Reason; pItem: POINTER TO Item; LOOPHOLE[AwaitPerform, PrincOps.Port].dest _ Frame.GetReturnLink[]; linkage.Perform _ LOOPHOLE[@AwaitPerform]; DO linkage.Await _ LOOPHOLE[Install]; reason _ AwaitPerform[]; ProcessorFace.SetMP[PilotMP.cCleanup]; linkage.Await _ LOOPHOLE[Frame.MyLocalFrame[]]; FOR pItem _ pItemFirst, pItem.pItemNext WHILE pItem ~= NIL DO [] _ pItem.Procedure[reason] -- value should be pItem ENDLOOP ENDLOOP END; AwaitPerform: PORT RETURNS [reason: DeviceCleanup.Reason]; Install: ENTRY PROC [pItem: POINTER TO Item] = BEGIN fCaller: PrincOps.FrameHandle = Frame.GetReturnFrame[]; -- cleanup procedure pItem^ _ [pItemNext: pItemFirst, Procedure: LOOPHOLE[fCaller]]; pItemFirst _ pItem; Frame.SetReturnLink[fCaller.returnlink] END; Item: PUBLIC TYPE = RECORD [ pItemNext: POINTER TO Item, Procedure: PROC [DeviceCleanup.Reason] RETURNS [POINTER TO Item] _ NULL]; pItemFirst: POINTER TO Item _ NIL; -- list of waiting cleanup procedures <<>> <> InitializePilotNub: PUBLIC PROC [ pageLoadState: Environment.PageNumber, countLoadState: Environment.PageCount, pVMMapLog: LONG POINTER --TO VMMMapLog.Descriptor--] = BEGIN InitializeDeviceCleanup[]; loadStatePage _ pageLoadState; <> <<(e.g. CoreSwap, WorryBreaker, WorryCallDebugger)>> <> <<(e.g. CoreSwap, WorryBreaker, WorryCallDebugger)>> <> <> swapInfo.versionident _ CPSwapDefs.VersionID; <> swapInfo.lspages _ countLoadState; swapInfo.mapLog _ pVMMapLog; swapInfo.mds _ GetMDS[]; -- delete this when WorryBreak, etc. set the field swapInfo.fill _ ALL[0]; LOOPHOLE[PSB.PDA.available, CPSwapDefs.SwapInfo].externalStateVector _ @swapInfo; InitBreakBlocks[]; BEGIN OPEN SDDefs; pSD: POINTER TO ARRAY [0..0) OF UNSPECIFIED = SD; pSD[sProcessBreakpoint] _ ProcessBreakpoint; pSD[sUncaughtSignal] _ Catcher; pSD[sInterrupt] _ Interrupt; pSD[sCallDebugger] _ CallDebugger; pSD[sBreak] _ Break; pSD[sAlternateBreak] _ WorryBreaker[]; pSD[sWorryCallDebugger] _ WorryCallDebugger[]; pulsesPerTwentySeconds _ LONG[20]*1000000*100/ProcessorFace.microsecondsPerHundredPulses; LOOPHOLE[ToDebugger, PrincOps.Port].out _ @FromPilot; <> LOOPHOLE[FromPilot, PrincOps.Port].out _ @ToDebugger; <> pSD[sCoreSwap] _ @FromPilot; LOOPHOLE[FromPilot, PrincOps.Port].in _ MemorySwap; ToDebugger[NIL]; -- allocate frame for MemorySwap END; END; END.