PilotNub.mesa
last edited by Levin on March 11, 1983 4:40 pm
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
This module is the debugger's representative in the client world.
loadStatePage: PUBLIC Environment.PageNumber; -- exported to RuntimeInternal
CAbort: PUBLIC SIGNAL = CODE;
CantSwap: PUBLIC SIGNAL = CODE;
KillThisTurkey: SIGNAL = CODE;
Quit: SIGNAL = CODE;
Breakpoints
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]];
mds relative (should really be long pointer)
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;
make this INLINE someday??
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;
decide whether to take the breakpoint
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;
executed by worry-mode BRK instruction
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;
Uncaught signals
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;
The call stack below here is: Signaller, [Signaller,] offender
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 =
Initialize Pilot's CTRL-SWAT watcher (if one was requested).
Must not be invoked until KeyboardFace has been initialized.
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;
Miscellaneous Runtime, RuntimeInternal items
CallDebugger: PROC [s: STRING] =
Runtime.CallDebugger is KFCB[sCallDebugger]
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;
The following dance is needed because fixed frame procedures that are not
called as trap handlers don't have source & destination links put on the
stack. We insert an indirect link to force them to be put there.
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;
Procedures that cause swap to debugger
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--
Body of CoreSwap:
IF ~DebuggerSwap.canSwap THEN SIGNAL CantSwap;
swapInfo.state ← sp;
DP.string ← parmstring;
swapInfo.parameter ← @DP;
versionident, debuggee, lspages, fill, mapLog set by Initialize
level, loadstatepage set by MemorySwap
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;
Serious swapper
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];
Save processor state not captured in PDA:
saveptc ← ProcessOperations.ReadPTC[];
savewdc ← ProcessOperations.ReadWDC[];
Manually save the state of the current process so that Copilot
will be able to examine it just as if we were waiting:
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
OutLoad onto swatee, then boot Debugger.
ELSE IF BootSwap.OutLoad[@DebuggerSwap.parameters.locDebuggee, restore] =
outLoaded THEN
BEGIN OPEN DebuggerSwap, parameters;
The next line should be in BootSwap.InLoad but blows up the compiler
IF pMicrocodeCopy ~= NIL THEN DeviceCleanup.Perform[kill];
BootSwap.InLoad[pMicrocodeCopy, pGermCopy, nGerm, @locDebugger]
never returns
END;
Restore processor state not captured in PDA:
ProcessOperations.WriteWDC[savewdc];
ProcessOperations.WritePTC[saveptc];
Restore process state not captured in PDA:
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[];
Set our return link so Display Stack will work:
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;
DeviceCleanup implementation
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
Initialization
InitializePilotNub: PUBLIC PROC [
pageLoadState: Environment.PageNumber,
countLoadState: Environment.PageCount,
pVMMapLog: LONG POINTER --TO VMMMapLog.Descriptor--] =
BEGIN
InitializeDeviceCleanup[];
loadStatePage ← pageLoadState;
swapInfo.state set in caller of MemorySwap
(e.g. CoreSwap, WorryBreaker, WorryCallDebugger)
swapInfo.reason set in caller of MemorySwap
(e.g. CoreSwap, WorryBreaker, WorryCallDebugger)
swapInfo.level set in MemorySwap
swapInfo.parameter set by debugger and obeyed by CoreSwap
swapInfo.versionident ← CPSwapDefs.VersionID;
swapInfo.loadstatepage set in MemorySwap
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;
connect ToDebugger to FromPilot
LOOPHOLE[FromPilot, PrincOps.Port].out ← @ToDebugger;
connect FromPilot to ToDebugger
pSD[sCoreSwap] ← @FromPilot;
LOOPHOLE[FromPilot, PrincOps.Port].in ← MemorySwap;
ToDebugger[NIL]; -- allocate frame for MemorySwap
END;
END;
END.