-- DebugNub.mesa; edited by Sandman October 9, 1980 9:57 AM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
AltoDefs USING [BYTE],
BcplOps USING [BcplJSR],
BFSDefs USING [MakeCFP],
ControlDefs USING [
ControlLink, FieldDescriptor, FrameHandle, GlobalFrameHandle, NullFrame,
StateVector, SVPointer],
CoreSwapDefs USING [
BBArray, BBHandle, CallDP, DebugParameter, VersionID, ExternalStateVector,
level, PuntInfo, PuntTable, StartDP, SVPointer, SwapReason, UBBPointer,
UserBreakBlock],
DiskDefs USING [RealDA],
ForgotDefs USING [BitmapDS],
FrameDefs USING [LockCode, UnlockCode, UnNew],
FrameOps USING [
GetReturnFrame, GetReturnLink, MyLocalFrame, SetReturnFrame, SetReturnLink],
ImageDefs USING [
AbortMesa, AddCleanupProcedure, AddFileRequest, AllReasons, CleanupItem,
CleanupMask, CleanupProcedure, FileRequest, PuntMesa, StopMesa,
UserCleanupProc],
KeyDefs USING [Keys],
LoadStateOps USING [state],
MiscOps USING [BitmapPages],
Mopcodes USING [zRFS],
NubOps USING [Place],
NucleusOps USING [Wart],
ProcessDefs USING [DisableInterrupts, EnableInterrupts],
SDDefs USING [
sBreakBlock, sBreakBlockSize, sCallDebugger, sCoreSwap, SD, sInterrupt,
sProcessBreakpoint, sUncaughtSignal],
SegmentDefs USING [
DataSegmentHandle, DefaultXMBase, DeleteDataSegment, DeleteFileSegment,
FileHandle, FileSegmentHandle, GetFileSegmentDA, InsufficientVM,
LongDataSegmentAddress, LongVMtoDataSegment, MakeDataSegment, memConfig,
NewFileSegment, Read, ReleaseFile, UnlockFile],
SwapperOps USING [systemTable],
Storage USING [Node, CopyString, Free, FreeString];
DebugNub: PROGRAM [user: PROGRAM]
IMPORTS
BFSDefs, CoreSwapDefs, DiskDefs, FrameDefs, FrameOps, ImageDefs, LoadStateOps,
BcplOps, NucleusOps, ProcessDefs, SegmentDefs, SwapperOps, Storage
EXPORTS CoreSwapDefs, MiscOps, NubOps
SHARES SegmentDefs, ControlDefs =
BEGIN OPEN CoreSwapDefs, SegmentDefs;
WhereAmI: PUBLIC PROCEDURE RETURNS [NubOps.Place] = {RETURN[normal]};
FrameHandle: TYPE = ControlDefs.FrameHandle;
SVPointer: TYPE = ControlDefs.SVPointer;
ProcessBreakpoint: PROCEDURE [s: SVPointer] =
BEGIN -- called by BRK trap handler in resident code
inst: AltoDefs.BYTE;
swap: BOOLEAN;
IF ~Swappable THEN BEGIN SwatBreak[s]; RETURN END;
[inst, swap] ← DoBreakpoint[s];
IF swap THEN
BEGIN
FrameDefs.LockCode[s.dest];
CoreSwap[breakpoint, s];
FrameDefs.UnlockCode[s.dest];
END
ELSE s.instbyte ← inst; --replant the instruction and go on
RETURN
END;
DoBreakpoint: PROCEDURE [s: SVPointer] RETURNS [AltoDefs.BYTE, BOOLEAN] =
BEGIN OPEN ControlDefs;
ubb: CoreSwapDefs.UBBPointer;
bba: BBHandle = SDDefs.SD[SDDefs.sBreakBlock];
i: CARDINAL;
l: FrameHandle ← s.dest;
FOR i IN [0..bba.length) DO
ubb ← @bba.blocks[i];
IF ubb.frame = l.accesslink AND ubb.pc = l.pc THEN
IF TrueCondition[ubb, l, s] THEN EXIT ELSE RETURN[ubb.inst, FALSE];
ENDLOOP;
RETURN[0, TRUE];
END;
TrueCondition: PROCEDURE [ubb: CoreSwapDefs.UBBPointer, base: POINTER, s: SVPointer]
RETURNS [BOOLEAN] =
BEGIN --decide whether to take the breakpoint
fd: ControlDefs.FieldDescriptor;
locL, locR: POINTER;
left, right: UNSPECIFIED;
IF ubb.counterL THEN
IF (ubb.ptrL ← ubb.ptrL + 1) = ubb.ptrR THEN
BEGIN ubb.ptrL ← LOOPHOLE[0]; RETURN[TRUE] END
ELSE RETURN[FALSE];
locL ← SELECT TRUE FROM
ubb.localL => base + LOOPHOLE[ubb.ptrL, CARDINAL],
ubb.stackRelative => @s.stk[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;
ReadField: PROCEDURE [POINTER, ControlDefs.FieldDescriptor]
RETURNS [UNSPECIFIED] = MACHINE CODE BEGIN Mopcodes.zRFS END;
NumberBlocks: CARDINAL = 5;
InitBreakBlocks: PROCEDURE =
BEGIN OPEN SDDefs;
p: CoreSwapDefs.BBHandle ← Storage.Node[
SIZE[UserBreakBlock]*NumberBlocks + SIZE[BBArray]];
SD[sBreakBlock] ← p;
SD[sBreakBlockSize] ← SIZE[UserBreakBlock]*NumberBlocks + SIZE[BBArray];
p.length ← 0;
RETURN
END;
SwatBreak: PROCEDURE [s: CoreSwapDefs.SVPointer] =
BEGIN OPEN BcplOps;
break: RECORD [a, b: WORD];
break ← [77400B, 1400B];
s.instbyte ← BcplJSR[JSR, @break, 0];
RETURN
END;
Interrupt: PROCEDURE =
BEGIN -- called by BRK trap handler in resident code
state: ControlDefs.StateVector;
state ← STATE;
state.dest ← FrameOps.MyLocalFrame[];
CoreSwap[breakpoint, @state];
END;
Catcher: PROCEDURE [msg, signal: UNSPECIFIED, frame: FrameHandle] =
BEGIN OPEN ControlDefs;
SignallerGF: GlobalFrameHandle;
state: StateVector;
f: FrameHandle;
state.stk[0] ← msg;
state.stk[1] ← signal;
state.stkptr ← 0;
-- the call stack below here is: Signaller, [Signaller,] offender
f ← FrameOps.GetReturnFrame[];
SignallerGF ← f.accesslink;
state.dest ← f ← f.returnlink.frame;
IF f.accesslink = SignallerGF THEN state.dest ← f.returnlink;
IF ~Swappable THEN BEGIN SwatBreak[@state]; RETURN END;
BEGIN
CoreSwap[uncaughtsignal, @state ! ABORTED => IF signal = ABORTED THEN GOTO abort];
EXITS abort => {BackStop[frame]; ERROR KillThisTurkey};
END;
RETURN
END;
BackStop: PROCEDURE [root: FrameHandle] =
BEGIN
endProcess: ControlDefs.ControlLink ← root.returnlink;
caller: PROCEDURE = LOOPHOLE[FrameOps.GetReturnLink[]];
root.returnlink ← LOOPHOLE[FrameOps.MyLocalFrame[]];
FrameOps.SetReturnFrame[ControlDefs.NullFrame];
caller[ ! KillThisTurkey => CONTINUE];
FrameOps.SetReturnLink[endProcess];
RETURN
END;
KillThisTurkey: SIGNAL = CODE;
-- The core swapper
Quit: SIGNAL = CODE;
CantSwap: PUBLIC SIGNAL = CODE;
DoSwap: PORT [POINTER TO ExternalStateVector];
parmstring: STRING ← [40];
CoreSwap: PUBLIC PROCEDURE [why: SwapReason, sp: SVPointer] =
BEGIN OPEN BcplOps;
loadstate: FileSegmentHandle ← LoadStateOps.state;
e: ExternalStateVector;
DP: DebugParameter;
decode: PROCEDURE RETURNS [BOOLEAN] =
BEGIN OPEN ControlDefs; -- decode the SwapReason
f: GlobalFrameHandle;
lsv: StateVector;
SELECT e.reason FROM
proceed, resume => RETURN[TRUE];
call =>
BEGIN
lsv ← LOOPHOLE[e.parameter, CallDP].sv;
lsv.source ← FrameOps.MyLocalFrame[];
TRANSFER WITH lsv;
lsv ← STATE;
LOOPHOLE[e.parameter, CallDP].sv ← lsv;
why ← return;
END;
start =>
BEGIN
f ← LOOPHOLE[e.parameter, StartDP].frame;
IF ~f.started THEN START LOOPHOLE[f, PROGRAM] ELSE RESTART f;
why ← return;
END;
quit => SIGNAL Quit;
kill => ImageDefs.AbortMesa[];
showscreen =>
BEGIN
UNTIL KeyDefs.Keys.Spare3 = down DO NULL ENDLOOP;
why ← return;
END;
ENDCASE => BEGIN RETURN[TRUE]; END;
RETURN[FALSE]
END;
-- Body of CoreSwap
IF ~Swappable THEN SIGNAL CantSwap;
DP.string ← parmstring;
e ←
[state: sp, reason:, level:, tables: @SwapperOps.systemTable,
drumFile: MesaCoreFH, parameter: @DP, versionident: VersionID,
loadstateCFA:
[fp: loadstate.file.fp,
fa: [page: loadstate.base, byte: 0, da: GetFileSegmentDA[loadstate]]],
lspages: loadstate.pages, mds: 0, bitmap: debuggerBitmap,
bitmapPages: bitmapPages, fill: ALL[0]];
DO
e.reason ← why;
ImageDefs.UserCleanupProc[OutLd ! ANY => CONTINUE];
ProcessDefs.DisableInterrupts[];
DoSwap[@e];
ProcessDefs.EnableInterrupts[];
ImageDefs.UserCleanupProc[InLd];
BEGIN
IF decode[
! ABORTED => IF e.level > 0 THEN {why ← return; CONTINUE};
Quit => GOTO abort] THEN EXIT
EXITS abort => ERROR ABORTED;
END;
ENDLOOP;
RETURN
END;
-- initialization
Swappable: BOOLEAN;
puntData: PuntTable;
MesaCoreFH: FileHandle ← NIL;
FindFiles: PROCEDURE =
BEGIN OPEN ControlDefs;
f: FileHandle;
s: FileSegmentHandle;
puntData.puntESV.reason ← punt;
puntData.puntESV.tables ← @SwapperOps.systemTable;
s ← LoadStateOps.state;
puntData.puntESV.versionident ← VersionID;
puntData.puntESV.loadstateCFA.fp ← s.file.fp;
puntData.puntESV.loadstateCFA.fa ←
[page: s.base, byte: 0, da: GetFileSegmentDA[s]];
puntData.puntESV.lspages ← s.pages;
puntData.pDebuggerFP ← puntData.pCoreFP ← LOOPHOLE[0];
puntData.puntESV.bitmap ← NIL;
puntData.puntESV.bitmapPages ← 0;
puntData.puntESV.fill ← ALL[0];
Swappable ← TRUE;
IF (f ← requests[core].file) = NIL THEN
IF (f ← requests[swatee].file) = NIL THEN Swappable ← FALSE ELSE NULL
ELSE
IF requests[swatee].file # NIL THEN
BEGIN OPEN SegmentDefs;
fh: FileHandle = requests[swatee].file;
UnlockFile[fh];
ReleaseFile[fh];
END;
IF Swappable THEN
BEGIN OPEN DiskDefs, SegmentDefs;
ENABLE ANY => GOTO bad;
puntData.puntESV.drumFile ← MesaCoreFH ← f;
s ← NewFileSegment[f, 1, 1, Read];
BFSDefs.MakeCFP[@puntData.coreFP, @f.fp];
puntData.coreFP.leaderDA ← LOOPHOLE[RealDA[GetFileSegmentDA[s]]];
puntData.pCoreFP ← @puntData.coreFP;
DeleteFileSegment[s];
IF (f ← requests[debugger].file) = NIL THEN GOTO bad;
s ← NewFileSegment[f, 1, 1, Read];
BFSDefs.MakeCFP[@puntData.debuggerFP, @f.fp];
puntData.debuggerFP.leaderDA ← LOOPHOLE[RealDA[GetFileSegmentDA[s]]];
puntData.pDebuggerFP ← @puntData.debuggerFP;
UnlockFile[f];
DeleteFileSegment[s];
EXITS bad => Swappable ← FALSE;
END
ELSE puntData.pDebuggerFP ← NIL;
Storage.FreeString[requests[debugger].name];
Storage.FreeString[requests[core].name];
Storage.FreeString[requests[swatee].name];
Storage.Free[requests];
puntData.puntESV.drumFile ← MesaCoreFH;
IF bitmapWanted THEN AllocateDebuggerBitmap[MiscOps.BitmapPages];
PuntInfo↑ ← @puntData;
RETURN
END;
RequestType: TYPE = {debugger, core, swatee};
Requests: TYPE = ARRAY RequestType OF ImageDefs.FileRequest;
requests: POINTER TO Requests;
debuggerBitmap: PUBLIC LONG POINTER ← NIL;
bitmapPages: CARDINAL ← 0;
bitmapWanted: BOOLEAN ← TRUE;
AllocateDebuggerBitmap: PUBLIC PROCEDURE [nPages: CARDINAL] =
BEGIN OPEN SegmentDefs;
seg: DataSegmentHandle;
IF debuggerBitmap # NIL THEN ReleaseDebuggerBitmap[];
bitmapWanted ← TRUE;
IF ~memConfig.useXM OR ~Swappable THEN RETURN;
seg ← MakeDataSegment[
base: DefaultXMBase, pages: (bitmapPages ← nPages) !
InsufficientVM => {seg ← NIL; CONTINUE}];
IF seg # NIL THEN
BEGIN
debuggerBitmap ← LongDataSegmentAddress[seg];
seg.type ← ForgotDefs.BitmapDS;
puntData.puntESV.bitmap ← debuggerBitmap;
puntData.puntESV.bitmapPages ← nPages;
END;
END;
ReleaseDebuggerBitmap: PUBLIC PROCEDURE =
BEGIN OPEN SegmentDefs;
seg: DataSegmentHandle;
IF debuggerBitmap # NIL THEN
BEGIN
seg ← LongVMtoDataSegment[debuggerBitmap];
debuggerBitmap ← puntData.puntESV.bitmap ← NIL;
bitmapPages ← puntData.puntESV.bitmapPages ← 0;
DeleteDataSegment[seg];
END;
bitmapWanted ← FALSE;
END;
RequestFiles: PROCEDURE =
BEGIN OPEN Storage;
requests ← Node[SIZE[Requests]];
requests[debugger] ←
[file: NIL, access: Read, link:, name: CopyString["MesaDebugger."L]];
requests[core] ←
[file: NIL, access: Read, link:, name: CopyString["MesaCore."L]];
requests[swatee] ←
[file: NIL, access: Read, link:, name: CopyString["Swatee."L]];
ImageDefs.AddFileRequest[@requests[debugger]];
ImageDefs.AddFileRequest[@requests[core]];
ImageDefs.AddFileRequest[@requests[swatee]];
END;
bypassExec: PUBLIC BOOLEAN ← FALSE;
item1: ImageDefs.CleanupItem ←
[link:, proc: GoToDebugger,
mask: ImageDefs.CleanupMask[Finish] + ImageDefs.CleanupMask[Abort]];
GoToDebugger: ImageDefs.CleanupProcedure =
BEGIN
IF ~bypassExec THEN RETURN;
IF debuggerBitmap = NIL THEN AllocateDebuggerBitmap[MiscOps.BitmapPages];
CoreSwapDefs.level ← -1;
bypassExec ← FALSE;
CallTheDebugger["You Called?"L];
END;
item2: ImageDefs.CleanupItem ←
[link:, proc: CleanupNub, mask: ImageDefs.AllReasons];
CleanupNub: ImageDefs.CleanupProcedure =
BEGIN
save: BOOLEAN ← bitmapWanted;
SELECT why FROM
Save => {RequestFiles[]; ReleaseDebuggerBitmap[]; bitmapWanted ← save};
Restore => FindFiles[];
Checkpoint => {ReleaseDebuggerBitmap[]; bitmapWanted ← save};
Continue, Restart =>
IF bitmapWanted THEN AllocateDebuggerBitmap[MiscOps.BitmapPages];
ENDCASE;
END;
CallTheDebugger: PROCEDURE [s: STRING] =
BEGIN -- user's entry point to debugger
state: ControlDefs.StateVector;
filler0, filler1: CARDINAL;
state ← STATE;
state.stk[0] ← s;
state.stkptr ← 1;
state.dest ← FrameOps.GetReturnLink[];
CoreSwap[explicitcall, @state];
IF FALSE THEN filler0 ← filler1 ← 0;
RETURN
END;
SetSD: PROCEDURE =
BEGIN OPEN SDDefs;
sd: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD;
sd[sProcessBreakpoint] ← ProcessBreakpoint;
sd[sUncaughtSignal] ← Catcher;
sd[sInterrupt] ← Interrupt;
sd[sCallDebugger] ← CallTheDebugger;
END;
-- Main body
P: TYPE = MACHINE DEPENDENT RECORD [in, out: UNSPECIFIED]; -- PORT
LOOPHOLE[DoSwap, P] ← [in: 0, out: SDDefs.SD[SDDefs.sCoreSwap]];
RequestFiles[];
ImageDefs.AddCleanupProcedure[@item1];
START user;
STOP;
BEGIN
ENABLE ANY => ImageDefs.PuntMesa;
FindFiles[];
InitBreakBlocks[];
SetSD[];
FrameDefs.UnNew[LOOPHOLE[NucleusOps.Wart]];
END;
ImageDefs.AddCleanupProcedure[@item2];
RESTART user[ ! ABORTED => CONTINUE];
ImageDefs.StopMesa[];
END...