perm filename IAUX2A.2[EAL,HE] blob sn#708972 filedate 1983-05-01 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00009 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	{$NOMAIN	Interpreter - Level 2 aux routines }
C00028 00003	{ Externally defined routines from elsewhere: }
C00032 00004	(* message passing routines: getReply, whereArm *)
C00035 00005	(* Graph structure routines: eval, feval, invalidate, stvals, change, getDevice, getFrame *)
C00047 00006	(* aux routine: getVal *)
C00049 00007	(* Aux routine: setVal *)
C00051 00008	(* affixment auxiliary routines: affixaux, unfixaux & unfix *)
C00057 00009	(* Aux routines to destroy variables: killVar, killEnv *)
C00062 ENDMK
C⊗;
{$NOMAIN	Interpreter - Level 2 aux routines }

const

  (* Constants from EDIT *)

  maxLines = 28;
  maxPPLines = 18;
  maxBpts = 25;
  maxTBpts = 20;	(* max could be exceeded by huge case stmnt *)
  listinglength = 2000;	(* Length of Listingarray *)

(* Random type declarations for OMSI/SAIL compatibility *)

type
  byte = 0..255;	(* doesn't really belong here, but... *)
  ascii = char; 
  atext = text;

{ Define all the pointer types here }

vectorp = ↑vector;
transp = ↑trans;
strngp = ↑strng;
eventp = ↑event;
framep = ↑frame;
statementp = ↑statement;
varidefp = ↑varidef;
nodep = ↑node;
pdbp = ↑pdb;
envheaderp = ↑envheader;
enventryp = ↑enventry;
environp = ↑environment;
cmoncbp = ↑cmoncb;
messagep = ↑message;

(* This one is used whenever a pointer is needed for which the 	*)
(* definition is missing from this file; naturally, all 	*)
(* pointers use the same space 					*)

dump = ↑integer;
token = array[1..4] of integer;		{Uses same space as a token}
cursorp = array[1..4] of integer;	{Ditto, for cursorp}


(* datatype definitions *)

datatypes = (pconstype, varitype, svaltype, vectype, rottype, transtype,
	     frametype, eventtype, strngtype, labeltype, proctype, arraytype,
	     reftype, valtype, cmontype, nulltype, undeftype,
	     dimensiontype, mactype, macargtype, freevartype);

scalar = real;

u = (used,free);
vector = record case u of
	   used: (refcnt: integer; val: array [1..3] of real);
	   free: (next: vectorp);
	 end;

trans = record case u of
	   used: (refcnt: integer; val: array [1..3,1..4] of real);
	   free: (next: transp);
	end;

cstring = packed array [1..10] of ascii;
c4str = packed array [1..4] of ascii;
c5str = packed array [1..5] of ascii;
c20str = packed array [1..20] of ascii;
linestr = packed array [1..130] of ascii;

strng = record
	  next: strngp;
	  ch: cstring;
	end;

event = record
	  next: eventp;		(* all events are on one big list *)
	  count: integer;
	  waitlist: pdbp;
	end;

frame = record
	  vari: varidefp;	(* back pointer to variable name & info *)
	  calcs: nodep;		(* affixment info *)
	  case ftype: boolean of	(* frame = true, device = false *)
  true:	    (valid: integer; val, fdepr: transp; dcntr: integer; dev: framep);
  false:    (mech: integer; case sdev: boolean of
		true: (sdest: real); false: (tdest,appr,depr: transp));
		(* sdev = true for scalar devices, false for frames *)
	end;



(* statement definitions *)

stmntypes = (progtype, blocktype, coblocktype, endtype, coendtype,
		fortype, iftype, whiletype, untiltype, casetype,
		calltype, returntype,
		printtype, prompttype, pausetype, aborttype, assigntype,
		signaltype, waittype, enabletype, disabletype, cmtype,
		affixtype, unfixtype,
		movetype,jtmovetype,operatetype,opentype,closetype,centertype,
		floattype, stoptype, retrytype,
		requiretype, definetype, macrotype, commenttype, dimdeftype,
		setbasetype, wristtype, saytype, declaretype, emptytype,
		evaltype, armmagictype);
		(* more??? *)

statement = packed record
		next, last: statementp;
		stlab: varidefp;
		exprs: nodep;	(* any expressions used by this statement *)
		nlines: integer;
		bpt,bad: boolean;
		case stype: stmntypes of

    progtype:	    (pcode: statementp; errors: integer);
    affixtype,
    unfixtype:	    (frame1, frame2, byvar, atexp: nodep; rigid: boolean);
    cmtype:	    (oncond: nodep; conclusion: statementp;
			deferCm, exprCm: boolean; cdef: varidefp);
    signaltype,
    waittype:	    (event: nodep);
		end;



(* auxiliary definitions: variable, etc. *)

varidef = packed record
	    next,dnext: varidefp;
	    name: dump;
	    level: 0..255;	(* environment level *)
	    offset: 0..255;	(* environment offset *)
	    dtype: varidefp;	(* to hold the dimension info *)
	    tbits: 0..15;  (* special type bits: array = 1, proc = 2, ref = 4 & ? *)
	    dbits: 0..15;	(* for use by debugger/interpreter *)
	    case vtype: datatypes of
  arraytype:  (a: nodep);
  proctype:   (p: nodep);
  labeltype,
  cmontype:   (s: statementp);
  mactype:    (mdef: statementp);
  macargtype: (marg: dump);
  pconstype:  (c: nodep);
  dimensiontype: (dim: nodep);
	  end;



(* definition of the ubiquitous NODE record *)

nodetypes = (exprnode, leafnode, listnode, clistnode, colistnode, forvalnode,
		deprnode, viaptnode, apprnode, destnode, byptnode, durnode,
		sfacnode, wobblenode, swtnode, nullingnode, wristnode, cwnode,
		arrivalnode, departingnode,
		ffnode, forcenode, stiffnode, gathernode, cmonnode, errornode,
		calcnode, arraydefnode, bnddefnode, bndvalnode,
		waitlistnode, procdefnode, tlistnode, dimnode, commentnode,
		linearnode, elbownode, shouldernode, flipnode, wrtnode,
		loadnode,velocitynode);

exprtypes =  (	svalop,					(* scalar operators *)
		sltop, sleop, seqop, sgeop, sgtop, sneop,	(* relations *)
		notop, orop, xorop, andop, eqvop,		(* logical *)
		saddop, ssubop, smulop, sdivop, snegop, sabsop, (* scalar ops *)
		sexpop, maxop, minop, intop, idivop, modop,
		sqrtop, logop, expop, timeop,			(* functions *)
		sinop, cosop, tanop, asinop, acosop, atan2op,	(* trig *)
		vdotop, vmagnop, tmagnop,
		vecop,					(* vector operators *)
		vmakeop, unitvop, vaddop, vsubop, crossvop, vnegop,
		svmulop, vsmulop, vsdivop, tvmulop, wrtop,
		tposop, taxisop,
		transop,				(* trans operators *)
		tmakeop, torientop, ttmulop, tvaddop, tvsubop, tinvrtop,
		vsaxwrop, constrop, ftofop, deproachop, fmakeop, vmkfrcop,
		ioop,					(* i/o operators *)
		queryop, inscalarop,
		specop,					(* special operators *)
		arefop, callop, grinchop, macroop, vmop, adcop, dacop, jointop,
		badop,
		addop, subop, negop, mulop, divop, absop); (* for parsing *)

leaftypes = pconstype..strngtype;

reltypes = sltop..sgtop;
forcetypes = (force,absforce,torque,abstorque,angvelocity);

node = record
	next: nodep;
	case ntype: nodetypes of
    exprnode:	(op: exprtypes; arg1, arg2, arg3: nodep; elength: integer);
    leafnode:	(case ltype: leaftypes of
	varitype:  (vari: varidefp; vid: dump);
	pconstype: (cname: varidefp; pcval: nodep);
	svaltype:  (s: scalar; wid: integer);
	vectype:   (v: vectorp);
	transtype: (t: transp);
	strngtype: (length: integer; str: strngp) ); (* also used by commentnodes *)
    listnode:	(lval: nodep);
    clistnode:	(cval: integer; stmnt: statementp; clast: nodep);
    colistnode:	(prev: nodep; cstmnt: statementp);
    calcnode: 	(rigid, frame1: boolean; other: framep; case tvarp: boolean of 
		    false: (tval: transp); true: (tvar: enventryp) );
    arraydefnode: (numdims: 1..10; bounds: nodep; combnds: boolean);
    bnddefnode:	(lower, upper: nodep);
    bndvalnode:	(lb, ub, mult: integer);
    waitlistnode: (who: pdbp; when: integer);
    procdefnode:(ptype: datatypes; level: 0..255;
		    pname, paramlist: varidefp; body: statementp);
    dimnode:	(time, distance, angle, dforce: integer);
	end;



(* process descriptor blocks & environment record definitions *)

queuetypes = (nullqueue,nowrunning,runqueue,inputqueue,eventqueue,sleepqueue,
		forcewait,devicewait,joinwait,proccall);

pdb = packed record
	nextpdb,next: pdbp;	(* for list of all/active pdb's *)
	level: 0..255;		(* lexical level *)
	mode: 0..255;		(* expression/statement/sub-statement *)
	priority: 0..255;	(* probably never greater than 3? *)
	status: queuetypes;	(* what are we doing *)
	env: envheaderp;
	spc: statementp;	(* current statement *)
	epc: nodep;		(* current expression (if any) *)
	sp: nodep;		(* intermediate value stack *)
	cm: cmoncbp;		(* if we're a cmon point to our definition *)
	mech: framep;		(* current device being used *)
	linenum: integer;	(* used by editor/debugger *)
	 case procp: boolean of	(* true if we're a procedure *)
true:  (opdb: pdbp;		(* pdb to restore when procedure exits *)
	pdef: nodep);		(* procedure definition node *)
false: (evt: eventp;		(* event to signal when process goes away *)
	sdef: statementp);	(* first statement where process was defined *)
      end;


envheader = packed record
	      parent: envheaderp;
	      env: array [0..4] of environp;
	      varcnt: 0..255;		(* # of variables in use ??? *)
		case procp: boolean of  (* true if we're a procedure *)
	true: (proc: nodep);
	false:(block: statementp);
	    end;

enventry = record
	    case etype: datatypes of
  svaltype:  (s: scalar);
  vectype:   (v: vectorp);
  transtype: (t: transp);
  frametype: (f: framep);
  eventtype: (evt: eventp);
  strngtype: (length: integer; str: strngp);
  cmontype:  (c: cmoncbp);
  proctype:  (p: nodep; penv: envheaderp);
  reftype:   (r: enventryp);
  arraytype: (a: envheaderp; bnds: nodep);
	   end;


environment = record
		next: environp;
		vals: array [0..9] of enventryp;
	      end;


cmoncb = record
	   running, enabled: boolean;		(* cmon's status *)
	   cmon: statementp;
	   pdb: pdbp;
	   evt: eventp;
	   fbits: integer;			(* bits for force sensing *)
	   oldcmon: cmoncbp;			(* for debugger *)
	 end;



(* definition of AL-ARM messages *)

msgtypes = (initarmscmd,calibcmd,killarmscmd,wherecmd,
	    abortcmd,stopcmd,movehdrcmd,movesegcmd,
	    centercmd,operatecmd,movedonecmd,signalcmd,
	    setccmd,forcesigcmd,forceoffcmd,biasoncmd,biasoffcmd,setstiffcmd,
	    zerowristcmd,wristcmd,gathercmd,getgathercmd,readadccmd,writedaccmd,
	    errorcmd,floatcmd,setloadcmd,
	    armmagiccmd,realcmd,vectorcmd,transcmd);

errortypes = (noerror,noarmsol,timerr,durerr,toolong,featna,
	      unkmess,srvdead,adcdead,nozind,exjtfc,paslim,nopower,badpot,devbusy,
	      baddev,timout,panicb,nocart,cbound,badparm);

message = record
	   cmd: msgtypes;
	   ok: boolean;
	   case integer of
	1:   (dev, bits, n: integer;
(*	     (dev, bits, n, evt: integer;	(* for arm code version *)
	      evt: eventp;
	      dur: real;
	      case integer of
		1: (v1,v2,v3: real);
		2: (sfac,wobble,pos: real);
		3: (val,angle,mag: real);
		4: (max,min: real);
		5: (error: errortypes));
	2:   (fv1,fv2,fv3,mv1,mv2,mv3: real);	(* may never use these... *)
	3:   (t: array [1..6] of real);
	  end;

interr = record
         case integer of
           0: (i: integer);
	   1: (err,foo: errortypes);
	 end;

listingarray = packed array [0..listinglength] of ascii;


(* global variables *)

var
	(* from EDIT *)
    listing: listingarray;  (* first 150 chars are used by expression editor *)
			    (* next 40 by header & trailer lines *)
{*} cursorStack: array [1..15] of cursorp;	{These are BIG records! }
(*  lbuf: array [1..160] of ascii;
    ppBuf: array [1..100] of ascii; *)
    dum1: array[1..260] of ascii;
    lines: array [1..maxLines] of dump; 
    ppLines: array [1..maxPPLines] of dump;	
(*  marks: array [1..20] of integer;
    reswords: array [0..26] of reswordp;
    idents: array [0..26] of identp;
    macrostack: array [1..10] of tokenp;
    curmacstack: array [1..10] of varidefp;
    screenheight,dispHeight: integer;
    ppBufp,oppBufp,ppOffset,ppSize,nmarks: integer;
    lbufp,cursor,ocur,cursorLine,fieldnum,lineNum,findLine,pcLine: integer;
    firstDline,topDline,botDline,firstLine,lastLine,curLine: integer;
    freeLines,oldLines: linerecp;
    sysVars: varidefp;
    dProg: statementp;
    curBlock, newDeclarations, findStmnt: statementp;
    macrodepth: integer;
    filedepth, errCount, sCursor: integer;
    curChar, maxChar, curFLine, curPage: integer;
    nodim, distancedim, timedim, angledim,
      forcedim, torquedim, veldim, angveldim: varidefp;
    fvstiffdim, mvstiffdim: nodep;
    pnode: nodep;
*)  dum2: array[1..141] of dump;
(*  smartTerminal: boolean; 
    setUp,setExpr,setCursor,dontPrint,outFilep,collect,fParse,sParse,
      eofError,endOfLine,backup,expandmacros,flushcomments,checkDims,
      shownLine: boolean;
*)  dum3: array[1..16] of boolean;
    curtoken: token;
    file1,file2,file3,file4,file5,outFile: atext;

    bpts: array [1..maxBpts] of statementp;	(* debugging crap *)
    tbpts: array [1..maxTBpts] of statementp;
    debugPdbs: array [0..10] of pdbp;
(*  nbpts,ntbpts,debugLevel: integer;
    eCurInt: pdbp;
    STLevel: integer;
*)  dum4: array[1..5] of integer;
    singleThreadMode,tSingleThreadMode: boolean;

	(* from INTERP *)
    inputLine: array [1..20] of ascii;
    talk: text;			(* for using the speech synthesizer *)
    curInt, activeInts, readQueue, allPdbs: pdbp;
    sysEnv: envheaderp;
    clkQueue: nodep;
    allEvents: eventp;
    etime: integer;		(* used by eval *)
    curtime: integer; (* who knows where this will get updated - an ast? *)
    stime: integer;		(* used for clock queue on 10 *)
    msg: messagep;		(* for AL-ARM interaction *)
    inputp: integer;		(* current offset into inputLine array above *)
    resched, running, escapeI, iSingleThreadMode: boolean;
    msgp: boolean;		(* flag set if any messages pending *)
    inputReady: boolean;

(* various constant pointers *)
    xhat,yhat,zhat,nilvect: vectorp;
    niltrans: transp;
    gpark, rpark: transp;		(* arm park positions *)

(* various device & variable pointers *)
    speedfactor: enventryp;
    garm: framep;

{ Externally defined routines from elsewhere: }

	(* From ALLOC *)
procedure relVector(v: vectorp);				external;
function newTrans: transp;					external;
procedure relTrans(t: transp);					external;
function newNode: nodep;					external;
procedure relNode(n: nodep);					external;
procedure relEentry(n: enventryp);				external;
procedure relCmoncb(n: cmoncbp);				external;
procedure relFrame(n: framep);					external;
procedure relEheader(n: envheaderp);				external;
procedure relEnvironment(n: environp);				external;

	(* Arithmetic routines *)
function ttmul (t1,t2: transp): transp; 			external;
function tinvrt (t: transp): transp; 				external;
function taxis (t: transp): vectorp; 				external;
function tmagn (t: transp): scalar; 				external;

	(* From RSXMSG *)
function startArm: boolean;                                  	external;
procedure initMsg(var buf: messagep; var flag: boolean);     	external;
function SendArm: boolean;                                   	external;
function GetArm: boolean;                                    	external;
procedure signalArm;                                         	external;

	(* From IAUX1A *)
procedure push (n: nodep);					external;
function pop: nodep;						external;
procedure upTrans (var t: transp; tp: transp);			external;
function getEntry (level, offset: byte): enventryp; 		external;
function getVar (level, offset: byte): enventryp;		external;
procedure freePdb(p: pdbp);					external;
procedure freeEvent(e: eventp);					external;
procedure sendCmd;						external;

	(* From IAUX1B *)
procedure msgDispatch;						external;
procedure ppArmError(err: errortypes; angle: integer);		external;

	(* From PP *)
procedure ppLine; 						external;
procedure ppOutNow; 						external;
procedure ppChar(ch: ascii); 					external;
procedure pp5(ch: c5str; length: integer); 			external;
procedure pp10(ch: cstring; length: integer); 			external;
procedure pp10L(ch: cstring; length: integer);			external;
procedure pp20(ch: c20str; length: integer); 			external;
procedure pp20L(ch: c20str; length: integer); 			external;
procedure ppInt(i: integer); 					external;

(* message passing routines: getReply, whereArm *)

procedure getReply(sendIt: boolean); external;
procedure getReply;
 var ocmd: msgtypes; b: boolean;
 begin
 with msg↑ do
  begin
  ocmd := cmd;			(* remember what we're waiting for *)
  if sendIt then sendCmd;	(* send request to ARM servo *)
  repeat
   b := getArm;			(* try to read a message packet from ARM *)
   if b and (cmd <> ocmd) then	(* if we got one, was it our reply? *)
     begin
     msgDispatch;		(* deal with whatever the ARM servo sent over *)
     b := false;		(* keep waiting for our reply *)
     end
  until b;			(* wait for reply *)
  end;
 end;

function whereArm (mech: integer): transp; external;	(* to read in the arm's position *)
function whereArm ;
 var tp: transp; i,j: integer; b: boolean;
 begin
 tp := newTrans;
 with msg↑,tp↑ do
  begin
  cmd := wherecmd;
  dev := mech;
  bits := 0;
  getReply(true);		(* go get 1st message packet *)
  if ok then			(* check there's no error *)
    begin
    for i := 1 to 3 do
     for j := 1 to 2 do val[i,j] := t[i + 3*(j-1)];	(* copy result *)
    repeat b := getArm until b;	(* get 2nd packet (guaranteed to be next) *)
    for i := 1 to 3 do
     for j := 3 to 4 do val[i,j] := t[i + 3*(j-3)];	(* copy result *)
    end
   else
    begin			(* ERROR - complain *)
    ppArmError(error,bits);
    relTrans(tp);		(* don't need this anymore *)
    tp := niltrans;
    end;
  end;
 whereArm := tp;
 end;

(* Graph structure routines: eval, feval, invalidate, stvals, change, getDevice, getFrame *)

procedure nextTime; external;
procedure nextTime;
 begin
 if etime = Maxint then etime := 1 (* should reset all invalid frames, but ... *)
  else etime := etime + 1;
 end;

procedure eval (f: framep); external;
procedure eval ;
 var calc: nodep; b: boolean; f2, tr: transp;
 begin
 if f↑.valid <> etime then	(* Haven't looked at it yet *)
  begin
  f↑.valid := etime;		(* Mark it *)
  calc := f↑.calcs;		(* Get list of calculators *)
  b := true;
  while (calc <> nil) and b do	(* See if someone it's affixed to is now valid *)
   if (calc↑.ntype = calcnode) and (calc↑.rigid or calc↑.frame1) then
    with calc↑.other↑ do	(* A possibility, look at other frame *)
     begin
     if not ftype then	(* See if it's a device or frame *)
       begin		(* It's a device - use it to compute current value *)
       f2 := whereArm(mech);	(* Get current device pos *)
       b := false;		(* No need to look further *)
       end
      else if (dcntr=0) and (valid=0) then	(* not dynamic & valid frame *)
	    begin f2 := val; b := false end
	    else calc := calc↑.next	(* dynamic or not valid - try next *)
     end
    else calc := calc↑.next; (* not a calc, or nonrigid and frame2 - try next *)

  if calc = nil then
   begin  (* Check calcs again - this time trying to evaluate other frame *)
   calc := f↑.calcs;
   b := true;
   while (calc <> nil) and b do
    if (calc↑.ntype = calcnode) and (calc↑.rigid or calc↑.frame1) then
     begin
     eval(calc↑.other);		(* Try to get a value for it *)
     if calc↑.other↑.valid=0 then		(* Is it now valid? *)
	begin f2 := calc↑.other↑.val; b := false end	(* Yes - all done *)
      else calc := calc↑.next	(* still not valid - try next *)
     end
    else calc := calc↑.next; (* not a calc, or nonrigid and frame2 - try next *)
   end;

  if calc <> nil then
    with calc↑ do
     begin	(* use other frame to evaluate desired one & return success *)
     if tvarp then tr := tvar↑.t else tr := tval; (* explicitly named trans? *)
     if not frame1 then tr := tinvrt(tr);  (* second := inv(trans) * first *)
     upTrans(f↑.val,ttmul(tr,f2));	  (* first := trans * second *)
     f↑.valid := 0;			  (* Mark it as now valid *)
     end;
  end;
 end;

function feval (f: framep): transp; external;
function feval ;
 begin
 if not f↑.ftype then 
   begin			(* If device use its current value *)
   feval := whereArm(f↑.mech);	(* Get current device pos *)
   end
  else					(* frame *)
   begin
   if (f↑.dcntr<>0) or (f↑.valid<>0) then  (* dynamic frame or not valid? *)
    begin			(* Need to calculate current value *)
    nextTime;			(* update eval time *)
    eval(f);			(* try to evaluate the variable *)
    end;
   if f↑.valid = 0 then feval := f↑.val		(* copy trans pointer *)
    else feval := niltrans;			(* but always return something *)
   end;
 end;

function invalidate (f: framep): boolean; external;
function invalidate ;
 var calc: nodep; b: boolean;
 begin

(* invalidate frame & all other frames affixed either rigidly or
    non-rigidly with this being frame2,
   else indicate we need to modify non-rigid trans. *)

 b := false;		(* assume no updating of non-rigid relationships *)
 if etime <> f↑.valid then		(* haven't marked this one yet *)
  with f↑ do
   begin
   if valid = 0 then upTrans(val,nil);	(* flush old value *)
   valid := etime;	(* mark us as having an invalid value *)
   calc := calcs;
   while calc <> nil do		(* invalidate everyone we're affixed to *)
     begin			(* rigidly or if we're frame 2 *)
     if (calc↑.ntype = calcnode) and (calc↑.rigid or (not calc↑.frame1))
	then b := b or invalidate(calc↑.other)	(* go invalidate frame *)
	else b := true;		(* found a non-rigid affixment to update *)
     calc := calc↑.next;	(* now repeat with next calc *)
     end;
   end;
 invalidate := b;
 end;

procedure stvals (f: framep); external;
procedure stvals ;
 var calc,c2: nodep; t,val: transp; f2: framep;
 begin
 calc := f↑.calcs;
 val := f↑.val;			(* frames current value *)
 while calc <> nil do		(* update everyone we're affixed to *)
  with calc↑ do
   begin
   f2 := other;
   if (ntype = calcnode) and (rigid or (not frame1)) then
     begin			(* see if we need to update this frame *)
     if f2↑.valid <> 0 then		(* haven't updated it yet *)
	begin
	if tvarp then t := tvar↑.t else t := tval; (* explicitly named trans? *)
	if frame1 then t := tinvrt(t);	(* second := inv(trans) * first *)
	upTrans(f2↑.val,ttmul(t,val));	(* first := trans * second *)
	f2↑.valid := 0;			(* Mark it as now valid *)
	stvals(f2);			(* and go update its affixments *)
	end
     end
    else
     begin			(* need to update relation trans *)
     t := feval(f2);			(* get a value for f2 *)
     t := ttmul(val,tinvrt(t));		(* compute new relation trans *)
     if tvarp then upTrans(tvar↑.t,t) 
      else
       begin
       upTrans(tval,t); (* store it *)
       c2 := f2↑.calcs;		(* now go fix trans up in f2's calc list *)
       while c2↑.other <> f do c2 := c2↑.next;	(* find other calc of pair *)
       upTrans(c2↑.tval,t);	(* copy trans to it too *)
       end;
     end;
   calc := calc↑.next;		(* move on to next one *)
   end;
 end;

procedure change (f: framep; res: nodep); external;
procedure change ;
 var calc: nodep; b: boolean;
 begin
 if f↑.dcntr=0 then		(* if not dynamic *)
   begin
   nextTime;
   b := invalidate(f);	(* b = true if any non-rigid affixments need updating *)
   f↑.val := res↑.t;			(* copy trans pointer *)
   f↑.val↑.refcnt:=f↑.val↑.refcnt + 1;	(* mark trans in use *)
   f↑.valid := 0;			(* mark us as having a valid value *)
   if b then stvals(f);	(* go fix up the non-rigid relationships *)
   end
  else begin
       pp20L('Can''t assign to dyna',20); pp10('mic frames',10); ppLine;
	(* maybe also give name of frame?? *)
       end;
 end;

procedure getDevice (f: framep; r: nodep); external;
procedure getDevice ;
 var i: integer; 
 begin
 if f↑.sdev then 
   with msg↑ do
    begin
    cmd := wherecmd;
    dev := f↑.mech;
    bits := 0;
    getReply(true);		(* have ARM servo read in the hand/device value *)
    if ok then r↑.s := t[1]
     else
      begin			(* ERROR - complain *)
      ppArmError(error,bits);
      r↑.s := 0;
      end;
    r↑.ltype := svaltype;
    end
  else
   r↑.t := whereArm(f↑.mech);	(* go read in the arm's position *)
 end;

procedure getFrame (f: framep; r: nodep); external;
procedure getFrame ;
 begin
 if not f↑.ftype then getDevice(f,r)	(* If device get its current value *)
  else					(* frame *)
   begin
   if (f↑.dcntr<>0) or (f↑.valid<>0) then  (* dynamic frame or not valid? *)
    begin			(* Need to calculate current value *)
    nextTime;			(* update eval time *)
    eval(f);			(* try to evaluate the variable *)
    end;
   r↑.t := f↑.val;		(* copy trans pointer *)
   if r↑.t = nil then r↑.t := niltrans;	(* always return something *)
					(* complain though??? *)
   end;
 end;

(* aux routine: getVal *)

procedure getVal (level, offset: byte); external;
procedure getVal ;
 var entry: enventryp; res: nodep;
 begin
 entry := getVar(level,offset);	(* look up environment entry for variable *)
 res := newNode;
 res↑.ntype := leafnode;
 res↑.ltype := entry↑.etype;		(* copy datatype of result *)
 if entry↑.etype = svaltype then res↑.s := entry↑.s	(* it's a scalar *)
  else if entry↑.etype <> frametype then (* it's a vector, trans or string *)
   with res↑ do
    begin
    v := entry↑.v;		(* copy pointer *)
    str := entry↑.str;
    if v = nil then
     if ltype = vectype then v := nilvect
     else if ltype = transtype then t := niltrans
     else length := 0;
				(* complain??? *)
    end
  else
    begin
    res↑.ltype := transtype;
    getFrame(entry↑.f,res);
    end;
 push(res);			(* store the value on the stack *)
 end;

(* Aux routine: setVal *)

procedure setVal (level, offset: byte); external;
procedure setVal ;
 var entry: enventryp; res: nodep;
 begin
 entry := getVar(level,offset);	(* look up environment entry for variable *)
 res := pop;			(* pop value off of stack *)
 with entry↑ do
  if etype = svaltype then s := res↑.s		(* it's a scalar *)
   else if etype = vectype then
	 begin
	 with res↑.v↑ do refcnt := refcnt + 1;	(* indicate new vector is in use *)
	 if v <> nil then
	  begin
	  v↑.refcnt := v↑.refcnt - 1;		(* we're done with vector now *)
	  if v↑.refcnt <= 0 then relVector(v);	(* release it if no one wants it *)
	  end;
	 v := res↑.v;				(* copy new vector pointer *)
	 end
   else if etype = transtype then upTrans(t,res↑.t) (* update trans with new value *)
   else if etype = strngtype then
	 begin
	 length := res↑.length;
	 str := res↑.str;			(* copy new string pointer *)
	 end
   else change(f,res);	(* change frame's value, updating affixed frames *)
 relNode(res);		(* free node up *)
 end;

(* affixment auxiliary routines: affixaux, unfixaux & unfix *)

procedure affixaux (f, d: framep; cnt: integer); external;
procedure affixaux ;
 var c1,c2,ct: nodep;
 begin
 with f↑ do
  if not (ftype and (dev <> nil)) then		(* haven't marked it yet *)
   begin
   if not ftype then cnt := 1			(* it's a device *)
    else begin dev := d; dcntr := cnt; cnt := cnt + 1; end;	(* mark frame *)
   c1 := calcs;
   ct := nil;
   while c1 <> nil do
    begin				(* mark everyone it's affixed to *)
    if c1↑.rigid or not c1↑.frame1 then affixaux(c1↑.other,d,cnt)
     else if c1↑.other↑.dev = nil then
	   begin		(* need to break non-rigid affixment *)
				(* first splice calcs out of affixment lists *)
	   if ct = nil then calcs := c1↑.next else ct↑.next := c1↑.next;
	   c2 := c1↑.other↑.calcs;
	   ct := nil;
	   while c2↑.other <> f do begin ct := c2; c2 := c2↑.next; end;
	   if ct = nil then c1↑.other↑.calcs := c2↑.next else ct↑.next := c2↑.next;
	   if not c1↑.tvarp then
	     begin 			(* release relation trans *)
	     upTrans(c1↑.tval,nil);
	     upTrans(c2↑.tval,nil);
	     end;
	   relNode(c1);			(* finally release calc nodes *)
	   relNode(c2);
	   c1 := ct;
	   end;
    ct := c1;
    c1 := c1↑.next;
    end;
   end;
  end;

function unfixaux (f: framep; cnt: integer): boolean; external;
function unfixaux ;
 var c: nodep; b: boolean; d: framep;
 begin
 b := false;
 with f↑ do
  if not ftype then affixaux(f,f,1)	(* a device - remark everyone as dynamic *)
   else if dev <> nil then  (* check we're still marked as dynamic, else done *)
    if cnt > dcntr then
      begin
      d := dev; dev := nil;		(* so affixaux will mark us *)
      affixaux(f,d,dcntr);		(* need to remark everyone *)
      end
     else
      begin				(* unmark us *)
      dev := nil;
      dcntr := 0;
      b := true;
      c := calcs;
      while (c <> nil) and b do
	begin
	b := unfixaux(c↑.other,cnt);
	c := c↑.next
	end
      end;
 unfixaux := b;
 end;

procedure unfix (f1,f2: framep); external;
procedure unfix ;
 var t: transp; c1, c2: nodep; b: boolean; i: integer;
 begin
 if f1↑.ftype then t := feval(f1);	(* try to get a value for both *)
 if f2↑.ftype then t := feval(f2);	(* if they're frames *)
 c1 := f1↑.calcs;		(* unfix f1 from f2 *)
 c2 := nil;
 b := true;
 while (c1 <> nil) and b do
  if c1↑.other = f2 then
    begin			(* found calc - splice it out of list *)
    b := false;
    if c2 = nil then f1↑.calcs := c1↑.next else c2↑.next := c1↑.next;
    if not c1↑.tvarp then upTrans(c1↑.tval,nil);   (* release old trans values *)
    relNode(c1);		(* done with calc node *)
    end
   else begin c2 := c1; c1 := c1↑.next end;	(* try next *)
 c1 := f2↑.calcs;		(* now unfix f2 from f1 *)
 c2 := nil;
 b := true;
 while (c1 <> nil) and b do
  if c1↑.other = f1 then
    begin			(* found calc - splice it out of list *)
    b := false;
    if c2 = nil then f2↑.calcs := c1↑.next else c2↑.next := c1↑.next;
    if not c1↑.tvarp then upTrans(c1↑.tval,nil);   (* release old trans values *)
    relNode(c1);		(* done with calc node *)
    end
   else begin c2 := c1; c1 := c1↑.next end;	(* try next *)
 if not f1↑.ftype then b := unfixaux(f2,0)	(* f2 no longer dynamic *)
  else if not f2↑.ftype then b := unfixaux(f1,0)	(* f1 no longer dynamic *)
  else if f1↑.dev <> nil then		(* both currently dynamic *)
	if f1↑.dcntr < f2↑.dcntr then b := unfixaux(f2,f1↑.dcntr) (* unmark f2 *)
	 else b := unfixaux(f1,f2↑.dcntr);	(* unmark f1 *)
 end;
(* Aux routines to destroy variables: killVar, killEnv *)

procedure killVar(e: enventryp); external;
procedure killVar;
 var j,k,size: integer; envhdr: envheaderp; env,eo: environp; ep: enventryp;
     b,bo: nodep; pp: pdbp; cp: cmoncbp;
 begin
  with e↑ do
   case etype of
svaltype,
strngtype: begin end;				(* nothing to do *)
vectype:   if v <> nil then			(* check for old value *)
	    begin
	    v↑.refcnt := v↑.refcnt - 1;		(* we're done with vector now *)
	    if v↑.refcnt <= 0 then relVector(v);  (* release it if no one else wants it *)
	    end;
transtype: upTrans(t,nil);
frametype: begin
	   while f↑.calcs <> nil do
	    unfix(f,f↑.calcs↑.other);		(* unfix us from everyone *)
	   upTrans(f↑.val,nil);			(* flush our current value *)
	   relFrame(f);				(* flush frame *)
	   end;
eventtype: begin
	   (* *** what to do with those processes waiting on this event? *** *)
	   pp := evt↑.waitlist;
	   while pp <> nil do
	    begin pp↑.status := nullqueue; pp := pp↑.next end;
	   freeEvent(evt);
	   end;
cmontype:  repeat
	    if c↑.cmon↑.oncond↑.ntype = forcenode then freeEvent(c↑.evt);
	    freePdb(c↑.pdb);		(* now it's ok to flush its pdb *)
	    cp := c↑.oldcmon;		(* did we have several copies active? *)
	    relCmoncb(c);		(* and also free up its cmoncb *)
	    c := cp;
	   until cp = nil;
arraytype: begin
	   b := e↑.bnds;
	   size := b↑.mult * (b↑.ub - b↑.lb + 1); (* get array size *)
	   while b <> nil do begin bo := b; b := b↑.next; relNode(bo) end;
	   envhdr := e↑.a;
	   env := envhdr↑.env[0];
	   relEheader(envhdr);
	   j := -1;
	   for k := 1 to size do
	    begin
	    if j = 9 then
	      begin eo := env; env := env↑.next; relEnvironment(eo); j := 0 end
	     else j := j + 1;
	    ep := env↑.vals[j];
	    killVar(ep);		(* kill variable environment entry *)
	    end;
	   relEnvironment(env);
	   end;
	(* nothing to do for procedures or indirect references *)
otherwise {do nothing};
    end;
   relEentry(e);
   e := nil;
 end;

procedure killEnv; external;
procedure killEnv;
 var envhdr: envheaderp; envir,eo: environp; e: enventryp; j: integer;
 begin
 if (curInt↑.env <> sysEnv) and (curInt↑.env↑.varcnt < 255) then
   begin	(* varcnt check is so flushall doesn't have us kill it twice *)
   with curInt↑ do
    begin
    envhdr := env;
    env := envhdr↑.parent;
    end;
   envhdr↑.varcnt := 255;
   envir := envhdr↑.env[0];
   relEheader(envhdr);
   j := 0;
   while envir <> nil do           (* deallocate variables *)
    begin
    e := envir↑.vals[j];
    if e <> nil then killVar(e);   (* kill var's environment entry *)
    if j = 9 then
      begin
      eo := envir;
      envir := envir↑.next;
      relEnvironment(eo);
      j := 0
      end
     else j := j + 1;
    end;
   end
  else curInt↑.env := sysEnv;
 end;