-- File DisjointIO.mesa
-- Get input from Magic database and make direct calls on Disjoint
-- translates and rotates are removed on the fly
-- also has routines to output CIF
-- Written by Martin Newell/Dan Fitzpatrick, March 1981
-- Last updated (Pilot): 22-Jul-81 16:54:25

DIRECTORY

AuxIntDefs: FROM "AuxIntDefs" USING [IExpand, IGetRootID, IGetFirstSymbol,
	IGetNextSymbol, IStop, ISymBB],
AuxOutputDefs: FROM "AuxOutputDefs",
DisjointIODefs: FROM "DisjointIODefs",
DisjointAllocDefs: FROM "DisjointAllocDefs" USING
	[MakeSymbol, MakeInstance, MakeGeometry, AllocateRectangle, FreeRectangle, EnumerateSymbols],
DisjointPropDefs: FROM "DisjointPropDefs" USING [AllocPropID, PutProp, GetProp],
DisjointTypes: FROM "DisjointTypes" USING [DisCell, Symbol, Rectangle, Instance, Geometry, PIP, PropID],
Inline: FROM "Inline" USING [LowHalf, BITSHIFT, BITAND],
IntDefs: FROM "IntDefs" USING [IBoundBox],
IntStorageDefs: FROM "IntStorageDefs" USING [ObjectName, NilObjectName],
IntTransDefs: FROM "IntTransDefs" USING [Transform, TransformRecord],
IODefs: FROM "IODefs" USING [CR, WriteLine, WriteString, WriteDecimal,
	WriteChar, GetOutputStream, SetOutputStream],
JaMFnsDefs: FROM "JaMFnsDefs" USING [GetJaMBreak, SetJaMBreak],
ParserTypeDefs: FROM "ParserTypeDefs" USING [Point, Path],
Real: FROM "Real" USING [Fix, WriteReal],
StreamDefs: FROM "StreamDefs" USING [StreamHandle, NewByteStream, Append, Write],
String: FROM "String" USING[AppendDecimal, AppendLongDecimal],
SystemDefs: FROM "SystemDefs" USING [AllocateHeapNode, FreeHeapNode];

DisjointIO: PROGRAM
IMPORTS AuxIntDefs, DisjointAllocDefs, DisjointPropDefs, Inline, IntDefs, IODefs, JaMFnsDefs, Real, StreamDefs, String, SystemDefs
EXPORTS DisjointIODefs, AuxOutputDefs =

BEGIN OPEN AuxIntDefs, AuxOutputDefs, DisjointAllocDefs, DisjointPropDefs, DisjointTypes, Inline, IntDefs, IntStorageDefs, IntTransDefs, IODefs, JaMFnsDefs, ParserTypeDefs, Real, StreamDefs, String, SystemDefs;

CIFInput: PUBLIC PROCEDURE[allSymbols: BOOLEAN, parent: Symbol] =
--Get layout from Magic database and make direct calls on Disjoint
--allSymbols is true for ALL symbols, false for REFERENCED only
--mainline will be hung on parent
	BEGIN
	symbol: ObjectName;
	cifNum: CARDINAL;
	orient: CARDINAL;

	ParentSymbol ← parent;
	Aborted ← FALSE;
	InitDict[256];
	IF allSymbols THEN --get handle on all symbols, even if not referenced
	{	FOR s:ObjectName ← IGetFirstSymbol[],IGetNextSymbol[]
				UNTIL s=NilObjectName DO
			[] ← Lookup[s, 0];
		ENDLOOP;
	};
--provoke initial entries in dictionary
	ScanningTopLevel ← TRUE;
	Rotation ← 0;
	Mirror ← FALSE;
	IExpand[IGetRootID[]];
	IF Aborted THEN
	{	Abort[];
		RETURN;
	};
	ScanningTopLevel ← FALSE;
--output symbols
	DoHeader[];
	[symbol,orient,cifNum] ← GetNonProcessedEntry[];
	UNTIL cifNum=0 DO
		DoSymbolHeader[symbol,orient];
		IExpand[symbol];
		IF Aborted THEN
		{	Abort[];
			RETURN;
		};
		DoSymbolTrailer[symbol];
		[symbol,orient,cifNum] ← GetNonProcessedEntry[];
	ENDLOOP;
