// VMemInit.bcpl - does VMem setup for MainInit
// Last change March 28, 1985 6:04 PM by Bill van Melle
// Bpt in high vmem March 17, 1985 2:13 PM by Bill van Melle
// Major rewrite November 20, 1984 6:34 PM by Bill van Melle
// Last change May 24, 1984 10:28 AM by Bill van Melle
// Last change May 25, 1983 11:19 AM by Bill van Melle
// Last change February 4, 1983 10:29 PM by Bill van Melle
// Revised real page setup July 13, 1982 10:40 AM by Bill van Melle
// Last change June 17, 1982 5:01 PM by Bill van Melle
// Last change May 19, 1982 10:23 PM by Bill van Melle
// Last change September 5, 1981 12:38 PM by Bill van Melle
get "LispBcpl.decl"
get "Stats.decl"
get "VMem.decl"
get "AltoDefs.d"
external [ // procedures defined here
SetupLispMem
// O.S. procedures
Zero; SetBlock
CallSwat; Serial; Timer; Min
// procedures used
@BSetWriteBR; @RWrite; IGetBase; IPutBase; @BPutBase; @BGetBase
ReadRP; SetFlags; ReadFlags; MachineType
BP; VP2; IndexedPageIO; InitFmap; @BPutBase32
AllocVec; ShortStack
RestoreCursor; GiveUp
// statics defined and/or used
@MiscSTATSbase; NPages; ScreenWidth
LispFmap; EmulatorSpace; LastRealPageNo
UserName; UserPassword; AllocPtr
HostMagic0; HostMagic1; HostMagic2
sysDisk; extraBuffers; extraBufLength
fillMemory; realPageTableSetup; bigMemTable
SwapBuf; SwapBufVp; SwapBufFileP; SwapBufDirty
FirstRealPageNo; memAvailTable
ScreenWords; MinLispForRam; RamVersion
]
static [ // statics defined
EmulatorSpace; LastRealPageNo; NPages
lispBufVp
nextRP
nextRPT
initBufVPs
initBufRPs
initBufVP
initBufRP
initBuffers
lastMapIndex = -1
maxMapIndex
lastMappedFP = -2
firstMappedFP
]
manifest [
PTBLsize = 100
IsfChunkSize = 50
fpEMPTY = #177777
minSwapPages = 8
unfilledSlop = 300 // guess as to how many pages in FPTOVP are marked empty
minInitPages = 3000 // min number of pages to read in from vmem
BptThreshold = #10000 // NPages gr than this implies use highBptSegment
highBptSegment = SMALLNEGspace
]
let SetupLispMem(SYSINid, VMEMid, pnum, ppm) be
[
// SYSINid is currently only used to decide whether a /I was done, and
// hence whether we should initialize some stats.
ScreenWidth = ScreenWords lshift 4 // Save width in bits
NPages = pnum
unless NPages gr PagesPerSegment
do CallSwat("Not enough real memory")
EmulatorSpace = selecton MachineType() into
[ case Dolphin: D0BCPLspace
case Dorado: D1BCPLspace
default: CallSwat("Invalid machine type")
]
// Initialize ISF routines for access to Lisp.virtualmem
LispFmap = AllocVec(PTBLsize + IsfChunkSize*2 + 6)
// PTBLsize for isf map, rest for Lisp's use
unless InitFmap(LispFmap, PTBLsize, VMEMid, true, IsfChunkSize)
do CallSwat("Fragmented VMem")
// TransferPage needs one page aligned paging buffer for reading in a page
SwapBuf = AllocVec(WordsPerPage, WordsPerPage)
BufVp = VP2(EmulatorSpace, SwapBuf)
BufRP = ReadRP(BufVp)
lispBufVp = VP2(EmulatorSpace, AllocVec(WordsPerPage, WordsPerPage))
SetupRealPageTable(ppm)
RestoreCursor ( table [ #177777; #100001; #40002; #20004;
#14010; #6160; #3740; #1700;
#1100; #2440; #4220; #11710;
#27764; #77776; #177777; #177777
] ) // new cursor
LoadMemory() // swap in the necessary pages
if SYSINid then
[ BSetWriteBR(STATSspace, MISCSTATSbase) // zero Stats counters
for i = 0 to lenMiscStats-1 do RWrite(i, 0) ]
let tmr = vec 1 // set StartTime
Timer(tmr)
BPutBase32(STATSspace, MISCSTATSbase+MSstrtTime, tmr)
MiscSTATSbase = MISCSTATSbase // enable miscstats
]
and SetupRealPageTable(ppm) be
[ // ppm ignored nowadays
// Emulator pages are not represented in Bpt, since they are always
// resident and Lisp doesn't manage them (though it may temporarily map
// emulator buffers elsewhere). RPoffset is computed so that Bpt[i]
// corresponds to real page i+RPoffset. There is a dummy header entry 0,
// which corresponds to no page. We assume Initial has mapped virtual
// pages 0 thru NPages-1 into each good page of memory. Bpt spans that
// set of real pages, with bad or missing pages marked UNAVAIL.
// first find the smallest and largest real pages outside of the emulator,
// so we know how big to make Bpt.
let maxRealPageNo = MachineType() eq Dorado? NPages,
Min((NPages*4)/3, #10000)
maxRealPageNo = (maxRealPageNo+255) & (not 255) // round up to next segment
let tableSize = maxRealPageNo rshift 4
let masks = table [ 1; 2; 4; #10; #20; #40; #100; #200; #400;
#1000; #2000; #4000; #10000; #20000; #40000; #100000 ]
memAvailTable = AllocVec(tableSize)
Zero(memAvailTable, tableSize)
let maxp = NPages-1 // must be at least this big
let minp = #10000
let nextvp = VP2((EmulatorSpace eq 0? 1, 0), 0)
// first page after emulator
let lastvp = nextvp+NPages-PagesPerSegment-1
for vp = nextvp to lastvp
do [
if (ReadFlags(vp) & VACANT) eq VACANT
then MapConfused(vp, 0)
let p = ReadRP(vp)
test p gr maxp
then [
if p ge maxRealPageNo
then MemInitFailed("Too many bad pages", p, vp, maxRealPageNo)
maxp = p
]
or if p ls minp
then minp = p
let base = memAvailTable + (p rshift 4)
@base = @base % @(masks + (p)) // set bit for this page
SetFlags(vp, 0, VACANT) // unmap page
]
FirstRealPageNo = minp // should be #400
// to simplify what follows, insist that minp correspond to start of table
let slop = minp & #17
if slop ne 0
then [ // waste some space
NPages = NPages - (#20-slop)
minp = minp + (#20-slop)
]
let bitBase = memAvailTable + (minp rshift 4)
LastRealPageNo = maxp+1 // actually, exclusive upper bound...
RPoffset = minp - 1 // Bpt's first real entry is minp
BptSize = LastRealPageNo-RPoffset
let BptLength = lBPT*BptSize
let nBptPages = nil
// decide what kind of table to build, in this segment or elsewhere
test (NPages gr BptThreshold) % bigMemTable
ifnot [ // allocate it in this segment
Bpt = AllocVec(BptLength)
SetBlock(Bpt, EMPTY, BptLength) // assume all good
BptSegment = EmulatorSpace
]
ifso [ // allocate it in higher virtual space
BptSegment = highBptSegment
Bpt = 0
nBptPages = (BptLength-1) rshift 8 + 1
let BptVp = VP2(BptSegment, 0)
let lastBitBase = memAvailTable + (nBptPages-1) rshift 4
for base = bitBase to lastBitBase
do if @base + 1 ne 0
then MemInitFailed ("Early bad pages")
for i = 0 to nBptPages-1
do [ // allocate Bpt. Initialize pages to EMPTY
// do this by swap buf into desired real page
// setting SwapBuf appropriately, then
// swinging map back
SetFlags(BufVp, minp+i, OCCUPIED)
SetBlock(SwapBuf, EMPTY, WordsPerPage)
SetFlags(BufVp, BufRP, OCCUPIED)
SetFlags(BptVp+i, minp+i, OCCUPIED)
]
]
// now march through bit table, marking bad pages UNAVAIL
let lastBitBase = memAvailTable + (maxp rshift 4) + 1
let base = Bpt+1 // offset to flag word
BPutBase(BptSegment, base, UNAVAIL) // dummy first entry
[ let info = @bitBase
test info eq 0
ifso // 16 unavailable pages
for i = 0 to 15
do [
base = base+3
BPutBase(BptSegment, base, UNAVAIL)
]
ifnot test info eq -1
ifso // all available, do nothing
base = base + (16*3)
ifnot // mix of available and unavailable
for i = 0 to 15
do [
base = base+3
if (info&1) eq 0
then BPutBase(BptSegment, base, UNAVAIL)
info = info rshift 1
]
bitBase = bitBase + 1
] repeatuntil bitBase eq lastBitBase
if BptSegment ne EmulatorSpace
then [ // mark Bpt pages unavailable
base = 1
for i = 1 to nBptPages
do [ base = base+3; BPutBase(BptSegment, base, UNAVAIL) ]
]
// Finally, a consistency check: make sure each emulator page is resident,
// and that none are mapped into pages that Lisp thinks it owns.
let emvp = VP2(EmulatorSpace, 0)
for vp = emvp to emvp+PagesPerSegment-1
do [
let rp = ReadRP(vp)
if ((ReadFlags(vp) & VACANT) eq VACANT) %
(rp ge minp & rp le maxp
& BGetBase(BptSegment, BP(rp-RPoffset)+1) ne UNAVAIL)
then MapConfused(vp, rp)
]
// table is setup now, so finish proc can be happy anytime...
realPageTableSetup = true
]
and MapConfused(badvp, badrp) be
[
CallSwat("Memory map confused",
MachineType() eq Dolphin?
"Try booting to reinitialize map",
"Try triple booting to reinitialize map")
]
and LoadMemory() be
[
nextRP = 0
nextRPT = Bpt+1 // Initialize GetNextRP
// load InterfacePage first to get vital info
let vp = VP2(INTERFACEspace, INTERFACEbase)
GetNextRP()
SetFlags(BufVp, nextRP+RPoffset, OCCUPIED) // map real page onto buff
IndexedPageIO(LispFmap, FirstVmemBlock, BufVp lshift 8, 1, false)
SetFlags(BufVp, BufRP, OCCUPIED) // map buf back
SetFlags(vp, nextRP+RPoffset, 0) // reset flags
BPutBase(BptSegment, nextRPT, vp)
BPutBase(BptSegment, nextRPT+1, FirstVmemBlock) // nextRPT>>BPT.FILEP
LoadIPage()
// InterfacePage is in and ok. Map in more stuff now.
// First get lots of space for buffers
// Want space for FPTOVP, as well as a bunch of buffers to read vps
let FPTOVP = (@StackEnd + WordsPerPage-1) & not (WordsPerPage-1)
let newend = ShortStack(1000)
let oldend = @StackEnd
@StackEnd = newend
initBuffers = (memAvailTable + WordsPerPage-1) & not (WordsPerPage-1)
// craftiness: initBuffers is the start of a buffer space used solely for
// its virtual pages, not its contents. So it can overlap anything that
// Bcpl does not need to touch during the function DoMapInBatch.
// In particular, it can overlap FPTOVP, and also Bpt, which was the last
// allocation before this.
let nInitBufs = (newend - initBuffers) rshift 8
// number of pages of buffer space available
initBufVPs = newend - (nInitBufs lshift 1)
// allocate initBufxx vectors at end of buf space
// decrease number of buffers if necessary
nInitBufs = (initBufVPs - initBuffers) rshift 8
initBufRPs = initBufVPs + nInitBufs
initBufVP = VP2(EmulatorSpace, initBuffers)
initBufRP = ReadRP(initBufVP) // VP and RP of first of the buffers
maxMapIndex = nInitBufs-1
// now carve out space for FPTOVP somewhere between newend and initBufVPs
let bufAvail = (initBufVPs & not (WordsPerPage-1)) - FPTOVP
// space available for reading FPTOVP
let lastFP = IGetBase(IFPLastLockedFilePage)
if lastFP ge bufAvail
then CallSwat("No space for FPTOVP")
lastFP = Min(bufAvail-1, (fillMemory? NPages, minInitPages) + unfilledSlop)
let maxFP = (lastFP + WordsPerPage-1) & not (WordsPerPage-1)
// will read an integral number of pages of FPTOVP
lastFP = Min (maxFP-1, IGetBase(IFPNActivePages))
// load enough pages of FPTOVP to get to lastFP
IndexedPageIO(LispFmap, IGetBase(IFPFPTOVPStart), FPTOVP, maxFP rshift 8, false)
// now process FPTOVP until done
for nextFP = FirstVmemBlock + 1 to lastFP
do [
vp = @(FPTOVP + nextFP)
if vp ne fpEMPTY
then [
unless GetNextRP() do break
BPutBase(BptSegment, nextRPT, vp)
BPutBase(BptSegment, nextRPT+1, nextFP) // nextRPT>>BPT.FILEP
// map virtual page vp living on file page nextFP into real page nextRP.
// to take best advantage of disk, we do chunks of consecutive file
// pages at once. Thus, buffer up a few pages at a time.
if (nextFP ne lastMappedFP+1) % (lastMapIndex eq maxMapIndex)
then DoMapInBatch() // not consecutive, dump what we have
lastMapIndex = lastMapIndex+1
@(initBufRPs + lastMapIndex) = nextRP+RPoffset
@(initBufVPs + lastMapIndex) = vp
if lastMappedFP le 0
then firstMappedFP = nextFP
lastMappedFP = nextFP
]
]
if lastMapIndex ge 0
then DoMapInBatch() // finish up stragglers
@StackEnd = oldend // restore stack
]
and GetNextRP() = valof
[
// sets nextRP and nextRPT to point to next real pages, or returns false
[ nextRP = nextRP + 1
if nextRP ge BptSize
then resultis false
nextRPT = nextRPT + 3
] repeatwhile BGetBase(BptSegment, nextRPT) eq UNAVAIL
resultis true
]
and DoMapInBatch() be
[
// actually do the file transfers: read file pages firstMappedFP thru
// lastMappedFP into the virtual/real pages indicated by the vectors
// initBufVPs and initBufRPs. This is actually accomplished by mapping the
// appropriate real pages into the buffer pages, then reading the pages off
// the file (since alto disk can only read into alto pages), then swinging
// the map so that those real pages are now mapped to the appropriate
// virtual pages.
if lastMapIndex ls 0 then return
for i = 0 to lastMapIndex
do // map alto buffers into desired real pages
// note that nobody is mapped to those pages now
SetFlags (initBufVP+i, @(initBufRPs+i), OCCUPIED)
IndexedPageIO(LispFmap, firstMappedFP, initBuffers, lastMapIndex+1, false)
for i = 0 to lastMapIndex
do [ // map alto buffers back where they started, then map
// the desired virtual pages into the real pages just filled
SetFlags (initBufVP+i, initBufRP+i, OCCUPIED)
SetFlags (@(initBufVPs+i), @(initBufRPs+i), OCCUPIED)
]
lastMapIndex = -1
lastMappedFP = -2
]
and MemInitFailed (reason, a, b, c) be
CallSwat ("Memory Initialization failed", reason)
and LoadIPage() be
[
// LoadIPage checks keys and versions
// Key check - verify file is valid and complete
if IGetBase(IFPKey) ne IFPValidKey
then GiveUp(IGetBase(IFPKey) eq (not IFPValidKey) ?
"Can't resume: Inconsistent VMem file" ,
"Invalid or obselete Lisp VMem")
// Version checking
// IPage has the actual version of Lisp (which is checked here against the
// versions required by both ucode and Bcpl) and the min required versions
// of ucode and Bcpl. Bcpl/ucode compatibility is checked earlier.
let LispV = IGetBase(IFPLVersion)
unless LispV ge MinLispForRam
do GiveUp("Sysout too old for this microcode")
unless LispV ge MinLispForBcpl
do GiveUp("Sysout too old for this Lisp.Run")
unless RamVersion ge IGetBase(IFPMinRVersion)
do GiveUp("Microcode too old for this sysout")
unless BcplVersion ge IGetBase(IFPMinBVersion)
do GiveUp("Lisp.Run too old for this sysout")
if (MachineType() eq Dolphin) & (IGetBase(IFPFullSpaceUsed) ne 0)
then GiveUp("Sysout has larger virtual address space than this machine can read")
// Updating IPage entries
IPutBase(IFPRVersion, RamVersion)
IPutBase(IFPBVersion, BcplVersion)
IPutBase(IFPSerialNumber, Serial())
IPutBase(IFPEmulatorSpace, EmulatorSpace)
IPutBase(IFPScreenWidth, ScreenWords)
IPutBase(IFPUserNameAddr, UserName)
IPutBase(IFPUserPswdAddr, UserPassword)
IPutBase(IFPREALPAGETABLEPTR, BptSegment)
IPutBase(IFPREALPAGETABLEPTR+1, Bpt) // new format: store as pointer
IPutBase(IFPRPTSIZE, BptSize)
IPutBase(IFPRPOFFSET, RPoffset)
IPutBase(IFPEMBUFVP, lispBufVp)
IPutBase(IFPMachineType, MachineType())
IPutBase(IFPNSHost0, HostMagic0)
IPutBase(IFPNSHost1, HostMagic1)
IPutBase(IFPNSHost2, HostMagic2)
IPutBase(IFPSYSDISK, sysDisk)
IPutBase(IFPISFMAP, LispFmap)
IPutBase(IFPEMUBUFFERS, extraBuffers)
IPutBase(IFPEMUBUFLENGTH, extraBufLength)
IPutBase(IFPNRealPages, NPages)
]