// IfsOverlays.bcpl - Interim File System Overlay interface & package
// Copyright Xerox Corporation 1979, 1980, 1981, 1982

// last modified May 12, 1982  7:09 PM by Taft

get "IfsOverlays.decl"
get "IfsVMem.decl"
get "SysDefs.d"
get "AltoFileSys.d"
get "Disks.d"
get "IfsXEmulator.decl"

external
[
//outgoing procedures
OvDOPAGEIO; OvCleanupLocks; OvPageType
OvPageGroupAlign; OvPageGroupBase; OvPageGroupSize
OverlayLockProc; SwappedOut
FindOverlayFromPn; GenerateOverlays; DeclareOverlayPresent
OverlayFirstPn; OverlayNpages; ReadOverlay; ReleaseOverlay
@OverlayFaultProc

//incoming procedures
@VRRP; LockCell; UnlockCell; CallProc; LoadXM; StoreXM
Dequeue; Unqueue; InsertAfter
Allocate; Zero; CallersFrame; IFSError; DoubleBlt; DoubleIncrement
SetBlock; ActOnDiskPages; Dvec

//outgoing statics
oVmd; ovPageSize; logOvPageSize; logOvVmPages; ovFirstPage
numOvXMPages; numOvXMPagesPerBank; offsetResidentXM
// following are for OverlaysInit only
@OverlayEntry; @OverlaySave; FirstOD; EndOD; FirstOEP; EndOEP;
OverlayFp; OverlayDisk

//incoming statics
ifsCtxQ; sysZone; CtxRunning; CtxCaller; vmStats
]

static
[
oVmd			// -> VMD for overlays
ovPageSize		// overlay disk page size
logOvPageSize		// log of overlay disk page size
logOvVmPages		// log of VMem pages per overlay disk page
ovFirstPage		// file page number of first overlay, used to
			// normalize references in the VMem region
numOvXMPages = 0	// number of overlay pages kept in extended memory
numOvXMPagesPerBank	// number of overlay pages per XM bank
offsetResidentXM	// offset for resident code moved to XM
numOverlaysPresent = 0	// number of overlays presently in memory
@OverlayEntry
@OverlaySave
FirstOD
EndOD
FirstOEP
EndOEP
OverlayFp
OverlayDisk
// MinOverlayLoc = -1
]

structure Ctx:		// Context
[
next word		// queue link
stack word		// current stack pointer
stackMin word		// stack limit
]

manifest
[
jsrRv = #6000
jsrRv370=#6370; sta312=#55001; jsrRv367=#6367; jsrRv366=#6366;

ecSwappedOut = 24
ecOverlayAlreadyPresent = 52
ecBadOverlayCall = 53
ecFindOverlayFromPn = 54
]

// Overlay package pages are real disk pages as described in
// the "disk" structure.  logOvVmPages is a static containing
// the log of the number of vmem pages per real disk page.


// VMEM interface procedures

//----------------------------------------------------------------------------
let ReadOv(od, ctx) = valof
//----------------------------------------------------------------------------
// Procedure called via CallProc from OverlayFaultProc to assign storage
// for an overlay and read it in.  Ctx is the context that suffered the fault.
[
LockCell(lv od>>OD.core, OverlayLockProc)
let core = VRRP((OverlayFirstPn(od)-ovFirstPage) lshift logOvVmPages +
 oVmd>>VMD.base)
// The following ugly hack ensures that the context that suffered the fault
// will be the next one to run.  Otherwise the pointer we are passing back
// might become invalid because it hasn't been properly installed yet.
if ctx ne CtxRunning then
   [ Unqueue(ifsCtxQ, ctx); InsertAfter(ifsCtxQ, CtxRunning, ctx) ]
resultis core
]

//----------------------------------------------------------------------------
and OverlayLockProc(lvLock, newAddr, flag) = valof
//----------------------------------------------------------------------------
[
let ok = ReleaseOverlay(lvLock-(offset OD.core/16), flag)
if ok & flag then UnlockCell(lvLock)
resultis ok
]

//----------------------------------------------------------------------------
and OvCleanupLocks(vmd) be
//----------------------------------------------------------------------------
// Recomputes the onstack bits of all overlays by enumerating all frames
// in all contexts' stacks.
// We do this differently from the manner described in the Overlay package
// documentation because that procedure has a cost proportional to f*n,
// where f is the number of active frames and n is the number of overlays.
// IFS has a very large number of overlays, so that is much too expensive.
// Our procedure has a cost proportional to f+n.
// If CtxCaller=0 then we are still initializing and contexts aren't
// running yet, so don't enumerate the ifsCtxQ.
[
if numOverlaysPresent eq 0 then return  // nothing to do!

// Build temporary table of pages containing locked overlays.
let lenLockTable = 1 lshift (16-logOvPageSize)
let lockTable = lenLockTable; Dvec(OvCleanupLocks, lv lockTable)
Zero(lockTable, lenLockTable)
let ctx = CtxCaller eq 0? CtxRunning, ifsCtxQ!0
while ctx ne 0 do  //for all contexts
   [
   let frame = ctx eq CtxRunning? CallersFrame(), ctx>>Ctx.stack
   while frame ne 0 do
      [  //lock all pending procedures on stack
      lockTable!(frame>>FRAME.savedPC rshift logOvPageSize) = true;
      frame = frame>>FRAME.callersFrame;
      ]
   if CtxCaller eq 0 break
   ctx = ctx>>Ctx.next
   ]

// Transfer information to the Overlay Descriptors
let od = FirstOD
while od ne EndOD do
   [
   od>>OD.onstack = false
   if od>>OD.core ne 0 then
      for i = 0 to (od+lOD)>>OD.firstPn - od>>OD.firstPn -1 do
         [
         let ovPage = od>>OD.core rshift logOvPageSize + i;
         if lockTable!ovPage then od>>OD.onstack = true;
         ]
   od = od+lOD
   ]
]

