-- Cedar Remote Debugging: MapLog handling

-- WVMMapLog.mesa

-- Andrew Birrell August 2, 1983 1:35 pm

DIRECTORY
CPSwapDefs  USING[ ExternalStateVector, SwapInfo ],
DebuggerFormat USING[ ExternalStateVector, Run, SwapInfo, VersionID, VMRunTable ],
DeviceTypes USING[ sa4000 ],
PSB    USING[ PDA ],
VMMapLog,
WorldVM  USING[Address, CopyRead, World],
WVMPrivate;

WVMMapLog: MONITOR
IMPORTS WorldVM, WVMPrivate
EXPORTS WVMPrivate, WorldVM =

BEGIN

MapLogObject: PUBLIC TYPE = RECORD[
found: CARDINAL ← 0, -- maplog entries read in so far --
entries: REF DebuggerFormat.VMRunTable];

MapLog: TYPE = REF MapLogObject;

CreateMapLog: PUBLIC PROC[world: WorldVM.World] RETURNS[ MapLog ] =
BEGIN
RETURN[NEW[MapLogObject ← [0,NIL]]]
END;

oldESVVersion: CARDINAL = 08130; -- Rubicon Pilot ESV format

ReadMapLog: PUBLIC PROC[world: WorldVM.World, log: MapLog]
RETURNS[patchTable, loadState: WorldVM.Address] =
BEGIN
swapInfo: DebuggerFormat.SwapInfo;
swapInfoAddr: WorldVM.Address = LOOPHOLE[@PSB.PDA.available];
esv: DebuggerFormat.ExternalStateVector;
esvAddr: WorldVM.Address;
WorldVM.CopyRead[world, swapInfoAddr, SIZE[DebuggerFormat.SwapInfo],
@swapInfo];
esvAddr ← LOOPHOLE[swapInfo.externalStateVector];
WorldVM.CopyRead[world, esvAddr, SIZE[DebuggerFormat.ExternalStateVector],
@esv];
loadState ← WVMPrivate.PageAddress[esv.loadstatepage];
SELECT esv.versionident FROM
oldESVVersion =>
patchTable ← GetPilotMapLog[world, LOOPHOLE[@esv], log];
DebuggerFormat.VersionID =>
BEGIN
IF esv.vmRunTable # NIL
THEN BEGIN
temp: ARRAY [0..SIZE[DebuggerFormat.VMRunTable[0]]) OF WORD;
cheat: POINTER TO DebuggerFormat.VMRunTable = LOOPHOLE[@temp];
WorldVM.CopyRead[world,
LOOPHOLE[esv.vmRunTable, WorldVM.Address],
SIZE[DebuggerFormat.VMRunTable[0]],
@temp ];
log.entries ← NEW[DebuggerFormat.VMRunTable[cheat.length]];
WorldVM.CopyRead[world,
LOOPHOLE[esv.vmRunTable, WorldVM.Address],
SIZE[DebuggerFormat.VMRunTable[cheat.length]],
LOOPHOLE[log.entries, LONG POINTER] ];
log.found ← log.entries.nRuns;
END;
patchTable ← LOOPHOLE[LONG[NIL]];
END;
ENDCASE => patchTable ← LOOPHOLE[LONG[NIL]]; -- treat as if there was no maplog
END;

GetPilotMapLog: PROC[world: WorldVM.World,
esv: POINTER TO CPSwapDefs.ExternalStateVector,
log: MapLog]
RETURNS[patchTable: WorldVM.Address] =
BEGIN
descr: VMMapLog.Descriptor;
descrAddr: WorldVM.Address = LOOPHOLE[esv.mapLog];
logBase: WorldVM.Address;
logSize: CARDINAL; -- number of entries, excluding self --
mapLogEntry: VMMapLog.Entry;
Translate: PROC =
BEGIN
WITH entry: mapLogEntry SELECT FROM
disk =>
BEGIN
log.entries[log.found] ← [
page: mapLogEntry.page,
count: mapLogEntry.count,
deviceType: DeviceTypes.sa4000,
deviceOrdinal: 0,
diskPage: [entry.diskPage],
labelCheck: entry.labelCheck
];
log.found ← log.found+1;
END;
ENDCASE => NULL;
END;
WorldVM.CopyRead[world, descrAddr, SIZE[VMMapLog.Descriptor],
@descr];
patchTable ← LOOPHOLE[descr.patchTable];
logBase ← WVMPrivate.PageAddress[descr.self.page];
logSize ← LOOPHOLE[descr.limit, CARDINAL] / SIZE[VMMapLog.Entry];
log.entries ← NEW[DebuggerFormat.VMRunTable[logSize+1--self--]];
log.found ← 0;
mapLogEntry ← descr.self; Translate[];
IF logBase # 0 AND logSize > 0
THEN FOR a: WorldVM.Address ← logBase+LOOPHOLE[descr.reader,CARDINAL],
a+SIZE[VMMapLog.Entry]
UNTIL a = logBase+LOOPHOLE[descr.writer,CARDINAL]
DO WorldVM.CopyRead[world, a, SIZE[VMMapLog.Entry], @mapLogEntry]; Translate[];
ENDLOOP;
END;

LocalPatchTable: PUBLIC PROC RETURNS[patchTable: WorldVM.Address] =
BEGIN
swapInfo: LONG POINTER TO CPSwapDefs.SwapInfo = LOOPHOLE[@PSB.PDA.available];
descr: LONG POINTER TO VMMapLog.Descriptor =
swapInfo.externalStateVector.mapLog;
RETURN[LOOPHOLE[descr.patchTable]]
END;

AddressFault: PUBLIC ERROR[mempage: WVMPrivate.PageNumber] = CODE;

lastEntryIndex: CARDINAL ← 0; -- hint for log entry to try first --

GetMapLogEntry: PUBLIC ENTRY PROC[log: MapLog, mempage: WVMPrivate.PageNumber]
RETURNS[ WVMPrivate.DiskAddress ] =
BEGIN
ENABLE UNWIND => NULL;
entry: LONG POINTER TO DebuggerFormat.Run ←
IF lastEntryIndex IN [0..log.found) -- lastEntryIndex may be for another log!
THEN @log.entries[lastEntryIndex]
ELSE NIL;
IF entry = NIL
OR mempage NOT IN [entry.page..entry.page+entry.count)
THEN -- sigh! Need to search for it. --
BEGIN
FOR i: CARDINAL IN [0..log.found)
DO entry ← @log.entries[lastEntryIndex←i];
IF mempage IN [entry.page..entry.page+entry.count)
THEN EXIT;
REPEAT FINISHED => ERROR AddressFault[mempage]
ENDLOOP;
END;
RETURN[[entry.deviceType, entry.deviceOrdinal, entry.diskPage, mempage-entry.page, entry.labelCheck]];
END;

END.