-- file: BackStop.Mesa
-- edited by Levin, March 13, 1981 4:44 PM
-- edited by Schroeder, March 15, 1981 2:45 PM
-- edited by Brotz, March 6, 1981 2:22 PM

DIRECTORY
Ascii,
ControlDefs,
crD: FROM "CoreDefs",
DiskDefs,
drD: FROM "LaurelDriverDefs",
dsD: FROM "DisplayDefs",
Editor,
exD: FROM "ExceptionDefs",
FrameOps,
gsD: FROM "GlobalStorageDefs",
ImageDefs,
inD: FROM "InteractorDefs",
Inline,
intCommon: FROM "IntCommon",
ModuleName,
ovD: FROM "OverviewDefs",
PupDefs,
SegmentDefs,
SDDefs,
String,
TrapDefs,
vmD: FROM "VirtualMgrDefs";

BackStop: PROGRAM
IMPORTS crD, DiskDefs, dsD, Editor, exD, FrameOps, gsD, ImageDefs, Inline,
intC: intCommon, inD, ModuleName, PupDefs, SegmentDefs, String, TrapDefs, vmD
EXPORTS drD
SHARES inD = PUBLIC

BEGIN

OPEN inD;

recursionDepth: INTEGER ← -1;
oldSignalMessage: STRING ← "Signal lost.";

PupTrouble: ERROR[why: UNSPECIFIED] = CODE;

-- ProcessBugReport is called during initialization if a /b switch is encountered.

ProcessBugReport: PROCEDURE =
BEGIN
error: ovD.ErrorCode;
state: {initial, firstCR, endOfHeader, secondCR} ← initial;
selectionStart, selectionEnd: CARDINAL;
mnp: inD.MessageTextNbrPtr = intC.cmTextNbr;
message: vmD.ComposeMessagePtr = LOOPHOLE[mnp.message];
modelessEditor: BOOLEAN = (intC.editorType = modeless);

[error, ] ← vmD.InsertFileInMessage[0, message, "Laurel.BugReport$"L, intC.user];
IF error = ovD.ok THEN
BEGIN
i: ovD.CharIndex;
char: CHARACTER;
FOR i IN [0 .. vmD.GetMessageSize[message]) DO
char ← vmD.GetMessageChar[message, i];
SELECT state FROM
initial => IF char = Ascii.CR THEN state ← firstCR;
firstCR =>
IF char = Ascii.CR THEN {state ← endOfHeader; selectionStart ← i+1}
ELSE state ← initial;
endOfHeader => IF char = Ascii.CR THEN state ← secondCR;
secondCR =>
IF char = Ascii.CR THEN {selectionEnd ← i-1; EXIT}
ELSE state ← endOfHeader;
ENDCASE;
REPEAT
FINISHED => GO TO MalFormedMessage;
ENDLOOP;
intC.target ← inD.TextSelection
[mnp: mnp, start: selectionStart, end: selectionEnd, point: selectionStart,
mode: char, pendingDelete: intC.editorType = modeless];
intC.newTargetSelection ← TRUE;
intC.pendingDeleteSetByControl ← FALSE;
Editor.SetHighWaterMark[0];
mnp.haveMessage ← TRUE;
IF modelessEditor THEN inD.StopBlinkingCaret[];
Editor.RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: mnp.lines, mnp: mnp];
IF modelessEditor THEN inD.SetCaretBlinking[selectionStart, mnp];
END
ELSE GO TO MalFormedMessage;
EXITS
MalFormedMessage => exD.DisplayException[exception: exD.noBugReportFile];
END; -- of ProcessBugReport --


CapturePupGlitch: PROCEDURE[why: UNSPECIFIED -- DriverDefs.GlitchType --] =
BEGIN
ERROR PupTrouble[why];
END;
-- of CapturePupGlitch --


-- "Streaming" Procedures

maxChars: CARDINAL = 512;
buffer: POINTER TO PACKED ARRAY [0..maxChars) OF CHARACTER ← NIL;
charsInBuffer: [0..maxChars];
currentPage: CARDINAL;
fileH: crD.UFileHandle ← NIL;


CantMessWithFile: ERROR = CODE;


OpenFile: PROCEDURE [fileName: STRING] =
BEGIN
error: ovD.ErrorCode;
[error, fileH] ← crD.OpenFile[intC.user, fileName, update];
IF error # ovD.ok THEN ERROR CantMessWithFile;
error ← crD.UFileTruncate[0, 0, fileH];
IF error # ovD.ok THEN ERROR CantMessWithFile;
currentPage ← charsInBuffer ← 0;
IF buffer = NIL THEN buffer ← gsD.GetMemoryPages[1];
END; -- of OpenFile --


CloseFile: PROCEDURE =
BEGIN
error: ovD.ErrorCode;
IF charsInBuffer # 0 THEN
BEGIN
error ← crD.WritePages[buffer, charsInBuffer, currentPage, fileH];
IF error # ovD.ok THEN ERROR CantMessWithFile;
END;
[error] ← crD.CloseFile[fileH];
fileH ← NIL;
IF error # ovD.ok THEN ERROR CantMessWithFile;
END; -- of CloseFile --


