--routeCrosses.mesa
DIRECTORY RouteDefs;
RouteCrosses:PROGRAM IMPORTS RouteDefs EXPORTS RouteDefs =BEGIN
OPEN RouteDefs;
--the function of Crosses is to fill in the offset field in a Conn record.
--this offset will keep cross wires from shorting.
--strtKind and endKind are also computed but no one uses them except crosses.
Error:SIGNAL=CODE;
marginChanged:BOOLEAN;
InstallInternalContacts:PUBLIC CtlProc=BEGIN
marginChanged←FALSE;
EnumerateChannels[MakeOffsets];
IF ~chipmonk THEN EnumerateChannels[ShowCrossesX];
EnumerateChannels[CheckCrosses];
RETURN[IF marginChanged THEN 1 ELSE -1];
END;
MakeOffsets:PROCEDURE[rect:RectanglePtr]=BEGIN
EnumerateConns[rect,InitConn];
EnumerateConns[rect,OffsetSources];
OffsetJogs[rect,-1];
OffsetJogs[rect,bigRun];
SetRunOffsets[rect];
EnumerateConns[rect,FixJogColor];
EnumerateConns[rect,SetX];
EnumerateConns[rect,CheckMargins];
END;
InitConn:PROCEDURE[rect:RectanglePtr,con:ConnPtr]=BEGIN
con.startKind←FigureKind[rect,con,con.start];
con.endKind←FigureKind[rect,con,con.end];
IF con.offset#noOffset THEN Error;
IF con.lambdaX#-1 THEN Error;
END;
FigureKind:PROCEDURE[rect:RectanglePtr,con:ConnPtr,where:RunNo]
RETURNS[kind:ConnKind]=BEGIN
kind←none;
IF where=-1 OR where=bigRun THEN RETURN[edge];
FOR rl:RunListPtr←rect.runs, rl.t UNTIL rl=NIL DO
run:RunPtr=rl.h;
IF ~(con.circuit=run.circuit AND where=run.run) THEN LOOP;
IF con.event IN [run.start..run.end) THEN kind←CombineKinds[kind,right];
IF con.event IN (run.start..run.end] THEN kind←CombineKinds[kind,left];
ENDLOOP;
IF kind=none THEN SELECT con.event FROM
-1=>kind←left; bigRun=>kind←right; ENDCASE=>Error;
END;
CombineKinds:PROCEDURE[a,b:ConnKind] RETURNS[ConnKind]=BEGIN
IF a=edge OR b=edge THEN {Error; RETURN[edge]};
RETURN[IF a=b OR a=none THEN b ELSE IF b=none THEN a ELSE tee]
END;
OffsetSources:PROCEDURE[rect:RectanglePtr,c:ConnPtr]=BEGIN
runMax:INTEGER=rect.sizeC.y;
c2,c3:ConnPtr←NIL;
tt:ConnPtr=IF c.eventPtr=NIL OR c.eventPtr.opposite=NIL THEN NIL
ELSE c.eventPtr.opposite.conn;
IF c.offset#noOffset THEN RETURN;
IF c.start=-1
THEN IF c.end=bigRun THEN {c.offset←0; RETURN} ELSE {c2←c; c3←tt}
ELSE IF c.end=bigRun THEN {c3←c; c2←tt} ELSE RETURN;
IF c2#NIL THEN c2.offset←0;
IF c3#NIL THEN c3.offset←0;
IF c2=c3 THEN Error;
IF c2=NIL OR c3=NIL OR c3.start>c2.end THEN RETURN;
BEGIN
r2:RunNo=IF c2=NIL THEN 0 ELSE IF c2=c3 THEN runMax ELSE c2.end;
r3:RunNo=IF c3=NIL THEN runMax ELSE c3.start;
r2Gain:RunNo←r2+1;
r3Gain:RunNo←runMax-r3;
index:INTEGER=(SELECT c2.endKind FROM left=>0,tee=>3,right=>6, ENDCASE=>ERROR)
+(SELECT c3.startKind FROM left=>0,tee=>1,right=>2, ENDCASE=>ERROR);
FOR rl:RunListPtr←rect.runs,rl.t UNTIL rl=NIL DO
r:RunPtr=rl.h;
IF c.event NOT IN (r.start..r.end) THEN LOOP;
IF r.run<r2 THEN r2Gain←MIN[r2Gain,r2-r.run];
IF r.run>r3 THEN r3Gain←MIN[r3Gain,r.run-r3];
ENDLOOP;
SELECT index FROM
0=>IF r2Gain>r3Gain THEN c3.offset←-1 ELSE c2.offset←-1;
1=>c3.offset←1;
2=>c3.offset←1;
3=>c2.offset←1;
4=>c2.offset←1;--don't care
5=>c2.offset←-1;
6=>c2.offset←1;
7=>c3.offset←-1;
8=>IF r2Gain>r3Gain THEN c3.offset←1 ELSE c2.offset←1;
ENDCASE=>Error;
END; END;
OffsetJogs:PROCEDURE[rect:RectanglePtr,event:INTEGER]=BEGIN
FOR cl:ConnListPtr←rect.conns, cl.t UNTIL cl=NIL DO
con:ConnPtr=cl.h;
IF con.event#event OR con.offset#noOffset THEN LOOP;
FOR j:INTEGER IN [0..1] UNTIL con.offset#noOffset DO
this:INTEGER←0;
preferR,legalR,emptyR:INTEGER←noOffset;
preferL,legalL,emptyL:INTEGER←-noOffset;
SELECT con.startKind FROM left,right=>NULL; ENDCASE=>Error;
SELECT con.endKind FROM left,right=>NULL; ENDCASE=>Error;
FOR w:ConnListPtr←rect.conns, w.t UNTIL w=NIL DO
con2:ConnPtr=w.h;
plus:BOOLEAN←FALSE;
left,right:BOOLEAN←FALSE;
off:Lambda=con2.offset;
IF con2.event#event OR con2.circuit=con.circuit OR con2.offset=noOffset
THEN LOOP;
IF con2.start>con.end OR con.start>con2.end THEN LOOP;
IF con2.end=con.start THEN
{IF con.startKind=right THEN right←TRUE ELSE left←TRUE};
IF con2.start=con.end THEN
{IF con.endKind=right THEN right←TRUE ELSE left←TRUE};
SELECT con2.end-con.end FROM
0 =>SELECT con2.start-con.start FROM
0=>left←right←TRUE;
ENDCASE=>IF con.endKind=right THEN {right←plus←TRUE} ELSE left←TRUE;
>0 =>SELECT con2.start-con.start FROM
0=>IF con.startKind=right THEN {right←plus←TRUE} ELSE left←TRUE;
>0=>plus←FALSE;
ENDCASE=>plus←off+1<=preferL;
ENDCASE=>SELECT con2.start-con.start FROM
0=>IF con.startKind=right THEN {right←plus←TRUE} ELSE left←TRUE;
>0=>plus←off+1<=preferL;
ENDCASE=>plus←TRUE;
IF plus THEN preferL←MAX[off+1,preferL] ELSE preferR←MIN[off-1,preferR];
emptyL←MAX[emptyL,off+1];
emptyR←MIN[emptyR,off-1];
IF right THEN {IF legalL#-noOffset THEN Error ELSE legalL←off+1};
IF left THEN {IF legalR# noOffset THEN Error ELSE legalR←off-1};
ENDLOOP;
IF legalL#-noOffset AND legalR#noOffset THEN {SplitConn[rect,event,cl,j]; LOOP};
this←IF legalL#-noOffset OR legalR=noOffset AND (preferL#-noOffset OR preferR=noOffset)
THEN MAX[0,emptyL] ELSE MIN[0,emptyR];
IF this NOT IN [legalL..legalR] OR this IN (emptyR..emptyL) THEN Error;
con.offset←this;
IF this=noOffset OR this=-noOffset THEN Error;
IF event=-1 AND this<-1 THEN Error;
IF event=-1 AND this<0 THEN MoveConns[rect,event,1];
IF event=bigRun AND this>-1 THEN MoveConns[rect,event,-1];
IF event=bigRun AND this>0 THEN MoveConns[rect,event,-1];
ENDLOOP; ENDLOOP;
END;
MoveConns:PROCEDURE[rect:RectanglePtr,event,delta:INTEGER]=BEGIN
Sub:PROCEDURE[rect:RectanglePtr,c:ConnPtr]=
{IF c.event=event AND c.offset#noOffset THEN c.offset←c.offset+delta};
EnumerateConns[rect,Sub];
END;
SplitConn:PROCEDURE[rect:RectanglePtr,event:EventNo,cl:ConnListPtr,j:INTEGER]=
BEGIN
con:ConnPtr=cl.h;
yy:RunNo=FindEmptyPlace[rect,event,con.start];
IF yy=-1 OR j=1 THEN {Error; con.offset←0; RETURN};
BEGIN
t:BOOLEAN=IF con.startRun=NIL THEN (event=-1)
ELSE IF con.endRun=NIL THEN (event#bigRun)
ELSE con.startRun.start<con.endRun.end;
con3:ConnPtr=AllocateConn[];
run3:RunPtr=AllocateRun[];
listC:ConnListPtr=AllocateList[];
listR:RunListPtr=AllocateList[];
listC↑←[con3,cl.t];
cl.t←listC;
listR↑←[run3,rect.runs];
rect.runs←listR;
con3↑←con↑;
con.endRun←con3.startRun←run3;
run3↑←[con.circuit,yy,con.event,con.event,0,0,rect];
IF t THEN {run3.startConn←con; run3.endConn←con3}
ELSE {run3.startConn←con3; run3.endConn←con};
con.end←yy;
con.endKind←IF con.startKind=left THEN right ELSE left;
IF con.start>con.end THEN SwapEnds[con];
con3.start←yy;
con3.startKind←IF con3.endKind=left THEN right ELSE left;
IF con3.start>con3.end THEN SwapEnds[con3];
END;
END;
FindEmptyPlace:PROCEDURE[rect:RectanglePtr,event:EventNo,near:RunNo]
RETURNS[RunNo]=BEGIN
Try:PROCEDURE[where:RunNo] RETURNS[BOOLEAN]=BEGIN
IF where NOT IN [0..rect.sizeC.y) THEN RETURN[FALSE];
FOR rl:RunListPtr←rect.runs,rl.t UNTIL rl=NIL DO
r:RunPtr=rl.h;
IF r.run=where AND event IN [r.start..r.end] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
END;
FOR i:INTEGER IN [0..rect.sizeC.y) DO
IF Try[near+i] THEN RETURN[near+i];
IF Try[near-i] THEN RETURN[near-i];
ENDLOOP;
RETURN[-1];
END;
SwapEnds:PROCEDURE[c:ConnPtr]=BEGIN
{t:RunNo=c.start; c.start←c.end; c.end←t};
{q:ConnKind=c.startKind; c.startKind←c.endKind; c.endKind←q};
END;
SetRunOffsets:PROCEDURE[rect:RectanglePtr]=BEGIN
FOR rl:RunListPtr←rect.runs,rl.t UNTIL rl=NIL DO
run:RunPtr=rl.h;
run.startOff←run.endOff←0;
-- use start and end runs
FOR cl:ConnListPtr←rect.conns,cl.t UNTIL cl=NIL DO
con:ConnPtr=cl.h;
IF con.circuit#run.circuit THEN LOOP;
IF run.run#con.start AND run.run#con.end
AND (con.event=-1 OR con.event=bigRun) THEN LOOP;
IF con.event#run.start AND con.event#run.end THEN LOOP;
{end:BOOLEAN=IF run.start#run.end THEN run.end=con.event ELSE
(run.run=con.start AND con.startKind=left
OR run.run=con.end AND con.endKind=left);
IF end THEN run.endOff←con.offset ELSE run.startOff←con.offset};
ENDLOOP;
ENDLOOP;
END;
FixJogColor:PROCEDURE[rect:RectanglePtr,con:ConnPtr]=BEGIN
event:EventPtr=con.eventPtr;
eventNo:INTEGER=con.event;
con.level←blue;
con.closeS←con.closeE←FALSE;
IF con.start=-1 AND con.end=bigRun AND
(event.level=red OR event.opposite.level=red)
THEN {con.level←red; RETURN};
IF event#NIL AND event.level=red THEN {con.level←red; RETURN};
FOR rl:RunListPtr←rect.runs,rl.t UNTIL rl=NIL DO
run:RunPtr=rl.h;
IF run.circuit=con.circuit THEN LOOP;
IF run.run NOT IN [con.start..con.end] THEN LOOP;
IF eventNo NOT IN [run.start..run.end] THEN LOOP;
IF con.offset=0 THEN BEGIN
IF eventNo= run.start AND con.offset<run.startOff THEN LOOP;
IF eventNo= run.end AND con.offset>run.endOff THEN LOOP;
END;
con.level←red;
IF con.start=-1 AND run.run=0 AND
(event.where=bottom AND event.level=blue
OR event.where=top AND event.opposite.level=blue)
THEN con.closeS←TRUE;
IF con.end=bigRun AND run.run>=rect.sizeC.y-1 AND
(event.where=top AND event.level=blue
OR event.where=bottom AND event.opposite.level=blue)
THEN con.closeE←TRUE;
ENDLOOP;
END;
CheckCrosses:PROCEDURE[rect:RectanglePtr]=BEGIN
FOR cl:ConnListPtr←rect.conns,cl.t UNTIL cl=NIL DO
con:ConnPtr=cl.h;
IF con.offset=noOffset OR con.offset=-noOffset THEN Error;
ENDLOOP;
END;
ShowCrossesX:PROCEDURE[rect:RectanglePtr]=BEGIN
Store:PROCEDURE[c:CHARACTER,x:INTEGER]=BEGIN
IF x NOT IN [0..line/2) THEN
{x←x-limit+line-1; IF x NOT IN [line/2..line) THEN RETURN};
IF c='r AND s[x]#' THEN c←'R;
IF c='c THEN {IF s[x]=' THEN c←'b ELSE RETURN};
s[x]←c;
END;
runMax:INTEGER=rect.sizeC.y;
limit:CARDINAL=4+2*runMax;
line:CARDINAL=40;
limitE:CARDINAL=MAX[limit-1,line/2];
s:STRING←[line];
ShowLabel["CROSSES "];
ShowDecimal[rect.channelNo];
Return[];
FOR i:INTEGER IN [0..rect.sizeC.x] DO
max:INTEGER←-noOffset;
min:INTEGER←noOffset;
FOR cl:ConnListPtr←rect.conns,cl.t UNTIL cl=NIL DO
con:ConnPtr=cl.h;
IF con.event#i THEN LOOP;
max←MAX[max,con.offset];
min←MIN[min,con.offset];
ENDLOOP;
FOR z:INTEGER IN [min..max] DO
Return[];
Clear[s];
IF z=0 THEN FOR t:EventListPtr←rect.events, t.t UNTIL t=NIL DO
e:EventPtr←t.h;
IF i # e.index THEN LOOP;
IF e.where=top THEN s[line-1]←ShowCircuit[e.circuit];
IF e.where=bottom THEN s[0]←ShowCircuit[e.circuit];
ENDLOOP;
FOR cl:ConnListPtr←rect.conns,cl.t UNTIL cl=NIL DO
con:ConnPtr=cl.h;
stop:INTEGER=IF con.end=bigRun THEN limitE ELSE 2+2*con.end;
IF con.event#i OR con.offset#z THEN LOOP;
FOR j:[0..10000) IN [2+2*con.start..stop]
DO IF ~(j=0 OR j=limit) THEN Store['b,j]; ENDLOOP;
ENDLOOP;
FOR cl:ConnListPtr←rect.conns,cl.t UNTIL cl=NIL DO
con:ConnPtr=cl.h;
IF con.event#i OR con.offset=z THEN LOOP;
IF con.startKind=tee OR con.startKind=right AND con.offset<z
OR con.startKind=left AND con.offset>z THEN Store['b,2+2*con.start];
IF con.endKind=tee OR con.endKind=right AND con.offset<z
OR con.endKind=left AND con.offset>z THEN Store['b,2+2*con.end];
ENDLOOP;
FOR rl:RunListPtr←rect.runs,rl.t UNTIL rl=NIL DO
r:RunPtr=rl.h; IF i IN (r.start..r.end) THEN Store['c,2+2*r.run];
ENDLOOP;
ShowString[s];
ENDLOOP;
Clear[s];
FOR rl:RunListPtr←rect.runs,rl.t UNTIL rl=NIL DO
r:RunPtr=rl.h;
IF i IN [r.start..r.end) THEN Store['b,2+2*r.run];
ENDLOOP;
FOR zz:INTEGER IN [0..3) DO Return[]; ShowString[s]; ENDLOOP;
ENDLOOP;
END;
Convert:PROCEDURE[x:ConnKind] RETURNS[CHARACTER]=BEGIN
RETURN[SELECT x FROM tee=>'t, right=>'r, left=>'l, edge=>'e, none=>'x, ENDCASE=>'z];
END;
SetX:PROCEDURE[rect:RectanglePtr,c1:ConnPtr]=BEGIN
span:Lambda=IF rect.orient=hor THEN rect.sizeL.x ELSE rect.sizeL.y;
ok1:PROCEDURE[c:ConnPtr,x:Lambda] RETURNS[BOOLEAN]=BEGIN
e1:EventPtr=c.eventPtr;
e2:EventPtr=IF e1=NIL THEN NIL ELSE e1.opposite;
IF e1=NIL THEN RETURN[TRUE];
IF x NOT IN [0..span) THEN RETURN[FALSE];
RETURN[(e1.next=NIL OR e1.next.offset>x+6)
AND (e1.prev=NIL OR e1.prev.offset<x-6)
AND (e2.next=NIL OR e2.next.offset>x+6 OR Decoupled[c,e2.next.conn])
AND (e2.prev=NIL OR e2.prev.offset<x-6 OR Decoupled[c,e2.prev.conn])
];
END;
IF c1.lambdaX#-1 THEN RETURN;
IF c1.event NOT IN [0..bigRun) THEN BEGIN
IF c1.event<0 THEN c1.lambdaX←7*c1.offset;
IF c1.event>=bigRun THEN c1.lambdaX←
(IF rect.orient=hor THEN rect.sizeL.x ELSE rect.sizeL.y) + 7*c1.offset;
RETURN;
END;
BEGIN
e1:EventPtr=c1.eventPtr;
e2:EventPtr=e1.opposite;
IF e2=NIL THEN {c1.lambdaX←e1.offset; RETURN};
BEGIN
lSub:PROCEDURE[t:Lambda] RETURNS[b:BOOLEAN]=BEGIN
b←ok1[c1,t] AND ok1[c2,t+del];
IF b THEN {c1.lambdaX←t; c2.lambdaX←t+del};
END;
c2:ConnPtr=e2.conn;
del:Lambda=7*(c2.offset-c1.offset);
x1:Lambda=e1.offset;
x2:Lambda=e2.offset;
xmin:Lambda=MIN[x1,x2];
xmax:Lambda=MAX[x1,x2];
IF Decoupled[c1,c2] THEN BEGIN
IF ok1[c1,x1] THEN
{c1.lambdaX←x1; IF ok1[c2,x2] THEN c2.lambdaX←x2; RETURN};
FOR t:INTEGER IN [x1-6..x1+6] DO
IF ok1[c1,t] THEN {c1.lambdaX←t; RETURN};
ENDLOOP;
Error;
RETURN;
END;
IF c1.offset=0 THEN {IF lSub[x1] OR lSub[x2] THEN RETURN}
ELSE {IF lSub[x2] OR lSub[x1] THEN RETURN};
FOR t:Lambda IN (xmin..xmax) DO IF lSub[t] THEN RETURN; ENDLOOP;
FOR t:Lambda IN [xmin-6..xmin) DO IF lSub[t] THEN RETURN; ENDLOOP;
FOR t:Lambda IN (xmax..xmax+6] DO IF lSub[t] THEN RETURN; ENDLOOP;
END;
END;
END;
Decoupled:PROCEDURE [c1,c2:ConnPtr] RETURNS[BOOLEAN]=
{RETURN[c1.end<c2.start OR c2.end<c1.start]};
CheckMargins:PROCEDURE[rect:RectanglePtr,con:ConnPtr]=BEGIN
IF (con.start=-1) = (con.end=bigRun)
OR con.eventPtr=NIL
OR con.eventPtr.opposite=NIL
OR con.lambdaX=con.eventPtr.offset THEN RETURN ELSE BEGIN
con2:ConnPtr=con.eventPtr.opposite.conn;
IF con.start=-1 AND con2.start=0 THEN BEGIN
t:INTEGER←IF con.closeS THEN 9 ELSE 6;
IF rect.orient=hor
THEN {t←t-rect.levelers.s; IF rect.margins.s<t
THEN {rect.margins.s←t; marginChanged←TRUE}}
ELSE {t←t-rect.levelers.e; IF rect.margins.e<t
THEN {rect.margins.e←t; marginChanged←TRUE}};
END;
IF con.end=bigRun AND con2.end>=rect.sizeC.y-1 THEN BEGIN
t:INTEGER←IF con.closeE THEN 9 ELSE 6;
IF rect.orient=hor
THEN {t←t-rect.levelers.n; IF rect.margins.n<t
THEN {rect.margins.n←t; marginChanged←TRUE}}
ELSE {t←t-rect.levelers.w; IF rect.margins.w<t
THEN {rect.margins.w←t; marginChanged←TRUE}};
END;
END; END;
END.