-- Swapper.Mesa Edited by Sandman on Mar 15, 1979 12:06 PM -- Edited for XMesa by Levin on March 19, 1979 9:29 AM DIRECTORY AllocDefs: FROM "allocdefs" USING [ AllocHandle, AllocInfo, AllocObject, DefaultDataSegmentInfo, DefaultFileSegmentInfo, DefaultFrameSegmentInfo, DefaultTableSegmentInfo, PageState, SwappingProcedure, SwapStrategy], AltoDefs: FROM "altodefs" USING [ BYTE, MaxVMPage, PageCount, PageNumber, PageSize], AltoFileDefs: FROM "altofiledefs" USING [eofDA, vDC], BootDefs: FROM "bootdefs" USING [ BusyPage, FreePage, PageMap, PositionSeg, SystemTable, SystemTableHandle, Table, TableHandle], ControlDefs: FROM "controldefs" USING [ GFT, GFTIndex, GlobalFrameHandle, NullGlobalFrame, PrefixHandle, PrefixHeader], DiskDefs: FROM "diskdefs" USING [DiskRequest, SwapPages], FrameDefs: FROM "framedefs" USING [ValidateGlobalFrame], FrameOps: FROM "frameops" USING [CodeHandle, FlushLargeFrames], InlineDefs: FROM "inlinedefs" USING [BITAND, DIVMOD, LongNumber], LongCodeOps: FROM "longcodeops", MemoryOps: FROM "memoryops" USING [BankIndex, GetMemoryConfig, MemoryConfig], Mopcodes: FROM "mopcodes" USING [zBLTL, zPUSH, zRBL, zWBL], NucleusDefs: FROM "nucleusdefs" USING [Miscellaneous], ProcessDefs: FROM "processdefs" USING [DisableInterrupts, EnableInterrupts], SDDefs: FROM "sddefs" USING [SD, sGFTLength], SegmentDefs: FROM "segmentdefs" USING [ ChangeDataToFileSegment, DataSegmentHandle, DefaultBase, DeleteFileSegment, FileHint, FileSegmentHandle, FrobHandle, FrobLink, FrobNull, InvalidSegmentSize, MaxLocks, MaxRefs, NewFileSegment, Object, ObjectHandle, ObjectType, OpenFile, PageNumber, PagePointer, Read, RemoteSegCommand, SegmentHandle, Write], SystemDefs: FROM "systemdefs" USING [FreePages], XMESA: FROM "xmesaops" USING [ Bank1X, BankMasks, BankOption, FreeSeal, InUseSeal, PageMapAllocInfo, XDataSegmentHandle, XFileSegmentHandle, XFileSegmentObject, XMremote, XSegInfo, XSegInfoIndex, XSegInfoTable, XSegmentHandle], XMesaDefs: FROM "xmesadefs" USING [ DefaultBase0, DefaultBase3, DefaultMDSBase, DefaultXMBase, MaxXPage, LongAddressFromPage, PagesPerBank, XCOPY, XDataSegmentAddress, XFileSegmentAddress]; Swapper: PROGRAM [ffvmp, lfvmp: AltoDefs.PageNumber] IMPORTS BootDefs, DiskDefs, FrameDefs, FrameOps, InlineDefs, MemoryOps, NucleusDefs, ProcessDefs, SegmentDefs, SystemDefs, XMesaDefs EXPORTS AllocDefs, BootDefs, FrameDefs, LongCodeOps, MemoryOps, NucleusDefs, SegmentDefs, XMESA, XMesaDefs SHARES MemoryOps, SegmentDefs, XMESA = BEGIN OPEN SegmentDefs; AllocInfo: TYPE = AllocDefs.AllocInfo; AllocHandle: TYPE = AllocDefs.AllocHandle; PageState: TYPE = AllocDefs.PageState; SwappingProcedure: TYPE = AllocDefs.SwappingProcedure; SwapStrategy: TYPE = AllocDefs.SwapStrategy; PageCount: TYPE = AltoDefs.PageCount; PageNumber: TYPE = AltoDefs.PageNumber; TableHandle: TYPE = BootDefs.TableHandle; SegmentHandle: TYPE = SegmentDefs.SegmentHandle; DataSegmentHandle: TYPE = SegmentDefs.DataSegmentHandle; FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle; MaxVMPage: PageNumber = AltoDefs.MaxVMPage; PageSize: CARDINAL = AltoDefs.PageSize; PAGEDISP: TYPE = MACHINE DEPENDENT RECORD [ page: [0..MaxVMPage], disp: [0..PageSize)]; PageFromAddress: PUBLIC PROCEDURE [a:POINTER] RETURNS [PageNumber] = BEGIN RETURN[LOOPHOLE[a,PAGEDISP].page] END; AddressFromPage: PUBLIC PROCEDURE [p:PageNumber] RETURNS [POINTER] = BEGIN RETURN[LOOPHOLE[PAGEDISP[p,0]]] END; PagePointer: PUBLIC PROCEDURE [a:POINTER] RETURNS [POINTER] = BEGIN LOOPHOLE[a,PAGEDISP].disp ← 0; RETURN[a] END; -- Data Segments DefaultBase: PageNumber = SegmentDefs.DefaultBase; NewDataSegment: PUBLIC PROCEDURE [base:PageNumber, pages:PageCount] RETURNS [seg:DataSegmentHandle] = BEGIN RETURN[MakeDataSegment[base, pages, AllocDefs.DefaultDataSegmentInfo]] END; NewFrameSegment: PUBLIC PROCEDURE [pages:PageCount] RETURNS [seg:DataSegmentHandle] = BEGIN RETURN[MakeDataSegment[DefaultBase, pages, AllocDefs.DefaultFrameSegmentInfo]] END; MakeDataSegment: PUBLIC PROCEDURE [ base: PageNumber, pages: PageCount, info: AllocInfo] RETURNS [seg: DataSegmentHandle] = BEGIN IF pages ~IN (0..MaxVMPage+1] THEN ERROR InvalidSegmentSize[pages]; seg ← AllocateDataSegment[]; seg↑ ← [busy:, body: segment[data[VMpage:, pages:, unused:]]]; base ← alloc.alloc[base, pages, seg, info ! UNWIND => LiberateDataSegment[seg]]; seg↑ ← [busy: FALSE, body: segment[data[VMpage: base, pages: pages, unused: 0]]]; IF base > MaxVMPage THEN --XM BEGIN --XM OPEN s: LOOPHOLE[seg, XMESA.XDataSegmentHandle]; --XM s.VMpage ← 0; s.XMpage ← base; --XM END; --XM alloc.update[base, pages, inuse, seg]; RETURN END; BootDataSegment: PUBLIC PROCEDURE [base:PageNumber, pages:PageCount] RETURNS [seg:DataSegmentHandle] = BEGIN OPEN AllocDefs; i: PageNumber; FOR i IN [base..base+pages) DO IF alloc.status[i].status # busy THEN ERROR; ENDLOOP; seg ← AllocateDataSegment[]; seg↑ ← [busy: FALSE, body: segment[data[VMpage: base, pages: pages, unused: 0]]]; alloc.update[base, pages, inuse, seg]; RETURN END; DeleteDataSegment: PUBLIC PROCEDURE [seg:DataSegmentHandle] = BEGIN base: PageNumber; pages: PageCount; ValidateDataSegment[seg]; BEGIN OPEN s: LOOPHOLE[seg, XMESA.XDataSegmentHandle]; --XM base ← IF s.VMpage = 0 THEN s.XMpage ELSE s.VMpage; --XM END; --XM pages ← seg.pages; --XM alloc.update[base, pages, busy, NIL]; LiberateDataSegment[seg]; alloc.update[base, pages, free, NIL]; RETURN END; DataSegmentAddress: PUBLIC PROCEDURE [seg:DataSegmentHandle] RETURNS [POINTER] = BEGIN IF seg.VMpage = 0 THEN ERROR; --XM RETURN[LOOPHOLE[PAGEDISP[seg.VMpage,0]]] END; -- Swapping Segments SwapError: PUBLIC SIGNAL [seg:FileSegmentHandle] = CODE; MakeSwappedIn: PUBLIC PROCEDURE [ seg: FileSegmentHandle, base: PageNumber, info: AllocInfo] = BEGIN vmpage: PageNumber; IF seg.lock = MaxLocks THEN ERROR SwapError[seg]; ValidateObject[seg]; ProcessDefs.DisableInterrupts[]; IF seg.swappedin THEN --XM BEGIN seg.lock ← seg.lock+1; ProcessDefs.EnableInterrupts[]; RETURN END; --XM ProcessDefs.EnableInterrupts[]; IF seg.file.swapcount = MaxRefs THEN SIGNAL SwapError[seg]; IF ~seg.file.open THEN OpenFile[seg.file]; vmpage ← alloc.alloc[base, seg.pages, seg, info]; -- There is a funny side-effect of AllocVM that we have to worry about. In the course -- of allocating space for the segment, it is possible that segments have been swapped in -- (in order to call swapping procedures or execute instructions implemented in software). -- In particular, it is possible that 'seg' itself has been swapped in this way. The exits -- in the following block of code deal with this possibility. BEGIN -- block for funny cases -- --XM ENABLE UNWIND => alloc.update[vmpage, seg.pages, free, NIL]; ProcessDefs.DisableInterrupts[]; IF seg.swappedin THEN --XM BEGIN seg.lock ← seg.lock+1; ProcessDefs.EnableInterrupts[]; GO TO Surprise END; --XM ProcessDefs.EnableInterrupts[]; --XM WITH s: seg SELECT FROM disk => IF vmpage > MaxVMPage THEN --XM BEGIN --XM MakeSwappedIn[@s, XMesaDefs.DefaultMDSBase, AllocDefs.DefaultFileSegmentInfo]; --XM IF s.loc ~= disk THEN GO TO Surprise; -- already in an XM bank -- --XM XMesaDefs.XCOPY[from: XMesaDefs.LongAddressFromPage[s.VMpage], --XM to: XMesaDefs.LongAddressFromPage[vmpage], --XM nwords: s.pages*PageSize]; --XM alloc.update[VanillaToChocolate[seg, vmpage], s.pages, free, NIL]; --XM ProcessDefs.DisableInterrupts[]; --XM END --XM ELSE BEGIN seg.VMpage ← vmpage; IF (s.hint.page # s.base OR s.hint.da = AltoFileDefs.eofDA) AND BootDefs.PositionSeg[@s,TRUE] AND s.pages = 1 THEN NULL ELSE MapVM[@s, ReadD]; GO TO BumpCounts --XM END; remote => IF s.proc = XMESA.XMremote THEN ERROR --XM ELSE BEGIN s.proc[@s, remoteRead]; GO TO BumpCounts END; --XM ENDCASE; EXITS BumpCounts => -- normal termination of non-chocolate segment read-in -- --XM BEGIN --XM ProcessDefs.DisableInterrupts[]; --XM seg.file.swapcount ← seg.file.swapcount+1; --XM seg.lock ← seg.lock+1; --XM seg.swappedin ← TRUE; --XM END; --XM Surprise => BEGIN alloc.update[vmpage, seg.pages, free, NIL]; RETURN END; --XM END; -- block for funny cases -- --XM alloc.update[vmpage, seg.pages, inuse, seg]; ProcessDefs.EnableInterrupts[]; RETURN END; SwapIn: PUBLIC PROCEDURE [seg:FileSegmentHandle] = BEGIN MakeSwappedIn[seg, DefaultBase, AllocDefs.DefaultFileSegmentInfo]; RETURN END; Unlock: PUBLIC PROCEDURE [seg:FileSegmentHandle] = BEGIN OPEN seg; IF lock = 0 THEN ERROR SwapError[seg]; ValidateObject[seg]; ProcessDefs.DisableInterrupts[]; lock ← lock-1; ProcessDefs.EnableInterrupts[]; RETURN END; SwapUp: PUBLIC PROCEDURE [seg:FileSegmentHandle] = BEGIN OPEN seg; ValidateObject[seg]; IF swappedin AND write THEN BEGIN WITH s: seg SELECT FROM disk => BEGIN IF s.hint.page # base OR s.hint.da = AltoFileDefs.eofDA THEN [] ← BootDefs.PositionSeg[@s,FALSE]; MapVM[@s, WriteD]; END; remote => IF s.proc = XMESA.XMremote THEN --XM BEGIN OPEN xs: LOOPHOLE[seg, POINTER TO remote XMESA.XFileSegmentObject]; --XM mdsFileSeg: FileSegmentHandle ← NewFileSegment[xs.file, xs.base, xs.pages, Read+Write]; mdsDataSeg: DataSegmentHandle ← NewDataSegment[XMesaDefs.DefaultMDSBase, xs.pages]; --XM XMesaDefs.XCOPY[from: XMesaDefs.XFileSegmentAddress[seg], --XM to: XMesaDefs.XDataSegmentAddress[mdsDataSeg], --XM nwords: xs.pages*PageSize]; --XM ChangeDataToFileSegment[mdsDataSeg, mdsFileSeg]; --XM Unlock[mdsFileSeg]; --XM DeleteFileSegment[mdsFileSeg]; --XM END --XM ELSE s.proc[@s, remoteWrite]; ENDCASE; END; RETURN END; SwapOut: PUBLIC PROCEDURE [seg:FileSegmentHandle] = BEGIN OPEN seg; temp: PageNumber; -- ValidateObject[seg]; -- --XM SwapUp[seg]; ProcessDefs.DisableInterrupts[]; IF ~swappedin THEN BEGIN ProcessDefs.EnableInterrupts[]; RETURN END; IF lock # 0 THEN BEGIN ProcessDefs.EnableInterrupts[]; ERROR SwapError[seg] END; busy ← TRUE; WITH s: seg SELECT FROM disk => BEGIN temp ← s.VMpage; s.VMpage ← 0; END; remote => IF s.proc = XMESA.XMremote THEN temp ← ChocolateToVanilla[LOOPHOLE[seg],0] --XM ELSE BEGIN temp ← s.VMpage; s.VMpage ← 0; END; --XM ENDCASE; --XM alloc.update[temp, pages, busy, NIL]; --XM swappedin ← FALSE; busy ← FALSE; file.swapcount ← file.swapcount-1; ProcessDefs.EnableInterrupts[]; -- IF swapcount = 0 THEN CloseFile[]; ??? alloc.update[temp, pages, free, NIL]; RETURN END; remoteRead: RemoteSegCommand = 0; remoteWrite: RemoteSegCommand = 1; SegmentFault: PUBLIC SIGNAL [seg:FileSegmentHandle, pages:PageCount] = CODE; MapVM: PUBLIC PROCEDURE [seg:FileSegmentHandle, dc: AltoFileDefs.vDC] = BEGIN OPEN seg; page: PageNumber; byte: CARDINAL; temp: PageCount; arg: swap DiskDefs.DiskRequest; WITH s: seg SELECT FROM disk => BEGIN arg ← DiskDefs.DiskRequest[AddressFromPage[s.VMpage], @s.hint.da, s.base, s.base+s.pages-1, @s.file.fp, FALSE, dc, dc, FALSE, swap[NIL]]; IF s.hint.page # s.base THEN ERROR SwapError[@s]; [page,byte] ← DiskDefs.SwapPages[@arg]; temp ← page-base+(IF byte=0 THEN 0 ELSE 1); IF temp=0 THEN ERROR SegmentFault[@s,0]; IF temp # pages THEN BEGIN SIGNAL SegmentFault[@s,temp]; alloc.update[s.VMpage+temp, s.pages-temp, free, NIL]; s.pages ← temp; END; END; remote => ERROR SwapError[@s]; ENDCASE; RETURN END; -- Kludge to extend segment objects; will go away in XMesa 6.0 -- --XM VanillaToChocolate: PUBLIC PROCEDURE [seg: FileSegmentHandle, newpage: PageNumber] RETURNS [oldpage: PageNumber] = BEGIN xs: POINTER TO XMESA.XSegInfo ← AllocXSegInfo[]; oldpage ← seg.VMpage; xs.XMpage ← seg.VMpage ← newpage; -- reset seg.VMpage for CopyNew -- WITH s: seg SELECT FROM disk => xs.body ← hint[s.hint]; ENDCASE => ERROR; seg.location ← remote[XMESA.XMremote, xs]; -- note variant changes here!!!-- END; ChocolateToVanilla: PUBLIC PROCEDURE [seg: XMESA.XFileSegmentHandle, newpage: PageNumber] RETURNS [oldpage: PageNumber] = BEGIN WITH s: seg SELECT FROM remote => IF s.proc = XMESA.XMremote THEN BEGIN fh: FileHint ← s.info.hint; oldpage ← s.info.XMpage; FreeXSegInfo[s.info]; seg.location ← disk[fh]; -- note that variant changes here!!!-- seg.VMpage ← newpage; END ELSE ERROR; ENDCASE => ERROR; END; XSIhead: POINTER TO XMESA.XSegInfoTable ← NIL; AllocXSegInfo: PROCEDURE RETURNS [xs: POINTER TO XMESA.XSegInfo] = BEGIN OPEN XMESA; i: XSegInfoIndex; p: POINTER TO XSegInfoTable; ProcessDefs.DisableInterrupts[]; FOR p ← XSIhead, AddressFromPage[p.nextPage] UNTIL p = NIL DO IF p.freeCount # 0 THEN BEGIN xs ← p.freeHead; IF xs.seal # FreeSeal THEN ERROR; EXIT END; REPEAT FINISHED => BEGIN p ← DataSegmentAddress[NewDataSegment[DefaultBase,1]]; p.nextPage ← PageFromAddress[XSIhead]; XSIhead ← p; p.freeCount ← LAST[XSegInfoIndex]+1; FOR i IN XSegInfoIndex DO p.table[i].link ← @p.table[i+1]; p.table[i].seal ← FreeSeal; ENDLOOP; xs ← @p.table[0]; END; ENDLOOP; p.freeHead ← xs.link; p.freeCount ← p.freeCount-1; ProcessDefs.EnableInterrupts[]; xs.seal ← InUseSeal; END; FreeXSegInfo: PROCEDURE [xs: POINTER TO XMESA.XSegInfo] = BEGIN OPEN XMESA; p: POINTER TO XSegInfoTable ← PagePointer[xs]; IF xs.seal # InUseSeal THEN ERROR; xs.seal ← FreeSeal; ProcessDefs.DisableInterrupts[]; xs.link ← p.freeHead; p.freeHead ← xs; p.freeCount ← p.freeCount+1; IF p.freeCount = LAST[XSegInfoIndex]+1 THEN BEGIN prev: POINTER TO XSegInfoTable ← XSIhead; IF prev = p THEN XSIhead ← AddressFromPage[p.nextPage] ELSE BEGIN UNTIL prev = NIL DO IF prev.nextPage = PageFromAddress[p] THEN EXIT; prev ← AddressFromPage[prev.nextPage]; REPEAT FINISHED => ERROR; ENDLOOP; prev.nextPage ← p.nextPage; END; SystemDefs.FreePages[p]; END; ProcessDefs.EnableInterrupts[]; END; -- Code Swapping and Swap Strategies trySwapInProgress: BOOLEAN ← FALSE; TrySwapping: SwappingProcedure = BEGIN did: BOOLEAN; sp, next: POINTER TO SwapStrategy; ProcessDefs.DisableInterrupts[]; IF trySwapInProgress THEN BEGIN ProcessDefs.EnableInterrupts[]; RETURN[TryCodeSwapping[needed, info, seg]]; END; trySwapInProgress ← TRUE; ProcessDefs.EnableInterrupts[]; did ← TRUE; FOR sp ← StrategyList, next UNTIL sp = NIL DO next ← sp.link; IF sp.proc[needed, info, seg] THEN EXIT; REPEAT FINISHED => did ← FALSE; ENDLOOP; trySwapInProgress ← FALSE; RETURN[did] END; CantSwap: PUBLIC SwappingProcedure = BEGIN RETURN[FALSE] END; -- These three variables are set by XAllocVM -- --XM pageRover: AltoDefs.PageNumber ← 0; roverMax: AltoDefs.PageNumber ← AltoDefs.MaxVMPage; --XM roverMin: AltoDefs.PageNumber ← 0; --XM TryCodeSwapping: PUBLIC SwappingProcedure = BEGIN OPEN ControlDefs; foundHole: BOOLEAN ← FALSE; pass: {first, second, quit} ← first; okay: BOOLEAN; base, page: AltoDefs.PageNumber; segment: XMESA.XSegmentHandle; --XM status: AllocDefs.PageState; p: PrefixHandle; n, inc: PageCount; page ← n ← 0; ProcessDefs.DisableInterrupts[]; segment ← LOOPHOLE[alloc.status[pageRover].seg]; --XM IF segment # NIL THEN WITH s: segment SELECT FROM data => pageRover ← IF s.VMpage = 0 THEN s.XMpage ELSE s.VMpage; --XM file => --XM WITH fs: s SELECT FROM --XM disk => pageRover ← s.VMpage; --XM remote => pageRover ← --XM IF fs.proc = XMESA.XMremote THEN fs.info.XMpage ELSE fs.VMpage; --XM ENDCASE; --XM ENDCASE; base ← pageRover; DO -- until we've looked at them all twice [segment, status] ← LOOPHOLE[alloc.status[pageRover], --XM RECORD[XMESA.XSegmentHandle, AllocDefs.PageState]]; --XM okay ← FALSE; SELECT status FROM inuse => WITH s: segment SELECT FROM data => inc ← s.pages; file => BEGIN IF s.lock = 0 AND ~s.write THEN BEGIN IF s.class = code THEN BEGIN longP: LONG POINTER TO PrefixHeader; --XM info: CARDINAL; --XM ReadLRUInfo: PROCEDURE[LONG POINTER TO PrefixHeader] RETURNS[CARDINAL] = --XM MACHINE CODE BEGIN Mopcodes.zRBL, 0; END; --XM WriteLRUInfo: PROCEDURE[CARDINAL, LONG POINTER TO PrefixHeader] = --XM MACHINE CODE BEGIN Mopcodes.zWBL, 0; END; --XM WITH fs: s SELECT FROM --XM remote => --XM IF fs.proc = XMESA.XMremote THEN --XM BEGIN --XM longP ← XMesaDefs.LongAddressFromPage[fs.info.XMpage]; --XM info ← ReadLRUInfo[longP]; --XM IF info > 1 THEN longP ← longP + info; --XM IF info = 0 THEN okay ← TRUE ELSE WriteLRUInfo[0, longP]; --XM GO TO XMCodeProcessed --XM END; --XM ENDCASE; --XM p ← AddressFromPage[s.VMpage]; IF p.header.swapinfo > 1 THEN p ← p + p.header.swapinfo; IF p.header.swapinfo = 0 THEN okay ← TRUE ELSE p.header.swapinfo ← 0; EXITS --XM XMCodeProcessed => NULL; --XM END ELSE IF s.inuse THEN s.inuse ← FALSE ELSE okay ← TRUE; END; inc ← s.pages; END; ENDCASE; ENDCASE => BEGIN IF status = free THEN okay ← TRUE; inc ← 1; END; IF ~okay THEN BEGIN page ← n ← 0; IF pass = quit THEN EXIT; END ELSE BEGIN IF page = 0 THEN page ← pageRover; IF (n ← n+inc) >= needed THEN BEGIN foundHole ← TRUE; EXIT END; END; IF (pageRover ← pageRover+inc) > roverMax THEN --XM IF pass = quit THEN EXIT ELSE BEGIN pageRover ← page ← roverMin; n ← 0; END; --XM IF pageRover = base THEN pass ← IF pass = first THEN second ELSE quit; ENDLOOP; base ← pageRover ← page + n; WHILE page < base DO segment ← LOOPHOLE[alloc.status[page].seg]; --XM IF segment # NIL THEN WITH s: segment SELECT FROM data => page ← (IF s.VMpage = 0 THEN s.XMpage ELSE s.VMpage) + s.pages; --XM file => BEGIN WITH fs: s SELECT FROM --XM disk => page ← s.VMpage; --XM remote => page ← IF fs.proc = XMESA.XMremote THEN fs.info.XMpage ELSE fs.VMpage; --XM ENDCASE; --XM IF s.lock = 0 THEN BEGIN IF s.class = code THEN UpdateCodebases[@s]; IF ~s.write THEN SwapOutUnlocked[@s]; alloc.update[page, s.pages, free, NIL]; END; page ← page + s.pages; END; ENDCASE ELSE page ← page + 1; ENDLOOP; ProcessDefs.EnableInterrupts[]; RETURN[foundHole] END; SwapOutCode: PUBLIC PROCEDURE [f: ControlDefs.GlobalFrameHandle] = BEGIN OPEN SegmentDefs, ControlDefs; cseg: SegmentDefs.FileSegmentHandle = FrameOps.CodeHandle[f]; FrameDefs.ValidateGlobalFrame[f]; IF cseg = NIL THEN RETURN; ProcessDefs.DisableInterrupts[]; IF cseg.swappedin THEN BEGIN SwapIn[cseg]; -- lock it so it won't go away UpdateCodebases[LOOPHOLE[cseg]]; --XM Unlock[cseg]; SwapOut[cseg]; END; ProcessDefs.EnableInterrupts[]; RETURN END; SwapInCode: PUBLIC PROCEDURE [f: ControlDefs.GlobalFrameHandle] = BEGIN seg: FileSegmentHandle; gfi: ControlDefs.GFTIndex ← f.gfi; -- It is believed that Disabling during SwapIn is unnecessary -- as long as ALL interrupt code is locked. The -- Swapper should have segment locks to help fix this. info: AllocDefs.AllocInfo = [0,easy,bottomup,initial,code,FALSE,FALSE]; -- The following kludge permits us to leave CodeHandle (and everything it calls) in -- --XM -- Miscellaneous, since if the code being swapped in is Miscellaneous itself, then -- --XM -- f.code.highByte will never be 0. In effect, we are promising that if f is -- --XM -- Miscellaneous, f.code.out will never be TRUE when f.code.highByte is 0. -- --XM IF f.code.highByte = 0 THEN seg ← FrameOps.CodeHandle[f] --XM ELSE seg ← f.code.handle; --XM MakeSwappedIn[seg, DefaultBase, info]; --XM ProcessDefs.DisableInterrupts[]; IF f.code.out THEN BEGIN offset: CARDINAL ← f.code.offset; --XM BEGIN -- block for exits -- --XM -- Don't call FileSegmentAddress; it's not locked! WITH s: seg SELECT FROM --XM remote => --XM BEGIN OPEN xs: LOOPHOLE[seg, POINTER TO remote XMESA.XFileSegmentObject]; --XM IF xs.proc ~= XMESA.XMremote THEN GO TO Short --XM ELSE f.code.longbase ← LongAddressFromPage[xs.info.XMpage]+offset; --XM END; --XM ENDCASE => GO TO Short; --XM EXITS --XM Short => f.code.shortbase ← AddressFromPage[seg.VMpage]+offset; --XM END; -- block for exits -- --XM f.code.out ← FALSE; END; ProcessDefs.EnableInterrupts[]; RETURN END; ReleaseCode: PUBLIC PROCEDURE [f: ControlDefs.GlobalFrameHandle] = BEGIN Unlock[FrameOps.CodeHandle[f]]; --XM RETURN END; LastResort: SwapStrategy ← SwapStrategy[NIL,TryCodeSwapping]; StrategyList: POINTER TO SwapStrategy ← @LastResort; AddSwapStrategy: PUBLIC PROCEDURE [strategy:POINTER TO SwapStrategy] = BEGIN sp: POINTER TO SwapStrategy; ProcessDefs.DisableInterrupts[]; FOR sp ← StrategyList, sp.link UNTIL sp = NIL DO IF sp = strategy THEN RETURN; ENDLOOP; strategy.link ← StrategyList; StrategyList ← strategy; ProcessDefs.EnableInterrupts[]; RETURN END; RemoveSwapStrategy: PUBLIC PROCEDURE [strategy:POINTER TO SwapStrategy] = BEGIN sp: POINTER TO SwapStrategy; prev: POINTER TO SwapStrategy ← NIL; ProcessDefs.DisableInterrupts[]; FOR sp ← StrategyList, sp.link UNTIL sp = NIL DO IF sp = strategy THEN BEGIN IF prev = NIL THEN StrategyList ← sp.link ELSE prev.link ← sp.link; EXIT END; prev ← sp; ENDLOOP; ProcessDefs.EnableInterrupts[]; strategy.link ← NIL; RETURN END; -- XMESA Utilities -- --XM SwapOutFileSegment: PUBLIC PROCEDURE [seg: SegmentDefs.FileSegmentHandle] = BEGIN IF seg.class = code THEN BEGIN ProcessDefs.DisableInterrupts[]; SwapIn[seg]; -- lock it so it won't go away -- UpdateCodebases[LOOPHOLE[seg]]; Unlock[seg]; ProcessDefs.EnableInterrupts[]; END; SwapOut[seg]; RETURN END; XCOPY: PUBLIC PROCEDURE [from: LONG POINTER, nwords: CARDINAL, to: LONG POINTER] = BEGIN TryBLTL: PROCEDURE[from: LONG POINTER, nwords: CARDINAL, to: LONG POINTER] RETURNS [LONG POINTER] = MACHINE CODE BEGIN Mopcodes.zBLTL; Mopcodes.zPUSH; Mopcodes.zPUSH; END; Read: PROCEDURE[LONG POINTER] RETURNS [WORD] = MACHINE CODE BEGIN Mopcodes.zRBL, 0; END; Write: PROCEDURE[WORD, LONG POINTER] = MACHINE CODE BEGIN Mopcodes.zWBL, 0; END; word: WORD; lp: LONG POINTER ← TryBLTL[from, nwords, to]; IF lp ~= from THEN RETURN; -- BLTL executed in microcode -- THROUGH [0..nwords) DO word ← Read[from]; Write[word, to]; from ← from + 1; to ← to + 1; ENDLOOP; END; InvalidXMPage: PUBLIC ERROR[page: AltoDefs.PageNumber] = CODE; LongAddressFromPage: PUBLIC PROCEDURE[page: AltoDefs.PageNumber] RETURNS[lp: LONG POINTER] = BEGIN bank: MemoryOps.BankIndex; relpn: AltoDefs.PageNumber; lc: InlineDefs.LongNumber; IF page ~IN [0..XMesaDefs.MaxXPage] THEN ERROR InvalidXMPage[page]; [bank, relpn] ← InlineDefs.DIVMOD[page, XMesaDefs.PagesPerBank]; lc.lowbits ← LOOPHOLE[PAGEDISP[page: relpn, disp: 0], CARDINAL]; lc.highbits ← bank; RETURN[lc.lu]; END; -- Memory Allocator -- Extra entries in PageMap are used exclusively to communicate with the Debugger -- --XM PageMap: ARRAY [0..XMesaDefs.PagesPerBank+3) OF SegmentHandle; --XM FreePage: SegmentHandle = BootDefs.FreePage; BusyPage: SegmentHandle = BootDefs.BusyPage; BankState: TYPE = RECORD --XM [ --XM ffvmp, lfvmp: PageNumber, -- first and last free pages (hint) -- --XM bankAvailable: BOOLEAN, -- TRUE if bank can be accessed -- --XM pageRover: PageNumber, -- first place to try swapping (hint) -- --XM nFree: PageCount -- number of free pages in bank (hint) -- --XM ]; allocState: ARRAY MemoryOps.BankIndex OF BankState; --XM PageMaps: ARRAY MemoryOps.BankIndex OF POINTER TO BootDefs.PageMap; --XM -- *** The framesize of PageAvailable is depended on below *** PageAvailable: PROCEDURE [page: PageNumber, info: AllocInfo] RETURNS [available: BOOLEAN] = BEGIN seg: SegmentHandle; bank: PageCount; --XM relPage: PageNumber; --XM dummy: ARRAY [0..4) OF WORD; -- ensure that this frame is suitably large! -- --XM dummy[0]←0; --XM available ← FALSE; [bank, relPage] ← InlineDefs.DIVMOD[page, XMesaDefs.PagesPerBank]; --XM IF bank ~IN MemoryOps.BankIndex THEN ERROR; --XM IF PageMaps[bank] = NIL THEN RETURN; --XM ProcessDefs.DisableInterrupts[]; seg ← PageMaps[bank][relPage]; --XM IF seg = FreePage THEN available ← TRUE ELSE IF seg # BusyPage THEN WITH s: seg SELECT FROM file => IF (info.effort = hard OR info.swapunlocked) AND s.lock = 0 AND ~s.write AND ~s.busy THEN available ← TRUE; ENDCASE; ProcessDefs.EnableInterrupts[]; RETURN END; -- *** PageStatus' framesize must be <= PageAvailable's *** PageStatus: PROCEDURE [page: PageNumber] RETURNS [seg: SegmentHandle, status: PageState] = BEGIN bank: PageCount; --XM relPage: PageNumber; --XM [bank, relPage] ← InlineDefs.DIVMOD[page, XMesaDefs.PagesPerBank]; --XM IF bank ~IN MemoryOps.BankIndex THEN ERROR; --XM ProcessDefs.DisableInterrupts[]; IF PageMaps[bank] = NIL THEN seg ← BusyPage ELSE seg ← PageMaps[bank][relPage]; --XM SELECT seg FROM BusyPage => BEGIN status ← busy; seg ← NIL END; FreePage => BEGIN status ← free; seg ← NIL END; ENDCASE => IF seg.busy THEN BEGIN status ← free; seg ← NIL; END ELSE status ← inuse; ProcessDefs.EnableInterrupts[]; RETURN END; -- The following procedure replaces AllocVM in alloc.alloc. AllocVM is now called -- --XM -- only as an internal procedure by XAllocVM. -- --XM XAllocVM: PROCEDURE [base: PageNumber, pages: PageCount, seg: SegmentHandle, info: AllocInfo] RETURNS [PageNumber] = BEGIN OPEN XMESA; baseCopy: PageNumber ← DefaultBase; i, bank: MemoryOps.BankIndex; page: PageNumber; worthwhile, allocSucceeded: BOOLEAN; bankSelect: ARRAY MemoryOps.BankIndex OF BankOption; SetForSingleBank: PROCEDURE[b: MemoryOps.BankIndex] = BEGIN bankSelect[0] ← b; bankSelect[1] ← LAST[BankOption]; END; Swap: PROCEDURE[i,j: BankOption] = BEGIN temp: BankOption ← bankSelect[i]; bankSelect[i] ← bankSelect[j]; bankSelect[j] ← temp; END; OrderBanks: PROCEDURE[includeZero: BOOLEAN] = BEGIN -- sort XM banks from most empty to fullest bankSelect ← [1, 2, 3, 4]; IF allocState[bankSelect[0]].nFree < allocState[bankSelect[1]].nFree THEN Swap[0,1]; IF allocState[bankSelect[0]].nFree > allocState[bankSelect[2]].nFree THEN BEGIN IF allocState[bankSelect[1]].nFree < allocState[bankSelect[2]].nFree THEN Swap[1,2] END ELSE BEGIN Swap[1,2]; Swap[0,1] END; IF includeZero THEN bankSelect[LAST[MemoryOps.BankIndex]] ← 0; END; FrameOps.FlushLargeFrames[]; DO -- loop so that we can retry if InsufficientVM is RESUMEd BEGIN SELECT base FROM < XMesaDefs.MaxXPage => -- explicit base given BEGIN baseCopy ← base; SetForSingleBank[base/XMesaDefs.PagesPerBank]; END; IN [XMesaDefs.DefaultBase0 .. XMesaDefs.DefaultBase3] => SetForSingleBank[base-XMesaDefs.DefaultBase0]; XMesaDefs.DefaultMDSBase => SetForSingleBank[0]; XMesaDefs.DefaultXMBase => OrderBanks[includeZero: FALSE]; ENDCASE => -- must be DefaultBase IF info.class=code THEN OrderBanks[includeZero: TRUE] ELSE SetForSingleBank[0]; FOR i IN MemoryOps.BankIndex WHILE (bank ← bankSelect[i]) IN MemoryOps.BankIndex DO IF allocState[bank].bankAvailable THEN BEGIN [allocSucceeded, page] ← AllocVM[baseCopy, pages, seg, info, bank]; IF allocSucceeded THEN RETURN[page] END; ENDLOOP; IF info.class=table OR info.class=frame THEN GOTO SendSignal; FOR i IN MemoryOps.BankIndex WHILE (bank ← bankSelect[i]) IN MemoryOps.BankIndex DO IF allocState[bank].bankAvailable THEN DO pageRover ← allocState[bank].pageRover; roverMin ← bank*XMesaDefs.PagesPerBank; roverMax ← roverMin+XMesaDefs.PagesPerBank-1; worthwhile ← TrySwapping[pages, info, seg]; allocState[bank].pageRover ← pageRover; IF worthwhile THEN BEGIN [allocSucceeded, page] ← AllocVM[baseCopy, pages, seg, info, bank]; IF allocSucceeded THEN RETURN[page]; END ELSE EXIT; ENDLOOP ENDLOOP; EXITS SendSignal => NULL; END; SIGNAL InsufficientVM[pages]; -- falling out of the second loop means really no room ENDLOOP; END; InsufficientVM: PUBLIC SIGNAL [needed: PageCount] = CODE; VMnotFree: PUBLIC SIGNAL [base: PageNumber, pages: PageCount] = CODE; AllocVM: PROCEDURE [base: PageNumber, pages: PageCount, seg: SegmentHandle, info: AllocInfo, bank: MemoryOps.BankIndex] --XM RETURNS [success: BOOLEAN, p: PageNumber] = --XM BEGIN tempseg: XMESA.XSegmentHandle; --XM n: CARDINAL; direction: INTEGER; vm: PageNumber; IF base # DefaultBase THEN DO -- repeat if requested VM not free ProcessDefs.DisableInterrupts[]; FOR vm IN [base.. base+pages) DO IF ~alloc.avail[vm, info] THEN EXIT; REPEAT FINISHED => GOTO found; ENDLOOP; ProcessDefs.EnableInterrupts[]; SIGNAL VMnotFree[base, pages]; REPEAT found => NULL ENDLOOP ELSE BEGIN --XM ProcessDefs.DisableInterrupts[]; n ← 0; -- count of contiguous free pages IF info.direction = bottomup THEN BEGIN direction ← 1; base ← allocState[bank].ffvmp; END --XM ELSE BEGIN direction ← -1; base ← allocState[bank].lfvmp; END; --XM WHILE base IN [allocState[bank].ffvmp..allocState[bank].lfvmp] DO --XM IF ~alloc.avail[base, info] THEN n ← 0 ELSE IF (n ← n+1) = pages THEN BEGIN IF direction>0 THEN base ← base-n+1; GOTO foundHole END; base ← base+direction ENDLOOP; ProcessDefs.EnableInterrupts[]; RETURN[FALSE, 0]; -- leave strategies up to XAllocVM-- --XM EXITS --XM foundHole => NULL; END; --XM FOR vm IN [base..base+pages) DO tempseg ← LOOPHOLE[alloc.status[vm].seg]; IF tempseg # NIL THEN WITH s: tempseg SELECT FROM file => BEGIN csegBase: PageNumber ← s.VMpage; --XM WITH fs: s SELECT FROM --XM remote => IF fs.proc = XMESA.XMremote THEN csegBase ← fs.info.XMpage; --XM ENDCASE; --XM alloc.update[csegBase, s.pages, free, NIL]; --XM IF s.class = code THEN UpdateCodebases[@s]; SwapOutUnlocked[@s]; END; ENDCASE; ENDLOOP; alloc.update[base, pages, busy, seg]; ProcessDefs.EnableInterrupts[]; RETURN[TRUE, base] --XM END; -- *** UpdateVM's framesize must be <= PageAvailable's *** UpdateVM: PROCEDURE [base: PageNumber, pages: PageCount, status: PageState, seg: SegmentHandle] = BEGIN bank: PageCount; --XM relPage: PageNumber; --XM wereFree: PageCount ← 0; --XM [bank, relPage] ← InlineDefs.DIVMOD[base, XMesaDefs.PagesPerBank]; --XM IF bank ~IN MemoryOps.BankIndex OR PageMaps[bank] = NIL THEN ERROR; --XM IF status = free THEN BEGIN ProcessDefs.DisableInterrupts[]; allocState[bank].ffvmp ← MIN[allocState[bank].ffvmp,base]; --XM allocState[bank].lfvmp ← MAX[allocState[bank].lfvmp,base+pages-1]; --XM allocState[bank].nFree ← allocState[bank].nFree+pages; --XM ProcessDefs.EnableInterrupts[]; END; seg ← SELECT status FROM free => FreePage, busy => BusyPage, ENDCASE => IF seg = NIL THEN BusyPage ELSE seg; ProcessDefs.DisableInterrupts[]; FOR base IN [relPage..relPage+pages) DO --XM IF PageMaps[bank][base] = FreePage THEN wereFree ← wereFree+1; --XM PageMaps[bank][base] ← seg; --XM ENDLOOP; allocState[bank].nFree ← allocState[bank].nFree-wereFree; --XM ProcessDefs.EnableInterrupts[]; RETURN END; -- *** SwapOutUnlocked's framesize must be <= PageAvailable's *** SwapOutUnlocked: PROCEDURE [seg: XMESA.XFileSegmentHandle] = --XM BEGIN ProcessDefs.DisableInterrupts[]; seg.swappedin ← FALSE; WITH s: seg SELECT FROM --XM remote => --XM IF s.proc = XMESA.XMremote THEN -- seg.VMpage ← ChocolateToVanilla[seg,0] -- --XM BEGIN OPEN XMESA; --XM -- can't call ChocolateToVanilla because of possible frame trap, so... -- --XM fh: FileHint ← s.info.hint; --XM xsi: POINTER TO XSegInfo ← s.info; --XM p: POINTER TO XSegInfoTable ← LOOPHOLE[xsi]; --XM LOOPHOLE[p,PAGEDISP].disp ← 0; --XM IF xsi.seal # InUseSeal THEN ERROR; --XM xsi.seal ← FreeSeal; --XM xsi.link ← p.freeHead; --XM p.freeHead ← xsi; --XM p.freeCount ← p.freeCount+1; --XM seg.location ← disk[fh]; -- note that variant changes here -- --XM END; -- don't try to release possibly empty page -- --XM ENDCASE; --XM seg.VMpage ← 0; seg.file.swapcount ← seg.file.swapcount-1; ProcessDefs.EnableInterrupts[]; END; -- *** UpdateCodebases's framesize must be <= PageAvailable's *** UpdateCodebases: PROCEDURE [seg: XMESA.XFileSegmentHandle] = --XM BEGIN OPEN ControlDefs; lastUser, f: ControlDefs.GlobalFrameHandle; nUsers, i: CARDINAL; epbase: CARDINAL; segBase: PageNumber; --XM zeroPtr: POINTER = LOOPHOLE[0]; --XM vmpage: PageNumber; --XM ProcessDefs.DisableInterrupts[]; nUsers ← 0; FOR i IN [1..SDDefs.SD[SDDefs.sGFTLength]) DO [frame: f, epbase: epbase] ← GFT[i]; IF f # NullGlobalFrame AND epbase = 0 THEN --XM BEGIN IF ~f.code.out THEN BEGIN vmpage ← LOOPHOLE[f.code.shortbase,PAGEDISP].page; --XM IF f.code.highByte = 0 THEN vmpage ← vmpage + 256*f.code.otherByte; --XM segBase ← seg.VMpage; --XM WITH s:seg SELECT FROM --XM remote => IF s.proc = XMESA.XMremote THEN segBase ← s.info.XMpage; --XM ENDCASE; --XM IF vmpage ~IN [segBase..segBase+seg.pages) THEN LOOP; --XM f.code.offset ← f.code.offset - AltoDefs.PageSize*segBase; --XM f.code.handle ← LOOPHOLE[seg]; --XM f.code.out ← TRUE; END ELSE IF f.code.handle # LOOPHOLE[seg] THEN LOOP; --XM IF ~f.shared THEN EXIT; nUsers ← nUsers+1; lastUser ← f; END; REPEAT FINISHED => IF nUsers = 1 THEN lastUser.shared ← FALSE; ENDLOOP; ProcessDefs.EnableInterrupts[]; RETURN END; SetAllocationObject: PUBLIC PROCEDURE [new: AllocHandle] RETURNS [old: AllocHandle] = BEGIN ProcessDefs.DisableInterrupts[]; old ← alloc; alloc ← new; ProcessDefs.EnableInterrupts[]; RETURN END; GetAllocationObject: PUBLIC PROCEDURE RETURNS [old: AllocHandle] = BEGIN RETURN[alloc] END; -- Primative Object Allocation ObjectSeal: AltoDefs.BYTE = 21B; InvalidObject: PUBLIC SIGNAL [object: POINTER] = CODE; AllocateObject: PUBLIC PROCEDURE [size: CARDINAL] RETURNS [ObjectHandle] = BEGIN OPEN BootDefs; frob: FrobLink; frobject: FrobHandle; table: TableHandle; base, length: CARDINAL; ProcessDefs.DisableInterrupts[]; FOR table ← systemTable.table, table.link UNTIL table = NIL DO IF table.free.fwdp # FIRST[FrobLink] THEN BEGIN base ← LOOPHOLE[table, CARDINAL]; FOR frob ← table.free.fwdp, frobject.fwdp UNTIL frob = FIRST[FrobLink] DO frobject ← base+frob; length ← frobject.size; UNTIL frob+length > FrobNull DO WITH n: LOOPHOLE[frobject+length, ObjectHandle] SELECT FROM free => -- coalesce nodes BEGIN (base+n.fwdp).backp ← n.backp; (base+n.backp).fwdp ← n.fwdp; length ← length + n.size; END; ENDCASE => EXIT; ENDLOOP; SELECT length FROM = size => BEGIN (base+frobject.fwdp).backp ← frobject.backp; (base+frobject.backp).fwdp ← frobject.fwdp; table.free.size ← table.free.size + size; ProcessDefs.EnableInterrupts[]; RETURN[frobject]; END; > size => BEGIN frobject.size ← length - size; table.free.size ← table.free.size + size; ProcessDefs.EnableInterrupts[]; RETURN[frobject+length-size]; END; ENDCASE => frobject.size ← length; ENDLOOP; END; ENDLOOP; table ← AllocateTable[! UNWIND => ProcessDefs.EnableInterrupts[]]; frob ← table.free.fwdp; frobject ← LOOPHOLE[table, CARDINAL]+frob; frobject.size ← frobject.size - size; table.free.size ← table.free.size + size; ProcessDefs.EnableInterrupts[]; RETURN[frobject+frobject.size]; END; LiberateObject: PUBLIC PROCEDURE [object: ObjectHandle] = BEGIN table: TableHandle ← SegmentDefs.PagePointer[object]; size, base: CARDINAL; frob: FrobLink ← InlineDefs.BITAND[object, 377B]; base ← LOOPHOLE[table]; ValidateObject[object]; size ← WITH o: object SELECT FROM segment => SELECT o.type FROM data => SIZE[data segment Object], ENDCASE => SIZE[file segment Object], file => SIZE[file Object], ENDCASE => SIZE[length Object]; ProcessDefs.DisableInterrupts[]; IF (table.free.size ← table.free.size - size) = 0 THEN LiberateTable[table ! UNWIND => ProcessDefs.EnableInterrupts[]] ELSE BEGIN object↑ ← Object[FALSE, free[seal: ObjectSeal, size: size, fwdp: table.free.fwdp, backp: FIRST[FrobLink]]]; (base+table.free.fwdp).backp ← frob; table.free.fwdp ← frob; END; ProcessDefs.EnableInterrupts[]; RETURN END; AllocateTable: PROCEDURE RETURNS [newTable: TableHandle] = BEGIN OPEN BootDefs, SegmentDefs; frob: FrobLink = LOOPHOLE[SIZE[Table]]; base: CARDINAL; page: PageNumber; page ← alloc.alloc[DefaultBase, 1, NIL, AllocDefs.DefaultTableSegmentInfo]; newTable ← AddressFromPage[page]; newTable↑ ← Table[[FALSE, free[ObjectSeal,0, frob, frob]],systemTable.table,NIL]; base ← LOOPHOLE[newTable]; (base+frob)↑ ← [FALSE, free[ObjectSeal, AltoDefs.PageSize-SIZE[Table], FIRST[FrobLink], FIRST[FrobLink]]]; systemTable.table ← newTable; systemTable.table.seg ← BootDataSegment[page, 1]; RETURN END; LiberateTable: PROCEDURE [table:TableHandle] = BEGIN current: TableHandle; prev: TableHandle ← NIL; FOR current ← systemTable.table, current.link UNTIL current = NIL DO IF current = table THEN BEGIN IF prev = NIL THEN systemTable.table ← current.link ELSE prev.link ← current.link; -- oops: this had better not recur! DeleteDataSegment[current.seg]; RETURN END; prev ← current; ENDLOOP; ERROR InvalidObject[table]; END; ValidateObject: PUBLIC PROCEDURE [object:ObjectHandle] = BEGIN t: TableHandle; table: TableHandle = PagePointer[object]; BEGIN IF object = NIL OR InlineDefs.BITAND[LOOPHOLE[object, CARDINAL], 1] = 1 OR object.tag = free THEN GOTO invalid; IF table.free.seal # ObjectSeal THEN GOTO invalid; FOR t ← systemTable.table, t.link UNTIL t = NIL DO IF t = table THEN EXIT; REPEAT FINISHED => GOTO invalid; ENDLOOP; EXITS invalid => ERROR InvalidObject[object]; END; RETURN END; EnumerateObjects: PUBLIC PROCEDURE [type: ObjectType, proc:PROCEDURE [ObjectHandle] RETURNS [BOOLEAN]] RETURNS [object: ObjectHandle] = BEGIN i, j: CARDINAL; table: TableHandle; FOR table ← systemTable.table, table.link UNTIL table = NIL DO j ← i ← SIZE[BootDefs.Table]; FOR object ← @table.free + i, object + i UNTIL j >= AltoDefs.PageSize DO i ← WITH obj:object SELECT FROM segment => SELECT obj.type FROM data => SIZE[data segment Object], ENDCASE => SIZE[file segment Object], file => SIZE[file Object], free => obj.size, ENDCASE => SIZE[length Object]; j ← j + i; IF object.tag = type AND proc[object] THEN RETURN[object]; ENDLOOP; ENDLOOP; RETURN[NIL] END; -- Managing Data Segment Objects AllocateDataSegment: PROCEDURE RETURNS [DataSegmentHandle] = BEGIN RETURN[LOOPHOLE[AllocateObject[SIZE[data segment Object]]]]; END; ValidateDataSegment: PROCEDURE [DataSegmentHandle] = LOOPHOLE[ValidateObject]; LiberateDataSegment: PROCEDURE [DataSegmentHandle] = LOOPHOLE[LiberateObject]; GetSystemTable: PUBLIC PROCEDURE RETURNS [BootDefs.SystemTableHandle] = BEGIN RETURN[@systemTable]; END; -- Memory Bank Management Initialization -- --XM InitBank: PROCEDURE [bx: MemoryOps.BankIndex] = BEGIN OPEN XMESA, XMesaDefs; i: PageNumber; bankPageMap: POINTER TO BootDefs.PageMap; IF bx#0 THEN BEGIN ffvmp ← bx*PagesPerBank; lfvmp ← ffvmp+PagesPerBank-3; END; allocState[bx] ← [ffvmp: ffvmp, lfvmp: lfvmp, pageRover: ffvmp, nFree: lfvmp-ffvmp+1, bankAvailable: TRUE]; bankPageMap ← PageMaps[bx] ← IF bx=0 THEN LOOPHOLE[@PageMap] ELSE (PageMap[bx+Bank1X-1] ← DataSegmentAddress[MakeDataSegment[DefaultBase0, 1, PageMapAllocInfo]]); FOR i IN [0..PagesPerBank) DO bankPageMap[i] ← IF i+bx*PagesPerBank IN [ffvmp .. lfvmp] THEN FreePage ELSE BusyPage; ENDLOOP; END; EnableBank: PUBLIC PROCEDURE [b: MemoryOps.BankIndex] = BEGIN IF PageMaps[b] # NIL THEN allocState[b].bankAvailable ← TRUE ELSE InitBank[b]; END; DisableBank: PUBLIC PROCEDURE [b: MemoryOps.BankIndex] = BEGIN allocState[b].bankAvailable ← FALSE; END; -- Main body systemTable: BootDefs.SystemTable ← BootDefs.SystemTable[LOOPHOLE[@PageMap], NIL]; systemObject: AllocDefs.AllocObject; alloc: AllocHandle ← @systemObject; -- The initialization code has been almost completely rewritten for XMesa -- --XM Init: PROCEDURE = BEGIN bx: MemoryOps.BankIndex; memConfig: MemoryOps.MemoryConfig ← MemoryOps.GetMemoryConfig[]; FOR bx IN MemoryOps.BankIndex DO IF (~memConfig.useXM AND bx # 0) OR InlineDefs.BITAND[memConfig.banks, XMESA.BankMasks[bx]] = 0 THEN BEGIN allocState[bx].nFree ← 0; allocState[bx].bankAvailable←FALSE; allocState[bx].ffvmp ← bx*XMesaDefs.PagesPerBank; allocState[bx].lfvmp ← allocState[bx].ffvmp-1; PageMaps[bx] ← PageMap[bx+XMESA.Bank1X-1] ← NIL; END ELSE InitBank[bx]; ENDLOOP; END; START NucleusDefs.Miscellaneous; --XM alloc↑ ← [PageAvailable, PageStatus, UpdateVM, XAllocVM]; --XM Init[]; END.....