
{$K1} {$K2} {$K4} {$K7} {$K12} {$K13} {$K14} { symbol table space reduction }

module cpsinterpreter;


{$I global.inc }

var
   cc:	external integer;	{character counter}
   lc: external integer;	{program location counter}
   ll:	external integer;	{length of current line}
   ch: external char;
   errs: external set of er;
   errpos: external  integer;
   progname:external  alfa;
   skipflag: external boolean;
   constbegsys, typebegsys, blockbegsys, facbegsys, statbegsys: external symset;
   key:external  array
      [1.. nkw] of alfa;
   ksy:external  array
      [1.. nkw] of symbol;
   sps:external  array
      [char]	of symbol; 			{special aymbols}
   t, a, b, sx, c1, c2: external integer;	{indices to tables}
   stantyps: external  typset;
   display:external  array
      [0.. lmax] of integer;
   tab: external array
      [0.. tmax] of 			{identifier table}
   packed record
	      name: alfa;
	      link: index;
	      obj: object;
	      typ: types;
	      ref: index;
	      normal: boolean;
	      lev: 0.. lmax;
	      adr: integer;
	   end;
   atab: external  array
      [1.. amax] of		 {array-table}
   packed record
	      inxtyp, eltyp: types;
	      elref, low, high,	elsize,	size: index;
	   end;
   btab: external array
      [1..bmax] of 		{block-table}
   packed record
	      last, lastpar, psize, vsize: index
	   end;
   stab: external packed array
      [0.. smax] of char;		{string table}
   code: external array
      [0.. cmax] of order;		{ interpreter	declarations }
   ir:	order;				{instruction buffer}
   ps:					{processor status}
      (run, fin, divchk, inxchk, stkchk, linchk, lngchk, redchk,
	deadlock);
   lncnt,				{number of lines}
   chrcnt: integer;			{number of characters	in lines}
   h1,	h2, h3,	h4: integer;		{local variables}
   s: array
      [1.. stmax] of	integer;	{the stack}

{process table-one entry for each process}
   ptab:  array
      [ptype] of record
		       t, b,	 {top, bottom of stack}
		       pc,	 {program counter}
		       stacksize: integer;	{stack limit}
		       display:	array
			  [1..	lmax] of integer;
		       suspend:	integer;	{0 or	index of semaphore}
		       active: boolean 		{procedure active flag}
		    end;
   npr, 			{number of concurrent	processes}
   curpr: ptype;		{current process executing}
   stepcount:  integer;		{number of steps before switch}
   seed: real;			{random seed}
   pflag: boolean;		{concurrent call flag	}


function ran: real;

{ random number generator. output : 0 < ran < 1 .
  bowles,k. microcomputer problem solving using pascal, p. 257 }

   const
      mult = 27.182813;
      incr = 31.415917; 

   begin
      seed := seed * mult + incr;
      seed := seed - trunc(seed);
      ran := seed;
   end	{ran};

{functions to	convert	integers to booleans and converesely}

function itob(i: integer): boolean;

   begin
      if i = tru
      then
	  itob := true
      else
	  itob := false
   end	{itob};


function btoi(b: boolean): integer;

   begin
      if b
      then
	  btoi := tru
      else
	  btoi := fals
   end	{btoi};


procedure initialize;

var cpf : ptype;

   begin
      s[1] := 0;
      s[2] := 0;
      s[3] := - 1;
      s[4] := btab[1].last;
      with ptab[0] do
	  begin
	     b := 0;
	     suspend :=	0;
	     display[1] := 0;
	     t := btab[2].vsize - 1;
	     pc	:= tab[s[4]].adr;
	     active := true;
	     stacksize := stmax	- pmax * stkincr
	  end;
      for cpf   := 1 to pmax do
	  with ptab[cpf] do
	     begin
		active := false;
		display[1] :=	0;
		pc := 0;
		suspend	:= 0;
		b := ptab[cpf - 1].stacksize + 1;
		stacksize := b + stkincr - 1;
		t := b - 1
	     end;
      npr := 0;
      curpr :=	0;
      pflag :=	false;
      seed := 1.23456789; { seed for random number generator }
      stepcount := 0;
      ps := run;
      lncnt :=	0;
      chrcnt := 0;
   end	{initialize};

