MBVMemory.mesa
Last edited by Sandman on 6-Aug-81 15:43:17
Last edited by Lewis on 17-Sep-81 15:52:42
Last edited by Levin on April 5, 1983 3:56 pm
DIRECTORY
Environment USING [bitsPerWord, wordsPerPage],
BcdOps USING [SPHandle],
Heap USING [Create, Delete],
Inline USING [LongCOPY, LongDiv, LongMult, LowHalf],
MB USING [BitArray, Error, Handle, Zero],
MBStorage USING [FreeWords, Words],
MBVM USING [
Base, Code, CodeSeg, DataSeg, DefaultBase, Direction, FileSeg, First64K, HyperSpace, MaxBase, MDS, nullIndex, Object, ObjectSeal, Pages, Seg],
Segments USING [DeleteSegment, FHandle, LockFromSegment, Unlock],
StartList USING [NullSwapUnitIndex];
MBVMemory: PROGRAM
IMPORTS Heap, Inline, MB, MBStorage, Segments
EXPORTS MB, MBVM =
BEGIN
OPEN MBVM;
PageSize: CARDINAL = Environment.wordsPerPage;
data: MB.Handle ← NIL;
z: UNCOUNTED ZONENIL;
minimumLength: Base = PageSize*Environment.bitsPerWord;
incrementalLength: CARDINAL = PageSize*Environment.bitsPerWord;
InitVM: PUBLIC PROC [h: MB.Handle] = {
IF data ~= NIL THEN FinishVM[];
data ← h;
data.lastVMPage ← 0;
segList ← NIL;
z ← Heap.Create[4];
};
FinishVM: PUBLIC PROC = {
IF data.vmMap ~= NIL THEN {MBStorage.FreeWords[BASE[data.vmMap]]; data.vmMap ← NIL};
IF z ~= NIL THEN {Heap.Delete[z]; z ← NIL};
data ← NIL;
};
WordsForBits: PROC [nBits: CARDINAL] RETURNS [nWords: CARDINAL] = INLINE
{RETURN[(nBits+Environment.bitsPerWord-1)/Environment.bitsPerWord]};
Segment Routines
segList: Seg ← NIL;
AllocMDSData: PUBLIC PROC [
base: Base, pages: Pages, dir: Direction ← down] RETURNS [s: DataSeg] = {
SELECT base FROM
MDS, DefaultBase => NULL;
First64K, HyperSpace, Code => ERROR;
ENDCASE => base ← (data.mdsBase + base);
RETURN[AllocData[base: base, pages: pages, dir: dir]]
};
AllocData: PUBLIC PROC [base: Base, pages: Pages, dir: Direction ← down] RETURNS [s: DataSeg] = {
low, high: Base;
SELECT base FROM
MDS => {low ← data.mdsBase; high ← data.mdsBase+255; base ← DefaultBase};
First64K => {low ← 0; high ← 255; base ← DefaultBase};
HyperSpace => {low ← data.mdsBase+256; high ← MaxBase; base ← DefaultBase};
ENDCASE => {low ← 0; high ← MaxBase};
base ← AllocVM[base: base, pages: pages, dir: dir, high: high, low: low, code: FALSE];
s ← z.NEW[data Object ← Object[
index: StartList.NullSwapUnitIndex, base: base, pages: pages, link: segList,
body: data[]]
];
segList ← s;
};
AllocCode: PUBLIC PROC [
file: Segments.FHandle, base: Base, pages: Pages, fileBase: CARDINAL, sph: BcdOps.SPHandle]
RETURNS [s: CodeSeg] = {
low, high: Base;
SELECT base FROM
Code => {low ← data.codeBase; high ← MaxBase; base ← DefaultBase};
ENDCASE => {low ← 0; high ← MaxBase};
base ← AllocVM[base: base, pages: pages, dir: up, high: high, low: low, code: TRUE];
s ← z.NEW[code Object ← Object[
index: StartList.NullSwapUnitIndex, base: base, pages: pages, link: segList,
body: code[sph: sph, file: file, fileBase: fileBase, segment: NIL]]];
segList ← s};
AllocFile: PUBLIC PROC [
file: Segments.FHandle, base: Base, pages: Pages, fileBase: CARDINAL]
RETURNS [s: FileSeg] = {
low, high: Base;
SELECT base FROM
MDS => {low ← data.mdsBase; high ← data.mdsBase+255; base ← DefaultBase};
First64K => {low ← 0; high ← 255; base ← DefaultBase};
HyperSpace => {low ← data.mdsBase+256; high ← MaxBase; base ← DefaultBase};
ENDCASE => {low ← 0; high ← MaxBase};
base ← AllocVM[base: base, pages: pages, dir: up, high: high, low: low, code: FALSE];
s ← z.NEW[file Object ← Object[
index: StartList.NullSwapUnitIndex, base: base, pages: pages, link: segList,
body: file[file: file, fileBase: fileBase, segment: NIL, bIndex: nullIndex]]
];
segList ← s;
};
SortSegs: PUBLIC PROC RETURNS [segs: LONG DESCRIPTOR FOR ARRAY OF MBVM.Seg] = {
i, nSegs: CARDINAL;
seg: MBVM.Seg;
SiftUp: PROC [low, high: CARDINAL] = {
k, son: CARDINAL;
k ← low;
DO
IF k*2 > high THEN EXIT;
IF k*2+1 > high OR segs[k*2+1-1].base < segs[k*2-1].base THEN son ← k*2
ELSE son ← k*2+1;
IF segs[son-1].base < segs[k-1].base THEN EXIT;
Exchange[son-1, k-1];
k ← son;
ENDLOOP;
};
Exchange: PROC [a, b: CARDINAL] = {
temp: MBVM.Seg ← segs[a];
segs[a] ← segs[b];
segs[b] ← temp;
};
nSegs ← 0;
FOR seg ← segList, seg.link UNTIL seg = NIL DO nSegs ← nSegs + 1 ENDLOOP;
segs ← DESCRIPTOR[MBStorage.Words[nSegs*SIZE[MBVM.Seg]], nSegs];
i ← 0;
FOR seg ← segList, seg.link UNTIL seg = NIL DO
segs[i] ← seg; i ← i + 1;
ENDLOOP;
FOR i DECREASING IN [1..nSegs/2] DO SiftUp[i, nSegs] ENDLOOP;
FOR i DECREASING IN [1..nSegs) DO Exchange[0, i]; SiftUp[1, i] ENDLOOP;
};
ReleaseCodeSegs: PUBLIC PROC = {
FreeOneCodeSegment: PROC [s: Seg] RETURNS [stop: BOOL] = {
WITH s SELECT FROM
code =>
IF segment # NIL THEN {
IF Segments.LockFromSegment[segment] # 0 THEN Segments.Unlock[segment];
Segments.DeleteSegment[segment];
segment ← NIL;
};
ENDCASE;
RETURN[FALSE]
};
[] ← EnumerateSegs[FreeOneCodeSegment]
};
PointerFromSeg: PUBLIC PROC [s: Seg] RETURNS [p: POINTER] = {
IF ~(s.base IN [data.mdsBase..data.mdsBase+255]) THEN ERROR;
RETURN[LOOPHOLE[(s.base-data.mdsBase)*PageSize]]
};
LongPointerFromSeg: PUBLIC PROC [s: Seg] RETURNS [p: LONG POINTER] = {
RETURN[LOOPHOLE[Inline.LongMult[s.base, PageSize]]]};
SegFromPointer: PUBLIC PROC [p: POINTER] RETURNS [s: Seg] = {
base: Base ← LOOPHOLE[p, CARDINAL]/PageSize;
FindSeg: PROC [seg: Seg] RETURNS [BOOL] = {RETURN[s.base = base]};
RETURN[EnumerateSegs[FindSeg]]
};
SegFromLongPointer: PUBLIC PROC [p: LONG POINTER] RETURNS [s: Seg] = {
base: Base ← Inline.LongDiv[LOOPHOLE[p], PageSize];
FindSeg: PROC [seg: Seg] RETURNS [BOOL] = {RETURN[s.base = base]};
RETURN[EnumerateSegs[FindSeg]]
};
VM Routines
EnumerateSegs: PROC [proc: PROC [Seg] RETURNS [BOOL]] RETURNS [Seg] = {
FOR seg: Seg ← segList, seg.link UNTIL seg = NIL DO
IF proc[seg] THEN RETURN[seg];
ENDLOOP;
RETURN[NIL]
};
AllocVM: PROC [base: Base, pages: Pages, dir: Direction, low, high: Base, code: BOOL]
RETURNS [Base] = {
vm: Base;
IF data.vmMap = NIL THEN {
initialLength: CARDINAL = MAX[data.mdsBase, minimumLength];
wordsForMap: CARDINAL = WordsForBits[initialLength];
data.vmMap ← DESCRIPTOR[MBStorage.Words[wordsForMap], initialLength];
MB.Zero[BASE[data.vmMap], wordsForMap];
};
IF base = DefaultBase THEN
allocate 'pages' consecutive pages in virtual pages range [low..high].
DO
incr: INTEGER;
n: CARDINAL ← 0; -- count of contiguous free pages
SELECT dir FROM
up => {incr ← 1; vm ← MIN[low, LENGTH[data.vmMap]-1]};
ENDCASE => {incr ← -1; vm ← MIN[high, LENGTH[data.vmMap]-1]};
FOR vm ← vm, vm+incr UNTIL (IF dir = up THEN vm > high ELSE vm < low) DO
IF data.vmMap[vm] OR vm = 0 OR vm = data.mdsBase THEN n ← 0
ELSE IF (n ← n + 1) = pages THEN {
base ← IF dir = up THEN vm - n + 1 ELSE vm;
IF code AND Spans64KBoundary[base, pages] THEN -- try again on next 64K boundary
IF dir = up THEN {n ← 1; vm ← 256*((base+255)/256)}
ELSE {n ← 1; vm ← 256*((base+255)/256)-1}
ELSE {ReserveVM[base, pages]; RETURN[base]};
n ← 0;
};
ENDLOOP;
Can't find sufficient VM in present vmMap. If it hasn't reached its limit,
extend it and try again.
IF ~ExtendVM[] THEN MB.Error["Memory Full"L];
ENDLOOP
ELSE {
EnsureAdequateVM[base+pages];
FOR vm IN [base..base+pages) DO
IF data.vmMap[vm] OR vm = 0 OR vm = data.mdsBase THEN EXIT;
REPEAT
FINISHED => {ReserveVM[base, pages]; RETURN[base]};
ENDLOOP;
MB.Error["Memory Busy"L];
};
ERROR
};
Spans64KBoundary: PROC [base: Base, pages: Pages] RETURNS [BOOL] = {
RETURN[(base+pages-1)/256 # base/256]};
ReserveVM: PROC [base: Base, pages: Pages] = {
data.lastVMPage ← MAX[base+pages-1, data.lastVMPage];
EnsureAdequateVM[base+pages];
FOR base IN [base..base+pages) DO data.vmMap[base] ← TRUE ENDLOOP;
};
EnsureAdequateVM: PROC [size: CARDINAL] = {
WHILE size > LENGTH[data.vmMap] DO
IF ~ExtendVM[] THEN ERROR;
ENDLOOP;
};
ExtendVM: PROC RETURNS [worked: BOOL] = {
use LONG arithmetic to avoid overflows
newLength: CARDINAL =
Inline.LowHalf[MIN[LONG[LENGTH[data.vmMap]] + incrementalLength, LONG[MaxBase] + 1]];
newWords: CARDINAL = WordsForBits[newLength];
newMap: MB.BitArray;
IF LENGTH[data.vmMap] = newLength THEN RETURN[FALSE];
newMap ← DESCRIPTOR[MBStorage.Words[newWords], newLength];
MB.Zero[BASE[newMap], newWords];
Inline.LongCOPY[
from: BASE[data.vmMap], to: BASE[newMap], nwords: WordsForBits[LENGTH[data.vmMap]]];
MBStorage.FreeWords[BASE[data.vmMap]];
data.vmMap ← newMap;
RETURN[TRUE]
};
The following isn't used any more, but we might need it someday and it will be
easy to get wrong if we recode from scratch!
ReleaseVM: PROC [base: Base, pages: Pages] = {
IF data.lastVMPage = base+pages-1 THEN
FOR lastPage: MBVM.Base DECREASING IN [0..data.lastVMPage) DO
IF data.vmMap[lastPage] THEN {data.lastVMPage ← lastPage; EXIT};
ENDLOOP;
FOR base IN [base..base+pages) DO data.vmMap[base] ← FALSE ENDLOOP
};
END.