-- NutsMain.mesa -- Mainline CIF-to-Chipmonk conversion -- Modifications: -- Dillon 19-Nov-84 9:58:40. Convert rectangular polygons to boxes -- Also convert illegal zero-width boxes to 1/2 lambda boxes -- Dillon February 10, 1985 10:41 AM. Convert wires to boxes -- Also implement Flashes as square Boxes -- Last Edit: See "herald:" below. DIRECTORY AuxIntDefs: FROM "AuxIntDefs" USING [IGetFirstSymbol, IGetNextSymbol, IGetRootID, ISymBB, IExpand], AuxOutputDefs: FROM "AuxOutputDefs" USING [], ImageDefs: FROM "ImageDefs" USING [AbortMesa], InlineDefs: FROM "InlineDefs" USING [BITOR,BITSHIFT, COPY, LowByte, LowHalf, HighHalf], IntDefs: FROM "IntDefs" USING [InitInterpreter, FinishInterpreter, IScaleLongInt, IUserObject], IntTransDefs: FROM "IntTransDefs" USING [Transform], IntStorageDefs: FROM "IntStorageDefs" USING [ObjectType, ObjectName, NilObjectName], IODefs: FROM "IODefs" USING [WriteString, WriteLine, ReadLine, ReadDecimal, WriteDecimal, Rubout, WriteChar, CR], NutsDictDefs: FROM "NutsDictDefs" USING [AllocateDict, Record, Lookup, What, FreeDict], OutputDefs: FROM "OutputDefs" USING [RelationType, VisibleType], ParserDefs: FROM "ParserDefs" USING [CommandType,InitParser,FinishParser, ParseStatement], ParserErrorDefs: FROM "ParserErrorDefs" USING [ParserAbort, ErrorType, Report], ParserInputDefs: FROM "ParserInputDefs" USING [InFromFile], ParserTypeDefs: FROM "ParserTypeDefs" USING [LinkedPoint, Point, Path], Real: FROM "Real" USING [Fix, Float], RealFns: FROM "RealFns" USING [SqRt], Storage: FROM "Storage" USING [CopyString, FreeString, Node, StringLength], StreamDefs: FROM "StreamDefs" USING [DiskHandle, NewWordStream, WriteAppend], StringDefs: FROM "StringDefs" USING [AppendChar, AppendDecimal, AppendString, EqualStrings, InvalidNumber, StringToLongNumber, WordsForString, UpperCase]; NutsMain: PROGRAM IMPORTS AuxIntDefs, ImageDefs, IntDefs, IODefs, InlineDefs, NutsDictDefs, ParserDefs, ParserErrorDefs, ParserInputDefs, Real, RealFns, Storage, StreamDefs, StringDefs EXPORTS AuxOutputDefs, OutputDefs = BEGIN herald: STRING ← "NUTS of February 10, 1985 10:41 AM"; -- by Dillon UCmdType: TYPE = {SymbolName,NodeName}; UserCmd: TYPE = RECORD [ type: UCmdType, name: STRING, x,y: LONG INTEGER, stringBody: ARRAY [0..0) OF WORD]; ChipLayers: ARRAY [0..7] OF CARDINAL = -- Mapping from MAGIC to Chipmonk layer numbers [4, -- MAGIC 0 = implant 1, -- MAGIC 1 = diffusion 2, -- MAGIC 2 = poly 0, -- MAGIC 3 = contact 3, -- MAGIC 4 = metal 6, -- MAGIC 5 = buried 5, -- MAGIC 6 = glass 7]; -- MAGIC 7 = undef chipSymNo: CARDINAL; ModeType: TYPE = {Idle, Marking, ScanningSymbol, CountingItems, PuttingDefn, PuttingItems}; Mode: ModeType; itemCount: CARDINAL; Name: STRING ← [50]; hasName: BOOLEAN ← FALSE; CurrentSymbol: IntStorageDefs.ObjectName; -- symbol whose definition is being processed FileRoot: STRING ← [40]; CifFileName: STRING ← [40]; ChipFileName: STRING ← [40]; ChipFile: StreamDefs.DiskHandle; Scale: CARDINAL ← 125; -- Half-lambda per cif unit prevProblem, prevPrevProblem: STRING ← NIL; -- to avoid explosion of warnings Mark: PROCEDURE [markee: IntStorageDefs.ObjectName] = -- recursive procedure eliminates forward symbol references: -- FOR all callee CALLED BY markee DO Mark[callee] ENDLOOP -- then record markee in dictionary BEGIN IF NutsDictDefs.Lookup[markee] = 0 THEN BEGIN AuxIntDefs.IExpand[markee]; -- mark all callees NutsDictDefs.Record[chipSymNo,markee]; -- then record self chipSymNo ← chipSymNo+1; -- ELSE already marked, nothing to do END; END; PutChipFile: PROCEDURE = BEGIN sequenceNumber, symbolCount: CARDINAL; PutWord [123751B]; -- Password for Chip file. PutWord [4]; -- Version number -- First count symbols, to determine size of dictionary symbolCount ← 0; CurrentSymbol ← AuxIntDefs.IGetFirstSymbol[]; UNTIL CurrentSymbol=IntStorageDefs.NilObjectName DO symbolCount ← symbolCount+1; CurrentSymbol ← AuxIntDefs.IGetNextSymbol[]; ENDLOOP; NutsDictDefs.AllocateDict[symbolCount]; PutWord [symbolCount]; -- Go through list of symbols, looking for symbols not yet in dictionary. -- For each such symbol, record first all symbols it calls, then itself. -- (Initially none are recorded, but this changes quickly!) chipSymNo ← 1; SetMode[Marking]; CurrentSymbol ← AuxIntDefs.IGetFirstSymbol[]; UNTIL CurrentSymbol=IntStorageDefs.NilObjectName DO Mark[CurrentSymbol]; CurrentSymbol ← AuxIntDefs.IGetNextSymbol[]; ENDLOOP; -- write out symbol definitions in sorted order SetMode[PuttingDefn]; FOR sequenceNumber IN [1..symbolCount] DO CurrentSymbol ← NutsDictDefs.What[sequenceNumber]; PutSymbolDef[CurrentSymbol, sequenceNumber]; ENDLOOP; CurrentSymbol ← AuxIntDefs.IGetRootID[]; SetMode[CountingItems]; AuxIntDefs.IExpand[CurrentSymbol]; -- this time, just to count items PutWord[itemCount]; SetMode[PuttingItems]; AuxIntDefs.IExpand[CurrentSymbol]; -- now for real SetMode[Idle]; END; PutSymbolDef: PROCEDURE [symbol: IntStorageDefs.ObjectName, chipSymNo: CARDINAL] = BEGIN l,r,b,t: LONG INTEGER; -- left,right,bottom,top of BBox in CIF units xSize,ySize: CARDINAL; -- dimensions of BBox in half-lambda PutWord [chipSymNo]; SetMode[ScanningSymbol]; AuxIntDefs.IExpand[symbol]; -- count items, find name PutString[GetName[chipSymNo]]; [l,r,b,t] ← AuxIntDefs.ISymBB[symbol]; xSize ← ChipScale[r-l]; ySize ← ChipScale[t-b]; PutWord[xSize]; PutWord[ySize]; PutWord[0]; -- reserved for future Chipmonks PutWord[itemCount]; SetMode[PuttingDefn]; AuxIntDefs.IExpand[symbol]; -- list contents of symbol END; ParseCifFile: PROCEDURE [fname: STRING] RETURNS [ok: BOOLEAN] = BEGIN ENABLE ParserErrorDefs.ParserAbort=>ImageDefs.AbortMesa; stmtCount: CARDINAL ← 0; IODefs.WriteString["Initializing Parser"]; ok←ParserDefs.InitParser[]; IF NOT ok THEN InternalError["problem Initializing Parser"]; IODefs.WriteLine[", Interpreter"]; ok←IntDefs.InitInterpreter[]; IF NOT ok THEN InternalError["problem Initializing Interpreter"]; ok ← ParserInputDefs.InFromFile[fname]; IF ok THEN BEGIN IODefs.WriteString["Parsing "]; IODefs.WriteString[fname]; IODefs.WriteString["..."]; UNTIL ParserDefs.ParseStatement[]=End DO stmtCount ← stmtCount + 1; IF stmtCount MOD 100 = 0 THEN BEGIN IODefs.WriteDecimal[stmtCount]; IODefs.WriteString["..."]; END; ENDLOOP; IF ParserDefs.FinishParser[] THEN IODefs.WriteLine["finished."]; END END; GetFileNames: PROCEDURE = BEGIN ENABLE IODefs.Rubout=> BEGIN IODefs.WriteLine["XXX"]; RETRY; END; IODefs.WriteString["File Name (no extension): "]; IODefs.ReadLine[FileRoot]; CifFileName.length ← 0; StringDefs.AppendString[to: CifFileName, from: FileRoot]; StringDefs.AppendString[to: CifFileName, from: ".cif"]; ChipFileName.length ← 0; StringDefs.AppendString[to: ChipFileName, from: FileRoot]; StringDefs.AppendString[to: ChipFileName, from: ".chip"]; END; GetLambda: PROCEDURE RETURNS [lambda: CARDINAL] = BEGIN ENABLE StringDefs.InvalidNumber=> BEGIN IODefs.WriteLine[" is invalid number!"]; RETRY; END; DO IODefs.WriteString["Lambda (CIF units): "]; lambda ← IODefs.ReadDecimal[]; IODefs.WriteLine[""]; IF (lambda MOD 2) = 0 THEN RETURN[lambda]; IODefs.WriteLine["must be even!"]; ENDLOOP; END; OpenChipFile: PROCEDURE [filename: STRING] = BEGIN ChipFile ← StreamDefs.NewWordStream[filename, StreamDefs.WriteAppend]; IODefs.WriteString["Writing "]; IODefs.WriteString[filename]; IODefs.WriteString["..."]; END; CloseChipFile: PROCEDURE = BEGIN ChipFile.destroy[ChipFile]; IODefs.WriteLine["finished."]; END; ResolveRotation: PROCEDURE [length, width: LONG CARDINAL, xRotation, yRotation: LONG INTEGER] RETURNS [xSize, ySize: LONG CARDINAL] = BEGIN -- (xRotation, yRotation) is direction vector; (1,0) = no rotation IF xRotation # 0 AND yRotation # 0 THEN BEGIN Warning ["45-degree","taken as no rotation"]; xSize ← width; ySize ← length; END ELSE SELECT TRUE FROM xRotation # 0 => -- no rotation BEGIN xSize ← length; ySize ← width; END; yRotation # 0 => -- 90 degrees BEGIN xSize ← width; ySize ← length; END; ENDCASE => BEGIN Warning ["zero vector","taken as no rotation"]; xSize ← width; ySize ← length; END; -- Hack to allow zero-dimension boxes to be expanded to 1/2 lambda. -- Some DRC programs outline errors with such objects. -- Treat negative sizes as errors. IF xSize = 0 THEN xSize ← Scale; IF ySize = 0 THEN ySize ← Scale; END; SetLambda: PROCEDURE [lambda: CARDINAL] = BEGIN Scale ← lambda/2; END; ChipScale: PROCEDURE [cifUnits: LONG INTEGER] RETURNS [INTEGER] = BEGIN quotient: LONG INTEGER; IF cifUnits MOD Scale # 0 THEN Warning ["Non-lambda-grid item","dimension truncated"]; quotient ← cifUnits/Scale; IF InlineDefs.HighHalf[quotient] NOT IN [-1..0] THEN BEGIN Warning ["Number too big to scale","taken as zero"]; RETURN[0]; END; RETURN [InlineDefs.LowHalf[quotient]]; END; PutNullItem: PROCEDURE[] = BEGIN PutWord[0]; -- x position PutWord[0]; -- y position PutWord[0]; -- orientation PutWord[0]; -- code for null item PutWord[0]; -- contents of null item PutWord[0]; -- zero properites END; PutWord: PROCEDURE[word: UNSPECIFIED] = BEGIN ChipFile.put[ChipFile, word]; END; PutString: PROCEDURE[string: STRING] = BEGIN bflg: BOOLEAN ← FALSE; save: CARDINAL; i: CARDINAL; PutByte: PROCEDURE [byte: CHARACTER] = BEGIN IF bflg THEN PutWord[InlineDefs.BITOR[byte, save]] ELSE save ← InlineDefs.BITSHIFT[byte, 8]; bflg ← NOT bflg; END; PutByte[InlineDefs.LowByte[string.length]]; FOR i IN [0..string.length) DO PutByte[string[i]]; ENDLOOP; PutByte[0C]; -- complete word if odd number bytes put so far END; Warning: PROCEDURE [problem, fixup: STRING] = BEGIN chipSymNo: CARDINAL; IF StringDefs.EqualStrings[problem, prevProblem] OR StringDefs.EqualStrings[problem, prevPrevProblem] THEN RETURN; Storage.FreeString[prevPrevProblem]; prevPrevProblem ← prevProblem; prevProblem ← Storage.CopyString[problem]; IODefs.WriteString["Warning: "]; IODefs.WriteString[problem]; IF (chipSymNo←NutsDictDefs.Lookup[CurrentSymbol]) > 0 THEN BEGIN IODefs.WriteString[" in "]; IODefs.WriteString[GetName[chipSymNo]]; END ELSE IF CurrentSymbol = AuxIntDefs.IGetRootID[] THEN IODefs.WriteString[" in top level"]; IODefs.WriteString[" - "]; IODefs.WriteLine[fixup]; END; InternalError: PROCEDURE [message: STRING] = BEGIN IODefs.WriteString["Fatal Internal Error: "]; IODefs.WriteLine[message]; CloseChipFile; ImageDefs.AbortMesa; END; GetName: PROCEDURE [chipSymNo: CARDINAL] RETURNS [STRING] = BEGIN i: CARDINAL; IF ~hasName THEN -- Invent name from file root, number. BEGIN Name.length ← 0; FOR i IN [0..FileRoot.length) DO StringDefs.AppendChar[Name,StringDefs.UpperCase[FileRoot[i]]]; ENDLOOP; StringDefs.AppendDecimal[Name,chipSymNo]; END; RETURN [Name]; END; SetMode: PROCEDURE [newMode: ModeType] = BEGIN Mode ← newMode; SELECT Mode FROM ScanningSymbol => BEGIN Name.length ← 0; hasName ← FALSE; END; ENDCASE; itemCount ← 0; END; CountItem: PROCEDURE = BEGIN itemCount ← itemCount + 1; END; -- Each routine below implements the output of one primitive geometric construct from CIF, called by Interpreter. WireIsBox: PROCEDURE[layerName: CARDINAL, width: LONG CARDINAL, a: ParserTypeDefs.Path, proc: PROC [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER]] RETURNS [isbox: BOOLEAN] = BEGIN isbox ← TRUE; -- isbox returns false if any wire is not a box -- Decompose the wire into boxes as suggested in Mead & Conway -- i,j,k are previous, current, and next points on the wire's path. IF a↑.length = 1 THEN -- make a flash proc[layerName, width, width, a↑.first↑.value, 1, 0] ELSE { halfWidth: LONG CARDINAL = (width+1)/2; extension, oldExtension, segLength: LONG CARDINAL; segment, nextSeg, bend: ParserTypeDefs.Point; j,n,nn:POINTER TO ParserTypeDefs.LinkedPoint; j ← a.first; n ← j.next; oldExtension ← halfWidth; segment ← [n↑.value.x-j↑.value.x, n↑.value.y-j↑.value.y]; -- loop invariant (true on entry) n ← j.next; nn ← n.next; FOR j ← j, n UNTIL n=NIL DO nn ← n.next; IF nn = NIL THEN extension ← halfWidth ELSE { nextSeg ← [nn↑.value.x-n↑.value.x, nn↑.value.y-n↑.value.y]; -- avoid long multiply overflows for Manhattan geometry bend ← segment; IF bend.x=0 THEN bend.y ← IF bend.y>0 THEN 1 ELSE -1 ELSE IF bend.y=0 THEN bend.x ← IF bend.x>0 THEN 1 ELSE -1; bend ← [nextSeg.x*bend.x+nextSeg.y*bend.y, nextSeg.x*bend.y-nextSeg.y*bend.x]; extension ← halfWidth*(ABS[bend.y]/(Length[bend]+ABS[bend.x])); }; segLength ← Length[segment]; proc[layerName: layerName, length: segLength+extension+oldExtension, width: width, center: [(n↑.value.x+j↑.value.x+(extension-oldExtension)*segment.x/segLength)/2, (n↑.value.y+j↑.value.y+(extension-oldExtension)*segment.x/segLength)/2], xRotation: segment.x, yRotation: segment.y]; IF isbox THEN isbox ← segment.x=0 OR segment.y=0; segment ← nextSeg; oldExtension ← extension; n ← nn; ENDLOOP; }; END; AuxWire: PUBLIC PROCEDURE [layerName: CARDINAL, width: LONG CARDINAL, a: ParserTypeDefs.Path] = BEGIN -- Decompose the wire into boxes as suggested in Mead & Conway [] ← WireIsBox[layerName, width, a, AuxBox]; END; Length: PROCEDURE[dif:ParserTypeDefs.Point] RETURNS [LONG CARDINAL] = { -- Utility to calculate length of vector dif. -- Avoids using SqRt in the typical case of Manhattan orientation. SELECT TRUE FROM dif.x = 0 => RETURN[ABS[dif.y]]; dif.y = 0 => RETURN[ABS[dif.x]]; ENDCASE => { x: REAL = Real.Float[dif.x]; y: REAL = Real.Float[dif.y]; RETURN[Real.Fix[RealFns.SqRt[x*x+y*y]]] }; }; AuxFlash: PUBLIC PROCEDURE [layerName: CARDINAL, diameter: LONG CARDINAL, center: ParserTypeDefs.Point] = BEGIN -- Make a box of this dimension AuxBox[layerName, diameter, diameter, center, 1, 0]; END; PolygonIsBox: PROCEDURE [layerName: CARDINAL, a: ParserTypeDefs.Path, proc: PROC [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER]] RETURNS [isbox: BOOLEAN] = BEGIN -- calls proc iff a describes a rectangular box -- Special hack: convert length/width-zero polygons to 1/2 lambda boxes. -- Some DRC programs use such polygons to outline errors. -- Rectangular iff, for 4 points i,j,k,l (vector notation): -- (i-j)=(l-k) (parallelogram rule, equivalently: (i-j+k-l)=0) -- (i-j).(i-l)=0 (right angle rule) -- The i-j direction is the length, the i-l direction is the width. isbox ← FALSE; IF a.length = 4 THEN { -- quadralaterals only total: ParserTypeDefs.Point ← [0,0]; FOR j: POINTER TO ParserTypeDefs.LinkedPoint ← a.first, j.next UNTIL j=NIL DO total.x ← j.value.x-total.x; total.y ← j.value.y-total.y; ENDLOOP; IF total = [0,0] THEN { -- parallelograms only OPEN i: a↑.first↑.value, j: a↑.first↑.next↑.value, l: a↑.last↑.value; IF (i.x-j.x)*(i.x-l.x) + (i.y-j.y)*(i.y-l.y) = 0 THEN { -- right angles only xRotation: LONG CARDINAL = i.x-j.x; yRotation: LONG CARDINAL = i.y-j.y; length: LONG CARDINAL = Length[[xRotation,yRotation]]; width: LONG CARDINAL = Length[[i.x-l.x,i.y-l.y]]; isbox ← TRUE; -- Use diagonals for center total.x ← (j.x+l.x)/2; total.y ← (j.y+l.y)/2; proc[layerName, length, width, total, xRotation, yRotation]; }; }; }; END; AuxPolygon: PUBLIC PROCEDURE [layerName: CARDINAL, a: ParserTypeDefs.Path] = BEGIN -- Ignore if non-rectangular, otherwise call the Box routine. [] ← PolygonIsBox[layerName, a, AuxBox]; END; AuxBox: PUBLIC PROCEDURE [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] = BEGIN manhattan: BOOLEAN ← (xRotation=0 OR yRotation=0); IF manhattan THEN SELECT Mode FROM ScanningSymbol,CountingItems => CountItem; PuttingDefn,PuttingItems => PutBox [layerName, length, width, center, xRotation, yRotation]; ENDCASE; -- ELSE ignore 45's END; AuxUserObject: PUBLIC PROCEDURE [layerName: CARDINAL, size: CARDINAL, data: POINTER TO UNSPECIFIED] = BEGIN u: POINTER TO UserCmd ← data; SELECT u↑.type FROM SymbolName => BEGIN SELECT Mode FROM ScanningSymbol => -- register name BEGIN i: CARDINAL; hasName ← TRUE; FOR i IN [1..u↑.name.length) DO StringDefs.AppendChar[Name,StringDefs.UpperCase[u↑.name[i]]]; ENDLOOP; END; ENDCASE; -- otherwise ignore END; NodeName => SELECT Mode FROM ScanningSymbol,CountingItems => CountItem[]; PuttingDefn,PuttingItems => PutNodeName[u↑.name,u↑.x,u↑.y]; ENDCASE; ENDCASE => InternalError["Funny User Type"]; END; AuxCall: PUBLIC PROCEDURE [objectname: IntStorageDefs.ObjectName, number: LONG CARDINAL, xform: IntTransDefs.Transform] = BEGIN SELECT Mode FROM Marking => Mark[objectname]; ScanningSymbol,CountingItems => CountItem; PuttingDefn,PuttingItems => PutCall[objectname, number, xform]; ENDCASE; END; PutBox: PROCEDURE [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] = BEGIN xSize, ySize: LONG CARDINAL; chipX,chipY,chipOrient,chipType,chipWidth,chipLength,chipLayer: CARDINAL; bbrel: ParserTypeDefs.Point; [xSize,ySize] ← ResolveRotation[length, width, xRotation, yRotation]; bbrel ← BBRel[center]; -- find center rel to bbox left top corner chipX ← ChipScale[(bbrel.x - xSize/2)]; chipY ← ChipScale[-(bbrel.y + ySize/2)]; IF xSize > ySize THEN -- long ways horizontal BEGIN chipOrient ← 4; chipLength ← ChipScale[xSize]; chipWidth ← ChipScale[ySize]; END ELSE -- long ways vertical BEGIN chipOrient ← 0; chipLength ← ChipScale[ySize]; chipWidth ← ChipScale[xSize]; END; chipLayer ← ChipLayers[layerName]; chipType ← SELECT chipLayer FROM 1,2,3 => 4, -- dif,pol,met => Chip Wire ENDCASE => 5; -- other => Chip Rectangle PutWord[chipX]; PutWord[chipY]; PutWord[chipOrient]; PutWord[chipType]; PutWord[chipWidth]; PutWord[chipLength]; PutWord[chipLayer]; PutWord[0]; -- zero properties (for now) END; PutNodeName: PROCEDURE [name: STRING, x,y: LONG INTEGER] = BEGIN orgrel: ParserTypeDefs.Point ← [x,y]; bbrel: ParserTypeDefs.Point ← BBRel[orgrel]; chipLayer: CARDINAL; -- chipLayer ← ChipLayers[FindLayer[x,y]]; chipLayer ← 3; Warning["Node name placed on Metal",name]; PutWord[ChipScale[ bbrel.x]]; -- x position PutWord[ChipScale[-bbrel.y]]; -- y position PutWord[0]; -- orientation PutWord[4]; -- wire object PutWord[0]; -- width zero PutWord[0]; -- length zero PutWord[chipLayer]; PutWord[1]; -- with one property... PutWord[1]; -- of type text... PutString[name]; END; FindLayer: PROCEDURE [x,y: LONG INTEGER] RETURNS [cifLayer: CARDINAL] = -- given point, finds layer there. BEGIN -- not implemented yet cifLayer ← 4; InternalError ["FindLayer Not Implemented"]; END; PutCall: PROCEDURE [objectname: IntStorageDefs.ObjectName, number: LONG CARDINAL, xform: IntTransDefs.Transform] = BEGIN d,l,r,b,t,xOrigin,yOrigin,xLeft,yTop: LONG INTEGER; chipX,chipY: INTEGER; chipOrient: CARDINAL; matrix: RECORD[a11,a12,a21,a22: LONG INTEGER]; leftTopRelOrigin,leftTopRelBBox: ParserTypeDefs.Point; [l,r,b,t] ← AuxIntDefs.ISymBB[objectname]; -- bbox of called symbol relative to its origin d ← Real.Fix[xform.a33]; matrix ← [Real.Fix[xform.a11]/d, Real.Fix[xform.a12]/d, Real.Fix[xform.a21]/d, Real.Fix[xform.a22]/d]; xOrigin ← Real.Fix[xform.a31]/d; yOrigin ← Real.Fix[xform.a32]/d; -- have origin of instance relative to origin of containing symbol SELECT matrix FROM [ 1, 0, 0, 1] => -- identity BEGIN chipOrient ← 0; xLeft ← xOrigin+l; yTop ← yOrigin+t; END; [-1, 0, 0, 1] => -- mirror x (about y) BEGIN chipOrient ← 1; xLeft ← xOrigin-r; yTop ← yOrigin+t; END; [ 0,-1, 1, 0] => -- rotate 90 clockwise BEGIN chipOrient ← 4; xLeft ← xOrigin+b; yTop ← yOrigin-l; END; [ 0,-1,-1, 0] => -- rotate 90 cw, then mirror x BEGIN chipOrient ← 5; xLeft ← xOrigin-t; yTop ← yOrigin-l; END; [-1, 0, 0,-1] => -- rotate 180 BEGIN chipOrient ← 8; xLeft ← xOrigin-r; yTop ← yOrigin-b; END; [ 1, 0, 0,-1] => -- mirror y (about x) BEGIN chipOrient ← 9; xLeft ← xOrigin+l; yTop ← yOrigin-b; END; [ 0, 1,-1, 0] => -- rotate 90 counterclockwise BEGIN chipOrient ← 12; xLeft ← xOrigin-t; yTop ← yOrigin+r; END; [ 0, 1, 1, 0] => -- rotate 90 ccw, then mirror x BEGIN chipOrient ← 13; xLeft ← xOrigin+b; yTop ← yOrigin+r; END; ENDCASE => -- other BEGIN Warning ["Arbitrary transformation","taken as zero transform"]; chipOrient ← 0; xLeft ← xOrigin+l; yTop ← yOrigin+t; END; -- have left top of instance relative to origin of containing symbol leftTopRelOrigin ← [xLeft,yTop]; leftTopRelBBox ← BBRel[leftTopRelOrigin]; -- have left top of instance relative to left top of containing symbol chipX ← ChipScale[leftTopRelBBox.x]; chipY ← ChipScale[-leftTopRelBBox.y]; PutWord[chipX]; -- x position PutWord[chipY]; -- y position PutWord[chipOrient]; -- orientation PutWord[1]; -- code for "call" = "cell instance" PutWord[NutsDictDefs.Lookup[objectname]]; -- which cell PutWord[0]; -- no properties END; BBRel: PROCEDURE [relOrigin: ParserTypeDefs.Point] RETURNS [relBBox: ParserTypeDefs.Point] = -- given x, y of item relative to "origin" of enclosing symbol -- returns x,y of item relative to left top corner of its BBox BEGIN IF CurrentSymbol = IntStorageDefs.NilObjectName THEN BEGIN relBBox.x ← relOrigin.x; relBBox.y ← relOrigin.y; END ELSE BEGIN l,r,b,t: LONG INTEGER; -- BBox of enclosing symbol in CIF units [l,r,b,t] ← AuxIntDefs.ISymBB[CurrentSymbol]; relBBox.x ← relOrigin.x - l; relBBox.y ← relOrigin.y - t; END; END; InitOutput: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN InternalError["InitOutput not implemented"]; RETURN[FALSE]; END; FinishOutput: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN InternalError["FinishOutput not implemented"]; RETURN[FALSE]; END; SendMessage: PUBLIC PROCEDURE [type: ParserErrorDefs.ErrorType, message: STRING, wantCR: BOOLEAN ← TRUE] = BEGIN IODefs.WriteString[message]; IF wantCR THEN IODefs.WriteChar[IODefs.CR]; END; -- associates a CARDINAL with a layer name - a PACKED ARRAY [0..4) OF CHARACTER, padded by spaces to make 4 characters (if necessary) Map: PUBLIC PROCEDURE [layerName: PACKED ARRAY [0..4) OF CHARACTER] RETURNS [CARDINAL] = -- same layer numbers as Magic, for future compatibility BEGIN IF layerName[0] = 'N AND layerName[2] = ' AND layerName[3] = ' THEN SELECT layerName[1] FROM 'I => RETURN[0]; -- implant 'D => RETURN[1]; -- diffusion 'P => RETURN[2]; -- poly 'C => RETURN[3]; -- contact 'M => RETURN[4]; -- metal 'B => RETURN[5]; -- buried 'G => RETURN[6]; -- glass ENDCASE => BEGIN ParserErrorDefs.Report["Unrecognized layer name becomes Chipmonk layer 7",Advisory]; RETURN[7]; END; RETURN[7]; -- Never executed but necessary to keep compiler happy! END; -- Each routine below implements the output of one primitive geometric construct form CIF. OutputWire: PUBLIC PROCEDURE [visible: OutputDefs.VisibleType, layerName: CARDINAL, width: LONG CARDINAL, a: ParserTypeDefs.Path] = BEGIN InternalError["OutputWire not implemented"]; END; OutputFlash: PUBLIC PROCEDURE [visible: OutputDefs.VisibleType, layerName: CARDINAL, diameter: LONG CARDINAL, center: ParserTypeDefs.Point] = BEGIN InternalError["OutputFlash not implemented"]; END; OutputPolygon: PUBLIC PROCEDURE [visible: OutputDefs.VisibleType, layerName: CARDINAL, a: ParserTypeDefs.Path] = BEGIN InternalError["OutputPolygon not implemented"]; END; OutputBox: PUBLIC PROCEDURE [visible: OutputDefs.VisibleType, layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] = BEGIN InternalError["OutputBox not implemented"]; END; OutputUserCommand: PUBLIC PROCEDURE [command: [0..9], userText: STRING] = BEGIN stringWords: CARDINAL = StringDefs.WordsForString[Storage.StringLength[userText]]; size: CARDINAL = SIZE[UserCmd] + stringWords; pu: POINTER TO UserCmd = Storage.Node[size]; IF command = 9 AND userText[0] = ' THEN -- SymbolName BEGIN pu↑.type ← SymbolName; InlineDefs.COPY[from: userText, nwords: stringWords, to: @pu↑.stringBody]; pu↑.name ← LOOPHOLE[@pu↑.stringBody, STRING]; pu↑.x ← pu↑.y ← 0; END ELSE IF command = 9 AND userText[0] = '4 AND userText[1] = ' THEN -- NodeName BEGIN -- code to parse node names (hastily written, could use refinement) ENABLE StringDefs.InvalidNumber => BEGIN ParserErrorDefs.Report["Fatal error, sorry: Invalid number",Fatal]; ImageDefs.AbortMesa; END; temp: STRING ← [20]; i: CARDINAL; NextNumber: PROCEDURE RETURNS [LONG CARDINAL] = BEGIN UNTIL userText[i] IN ['-..'9] DO i←i+1; ENDLOOP; -- skip blanks etc. temp.length ← 0; WHILE userText[i] IN ['-..'9] AND i<userText.length DO StringDefs.AppendChar[temp,userText[i]]; i←i+1; ENDLOOP; RETURN [IntDefs.IScaleLongInt[StringDefs.StringToLongNumber[temp,10]]]; END; pu↑.type ← NodeName; pu↑.name ← LOOPHOLE[@pu↑.stringBody, STRING]; InlineDefs.COPY[from: userText, nwords: stringWords, to: @pu↑.stringBody]; -- hack to initialize pu↑.name.maxlength pu↑.name.length ← 0; i←2; UNTIL userText[i] = ' DO -- copy up to first blank StringDefs.AppendChar[pu↑.name,userText[i]]; i←i+1; ENDLOOP; pu↑.x ← NextNumber[]; pu↑.y ← NextNumber[]; END ELSE -- Undef; BEGIN ParserErrorDefs.Report["Undefined User Object - Ignored",Advisory]; END; IntDefs.IUserObject[size,pu]; END; OutputUserObject: PUBLIC PROCEDURE [visible: OutputDefs.VisibleType, layerName: CARDINAL, size: CARDINAL, data: POINTER TO UNSPECIFIED] = BEGIN InternalError["OutputUserObject not implemented"]; END; -- establish ordering Relation: PUBLIC PROCEDURE [left1, right1, bottom1, top1, left2, right2, bottom2, top2: LONG INTEGER] RETURNS [OutputDefs.RelationType] = BEGIN InternalError["Relation not implemented"]; RETURN[dontcare]; END; -- determine whether an item should be shown Visible: PUBLIC PROCEDURE [kind: IntStorageDefs.ObjectType, level: CARDINAL, parentVis: OutputDefs.VisibleType, left,right,bottom,top: LONG INTEGER] RETURNS [OutputDefs.VisibleType] = BEGIN InternalError["Visible not implemented"]; RETURN[maybe]; END; -- returns the bounding box of the primitive interpreted in the current context BBWire: PUBLIC PROCEDURE [layerName: CARDINAL, width: LONG CARDINAL, a: ParserTypeDefs.Path] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN BBBoxDummy: PROC [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] = BEGIN a,b,c,d: LONG INTEGER; [a,b,c,d] ← BBBox[layerName, length, width, center, xRotation, yRotation]; left ← MAX[left,a]; right ← MIN[right,b]; bottom ← MAX[bottom,c]; top ← MIN[top,d]; END; left ← bottom ← FIRST[LONG INTEGER]; right ← top ← LAST[LONG INTEGER]; IF ~WireIsBox[layerName, width, a, BBBoxDummy] THEN { ParserErrorDefs.Report["Non-manhattan wire ignored",Advisory]; left ← right ← bottom ← top ← 0; }; END; BBFlash: PUBLIC PROCEDURE [layerName: CARDINAL, diameter: LONG CARDINAL, center: ParserTypeDefs.Point] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN [left,right,bottom,top] ← BBBox[layerName, diameter, diameter, center, 1, 0]; END; BBPolygon: PUBLIC PROCEDURE [layerName: CARDINAL, a: ParserTypeDefs.Path] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN BBBoxDummy: PROC [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] = BEGIN [left,right,bottom,top] ← BBBox[layerName, length, width, center, xRotation, yRotation]; END; IF ~PolygonIsBox[layerName, a, BBBoxDummy] THEN ParserErrorDefs.Report["Non-rectangular Polygon ignored",Advisory]; END; BBBox: PUBLIC PROCEDURE [layerName: CARDINAL, length, width: LONG CARDINAL, center: ParserTypeDefs.Point, xRotation, yRotation: LONG INTEGER] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN xSize,ySize: LONG CARDINAL; manhattan: BOOLEAN ← (xRotation=0 OR yRotation=0); IF manhattan THEN [xSize,ySize] ← ResolveRotation[length, width, xRotation, yRotation] ELSE BEGIN ParserErrorDefs.Report["Non-manhattan box ignored",Advisory]; xSize ← ySize ← 0; END; left ← center.x - xSize/2; right ← center.x + xSize/2; bottom ← center.y - ySize/2; top ← center.y + ySize/2; END; BBUserObject: PUBLIC PROCEDURE [layerName: CARDINAL, size: CARDINAL, data: POINTER TO UNSPECIFIED] RETURNS [left,right,bottom,top: LONG INTEGER] = BEGIN u: POINTER TO UserCmd ← data; SELECT u↑.type FROM SymbolName => RETURN[0,0,0,0]; NodeName => RETURN[u↑.x,u↑.x,u↑.y,u↑.y]; ENDCASE => InternalError["Funny User Type"]; RETURN[0,0,0,0]; END; -- Mainline code IODefs.WriteLine[herald]; GetFileNames; SetLambda[GetLambda[]]; UNTIL ParseCifFile[CifFileName] DO GetFileNames; ENDLOOP; OpenChipFile[ChipFileName]; PutChipFile; CloseChipFile; IF NOT IntDefs.FinishInterpreter[] THEN InternalError["problem finishing Interpreter"]; NutsDictDefs.FreeDict; END.