-- RTStartImpl.Mesa
-- last edited 27-Oct-81 10:16:05 by Haugeland
-- last edited September 9, 1982 10:39 am by Paul Rovner

DIRECTORY
CPSwapDefs USING[ProcessState],
DeviceCleanup USING[Item, Reason, Await],
Frame USING[MyLocalFrame, Free, GetReturnFrame, Alloc],
Inline USING[COPY],
MiscAlpha USING[alpha],
PrincOps USING[StateVector, ControlLink, Frame, NullLink, FrameSizeIndex, FrameHandle],
Process USING[priorityForeground, Yield],
Processes,
ProcessOperations USING[ReadPSB, Enter, Wait, ReEnter, Broadcast, Exit, Notify, HandleToIndex],
PSB USING[PsbIndex, PsbHandle, ConditionVariable, PsbNull, ProcessStateBlock, NoTimeout],
RTBasic USING[Pointer],
RTMicrocode USING[aRECLAIMEDREF, aALTERCOUNT, opASSIGNREF, aGetReferentType,
opASSIGNREFNEW, aRTMOVESTATUS, RTMOVESTATUS, aAllocateQuantizedNode,
aAllocateHeapNode, aGetCanonicalReferentType, aFreeObject,
aFreeQuantizedNode, aFreePrefixedNode],
RTOS USING[UnregisterCedarProcess, RegisterCedarProcess],
RTRefCounts USING[ClobberedOverflowTable, ReadUCodeRegsTrap, useUCodedGC,
   ReclaimedRefOutOfOverflowTable, RTMOVESTATUSTrap, AssignRefTrap,
   AssignRefNewTrap, CREATEOutOfOverflowTable, ALTERCOUNTOutOfOverflowTable,
   aCREATEREF],
RTSD USING[SD, sAllocateQuantizedNodeTrap, sAllocateHeapNodeTrap,
sGetCanonicalReferentType, sCheckForNarrowRefFault, sAssignRefTrap,
sAssignRefNewTrap, sClobberedOverflowTable, sFork, sRaiseNarrowFault,
  sProcCheck, sAssignComposite, sAssignCompositeNew],
RTStart USING[],
RTStorageOps USING[AssignComposite, AssignCompositeNew,
   AssignRef, AssignRefNew],
RTTypesBasicPrivate USING[CheckForNarrowRefFault, GetCanonicalReferentTypeTrap, GetReferentTypeTrap, GetCanonicalReferentTypeSDTrap],
RTZones USING[AllocateQuantizedNodeSDTrap, AllocateHeapNodeSDTrap, FreeObjectTrap, CreateQuantizedObjectTrap, CreatePrefixedObjectTrap, FreeQuantizedNodeTrap, FreePrefixedNodeTrap],
Runtime USING[GlobalFrame],
RuntimeInternal USING[FrameSize],
SafeStorage USING[NarrowFault],
SpecialSpace USING[MakeCodeResident, MakeGlobalFrameResident],
TrapSupport USING[opTrapTable];

RTStartImpl: PROGRAM
IMPORTS DeviceCleanup, Frame, Inline, Process, ProcessOperations, RTMicrocode, RTOS, RTRefCounts, RTStorageOps,
SafeStorage, RTTypesBasicPrivate, RTZones, Runtime, RuntimeInternal,
  SpecialSpace
EXPORTS RTOS--End--, RTRefCounts, RTStart, SafeStorage
SHARES Processes
= BEGIN OPEN TrapSupport, RTMicrocode, RTRefCounts, RTSD, RTStorageOps;

-- PUBLIC SIGNALS --
UnsafeProcAssignment: PUBLIC SAFE SIGNAL[proc: PROC ANY RETURNS ANY] = CODE;

-- PUBLIC PROCEDURES --

StartRT: PUBLIC PROC = {NULL}; -- the start trap does it all

--//////////////

-- Cleanup stuff. Here 'cause its code must be pinned.
-- *****************************
InitializeCleanup: PUBLIC PROC[gcStateBank: CARDINAL] =
{ DO
item: DeviceCleanup.Item;
reason: DeviceCleanup.Reason ← DeviceCleanup.Await[@item];
SELECT reason FROM
turnOff, kill =>
IF useUCodedGC
THEN [] ← RTMicrocode.RTMOVESTATUS[toMemory, 0--ignored--];
turnOn => IF useUCodedGC
THEN [] ← RTMicrocode.RTMOVESTATUS[toUCodeRegisters, gcStateBank];
ENDCASE;
ENDLOOP};

