// VMemInit.bcpl - does VMem setup for MainInit
// Last change September 5, 1981  12:38 PM by Bill van Melle
// Last change May 8, 1981  11:53 PM by Beau Sheil
// Tone change March 17, 1981  9:18 PM by Beau Sheil
// Trill change February 24, 1981  10:40 AM by Beau Sheil
// Last change February 19, 1981  1:05 PM by Beau Sheil

	get "LispBcpl.decl"
	get "Stats.decl"
	get "VMem.decl"

external [	// procedures defined here
	SetupLispMem; LoadIPage

		// O.S. procedures
	Closes; Endofs; ReadBlock; Zero; OpenFileFromFp; PositionPage
	CallSwat; Serial; Timer

		// procedures used
	@BSetBR; @BGetBase; @RWrite; IGetBase; IPutBase; ReadRP
	SetFlags; MachineType
	BP; VP2; IndexedPageIO; InitFmap; SmallUnbox; @BPutBase32
        AllocVec; Bytes2; ShortStack; LockPages; InitLispRegs; WriteStatsX

		// statics defined and/or used
	@MiscSTATSbase; NPages; ScreenWidth; @InterruptEnable; @InterruptChar
	EmuDiskVp; EmuDiskBuffer; LispFmap; EmulatorSpace; LastRealPageNo
	UserName; UserPassword
	]

static	[	// statics defined
	EmuDiskVp; EmuDiskBuffer; EmulatorSpace; LastRealPageNo; NPages
	]

manifest PTBLsize = 100

