-- RTZones.Mesa: An internal Cedar system interface.
-- Defines ZONE data structures (also quantum map, object storage format)
-- last edited 12-Aug-81 15:27:01 by Willie-Sue Haugeland
-- last edited May 20, 1982 10:04 am by Paul Rovner

DIRECTORY
  RTQuanta USING[QuantumSize, QuantumIndex, QuantumCount, QuantumSizeDIV,
                       LASTAddress, PtrToQtmIndex],
  RTBases USING[BaseOverhead],
  RTBasic USING[Base, Pointer, TypeIndex, Type],
  SafeStorage USING [SizeRepresentation, ZoneFullProc],
  UnsafeStorage USING [UZoneFullProc],
  Runs USING [Run];

RTZones: DEFINITIONS IMPORTS RTQuanta SHARES RTBasic
= BEGIN
  OPEN RTBasic, RTQuanta, SafeStorage, UnsafeStorage;

    -- Eternal refs to the built-in zones
  zoneSystem: ZONE;
  zoneHeapSystem: UNCOUNTED ZONE;

    -- The "quantum map": VM QuantumIndex to ZoneFinger
  MapQZf: TMapQZf;
  TMapQZf: TYPE = LONG POINTER TO RMapQZf;
  RMapQZf: TYPE = RECORD[mzs: SEQUENCE length: NAT OF ZoneFinger];

    -- each ZoneFinger identifies either a prefixed ZONE or a subZone of a quantized ZONE
  ZoneFinger: TYPE = MACHINE DEPENDENT RECORD
    [SELECT ztype: * FROM
       prefixed => [zi: ZoneIndex],
       sub => [szi: SubZoneIndex],
      ENDCASE];
  mzVacant: ZoneFinger = [prefixed[0]];

  ZoneIndex: TYPE = CARDINAL [0..LAST[CARDINAL]/2];  -- so ZoneFinger fits in 1 word
  SubZoneIndex: TYPE = CARDINAL [0..LAST[CARDINAL]/2];  -- ditto

    -- The map from ZoneIndex to Zone
  MapZiZn: TMapZiZn;
  TMapZiZn: TYPE = REF RMapZiZn;
  RMapZiZn: TYPE = RECORD[zones: SEQUENCE length: NAT OF Zone];
  PMapZiZn: TYPE = RECORD[zones: SEQUENCE length: NAT OF LONG UNSPECIFIED];

  Zone: TYPE = REF ZoneRec;
    QuantizedZone: TYPE = REF quantized ZoneRec;
    PrefixedZone: TYPE = REF prefixed ZoneRec;

  PZone: TYPE = LONG POINTER TO ZoneRec;
    PQuantizedZone: TYPE = LONG POINTER TO quantized ZoneRec;
    PPrefixedZone: TYPE = LONG POINTER TO prefixed ZoneRec;

  ZoneRec: TYPE = MACHINE DEPENDENT RECORD  -- used by the microcode
    [ new: UNSPECIFIED,  -- PROC[self: PrefixedZone, size: CARDINAL, type: Type]
                               --   RETURNS[REF ANY] 
      free: UNSPECIFIED,  -- PROC[self: PrefixedZone, object: REF ANY]
      LOCK: MONITORLOCK,
      zi: CARDINAL, --ZoneIndex,--  -- this ZONE's zone index
      linkage: ZoneLinkage,  -- indicates ZONE or UNCOUNTED ZONE
      qFirst: CARDINAL ← 0, --a QuantumIndex. If non-zero, this indicates that this ZONE owns
                                  --the first quantum of its BASE, hance that adjustment by
                                  --BaseOverhead is required in various situations
      runs: Runs.Run ← NIL,  -- of quanta that belong to this zone
      cellsInService: LONG CARDINAL ← 0,    -- don't count on it
      objectsInService: LONG CARDINAL ← 0,  -- ditto
      overheadCells: LONG CARDINAL ← 0,    -- ditto
      freeLists: SELECT sr: SafeStorage.SizeRepresentation FROM
        quantized =>
          [ fill: [0..LAST[CARDINAL]/2] ← 0,
            qNext, qLast: CARDINAL ← 0, --QuantumIndices assigned to this subzone but not
                                               --carved up yet and put on a free list
                                               -- [qNext..qLast)
            mAsz: CARDINAL ← 3,  -- hash mask
            nAsz: CARDINAL ← 0,  -- number of hash table entries in use
            pAsz: SubZoneArray ← szaNil  -- the subzone hash table, (type, size) -> SubZoneRec
          ],
        prefixed =>
          [ fill: [0..LAST[CARDINAL]/2] ← 0,
            fnd: free NodeHeader ← fndNil,  -- the prefixed free list is doubly linked; the free ring
                                                  -- includes the ZoneRec, which looks like a zero-length
                                                  -- object
            pfn: PFreeNode ← NIL  -- the "rover" of the prefixed free list
          ],
       ENDCASE
    ];

  ZoneLinkage: TYPE = RECORD
    [ SELECT tag: * FROM
        collectible => [fullProc: SafeStorage.ZoneFullProc, base: RTBasic.Base],
        heap => [fullProc: UnsafeStorage.UZoneFullProc,
                       -- UNCOUNTED ZONEs get their quanta from the RootBase
                   typeRepresentation: BOOLEAN ← FALSE,
                   fill: CARDINAL ← 0]
       ENDCASE];

  SubZoneArray: TYPE = LONG DESCRIPTOR FOR ARRAY OF SubZoneRec;
    szaNil: LONG DESCRIPTOR FOR ARRAY OF UNSPECIFIED = LOOPHOLE[descNilA];
         -- done this way because Mesa doesn't recognize DESCRIPTOR[NIL, 0] as constant
    descNilA: ARRAY [0..2] OF CARDINAL = [0, 0, 0];

  SubZoneRec: TYPE = MACHINE DEPENDENT RECORD  -- "rSz", element of hash array
    [ typePad: [0..3] ← 0,
      type: Type,
      size: CARDINAL,
      fl: FreeList ← NIL,
      zi: CARDINAL--ZoneIndex--,  -- my parent's index in MapZiZn (logically a backpointer)
      szi: CARDINAL--SubZoneIndex--  -- my own index in MapSziSz (logically a backpointer)
    ];

  FreeList: TYPE = LONG POINTER TO FreeList;

  SubZone: TYPE = LONG POINTER TO SubZoneRec;

    -- The map from subzone index to SubZone
  MapSziSz: TMapSziSz;
  TMapSziSz: TYPE = LONG POINTER TO RMapSziSz;
  RMapSziSz: TYPE = RECORD[pointers: SEQUENCE length: NAT OF SubZone];

