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.