-- Swapper.Mesa Edited by Levin on December 29, 1978 4:22 PM
DIRECTORY
AllocDefs: FROM "allocdefs" USING [AllocHandle, AllocInfo, AllocObject, DefaultDataSegmentInfo,
DefaultFileSegmentInfo, DefaultFrameSegmentInfo, DefaultTableSegmentInfo, PageNumber,
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],
CodeDefs: FROM "codedefs" USING [CodeHandle],
ControlDefs: FROM "controldefs" USING [CSegPrefix, GFT, GlobalFrameHandle, NullGlobalFrame],
DiskDefs: FROM "diskdefs" USING [DiskRequest, SwapPages],
FrameDefs: FROM "framedefs" USING [FlushLargeFrames, ValidateGlobalFrame],
GlobalFrameDefs: FROM "globalframedefs" USING [GlobalFrameHandle],
InlineDefs: FROM "inlinedefs" USING [BITAND, DIVMOD],
MiscDefs: FROM "miscdefs",
NucleusDefs: FROM "nucleusdefs",
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, Read, RemoteSegCommand, SegmentHandle, Write],
SystemDefs: FROM "systemdefs" USING [FreePages],
XMESA: FROM "xmesaprivatedefs" USING [Bank1X, BankMasks, BankOption, FreeSeal, InUseSeal,
PageMapAllocInfo, XDataSegmentHandle, XFileSegmentHandle,
XFileSegmentObject, XMremote, XSegInfo, XSegInfoIndex, XSegInfoTable, XSegmentHandle],
XMesaDefs: FROM "xmesadefs" USING [BankIndex, DefaultBase0, DefaultBase3, DefaultXMBase,
GetMemoryConfig, MaxXPage, MemoryConfig, LongAddressFromPage, PagesPerBank, XCOPY,
XDataSegmentAddress, XFileSegmentAddress];
DEFINITIONS FROM SegmentDefs;
Swapper: PROGRAM [ffvmp, lfvmp: AltoDefs.PageNumber]
IMPORTS BootDefs, CodeDefs, DiskDefs, FrameDefs, SegmentDefs, SystemDefs, XMesaDefs
EXPORTS AllocDefs, BootDefs, FrameDefs, NucleusDefs, SegmentDefs, XMESA
SHARES SegmentDefs, XMESA = BEGIN
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: , pages: pages, unused: 0]]]; --XM
IF base > MaxVMPage THEN --XM
BEGIN --XM
OPEN s: LOOPHOLE[seg, XMESA.XDataSegmentHandle]; --XM
s.VMpage ← 0; s.XMpage ← base --XM
END --XM
ELSE seg.VMpage ← base; --XM
alloc.update[base, pages, inuse, seg];
RETURN
END;
BootDataSegment: PUBLIC PROCEDURE [base:PageNumber, pages:PageCount]
RETURNS [seg:DataSegmentHandle] =
BEGIN OPEN AllocDefs;
-- called only by Wart we hope -- --XM
i: PageNumber;
IF base > MaxVMPage THEN ERROR; --XM
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;
alloc.update[base, pages, busy, NIL]; --XM
LiberateDataSegment[seg];
alloc.update[base, pages, free, NIL]; --XM
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 BEGIN seg.lock ← seg.lock+1; ProcessDefs.EnableInterrupts[]; RETURN END; -- XM
-- seg.busy ← TRUE;
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]; --XM
-- 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[]; --XM
IF seg.swappedin THEN
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.DefaultBase0, AllocDefs.DefaultFileSegmentInfo]; --XM
IF s.loc ~= disk THEN GO TO Surprise; -- already in a high 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
s.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 --XM
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
info: AllocDefs.AllocInfo ← AllocDefs.DefaultFileSegmentInfo;
IF seg.class = code THEN info.class ← code;
MakeSwappedIn[seg, DefaultBase, info];
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
BEGIN OPEN xs: LOOPHOLE[seg,POINTER TO remote XMESA.XFileSegmentObject]; --XM
lowFileSeg: FileSegmentHandle ← NewFileSegment[xs.file, xs.base, xs.pages, Read+Write];
lowDataSeg: DataSegmentHandle ← NewDataSegment[DefaultBase, xs.pages]; --XM
XMesaDefs.XCOPY[from: XMesaDefs.XFileSegmentAddress[seg], --XM
to: XMesaDefs.XDataSegmentAddress[lowDataSeg], --XM
nwords: xs.pages*PageSize]; --XM
ChangeDataToFileSegment[lowDataSeg, lowFileSeg]; --XM
Unlock[lowFileSeg]; --XM
DeleteFileSegment[lowFileSeg]; --XM
END --XM
ELSE s.proc[@s, remoteWrite]; --XM
ENDCASE;
END;
RETURN
END;
SwapOut: PUBLIC PROCEDURE [seg:FileSegmentHandle] =
BEGIN OPEN seg;
base: PageNumber;
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 --XM
disk => BEGIN base ← s.VMpage; s.VMpage ← 0; END; --XM
remote =>
IF s.proc = XMESA.XMremote THEN base ← ChocolateToVanilla[LOOPHOLE[seg],0] --XM
ELSE BEGIN base ← s.VMpage; s.VMpage ← 0; END; --XM
ENDCASE;
alloc.update[base, pages, busy, NIL];
swappedin ← FALSE;
busy ← FALSE;
file.swapcount ← file.swapcount-1;
ProcessDefs.EnableInterrupts[];
-- IF swapcount = 0 THEN CloseFile[]; ???
alloc.update[base, pages, free, NIL];
RETURN
END;
VanillaToChocolate: PUBLIC PROCEDURE [seg: FileSegmentHandle, newpage: PageNumber]
RETURNS [oldpage: PageNumber] = --XM
BEGIN
xs: POINTER TO XMESA.XSegInfo ← AllocXSegInfo[];
oldpage ← seg.VMpage;
xs.XMpage ← newpage;
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] = --XM
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; --XM
AllocXSegInfo: PROCEDURE RETURNS [xs: POINTER TO XMESA.XSegInfo] = --XM
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] = --XM
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;
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;
-- 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;
pageRover: AltoDefs.PageNumber ← 0;-- these three variables are set by XAllocVM-- --XM
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;
status: AllocDefs.PageState;
p: POINTER TO CSegPrefix; --XM
longP: LONG POINTER TO CSegPrefix; --XM
csegpfx: CSegPrefix; --XM
n, inc: PageCount;
page ← n ← 0;
ProcessDefs.DisableInterrupts[];
segment ← LOOPHOLE[alloc.status[pageRover].seg,XMESA.XSegmentHandle]; --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 ← 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],
RECORD[XMESA.XSegmentHandle,AllocDefs.PageState]];
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
IF s.class = code THEN
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
longP ← XMesaDefs.LongAddressFromPage[csegBase]; --XM
p←@csegpfx; --XM
XMesaDefs.XCOPY[from: longP, to: LONG[p], nwords: SIZE[CSegPrefix]]; --XM
IF p.swapinfo > 1 THEN --XM
BEGIN --XM
longP ← longP + p.swapinfo; --XM
XMesaDefs.XCOPY[from: longP, to: LONG[p], nwords: SIZE[CSegPrefix]]; --XM
END; --XM
IF p.swapinfo = 0 THEN okay ← TRUE
ELSE --XM
BEGIN --XM
p.swapinfo←0; --XM
XMesaDefs.XCOPY[from: LONG[p], to: longP, nwords: SIZE[CSegPrefix]]; --XM
END; --XM
END
ELSE okay ← TRUE;
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; --XM
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 ← page + n;
WHILE page < base DO
segment ← LOOPHOLE[alloc.status[page].seg];
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
alloc.update[page, s.pages, free, NIL];
IF s.class = code THEN UpdateCodebases[@s];
SwapOutUnlocked[@s];
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: FileSegmentHandle;
FrameDefs.ValidateGlobalFrame[f];
ProcessDefs.DisableInterrupts[];
cseg ← CodeDefs.CodeHandle[f];
IF cseg # NIL THEN
BEGIN
SwapIn[cseg]; -- lock it so it won't go away
UpdateCodebases[LOOPHOLE[cseg]];
Unlock[cseg]; SwapOut[cseg];
END;
ProcessDefs.EnableInterrupts[];
RETURN
END;
SwapOutFileSegment: PUBLIC PROCEDURE [seg: SegmentDefs.FileSegmentHandle] = --XM
BEGIN
IF seg.class = code THEN
BEGIN
ProcessDefs.DisableInterrupts[];
SwapIn[seg]; -- lock it so it won't go away
UpdateCodebases[LOOPHOLE[seg]];
Unlock[seg]; SwapOut[seg];
ProcessDefs.EnableInterrupts[];
END
ELSE SwapOut[seg];
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;
-- Memory Allocator
-- extra entries to point at maps for banks 1, 2, and 3 (kludge because of interface between Mesa world and external debugger).
PageMap: ARRAY [0..XMesaDefs.PagesPerBank+3) OF SegmentHandle; --XM
FreePage: SegmentHandle = BootDefs.FreePage; --XM
BusyPage: SegmentHandle = BootDefs.BusyPage; --XM
PageMaps: ARRAY XMesaDefs.BankIndex OF POINTER TO BootDefs.PageMap; -- PageMaps[0]=@PageMap-- --XM
BankState: TYPE = RECORD --XM
[
ffvmp, lfvmp: PageNumber, -- absolute page numbers for first and last free pages (hint)
pageRover: PageNumber, -- absolute page number for first place to try swapping (hint)
nFree: PageCount, -- number of free pages in bank (hint)
bankAvailable: BOOLEAN -- TRUE if can access this bank
];
allocState: ARRAY XMesaDefs.BankIndex OF BankState; --XM
-- *** The framesize of PageAvailable is depended on below ***
PageAvailable: PROCEDURE [page: PageNumber, info: AllocInfo]
RETURNS [available: BOOLEAN] =
BEGIN
seg: SegmentHandle;
dummy: ARRAY [0..4) OF UNSPECIFIED; -- ensure that this frame is suitably large!
bank: PageCount; relPage: PageNumber; --XM
dummy[0] ← 0;
[bank, relPage] ← InlineDefs.DIVMOD[page, XMesaDefs.PagesPerBank]; --XM
IF bank ~IN XMesaDefs.BankIndex THEN ERROR; --XM
available ← FALSE;
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; relPage: PageNumber; --XM
[bank, relPage] ← InlineDefs.DIVMOD[page, XMesaDefs.PagesPerBank]; --XM
IF bank ~IN XMesaDefs.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;
XAllocVM: PROCEDURE [base: PageNumber, pages: PageCount, seg: SegmentHandle, info: AllocInfo]
RETURNS [PageNumber] = --XM
BEGIN
OPEN XMESA;
baseCopy: PageNumber ← DefaultBase;
i, bank: XMesaDefs.BankIndex;
page: PageNumber;
worthwhile, allocSucceeded: BOOLEAN;
bankSelect: ARRAY XMesaDefs.BankIndex OF BankOption;
SetForSingleBank: PROCEDURE[b: XMesaDefs.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[XMesaDefs.BankIndex]] ← 0;
END;
FrameDefs.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.DefaultXMBase => OrderBanks[includeZero: FALSE];
ENDCASE => -- must be DefaultBase
IF info.class=code THEN OrderBanks[includeZero: TRUE] ELSE SetForSingleBank[0];
-- now that the bankSelect array is set up, begin the allocation process
FOR i IN XMesaDefs.BankIndex WHILE (bank ← bankSelect[i]) IN XMesaDefs.BankIndex
DO -- first just try the appropriate banks
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; -- don't try harder in this case
FOR i IN XMesaDefs.BankIndex WHILE (bank ← bankSelect[i]) IN XMesaDefs.BankIndex
DO -- now try swapping for each bank and then retry
IF allocState[bank].bankAvailable THEN
DO
pageRover ← allocState[bank].pageRover; -- global var used by TryCodeSwapping
roverMin ← bank*XMesaDefs.PagesPerBank; roverMax ← roverMin+XMesaDefs.PagesPerBank-1;
worthwhile ← TrySwapping[pages, info, seg];
allocState[bank].pageRover ← pageRover; -- save its new value
IF worthwhile THEN
BEGIN
[allocSucceeded, page] ← AllocVM[baseCopy, pages, seg, info, bank]; --pass 2
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: XMesaDefs.BankIndex] --XM
RETURNS [success: BOOLEAN, p: PageNumber] =
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[base, info] THEN EXIT;
REPEAT
FINISHED => GOTO found;
ENDLOOP;
ProcessDefs.EnableInterrupts[];
SIGNAL VMnotFree[base, pages];
REPEAT
found => NULL
ENDLOOP
ELSE
BEGIN
ProcessDefs.DisableInterrupts[];
n ← 0; -- count of contiguous free pages
IF info.direction = bottomup THEN
BEGIN direction ← 1; base ← allocState[bank].ffvmp; END
ELSE
BEGIN direction ← -1; base ← allocState[bank].lfvmp; END;
WHILE base IN [allocState[bank].ffvmp..allocState[bank].lfvmp] DO
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
foundHole => NULL;
END;
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];
IF s.class = code THEN UpdateCodebases[@s];
SwapOutUnlocked[@s];
END;
ENDCASE;
ENDLOOP;
alloc.update[base, pages, busy, seg];
ProcessDefs.EnableInterrupts[];
RETURN[TRUE, base]
END;
-- *** UpdateVM's framesize must be <= PageAvailable's ***
UpdateVM: PROCEDURE [base: PageNumber, pages: PageCount, status: PageState,
seg: SegmentHandle] =
BEGIN
bank: PageCount; relPage: PageNumber; --XM
wereFree: PageCount ← 0; --XM
[bank, relPage] ← InlineDefs.DIVMOD[base, XMesaDefs.PagesPerBank]; --XM
IF bank ~IN XMesaDefs.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
IF PageMaps[bank][base] = FreePage THEN wereFree ← wereFree+1;
PageMaps[bank][base] ← seg;
ENDLOOP; --XM
allocState[bank].nFree ← allocState[bank].nFree-wereFree; --XM
ProcessDefs.EnableInterrupts[];
RETURN
END;
-- *** SwapOutUnlocked's framesize must be <= PageAvailable's ***
SwapOutUnlocked: PROCEDURE [seg: XMESA.XFileSegmentHandle] =
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] =
BEGIN OPEN ControlDefs;
lastUser, f: GlobalFrameHandle;
nUsers: CARDINAL ← 0; --XM
i: CARDINAL; --XM
segBase: PageNumber; --XM
zeroPtr: POINTER = LOOPHOLE[0]; --XM
vmpage: PageNumber;
ProcessDefs.DisableInterrupts[];
FOR i IN [1..SDDefs.SD[SDDefs.sGFTLength]) DO
f ← GFT[i].frame;
IF f # NullGlobalFrame AND GFT[i].epbase = 0 THEN
BEGIN
IF ~f.code.swappedout THEN --XM
BEGIN OPEN frame: LOOPHOLE[f, GlobalFrameDefs.GlobalFrameHandle];
vmpage ← LOOPHOLE[frame.code.shortCodebase,PAGEDISP].page;
IF frame.code.highByte = 0 THEN vmpage ← vmpage + 256*frame.code.topByteOfLongPointer; --XM
segBase ← seg.VMpage; --XM
WITH s:seg SELECT FROM
remote => IF s.proc = XMESA.XMremote THEN segBase ← s.info.XMpage; --XM
ENDCASE;
IF vmpage ~IN [segBase..segBase+seg.pages) THEN LOOP; --XM
f.code.offset ← f.code.offset - AltoDefs.PageSize*segBase;
f.code.swappedout ← TRUE;
f.codesegment ← LOOPHOLE[seg];
END
ELSE IF f.codesegment # 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 ← LOOPHOLE[InlineDefs.BITAND[LOOPHOLE[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;
-- Main body
systemTable: BootDefs.SystemTable ← BootDefs.SystemTable[@PageMap, NIL];
systemObject: AllocDefs.AllocObject ← [
PageAvailable, PageStatus, UpdateVM, XAllocVM];
alloc: AllocHandle ← @systemObject;
EnableBank: PUBLIC PROCEDURE [b: XMesaDefs.BankIndex] = --XM
BEGIN
IF PageMaps[b] # NIL THEN allocState[b].bankAvailable ← TRUE
ELSE InitBank[b];
END;
DisableBank: PUBLIC PROCEDURE [b: XMesaDefs.BankIndex] = --XM
BEGIN
allocState[b].bankAvailable ← FALSE;
END;
InitBank: PROCEDURE [bx: XMesaDefs.BankIndex] = --XM
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;
Init: PROCEDURE = -- almost completely rewritten for XMESA -- --XM
BEGIN OPEN XMESA, XMesaDefs;
bx: BankIndex;
memoryConfig: MemoryConfig ← GetMemoryConfig[];
FOR bx IN BankIndex
DO
IF (~memoryConfig.useXM AND bx # 0) OR InlineDefs.BITAND[memoryConfig.banks, BankMasks[bx]] = 0 THEN
BEGIN -- bank not available on this Alto; set so can't be used (will never be executed for bx=0)
allocState[bx].nFree ← 0; allocState[bx].bankAvailable←FALSE;
allocState[bx].ffvmp ← bx*PagesPerBank; allocState[bx].lfvmp ← allocState[bx].ffvmp-1;
PageMaps[bx] ← PageMap[bx+Bank1X-1] ← NIL;
END
ELSE InitBank[bx];
ENDLOOP;
END;
Init[];
END.