-- *****************************

--//////////////
-- procedures for maintaining a registry of Cedar processes

--PrincOps.--FsiFrame: TYPE = MACHINE DEPENDENT RECORD [
fsi (0): PrincOps.FrameSizeIndex, -- must be at 3 MOD 4 boundary.
frame (1): local PrincOps.Frame];

ProcessState: TYPE = CPSwapDefs.ProcessState;

pm: POINTER TO FRAME[Processes] = LOOPHOLE[Runtime.GlobalFrame[Process.Yield]];

ProcState: -- for access to ProcessState field.
PROC[psbh: PSB.PsbHandle] RETURNS [ProcessState] =
INLINE {RETURN[LOOPHOLE[pm.pda[psbh].flags.available, ProcessState]]};

End: PUBLIC --"ENTRY"-- PROCEDURE =
-- When the top context of a process "returns", it Xfers to
-- this procedure with its results on the stack.
BEGIN OPEN PSB, ProcessOperations;
sv: RECORD [filler: UNSPECIFIED, results: PrincOps.StateVector];
DyingFrameHandle: TYPE = POINTER TO dying PrincOps.Frame;
frame: DyingFrameHandle;
h: PsbHandle;
sv.results ← STATE; -- save stack containing returned results.
WHILE ~ProcessOperations.Enter[@pm.processLock] DO NULL ENDLOOP;
frame ← LOOPHOLE[Frame.MyLocalFrame[]];
frame.state ← alive;
h ← ProcessOperations.ReadPSB[];
--***-- RTOS.UnregisterCedarProcess[HandleToIndex[h]];
LOOPHOLE[pm.pda[h].flags.available, ProcessState].state ← frameReady;
pm.pda[h].flags.abort ← FALSE; -- too late for Aborts: they no-op
Broadcast[@pm.frameReady]; -- wake any parent process waiting to Join.
-- Wait till this process is Detached or Joined:
UNTIL ProcState[h].state = frameTaken
OR ProcState[h].detached DO
Wait[@pm.processLock, @pm.frameTaken,
LOOPHOLE[pm.frameTaken, ConditionVariable].timeout];
WHILE ~ReEnter[@pm.processLock, @pm.frameTaken] DO NULL ENDLOOP;
ENDLOOP;
-- Free any frame left over from a previous dead detached process:
IF pm.deadFrame ~= NIL THEN {Frame.Free[pm.deadFrame]; pm.deadFrame ← NIL};
IF ProcState[h].detached THEN
pm.deadFrame ← frame; -- If detached, leave our frame for freeing.
frame.state ← dead; -- tell Joiner that we're done.
LOOPHOLE[pm.pda[h].flags.available, pm.ProcessState].state ← dead;
Broadcast[@pm.dead]; -- tell parent our frame has been left for freeing.
Wait[@pm.processLock, @pm.rebirth,
LOOPHOLE[pm.rebirth, ConditionVariable].timeout];
-- This process is dead. Its PSB sits in the rebirth queue until
-- it is recycled into a new process by Fork.
-- Our current frame however, has one of two fates:
-- (a) If this process was detached, the frame will simply be freed
-- by the next process that finishes ("deadFrame").
-- (b) if this process is being Joined, the parent process will
-- have acquired a pointer to our frame. The JOIN code will Xfer
-- to our frame and the code below will be executed
-- BY THE PARENT PROCESS. The parent process therefore
-- MUST BE RUNNING IN THE SAME MDS as the child process!
WHILE ~ReEnter[@pm.processLock, @pm.rebirth] DO NULL ENDLOOP;
sv.results.dest ← LOOPHOLE[frame.returnlink, PrincOps.ControlLink]; -- (frame.returnlink was set by Join[] to point to the context of JOIN.)
sv.results.source ← PrincOps.NullLink;
Exit[@pm.processLock];
-- Reload returned results into stack, return to parent:
RETURN WITH sv.results;
END;

CedarForker: --"ENTRY"-- PROCEDURE [--argsForRoot,-- root: PrincOps.ControlLink]
RETURNS [childPsb: PSB.PsbIndex] =
BEGIN OPEN PSB, ProcessOperations;
PForkFrame: TYPE = POINTER TO FRAME[CedarForker];

