-- UserSegments.Mesa -- Edited by: -- Sandman on May 5, 1980 6:06 PM -- Johnsson on August 30, 1978 12:02 PM -- Barbara on April 6, 1979 12:22 PM -- Bruce on July 2, 1980 5:39 PM DIRECTORY AltoDefs USING [BYTE, BytesPerPage, PageNumber], AltoFileDefs USING [eofDA, FP], BcdDefs USING [GFTIndex, MTIndex], BcdOps USING [BcdBase, MTHandle, ProcessModules], ControlDefs USING [BYTE, BytePC, FrameHandle, GFTIndex, GlobalFrame, GlobalFrameHandle, InstWord], DebugFormat USING [CodeObject], DebugOps USING [LongREAD, LongWRITE, ShortCopyREAD, ShortCopyWRITE], Drum USING [DrumItem, Handle], Frames USING [Invalid], Gf USING [Check, GFI, Handle, Original], InlineDefs USING [LongNumber], LoadStateOps USING [AcquireBcd, InputLoadState, MapRealToConfig, NullConfig, ReleaseBcd, ReleaseLoadState], Mopcodes USING [zRBL], SegmentDefs USING [AddressFromPage, BankIndex, DeleteFileSegment, FileHandle, FileHint, FileObject, FileSegmentAddress, FileSegmentHandle, FileSegmentObject, InsertFile, InvalidFP, NewFileSegment, PageFromAddress, PageNumber, Read, ReleaseFile, SegmentFault, SetEndOfFile, SwapIn, SwapOut, Unlock], State USING [GetGS, GSHandle], Storage USING [Free, Node]; UserSegments: PROGRAM IMPORTS BcdOps, DebugOps, Frames, Gf, LoadStateOps, SegmentDefs, State, Storage EXPORTS DebugOps, Drum, Gf = BEGIN OPEN DebugOps, DebugFormat, Gf, Drum; BytePC: TYPE = ControlDefs.BytePC; BYTE: TYPE = AltoDefs.BYTE; FrameHandle: TYPE = ControlDefs.FrameHandle; GlobalFrameHandle: TYPE = ControlDefs.GlobalFrameHandle; FileHandle: TYPE = SegmentDefs.FileHandle; FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle; InstWord: TYPE = ControlDefs.InstWord; data: State.GSHandle ← State.GetGS[]; -- Utilities UserSegment: SegmentDefs.FileSegmentObject; UserFileSegmentAddress: PROCEDURE[useg: FileSegmentHandle] RETURNS[POINTER] = BEGIN RETURN[SegmentDefs.AddressFromPage[ReadUserSegment[useg].VMpage]] END; 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: 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; ReadCodeByte: PUBLIC PROCEDURE [gf: GlobalFrameHandle, pc: BytePC] RETURNS [BYTE] = BEGIN OPEN SegmentDefs; iword: InstWord; lpc: LONG POINTER; patched: BOOLEAN ← FALSE; LocateCode[gf]; lpc ← gfCache.p+pc/2; IF (gfCache.in OR gfCache.seg = NIL) AND ~patched THEN iword ← LongREAD[lpc] ELSE IF gfCache.seg # NIL THEN BEGIN ENABLE InvalidFP => BEGIN iword ← LOOPHOLE[-1]; CONTINUE END; useg: FileSegmentHandle = ReadUserSegment[gfCache.seg]; WITH useg SELECT FROM remote => ERROR RemoteSeg[gfCache.seg]; ENDCASE; SwapIn[gfCache.dseg]; iword ← (FileSegmentAddress[gfCache.dseg]+gfCache.offset+pc/2)↑; Unlock[gfCache.dseg]; END; RETURN[IF pc MOD 2 = 0 THEN iword.evenbyte ELSE iword.oddbyte] END; ReadCodeWord: PUBLIC PROCEDURE [gf: GlobalFrameHandle, offset: INTEGER] RETURNS [iword: UNSPECIFIED] = BEGIN OPEN SegmentDefs; lpc: LONG POINTER; LocateCode[gf]; lpc ← gfCache.p+offset; IF (gfCache.in OR gfCache.seg = NIL) THEN iword ← LongREAD[lpc] ELSE IF gfCache.seg # NIL THEN BEGIN ENABLE InvalidFP => BEGIN iword ← 0; CONTINUE END; useg: FileSegmentHandle = ReadUserSegment[gfCache.seg]; WITH useg SELECT FROM remote => ERROR RemoteSeg[gfCache.seg]; ENDCASE; SwapIn[gfCache.dseg]; iword ← (FileSegmentAddress[gfCache.dseg]+gfCache.offset+offset)↑; Unlock[gfCache.dseg]; END; RETURN END; WriteCodeByte: PUBLIC PROCEDURE [gf: GlobalFrameHandle, pc: BytePC, inst: BYTE] = BEGIN iword: InstWord; even: BOOLEAN; pi: POINTER TO InstWord; co: CodeObject; even ← pc MOD 2 = 0; co ← Code[gf]; 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 LoadStateOps, coCache; cgfi: ControlDefs.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 BEGIN code.seg ← mth.code.sgi; RETURN[TRUE]; END; RETURN[FALSE]; END; IF coCache.gf = f THEN RETURN[code]; [] ← InputLoadState[]; [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]; ReleaseLoadState[]; coCache.gf ← f; RETURN[code] END; 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; -- copied from GlobalFrameDefs.mesa GlobalFrame: TYPE = MACHINE DEPENDENT RECORD [ gfi: [0..777B], unused: [0..1], -- reserved for future gfi expansion copied, alloced, shared, started: BOOLEAN, trapxfers, codelinks: BOOLEAN, code: FrameCodeBase, global: ARRAY [0..0) OF UNSPECIFIED]; LN: TYPE = InlineDefs.LongNumber; FrameCodeBase: TYPE = MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM in => [ SELECT OVERLAID * FROM codebase => [ codebase: LONG POINTER], shortCodebase => [ shortCodebase: UNSPECIFIED, highHalf: CARDINAL], ENDCASE], out => [ offset: CARDINAL, handle: POINTER], either => [ fill1: [0..77777B], swappedout: BOOLEAN, highByte, topByteOfLongPointer: [0..377B]], ENDCASE]; File: PUBLIC PROCEDURE [f: GlobalFrameHandle] RETURNS [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]; gfCache.gf ← f; in ← TRUE; p ← NIL; seg ← NIL; offset ← 0; ShortCopyREAD[from: f, to: @gf, nwords: SIZE[GlobalFrame]]; IF gf.code.swappedout THEN gf.code.swappedout ← in ← FALSE; IF gf.code.highByte # 0 THEN BEGIN seg ← gf.code.handle; IF in THEN BEGIN LOOPHOLE[p, LN].lowbits ← gf.code.shortCodebase; offset ← gf.code.shortCodebase - LOOPHOLE[AddressFromPage[ReadUserSegment[seg].VMpage], CARDINAL]; END ELSE offset ← gf.code.offset; IF ReadUserSegment[seg].swappedin THEN BEGIN in ← TRUE; LOOPHOLE[p, LN].lowbits ← offset + LOOPHOLE[AddressFromPage[ReadUserSegment[seg].VMpage], CARDINAL]; END; END ELSE BEGIN p ← gf.code.codebase; IF gf.code.topByteOfLongPointer <= LAST[BankIndex] THEN BEGIN LREAD: PROCEDURE [page: CARDINAL, bank: CARDINAL] RETURNS [POINTER] = MACHINE CODE BEGIN Mopcodes.zRBL, 0 END; page: CARDINAL = PageFromAddress[gf.code.shortCodebase]; seg ← LREAD[page: page, bank: gf.code.topByteOfLongPointer]; in ← TRUE; offset ← gf.code.shortCodebase - LOOPHOLE[AddressFromPage[ReadUserSegment[seg].VMpage], CARDINAL]; END; END; dseg ← IF seg # NIL THEN MapUSeg[seg] ELSE NIL; END; END.