-- Constants
  nZiMaxInit: NAT = 200;  -- initial size of MapZiZn
  nSziMaxInit: NAT = 300;  -- initial size of MapSziSz
  sziVacant: SubZoneIndex = 0;

-- Node structures in prefixed zones
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
--  all objects start on even word boundaries
--  a TypeIndex is 14 bits
--  max client object size = 64K words (free blocks can be arbitrarily big)
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

    -- size includes the overhead; is for entire block
  NodeHeader: TYPE = MACHINE DEPENDENT RECORD
    [SizeLo: LongNodeSizeLo← 0,
     body: SELECT state: NodeState FROM
               inuse => [marked: BOOLEAN← FALSE,
                           type: TypeIndex--14 bits--],
               free =>  [unused: [0..177B]← 0,
                          SizeHi: LongNodeSizeHi← 0,
                          pfnNext, pfnPrev: PFreeNode],
              ENDCASE
    ];

  NodeState: TYPE = MACHINE DEPENDENT {inuse(0), free(1)};

  PNode: TYPE = LONG POINTER TO NodeHeader;  -- "pn"
  PFreeNode: TYPE = LONG POINTER TO free NodeHeader;  -- "pfn"
  InusePNode: TYPE = LONG POINTER TO inuse NodeHeader;  -- "ipn"

  fndNil: free NodeHeader =  [body: free[0, 0, NIL, NIL]];
  sizeNd: CARDINAL = SIZE[inuse NodeHeader];  -- overhead for an allocated prefixed node

  LongNodeSizeLo: TYPE = CARDINAL;
  LongNodeSizeHi: TYPE = [0..377B];
    MinBlockSize: LongNodeSizeLo = SIZE[free NodeHeader];
  LongNodeSize: TYPE = LONG CARDINAL;
   LASTLongNodeSize: LongNodeSize = LAST[LONG CARDINAL]/256;  -- 24 bits
   MaxClientBlockSize: CARDINAL = LAST[CARDINAL] - 1;        -- even size

-- procedures

  MapPtrZf: PROC[ptr: Pointer] RETURNS[ZoneFinger] = INLINE
    { RETURN[MapQZf[MapPtrQ[ptr]]]};
  MapPtrQ: PROC[ptr: Pointer] RETURNS[QuantumIndex] = INLINE
    { RETURN[PtrToQtmIndex[ptr]]};

  MapRefZf: PROC[ref: REF ANY] RETURNS[ZoneFinger] = INLINE
    { RETURN[MapQZf[MapRefQ[ref]]]};
  MapRefQ: PROC[ref: REF ANY] RETURNS[QuantumIndex] = INLINE
    { RETURN[MapPtrQ[LOOPHOLE[ref, Pointer]]]};

  MapSizeNq: PROC [size: LONG CARDINAL] RETURNS[QuantumCount] = INLINE
    {RETURN[RTQuanta.QuantumSizeDIV[size+(RTBases.BaseOverhead+QuantumSize-1)]]};

  IsSubZoneVacant: PROC[sz: SubZone] RETURNS[BOOLEAN] = INLINE
    {RETURN[sz.szi = sziVacant]};

  GetZone: PROC[zi: ZoneIndex] RETURNS[Zone];

  ReturnQuanta: PROC[pzn: PZone, q: QuantumIndex, nQ: QuantumCount];

  AddBlock: PROC[ptr: Pointer, size: LongNodeSize, pfnPrev: PFreeNode];

  NodeLength: PROC[pn: PNode] RETURNS[LongNodeSize] = INLINE
    { lnds: MDRLongNodeSize;
      lnds.lnsLo← pn.SizeLo;
      WITH pnx: pn SELECT FROM
                inuse => lnds.lnsHi← LOOPHOLE[0];
                free => lnds.lnsHi← pnx.SizeHi;
                ENDCASE;

      lnds.unused← LOOPHOLE[0];
      RETURN[LOOPHOLE[lnds, LongNodeSize]]};

          -- used to make NodeLength fast
      MDRLongNodeSize: TYPE = MACHINE DEPENDENT RECORD
          [lnsLo: LongNodeSizeLo,
           unused: [0..377B],
           lnsHi: LongNodeSizeHi
          ];

  AllocateZi: PROC RETURNS[zi: ZoneIndex];
  AllocateSzi: PROC RETURNS[szi: SubZoneIndex];

  GetZiNext: PROC RETURNS[zi: ZoneIndex];
  GetSZiNext: PROC RETURNS[szi: SubZoneIndex];
  GetnZiMax: PROC RETURNS[zi: ZoneIndex];
  GetnSziMax: PROC RETURNS[szi: SubZoneIndex];

  SetZiNext: PROC[zi: ZoneIndex];
 
  GetSzZi: PROC[szi: SubZoneIndex] RETURNS[ZoneIndex] =