{ because of limitations of procedure length in pascal/mt the case statement of
  the interpreter has been split into four procedures.
}

procedure exec1;  
var hx : integer;
   begin
      with ptab[curpr] do
	  case ir.f of
	     0:
		begin {load address}
		   t :=	t + 1;
		   if t	> stacksize
		   then
		      ps := stkchk
		   else
		      s[t] :=	display[ir.x]	+ ir.y
		end;
	     1:
		begin {load value}
		   t :=	t + 1;
		   if t	> stacksize
		   then
		      ps := stkchk
		   else
		      s[t] :=	s[display[ir.x] + ir.y]
		end;
	     2:
		begin {load indirect}
		   t :=	t + 1;
		   if t	> stacksize
		   then
		      ps := stkchk
		   else
		      s[t] :=	s[s[display[ir.x] +	ir.y]]
		end;
	     3:
		begin {update display}
		   h1 := ir.y;
		   h2 := ir.x;
		   h3 := b;
		   repeat
		      display[h1] := h3;
		      h1 := h1 - 1;
		      h3 := s[h3 + 2]
		   until h1 = h2
		end;
	     4:	{cobegin}
		pflag := true;
	     5:	{coend}
		begin
		   pflag := false;
		   ptab[0].active := false
		end;
	     6:
		begin {wait}
		   h1 := s[t];
		   t :=	t - 1;
		   if s[h1] >	0
		   then
		      s[h1] := s[h1] - 1
		   else
		      begin
			 suspend := h1;
			 stepcount := 0
		      end
		end;
	     7:
		begin {signal}
		   h1 := s[t];
		   t :=	t - 1;
		   h2 := pmax +	1;
		   h3 := trunc(ran * h2);
		   while (h2 >=	0) and (ptab[h3].suspend <> h1) do
		      begin
			 h3 := (h3 + 1)	mod (pmax + 1);
			 h2 := h2 - 1
		      end;
		   if h2 < 0
		   then
		      s[h1] := s[h1] + 1
		   else
		      ptab[h3].suspend := 0
		end;
	     8:
		case ir.y of
		   17:
		      begin
			 t := t	+ 1;
			 if t >	stacksize
			 then
			    ps := stkchk
			 else
			    s[t] := btoi(eof(input))
		      end;
		   18:
		      begin
			 t := t	+ 1;
			 if t >	stacksize
			 then
			    ps := stkchk
			 else
			    s[t] := btoi(eoln(input))
		      end;
		end;
	     10:
		pc := ir.y;	{jump}
	     11:
		begin {conditional jump}
		   if s[t] = fals then
		      pc := ir.y;
		   t :=	t - 1
		end;
	     14:
		begin {for1up}
		   h1 := s[t -	1];
		   if h1 <= s[t]
		   then
		      s[s[t -	2]] := h1
		   else
		      begin
			 t := t	- 3;
			 pc := ir.y
		      end
		end;
	  end;
   end	{exec1};

procedure exec2;
var hx :integer;

   begin
      with ptab[curpr] do
	  case ir.f of
	     15:
		begin {for2up}
		   h2 := s[t -	2];
		   h1 := s[h2] + 1;
		   if h1 <= s[t]
		   then
		      begin
			 s[h2] := h1;
			 pc := ir.y
		      end
		   else
		      t	:= t - 3;
		end;
	     18:
		begin
		   h1 := btab[tab[ir.y].ref].vsize;
		   if t	+ h1 > stacksize
		   then
		      ps := stkchk
		   else
		      begin
			 t := t	+ 5;
			 s[t -	1] := h1 - 1;
			 s[t]	:= ir.y
		      end;
		end;
	     19:
		begin
		   active := true;
		   h1 := t - ir.y;
		   h2 := s[h1 + 4];	{h2 points to	tab}
		   h3 := tab[h2].lev;
		   display[h3 + 1] :=	h1;
		   h4 := s[h1 + 3] + h1;
		   s[h1 + 1] := pc;
		   s[h1 + 2] := display[h3];
		   if pflag
		   then
		      s[h1 + 3] := ptab[0].b
		   else
		      s[h1 + 3] := b;
		   for hx := t + 1 to h4 do
		      s[hx] := 0;
		   b :=	h1;
		   t :=	h4;
		   pc := tab[h2].adr
		end;
	     21:
		begin {index}
		   h1 := ir.y;	{h1 points to	atab}
		   h2 := atab[h1].low;
		   h3 := s[t];
		   if h3 < h2
		   then
		      ps := inxchk
		   else
		      if h3 > atab[h1].high
		      then
			 ps := inxchk
		      else
			 begin
			    t := t - 1;
			    s[t] := s[t] + (h3 - h2) * atab[h1].
			       elsize
			 end
		end;
	     22:
		begin {load block}
		   h1 := s[t];
		   t :=	t - 1;
		   h2 := ir.y +	t;
		   if h2 > stacksize
		   then
		      ps := stkchk
		   else
		      while t <	h2 do
			 begin
			    t := t + 1;
			    s[t] := s[h1];
			    h1 := h1 + 1
			 end
		end;
	     23:
		begin {copy block}
		   h1 := s[t -	1];
		   h2 := s[t];
		   h3 := h1 + ir.y;
		   while h1 < h3 do
		      begin
			 s[h1] := s[h2];
			 h1 := h1 + 1;
			 h2 := h2 + 1
		      end;
		   t :=	t - 2
		end;
	  end;
   end	{exec1};