ChildBuilder: PROC [--parent's locals--] RETURNS [--MUST BE NULL!--] =
BEGIN
pChild: LONG POINTER TO ProcessStateBlock ← @pm.pda.block[childPsb];
parentFrame: PForkFrame = LOOPHOLE[Frame.GetReturnFrame[]];
fsi: PrincOps.FrameSizeIndex = LOOPHOLE[
(parentFrame-1), POINTER TO FsiFrame].fsi;
childFrame: PForkFrame = Frame.Alloc[fsi];
Inline.COPY[from: parentFrame-1, to: childFrame-1,
nwords: RuntimeInternal.FrameSize[fsi] + SIZE[PrincOps.FrameSizeIndex] ];
--***-- LOOPHOLE[childFrame, PrincOps.FrameHandle].returnlink ← LOOPHOLE[End];
childFrame.identity ← child;
pChild.link.failed ← FALSE;
pChild.link.priority ← pm.pda[ReadPSB[]].link.priority;
--***-- IF pChild.link.priority > Process.priorityForeground THEN ERROR;
pChild.flags ←
[available: LOOPHOLE[pm.ProcessState[state: alive, detached: FALSE]],
cleanup: PsbNull, waiting: FALSE, abort: FALSE];
pChild.context ← [frame[LOOPHOLE[childFrame, PrincOps.FrameHandle]]];
-- TEMP until microcode uses timeout vector:
-- pChild.mds ← Inline.HighHalf[LONG[LOOPHOLE[1, POINTER]]];
pChild.mds ← NoTimeout;
pm.pda[pm.pda.timeout][childPsb] ← NoTimeout;
--***-- RTOS.RegisterCedarProcess[childPsb];
Notify[@pm.rebirth]; -- starts the new process executing. Its PC
-- is set to begin execution at the instruction after the call
-- to ChildBuilder. Its stack is empty. Therefore,
-- ChildBuilder MUST NOT RETURN ANY RESULTS!
END;

identity: {parent, child};
argsForChild: PrincOps.StateVector;
-- ("root" is automatically popped off the stack first.)
argsForChild ← STATE; -- must be first!
identity ← parent;
WHILE ~Enter[@pm.processLock] DO NULL ENDLOOP;
WHILE LOOPHOLE[pm.rebirth, ConditionVariable].condition.tail = PsbNull
DO Exit[@pm.processLock];
Process.Yield[];
WHILE ~Enter[@pm.processLock] DO NULL ENDLOOP;
ENDLOOP;
childPsb ← pm.pda.block[LOOPHOLE[pm.rebirth, ConditionVariable].condition.tail]
.link.next; -- walk to tail, then to head.
[] ← ChildBuilder[--my local vars--];
-- Both parent and child processes will execute the following code:
SELECT identity FROM
parent =>
{ Exit[@pm.processLock];
RETURN[childPsb] }; -- return child handle to FORKing parent.
child =>
BEGIN
-- To simulate a procedure call, we must also store dest and
-- source links *above* the stack since ReturnWithState doesn't.
-- (stack pointer is *not* incremented.)
argsForChild.stk[argsForChild.stkptr] ← root;
argsForChild.dest ← root;
-- Set child's top context to call End when it returns:
argsForChild.stk[argsForChild.stkptr+1] ← End;
argsForChild.source ← LOOPHOLE[End, PrincOps.ControlLink];
RETURN WITH argsForChild; -- "call" root procedure of child.
END;
ENDCASE;
END;

-- procedures for maintaining a registry of Cedar processes (end)
--//////////////

-- procedures for supporting the compiler
--//////////////
RaiseNarrowFault: PROC =
{ state: PrincOps.StateVector;
kludge: LONG UNSPECIFIED;
state ← STATE; -- incantation to clear the stack
IF FALSE THEN kludge ← 0;
ERROR SafeStorage.NarrowFault};

ProcCheck: PROC[proc: PROC ANY RETURNS ANY] RETURNS[PROC ANY RETURNS ANY] = {
p: PrincOps.ControlLink = LOOPHOLE[proc];
IF p # PrincOps.NullLink AND ~p.tag THEN SIGNAL UnsafeProcAssignment[proc];
RETURN[proc]};

-- procedures for supporting the compiler (end)
--//////////////

-- *****************************
aREADREGS: MiscAlpha.alpha = 66B; -- temp for Dorado debugging


-- START HERE

opTrapTable.misc[aRECLAIMEDREF] ← LOOPHOLE[ReclaimedRefOutOfOverflowTable];
opTrapTable.misc[aRTMOVESTATUS] ← LOOPHOLE[RTMOVESTATUSTrap];
opTrapTable.main[opASSIGNREF] ← LOOPHOLE[AssignRefTrap];
opTrapTable.main[opASSIGNREFNEW] ← LOOPHOLE[AssignRefNewTrap];
opTrapTable.misc[aCREATEREF] ← LOOPHOLE[CREATEOutOfOverflowTable];
opTrapTable.misc[aALTERCOUNT] ← LOOPHOLE[ALTERCOUNTOutOfOverflowTable];
opTrapTable.misc[aAllocateQuantizedNode] ← LOOPHOLE[RTZones.CreateQuantizedObjectTrap];
opTrapTable.misc[aAllocateHeapNode] ← LOOPHOLE[RTZones.CreatePrefixedObjectTrap];
opTrapTable.misc[aFreeObject] ← LOOPHOLE[RTZones.FreeObjectTrap];
opTrapTable.misc[aFreeQuantizedNode] ← LOOPHOLE[RTZones.FreeQuantizedNodeTrap];
opTrapTable.misc[aFreePrefixedNode] ← LOOPHOLE[RTZones.FreePrefixedNodeTrap];
opTrapTable.misc[aGetCanonicalReferentType] ← LOOPHOLE[RTTypesBasicPrivate.GetCanonicalReferentTypeTrap];
opTrapTable.misc[aGetReferentType] ← LOOPHOLE[RTTypesBasicPrivate.GetReferentTypeTrap];
opTrapTable.misc[aREADREGS]← LOOPHOLE[ReadUCodeRegsTrap];

IF SD[sFork] # 0 THEN ERROR;
SD[sFork] ← LOOPHOLE[CedarForker, CARDINAL];

IF SD[sRaiseNarrowFault] # 0 THEN ERROR;
SD[sRaiseNarrowFault] ← LOOPHOLE[RaiseNarrowFault, CARDINAL];

IF SD[sProcCheck] # 0 THEN ERROR;
SD[sProcCheck] ← LOOPHOLE[ProcCheck, CARDINAL];

IF SD[sAssignRefTrap] # 0 THEN ERROR;
SD[sAssignRefTrap] ← LOOPHOLE[AssignRef, CARDINAL];

IF SD[sAssignRefNewTrap] # 0 THEN ERROR;
SD[sAssignRefNewTrap] ← LOOPHOLE[AssignRefNew, CARDINAL];

IF SD[sClobberedOverflowTable] # 0 THEN ERROR;
SD[sClobberedOverflowTable] ← LOOPHOLE[ClobberedOverflowTable, CARDINAL];

IF SD[sAssignComposite] # 0 THEN ERROR;
SD[sAssignComposite] ← LOOPHOLE[AssignComposite, CARDINAL];

IF SD[sAssignCompositeNew] # 0 THEN ERROR;
SD[sAssignCompositeNew] ← LOOPHOLE[AssignCompositeNew, CARDINAL];

IF SD[sCheckForNarrowRefFault] # 0 THEN ERROR;
SD[sCheckForNarrowRefFault] ← LOOPHOLE[RTTypesBasicPrivate.CheckForNarrowRefFault, CARDINAL];

IF SD[sGetCanonicalReferentType] # 0 THEN ERROR;
SD[sGetCanonicalReferentType] ← LOOPHOLE[RTTypesBasicPrivate.GetCanonicalReferentTypeSDTrap, CARDINAL];

IF SD[sAllocateQuantizedNodeTrap] # 0 THEN ERROR;
SD[sAllocateQuantizedNodeTrap] ← LOOPHOLE[RTZones.AllocateQuantizedNodeSDTrap, CARDINAL];

IF SD[sAllocateHeapNodeTrap] # 0 THEN ERROR;
SD[sAllocateHeapNodeTrap] ← LOOPHOLE[RTZones.AllocateHeapNodeSDTrap, CARDINAL];

SpecialSpace.MakeCodeResident[Runtime.GlobalFrame[InitializeCleanup]];
SpecialSpace.MakeGlobalFrameResident[RTStartImpl];

END.