-- ConnectorImpl.mesa
-- last edited  by Suzuki:  5-Jan-82 15:24:38 

DIRECTORY
	Connector,
	MOSSIMFunc,
	Process,
	Rope,
	RandomCard,
	SimTsim,
	Storage,
	TTY;

ConnectorImpl: MONITOR
	IMPORTS RandomCard, SimTsim, Storage, Process, TTY
	EXPORTS Connector, MOSSIMFunc = {

Node: PUBLIC TYPE = RECORD [
  value: REF ANY,
  cond: CONDITION,
  waitingArrayLength: CARDINAL ← 0,
  waiting: POINTER TO ARRAY OF LONG POINTER TO CONDITION ← NIL,
  port: REF Port,
  max: CARDINAL ← 0,
  body: SEQUENCE size: [0..LAST[INTEGER]] OF RECORD [
        choice: PrivateChoice,
	response: CARDINAL,
	direction: {Up, Down, Change}]
  ];
PrivateHandle: TYPE = REF Node;
Port: TYPE = RECORD [
  body: SELECT size: {Single, Multiple} FROM
    Single => [handle: SimTsim.nptr],
    Multiple => [handle: REF PortSequence],
    ENDCASE];
PortSequence: TYPE = RECORD[SEQUENCE size: [0..LAST[INTEGER]] OF SimTsim.nptr];
    
ChoiceNode: PUBLIC TYPE = RECORD [
  response: CARDINAL,
  cond: CONDITION];
PrivateChoice: TYPE = REF ChoiceNode;

TooManyWaiting: SIGNAL = CODE;

OutOfBounds: ERROR = CODE;

Assign: PUBLIC PROC [cond: LONG POINTER TO CONDITION, h:PrivateHandle] 
    RETURNS [CARDINAL] = {
  IF h.waiting=NIL THEN {
    h.waiting ← Storage.Node[20];
    h.waitingArrayLength ← 10;
    FOR i: NAT IN [0..10) DO
      h.waiting[i] ← NIL ENDLOOP};
  FOR i: NAT IN [0..10) DO
    IF h.waiting[i]=NIL THEN {h.waiting[i] ← cond; RETURN[i]};
    REPEAT
      FINISHED => SIGNAL TooManyWaiting
    ENDLOOP;
  RETURN[0]};

CreateChoice: PUBLIC PROC RETURNS [PrivateChoice] = {
	choice: PrivateChoice ← NEW[ChoiceNode];
	Process.DisableTimeout[@choice.cond];
	RETURN[choice]};

RegisterUp: PUBLIC PROC [choice: PrivateChoice, retValue: CARDINAL, port: PrivateHandle] = {
	port.body[port.max] ← [choice, retValue, Up];
	port.max ← port.max+1};

Remove: PUBLIC PROC [loc: CARDINAL, h: PrivateHandle] = {
  IF loc>=h.waitingArrayLength THEN ERROR OutOfBounds;
  h.waiting[loc] ← NIL};

GetChoice: PUBLIC PROC [choice: PrivateChoice] RETURNS [CARDINAL] = {
	Wait[@choice.cond];
	RETURN[choice.response]};

Create: PUBLIC PROC [size: CARDINAL] RETURNS [ret: PrivateHandle] = {
	ret ← NEW[Node[size]];
	Process.DisableTimeout[@ret.cond]};

Put: PUBLIC PROC [n: PrivateHandle, v: REF ANY] = {
--	WF.WF1["Put S:%u ", v];
	CheckAbort[];
	StuffIt[n, v];
	WakeUp[n];
--	StandardDelay[];
	Process.Yield[];
--	WF.WF0["Put F "]
	};

Stuff: PROC [n: PrivateHandle, v: REF ANY] = {
--	WF.WF1["  Stuff S:%b ", v];
	StuffIt[n, v];
	WakeUp[n];
	Process.Yield[];
--	WF.WF0["  Stuff F "]
	};

StuffIt: ENTRY PROC [n: PrivateHandle, v: REF ANY] = {
	n.value ← v};

Place: PUBLIC PROC [n: PrivateHandle, v: REF ANY] = {
	CheckAbort[];
	StuffIt[n, v];
	};

Get: PUBLIC PROC [n: PrivateHandle] RETURNS [ret: REF ANY] = {
--	WF.WF0["    Get S "];
	CheckAbort[];
	ret ← GrabIt[n];
	UNTIL ret#NIL DO
	  StandardDelay[];
          ret ← GrabIt[n];
	  ENDLOOP;
	Process.Yield[]
--	StandardDelay[];
--	WF.WF0["    Get F "]
	};

Grab: PUBLIC ENTRY PROC [n: PrivateHandle] RETURNS [ret: REF ANY] = {
--	WF.WF0["      Grab S"];
	CheckAbort[];
	ret ← n.value;
--	WF.WF0["      Grab F "]
	};

GrabIt: ENTRY PROC [n: PrivateHandle] RETURNS[REF ANY] = {
	RETURN[n.value]};

GetNew: PUBLIC PROC [n: PrivateHandle, v: BOOLEAN] = {
--	WF.WF1["        GetNew S:%b ", v];
	CheckAbort[];
	StandardDelay[];
	GetNewBody[n, v];
--	WF.WF0["        GetNew F"]
	};

GetNewChange: PUBLIC PROC [n: PrivateHandle] = {
	CheckAbort[];
	Wait[@n.cond]
	};
	
GetNewBody: ENTRY PROC[n: PrivateHandle, v: BOOLEAN] = {
	DO
		WaitInternal[@n.cond];
		IF v=NARROW[n.value, REF BOOLEAN]↑ THEN EXIT;
	ENDLOOP;
	};

WakeUp: ENTRY PROC [h: PrivateHandle] = {
	BROADCAST h.cond;
	FOR i: NAT IN [0..h.waitingArrayLength) DO
	  IF h.waiting[i]#NIL THEN BROADCAST h.waiting[i]↑;
	  ENDLOOP;
	FOR i: NAT IN [0..h.max) DO
	  IF (SELECT h.body[i].direction FROM
	    Change => TRUE,
	    Up => NARROW[h.value, REF BOOLEAN]↑,
	    Down => NOT NARROW[h.value, REF BOOLEAN]↑,
	    ENDCASE => FALSE) THEN {
	      h.body[i].choice.response ← h.body[i].response;
	      BROADCAST h.body[i].choice.cond};
	  ENDLOOP;
	};


MOSSIMGet: PUBLIC PROC [node: PrivateHandle] = {
    n: SimTsim.nptr;
    CheckAbort[];
    WITH s: node.port SELECT FROM
      Single => {
        n ← s.handle;
        IF SimTsim.pchars[n.npot]='X THEN 
          SimTsim.error3String["Undefined value: ", "%s*n", n.nname]
        ELSE {
          val: REF BOOLEAN ← NEW[BOOLEAN ← SimTsim.boolvalue[n.npot]];
	  Stuff[node, val]}};
      Multiple => IF s.handle.size<=16 THEN {
          val: REF CARDINAL ← NEW[CARDINAL ← CARDPort[node]];
	  Stuff[node, val]}
        ELSE IF s.handle.size<=32 THEN {
          val: REF LONG CARDINAL ← NEW[LONG CARDINAL ← LongCARDPort[node]];
          Stuff[node, val]}
        ELSE SimTsim.error3String["Unimplemented port type for port: ", 
          "%s*n", n.nname];
      ENDCASE
    };

  MOSSIMSet: PUBLIC PROC [node: PrivateHandle] = {
  -- Assigns value from Sakura to MOSSIM through input ports
    CheckAbort[];
    IF node.value=NIL THEN { -- make nodes undefined
      WITH p: node.port SELECT FROM
        Single => SimTsim.setin[p.handle, 'x];
	Multiple => FOR i: NAT IN [0..p.handle.size) DO
	  SimTsim.setin[p.handle[i], 'x] ENDLOOP;
	ENDCASE;
      RETURN};
    WITH node.value SELECT FROM
      n: REF BOOLEAN => 
        WITH p: node.port SELECT FROM
	  Single => SimTsim.setin[p.handle, IF n↑ THEN 'h ELSE 'l];
	  ENDCASE => ERROR;
      n: REF CARDINAL =>
        WITH p: node.port SELECT FROM
	  Multiple => IF p.handle.size<=16 THEN {
	    val: CARDINAL ← n↑;
	    FOR i: NAT IN [p.handle.size..16) DO val ← val*2 ENDLOOP;
	    FOR i: NAT IN [0..p.handle.size) DO
	      SimTsim.setin[p.handle[i], IF val>=100000B THEN 'h ELSE 'l];
	      val ← val*2;
	      ENDLOOP}
	    ELSE ERROR;
	  ENDCASE => ERROR;
      n: REF LONG CARDINAL =>
        WITH p: node.port SELECT FROM
	  Multiple => IF p.handle.size<=32 THEN {
	    val: LONG CARDINAL ← n↑;
	    FOR i: NAT IN [p.handle.size..32) DO val ← val*2 ENDLOOP;
	    FOR i: NAT IN [0..32) DO
	      SimTsim.setin[p.handle[i], IF val>=20000000000B THEN 'h ELSE 'l];
	      val ← val*2;
	      ENDLOOP}
	    ELSE ERROR;
	  ENDCASE => ERROR;
      ENDCASE => ERROR};

  MOSSIMConnect: PUBLIC PROC [node: PrivateHandle, name: STRING] = {
    n: SimTsim.nptr ← SimTsim.ufind[name];
    CheckAbort[];
    IF n=NIL THEN 
      SimTsim.error3String["Cannot find the name: ", "%s*n", name]
    ELSE {
      node.port ← NEW[Single Port];
      node.port↑ ← [Single[n]]}};
    
  MOSSIMMultiConnectInit, CharMultiConnectInit: PUBLIC PROC [node: PrivateHandle, size: CARDINAL] = {
    node.port ← NEW[Multiple Port];
    node.port↑ ← AllocatePorts[size]}; 
    
  AllocatePorts: PROC [size: CARDINAL] 
      RETURNS [h: Multiple Port] = {
    h.handle ← NEW[PortSequence[size]]};
      
  MOSSIMMultiConnectAssign, CharMultiConnectAssign: 
    PUBLIC PROC [node: PrivateHandle, name: STRING,  
      loc: CARDINAL] = {
    n: SimTsim.nptr ← SimTsim.ufind[name];
    CheckAbort[];
    IF n=NIL THEN 
      SimTsim.error3String["Cannot find the name: ", "%s*n", name]
    ELSE WITH h: node.port SELECT FROM
      Single => ERROR;
      Multiple => h.handle[loc] ← n;
      ENDCASE => ERROR};
      
 CARDPort: PROC [node: PrivateHandle] RETURNS [ret: CARDINAL] = {
   WITH n: node.port SELECT FROM
     Single => ERROR;
     Multiple => 
       SELECT n.handle.size FROM
         <= 16 => {
           ret ← 0;
	   FOR i: CARDINAL IN [0..n.handle.size) DO
	     SELECT SimTsim.pchars[n.handle[i].npot] FROM
	       '1  => ret ← 2*ret+1;
	       '0 => ret ← 2*ret;
	       ENDCASE => SimTsim.error3String[
	         "This node returns undefined value: ", "%s*n", 
		 n.handle[i].nname];
	     ENDLOOP};
	 ENDCASE => ERROR;
     ENDCASE};
   
 LongCARDPort: PROC [node: PrivateHandle] RETURNS [ret: LONG CARDINAL] = {
   WITH n: node.port SELECT FROM
     Single => ERROR;
     Multiple => 
       SELECT n.handle.size FROM
         <= 32 => {
           ret ← 0;
	   FOR i: CARDINAL IN [0..n.handle.size) DO
	     SELECT SimTsim.pchars[n.handle[i].npot] FROM
	       '1  => ret ← 2*ret+1;
	       '0 => ret ← 2*ret;
	       ENDCASE => SimTsim.error3String[
	         "This node returns undefined value: ", "%s*n", 
		 n.handle[i].nname];
	     ENDLOOP};
	 ENDCASE => ERROR;
     ENDCASE};
   
  CharGet: PUBLIC PROC [node: PrivateHandle] = {
    n: SimTsim.nptr;
    ArrayPort: TYPE = RECORD[SEQUENCE size: [0..LAST[INTEGER]] OF CHARACTER];
    CharPorts: PROC [handle: REF PortSequence, size: CARDINAL] 
        RETURNS [val: REF ArrayPort] = {
      val ← NEW [ArrayPort[size]];
      FOR i: NAT IN [0..size) DO
        val[i] ← SimTsim.pchars[handle[i].npot];
        ENDLOOP;
      }; -- CharPorts
    CheckAbort[];
    WITH s: node.port SELECT FROM
      Single => {
        val: REF CHARACTER ← NEW[CHARACTER ← SimTsim.pchars[s.handle.npot]];
	Stuff[node, val]};
      Multiple => IF s.handle.size=16 THEN Stuff[node, CharPorts[s.handle, 16]]
        ELSE IF s.handle.size=32 THEN Stuff[node, CharPorts[s.handle, 32]]
        ELSE SimTsim.error3String["Unimplemented port type for port: ", 
          "%s*n", n.nname];
      ENDCASE
    };
  
  Notify: PUBLIC PROC [node: PrivateHandle] = {
    val: REF BOOLEAN;
    CheckAbort[];
    IF node=NIL THEN ERROR;
    val ← NARROW[GrabIt[node], REF BOOLEAN];
    IF val=NIL THEN
      val ← NEW[BOOLEAN]
    ELSE IF val↑ THEN
      {val↑ ← FALSE;
      StuffIt[node, val]};
    val↑ ← TRUE;
    Put[node, val]};
  

-- Scheduler

currentProcessSize: CARDINAL;

Pool: TYPE = RECORD [
	cond: CONDITION, free: POINTER TO Pool];
MaxCond: CARDINAL = 100;
ConditionPool: ARRAY [0..MaxCond) OF Pool;
FreePool: POINTER TO Pool;
UniversalTime: LONG CARDINAL;

TimeQueue: TYPE = RECORD [
	next: REF TimeQueue,
	time: LONG CARDINAL,
	size: CARDINAL,
	events: POINTER TO Pool];
AllTime: REF TimeQueue ← NIL;

Fork: PUBLIC PROC[newProcess: PROC] RETURNS[PROCESS] = {
	p: PROCESS;
	CheckAbort[];
	IncCurrentEntry[];
	p ← FORK newProcess[];
	Process.Yield[];
	RETURN[p]
	};

Join: PUBLIC PROC[process: PROCESS] = {
	[] ← JOIN process;
	};

NotifyAll: PUBLIC PROC[p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, 
  p14, p15: PrivateHandle] = {
  NotifyInternal[p1];
  NotifyInternal[p2];
  NotifyInternal[p3];
  NotifyInternal[p4];
  NotifyInternal[p5];
  NotifyInternal[p6];
  NotifyInternal[p7];
  NotifyInternal[p8];
  NotifyInternal[p9];
  NotifyInternal[p10];
  NotifyInternal[p11];
  NotifyInternal[p12];
  NotifyInternal[p13];
  NotifyInternal[p14];
  NotifyInternal[p15]};

NotifyInternal: ENTRY PROC [p: PrivateHandle] = {
  IF p=NIL THEN RETURN;
  NOTIFY p.cond;
  FOR i: NAT IN [0..p.waitingArrayLength) DO
    IF p.waiting[i]#NIL THEN NOTIFY p.waiting[i]↑
    ENDLOOP;
  FOR i: NAT IN [0..p.size) DO
    IF p.body[i].choice#NIL THEN NOTIFY p.body[i].choice.cond;
    ENDLOOP};
    
ProcessEnd: PUBLIC ENTRY PROC = {
	DecCurrentInternal[];
	IF AllTime = NIL THEN RETURN;
	IF ~CurrentMore[] THEN NotifyEarliest[];
	};

GetProcessSize: PUBLIC ENTRY PROC RETURNS[CARDINAL] = {
	RETURN[currentProcessSize]};

Delay: PUBLIC PROC [max: CARDINAL] = {
	CheckAbort[];
	DelayInternal[max];
	Process.Yield[];
	};

RandomDelay: PUBLIC PROC [min, max: CARDINAL] = {
	CheckAbort[];
	DelayInternal[RandomCard.Choose[min, max]]
	};

DelayInternal: ENTRY PROC [max: CARDINAL] = {
	index: CARDINAL;
	startTime: LONG CARDINAL ← UniversalTime+max;
	DecCurrentInternal[];
	IF currentProcessSize#0 THEN {
		PutThisProcess[startTime];
		RETURN};
	IF startTime<Earliest[] THEN {
		currentProcessSize ← currentProcessSize+1;
		UniversalTime ← startTime; RETURN};
	IF startTime>Earliest[] THEN {
		NotifyEarliest[];
		PutThisProcess[startTime];
		RETURN};
	index ← RandomCard.Choose[0,EarliestSize[]];
	IF index=0 THEN {
		currentProcessSize ← currentProcessSize+1;
		UniversalTime ← startTime; RETURN};
	NotifyEarliestAt[index-1];
	PutThisProcess[startTime]
	};

StandardDelay: PUBLIC PROC = {
	CheckAbort[];
	DelayInternal[RandomCard.Choose[1, 2]];
	Process.Yield[];
	};

Wait: PUBLIC ENTRY PROC [c: LONG POINTER TO CONDITION] = {
	CheckAbort[];
	IF currentProcessSize = 0 THEN ERROR;
	currentProcessSize ← currentProcessSize-1;
	IF AllTime#NIL AND currentProcessSize = 0 THEN NotifyEarliest[];
	WAIT c↑;
	currentProcessSize ← currentProcessSize+1};

WaitInternal: INTERNAL PROC [c: LONG POINTER TO CONDITION] = {
	IF currentProcessSize = 0 THEN ERROR;
	currentProcessSize ← currentProcessSize-1;
	IF AllTime#NIL AND currentProcessSize = 0 THEN NotifyEarliest[];
	WAIT c↑;
	currentProcessSize ← currentProcessSize+1};
	
IncCurrent: PUBLIC PROC = {
	CheckAbort[];
	IncCurrentEntry[];
	Process.Yield[]};

-- Current process size manipulation

CurrentMore: INTERNAL PROC RETURNS[BOOLEAN] = {
	RETURN[currentProcessSize#0]
	};

DecCurrentInternal: INTERNAL PROC = {
	IF currentProcessSize = 0 THEN ERROR;
	currentProcessSize ← currentProcessSize-1;
	};

IncCurrentEntry: ENTRY PROC = {
	currentProcessSize ← currentProcessSize+1;
	};

-- Time queue manipulation

Earliest: INTERNAL PROC RETURNS[LONG CARDINAL] = {
	IF AllTime=NIL THEN RETURN[LAST[LONG CARDINAL]];
	RETURN[AllTime.time]};

EarliestSize: INTERNAL PROC RETURNS[CARDINAL] = {
	RETURN[AllTime.size]};

NotifyEarliest: INTERNAL PROC = {
	index: CARDINAL ← RandomCard.Choose[0,AllTime.size-1];
	NotifyEarliestAt[index]
	};

NotifyEarliestAt: INTERNAL PROC [index: CARDINAL] = {
	eventQueue: POINTER TO Pool ← AllTime.events;
	back: POINTER TO Pool;
	currentProcessSize ← currentProcessSize+1;
	UniversalTime ← AllTime.time;
	IF index=0 THEN {
		AllTime.events ← eventQueue.free;
		NOTIFY eventQueue.cond;
		eventQueue.free ← FreePool;
		FreePool ← eventQueue;
		IF AllTime.size=1 THEN {
			time: REF TimeQueue ← AllTime;
			AllTime ← AllTime.next}
		ELSE AllTime.size ← AllTime.size-1;
		RETURN};
	THROUGH [0..index-1) DO
		eventQueue ← eventQueue.free;
	ENDLOOP;
	back ← eventQueue;
	eventQueue ← eventQueue.free;
	NOTIFY eventQueue.cond;
	back.free ← eventQueue.free;
	eventQueue.free ← FreePool;
	FreePool ← eventQueue;
	AllTime.size ← AllTime.size-1;
	};

PutToQueue: PROC[newProcess: PROC] = {
	LockAndPutThisProcess[UniversalTime];
	newProcess[]
	};

LockAndPutThisProcess: ENTRY PROC[startTime: LONG CARDINAL] = {
	PutThisProcess[startTime]
	};

PutThisProcess: INTERNAL PROC[startTime: LONG CARDINAL] = {
	timeQueue: REF TimeQueue;
	{IF AllTime=NIL OR AllTime.time>startTime THEN GOTO PutFront;
	timeQueue ← AllTime;
	IF AllTime.time=startTime THEN GOTO PutHere;
	FOR timeQueue← AllTime, timeQueue.next UNTIL timeQueue.next=NIL DO
		IF timeQueue.time=startTime THEN GOTO PutHere;
		IF timeQueue.next.time>startTime THEN GOTO PutAfter
	REPEAT
		FINISHED => 
			IF timeQueue.time=startTime THEN GOTO PutHere
			ELSE GOTO PutAfter
	ENDLOOP;
	EXITS
		PutFront => {
			time: REF TimeQueue ← NEW[TimeQueue];
			event: POINTER TO Pool;
			time.next ← AllTime;
			AllTime ← time;
			time.time ← startTime;
			time.size ← 1;
			time.events ← FreePool;
			IF FreePool = NIL THEN ERROR;
			FreePool ← FreePool.free;
			time.events.free ← NIL;
			event ← time.events;
			WAIT event.cond};
		PutHere => {
			pool: POINTER TO Pool ← FreePool;
			IF FreePool=NIL THEN ERROR;
			FreePool ← FreePool.free;
			pool.free ← timeQueue.events;
			timeQueue.events ← pool;
			timeQueue.size ← timeQueue.size+1;
			WAIT pool.cond};
		PutAfter => {
			time: REF TimeQueue ← NEW[TimeQueue];
			event: POINTER TO Pool;
			time.next ← timeQueue.next;
			timeQueue.next ← time;
			time.time ← startTime;
			time.size ← 1;
			time.events ← FreePool;
			IF FreePool = NIL THEN ERROR;
			FreePool ← FreePool.free;
			time.events.free ← NIL;
			event ← time.events;
			WAIT event.cond};
	}};

-- Abort

CheckAbort: PROC = {
  IF TTY.UserAbort[] THEN ERROR ABORTED};
    
-- Main part

[] ← RandomCard.InitRandom[seed: -1];
FreePool ← @ConditionPool[0];
{i: CARDINAL;
FOR i IN [0..MaxCond) DO
	Process.DisableTimeout[@ConditionPool[i].cond];
ENDLOOP;
FOR i IN [0..MaxCond-1) DO
	ConditionPool[i].free ← @ConditionPool[i+1]
ENDLOOP};
ConditionPool[MaxCond-1].free ← NIL;
UniversalTime ← 0;
currentProcessSize ← 0;
}.