procedure exec4;
var hx :integer;

   begin
      with ptab[curpr] do
	  case ir.f of
	     38:
		begin {store}
		   s[s[t - 1]] := s[t];
		   t :=	t - 2
		end;
	     45:
		begin
		   t :=	t - 1;
		   s[t] := btoi(s[t] = s[t+ 1])
		end;
	     46:
		begin
		   t :=	t - 1;
		   s[t] := btoi(s[t] <> s[t + 1])
		end;
	     47:
		begin
		   t :=	t - 1;
		   s[t] := btoi(s[t] < s[t	+ 1])
		end;
	     48:
		begin
		   t :=	t - 1;
		   s[t] := btoi(s[t] <= s[t + 1])
		end;
	     49:
		begin
		   t :=	t - 1;
		   s[t] := btoi(s[t] > s[t	+ 1])
		end;
	     50:
 		begin
		   t :=	t - 1;
		   s[t] := btoi(s[t] >= s[t + 1])
		end;
	     51:
		begin
		   t :=	t - 1;
		   s[t] := btoi(itob(s[t]) or itob(s[t + 1]))
		end;
	     52:
		begin
		   t :=	t - 1;
		   s[t] := s[t] + s[t + 1]
		end;
	     53:
		begin
		   t :=	t - 1;
		   s[t] := s[t] - s[t + 1]
		end;
	     56:
		begin
		   t :=	t - 1;
		   s[t] := btoi(itob(s[t]) and itob(s[t + 1]))
		end;
	     57:
		begin
		   t :=	t - 1;
		   s[t] := s[t] * s[t + 1]
		end;
	     58:
		begin
		   t :=	t - 1;
		   if s[t + 1] = 0
		   then
		      ps := divchk
		   else
		      s[t] :=	s[t] div s[t	+ 1]
		end;
	     59:
		begin
		   t :=	t - 1;
		   if s[t + 1] = 0
		   then
		      ps := divchk
		   else
		      s[t] :=	s[t] mod s[t	+ 1]
		end;
	     62:
		if eof(input)
		then
		   ps := redchk
		else
		   readln;
	     63:
		begin
		   writeln;
		   lncnt := lncnt + 1;
		   chrcnt := 0;
		   if lncnt > linelimit	then
		      ps := linchk
		end
	  end;
   end	{exec1};