--output mainline
	CurrentSymbol ← ParentSymbol;
	Rotation ← 0;
	Mirror ← FALSE;
	IExpand[IGetRootID[]];
	IF Aborted THEN
	{	Abort[];
		RETURN;
	};
	DoTrailer[];
	FreeDict[];
	END;

Abort: PROCEDURE[] =
	BEGIN
	WriteLine["***Aborted***"];
	DoTrailer[];
	FreeDict[];
	END;

DoHeader: PROCEDURE =
	BEGIN
	l,b,r,t: LONG INTEGER;
	rec,next: Rectangle;
	window: Rectangle ← AllocateRectangle[];
--set window of parent to be BB of whole layout
	[l,r,b,t] ← IBoundBox[];
	window↑ ← [
		next: NIL,
		l:l, b:b, r:r, t:t
	];
	FOR rec ← ParentSymbol.windows, next UNTIL rec=NIL DO
		next ← rec.next;
		FreeRectangle[rec];
	ENDLOOP;
	ParentSymbol.windows ← window;
	CurrentLayer ← 32000; --i.e. not set
	END;

DoTrailer: PROCEDURE =
	BEGIN
	END;

DoSymbolHeader: PROCEDURE [symbol: ObjectName, orient: CARDINAL] =
	BEGIN
	n: INTEGER;
	name: STRING ← [20];
	l,b,r,t: LONG INTEGER;
--set up current Rotation and Mirror from orient
	Rotation ← BITSHIFT[orient,-1];
	Mirror ← BITAND[orient,1]#0;
	n ← Lookup[symbol,orient];
	name.length ← 0;
	AppendDecimal[name,n];
	[l,r,b,t] ← ISymBB[symbol];
--Rotate and mirror bb
	[l,b] ← RotMirXY[Rotation, Mirror, l,b];
	[r,t] ← RotMirXY[Rotation, Mirror, r,t];
	CurrentSymbol ← MakeSymbol[name, MIN[l,r],MIN[b,t],MAX[l,r],MAX[b,t]];
	SaveLayer ← CurrentLayer;
	CurrentLayer ← 32000;
	END;

DoSymbolTrailer: PROCEDURE[symbol: ObjectName] =
	BEGIN
	CurrentLayer ← SaveLayer;
	END;

--Orientation is packed as a 3 bit field, [0..3][0..1] representing ccw rotation in units of 90 degrees, and mirroring of the x coordinate.  These are considered to be applied to the object in the order: mirror, rotation.

Rotation: CARDINAL; --current ccw rotation of symbol being expanded
Mirror: BOOLEAN; --current mirror status of symbol being expanded

--Procedures that export AuxOutputDefs--

AuxWire: PUBLIC PROCEDURE [layerName: CARDINAL, width: LONG CARDINAL, a: ParserTypeDefs.Path] =
	BEGIN
	WriteLine["Wire not implemented in CIFDJ"];
	END;

AuxFlash: PUBLIC PROCEDURE [layerName: CARDINAL, diameter: LONG CARDINAL, center: ParserTypeDefs.Point] =
	BEGIN
	WriteLine["Flash not implemented in CIFDJ"];
	END;

AuxPolygon: PUBLIC PROCEDURE [layerName: CARDINAL, a: ParserTypeDefs.Path] =
	BEGIN
	WriteLine["Polygon not implemented in CIFDJ"];
	END;

