LarkCommImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Stewart, December 21, 1983 4:08 pm
Last Edited by: Swinehart, December 17, 1986 10:34:17 pm PST
DIRECTORY
Basics USING [BITAND, LowHalf],
BasicTime USING [Now],
Buttons USING [ReLabel],
IO,
Labels USING [Set],
LarkControl,
LarkPrograms USING [AddOrReplaceProgram, FetchProgram, FindVariable, PatchItem, Program, ReadProgramFromDisk, ReadProgramFromMBFile, STObject],
Process USING [CheckForAbort],
NamesGV USING [GVIsAuthenticated, GVGetAttribute],
Rope USING [Concat, Fetch, Find, FromChar, Length, ROPE, Substr],
TeleLoad USING [Advice, Call, CallPkt, CallPktObject, CoreAddress, CoreBlock, CoreBlockObject, EventRecordObject, Failed, Fetch, FetchState, FlushWrites, GetEventData, Go, GoFromBreak, GoToDebugger, Handle, ReadWord, Registers8086, ResetCache, SetCacheSize, SingleStep, SlaveFetch, SlaveStore, State8086, State8086Object, Store, SwabState, TeleLoadProc, Write, WriteWord];
LarkCommImpl: CEDAR PROGRAM
IMPORTS Buttons, Basics, BasicTime, IO, Labels, LarkControl, LarkPrograms, NamesGV, Process, Rope, TeleLoad
EXPORTS LarkControl =
{
loadWindowLow: PUBLIC TeleLoad.CoreAddress ← 0;
loadWindowHigh: PUBLIC TeleLoad.CoreAddress ← 57344; -- 0E000H
LoadProgram: PUBLIC PROC [lark: LarkControl.LarkData, go: BOOL, main: BOOL] = {
ENABLE TeleLoad.Failed => GOTO BadComm;
ok: BOOL;
tries: NAT;
numBlocks: INT𡤀
h: TeleLoad.Handle ← lark.h;
log: IO.STREAM ← h.log;
program: LarkPrograms.Program ← IF main THEN lark.program ELSE lark.slaveProgram;
IF program = NIL THEN {
log.PutRope["No program specified\n"];
RETURN;
};
TeleLoad.FlushWrites[h];
TeleLoad.SetCacheSize[h, 0];
IF lark.breakList # NIL THEN {
LarkControl.LogEntry[lark: lark, rope: "Clearing breakpoints\n"];
lark.breakList ← NIL;
};
LarkControl.LogEntry[lark: lark, rope: IO.PutFR["Loading %g: ", IO.rope[program.programName]]];
IF NOT FetchState[h].ok THEN {
log.PutRope["\n"];
RETURN;
};
FOR l: LIST OF TeleLoad.CoreBlock ← program.program, l.rest WHILE l # NIL DO
IF l.first.address >= LarkControl.loadWindowLow AND (l.first.address + l.first.count) < LarkControl.loadWindowHigh THEN {
l.first.advice ← [FALSE, FALSE, 0];
[ok, tries] ← IF program.addressSpace = main THEN TeleLoad.Store[h, l.first] ELSE TeleLoad.SlaveStore[h, l.first];
IF ok THEN log.PutChar[IF tries = 1 THEN '! ELSE ('0 + MIN[tries, 9])]
ELSE {
log.PutRope["\nTeleload Error\n"];
EXIT;
};
numBlocks ← numBlocks + 1;
IF numBlocks MOD 10 = 0 THEN log.PutChar[' ]; -- avoids long "words"
}
ELSE {
FOR partial: TeleLoad.CoreAddress IN [0..l.first.count) DO
IF (l.first.address + partial) >= LarkControl.loadWindowLow AND (l.first.address + partial) < LarkControl.loadWindowHigh THEN TeleLoad.Write[h: h, addr: l.first.address + partial, value: l.first[partial], addressSpace: program.addressSpace];
ENDLOOP;
TeleLoad.FlushWrites[h];
log.PutChar['s];
};
ENDLOOP;
FOR pl: LIST OF LarkPrograms.PatchItem ← program.patchList, pl.rest WHILE pl # NIL DO
TeleLoad.WriteWord[h: h, addr: pl.first.address, value: pl.first.wordValue, addressSpace: program.addressSpace];
log.PutChar['p];
ENDLOOP;
TeleLoad.FlushWrites[h];
lark.state ← program.startState;
IF go THEN {
[] ← SetState[h: h, state: lark.state, tp: TeleLoad.Go];
Labels.Set[lark.status, LarkControl.PollLark[lark, FALSE, FALSE]];
lark.lastBoot ← BasicTime.Now[];
lark.lastBootTimeValid ← TRUE;
};
log.PutRope["\n"];
EXITS
BadComm => NULL;
};
PaintMode: PUBLIC PROC [lark: LarkControl.LarkData] = {
Buttons.ReLabel[button: lark.mode, newName: Rope.Concat["Mode ", Rope.FromChar[lark.larkMode]]];
};
SetState: PUBLIC PROC [h: TeleLoad.Handle, state: TeleLoad.State8086Object, tp: TeleLoad.TeleLoadProc, print: BOOLTRUE] RETURNS [ok: BOOL] = TRUSTED {
cb: TeleLoad.CoreBlock;
tries: NAT;
pp: TeleLoad.State8086;
size: CARDINAL = SIZE[TeleLoad.State8086Object]*2;
IF h = NIL THEN ERROR;
cb ← NEW[TeleLoad.CoreBlockObject[size]];
cb.address ← 1;
cb.advice ← [FALSE, FALSE, 0];
pp ← LOOPHOLE[BASE[DESCRIPTOR[cb.data]], TeleLoad.State8086];
pp^ ← TeleLoad.SwabState[state];
pp ← NIL;
[ok, tries] ← tp[h, cb];
IF NOT ok AND print THEN {
reason: Rope.ROPESELECT tp FROM
TeleLoad.Store => "Store",
TeleLoad.Fetch => "Fetch",
TeleLoad.Go => "Go",
TeleLoad.FetchState => "FetchState",
TeleLoad.SingleStep => "SingleStep",
TeleLoad.GoFromBreak => "GoFromBreak",
TeleLoad.GoToDebugger => "GoToDebugger",
TeleLoad.SlaveStore => "SlaveStore",
TeleLoad.SlaveFetch => "SlaveFetch",
TeleLoad.Call => "Call"
ENDCASE => "SetState";
IF h.log # NIL THEN h.log.PutF["%g: %g failed (%d attempts)\n", IO.rope[h.host], IO.rope[reason], IO.int[tries]];
};
};
FetchState: PUBLIC PROC[h: TeleLoad.Handle, adv: TeleLoad.Advice ← [FALSE, FALSE, 0], print: BOOLTRUE] RETURNS [ok: BOOL, state: TeleLoad.State8086Object] = TRUSTED {
cb: TeleLoad.CoreBlock;
tries: NAT;
pp: TeleLoad.State8086;
size: CARDINAL = SIZE[TeleLoad.State8086Object]*2;
IF h = NIL THEN ERROR;
cb ← NEW[TeleLoad.CoreBlockObject[size]];
cb.address ← 1;
cb.advice ← adv;
[ok, tries] ← TeleLoad.FetchState[h, cb];
IF NOT ok AND print THEN {
IF h.log # NIL THEN h.log.PutF["%g: FetchState failed (%d attempts)\n", IO.rope[h.host], IO.int[tries]];
state ← [ALL[0]];
RETURN;
};
pp ← LOOPHOLE[BASE[DESCRIPTOR[cb.data]], TeleLoad.State8086];
state ← TeleLoad.SwabState[pp^];
pp ← NIL;
};
changes the word at offset from an identifier, use for patch decks
SetValue: PUBLIC PROC [lark: LarkControl.LarkData, name: Rope.ROPE, value: CARDINAL, offset: NAT ← 0, print: BOOLTRUE] RETURNS [ok: BOOL] = {{
ENABLE TeleLoad.Failed => GOTO BadComm;
ob: LarkPrograms.STObject;
address: TeleLoad.CoreAddress;
found: BOOL;
oldValue: CARDINAL;
out: IO.STREAM;
IF lark = NIL OR lark.h = NIL THEN RETURN[FALSE];
out ← lark.h.log;
TeleLoad.ResetCache[lark.h];
[item: ob, found: found] ← LarkPrograms.FindVariable[lark.program, name];
IF NOT found THEN {
IF print THEN out.PutF[" %g: not found\n", IO.rope[name]];
RETURN[FALSE];
}
ELSE { -- fetch data and print it
address ← ob.addr;
IF print THEN out.PutF[" %g + %04xH (%04xH^): ", IO.rope[name], IO.card[offset], IO.card[address]];
oldValue ← TeleLoad.ReadWord[lark.h, address + offset];
IF ob.type = variable THEN {
TeleLoad.SetCacheSize[lark.h, 2]; -- one word at a time!
TeleLoad.WriteWord[h: lark.h, addr: address + offset, value: value];
TeleLoad.FlushWrites[lark.h];
TeleLoad.SetCacheSize[lark.h, 0];
IF print THEN out.PutF["%04xH ← %04xH\n", IO.card[oldValue], IO.card[value]];
}
ELSE {
IF print THEN out.PutF["%04xH . . . not a variable\n", IO.card[oldValue]];
RETURN[FALSE];
};
};
};
RETURN[TRUE];
EXITS
BadComm => RETURN[FALSE];
};
DisplayState: PUBLIC PROC[s: IO.STREAM, state: TeleLoad.State8086Object, indent: NAT ← 0] = {
THROUGH [0..indent) DO s.PutChar[' ]; ENDLOOP;
FOR i: TeleLoad.Registers8086 IN [AX..SI] DO PutReg[s, i, state.Regs[i]]; ENDLOOP;
s.PutChar['\n];
THROUGH [0..indent) DO s.PutChar[' ]; ENDLOOP;
FOR i: TeleLoad.Registers8086 IN [DI..FL] DO PutReg[s, i, state.Regs[i]]; ENDLOOP;
s.PutChar['\n];
};
PutReg: PROC [s: IO.STREAM, i: TeleLoad.Registers8086, v: CARDINAL] = {
s.PutF[" %2s=%04xH", IO.rope[RegNames[i]], IO.card[v]];
};
RegNames: ARRAY TeleLoad.Registers8086 OF Rope.ROPE = [
"AX", "BX", "CX", "DX", "SP", "BP", "SI", "DI", "CS", "DS", "SS", "ES", "IP", "FL"];
LocalGo: PUBLIC PROC [lark: LarkControl.LarkData] = {
found: BOOLFALSE;
breakpoint: LarkControl.Breakpoint;
FOR l: LIST OF LarkControl.Breakpoint ← lark.breakList, l.rest WHILE l # NIL DO
IF l.first.address = lark.state.Regs[IP] THEN {
found ← TRUE;
breakpoint ← l.first;
EXIT;
};
ENDLOOP;
IF found THEN {
RepairBreakpoint[lark.h, breakpoint];
[] ← LarkControl.SetState[h: lark.h, state: lark.state, tp: TeleLoad.GoFromBreak, print: TRUE];
}
ELSE [] ← LarkControl.SetState[h: lark.h, state: lark.state, tp: TeleLoad.Go, print: TRUE];
IF lark.status # NIL THEN Labels.Set[lark.status, LarkControl.PollLark[lark, FALSE, FALSE]];
};
PollLark: PUBLIC PROC [lark: LarkControl.LarkData, print: BOOLFALSE, setPointers: BOOLTRUE] RETURNS [reason: Rope.ROPE] = {
event: TeleLoad.EventRecordObject;
ok: BOOL;
[event: event, ok: ok] ← TeleLoad.GetEventData[h: lark.h, setPointers: setPointers];
IF NOT ok THEN {
reason ← "Down";
lark.event.reason ← LarkControl.cDown;
}
ELSE {
reason ← LarkControl.BootReason[reason: event.reason, BX: event.regs.Regs[BX]];
IF lark.lastBootTimeValid THEN reason ← IO.PutFR["%-18g%g booted: %g", IO.rope[reason], IO.char[lark.applicationMode], IO.time[lark.lastBoot]];
lark.event ← event;
};
IF print THEN {
IF ok THEN LarkControl.DisplayEvent[lark: lark, event: event, out: lark.h.log, reasonRope: reason, preRope: "Status of", short: TRUE]
ELSE LogEntry[lark: lark, rope: reason, endWithCR: TRUE];
};
};
SetApplicationMode: PUBLIC PROC[lark: LarkControl.LarkData, mode: CHAR] = {
IF lark=NIL THEN RETURN;
lark.applicationMode ← mode;
[]←PollLark[lark, TRUE];
};
RepairBreakpoint: PUBLIC PROC [h: TeleLoad.Handle, breakpoint: LarkControl.Breakpoint] = {
TeleLoad.FlushWrites[h];
TeleLoad.ResetCache[h];
TeleLoad.SetCacheSize[h, 1];
TeleLoad.Write[h, breakpoint.address, breakpoint.codeByte];
TeleLoad.FlushWrites[h];
TeleLoad.SetCacheSize[h, 0];
};
BootLark: PUBLIC PROC [lark: LarkControl.LarkData] = TRUSTED {
ok: BOOL;
cb: TeleLoad.CoreBlock ← NEW[TeleLoad.CoreBlockObject[SIZE[TeleLoad.CallPktObject]*2]];
cp: TeleLoad.CallPkt ← LOOPHOLE[BASE[DESCRIPTOR[cb.data]]];
sym: LarkPrograms.STObject;
tries: NAT;
cb.address ← 0;
cb.advice ← [FALSE, FALSE, 0];
[item: sym, found: ok] ← LarkPrograms.FindVariable[lark.program, "Boot"];
IF NOT ok THEN RETURN;
cp.proc ← Basics.LowHalf[sym.addr];
cp.args ← ALL[0];
cp.nargs ← 0;
cp.returnArg ← 0;
[ok, tries] ← TeleLoad.Call[lark.h, cb, 1];
};
LarkGVSet: PUBLIC PROC [lark: LarkControl.LarkData, setMode: BOOL, setProgram: BOOL] = {
programName: Rope.ROPENIL;
found: BOOLFALSE;
IF lark.larkMode = 'd THEN {
lark.h.log.PutF["%g: local mode override\n", IO.rope[lark.rName]];
Force a GV lookup next time.
RETURN;
};
SELECT NamesGV.GVIsAuthenticated[lark.rName] FROM
authentic, perhaps => found←TRUE;
bogus, nonexistent => lark.h.log.PutF["%g: Not known\n", IO.rope[lark.rName]];
unknown => lark.h.log.PutF["%g: Grapevine failure\n", IO.rope[lark.rName]];
ENDCASE => ERROR;
IF found THEN {
larkPos: INT;
programName ← NamesGV.GVGetAttribute[lark.rName, $program, "Lark"];
lark.h.log.PutF["%g program: %g\n", IO.rope[lark.rName], IO.rope[programName]];
Peel off the .lark, if there is one
lark.userRName ← NamesGV.GVGetAttribute[lark.rName, $owner, "Unknown"];
larkPos←-1;
DO
lp: INT ← Rope.Find[s1: lark.userRName, s2: ".lark", pos1: larkPos+1, case: FALSE];
IF lp<0 THEN EXIT ELSE larkPos←lp;
ENDLOOP;
IF larkPos >= 0 THEN lark.userRName ← Rope.Substr[base: lark.userRName, len: larkPos];
};
Labels.Set[lark.userRNameViewer, lark.userRName];
IF setMode THEN {
larkMode: Rope.ROPE;
larkMode ← NamesGV.GVGetAttribute[lark.rName, $mode, "U"];
IF larkMode.Length[]=0 THEN larkMode←"U";
lark.larkMode ← larkMode.Fetch[0];
};
IF setProgram THEN {
program: LarkPrograms.Program;
IF programName.Length[] > 0 THEN {
program ← LarkPrograms.FetchProgram[programName];
IF program = NIL THEN {
program ← LarkPrograms.ReadProgramFromDisk[objectFileName: programName, log: lark.h.log, addressSpace: main];
IF program # NIL THEN LarkPrograms.AddOrReplaceProgram[program];
};
IF program # NIL THEN {
lark.program ← program;
LarkControl.LogEntry[lark: lark];
lark.h.log.PutF["setting program to %g\n", IO.rope[lark.program.programName]];
}
ELSE LarkControl.LogEntry[lark: lark, rope: "setting program to NIL.\n"];
Now install a standard slave program, if any
programName ← Rope.Concat[programName, "SlaveRam.mb"];
program ← LarkPrograms.FetchProgram[programName];
IF program = NIL THEN {
program ← LarkPrograms.ReadProgramFromMBFile[mbFileName: programName, baseAddress: 02000H, log: NIL, addressSpace: slave];
IF program # NIL THEN LarkPrograms.AddOrReplaceProgram[program];
};
IF program # NIL THEN {
lark.slaveProgram ← program;
LarkControl.LogEntry[lark: lark];
lark.h.log.PutF[" setting slave program to %g\n", IO.rope[lark.slaveProgram.programName]];
};
};
};
};
LogEntry: PUBLIC PROC [lark: LarkControl.LarkData, rope: Rope.ROPENIL, endWithCR: BOOLFALSE] = {
lark.h.log.PutF["%g, %g", IO.time[], IO.rope[lark.nameRope]];
IF rope # NIL THEN {
lark.h.log.PutRope[": "];
lark.h.log.PutRope[rope];
}
ELSE lark.h.log.PutChar[' ];
IF endWithCR THEN lark.h.log.PutRope["\n"];
};
DisplayEvent: PUBLIC PROC [lark: LarkControl.LarkData, event: TeleLoad.EventRecordObject, out: IO.STREAM, reasonRope: Rope.ROPE, preRope: Rope.ROPE, short: BOOLFALSE] = {
longClock: LONG CARDINAL;
relays: Rope.ROPE;
LogEntry[lark: lark, rope: preRope];
out.PutF[" %04xH (%g)\n", IO.card[event.reason], IO.rope[reasonRope]];
longClock ← event.clockHigh;
longClock ← longClock * 65536;
longClock ← longClock + event.clockLow;
out.PutF[" clock %08x, switches: %04xH, advice: %04xH\n", IO.card[longClock], IO.card[event.bootSwitches], IO.card[event.advice]];
SELECT Basics.BITAND[event.monRelays, 9] FROM
0 => relays ← "TeleSet and Hookswitch active ";
1 => relays ← "TeleSet active ";
8 => relays ← "Hookswitch active ";
ENDCASE => relays ← NIL;
SELECT Basics.BITAND[event.monRelays, 6] FROM
2 => relays ← relays.Concat["A relay active "];
4 => relays ← relays.Concat["Off hook "];
6 => relays ← relays.Concat["Off hook and A relay active "];
ENDCASE => NULL;
out.PutRope[" "];
IF relays.Length[] > 0 THEN {
out.PutRope["Previously "];
out.PutRope[relays];
out.PutRope[", "];
};
out.PutF["TeleLoad Host: %b#%b# via %b\n", IO.card[event.tlNet], IO.card[event.tlHost], IO.card[event.tlImHost]];
IF NOT short THEN LarkControl.DisplayState[s: out, state: event.regs, indent: 6];
out.PutRope["% "];
IF lark.status # NIL THEN Labels.Set[lark.status, reasonRope];
};
VerifyInternal: PUBLIC PROC [lark: LarkControl.LarkData, program: LarkPrograms.Program, startAddress, stopAddress: TeleLoad.CoreAddress] = {
ENABLE TeleLoad.Failed => GOTO BadComm;
log: IO.STREAM ← lark.h.log;
h: TeleLoad.Handle ← lark.h;
blockCount: NAT;
blockAddress: TeleLoad.CoreAddress;
tries: NAT;
tcb: TeleLoad.CoreBlock ← NIL;
ok: BOOL;
endCode: TeleLoad.CoreAddress;
item: LarkPrograms.STObject;
found: BOOL;
IF stopAddress <= startAddress THEN {
[item: item, found: found] ← LarkPrograms.FindVariable[program, "endCode", FALSE];
endCode ← item.addr;
IF NOT found THEN {
[item: item, found: found] ← LarkPrograms.FindVariable[program, "monEndCode", FALSE];
endCode ← item.addr;
IF found THEN {
startAddress ← program.startState.Regs[IP];
stopAddress ← endCode;
}
ELSE {
log.PutRope["Can't find endCode symbol.\n"];
log.PutRope["Checking entire image\n"];
startAddress ← LAST[TeleLoad.CoreAddress];
stopAddress ← FIRST[TeleLoad.CoreAddress];
FOR l: LIST OF TeleLoad.CoreBlock ← program.program, l.rest WHILE l # NIL DO
IF l.first.address < startAddress THEN startAddress ← l.first.address;
IF l.first.address + l.first.count > stopAddress THEN stopAddress ← l.first.address + l.first.count;
ENDLOOP;
};
}
ELSE {
startAddress ← program.startState.Regs[IP];
stopAddress ← endCode;
};
};
log.PutF["%g: Checking %s between %04x and %04x: ", IO.rope[lark.nameRope], IO.rope[program.programName], IO.card[startAddress], IO.card[stopAddress]];
TeleLoad.FlushWrites[h];
FOR l: LIST OF TeleLoad.CoreBlock ← program.program, l.rest WHILE l # NIL DO
IF lark.world.pleaseStop THEN EXIT;
blockCount ← l.first.count;
blockAddress ← l.first.address;
IF l.first.address < startAddress THEN {
IF l.first.address + blockCount < startAddress THEN LOOP;
blockCount ← blockCount - (startAddress - l.first.address);
blockAddress ← startAddress;
};
IF l.first.address + blockCount > stopAddress THEN {
IF l.first.address > stopAddress THEN LOOP;
blockCount ← stopAddress - l.first.address;
};
IF tcb = NIL OR tcb.count # blockCount THEN tcb ← NEW[TeleLoad.CoreBlockObject[blockCount]];
tcb.address ← blockAddress;
tcb.advice ← [FALSE, FALSE, 0];
[ok, tries] ← IF program.addressSpace = main THEN TeleLoad.Fetch[h, tcb] ELSE TeleLoad.SlaveFetch[h, tcb];
IF ok THEN {
log.PutChar[IF tries # 1 THEN ('0 + MIN[tries, 9]) ELSE IF program.addressSpace = main THEN '! ELSE 's];
}
ELSE {
log.PutRope["\nTeleload Read Error\r"];
EXIT;
};
FOR i: CARDINAL IN [0..tcb.count) DO
IF lark.world.pleaseStop THEN EXIT;
IF tcb[i] # l.first[tcb.address + i - l.first.address] THEN {
log.PutF["addr: %04x is: %02x was %02x\n", IO.card[tcb.address+i], IO.card[tcb[i]], IO.card[l.first[tcb.address + i - l.first.address]]];
Process.CheckForAbort[];
};
ENDLOOP;
ENDLOOP;
lark.world.pleaseStop ← FALSE;
log.PutRope["\n"];
EXITS
BadComm => NULL;
};
}.
April 25, 1983 12:13 pm, LCS, created from old LarkPrograms.mesa
April 29, 1983 12:06 pm, LCS, new Event stuff
May 14, 1985 4:30:02 pm PDT, LCS, new Cedar 5, December
Swinehart, May 14, 1985 4:29:56 pm PDT
Cedar 6.0
changes to: DIRECTORY, IMPORTS, VerifyInternal