//----------------------------------------------------------------------------
and OvPageType(vmd, vPage, wFlag) = 1
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and OvPageGroupAlign(vmd, vPage) = 1 lshift logOvVmPages -1
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and OvPageGroupBase(vmd, vPage) = vPage
//----------------------------------------------------------------------------
// This gets called only for the vPage that faulted, and the overlay package
// always touches the first page of the group.

//----------------------------------------------------------------------------
and OvPageGroupSize(vmd, vPage) =
//----------------------------------------------------------------------------
   OverlayNpages(FindOverlayFromPn((vPage-vmd>>VMD.base) rshift logOvVmPages +
    ovFirstPage)) lshift logOvVmPages

//----------------------------------------------------------------------------
and OvDOPAGEIO(vmd, vPage, core, nVPgs, wFlag) be
//----------------------------------------------------------------------------
[
let normPage = (vPage-vmd>>VMD.base) rshift logOvVmPages
let nPages = nVPgs rshift logOvVmPages
test normPage+nPages le numOvXMPages
   ifso  // Read overlay from XM
      [
      DoubleBlt(core, (normPage rem numOvXMPagesPerBank) lshift logOvPageSize,
       nPages lshift logOvPageSize, normPage/numOvXMPagesPerBank +1)
      DoubleIncrement(lv vmStats>>VMStats.ovXMReads)
      ]
   ifnot  // Read overlay from disk
      [
      ReadOverlay(ovFirstPage+normPage, core, nPages)
      DoubleIncrement(lv vmStats>>VMStats.ovDiskReads)
      ]
]

//----------------------------------------------------------------------------
and SwappedOut(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
    nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) be
//----------------------------------------------------------------------------
   IFSError(ecSwappedOut)

// IFS Extended Emulator version of overlay package
// Based on: Overlays.BCPL - Bcpl overlay package, by P. Deutsch,
// last edited May 24, 1977  12:48 PM

//----------------------------------------------------------------------------
// and LockPendingCode(stack; numargs na) be
//----------------------------------------------------------------------------
// [
// if na eq 0 then	// current stack
//    [
//    let od = FirstOD
//    while od ne EndOD do [ od>>OD.onstack = 0; od = od+lOD ]
//    stack = (lv stack)!-4
//    ]
// while @stack ugr stack do
//    [
//    LockPendingPc(stack!1)
//    stack = @stack
//    ]
// ]

//----------------------------------------------------------------------------
// and LockPendingPc(pc) be
//----------------------------------------------------------------------------
// [
// if pc uge MinOverlayLoc then	// O.S. or overlay
//    [
//    let od = FirstOD
//    while od ne EndOD do
//       [
//       if od>>OD.core ne 0 &
//        (pc-od>>OD.core) rshift OverlayDisk>>DSK.lnPageSize ls
//        (od+lOD)>>OD.firstPn-od>>OD.firstPn then
//          od>>OD.onstack = 1
//       od = od+lOD
//       ]
//    ]
// ]

//----------------------------------------------------------------------------
and FindOverlayFromPn(pn) = valof
//----------------------------------------------------------------------------
[
let od = FirstOD
while od ne EndOD do
   [
   if pn eq od>>OD.firstPn resultis od
   od = od+lOD
   ]
IFSError(ecFindOverlayFromPn)
]

//----------------------------------------------------------------------------
// and GeneratePresentOverlays(proc) be
//----------------------------------------------------------------------------
// [
// let od = FirstOD
// while od ne EndOD do
//    [ if od>>OD.core ne 0 then proc(od); od = od+lOD ]
// ]

//----------------------------------------------------------------------------
and GenerateOverlays(proc) be
//----------------------------------------------------------------------------
[
let od = FirstOD
while od ne EndOD do [ proc(od); od = od+lOD ]
]

//----------------------------------------------------------------------------
and OverlayFaultProc(ac0, ac1, nil, nil, nil, nil, nil, nil, nil, nil, nil,
 nil, nil, nil, nil, nil, nil, nil, nil, nil) = valof
