LarkProgramsImpl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved.
Stewart, December 21, 1983 3:51 pm
Last Edited by: Pier, May 30, 1984 12:36:27 pm PDT
Swinehart, April 6, 1987 10:12:44 am PDT
DIRECTORY
Basics USING [BITSHIFT, LowHalf],
Convert USING [CardFromRope, Error],
FS USING [Error, StreamOpen],
IntelHex,
IO,
LarkPrograms,
List USING [Remove],
Rope USING [Compare, Concat, Equal, Fetch, Find, Length, ROPE, Substr],
TeleLoad;
LarkProgramsImpl: CEDAR PROGRAM
IMPORTS Basics, Convert, FS, IntelHex, IO, List, Rope, TeleLoad
global variables
programs: LIST OF REF ANY;
nullItem: PUBLIC LarkPrograms.STObject ← ["???", 0, procedure];
monitor: LarkPrograms.Program;
slaveMonitor: LarkPrograms.Program;
Junk: SIGNAL = CODE;
ReadProgramFromMBFile:
PUBLIC
PROC [mbFileName: Rope.
ROPE, baseAddress: TeleLoad.CoreAddress, log:
IO.
STREAM ←
NIL, addressSpace: TeleLoad.AddressSpace ← main, wDir: Rope.
ROPE]
RETURNS [p: LarkPrograms.Program] =
TRUSTED {
trueFileName: Rope.ROPE ← mbFileName;
mbFileStream: IO.STREAM;
data: PACKED ARRAY [0..TeleLoad.MaxByte) OF TeleLoad.Byte;
count: NAT ← 0;
nameLength: NAT;
IWord:
PROC
RETURNS [x:
CARDINAL] =
TRUSTED {
x ← LOOPHOLE[mbFileStream.GetChar[], CARDINAL];
x ← Basics.BITSHIFT[x, 8];
x ← x + LOOPHOLE[mbFileStream.GetChar[], CARDINAL];
RETURN[x];
};
IByte:
PROC
RETURNS [x:
CARDINAL] =
TRUSTED {
x ← LOOPHOLE[mbFileStream.GetChar[], CARDINAL];
RETURN[x];
};
DataFlush:
PROC =
TRUSTED {
cb: TeleLoad.CoreBlock;
cb ← NEW[TeleLoad.CoreBlockObject[count]];
cb.address ← baseAddress;
FOR i:
CARDINAL
IN [0..count)
DO
cb.data[i] ← data[i];
ENDLOOP;
baseAddress ← baseAddress + count;
count ← 0;
p.program ← CONS[cb, p.program];
IF log # NIL THEN log.PutRope["! "];
p ← NEW[LarkPrograms.ProgramObject];
p.addressSpace ← addressSpace;
IF trueFileName =
NIL
THEN {
IF log # NIL THEN log.PutF["Bad file name\n"];
RETURN[NIL];
};
IF log # NIL THEN log.PutF[" ParseMB %g: ", IO.rope[trueFileName]];
IF Rope.Find[s1: trueFileName, s2: ".", pos1: 0, case: FALSE] = -1 THEN trueFileName ← Rope.Concat[trueFileName, ".mb"];
p.programName ← trueFileName;
mbFileStream ←
FS.StreamOpen[fileName: trueFileName, wDir: wDir !
FS.Error =>
TRUSTED {
IF log # NIL THEN log.PutRope[" ... file not found\n"];
CONTINUE;
}];
IF mbFileStream = NIL THEN RETURN[NIL];
Now read in the MB file!
{
IF IWord[] # 4 THEN GOTO BadFormat; -- boilerplate
IF IWord[] # 1 THEN GOTO BadFormat; -- memory number
IF IWord[] # 8 THEN GOTO BadFormat; -- 8 bit memory
nameLength ← 0;
DO
nameLength ← nameLength + 1;
IF IByte[] = 0 THEN EXIT;
ENDLOOP;
IF nameLength MOD 2 # 0 THEN [] ← IByte[]; -- skip to even boundary
IF IWord[] # 2 THEN GOTO BadFormat; -- set current memory and location
IF IWord[] # 1 THEN GOTO BadFormat; -- memory number
IF IWord[] # 0 THEN GOTO BadFormat; -- 8 bit memory
DO
SELECT IWord[]
FROM
0 => EXIT;
1 => NULL; -- memory data;
ENDCASE => GOTO BadFormat;
IF IWord[] # 0 THEN GOTO BadFormat; -- source line number (not used)
data[count] ← IByte[];
[] ← IByte[];
count ← count + 1;
IF count >= TeleLoad.MaxByte THEN DataFlush[];
ENDLOOP;
DataFlush[];
FOR i: TeleLoad.Registers8086 IN [AX..FL] DO p.startState.Regs[i] ← 0; ENDLOOP;
p.startState.Regs[SP] ← 54256; -- D3F0
p.startState.Regs[IP] ← Basics.LowHalf[baseAddress];
IF log # NIL THEN log.PutRope["\n"];
EXITS
BadFormat => {
IF log # NIL THEN log.PutRope[" Bad MB file format\n"];
p ← NIL;
};
};
mbFileStream.Close[];
};
ReadProgramFromDisk:
PUBLIC
PROC [objectFileName: Rope.
ROPE, log:
IO.
STREAM ←
NIL, addressSpace: TeleLoad.AddressSpace ← main, wDir: Rope.
ROPE]
RETURNS [p: LarkPrograms.Program] =
TRUSTED {
trueFileName: Rope.ROPE ← objectFileName;
myEndOf: IntelHex.EndOfProc = TRUSTED { RETURN[objectFileStream.EndOf[]]; };
myGetByte: IntelHex.GetByteProc = TRUSTED { RETURN[objectFileStream.GetChar[] - 0C]; };
myGetPosition: IntelHex.GetPositionProc =
TRUSTED {
RETURN[LOOPHOLE[objectFileStream.GetIndex[], LONG CARDINAL]];
};
mySetPosition: IntelHex.SetPositionProc = TRUSTED { objectFileStream.SetIndex[LOOPHOLE[p, INT]]; };
myPutC: IntelHex.PutCharProc = TRUSTED { IF log # NIL THEN log.PutChar[c]; };
Write an end record.
myPutEndRecord: IntelHex.PutEndRecordProc =
TRUSTED {
DataFlush[];
IF log # NIL THEN log.PutF[" ...End"];
};
Write a start record.
myPutStartRecord: IntelHex.PutStartRecordProc =
TRUSTED {
DataFlush[];
IF log # NIL THEN log.PutF[" ...Start at %04x:%04x", IO.int[frame], IO.int[bytepos]];
p.startState[CS] ← frame;
p.startState[IP] ← bytepos;
};
myDataByte: IntelHex.DataByteProc =
TRUSTED {
IF gnaflag OR adr#gda THEN DataFlush[];
gdata[gptr] ← d;
IF gptr = 0 THEN fda ← adr;
gptr ← gptr+1;
gda ← adr+1;
IF gptr >= TeleLoad.locMaxByte THEN DataFlush[];
};
DataFlush:
PROC =
TRUSTED {
cb: TeleLoad.CoreBlock;
IF gptr=0 THEN RETURN;
gnaflag ← FALSE;
cb ← NEW[TeleLoad.CoreBlockObject[gptr]];
cb.address ← fda;
FOR i:
CARDINAL
IN [0..gptr)
DO
cb.data[i] ← gdata[i];
ENDLOOP;
gptr ← 0;
p.program ← CONS[cb, p.program];
IF log # NIL THEN log.PutRope["! "];
gda: LONG CARDINAL ← LAST[LONG CARDINAL];
fda: LONG CARDINAL;
gptr: CARDINAL ← 0;
gdata: PACKED ARRAY [0..TeleLoad.MaxByte) OF TeleLoad.Byte;
gnaflag: BOOLEAN ← TRUE;
objectFileStream: IO.STREAM;
{
myIStream: IntelHex.IStream ← [
endof: myEndOf,
get: myGetByte,
GetPosition: myGetPosition,
SetPosition: mySetPosition
];
myOStream: IntelHex.OStream ← [
PutEndRecord: myPutEndRecord,
PutStartRecord: myPutStartRecord,
DataByte: myDataByte
];
p ← NEW[LarkPrograms.ProgramObject];
p.addressSpace ← addressSpace;
IF trueFileName =
NIL
THEN {
IF log # NIL THEN log.PutF["Bad file name\n"];
RETURN[NIL];
};
IF log # NIL THEN log.PutF[" Parse %g: ", IO.rope[trueFileName]];
IF Rope.Find[s1: trueFileName, s2: ".", pos1: 0, case: FALSE] = -1 THEN trueFileName ← Rope.Concat[trueFileName, ".obj"];
p.programName ← trueFileName;
objectFileStream ←
FS.StreamOpen[fileName: trueFileName, wDir: wDir !
FS.Error =>
TRUSTED {
IF log # NIL THEN log.PutRope[" ... file not found\n"];
CONTINUE;
}];
IF objectFileStream = NIL THEN RETURN[NIL];
FOR i: TeleLoad.Registers8086 IN [AX..FL] DO p.startState.Regs[i] ← 0; ENDLOOP;
p.startState.Regs[SP] ← 54256; -- D3F0
IF
NOT IntelHex.ProcessFile[in: myIStream, out: myOStream, errs: myPutC]
THEN {
IF log # NIL THEN log.PutRope[" ...IntelHex Failure"];
p ← NIL;
}
ELSE {
extension: INT ← Rope.Find[s1: p.programName, s2: ".obj", case: FALSE];
patchFileName: Rope.ROPE;
IF extension # -1 THEN BuildSearchTable[program: p, name: Rope.Substr[base: p.programName, start: 0, len: extension], log: log, wDir: wDir];
IF p.searchTable # NIL AND log # NIL THEN log.PutF[" ... symbol table read"];
Autonmatically incorporate patches from a standard place
ResetPatchList[p];
patchFileName ← Rope.Concat[Rope.Substr[base: p.programName, start: 0, len: extension], ".patches"];
p.patchList ← PatchFromFile[program: p, fileName: patchFileName, log: log, wDir: wDir];
};
IF log # NIL THEN log.PutRope["\n"];
objectFileStream.Close[];
};
};
ResetPatchList:
PUBLIC
PROC [program: LarkPrograms.Program] = {
IF program#NIL THEN program.patchList ← NIL;
};
PatchProgramWord:
PUBLIC
PROC [program: LarkPrograms.Program, address: TeleLoad.CoreAddress, wordValue:
CARDINAL, name: Rope.
ROPE ←
NIL] = {
item: LarkPrograms.PatchItem ← NEW[LarkPrograms.PatchItemObject ← [name: name, address: address, wordValue: wordValue]];
program.patchList ← CONS[item, program.patchList];
};
Monitor:
PUBLIC
PROC [log:
IO.
STREAM ←
NIL, addressSpace: TeleLoad.AddressSpace ← main, wDir: Rope.
ROPE]
RETURNS [mon: LarkPrograms.Program] = {
IF addressSpace = main
THEN {
IF monitor # NIL THEN RETURN[monitor];
monitor ← ReadProgramFromDisk[objectFileName: "LarkMon", log: log, addressSpace: main, wDir: wDir];
IF monitor.nameTable = NIL OR monitor.searchTable = NIL THEN monitor ← NIL;
mon ← monitor;
}
ELSE {
IF slaveMonitor # NIL THEN RETURN[slaveMonitor];
slaveMonitor ← ReadProgramFromMBFile[mbFileName: "LarkSlave.mb", baseAddress: 0E000H, log: log, addressSpace: slave, wDir: wDir];
BuildSearchTable[program: slaveMonitor, name: "LarkSlave", log: log, wDir: wDir];
mon ← slaveMonitor;
};
};
GetHex:
PROC [h:
IO.
STREAM]
RETURNS [n:
LONG
CARDINAL] = {
r: Rope.ROPE ← h.GetTokenRope[].token;
n ← Convert.CardFromRope[r, 16 ! Convert.Error => SIGNAL Junk];
};
GetHex: PROC [h: IO.Handle] RETURNS [LONG CARDINAL] = {
r: Rope.ROPE ← h.GetToken[];
v: Convert.Value ← Convert.Parse[text: [rope[r]], template: [unsigned[base: 16]]].value;
RETURN[NARROW[v, Convert.Value[unsigned]! ANY => SIGNAL Junk].unsigned];
};
BuildSearchTable:
PUBLIC
PROC [program: LarkPrograms.Program, name: Rope.
ROPE, log:
IO.
STREAM ←
NIL, wDir: Rope.
ROPE] = {{
tabFile: IO.STREAM;
count: INT;
type: Rope.ROPE;
program.searchTable ← NIL;
program.nameTable ← NIL;
tabFile ←
FS.StreamOpen[fileName: name, wDir: wDir !
FS.Error =>
TRUSTED {
tabFile ← NIL;
CONTINUE;
}];
IF tabFile =
NIL
THEN tabFile ←
FS.StreamOpen[fileName: Rope.Concat[name, ".syms"], wDir: wDir !
FS.Error =>
TRUSTED {
tabFile ← NIL;
IF log # NIL THEN log.PutF["Cannot find %g or %g\n", IO.rope[name], IO.rope[Rope.Concat[name, ".syms"]]];
GOTO Die;
}];
type ← tabFile.GetTokenRope[].token;
IF NOT Rope.Equal[type, "Names", FALSE] THEN GOTO BadFormat;
count ← tabFile.GetInt[];
program.nameTable ←NEW[LarkPrograms.SymTabObject[count]];
FOR i:
INT
IN [0..count)
DO
program.nameTable[i].type ← IF Rope.Equal[tabFile.GetTokenRope[].token, "V"] THEN variable ELSE procedure;
program.nameTable[i].addr ← GetHex[tabFile];
program.nameTable[i].id ← tabFile.GetTokenRope[].token;
ENDLOOP;
BubbleSortNameTable[program.nameTable];
type ← tabFile.GetTokenRope[].token;
IF NOT Rope.Equal[type, "Addresses", FALSE] THEN GOTO BadFormat;
count ← tabFile.GetInt[];
program.searchTable ← NEW[LarkPrograms.SymTabObject[count]];
FOR i:
INT
IN [0..count)
DO
program.searchTable[i].type ← IF Rope.Equal[tabFile.GetTokenRope[].token, "V"] THEN variable ELSE procedure;
program.searchTable[i].addr ← GetHex[tabFile];
program.searchTable[i].id ← tabFile.GetTokenRope[].token;
ENDLOOP;
BubbleSortSearchTable[program.searchTable];
tabFile.Close[];
EXITS
BadFormat => {
IF log # NIL THEN log.PutF["Bad symbols format\n"];
program.searchTable ← NIL;
program.nameTable ← NIL;
NULL;
};
Die => NULL;
};};
BubbleSortSearchTable:
PROC [searchTable: LarkPrograms.SymbolTable] = {
anyswap: BOOL ← TRUE;
temp: LarkPrograms.STObject;
WHILE anyswap
DO
anyswap ← FALSE;
FOR i:
NAT
IN [0..searchTable.length-1)
DO
IF searchTable[i].addr > searchTable[i+1].addr
THEN {
temp ← searchTable[i];
searchTable[i] ← searchTable[i+1];
searchTable[i+1] ← temp;
anyswap ← TRUE;
};
ENDLOOP;
ENDLOOP;
};
BubbleSortNameTable:
PROC [nameTable: LarkPrograms.SymbolTable] = {
anyswap: BOOL ← TRUE;
temp: LarkPrograms.STObject;
WHILE anyswap
DO
anyswap ← FALSE;
FOR i:
NAT
IN [0..nameTable.length-1)
DO
IF Rope.Compare[nameTable[i].id, nameTable[i+1].id,
FALSE] = greater
THEN {
temp ← nameTable[i];
nameTable[i] ← nameTable[i+1];
nameTable[i+1] ← temp;
anyswap ← TRUE;
};
ENDLOOP;
ENDLOOP;
};
ProcedureEnclosing:
PUBLIC
PROC [program: LarkPrograms.Program, addr: TeleLoad.CoreAddress, searchMonitor:
BOOL ←
TRUE, wDir: Rope.
ROPE]
RETURNS [s: LarkPrograms.STObject] = {
u: NAT;
l: NAT ← 0;
i: NAT;
query: INT = addr;
IF program =
NIL
OR program.searchTable =
NIL
THEN {
IF searchMonitor THEN RETURN[ProcedureEnclosing[program: Monitor[wDir: wDir], addr: addr, searchMonitor: FALSE, wDir: wDir]]
ELSE RETURN[nullItem];
};
u ← program.searchTable.length - 1;
DO
i ← (l+u)/2;
IF u < l THEN ERROR;
IF program.searchTable[i].addr <= query
THEN {
IF i = (program.searchTable.length-1)
THEN {
IF searchMonitor
THEN {
s ← ProcedureEnclosing[program: Monitor[wDir: wDir], addr: addr, searchMonitor: FALSE, wDir: wDir];
IF s # nullItem THEN RETURN[s];
};
RETURN [program.searchTable[i]];
};
IF program.searchTable[i+1].addr > query THEN RETURN [program.searchTable[i]]
ELSE l ← i + 1;
}
ELSE {
IF i = 0 THEN RETURN [nullItem]
ELSE u ← i - 1;
};
ENDLOOP;
};
FindVariable:
PUBLIC
PROC [program: LarkPrograms.Program, name: Rope.
ROPE, searchMonitor:
BOOL ←
TRUE, wDir: Rope.
ROPE]
RETURNS [item: LarkPrograms.STObject, found:
BOOL] = {{
u: NAT;
l: NAT ← 0;
i: NAT;
IF program = NIL OR program.nameTable = NIL THEN GOTO TryMonitor;
u ← program.nameTable.length - 1;
DO
i ← (l+u)/2;
IF u < l THEN GOTO TryMonitor;
SELECT Rope.Compare[name, program.nameTable[i].id,
FALSE]
FROM
less => {
IF i = 0 THEN GOTO TryMonitor
ELSE u ← i - 1;
};
greater => {
IF i = (program.nameTable.length-1) THEN GOTO TryMonitor
ELSE l ← i + 1;
};
equal => RETURN [item: program.nameTable[i], found: TRUE];
ENDCASE => ERROR;
ENDLOOP;
EXITS
TryMonitor => {
IF searchMonitor
THEN {
[item: item, found: found] ← FindVariable[program: Monitor[wDir: wDir], name: name, searchMonitor: FALSE, wDir: wDir];
RETURN[item: item, found: found];
}
ELSE {
IF IsNumber[name]
THEN {
item.id ← "???";
item.addr ← Convert.CardFromRope[name, 16 ! Convert.Error => GOTO GiveUp];
item.type ← variable;
RETURN[item: item, found: TRUE];
}
ELSE RETURN[item: nullItem, found: FALSE];
};
};
};
EXITS
GiveUp => RETURN[item: nullItem, found: FALSE];
};
IsNumber:
PROC [r: Rope.
ROPE]
RETURNS [
BOOL] = {
FOR i:
INT
IN [0..r.Length[])
DO
SELECT r.Fetch[i]
FROM
IN ['0..'9], IN ['a..'f], IN ['A..'F] => LOOP;
ENDCASE => RETURN [FALSE];
ENDLOOP;
RETURN[TRUE];
};
EnumeratePrograms:
PUBLIC
PROC [proc:
PROC [program: LarkPrograms.Program]
RETURNS [stop:
BOOL]] = {
list: LIST OF REF ANY ← programs;
WHILE list #
NIL
DO
IF proc[NARROW[list.first, LarkPrograms.Program]] THEN EXIT;
list ← list.rest;
ENDLOOP;
};
FetchProgram:
PUBLIC
PROC [programName: Rope.
ROPE]
RETURNS [p: LarkPrograms.Program] =
TRUSTED {
searchName: Rope.ROPE ← programName;
lmap:
PROC [item:
REF
ANY] =
TRUSTED {
lp: LarkPrograms.Program ← NARROW[item];
IF Rope.Equal[lp.programName, searchName, FALSE] THEN p ← lp;
};
IF Rope.Find[s1: searchName, s2: ".", pos1: 0, case: FALSE] = -1 THEN searchName ← Rope.Concat[searchName, ".obj"];
MyMap[list: programs, proc: lmap];
};
AddOrReplaceProgram:
PUBLIC
PROC [program: LarkPrograms.Program] = {
old: LarkPrograms.Program ← FetchProgram[program.programName];
IF old # NIL THEN programs ← List.Remove[ref: old, list: programs];
programs ← CONS[program, programs];
IF monitor # NIL AND Rope.Equal[program.programName, monitor.programName, FALSE] THEN monitor ← program;
};
MyMap:
PROC [list:
LIST
OF
REF
ANY, proc:
PROC [item:
REF
ANY]] = {
WHILE list #
NIL
DO
proc[list.first];
list ← list.rest;
ENDLOOP;
};
PatchFromFile:
PUBLIC
PROC [program: LarkPrograms.Program, fileName: Rope.
ROPE, log:
IO.
STREAM ←
NIL, wDir: Rope.
ROPE]
RETURNS [
LIST
OF LarkPrograms.PatchItem] = {
id: Rope.ROPE;
value: CARDINAL;
offset: NAT ← 0;
patFile: IO.STREAM;
line: Rope.ROPE;
lis: IO.STREAM;
pl: LIST OF LarkPrograms.PatchItem ← NIL;
item: LarkPrograms.STObject;
found: BOOL;
{
IF log # NIL THEN log.PutF["Patches from file %g\n", IO.rope[fileName]];
patFile ←
FS.StreamOpen[fileName: fileName, wDir: wDir !
FS.Error =>
TRUSTED {
patFile ← NIL;
GOTO GiveUp;
}];
IF patFile =
NIL
THEN {
IF log # NIL THEN log.PutF["Cannot find %g\n", IO.rope[fileName]];
RETURN[NIL];
};
DO {
ENABLE {
IO.Error => {
IF log # NIL THEN log.PutRope["IO.Error!\n"];
pl ← NIL;
GOTO CloseFile;
};
IO.EndOfStream => GOTO CloseFile;
};
IF patFile.EndOf[] THEN GOTO CloseFile;
line ← IO.GetLineRope[patFile];
IF line.Length <= 1 THEN GOTO CloseFile;
IF line.Fetch[0] = '-
THEN {
IF log # NIL THEN log.PutF["%g\n", IO.rope[line]];
GOTO TryNextLine;
};
lis ← IO.RIS[rope: line, oldStream: lis];
id ← lis.GetTokenRope[].token;
offset ← GetHex[lis];
value ← GetHex[lis];
[item: item, found: found] ← FindVariable[program, id,,wDir];
IF
NOT found
THEN {
IF log # NIL THEN log.PutF[" %g: not found\n", IO.rope[id]];
RETURN [NIL];
}
ELSE pl ← CONS[NEW[LarkPrograms.PatchItemObject ← [name: IF offset = 0 THEN id ELSE IO.PutFR["%g+%xH", IO.rope[id], IO.card[offset]], address: item.addr + offset, wordValue: value]], pl];
EXITS
TryNextLine => NULL;
};
ENDLOOP;
EXITS
CloseFile => patFile.Close[];
GiveUp => NULL;
};
RETURN [pl];
};
}.
April 25, 1983 12:20 pm, LCS, created from old LarkPrograms
August 10, 1983 12:02 pm, LCS, bug in PatchProgramWord
August 10, 1983 12:02 pm, LCS, added ReadProgramFromMBFile
August 30, 1983 3:16 pm, LCS, added slaveMonitor
Swinehart, December 17, 1986 5:13:10 pm PST
Force root directory lookups. That's not as good as making LarkControl do the right thing about working directories, but it's better than having lookups be random.
changes to: ReadProgramFromMBFile, ReadProgramFromDisk, BuildSearchTable, PatchFromFile