WriteChar: PROCEDURE [c: CHARACTER] =
BEGIN
error: ovD.ErrorCode;
IF charsInBuffer = maxChars THEN
BEGIN
error ← crD.WritePages[buffer, maxChars, currentPage, fileH];
IF error # ovD.ok THEN ERROR CantMessWithFile;
currentPage ← currentPage+1;
charsInBuffer ← 0;
END;
buffer[charsInBuffer] ← c;
charsInBuffer ← charsInBuffer+1;
END; -- of WriteChar --


WriteString: PROCEDURE[s: STRING] =
BEGIN
i: CARDINAL;
FOR i IN [0..s.length) DO WriteChar[s[i]]; ENDLOOP;
END; -- of WriteString --


-- Message Formatting Procedures


StuffOctal: PROCEDURE [value: UNSPECIFIED, s: STRING, start: CARDINAL, places: CARDINAL ← 6] =
BEGIN
FOR i: CARDINAL DECREASING IN [start..start+places) DO
digit: CARDINAL;
[value, digit] ← Inline.DIVMOD[value, 8];
s[i] ← LOOPHOLE[digit + 60B, CHARACTER];
ENDLOOP;
END; -- of StuffOctal --


DumpCallStack: PROCEDURE =
BEGIN
OPEN ControlDefs;
moduleName: STRING ← [20];
pcTemplate: STRING ← ", xxxxxx
"L;
localFrame: ControlLink ← FrameOps.MyLocalFrame[].returnlink;
THROUGH [0..100) DO
SELECT localFrame.tag FROM
frame =>
BEGIN
OPEN f:localFrame.frame;
IF @f = NullFrame THEN EXIT;
ModuleName.ControlLinkToModuleName[localFrame, moduleName];
WriteString[moduleName];
IF f.pc < 0
THEN StuffOctal[(-f.pc)*2+1,pcTemplate,2]
ELSE StuffOctal[(f.pc)*2,pcTemplate,2];
WriteString[pcTemplate];
localFrame ← f.returnlink;
END;
ENDCASE =>
BEGIN
WriteString["Stack end "L];
ModuleName.ControlLinkToModuleName[localFrame, moduleName];
WriteString[moduleName];
WriteChar[Ascii.CR];
EXIT;
END;
ENDLOOP;
WriteChar[Ascii.CR];
END; -- of DumpCallStack --


WriteGoryDetails: PROCEDURE =
BEGIN
OPEN SegmentDefs;
s: STRING ← " (nnn#nnn#) with "L;
rest: STRING ← "MDS: nn Banks: nnnnnn"L;
ucode: STRING ← " flcode: nnnnnn"L;
me: PupDefs.PupAddress;
WriteString[" "L];
WriteString[ SELECT memConfig.AltoType FROM
AltoI => "Alto I"L,
AltoII => "Alto II"L,
AltoIIXM => "Alto II XM"L,
D0 => "Dolphin"L,
Dorado => "Dorado"L,
ENDCASE => "IBM 360/15"L];
PupDefs.GetPupAddress[@me, "ME"L];
StuffOctal[me.net, s, 2, 3]; StuffOctal[me.host, s, 6, 3];
WriteString[s];
IF memConfig.AltoType < D0 THEN
WriteString[SELECT memConfig.controlStore FROM
Ram0 => "Ram only "L,
RamandRom => "2K Rom "L,
Ram3k => "3K Ram "L,
ENDCASE => "Paper tape "L];
IF memConfig.xmMicroCode THEN WriteString["XMflcode "L];
IF memConfig.useXM THEN WriteString["using XM "L];
StuffOctal[memConfig.mdsBank, rest, 5, 2];
StuffOctal[memConfig.banks, rest, 15];
WriteString[rest];
IF memConfig.AltoType < D0 THEN
{StuffOctal[memConfig.mesaMicrocodeVersion, ucode, 8]; WriteString[ucode]};
END; -- of WriteGoryDetails --


Oops: PROCEDURE =
BEGIN
exD.FlashExceptionsRegion[];
UNTIL UserWants[] = terminate DO ENDLOOP; --
Die[];
END; -- of Oops --


Die: PROCEDURE =
BEGIN
dsD.DCBorg↑ ← LOOPHOLE[0]; -- keep display tidy
ImageDefs.StopMesa[];
END; -- of Die --


UserWants: PROCEDURE RETURNS[{tryIt,terminate}] =
BEGIN
mouseButton: POINTER TO UNSPECIFIED = LOOPHOLE[177030B];
redBlue: UNSPECIFIED = 4+2;
yellow: UNSPECIFIED = 1;
UNTIL Inline.BITAND[mouseButton↑,yellow] = 0 DO
IF Inline.BITAND[mouseButton↑,redBlue] = 0 THEN RETURN[terminate];
ENDLOOP;
RETURN[tryIt];
END; -- of UserWants --