AuxBox: PUBLIC PROCEDURE [layerName: CARDINAL, length, width: LONG CARDINAL,
			center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] =
	BEGIN
	len,wid,length2,width2: LONG CARDINAL;
	xc,yc: LONG INTEGER;
	rot: BOOLEAN;
	IF GetJaMBreak[] THEN
	{	IStop[];
		SetJaMBreak[FALSE];
		Aborted ← TRUE;
		RETURN;
	};
	IF ScanningTopLevel THEN RETURN;
	IF yRotation#0 AND xRotation#0 THEN
	{	WriteLine["CIFDisjoint - oblique rectangles not implemented"];
		RETURN;
	};
	SetLayer[layerName];
	rot ← ((Rotation=0 OR Rotation=2) AND yRotation#0) OR
			((Rotation=1 OR Rotation=3) AND yRotation=0); --xor
	IF rot THEN {len ← width; wid ← length; }
	ELSE {len ← length; wid ← width; };
	[xc,yc] ← RotMirXY[Rotation, Mirror, center.x,center.y];
	length2 ← len/2;
	IF length2+length2<len THEN length2 ← length2 + 1;
	width2 ← wid/2;
	IF width2+width2<wid THEN width2 ← width2 + 1;
	[] ← MakeGeometry[CurrentSymbol, CurrentLayer,
				xc-length2,yc-width2,xc+length2,yc+width2];
	END;

AuxUserObject: PUBLIC PROCEDURE [layerName: CARDINAL,
		size: CARDINAL, data: POINTER TO UNSPECIFIED] =
	BEGIN
	END;

AuxCall: PUBLIC PROCEDURE [symbol: ObjectName, cifNumber: LONG CARDINAL, transform: Transform] =
	BEGIN
	save: StreamHandle ← GetOutputStream[];
	t: TransformRecord;
	mx: BOOLEAN ← FALSE;
	rot: INTEGER ← 0;
	tx: LONG INTEGER ← 0;
	ty: LONG INTEGER ← 0;
	orient: INTEGER;
	c,s: REAL;
	cifNum: INTEGER;
	name: STRING ← [20];
--unpick transform in [tran][rot][mx]<object>
	t ← transform↑;
--get local mirror
	IF (t.a11*t.a22-t.a21*t.a12)<0 THEN --determinant negative - need mirror
	{	t.a11 ← -t.a11;
		t.a12 ← -t.a12;
		mx ← TRUE;
	};
--get local rotate
	c ← t.a11/t.a33;
	s ← t.a12/t.a33;
	IF s#0 OR c<0 THEN
	{	SELECT TRUE FROM
		c=0	=> rot ← IF s<0 THEN 3 ELSE 1;
		s=0	=> rot ← IF c<0 THEN 2 ELSE 4;
		ENDCASE =>
			WriteLine["CIFDJ can't handle non-90 degree rotations"];
	};
--get local translation
	IF t.a31#0 OR t.a32#0 THEN {tx ← Fix[t.a31/t.a33]; ty ← Fix[t.a32/t.a33];};
--compose local transformation with current Rotation and Mirror
	IF Mirror THEN
	{	rot ← (4-rot) MOD 4;
		mx ← NOT mx;
	};
	IF Rotation#0 THEN rot ← (rot + Rotation) MOD 4;
	[tx,ty] ← RotMirXY[Rotation, Mirror, tx,ty];
	orient ← (rot + rot) + (IF mx THEN 1 ELSE 0);
--look up symbol, either to make initial entry or to use it
	cifNum ← Lookup[symbol,orient]; --need to do this always
	IF ScanningTopLevel THEN RETURN;
--output call with only translate
	name.length ← 0;
	AppendDecimal[name,cifNum];
	[] ← MakeInstance[CurrentSymbol, name, tx,ty];
	END;

two22: LONG INTEGER ← 20000000B;

SetLayer: PUBLIC PROCEDURE [layerName: CARDINAL] =
	BEGIN
	CurrentLayer ← layerName;
	END;

RotMirXY: PROCEDURE [r: INTEGER, m: BOOLEAN, x,y: LONG UNSPECIFIED]
		RETURNS[LONG UNSPECIFIED,LONG UNSPECIFIED] =
-- mirror x,y in x if m TRUE, then rotate result by r*90 degrees
--works for both REAL and LONG INTEGER for x and y
	BEGIN
	IF m THEN x ← -x;
	SELECT r FROM
	0 => RETURN[x,y];
	1 => RETURN[-y,x];
	2 => RETURN[-x,-y];
	3 => RETURN[y,-x];
	ENDCASE =>
	{	x1,y1: LONG UNSPECIFIED;
		[x1,y1] ← RotMirXY[r MOD 4, FALSE, x,y];
		RETURN[x1,y1];
	};
	END;

--Dictionary

--Each entry in the dictionary has an ObjectName for a key, and an array of CARDINALs for values.  On Lookup, both the ObjectName and an index are provided, and the indexed entry in the corresponding entry is returned as the value

DictEntry: TYPE = POINTER TO DictEntryRecord;
DictEntryRecord: TYPE = RECORD [
	next: DictEntry,		--global list in order in which entries are made
	ovflow: DictEntry,	--overflow list from entry in hash table
	key: ObjectName,		--internal ID of symbol
	value: ARRAY [0..7] OF INTEGER	--generated CIF symbols # for output, one for each possible rotation and mirroring. -ve means not yet processed and symbol is -.value[i], 0 means unset
];

DictHead: DictEntry;
DictTail: DictEntry;
HTable: DESCRIPTOR FOR ARRAY OF DictEntry;
LastProcessedEntry: DictEntry; --points to last processed entry
Value: CARDINAL;

InitDict: PROCEDURE[hashlength: CARDINAL] =
	BEGIN
	i: CARDINAL;
	DictHead ← NIL;
	DictTail ← NIL;
	HTable ←
		DESCRIPTOR[AllocateHeapNode[hashlength*SIZE[DictEntry]],hashlength];
	FOR i IN [0..hashlength) DO HTable[i] ← NIL; ENDLOOP;
	LastProcessedEntry ← NIL;
	Value ← 0;
	END;

Lookup: PROCEDURE[key: ObjectName, index: CARDINAL] RETURNS[INTEGER] =
--Return value[index] associated with key.  If key not found, or
-- value[index] not set, then generate a value, and return it.
	BEGIN
	de: DictEntry;
	h: CARDINAL;
	[de,h] ← Where[key];
	IF de=NIL THEN
	{	de ← AllocateHeapNode[SIZE[DictEntryRecord]];
		de↑ ← [
			next: NIL,
			ovflow: HTable[h],
			key: key,
			value: ALL[0]];
		HTable[h] ← de;
		IF DictTail=NIL THEN DictHead ← DictTail ← de
		ELSE
		{	DictTail.next ← de;
			DictTail ← de;
		};
	};
	IF de.value[index]=0 THEN
	{	Value ← Value + 1;
		de.value[index] ← -Value; --sign bit used to indicate not yet expanded
	};
	RETURN[ABS[de.value[index]]];
	END;

GetNonProcessedEntry: PROCEDURE
		RETURNS[key: ObjectName, index: CARDINAL, value: CARDINAL] =
--Search all entries, starting at LastProcessedEntry, and return its key, index, and value, if any, having marked it as processed
	BEGIN
	startEntry: DictEntry ← LastProcessedEntry;
	DO
	--check LastProcessedEntry to see if it contains any unprocessed element
		IF LastProcessedEntry#NIL THEN
		{	FOR i:CARDINAL IN [0..7] DO
				IF LastProcessedEntry.value[i]<0 THEN
				{	LastProcessedEntry.value[i] ← -LastProcessedEntry.value[i];
					RETURN[LastProcessedEntry.key, i, LastProcessedEntry.value[i]];
				};
			ENDLOOP;	
		};
	--step LastProcessedEntry by one
		IF LastProcessedEntry=NIL THEN LastProcessedEntry ← DictHead
		ELSE LastProcessedEntry ← LastProcessedEntry.next;
	--
		IF LastProcessedEntry=startEntry THEN
			RETURN[NilObjectName,0,0]; --been all round
	ENDLOOP;
	END;

FreeDict: PROCEDURE =
	BEGIN
	de,nextde: DictEntry;
	FOR de ← DictHead, nextde UNTIL de=NIL DO
		nextde ← de.next;
		FreeHeapNode[de];
	ENDLOOP;
	FreeHeapNode[BASE[HTable]];
	END;

Where: PROCEDURE[key: ObjectName] RETURNS[de: DictEntry, h: CARDINAL] =
--h is result of hashing name
	BEGIN
	h ← Hash[key, LENGTH[HTable]];
	FOR de  ← HTable[h], de.ovflow UNTIL de=NIL OR key=de.key
		DO --nothing-- ENDLOOP;
	END;

Hash: PROCEDURE[key: ObjectName, range: CARDINAL] RETURNS[h: CARDINAL] =
--h is result of hashing name into [0..range)
	BEGIN
	i: CARDINAL ← LowHalf[key];
	RETURN[i MOD range];
	END;

--Utilities

WriteLongDecimal: PROCEDURE[n: LONG INTEGER] =
	BEGIN
	s: STRING ← [50];
	AppendLongDecimal[s,n];
	WriteString[s];
	END;

WriteFloat: PROCEDURE[r: REAL] =
	BEGIN
	WriteReal[WriteChar,r];
	END;

-- parameters
ScanningTopLevel: BOOLEAN;
SaveLayer: CARDINAL;
CurrentLayer: CARDINAL;
CurrentSymbol: Symbol;
ParentSymbol: Symbol;
Aborted: BOOLEAN;


-- Output Procedures

CIFOutput: PUBLIC PROCEDURE [symbol: Symbol, callOnce: BOOLEAN,
		fileName: STRING] =
	BEGIN
	countID: PropID ← AllocPropID[];
	markID: PropID ← AllocPropID[];

	Mark: PROC[s: Symbol] RETURNS[BOOLEAN] =
		BEGIN
	-- mark = 1 means symbol has not yet been output
	-- count#0 means symbol can still be called
		count ← count + 1;
		PutProp[@s.prop,markID,1];
		PutProp[@s.prop,countID,count];
		RETURN[FALSE];
		END;

	WriteSymbolNumber: PROC[s: Symbol] =
		BEGIN
		--str: STRING ← [50];
		--AppendLongNumber[str,s,8];
		--WriteString[str];
		WriteDecimal[GetProp[s.prop,countID]];
		END;

	WriteLongDecimal: PROC[n: LONG INTEGER] =
		BEGIN
		str: STRING ← [50];
		AppendLongDecimal[str,n];
		WriteString[str];
		END;

	Out: PROC[s: Symbol] RETURNS[BOOLEAN] =
		BEGIN
		PutProp[@s.prop,markID,0];
		FOR in: Instance ← s.insts,in.next UNTIL in = NIL DO
			IF GetProp[in.symbol.prop,markID] = 1 THEN [] ← Out[in.symbol];
			ENDLOOP;
		WriteLine[""]; WriteString["DS "]; WriteSymbolNumber[s];
		WriteLine[";"];
		-- write geometry
		FOR g:Geometry ← s.geom,g.next UNTIL g = NIL DO
			WriteString["L N"]; WriteChar[LayerChar[g.layer]];	-- layer
			WriteString["; B "];
			WriteLongDecimal[Fix[g.r-g.l]]; WriteString[" "];		-- length
			WriteLongDecimal[Fix[g.t-g.b]]; WriteString[" "];		--	width
			WriteLongDecimal[Fix[(g.r+g.l)/2]]; WriteString[" "];	-- x Center
			WriteLongDecimal[Fix[(g.t+g.b)/2]]; WriteLine[";"];	-- y Center 
			ENDLOOP;
		FOR in: Instance ← s.insts,in.next UNTIL in = NIL DO
			IF GetProp[in.symbol.prop,countID]#0 THEN
			{	WriteString["C "]; WriteSymbolNumber[in.symbol];
				WriteString[" T "]; WriteLongDecimal[Fix[in.xOffset]];
				WriteString[" "]; WriteLongDecimal[Fix[in.yOffset]];
				WriteLine[";"];
				IF callOnce THEN PutProp[@in.symbol.prop,countID,0];
			};
			ENDLOOP;
		WriteLine["DF;"];
		RETURN[FALSE];
		END;

	count: CARDINAL ← 0;
	IF symbol # NIL THEN {
		cifFile: StreamHandle ← NewByteStream[fileName,Write+Append];
		default: StreamHandle ← GetOutputStream[];
		cifFile.reset[cifFile];
		SetOutputStream[cifFile];
		WriteString["(Generated by Disjoint from symbol "];
		WriteString[symbol.name]; WriteLine[");"];
		-- mark all the symbols
		[] ← EnumerateSymbols[Mark];
		[] ← Out[symbol];
		WriteLine[""]; WriteString["C "]; WriteSymbolNumber[symbol];
		WriteLine[";"];
		cifFile.destroy[cifFile];
		SetOutputStream[default];
		WriteDecimal[count]; WriteString[" symbols from "];
		WriteString[symbol.name]; WriteString[" in "]; WriteLine[fileName];
		};
	END;


