-- 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