-- XDUserSegments.Mesa
-- Edited by:
-- Sandman on July 22, 1980 5:07 PM
-- Johnsson on August 30, 1978 12:02 PM
-- Barbara on April 6, 1979 12:22 PM
-- Bruce on October 3, 1980 5:04 PM
DIRECTORY
AltoDefs USING [BYTE, BytesPerPage, PageNumber],
AltoFileDefs USING [eofDA, FP],
BcdDefs USING [Base, CodeDesc, FTNull, FTSelf, GFTIndex, MTIndex],
BcdOps USING [BcdBase, FTHandle, MTHandle, NameString, ProcessModules, SGHandle],
ControlDefs USING [InstWord],
DebugFormat USING [CodeObject],
DebugOps USING [CacheNewFile, LongREAD, LongWRITE, ShortCopyREAD, ShortCopyWRITE],
DLoadState USING [Acquire, AcquireBcd, MapRealToConfig, ReleaseBcd, Release],
Drum USING [DrumItem, Handle],
Frames USING [Invalid],
Gf USING [Check, GFI, Handle, Original],
Init USING [bootLoaded],
Inline USING [LongNumber],
MachineDefs USING [BYTE, BytePC, FHandle, GFTIndex, GlobalFrame, GFHandle, NullConfig],
Mopcodes USING [zRBL],
SegmentDefs USING [
AddressFromPage, BankIndex, DefaultAccess, DeleteFileSegment, FileHandle, FileHint,
FileObject, FileSegmentAddress, FileSegmentHandle, FileSegmentObject,
InsertFile, InvalidFP, LongAddressFromPage, LongVMtoFileSegment, NewFileSegment,
PageFromAddress, PageNumber, Read, ReleaseFile, SegmentFault, SetEndOfFile,
SwapIn, SwapOut, Unlock],
State USING [GetGS, GSHandle],
Storage USING [Free, Node],
String USING [AppendString],
Strings USING [AppendSubString, SubStringDescriptor];
XDUserSegments: PROGRAM
IMPORTS BcdOps, DebugOps, DLoadState, Frames, Gf, Init,
SegmentDefs, State, Storage, String, Strings
EXPORTS DebugOps, Drum, Gf =
BEGIN OPEN DebugOps, DebugFormat, Gf, Drum, MachineDefs;
FrameHandle: TYPE = MachineDefs.FHandle;
GlobalFrameHandle: TYPE = MachineDefs.GFHandle;
FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle;
NoCode: ERROR = CODE;
data: State.GSHandle ← State.GetGS[];
-- Utilities
UserSegment: SegmentDefs.FileSegmentObject;
ReadUserSegment: PROCEDURE [s: FileSegmentHandle] RETURNS [FileSegmentHandle] =
BEGIN
ShortCopyREAD[to: @UserSegment, from: s,
nwords: SIZE[SegmentDefs.FileSegmentObject]];
RETURN [@UserSegment]
END;
WriteUserSegment: PROCEDURE [s: FileSegmentHandle] =
BEGIN
ShortCopyWRITE[ to: s, from: @UserSegment,
nwords: SIZE[SegmentDefs.FileSegmentObject]];
END;
-- "Swapping Drum" and user code manipulation
diHead: Handle ← NIL;
endHint: SegmentDefs.FileHint;
endPage: AltoDefs.PageNumber;
drumFile: SegmentDefs.FileHandle;
MoveToDrum: PROCEDURE [f: GlobalFrameHandle, co: CodeObject] =
BEGIN
LocateCode[f];
IF gfCache.seg # NIL THEN Alloc[gfCache.seg].di.co ← co;
FlushCodeCache[];
RETURN
END;
Alloc: PUBLIC PROCEDURE [useg: FileSegmentHandle]
RETURNS [di: Handle] =
BEGIN OPEN SegmentDefs;
p: Handle;
lfo: FileObject;
tfile: FileHandle = @lfo; -- copy of user file object
tseg: FileSegmentHandle; -- copy of user segment
dseg: FileSegmentHandle = MapUSeg[useg];
old: FileHandle = dseg.file;
di ← Storage.Node[SIZE[DrumItem]];
di.next ← NIL;
di.dseg ← dseg;
-- copy values from user segment
tseg ← ReadUserSegment[di.useg ← useg];
di.oldBase ← tseg.base;
di.oldFile ← tseg.file;
WITH t: tseg SELECT FROM
disk => di.oldHint ← t.hint;
ENDCASE => ERROR RemoteSeg[useg
! UNWIND => Storage.Free[di]];
-- remove segment from user's file object
ShortCopyREAD[to: tfile, from: tseg.file, nwords: SIZE[FileObject]];
tfile.lock ← tfile.lock + 1;
tfile.segcount ← tfile.segcount - 1;
IF tseg.swappedin THEN tfile.swapcount ← tfile.swapcount - 1;
ShortCopyWRITE[from: tfile, to: tseg.file, nwords: SIZE[FileObject]];
-- move user segment to drum file
tseg.file ← data.ESV.drumFile;
tseg.base ← endPage;
-- reflect new seg and swap counts in users drum file object
ShortCopyREAD[
to: tfile, from: data.ESV.drumFile, nwords: SIZE[FileObject]];
tfile.segcount ← tfile.segcount + 1;
IF tseg.swappedin THEN tfile.swapcount ← tfile.swapcount + 1;
ShortCopyWRITE[
from: tfile, to: data.ESV.drumFile, nwords: SIZE[FileObject]];
SwapIn[dseg];
dseg.write ← TRUE;
-- update seg and swap counts for debugger's files
old.swapcount ← old.swapcount - 1;
IF (old.segcount ← old.segcount - 1) = 0 THEN
ReleaseFile[old];
drumFile.segcount ← drumFile.segcount + 1;
drumFile.swapcount ← drumFile.swapcount + 1;
-- move drum segment to drum file
dseg.file ← drumFile;
dseg.base ← endPage;
WITH d: dseg SELECT FROM
disk => d.hint ← endHint;
ENDCASE;
endPage ← endPage + dseg.pages;
Unlock[dseg];
SwapOut[dseg !
SegmentFault =>
BEGIN
SetEndOfFile[drumFile,endPage-1,AltoDefs.BytesPerPage];
RETRY
END];
WITH d: dseg SELECT FROM
disk => endHint ← d.hint;
ENDCASE;
WITH t: tseg SELECT FROM
disk => t.hint ← endHint;
ENDCASE;
WriteUserSegment[useg];
dseg.write ← FALSE;
-- add new item to end of list
IF diHead = NIL THEN diHead ← di
ELSE FOR p ← diHead, p.next UNTIL p.next = NIL DO
NULL;
REPEAT FINISHED => p.next ← di;
ENDLOOP;
RETURN
END;
Free: PUBLIC PROCEDURE [f: GlobalFrameHandle] =
BEGIN
LocateCode[f];
IF gfCache.seg # NIL THEN Remove[gfCache.seg];
FlushCodeCache[];
RETURN
END;
Remove: PUBLIC PROCEDURE [useg: FileSegmentHandle] =
BEGIN OPEN SegmentDefs;
lfo: FileObject;
tfile: FileHandle = @lfo; -- copy of user file object
tseg: FileSegmentHandle; -- copy of user segment
prev, di: Handle;
-- find item on the list
prev ← NIL;
FOR di ← diHead, di.next UNTIL di = NIL DO
IF di.useg = useg THEN EXIT;
prev ← di;
REPEAT FINISHED => RETURN
ENDLOOP;
IF prev = NIL THEN diHead ← di.next
ELSE prev.next ← di.next;
-- put old values back into user segment
tseg ← ReadUserSegment[useg];
tseg.file ← di.oldFile;
tseg.base ← di.oldBase;
WITH t: tseg SELECT FROM
disk => t.hint ← di.oldHint;
ENDCASE;
-- add segment to original file
ShortCopyREAD[to: tfile, from: tseg.file, nwords: SIZE[FileObject]];
tfile.lock ← tfile.lock - 1;
tfile.segcount ← tfile.segcount + 1;
IF tseg.swappedin THEN tfile.swapcount ← tfile.swapcount + 1;
ShortCopyWRITE[from: tfile, to: tseg.file, nwords: SIZE[FileObject]];
-- remove segment from drum file
ShortCopyREAD[
to: tfile, from: data.ESV.drumFile, nwords: SIZE[FileObject]];
tfile.segcount ← tfile.segcount - 1;
IF tseg.swappedin THEN tfile.swapcount ← tfile.swapcount - 1;
ShortCopyWRITE[
from: tfile, to: data.ESV.drumFile, nwords: SIZE[FileObject]];
WriteUserSegment[useg];
-- update end values and shuffle
WITH s: di.dseg SELECT FROM
disk => endHint ← s.hint;
ENDCASE;
endPage ← di.dseg.base;
DeleteFileSegment[di.dseg]; -- delete the real debugger segment
ShuffleDrum[di.next];
Storage.Free[di];
RETURN
END;
CodeOnDrum: PROCEDURE [co: CodeObject] RETURNS [BOOLEAN] =
BEGIN
di: Handle;
FOR di ← diHead, di.next UNTIL di = NIL DO
IF di.co = co THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
END;
ShuffleDrum: PROCEDURE [di: Handle] =
-- Starting with di, shuffle segments to lower addresses on the drum
-- and update the user's copies
BEGIN OPEN SegmentDefs;
seg: FileSegmentHandle;
useg: FileSegmentHandle;
UNTIL di = NIL DO
SwapIn[seg ← di.dseg];
useg ← ReadUserSegment[di.useg];
useg.base ← seg.base ← endPage;
WITH s: seg SELECT FROM
disk => s.hint ← endHint;
ENDCASE;
WITH u: useg SELECT FROM
disk => u.hint ← endHint;
ENDCASE;
WriteUserSegment[di.useg];
endPage ← endPage + seg.pages;
Unlock[seg];
SwapOut[seg];
WITH s: seg SELECT FROM
disk => endHint ← s.hint;
ENDCASE;
di ← di.next;
ENDLOOP;
END;
RemoteSeg: PUBLIC SIGNAL [seg: FileSegmentHandle] = CODE;
MapUSeg: PUBLIC PROCEDURE [useg: FileSegmentHandle] RETURNS [seg: FileSegmentHandle]=
-- Return a segment in the debugger space for the given user segment
BEGIN OPEN SegmentDefs;
tempseg: FileSegmentHandle;
localfp: AltoFileDefs.FP;
tempseg ← ReadUserSegment[useg];
IF tempseg = NIL OR tempseg.file = NIL THEN RETURN[NIL];
ShortCopyREAD[
from: @tempseg.file.fp,
to: @localfp,
nwords: SIZE[AltoFileDefs.FP]];
seg ← NewFileSegment[
InsertFile[@localfp, Read],tempseg.base,tempseg.pages,Read];
WITH s: seg SELECT FROM
disk =>
s.hint ← WITH t: tempseg SELECT FROM
disk => t.hint,
ENDCASE => FileHint[AltoFileDefs.eofDA, 0];
ENDCASE;
RETURN
END;
Initialize: PUBLIC PROCEDURE =
BEGIN
next: Handle;
UNTIL diHead = NIL DO
next ← diHead.next;
SegmentDefs.DeleteFileSegment[diHead.dseg];
Storage.Free[diHead];
diHead ← next;
ENDLOOP;
drumFile ← data.debuggeeFH;
endHint ← [AltoFileDefs.eofDA, 0];
endPage ← 256; -- after core image
SegmentDefs.SetEndOfFile[drumFile,endPage+19,AltoDefs.BytesPerPage];
RETURN
END;
GetInst: PROC [offset: INTEGER] RETURNS [iword: ControlDefs.InstWord] = {
SegmentDefs.SwapIn[gfCache.dseg];
iword ← (SegmentDefs.FileSegmentAddress[gfCache.dseg]+gfCache.offset+offset)↑;
SegmentDefs.Unlock[gfCache.dseg]};
ReadCodeByte: PUBLIC PROCEDURE [gf: GFHandle, pc: BytePC] RETURNS [BYTE] = {
iword: ControlDefs.InstWord ← ReadCodeWord[gf,pc/2];
RETURN[IF pc MOD 2 = 0 THEN iword.evenbyte ELSE iword.oddbyte]};
ReadCodeWord: PUBLIC PROCEDURE [gf: GFHandle, offset: INTEGER] RETURNS [u: UNSPECIFIED] =
BEGIN OPEN SegmentDefs;
LocateCode[gf];
SELECT TRUE FROM
Init.bootLoaded => RETURN[GetInst[offset]];
gfCache.in OR gfCache.seg = NIL => {
lpc: LONG POINTER ← gfCache.p+offset;
RETURN[LongREAD[lpc]]};
gfCache.seg # NIL =>
BEGIN ENABLE InvalidFP => GOTO bad;
useg: FileSegmentHandle = ReadUserSegment[gfCache.seg];
WITH useg SELECT FROM
remote => ERROR RemoteSeg[gfCache.seg];
ENDCASE;
RETURN[GetInst[offset]];
EXITS bad => RETURN[0];
END;
Init.bootLoaded => RETURN[GetInst[offset]];
ENDCASE;
ERROR NoCode;
END;
WriteCodeByte: PUBLIC PROCEDURE [gf: GlobalFrameHandle, pc: BytePC, inst: BYTE] =
BEGIN
iword: ControlDefs.InstWord;
even: BOOLEAN;
pi: POINTER TO ControlDefs.InstWord;
co: CodeObject;
even ← pc MOD 2 = 0;
co ← Code[gf];
IF Init.bootLoaded THEN RETURN;
IF ~CodeOnDrum[co] THEN MoveToDrum[gf, co];
LocateCode[gf];
IF gfCache.in OR gfCache.seg = NIL THEN
BEGIN
iword ← LongREAD[gfCache.p+pc/2];
IF even THEN iword.evenbyte ← inst ELSE iword.oddbyte ← inst;
LongWRITE[gfCache.p+pc/2, iword];
END;
IF gfCache.seg # NIL THEN
BEGIN OPEN SegmentDefs;
useg: FileSegmentHandle = ReadUserSegment[gfCache.seg];
WITH useg SELECT FROM
remote => ERROR RemoteSeg[gfCache.seg];
ENDCASE;
gfCache.dseg.write ← TRUE;
SwapIn[gfCache.dseg];
pi ← FileSegmentAddress[gfCache.dseg]+gfCache.offset+pc/2;
IF even THEN pi.evenbyte ← inst ELSE pi.oddbyte ← inst;
Unlock[gfCache.dseg];
END;
RETURN
END;
COCacheObject: TYPE = RECORD [
gf: GlobalFrameHandle,
code: CodeObject];
coCache: COCacheObject ← [NIL,];
Code: PUBLIC PROCEDURE [f: GlobalFrameHandle] RETURNS [CodeObject] =
BEGIN OPEN DLoadState, coCache;
cgfi: MachineDefs.GFTIndex;
bcd: BcdOps.BcdBase;
FindModuleSeg: PROCEDURE [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS [BOOLEAN] =
BEGIN
IF cgfi IN[mth.gfi..mth.gfi+mth.ngfi) THEN {
IF Init.bootLoaded THEN FindSeg[bcd, mth.code, f];
code.seg ← mth.code.sgi; RETURN[TRUE]};
RETURN[FALSE];
END;
IF coCache.gf = f THEN RETURN[code];
[] ← Acquire[];
[cgfi,code.config] ← MapRealToConfig[GFI[Original[f]]];
IF code.config = NullConfig THEN ERROR Frames.Invalid[f];
bcd ← AcquireBcd[code.config];
[] ← BcdOps.ProcessModules[bcd, FindModuleSeg];
ReleaseBcd[bcd];
Release[];
coCache.gf ← f;
RETURN[code]
END;
FindSeg: PROC [bcd: BcdOps.BcdBase, code: BcdDefs.CodeDesc, gf: GlobalFrameHandle] =
BEGIN OPEN SegmentDefs;
sgb: BcdDefs.Base = LOOPHOLE[bcd+bcd.sgOffset];
sgh: BcdOps.SGHandle = @sgb[code.sgi];
f: BcdOps.FTHandle = @LOOPHOLE[bcd+bcd.ftOffset, BcdDefs.Base][sgh.file];
IF gfCache.gf = gf THEN RETURN;
FlushCodeCache[];
SELECT sgh.file FROM
BcdDefs.FTNull => ERROR NoCode;
BcdDefs.FTSelf =>
BEGIN
bcdseg: FileSegmentHandle ← LongVMtoFileSegment[bcd];
gfCache.dseg ← NewFileSegment[bcdseg.file, sgh.base, sgh.pages, Read];
END;
ENDCASE => gfCache.dseg ← NewFileSegment[OpenFile[bcd,f], sgh.base, sgh.pages, Read];
gfCache.gf ← gf; gfCache.seg ← NIL; gfCache.p ← NIL;
gfCache.in ← FALSE; gfCache.offset ← code.offset;
END;
OpenFile: PROC [bcd: BcdOps.BcdBase, f: BcdOps.FTHandle]
RETURNS [fh: SegmentDefs.FileHandle] = {
ssb: BcdOps.NameString = LOOPHOLE[bcd+bcd.ssOffset];
ss: Strings.SubStringDescriptor ← [@ssb.string, f.name, ssb.size[f.name]];
name: STRING ← [40];
Strings.AppendSubString[name, @ss];
CheckExtension[name];
fh ← DebugOps.CacheNewFile[name, SegmentDefs.DefaultAccess] };
CheckExtension: PROCEDURE [s: STRING] = {
FOR i: CARDINAL DECREASING IN [1..s.length) DO
IF s[i] = '. THEN RETURN;
ENDLOOP;
String.AppendString[s,".bcd"L]};
FrameCacheObject: TYPE = RECORD [
gf: GlobalFrameHandle,
seg: FileSegmentHandle,
p: LONG POINTER,
in: BOOLEAN,
offset: CARDINAL,
dseg: FileSegmentHandle];
gfCache: FrameCacheObject ← [NIL,,,,,];
FlushCodeCache: PROCEDURE =
BEGIN
IF gfCache.gf # NIL AND gfCache.dseg # NIL THEN
SegmentDefs.DeleteFileSegment[gfCache.dseg];
gfCache.gf ← NIL;
RETURN;
END;
FlushCodeSegmentCache: PUBLIC PROCEDURE =
BEGIN
FlushCodeCache[];
coCache.gf ← NIL;
RETURN;
END;
LN: TYPE = Inline.LongNumber;
File: PUBLIC PROCEDURE [f: GlobalFrameHandle] RETURNS [SegmentDefs.FileHandle] =
BEGIN
co: CodeObject ← Code[f];
di: Handle;
fp: AltoFileDefs.FP;
LocateCode[f];
IF gfCache.dseg = NIL THEN RETURN[NIL];
FOR di ← diHead, di.next UNTIL di = NIL DO
IF di.co = co THEN
BEGIN OPEN SegmentDefs;
IF di.oldFile = NIL THEN RETURN[NIL];
ShortCopyREAD[from: @di.oldFile.fp, to: @fp, nwords: SIZE[AltoFileDefs.FP]];
RETURN[InsertFile[@fp, Read]]
END;
ENDLOOP;
RETURN[gfCache.dseg.file]
END;
LocateCode: PROCEDURE [f: GlobalFrameHandle] =
BEGIN OPEN SegmentDefs, gfCache;
gf: GlobalFrame;
IF gfCache.gf = f THEN RETURN;
FlushCodeCache[];
Gf.Check[f];
IF Init.bootLoaded THEN {[] ← Code[f]; RETURN};
gfCache.gf ← f;
in ← TRUE;
p ← NIL;
seg ← NIL;
offset ← 0;
ShortCopyREAD[from: f, to: @gf, nwords: SIZE[GlobalFrame]];
IF gf.code.out THEN gf.code.out ← in ← FALSE;
IF gf.code.highByte # 0 THEN
BEGIN
seg ← gf.code.handle;
IF in THEN
BEGIN
LOOPHOLE[p, LN].lowbits ← LOOPHOLE[gf.code.shortbase];
offset ← LOOPHOLE[gf.code.shortbase, CARDINAL] -
LOOPHOLE[AddressFromPage[ReadUserSegment[seg].VMpage], CARDINAL];
END
ELSE offset ← gf.code.offset;
IF ReadUserSegment[seg].swappedin THEN {
in ← TRUE;
p ← offset + LongAddressFromPage[ReadUserSegment[seg].VMpage]};
END
ELSE
BEGIN
p ← gf.code.longbase;
IF gf.code.otherByte <= LAST[BankIndex] THEN
BEGIN
LREAD: PROCEDURE [page: CARDINAL, bank: CARDINAL] RETURNS [POINTER] =
MACHINE CODE BEGIN Mopcodes.zRBL, 0 END;
page: CARDINAL = PageFromAddress[gf.code.shortbase];
seg ← LREAD[page: page, bank: gf.code.otherByte];
in ← TRUE;
offset ← LOOPHOLE[gf.code.shortbase, CARDINAL] -
LOOPHOLE[AddressFromPage[ReadUserSegment[seg].VMpage], CARDINAL];
END;
END;
dseg ← IF seg # NIL THEN MapUSeg[seg] ELSE NIL;
END;
END.