PrintDisCells: PUBLIC PROCEDURE[disCellList: DisCell] =
	BEGIN
	FOR dc:DisCell ← disCellList, dc.next UNTIL dc=NIL DO
		PrintDisCell[dc];
	ENDLOOP;
	END;

PrintDisCell: PUBLIC PROCEDURE[disCell: DisCell] =
	BEGIN OPEN disCell;
	w: DisjointTypes.Rectangle;
	WriteLine["DisCell"];
	WriteString[" geom: "];
	FOR g:Geometry ← disCell.geom, g.next UNTIL g=NIL DO
		WriteChar['[]; WriteChar['N]; WriteChar[LayerChar[g.layer]];
		WriteChar[',]; WriteReal[WriteChar, g.l];
		WriteChar[',]; WriteReal[WriteChar, g.b];
		WriteChar[',]; WriteReal[WriteChar, g.r];
		WriteChar[',]; WriteReal[WriteChar, g.t];
		WriteChar[']];
	ENDLOOP;
	WriteChar[CR];
	WriteString[" instances: "];
	FOR p:PIP ← disCell.insts, p.next UNTIL p=NIL DO
		i: Instance ← p.inst;
		WriteString["{"""]; WriteString[i.symbol.name]; WriteChar['"];
		WriteChar[',]; WriteReal[WriteChar, i.xOffset];
		WriteChar[',]; WriteReal[WriteChar, i.yOffset];
		WriteChar['}];
	ENDLOOP;
	WriteChar[CR];
	WriteString[" windows: "];
	FOR w ← disCell.windows, w.next UNTIL w=NIL DO
		WriteChar['[]; WriteReal[WriteChar, w.l];
		WriteChar[',]; WriteReal[WriteChar, w.b];
		WriteChar[',]; WriteReal[WriteChar, w.r];
		WriteChar[',]; WriteReal[WriteChar, w.t];
		WriteChar[']];
	ENDLOOP;
	WriteChar[CR];
	END;


PrintSymbols: PUBLIC PROCEDURE =
	BEGIN
	PS: PROC[s: Symbol] RETURNS[BOOLEAN] =
		BEGIN
		PrintSymbol[s];
		RETURN[FALSE];
		END;
	[] ← EnumerateSymbols[PS];
	END;

PrintSymbol: PUBLIC PROCEDURE[s: Symbol] =
	BEGIN
	w: DisjointTypes.Rectangle;
	WriteString["Symbol: "]; WriteLine[s.name];
	WriteString[" geom: "];
	FOR g:Geometry ← s.geom, g.next UNTIL g=NIL DO
		WriteChar['[]; WriteChar['N]; WriteChar[LayerChar[g.layer]];
		WriteChar[',]; WriteReal[WriteChar,g.l];
		WriteChar[',]; WriteReal[WriteChar,g.b];
		WriteChar[',]; WriteReal[WriteChar,g.r];
		WriteChar[',]; WriteReal[WriteChar,g.t];
		WriteChar[']];
	ENDLOOP;
	WriteChar[CR];
	WriteString[" instances: "];
	FOR i:Instance ← s.insts,i.next UNTIL i=NIL DO
		WriteString["{"""]; WriteString[i.symbol.name]; WriteChar['"];
		WriteChar[',]; WriteReal[WriteChar,i.xOffset];
		WriteChar[',]; WriteReal[WriteChar,i.yOffset];
		WriteChar['}];
	ENDLOOP;
	WriteChar[CR];
	WriteString[" windows: "];
	FOR w ← s.windows, w.next UNTIL w=NIL DO
		WriteChar['[]; WriteReal[WriteChar,w.l];
		WriteChar[',]; WriteReal[WriteChar,w.b];
		WriteChar[',]; WriteReal[WriteChar,w.r];
		WriteChar[',]; WriteReal[WriteChar,w.t];
		WriteChar[']];
	ENDLOOP;
	WriteChar[CR];
	END;

LayerChar: ARRAY [0..15] OF CHARACTER ← ALL['X];

--Set up CIF Layer names
LayerChar[0] ← 'I;	--implant
LayerChar[1] ← 'D;	--diffusion
LayerChar[2] ← 'P;	--poly
LayerChar[3] ← 'C;	--contact
LayerChar[4] ← 'M;	--metal
LayerChar[5] ← 'B;	--buried
LayerChar[6] ← 'G;	--glass
LayerChar[7] ← 'X;	--undef


END.