-- Subroutine to generate CIF from Chipmonk

-- last modified by McCreight, January 10, 1983  3:24 PM
  -- to use either lambda/2 or lambda/4 resolution as requested
-- adapted from ppio.mesa by McCreight, November 9, 1982  5:09 PM

DIRECTORY
  ChipOrient,
  ChipUserInt,
  InlineDefs,
  StreamDefs,
  StringDefs,
  ppdddefs,
  ppddefs,
  ppdefs,
  ppMainDefs,
  pppdefs,
  ppoutdefs,
  TimeDefs,
  ZoneAllocDefs;

CIFGen: PROGRAM
  IMPORTS
    ChipOrient, ChipUserInt, InlineDefs,
    ppdefs, ppdddefs, ppMainDefs, pppdefs,
    StreamDefs, StringDefs, TimeDefs, ZoneAllocDefs
  EXPORTS ppdefs =
  BEGIN
  OPEN InlineDefs, StreamDefs, ppdefs, ppdddefs,
    ChipOrient, ChipUserInt;

  aux: PUBLIC TYPE = CifCell;

  CifCellPtr: TYPE = LONG POINTER TO CifCell;
  CifCell: TYPE = RECORD[id: CellId];
  CellId: TYPE = CARDINAL;


  cifLayName: ARRAY level OF STRING ←
    [cut: "NC",
    dif: "ND",
    pol: "NP",
    met: "NM",
    imp: "NI",
    ovg: "NG",
    bur: "NB",
    snerd: "X",
    cut2: "NC2",
    pdif: "Q",
    pwelCont: "X",
    met2: "NM2",
    pwel: "X",
    nwel: "T",
    nwelCont: "X",
    NOcOL: "X"];

  chipmonkLayName: ARRAY level OF STRING ←
    [cut: "cut",
    dif: "dif",
    pol: "pol",
    met: "met",
    imp: "imp",
    ovg: "ovg",
    bur: "bur",
    snerd: "",
    cut2: "cut2",
    pdif: "pdif",
    pwelCont: "pwelCont",
    met2: "met2",
    pwel: "pwel",
    nwel: "nwel",
    nwelCont: "nwelCont",
    NOcOL: ""];

  infinity: locNum = LAST[locNum];

  cellCnt: CellId ← 0;
  cifFile: StreamDefs.DiskHandle ← NIL;

  levelAnnounced: BOOLEAN ← FALSE;
  curLevel: level;
  cifScale: INTEGER ← 50; -- CIF units per half-locNum
    -- Note: This dealing in half-locnums means that we can
    --  represent the center of any box exactly.

  cifDrR: drRecord ← [
    [x1: -infinity, y1: -infinity, x2: infinity, y2: infinity],
    [x1: -infinity, y1: -infinity, x2: infinity, y2: infinity],
    cifOrArea, cifOrArea,
    nullOutl, nullCifDrawText, 0];

  nullOutl: PROCEDURE[a, b, c, d: INTEGER, q: color,
    p: POINTER TO Rect] = {NULL};

  nullCifDrawText: PROCEDURE[x, y, sx, sy: INTEGER,
    s: STRING, pz: POINTER TO Rect] = {NULL};


  cifDefineObject: PROC [ob: obPtr] =
    BEGIN

    SymHeader: PROC =
      BEGIN
      cifOutStr["DS "];
      cellCnt ← cellCnt+1;
      cifOutNum[cellCnt];
      ob.auxPnt ← uz.NEW[CifCell ← [id: cellCnt]];
      cifOutStr[" "];
      cifOutNum[cifScale];  -- CIF units per locNum
      cifOutStr[" 1"];
      cifOutEndCom[];
      END;

    SymTrailer: PROC =
      BEGIN
      cifOutStr["DF"];
      cifOutEndCom[];
      END;

    IF ob.auxPnt#NIL THEN RETURN; -- already defined

    WITH dob: ob SELECT FROM
      wire, rect => NULL;

      cell =>
        BEGIN
        pp: listPtr;
        FOR pp ← dob.ptr, pp.nxt WHILE pp # NIL DO
          cifDefineObject[pp.ob];
          ENDLOOP;

        SymHeader[];
    
        IF dob.ptr=masterList THEN
          cifDrawCellName["TopLevelDesign"]
        ELSE
          FOR cl: LONG POINTER TO cList ← cellList, cl.nxt
            WHILE cl#NIL DO
            IF cl.ob = @dob THEN
              {cifDrawCellName[cl.name]; EXIT};
            ENDLOOP;

        FOR pp ← dob.ptr, pp.nxt WHILE pp # NIL DO

          -- first call all interior cells
          WITH cob: pp.ob SELECT FROM
            rect, wire => NULL;
            ENDCASE =>
              BEGIN
              refCorner: Rect = ChipOrient.MapRect[
                itemInCell: [0, 0, 0, 0],
                cellSize: [x: cob.size[0], y: cob.size[1]],
                cellInstOrient: pp.idx,
                cellInstPos: [x: pp.lx, y: pp.ly]];
              cifSymbolCall[@cob];
              IF pp.idx # 0 THEN
                BEGIN
                jj: CARDINAL;
                IF (jj ← BITAND[pp.idx, 12]) # 0 THEN
                  BEGIN
                  cifOutStr[" R "];
                  cifOutStr[SELECT jj FROM
                    4 => "0,1",
                    8 => "-1,0",
                    12 => "0,-1",
                    ENDCASE => "1,0" -- we'll never use this one --];
                  END;
                IF BITAND[pp.idx, 1] # 0 THEN cifOutStr[" M X"];
                END;
              IF refCorner.x1#0 OR refCorner.y1#0 THEN
                BEGIN
                cifOutStr[" T "];
                cifOutPoint[refCorner.x1, refCorner.y1];
                END;
              cifOutEndCom[];
              END;
          ENDLOOP;

        -- next generate all rectangles, by layer
        FOR curLevel IN level DO
          levelAnnounced ← FALSE;
          FOR pp ← dob.ptr, pp.nxt WHILE pp # NIL DO
            WITH cob: pp.ob SELECT FROM
              rect, wire =>
                IF cob.l=curLevel THEN
                  cob.p.drawme[pp.idx][@cob, pp.lx, pp.ly, @cifDrR];
              ENDCASE => NULL;
            ENDLOOP;
          ENDLOOP;

        -- finally generate all signal names
        FOR pp ← dob.ptr, pp.nxt WHILE pp # NIL DO
          IF pp.gotText THEN
            WITH cob: pp.ob SELECT FROM
              wire =>
                cifLabelTerminal[pp, getTextProp[pp].s, pp.ob.l];
              cont =>
                BEGIN
                lev: level = (SELECT cob.typ FROM
                  burr => pol,
                  ENDCASE => met);
                cifLabelTerminal[pp, getTextProp[pp].s, lev];
                END;
              ENDCASE => NULL;
          ENDLOOP;

        SymTrailer[];
        END; -- of cell

      ENDCASE => -- geometry
        BEGIN
        SymHeader[];
        FOR curLevel IN level DO
          levelAnnounced ← FALSE;
          dob.p.drawme[0][@dob, 0, 0, @cifDrR];
          ENDLOOP;
        SymTrailer[];
        END;
    END;

  cifSymbolCall: PROCEDURE[ob: obPtr] =
    BEGIN
    cp: CifCellPtr = ob.auxPnt;
    IF cp=NIL THEN ERROR;
    cifOutStr["C "];
    cifOutNum[cp.id];
    END;

  cifOrArea: PROCEDURE [x1, y1, x2, y2: INTEGER, l: level,
    p: POINTER TO Rect] =
    BEGIN
    x, y: LONG INTEGER;
    IF l # curLevel OR x2=x1 OR y2=y1 THEN RETURN;
    IF x2<x1 THEN
      {t: locNum ← x1; x1 ← x2; x2 ← t};
    IF y2<y1 THEN
      {t: locNum ← y1; y1 ← y2; y2 ← t};
    IF NOT levelAnnounced THEN
      BEGIN
      cifOutStr["L "];
      cifOutStr[cifLayName[curLevel]];
      cifOutEndCom[];
      levelAnnounced ← TRUE;
      END;
    IF bloatImplant AND l=imp THEN
      BEGIN
      x1 ← x1-Lambda/2;
      x2 ← x2+Lambda/2;
      y1 ← y1-Lambda/2;
      y2 ← y2+Lambda/2;
      END;
    x ← x1;
    y ← y1;
    x ← (x + x2)*cifScale;
    y ← (y + y2)*cifScale;
    x ← x/2;
    y ← y/2;
    cifOutStr["B "];
    cifOutNum[resolution*ABS[x2 - x1]]; -- width
    cifOutChr[' ];
    cifOutNum[resolution*ABS[y2 - y1]];
    cifOutChr[' ];
    IF (x1+x2) MOD resolution # 0 THEN
      RemarkAtPoint[
        p: [(x1+x2)/2, (y1+y2)/2],
        s: "Point off-grid in x."];
    cifOutNum[(resolution*(x1+x2))/2]; -- center
    cifOutChr[',];
    IF (y1+y2) MOD resolution # 0 THEN
      RemarkAtPoint[
        p: [(x1+x2)/2, (y1+y2)/2],
        s: "Point off-grid in y."];
    cifOutNum[(resolution*(y1+y2))/2];
    cifOutEndCom[];
    END;

  cifLabelTerminal: PROCEDURE [lp: listPtr, s: STRING,
    lev: level] =
    BEGIN
    size: Point =
      Size[size: [x: lp.ob.size[0], y: lp.ob.size[1]], orient: lp.idx];
    cifOutStr["94 "];
    cifOutStr[s];
    cifOutStr[" "];
    cifOutPoint[lp.lx+size.x/2, lp.ly+size.y/2]; -- in the center
    cifOutStr[" "];
    cifOutStr[cifLayName[lev]];
    cifOutEndCom[];
    END;

  cifDrawCellName: PROCEDURE [s: STRING] =
    BEGIN
    cifOutStr["9 "];
    cifOutStr[s];
    cifOutEndCom[];
    END;

  cifOutStr: PUBLIC PROCEDURE [s: STRING] =
    BEGIN
    FOR i: CARDINAL IN [0..s.length) DO
      cifFile.put[cifFile, s[i]];
      ENDLOOP;
    END;

  cifOutChr: PUBLIC PROCEDURE [c: CHARACTER] = INLINE
    {cifFile.put[cifFile, c]};

  cifOutEndCom: PROCEDURE =
    {cifOutChr[';]; cifOutChr[15C]};

  cifOutNum: PUBLIC PROCEDURE [n: LONG INTEGER] =
    BEGIN
    IF n < 0 THEN {cifOutChr['-]; n ← -n};
    IF n>9 THEN cifOutNum[n/10];
    cifOutChr['0 + LowHalf[n MOD 10]];
    END;

  cifOutPoint: PROCEDURE [x, y: locNum] =
    {cifOutPair[resolution*x, resolution*y]}; 

  cifOutPair: PROCEDURE [x, y: LONG INTEGER] =
    {cifOutNum[x]; cifOutChr[',]; cifOutNum[y]}; 

  findname: PROCEDURE [p: LONG POINTER TO cell object]
    RETURNS[s: STRING] =
    BEGIN
    cp: LONG POINTER TO cList←cellList;
    s ← "";
    FOR cp: LONG POINTER TO cList←cellList, cp.nxt
      WHILE cp # NIL DO
      IF cp.ob = p THEN {s ← cp.name; RETURN};
      ENDLOOP;
    END;


  mainRect: Rect ← [x1: infinity, y1: infinity,
    x2: -infinity, y2: -infinity];

  cifMeasureR: drRecord ← [
    [x1: -infinity, y1: -infinity, x2: infinity, y2: infinity],
    [x1: -infinity, y1: -infinity, x2: infinity, y2: infinity],
    cifMeasureArea, cifMeasureArea,
    nullOutl, nullCifDrawText, 0];

  cifMeasureArea: PROCEDURE [x1, y1, x2, y2: INTEGER, l: level,
    p: POINTER TO Rect] =
    BEGIN
    IF x2=x1 OR y2=y1 THEN RETURN;
    IF x2<x1 THEN
      {t: locNum ← x1; x1 ← x2; x2 ← t};
    IF y2<y1 THEN
      {t: locNum ← y1; y1 ← y2; y2 ← t};
    mainRect ←
      [x1: MIN[x1, mainRect.x1], y1: MIN[y1, mainRect.y1],
      x2: MAX[x2, mainRect.x2], y2: MAX[y2, mainRect.y2]];
    END;

  NullLpAux: PROCEDURE[lp: listPtr] =
    BEGIN
    WHILE lp#NIL DO
      NullObAux[lp.ob];
      lp ← lp.nxt;
      ENDLOOP;
    END;

  NullObAux: PROCEDURE[ob: obPtr] =
    BEGIN
    ob.auxPnt ← NIL;
    WITH o: ob SELECT FROM
        cell => NullLpAux[o.ptr];
        ENDCASE => NULL;
    END;


  -- Module START code

  uz: UNCOUNTED ZONE ←
    ZoneAllocDefs.GetAnXMZone[checkSegments: TRUE];
  bloatImplant: BOOLEAN;
  resolution: INTEGER;
  name, comment: STRING ← NIL;

  NullLpAux[masterList];

    BEGIN OPEN StringDefs, TimeDefs;
    ENABLE Punt, UNWIND => GOTO Finished;
    time: STRING ← [100];
    mainOb: object ← [
      p: NIL,
      size: [infinity, infinity, infinity],
      refCnt: 0,
      auxPnt: NIL,
      l: NOcOL,
      returnable: FALSE,
      marked: FALSE,
      varpart: cell[cnt: 0, ptr: masterList, super: NIL]];
    name ← RequestString["Name of CIF file:"];
    IF name=NIL OR name.length=0 THEN
      name ← newString[ppMainDefs.fileName];
    name ← FixExtension[name, ".cif"];
    cifFile ← NewByteStream[name, WriteAppend];
    bloatImplant ← HeSaysYes[
      "Should I bloat implant rectangles by lambda/2 in all directions?"];
    resolution ← IF HeSaysYes[
      "Will all CIF co-ordinates lie on a half-lambda grid?",
      "(almost always true...)"] THEN 1 ELSE 2;
    cifScale ← RequestInteger["How many CIF units per lambda?"]/(resolution*Lambda);
    time.length ← 0;
    AppendDayTime[time, UnpackDT[CurrentDayTime[]]];
    cifOutStr["("];
    cifOutStr[name];
    cifOutStr[" - generated "];
    cifOutStr[time];
    cifOutStr[" by Xerox PARC Chipmonk with Lambda = "];
    cifOutNum[resolution*cifScale*Lambda];
    cifOutStr[" CIF units)"];
    cifOutEndCom[];
    pppdefs.drCell0[ob: @mainOb, x: 0, y: 0, pr: @cifMeasureR];
    cifOutStr["(Origin = [x: 0, y: 0], Size = [x: "];
    cifOutNum[resolution*cifScale*(mainRect.x2-mainRect.x1)];
    cifOutStr[", y: "];
    cifOutNum[resolution*cifScale*(mainRect.y2-mainRect.y1)];
    cifOutStr["] CIF units)"];
    cifOutEndCom[];
    IF bloatImplant THEN
      BEGIN
      cifOutStr["( Implant rectangles bloated by lambda/2 from Chipmonk design )"];
      cifOutEndCom[];
      END;
    comment ← RequestString[s1: "Comment line:",
      s2: "(any parentheses must be balanced)",
      s3: "(CR for no comment)",
      lowerCaseOK: TRUE];
    WHILE comment#NIL AND comment.length>0 DO
      cifOutStr["( "];
      cifOutStr[comment];
      cifOutStr[" )"];
      cifOutEndCom[];
      FreeString[comment];
      comment ← NIL;
      comment ← RequestString[s1: "Another comment line:",
        s2: "(any parentheses must be balanced)",
        s3: "(CR for no further comments)",
        lowerCaseOK: TRUE];
      ENDLOOP;
    IF HeSaysYes["Want to change standard layer names?"] THEN
      FOR l:level IN level DO
        IF chipmonkLayName[l].length > 0 THEN
          BEGIN
          cln: STRING ← NIL;
          s1: STRING ← [100];
          s2: STRING ← [100];
          s1.length ← s2.length ← 0;
          AppendString[to: s1, from: "CIF name for "];
          AppendString[to: s1, from: chipmonkLayName[l]];
          AppendString[to: s1, from: "?"];
          AppendString[to: s2, from: "(CR to keep standard name of "];
          AppendString[to: s2, from: cifLayName[l]];
          AppendString[to: s2, from: ")"];
          cln ← RequestString[s1, s2];
          IF cln # NIL AND cln.length > 0 THEN
            cifLayName[l] ← cln;
          END;
        ENDLOOP;
    cifDefineObject[@mainOb];
    cifSymbolCall[@mainOb];
    cifOutStr[" M Y T "];
    cifOutPair[x: -resolution*cifScale*mainRect.x1,
      y: resolution*cifScale*mainRect.y2];
    cifOutStr[";"];
    cifOutChr[15C];
    cifOutStr["End ..."];
    cifOutChr[15C];
    EXITS Finished => NULL;
    END;

  IF cifFile#NIL THEN
    BEGIN
    TruncateDiskStream[cifFile];
    cifFile ← NIL;
    END;
  IF name#NIL THEN {FreeString[name]; name ← NIL};
  IF comment#NIL THEN {FreeString[comment]; comment ← NIL};
  NullLpAux[masterList];
  uz ← ZoneAllocDefs.DestroyAnXMZone[uz];
  END. -- of CIFGen