-- File: Segs.mesa -- Edited by: Sandman, July 10, 1980 7:55 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [PageSize], InlineDefs USING [BITAND, DIVMOD], Mopcodes USING [zLI4, zRFS, zSHIFT, zWFS], SegmentDefs USING [ FileHandle, GetEndOfFile, GetFileAccess, PageCount, PageNumber], SegOps USING [DefaultBase, DefaultFile, DefaultPages, Seg, SegObject], Storage USING [Pages, FreePages], WartDefs USING [NullSeg]; Segs: PROGRAM IMPORTS InlineDefs, SegmentDefs, Storage EXPORTS SegOps SHARES SegOps = BEGIN OPEN AltoDefs, SegmentDefs, WartDefs, SegOps; vmFile: PUBLIC FileHandle ← NIL; TooManySegs: PUBLIC SIGNAL = CODE; VMBusy: PUBLIC SIGNAL [base: PageNumber, pages: PageCount] = CODE; InvalidSegSize: PUBLIC ERROR = CODE; NoVM: PUBLIC SIGNAL [needed: PageCount] = CODE; NewSeg: PUBLIC PROCEDURE [ file: FileHandle, base: PageNumber, pages: PageCount, write: BOOLEAN ← FALSE] RETURNS [s: Seg] = BEGIN IF file = DefaultFile THEN file ← vmFile; s ← GetSeg[]; BEGIN ENABLE UNWIND => ReturnSeg[s]; IF file = vmFile THEN BEGIN IF pages = DefaultPages OR pages <= 0 THEN ERROR InvalidSegSize; IF base = DefaultBase THEN base ← AllocVM[pages, -1] ELSE IF base IN [0..256) THEN BEGIN WHILE ~PagesFree[base, pages] DO SIGNAL VMBusy[base, pages]; ENDLOOP; ReserveVM[base, pages] END END ELSE BEGIN [] ← GetFileAccess[file]; IF base = DefaultBase THEN base ← 1; IF pages = DefaultPages THEN pages ← GetEndOfFile[file].page - base + 1; END; END; s↑ ← SegObject[ realFile: file, resident: TRUE, write: write, data: TRUE, in: FALSE, inUse: TRUE, base: base, pages: pages, vmPage: 0, lockCount: 0, trueFile: NIL, imageBase: 0, link: NIL, link2: NIL, index: WartDefs.NullSeg]; IF file # vmFile THEN file.segcount ← file.segcount + 1 ELSE BEGIN s.in ← TRUE; s.vmPage ← base; s.lockCount ← 1; END; RETURN END; -- Swapping Segments (but faking it) SwappingError: PUBLIC ERROR [s: Seg] = CODE; SwapInSeg: PUBLIC PROCEDURE [s: Seg] = BEGIN ValidateSeg[s]; IF ~s.in THEN BEGIN IF s.pages = 0 THEN ERROR SwappingError[s]; s.vmPage ← AllocVM[s.pages, 1]; s.in ← TRUE; END; s.lockCount ← s.lockCount + 1; RETURN END; UnlockSeg: PUBLIC PROCEDURE [s: Seg] = BEGIN ValidateSeg[s]; IF s.lockCount = 0 THEN ERROR SwappingError[s]; s.lockCount ← s.lockCount - 1; RETURN END; SwapOutSeg: PUBLIC PROCEDURE [s: Seg] = BEGIN OPEN InlineDefs; ValidateSeg[s]; IF ~s.in THEN RETURN; IF s.pages = 0 OR s.lockCount > 0 THEN ERROR SwappingError[s]; ReleaseVM[s.vmPage, s.pages]; s.in ← FALSE; s.vmPage ← 0; RETURN END; -- VM Suboutines PageState: TYPE = {free, busy}; PageID: TYPE = WORD; PageMapHandle: TYPE = POINTER TO PageMap; PageMap: TYPE = ARRAY [0..15] OF WORD; VMmap: PageMap ← ALL[0]; MakePageID: PROCEDURE [PageNumber] RETURNS [PageID] = MACHINE CODE BEGIN Mopcodes.zLI4; Mopcodes.zSHIFT END; GetPageState: PROCEDURE [page: PageNumber] RETURNS [PageState] = BEGIN OPEN InlineDefs; ReadPageState: PROCEDURE [PageMapHandle, PageID] RETURNS [PageState] = MACHINE CODE BEGIN Mopcodes.zRFS END; RETURN[ReadPageState[@VMmap, MakePageID[page]]]; END; SetPageState: PROCEDURE [page: PageNumber, state: PageState] = BEGIN OPEN InlineDefs; WritePageState: PROCEDURE [PageState, PageMapHandle, PageID] = MACHINE CODE BEGIN Mopcodes.zWFS END; WritePageState[state, @VMmap, MakePageID[page]]; RETURN; END; ffvmp, lfvmp: PUBLIC CARDINAL; minap, maxap: PageNumber; AllocVM: PROCEDURE [pages: PageCount, direction: INTEGER] RETURNS [PageNumber] = BEGIN n: INTEGER; pg, end, base: PageNumber; IF pages~ IN (0..256] THEN ERROR InvalidSegSize; DO -- repeat if insufficient VM IF direction > 0 THEN BEGIN direction ← 1; -- eliminate any prefix of allocated pages and update ffvmp FOR ffvmp IN [ffvmp..lfvmp] DO IF GetPageState[ffvmp] = free THEN EXIT; ENDLOOP; pg ← ffvmp; end ← lfvmp; END ELSE BEGIN direction ← -1; -- eliminate any suffix of allocated pages and update lfvmp FOR lfvmp DECREASING IN [ffvmp..lfvmp] DO IF GetPageState[lfvmp] = free THEN EXIT; ENDLOOP; pg ← lfvmp; end ← ffvmp; END; n ← 0; -- count of contiguous free pages FOR pg ← pg, pg + direction UNTIL (IF direction > 0 THEN pg > end ELSE pg < end) DO IF GetPageState[pg] # free THEN n ← 0 -- page in use; reset free count ELSE IF (n ← n + 1) = pages THEN BEGIN base ← IF direction > 0 THEN pg - n + 1 ELSE pg; ReserveVM[base, pages]; RETURN[base] END; ENDLOOP; SIGNAL NoVM[pages]; ENDLOOP END; ReleaseVM: PROCEDURE [base: CARDINAL, pages: CARDINAL] = BEGIN ffvmp ← MIN[ffvmp, base]; lfvmp ← MAX[lfvmp, base + pages - 1]; FOR base IN [base..base + pages) DO SetPageState[base, free]; ENDLOOP; RETURN END; ReserveVM: PROCEDURE [base: CARDINAL, pages: CARDINAL] = BEGIN IF ffvmp IN [base..base + pages) THEN ffvmp ← base + pages; IF lfvmp IN [base..base + pages) THEN lfvmp ← base - 1; FOR base IN [base..base + pages) DO SetPageState[base, busy]; ENDLOOP; RETURN END; SetVMBounds: PROCEDURE [fp, lp: PageNumber] = BEGIN minap ← ffvmp ← fp; maxap ← lfvmp ← lp; RETURN END; PagesFree: PROCEDURE [base: PageNumber, pages: PageCount] RETURNS [BOOLEAN] = BEGIN FOR base IN [base..base + pages) DO IF GetPageState[base] # free THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE] END; -- Segment Sub-Routines freeSegs: Seg ← NIL; tables: SegTableHandle ← NIL; SegsPerTable: CARDINAL = (AltoDefs.PageSize - 1)/SIZE[SegObject]; SegTable: TYPE = RECORD [ link: SegTableHandle, segs: ARRAY [0..SegsPerTable) OF SegObject]; SegTableHandle: TYPE = POINTER TO SegTable; InvalidSeg: PUBLIC ERROR [POINTER] = CODE; ValidateSeg: PUBLIC PROCEDURE [s: Seg] = BEGIN OPEN InlineDefs; table: SegTableHandle ← BITAND[s, 177400B]; i, j: CARDINAL; t: SegTableHandle; FOR t ← tables, t.link UNTIL t = NIL DO IF t = table THEN EXIT; REPEAT FINISHED => ERROR InvalidSeg[s]; ENDLOOP; [i, j] ← DIVMOD[s - @table.segs[0], SIZE[SegObject]]; IF j # 0 OR ~s.inUse THEN ERROR InvalidSeg[s]; RETURN END; InitSegMachinery: PUBLIC PROCEDURE [firstpage, lastpage: PageNumber] = BEGIN t: SegTableHandle; SetVMBounds[firstpage, lastpage]; FOR t ← tables, tables UNTIL t = NIL DO tables ← t.link; Storage.FreePages[t]; ENDLOOP; freeSegs ← NIL; RETURN END; GetNewTable: PROCEDURE = BEGIN t: SegTableHandle ← Storage.Pages[1]; tt: SegTableHandle; i: CARDINAL; t.link ← NIL; FOR i DECREASING IN [0..SegsPerTable) DO ReturnSeg[@t.segs[i]]; ENDLOOP; IF tables = NIL THEN BEGIN tables ← t; RETURN END; FOR tt ← tables, tt.link UNTIL tt.link = NIL DO NULL ENDLOOP; tt.link ← t; RETURN END; GetSeg: PROCEDURE RETURNS [s: Seg] = BEGIN IF freeSegs = NIL THEN GetNewTable[]; s ← freeSegs; freeSegs ← s.link; s.inUse ← TRUE; RETURN END; ReturnSeg: PROCEDURE [s: Seg] = BEGIN s.inUse ← FALSE; s.link ← freeSegs; freeSegs ← s; RETURN END; EnumerateSegs: PUBLIC PROCEDURE [proc: PROCEDURE [Seg] RETURNS [BOOLEAN]] RETURNS [seg: Seg] = BEGIN i: CARDINAL; t: SegTableHandle; FOR t ← tables, t.link UNTIL t = NIL DO FOR i IN [0..SegsPerTable) DO seg ← @t.segs[i]; IF seg.inUse AND proc[seg] THEN RETURN[seg]; ENDLOOP; ENDLOOP; RETURN[NIL] END; END...