// VMemInit.bcpl - does VMem setup for MainInit
// 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"
get "AltoFileSys.d"
get "Streams.d"
external [ // procedures defined here
SetupLispMem; LoadIPage
// in LocalVMemInit.bcpl
LocalInitVmem
// O.S. procedures
Closes; Endofs; ReadBlock; Zero; SetBlock; OpenFile; PositionPage
CallSwat; Serial; Timer; OpenFileFromFp; WriteBlock; Min
// procedures used
@BSetWriteBR; @BGetBase; @RWrite; IGetBase; IPutBase; ReadRP
SetFlags; ReadFlags; LookupPage; GetPageInCore; MachineType
BP; VP2; IndexedPageIO; InitFmap; SmallUnbox; @BPutBase32
AllocVec; Bytes2; ShortStack
RestoreCursor; GiveUp
// statics defined and/or used
@MiscSTATSbase; NPages; ScreenWidth
EmuDiskVp; EmuDiskBuffer; LispFmap; EmulatorSpace; LastRealPageNo
UserName; UserPassword; AllocPtr
HostMagic0; HostMagic1; HostMagic2
sysDisk; extraBuffers; extraBufLength
fillMemory; realPageTableSetup
SwapBuf; SwapBufVp; SwapBufFileP; SwapBufDirty
ScreenWords; MinLispForRam; RamVersion
]
static [ // statics defined
EmuDiskVp; EmuDiskBuffer; EmulatorSpace; LastRealPageNo; NPages
lispBufVp
]
manifest [
PTBLsize = 100
IsfChunkSize = 50
]
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")
]
// TransferPage needs one page aligned paging buffer for reading in a page
SwapBuf = AllocVec(WordsPerPage, WordsPerPage)
BufVp = VP2(EmulatorSpace, SwapBuf)
lispBufVp = VP2(EmulatorSpace, AllocVec(WordsPerPage, WordsPerPage))
BufRP = ReadRP(BufVp)
// Disk routines need one also
// EmuDiskBuffer = AllocVec(WordsPerPage, WordsPerPage)
EmuDiskBuffer = extraBuffers+extraBufLength-WordsPerPage
EmuDiskVp = VP2(EmulatorSpace, EmuDiskBuffer)
SetupRealPageTable(ppm)
LispFmap = AllocVec(PTBLsize + IsfChunkSize*2 + 6)
// for isf access to lisp.virtualmem
// PTBLsize for isf map, rest for Lisp's use
unless InitFmap(LispFmap, PTBLsize, VMEMid, true, IsfChunkSize)
do CallSwat("Fragmented VMem")
RestoreCursor ( table [ #0; #100001; #40002; #20004;
#14010; #6160; #3740; #1700;
#1100; #2440; #4220; #11710;
#27764; #77776; #177777; #177777
] ) // new cursor
LockInitPages() // 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
IPutBase(IFPRPTLAST, BptLast) // store chain tail pointer
BptLast = -1 // and disable it from now on
]
and SetupRealPageTable(ppm) be
[ // ppm ignored nowadays
if NPages gr #10000
then NPages = #10000 // temporary to keep big mem Dorados happy
// 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 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 maxp = p
or if p ls minp
then minp = p
]
LastRealPageNo = maxp+1 // actually, exclusive upper bound...
RPoffset = minp - 1 // Bpt's first real entry is minp
BptSize = LastRealPageNo-RPoffset
let BptLength = lBPT*BptSize
Bpt = AllocVec(BptLength) // alloc and initialize page table
SetBlock(Bpt, UNAVAIL, BptLength) // table entries all bad at first
// now march through the map again, marking good pages EMPTY,
// and linking them together
let lastbp = Bpt // trailing pointer
let nextrp = nil
for vp = nextvp to lastvp
do [
nextrp = ReadRP(vp) // next real page
let thisbp = BP(nextrp - RPoffset) // its Bpt entry
if thisbp>>BPT.STATE ne UNAVAIL
then MapConfused(vp, nextrp)
thisbp>>BPT.STATE = EMPTY
lastbp>>BPT.NEXT = nextrp - RPoffset // chain previous entry to it
lastbp = thisbp
SetFlags(vp, 0, VACANT) // tell map this page is now empty, too
]
lastbp>>BPT.NEXT = 0
BptLast = nextrp - RPoffset
// 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 & BP(rp-RPoffset)>>BPT.STATE ne UNAVAIL)
then MapConfused(vp, rp)
]
// table is setup now, so finish proc can be happy anytime...
realPageTableSetup = true
// just in case, let's also mark all the other vp's vacant.
// Actually, EmulatorSpace is always zero now, but this is a
// marker in case someone changes his mind.
for vp = NPages to (LastVirtualPage<<VP.segment eq EmulatorSpace?
LastVirtualPage-PagesPerSegment, LastVirtualPage)
do SetFlags(vp, 0, VACANT)
]
and MapConfused(badvp, badrp) be
[
CallSwat("Memory map confused",
MachineType() eq Dolphin?
"Try booting to reinitialize map",
"Try triple booting to reinitialize map")
]
and LoadIPage() be
[
// LoadIPage loads and locks the interface page and checks keys and versions
LockSpecialPageVp(VP2(INTERFACEspace, INTERFACEbase))
// 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)
then GiveUp("Lisp.Run too old for this sysout")
// 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(IFPREALPAGETABLE, Bpt)
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)
]
and LockInitPages() be
[
LoadIPage()
// get PageMapTBL and the pages of the PageMap
SpecialLockPages(PMTspace, PMTbase, 2)
// 2 pages covers 22-bit addresses
// we need only the first page but all are locked for performance
SpecialLockPages(PAGEMAPspace, PAGEMAPbase,
(IGetBase(IFPNxtPMAddr)-1)<<HiByte+1)
// now that the essentials are in, pull in any locked pages
for i = 0 to MaxKey1
do [ // mapping over primary table
let pmpE = BGetBase(PMTspace, PMTbase+i)
if pmpE ne -1
then [ // there are secondary entries
for j = 0 to MaxKey2
do [
let fp = BGetBase(PAGEMAPspace, PAGEMAPbase+pmpE+j)
if fp ls 0
then [ // lock bit set
let vp = (i lshift 5)+j
test (ReadFlags(vp) & VACANT) eq VACANT
ifso GetPageInCore(vp, fp, false)
ifnot // page already read
BP(ReadRP(vp)-RPoffset)>>BPT.LOCK = true
// redundant??
]
]
]
]
if fillMemory
then [ // fill real memory with everything else
for i = 0 to MaxKey1
do [ // mapping over primary table
if BP(Bpt>>BPT.NEXT)>>BPT.STATE ne EMPTY
then break // stop when no empties left
let pmpE = BGetBase(PMTspace, PMTbase+i)
if pmpE ne -1
then [ // there are secondary entries
for j = 0 to MaxKey2
do [
let fp = BGetBase(PAGEMAPspace, PAGEMAPbase+pmpE+j)
if fp gr 0
then [ // lock bit set
let vp = (i lshift 5)+j
if (ReadFlags(vp) & VACANT) eq VACANT
then GetPageInCore(vp, fp, false)
]
]
]
]
]
]
and SpecialLockPages(vaHi, vaLo, nbrPages) be
[
let v = VP2(vaHi, vaLo)
for i = 0 to nbrPages-1 do LockSpecialPageVp(v+i)
]
and LockSpecialPageVp(vp) be
[
if (ReadFlags(vp) & VACANT) eq VACANT // is page already in core?
then
[
// Special case code to return vmem file address of pages which support
// page lookup. These pages are looked up here b/c they are always locked
// before their first reference and this is a low frequency operation.
// InterfacePage is known to be on FirstVmemBlock. Rest of page map
// tables are at a fixed location wrt various interface page entries.
let fpage =
vp eq InterfacePageVP ? FirstVmemBlock,
vp eq PAGEMAPvp ? IGetBase(IFPfilePnPMP0),
(vp & #177770) eq PMTspaceVP ?
IGetBase(IFPfilePnPMT0)+(vp&7),
LookupPage(vp)
unless fpage do return
GetPageInCore(vp, fpage % LOCKbit, false)
]
]