let SetupLispMem(SYSINid, VMEMid) be
   [
   let RamV, MinBcplForRam, MinLispForRam, ScreenWords, pnum, ppm
       = 0, nil, nil, nil, nil, nil	// adjacent for InitLispRegs
   InitLispRegs(lv RamV)		// fills in 6 words
   unless RamV ge MinRamForBcpl		// Check BEFORE using other ILR values
       do CallSwat("Microcode too old for this lisp.run")
   unless BcplVersion ge MinBcplForRam
       do CallSwat("Lisp.run too old for this microcode")
   ScreenWidth = ScreenWords lshift 4	// Save width in bits
   unless (pnum rem ppm) eq 0		// bad pages unless pnum mod ppm = 0
       do CallSwat("Bad pages found in main memory")
   let mnum = pnum / ppm		// # modules
   let pbm = nil			// pages between mods
   switchon MachineType() into
      [ case Dorado:			// Dorado specific code
             [ pbm = ppm; EmulatorSpace = D1BCPLspace ]
             endcase
	case Dolphin:			// Dolphin specific code
             [ pbm = #1000; EmulatorSpace = D0BCPLspace ]
             endcase
	default: CallSwat("Invalid machine type")
      ]
   LastRealPageNo = ppm+pbm*(mnum-1)
   NPages = mnum*ppm			// dumped by stats
   unless NPages gr PagesPerSegment
       do CallSwat("Not enough real memory")

// TransferPage needs one page aligned paging buffer for reading in a page
   BufVp = VP2(EmulatorSpace, AllocVec(WordsPerPage, WordsPerPage))
   BufRP = ReadRP(BufVp)

// Disk routines need one also
   EmuDiskBuffer = AllocVec(WordsPerPage, WordsPerPage)
   EmuDiskVp = VP2(EmulatorSpace, EmuDiskBuffer)

// ReadRP gets page number of first real page. Then skip over emulator pages
   RPoffset = ReadRP(VP2(EmulatorSpace, 0))+PagesPerSegment
   BptSize = LastRealPageNo-PagesPerSegment
   [
   let BptLength = lBPT*BptSize
   Bpt = AllocVec(BptLength)		// alloc and zero page table
   Zero(Bpt, BptLength)
   ]
   for i = 0 to BptSize-1 do BP(i)>>BPT.STATE = UNAVAIL
// can't use buffer rel 0, as 0 used as an end marker
   AddBuffers(1, ppm - #401)		// real pages for xvmem
   for m = 1 to mnum-1 do
	AddBuffers(pbm*m - WordsPerPage, pbm*m + ppm - #401)

// make all 2↑14 map entries (except for alto emulator) vacant
   for s = 0 to LastVirtualPage<<VP.segment do unless s eq EmulatorSpace
    do for p = 0 to PagesPerSegment-1
        do [ let i = Bytes2(s, p); SetFlags(i, i+PagesPerSegment, VACANT) ]

   LispFmap = AllocVec(PTBLsize)	// for isf access to lisp.virtualmem
   unless InitFmap(LispFmap, PTBLsize, VMEMid, true, 50)
       do CallSwat("Fragmented VMem")

   test (SYSINid eq 0) % (SYSINid eq -1) // SYSINid is zero if no sysin file
     ifso LoadIPage(RamV, MinLispForRam, ScreenWords)
     ifnot				// read sysin file into VMem
   [ let sst = OpenFileFromFp(SYSINid)
     unless sst do CallSwat("Can't find /I file")

//   We can use the rest of the space between the last AllocVec and the
//   current end of stack for buffers.  This space will eventually go to
//   the display bit map, but for now it is empty.
     let Buffers = AllocVec(0, WordsPerPage)	// first buf page
     let LowStackPage = ShortStack(1000)	// allow 1000 words of stack
     let bsize = LowStackPage - Buffers		// size of buffer region
     if bsize ls WordsPerPage then CallSwat("No buffers for SYSIN")
     let OldLowStack = rv StackEnd		// save old low stack
     @StackEnd = LowStackPage			// enforce stack end in read

//   Restore SavedVMEMstate from the InterfacePage
     PositionPage(sst, FirstVmemBlock);  ReadBlock(sst, Buffers, WordsPerPage)
//   First page of Buffers now has InterfacePage of sysin - write it into vmem
     IndexedPageIO(LispFmap, FirstVmemBlock, Buffers, 1, -1)
//   Interface page is now in VMEM file, so load, lock and check it
     LoadIPage(RamV, MinLispForRam, ScreenWords)

//   now read the pages off the file and into vmem - the entries in
//   the page maps on the file have the correct vmem address already
     let nxtpage = FirstVmemBlock + 1
     until Endofs(sst) do
       [ let npgs = ReadBlock(sst, Buffers, bsize)<<HiByte
         IndexedPageIO(LispFmap, nxtpage, Buffers, npgs, -1)
         nxtpage = nxtpage + npgs
       ]
     Closes(sst)
     @StackEnd = OldLowStack			// restore previous stack end
     ]

// get PageMapTBL and the pages of the PageMap
   LockPages(PMTspace, PMTbase, 2)	// 2 pages covers 22-bit addresses
// we need only the first page but all are locked for performance
   LockPages(PAGEMAPspace, PAGEMAPbase, (IGetBase(IFPNxtPMAddr)-1)<<HiByte+1)

// for free variable lookup in microcode, all stack pages must be in core
   LockPages(STACKspace, 0, IGetBase(IFPEndOfStack)<<HiByte + 1)

// bring in the pages of the MDStypeTable and the locked down STATS pages.
// The latter contain the interrupttable, misc stats, UFN table, etc.
   LockPages(MDSTYPEspace, MDSTYPEbase, MDSTTsize)
   LockPages(STATSspace, STATSbase, STATSsize)
   
// lock down the defs of the #400+ subrs as they field punts incl page faults
   LockPages(DEFspace, #1000, 1)

   compileif DTDSize then [
	// lock down DTD table
	LockPages(DTDspace, DTDbase, DTDSize)

	// lock down the main GC table, so gc refs work in microcode.
	LockPages(HTMAINspace, HTMAINbase, HTMAINnpages)
	]

   if SYSINid then
     [ BSetBR(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 AddBuffers(firstRP, lastRP) be
   for tn = firstRP to lastRP do
      [ let bp = BP(tn)
        bp>>BPT.STATE = EMPTY
        bp>>BPT.NEXT = @Bpt
        if @Bpt eq 0 then BptLast = tn
        @Bpt = tn
        if LogPagingFlag then WriteStatsX(evAddBuffer, 0, tn)
      ]

and LoadIPage(RamVersion, MinLispForRam, ScreenWords) be
  [ 
  // LoadIPage loads and locks the interface page and checks keys and versions
  LockPages(INTERFACEspace, INTERFACEbase, 1)

  // Key check - verify file is valid and complete
  if IGetBase(IFPKey) ne IFPValidKey
     then CallSwat(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 CallSwat("Sysout too old for this microcode")
  unless LispV ge MinLispForBcpl
      do CallSwat("Sysout too old for this Lisp.Run")
  unless RamVersion ge IGetBase(IFPMinRVersion)
      do CallSwat("Microcode too old for this sysout")
  unless BcplVersion ge IGetBase(IFPMinBVersion)
     then CallSwat("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)

  // Caching some data from the IPage into statics
  InterruptEnable = IGetBase(IFPInterruptEnable)
  InterruptChar = IGetBase(IFPInterruptChar)

  ]