INLINE{RETURN[MapSziSz[szi].zi]};

  GetSzSize: PROC[szi: SubZoneIndex] RETURNS[CARDINAL] = INLINE{RETURN[MapSziSz[szi].size]};

  GetSzType: PROC[szi: SubZoneIndex] RETURNS[type: Type] = INLINE{RETURN[MapSziSz[szi].type]};

  AssignSz: PROC[szi: SubZoneIndex, sz: SubZone];
 
  FromCollectibleZone: PROC[refObj: REF ANY] RETURNS[BOOLEAN] = INLINE
   { z: Zone;
     zf: ZoneFinger;
     IF LOOPHOLE[refObj, LONG CARDINAL] > RTQuanta.LASTAddress THEN RETURN[FALSE];
     zf ← MapRefZf[refObj];
     WITH zf: zf SELECT FROM
       sub => z ← MapZiZn[GetSzZi[zf.szi]];
       prefixed => z ← MapZiZn[zf.zi];
      ENDCASE;
     RETURN[z # NIL]};

--  allocation procedures, e.g. to be put in ZONE objects
  NewPrefixedObject: PROC[self: PrefixedZone, size: CARDINAL, type: Type] RETURNS[REF ANY];

  CreatePrefixedTypedUObject: PROC[type: Type, size: CARDINAL, zn: PPrefixedZone]
                 RETURNS[ptr: Pointer];

  CreatePrefixedUObject: PROC[size: CARDINAL, zn: PPrefixedZone]
                 RETURNS[ptr: Pointer];

  FreePrefixedObject: PROC[self: PrefixedZone, object: REF ANY];

  NewQuantizedObject: PROC[self: QuantizedZone, size: CARDINAL, type: Type]
                 RETURNS[REF ANY];

  FreeQuantizedObject: PROC[self: QuantizedZone, object: REF ANY];

  NewPrefixedHeapObject: PROC[self: LONG POINTER TO PPrefixedZone, size: CARDINAL]
                 RETURNS[LONG POINTER];

  FreePrefixedHeapObject: PROC[self: LONG POINTER TO PPrefixedZone, object: LONG POINTER];

  NewQuantizedHeapObject: PROC[self: LONG POINTER TO PQuantizedZone, size: CARDINAL]
                 RETURNS[LONG POINTER];

  FreeQuantizedHeapObject: PROC[self: LONG POINTER TO PQuantizedZone,
                                         object: LONG POINTER];

--  Software support for the microcode, or for use instead of microcode
  CreateQuantizedObjectTrap: PROC[zn: PQuantizedZone, size: CARDINAL, type: Type]
                 RETURNS[ptr: Pointer];

  CreatePrefixedObjectTrap: PROC[zn: PPrefixedZone, size: CARDINAL, type: Type]
                 RETURNS[ptr: Pointer];

  FreeObjectTrap: PROC[ptr: Pointer];

  FreeQuantizedNodeTrap: PROC[ptr: Pointer, zn: PZone];

  FreePrefixedNodeTrap: PROC[ptr: Pointer, zn: PZone];

  AllocateQuantizedNodeSDTrap: PROC[zn: PQuantizedZone, size: CARDINAL, type: Type]
                 RETURNS[ptr: Pointer];

  AllocateHeapNodeSDTrap: PROC[zn: PPrefixedZone, size: CARDINAL, type: Type]
                 RETURNS[ptr: Pointer];

    -- NOTE this is not an ENTRY proc!! It is meant to be called while debugging
  SummarizeZone: PROC[zn: PZone]
      RETURNS[nQuanta, freeQuanta, objectsInService, overheadCells, freeObjects,
                  cellsInService, freeCells, residueCells: LONG CARDINAL];
 
END.