-- JaMVMImpl.mesa
-- Pilot version
-- Original version by Martin Newell, February 1979
-- Updated June 12, 1979 4:42 PM by MN
-- Last changed by Bill Paxton, 14-Jan-82 14:03:03
-- Virtual memory is mapped onto a file, using the Pilot Space machinery.
-- The maximum size of the file is fixed at the time the VM is started,
-- but the actual file will grow in increments as necessary.
-- VM address 0 maps onto the first word of page 2 (counting from 0) of the
-- file, because page 0 is reserved for the leader page, and page 1 is used
-- by the VM package for restart information.
-- A simple marching allocator is provided.
DIRECTORY
JaMBasic,
JaMVM,
DCSFileTypes USING [tLeaderPage],
Directory,
File,
Inline,
Space;
JaMVMImpl: MONITOR
IMPORTS JaMVM, Directory, File, Inline, Space
EXPORTS JaMVM = {
OPEN JaMBasic;
-- Types and constants
AllocInfo: TYPE = MACHINE DEPENDENT RECORD[
password: CARDINAL,
words: LONG CARDINAL,
offset: CARDINAL];
vmPassword: CARDINAL = 48249; -- a more-or-less random number
abase: CARDINAL = 1; -- base page in file for alloc info
vmbase: CARDINAL = 2; -- base page in file for vm
initialSize: CARDINAL = 256;
sizeIncrement: CARDINAL = 16;
maximumPages: CARDINAL = 4096;
-- Globals
vmfile: File.Capability ← File.nullCapability;
aspace: Space.Handle ← Space.nullHandle; -- space for alloc info (1 page)
vmspace: Space.Handle ← Space.nullHandle; -- space for all of vm
vmpages: CARDINAL; -- number of pages currently mapped
vmwords: LONG CARDINAL; -- number of words currently mapped
alloc: LONG POINTER TO AllocInfo ← NIL;
vm: PUBLIC Base ← NIL;
-- Errors and Signals
VMError: PUBLIC ERROR = CODE;
BadVMPassword: PUBLIC SIGNAL = CODE;
-- Procedures
WordsForPages: PROC[p: CARDINAL] RETURNS[LONG CARDINAL] = INLINE {
RETURN[Inline.LongMult[p,Space.wordsPerPage]] };
PagesForWords: PROC[w: LONG CARDINAL] RETURNS[CARDINAL] = INLINE {
RETURN[Inline.LongDiv[w + Space.wordsPerPage - 1, Space.wordsPerPage]] };
OpenFile: PROC[name: LONG STRING] RETURNS[file: File.Capability, old: BOOLEAN] = {
-- Open a file with the given name.
-- If no file with that name exists, create one with initialSize pages.
permissions: File.Permissions = File.read + File.write + File.grow;
new: BOOLEAN ← FALSE;
-- Look up the file
file ← Directory.Lookup[fileName: name,
permissions: permissions ! Directory.Error => SELECT type FROM
fileNotFound => { new ← TRUE; CONTINUE }; ENDCASE];
IF new THEN {
file ← Directory.CreateFile[fileName: name,
fileType: DCSFileTypes.tLeaderPage, size: vmbase + initialSize];
file ← File.LimitPermissions[file,permissions];
}
ELSE {
type: File.Type ← File.GetAttributes[file].type;
IF type#DCSFileTypes.tLeaderPage THEN ERROR VMError;
IF File.GetSize[file]<vmbase THEN File.SetSize[file,vmbase];
};
RETURN[file,~new];
};
Start: PUBLIC ENTRY PROC[name: LONG STRING, maxPages: CARDINAL]
RETURNS[restarted: BOOLEAN] = {
size: CARDINAL;
IF vm#NIL THEN ERROR VMError;
[vmfile,restarted] ← OpenFile[name];
aspace ← Space.Create[size: 1, parent: Space.virtualMemory];
Space.Map[space: aspace, window: [file: vmfile, base: abase]];
alloc ← Space.LongPointer[aspace];
IF restarted THEN {
IF alloc.password#vmPassword THEN { -- file is bad
--SIGNAL BadVMPassword;-- --don't raise this now since nobody in good position to catch it!
restarted ← FALSE }; -- pretend the file didn't exist before
};
IF ~restarted THEN {
alloc.password ← vmPassword;
alloc.words ← SIZE[Root];
alloc.offset ← 0;
};
maxPages ← MIN[maxPages, maximumPages];
vmspace ← Space.Create[size: maxPages, parent: Space.virtualMemory];
vm ← Space.LongPointer[vmspace]; vmpages ← 0;
size ← PagesForWords[alloc.words + (IF alloc.offset>0 THEN 1 ELSE 0)];
MapUpTo[MAX[size,initialSize]];
};
Expand: PROC = {
want: CARDINAL ← vmpages + sizeIncrement; -- want to add at least sizeIncrement
need: CARDINAL ← PagesForWords[alloc.words + (IF alloc.offset>0 THEN 1 ELSE 0)];
pages: CARDINAL ← MAX[want,need];
MapUpTo[pages];
};
MapUpTo: PROC[pages: CARDINAL] = {
filesize: CARDINAL ← vmbase + pages; -- required file size
h: Space.Handle;
IF pages>maximumPages THEN ERROR VMError; -- too many pages
IF File.GetSize[vmfile]<filesize THEN File.SetSize[vmfile,filesize];
h ← Space.Create[size: pages - vmpages, parent: vmspace, base: vmpages];
Space.CreateUniformSwapUnits[size: 1, parent: h];
Space.Map[space: h, window: [file: vmfile, base: vmbase + vmpages]];
vmwords ← WordsForPages[vmpages ← pages];
};
Check: PUBLIC ENTRY PROC = {
IF vm=NIL THEN ERROR VMError;
Space.ForceOut[aspace];
Space.ForceOut[vmspace];
};
Flush: PUBLIC ENTRY PROC = {
IF vm=NIL THEN ERROR VMError;
vm ← NIL; alloc ← NIL;
Space.Delete[aspace];
Space.Delete[vmspace];
aspace ← vmspace ← Space.nullHandle;
};
Rptr: TYPE = Base RELATIVE LONG POINTER;
Offs: TYPE = [0..1];
AllocWords: ENTRY PROC[n: LONG CARDINAL] RETURNS[Rptr] = {
-- Increment allocation pointer by n words
-- AllocWords[0] aligns on a word boundary
-- Generates VMError (from Expand) if no more room:
rptr: Rptr;
IF alloc.offset > 0 THEN { alloc.words ← alloc.words + 1; alloc.offset ← 0 };
rptr ← LOOPHOLE[alloc.words];
alloc.words ← alloc.words + n;
IF alloc.words > vmwords THEN Expand[];
RETURN[rptr];
};
AllocChars: ENTRY PROC[n: CARDINAL] RETURNS[Rptr,Offs] = {
-- Increment allocataion pointer by length chars
-- Generates VMError (from Expand) if no more room:
rptr: Rptr ← LOOPHOLE[alloc.words];
offs: Offs ← alloc.offset;
frac: CARDINAL ← offs + n MOD 2;
alloc.words ← alloc.words + n/2 + frac/2; -- mind overflow!
alloc.offset ← frac MOD 2;
IF alloc.words >= vmwords THEN Expand[];
RETURN[rptr,offs];
};
AllocString: PUBLIC PROC[length: StringLength] RETURNS[string Object] = {
string: string Object ← [L,string[length: length, text: , offset: ]];
[string.text,string.offset] ← AllocChars[length];
RETURN[string];
};
AllocArray: PUBLIC PROC[length: CARDINAL] RETURNS[array Object] = {
array: array Object ← [L,array[length: length, base: ]];
array.base ← AllocWords[Inline.LongMult[length,SIZE[Object]]];
RETURN[array];
};
AllocTuples: PROC[size: CARDINAL] RETURNS[beg,end: TupleRptr] = {
words: LONG CARDINAL ← Inline.LongMult[size,SIZE[Tuple]];
beg ← AllocWords[words]; end ← beg + words;
RETURN[beg,end];
};
AllocDict: PUBLIC PROC[size: CARDINAL] RETURNS[dict Object] = {
dict: dict Object ← [L,dict[AllocWords[SIZE[DictBody]]]];
dd: DictBody ← [curlen: 0, maxlen: 0, size: size,
beg: , end: , curatt: 0, attach: [L,array[0,vmNIL]]];
[dd.beg,dd.end] ← AllocTuples[size];
JaMVM.PutDict[dict,dd];
RETURN[dict];
};
CopyArray: PUBLIC PROC[src,dst: array Object] = {
count: CARDINAL ← MIN[src.length,dst.length]; -- number of objects to copy
sptr: LONG POINTER ← @vm[src.base];
dptr: LONG POINTER ← @vm[dst.base];
words: LONG CARDINAL ← Inline.LongMult[count,SIZE[Object]];
chunk: CARDINAL = (LAST[CARDINAL]/SIZE[Object])*SIZE[Object];
WHILE words>0 DO
w: CARDINAL ← Inline.LowHalf[MIN[words,chunk]];
Inline.LongCOPY[from: sptr, nwords: w, to: dptr];
sptr ← sptr + w; dptr ← dptr + w; words ← words - w;
ENDLOOP;
};
GetText: PUBLIC PROC[string: string Object, text: LONG STRING] = {
count: CARDINAL ← MIN[string.length,text.maxlength];
stext: LONG POINTER TO TextBody ← @vm[string.text];
soffs: CARDINAL ← string.offset;
FOR i: CARDINAL IN[0..count) DO text[i] ← stext[soffs+i] ENDLOOP;
text.length ← count;
};
PutText: PUBLIC PROC[string: string Object, text: LONG STRING] = {
count: CARDINAL ← MIN[string.length,text.length];
stext: LONG POINTER TO TextBody ← @vm[string.text];
soffs: CARDINAL ← string.offset;
FOR i: CARDINAL IN[0..count) DO stext[soffs+i] ← text[i] ENDLOOP;
};
CopyString: PUBLIC PROC[src,dst: string Object] = {
count: CARDINAL ← MIN[src.length,dst.length]; -- number of chars to copy
stext: LONG POINTER TO TextBody ← @vm[src.text];
dtext: LONG POINTER TO TextBody ← @vm[dst.text];
soffs: CARDINAL ← src.offset;
doffs: CARDINAL ← dst.offset;
FOR i: CARDINAL IN[0..count) DO dtext[doffs+i] ← stext[soffs+i] ENDLOOP;
};
}.
Wyatt 24-Oct-81 14:42:23
add maxPages parameter to Start