//----------------------------------------------------------------------------
[
// *** Critical code began at JSR to missing procedure
let ove = OverlayEntry
// *** End critical code
let od = ove - (offset OD.JSR/16 +1)
// Compute static location
let callerframe = (lv ac0)!-4
let callerpc = callerframe!1-1
let callerNumArgs = callerpc!1
let callins = @callerpc
let disp = (callins&#177)-(callins&#200)
let callerea = callerpc!disp  // (extra work if not JSRII)

// Handle calls from extended memory
if callerpc + 1 eq lv (callerframe!xArgs) &  // extended call,
 od-FirstOEP uge EndOEP-FirstOEP then  // and not special?
   [
   callerframe!xPC = callerframe!xPC - 2;  // backup xPC
   let bank = callerframe!xJmp & 3;
   callins = LoadXM(bank, callerframe!xPC);
   disp = (callins&#177)-(callins&#200);
   callerea = LoadXM(bank, callerframe!xPC+disp);
   callerpc = lv callerframe!xJmp;
   ]
let target = selecton (callins&#177400) into
   [
   case #64400: callerea		// JSRII
   case #65000: callerframe!disp	// JSRIS
   case jsrRv: callins&#377		// JSR @ page 0
   case #7000: callerframe+disp		// JSR @,2
   case #77400: lv EndOD		// SWAT, ok
   default: lv FirstOD			// no good
   ]

// Find overlay descriptor
let special = nil;  // remember if special
test od-FirstOEP uls EndOEP-FirstOEP
   ifso	// special entry
      [
      if od>>OEP.JSRJMP ne (STA3JSRI + lv OverlaySave) then  // already in
        IFSError(ecOverlayAlreadyPresent)
      callerpc = od+(offset OEP.JSRJMP/16)	// re-execute in OEP
      target = lv od>>OEP.ODorEP
      od = @target
      special = true;
      ]
   ifnot
      [
      if od>>OD.core ne 0 then IFSError(ecOverlayAlreadyPresent)
      special = false;
      ]

// Read in the overlay
DeclareOverlayPresent(od, CallProc(ReadOv, od, CtxRunning))
if @target-FirstOD uls EndOD-FirstOD then
   unless special & @(target-1) ne (STA3JSRI + lv OverlaySave) do
      IFSError(ecBadOverlayCall)

// Re-execute call
callerframe!2 = callerpc
resultis (table
   [	// load up ACs and exit
   #31000	// LDA 2 0,2
   #35001	// LDA 3 1 2	// in case OEP, JSR will not be re-executed
   #3002	// JMP @2,2
   ])(ac0, ac1)
]

//----------------------------------------------------------------------------
and DeclareOverlayPresent(od, base) be
//----------------------------------------------------------------------------
[
// if base uls MinOverlayLoc then MinOverlayLoc = base
let p = base+#21+base!1
let plim = p+2*p!-1
while p ne plim do
   [
   let sa, ep = p!0, base+#20+p!1
   test @sa-FirstOEP uls EndOEP-FirstOEP
      ifso
         [  // OEP
         (@sa)>>OEP.JSRJMP = #2401  // JMP @.+1
         (@sa)>>OEP.ODorEP = ep
         ]
      ifnot
         @sa = ep  // non-OEP
   p = p+2
   ]
od>>OD.core = base
numOverlaysPresent = numOverlaysPresent+1
]

//----------------------------------------------------------------------------
and OverlayFirstPn(od) = od>>OD.firstPn
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and OverlayNpages(od) = (od+lOD)>>OD.firstPn-od>>OD.firstPn
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// and OverlayDiskAddr(od) = od>>OD.da
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// and OverlayCoreAddr(od) = od>>OD.core
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and ReadOverlay(pn, core, np) be
//----------------------------------------------------------------------------
[
let DAs, CAs = np, np
Dvec(ReadOverlay, lv DAs, lv CAs)
for i = 0 to np-1 do CAs!i = core + i lshift OverlayDisk>>DSK.lnPageSize
SetBlock(DAs, fillInDA, np+1)
DAs!0 = FindOverlayFromPn(pn)>>OD.da
ActOnDiskPages(OverlayDisk, CAs-pn, DAs-pn, OverlayFp, pn, pn+np-1, DCreadD)
]

//----------------------------------------------------------------------------
and ReleaseOverlay(od, flag) = valof
//----------------------------------------------------------------------------
[
if od>>OD.onstack ne 0 resultis false
unless flag resultis true
let base = od>>OD.core
let p = base+#21+base!1
let plim = p+2*p!-1
while p ne plim do
   [
   let sa = p!0
   test @sa-FirstOEP uls EndOEP-FirstOEP  // special entry?
      ifso  // yes
         [ (@sa)>>OEP.JSRJMP=STA3JSRI+lv OverlaySave; (@sa)>>OEP.ODorEP=od; ]
      ifnot @sa = od  // no
   p = p+2
   ]
od>>OD.core = 0
numOverlaysPresent = numOverlaysPresent-1
resultis true
]