procedure exec3;

   begin
      with ptab[curpr] do
	  case ir.f of
	     24:
		begin {literal}
		   t :=	t + 1;
		   if t	> stacksize
		   then
		      ps := stkchk
		   else
		      s[t] :=	ir.y
		end;
	     27:
		begin {read}
		   if eof(input)
		   then
		      ps := redchk
		   else
		      case ir.y	of
			 1:
			    read(s[s[t]]);
			 3:
			    begin
			       read(ch);
			       s[s[t]] := ord(ch)
			    end;
		      end;
		   t :=	t - 1
		end;
	     28:
		begin {write string}
		   h1 := s[t];
		   h2 := ir.y;
		   t :=	t - 1;
		   chrcnt := chrcnt + h1;
		   if chrcnt > lineleng	then
		      ps := lngchk;
		   repeat
		      write(stab[h2]);
		      h1 := h1 - 1;
		      h2 := h2 + 1
		   until h1 = 0
		end;
	     29:
		begin {write1}
		   if ir.y = 3
		   then
		      h1 := 1
		   else
		      h1 := 10;
		   chrcnt := chrcnt + h1;
		   if chrcnt > lineleng
		   then
		      ps := lngchk
		   else
		      case ir.y	of
			 1:
			    write(s[t]);
			 2:
			    write(itob(s[t]));
			 3:
			    if (s[t] < charl)	or (s[t] > charh)
			    then
			       ps := inxchk
			    else
			       write(chr(s[t]))
		      end;
		   t :=	t - 1
		end;
	     31:
		ps := fin;
	     32:
		begin
		   t :=	b - 1;
		   pc := s[b +	1];
		   if pc <> 0
		   then
		      b	:= s[b	+ 3]
		   else
		      begin
			 npr :=	npr - 1;
			 active	:= false;
			 stepcount := 0;
			 ptab[0].active := (npr = 0)
		      end
		end;
	     33:
		begin {exit function}
		   t :=	b;
		   pc := s[b +	1];
		   b :=	s[b + 3]
		end;
	     34:
		s[t] := s[s[t]];
	     35:
		s[t] := btoi(not (itob(s[t])));
	     36:
		s[t] := - s[t];
	  end {case};
   end	{exec3};


procedure interpret;
var hx:integer;
   label
      97, 98;


   procedure chooseproc;

{from	a random starting point	search for a process that is active and
not suspended.	d aborts the interpreter if a deadlock occurs.}

      var
	  d: integer;

      begin
	  d := pmax + 1;
	  curpr	:= (curpr + trunc(ran *	pmax)) mod (pmax + 1);
	  while	((not ptab[curpr].active) or (ptab[curpr].suspend <>
	     0)) and (d	>= 0) do
	     begin
		d := d - 1;
		curpr := (curpr	+ 1) mod (pmax + 1)
	     end;
	  if d < 0
	  then
	     begin
		ps := deadlock;
		writeln('deadlock');
		readln;
	     end
	  else
	     stepcount := trunc(ran * stepmax);
      end {chooseproc};


   begin {interpret}
      initialize;
      repeat
	  if ptab[0].active
	  then
	     curpr := 0
	  else
	     if	stepcount = 0
	     then
		chooseproc
	     else
		stepcount := stepcount - 1;
	  with ptab[curpr] do
	     begin
		ir := code[pc];
		pc := pc + 1
	     end;
	  if pflag then
	     begin
		if ir.f	= 18 {markstack} then
		   npr := npr +	1;
		curpr := npr
	     end;
	  with ptab[curpr] do
	  begin 
	     if	ir.f < 15
	     then
		exec1
	     else if ir.f < 24 
	     then exec2
	     else if ir.f < 37
	     then exec3
	     else exec4;
	end;	
      until ps	<> run;
  98: writeln;
      if ps <>	fin
      then
	  begin
	     with ptab[curpr]	do
		write('	halt at', pc: 3, ' in process',	curpr: 4,
		   ' because of	');
	     case ps of
		deadlock:
		   writeln('deadlock');
		divchk:
		   writeln('division by	0');
		inxchk:
		   writeln('invalid index');
		stkchk:
		   writeln('storage overflow');
		linchk:
		   writeln('too	much output');
		lngchk:
		   writeln('linr too long');
		redchk:
		   writeln('reading past end of	file');
	     end;
	     writeln('process active suspend pc');
	     for hx := 0 to pmax do
		with ptab[hx]	do
		   writeln(hx: 4,' ':4,active:6,' ',suspend:4,' ':4,pc);
	     writeln;
	     writeln('global variables');
	     for hx := btab[1].last +	1 to tmax do
		with tab[hx] do
		   if lev <> 1
		   then
		      goto 97
		   else
		      if obj = variable
		      then
			 if typ	in stantyps
			 then
			    case typ of
			       ints:
				  writeln(name,	' = ', s[adr]);
			       bools:
				  writeln(name,	' = ', itob(s[adr]));
			       chars:
				  writeln(name,	' = ', chr(s[adr] { mod
				  64}));
			    end;
	  end;
  97: writeln
   end	{interpret};
modend.