Catcher: PROCEDURE [msg: UNSPECIFIED, signal: SIGNAL[UNSPECIFIED],
frame: ControlDefs.FrameHandle] =
BEGIN
i: CARDINAL;
savedCursor: ARRAY [0 .. 15] OF CARDINAL;
pCursor: POINTER TO ARRAY [0 .. 15] OF CARDINAL = LOOPHOLE[431B];
errorCursor: ARRAY [0 .. 15] OF CARDINAL =
[34034B, 42042B, 101102B, 101202B, 101204B, 42210B, 34160B, 0,
176174B, 101202B, 101200B, 176174B, 100002B, 100202B, 100174B, 0];
moduleName: STRING ← [20];

twoCRs: STRING ← "

"L;

headerPart: STRING ← "Subject: Error Report
To: "L;

bodyPart: STRING ← "Please replace this paragraph with a brief description of what you were doing when Laurel broke, then ""Deliver"" this message. (Be sure to include the last command you invoked.) After the message is delivered, you can continue to use Laurel as usual."L;

parityErrorMessage: STRING ← "Parity error at ??????."L;
-- 28 spaces required after msg↑
signalMessage: STRING ← "Signal xxxxxx in "L;
msgMessage: STRING ← "msg xxxxxx, msg↑ "L;

SELECT (recursionDepth ← recursionDepth+1) FROM
0 => NULL;
1 => BEGIN
exceptionString: STRING ← [exD.maxExceptionStringLength];
pCursor↑ ← errorCursor;
exD.GetExceptionString[exD.recursiveTrouble, exceptionString];
String.AppendString[exceptionString, intC.bugReportee];
exD.AppendExceptionString[exD.mustEndSession, exceptionString];
exD.DisplayExceptionStringOnLine[exceptionString, 1];
exD.DisplayExceptionStringOnLine[oldSignalMessage, 2];
Oops[];
END;
ENDCASE => DO ENDLOOP;

savedCursor ← pCursor↑;
pCursor↑ ← errorCursor;
SELECT signal FROM
LOOPHOLE[TrapDefs.PhantomParityError, SIGNAL[UNSPECIFIED]],
TrapDefs.ParityError =>
BEGIN
IF signal = TrapDefs.ParityError THEN
StuffOctal[msg, parityErrorMessage, 16];
exD.DisplayExceptionStringOnLine[parityErrorMessage, 1];
exD.DisplayExceptionLine[exD.tryToContinue, 2];
exD.FlashExceptionsRegion[];
IF UserWants[] = terminate THEN Die[];
pCursor↑ ← savedCursor;
recursionDepth ← -1;
exD.ClearExceptionsRegion[];
intC.timeMayBeBogus ← TRUE;
RETURN; --resume the parity error
END;
exD.SysBugSignal =>
IF ~LOOPHOLE[msg, BOOLEAN] THEN Oops[];
LOOPHOLE[DiskDefs.UnrecoverableDiskError, SIGNAL[UNSPECIFIED]] =>
BEGIN
exD.DisplayExceptionLine[exD.unrecDiskError, 1];
exD.DisplayExceptionLine[exD.cannotProceed, 2];
Oops[];
END;
ENDCASE;

--remember this instance of signalMessage for possible recursive error
oldSignalMessage ← signalMessage;

--prepare signal message
StuffOctal[signal, signalMessage, 7];
ModuleName.ControlLinkToModuleName[LOOPHOLE[signal], moduleName];
StuffOctal[msg, msgMessage, 4];
IF LOOPHOLE[msg, CARDINAL] > 1400B THEN
FOR i ← 17, i + 7 WHILE i < 39 DO
StuffOctal[LOOPHOLE[msg + (i - 17) / 7, POINTER]↑, msgMessage, i];
ENDLOOP;

exD.DisplayExceptionLine[exD.systemError, 1];
exD.DisplayExceptionLine[exD.autoErrorReport, 2];
exD.FlashExceptionsRegion[];
IF UserWants[] = terminate THEN Die[];

dsD.SetCursor[hourGlass];

OpenFile["Laurel.BugReport$"L];
WriteString[headerPart]; WriteString[intC.bugReportee]; WriteString[twoCRs];
WriteString[bodyPart]; WriteString[twoCRs];
WriteString[intC.versionHouse.text]; WriteGoryDetails[]; WriteString[twoCRs];
WriteString[signalMessage]; WriteString[moduleName]; WriteChar[Ascii.CR];
WriteString[msgMessage]; WriteString[twoCRs];
DumpCallStack[];
CloseFile[];
IF intC.imageFileName # NIL THEN
BEGIN
OpenFile["Rem.cm"L];
WriteString[intC.imageFileName]; WriteString["/b"L]; WriteChar[Ascii.CR];
CloseFile[];
END;

Die[];
END; -- of Catcher --


END.
-- of BackStop --