OctalCommands.mesa: wizards debugging aids
Russ Atkinson, May 18, 1983 8:42 pm
DIRECTORY
AMBridge
USING
[GetWorld, IsStarted, TVForFrame, TVForGFHReferent, TVForProc, TVForRemoteFrame, TVForRemoteGFHReferent, TVToCardinal],
AMEvents USING [BreakID, ClearBreak, SetBreak],
AMTypes USING[Globals, IndexToName, IndexToTV, NComponents, TVType],
BBBugOut USING[ShowChar, ShowCR, ShowDecimal, ShowOctal, ShowRope, ShowRopes],
BBContext USING [Context, GetContents, FindAction, FindMatchingGlobalFrames],
BBEmptyReturn USING [TheEmptyReturn],
BBEval USING [Eval, EvalHead, Tree],
BBEvalQuote USING [EvalQuoteProc, Register],
BBEvalUtil USING [TreeToRope, WorldFromHead],
BBSafety USING [IsValidAddr, Mother],
Commander USING [Handle],
Convert USING [MapValue],
IO USING [CreateViewerStreams, PutChar, STREAM],
List USING [Assoc],
PPLeaves USING [LTIndex],
PPTreeOps USING [NSons, NthSon, OpName],
PrincOps USING [FrameCodeBase],
PrintTV USING [NullPutClosure, Print, PutClosure, PutProc],
ProcessProps USING [GetPropList],
Rope USING [Fetch, Flatten, Match, ROPE, SkipTo, Size],
RTBasic USING [TV, Type],
TVGuide USING [RegisterTV],
OctalCommands:
CEDAR
MONITOR
IMPORTS
AMBridge, AMEvents, AMTypes, BBBugOut, BBContext, BBEmptyReturn, BBEval, BBEvalQuote, BBEvalUtil, BBSafety, Convert, IO, List, PPTreeOps, PrintTV, ProcessProps, Rope, TVGuide, WorldVM
SHARES BBContext
= BEGIN OPEN PrintTV, Rope, RTBasic, WorldVM;
STREAM: TYPE = IO.STREAM;
ShortAddrHack: CARDINAL = 100000B;
Bit1: TYPE = CARDINAL [0..2);
Bit2: TYPE = CARDINAL [0..4);
Bit4: TYPE = CARDINAL [0..16);
Bit8: TYPE = CARDINAL [0..256);
Array1: TYPE = PACKED ARRAY [0..16) OF Bit1;
Array2: TYPE = PACKED ARRAY [0..8) OF Bit2;
Array4: TYPE = PACKED ARRAY [0..4) OF Bit4;
Array8: TYPE = PACKED ARRAY [0..2) OF Bit8;
Pair: TYPE = MACHINE DEPENDENT RECORD [lo,hi: CARDINAL];
safetyCheck: BOOL ← TRUE;
FindMatching:
PROC [name:
ROPE, world: WorldVM.World ←
NIL] =
TRUSTED {
put: PutClosure ← GetPutClosure[NIL];
pos: INT ← name.SkipTo[0, "."];
globalName: ROPE ← name.Flatten[0, pos];
localName: ROPE ← IF (pos ← pos + 1) >= name.Size THEN NIL ELSE name.Flatten[pos];
tryElem:
PROC [glob:
TV, index:
INT] =
TRUSTED {
elem: TV ← NIL;
innerElem:
PROC =
TRUSTED {
elem ← AMTypes.IndexToTV[glob, index];
PrintTV.Print[elem, put];
BBBugOut.ShowRope["\n", put];
};
msg: ROPE ← BBSafety.Mother[innerElem];
IF msg #
NIL
THEN
BBBugOut.ShowRopes[r1: "--{", r2: msg, r3: "}--\n", put: put];
};
eachMatch: BBContext.FindAction =
TRUSTED {
[gf: TV, name: ROPE], where gf is the global frame
inner:
PROC =
TRUSTED {
glob: TV ← AMTypes.Globals[gf];
globType: Type ← AMTypes.TVType[glob];
n: NAT ← AMTypes.NComponents[globType];
first: BOOL ← TRUE;
FOR i:
INT
IN [1..n]
DO
compName: ROPE ← AMTypes.IndexToName[globType, i];
IF NOT localName.Match[compName, FALSE] THEN LOOP;
IF first THEN {BBBugOut.ShowRope["\n", put]; showGF[]; first ← FALSE};
BBBugOut.ShowRope[" ", put];
BBBugOut.ShowRope[compName, put];
BBBugOut.ShowRope[": ", put];
tryElem[glob, i];
ENDLOOP;
};
showGF:
PROC =
TRUSTED {
BBBugOut.ShowRope[" ", put];
PrintTV.Print[gf, put];
IF
NOT AMBridge.IsStarted[gf]
THEN
BBBugOut.ShowRope["~", put];
BBBugOut.ShowRope["\n", put];
};
msg: ROPE ← NIL;
IF localName = NIL THEN {showGF[]; RETURN};
msg ← BBSafety.Mother[inner];
IF msg #
NIL
THEN
BBBugOut.ShowRopes[r1: " --{", r2: msg, r3: "}--\n", put: put];
};
IF world = NIL THEN world ← WorldVM.LocalWorld[];
BBContext.FindMatchingGlobalFrames[world, globalName, eachMatch];
};
OctalRead:
PUBLIC
PROC [
addr: LONG CARDINAL ← 0, len: CARDINAL ← 4,
width: CARDINAL ← 16, base: CARDINAL ← 8,
offset: CARDINAL ← 0] = TRUSTED {
world: WorldVM.World ← GetDefaultWorld[];
p: Address ← addr;
pos: CARDINAL ← 0;
poslim: CARDINAL ← 8;
put: PutClosure ← GetPutClosure[NIL];
IF base
NOT
IN [1..36]
THEN {
OctalOut["invalid base", base, put];
RETURN};
SELECT width
FROM
1,2,4,8,16,32 => {};
ENDCASE => {OctalOut["invalid width", width, put]; RETURN};
IF base < 8 OR width = 32 THEN poslim ← 4; -- hack!
IF base = 1 THEN width ← 8; -- another hack!
SELECT
LOOPHOLE[p, Pair].hi
FROM
0 => {
hack to lengthen the pointer
p ← WorldVM.Long[world, p]};
ShortAddrHack => {
special hack for the short octal read
LOOPHOLE[p, Pair].hi ← 0;
};
ENDCASE;
WHILE len > 0
DO
card: LONG CARDINAL ← 0;
pp: Address ← p;
pint: INT ← 0;
lo: CARDINAL ← 0;
SELECT width
FROM
1 => pp ← p + offset / 16;
2 => pp ← p + offset / 8;
4 => pp ← p + offset / 4;
8 => pp ← p + offset / 2;
16 => pp ← p + offset;
32 => pp ← p + offset + offset;
ENDCASE => ERROR;
IF safetyCheck
AND
NOT BBSafety.IsValidAddr[world, pp]
THEN {
OctalOut["invalid address: %B", pp, put];
RETURN};
card ← WorldVM.Read[world, pp];
lo ← LOOPHOLE[card, Pair].lo;
SELECT width
FROM
1 => {
a: Array1 ← LOOPHOLE[lo];
card ← a[offset MOD 16]};
2 => {
a: Array2 ← LOOPHOLE[lo];
card ← a[offset MOD 8]};
4 => {
a: Array4 ← LOOPHOLE[lo];
card ← a[offset MOD 4]};
8 => {
a: Array8 ← LOOPHOLE[lo];
card ← a[offset MOD 2]};
16 => {};
32 => {
IF safetyCheck
AND
NOT BBSafety.IsValidAddr[world, pp+1]
THEN {
OctalOut["invalid address: %B", pp+1, put];
RETURN};
LOOPHOLE[card, Pair].hi ← WorldVM.Read[world, pp+1];
};
ENDCASE => ERROR;
IF pos = poslim
THEN {
pos ← 0;
IF base = 1 THEN BBBugOut.ShowChar['", put];
BBBugOut.ShowCR[put]};
IF pos = 0
THEN {
OctalOut["%B: ", pp, put];
IF base = 1 THEN BBBugOut.ShowChar['", put];
}
ELSE IF base # 1 THEN BBBugOut.ShowChar[' , put];
pos ← pos + 1;
SELECT base
FROM
10 =>
BBBugOut.ShowDecimal[LOOPHOLE[card, INT], put];
8 =>
OctalOut["%B", card, put];
1 => {
-- print the ASCII value
short: CARDINAL ← card;
IF short
IN [40B..176B]
THEN {
c: CHAR ← LOOPHOLE[short];
IF c = '"
OR c = '\\
THEN
BBBugOut.ShowChar['\\, put];
BBBugOut.ShowChar[c, put]}
ELSE OctalOut["\\%", card, put]};
ENDCASE => {
put1: SAFE PROC [c: CHAR] = CHECKED {BBBugOut.ShowChar[c, put]};
Convert.MapValue[put1, [unsigned[card, base]]]};
offset ← offset + 1;
len ← len - 1;
ENDLOOP;
IF base = 1 THEN BBBugOut.ShowChar['", put];
BBBugOut.ShowChar['\n, put];
};
OctalReadShort:
PROC [
addr: CARDINAL ← 0, len: CARDINAL ← 4,
width: CARDINAL ← 16, base: CARDINAL ← 8, offset: CARDINAL ← 0] = {
p: Pair ← [lo: addr, hi: ShortAddrHack];
OctalRead[LOOPHOLE[p], len, width, base, offset];
};
OctalWrite:
PROC [
addr: LONG CARDINAL ← 0, word: CARDINAL ← 0, len: INT ← 1] = TRUSTED {
world: WorldVM.World ← GetDefaultWorld[];
pair: Pair ← LOOPHOLE[addr];
p: WorldVM.Address ← LOOPHOLE[addr];
fault: WorldVM.Address ← 0;
SELECT pair.hi
FROM
0 => {
hack to lengthen the pointer
p ← WorldVM.Long[world, p]};
ShortAddrHack => {
special hack for the short octal read
pair.hi ← 0;
p ← LOOPHOLE[pair];
};
ENDCASE;
IF addr = 0
OR p = 0
THEN {
put: PutClosure ← GetPutClosure[NIL];
BBBugOut.ShowRope["can't write through NIL", put];
};
FOR i:
INT
IN [0..len)
DO
ENABLE {
ABORTED => GO TO abort;
ANY => {fault ← p+i; EXIT};
};
WorldVM.Write[world, p+i, word];
ENDLOOP;
IF fault # 0
THEN {
put: PutClosure ← GetPutClosure[NIL];
OctalOut["invalid address: %B", LOOPHOLE[fault], put];
};
EXITS abort => ERROR ABORTED;
};
OctalWriteShort:
PROC [
addr: CARDINAL ← 0, word: CARDINAL ← 0, len: INT ← 1] = {
p: Pair ← [lo: addr, hi: ShortAddrHack];
OctalWrite[LOOPHOLE[p], word];
};
AsciiRead:
PROC
[addr: LONG CARDINAL ← 0, bytes: NAT ← 8] = {
IF addr = 0 THEN RETURN;
OctalRead[addr, bytes, 8, 1, 0];
};
AsciiReadShort:
PROC
[addr: CARDINAL ← 0, bytes: NAT ← 8] = {
p: Pair ← [lo: addr, hi: ShortAddrHack];
OctalRead[LOOPHOLE[p], bytes, 8, 1, 0];
};
OctalReadCode:
PROC
[gf,pc: CARDINAL, bytes: CARDINAL ← 8] = TRUSTED {
OctalRead[GetCodeBase[GetDefaultWorld[], gf], bytes, 8, 8, pc];
};
OctalFindCode:
PROC [
gf: CARDINAL, pc: CARDINAL ← 0,
b0,b1,b2,b3,b4,b5,b6,b7,b8,b9: CARDINAL ← 0] = TRUSTED {
put: PutClosure ← GetPutClosure[NIL];
addr: Address ← 0;
world: World ← GetDefaultWorld[];
{
ENABLE InvalidAddress => {
OctalOut["invalid address: %B", LOOPHOLE[bad], put];
GO TO bye};
target: ARRAY [0..10) OF CARDINAL = [b0,b1,b2,b3,b4,b5,b6,b7,b8,b9];
len: CARDINAL ← 10;
addr ← GetCodeBase[world, gf];
WHILE len > 0
DO
IF target[len-1] # 0 THEN EXIT;
len ← len - 1;
ENDLOOP;
IF len = 0 THEN RETURN;
DO
IF ReadByte[world, addr, pc] = b0
THEN {
found: BOOL ← TRUE;
FOR i:
CARDINAL
IN [1..len)
WHILE found
DO
found ← ReadByte[world, addr, pc+i] = target[i];
ENDLOOP;
IF found THEN EXIT};
pc ← pc + 1;
ENDLOOP;
OctalOut["pc = %B, ", pc, put];
OctalRead[addr + (pc/2), len, 8, 8, pc MOD 2]
};
};
FindMatchingHelper: BBEvalQuote.EvalQuoteProc =
TRUSTED {
[head: BBEval.EvalHead, tree: BBEval.Tree, target: Type ← nullType, data: REF ← NIL] RETURNS [return: TV]
world: WorldVM.World ← BBEvalUtil.WorldFromHead[head];
arg: BBEval.Tree ← GetArg[tree, 1];
rope: ROPE ← BBEvalUtil.TreeToRope[arg];
IF rope =
NIL
THEN
WITH arg
SELECT
FROM
lit: PPLeaves.LTIndex =>
WITH lit.value
SELECT
FROM
ropeVal: ROPE => rope ← ropeVal;
ENDCASE;
ENDCASE;
FindMatching[rope];
RETURN [BBEmptyReturn.TheEmptyReturn[]];
};
GetArg:
PROC [tree: BBEval.Tree, which:
NAT]
RETURNS [son: BBEval.Tree ←
NIL] = {
args: BBEval.Tree ← PPTreeOps.NthSon[tree, 2];
IF PPTreeOps.OpName[args] = list
THEN {
IF which
IN [1..PPTreeOps.NSons[args]]
THEN
son ← PPTreeOps.NthSon[args, which]}
ELSE IF which = 1 THEN son ← args;
};
InvalidAddress: ERROR [bad: Address] = CODE;
ReadByte:
PROC [world: World, addr: Address, pc:
CARDINAL]
RETURNS [Bit8] =
TRUSTED {
card: CARDINAL ← 0;
addr ← addr + (pc / 2);
IF safetyCheck
AND
NOT BBSafety.IsValidAddr[world, addr]
THEN
ERROR InvalidAddress[addr];
pc ← pc MOD 2;
card ← WorldVM.Read[world, addr];
RETURN [LOOPHOLE[card, Array8][pc]];
};
SetOctalBreak:
PUBLIC
PROC [gf,pc:
CARDINAL] =
TRUSTED {
world: WorldVM.World ← GetDefaultWorld[];
put: PutClosure ← GetPutClosure[NIL];
id: AMEvents.BreakID ← NIL;
msg: ROPE ← NIL;
inner:
PROC =
TRUSTED {
tv:
TV ←
IF world = WorldVM.LocalWorld[]
THEN AMBridge.TVForGFHReferent[LOOPHOLE[gf]]
ELSE AMBridge.TVForRemoteGFHReferent
[[world, WorldVM.CurrentIncarnation[world], gf]];
id ← NewBreak[tv, gf, pc];
};
OctalOut["Break at (gf: %B", gf, put];
OctalOut[", pc: %B) ", pc, put];
msg ← BBSafety.Mother[inner];
IF id #
NIL
THEN BBBugOut.ShowRope["set.\n", put]
ELSE {
IF msg = NIL THEN msg ← "duplicate break";
BBBugOut.ShowRope["NOT set (", put];
BBBugOut.ShowRope[msg, put];
BBBugOut.ShowRope[").\n", put]
};
};
localBreakList: LocalBreakList;
LocalBreakList: TYPE = LIST OF LocalBreakEntry;
LocalBreakEntry:
TYPE =
RECORD [
id: AMEvents.BreakID, world: WorldVM.World, gf,pc: CARDINAL];
NewBreak:
ENTRY
PROC
[tv: TV, gf,pc: CARDINAL] RETURNS [id: AMEvents.BreakID ← NIL] = TRUSTED {
... adds a new break to the local list of octal breaks. It will get a private error out of AMEventsImpl if FrameBreak can't handle setting the break, including the case where there are duplicate breaks. This means that the caller has to be able to handle ANY error (sigh).
ENABLE UNWIND => NULL;
world: WorldVM.World ← AMBridge.GetWorld[tv];
FOR list: LocalBreakList ← localBreakList, list.rest
UNTIL list =
NIL
DO
IF pc = list.first.pc AND gf = list.first.gf AND world = list.first.world THEN RETURN;
ENDLOOP;
id ← AMEvents.SetBreak[world, GetCodeBase[world, gf], pc, $OctalCommands];
localBreakList ← CONS[[id: id, world: world, gf: gf, pc: pc], localBreakList];
};
GetCodeBase:
PROC
[world: WorldVM.World, gfh: CARDINAL] RETURNS [addr: WorldVM.Address] = TRUSTED {
... gets the code base for the octal gfh, clearing out the code trap bit to be safe.
ENABLE UNWIND => NULL;
addr ← WorldVM.Long[world, gfh] + 1;
addr ← WorldVM.LongRead[world, addr]; -- code base
LOOPHOLE[addr, PrincOps.FrameCodeBase].out ← FALSE; -- stupid code traps!
};
FindBreak:
ENTRY
PROC
[tv: TV, gf,pc: CARDINAL, delete: BOOL ← FALSE]
RETURNS [id: AMEvents.BreakID ← NIL] = TRUSTED {
This procedure just runs around trying to find the given break. If one is found, it removes it if delete is TRUE, and calls AMEvents.ClearBreak to clear the succker. Since AMEvents can barf at this request, we have to be able to handle this problem.
ENABLE UNWIND => NULL;
lag: LocalBreakList ← NIL;
world: WorldVM.World ← AMBridge.GetWorld[tv];
FOR list: LocalBreakList ← localBreakList, list.rest
UNTIL list =
NIL
DO
IF pc = list.first.pc
AND gf = list.first.gf
AND world = list.first.world
THEN {
id ← list.first.id;
IF delete
THEN {
IF lag = NIL THEN localBreakList ← list.rest ELSE lag.rest ← list.rest;
AMEvents.ClearBreak[id];
};
RETURN;
};
lag ← list;
ENDLOOP;
};
ClearOctalBreak:
PUBLIC
PROC [gf,pc:
CARDINAL] =
TRUSTED {
world: WorldVM.World ← GetDefaultWorld[];
put: PutClosure ← GetPutClosure[NIL];
id: AMEvents.BreakID ← NIL;
msg: ROPE ← NIL;
inner:
PROC =
TRUSTED {
tv:
TV ←
IF world = WorldVM.LocalWorld[]
THEN AMBridge.TVForGFHReferent[LOOPHOLE[gf]]
ELSE AMBridge.TVForRemoteGFHReferent
[[world, WorldVM.CurrentIncarnation[world], gf]];
id ← FindBreak[tv, gf, pc, TRUE]
};
OctalOut["Break at (gf: %B", gf, put];
OctalOut[", pc: %B) ", pc, put];
msg ← BBSafety.Mother[inner];
IF id #
NIL
THEN {
BBBugOut.ShowRope["cleared.\n", put]
}
ELSE {
IF msg = NIL THEN msg ← "not found";
BBBugOut.ShowRope["NOT cleared (", put];
BBBugOut.ShowRope[msg, put];
BBBugOut.ShowRope[").\n", put]
};
};
ListOctalBreaks:
PUBLIC
ENTRY
PROC =
TRUSTED {
ENABLE UNWIND => NULL;
put: PutClosure ← GetPutClosure[NIL];
IF localBreakList =
NIL
THEN {
BBBugOut.ShowRope["No current octal breaks.\n", put];
RETURN};
BBBugOut.ShowRope["Current octal breaks:\n", put];
FOR list: LocalBreakList ← localBreakList, list.rest
UNTIL list =
NIL
DO
OctalOut[" Break at (gf: %B", list.first.gf, put];
OctalOut[", pc: %B)\n", list.first.pc, put];
ENDLOOP;
};
PrintLocalFrame:
PROC [lf:
CARDINAL] =
TRUSTED {
put: PutClosure ← GetPutClosure[NIL];
tv: TV = TrustLocalFrame[lf];
oct: CARDINAL ← 0;
PrintTV.Print[tv: tv, put: put, verbose: TRUE];
};
PrintGlobalFrame:
PROC [gf:
CARDINAL] =
TRUSTED {
put: PutClosure ← GetPutClosure[NIL];
tv: TV = TrustGlobalFrame[gf];
PrintTV.Print[tv: tv, put: put, verbose: TRUE];
};
TrustLocalFrame:
PROC [lf:
CARDINAL]
RETURNS [tv:
TV] =
TRUSTED {
world: WorldVM.World ← GetDefaultWorld[];
IF world = WorldVM.LocalWorld[]
THEN tv ← AMBridge.TVForFrame[fh: LOOPHOLE[lf]]
ELSE
tv ← AMBridge.TVForRemoteFrame[[
world: world,
worldIncarnation: WorldVM.CurrentIncarnation[world],
fh: LOOPHOLE[lf]]];
};
TrustGlobalFrame:
PROC [gf:
CARDINAL]
RETURNS [tv:
TV] =
TRUSTED {
world: WorldVM.World ← GetDefaultWorld[];
IF world = WorldVM.LocalWorld[]
THEN tv ← AMBridge.TVForGFHReferent[gfh: LOOPHOLE[gf]]
ELSE
tv ← AMBridge.TVForRemoteGFHReferent[[
world: world,
worldIncarnation: WorldVM.CurrentIncarnation[world],
gfh: LOOPHOLE[gf]]];
};
TrustLocalFrameHelper: BBEvalQuote.EvalQuoteProc =
TRUSTED {
[head: BBEval.EvalHead, tree: BBEval.Tree, target: Type ← nullType, data: REF ← NIL] RETURNS [return: TV]
world: WorldVM.World ← BBEvalUtil.WorldFromHead[head];
arg: BBEval.Tree ← GetArg[tree, 1];
tv: TV ← BBEval.Eval[arg, head, target];
RETURN [TrustLocalFrame[AMBridge.TVToCardinal[tv]]];
};
TrustGlobalFrameHelper: BBEvalQuote.EvalQuoteProc =
TRUSTED {
[head: BBEval.EvalHead, tree: BBEval.Tree, target: Type ← nullType, data: REF ← NIL] RETURNS [return: TV]
world: WorldVM.World ← BBEvalUtil.WorldFromHead[head];
arg: BBEval.Tree ← GetArg[tree, 1];
tv: TV ← BBEval.Eval[arg, head, target];
RETURN [TrustGlobalFrame[AMBridge.TVToCardinal[tv]]];
};
OctalOut:
PROC [msg:
ROPE, x:
INT ← 0, put: PutClosure ← NullPutClosure] = {
pos: INT ← 0;
lim: INT ← msg.Size[];
WHILE pos < lim
DO
c: CHAR ← msg.Fetch[pos];
pos ← pos + 1;
IF c = '%
THEN BBBugOut.ShowOctal[x, put]
ELSE BBBugOut.ShowChar[c, put];
ENDLOOP;
};
GetDefaultWorld:
PROC
RETURNS [world: World ←
NIL] =
TRUSTED {
head: BBEval.EvalHead = NARROW[List.Assoc[$EvalHead, ProcessProps.GetPropList[]]];
IF head #
NIL
THEN {
context: BBContext.Context = head.context;
IF context # NIL THEN world ← BBContext.GetContents[context].world;
};
IF world = NIL THEN world ← WorldVM.LocalWorld[];
};
hackDefaultStream: STREAM ← NIL;
GetPutClosure:
PROC
[stream: STREAM] RETURNS [pc: PutClosure ← NullPutClosure] = TRUSTED {
IF stream =
NIL
THEN {
command: Commander.Handle =
NARROW[List.Assoc[$CommanderHandle, ProcessProps.GetPropList[]]];
IF command # NIL THEN stream ← command.out;
};
IF stream =
NIL
THEN {
IF hackDefaultStream =
NIL
THEN
hackDefaultStream ← IO.CreateViewerStreams["Octal.log"].out;
stream ← hackDefaultStream;
};
pc ← [PutChar, stream];
};
PutChar: PrintTV.PutProc = {
IO.PutChar[NARROW[data], c];
};
RegisterCommands:
PROC = {
RegisterOne[OctalRead, "&or[ptr,len]\tOctal Read"];
RegisterOne[OctalReadShort, "&ors[ptr,len]\tOctal Read Short"];
RegisterOne[OctalWrite, "&ow[ptr,word,len]\tOctal Write"];
RegisterOne[OctalWriteShort, "&ows[ptr,word,len]\tOctal Write Short"];
RegisterOne[AsciiRead, "&ar[ptr,len]\tAscii Read"];
RegisterOne[AsciiReadShort, "&ars[ptr,len]\tAscii Read Short"];
RegisterOne[OctalReadCode, "&orc[gf,pc,len]\tOctal Read Code"];
RegisterOne[OctalFindCode, "&ofc[gf,pc,...]\tOctal Find Code"];
RegisterOne[SetOctalBreak, "&sob[gf,pc]\tSet Octal Break"];
RegisterOne[ClearOctalBreak, "&clob[gf,pc]\tCLear Octal Break"];
RegisterOne[ListOctalBreaks, "&lob[]\tList Octal Breaks"];
RegisterOne[FindMatching, "&fm[pattern]\tFind Matching"];
RegisterOne[TrustLocalFrame, "&tlf[lf]\tTrust Local Frame"];
RegisterOne[TrustGlobalFrame, "&tgf[gf]\tTrust Global Frame"];
RegisterOne[PrintLocalFrame, "&plf[lf]\tPrint Local Frame"];
RegisterOne[PrintGlobalFrame, "&pgf[gf]\tPrint Global Frame"];
BBEvalQuote.Register["&fm", FindMatchingHelper];
BBEvalQuote.Register["&tlf", TrustLocalFrameHelper];
BBEvalQuote.Register["&tgf", TrustGlobalFrameHelper];
};
RegisterOne:
PROC [proc:
PROC
ANY
RETURNS
ANY ←
NIL, help:
ROPE ←
NIL] =
TRUSTED {
[] ← TVGuide.RegisterTV[
name: help.Flatten[0, help.SkipTo[0, "["]],
tv: AMBridge.TVForProc[proc],
help: help];
};
RegisterCommands[ ! ANY => CONTINUE];